public static PropertyCollection getDefaultStructValue(string className, bool stripTransients)
        {
            bool isImmutable = UnrealObjectInfo.IsImmutable(className, MEGame.ME1);

            if (Structs.ContainsKey(className))
            {
                ClassInfo info = Structs[className];
                try
                {
                    PropertyCollection structProps = new PropertyCollection();
                    ClassInfo          tempInfo    = info;
                    while (tempInfo != null)
                    {
                        foreach ((string propName, PropertyInfo propInfo) in tempInfo.properties)
                        {
                            if (stripTransients && propInfo.Transient)
                            {
                                continue;
                            }
                            if (getDefaultProperty(propName, propInfo, stripTransients, isImmutable) is UProperty uProp)
                            {
                                structProps.Add(uProp);
                            }
                        }
                        if (!Structs.TryGetValue(tempInfo.baseClass, out tempInfo))
                        {
                            tempInfo = null;
                        }
                    }
                    structProps.Add(new NoneProperty());

                    string filepath = Path.Combine(ME1Directory.BioGamePath, info.pccPath);
                    if (File.Exists(info.pccPath))
                    {
                        filepath = info.pccPath; //Used for dynamic lookup
                    }
                    else if (info.pccPath == UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName)
                    {
                        filepath = App.CustomResourceFilePath(MEGame.ME3);
                    }
                    if (!File.Exists(filepath))
                    {
                        filepath = Path.Combine(ME1Directory.gamePath, info.pccPath); //for files from ME1 DLC
                    }
                    if (File.Exists(filepath))
                    {
                        using (IMEPackage importPCC = MEPackageHandler.OpenME1Package(filepath))
                        {
                            var                exportToRead = importPCC.GetUExport(info.exportIndex);
                            byte[]             buff         = exportToRead.Data.Skip(0x30).ToArray();
                            PropertyCollection defaults     = PropertyCollection.ReadProps(exportToRead, new MemoryStream(buff), className);
                            foreach (var prop in defaults)
                            {
                                structProps.TryReplaceProp(prop);
                            }
                        }
                    }
                    return(structProps);
                }
                catch
                {
                    return(null);
                }
            }
            return(null);
        }
Example #2
0
        private Mod GenerateCompatibilityPackForFiles(List <string> dlcModList, List <string> filesToBePatched, SevenZipExtractor uiArchive)
        {
            dlcModList = dlcModList.Select(x =>
            {
                var tpmi = ThirdPartyServices.GetThirdPartyModInfo(x, MEGame.ME3);
                if (tpmi == null)
                {
                    return(x);
                }
                return(tpmi.modname);
            }).ToList();
            string dlcs = string.Join("\n - ", dlcModList); // do not localize

            StarterKitOptions sko = new StarterKitOptions
            {
                ModName          = M3L.GetString(M3L.string_guiCompatibilityPack),
                ModDescription   = M3L.GetString(M3L.string_interp_compatPackModDescription, dlcs, DateTime.Now.ToString()),
                ModDeveloper     = App.AppVersionHR,
                ModDLCFolderName = UI_MOD_NAME,
                ModGame          = MEGame.ME3,
                ModInternalName  = @"UI Mod Compatibility Pack",
                ModInternalTLKID = 1420400890,
                ModMountFlag     = EMountFileFlag.ME3_SPOnly_NoSaveFileDependency,
                ModMountPriority = 31050,
                ModURL           = null,
                ModModuleNumber  = 0
            };

            Mod generatedMod = null;

            void modGenerated(Mod mod)
            {
                generatedMod = mod;
                lock (modGeneratedSignaler)
                {
                    Monitor.Pulse(modGeneratedSignaler);
                }
            }

            void skUicallback(string text)
            {
                ActionSubstring = text;
            }

            StarterKitGeneratorWindow.CreateStarterKitMod(sko, skUicallback, modGenerated);
            lock (modGeneratedSignaler)
            {
                Monitor.Wait(modGeneratedSignaler);
            }

            //Mod has been generated.

            string outputPath = Path.Combine(generatedMod.ModPath, @"DLC_MOD_" + UI_MOD_NAME, @"CookedPCConsole");

            ActionString    = M3L.GetString(M3L.string_preparingUiLibrary);
            ActionSubstring = M3L.GetString(M3L.string_decompressingData);

            int done = 0;

            CaseInsensitiveDictionary <byte[]> uiLibraryData = new CaseInsensitiveDictionary <byte[]>();
            var filesToDecompress = uiArchive.ArchiveFileData.Where(x => x.FileName.EndsWith(@".swf")).ToList();

            foreach (var f in filesToDecompress)
            {
                MemoryStream decompressedStream = new MemoryStream();
                uiArchive.ExtractFile(f.Index, decompressedStream);
                string fname = f.FileName.Substring(f.FileName.IndexOf('\\') + 1);
                fname = fname.Substring(0, fname.IndexOf(@".swf", StringComparison.InvariantCultureIgnoreCase));

                uiLibraryData[fname] = decompressedStream.ToArray();
                done++;
                Percent = getPercent(done, filesToDecompress.Count);
            }

            ActionString = M3L.GetString(M3L.string_patchingFiles);
            Percent      = 0;
            done         = 0;
            string singlesuffix = M3L.GetString(M3L.string_singularFile);
            string pluralsuffix = M3L.GetString(M3L.string_pluralFiles);

            // Logging
            Log.Information(@"The following files will be promoted in the compatibility pack:");
            foreach (var file in filesToBePatched)
            {
                Log.Information(@" - " + file);
            }

            foreach (var file in filesToBePatched)
            {
                Log.Information(@"Patching file: " + file);
                ActionSubstring = Path.GetFileName(file);
                var package = MEPackageHandler.OpenMEPackage(file);
                if (package == null)
                {
                    Log.Error(@"pPackage object is null!!!");
                }
                var guiExports = package.Exports.Where(x => !x.IsDefaultObject && x.ClassName == @"GFxMovieInfo").ToList();
                if (guiExports.Count > 0)
                {
                    //potential item needing replacement
                    //Check GUI library to see if we have anything.
                    foreach (var export in guiExports)
                    {
                        if (uiLibraryData.TryGetValue(export.GetFullPath, out var newData))
                        {
                            Log.Information(@" >> Patching export " + export.GetFullPath);
                            //Patching this export.
                            var exportProperties = export.GetProperties();
                            var rawData          = exportProperties.GetProp <ArrayProperty <ByteProperty> >(@"RawData");
                            if (rawData == null)
                            {
                                Log.Error(@"Rawdata is null!!");
                            }
                            rawData.Clear();
                            rawData.AddRange(newData.Select(x => new ByteProperty(x))); //This will be terribly slow. Need to port over new ME3Exp binary data handler
                            export.WriteProperties(exportProperties);
                        }
                        else
                        {
                            Debug.WriteLine(@"Not patching gui export, file not in library: " + export.GetFullPath);
                        }
                    }

                    var outpath = Path.Combine(outputPath, Path.GetFileName(package.FilePath));
                    if (package.IsModified)
                    {
                        Log.Information(@"Saving patched package to " + outpath);
                        package.save(outpath, true);
                        done++;
                        ActionSubstring = M3L.GetString(M3L.string_interp_patchedXY, done.ToString(), done == 1 ? singlesuffix : pluralsuffix);
                    }
                    else
                    {
                        done++;
                        Log.Information(@"File was patched but data did not change! " + outpath);
                    }

                    Percent = getPercent(done, filesToBePatched.Count);
                }
                else
                {
                    throw new Exception($@"Error: {Path.GetFileName(file)} doesn't contain GUI exports! This shouldn't have been possible.");
                }
            }

            // Write builtagainst for metacmm.
            IniData iniData      = new FileIniDataParser().ReadFile(generatedMod.ModDescPath);
            var     builtAgainst = VanillaDatabaseService.GetInstalledDLCMods(target);

            builtAgainst.Remove(@"DLC_MOD_" + UI_MOD_NAME);
            iniData[@"ControllerCompat"][@"builtagainst"] = string.Join(';', builtAgainst);
            File.WriteAllText(generatedMod.ModDescPath, iniData.ToString());

            return(generatedMod);
        }
