Add EnsureSaveData. Use ApplicationControlProperty struct

This commit is contained in:
Alex Barney 2019-10-29 01:27:15 -05:00
parent 41812939fa
commit 6cb52a5909
5 changed files with 44 additions and 216 deletions

View file

@ -1,8 +1,10 @@
using LibHac;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsService;
using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils;
using LibHac.Ns;
using LibHac.Spl;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem.Content;
@ -103,7 +105,7 @@ namespace Ryujinx.HLE.HOS
private bool _hasStarted;
public Nacp ControlData { get; set; }
public BlitStruct<ApplicationControlProperty> ControlData { get; set; }
public string CurrentTitle { get; private set; }
@ -118,11 +120,13 @@ namespace Ryujinx.HLE.HOS
internal long HidBaseAddress { get; private set; }
internal FileSystemServer FsServer { get; private set; }
internal FileSystemClient FsClient { get; private set; }
internal EmulatedGameCard GameCard { get; private set; }
public Horizon(Switch device)
{
ControlData = new Nacp();
ControlData = new BlitStruct<ApplicationControlProperty>(1);
Device = device;
@ -247,6 +251,7 @@ namespace Ryujinx.HLE.HOS
};
FsServer = new FileSystemServer(fsServerConfig);
FsClient = FsServer.CreateFileSystemClient();
}
public void LoadCart(string exeFsDir, string romFsFile = null)
@ -364,9 +369,13 @@ namespace Ryujinx.HLE.HOS
if (result.IsSuccess())
{
ControlData = new Nacp(controlFile.AsStream());
result = controlFile.Read(out long bytesRead, 0, ControlData.ByteSpan, ReadOption.None);
TitleName = CurrentTitle = ControlData.Descriptions[(int) State.DesiredTitleLanguage].Title;
if (result.IsSuccess() && bytesRead == ControlData.ByteSpan.Length)
{
TitleName = CurrentTitle = ControlData.Value
.GetTitles()[(int) State.DesiredTitleLanguage].Name.ToString();
}
}
}
@ -615,28 +624,28 @@ namespace Ryujinx.HLE.HOS
if (nacpSize != 0)
{
input.Seek(obj.FileSize + (long)nacpOffset, SeekOrigin.Begin);
using (MemoryStream stream = new MemoryStream(reader.ReadBytes((int)nacpSize)))
{
ControlData = new Nacp(stream);
}
metaData.TitleName = ControlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
reader.Read(ControlData.ByteSpan);
ref ApplicationControlProperty nacp = ref ControlData.Value;
metaData.TitleName = nacp.GetTitles()[(int)State.DesiredTitleLanguage].Name.ToString();
if (string.IsNullOrWhiteSpace(metaData.TitleName))
{
metaData.TitleName = ControlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
metaData.TitleName = nacp.GetTitles().ToArray().FirstOrDefault(x => x.Name[0] != 0).ToString();
}
metaData.Aci0.TitleId = ControlData.PresenceGroupId;
metaData.Aci0.TitleId = nacp.PresenceGroupId;
if (metaData.Aci0.TitleId == 0)
{
metaData.Aci0.TitleId = ControlData.SaveDataOwnerId;
metaData.Aci0.TitleId = nacp.SaveDataOwnerId.Value;
}
if (metaData.Aci0.TitleId == 0)
{
metaData.Aci0.TitleId = ControlData.AddOnContentBaseId - 0x1000;
metaData.Aci0.TitleId = nacp.AddOnContentBaseId - 0x1000;
}
if (metaData.Aci0.TitleId.ToString("x16") == "fffffffffffff000")

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,18 @@
using LibHac;
using LibHac.Account;
using LibHac.Ncm;
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 +29,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 +38,15 @@ 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);
Result result = EnsureApplicationSaveData(context.Device.System.FsClient, out long requiredSize, titleId,
ref context.Device.System.ControlData.Value, ref userId);
Logger.PrintStub(LogClass.ServiceAm, new { userId });
context.ResponseData.Write(requiredSize);
return ResultCode.Success;
return (ResultCode)result.Value;
}
[Command(21)]

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

@ -51,7 +51,7 @@
<ItemGroup>
<PackageReference Include="Concentus" Version="1.1.7" />
<PackageReference Include="LibHac" Version="0.7.0--save-tweaks.20" />
<PackageReference Include="LibHac" Version="0.7.0--save-tweaks.22" />
<PackageReference Include="TimeZoneConverter.Posix" Version="2.1.0" />
</ItemGroup>