/// <summary>
        /// Aligns a mod's ModKey to a path's implied ModKey.
        /// Will adjust its logic based on the MasterFlagSync option:
        ///  - ThrowIfMisaligned:  If the path and mod do not match, throw.
        ///  - CorrectToPath:  If the path and mod do not match, use path's key.
        /// </summary>
        /// <param name="mod">Mod to check and adjust</param>
        /// <param name="path">Path to compare to</param>
        /// <returns>ModKey to use</returns>
        /// <exception cref="ArgumentException">If misaligned and set to ThrowIfMisaligned</exception>
        public ModKey RunMasterMatch(IModGetter mod, string path)
        {
            if (ModKeySync == ModKeySyncOption.NoCheck)
            {
                return(mod.ModKey);
            }
            if (!ModKey.TryFactory(Path.GetFileName(path), out var pathModKey))
            {
                throw new ArgumentException($"Could not convert path to a ModKey to compare against: {Path.GetFileName(path)}");
            }
            switch (ModKeySync)
            {
            case ModKeySyncOption.ThrowIfMisaligned:
                if (mod.ModKey != pathModKey)
                {
                    throw new ArgumentException($"ModKeys were misaligned: {mod.ModKey} != {pathModKey}");
                }
                return(mod.ModKey);

            case ModKeySyncOption.CorrectToPath:
                return(pathModKey);

            default:
                throw new NotImplementedException();
            }
        }
Beispiel #2
0
 /// <summary>
 /// Asserts a given mod is in the collection of keys.
 /// </summary>
 /// <param name="keys">Listings to look through</param>
 /// <param name="modKey">ModKey to look for</param>
 /// <param name="message">Message to attach to exception if mod doesn't exist</param>
 /// <exception cref="MissingModException">
 /// Thrown if given mod is not on the collection of listings
 /// </exception>
 public static void AssertHasMod(this IEnumerable <ModKey> keys, ModKey modKey, string?message = null)
 {
     if (!HasMod(keys, modKey))
     {
         throw new MissingModException(keys, message: message);
     }
 }
Beispiel #3
0
        /// <summary>
        /// Parses a stream to retrieve all ModKeys in expected plugin file format
        /// </summary>
        /// <param name="stream">Stream to read from</param>
        /// <returns>List of modkeys representing a load order</returns>
        /// <exception cref="ArgumentException">Line in plugin stream is unexpected</exception>
        public static IExtendedList <ModKey> ProcessLoadOrder(Stream stream)
        {
            var ret = new ExtendedList <ModKey>();

            using var streamReader = new StreamReader(stream);
            while (!streamReader.EndOfStream)
            {
                var str          = streamReader.ReadLine();
                var commentIndex = str.IndexOf('#');
                if (commentIndex != -1)
                {
                    str = str.Substring(0, commentIndex);
                }
                if (string.IsNullOrWhiteSpace(str))
                {
                    continue;
                }
                str = str.Trim();
                if (!ModKey.TryFactory(str, out var key))
                {
                    throw new ArgumentException($"Load order file had malformed line: {str}");
                }
                ret.Add(key);
            }
            return(ret);
        }
Beispiel #4
0
 /// <summary>
 /// Asserts a given mod is in the collection of keys.
 /// </summary>
 /// <param name="loadOrder">LoadOrder to query</param>
 /// <param name="modKey">ModKey to look for</param>
 /// <param name="message">Message to attach to exception if mod doesn't exist</param>
 /// <exception cref="MissingModException">
 /// Thrown if given mod is not on the collection of listings
 /// </exception>
 public static void AssertHasMod(this ILoadOrderGetter loadOrder, ModKey modKey, string?message = null)
 {
     if (!HasMod(loadOrder, modKey))
     {
         throw new MissingModException(loadOrder.ListedOrder, message: message);
     }
 }
 public static string GetFileName(
     StringsLanguageFormat languageFormat,
     ModKey modKey,
     Language language,
     StringsSource source)
 {
     return($"{modKey.Name}_{GetLanguageString(languageFormat, language)}.{GetSourceString(source)}");
 }
 public static RecordException Create(string message, ModKey modKey, Exception?innerException = null)
 {
     return(new RecordException(
                formKey: null,
                modKey: modKey,
                edid: null,
                recordType: null,
                message: message,
                innerException: innerException));
 }
Beispiel #7
0
 public static IEnumerable <LoadOrderListing> ListingsFromPath(
     FilePath cccFilePath,
     DirectoryPath dataPath)
 {
     return(File.ReadAllLines(cccFilePath.Path)
            .Select(x => ModKey.FromNameAndExtension(x))
            .Where(x => File.Exists(Path.Combine(dataPath.Path, x.FileName)))
            .Select(x => new LoadOrderListing(x, enabled: true))
            .ToList());
 }
