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,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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||
|
@ -15,17 +13,15 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||
|
||||
private List<DirectoryEntry> _directoryEntries;
|
||||
|
||||
private int _currentItemIndex;
|
||||
private IEnumerator<LibHac.Fs.DirectoryEntry> _enumerator;
|
||||
|
||||
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>
|
||||
{
|
||||
|
@ -33,22 +29,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{ 1, GetEntryCount }
|
||||
};
|
||||
|
||||
_provider = provider;
|
||||
DirectoryPath = directoryPath;
|
||||
_provider = directory;
|
||||
|
||||
_directoryEntries = new List<DirectoryEntry>();
|
||||
Path = directory.FullPath;
|
||||
|
||||
if ((flags & 1) != 0)
|
||||
{
|
||||
_directoryEntries.AddRange(provider.GetDirectories(directoryPath));
|
||||
}
|
||||
|
||||
if ((flags & 2) != 0)
|
||||
{
|
||||
_directoryEntries.AddRange(provider.GetFiles(directoryPath));
|
||||
}
|
||||
|
||||
_currentItemIndex = 0;
|
||||
_enumerator = directory.Read().GetEnumerator();
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
int maxReadCount = (int)(bufferLen / DirectoryEntrySize);
|
||||
int readCount = 0;
|
||||
|
||||
int count = Math.Min(_directoryEntries.Count - _currentItemIndex, maxReadCount);
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
while (readCount < maxReadCount && _enumerator.MoveNext())
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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.WriteInt32(position + 0x300, 0); //Padding?
|
||||
context.Memory.WriteInt32(position + 0x304, (byte)entry.EntryType);
|
||||
context.Memory.WriteInt32(position + 0x300, (int)entry.Attributes);
|
||||
context.Memory.WriteInt32(position + 0x304, (byte)entry.Type);
|
||||
context.Memory.WriteInt64(position + 0x308, entry.Size);
|
||||
}
|
||||
|
||||
// GetEntryCount() -> u64
|
||||
public long GetEntryCount(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write((long)_directoryEntries.Count);
|
||||
context.ResponseData.Write((long)_provider.GetEntryCount());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||
{
|
||||
|
@ -11,13 +10,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||
|
||||
private Stream _baseStream;
|
||||
private LibHac.Fs.IFile _baseFile;
|
||||
|
||||
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>
|
||||
{
|
||||
|
@ -28,24 +27,24 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{ 4, GetSize }
|
||||
};
|
||||
|
||||
_baseStream = baseStream;
|
||||
HostPath = hostPath;
|
||||
_baseFile = baseFile;
|
||||
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)
|
||||
{
|
||||
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 size = context.RequestData.ReadInt64();
|
||||
|
||||
byte[] data = new byte[size];
|
||||
|
||||
_baseStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
int readSize = _baseStream.Read(data, 0, (int)size);
|
||||
int readSize = _baseFile.Read(data, offset);
|
||||
|
||||
context.Memory.WriteBytes(position, data);
|
||||
|
||||
|
@ -54,19 +53,20 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
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)
|
||||
{
|
||||
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 size = context.RequestData.ReadInt64();
|
||||
|
||||
byte[] data = context.Memory.ReadBytes(position, size);
|
||||
|
||||
_baseStream.Seek(offset, SeekOrigin.Begin);
|
||||
_baseStream.Write(data, 0, (int)size);
|
||||
_baseFile.Write(data, offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
// Flush()
|
||||
public long Flush(ServiceCtx context)
|
||||
{
|
||||
_baseStream.Flush();
|
||||
_baseFile.Flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
long size = context.RequestData.ReadInt64();
|
||||
|
||||
_baseStream.SetLength(size);
|
||||
_baseFile.SetSize(size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
// GetSize() -> u64 fileSize
|
||||
public long GetSize(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(_baseStream.Length);
|
||||
context.ResponseData.Write(_baseFile.GetSize());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -104,9 +104,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _baseStream != null)
|
||||
if (disposing && _baseFile != null)
|
||||
{
|
||||
_baseStream.Dispose();
|
||||
_baseFile.Dispose();
|
||||
|
||||
Disposed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using Ryujinx.HLE.FileSystem;
|
||||
using LibHac.Fs;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using Ryujinx.Common.Logging;
|
||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
||||
using static Ryujinx.HLE.Utilities.StringUtils;
|
||||
|
||||
|
@ -17,11 +17,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
private HashSet<string> _openPaths;
|
||||
|
||||
private string _path;
|
||||
private LibHac.Fs.IFileSystem _provider;
|
||||
|
||||
private IFileSystemProvider _provider;
|
||||
|
||||
public IFileSystem(string path, IFileSystemProvider provider)
|
||||
public IFileSystem(LibHac.Fs.IFileSystem provider)
|
||||
{
|
||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||
{
|
||||
|
@ -44,36 +42,50 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
_openPaths = new HashSet<string>();
|
||||
|
||||
_path = path;
|
||||
_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)
|
||||
{
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
long mode = context.RequestData.ReadInt64();
|
||||
int size = context.RequestData.ReadInt32();
|
||||
int createOption = 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);
|
||||
}
|
||||
|
||||
if (_provider.FileExists(fileName))
|
||||
if (_provider.FileExists(name))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||
}
|
||||
|
||||
if (IsPathAlreadyInUse(fileName))
|
||||
if (IsPathAlreadyInUse(name))
|
||||
{
|
||||
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)
|
||||
|
@ -81,19 +93,32 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
string fileName = _provider.GetFullPath(name);
|
||||
|
||||
if (!_provider.FileExists(fileName))
|
||||
if (!_provider.FileExists(name))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||
}
|
||||
|
||||
if (IsPathAlreadyInUse(fileName))
|
||||
if (IsPathAlreadyInUse(name))
|
||||
{
|
||||
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)
|
||||
|
@ -101,24 +126,35 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
string dirName = _provider.GetFullPath(name);
|
||||
|
||||
if (dirName == null)
|
||||
if (name == null)
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||
}
|
||||
|
||||
if (_provider.DirectoryExists(dirName))
|
||||
if (_provider.DirectoryExists(name))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||
}
|
||||
|
||||
if (IsPathAlreadyInUse(dirName))
|
||||
if (IsPathAlreadyInUse(name))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -126,32 +162,61 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
// DeleteDirectory(buffer<bytes<0x301>, 0x19, 0x301> path)
|
||||
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)
|
||||
public long DeleteDirectoryRecursively(ServiceCtx context)
|
||||
{
|
||||
return DeleteDirectory(context, true);
|
||||
}
|
||||
|
||||
private long DeleteDirectory(ServiceCtx context, bool recursive)
|
||||
{
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
string dirName = _provider.GetFullPath(name);
|
||||
|
||||
if (!Directory.Exists(dirName))
|
||||
if (!_provider.DirectoryExists(name))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||
}
|
||||
|
||||
if (IsPathAlreadyInUse(dirName))
|
||||
if (IsPathAlreadyInUse(name))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -162,25 +227,37 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
string oldName = ReadUtf8String(context, 0);
|
||||
string newName = ReadUtf8String(context, 1);
|
||||
|
||||
string oldFileName = _provider.GetFullPath(oldName);
|
||||
string newFileName = _provider.GetFullPath(newName);
|
||||
|
||||
if (_provider.FileExists(oldFileName))
|
||||
if (_provider.FileExists(oldName))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||
}
|
||||
|
||||
if (_provider.FileExists(newFileName))
|
||||
if (_provider.FileExists(newName))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||
}
|
||||
|
||||
if (IsPathAlreadyInUse(oldFileName))
|
||||
if (IsPathAlreadyInUse(oldName))
|
||||
{
|
||||
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)
|
||||
|
@ -189,25 +266,37 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
string oldName = ReadUtf8String(context, 0);
|
||||
string newName = ReadUtf8String(context, 1);
|
||||
|
||||
string oldDirName = _provider.GetFullPath(oldName);
|
||||
string newDirName = _provider.GetFullPath(newName);
|
||||
|
||||
if (!_provider.DirectoryExists(oldDirName))
|
||||
if (!_provider.DirectoryExists(oldName))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||
}
|
||||
|
||||
if (!_provider.DirectoryExists(newDirName))
|
||||
if (!_provider.DirectoryExists(newName))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||
}
|
||||
|
||||
if (IsPathAlreadyInUse(oldDirName))
|
||||
if (IsPathAlreadyInUse(oldName))
|
||||
{
|
||||
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
|
||||
|
@ -215,17 +304,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
string fileName = _provider.GetFullPath(name);
|
||||
try
|
||||
{
|
||||
LibHac.Fs.DirectoryEntryType entryType = _provider.GetEntryType(name);
|
||||
|
||||
if (_provider.FileExists(fileName))
|
||||
{
|
||||
context.ResponseData.Write(1);
|
||||
context.ResponseData.Write((int)entryType);
|
||||
}
|
||||
else if (_provider.DirectoryExists(fileName))
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
}
|
||||
else
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
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
|
||||
public long OpenFile(ServiceCtx context)
|
||||
{
|
||||
int filterFlags = context.RequestData.ReadInt32();
|
||||
int mode = context.RequestData.ReadInt32();
|
||||
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
string fileName = _provider.GetFullPath(name);
|
||||
|
||||
if (!_provider.FileExists(fileName))
|
||||
if (!_provider.FileExists(name))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||
}
|
||||
|
||||
if (IsPathAlreadyInUse(fileName))
|
||||
if (IsPathAlreadyInUse(name))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||
}
|
||||
|
||||
IFile fileInterface;
|
||||
|
||||
long error = _provider.OpenFile(fileName, out IFile fileInterface);
|
||||
|
||||
if (error == 0)
|
||||
try
|
||||
{
|
||||
fileInterface.Disposed += RemoveFileInUse;
|
||||
LibHac.Fs.IFile file = _provider.OpenFile(name, (OpenMode)mode);
|
||||
|
||||
lock (_openPaths)
|
||||
{
|
||||
_openPaths.Add(fileName);
|
||||
}
|
||||
fileInterface = new IFile(file, name);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||
|
||||
MakeObject(context, fileInterface);
|
||||
|
||||
return 0;
|
||||
throw;
|
||||
}
|
||||
|
||||
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
|
||||
public long OpenDirectory(ServiceCtx context)
|
||||
{
|
||||
int filterFlags = context.RequestData.ReadInt32();
|
||||
int mode = context.RequestData.ReadInt32();
|
||||
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
string dirName = _provider.GetFullPath(name);
|
||||
|
||||
if (!_provider.DirectoryExists(dirName))
|
||||
if (!_provider.DirectoryExists(name))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||
}
|
||||
|
||||
if (IsPathAlreadyInUse(dirName))
|
||||
if (IsPathAlreadyInUse(name))
|
||||
{
|
||||
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)
|
||||
{
|
||||
_openPaths.Add(dirName);
|
||||
}
|
||||
dirInterface = new IDirectory(dir);
|
||||
}
|
||||
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()
|
||||
public long Commit(ServiceCtx context)
|
||||
{
|
||||
_provider.Commit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -321,7 +421,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
context.ResponseData.Write(_provider.GetFreeSpace(context));
|
||||
context.ResponseData.Write(_provider.GetFreeSpaceSize(name));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -331,7 +431,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
context.ResponseData.Write(_provider.GetFreeSpace(context));
|
||||
context.ResponseData.Write(_provider.GetTotalSpaceSize(name));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -341,28 +441,25 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
string dirName = _provider.GetFullPath(name);
|
||||
|
||||
if (!_provider.DirectoryExists(dirName))
|
||||
if (!_provider.DirectoryExists(name))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||
}
|
||||
|
||||
if (IsPathAlreadyInUse(dirName))
|
||||
if (IsPathAlreadyInUse(name))
|
||||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
||||
}
|
||||
|
||||
foreach (DirectoryEntry entry in _provider.GetEntries(dirName))
|
||||
try
|
||||
{
|
||||
if (_provider.DirectoryExists(entry.Path))
|
||||
{
|
||||
_provider.DeleteDirectory(entry.Path, true);
|
||||
}
|
||||
else if (_provider.FileExists(entry.Path))
|
||||
{
|
||||
_provider.DeleteFile(entry.Path);
|
||||
}
|
||||
_provider.CleanDirectoryRecursively(name);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -373,15 +470,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
string name = ReadUtf8String(context);
|
||||
|
||||
string path = _provider.GetFullPath(name);
|
||||
|
||||
if (_provider.FileExists(path) || _provider.DirectoryExists(path))
|
||||
if (_provider.FileExists(name) || _provider.DirectoryExists(name))
|
||||
{
|
||||
FileTimestamp timestamp = _provider.GetFileTimeStampRaw(path);
|
||||
FileTimeStampRaw timestamp = _provider.GetFileTimeStampRaw(name);
|
||||
|
||||
context.ResponseData.Write(new DateTimeOffset(timestamp.CreationDateTime).ToUnixTimeSeconds());
|
||||
context.ResponseData.Write(new DateTimeOffset(timestamp.ModifiedDateTime).ToUnixTimeSeconds());
|
||||
context.ResponseData.Write(new DateTimeOffset(timestamp.LastAccessDateTime).ToUnixTimeSeconds());
|
||||
context.ResponseData.Write(timestamp.Created);
|
||||
context.ResponseData.Write(timestamp.Modified);
|
||||
context.ResponseData.Write(timestamp.Accessed);
|
||||
|
||||
byte[] data = new byte[8];
|
||||
|
||||
|
@ -412,7 +507,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
fileInterface.Disposed -= RemoveFileInUse;
|
||||
|
||||
_openPaths.Remove(fileInterface.HostPath);
|
||||
_openPaths.Remove(fileInterface.Path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,7 +519,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
dirInterface.Disposed -= RemoveDirectoryInUse;
|
||||
|
||||
_openPaths.Remove(dirInterface.DirectoryPath);
|
||||
_openPaths.Remove(dirInterface.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
using LibHac;
|
||||
using LibHac.IO;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.NcaUtils;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
|
||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
||||
|
@ -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
|
||||
public long OpenBisFileSystem(ServiceCtx context)
|
||||
{
|
||||
int bisPartitionId = context.RequestData.ReadInt32();
|
||||
string partitionString = ReadUtf8String(context);
|
||||
string bisPartitonPath = string.Empty;
|
||||
int bisPartitionId = context.RequestData.ReadInt32();
|
||||
string partitionString = ReadUtf8String(context);
|
||||
string bisPartitionPath = string.Empty;
|
||||
|
||||
switch (bisPartitionId)
|
||||
{
|
||||
case 29:
|
||||
bisPartitonPath = SafeNandPath;
|
||||
bisPartitionPath = SafeNandPath;
|
||||
break;
|
||||
case 30:
|
||||
case 31:
|
||||
bisPartitonPath = SystemNandPath;
|
||||
bisPartitionPath = SystemNandPath;
|
||||
break;
|
||||
case 32:
|
||||
bisPartitonPath = UserNandPath;
|
||||
bisPartitionPath = UserNandPath;
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
@ -113,9 +113,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
// OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
|
||||
public long OpenDataStorageByCurrentProcess(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IStorage(context.Device.FileSystem.RomFs));
|
||||
MakeObject(context, new IStorage(context.Device.FileSystem.RomFs.AsStorage()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
if (installedStorage == StorageId.None)
|
||||
{
|
||||
contentType = ContentType.AocData;
|
||||
contentType = ContentType.PublicData;
|
||||
|
||||
installedStorage =
|
||||
context.Device.System.ContentManager.GetInstalledStorage(titleId, contentType, storageId);
|
||||
|
@ -175,12 +175,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
if (File.Exists(ncaPath))
|
||||
{
|
||||
LibHac.IO.IStorage ncaStorage = new FileStream(ncaPath, FileMode.Open, FileAccess.Read).AsStorage();
|
||||
Nca nca = new Nca(context.Device.System.KeySet, ncaStorage, false);
|
||||
NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
|
||||
Stream romfsStream = nca.OpenSection(romfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel, false).AsStream();
|
||||
LibHac.Fs.IStorage ncaStorage = new LocalStorage(ncaPath, FileAccess.Read, FileMode.Open);
|
||||
Nca nca = new Nca(context.Device.System.KeySet, ncaStorage);
|
||||
LibHac.Fs.IStorage romfsStorage = nca.OpenStorage(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel);
|
||||
|
||||
MakeObject(context, new IStorage(romfsStream));
|
||||
MakeObject(context, new IStorage(romfsStorage));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -201,7 +200,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
// OpenPatchDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage>
|
||||
public long OpenPatchDataStorageByCurrentProcess(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IStorage(context.Device.FileSystem.RomFs));
|
||||
MakeObject(context, new IStorage(context.Device.FileSystem.RomFs.AsStorage()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -224,57 +223,44 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
context.RequestData.ReadInt64(),
|
||||
context.RequestData.ReadInt64());
|
||||
|
||||
long saveId = context.RequestData.ReadInt64();
|
||||
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadByte();
|
||||
SaveInfo saveInfo = new SaveInfo(titleId, saveId, saveDataType, userId, saveSpaceId);
|
||||
string savePath = context.Device.FileSystem.GetGameSavePath(saveInfo, context);
|
||||
FileSystemProvider fileSystemProvider = new FileSystemProvider(savePath, context.Device.FileSystem.GetBasePath());
|
||||
long saveId = context.RequestData.ReadInt64();
|
||||
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadByte();
|
||||
SaveInfo saveInfo = new SaveInfo(titleId, saveId, saveDataType, userId, saveSpaceId);
|
||||
string savePath = context.Device.FileSystem.GetGameSavePath(saveInfo, context);
|
||||
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)
|
||||
{
|
||||
FileStream pfsFile = new FileStream(pfsPath, FileMode.Open, FileAccess.Read);
|
||||
Pfs nsp = new Pfs(pfsFile.AsStorage());
|
||||
LocalStorage storage = new LocalStorage(pfsPath, FileAccess.Read, FileMode.Open);
|
||||
PartitionFileSystem nsp = new PartitionFileSystem(storage);
|
||||
|
||||
ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet);
|
||||
|
||||
|
||||
IFileSystem nspFileSystem = new IFileSystem(pfsPath, new PFsProvider(nsp));
|
||||
|
||||
IFileSystem nspFileSystem = new IFileSystem(nsp);
|
||||
|
||||
MakeObject(context, nspFileSystem);
|
||||
|
||||
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);
|
||||
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
|
||||
if (!nca.SectionExists(NcaSectionType.Data))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -294,7 +280,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
FileMode.Open,
|
||||
FileAccess.Read);
|
||||
|
||||
Pfs nsp = new Pfs(pfsFile.AsStorage());
|
||||
PartitionFileSystem nsp = new PartitionFileSystem(pfsFile.AsStorage());
|
||||
|
||||
ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet);
|
||||
|
||||
|
@ -302,18 +288,18 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||
{
|
||||
|
@ -10,9 +9,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
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>
|
||||
{
|
||||
|
@ -20,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{ 4, GetSize }
|
||||
};
|
||||
|
||||
_baseStream = baseStream;
|
||||
_baseStorage = baseStorage;
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
lock (_baseStream)
|
||||
{
|
||||
_baseStream.Seek(offset, SeekOrigin.Begin);
|
||||
_baseStream.Read(data, 0, data.Length);
|
||||
}
|
||||
_baseStorage.Read(data, offset);
|
||||
|
||||
context.Memory.WriteBytes(buffDesc.Position, data);
|
||||
}
|
||||
|
@ -56,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
// GetSize() -> u64 size
|
||||
public long GetSize(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(_baseStream.Length);
|
||||
context.ResponseData.Write(_baseStorage.GetSize());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue