public byte[] CompressChunk(Chunk chunk) { int numBlocks = (chunk.Uncompressed.Length + maxBlockSize - 1) / maxBlockSize; if (numBlocks > 8) throw new FormatException("Maximum block number exceeded"); ChunkHeader head = new ChunkHeader(); head.magic = -1641380927; head.blocksize = maxBlockSize; head.uncompressedsize = chunk.Uncompressed.Length; int pos = 0; MemoryStream mem = new MemoryStream(); List<Block> blockList = new List<Block>(); int startData = 16 + 8 * numBlocks; mem.Seek(startData, SeekOrigin.Begin); for (int i = 0; i < numBlocks; i++) { Block block = new Block(); byte[] result, temp; if (i != numBlocks - 1) { block.uncompressedsize = maxBlockSize; temp = new byte[maxBlockSize]; } else { block.uncompressedsize = head.uncompressedsize - pos; temp = new byte[block.uncompressedsize]; } Buffer.BlockCopy(chunk.Uncompressed, pos, temp, 0, temp.Length); result = LZO1X.Compress(temp); if (result.Length == 0) throw new Exception("LZO compression error!"); block.compressedsize = result.Length; mem.WriteBytes(result); blockList.Add(block); pos += maxBlockSize; } head.compressedsize = (int)mem.Length; mem.Seek(0, SeekOrigin.Begin); mem.WriteValueS32(head.magic); mem.WriteValueS32(head.blocksize); mem.WriteValueS32(head.compressedsize); mem.WriteValueS32(head.uncompressedsize); foreach (Block block in blockList) { mem.WriteValueS32(block.compressedsize); mem.WriteValueS32(block.uncompressedsize); } return mem.ToArray(); }
public byte[] ToArray() { MemoryStream buffer = new MemoryStream(); buffer.WriteValueU32(firstVal); buffer.WriteValueS32(pccRef.Names.FindIndex(name => name == "None")); buffer.Seek(16, SeekOrigin.Begin); buffer.WriteValueU32(otherVal); buffer.WriteValueS32(enumTextureGroups.Count); foreach (ByteProp byteProp in enumTextureGroups) { buffer.WriteValueS32(pccRef.Names.FindIndex(name => name == byteProp.name)); buffer.WriteValueS32(byteProp.value); } return buffer.ToArray(); }
/* * Name function: Compress * Purpose: compress a part of the byte array into a Zlib Block * Input: - buffer: byte array * - offset: starting offset inside the array * - count: num of bytes to compress starting from the offset * Output: compressed byte array block, the structure is: * - magic word * - max segment size * - total compressed size * - total uncompressed size * - segment list * - compressed data list */ public static byte[] Compress(byte[] buffer, int offset, int count) { if(buffer == null) throw new ArgumentNullException(); if (count < 0) throw new FormatException(); if (offset + count > buffer.Length) throw new IndexOutOfRangeException(); MemoryStream headBlock = new MemoryStream(); MemoryStream dataBlock = new MemoryStream(); DeflaterOutputStream zipStream; int numSeg = (int)Math.Ceiling((double)count / (double)maxSegmentSize); headBlock.WriteValueU32(magic); headBlock.WriteValueU32(maxSegmentSize); headBlock.WriteValueU32(0x0); //total compressed size, still to calculate headBlock.WriteValueS32(count); //total uncompressed size for (int i = count; i > 0; i -= (int)maxSegmentSize) { int copyBytes = Math.Min(i, (int)maxSegmentSize); uint precCompSize = (uint)dataBlock.Length; zipStream = new DeflaterOutputStream(dataBlock); zipStream.Write(buffer, offset + (count - i), copyBytes); zipStream.Flush(); zipStream.Finish(); headBlock.WriteValueU32((uint)dataBlock.Length - precCompSize); //compressed segment size headBlock.WriteValueS32(copyBytes); //uncompressed segment size //Console.WriteLine(" Segment size: {0}, total read: {1}, compr size: {2}", maxSegmentSize, copyBytes, (uint)dataBlock.Length - precCompSize); } headBlock.Seek(8, SeekOrigin.Begin); headBlock.WriteValueS32((int)dataBlock.Length); // total compressed size byte[] finalBlock = new byte[headBlock.Length + dataBlock.Length]; Buffer.BlockCopy(headBlock.ToArray(), 0, finalBlock, 0, (int)headBlock.Length); Buffer.BlockCopy(dataBlock.ToArray(), 0, finalBlock, (int)headBlock.Length, (int)dataBlock.Length); headBlock.Close(); dataBlock.Close(); return finalBlock; }
public void addBiggerImage(string imagePathToAdd, string archiveDir) { ImageSize biggerImageSizeOnList = imgList.Max(image => image.imgSize); // check if replacing image is supported ImageFile imgFile; string fileFormat = Path.GetExtension(imagePathToAdd); switch (fileFormat) { case ".dds": imgFile = new DDS(imagePathToAdd, null); break; case ".tga": imgFile = new TGA(imagePathToAdd, null); break; default: throw new FileFormatException(fileFormat + " image extension not supported"); } // check if image to add is valid if(biggerImageSizeOnList.width * 2 != imgFile.imgSize.width || biggerImageSizeOnList.height * 2 != imgFile.imgSize.height) throw new FormatException("image size " + imgFile.imgSize + " isn't valid, must be " + new ImageSize(biggerImageSizeOnList.width * 2,biggerImageSizeOnList.height * 2)); // this check avoids insertion inside textures that have only 1 image stored inside pcc if(!imgList.Exists(img => img.storageType != storage.empty && img.storageType != storage.pccSto)) throw new Exception("Unable to add image, texture must have a reference to an external archive"); // !!! warning, this method breaks consistency between imgList and imageData[] !!! ImageInfo newImgInfo = new ImageInfo(); newImgInfo.storageType = imgList.Find(img => img.storageType != storage.empty && img.storageType != storage.pccSto).storageType; newImgInfo.imgSize = imgFile.imgSize; newImgInfo.uncSize = imgFile.resize().Length; newImgInfo.cprSize = 0x00; // not yet filled newImgInfo.offset = 0x00; // not yet filled imgList.Insert(0, newImgInfo); // insert new image on top of the list //now I let believe the program that I'm doing an image replace, saving lot of code ;) replaceImage(newImgInfo.imgSize.ToString(), imagePathToAdd, archiveDir); //updating num of images numMipMaps++; // update MipTailBaseIdx //PropertyReader.Property MipTail = properties["MipTailBaseIdx"]; int propVal = properties["MipTailBaseIdx"].Value.IntValue; propVal++; properties["MipTailBaseIdx"].Value.IntValue = propVal; //MessageBox.Show("raw size: " + properties["MipTailBaseIdx"].raw.Length + "\nproperty offset: " + properties["MipTailBaseIdx"].offsetval); using (MemoryStream rawStream = new MemoryStream(properties["MipTailBaseIdx"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["MipTailBaseIdx"].raw = rawStream.ToArray(); } //properties["MipTailBaseIdx"] = MipTail; // update Sizes //PropertyReader.Property Size = properties["SizeX"]; propVal = (int)newImgInfo.imgSize.width; properties["SizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["SizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeX"].raw = rawStream.ToArray(); } //properties["SizeX"] = Size; //Size = properties["SizeY"]; properties["SizeY"].Value.IntValue = (int)newImgInfo.imgSize.height; using (MemoryStream rawStream = new MemoryStream(properties["SizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeY"].raw = rawStream.ToArray(); } //properties["SizeY"] = Size; properties["OriginalSizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeX"].raw = rawStream.ToArray(); } properties["OriginalSizeY"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeY"].raw = rawStream.ToArray(); } }
public void OneImageToRuleThemAll(string archiveDir, ImageFile im, out string newTextureGroup, byte[] imgData) { newTextureGroup = null; ImageMipMapHandler imgMipMap = new ImageMipMapHandler("", imgData); // starts from the smaller image for (int i = imgMipMap.imageList.Count - 1; i >= 0; i--) { ImageFile newImageFile = imgMipMap.imageList[i]; // insert images only with size > 64 if (newImageFile.imgSize.width < 64 && newImageFile.imgSize.height < 64) continue; // if the image size exists inside the texture2d image list then we have to replace it if (imgList.Exists(img => img.imgSize == newImageFile.imgSize)) { // ...but at least for now I can reuse my replaceImage function... ;) replaceImage(newImageFile.imgSize.ToString(), newImageFile, archiveDir); } else // if the image doesn't exists then we have to add it { // ...and use my addBiggerImage function! :P addBiggerImage(newImageFile, archiveDir); } File.Delete(newImageFile.fileName); } // add texturegroup_world inside GamerSettings.ini in order to overwrite values ImageSize maxSize = imgList.Max(image => image.imgSize); uint maxValue = Math.Max(maxSize.width, maxSize.height); string section = "SystemSettings"; string key = "texturegroup_shadowmap"; string newValue = "(MinLODSize=128,MaxLODSize=" + maxValue + ",LODBias=0)"; IniFile iniFile = new IniFile(ME3Directory.GamerSettingsIniFile); string oldValue = iniFile.IniReadValue(section, key); if (oldValue == "") { iniFile.IniWriteValue(section, key, newValue); } else { char[] delimiters = new char[] { '=', ',' }; uint maxLODSize = Convert.ToUInt32(oldValue.Split(delimiters)[3]); if (maxValue > maxLODSize) iniFile.IniWriteValue(section, key, newValue); } // check that Texture2D has a TextureGroup if (!properties.ContainsKey("LODGroup")) return; // extracting values from LODGroup Property PropertyReader.Property LODGroup = properties["LODGroup"]; string textureGroupName = pccRef.Names[LODGroup.Value.IntValue]; string newTextureGroupName = "TEXTUREGROUP_Shadowmap"; textureGroupName = newTextureGroupName; if (!pccRef.Names.Exists(name => name == newTextureGroupName)) pccRef.Names.Add(newTextureGroupName); using (MemoryStream rawStream = new MemoryStream(LODGroup.raw)) { rawStream.Seek(32, SeekOrigin.Begin); rawStream.WriteValueS32(pccRef.Names.FindIndex(name => name == newTextureGroupName)); //rawStream.Seek(32, SeekOrigin.Begin); rawStream.WriteValueS32(0); properties["LODGroup"].raw = rawStream.ToArray(); } }
public void singleImageUpscale(ImageFile im, string archiveDir) { ImageSize biggerImageSizeOnList = privateimgList.Max(image => image.imgSize); // check if replacing image is supported ImageFile imgFile = im; ImageEngineFormat imageFileFormat = Textures.Methods.ParseFormat(imgFile.format); //NEW Check for correct image format if (texFormat != imageFileFormat) throw new FormatException("Different image format, original is " + texFormat + ", new is " + imgFile.subtype()); // !!! warning, this method breaks consistency between imgList and imageData[] !!! ImageInfo newImgInfo = new ImageInfo(); newImgInfo.storageType = privateimgList.Find(img => img.storageType != storage.empty && img.storageType != storage.pccSto).storageType; newImgInfo.imgSize = imgFile.imgSize; newImgInfo.uncSize = imgFile.resize().Length; newImgInfo.cprSize = 0x00; // not yet filled newImgInfo.offset = 0x00; // not yet filled //imgList.Insert(0, newImgInfo); // insert new image on top of the list privateimgList.RemoveAt(0); // Remove old single image and add new one privateimgList.Add(newImgInfo); //now I let believe the program that I'm doing an image replace, saving lot of code ;) replaceImage2(newImgInfo.imgSize.ToString(), im, archiveDir); // update Sizes //PropertyReader.Property Size = properties["SizeX"]; int propVal = (int)newImgInfo.imgSize.width; properties["SizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["SizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeX"].raw = rawStream.ToArray(); } //properties["SizeX"] = Size; //Size = properties["SizeY"]; properties["SizeY"].Value.IntValue = (int)newImgInfo.imgSize.height; using (MemoryStream rawStream = new MemoryStream(properties["SizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeY"].raw = rawStream.ToArray(); } //properties["SizeY"] = Size; properties["OriginalSizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeX"].raw = rawStream.ToArray(); } properties["OriginalSizeY"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeY"].raw = rawStream.ToArray(); } //this.hasChanged = true; }
public void removeImage() { privateimgList.RemoveAt(0); numMipMaps--; int propVal = properties["MipTailBaseIdx"].Value.IntValue; propVal--; properties["MipTailBaseIdx"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["MipTailBaseIdx"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["MipTailBaseIdx"].raw = rawStream.ToArray(); } propVal = (int)privateimgList[0].imgSize.width; properties["SizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["SizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeX"].raw = rawStream.ToArray(); } properties["SizeY"].Value.IntValue = (int)privateimgList[0].imgSize.height; using (MemoryStream rawStream = new MemoryStream(properties["SizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeY"].raw = rawStream.ToArray(); } properties["OriginalSizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeX"].raw = rawStream.ToArray(); } properties["OriginalSizeY"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeY"].raw = rawStream.ToArray(); } }
public void CopyImgList(ME3SaltTexture2D inTex, ME3PCCObject pcc) { imageData = inTex.imageData; privateimgList = inTex.privateimgList; numMipMaps = inTex.numMipMaps; //Copy Properties byte[] buff; using (MemoryStream tempMem = new MemoryStream()) { tempMem.WriteBytes(headerData); for (int i = 0; i < inTex.properties.Count; i++) { SaltPropertyReader.Property prop = inTex.properties.ElementAt(i).Value; if (prop.Name == "UnpackMin") { for (int j = 0; j < inTex.UnpackNum; j++) { tempMem.WriteValueS64(pcc.addName2(prop.Name)); tempMem.WriteValueS64(pcc.addName2(prop.TypeVal.ToString())); tempMem.WriteValueS32(prop.Size); tempMem.WriteValueS32(j); tempMem.WriteValueF32(prop.Value.FloatValue); } continue; } tempMem.WriteValueS64(pcc.addName2(prop.Name)); if (prop.Name == "None") continue; tempMem.WriteValueS64(pcc.addName2(prop.TypeVal.ToString())); tempMem.WriteValueS64(prop.Size); switch (prop.TypeVal) { case SaltPropertyReader.Type.FloatProperty: tempMem.WriteValueF32(prop.Value.FloatValue); break; case SaltPropertyReader.Type.IntProperty: tempMem.WriteValueS32(prop.Value.IntValue); break; case SaltPropertyReader.Type.NameProperty: tempMem.WriteValueS64(pcc.addName2(prop.Value.StringValue)); // Heff: Modified to handle name references. //var index = pcc.addName2(prop.Value.StringValue); //tempMem.WriteValueS32(index); //tempMem.WriteValueS32(prop.Value.NameValue.count); break; case SaltPropertyReader.Type.ByteProperty: tempMem.WriteValueS64(pcc.addName2(prop.Value.StringValue)); tempMem.WriteValueS32(pcc.addName2(prop.Value.String2)); byte[] footer = new byte[4]; Buffer.BlockCopy(prop.raw, prop.raw.Length - 4, footer, 0, 4); tempMem.WriteBytes(footer); break; case SaltPropertyReader.Type.BoolProperty: tempMem.WriteValueBoolean(prop.Value.Boolereno); break; case SaltPropertyReader.Type.StructProperty: tempMem.WriteValueS64(pcc.addName2(prop.Value.StringValue)); for (int k = 0; k < prop.Size; k++) tempMem.WriteByte((byte)prop.Value.Array[k].IntValue); break; default: throw new NotImplementedException("Property type: " + prop.TypeVal + ", not yet implemented. TELL ME ABOUT THIS!"); } } buff = tempMem.ToArray(); } properties = new Dictionary<string, SaltPropertyReader.Property>(); List<SaltPropertyReader.Property> tempProperties = SaltPropertyReader.ReadProp(pcc, buff, headerData.Length); for (int i = 0; i < tempProperties.Count; i++) { SaltPropertyReader.Property property = tempProperties[i]; if (property.Name == "UnpackMin") UnpackNum++; if (!properties.ContainsKey(property.Name)) properties.Add(property.Name, property); switch (property.Name) { case "Format": texFormat = Textures.Methods.ParseFormat(pcc.Names[property.Value.IntValue].Substring(3)); break; case "TextureFileCacheName": arcName = property.Value.NameValue.Name; break; case "LODGroup": LODGroup = property.Value.NameValue.Name; break; case "None": dataOffset = (uint)(property.offsetval + property.Size); break; } } // if "None" property isn't found throws an exception if (dataOffset == 0) throw new Exception("\"None\" property not found"); }
public void Save() { MemoryStream m = new MemoryStream(); SerializingContainer Container = new SerializingContainer(m); Container.isLoading = false; Serialize(Container); m = Container.Memory; MemoryStream res = new MemoryStream(); List<PropertyReader.Property> props = PropertyReader.getPropList(export); int start = props[props.Count - 1].offend; res.Write(export.Data, 0, start); res.WriteValueS32((int)m.Length); res.WriteStream(m); export.Data = res.ToArray(); }
public string saveToFile(bool fileOverwrite = true) { bChanged = false; string finalTocFile = fileOverwrite ? tocFilePath : tocFilePath + ".tmp"; using (FileStream newFileStream = File.Create(finalTocFile)) { newFileStream.WriteValueU32(0x3AB70C13); newFileStream.WriteValueS32(0x0); newFileStream.WriteValueS32(chunkList.Count); int chunkOffset = 12; int fileOffset = 12 + (chunkList.Count * 8); string lastFile = chunkList.Last(x => (x.fileList != null) && x.fileList.Count(/*y => y.exist*/) != 0).fileList.Last(/*z => z.exist*/).filePath; //foreach (chunk element in chunkList) for(int i = 0; i < chunkList.Count; i++) { chunk element = chunkList[i]; newFileStream.Seek(chunkOffset, SeekOrigin.Begin); if (element.countNextFiles == 0)// || element.fileList.Count(x => x.exist) == 0) { newFileStream.WriteValueS64(0x0); chunkOffset = (int)newFileStream.Position; } else { newFileStream.WriteValueS32(fileOffset - chunkOffset); newFileStream.WriteValueS32(element.fileList.Count/*(x => x.exist)*/); chunkOffset = (int)newFileStream.Position; newFileStream.Seek(fileOffset, SeekOrigin.Begin); //foreach (fileStruct fileElement in element.fileList.Where(x => x.exist)) for(int j = 0; j < element.fileList.Count; j++) { fileStruct fileElement = element.fileList[j]; //if (!fileElement.exist) // continue; MemoryStream buffer = new MemoryStream(fileElement.blockSize); { if (fileElement.filePath == lastFile) buffer.WriteValueS16(0x0); else buffer.WriteValueS16(fileElement.blockSize); buffer.WriteValueS16(fileElement.flag); buffer.WriteValueS32(fileElement.fileSize); buffer.WriteBytes(fileElement.sha1); buffer.WriteStringZ(fileElement.filePath); byte[] byteBuff = new byte[fileElement.blockSize]; buffer.ToArray().CopyTo(byteBuff, 0); newFileStream.WriteBytes(byteBuff); } //newFileStream.Seek(fileOffset, SeekOrigin.Begin); } fileOffset = (int)newFileStream.Position; } } } return finalTocFile; }
public PCCObject(String path, Boolean littleEndian=true) { lzo = new SaltLZOHelper(); fullname = path; BitConverter.IsLittleEndian = littleEndian; StreamHelpers.setIsLittleEndian(littleEndian); DebugOutput.PrintLn("Load file : " + path); pccFileName = Path.GetFullPath(path); MemoryStream tempStream = new MemoryStream(); if (!File.Exists(pccFileName)) throw new FileNotFoundException("PCC file not found"); using (FileStream fs = new FileStream(pccFileName, FileMode.Open, FileAccess.Read)) { FileInfo tempInfo = new FileInfo(pccFileName); tempStream.WriteFromStream(fs, tempInfo.Length); if (tempStream.Length != tempInfo.Length) { throw new FileLoadException("File not fully read in. Try again later"); } } tempStream.Seek(12, SeekOrigin.Begin); int tempNameSize = tempStream.ReadValueS32(); tempStream.Seek(64 + tempNameSize, SeekOrigin.Begin); int tempGenerator = tempStream.ReadValueS32(); tempStream.Seek(36 + tempGenerator * 12, SeekOrigin.Current); int tempPos = (int)tempStream.Position; NumChunks = tempStream.ReadValueS32(); tempStream.Seek(0, SeekOrigin.Begin); header = tempStream.ReadBytes(tempPos); tempStream.Seek(0, SeekOrigin.Begin); if (magic != ZBlock.magic && magic.Swap() != ZBlock.magic) { DebugOutput.PrintLn("Magic number incorrect: " + magic); throw new FormatException("This is not a pcc file. The magic number is incorrect."); } if (bCompressed) { DebugOutput.PrintLn("File is compressed"); { listsStream = lzo.DecompressPCC(tempStream, this); //Correct the header bCompressed = false; listsStream.Seek(0, SeekOrigin.Begin); listsStream.WriteBytes(header); //Set numblocks to zero listsStream.WriteValueS32(0); //Write the magic number listsStream.WriteValueS32(1026281201); //Write 8 bytes of 0 listsStream.WriteValueS32(0); listsStream.WriteValueS32(0); } } else { DebugOutput.PrintLn("File already decompressed. Reading decompressed data."); listsStream = tempStream; } ReadNames(listsStream); ReadImports(listsStream); ReadExports(listsStream); LoadExports(); }
public void Serialize(Stream output) { var saveGame = this.SaveGame; byte[] innerUncompressedBytes; using (var innerUncompressedData = new MemoryStream()) { saveGame.Compose(); try { ProtoBuf.Serializer.Serialize(innerUncompressedData, saveGame); } finally { saveGame.Decompose(); } innerUncompressedData.Position = 0; innerUncompressedBytes = innerUncompressedData.ReadBytes((uint)innerUncompressedData.Length); } byte[] innerCompressedBytes; using (var innerCompressedData = new MemoryStream()) { var endian = this.Endian; innerCompressedData.WriteValueS32(0, Endian.Big); innerCompressedData.WriteString("WSG"); innerCompressedData.WriteValueU32(2, endian); innerCompressedData.WriteValueU32(CRC32.Hash(innerUncompressedBytes, 0, innerUncompressedBytes.Length), endian); // crc32 innerCompressedData.WriteValueS32(innerUncompressedBytes.Length, endian); var encoder = new Huffman.Encoder(); encoder.Build(innerUncompressedBytes); innerCompressedData.WriteBytes(encoder.Encode(innerUncompressedBytes)); innerCompressedData.Position = 0; innerCompressedData.WriteValueU32((uint)(innerCompressedData.Length - 4), Endian.Big); innerCompressedData.Position = 0; innerCompressedBytes = innerCompressedData.ReadBytes((uint)innerCompressedData.Length); } byte[] compressedBytes; if (innerCompressedBytes.Length <= BlockSize) { compressedBytes = new byte[innerCompressedBytes.Length + (innerCompressedBytes.Length / 16) + 64 + 3]; var actualCompressedSize = compressedBytes.Length; var result = LZO.Compress(innerCompressedBytes, 0, innerCompressedBytes.Length, compressedBytes, 0, ref actualCompressedSize); if (result != LZO.ErrorCode.Success) { throw new SaveCorruptionException(string.Format("LZO compression failure ({0})", result)); } Array.Resize(ref compressedBytes, actualCompressedSize); } else { int innerCompressedOffset = 0; int innerCompressedSizeLeft = innerCompressedBytes.Length; using (var blockData = new MemoryStream()) { var blockCount = (innerCompressedSizeLeft + BlockSize) / BlockSize; blockData.WriteValueS32(blockCount, Endian.Big); blockData.Position = 4 + (blockCount * 8); var blockInfos = new List<Tuple<uint, uint>>(); while (innerCompressedSizeLeft > 0) { var blockUncompressedSize = Math.Min(BlockSize, innerCompressedSizeLeft); compressedBytes = new byte[blockUncompressedSize + (blockUncompressedSize / 16) + 64 + 3]; var actualCompressedSize = compressedBytes.Length; var result = LZO.Compress(innerCompressedBytes, innerCompressedOffset, blockUncompressedSize, compressedBytes, 0, ref actualCompressedSize); if (result != LZO.ErrorCode.Success) { throw new SaveCorruptionException(string.Format("LZO compression failure ({0})", result)); } blockData.Write(compressedBytes, 0, actualCompressedSize); blockInfos.Add(new Tuple<uint, uint>((uint)actualCompressedSize, BlockSize)); innerCompressedOffset += blockUncompressedSize; innerCompressedSizeLeft -= blockUncompressedSize; } blockData.Position = 4; foreach (var blockInfo in blockInfos) { blockData.WriteValueU32(blockInfo.Item1, Endian.Big); blockData.WriteValueU32(blockInfo.Item2, Endian.Big); } blockData.Position = 0; compressedBytes = blockData.ReadBytes((uint)blockData.Length); } } byte[] uncompressedBytes; using (var uncompressedData = new MemoryStream()) { uncompressedData.WriteValueS32(innerCompressedBytes.Length, Endian.Big); uncompressedData.WriteBytes(compressedBytes); uncompressedData.Position = 0; uncompressedBytes = uncompressedData.ReadBytes((uint)uncompressedData.Length); } byte[] computedHash; using (var sha1 = new System.Security.Cryptography.SHA1Managed()) { computedHash = sha1.ComputeHash(uncompressedBytes); } output.WriteBytes(computedHash); output.WriteBytes(uncompressedBytes); }
/// <summary> /// save PCCObject to file. /// </summary> /// <param name="newFileName">set full path + file name.</param> /// <param name="saveCompress">set true if you want a zlib compressed pcc file.</param> public void saveToCprFile(string newFileName = null, bool saveCompress = false) { bool bOverwriteFile = false; if (string.IsNullOrWhiteSpace(newFileName) || newFileName == pccFileName) { bOverwriteFile = true; newFileName = Path.GetFullPath(pccFileName) + ".tmp"; } if (bDLCStored) saveCompress = false; using (MemoryStream newPccStream = new MemoryStream()) { //ME3Explorer.DebugOutput.Clear(); DebugOutput.PrintLn("Saving file..."); DebugOutput.PrintLn("writing names list..."); // this condition needs a deeper check. todo... if (bExtraNamesList) { //MessageBox.Show("sono dentro, dimensione extranamelist: " + extraNamesList.Length + " bytes"); newPccStream.Seek(headerSize, SeekOrigin.Begin); newPccStream.Write(extraNamesList, 0, extraNamesList.Length); } //writing names list newPccStream.Seek(NameOffset, SeekOrigin.Begin); NameCount = Names.Count; foreach (string name in Names) { newPccStream.WriteValueS32(-(name.Length + 1)); newPccStream.WriteString(name + "\0", (uint)(name.Length + 1) * 2, Encoding.Unicode); } DebugOutput.PrintLn("writing imports list..."); //writing import infos ImportOffset = (int)newPccStream.Position; ImportCount = Imports.Count; foreach (ME3ImportEntry import in Imports) newPccStream.Write(import.data, 0, import.data.Length); //updating general export infos ExportOffset = (int)newPccStream.Position; ExportCount = Exports.Count; expInfoEndOffset = ExportOffset + Exports.Sum(export => export.info.Length); expDataBegOffset = expInfoEndOffset; // WV code stuff... DebugOutput.PrintLn("writing export data..."); int counter = 0; int breaker = Exports.Count / 100; if (breaker == 0) breaker = 1; //updating general export infos ExportOffset = (int)newPccStream.Position; ExportCount = Exports.Count; expInfoEndOffset = ExportOffset + Exports.Sum(export => export.info.Length); if (expDataBegOffset < expInfoEndOffset) expDataBegOffset = expInfoEndOffset; //writing export data /*newPccStream.Seek(expDataBegOffset, SeekOrigin.Begin); foreach (ExportEntry export in Exports) { //updating info values export.DataSize = export.Data.Length; export.DataOffset = (int)newPccStream.Position; //writing data newPccStream.Write(export.Data, 0, export.Data.Length); }*/ //writing export data List<ME3ExportEntry> unchangedExports = Exports.Where(export => !export.hasChanged || (export.hasChanged && export.Data.Length <= export.DataSize)).ToList(); List<ME3ExportEntry> changedExports = Exports.Where(export => export.hasChanged && export.Data.Length > export.DataSize).ToList(); foreach (ME3ExportEntry export in unchangedExports) { newPccStream.Seek(export.DataOffset, SeekOrigin.Begin); //updating info values export.DataSize = export.Data.Length; //export.DataOffset = (int)newPccStream.Position; export.DataOffset = (uint)newPccStream.Position; //writing data newPccStream.Write(export.Data, 0, export.Data.Length); } ME3ExportEntry lastExport = unchangedExports.Find(export => export.DataOffset == unchangedExports.Max(maxExport => maxExport.DataOffset)); int lastDataOffset = (int)(lastExport.DataOffset + lastExport.DataSize); newPccStream.Seek(lastDataOffset, SeekOrigin.Begin); foreach (ME3ExportEntry export in changedExports) { //updating info values export.DataSize = export.Data.Length; export.DataOffset = (uint)newPccStream.Position; //writing data newPccStream.Write(export.Data, 0, export.Data.Length); } //if (Exports.Any(x => x.Data == null)) // throw new Exception("values null!!"); //writing export info newPccStream.Seek(ExportOffset, SeekOrigin.Begin); foreach (ME3ExportEntry export in Exports) { newPccStream.Write(export.info, 0, export.info.Length); } /*foreach (ExportEntry export in unchangedExports) { newPccStream.Write(export.info, 0, export.info.Length); } foreach (ExportEntry export in changedExports) { newPccStream.Write(export.info, 0, export.info.Length); }*/ DebugOutput.PrintLn("writing header file..."); //writing header bCompressed = false; newPccStream.Seek(0, SeekOrigin.Begin); newPccStream.Write(header, 0, header.Length); newPccStream.Seek(0, SeekOrigin.Begin); if (saveCompress) { DebugOutput.PrintLn("compressing in zlib format, it may take a while..."); PCCHandler.CompressAndSave(newPccStream, newFileName); } else { using (FileStream outputStream = File.Create(newFileName)) { newPccStream.CopyTo(outputStream); } } } if (bOverwriteFile) { File.Delete(pccFileName); File.Move(newFileName, pccFileName); } DebugOutput.PrintLn(Path.GetFileName(pccFileName) + " has been saved."); }
public void CopyImgList(Texture2D inTex, PCCObject pcc, bool norender = false) { List<ImageInfo> tempList = new List<ImageInfo>(); MemoryStream tempData = new MemoryStream(); SaltLZOHelper lzo = new SaltLZOHelper(); numMipMaps = inTex.numMipMaps; // forced norenderfix // norender = true; int type = -1; if (!norender) { if (imgList.Exists(img => img.storageType == storage.arcCpr) && imgList.Count > 1) type = 1; else if (imgList.Exists(img => img.storageType == storage.pccCpr)) type = 2; else if (imgList.Exists(img => img.storageType == storage.pccSto) || imgList.Count == 1) type = 3; } else type = 3; switch (type) { case 1: for (int i = 0; i < inTex.imgList.Count; i++) { try { ImageInfo newImg = new ImageInfo(); ImageInfo replaceImg = inTex.imgList[i]; Texture2D.storage replaceType = imgList.Find(img => img.imgSize == replaceImg.imgSize).storageType; int j = 0; while (replaceType == storage.empty) { j++; replaceType = imgList[imgList.FindIndex(img => img.imgSize == replaceImg.imgSize) + j].storageType; } if (replaceType == storage.arcCpr || !imgList.Exists(img => img.imgSize == replaceImg.imgSize)) { newImg.storageType = storage.arcCpr; newImg.uncSize = replaceImg.uncSize; newImg.cprSize = replaceImg.cprSize; newImg.imgSize = replaceImg.imgSize; newImg.offset = (int)(replaceImg.offset + inTex.pccOffset + inTex.dataOffset); } else { newImg.storageType = storage.pccSto; newImg.uncSize = replaceImg.uncSize; newImg.cprSize = replaceImg.uncSize; newImg.imgSize = replaceImg.imgSize; newImg.offset = (int)(tempData.Position); using (MemoryStream tempStream = new MemoryStream(inTex.imageData)) { tempData.WriteBytes(lzo.DecompressTex(tempStream, replaceImg.offset, replaceImg.uncSize, replaceImg.cprSize)); } } tempList.Add(newImg); } catch { ImageInfo replaceImg = inTex.imgList[i]; if (!imgList.Exists(img => img.imgSize == replaceImg.imgSize)) throw new Exception("An error occurred during imglist copying and no suitable replacement was found"); ImageInfo newImg = imgList.Find(img => img.imgSize == replaceImg.imgSize); if (newImg.storageType != storage.pccCpr && newImg.storageType != storage.pccSto) throw new Exception("An error occurred during imglist copying and no suitable replacement was found"); int temppos = newImg.offset; newImg.offset = (int)tempData.Position; tempData.Write(imageData, temppos, newImg.cprSize); tempList.Add(newImg); } } break; case 2: for (int i = 0; i < inTex.imgList.Count; i++) { ImageInfo newImg = new ImageInfo(); ImageInfo replaceImg = inTex.imgList[i]; newImg.storageType = storage.pccCpr; newImg.uncSize = replaceImg.uncSize; newImg.cprSize = replaceImg.cprSize; newImg.imgSize = replaceImg.imgSize; newImg.offset = (int)(tempData.Position); byte[] buffer = new byte[newImg.cprSize]; Buffer.BlockCopy(inTex.imageData, replaceImg.offset, buffer, 0, buffer.Length); tempData.WriteBytes(buffer); tempList.Add(newImg); } break; case 3: for (int i = 0; i < inTex.imgList.Count; i++) { ImageInfo newImg = new ImageInfo(); ImageInfo replaceImg = inTex.imgList[i]; newImg.storageType = storage.pccSto; newImg.uncSize = replaceImg.uncSize; newImg.cprSize = replaceImg.uncSize; newImg.imgSize = replaceImg.imgSize; newImg.offset = (int)(tempData.Position); if (replaceImg.storageType == storage.pccCpr) { using (MemoryStream tempStream = new MemoryStream(inTex.imageData)) { tempData.WriteBytes(lzo.DecompressTex(tempStream, replaceImg.offset, replaceImg.uncSize, replaceImg.cprSize)); } } else if (replaceImg.storageType == storage.pccSto) { byte[] buffer = new byte[newImg.cprSize]; Buffer.BlockCopy(inTex.imageData, replaceImg.offset, buffer, 0, buffer.Length); tempData.WriteBytes(buffer); } else throw new NotImplementedException("Copying from non package stored texture no available"); tempList.Add(newImg); } break; default: throw new NotImplementedException(); } for (int i = 0; i < tempList.Count; i++) { ImageInfo tempinfo = tempList[i]; if (inTex.imgList[i].storageType == storage.empty) tempinfo.storageType = storage.empty; tempList[i] = tempinfo; } imgList = tempList; imageData = tempData.ToArray(); tempData.Close(); byte[] buff; //Copy properties using (MemoryStream tempMem = new MemoryStream()) { tempMem.WriteBytes(headerData); for (int i = 0; i < inTex.properties.Count; i++) { SaltPropertyReader.Property prop = inTex.properties.ElementAt(i).Value; if (prop.Name == "UnpackMin") { for (int j = 0; j < inTex.UnpackNum; j++) { tempMem.WriteValueS64(pcc.AddName(prop.Name)); tempMem.WriteValueS64(pcc.AddName(prop.TypeVal.ToString())); tempMem.WriteValueS32(prop.Size); tempMem.WriteValueS32(j); tempMem.WriteValueF32(prop.Value.FloatValue, Endian.Little); } continue; } tempMem.WriteValueS64(pcc.AddName(prop.Name)); if (prop.Name == "None") { for (int j = 0; j < 12; j++) tempMem.WriteByte(0); } else { tempMem.WriteValueS64(pcc.AddName(prop.TypeVal.ToString())); tempMem.WriteValueS64(prop.Size); switch (prop.TypeVal) { case SaltPropertyReader.Type.IntProperty: tempMem.WriteValueS32(prop.Value.IntValue); break; case SaltPropertyReader.Type.BoolProperty: tempMem.Seek(-4, SeekOrigin.Current); tempMem.WriteValueS32(prop.Value.IntValue); tempMem.Seek(4, SeekOrigin.Current); break; case SaltPropertyReader.Type.NameProperty: tempMem.WriteValueS64(pcc.AddName(prop.Value.StringValue)); break; case SaltPropertyReader.Type.StrProperty: tempMem.WriteValueS32(prop.Value.StringValue.Length + 1); foreach (char c in prop.Value.StringValue) tempMem.WriteByte((byte)c); tempMem.WriteByte(0); break; case SaltPropertyReader.Type.StructProperty: tempMem.WriteValueS64(pcc.AddName(prop.Value.StringValue)); foreach (SaltPropertyReader.PropertyValue value in prop.Value.Array) tempMem.WriteValueS32(value.IntValue); break; case SaltPropertyReader.Type.ByteProperty: tempMem.WriteValueS32(pcc.AddName(prop.Value.StringValue)); tempMem.WriteValueS32(prop.Value.IntValue); break; case SaltPropertyReader.Type.FloatProperty: tempMem.WriteValueF32(prop.Value.FloatValue, Endian.Little); break; default: throw new FormatException("unknown property"); } } } buff = tempMem.ToArray(); } int propertiesOffset = SaltPropertyReader.detectStart(pcc, buff); headerData = new byte[propertiesOffset]; Buffer.BlockCopy(buff, 0, headerData, 0, propertiesOffset); properties = new Dictionary<string, SaltPropertyReader.Property>(); List<SaltPropertyReader.Property> tempProperties = SaltPropertyReader.getPropList(pcc, buff); UnpackNum = 0; for (int i = 0; i < tempProperties.Count; i++) { SaltPropertyReader.Property property = tempProperties[i]; if (property.Name == "UnpackMin") UnpackNum++; if (!properties.ContainsKey(property.Name)) properties.Add(property.Name, property); switch (property.Name) { case "Format": texFormat = property.Value.StringValue; break; case "LODGroup": LODGroup = property.Value.StringValue; break; case "CompressionSettings": Compression = property.Value.StringValue; break; case "None": dataOffset = (uint)(property.offsetval + property.Size); break; } } // if "None" property isn't found throws an exception if (dataOffset == 0) throw new Exception("\"None\" property not found"); }
public byte[] ToArray(int pccExportDataOffset, PCCObject pcc) { MemoryStream buffer = new MemoryStream(); buffer.Write(headerData, 0, headerData.Length); if (properties.ContainsKey("LODGroup")) { properties["LODGroup"].Value.StringValue = "TEXTUREGROUP_LightAndShadowMap"; //properties["LODGroup"].Value.IntValue = 1025; } else { buffer.WriteValueS64(pcc.AddName("LODGroup")); buffer.WriteValueS64(pcc.AddName("ByteProperty")); buffer.WriteValueS64(8); buffer.WriteValueS32(pcc.AddName("TEXTUREGROUP_LightAndShadowMap")); buffer.WriteValueS32(1025); } foreach (KeyValuePair<string, SaltPropertyReader.Property> kvp in properties) { SaltPropertyReader.Property prop = kvp.Value; if (prop.Name == "UnpackMin") { for (int j = 0; j < UnpackNum; j++) { buffer.WriteValueS64(pcc.AddName(prop.Name)); buffer.WriteValueS64(pcc.AddName(prop.TypeVal.ToString())); buffer.WriteValueS32(prop.Size); buffer.WriteValueS32(j); buffer.WriteValueF32(prop.Value.FloatValue, Endian.Little); } continue; } buffer.WriteValueS64(pcc.AddName(prop.Name)); if (prop.Name == "None") { for (int j = 0; j < 12; j++) buffer.WriteByte(0); } else { buffer.WriteValueS64(pcc.AddName(prop.TypeVal.ToString())); buffer.WriteValueS64(prop.Size); switch (prop.TypeVal) { case SaltPropertyReader.Type.IntProperty: buffer.WriteValueS32(prop.Value.IntValue); break; case SaltPropertyReader.Type.BoolProperty: buffer.Seek(-4, SeekOrigin.Current); buffer.WriteValueS32(prop.Value.IntValue); buffer.Seek(4, SeekOrigin.Current); break; case SaltPropertyReader.Type.NameProperty: buffer.WriteValueS64(pcc.AddName(prop.Value.StringValue)); break; case SaltPropertyReader.Type.StrProperty: buffer.WriteValueS32(prop.Value.StringValue.Length + 1); foreach (char c in prop.Value.StringValue) buffer.WriteByte((byte)c); buffer.WriteByte(0); break; case SaltPropertyReader.Type.StructProperty: buffer.WriteValueS64(pcc.AddName(prop.Value.StringValue)); foreach (SaltPropertyReader.PropertyValue value in prop.Value.Array) buffer.WriteValueS32(value.IntValue); break; case SaltPropertyReader.Type.ByteProperty: buffer.WriteValueS32(pcc.AddName(prop.Value.StringValue)); buffer.WriteValueS32(prop.Value.IntValue); break; case SaltPropertyReader.Type.FloatProperty: buffer.WriteValueF32(prop.Value.FloatValue, Endian.Little); break; default: throw new FormatException("unknown property"); } } } buffer.WriteValueS32((int)(pccOffset + buffer.Position + 4)); //Remove empty textures List<ImageInfo> tempList = new List<ImageInfo>(); foreach (ImageInfo imgInfo in imgList) { if (imgInfo.storageType != storage.empty) tempList.Add(imgInfo); } imgList = tempList; numMipMaps = (uint)imgList.Count; buffer.WriteValueU32(numMipMaps); foreach (ImageInfo imgInfo in imgList) { buffer.WriteValueS32((int)imgInfo.storageType); buffer.WriteValueS32(imgInfo.uncSize); buffer.WriteValueS32(imgInfo.cprSize); if (imgInfo.storageType == storage.pccSto) { buffer.WriteValueS32((int)(imgInfo.offset + pccExportDataOffset + dataOffset)); buffer.Write(imageData, imgInfo.offset, imgInfo.uncSize); } else if (imgInfo.storageType == storage.pccCpr) { buffer.WriteValueS32((int)(imgInfo.offset + pccExportDataOffset + dataOffset)); buffer.Write(imageData, imgInfo.offset, imgInfo.cprSize); } else buffer.WriteValueS32(imgInfo.offset); if (imgInfo.imgSize.width < 4) buffer.WriteValueU32(4); else buffer.WriteValueU32(imgInfo.imgSize.width); if (imgInfo.imgSize.height < 4) buffer.WriteValueU32(4); else buffer.WriteValueU32(imgInfo.imgSize.height); } buffer.WriteBytes(footerData); return buffer.ToArray(); }
public void OneImageToRuleThemAll(string imageFileName, string archiveDir, out string newTextureGroup) { newTextureGroup = null; ImageMipMapHandler imgMipMap = new ImageMipMapHandler(imageFileName, null); // starts from the smaller image for (int i = imgMipMap.imageList.Count - 1; i >= 0; i--) { ImageFile newImageFile = imgMipMap.imageList[i]; // insert images only with size > 64 if (newImageFile.imgSize.width < 64 && newImageFile.imgSize.height < 64) continue; // write the new image into a file (I know that's crappy solution, I'll find another one later...) using (FileStream newImageStream = File.Create(newImageFile.fileName)) { byte[] buffer = newImageFile.ToArray(); newImageStream.Write(buffer, 0, buffer.Length); } // if the image size exists inside the texture2d image list then we have to replace it if (imgList.Exists(img => img.imgSize == newImageFile.imgSize)) { // ...but at least for now I can reuse my replaceImage function... ;) replaceImage(newImageFile.imgSize.ToString(), newImageFile.fileName, archiveDir); } else // if the image doesn't exists then we have to add it { // ...and use my addBiggerImage function! :P addBiggerImage(newImageFile.fileName, archiveDir); } File.Delete(newImageFile.fileName); } // check that Texture2D has a TextureGroup if (!properties.ContainsKey("LODGroup")) return; // extracting values from LODGroup Property PropertyReader.Property LODGroup = properties["LODGroup"]; string textureGroupName = pccRef.Names[LODGroup.Value.IntValue]; string newTextureGroupName = "TEXTUREGROUP_Shadowmap"; textureGroupName = newTextureGroupName; int nameIndex = pccRef.FindNameOrAdd(newTextureGroupName); using (MemoryStream rawStream = new MemoryStream(LODGroup.raw)) { rawStream.Seek(32, SeekOrigin.Begin); rawStream.WriteValueS32(nameIndex); //rawStream.Seek(32, SeekOrigin.Begin); rawStream.WriteValueS32(0); properties["LODGroup"].raw = rawStream.ToArray(); } //LODGroup.Value.IntValue = pccRef.Names.FindIndex(name => name == newTextureGroupName); /*MessageBox.Show("Texturegroup Name: " + textureGroupName); ImageSize maxImageSize = imgList.Max(image => image.imgSize); int textureGroupValue = (int)Math.Max(maxImageSize.width, maxImageSize.height) + 1; // open Engine.pcc file and edit TextureGroup enumerator { PCCObject pccEngine = new PCCObject(ME3Directory.cookedPath + "Engine.pcc"); int idxTexGroups = pccEngine.Exports.FindIndex(export => export.ObjectName == "TextureGroup"); TextureGroup texGroup = new TextureGroup(pccEngine, pccEngine.Exports[idxTexGroups].Data); if (texGroup.ExistsTextureGroup(textureGroupName, textureGroupValue)) return; else { if (!pccEngine.Names.Exists(name => name == newTextureGroupName)) pccEngine.Names.Add(newTextureGroupName); newTextureGroup = textureGroupName + "_" + (textureGroupValue - 1); texGroup.Add(textureGroupName, textureGroupValue); MessageBox.Show("Now editing texgroup enum"); pccEngine.Exports[idxTexGroups].Data = texGroup.ToArray(); MessageBox.Show("Now saving engine.pcc"); pccEngine.saveToFile(true, ME3Directory.cookedPath + "Engine.pcc"); MessageBox.Show("Saved engine.pcc"); } }*/ }
public void removeImage() { //MessageBox.Show("1. Number of imgs = " + imgList.Count); imgList.RemoveAt(0); //MessageBox.Show("2. Number of imgs = " + imgList.Count); numMipMaps--; int propVal = properties["MipTailBaseIdx"].Value.IntValue; propVal--; properties["MipTailBaseIdx"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["MipTailBaseIdx"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["MipTailBaseIdx"].raw = rawStream.ToArray(); } //MessageBox.Show("Init. width = " + imgList[0].imgSize.width); propVal = (int)imgList[0].imgSize.width; properties["SizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["SizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeX"].raw = rawStream.ToArray(); } //MessageBox.Show("Final width = " + imgList[0].imgSize.width); //properties["SizeX"] = Size; //Size = properties["SizeY"]; //properties["SizeY"].Value.IntValue = (int)newImgInfo.imgSize.height; properties["SizeY"].Value.IntValue = (int)imgList[0].imgSize.height; using (MemoryStream rawStream = new MemoryStream(properties["SizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeY"].raw = rawStream.ToArray(); } //properties["SizeY"] = Size; properties["OriginalSizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeX"].raw = rawStream.ToArray(); } properties["OriginalSizeY"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeY"].raw = rawStream.ToArray(); } }
/// <summary> /// save PCCObject to file by reconstruction from data /// </summary> /// <param name="path">full path + file name.</param> public void saveByReconstructing(string path) { //load in all data byte[] buff; foreach (ExportEntry e in Exports) { buff = e.Data; } try { MemoryStream m = new MemoryStream(); m.WriteBytes(header); //name table NameOffset = (int)m.Position; NameCount = Names.Count; foreach (NameEntry n in Names) { string text = n.name; if (!text.EndsWith("\0")) { text += "\0"; } m.Write(BitConverter.GetBytes(text.Length), 0, 4); foreach (char c in text) { m.WriteByte((byte)c); m.WriteByte(0); } m.WriteValueS32(n.unk); m.WriteValueS32(n.flags); } //import table ImportOffset = (int)m.Position; ImportCount = Imports.Count; foreach (ImportEntry e in Imports) { m.WriteBytes(e.header); } //export table ExportOffset = (int)m.Position; ExportCount = Exports.Count; for (int i = 0; i < Exports.Count; i++) { ExportEntry e = Exports[i]; e.offset = (uint)m.Position; m.WriteBytes(e.header); } //freezone int FreeZoneSize = (int)FreeZoneEnd - (int)FreeZoneStart; FreeZoneStart = (uint)m.Position; m.Write(new byte[FreeZoneSize], 0, FreeZoneSize); FreeZoneEnd = HeaderLength = (uint)m.Position; //export data for (int i = 0; i < Exports.Count; i++) { ExportEntry e = Exports[i]; e.DataOffset = (int)m.Position; e.DataSize = e.Data.Length; m.WriteBytes(e.Data); long pos = m.Position; m.Seek(e.offset + 32, SeekOrigin.Begin); m.Write(BitConverter.GetBytes(e.DataSize), 0, 4); m.Write(BitConverter.GetBytes(e.DataOffset), 0, 4); m.Seek(pos, SeekOrigin.Begin); } //update header m.Seek(0, SeekOrigin.Begin); m.WriteBytes(header); File.WriteAllBytes(path, m.ToArray()); } catch (Exception ex) { MessageBox.Show("PCC Save error:\n" + ex.Message); } }
private void LoadHelper(MemoryStream tempStream) { tempStream.Seek(12, SeekOrigin.Begin); int tempNameSize = tempStream.ReadValueS32(); tempStream.Seek(64 + tempNameSize, SeekOrigin.Begin); int tempGenerator = tempStream.ReadValueS32(); tempStream.Seek(36 + tempGenerator * 12, SeekOrigin.Current); int tempPos = (int)tempStream.Position + 4; tempStream.Seek(0, SeekOrigin.Begin); header = tempStream.ReadBytes(tempPos); tempStream.Seek(0, SeekOrigin.Begin); if (magic != ZBlock.magic && magic.Swap() != ZBlock.magic) { DebugOutput.PrintLn("Magic number incorrect: " + magic); throw new FormatException("This is not a pcc file. The magic number is incorrect."); } if (bCompressed) { DebugOutput.PrintLn("File is compressed"); listsStream = lzo.DecompressPCC(tempStream, this); //Correct the header bCompressed = false; listsStream.Seek(0, SeekOrigin.Begin); listsStream.WriteBytes(header); // Set numblocks to zero listsStream.WriteValueS32(0); //Write the magic number listsStream.WriteBytes(new byte[] { 0xF2, 0x56, 0x1B, 0x4E }); // Write 4 bytes of 0 listsStream.WriteValueS32(0); } else { DebugOutput.PrintLn("File already decompressed. Reading decompressed data."); //listsStream = tempStream; listsStream = new MemoryStream(); tempStream.WriteTo(listsStream); } tempStream.Dispose(); ReadNames(listsStream); ReadImports(listsStream); ReadExports(listsStream); LoadExports(); }
/// <summary> /// save PCCObject to file by reconstruction from data /// </summary> /// <param name="path">full path + file name.</param> public void saveByReconstructing(string path) { try { this.IsCompressed = false; MemoryStream m = new MemoryStream(); m.WriteBytes(header); // Set numblocks to zero m.WriteValueS32(0); //Write the magic number (What is this?) m.WriteBytes(new byte[] { 0xF2, 0x56, 0x1B, 0x4E }); // Write 4 bytes of 0 m.WriteValueS32(0); //name table NameOffset = (int)m.Position; NameCount = names.Count; foreach (string name in names) { m.WriteValueS32(name.Length + 1); m.WriteString(name); m.WriteByte(0); m.WriteValueS32(0); m.WriteValueS32(458768); } //import table ImportOffset = (int)m.Position; ImportCount = imports.Count; foreach (ImportEntry e in imports) { m.WriteBytes(e.header); } //export table ExportOffset = (int)m.Position; ExportCount = exports.Count; for (int i = 0; i < exports.Count; i++) { ME1ExportEntry e = exports[i]; e.headerOffset = (uint)m.Position; m.WriteBytes(e.header); } //freezone int FreeZoneSize = expDataBegOffset - FreeZoneStart; FreeZoneStart = (int)m.Position; m.Write(new byte[FreeZoneSize], 0, FreeZoneSize); expDataBegOffset = (int)m.Position; //export data for (int i = 0; i < exports.Count; i++) { ME1ExportEntry e = exports[i]; e.DataOffset = (int)m.Position; e.DataSize = e.Data.Length; m.WriteBytes(e.Data); long pos = m.Position; m.Seek(e.headerOffset + 32, SeekOrigin.Begin); m.WriteValueS32(e.DataSize); m.WriteValueS32(e.DataOffset); m.Seek(pos, SeekOrigin.Begin); } //update header m.Seek(0, SeekOrigin.Begin); m.WriteBytes(header); File.WriteAllBytes(path, m.ToArray()); AfterSave(); } catch (Exception ex) { MessageBox.Show("PCC Save error:\n" + ex.Message); } }
/// <summary> /// compress an entire ME3 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 Serialize(Stream output) { var endian = this.Endian; const uint headerSize = 32; output.WriteValueU32(0x42424947, endian); output.WriteValueU32(this.Version, endian); var keys = new List<string>() {""}; int maxValueLength = 0; var blob = new StringBuilder(); foreach (var file in this.Files) { keys.Add(file.Name); foreach (var section in file.Sections) { keys.Add(section.Key); foreach (var value in section.Value) { keys.Add(value.Key); foreach (var item in value.Value) { if (item.Value != null) { blob.Append(item.Value + '\0'); maxValueLength = Math.Max(maxValueLength, item.Value.Length); } } } } } var huffmanEncoder = new Huffman.Encoder(); huffmanEncoder.Build(blob.ToString()); keys = keys.Distinct().OrderBy(k => k.HashCrc32()).ToList(); int maxKeyLength = keys.Max(k => k.Length); uint stringTableSize; using (var data = new MemoryStream()) { data.Position = 4; data.WriteValueS32(keys.Count, endian); data.Position = 4 + 4 + (8 * keys.Count); var offsets = new List<KeyValuePair<uint, uint>>(); foreach (var key in keys) { var offset = (uint)data.Position; data.WriteValueU16((ushort)key.Length, endian); data.WriteString(key, Encoding.UTF8); offsets.Add(new KeyValuePair<uint, uint>(key.HashCrc32(), offset)); } data.Position = 8; foreach (var kv in offsets) { data.WriteValueU32(kv.Key, endian); data.WriteValueU32(kv.Value - 8, endian); } data.Position = 0; data.WriteValueU32((uint)data.Length, endian); data.Position = 0; stringTableSize = (uint)data.Length; output.Seek(headerSize, SeekOrigin.Begin); output.WriteFromStream(data, data.Length); } uint huffmanSize; using (var data = new MemoryStream()) { var pairs = huffmanEncoder.GetPairs(); data.WriteValueU16((ushort)pairs.Length, endian); foreach (var pair in pairs) { data.WriteValueS32(pair.Left, endian); data.WriteValueS32(pair.Right, endian); } data.Position = 0; huffmanSize = (uint)data.Length; output.Seek(headerSize + stringTableSize, SeekOrigin.Begin); output.WriteFromStream(data, data.Length); } var bits = new BitArray(huffmanEncoder.TotalBits); var bitOffset = 0; uint indexSize; using (var index = new MemoryStream()) { var fileDataOffset = 2 + (this.Files.Count * 6); var files = new List<KeyValuePair<ushort, int>>(); foreach (var file in this.Files.OrderBy(f => keys.IndexOf(f.Name))) { files.Add(new KeyValuePair<ushort, int>( (ushort)keys.IndexOf(file.Name), fileDataOffset)); var sectionDataOffset = 2 + (file.Sections.Count * 6); var sections = new List<KeyValuePair<ushort, int>>(); foreach (var section in file.Sections.OrderBy(s => keys.IndexOf(s.Key))) { sections.Add(new KeyValuePair<ushort, int>( (ushort)keys.IndexOf(section.Key), sectionDataOffset)); var valueDataOffset = 2 + (section.Value.Count * 6); var values = new List<KeyValuePair<ushort, int>>(); foreach (var value in section.Value.OrderBy(v => keys.IndexOf(v.Key))) { index.Position = fileDataOffset + sectionDataOffset + valueDataOffset; values.Add(new KeyValuePair<ushort, int>( (ushort)keys.IndexOf(value.Key), valueDataOffset)); index.WriteValueU16((ushort)value.Value.Count, endian); valueDataOffset += 2; foreach (var item in value.Value) { if (item.Type == 1) { index.WriteValueS32((1 << 29) | bitOffset, endian); } else if (item.Type == 0 || item.Type == 2 || item.Type == 3 || item.Type == 4) { index.WriteValueS32((item.Type << 29) | bitOffset, endian); bitOffset += huffmanEncoder.Encode((item.Value ?? "") + '\0', bits, bitOffset); } valueDataOffset += 4; } } index.Position = fileDataOffset + sectionDataOffset; index.WriteValueU16((ushort)values.Count, endian); sectionDataOffset += 2; foreach (var value in values) { index.WriteValueU16(value.Key, endian); index.WriteValueS32(value.Value, endian); sectionDataOffset += 6; } sectionDataOffset += valueDataOffset; } index.Position = fileDataOffset; index.WriteValueU16((ushort)sections.Count, endian); fileDataOffset += 2; foreach (var section in sections) { index.WriteValueU16(section.Key, endian); index.WriteValueS32(section.Value, endian); fileDataOffset += 6; } fileDataOffset += sectionDataOffset; } index.Position = 0; index.WriteValueU16((ushort)files.Count, endian); foreach (var file in files) { index.WriteValueU16(file.Key, endian); index.WriteValueS32(file.Value, endian); } index.Position = 0; indexSize = (uint)index.Length; output.Seek(headerSize + stringTableSize + huffmanSize, SeekOrigin.Begin); output.WriteFromStream(index, index.Length); } output.Seek(headerSize + stringTableSize + huffmanSize + indexSize, SeekOrigin.Begin); output.WriteValueS32(bits.Length, endian); var bytes = new byte[(bits.Length - 1) / 8 + 1]; bits.CopyTo(bytes, 0); output.WriteBytes(bytes); output.Seek(8, SeekOrigin.Begin); output.WriteValueS32(maxKeyLength, endian); output.WriteValueS32(maxValueLength, endian); output.WriteValueU32(stringTableSize, endian); output.WriteValueU32(huffmanSize, endian); output.WriteValueU32(indexSize, endian); output.WriteValueS32(bytes.Length, endian); output.Seek(0, SeekOrigin.Begin); output.WriteValueU32(0x666D726D, endian); }
public byte[] ToArray(uint pccExportDataOffset, ME3PCCObject pcc) { using (MemoryStream tempStream = new MemoryStream()) { tempStream.WriteBytes(headerData); // Whilst testing get rid of this // Heff: Seems like the shadowmap was the best solution in most cases, // adding an exception for known problematic animated textures for now. // (See popup in tpftools) if (properties.ContainsKey("LODGroup")) properties["LODGroup"].Value.String2 = "TEXTUREGROUP_Shadowmap"; else { tempStream.WriteValueS64(pcc.addName2("LODGroup")); tempStream.WriteValueS64(pcc.addName2("ByteProperty")); tempStream.WriteValueS64(8); tempStream.WriteValueS64(pcc.addName2("TextureGroup")); tempStream.WriteValueS64(pcc.addName2("TEXTUREGROUP_Shadowmap")); } foreach (KeyValuePair<string, SaltPropertyReader.Property> kvp in properties) { SaltPropertyReader.Property prop = kvp.Value; if (prop.Name == "UnpackMin") { for (int i = 0; i < UnpackNum; i++) { tempStream.WriteValueS64(pcc.addName2(prop.Name)); tempStream.WriteValueS64(pcc.addName2(prop.TypeVal.ToString())); tempStream.WriteValueS32(prop.Size); tempStream.WriteValueS32(i); tempStream.WriteValueF32(prop.Value.FloatValue); } continue; } tempStream.WriteValueS64(pcc.addName2(prop.Name)); if (prop.Name == "None") continue; tempStream.WriteValueS64(pcc.addName2(prop.TypeVal.ToString())); tempStream.WriteValueS64(prop.Size); switch (prop.TypeVal) { case SaltPropertyReader.Type.FloatProperty: tempStream.WriteValueF32(prop.Value.FloatValue); break; case SaltPropertyReader.Type.IntProperty: tempStream.WriteValueS32(prop.Value.IntValue); break; case SaltPropertyReader.Type.NameProperty: tempStream.WriteValueS64(pcc.addName2(prop.Value.StringValue)); // Heff: Modified to handle name references. //var nameIndex = pcc.addName2(prop.Value.StringValue); //tempStream.WriteValueS32(nameIndex); //tempStream.WriteValueS32(prop.Value.NameValue.count); break; case SaltPropertyReader.Type.ByteProperty: tempStream.WriteValueS64(pcc.addName2(prop.Value.StringValue)); tempStream.WriteValueS64(pcc.addName2(prop.Value.String2)); // Heff: Modified to handle name references. //var valueIndex = pcc.addName2(prop.Value.String2); //tempStream.WriteValueS32(valueIndex); //tempStream.WriteValueS32(prop.Value.NameValue.count); //tempStream.WriteValueS32(pcc.addName2(prop.Value.String2)); //byte[] footer = new byte[4]; //Buffer.BlockCopy(prop.raw, prop.raw.Length - 4, footer, 0, 4); //tempStream.WriteBytes(footer); break; case SaltPropertyReader.Type.BoolProperty: tempStream.WriteValueBoolean(prop.Value.Boolereno); break; case SaltPropertyReader.Type.StructProperty: tempStream.WriteValueS64(pcc.addName2(prop.Value.StringValue)); for (int i = 0; i < prop.Size; i++) tempStream.WriteByte((byte)prop.Value.Array[i].IntValue); break; default: throw new NotImplementedException("Property type: " + prop.TypeVal + ", not yet implemented. TELL ME ABOUT THIS!"); } } //Remove empty textures List<ImageInfo> tempList = new List<ImageInfo>(); foreach (ImageInfo imgInfo in privateimgList) { if (imgInfo.storageType != storage.empty) tempList.Add(imgInfo); } privateimgList = tempList; numMipMaps = (uint)privateimgList.Count; tempStream.WriteValueU32(numMipMaps); foreach (ImageInfo imgInfo in privateimgList) { tempStream.WriteValueS32((int)imgInfo.storageType); tempStream.WriteValueS32(imgInfo.uncSize); tempStream.WriteValueS32(imgInfo.cprSize); if (imgInfo.storageType == storage.pccSto) { tempStream.WriteValueS32((int)(imgInfo.offset + pccExportDataOffset + dataOffset)); tempStream.Write(imageData, imgInfo.offset, imgInfo.uncSize); } else tempStream.WriteValueS32(imgInfo.offset); tempStream.WriteValueU32(imgInfo.imgSize.width); tempStream.WriteValueU32(imgInfo.imgSize.height); } //// Texture2D footer, 24 bytes size - changed to 20 //tempStream.Write(imageData, imageData.Length - 20, 20); tempStream.WriteBytes(footerData); return tempStream.ToArray(); } #region Unused Code /* bool lodExists = false; foreach (KeyValuePair<string, PropertyReader.Property> kvp in properties) { PropertyReader.Property property = kvp.Value; if (kvp.Key == "LODGroup") { lodExists = true; break; } } MemoryStream buffer = new MemoryStream(); buffer.Write(headerData, 0, headerData.Length); if (lodExists) { // extracting values from LODGroup Property PropertyReader.Property LODGroup = properties["LODGroup"]; string textureGroupName = pcc.Names[LODGroup.Value.IntValue]; bool nameExists = false; string newTextureGroupName = "TEXTUREGROUP_Shadowmap"; if (String.Compare(newTextureGroupName, textureGroupName) != 0) { textureGroupName = newTextureGroupName; if (!pcc.Names.Exists(name => name == newTextureGroupName)) pcc.Names.Add(newTextureGroupName); using (MemoryStream rawStream = new MemoryStream(LODGroup.raw)) { rawStream.Seek(32, SeekOrigin.Begin); rawStream.WriteValueS32(pcc.Names.FindIndex(name => name == newTextureGroupName)); rawStream.WriteValueS32(0); properties["LODGroup"].raw = rawStream.ToArray(); } } else nameExists = true; //MemoryStream buffer = new MemoryStream(); //buffer.Write(headerData, 0, headerData.Length); foreach (KeyValuePair<string, PropertyReader.Property> kvp in properties) { PropertyReader.Property property = kvp.Value; if (kvp.Key == "LODBias") continue; if (kvp.Key == "InternalFormatLODBias") continue; if (kvp.Key == "LODGroup" && nameExists == false) { int name; if (!nameExists) name = pcc.Names.Count - 1; //Warranty Voiders Name redirect hack^^ else name = LODGroup.Value.IntValue; ME3_HR_Patch.Helper.BitConverter.IsLittleEndian = true; byte[] buff = ME3_HR_Patch.Helper.BitConverter.GetBytes(name); for (int i = 0; i < 4; i++) property.raw[i + 24] = buff[i]; } buffer.Write(property.raw, 0, property.raw.Length); if (kvp.Key == "UnpackMin") { buffer.Write(property.raw, 0, property.raw.Length); buffer.Write(property.raw, 0, property.raw.Length); } } } else { //MemoryStream buffer = new MemoryStream(); //buffer.Write(headerData, 0, headerData.Length); int lodID = pcc.findName("LODGroup"); if (lodID == -1) { pcc.addName("LODGroup"); lodID = pcc.Names.Count - 1; } buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes(lodID)); buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes((int)0)); lodID = pcc.findName("ByteProperty"); buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes(lodID)); buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes((int)0)); //Write an int buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes((int)8)); buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes((int)0)); lodID = pcc.findName("TextureGroup"); if (lodID == -1) { pcc.addName("TextureGroup"); lodID = pcc.Names.Count - 1; } buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes(lodID)); buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes((int)0)); lodID = pcc.findName("TEXTUREGROUP_Shadowmap"); if (lodID == -1) { pcc.addName("TEXTUREGROUP_Shadowmap"); lodID = pcc.Names.Count - 1; } buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes(lodID)); buffer.WriteBytes(ME3_HR_Patch.Helper.BitConverter.GetBytes((int)0)); foreach (KeyValuePair<string, PropertyReader.Property> kvp in properties) { PropertyReader.Property property = kvp.Value; if (kvp.Key == "LODBias") continue; if (kvp.Key == "InternalFormatLODBias") continue; if (kvp.Key == "LODGroup") { int name = pcc.Names.Count - 1; //Warranty Voiders Name redirect hack^^ ME3_HR_Patch.Helper.BitConverter.IsLittleEndian = true; byte[] buff = ME3_HR_Patch.Helper.BitConverter.GetBytes(name); for (int i = 0; i < 4; i++) property.raw[i + 24] = buff[i]; } buffer.Write(property.raw, 0, property.raw.Length); if (kvp.Key == "UnpackMin") { buffer.Write(property.raw, 0, property.raw.Length); buffer.Write(property.raw, 0, property.raw.Length); } } } buffer.WriteValueU32(numMipMaps); foreach (ImageInfo imgInfo in imgList) { buffer.WriteValueS32((int)imgInfo.storageType); buffer.WriteValueS32(imgInfo.uncSize); buffer.WriteValueS32(imgInfo.cprSize); if (imgInfo.storageType == storage.pccSto) { buffer.WriteValueS32((int)(imgInfo.offset + pccExportDataOffset + dataOffset)); buffer.Write(imageData, imgInfo.offset, imgInfo.uncSize); } else buffer.WriteValueS32(imgInfo.offset); buffer.WriteValueU32(imgInfo.imgSize.width); buffer.WriteValueU32(imgInfo.imgSize.height); } // Texture2D footer, 24 bytes size buffer.Write(imageData, imageData.Length - 24, 24); byte[] rawData = buffer.ToArray(); return rawData; */ #endregion }
public byte[] ThisToArray(uint pccExportDataOffset, ME2PCCObject pcc) { MemoryStream buffer = new MemoryStream(); buffer.Write(headerData, 0, headerData.Length); if (properties.ContainsKey("LODGroup")) { properties["LODGroup"].Value.StringValue = "TEXTUREGROUP_LightAndShadowMap"; properties["LODGroup"].Value.String2 = pcc.Names[0]; } else { buffer.WriteValueS64(pcc.AddName("LODGroup")); buffer.WriteValueS64(pcc.AddName("ByteProperty")); buffer.WriteValueS64(8); buffer.WriteValueS64(pcc.AddName("TEXTUREGROUP_LightAndShadowMap")); } int count = 0; foreach (KeyValuePair<string, SaltPropertyReader.Property> kvp in properties) { SaltPropertyReader.Property prop = kvp.Value; if (prop.Name == "UnpackMin") { for (int j = 0; j < UnpackNum; j++) { buffer.WriteValueS64(pcc.AddName(prop.Name)); buffer.WriteValueS64(pcc.AddName(prop.TypeVal.ToString())); buffer.WriteValueS32(prop.Size); buffer.WriteValueS32(j); buffer.WriteValueF32(prop.Value.FloatValue, Endian.Little); } continue; } buffer.WriteValueS64(pcc.AddName(prop.Name)); if (prop.Name == "None") { for (int j = 0; j < 12; j++) buffer.WriteByte(0); } else { buffer.WriteValueS64(pcc.AddName(prop.TypeVal.ToString())); buffer.WriteValueS64(prop.Size); switch (prop.TypeVal) { case SaltPropertyReader.Type.IntProperty: buffer.WriteValueS32(prop.Value.IntValue); break; case SaltPropertyReader.Type.BoolProperty: buffer.WriteValueS32(prop.Value.IntValue); break; case SaltPropertyReader.Type.NameProperty: buffer.WriteValueS64(pcc.AddName(prop.Value.StringValue)); // Heff: Modified to handle name references. //var index = pcc.AddName(prop.Value.StringValue); //buffer.WriteValueS32(index); //buffer.WriteValueS32(prop.Value.NameValue.count); break; case SaltPropertyReader.Type.StrProperty: buffer.WriteValueS32(prop.Value.StringValue.Length + 1); foreach (char c in prop.Value.StringValue) buffer.WriteByte((byte)c); buffer.WriteByte(0); break; case SaltPropertyReader.Type.StructProperty: string strVal = prop.Value.StringValue; if (prop.Name.ToLowerInvariant().Contains("guid")) strVal = "Guid"; buffer.WriteValueS64(pcc.AddName(strVal)); foreach (SaltPropertyReader.PropertyValue value in prop.Value.Array) buffer.WriteValueS32(value.IntValue); break; case SaltPropertyReader.Type.ByteProperty: buffer.WriteValueS32(pcc.AddName(prop.Value.StringValue)); buffer.WriteValueS32(pcc.AddName(prop.Value.String2)); break; case SaltPropertyReader.Type.FloatProperty: buffer.WriteValueF32(prop.Value.FloatValue, Endian.Little); break; default: throw new FormatException("unknown property"); } } } buffer.WriteValueS32((int)buffer.Position + (int)pccExportDataOffset); //Remove empty textures List<ImageInfo> tempList = new List<ImageInfo>(); foreach (ImageInfo imgInfo in privateimgList) { if (imgInfo.storageType != storage.empty) tempList.Add(imgInfo); } privateimgList = tempList; numMipMaps = (uint)privateimgList.Count; buffer.WriteValueU32(numMipMaps); foreach (ImageInfo imgInfo in privateimgList) { buffer.WriteValueS32((int)imgInfo.storageType); buffer.WriteValueS32(imgInfo.uncSize); buffer.WriteValueS32(imgInfo.cprSize); if (imgInfo.storageType == storage.pccSto) { buffer.WriteValueS32((int)(buffer.Position + pccExportDataOffset)); buffer.Write(imageData, imgInfo.offset, imgInfo.uncSize); } else buffer.WriteValueS32(imgInfo.offset); if (imgInfo.imgSize.width < 4) buffer.WriteValueU32(4); else buffer.WriteValueU32(imgInfo.imgSize.width); if (imgInfo.imgSize.height < 4) buffer.WriteValueU32(4); else buffer.WriteValueU32(imgInfo.imgSize.height); } buffer.WriteBytes(footerData); return buffer.ToArray(); }
public void upscaleImage(ME3SaltTexture2D inTex) { numMipMaps++; int propVal = properties["MipTailBaseIdx"].Value.IntValue; propVal++; properties["MipTailBaseIdx"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["MipTailBaseIdx"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["MipTailBaseIdx"].raw = rawStream.ToArray(); } // update Sizes propVal = (int)privateimgList[0].imgSize.width; properties["SizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["SizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeX"].raw = rawStream.ToArray(); } properties["SizeY"].Value.IntValue = (int)privateimgList[0].imgSize.height; using (MemoryStream rawStream = new MemoryStream(properties["SizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeY"].raw = rawStream.ToArray(); } properties["OriginalSizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeX"].raw = rawStream.ToArray(); } properties["OriginalSizeY"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["OriginalSizeY"].raw = rawStream.ToArray(); } }
public byte[] ToArray(int pccExportDataOffset) { MemoryStream buffer = new MemoryStream(); buffer.Write(headerData, 0, headerData.Length); foreach (KeyValuePair<string, PropertyReader.Property> kvp in properties) { PropertyReader.Property property = kvp.Value; // this is the part when I get rid of the LODGroup property!!!!!!!!!!!!!!! // the texture will use texturegroup_world as default texturegroup //if (kvp.Key == "LODGroup") // continue; if (kvp.Key == "LODBias") continue; if (kvp.Key == "InternalFormatLODBias") continue; buffer.Write(property.raw, 0, property.raw.Length); if (kvp.Key == "UnpackMin") { buffer.Write(property.raw, 0, property.raw.Length); buffer.Write(property.raw, 0, property.raw.Length); } } buffer.WriteValueU32(numMipMaps); foreach (ImageInfo imgInfo in imgList) { buffer.WriteValueS32((int)imgInfo.storageType); buffer.WriteValueS32(imgInfo.uncSize); buffer.WriteValueS32(imgInfo.cprSize); if (imgInfo.storageType == storage.pccSto) { buffer.WriteValueS32((int)(imgInfo.offset + pccExportDataOffset + dataOffset)); buffer.Write(imageData, imgInfo.offset, imgInfo.uncSize); } else buffer.WriteValueS32(imgInfo.offset); buffer.WriteValueU32(imgInfo.imgSize.width); buffer.WriteValueU32(imgInfo.imgSize.height); } // Texture2D footer, 24 bytes size buffer.Write(imageData, imageData.Length-24, 24); return buffer.ToArray(); }
public void addBiggerImage(ImageFile im, string archiveDir) { //DebugOutput.PrintLn( "\nIn Addbiggerimage...\n"); ImageSize biggerImageSizeOnList = privateimgList.Max(image => image.imgSize); // check if replacing image is supported ImageFile imgFile = im; ImageEngineFormat imgFileFormat = Textures.Methods.ParseFormat(imgFile.format); //NEW Check for correct image format if (texFormat != imgFileFormat) throw new FormatException("Different image format, original is " + texFormat + ", new is " + imgFile.subtype()); // check if image to add is valid if (biggerImageSizeOnList.width * 2 != imgFile.imgSize.width || biggerImageSizeOnList.height * 2 != imgFile.imgSize.height) throw new FormatException("image size " + imgFile.imgSize + " isn't valid, must be " + new ImageSize(biggerImageSizeOnList.width * 2, biggerImageSizeOnList.height * 2)); // this check avoids insertion inside textures that have only 1 image stored inside pcc if (privateimgList.Count <= 1) throw new Exception("Unable to add image, texture must have more than one existing image"); // !!! warning, this method breaks consistency between imgList and imageData[] !!! ImageInfo newImgInfo = new ImageInfo(); newImgInfo.storageType = privateimgList.Find(img => img.storageType != storage.empty && img.storageType != storage.pccSto).storageType; newImgInfo.imgSize = imgFile.imgSize; newImgInfo.uncSize = imgFile.resize().Length; newImgInfo.cprSize = 0x00; // not yet filled newImgInfo.offset = 0x00; // not yet filled privateimgList.Insert(0, newImgInfo); // insert new image on top of the list //now I let believe the program that I'm doing an image replace, saving lot of code ;) replaceImage(newImgInfo.imgSize.ToString(), im, archiveDir); //updating num of images numMipMaps++; // update MipTailBaseIdx //PropertyReader.Property MipTail = properties["MipTailBaseIdx"]; int propVal = properties["MipTailBaseIdx"].Value.IntValue; propVal++; properties["MipTailBaseIdx"].Value.IntValue = propVal; //MessageBox.Show("raw size: " + properties["MipTailBaseIdx"].raw.Length + "\nproperty offset: " + properties["MipTailBaseIdx"].offsetval); using (MemoryStream rawStream = new MemoryStream(properties["MipTailBaseIdx"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["MipTailBaseIdx"].raw = rawStream.ToArray(); } //properties["MipTailBaseIdx"] = MipTail; // update Sizes //PropertyReader.Property Size = properties["SizeX"]; // Heff: Fixed(?) to account for non-square images int propX = (int)newImgInfo.imgSize.width; int propY = (int)newImgInfo.imgSize.height; properties["SizeX"].Value.IntValue = propX; using (MemoryStream rawStream = new MemoryStream(properties["SizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propX); properties["SizeX"].raw = rawStream.ToArray(); } //properties["SizeX"] = Size; //Size = properties["SizeY"]; properties["SizeY"].Value.IntValue = propY; using (MemoryStream rawStream = new MemoryStream(properties["SizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propY); properties["SizeY"].raw = rawStream.ToArray(); } //properties["SizeY"] = Size; try { properties["OriginalSizeX"].Value.IntValue = propX; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propX); properties["OriginalSizeX"].raw = rawStream.ToArray(); } properties["OriginalSizeY"].Value.IntValue = propY; using (MemoryStream rawStream = new MemoryStream(properties["OriginalSizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propY); properties["OriginalSizeY"].raw = rawStream.ToArray(); } } catch { // Some lightmaps don't have these properties. I'm ignoring them cos I'm ignorant. KFreon. } //this.hasChanged = true; }
public void addBiggerImage(ImageFile im) { ImageSize biggerImageSizeOnList = privateimgList.Max(image => image.imgSize); // check if replacing image is supported ImageFile imgFile = im; if (imgFile.format == "R8G8B8") { byte[] buff = ImageMipMapHandler.ConvertTo32bit(imgFile.imgData, (int)imgFile.imgSize.width, (int)imgFile.imgSize.height); imgFile = new DDS(null, imgFile.imgSize, "A8R8G8B8", buff); } if (!Methods.CheckTextureFormat(texFormat, imgFile.format)) throw new FormatException("Different image format, original is " + texFormat + ", new is " + imgFile.subtype()); // check if image to add is valid if (biggerImageSizeOnList.width * 2 != imgFile.imgSize.width || biggerImageSizeOnList.height * 2 != imgFile.imgSize.height) throw new FormatException("image size " + imgFile.imgSize + " isn't valid, must be " + new ImageSize(biggerImageSizeOnList.width * 2, biggerImageSizeOnList.height * 2)); // this check avoids insertion inside textures that have only 1 image stored inside pcc //if (!imgList.Exists(img => img.storageType != storage.empty && img.storageType != storage.pccSto)) // throw new Exception("Unable to add image, texture must have a reference to an external archive"); if (privateimgList.Count <= 1) throw new Exception("Unable to add image, texture must have more than one image present"); // !!! warning, this method breaks consistency between imgList and imageData[] !!! ImageInfo newImgInfo = new ImageInfo(); newImgInfo.storageType = privateimgList.Find(img => img.storageType != storage.empty && img.storageType != storage.pccSto).storageType; newImgInfo.imgSize = imgFile.imgSize; newImgInfo.uncSize = imgFile.resize().Length; newImgInfo.cprSize = 0x00; // not yet filled newImgInfo.offset = 0x00; // not yet filled privateimgList.Insert(0, newImgInfo); // insert new image on top of the list //now I let believe the program that I'm doing an image replace, saving lot of code ;) replaceImage(newImgInfo.imgSize.ToString(), im); //updating num of images numMipMaps++; // update MipTailBaseIdx //SaltPropertyReader.Property MipTail = properties["MipTailBaseIdx"]; int propVal = properties["MipTailBaseIdx"].Value.IntValue; propVal++; properties["MipTailBaseIdx"].Value.IntValue = propVal; //MessageBox.Show("raw size: " + properties["MipTailBaseIdx"].raw.Length + "\nproperty offset: " + properties["MipTailBaseIdx"].offsetval); using (MemoryStream rawStream = new MemoryStream(properties["MipTailBaseIdx"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["MipTailBaseIdx"].raw = rawStream.ToArray(); } //properties["MipTailBaseIdx"] = MipTail; // update Sizes //SaltPropertyReader.Property Size = properties["SizeX"]; propVal = (int)newImgInfo.imgSize.width; properties["SizeX"].Value.IntValue = propVal; using (MemoryStream rawStream = new MemoryStream(properties["SizeX"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeX"].raw = rawStream.ToArray(); } //properties["SizeX"] = Size; //Size = properties["SizeY"]; properties["SizeY"].Value.IntValue = (int)newImgInfo.imgSize.height; using (MemoryStream rawStream = new MemoryStream(properties["SizeY"].raw)) { rawStream.Seek(rawStream.Length - 4, SeekOrigin.Begin); rawStream.WriteValueS32(propVal); properties["SizeY"].raw = rawStream.ToArray(); } //properties["SizeY"] = Size; //this.hasChanged = true; }