Beispiel #8
0
        public static IObservable <IChangeSet <LoadOrderListing> > GetLiveLoadOrder(
            FilePath cccFilePath,
            DirectoryPath dataFolderPath,
            out IObservable <ErrorResponse> state,
            bool orderListings = true)
        {
            var raw = ObservableExt.WatchFile(cccFilePath.Path)
                      .StartWith(Unit.Default)
                      .Select(_ =>
            {
                try
                {
                    return(GetResponse <IObservable <IChangeSet <ModKey> > > .Succeed(
                               File.ReadAllLines(cccFilePath.Path)
                               .Select(x => ModKey.FromNameAndExtension(x))
                               .AsObservableChangeSet()));
                }
                catch (Exception ex)
                {
                    return(GetResponse <IObservable <IChangeSet <ModKey> > > .Fail(ex));
                }
            })
                      .Replay(1)
                      .RefCount();

            state = raw
                    .Select(r => (ErrorResponse)r);
            var ret = ObservableListEx.And(
                raw
                .Select(r =>
            {
                return(r.Value ?? Observable.Empty <IChangeSet <ModKey> >());
            })
                .Switch(),
                ObservableExt.WatchFolderContents(dataFolderPath.Path)
                .Transform(x =>
            {
                if (ModKey.TryFromNameAndExtension(Path.GetFileName(x), out var modKey))
                {
                    return(TryGet <ModKey> .Succeed(modKey));
                }
                return(TryGet <ModKey> .Failure);
            })
                .Filter(x => x.Succeeded)
                .Transform(x => x.Value)
                .RemoveKey())
                      .Transform(x => new LoadOrderListing(x, true));

            if (orderListings)
            {
                ret = ret.OrderListings();
            }
            return(ret);
        }
Beispiel #9
0
 /// <summary>
 /// Checks whether a given mod is in the collection of listings.
 /// </summary>
 /// <param name="listings">Listings to look through</param>
 /// <param name="modKey">ModKey to look for</param>
 /// <param name="enabled">Whether the ModKey should be enabled/disabled.  Default is no preference</param>
 /// <returns>True if ModKey is in the listings, with the desired enabled state</returns>
 public static bool HasMod(this IEnumerable <LoadOrderListing> listings, ModKey modKey, bool?enabled = null)
 {
     foreach (var listing in listings)
     {
         if (listing.ModKey == modKey &&
             (enabled == null || listing.Enabled == enabled))
         {
             return(true);
         }
     }
     return(false);
 }
Beispiel #10
0
 public static RecordException Factory(Exception ex, ModKey modKey)
 {
     if (ex is RecordException rec)
     {
         rec.ModKey = modKey;
         return(rec);
     }
     return(new RecordException(
                formKey: null,
                modKey: modKey,
                edid: null,
                innerException: ex));
 }
Beispiel #11
0
 /// <summary>
 /// Duplicates records into a given mod 'modToDuplicateInto', which originated from target ModKey 'modKeyToDuplicateFrom'.<br />
 /// Only considers records that are currently within the target modToDuplicateInto, which are then duplicated. <br/>
 /// Records from the modKeyToDuplicateFrom that are not within or referenced by records in the target mod are skipped.<br />
 /// <br />
 /// End result will be that all records that the given modToDuplicateInto contains or references that originate from the target modKeyToDuplicateFrom will be duplicated in
 /// and replace the records they duplicated.  No references to the modKeyToDuplicateFrom will remain.
 /// </summary>
 /// <typeparam name="TMod"></typeparam>
 /// <typeparam name="TModGetter"></typeparam>
 /// <param name="modToDuplicateInto">Mod to duplicate into and originate new records from</param>
 /// <param name="linkCache">LinkCache for lookup</param>
 /// <param name="modKeyToDuplicateFrom">ModKey to search modToDuplicateInto for, and duplicate records that originate from modKeyToDuplicateFrom</param>
 /// <param name="typesToInspect">
 /// Types to iterate and look at within modToDuplicateInto for references to modKeyToDuplicateFrom<br />
 /// Only use if you know specifically the types that can reference modKeyToDuplicateFrom, and want a little bit of speed
 /// by not checking uninteresting records.
 /// </param>
 public static void DuplicateFromOnlyReferenced <TMod, TModGetter>(
     this TMod modToDuplicateInto,
     ILinkCache <TMod, TModGetter> linkCache,
     ModKey modKeyToDuplicateFrom,
     params Type[] typesToInspect)
     where TModGetter : class, IModGetter
     where TMod : class, TModGetter, IMod
 {
     DuplicateFromOnlyReferenced(
         modToDuplicateInto,
         linkCache,
         modKeyToDuplicateFrom,
         out _,
         typesToInspect);
 }
