예제 #1
0
        public int FindNameOrAdd(string name)
        {
            if (nameLookupTable.TryGetValue(name, out var index))
            {
                return(index);
            }

            addName(name, true); //Don't bother doing a lookup as we just did one.
            // If this was an issue it'd be a multithreading issue that still could occur and is an
            // issue in the user code
            return(names.Count - 1);
        }
예제 #2
0
        private Dictionary <string, List <string> > getFileSupercedances()
        {
            Mod.MEGame game = target.Game;
            //make dictionary from basegame files
            var fileListMapping = new CaseInsensitiveDictionary <List <string> >();
            var directories     = MELoadedFiles.GetEnabledDLC(target).OrderBy(dir => MELoadedFiles.GetMountPriority(dir, target.Game));

            foreach (string directory in directories)
            {
                var dlc = Path.GetFileName(directory);
                if (MEDirectories.OfficialDLC(target.Game).Contains(dlc))
                {
                    continue;                                                       //skip
                }
                foreach (string filePath in MELoadedFiles.GetCookedFiles(target.Game, directory, false))
                {
                    string fileName = Path.GetFileName(filePath);
                    if (fileName != null && fileName.RepresentsPackageFilePath())
                    {
                        if (fileListMapping.TryGetValue(fileName, out var supercedingList))
                        {
                            supercedingList.Insert(0, dlc);
                        }
                        else
                        {
                            fileListMapping[fileName] = new List <string>(new[] { dlc });
                        }
                    }
                }
            }

            return(fileListMapping);
        }
        private static NameWrapper AddNameToUniqueStrings(string name, Dictionary <string, string> dontShortenStrings,
                                                          CaseInsensitiveDictionary <StringWrapper> uniqueStrings)
        {
            NameWrapper nameWrapper = new NameWrapper();

            List <string> nameStrings = StringUtils.SplitAndReallyRemoveEmptyEntries(name, Utils.NAME_SEPARATOR, StringComparison.OrdinalIgnoreCase);

            foreach (string nameString in nameStrings)
            {
                bool          canShorten = !dontShortenStrings.ContainsKey(nameString);
                StringWrapper stringWrapper;
                if (uniqueStrings.TryGetValue(nameString, out stringWrapper))
                {
                    DebugUtils.AssertDebuggerBreak(stringWrapper.CanShorten == canShorten);
                }
                else
                {
                    stringWrapper = new StringWrapper(nameString, canShorten);
                    uniqueStrings.Add(nameString, stringWrapper);
                }
                nameWrapper.AddString(stringWrapper);
            }

            return(nameWrapper);
        }
예제 #4
0
        /// <summary>
        /// Gets a list of superceding package files from the DLC of the game. Only files in DLC mods are returned
        /// </summary>
        /// <param name="target">Target to get supercedances for</param>
        /// <returns>Dictionary mapping filename to list of DLCs that contain that file, in order of highest priority to lowest</returns>
        public static Dictionary <string, List <string> > GetFileSupercedances(GameTarget target, string[] additionalExtensionsToInclude = null)
        {
            //make dictionary from basegame files
            var fileListMapping = new CaseInsensitiveDictionary <List <string> >();
            var directories     = MELoadedFiles.GetEnabledDLCFolders(target.Game, target.TargetPath).OrderBy(dir => MELoadedFiles.GetMountPriority(dir, target.Game)).ToList();

            foreach (string directory in directories)
            {
                var dlc = Path.GetFileName(directory);
                if (MEDirectories.OfficialDLC(target.Game).Contains(dlc))
                {
                    continue;                                                       //skip
                }
                foreach (string filePath in MELoadedFiles.GetCookedFiles(target.Game, directory, false, additionalExtensions: additionalExtensionsToInclude))
                {
                    string fileName = Path.GetFileName(filePath);
                    if (fileName != null && fileName.RepresentsPackageFilePath() || (additionalExtensionsToInclude != null && additionalExtensionsToInclude.Contains(Path.GetExtension(fileName))))
                    {
                        if (fileListMapping.TryGetValue(fileName, out var supercedingList))
                        {
                            supercedingList.Insert(0, dlc);
                        }
                        else
                        {
                            fileListMapping[fileName] = new List <string>(new[] { dlc });
                        }
                    }
                }
            }

            return(fileListMapping);
        }
