public void ImportKeys(Vector3[] loc, Vector4[] rot, int time) { MemoryStream m = new MemoryStream(); int pos = 0; var temp = new int[CompressedTrackOffsets.Count]; for (int i = 0; i < CompressedTrackOffsets.Count; i++) { m.Write(BitConverter.GetBytes(loc[i].X), 0, 4); m.Write(BitConverter.GetBytes(loc[i].Y * -1f), 0, 4); m.Write(BitConverter.GetBytes(loc[i].Z), 0, 4); m.Write(BitConverter.GetBytes(rot[i].X), 0, 4); m.Write(BitConverter.GetBytes(rot[i].Y * -1f), 0, 4); m.Write(BitConverter.GetBytes(rot[i].Z), 0, 4); TrackOffsets t = CompressedTrackOffsets[i]; t.Trans = loc[i]; t.TransOffset = pos; t.TransNumKeys = 0; t.Rot = rot[i]; t.RotOffset = pos + 12; t.RotNumKeys = time; CompressedTrackOffsets[i] = t; pos += 24; temp[i * 4] = t.TransOffset; temp[i * 4 + 1] = t.TransNumKeys; temp[i * 4 + 2] = t.RotOffset; temp[i * 4 + 3] = t.RotNumKeys; } Props.AddOrReplaceProp(new ArrayProperty <IntProperty>(temp.Select(i => new IntProperty(i)), "CompressedTrackOffsets")); Props.AddOrReplaceProp(new IntProperty(time, "NumFrames")); CompressedBlob = m.ToArray(); }
private static StructProperty GeneratePlotStreamingElement(string packageName, int conditionalNum) { PropertyCollection pc = new PropertyCollection(); pc.AddOrReplaceProp(new NameProperty(packageName, @"ChunkName")); pc.AddOrReplaceProp(new IntProperty(conditionalNum, @"Conditional")); pc.AddOrReplaceProp(new BoolProperty(false, @"bFallback")); pc.AddOrReplaceProp(new NoneProperty()); return(new StructProperty(@"PlotStreamingElement", pc)); }
public static bool RandomizeExport(ExportEntry export, RandomizationOption option) { if (!CanRandomize(export)) { return(false); } var props = export.GetProperties(); if (export.ObjectName.Name.Contains("SFXPower")) { props.AddOrReplaceProp(new BoolProperty(true, "bCustomDroneColor")); props.AddOrReplaceProp(new BoolProperty(true, "bCustomDroneColor2")); } else { //sfxpawn props.AddOrReplaceProp(new BoolProperty(true, "bCustomColor")); props.AddOrReplaceProp(new BoolProperty(true, "bCustomColor2")); } PropertyCollection randColors = new PropertyCollection(); randColors.AddOrReplaceProp(new FloatProperty(ThreadSafeRandom.NextFloat(0, 60), "X")); randColors.AddOrReplaceProp(new FloatProperty(ThreadSafeRandom.NextFloat(0, 60), "Y")); randColors.AddOrReplaceProp(new FloatProperty(ThreadSafeRandom.NextFloat(0, 60), "Z")); PropertyCollection randColors2 = new PropertyCollection(); randColors2.AddOrReplaceProp(new FloatProperty(ThreadSafeRandom.NextFloat(0, 128), "X")); randColors2.AddOrReplaceProp(new FloatProperty(ThreadSafeRandom.NextFloat(0, 128), "Y")); randColors2.AddOrReplaceProp(new FloatProperty(ThreadSafeRandom.NextFloat(0, 128), "Z")); if (export.ObjectName.Name.Contains("SFXPower")) { props.AddOrReplaceProp(new StructProperty("Vector", randColors, "CustomDroneColor", true)); props.AddOrReplaceProp(new StructProperty("Vector", randColors2, "CustomDroneColor2", true)); } else { //sfxpawn props.AddOrReplaceProp(new StructProperty("Vector", randColors, "DroneColor", true)); props.AddOrReplaceProp(new StructProperty("Vector", randColors2, "DroneColor2", true)); } export.WriteProperties(props); return(true); }
static PropertyCollection replacePropertyReferences(PropertyCollection props, int targetUIndex, int replaceUIndex, ref int replacementCount) { var newprops = new PropertyCollection(); foreach (Property prop in props) { switch (prop) { case ObjectProperty objectProperty: if (objectProperty.Value == targetUIndex) { objectProperty.Value = replaceUIndex; replacementCount++; } break; case DelegateProperty delegateProperty: if (delegateProperty.Value.Object == targetUIndex) { delegateProperty.Value = new ScriptDelegate(replaceUIndex, delegateProperty.Value.FunctionName); replacementCount++; } break; case StructProperty structProperty: structProperty.Properties = replacePropertyReferences(structProperty.Properties, targetUIndex, replaceUIndex, ref replacementCount); break; case ArrayProperty <ObjectProperty> arrayProperty: foreach (ObjectProperty objProp in arrayProperty) { if (objProp.Value == targetUIndex) { objProp.Value = replaceUIndex; replacementCount++; } } break; case ArrayProperty <StructProperty> arrayProperty: foreach (StructProperty structProp in arrayProperty) { structProp.Properties = replacePropertyReferences(structProp.Properties, targetUIndex, replaceUIndex, ref replacementCount); } break; } newprops.AddOrReplaceProp(prop); } return(newprops); }
public static void AddObjectToSequence(ExportEntry newObject, ExportEntry sequenceExport, bool removeLinks = false) { ArrayProperty <ObjectProperty> seqObjs = sequenceExport.GetProperty <ArrayProperty <ObjectProperty> >("SequenceObjects") ?? new ArrayProperty <ObjectProperty>("SequenceObjects"); seqObjs.Add(new ObjectProperty(newObject)); sequenceExport.WriteProperty(seqObjs); PropertyCollection newObjectProps = newObject.GetProperties(); newObjectProps.AddOrReplaceProp(new ObjectProperty(sequenceExport, "ParentSequence")); newObject.WriteProperties(newObjectProps); if (removeLinks) { RemoveAllLinks(newObject); } newObject.Parent = sequenceExport; }
public void UpdateProps(PropertyCollection props, MEGame newGame, AnimationCompressionFormat newRotationCompression = AnimationCompressionFormat.ACF_Float96NoW) { if (compressedDataSource == MEGame.Unknown || (newGame != compressedDataSource && !(newGame <= MEGame.ME3 && compressedDataSource <= MEGame.ME3))) { CompressAnimationData(newGame, newRotationCompression); props.RemoveNamedProperty("KeyEncodingFormat"); props.RemoveNamedProperty("TranslationCompressionFormat"); props.AddOrReplaceProp(new EnumProperty(rotCompression.ToString(), nameof(AnimationCompressionFormat), newGame, "RotationCompressionFormat")); props.AddOrReplaceProp(new ArrayProperty <IntProperty>(TrackOffsets.Select(i => new IntProperty(i)), "CompressedTrackOffsets")); props.AddOrReplaceProp(new IntProperty(NumFrames, "NumFrames")); props.AddOrReplaceProp(new FloatProperty(RateScale, "RateScale")); props.AddOrReplaceProp(new FloatProperty(SequenceLength, "SequenceLength")); props.AddOrReplaceProp(new NameProperty(Name, "SequenceName")); } }
public static string Replace(this Texture2D t2d, Image image, PropertyCollection props, string fileSourcePath = null, string forcedTFCName = null) { string errors = ""; var textureCache = forcedTFCName ?? t2d.GetTopMip().TextureCacheName; string fmt = t2d.TextureFormat; PixelFormat pixelFormat = Image.getPixelFormatType(fmt); t2d.RemoveEmptyMipsFromMipList(); // Not sure what this does? // Remove all but one mip? //if (Export.Game == MEGame.ME1 && texture.mipMapsList.Count < 6) //{ // for (int i = texture.mipMapsList.Count - 1; i != 0; i--) // texture.mipMapsList.RemoveAt(i); //} PixelFormat newPixelFormat = pixelFormat; //Changing Texture Type. Not sure what this is, exactly. //if (mod.markConvert) // newPixelFormat = changeTextureType(pixelFormat, image.pixelFormat, ref package, ref texture); if (!image.checkDDSHaveAllMipmaps() || t2d.Mips.Count > 1 && image.mipMaps.Count() <= 1 || image.pixelFormat != newPixelFormat) //(!mod.markConvert && image.pixelFormat != pixelFormat)) { bool dxt1HasAlpha = false; byte dxt1Threshold = 128; if (pixelFormat == PixelFormat.DXT1 && props.GetProp <EnumProperty>("CompressionSettings") is EnumProperty compressionSettings && compressionSettings.Value.Name == "TC_OneBitAlpha") { dxt1HasAlpha = true; if (image.pixelFormat == PixelFormat.ARGB || image.pixelFormat == PixelFormat.DXT3 || image.pixelFormat == PixelFormat.DXT5) { errors += "Warning: Texture was converted from full alpha to binary alpha." + Environment.NewLine; } } //Generate lower mips image.correctMips(newPixelFormat, dxt1HasAlpha, dxt1Threshold); } if (t2d.Mips.Count == 1) { var topMip = image.mipMaps[0]; image.mipMaps.Clear(); image.mipMaps.Add(topMip); } else { // remove lower mipmaps from source image which not exist in game data //Not sure what this does since we just generated most of these mips for (int t = 0; t < image.mipMaps.Count(); t++) { if (image.mipMaps[t].origWidth <= t2d.Mips[0].width && image.mipMaps[t].origHeight <= t2d.Mips[0].height && t2d.Mips.Count > 1) { if (!t2d.Mips.Exists(m => m.width == image.mipMaps[t].origWidth && m.height == image.mipMaps[t].origHeight)) { image.mipMaps.RemoveAt(t--); } } } // put empty mips if missing for (int t = 0; t < t2d.Mips.Count; t++) { if (t2d.Mips[t].width <= image.mipMaps[0].origWidth && t2d.Mips[t].height <= image.mipMaps[0].origHeight) { if (!image.mipMaps.Exists(m => m.origWidth == t2d.Mips[t].width && m.origHeight == t2d.Mips[t].height)) { MipMap mipmap = new MipMap(t2d.Mips[t].width, t2d.Mips[t].height, pixelFormat); image.mipMaps.Add(mipmap); } } } } //if (!texture.properties.exists("LODGroup")) // texture.properties.setByteValue("LODGroup", "TEXTUREGROUP_Character", "TextureGroup", 1025); List <byte[]> compressedMips = new List <byte[]>(); for (int m = 0; m < image.mipMaps.Count(); m++) { if (t2d.Export.Game == MEGame.ME2) { compressedMips.Add(TextureCompression.CompressTexture(image.mipMaps[m].data, StorageTypes.extLZO)); //LZO } else { compressedMips.Add(TextureCompression.CompressTexture(image.mipMaps[m].data, StorageTypes.extZlib)); //ZLib } } List <Texture2DMipInfo> mipmaps = new List <Texture2DMipInfo>(); for (int m = 0; m < image.mipMaps.Count(); m++) { Texture2DMipInfo mipmap = new Texture2DMipInfo(); mipmap.Export = t2d.Export; mipmap.width = image.mipMaps[m].origWidth; mipmap.height = image.mipMaps[m].origHeight; mipmap.TextureCacheName = textureCache; if (t2d.Mips.Exists(x => x.width == mipmap.width && x.height == mipmap.height)) { var oldMip = t2d.Mips.First(x => x.width == mipmap.width && x.height == mipmap.height); mipmap.storageType = oldMip.storageType; } else { mipmap.storageType = t2d.Mips[0].storageType; if (t2d.Mips.Count() > 1) { //Will implement later. ME3Explorer won't support global relinking, that's MEM's job. //if (Export.Game == MEGame.ME1 && matched.linkToMaster == -1) //{ // if (mipmap.storageType == StorageTypes.pccUnc) // { // mipmap.storageType = StorageTypes.pccLZO; // } //} //else if (Export.Game == MEGame.ME1 && matched.linkToMaster != -1) //{ // if (mipmap.storageType == StorageTypes.pccUnc || // mipmap.storageType == StorageTypes.pccLZO || // mipmap.storageType == StorageTypes.pccZlib) // { // mipmap.storageType = StorageTypes.extLZO; // } //} //else } } //ME2,ME3: Force compression type (not implemented yet) if (t2d.Export.Game == MEGame.ME3) { if (mipmap.storageType == StorageTypes.extLZO) //ME3 LZO -> ZLIB { mipmap.storageType = StorageTypes.extZlib; } if (mipmap.storageType == StorageTypes.pccLZO) //ME3 PCC LZO -> PCCZLIB { mipmap.storageType = StorageTypes.pccZlib; } if (mipmap.storageType == StorageTypes.extUnc) //ME3 Uncomp -> ZLib { mipmap.storageType = StorageTypes.extZlib; } //Leave here for future. WE might need this after dealing with double compression //if (mipmap.storageType == StorageTypes.pccUnc && mipmap.width > 32) //ME3 Uncomp -> ZLib // mipmap.storageType = StorageTypes.pccZlib; if (mipmap.storageType == StorageTypes.pccUnc && m < image.mipMaps.Count() - 6 && textureCache != null) //Moving texture to store externally. { mipmap.storageType = StorageTypes.extZlib; } } else if (t2d.Export.Game == MEGame.ME2) { if (mipmap.storageType == StorageTypes.extZlib) //ME2 ZLib -> LZO { mipmap.storageType = StorageTypes.extLZO; } if (mipmap.storageType == StorageTypes.pccZlib) //M2 ZLib -> LZO { mipmap.storageType = StorageTypes.pccLZO; } if (mipmap.storageType == StorageTypes.extUnc) //ME2 Uncomp -> LZO { mipmap.storageType = StorageTypes.extLZO; } //Leave here for future. We might neable this after dealing with double compression //if (mipmap.storageType == StorageTypes.pccUnc && mipmap.width > 32) //ME2 Uncomp -> LZO // mipmap.storageType = StorageTypes.pccLZO; if (mipmap.storageType == StorageTypes.pccUnc && m < image.mipMaps.Count() - 6 && textureCache != null) //Moving texture to store externally. make sure bottom 6 are pcc stored { mipmap.storageType = StorageTypes.extLZO; } } //Investigate. this has something to do with archive storage types //if (mod.arcTexture != null) //{ // if (mod.arcTexture[m].storageType != mipmap.storageType) // { // mod.arcTexture = null; // } //} mipmap.width = image.mipMaps[m].width; mipmap.height = image.mipMaps[m].height; mipmaps.Add(mipmap); if (t2d.Mips.Count() == 1) { break; } } #region MEM code comments. Should probably leave for reference //if (texture.properties.exists("TextureFileCacheName")) //{ // string archive = texture.properties.getProperty("TextureFileCacheName").valueName; // if (mod.arcTfcDLC && mod.arcTfcName != archive) // mod.arcTexture = null; // if (mod.arcTexture == null) // { // archiveFile = Path.Combine(GameData.MainData, archive + ".tfc"); // if (matched.path.ToLowerInvariant().Contains("\\dlc")) // { // mod.arcTfcDLC = true; // string DLCArchiveFile = Path.Combine(Path.GetDirectoryName(GameData.GamePath + matched.path), archive + ".tfc"); // if (File.Exists(DLCArchiveFile)) // archiveFile = DLCArchiveFile; // else if (!File.Exists(archiveFile)) // { // List<string> files = Directory.GetFiles(GameData.bioGamePath, archive + ".tfc", // SearchOption.AllDirectories).Where(item => item.EndsWith(".tfc", StringComparison.OrdinalIgnoreCase)).ToList(); // if (files.Count == 1) // archiveFile = files[0]; // else if (files.Count == 0) // { // using (FileStream fs = new FileStream(DLCArchiveFile, FileMode.CreateNew, FileAccess.Write)) // { // fs.WriteFromBuffer(texture.properties.getProperty("TFCFileGuid").valueStruct); // } // archiveFile = DLCArchiveFile; // newTfcFile = true; // } // else // throw new Exception("More instnces of TFC file: " + archive + ".tfc"); // } // } // else // { // mod.arcTfcDLC = false; // } // // check if texture fit in old space // for (int mip = 0; mip < image.mipMaps.Count(); mip++) // { // Texture.MipMap testMipmap = new Texture.MipMap(); // testMipmap.width = image.mipMaps[mip].origWidth; // testMipmap.height = image.mipMaps[mip].origHeight; // if (ExistMipmap(testMipmap.width, testMipmap.height)) // testMipmap.storageType = texture.getMipmap(testMipmap.width, testMipmap.height).storageType; // else // { // oldSpace = false; // break; // } // if (testMipmap.storageType == StorageTypes.extZlib || // testMipmap.storageType == StorageTypes.extLZO) // { // Texture.MipMap oldTestMipmap = texture.getMipmap(testMipmap.width, testMipmap.height); // if (mod.cacheCprMipmaps[mip].Length > oldTestMipmap.compressedSize) // { // oldSpace = false; // break; // } // } // if (texture.mipMapsList.Count() == 1) // break; // } // long fileLength = new FileInfo(archiveFile).Length; // if (!oldSpace && fileLength + 0x5000000 > 0x80000000) // { // archiveFile = ""; // foreach (TFCTexture newGuid in guids) // { // archiveFile = Path.Combine(GameData.MainData, newGuid.name + ".tfc"); // if (!File.Exists(archiveFile)) // { // texture.properties.setNameValue("TextureFileCacheName", newGuid.name); // texture.properties.setStructValue("TFCFileGuid", "Guid", newGuid.guid); // using (FileStream fs = new FileStream(archiveFile, FileMode.CreateNew, FileAccess.Write)) // { // fs.WriteFromBuffer(newGuid.guid); // } // newTfcFile = true; // break; // } // else // { // fileLength = new FileInfo(archiveFile).Length; // if (fileLength + 0x5000000 < 0x80000000) // { // texture.properties.setNameValue("TextureFileCacheName", newGuid.name); // texture.properties.setStructValue("TFCFileGuid", "Guid", newGuid.guid); // break; // } // } // archiveFile = ""; // } // if (archiveFile == "") // throw new Exception("No free TFC texture file!"); // } // } // else // { // texture.properties.setNameValue("TextureFileCacheName", mod.arcTfcName); // texture.properties.setStructValue("TFCFileGuid", "Guid", mod.arcTfcGuid); // } //} #endregion int allextmipssize = 0; for (int m = 0; m < image.mipMaps.Count(); m++) { Texture2DMipInfo x = mipmaps[m]; var compsize = image.mipMaps[m].data.Length; if (x.storageType == StorageTypes.extZlib || x.storageType == StorageTypes.extLZO || x.storageType == StorageTypes.extUnc) { allextmipssize += compsize; //compsize on Unc textures is same as LZO/ZLib } } //todo: check to make sure TFC will not be larger than 2GiB Guid tfcGuid = Guid.NewGuid(); //make new guid as storage bool locallyStored = mipmaps[0].storageType == StorageTypes.pccUnc || mipmaps[0].storageType == StorageTypes.pccZlib || mipmaps[0].storageType == StorageTypes.pccLZO; for (int m = 0; m < image.mipMaps.Count(); m++) { Texture2DMipInfo mipmap = mipmaps[m]; //if (mipmap.width > 32) //{ // if (mipmap.storageType == StorageTypes.pccUnc) // { // mipmap.storageType = Export.Game == MEGame.ME2 ? StorageTypes.pccLZO : StorageTypes.pccZlib; // } // if (mipmap.storageType == StorageTypes.extUnc) // { // mipmap.storageType = Export.Game == MEGame.ME2 ? StorageTypes.extLZO : StorageTypes.extZlib; // } //} mipmap.uncompressedSize = image.mipMaps[m].data.Length; if (t2d.Export.Game == MEGame.ME1) { if (mipmap.storageType == StorageTypes.pccLZO || mipmap.storageType == StorageTypes.pccZlib) { mipmap.Mip = compressedMips[m]; mipmap.compressedSize = mipmap.Mip.Length; } else if (mipmap.storageType == StorageTypes.pccUnc) { mipmap.compressedSize = mipmap.uncompressedSize; mipmap.Mip = image.mipMaps[m].data; } else { throw new Exception("Unknown mip storage type!"); } } else { if (mipmap.storageType == StorageTypes.extZlib || mipmap.storageType == StorageTypes.extLZO) { if (compressedMips.Count != image.mipMaps.Count()) { throw new Exception("Amount of compressed mips does not match number of mips of incoming image!"); } mipmap.Mip = compressedMips[m]; mipmap.compressedSize = mipmap.Mip.Length; } if (mipmap.storageType == StorageTypes.pccUnc || mipmap.storageType == StorageTypes.extUnc) { mipmap.compressedSize = mipmap.uncompressedSize; mipmap.Mip = image.mipMaps[m].data; } if (mipmap.storageType == StorageTypes.pccLZO || mipmap.storageType == StorageTypes.pccZlib) { mipmap.Mip = compressedMips[m]; mipmap.compressedSize = mipmap.Mip.Length; } if (mipmap.storageType == StorageTypes.extZlib || mipmap.storageType == StorageTypes.extLZO || mipmap.storageType == StorageTypes.extUnc) { if (!string.IsNullOrEmpty(mipmap.TextureCacheName) && mipmap.Export.Game != MEGame.ME1) { //Check local dir string tfcarchive = mipmap.TextureCacheName + ".tfc"; var localDirectoryTFCPath = Path.Combine(Path.GetDirectoryName(mipmap.Export.FileRef.FilePath), tfcarchive); if (File.Exists(localDirectoryTFCPath)) { try { using (FileStream fs = new FileStream(localDirectoryTFCPath, FileMode.Open, FileAccess.ReadWrite)) { tfcGuid = fs.ReadGuid(); fs.Seek(0, SeekOrigin.End); mipmap.externalOffset = (int)fs.Position; fs.Write(mipmap.Mip, 0, mipmap.compressedSize); } } catch (Exception e) { throw new Exception("Problem appending to TFC file " + tfcarchive + ": " + e.Message); } continue; } //Check game var gameFiles = MELoadedFiles.GetFilesLoadedInGame(mipmap.Export.Game, includeTFCs: true); if (gameFiles.TryGetValue(tfcarchive, out string archiveFile)) { try { using (FileStream fs = new FileStream(archiveFile, FileMode.Open, FileAccess.ReadWrite)) { tfcGuid = fs.ReadGuid(); fs.Seek(0, SeekOrigin.End); mipmap.externalOffset = (int)fs.Position; fs.Write(mipmap.Mip, 0, mipmap.compressedSize); } } catch (Exception e) { throw new Exception("Problem appending to TFC file " + archiveFile + ": " + e.Message); } continue; } //Cache not found. Make new TFC try { using (FileStream fs = new FileStream(localDirectoryTFCPath, FileMode.OpenOrCreate, FileAccess.Write)) { fs.WriteGuid(tfcGuid); mipmap.externalOffset = (int)fs.Position; fs.Write(mipmap.Mip, 0, mipmap.compressedSize); } } catch (Exception e) { throw new Exception("Problem creating new TFC file " + tfcarchive + ": " + e.Message); } continue; } } } mipmaps[m] = mipmap; if (t2d.Mips.Count() == 1) { break; } } t2d.ReplaceMips(mipmaps); //Set properties // The bottom 6 mips are apparently always pcc stored. If there is less than 6 mips, set neverstream to true, which tells game // and toolset to never look into archives for mips. //if (Export.Game == MEGame.ME2 || Export.Game == MEGame.ME3) //{ // if (texture.properties.exists("TextureFileCacheName")) // { // if (texture.mipMapsList.Count < 6) // { // mipmap.storageType = StorageTypes.pccUnc; // texture.properties.setBoolValue("NeverStream", true); // } // else // { // if (Export.Game == MEGame.ME2) // mipmap.storageType = StorageTypes.extLZO; // else // mipmap.storageType = StorageTypes.extZlib; // } // } //} var hasNeverStream = props.GetProp <BoolProperty>("NeverStream") != null; if (locallyStored) { // Rules for default neverstream // 1. Must be Package Stored // 2. Must have at least 6 not empty mips if (mipmaps.Count >= 6) { props.AddOrReplaceProp(new BoolProperty(true, "NeverStream")); } // Side case: NeverStream property was already set, we should respect the value // But won't that always be set here? // Is there a time where we should remove NeverStream? I can't see any logical way // neverstream would be here } if (mipmaps.Count < 6) { props.RemoveNamedProperty("NeverStream"); } if (!locallyStored) { props.AddOrReplaceProp(tfcGuid.ToGuidStructProp("TFCFileGuid")); if (mipmaps[0].storageType == StorageTypes.extLZO || mipmaps[0].storageType == StorageTypes.extUnc || mipmaps[0].storageType == StorageTypes.extZlib) { //Requires texture cache name props.AddOrReplaceProp(new NameProperty(textureCache, "TextureFileCacheName")); } else { //Should not have texture cache name var cacheProp = props.GetProp <NameProperty>("TextureFileCacheName"); if (cacheProp != null) { props.Remove(cacheProp); } } } else { props.RemoveNamedProperty("TFCFileGuid"); } props.AddOrReplaceProp(new IntProperty(t2d.Mips.First().width, "SizeX")); props.AddOrReplaceProp(new IntProperty(t2d.Mips.First().height, "SizeY")); if (t2d.Export.Game < MEGame.ME3 && fileSourcePath != null) { props.AddOrReplaceProp(new StrProperty(fileSourcePath, "SourceFilePath")); props.AddOrReplaceProp(new StrProperty(File.GetLastWriteTimeUtc(fileSourcePath).ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), "SourceFileTimestamp")); } var mipTailIdx = props.GetProp <IntProperty>("MipTailBaseIdx"); if (mipTailIdx != null) { mipTailIdx.Value = t2d.Mips.Count - 1; } EndianReader mem = new EndianReader(new MemoryStream()) { Endian = t2d.Export.FileRef.Endian }; props.WriteTo(mem.Writer, t2d.Export.FileRef); mem.Position = 0; var test = PropertyCollection.ReadProps(t2d.Export, mem.BaseStream, "Texture2D", true, true); //do not set properties as this may interfere with some other code. may change later. int propStart = t2d.Export.GetPropertyStart(); var pos = mem.Position; mem.Position = 0; byte[] propData = mem.ToArray(); if (t2d.Export.Game == MEGame.ME3) { t2d.Export.Data = t2d.Export.Data.Take(propStart).Concat(propData).Concat(t2d.SerializeNewData()).ToArray(); } else { var array = t2d.Export.Data.Take(propStart).Concat(propData).ToArray(); var testdata = new MemoryStream(array); var test2 = PropertyCollection.ReadProps(t2d.Export, testdata, "Texture2D", true, true, t2d.Export); //do not set properties as this may interfere with some other code. may change later. //ME2 post-data is this right? t2d.Export.Data = t2d.Export.Data.Take(propStart).Concat(propData).Concat(t2d.SerializeNewData()).ToArray(); } //using (MemoryStream newData = new MemoryStream()) //{ // newData.WriteFromBuffer(texture.properties.toArray()); // newData.WriteFromBuffer(texture.toArray(0, false)); // filled later // package.setExportData(matched.exportID, newData.ToArray()); //} //using (MemoryStream newData = new MemoryStream()) //{ // newData.WriteFromBuffer(texture.properties.toArray()); // newData.WriteFromBuffer(texture.toArray(package.exportsTable[matched.exportID].dataOffset + (uint)newData.Position)); // package.setExportData(matched.exportID, newData.ToArray()); //} //Since this is single replacement, we don't want to relink to master //We want to ensure names are different though, will have to implement into UI //if (Export.Game == MEGame.ME1) //{ // if (matched.linkToMaster == -1) // mod.masterTextures.Add(texture.mipMapsList, entryMap.listIndex); //} //else //{ // if (triggerCacheArc) // { // mod.arcTexture = texture.mipMapsList; // mod.arcTfcGuid = texture.properties.getProperty("TFCFileGuid").valueStruct; // mod.arcTfcName = texture.properties.getProperty("TextureFileCacheName").valueName; // } //} return(errors); }
public bool ApplyUpdate(IMEPackage package, PropertyCollection properties, MergeFileChange1 mfc) { var propKeys = PropertyName.Split('.'); PropertyCollection operatingCollection = properties; int i = 0; while (i < propKeys.Length - 1) { var matchingProp = operatingCollection.FirstOrDefault(x => x.Name.Instanced == propKeys[i]); if (matchingProp is StructProperty sp) { operatingCollection = sp.Properties; } // ARRAY PROPERTIES NOT SUPPORTED i++; } Log.Information($@"Applying property update: {PropertyName} -> {PropertyValue}"); switch (PropertyType) { case @"FloatProperty": FloatProperty fp = new FloatProperty(float.Parse(PropertyValue, CultureInfo.InvariantCulture), propKeys.Last()); operatingCollection.AddOrReplaceProp(fp); break; case @"IntProperty": IntProperty ip = new IntProperty(int.Parse(PropertyValue), propKeys.Last()); operatingCollection.AddOrReplaceProp(ip); break; case @"BoolProperty": BoolProperty bp = new BoolProperty(bool.Parse(PropertyValue), propKeys.Last()); operatingCollection.AddOrReplaceProp(bp); break; case @"NameProperty": var index = 0; var baseName = PropertyValue; var indexIndex = PropertyValue.IndexOf(@"|", StringComparison.InvariantCultureIgnoreCase); if (indexIndex > 0) { baseName = baseName.Substring(0, indexIndex); index = int.Parse(baseName.Substring(indexIndex + 1)); } NameProperty np = new NameProperty(new NameReference(baseName, index), PropertyName); operatingCollection.AddOrReplaceProp(np); break; case @"ObjectProperty": // This does not support porting in, only relinking existing items ObjectProperty op = new ObjectProperty(0, PropertyName); if (PropertyValue != null && PropertyValue != @"M3M_NULL") //M3M_NULL is a keyword for setting it to null to satisfy the schema { var entry = package.FindEntry(PropertyValue); if (entry == null) { throw new Exception(M3L.GetString(M3L.string_interp_mergefile_failedToUpdateObjectPropertyItemNotInPackage, PropertyName, PropertyValue, PropertyValue, package.FilePath)); } op.Value = entry.UIndex; } operatingCollection.AddOrReplaceProp(op); break; case @"EnumProperty": var enumInfo = PropertyValue.Split('.'); EnumProperty ep = new EnumProperty(enumInfo[0], mfc.OwningMM.Game, PropertyName); ep.Value = NameReference.FromInstancedString(enumInfo[1]); operatingCollection.AddOrReplaceProp(ep); break; case @"StrProperty": var sp = new StrProperty(PropertyValue, propKeys.Last()); operatingCollection.AddOrReplaceProp(sp); break; default: throw new Exception(M3L.GetString(M3L.string_interp_mergefile_unsupportedPropertyType, PropertyType)); } return(true); }