Пример #1
0
        public static void BuildStartupPackage()
        {
            var weaponAnims = MEPackageHandler.OpenMEPackage(@"C:\Users\mgame\source\repos\ME2Randomizer\ME2Randomizer\staticfiles\binary\WeaponAnims.pcc");
            var existingSUF = MEPackageHandler.OpenMEPackage(@"B:\SteamLibrary\steamapps\common\Mass Effect 2\BioGame\DLC\DLC_PRE_General\CookedPC\Startup_PRE_General_int.pcc");

            var startupFile = @"C:\Users\mgame\source\repos\ME2Randomizer\ME2Randomizer\staticfiles\binary\Startup_DLC_MOD_ME2Randomizer_INT.pcc";

            MEPackageHandler.CreateAndSavePackage(startupFile, MERFileSystem.Game);
            var startupPackage = MEPackageHandler.OpenMEPackage(startupFile);

            var objReferencer = existingSUF.FindExport("ObjectReferencer_0");

            objReferencer.WriteProperty(new ArrayProperty <ObjectProperty>("ReferencedObjects")); // prevent porting other stuff
            var sObjRefencer = EntryImporter.ImportExport(startupPackage, objReferencer, 0, true);

            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, weaponAnims.FindExport("WeaponAnimData"), startupPackage, null, true, out _);
            var referencedObjects = new ArrayProperty <ObjectProperty>("ReferencedObjects");

            referencedObjects.AddRange(startupPackage.Exports.Where(x => x.ClassName == "AnimSet").Select(x => new ObjectProperty(x.UIndex)));


            sObjRefencer.WriteProperty(referencedObjects);
            startupPackage.Save(compress: true);
            Debug.WriteLine("Done");
        }
Пример #2
0
        public bool ApplyUpdate(IMEPackage package, ExportEntry targetExport, Mod installingMod)
        {
            Stream binaryStream;

            if (OwningMM.Assets[AssetName].AssetBinary != null)
            {
                binaryStream = new MemoryStream(OwningMM.Assets[AssetName].AssetBinary);
            }
            else
            {
                var sourcePath = FilesystemInterposer.PathCombine(installingMod.IsInArchive, installingMod.ModPath, Mod.MergeModFolderName, OwningMM.MergeModFilename);
                using var fileS = File.OpenRead(sourcePath);
                fileS.Seek(OwningMM.Assets[AssetName].FileOffset, SeekOrigin.Begin);
                binaryStream = fileS.
                               ReadToMemoryStream(OwningMM.Assets[AssetName].FileSize);
            }

            using var sourcePackage = MEPackageHandler.OpenMEPackageFromStream(binaryStream);
            var sourceEntry = sourcePackage.FindExport(EntryName);

            if (sourceEntry == null)
            {
                throw new Exception(M3L.GetString(M3L.string_interp_mergefile_cannotFindAssetEntryInAssetPackage, AssetName, EntryName));
            }

            var resultst = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.ReplaceSingular,
                                                                sourceEntry, targetExport.FileRef, targetExport, true, new RelinkerOptionsPackage()
            {
                ErrorOccurredCallback =
                    x => throw new Exception(M3L.GetString(M3L.string_interp_mergefile_errorMergingAssetsX, x))
            }, out _);
Пример #3
0
        public static void SetUpAnimStreamFile(string animSourceFilePath, int animSequenceUIndex, string saveAsName)
        {
            string animViewerAnimStreamFilePath = Path.Combine(App.ExecFolder, "ME3AnimViewer_StreamAnim.pcc");

            using IMEPackage pcc = MEPackageHandler.OpenMEPackage(animViewerAnimStreamFilePath);
            if (animSourceFilePath != null)
            {
                try
                {
                    const int InterpDataUIndex             = 8;
                    const int InterpTrackAnimControlUIndex = 10;
                    const int KIS_DYN_AnimsetUIndex        = 6;
#if DEBUG
                    Debug.WriteLine($"AnimViewer Loading: {animSourceFilePath} #{animSequenceUIndex}");
#endif
                    using IMEPackage animSourceFile = MEPackageHandler.OpenMEPackage(animSourceFilePath);

                    ExportEntry sourceAnimSeq = animSourceFile.GetUExport(animSequenceUIndex);

                    IEntry parent = EntryImporter.GetOrAddCrossImportOrPackage(sourceAnimSeq.ParentFullPath, animSourceFile, pcc);

                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceAnimSeq, pcc, parent, true, out IEntry ent);
                    ExportEntry importedAnimSeq = (ExportEntry)ent;

                    NameReference seqName    = importedAnimSeq.GetProperty <NameProperty>("SequenceName").Value;
                    float         seqLength  = importedAnimSeq.GetProperty <FloatProperty>("SequenceLength");
                    IEntry        bioAnimSet = pcc.GetEntry(importedAnimSeq.GetProperty <ObjectProperty>("m_pBioAnimSetData").Value);
                    string        setName    = importedAnimSeq.ObjectName.Name.RemoveRight(seqName.Name.Length + 1);

                    ExportEntry animInterpData = pcc.GetUExport(InterpDataUIndex);
                    animInterpData.WriteProperty(new FloatProperty(seqLength, "InterpLength"));

                    ExportEntry animTrack   = pcc.GetUExport(InterpTrackAnimControlUIndex);
                    var         animSeqKeys = animTrack.GetProperty <ArrayProperty <StructProperty> >("AnimSeqs");
                    animSeqKeys[0].Properties.AddOrReplaceProp(new NameProperty(seqName, "AnimSeqName"));
                    animTrack.WriteProperty(animSeqKeys);

                    ExportEntry dynamicAnimSet = pcc.GetUExport(KIS_DYN_AnimsetUIndex);
                    dynamicAnimSet.WriteProperty(new ObjectProperty(bioAnimSet.UIndex, "m_pBioAnimSetData"));
                    dynamicAnimSet.WriteProperty(new NameProperty(setName, "m_nmOrigSetName"));
                    dynamicAnimSet.WriteProperty(new ArrayProperty <ObjectProperty>("Sequences")
                    {
                        new ObjectProperty(importedAnimSeq.UIndex)
                    });
                }
                catch (Exception e)
                {
                    MessageBox.Show($"Error Loading {animSourceFilePath} #{animSequenceUIndex}");
                }
            }

            string tempFilePath = Path.Combine(ME3Directory.CookedPCPath, $"{saveAsName}.pcc");

            pcc.Save(tempFilePath);
            InteropHelper.TryPadFile(tempFilePath, 10_485_760);
        }
