diff --git a/Ryujinx.HLE/FileSystem/SaveDataType.cs b/Ryujinx.HLE/FileSystem/SaveDataType.cs new file mode 100644 index 000000000..edfe8ab1d --- /dev/null +++ b/Ryujinx.HLE/FileSystem/SaveDataType.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.FileSystem +{ + enum SaveDataType : byte + { + SystemSaveData, + SaveData, + BcatDeliveryCacheStorage, + DeviceSaveData, + TemporaryStorage, + CacheStorage + } +} diff --git a/Ryujinx.HLE/FileSystem/SaveHelper.cs b/Ryujinx.HLE/FileSystem/SaveHelper.cs new file mode 100644 index 000000000..67f010169 --- /dev/null +++ b/Ryujinx.HLE/FileSystem/SaveHelper.cs @@ -0,0 +1,46 @@ +using Ryujinx.HLE.HOS; +using System.IO; + +using static Ryujinx.HLE.FileSystem.VirtualFileSystem; + +namespace Ryujinx.HLE.FileSystem +{ + static class SaveHelper + { + public static string GetSavePath(SaveInfo SaveMetaData, ServiceCtx Context) + { + string BaseSavePath = NandPath; + long CurrentTitleId = SaveMetaData.TitleId; + + switch (SaveMetaData.SaveSpaceId) + { + case SaveSpaceId.NandUser: + BaseSavePath = UserNandPath; + break; + case SaveSpaceId.NandSystem: + BaseSavePath = SystemNandPath; + break; + case SaveSpaceId.SdCard: + BaseSavePath = Path.Combine(SdCardPath, "Nintendo"); + break; + } + + BaseSavePath = Path.Combine(BaseSavePath, "save"); + + if (SaveMetaData.TitleId == 0 && SaveMetaData.SaveDataType == SaveDataType.SaveData) + { + if (Context.Process.MetaData != null) + { + CurrentTitleId = Context.Process.MetaData.ACI0.TitleId; + } + } + + string SavePath = Path.Combine(BaseSavePath, + SaveMetaData.SaveId.ToString("x16"), + SaveMetaData.UserId.ToString(), + SaveMetaData.SaveDataType == SaveDataType.SaveData ? CurrentTitleId.ToString("x16") : string.Empty); + + return SavePath; + } + } +} diff --git a/Ryujinx.HLE/FileSystem/SaveInfo.cs b/Ryujinx.HLE/FileSystem/SaveInfo.cs new file mode 100644 index 000000000..f3790ec78 --- /dev/null +++ b/Ryujinx.HLE/FileSystem/SaveInfo.cs @@ -0,0 +1,28 @@ +using Ryujinx.HLE.HOS.SystemState; + +namespace Ryujinx.HLE.FileSystem +{ + struct SaveInfo + { + public long TitleId { get; private set; } + public long SaveId { get; private set; } + public UserId UserId { get; private set; } + + public SaveDataType SaveDataType { get; private set; } + public SaveSpaceId SaveSpaceId { get; private set; } + + public SaveInfo( + long TitleId, + long SaveId, + SaveDataType SaveDataType, + UserId UserId, + SaveSpaceId SaveSpaceId) + { + this.TitleId = TitleId; + this.UserId = UserId; + this.SaveId = SaveId; + this.SaveDataType = SaveDataType; + this.SaveSpaceId = SaveSpaceId; + } + } +} diff --git a/Ryujinx.HLE/FileSystem/SaveSpaceId.cs b/Ryujinx.HLE/FileSystem/SaveSpaceId.cs new file mode 100644 index 000000000..5a2b32d63 --- /dev/null +++ b/Ryujinx.HLE/FileSystem/SaveSpaceId.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.FileSystem +{ + enum SaveSpaceId : byte + { + NandSystem, + NandUser, + SdCard, + TemporaryStorage + } +} diff --git a/Ryujinx.HLE/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs similarity index 82% rename from Ryujinx.HLE/VirtualFileSystem.cs rename to Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 133538f93..e621ec2b1 100644 --- a/Ryujinx.HLE/VirtualFileSystem.cs +++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -1,14 +1,18 @@ +using Ryujinx.HLE.HOS; using System; using System.IO; -namespace Ryujinx.HLE +namespace Ryujinx.HLE.FileSystem { class VirtualFileSystem : IDisposable { - private const string BasePath = "RyuFs"; - private const string NandPath = "nand"; - private const string SdCardPath = "sdmc"; - private const string SystemPath = "system"; + public const string BasePath = "RyuFs"; + public const string NandPath = "nand"; + public const string SdCardPath = "sdmc"; + public const string SystemPath = "system"; + + public static string SystemNandPath = Path.Combine(NandPath, "system"); + public static string UserNandPath = Path.Combine(NandPath, "user"); public Stream RomFs { get; private set; } @@ -50,10 +54,15 @@ namespace Ryujinx.HLE public string GetSdCardPath() => MakeDirAndGetFullPath(SdCardPath); - public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath); + public string GetNandPath() => MakeDirAndGetFullPath(NandPath); public string GetSystemPath() => MakeDirAndGetFullPath(SystemPath); + public string GetGameSavePath(SaveInfo Save, ServiceCtx Context) + { + return MakeDirAndGetFullPath(SaveHelper.GetSavePath(Save, Context)); + } + public string SwitchPathToSystemPath(string SwitchPath) { string[] Parts = SwitchPath.Split(":"); diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 92a87661d..2e216cdf1 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS } } - if (!MainProcess.MetaData.Is64Bits) + if (!(MainProcess.MetaData?.Is64Bits ?? true)) { throw new NotImplementedException("32-bit titles are unsupported!"); } diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs index 14edcc759..937ea6d6b 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs @@ -1,5 +1,6 @@ +using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; +using Ryujinx.HLE.HOS.SystemState; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.FspSrv @@ -14,13 +15,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { m_Commands = new Dictionary() { - { 1, SetCurrentProcess }, - { 18, OpenSdCardFileSystem }, - { 22, CreateSaveDataFileSystem }, - { 51, OpenSaveDataFileSystem }, - { 200, OpenDataStorageByCurrentProcess }, - { 203, OpenPatchDataStorageByCurrentProcess }, - { 1005, GetGlobalAccessLogMode } + { 1, SetCurrentProcess }, + { 18, OpenSdCardFileSystem }, + { 51, OpenSaveDataFileSystem }, + { 52, OpenSaveDataFileSystemBySystemSaveDataId }, + { 200, OpenDataStorageByCurrentProcess }, + { 203, OpenPatchDataStorageByCurrentProcess }, + { 1005, GetGlobalAccessLogMode } }; } @@ -36,16 +37,16 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } - public long CreateSaveDataFileSystem(ServiceCtx Context) + public long OpenSaveDataFileSystem(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceFs, "Stubbed."); + LoadSaveDataFileSystem(Context); return 0; } - public long OpenSaveDataFileSystem(ServiceCtx Context) + public long OpenSaveDataFileSystemBySystemSaveDataId(ServiceCtx Context) { - MakeObject(Context, new IFileSystem(Context.Device.FileSystem.GetGameSavesPath())); + LoadSaveDataFileSystem(Context); return 0; } @@ -70,5 +71,24 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + + public void LoadSaveDataFileSystem(ServiceCtx Context) + { + SaveSpaceId SaveSpaceId = (SaveSpaceId)Context.RequestData.ReadInt64(); + + long TitleId = Context.RequestData.ReadInt64(); + + UserId UserId = new UserId( + Context.RequestData.ReadInt64(), + Context.RequestData.ReadInt64()); + + long SaveId = Context.RequestData.ReadInt64(); + + SaveDataType SaveDataType = (SaveDataType)Context.RequestData.ReadByte(); + + SaveInfo SaveInfo = new SaveInfo(TitleId, SaveId, SaveDataType, UserId, SaveSpaceId); + + MakeObject(Context, new IFileSystem(Context.Device.FileSystem.GetGameSavePath(SaveInfo, Context))); + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 090aae111..70bd7060e 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -1,6 +1,7 @@ using Ryujinx.Audio; using Ryujinx.Graphics; using Ryujinx.Graphics.Gal; +using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.Input; using Ryujinx.HLE.Logging;