/// <summary> /// Parse the file table. /// </summary> /// <returns>Number of files found.</returns> public override int ParseFileTable() { this.fileTable = new List <FileItem>(); // Extract TOC { uint identifier = StreamUtility.ReadUIntFromStream(this.container, this.tableOffset); if (identifier != 0x544f4320) // TOC { throw new FormatException(container.Name + ": Checking TOC identified failed!"); } // Get UTF length int tocLength = StreamUtility.ReadIntFromStream(this.container, this.tableOffset + 0x8, false); byte[] tocBytes = StreamUtility.ReadBytesFromStream(this.container, this.tableOffset + 0x10, tocLength); this.toc = CriPack_UTF.ByteUnpack(tocBytes); } // Extract E-TOC (Extended TOC) try { long etocOffset = (long)(ulong)this.header.GetPropertyValue(0, "EtocOffset"); uint identifier = StreamUtility.ReadUIntFromStream(this.container, etocOffset); if (identifier != 0x45544f43) // ETOC { throw new FormatException(container.Name + ": Checking E-TOC identified failed!"); } // Get UTF length int etocLength = StreamUtility.ReadIntFromStream(this.container, etocOffset + 0x8, false); byte[] etocBytes = StreamUtility.ReadBytesFromStream(this.container, etocOffset + 0x10, etocLength); this.etoc = CriPack_UTF.ByteUnpack(etocBytes); } catch (KeyNotFoundException e) { } // No E-TOC for (int i = 0; i < toc.properties.Length; i++) { CriPackFileItem item = new CriPackFileItem(this.container); item.AssignTocProperties(toc.properties[i], this.tableOffset); if (this.etoc != null) { item.AssignEtocProperties(etoc.properties[i]); } fileTable.Add(item); } return(fileTable.Count); }
/// <summary> /// Construct a CriPack file. /// </summary> /// <param name="file">File stream.</param> public CriPack_Container(FileStream file) : base(file) { long offset = 0; // Confirm 'CriPack' identifier uint identifier = StreamUtility.ReadUIntFromStream(this.container, offset); offset += 4; if (identifier != 0x43504b20) // If file identifier is not CriPack { throw new FormatException(container.Name + ": File is not CriPack format!"); } // Get UTF length offset += 4; int headerLength = StreamUtility.ReadIntFromStream(this.container, offset, false); offset += 4; // Zero padding, ignore offset += 4; this.containerType = "CRI Pack Container"; //byte[] utfHeader = StreamUtility.ReadBytesFromStream(this.container, offset, (int)this.utfLength); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0xedb88320, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0x04C11DB7, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); //utfHeader = StreamUtility.ReadBytesFromStream(this.container, offset + 8, (int)this.utfLength - 8); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0xedb88320, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0x04C11DB7, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); //utfHeader = StreamUtility.ReadBytesFromStream(this.container, offset - 8, (int)this.utfLength + 8); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0xedb88320, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0x04C11DB7, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); //utfHeader = StreamUtility.ReadBytesFromStream(this.container, offset - 16, (int)this.utfLength + 16); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0xedb88320, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0x04C11DB7, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); //utfHeader = StreamUtility.ReadBytesFromStream(this.container, offset + 4, (int)this.utfLength - 4); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0xedb88320, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); //MessageBox.Show("CRC32 of UTF: " + String.Format("0x{0:X4}", Crc32.Compute(0x04C11DB7, 0xffffffff, utfHeader)), "Info", MessageBoxButtons.OK); byte[] headerBytes = StreamUtility.ReadBytesFromStream(this.container, offset, headerLength); this.header = CriPack_UTF.ByteUnpack(headerBytes); this.tableOffset = (long)(ulong)this.header.GetPropertyValue(0, "TocOffset"); }
/// <summary> /// Dump this file data to another file. /// </summary> /// <param name="toFile">The destination file stream.</param> /// <returns>Length copied.</returns> public override long DumpData(FileStream toFile) { if (this.file == null || !this.file.CanRead) { throw new IOException("Source stream is not accessable!"); } if (this.fileSize < this.extractSize) { // Compressed return(this.DecompressToFile(toFile)); } else { // Not compressed byte[] content = StreamUtility.ReadBytesFromStream(this.file, this.fileOffset, (int)this.extractSize); Debug.WriteLine(String.Format("CRC for file {0} is {1:X4}", this.fileName, Crc32.CalcCrc32(content))); return(StreamUtility.CopyBlock(this.file, toFile, this.fileOffset, (int)this.extractSize)); } }
public static CriPack_UTF ByteUnpack(byte[] stream) { if (stream == null || stream.Length < 4) { return(null); } if (stream[0] != 0x40 || // @ stream[1] != 0x55 || // U stream[2] != 0x54 || // T stream[3] != 0x46) // F { stream = CriPack_UTF.Decrypt(stream); } using (MemoryStream buffer = new MemoryStream(stream)) { CriPack_UTF utf = new CriPack_UTF(); long offset = 4; utf.payloadSize = StreamUtility.ReadUIntFromStream(buffer, offset); offset += 4; utf.dataBankOffset = StreamUtility.ReadUIntFromStream(buffer, offset) + 8; offset += 4; utf.stringBankOffset = StreamUtility.ReadUIntFromStream(buffer, offset) + 8; offset += 4; utf.endOfPayloadOffset = StreamUtility.ReadUIntFromStream(buffer, offset) + 8; offset += 4; // Optional check if (utf.endOfPayloadOffset != (utf.payloadSize + 8)) { MessageBox.Show("End of UTF Payload (" + String.Format("0x{0:X4}", utf.endOfPayloadOffset) + ") " + "does not match UTF Payload Size (" + String.Format("0x{0:X4}", utf.payloadSize + 8) + ")", "Warning", MessageBoxButtons.OK); } int nameOffset = StreamUtility.ReadIntFromStream(buffer, offset); offset += 4; uint temp = StreamUtility.ReadUIntFromStream(buffer, offset); offset += 4; int numProperties = (int)((temp >> 16) & 0xff); utf.itemDataLength = (int)(temp & 0xff); utf.numItem = StreamUtility.ReadIntFromStream(buffer, offset); offset += 4; // Optional check if ((utf.itemDataLength * utf.numItem) != (utf.stringBankOffset - utf.dataBankOffset)) { MessageBox.Show( String.Format("Calculated data bank size (0x{0:X4}) does not match specified size (0x{1:X4})", utf.stringBankOffset - utf.dataBankOffset, utf.itemDataLength * utf.numItem), "Warning", MessageBoxButtons.OK); } { // Extract string bank int address = (int)utf.stringBankOffset; utf.stringBank = new Dictionary <int, string>(); while (address < utf.endOfPayloadOffset) { string value = StreamUtility.ReadStringFromStream(buffer, address, 0); utf.stringBank.Add((address - (int)utf.stringBankOffset), value); address += value.Length + 1; } } // Get name of this UTF utf.name = utf.GetStringFromBank(nameOffset); // Extract items utf.properties = new CriPack_UTFProperty[utf.numItem][]; for (int i = 0; i < utf.numItem; i++) { // Extract properties utf.properties[i] = new CriPack_UTFProperty[numProperties]; long itemOffset = offset; int dataBankCursor = (int)utf.dataBankOffset + utf.itemDataLength * i; for (int p = 0; p < numProperties; p++) { // Read flag byte[] bytes = StreamUtility.ReadBytesFromStream(buffer, itemOffset, 1); itemOffset += 1; byte flag = bytes[0]; // Determine storage and type CriPack_UTFProperty property = new CriPack_UTFProperty(); property.storage = (CriPack_DataStorage)(flag & (byte)CriPack_DataStorage.MASK); property.type = (CriPack_DataType)(flag & (byte)CriPack_DataType.MASK); if (property.storage == CriPack_DataStorage.UNDEFINED) { throw new NotSupportedException(String.Format("Unrecognized storage location: 0x{0:X4}", (byte)property.storage)); } // Extract name of the property { int address = StreamUtility.ReadIntFromStream(buffer, itemOffset); itemOffset += 4; property.name = utf.GetStringFromBank(address); } // Extract value if (property.storage == CriPack_DataStorage.NONE) { // No value associated } else if (property.storage == CriPack_DataStorage.STRING) { // String value int address = StreamUtility.ReadIntFromStream(buffer, itemOffset); itemOffset += 4; property.value = utf.GetStringFromBank(address); } else if (property.storage == CriPack_DataStorage.DATA) { int address; // Data value specified by 'type' switch (property.type) { case CriPack_DataType.UNSIGNED_BYTE: case CriPack_DataType.SIGNED_BYTE: bytes = StreamUtility.ReadBytesFromStream(buffer, dataBankCursor, 1); dataBankCursor += 1; property.value = bytes[0]; break; case CriPack_DataType.UNSIGNED_SHORT: bytes = StreamUtility.ReadBytesFromStream(buffer, dataBankCursor, 2); dataBankCursor += 2; Array.Reverse(bytes); // Change endian property.value = BitConverter.ToUInt16(bytes, 0); break; case CriPack_DataType.SIGNED_SHORT: bytes = StreamUtility.ReadBytesFromStream(buffer, dataBankCursor, 2); dataBankCursor += 2; Array.Reverse(bytes); // Change endian property.value = BitConverter.ToInt16(bytes, 0); break; case CriPack_DataType.UNSIGNED_INT: property.value = StreamUtility.ReadUIntFromStream(buffer, dataBankCursor); dataBankCursor += 4; break; case CriPack_DataType.SIGNED_INT: property.value = StreamUtility.ReadIntFromStream(buffer, dataBankCursor); dataBankCursor += 4; break; case CriPack_DataType.UNSIGNED_LONG: bytes = StreamUtility.ReadBytesFromStream(buffer, dataBankCursor, 8); dataBankCursor += 8; Array.Reverse(bytes); // Change endian property.value = BitConverter.ToUInt64(bytes, 0); break; case CriPack_DataType.SIGNED_LONG: bytes = StreamUtility.ReadBytesFromStream(buffer, dataBankCursor, 8); dataBankCursor += 8; Array.Reverse(bytes); // Change endian property.value = BitConverter.ToInt64(bytes, 0); break; case CriPack_DataType.FLOAT: bytes = StreamUtility.ReadBytesFromStream(buffer, dataBankCursor, 4); dataBankCursor += 4; Array.Reverse(bytes); // Change endian property.value = BitConverter.ToSingle(bytes, 0); break; case CriPack_DataType.DOUBLE: bytes = StreamUtility.ReadBytesFromStream(buffer, dataBankCursor, 8); dataBankCursor += 8; Array.Reverse(bytes); // Change endian property.value = BitConverter.ToDouble(bytes, 0); break; case CriPack_DataType.STRING: address = StreamUtility.ReadIntFromStream(buffer, dataBankCursor); dataBankCursor += 4; property.value = utf.GetStringFromBank(address); break; case CriPack_DataType.DATA: address = StreamUtility.ReadIntFromStream(buffer, dataBankCursor); dataBankCursor += 4; int size = StreamUtility.ReadIntFromStream(buffer, dataBankCursor); dataBankCursor += 4; // Assume this is another table bytes = StreamUtility.ReadBytesFromStream(buffer, address, size); property.value = CriPack_UTF.ByteUnpack(bytes); break; default: property.value = null; break; } } utf.properties[i][p] = property; } } return(utf); } }