コード例 #1
0
        private static bool ForcedRun(ExportEntry hairMeshExport)
        {
            if (hairMeshExport.GetProperty <ObjectProperty>("SkeletalMesh") is ObjectProperty obj && obj.Value != 0 && obj.ResolveToEntry(hairMeshExport.FileRef) is IEntry entry)
            {
                var isfemaleHair = entry.ObjectName.Name.StartsWith("HMF_HIR_");
                var newHair      = isfemaleHair ? HairListFemale.RandomElement() : HairListMale.RandomElement();
                if (newHair.ObjectName.Name == entry.ObjectName.Name)
                {
                    return(false); // We are not changing this
                }
                MERLog.Information($"{Path.GetFileName(hairMeshExport.FileRef.FilePath)} Changing hair mesh: {entry.ObjectName} -> {newHair.ObjectName}, object {hairMeshExport.FullPath}, class {entry.ClassName}");
                var newHairMdl = PackageTools.PortExportIntoPackage(hairMeshExport.FileRef, newHair);
                var mdlBin     = ObjectBinary.From <SkeletalMesh>(newHairMdl);
                obj.Value = newHairMdl.UIndex;
                hairMeshExport.WriteProperty(obj);

                // Update the materials
                var materials = hairMeshExport.GetProperty <ArrayProperty <ObjectProperty> >("Materials");
                if (materials != null && materials.Any())
                {
                    //if (materials.Count() != 1)
                    //    Debugger.Break();
                    var mat = materials[0].ResolveToEntry(hairMeshExport.FileRef) as ExportEntry;
                    if (mat != null)
                    {
                        mat.WriteProperty(new ObjectProperty(mdlBin.Materials[0], "Parent"));
                        var parentMat = mdlBin.Materials[0] > 0 ? hairMeshExport.FileRef.GetUExport(mdlBin.Materials[0]) : EntryImporter.ResolveImport(hairMeshExport.FileRef.GetImport(mdlBin.Materials[0]));
                        // Need to match child to parent params that start with HAIR
                        var parentMatTextureParms = parentMat.GetProperty <ArrayProperty <StructProperty> >("TextureParameterValues");
                        if (parentMatTextureParms != null)
                        {
                            var parentMatHairParms = parentMatTextureParms.Where(x => x.Properties.GetProp <NameProperty>("ParameterName").Value.Name.StartsWith("HAIR_")).ToList();

                            // Need to match child to parent params that start with HAIR
                            var matTextureParms = mat.GetProperty <ArrayProperty <StructProperty> >("TextureParameterValues");
                            if (matTextureParms != null)
                            {
                                var matHairParms = matTextureParms.Where(x => x.Properties.GetProp <NameProperty>("ParameterName").Value.Name.StartsWith("HAIR_")).ToList();

                                // Map them
                                foreach (var matHairParm in matHairParms)
                                {
                                    var locName        = matHairParm.Properties.GetProp <NameProperty>("ParameterName");
                                    var matchingParent = parentMatHairParms.FirstOrDefault(x => x.Properties.GetProp <NameProperty>("ParameterName").Value == locName.Value);

                                    // Assign it
                                    if (matchingParent != null)
                                    {
                                        matHairParm.Properties.AddOrReplaceProp(matchingParent.GetProp <ObjectProperty>("ParameterValue"));
                                    }
                                }
                                mat.WriteProperty(matTextureParms);
                            }
                        }
                        else
                        {
                        }
                    }

                    //foreach (var mat in materials)
                    //{

                    //    mdlBin.
                    //}
                }
                return(true);
            }
            return(false);
        }