예제 #5
0
        private static Log GetLogger(string name = null)
        {
            var logName = name ?? Assembly.GetCallingAssembly().GetName().Name;
            Log log;

            lock (LoggerLock)
            {
                if (Loggers.TryGetValue(logName, out log))
                {
                    return(log);
                }

                log = new Log(logName);
                Loggers.Add(logName, log);
            }
            return(log);
        }
예제 #6
0
        public TestMessageResult DoAllMessageValuesMatchDataTypes(CaseInsensitiveDictionary <object> messageKeyDictionary,
                                                                  CaseInsensitiveDictionary <SchemaObject> contractDictionary, bool allowSubset = false)
        {
            var testMessageResult = new TestMessageResult();

            // loop through each property in the message Dictionary - trying to parse the values into the data type in the contract
            foreach (KeyValuePair <string, object> kv in messageKeyDictionary)
            {
                // if the contract doesn't supply the datatype for the parameter -> then the message doesn't match the contract.
                contractDictionary.TryGetValue(kv.Key, out var propertySchemaObject);

                if (propertySchemaObject != null)
                {
                    ChecksForMessage(propertySchemaObject, kv, testMessageResult, allowSubset);
                }
            }

            return(testMessageResult);
        }
예제 #7
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);

            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);

            ActionSubstring = M3L.GetString(M3L.string_interp_patchedXY, done.ToString(), done == 1 ? singlesuffix : pluralsuffix);
            foreach (var file in filesToBePatched)
            {
                var package    = MEPackageHandler.OpenMEPackage(file);
                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))
                        {
                            //Patching this export.
                            var exportProperties = export.GetProperties();
                            var rawData          = exportProperties.GetProp <ArrayProperty <ByteProperty> >(@"RawData");
                            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);
                        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.");
                }
            }

            return(generatedMod);
        }