Пример #4
0
        public static void TestPropertiesInBinaryAssets()
        {
            var dlcModPath            = @"C:\Users\mgame\source\repos\ME2Randomizer\ME2Randomizer\staticfiles\binary";
            ReferenceCheckPackage rcp = new ReferenceCheckPackage();
            bool checkCanceled        = false;

            EntryChecker.CheckReferences(rcp, dlcModPath, EntryChecker.NonLocalizedStringConverter, x => Debug.WriteLine(x));

            var cookedPC       = ME2Directory.CookedPCPath;
            var sourcePackages = Directory.GetFiles(cookedPC, "SFX*.pcc", SearchOption.TopDirectoryOnly).Select(x => MEPackageHandler.OpenMEPackage(x)).ToList();

            sourcePackages.AddRange(Directory.GetFiles(cookedPC, "Bio*.pcc", SearchOption.TopDirectoryOnly).Select(x => MEPackageHandler.OpenMEPackage(x)));


            foreach (var s in rcp.GetInfoWarnings())
            {
                Debug.WriteLine($"INFO: {s.Message}");
            }

            foreach (var s in rcp.GetSignificantIssues())
            {
                Debug.WriteLine($"SIGNIFICANT: {s.Message}");
                if (s.Entry is ExportEntry exp)
                {
                    ExportEntry sourceToPull = null;
                    foreach (var sp in sourcePackages)
                    {
                        sourceToPull = sp.FindExport(exp.InstancedFullPath);
                        if (sourceToPull != null)
                        {
                            break;
                        }
                    }
                    if (sourceToPull != null)
                    {
                        EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.ReplaceSingular, sourceToPull, exp.FileRef, exp, true, out _);
                        Debug.WriteLine(" > REPLACED");
                    }
                }
            }

            foreach (var s in rcp.GetBlockingErrors())
            {
                Debug.WriteLine($"BLOCKING: {s.Message}");
            }

            var packagesToSave = rcp.GetSignificantIssues().Where(x => x.Entry != null && x.Entry.FileRef.IsModified).Select(x => x.Entry.FileRef).Distinct().ToList();

            foreach (var p in packagesToSave)
            {
                p.Save();
            }
        }
        public static bool RandomizeIconicFemShep(RandomizationOption option)
        {
            var femF = MERFileSystem.GetPackageFile("BIOG_Female_Player_C.pcc");

            if (femF != null && File.Exists(femF))
            {
                var femP         = MEPackageHandler.OpenMEPackage(femF);
                var femMorphFace = femP.GetUExport(682);
                RBioMorphFace.RandomizeExportNonHench(femMorphFace, option);
                var matSetup = femP.GetUExport(681);
                RBioMaterialOverride.RandomizeExport(matSetup, option);

                // Copy this data into BioP_Char so you get accurate results
                var biop_charF = MERFileSystem.GetPackageFile(@"BioP_Char.pcc");
                var biop_char  = MEPackageHandler.OpenMEPackage(biop_charF);
                EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.ReplaceSingular, femMorphFace, biop_char, biop_char.GetUExport(3482), true, out IEntry _);
                EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.ReplaceSingular, matSetup, biop_char, biop_char.GetUExport(3472), true, out IEntry _);
                //biop_char.GetUExport(3482).WriteProperties(femMorphFace.GetProperties()); // Copy the morph face
                //biop_char.GetUExport(3472).WriteProperties(matSetup.GetProperties()); // Copy the material setups
                MERFileSystem.SavePackage(biop_char);
                MERFileSystem.SavePackage(femP);
            }
            return(true);
        }
Пример #6
0
        public static IEntry EnsureClassIsInFile(IMEPackage pcc, string className)
        {
            //check to see class is already in file
            foreach (ImportEntry import in pcc.Imports)
            {
                if (import.IsClass && import.ObjectName == className)
                {
                    return(import);
                }
            }
            foreach (ExportEntry export in pcc.Exports)
            {
                if (export.IsClass && export.ObjectName == className)
                {
                    return(export);
                }
            }

            ClassInfo info = UnrealObjectInfo.GetClassOrStructInfo(pcc.Game, className);

            //backup some package state so we can undo changes if something goes wrong
            int           exportCount    = pcc.ExportCount;
            int           importCount    = pcc.ImportCount;
            List <string> nameListBackup = pcc.Names.ToList();

            try
            {
                if (EntryImporter.IsSafeToImportFrom(info.pccPath, pcc.Game))
                {
                    string package = Path.GetFileNameWithoutExtension(info.pccPath);
                    return(pcc.getEntryOrAddImport($"{package}.{className}"));
                }

                //It's a class that's defined locally in every file that uses it.
                Stream loadStream = null;
                if (info.pccPath == UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName)
                {
                    loadStream = Utilities.GetCustomAppResourceStream(pcc.Game);
                    //string resourceFilePath = App.CustomResourceFilePath(pcc.Game);
                    //if (File.Exists(resourceFilePath))
                    //{
                    //    sourceFilePath = resourceFilePath;
                    //}
                }
                else
                {
                    string testPath = Path.Combine(MEDirectories.GetBioGamePath(pcc.Game), info.pccPath);
                    if (File.Exists(testPath))
                    {
                        loadStream = new MemoryStream(File.ReadAllBytes(testPath));
                    }
                    else if (pcc.Game == MEGame.ME1)
                    {
                        testPath = Path.Combine(ME1Directory.DefaultGamePath, info.pccPath);
                        if (File.Exists(testPath))
                        {
                            loadStream = new MemoryStream(File.ReadAllBytes(testPath));
                        }
                    }
                }

                if (loadStream == null)
                {
                    //can't find file to import from. This may occur if user does not have game or neccesary dlc installed
                    return(null);
                }

                using IMEPackage sourcePackage = MEPackageHandler.OpenMEPackageFromStream(loadStream);

                if (!sourcePackage.IsUExport(info.exportIndex))
                {
                    return(null); //not sure how this would happen
                }

                ExportEntry sourceClassExport = sourcePackage.GetUExport(info.exportIndex);

                if (sourceClassExport.ObjectName != className)
                {
                    return(null);
                }

                //Will make sure that, if the class is in a package, that package will exist in pcc
                IEntry parent = EntryImporter.GetOrAddCrossImportOrPackage(sourceClassExport.ParentFullPath, sourcePackage, pcc);

                var relinkResults = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceClassExport, pcc, parent, true, out IEntry result);
                if (relinkResults?.Count > 0)
                {
                    ListDialog ld = new ListDialog(relinkResults, "Relink report", "The following items failed to relink.", null);
                    ld.Show();
                }
                return(result);
            }
            catch (Exception e)
            {
                //remove added entries
                var entriesToRemove = new List <IEntry>();
                for (int i = exportCount; i < pcc.Exports.Count; i++)
                {
                    entriesToRemove.Add(pcc.Exports[i]);
                }
                for (int i = importCount; i < pcc.Imports.Count; i++)
                {
                    entriesToRemove.Add(pcc.Imports[i]);
                }
                EntryPruner.TrashEntries(pcc, entriesToRemove);
                pcc.restoreNames(nameListBackup);
                return(null);
            }
        }