Example #3
0
        /// <summary>
        /// Copy ME2 Static art and collision into an ME3 file.
        /// By Kinkojiro
        /// </summary>
        /// <param name="Game">Target Game</param>
        /// <param name="BioPSource">Source BioP</param>
        /// <param name="tgtOutputfolder">OutputFolder</param>
        /// <param name="BioArtsToCopy">List of level source file locations</param>
        /// <param name="ActorsToMove">Dicitionary: key Actors, value filename, entry uid</param>
        /// <param name="AssetsToMove">Dictionary key: AssetInstancedPath, value filename, isimport, entry uid</param>
        /// <param name="fromreload">is reloaded json</param>
        public static async Task <List <string> > ConvertLevelToGame(MEGame Game, IMEPackage BioPSource, string tgtOutputfolder, string tgttfc, Action <string> callbackAction, LevelConversionData conversionData = null, bool fromreload = false, bool createtestlevel = false)
        {
            //VARIABLES / VALIDATION
            var actorclassesToMove = new List <string>()
            {
                "BlockingVolume", "SpotLight", "SpotLightToggleable", "PointLight", "PointLightToggleable", "SkyLight", "HeightFog", "LenseFlareSource", "StaticMeshActor", "BioTriggerStream", "BioBlockingVolume"
            };
            var actorclassesToSubstitute = new Dictionary <string, string>()
            {
                { "BioBlockingVolume", "Engine.BlockingVolume" }
            };
            var archetypesToSubstitute = new Dictionary <string, string>()
            {
                { "Default__BioBlockingVolume", "Default__BlockingVolume" }
            };
            var    fails    = new List <string>();
            string busytext = null;

            if ((BioPSource.Game == MEGame.ME2 && ME2Directory.DefaultGamePath == null) || (BioPSource.Game == MEGame.ME1 && ME1Directory.DefaultGamePath == null) || (BioPSource.Game == MEGame.ME3 && ME3Directory.DefaultGamePath == null) || BioPSource.Game == MEGame.UDK)
            {
                fails.Add("Source Game Directory not found");
                return(fails);
            }

            //Get filelist from BioP, Save a copy in outputdirectory, Collate Actors and asset information
            if (!fromreload)
            {
                busytext = "Collating level files...";
                callbackAction?.Invoke(busytext);
                conversionData = new LevelConversionData(Game, BioPSource.Game, null, null, null, new ConcurrentDictionary <string, string>(), new ConcurrentDictionary <string, (string, int)>(), new ConcurrentDictionary <string, (string, int, List <string>)>());
                var supportedExtensions = new List <string> {
                    ".pcc", ".u", ".upk", ".sfm"
                };
                if (Path.GetFileName(BioPSource.FilePath).ToLowerInvariant().StartsWith("biop_") && BioPSource.Exports.FirstOrDefault(x => x.ClassName == "BioWorldInfo") is ExportEntry BioWorld)
                {
                    string biopname = Path.GetFileNameWithoutExtension(BioPSource.FilePath);
                    conversionData.GameLevelName = biopname.Substring(5, biopname.Length - 5);
                    var lsks = BioWorld.GetProperty <ArrayProperty <ObjectProperty> >("StreamingLevels").ToList();
                    if (lsks.IsEmpty())
                    {
                        fails.Add("No files found in level.");
                        return(fails);
                    }
                    foreach (var l in lsks)
                    {
                        var lskexp   = BioPSource.GetUExport(l.Value);
                        var filename = lskexp.GetProperty <NameProperty>("PackageName");
                        if ((filename?.Value.ToString().ToLowerInvariant().StartsWith("bioa") ?? false) || (filename?.Value.ToString().ToLowerInvariant().StartsWith("biod") ?? false))
                        {
                            var filePath = Directory.GetFiles(ME2Directory.DefaultGamePath, $"{filename.Value.Instanced}.pcc", SearchOption.AllDirectories).FirstOrDefault();
                            conversionData.FilesToCopy.TryAdd(filename.Value.Instanced, filePath);
                        }
                    }
                    conversionData.BioPSource = $"{biopname}_{BioPSource.Game}";
                    BioPSource.Save(Path.Combine(tgtOutputfolder, $"{conversionData.BioPSource}.pcc"));
                    BioPSource.Dispose();
                }
                else
                {
                    fails.Add("Requires an BioP to work with.");
                    return(fails);
                }

                busytext = "Collating actors and assets...";
                callbackAction?.Invoke(busytext);
                Parallel.ForEach(conversionData.FilesToCopy, (pccref) =>
                {
                    using IMEPackage pcc = MEPackageHandler.OpenMEPackage(pccref.Value);
                    var sourcelevel      = pcc.Exports.FirstOrDefault(l => l.ClassName == "Level");
                    if (ObjectBinary.From(sourcelevel) is Level levelbin)
                    {
                        foreach (var act in levelbin.Actors)
                        {
                            if (act < 1)
                            {
                                continue;
                            }
                            var actor = pcc.GetUExport(act);
                            if (actorclassesToMove.Contains(actor.ClassName))
                            {
                                conversionData.ActorsToMove.TryAdd($"{pccref.Key}.{actor.InstancedFullPath}", (pccref.Key, act));
                                HashSet <int> actorrefs = pcc.GetReferencedEntries(true, true, actor);
                                foreach (var r in actorrefs)
                                {
                                    var objref = pcc.GetEntry(r);
                                    if (objref != null)
                                    {
                                        if (objref.InstancedFullPath.Contains("PersistentLevel"))  //remove components of actors
                                        {
                                            continue;
                                        }
                                        string instancedPath = objref.InstancedFullPath;
                                        if (objref.idxLink == 0)
                                        {
                                            instancedPath = $"{pccref.Key}.{instancedPath}";
                                        }
                                        var added = conversionData.AssetsToMove.TryAdd(instancedPath, (pccref.Key, r, new List <string>()
                                        {
                                            pccref.Key
                                        }));
                                        if (!added)
                                        {
                                            conversionData.AssetsToMove[instancedPath].Item3.FindOrAdd(pccref.Key);
                                            if (r > 0 && conversionData.AssetsToMove[instancedPath].Item2 < 0) //Replace  imports with exports if possible
                                            {
                                                var currentlist = conversionData.AssetsToMove[instancedPath].Item3;
                                                conversionData.AssetsToMove[instancedPath] = (pccref.Key, r, currentlist);
                                            }
                                        }
Example #4
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);
        }
Example #5
0
        private void scanToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(ME3Directory.cookedPath))
            {
                MessageBox.Show("This functionality requires ME3 to be installed. Set its path at:\n Options > Set Custom Path > Mass Effect 3");
                return;
            }
            DebugOutput.StartDebugger("Meshplorer2");
            int count = 0;

            timer1.Enabled = false;
            Entries        = new List <EntryStruct>();
            bool ScanDLC = false;

            if (MessageBox.Show("Scan DLCs too?", "Meshplorer 2", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                ScanDLC = true;
            }
            if (ScanDLC)
            {
                #region DLC Stuff
                string dirDLC = ME3Directory.DLCPath;
                if (!Directory.Exists(dirDLC))
                {
                    DebugOutput.PrintLn("No DLC Folder found!");
                }
                else
                {
                    string[] subdirs = Directory.GetDirectories(dirDLC);
                    string   loc     = Path.GetDirectoryName(Application.ExecutablePath) + "\\exec\\";
                    Directory.CreateDirectory(loc + "temp");
                    foreach (string DLCpath in subdirs)
                    {
                        if (!DLCpath.StartsWith("__") && !DLCpath.StartsWith("DLC_UPD"))
                        {
                            string  path = DLCpath + "\\CookedPCConsole\\Default.sfar";
                            DLCBase dlcbase;
                            try
                            {
                                dlcbase       = new DLCBase(path);
                                count         = 0;
                                pbar1.Maximum = dlcbase.fileList.Count;
                                foreach (sfarFile file in dlcbase.fileList)
                                {
                                    try
                                    {
                                        string filename = Path.GetFileName(file.fileName);
                                        if (Path.GetExtension(filename).ToLower().EndsWith(".pcc"))
                                        {
                                            using (Stream input = File.OpenRead(path), output = File.Create(loc + "temp\\" + filename))
                                            {
                                                AmaroK86.MassEffect3.DLCUnpack.DecompressEntry(file, input, output, dlcbase.CompressionScheme);
                                            }
                                            FileInfo f = new FileInfo(loc + "temp\\" + filename);
                                            DebugOutput.PrintLn("checking DLC: " + Path.GetFileName(DLCpath) + " File: " + filename + " Size: " + f.Length + " bytes", count % 3 == 0);
                                            using (ME3Package pcc = MEPackageHandler.OpenME3Package(loc + "temp\\" + filename))
                                            {
                                                IReadOnlyList <IExportEntry> Exports = pcc.Exports;
                                                for (int i = 0; i < Exports.Count; i++)
                                                {
                                                    if (Exports[i].ClassName == "SkeletalMesh" ||
                                                        Exports[i].ClassName == "StaticMesh")
                                                    {
                                                        EntryStruct ent = new EntryStruct();
                                                        ent.DLCName    = Path.GetFileName(DLCpath);
                                                        ent.Filename   = filename;
                                                        ent.Index      = i;
                                                        ent.isDLC      = true;
                                                        ent.ObjectPath = Exports[i].GetFullPath;
                                                        ent.isSkeletal = Exports[i].ClassName == "SkeletalMesh";
                                                        Entries.Add(ent);
                                                    }
                                                }
                                            }
                                            File.Delete(loc + "temp\\" + filename);
                                        }
                                        if (count % 3 == 0)
                                        {
                                            pbar1.Value = count;
                                            Application.DoEvents();
                                        }
                                        count++;
                                    }
                                    catch (Exception ex)
                                    {
                                        DebugOutput.PrintLn("=====ERROR=====\n" + ex.ToString() + "\n=====ERROR=====");
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                DebugOutput.PrintLn("=====ERROR=====\n" + ex.ToString() + "\n=====ERROR=====");
                            }
                        }
                    }
                    Directory.Delete(loc + "temp", true);
                }
                #endregion
            }
            #region Basegame Stuff
            string   dir   = ME3Directory.cookedPath;
            string[] files = Directory.GetFiles(dir, "*.pcc");
            pbar1.Maximum = files.Length - 1;
            foreach (string file in files)
            {
                DebugOutput.PrintLn("Scan file #" + count + " : " + file, count % 10 == 0);
                try
                {
                    using (ME3Package pcc = MEPackageHandler.OpenME3Package(file))
                    {
                        IReadOnlyList <IExportEntry> Exports = pcc.Exports;
                        for (int i = 0; i < Exports.Count; i++)
                        {
                            if (Exports[i].ClassName == "SkeletalMesh" ||
                                Exports[i].ClassName == "StaticMesh")
                            {
                                EntryStruct ent = new EntryStruct();
                                ent.DLCName    = "";
                                ent.Filename   = Path.GetFileName(file);
                                ent.Index      = i;
                                ent.isDLC      = false;
                                ent.ObjectPath = Exports[i].GetFullPath;
                                ent.isSkeletal = Exports[i].ClassName == "SkeletalMesh";
                                Entries.Add(ent);
                            }
                        }
                    }
                    if (count % 10 == 0)
                    {
                        Application.DoEvents();
                        pbar1.Value = count;
                    }
                    count++;
                }
                catch (Exception ex)
                {
                    DebugOutput.PrintLn("=====ERROR=====\n" + ex.ToString() + "\n=====ERROR=====");
                }
            }
            #endregion
            #region Sorting
            bool run = true;
            DebugOutput.PrintLn("=====Info=====\n\nSorting names...\n\n=====Info=====");
            count = 0;
            while (run)
            {
                run = false;
                for (int i = 0; i < Entries.Count - 1; i++)
                {
                    if (Entries[i].Filename.CompareTo(Entries[i + 1].Filename) > 0)
                    {
                        EntryStruct tmp = Entries[i];
                        Entries[i]     = Entries[i + 1];
                        Entries[i + 1] = tmp;
                        run            = true;
                        if (count++ % 100 == 0)
                        {
                            Application.DoEvents();
                        }
                    }
                }
            }
            #endregion
            TreeRefresh();
            timer1.Enabled = true;
        }
Example #6
0
        public static bool InstallOHKO(RandomizationOption arg)
        {
            MERLog.Information("Installing One-Hit KO");

            var sfxgame = MERFileSystem.GetPackageFile("SFXGame.pcc");

            if (sfxgame != null && File.Exists(sfxgame))
            {
                var sfxgameP = MEPackageHandler.OpenMEPackage(sfxgame);

                // Blood on screen VFX
                sfxgameP.GetUExport(29336).RemoveProperty("BleedOutEventPair");
                sfxgameP.GetUExport(29336).RemoveProperty("BleedOutVFXTemplate");

                // Prevent weird landing glitch
                var fallingStateLanded = sfxgameP.GetUExport(11293);
                var landedData         = fallingStateLanded.Data;
                NopRange(landedData, 0x2C, 0x14);
                fallingStateLanded.Data = landedData;

                MERFileSystem.SavePackage(sfxgameP);
            }
            else
            {
            }


            // ProCer tutorials setting min1Health
            SetupProCer();

            // Player classes - Remove shields, set maxhealth to 1
            string[] classes = new[] { "Adept", "Engineer", "Infiltrator", "Sentinel", "Soldier", "Vanguard" };
            foreach (var c in classes)
            {
                var charClass = MERFileSystem.GetPackageFile($"SFXCharacterClass_{c}.pcc");
                if (charClass != null && File.Exists(charClass))
                {
                    var charClassP = MEPackageHandler.OpenMEPackage(charClass);
                    var ccLoadout  = charClassP.FindExport($"BioChar_Loadouts.Player.PLY_{c}");

                    var ccProps = ccLoadout.GetProperties();
                    ccProps.GetProp <ArrayProperty <StructProperty> >("ShieldLoadouts").Clear(); // Remove shields

                    // Set health to 1
                    var health = ccProps.GetProp <StructProperty>("MaxHealth");
                    health.GetProp <FloatProperty>("X").Value = 1;
                    health.GetProp <FloatProperty>("Y").Value = 1;

                    ccLoadout.WriteProperties(ccProps);

                    // Remove any passive powers
                    ZeroOutStatList(charClassP.FindExport($"SFXGameContent_Powers.SFXPower_{c}Passive"), "HealthBonus", false);
                    ZeroOutStatList(charClassP.FindExport($"SFXGameContent_Powers.SFXPower_{c}Passive_Evolved1"), "HealthBonus", false);
                    ZeroOutStatList(charClassP.FindExport($"SFXGameContent_Powers.SFXPower_{c}Passive_Evolved2"), "HealthBonus", false);

                    if (c == "Vanguard")
                    {
                        // Patch the immunity effect
                        var shieldEffectOnApplied = charClassP.GetUExport(530);
                        var seData = shieldEffectOnApplied.Data;
                        NopRange(seData, 0x2BF, 0x13);
                        shieldEffectOnApplied.Data = seData;
                    }

                    MERFileSystem.SavePackage(charClassP);
                }
            }

            // Zero out stats in tables
            MERPackageCache cache = new MERPackageCache();

            foreach (var asset in ZeroOutStatAssets)
            {
                var statClass = asset.GetAsset(cache);
                if (statClass != null)
                {
                    foreach (var zos in asset.PropertiesToZeroOut)
                    {
                        ZeroOutStatList(statClass, zos, true);
                    }
                }
            }

            foreach (var package in cache.GetPackages())
            {
                MERFileSystem.SavePackage(package);
            }

            {
                //var reaveF = MERFileSystem.GetPackageFile("SFXPower_Reave_Player.pcc");
                //if (reaveF != null && File.Exists(reaveF))
                //{
                //    var reaveP = MEPackageHandler.OpenMEPackage(reaveF);

                //    var defaultReave = reaveP.GetUExport(27);
                //    var regen = defaultReave.GetProperty<ArrayProperty<FloatProperty>>("HealthRegenMult");
                //    regen[0].Value = 0;
                //    regen[1].Value = 0;
                //    regen[2].Value = 0;
                //    regen[3].Value = 0;
                //    defaultReave.WriteProperty(regen);

                //    var bonusDuration = defaultReave.GetProperty<ArrayProperty<FloatProperty>>("HealthBonusDuration");
                //    bonusDuration[0].Value = 0;
                //    bonusDuration[1].Value = 0;
                //    bonusDuration[2].Value = 0;
                //    bonusDuration[3].Value = 0;
                //    defaultReave.WriteProperty(bonusDuration);

                //    MERFileSystem.SavePackage(reaveP);
                //}
            }

            return(true);
        }
Example #7
0
        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            TreeNode t = treeView1.SelectedNode;

            if (DisplayStyle == 0)
            {
                if (t.Parent == null || t.Name == "")
                {
                    return;
                }
                preview?.Dispose();
                preview = null;
                try
                {
                    if (int.TryParse(t.Name, out int i))
                    {
                        EntryStruct en = Entries[i];
                        if (!en.isDLC)
                        {
                            using (ME3Package pcc = MEPackageHandler.OpenME3Package(ME3Directory.cookedPath + en.Filename))
                            {
                                if (en.isSkeletal)
                                {
                                    SkeletalMesh skmesh = new SkeletalMesh(pcc, en.Index); // TODO: pass device
                                    preview = new ModelPreview(view.Device, skmesh, view.TextureCache);
                                    CenterView();
                                    treeView2.Nodes.Clear();
                                    if (previewWithTreeToolStripMenuItem.Checked)
                                    {
                                        treeView2.Visible = false;
                                        Application.DoEvents();
                                        treeView2.Nodes.Add(skmesh.ToTree());
                                        treeView2.Visible = true;
                                    }
                                }
                                else
                                {
                                }
                            }
                        }
                        else
                        {
                            string loc    = Path.GetDirectoryName(Application.ExecutablePath) + "\\exec\\";
                            string dirDLC = ME3Directory.DLCPath;
                            dirDLC += en.DLCName;
                            dirDLC += "\\CookedPCConsole\\Default.sfar";
                            DLCUnpack dlc = new DLCUnpack(dirDLC);
                            foreach (DLCUnpack.DLCEntry file in dlc.filesList)
                            {
                                try
                                {
                                    string filename = Path.GetFileName(file.filenamePath);
                                    if (Path.GetExtension(filename).ToLower().EndsWith(".pcc") && filename == en.Filename)
                                    {
                                        using (Stream input = File.OpenRead(dirDLC), output = File.Create(loc + filename))
                                        {
                                            dlc.ExtractEntry(file, input, output);
                                        }
                                        if (File.Exists(loc + filename))
                                        {
                                            try
                                            {
                                                if (en.isSkeletal)
                                                {
                                                    using (ME3Package pcc = MEPackageHandler.OpenME3Package(loc + filename))
                                                    {
                                                        SkeletalMesh skmesh = new SkeletalMesh(pcc, en.Index);
                                                        CenterView();
                                                        treeView2.Nodes.Clear();
                                                        if (previewWithTreeToolStripMenuItem.Checked)
                                                        {
                                                            treeView2.Visible = false;
                                                            Application.DoEvents();
                                                            treeView2.Nodes.Add(skmesh.ToTree());
                                                            treeView2.Visible = true;
                                                        }
                                                    }
                                                }
                                            }
                                            catch (Exception)
                                            {
                                            }
                                            File.Delete(loc + filename);
                                        }
                                    }
                                }
                                catch (Exception)
                                {
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }
        }
        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
        }
Example #9
0
        public TreeNode BuildPccTree(string pcc)
        {
            if (!_isPackageHandlerInitialized)
            {
                MEPackageHandler.Initialize();
                _isPackageHandlerInitialized = true;;
            }

            // check first if this pcc has already been added in the tree
            bool IsPccAlreadyInTree = PccList.Find(ptn => ptn.pcc.FileName == pcc) != null;

            if (IsPccAlreadyInTree)
            {
                return(null);
            }

            var Pcc = MEPackageHandler.OpenMEPackage(pcc);

            /*
             * ME3Package me3Pcc = Pcc as ME3Package;
             *
             * if (me3Pcc == null)
             * {
             *  // supports only ME3 for the time being
             *  throw new ArgumentException("This is not a ME3 package. The tool only supports ME3 at this time.");
             * }
             * */

            PccTreeNode pccRootNode = null;

            var MorphExpIndexes = Pcc.Exports.Select((value, index) => new { value, index })
                                  .Where(z => z.value.ClassName == "BioMorphFace")
                                  .Select(z => z.index);

            if (MorphExpIndexes.Count() > 0)
            {
                pccRootNode = new PccTreeNode(Pcc);

                // Morphs
                TreeNode ParentMorphNode = new TreeNode();
                ParentMorphNode.Text = "BioMorphFace";

                foreach (int morphIndex in MorphExpIndexes)
                {
                    var           morph     = new Unreal.BioMorphFace(Pcc, morphIndex);
                    MorphTreeNode morphNode = new MorphTreeNode(morph);
                    ParentMorphNode.Nodes.Add(morphNode);
                }

                // Meshes
                TreeNode ParentMeshNode = new TreeNode();
                ParentMeshNode.Text = "SkeletalMesh";

                var MeshExpIndexes = Pcc.Exports.Select((value, index) => new { value, index })
                                     .Where(z => z.value.ClassName == "SkeletalMesh")
                                     .Select(z => z.index);

                foreach (int meshIndex in MeshExpIndexes)
                {
                    IExportEntry exp = Pcc.Exports[meshIndex];
                    //SkeletalMesh skMesh = new SkeletalMesh((ME3Package)Pcc, meshIndex);
                    var          skMesh   = new Unreal.MESkeletalMesh(Pcc, meshIndex);
                    MeshTreeNode meshNode = new MeshTreeNode(skMesh, exp);
                    ParentMeshNode.Nodes.Add(meshNode);
                }

                // build hierarchy
                pccRootNode.Nodes.Add(ParentMeshNode);
                pccRootNode.Nodes.Add(ParentMorphNode);

                // add in the list
                PccList.Add(pccRootNode);
            }
            else
            {
                Pcc = null;
            }
            return(pccRootNode);
        }
        public static void ApplyMixinsToModule(KeyValuePair <ModJob.JobHeader, Dictionary <string, List <Mixin> > > mapping, string modpath, Action completedSingleApplicationCallback, Action <string> failedApplicationCallback)
        {
            var dlcFolderName = ModMakerCompiler.ModmakerChunkNameToDLCFoldername(mapping.Key.ToString());
            var outdir        = Path.Combine(modpath, ModMakerCompiler.HeaderToDefaultFoldername(mapping.Key), @"CookedPCConsole");

            Directory.CreateDirectory(outdir);
            if (mapping.Key == ModJob.JobHeader.BASEGAME)
            {
                //basegame
                foreach (var file in mapping.Value)
                {
                    try
                    {
                        using var packageAsStream =
                                  VanillaDatabaseService.FetchBasegameFile(MEGame.ME3,
                                                                           Path.GetFileName(file.Key));
                        //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc");
                        using var decompressedStream = MEPackage.GetDecompressedPackageStream(packageAsStream, false, true);
                        using var finalStream        = MixinHandler.ApplyMixins(decompressedStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback);
                        CLog.Information(@"Compressing package to mod directory: " + file.Key, Settings.LogModMakerCompiler);
                        finalStream.Position = 0;
                        var package = MEPackageHandler.OpenMEPackageFromStream(finalStream);
                        var outfile = Path.Combine(outdir, Path.GetFileName(file.Key));
                        package.Save(outfile, false, includeAdditionalPackagesToCook: false, includeDependencyTable: true); // don't compress, use mixin saving rules for basegame files
                    }
                    catch (Exception e)
                    {
                        var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName));
                        Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}");
                        failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message));
                    }
                }
            }
            else
            {
                //dlc
                var dlcPackage = VanillaDatabaseService.FetchVanillaSFAR(dlcFolderName); //do not have to open file multiple times.
                foreach (var file in mapping.Value)
                {
                    try
                    {
                        using var packageAsStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, file.Key, forcedDLC: dlcPackage);
                        //as file comes from backup, we don't need to decompress it, it will always be decompressed in sfar
                        using var finalStream = MixinHandler.ApplyMixins(packageAsStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback);

                        var outfile = Path.Combine(outdir, Path.GetFileName(file.Key));

                        if (mapping.Key != ModJob.JobHeader.TESTPATCH)
                        {
                            // TestPatch is never unpacked. So there is not really point to
                            // compressing it's rather small files. The other DLC jobs likely will be packed still, but this will save some disk space.

                            CLog.Information($@"Compressing package to mod directory: {outfile}", Settings.LogModMakerCompiler);
                            finalStream.Position = 0;
                            var package = MEPackageHandler.OpenMEPackageFromStream(finalStream);
                            package.Save(outfile, true);
                        }
                        else
                        {
                            Log.Information($@"Writing patched file to disk: {outfile}");
                            finalStream.WriteToFile(outfile);
                        }
                    }
                    catch (Exception e)
                    {
                        var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName));
                        Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}");
                        failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message));
                    }

                    //finalStream.WriteToFile(outfile);
                }
            }
        }
        private void CompileIntoGame()
        {
            NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"MixinManager CompileIntoGameThread");
            List <string>         failedApplications = new List <string>();

            nbw.DoWork += (a, b) =>
            {
                BottomLeftMessage   = M3L.GetString(M3L.string_compilingMixins);
                OperationInProgress = true;
                //DEBUG STUFF
#if DEBUG
                int numCoresToApplyWith = 1;
#else
                var numCoresToApplyWith = Environment.ProcessorCount;
                if (numCoresToApplyWith > 4)
                {
                    numCoresToApplyWith = 4;                          //no more than 4 as this uses a lot of memory
                }
#endif

                var mixins = AvailableOfficialMixins.Where(x => x.UISelectedForUse).ToList();
                MixinHandler.LoadPatchDataForMixins(mixins); //before dynamic
                void failedApplicationCallback(string str)
                {
                    failedApplications.Add(str);
                }

                var compilingListsPerModule = MixinHandler.GetMixinApplicationList(mixins, failedApplicationCallback);
                if (Enumerable.Any(failedApplications))
                {
                    //Error building list
                    Log.Information(@"Aborting mixin install due to incompatible selection of mixins");
                    return;
                }

                ProgressBarMax   = mixins.Count();
                ProgressBarValue = 0;
                int numdone = 0;

                void completedSingleApplicationCallback()
                {
                    var val = Interlocked.Increment(ref numdone);

                    ProgressBarValue = val;
                }

                //Mixins are ready to be applied
                Parallel.ForEach(compilingListsPerModule,
                                 new ParallelOptions
                {
                    MaxDegreeOfParallelism = Environment.ProcessorCount > numCoresToApplyWith
                    ? numCoresToApplyWith
                    : Environment.ProcessorCount
                }, mapping =>
                {
                    var dlcFolderName = ModMakerCompiler.ModmakerChunkNameToDLCFoldername(mapping.Key.ToString());
                    //var outdir = Path.Combine(modpath, ModMakerCompiler.HeaderToDefaultFoldername(mapping.Key), @"CookedPCConsole");
                    //Directory.CreateDirectory(outdir);
                    if (mapping.Key == ModJob.JobHeader.BASEGAME)
                    {
                        //basegame
                        foreach (var file in mapping.Value)
                        {
                            try
                            {
                                using var vanillaPackageAsStream = VanillaDatabaseService.FetchBasegameFile(MEGame.ME3, Path.GetFileName(file.Key));
                                //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc");
                                using var decompressedStream = MEPackage.GetDecompressedPackageStream(vanillaPackageAsStream, false, true);
                                decompressedStream.Position  = 0;
                                var vanillaPackage           = MEPackageHandler.OpenMEPackageFromStream(decompressedStream, $@"Vanilla - {Path.GetFileName(file.Key)}");
                                //decompressedStream.WriteToFile(@"C:\users\dev\desktop\decompressed.pcc");

                                using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, true,
                                                                                         completedSingleApplicationCallback, failedApplicationCallback);
                                mixinModifiedStream.Position = 0;
                                var modifiedPackage          = MEPackageHandler.OpenMEPackageFromStream(mixinModifiedStream, $@"Mixin Modified - {Path.GetFileName(file.Key)}");

                                // three way merge: get target stream
                                var targetFile    = Path.Combine(M3Directories.GetCookedPath(SelectedInstallTarget), Path.GetFileName(file.Key));
                                var targetPackage = MEPackageHandler.OpenMEPackage(targetFile);

                                var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage);
                                if (merged)
                                {
                                    targetPackage.Save(compress: true);
                                    Log.Information(@"Three way merge succeeded for " + targetFile);
                                }
                                else
                                {
                                    Log.Error(@"Could not merge three way merge into " + targetFile);
                                }
                                //var outfile = Path.Combine(outdir, Path.GetFileName(file.Key));
                                //package.save(outfile, false); // don't compress
                                //finalStream.WriteToFile(outfile);
                                //File.WriteAllBytes(outfile, finalStream.ToArray());
                            }
                            catch (Exception e)
                            {
                                var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName));
                                Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}");
                                failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message));
                            }
                        }
                    }
                    else
                    {
                        //dlc
                        var dlcPackage        = VanillaDatabaseService.FetchVanillaSFAR(dlcFolderName); //do not have to open file multiple times.
                        var targetCookedPCDir = Path.Combine(M3Directories.GetDLCPath(SelectedInstallTarget), dlcFolderName, @"CookedPCConsole");
                        var sfar      = mapping.Key == ModJob.JobHeader.TESTPATCH ? M3Directories.GetTestPatchSFARPath(SelectedInstallTarget) : Path.Combine(targetCookedPCDir, @"Default.sfar");
                        bool unpacked = new FileInfo(sfar).Length == 32;
                        DLCPackage targetDLCPackage = unpacked ? null : new DLCPackage(sfar); //cache SFAR target

                        foreach (var file in mapping.Value)
                        {
                            try
                            {
                                using var vanillaPackageAsStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, file.Key, forcedDLC: dlcPackage);
                                using var decompressedStream     = MEPackage.GetDecompressedPackageStream(vanillaPackageAsStream);
                                decompressedStream.Position      = 0;
                                var vanillaPackage            = MEPackageHandler.OpenMEPackageFromStream(decompressedStream, $@"VanillaDLC - {Path.GetFileName(file.Key)}");
                                using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback);
                                mixinModifiedStream.Position  = 0;
                                var modifiedPackage           = MEPackageHandler.OpenMEPackageFromStream(mixinModifiedStream, $@"Mixin Modified - {Path.GetFileName(file.Key)}");

                                // three way merge: get target stream
                                // must see if DLC is unpacked first

                                MemoryStream targetFileStream = null;

                                //Packed
                                if (unpacked)
                                {
                                    targetFileStream = new MemoryStream(File.ReadAllBytes(Path.Combine(targetCookedPCDir, file.Key)));
                                }
                                else
                                {
                                    targetFileStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, Path.GetFileName(file.Key), forcedDLC: targetDLCPackage);
                                }

                                var targetPackage = MEPackageHandler.OpenMEPackageFromStream(targetFileStream, $@"Target package {dlcFolderName} - {file.Key}, from SFAR: {unpacked}");

                                var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage);
                                if (merged)
                                {
                                    if (unpacked)
                                    {
                                        targetPackage.Save(Path.Combine(targetCookedPCDir, file.Key));
                                        Log.Information(@"Three way merge succeeded for " + targetPackage.FilePath);
                                    }
                                    else
                                    {
                                        var finalSTream = targetPackage.SaveToStream(false); // No compress. Not sure if we should support doing that though.
                                        targetDLCPackage.ReplaceEntry(finalSTream.ToArray(), targetDLCPackage.FindFileEntry(Path.GetFileName(file.Key)));
                                        Log.Information(@"Three way merge succeeded for " + targetPackage.FilePath);
                                    }
                                }
                                else
                                {
                                    Log.Error(@"Could not three way merge into: " + targetFileStream);
                                }
                            }
                            catch (Exception e)
                            {
                                var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName));
                                Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}");
                                failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message));
                            }

                            //finalStream.WriteToFile(outfile);
                        }
                    }
                });

                MixinHandler.FreeME3TweaksPatchData();
                var percent = 0; //this is used to save a localization
                BottomLeftMessage = M3L.GetString(M3L.string_interp_runningAutoTOCOnGamePercentX, percent);

                //Run autotoc
                void tocingUpdate(int percent)
                {
                    BottomLeftMessage = M3L.GetString(M3L.string_interp_runningAutoTOCOnGamePercentX, percent);
                }

                AutoTOC.RunTOCOnGameTarget(SelectedInstallTarget, tocingUpdate);

                //Generate moddesc
                //IniData ini = new IniData();
                //ini[@"ModManager"][@"cmmver"] = App.HighestSupportedModDesc.ToString(CultureInfo.InvariantCulture); //prevent commas
                //ini[@"ModInfo"][@"game"] = @"ME3";
                //ini[@"ModInfo"][@"modname"] = modname;
                //ini[@"ModInfo"][@"moddev"] = App.AppVersionHR;
                //ini[@"ModInfo"][@"moddesc"] = M3L.GetString(M3L.string_compiledFromTheFollowingMixins);
                //ini[@"ModInfo"][@"modver"] = @"1.0";

                //generateRepaceFilesMapping(ini, modpath);
                //File.WriteAllText(Path.Combine(modpath, @"moddesc.ini"), ini.ToString());
            };
            nbw.RunWorkerCompleted += (a, b) =>
            {
                if (b.Error != null)
                {
                    Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}");
                }
                OperationInProgress = false;
                ClearMixinHandler();
                if (failedApplications.Count > 0)
                {
                    var ld = new ListDialog(failedApplications, M3L.GetString(M3L.string_failedToApplyAllMixins), M3L.GetString(M3L.string_theFollowingMixinsFailedToApply), mainwindow);
                    ld.ShowDialog();
                }

                /*if (modpath != null)
                 * {
                 *  OnClosing(new DataEventArgs(modpath));
                 * }
                 * else
                 * {*/
                BottomLeftMessage = M3L.GetString(M3L.string_mixinsInstalledMaybe);
                //}
            };
            CompilePanelButton.IsOpen = false;
            nbw.RunWorkerAsync();
        }