コード例 #2
0
        /// <summary>
        /// Ports an export into a package. Checks if the export already exists, and if it does, returns that instead.
        /// </summary>
        /// <param name="targetPackage">The target package to port into.</param>
        /// <param name="sourceExport">The source export to port over, including all dependencies and references.</param>
        /// <param name="targetLink">The target link UIndex. Only used if createParentPackages is false.</param>
        /// <param name="createParentPackages">If the export should be ported in the same way as it was cooked into the package natively, e.g. create the parent package paths. The export must directly sit under a Package or an exception will be thrown.</param>
        /// <param name="ensureMemoryUniqueness">If this object is an instance, such as a sequence object, and should be made memory-unique so it is properly used</param>
        /// <returns></returns>
        public static ExportEntry PortExportIntoPackage(IMEPackage targetPackage, ExportEntry sourceExport, int targetLink = 0, bool createParentPackages = true, bool ensureMemoryUniqueness = false, bool useMemorySafeImport = false, PackageCache cache = null)
        {
#if DEBUG
            // in preprocessor to prevent this from running in release mode
            if (sourceExport.FileRef.FilePath != null && targetPackage.FilePath != null)
            {
                Debug.WriteLine($"Porting {sourceExport.InstancedFullPath} from {Path.GetFileName(sourceExport.FileRef.FilePath)} into {Path.GetFileName(targetPackage.FilePath)}");
            }
#endif
            var existing = targetPackage.FindExport(sourceExport.InstancedFullPath);
            if (existing != null)
            {
                return(existing);
            }

            // Create parent hierarchy
            IEntry newParent = null;
            if (createParentPackages)
            {
                List <IEntry> parents = new List <IEntry>();
                var           parent  = sourceExport.Parent;
                while (parent != null)
                {
                    if (parent.ClassName != "Package")
                    {
                        throw new Exception("Parent is not package!");
                    }
                    parents.Add(parent);
                    parent = parent.Parent;
                }

                // Create the parents
                parents.Reverse();
                foreach (var p in parents)
                {
                    var sourceFullPath = p.InstancedFullPath;
                    var matchingParent = targetPackage.FindEntry(sourceFullPath);

                    if (matchingParent != null)
                    {
                        newParent = matchingParent;
                        continue;
                    }

                    newParent = ExportCreator.CreatePackageExport(targetPackage, p.ObjectName, newParent);
                }
            }
            else
            {
                newParent = targetPackage.GetEntry(targetLink);
            }


            IEntry newEntry;
            if (!useMemorySafeImport)
            {
                Dictionary <IEntry, IEntry> crossPCCObjectMap = new Dictionary <IEntry, IEntry>(); // Not sure what this is used for these days. Should probably just be part of the method
                var relinkResults = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceExport, targetPackage,
                                                                         newParent, true, out newEntry, crossPCCObjectMap);
                if (relinkResults.Any())
                {
                    Debugger.Break();
                }
            }
            else
            {
                // Memory safe, fixes upstream
                var relinkedResults = EntryExporter.ExportExportToPackage(sourceExport, targetPackage, out newEntry, MERFileSystem.GetGlobalCache(), cache);
                if (relinkedResults.Any())
                {
                    Debugger.Break();
                }
            }
#if DEBUG
            //(sourceExport.FileRef as MEPackage).CompareToPackageDetailed(targetPackage);
#endif

            // Helps ensure we don't have memory duplicates
            if (ensureMemoryUniqueness)
            {
                newEntry.ObjectName = targetPackage.GetNextIndexedName(newEntry.ObjectName);
            }

            return(newEntry as ExportEntry);
        }
コード例 #3
0
 public override void SetUp()
 {
     base.SetUp();
     EntryImporter.Import(EntryImporterTest.SourceDirectory,
                          ContentDirectory);
 }