예제 #8
0
        public void ApplyChanges(GameTarget gameTarget, CaseInsensitiveDictionary <string> loadedFiles, Mod associatedMod, ref int numMergesCompleted, int numTotalMerges, Action <int, int, string, string> mergeProgressDelegate = null)
        {
            List <string> targetFiles = new List <string>();

            if (ApplyToAllLocalizations)
            {
                var targetnameBase  = Path.GetFileNameWithoutExtension(FileName);
                var targetExtension = Path.GetExtension(FileName);
                var localizations   = StarterKitGeneratorWindow.GetLanguagesForGame(associatedMod.Game);

                // Ensure end name is not present on base
                foreach (var l in localizations)
                {
                    if (targetnameBase.EndsWith($@"_{l.filecode}", StringComparison.InvariantCultureIgnoreCase))
                    {
                        targetnameBase = targetnameBase.Substring(0, targetnameBase.Length - (l.filecode.Length + 1)); //_FileCode
                    }
                }

                foreach (var l in localizations)
                {
                    var targetname = $@"{targetnameBase}_{l.filecode}{targetExtension}";
                    if (loadedFiles.TryGetValue(targetname, out var fullpath))
                    {
                        targetFiles.Add(fullpath);
                    }
                    else
                    {
                        Log.Warning($@"File not found in game: {targetname}, skipping...");
                        numMergesCompleted++;
                        mergeProgressDelegate?.Invoke(numMergesCompleted, numMergesCompleted, null, null);
                    }
                }
            }
            else
            {
                if (loadedFiles.TryGetValue(FileName, out var fullpath))
                {
                    targetFiles.Add(fullpath);
                }
                else
                {
                    Log.Warning($@"File not found in game: {FileName}, skipping...");
                    numMergesCompleted++;
                    mergeProgressDelegate?.Invoke(numMergesCompleted, numMergesCompleted, null, null);
                }
            }


            MergeAssetCache1 mac = new MergeAssetCache1();

            foreach (var f in targetFiles)
            {
                Log.Information($@"Opening package {f}");
#if DEBUG
                Stopwatch sw = Stopwatch.StartNew();
#endif
                // Open as memorystream as we need to hash this file for tracking
                using MemoryStream ms = new MemoryStream(File.ReadAllBytes(f));

                var existingMD5 = Utilities.CalculateMD5(ms);
                var package     = MEPackageHandler.OpenMEPackageFromStream(ms, f);
#if DEBUG
                Debug.WriteLine($@"Opening package {f} took {sw.ElapsedMilliseconds} ms");
#endif
                foreach (var pc in MergeChanges)
                {
                    pc.ApplyChanges(package, mac, associatedMod, gameTarget);
                }

                var track = package.IsModified;
                if (package.IsModified)
                {
                    Log.Information($@"Saving package {package.FilePath}");
#if DEBUG
                    sw.Restart();
#endif
                    package.Save(savePath: f, compress: true);
#if DEBUG
                    Debug.WriteLine($@"Saving package {f} took {sw.ElapsedMilliseconds} ms");
#endif
                }
                else
                {
                    Log.Information($@"Package {package.FilePath} was not modified. This change is likely already installed, not saving package");
                }

                numMergesCompleted++;
                mergeProgressDelegate?.Invoke(numMergesCompleted, numTotalMerges, track ? existingMD5 : null, track ? f : null);
            }
        }
        /// <summary>
        /// Checks mods for updates. ForceUpdateCheck will force the mod to validate against the server (essentially repair mode). It is not used for rate limiting!
        /// </summary>
        /// <param name="modsToCheck">Mods to have server send information about</param>
        /// <param name="forceUpdateCheck">Force update check regardless of version</param>
        /// <returns></returns>
        public static List <ModUpdateInfo> CheckForModUpdates(List <Mod> modsToCheck, bool forceUpdateCheck, Action <string> updateStatusCallback = null)
        {
            string updateFinalRequest = UpdaterServiceManifestEndpoint;
            bool   first = true;

            foreach (var mod in modsToCheck)
            {
                if (mod.ModModMakerID > 0)
                {
                    //Modmaker style
                    if (first)
                    {
                        updateFinalRequest += "?";
                        first = false;
                    }
                    else
                    {
                        updateFinalRequest += "&";
                    }

                    updateFinalRequest += "modmakerupdatecode[]=" + mod.ModModMakerID;
                }
                else if (mod.ModClassicUpdateCode > 0)
                {
                    //Classic style
                    if (first)
                    {
                        updateFinalRequest += "?";
                        first = false;
                    }
                    else
                    {
                        updateFinalRequest += "&";
                    }
                    updateFinalRequest += "classicupdatecode[]=" + mod.ModClassicUpdateCode;
                }
                else if (mod.NexusModID > 0 && mod.NexusUpdateCheck)
                {
                    //Nexus style
                    if (first)
                    {
                        updateFinalRequest += "?";
                        first = false;
                    }
                    else
                    {
                        updateFinalRequest += "&";
                    }
                    updateFinalRequest += "nexusupdatecode[]=" + mod.Game.ToString().Substring(2) + "-" + mod.NexusModID;
                }
                //else if (mod.NexusModID > 0)
                //{
                //    //Classic style
                //    if (first)
                //    {
                //        updateFinalRequest += "?";
                //        first = false;
                //    }
                //    else
                //    {
                //        updateFinalRequest += "&";
                //    }
                //    updateFinalRequest += "nexusupdatecode[]=" + mod.ModClassicUpdateCode;
                //}
            }

            using var wc = new System.Net.WebClient();
            try
            {
                Debug.WriteLine(updateFinalRequest);
                string updatexml = wc.DownloadStringAwareOfEncoding(updateFinalRequest);

                XElement rootElement = XElement.Parse(updatexml);

                #region classic mods

                var modUpdateInfos     = new List <ModUpdateInfo>();
                var classicUpdateInfos = (from e in rootElement.Elements("mod")
                                          select new ModUpdateInfo
                {
                    changelog = (string)e.Attribute("changelog"),
                    versionstr = (string)e.Attribute("version"),
                    updatecode = (int)e.Attribute("updatecode"),
                    serverfolder = (string)e.Attribute("folder"),
                    sourceFiles = (from f in e.Elements("sourcefile")
                                   select new SourceFile
                    {
                        lzmahash = (string)f.Attribute("lzmahash"),
                        hash = (string)f.Attribute("hash"),
                        size = (int)f.Attribute("size"),
                        lzmasize = (int)f.Attribute("lzmasize"),
                        relativefilepath = f.Value,
                        timestamp = (Int64?)f.Attribute("timestamp") ?? (Int64)0
                    }).ToList(),
                    blacklistedFiles = e.Elements("blacklistedfile").Select(x => x.Value).ToList()
                }).ToList();

                // CALCULATE UPDATE DELTA
                CaseInsensitiveDictionary <USFileInfo> hashMap = new CaseInsensitiveDictionary <USFileInfo>(); //Used to rename files
                foreach (var modUpdateInfo in classicUpdateInfos)
                {
                    modUpdateInfo.ResolveVersionVar();
                    //Calculate update information
                    var matchingMod = modsToCheck.FirstOrDefault(x => x.ModClassicUpdateCode == modUpdateInfo.updatecode);
                    if (matchingMod != null && (forceUpdateCheck || matchingMod.ParsedModVersion < modUpdateInfo.version))
                    {
                        // The following line is left so we know that it was at one point considered implemented.
                        // This prevents updating copies of the same mod in the library. Cause it's just kind of a bandwidth waste.
                        //modsToCheck.Remove(matchingMod); //This makes it so we don't feed in multiple same-mods. For example, nexus check on 3 Project Variety downloads
                        modUpdateInfo.mod = matchingMod;
                        modUpdateInfo.SetLocalizedInfo();
                        string        modBasepath = matchingMod.ModPath;
                        double        i           = 0;
                        List <string> references  = null;
                        try
                        {
                            references = matchingMod.GetAllRelativeReferences(true);
                        }
                        catch
                        {
                            // There's an error. Underlying disk state may have changed since we originally loaded the mod
                        }

                        if (references == null || !matchingMod.ValidMod)
                        {
                            // The mod failed to load. We should just index everything the
                            // references will not be dfully parsed.
                            var localFiles = Directory.GetFiles(matchingMod.ModPath, "*", SearchOption.AllDirectories);
                            references = localFiles.Select(x => x.Substring(matchingMod.ModPath.Length + 1)).ToList();
                        }
                        int total = references.Count;

                        // Index existing files
                        foreach (var v in references)
                        {
                            updateStatusCallback?.Invoke(
                                $"Indexing {modUpdateInfo.mod.ModName} for updates {(int)(i * 100 / total)}%");
                            i++;
                            var fpath = Path.Combine(matchingMod.ModPath, v);
                            if (fpath.RepresentsPackageFilePath())
                            {
                                // We need to make sure it's decompressed
                                var qPackage = MEPackageHandler.QuickOpenMEPackage(fpath);
                                if (qPackage.IsCompressed)
                                {
                                    CLog.Information(
                                        $" >> Decompressing compressed package for update comparison check: {fpath}",
                                        Settings.LogModUpdater);
                                    qPackage = MEPackageHandler.OpenMEPackage(fpath);
                                    MemoryStream tStream = new MemoryStream();
                                    tStream    = qPackage.SaveToStream(false);
                                    hashMap[v] = new USFileInfo()
                                    {
                                        MD5              = Utilities.CalculateMD5(tStream),
                                        CompressedMD5    = Utilities.CalculateMD5(fpath),
                                        Filesize         = tStream.Length,
                                        RelativeFilepath = v
                                    };
                                    continue;
                                }
                            }

                            hashMap[v] = new USFileInfo()
                            {
                                MD5              = Utilities.CalculateMD5(fpath),
                                Filesize         = new FileInfo(fpath).Length,
                                RelativeFilepath = v
                            };
                        }

                        i     = 0;
                        total = modUpdateInfo.sourceFiles.Count;
                        foreach (var serverFile in modUpdateInfo.sourceFiles)
                        {
                            Log.Information($@"Checking {serverFile.relativefilepath} for update applicability");
                            updateStatusCallback?.Invoke(
                                $"Calculating update delta for {modUpdateInfo.mod.ModName} {(int)(i * 100 / total)}%");
                            i++;

                            bool calculatedOp = false;
                            if (hashMap.TryGetValue(serverFile.relativefilepath, out var indexInfo))
                            {
                                if (indexInfo.MD5 == serverFile.hash)
                                {
                                    CLog.Information(@" >> File is up to date", Settings.LogModUpdater);
                                    calculatedOp = true;
                                }
                                else if (indexInfo.CompressedMD5 != null && indexInfo.CompressedMD5 == serverFile.hash)
                                {
                                    CLog.Information(@" >> Compressed package file is up to date",
                                                     Settings.LogModUpdater);
                                    calculatedOp = true;
                                }
                            }

                            if (!calculatedOp)
                            {
                                // File is missing or hash was wrong. We should try to map it to another existing file
                                // to save bandwidth
                                var existingFilesThatMatchServerHash =
                                    hashMap.Where(x =>
                                                  x.Value.MD5 == serverFile.hash || (x.Value.CompressedMD5 != null &&
                                                                                     x.Value.CompressedMD5 ==
                                                                                     serverFile.hash))
                                    .ToList();
                                if (existingFilesThatMatchServerHash.Any())
                                {
                                    CLog.Information(
                                        $" >> Server file can be cloned from local file {existingFilesThatMatchServerHash[0].Value.RelativeFilepath} as it has same hash",
                                        Settings.LogModUpdater);
                                    modUpdateInfo.cloneOperations[serverFile] =
                                        existingFilesThatMatchServerHash[0]
                                        .Value;     // Server file can be sourced from the value
                                }
                                else if (indexInfo == null)
                                {
                                    // we don't have file hashed (new file)
                                    CLog.Information(
                                        $" >> Applicable for updates, File does not exist locally",
                                        Settings.LogModUpdater);
                                    modUpdateInfo.applicableUpdates.Add(serverFile);
                                }
                                else
                                {
                                    // Existing file has wrong hash
                                    CLog.Information($" >> Applicable for updates, hash has changed",
                                                     Settings.LogModUpdater);
                                    modUpdateInfo.applicableUpdates.Add(serverFile);
                                }
                            }
                        }

                        foreach (var blacklistedFile in modUpdateInfo.blacklistedFiles)
                        {
                            var blLocalFile = Path.Combine(modBasepath, blacklistedFile);
                            if (File.Exists(blLocalFile))
                            {
                                Log.Information(@"Blacklisted file marked for deletion: " + blLocalFile);
                                modUpdateInfo.filesToDelete.Add(blLocalFile);
                            }
                        }


                        // alphabetize files
                        modUpdateInfo.applicableUpdates.Sort(x => x.relativefilepath);

                        //Files to remove calculation
                        var modFiles = Directory.GetFiles(modBasepath, "*", SearchOption.AllDirectories)
                                       .Select(x => x.Substring(modBasepath.Length + 1)).ToList();

                        var additionalFilesToDelete = modFiles.Except(
                            modUpdateInfo.sourceFiles.Select(x => x.relativefilepath),
                            StringComparer.InvariantCultureIgnoreCase).Distinct().ToList();
                        modUpdateInfo.filesToDelete.AddRange(
                            additionalFilesToDelete); //Todo: Add security check here to prevent malicious


                        modUpdateInfo.TotalBytesToDownload = modUpdateInfo.applicableUpdates.Sum(x => x.lzmasize);
                    }
                }

                modUpdateInfos.AddRange(classicUpdateInfos);

                #endregion

                #region modmaker mods

                var modmakerModUpdateInfos = (from e in rootElement.Elements("modmakermod")
                                              select new ModMakerModUpdateInfo
                {
                    ModMakerId = (int)e.Attribute("id"),
                    versionstr = (string)e.Attribute("version"),
                    PublishDate = DateTime.ParseExact((string)e.Attribute("publishdate"), "yyyy-MM-dd",
                                                      CultureInfo.InvariantCulture),
                    changelog = (string)e.Attribute("changelog")
                }).ToList();
                modUpdateInfos.AddRange(modmakerModUpdateInfos);

                #endregion

                #region Nexus Mod Third Party

                var nexusModsUpdateInfo = (from e in rootElement.Elements("nexusmod")
                                           select new NexusModUpdateInfo
                {
                    NexusModsId = (int)e.Attribute("id"),
                    GameId = (int)e.Attribute("game"),
                    versionstr = (string)e.Attribute("version"),
                    UpdatedTime = DateTimeOffset.FromUnixTimeSeconds((long)e.Attribute("updated_timestamp"))
                                  .DateTime
                }).ToList();
                modUpdateInfos.AddRange(nexusModsUpdateInfo);

                #endregion

                return(modUpdateInfos);
            }
            catch (Exception e)
            {
                Log.Error("Error checking for mod updates: " + App.FlattenException(e));
                Crashes.TrackError(e, new Dictionary <string, string>()
                {
                    { "Update check URL", updateFinalRequest }
                });
            }

            return(null);
        }
