diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs index 618ee241a6..43193245af 100644 --- a/Ryujinx.HLE/Loaders/Executable.cs +++ b/Ryujinx.HLE/Loaders/Executable.cs @@ -2,6 +2,7 @@ using ChocolArm64.Memory; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.OsHle; using System.Collections.Generic; +using System.IO; namespace Ryujinx.HLE.Loaders { @@ -15,6 +16,8 @@ namespace Ryujinx.HLE.Loaders public string Name { get; private set; } + public string FilePath { get; private set; } + private AMemory Memory; public long ImageBase { get; private set; } @@ -26,8 +29,9 @@ namespace Ryujinx.HLE.Loaders m_SymbolTable = new Dictionary(); - Name = Exe.Name; + FilePath = Exe.FilePath; + Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, "")); this.Memory = Memory; this.ImageBase = ImageBase; this.ImageEnd = ImageBase; diff --git a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs index 1e8e569a5b..44bad61497 100644 --- a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs +++ b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.Loaders.Executables { public interface IExecutable { - string Name { get; } + string FilePath { get; } byte[] Text { get; } byte[] RO { get; } diff --git a/Ryujinx.HLE/Loaders/Executables/Nro.cs b/Ryujinx.HLE/Loaders/Executables/Nro.cs index 9e2b7e9073..0b5068d7b9 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nro.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nro.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.Loaders.Executables { class Nro : IExecutable { - public string Name { get; private set; } + public string FilePath { get; private set; } public byte[] Text { get; private set; } public byte[] RO { get; private set; } @@ -16,9 +16,9 @@ namespace Ryujinx.HLE.Loaders.Executables public int DataOffset { get; private set; } public int BssSize { get; private set; } - public Nro(Stream Input, string Name) + public Nro(Stream Input, string FilePath) { - this.Name = Name; + this.FilePath = FilePath; BinaryReader Reader = new BinaryReader(Input); diff --git a/Ryujinx.HLE/Loaders/Executables/Nso.cs b/Ryujinx.HLE/Loaders/Executables/Nso.cs index a5e1a361ee..6a55c755a8 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nso.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nso.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.Loaders.Executables { class Nso : IExecutable { - public string Name { get; private set; } + public string FilePath { get; private set; } public byte[] Text { get; private set; } public byte[] RO { get; private set; } @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Loaders.Executables public Nso(Stream Input, string Name) { - this.Name = Name; + this.FilePath = FilePath; BinaryReader Reader = new BinaryReader(Input); diff --git a/Ryujinx.HLE/OsHle/Homebrew.cs b/Ryujinx.HLE/OsHle/Homebrew.cs index 4266c8db46..778e52fe54 100644 --- a/Ryujinx.HLE/OsHle/Homebrew.cs +++ b/Ryujinx.HLE/OsHle/Homebrew.cs @@ -1,11 +1,14 @@ using ChocolArm64.Memory; +using System.Text; namespace Ryujinx.HLE.OsHle { static class Homebrew { + public const string TemporaryNroSuffix = ".ryu_tmp.nro"; + //http://switchbrew.org/index.php?title=Homebrew_ABI - public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle) + public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle, string SwitchPath) { Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW); @@ -15,6 +18,11 @@ namespace Ryujinx.HLE.OsHle //NextLoadPath WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400); + // Argv + long ArgvPosition = Position + 0xC00; + WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition); + Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0")); + //AppletType WriteConfigEntry(Memory, ref Position, 7); diff --git a/Ryujinx.HLE/OsHle/Horizon.cs b/Ryujinx.HLE/OsHle/Horizon.cs index a5bf0616c6..9d8a937ff2 100644 --- a/Ryujinx.HLE/OsHle/Horizon.cs +++ b/Ryujinx.HLE/OsHle/Horizon.cs @@ -87,19 +87,36 @@ namespace Ryujinx.HLE.OsHle MainProcess.Run(); } - public void LoadProgram(string FileName) + public void LoadProgram(string FilePath) { - bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro"; + bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro"; - string Name = Path.GetFileNameWithoutExtension(FileName); + string Name = Path.GetFileNameWithoutExtension(FilePath); + string SwitchFilePath = Ns.VFs.SystemPathToSwitchPath(FilePath); + + if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/"))) + { + // TODO: avoid copying the file if we are already inside a sdmc directory + string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}"; + string TempPath = Ns.VFs.SwitchPathToSystemPath(SwitchPath); + + string SwitchDir = Path.GetDirectoryName(TempPath); + if (!Directory.Exists(SwitchDir)) + { + Directory.CreateDirectory(SwitchDir); + } + File.Copy(FilePath, TempPath, true); + + FilePath = TempPath; + } Process MainProcess = MakeProcess(); - using (FileStream Input = new FileStream(FileName, FileMode.Open)) + using (FileStream Input = new FileStream(FilePath, FileMode.Open)) { MainProcess.LoadProgram(IsNro - ? (IExecutable)new Nro(Input, Name) - : (IExecutable)new Nso(Input, Name)); + ? (IExecutable)new Nro(Input, FilePath) + : (IExecutable)new Nso(Input, FilePath)); } MainProcess.SetEmptyArgs(); diff --git a/Ryujinx.HLE/OsHle/Process.cs b/Ryujinx.HLE/OsHle/Process.cs index 53e357ab9a..be27dcc288 100644 --- a/Ryujinx.HLE/OsHle/Process.cs +++ b/Ryujinx.HLE/OsHle/Process.cs @@ -13,6 +13,7 @@ using Ryujinx.HLE.OsHle.Services.Nv; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; using System.Text; namespace Ryujinx.HLE.OsHle @@ -155,7 +156,9 @@ namespace Ryujinx.HLE.OsHle { HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); - Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle); + string SwitchPath = Ns.VFs.SystemPathToSwitchPath(Executables[0].FilePath); + + Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle, SwitchPath); MainThread.Thread.ThreadState.X0 = (ulong)HbAbiDataPosition; MainThread.Thread.ThreadState.X1 = ulong.MaxValue; @@ -400,6 +403,11 @@ namespace Ryujinx.HLE.OsHle { if (Disposing && !Disposed) { + if (NeedsHbAbi && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix)) + { + File.Delete(Executables[0].FilePath); + } + //If there is still some thread running, disposing the objects is not //safe as the thread may try to access those resources. Instead, we set //the flag to have the Process disposed when all threads finishes. diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 1946b187ba..74c0564a9f 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -81,8 +81,9 @@ namespace Ryujinx.HLE Gpu.Fifo.DispatchCalls(); } - internal virtual void OnFinish(EventArgs e) + public virtual void OnFinish(EventArgs e) { + Os.Dispose(); Finish?.Invoke(this, e); } diff --git a/Ryujinx.HLE/VirtualFileSystem.cs b/Ryujinx.HLE/VirtualFileSystem.cs index 8b71caa97a..38df81f87d 100644 --- a/Ryujinx.HLE/VirtualFileSystem.cs +++ b/Ryujinx.HLE/VirtualFileSystem.cs @@ -45,6 +45,35 @@ namespace Ryujinx.HLE public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath); + public string SwitchPathToSystemPath(string SwitchPath) + { + string[] Parts = SwitchPath.Split(":"); + if (Parts.Length != 2) + { + return null; + } + return GetFullPath(MakeDirAndGetFullPath(Parts[0]), Parts[1]); + } + + public string SystemPathToSwitchPath(string SystemPath) + { + string BaseSystemPath = GetBasePath() + "/"; + if (SystemPath.StartsWith(BaseSystemPath)) + { + string RawPath = SystemPath.Replace(BaseSystemPath, ""); + int FirstSeparatorOffset = RawPath.IndexOf('/'); + if (FirstSeparatorOffset == -1) + { + return $"{RawPath}:/"; + } + + string BasePath = RawPath.Substring(0, FirstSeparatorOffset); + string FileName = RawPath.Substring(FirstSeparatorOffset + 1); + return $"{BasePath}:/{FileName}"; + } + return null; + } + private string MakeDirAndGetFullPath(string Dir) { string FullPath = Path.Combine(GetBasePath(), Dir); diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index 5cacc6228b..879b9c20bf 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -68,6 +68,7 @@ namespace Ryujinx }; Screen.MainLoop(); + Ns.OnFinish(EventArgs.Empty); } Environment.Exit(0);