Example #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);
        }
Example #13
0
        private PreviewTextureCache.PreviewTextureEntry FindTexture(PreviewTextureCache texcache, string FullTextureName, string ImportPCC)
        {
            string importfiledir  = System.IO.Path.GetDirectoryName(ImportPCC).ToLower();
            string importfilename = System.IO.Path.GetFileName(ImportPCC).ToLower();
            string pccpath        = "";
            int    id             = 0;

            // First, check the pcc that contains the material
            using (ME3Package pcc = MEPackageHandler.OpenME3Package(ImportPCC))
            {
                foreach (IExportEntry exp in pcc.Exports)
                {
                    if (exp.GetFullPath == FullTextureName && exp.ClassName == "Texture2D")
                    {
                        pccpath = ImportPCC;
                        id      = exp.Index;
                        break;
                    }
                }
            }
            // Next, split the filename by underscores
            string[] parts = System.IO.Path.GetFileNameWithoutExtension(importfilename).Split('_');
            if (pccpath == "" && (importfilename.StartsWith("bioa") || importfilename.StartsWith("biod")))
            {
                // Maybe go for the one with one less segment? ex. for input BioA_Nor_201CIC.pcc, look in BioA_Nor.pcc
                if (parts.Length == 3)
                {
                    string filename = importfiledir + "\\" + parts[0] + "_" + parts[1] + ".pcc";
                    if (System.IO.File.Exists(filename))
                    {
                        using (ME3Package pcc = MEPackageHandler.OpenME3Package(filename))
                        {
                            foreach (IExportEntry exp in pcc.Exports)
                            {
                                if (exp.GetFullPath == FullTextureName && exp.ClassName == "Texture2D")
                                {
                                    pccpath = importfiledir + "\\" + parts[0] + "_" + parts[1] + ".pcc";
                                    id      = exp.Index;
                                    break;
                                }
                            }
                        }
                    }
                }
                // Now go for the BioP one.
                if (pccpath == "" && parts.Length >= 2)
                {
                    string filename = importfiledir + "\\" + "BioP" + "_" + parts[1] + ".pcc";
                    if (System.IO.File.Exists(filename))
                    {
                        using (ME3Package pcc = MEPackageHandler.OpenME3Package(filename))
                        {
                            foreach (IExportEntry exp in pcc.Exports)
                            {
                                if (exp.GetFullPath == FullTextureName && exp.ClassName == "Texture2D")
                                {
                                    pccpath = importfiledir + "\\" + "BioP" + "_" + parts[1] + ".pcc";
                                    id      = exp.Index;
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            if (id > 0)
            {
                return(texcache.LoadTexture(pccpath, id));
            }
            else
            {
                Console.WriteLine("[TEXLOAD]: Could not find texture \"" + FullTextureName + "\", imported in \"" + ImportPCC + "\".");
                return(null);
            }
        }
        //call this method to regenerate ME1ObjectInfo.json
        //Takes a long time (10 to 20 minutes maybe?). Application will be completely unresponsive during that time.
        public static void generateInfo()
        {
            Classes         = new Dictionary <string, ClassInfo>();
            Structs         = new Dictionary <string, ClassInfo>();
            Enums           = new Dictionary <string, List <NameReference> >();
            SequenceObjects = new Dictionary <string, SequenceObjectInfo>();
            foreach (string file in MELoadedFiles.GetOfficialFiles(MEGame.ME1))
            {
                if (Path.GetExtension(file) == ".upk" || Path.GetExtension(file) == ".sfm" || Path.GetExtension(file) == ".u")
                {
                    Debug.WriteLine($"File: {file}");
                    using IMEPackage pcc = MEPackageHandler.OpenME1Package(file);
                    for (int j = 1; j <= pcc.ExportCount; j++)
                    {
                        ExportEntry exportEntry = pcc.GetUExport(j);
                        string      className   = exportEntry.ClassName;
                        if (className == "Enum")
                        {
                            generateEnumValues(exportEntry);
                        }
                        else if (className == "Class")
                        {
                            string objectName = exportEntry.ObjectName;
                            Debug.WriteLine($"Generating information for {objectName}");
                            if (!Classes.ContainsKey(objectName))
                            {
                                Classes.Add(objectName, generateClassInfo(exportEntry));
                            }
                        }
                        else if (className == "ScriptStruct")
                        {
                            string objectName = exportEntry.ObjectName;
                            if (!Structs.ContainsKey(exportEntry.ObjectName))
                            {
                                Structs.Add(objectName, generateClassInfo(exportEntry, isStruct: true));
                            }
                        }
                        else if (exportEntry.IsOrInheritsFrom("SequenceObject"))
                        {
                            if (!SequenceObjects.TryGetValue(className, out SequenceObjectInfo seqObjInfo))
                            {
                                seqObjInfo = new SequenceObjectInfo();
                                SequenceObjects.Add(className, seqObjInfo);
                            }

                            int objInstanceVersion = exportEntry.GetProperty <IntProperty>("ObjInstanceVersion");
                            if (objInstanceVersion > seqObjInfo.ObjInstanceVersion)
                            {
                                seqObjInfo.ObjInstanceVersion = objInstanceVersion;
                            }
                        }
                    }
                }
            }

            //CUSTOM ADDITIONS
            Classes["LightMapTexture2D"] = new ClassInfo
            {
                baseClass   = "Texture2D",
                exportIndex = 0,
                pccPath     = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName
            };

            Classes["StaticMesh"] = new ClassInfo
            {
                baseClass   = "Object",
                exportIndex = 0,
                pccPath     = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName,
                properties  =
                {
                    new KeyValuePair <string, PropertyInfo>("UseSimpleRigidBodyCollision",   new PropertyInfo(PropertyType.BoolProperty)),
                    new KeyValuePair <string, PropertyInfo>("UseSimpleLineCollision",        new PropertyInfo(PropertyType.BoolProperty)),
                    new KeyValuePair <string, PropertyInfo>("UseSimpleBoxCollision",         new PropertyInfo(PropertyType.BoolProperty)),
                    new KeyValuePair <string, PropertyInfo>("ForceDoubleSidedShadowVolumes", new PropertyInfo(PropertyType.BoolProperty)),
                    new KeyValuePair <string, PropertyInfo>("BodySetup",                     new PropertyInfo(PropertyType.ObjectProperty, "RB_BodySetup")),
                    new KeyValuePair <string, PropertyInfo>("LODDistanceRatio",              new PropertyInfo(PropertyType.FloatProperty)),
                    new KeyValuePair <string, PropertyInfo>("LightMapCoordinateIndex",       new PropertyInfo(PropertyType.IntProperty)),
                    new KeyValuePair <string, PropertyInfo>("LightMapResolution",            new PropertyInfo(PropertyType.IntProperty)),
                    new KeyValuePair <string, PropertyInfo>("SoundCue",                      new PropertyInfo(PropertyType.ObjectProperty, "SoundCue")),
                }
            };


            File.WriteAllText(jsonPath, JsonConvert.SerializeObject(new { SequenceObjects, Classes, Structs, Enums }, Formatting.Indented));
        }
        /// <summary>
        /// Extracts the mod from the archive. The caller should handle exception that may be thrown.
        /// </summary>
        /// <param name="archivePath"></param>
        /// <param name="outputFolderPath"></param>
        /// <param name="compressPackages"></param>
        /// <param name="updateTextCallback"></param>
        /// <param name="extractingCallback"></param>
        /// <param name="compressedPackageCallback"></param>
        /// <param name="testRun"></param>
        public void ExtractFromArchive(string archivePath, string outputFolderPath, bool compressPackages,
                                       Action <string> updateTextCallback = null, Action <DetailedProgressEventArgs> extractingCallback = null, Action <string, int, int> compressedPackageCallback = null,
                                       bool testRun = false, Stream archiveStream = null)
        {
            if (!IsInArchive)
            {
                throw new Exception(@"Cannot extract a mod that is not part of an archive.");
            }
            if (archiveStream == null && !File.Exists(archivePath))
            {
                throw new Exception(M3L.GetString(M3L.string_interp_theArchiveFileArchivePathIsNoLongerAvailable, archivePath));
            }

            compressPackages &= Game >= MEGame.ME2;

            SevenZipExtractor archive;
            var isExe = archivePath.EndsWith(@".exe", StringComparison.InvariantCultureIgnoreCase);

            bool closeStreamOnFinish = true;

            if (archiveStream != null)
            {
                archive             = isExe ? new SevenZipExtractor(archiveStream, InArchiveFormat.Nsis) : new SevenZipExtractor(archiveStream);
                closeStreamOnFinish = false;
            }
            else
            {
                archive = isExe ? new SevenZipExtractor(archivePath, InArchiveFormat.Nsis) : new SevenZipExtractor(archivePath);
            }

            var fileIndicesToExtract       = new List <int>();
            var filePathsToExtractTESTONLY = new List <string>();
            var referencedFiles            = GetAllRelativeReferences(!IsVirtualized, archive);

            if (isExe)
            {
                //remap to mod root. Not entirely sure if this needs to be done for sub mods?
                referencedFiles = Enumerable.Select <string, string>(referencedFiles, x => FilesystemInterposer.PathCombine(IsInArchive, ModPath, x)).ToList(); //remap to in-archive paths so they match entry paths
            }
            foreach (var info in archive.ArchiveFileData)
            {
                if (!info.IsDirectory && (ModPath == "" || info.FileName.Contains((string)ModPath)))
                {
                    var relativedName = isExe ? info.FileName : info.FileName.Substring(ModPath.Length).TrimStart('\\');
                    if (referencedFiles.Contains(relativedName))
                    {
                        Log.Information(@"Adding file to extraction list: " + info.FileName);
                        fileIndicesToExtract.Add(info.Index);
                        filePathsToExtractTESTONLY.Add(relativedName);
                    }
                }
            }

            void archiveExtractionProgress(object?sender, DetailedProgressEventArgs args)
            {
                extractingCallback?.Invoke(args);
            }

            archive.Progressing += archiveExtractionProgress;
            string outputFilePathMapping(ArchiveFileInfo entryInfo)
            {
                Log.Information(@"Mapping extraction target for " + entryInfo.FileName);

                string entryPath = entryInfo.FileName;

                if (ExeExtractionTransform != null && Enumerable.Any <(int index, string outfile)>(ExeExtractionTransform.PatchRedirects, x => x.index == entryInfo.Index))
                {
                    Log.Information(@"Extracting vpatch file at index " + entryInfo.Index);
                    return(Path.Combine(Utilities.GetVPatchRedirectsFolder(), Enumerable.First <(int index, string outfile)>(ExeExtractionTransform.PatchRedirects, x => x.index == entryInfo.Index).outfile));
                }

                if (ExeExtractionTransform != null && Enumerable.Any <int>(ExeExtractionTransform.NoExtractIndexes, x => x == entryInfo.Index))
                {
                    Log.Information(@"Extracting file to trash (not used): " + entryPath);
                    return(Path.Combine(Utilities.GetTempPath(), @"Trash", @"trashfile"));
                }

                if (ExeExtractionTransform != null && Enumerable.Any <(int index, string outfile)>(ExeExtractionTransform.AlternateRedirects, x => x.index == entryInfo.Index))
                {
                    var outfile = Enumerable.First <(int index, string outfile)>(ExeExtractionTransform.AlternateRedirects, x => x.index == entryInfo.Index).outfile;
                    Log.Information($@"Extracting file with redirection: {entryPath} -> {outfile}");
                    return(Path.Combine(outputFolderPath, outfile));
                }

                //Archive path might start with a \. Substring may return value that start with a \
                var subModPath = entryPath /*.TrimStart('\\')*/.Substring(ModPath.Length).TrimStart('\\');
                var path       = Path.Combine(outputFolderPath, subModPath);


                //Debug.WriteLine("remapping output: " + entryPath + " -> " + path);
                return(path);
            }

            if (compressPackages)
            {
                compressionQueue = new BlockingCollection <string>();
            }

            int numberOfPackagesToCompress = Enumerable.Count <string>(referencedFiles, x => StringExtensions.RepresentsPackageFilePath(x));
            int compressedPackageCount     = 0;
            NamedBackgroundWorker compressionThread;

            if (compressPackages)
            {
                compressionThread         = new NamedBackgroundWorker(@"ImportingCompressionThread");
                compressionThread.DoWork += (a, b) =>
                {
                    try
                    {
                        while (true)
                        {
                            var  package           = compressionQueue.Take();
                            var  p                 = MEPackageHandler.OpenMEPackage(package);
                            bool shouldNotCompress = Game == MEGame.ME1;
                            if (!shouldNotCompress)
                            {
                                //updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package)));
                                FileInfo fileInfo     = new FileInfo(package);
                                var      created      = fileInfo.CreationTime;  //File Creation
                                var      lastmodified = fileInfo.LastWriteTime; //File Modification

                                compressedPackageCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package)), compressedPackageCount, numberOfPackagesToCompress);
                                Log.Information(@"Compressing package: " + package);
                                p.Save(compress: true);
                                File.SetCreationTime(package, created);
                                File.SetLastWriteTime(package, lastmodified);
                            }
                            else
                            {
                                Log.Information(@"Skipping compression for ME1 package file: " + package);
                            }


                            Interlocked.Increment(ref compressedPackageCount);
                            compressedPackageCallback?.Invoke(M3L.GetString(M3L.string_interp_compressedX, Path.GetFileName(package)), compressedPackageCount, numberOfPackagesToCompress);
                        }
                    }
                    catch (InvalidOperationException)
                    {
                        //Done.
                        lock (compressionCompletedSignaler)
                        {
                            Monitor.Pulse(compressionCompletedSignaler);
                        }
                    }
                };
                compressionThread.RunWorkerAsync();
            }

            void compressPackage(object?sender, FileInfoEventArgs args)
            {
                if (compressPackages)
                {
                    var fToCompress = outputFilePathMapping(args.FileInfo);
                    if (fToCompress.RepresentsPackageFilePath())
                    {
                        //Debug.WriteLine("Adding to blocking queue");
                        compressionQueue.TryAdd(fToCompress);
                    }
                }
            }

            archive.FileExtractionFinished += compressPackage;

            if (!testRun)
            {
                Log.Information(@"Extracting files...");
                archive.ExtractFiles(outputFolderPath, outputFilePathMapping, fileIndicesToExtract.ToArray());
            }
            else
            {
                // test run mode
                // exes can have duplicate filenames but different indexes so we must check for those here.
                if (fileIndicesToExtract.Count != referencedFiles.Count && filePathsToExtractTESTONLY.Distinct().ToList().Count != referencedFiles.Count)
                {
                    throw new Exception(@"The amount of referenced files does not match the amount of files that are going to be extracted!");
                }
            }
            Log.Information(@"File extraction completed.");
            archive.Progressing -= archiveExtractionProgress;

            compressionQueue?.CompleteAdding();
            if (compressPackages && numberOfPackagesToCompress > 0 && numberOfPackagesToCompress > compressedPackageCount)
            {
                Log.Information(@"Waiting for compression of packages to complete.");
                while (!compressionQueue.IsCompleted)
                {
                    lock (compressionCompletedSignaler)
                    {
                        Monitor.Wait(compressionCompletedSignaler);
                    }
                }

                Log.Information(@"Package compression has completed.");
            }

            archive.FileExtractionFinished -= compressPackage;

            ModPath = outputFolderPath;
            if (IsVirtualized)
            {
                var parser = new IniDataParser().Parse(VirtualizedIniText);
                parser[@"ModInfo"][@"modver"] = ModVersionString; //In event relay service resolved this
                if (!testRun)
                {
                    File.WriteAllText(Path.Combine(ModPath, @"moddesc.ini"), parser.ToString());
                }
                IsVirtualized = false; //no longer virtualized
            }

            if (ExeExtractionTransform != null)
            {
                if (EnumerableExtensions.Any <ModArchiveImporter.ExeTransform.VPatchDirective>(ExeExtractionTransform.VPatches))
                {
                    // MEHEM uses Vpatching for its alternates.
                    var vpat = Utilities.GetCachedExecutablePath(@"vpat.exe");
                    if (!testRun)
                    {
                        Utilities.ExtractInternalFile(@"MassEffectModManagerCore.modmanager.executables.vpat.exe", vpat, true);
                    }
                    //Handle VPatching
                    foreach (var transform in ExeExtractionTransform.VPatches)
                    {
                        var patchfile  = Path.Combine(Utilities.GetVPatchRedirectsFolder(), transform.patchfile);
                        var inputfile  = Path.Combine(ModPath, transform.inputfile);
                        var outputfile = Path.Combine(ModPath, transform.outputfile);

                        var args = $"\"{patchfile}\" \"{inputfile}\" \"{outputfile}\""; //do not localize
                        if (!testRun)
                        {
                            Directory.CreateDirectory(Directory.GetParent(outputfile).FullName); //ensure output directory exists as vpatch will not make one.
                        }
                        Log.Information($@"VPatching file into alternate: {inputfile} to {outputfile}");
                        updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_vPatchingIntoAlternate, Path.GetFileName(inputfile)));
                        if (!testRun)
                        {
                            Utilities.RunProcess(vpat, args, true, false, false, true);
                        }
                    }
                }

                //Handle copyfile
                foreach (var copyfile in ExeExtractionTransform.CopyFiles)
                {
                    string srcfile  = Path.Combine(ModPath, copyfile.inputfile);
                    string destfile = Path.Combine(ModPath, copyfile.outputfile);
                    Log.Information($@"Applying transform copyfile: {srcfile} -> {destfile}");
                    if (!testRun)
                    {
                        File.Copy(srcfile, destfile, true);
                    }
                }

                if (ExeExtractionTransform.PostTransformModdesc != null)
                {
                    //fetch online moddesc for this mod.
                    Log.Information(@"Fetching post-transform third party moddesc.");
                    var moddesc = OnlineContent.FetchThirdPartyModdesc(ExeExtractionTransform.PostTransformModdesc);
                    if (!testRun)
                    {
                        File.WriteAllText(Path.Combine(ModPath, @"moddesc.ini"), moddesc);
                    }
                }
            }

            //int packagesCompressed = 0;
            //if (compressPackages)
            //{
            //    var packages = Utilities.GetPackagesInDirectory(ModPath, true);
            //    extractingCallback?.Invoke(new ProgressEventArgs((byte)(packagesCompressed * 100.0 / packages.Count), 0));
            //    foreach (var package in packages)
            //    {
            //        updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package)));
            //        Log.Information("Compressing package: " + package);
            //        var p = MEPackageHandler.OpenMEPackage(package);
            //        p.save(true);

            //        packagesCompressed++;
            //        extractingCallback?.Invoke(new ProgressEventArgs((byte)(packagesCompressed * 100.0 / packages.Count), 0));
            //    }
            //}
            if (closeStreamOnFinish)
            {
                archive?.Dispose();
            }
            else
            {
                archive?.DisposeObjectOnly();
            }
        }
        public static PropertyCollection getDefaultStructValue(string className, bool stripTransients)
        {
            bool isImmutable = UnrealObjectInfo.IsImmutable(className, MEGame.ME2);

            if (Structs.ContainsKey(className))
            {
                ClassInfo info = Structs[className];
                try
                {
                    PropertyCollection structProps = new PropertyCollection();
                    ClassInfo          tempInfo    = info;
                    while (tempInfo != null)
                    {
                        foreach ((string propName, PropertyInfo propInfo) in tempInfo.properties)
                        {
                            if (stripTransients && propInfo.Transient)
                            {
                                continue;
                            }
                            if (getDefaultProperty(propName, propInfo, stripTransients, isImmutable) is Property uProp)
                            {
                                structProps.Add(uProp);
                            }
                        }
                        if (!Structs.TryGetValue(tempInfo.baseClass, out tempInfo))
                        {
                            tempInfo = null;
                        }
                    }
                    structProps.Add(new NoneProperty());

                    string filepath = null;
                    if (ME2Directory.BioGamePath != null)
                    {
                        filepath = Path.Combine(ME2Directory.BioGamePath, info.pccPath);
                    }

                    Stream loadStream = null;
                    if (File.Exists(info.pccPath))
                    {
                        filepath   = info.pccPath;
                        loadStream = new MemoryStream(File.ReadAllBytes(info.pccPath));
                    }
                    else if (info.pccPath == UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName)
                    {
                        filepath   = "GAMERESOURCES_ME2";
                        loadStream = Utilities.LoadFileFromCompressedResource("GameResources.zip", CoreLib.CustomResourceFileName(MEGame.ME2));
                    }
                    else if (filepath != null && File.Exists(filepath))
                    {
                        loadStream = new MemoryStream(File.ReadAllBytes(filepath));
                    }
#if AZURE
                    else if (MiniGameFilesPath != null && File.Exists(Path.Combine(MiniGameFilesPath, info.pccPath)))
                    {
                        // Load from test minigame folder. This is only really useful on azure where we don't have access to
                        // games
                        filepath   = Path.Combine(MiniGameFilesPath, info.pccPath);
                        loadStream = new MemoryStream(File.ReadAllBytes(filepath));
                    }
#endif

                    if (loadStream != null)
                    {
                        using (IMEPackage importPCC = MEPackageHandler.OpenMEPackageFromStream(loadStream, filepath, useSharedPackageCache: true))
                        {
                            var                exportToRead = importPCC.GetUExport(info.exportIndex);
                            byte[]             buff         = exportToRead.Data.Skip(0x30).ToArray();
                            PropertyCollection defaults     = PropertyCollection.ReadProps(exportToRead, new MemoryStream(buff), className);
                            foreach (var prop in defaults)
                            {
                                structProps.TryReplaceProp(prop);
                            }
                        }
                    }
                    return(structProps);
                }
                catch
                {
                    return(null);
                }
            }
            return(null);
        }
Example #17
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
                }
            }
        //call this method to regenerate ME2ObjectInfo.json
        //Takes a long time (10 minutes maybe?). Application will be completely unresponsive during that time.
        public static void generateInfo(string outpath)
        {
            var NewClasses         = new Dictionary <string, ClassInfo>();
            var NewStructs         = new Dictionary <string, ClassInfo>();
            var NewEnums           = new Dictionary <string, List <NameReference> >();
            var NewSequenceObjects = new Dictionary <string, SequenceObjectInfo>();

            foreach (string filePath in MELoadedFiles.GetOfficialFiles(MEGame.ME2))
            {
                if (Path.GetExtension(filePath) == ".pcc")
                {
                    using IMEPackage pcc = MEPackageHandler.OpenME2Package(filePath);
                    for (int j = 1; j <= pcc.ExportCount; j++)
                    {
                        ExportEntry exportEntry = pcc.GetUExport(j);
                        string      className   = exportEntry.ClassName;
                        if (className == "Enum")
                        {
                            generateEnumValues(exportEntry, NewEnums);
                        }
                        else if (className == "Class")
                        {
                            string objectName = exportEntry.ObjectName;
                            if (!NewClasses.ContainsKey(objectName))
                            {
                                NewClasses.Add(objectName, generateClassInfo(exportEntry));
                            }
                        }
                        else if (className == "ScriptStruct")
                        {
                            string objectName = exportEntry.ObjectName;
                            if (!NewStructs.ContainsKey(objectName))
                            {
                                NewStructs.Add(objectName, generateClassInfo(exportEntry, isStruct: true));
                            }
                        }
                        else if (exportEntry.IsA("SequenceObject"))
                        {
                            if (!NewSequenceObjects.TryGetValue(className, out SequenceObjectInfo seqObjInfo))
                            {
                                seqObjInfo = new SequenceObjectInfo();
                                NewSequenceObjects.Add(className, seqObjInfo);
                            }

                            int objInstanceVersion = exportEntry.GetProperty <IntProperty>("ObjInstanceVersion");
                            if (objInstanceVersion > seqObjInfo.ObjInstanceVersion)
                            {
                                seqObjInfo.ObjInstanceVersion = objInstanceVersion;
                            }
                        }
                    }
                    //Debug.WriteLine("Releasing " + pcc.FileName);
                }
            }

            //CUSTOM ADDITIONS
            try
            {
                NewClasses.Add("LightMapTexture2D", new ClassInfo
                {
                    baseClass   = "Texture2D",
                    exportIndex = 0,
                    pccPath     = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName
                });

                NewClasses["StaticMesh"] = new ClassInfo
                {
                    baseClass   = "Object",
                    exportIndex = 0,
                    pccPath     = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName,
                    properties  =
                    {
                        new KeyValuePair <string, PropertyInfo>("UseSimpleRigidBodyCollision", new PropertyInfo(PropertyType.BoolProperty)),
                        new KeyValuePair <string, PropertyInfo>("UseSimpleLineCollision",      new PropertyInfo(PropertyType.BoolProperty)),
                        new KeyValuePair <string, PropertyInfo>("UseSimpleBoxCollision",       new PropertyInfo(PropertyType.BoolProperty)),
                        new KeyValuePair <string, PropertyInfo>("UseFullPrecisionUVs",         new PropertyInfo(PropertyType.BoolProperty)),
                        new KeyValuePair <string, PropertyInfo>("BodySetup",                   new PropertyInfo(PropertyType.ObjectProperty, "RB_BodySetup")),
                        new KeyValuePair <string, PropertyInfo>("LODDistanceRatio",            new PropertyInfo(PropertyType.FloatProperty)),
                        new KeyValuePair <string, PropertyInfo>("LightMapCoordinateIndex",     new PropertyInfo(PropertyType.IntProperty)),
                        new KeyValuePair <string, PropertyInfo>("LightMapResolution",          new PropertyInfo(PropertyType.IntProperty)),
                    }
                };
            }
            catch (Exception)
            {
            }

            //SFXPhysicalMaterialDecals missing items
            ClassInfo sfxpmd = NewClasses["SFXPhysicalMaterialDecals"];

            string[] decalComponentArrays = { "HeavyPistol", "AutoPistol", "HandCannon", "SMG", "Shotgun", "HeavyShotgun", "FlakGun", "AssaultRifle", "Needler", "Machinegun", "SniperRifle", "AntiMatRifle", "MassCannon", "ParticleBeam" };
            foreach (string decal in decalComponentArrays)
            {
                sfxpmd.properties.Add(decal, new PropertyInfo(PropertyType.ArrayProperty, "DecalComponent"));
            }

            NewClasses["SFXWeapon"].properties.Add("InstantHitDamageTypes", new PropertyInfo(PropertyType.ArrayProperty, "Class"));

            File.WriteAllText("ME2ObjectInfo.json", JsonConvert.SerializeObject(new { SequenceObjects = NewSequenceObjects, Classes = NewClasses, Structs = NewStructs, Enums = NewEnums }, Formatting.Indented));
        }