예제 #10
0
        public static void BuildBioPGlobal(GameTarget target)
        {
            M3MergeDLC.RemoveMergeDLC(target);
            var loadedFiles = MELoadedFiles.GetFilesLoadedInGame(target.Game, gameRootOverride: target.TargetPath);

            //var mergeFiles = loadedFiles.Where(x =>
            //    x.Key.StartsWith(@"BioH_") && x.Key.Contains(@"_DLC_MOD_") && x.Key.EndsWith(@".pcc") && !x.Key.Contains(@"_LOC_") && !x.Key.Contains(@"_Explore."));

            Log.Information($@"SQMMERGE: Building BioP_Global");
            var appearanceInfo = new CaseInsensitiveDictionary <List <SquadmateInfoSingle> >();

            int appearanceId       = 255; // starting
            int currentConditional = STARTING_OUTFIT_CONDITIONAL;

            // Scan squadmate merge files
            var sqmSupercedances = M3Directories.GetFileSupercedances(target, new[] { @".sqm" });

            if (sqmSupercedances.TryGetValue(SQUADMATE_MERGE_MANIFEST_FILE, out var infoList))
            {
                infoList.Reverse();
                foreach (var dlc in infoList)
                {
                    Log.Information($@"SQMMERGE: Processing {dlc}");

                    var jsonFile    = Path.Combine(M3Directories.GetDLCPath(target), dlc, target.Game.CookedDirName(), SQUADMATE_MERGE_MANIFEST_FILE);
                    var infoPackage = JsonConvert.DeserializeObject <SquadmateMergeInfo>(File.ReadAllText(jsonFile));
                    if (!infoPackage.Validate(dlc, target, loadedFiles))
                    {
                        continue; // skip this
                    }

                    // Enumerate all outfits listed for a single squadmate
                    foreach (var outfit in infoPackage.Outfits)
                    {
                        List <SquadmateInfoSingle> list;

                        // See if we already have an outfit list for this squadmate, maybe from another mod...
                        if (!appearanceInfo.TryGetValue(outfit.HenchName, out list))
                        {
                            list = new List <SquadmateInfoSingle>();
                            appearanceInfo[outfit.HenchName] = list;
                        }

                        outfit.ConditionalIndex = currentConditional++; // This is always incremented, so it might appear out of order in game files depending on how mod order is processed, that should be okay though.
                        outfit.AppearanceId     = appearanceId++;       // may need adjusted
                        outfit.DLCName          = dlc;
                        list.Add(outfit);
                        Log.Information($@"SQMMERGE: ConditionalIndex for {outfit.HenchName} appearanceid {outfit.AppearanceId}: {outfit.ConditionalIndex}");
                    }
                }
            }

            if (appearanceInfo.Any())
            {
                var biopGlobal      = MEPackageHandler.OpenMEPackage(loadedFiles[@"BioP_Global.pcc"]);
                var lsk             = biopGlobal.Exports.FirstOrDefault(x => x.ClassName == @"LevelStreamingKismet");
                var persistentLevel = biopGlobal.FindExport(@"TheWorld.PersistentLevel");

                // Clone LevelStreamingKismets
                foreach (var sqm in appearanceInfo.Values)
                {
                    foreach (var outfit in sqm)
                    {
                        var fName  = outfit.HenchPackage;
                        var newLSK = EntryCloner.CloneEntry(lsk);
                        newLSK.WriteProperty(new NameProperty(fName, @"PackageName"));

                        if (target.Game.IsGame3())
                        {
                            // Game 3 has _Explore files too
                            fName += @"_Explore";
                            newLSK = EntryCloner.CloneEntry(lsk);
                            newLSK.WriteProperty(new NameProperty(fName, @"PackageName"));
                        }
                    }
                }

                // Update BioWorldInfo
                // Doesn't have consistent number so we can't find it by instanced full path
                var bioWorldInfo = biopGlobal.Exports.FirstOrDefault(x => x.ClassName == @"BioWorldInfo");

                var props = bioWorldInfo.GetProperties();

                // Update Plot Streaming
                var plotStreaming = props.GetProp <ArrayProperty <StructProperty> >(@"PlotStreaming");
                foreach (var sqm in appearanceInfo.Values)
                {
                    foreach (var outfit in sqm)
                    {
                        // find item to add to
                        buildPlotElementObject(plotStreaming, outfit, target.Game, false);
                        if (target.Game.IsGame3())
                        {
                            buildPlotElementObject(plotStreaming, outfit, target.Game, true);
                        }
                    }
                }


                // Update StreamingLevels
                var streamingLevels = props.GetProp <ArrayProperty <ObjectProperty> >(@"StreamingLevels");
                streamingLevels.ReplaceAll(biopGlobal.Exports.Where(x => x.ClassName == @"LevelStreamingKismet").Select(x => new ObjectProperty(x)));

                bioWorldInfo.WriteProperties(props);


                M3MergeDLC.GenerateMergeDLC(target, Guid.NewGuid());

                // Save BioP_Global into DLC
                var cookedDir = Path.Combine(M3Directories.GetDLCPath(target), M3MergeDLC.MERGE_DLC_FOLDERNAME, target.Game.CookedDirName());
                var outP      = Path.Combine(cookedDir, @"BioP_Global.pcc");
                biopGlobal.Save(outP);

                // Generate conditionals file
                if (target.Game.IsGame3())
                {
                    CNDFile cnd = new CNDFile();
                    cnd.ConditionalEntries = new List <CNDFile.ConditionalEntry>();

                    foreach (var sqm in appearanceInfo.Values)
                    {
                        foreach (var outfit in sqm)
                        {
                            var scText   = $@"(plot.ints[{GetSquadmateOutfitInt(outfit.HenchName, target.Game)}] == i{outfit.MemberAppearanceValue})";
                            var compiled = ME3ConditionalsCompiler.Compile(scText);
                            cnd.ConditionalEntries.Add(new CNDFile.ConditionalEntry()
                            {
                                Data = compiled, ID = outfit.ConditionalIndex
                            });
                        }
                    }

                    cnd.ToFile(Path.Combine(cookedDir, $@"Conditionals{M3MergeDLC.MERGE_DLC_FOLDERNAME}.cnd"));
                }
                else if (target.Game.IsGame2())
                {
                    var startupF = Path.Combine(cookedDir, $@"Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc");
                    var startup  = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream(
                                                                                $@"MassEffectModManagerCore.modmanager.mergedlc.{target.Game}.Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc"));
                    var conditionalClass =
                        startup.FindExport($@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}.BioAutoConditionals");

                    // Add Conditional Functions
                    FileLib fl          = new FileLib(startup);
                    bool    initialized = fl.Initialize(new RelativePackageCache()
                    {
                        RootPath = M3Directories.GetBioGamePath(target)
                    });
                    if (!initialized)
                    {
                        throw new Exception(
                                  $@"FileLib for script update could not initialize, cannot install conditionals");
                    }


                    var funcToClone =
                        startup.FindExport($@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}.BioAutoConditionals.TemplateFunction");
                    foreach (var sqm in appearanceInfo.Values)
                    {
                        foreach (var outfit in sqm)
                        {
                            var func = EntryCloner.CloneEntry(funcToClone);
                            func.ObjectName = $@"F{outfit.ConditionalIndex}";
                            func.indexValue = 0;

                            var scText = new StreamReader(Utilities.GetResourceStream(
                                                              $@"MassEffectModManagerCore.modmanager.squadmates.{target.Game}.HasOutfitOnConditional.txt"))
                                         .ReadToEnd();

                            scText = scText.Replace(@"%CONDITIONALNUM%", outfit.ConditionalIndex.ToString());
                            scText = scText.Replace(@"%SQUADMATEOUTFITPLOTINT%", outfit.AppearanceId.ToString());
                            scText = scText.Replace(@"%OUTFITINDEX%", outfit.MemberAppearanceValue.ToString());

                            (_, MessageLog log) = UnrealScriptCompiler.CompileFunction(func, scText, fl);
                            if (log.AllErrors.Any())
                            {
                                Log.Error($@"Error compiling function {func.InstancedFullPath}:");
                                foreach (var l in log.AllErrors)
                                {
                                    Log.Error(l.Message);
                                }

                                throw new Exception(M3L.GetString(M3L.string_interp_errorCompilingConditionalFunction, func, string.Join('\n', log.AllErrors.Select(x => x.Message))));
                            }
                        }
                    }


                    // Relink the conditionals chain
                    UClass uc = ObjectBinary.From <UClass>(conditionalClass);
                    uc.UpdateLocalFunctions();
                    uc.UpdateChildrenChain();
                    conditionalClass.WriteBinary(uc);

                    startup.Save(startupF);
                }


                // Add startup package, member appearances
                if (target.Game.IsGame2())
                {
                    var bioEngine = Path.Combine(cookedDir, @"BIOEngine.ini");
                    var ini       = DuplicatingIni.LoadIni(bioEngine);

                    var startupSection = ini.GetOrAddSection(@"Engine.StartupPackages");

                    startupSection.Entries.Add(new DuplicatingIni.IniEntry(@"+DLCStartupPackage", $@"Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}"));
                    startupSection.Entries.Add(new DuplicatingIni.IniEntry(@"+Package", $@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}"));

                    ini.WriteToFile(bioEngine);
                }
                else if (target.Game.IsGame3())
                {
                    var mergeCoalFile = Path.Combine(M3Directories.GetDLCPath(target), M3MergeDLC.MERGE_DLC_FOLDERNAME, target.Game.CookedDirName(), $@"Default_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.bin");
                    var mergeCoal     = CoalescedConverter.DecompileGame3ToMemory(new MemoryStream(File.ReadAllBytes(mergeCoalFile)));

                    // Member appearances
                    var bioUiDoc = XDocument.Parse(mergeCoal[@"BIOUI.xml"]);

                    foreach (var sqm in appearanceInfo.Values)
                    {
                        foreach (var outfit in sqm)
                        {
                            var entry = new Game3CoalescedValueEntry()
                            {
                                Section = @"sfxgame.sfxguidata_teamselect",
                                Name    = @"selectappearances",
                                Type    = 3,
                                Value   = StringStructParser.BuildCommaSeparatedSplitValueList(outfit.ToPropertyDictionary(), @"AvailableImage", @"HighlightImage", @"DeadImage", @"SilhouetteImage")
                            };
                            Game3CoalescedHelper.AddArrayEntry(bioUiDoc, entry);
                        }
                    }

                    mergeCoal[@"BIOUI.xml"] = bioUiDoc.ToString();

                    // Dynamic load mapping
                    var bioEngineDoc = XDocument.Parse(mergeCoal[@"BIOEngine.xml"]);

                    foreach (var sqm in appearanceInfo.Values)
                    {
                        foreach (var outfit in sqm)
                        {
                            //
                            // * <Section name="sfxgame.sfxengine">
                            // <Property name="dynamicloadmapping">
                            // <Value type="3">(ObjectName="BIOG_GesturesConfigDLC.RuntimeData",SeekFreePackageName="GesturesConfigDLC")</Value>

                            var entry = new Game3CoalescedValueEntry()
                            {
                                Section = @"sfxgame.sfxengine",
                                Name    = @"dynamicloadmapping",
                                Type    = 3
                            };

                            entry.Values.Add($"(ObjectName=\"{outfit.AvailableImage}\",SeekFreePackageName=\"SFXHenchImages_{outfit.DLCName}\")"); // do not localize
                            entry.Values.Add($"(ObjectName=\"{outfit.HighlightImage}\",SeekFreePackageName=\"SFXHenchImages_{outfit.DLCName}\")"); // do not localize
                            Game3CoalescedHelper.AddArrayEntry(bioEngineDoc, entry);
                        }
                    }

                    mergeCoal[@"BIOEngine.xml"] = bioEngineDoc.ToString();


                    CoalescedConverter.CompileFromMemory(mergeCoal).WriteToFile(mergeCoalFile);
                }
            }
        }