Beispiel #12
0
 public static void TypicalPatch <TMod>(
     string[] mainArgs,
     ModKey outputMod,
     IReadOnlyList <ModKey> loadOrderList,
     LoadOrder <TMod> .Importer importer,
     Func <ModKey, LoadOrder <TMod>, TMod> processor)
     where TMod : class, IMod
 {
     TypicalPatch(
         dataFolder: new Noggog.DirectoryPath(mainArgs[0]),
         outModKey: outputMod,
         loadOrderList: loadOrderList,
         processor: processor,
         importer: importer);
 }
 public static RecordException Enrich(Exception ex, ModKey modKey)
 {
     if (ex is RecordException rec)
     {
         if (rec.ModKey == null)
         {
             rec.ModKey = modKey;
         }
         return(rec);
     }
     return(new RecordException(
                formKey: null,
                modKey: modKey,
                edid: null,
                recordType: null,
                innerException: ex));
 }
Beispiel #14
0
        public static void TypicalPatch <TMod>(
            DirectoryPath dataFolder,
            ModKey outModKey,
            IReadOnlyList <ModKey> loadOrderList,
            LoadOrder <TMod> .Importer importer,
            Func <ModKey, LoadOrder <TMod>, TMod> processor)
            where TMod : class, IMod
        {
            var loadOrderInternal = loadOrderList.ToList();

            loadOrderInternal.Remove(outModKey);
            var loadOrder = new LoadOrder <TMod>();

            loadOrder.Import(
                dataFolder,
                loadOrderInternal,
                importer);
            var outMod = processor(outModKey, loadOrder);

            foreach (var npc in outMod.EnumerateMajorRecords())
            {
                npc.IsCompressed = false;
            }
            var linkCache = loadOrder.CreateLinkCache();

            outMod.MasterReferences.SetTo(
                outMod.LinkFormKeys
                .Where(fk => !fk.IsNull)
                .Select(s => s.ModKey)
                .Where(modKey => modKey != outModKey)
                .Distinct()
                .OrderBy(modKey => loadOrder.IndexOf(modKey))
                .Select(modKey => new MasterReference()
            {
                Master = modKey
            }));
            outMod.SyncRecordCount();
            outMod.WriteToBinary(
                path: Path.Combine(dataFolder.Path, outModKey.FileName));
        }
Beispiel #15
0
        /// <summary>
        /// Enumerates all applicable BSAs for a given release and ModKey that are within a given dataFolderPath.
        /// Orders the results to a BSA load order driven by the ini
        /// </summary>
        /// <param name="release">GameRelease to query for</param>
        /// <param name="dataFolderPath">Folder to query within</param>
        /// <param name="modKey">ModKey to query about</param>
        /// <param name="bsaOrdering">How to order the bsa archive paths.  Null for no ordering</param>
        /// <returns></returns>
        public static IEnumerable <string> GetApplicableArchivePaths(GameRelease release, string dataFolderPath, ModKey modKey, IComparer <string>?bsaOrdering)
        {
            var ret = Directory.EnumerateFiles(dataFolderPath, $"*{GetArchiveExtension(release)}");

            if (bsaOrdering != null)
            {
                var ret2 = ret.OrderBy(x => x, bsaOrdering);
                return(ret2);
            }
            return(ret);
        }
Beispiel #16
0
 /// <summary>
 /// Enumerates all applicable BSAs for a given release and ModKey that are within a given dataFolderPath.
 /// </summary>
 /// <param name="release">GameRelease to query for</param>
 /// <param name="dataFolderPath">Folder to query within</param>
 /// <param name="modKey">ModKey to query about</param>
 /// <param name="bsaOrdering">BSA ordering overload.  Empty enumerable means no ordering.</param>
 /// <returns></returns>
 public static IEnumerable <string> GetApplicableArchivePaths(GameRelease release, string dataFolderPath, ModKey modKey, IEnumerable <string> bsaOrdering)
 {
     return(GetApplicableArchivePaths(release, dataFolderPath, modKey, GetPriorityOrderComparer(release, bsaOrdering)));
 }
Beispiel #17
0
 /// <summary>
 /// Constructor taking a ModKey and ID as separate parameters
 /// </summary>
 /// <param name="modKey">ModKey to use</param>
 /// <param name="id">Record ID to use.  Must be less than 0x00FFFFFF.</param>
 /// <exception cref="ArgumentException">ID needs to contain no data in upper two bytes, or it will throw.</exception>
 public FormKey(ModKey modKey, uint id)
 {
     this.ModKey = modKey;
     this.ID     = id & 0xFFFFFF;
 }
Beispiel #18
0
 public ModPath(ModKey modKey, string path)
 {
     ModKey = modKey;
     Path   = path;
 }