Example #19
0
        public void ExtractFromArchive(string archivePath, string outputFolderPath, bool compressPackages,
                                       Action <string> updateTextCallback = null, Action <DetailedProgressEventArgs> extractingCallback = null, Action <string, int, int> compressedPackageCallback = null,
                                       bool testRun = false)
        {
            if (!IsInArchive)
            {
                throw new Exception(@"Cannot extract a mod that is not part of an archive.");
            }
            compressPackages &= Game == MEGame.ME3; //ME3 ONLY FOR NOW
            var isExe       = archivePath.EndsWith(@".exe", StringComparison.InvariantCultureIgnoreCase);
            var archiveFile = isExe ? new SevenZipExtractor(archivePath, InArchiveFormat.Nsis) : new SevenZipExtractor(archivePath);

            using (archiveFile)
            {
                var fileIndicesToExtract = new List <int>();
                var referencedFiles      = GetAllRelativeReferences(!IsVirtualized, archiveFile);
                if (isExe)
                {
                    //remap to mod root. Not entirely sure if this needs to be done for sub mods?
                    referencedFiles = referencedFiles.Select(x => FilesystemInterposer.PathCombine(IsInArchive, ModPath, x)).ToList(); //remap to in-archive paths so they match entry paths
                }
                foreach (var info in archiveFile.ArchiveFileData)
                {
                    if (!info.IsDirectory && (ModPath == "" || info.FileName.Contains(ModPath)))
                    {
                        var relativedName = isExe ? info.FileName : info.FileName.Substring(ModPath.Length).TrimStart('\\');
                        if (referencedFiles.Contains(relativedName))
                        {
                            Log.Information(@"Adding file to extraction list: " + info.FileName);
                            fileIndicesToExtract.Add(info.Index);
                        }
                    }
                }
                #region old

                /*
                 * bool fileAdded = false;
                 * //moddesc.ini
                 * if (info.FileName == ModDescPath)
                 * {
                 * //Debug.WriteLine("Add file to extraction list: " + info.FileName);
                 * fileIndicesToExtract.Add(info.Index);
                 * continue;
                 * }
                 *
                 * //Check each job
                 * foreach (ModJob job in InstallationJobs)
                 * {
                 * if (job.Header == ModJob.JobHeader.CUSTOMDLC)
                 * {
                 #region Extract Custom DLC
                 *  foreach (var localCustomDLCFolder in job.CustomDLCFolderMapping.Keys)
                 *  {
                 *      if (info.FileName.StartsWith(FilesystemInterposer.PathCombine(IsInArchive, ModPath, localCustomDLCFolder)))
                 *      {
                 *          //Debug.WriteLine("Add file to extraction list: " + info.FileName);
                 *          fileIndicesToExtract.Add(info.Index);
                 *          fileAdded = true;
                 *          break;
                 *      }
                 *  }
                 *
                 *  if (fileAdded) break;
                 *
                 *  //Alternate files
                 *  foreach (var alt in job.AlternateFiles)
                 *  {
                 *      if (alt.AltFile != null && info.FileName.Equals(FilesystemInterposer.PathCombine(IsInArchive, ModPath, alt.AltFile), StringComparison.InvariantCultureIgnoreCase))
                 *      {
                 *          //Debug.WriteLine("Add alternate file to extraction list: " + info.FileName);
                 *          fileIndicesToExtract.Add(info.Index);
                 *          fileAdded = true;
                 *          break;
                 *      }
                 *  }
                 *
                 *  if (fileAdded) break;
                 *
                 *  //Alternate DLC
                 *  foreach (var alt in job.AlternateDLCs)
                 *  {
                 *      if (info.FileName.StartsWith(FilesystemInterposer.PathCombine(IsInArchive, ModPath, alt.AlternateDLCFolder), StringComparison.InvariantCultureIgnoreCase))
                 *      {
                 *          //Debug.WriteLine("Add alternate dlc file to extraction list: " + info.FileName);
                 *          fileIndicesToExtract.Add(info.Index);
                 *          fileAdded = true;
                 *          break;
                 *      }
                 *  }
                 *
                 *  if (fileAdded) break;
                 *
                 #endregion
                 * }
                 * else
                 * {
                 #region Official headers
                 *
                 *  foreach (var inSubDirFile in job.FilesToInstall.Values)
                 *  {
                 *      var inArchivePath = FilesystemInterposer.PathCombine(IsInArchive, ModPath, job.JobDirectory, inSubDirFile); //keep relative if unpacked mod, otherwise use full in-archive path for extraction
                 *      if (info.FileName.Equals(inArchivePath, StringComparison.InvariantCultureIgnoreCase))
                 *      {
                 *          //Debug.WriteLine("Add file to extraction list: " + info.FileName);
                 *          fileIndicesToExtract.Add(info.Index);
                 *          fileAdded = true;
                 *          break;
                 *      }
                 *  }
                 *
                 *  if (fileAdded) break;
                 *  //Alternate files
                 *  foreach (var alt in job.AlternateFiles)
                 *  {
                 *      if (alt.AltFile != null && info.FileName.Equals(FilesystemInterposer.PathCombine(IsInArchive, ModPath, alt.AltFile), StringComparison.InvariantCultureIgnoreCase))
                 *      {
                 *          //Debug.WriteLine("Add alternate file to extraction list: " + info.FileName);
                 *          fileIndicesToExtract.Add(info.Index);
                 *          fileAdded = true;
                 *          break;
                 *      }
                 *  }
                 *
                 *  if (fileAdded) break;
                 *
                 #endregion
                 * }
                 * }
                 * }*/
                #endregion
                archiveFile.Progressing += (sender, args) => { extractingCallback?.Invoke(args); };
                string outputFilePathMapping(ArchiveFileInfo entryInfo)
                {
                    Log.Information(@"Mapping extraction target for " + entryInfo.FileName);

                    string entryPath = entryInfo.FileName;

                    if (ExeExtractionTransform != null && ExeExtractionTransform.PatchRedirects.Any(x => x.index == entryInfo.Index))
                    {
                        Log.Information(@"Extracting vpatch file at index " + entryInfo.Index);
                        return(Path.Combine(Utilities.GetVPatchRedirectsFolder(), ExeExtractionTransform.PatchRedirects.First(x => x.index == entryInfo.Index).outfile));
                    }

                    if (ExeExtractionTransform != null && ExeExtractionTransform.NoExtractIndexes.Any(x => x == entryInfo.Index))
                    {
                        Log.Information(@"Extracting file to trash (not used): " + entryPath);
                        return(Path.Combine(Utilities.GetTempPath(), @"Trash", @"trashfile"));
                    }

                    if (ExeExtractionTransform != null && ExeExtractionTransform.AlternateRedirects.Any(x => x.index == entryInfo.Index))
                    {
                        var outfile = ExeExtractionTransform.AlternateRedirects.First(x => x.index == entryInfo.Index).outfile;
                        Log.Information($@"Extracting file with redirection: {entryPath} {outfile}");
                        return(Path.Combine(outputFolderPath, outfile));
                    }

                    //Archive path might start with a \. Substring may return value that start with a \
                    var subModPath = entryPath /*.TrimStart('\\')*/.Substring(ModPath.Length).TrimStart('\\');
                    var path       = Path.Combine(outputFolderPath, subModPath);


                    //Debug.WriteLine("remapping output: " + entryPath + " -> " + path);
                    return(path);
                }

                if (compressPackages)
                {
                    compressionQueue = new BlockingCollection <string>();
                }

                int numberOfPackagesToCompress = referencedFiles.Count(x => x.RepresentsPackageFilePath());
                int compressedPackageCount     = 0;
                NamedBackgroundWorker compressionThread;
                if (compressPackages)
                {
                    compressionThread         = new NamedBackgroundWorker(@"ImportingCompressionThread");
                    compressionThread.DoWork += (a, b) =>
                    {
                        try
                        {
                            while (true)
                            {
                                var package = compressionQueue.Take();
                                //updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package)));
                                var p = MEPackageHandler.OpenMEPackage(package);
                                //Check if any compressed textures.
                                bool shouldNotCompress = false;
                                foreach (var texture in p.Exports.Where(x => x.IsTexture()))
                                {
                                    var storageType = Texture2D.GetTopMipStorageType(texture);
                                    shouldNotCompress |= storageType == ME3Explorer.Unreal.StorageTypes.pccLZO || storageType == ME3Explorer.Unreal.StorageTypes.pccZlib;
                                    if (!shouldNotCompress)
                                    {
                                        break;
                                    }
                                }

                                if (!shouldNotCompress)
                                {
                                    compressedPackageCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package)), compressedPackageCount, numberOfPackagesToCompress);
                                    Log.Information(@"Compressing package: " + package);
                                    p.save(true);
                                }
                                else
                                {
                                    Log.Information(@"Not compressing package due to file containing compressed textures: " + package);
                                }


                                Interlocked.Increment(ref compressedPackageCount);
                                compressedPackageCallback?.Invoke(M3L.GetString(M3L.string_interp_compressedX, Path.GetFileName(package)), compressedPackageCount, numberOfPackagesToCompress);
                            }
                        }
                        catch (InvalidOperationException)
                        {
                            //Done.
                            lock (compressionCompletedSignaler)
                            {
                                Monitor.Pulse(compressionCompletedSignaler);
                            }
                        }
                    };
                    compressionThread.RunWorkerAsync();
                }
                archiveFile.FileExtractionFinished += (sender, args) =>
                {
                    if (compressPackages)
                    {
                        var fToCompress = outputFilePathMapping(args.FileInfo);
                        if (fToCompress.RepresentsPackageFilePath())
                        {
                            //Debug.WriteLine("Adding to blocking queue");
                            compressionQueue.TryAdd(fToCompress);
                        }
                    }
                };

                if (!testRun)
                {
                    Log.Information(@"Extracting files...");
                    archiveFile.ExtractFiles(outputFolderPath, outputFilePathMapping, fileIndicesToExtract.ToArray());
                }
                else
                {
                    //test run mode
                    if (fileIndicesToExtract.Count != referencedFiles.Count)
                    {
                        throw new Exception("The amount of referenced files does not match the amount of files that are going to be extracted!");
                    }
                }
                Log.Information(@"File extraction completed.");


                compressionQueue?.CompleteAdding();
                if (compressPackages && numberOfPackagesToCompress > 0 && numberOfPackagesToCompress > compressedPackageCount)
                {
                    Log.Information(@"Waiting for compression of packages to complete.");
                    while (!compressionQueue.IsCompleted)
                    {
                        lock (compressionCompletedSignaler)
                        {
                            Monitor.Wait(compressionCompletedSignaler);
                        }
                    }

                    Log.Information(@"Package compression has completed.");
                }
                ModPath = outputFolderPath;
                if (IsVirtualized)
                {
                    var parser = new IniDataParser().Parse(VirtualizedIniText);
                    parser[@"ModInfo"][@"modver"] = ModVersionString; //In event relay service resolved this
                    if (!testRun)
                    {
                        File.WriteAllText(Path.Combine(ModPath, @"moddesc.ini"), parser.ToString());
                    }
                    IsVirtualized = false; //no longer virtualized
                }

                if (ExeExtractionTransform != null)
                {
                    if (ExeExtractionTransform.VPatches.Any())
                    {
                        var vpat = Utilities.GetCachedExecutablePath(@"vpat.exe");
                        if (!testRun)
                        {
                            Utilities.ExtractInternalFile(@"MassEffectModManagerCore.modmanager.executables.vpat.exe", vpat, true);
                        }
                        //Handle VPatching
                        foreach (var transform in ExeExtractionTransform.VPatches)
                        {
                            var patchfile  = Path.Combine(Utilities.GetVPatchRedirectsFolder(), transform.patchfile);
                            var inputfile  = Path.Combine(ModPath, transform.inputfile);
                            var outputfile = Path.Combine(ModPath, transform.outputfile);

                            var args = $"\"{patchfile}\" \"{inputfile}\" \"{outputfile}\""; //do not localize
                            if (!testRun)
                            {
                                Directory.CreateDirectory(Directory.GetParent(outputfile).FullName); //ensure output directory exists as vpatch will not make one.
                            }
                            Log.Information($@"VPatching file into alternate: {inputfile} to {outputfile}");
                            updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_vPatchingIntoAlternate, Path.GetFileName(inputfile)));
                            if (!testRun)
                            {
                                Utilities.RunProcess(vpat, args, true, false, false, true);
                            }
                        }
                    }

                    //Handle copyfile
                    foreach (var copyfile in ExeExtractionTransform.CopyFiles)
                    {
                        string srcfile  = Path.Combine(ModPath, copyfile.inputfile);
                        string destfile = Path.Combine(ModPath, copyfile.outputfile);
                        Log.Information($@"Applying transform copyfile: {srcfile} -> {destfile}");
                        if (!testRun)
                        {
                            File.Copy(srcfile, destfile, true);
                        }
                    }

                    if (ExeExtractionTransform.PostTransformModdesc != null)
                    {
                        //fetch online moddesc for this mod.
                        Log.Information(@"Fetching post-transform third party moddesc.");
                        var moddesc = OnlineContent.FetchThirdPartyModdesc(ExeExtractionTransform.PostTransformModdesc);
                        if (!testRun)
                        {
                            File.WriteAllText(Path.Combine(ModPath, @"moddesc.ini"), moddesc);
                        }
                    }
                }

                //int packagesCompressed = 0;
                //if (compressPackages)
                //{
                //    var packages = Utilities.GetPackagesInDirectory(ModPath, true);
                //    extractingCallback?.Invoke(new ProgressEventArgs((byte)(packagesCompressed * 100.0 / packages.Count), 0));
                //    foreach (var package in packages)
                //    {
                //        updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package)));
                //        Log.Information("Compressing package: " + package);
                //        var p = MEPackageHandler.OpenMEPackage(package);
                //        p.save(true);

                //        packagesCompressed++;
                //        extractingCallback?.Invoke(new ProgressEventArgs((byte)(packagesCompressed * 100.0 / packages.Count), 0));
                //    }
                //}
            }
        }
