Пример #1
0
 internal static void SetFMResource(FanMission fm, CustomResources resource, bool value)
 {
     if (value)
     {
         fm.Resources |= resource;
     }
     else
     {
         fm.Resources &= ~resource;
     }
 }
Пример #2
0
        // Static for perf - this gets called from most comparer classes and we don't want to be instantiating
        // new title-sort classes in a loop!
        internal static int TitleCompare(FanMission x, FanMission y)
        {
            // Important: these get modified, so don't use the originals!
            string xTitle = x.Title;
            string yTitle = y.Title;

            // null for perf: don't create a new List<string> just to signify empty
            var articles = Config.EnableArticles ? Config.Articles : null;

            if (xTitle == yTitle)
            {
                return(TitleOrFallback(xTitle, yTitle, x, y, compareTitles: false));
            }
            if (xTitle.IsEmpty())
            {
                return(-1);
            }
            if (yTitle.IsEmpty())
            {
                return(1);
            }

            int xTitleLen = xTitle.Length;
            int yTitleLen = yTitle.Length;

            if (articles == null || articles.Count == 0)
            {
                return(TitleOrFallback(xTitle, yTitle, x, y));
            }

            bool xArticleSet = false;
            bool yArticleSet = false;

            foreach (string a in articles)
            {
                int aLen = a.Length;

                // Avoid concats for perf
                if (!xArticleSet && xTitle.StartsWithI(a) && xTitleLen > aLen && char.IsWhiteSpace(xTitle[aLen]))
                {
                    xTitle      = xTitle.Substring(aLen + 1);
                    xArticleSet = true;
                }
                if (!yArticleSet && yTitle.StartsWithI(a) && yTitleLen > aLen && char.IsWhiteSpace(yTitle[aLen]))
                {
                    yTitle      = yTitle.Substring(aLen + 1);
                    yArticleSet = true;
                }
            }

            return(TitleOrFallback(xTitle, yTitle, x, y));
        }
Пример #3
0
        public int Compare(FanMission x, FanMission y)
        {
            int ret =
                x.DisableAllMods && !y.DisableAllMods ? -1 :
                !x.DisableAllMods && y.DisableAllMods ? 1 :
                (x.DisableAllMods && y.DisableAllMods) || x.DisabledMods == y.DisabledMods ? TitleCompare(x, y) :
                // Sort this column content-first for better UX
                x.DisabledMods.IsEmpty() ? 1 :
                y.DisabledMods.IsEmpty() ? -1 :
                string.Compare(x.DisabledMods, y.DisabledMods, StringComparison.InvariantCultureIgnoreCase);

            return(SortOrder == SortOrder.Ascending ? ret : -ret);
        }
