From cc1a933a2f4adf05a45c7adcf02669c4f423bedb Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Wed, 3 May 2023 02:07:16 +0200 Subject: [PATCH] ModLoader: Fix case sensitivy issues (#4720) * Fix case sensitivity for mod subdirectories * Small refactoring of ModLoader --- .../UI/ViewModels/MainWindowViewModel.cs | 8 +- .../UI/Windows/CheatWindow.axaml.cs | 4 +- src/Ryujinx.HLE/HOS/ModLoader.cs | 269 +++++++++--------- .../Extensions/LocalFileSystemExtensions.cs | 7 +- .../Processes/Extensions/NcaExtensions.cs | 5 +- .../Ui/Widgets/GameTableContextMenu.cs | 8 +- src/Ryujinx/Ui/Windows/CheatWindow.cs | 4 +- 7 files changed, 158 insertions(+), 147 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index f4556bc3c9..4ce4799904 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1576,8 +1576,8 @@ namespace Ryujinx.Ava.UI.ViewModels { if (SelectedApplication != null) { - string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } @@ -1587,8 +1587,8 @@ namespace Ryujinx.Ava.UI.ViewModels { if (SelectedApplication != null) { - string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId); + string sdModsBasePath = ModLoader.GetSdModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId); OpenHelper.OpenFolder(titleModsPath); } diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs index cb939763b0..241a6c346c 100644 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs @@ -35,8 +35,8 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); - string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId); ulong titleIdValue = ulong.Parse(titleId, System.Globalization.NumberStyles.HexNumber); _enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt"); diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index 1651254144..a173ac418e 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS } // Title independent mods - public class PatchCache + private class PatchCache { public List> NsoPatches { get; } public List> NroPatches { get; } @@ -107,14 +107,14 @@ namespace Ryujinx.HLE.HOS } } - public Dictionary AppMods; // key is TitleId - public PatchCache Patches; + private readonly Dictionary _appMods; // key is TitleId + private PatchCache _patches; - private static readonly EnumerationOptions _dirEnumOptions; + private static readonly EnumerationOptions DirEnumOptions; static ModLoader() { - _dirEnumOptions = new EnumerationOptions + DirEnumOptions = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive, MatchType = MatchType.Simple, @@ -125,37 +125,73 @@ namespace Ryujinx.HLE.HOS public ModLoader() { - AppMods = new Dictionary(); - Patches = new PatchCache(); + _appMods = new Dictionary(); + _patches = new PatchCache(); } - public void Clear() + private void Clear() { - AppMods.Clear(); - Patches = new PatchCache(); + _appMods.Clear(); + _patches = new PatchCache(); } private static bool StrEquals(string s1, string s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase); - public string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); - public string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath()); + public static string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath()); + public static string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath()); - private string EnsureBaseDirStructure(string modsBasePath) + private static string EnsureBaseDirStructure(string modsBasePath) { var modsDir = new DirectoryInfo(modsBasePath); modsDir.CreateSubdirectory(AmsContentsDir); modsDir.CreateSubdirectory(AmsNsoPatchDir); modsDir.CreateSubdirectory(AmsNroPatchDir); - // modsDir.CreateSubdirectory(AmsKipPatchDir); // uncomment when KIPs are supported + // TODO: uncomment when KIPs are supported + // modsDir.CreateSubdirectory(AmsKipPatchDir); return modsDir.FullName; } private static DirectoryInfo FindTitleDir(DirectoryInfo contentsDir, string titleId) - => contentsDir.EnumerateDirectories($"{titleId}*", _dirEnumOptions).FirstOrDefault(); + => contentsDir.EnumerateDirectories($"{titleId}*", DirEnumOptions).FirstOrDefault(); - public string GetTitleDir(string modsBasePath, string titleId) + private static void AddModsFromDirectory(ModCache mods, DirectoryInfo dir, string titleId) + { + System.Text.StringBuilder types = new(); + + foreach (var modDir in dir.EnumerateDirectories()) + { + types.Clear(); + Mod mod = new("", null); + + if (StrEquals(RomfsDir, modDir.Name)) + { + mods.RomfsDirs.Add(mod = new Mod($"<{titleId} RomFs>", modDir)); + types.Append('R'); + } + else if (StrEquals(ExefsDir, modDir.Name)) + { + mods.ExefsDirs.Add(mod = new Mod($"<{titleId} ExeFs>", modDir)); + types.Append('E'); + } + else if (StrEquals(CheatDir, modDir.Name)) + { + types.Append('C', QueryCheatsDir(mods, modDir)); + } + else + { + AddModsFromDirectory(mods, modDir, titleId); + } + + if (types.Length > 0) + { + Logger.Info?.Print(LogClass.ModLoader, $"Found mod '{mod.Name}' [{types}]"); + } + } + } + + public static string GetTitleDir(string modsBasePath, string titleId) { var contentsDir = new DirectoryInfo(Path.Combine(modsBasePath, AmsContentsDir)); var titleModsPath = FindTitleDir(contentsDir, titleId); @@ -170,17 +206,32 @@ namespace Ryujinx.HLE.HOS } // Static Query Methods - public static void QueryPatchDirs(PatchCache cache, DirectoryInfo patchDir) + private static void QueryPatchDirs(PatchCache cache, DirectoryInfo patchDir) { - if (cache.Initialized || !patchDir.Exists) return; + if (cache.Initialized || !patchDir.Exists) + { + return; + } - var patches = cache.KipPatches; - string type = null; + List> patches; + string type; - if (StrEquals(AmsNsoPatchDir, patchDir.Name)) { patches = cache.NsoPatches; type = "NSO"; } - else if (StrEquals(AmsNroPatchDir, patchDir.Name)) { patches = cache.NroPatches; type = "NRO"; } - else if (StrEquals(AmsKipPatchDir, patchDir.Name)) { patches = cache.KipPatches; type = "KIP"; } - else return; + if (StrEquals(AmsNsoPatchDir, patchDir.Name)) + { + patches = cache.NsoPatches; type = "NSO"; + } + else if (StrEquals(AmsNroPatchDir, patchDir.Name)) + { + patches = cache.NroPatches; type = "NRO"; + } + else if (StrEquals(AmsKipPatchDir, patchDir.Name)) + { + patches = cache.KipPatches; type = "KIP"; + } + else + { + return; + } foreach (var modDir in patchDir.EnumerateDirectories()) { @@ -189,9 +240,12 @@ namespace Ryujinx.HLE.HOS } } - public static void QueryTitleDir(ModCache mods, DirectoryInfo titleDir) + private static void QueryTitleDir(ModCache mods, DirectoryInfo titleDir) { - if (!titleDir.Exists) return; + if (!titleDir.Exists) + { + return; + } var fsFile = new FileInfo(Path.Combine(titleDir.FullName, RomfsContainer)); if (fsFile.Exists) @@ -205,64 +259,15 @@ namespace Ryujinx.HLE.HOS mods.ExefsContainers.Add(new Mod($"<{titleDir.Name} ExeFs>", fsFile)); } - System.Text.StringBuilder types = new System.Text.StringBuilder(5); - - foreach (var modDir in titleDir.EnumerateDirectories()) - { - types.Clear(); - Mod mod = new Mod("", null); - - if (StrEquals(RomfsDir, modDir.Name)) - { - mods.RomfsDirs.Add(mod = new Mod($"<{titleDir.Name} RomFs>", modDir)); - types.Append('R'); - } - else if (StrEquals(ExefsDir, modDir.Name)) - { - mods.ExefsDirs.Add(mod = new Mod($"<{titleDir.Name} ExeFs>", modDir)); - types.Append('E'); - } - else if (StrEquals(CheatDir, modDir.Name)) - { - for (int i = 0; i < QueryCheatsDir(mods, modDir); i++) - { - types.Append('C'); - } - } - else - { - var romfs = new DirectoryInfo(Path.Combine(modDir.FullName, RomfsDir)); - var exefs = new DirectoryInfo(Path.Combine(modDir.FullName, ExefsDir)); - var cheat = new DirectoryInfo(Path.Combine(modDir.FullName, CheatDir)); - - if (romfs.Exists) - { - mods.RomfsDirs.Add(mod = new Mod(modDir.Name, romfs)); - types.Append('R'); - } - - if (exefs.Exists) - { - mods.ExefsDirs.Add(mod = new Mod(modDir.Name, exefs)); - types.Append('E'); - } - - if (cheat.Exists) - { - for (int i = 0; i < QueryCheatsDir(mods, cheat); i++) - { - types.Append('C'); - } - } - } - - if (types.Length > 0) Logger.Info?.Print(LogClass.ModLoader, $"Found mod '{mod.Name}' [{types}]"); - } + AddModsFromDirectory(mods, titleDir, titleDir.Name); } public static void QueryContentsDir(ModCache mods, DirectoryInfo contentsDir, ulong titleId) { - if (!contentsDir.Exists) return; + if (!contentsDir.Exists) + { + return; + } Logger.Info?.Print(LogClass.ModLoader, $"Searching mods for {((titleId & 0x1000) != 0 ? "DLC" : "Title")} {titleId:X16}"); @@ -302,9 +307,16 @@ namespace Ryujinx.HLE.HOS continue; } + int oldCheatsCount = mods.Cheats.Count; + // A cheat file can contain several cheats for the same executable, so the file must be parsed in // order to properly enumerate them. mods.Cheats.AddRange(GetCheatsInFile(file)); + + if (mods.Cheats.Count - oldCheatsCount > 0) + { + numMods++; + } } return numMods; @@ -313,57 +325,54 @@ namespace Ryujinx.HLE.HOS private static IEnumerable GetCheatsInFile(FileInfo cheatFile) { string cheatName = DefaultCheatName; - List instructions = new List(); - List cheats = new List(); + List instructions = new(); + List cheats = new(); - using (StreamReader cheatData = cheatFile.OpenText()) + using StreamReader cheatData = cheatFile.OpenText(); + while (cheatData.ReadLine() is { } line) { - string line; - while ((line = cheatData.ReadLine()) != null) + line = line.Trim(); + + if (line.StartsWith('[')) { - line = line.Trim(); - - if (line.StartsWith('[')) + // This line starts a new cheat section. + if (!line.EndsWith(']') || line.Length < 3) { - // This line starts a new cheat section. - if (!line.EndsWith(']') || line.Length < 3) - { - // Skip the entire file if there's any error while parsing the cheat file. + // Skip the entire file if there's any error while parsing the cheat file. - Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); + Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); - return new List(); - } - - // Add the previous section to the list. - if (instructions.Count != 0) - { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); - } - - // Start a new cheat section. - cheatName = line.Substring(1, line.Length - 2); - instructions = new List(); + return Array.Empty(); } - else if (line.Length > 0) + + // Add the previous section to the list. + if (instructions.Count > 0) { - // The line contains an instruction. - instructions.Add(line); + cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); } + + // Start a new cheat section. + cheatName = line.Substring(1, line.Length - 2); + instructions.Clear(); } - - // Add the last section being processed. - if (instructions.Count != 0) + else if (line.Length > 0) { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + // The line contains an instruction. + instructions.Add(line); } } + // Add the last section being processed. + if (instructions.Count > 0) + { + cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + } + return cheats; } // Assumes searchDirPaths don't overlap - public static void CollectMods(Dictionary modCaches, PatchCache patches, params string[] searchDirPaths) + private static void CollectMods(Dictionary modCaches, PatchCache patches, params string[] searchDirPaths) { static bool IsPatchesDir(string name) => StrEquals(AmsNsoPatchDir, name) || StrEquals(AmsNroPatchDir, name) || @@ -375,7 +384,7 @@ namespace Ryujinx.HLE.HOS { if (IsContentsDir(searchDir.Name)) { - foreach (var (titleId, cache) in modCaches) + foreach ((ulong titleId, ModCache cache) in modCaches) { QueryContentsDir(cache, searchDir, titleId); } @@ -419,15 +428,15 @@ namespace Ryujinx.HLE.HOS foreach (ulong titleId in titles) { - AppMods[titleId] = new ModCache(); + _appMods[titleId] = new ModCache(); } - CollectMods(AppMods, Patches, searchDirPaths); + CollectMods(_appMods, _patches, searchDirPaths); } internal IStorage ApplyRomFsMods(ulong titleId, IStorage baseStorage) { - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.RomfsDirs.Count + mods.RomfsContainers.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.RomfsDirs.Count + mods.RomfsContainers.Count == 0) { return baseStorage; } @@ -487,7 +496,7 @@ namespace Ryujinx.HLE.HOS return newStorage; } - private static void AddFiles(IFileSystem fs, string modName, HashSet fileSet, RomFsBuilder builder) + private static void AddFiles(IFileSystem fs, string modName, ISet fileSet, RomFsBuilder builder) { foreach (var entry in fs.EnumerateEntries() .Where(f => f.Type == DirectoryEntryType.File) @@ -509,7 +518,7 @@ namespace Ryujinx.HLE.HOS internal bool ReplaceExefsPartition(ulong titleId, ref IFileSystem exefs) { - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsContainers.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsContainers.Count == 0) { return false; } @@ -537,13 +546,13 @@ namespace Ryujinx.HLE.HOS internal ModLoadResult ApplyExefsMods(ulong titleId, NsoExecutable[] nsos) { - ModLoadResult modLoadResult = new ModLoadResult + ModLoadResult modLoadResult = new() { Stubs = new BitVector32(), Replaces = new BitVector32() }; - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) { return modLoadResult; } @@ -561,7 +570,7 @@ namespace Ryujinx.HLE.HOS { var nsoName = ProcessConst.ExeFsPrefixes[i]; - FileInfo nsoFile = new FileInfo(Path.Combine(mod.Path.FullName, nsoName)); + FileInfo nsoFile = new(Path.Combine(mod.Path.FullName, nsoName)); if (nsoFile.Exists) { if (modLoadResult.Replaces[1 << i]) @@ -580,7 +589,7 @@ namespace Ryujinx.HLE.HOS modLoadResult.Stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension)); } - FileInfo npdmFile = new FileInfo(Path.Combine(mod.Path.FullName, "main.npdm")); + FileInfo npdmFile = new(Path.Combine(mod.Path.FullName, "main.npdm")); if (npdmFile.Exists) { if (modLoadResult.Npdm != null) @@ -611,7 +620,7 @@ namespace Ryujinx.HLE.HOS internal void ApplyNroPatches(NroExecutable nro) { - var nroPatches = Patches.NroPatches; + var nroPatches = _patches.NroPatches; if (nroPatches.Count == 0) return; @@ -622,9 +631,9 @@ namespace Ryujinx.HLE.HOS internal bool ApplyNsoPatches(ulong titleId, params IExecutable[] programs) { - IEnumerable> nsoMods = Patches.NsoPatches; + IEnumerable> nsoMods = _patches.NsoPatches; - if (AppMods.TryGetValue(titleId, out ModCache mods)) + if (_appMods.TryGetValue(titleId, out ModCache mods)) { nsoMods = nsoMods.Concat(mods.ExefsDirs); } @@ -636,7 +645,7 @@ namespace Ryujinx.HLE.HOS internal void LoadCheats(ulong titleId, ProcessTamperInfo tamperInfo, TamperMachine tamperMachine) { - if (tamperInfo == null || tamperInfo.BuildIds == null || tamperInfo.CodeAddresses == null) + if (tamperInfo?.BuildIds == null || tamperInfo.CodeAddresses == null) { Logger.Error?.Print(LogClass.ModLoader, "Unable to install cheat because the associated process is invalid"); @@ -645,14 +654,14 @@ namespace Ryujinx.HLE.HOS Logger.Info?.Print(LogClass.ModLoader, $"Build ids found for title {titleId:X16}:\n {String.Join("\n ", tamperInfo.BuildIds)}"); - if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.Cheats.Count == 0) + if (!_appMods.TryGetValue(titleId, out ModCache mods) || mods.Cheats.Count == 0) { return; } var cheats = mods.Cheats; var processExes = tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses, (k, v) => new { k, v }) - .ToDictionary(x => x.k.Substring(0, Math.Min(Cheat.CheatIdSize, x.k.Length)), x => x.v); + .ToDictionary(x => x.k[..Math.Min(Cheat.CheatIdSize, x.k.Length)], x => x.v); foreach (var cheat in cheats) { @@ -758,4 +767,4 @@ namespace Ryujinx.HLE.HOS return count > 0; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs index 28d9078512..fb85329d2b 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs @@ -2,6 +2,7 @@ using LibHac.FsSystem; using LibHac.Loader; using LibHac.Ns; +using Ryujinx.HLE.HOS; using Ryujinx.HLE.Loaders.Processes.Extensions; using ApplicationId = LibHac.Ncm.ApplicationId; @@ -17,8 +18,8 @@ namespace Ryujinx.HLE.Loaders.Processes device.Configuration.VirtualFileSystem.ModLoader.CollectMods( new[] { programId }, - device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + ModLoader.GetModsBasePath(), + ModLoader.GetSdModsBasePath()); if (programId != 0) { @@ -36,4 +37,4 @@ namespace Ryujinx.HLE.Loaders.Processes return processResult; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs index 473f374db9..e11b81d7f7 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -8,6 +8,7 @@ using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS; using System.IO; using System.Linq; using ApplicationId = LibHac.Ncm.ApplicationId; @@ -35,8 +36,8 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions // Collecting mods related to AocTitleIds and ProgramId. device.Configuration.VirtualFileSystem.ModLoader.CollectMods( device.Configuration.ContentManager.GetAocTitleIds().Prepend(metaLoader.GetProgramId()), - device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + ModLoader.GetModsBasePath(), + ModLoader.GetSdModsBasePath()); // Load Nacp file. var nacpData = new BlitStruct(1); diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 558288aab3..6d83316586 100644 --- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -460,16 +460,16 @@ namespace Ryujinx.Ui.Widgets private void OpenTitleModDir_Clicked(object sender, EventArgs args) { - string modsBasePath = _virtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, _titleIdText); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } private void OpenTitleSdModDir_Clicked(object sender, EventArgs args) { - string sdModsBasePath = _virtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); + string sdModsBasePath = ModLoader.GetSdModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); OpenHelper.OpenFolder(titleModsPath); } diff --git a/src/Ryujinx/Ui/Windows/CheatWindow.cs b/src/Ryujinx/Ui/Windows/CheatWindow.cs index 917603b290..7dbea01289 100644 --- a/src/Ryujinx/Ui/Windows/CheatWindow.cs +++ b/src/Ryujinx/Ui/Windows/CheatWindow.cs @@ -28,8 +28,8 @@ namespace Ryujinx.Ui.Windows builder.Autoconnect(this); _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; - string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); + string modsBasePath = ModLoader.GetModsBasePath(); + string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); _enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt");