aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs51
-rw-r--r--Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs30
-rw-r--r--Ryujinx/Ui/GLScreen.cs6
-rw-r--r--Ryujinx/Ui/MainWindow.cs6
5 files changed, 85 insertions, 14 deletions
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 855d8914..428e1922 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -1,9 +1,11 @@
using LibHac;
+using LibHac.Account;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsService;
using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils;
+using LibHac.Ncm;
using LibHac.Ns;
using LibHac.Spl;
using Ryujinx.Common.Logging;
@@ -32,6 +34,8 @@ using System.Threading;
using TimeServiceManager = Ryujinx.HLE.HOS.Services.Time.TimeManager;
using NxStaticObject = Ryujinx.HLE.Loaders.Executables.NxStaticObject;
+using static LibHac.Fs.ApplicationSaveDataManagement;
+
namespace Ryujinx.HLE.HOS
{
public class Horizon : IDisposable
@@ -109,7 +113,8 @@ namespace Ryujinx.HLE.HOS
public string TitleName { get; private set; }
- public string TitleId { get; private set; }
+ public ulong TitleId { get; private set; }
+ public string TitleIdText => TitleId.ToString("x16");
public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
@@ -513,7 +518,7 @@ namespace Ryujinx.HLE.HOS
LoadExeFs(codeFs, out Npdm metaData);
- TitleId = metaData.Aci0.TitleId.ToString("x16");
+ TitleId = metaData.Aci0.TitleId;
if (controlNca != null)
{
@@ -523,6 +528,11 @@ namespace Ryujinx.HLE.HOS
{
ControlData.ByteSpan.Clear();
}
+
+ if (TitleId != 0)
+ {
+ EnsureSaveData(new TitleId(TitleId));
+ }
}
private void LoadExeFs(IFileSystem codeFs, out Npdm metaData)
@@ -561,7 +571,7 @@ namespace Ryujinx.HLE.HOS
}
}
- TitleId = metaData.Aci0.TitleId.ToString("x16");
+ TitleId = metaData.Aci0.TitleId;
LoadNso("rtld");
LoadNso("main");
@@ -664,7 +674,7 @@ namespace Ryujinx.HLE.HOS
ContentManager.LoadEntries();
TitleName = metaData.TitleName;
- TitleId = metaData.Aci0.TitleId.ToString("x16");
+ TitleId = metaData.Aci0.TitleId;
ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject });
}
@@ -679,6 +689,39 @@ namespace Ryujinx.HLE.HOS
}
}
+ private Result EnsureSaveData(TitleId titleId)
+ {
+ Logger.PrintInfo(LogClass.Application, "Ensuring required savedata exists.");
+
+ UInt128 lastOpenedUser = State.Account.LastOpenedUser.UserId;
+ Uid user = new Uid((ulong)lastOpenedUser.Low, (ulong)lastOpenedUser.High);
+
+ ref ApplicationControlProperty control = ref ControlData.Value;
+
+ if (LibHac.Util.IsEmpty(ControlData.ByteSpan))
+ {
+ // If the current application doesn't have a loaded control property, create a dummy one
+ // and set the savedata sizes so a user savedata will be created.
+ control = ref new BlitStruct<ApplicationControlProperty>(1).Value;
+
+ // The set sizes don't actually matter as long as they're non-zero because we use directory savedata.
+ control.UserAccountSaveDataSize = 0x4000;
+ control.UserAccountSaveDataJournalSize = 0x4000;
+
+ Logger.PrintWarning(LogClass.Application,
+ "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
+ }
+
+ Result rc = EnsureApplicationSaveData(FsClient, out _, titleId, ref ControlData.Value, ref user);
+
+ if (rc.IsFailure())
+ {
+ Logger.PrintError(LogClass.Application, $"Error calling EnsureApplicationSaveData. Result code {rc.ToStringWithName()}");
+ }
+
+ return rc;
+ }
+
public void LoadKeySet()
{
string keyFile = null;
diff --git a/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs b/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs
index 4962e3ff..686577d7 100644
--- a/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs
+++ b/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs
@@ -1,12 +1,10 @@
using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.Utilities;
-using System;
namespace Ryujinx.HLE.HOS.Services.Arp
{
class ApplicationLaunchProperty
{
- public long TitleId;
+ public ulong TitleId;
public int Version;
public byte BaseGameStorageId;
public byte UpdateGameStorageId;
@@ -33,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp
return new ApplicationLaunchProperty
{
- TitleId = BitConverter.ToInt64(StringUtils.HexToBytes(context.Device.System.TitleId), 0),
+ TitleId = context.Device.System.TitleId,
Version = 0x00,
BaseGameStorageId = (byte)StorageId.NandSystem,
UpdateGameStorageId = (byte)StorageId.None
diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs
index 60f4a3f4..4e967886 100644
--- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs
+++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs
@@ -133,6 +133,20 @@ namespace Ryujinx.HLE.HOS.Services.Fs
SaveDataCreateInfo createInfo = context.RequestData.ReadStruct<SaveDataCreateInfo>();
SaveMetaCreateInfo metaCreateInfo = context.RequestData.ReadStruct<SaveMetaCreateInfo>();
+ // TODO: There's currently no program registry for FS to reference.
+ // Workaround that by setting the application ID and owner ID if they're not already set
+ if (attribute.TitleId == TitleId.Zero)
+ {
+ attribute.TitleId = new TitleId(context.Process.TitleId);
+ }
+
+ if (createInfo.OwnerId == TitleId.Zero)
+ {
+ createInfo.OwnerId = new TitleId(context.Process.TitleId);
+ }
+
+ Logger.PrintInfo(LogClass.ServiceFs, $"Creating save with title ID {attribute.TitleId.Value:x16}");
+
Result result = _baseFileSystemProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaCreateInfo);
return (ResultCode)result.Value;
@@ -196,6 +210,18 @@ namespace Ryujinx.HLE.HOS.Services.Fs
SaveMetaCreateInfo metaCreateInfo = context.RequestData.ReadStruct<SaveMetaCreateInfo>();
HashSalt hashSalt = context.RequestData.ReadStruct<HashSalt>();
+ // TODO: There's currently no program registry for FS to reference.
+ // Workaround that by setting the application ID and owner ID if they're not already set
+ if (attribute.TitleId == TitleId.Zero)
+ {
+ attribute.TitleId = new TitleId(context.Process.TitleId);
+ }
+
+ if (createInfo.OwnerId == TitleId.Zero)
+ {
+ createInfo.OwnerId = new TitleId(context.Process.TitleId);
+ }
+
Result result = _baseFileSystemProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref createInfo, ref metaCreateInfo, ref hashSalt);
return (ResultCode)result.Value;
@@ -208,6 +234,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>();
+ // TODO: There's currently no program registry for FS to reference.
+ // Workaround that by setting the application ID if it's not already set
if (attribute.TitleId == TitleId.Zero)
{
attribute.TitleId = new TitleId(context.Process.TitleId);
@@ -247,6 +275,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>();
+ // TODO: There's currently no program registry for FS to reference.
+ // Workaround that by setting the application ID if it's not already set
if (attribute.TitleId == TitleId.Zero)
{
attribute.TitleId = new TitleId(context.Process.TitleId);
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index 6c0c2858..8e391262 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -307,10 +307,10 @@ namespace Ryujinx.Ui
string titleNameSection = string.IsNullOrWhiteSpace(_device.System.TitleName) ? string.Empty
: " | " + _device.System.TitleName;
- string titleIDSection = string.IsNullOrWhiteSpace(_device.System.TitleId) ? string.Empty
- : " | " + _device.System.TitleId.ToUpper();
+ string titleIdSection = string.IsNullOrWhiteSpace(_device.System.TitleIdText) ? string.Empty
+ : " | " + _device.System.TitleIdText.ToUpper();
- _newTitle = $"Ryujinx{titleNameSection}{titleIDSection} | Host FPS: {hostFps:0.0} | Game FPS: {gameFps:0.0} | " +
+ _newTitle = $"Ryujinx{titleNameSection}{titleIdSection} | Host FPS: {hostFps:0.0} | Game FPS: {gameFps:0.0} | " +
$"Game Vsync: {(_device.EnableDeviceVsync ? "On" : "Off")}";
_titleEvent = true;
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index c914a4a7..af7dd524 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -305,9 +305,9 @@ namespace Ryujinx.Ui
_firmwareInstallFile.Sensitive = false;
_firmwareInstallDirectory.Sensitive = false;
- DiscordIntegrationModule.SwitchToPlayingState(_device.System.TitleId, _device.System.TitleName);
+ DiscordIntegrationModule.SwitchToPlayingState(_device.System.TitleIdText, _device.System.TitleName);
- ApplicationLibrary.LoadAndSaveMetaData(_device.System.TitleId, appMetadata =>
+ ApplicationLibrary.LoadAndSaveMetaData(_device.System.TitleIdText, appMetadata =>
{
appMetadata.LastPlayed = DateTime.UtcNow.ToString();
});
@@ -337,7 +337,7 @@ namespace Ryujinx.Ui
if (_gameLoaded)
{
- ApplicationLibrary.LoadAndSaveMetaData(_device.System.TitleId, appMetadata =>
+ ApplicationLibrary.LoadAndSaveMetaData(_device.System.TitleIdText, appMetadata =>
{
DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed);
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;