Пример #4
0
        // PERF_TODO: ffmpeg can do multiple files in one run. Switch to that, and see if ffprobe can do it too.

        // OpenAL doesn't play nice with anything over 16 bits, blasting out white noise when it tries to play
        // such. Converting all >16bit wavs to 16 bit fixes this.
        internal static async Task ConvertWAVsTo16Bit(FanMission fm, bool doChecksAndProgressBox)
        {
            if (doChecksAndProgressBox)
            {
                var(success, refreshFM) = ChecksPassed(fm);
                if (!success)
                {
                    if (refreshFM)
                    {
                        await Core.View.RefreshSelectedFM(refreshReadme : false);
                    }
                    return;
                }
            }
Пример #5
0
        public async void InstallFM_Test()
        {
            var fm = new FanMission();

            // TODO:
            // -Create test fm zip file
            // -Fill out fm fields with appropriate values
            // -Either modify InstallFM so that we pass it everything it uses, or else just fill out every global
            // it uses with correct values for our test

            //bool success = await FMInstallAndPlay.InstallFM(fm);

            // TODO: Assert audio files have been converted properly
        }
Пример #6
0
        public void UpdateFMTagsString_Test()
        {
            var fm = new FanMission();

            var cat1 = new CatAndTags {
                Category = "author"
            };

            cat1.Tags.Add("Tannar");
            cat1.Tags.Add("Random_Taffer");
            fm.Tags.Add(cat1);

            var cat2 = new CatAndTags {
                Category = "contest"
            };

            cat2.Tags.Add("10 rooms");
            fm.Tags.Add(cat2);

            var cat3 = new CatAndTags {
                Category = "length"
            };

            cat3.Tags.Add("short");
            fm.Tags.Add(cat3);

            var cat4 = new CatAndTags {
                Category = "series"
            };

            fm.Tags.Add(cat4);

            var cat5 = new CatAndTags {
                Category = "misc"
            };

            cat5.Tags.Add("campaign");
            cat5.Tags.Add("atmospheric");
            cat5.Tags.Add("other protagonist");
            cat5.Tags.Add("water");
            cat5.Tags.Add("thing_shaped");
            fm.Tags.Add(cat5);

            FMTags.UpdateFMTagsString(fm);

            Assert.Equal(
                "author:Tannar,author:Random_Taffer,contest:10 rooms,length:short,series,misc:campaign,misc:atmospheric,misc:other protagonist,misc:water,misc:thing_shaped",
                fm.TagsString);
        }
Пример #7
0
        // @BetterErrors(FillFMSupportedLangs())
        internal static void FillFMSupportedLangs(FanMission fm)
        {
            // We should already have checked before getting here, but just for safety...
            if (!GameIsDark(fm.Game))
            {
                return;
            }

            string        fmInstPath = Path.Combine(Config.GetFMInstallPath(GameToGameIndex(fm.Game)), fm.InstalledDir);
            List <string> langs;

            if (FMIsReallyInstalled(fm))
            {
                try
                {
                    langs = GetFMSupportedLanguagesFromInstDir(fmInstPath, earlyOutOnEnglish: false);
                }
                catch (Exception ex)
                {
                    Log("Exception trying to detect language folders in installed dir for fm '" +
                        fm.Archive + "' (inst dir '" + fm.InstalledDir + "')", ex);
                    fm.LangsScanned = false;
                    return;
                }
            }
            else
            {
                try
                {
                    (_, langs) = GetFMSupportedLanguagesFromArchive(fm.Archive, earlyOutOnEnglish: false);
                }
                catch (Exception ex)
                {
                    Log("Exception trying to detect language folders in archive for fm '" +
                        fm.Archive + "' (inst dir '" + fm.InstalledDir + "')", ex);
                    fm.LangsScanned = false;
                    return;
                }
            }

            if (langs.Count > 0)
            {
                langs    = SortLangsToSpec(langs.ToHashSetI());
                fm.Langs = string.Join(",", langs);
            }

            fm.LangsScanned = true;
        }
Пример #8
0
            static int TitleOrFallback(string title1, string title2, FanMission fm1, FanMission fm2, bool compareTitles = true)
            {
                int ret;

                if (compareTitles)
                {
                    ret = string.Compare(title1, title2, StringComparison.InvariantCultureIgnoreCase);
                    if (ret != 0)
                    {
                        return(ret);
                    }
                }

                ret = string.Compare(fm1.Archive, fm2.Archive, StringComparison.InvariantCultureIgnoreCase);
                if (ret != 0)
                {
                    return(ret);
                }

                return(string.Compare(fm1.InstalledDir, fm2.InstalledDir, StringComparison.InvariantCultureIgnoreCase));
            }
Пример #9
0
        private static void ClearCacheDir(FanMission fm)
        {
            string fmCachePath = Path.Combine(Paths.FMsCache, fm.InstalledDir);

            if (!fmCachePath.TrimEnd(CA_BS_FS).PathEqualsI(Paths.FMsCache.TrimEnd(CA_BS_FS)) && Directory.Exists(fmCachePath))
            {
                try
                {
                    foreach (string f in FastIO.GetFilesTopOnly(fmCachePath, "*"))
                    {
                        File.Delete(f);
                    }
                    foreach (string d in FastIO.GetDirsTopOnly(fmCachePath, "*"))
                    {
                        Directory.Delete(d, recursive: true);
                    }
                }
                catch (Exception ex)
                {
                    Log("Exception clearing files in FM cache for " + fm.Archive + " / " + fm.InstalledDir, ex);
                }
            }
        }
Пример #10
0
        // If some files exist but not all that are in the zip, the user can just re-scan for this data by clicking
        // a button, so don't worry about it
        internal static async Task <CacheData> GetCacheableData(FanMission fm, bool refreshCache)
        {
            if (fm.Game == Game.Unsupported)
            {
                if (!fm.InstalledDir.IsEmpty())
                {
                    ClearCacheDir(fm);
                }
                return(new CacheData());
            }

            try
            {
                return(FMIsReallyInstalled(fm)
                    ? GetCacheableDataInFMInstalledDir(fm)
                    : await GetCacheableDataInFMCacheDir(fm, refreshCache));
            }
            catch (Exception ex)
            {
                Log("Exception in GetCacheableData", ex);
                return(new CacheData());
            }
        }
Пример #11
0
 private static void FMData_InstalledDir_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.InstalledDir = valTrimmed;
 }
