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:
Alex Barney 2020-01-05 04:49:44 -07:00 committed by Thog
parent e0e12b1672
commit 63b24b4af2
22 changed files with 877 additions and 384 deletions

View file

@ -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);

View file

@ -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)]

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View 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;
}
}
}

View file

@ -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;
}

View file

@ -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;
}