ryujinx/Ryujinx.HLE/HOS/Horizon.cs

228 lines
6.3 KiB
C#
Raw Normal View History

using Ryujinx.HLE.HOS.Font;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Executables;
2018-07-20 21:53:06 +00:00
using Ryujinx.HLE.Loaders.Npdm;
using Ryujinx.HLE.Logging;
using System;
2018-02-04 23:08:20 +00:00
using System.Collections.Concurrent;
using System.IO;
namespace Ryujinx.HLE.HOS
2018-02-04 23:08:20 +00:00
{
public class Horizon : IDisposable
2018-02-04 23:08:20 +00:00
{
internal const int HidSize = 0x40000;
internal const int FontSize = 0x1100000;
2018-02-04 23:08:20 +00:00
private Switch Device;
private KProcessScheduler Scheduler;
2018-02-04 23:08:20 +00:00
private ConcurrentDictionary<int, Process> Processes;
public SystemStateMgr State { get; private set; }
internal KSharedMemory HidSharedMem { get; private set; }
internal KSharedMemory FontSharedMem { get; private set; }
internal SharedFontManager Font { get; private set; }
internal KEvent VsyncEvent { get; private set; }
public Horizon(Switch Device)
2018-02-04 23:08:20 +00:00
{
this.Device = Device;
2018-02-04 23:08:20 +00:00
Scheduler = new KProcessScheduler(Device.Log);
2018-02-04 23:08:20 +00:00
Processes = new ConcurrentDictionary<int, Process>();
State = new SystemStateMgr();
if (!Device.Memory.Allocator.TryAllocate(HidSize, out long HidPA) ||
!Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA))
{
throw new InvalidOperationException();
}
HidSharedMem = new KSharedMemory(HidPA, HidSize);
FontSharedMem = new KSharedMemory(FontPA, FontSize);
Font = new SharedFontManager(Device, FontSharedMem.PA);
VsyncEvent = new KEvent();
2018-02-04 23:08:20 +00:00
}
public void LoadCart(string ExeFsDir, string RomFsFile = null)
{
if (RomFsFile != null)
{
Device.FileSystem.LoadRomFs(RomFsFile);
2018-02-04 23:08:20 +00:00
}
string NpdmFileName = Path.Combine(ExeFsDir, "main.npdm");
Npdm MetaData = null;
if (File.Exists(NpdmFileName))
{
Device.Log.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
using (FileStream Input = new FileStream(NpdmFileName, FileMode.Open))
{
MetaData = new Npdm(Input);
}
}
else
{
Device.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
}
Process MainProcess = MakeProcess(MetaData);
2018-02-04 23:08:20 +00:00
void LoadNso(string FileName)
{
foreach (string File in Directory.GetFiles(ExeFsDir, FileName))
{
if (Path.GetExtension(File) != string.Empty)
{
continue;
}
Device.Log.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
2018-02-04 23:08:20 +00:00
using (FileStream Input = new FileStream(File, FileMode.Open))
{
string Name = Path.GetFileNameWithoutExtension(File);
Nso Program = new Nso(Input, Name);
2018-02-04 23:08:20 +00:00
MainProcess.LoadProgram(Program);
}
}
}
if (!MainProcess.MetaData.Is64Bits)
2018-07-20 21:53:06 +00:00
{
throw new NotImplementedException("32-bit titles are unsupported!");
2018-07-20 21:53:06 +00:00
}
2018-02-04 23:08:20 +00:00
LoadNso("rtld");
MainProcess.SetEmptyArgs();
LoadNso("main");
LoadNso("subsdk*");
LoadNso("sdk");
MainProcess.Run();
}
public void LoadProgram(string FilePath)
2018-02-04 23:08:20 +00:00
{
bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
string Name = Path.GetFileNameWithoutExtension(FilePath);
string SwitchFilePath = Device.FileSystem.SystemPathToSwitchPath(FilePath);
if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/")))
{
string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}";
string TempPath = Device.FileSystem.SwitchPathToSystemPath(SwitchPath);
string SwitchDir = Path.GetDirectoryName(TempPath);
if (!Directory.Exists(SwitchDir))
{
Directory.CreateDirectory(SwitchDir);
}
File.Copy(FilePath, TempPath, true);
FilePath = TempPath;
}
Process MainProcess = MakeProcess();
2018-02-04 23:08:20 +00:00
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
2018-02-04 23:08:20 +00:00
{
MainProcess.LoadProgram(IsNro
? (IExecutable)new Nro(Input, FilePath)
: (IExecutable)new Nso(Input, FilePath));
2018-02-04 23:08:20 +00:00
}
MainProcess.SetEmptyArgs();
MainProcess.Run(IsNro);
2018-02-04 23:08:20 +00:00
}
public void SignalVsync() => VsyncEvent.WaitEvent.Set();
private Process MakeProcess(Npdm MetaData = null)
2018-02-04 23:08:20 +00:00
{
Process Process;
lock (Processes)
2018-02-04 23:08:20 +00:00
{
int ProcessId = 0;
while (Processes.ContainsKey(ProcessId))
{
ProcessId++;
}
Process = new Process(Device, Scheduler, ProcessId, MetaData);
Processes.TryAdd(ProcessId, Process);
2018-02-04 23:08:20 +00:00
}
InitializeProcess(Process);
return Process;
2018-02-04 23:08:20 +00:00
}
private void InitializeProcess(Process Process)
{
Process.AppletState.SetFocus(true);
}
internal void ExitProcess(int ProcessId)
{
if (Processes.TryRemove(ProcessId, out Process Process))
{
Process.Dispose();
if (Processes.Count == 0)
{
Unload();
Device.Unload();
}
}
}
private void Unload()
2018-02-04 23:08:20 +00:00
{
VsyncEvent.Dispose();
Scheduler.Dispose();
2018-02-04 23:08:20 +00:00
}
public void Dispose()
2018-02-04 23:08:20 +00:00
{
Dispose(true);
}
2018-02-04 23:08:20 +00:00
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
2018-02-04 23:08:20 +00:00
{
foreach (Process Process in Processes.Values)
{
Process.Dispose();
}
2018-02-04 23:08:20 +00:00
}
}
}
}