Rename "RyuFs" directory to "Ryujinx" and use the same savedata system the Switch uses (#801)
* Use savedata FS commands from LibHac * Add EnsureSaveData. Use ApplicationControlProperty struct * Add a function to migrate to the new directory layout * LibHac update * Change backup structure * Don't create UI files in the save path * Update RyuFs paths * Add GetProgramIndexForAccessLog Ryujinx only runs one program at a time, so always return values reflecting that * Load control NCA when loading from an NSP * Skip over UI stats when exiting * Set TitleName and TitleId in more cases. Fix TitleID naming style * Completely comment out GUI play stats code * rebase * Update LibHac * Update LibHac * Revert UI changes * Do migration automatically at startup * Rename RyuFs directory to Ryujinx * Update RyuFs text * Store savedata paths in the GUI * Make "Open Save Directory" work * Use a dummy NACP in EnsureSaveData if one is not loaded * Remove manual migration button * Respond to feedback * Don't read the installer config to get a version string * Delete nuget.config * Exclude 'sdcard' and 'bis' during migration Co-authored-by: Thog <thog@protonmail.com>
This commit is contained in:
parent
e0e12b1672
commit
63b24b4af2
22 changed files with 877 additions and 384 deletions
|
@ -287,7 +287,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||
// Account actually calls nn::arp::detail::IReader::GetApplicationControlProperty() with the current PID and store the result (NACP File) internally.
|
||||
// But since we use LibHac and we load one Application at a time, it's not necessary.
|
||||
|
||||
context.ResponseData.Write(context.Device.System.ControlData.UserAccountSwitchLock);
|
||||
context.ResponseData.Write(context.Device.System.ControlData.Value.UserAccountSwitchLock);
|
||||
|
||||
Logger.PrintStub(LogClass.ServiceAcc);
|
||||
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
using LibHac;
|
||||
using LibHac.Account;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage;
|
||||
using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System;
|
||||
|
||||
using static LibHac.Fs.ApplicationSaveDataManagement;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy
|
||||
{
|
||||
class IApplicationFunctions : IpcService
|
||||
|
@ -24,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||
public ResultCode PopLaunchParameter(ServiceCtx context)
|
||||
{
|
||||
// Only the first 0x18 bytes of the Data seems to be actually used.
|
||||
MakeObject(context, new IStorage(StorageHelper.MakeLaunchParams()));
|
||||
MakeObject(context, new AppletAE.IStorage(StorageHelper.MakeLaunchParams()));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@ -33,13 +39,33 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||
// EnsureSaveData(nn::account::Uid) -> u64
|
||||
public ResultCode EnsureSaveData(ServiceCtx context)
|
||||
{
|
||||
UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10));
|
||||
Uid userId = context.RequestData.ReadStruct<Uid>();
|
||||
TitleId titleId = new TitleId(context.Process.TitleId);
|
||||
|
||||
context.ResponseData.Write(0L);
|
||||
BlitStruct<ApplicationControlProperty> controlHolder = context.Device.System.ControlData;
|
||||
|
||||
Logger.PrintStub(LogClass.ServiceAm, new { userId });
|
||||
ref ApplicationControlProperty control = ref controlHolder.Value;
|
||||
|
||||
return ResultCode.Success;
|
||||
if (Util.IsEmpty(controlHolder.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.ServiceAm,
|
||||
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
|
||||
}
|
||||
|
||||
Result result = EnsureApplicationSaveData(context.Device.System.FsClient, out long requiredSize, titleId,
|
||||
ref context.Device.System.ControlData.Value, ref userId);
|
||||
|
||||
context.ResponseData.Write(requiredSize);
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(21)]
|
||||
|
|
|
@ -3,54 +3,12 @@ using LibHac.Fs;
|
|||
using LibHac.FsSystem;
|
||||
using LibHac.FsSystem.NcaUtils;
|
||||
using LibHac.Spl;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
||||
{
|
||||
static class FileSystemProxyHelper
|
||||
{
|
||||
public static ResultCode LoadSaveDataFileSystem(ServiceCtx context, bool readOnly, out IFileSystem loadedFileSystem)
|
||||
{
|
||||
loadedFileSystem = null;
|
||||
|
||||
SaveSpaceId saveSpaceId = (SaveSpaceId)context.RequestData.ReadInt64();
|
||||
ulong titleId = context.RequestData.ReadUInt64();
|
||||
UInt128 userId = context.RequestData.ReadStruct<UInt128>();
|
||||
long saveId = context.RequestData.ReadInt64();
|
||||
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadByte();
|
||||
SaveInfo saveInfo = new SaveInfo(titleId, saveId, saveDataType, saveSpaceId, userId);
|
||||
string savePath = context.Device.FileSystem.GetSavePath(context, saveInfo);
|
||||
|
||||
try
|
||||
{
|
||||
LocalFileSystem fileSystem = new LocalFileSystem(savePath);
|
||||
|
||||
Result result = DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem dirFileSystem, fileSystem);
|
||||
if (result.IsFailure())
|
||||
{
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
LibHac.Fs.IFileSystem saveFileSystem = dirFileSystem;
|
||||
|
||||
if (readOnly)
|
||||
{
|
||||
saveFileSystem = new ReadOnlyFileSystem(saveFileSystem);
|
||||
}
|
||||
|
||||
loadedFileSystem = new IFileSystem(saveFileSystem);
|
||||
}
|
||||
catch (HorizonResultException ex)
|
||||
{
|
||||
return (ResultCode)ex.ResultValue.Value;
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public static ResultCode OpenNsp(ServiceCtx context, string pfsPath, out IFileSystem openedFileSystem)
|
||||
{
|
||||
openedFileSystem = null;
|
||||
|
@ -154,5 +112,15 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Result ReadFsPath(out FsPath path, ServiceCtx context, int index = 0)
|
||||
{
|
||||
long position = context.Request.SendBuff[index].Position;
|
||||
long size = context.Request.SendBuff[index].Size;
|
||||
|
||||
byte[] pathBytes = context.Memory.ReadBytes(position, size);
|
||||
|
||||
return FsPath.FromSpan(out path, pathBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,14 @@ using LibHac.Fs;
|
|||
using LibHac.FsService;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSystem.NcaUtils;
|
||||
using LibHac.Ncm;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
|
||||
using System.IO;
|
||||
|
||||
using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
|
||||
using static Ryujinx.HLE.Utilities.StringUtils;
|
||||
using StorageId = Ryujinx.HLE.FileSystem.StorageId;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Fs
|
||||
{
|
||||
|
@ -90,29 +91,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||
// OpenBisFileSystem(nn::fssrv::sf::Partition partitionID, buffer<bytes<0x301>, 0x19, 0x301>) -> object<nn::fssrv::sf::IFileSystem> Bis
|
||||
public ResultCode OpenBisFileSystem(ServiceCtx context)
|
||||
{
|
||||
int bisPartitionId = context.RequestData.ReadInt32();
|
||||
string partitionString = ReadUtf8String(context);
|
||||
string bisPartitionPath = string.Empty;
|
||||
BisPartitionId bisPartitionId = (BisPartitionId)context.RequestData.ReadInt32();
|
||||
|
||||
switch (bisPartitionId)
|
||||
{
|
||||
case 29:
|
||||
bisPartitionPath = SafeNandPath;
|
||||
break;
|
||||
case 30:
|
||||
case 31:
|
||||
bisPartitionPath = SystemNandPath;
|
||||
break;
|
||||
case 32:
|
||||
bisPartitionPath = UserNandPath;
|
||||
break;
|
||||
default:
|
||||
return ResultCode.InvalidInput;
|
||||
}
|
||||
Result rc = FileSystemProxyHelper.ReadFsPath(out FsPath path, context);
|
||||
if (rc.IsFailure()) return (ResultCode)rc.Value;
|
||||
|
||||
string fullPath = context.Device.FileSystem.GetFullPartitionPath(bisPartitionPath);
|
||||
|
||||
LocalFileSystem fileSystem = new LocalFileSystem(fullPath);
|
||||
rc = _baseFileSystemProxy.OpenBisFileSystem(out LibHac.Fs.IFileSystem fileSystem, ref path, bisPartitionId);
|
||||
if (rc.IsFailure()) return (ResultCode)rc.Value;
|
||||
|
||||
MakeObject(context, new FileSystemProxy.IFileSystem(fileSystem));
|
||||
|
||||
|
@ -123,15 +108,69 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||
// OpenSdCardFileSystem() -> object<nn::fssrv::sf::IFileSystem>
|
||||
public ResultCode OpenSdCardFileSystem(ServiceCtx context)
|
||||
{
|
||||
string sdCardPath = context.Device.FileSystem.GetSdCardPath();
|
||||
|
||||
LocalFileSystem fileSystem = new LocalFileSystem(sdCardPath);
|
||||
Result rc = _baseFileSystemProxy.OpenSdCardFileSystem(out LibHac.Fs.IFileSystem fileSystem);
|
||||
if (rc.IsFailure()) return (ResultCode)rc.Value;
|
||||
|
||||
MakeObject(context, new FileSystemProxy.IFileSystem(fileSystem));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[Command(21)]
|
||||
public ResultCode DeleteSaveDataFileSystem(ServiceCtx context)
|
||||
{
|
||||
ulong saveDataId = context.RequestData.ReadUInt64();
|
||||
|
||||
Result result = _baseFileSystemProxy.DeleteSaveDataFileSystem(saveDataId);
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(22)]
|
||||
public ResultCode CreateSaveDataFileSystem(ServiceCtx context)
|
||||
{
|
||||
SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>();
|
||||
SaveDataCreateInfo createInfo = context.RequestData.ReadStruct<SaveDataCreateInfo>();
|
||||
SaveMetaCreateInfo metaCreateInfo = context.RequestData.ReadStruct<SaveMetaCreateInfo>();
|
||||
|
||||
Result result = _baseFileSystemProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaCreateInfo);
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(23)]
|
||||
public ResultCode CreateSaveDataFileSystemBySystemSaveDataId(ServiceCtx context)
|
||||
{
|
||||
SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>();
|
||||
SaveDataCreateInfo createInfo = context.RequestData.ReadStruct<SaveDataCreateInfo>();
|
||||
|
||||
Result result = _baseFileSystemProxy.CreateSaveDataFileSystemBySystemSaveDataId(ref attribute, ref createInfo);
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(25)]
|
||||
public ResultCode DeleteSaveDataFileSystemBySaveDataSpaceId(ServiceCtx context)
|
||||
{
|
||||
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
|
||||
ulong saveDataId = context.RequestData.ReadUInt64();
|
||||
|
||||
Result result = _baseFileSystemProxy.DeleteSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId);
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(28)]
|
||||
public ResultCode DeleteSaveDataFileSystemBySaveDataAttribute(ServiceCtx context)
|
||||
{
|
||||
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
|
||||
SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>();
|
||||
|
||||
Result result = _baseFileSystemProxy.DeleteSaveDataFileSystemBySaveDataAttribute(spaceId, ref attribute);
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(30)]
|
||||
// OpenGameCardStorage(u32, u32) -> object<nn::fssrv::sf::IStorage>
|
||||
public ResultCode OpenGameCardStorage(ServiceCtx context)
|
||||
|
@ -149,46 +188,141 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(35)]
|
||||
public ResultCode CreateSaveDataFileSystemWithHashSalt(ServiceCtx context)
|
||||
{
|
||||
SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>();
|
||||
SaveDataCreateInfo createInfo = context.RequestData.ReadStruct<SaveDataCreateInfo>();
|
||||
SaveMetaCreateInfo metaCreateInfo = context.RequestData.ReadStruct<SaveMetaCreateInfo>();
|
||||
HashSalt hashSalt = context.RequestData.ReadStruct<HashSalt>();
|
||||
|
||||
Result result = _baseFileSystemProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref createInfo, ref metaCreateInfo, ref hashSalt);
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(51)]
|
||||
// OpenSaveDataFileSystem(u8 save_data_space_id, nn::fssrv::sf::SaveStruct saveStruct) -> object<nn::fssrv::sf::IFileSystem> saveDataFs
|
||||
public ResultCode OpenSaveDataFileSystem(ServiceCtx context)
|
||||
{
|
||||
ResultCode result = FileSystemProxyHelper.LoadSaveDataFileSystem(context, false, out FileSystemProxy.IFileSystem fileSystem);
|
||||
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
|
||||
SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>();
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
if (attribute.TitleId == TitleId.Zero)
|
||||
{
|
||||
MakeObject(context, fileSystem);
|
||||
attribute.TitleId = new TitleId(context.Process.TitleId);
|
||||
}
|
||||
|
||||
return result;
|
||||
Result result = _baseFileSystemProxy.OpenSaveDataFileSystem(out LibHac.Fs.IFileSystem fileSystem, spaceId, ref attribute);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
MakeObject(context, new FileSystemProxy.IFileSystem(fileSystem));
|
||||
}
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(52)]
|
||||
// OpenSaveDataFileSystemBySystemSaveDataId(u8 save_data_space_id, nn::fssrv::sf::SaveStruct saveStruct) -> object<nn::fssrv::sf::IFileSystem> systemSaveDataFs
|
||||
public ResultCode OpenSaveDataFileSystemBySystemSaveDataId(ServiceCtx context)
|
||||
{
|
||||
ResultCode result = FileSystemProxyHelper.LoadSaveDataFileSystem(context, false, out FileSystemProxy.IFileSystem fileSystem);
|
||||
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
|
||||
SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>();
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
Result result = _baseFileSystemProxy.OpenSaveDataFileSystemBySystemSaveDataId(out LibHac.Fs.IFileSystem fileSystem, spaceId, ref attribute);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
MakeObject(context, fileSystem);
|
||||
MakeObject(context, new FileSystemProxy.IFileSystem(fileSystem));
|
||||
}
|
||||
|
||||
return result;
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(53)]
|
||||
// OpenReadOnlySaveDataFileSystem(u8 save_data_space_id, nn::fssrv::sf::SaveStruct save_struct) -> object<nn::fssrv::sf::IFileSystem>
|
||||
public ResultCode OpenReadOnlySaveDataFileSystem(ServiceCtx context)
|
||||
{
|
||||
ResultCode result = FileSystemProxyHelper.LoadSaveDataFileSystem(context, true, out FileSystemProxy.IFileSystem fileSystem);
|
||||
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
|
||||
SaveDataAttribute attribute = context.RequestData.ReadStruct<SaveDataAttribute>();
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
if (attribute.TitleId == TitleId.Zero)
|
||||
{
|
||||
MakeObject(context, fileSystem);
|
||||
attribute.TitleId = new TitleId(context.Process.TitleId);
|
||||
}
|
||||
|
||||
return result;
|
||||
Result result = _baseFileSystemProxy.OpenReadOnlySaveDataFileSystem(out LibHac.Fs.IFileSystem fileSystem, spaceId, ref attribute);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
MakeObject(context, new FileSystemProxy.IFileSystem(fileSystem));
|
||||
}
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(60)]
|
||||
public ResultCode OpenSaveDataInfoReader(ServiceCtx context)
|
||||
{
|
||||
Result result = _baseFileSystemProxy.OpenSaveDataInfoReader(out LibHac.FsService.ISaveDataInfoReader infoReader);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
MakeObject(context, new ISaveDataInfoReader(infoReader));
|
||||
}
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(61)]
|
||||
public ResultCode OpenSaveDataInfoReaderBySaveDataSpaceId(ServiceCtx context)
|
||||
{
|
||||
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadByte();
|
||||
|
||||
Result result = _baseFileSystemProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(out LibHac.FsService.ISaveDataInfoReader infoReader, spaceId);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
MakeObject(context, new ISaveDataInfoReader(infoReader));
|
||||
}
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(67)]
|
||||
public ResultCode FindSaveDataWithFilter(ServiceCtx context)
|
||||
{
|
||||
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
|
||||
SaveDataFilter filter = context.RequestData.ReadStruct<SaveDataFilter>();
|
||||
|
||||
long bufferPosition = context.Request.ReceiveBuff[0].Position;
|
||||
long bufferLen = context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
byte[] infoBuffer = new byte[bufferLen];
|
||||
|
||||
Result result = _baseFileSystemProxy.FindSaveDataWithFilter(out long count, infoBuffer, spaceId, ref filter);
|
||||
|
||||
context.Memory.WriteBytes(bufferPosition, infoBuffer);
|
||||
context.ResponseData.Write(count);
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(68)]
|
||||
public ResultCode OpenSaveDataInfoReaderWithFilter(ServiceCtx context)
|
||||
{
|
||||
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
|
||||
SaveDataFilter filter = context.RequestData.ReadStruct<SaveDataFilter>();
|
||||
|
||||
Result result = _baseFileSystemProxy.OpenSaveDataInfoReaderWithFilter(out LibHac.FsService.ISaveDataInfoReader infoReader, spaceId, ref filter);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
MakeObject(context, new ISaveDataInfoReader(infoReader));
|
||||
}
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
[Command(200)]
|
||||
|
@ -306,5 +440,17 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[Command(1011)]
|
||||
public ResultCode GetProgramIndexForAccessLog(ServiceCtx context)
|
||||
{
|
||||
int programIndex = 0;
|
||||
int programCount = 1;
|
||||
|
||||
context.ResponseData.Write(programIndex);
|
||||
context.ResponseData.Write(programCount);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
31
Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs
Normal file
31
Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using LibHac;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Fs
|
||||
{
|
||||
class ISaveDataInfoReader : IpcService
|
||||
{
|
||||
private LibHac.FsService.ISaveDataInfoReader _baseReader;
|
||||
|
||||
public ISaveDataInfoReader(LibHac.FsService.ISaveDataInfoReader baseReader)
|
||||
{
|
||||
_baseReader = baseReader;
|
||||
}
|
||||
|
||||
[Command(0)]
|
||||
// ReadSaveDataInfo() -> (u64, buffer<unknown, 6>)
|
||||
public ResultCode ReadSaveDataInfo(ServiceCtx context)
|
||||
{
|
||||
long bufferPosition = context.Request.ReceiveBuff[0].Position;
|
||||
long bufferLen = context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
byte[] infoBuffer = new byte[bufferLen];
|
||||
|
||||
Result result = _baseReader.ReadSaveDataInfo(out long readCount, infoBuffer);
|
||||
|
||||
context.Memory.WriteBytes(bufferPosition, infoBuffer);
|
||||
context.ResponseData.Write(readCount);
|
||||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,4 @@
|
|||
using LibHac;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns
|
||||
{
|
||||
[Service("ns:am")]
|
||||
class IApplicationManagerInterface : IpcService
|
||||
|
@ -10,201 +6,17 @@ namespace Ryujinx.HLE.HOS.Services.Ns
|
|||
public IApplicationManagerInterface(ServiceCtx context) { }
|
||||
|
||||
[Command(400)]
|
||||
// GetApplicationControlData(unknown<0x10>) -> (unknown<4>, buffer<unknown, 6>)
|
||||
// GetApplicationControlData(u8, u64) -> (unknown<4>, buffer<unknown, 6>)
|
||||
public ResultCode GetApplicationControlData(ServiceCtx context)
|
||||
{
|
||||
byte source = (byte)context.RequestData.ReadInt64();
|
||||
ulong titleId = (byte)context.RequestData.ReadUInt64();
|
||||
|
||||
long position = context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
Nacp nacp = context.Device.System.ControlData;
|
||||
byte[] nacpData = context.Device.System.ControlData.ByteSpan.ToArray();
|
||||
|
||||
for (int i = 0; i < 0x10; i++)
|
||||
{
|
||||
NacpDescription description = nacp.Descriptions[i];
|
||||
|
||||
byte[] titleData = new byte[0x200];
|
||||
byte[] developerData = new byte[0x100];
|
||||
|
||||
if (description !=null && description.Title != null)
|
||||
{
|
||||
byte[] titleDescriptionData = Encoding.ASCII.GetBytes(description.Title);
|
||||
Buffer.BlockCopy(titleDescriptionData, 0, titleData, 0, titleDescriptionData.Length);
|
||||
|
||||
}
|
||||
|
||||
if (description != null && description.Developer != null)
|
||||
{
|
||||
byte[] developerDescriptionData = Encoding.ASCII.GetBytes(description.Developer);
|
||||
Buffer.BlockCopy(developerDescriptionData, 0, developerData, 0, developerDescriptionData.Length);
|
||||
}
|
||||
|
||||
context.Memory.WriteBytes(position, titleData);
|
||||
context.Memory.WriteBytes(position + 0x200, developerData);
|
||||
|
||||
position += i * 0x300;
|
||||
}
|
||||
|
||||
byte[] isbn = new byte[0x25];
|
||||
|
||||
if (nacp.Isbn != null)
|
||||
{
|
||||
byte[] isbnData = Encoding.ASCII.GetBytes(nacp.Isbn);
|
||||
Buffer.BlockCopy(isbnData, 0, isbn, 0, isbnData.Length);
|
||||
}
|
||||
|
||||
context.Memory.WriteBytes(position, isbn);
|
||||
position += isbn.Length;
|
||||
|
||||
context.Memory.WriteByte(position++, nacp.StartupUserAccount);
|
||||
context.Memory.WriteByte(position++, nacp.UserAccountSwitchLock);
|
||||
context.Memory.WriteByte(position++, nacp.AocRegistrationType);
|
||||
|
||||
context.Memory.WriteInt32(position, nacp.AttributeFlag);
|
||||
position += 4;
|
||||
|
||||
context.Memory.WriteUInt32(position, nacp.SupportedLanguageFlag);
|
||||
position += 4;
|
||||
|
||||
context.Memory.WriteUInt32(position, nacp.ParentalControlFlag);
|
||||
position += 4;
|
||||
|
||||
context.Memory.WriteByte(position++, nacp.Screenshot);
|
||||
context.Memory.WriteByte(position++, nacp.VideoCapture);
|
||||
context.Memory.WriteByte(position++, nacp.DataLossConfirmation);
|
||||
context.Memory.WriteByte(position++, nacp.PlayLogPolicy);
|
||||
|
||||
context.Memory.WriteUInt64(position, nacp.PresenceGroupId);
|
||||
position += 8;
|
||||
|
||||
for (int i = 0; i < nacp.RatingAge.Length; i++)
|
||||
{
|
||||
context.Memory.WriteSByte(position++, nacp.RatingAge[i]);
|
||||
}
|
||||
|
||||
byte[] displayVersion = new byte[0x10];
|
||||
|
||||
if (nacp.DisplayVersion != null)
|
||||
{
|
||||
byte[] displayVersionData = Encoding.ASCII.GetBytes(nacp.DisplayVersion);
|
||||
Buffer.BlockCopy(displayVersionData, 0, displayVersion, 0, displayVersionData.Length);
|
||||
}
|
||||
|
||||
context.Memory.WriteBytes(position, displayVersion);
|
||||
position += displayVersion.Length;
|
||||
|
||||
context.Memory.WriteUInt64(position, nacp.AddOnContentBaseId);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteUInt64(position, nacp.SaveDataOwnerId);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.UserAccountSaveDataSize);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.UserAccountSaveDataJournalSize);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.DeviceSaveDataSize);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.DeviceSaveDataJournalSize);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.BcatDeliveryCacheStorageSize);
|
||||
position += 8;
|
||||
|
||||
byte[] applicationErrorCodeCategory = new byte[0x8];
|
||||
|
||||
if (nacp.ApplicationErrorCodeCategory != null)
|
||||
{
|
||||
byte[] applicationErrorCodeCategoryData = Encoding.ASCII.GetBytes(nacp.ApplicationErrorCodeCategory);
|
||||
Buffer.BlockCopy(applicationErrorCodeCategoryData, 0, applicationErrorCodeCategoryData, 0, applicationErrorCodeCategoryData.Length);
|
||||
}
|
||||
|
||||
context.Memory.WriteBytes(position, applicationErrorCodeCategory);
|
||||
position += applicationErrorCodeCategory.Length;
|
||||
|
||||
for (int i = 0; i < nacp.LocalCommunicationId.Length; i++)
|
||||
{
|
||||
context.Memory.WriteUInt64(position, nacp.LocalCommunicationId[i]);
|
||||
position += 8;
|
||||
}
|
||||
|
||||
context.Memory.WriteByte(position++, nacp.LogoType);
|
||||
context.Memory.WriteByte(position++, nacp.LogoHandling);
|
||||
context.Memory.WriteByte(position++, nacp.RuntimeAddOnContentInstall);
|
||||
|
||||
byte[] reserved000 = new byte[0x3];
|
||||
context.Memory.WriteBytes(position, reserved000);
|
||||
position += reserved000.Length;
|
||||
|
||||
context.Memory.WriteByte(position++, nacp.CrashReport);
|
||||
context.Memory.WriteByte(position++, nacp.Hdcp);
|
||||
context.Memory.WriteUInt64(position, nacp.SeedForPseudoDeviceId);
|
||||
position += 8;
|
||||
|
||||
byte[] bcatPassphrase = new byte[65];
|
||||
if (nacp.BcatPassphrase != null)
|
||||
{
|
||||
byte[] bcatPassphraseData = Encoding.ASCII.GetBytes(nacp.BcatPassphrase);
|
||||
Buffer.BlockCopy(bcatPassphraseData, 0, bcatPassphrase, 0, bcatPassphraseData.Length);
|
||||
}
|
||||
|
||||
context.Memory.WriteBytes(position, bcatPassphrase);
|
||||
position += bcatPassphrase.Length;
|
||||
|
||||
context.Memory.WriteByte(position++, nacp.Reserved01);
|
||||
|
||||
byte[] reserved02 = new byte[0x6];
|
||||
context.Memory.WriteBytes(position, reserved02);
|
||||
position += reserved02.Length;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.UserAccountSaveDataSizeMax);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.UserAccountSaveDataJournalSizeMax);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.DeviceSaveDataSizeMax);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.DeviceSaveDataJournalSizeMax);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.TemporaryStorageSize);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.CacheStorageSize);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.CacheStorageJournalSize);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt64(position, nacp.CacheStorageDataAndJournalSizeMax);
|
||||
position += 8;
|
||||
|
||||
context.Memory.WriteInt16(position, nacp.CacheStorageIndex);
|
||||
position += 2;
|
||||
|
||||
byte[] reserved03 = new byte[0x6];
|
||||
context.Memory.WriteBytes(position, reserved03);
|
||||
position += reserved03.Length;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ulong value = 0;
|
||||
|
||||
if (nacp.PlayLogQueryableApplicationId.Count > i)
|
||||
{
|
||||
value = nacp.PlayLogQueryableApplicationId[i];
|
||||
}
|
||||
|
||||
context.Memory.WriteUInt64(position, value);
|
||||
position += 8;
|
||||
}
|
||||
|
||||
context.Memory.WriteByte(position++, nacp.PlayLogQueryCapability);
|
||||
context.Memory.WriteByte(position++, nacp.RepairFlag);
|
||||
context.Memory.WriteByte(position++, nacp.ProgramIndex);
|
||||
context.Memory.WriteBytes(position, nacpData);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
|
|||
}
|
||||
}
|
||||
|
||||
PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)context.Device.System.ControlData.PlayLogQueryCapability;
|
||||
PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)context.Device.System.ControlData.Value.PlayLogQueryCapability;
|
||||
|
||||
List<ulong> titleIds = new List<ulong>();
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
|
|||
// Check if input title ids are in the whitelist.
|
||||
foreach (ulong titleId in titleIds)
|
||||
{
|
||||
if (!context.Device.System.ControlData.PlayLogQueryableApplicationId.Contains(titleId))
|
||||
if (!context.Device.System.ControlData.Value.PlayLogQueryableApplicationId.Contains(titleId))
|
||||
{
|
||||
return (ResultCode)Am.ResultCode.ObjectInvalid;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue