hbabi: Implement argv (#272)
This commit implements the argv config key in Ryujinx (by creating a temporary copy of the homebrew executable in the sdmc VFS) to make it possible to load libnx's "romfs" files. This commit also call Os.Dispose in Ns.OnFinish to dispose all resources when exiting
This commit is contained in:
parent
5d698a7d8d
commit
c2c765b30f
10 changed files with 84 additions and 16 deletions
|
@ -2,6 +2,7 @@ using ChocolArm64.Memory;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.OsHle;
|
using Ryujinx.HLE.OsHle;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders
|
||||||
{
|
{
|
||||||
|
@ -15,6 +16,8 @@ namespace Ryujinx.HLE.Loaders
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
public string FilePath { get; private set; }
|
||||||
|
|
||||||
private AMemory Memory;
|
private AMemory Memory;
|
||||||
|
|
||||||
public long ImageBase { get; private set; }
|
public long ImageBase { get; private set; }
|
||||||
|
@ -26,8 +29,9 @@ namespace Ryujinx.HLE.Loaders
|
||||||
|
|
||||||
m_SymbolTable = new Dictionary<long, string>();
|
m_SymbolTable = new Dictionary<long, string>();
|
||||||
|
|
||||||
Name = Exe.Name;
|
FilePath = Exe.FilePath;
|
||||||
|
|
||||||
|
Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, ""));
|
||||||
this.Memory = Memory;
|
this.Memory = Memory;
|
||||||
this.ImageBase = ImageBase;
|
this.ImageBase = ImageBase;
|
||||||
this.ImageEnd = ImageBase;
|
this.ImageEnd = ImageBase;
|
||||||
|
|
|
@ -2,7 +2,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
public interface IExecutable
|
public interface IExecutable
|
||||||
{
|
{
|
||||||
string Name { get; }
|
string FilePath { get; }
|
||||||
|
|
||||||
byte[] Text { get; }
|
byte[] Text { get; }
|
||||||
byte[] RO { get; }
|
byte[] RO { get; }
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nro : IExecutable
|
class Nro : IExecutable
|
||||||
{
|
{
|
||||||
public string Name { get; private set; }
|
public string FilePath { get; private set; }
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { 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 DataOffset { get; private set; }
|
||||||
public int BssSize { 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);
|
BinaryReader Reader = new BinaryReader(Input);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nso : IExecutable
|
class Nso : IExecutable
|
||||||
{
|
{
|
||||||
public string Name { get; private set; }
|
public string FilePath { get; private set; }
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
|
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
|
|
||||||
public Nso(Stream Input, string Name)
|
public Nso(Stream Input, string Name)
|
||||||
{
|
{
|
||||||
this.Name = Name;
|
this.FilePath = FilePath;
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Input);
|
BinaryReader Reader = new BinaryReader(Input);
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.OsHle
|
namespace Ryujinx.HLE.OsHle
|
||||||
{
|
{
|
||||||
static class Homebrew
|
static class Homebrew
|
||||||
{
|
{
|
||||||
|
public const string TemporaryNroSuffix = ".ryu_tmp.nro";
|
||||||
|
|
||||||
//http://switchbrew.org/index.php?title=Homebrew_ABI
|
//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);
|
Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW);
|
||||||
|
|
||||||
|
@ -15,6 +18,11 @@ namespace Ryujinx.HLE.OsHle
|
||||||
//NextLoadPath
|
//NextLoadPath
|
||||||
WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400);
|
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
|
//AppletType
|
||||||
WriteConfigEntry(Memory, ref Position, 7);
|
WriteConfigEntry(Memory, ref Position, 7);
|
||||||
|
|
||||||
|
|
|
@ -87,19 +87,36 @@ namespace Ryujinx.HLE.OsHle
|
||||||
MainProcess.Run();
|
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();
|
Process MainProcess = MakeProcess();
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
|
||||||
{
|
{
|
||||||
MainProcess.LoadProgram(IsNro
|
MainProcess.LoadProgram(IsNro
|
||||||
? (IExecutable)new Nro(Input, Name)
|
? (IExecutable)new Nro(Input, FilePath)
|
||||||
: (IExecutable)new Nso(Input, Name));
|
: (IExecutable)new Nso(Input, FilePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
MainProcess.SetEmptyArgs();
|
MainProcess.SetEmptyArgs();
|
||||||
|
|
|
@ -13,6 +13,7 @@ using Ryujinx.HLE.OsHle.Services.Nv;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.OsHle
|
namespace Ryujinx.HLE.OsHle
|
||||||
|
@ -155,7 +156,9 @@ namespace Ryujinx.HLE.OsHle
|
||||||
{
|
{
|
||||||
HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd);
|
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.X0 = (ulong)HbAbiDataPosition;
|
||||||
MainThread.Thread.ThreadState.X1 = ulong.MaxValue;
|
MainThread.Thread.ThreadState.X1 = ulong.MaxValue;
|
||||||
|
@ -400,6 +403,11 @@ namespace Ryujinx.HLE.OsHle
|
||||||
{
|
{
|
||||||
if (Disposing && !Disposed)
|
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
|
//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
|
//safe as the thread may try to access those resources. Instead, we set
|
||||||
//the flag to have the Process disposed when all threads finishes.
|
//the flag to have the Process disposed when all threads finishes.
|
||||||
|
|
|
@ -81,8 +81,9 @@ namespace Ryujinx.HLE
|
||||||
Gpu.Fifo.DispatchCalls();
|
Gpu.Fifo.DispatchCalls();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal virtual void OnFinish(EventArgs e)
|
public virtual void OnFinish(EventArgs e)
|
||||||
{
|
{
|
||||||
|
Os.Dispose();
|
||||||
Finish?.Invoke(this, e);
|
Finish?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,35 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath);
|
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)
|
private string MakeDirAndGetFullPath(string Dir)
|
||||||
{
|
{
|
||||||
string FullPath = Path.Combine(GetBasePath(), Dir);
|
string FullPath = Path.Combine(GetBasePath(), Dir);
|
||||||
|
|
|
@ -68,6 +68,7 @@ namespace Ryujinx
|
||||||
};
|
};
|
||||||
|
|
||||||
Screen.MainLoop();
|
Screen.MainLoop();
|
||||||
|
Ns.OnFinish(EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
|
|
Loading…
Reference in a new issue