예제 #11
0
        public async Task RunAsync()
        {
            var lineWithCommand = new Regex($"^{_context.CommandPrefix} (\\S+)(?: (\\S+))*$");

            using (var stream = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(_context.InputFile))))
            {
                while (!stream.EndOfStream)
                {
                    var line = await stream.ReadLineAsync();

                    if (line == null)
                    {
                        continue;
                    }

                    var commandCheck = lineWithCommand.Match(line);

                    if (commandCheck.Success)
                    {
                        var groupEnumerator = commandCheck.Groups.GetEnumerator();
                        groupEnumerator.MoveNext();
                        groupEnumerator.MoveNext();

                        var group = groupEnumerator.Current as Group;
                        if (group == null)
                        {
                            continue;
                        }

                        var command = group.Value;
                        if (command == null)
                        {
                            continue;
                        }

                        if (!_instructions.TryGetValue(command, out var instruction))
                        {
                            continue;
                        }

                        var parameters = new List <string>();
                        while (groupEnumerator.MoveNext())
                        {
                            var value = (groupEnumerator.Current as Group)?.Value;
                            if (!string.IsNullOrWhiteSpace(value))
                            {
                                parameters.Add(value ?? throw new InvalidOperationException());
                            }
                        }

                        if (!_activeInstructions.ContainsKey(instruction.Command))
                        {
                            _activeInstructions.Add(instruction.Command, instruction);
                        }

                        await instruction.InitializeInstructionAsync(_context, parameters.ToArray());
                    }
                    else
                    {
                        foreach (var inst in _activeInstructions.Values)
                        {
                            await inst.HandleLineAsync(line);
                        }
                    }
                }
            }
        }