/// <summary> /// compress an entire pcc into a byte array. /// </summary> /// <param name="uncompressedPcc">uncompressed pcc stream.</param> /// <returns>a compressed array of bytes.</returns> public static Stream Compress(Stream uncompressedPcc) { uncompressedPcc.Position = 0; var magic = uncompressedPcc.ReadValueU32(Endian.Little); if (magic != 0x9E2A83C1 && magic.Swap() != 0x9E2A83C1) { throw new FormatException("not a pcc package"); } var endian = magic == 0x9E2A83C1 ? Endian.Little : Endian.Big; var encoding = endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode; var versionLo = uncompressedPcc.ReadValueU16(endian); var versionHi = uncompressedPcc.ReadValueU16(endian); if (versionLo != 684 && versionHi != 194) { throw new FormatException("unsupported version"); } uncompressedPcc.Seek(4, SeekOrigin.Current); var folderNameLength = uncompressedPcc.ReadValueS32(endian); var folderNameByteLength = folderNameLength >= 0 ? folderNameLength : (-folderNameLength * 2); uncompressedPcc.Seek(folderNameByteLength, SeekOrigin.Current); var packageFlagsOffset = uncompressedPcc.Position; var packageFlags = uncompressedPcc.ReadValueU32(endian); if ((packageFlags & 8) != 0) { uncompressedPcc.Seek(4, SeekOrigin.Current); } var nameCount = uncompressedPcc.ReadValueU32(endian); var namesOffset = uncompressedPcc.ReadValueU32(endian); var exportCount = uncompressedPcc.ReadValueU32(endian); var exportInfosOffset = uncompressedPcc.ReadValueU32(endian); SortedDictionary <uint, uint> exportDataOffsets = new SortedDictionary <uint, uint>(); Stream data; if ((packageFlags & 0x02000000) == 0) { data = uncompressedPcc; } else { throw new FormatException("pcc data is compressed"); } // get info about export data, sizes and offsets data.Seek(exportInfosOffset, SeekOrigin.Begin); for (uint i = 0; i < exportCount; i++) { var classIndex = data.ReadValueS32(endian); data.Seek(4, SeekOrigin.Current); var outerIndex = data.ReadValueS32(endian); var objectNameIndex = data.ReadValueS32(endian); data.Seek(16, SeekOrigin.Current); uint exportDataSize = data.ReadValueU32(endian); uint exportDataOffset = data.ReadValueU32(endian); exportDataOffsets.Add(exportDataOffset, exportDataSize); data.Seek(4, SeekOrigin.Current); var count = data.ReadValueU32(endian); data.Seek(count * 4, SeekOrigin.Current); data.Seek(20, SeekOrigin.Current); } const uint maxBlockSize = 0x100000; Stream outputStream = new MemoryStream(); // copying pcc header byte[] buffer = new byte[130]; uncompressedPcc.Seek(0, SeekOrigin.Begin); uncompressedPcc.Read(buffer, 0, 130); outputStream.Write(buffer, 0, buffer.Length); //add compressed pcc flag uncompressedPcc.Seek(12, SeekOrigin.Begin); folderNameLength = uncompressedPcc.ReadValueS32(); folderNameByteLength = folderNameLength >= 0 ? folderNameLength : (-folderNameLength * 2); uncompressedPcc.Seek(folderNameByteLength, SeekOrigin.Current); outputStream.Seek(uncompressedPcc.Position, SeekOrigin.Begin); packageFlags = uncompressedPcc.ReadValueU32(); packageFlags |= 0x02000000; // add compression flag outputStream.WriteValueU32(packageFlags); outputStream.Seek(buffer.Length, SeekOrigin.Begin); long outOffsetData; long outOffsetBlockInfo; long inOffsetData = namesOffset; List <int> blockSizes = new List <int>(); int countSize = (int)(exportDataOffsets.Min(obj => obj.Key) - namesOffset); //count the number of blocks and relative sizes uint lastOffset = exportDataOffsets.Min(obj => obj.Key); foreach (KeyValuePair <uint, uint> exportInfo in exportDataOffsets) { // part that adds empty spaces (leaved when editing export data and moved to the end of pcc) into the count if (exportInfo.Key != lastOffset) { int emptySpace = (int)(exportInfo.Key - lastOffset); if (countSize + emptySpace > maxBlockSize) { blockSizes.Add(countSize); countSize = 0; } else { countSize += emptySpace; } } // adds export data into the count if (countSize + exportInfo.Value > maxBlockSize) { blockSizes.Add(countSize); countSize = (int)exportInfo.Value; } else { countSize += (int)exportInfo.Value; } lastOffset = exportInfo.Key + exportInfo.Value; } blockSizes.Add(countSize); outputStream.WriteValueS32(blockSizes.Count); outOffsetBlockInfo = outputStream.Position; outOffsetData = namesOffset + (blockSizes.Count * 16); uncompressedPcc.Seek(namesOffset, SeekOrigin.Begin); //divide the block in segments for (int i = 0; i < blockSizes.Count; i++) { int currentUncBlockSize = blockSizes[i]; outputStream.Seek(outOffsetBlockInfo, SeekOrigin.Begin); outputStream.WriteValueU32((uint)uncompressedPcc.Position); outputStream.WriteValueS32(currentUncBlockSize); outputStream.WriteValueU32((uint)outOffsetData); byte[] inputBlock = new byte[currentUncBlockSize]; uncompressedPcc.Read(inputBlock, 0, currentUncBlockSize); byte[] compressedBlock = ZBlock.Compress(inputBlock, 0, inputBlock.Length); outputStream.WriteValueS32(compressedBlock.Length); outOffsetBlockInfo = outputStream.Position; outputStream.Seek(outOffsetData, SeekOrigin.Begin); outputStream.Write(compressedBlock, 0, compressedBlock.Length); outOffsetData = outputStream.Position; } //copying some unknown values + extra names list int bufferSize = (int)namesOffset - 0x86; buffer = new byte[bufferSize]; uncompressedPcc.Seek(0x86, SeekOrigin.Begin); uncompressedPcc.Read(buffer, 0, buffer.Length); outputStream.Seek(outOffsetBlockInfo, SeekOrigin.Begin); outputStream.Write(buffer, 0, buffer.Length); outputStream.Seek(0, SeekOrigin.Begin); return(outputStream); }
public void replaceImage(string strImgSize, ImageFile im, string archiveDir) { ImageSize imgSize = ImageSize.stringToSize(strImgSize); if (!imgList.Exists(img => img.imgSize == imgSize)) { throw new FileNotFoundException("Image with resolution " + imgSize + " isn't found"); } int imageIdx = privateImageList.FindIndex(img => img.imgSize == imgSize); ImageInfo imgInfo = privateImageList[imageIdx]; ImageFile imgFile = im; ImageEngineFormat imgFileFormat = Textures.Methods.ParseFormat(imgFile.format); // check if images have same format type if (texFormat != imgFileFormat) { bool res = KFreonLib.Misc.Methods.DisplayYesNoDialogBox("Warning, replacing image has format " + imgFile.subtype() + " while original has " + texFormat + ", would you like to replace it anyway?", "Warning, different image format found"); if (res) { imgFile.format = Textures.Methods.StringifyFormat(texFormat); } else { return; } //throw new FormatException("Different image format, original is " + texFormat + ", new is " + imgFile.subtype()); } byte[] imgBuffer; // if the image is empty then recover the archive compression from the image list if (imgInfo.storageType == storage.empty) { imgInfo.storageType = privateImageList.Find(img => img.storageType != storage.empty && img.storageType != storage.pccSto).storageType; imgInfo.uncSize = imgFile.resize().Length; imgInfo.cprSize = imgFile.resize().Length; } switch (imgInfo.storageType) { case storage.arcCpr: case storage.arcUnc: string archivePath = archiveDir + "\\" + arcName + ".tfc"; if (!File.Exists(archivePath)) { throw new FileNotFoundException("Texture archive not found in " + archivePath); } if (getFileFormat() == ".tga") { imgBuffer = imgFile.resize(); // shrink image to essential data } else { imgBuffer = imgFile.imgData; } if (imgBuffer.Length != imgInfo.uncSize) { throw new FormatException("image sizes do not match, original is " + imgInfo.uncSize + ", new is " + imgBuffer.Length); } using (FileStream archiveStream = new FileStream(archivePath, FileMode.Append, FileAccess.Write)) { int newOffset = (int)archiveStream.Position; if (imgInfo.storageType == storage.arcCpr) { imgBuffer = ZBlock.Compress(imgBuffer); /*byte[] compressed = ZBlock.Compress(imgBuffer); * archiveStream.Write(compressed, 0, compressed.Length);*/ imgInfo.cprSize = imgBuffer.Length; } //else archiveStream.Write(imgBuffer, 0, imgBuffer.Length); imgInfo.offset = newOffset; } break; case storage.pccSto: imgBuffer = imgFile.imgData; // copy image data as-is if (imgBuffer.Length != imgInfo.uncSize) { throw new FormatException("image sizes do not match, original is " + imgInfo.uncSize + ", new is " + imgBuffer.Length); } using (MemoryStream dataStream = new MemoryStream(imageData)) { dataStream.Seek(imgInfo.offset, SeekOrigin.Begin); dataStream.Write(imgBuffer, 0, imgBuffer.Length); } break; } imgList[imageIdx] = imgInfo; }
public void replaceImage(string strImgSize, string fileToReplace, string archiveDir) { ImageSize imgSize = ImageSize.stringToSize(strImgSize); if (!imgList.Exists(img => img.imgSize == imgSize)) { throw new FileNotFoundException("Image with resolution " + imgSize + " isn't found"); } int imageIdx = imgList.FindIndex(img => img.imgSize == imgSize); ImageInfo imgInfo = imgList[imageIdx]; if (!File.Exists(fileToReplace)) { throw new FileNotFoundException("invalid file to replace: " + fileToReplace); } // check if replacing image is supported ImageFile imgFile; string fileFormat = Path.GetExtension(fileToReplace); switch (fileFormat) { case ".dds": imgFile = new DDS(fileToReplace, null); break; case ".tga": imgFile = new TGA(fileToReplace, null); break; default: throw new FileFormatException(fileFormat + " image extension not supported"); } // check if images have same format type if (texFormat != imgFile.format) { DialogResult selection = MessageBox.Show("Warning, replacing image has format " + imgFile.subtype() + " while original has " + texFormat + ", would you like to replace it anyway?", "Warning, different image format found", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (selection == DialogResult.Yes) { imgFile.format = texFormat; } else { return; } //throw new FormatException("Different image format, original is " + texFormat + ", new is " + imgFile.subtype()); } byte[] imgBuffer; // if the image is empty then recover the archive compression from the image list if (imgInfo.storageType == storage.empty) { imgInfo.storageType = imgList.Find(img => img.storageType != storage.empty && img.storageType != storage.pccSto).storageType; imgInfo.uncSize = imgFile.resize().Length; imgInfo.cprSize = imgFile.resize().Length; } switch (imgInfo.storageType) { case storage.arcCpr: case storage.arcUnc: string archivePath = archiveDir + "\\" + arcName + ".tfc"; if (!File.Exists(archivePath)) { throw new FileNotFoundException("Texture archive not found in " + archivePath); } if (getFileFormat() == ".tga") { imgBuffer = imgFile.resize(); // shrink image to essential data } else { imgBuffer = imgFile.imgData; } if (imgBuffer.Length != imgInfo.uncSize) { throw new FormatException("image sizes do not match, original is " + imgInfo.uncSize + ", new is " + imgBuffer.Length); } using (FileStream archiveStream = new FileStream(archivePath, FileMode.Append, FileAccess.Write)) { int newOffset = (int)archiveStream.Position; if (imgInfo.storageType == storage.arcCpr) { imgBuffer = ZBlock.Compress(imgBuffer); /*byte[] compressed = ZBlock.Compress(imgBuffer); * archiveStream.Write(compressed, 0, compressed.Length);*/ imgInfo.cprSize = imgBuffer.Length; } //else archiveStream.Write(imgBuffer, 0, imgBuffer.Length); imgInfo.offset = newOffset; } break; case storage.pccSto: imgBuffer = imgFile.imgData; // copy image data as-is if (imgBuffer.Length != imgInfo.uncSize) { throw new FormatException("image sizes do not match, original is " + imgInfo.uncSize + ", new is " + imgBuffer.Length); } using (MemoryStream dataStream = new MemoryStream(imageData)) { dataStream.Seek(imgInfo.offset, SeekOrigin.Begin); dataStream.Write(imgBuffer, 0, imgBuffer.Length); } break; } imgList[imageIdx] = imgInfo; }
public void ImportFromFile(int tfc) { OpenFileDialog Dialog = new OpenFileDialog(); if (T2D.PixelFormat == "PF_DXT1\0" || T2D.PixelFormat == "PF_DXT5\0") { Dialog.Filter = "DDS Files (*.dds)|*.dds"; } if (T2D.PixelFormat == "PF_G8\0" || T2D.PixelFormat == "PF_V8U8\0" || T2D.PixelFormat == "PF_A8R8G8B8\0") { Dialog.Filter = "TGA Files (*.tga)|*.tga"; } int format = -1; if (T2D.PixelFormat == "PF_DXT1\0") { format = 0; } if (T2D.PixelFormat == "PF_DXT5\0") { format = 1; } if (T2D.PixelFormat == "PF_V8U8\0") { format = 2; } if (T2D.PixelFormat == "PF_A8R8G8B8\0") { format = 3; } if (T2D.PixelFormat == "PF_G8\0") { format = 4; } if (!TextureTFCs[tfc].InCache) { if (Dialog.ShowDialog() == DialogResult.OK) { if (format == 0 || format == 1) { ImageFile t = new ImageFile(); t.ImportFromFile(Dialog.FileName); if (t.ImageSizeX == TextureTFCs[tfc].sizeX && t.ImageSizeY == TextureTFCs[tfc].sizeY && t.ImageFormat == format && t.memsize == TextureTFCs[tfc].size) { for (int i = 0; i < t.memsize; i++) { memory[TextureTFCs[tfc].offset + i] = t.memory[i]; } MessageBox.Show("Done."); } } if (format > 1 && format < 5) { ImageFile t = new ImageFile(); t.ImportFromFile(Dialog.FileName); if (t.ImageSizeX == TextureTFCs[tfc].sizeX && t.ImageSizeY == TextureTFCs[tfc].sizeY) { if (t.ImageBits != 32) { MessageBox.Show("Please use 32 bit Targa image!"); } else { switch (format) { case 2: for (int i = 0; i < t.memsize / 4; i++) { memory[TextureTFCs[tfc].offset + i * 2] = t.memory[i * 4]; memory[TextureTFCs[tfc].offset + i * 2 + 1] = t.memory[i * 4 + 2]; } break; case 3: for (int i = 0; i < t.memsize; i++) { memory[TextureTFCs[tfc].offset + i] = t.memory[i]; } break; case 4: for (int i = 0; i < t.memsize / 4; i++) { memory[TextureTFCs[tfc].offset + i] = t.memory[i * 4]; } break; } MessageBox.Show("Done."); } } } } } else if (Dialog.ShowDialog() == DialogResult.OK) { ImageFile t = new ImageFile(); t.ImportFromFile(Dialog.FileName); if ((format == 0 || format == 1) && t.ImageFormat == format) { if (t.ImageSizeX == TextureTFCs[tfc].sizeX && t.ImageSizeY == TextureTFCs[tfc].sizeY) { TFCFile TFCf = new TFCFile(T2D.Tsource); byte[] buff; if (TFCf.isTFCCompressed()) { buff = ZBlock.Compress(t.memory); } else { buff = t.memory; } byte[] buff2 = BitConverter.GetBytes(TFCf.getFileSize()); for (int i = 0; i < 4; i++) { memory[TextureTFCs[tfc].HeaderOffset + 20 + i] = buff2[i]; } buff2 = BitConverter.GetBytes(buff.Length); for (int i = 0; i < 4; i++) { memory[TextureTFCs[tfc].HeaderOffset + 16 + i] = buff2[i]; } TFCf.AppendToTFC(buff); int size = TFCf.getFileSize(); if (size != -1) { TOCeditor tc = new TOCeditor(); if (!tc.UpdateFile(T2D.Tsource + ".tfc", (uint)size)) { MessageBox.Show("Didn't found Entry"); } tc.Close(); } } else { System.Windows.Forms.DialogResult m = MessageBox.Show("The size doesn't match, import anyway?", "ME3 Explorer", MessageBoxButtons.YesNo); if (m == DialogResult.Yes) { TFCFile TFCf = new TFCFile(T2D.Tsource); byte[] buff; if (TFCf.isTFCCompressed()) { buff = ZBlock.Compress(t.memory); } else { buff = t.memory; } byte[] buff2 = BitConverter.GetBytes(TFCf.getFileSize()); for (int i = 0; i < 4; i++) { memory[TextureTFCs[tfc].HeaderOffset + 20 + i] = buff2[i]; } buff2 = BitConverter.GetBytes(buff.Length); for (int i = 0; i < 4; i++) { memory[TextureTFCs[tfc].HeaderOffset + 16 + i] = buff2[i]; } if (tfc == 0) { buff2 = BitConverter.GetBytes(t.ImageSizeX); for (int i = 0; i < 4; i++) { memory[T2D.offSizeX + i] = buff2[i]; } buff2 = BitConverter.GetBytes(t.ImageSizeY); for (int i = 0; i < 4; i++) { memory[T2D.offSizeY + i] = buff2[i]; } } else { buff2 = BitConverter.GetBytes(t.ImageSizeX * 2); for (int i = 0; i < 4; i++) { memory[TextureTFCs[tfc].HeaderOffset + i] = buff2[i]; } buff2 = BitConverter.GetBytes(t.ImageSizeY * 2); for (int i = 0; i < 4; i++) { memory[TextureTFCs[tfc].HeaderOffset + 4 + i] = buff2[i]; } TFCf.AppendToTFC(buff); } TFCf.AppendToTFC(buff); int size = TFCf.getFileSize(); if (size != -1) { TOCeditor tc = new TOCeditor(); if (!tc.UpdateFile(T2D.Tsource + ".tfc", (uint)size)) { MessageBox.Show("Didn't found Entry"); } tc.Close(); } } } } if (format > 1 && format < 5) { if (t.ImageSizeX == TextureTFCs[tfc].sizeX && t.ImageSizeY == TextureTFCs[tfc].sizeY) { if (t.ImageBits != 32) { MessageBox.Show("Please use 32 bit Targa image!"); } else { byte[] buf = new byte[0]; switch (format) { case 2: buf = new byte[t.memsize / 2]; for (int i = 0; i < t.memsize / 4; i++) { buf[i * 2] = t.memory[i * 4]; buf[i * 2 + 1] = t.memory[i * 4 + 2]; } break; case 3: buf = t.memory; break; case 4: buf = new byte[t.memsize / 4]; for (int i = 0; i < t.memsize / 4; i++) { buf[i] = t.memory[i * 4]; } break; } TFCFile TFCf = new TFCFile(T2D.Tsource); byte[] buff; if (TFCf.isTFCCompressed()) { buff = ZBlock.Compress(t.memory); } else { buff = t.memory; } byte[] buff2 = BitConverter.GetBytes(TFCf.getFileSize()); for (int i = 0; i < 4; i++) { memory[TextureTFCs[tfc].HeaderOffset + 20 + i] = buff2[i]; } buff2 = BitConverter.GetBytes(buff.Length); for (int i = 0; i < 4; i++) { memory[TextureTFCs[tfc].HeaderOffset + 12 + i] = buff2[i]; } TFCf.AppendToTFC(buff); int size = TFCf.getFileSize(); if (size != -1) { TOCeditor tc = new TOCeditor(); if (!tc.UpdateFile(T2D.Tsource + ".tfc", (uint)size)) { MessageBox.Show("Didn't found Entry"); } tc.Close(); } MessageBox.Show("Done."); } } } } }