diff --git a/Ryujinx.Core/Loaders/Executable.cs b/Ryujinx.Core/Loaders/Executable.cs index 6a6073ef28..e26608389b 100644 --- a/Ryujinx.Core/Loaders/Executable.cs +++ b/Ryujinx.Core/Loaders/Executable.cs @@ -26,7 +26,12 @@ namespace Ryujinx.Core.Loaders if (Exe.Mod0Offset == 0) { - MapBss(ImageBase + Exe.DataOffset + Exe.Data.Count, Exe.BssSize); + int BssOffset = Exe.DataOffset + Exe.Data.Count; + int BssSize = Exe.BssSize; + + MapBss(ImageBase + BssOffset, BssSize); + + ImageEnd = ImageBase + BssOffset + BssSize; return; } diff --git a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs index deab88969a..612d15eb0f 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs @@ -34,9 +34,12 @@ namespace Ryujinx.Core.OsHle.Ipc { ( "hid", 0), Service.HidCreateAppletResource }, { ( "hid", 11), Service.HidActivateTouchScreen }, { ( "hid", 100), Service.HidSetSupportedNpadStyleSet }, + { ( "hid", 101), Service.HidGetSupportedNpadStyleSet }, { ( "hid", 102), Service.HidSetSupportedNpadIdType }, { ( "hid", 103), Service.HidActivateNpad }, { ( "hid", 120), Service.HidSetNpadJoyHoldType }, + { ( "hid", 121), Service.HidGetNpadJoyHoldType }, + { ( "hid", 203), Service.HidCreateActiveVibrationDeviceList }, { ( "lm", 0), Service.LmInitialize }, { ( "nvdrv", 0), Service.NvDrvOpen }, { ( "nvdrv", 1), Service.NvDrvIoctl }, @@ -79,6 +82,7 @@ namespace Ryujinx.Core.OsHle.Ipc AMemory Memory, HSession Session, IpcMessage Request, + int ThreadId, long CmdPtr, int HndId) { @@ -111,13 +115,13 @@ namespace Ryujinx.Core.OsHle.Ipc { ServiceCmds.TryGetValue((ServiceName, CmdId), out ProcReq); - DbgServiceName = $"{ServiceName} {ProcReq?.Method.Name ?? CmdId.ToString()}"; + DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}"; } else if (Obj != null) { ((IIpcInterface)Obj).Commands.TryGetValue(CmdId, out ProcReq); - DbgServiceName = $"{ServiceName} {Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}"; + DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}"; } } else if (Request.DomCmd == IpcDomCmd.DeleteObj) @@ -140,16 +144,18 @@ namespace Ryujinx.Core.OsHle.Ipc ((IIpcInterface)Obj).Commands.TryGetValue(CmdId, out ProcReq); - DbgServiceName = $"{ServiceName} {Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}"; + DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}"; } else { ServiceCmds.TryGetValue((ServiceName, CmdId), out ProcReq); - DbgServiceName = $"{ServiceName} {ProcReq?.Method.Name ?? CmdId.ToString()}"; + DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}"; } } + DbgServiceName = $"Tid {ThreadId} {ServiceName} {DbgServiceName}"; + Logging.Debug($"IpcMessage: {DbgServiceName}"); if (ProcReq != null) diff --git a/Ryujinx.Core/OsHle/Objects/ErrorCode.cs b/Ryujinx.Core/OsHle/Objects/ErrorCode.cs new file mode 100644 index 0000000000..20f97f8484 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/ErrorCode.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Core.OsHle.Objects +{ + static class ErrorCode + { + public static long MakeError(ErrorModule Module, int Code) + { + return (int)Module | (Code << 9); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/ErrorModule.cs b/Ryujinx.Core/OsHle/Objects/ErrorModule.cs new file mode 100644 index 0000000000..0221031b41 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/ErrorModule.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Core.OsHle.Objects +{ + enum ErrorModule + { + Fs = 2, + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/FsErr.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/FsErr.cs new file mode 100644 index 0000000000..d9aa60ae73 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/FspSrv/FsErr.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Core.OsHle.Objects.FspSrv +{ + static class FsErr + { + public const int PathDoesNotExist = 1; + public const int PathAlreadyExists = 2; + public const int PathAlreadyInUse = 7; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs index 88fce28eb6..785b1ba387 100644 --- a/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs +++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs @@ -3,131 +3,115 @@ using Ryujinx.Core.OsHle.Ipc; using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Runtime.InteropServices; using System.Text; namespace Ryujinx.Core.OsHle.Objects.FspSrv { - [StructLayout(LayoutKind.Sequential, Size = 0x310)] - struct DirectoryEntry + class IDirectory : IIpcInterface, IDisposable { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x300)] - public byte[] Name; - public int Unknown; - public byte Type; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3)] - public byte[] Padding; - public long Size; - } + private const int DirectoryEntrySize = 0x310; - enum DirectoryEntryType - { - Directory, - File - } - - class IDirectory : IIpcInterface - { - private List DirectoryEntries = new List(); private Dictionary m_Commands; public IReadOnlyDictionary Commands => m_Commands; - private string HostPath; + private List DirectoryEntries; - public IDirectory(string HostPath, int flags) + private int CurrentItemIndex; + + public event EventHandler Disposed; + + public string HostPath { get; private set; } + + public IDirectory(string HostPath, int Flags) { m_Commands = new Dictionary() { - { 0, Read }, - { 1, GetEntryCount } + { 0, Read }, + { 1, GetEntryCount } }; this.HostPath = HostPath; - if ((flags & 1) == 1) + DirectoryEntries = new List(); + + if ((Flags & 1) != 0) { - string[] Directories = Directory.GetDirectories(HostPath, "*", SearchOption.TopDirectoryOnly). - Where(x => (new FileInfo(x).Attributes & FileAttributes.Hidden) == 0).ToArray(); - - foreach (string Directory in Directories) - { - DirectoryEntry Info = new DirectoryEntry - { - Name = Encoding.UTF8.GetBytes(Directory), - Type = (byte)DirectoryEntryType.Directory, - Size = 0 - }; - - Array.Resize(ref Info.Name, 0x300); - DirectoryEntries.Add(Info); - } + DirectoryEntries.AddRange(Directory.GetDirectories(HostPath)); } - if ((flags & 2) == 2) + if ((Flags & 2) != 0) { - string[] Files = Directory.GetFiles(HostPath, "*", SearchOption.TopDirectoryOnly). - Where(x => (new FileInfo(x).Attributes & FileAttributes.Hidden) == 0).ToArray(); - - foreach (string FileName in Files) - { - DirectoryEntry Info = new DirectoryEntry - { - Name = Encoding.UTF8.GetBytes(Path.GetFileName(FileName)), - Type = (byte)DirectoryEntryType.File, - Size = new FileInfo(Path.Combine(HostPath, FileName)).Length - }; - - Array.Resize(ref Info.Name, 0x300); - DirectoryEntries.Add(Info); - } + DirectoryEntries.AddRange(Directory.GetFiles(HostPath)); } + + CurrentItemIndex = 0; } - private int LastItem = 0; public long Read(ServiceCtx Context) { long BufferPosition = Context.Request.ReceiveBuff[0].Position; - long BufferLen = Context.Request.ReceiveBuff[0].Size; - long MaxDirectories = BufferLen / Marshal.SizeOf(typeof(DirectoryEntry)); + long BufferLen = Context.Request.ReceiveBuff[0].Size; - if (MaxDirectories > DirectoryEntries.Count - LastItem) + int MaxReadCount = (int)(BufferLen / DirectoryEntrySize); + + int Count = Math.Min(DirectoryEntries.Count - CurrentItemIndex, MaxReadCount); + + for (int Index = 0; Index < Count; Index++) { - MaxDirectories = DirectoryEntries.Count - LastItem; + long Position = BufferPosition + Index * DirectoryEntrySize; + + WriteDirectoryEntry(Context, Position, DirectoryEntries[CurrentItemIndex++]); } - int CurrentIndex; - for (CurrentIndex = 0; CurrentIndex < MaxDirectories; CurrentIndex++) - { - int CurrentItem = LastItem + CurrentIndex; - - byte[] DirectoryEntry = new byte[Marshal.SizeOf(typeof(DirectoryEntry))]; - IntPtr Ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DirectoryEntry))); - Marshal.StructureToPtr(DirectoryEntries[CurrentItem], Ptr, true); - Marshal.Copy(Ptr, DirectoryEntry, 0, Marshal.SizeOf(typeof(DirectoryEntry))); - Marshal.FreeHGlobal(Ptr); - - AMemoryHelper.WriteBytes(Context.Memory, BufferPosition + Marshal.SizeOf(typeof(DirectoryEntry)) * CurrentIndex, DirectoryEntry); - } - - if (LastItem < DirectoryEntries.Count) - { - LastItem += CurrentIndex; - Context.ResponseData.Write((long)CurrentIndex); // index = number of entries written this call. - } - else - { - Context.ResponseData.Write((long)0); - } + Context.ResponseData.Write((long)Count); return 0; } + private void WriteDirectoryEntry(ServiceCtx Context, long Position, string FullPath) + { + for (int Offset = 0; Offset < 0x300; Offset += 8) + { + Context.Memory.WriteInt64(Position + Offset, 0); + } + + byte[] NameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(FullPath)); + + AMemoryHelper.WriteBytes(Context.Memory, Position, NameBuffer); + + int Type = 0; + long Size = 0; + + if (File.Exists(FullPath)) + { + Type = 1; + Size = new FileInfo(FullPath).Length; + } + + Context.Memory.WriteInt32(Position + 0x300, 0); //Padding? + Context.Memory.WriteInt32(Position + 0x304, Type); + Context.Memory.WriteInt64(Position + 0x308, Size); + } + public long GetEntryCount(ServiceCtx Context) { Context.ResponseData.Write((long)DirectoryEntries.Count); + return 0; } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Disposed?.Invoke(this, EventArgs.Empty); + } + } } } diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs index 95fbc650f5..82706f609a 100644 --- a/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs +++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs @@ -14,18 +14,23 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv private Stream BaseStream; - public IFile(Stream BaseStream) + public event EventHandler Disposed; + + public string HostPath { get; private set; } + + public IFile(Stream BaseStream, string HostPath) { m_Commands = new Dictionary() { - { 0, Read }, - { 1, Write }, - // { 2, Flush }, + { 0, Read }, + { 1, Write }, + { 2, Flush }, { 3, SetSize }, { 4, GetSize } }; this.BaseStream = BaseStream; + this.HostPath = HostPath; } public long Read(ServiceCtx Context) @@ -39,6 +44,7 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv byte[] Data = new byte[Size]; BaseStream.Seek(Offset, SeekOrigin.Begin); + int ReadSize = BaseStream.Read(Data, 0, (int)Size); AMemoryHelper.WriteBytes(Context.Memory, Position, Data); @@ -64,16 +70,26 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv return 0; } - public long GetSize(ServiceCtx Context) + public long Flush(ServiceCtx Context) { - Context.ResponseData.Write(BaseStream.Length); + BaseStream.Flush(); + return 0; } public long SetSize(ServiceCtx Context) { long Size = Context.RequestData.ReadInt64(); + BaseStream.SetLength(Size); + + return 0; + } + + public long GetSize(ServiceCtx Context) + { + Context.ResponseData.Write(BaseStream.Length); + return 0; } @@ -87,6 +103,8 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv if (disposing && BaseStream != null) { BaseStream.Dispose(); + + Disposed?.Invoke(this, EventArgs.Empty); } } } diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs index 68b1584528..e18c1dafd1 100644 --- a/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs +++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs @@ -1,8 +1,10 @@ using ChocolArm64.Memory; using Ryujinx.Core.OsHle.Ipc; +using System; using System.Collections.Generic; using System.IO; +using static Ryujinx.Core.OsHle.Objects.ErrorCode; using static Ryujinx.Core.OsHle.Objects.ObjHelper; namespace Ryujinx.Core.OsHle.Objects.FspSrv @@ -13,153 +15,214 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv public IReadOnlyDictionary Commands => m_Commands; + private HashSet OpenPaths; + private string Path; public IFileSystem(string Path) { - //TODO: implement. m_Commands = new Dictionary() { - { 0, CreateFile }, - { 1, DeleteFile }, - { 2, CreateDirectory }, - { 3, DeleteDirectory }, + { 0, CreateFile }, + { 1, DeleteFile }, + { 2, CreateDirectory }, + { 3, DeleteDirectory }, { 4, DeleteDirectoryRecursively }, - { 5, RenameFile }, - { 6, RenameDirectory }, - { 7, GetEntryType }, - { 8, OpenFile }, - { 9, OpenDirectory }, - { 10, Commit }, - //{ 11, GetFreeSpaceSize }, - //{ 12, GetTotalSpaceSize }, - //{ 13, CleanDirectoryRecursively }, - //{ 14, GetFileTimeStampRaw } + { 5, RenameFile }, + { 6, RenameDirectory }, + { 7, GetEntryType }, + { 8, OpenFile }, + { 9, OpenDirectory }, + { 10, Commit }, + { 11, GetFreeSpaceSize }, + { 12, GetTotalSpaceSize }, + //{ 13, CleanDirectoryRecursively }, + //{ 14, GetFileTimeStampRaw } }; + OpenPaths = new HashSet(); + this.Path = Path; } public long CreateFile(ServiceCtx Context) { long Position = Context.Request.PtrBuff[0].Position; + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); - ulong Mode = Context.RequestData.ReadUInt64(); - uint Size = Context.RequestData.ReadUInt32(); + + long Mode = Context.RequestData.ReadInt64(); + int Size = Context.RequestData.ReadInt32(); + string FileName = Context.Ns.VFs.GetFullPath(Path, Name); - if (FileName != null) + if (FileName == null) { - FileStream NewFile = File.Create(FileName); - NewFile.SetLength(Size); - NewFile.Close(); - return 0; + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - //TODO: Correct error code. - return -1; + if (File.Exists(FileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); + } + + if (IsPathAlreadyInUse(FileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + using (FileStream NewFile = File.Create(FileName)) + { + NewFile.SetLength(Size); + } + + return 0; } public long DeleteFile(ServiceCtx Context) { long Position = Context.Request.PtrBuff[0].Position; + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string FileName = Context.Ns.VFs.GetFullPath(Path, Name); - if (FileName != null) + if (!File.Exists(FileName)) { - File.Delete(FileName); - return 0; + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - //TODO: Correct error code. - return -1; + if (IsPathAlreadyInUse(FileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + File.Delete(FileName); + + return 0; } public long CreateDirectory(ServiceCtx Context) { long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); - string FileName = Context.Ns.VFs.GetFullPath(Path, Name); - if (FileName != null) + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + + string DirName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (DirName == null) { - Directory.CreateDirectory(FileName); - return 0; + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - //TODO: Correct error code. - return -1; + if (Directory.Exists(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); + } + + if (IsPathAlreadyInUse(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + Directory.CreateDirectory(DirName); + + return 0; } public long DeleteDirectory(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); - string FileName = Context.Ns.VFs.GetFullPath(Path, Name); - - if (FileName != null) - { - Directory.Delete(FileName); - return 0; - } - - // TODO: Correct error code. - return -1; + return DeleteDirectory(Context, false); } public long DeleteDirectoryRecursively(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); - string FileName = Context.Ns.VFs.GetFullPath(Path, Name); + return DeleteDirectory(Context, true); + } - if (FileName != null) + private long DeleteDirectory(ServiceCtx Context, bool Recursive) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + + string DirName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (!Directory.Exists(DirName)) { - Directory.Delete(FileName, true); // recursive = true - return 0; + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - // TODO: Correct error code. - return -1; + if (IsPathAlreadyInUse(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + Directory.Delete(DirName, Recursive); + + return 0; } public long RenameFile(ServiceCtx Context) { long OldPosition = Context.Request.PtrBuff[0].Position; long NewPosition = Context.Request.PtrBuff[0].Position; + string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition); string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition); + string OldFileName = Context.Ns.VFs.GetFullPath(Path, OldName); string NewFileName = Context.Ns.VFs.GetFullPath(Path, NewName); - if (OldFileName != null && NewFileName != null) + if (!File.Exists(OldFileName)) { - File.Move(OldFileName, NewFileName); - return 0; + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - // TODO: Correct error code. - return -1; + if (File.Exists(NewFileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); + } + + if (IsPathAlreadyInUse(OldFileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + File.Move(OldFileName, NewFileName); + + return 0; } public long RenameDirectory(ServiceCtx Context) { long OldPosition = Context.Request.PtrBuff[0].Position; long NewPosition = Context.Request.PtrBuff[0].Position; + string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition); string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition); + string OldDirName = Context.Ns.VFs.GetFullPath(Path, OldName); string NewDirName = Context.Ns.VFs.GetFullPath(Path, NewName); - if (OldDirName != null && NewDirName != null) + if (!Directory.Exists(OldDirName)) { - Directory.Move(OldDirName, NewDirName); - return 0; + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - // TODO: Correct error code. - return -1; + if (Directory.Exists(NewDirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); + } + + if (IsPathAlreadyInUse(OldDirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + Directory.Move(OldDirName, NewDirName); + + return 0; } public long GetEntryType(ServiceCtx Context) @@ -170,15 +233,20 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv string FileName = Context.Ns.VFs.GetFullPath(Path, Name); - if (FileName == null) + if (File.Exists(FileName)) { - //TODO: Correct error code. - return -1; + Context.ResponseData.Write(1); } + else if (Directory.Exists(FileName)) + { + Context.ResponseData.Write(0); + } + else + { + Context.ResponseData.Write(0); - bool IsFile = File.Exists(FileName); - - Context.ResponseData.Write(IsFile ? 1 : 0); + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } return 0; } @@ -193,22 +261,21 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv string FileName = Context.Ns.VFs.GetFullPath(Path, Name); - if (FileName == null) + if (!File.Exists(FileName)) { - //TODO: Correct error code. - return -1; + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (File.Exists(FileName)) + if (IsPathAlreadyInUse(FileName)) { - FileStream Stream = new FileStream(FileName, FileMode.OpenOrCreate); - MakeObject(Context, new IFile(Stream)); - - return 0; + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - //TODO: Correct error code. - return -1; + FileStream Stream = new FileStream(FileName, FileMode.Open); + + MakeObject(Context, new IFile(Stream, FileName)); + + return 0; } public long OpenDirectory(ServiceCtx Context) @@ -221,27 +288,87 @@ namespace Ryujinx.Core.OsHle.Objects.FspSrv string DirName = Context.Ns.VFs.GetFullPath(Path, Name); - if(DirName != null) + if (!Directory.Exists(DirName)) { - if (Directory.Exists(DirName)) - { - MakeObject(Context, new IDirectory(DirName, FilterFlags)); - return 0; - } - else - { - // TODO: correct error code. - return -1; - } + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - // TODO: Correct error code. - return -1; + if (IsPathAlreadyInUse(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + IDirectory DirInterface = new IDirectory(DirName, FilterFlags); + + DirInterface.Disposed += RemoveDirectoryInUse; + + lock (OpenPaths) + { + OpenPaths.Add(DirName); + } + + MakeObject(Context, DirInterface); + + return 0; } public long Commit(ServiceCtx Context) { return 0; } + + public long GetFreeSpaceSize(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + + Context.ResponseData.Write(Context.Ns.VFs.GetDrive().AvailableFreeSpace); + + return 0; + } + + public long GetTotalSpaceSize(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + + Context.ResponseData.Write(Context.Ns.VFs.GetDrive().TotalSize); + + return 0; + } + + private bool IsPathAlreadyInUse(string Path) + { + lock (OpenPaths) + { + return OpenPaths.Contains(Path); + } + } + + private void RemoveFileInUse(object sender, EventArgs e) + { + IFile FileInterface = (IFile)sender; + + lock (OpenPaths) + { + FileInterface.Disposed -= RemoveDirectoryInUse; + + OpenPaths.Remove(FileInterface.HostPath); + } + } + + private void RemoveDirectoryInUse(object sender, EventArgs e) + { + IDirectory DirInterface = (IDirectory)sender; + + lock (OpenPaths) + { + DirInterface.Disposed -= RemoveDirectoryInUse; + + OpenPaths.Remove(DirInterface.HostPath); + } + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Hid/IActiveVibrationDeviceList.cs b/Ryujinx.Core/OsHle/Objects/Hid/IActiveVibrationDeviceList.cs new file mode 100644 index 0000000000..1f0c8592a7 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Hid/IActiveVibrationDeviceList.cs @@ -0,0 +1,18 @@ +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.Objects.Hid +{ + class IActiveApplicationDeviceList : IIpcInterface + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public IActiveApplicationDeviceList() + { + m_Commands = new Dictionary() { }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs b/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs index d6e8947bbc..7d56d04bab 100644 --- a/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs +++ b/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Core.OsHle.Objects.Hid public IReadOnlyDictionary Commands => m_Commands; - public HSharedMem Handle; + private HSharedMem Handle; public IAppletResource(HSharedMem Handle) { diff --git a/Ryujinx.Core/OsHle/Services/ServiceHid.cs b/Ryujinx.Core/OsHle/Services/ServiceHid.cs index 4b2e82ffb1..aed3e959ce 100644 --- a/Ryujinx.Core/OsHle/Services/ServiceHid.cs +++ b/Ryujinx.Core/OsHle/Services/ServiceHid.cs @@ -23,6 +23,13 @@ namespace Ryujinx.Core.OsHle.Services return 0; } + public static long HidGetSupportedNpadStyleSet(ServiceCtx Context) + { + Context.ResponseData.Write(0); + + return 0; + } + public static long HidSetSupportedNpadStyleSet(ServiceCtx Context) { long Unknown0 = Context.RequestData.ReadInt64(); @@ -52,5 +59,19 @@ namespace Ryujinx.Core.OsHle.Services return 0; } + + public static long HidGetNpadJoyHoldType(ServiceCtx Context) + { + Context.ResponseData.Write(0L); + + return 0; + } + + public static long HidCreateActiveVibrationDeviceList(ServiceCtx Context) + { + MakeObject(Context, new IActiveApplicationDeviceList()); + + return 0; + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs index 7f593c8f2a..40369b9954 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs @@ -106,7 +106,7 @@ namespace Ryujinx.Core.OsHle.Svc if (Session != null) { - IpcHandler.IpcCall(Ns, Memory, Session, Cmd, CmdPtr, Handle); + IpcHandler.IpcCall(Ns, Memory, Session, Cmd, ThreadState.ThreadId, CmdPtr, Handle); byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); diff --git a/Ryujinx.Core/VirtualFs.cs b/Ryujinx.Core/VirtualFs.cs index 23c7285c61..0c91111659 100644 --- a/Ryujinx.Core/VirtualFs.cs +++ b/Ryujinx.Core/VirtualFs.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Core public string GetGameSavesPath() => MakeDirAndGetFullPath(SavesPath); - private static string MakeDirAndGetFullPath(string Dir) + private string MakeDirAndGetFullPath(string Dir) { string FullPath = Path.Combine(GetBasePath(), Dir); @@ -49,7 +49,12 @@ namespace Ryujinx.Core return FullPath; } - public static string GetBasePath() + public DriveInfo GetDrive() + { + return new DriveInfo(Path.GetPathRoot(GetBasePath())); + } + + public string GetBasePath() { return Path.Combine(Directory.GetCurrentDirectory(), BasePath); } diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 9c05953bf3..a100080e09 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -154,7 +154,7 @@ vec3 get_scale_ratio() { (window_size.y * native_size.x) / (native_size.y * window_size.x), (window_size.x * native_size.y) / (native_size.x * window_size.y) ); - return vec3(min(ratio, vec2(1, 1)), 1); + return vec3(min(ratio, vec2(1, 1)) * vec2(1, -1), 1); } void main(void) {