Пример #12
0
 // Static for perf - this gets called from most comparer classes and we don't want to be instantiating
 // new title-sort classes in a loop!
 private static int TitleCompare(FanMission x, FanMission y)
 {
Пример #13
0
        ImportInternal(string iniFile, bool importFMData, bool importSaves, bool returnUnmergedFMsList = false,
                       FieldsToImport?fields = null)
        {
            string[] lines = await Task.Run(() => File.ReadAllLines(iniFile));

            var fms = new List <FanMission>();

            var error = ImportError.None;

            if (importFMData)
            {
                bool missionDirsRead = false;
                var  archiveDirs     = new List <string>();

                error = await Task.Run(() =>
                {
                    try
                    {
                        for (int i = 0; i < lines.Length; i++)
                        {
                            string line   = lines[i];
                            string lineTS = line.TrimStart();
                            string lineTB = lineTS.TrimEnd();

                            #region Read archive directories

                            // We need to know the archive dirs before doing anything, because we may need to recreate
                            // some lossy names (if any bad chars have been removed by DarkLoader).
                            if (!missionDirsRead && lineTB == "[mission directories]")
                            {
                                while (i < lines.Length - 1)
                                {
                                    string lt = lines[i + 1].Trim();
                                    if (!lt.IsEmpty() && lt[0] != '[' && lt.EndsWith("=1"))
                                    {
                                        archiveDirs.Add(lt.Substring(0, lt.Length - 2));
                                    }
                                    else if (!lt.IsEmpty() && lt[0] == '[' && lt[lt.Length - 1] == ']')
                                    {
                                        break;
                                    }
                                    i++;
                                }

                                if (archiveDirs.Count == 0 || archiveDirs.All(x => x.IsWhiteSpace()))
                                {
                                    return(ImportError.NoArchiveDirsFound);
                                }

                                // Restart from the beginning of the file, this time skipping anything that isn't an
                                // FM entry
                                i = -1;
                                missionDirsRead = true;
                                continue;
                            }

                            #endregion

                            #region Read FM entries

                            // MUST CHECK missionDirsRead OR IT ADDS EVERY FM TWICE!
                            if (missionDirsRead &&
                                !NonFMHeaders.Contains(lineTB) && lineTB.Length > 0 && lineTB[0] == '[' &&
                                lineTB[lineTB.Length - 1] == ']' && lineTB.Contains('.') &&
                                DarkLoaderFMRegex.Match(lineTB).Success)
                            {
                                int lastIndexDot = lineTB.LastIndexOf('.');
                                string archive   = lineTB.Substring(1, lastIndexDot - 1);
                                string size      = lineTB.Substring(lastIndexDot + 1, lineTB.Length - lastIndexDot - 2);

                                foreach (string dir in archiveDirs)
                                {
                                    if (!Directory.Exists(dir))
                                    {
                                        continue;
                                    }
                                    try
                                    {
                                        // DarkLoader only does zip format
                                        foreach (string f in FastIO.GetFilesTopOnly(dir, "*.zip"))
                                        {
                                            string fn = Path.GetFileNameWithoutExtension(f);
                                            if (RemoveDLArchiveBadChars(fn).EqualsI(archive))
                                            {
                                                archive = fn;
                                                goto breakout;
                                            }
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        Log("Exception in DarkLoader archive dir file enumeration", ex);
                                    }
                                }

breakout:

                                // Add .zip back on; required because everything expects it, and furthermore if there's
                                // a dot anywhere in the name then everything after it will be treated as the extension
                                // and is liable to be lopped off at any time
                                archive += ".zip";

                                ulong.TryParse(size, out ulong sizeBytes);
                                var fm = new FanMission
                                {
                                    Archive      = archive,
                                    InstalledDir = archive.ToInstDirNameFMSel(),
                                    SizeBytes    = sizeBytes
                                };

                                // We don't import game type, because DarkLoader by default gets it wrong for NewDark
                                // FMs (the user could have changed it manually in the ini file, and in fact it's
                                // somewhat likely they would have done so, but still, better to just scan for it
                                // ourselves later)

                                while (i < lines.Length - 1)
                                {
                                    string lts = lines[i + 1].TrimStart();
                                    string ltb = lts.TrimEnd();

                                    if (lts.StartsWith("comment=\""))
                                    {
                                        string comment = ltb.Substring(9);
                                        if (comment.Length >= 2 && comment[comment.Length - 1] == '\"')
                                        {
                                            comment    = comment.Substring(0, comment.Length - 1);
                                            fm.Comment = DLUnescapeChars(comment);
                                        }
                                    }
                                    else if (lts.StartsWith("title=\""))
                                    {
                                        string title = ltb.Substring(7);
                                        if (title.Length >= 2 && title[title.Length - 1] == '\"')
                                        {
                                            title    = title.Substring(0, title.Length - 1);
                                            fm.Title = DLUnescapeChars(title);
                                        }
                                    }
                                    else if (lts.StartsWith("misdate="))
                                    {
                                        ulong.TryParse(ltb.Substring(8), out ulong result);
                                        try
                                        {
                                            var date = new DateTime(1899, 12, 30).AddDays(result);
                                            fm.ReleaseDate.DateTime = date.Year > 1998 ? date : (DateTime?)null;
                                        }
                                        catch (ArgumentOutOfRangeException)
                                        {
                                            fm.ReleaseDate.DateTime = null;
                                        }
                                    }
                                    else if (lts.StartsWith("date="))
                                    {
                                        ulong.TryParse(ltb.Substring(5), out ulong result);
                                        try
                                        {
                                            var date = new DateTime(1899, 12, 30).AddDays(result);
                                            fm.LastPlayed.DateTime = date.Year > 1998 ? date : (DateTime?)null;
                                        }
                                        catch (ArgumentOutOfRangeException)
                                        {
                                            fm.LastPlayed.DateTime = null;
                                        }
                                    }
                                    else if (lts.StartsWith("finished="))
                                    {
                                        uint.TryParse(ltb.Substring(9), out uint result);
                                        // result will be 0 on fail, which is the empty value so it's fine
                                        fm.FinishedOn = result;
                                    }
                                    else if (!ltb.IsEmpty() && ltb[0] == '[' && ltb[ltb.Length - 1] == ']')
                                    {
                                        break;
                                    }
                                    i++;
                                }

                                fms.Add(fm);
                            }

                            #endregion
                        }
                        return(ImportError.None);
                    }
                    catch (Exception ex)
                    {
                        Log("Exception in " + nameof(ImportDarkLoader) + "." + nameof(ImportInternal), ex);
                        return(ImportError.Unknown);
                    }
                    finally
                    {
                        Core.View.InvokeSync(new Action(Core.View.HideProgressBox));
                    }
                });
            }

            if (error != ImportError.None)
            {
                return(error, fms);
            }

            if (importSaves)
            {
                bool success = await ImportSaves(lines);
            }

            var importedFMs = returnUnmergedFMsList
                ? fms
                : ImportCommon.MergeImportedFMData(ImportType.DarkLoader, fms, fields);

            return(ImportError.None, importedFMs);
        }
Пример #14
0
 // Update fm.TagsString here. We keep TagsString around because when we're reading, writing, and merging
 // FMs, we don't want to spend time converting back and forth. So Tags is session-only, and only gets
 // filled out for FMs that will be displayed. TagsString is the one that gets saved and loaded, and must
 // be kept in sync with Tags. This should ONLY be called when a tag is added or removed. Keep it simple
 // so we can see and follow the logic.
 private static void UpdateFMTagsString(FanMission fm)
 {
     fm.TagsString = TagsToString(fm.Tags, writeEmptyCategories: false);
 }
Пример #15
0
 private static void FMData_Installed_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.Installed = valTrimmed.EqualsTrue();
 }
Пример #16
0
 private static void FMData_Author_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.Author = valTrimmed;
 }
