edafce57be
* Added GUI to Ryujinx * Updated to use Glade Also added scrollbar and default dark theme * Added support for loading icon from .nro files and cleaned up the code a bit * Added General Settings Menu (read-only for now) and moved some functionality from MainMenu.cs to ApplicationLibrary.cs * Added custom GUI theme support and changed the defualt theme to one I just wrote * Added GTK to process path, fixed a bug and minor edits * some more edits and a bug fix * general settings menu is now fully functional. also fixed the bug where ryujinx crashes when it trys to load an invalid gamedir * big rewrite * aesthetic changes to General Settings menu * Added Control Settings one day done feature :P * minor changes * 1st wave of changes * 2nd wave of changes * 3rd wave of changes * Cleanup settings ui * minor edits * new about window added, still needs styling * added spin button for new option and tooltips to settings * Game icons and names are now shown in the games list * add nuget package which contains gtk dependencies * requested changes have been changed * put CreateGameWindow on a new thread and stopped destroying the main menu when a game loads * fixed bug that allowed a user to attempt to load multiple games at a time which causes a crash * Added LastPlayed and TimePlayed columns to the game list * Did some testing and fixed some bugs Im not happy with one of the fixes so i will do it properly an upcoming commit * did some more bug testing and fixed another 2 bugs * caught an exception when ryujinx tries to load non-homebrew as homebrew * Large changes Rewrote ApplicationLibrary.cs (added comments too) so any devs reading it wont get eye cancer, also its probably more efficient now. Added 2 new columns (Developer name and application version) to the game list and wrote the logic for it. Ryujinx now loads NRO's TitleName and TitleID from the NACP file instead of the default NPDM. I also killed a lot of bugs * Moved Files moved ApplicationLibrary.cs to Ryujinx.HLE as that is a better place for it. Moved contents of GUI folder to Ui folder and changed the namespaces of the gui files from Ryujinx to Ryujinx.Ui * Added 'Open Ryujinx Folder' button to the file menu and did some small fixes * New features * updated nuget package with missing dlls and changed emmauss' requested changes * fixed some minor issues * all requested changes marked as resolved have been changed * gdkchan's requested changes * fixed an issue with settings window getting chopped on small res * fixed 2 problems caused by rebase * changed the default theme * applied Thog's patch to fix issue on linux * fixed issue caused by rebase * added update check button that runs ryujinx-updater * reads version info from installer and displays it in about menu * changes completed * requested changes changed * fixed issue with default theme * fixed a bug and completed requested changes * added more tooltips and changed some text
583 lines
17 KiB
C#
583 lines
17 KiB
C#
using ARMeilleure.Memory;
|
|
using Ryujinx.Common;
|
|
using Ryujinx.Common.Logging;
|
|
using Ryujinx.HLE.Exceptions;
|
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
|
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|
{
|
|
partial class SvcHandler
|
|
{
|
|
public void ExitProcess64()
|
|
{
|
|
ExitProcess();
|
|
}
|
|
|
|
private void ExitProcess()
|
|
{
|
|
_system.Scheduler.GetCurrentProcess().Terminate();
|
|
}
|
|
|
|
public KernelResult SignalEvent64(int handle)
|
|
{
|
|
return SignalEvent(handle);
|
|
}
|
|
|
|
private KernelResult SignalEvent(int handle)
|
|
{
|
|
KWritableEvent writableEvent = _process.HandleTable.GetObject<KWritableEvent>(handle);
|
|
|
|
KernelResult result;
|
|
|
|
if (writableEvent != null)
|
|
{
|
|
writableEvent.Signal();
|
|
|
|
result = KernelResult.Success;
|
|
}
|
|
else
|
|
{
|
|
result = KernelResult.InvalidHandle;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public KernelResult ClearEvent64(int handle)
|
|
{
|
|
return ClearEvent(handle);
|
|
}
|
|
|
|
private KernelResult ClearEvent(int handle)
|
|
{
|
|
KernelResult result;
|
|
|
|
KWritableEvent writableEvent = _process.HandleTable.GetObject<KWritableEvent>(handle);
|
|
|
|
if (writableEvent == null)
|
|
{
|
|
KReadableEvent readableEvent = _process.HandleTable.GetObject<KReadableEvent>(handle);
|
|
|
|
result = readableEvent?.Clear() ?? KernelResult.InvalidHandle;
|
|
}
|
|
else
|
|
{
|
|
result = writableEvent.Clear();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public KernelResult CloseHandle64(int handle)
|
|
{
|
|
return CloseHandle(handle);
|
|
}
|
|
|
|
private KernelResult CloseHandle(int handle)
|
|
{
|
|
KAutoObject obj = _process.HandleTable.GetObject<KAutoObject>(handle);
|
|
|
|
_process.HandleTable.CloseHandle(handle);
|
|
|
|
if (obj == null)
|
|
{
|
|
return KernelResult.InvalidHandle;
|
|
}
|
|
|
|
if (obj is KSession session)
|
|
{
|
|
session.Dispose();
|
|
}
|
|
else if (obj is KTransferMemory transferMemory)
|
|
{
|
|
_process.MemoryManager.ResetTransferMemory(
|
|
transferMemory.Address,
|
|
transferMemory.Size);
|
|
}
|
|
|
|
return KernelResult.Success;
|
|
}
|
|
|
|
public KernelResult ResetSignal64(int handle)
|
|
{
|
|
return ResetSignal(handle);
|
|
}
|
|
|
|
private KernelResult ResetSignal(int handle)
|
|
{
|
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
|
|
KReadableEvent readableEvent = currentProcess.HandleTable.GetObject<KReadableEvent>(handle);
|
|
|
|
KernelResult result;
|
|
|
|
if (readableEvent != null)
|
|
{
|
|
result = readableEvent.ClearIfSignaled();
|
|
}
|
|
else
|
|
{
|
|
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
|
|
|
|
if (process != null)
|
|
{
|
|
result = process.ClearIfNotExited();
|
|
}
|
|
else
|
|
{
|
|
result = KernelResult.InvalidHandle;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public ulong GetSystemTick64()
|
|
{
|
|
return _system.Scheduler.GetCurrentThread().Context.CntpctEl0;
|
|
}
|
|
|
|
public KernelResult GetProcessId64(int handle, out long pid)
|
|
{
|
|
return GetProcessId(handle, out pid);
|
|
}
|
|
|
|
private KernelResult GetProcessId(int handle, out long pid)
|
|
{
|
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
|
|
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
|
|
|
|
if (process == null)
|
|
{
|
|
KThread thread = currentProcess.HandleTable.GetKThread(handle);
|
|
|
|
if (thread != null)
|
|
{
|
|
process = thread.Owner;
|
|
}
|
|
|
|
// TODO: KDebugEvent.
|
|
}
|
|
|
|
pid = process?.Pid ?? 0;
|
|
|
|
return process != null
|
|
? KernelResult.Success
|
|
: KernelResult.InvalidHandle;
|
|
}
|
|
|
|
public void Break64(ulong reason, ulong x1, ulong info)
|
|
{
|
|
Break(reason);
|
|
}
|
|
|
|
private void Break(ulong reason)
|
|
{
|
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
|
|
|
if ((reason & (1UL << 31)) == 0)
|
|
{
|
|
currentThread.PrintGuestStackTrace();
|
|
|
|
throw new GuestBrokeExecutionException();
|
|
}
|
|
else
|
|
{
|
|
Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered.");
|
|
|
|
currentThread.PrintGuestStackTrace();
|
|
}
|
|
}
|
|
|
|
public void OutputDebugString64(ulong strPtr, ulong size)
|
|
{
|
|
OutputDebugString(strPtr, size);
|
|
}
|
|
|
|
private void OutputDebugString(ulong strPtr, ulong size)
|
|
{
|
|
string str = MemoryHelper.ReadAsciiString(_process.CpuMemory, (long)strPtr, (long)size);
|
|
|
|
Logger.PrintWarning(LogClass.KernelSvc, str);
|
|
}
|
|
|
|
public KernelResult GetInfo64(uint id, int handle, long subId, out long value)
|
|
{
|
|
return GetInfo(id, handle, subId, out value);
|
|
}
|
|
|
|
private KernelResult GetInfo(uint id, int handle, long subId, out long value)
|
|
{
|
|
value = 0;
|
|
|
|
switch (id)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
case 16:
|
|
case 17:
|
|
case 18:
|
|
case 20:
|
|
case 21:
|
|
case 22:
|
|
{
|
|
if (subId != 0)
|
|
{
|
|
return KernelResult.InvalidCombination;
|
|
}
|
|
|
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
|
|
KProcess process = currentProcess.HandleTable.GetKProcess(handle);
|
|
|
|
if (process == null)
|
|
{
|
|
return KernelResult.InvalidHandle;
|
|
}
|
|
|
|
switch (id)
|
|
{
|
|
case 0: value = process.Capabilities.AllowedCpuCoresMask; break;
|
|
case 1: value = process.Capabilities.AllowedThreadPriosMask; break;
|
|
|
|
case 2: value = (long)process.MemoryManager.AliasRegionStart; break;
|
|
case 3: value = (long)(process.MemoryManager.AliasRegionEnd -
|
|
process.MemoryManager.AliasRegionStart); break;
|
|
|
|
case 4: value = (long)process.MemoryManager.HeapRegionStart; break;
|
|
case 5: value = (long)(process.MemoryManager.HeapRegionEnd -
|
|
process.MemoryManager.HeapRegionStart); break;
|
|
|
|
case 6: value = (long)process.GetMemoryCapacity(); break;
|
|
|
|
case 7: value = (long)process.GetMemoryUsage(); break;
|
|
|
|
case 12: value = (long)process.MemoryManager.GetAddrSpaceBaseAddr(); break;
|
|
|
|
case 13: value = (long)process.MemoryManager.GetAddrSpaceSize(); break;
|
|
|
|
case 14: value = (long)process.MemoryManager.StackRegionStart; break;
|
|
case 15: value = (long)(process.MemoryManager.StackRegionEnd -
|
|
process.MemoryManager.StackRegionStart); break;
|
|
|
|
case 16: value = (long)process.PersonalMmHeapPagesCount * KMemoryManager.PageSize; break;
|
|
|
|
case 17:
|
|
if (process.PersonalMmHeapPagesCount != 0)
|
|
{
|
|
value = process.MemoryManager.GetMmUsedPages() * KMemoryManager.PageSize;
|
|
}
|
|
|
|
break;
|
|
|
|
case 18: value = (long)process.TitleId; break;
|
|
|
|
case 20: value = (long)process.UserExceptionContextAddress; break;
|
|
|
|
case 21: value = (long)process.GetMemoryCapacityWithoutPersonalMmHeap(); break;
|
|
|
|
case 22: value = (long)process.GetMemoryUsageWithoutPersonalMmHeap(); break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 8:
|
|
{
|
|
if (handle != 0)
|
|
{
|
|
return KernelResult.InvalidHandle;
|
|
}
|
|
|
|
if (subId != 0)
|
|
{
|
|
return KernelResult.InvalidCombination;
|
|
}
|
|
|
|
value = _system.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
|
|
|
|
break;
|
|
}
|
|
|
|
case 9:
|
|
{
|
|
if (handle != 0)
|
|
{
|
|
return KernelResult.InvalidHandle;
|
|
}
|
|
|
|
if (subId != 0)
|
|
{
|
|
return KernelResult.InvalidCombination;
|
|
}
|
|
|
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
|
|
if (currentProcess.ResourceLimit != null)
|
|
{
|
|
KHandleTable handleTable = currentProcess.HandleTable;
|
|
KResourceLimit resourceLimit = currentProcess.ResourceLimit;
|
|
|
|
KernelResult result = handleTable.GenerateHandle(resourceLimit, out int resLimHandle);
|
|
|
|
if (result != KernelResult.Success)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
value = (uint)resLimHandle;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 10:
|
|
{
|
|
if (handle != 0)
|
|
{
|
|
return KernelResult.InvalidHandle;
|
|
}
|
|
|
|
int currentCore = _system.Scheduler.GetCurrentThread().CurrentCore;
|
|
|
|
if (subId != -1 && subId != currentCore)
|
|
{
|
|
return KernelResult.InvalidCombination;
|
|
}
|
|
|
|
value = _system.Scheduler.CoreContexts[currentCore].TotalIdleTimeTicks;
|
|
|
|
break;
|
|
}
|
|
|
|
case 11:
|
|
{
|
|
if (handle != 0)
|
|
{
|
|
return KernelResult.InvalidHandle;
|
|
}
|
|
|
|
if ((ulong)subId > 3)
|
|
{
|
|
return KernelResult.InvalidCombination;
|
|
}
|
|
|
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
|
|
|
|
value = currentProcess.RandomEntropy[subId];
|
|
|
|
break;
|
|
}
|
|
|
|
case 0xf0000002u:
|
|
{
|
|
if (subId < -1 || subId > 3)
|
|
{
|
|
return KernelResult.InvalidCombination;
|
|
}
|
|
|
|
KThread thread = _system.Scheduler.GetCurrentProcess().HandleTable.GetKThread(handle);
|
|
|
|
if (thread == null)
|
|
{
|
|
return KernelResult.InvalidHandle;
|
|
}
|
|
|
|
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
|
|
|
int currentCore = currentThread.CurrentCore;
|
|
|
|
if (subId != -1 && subId != currentCore)
|
|
{
|
|
return KernelResult.Success;
|
|
}
|
|
|
|
KCoreContext coreContext = _system.Scheduler.CoreContexts[currentCore];
|
|
|
|
long timeDelta = PerformanceCounter.ElapsedMilliseconds - coreContext.LastContextSwitchTime;
|
|
|
|
if (subId != -1)
|
|
{
|
|
value = KTimeManager.ConvertMillisecondsToTicks(timeDelta);
|
|
}
|
|
else
|
|
{
|
|
long totalTimeRunning = thread.TotalTimeRunning;
|
|
|
|
if (thread == currentThread)
|
|
{
|
|
totalTimeRunning += timeDelta;
|
|
}
|
|
|
|
value = KTimeManager.ConvertMillisecondsToTicks(totalTimeRunning);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: return KernelResult.InvalidEnumValue;
|
|
}
|
|
|
|
return KernelResult.Success;
|
|
}
|
|
|
|
public KernelResult CreateEvent64(out int wEventHandle, out int rEventHandle)
|
|
{
|
|
return CreateEvent(out wEventHandle, out rEventHandle);
|
|
}
|
|
|
|
private KernelResult CreateEvent(out int wEventHandle, out int rEventHandle)
|
|
{
|
|
KEvent Event = new KEvent(_system);
|
|
|
|
KernelResult result = _process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle);
|
|
|
|
if (result == KernelResult.Success)
|
|
{
|
|
result = _process.HandleTable.GenerateHandle(Event.ReadableEvent, out rEventHandle);
|
|
|
|
if (result != KernelResult.Success)
|
|
{
|
|
_process.HandleTable.CloseHandle(wEventHandle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rEventHandle = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public KernelResult GetProcessList64(ulong address, int maxCount, out int count)
|
|
{
|
|
return GetProcessList(address, maxCount, out count);
|
|
}
|
|
|
|
private KernelResult GetProcessList(ulong address, int maxCount, out int count)
|
|
{
|
|
count = 0;
|
|
|
|
if ((maxCount >> 28) != 0)
|
|
{
|
|
return KernelResult.MaximumExceeded;
|
|
}
|
|
|
|
if (maxCount != 0)
|
|
{
|
|
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
|
|
|
ulong copySize = (ulong)maxCount * 8;
|
|
|
|
if (address + copySize <= address)
|
|
{
|
|
return KernelResult.InvalidMemState;
|
|
}
|
|
|
|
if (currentProcess.MemoryManager.OutsideAddrSpace(address, copySize))
|
|
{
|
|
return KernelResult.InvalidMemState;
|
|
}
|
|
}
|
|
|
|
int copyCount = 0;
|
|
|
|
lock (_system.Processes)
|
|
{
|
|
foreach (KProcess process in _system.Processes.Values)
|
|
{
|
|
if (copyCount < maxCount)
|
|
{
|
|
if (!KernelTransfer.KernelToUserInt64(_system, address + (ulong)copyCount * 8, process.Pid))
|
|
{
|
|
return KernelResult.UserCopyFailed;
|
|
}
|
|
}
|
|
|
|
copyCount++;
|
|
}
|
|
}
|
|
|
|
count = copyCount;
|
|
|
|
return KernelResult.Success;
|
|
}
|
|
|
|
public KernelResult GetSystemInfo64(uint id, int handle, long subId, out long value)
|
|
{
|
|
return GetSystemInfo(id, handle, subId, out value);
|
|
}
|
|
|
|
private KernelResult GetSystemInfo(uint id, int handle, long subId, out long value)
|
|
{
|
|
value = 0;
|
|
|
|
if (id > 2)
|
|
{
|
|
return KernelResult.InvalidEnumValue;
|
|
}
|
|
|
|
if (handle != 0)
|
|
{
|
|
return KernelResult.InvalidHandle;
|
|
}
|
|
|
|
if (id < 2)
|
|
{
|
|
if ((ulong)subId > 3)
|
|
{
|
|
return KernelResult.InvalidCombination;
|
|
}
|
|
|
|
KMemoryRegionManager region = _system.MemoryRegions[subId];
|
|
|
|
switch (id)
|
|
{
|
|
// Memory region capacity.
|
|
case 0: value = (long)region.Size; break;
|
|
|
|
// Memory region free space.
|
|
case 1:
|
|
{
|
|
ulong freePagesCount = region.GetFreePages();
|
|
|
|
value = (long)(freePagesCount * KMemoryManager.PageSize);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else /* if (Id == 2) */
|
|
{
|
|
if ((ulong)subId > 1)
|
|
{
|
|
return KernelResult.InvalidCombination;
|
|
}
|
|
|
|
switch (subId)
|
|
{
|
|
case 0: value = _system.PrivilegedProcessLowestId; break;
|
|
case 1: value = _system.PrivilegedProcessHighestId; break;
|
|
}
|
|
}
|
|
|
|
return KernelResult.Success;
|
|
}
|
|
}
|
|
}
|