Пример #7
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(ME3ExplorerCoreUtilities.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");
                        }
                    }
                }
            }
        }
    }
        /// <summary>
        /// Ports a power into a package
        /// </summary>
        /// <param name="targetPackage"></param>
        /// <param name="powerInfo"></param>
        /// <param name="additionalPowers">A list of additioanl powers that are referenced when this powerinfo is an import only power (prevent re-opening package)</param>
        /// <returns></returns>
        public static IEntry PortPowerIntoPackage(IMEPackage targetPackage, PowerInfo powerInfo, out IMEPackage sourcePackage)
        {
            if (powerInfo.IsCorrectedPackage)
            {
                var sourceData = MERUtilities.GetEmbeddedStaticFilesBinaryFile("correctedloadouts.powers." + powerInfo.PackageFileName);
                sourcePackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(sourceData));
            }
            else
            {
                sourcePackage = NonSharedPackageCache.Cache.GetCachedPackage(powerInfo.PackageFileName);
            }

            if (sourcePackage != null)
            {
                var sourceExport = sourcePackage.GetUExport(powerInfo.SourceUIndex);
                if (!sourceExport.InheritsFrom("SFXPower") || sourceExport.IsDefaultObject)
                {
                    throw new Exception("Wrong setup!");
                }
                if (sourceExport.Parent != null && sourceExport.Parent.ClassName != "Package")
                {
                    throw new Exception("Cannot port power - parent object is not Package!");
                }

                var    newParent = EntryExporter.PortParents(sourceExport, targetPackage);
                IEntry newEntry;
                if (powerInfo.ImportOnly)
                {
                    //Debug.WriteLine($"ImportOnly in file {targetPackage.FilePath}");
                    if (powerInfo.RequiresStartupPackage)
                    {
                        ThreadSafeDLCStartupPackage.AddStartupPackage(Path.GetFileNameWithoutExtension(powerInfo.PackageFileName));
                        if (powerInfo.IsCorrectedPackage)
                        {
                            // File must be added to MERFS DLC
                            var outP = Path.Combine(MERFileSystem.DLCModCookedPath, powerInfo.PackageFileName);
                            if (!File.Exists(outP))
                            {
                                sourcePackage.Save(outP, true);
                            }
                        }
                    }

                    newEntry = PackageTools.CreateImportForClass(sourceExport, targetPackage, newParent);

                    // Port in extra imports so the calling class can reference them as necessary.
                    foreach (var addlPow in powerInfo.AdditionalRequiredPowers)
                    {
                        var addlSourceExp = sourcePackage.FindExport(addlPow);
                        PackageTools.CreateImportForClass(addlSourceExp, targetPackage, EntryExporter.PortParents(addlSourceExp, targetPackage));
                    }
                }
                else
                {
#if DEBUG
                    // DEBUG ONLY-----------------------------------
                    //var defaults = sourceExport.GetDefaults();
                    //defaults.RemoveProperty("VFX");
                    //var vfx = defaults.GetProperty<ObjectProperty>("VFX").ResolveToEntry(sourcePackage) as ExportEntry;
                    //vxx.RemoveProperty("PlayerCrust");
                    //vfx.FileRef.GetUExport(1544).RemoveProperty("oPrefab");

                    ////vfx = defaults.FileRef.GetUExport(6211); // Prefab
                    ////vfx.RemoveProperty("WorldImpactVisualEffect");
                    //MERPackageCache cached = new MERPackageCache();
                    //EntryExporter.ExportExportToPackage(vfx, targetPackage, out newEntry, cached);
                    //PackageTools.AddReferencesToWorld(targetPackage, new [] {newEntry as ExportEntry});

                    //return null;


                    // END DEBUG ONLY--------------------------------
#endif
                    List <EntryStringPair> relinkResults = null;
                    if ((powerInfo.IsCorrectedPackage || (PackageTools.IsPersistentPackage(powerInfo.PackageFileName) && MERFileSystem.GetPackageFile(powerInfo.PackageFileName.ToLocalizedFilename()) == null)))
                    {
                        // Faster this way, without having to check imports
                        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
                        relinkResults = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceExport, targetPackage,
                                                                             newParent, true, out newEntry, crossPCCObjectMap);
                    }
                    else
                    {
                        // MEMORY SAFE (resolve imports to exports)
                        MERPackageCache cache = new MERPackageCache();
                        relinkResults = EntryExporter.ExportExportToPackage(sourceExport, targetPackage, out newEntry, cache);
                    }

                    if (relinkResults.Any())
                    {
                        Debugger.Break();
                    }
                }

                return(newEntry);
            }
            return(null); // No package was found
        }
