forked from Mirror/Ryujinx
Update to version 0.4 of LibHac (#689)
* It compiles * Print correct name when loading an exefs * Use DirectorySaveDataFileSystem for savedata * Handle more errors in IFileSystem * Remove structs replaced by LibHac structs * Fix alignment * Fix alignment again * Fix IFile and IFileSystem IPC * Alignment * Use released libhac version
This commit is contained in:
parent
92c1726647
commit
5fc1f6a1af
20 changed files with 465 additions and 1169 deletions
|
@ -1,5 +1,5 @@
|
||||||
using LibHac;
|
using LibHac.Fs;
|
||||||
using LibHac.IO;
|
using LibHac.Fs.NcaUtils;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -13,6 +13,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
private Dictionary<StorageId, LinkedList<LocationEntry>> _locationEntries;
|
private Dictionary<StorageId, LinkedList<LocationEntry>> _locationEntries;
|
||||||
|
|
||||||
private Dictionary<string, long> _sharedFontTitleDictionary;
|
private Dictionary<string, long> _sharedFontTitleDictionary;
|
||||||
|
private Dictionary<string, string> _sharedFontFilenameDictionary;
|
||||||
|
|
||||||
private SortedDictionary<(ulong, ContentType), string> _contentDictionary;
|
private SortedDictionary<(ulong, ContentType), string> _contentDictionary;
|
||||||
|
|
||||||
|
@ -33,6 +34,16 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{ "FontNintendoExtended", 0x0100000000000810 }
|
{ "FontNintendoExtended", 0x0100000000000810 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_sharedFontFilenameDictionary = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "FontStandard", "nintendo_udsg-r_std_003.bfttf" },
|
||||||
|
{ "FontChineseSimplified", "nintendo_udsg-r_org_zh-cn_003.bfttf" },
|
||||||
|
{ "FontExtendedChineseSimplified", "nintendo_udsg-r_ext_zh-cn_003.bfttf" },
|
||||||
|
{ "FontKorean", "nintendo_udsg-r_ko_003.bfttf" },
|
||||||
|
{ "FontChineseTraditional", "nintendo_udjxh-db_zh-tw_003.bfttf" },
|
||||||
|
{ "FontNintendoExtended", "nintendo_ext_003.bfttf" }
|
||||||
|
};
|
||||||
|
|
||||||
_device = device;
|
_device = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +85,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
|
|
||||||
using (FileStream ncaFile = new FileStream(Directory.GetFiles(directoryPath)[0], FileMode.Open, FileAccess.Read))
|
using (FileStream ncaFile = new FileStream(Directory.GetFiles(directoryPath)[0], FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage(), false);
|
Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage());
|
||||||
|
|
||||||
string switchPath = Path.Combine(contentPathString + ":",
|
string switchPath = Path.Combine(contentPathString + ":",
|
||||||
ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart('\\'));
|
ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart('\\'));
|
||||||
|
@ -102,7 +113,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
|
|
||||||
using (FileStream ncaFile = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
using (FileStream ncaFile = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage(), false);
|
Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage());
|
||||||
|
|
||||||
string switchPath = Path.Combine(contentPathString + ":",
|
string switchPath = Path.Combine(contentPathString + ":",
|
||||||
filePath.Replace(contentDirectory, string.Empty).TrimStart('\\'));
|
filePath.Replace(contentDirectory, string.Empty).TrimStart('\\'));
|
||||||
|
@ -230,7 +241,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
using (FileStream file = new FileStream(installedPath, FileMode.Open, FileAccess.Read))
|
using (FileStream file = new FileStream(installedPath, FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
Nca nca = new Nca(_device.System.KeySet, file.AsStorage(), false);
|
Nca nca = new Nca(_device.System.KeySet, file.AsStorage());
|
||||||
bool contentCheck = nca.Header.ContentType == contentType;
|
bool contentCheck = nca.Header.ContentType == contentType;
|
||||||
|
|
||||||
return contentCheck;
|
return contentCheck;
|
||||||
|
@ -287,6 +298,11 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
return _sharedFontTitleDictionary.TryGetValue(fontName, out titleId);
|
return _sharedFontTitleDictionary.TryGetValue(fontName, out titleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryGetFontFilename(string fontName, out string filename)
|
||||||
|
{
|
||||||
|
return _sharedFontFilenameDictionary.TryGetValue(fontName, out filename);
|
||||||
|
}
|
||||||
|
|
||||||
private LocationEntry GetLocation(long titleId, ContentType contentType, StorageId storageId)
|
private LocationEntry GetLocation(long titleId, ContentType contentType, StorageId storageId)
|
||||||
{
|
{
|
||||||
LinkedList<LocationEntry> locationList = _locationEntries[storageId];
|
LinkedList<LocationEntry> locationList = _locationEntries[storageId];
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using LibHac;
|
using LibHac.Fs.NcaUtils;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem.Content
|
namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,313 +0,0 @@
|
||||||
using Ryujinx.HLE.HOS;
|
|
||||||
using Ryujinx.HLE.HOS.Services.FspSrv;
|
|
||||||
using Ryujinx.HLE.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem
|
|
||||||
{
|
|
||||||
class FileSystemProvider : IFileSystemProvider
|
|
||||||
{
|
|
||||||
private readonly string _basePath;
|
|
||||||
private readonly string _rootPath;
|
|
||||||
|
|
||||||
public FileSystemProvider(string basePath, string rootPath)
|
|
||||||
{
|
|
||||||
_basePath = basePath;
|
|
||||||
_rootPath = rootPath;
|
|
||||||
|
|
||||||
CheckIfDescendentOfRootPath(basePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long CreateDirectory(string name)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(name);
|
|
||||||
|
|
||||||
if (Directory.Exists(name))
|
|
||||||
{
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.CreateDirectory(name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long CreateFile(string name, long size)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(name);
|
|
||||||
|
|
||||||
if (File.Exists(name))
|
|
||||||
{
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
|
||||||
}
|
|
||||||
|
|
||||||
using (FileStream newFile = File.Create(name))
|
|
||||||
{
|
|
||||||
newFile.SetLength(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long DeleteDirectory(string name, bool recursive)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(name);
|
|
||||||
|
|
||||||
string dirName = name;
|
|
||||||
|
|
||||||
if (!Directory.Exists(dirName))
|
|
||||||
{
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.Delete(dirName, recursive);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long DeleteFile(string name)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(name);
|
|
||||||
|
|
||||||
if (!File.Exists(name))
|
|
||||||
{
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
File.Delete(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectoryEntry[] GetDirectories(string path)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(path);
|
|
||||||
|
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
|
||||||
|
|
||||||
foreach(string directory in Directory.EnumerateDirectories(path))
|
|
||||||
{
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(directory, DirectoryEntryType.Directory);
|
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectoryEntry[] GetEntries(string path)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(path);
|
|
||||||
|
|
||||||
if (Directory.Exists(path))
|
|
||||||
{
|
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
|
||||||
|
|
||||||
foreach (string directory in Directory.EnumerateDirectories(path))
|
|
||||||
{
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(directory, DirectoryEntryType.Directory);
|
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string file in Directory.EnumerateFiles(path))
|
|
||||||
{
|
|
||||||
FileInfo fileInfo = new FileInfo(file);
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file, DirectoryEntryType.File, fileInfo.Length);
|
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectoryEntry[] GetFiles(string path)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(path);
|
|
||||||
|
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
|
||||||
|
|
||||||
foreach (string file in Directory.EnumerateFiles(path))
|
|
||||||
{
|
|
||||||
FileInfo fileInfo = new FileInfo(file);
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file, DirectoryEntryType.File, fileInfo.Length);
|
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetFreeSpace(ServiceCtx context)
|
|
||||||
{
|
|
||||||
return context.Device.FileSystem.GetDrive().AvailableFreeSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFullPath(string name)
|
|
||||||
{
|
|
||||||
if (name.StartsWith("//"))
|
|
||||||
{
|
|
||||||
name = name.Substring(2);
|
|
||||||
}
|
|
||||||
else if (name.StartsWith('/'))
|
|
||||||
{
|
|
||||||
name = name.Substring(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
string fullPath = Path.Combine(_basePath, name);
|
|
||||||
|
|
||||||
CheckIfDescendentOfRootPath(fullPath);
|
|
||||||
|
|
||||||
return fullPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetTotalSpace(ServiceCtx context)
|
|
||||||
{
|
|
||||||
return context.Device.FileSystem.GetDrive().TotalSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DirectoryExists(string name)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(name);
|
|
||||||
|
|
||||||
return Directory.Exists(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool FileExists(string name)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(name);
|
|
||||||
|
|
||||||
return File.Exists(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(name);
|
|
||||||
|
|
||||||
if (Directory.Exists(name))
|
|
||||||
{
|
|
||||||
directoryInterface = new IDirectory(name, filterFlags, this);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
directoryInterface = null;
|
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long OpenFile(string name, out IFile fileInterface)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(name);
|
|
||||||
|
|
||||||
if (File.Exists(name))
|
|
||||||
{
|
|
||||||
FileStream stream = new FileStream(name, FileMode.Open);
|
|
||||||
|
|
||||||
fileInterface = new IFile(stream, name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileInterface = null;
|
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long RenameDirectory(string oldName, string newName)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(oldName);
|
|
||||||
CheckIfDescendentOfRootPath(newName);
|
|
||||||
|
|
||||||
if (Directory.Exists(oldName))
|
|
||||||
{
|
|
||||||
Directory.Move(oldName, newName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long RenameFile(string oldName, string newName)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(oldName);
|
|
||||||
CheckIfDescendentOfRootPath(newName);
|
|
||||||
|
|
||||||
if (File.Exists(oldName))
|
|
||||||
{
|
|
||||||
File.Move(oldName, newName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CheckIfDescendentOfRootPath(string path)
|
|
||||||
{
|
|
||||||
DirectoryInfo pathInfo = new DirectoryInfo(path);
|
|
||||||
DirectoryInfo rootInfo = new DirectoryInfo(_rootPath);
|
|
||||||
|
|
||||||
while (pathInfo.Parent != null)
|
|
||||||
{
|
|
||||||
if (pathInfo.Parent.FullName == rootInfo.FullName)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pathInfo = pathInfo.Parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException($"Path {path} is not a child directory of {_rootPath}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileTimestamp GetFileTimeStampRaw(string name)
|
|
||||||
{
|
|
||||||
CheckIfDescendentOfRootPath(name);
|
|
||||||
|
|
||||||
DateTime creationDateTime = DateTime.UnixEpoch;
|
|
||||||
DateTime modifiedDateTime = DateTime.UnixEpoch;
|
|
||||||
DateTime lastAccessDateTime = DateTime.UnixEpoch;
|
|
||||||
|
|
||||||
if (File.Exists(name))
|
|
||||||
{
|
|
||||||
creationDateTime = File.GetCreationTime(name);
|
|
||||||
modifiedDateTime = File.GetLastWriteTime(name);
|
|
||||||
lastAccessDateTime = File.GetLastAccessTime(name);
|
|
||||||
}
|
|
||||||
else if (Directory.Exists(name))
|
|
||||||
{
|
|
||||||
creationDateTime = Directory.GetCreationTime(name);
|
|
||||||
modifiedDateTime = Directory.GetLastWriteTime(name);
|
|
||||||
lastAccessDateTime = Directory.GetLastAccessTime(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FileTimestamp
|
|
||||||
{
|
|
||||||
CreationDateTime = creationDateTime,
|
|
||||||
ModifiedDateTime = modifiedDateTime,
|
|
||||||
LastAccessDateTime = lastAccessDateTime
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
using Ryujinx.HLE.HOS;
|
|
||||||
using Ryujinx.HLE.HOS.Services.FspSrv;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem
|
|
||||||
{
|
|
||||||
interface IFileSystemProvider
|
|
||||||
{
|
|
||||||
long CreateFile(string name, long size);
|
|
||||||
|
|
||||||
long CreateDirectory(string name);
|
|
||||||
|
|
||||||
long RenameFile(string oldName, string newName);
|
|
||||||
|
|
||||||
long RenameDirectory(string oldName, string newName);
|
|
||||||
|
|
||||||
DirectoryEntry[] GetEntries(string path);
|
|
||||||
|
|
||||||
DirectoryEntry[] GetDirectories(string path);
|
|
||||||
|
|
||||||
DirectoryEntry[] GetFiles(string path);
|
|
||||||
|
|
||||||
long DeleteFile(string name);
|
|
||||||
|
|
||||||
long DeleteDirectory(string name, bool recursive);
|
|
||||||
|
|
||||||
bool FileExists(string name);
|
|
||||||
|
|
||||||
bool DirectoryExists(string name);
|
|
||||||
|
|
||||||
long OpenFile(string name, out IFile fileInterface);
|
|
||||||
|
|
||||||
long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface);
|
|
||||||
|
|
||||||
string GetFullPath(string name);
|
|
||||||
|
|
||||||
long GetFreeSpace(ServiceCtx context);
|
|
||||||
|
|
||||||
long GetTotalSpace(ServiceCtx context);
|
|
||||||
|
|
||||||
FileTimestamp GetFileTimeStampRaw(string name);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
using LibHac;
|
|
||||||
using LibHac.IO;
|
|
||||||
using Ryujinx.HLE.HOS;
|
|
||||||
using Ryujinx.HLE.HOS.Services.FspSrv;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem
|
|
||||||
{
|
|
||||||
class PFsProvider : IFileSystemProvider
|
|
||||||
{
|
|
||||||
private Pfs _pfs;
|
|
||||||
|
|
||||||
public PFsProvider(Pfs pfs)
|
|
||||||
{
|
|
||||||
_pfs = pfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long CreateDirectory(string name)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long CreateFile(string name, long size)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long DeleteDirectory(string name, bool recursive)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long DeleteFile(string name)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectoryEntry[] GetDirectories(string path)
|
|
||||||
{
|
|
||||||
return new DirectoryEntry[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectoryEntry[] GetEntries(string path)
|
|
||||||
{
|
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
|
||||||
|
|
||||||
foreach (PfsFileEntry file in _pfs.Files)
|
|
||||||
{
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.Size);
|
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectoryEntry[] GetFiles(string path)
|
|
||||||
{
|
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
|
||||||
|
|
||||||
foreach (PfsFileEntry file in _pfs.Files)
|
|
||||||
{
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.Size);
|
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetFreeSpace(ServiceCtx context)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFullPath(string name)
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetTotalSpace(ServiceCtx context)
|
|
||||||
{
|
|
||||||
return _pfs.Files.Sum(x => x.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DirectoryExists(string name)
|
|
||||||
{
|
|
||||||
return name == "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool FileExists(string name)
|
|
||||||
{
|
|
||||||
name = name.TrimStart('/');
|
|
||||||
|
|
||||||
return _pfs.FileExists(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface)
|
|
||||||
{
|
|
||||||
if (name == "/")
|
|
||||||
{
|
|
||||||
directoryInterface = new IDirectory(name, filterFlags, this);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long OpenFile(string name, out IFile fileInterface)
|
|
||||||
{
|
|
||||||
name = name.TrimStart('/');
|
|
||||||
|
|
||||||
if (_pfs.FileExists(name))
|
|
||||||
{
|
|
||||||
Stream stream = _pfs.OpenFile(name).AsStream();
|
|
||||||
fileInterface = new IFile(stream, name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileInterface = null;
|
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long RenameDirectory(string oldName, string newName)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long RenameFile(string oldName, string newName)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CheckIfOutsideBasePath(string path)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileTimestamp GetFileTimeStampRaw(string name)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,169 +0,0 @@
|
||||||
using LibHac;
|
|
||||||
using LibHac.IO;
|
|
||||||
using Ryujinx.HLE.HOS;
|
|
||||||
using Ryujinx.HLE.HOS.Services.FspSrv;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem
|
|
||||||
{
|
|
||||||
class RomFsProvider : IFileSystemProvider
|
|
||||||
{
|
|
||||||
private Romfs _romFs;
|
|
||||||
|
|
||||||
public RomFsProvider(LibHac.IO.IStorage storage)
|
|
||||||
{
|
|
||||||
_romFs = new Romfs(storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long CreateDirectory(string name)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long CreateFile(string name, long size)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long DeleteDirectory(string name, bool recursive)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long DeleteFile(string name)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectoryEntry[] GetDirectories(string path)
|
|
||||||
{
|
|
||||||
List<DirectoryEntry> directories = new List<DirectoryEntry>();
|
|
||||||
|
|
||||||
foreach(RomfsDir directory in _romFs.Directories)
|
|
||||||
{
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(directory.Name, DirectoryEntryType.Directory);
|
|
||||||
|
|
||||||
directories.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return directories.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectoryEntry[] GetEntries(string path)
|
|
||||||
{
|
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
|
||||||
|
|
||||||
foreach (RomfsDir directory in _romFs.Directories)
|
|
||||||
{
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(directory.Name, DirectoryEntryType.Directory);
|
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (RomfsFile file in _romFs.Files)
|
|
||||||
{
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.DataLength);
|
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectoryEntry[] GetFiles(string path)
|
|
||||||
{
|
|
||||||
List<DirectoryEntry> files = new List<DirectoryEntry>();
|
|
||||||
|
|
||||||
foreach (RomfsFile file in _romFs.Files)
|
|
||||||
{
|
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.DataLength);
|
|
||||||
|
|
||||||
files.Add(directoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return files.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetFreeSpace(ServiceCtx context)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFullPath(string name)
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetTotalSpace(ServiceCtx context)
|
|
||||||
{
|
|
||||||
return _romFs.Files.Sum(x => x.DataLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DirectoryExists(string name)
|
|
||||||
{
|
|
||||||
return _romFs.Directories.Exists(x=>x.Name == name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool FileExists(string name)
|
|
||||||
{
|
|
||||||
return _romFs.FileExists(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface)
|
|
||||||
{
|
|
||||||
RomfsDir directory = _romFs.Directories.Find(x => x.Name == name);
|
|
||||||
|
|
||||||
if (directory != null)
|
|
||||||
{
|
|
||||||
directoryInterface = new IDirectory(name, filterFlags, this);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
directoryInterface = null;
|
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long OpenFile(string name, out IFile fileInterface)
|
|
||||||
{
|
|
||||||
if (_romFs.FileExists(name))
|
|
||||||
{
|
|
||||||
Stream stream = _romFs.OpenFile(name).AsStream();
|
|
||||||
|
|
||||||
fileInterface = new IFile(stream, name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileInterface = null;
|
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long RenameDirectory(string oldName, string newName)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long RenameFile(string oldName, string newName)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CheckIfOutsideBasePath(string path)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileTimestamp GetFileTimeStampRaw(string name)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,11 @@
|
||||||
using LibHac;
|
using LibHac.Fs;
|
||||||
using LibHac.IO;
|
using LibHac.Fs.NcaUtils;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
using Ryujinx.HLE.FileSystem.Content;
|
||||||
using Ryujinx.HLE.Resource;
|
using Ryujinx.HLE.Resource;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using static Ryujinx.HLE.Utilities.FontUtils;
|
using static Ryujinx.HLE.Utilities.FontUtils;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Font
|
namespace Ryujinx.HLE.HOS.Font
|
||||||
|
@ -53,29 +51,21 @@ namespace Ryujinx.HLE.HOS.Font
|
||||||
|
|
||||||
FontInfo CreateFont(string name)
|
FontInfo CreateFont(string name)
|
||||||
{
|
{
|
||||||
if (contentManager.TryGetFontTitle(name, out long fontTitle))
|
if (contentManager.TryGetFontTitle(name, out long fontTitle) &&
|
||||||
|
contentManager.TryGetFontFilename(name, out string fontFilename))
|
||||||
{
|
{
|
||||||
string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, ContentType.Data);
|
string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, ContentType.Data);
|
||||||
string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath);
|
string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(fontPath))
|
if (!string.IsNullOrWhiteSpace(fontPath))
|
||||||
{
|
{
|
||||||
int fileIndex = 0;
|
|
||||||
|
|
||||||
//Use second file in Chinese Font title for standard
|
|
||||||
if(name == "FontChineseSimplified")
|
|
||||||
{
|
|
||||||
fileIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
using (FileStream ncaFileStream = new FileStream(fontPath, FileMode.Open, FileAccess.Read))
|
using (IStorage ncaFileStream = new LocalStorage(fontPath, FileAccess.Read, FileMode.Open))
|
||||||
{
|
{
|
||||||
Nca nca = new Nca(_device.System.KeySet, ncaFileStream.AsStorage(), false);
|
Nca nca = new Nca(_device.System.KeySet, ncaFileStream);
|
||||||
NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
|
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
|
||||||
Romfs romfs = new Romfs(nca.OpenSection(romfsSection.SectionNum, false, _device.System.FsIntegrityCheckLevel, false));
|
Stream fontFile = romfs.OpenFile(fontFilename, OpenMode.Read).AsStream();
|
||||||
Stream fontFile = romfs.OpenFile(romfs.Files[fileIndex]).AsStream();
|
|
||||||
|
|
||||||
data = DecryptFont(fontFile);
|
data = DecryptFont(fontFile);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.IO;
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.NcaUtils;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
using Ryujinx.HLE.FileSystem.Content;
|
||||||
using Ryujinx.HLE.HOS.Font;
|
using Ryujinx.HLE.HOS.Font;
|
||||||
|
@ -195,58 +196,9 @@ namespace Ryujinx.HLE.HOS
|
||||||
Device.FileSystem.LoadRomFs(romFsFile);
|
Device.FileSystem.LoadRomFs(romFsFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
string npdmFileName = Path.Combine(exeFsDir, "main.npdm");
|
LocalFileSystem codeFs = new LocalFileSystem(exeFsDir);
|
||||||
|
|
||||||
Npdm metaData = null;
|
LoadExeFs(codeFs, out _);
|
||||||
|
|
||||||
if (File.Exists(npdmFileName))
|
|
||||||
{
|
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
|
|
||||||
|
|
||||||
using (FileStream input = new FileStream(npdmFileName, FileMode.Open))
|
|
||||||
{
|
|
||||||
metaData = new Npdm(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
|
||||||
|
|
||||||
metaData = GetDefaultNpdm();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IExecutable> staticObjects = new List<IExecutable>();
|
|
||||||
|
|
||||||
void LoadNso(string searchPattern)
|
|
||||||
{
|
|
||||||
foreach (string file in Directory.GetFiles(exeFsDir, searchPattern))
|
|
||||||
{
|
|
||||||
if (Path.GetExtension(file) != string.Empty)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(file)}...");
|
|
||||||
|
|
||||||
using (FileStream input = new FileStream(file, FileMode.Open))
|
|
||||||
{
|
|
||||||
NxStaticObject staticObject = new NxStaticObject(input);
|
|
||||||
|
|
||||||
staticObjects.Add(staticObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
|
|
||||||
|
|
||||||
LoadNso("rtld");
|
|
||||||
LoadNso("main");
|
|
||||||
LoadNso("subsdk*");
|
|
||||||
LoadNso("sdk");
|
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
|
||||||
|
|
||||||
ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadXci(string xciFile)
|
public void LoadXci(string xciFile)
|
||||||
|
@ -255,7 +207,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
Xci xci = new Xci(KeySet, file.AsStorage());
|
Xci xci = new Xci(KeySet, file.AsStorage());
|
||||||
|
|
||||||
(Nca mainNca, Nca controlNca) = GetXciGameData(xci);
|
(Nca mainNca, Nca patchNca, Nca controlNca) = GetXciGameData(xci);
|
||||||
|
|
||||||
if (mainNca == null)
|
if (mainNca == null)
|
||||||
{
|
{
|
||||||
|
@ -266,7 +218,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries();
|
||||||
|
|
||||||
LoadNca(mainNca, controlNca);
|
LoadNca(mainNca, patchNca, controlNca);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadKip(string kipFile)
|
public void LoadKip(string kipFile)
|
||||||
|
@ -277,9 +229,9 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private (Nca Main, Nca Control) GetXciGameData(Xci xci)
|
private (Nca Main, Nca patch, Nca Control) GetXciGameData(Xci xci)
|
||||||
{
|
{
|
||||||
if (xci.SecurePartition == null)
|
if (!xci.HasPartition(XciPartitionType.Secure))
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("Could not find XCI secure partition");
|
throw new InvalidDataException("Could not find XCI secure partition");
|
||||||
}
|
}
|
||||||
|
@ -288,9 +240,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
Nca patchNca = null;
|
Nca patchNca = null;
|
||||||
Nca controlNca = null;
|
Nca controlNca = null;
|
||||||
|
|
||||||
foreach (PfsFileEntry ticketEntry in xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".tik")))
|
XciPartition securePartition = xci.OpenPartition(XciPartitionType.Secure);
|
||||||
|
|
||||||
|
foreach (DirectoryEntry ticketEntry in securePartition.EnumerateEntries("*.tik"))
|
||||||
{
|
{
|
||||||
Ticket ticket = new Ticket(xci.SecurePartition.OpenFile(ticketEntry).AsStream());
|
Ticket ticket = new Ticket(securePartition.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream());
|
||||||
|
|
||||||
if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId))
|
if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId))
|
||||||
{
|
{
|
||||||
|
@ -298,22 +252,24 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (PfsFileEntry fileEntry in xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".nca")))
|
foreach (DirectoryEntry fileEntry in securePartition.EnumerateEntries("*.nca"))
|
||||||
{
|
{
|
||||||
IStorage ncaStorage = xci.SecurePartition.OpenFile(fileEntry);
|
IStorage ncaStorage = securePartition.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage();
|
||||||
|
|
||||||
Nca nca = new Nca(KeySet, ncaStorage, true);
|
Nca nca = new Nca(KeySet, ncaStorage);
|
||||||
|
|
||||||
if (nca.Header.ContentType == ContentType.Program)
|
if (nca.Header.ContentType == ContentType.Program)
|
||||||
{
|
{
|
||||||
if (nca.Sections.Any(x => x?.Type == SectionType.Romfs))
|
int dataIndex = Nca.SectionIndexFromType(NcaSectionType.Data, ContentType.Program);
|
||||||
{
|
|
||||||
mainNca = nca;
|
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
|
||||||
}
|
|
||||||
else if (nca.Sections.Any(x => x?.Type == SectionType.Bktr))
|
|
||||||
{
|
{
|
||||||
patchNca = nca;
|
patchNca = nca;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mainNca = nca;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (nca.Header.ContentType == ContentType.Control)
|
else if (nca.Header.ContentType == ContentType.Control)
|
||||||
{
|
{
|
||||||
|
@ -326,50 +282,43 @@ namespace Ryujinx.HLE.HOS
|
||||||
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file");
|
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file");
|
||||||
}
|
}
|
||||||
|
|
||||||
mainNca.SetBaseNca(patchNca);
|
|
||||||
|
|
||||||
if (controlNca != null)
|
if (controlNca != null)
|
||||||
{
|
{
|
||||||
ReadControlData(controlNca);
|
ReadControlData(controlNca);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (patchNca != null)
|
return (mainNca, patchNca, controlNca);
|
||||||
{
|
|
||||||
patchNca.SetBaseNca(mainNca);
|
|
||||||
|
|
||||||
return (patchNca, controlNca);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (mainNca, controlNca);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReadControlData(Nca controlNca)
|
public void ReadControlData(Nca controlNca)
|
||||||
{
|
{
|
||||||
Romfs controlRomfs = new Romfs(controlNca.OpenSection(0, false, FsIntegrityCheckLevel, true));
|
IFileSystem controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, FsIntegrityCheckLevel);
|
||||||
|
|
||||||
IStorage controlFile = controlRomfs.OpenFile("/control.nacp");
|
IFile controlFile = controlFs.OpenFile("/control.nacp", OpenMode.Read);
|
||||||
|
|
||||||
ControlData = new Nacp(controlFile.AsStream());
|
ControlData = new Nacp(controlFile.AsStream());
|
||||||
|
|
||||||
|
TitleName = CurrentTitle = ControlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNca(string ncaFile)
|
public void LoadNca(string ncaFile)
|
||||||
{
|
{
|
||||||
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
Nca nca = new Nca(KeySet, file.AsStorage(false), false);
|
Nca nca = new Nca(KeySet, file.AsStorage(false));
|
||||||
|
|
||||||
LoadNca(nca, null);
|
LoadNca(nca, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNsp(string nspFile)
|
public void LoadNsp(string nspFile)
|
||||||
{
|
{
|
||||||
FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read);
|
FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
Pfs nsp = new Pfs(file.AsStorage(false));
|
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
|
||||||
|
|
||||||
foreach (PfsFileEntry ticketEntry in nsp.Files.Where(x => x.Name.EndsWith(".tik")))
|
foreach (DirectoryEntry ticketEntry in nsp.EnumerateEntries("*.tik"))
|
||||||
{
|
{
|
||||||
Ticket ticket = new Ticket(nsp.OpenFile(ticketEntry).AsStream());
|
Ticket ticket = new Ticket(nsp.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream());
|
||||||
|
|
||||||
if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId))
|
if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId))
|
||||||
{
|
{
|
||||||
|
@ -378,15 +327,27 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
|
|
||||||
Nca mainNca = null;
|
Nca mainNca = null;
|
||||||
|
Nca patchNca = null;
|
||||||
Nca controlNca = null;
|
Nca controlNca = null;
|
||||||
|
|
||||||
foreach (PfsFileEntry ncaFile in nsp.Files.Where(x => x.Name.EndsWith(".nca")))
|
foreach (DirectoryEntry fileEntry in nsp.EnumerateEntries("*.nca"))
|
||||||
{
|
{
|
||||||
Nca nca = new Nca(KeySet, nsp.OpenFile(ncaFile), true);
|
IStorage ncaStorage = nsp.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage();
|
||||||
|
|
||||||
|
Nca nca = new Nca(KeySet, ncaStorage);
|
||||||
|
|
||||||
if (nca.Header.ContentType == ContentType.Program)
|
if (nca.Header.ContentType == ContentType.Program)
|
||||||
{
|
{
|
||||||
mainNca = nca;
|
int dataIndex = Nca.SectionIndexFromType(NcaSectionType.Data, ContentType.Program);
|
||||||
|
|
||||||
|
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
|
||||||
|
{
|
||||||
|
patchNca = nca;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mainNca = nca;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (nca.Header.ContentType == ContentType.Control)
|
else if (nca.Header.ContentType == ContentType.Control)
|
||||||
{
|
{
|
||||||
|
@ -396,65 +357,16 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
if (mainNca != null)
|
if (mainNca != null)
|
||||||
{
|
{
|
||||||
LoadNca(mainNca, controlNca);
|
LoadNca(mainNca, patchNca, controlNca);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is not a normal NSP, it's actually a ExeFS as a NSP
|
// This is not a normal NSP, it's actually a ExeFS as a NSP
|
||||||
Npdm metaData = null;
|
LoadExeFs(nsp, out _);
|
||||||
|
|
||||||
PfsFileEntry npdmFile = nsp.Files.FirstOrDefault(x => x.Name.Equals("main.npdm"));
|
|
||||||
|
|
||||||
if (npdmFile != null)
|
|
||||||
{
|
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
|
|
||||||
|
|
||||||
metaData = new Npdm(nsp.OpenFile(npdmFile).AsStream());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
|
||||||
|
|
||||||
metaData = GetDefaultNpdm();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IExecutable> staticObjects = new List<IExecutable>();
|
|
||||||
|
|
||||||
void LoadNso(string searchPattern)
|
|
||||||
{
|
|
||||||
PfsFileEntry entry = nsp.Files.FirstOrDefault(x => x.Name.Equals(searchPattern));
|
|
||||||
|
|
||||||
if (entry != null)
|
|
||||||
{
|
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading {entry.Name}...");
|
|
||||||
|
|
||||||
NxStaticObject staticObject = new NxStaticObject(nsp.OpenFile(entry).AsStream());
|
|
||||||
|
|
||||||
staticObjects.Add(staticObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
|
|
||||||
|
|
||||||
LoadNso("rtld");
|
|
||||||
LoadNso("main");
|
|
||||||
LoadNso("subsdk*");
|
|
||||||
LoadNso("sdk");
|
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
|
||||||
|
|
||||||
if (staticObjects.Count == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNca(Nca mainNca, Nca controlNca)
|
public void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
|
||||||
{
|
{
|
||||||
if (mainNca.Header.ContentType != ContentType.Program)
|
if (mainNca.Header.ContentType != ContentType.Program)
|
||||||
{
|
{
|
||||||
|
@ -463,71 +375,64 @@ namespace Ryujinx.HLE.HOS
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IStorage romfsStorage = mainNca.OpenSection(ProgramPartitionType.Data, false, FsIntegrityCheckLevel, false);
|
IStorage dataStorage = null;
|
||||||
IStorage exefsStorage = mainNca.OpenSection(ProgramPartitionType.Code, false, FsIntegrityCheckLevel, true);
|
IFileSystem codeFs = null;
|
||||||
|
|
||||||
if (exefsStorage == null)
|
if (patchNca == null)
|
||||||
|
{
|
||||||
|
if (mainNca.CanOpenSection(NcaSectionType.Data))
|
||||||
|
{
|
||||||
|
dataStorage = mainNca.OpenStorage(NcaSectionType.Data, FsIntegrityCheckLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainNca.CanOpenSection(NcaSectionType.Code))
|
||||||
|
{
|
||||||
|
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, FsIntegrityCheckLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (patchNca.CanOpenSection(NcaSectionType.Data))
|
||||||
|
{
|
||||||
|
dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, FsIntegrityCheckLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patchNca.CanOpenSection(NcaSectionType.Code))
|
||||||
|
{
|
||||||
|
codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, FsIntegrityCheckLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeFs == null)
|
||||||
{
|
{
|
||||||
Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA");
|
Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (romfsStorage == null)
|
if (dataStorage == null)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA");
|
Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Device.FileSystem.SetRomFs(romfsStorage.AsStream(false));
|
Device.FileSystem.SetRomFs(dataStorage.AsStream(FileAccess.Read));
|
||||||
}
|
}
|
||||||
|
|
||||||
Pfs exefs = new Pfs(exefsStorage);
|
LoadExeFs(codeFs, out Npdm metaData);
|
||||||
|
|
||||||
Npdm metaData = null;
|
|
||||||
|
|
||||||
if (exefs.FileExists("main.npdm"))
|
|
||||||
{
|
|
||||||
Logger.PrintInfo(LogClass.Loader, "Loading main.npdm...");
|
|
||||||
|
|
||||||
metaData = new Npdm(exefs.OpenFile("main.npdm").AsStream());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
|
||||||
|
|
||||||
metaData = GetDefaultNpdm();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IExecutable> staticObjects = new List<IExecutable>();
|
|
||||||
|
|
||||||
void LoadNso(string filename)
|
|
||||||
{
|
|
||||||
foreach (PfsFileEntry file in exefs.Files.Where(x => x.Name.StartsWith(filename)))
|
|
||||||
{
|
|
||||||
if (Path.GetExtension(file.Name) != string.Empty)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading {filename}...");
|
|
||||||
|
|
||||||
NxStaticObject staticObject = new NxStaticObject(exefs.OpenFile(file).AsStream());
|
|
||||||
|
|
||||||
staticObjects.Add(staticObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Nacp ReadControlData()
|
Nacp ReadControlData()
|
||||||
{
|
{
|
||||||
Romfs controlRomfs = new Romfs(controlNca.OpenSection(0, false, FsIntegrityCheckLevel, true));
|
IFileSystem controlRomfs = controlNca.OpenFileSystem(NcaSectionType.Data, FsIntegrityCheckLevel);
|
||||||
|
|
||||||
IStorage controlFile = controlRomfs.OpenFile("/control.nacp");
|
IFile controlFile = controlRomfs.OpenFile("/control.nacp", OpenMode.Read);
|
||||||
|
|
||||||
Nacp controlData = new Nacp(controlFile.AsStream());
|
Nacp controlData = new Nacp(controlFile.AsStream());
|
||||||
|
|
||||||
TitleName = CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
|
TitleName = CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
|
||||||
TitleID = metaData.Aci0.TitleId.ToString("x16");
|
TitleID = metaData.Aci0.TitleId.ToString("x16");
|
||||||
|
|
||||||
|
CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(CurrentTitle))
|
if (string.IsNullOrWhiteSpace(CurrentTitle))
|
||||||
{
|
{
|
||||||
|
@ -545,6 +450,43 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
|
TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadExeFs(IFileSystem codeFs, out Npdm metaData)
|
||||||
|
{
|
||||||
|
if (codeFs.FileExists("/main.npdm"))
|
||||||
|
{
|
||||||
|
Logger.PrintInfo(LogClass.Loader, "Loading main.npdm...");
|
||||||
|
|
||||||
|
metaData = new Npdm(codeFs.OpenFile("/main.npdm", OpenMode.Read).AsStream());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.PrintWarning(LogClass.Loader, "NPDM file not found, using default values!");
|
||||||
|
|
||||||
|
metaData = GetDefaultNpdm();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IExecutable> staticObjects = new List<IExecutable>();
|
||||||
|
|
||||||
|
void LoadNso(string filename)
|
||||||
|
{
|
||||||
|
foreach (DirectoryEntry file in codeFs.EnumerateEntries($"{filename}*"))
|
||||||
|
{
|
||||||
|
if (Path.GetExtension(file.Name) != string.Empty)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.PrintInfo(LogClass.Loader, $"Loading {file.Name}...");
|
||||||
|
|
||||||
|
NxStaticObject staticObject = new NxStaticObject(codeFs.OpenFile(file.FullPath, OpenMode.Read).AsStream());
|
||||||
|
|
||||||
|
staticObjects.Add(staticObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
|
||||||
|
|
||||||
LoadNso("rtld");
|
LoadNso("rtld");
|
||||||
LoadNso("main");
|
LoadNso("main");
|
||||||
|
@ -613,7 +555,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries();
|
||||||
|
|
||||||
TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
|
TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
|
||||||
TitleName = metaData.TitleName;
|
TitleName = metaData.TitleName;
|
||||||
|
|
||||||
ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject });
|
ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject });
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
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)
|
|
||||||
{
|
|
||||||
Path = path;
|
|
||||||
EntryType = directoryEntryType;
|
|
||||||
Size = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|
||||||
{
|
|
||||||
public enum DirectoryEntryType
|
|
||||||
{
|
|
||||||
Directory,
|
|
||||||
File
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|
||||||
{
|
|
||||||
struct FileTimestamp
|
|
||||||
{
|
|
||||||
public DateTime CreationDateTime;
|
|
||||||
public DateTime ModifiedDateTime;
|
|
||||||
public DateTime LastAccessDateTime;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,6 @@
|
||||||
using Ryujinx.HLE.FileSystem;
|
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
@ -15,17 +13,15 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||||
|
|
||||||
private List<DirectoryEntry> _directoryEntries;
|
private IEnumerator<LibHac.Fs.DirectoryEntry> _enumerator;
|
||||||
|
|
||||||
private int _currentItemIndex;
|
|
||||||
|
|
||||||
public event EventHandler<EventArgs> Disposed;
|
public event EventHandler<EventArgs> Disposed;
|
||||||
|
|
||||||
public string DirectoryPath { get; private set; }
|
public string Path { get; }
|
||||||
|
|
||||||
private IFileSystemProvider _provider;
|
private LibHac.Fs.IDirectory _provider;
|
||||||
|
|
||||||
public IDirectory(string directoryPath, int flags, IFileSystemProvider provider)
|
public IDirectory(LibHac.Fs.IDirectory directory)
|
||||||
{
|
{
|
||||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||||
{
|
{
|
||||||
|
@ -33,22 +29,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{ 1, GetEntryCount }
|
{ 1, GetEntryCount }
|
||||||
};
|
};
|
||||||
|
|
||||||
_provider = provider;
|
_provider = directory;
|
||||||
DirectoryPath = directoryPath;
|
|
||||||
|
|
||||||
_directoryEntries = new List<DirectoryEntry>();
|
Path = directory.FullPath;
|
||||||
|
|
||||||
if ((flags & 1) != 0)
|
_enumerator = directory.Read().GetEnumerator();
|
||||||
{
|
|
||||||
_directoryEntries.AddRange(provider.GetDirectories(directoryPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((flags & 2) != 0)
|
|
||||||
{
|
|
||||||
_directoryEntries.AddRange(provider.GetFiles(directoryPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentItemIndex = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read() -> (u64 count, buffer<nn::fssrv::sf::IDirectoryEntry, 6, 0> entries)
|
// Read() -> (u64 count, buffer<nn::fssrv::sf::IDirectoryEntry, 6, 0> entries)
|
||||||
|
@ -58,41 +43,42 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
long bufferLen = context.Request.ReceiveBuff[0].Size;
|
long bufferLen = context.Request.ReceiveBuff[0].Size;
|
||||||
|
|
||||||
int maxReadCount = (int)(bufferLen / DirectoryEntrySize);
|
int maxReadCount = (int)(bufferLen / DirectoryEntrySize);
|
||||||
|
int readCount = 0;
|
||||||
|
|
||||||
int count = Math.Min(_directoryEntries.Count - _currentItemIndex, maxReadCount);
|
while (readCount < maxReadCount && _enumerator.MoveNext())
|
||||||
|
|
||||||
for (int index = 0; index < count; index++)
|
|
||||||
{
|
{
|
||||||
long position = bufferPosition + index * DirectoryEntrySize;
|
long position = bufferPosition + readCount * DirectoryEntrySize;
|
||||||
|
|
||||||
WriteDirectoryEntry(context, position, _directoryEntries[_currentItemIndex++]);
|
WriteDirectoryEntry(context, position, _enumerator.Current);
|
||||||
|
|
||||||
|
readCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.ResponseData.Write((long)count);
|
context.ResponseData.Write((long)readCount);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteDirectoryEntry(ServiceCtx context, long position, DirectoryEntry entry)
|
private void WriteDirectoryEntry(ServiceCtx context, long position, LibHac.Fs.DirectoryEntry entry)
|
||||||
{
|
{
|
||||||
for (int offset = 0; offset < 0x300; offset += 8)
|
for (int offset = 0; offset < 0x300; offset += 8)
|
||||||
{
|
{
|
||||||
context.Memory.WriteInt64(position + offset, 0);
|
context.Memory.WriteInt64(position + offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] nameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(entry.Path));
|
byte[] nameBuffer = Encoding.UTF8.GetBytes(entry.Name);
|
||||||
|
|
||||||
context.Memory.WriteBytes(position, nameBuffer);
|
context.Memory.WriteBytes(position, nameBuffer);
|
||||||
|
|
||||||
context.Memory.WriteInt32(position + 0x300, 0); //Padding?
|
context.Memory.WriteInt32(position + 0x300, (int)entry.Attributes);
|
||||||
context.Memory.WriteInt32(position + 0x304, (byte)entry.EntryType);
|
context.Memory.WriteInt32(position + 0x304, (byte)entry.Type);
|
||||||
context.Memory.WriteInt64(position + 0x308, entry.Size);
|
context.Memory.WriteInt64(position + 0x308, entry.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEntryCount() -> u64
|
// GetEntryCount() -> u64
|
||||||
public long GetEntryCount(ServiceCtx context)
|
public long GetEntryCount(ServiceCtx context)
|
||||||
{
|
{
|
||||||
context.ResponseData.Write((long)_directoryEntries.Count);
|
context.ResponseData.Write((long)_provider.GetEntryCount());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
|
@ -11,13 +10,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||||
|
|
||||||
private Stream _baseStream;
|
private LibHac.Fs.IFile _baseFile;
|
||||||
|
|
||||||
public event EventHandler<EventArgs> Disposed;
|
public event EventHandler<EventArgs> Disposed;
|
||||||
|
|
||||||
public string HostPath { get; private set; }
|
public string Path { get; private set; }
|
||||||
|
|
||||||
public IFile(Stream baseStream, string hostPath)
|
public IFile(LibHac.Fs.IFile baseFile, string path)
|
||||||
{
|
{
|
||||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||||
{
|
{
|
||||||
|
@ -28,24 +27,24 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{ 4, GetSize }
|
{ 4, GetSize }
|
||||||
};
|
};
|
||||||
|
|
||||||
_baseStream = baseStream;
|
_baseFile = baseFile;
|
||||||
HostPath = hostPath;
|
Path = LibHac.Fs.PathTools.Normalize(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read(u32, u64 offset, u64 size) -> (u64 out_size, buffer<u8, 0x46, 0> out_buf)
|
// Read(u32 readOption, u64 offset, u64 size) -> (u64 out_size, buffer<u8, 0x46, 0> out_buf)
|
||||||
public long Read(ServiceCtx context)
|
public long Read(ServiceCtx context)
|
||||||
{
|
{
|
||||||
long position = context.Request.ReceiveBuff[0].Position;
|
long position = context.Request.ReceiveBuff[0].Position;
|
||||||
|
|
||||||
long zero = context.RequestData.ReadInt64();
|
int readOption = context.RequestData.ReadInt32();
|
||||||
|
context.RequestData.BaseStream.Position += 4;
|
||||||
|
|
||||||
long offset = context.RequestData.ReadInt64();
|
long offset = context.RequestData.ReadInt64();
|
||||||
long size = context.RequestData.ReadInt64();
|
long size = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
byte[] data = new byte[size];
|
byte[] data = new byte[size];
|
||||||
|
|
||||||
_baseStream.Seek(offset, SeekOrigin.Begin);
|
int readSize = _baseFile.Read(data, offset);
|
||||||
|
|
||||||
int readSize = _baseStream.Read(data, 0, (int)size);
|
|
||||||
|
|
||||||
context.Memory.WriteBytes(position, data);
|
context.Memory.WriteBytes(position, data);
|
||||||
|
|
||||||
|
@ -54,19 +53,20 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write(u32, u64 offset, u64 size, buffer<u8, 0x45, 0>)
|
// Write(u32 writeOption, u64 offset, u64 size, buffer<u8, 0x45, 0>)
|
||||||
public long Write(ServiceCtx context)
|
public long Write(ServiceCtx context)
|
||||||
{
|
{
|
||||||
long position = context.Request.SendBuff[0].Position;
|
long position = context.Request.SendBuff[0].Position;
|
||||||
|
|
||||||
long zero = context.RequestData.ReadInt64();
|
int writeOption = context.RequestData.ReadInt32();
|
||||||
|
context.RequestData.BaseStream.Position += 4;
|
||||||
|
|
||||||
long offset = context.RequestData.ReadInt64();
|
long offset = context.RequestData.ReadInt64();
|
||||||
long size = context.RequestData.ReadInt64();
|
long size = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
byte[] data = context.Memory.ReadBytes(position, size);
|
byte[] data = context.Memory.ReadBytes(position, size);
|
||||||
|
|
||||||
_baseStream.Seek(offset, SeekOrigin.Begin);
|
_baseFile.Write(data, offset);
|
||||||
_baseStream.Write(data, 0, (int)size);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
// Flush()
|
// Flush()
|
||||||
public long Flush(ServiceCtx context)
|
public long Flush(ServiceCtx context)
|
||||||
{
|
{
|
||||||
_baseStream.Flush();
|
_baseFile.Flush();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
long size = context.RequestData.ReadInt64();
|
long size = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
_baseStream.SetLength(size);
|
_baseFile.SetSize(size);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
// GetSize() -> u64 fileSize
|
// GetSize() -> u64 fileSize
|
||||||
public long GetSize(ServiceCtx context)
|
public long GetSize(ServiceCtx context)
|
||||||
{
|
{
|
||||||
context.ResponseData.Write(_baseStream.Length);
|
context.ResponseData.Write(_baseFile.GetSize());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -104,9 +104,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing && _baseStream != null)
|
if (disposing && _baseFile != null)
|
||||||
{
|
{
|
||||||
_baseStream.Dispose();
|
_baseFile.Dispose();
|
||||||
|
|
||||||
Disposed?.Invoke(this, EventArgs.Empty);
|
Disposed?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using Ryujinx.HLE.FileSystem;
|
using LibHac.Fs;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
using static Ryujinx.HLE.HOS.ErrorCode;
|
||||||
using static Ryujinx.HLE.Utilities.StringUtils;
|
using static Ryujinx.HLE.Utilities.StringUtils;
|
||||||
|
|
||||||
|
@ -17,11 +17,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
private HashSet<string> _openPaths;
|
private HashSet<string> _openPaths;
|
||||||
|
|
||||||
private string _path;
|
private LibHac.Fs.IFileSystem _provider;
|
||||||
|
|
||||||
private IFileSystemProvider _provider;
|
public IFileSystem(LibHac.Fs.IFileSystem provider)
|
||||||
|
|
||||||
public IFileSystem(string path, IFileSystemProvider provider)
|
|
||||||
{
|
{
|
||||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||||
{
|
{
|
||||||
|
@ -44,36 +42,50 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
_openPaths = new HashSet<string>();
|
_openPaths = new HashSet<string>();
|
||||||
|
|
||||||
_path = path;
|
|
||||||
_provider = provider;
|
_provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateFile(u32 mode, u64 size, buffer<bytes<0x301>, 0x19, 0x301> path)
|
// CreateFile(u32 createOption, u64 size, buffer<bytes<0x301>, 0x19, 0x301> path)
|
||||||
public long CreateFile(ServiceCtx context)
|
public long CreateFile(ServiceCtx context)
|
||||||
{
|
{
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
long mode = context.RequestData.ReadInt64();
|
int createOption = context.RequestData.ReadInt32();
|
||||||
int size = context.RequestData.ReadInt32();
|
context.RequestData.BaseStream.Position += 4;
|
||||||
|
|
||||||
string fileName = _provider.GetFullPath(name);
|
long size = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
if (fileName == null)
|
if (name == null)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_provider.FileExists(fileName))
|
if (_provider.FileExists(name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathAlreadyInUse(fileName))
|
if (IsPathAlreadyInUse(name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _provider.CreateFile(fileName, size);
|
try
|
||||||
|
{
|
||||||
|
_provider.CreateFile(name, size, (CreateFileOptions)createOption);
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteFile(buffer<bytes<0x301>, 0x19, 0x301> path)
|
// DeleteFile(buffer<bytes<0x301>, 0x19, 0x301> path)
|
||||||
|
@ -81,19 +93,32 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
string fileName = _provider.GetFullPath(name);
|
if (!_provider.FileExists(name))
|
||||||
|
|
||||||
if (!_provider.FileExists(fileName))
|
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathAlreadyInUse(fileName))
|
if (IsPathAlreadyInUse(name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _provider.DeleteFile(fileName);
|
try
|
||||||
|
{
|
||||||
|
_provider.DeleteFile(name);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateDirectory(buffer<bytes<0x301>, 0x19, 0x301> path)
|
// CreateDirectory(buffer<bytes<0x301>, 0x19, 0x301> path)
|
||||||
|
@ -101,24 +126,35 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
string dirName = _provider.GetFullPath(name);
|
if (name == null)
|
||||||
|
|
||||||
if (dirName == null)
|
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_provider.DirectoryExists(dirName))
|
if (_provider.DirectoryExists(name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathAlreadyInUse(dirName))
|
if (IsPathAlreadyInUse(name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
_provider.CreateDirectory(dirName);
|
try
|
||||||
|
{
|
||||||
|
_provider.CreateDirectory(name);
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -126,32 +162,61 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
// DeleteDirectory(buffer<bytes<0x301>, 0x19, 0x301> path)
|
// DeleteDirectory(buffer<bytes<0x301>, 0x19, 0x301> path)
|
||||||
public long DeleteDirectory(ServiceCtx context)
|
public long DeleteDirectory(ServiceCtx context)
|
||||||
{
|
{
|
||||||
return DeleteDirectory(context, false);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
|
if (!_provider.DirectoryExists(name))
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsPathAlreadyInUse(name))
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_provider.DeleteDirectory(name);
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDirectoryRecursively(buffer<bytes<0x301>, 0x19, 0x301> path)
|
// DeleteDirectoryRecursively(buffer<bytes<0x301>, 0x19, 0x301> path)
|
||||||
public long DeleteDirectoryRecursively(ServiceCtx context)
|
public long DeleteDirectoryRecursively(ServiceCtx context)
|
||||||
{
|
|
||||||
return DeleteDirectory(context, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long DeleteDirectory(ServiceCtx context, bool recursive)
|
|
||||||
{
|
{
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
string dirName = _provider.GetFullPath(name);
|
if (!_provider.DirectoryExists(name))
|
||||||
|
|
||||||
if (!Directory.Exists(dirName))
|
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathAlreadyInUse(dirName))
|
if (IsPathAlreadyInUse(name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
_provider.DeleteDirectory(dirName, recursive);
|
try
|
||||||
|
{
|
||||||
|
_provider.DeleteDirectoryRecursively(name);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -162,25 +227,37 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
string oldName = ReadUtf8String(context, 0);
|
string oldName = ReadUtf8String(context, 0);
|
||||||
string newName = ReadUtf8String(context, 1);
|
string newName = ReadUtf8String(context, 1);
|
||||||
|
|
||||||
string oldFileName = _provider.GetFullPath(oldName);
|
if (_provider.FileExists(oldName))
|
||||||
string newFileName = _provider.GetFullPath(newName);
|
|
||||||
|
|
||||||
if (_provider.FileExists(oldFileName))
|
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_provider.FileExists(newFileName))
|
if (_provider.FileExists(newName))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathAlreadyInUse(oldFileName))
|
if (IsPathAlreadyInUse(oldName))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _provider.RenameFile(oldFileName, newFileName);
|
try
|
||||||
|
{
|
||||||
|
_provider.RenameFile(oldName, newName);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {oldName} or {newName}");
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenameDirectory(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath)
|
// RenameDirectory(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath)
|
||||||
|
@ -189,25 +266,37 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
string oldName = ReadUtf8String(context, 0);
|
string oldName = ReadUtf8String(context, 0);
|
||||||
string newName = ReadUtf8String(context, 1);
|
string newName = ReadUtf8String(context, 1);
|
||||||
|
|
||||||
string oldDirName = _provider.GetFullPath(oldName);
|
if (!_provider.DirectoryExists(oldName))
|
||||||
string newDirName = _provider.GetFullPath(newName);
|
|
||||||
|
|
||||||
if (!_provider.DirectoryExists(oldDirName))
|
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_provider.DirectoryExists(newDirName))
|
if (!_provider.DirectoryExists(newName))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathAlreadyInUse(oldDirName))
|
if (IsPathAlreadyInUse(oldName))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _provider.RenameDirectory(oldDirName, newDirName);
|
try
|
||||||
|
{
|
||||||
|
_provider.RenameFile(oldName, newName);
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {oldName} or {newName}");
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEntryType(buffer<bytes<0x301>, 0x19, 0x301> path) -> nn::fssrv::sf::DirectoryEntryType
|
// GetEntryType(buffer<bytes<0x301>, 0x19, 0x301> path) -> nn::fssrv::sf::DirectoryEntryType
|
||||||
|
@ -215,17 +304,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
string fileName = _provider.GetFullPath(name);
|
try
|
||||||
|
{
|
||||||
|
LibHac.Fs.DirectoryEntryType entryType = _provider.GetEntryType(name);
|
||||||
|
|
||||||
if (_provider.FileExists(fileName))
|
context.ResponseData.Write((int)entryType);
|
||||||
{
|
|
||||||
context.ResponseData.Write(1);
|
|
||||||
}
|
}
|
||||||
else if (_provider.DirectoryExists(fileName))
|
catch (FileNotFoundException)
|
||||||
{
|
|
||||||
context.ResponseData.Write(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
context.ResponseData.Write(0);
|
context.ResponseData.Write(0);
|
||||||
|
|
||||||
|
@ -238,81 +323,96 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
// OpenFile(u32 mode, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFile> file
|
// OpenFile(u32 mode, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFile> file
|
||||||
public long OpenFile(ServiceCtx context)
|
public long OpenFile(ServiceCtx context)
|
||||||
{
|
{
|
||||||
int filterFlags = context.RequestData.ReadInt32();
|
int mode = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
string fileName = _provider.GetFullPath(name);
|
if (!_provider.FileExists(name))
|
||||||
|
|
||||||
if (!_provider.FileExists(fileName))
|
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathAlreadyInUse(fileName))
|
if (IsPathAlreadyInUse(name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IFile fileInterface;
|
||||||
|
|
||||||
long error = _provider.OpenFile(fileName, out IFile fileInterface);
|
try
|
||||||
|
|
||||||
if (error == 0)
|
|
||||||
{
|
{
|
||||||
fileInterface.Disposed += RemoveFileInUse;
|
LibHac.Fs.IFile file = _provider.OpenFile(name, (OpenMode)mode);
|
||||||
|
|
||||||
lock (_openPaths)
|
fileInterface = new IFile(file, name);
|
||||||
{
|
}
|
||||||
_openPaths.Add(fileName);
|
catch (UnauthorizedAccessException)
|
||||||
}
|
{
|
||||||
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||||
|
|
||||||
MakeObject(context, fileInterface);
|
throw;
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return error;
|
fileInterface.Disposed += RemoveFileInUse;
|
||||||
|
|
||||||
|
lock (_openPaths)
|
||||||
|
{
|
||||||
|
_openPaths.Add(fileInterface.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
MakeObject(context, fileInterface);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenDirectory(u32 filter_flags, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IDirectory> directory
|
// OpenDirectory(u32 filter_flags, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IDirectory> directory
|
||||||
public long OpenDirectory(ServiceCtx context)
|
public long OpenDirectory(ServiceCtx context)
|
||||||
{
|
{
|
||||||
int filterFlags = context.RequestData.ReadInt32();
|
int mode = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
string dirName = _provider.GetFullPath(name);
|
if (!_provider.DirectoryExists(name))
|
||||||
|
|
||||||
if (!_provider.DirectoryExists(dirName))
|
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathAlreadyInUse(dirName))
|
if (IsPathAlreadyInUse(name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
long error = _provider.OpenDirectory(dirName, filterFlags, out IDirectory dirInterface);
|
IDirectory dirInterface;
|
||||||
|
|
||||||
if (error == 0)
|
try
|
||||||
{
|
{
|
||||||
dirInterface.Disposed += RemoveDirectoryInUse;
|
LibHac.Fs.IDirectory dir = _provider.OpenDirectory(name, (OpenDirectoryMode) mode);
|
||||||
|
|
||||||
lock (_openPaths)
|
dirInterface = new IDirectory(dir);
|
||||||
{
|
}
|
||||||
_openPaths.Add(dirName);
|
catch (UnauthorizedAccessException)
|
||||||
}
|
{
|
||||||
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||||
|
|
||||||
MakeObject(context, dirInterface);
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
return error;
|
dirInterface.Disposed += RemoveDirectoryInUse;
|
||||||
|
|
||||||
|
lock (_openPaths)
|
||||||
|
{
|
||||||
|
_openPaths.Add(dirInterface.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
MakeObject(context, dirInterface);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit()
|
// Commit()
|
||||||
public long Commit(ServiceCtx context)
|
public long Commit(ServiceCtx context)
|
||||||
{
|
{
|
||||||
|
_provider.Commit();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +421,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
context.ResponseData.Write(_provider.GetFreeSpace(context));
|
context.ResponseData.Write(_provider.GetFreeSpaceSize(name));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -331,7 +431,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
context.ResponseData.Write(_provider.GetFreeSpace(context));
|
context.ResponseData.Write(_provider.GetTotalSpaceSize(name));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -341,28 +441,25 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
string dirName = _provider.GetFullPath(name);
|
if (!_provider.DirectoryExists(name))
|
||||||
|
|
||||||
if (!_provider.DirectoryExists(dirName))
|
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathAlreadyInUse(dirName))
|
if (IsPathAlreadyInUse(name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (DirectoryEntry entry in _provider.GetEntries(dirName))
|
try
|
||||||
{
|
{
|
||||||
if (_provider.DirectoryExists(entry.Path))
|
_provider.CleanDirectoryRecursively(name);
|
||||||
{
|
}
|
||||||
_provider.DeleteDirectory(entry.Path, true);
|
catch (UnauthorizedAccessException)
|
||||||
}
|
{
|
||||||
else if (_provider.FileExists(entry.Path))
|
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||||
{
|
|
||||||
_provider.DeleteFile(entry.Path);
|
throw;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -373,15 +470,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
string name = ReadUtf8String(context);
|
string name = ReadUtf8String(context);
|
||||||
|
|
||||||
string path = _provider.GetFullPath(name);
|
if (_provider.FileExists(name) || _provider.DirectoryExists(name))
|
||||||
|
|
||||||
if (_provider.FileExists(path) || _provider.DirectoryExists(path))
|
|
||||||
{
|
{
|
||||||
FileTimestamp timestamp = _provider.GetFileTimeStampRaw(path);
|
FileTimeStampRaw timestamp = _provider.GetFileTimeStampRaw(name);
|
||||||
|
|
||||||
context.ResponseData.Write(new DateTimeOffset(timestamp.CreationDateTime).ToUnixTimeSeconds());
|
context.ResponseData.Write(timestamp.Created);
|
||||||
context.ResponseData.Write(new DateTimeOffset(timestamp.ModifiedDateTime).ToUnixTimeSeconds());
|
context.ResponseData.Write(timestamp.Modified);
|
||||||
context.ResponseData.Write(new DateTimeOffset(timestamp.LastAccessDateTime).ToUnixTimeSeconds());
|
context.ResponseData.Write(timestamp.Accessed);
|
||||||
|
|
||||||
byte[] data = new byte[8];
|
byte[] data = new byte[8];
|
||||||
|
|
||||||
|
@ -412,7 +507,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
fileInterface.Disposed -= RemoveFileInUse;
|
fileInterface.Disposed -= RemoveFileInUse;
|
||||||
|
|
||||||
_openPaths.Remove(fileInterface.HostPath);
|
_openPaths.Remove(fileInterface.Path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,7 +519,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
dirInterface.Disposed -= RemoveDirectoryInUse;
|
dirInterface.Disposed -= RemoveDirectoryInUse;
|
||||||
|
|
||||||
_openPaths.Remove(dirInterface.DirectoryPath);
|
_openPaths.Remove(dirInterface.Path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.IO;
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.NcaUtils;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
|
using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
|
||||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
using static Ryujinx.HLE.HOS.ErrorCode;
|
||||||
|
@ -79,31 +79,31 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
// OpenBisFileSystem(nn::fssrv::sf::Partition partitionID, buffer<bytes<0x301>, 0x19, 0x301>) -> object<nn::fssrv::sf::IFileSystem> Bis
|
// OpenBisFileSystem(nn::fssrv::sf::Partition partitionID, buffer<bytes<0x301>, 0x19, 0x301>) -> object<nn::fssrv::sf::IFileSystem> Bis
|
||||||
public long OpenBisFileSystem(ServiceCtx context)
|
public long OpenBisFileSystem(ServiceCtx context)
|
||||||
{
|
{
|
||||||
int bisPartitionId = context.RequestData.ReadInt32();
|
int bisPartitionId = context.RequestData.ReadInt32();
|
||||||
string partitionString = ReadUtf8String(context);
|
string partitionString = ReadUtf8String(context);
|
||||||
string bisPartitonPath = string.Empty;
|
string bisPartitionPath = string.Empty;
|
||||||
|
|
||||||
switch (bisPartitionId)
|
switch (bisPartitionId)
|
||||||
{
|
{
|
||||||
case 29:
|
case 29:
|
||||||
bisPartitonPath = SafeNandPath;
|
bisPartitionPath = SafeNandPath;
|
||||||
break;
|
break;
|
||||||
case 30:
|
case 30:
|
||||||
case 31:
|
case 31:
|
||||||
bisPartitonPath = SystemNandPath;
|
bisPartitionPath = SystemNandPath;
|
||||||
break;
|
break;
|
||||||
case 32:
|
case 32:
|
||||||
bisPartitonPath = UserNandPath;
|
bisPartitionPath = UserNandPath;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return MakeError(ErrorModule.Fs, FsErr.InvalidInput);
|
return MakeError(ErrorModule.Fs, FsErr.InvalidInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
string fullPath = context.Device.FileSystem.GetFullPartitionPath(bisPartitonPath);
|
string fullPath = context.Device.FileSystem.GetFullPartitionPath(bisPartitionPath);
|
||||||
|
|
||||||
FileSystemProvider fileSystemProvider = new FileSystemProvider(fullPath, context.Device.FileSystem.GetBasePath());
|
LocalFileSystem fileSystem = new LocalFileSystem(fullPath);
|
||||||
|
|
||||||
MakeObject(context, new IFileSystem(fullPath, fileSystemProvider));
|
MakeObject(context, new IFileSystem(fileSystem));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -113,9 +113,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
string sdCardPath = context.Device.FileSystem.GetSdCardPath();
|
string sdCardPath = context.Device.FileSystem.GetSdCardPath();
|
||||||
|
|
||||||
FileSystemProvider fileSystemProvider = new FileSystemProvider(sdCardPath, context.Device.FileSystem.GetBasePath());
|
LocalFileSystem fileSystem = new LocalFileSystem(sdCardPath);
|
||||||
|
|
||||||
MakeObject(context, new IFileSystem(sdCardPath, fileSystemProvider));
|
MakeObject(context, new IFileSystem(fileSystem));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
// OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
|
// OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
|
||||||
public long OpenDataStorageByCurrentProcess(ServiceCtx context)
|
public long OpenDataStorageByCurrentProcess(ServiceCtx context)
|
||||||
{
|
{
|
||||||
MakeObject(context, new IStorage(context.Device.FileSystem.RomFs));
|
MakeObject(context, new IStorage(context.Device.FileSystem.RomFs.AsStorage()));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
if (installedStorage == StorageId.None)
|
if (installedStorage == StorageId.None)
|
||||||
{
|
{
|
||||||
contentType = ContentType.AocData;
|
contentType = ContentType.PublicData;
|
||||||
|
|
||||||
installedStorage =
|
installedStorage =
|
||||||
context.Device.System.ContentManager.GetInstalledStorage(titleId, contentType, storageId);
|
context.Device.System.ContentManager.GetInstalledStorage(titleId, contentType, storageId);
|
||||||
|
@ -175,12 +175,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
if (File.Exists(ncaPath))
|
if (File.Exists(ncaPath))
|
||||||
{
|
{
|
||||||
LibHac.IO.IStorage ncaStorage = new FileStream(ncaPath, FileMode.Open, FileAccess.Read).AsStorage();
|
LibHac.Fs.IStorage ncaStorage = new LocalStorage(ncaPath, FileAccess.Read, FileMode.Open);
|
||||||
Nca nca = new Nca(context.Device.System.KeySet, ncaStorage, false);
|
Nca nca = new Nca(context.Device.System.KeySet, ncaStorage);
|
||||||
NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
|
LibHac.Fs.IStorage romfsStorage = nca.OpenStorage(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel);
|
||||||
Stream romfsStream = nca.OpenSection(romfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel, false).AsStream();
|
|
||||||
|
|
||||||
MakeObject(context, new IStorage(romfsStream));
|
MakeObject(context, new IStorage(romfsStorage));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -201,7 +200,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
// OpenPatchDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage>
|
// OpenPatchDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage>
|
||||||
public long OpenPatchDataStorageByCurrentProcess(ServiceCtx context)
|
public long OpenPatchDataStorageByCurrentProcess(ServiceCtx context)
|
||||||
{
|
{
|
||||||
MakeObject(context, new IStorage(context.Device.FileSystem.RomFs));
|
MakeObject(context, new IStorage(context.Device.FileSystem.RomFs.AsStorage()));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -224,57 +223,44 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
context.RequestData.ReadInt64(),
|
context.RequestData.ReadInt64(),
|
||||||
context.RequestData.ReadInt64());
|
context.RequestData.ReadInt64());
|
||||||
|
|
||||||
long saveId = context.RequestData.ReadInt64();
|
long saveId = context.RequestData.ReadInt64();
|
||||||
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadByte();
|
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadByte();
|
||||||
SaveInfo saveInfo = new SaveInfo(titleId, saveId, saveDataType, userId, saveSpaceId);
|
SaveInfo saveInfo = new SaveInfo(titleId, saveId, saveDataType, userId, saveSpaceId);
|
||||||
string savePath = context.Device.FileSystem.GetGameSavePath(saveInfo, context);
|
string savePath = context.Device.FileSystem.GetGameSavePath(saveInfo, context);
|
||||||
FileSystemProvider fileSystemProvider = new FileSystemProvider(savePath, context.Device.FileSystem.GetBasePath());
|
LocalFileSystem fileSystem = new LocalFileSystem(savePath);
|
||||||
|
|
||||||
MakeObject(context, new IFileSystem(savePath, fileSystemProvider));
|
DirectorySaveDataFileSystem saveFileSystem = new DirectorySaveDataFileSystem(fileSystem);
|
||||||
|
|
||||||
|
MakeObject(context, new IFileSystem(saveFileSystem));
|
||||||
}
|
}
|
||||||
|
|
||||||
private long OpenNsp(ServiceCtx context, string pfsPath)
|
private long OpenNsp(ServiceCtx context, string pfsPath)
|
||||||
{
|
{
|
||||||
FileStream pfsFile = new FileStream(pfsPath, FileMode.Open, FileAccess.Read);
|
LocalStorage storage = new LocalStorage(pfsPath, FileAccess.Read, FileMode.Open);
|
||||||
Pfs nsp = new Pfs(pfsFile.AsStorage());
|
PartitionFileSystem nsp = new PartitionFileSystem(storage);
|
||||||
|
|
||||||
ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet);
|
ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet);
|
||||||
|
|
||||||
|
IFileSystem nspFileSystem = new IFileSystem(nsp);
|
||||||
IFileSystem nspFileSystem = new IFileSystem(pfsPath, new PFsProvider(nsp));
|
|
||||||
|
|
||||||
MakeObject(context, nspFileSystem);
|
MakeObject(context, nspFileSystem);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long OpenNcaFs(ServiceCtx context, string ncaPath, LibHac.IO.IStorage ncaStorage)
|
private long OpenNcaFs(ServiceCtx context, string ncaPath, LibHac.Fs.IStorage ncaStorage)
|
||||||
{
|
{
|
||||||
Nca nca = new Nca(context.Device.System.KeySet, ncaStorage, false);
|
Nca nca = new Nca(context.Device.System.KeySet, ncaStorage);
|
||||||
|
|
||||||
NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
|
if (!nca.SectionExists(NcaSectionType.Data))
|
||||||
NcaSection pfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0);
|
|
||||||
|
|
||||||
if (romfsSection != null)
|
|
||||||
{
|
|
||||||
LibHac.IO.IStorage romfsStorage = nca.OpenSection(romfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel, false);
|
|
||||||
IFileSystem ncaFileSystem = new IFileSystem(ncaPath, new RomFsProvider(romfsStorage));
|
|
||||||
|
|
||||||
MakeObject(context, ncaFileSystem);
|
|
||||||
}
|
|
||||||
else if(pfsSection != null)
|
|
||||||
{
|
|
||||||
LibHac.IO.IStorage pfsStorage = nca.OpenSection(pfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel, false);
|
|
||||||
Pfs pfs = new Pfs(pfsStorage);
|
|
||||||
IFileSystem ncaFileSystem = new IFileSystem(ncaPath, new PFsProvider(pfs));
|
|
||||||
|
|
||||||
MakeObject(context, ncaFileSystem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PartitionNotFound);
|
return MakeError(ErrorModule.Fs, FsErr.PartitionNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LibHac.Fs.IFileSystem fileSystem = nca.OpenFileSystem(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel);
|
||||||
|
|
||||||
|
MakeObject(context, new IFileSystem(fileSystem));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +280,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
FileMode.Open,
|
FileMode.Open,
|
||||||
FileAccess.Read);
|
FileAccess.Read);
|
||||||
|
|
||||||
Pfs nsp = new Pfs(pfsFile.AsStorage());
|
PartitionFileSystem nsp = new PartitionFileSystem(pfsFile.AsStorage());
|
||||||
|
|
||||||
ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet);
|
ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet);
|
||||||
|
|
||||||
|
@ -302,18 +288,18 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
if (nsp.FileExists(filename))
|
if (nsp.FileExists(filename))
|
||||||
{
|
{
|
||||||
return OpenNcaFs(context, fullPath, nsp.OpenFile(filename));
|
return OpenNcaFs(context, fullPath, nsp.OpenFile(filename, OpenMode.Read).AsStorage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ImportTitleKeysFromNsp(Pfs nsp, Keyset keySet)
|
private void ImportTitleKeysFromNsp(LibHac.Fs.IFileSystem nsp, Keyset keySet)
|
||||||
{
|
{
|
||||||
foreach (PfsFileEntry ticketEntry in nsp.Files.Where(x => x.Name.EndsWith(".tik")))
|
foreach (LibHac.Fs.DirectoryEntry ticketEntry in nsp.EnumerateEntries("*.tik"))
|
||||||
{
|
{
|
||||||
Ticket ticket = new Ticket(nsp.OpenFile(ticketEntry).AsStream());
|
Ticket ticket = new Ticket(nsp.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream());
|
||||||
|
|
||||||
if (!keySet.TitleKeys.ContainsKey(ticket.RightsId))
|
if (!keySet.TitleKeys.ContainsKey(ticket.RightsId))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{
|
{
|
||||||
|
@ -10,9 +9,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||||
|
|
||||||
private Stream _baseStream;
|
private LibHac.Fs.IStorage _baseStorage;
|
||||||
|
|
||||||
public IStorage(Stream baseStream)
|
public IStorage(LibHac.Fs.IStorage baseStorage)
|
||||||
{
|
{
|
||||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||||
{
|
{
|
||||||
|
@ -20,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
{ 4, GetSize }
|
{ 4, GetSize }
|
||||||
};
|
};
|
||||||
|
|
||||||
_baseStream = baseStream;
|
_baseStorage = baseStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer
|
// Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer
|
||||||
|
@ -41,11 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
|
|
||||||
byte[] data = new byte[size];
|
byte[] data = new byte[size];
|
||||||
|
|
||||||
lock (_baseStream)
|
_baseStorage.Read(data, offset);
|
||||||
{
|
|
||||||
_baseStream.Seek(offset, SeekOrigin.Begin);
|
|
||||||
_baseStream.Read(data, 0, data.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Memory.WriteBytes(buffDesc.Position, data);
|
context.Memory.WriteBytes(buffDesc.Position, data);
|
||||||
}
|
}
|
||||||
|
@ -56,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||||
// GetSize() -> u64 size
|
// GetSize() -> u64 size
|
||||||
public long GetSize(ServiceCtx context)
|
public long GetSize(ServiceCtx context)
|
||||||
{
|
{
|
||||||
context.ResponseData.Write(_baseStream.Length);
|
context.ResponseData.Write(_baseStorage.GetSize());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using LibHac;
|
using LibHac.Fs.NcaUtils;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
using Ryujinx.HLE.FileSystem.Content;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Lr
|
||||||
{
|
{
|
||||||
long titleId = context.RequestData.ReadInt64();
|
long titleId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
if (ResolvePath(context, titleId, ContentType.Data) || ResolvePath(context, titleId, ContentType.AocData))
|
if (ResolvePath(context, titleId, ContentType.Data) || ResolvePath(context, titleId, ContentType.PublicData))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using LibHac;
|
using LibHac.Fs;
|
||||||
using LibHac.IO;
|
using LibHac.Fs.NcaUtils;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
@ -184,21 +184,20 @@ namespace Ryujinx.HLE.HOS.Services.Set
|
||||||
|
|
||||||
string firmwareTitlePath = device.FileSystem.SwitchPathToSystemPath(contentPath);
|
string firmwareTitlePath = device.FileSystem.SwitchPathToSystemPath(contentPath);
|
||||||
|
|
||||||
using(FileStream firmwareStream = File.Open(firmwareTitlePath, FileMode.Open, FileAccess.Read))
|
using(IStorage firmwareStorage = new LocalStorage(firmwareTitlePath, FileAccess.Read))
|
||||||
{
|
{
|
||||||
Nca firmwareContent = new Nca(device.System.KeySet, firmwareStream.AsStorage(), false);
|
Nca firmwareContent = new Nca(device.System.KeySet, firmwareStorage);
|
||||||
IStorage romFsStorage = firmwareContent.OpenSection(0, false, device.System.FsIntegrityCheckLevel, false);
|
|
||||||
|
|
||||||
if(romFsStorage == null)
|
if (!firmwareContent.CanOpenSection(NcaSectionType.Data))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Romfs firmwareRomFs = new Romfs(romFsStorage);
|
IFileSystem firmwareRomFs = firmwareContent.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel);
|
||||||
|
|
||||||
IStorage firmwareFile = firmwareRomFs.OpenFile("/file");
|
IFile firmwareFile = firmwareRomFs.OpenFile("/file", OpenMode.Read);
|
||||||
|
|
||||||
byte[] data = new byte[firmwareFile.Length];
|
byte[] data = new byte[firmwareFile.GetSize()];
|
||||||
|
|
||||||
firmwareFile.Read(data, 0);
|
firmwareFile.Read(data, 0);
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Concentus" Version="1.1.7" />
|
<PackageReference Include="Concentus" Version="1.1.7" />
|
||||||
<PackageReference Include="LibHac" Version="0.2.0" />
|
<PackageReference Include="LibHac" Version="0.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using LibHac.IO;
|
using LibHac.Fs;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
|
Reference in a new issue