Пример #17
0
 private static void FMData_HasResources_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.ResourcesScanned = !valTrimmed.EqualsI("NotScanned");
     FillFMHasXFields(fm, valTrimmed);
 }
Пример #18
0
 internal static bool FMHasResource(FanMission fm, CustomResources resource) => (fm.Resources & resource) == resource;
Пример #19
0
 internal static void AddTagToFM(FanMission fm, string catAndTag)
 {
     AddTagsToFMAndGlobalList(catAndTag, fm.Tags);
     UpdateFMTagsString(fm);
     Ini.WriteFullFMDataIni();
 }
Пример #20
0
 private static void FMData_HasSubtitles_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     SetFMResource(fm, CustomResources.Subtitles, valTrimmed.EqualsTrue());
     fm.ResourcesScanned = true;
 }
Пример #21
0
        // This nonsense is to allow for keys to be looked up in a dictionary rather than running ten thousand
        // if statements on every line.

        private static void FMData_NoArchive_Set(FanMission fm, string valTrimmed, string valRaw)
        {
            fm.NoArchive = valTrimmed.EqualsTrue();
        }
Пример #22
0
 private static void FMData_TagsString_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.TagsString = valTrimmed;
 }
Пример #23
0
 private static void FMData_SelectedLang_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.SelectedLang = valTrimmed;
 }