Пример #9
0
        public static string GenerateUDKFileForLevel(string udkPath, IMEPackage pcc)
        {
            #region AssetPackage

            string meshPackageName = $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}Meshes";
            string meshFile        = Path.Combine(udkPath, @"UDKGame\Content\Shared\", $"{meshPackageName}.upk");
            MEPackageHandler.CreateAndSavePackage(meshFile, MEGame.UDK);
            using IMEPackage meshPackage = MEPackageHandler.OpenUDKPackage(meshFile);
            meshPackage.getEntryOrAddImport("Core.Package");

            IEntry defMat    = meshPackage.getEntryOrAddImport("EngineMaterials.DefaultMaterial", "Material", "Engine");
            var    allMats   = new HashSet <int>();
            var    relinkMap = new Dictionary <IEntry, IEntry>();
            #region StaticMeshes

            List <ExportEntry> staticMeshes = pcc.Exports.Where(exp => exp.ClassName == "StaticMesh").ToList();
            foreach (ExportEntry mesh in staticMeshes)
            {
                var        mats = new Queue <int>();
                StaticMesh stm  = ObjectBinary.From <StaticMesh>(mesh);
                foreach (StaticMeshRenderData lodModel in stm.LODModels)
                {
                    foreach (StaticMeshElement meshElement in lodModel.Elements)
                    {
                        mats.Enqueue(meshElement.Material);
                        allMats.Add(meshElement.Material);
                        meshElement.Material = 0;
                    }
                }
                if (pcc.GetEntry(stm.BodySetup) is ExportEntry rbBodySetup)
                {
                    rbBodySetup.RemoveProperty("PhysMaterial");
                }
                mesh.WriteBinary(stm);
                IEntry newParent = EntryImporter.GetOrAddCrossImportOrPackage(mesh.ParentFullPath, pcc, meshPackage);
                EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, mesh, meshPackage, newParent, false, out IEntry ent, relinkMap);
                ExportEntry portedMesh = (ExportEntry)ent;
                stm = ObjectBinary.From <StaticMesh>(portedMesh);
                foreach (StaticMeshRenderData lodModel in stm.LODModels)
                {
                    foreach (StaticMeshElement meshElement in lodModel.Elements)
                    {
                        meshElement.Material = mats.Dequeue();
                    }
                }
                portedMesh.WriteBinary(stm);
            }

            #endregion

            #region Materials
            using (IMEPackage udkResources = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetCustomAppResourceStream(MEGame.UDK)))
            {
                ExportEntry normDiffMat = udkResources.Exports.First(exp => exp.ObjectName == "NormDiffMat");
                foreach (int matUIndex in allMats)
                {
                    if (pcc.GetEntry(matUIndex) is ExportEntry matExp)
                    {
                        List <IEntry> textures = new MaterialInstanceConstant(matExp).Textures;
                        ExportEntry   diff     = null;
                        ExportEntry   norm     = null;
                        foreach (IEntry texEntry in textures)
                        {
                            if (texEntry is ExportEntry texport)
                            {
                                if (texport.ObjectName.Name.ToLower().Contains("diff"))
                                {
                                    diff = texport;
                                }
                                else if (texport.ObjectName.Name.ToLower().Contains("norm"))
                                {
                                    norm = texport;
                                }
                            }
                        }
                        if (diff == null)
                        {
                            relinkMap[matExp] = defMat;
                            continue;
                        }
                        else
                        {
                            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, diff, meshPackage, null, false, out IEntry ent);
                            diff = (ExportEntry)ent;
                            diff.RemoveProperty("TextureFileCacheName");
                            diff.RemoveProperty("TFCFileGuid");
                            diff.RemoveProperty("LODGroup");
                        }
                        if (norm != null)
                        {
                            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, norm, meshPackage, null, false, out IEntry ent);
                            norm = (ExportEntry)ent;
                            norm.RemoveProperty("TextureFileCacheName");
                            norm.RemoveProperty("TFCFileGuid");
                            norm.RemoveProperty("LODGroup");
                        }
                        EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, normDiffMat, meshPackage, null, true, out IEntry matEnt);
                        ExportEntry newMat = (ExportEntry)matEnt;
                        newMat.ObjectName = matExp.ObjectName;
                        Material matBin = ObjectBinary.From <Material>(newMat);
                        matBin.SM3MaterialResource.UniformExpressionTextures = new UIndex[] { norm?.UIndex ?? 0, diff.UIndex };
                        newMat.WriteBinary(matBin);
                        relinkMap[matExp] = newMat;
                        if (newMat.GetProperty <ArrayProperty <ObjectProperty> >("Expressions") is {} expressionsProp&& expressionsProp.Count >= 2)
                        {
                            ExportEntry diffExpression = meshPackage.GetUExport(expressionsProp[0].Value);
                            ExportEntry normExpression = meshPackage.GetUExport(expressionsProp[1].Value);
                            diffExpression.WriteProperty(new ObjectProperty(diff.UIndex, "Texture"));
                            normExpression.WriteProperty(new ObjectProperty(norm?.UIndex ?? 0, "Texture"));
                        }
                    }
                    else if (pcc.GetEntry(matUIndex) is ImportEntry matImp)
                    {
                        relinkMap[matImp] = defMat;
                    }
                }

                var relinkMapping = new OrderedMultiValueDictionary <IEntry, IEntry>(relinkMap);
                foreach (ExportEntry stmExport in staticMeshes)
                {
                    if (relinkMap.TryGetValue(stmExport, out IEntry destEnt) && destEnt is ExportEntry destExp)
                    {
                        Relinker.Relink(stmExport, destExp, relinkMapping);
                    }
                }
            }
            #endregion


            meshPackage.Save();

            #endregion


            var    staticMeshActors = new List <ExportEntry>();
            var    lightActors      = new List <ExportEntry>();
            string tempPackagePath  = Path.Combine(App.ExecFolder, $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk");
            File.Copy(Path.Combine(App.ExecFolder, "empty.udk"), tempPackagePath, true);
            using IMEPackage udkPackage = MEPackageHandler.OpenUDKPackage(tempPackagePath);
            {
                var topLevelMeshPackages = new List <IEntry>();
                foreach (ExportEntry exportEntry in staticMeshes)
                {
                    IEntry imp = udkPackage.getEntryOrAddImport($"{exportEntry.FullPath}", "StaticMesh", "Engine", exportEntry.ObjectName.Number);
                    while (imp.Parent != null)
                    {
                        imp = imp.Parent;
                    }
                    if (!topLevelMeshPackages.Contains(imp))
                    {
                        topLevelMeshPackages.Add(imp);
                    }
                }

                ExportEntry levelExport          = udkPackage.Exports.First(exp => exp.ClassName == "Level");
                List <int>  actorsInLevel        = ObjectBinary.From <Level>(pcc.Exports.First(exp => exp.ClassName == "Level")).Actors.Select(u => u.value).ToList();
                var         componentToMatrixMap = new Dictionary <int, Matrix>();
                foreach (int uIndex in actorsInLevel)
                {
                    if (pcc.GetEntry(uIndex) is ExportEntry stcExp)
                    {
                        if (stcExp.ClassName == "StaticMeshCollectionActor")
                        {
                            StaticMeshCollectionActor stmc = ObjectBinary.From <StaticMeshCollectionActor>(stcExp);
                            var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("StaticMeshComponents");
                            for (int i = 0; i < components.Count; i++)
                            {
                                componentToMatrixMap[components[i].Value] = stmc.LocalToWorldTransforms[i];
                            }
                        }
                        else if (stcExp.ClassName == "StaticLightCollectionActor")
                        {
                            StaticLightCollectionActor stlc = ObjectBinary.From <StaticLightCollectionActor>(stcExp);
                            var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("LightComponents");
                            for (int i = 0; i < components.Count; i++)
                            {
                                componentToMatrixMap[components[i].Value] = stlc.LocalToWorldTransforms[i];
                            }
                        }
                    }
                }

                #region StaticMeshActors
                {
                    var    emptySMCBin          = new StaticMeshComponent();
                    IEntry staticMeshActorClass = udkPackage.getEntryOrAddImport("Engine.StaticMeshActor");
                    udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor", "StaticMeshActor", "Engine");
                    IEntry staticMeshComponentArchetype = udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor.StaticMeshComponent0",
                                                                                         "StaticMeshComponent", "Engine");
                    int smaIndex = 2;
                    int smcIndex = 2;
                    foreach (ExportEntry smc in pcc.Exports.Where(exp => exp.ClassName == "StaticMeshComponent"))
                    {
                        if (smc.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex) && parent.IsA("StaticMeshActorBase"))
                        {
                            StructProperty locationProp;
                            StructProperty rotationProp;
                            StructProperty scaleProp = null;
                            smc.CondenseArchetypes();
                            if (!(smc.GetProperty <ObjectProperty>("StaticMesh") is { } meshProp) || !pcc.IsUExport(meshProp.Value))
                            {
                                continue;
                            }

                            smc.WriteBinary(emptySMCBin);
                            smc.RemoveProperty("bBioIsReceivingDecals");
                            smc.RemoveProperty("bBioForcePrecomputedShadows");
                            //smc.RemoveProperty("bUsePreComputedShadows");
                            smc.RemoveProperty("bAcceptsLights");
                            smc.RemoveProperty("IrrelevantLights");
                            smc.RemoveProperty("Materials"); //should make use of this?
                            smc.ObjectName = new NameReference("StaticMeshComponent", smcIndex++);
                            if (parent.ClassName == "StaticMeshCollectionActor")
                            {
                                if (!componentToMatrixMap.TryGetValue(smc.UIndex, out Matrix m))
                                {
                                    continue;
                                }
                                (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose();
                                locationProp = CommonStructs.Vector3Prop(posVec, "Location");
                                rotationProp = CommonStructs.RotatorProp(rotator, "Rotation");
                                scaleProp    = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D");
                                //smc.WriteProperty(CommonStructs.Matrix(m, "CachedParentToWorld"));
                            }
                            else
                            {
                                locationProp = parent.GetProperty <StructProperty>("Location");
                                rotationProp = parent.GetProperty <StructProperty>("Rotation");
                                scaleProp    = parent.GetProperty <StructProperty>("DrawScale3D");
                                if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale)
                                {
                                    Vector3 scaleVec = Vector3.One;
                                    if (scaleProp != null)
                                    {
                                        scaleVec = CommonStructs.GetVector3(scaleProp);
                                    }
                                    scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D");
                                }
                            }

                            ExportEntry sma = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, staticMeshActorClass.UIndex))
                            {
                                ObjectName = new NameReference("StaticMeshActor", smaIndex++),
                                Class      = staticMeshActorClass,
                                Parent     = levelExport
                            };
                            sma.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack;
                            udkPackage.AddExport(sma);

                            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, smc, udkPackage,
                                                                 sma, true, out IEntry result);
                            var props = new PropertyCollection
                            {
                                new ObjectProperty(result.UIndex, "StaticMeshComponent"),
                                new NameProperty(new NameReference(Path.GetFileNameWithoutExtension(smc.FileRef.FilePath), smc.UIndex),
                                                 "Tag"),
                                new ObjectProperty(result.UIndex, "CollisionComponent")
                            };
                            if (locationProp != null)
                            {
                                props.Add(locationProp);
                            }
                            if (rotationProp != null)
                            {
                                props.Add(rotationProp);
                            }

                            if (scaleProp != null)
                            {
                                props.Add(scaleProp);
                            }
                            sma.WriteProperties(props);
                            staticMeshActors.Add(sma);
                        }
                    }
                    IEntry topMeshPackageImport = udkPackage.getEntryOrAddImport(meshPackageName, "Package");
                    foreach (IEntry mp in topLevelMeshPackages)
                    {
                        mp.Parent = topMeshPackageImport;
                    }
                }
                #endregion

                #region LightActors
                {
                    IEntry pointLightClass       = udkPackage.getEntryOrAddImport("Engine.PointLight");
                    IEntry spotLightClass        = udkPackage.getEntryOrAddImport("Engine.SpotLight");
                    IEntry directionalLightClass = udkPackage.getEntryOrAddImport("Engine.DirectionalLight");

                    int plaIndex = 1;
                    int plcIndex = 1;
                    int slaIndex = 1;
                    int slcIndex = 1;
                    int dlaIndex = 1;
                    int dlcIndex = 1;
                    foreach (ExportEntry lightComponent in pcc.Exports)
                    {
                        if (!(lightComponent.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex)))
                        {
                            continue;
                        }
                        StructProperty locationProp;
                        StructProperty rotationProp;
                        StructProperty scaleProp;
                        switch (lightComponent.ClassName)
                        {
                        case "PointLightComponent":
                            lightComponent.CondenseArchetypes();
                            lightComponent.ObjectName = new NameReference("PointLightComponent", plcIndex++);
                            if (parent.ClassName == "StaticLightCollectionActor")
                            {
                                if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m))
                                {
                                    continue;
                                }
                                (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose();
                                locationProp = CommonStructs.Vector3Prop(posVec, "Location");
                                rotationProp = CommonStructs.RotatorProp(rotator, "Rotation");
                                scaleProp    = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D");
                            }
                            else
                            {
                                locationProp = parent.GetProperty <StructProperty>("Location");
                                rotationProp = parent.GetProperty <StructProperty>("Rotation");
                                scaleProp    = parent.GetProperty <StructProperty>("DrawScale3D");
                                if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale)
                                {
                                    Vector3 scaleVec = Vector3.One;
                                    if (scaleProp != null)
                                    {
                                        scaleVec = CommonStructs.GetVector3(scaleProp);
                                    }
                                    scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D");
                                }
                            }

                            ExportEntry pla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, pointLightClass.UIndex))
                            {
                                ObjectName = new NameReference("PointLight", plaIndex++),
                                Class      = pointLightClass,
                                Parent     = levelExport
                            };
                            pla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack;
                            udkPackage.AddExport(pla);

                            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, pla, true,
                                                                 out IEntry portedPLC);
                            var plsProps = new PropertyCollection
                            {
                                new ObjectProperty(portedPLC.UIndex, "LightComponent"),
                                new NameProperty("PointLight", "Tag"),
                            };
                            if (locationProp != null)
                            {
                                plsProps.Add(locationProp);
                            }
                            if (rotationProp != null)
                            {
                                plsProps.Add(rotationProp);
                            }
                            if (scaleProp != null)
                            {
                                plsProps.Add(scaleProp);
                            }
                            pla.WriteProperties(plsProps);
                            lightActors.Add(pla);
                            break;

                        case "SpotLightComponent":
                            lightComponent.CondenseArchetypes();
                            lightComponent.ObjectName = new NameReference("SpotLightComponent", slcIndex++);
                            if (parent.ClassName == "StaticLightCollectionActor")
                            {
                                if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m))
                                {
                                    continue;
                                }
                                (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose();
                                locationProp = CommonStructs.Vector3Prop(posVec, "Location");
                                rotationProp = CommonStructs.RotatorProp(rotator, "Rotation");
                                scaleProp    = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D");
                            }
                            else
                            {
                                locationProp = parent.GetProperty <StructProperty>("Location");
                                rotationProp = parent.GetProperty <StructProperty>("Rotation");
                                scaleProp    = parent.GetProperty <StructProperty>("DrawScale3D");
                                if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale)
                                {
                                    Vector3 scaleVec = Vector3.One;
                                    if (scaleProp != null)
                                    {
                                        scaleVec = CommonStructs.GetVector3(scaleProp);
                                    }
                                    scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D");
                                }
                            }

                            ExportEntry sla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, spotLightClass.UIndex))
                            {
                                ObjectName = new NameReference("SpotLight", slaIndex++),
                                Class      = spotLightClass,
                                Parent     = levelExport
                            };
                            sla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack;
                            udkPackage.AddExport(sla);

                            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, sla, true,
                                                                 out IEntry portedSLC);
                            var slaProps = new PropertyCollection
                            {
                                new ObjectProperty(portedSLC.UIndex, "LightComponent"),
                                new NameProperty("SpotLight", "Tag"),
                            };
                            if (locationProp != null)
                            {
                                slaProps.Add(locationProp);
                            }
                            if (rotationProp != null)
                            {
                                slaProps.Add(rotationProp);
                            }
                            if (scaleProp != null)
                            {
                                slaProps.Add(scaleProp);
                            }
                            sla.WriteProperties(slaProps);
                            lightActors.Add(sla);
                            break;

                        case "DirectionalLightComponent":
                            lightComponent.CondenseArchetypes();
                            lightComponent.ObjectName = new NameReference("DirectionalLightComponent", dlcIndex++);
                            if (parent.ClassName == "StaticLightCollectionActor")
                            {
                                if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m))
                                {
                                    continue;
                                }
                                (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose();
                                locationProp = CommonStructs.Vector3Prop(posVec, "Location");
                                rotationProp = CommonStructs.RotatorProp(rotator, "Rotation");
                                scaleProp    = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D");
                            }
                            else
                            {
                                locationProp = parent.GetProperty <StructProperty>("Location");
                                rotationProp = parent.GetProperty <StructProperty>("Rotation");
                                scaleProp    = parent.GetProperty <StructProperty>("DrawScale3D");
                                if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale)
                                {
                                    Vector3 scaleVec = Vector3.One;
                                    if (scaleProp != null)
                                    {
                                        scaleVec = CommonStructs.GetVector3(scaleProp);
                                    }
                                    scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D");
                                }
                            }

                            ExportEntry dla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, directionalLightClass.UIndex))
                            {
                                ObjectName = new NameReference("DirectionalLight", dlaIndex++),
                                Class      = directionalLightClass,
                                Parent     = levelExport
                            };
                            dla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack;
                            udkPackage.AddExport(dla);

                            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, dla, true,
                                                                 out IEntry portedDLC);
                            var dlaProps = new PropertyCollection
                            {
                                new ObjectProperty(portedDLC.UIndex, "LightComponent"),
                                new NameProperty("DirectionalLight", "Tag"),
                            };
                            if (locationProp != null)
                            {
                                dlaProps.Add(locationProp);
                            }
                            if (rotationProp != null)
                            {
                                dlaProps.Add(rotationProp);
                            }
                            if (scaleProp != null)
                            {
                                dlaProps.Add(scaleProp);
                            }
                            dla.WriteProperties(dlaProps);
                            lightActors.Add(dla);
                            break;
                        }
                    }
                }
                UDKifyLights(udkPackage);
                #endregion

                Level level = ObjectBinary.From <Level>(levelExport);
                level.Actors = levelExport.GetChildren().Where(ent => ent.IsA("Actor")).Select(ent => new UIndex(ent.UIndex)).ToList();
                levelExport.WriteBinary(level);

                udkPackage.Save();
            }

            string resultFilePath = Path.Combine(udkPath, @"UDKGame\Content\Maps\", $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk");
            using (IMEPackage udkPackage2 = MEPackageHandler.OpenUDKPackage(Path.Combine(App.ExecFolder, "empty.udk")))
            {
                ExportEntry levelExport = udkPackage2.Exports.First(exp => exp.ClassName == "Level");
                Level       levelBin    = ObjectBinary.From <Level>(levelExport);
                foreach (ExportEntry actor in staticMeshActors)
                {
                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result);
                    levelBin.Actors.Add(result.UIndex);
                }
                foreach (ExportEntry actor in lightActors)
                {
                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result);
                    levelBin.Actors.Add(result.UIndex);
                }
                levelExport.WriteBinary(levelBin);
                udkPackage2.Save(resultFilePath);
            }
            File.Delete(tempPackagePath);
            return(resultFilePath);
        }