コード例 #4
0
        public static void ScanStuff(PackageEditorWPF pewpf)
        {
            //var filePaths = MELoadedFiles.GetOfficialFiles(MEGame.ME3);//.Concat(MELoadedFiles.GetOfficialFiles(MEGame.ME2));//.Concat(MELoadedFiles.GetOfficialFiles(MEGame.ME1));
            //var filePaths = MELoadedFiles.GetAllFiles(game);
            /*"Core.pcc", "Engine.pcc", "GameFramework.pcc", "GFxUI.pcc", "WwiseAudio.pcc", "SFXOnlineFoundation.pcc", "SFXGame.pcc" */
            var filePaths = new[] { "Core.pcc", "Engine.pcc", "GameFramework.pcc", "GFxUI.pcc", "WwiseAudio.pcc", "SFXOnlineFoundation.pcc" }.Select(f => Path.Combine(ME3Directory.CookedPCPath, f));
            var interestingExports = new List <EntryStringPair>();
            var foundClasses       = new HashSet <string>(); //new HashSet<string>(BinaryInterpreterWPF.ParsableBinaryClasses);
            var foundProps         = new Dictionary <string, string>();


            var unkOpcodes = new List <int>();//Enumerable.Range(0x5B, 8).ToList();

            unkOpcodes.Add(0);
            unkOpcodes.Add(1);
            var unkOpcodesInfo = unkOpcodes.ToDictionary(i => i, i => new OpcodeInfo());
            var comparisonDict = new Dictionary <string, (byte[] original, byte[] newData)>();

            var extraInfo = new HashSet <string>();

            pewpf.IsBusy   = true;
            pewpf.BusyText = "Scanning";
            Task.Run(() =>
            {
                //preload base files for faster scanning
                using var baseFiles = MEPackageHandler.OpenMEPackages(EntryImporter.FilesSafeToImportFrom(MEGame.ME3)
                                                                      .Select(f => Path.Combine(ME3Directory.CookedPCPath, f)));
                baseFiles.Add(
                    MEPackageHandler.OpenMEPackage(Path.Combine(ME3Directory.CookedPCPath, "BIOP_MP_COMMON.pcc")));

                foreach (string filePath in filePaths)
                {
                    //ScanShaderCache(filePath);
                    //ScanMaterials(filePath);
                    //ScanStaticMeshComponents(filePath);
                    //ScanLightComponents(filePath);
                    //ScanLevel(filePath);
                    //if (findClass(filePath, "ShaderCache", true)) break;
                    //findClassesWithBinary(filePath);
                    ScanScripts2(filePath);
                    //if (interestingExports.Count > 0)
                    //{
                    //    break;
                    //}
                    //if (resolveImports(filePath)) break;
                }
            }).ContinueWithOnUIThread(prevTask =>
            {
                pewpf.IsBusy = false;
                interestingExports.Add(new EntryStringPair(null, string.Join("\n", extraInfo)));
                var listDlg = new ListDialog(interestingExports, "Interesting Exports", "", pewpf)
                {
                    DoubleClickEntryHandler = entryItem =>
                    {
                        if (entryItem?.Entry is IEntry entryToSelect)
                        {
                            PackageEditorWPF p = new PackageEditorWPF();
                            p.Show();
                            p.LoadFile(entryToSelect.FileRef.FilePath, entryToSelect.UIndex);
                            p.Activate();
                            if (comparisonDict.TryGetValue($"{entryToSelect.UIndex} {entryToSelect.FileRef.FilePath}", out (byte[] original, byte[] newData)val))
                            {
                                File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "original.bin"), val.original);
                                File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "new.bin"), val.newData);
                            }
                        }
                    }
                };
                listDlg.Show();
            });

            #region extra scanning functions

            bool findClass(string filePath, string className, bool withBinary = false)
            {
                Debug.WriteLine($" {filePath}");
                using (IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath))
                {
                    //if (!pcc.IsCompressed) return false;

                    var exports = pcc.Exports.Where(exp => !exp.IsDefaultObject && exp.IsA(className));
                    foreach (ExportEntry exp in exports)
                    {
                        try
                        {
                            //Debug.WriteLine($"{exp.UIndex}: {filePath}");
                            var originalData = exp.Data;
                            exp.WriteBinary(ObjectBinary.From(exp));
                            var newData = exp.Data;
                            if (!originalData.SequenceEqual(newData))
                            {
                                interestingExports.Add(exp);
                                File.WriteAllBytes(
                                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                                                 "original.bin"), originalData);
                                File.WriteAllBytes(
                                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                                                 "new.bin"), newData);
                                return(true);
                            }
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine(exception);
                            interestingExports.Add(new EntryStringPair(exp, $"{exception}"));
                            return(true);
                        }
                    }
                }

                return(false);
            }

            void findClassesWithBinary(string filePath)
            {
                using (IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath))
                {
                    foreach (ExportEntry exp in pcc.Exports.Where(exp => !exp.IsDefaultObject))
                    {
                        try
                        {
                            if (!foundClasses.Contains(exp.ClassName) && exp.propsEnd() < exp.DataSize)
                            {
                                if (ObjectBinary.From(exp) != null)
                                {
                                    foundClasses.Add(exp.ClassName);
                                }
                                else if (exp.GetBinaryData().Any(b => b != 0))
                                {
                                    foundClasses.Add(exp.ClassName);
                                    interestingExports.Add(exp);
                                }
                            }
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine(exception);
                            interestingExports.Add(new EntryStringPair(exp, $"{exp.UIndex}: {filePath}\n{exception}"));
                        }
                    }
                }
            }

            void ScanShaderCache(string filePath)
            {
                using (IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath))
                {
                    ExportEntry shaderCache = pcc.Exports.FirstOrDefault(exp => exp.ClassName == "ShaderCache");
                    if (shaderCache == null)
                    {
                        return;
                    }
                    int oldDataOffset = shaderCache.DataOffset;

                    try
                    {
                        MemoryStream binData = new MemoryStream(shaderCache.Data);
                        binData.JumpTo(shaderCache.propsEnd() + 1);

                        int nameList1Count = binData.ReadInt32();
                        binData.Skip(nameList1Count * 12);

                        int namelist2Count = binData.ReadInt32(); //namelist2
                        binData.Skip(namelist2Count * 12);

                        int shaderCount = binData.ReadInt32();
                        for (int i = 0; i < shaderCount; i++)
                        {
                            binData.Skip(24);
                            int nextShaderOffset = binData.ReadInt32() - oldDataOffset;
                            binData.Skip(14);
                            if (binData.ReadInt32() != 1111577667) //CTAB
                            {
                                interestingExports.Add(new EntryStringPair(null,
                                                                           $"{binData.Position - 4}: {filePath}"));
                                return;
                            }

                            binData.JumpTo(nextShaderOffset);
                        }

                        int vertexFactoryMapCount = binData.ReadInt32();
                        binData.Skip(vertexFactoryMapCount * 12);

                        int materialShaderMapCount = binData.ReadInt32();
                        for (int i = 0; i < materialShaderMapCount; i++)
                        {
                            binData.Skip(16);

                            int switchParamCount = binData.ReadInt32();
                            binData.Skip(switchParamCount * 32);

                            int componentMaskParamCount = binData.ReadInt32();
                            //if (componentMaskParamCount != 0)
                            //{
                            //    interestingExports.Add($"{i}: {filePath}");
                            //    return;
                            //}

                            binData.Skip(componentMaskParamCount * 44);

                            int normalParams = binData.ReadInt32();
                            if (normalParams != 0)
                            {
                                interestingExports.Add(new EntryStringPair(null, $"{i}: {filePath}"));
                                return;
                            }

                            binData.Skip(normalParams * 29);

                            int unrealVersion   = binData.ReadInt32();
                            int licenseeVersion = binData.ReadInt32();
                            if (unrealVersion != 684 || licenseeVersion != 194)
                            {
                                interestingExports.Add(new EntryStringPair(null,
                                                                           $"{binData.Position - 8}: {filePath}"));
                                return;
                            }

                            int nextMaterialShaderMapOffset = binData.ReadInt32() - oldDataOffset;
                            binData.JumpTo(nextMaterialShaderMapOffset);
                        }
                    }
                    catch (Exception exception)
                    {
                        Console.WriteLine(exception);
                        interestingExports.Add(new EntryStringPair(null, $"{filePath}\n{exception}"));
                    }
                }
            }

            void ScanScripts(string filePath)
            {
                using IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath);
                foreach (ExportEntry exp in pcc.Exports.Where(exp => !exp.IsDefaultObject))
                {
                    try
                    {
                        if ((exp.ClassName == "State" || exp.ClassName == "Function") &&
                            ObjectBinary.From(exp) is UStruct uStruct)
                        {
                            byte[] data = exp.Data;
                            (_, List <BytecodeSingularToken> tokens) = Bytecode.ParseBytecode(uStruct.ScriptBytes, exp);
                            foreach (var token in tokens)
                            {
                                if (token.CurrentStack.Contains("UNKNOWN") || token.OpCodeString.Contains("UNKNOWN"))
                                {
                                    interestingExports.Add(exp);
                                }

                                if (unkOpcodes.Contains(token.OpCode))
                                {
                                    int    refUIndex = EndianReader.ToInt32(data, token.StartPos + 1, pcc.Endian);
                                    IEntry entry     = pcc.GetEntry(refUIndex);
                                    if (entry != null && (entry.ClassName == "ByteProperty"))
                                    {
                                        var info = unkOpcodesInfo[token.OpCode];
                                        info.Usages.Add(pcc.FilePath, exp.UIndex, token.StartPos);
                                        info.PropTypes.Add(refUIndex switch
                                        {
                                            0 => "Null",
                                            _ when entry != null => entry.ClassName,
                                            _ => "Invalid"
                                        });
                                        if (entry != null)
                                        {
                                            if (entry.Parent == exp)
                                            {
                                                info.PropLocations.Add("Local");
                                            }
                                            else if (entry.Parent == (exp.Parent.ClassName == "State" ? exp.Parent.Parent : exp.Parent))
                                            {
                                                info.PropLocations.Add("ThisClass");
                                            }
                                            else if (entry.Parent.ClassName == "Function")
                                            {
                                                info.PropLocations.Add("OtherFunction");
                                            }
                                            else if (exp.Parent.IsA(entry.Parent.ObjectName))
                                            {
                                                info.PropLocations.Add("AncestorClass");
                                            }
                                            else
                                            {
                                                info.PropLocations.Add("OtherClass");
                                            }
                                        }
                                    }
                                }
                            }
コード例 #5
0
        public static void ConvertTo(this MEPackage package, MEGame newGame, string tfcPath = null, bool preserveMaterialInstances = false)
        {
            MEGame oldGame         = package.Game;
            var    prePropBinary   = new List <byte[]>(package.ExportCount);
            var    propCollections = new List <PropertyCollection>(package.ExportCount);
            var    postPropBinary  = new List <ObjectBinary>(package.ExportCount);

            if (oldGame == MEGame.ME1 && newGame != MEGame.ME1)
            {
                int idx = package.Names.IndexOf("BIOC_Base");
                if (idx >= 0)
                {
                    package.replaceName(idx, "SFXGame");
                }
            }
            else if (newGame == MEGame.ME1)
            {
                int idx = package.Names.IndexOf("SFXGame");
                if (idx >= 0)
                {
                    package.replaceName(idx, "BIOC_Base");
                }
            }

            //fix up Default_ package.Imports
            if (newGame == MEGame.ME3)
            {
                using IMEPackage core    = MEPackageHandler.OpenMEPackage(Path.Combine(ME3Directory.CookedPCPath, "Core.pcc"));
                using IMEPackage engine  = MEPackageHandler.OpenMEPackage(Path.Combine(ME3Directory.CookedPCPath, "Engine.pcc"));
                using IMEPackage sfxGame = MEPackageHandler.OpenMEPackage(Path.Combine(ME3Directory.CookedPCPath, "SFXGame.pcc"));
                foreach (ImportEntry defImp in package.Imports.Where(imp => imp.ObjectName.Name.StartsWith("Default_")).ToList())
                {
                    string     packageName = defImp.FullPath.Split('.')[0];
                    IMEPackage pck         = packageName switch
                    {
                        "Core" => core,
                        "Engine" => engine,
                        "SFXGame" => sfxGame,
                        _ => null
                    };
                    if (pck != null && pck.Exports.FirstOrDefault(exp => exp.ObjectName == defImp.ObjectName) is ExportEntry defExp)
                    {
                        List <IEntry> impChildren = defImp.GetChildren();
                        List <IEntry> expChildren = defExp.GetChildren();
                        foreach (IEntry expChild in expChildren)
                        {
                            if (impChildren.FirstOrDefault(imp => imp.ObjectName == expChild.ObjectName) is ImportEntry matchingImp)
                            {
                                impChildren.Remove(matchingImp);
                            }
                            else
                            {
                                package.AddImport(new ImportEntry(package)
                                {
                                    idxLink     = defImp.UIndex,
                                    ClassName   = expChild.ClassName,
                                    ObjectName  = expChild.ObjectName,
                                    PackageFile = defImp.PackageFile
                                });
                            }
                        }

                        foreach (IEntry impChild in impChildren)
                        {
                            EntryPruner.TrashEntries(package, impChild.GetAllDescendants().Prepend(impChild));
                        }
                    }
                }
            }

            //purge MaterialExpressions
            if (newGame == MEGame.ME3)
            {
                var entriesToTrash = new List <IEntry>();
                foreach (ExportEntry mat in package.Exports.Where(exp => exp.ClassName == "Material").ToList())
                {
                    entriesToTrash.AddRange(mat.GetAllDescendants());
                }
                EntryPruner.TrashEntries(package, entriesToTrash.ToHashSet());
            }

            EntryPruner.TrashIncompatibleEntries(package, oldGame, newGame);

            foreach (ExportEntry export in package.Exports)
            {
                //convert stack, or just get the pre-prop binary if no stack
                prePropBinary.Add(ExportBinaryConverter.ConvertPrePropBinary(export, newGame));

                PropertyCollection props = export.ClassName == "Class" ? null : EntryPruner.RemoveIncompatibleProperties(package, export.GetProperties(), export.ClassName, newGame);
                propCollections.Add(props);

                //convert binary data
                postPropBinary.Add(ExportBinaryConverter.ConvertPostPropBinary(export, newGame, props));

                //writes header in whatever format is correct for newGame
                export.RegenerateHeader(newGame, true);
            }

            package.setGame(newGame);

            for (int i = 0; i < package.Exports.Count; i++)
            {
                package.Exports[i].WritePrePropsAndPropertiesAndBinary(prePropBinary[i], propCollections[i], postPropBinary[i]);
            }

            if (newGame != MEGame.ME3)  //Fix Up Textures before Materials
            {
                foreach (ExportEntry texport in package.Exports.Where(exp => exp.IsTexture()))
                {
                    texport.WriteProperty(new BoolProperty(true, "NeverStream"));
                }
            }
            else if (package.Exports.Any(exp => exp.IsTexture() && Texture2D.GetTexture2DMipInfos(exp, null)
                                         .Any(mip => mip.storageType == StorageTypes.pccLZO ||
                                              mip.storageType == StorageTypes.pccZlib)))
            {
                //ME3 can't deal with compressed textures in a pcc, so we'll need to stuff them into a tfc
                tfcPath ??= Path.ChangeExtension(package.FilePath, "tfc");
                string tfcName = Path.GetFileNameWithoutExtension(tfcPath);
                using var tfc = new FileStream(tfcPath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                Guid tfcGuid;
                if (tfc.Length >= 16)
                {
                    tfcGuid = tfc.ReadGuid();
                    tfc.SeekEnd();
                }
                else
                {
                    tfcGuid = Guid.NewGuid();
                    tfc.WriteGuid(tfcGuid);
                }

                foreach (ExportEntry texport in package.Exports.Where(exp => exp.IsTexture()))
                {
                    List <Texture2DMipInfo> mips = Texture2D.GetTexture2DMipInfos(texport, null);
                    var offsets = new List <int>();
                    foreach (Texture2DMipInfo mipInfo in mips)
                    {
                        if (mipInfo.storageType == StorageTypes.pccLZO || mipInfo.storageType == StorageTypes.pccZlib)
                        {
                            offsets.Add((int)tfc.Position);
                            byte[] mip = mipInfo.storageType == StorageTypes.pccLZO
                                ? TextureCompression.CompressTexture(Texture2D.GetTextureData(mipInfo, texport.Game), StorageTypes.extZlib)
                                : Texture2D.GetTextureData(mipInfo, texport.Game, decompress: false);
                            tfc.WriteFromBuffer(mip);
                        }
                    }
                    offsets.Add((int)tfc.Position);
                    texport.WriteBinary(ExportBinaryConverter.ConvertTexture2D(texport, package.Game, offsets, StorageTypes.extZlib));
                    texport.WriteProperty(new NameProperty(tfcName, "TextureFileCacheName"));
                    texport.WriteProperty(tfcGuid.ToGuidStructProp("TFCFileGuid"));
                }
            }
            if (oldGame == MEGame.ME3 && newGame != MEGame.ME3)
            {
                int idx = package.Names.IndexOf("location");
                if (idx >= 0)
                {
                    package.replaceName(idx, "Location");
                }
            }
            else if (newGame == MEGame.ME3)
            {
                int idx = package.Names.IndexOf("Location");
                if (idx >= 0)
                {
                    package.replaceName(idx, "location");
                }
            }

            if (newGame == MEGame.ME3) //Special handling where materials have been ported between games.
            {
                //change all materials to default material, but try to preserve diff and norm textures
                using var resourcePCC = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetCustomAppResourceStream(MEGame.ME3));
                var defaultmaster = resourcePCC.Exports.First(exp => exp.ObjectName == "NormDiffMaterial");
                var materiallist  = package.Exports.Where(exp => exp.ClassName == "Material" || exp.ClassName == "MaterialInstanceConstant").ToList();
                foreach (var mat in materiallist)
                {
                    Debug.WriteLine($"Fixing up {mat.FullPath}");
                    var      masterMat        = defaultmaster;
                    var      hasDefaultMaster = true;
                    UIndex[] textures         = Array.Empty <UIndex>();
                    if (mat.ClassName == "Material")
                    {
                        textures = ObjectBinary.From <Material>(mat).SM3MaterialResource.UniformExpressionTextures;
                        switch (mat.FullPath)
                        {
                        case "BioT_Volumetric.LAG_MM_Volumetric":
                        case "BioT_Volumetric.LAG_MM_FalloffSphere":
                        case "BioT_LevelMaster.Materials.Opaque_MM":
                        case "BioT_LevelMaster.Materials.GUI_Lit_MM":
                        case "BioT_LevelMaster.Materials.Signage.MM_GUIMaster_Emissive":
                        case "BioT_LevelMaster.Materials.Signage.MM_GUIMaster_Emissive_Fallback":
                        case "BioT_LevelMaster.Materials.Opaque_Standard_MM":
                        case "BioT_LevelMaster.Tech_Inset_MM":
                        case "BioT_LevelMaster.Tech_Border_MM":
                        case "BioT_LevelMaster.Brushed_Metal":
                            masterMat        = resourcePCC.Exports.First(exp => exp.FullPath == mat.FullPath);
                            hasDefaultMaster = false;
                            break;

                        default:
                            break;
                        }
                    }
                    else if (mat.GetProperty <BoolProperty>("bHasStaticPermutationResource")?.Value == true)
                    {
                        if (mat.GetProperty <ObjectProperty>("Parent") is ObjectProperty parentProp && package.GetEntry(parentProp.Value) is IEntry parent && parent.ClassName == "Material")
                        {
                            switch (parent.FullPath)
                            {
                            case "BioT_LevelMaster.Materials.Opaque_MM":
                                masterMat        = resourcePCC.Exports.First(exp => exp.FullPath == "Materials.Opaque_MM_INST");
                                hasDefaultMaster = false;
                                break;

                            case "BIOG_APL_MASTER_MATERIAL.Placeable_MM":
                                masterMat        = resourcePCC.Exports.First(exp => exp.FullPath == "Materials.Placeable_MM_INST");
                                hasDefaultMaster = false;
                                break;

                            case "BioT_LevelMaster.Materials.Opaque_Standard_MM":
                                masterMat        = resourcePCC.Exports.First(exp => exp.FullPath == "Materials.Opaque_Standard_MM_INST");
                                hasDefaultMaster = false;
                                break;

                            default:
                                textures = ObjectBinary.From <MaterialInstance>(mat).SM3StaticPermutationResource.UniformExpressionTextures;
                                break;
                            }

                            if (!hasDefaultMaster && mat.GetProperty <ArrayProperty <StructProperty> >("TextureParameterValues") is ArrayProperty <StructProperty> texParams)
                            {
                                textures = texParams.Select(structProp => new UIndex(structProp.GetProp <ObjectProperty>("ParameterValue")?.Value ?? 0)).ToArray();
                            }
                        }
                    }
                    else if (preserveMaterialInstances)
                    {
                        continue;
                    }
                    else if (mat.GetProperty <ArrayProperty <StructProperty> >("TextureParameterValues") is ArrayProperty <StructProperty> texParams)
                    {
                        textures = texParams.Select(structProp => new UIndex(structProp.GetProp <ObjectProperty>("ParameterValue")?.Value ?? 0)).ToArray();
                    }
                    else if (mat.GetProperty <ObjectProperty>("Parent") is ObjectProperty parentProp && package.GetEntry(parentProp.Value) is ExportEntry parent && parent.ClassName == "Material")
                    {
                        textures = ObjectBinary.From <Material>(parent).SM3MaterialResource.UniformExpressionTextures;
                    }

                    if (hasDefaultMaster)
                    {
                        EntryImporter.ReplaceExportDataWithAnother(masterMat, mat);
                        int norm = 0;
                        int diff = 0;
                        foreach (UIndex texture in textures)
                        {
                            if (package.GetEntry(texture) is IEntry tex)
                            {
                                if (diff == 0 && tex.ObjectName.Name.Contains("diff", StringComparison.OrdinalIgnoreCase))
                                {
                                    diff = texture;
                                }
                                else if (norm == 0 && tex.ObjectName.Name.Contains("norm", StringComparison.OrdinalIgnoreCase))
                                {
                                    norm = texture;
                                }
                            }
                        }
                        if (diff == 0)
                        {
                            diff = EntryImporter.GetOrAddCrossImportOrPackage("EngineMaterials.DefaultDiffuse", resourcePCC, package).UIndex;
                        }

                        var matBin = ObjectBinary.From <Material>(mat);
                        matBin.SM3MaterialResource.UniformExpressionTextures = new UIndex[] { norm, diff };
                        mat.WriteBinary(matBin);
                        mat.Class = package.Imports.First(imp => imp.ObjectName == "Material");
                    }
                    else if (mat.ClassName == "Material")
                    {
                        var mmparent = EntryImporter.GetOrAddCrossImportOrPackage(masterMat.ParentFullPath, resourcePCC, package);
                        EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, masterMat, package, mmparent, true, out IEntry targetexp);
                        mat.ReplaceAllReferencesToThisOne(targetexp);
                        EntryPruner.TrashEntryAndDescendants(mat);
                    }
                    else if (mat.ClassName == "MaterialInstanceConstant")
                    {
                        try
                        {
                            var matprops        = mat.GetProperties();
                            var parentlightguid = masterMat.GetProperty <StructProperty>("ParentLightingGuid");
                            matprops.AddOrReplaceProp(parentlightguid);
                            var mguid = masterMat.GetProperty <StructProperty>("m_Guid");
                            matprops.AddOrReplaceProp(mguid);
                            var lguid = masterMat.GetProperty <StructProperty>("LightingGuid");
                            matprops.AddOrReplaceProp(lguid);
                            var    masterBin          = ObjectBinary.From <MaterialInstance>(masterMat);
                            var    matBin             = ObjectBinary.From <MaterialInstance>(mat);
                            var    staticResTextures3 = masterBin.SM3StaticPermutationResource.UniformExpressionTextures.ToList();
                            var    newtextures3       = new List <UIndex>();
                            var    staticResTextures2 = masterBin.SM2StaticPermutationResource.UniformExpressionTextures.ToList();
                            var    newtextures2       = new List <UIndex>();
                            IEntry norm = null;
                            IEntry diff = null;
                            IEntry spec = null;
                            foreach (var texref in textures)
                            {
                                IEntry texEnt  = package.GetEntry(texref);
                                string texName = texEnt?.ObjectName ?? "None";
                                if (texName.ToLowerInvariant().Contains("norm"))
                                {
                                    norm = texEnt;
                                }
                                else if (texName.ToLowerInvariant().Contains("diff"))
                                {
                                    diff = texEnt;
                                }
                                else if (texName.ToLowerInvariant().Contains("spec"))
                                {
                                    spec = texEnt;
                                }
                                else if (texName.ToLowerInvariant().Contains("msk"))
                                {
                                    spec = texEnt;
                                }
                            }

                            foreach (var texidx in staticResTextures2)
                            {
                                var    masterTxt = resourcePCC.GetEntry(texidx);
                                IEntry newTxtEnt = masterTxt;
                                switch (masterTxt?.ObjectName.Name)
                                {
                                case "DefaultDiffuse":
                                    if (diff != null)
                                    {
                                        newTxtEnt = diff;
                                    }
                                    break;

                                case "DefaultNormal":
                                    if (norm != null)
                                    {
                                        newTxtEnt = norm;
                                    }
                                    break;

                                case "Gray":      //Spec
                                    if (spec != null)
                                    {
                                        newTxtEnt = spec;
                                    }
                                    break;

                                default:
                                    break;
                                }

                                var newtexidx = package.Exports.FirstOrDefault(x => x.FullPath == newTxtEnt.FullPath)?.UIndex ?? 0;
                                if (newtexidx == 0)
                                {
                                    newtexidx = package.Imports.FirstOrDefault(x => x.FullPath == newTxtEnt.FullPath)?.UIndex ?? 0;
                                }
                                if (newTxtEnt == masterTxt && newtexidx == 0)
                                {
                                    var texparent = EntryImporter.GetOrAddCrossImportOrPackage(newTxtEnt.ParentFullPath, resourcePCC, package);
                                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, newTxtEnt, package, texparent, true, out IEntry newtext);
                                    newtextures2.Add(newtext?.UIndex ?? 0);
                                }
                                else
                                {
                                    newtextures2.Add(newtexidx);
                                }
                            }

                            foreach (var texidx in staticResTextures3)
                            {
                                var    masterTxt = resourcePCC.GetEntry(texidx);
                                IEntry newTxtEnt = masterTxt;
                                switch (masterTxt?.ObjectName)
                                {
                                case "DefaultDiffuse":
                                    if (diff != null)
                                    {
                                        newTxtEnt = diff;
                                    }
                                    break;

                                case "DefaultNormal":
                                    if (norm != null)
                                    {
                                        newTxtEnt = norm;
                                    }
                                    break;

                                case "Gray":      //Spec
                                    if (spec != null)
                                    {
                                        newTxtEnt = spec;
                                    }
                                    break;

                                default:
                                    break;
                                }
                                var newtexidx = package.Exports.FirstOrDefault(x => x.FullPath == newTxtEnt.FullPath)?.UIndex ?? 0;
                                if (newtexidx == 0)
                                {
                                    newtexidx = package.Imports.FirstOrDefault(x => x.FullPath == newTxtEnt.FullPath)?.UIndex ?? 0;
                                }
                                if (newTxtEnt == masterTxt && newtexidx == 0)
                                {
                                    var texparent = EntryImporter.GetOrAddCrossImportOrPackage(newTxtEnt.ParentFullPath, resourcePCC, package);
                                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, newTxtEnt, package, texparent, true, out IEntry newtext);
                                    newtextures3.Add(newtext?.UIndex ?? 0);
                                }
                                else
                                {
                                    newtextures3.Add(newtexidx);
                                }
                            }
                            masterBin.SM2StaticPermutationResource.UniformExpressionTextures = newtextures2.ToArray();
                            masterBin.SM3StaticPermutationResource.UniformExpressionTextures = newtextures3.ToArray();
                            mat.WritePropertiesAndBinary(matprops, masterBin);
                        }
                        catch
                        {
                            Debug.WriteLine("MaterialInstanceConversion error");
                        }
                    }
                }
            }
        }
    }