Пример #24
0
 private static void FMData_Langs_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.Langs = valTrimmed;
 }
Пример #25
0
        internal static bool RemoveTagFromFM(FanMission fm, string catText, string tagText)
        {
            if (tagText.IsEmpty())
            {
                return(false);
            }

            // Parent node (category)
            if (catText.IsEmpty())
            {
                // TODO: These messageboxes are annoying, but they prevent accidental deletion.
                // Figure out something better.
                bool cont = Core.View.AskToContinue(LText.TagsTab.AskRemoveCategory, LText.TagsTab.TabText, true);
                if (!cont)
                {
                    return(false);
                }

                CatAndTags?cat = fm.Tags.FirstOrDefault(x => x.Category == tagText);
                if (cat != null)
                {
                    fm.Tags.Remove(cat);
                    UpdateFMTagsString(fm);

                    // TODO: Profile the FirstOrDefaults and see if I should make them for loops
                    GlobalCatAndTags?globalCat = GlobalTags.FirstOrDefault(x => x.Category.Name == cat.Category);
                    if (globalCat != null && !globalCat.Category.IsPreset)
                    {
                        if (globalCat.Category.UsedCount > 0)
                        {
                            globalCat.Category.UsedCount--;
                        }
                        if (globalCat.Category.UsedCount == 0)
                        {
                            GlobalTags.Remove(globalCat);
                        }
                    }
                }
            }
            // Child node (tag)
            else
            {
                bool cont = Core.View.AskToContinue(LText.TagsTab.AskRemoveTag, LText.TagsTab.TabText, true);
                if (!cont)
                {
                    return(false);
                }

                CatAndTags?cat = fm.Tags.FirstOrDefault(x => x.Category == catText);
                string?    tag = cat?.Tags.FirstOrDefault(x => x == tagText);
                if (tag != null)
                {
                    cat !.Tags.Remove(tag);
                    if (cat.Tags.Count == 0)
                    {
                        fm.Tags.Remove(cat);
                    }
                    UpdateFMTagsString(fm);

                    GlobalCatAndTags?globalCat = GlobalTags.FirstOrDefault(x => x.Category.Name == cat.Category);
                    GlobalCatOrTag?  globalTag = globalCat?.Tags.FirstOrDefault(x => x.Name == tagText);
                    if (globalTag != null && !globalTag.IsPreset)
                    {
                        if (globalTag.UsedCount > 0)
                        {
                            globalTag.UsedCount--;
                        }
                        if (globalTag.UsedCount == 0)
                        {
                            globalCat !.Tags.Remove(globalTag);
                        }
                        if (globalCat !.Tags.Count == 0)
                        {
                            GlobalTags.Remove(globalCat);
                        }
                    }
                }
            }

            Ini.WriteFullFMDataIni();

            return(true);
        }
Пример #26
0
 private static void FMData_Title_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.Title = valTrimmed;
 }
Пример #27
0
 private static void FMData_MarkedScanned_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.MarkedScanned = valTrimmed.EqualsTrue();
 }
Пример #28
0
 private static void FMData_DisableAllMods_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.DisableAllMods = valTrimmed.EqualsTrue();
 }
Пример #29
0
 internal static bool FMNeedsScan(FanMission fm) => fm.Game == Game.Null || (fm.Game != Game.Unsupported && !fm.MarkedScanned);
Пример #30
0
 private static void FMData_Archive_Set(FanMission fm, string valTrimmed, string valRaw)
 {
     fm.Archive = valTrimmed;
 }