Пример #10
0
        public void CreateLiveEditFile()
        {
            string filePath = LiveFaceFxEditorFilePath;

            File.Copy(Path.Combine(App.ExecFolder, "ME3EmptyLevel.pcc"), filePath);
            LiveFile = MEPackageHandler.OpenMEPackage(filePath);
            for (int i = 0; i < LiveFile.Names.Count; i++)
            {
                if (LiveFile.Names[i].Equals("ME3EmptyLevel"))
                {
                    LiveFile.replaceName(i, Path.GetFileNameWithoutExtension(filePath));
                }
            }

            var packguid = Guid.NewGuid();
            var package  = LiveFile.GetUExport(1);

            package.PackageGUID  = packguid;
            LiveFile.PackageGuid = packguid;

            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, SourceAnimSet.Export.Parent, LiveFile, null, true, out _);

            var gender = SourceAnimSet.Export.ObjectNameString.Last() switch
            {
                'F' => FaceFXGender.Female,
                'M' => FaceFXGender.Male,
                _ => FaceFXGender.NonSpkr
            };

            ActorTag = null;
            try
            {
                if (gender is not FaceFXGender.NonSpkr)
                {
                    bool isFemale        = gender is FaceFXGender.Female;
                    var  bioConv         = LiveFile.Exports.First(exp => exp.Class.ObjectName == "BioConversation");
                    var  LiveFileAnimSet = LiveFile.FindExport(SourceAnimSet.Export.InstancedFullPath);
                    var  propName        = isFemale ? "m_aMaleFaceSets" : "m_aFemaleFaceSets";
                    int  idx             = bioConv.GetProperty <ArrayProperty <ObjectProperty> >(propName).FindIndex(objProp => objProp.Value == LiveFileAnimSet.UIndex);
                    if (idx is 0)
                    {
                        //player
                        ActorTag = $"Player_{(isFemale ? 'F' : 'M')}";
                        IEntry ent;
                        using (IMEPackage soldierFile = MEPackageHandler.OpenME3Package(Path.Combine(ME3Directory.CookedPCPath, "SFXCharacterClass_Soldier.pcc")))
                        {
                            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, soldierFile.GetUExport(isFemale ? 10327 : 10330),
                                                                 LiveFile, LiveFile.GetUExport(3), true, out ent);
                        }
                        ExportEntry actor = (ExportEntry)ent;
                        LiveFile.AddToLevelActorsIfNotThere(actor);
                        actor.WriteProperty(new NameProperty(ActorTag, "Tag"));

                        using (IMEPackage clothingFile = MEPackageHandler.OpenME3Package(Path.Combine(ME3Directory.CookedPCPath, $"BIOG_HM{(isFemale ? "F" : "M")}_ARM_CTH_R.pcc")))
                        {
                            var clothingPackage = EntryImporter.GetOrAddCrossImportOrPackage("CTHa", clothingFile, LiveFile);
                            EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, clothingFile.GetUExport(isFemale ? 1625 : 1966),
                                                                 LiveFile, clothingPackage, true, out ent);
                        }

                        ExportEntry bodyComponent = LiveFile.GetUExport(actor.GetProperty <ObjectProperty>("BodyMesh").Value);
                        bodyComponent.WriteProperty(new ObjectProperty(ent.UIndex, "SkeletalMesh"));
                    }
                    else if (idx is 1)
                    {
                        //owner
                        using IMEPackage parentFile = getParentFile();
                        foreach (ExportEntry export in parentFile.Exports.Where(exp => exp.ClassName is "SFXSeqAct_StartConversation" or "SFXSeqAct_StartAmbientConv"))
                        {
                            if (export.GetProperty <ObjectProperty>("Conv") is ObjectProperty convProp && parentFile.TryGetImport(convProp.Value, out var convImport) &&
                                convImport.ObjectName == bioConv.ObjectName)
                            {
                                ExportEntry seqVar = parentFile.GetUExport(export.GetProperty <ArrayProperty <StructProperty> >("VariableLinks")[0].GetProp <ArrayProperty <ObjectProperty> >("LinkedVariables")[0].Value);
                                if (seqVar.ClassName == "BioSeqVar_ObjectFindByTag")
                                {
                                    ActorTag = seqVar.GetProperty <NameProperty>("m_sObjectTagToFind").Value;
                                    if (!ActorTag.StartsWith("hench_", StringComparison.OrdinalIgnoreCase))
                                    {
                                        importTaggedActor(parentFile);
                                    }
                                }
                                else
                                {
                                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies,
                                                                         parentFile.GetUExport(seqVar.GetProperty <ObjectProperty>("ObjValue").Value),
                                                                         LiveFile, LiveFile.GetUExport(3), true, out var ent);
                                    ExportEntry actor = (ExportEntry)ent;
                                    LiveFile.AddToLevelActorsIfNotThere(actor);
                                    ActorTag = actor.GetProperty <NameProperty>("Tag")?.Value.Name;
                                    if (ActorTag is null)
                                    {
                                        ActorTag = "ConvoOwner";
                                        actor.WriteProperty(new NameProperty(ActorTag, "Tag"));
                                    }
                                }
                                break;
                            }
                        }
                    }
                    else
                    {
                        ActorTag = bioConv.GetProperty <ArrayProperty <NameProperty> >("m_aSpeakerList")[idx - 2].Value;
                        if (!ActorTag.StartsWith("hench_", StringComparison.OrdinalIgnoreCase))
                        {
                            using IMEPackage parentFile = getParentFile();
                            importTaggedActor(parentFile);
                        }
                    }
                }
                else
                {
                    //find nonspkr linkage somehow
                }
            }
        public static IEntry PortWeaponIntoPackage(IMEPackage targetPackage, GunInfo gunInfo)
        {
            IMEPackage sourcePackage;

            if (gunInfo.IsCorrectedPackage)
            {
                var sourceData = MERUtilities.GetEmbeddedStaticFilesBinaryFile("correctedloadouts.weapons." + gunInfo.PackageFileName);
                sourcePackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(sourceData));

                if (gunInfo.ImportOnly)
                {
                    // We need to install this file
                    var outfname = Path.Combine(MERFileSystem.DLCModCookedPath, gunInfo.PackageFileName);
                    if (!File.Exists(outfname))
                    {
                        sourcePackage.Save(outfname, true);
                        ThreadSafeDLCStartupPackage.AddStartupPackage(Path.GetFileNameWithoutExtension(gunInfo.PackageFileName));
                    }
                }
            }
            else
            {
                sourcePackage = NonSharedPackageCache.Cache.GetCachedPackage(gunInfo.PackageFileName);
            }

            if (sourcePackage != null)
            {
                var sourceExport = sourcePackage.GetUExport(gunInfo.SourceUIndex);

                if (!sourceExport.InheritsFrom("SFXWeapon") || sourceExport.IsDefaultObject)
                {
                    throw new Exception("Wrong setup!");
                }

                if (sourceExport.Parent != null && sourceExport.Parent.ClassName != "Package")
                {
                    throw new Exception("Cannot port weapon - parent object is not Package!");
                }

                // 1. Setup the link that will be used.
                //var newParent = EntryExporter.PortParents(sourceExport, targetPackage);
                var newParent = EntryExporter.PortParents(sourceExport, targetPackage, gunInfo.ImportOnly);

                void errorOccuredCB(string s)
                {
                    Debugger.Break();
                }

                IEntry newEntry = null;
                if (gunInfo.ImportOnly)
                {
                    Debug.WriteLine($"Gun ImportOnly in file {targetPackage.FilePath}");
                    if (gunInfo.RequiresStartupPackage)
                    {
                        ThreadSafeDLCStartupPackage.AddStartupPackage(Path.GetFileNameWithoutExtension(gunInfo.PackageFileName));
                    }

                    newEntry = PackageTools.CreateImportForClass(sourceExport, targetPackage, newParent);
                }
                else
                {
                    List <EntryStringPair> relinkResults = null;
                    if (gunInfo.IsCorrectedPackage || (PackageTools.IsPersistentPackage(gunInfo.PackageFileName) && MERFileSystem.GetPackageFile(gunInfo.PackageFileName.ToLocalizedFilename()) == null))
                    {
                        // Faster this way, without having to check imports
                        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
                        relinkResults = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceExport, targetPackage,
                                                                             newParent, true, out newEntry, crossPCCObjectMap);
                    }
                    else
                    {
                        // MEMORY SAFE (resolve imports to exports)
                        MERPackageCache cache = new MERPackageCache();
                        relinkResults = EntryExporter.ExportExportToPackage(sourceExport, targetPackage, out newEntry, cache);
                    }

                    if (relinkResults.Any())
                    {
                        Debugger.Break();
                    }
                }

                return(newEntry);
            }
            else
            {
                Debug.WriteLine($"Package for gun porting not found: {gunInfo.PackageFileName}");
            }
            return(null); // No package was found
        }