Example #20
0
 public static byte[] getDefaultClassValue(ME3Package pcc, string className, bool fullProps = false)
 {
     if (Structs.ContainsKey(className))
     {
         bool      immutable = isImmutable(className);
         ClassInfo info      = Structs[className];
         try
         {
             string filepath = (Path.Combine(ME3Directory.gamePath, @"BIOGame\" + info.pccPath));
             if (File.Exists(info.pccPath))
             {
                 filepath = info.pccPath; //Used for dynamic lookup
             }
             using (ME3Package importPCC = MEPackageHandler.OpenME3Package(filepath))
             {
                 byte[] buff;
                 //Plane and CoverReference inherit from other structs, meaning they don't have default values (who knows why)
                 //thus, I have hardcoded what those default values should be
                 if (className == "Plane")
                 {
                     buff = PlaneDefault;
                 }
                 else if (className == "CoverReference")
                 {
                     buff = CoverReferenceDefault;
                 }
                 else
                 {
                     buff = importPCC.Exports[info.exportIndex].Data.Skip(0x24).ToArray();
                 }
                 List <PropertyReader.Property> Props = PropertyReader.ReadProp(importPCC, buff, 0);
                 MemoryStream m = new MemoryStream();
                 foreach (PropertyReader.Property p in Props)
                 {
                     string propName = importPCC.getNameEntry(p.Name);
                     //check if property is transient, if so, skip (neither of the structs that inherit have transient props)
                     if (info.properties.ContainsKey(propName) || propName == "None" || info.baseClass != "Class")
                     {
                         if (immutable && !fullProps)
                         {
                             PropertyReader.ImportImmutableProperty(pcc, importPCC, p, className, m, true);
                         }
                         else
                         {
                             PropertyReader.ImportProperty(pcc, importPCC, p, className, m, true);
                         }
                     }
                 }
                 return(m.ToArray());
             }
         }
         catch (Exception)
         {
             return(null);
         }
     }
     else if (Classes.ContainsKey(className))
     {
         ClassInfo info = Structs[className];
         try
         {
             string filepath = (Path.Combine(ME3Directory.gamePath, @"BIOGame\" + info.pccPath));
             if (File.Exists(info.pccPath))
             {
                 filepath = info.pccPath; //Used for dynamic lookup
             }
             using (ME3Package importPCC = MEPackageHandler.OpenME3Package(filepath))
             {
                 IExportEntry entry = pcc.Exports[info.exportIndex + 1];
                 List <PropertyReader.Property> Props = PropertyReader.getPropList(entry);
                 MemoryStream m = new MemoryStream(entry.DataSize - 4);
                 foreach (PropertyReader.Property p in Props)
                 {
                     if (!info.properties.ContainsKey(importPCC.getNameEntry(p.Name)))
                     {
                         //property is transient
                         continue;
                     }
                     PropertyReader.ImportProperty(pcc, importPCC, p, className, m);
                 }
                 return(m.ToArray());
             }
         }
         catch (Exception)
         {
             return(null);
         }
     }
     return(null);
 }
Example #21
0
        private static void RandomizeAfterlifeShepDance()
        {
            var denDanceF = MERFileSystem.GetPackageFile(@"BioD_OmgHub_230DenDance.pcc");

            if (denDanceF != null)
            {
                var loungeP  = MEPackageHandler.OpenMEPackage(denDanceF);
                var sequence = loungeP.GetUExport(3924);

                MERPackageCache cache = new MERPackageCache();
                List <InterpTools.InterpData> interpDatas = new List <InterpTools.InterpData>();
                var interp1 = loungeP.GetUExport(3813);

                // Make 2 additional dance options by cloning the interp and the data tree
                var interp2 = MERSeqTools.CloneBasicSequenceObject(interp1);
                var interp3 = MERSeqTools.CloneBasicSequenceObject(interp1);


                // Clone the interp data for attaching to 2 and 3
                var interpData1 = loungeP.GetUExport(1174);
                var interpData2 = EntryCloner.CloneTree(interpData1);
                var interpData3 = EntryCloner.CloneTree(interpData2);
                KismetHelper.AddObjectToSequence(interpData2, sequence);
                KismetHelper.AddObjectToSequence(interpData3, sequence);

                // Load ID for randomization
                interpDatas.Add(new InterpTools.InterpData(interpData1));
                interpDatas.Add(new InterpTools.InterpData(interpData2));
                interpDatas.Add(new InterpTools.InterpData(interpData3));


                // Chance the data for interp2/3
                var id2 = SeqTools.GetVariableLinksOfNode(interp2);
                id2[0].LinkedNodes[0] = interpData2;
                SeqTools.WriteVariableLinksToNode(interp2, id2);

                var id3 = SeqTools.GetVariableLinksOfNode(interp3);
                id3[0].LinkedNodes[0] = interpData3;
                SeqTools.WriteVariableLinksToNode(interp3, id3);

                // Add additional finished states for fadetoblack when done
                KismetHelper.CreateOutputLink(loungeP.GetUExport(958), "Finished", interp2, 2);
                KismetHelper.CreateOutputLink(loungeP.GetUExport(958), "Finished", interp3, 2);


                // Link up the random choice it makes
                var randSw = MERSeqTools.InstallRandomSwitchIntoSequence(sequence, 3);
                KismetHelper.CreateOutputLink(randSw, "Link 1", interp1);
                KismetHelper.CreateOutputLink(randSw, "Link 2", interp2);
                KismetHelper.CreateOutputLink(randSw, "Link 3", interp3);

                // Break the original output to start the interp, repoint it's output to the switch instead
                var sgm = loungeP.GetUExport(1003); //set gesture mode
                KismetHelper.RemoveOutputLinks(sgm);
                KismetHelper.CreateOutputLink(sgm, "Done", loungeP.GetUExport(960));
                KismetHelper.CreateOutputLink(sgm, "Done", randSw);

                // Now install the dances
                foreach (var id in interpDatas)
                {
                    var danceTrack = id.InterpGroups[0].Tracks[0];
                    OmegaHub.InstallShepardDanceGesture(danceTrack.Export, cache);
                }

                MERFileSystem.SavePackage(loungeP);
            }
        }
Example #22
0
        private void CompactAFCBackgroundThread(object sender, DoWorkEventArgs e)
        {
            var    arguments      = (Tuple <string, string>)e.Argument;
            string path           = arguments.Item1;
            string NewAFCBaseName = arguments.Item2;

            string[] pccFiles = System.IO.Directory.GetFiles(path, "*.pcc");
            string[] afcFiles = System.IO.Directory.GetFiles(path, "*.afc").Select(x => System.IO.Path.GetFileNameWithoutExtension(x).ToLower()).ToArray();

            var ReferencedAFCAudio = new List <Tuple <string, int, int> >();

            int i = 1;

            foreach (string pccPath in pccFiles)
            {
                BusyText = "Finding all referenced audio (" + i + "/" + pccFiles.Count() + ")";
                using (IMEPackage pack = MEPackageHandler.OpenMEPackage(pccPath))
                {
                    List <IExportEntry> wwiseStreamExports = pack.Exports.Where(x => x.ClassName == "WwiseStream").ToList();
                    foreach (IExportEntry exp in wwiseStreamExports)
                    {
                        var afcNameProp = exp.GetProperty <NameProperty>("Filename");
                        if (afcNameProp != null && afcFiles.Contains(afcNameProp.ToString().ToLower()))
                        {
                            string afcName     = afcNameProp.ToString().ToLower();
                            int    readPos     = exp.Data.Length - 8;
                            int    audioSize   = BitConverter.ToInt32(exp.Data, exp.Data.Length - 8);
                            int    audioOffset = BitConverter.ToInt32(exp.Data, exp.Data.Length - 4);
                            ReferencedAFCAudio.Add(new Tuple <string, int, int>(afcName, audioSize, audioOffset));
                        }
                    }
                }
                i++;
            }
            ReferencedAFCAudio = ReferencedAFCAudio.Distinct().ToList();

            //extract referenced audio
            BusyText = "Extracting referenced audio";
            var extractedAudioMap = new Dictionary <Tuple <string, int, int>, byte[]>();

            i = 1;
            foreach (Tuple <string, int, int> reference in ReferencedAFCAudio)
            {
                BusyText = "Extracting referenced audio (" + i + " / " + ReferencedAFCAudio.Count() + ")";
                string     afcPath = System.IO.Path.Combine(path, reference.Item1 + ".afc");
                FileStream stream  = new FileStream(afcPath, FileMode.Open, FileAccess.Read);
                stream.Seek(reference.Item3, SeekOrigin.Begin);
                byte[] extractedAudio = new byte[reference.Item2];
                stream.Read(extractedAudio, 0, reference.Item2);
                stream.Close();
                extractedAudioMap[reference] = extractedAudio;
                i++;
            }

            var newAFCEntryPointMap = new Dictionary <Tuple <string, int, int>, long>();

            i = 1;
            string newAfcPath = System.IO.Path.Combine(path, NewAFCBaseName + ".afc");

            if (File.Exists(newAfcPath))
            {
                File.Delete(newAfcPath);
            }
            FileStream newAFCStream = new FileStream(newAfcPath, FileMode.CreateNew, FileAccess.Write);

            foreach (Tuple <string, int, int> reference in ReferencedAFCAudio)
            {
                BusyText = "Building new AFC file (" + i + " / " + ReferencedAFCAudio.Count() + ")";
                newAFCEntryPointMap[reference] = newAFCStream.Position; //save entry point in map
                newAFCStream.Write(extractedAudioMap[reference], 0, extractedAudioMap[reference].Length);
                i++;
            }
            newAFCStream.Close();
            extractedAudioMap = null; //clean out ram on next GC

            i = 1;
            foreach (string pccPath in pccFiles)
            {
                BusyText = "Updating audio references (" + i + "/" + pccFiles.Count() + ")";
                using (IMEPackage pack = MEPackageHandler.OpenMEPackage(pccPath))
                {
                    bool shouldSave = false;
                    List <IExportEntry> wwiseStreamExports = pack.Exports.Where(x => x.ClassName == "WwiseStream").ToList();
                    foreach (IExportEntry exp in wwiseStreamExports)
                    {
                        var afcNameProp = exp.GetProperty <NameProperty>("Filename");
                        if (afcNameProp != null && afcFiles.Contains(afcNameProp.ToString().ToLower()))
                        {
                            string afcName     = afcNameProp.ToString().ToLower();
                            int    readPos     = exp.Data.Length - 8;
                            int    audioSize   = BitConverter.ToInt32(exp.Data, exp.Data.Length - 8);
                            int    audioOffset = BitConverter.ToInt32(exp.Data, exp.Data.Length - 4);
                            var    key         = new Tuple <string, int, int>(afcName, audioSize, audioOffset);
                            long   newOffset;
                            if (newAFCEntryPointMap.TryGetValue(key, out newOffset))
                            {
                                //its a match
                                afcNameProp.Value = NewAFCBaseName;
                                Application.Current.Dispatcher.Invoke(() =>
                                {
                                    exp.WriteProperty(afcNameProp);
                                    byte[] newData = exp.Data;
                                    Buffer.BlockCopy(BitConverter.GetBytes((int)newOffset), 0, newData, newData.Length - 4, 4); //update AFC audio offset
                                    exp.Data = newData;
                                    if (exp.DataChanged)
                                    {
                                        //don't mark for saving if the data didn't acutally change (e.g. trying to compact a compacted AFC).
                                        shouldSave = true;
                                    }
                                });
                            }
                        }
                    }
                    if (shouldSave)
                    {
                        Application.Current.Dispatcher.Invoke(
                            () =>
                        {
                            // Must run on the UI thread or the tool interop will throw an exception
                            // because we are on a background thread.
                            pack.save();
                        });
                    }
                }
                i++;
            }
            BusyText = "Rebuild complete";
            System.Threading.Thread.Sleep(2000);
        }
Example #23
0
        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            TreeNode t = treeView1.SelectedNode;

            if (DisplayStyle == 0)
            {
                if (t.Parent == null || t.Name == "")
                {
                    return;
                }
                Renderer.STM?.pcc.Dispose();
                Renderer.STM = null;
                Renderer.SKM?.Owner.Dispose();
                Renderer.SKM         = null;
                Renderer.CamDistance = 10;
                Renderer.CamOffset   = new Microsoft.DirectX.Vector3(0, 0, 0);
                try
                {
                    int i = 0;
                    if (Int32.TryParse(t.Name, out i))
                    {
                        EntryStruct en = Entries[i];
                        if (!en.isDLC)
                        {
                            ME3Package pcc = MEPackageHandler.OpenME3Package(ME3Directory.cookedPath + en.Filename);
                            if (en.isSkeletal)
                            {
                                Renderer.SKM         = new SkeletalMesh(pcc, en.Index);
                                Renderer.CamDistance = Renderer.SKM.Bounding.r * 2.0f;
                                Renderer.CamOffset   = Renderer.SKM.Bounding.origin;
                                treeView2.Nodes.Clear();
                                if (previewWithTreeToolStripMenuItem.Checked)
                                {
                                    treeView2.Visible = false;
                                    Application.DoEvents();
                                    treeView2.Nodes.Add(Renderer.SKM.ToTree());
                                    treeView2.Visible = true;
                                }
                            }
                            else
                            {
                            }
                        }
                        else
                        {
                            string loc    = Path.GetDirectoryName(Application.ExecutablePath) + "\\exec\\";
                            string dirDLC = ME3Directory.DLCPath;
                            dirDLC += en.DLCName;
                            dirDLC += "\\CookedPCConsole\\Default.sfar";
                            DLCBase dlc = new DLCBase(dirDLC);
                            foreach (sfarFile file in dlc.fileList)
                            {
                                try
                                {
                                    string filename = Path.GetFileName(file.fileName);
                                    if (Path.GetExtension(filename).ToLower().EndsWith(".pcc") && filename == en.Filename)
                                    {
                                        using (Stream input = File.OpenRead(dirDLC), output = File.Create(loc + filename))
                                        {
                                            AmaroK86.MassEffect3.DLCUnpack.DecompressEntry(file, input, output, dlc.CompressionScheme);
                                        }
                                        if (File.Exists(loc + filename))
                                        {
                                            try
                                            {
                                                ME3Package pcc = MEPackageHandler.OpenME3Package(loc + filename);
                                                if (en.isSkeletal)
                                                {
                                                    Renderer.SKM         = new SkeletalMesh(pcc, en.Index);
                                                    Renderer.CamDistance = Renderer.SKM.Bounding.r * 2.0f;
                                                    Renderer.CamOffset   = Renderer.SKM.Bounding.origin;
                                                    treeView2.Nodes.Clear();
                                                    if (previewWithTreeToolStripMenuItem.Checked)
                                                    {
                                                        treeView2.Visible = false;
                                                        Application.DoEvents();
                                                        treeView2.Nodes.Add(Renderer.SKM.ToTree());
                                                        treeView2.Visible = true;
                                                    }
                                                }
                                                else
                                                {
                                                }
                                            }
                                            catch (Exception)
                                            {
                                            }
                                            File.Delete(loc + filename);
                                        }
                                    }
                                }
                                catch (Exception)
                                {
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }
        }
        private void CheckModForAFCCompactability(DeploymentChecklistItem item)
        {
            bool hasError = false;

            item.HasError = false;
            item.ItemText = "Checking audio references in mod";
            var           referencedFiles  = ModBeingDeployed.GetAllRelativeReferences().Select(x => Path.Combine(ModBeingDeployed.ModPath, x)).ToList();
            int           numChecked       = 0;
            GameTarget    validationTarget = mainWindow.InstallationTargets.FirstOrDefault(x => x.Game == ModBeingDeployed.Game);
            List <string> gameFiles        = MEDirectories.EnumerateGameFiles(validationTarget.Game, validationTarget.TargetPath);

            var errors = new List <string>();
            Dictionary <string, MemoryStream> cachedAudio = new Dictionary <string, MemoryStream>();

            foreach (var f in referencedFiles)
            {
                if (_closed)
                {
                    return;
                }
                numChecked++;
                item.ItemText = $"Checking audio references in mod [{numChecked}/{referencedFiles.Count}]";
                if (f.RepresentsPackageFilePath())
                {
                    var package      = MEPackageHandler.OpenMEPackage(f);
                    var wwiseStreams = package.Exports.Where(x => x.ClassName == "WwiseStream" && !x.IsDefaultObject).ToList();
                    foreach (var wwisestream in wwiseStreams)
                    {
                        if (_closed)
                        {
                            return;
                        }
                        //Check each reference.
                        var afcNameProp = wwisestream.GetProperty <NameProperty>("Filename");
                        if (afcNameProp != null)
                        {
                            string afcNameWithExtension = afcNameProp + ".afc";
                            int    audioSize            = BitConverter.ToInt32(wwisestream.Data, wwisestream.Data.Length - 8);
                            int    audioOffset          = BitConverter.ToInt32(wwisestream.Data, wwisestream.Data.Length - 4);

                            string afcPath               = null;
                            Stream audioStream           = null;
                            var    localDirectoryAFCPath = Path.Combine(Path.GetDirectoryName(wwisestream.FileRef.FilePath), afcNameWithExtension);
                            bool   isInOfficialArea      = false;
                            if (File.Exists(localDirectoryAFCPath))
                            {
                                //local afc
                                afcPath = localDirectoryAFCPath;
                            }
                            else
                            {
                                //Check game
                                var fullPath = gameFiles.FirstOrDefault(x => Path.GetFileName(x).Equals(afcNameWithExtension, StringComparison.InvariantCultureIgnoreCase));
                                if (fullPath != null)
                                {
                                    afcPath          = fullPath;
                                    isInOfficialArea = MEDirectories.IsInBasegame(afcPath, validationTarget) || MEDirectories.IsInOfficialDLC(afcPath, validationTarget);
                                }
                                else if (cachedAudio.TryGetValue(afcNameProp.Value.Name, out var cachedAudioStream))
                                {
                                    audioStream = cachedAudioStream;
                                    //isInOfficialArea = true; //cached from vanilla SFAR
                                }
                                else if (MEDirectories.OfficialDLC(validationTarget.Game).Any(x => afcNameProp.Value.Name.StartsWith(x)))
                                {
                                    var dlcName = afcNameProp.Value.Name.Substring(0, afcNameProp.Value.Name.LastIndexOf("_", StringComparison.InvariantCultureIgnoreCase));
                                    var audio   = VanillaDatabaseService.FetchFileFromVanillaSFAR(validationTarget, dlcName, afcNameWithExtension);
                                    if (audio != null)
                                    {
                                        cachedAudio[afcNameProp.Value.Name] = audio;
                                    }

                                    audioStream = audio;
                                    //isInOfficialArea = true; as this is in a vanilla SFAR we don't test against this since it will be correct.
                                    continue;
                                }
                                else
                                {
                                    hasError        = true;
                                    item.Icon       = FontAwesomeIcon.TimesCircle;
                                    item.Foreground = Brushes.Red;
                                    item.Spinning   = false;
                                    errors.Add($"{wwisestream.FileRef.FilePath} - {wwisestream.GetInstancedFullPath}: Could not find referenced audio file cache: {afcNameProp}");
                                    continue;
                                }
                            }

                            if (afcPath != null)
                            {
                                audioStream = new FileStream(afcPath, FileMode.Open);
                            }

                            try
                            {
                                audioStream.Seek(audioOffset, SeekOrigin.Begin);
                                if (audioStream.ReadStringASCIINull(4) != "RIFF")
                                {
                                    hasError        = true;
                                    item.Icon       = FontAwesomeIcon.TimesCircle;
                                    item.Foreground = Brushes.Red;
                                    item.Spinning   = false;
                                    errors.Add($"{wwisestream.FileRef.FilePath} - {wwisestream.GetInstancedFullPath}: Invalid audio pointer, does not point to RIFF header");
                                    if (audioStream is FileStream)
                                    {
                                        audioStream.Close();
                                    }
                                    continue;
                                }

                                //attempt to seek audio length.
                                audioStream.Seek(audioSize + 4, SeekOrigin.Current);

                                //Check if this file is in basegame
                                if (isInOfficialArea)
                                {
                                    //Verify offset is not greater than vanilla size
                                    var vanillaInfo = VanillaDatabaseService.GetVanillaFileInfo(validationTarget, afcPath.Substring(validationTarget.TargetPath.Length + 1));
                                    if (vanillaInfo == null)
                                    {
                                        Crashes.TrackError(new Exception("Vanilla information was null when performing vanilla file check for " + afcPath.Substring(validationTarget.TargetPath.Length + 1)));
                                    }
                                    if (audioOffset >= vanillaInfo[0].size)
                                    {
                                        hasError        = true;
                                        item.Icon       = FontAwesomeIcon.TimesCircle;
                                        item.Foreground = Brushes.Red;
                                        item.Spinning   = false;
                                        errors.Add($"{wwisestream.FileRef.FilePath} - {wwisestream.GetInstancedFullPath}: This audio is in a basegame/DLC AFC but is not vanilla audio. End-users will not have this audio available. Use AFC compactor in ME3Explorer to pull this audio locally into an AFC for this mod.");
                                    }
                                }
                                if (audioStream is FileStream)
                                {
                                    audioStream.Close();
                                }
                            }
                            catch (Exception e)
                            {
                                hasError        = true;
                                item.Icon       = FontAwesomeIcon.TimesCircle;
                                item.Foreground = Brushes.Red;
                                item.Spinning   = false;
                                errors.Add($"{wwisestream.FileRef.FilePath} - {wwisestream.GetInstancedFullPath}: Error validating audio reference: {e.Message}");
                                continue;
                            }
                        }
                    }
                }
            }


            if (!hasError)
            {
                item.Foreground = Brushes.Green;
                item.Icon       = FontAwesomeIcon.CheckCircle;
                item.ItemText   = "No audio issues were detected";
                item.ToolTip    = "Validation passed";
            }
            else
            {
                item.Errors   = errors;
                item.ItemText = "Audio issues were detected";
                item.ToolTip  = "Validation failed";
            }
            item.HasError = hasError;
            cachedAudio.Clear();
        }
Example #25
0
        private void importLODToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ME3Package pcc = null;

            try
            {
                int n = listBox1.SelectedIndex;
                if (n == -1)
                {
                    return;
                }
                int m = listBox2.SelectedIndex;
                if (m == -1)
                {
                    return;
                }
                TreeNode t1 = treeView1.SelectedNode;
                if (t1 == null || t1.Parent == null || t1.Name == "")
                {
                    return;
                }
                SkeletalMesh skm = new SkeletalMesh();
                EntryStruct  en;
                string       loc = Path.GetDirectoryName(Application.ExecutablePath) + "\\exec\\";
                if (DisplayStyle == 0)
                {
                    int o = 0;
                    if (!Int32.TryParse(t1.Name, out o))
                    {
                        return;
                    }
                    en = Entries[o];
                    if (!en.isDLC)
                    {
                        if (en.isSkeletal)
                        {
                            pcc = MEPackageHandler.OpenME3Package(ME3Directory.cookedPath + en.Filename);
                            skm = new SkeletalMesh(pcc, en.Index);
                        }
                        else
                        {
                            return;
                        }
                    }
                    else
                    {
                        string dirDLC = ME3Directory.DLCPath;
                        dirDLC += en.DLCName;
                        dirDLC += "\\CookedPCConsole\\Default.sfar";
                        DLCBase dlc = new DLCBase(dirDLC);
                        foreach (sfarFile file in dlc.fileList)
                        {
                            try
                            {
                                string filename = Path.GetFileName(file.fileName);
                                if (Path.GetExtension(filename).ToLower().EndsWith(".pcc") && filename == en.Filename)
                                {
                                    if (File.Exists(loc + "dlc.pcc"))
                                    {
                                        File.Delete(loc + "dlc.pcc");
                                    }
                                    using (Stream input = File.OpenRead(dirDLC), output = File.Create(loc + "dlc.pcc"))
                                    {
                                        AmaroK86.MassEffect3.DLCUnpack.DecompressEntry(file, input, output, dlc.CompressionScheme);
                                    }
                                    if (File.Exists(loc + "dlc.pcc"))
                                    {
                                        try
                                        {
                                            if (en.isSkeletal)
                                            {
                                                pcc = MEPackageHandler.OpenME3Package(loc + "dlc.pcc");
                                                skm = new SkeletalMesh(pcc, en.Index);
                                                break;
                                            }
                                            else
                                            {
                                                return;
                                            }
                                        }
                                        catch (Exception)
                                        {
                                            return;
                                        }
                                    }
                                }
                            }
                            catch (Exception)
                            {
                                return;
                            }
                        }
                    }
                }
                else
                {
                    return;
                }
                if (!skm.Loaded || pcc == null)
                {
                    return;
                }
                SkeletalMesh.LODModelStruct          lodpcc = skm.LODModels[0];
                UDKExplorer.UDK.Classes.SkeletalMesh skmudk = new UDKExplorer.UDK.Classes.SkeletalMesh(udk, Objects[n]);
                UDKExplorer.UDK.Classes.SkeletalMesh.LODModelStruct lodudk = skmudk.LODModels[m];
                lodpcc.Sections = new List <SkeletalMesh.SectionStruct>();
                foreach (UDKExplorer.UDK.Classes.SkeletalMesh.SectionStruct secudk in lodudk.Sections)
                {
                    SkeletalMesh.SectionStruct secpcc = new SkeletalMesh.SectionStruct();
                    secpcc.BaseIndex     = secudk.BaseIndex;
                    secpcc.ChunkIndex    = secudk.ChunkIndex;
                    secpcc.MaterialIndex = secudk.MaterialIndex;
                    secpcc.NumTriangles  = secudk.NumTriangles;
                    lodpcc.Sections.Add(secpcc);
                }
                lodpcc.IndexBuffer            = new SkeletalMesh.MultiSizeIndexContainerStruct();
                lodpcc.IndexBuffer.IndexCount = lodudk.IndexBuffer.IndexCount;
                lodpcc.IndexBuffer.IndexSize  = lodudk.IndexBuffer.IndexSize;
                lodpcc.IndexBuffer.Indexes    = new List <ushort>();
                foreach (ushort Idx in lodudk.IndexBuffer.Indexes)
                {
                    lodpcc.IndexBuffer.Indexes.Add(Idx);
                }
                List <int> BoneMap = new List <int>();
                for (int i = 0; i < skmudk.Bones.Count; i++)
                {
                    string udkb  = udk.GetName(skmudk.Bones[i].Name);
                    bool   found = false;
                    for (int j = 0; j < skm.Bones.Count; j++)
                    {
                        string pccb = pcc.getNameEntry(skm.Bones[j].Name);
                        if (pccb == udkb)
                        {
                            found = true;
                            BoneMap.Add(j);
                            if (importBonesToolStripMenuItem.Checked)
                            {
                                SkeletalMesh.BoneStruct bpcc = skm.Bones[j];
                                UDKExplorer.UDK.Classes.SkeletalMesh.BoneStruct budk = skmudk.Bones[i];
                                bpcc.Orientation = budk.Orientation;
                                bpcc.Position    = budk.Position;
                                skm.Bones[j]     = bpcc;
                            }
                        }
                    }
                    if (!found)
                    {
                        DebugOutput.PrintLn("ERROR: Cant Match Bone \"" + udkb + "\"");
                        BoneMap.Add(0);
                    }
                }

                lodpcc.ActiveBones = new List <ushort>();
                foreach (ushort Idx in lodudk.ActiveBones)
                {
                    lodpcc.ActiveBones.Add((ushort)BoneMap[Idx]);
                }
                lodpcc.Chunks = new List <SkeletalMesh.SkelMeshChunkStruct>();
                foreach (UDKExplorer.UDK.Classes.SkeletalMesh.SkelMeshChunkStruct chunkudk in lodudk.Chunks)
                {
                    SkeletalMesh.SkelMeshChunkStruct chunkpcc = new SkeletalMesh.SkelMeshChunkStruct();
                    chunkpcc.BaseVertexIndex   = chunkudk.BaseVertexIndex;
                    chunkpcc.MaxBoneInfluences = chunkudk.MaxBoneInfluences;
                    chunkpcc.NumRigidVertices  = chunkudk.NumRigidVertices;
                    chunkpcc.NumSoftVertices   = chunkudk.NumSoftVertices;
                    chunkpcc.BoneMap           = new List <ushort>();
                    chunkpcc.RiginSkinVertices = new List <SkeletalMesh.RigidSkinVertexStruct>();
                    chunkpcc.SoftSkinVertices  = new List <SkeletalMesh.SoftSkinVertexStruct>();
                    foreach (ushort Idx in chunkudk.BoneMap)
                    {
                        chunkpcc.BoneMap.Add((ushort)BoneMap[Idx]);
                    }
                    lodpcc.Chunks.Add(chunkpcc);
                }
                lodpcc.Size          = lodudk.Size;
                lodpcc.NumVertices   = lodudk.NumVertices;
                lodpcc.RequiredBones = new List <byte>();
                foreach (byte b in lodudk.RequiredBones)
                {
                    lodpcc.RequiredBones.Add(b);
                }
                lodpcc.VertexBufferGPUSkin = new SkeletalMesh.VertexBufferGPUSkinStruct();
                lodpcc.VertexBufferGPUSkin.NumTexCoords = lodudk.VertexBufferGPUSkin.NumTexCoords;
                lodpcc.VertexBufferGPUSkin.Extension    = lodudk.VertexBufferGPUSkin.Extension;
                lodpcc.VertexBufferGPUSkin.Origin       = lodudk.VertexBufferGPUSkin.Origin;
                lodpcc.VertexBufferGPUSkin.VertexSize   = lodudk.VertexBufferGPUSkin.VertexSize;
                lodpcc.VertexBufferGPUSkin.Vertices     = new List <SkeletalMesh.GPUSkinVertexStruct>();
                foreach (UDKExplorer.UDK.Classes.SkeletalMesh.GPUSkinVertexStruct vudk in lodudk.VertexBufferGPUSkin.Vertices)
                {
                    SkeletalMesh.GPUSkinVertexStruct vpcc = new SkeletalMesh.GPUSkinVertexStruct();
                    vpcc.TangentX         = vudk.TangentX;
                    vpcc.TangentZ         = vudk.TangentZ;
                    vpcc.Position         = vudk.Position;
                    vpcc.InfluenceBones   = vudk.InfluenceBones;
                    vpcc.InfluenceWeights = vudk.InfluenceWeights;
                    vpcc.U = vudk.U;
                    vpcc.V = vudk.V;
                    lodpcc.VertexBufferGPUSkin.Vertices.Add(vpcc);
                }
                for (int i = 0; i < skm.LODModels.Count; i++)
                {
                    skm.LODModels[i] = lodpcc;
                }
                SerializingContainer con = new SerializingContainer();
                con.Memory    = new MemoryStream();
                con.isLoading = false;
                skm.Serialize(con);
                int          end = skm.GetPropertyEnd();
                MemoryStream mem = new MemoryStream();
                mem.Write(pcc.Exports[en.Index].Data, 0, end);
                mem.Write(con.Memory.ToArray(), 0, (int)con.Memory.Length);
                pcc.Exports[en.Index].Data = mem.ToArray();
                pcc.save();
                if (!en.isDLC)
                {
                    MessageBox.Show("Done");
                }
                else
                {
                    MessageBox.Show("Done. The file is now in following folder, please replace it back to DLC :\n" + loc + "dlc.pcc");
                }
                globalTreeToolStripMenuItem.Visible       =
                    optionsToolStripMenuItem.Visible      =
                        transferToolStripMenuItem.Visible =
                            splitContainer1.Visible       = true;
                fileToolStripMenuItem.Visible             =
                    importLODToolStripMenuItem.Visible    =
                        splitContainer3.Visible           = false;
            }
            finally
            {
                pcc?.Dispose();
            }
        }
        private void CheckModForTFCCompactability(DeploymentChecklistItem item)
        {
            // if (ModBeingDeployed.Game >= Mod.MEGame.ME2)
            //{
            bool hasError = false;

            item.HasError = false;
            item.ItemText = "Checking textures in mod";
            var        referencedFiles  = ModBeingDeployed.GetAllRelativeReferences().Select(x => Path.Combine(ModBeingDeployed.ModPath, x)).ToList();
            int        numChecked       = 0;
            GameTarget validationTarget = mainWindow.InstallationTargets.FirstOrDefault(x => x.Game == ModBeingDeployed.Game);
            var        errors           = new List <string>();

            foreach (var f in referencedFiles)
            {
                if (_closed)
                {
                    return;
                }
                numChecked++;
                item.ItemText = $"Checking textures in mod [{numChecked}/{referencedFiles.Count}]";
                if (f.RepresentsPackageFilePath())
                {
                    var package  = MEPackageHandler.OpenMEPackage(f);
                    var textures = package.Exports.Where(x => x.IsTexture()).ToList();
                    foreach (var texture in textures)
                    {
                        if (_closed)
                        {
                            return;
                        }
                        var cache = texture.GetProperty <NameProperty>("TextureFileCacheName");
                        if (cache != null)
                        {
                            if (!VanillaDatabaseService.IsBasegameTFCName(cache.Value, ModBeingDeployed.Game))
                            {
                                var       mips = Texture2D.GetTexture2DMipInfos(texture, cache.Value);
                                Texture2D tex  = new Texture2D(texture);
                                try
                                {
                                    tex.GetImageBytesForMip(tex.GetTopMip(), validationTarget, false); //use active target
                                }
                                catch (Exception e)
                                {
                                    hasError        = true;
                                    item.Icon       = FontAwesomeIcon.TimesCircle;
                                    item.Foreground = Brushes.Red;
                                    item.Spinning   = false;
                                    errors.Add($"{texture.FileRef.FilePath} - {texture.GetInstancedFullPath}: Could not load texture data: {e.Message}");
                                }
                            }
                        }
                    }
                }
            }

            if (!hasError)
            {
                item.Foreground = Brushes.Green;
                item.Icon       = FontAwesomeIcon.CheckCircle;
                item.ItemText   = "No broken textures were found";
                item.ToolTip    = "Validation passed";
            }
            else
            {
                item.Errors   = errors;
                item.ItemText = "Texture issues were detected";
                item.ToolTip  = "Validation failed";
            }
            item.HasError = hasError;
            //}
            //} else
            //{
            //    item.Foreground = Brushes.Green;
            //    item.Icon = FontAwesomeIcon.CheckCircle;
            //    item.ItemText = "Textures are not ";
            //    item.ToolTip = "Validation passed";
            //}
        }
Example #27
0
        private void startScanToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(ME3Directory.cookedPath))
            {
                MessageBox.Show("This functionality requires ME3 to be installed. Set its path at:\n Options > Set Custom Path > Mass Effect 3");
                return;
            }
            string path = ME3Directory.cookedPath;

            string[] files = Directory.GetFiles(path, "*.pcc");
            pb1.Maximum = files.Length;
            DebugOutput.Clear();
            database = new List <DBEntry>();
            int count = 0;

            foreach (string file in files)
            {
                pb1.Value = count++;
                DebugOutput.PrintLn("Scanning file : " + Path.GetFileName(file) + " ...");
                try
                {
                    using (ME3Package pcc = MEPackageHandler.OpenME3Package(file))
                    {
                        DBEntry ent = new DBEntry();
                        ent.filename = Path.GetFileName(file);
                        ent.Objects  = new List <ObjInf>();
                        IReadOnlyList <IExportEntry> Exports = pcc.Exports;
                        for (int i = 0; i < Exports.Count; i++)
                        {
                            IExportEntry ex = Exports[i];
                            ObjInf       obj;
                            switch (ex.ClassName)
                            {
                            case "StaticMesh":
                                obj       = new ObjInf();
                                obj.Index = i;
                                obj.Type  = 0;
                                obj.name  = ex.ObjectName;
                                ent.Objects.Add(obj);
                                break;

                            case "SkeletalMesh":
                                obj       = new ObjInf();
                                obj.Index = i;
                                obj.Type  = 1;
                                obj.name  = ex.ObjectName;
                                ent.Objects.Add(obj);
                                break;
                            }
                        }
                        if (ent.Objects.Count != 0)
                        {
                            DebugOutput.PrintLn("Found " + ent.Objects.Count + " Objects:", false);
                            //foreach (ObjInf o in ent.Objects)
                            //    DebugOutput.PrintLn("\t" + o.Index + " : " + o.name + " (" + TypeToString(o.Type) + ")", false);
                            //DebugOutput.Update();
                            database.Add(ent);
                        }
                        else
                        {
                            DebugOutput.PrintLn("Nothing...", false);
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error:\n" + ex.Message);
                    DebugOutput.PrintLn("Could not open file : " + Path.GetFileName(file));
                }
            }
            RefreshLists();
            pb1.Value = 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();
            }
        }
        private void StartGuiCompatibilityScanner()
        {
            NamedBackgroundWorker bw = new NamedBackgroundWorker(@"GUICompatibilityScanner");

            bw.DoWork += (a, b) =>
            {
                Percent         = 0;
                ActionString    = M3L.GetString(M3L.string_preparingCompatGenerator);
                ActionSubstring = M3L.GetString(M3L.string_pleaseWait);
                var installedDLCMods = VanillaDatabaseService.GetInstalledDLCMods(target);
                var numTotalDLCMods  = installedDLCMods.Count;
                var uiModInstalled   = installedDLCMods.Intersect(DLCUIModFolderNames).Any();
                var dlcRoot          = MEDirectories.DLCPath(target);
                if (uiModInstalled)
                {
                    var nonUIinstalledDLCMods = installedDLCMods.Except(DLCUIModFolderNamesIncludingPatch).ToList();

                    if (nonUIinstalledDLCMods.Count < numTotalDLCMods && nonUIinstalledDLCMods.Count > 0)
                    {
                        //Get UI library
                        bool xbxLibrary       = installedDLCMods.Contains(@"DLC_CON_XBX");
                        bool uiscalinglibrary = installedDLCMods.Contains(@"DLC_CON_UIScaling");
                        if (!xbxLibrary && !uiscalinglibrary)
                        {
                            uiscalinglibrary = installedDLCMods.Contains(@"DLC_CON_UIScaling_Shared");
                        }
                        if (xbxLibrary && uiscalinglibrary)
                        {
                            //can't have both! Not supported.
                            Application.Current.Dispatcher.Invoke(delegate
                            {
                                Log.Error(@"Cannot make compat pack: Both ISM and SP Controller are installed, this is not supported.");
                                M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogCannotGenerateCompatPackInvalidConfig), M3L.GetString(M3L.string_invalidConfiguration), MessageBoxButton.OK, MessageBoxImage.Error);
                                OnClosing(DataEventArgs.Empty);
                            });
                            b.Result = GUICompatibilityThreadResult.INVALID_UI_MOD_CONFIG;
                            return;
                        }

                        void progressCallback(long done, long total)
                        {
                            ActionString    = M3L.GetString(M3L.string_downloadingUiLibrary);
                            ActionSubstring = xbxLibrary ? @"DLC_CON_XBX" : @"DLC_CON_UIScaling";
                            Percent         = getPercent(done, total);
                        }

                        var uiLibraryPath = GetUILibraryPath(xbxLibrary ? @"DLC_CON_XBX" : @"DLC_CON_UIScaling", true, progressCallback);
                        if (uiLibraryPath == null)
                        {
                            Log.Error(@"Required UI library could not be downloaded.");
                            Application.Current.Dispatcher.Invoke(delegate
                            {
                                M3L.ShowDialog(window, M3L.GetString(M3L.string_cannotGeneratorCompatPackCouldNotDownload), M3L.GetString(M3L.string_couldNotAcquireUiLibrary), MessageBoxButton.OK, MessageBoxImage.Error);
                                OnClosing(DataEventArgs.Empty);
                            });
                            b.Result = GUICompatibilityThreadResult.NO_UI_LIBRARY;
                            return;
                        }

                        //Open UI library
                        SevenZipExtractor libraryArchive = new SevenZipExtractor(uiLibraryPath);
                        List <string>     libraryGUIs    = libraryArchive.ArchiveFileData.Where(x => !x.IsDirectory).Select(x => x.FileName.Substring(Path.GetFileNameWithoutExtension(uiLibraryPath).Length + 1)).Select(x => x.Substring(0, x.Length - 4)).ToList(); //remove / on end too

                        //We have UI mod(s) installed and at least one other DLC mod.
                        var supercedanceList = getFileSupercedances().Where(x => x.Value.Any(x => !DLCUIModFolderNamesIncludingPatch.Contains(x))).ToDictionary(p => p.Key, p => p.Value);

                        //Find GUIs

                        ConcurrentDictionary <string, string> filesToBePatched = new ConcurrentDictionary <string, string>(); //Dictionary because there is no ConcurrentList. Keys and values are idenitcal.
                        ActionString    = M3L.GetString(M3L.string_scanningForGuiExports);
                        ActionSubstring = M3L.GetString(M3L.string_pleaseWait);
                        Percent         = 0;
                        int    done         = 0;
                        string singlesuffix = M3L.GetString(M3L.string_singularFile);
                        string pluralsuffix = M3L.GetString(M3L.string_pluralFiles);
                        Parallel.ForEach(supercedanceList, new ParallelOptions()
                        {
                            MaxDegreeOfParallelism = 4
                        }, (pair) =>
                        {
                            var firstNonUIModDlc = pair.Value.FirstOrDefault(x => !DLCUIModFolderNamesIncludingPatch.Contains(x));

                            if (firstNonUIModDlc != null)
                            {
                                //Scan file.
                                var packagefile = Path.Combine(dlcRoot, firstNonUIModDlc, target.Game == MEGame.ME3 ? @"CookedPCConsole" : @"CookedPC", pair.Key);
                                Log.Information(@"Scanning file for GFXMovieInfo exports: " + packagefile);
                                if (!File.Exists(packagefile))
                                {
                                    throw new Exception($@"Package file for inspecting GUIs in was not found: {packagefile}");
                                }
                                var package    = MEPackageHandler.OpenMEPackage(packagefile);
                                var guiExports = package.Exports.Where(x => !x.IsDefaultObject && x.ClassName == @"GFxMovieInfo").ToList();
                                if (guiExports.Count > 0)
                                {
                                    //potential item needing replacement
                                    //Check GUI library to see if we have anything.
                                    foreach (var export in guiExports)
                                    {
                                        if (libraryGUIs.Contains(export.GetFullPath, StringComparer.InvariantCultureIgnoreCase))
                                        {
                                            //match
                                            filesToBePatched[packagefile] = packagefile;
                                            ActionSubstring = M3L.GetString(M3L.string_interp_XFilesNeedToBePatched, filesToBePatched.Count.ToString());
                                            Log.Information($@"{firstNonUIModDlc} {pair.Key} has GUI export that is in UI library, marking for patching. Trigger: {export.GetFullPath}");
                                            break;
                                        }
                                    }
                                }
                            }
                            Interlocked.Increment(ref done);
                            Percent = getPercent(done, supercedanceList.Count);
                        });

                        if (filesToBePatched.Count > 0)
                        {
                            Log.Information(@"A GUI compatibility patch is required for this game configuration");
                            b.Result = GUICompatibilityThreadResult.REQUIRED;
                            var generatedMod = GenerateCompatibilityPackForFiles(nonUIinstalledDLCMods, filesToBePatched.Keys.ToList(), libraryArchive);
                            b.Result = GUICompatibilityThreadResult.GENERATED_PACK;
                            Application.Current.Dispatcher.Invoke(delegate { ((MainWindow)window).LoadMods(generatedMod); }); //reload to this mod
                            return;
                        }
                    }

                    Log.Information(@"A GUI compatibility patch is not required for this game configuration");
                    b.Result = GUICompatibilityThreadResult.NOT_REQUIRED;
                }
                else
                {
                    Log.Information(@"No UI mods are installed - no GUI compatibility pack required");
                    b.Result = GUICompatibilityThreadResult.NO_UI_MODS_INSTALLED;
                }
            };
            bw.RunWorkerCompleted += (a, b) =>
            {
                if (b.Result is GUICompatibilityThreadResult gctr)
                {
                    Analytics.TrackEvent(@"Generated a UI compatibility pack", new Dictionary <string, string>()
                    {
                        { @"Result", gctr.ToString() }
                    });
                    OnClosing(DataEventArgs.Empty);
                    if (gctr == GUICompatibilityThreadResult.NOT_REQUIRED)
                    {
                        M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_dialogNoCompatPackRequired), M3L.GetString(M3L.string_noCompatPackRequired), MessageBoxButton.OK);
                    }
                }
                else
                {
                    throw new Exception(@"GUI Compatibility generator thread did not return a result! Please report this to ME3Tweaks");
                }
            };
            bw.RunWorkerAsync();
        }
        public static bool PortPawnIntoPackage(PortablePawn pawn, IMEPackage targetPackage)
        {
            if (IsPawnAssetInPackageAlready(pawn, targetPackage))
            {
                return(true); // Pawn asset to port in already ported in
            }

            IMEPackage pawnPackage = null;

            if (pawn.IsCorrectedPackage)
            {
                // DEBUG
                //if (pawn.PackageFilename == "BioPawn_Collector_Batarian.pcc")
                //{
                //    pawnPackage = MEPackageHandler.OpenMEPackage(@"C:\Users\mgame\source\repos\ME2Randomizer\ME2Randomizer\staticfiles\binary\correctedpawns\" + pawn.PackageFilename);
                //}
                //else
                //{
                var correctedPawnData = MERUtilities.GetEmbeddedStaticFilesBinaryFile($"correctedpawns.{pawn.PackageFilename}");
                pawnPackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(correctedPawnData));
                //}
            }
            else
            {
                var pF = MERFileSystem.GetPackageFile(pawn.PackageFilename);
                if (pF != null)
                {
                    pawnPackage = MERFileSystem.OpenMEPackage(pF);
                }
                else
                {
                    Debug.WriteLine("Pawn package not found: {pawn.PackageFilename}");
                }
            }

            if (pawnPackage != null)
            {
                PackageTools.PortExportIntoPackage(targetPackage, pawnPackage.FindExport(pawn.AssetToPortIn), useMemorySafeImport: !pawn.IsCorrectedPackage);

                // Ensure the assets are too as they may not be directly referenced except in the level instance
                foreach (var asset in pawn.AssetPaths)
                {
                    if (targetPackage.FindExport(asset) == null)
                    {
                        PackageTools.PortExportIntoPackage(targetPackage, pawnPackage.FindExport(asset), useMemorySafeImport: !pawn.IsCorrectedPackage);
                    }
                }

                if (pawn.TextureUpdates != null)
                {
                    foreach (var tu in pawn.TextureUpdates)
                    {
                        var targetTextureExp = targetPackage.FindExport(tu.TextureInstancedFullPath);
                        TFCBuilder.InstallTexture(tu, targetTextureExp);
                    }
                }

                return(true);
            }
            return(false);
        }