public void extractImage(ImageInfo imgInfo, string archiveDir = null, string fileName = null) { ImageFile imgFile; if (fileName == null) { fileName = texName + "_" + imgInfo.imgSize + getFileFormat(); } byte[] imgBuffer; switch (imgInfo.storageType) { case storage.pccSto: imgBuffer = new byte[imgInfo.uncSize]; Buffer.BlockCopy(imageData, imgInfo.offset, imgBuffer, 0, imgInfo.uncSize); break; case storage.arcCpr: case storage.arcUnc: string archivePath = archiveDir + "\\" + arcName + ".tfc"; if (!File.Exists(archivePath)) { throw new FileNotFoundException("Texture archive not found in " + archivePath); } using (FileStream archiveStream = File.OpenRead(archivePath)) { archiveStream.Seek(imgInfo.offset, SeekOrigin.Begin); if (imgInfo.storageType == storage.arcCpr) { imgBuffer = ZBlock.Decompress(archiveStream, imgInfo.cprSize); } else { imgBuffer = new byte[imgInfo.uncSize]; archiveStream.Read(imgBuffer, 0, imgBuffer.Length); } } break; default: throw new FormatException("Unsupported texture storage type"); } if (getFileFormat() == ".dds") { imgFile = new DDS(fileName, imgInfo.imgSize, texFormat, imgBuffer); } else { imgFile = new TGA(fileName, imgInfo.imgSize, texFormat, imgBuffer); } byte[] saveImg = imgFile.ToArray(); using (FileStream outputImg = new FileStream(imgFile.fileName, FileMode.Create, FileAccess.Write)) outputImg.Write(saveImg, 0, saveImg.Length); }
/// <summary> /// given export data offset, the function recovers it from the file. /// </summary> /// <param name="offset">offset position of desired export data</param> private void getData(int offset, ExportEntry exp = null) { byte[] buffer; if (bCompressed) { Block selected = blockList.Find(block => block.uncOffset <= offset && block.uncOffset + block.uncSize > offset); byte[] uncBlock; using (FileStream pccStream = File.OpenRead(pccFileName)) { pccStream.Seek(selected.cprOffset, SeekOrigin.Begin); uncBlock = ZBlock.Decompress(pccStream, selected.cprSize); // the selected block has been read selected.bRead = true; } // fill all the exports data extracted from the uncBlock foreach (ExportEntry expInfo in Exports) { if (expInfo.DataOffset >= selected.uncOffset && expInfo.DataOffset + expInfo.DataSize <= selected.uncOffset + selected.uncSize) { buffer = new byte[expInfo.DataSize]; Buffer.BlockCopy(uncBlock, expInfo.DataOffset - selected.uncOffset, buffer, 0, expInfo.DataSize); expInfo.Data = buffer; } } } else { ExportEntry expSelect; if (exp == null) { int expIndex = Exports.FindIndex(export => export.DataOffset <= offset && export.DataOffset + export.DataSize > offset); expSelect = Exports[expIndex]; } else { expSelect = exp; } using (FileStream pccStream = File.OpenRead(pccFileName)) { buffer = new byte[expSelect.DataSize]; pccStream.Seek(expSelect.DataOffset, SeekOrigin.Begin); pccStream.Read(buffer, 0, buffer.Length); expSelect.Data = buffer; } } }
public byte[] extractRawData(ImageInfo imgInfo, string archiveDir = null) { byte[] imgBuffer; switch (imgInfo.storageType) { case storage.pccSto: imgBuffer = new byte[imgInfo.uncSize]; System.Buffer.BlockCopy(imageData, imgInfo.offset, imgBuffer, 0, imgInfo.uncSize); break; case storage.arcCpr: case storage.arcUnc: string archivePath = GetTFC(arcName); if (archivePath != null && File.Exists(archivePath)) { Console.WriteLine("Loaded texture from tfc '" + archivePath + "'."); using (FileStream archiveStream = File.OpenRead(archivePath)) { archiveStream.Seek(imgInfo.offset, SeekOrigin.Begin); if (imgInfo.storageType == storage.arcCpr) { imgBuffer = ZBlock.Decompress(archiveStream, imgInfo.cprSize); } else { imgBuffer = new byte[imgInfo.uncSize]; archiveStream.Read(imgBuffer, 0, imgBuffer.Length); } } } else { //how do i put default unreal texture imgBuffer = null; //this will cause exception that will bubble up. } break; default: throw new FormatException("Unsupported texture storage type"); } return(imgBuffer); //cannot be uninitialized. }
/// <summary> /// decompress an entire pcc file. /// </summary> /// <param name="input">pcc file passed in stream format</param> /// <returns>a decompressed array of bytes</returns> public static byte[] Decompress(Stream input) { input.Seek(0, SeekOrigin.Begin); var magic = input.ReadValueU32(Endian.Little); if (magic != 0x9E2A83C1 && magic.Swap() != 0x9E2A83C1) { throw new FormatException("not a pcc file"); } var endian = magic == 0x9E2A83C1 ? Endian.Little : Endian.Big; var versionLo = input.ReadValueU16(endian); var versionHi = input.ReadValueU16(endian); if (versionLo != 684 && versionHi != 194) { throw new FormatException("unsupported pcc version"); } long headerSize = 8; input.Seek(4, SeekOrigin.Current); headerSize += 4; var folderNameLength = input.ReadValueS32(endian); headerSize += 4; var folderNameByteLength = folderNameLength >= 0 ? folderNameLength : (-folderNameLength * 2); input.Seek(folderNameByteLength, SeekOrigin.Current); headerSize += folderNameByteLength; var packageFlagsOffset = input.Position; var packageFlags = input.ReadValueU32(endian); headerSize += 4; if ((packageFlags & 0x02000000u) == 0) { throw new FormatException("pcc file is already decompressed"); } if ((packageFlags & 8) != 0) { input.Seek(4, SeekOrigin.Current); headerSize += 4; } uint nameCount = input.ReadValueU32(endian); uint nameOffset = input.ReadValueU32(endian); input.Seek(52, SeekOrigin.Current); headerSize += 60; var generationsCount = input.ReadValueU32(endian); input.Seek(generationsCount * 12, SeekOrigin.Current); headerSize += generationsCount * 12; input.Seek(20, SeekOrigin.Current); headerSize += 24; var blockCount = input.ReadValueU32(endian); int headBlockOff = (int)input.Position; var afterBlockTableOffset = headBlockOff + (blockCount * 16); var indataOffset = afterBlockTableOffset + 8; byte[] buff; input.Seek(0, SeekOrigin.Begin); using (MemoryStream output = new MemoryStream()) { output.Seek(0, SeekOrigin.Begin); output.WriteFromStream(input, headerSize); output.WriteValueU32(0, endian); // block count input.Seek(afterBlockTableOffset, SeekOrigin.Begin); output.WriteFromStream(input, 8); //check if has extra name list (don't know it's usage...) if ((packageFlags & 0x10000000) != 0) { long curPos = output.Position; output.WriteFromStream(input, nameOffset - curPos); } for (int i = 0; i < blockCount; i++) { input.Seek(headBlockOff, SeekOrigin.Begin); var uncompressedOffset = input.ReadValueU32(endian); var uncompressedSize = input.ReadValueU32(endian); var compressedOffset = input.ReadValueU32(endian); var compressedSize = input.ReadValueU32(endian); headBlockOff = (int)input.Position; buff = new byte[compressedSize]; input.Seek(compressedOffset, SeekOrigin.Begin); input.Read(buff, 0, buff.Length); byte[] temp = ZBlock.Decompress(buff, 0, buff.Length); output.Seek(uncompressedOffset, SeekOrigin.Begin); output.Write(temp, 0, temp.Length); } output.Seek(packageFlagsOffset, SeekOrigin.Begin); output.WriteValueU32(packageFlags & ~0x02000000u, endian); return(output.ToArray()); } }
/// <summary> /// PCCObject class constructor. It also load namelist, importlist and exportinfo (not exportdata) from pcc file /// </summary> /// <param name="pccFilePath">full path + file name of desired pcc file.</param> public PCCObject(string pccFilePath, bool fullFileInMemory = false) { Loaded = true; pccFileName = Path.GetFullPath(pccFilePath); Debug.WriteLine("Loading pcc " + pccFileName); using (FileStream pccStream = File.OpenRead(pccFileName)) { Names = new List <string>(); Imports = new List <ImportEntry>(); Exports = new List <ExportEntry>(); pccStream.Read(header, 0, header.Length); if (magic != ZBlock.magic && magic.Swap() != ZBlock.magic) { throw new FormatException("not a pcc file"); } // BitConverter isn't working?!?! if (magic == 0x9E2A83C1) { BitConverter.IsLittleEndian = true; } else { BitConverter.IsLittleEndian = true; } if (lowVers != 684 && highVers != 194) { throw new FormatException("unsupported version"); } Stream listsStream; if (bCompressed) { Debug.WriteLine("COMPRESSED..."); // seeks the blocks info position pccStream.Seek(idxOffsets + 60, SeekOrigin.Begin); int generator = pccStream.ReadValueS32(); pccStream.Seek((generator * 12) + 20, SeekOrigin.Current); int blockCount = pccStream.ReadValueS32(); blockList = new List <Block>(); // creating the Block list for (int i = 0; i < blockCount; i++) { Block temp = new Block(); temp.uncOffset = pccStream.ReadValueS32(); temp.uncSize = pccStream.ReadValueS32(); temp.cprOffset = pccStream.ReadValueS32(); temp.cprSize = pccStream.ReadValueS32(); blockList.Add(temp); } // correcting the header, in case there's need to be saved Buffer.BlockCopy(BitConverter.GetBytes((int)0), 0, header, header.Length - 12, sizeof(int)); pccStream.Read(header, header.Length - 8, 8); headerEnd = (int)pccStream.Position; // copying the extraNamesList int extraNamesLenght = blockList[0].cprOffset - headerEnd; if (extraNamesLenght > 0) { extraNamesList = new byte[extraNamesLenght]; pccStream.Read(extraNamesList, 0, extraNamesLenght); //FileStream fileStream = File.Create(Path.GetDirectoryName(pccFileName) + "\\temp.bin"); //fileStream.Write(extraNamesList, 0, extraNamesLenght); //MessageBox.Show("posizione: " + pccStream.Position.ToString("X8")); } // decompress first block that holds infos about names, imports and exports pccStream.Seek(blockList[0].cprOffset, SeekOrigin.Begin); byte[] uncBlock = ZBlock.Decompress(pccStream, blockList[0].cprSize); // write decompressed block inside temporary stream listsStream = new MemoryStream(); listsStream.Seek(blockList[0].uncOffset, SeekOrigin.Begin); listsStream.Write(uncBlock, 0, uncBlock.Length); } else { listsStream = pccStream; headerEnd = (int)NameOffset; // copying the extraNamesList int extraNamesLenght = headerEnd - headerSize; if (extraNamesLenght > 0) { extraNamesList = new byte[extraNamesLenght]; listsStream.Seek(headerSize, SeekOrigin.Begin); listsStream.Read(extraNamesList, 0, extraNamesLenght); //FileStream fileStream = File.Create(Path.GetDirectoryName(pccFileName) + "\\temp.bin"); //fileStream.Write(extraNamesList, 0, extraNamesLenght); //MessageBox.Show("posizione: " + pccStream.Position.ToString("X8")); } } /*if(bExtraNamesList) * { * int extraNamesListSize = namesOffset - headerEnd; * extraNamesList = new byte[extraNamesListSize]; * pccStream.Seek(headerEnd, SeekOrigin.Begin); * pccStream.Read(extraNamesList, 0, extraNamesList.Length); * }*/ // fill names list listsStream.Seek(NameOffset, SeekOrigin.Begin); for (int i = 0; i < NameCount; i++) { long currOffset = listsStream.Position; int strLength = listsStream.ReadValueS32(); string str = listsStream.ReadString(strLength * -2, true, Encoding.Unicode); //Debug.WriteLine("Read name "+i+" "+str+" length: " + strLength+", offset: "+currOffset); Names.Add(str); } //Debug.WriteLine("Names done. Current offset: "+listsStream.Position); //Debug.WriteLine("Import Offset: " + ImportOffset); // fill import list //Console.Out.WriteLine("IMPORT OFFSET: " + ImportOffset); listsStream.Seek(ImportOffset, SeekOrigin.Begin); byte[] buffer = new byte[ImportEntry.byteSize]; for (int i = 0; i < ImportCount; i++) { long offset = listsStream.Position; ImportEntry e = new ImportEntry(this, listsStream); Imports.Add(e); //Debug.WriteLine("Read import " + i + " " + e.ObjectName + ", offset: " + offset); } ; // fill export list (only the headers, not the data) listsStream.Seek(ExportOffset, SeekOrigin.Begin); //Console.Out.WriteLine("Export OFFSET: " + ImportOffset); for (int i = 0; i < ExportCount; i++) { uint expInfoOffset = (uint)listsStream.Position; listsStream.Seek(44, SeekOrigin.Current); int count = listsStream.ReadValueS32(); listsStream.Seek(-48, SeekOrigin.Current); int expInfoSize = 68 + (count * 4); buffer = new byte[expInfoSize]; listsStream.Read(buffer, 0, buffer.Length); ExportEntry e = new ExportEntry(this, buffer, expInfoOffset); //Debug.WriteLine("Read export " + i + " " + e.ObjectName + ", offset: " + expInfoOffset+ ", size: "+expInfoSize); Exports.Add(e); } } //Debug.WriteLine(getMetadataString()); }
public void ME3PCCObjectHelper(MemoryStream tempStream, string filePath, bool TablesOnly) { tempStream.Seek(0, SeekOrigin.Begin); DataStream = new MemoryStream(); tempStream.WriteTo(DataStream); Names = new List <string>(); Imports = new List <ME3ImportEntry>(); Exports = new List <ME3ExportEntry>(); header = tempStream.ReadBytes(headerSize); if (magic != ZBlock.magic && magic.Swap() != ZBlock.magic) { throw new FormatException(filePath + " is not a pcc file"); } if (lowVers != 684 && highVers != 194) { throw new FormatException("unsupported version"); } if (bCompressed) { // seeks the blocks info position tempStream.Seek(idxOffsets + 60, SeekOrigin.Begin); int generator = tempStream.ReadValueS32(); tempStream.Seek((generator * 12) + 20, SeekOrigin.Current); int blockCount = tempStream.ReadValueS32(); blockList = new List <Block>(); // creating the Block list for (int i = 0; i < blockCount; i++) { Block temp = new Block(); temp.uncOffset = tempStream.ReadValueS32(); temp.uncSize = tempStream.ReadValueS32(); temp.cprOffset = tempStream.ReadValueS32(); temp.cprSize = tempStream.ReadValueS32(); blockList.Add(temp); } // correcting the header, in case there's need to be saved Buffer.BlockCopy(BitConverter.GetBytes(0), 0, header, header.Length - 12, sizeof(int)); tempStream.Read(header, header.Length - 8, 8); headerEnd = (int)tempStream.Position; // copying the extraNamesList int extraNamesLenght = blockList[0].cprOffset - headerEnd; if (extraNamesLenght > 0) { extraNamesList = new byte[extraNamesLenght]; tempStream.Read(extraNamesList, 0, extraNamesLenght); //FileStream fileStream = File.Create(Path.GetDirectoryName(pccFileName) + "\\temp.bin"); //fileStream.Write(extraNamesList, 0, extraNamesLenght); //MessageBox.Show("posizione: " + pccStream.Position.ToString("X8")); } int dataStart = 0; using (MemoryStream he = new MemoryStream(header)) { he.Seek(0, SeekOrigin.Begin); he.ReadValueS32(); he.ReadValueS32(); dataStart = he.ReadValueS32(); } if (TablesOnly) { int TableStart = 0; for (int m = 0; m < blockList.Count; m++) { if (blockList[m].uncOffset + blockList[m].uncSize > dataStart) { TableStart = m; break; } } listsStream = new MemoryStream(); tempStream.Seek(blockList[TableStart].cprOffset, SeekOrigin.Begin); listsStream.Seek(blockList[TableStart].uncOffset, SeekOrigin.Begin); listsStream.WriteBytes(ZBlock.Decompress(tempStream, blockList[TableStart].cprSize)); DataStream = new MemoryStream(); tempStream.WriteTo(DataStream); bCompressed = true; } else { //Decompress ALL blocks listsStream = new MemoryStream(); for (int i = 0; i < blockCount; i++) { tempStream.Seek(blockList[i].cprOffset, SeekOrigin.Begin); listsStream.Seek(blockList[i].uncOffset, SeekOrigin.Begin); listsStream.WriteBytes(ZBlock.Decompress(tempStream, blockList[i].cprSize)); } } bCompressed = false; } else { listsStream = new MemoryStream(); listsStream.WriteBytes(tempStream.ToArray()); } tempStream.Dispose(); //Fill name list listsStream.Seek(NameOffset, SeekOrigin.Begin); for (int i = 0; i < NameCount; i++) { int strLength = listsStream.ReadValueS32(); Names.Add(listsStream.ReadString(strLength * -2, true, Encoding.Unicode)); } // fill import list listsStream.Seek(ImportOffset, SeekOrigin.Begin); byte[] buffer = new byte[ME3ImportEntry.byteSize]; for (int i = 0; i < ImportCount; i++) { Imports.Add(new ME3ImportEntry(this, listsStream)); } //fill export list listsStream.Seek(ExportOffset, SeekOrigin.Begin); for (int i = 0; i < ExportCount; i++) { uint expInfoOffset = (uint)listsStream.Position; listsStream.Seek(44, SeekOrigin.Current); int count = listsStream.ReadValueS32(); listsStream.Seek(-48, SeekOrigin.Current); int expInfoSize = 68 + (count * 4); buffer = new byte[expInfoSize]; listsStream.Read(buffer, 0, buffer.Length); Exports.Add(new ME3ExportEntry(this, buffer, expInfoOffset)); } }
// KFreon: Decompresses specific blocks if compressed, or returns data specified by offset and length. public byte[] Decompressor(uint offset, int length) { using (MemoryStream retval = new MemoryStream()) { uint newoffset = 0; /*if (blockList.Count == 1) * bCompressed = false;*/ /*if (bCompressed) * {*/ // KFreon: Find datablocks to decompress int DataStart = 0; int DataEnd = 0; int got = 0; for (int m = 0; m < blockList.Count; m++) { if (got == 0 && blockList[m].uncOffset + blockList[m].uncSize > offset) { DataStart = m; got++; } if (got == 1 && blockList[m].uncOffset + blockList[m].uncSize > offset + length) { DataEnd = m; got++; } if (got == 2) { break; } } if (DataEnd == 0 && DataStart != 0) { DataEnd = DataStart; } /*// KFreon: Move end along so as able to read * if (DataStart == DataEnd) * DataEnd++;*/ // KFreon: Decompress blocks newoffset = offset - (uint)blockList[DataStart].uncOffset; for (int i = DataStart; i <= DataEnd; i++) { DataStream.Seek(blockList[i].cprOffset, SeekOrigin.Begin); //retval.Seek(blockList[i].uncOffset, SeekOrigin.Begin); retval.WriteBytes(ZBlock.Decompress(DataStream, blockList[i].cprSize)); } /*} * else * { * listsStream.Seek(offset, SeekOrigin.Begin); * retval.ReadFrom(listsStream, length); * newoffset = 0; * }*/ //retval.Seek(offset, SeekOrigin.Begin); retval.Seek(newoffset, SeekOrigin.Begin); return(retval.ReadBytes(length)); } }