Пример #12
0
        public static void RunGame2EmailMerge(GameTarget target)
        {
            M3MergeDLC.RemoveMergeDLC(target);
            var loadedFiles = MELoadedFiles.GetFilesLoadedInGame(target.Game, gameRootOverride: target.TargetPath);

            // File to base modifications on
            using IMEPackage pcc = MEPackageHandler.OpenMEPackage(loadedFiles[@"BioD_Nor103_Messages.pcc"]);

            // Path to Message templates file - different files for ME2/LE2
            string ResourcesFilePath = $@"MassEffectModManagerCore.modmanager.emailmerge.{target.Game}.103Message_Template_{target.Game}";

            using IMEPackage resources = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream(ResourcesFilePath));

            // Startup file to place conditionals and transitions into
            using IMEPackage startup = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream(
                                                                                    $@"MassEffectModManagerCore.modmanager.mergedlc.{target.Game}.Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc"));


            var emailInfos        = new List <ME2EmailMergeFile>();
            var jsonSupercedances = M3Directories.GetFileSupercedances(target, new[] { @".json" });

            if (jsonSupercedances.TryGetValue(EMAIL_MERGE_MANIFEST_FILE, out var jsonList))
            {
                jsonList.Reverse();
                foreach (var dlc in jsonList)
                {
                    var jsonFile = Path.Combine(M3Directories.GetDLCPath(target), dlc, target.Game.CookedDirName(),
                                                EMAIL_MERGE_MANIFEST_FILE);
                    emailInfos.Add(JsonConvert.DeserializeObject <ME2EmailMergeFile>(File.ReadAllText(jsonFile)));
                }
            }

            // Sanity checks
            if (!emailInfos.Any() || !emailInfos.SelectMany(e => e.Emails).Any())
            {
                return;
            }

            if (emailInfos.Any(e => e.Game != target.Game))
            {
                throw new Exception("ME2 email merge manifest targets incorrect game");
            }

            // Startup File
            // Could replace this with full instanced path in M3 implementation
            ExportEntry stateEventMapExport = startup.Exports
                                              .First(e => e.ClassName == "BioStateEventMap" && e.ObjectName == "StateTransitionMap");
            BioStateEventMap StateEventMap    = stateEventMapExport.GetBinaryData <BioStateEventMap>();
            ExportEntry      ConditionalClass =
                startup.FindExport($@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}.BioAutoConditionals");

            #region Sequence Exports
            // Send message - All email conditionals are checked and emails transitions are triggered
            ExportEntry SendMessageContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_Messages");
            ExportEntry LastSendMessage      = KismetHelper.GetSequenceObjects(SendMessageContainer).OfType <ExportEntry>()
                                               .FirstOrDefault(e =>
            {
                var outbound = KismetHelper.GetOutboundLinksOfNode(e);
                return(outbound.Count == 1 && outbound[0].Count == 0);
            });
            ExportEntry TemplateSendMessage          = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_MessageTemplate");
            ExportEntry TemplateSendMessageBoolCheck = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_MessageTemplate_BoolCheck");


            // Mark Read - email ints are set to read
            // This is the only section that does not gracefully handle different DLC installations - DLC_CER is required atm
            ExportEntry MarkReadContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read");
            ExportEntry LastMarkRead      = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read.DLC_CER");
            ExportEntry MarkReadOutLink   = KismetHelper.GetOutboundLinksOfNode(LastMarkRead)[0][0].LinkedOp as ExportEntry;
            KismetHelper.RemoveOutputLinks(LastMarkRead);

            ExportEntry TemplateMarkRead           = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_ReadTemplate");
            ExportEntry TemplateMarkReadTransition = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read_Transition");


            // Display Messages - Str refs are passed through to GUI
            ExportEntry DisplayMessageContainer =
                pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_Messages");
            ExportEntry DisplayMessageOutLink =
                pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_Messages.SeqCond_CompareBool_0");

            ExportEntry LastDisplayMessage = SeqTools.FindOutboundConnectionsToNode(DisplayMessageOutLink, KismetHelper.GetSequenceObjects(DisplayMessageContainer).OfType <ExportEntry>())[0];
            KismetHelper.RemoveOutputLinks(LastDisplayMessage);
            var         DisplayMessageVariableLinks = LastDisplayMessage.GetProperty <ArrayProperty <StructProperty> >("VariableLinks");
            ExportEntry TemplateDisplayMessage      =
                resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_MessageTemplate");

            // Archive Messages - Message ints are set to 3
            ExportEntry ArchiveContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Archive_Message");
            ExportEntry ArchiveSwitch    = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Archive_Message.SeqAct_Switch_0");
            ExportEntry ArchiveOutLink   =
                pcc.FindExport(
                    @"TheWorld.PersistentLevel.Main_Sequence.Archive_Message.BioSeqAct_PMCheckConditional_1");
            ExportEntry ExampleSetInt  = KismetHelper.GetOutboundLinksOfNode(ArchiveSwitch)[0][0].LinkedOp as ExportEntry;
            ExportEntry ExamplePlotInt = SeqTools.GetVariableLinksOfNode(ExampleSetInt)[0].LinkedNodes[0] as ExportEntry;
            #endregion

            int messageID      = KismetHelper.GetOutboundLinksOfNode(ArchiveSwitch).Count + 1;
            int currentSwCount = ArchiveSwitch.GetProperty <IntProperty>("LinkCount").Value;

            foreach (var emailMod in emailInfos)
            {
                string modName = "DLC_MOD_" + emailMod.ModName;

                foreach (var email in emailMod.Emails)
                {
                    string emailName = modName + "_" + email.EmailName;

                    // Create send transition
                    int transitionId  = WriteTransition(StateEventMap, email.StatusPlotInt);
                    int conditionalId = WriteConditional(email.TriggerConditional);

                    #region SendMessage
                    //////////////
                    // SendMessage
                    //////////////

                    // Create seq object
                    var SMTemp = emailMod.InMemoryBool.HasValue ? TemplateSendMessageBoolCheck : TemplateSendMessage;
                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild,
                                                         SMTemp, pcc, SendMessageContainer, true, new RelinkerOptionsPackage(), out var outSendEntry);

                    var newSend = outSendEntry as ExportEntry;

                    // Set name, comment, add to sequence
                    newSend.ObjectName = new NameReference(emailName);
                    KismetHelper.AddObjectToSequence(newSend, SendMessageContainer);
                    KismetHelper.SetComment(newSend, emailName);
                    if (target.Game == MEGame.ME2)
                    {
                        newSend.WriteProperty(new StrProperty(emailName, "ObjName"));
                    }

                    // Set Trigger Conditional
                    var pmCheckConditionalSM = newSend.GetChildren()
                                               .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMCheckConditional" && e is ExportEntry);
                    if (pmCheckConditionalSM is ExportEntry conditional)
                    {
                        conditional.WriteProperty(new IntProperty(conditionalId, "m_nIndex"));
                        KismetHelper.SetComment(conditional, "Time for " + email.EmailName + "?");
                    }

                    // Set Send Transition
                    var pmExecuteTransitionSM = newSend.GetChildren()
                                                .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMExecuteTransition" && e is ExportEntry);
                    if (pmExecuteTransitionSM is ExportEntry transition)
                    {
                        transition.WriteProperty(new IntProperty(transitionId, "m_nIndex"));
                        KismetHelper.SetComment(transition, "Send " + email.EmailName + " message.");
                    }

                    // Set Send Transition
                    if (emailMod.InMemoryBool.HasValue)
                    {
                        var pmCheckStateSM = newSend.GetChildren()
                                             .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMCheckState" && e is ExportEntry);
                        if (pmCheckStateSM is ExportEntry checkState)
                        {
                            checkState.WriteProperty(new IntProperty(emailMod.InMemoryBool.Value, "m_nIndex"));
                            KismetHelper.SetComment(checkState, "Is " + emailMod.ModName + " installed?");
                        }
                    }

                    // Hook up output links
                    KismetHelper.CreateOutputLink(LastSendMessage, "Out", newSend);
                    LastSendMessage = newSend;
                    #endregion

                    #region MarkRead
                    ///////////
                    // MarkRead
                    ///////////

                    // Create seq object
                    var MRTemp = email.ReadTransition.HasValue ? TemplateMarkReadTransition : TemplateMarkRead;
                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild,
                                                         MRTemp, pcc, MarkReadContainer, true, new RelinkerOptionsPackage(), out var outMarkReadEntry);
                    var newMarkRead = outMarkReadEntry as ExportEntry;

                    // Set name, comment, add to sequence
                    newMarkRead.ObjectName = new NameReference(emailName);
                    KismetHelper.AddObjectToSequence(newMarkRead, MarkReadContainer);
                    KismetHelper.SetComment(newMarkRead, emailName);
                    if (target.Game == MEGame.ME2)
                    {
                        newMarkRead.WriteProperty(new StrProperty(emailName, "ObjName"));
                    }

                    // Set Plot Int
                    var storyManagerIntMR = newMarkRead.GetChildren()
                                            .FirstOrDefault(e => e.ClassName == "BioSeqVar_StoryManagerInt" && e is ExportEntry);
                    if (storyManagerIntMR is ExportEntry plotIntMR)
                    {
                        plotIntMR.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex"));
                        KismetHelper.SetComment(plotIntMR, email.EmailName);
                    }

                    if (email.ReadTransition.HasValue)
                    {
                        var pmExecuteTransitionMR = newMarkRead.GetChildren()
                                                    .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMExecuteTransition" && e is ExportEntry);
                        if (pmExecuteTransitionMR is ExportEntry transitionMR)
                        {
                            transitionMR.WriteProperty(new IntProperty(email.ReadTransition.Value, "m_nIndex"));
                            KismetHelper.SetComment(transitionMR, "Trigger " + email.EmailName + " read transition");
                        }
                    }

                    // Hook up output links
                    KismetHelper.CreateOutputLink(LastMarkRead, "Out", newMarkRead);
                    LastMarkRead = newMarkRead;
                    #endregion

                    #region DisplayEmail
                    ////////////////
                    // Display Email
                    ////////////////

                    // Create seq object
                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild,
                                                         TemplateDisplayMessage, pcc, DisplayMessageContainer, true, new RelinkerOptionsPackage(), out var outDisplayMessage);
                    var newDisplayMessage = outDisplayMessage as ExportEntry;

                    // Set name, comment, variable links, add to sequence
                    newDisplayMessage.ObjectName = new NameReference(emailName);
                    KismetHelper.AddObjectToSequence(newDisplayMessage, DisplayMessageContainer);
                    newDisplayMessage.WriteProperty(DisplayMessageVariableLinks);
                    KismetHelper.SetComment(newDisplayMessage, emailName);
                    if (target.Game == MEGame.ME2)
                    {
                        newDisplayMessage.WriteProperty(new StrProperty(emailName, "ObjName"));
                    }

                    var displayChildren = newDisplayMessage.GetChildren();

                    // Set Plot Int
                    var storyManagerIntDE = displayChildren.FirstOrDefault(e =>
                                                                           e.ClassName == "BioSeqVar_StoryManagerInt" && e is ExportEntry);
                    if (storyManagerIntDE is ExportEntry plotIntDE)
                    {
                        plotIntDE.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex"));
                    }

                    // Set Email ID
                    var emailIdDE = displayChildren.FirstOrDefault(e =>
                                                                   e.ClassName == "SeqVar_Int" && e is ExportEntry);
                    if (emailIdDE is ExportEntry EmailIDDE)
                    {
                        EmailIDDE.WriteProperty(new IntProperty(messageID, "IntValue"));
                    }

                    // Set Title StrRef
                    var titleStrRef = displayChildren.FirstOrDefault(e =>
                                                                     e.ClassName == "BioSeqVar_StrRef" && e is ExportEntry ee && ee.GetProperty <NameProperty>("VarName").Value == "Title StrRef");
                    if (titleStrRef is ExportEntry Title)
                    {
                        Title.WriteProperty(new StringRefProperty(email.TitleStrRef, "m_srValue"));
                    }

                    // Set Description StrRef
                    var descStrRef = displayChildren.FirstOrDefault(e =>
                                                                    e.ClassName == "BioSeqVar_StrRef" && e is ExportEntry ee && ee.GetProperty <NameProperty>("VarName").Value == "Desc StrRef");
                    if (descStrRef is ExportEntry Desc)
                    {
                        Desc.WriteProperty(new StringRefProperty(email.DescStrRef, "m_srValue"));
                    }

                    // Hook up output links
                    KismetHelper.CreateOutputLink(LastDisplayMessage, "Out", newDisplayMessage);
                    LastDisplayMessage = newDisplayMessage;
                    #endregion

                    #region ArchiveEmail
                    ////////////////
                    // Archive Email
                    ////////////////

                    var NewSetInt = EntryCloner.CloneEntry(ExampleSetInt);
                    KismetHelper.AddObjectToSequence(NewSetInt, ArchiveContainer);
                    KismetHelper.CreateOutputLink(NewSetInt, "Out", ArchiveOutLink);

                    KismetHelper.CreateNewOutputLink(ArchiveSwitch, "Link " + (messageID - 1), NewSetInt);

                    var NewPlotInt = EntryCloner.CloneEntry(ExamplePlotInt);
                    KismetHelper.AddObjectToSequence(NewPlotInt, ArchiveContainer);
                    NewPlotInt.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex"));
                    NewPlotInt.WriteProperty(new StrProperty(emailName, "m_sRefName"));

                    var linkedVars = SeqTools.GetVariableLinksOfNode(NewSetInt);
                    linkedVars[0].LinkedNodes = new List <IEntry>()
                    {
                        NewPlotInt
                    };
                    SeqTools.WriteVariableLinksToNode(NewSetInt, linkedVars);

                    messageID++;
                    currentSwCount++;
                    #endregion
                }
            }
            KismetHelper.CreateOutputLink(LastMarkRead, "Out", MarkReadOutLink);
            KismetHelper.CreateOutputLink(LastDisplayMessage, "Out", DisplayMessageOutLink);
            ArchiveSwitch.WriteProperty(new IntProperty(currentSwCount, "LinkCount"));

            stateEventMapExport.WriteBinary(StateEventMap);

            // Save Messages file into DLC
            var cookedDir   = Path.Combine(M3Directories.GetDLCPath(target), M3MergeDLC.MERGE_DLC_FOLDERNAME, target.Game.CookedDirName());
            var outMessages = Path.Combine(cookedDir, @"BioD_Nor103_Messages.pcc");
            pcc.Save(outMessages);

            // Save Startup file into DLC
            var startupF = Path.Combine(cookedDir, $@"Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc");
            startup.Save(startupF);
        }
        /// <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);
        }