Implement modding support (#1249)

* Implement Modding Support

* Executables: Rewrite to use contiguous mem and Spans

* Reorder ExeFs, Npdm, ControlData and SaveData calls

After discussion with gdkchan, it was decided it's best to call
LoadExeFs after all other loads are done as it starts the guest process.

* Build RomFs manually instead of Layering FS

Layered FS approach has considerable latency when building the final
romfs. So, we manually replace files in a single romfs instance.

* Add RomFs modding via storage file

* Fix and cleanup MemPatch

* Add dynamically loaded NRO patching

* Support exefs file replacement

* Rewrite ModLoader to use mods-search architecture

* Disable PPTC when exefs patches are detected

Disable PPTC on exefs replacements too

* Rewrite ModLoader, again

* Increased maintainability and matches Atmosphere closely
* Creates base mods structure if it doesn't exist
* Add Exefs partition replacement
* IPSwitch: Fix nsobid parsing

* Move mod logs to new LogClass

* Allow custom suffixes to title dirs again

* Address nits

* Add a per-App "Open Mods Directory" context menu item

Creates the path if not present.

* Normalize tooltips verbiage

* Use LocalStorage and remove unused namespaces
This commit is contained in:
mageven 2020-07-09 10:01:15 +05:30 committed by GitHub
parent c050994995
commit 189c0c9c72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1234 additions and 153 deletions

View file

@ -45,24 +45,24 @@ namespace Ryujinx.Ui
MenuItem openSaveUserDir = new MenuItem("Open User Save Directory")
{
Sensitive = !Util.IsEmpty(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0,
TooltipText = "Open the folder where the User save for the application is loaded"
TooltipText = "Open the directory which contains Application's User Saves."
};
MenuItem openSaveDeviceDir = new MenuItem("Open Device Save Directory")
{
Sensitive = !Util.IsEmpty(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0,
TooltipText = "Open the folder where the Device save for the application is loaded"
TooltipText = "Open the directory which contains Application's Device Saves."
};
MenuItem openSaveBcatDir = new MenuItem("Open BCAT Save Directory")
{
Sensitive = !Util.IsEmpty(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0,
TooltipText = "Open the folder where the BCAT save for the application is loaded"
TooltipText = "Open the directory which contains Application's BCAT Saves."
};
MenuItem manageTitleUpdates = new MenuItem("Manage Title Updates")
{
TooltipText = "Open the title update management window"
TooltipText = "Open the Title Update management window"
};
MenuItem manageDlc = new MenuItem("Manage DLC")
@ -70,6 +70,11 @@ namespace Ryujinx.Ui
TooltipText = "Open the DLC management window"
};
MenuItem openTitleModDir = new MenuItem("Open Mods Directory")
{
TooltipText = "Open the directory which contains Application's Mods."
};
string ext = System.IO.Path.GetExtension(_gameTableStore.GetValue(_rowIter, 9).ToString()).ToLower();
bool hasNca = ext == ".nca" || ext == ".nsp" || ext == ".pfs0" || ext == ".xci";
@ -78,19 +83,19 @@ namespace Ryujinx.Ui
MenuItem extractRomFs = new MenuItem("RomFS")
{
Sensitive = hasNca,
TooltipText = "Extract the RomFs section present in the main NCA"
TooltipText = "Extract the RomFS section from Application's current config (including updates)."
};
MenuItem extractExeFs = new MenuItem("ExeFS")
{
Sensitive = hasNca,
TooltipText = "Extract the ExeFs section present in the main NCA"
TooltipText = "Extract the ExeFS section from Application's current config (including updates)."
};
MenuItem extractLogo = new MenuItem("Logo")
{
Sensitive = hasNca,
TooltipText = "Extract the Logo section present in the main NCA"
TooltipText = "Extract the Logo section from Application's current config (including updates)."
};
Menu extractSubMenu = new Menu();
@ -103,14 +108,14 @@ namespace Ryujinx.Ui
MenuItem managePtcMenu = new MenuItem("Cache Management");
MenuItem purgePtcCache = new MenuItem("Purge the PPTC cache")
MenuItem purgePtcCache = new MenuItem("Purge PPTC cache")
{
TooltipText = "Delete the PPTC cache of the game"
TooltipText = "Delete the Application's PPTC cache."
};
MenuItem openPtcDir = new MenuItem("Open the PPTC directory")
MenuItem openPtcDir = new MenuItem("Open PPTC directory")
{
TooltipText = "Open the PPTC directory in the file explorer"
TooltipText = "Open the directory which contains Application's PPTC cache."
};
Menu managePtcSubMenu = new Menu();
@ -125,6 +130,7 @@ namespace Ryujinx.Ui
openSaveBcatDir.Activated += OpenSaveBcatDir_Clicked;
manageTitleUpdates.Activated += ManageTitleUpdates_Clicked;
manageDlc.Activated += ManageDlc_Clicked;
openTitleModDir.Activated += OpenTitleModDir_Clicked;
extractRomFs.Activated += ExtractRomFs_Clicked;
extractExeFs.Activated += ExtractExeFs_Clicked;
extractLogo.Activated += ExtractLogo_Clicked;
@ -137,6 +143,7 @@ namespace Ryujinx.Ui
this.Add(new SeparatorMenuItem());
this.Add(manageTitleUpdates);
this.Add(manageDlc);
this.Add(openTitleModDir);
this.Add(new SeparatorMenuItem());
this.Add(managePtcMenu);
this.Add(extractMenu);
@ -602,6 +609,21 @@ namespace Ryujinx.Ui
dlcWindow.Show();
}
private void OpenTitleModDir_Clicked(object sender, EventArgs args)
{
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
var modsBasePath = _virtualFileSystem.GetBaseModsPath();
var titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId);
Process.Start(new ProcessStartInfo
{
FileName = titleModsPath,
UseShellExecute = true,
Verb = "open"
});
}
private void ExtractRomFs_Clicked(object sender, EventArgs args)
{
ExtractSection(NcaSectionType.Data);