2018-06-11 02:46:42 +02:00
|
|
|
using Ryujinx.HLE.Loaders.Executables;
|
2018-07-20 23:53:06 +02:00
|
|
|
using Ryujinx.HLE.Loaders.Npdm;
|
2018-06-11 02:46:42 +02:00
|
|
|
using Ryujinx.HLE.Logging;
|
|
|
|
using Ryujinx.HLE.OsHle.Handles;
|
2018-02-20 21:09:23 +01:00
|
|
|
using System;
|
2018-02-05 00:08:20 +01:00
|
|
|
using System.Collections.Concurrent;
|
|
|
|
using System.IO;
|
|
|
|
|
2018-06-11 02:46:42 +02:00
|
|
|
namespace Ryujinx.HLE.OsHle
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
public class Horizon : IDisposable
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
|
|
|
internal const int HidSize = 0x40000;
|
|
|
|
internal const int FontSize = 0x50;
|
|
|
|
|
2018-04-24 22:14:26 +02:00
|
|
|
private Switch Ns;
|
|
|
|
|
2018-04-19 04:52:23 +02:00
|
|
|
private KProcessScheduler Scheduler;
|
2018-02-05 00:08:20 +01:00
|
|
|
|
|
|
|
private ConcurrentDictionary<int, Process> Processes;
|
|
|
|
|
2018-04-30 01:18:46 +02:00
|
|
|
public SystemStateMgr SystemState { get; private set; }
|
2018-04-24 22:14:26 +02:00
|
|
|
|
NvServices refactoring (#120)
* Initial implementation of NvMap/NvHostCtrl
* More work on NvHostCtrl
* Refactoring of nvservices, move GPU Vmm, make Vmm per-process, refactor most gpu devices, move Gpu to Core, fix CbBind
* Implement GetGpuTime, support CancelSynchronization, fix issue on InsertWaitingMutex, proper double buffering support (again, not working properly for commercial games, only hb)
* Try to fix perf regression reading/writing textures, moved syncpts and events to a UserCtx class, delete global state when the process exits, other minor tweaks
* Remove now unused code, add comment about probably wrong result codes
2018-05-07 20:53:23 +02:00
|
|
|
internal MemoryAllocator Allocator { get; private set; }
|
|
|
|
|
2018-03-19 19:58:46 +01:00
|
|
|
internal HSharedMem HidSharedMem { get; private set; }
|
|
|
|
internal HSharedMem FontSharedMem { get; private set; }
|
|
|
|
|
|
|
|
internal KEvent VsyncEvent { get; private set; }
|
2018-02-17 22:36:08 +01:00
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
public Horizon(Switch Ns)
|
|
|
|
{
|
|
|
|
this.Ns = Ns;
|
|
|
|
|
2018-04-24 20:57:39 +02:00
|
|
|
Scheduler = new KProcessScheduler(Ns.Log);
|
2018-02-05 00:08:20 +01:00
|
|
|
|
|
|
|
Processes = new ConcurrentDictionary<int, Process>();
|
|
|
|
|
2018-04-24 22:14:26 +02:00
|
|
|
SystemState = new SystemStateMgr();
|
|
|
|
|
NvServices refactoring (#120)
* Initial implementation of NvMap/NvHostCtrl
* More work on NvHostCtrl
* Refactoring of nvservices, move GPU Vmm, make Vmm per-process, refactor most gpu devices, move Gpu to Core, fix CbBind
* Implement GetGpuTime, support CancelSynchronization, fix issue on InsertWaitingMutex, proper double buffering support (again, not working properly for commercial games, only hb)
* Try to fix perf regression reading/writing textures, moved syncpts and events to a UserCtx class, delete global state when the process exits, other minor tweaks
* Remove now unused code, add comment about probably wrong result codes
2018-05-07 20:53:23 +02:00
|
|
|
Allocator = new MemoryAllocator();
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
HidSharedMem = new HSharedMem();
|
|
|
|
FontSharedMem = new HSharedMem();
|
2018-03-19 19:58:46 +01:00
|
|
|
|
|
|
|
VsyncEvent = new KEvent();
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
|
|
|
{
|
|
|
|
if (RomFsFile != null)
|
|
|
|
{
|
|
|
|
Ns.VFs.LoadRomFs(RomFsFile);
|
|
|
|
}
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
Process MainProcess = MakeProcess();
|
2018-02-05 00:08:20 +01:00
|
|
|
|
|
|
|
void LoadNso(string FileName)
|
|
|
|
{
|
|
|
|
foreach (string File in Directory.GetFiles(ExeFsDir, FileName))
|
|
|
|
{
|
|
|
|
if (Path.GetExtension(File) != string.Empty)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-04-24 20:57:39 +02:00
|
|
|
Ns.Log.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
|
2018-02-17 22:06:11 +01:00
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
using (FileStream Input = new FileStream(File, FileMode.Open))
|
|
|
|
{
|
2018-04-22 06:21:49 +02:00
|
|
|
string Name = Path.GetFileNameWithoutExtension(File);
|
|
|
|
|
|
|
|
Nso Program = new Nso(Input, Name);
|
2018-02-05 00:08:20 +01:00
|
|
|
|
|
|
|
MainProcess.LoadProgram(Program);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-20 23:53:06 +02:00
|
|
|
void LoadNpdm(string FileName)
|
|
|
|
{
|
|
|
|
string File = Directory.GetFiles(ExeFsDir, FileName)[0];
|
|
|
|
|
|
|
|
Ns.Log.PrintInfo(LogClass.Loader, "Loading Title Metadata...");
|
|
|
|
|
|
|
|
using (FileStream Input = new FileStream(File, FileMode.Open))
|
|
|
|
{
|
|
|
|
SystemStateMgr.TitleMetadata = new Npdm(Input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LoadNpdm("*.npdm");
|
|
|
|
|
|
|
|
if (!SystemStateMgr.TitleMetadata.Is64Bits)
|
|
|
|
{
|
|
|
|
throw new Exception("32-bit titles are unsupported!");
|
|
|
|
}
|
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
LoadNso("rtld");
|
|
|
|
|
|
|
|
MainProcess.SetEmptyArgs();
|
|
|
|
|
|
|
|
LoadNso("main");
|
|
|
|
LoadNso("subsdk*");
|
|
|
|
LoadNso("sdk");
|
|
|
|
|
|
|
|
MainProcess.Run();
|
|
|
|
}
|
|
|
|
|
2018-07-17 21:14:27 +02:00
|
|
|
public void LoadProgram(string FilePath)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-07-17 21:14:27 +02:00
|
|
|
bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
|
2018-02-24 01:59:38 +01:00
|
|
|
|
2018-07-17 21:14:27 +02:00
|
|
|
string Name = Path.GetFileNameWithoutExtension(FilePath);
|
|
|
|
string SwitchFilePath = Ns.VFs.SystemPathToSwitchPath(FilePath);
|
|
|
|
|
|
|
|
if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/")))
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2018-04-22 06:21:49 +02:00
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
Process MainProcess = MakeProcess();
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-07-17 21:14:27 +02:00
|
|
|
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-24 01:59:38 +01:00
|
|
|
MainProcess.LoadProgram(IsNro
|
2018-07-17 21:14:27 +02:00
|
|
|
? (IExecutable)new Nro(Input, FilePath)
|
|
|
|
: (IExecutable)new Nso(Input, FilePath));
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
MainProcess.SetEmptyArgs();
|
2018-02-24 01:59:38 +01:00
|
|
|
MainProcess.Run(IsNro);
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-04-19 04:52:23 +02:00
|
|
|
public void SignalVsync() => VsyncEvent.WaitEvent.Set();
|
2018-03-19 19:58:46 +01:00
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
private Process MakeProcess()
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
Process Process;
|
|
|
|
|
|
|
|
lock (Processes)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
int ProcessId = 0;
|
|
|
|
|
|
|
|
while (Processes.ContainsKey(ProcessId))
|
|
|
|
{
|
|
|
|
ProcessId++;
|
|
|
|
}
|
|
|
|
|
2018-04-19 04:52:23 +02:00
|
|
|
Process = new Process(Ns, Scheduler, ProcessId);
|
2018-03-12 05:04:52 +01:00
|
|
|
|
|
|
|
Processes.TryAdd(ProcessId, Process);
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
2018-03-12 05:04:52 +01:00
|
|
|
|
2018-03-19 19:58:46 +01:00
|
|
|
InitializeProcess(Process);
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
return Process;
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-03-19 19:58:46 +01:00
|
|
|
private void InitializeProcess(Process Process)
|
|
|
|
{
|
|
|
|
Process.AppletState.SetFocus(true);
|
|
|
|
}
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
internal void ExitProcess(int ProcessId)
|
2018-02-17 22:36:08 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi)
|
2018-02-17 22:36:08 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
|
|
|
|
|
2018-04-24 20:57:39 +02:00
|
|
|
Ns.Log.PrintInfo(LogClass.Loader, $"HbAbi NextLoadPath {NextNro}");
|
2018-03-12 05:04:52 +01:00
|
|
|
|
|
|
|
if (NextNro == string.Empty)
|
|
|
|
{
|
|
|
|
NextNro = "sdmc:/hbmenu.nro";
|
|
|
|
}
|
|
|
|
|
|
|
|
NextNro = NextNro.Replace("sdmc:", string.Empty);
|
|
|
|
|
|
|
|
NextNro = Ns.VFs.GetFullPath(Ns.VFs.GetSdCardPath(), NextNro);
|
|
|
|
|
|
|
|
if (File.Exists(NextNro))
|
|
|
|
{
|
|
|
|
LoadProgram(NextNro);
|
|
|
|
}
|
2018-02-15 13:16:16 +01:00
|
|
|
}
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
if (Processes.TryRemove(ProcessId, out Process))
|
2018-02-17 22:36:08 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
Process.StopAllThreadsAsync();
|
|
|
|
Process.Dispose();
|
2018-02-17 22:36:08 +01:00
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
if (Processes.Count == 0)
|
|
|
|
{
|
|
|
|
Ns.OnFinish(EventArgs.Empty);
|
|
|
|
}
|
|
|
|
}
|
2018-02-15 13:16:16 +01:00
|
|
|
}
|
2018-02-17 22:36:08 +01:00
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
internal bool TryGetProcess(int ProcessId, out Process Process)
|
|
|
|
{
|
2018-02-20 11:54:00 +01:00
|
|
|
return Processes.TryGetValue(ProcessId, out Process);
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
public void Dispose()
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
Dispose(true);
|
|
|
|
}
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
protected virtual void Dispose(bool Disposing)
|
|
|
|
{
|
|
|
|
if (Disposing)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
foreach (Process Process in Processes.Values)
|
|
|
|
{
|
|
|
|
Process.StopAllThreadsAsync();
|
|
|
|
Process.Dispose();
|
|
|
|
}
|
2018-03-19 19:58:46 +01:00
|
|
|
|
|
|
|
VsyncEvent.Dispose();
|
2018-04-19 05:19:22 +02:00
|
|
|
|
|
|
|
Scheduler.Dispose();
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|