diff options
Diffstat (limited to 'Ryujinx.HLE/HOS')
22 files changed, 933 insertions, 132 deletions
diff --git a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs index 0be5e896..313db345 100644 --- a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs @@ -1,14 +1,19 @@ -using Ryujinx.HLE.Memory; +using LibHac; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.Resource; using Ryujinx.HLE.Utilities; using System.Collections.Generic; using System.IO; +using System.Linq; + +using static Ryujinx.HLE.Utilities.FontUtils; namespace Ryujinx.HLE.HOS.Font { class SharedFontManager { - private DeviceMemory Memory; + private Switch Device; private long PhysicalAddress; @@ -32,21 +37,64 @@ namespace Ryujinx.HLE.HOS.Font { this.PhysicalAddress = PhysicalAddress; - Memory = Device.Memory; + this.Device = Device; FontsPath = Path.Combine(Device.FileSystem.GetSystemPath(), "fonts"); } - public void EnsureInitialized() + public void EnsureInitialized(ContentManager ContentManager) { if (FontData == null) { - Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize); + Device.Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize); uint FontOffset = 0; FontInfo CreateFont(string Name) { + if (ContentManager.TryGetFontTitle(Name, out long FontTitle)) + { + string ContentPath = ContentManager.GetInstalledContentPath(FontTitle, StorageId.NandSystem, ContentType.Data); + string FontPath = Device.FileSystem.SwitchPathToSystemPath(ContentPath); + + if (!string.IsNullOrWhiteSpace(FontPath)) + { + int FileIndex = 0; + + //Use second file in Chinese Font title for standard + if(Name == "FontChineseSimplified") + { + FileIndex = 1; + } + + FileStream NcaFileStream = new FileStream(FontPath, FileMode.Open, FileAccess.Read); + Nca Nca = new Nca(Device.System.KeySet, NcaFileStream, false); + NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); + Romfs Romfs = new Romfs(Nca.OpenSection(RomfsSection.SectionNum, false, Device.System.FsIntegrityCheckLevel)); + Stream FontFile = Romfs.OpenFile(Romfs.Files[FileIndex]); + + byte[] Data = DecryptFont(FontFile); + + FontInfo Info = new FontInfo((int)FontOffset, Data.Length); + + WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length); + + FontOffset += 8; + + uint Start = FontOffset; + + for (; FontOffset - Start < Data.Length; FontOffset++) + { + Device.Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]); + } + + NcaFileStream.Dispose(); + Nca.Dispose(); + + return Info; + } + } + string FontFilePath = Path.Combine(FontsPath, Name + ".ttf"); if (File.Exists(FontFilePath)) @@ -63,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Font for (; FontOffset - Start < Data.Length; FontOffset++) { - Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]); + Device.Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]); } return Info; @@ -101,20 +149,20 @@ namespace Ryujinx.HLE.HOS.Font int EncryptedSize = EndianSwap.Swap32(Size ^ Key); - Memory.WriteInt32(Position + 0, DecMagic); - Memory.WriteInt32(Position + 4, EncryptedSize); + Device.Memory.WriteInt32(Position + 0, DecMagic); + Device.Memory.WriteInt32(Position + 4, EncryptedSize); } public int GetFontSize(SharedFontType FontType) { - EnsureInitialized(); + EnsureInitialized(Device.System.ContentManager); return FontData[FontType].Size; } public int GetSharedMemoryAddressOffset(SharedFontType FontType) { - EnsureInitialized(); + EnsureInitialized(Device.System.ContentManager); return FontData[FontType].Offset + 8; } diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 1b336647..d967c896 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -1,5 +1,6 @@ using LibHac; using Ryujinx.Common.Logging; +using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS.Font; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.SystemState; @@ -42,6 +43,8 @@ namespace Ryujinx.HLE.HOS internal SharedFontManager Font { get; private set; } + internal ContentManager ContentManager { get; private set; } + internal KEvent VsyncEvent { get; private set; } internal Keyset KeySet { get; private set; } @@ -90,6 +93,8 @@ namespace Ryujinx.HLE.HOS VsyncEvent = new KEvent(this); LoadKeySet(); + + ContentManager = new ContentManager(Device); } public void LoadCart(string ExeFsDir, string RomFsFile = null) @@ -156,6 +161,8 @@ namespace Ryujinx.HLE.HOS LoadNso("subsdk*"); LoadNso("sdk"); + ContentManager.LoadEntries(); + MainProcess.Run(); } @@ -174,6 +181,8 @@ namespace Ryujinx.HLE.HOS return; } + ContentManager.LoadEntries(); + LoadNca(MainNca, ControlNca); } @@ -412,6 +421,8 @@ namespace Ryujinx.HLE.HOS LoadNso("subsdk"); LoadNso("sdk"); + ContentManager.LoadEntries(); + MainProcess.Run(); } @@ -419,13 +430,13 @@ namespace Ryujinx.HLE.HOS { bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro"; - string Name = Path.GetFileNameWithoutExtension(FilePath); + string Name = Path.GetFileNameWithoutExtension(FilePath); string SwitchFilePath = Device.FileSystem.SystemPathToSwitchPath(FilePath); if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/"))) { string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}"; - string TempPath = Device.FileSystem.SwitchPathToSystemPath(SwitchPath); + string TempPath = Device.FileSystem.SwitchPathToSystemPath(SwitchPath); string SwitchDir = Path.GetDirectoryName(TempPath); @@ -449,6 +460,9 @@ namespace Ryujinx.HLE.HOS } MainProcess.SetEmptyArgs(); + + ContentManager.LoadEntries(); + MainProcess.Run(IsNro); } diff --git a/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs b/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs new file mode 100644 index 00000000..f11c78cc --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs @@ -0,0 +1,23 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Es +{ + class IETicketService : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + private bool IsInitialized; + + public IETicketService() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + + }; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs new file mode 100644 index 00000000..74ebddc2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.FspSrv +{ + public struct DirectoryEntry + { + public string Path { get; private set; } + public long Size { get; private set; } + + public DirectoryEntryType EntryType { get; set; } + + public DirectoryEntry(string Path, DirectoryEntryType DirectoryEntryType, long Size = 0) + { + this.Path = Path; + EntryType = DirectoryEntryType; + this.Size = Size; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs new file mode 100644 index 00000000..da075a5f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.FspSrv +{ + public enum DirectoryEntryType + { + Directory, + File + } +} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/FileSystemType.cs b/Ryujinx.HLE/HOS/Services/FspSrv/FileSystemType.cs new file mode 100644 index 00000000..2fd59673 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/FspSrv/FileSystemType.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.HOS.Services.FspSrv +{ + enum FileSystemType : int + { + Logo = 2, + ContentControl = 3, + ContentManual = 4, + ContentMeta = 5, + ContentData = 6, + ApplicationPackage = 7 + } +} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/FsErr.cs b/Ryujinx.HLE/HOS/Services/FspSrv/FsErr.cs index 39eadcec..16ef03be 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/FsErr.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/FsErr.cs @@ -5,5 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv public const int PathDoesNotExist = 1; public const int PathAlreadyExists = 2; public const int PathAlreadyInUse = 7; + public const int PartitionNotFound = 1001; + public const int InvalidInput = 6001; } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs index d6ae084f..c964eecb 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; using System; using System.Collections.Generic; @@ -14,15 +15,17 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; - private List<string> DirectoryEntries; + private List<DirectoryEntry> DirectoryEntries; private int CurrentItemIndex; public event EventHandler<EventArgs> Disposed; - public string HostPath { get; private set; } + public string DirectoryPath { get; private set; } - public IDirectory(string HostPath, int Flags) + private IFileSystemProvider Provider; + + public IDirectory(string DirectoryPath, int Flags, IFileSystemProvider Provider) { m_Commands = new Dictionary<int, ServiceProcessRequest>() { @@ -30,23 +33,25 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { 1, GetEntryCount } }; - this.HostPath = HostPath; + this.Provider = Provider; + this.DirectoryPath = DirectoryPath; - DirectoryEntries = new List<string>(); + DirectoryEntries = new List<DirectoryEntry>(); if ((Flags & 1) != 0) { - DirectoryEntries.AddRange(Directory.GetDirectories(HostPath)); + DirectoryEntries.AddRange(Provider.GetDirectories(DirectoryPath)); } if ((Flags & 2) != 0) { - DirectoryEntries.AddRange(Directory.GetFiles(HostPath)); + DirectoryEntries.AddRange(Provider.GetFiles(DirectoryPath)); } CurrentItemIndex = 0; } + // Read() -> (u64 count, buffer<nn::fssrv::sf::IDirectoryEntry, 6, 0> entries) public long Read(ServiceCtx Context) { long BufferPosition = Context.Request.ReceiveBuff[0].Position; @@ -68,31 +73,23 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } - private void WriteDirectoryEntry(ServiceCtx Context, long Position, string FullPath) + private void WriteDirectoryEntry(ServiceCtx Context, long Position, DirectoryEntry Entry) { for (int Offset = 0; Offset < 0x300; Offset += 8) { Context.Memory.WriteInt64(Position + Offset, 0); } - byte[] NameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(FullPath)); + byte[] NameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(Entry.Path)); Context.Memory.WriteBytes(Position, NameBuffer); - int Type = 0; - long Size = 0; - - if (File.Exists(FullPath)) - { - Type = 1; - Size = new FileInfo(FullPath).Length; - } - Context.Memory.WriteInt32(Position + 0x300, 0); //Padding? - Context.Memory.WriteInt32(Position + 0x304, Type); - Context.Memory.WriteInt64(Position + 0x308, Size); + Context.Memory.WriteInt32(Position + 0x304, (byte)Entry.EntryType); + Context.Memory.WriteInt64(Position + 0x308, Entry.Size); } + // GetEntryCount() -> u64 public long GetEntryCount(ServiceCtx Context) { Context.ResponseData.Write((long)DirectoryEntries.Count); diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs index 194d9420..9bf152c4 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs @@ -32,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv this.HostPath = HostPath; } + // Read(u32, u64 offset, u64 size) -> (u64 out_size, buffer<u8, 0x46, 0> out_buf) public long Read(ServiceCtx Context) { long Position = Context.Request.ReceiveBuff[0].Position; @@ -53,6 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // Write(u32, u64 offset, u64 size, buffer<u8, 0x45, 0>) public long Write(ServiceCtx Context) { long Position = Context.Request.SendBuff[0].Position; @@ -69,6 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // Flush() public long Flush(ServiceCtx Context) { BaseStream.Flush(); @@ -76,6 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // SetSize(u64 size) public long SetSize(ServiceCtx Context) { long Size = Context.RequestData.ReadInt64(); @@ -85,6 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // GetSize() -> u64 fileSize public long GetSize(ServiceCtx Context) { Context.ResponseData.Write(BaseStream.Length); diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs index bd249e50..edcdfa58 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs @@ -1,10 +1,11 @@ +using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; using System; using System.Collections.Generic; using System.IO; -using System.Text; using static Ryujinx.HLE.HOS.ErrorCode; +using static Ryujinx.HLE.Utilities.StringUtils; namespace Ryujinx.HLE.HOS.Services.FspSrv { @@ -18,7 +19,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv private string Path; - public IFileSystem(string Path) + private IFileSystemProvider Provider; + + public IFileSystem(string Path, IFileSystemProvider Provider) { m_Commands = new Dictionary<int, ServiceProcessRequest>() { @@ -41,9 +44,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv OpenPaths = new HashSet<string>(); - this.Path = Path; + this.Path = Path; + this.Provider = Provider; } + // CreateFile(u32 mode, u64 size, buffer<bytes<0x301>, 0x19, 0x301> path) public long CreateFile(ServiceCtx Context) { string Name = ReadUtf8String(Context); @@ -51,14 +56,14 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv long Mode = Context.RequestData.ReadInt64(); int Size = Context.RequestData.ReadInt32(); - string FileName = Context.Device.FileSystem.GetFullPath(Path, Name); + string FileName = Provider.GetFullPath(Name); if (FileName == null) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (File.Exists(FileName)) + if (Provider.FileExists(FileName)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); } @@ -68,21 +73,17 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - using (FileStream NewFile = File.Create(FileName)) - { - NewFile.SetLength(Size); - } - - return 0; + return Provider.CreateFile(FileName, Size); } + // DeleteFile(buffer<bytes<0x301>, 0x19, 0x301> path) public long DeleteFile(ServiceCtx Context) { string Name = ReadUtf8String(Context); - string FileName = Context.Device.FileSystem.GetFullPath(Path, Name); + string FileName = Provider.GetFullPath(Name); - if (!File.Exists(FileName)) + if (!Provider.FileExists(FileName)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } @@ -92,23 +93,22 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - File.Delete(FileName); - - return 0; + return Provider.DeleteFile(FileName); } + // CreateDirectory(buffer<bytes<0x301>, 0x19, 0x301> path) public long CreateDirectory(ServiceCtx Context) { string Name = ReadUtf8String(Context); - string DirName = Context.Device.FileSystem.GetFullPath(Path, Name); + string DirName = Provider.GetFullPath(Name); if (DirName == null) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (Directory.Exists(DirName)) + if (Provider.DirectoryExists(DirName)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); } @@ -118,26 +118,28 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - Directory.CreateDirectory(DirName); + Provider.CreateDirectory(DirName); return 0; } + // DeleteDirectory(buffer<bytes<0x301>, 0x19, 0x301> path) public long DeleteDirectory(ServiceCtx Context) { return DeleteDirectory(Context, false); } + // DeleteDirectoryRecursively(buffer<bytes<0x301>, 0x19, 0x301> path) public long DeleteDirectoryRecursively(ServiceCtx Context) { return DeleteDirectory(Context, true); } - + private long DeleteDirectory(ServiceCtx Context, bool Recursive) { string Name = ReadUtf8String(Context); - string DirName = Context.Device.FileSystem.GetFullPath(Path, Name); + string DirName = Provider.GetFullPath(Name); if (!Directory.Exists(DirName)) { @@ -149,25 +151,26 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - Directory.Delete(DirName, Recursive); + Provider.DeleteDirectory(DirName, Recursive); return 0; } + // RenameFile(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath) public long RenameFile(ServiceCtx Context) { string OldName = ReadUtf8String(Context, 0); string NewName = ReadUtf8String(Context, 1); - string OldFileName = Context.Device.FileSystem.GetFullPath(Path, OldName); - string NewFileName = Context.Device.FileSystem.GetFullPath(Path, NewName); + string OldFileName = Provider.GetFullPath(OldName); + string NewFileName = Provider.GetFullPath(NewName); - if (!File.Exists(OldFileName)) + if (Provider.FileExists(OldFileName)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (File.Exists(NewFileName)) + if (Provider.FileExists(NewFileName)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); } @@ -177,25 +180,24 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - File.Move(OldFileName, NewFileName); - - return 0; + return Provider.RenameFile(OldFileName, NewFileName); } + // RenameDirectory(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath) public long RenameDirectory(ServiceCtx Context) { string OldName = ReadUtf8String(Context, 0); string NewName = ReadUtf8String(Context, 1); - string OldDirName = Context.Device.FileSystem.GetFullPath(Path, OldName); - string NewDirName = Context.Device.FileSystem.GetFullPath(Path, NewName); + string OldDirName = Provider.GetFullPath(OldName); + string NewDirName = Provider.GetFullPath(NewName); - if (!Directory.Exists(OldDirName)) + if (!Provider.DirectoryExists(OldDirName)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (Directory.Exists(NewDirName)) + if (!Provider.DirectoryExists(NewDirName)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); } @@ -205,22 +207,21 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - Directory.Move(OldDirName, NewDirName); - - return 0; + return Provider.RenameDirectory(OldDirName, NewDirName); } + // GetEntryType(buffer<bytes<0x301>, 0x19, 0x301> path) -> nn::fssrv::sf::DirectoryEntryType public long GetEntryType(ServiceCtx Context) { string Name = ReadUtf8String(Context); - string FileName = Context.Device.FileSystem.GetFullPath(Path, Name); + string FileName = Provider.GetFullPath(Name); - if (File.Exists(FileName)) + if (Provider.FileExists(FileName)) { Context.ResponseData.Write(1); } - else if (Directory.Exists(FileName)) + else if (Provider.DirectoryExists(FileName)) { Context.ResponseData.Write(0); } @@ -234,15 +235,16 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // OpenFile(u32 mode, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFile> file public long OpenFile(ServiceCtx Context) { int FilterFlags = Context.RequestData.ReadInt32(); string Name = ReadUtf8String(Context); - string FileName = Context.Device.FileSystem.GetFullPath(Path, Name); + string FileName = Provider.GetFullPath(Name); - if (!File.Exists(FileName)) + if (!Provider.FileExists(FileName)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } @@ -252,79 +254,96 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - FileStream Stream = new FileStream(FileName, FileMode.Open); - IFile FileInterface = new IFile(Stream, FileName); + long Error = Provider.OpenFile(FileName, out IFile FileInterface); - FileInterface.Disposed += RemoveFileInUse; - - lock (OpenPaths) + if (Error == 0) { - OpenPaths.Add(FileName); - } + FileInterface.Disposed += RemoveFileInUse; - MakeObject(Context, FileInterface); + lock (OpenPaths) + { + OpenPaths.Add(FileName); + } - return 0; + MakeObject(Context, FileInterface); + + return 0; + } + + return Error; } + // OpenDirectory(u32 filter_flags, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IDirectory> directory public long OpenDirectory(ServiceCtx Context) { int FilterFlags = Context.RequestData.ReadInt32(); string Name = ReadUtf8String(Context); - string DirName = Context.Device.FileSystem.GetFullPath(Path, Name); + string DirName = Provider.GetFullPath(Name); - if (!Directory.Exists(DirName)) + if (!Provider.DirectoryExists(DirName)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - IDirectory DirInterface = new IDirectory(DirName, FilterFlags); + if (IsPathAlreadyInUse(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } - DirInterface.Disposed += RemoveDirectoryInUse; + long Error = Provider.OpenDirectory(DirName, FilterFlags, out IDirectory DirInterface); - lock (OpenPaths) + if (Error == 0) { - OpenPaths.Add(DirName); - } + DirInterface.Disposed += RemoveDirectoryInUse; - MakeObject(Context, DirInterface); + lock (OpenPaths) + { + OpenPaths.Add(DirName); + } - return 0; + MakeObject(Context, DirInterface); + } + + return Error; } + // Commit() public long Commit(ServiceCtx Context) { return 0; } + // GetFreeSpaceSize(buffer<bytes<0x301>, 0x19, 0x301> path) -> u64 totalFreeSpace public long GetFreeSpaceSize(ServiceCtx Context) { string Name = ReadUtf8String(Context); - Context.ResponseData.Write(Context.Device.FileSystem.GetDrive().AvailableFreeSpace); + Context.ResponseData.Write(Provider.GetFreeSpace(Context)); return 0; } + // GetTotalSpaceSize(buffer<bytes<0x301>, 0x19, 0x301> path) -> u64 totalSize public long GetTotalSpaceSize(ServiceCtx Context) { string Name = ReadUtf8String(Context); - Context.ResponseData.Write(Context.Device.FileSystem.GetDrive().TotalSize); + Context.ResponseData.Write(Provider.GetFreeSpace(Context)); return 0; } + // CleanDirectoryRecursively(buffer<bytes<0x301>, 0x19, 0x301> path) public long CleanDirectoryRecursively(ServiceCtx Context) { string Name = ReadUtf8String(Context); - string DirName = Context.Device.FileSystem.GetFullPath(Path, Name); + string DirName = Provider.GetFullPath(Name); - if (!Directory.Exists(DirName)) + if (!Provider.DirectoryExists(DirName)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } @@ -334,15 +353,15 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - foreach (string Entry in Directory.EnumerateFileSystemEntries(DirName)) + foreach (DirectoryEntry Entry in Provider.GetEntries(DirName)) { - if (Directory.Exists(Entry)) + if (Provider.DirectoryExists(Entry.Path)) { - Directory.Delete(Entry, true); + Provider.DeleteDirectory(Entry.Path, true); } - else if (File.Exists(Entry)) + else if (Provider.FileExists(Entry.Path)) { - File.Delete(Entry); + Provider.DeleteFile(Entry.Path); } } @@ -377,30 +396,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { DirInterface.Disposed -= RemoveDirectoryInUse; - OpenPaths.Remove(DirInterface.HostPath); - } - } - - private string ReadUtf8String(ServiceCtx Context, int Index = 0) - { - long Position = Context.Request.PtrBuff[Index].Position; - long Size = Context.Request.PtrBuff[Index].Size; - - using (MemoryStream MS = new MemoryStream()) - { - while (Size-- > 0) - { - byte Value = Context.Memory.ReadByte(Position++); - - if (Value == 0) - { - break; - } - - MS.WriteByte(Value); - } - - return Encoding.UTF8.GetString(MS.ToArray()); + OpenPaths.Remove(DirInterface.DirectoryPath); } } } diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs index daf5e0b2..0fc1eb80 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs @@ -1,7 +1,14 @@ +using LibHac; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.Utilities; using System.Collections.Generic; +using System.IO; +using System.Linq; + +using static Ryujinx.HLE.FileSystem.VirtualFileSystem; +using static Ryujinx.HLE.HOS.ErrorCode; +using static Ryujinx.HLE.Utilities.StringUtils; namespace Ryujinx.HLE.HOS.Services.FspSrv { @@ -15,28 +22,104 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { m_Commands = new Dictionary<int, ServiceProcessRequest>() { - { 1, SetCurrentProcess }, + { 1, Initialize }, + { 8, OpenFileSystemWithId }, + { 11, OpenBisFileSystem }, { 18, OpenSdCardFileSystem }, { 51, OpenSaveDataFileSystem }, { 52, OpenSaveDataFileSystemBySystemSaveDataId }, { 200, OpenDataStorageByCurrentProcess }, + { 202, OpenDataStorageByDataId }, { 203, OpenPatchDataStorageByCurrentProcess }, { 1005, GetGlobalAccessLogMode } }; } - public long SetCurrentProcess(ServiceCtx Context) + // Initialize(u64, pid) + public long Initialize(ServiceCtx Context) + { + return 0; + } + + // OpenFileSystemWithId(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid, buffer<bytes<0x301>, 0x19, 0x301> path) + // -> object<nn::fssrv::sf::IFileSystem> contentFs + public long OpenFileSystemWithId(ServiceCtx Context) + { + FileSystemType FileSystemType = (FileSystemType)Context.RequestData.ReadInt32(); + long TitleId = Context.RequestData.ReadInt64(); + string SwitchPath = ReadUtf8String(Context); + string FullPath = Context.Device.FileSystem.SwitchPathToSystemPath(SwitchPath); + + if (!File.Exists(FullPath)) + { + if (FullPath.Contains(".")) + { + return OpenFileSystemFromInternalFile(Context, FullPath); + } + + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + FileStream FileStream = new FileStream(FullPath, FileMode.Open, FileAccess.Read); + string Extension = Path.GetExtension(FullPath); + + if (Extension == ".nca") + { + return OpenNcaFs(Context, FullPath, FileStream); + } + else if (Extension == ".nsp") + { + return OpenNsp(Context, FullPath); + } + + return MakeError(ErrorModule.Fs, FsErr.InvalidInput); + } + + // OpenBisFileSystem(nn::fssrv::sf::Partition partitionID, buffer<bytes<0x301>, 0x19, 0x301>) -> object<nn::fssrv::sf::IFileSystem> Bis + public long OpenBisFileSystem(ServiceCtx Context) { + int BisPartitionId = Context.RequestData.ReadInt32(); + string PartitionString = ReadUtf8String(Context); + string BisPartitonPath = string.Empty; + + switch (BisPartitionId) + { + case 29: + BisPartitonPath = SafeNandPath; + break; + case 30: + case 31: + BisPartitonPath = SystemNandPath; + break; + case 32: + BisPartitonPath = UserNandPath; + break; + default: + return MakeError(ErrorModule.Fs, FsErr.InvalidInput); + } + + string FullPath = Context.Device.FileSystem.GetFullPartitionPath(BisPartitonPath); + + FileSystemProvider FileSystemProvider = new FileSystemProvider(FullPath, Context.Device.FileSystem.GetBasePath()); + + MakeObject(Context, new IFileSystem(FullPath, FileSystemProvider)); + return 0; } + // OpenSdCardFileSystem() -> object<nn::fssrv::sf::IFileSystem> public long OpenSdCardFileSystem(ServiceCtx Context) { - MakeObject(Context, new IFileSystem(Context.Device.FileSystem.GetSdCardPath())); + string SdCardPath = Context.Device.FileSystem.GetSdCardPath(); + + FileSystemProvider FileSystemProvider = new FileSystemProvider(SdCardPath, Context.Device.FileSystem.GetBasePath()); + + MakeObject(Context, new IFileSystem(SdCardPath, FileSystemProvider)); return 0; } + // OpenSaveDataFileSystem(u8 save_data_space_id, nn::fssrv::sf::SaveStruct saveStruct) -> object<nn::fssrv::sf::IFileSystem> saveDataFs public long OpenSaveDataFileSystem(ServiceCtx Context) { LoadSaveDataFileSystem(Context); @@ -44,6 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // OpenSaveDataFileSystemBySystemSaveDataId(u8 save_data_space_id, nn::fssrv::sf::SaveStruct saveStruct) -> object<nn::fssrv::sf::IFileSystem> systemSaveDataFs public long OpenSaveDataFileSystemBySystemSaveDataId(ServiceCtx Context) { LoadSaveDataFileSystem(Context); @@ -51,6 +135,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage public long OpenDataStorageByCurrentProcess(ServiceCtx Context) { MakeObject(Context, new IStorage(Context.Device.FileSystem.RomFs)); @@ -58,6 +143,63 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // OpenDataStorageByDataId(u8 storageId, nn::ApplicationId tid) -> object<nn::fssrv::sf::IStorage> dataStorage + public long OpenDataStorageByDataId(ServiceCtx Context) + { + StorageId StorageId = (StorageId)Context.RequestData.ReadByte(); + byte[] Padding = Context.RequestData.ReadBytes(7); + long TitleId = Context.RequestData.ReadInt64(); + + StorageId InstalledStorage = + Context.Device.System.ContentManager.GetInstalledStorage(TitleId, ContentType.Data, StorageId); + + if (InstalledStorage == StorageId.None) + { + InstalledStorage = + Context.Device.System.ContentManager.GetInstalledStorage(TitleId, ContentType.AocData, StorageId); + } + + if (InstalledStorage != StorageId.None) + { + string ContentPath = Context.Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.AocData); + + if (string.IsNullOrWhiteSpace(ContentPath)) + { + ContentPath = Context.Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.AocData); + } + + string InstallPath = Context.Device.FileSystem.SwitchPathToSystemPath(ContentPath); + + if (!string.IsNullOrWhiteSpace(InstallPath)) + { + string NcaPath = InstallPath; + + if (File.Exists(NcaPath)) + { + FileStream NcaStream = new FileStream(NcaPath, FileMode.Open, FileAccess.Read); + Nca Nca = new Nca(Context.Device.System.KeySet, NcaStream, false); + NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); + Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel); + + MakeObject(Context, new IStorage(RomfsStream)); + + return 0; + } + else + { + throw new FileNotFoundException($"No Nca found in Path `{NcaPath}`."); + } + } + else + { + throw new DirectoryNotFoundException($"Path for title id {TitleId:x16} on Storage {StorageId} was not found in Path {InstallPath}."); + } + } + + throw new FileNotFoundException($"System archive with titleid {TitleId:x16} was not found on Storage {StorageId}. Found in {InstalledStorage}."); + } + + // OpenPatchDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> public long OpenPatchDataStorageByCurrentProcess(ServiceCtx Context) { MakeObject(Context, new IStorage(Context.Device.FileSystem.RomFs)); @@ -65,6 +207,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // GetGlobalAccessLogMode() -> u32 logMode public long GetGlobalAccessLogMode(ServiceCtx Context) { Context.ResponseData.Write(0); @@ -82,13 +225,102 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); - long SaveId = Context.RequestData.ReadInt64(); + long SaveId = Context.RequestData.ReadInt64(); + SaveDataType SaveDataType = (SaveDataType)Context.RequestData.ReadByte(); + SaveInfo SaveInfo = new SaveInfo(TitleId, SaveId, SaveDataType, UserId, SaveSpaceId); + string SavePath = Context.Device.FileSystem.GetGameSavePath(SaveInfo, Context); + FileSystemProvider FileSystemProvider = new FileSystemProvider(SavePath, Context.Device.FileSystem.GetBasePath()); + + MakeObject(Context, new IFileSystem(SavePath, FileSystemProvider)); + } + + private long OpenNsp(ServiceCtx Context, string PfsPath) + { + FileStream PfsFile = new FileStream(PfsPath, FileMode.Open, FileAccess.Read); + Pfs Nsp = new Pfs(PfsFile); + PfsFileEntry TicketFile = Nsp.Files.FirstOrDefault(x => x.Name.EndsWith(".tik")); + + if (TicketFile != null) + { + Ticket Ticket = new Ticket(Nsp.OpenFile(TicketFile)); + + Context.Device.System.KeySet.TitleKeys[Ticket.RightsId] = + Ticket.GetTitleKey(Context.Device.System.KeySet); + } + + IFileSystem NspFileSystem = new IFileSystem(PfsPath, new PFsProvider(Nsp)); + + MakeObject(Context, NspFileSystem); + + return 0; + } + + private long OpenNcaFs(ServiceCtx Context,string NcaPath, Stream NcaStream) + { + Nca Nca = new Nca(Context.Device.System.KeySet, NcaStream, false); + + NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); + NcaSection PfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); + + if (RomfsSection != null) + { + Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel); + IFileSystem NcaFileSystem = new IFileSystem(NcaPath, new RomFsProvider(RomfsStream)); + + MakeObject(Context, NcaFileSystem); + } + else if(PfsSection !=null) + { + Stream PfsStream = Nca.OpenSection(PfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel); + Pfs Pfs = new Pfs(PfsStream); + IFileSystem NcaFileSystem = new IFileSystem(NcaPath, new PFsProvider(Pfs)); + + MakeObject(Context, NcaFileSystem); + } + else + { + return MakeError(ErrorModule.Fs, FsErr.PartitionNotFound); + } + + return 0; + } + + private long OpenFileSystemFromInternalFile(ServiceCtx Context, string FullPath) + { + DirectoryInfo ArchivePath = new DirectoryInfo(FullPath).Parent; + + while (string.IsNullOrWhiteSpace(ArchivePath.Extension)) + { + ArchivePath = ArchivePath.Parent; + } + + if (ArchivePath.Extension == ".nsp" && File.Exists(ArchivePath.FullName)) + { + FileStream PfsFile = new FileStream( + ArchivePath.FullName.TrimEnd(Path.DirectorySeparatorChar), + FileMode.Open, + FileAccess.Read); + + Pfs Nsp = new Pfs(PfsFile); + PfsFileEntry TicketFile = Nsp.Files.FirstOrDefault(x => x.Name.EndsWith(".tik")); + + if (TicketFile != null) + { + Ticket Ticket = new Ticket(Nsp.OpenFile(TicketFile)); + + Context.Device.System.KeySet.TitleKeys[Ticket.RightsId] = + Ticket.GetTitleKey(Context.Device.System.KeySet); + } - SaveDataType SaveDataType = (SaveDataType)Context.RequestData.ReadByte(); + string Filename = FullPath.Replace(ArchivePath.FullName, string.Empty).TrimStart('\\'); - SaveInfo SaveInfo = new SaveInfo(TitleId, SaveId, SaveDataType, UserId, SaveSpaceId); + if (Nsp.FileExists(Filename)) + { + return OpenNcaFs(Context, FullPath, Nsp.OpenFile(Filename)); + } + } - MakeObject(Context, new IFileSystem(Context.Device.FileSystem.GetGameSavePath(SaveInfo, Context))); + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs index c3f1f2c4..6a78cdfe 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs @@ -22,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv this.BaseStream = BaseStream; } + // Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer public long Read(ServiceCtx Context) { long Offset = Context.RequestData.ReadInt64(); diff --git a/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs new file mode 100644 index 00000000..dc057bc1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs @@ -0,0 +1,265 @@ +using LibHac; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.FileSystem.Content; +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; +using System.Text; + +using static Ryujinx.HLE.HOS.ErrorCode; +using static Ryujinx.HLE.Utilities.StringUtils; + +namespace Ryujinx.HLE.HOS.Services.Lr +{ + class ILocationResolver : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + private StorageId StorageId; + + public ILocationResolver(StorageId StorageId) + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + { 0, ResolveProgramPath }, + { 1, RedirectProgramPath }, + { 2, ResolveApplicationControlPath }, + { 3, ResolveApplicationHtmlDocumentPath }, + { 4, ResolveDataPath }, + { 5, RedirectApplicationControlPath }, + { 6, RedirectApplicationHtmlDocumentPath }, + { 7, ResolveApplicationLegalInformationPath }, + { 8, RedirectApplicationLegalInformationPath }, + { 9, Refresh }, + { 10, SetProgramNcaPath2 }, + { 11, ClearLocationResolver2 }, + { 12, DeleteProgramNcaPath }, + { 13, DeleteControlNcaPath }, + { 14, DeleteDocHtmlNcaPath }, + { 15, DeleteInfoHtmlNcaPath } + }; + + this.StorageId = StorageId; + } + + // DeleteInfoHtmlNcaPath() + public long DeleteInfoHtmlNcaPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + DeleteContentPath(Context, TitleId, ContentType.Manual); + + return 0; + } + + // DeleteDocHtmlNcaPath() + public long DeleteDocHtmlNcaPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + DeleteContentPath(Context, TitleId, ContentType.Manual); + + return 0; + } + + // DeleteControlNcaPath() + public long DeleteControlNcaPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + DeleteContentPath(Context, TitleId, ContentType.Control); + + return 0; + } + + // DeleteProgramNcaPath() + public long DeleteProgramNcaPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + DeleteContentPath(Context, TitleId, ContentType.Program); + + return 0; + } + + // ClearLocationResolver2() + public long ClearLocationResolver2(ServiceCtx Context) + { + Context.Device.System.ContentManager.RefreshEntries(StorageId, 1); + + return 0; + } + + // SetProgramNcaPath2() + public long SetProgramNcaPath2(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + RedirectPath(Context, TitleId, 1, ContentType.Program); + + return 0; + } + + // RedirectApplicationControlPath() + public long RedirectApplicationControlPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + RedirectPath(Context, TitleId, 1, ContentType.Control); + + return 0; + } + + // RedirectApplicationHtmlDocumentPath() + public long RedirectApplicationHtmlDocumentPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + RedirectPath(Context, TitleId, 1, ContentType.Manual); + + return 0; + } + + // RedirectApplicationLegalInformationPath() + public long RedirectApplicationLegalInformationPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + RedirectPath(Context, TitleId, 1, ContentType.Manual); + + return 0; + } + + // ResolveDataPath() + public long ResolveDataPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + if (ResolvePath(Context, TitleId, ContentType.Data) || ResolvePath(Context, TitleId, ContentType.AocData)) + { + return 0; + } + else + { + return MakeError(ErrorModule.Lr, LrErr.AccessDenied); + } + } + + // ResolveApplicationHtmlDocumentPath() + public long ResolveApplicationHtmlDocumentPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + if (ResolvePath(Context, TitleId, ContentType.Manual)) + { + return 0; + } + else + { + return MakeError(ErrorModule.Lr, LrErr.AccessDenied); + } + } + + // ResolveApplicationLegalInformationPath() + public long ResolveApplicationLegalInformationPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + if (ResolvePath(Context, TitleId, ContentType.Manual)) + { + return 0; + } + else + { + return MakeError(ErrorModule.Lr, LrErr.AccessDenied); + } + } + + // ResolveApplicationControlPath() + public long ResolveApplicationControlPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + if (ResolvePath(Context, TitleId, ContentType.Control)) + { + return 0; + } + else + { + return MakeError(ErrorModule.Lr, LrErr.AccessDenied); + } + } + + // RedirectProgramPath() + public long RedirectProgramPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + RedirectPath(Context, TitleId, 0, ContentType.Program); + + return 0; + } + + // Refresh() + public long Refresh(ServiceCtx Context) + { + Context.Device.System.ContentManager.RefreshEntries(StorageId, 1); + + return 0; + } + + // ResolveProgramPath() + public long ResolveProgramPath(ServiceCtx Context) + { + long TitleId = Context.RequestData.ReadInt64(); + + if (ResolvePath(Context, TitleId, ContentType.Program)) + { + return 0; + } + else + { + return MakeError(ErrorModule.Lr, LrErr.ProgramLocationEntryNotFound); + } + } + + private void RedirectPath(ServiceCtx Context, long TitleId, int Flag, ContentType ContentType) + { + string ContentPath = ReadUtf8String(Context); + LocationEntry NewLocation = new LocationEntry(ContentPath, Flag, TitleId, ContentType); + + Context.Device.System.ContentManager.RedirectLocation(NewLocation, StorageId); + } + + private bool ResolvePath(ServiceCtx Context, long TitleId,ContentType ContentType) + { + ContentManager ContentManager = Context.Device.System.ContentManager; + string ContentPath = ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.Program); + + if (!string.IsNullOrWhiteSpace(ContentPath)) + { + long Position = Context.Request.RecvListBuff[0].Position; + long Size = Context.Request.RecvListBuff[0].Size; + + byte[] ContentPathBuffer = Encoding.UTF8.GetBytes(ContentPath); + + Context.Memory.WriteBytes(Position, ContentPathBuffer); + } + else + { + return false; + } + + return true; + } + + private void DeleteContentPath(ServiceCtx Context, long TitleId, ContentType ContentType) + { + ContentManager ContentManager = Context.Device.System.ContentManager; + string ContentPath = ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.Manual); + + ContentManager.ClearEntry(TitleId, ContentType.Manual, StorageId); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Lr/ILocationResolverManager.cs b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolverManager.cs new file mode 100644 index 00000000..77710f76 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolverManager.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.FileSystem; + +namespace Ryujinx.HLE.HOS.Services.Lr +{ + class ILocationResolverManager : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + public ILocationResolverManager() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + { 0, OpenLocationResolver }, + }; + } + + // OpenLocationResolver() + private long OpenLocationResolver(ServiceCtx Context) + { + StorageId StorageId = (StorageId)Context.RequestData.ReadByte(); + + MakeObject(Context, new ILocationResolver(StorageId)); + + return 0; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Lr/LrErr.cs b/Ryujinx.HLE/HOS/Services/Lr/LrErr.cs new file mode 100644 index 00000000..5c87b73b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Lr/LrErr.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Lr +{ + class LrErr + { + public const int ProgramLocationEntryNotFound = 2; + public const int AccessDenied = 5; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs b/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs new file mode 100644 index 00000000..29792a1b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs @@ -0,0 +1,20 @@ +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Ncm +{ + class IContentManager : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + public IContentManager() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + + }; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Ncm/IContentStorage.cs b/Ryujinx.HLE/HOS/Services/Ncm/IContentStorage.cs new file mode 100644 index 00000000..a19fe079 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ncm/IContentStorage.cs @@ -0,0 +1,20 @@ +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Ncm +{ + class IContentStorage : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + public IContentStorage() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + + }; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs new file mode 100644 index 00000000..ee438d99 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -0,0 +1,23 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Ns +{ + class IApplicationManagerInterface : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + private bool IsInitialized; + + public IApplicationManagerInterface() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + + }; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs index d73adc0e..0495a388 100644 --- a/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Pl public long GetSharedMemoryNativeHandle(ServiceCtx Context) { - Context.Device.System.Font.EnsureInitialized(); + Context.Device.System.Font.EnsureInitialized(Context.Device.System.ContentManager); if (Context.Process.HandleTable.GenerateHandle(Context.Device.System.FontSharedMem, out int Handle) != KernelResult.Success) { diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs index d91583d6..2be1db70 100644 --- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs @@ -4,12 +4,14 @@ using Ryujinx.HLE.HOS.Services.Apm; using Ryujinx.HLE.HOS.Services.Aud; using Ryujinx.HLE.HOS.Services.Bsd; using Ryujinx.HLE.HOS.Services.Caps; +using Ryujinx.HLE.HOS.Services.Es; using Ryujinx.HLE.HOS.Services.FspSrv; using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Irs; using Ryujinx.HLE.HOS.Services.Ldr; using Ryujinx.HLE.HOS.Services.Lm; using Ryujinx.HLE.HOS.Services.Mm; +using Ryujinx.HLE.HOS.Services.Ncm; using Ryujinx.HLE.HOS.Services.Nfp; using Ryujinx.HLE.HOS.Services.Ns; using Ryujinx.HLE.HOS.Services.Nv; @@ -87,6 +89,9 @@ namespace Ryujinx.HLE.HOS.Services case "csrng": return new IRandomInterface(); + case "es": + return new IETicketService(); + case "friend:a": return new Friend.IServiceCreator(); @@ -114,12 +119,18 @@ namespace Ryujinx.HLE.HOS.Services case "mm:u": return new IRequest(); + case "ncm": + return new IContentManager(); + case "nfp:user": return new IUserManager(); case "nifm:u": return new Nifm.IStaticService(); + case "ns:am": + return new IApplicationManagerInterface(); + case "ns:ec": return new IServiceGetterInterface(); diff --git a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs index 070a4d5e..416ea1fb 100644 --- a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; +using LibHac; +using Ryujinx.HLE.FileSystem; namespace Ryujinx.HLE.HOS.Services.Set { @@ -18,6 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Set { m_Commands = new Dictionary<int, ServiceProcessRequest>() { + { 3, GetFirmwareVersion }, { 4, GetFirmwareVersion2 }, { 23, GetColorSetId }, { 24, SetColorSetId }, @@ -25,11 +28,27 @@ namespace Ryujinx.HLE.HOS.Services.Set }; } + // GetFirmwareVersion() -> buffer<nn::settings::system::FirmwareVersion, 0x1a, 0x100> + public static long GetFirmwareVersion(ServiceCtx Context) + { + return GetFirmwareVersion2(Context); + } + + // GetFirmwareVersion2() -> buffer<nn::settings::system::FirmwareVersion, 0x1a, 0x100> public static long GetFirmwareVersion2(ServiceCtx Context) { long ReplyPos = Context.Request.RecvListBuff[0].Position; long ReplySize = Context.Request.RecvListBuff[0].Size; + byte[] FirmwareData = GetFirmwareData(Context.Device); + + if (FirmwareData != null) + { + Context.Memory.WriteBytes(ReplyPos, FirmwareData); + + return 0; + } + const byte MajorFWVersion = 0x03; const byte MinorFWVersion = 0x00; const byte MicroFWVersion = 0x00; @@ -74,6 +93,7 @@ namespace Ryujinx.HLE.HOS.Services.Set return 0; } + // GetColorSetId() -> i32 public static long GetColorSetId(ServiceCtx Context) { Context.ResponseData.Write((int)Context.Device.System.State.ThemeColor); @@ -81,6 +101,7 @@ namespace Ryujinx.HLE.HOS.Services.Set return 0; } + // GetColorSetId() -> i32 public static long SetColorSetId(ServiceCtx Context) { int ColorSetId = Context.RequestData.ReadInt32(); @@ -90,6 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Set return 0; } + // GetSettingsItemValue(buffer<nn::settings::SettingsName, 0x19, 0x48>, buffer<nn::settings::SettingsItemKey, 0x19, 0x48>) -> (u64, buffer<unknown, 6, 0>) public static long GetSettingsItemValue(ServiceCtx Context) { long ClassPos = Context.Request.PtrBuff[0].Position; @@ -148,5 +170,44 @@ namespace Ryujinx.HLE.HOS.Services.Set return 0; } + + public static byte[] GetFirmwareData(Switch Device) + { + byte[] Data = null; + long TitleId = 0x0100000000000809; + string ContentPath = Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId.NandSystem, ContentType.Data); + + if(string.IsNullOrWhiteSpace(ContentPath)) + { + return null; + } + + string FirmwareTitlePath = Device.FileSystem.SwitchPathToSystemPath(ContentPath); + FileStream FirmwareStream = File.Open(FirmwareTitlePath, FileMode.Open, FileAccess.Read); + Nca FirmwareContent = new Nca(Device.System.KeySet, FirmwareStream, false); + Stream RomFsStream = FirmwareContent.OpenSection(0, false, Device.System.FsIntegrityCheckLevel); + + if(RomFsStream == null) + { + return null; + } + + Romfs FirmwareRomFs = new Romfs(RomFsStream); + + using(MemoryStream MemoryStream = new MemoryStream()) + { + using (Stream FirmwareFile = FirmwareRomFs.OpenFile("/file")) + { + FirmwareFile.CopyTo(MemoryStream); + } + + Data = MemoryStream.ToArray(); + } + + FirmwareContent.Dispose(); + FirmwareStream.Dispose(); + + return Data; + } } } diff --git a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs index 3833ce9e..aa96a416 100644 --- a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs +++ b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs @@ -46,6 +46,8 @@ namespace Ryujinx.HLE.HOS.SystemState public ColorSet ThemeColor { get; set; } + public bool InstallContents { get; set; } + private ConcurrentDictionary<string, UserProfile> Profiles; internal UserProfile LastOpenUser { get; private set; } |