Beispiel #19
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="modKey">Key to assign the mod</param>
 public AMod(ModKey modKey)
 {
     this.ModKey     = modKey;
     this._allocator = new SimpleFormKeyAllocator(this);
 }
Beispiel #20
0
 public ModContext(ModKey modKey, IModContext?parent, T record)
 {
     ModKey = modKey;
     Parent = parent;
     Record = record;
 }
 public static RecordException Factory(string message, ModKey modKey, Exception?innerException = null)
 {
     return(Create(message, modKey, innerException));
 }
Beispiel #22
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="modKey">Key to assign the mod</param>
 /// <param name="allocator">Optional custom FormKey allocator logic</param>
 public AMod(ModKey modKey, IFormKeyAllocator?allocator = null)
 {
     this.ModKey     = modKey;
     this._allocator = allocator ?? new SimpleFormKeyAllocator(this);
 }
 public static TMod GetIfEnabledAndExists <TMod>(this ILoadOrderGetter <IModListingGetter <TMod> > loadOrder, ModKey modKey)
     where TMod : class, IModGetter
 {
     if (TryGetIfEnabledAndExists <TMod>(loadOrder, modKey, out var mod))
     {
         return(mod);
     }
     throw new MissingModException(modKey);
 }
Beispiel #24
0
        public static StringsFolderLookupOverlay?TypicalFactory(string referenceModPath, StringsReadParameters?instructions, ModKey modKey)
        {
            var ret = new StringsFolderLookupOverlay();
            var stringsFolderPath = instructions?.StringsFolderOverride;
            var dir = Path.GetDirectoryName(referenceModPath);

            if (stringsFolderPath == null)
            {
                stringsFolderPath = Path.Combine(dir, "Strings");
            }
            if (stringsFolderPath.Value.Exists)
            {
                foreach (var file in stringsFolderPath.Value.Info.EnumerateFiles($"{modKey.Name}*{StringsUtility.StringsFileExtension}"))
                {
                    if (!StringsUtility.TryRetrieveInfoFromString(file.Name, out var type, out var lang, out _))
                    {
                        continue;
                    }
                    var dict = ret.Get(type);
                    dict[lang] = new Lazy <IStringsLookup>(() => new StringsLookupOverlay(file.FullName, type), LazyThreadSafetyMode.ExecutionAndPublication);
                }
            }
            foreach (var bsaFile in Directory.EnumerateFiles(dir, "*.bsa"))
            {
                var bsaReader = BSAReader.Load(new AbsolutePath(bsaFile, skipValidation: true));
                foreach (var item in bsaReader.Files)
                {
                    if (!StringsUtility.TryRetrieveInfoFromString(Path.GetFileName(item.Path.ToString()), out var type, out var lang, out var modName))
                    {
                        continue;
                    }
                    if (!MemoryExtensions.Equals(modKey.Name, modName, StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }
                    var dict = ret.Get(type);
                    if (dict.ContainsKey(lang))
                    {
                        continue;
                    }
                    dict[lang] = new Lazy <IStringsLookup>(() =>
                    {
                        byte[] bytes     = new byte[item.Size];
                        using var stream = new MemoryStream(bytes);
                        item.CopyDataTo(stream).AsTask().Wait();
                        return(new StringsLookupOverlay(bytes, type));
                    }, LazyThreadSafetyMode.ExecutionAndPublication);
                }
            }
            return(ret);
        }
 public static RecordException Factory(Exception ex, ModKey modKey)
 {
     return(Enrich(ex, modKey));
 }
Beispiel #26
0
 public StringsWriter(ModKey modKey, DirectoryPath writeDirectory)
 {
     _modKey  = modKey;
     WriteDir = writeDirectory;
 }
Beispiel #27
0
 public MissingModException(ModKey key)
 {
     ModPath = new ModPath(key, string.Empty);
 }
        public static bool TryGetIfEnabledAndExists <TMod>(this ILoadOrderGetter <IModListingGetter <TMod> > loadOrder, ModKey modKey, [MaybeNullWhen(false)] out TMod item)
            where TMod : class, IModGetter
        {
            if (!TryGetIfEnabled(loadOrder, modKey, out var listing))
            {
                item = default;
                return(false);
            }

            item = listing.Mod;
            return(item != null);
        }
Beispiel #29
0
 public MissingModException(ModKey key, string message)
     : base(message)
 {
     ModPath = new ModPath(key, string.Empty);
 }
Beispiel #30
0
 public StringsWriter(GameRelease release, ModKey modKey, DirectoryPath writeDirectory)
 {
     _modKey         = modKey;
     _languageFormat = release.GetLanguageFormat();
     WriteDir        = writeDirectory;
 }