mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-07-24 23:47:10 +02:00
Split main project into core,graphics and chocolarm4 subproject (#29)
This commit is contained in:
parent
cb665bb715
commit
62b827f474
257 changed files with 415 additions and 285 deletions
95
Ryujinx.Core/Config.cs
Normal file
95
Ryujinx.Core/Config.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ryujinx.Core
|
||||
{
|
||||
public static class Config
|
||||
{
|
||||
public static bool LoggingEnableInfo { get; private set; }
|
||||
public static bool LoggingEnableTrace { get; private set; }
|
||||
public static bool LoggingEnableDebug { get; private set; }
|
||||
public static bool LoggingEnableWarn { get; private set; }
|
||||
public static bool LoggingEnableError { get; private set; }
|
||||
public static bool LoggingEnableFatal { get; private set; }
|
||||
public static bool LoggingEnableLogFile { get; private set; }
|
||||
|
||||
public static JoyCon FakeJoyCon { get; private set; }
|
||||
|
||||
public static void Read()
|
||||
{
|
||||
var iniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
var iniPath = Path.Combine(iniFolder, "Ryujinx.conf");
|
||||
IniParser Parser = new IniParser(iniPath);
|
||||
|
||||
LoggingEnableInfo = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
|
||||
LoggingEnableTrace = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
|
||||
LoggingEnableDebug = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
|
||||
LoggingEnableWarn = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
|
||||
LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
|
||||
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
|
||||
LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
|
||||
|
||||
FakeJoyCon = new JoyCon
|
||||
{
|
||||
Left = new JoyConLeft
|
||||
{
|
||||
StickUp = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Up")),
|
||||
StickDown = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Down")),
|
||||
StickLeft = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Left")),
|
||||
StickRight = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Right")),
|
||||
StickButton = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Button")),
|
||||
DPadUp = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Up")),
|
||||
DPadDown = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Down")),
|
||||
DPadLeft = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Left")),
|
||||
DPadRight = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Right")),
|
||||
ButtonMinus = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_Minus")),
|
||||
ButtonL = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_L")),
|
||||
ButtonZL = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_ZL"))
|
||||
},
|
||||
|
||||
Right = new JoyConRight
|
||||
{
|
||||
StickUp = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Up")),
|
||||
StickDown = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Down")),
|
||||
StickLeft = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Left")),
|
||||
StickRight = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Right")),
|
||||
StickButton = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Button")),
|
||||
ButtonA = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_A")),
|
||||
ButtonB = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_B")),
|
||||
ButtonX = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_X")),
|
||||
ButtonY = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Y")),
|
||||
ButtonPlus = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Plus")),
|
||||
ButtonR = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_R")),
|
||||
ButtonZR = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_ZR"))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/37772571
|
||||
public class IniParser
|
||||
{
|
||||
private readonly Dictionary<string, string> Values;
|
||||
|
||||
public IniParser(string Path)
|
||||
{
|
||||
Values = File.ReadLines(Path)
|
||||
.Where(Line => !string.IsNullOrWhiteSpace(Line) && !Line.StartsWith('#'))
|
||||
.Select(Line => Line.Split('=', 2))
|
||||
.ToDictionary(Parts => Parts[0].Trim(), Parts => Parts.Length > 1 ? Parts[1].Trim() : null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the setting value for the requested setting <see cref="Name"/>.
|
||||
/// </summary>
|
||||
/// <param name="Name">Setting Name</param>
|
||||
/// <param name="defaultValue">Default value of the setting</param>
|
||||
public string Value(string Name, string defaultValue = null)
|
||||
{
|
||||
return Values.TryGetValue(Name, out var value) ? value : defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
185
Ryujinx.Core/Hid.cs
Normal file
185
Ryujinx.Core/Hid.cs
Normal file
|
@ -0,0 +1,185 @@
|
|||
using Ryujinx.Core.OsHle;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Core
|
||||
{
|
||||
public class Hid
|
||||
{
|
||||
/*
|
||||
Thanks to:
|
||||
https://github.com/reswitched/libtransistor/blob/development/lib/hid.c
|
||||
https://github.com/reswitched/libtransistor/blob/development/include/libtransistor/hid.h
|
||||
https://github.com/switchbrew/libnx/blob/master/nx/source/services/hid.c
|
||||
https://github.com/switchbrew/libnx/blob/master/nx/include/switch/services/hid.h
|
||||
|
||||
struct HidSharedMemory
|
||||
{
|
||||
header[0x400];
|
||||
touchscreen[0x3000];
|
||||
mouse[0x400];
|
||||
keyboard[0x400];
|
||||
unkSection1[0x400];
|
||||
unkSection2[0x400];
|
||||
unkSection3[0x400];
|
||||
unkSection4[0x400];
|
||||
unkSection5[0x200];
|
||||
unkSection6[0x200];
|
||||
unkSection7[0x200];
|
||||
unkSection8[0x800];
|
||||
controllerSerials[0x4000];
|
||||
controllers[0x5000 * 10];
|
||||
unkSection9[0x4600];
|
||||
}
|
||||
*/
|
||||
|
||||
private const int Hid_Num_Entries = 16;
|
||||
private Switch Ns;
|
||||
private long SharedMemOffset;
|
||||
|
||||
public Hid(Switch Ns)
|
||||
{
|
||||
this.Ns = Ns;
|
||||
}
|
||||
|
||||
public void Init(long HidOffset)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (HidOffset == 0 || HidOffset + Horizon.HidSize > uint.MaxValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SharedMemOffset = HidOffset;
|
||||
|
||||
uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader));
|
||||
|
||||
IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
|
||||
|
||||
HidTouchScreen TouchScreen = new HidTouchScreen();
|
||||
TouchScreen.Header.TimestampTicks = (ulong)Environment.TickCount;
|
||||
TouchScreen.Header.NumEntries = (ulong)Hid_Num_Entries;
|
||||
TouchScreen.Header.LatestEntry = 0;
|
||||
TouchScreen.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1;
|
||||
TouchScreen.Header.Timestamp = (ulong)Environment.TickCount;
|
||||
|
||||
//TODO: Write this structure when the input is implemented
|
||||
//Marshal.StructureToPtr(TouchScreen, HidPtr, false);
|
||||
|
||||
InnerOffset += (uint)Marshal.SizeOf(typeof(HidTouchScreen));
|
||||
HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
|
||||
|
||||
HidMouse Mouse = new HidMouse();
|
||||
Mouse.Header.TimestampTicks = (ulong)Environment.TickCount;
|
||||
Mouse.Header.NumEntries = (ulong)Hid_Num_Entries;
|
||||
Mouse.Header.LatestEntry = 0;
|
||||
Mouse.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1;
|
||||
|
||||
//TODO: Write this structure when the input is implemented
|
||||
//Marshal.StructureToPtr(Mouse, HidPtr, false);
|
||||
|
||||
InnerOffset += (uint)Marshal.SizeOf(typeof(HidMouse));
|
||||
HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
|
||||
|
||||
HidKeyboard Keyboard = new HidKeyboard();
|
||||
Keyboard.Header.TimestampTicks = (ulong)Environment.TickCount;
|
||||
Keyboard.Header.NumEntries = (ulong)Hid_Num_Entries;
|
||||
Keyboard.Header.LatestEntry = 0;
|
||||
Keyboard.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1;
|
||||
|
||||
//TODO: Write this structure when the input is implemented
|
||||
//Marshal.StructureToPtr(Keyboard, HidPtr, false);
|
||||
|
||||
InnerOffset += (uint)Marshal.SizeOf(typeof(HidKeyboard)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection1)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection2)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection3)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection4)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection5)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection6)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection7)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection8)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidControllerSerials));
|
||||
|
||||
//Increase the loop to initialize more controller.
|
||||
for (int i = 8; i < Enum.GetNames(typeof(HidControllerID)).Length - 1; i++)
|
||||
{
|
||||
HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset + (uint)(Marshal.SizeOf(typeof(HidController)) * i));
|
||||
|
||||
HidController Controller = new HidController();
|
||||
Controller.Header.Type = (uint)(HidControllerType.ControllerType_Handheld | HidControllerType.ControllerType_JoyconPair);
|
||||
Controller.Header.IsHalf = 0;
|
||||
Controller.Header.SingleColorsDescriptor = (uint)(HidControllerColorDescription.ColorDesc_ColorsNonexistent);
|
||||
Controller.Header.SingleColorBody = 0;
|
||||
Controller.Header.SingleColorButtons = 0;
|
||||
Controller.Header.SplitColorsDescriptor = 0;
|
||||
Controller.Header.LeftColorBody = (uint)JoyConColor.Body_Neon_Red;
|
||||
Controller.Header.LeftColorButtons = (uint)JoyConColor.Buttons_Neon_Red;
|
||||
Controller.Header.RightColorBody = (uint)JoyConColor.Body_Neon_Blue;
|
||||
Controller.Header.RightColorButtons = (uint)JoyConColor.Buttons_Neon_Blue;
|
||||
|
||||
Controller.Layouts = new HidControllerLayout[Enum.GetNames(typeof(HidControllerLayouts)).Length];
|
||||
Controller.Layouts[(int)HidControllerLayouts.Main] = new HidControllerLayout();
|
||||
Controller.Layouts[(int)HidControllerLayouts.Main].Header.LatestEntry = (ulong)Hid_Num_Entries;
|
||||
|
||||
Marshal.StructureToPtr(Controller, HidPtr, false);
|
||||
}
|
||||
|
||||
Logging.Info("HID Initialized!");
|
||||
}
|
||||
}
|
||||
|
||||
public void SendControllerButtons(HidControllerID ControllerId,
|
||||
HidControllerLayouts Layout,
|
||||
HidControllerKeys Buttons,
|
||||
JoystickPosition LeftJoystick,
|
||||
JoystickPosition RightJoystick)
|
||||
{
|
||||
uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidTouchScreen)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidMouse)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidKeyboard)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection1)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection2)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection3)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection4)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection5)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection6)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection7)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidUnknownSection8)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidControllerSerials)) +
|
||||
((uint)(Marshal.SizeOf(typeof(HidController)) * (int)ControllerId)) +
|
||||
(uint)Marshal.SizeOf(typeof(HidControllerHeader)) +
|
||||
(uint)Layout * (uint)Marshal.SizeOf(typeof(HidControllerLayout));
|
||||
|
||||
IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
|
||||
|
||||
HidControllerLayoutHeader OldControllerHeaderLayout = (HidControllerLayoutHeader)Marshal.PtrToStructure(HidPtr, typeof(HidControllerLayoutHeader));
|
||||
|
||||
HidControllerLayoutHeader ControllerLayoutHeader = new HidControllerLayoutHeader
|
||||
{
|
||||
TimestampTicks = (ulong)Environment.TickCount,
|
||||
NumEntries = (ulong)Hid_Num_Entries,
|
||||
MaxEntryIndex = (ulong)Hid_Num_Entries - 1,
|
||||
LatestEntry = (OldControllerHeaderLayout.LatestEntry < (ulong)Hid_Num_Entries ? OldControllerHeaderLayout.LatestEntry + 1 : 0)
|
||||
};
|
||||
|
||||
Marshal.StructureToPtr(ControllerLayoutHeader, HidPtr, false);
|
||||
|
||||
InnerOffset += (uint)Marshal.SizeOf(typeof(HidControllerLayoutHeader)) + (uint)((uint)(ControllerLayoutHeader.LatestEntry) * Marshal.SizeOf(typeof(HidControllerInputEntry)));
|
||||
HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
|
||||
|
||||
HidControllerInputEntry ControllerInputEntry = new HidControllerInputEntry();
|
||||
ControllerInputEntry.Timestamp = (ulong)Environment.TickCount;
|
||||
ControllerInputEntry.Timestamp_2 = (ulong)Environment.TickCount;
|
||||
ControllerInputEntry.Buttons = (ulong)Buttons;
|
||||
ControllerInputEntry.Joysticks = new JoystickPosition[(int)HidControllerJoystick.Joystick_Num_Sticks];
|
||||
ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Left] = LeftJoystick;
|
||||
ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Right] = RightJoystick;
|
||||
ControllerInputEntry.ConnectionState = (ulong)(HidControllerConnectionState.Controller_State_Connected | HidControllerConnectionState.Controller_State_Wired);
|
||||
|
||||
Marshal.StructureToPtr(ControllerInputEntry, HidPtr, false);
|
||||
}
|
||||
}
|
||||
}
|
188
Ryujinx.Core/Hid/HidController.cs
Normal file
188
Ryujinx.Core/Hid/HidController.cs
Normal file
|
@ -0,0 +1,188 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Core
|
||||
{
|
||||
[Flags]
|
||||
public enum HidControllerKeys
|
||||
{
|
||||
KEY_A = (1 << 0),
|
||||
KEY_B = (1 << 1),
|
||||
KEY_X = (1 << 2),
|
||||
KEY_Y = (1 << 3),
|
||||
KEY_LSTICK = (1 << 4),
|
||||
KEY_RSTICK = (1 << 5),
|
||||
KEY_L = (1 << 6),
|
||||
KEY_R = (1 << 7),
|
||||
KEY_ZL = (1 << 8),
|
||||
KEY_ZR = (1 << 9),
|
||||
KEY_PLUS = (1 << 10),
|
||||
KEY_MINUS = (1 << 11),
|
||||
KEY_DLEFT = (1 << 12),
|
||||
KEY_DUP = (1 << 13),
|
||||
KEY_DRIGHT = (1 << 14),
|
||||
KEY_DDOWN = (1 << 15),
|
||||
KEY_LSTICK_LEFT = (1 << 16),
|
||||
KEY_LSTICK_UP = (1 << 17),
|
||||
KEY_LSTICK_RIGHT = (1 << 18),
|
||||
KEY_LSTICK_DOWN = (1 << 19),
|
||||
KEY_RSTICK_LEFT = (1 << 20),
|
||||
KEY_RSTICK_UP = (1 << 21),
|
||||
KEY_RSTICK_RIGHT = (1 << 22),
|
||||
KEY_RSTICK_DOWN = (1 << 23),
|
||||
KEY_SL = (1 << 24),
|
||||
KEY_SR = (1 << 25),
|
||||
|
||||
// Pseudo-key for at least one finger on the touch screen
|
||||
KEY_TOUCH = (1 << 26),
|
||||
|
||||
// Buttons by orientation (for single Joy-Con), also works with Joy-Con pairs, Pro Controller
|
||||
KEY_JOYCON_RIGHT = (1 << 0),
|
||||
KEY_JOYCON_DOWN = (1 << 1),
|
||||
KEY_JOYCON_UP = (1 << 2),
|
||||
KEY_JOYCON_LEFT = (1 << 3),
|
||||
|
||||
// Generic catch-all directions, also works for single Joy-Con
|
||||
KEY_UP = KEY_DUP | KEY_LSTICK_UP | KEY_RSTICK_UP,
|
||||
KEY_DOWN = KEY_DDOWN | KEY_LSTICK_DOWN | KEY_RSTICK_DOWN,
|
||||
KEY_LEFT = KEY_DLEFT | KEY_LSTICK_LEFT | KEY_RSTICK_LEFT,
|
||||
KEY_RIGHT = KEY_DRIGHT | KEY_LSTICK_RIGHT | KEY_RSTICK_RIGHT,
|
||||
}
|
||||
|
||||
public enum HidControllerID
|
||||
{
|
||||
CONTROLLER_PLAYER_1 = 0,
|
||||
CONTROLLER_PLAYER_2 = 1,
|
||||
CONTROLLER_PLAYER_3 = 2,
|
||||
CONTROLLER_PLAYER_4 = 3,
|
||||
CONTROLLER_PLAYER_5 = 4,
|
||||
CONTROLLER_PLAYER_6 = 5,
|
||||
CONTROLLER_PLAYER_7 = 6,
|
||||
CONTROLLER_PLAYER_8 = 7,
|
||||
CONTROLLER_HANDHELD = 8,
|
||||
CONTROLLER_UNKNOWN = 9
|
||||
}
|
||||
|
||||
public enum HidControllerJoystick
|
||||
{
|
||||
Joystick_Left = 0,
|
||||
Joystick_Right = 1,
|
||||
Joystick_Num_Sticks = 2
|
||||
}
|
||||
|
||||
public enum HidControllerLayouts
|
||||
{
|
||||
Pro_Controller,
|
||||
Handheld_Joined,
|
||||
Joined,
|
||||
Left,
|
||||
Right,
|
||||
Main_No_Analog,
|
||||
Main
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum HidControllerConnectionState
|
||||
{
|
||||
Controller_State_Connected = (1 << 0),
|
||||
Controller_State_Wired = (1 << 1)
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum HidControllerType
|
||||
{
|
||||
ControllerType_ProController = (1 << 0),
|
||||
ControllerType_Handheld = (1 << 1),
|
||||
ControllerType_JoyconPair = (1 << 2),
|
||||
ControllerType_JoyconLeft = (1 << 3),
|
||||
ControllerType_JoyconRight = (1 << 4)
|
||||
}
|
||||
|
||||
public enum HidControllerColorDescription
|
||||
{
|
||||
ColorDesc_ColorsNonexistent = (1 << 1),
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x8)]
|
||||
public struct JoystickPosition
|
||||
{
|
||||
public int DX;
|
||||
public int DY;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
||||
public struct HidControllerMAC
|
||||
{
|
||||
public ulong Timestamp;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public byte[] MAC;
|
||||
public ulong Unknown;
|
||||
public ulong Timestamp_2;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x28)]
|
||||
public struct HidControllerHeader
|
||||
{
|
||||
public uint Type;
|
||||
public uint IsHalf;
|
||||
public uint SingleColorsDescriptor;
|
||||
public uint SingleColorBody;
|
||||
public uint SingleColorButtons;
|
||||
public uint SplitColorsDescriptor;
|
||||
public uint LeftColorBody;
|
||||
public uint LeftColorButtons;
|
||||
public uint RightColorBody;
|
||||
public uint RightColorButtons;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
||||
public struct HidControllerLayoutHeader
|
||||
{
|
||||
public ulong TimestampTicks;
|
||||
public ulong NumEntries;
|
||||
public ulong LatestEntry;
|
||||
public ulong MaxEntryIndex;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x30)]
|
||||
public struct HidControllerInputEntry
|
||||
{
|
||||
public ulong Timestamp;
|
||||
public ulong Timestamp_2;
|
||||
public ulong Buttons;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)HidControllerJoystick.Joystick_Num_Sticks)]
|
||||
public JoystickPosition[] Joysticks;
|
||||
public ulong ConnectionState;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x350)]
|
||||
public struct HidControllerLayout
|
||||
{
|
||||
public HidControllerLayoutHeader Header;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
|
||||
public HidControllerInputEntry[] Entries;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x5000)]
|
||||
public struct HidController
|
||||
{
|
||||
public HidControllerHeader Header;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
|
||||
public HidControllerLayout[] Layouts;
|
||||
/*
|
||||
pro_controller
|
||||
handheld_joined
|
||||
joined
|
||||
left
|
||||
right
|
||||
main_no_analog
|
||||
main
|
||||
*/
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x2A70)]
|
||||
public byte[] Unknown_1;
|
||||
public HidControllerMAC MacLeft;
|
||||
public HidControllerMAC MacRight;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xDF8)]
|
||||
public byte[] Unknown_2;
|
||||
}
|
||||
}
|
33
Ryujinx.Core/Hid/HidKeyboard.cs
Normal file
33
Ryujinx.Core/Hid/HidKeyboard.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Core
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
||||
public struct HidKeyboardHeader
|
||||
{
|
||||
public ulong TimestampTicks;
|
||||
public ulong NumEntries;
|
||||
public ulong LatestEntry;
|
||||
public ulong MaxEntryIndex;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x38)]
|
||||
public struct HidKeyboardEntry
|
||||
{
|
||||
public ulong Timestamp;
|
||||
public ulong Timestamp_2;
|
||||
public ulong Modifier;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public uint[] Keys;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x400)]
|
||||
public struct HidKeyboard
|
||||
{
|
||||
public HidKeyboardHeader Header;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
|
||||
public HidKeyboardEntry[] Entries;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x28)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
}
|
37
Ryujinx.Core/Hid/HidMouse.cs
Normal file
37
Ryujinx.Core/Hid/HidMouse.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Core
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
||||
public struct HidMouseHeader
|
||||
{
|
||||
public ulong TimestampTicks;
|
||||
public ulong NumEntries;
|
||||
public ulong LatestEntry;
|
||||
public ulong MaxEntryIndex;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x30)]
|
||||
public struct HidMouseEntry
|
||||
{
|
||||
public ulong Timestamp;
|
||||
public ulong Timestamp_2;
|
||||
public uint X;
|
||||
public uint Y;
|
||||
public uint VelocityX;
|
||||
public uint VelocityY;
|
||||
public uint ScrollVelocityX;
|
||||
public uint ScrollVelocityY;
|
||||
public ulong Buttons;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x400)]
|
||||
public struct HidMouse
|
||||
{
|
||||
public HidMouseHeader Header;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
|
||||
public HidMouseEntry[] Entries;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xB0)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
}
|
54
Ryujinx.Core/Hid/HidTouchScreen.cs
Normal file
54
Ryujinx.Core/Hid/HidTouchScreen.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Core
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x28)]
|
||||
public struct HidTouchScreenHeader
|
||||
{
|
||||
public ulong TimestampTicks;
|
||||
public ulong NumEntries;
|
||||
public ulong LatestEntry;
|
||||
public ulong MaxEntryIndex;
|
||||
public ulong Timestamp;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||
public struct HidTouchScreenEntryHeader
|
||||
{
|
||||
public ulong Timestamp;
|
||||
public ulong NumTouches;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x28)]
|
||||
public struct HidTouchScreenEntryTouch
|
||||
{
|
||||
public ulong Timestamp;
|
||||
public uint Padding;
|
||||
public uint TouchIndex;
|
||||
public uint X;
|
||||
public uint Y;
|
||||
public uint DiameterX;
|
||||
public uint DiameterY;
|
||||
public uint Angle;
|
||||
public uint Padding_2;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x298)]
|
||||
public struct HidTouchScreenEntry
|
||||
{
|
||||
public HidTouchScreenEntryHeader Header;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
|
||||
public HidTouchScreenEntryTouch[] Touches;
|
||||
public ulong Unknown;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x3000)]
|
||||
public struct HidTouchScreen
|
||||
{
|
||||
public HidTouchScreenHeader Header;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
|
||||
public HidTouchScreenEntry[] Entries;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3C0)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
}
|
81
Ryujinx.Core/Hid/HidUnknown.cs
Normal file
81
Ryujinx.Core/Hid/HidUnknown.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Core
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x400)]
|
||||
public struct HidSharedMemHeader
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x400)]
|
||||
public struct HidUnknownSection1
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x400)]
|
||||
public struct HidUnknownSection2
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x400)]
|
||||
public struct HidUnknownSection3
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x400)]
|
||||
public struct HidUnknownSection4
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x200)]
|
||||
public struct HidUnknownSection5
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x200)]
|
||||
public struct HidUnknownSection6
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x200)]
|
||||
public struct HidUnknownSection7
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x800)]
|
||||
public struct HidUnknownSection8
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x800)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x4000)]
|
||||
public struct HidControllerSerials
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x4600)]
|
||||
public struct HidUnknownSection9
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4600)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
}
|
66
Ryujinx.Core/Hid/JoyCon.cs
Normal file
66
Ryujinx.Core/Hid/JoyCon.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
namespace Ryujinx
|
||||
{
|
||||
/// <summary>
|
||||
/// Common RGB color hex codes for JoyCon coloring.
|
||||
/// </summary>
|
||||
public enum JoyConColor //Thanks to CTCaer
|
||||
{
|
||||
Body_Grey = 0x828282,
|
||||
Body_Neon_Blue = 0x0AB9E6,
|
||||
Body_Neon_Red = 0xFF3C28,
|
||||
Body_Neon_Yellow = 0xE6FF00,
|
||||
Body_Neon_Pink = 0xFF3278,
|
||||
Body_Neon_Green = 0x1EDC00,
|
||||
Body_Red = 0xE10F00,
|
||||
|
||||
Buttons_Grey = 0x0F0F0F,
|
||||
Buttons_Neon_Blue = 0x001E1E,
|
||||
Buttons_Neon_Red = 0x1E0A0A,
|
||||
Buttons_Neon_Yellow = 0x142800,
|
||||
Buttons_Neon_Pink = 0x28001E,
|
||||
Buttons_Neon_Green = 0x002800,
|
||||
Buttons_Red = 0x280A0A
|
||||
}
|
||||
|
||||
public struct JoyConLeft
|
||||
{
|
||||
public int StickUp;
|
||||
public int StickDown;
|
||||
public int StickLeft;
|
||||
public int StickRight;
|
||||
public int StickButton;
|
||||
public int DPadUp;
|
||||
public int DPadDown;
|
||||
public int DPadLeft;
|
||||
public int DPadRight;
|
||||
public int ButtonMinus;
|
||||
public int ButtonL;
|
||||
public int ButtonZL;
|
||||
public int ButtonSL;
|
||||
public int ButtonSR;
|
||||
}
|
||||
|
||||
public struct JoyConRight
|
||||
{
|
||||
public int StickUp;
|
||||
public int StickDown;
|
||||
public int StickLeft;
|
||||
public int StickRight;
|
||||
public int StickButton;
|
||||
public int ButtonA;
|
||||
public int ButtonB;
|
||||
public int ButtonX;
|
||||
public int ButtonY;
|
||||
public int ButtonPlus;
|
||||
public int ButtonR;
|
||||
public int ButtonZR;
|
||||
public int ButtonSL;
|
||||
public int ButtonSR;
|
||||
}
|
||||
|
||||
public struct JoyCon
|
||||
{
|
||||
public JoyConLeft Left;
|
||||
public JoyConRight Right;
|
||||
}
|
||||
}
|
78
Ryujinx.Core/Loaders/Compression/Lz4.cs
Normal file
78
Ryujinx.Core/Loaders/Compression/Lz4.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Core.Loaders.Compression
|
||||
{
|
||||
static class Lz4
|
||||
{
|
||||
public static byte[] Decompress(byte[] Cmp, int DecLength)
|
||||
{
|
||||
byte[] Dec = new byte[DecLength];
|
||||
|
||||
int CmpPos = 0;
|
||||
int DecPos = 0;
|
||||
|
||||
int GetLength(int Length)
|
||||
{
|
||||
byte Sum;
|
||||
|
||||
if (Length == 0xf)
|
||||
{
|
||||
do
|
||||
{
|
||||
Length += (Sum = Cmp[CmpPos++]);
|
||||
}
|
||||
while (Sum == 0xff);
|
||||
}
|
||||
|
||||
return Length;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
byte Token = Cmp[CmpPos++];
|
||||
|
||||
int EncCount = (Token >> 0) & 0xf;
|
||||
int LitCount = (Token >> 4) & 0xf;
|
||||
|
||||
//Copy literal chunck
|
||||
LitCount = GetLength(LitCount);
|
||||
|
||||
Buffer.BlockCopy(Cmp, CmpPos, Dec, DecPos, LitCount);
|
||||
|
||||
CmpPos += LitCount;
|
||||
DecPos += LitCount;
|
||||
|
||||
if (CmpPos >= Cmp.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//Copy compressed chunck
|
||||
int Back = Cmp[CmpPos++] << 0 |
|
||||
Cmp[CmpPos++] << 8;
|
||||
|
||||
EncCount = GetLength(EncCount) + 4;
|
||||
|
||||
int EncPos = DecPos - Back;
|
||||
|
||||
if (EncCount <= Back)
|
||||
{
|
||||
Buffer.BlockCopy(Dec, EncPos, Dec, DecPos, EncCount);
|
||||
|
||||
DecPos += EncCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (EncCount-- > 0)
|
||||
{
|
||||
Dec[DecPos++] = Dec[EncPos++];
|
||||
}
|
||||
}
|
||||
}
|
||||
while (CmpPos < Cmp.Length &&
|
||||
DecPos < Dec.Length);
|
||||
|
||||
return Dec;
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx.Core/Loaders/ElfDyn.cs
Normal file
15
Ryujinx.Core/Loaders/ElfDyn.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace Ryujinx.Core.Loaders
|
||||
{
|
||||
struct ElfDyn
|
||||
{
|
||||
public ElfDynTag Tag { get; private set; }
|
||||
|
||||
public long Value { get; private set; }
|
||||
|
||||
public ElfDyn(ElfDynTag Tag, long Value)
|
||||
{
|
||||
this.Tag = Tag;
|
||||
this.Value = Value;
|
||||
}
|
||||
}
|
||||
}
|
72
Ryujinx.Core/Loaders/ElfDynTag.cs
Normal file
72
Ryujinx.Core/Loaders/ElfDynTag.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
namespace Ryujinx.Core.Loaders
|
||||
{
|
||||
enum ElfDynTag
|
||||
{
|
||||
DT_NULL = 0,
|
||||
DT_NEEDED = 1,
|
||||
DT_PLTRELSZ = 2,
|
||||
DT_PLTGOT = 3,
|
||||
DT_HASH = 4,
|
||||
DT_STRTAB = 5,
|
||||
DT_SYMTAB = 6,
|
||||
DT_RELA = 7,
|
||||
DT_RELASZ = 8,
|
||||
DT_RELAENT = 9,
|
||||
DT_STRSZ = 10,
|
||||
DT_SYMENT = 11,
|
||||
DT_INIT = 12,
|
||||
DT_FINI = 13,
|
||||
DT_SONAME = 14,
|
||||
DT_RPATH = 15,
|
||||
DT_SYMBOLIC = 16,
|
||||
DT_REL = 17,
|
||||
DT_RELSZ = 18,
|
||||
DT_RELENT = 19,
|
||||
DT_PLTREL = 20,
|
||||
DT_DEBUG = 21,
|
||||
DT_TEXTREL = 22,
|
||||
DT_JMPREL = 23,
|
||||
DT_BIND_NOW = 24,
|
||||
DT_INIT_ARRAY = 25,
|
||||
DT_FINI_ARRAY = 26,
|
||||
DT_INIT_ARRAYSZ = 27,
|
||||
DT_FINI_ARRAYSZ = 28,
|
||||
DT_RUNPATH = 29,
|
||||
DT_FLAGS = 30,
|
||||
DT_ENCODING = 32,
|
||||
DT_PREINIT_ARRAY = 32,
|
||||
DT_PREINIT_ARRAYSZ = 33,
|
||||
DT_GNU_PRELINKED = 0x6ffffdf5,
|
||||
DT_GNU_CONFLICTSZ = 0x6ffffdf6,
|
||||
DT_GNU_LIBLISTSZ = 0x6ffffdf7,
|
||||
DT_CHECKSUM = 0x6ffffdf8,
|
||||
DT_PLTPADSZ = 0x6ffffdf9,
|
||||
DT_MOVEENT = 0x6ffffdfa,
|
||||
DT_MOVESZ = 0x6ffffdfb,
|
||||
DT_FEATURE_1 = 0x6ffffdfc,
|
||||
DT_POSFLAG_1 = 0x6ffffdfd,
|
||||
DT_SYMINSZ = 0x6ffffdfe,
|
||||
DT_SYMINENT = 0x6ffffdff,
|
||||
DT_GNU_HASH = 0x6ffffef5,
|
||||
DT_TLSDESC_PLT = 0x6ffffef6,
|
||||
DT_TLSDESC_GOT = 0x6ffffef7,
|
||||
DT_GNU_CONFLICT = 0x6ffffef8,
|
||||
DT_GNU_LIBLIST = 0x6ffffef9,
|
||||
DT_CONFIG = 0x6ffffefa,
|
||||
DT_DEPAUDIT = 0x6ffffefb,
|
||||
DT_AUDIT = 0x6ffffefc,
|
||||
DT_PLTPAD = 0x6ffffefd,
|
||||
DT_MOVETAB = 0x6ffffefe,
|
||||
DT_SYMINFO = 0x6ffffeff,
|
||||
DT_VERSYM = 0x6ffffff0,
|
||||
DT_RELACOUNT = 0x6ffffff9,
|
||||
DT_RELCOUNT = 0x6ffffffa,
|
||||
DT_FLAGS_1 = 0x6ffffffb,
|
||||
DT_VERDEF = 0x6ffffffc,
|
||||
DT_VERDEFNUM = 0x6ffffffd,
|
||||
DT_VERNEED = 0x6ffffffe,
|
||||
DT_VERNEEDNUM = 0x6fffffff,
|
||||
DT_AUXILIARY = 0x7ffffffd,
|
||||
DT_FILTER = 0x7fffffff
|
||||
}
|
||||
}
|
19
Ryujinx.Core/Loaders/ElfRel.cs
Normal file
19
Ryujinx.Core/Loaders/ElfRel.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace Ryujinx.Core.Loaders
|
||||
{
|
||||
struct ElfRel
|
||||
{
|
||||
public long Offset { get; private set; }
|
||||
public long Addend { get; private set; }
|
||||
|
||||
public ElfSym Symbol { get; private set; }
|
||||
public ElfRelType Type { get; private set; }
|
||||
|
||||
public ElfRel(long Offset, long Addend, ElfSym Symbol, ElfRelType Type)
|
||||
{
|
||||
this.Offset = Offset;
|
||||
this.Addend = Addend;
|
||||
this.Symbol = Symbol;
|
||||
this.Type = Type;
|
||||
}
|
||||
}
|
||||
}
|
128
Ryujinx.Core/Loaders/ElfRelType.cs
Normal file
128
Ryujinx.Core/Loaders/ElfRelType.cs
Normal file
|
@ -0,0 +1,128 @@
|
|||
namespace Ryujinx.Core.Loaders
|
||||
{
|
||||
enum ElfRelType
|
||||
{
|
||||
R_AARCH64_NONE = 0,
|
||||
R_AARCH64_ABS64 = 257,
|
||||
R_AARCH64_ABS32 = 258,
|
||||
R_AARCH64_ABS16 = 259,
|
||||
R_AARCH64_PREL64 = 260,
|
||||
R_AARCH64_PREL32 = 261,
|
||||
R_AARCH64_PREL16 = 262,
|
||||
R_AARCH64_MOVW_UABS_G0 = 263,
|
||||
R_AARCH64_MOVW_UABS_G0_NC = 264,
|
||||
R_AARCH64_MOVW_UABS_G1 = 265,
|
||||
R_AARCH64_MOVW_UABS_G1_NC = 266,
|
||||
R_AARCH64_MOVW_UABS_G2 = 267,
|
||||
R_AARCH64_MOVW_UABS_G2_NC = 268,
|
||||
R_AARCH64_MOVW_UABS_G3 = 269,
|
||||
R_AARCH64_MOVW_SABS_G0 = 270,
|
||||
R_AARCH64_MOVW_SABS_G1 = 271,
|
||||
R_AARCH64_MOVW_SABS_G2 = 272,
|
||||
R_AARCH64_LD_PREL_LO19 = 273,
|
||||
R_AARCH64_ADR_PREL_LO21 = 274,
|
||||
R_AARCH64_ADR_PREL_PG_HI21 = 275,
|
||||
R_AARCH64_ADR_PREL_PG_HI21_NC = 276,
|
||||
R_AARCH64_ADD_ABS_LO12_NC = 277,
|
||||
R_AARCH64_LDST8_ABS_LO12_NC = 278,
|
||||
R_AARCH64_TSTBR14 = 279,
|
||||
R_AARCH64_CONDBR19 = 280,
|
||||
R_AARCH64_JUMP26 = 282,
|
||||
R_AARCH64_CALL26 = 283,
|
||||
R_AARCH64_LDST16_ABS_LO12_NC = 284,
|
||||
R_AARCH64_LDST32_ABS_LO12_NC = 285,
|
||||
R_AARCH64_LDST64_ABS_LO12_NC = 286,
|
||||
R_AARCH64_MOVW_PREL_G0 = 287,
|
||||
R_AARCH64_MOVW_PREL_G0_NC = 288,
|
||||
R_AARCH64_MOVW_PREL_G1 = 289,
|
||||
R_AARCH64_MOVW_PREL_G1_NC = 290,
|
||||
R_AARCH64_MOVW_PREL_G2 = 291,
|
||||
R_AARCH64_MOVW_PREL_G2_NC = 292,
|
||||
R_AARCH64_MOVW_PREL_G3 = 293,
|
||||
R_AARCH64_LDST128_ABS_LO12_NC = 299,
|
||||
R_AARCH64_MOVW_GOTOFF_G0 = 300,
|
||||
R_AARCH64_MOVW_GOTOFF_G0_NC = 301,
|
||||
R_AARCH64_MOVW_GOTOFF_G1 = 302,
|
||||
R_AARCH64_MOVW_GOTOFF_G1_NC = 303,
|
||||
R_AARCH64_MOVW_GOTOFF_G2 = 304,
|
||||
R_AARCH64_MOVW_GOTOFF_G2_NC = 305,
|
||||
R_AARCH64_MOVW_GOTOFF_G3 = 306,
|
||||
R_AARCH64_GOTREL64 = 307,
|
||||
R_AARCH64_GOTREL32 = 308,
|
||||
R_AARCH64_GOT_LD_PREL19 = 309,
|
||||
R_AARCH64_LD64_GOTOFF_LO15 = 310,
|
||||
R_AARCH64_ADR_GOT_PAGE = 311,
|
||||
R_AARCH64_LD64_GOT_LO12_NC = 312,
|
||||
R_AARCH64_LD64_GOTPAGE_LO15 = 313,
|
||||
R_AARCH64_TLSGD_ADR_PREL21 = 512,
|
||||
R_AARCH64_TLSGD_ADR_PAGE21 = 513,
|
||||
R_AARCH64_TLSGD_ADD_LO12_NC = 514,
|
||||
R_AARCH64_TLSGD_MOVW_G1 = 515,
|
||||
R_AARCH64_TLSGD_MOVW_G0_NC = 516,
|
||||
R_AARCH64_TLSLD_ADR_PREL21 = 517,
|
||||
R_AARCH64_TLSLD_ADR_PAGE21 = 518,
|
||||
R_AARCH64_TLSLD_ADD_LO12_NC = 519,
|
||||
R_AARCH64_TLSLD_MOVW_G1 = 520,
|
||||
R_AARCH64_TLSLD_MOVW_G0_NC = 521,
|
||||
R_AARCH64_TLSLD_LD_PREL19 = 522,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G2 = 523,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G1 = 524,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC = 525,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G0 = 526,
|
||||
R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC = 527,
|
||||
R_AARCH64_TLSLD_ADD_DTPREL_HI12 = 528,
|
||||
R_AARCH64_TLSLD_ADD_DTPREL_LO12 = 529,
|
||||
R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC = 530,
|
||||
R_AARCH64_TLSLD_LDST8_DTPREL_LO12 = 531,
|
||||
R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC = 532,
|
||||
R_AARCH64_TLSLD_LDST16_DTPREL_LO12 = 533,
|
||||
R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC = 534,
|
||||
R_AARCH64_TLSLD_LDST32_DTPREL_LO12 = 535,
|
||||
R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC = 536,
|
||||
R_AARCH64_TLSLD_LDST64_DTPREL_LO12 = 537,
|
||||
R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC = 538,
|
||||
R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 539,
|
||||
R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 540,
|
||||
R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 541,
|
||||
R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 542,
|
||||
R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 543,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G2 = 544,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G1 = 545,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 546,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G0 = 547,
|
||||
R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 548,
|
||||
R_AARCH64_TLSLE_ADD_TPREL_HI12 = 549,
|
||||
R_AARCH64_TLSLE_ADD_TPREL_LO12 = 550,
|
||||
R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 551,
|
||||
R_AARCH64_TLSLE_LDST8_TPREL_LO12 = 552,
|
||||
R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC = 553,
|
||||
R_AARCH64_TLSLE_LDST16_TPREL_LO12 = 554,
|
||||
R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC = 555,
|
||||
R_AARCH64_TLSLE_LDST32_TPREL_LO12 = 556,
|
||||
R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC = 557,
|
||||
R_AARCH64_TLSLE_LDST64_TPREL_LO12 = 558,
|
||||
R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC = 559,
|
||||
R_AARCH64_TLSDESC_LD_PREL19 = 560,
|
||||
R_AARCH64_TLSDESC_ADR_PREL21 = 561,
|
||||
R_AARCH64_TLSDESC_ADR_PAGE21 = 562,
|
||||
R_AARCH64_TLSDESC_LD64_LO12 = 563,
|
||||
R_AARCH64_TLSDESC_ADD_LO12 = 564,
|
||||
R_AARCH64_TLSDESC_OFF_G1 = 565,
|
||||
R_AARCH64_TLSDESC_OFF_G0_NC = 566,
|
||||
R_AARCH64_TLSDESC_LDR = 567,
|
||||
R_AARCH64_TLSDESC_ADD = 568,
|
||||
R_AARCH64_TLSDESC_CALL = 569,
|
||||
R_AARCH64_TLSLE_LDST128_TPREL_LO12 = 570,
|
||||
R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC = 571,
|
||||
R_AARCH64_TLSLD_LDST128_DTPREL_LO12 = 572,
|
||||
R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC = 573,
|
||||
R_AARCH64_COPY = 1024,
|
||||
R_AARCH64_GLOB_DAT = 1025,
|
||||
R_AARCH64_JUMP_SLOT = 1026,
|
||||
R_AARCH64_RELATIVE = 1027,
|
||||
R_AARCH64_TLS_DTPMOD64 = 1028,
|
||||
R_AARCH64_TLS_DTPREL64 = 1029,
|
||||
R_AARCH64_TLS_TPREL64 = 1030,
|
||||
R_AARCH64_TLSDESC = 1031
|
||||
}
|
||||
}
|
43
Ryujinx.Core/Loaders/ElfSym.cs
Normal file
43
Ryujinx.Core/Loaders/ElfSym.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
namespace Ryujinx.Core.Loaders
|
||||
{
|
||||
struct ElfSym
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public ElfSymType Type { get; private set; }
|
||||
public ElfSymBinding Binding { get; private set; }
|
||||
public ElfSymVisibility Visibility { get; private set; }
|
||||
|
||||
public bool IsFuncOrObject =>
|
||||
Type == ElfSymType.STT_FUNC ||
|
||||
Type == ElfSymType.STT_OBJECT;
|
||||
|
||||
public bool IsGlobalOrWeak =>
|
||||
Binding == ElfSymBinding.STB_GLOBAL ||
|
||||
Binding == ElfSymBinding.STB_WEAK;
|
||||
|
||||
public int SHIdx { get; private set; }
|
||||
public long ValueAbs { get; private set; }
|
||||
public long Value { get; private set; }
|
||||
public long Size { get; private set; }
|
||||
|
||||
public ElfSym(
|
||||
string Name,
|
||||
int Info,
|
||||
int Other,
|
||||
int SHIdx,
|
||||
long ImageBase,
|
||||
long Value,
|
||||
long Size)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Type = (ElfSymType)(Info & 0xf);
|
||||
this.Binding = (ElfSymBinding)(Info >> 4);
|
||||
this.Visibility = (ElfSymVisibility)Other;
|
||||
this.SHIdx = SHIdx;
|
||||
this.ValueAbs = Value + ImageBase;
|
||||
this.Value = Value;
|
||||
this.Size = Size;
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Core/Loaders/ElfSymBinding.cs
Normal file
9
Ryujinx.Core/Loaders/ElfSymBinding.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Core.Loaders
|
||||
{
|
||||
enum ElfSymBinding
|
||||
{
|
||||
STB_LOCAL = 0,
|
||||
STB_GLOBAL = 1,
|
||||
STB_WEAK = 2
|
||||
}
|
||||
}
|
13
Ryujinx.Core/Loaders/ElfSymType.cs
Normal file
13
Ryujinx.Core/Loaders/ElfSymType.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Ryujinx.Core.Loaders
|
||||
{
|
||||
enum ElfSymType
|
||||
{
|
||||
STT_NOTYPE = 0,
|
||||
STT_OBJECT = 1,
|
||||
STT_FUNC = 2,
|
||||
STT_SECTION = 3,
|
||||
STT_FILE = 4,
|
||||
STT_COMMON = 5,
|
||||
STT_TLS = 6
|
||||
}
|
||||
}
|
10
Ryujinx.Core/Loaders/ElfSymVisibility.cs
Normal file
10
Ryujinx.Core/Loaders/ElfSymVisibility.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Core.Loaders
|
||||
{
|
||||
enum ElfSymVisibility
|
||||
{
|
||||
STV_DEFAULT = 0,
|
||||
STV_INTERNAL = 1,
|
||||
STV_HIDDEN = 2,
|
||||
STV_PROTECTED = 3
|
||||
}
|
||||
}
|
149
Ryujinx.Core/Loaders/Executable.cs
Normal file
149
Ryujinx.Core/Loaders/Executable.cs
Normal file
|
@ -0,0 +1,149 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.Loaders.Executables;
|
||||
using Ryujinx.Core.OsHle;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.Loaders
|
||||
{
|
||||
class Executable
|
||||
{
|
||||
private AMemory Memory;
|
||||
|
||||
private ElfDyn[] Dynamic;
|
||||
|
||||
public long ImageBase { get; private set; }
|
||||
public long ImageEnd { get; private set; }
|
||||
|
||||
public Executable(IExecutable Exe, AMemory Memory, long ImageBase)
|
||||
{
|
||||
this.Memory = Memory;
|
||||
this.ImageBase = ImageBase;
|
||||
this.ImageEnd = ImageBase;
|
||||
|
||||
WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX);
|
||||
WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.Normal, AMemoryPerm.Read);
|
||||
WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.Normal, AMemoryPerm.RW);
|
||||
|
||||
if (Exe.Mod0Offset == 0)
|
||||
{
|
||||
MapBss(ImageBase + Exe.DataOffset + Exe.Data.Count, Exe.BssSize);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
long Mod0Offset = ImageBase + Exe.Mod0Offset;
|
||||
|
||||
int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
|
||||
long DynamicOffset = Memory.ReadInt32(Mod0Offset + 0x4) + Mod0Offset;
|
||||
long BssStartOffset = Memory.ReadInt32(Mod0Offset + 0x8) + Mod0Offset;
|
||||
long BssEndOffset = Memory.ReadInt32(Mod0Offset + 0xc) + Mod0Offset;
|
||||
long EhHdrStartOffset = Memory.ReadInt32(Mod0Offset + 0x10) + Mod0Offset;
|
||||
long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
|
||||
long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
|
||||
|
||||
MapBss(BssStartOffset, BssEndOffset - BssStartOffset);
|
||||
|
||||
ImageEnd = BssEndOffset;
|
||||
|
||||
List<ElfDyn> Dynamic = new List<ElfDyn>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
long TagVal = Memory.ReadInt64(DynamicOffset + 0);
|
||||
long Value = Memory.ReadInt64(DynamicOffset + 8);
|
||||
|
||||
DynamicOffset += 0x10;
|
||||
|
||||
ElfDynTag Tag = (ElfDynTag)TagVal;
|
||||
|
||||
if (Tag == ElfDynTag.DT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Dynamic.Add(new ElfDyn(Tag, Value));
|
||||
}
|
||||
|
||||
this.Dynamic = Dynamic.ToArray();
|
||||
}
|
||||
|
||||
private void WriteData(
|
||||
long Position,
|
||||
IList<byte> Data,
|
||||
MemoryType Type,
|
||||
AMemoryPerm Perm)
|
||||
{
|
||||
Memory.Manager.MapPhys(Position, Data.Count, (int)Type, AMemoryPerm.Write);
|
||||
|
||||
for (int Index = 0; Index < Data.Count; Index++)
|
||||
{
|
||||
Memory.WriteByte(Position + Index, Data[Index]);
|
||||
}
|
||||
|
||||
Memory.Manager.Reprotect(Position, Data.Count, Perm);
|
||||
}
|
||||
|
||||
private void MapBss(long Position, long Size)
|
||||
{
|
||||
Memory.Manager.MapPhys(Position, Size, (int)MemoryType.Normal, AMemoryPerm.RW);
|
||||
}
|
||||
|
||||
private ElfRel GetRelocation(long Position)
|
||||
{
|
||||
long Offset = Memory.ReadInt64(Position + 0);
|
||||
long Info = Memory.ReadInt64(Position + 8);
|
||||
long Addend = Memory.ReadInt64(Position + 16);
|
||||
|
||||
int RelType = (int)(Info >> 0);
|
||||
int SymIdx = (int)(Info >> 32);
|
||||
|
||||
ElfSym Symbol = GetSymbol(SymIdx);
|
||||
|
||||
return new ElfRel(Offset, Addend, Symbol, (ElfRelType)RelType);
|
||||
}
|
||||
|
||||
private ElfSym GetSymbol(int Index)
|
||||
{
|
||||
long StrTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_STRTAB);
|
||||
long SymTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_SYMTAB);
|
||||
|
||||
long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT);
|
||||
|
||||
long Position = SymTblAddr + Index * SymEntSize;
|
||||
|
||||
return GetSymbol(Position, StrTblAddr);
|
||||
}
|
||||
|
||||
private ElfSym GetSymbol(long Position, long StrTblAddr)
|
||||
{
|
||||
int NameIndex = Memory.ReadInt32(Position + 0);
|
||||
int Info = Memory.ReadByte(Position + 4);
|
||||
int Other = Memory.ReadByte(Position + 5);
|
||||
int SHIdx = Memory.ReadInt16(Position + 6);
|
||||
long Value = Memory.ReadInt64(Position + 8);
|
||||
long Size = Memory.ReadInt64(Position + 16);
|
||||
|
||||
string Name = string.Empty;
|
||||
|
||||
for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;)
|
||||
{
|
||||
Name += (char)Chr;
|
||||
}
|
||||
|
||||
return new ElfSym(Name, Info, Other, SHIdx, ImageBase, Value, Size);
|
||||
}
|
||||
|
||||
private long GetFirstValue(ElfDynTag Tag)
|
||||
{
|
||||
foreach (ElfDyn Entry in Dynamic)
|
||||
{
|
||||
if (Entry.Tag == Tag)
|
||||
{
|
||||
return Entry.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Core/Loaders/Executables/IExecutable.cs
Normal file
17
Ryujinx.Core/Loaders/Executables/IExecutable.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ryujinx.Core.Loaders.Executables
|
||||
{
|
||||
public interface IExecutable
|
||||
{
|
||||
ReadOnlyCollection<byte> Text { get; }
|
||||
ReadOnlyCollection<byte> RO { get; }
|
||||
ReadOnlyCollection<byte> Data { get; }
|
||||
|
||||
int Mod0Offset { get; }
|
||||
int TextOffset { get; }
|
||||
int ROOffset { get; }
|
||||
int DataOffset { get; }
|
||||
int BssSize { get; }
|
||||
}
|
||||
}
|
62
Ryujinx.Core/Loaders/Executables/Nro.cs
Normal file
62
Ryujinx.Core/Loaders/Executables/Nro.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.Loaders.Executables
|
||||
{
|
||||
class Nro : IExecutable
|
||||
{
|
||||
private byte[] m_Text;
|
||||
private byte[] m_RO;
|
||||
private byte[] m_Data;
|
||||
|
||||
public ReadOnlyCollection<byte> Text => Array.AsReadOnly(m_Text);
|
||||
public ReadOnlyCollection<byte> RO => Array.AsReadOnly(m_RO);
|
||||
public ReadOnlyCollection<byte> Data => Array.AsReadOnly(m_Data);
|
||||
|
||||
public int Mod0Offset { get; private set; }
|
||||
public int TextOffset { get; private set; }
|
||||
public int ROOffset { get; private set; }
|
||||
public int DataOffset { get; private set; }
|
||||
public int BssSize { get; private set; }
|
||||
|
||||
public Nro(Stream Input)
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(Input);
|
||||
|
||||
Input.Seek(4, SeekOrigin.Begin);
|
||||
|
||||
int Mod0Offset = Reader.ReadInt32();
|
||||
int Padding8 = Reader.ReadInt32();
|
||||
int Paddingc = Reader.ReadInt32();
|
||||
int NroMagic = Reader.ReadInt32();
|
||||
int Unknown14 = Reader.ReadInt32();
|
||||
int FileSize = Reader.ReadInt32();
|
||||
int Unknown1c = Reader.ReadInt32();
|
||||
int TextOffset = Reader.ReadInt32();
|
||||
int TextSize = Reader.ReadInt32();
|
||||
int ROOffset = Reader.ReadInt32();
|
||||
int ROSize = Reader.ReadInt32();
|
||||
int DataOffset = Reader.ReadInt32();
|
||||
int DataSize = Reader.ReadInt32();
|
||||
int BssSize = Reader.ReadInt32();
|
||||
|
||||
this.Mod0Offset = Mod0Offset;
|
||||
this.TextOffset = TextOffset;
|
||||
this.ROOffset = ROOffset;
|
||||
this.DataOffset = DataOffset;
|
||||
this.BssSize = BssSize;
|
||||
|
||||
byte[] Read(long Position, int Size)
|
||||
{
|
||||
Input.Seek(Position, SeekOrigin.Begin);
|
||||
|
||||
return Reader.ReadBytes(Size);
|
||||
}
|
||||
|
||||
m_Text = Read(TextOffset, TextSize);
|
||||
m_RO = Read(ROOffset, ROSize);
|
||||
m_Data = Read(DataOffset, DataSize);
|
||||
}
|
||||
}
|
||||
}
|
122
Ryujinx.Core/Loaders/Executables/Nso.cs
Normal file
122
Ryujinx.Core/Loaders/Executables/Nso.cs
Normal file
|
@ -0,0 +1,122 @@
|
|||
using Ryujinx.Core.Loaders.Compression;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.Loaders.Executables
|
||||
{
|
||||
class Nso : IExecutable
|
||||
{
|
||||
private byte[] m_Text;
|
||||
private byte[] m_RO;
|
||||
private byte[] m_Data;
|
||||
|
||||
public ReadOnlyCollection<byte> Text => Array.AsReadOnly(m_Text);
|
||||
public ReadOnlyCollection<byte> RO => Array.AsReadOnly(m_RO);
|
||||
public ReadOnlyCollection<byte> Data => Array.AsReadOnly(m_Data);
|
||||
|
||||
public int Mod0Offset { get; private set; }
|
||||
public int TextOffset { get; private set; }
|
||||
public int ROOffset { get; private set; }
|
||||
public int DataOffset { get; private set; }
|
||||
public int BssSize { get; private set; }
|
||||
|
||||
[Flags]
|
||||
private enum NsoFlags
|
||||
{
|
||||
IsTextCompressed = 1 << 0,
|
||||
IsROCompressed = 1 << 1,
|
||||
IsDataCompressed = 1 << 2,
|
||||
HasTextHash = 1 << 3,
|
||||
HasROHash = 1 << 4,
|
||||
HasDataHash = 1 << 5
|
||||
}
|
||||
|
||||
public Nso(Stream Input)
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(Input);
|
||||
|
||||
Input.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
int NsoMagic = Reader.ReadInt32();
|
||||
int Version = Reader.ReadInt32();
|
||||
int Reserved = Reader.ReadInt32();
|
||||
int FlagsMsk = Reader.ReadInt32();
|
||||
int TextOffset = Reader.ReadInt32();
|
||||
int TextMemOffset = Reader.ReadInt32();
|
||||
int TextDecSize = Reader.ReadInt32();
|
||||
int ModNameOffset = Reader.ReadInt32();
|
||||
int ROOffset = Reader.ReadInt32();
|
||||
int ROMemOffset = Reader.ReadInt32();
|
||||
int RODecSize = Reader.ReadInt32();
|
||||
int ModNameSize = Reader.ReadInt32();
|
||||
int DataOffset = Reader.ReadInt32();
|
||||
int DataMemOffset = Reader.ReadInt32();
|
||||
int DataDecSize = Reader.ReadInt32();
|
||||
int BssSize = Reader.ReadInt32();
|
||||
|
||||
byte[] BuildId = Reader.ReadBytes(0x20);
|
||||
|
||||
int TextSize = Reader.ReadInt32();
|
||||
int ROSize = Reader.ReadInt32();
|
||||
int DataSize = Reader.ReadInt32();
|
||||
|
||||
Input.Seek(0x24, SeekOrigin.Current);
|
||||
|
||||
int DynStrOffset = Reader.ReadInt32();
|
||||
int DynStrSize = Reader.ReadInt32();
|
||||
int DynSymOffset = Reader.ReadInt32();
|
||||
int DynSymSize = Reader.ReadInt32();
|
||||
|
||||
byte[] TextHash = Reader.ReadBytes(0x20);
|
||||
byte[] ROHash = Reader.ReadBytes(0x20);
|
||||
byte[] DataHash = Reader.ReadBytes(0x20);
|
||||
|
||||
NsoFlags Flags = (NsoFlags)FlagsMsk;
|
||||
|
||||
this.TextOffset = TextMemOffset;
|
||||
this.ROOffset = ROMemOffset;
|
||||
this.DataOffset = DataMemOffset;
|
||||
this.BssSize = BssSize;
|
||||
|
||||
//Text segment
|
||||
Input.Seek(TextOffset, SeekOrigin.Begin);
|
||||
|
||||
m_Text = Reader.ReadBytes(TextSize);
|
||||
|
||||
if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true)
|
||||
{
|
||||
m_Text = Lz4.Decompress(m_Text, TextDecSize);
|
||||
}
|
||||
|
||||
//Read-only data segment
|
||||
Input.Seek(ROOffset, SeekOrigin.Begin);
|
||||
|
||||
m_RO = Reader.ReadBytes(ROSize);
|
||||
|
||||
if (Flags.HasFlag(NsoFlags.IsROCompressed) || true)
|
||||
{
|
||||
m_RO = Lz4.Decompress(m_RO, RODecSize);
|
||||
}
|
||||
|
||||
//Data segment
|
||||
Input.Seek(DataOffset, SeekOrigin.Begin);
|
||||
|
||||
m_Data = Reader.ReadBytes(DataSize);
|
||||
|
||||
if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true)
|
||||
{
|
||||
m_Data = Lz4.Decompress(m_Data, DataDecSize);
|
||||
}
|
||||
|
||||
using (MemoryStream Text = new MemoryStream(m_Text))
|
||||
{
|
||||
BinaryReader TextReader = new BinaryReader(Text);
|
||||
|
||||
Text.Seek(4, SeekOrigin.Begin);
|
||||
|
||||
Mod0Offset = TextReader.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
132
Ryujinx.Core/Logging.cs
Normal file
132
Ryujinx.Core/Logging.cs
Normal file
|
@ -0,0 +1,132 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core
|
||||
{
|
||||
public static class Logging
|
||||
{
|
||||
private static Stopwatch ExecutionTime = new Stopwatch();
|
||||
private const string LogFileName = "Ryujinx.log";
|
||||
|
||||
public static bool EnableInfo = Config.LoggingEnableInfo;
|
||||
public static bool EnableTrace = Config.LoggingEnableTrace;
|
||||
public static bool EnableDebug = Config.LoggingEnableDebug;
|
||||
public static bool EnableWarn = Config.LoggingEnableWarn;
|
||||
public static bool EnableError = Config.LoggingEnableError;
|
||||
public static bool EnableFatal = Config.LoggingEnableFatal;
|
||||
public static bool EnableLogFile = Config.LoggingEnableLogFile;
|
||||
|
||||
static Logging()
|
||||
{
|
||||
ExecutionTime.Start();
|
||||
|
||||
if (File.Exists(LogFileName)) File.Delete(LogFileName);
|
||||
}
|
||||
|
||||
public static string GetExecutionTime()
|
||||
{
|
||||
return ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
|
||||
}
|
||||
|
||||
private static string WhoCalledMe()
|
||||
{
|
||||
return new StackTrace().GetFrame(2).GetMethod().Name;
|
||||
}
|
||||
|
||||
private static void LogFile(string Message)
|
||||
{
|
||||
if (EnableLogFile)
|
||||
{
|
||||
using (StreamWriter Writer = File.AppendText(LogFileName))
|
||||
{
|
||||
Writer.WriteLine(Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Info(string Message)
|
||||
{
|
||||
if (EnableInfo)
|
||||
{
|
||||
string Text = $"{GetExecutionTime()} | INFO > {Message}";
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
||||
Console.ResetColor();
|
||||
|
||||
LogFile(Text);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Trace(string Message)
|
||||
{
|
||||
if (EnableTrace)
|
||||
{
|
||||
string Text = $"{GetExecutionTime()} | TRACE > {WhoCalledMe()} - {Message}";
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
||||
Console.ResetColor();
|
||||
|
||||
LogFile(Text);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Debug(string Message)
|
||||
{
|
||||
if (EnableDebug)
|
||||
{
|
||||
string Text = $"{GetExecutionTime()} | DEBUG > {WhoCalledMe()} - {Message}";
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
||||
Console.ResetColor();
|
||||
|
||||
LogFile(Text);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Warn(string Message)
|
||||
{
|
||||
if (EnableWarn)
|
||||
{
|
||||
string Text = $"{GetExecutionTime()} | WARN > {WhoCalledMe()} - {Message}";
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
||||
Console.ResetColor();
|
||||
|
||||
LogFile(Text);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Error(string Message)
|
||||
{
|
||||
if (EnableError)
|
||||
{
|
||||
string Text = $"{GetExecutionTime()} | ERROR > {WhoCalledMe()} - {Message}";
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
||||
Console.ResetColor();
|
||||
|
||||
LogFile(Text);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fatal(string Message)
|
||||
{
|
||||
if (EnableFatal)
|
||||
{
|
||||
string Text = $"{GetExecutionTime()} | FATAL > {WhoCalledMe()} - {Message}";
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Magenta;
|
||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
||||
Console.ResetColor();
|
||||
|
||||
LogFile(Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
138
Ryujinx.Core/OsHle/CondVar.cs
Normal file
138
Ryujinx.Core/OsHle/CondVar.cs
Normal file
|
@ -0,0 +1,138 @@
|
|||
using Ryujinx.Core.OsHle.Handles;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
public class CondVar
|
||||
{
|
||||
private Process Process;
|
||||
|
||||
private long CondVarAddress;
|
||||
private long Timeout;
|
||||
|
||||
private bool OwnsCondVarValue;
|
||||
|
||||
private List<HThread> WaitingThreads;
|
||||
|
||||
public CondVar(Process Process, long CondVarAddress, long Timeout)
|
||||
{
|
||||
this.Process = Process;
|
||||
this.CondVarAddress = CondVarAddress;
|
||||
this.Timeout = Timeout;
|
||||
|
||||
WaitingThreads = new List<HThread>();
|
||||
}
|
||||
|
||||
public void WaitForSignal(HThread Thread)
|
||||
{
|
||||
int Count = Process.Memory.ReadInt32(CondVarAddress);
|
||||
|
||||
if (Count <= 0)
|
||||
{
|
||||
lock (WaitingThreads)
|
||||
{
|
||||
WaitingThreads.Add(Thread);
|
||||
}
|
||||
|
||||
if (Timeout == -1)
|
||||
{
|
||||
Process.Scheduler.WaitForSignal(Thread);
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000));
|
||||
|
||||
lock (WaitingThreads)
|
||||
{
|
||||
WaitingThreads.Remove(Thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AcquireCondVarValue();
|
||||
|
||||
Count = Process.Memory.ReadInt32(CondVarAddress);
|
||||
|
||||
if (Count > 0)
|
||||
{
|
||||
Process.Memory.WriteInt32(CondVarAddress, Count - 1);
|
||||
}
|
||||
|
||||
ReleaseCondVarValue();
|
||||
}
|
||||
|
||||
public void SetSignal(HThread Thread, int Count)
|
||||
{
|
||||
lock (WaitingThreads)
|
||||
{
|
||||
if (Count == -1)
|
||||
{
|
||||
Process.Scheduler.Signal(WaitingThreads.ToArray());
|
||||
|
||||
AcquireCondVarValue();
|
||||
|
||||
Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count);
|
||||
|
||||
ReleaseCondVarValue();
|
||||
|
||||
WaitingThreads.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WaitingThreads.Count > 0)
|
||||
{
|
||||
int HighestPriority = WaitingThreads[0].Priority;
|
||||
int HighestPrioIndex = 0;
|
||||
|
||||
for (int Index = 1; Index < WaitingThreads.Count; Index++)
|
||||
{
|
||||
if (HighestPriority > WaitingThreads[Index].Priority)
|
||||
{
|
||||
HighestPriority = WaitingThreads[Index].Priority;
|
||||
|
||||
HighestPrioIndex = Index;
|
||||
}
|
||||
}
|
||||
|
||||
Process.Scheduler.Signal(WaitingThreads[HighestPrioIndex]);
|
||||
|
||||
WaitingThreads.RemoveAt(HighestPrioIndex);
|
||||
}
|
||||
|
||||
AcquireCondVarValue();
|
||||
|
||||
Process.Memory.WriteInt32(CondVarAddress, Count);
|
||||
|
||||
ReleaseCondVarValue();
|
||||
}
|
||||
}
|
||||
|
||||
Process.Scheduler.Suspend(Thread.ProcessorId);
|
||||
Process.Scheduler.Resume(Thread);
|
||||
}
|
||||
|
||||
private void AcquireCondVarValue()
|
||||
{
|
||||
if (!OwnsCondVarValue)
|
||||
{
|
||||
while (!Process.Memory.AcquireAddress(CondVarAddress))
|
||||
{
|
||||
Thread.Yield();
|
||||
}
|
||||
|
||||
OwnsCondVarValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseCondVarValue()
|
||||
{
|
||||
if (OwnsCondVarValue)
|
||||
{
|
||||
OwnsCondVarValue = false;
|
||||
|
||||
Process.Memory.ReleaseAddress(CondVarAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Core/OsHle/Display.cs
Normal file
12
Ryujinx.Core/OsHle/Display.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
class Display
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public Display(string Name)
|
||||
{
|
||||
this.Name = Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Exceptions
|
||||
{
|
||||
public class GuestBrokeExecutionException : Exception
|
||||
{
|
||||
private const string ExMsg = "The guest program broke execution!";
|
||||
|
||||
public GuestBrokeExecutionException() : base(ExMsg) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Exceptions
|
||||
{
|
||||
public class UndefinedInstructionException : Exception
|
||||
{
|
||||
private const string ExMsg = "The instruction at 0x{0:x16} (opcode 0x{1:x8}) is undefined!";
|
||||
|
||||
public UndefinedInstructionException() : base() { }
|
||||
|
||||
public UndefinedInstructionException(long Position, int OpCode) : base(string.Format(ExMsg, Position, OpCode)) { }
|
||||
}
|
||||
}
|
12
Ryujinx.Core/OsHle/FileDesc.cs
Normal file
12
Ryujinx.Core/OsHle/FileDesc.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
class FileDesc
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public FileDesc(string Name)
|
||||
{
|
||||
this.Name = Name;
|
||||
}
|
||||
}
|
||||
}
|
58
Ryujinx.Core/OsHle/Handles/HDomain.cs
Normal file
58
Ryujinx.Core/OsHle/Handles/HDomain.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using Ryujinx.Core.OsHle.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
class HDomain : HSession
|
||||
{
|
||||
private Dictionary<int, object> Objects;
|
||||
|
||||
private IdPool ObjIds;
|
||||
|
||||
public HDomain(HSession Session) : base(Session)
|
||||
{
|
||||
Objects = new Dictionary<int, object>();
|
||||
|
||||
ObjIds = new IdPool();
|
||||
}
|
||||
|
||||
public int GenerateObjectId(object Obj)
|
||||
{
|
||||
int Id = ObjIds.GenerateId();
|
||||
|
||||
if (Id == -1)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Objects.Add(Id, Obj);
|
||||
|
||||
return Id;
|
||||
}
|
||||
|
||||
public void DeleteObject(int Id)
|
||||
{
|
||||
if (Objects.TryGetValue(Id, out object Obj))
|
||||
{
|
||||
if (Obj is IDisposable DisposableObj)
|
||||
{
|
||||
DisposableObj.Dispose();
|
||||
}
|
||||
|
||||
ObjIds.DeleteId(Id);
|
||||
Objects.Remove(Id);
|
||||
}
|
||||
}
|
||||
|
||||
public object GetObject(int Id)
|
||||
{
|
||||
if (Objects.TryGetValue(Id, out object Obj))
|
||||
{
|
||||
return Obj;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
7
Ryujinx.Core/OsHle/Handles/HEvent.cs
Normal file
7
Ryujinx.Core/OsHle/Handles/HEvent.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
class HEvent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
18
Ryujinx.Core/OsHle/Handles/HNvMap.cs
Normal file
18
Ryujinx.Core/OsHle/Handles/HNvMap.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
class HNvMap
|
||||
{
|
||||
public int Id { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
|
||||
public int Align { get; set; }
|
||||
public int Kind { get; set; }
|
||||
public long Address { get; set; }
|
||||
|
||||
public HNvMap(int Id, int Size)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.Size = Size;
|
||||
}
|
||||
}
|
||||
}
|
27
Ryujinx.Core/OsHle/Handles/HSession.cs
Normal file
27
Ryujinx.Core/OsHle/Handles/HSession.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
class HSession
|
||||
{
|
||||
public string ServiceName { get; private set; }
|
||||
|
||||
public bool IsInitialized { get; private set; }
|
||||
|
||||
public int State { get; set; }
|
||||
|
||||
public HSession(string ServiceName)
|
||||
{
|
||||
this.ServiceName = ServiceName;
|
||||
}
|
||||
|
||||
public HSession(HSession Session)
|
||||
{
|
||||
ServiceName = Session.ServiceName;
|
||||
IsInitialized = Session.IsInitialized;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
IsInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
30
Ryujinx.Core/OsHle/Handles/HSessionObj.cs
Normal file
30
Ryujinx.Core/OsHle/Handles/HSessionObj.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
class HSessionObj : HSession, IDisposable
|
||||
{
|
||||
public object Obj { get; private set; }
|
||||
|
||||
public HSessionObj(HSession Session, object Obj) : base(Session)
|
||||
{
|
||||
this.Obj = Obj;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool Disposing)
|
||||
{
|
||||
if (Disposing && Obj != null)
|
||||
{
|
||||
if (Obj is IDisposable DisposableObj)
|
||||
{
|
||||
DisposableObj.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
70
Ryujinx.Core/OsHle/Handles/HSharedMem.cs
Normal file
70
Ryujinx.Core/OsHle/Handles/HSharedMem.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
class HSharedMem
|
||||
{
|
||||
private List<long> Positions;
|
||||
|
||||
public int PositionsCount => Positions.Count;
|
||||
|
||||
public EventHandler<EventArgs> MemoryMapped;
|
||||
public EventHandler<EventArgs> MemoryUnmapped;
|
||||
|
||||
public HSharedMem(long PhysPos)
|
||||
{
|
||||
Positions = new List<long>();
|
||||
}
|
||||
|
||||
public void AddVirtualPosition(long Position)
|
||||
{
|
||||
lock (Positions)
|
||||
{
|
||||
Positions.Add(Position);
|
||||
|
||||
MemoryMapped?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveVirtualPosition(long Position)
|
||||
{
|
||||
lock (Positions)
|
||||
{
|
||||
Positions.Remove(Position);
|
||||
|
||||
MemoryUnmapped?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public long GetVirtualPosition(int Index)
|
||||
{
|
||||
lock (Positions)
|
||||
{
|
||||
if (Index < 0 || Index >= Positions.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
||||
}
|
||||
|
||||
return Positions[Index];
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetLastVirtualPosition(out long Position)
|
||||
{
|
||||
lock (Positions)
|
||||
{
|
||||
if (Positions.Count > 0)
|
||||
{
|
||||
Position = Positions[Positions.Count - 1];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Position = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
Ryujinx.Core/OsHle/Handles/HThread.cs
Normal file
21
Ryujinx.Core/OsHle/Handles/HThread.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using ChocolArm64;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
public class HThread
|
||||
{
|
||||
public AThread Thread { get; private set; }
|
||||
|
||||
public int ProcessorId { get; private set; }
|
||||
public int Priority { get; private set; }
|
||||
|
||||
public int ThreadId => Thread.ThreadId;
|
||||
|
||||
public HThread(AThread Thread, int ProcessorId, int Priority)
|
||||
{
|
||||
this.Thread = Thread;
|
||||
this.ProcessorId = ProcessorId;
|
||||
this.Priority = Priority;
|
||||
}
|
||||
}
|
||||
}
|
21
Ryujinx.Core/OsHle/Handles/HTransferMem.cs
Normal file
21
Ryujinx.Core/OsHle/Handles/HTransferMem.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using ChocolArm64.Memory;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
class HTransferMem
|
||||
{
|
||||
public AMemory Memory { get; private set; }
|
||||
public AMemoryPerm Perm { get; private set; }
|
||||
|
||||
public long Position { get; private set; }
|
||||
public long Size { get; private set; }
|
||||
|
||||
public HTransferMem(AMemory Memory, AMemoryPerm Perm, long Position, long Size)
|
||||
{
|
||||
this.Memory = Memory;
|
||||
this.Perm = Perm;
|
||||
this.Position = Position;
|
||||
this.Size = Size;
|
||||
}
|
||||
}
|
||||
}
|
334
Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
Normal file
334
Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
Normal file
|
@ -0,0 +1,334 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
public class KProcessScheduler : IDisposable
|
||||
{
|
||||
private class SchedulerThread : IDisposable
|
||||
{
|
||||
public HThread Thread { get; private set; }
|
||||
|
||||
public AutoResetEvent WaitEvent { get; private set; }
|
||||
|
||||
public SchedulerThread(HThread Thread)
|
||||
{
|
||||
this.Thread = Thread;
|
||||
|
||||
WaitEvent = new AutoResetEvent(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool Disposing)
|
||||
{
|
||||
if (Disposing)
|
||||
{
|
||||
WaitEvent.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ThreadQueue
|
||||
{
|
||||
private List<SchedulerThread> Threads;
|
||||
|
||||
public ThreadQueue()
|
||||
{
|
||||
Threads = new List<SchedulerThread>();
|
||||
}
|
||||
|
||||
public void Push(SchedulerThread Thread)
|
||||
{
|
||||
lock (Threads)
|
||||
{
|
||||
Threads.Add(Thread);
|
||||
}
|
||||
}
|
||||
|
||||
public SchedulerThread Pop(int MinPriority = 0x40)
|
||||
{
|
||||
lock (Threads)
|
||||
{
|
||||
SchedulerThread SchedThread;
|
||||
|
||||
int HighestPriority = MinPriority;
|
||||
|
||||
int HighestPrioIndex = -1;
|
||||
|
||||
for (int Index = 0; Index < Threads.Count; Index++)
|
||||
{
|
||||
SchedThread = Threads[Index];
|
||||
|
||||
if (HighestPriority > SchedThread.Thread.Priority)
|
||||
{
|
||||
HighestPriority = SchedThread.Thread.Priority;
|
||||
|
||||
HighestPrioIndex = Index;
|
||||
}
|
||||
}
|
||||
|
||||
if (HighestPrioIndex == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
SchedThread = Threads[HighestPrioIndex];
|
||||
|
||||
Threads.RemoveAt(HighestPrioIndex);
|
||||
|
||||
return SchedThread;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasThread(SchedulerThread SchedThread)
|
||||
{
|
||||
lock (Threads)
|
||||
{
|
||||
return Threads.Contains(SchedThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ConcurrentDictionary<HThread, SchedulerThread> AllThreads;
|
||||
|
||||
private ThreadQueue[] WaitingToRun;
|
||||
|
||||
private HashSet<int> ActiveProcessors;
|
||||
|
||||
private object SchedLock;
|
||||
|
||||
public KProcessScheduler()
|
||||
{
|
||||
AllThreads = new ConcurrentDictionary<HThread, SchedulerThread>();
|
||||
|
||||
WaitingToRun = new ThreadQueue[4];
|
||||
|
||||
for (int Index = 0; Index < 4; Index++)
|
||||
{
|
||||
WaitingToRun[Index] = new ThreadQueue();
|
||||
}
|
||||
|
||||
ActiveProcessors = new HashSet<int>();
|
||||
|
||||
SchedLock = new object();
|
||||
}
|
||||
|
||||
public void StartThread(HThread Thread)
|
||||
{
|
||||
lock (SchedLock)
|
||||
{
|
||||
SchedulerThread SchedThread = new SchedulerThread(Thread);
|
||||
|
||||
if (!AllThreads.TryAdd(Thread, SchedThread))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ActiveProcessors.Contains(Thread.ProcessorId))
|
||||
{
|
||||
ActiveProcessors.Add(Thread.ProcessorId);
|
||||
|
||||
Thread.Thread.Execute();
|
||||
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} running.");
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
|
||||
|
||||
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Suspend(int ProcessorId)
|
||||
{
|
||||
lock (SchedLock)
|
||||
{
|
||||
SchedulerThread SchedThread = WaitingToRun[ProcessorId].Pop();
|
||||
|
||||
if (SchedThread != null)
|
||||
{
|
||||
RunThread(SchedThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
ActiveProcessors.Remove(ProcessorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Resume(HThread CurrThread)
|
||||
{
|
||||
SchedulerThread SchedThread;
|
||||
|
||||
Logging.Debug($"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state.");
|
||||
|
||||
lock (SchedLock)
|
||||
{
|
||||
if (!AllThreads.TryGetValue(CurrThread, out SchedThread))
|
||||
{
|
||||
Logging.Error($"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TryResumingExecution(SchedThread);
|
||||
}
|
||||
|
||||
public void WaitForSignal(HThread Thread, int Timeout = -1)
|
||||
{
|
||||
SchedulerThread SchedThread;
|
||||
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state.");
|
||||
|
||||
lock (SchedLock)
|
||||
{
|
||||
SchedThread = WaitingToRun[Thread.ProcessorId].Pop();
|
||||
|
||||
if (SchedThread != null)
|
||||
{
|
||||
RunThread(SchedThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
ActiveProcessors.Remove(Thread.ProcessorId);
|
||||
}
|
||||
|
||||
if (!AllThreads.TryGetValue(Thread, out SchedThread))
|
||||
{
|
||||
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Timeout >= 0)
|
||||
{
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
|
||||
|
||||
SchedThread.WaitEvent.WaitOne(Timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
SchedThread.WaitEvent.WaitOne();
|
||||
}
|
||||
|
||||
TryResumingExecution(SchedThread);
|
||||
}
|
||||
|
||||
private void TryResumingExecution(SchedulerThread SchedThread)
|
||||
{
|
||||
HThread Thread = SchedThread.Thread;
|
||||
|
||||
lock (SchedLock)
|
||||
{
|
||||
if (ActiveProcessors.Add(Thread.ProcessorId))
|
||||
{
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
|
||||
}
|
||||
|
||||
SchedThread.WaitEvent.WaitOne();
|
||||
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
|
||||
}
|
||||
|
||||
public void Yield(HThread Thread)
|
||||
{
|
||||
SchedulerThread SchedThread;
|
||||
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
|
||||
|
||||
lock (SchedLock)
|
||||
{
|
||||
SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.Priority);
|
||||
|
||||
if (SchedThread == null)
|
||||
{
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
RunThread(SchedThread);
|
||||
|
||||
if (!AllThreads.TryGetValue(Thread, out SchedThread))
|
||||
{
|
||||
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
|
||||
}
|
||||
|
||||
SchedThread.WaitEvent.WaitOne();
|
||||
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
|
||||
}
|
||||
|
||||
private void RunThread(SchedulerThread SchedThread)
|
||||
{
|
||||
if (!SchedThread.Thread.Thread.Execute())
|
||||
{
|
||||
SchedThread.WaitEvent.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Signal(params HThread[] Threads)
|
||||
{
|
||||
lock (SchedLock)
|
||||
{
|
||||
foreach (HThread Thread in Threads)
|
||||
{
|
||||
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
||||
{
|
||||
if (!WaitingToRun[Thread.ProcessorId].HasThread(SchedThread))
|
||||
{
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
|
||||
|
||||
SchedThread.WaitEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetDbgThreadInfo(HThread Thread)
|
||||
{
|
||||
return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}";
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool Disposing)
|
||||
{
|
||||
if (Disposing)
|
||||
{
|
||||
foreach (SchedulerThread SchedThread in AllThreads.Values)
|
||||
{
|
||||
SchedThread.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
197
Ryujinx.Core/OsHle/Horizon.cs
Normal file
197
Ryujinx.Core/OsHle/Horizon.cs
Normal file
|
@ -0,0 +1,197 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.Loaders.Executables;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Utilities;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
public class Horizon
|
||||
{
|
||||
internal const int HidSize = 0x40000;
|
||||
internal const int FontSize = 0x50;
|
||||
|
||||
internal int HidHandle { get; private set; }
|
||||
internal int FontHandle { get; private set; }
|
||||
|
||||
public long HidOffset { get; private set; }
|
||||
public long FontOffset { get; private set; }
|
||||
|
||||
internal IdPool IdGen { get; private set; }
|
||||
internal IdPool NvMapIds { get; private set; }
|
||||
|
||||
internal IdPoolWithObj Handles { get; private set; }
|
||||
internal IdPoolWithObj Fds { get; private set; }
|
||||
internal IdPoolWithObj Displays { get; private set; }
|
||||
|
||||
public ConcurrentDictionary<long, Mutex> Mutexes { get; private set; }
|
||||
public ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
|
||||
|
||||
private ConcurrentDictionary<int, Process> Processes;
|
||||
|
||||
private HSharedMem HidSharedMem;
|
||||
|
||||
private AMemoryAlloc Allocator;
|
||||
|
||||
private Switch Ns;
|
||||
|
||||
public Horizon(Switch Ns)
|
||||
{
|
||||
this.Ns = Ns;
|
||||
|
||||
IdGen = new IdPool();
|
||||
NvMapIds = new IdPool();
|
||||
|
||||
Handles = new IdPoolWithObj();
|
||||
Fds = new IdPoolWithObj();
|
||||
Displays = new IdPoolWithObj();
|
||||
|
||||
Mutexes = new ConcurrentDictionary<long, Mutex>();
|
||||
CondVars = new ConcurrentDictionary<long, CondVar>();
|
||||
|
||||
Processes = new ConcurrentDictionary<int, Process>();
|
||||
|
||||
Allocator = new AMemoryAlloc();
|
||||
|
||||
HidOffset = Allocator.Alloc(HidSize);
|
||||
FontOffset = Allocator.Alloc(FontSize);
|
||||
|
||||
HidSharedMem = new HSharedMem(HidOffset);
|
||||
|
||||
HidSharedMem.MemoryMapped += HidInit;
|
||||
|
||||
HidHandle = Handles.GenerateId(HidSharedMem);
|
||||
|
||||
FontHandle = Handles.GenerateId(new HSharedMem(FontOffset));
|
||||
}
|
||||
|
||||
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
||||
{
|
||||
if (RomFsFile != null)
|
||||
{
|
||||
Ns.VFs.LoadRomFs(RomFsFile);
|
||||
}
|
||||
|
||||
int ProcessId = IdGen.GenerateId();
|
||||
|
||||
Process MainProcess = new Process(Ns, Allocator, ProcessId);
|
||||
|
||||
void LoadNso(string FileName)
|
||||
{
|
||||
foreach (string File in Directory.GetFiles(ExeFsDir, FileName))
|
||||
{
|
||||
if (Path.GetExtension(File) != string.Empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}...");
|
||||
|
||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
||||
{
|
||||
Nso Program = new Nso(Input);
|
||||
|
||||
MainProcess.LoadProgram(Program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LoadNso("rtld");
|
||||
|
||||
MainProcess.SetEmptyArgs();
|
||||
|
||||
LoadNso("main");
|
||||
LoadNso("subsdk*");
|
||||
LoadNso("sdk");
|
||||
|
||||
MainProcess.InitializeHeap();
|
||||
MainProcess.Run();
|
||||
|
||||
Processes.TryAdd(ProcessId, MainProcess);
|
||||
}
|
||||
|
||||
public void LoadProgram(string FileName)
|
||||
{
|
||||
int ProcessId = IdGen.GenerateId();
|
||||
|
||||
Process MainProcess = new Process(Ns, Allocator, ProcessId);
|
||||
|
||||
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
||||
{
|
||||
if (Path.GetExtension(FileName).ToLower() == ".nro")
|
||||
{
|
||||
MainProcess.LoadProgram(new Nro(Input));
|
||||
}
|
||||
else
|
||||
{
|
||||
MainProcess.LoadProgram(new Nso(Input));
|
||||
}
|
||||
}
|
||||
|
||||
MainProcess.SetEmptyArgs();
|
||||
MainProcess.InitializeHeap();
|
||||
MainProcess.Run();
|
||||
|
||||
Processes.TryAdd(ProcessId, MainProcess);
|
||||
}
|
||||
|
||||
public void FinalizeAllProcesses()
|
||||
{
|
||||
foreach (Process Process in Processes.Values)
|
||||
{
|
||||
Process.StopAllThreads();
|
||||
Process.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
internal bool ExitProcess(int ProcessId)
|
||||
{
|
||||
bool Success = Processes.TryRemove(ProcessId, out Process Process);
|
||||
|
||||
if (Success)
|
||||
{
|
||||
Process.StopAllThreads();
|
||||
}
|
||||
|
||||
if (Processes.Count == 0)
|
||||
{
|
||||
Ns.OnFinish(EventArgs.Empty);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal bool TryGetProcess(int ProcessId, out Process Process)
|
||||
{
|
||||
return Processes.TryGetValue(ProcessId, out Process);
|
||||
}
|
||||
|
||||
internal void CloseHandle(int Handle)
|
||||
{
|
||||
object HndData = Handles.GetData<object>(Handle);
|
||||
|
||||
if (HndData is HTransferMem TransferMem)
|
||||
{
|
||||
TransferMem.Memory.Manager.Reprotect(
|
||||
TransferMem.Position,
|
||||
TransferMem.Size,
|
||||
TransferMem.Perm);
|
||||
}
|
||||
|
||||
Handles.Delete(Handle);
|
||||
}
|
||||
|
||||
private void HidInit(object sender, EventArgs e)
|
||||
{
|
||||
HSharedMem SharedMem = (HSharedMem)sender;
|
||||
|
||||
if (SharedMem.TryGetLastVirtualPosition(out long Position))
|
||||
{
|
||||
Logging.Info($"HID shared memory successfully mapped to {Position:x16}!");
|
||||
Ns.Hid.Init(Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
Ryujinx.Core/OsHle/Ipc/IpcBuffDesc.cs
Normal file
27
Ryujinx.Core/OsHle/Ipc/IpcBuffDesc.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
struct IpcBuffDesc
|
||||
{
|
||||
public long Position { get; private set; }
|
||||
public long Size { get; private set; }
|
||||
public int Flags { get; private set; }
|
||||
|
||||
public IpcBuffDesc(BinaryReader Reader)
|
||||
{
|
||||
long Word0 = Reader.ReadUInt32();
|
||||
long Word1 = Reader.ReadUInt32();
|
||||
long Word2 = Reader.ReadUInt32();
|
||||
|
||||
Position = Word1;
|
||||
Position |= (Word2 << 4) & 0x0f00000000;
|
||||
Position |= (Word2 << 34) & 0x7000000000;
|
||||
|
||||
Size = Word0;
|
||||
Size |= (Word2 << 8) & 0xf00000000;
|
||||
|
||||
Flags = (int)Word2 & 3;
|
||||
}
|
||||
}
|
||||
}
|
8
Ryujinx.Core/OsHle/Ipc/IpcDomCmd.cs
Normal file
8
Ryujinx.Core/OsHle/Ipc/IpcDomCmd.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
enum IpcDomCmd
|
||||
{
|
||||
SendMsg = 1,
|
||||
DeleteObj = 2
|
||||
}
|
||||
}
|
90
Ryujinx.Core/OsHle/Ipc/IpcHandleDesc.cs
Normal file
90
Ryujinx.Core/OsHle/Ipc/IpcHandleDesc.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
class IpcHandleDesc
|
||||
{
|
||||
public bool HasPId { get; private set; }
|
||||
|
||||
public long PId { get; private set; }
|
||||
|
||||
public int[] ToCopy { get; private set; }
|
||||
public int[] ToMove { get; private set; }
|
||||
|
||||
public IpcHandleDesc(BinaryReader Reader)
|
||||
{
|
||||
int Word = Reader.ReadInt32();
|
||||
|
||||
HasPId = (Word & 1) != 0;
|
||||
|
||||
ToCopy = new int[(Word >> 1) & 0xf];
|
||||
ToMove = new int[(Word >> 5) & 0xf];
|
||||
|
||||
PId = HasPId ? Reader.ReadInt64() : 0;
|
||||
|
||||
for (int Index = 0; Index < ToCopy.Length; Index++)
|
||||
{
|
||||
ToCopy[Index] = Reader.ReadInt32();
|
||||
}
|
||||
|
||||
for (int Index = 0; Index < ToMove.Length; Index++)
|
||||
{
|
||||
ToMove[Index] = Reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public IpcHandleDesc(int[] Copy, int[] Move)
|
||||
{
|
||||
ToCopy = Copy ?? throw new ArgumentNullException(nameof(Copy));
|
||||
ToMove = Move ?? throw new ArgumentNullException(nameof(Move));
|
||||
}
|
||||
|
||||
public IpcHandleDesc(int[] Copy, int[] Move, long PId) : this(Copy, Move)
|
||||
{
|
||||
this.PId = PId;
|
||||
|
||||
HasPId = true;
|
||||
}
|
||||
|
||||
public static IpcHandleDesc MakeCopy(int Handle) => new IpcHandleDesc(
|
||||
new int[] { Handle },
|
||||
new int[0]);
|
||||
|
||||
public static IpcHandleDesc MakeMove(int Handle) => new IpcHandleDesc(
|
||||
new int[0],
|
||||
new int[] { Handle });
|
||||
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
int Word = HasPId ? 1 : 0;
|
||||
|
||||
Word |= (ToCopy.Length & 0xf) << 1;
|
||||
Word |= (ToMove.Length & 0xf) << 5;
|
||||
|
||||
Writer.Write(Word);
|
||||
|
||||
if (HasPId)
|
||||
{
|
||||
Writer.Write((long)PId);
|
||||
}
|
||||
|
||||
foreach (int Handle in ToCopy)
|
||||
{
|
||||
Writer.Write(Handle);
|
||||
}
|
||||
|
||||
foreach (int Handle in ToMove)
|
||||
{
|
||||
Writer.Write(Handle);
|
||||
}
|
||||
|
||||
return MS.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
277
Ryujinx.Core/OsHle/Ipc/IpcHandler.cs
Normal file
277
Ryujinx.Core/OsHle/Ipc/IpcHandler.cs
Normal file
|
@ -0,0 +1,277 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Objects;
|
||||
using Ryujinx.Core.OsHle.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
static class IpcHandler
|
||||
{
|
||||
private static Dictionary<(string, int), ServiceProcessRequest> ServiceCmds =
|
||||
new Dictionary<(string, int), ServiceProcessRequest>()
|
||||
{
|
||||
{ ( "acc:u0", 3), Service.AccU0ListOpenUsers },
|
||||
{ ( "acc:u0", 5), Service.AccU0GetProfile },
|
||||
{ ( "acc:u0", 100), Service.AccU0InitializeApplicationInfo },
|
||||
{ ( "acc:u0", 101), Service.AccU0GetBaasAccountManagerForApplication },
|
||||
{ ( "apm", 0), Service.ApmOpenSession },
|
||||
{ ( "apm:p", 0), Service.ApmOpenSession },
|
||||
{ ( "appletOE", 0), Service.AppletOpenApplicationProxy },
|
||||
{ ( "audout:u", 0), Service.AudOutListAudioOuts },
|
||||
{ ( "audout:u", 1), Service.AudOutOpenAudioOut },
|
||||
{ ( "audren:u", 0), Service.AudRenOpenAudioRenderer },
|
||||
{ ( "audren:u", 1), Service.AudRenGetAudioRendererWorkBufferSize },
|
||||
{ ( "friend:a", 0), Service.FriendCreateFriendService },
|
||||
{ ( "fsp-srv", 1), Service.FspSrvInitialize },
|
||||
{ ( "fsp-srv", 18), Service.FspSrvMountSdCard },
|
||||
{ ( "fsp-srv", 51), Service.FspSrvMountSaveData },
|
||||
{ ( "fsp-srv", 200), Service.FspSrvOpenDataStorageByCurrentProcess },
|
||||
{ ( "fsp-srv", 203), Service.FspSrvOpenRomStorage },
|
||||
{ ( "fsp-srv", 1005), Service.FspSrvGetGlobalAccessLogMode },
|
||||
{ ( "hid", 0), Service.HidCreateAppletResource },
|
||||
{ ( "hid", 11), Service.HidActivateTouchScreen },
|
||||
{ ( "hid", 100), Service.HidSetSupportedNpadStyleSet },
|
||||
{ ( "hid", 102), Service.HidSetSupportedNpadIdType },
|
||||
{ ( "hid", 103), Service.HidActivateNpad },
|
||||
{ ( "hid", 120), Service.HidSetNpadJoyHoldType },
|
||||
{ ( "lm", 0), Service.LmInitialize },
|
||||
{ ( "nvdrv", 0), Service.NvDrvOpen },
|
||||
{ ( "nvdrv", 1), Service.NvDrvIoctl },
|
||||
{ ( "nvdrv", 2), Service.NvDrvClose },
|
||||
{ ( "nvdrv", 3), Service.NvDrvInitialize },
|
||||
{ ( "nvdrv", 4), Service.NvDrvQueryEvent },
|
||||
{ ( "nvdrv", 8), Service.NvDrvSetClientPid },
|
||||
{ ( "nvdrv:a", 0), Service.NvDrvOpen },
|
||||
{ ( "nvdrv:a", 1), Service.NvDrvIoctl },
|
||||
{ ( "nvdrv:a", 2), Service.NvDrvClose },
|
||||
{ ( "nvdrv:a", 3), Service.NvDrvInitialize },
|
||||
{ ( "nvdrv:a", 4), Service.NvDrvQueryEvent },
|
||||
{ ( "nvdrv:a", 8), Service.NvDrvSetClientPid },
|
||||
{ ( "pctl:a", 0), Service.PctlCreateService },
|
||||
{ ( "pl:u", 1), Service.PlGetLoadState },
|
||||
{ ( "pl:u", 2), Service.PlGetFontSize },
|
||||
{ ( "pl:u", 3), Service.PlGetSharedMemoryAddressOffset },
|
||||
{ ( "pl:u", 4), Service.PlGetSharedMemoryNativeHandle },
|
||||
{ ( "set", 1), Service.SetGetAvailableLanguageCodes },
|
||||
{ ( "sm:", 0), Service.SmInitialize },
|
||||
{ ( "sm:", 1), Service.SmGetService },
|
||||
{ ( "time:u", 0), Service.TimeGetStandardUserSystemClock },
|
||||
{ ( "time:u", 1), Service.TimeGetStandardNetworkSystemClock },
|
||||
{ ( "time:u", 2), Service.TimeGetStandardSteadyClock },
|
||||
{ ( "time:u", 3), Service.TimeGetTimeZoneService },
|
||||
{ ( "time:u", 4), Service.TimeGetStandardLocalSystemClock },
|
||||
{ ( "time:s", 0), Service.TimeGetStandardUserSystemClock },
|
||||
{ ( "time:s", 1), Service.TimeGetStandardNetworkSystemClock },
|
||||
{ ( "time:s", 2), Service.TimeGetStandardSteadyClock },
|
||||
{ ( "time:s", 3), Service.TimeGetTimeZoneService },
|
||||
{ ( "time:s", 4), Service.TimeGetStandardLocalSystemClock },
|
||||
{ ( "vi:m", 2), Service.ViGetDisplayService },
|
||||
};
|
||||
|
||||
private const long SfciMagic = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'I' << 24;
|
||||
private const long SfcoMagic = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'O' << 24;
|
||||
|
||||
public static void IpcCall(
|
||||
Switch Ns,
|
||||
AMemory Memory,
|
||||
HSession Session,
|
||||
IpcMessage Request,
|
||||
long CmdPtr,
|
||||
int HndId)
|
||||
{
|
||||
IpcMessage Response = new IpcMessage(Request.IsDomain);
|
||||
|
||||
using (MemoryStream Raw = new MemoryStream(Request.RawData))
|
||||
{
|
||||
BinaryReader ReqReader = new BinaryReader(Raw);
|
||||
|
||||
if (Request.Type == IpcMessageType.Request)
|
||||
{
|
||||
string ServiceName = Session.ServiceName;
|
||||
|
||||
ServiceProcessRequest ProcReq = null;
|
||||
|
||||
bool IgnoreNullPR = false;
|
||||
|
||||
string DbgServiceName = string.Empty;
|
||||
|
||||
if (Session is HDomain Dom)
|
||||
{
|
||||
if (Request.DomCmd == IpcDomCmd.SendMsg)
|
||||
{
|
||||
long Magic = ReqReader.ReadInt64();
|
||||
int CmdId = (int)ReqReader.ReadInt64();
|
||||
|
||||
object Obj = Dom.GetObject(Request.DomObjId);
|
||||
|
||||
if (Obj is HDomain)
|
||||
{
|
||||
ServiceCmds.TryGetValue((ServiceName, CmdId), out ProcReq);
|
||||
|
||||
DbgServiceName = $"{ServiceName} {ProcReq?.Method.Name ?? CmdId.ToString()}";
|
||||
}
|
||||
else if (Obj != null)
|
||||
{
|
||||
((IIpcInterface)Obj).Commands.TryGetValue(CmdId, out ProcReq);
|
||||
|
||||
DbgServiceName = $"{ServiceName} {Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}";
|
||||
}
|
||||
}
|
||||
else if (Request.DomCmd == IpcDomCmd.DeleteObj)
|
||||
{
|
||||
Dom.DeleteObject(Request.DomObjId);
|
||||
|
||||
Response = FillResponse(Response, 0);
|
||||
|
||||
IgnoreNullPR = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
long Magic = ReqReader.ReadInt64();
|
||||
int CmdId = (int)ReqReader.ReadInt64();
|
||||
|
||||
if (Session is HSessionObj)
|
||||
{
|
||||
object Obj = ((HSessionObj)Session).Obj;
|
||||
|
||||
((IIpcInterface)Obj).Commands.TryGetValue(CmdId, out ProcReq);
|
||||
|
||||
DbgServiceName = $"{ServiceName} {Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}";
|
||||
}
|
||||
else
|
||||
{
|
||||
ServiceCmds.TryGetValue((ServiceName, CmdId), out ProcReq);
|
||||
|
||||
DbgServiceName = $"{ServiceName} {ProcReq?.Method.Name ?? CmdId.ToString()}";
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Debug($"IpcMessage: {DbgServiceName}");
|
||||
|
||||
if (ProcReq != null)
|
||||
{
|
||||
using (MemoryStream ResMS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter ResWriter = new BinaryWriter(ResMS);
|
||||
|
||||
ServiceCtx Context = new ServiceCtx(
|
||||
Ns,
|
||||
Memory,
|
||||
Session,
|
||||
Request,
|
||||
Response,
|
||||
ReqReader,
|
||||
ResWriter);
|
||||
|
||||
long Result = ProcReq(Context);
|
||||
|
||||
Response = FillResponse(Response, Result, ResMS.ToArray());
|
||||
}
|
||||
}
|
||||
else if (!IgnoreNullPR)
|
||||
{
|
||||
throw new NotImplementedException(DbgServiceName);
|
||||
}
|
||||
}
|
||||
else if (Request.Type == IpcMessageType.Control)
|
||||
{
|
||||
long Magic = ReqReader.ReadInt64();
|
||||
long CmdId = ReqReader.ReadInt64();
|
||||
|
||||
switch (CmdId)
|
||||
{
|
||||
case 0: Request = IpcConvertSessionToDomain(Ns, Session, Response, HndId); break;
|
||||
case 3: Request = IpcQueryBufferPointerSize(Response); break;
|
||||
case 4: Request = IpcDuplicateSessionEx(Ns, Session, Response, ReqReader); break;
|
||||
|
||||
default: throw new NotImplementedException(CmdId.ToString());
|
||||
}
|
||||
}
|
||||
else if (Request.Type == IpcMessageType.Unknown2)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(Request.Type.ToString());
|
||||
}
|
||||
|
||||
AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr));
|
||||
}
|
||||
}
|
||||
|
||||
private static IpcMessage IpcConvertSessionToDomain(
|
||||
Switch Ns,
|
||||
HSession Session,
|
||||
IpcMessage Response,
|
||||
int HndId)
|
||||
{
|
||||
HDomain Dom = new HDomain(Session);
|
||||
|
||||
Ns.Os.Handles.ReplaceData(HndId, Dom);
|
||||
|
||||
return FillResponse(Response, 0, Dom.GenerateObjectId(Dom));
|
||||
}
|
||||
|
||||
private static IpcMessage IpcDuplicateSessionEx(
|
||||
Switch Ns,
|
||||
HSession Session,
|
||||
IpcMessage Response,
|
||||
BinaryReader ReqReader)
|
||||
{
|
||||
int Unknown = ReqReader.ReadInt32();
|
||||
|
||||
int Handle = Ns.Os.Handles.GenerateId(Session);
|
||||
|
||||
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
|
||||
|
||||
return FillResponse(Response, 0);
|
||||
}
|
||||
|
||||
private static IpcMessage IpcQueryBufferPointerSize(IpcMessage Response)
|
||||
{
|
||||
return FillResponse(Response, 0, 0x500);
|
||||
}
|
||||
|
||||
private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
foreach (int Value in Values)
|
||||
{
|
||||
Writer.Write(Value);
|
||||
}
|
||||
|
||||
return FillResponse(Response, Result, MS.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private static IpcMessage FillResponse(IpcMessage Response, long Result, byte[] Data = null)
|
||||
{
|
||||
Response.Type = IpcMessageType.Response;
|
||||
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
Writer.Write(SfcoMagic);
|
||||
Writer.Write(Result);
|
||||
|
||||
if (Data != null)
|
||||
{
|
||||
Writer.Write(Data);
|
||||
}
|
||||
|
||||
Response.RawData = MS.ToArray();
|
||||
}
|
||||
|
||||
return Response;
|
||||
}
|
||||
}
|
||||
}
|
231
Ryujinx.Core/OsHle/Ipc/IpcMessage.cs
Normal file
231
Ryujinx.Core/OsHle/Ipc/IpcMessage.cs
Normal file
|
@ -0,0 +1,231 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
class IpcMessage
|
||||
{
|
||||
public IpcMessageType Type { get; set; }
|
||||
|
||||
public IpcHandleDesc HandleDesc { get; set; }
|
||||
|
||||
public List<IpcPtrBuffDesc> PtrBuff { get; private set; }
|
||||
public List<IpcBuffDesc> SendBuff { get; private set; }
|
||||
public List<IpcBuffDesc> ReceiveBuff { get; private set; }
|
||||
public List<IpcBuffDesc> ExchangeBuff { get; private set; }
|
||||
public List<IpcRecvListBuffDesc> RecvListBuff { get; private set; }
|
||||
|
||||
public List<int> ResponseObjIds { get; private set; }
|
||||
|
||||
public bool IsDomain { get; private set; }
|
||||
public IpcDomCmd DomCmd { get; private set; }
|
||||
public int DomObjId { get; private set; }
|
||||
|
||||
public byte[] RawData { get; set; }
|
||||
|
||||
public IpcMessage()
|
||||
{
|
||||
PtrBuff = new List<IpcPtrBuffDesc>();
|
||||
SendBuff = new List<IpcBuffDesc>();
|
||||
ReceiveBuff = new List<IpcBuffDesc>();
|
||||
ExchangeBuff = new List<IpcBuffDesc>();
|
||||
RecvListBuff = new List<IpcRecvListBuffDesc>();
|
||||
|
||||
ResponseObjIds = new List<int>();
|
||||
}
|
||||
|
||||
public IpcMessage(bool Domain) : this()
|
||||
{
|
||||
IsDomain = Domain;
|
||||
}
|
||||
|
||||
public IpcMessage(byte[] Data, long CmdPtr, bool Domain) : this()
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream(Data))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
|
||||
Initialize(Reader, CmdPtr, Domain);
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize(BinaryReader Reader, long CmdPtr, bool Domain)
|
||||
{
|
||||
IsDomain = Domain;
|
||||
|
||||
int Word0 = Reader.ReadInt32();
|
||||
int Word1 = Reader.ReadInt32();
|
||||
|
||||
Type = (IpcMessageType)(Word0 & 0xffff);
|
||||
|
||||
int PtrBuffCount = (Word0 >> 16) & 0xf;
|
||||
int SendBuffCount = (Word0 >> 20) & 0xf;
|
||||
int RecvBuffCount = (Word0 >> 24) & 0xf;
|
||||
int XchgBuffCount = (Word0 >> 28) & 0xf;
|
||||
|
||||
int RawDataSize = (Word1 >> 0) & 0x3ff;
|
||||
int RecvListFlags = (Word1 >> 10) & 0xf;
|
||||
bool HndDescEnable = ((Word1 >> 31) & 0x1) != 0;
|
||||
|
||||
if (HndDescEnable)
|
||||
{
|
||||
HandleDesc = new IpcHandleDesc(Reader);
|
||||
}
|
||||
|
||||
for (int Index = 0; Index < PtrBuffCount; Index++)
|
||||
{
|
||||
PtrBuff.Add(new IpcPtrBuffDesc(Reader));
|
||||
}
|
||||
|
||||
void ReadBuff(List<IpcBuffDesc> Buff, int Count)
|
||||
{
|
||||
for (int Index = 0; Index < Count; Index++)
|
||||
{
|
||||
Buff.Add(new IpcBuffDesc(Reader));
|
||||
}
|
||||
}
|
||||
|
||||
ReadBuff(SendBuff, SendBuffCount);
|
||||
ReadBuff(ReceiveBuff, RecvBuffCount);
|
||||
ReadBuff(ExchangeBuff, XchgBuffCount);
|
||||
|
||||
RawDataSize *= 4;
|
||||
|
||||
long RecvListPos = Reader.BaseStream.Position + RawDataSize;
|
||||
|
||||
long Pad0 = GetPadSize16(Reader.BaseStream.Position + CmdPtr);
|
||||
|
||||
Reader.BaseStream.Seek(Pad0, SeekOrigin.Current);
|
||||
|
||||
int RecvListCount = RecvListFlags - 2;
|
||||
|
||||
if (RecvListCount == 0)
|
||||
{
|
||||
RecvListCount = 1;
|
||||
}
|
||||
else if (RecvListCount < 0)
|
||||
{
|
||||
RecvListCount = 0;
|
||||
}
|
||||
|
||||
if (Domain)
|
||||
{
|
||||
int DomWord0 = Reader.ReadInt32();
|
||||
|
||||
DomCmd = (IpcDomCmd)(DomWord0 & 0xff);
|
||||
|
||||
RawDataSize = (DomWord0 >> 16) & 0xffff;
|
||||
|
||||
DomObjId = Reader.ReadInt32();
|
||||
|
||||
Reader.ReadInt64(); //Padding
|
||||
}
|
||||
|
||||
RawData = Reader.ReadBytes(RawDataSize);
|
||||
|
||||
Reader.BaseStream.Seek(RecvListPos, SeekOrigin.Begin);
|
||||
|
||||
for (int Index = 0; Index < RecvListCount; Index++)
|
||||
{
|
||||
RecvListBuff.Add(new IpcRecvListBuffDesc(Reader));
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetBytes(long CmdPtr)
|
||||
{
|
||||
//todo
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
int Word0;
|
||||
int Word1;
|
||||
|
||||
Word0 = (int)Type;
|
||||
Word0 |= (PtrBuff.Count & 0xf) << 16;
|
||||
Word0 |= (SendBuff.Count & 0xf) << 20;
|
||||
Word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
||||
Word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
||||
|
||||
byte[] HandleData = new byte[0];
|
||||
|
||||
if (HandleDesc != null)
|
||||
{
|
||||
HandleData = HandleDesc.GetBytes();
|
||||
}
|
||||
|
||||
int DataLength = RawData?.Length ?? 0;
|
||||
|
||||
int Pad0 = (int)GetPadSize16(CmdPtr + 8 + HandleData.Length);
|
||||
|
||||
//Apparently, padding after Raw Data is 16 bytes, however when there is
|
||||
//padding before Raw Data too, we need to subtract the size of this padding.
|
||||
//This is the weirdest padding I've seen so far...
|
||||
int Pad1 = 0x10 - Pad0;
|
||||
|
||||
DataLength = (DataLength + Pad0 + Pad1 + (IsDomain ? 0x10 : 0)) / 4;
|
||||
|
||||
DataLength += ResponseObjIds.Count;
|
||||
|
||||
Word1 = DataLength & 0x3ff;
|
||||
|
||||
if (HandleDesc != null)
|
||||
{
|
||||
Word1 |= 1 << 31;
|
||||
}
|
||||
|
||||
Writer.Write(Word0);
|
||||
Writer.Write(Word1);
|
||||
Writer.Write(HandleData);
|
||||
|
||||
MS.Seek(Pad0, SeekOrigin.Current);
|
||||
|
||||
if (IsDomain)
|
||||
{
|
||||
Writer.Write(ResponseObjIds.Count);
|
||||
Writer.Write(0);
|
||||
Writer.Write(0L);
|
||||
}
|
||||
|
||||
if (RawData != null)
|
||||
{
|
||||
Writer.Write(RawData);
|
||||
}
|
||||
|
||||
foreach (int Id in ResponseObjIds)
|
||||
{
|
||||
Writer.Write(Id);
|
||||
}
|
||||
|
||||
Writer.Write(new byte[Pad1]);
|
||||
|
||||
return MS.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private long GetPadSize16(long Position)
|
||||
{
|
||||
if ((Position & 0xf) != 0)
|
||||
{
|
||||
return 0x10 - (Position & 0xf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetSendBuffPtr()
|
||||
{
|
||||
if (SendBuff.Count > 0 && SendBuff[0].Position != 0)
|
||||
{
|
||||
return SendBuff[0].Position;
|
||||
}
|
||||
|
||||
if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0)
|
||||
{
|
||||
return PtrBuff[0].Position;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
10
Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs
Normal file
10
Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
enum IpcMessageType
|
||||
{
|
||||
Response = 0,
|
||||
Unknown2 = 2,
|
||||
Request = 4,
|
||||
Control = 5
|
||||
}
|
||||
}
|
26
Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs
Normal file
26
Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
struct IpcPtrBuffDesc
|
||||
{
|
||||
public long Position { get; private set; }
|
||||
public int Index { get; private set; }
|
||||
public short Size { get; private set; }
|
||||
|
||||
public IpcPtrBuffDesc(BinaryReader Reader)
|
||||
{
|
||||
long Word0 = Reader.ReadUInt32();
|
||||
long Word1 = Reader.ReadUInt32();
|
||||
|
||||
Position = Word1;
|
||||
Position |= (Word0 << 20) & 0x0f00000000;
|
||||
Position |= (Word0 << 30) & 0x7000000000;
|
||||
|
||||
Index = ((int)Word0 >> 0) & 0x03f;
|
||||
Index |= ((int)Word0 >> 3) & 0x1c0;
|
||||
|
||||
Size = (short)(Word0 >> 16);
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx.Core/OsHle/Ipc/IpcRecvListBuffDesc.cs
Normal file
19
Ryujinx.Core/OsHle/Ipc/IpcRecvListBuffDesc.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
struct IpcRecvListBuffDesc
|
||||
{
|
||||
public long Position { get; private set; }
|
||||
public short Size { get; private set; }
|
||||
|
||||
public IpcRecvListBuffDesc(BinaryReader Reader)
|
||||
{
|
||||
long Value = Reader.ReadInt64();
|
||||
|
||||
Position = Value & 0xffffffffffff;
|
||||
|
||||
Size = (short)(Value >> 48);
|
||||
}
|
||||
}
|
||||
}
|
4
Ryujinx.Core/OsHle/Ipc/ServiceProcessRequest.cs
Normal file
4
Ryujinx.Core/OsHle/Ipc/ServiceProcessRequest.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
delegate long ServiceProcessRequest(ServiceCtx Context);
|
||||
}
|
28
Ryujinx.Core/OsHle/MemoryInfo.cs
Normal file
28
Ryujinx.Core/OsHle/MemoryInfo.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using ChocolArm64.Memory;
|
||||
|
||||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
struct MemoryInfo
|
||||
{
|
||||
public long BaseAddress;
|
||||
public long Size;
|
||||
public int MemType;
|
||||
public int MemAttr;
|
||||
public int MemPerm;
|
||||
public int IpcRefCount;
|
||||
public int DeviceRefCount;
|
||||
public int Padding; //SBZ
|
||||
|
||||
public MemoryInfo(AMemoryMapInfo MapInfo)
|
||||
{
|
||||
BaseAddress = MapInfo.Position;
|
||||
Size = MapInfo.Size;
|
||||
MemType = MapInfo.Type;
|
||||
MemAttr = MapInfo.Attr;
|
||||
MemPerm = (int)MapInfo.Perm;
|
||||
IpcRefCount = 0;
|
||||
DeviceRefCount = 0;
|
||||
Padding = 0;
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Core/OsHle/MemoryType.cs
Normal file
25
Ryujinx.Core/OsHle/MemoryType.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
enum MemoryType
|
||||
{
|
||||
Unmapped = 0,
|
||||
Io = 1,
|
||||
Normal = 2,
|
||||
CodeStatic = 3,
|
||||
CodeMutable = 4,
|
||||
Heap = 5,
|
||||
SharedMemory = 6,
|
||||
ModCodeStatic = 8,
|
||||
ModCodeMutable = 9,
|
||||
IpcBuffer0 = 10,
|
||||
MappedMemory = 11,
|
||||
ThreadLocal = 12,
|
||||
TransferMemoryIsolated = 13,
|
||||
TransferMemory = 14,
|
||||
ProcessMemory = 15,
|
||||
Reserved = 16,
|
||||
IpcBuffer1 = 17,
|
||||
IpcBuffer3 = 18,
|
||||
KernelStack = 19
|
||||
}
|
||||
}
|
122
Ryujinx.Core/OsHle/Mutex.cs
Normal file
122
Ryujinx.Core/OsHle/Mutex.cs
Normal file
|
@ -0,0 +1,122 @@
|
|||
using Ryujinx.Core.OsHle.Handles;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
public class Mutex
|
||||
{
|
||||
private const int MutexHasListenersMask = 0x40000000;
|
||||
|
||||
private Process Process;
|
||||
|
||||
private long MutexAddress;
|
||||
|
||||
private bool OwnsMutexValue;
|
||||
|
||||
private object EnterWaitLock;
|
||||
|
||||
private ConcurrentQueue<HThread> WaitingThreads;
|
||||
|
||||
public Mutex(Process Process, long MutexAddress, int OwnerThreadHandle)
|
||||
{
|
||||
this.Process = Process;
|
||||
this.MutexAddress = MutexAddress;
|
||||
|
||||
//Process.Memory.WriteInt32(MutexAddress, OwnerThreadHandle);
|
||||
|
||||
EnterWaitLock = new object();
|
||||
|
||||
WaitingThreads = new ConcurrentQueue<HThread>();
|
||||
}
|
||||
|
||||
public void WaitForLock(HThread RequestingThread, int RequestingThreadHandle)
|
||||
{
|
||||
AcquireMutexValue();
|
||||
|
||||
lock (EnterWaitLock)
|
||||
{
|
||||
int CurrentThreadHandle = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
|
||||
|
||||
if (CurrentThreadHandle == RequestingThreadHandle ||
|
||||
CurrentThreadHandle == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Process.Memory.WriteInt32(MutexAddress, CurrentThreadHandle | MutexHasListenersMask);
|
||||
|
||||
ReleaseMutexValue();
|
||||
|
||||
WaitingThreads.Enqueue(RequestingThread);
|
||||
}
|
||||
|
||||
Process.Scheduler.WaitForSignal(RequestingThread);
|
||||
}
|
||||
|
||||
public void GiveUpLock(int ThreadHandle)
|
||||
{
|
||||
AcquireMutexValue();
|
||||
|
||||
lock (EnterWaitLock)
|
||||
{
|
||||
int CurrentThread = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
|
||||
|
||||
if (CurrentThread == ThreadHandle)
|
||||
{
|
||||
Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseMutexValue();
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
AcquireMutexValue();
|
||||
|
||||
lock (EnterWaitLock)
|
||||
{
|
||||
int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0;
|
||||
|
||||
Process.Memory.WriteInt32(MutexAddress, HasListeners);
|
||||
|
||||
ReleaseMutexValue();
|
||||
|
||||
HThread[] UnlockedThreads = new HThread[WaitingThreads.Count];
|
||||
|
||||
int Index = 0;
|
||||
|
||||
while (WaitingThreads.TryDequeue(out HThread Thread))
|
||||
{
|
||||
UnlockedThreads[Index++] = Thread;
|
||||
}
|
||||
|
||||
Process.Scheduler.Signal(UnlockedThreads);
|
||||
}
|
||||
}
|
||||
|
||||
private void AcquireMutexValue()
|
||||
{
|
||||
if (!OwnsMutexValue)
|
||||
{
|
||||
while (!Process.Memory.AcquireAddress(MutexAddress))
|
||||
{
|
||||
Thread.Yield();
|
||||
}
|
||||
|
||||
OwnsMutexValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseMutexValue()
|
||||
{
|
||||
if (OwnsMutexValue)
|
||||
{
|
||||
OwnsMutexValue = false;
|
||||
|
||||
Process.Memory.ReleaseAddress(MutexAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx.Core/OsHle/Objects/Acc/IManagerForApplication.cs
Normal file
33
Ryujinx.Core/OsHle/Objects/Acc/IManagerForApplication.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Acc
|
||||
{
|
||||
class IManagerForApplication : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IManagerForApplication()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, CheckAvailability },
|
||||
{ 1, GetAccountId }
|
||||
};
|
||||
}
|
||||
|
||||
public long CheckAvailability(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetAccountId(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(0xcafeL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx.Core/OsHle/Objects/Acc/IProfile.cs
Normal file
33
Ryujinx.Core/OsHle/Objects/Acc/IProfile.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Acc
|
||||
{
|
||||
class IProfile : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IProfile()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 1, GetBase }
|
||||
};
|
||||
}
|
||||
|
||||
public long GetBase(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(0L);
|
||||
Context.ResponseData.Write(0L);
|
||||
Context.ResponseData.Write(0L);
|
||||
Context.ResponseData.Write(0L);
|
||||
Context.ResponseData.Write(0L);
|
||||
Context.ResponseData.Write(0L);
|
||||
Context.ResponseData.Write(0L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
80
Ryujinx.Core/OsHle/Objects/Am/IApplicationFunctions.cs
Normal file
80
Ryujinx.Core/OsHle/Objects/Am/IApplicationFunctions.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class IApplicationFunctions : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IApplicationFunctions()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 1, PopLaunchParameter },
|
||||
{ 20, EnsureSaveData },
|
||||
{ 21, GetDesiredLanguage },
|
||||
{ 40, NotifyRunning }
|
||||
};
|
||||
}
|
||||
|
||||
private const uint LaunchParamsMagic = 0xc79497ca;
|
||||
|
||||
public long PopLaunchParameter(ServiceCtx Context)
|
||||
{
|
||||
//Only the first 0x18 bytes of the Data seems to be actually used.
|
||||
MakeObject(Context, new IStorage(MakeLaunchParams()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long EnsureSaveData(ServiceCtx Context)
|
||||
{
|
||||
long UIdLow = Context.RequestData.ReadInt64();
|
||||
long UIdHigh = Context.RequestData.ReadInt64();
|
||||
|
||||
Context.ResponseData.Write(0L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetDesiredLanguage(ServiceCtx Context)
|
||||
{
|
||||
//This is an enumerator where each number is a differnet language.
|
||||
//0 is Japanese and 1 is English, need to figure out the other codes.
|
||||
Context.ResponseData.Write(1L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long NotifyRunning(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte[] MakeLaunchParams()
|
||||
{
|
||||
//Size needs to be at least 0x88 bytes otherwise application errors.
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
MS.SetLength(0x88);
|
||||
|
||||
Writer.Write(LaunchParamsMagic);
|
||||
Writer.Write(1); //IsAccountSelected? Only lower 8 bits actually used.
|
||||
Writer.Write(1L); //User Id Low (note: User Id needs to be != 0)
|
||||
Writer.Write(0L); //User Id High
|
||||
|
||||
return MS.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
85
Ryujinx.Core/OsHle/Objects/Am/IApplicationProxy.cs
Normal file
85
Ryujinx.Core/OsHle/Objects/Am/IApplicationProxy.cs
Normal file
|
@ -0,0 +1,85 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class IApplicationProxy : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IApplicationProxy()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, GetCommonStateGetter },
|
||||
{ 1, GetSelfController },
|
||||
{ 2, GetWindowController },
|
||||
{ 3, GetAudioController },
|
||||
{ 4, GetDisplayController },
|
||||
{ 11, GetLibraryAppletCreator },
|
||||
{ 20, GetApplicationFunctions },
|
||||
{ 1000, GetDebugFunctions }
|
||||
};
|
||||
}
|
||||
|
||||
public long GetCommonStateGetter(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ICommonStateGetter());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetSelfController(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ISelfController());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetWindowController(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IWindowController());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetAudioController(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IAudioController());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetDisplayController(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IDisplayController());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetLibraryAppletCreator(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ILibraryAppletCreator());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetApplicationFunctions(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IApplicationFunctions());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetDebugFunctions(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IDebugFunctions());
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Objects/Am/IAudioController.cs
Normal file
20
Ryujinx.Core/OsHle/Objects/Am/IAudioController.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class IAudioController : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IAudioController()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
74
Ryujinx.Core/OsHle/Objects/Am/ICommonStateGetter.cs
Normal file
74
Ryujinx.Core/OsHle/Objects/Am/ICommonStateGetter.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class ICommonStateGetter : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public ICommonStateGetter()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, GetEventHandle },
|
||||
{ 1, ReceiveMessage },
|
||||
{ 5, GetOperationMode },
|
||||
{ 6, GetPerformanceMode },
|
||||
{ 9, GetCurrentFocusState },
|
||||
};
|
||||
}
|
||||
|
||||
private enum FocusState
|
||||
{
|
||||
InFocus = 1,
|
||||
OutOfFocus = 2
|
||||
}
|
||||
|
||||
private enum OperationMode
|
||||
{
|
||||
Handheld = 0,
|
||||
Docked = 1
|
||||
}
|
||||
|
||||
public long GetEventHandle(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(0L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long ReceiveMessage(ServiceCtx Context)
|
||||
{
|
||||
//Program expects 0xF at 0x17ae70 on puyo sdk,
|
||||
//otherwise runs on a infinite loop until it reads said value.
|
||||
//What it means is still unknown.
|
||||
Context.ResponseData.Write(0xfL);
|
||||
|
||||
return 0; //0x680;
|
||||
}
|
||||
|
||||
public long GetOperationMode(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write((byte)OperationMode.Handheld);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetPerformanceMode(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write((byte)0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetCurrentFocusState(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write((byte)FocusState.InFocus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Objects/Am/IDebugFunctions.cs
Normal file
20
Ryujinx.Core/OsHle/Objects/Am/IDebugFunctions.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class IDebugFunctions : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IDebugFunctions()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Objects/Am/IDisplayController.cs
Normal file
20
Ryujinx.Core/OsHle/Objects/Am/IDisplayController.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class IDisplayController : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IDisplayController()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Objects/Am/ILibraryAppletCreator.cs
Normal file
20
Ryujinx.Core/OsHle/Objects/Am/ILibraryAppletCreator.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class ILibraryAppletCreator : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public ILibraryAppletCreator()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Objects/Am/IParentalControlService.cs
Normal file
20
Ryujinx.Core/OsHle/Objects/Am/IParentalControlService.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class IParentalControlService : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IParentalControlService()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
53
Ryujinx.Core/OsHle/Objects/Am/ISelfController.cs
Normal file
53
Ryujinx.Core/OsHle/Objects/Am/ISelfController.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class ISelfController : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public ISelfController()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 11, SetOperationModeChangedNotification },
|
||||
{ 12, SetPerformanceModeChangedNotification },
|
||||
{ 13, SetFocusHandlingMode },
|
||||
{ 16, SetOutOfFocusSuspendingEnabled }
|
||||
};
|
||||
}
|
||||
|
||||
public long SetOperationModeChangedNotification(ServiceCtx Context)
|
||||
{
|
||||
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long SetPerformanceModeChangedNotification(ServiceCtx Context)
|
||||
{
|
||||
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long SetFocusHandlingMode(ServiceCtx Context)
|
||||
{
|
||||
bool Flag1 = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||
bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||
bool Flag3 = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long SetOutOfFocusSuspendingEnabled(ServiceCtx Context)
|
||||
{
|
||||
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx.Core/OsHle/Objects/Am/IStorage.cs
Normal file
33
Ryujinx.Core/OsHle/Objects/Am/IStorage.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class IStorage : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
public IStorage(byte[] Data)
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, Open }
|
||||
};
|
||||
|
||||
this.Data = Data;
|
||||
}
|
||||
|
||||
public long Open(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IStorageAccessor(this));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
62
Ryujinx.Core/OsHle/Objects/Am/IStorageAccessor.cs
Normal file
62
Ryujinx.Core/OsHle/Objects/Am/IStorageAccessor.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class IStorageAccessor : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
private IStorage Storage;
|
||||
|
||||
public IStorageAccessor(IStorage Storage)
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, GetSize },
|
||||
{ 11, Read }
|
||||
};
|
||||
|
||||
this.Storage = Storage;
|
||||
}
|
||||
|
||||
public long GetSize(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write((long)Storage.Data.Length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long Read(ServiceCtx Context)
|
||||
{
|
||||
long ReadPosition = Context.RequestData.ReadInt64();
|
||||
|
||||
if (Context.Request.RecvListBuff.Count > 0)
|
||||
{
|
||||
long Position = Context.Request.RecvListBuff[0].Position;
|
||||
short Size = Context.Request.RecvListBuff[0].Size;
|
||||
|
||||
byte[] Data;
|
||||
|
||||
if (Storage.Data.Length > Size)
|
||||
{
|
||||
Data = new byte[Size];
|
||||
|
||||
Buffer.BlockCopy(Storage.Data, 0, Data, 0, Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Data = Storage.Data;
|
||||
}
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, Position, Data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx.Core/OsHle/Objects/Am/IWindowController.cs
Normal file
33
Ryujinx.Core/OsHle/Objects/Am/IWindowController.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Am
|
||||
{
|
||||
class IWindowController : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IWindowController()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 1, GetAppletResourceUserId },
|
||||
{ 10, AcquireForegroundRights }
|
||||
};
|
||||
}
|
||||
|
||||
public long GetAppletResourceUserId(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(0L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long AcquireForegroundRights(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
28
Ryujinx.Core/OsHle/Objects/Apm/ISession.cs
Normal file
28
Ryujinx.Core/OsHle/Objects/Apm/ISession.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Apm
|
||||
{
|
||||
class ISession : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public ISession()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, SetPerformanceConfiguration }
|
||||
};
|
||||
}
|
||||
|
||||
public long SetPerformanceConfiguration(ServiceCtx Context)
|
||||
{
|
||||
int PerfMode = Context.RequestData.ReadInt32();
|
||||
int PerfConfig = Context.RequestData.ReadInt32();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
180
Ryujinx.Core/OsHle/Objects/Aud/IAudioOut.cs
Normal file
180
Ryujinx.Core/OsHle/Objects/Aud/IAudioOut.cs
Normal file
|
@ -0,0 +1,180 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using OpenTK.Audio;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Aud
|
||||
{
|
||||
class IAudioOut : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IAudioOut()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, GetAudioOutState },
|
||||
{ 1, StartAudioOut },
|
||||
{ 2, StopAudioOut },
|
||||
{ 3, AppendAudioOutBuffer },
|
||||
{ 4, RegisterBufferEvent },
|
||||
{ 5, GetReleasedAudioOutBuffer },
|
||||
{ 6, ContainsAudioOutBuffer },
|
||||
{ 7, AppendAudioOutBuffer_ex },
|
||||
{ 8, GetReleasedAudioOutBuffer_ex }
|
||||
};
|
||||
}
|
||||
|
||||
enum AudioOutState
|
||||
{
|
||||
Started,
|
||||
Stopped
|
||||
};
|
||||
|
||||
//IAudioOut
|
||||
private AudioOutState State = AudioOutState.Stopped;
|
||||
private Queue<long> KeysQueue = new Queue<long>();
|
||||
|
||||
//OpenAL
|
||||
private bool OpenALInstalled = true;
|
||||
private AudioContext AudioCtx;
|
||||
private int Source;
|
||||
private int Buffer;
|
||||
|
||||
//Return State of IAudioOut
|
||||
public long GetAudioOutState(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write((int)State);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long StartAudioOut(ServiceCtx Context)
|
||||
{
|
||||
if (State == AudioOutState.Stopped)
|
||||
{
|
||||
State = AudioOutState.Started;
|
||||
|
||||
try
|
||||
{
|
||||
AudioCtx = new AudioContext(); //Create the audio context
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logging.Warn("OpenAL Error! PS: Install OpenAL Core SDK!");
|
||||
OpenALInstalled = false;
|
||||
}
|
||||
|
||||
if (OpenALInstalled) AL.Listener(ALListenerf.Gain, (float)8.0); //Add more gain to it
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long StopAudioOut(ServiceCtx Context)
|
||||
{
|
||||
if (State == AudioOutState.Started)
|
||||
{
|
||||
if (OpenALInstalled)
|
||||
{
|
||||
if (AudioCtx == null) //Needed to call the instance of AudioContext()
|
||||
return 0;
|
||||
|
||||
AL.SourceStop(Source);
|
||||
AL.DeleteSource(Source);
|
||||
}
|
||||
State = AudioOutState.Stopped;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long AppendAudioOutBuffer(ServiceCtx Context)
|
||||
{
|
||||
long BufferId = Context.RequestData.ReadInt64();
|
||||
|
||||
KeysQueue.Enqueue(BufferId);
|
||||
|
||||
byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5);
|
||||
using (MemoryStream MS = new MemoryStream(AudioOutBuffer))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
long PointerNextBuffer = Reader.ReadInt64();
|
||||
long PointerSampleBuffer = Reader.ReadInt64();
|
||||
long CapacitySampleBuffer = Reader.ReadInt64();
|
||||
long SizeDataInSampleBuffer = Reader.ReadInt64();
|
||||
long OffsetDataInSampleBuffer = Reader.ReadInt64();
|
||||
|
||||
byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer);
|
||||
|
||||
if (OpenALInstalled)
|
||||
{
|
||||
if (AudioCtx == null) //Needed to call the instance of AudioContext()
|
||||
return 0;
|
||||
|
||||
Buffer = AL.GenBuffer();
|
||||
AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000);
|
||||
|
||||
Source = AL.GenSource();
|
||||
AL.SourceQueueBuffer(Source, Buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long RegisterBufferEvent(ServiceCtx Context)
|
||||
{
|
||||
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
|
||||
|
||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetReleasedAudioOutBuffer(ServiceCtx Context)
|
||||
{
|
||||
long TempKey = 0;
|
||||
|
||||
if (KeysQueue.Count > 0) TempKey = KeysQueue.Dequeue();
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, BitConverter.GetBytes(TempKey));
|
||||
|
||||
int ReleasedBuffersCount = 1;
|
||||
Context.ResponseData.Write(ReleasedBuffersCount);
|
||||
|
||||
if (OpenALInstalled)
|
||||
{
|
||||
if (AudioCtx == null) //Needed to call the instance of AudioContext()
|
||||
return 0;
|
||||
|
||||
AL.SourcePlay(Source);
|
||||
int[] FreeBuffers = AL.SourceUnqueueBuffers(Source, 1);
|
||||
AL.DeleteBuffers(FreeBuffers);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long ContainsAudioOutBuffer(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long AppendAudioOutBuffer_ex(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetReleasedAudioOutBuffer_ex(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
66
Ryujinx.Core/OsHle/Objects/Aud/IAudioRenderer.cs
Normal file
66
Ryujinx.Core/OsHle/Objects/Aud/IAudioRenderer.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Aud
|
||||
{
|
||||
class IAudioRenderer : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IAudioRenderer()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 4, RequestUpdateAudioRenderer },
|
||||
{ 5, StartAudioRenderer },
|
||||
{ 6, StopAudioRenderer },
|
||||
{ 7, QuerySystemEvent }
|
||||
};
|
||||
}
|
||||
|
||||
public long RequestUpdateAudioRenderer(ServiceCtx Context)
|
||||
{
|
||||
//(buffer<unknown, 5, 0>) -> (buffer<unknown, 6, 0>, buffer<unknown, 6, 0>)
|
||||
|
||||
long Position = Context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
//0x40 bytes header
|
||||
Context.Memory.WriteInt32(Position + 0x4, 0xb0); //Behavior Out State Size? (note: this is the last section)
|
||||
Context.Memory.WriteInt32(Position + 0x8, 0x18e0); //Memory Pool Out State Size?
|
||||
Context.Memory.WriteInt32(Position + 0xc, 0x600); //Voice Out State Size?
|
||||
Context.Memory.WriteInt32(Position + 0x14, 0xe0); //Effect Out State Size?
|
||||
Context.Memory.WriteInt32(Position + 0x1c, 0x20); //Sink Out State Size?
|
||||
Context.Memory.WriteInt32(Position + 0x20, 0x10); //Performance Out State Size?
|
||||
Context.Memory.WriteInt32(Position + 0x3c, 0x20e0); //Total Size (including 0x40 bytes header)
|
||||
|
||||
for (int Offset = 0x40; Offset < 0x40 + 0x18e0; Offset += 0x10)
|
||||
{
|
||||
Context.Memory.WriteInt32(Position + Offset, 5);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long StartAudioRenderer(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long StopAudioRenderer(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long QuerySystemEvent(ServiceCtx Context)
|
||||
{
|
||||
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
|
||||
|
||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Objects/Friend/IFriendService.cs
Normal file
20
Ryujinx.Core/OsHle/Objects/Friend/IFriendService.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Friend
|
||||
{
|
||||
class IFriendService : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IFriendService()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
133
Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs
Normal file
133
Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs
Normal file
|
@ -0,0 +1,133 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.FspSrv
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x310)]
|
||||
struct DirectoryEntry
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x300)]
|
||||
public byte[] Name;
|
||||
public int Unknown;
|
||||
public byte Type;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3)]
|
||||
public byte[] Padding;
|
||||
public long Size;
|
||||
}
|
||||
|
||||
enum DirectoryEntryType
|
||||
{
|
||||
Directory,
|
||||
File
|
||||
}
|
||||
|
||||
class IDirectory : IIpcInterface
|
||||
{
|
||||
private List<DirectoryEntry> DirectoryEntries = new List<DirectoryEntry>();
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
private string HostPath;
|
||||
|
||||
public IDirectory(string HostPath, int flags)
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, Read },
|
||||
{ 1, GetEntryCount }
|
||||
};
|
||||
|
||||
this.HostPath = HostPath;
|
||||
|
||||
if ((flags & 1) == 1)
|
||||
{
|
||||
string[] Directories = Directory.GetDirectories(HostPath, "*", SearchOption.TopDirectoryOnly).
|
||||
Where(x => (new FileInfo(x).Attributes & FileAttributes.Hidden) == 0).ToArray();
|
||||
|
||||
foreach (string Directory in Directories)
|
||||
{
|
||||
DirectoryEntry Info = new DirectoryEntry
|
||||
{
|
||||
Name = Encoding.UTF8.GetBytes(Directory),
|
||||
Type = (byte)DirectoryEntryType.Directory,
|
||||
Size = 0
|
||||
};
|
||||
|
||||
Array.Resize(ref Info.Name, 0x300);
|
||||
DirectoryEntries.Add(Info);
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & 2) == 2)
|
||||
{
|
||||
string[] Files = Directory.GetFiles(HostPath, "*", SearchOption.TopDirectoryOnly).
|
||||
Where(x => (new FileInfo(x).Attributes & FileAttributes.Hidden) == 0).ToArray();
|
||||
|
||||
foreach (string FileName in Files)
|
||||
{
|
||||
DirectoryEntry Info = new DirectoryEntry
|
||||
{
|
||||
Name = Encoding.UTF8.GetBytes(Path.GetFileName(FileName)),
|
||||
Type = (byte)DirectoryEntryType.File,
|
||||
Size = new FileInfo(Path.Combine(HostPath, FileName)).Length
|
||||
};
|
||||
|
||||
Array.Resize(ref Info.Name, 0x300);
|
||||
DirectoryEntries.Add(Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int LastItem = 0;
|
||||
public long Read(ServiceCtx Context)
|
||||
{
|
||||
long BufferPosition = Context.Request.ReceiveBuff[0].Position;
|
||||
long BufferLen = Context.Request.ReceiveBuff[0].Size;
|
||||
long MaxDirectories = BufferLen / Marshal.SizeOf(typeof(DirectoryEntry));
|
||||
|
||||
if (MaxDirectories > DirectoryEntries.Count - LastItem)
|
||||
{
|
||||
MaxDirectories = DirectoryEntries.Count - LastItem;
|
||||
}
|
||||
|
||||
int CurrentIndex;
|
||||
for (CurrentIndex = 0; CurrentIndex < MaxDirectories; CurrentIndex++)
|
||||
{
|
||||
int CurrentItem = LastItem + CurrentIndex;
|
||||
|
||||
byte[] DirectoryEntry = new byte[Marshal.SizeOf(typeof(DirectoryEntry))];
|
||||
IntPtr Ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DirectoryEntry)));
|
||||
Marshal.StructureToPtr(DirectoryEntries[CurrentItem], Ptr, true);
|
||||
Marshal.Copy(Ptr, DirectoryEntry, 0, Marshal.SizeOf(typeof(DirectoryEntry)));
|
||||
Marshal.FreeHGlobal(Ptr);
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, BufferPosition + Marshal.SizeOf(typeof(DirectoryEntry)) * CurrentIndex, DirectoryEntry);
|
||||
}
|
||||
|
||||
if (LastItem < DirectoryEntries.Count)
|
||||
{
|
||||
LastItem += CurrentIndex;
|
||||
Context.ResponseData.Write((long)CurrentIndex); // index = number of entries written this call.
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.ResponseData.Write((long)0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetEntryCount(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write((long)DirectoryEntries.Count);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
93
Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs
Normal file
93
Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs
Normal file
|
@ -0,0 +1,93 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.FspSrv
|
||||
{
|
||||
class IFile : IIpcInterface, IDisposable
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
private Stream BaseStream;
|
||||
|
||||
public IFile(Stream BaseStream)
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, Read },
|
||||
{ 1, Write },
|
||||
// { 2, Flush },
|
||||
{ 3, SetSize },
|
||||
{ 4, GetSize }
|
||||
};
|
||||
|
||||
this.BaseStream = BaseStream;
|
||||
}
|
||||
|
||||
public long Read(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
long Zero = Context.RequestData.ReadInt64();
|
||||
long Offset = Context.RequestData.ReadInt64();
|
||||
long Size = Context.RequestData.ReadInt64();
|
||||
|
||||
byte[] Data = new byte[Size];
|
||||
|
||||
BaseStream.Seek(Offset, SeekOrigin.Begin);
|
||||
int ReadSize = BaseStream.Read(Data, 0, (int)Size);
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, Position, Data);
|
||||
|
||||
Context.ResponseData.Write((long)ReadSize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long Write(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.SendBuff[0].Position;
|
||||
|
||||
long Zero = Context.RequestData.ReadInt64();
|
||||
long Offset = Context.RequestData.ReadInt64();
|
||||
long Size = Context.RequestData.ReadInt64();
|
||||
|
||||
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, Position, (int)Size);
|
||||
|
||||
BaseStream.Seek(Offset, SeekOrigin.Begin);
|
||||
BaseStream.Write(Data, 0, (int)Size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetSize(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(BaseStream.Length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long SetSize(ServiceCtx Context)
|
||||
{
|
||||
long Size = Context.RequestData.ReadInt64();
|
||||
BaseStream.SetLength(Size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && BaseStream != null)
|
||||
{
|
||||
BaseStream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
247
Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs
Normal file
247
Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs
Normal file
|
@ -0,0 +1,247 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.FspSrv
|
||||
{
|
||||
class IFileSystem : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
private string Path;
|
||||
|
||||
public IFileSystem(string Path)
|
||||
{
|
||||
//TODO: implement.
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, CreateFile },
|
||||
{ 1, DeleteFile },
|
||||
{ 2, CreateDirectory },
|
||||
{ 3, DeleteDirectory },
|
||||
{ 4, DeleteDirectoryRecursively },
|
||||
{ 5, RenameFile },
|
||||
{ 6, RenameDirectory },
|
||||
{ 7, GetEntryType },
|
||||
{ 8, OpenFile },
|
||||
{ 9, OpenDirectory },
|
||||
{ 10, Commit },
|
||||
//{ 11, GetFreeSpaceSize },
|
||||
//{ 12, GetTotalSpaceSize },
|
||||
//{ 13, CleanDirectoryRecursively },
|
||||
//{ 14, GetFileTimeStampRaw }
|
||||
};
|
||||
|
||||
this.Path = Path;
|
||||
}
|
||||
|
||||
public long CreateFile(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
||||
ulong Mode = Context.RequestData.ReadUInt64();
|
||||
uint Size = Context.RequestData.ReadUInt32();
|
||||
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
||||
|
||||
if (FileName != null)
|
||||
{
|
||||
FileStream NewFile = File.Create(FileName);
|
||||
NewFile.SetLength(Size);
|
||||
NewFile.Close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long DeleteFile(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
||||
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
||||
|
||||
if (FileName != null)
|
||||
{
|
||||
File.Delete(FileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long CreateDirectory(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
||||
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
||||
|
||||
if (FileName != null)
|
||||
{
|
||||
Directory.CreateDirectory(FileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long DeleteDirectory(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
||||
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
||||
|
||||
if (FileName != null)
|
||||
{
|
||||
Directory.Delete(FileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long DeleteDirectoryRecursively(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
||||
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
||||
|
||||
if (FileName != null)
|
||||
{
|
||||
Directory.Delete(FileName, true); // recursive = true
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long RenameFile(ServiceCtx Context)
|
||||
{
|
||||
long OldPosition = Context.Request.PtrBuff[0].Position;
|
||||
long NewPosition = Context.Request.PtrBuff[0].Position;
|
||||
string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition);
|
||||
string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition);
|
||||
string OldFileName = Context.Ns.VFs.GetFullPath(Path, OldName);
|
||||
string NewFileName = Context.Ns.VFs.GetFullPath(Path, NewName);
|
||||
|
||||
if (OldFileName != null && NewFileName != null)
|
||||
{
|
||||
File.Move(OldFileName, NewFileName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long RenameDirectory(ServiceCtx Context)
|
||||
{
|
||||
long OldPosition = Context.Request.PtrBuff[0].Position;
|
||||
long NewPosition = Context.Request.PtrBuff[0].Position;
|
||||
string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition);
|
||||
string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition);
|
||||
string OldDirName = Context.Ns.VFs.GetFullPath(Path, OldName);
|
||||
string NewDirName = Context.Ns.VFs.GetFullPath(Path, NewName);
|
||||
|
||||
if (OldDirName != null && NewDirName != null)
|
||||
{
|
||||
Directory.Move(OldDirName, NewDirName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long GetEntryType(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
||||
|
||||
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
||||
|
||||
if (FileName == null)
|
||||
{
|
||||
//TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool IsFile = File.Exists(FileName);
|
||||
|
||||
Context.ResponseData.Write(IsFile ? 1 : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long OpenFile(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
int FilterFlags = Context.RequestData.ReadInt32();
|
||||
|
||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
||||
|
||||
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
||||
|
||||
if (FileName == null)
|
||||
{
|
||||
//TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (File.Exists(FileName))
|
||||
{
|
||||
FileStream Stream = new FileStream(FileName, FileMode.OpenOrCreate);
|
||||
MakeObject(Context, new IFile(Stream));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long OpenDirectory(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
int FilterFlags = Context.RequestData.ReadInt32();
|
||||
|
||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
||||
|
||||
string DirName = Context.Ns.VFs.GetFullPath(Path, Name);
|
||||
|
||||
if(DirName != null)
|
||||
{
|
||||
if (Directory.Exists(DirName))
|
||||
{
|
||||
MakeObject(Context, new IDirectory(DirName, FilterFlags));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: correct error code.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Correct error code.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long Commit(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
52
Ryujinx.Core/OsHle/Objects/FspSrv/IStorage.cs
Normal file
52
Ryujinx.Core/OsHle/Objects/FspSrv/IStorage.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.FspSrv
|
||||
{
|
||||
class IStorage : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
private Stream BaseStream;
|
||||
|
||||
public IStorage(Stream BaseStream)
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, Read }
|
||||
};
|
||||
|
||||
this.BaseStream = BaseStream;
|
||||
}
|
||||
|
||||
public long Read(ServiceCtx Context)
|
||||
{
|
||||
long Offset = Context.RequestData.ReadInt64();
|
||||
long Size = Context.RequestData.ReadInt64();
|
||||
|
||||
if (Context.Request.ReceiveBuff.Count > 0)
|
||||
{
|
||||
IpcBuffDesc BuffDesc = Context.Request.ReceiveBuff[0];
|
||||
|
||||
//Use smaller length to avoid overflows.
|
||||
if (Size > BuffDesc.Size)
|
||||
{
|
||||
Size = BuffDesc.Size;
|
||||
}
|
||||
|
||||
byte[] Data = new byte[Size];
|
||||
|
||||
BaseStream.Seek(Offset, SeekOrigin.Begin);
|
||||
BaseStream.Read(Data, 0, Data.Length);
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, BuffDesc.Position, Data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
32
Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs
Normal file
32
Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Hid
|
||||
{
|
||||
class IAppletResource : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public HSharedMem Handle;
|
||||
|
||||
public IAppletResource(HSharedMem Handle)
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, GetSharedMemoryHandle }
|
||||
};
|
||||
|
||||
this.Handle = Handle;
|
||||
}
|
||||
|
||||
public static long GetSharedMemoryHandle(ServiceCtx Context)
|
||||
{
|
||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.HidHandle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
10
Ryujinx.Core/OsHle/Objects/IIpcInterface.cs
Normal file
10
Ryujinx.Core/OsHle/Objects/IIpcInterface.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects
|
||||
{
|
||||
interface IIpcInterface
|
||||
{
|
||||
IReadOnlyDictionary<int, ServiceProcessRequest> Commands { get; }
|
||||
}
|
||||
}
|
24
Ryujinx.Core/OsHle/Objects/ObjHelper.cs
Normal file
24
Ryujinx.Core/OsHle/Objects/ObjHelper.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects
|
||||
{
|
||||
static class ObjHelper
|
||||
{
|
||||
public static void MakeObject(ServiceCtx Context, object Obj)
|
||||
{
|
||||
if (Context.Session is HDomain Dom)
|
||||
{
|
||||
Context.Response.ResponseObjIds.Add(Dom.GenerateObjectId(Obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
HSessionObj HndData = new HSessionObj(Context.Session, Obj);
|
||||
|
||||
int VHandle = Context.Ns.Os.Handles.GenerateId(HndData);
|
||||
|
||||
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
58
Ryujinx.Core/OsHle/Objects/Parcel.cs
Normal file
58
Ryujinx.Core/OsHle/Objects/Parcel.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Android
|
||||
{
|
||||
static class Parcel
|
||||
{
|
||||
public static byte[] GetParcelData(byte[] Parcel)
|
||||
{
|
||||
if (Parcel == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Parcel));
|
||||
}
|
||||
|
||||
using (MemoryStream MS = new MemoryStream(Parcel))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
|
||||
int DataSize = Reader.ReadInt32();
|
||||
int DataOffset = Reader.ReadInt32();
|
||||
int ObjsSize = Reader.ReadInt32();
|
||||
int ObjsOffset = Reader.ReadInt32();
|
||||
|
||||
MS.Seek(DataOffset - 0x10, SeekOrigin.Current);
|
||||
|
||||
return Reader.ReadBytes(DataSize);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] MakeParcel(byte[] Data, byte[] Objs)
|
||||
{
|
||||
if (Data == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Data));
|
||||
}
|
||||
|
||||
if (Objs == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Objs));
|
||||
}
|
||||
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
Writer.Write(Data.Length);
|
||||
Writer.Write(0x10);
|
||||
Writer.Write(Objs.Length);
|
||||
Writer.Write(Data.Length + 0x10);
|
||||
|
||||
Writer.Write(Data);
|
||||
Writer.Write(Objs);
|
||||
|
||||
return MS.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Objects/Time/ISteadyClock.cs
Normal file
20
Ryujinx.Core/OsHle/Objects/Time/ISteadyClock.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Time
|
||||
{
|
||||
class ISteadyClock : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public ISteadyClock()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
42
Ryujinx.Core/OsHle/Objects/Time/ISystemClock.cs
Normal file
42
Ryujinx.Core/OsHle/Objects/Time/ISystemClock.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Time
|
||||
{
|
||||
class ISystemClock : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
private SystemClockType ClockType;
|
||||
|
||||
public ISystemClock(SystemClockType ClockType)
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, GetCurrentTime }
|
||||
};
|
||||
|
||||
this.ClockType = ClockType;
|
||||
}
|
||||
|
||||
public long GetCurrentTime(ServiceCtx Context)
|
||||
{
|
||||
DateTime CurrentTime = DateTime.Now;
|
||||
|
||||
if (ClockType == SystemClockType.User ||
|
||||
ClockType == SystemClockType.Network)
|
||||
{
|
||||
CurrentTime = CurrentTime.ToUniversalTime();
|
||||
}
|
||||
|
||||
Context.ResponseData.Write((long)(DateTime.Now - Epoch).TotalSeconds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Objects/Time/ITimeZoneService.cs
Normal file
20
Ryujinx.Core/OsHle/Objects/Time/ITimeZoneService.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Time
|
||||
{
|
||||
class ITimeZoneService : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public ITimeZoneService()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Core/OsHle/Objects/Time/SystemClockType.cs
Normal file
9
Ryujinx.Core/OsHle/Objects/Time/SystemClockType.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Core.OsHle.Objects.Time
|
||||
{
|
||||
enum SystemClockType
|
||||
{
|
||||
User,
|
||||
Network,
|
||||
Local
|
||||
}
|
||||
}
|
176
Ryujinx.Core/OsHle/Objects/Vi/IApplicationDisplayService.cs
Normal file
176
Ryujinx.Core/OsHle/Objects/Vi/IApplicationDisplayService.cs
Normal file
|
@ -0,0 +1,176 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.Android.Parcel;
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Vi
|
||||
{
|
||||
class IApplicationDisplayService : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IApplicationDisplayService()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 100, GetRelayService },
|
||||
{ 101, GetSystemDisplayService },
|
||||
{ 102, GetManagerDisplayService },
|
||||
{ 103, GetIndirectDisplayTransactionService },
|
||||
{ 1010, OpenDisplay },
|
||||
{ 2020, OpenLayer },
|
||||
{ 2030, CreateStrayLayer },
|
||||
{ 2101, SetLayerScalingMode },
|
||||
{ 5202, GetDisplayVSyncEvent }
|
||||
};
|
||||
}
|
||||
|
||||
public long GetRelayService(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IHOSBinderDriver());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetSystemDisplayService(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ISystemDisplayService());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetManagerDisplayService(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IManagerDisplayService());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetIndirectDisplayTransactionService(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IHOSBinderDriver());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long OpenDisplay(ServiceCtx Context)
|
||||
{
|
||||
string Name = GetDisplayName(Context);
|
||||
|
||||
long DisplayId = Context.Ns.Os.Displays.GenerateId(new Display(Name));
|
||||
|
||||
Context.ResponseData.Write(DisplayId);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long OpenLayer(ServiceCtx Context)
|
||||
{
|
||||
long LayerId = Context.RequestData.ReadInt64();
|
||||
long UserId = Context.RequestData.ReadInt64();
|
||||
|
||||
long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, ParcelPtr, Parcel);
|
||||
|
||||
Context.ResponseData.Write((long)Parcel.Length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long CreateStrayLayer(ServiceCtx Context)
|
||||
{
|
||||
long LayerFlags = Context.RequestData.ReadInt64();
|
||||
long DisplayId = Context.RequestData.ReadInt64();
|
||||
|
||||
long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
Display Disp = Context.Ns.Os.Displays.GetData<Display>((int)DisplayId);
|
||||
|
||||
byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, ParcelPtr, Parcel);
|
||||
|
||||
Context.ResponseData.Write(0L);
|
||||
Context.ResponseData.Write((long)Parcel.Length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long SetLayerScalingMode(ServiceCtx Context)
|
||||
{
|
||||
int ScalingMode = Context.RequestData.ReadInt32();
|
||||
long Unknown = Context.RequestData.ReadInt64();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetDisplayVSyncEvent(ServiceCtx Context)
|
||||
{
|
||||
string Name = GetDisplayName(Context);
|
||||
|
||||
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
|
||||
|
||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte[] MakeIGraphicsBufferProducer(long BasePtr)
|
||||
{
|
||||
long Id = 0x20;
|
||||
long CookiePtr = 0L;
|
||||
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
//flat_binder_object (size is 0x28)
|
||||
Writer.Write(2); //Type (BINDER_TYPE_WEAK_BINDER)
|
||||
Writer.Write(0); //Flags
|
||||
Writer.Write((int)(Id >> 0));
|
||||
Writer.Write((int)(Id >> 32));
|
||||
Writer.Write((int)(CookiePtr >> 0));
|
||||
Writer.Write((int)(CookiePtr >> 32));
|
||||
Writer.Write((byte)'d');
|
||||
Writer.Write((byte)'i');
|
||||
Writer.Write((byte)'s');
|
||||
Writer.Write((byte)'p');
|
||||
Writer.Write((byte)'d');
|
||||
Writer.Write((byte)'r');
|
||||
Writer.Write((byte)'v');
|
||||
Writer.Write((byte)'\0');
|
||||
Writer.Write(0L); //Pad
|
||||
|
||||
return MakeParcel(MS.ToArray(), new byte[] { 0, 0, 0, 0 });
|
||||
}
|
||||
}
|
||||
|
||||
private string GetDisplayName(ServiceCtx Context)
|
||||
{
|
||||
string Name = string.Empty;
|
||||
|
||||
for (int Index = 0; Index < 8 &&
|
||||
Context.RequestData.BaseStream.Position <
|
||||
Context.RequestData.BaseStream.Length; Index++)
|
||||
{
|
||||
byte Chr = Context.RequestData.ReadByte();
|
||||
|
||||
if (Chr >= 0x20 && Chr < 0x7f)
|
||||
{
|
||||
Name += (char)Chr;
|
||||
}
|
||||
}
|
||||
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
214
Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs
Normal file
214
Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs
Normal file
|
@ -0,0 +1,214 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using Ryujinx.Core.OsHle.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.Android.Parcel;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Vi
|
||||
{
|
||||
class IHOSBinderDriver : IIpcInterface
|
||||
{
|
||||
private delegate long ServiceProcessParcel(ServiceCtx Context, byte[] ParcelData);
|
||||
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
private Dictionary<(string, int), ServiceProcessParcel> m_Methods;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
private class BufferObj
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private IdPoolWithObj BufferSlots;
|
||||
|
||||
private byte[] Gbfr;
|
||||
|
||||
public IHOSBinderDriver()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, TransactParcel },
|
||||
{ 1, AdjustRefcount },
|
||||
{ 2, GetNativeHandle }
|
||||
};
|
||||
|
||||
m_Methods = new Dictionary<(string, int), ServiceProcessParcel>()
|
||||
{
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x1), GraphicBufferProducerRequestBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x3), GraphicBufferProducerDequeueBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x7), GraphicBufferProducerQueueBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x8), GraphicBufferProducerCancelBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x9), GraphicBufferProducerQuery },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0xa), GraphicBufferProducerConnect },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0xe), GraphicBufferPreallocateBuffer }
|
||||
};
|
||||
|
||||
BufferSlots = new IdPoolWithObj();
|
||||
}
|
||||
|
||||
public long TransactParcel(ServiceCtx Context)
|
||||
{
|
||||
int Id = Context.RequestData.ReadInt32();
|
||||
int Code = Context.RequestData.ReadInt32();
|
||||
|
||||
long DataPos = Context.Request.SendBuff[0].Position;
|
||||
long DataSize = Context.Request.SendBuff[0].Size;
|
||||
|
||||
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize);
|
||||
|
||||
Data = GetParcelData(Data);
|
||||
|
||||
using (MemoryStream MS = new MemoryStream(Data))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
|
||||
MS.Seek(4, SeekOrigin.Current);
|
||||
|
||||
int StrSize = Reader.ReadInt32();
|
||||
|
||||
string InterfaceName = Encoding.Unicode.GetString(Data, 8, StrSize * 2);
|
||||
|
||||
if (m_Methods.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq))
|
||||
{
|
||||
return ProcReq(Context, Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"{InterfaceName} {Code}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerRequestBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
int GbfrSize = Gbfr?.Length ?? 0;
|
||||
|
||||
byte[] Data = new byte[GbfrSize + 4];
|
||||
|
||||
if (Gbfr != null)
|
||||
{
|
||||
Buffer.BlockCopy(Gbfr, 0, Data, 0, GbfrSize);
|
||||
}
|
||||
|
||||
return MakeReplyParcel(Context, Data);
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerDequeueBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
//Note: It seems that the maximum number of slots is 64, because if we return
|
||||
//a Slot number > 63, it seems to cause a buffer overrun and it reads garbage.
|
||||
//Note 2: The size of each object associated with the slot is 0x30.
|
||||
int Slot = BufferSlots.GenerateId(new BufferObj());
|
||||
|
||||
return MakeReplyParcel(Context, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerQueueBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerCancelBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream(ParcelData))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
|
||||
MS.Seek(0x50, SeekOrigin.Begin);
|
||||
|
||||
int Slot = Reader.ReadInt32();
|
||||
|
||||
BufferSlots.Delete(Slot);
|
||||
|
||||
return MakeReplyParcel(Context, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerQuery(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
return MakeReplyParcel(Context, 0, 0);
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerConnect(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long GraphicBufferPreallocateBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
int GbfrSize = ParcelData.Length - 0x54;
|
||||
|
||||
Gbfr = new byte[GbfrSize];
|
||||
|
||||
Buffer.BlockCopy(ParcelData, 0x54, Gbfr, 0, GbfrSize);
|
||||
|
||||
using (MemoryStream MS = new MemoryStream(ParcelData))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
|
||||
MS.Seek(0xd4, SeekOrigin.Begin);
|
||||
|
||||
int Handle = Reader.ReadInt32();
|
||||
|
||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
||||
|
||||
Context.Ns.Gpu.Renderer.FrameBufferPtr = NvMap.Address;
|
||||
}
|
||||
|
||||
return MakeReplyParcel(Context, 0);
|
||||
}
|
||||
|
||||
private long MakeReplyParcel(ServiceCtx Context, params int[] Ints)
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
foreach (int Int in Ints)
|
||||
{
|
||||
Writer.Write(Int);
|
||||
}
|
||||
|
||||
return MakeReplyParcel(Context, MS.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private long MakeReplyParcel(ServiceCtx Context, byte[] Data)
|
||||
{
|
||||
long ReplyPos = Context.Request.ReceiveBuff[0].Position;
|
||||
long ReplySize = Context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
byte[] Reply = MakeParcel(Data, new byte[0]);
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long AdjustRefcount(ServiceCtx Context)
|
||||
{
|
||||
int Id = Context.RequestData.ReadInt32();
|
||||
int AddVal = Context.RequestData.ReadInt32();
|
||||
int Type = Context.RequestData.ReadInt32();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long GetNativeHandle(ServiceCtx Context)
|
||||
{
|
||||
int Id = Context.RequestData.ReadInt32();
|
||||
uint Unk = Context.RequestData.ReadUInt32();
|
||||
|
||||
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(0xbadcafe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx.Core/OsHle/Objects/Vi/IManagerDisplayService.cs
Normal file
33
Ryujinx.Core/OsHle/Objects/Vi/IManagerDisplayService.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Vi
|
||||
{
|
||||
class IManagerDisplayService : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IManagerDisplayService()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 2010, CreateManagedLayer },
|
||||
{ 6000, AddToLayerStack }
|
||||
};
|
||||
}
|
||||
|
||||
public static long CreateManagedLayer(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(0L); //LayerId
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long AddToLayerStack(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Core/OsHle/Objects/Vi/ISystemDisplayService.cs
Normal file
25
Ryujinx.Core/OsHle/Objects/Vi/ISystemDisplayService.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Vi
|
||||
{
|
||||
class ISystemDisplayService : IIpcInterface
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public ISystemDisplayService()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 2205, SetLayerZ }
|
||||
};
|
||||
}
|
||||
|
||||
public static long SetLayerZ(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
257
Ryujinx.Core/OsHle/Process.cs
Normal file
257
Ryujinx.Core/OsHle/Process.cs
Normal file
|
@ -0,0 +1,257 @@
|
|||
using ChocolArm64;
|
||||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using Ryujinx.Core.Loaders;
|
||||
using Ryujinx.Core.Loaders.Executables;
|
||||
using Ryujinx.Core.OsHle.Exceptions;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Svc;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
public class Process : IDisposable
|
||||
{
|
||||
private const int MaxStackSize = 8 * 1024 * 1024;
|
||||
|
||||
private const int TlsSize = 0x200;
|
||||
private const int TotalTlsSlots = 32;
|
||||
private const int TlsTotalSize = TotalTlsSlots * TlsSize;
|
||||
private const long TlsPageAddr = (AMemoryMgr.AddrSize - TlsTotalSize) & ~AMemoryMgr.PageMask;
|
||||
|
||||
private Switch Ns;
|
||||
|
||||
public int ProcessId { get; private set; }
|
||||
|
||||
public AMemory Memory { get; private set; }
|
||||
|
||||
public KProcessScheduler Scheduler { get; private set; }
|
||||
|
||||
private SvcHandler SvcHandler;
|
||||
|
||||
private ConcurrentDictionary<int, AThread> TlsSlots;
|
||||
|
||||
private ConcurrentDictionary<long, HThread> ThreadsByTpidr;
|
||||
|
||||
private List<Executable> Executables;
|
||||
|
||||
private HThread MainThread;
|
||||
|
||||
private long ImageBase;
|
||||
|
||||
public Process(Switch Ns, AMemoryAlloc Allocator, int ProcessId)
|
||||
{
|
||||
this.Ns = Ns;
|
||||
this.ProcessId = ProcessId;
|
||||
|
||||
Memory = new AMemory(Ns.Ram, Allocator);
|
||||
|
||||
Scheduler = new KProcessScheduler();
|
||||
|
||||
SvcHandler = new SvcHandler(Ns, this);
|
||||
|
||||
TlsSlots = new ConcurrentDictionary<int, AThread>();
|
||||
|
||||
ThreadsByTpidr = new ConcurrentDictionary<long, HThread>();
|
||||
|
||||
Executables = new List<Executable>();
|
||||
|
||||
ImageBase = 0x8000000;
|
||||
|
||||
Memory.Manager.MapPhys(
|
||||
TlsPageAddr,
|
||||
TlsTotalSize,
|
||||
(int)MemoryType.ThreadLocal,
|
||||
AMemoryPerm.RW);
|
||||
}
|
||||
|
||||
public void LoadProgram(IExecutable Program)
|
||||
{
|
||||
Logging.Info($"Image base at 0x{ImageBase:x16}.");
|
||||
|
||||
Executable Executable = new Executable(Program, Memory, ImageBase);
|
||||
|
||||
Executables.Add(Executable);
|
||||
|
||||
ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd);
|
||||
}
|
||||
|
||||
public void SetEmptyArgs()
|
||||
{
|
||||
ImageBase += AMemoryMgr.PageSize;
|
||||
}
|
||||
|
||||
public void InitializeHeap()
|
||||
{
|
||||
Memory.Manager.SetHeapAddr((ImageBase + 0x3fffffff) & ~0x3fffffff);
|
||||
}
|
||||
|
||||
public bool Run()
|
||||
{
|
||||
if (Executables.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long StackBot = TlsPageAddr - MaxStackSize;
|
||||
|
||||
Memory.Manager.MapPhys(StackBot, MaxStackSize, (int)MemoryType.Normal, AMemoryPerm.RW);
|
||||
|
||||
int Handle = MakeThread(Executables[0].ImageBase, TlsPageAddr, 0, 0, 0);
|
||||
|
||||
if (Handle == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MainThread = Ns.Os.Handles.GetData<HThread>(Handle);
|
||||
|
||||
Scheduler.StartThread(MainThread);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void StopAllThreads()
|
||||
{
|
||||
if (MainThread != null)
|
||||
{
|
||||
while (MainThread.Thread.IsAlive)
|
||||
{
|
||||
MainThread.Thread.StopExecution();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (AThread Thread in TlsSlots.Values)
|
||||
{
|
||||
while (Thread.IsAlive)
|
||||
{
|
||||
Thread.StopExecution();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MakeThread(
|
||||
long EntryPoint,
|
||||
long StackTop,
|
||||
long ArgsPtr,
|
||||
int Priority,
|
||||
int ProcessorId)
|
||||
{
|
||||
ThreadPriority ThreadPrio;
|
||||
|
||||
if (Priority < 12)
|
||||
{
|
||||
ThreadPrio = ThreadPriority.Highest;
|
||||
}
|
||||
else if (Priority < 24)
|
||||
{
|
||||
ThreadPrio = ThreadPriority.AboveNormal;
|
||||
}
|
||||
else if (Priority < 36)
|
||||
{
|
||||
ThreadPrio = ThreadPriority.Normal;
|
||||
}
|
||||
else if (Priority < 48)
|
||||
{
|
||||
ThreadPrio = ThreadPriority.BelowNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
ThreadPrio = ThreadPriority.Lowest;
|
||||
}
|
||||
|
||||
AThread Thread = new AThread(Memory, ThreadPrio, EntryPoint);
|
||||
|
||||
HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority);
|
||||
|
||||
int Handle = Ns.Os.Handles.GenerateId(ThreadHnd);
|
||||
|
||||
int TlsSlot = GetFreeTlsSlot(Thread);
|
||||
|
||||
if (TlsSlot == -1 || Handle == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
Thread.ThreadState.Break += BreakHandler;
|
||||
Thread.ThreadState.SvcCall += SvcHandler.SvcCall;
|
||||
Thread.ThreadState.Undefined += UndefinedHandler;
|
||||
Thread.ThreadState.ProcessId = ProcessId;
|
||||
Thread.ThreadState.ThreadId = Ns.Os.IdGen.GenerateId();
|
||||
Thread.ThreadState.Tpidr = TlsPageAddr + TlsSlot * TlsSize;
|
||||
Thread.ThreadState.X0 = (ulong)ArgsPtr;
|
||||
Thread.ThreadState.X1 = (ulong)Handle;
|
||||
Thread.ThreadState.X31 = (ulong)StackTop;
|
||||
|
||||
Thread.WorkFinished += ThreadFinished;
|
||||
|
||||
ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd);
|
||||
|
||||
return Handle;
|
||||
}
|
||||
|
||||
private void BreakHandler(object sender, AInstExceptEventArgs e)
|
||||
{
|
||||
throw new GuestBrokeExecutionException();
|
||||
}
|
||||
|
||||
private void UndefinedHandler(object sender, AInstUndEventArgs e)
|
||||
{
|
||||
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
|
||||
}
|
||||
|
||||
private int GetFreeTlsSlot(AThread Thread)
|
||||
{
|
||||
for (int Index = 1; Index < TotalTlsSlots; Index++)
|
||||
{
|
||||
if (TlsSlots.TryAdd(Index, Thread))
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void ThreadFinished(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is AThread Thread)
|
||||
{
|
||||
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
|
||||
|
||||
Ns.Os.IdGen.DeleteId(Thread.ThreadId);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetTlsSlot(long Position)
|
||||
{
|
||||
return (int)((Position - TlsPageAddr) / TlsSize);
|
||||
}
|
||||
|
||||
public HThread GetThread(long Tpidr)
|
||||
{
|
||||
if (!ThreadsByTpidr.TryGetValue(Tpidr, out HThread Thread))
|
||||
{
|
||||
Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!");
|
||||
}
|
||||
|
||||
return Thread;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool Disposing)
|
||||
{
|
||||
if (Disposing)
|
||||
{
|
||||
Scheduler.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
Ryujinx.Core/OsHle/ServiceCtx.cs
Normal file
36
Ryujinx.Core/OsHle/ServiceCtx.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
class ServiceCtx
|
||||
{
|
||||
public Switch Ns { get; private set; }
|
||||
public AMemory Memory { get; private set; }
|
||||
public HSession Session { get; private set; }
|
||||
public IpcMessage Request { get; private set; }
|
||||
public IpcMessage Response { get; private set; }
|
||||
public BinaryReader RequestData { get; private set; }
|
||||
public BinaryWriter ResponseData { get; private set; }
|
||||
|
||||
public ServiceCtx(
|
||||
Switch Ns,
|
||||
AMemory Memory,
|
||||
HSession Session,
|
||||
IpcMessage Request,
|
||||
IpcMessage Response,
|
||||
BinaryReader RequestData,
|
||||
BinaryWriter ResponseData)
|
||||
{
|
||||
this.Ns = Ns;
|
||||
this.Memory = Memory;
|
||||
this.Session = Session;
|
||||
this.Request = Request;
|
||||
this.Response = Response;
|
||||
this.RequestData = RequestData;
|
||||
this.ResponseData = ResponseData;
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx.Core/OsHle/Services/ServiceAcc.cs
Normal file
33
Ryujinx.Core/OsHle/Services/ServiceAcc.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Ryujinx.Core.OsHle.Objects.Acc;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long AccU0ListOpenUsers(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long AccU0GetProfile(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IProfile());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long AccU0InitializeApplicationInfo(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long AccU0GetBaasAccountManagerForApplication(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IManagerForApplication());
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Core/OsHle/Services/ServiceApm.cs
Normal file
16
Ryujinx.Core/OsHle/Services/ServiceApm.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Ryujinx.Core.OsHle.Objects.Apm;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long ApmOpenSession(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ISession());
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Core/OsHle/Services/ServiceAppletOE.cs
Normal file
16
Ryujinx.Core/OsHle/Services/ServiceAppletOE.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Ryujinx.Core.OsHle.Objects.Am;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long AppletOpenApplicationProxy(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IApplicationProxy());
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
71
Ryujinx.Core/OsHle/Services/ServiceAud.cs
Normal file
71
Ryujinx.Core/OsHle/Services/ServiceAud.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Objects.Aud;
|
||||
using System.Text;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long AudOutListAudioOuts(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, Position, Encoding.ASCII.GetBytes("iface"));
|
||||
|
||||
Context.ResponseData.Write(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long AudOutOpenAudioOut(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IAudioOut());
|
||||
|
||||
Context.ResponseData.Write(48000); //Sample Rate
|
||||
Context.ResponseData.Write(2); //Channel Count
|
||||
Context.ResponseData.Write(2); //PCM Format
|
||||
/*
|
||||
0 - Invalid
|
||||
1 - INT8
|
||||
2 - INT16
|
||||
3 - INT24
|
||||
4 - INT32
|
||||
5 - PCM Float
|
||||
6 - ADPCM
|
||||
*/
|
||||
Context.ResponseData.Write(0); //Unknown
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long AudRenOpenAudioRenderer(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IAudioRenderer());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long AudRenGetAudioRendererWorkBufferSize(ServiceCtx Context)
|
||||
{
|
||||
int SampleRate = Context.RequestData.ReadInt32();
|
||||
int Unknown4 = Context.RequestData.ReadInt32();
|
||||
int Unknown8 = Context.RequestData.ReadInt32();
|
||||
int UnknownC = Context.RequestData.ReadInt32();
|
||||
int Unknown10 = Context.RequestData.ReadInt32();
|
||||
int Unknown14 = Context.RequestData.ReadInt32();
|
||||
int Unknown18 = Context.RequestData.ReadInt32();
|
||||
int Unknown1c = Context.RequestData.ReadInt32();
|
||||
int Unknown20 = Context.RequestData.ReadInt32();
|
||||
int Unknown24 = Context.RequestData.ReadInt32();
|
||||
int Unknown28 = Context.RequestData.ReadInt32();
|
||||
int Unknown2c = Context.RequestData.ReadInt32();
|
||||
int Rev1Magic = Context.RequestData.ReadInt32();
|
||||
|
||||
Context.ResponseData.Write(0x400L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Core/OsHle/Services/ServiceFriend.cs
Normal file
16
Ryujinx.Core/OsHle/Services/ServiceFriend.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Ryujinx.Core.OsHle.Objects.Friend;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long FriendCreateFriendService(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IFriendService());
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
49
Ryujinx.Core/OsHle/Services/ServiceFspSrv.cs
Normal file
49
Ryujinx.Core/OsHle/Services/ServiceFspSrv.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using Ryujinx.Core.OsHle.Objects.FspSrv;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long FspSrvInitialize(ServiceCtx Context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long FspSrvMountSdCard(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetSdCardPath()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long FspSrvMountSaveData(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetGameSavesPath()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long FspSrvOpenDataStorageByCurrentProcess(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long FspSrvOpenRomStorage(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long FspSrvGetGlobalAccessLogMode(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
56
Ryujinx.Core/OsHle/Services/ServiceHid.cs
Normal file
56
Ryujinx.Core/OsHle/Services/ServiceHid.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Objects.Hid;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long HidCreateAppletResource(ServiceCtx Context)
|
||||
{
|
||||
HSharedMem HidHndData = Context.Ns.Os.Handles.GetData<HSharedMem>(Context.Ns.Os.HidHandle);
|
||||
|
||||
MakeObject(Context, new IAppletResource(HidHndData));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long HidActivateTouchScreen(ServiceCtx Context)
|
||||
{
|
||||
long Unknown = Context.RequestData.ReadInt64();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long HidSetSupportedNpadStyleSet(ServiceCtx Context)
|
||||
{
|
||||
long Unknown0 = Context.RequestData.ReadInt64();
|
||||
long Unknown8 = Context.RequestData.ReadInt64();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long HidSetSupportedNpadIdType(ServiceCtx Context)
|
||||
{
|
||||
long Unknown = Context.RequestData.ReadInt64();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long HidActivateNpad(ServiceCtx Context)
|
||||
{
|
||||
long Unknown = Context.RequestData.ReadInt64();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long HidSetNpadJoyHoldType(ServiceCtx Context)
|
||||
{
|
||||
long Unknown0 = Context.RequestData.ReadInt64();
|
||||
long Unknown8 = Context.RequestData.ReadInt64();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Core/OsHle/Services/ServiceLm.cs
Normal file
12
Ryujinx.Core/OsHle/Services/ServiceLm.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long LmInitialize(ServiceCtx Context)
|
||||
{
|
||||
Context.Session.Initialize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
610
Ryujinx.Core/OsHle/Services/ServiceNvDrv.cs
Normal file
610
Ryujinx.Core/OsHle/Services/ServiceNvDrv.cs
Normal file
|
@ -0,0 +1,610 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using Ryujinx.Core.OsHle.Utilities;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
private delegate long ServiceProcessRequest(ServiceCtx Context);
|
||||
|
||||
private static Dictionary<(string, int), ServiceProcessRequest> IoctlCmds =
|
||||
new Dictionary<(string, int), ServiceProcessRequest>()
|
||||
{
|
||||
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
|
||||
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
|
||||
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
|
||||
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
|
||||
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
|
||||
{ ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig },
|
||||
{ ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait },
|
||||
{ ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize },
|
||||
{ ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo },
|
||||
{ ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics },
|
||||
{ ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks },
|
||||
{ ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask },
|
||||
{ ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData },
|
||||
{ ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap },
|
||||
{ ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo },
|
||||
{ ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx },
|
||||
{ ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind },
|
||||
{ ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier },
|
||||
{ ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority },
|
||||
{ ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 },
|
||||
{ ("/dev/nvmap", 0x0101), NvMapIocCreate },
|
||||
{ ("/dev/nvmap", 0x0103), NvMapIocFromId },
|
||||
{ ("/dev/nvmap", 0x0104), NvMapIocAlloc },
|
||||
{ ("/dev/nvmap", 0x0109), NvMapIocParam },
|
||||
{ ("/dev/nvmap", 0x010e), NvMapIocGetId },
|
||||
};
|
||||
|
||||
public static long NvDrvOpen(ServiceCtx Context)
|
||||
{
|
||||
long NamePtr = Context.Request.SendBuff[0].Position;
|
||||
|
||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr);
|
||||
|
||||
int Fd = Context.Ns.Os.Fds.GenerateId(new FileDesc(Name));
|
||||
|
||||
Context.ResponseData.Write(Fd);
|
||||
Context.ResponseData.Write(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long NvDrvIoctl(ServiceCtx Context)
|
||||
{
|
||||
int Fd = Context.RequestData.ReadInt32();
|
||||
int Cmd = Context.RequestData.ReadInt32() & 0xffff;
|
||||
|
||||
FileDesc FdData = Context.Ns.Os.Fds.GetData<FileDesc>(Fd);
|
||||
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
Context.ResponseData.Write(0);
|
||||
|
||||
if (IoctlCmds.TryGetValue((FdData.Name, Cmd), out ServiceProcessRequest ProcReq))
|
||||
{
|
||||
return ProcReq(Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"{FdData.Name} {Cmd:x4}");
|
||||
}
|
||||
}
|
||||
|
||||
public static long NvDrvClose(ServiceCtx Context)
|
||||
{
|
||||
int Fd = Context.RequestData.ReadInt32();
|
||||
|
||||
Context.Ns.Os.Fds.Delete(Fd);
|
||||
|
||||
Context.ResponseData.Write(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long NvDrvInitialize(ServiceCtx Context)
|
||||
{
|
||||
long TransferMemSize = Context.RequestData.ReadInt64();
|
||||
int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0];
|
||||
|
||||
Context.ResponseData.Write(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long NvDrvQueryEvent(ServiceCtx Context)
|
||||
{
|
||||
int Fd = Context.RequestData.ReadInt32();
|
||||
int EventId = Context.RequestData.ReadInt32();
|
||||
|
||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(0xcafe);
|
||||
|
||||
Context.ResponseData.Write(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long NvDrvSetClientPid(ServiceCtx Context)
|
||||
{
|
||||
long Pid = Context.RequestData.ReadInt64();
|
||||
|
||||
Context.ResponseData.Write(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuAsIoctlBindChannel(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
int Fd = Context.Memory.ReadInt32(Position);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuAsIoctlAllocSpace(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
|
||||
int Pages = Reader.ReadInt32();
|
||||
int PageSize = Reader.ReadInt32();
|
||||
int Flags = Reader.ReadInt32();
|
||||
int Padding = Reader.ReadInt32();
|
||||
long Align = Reader.ReadInt64();
|
||||
|
||||
if ((Flags & 1) != 0)
|
||||
{
|
||||
Align = Context.Ns.Gpu.ReserveMemory(Align, (long)Pages * PageSize, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Align = Context.Ns.Gpu.ReserveMemory((long)Pages * PageSize, Align);
|
||||
}
|
||||
|
||||
Context.Memory.WriteInt64(Position + 0x10, Align);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
|
||||
int Flags = Reader.ReadInt32();
|
||||
int Kind = Reader.ReadInt32();
|
||||
int Handle = Reader.ReadInt32();
|
||||
int PageSize = Reader.ReadInt32();
|
||||
long BuffAddr = Reader.ReadInt64();
|
||||
long MapSize = Reader.ReadInt64();
|
||||
long Offset = Reader.ReadInt64();
|
||||
|
||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
||||
|
||||
if (NvMap != null)
|
||||
{
|
||||
if ((Flags & 1) != 0)
|
||||
{
|
||||
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, Offset, NvMap.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, NvMap.Size);
|
||||
}
|
||||
}
|
||||
|
||||
Context.Memory.WriteInt64(Position + 0x20, Offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuAsIoctlGetVaRegions(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
MemWriter Writer = new MemWriter(Context.Memory, Position);
|
||||
|
||||
long Unused = Reader.ReadInt64();
|
||||
int BuffSize = Reader.ReadInt32();
|
||||
int Padding = Reader.ReadInt32();
|
||||
|
||||
BuffSize = 0x30;
|
||||
|
||||
Writer.WriteInt64(Unused);
|
||||
Writer.WriteInt32(BuffSize);
|
||||
Writer.WriteInt32(Padding);
|
||||
|
||||
Writer.WriteInt64(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt64(0);
|
||||
|
||||
Writer.WriteInt64(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt64(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuAsIoctlInitializeEx(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
|
||||
int BigPageSize = Reader.ReadInt32();
|
||||
int AsFd = Reader.ReadInt32();
|
||||
int Flags = Reader.ReadInt32();
|
||||
int Reserved = Reader.ReadInt32();
|
||||
long Unknown10 = Reader.ReadInt64();
|
||||
long Unknown18 = Reader.ReadInt64();
|
||||
long Unknown20 = Reader.ReadInt64();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvHostIoctlCtrlGetConfig(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82);
|
||||
|
||||
for (int Index = 0; Index < 0x101; Index++)
|
||||
{
|
||||
Writer.WriteByte(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvHostIoctlCtrlEventWait(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
|
||||
int SyncPtId = Reader.ReadInt32();
|
||||
int Threshold = Reader.ReadInt32();
|
||||
int Timeout = Reader.ReadInt32();
|
||||
int Value = Reader.ReadInt32();
|
||||
|
||||
Context.Memory.WriteInt32(Position + 0xc, 0xcafe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
Context.Memory.WriteInt32(Position, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuIoctlZcullGetInfo(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemWriter Writer = new MemWriter(Context.Memory, Position);
|
||||
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuIoctlGetCharacteristics(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
MemWriter Writer = new MemWriter(Context.Memory, Position);
|
||||
|
||||
//Note: We should just ignore the BuffAddr, because official code
|
||||
//does __memcpy_device from Position + 0x10 to BuffAddr.
|
||||
long BuffSize = Reader.ReadInt64();
|
||||
long BuffAddr = Reader.ReadInt64();
|
||||
|
||||
BuffSize = 0xa0;
|
||||
|
||||
Writer.WriteInt64(BuffSize);
|
||||
Writer.WriteInt64(BuffAddr);
|
||||
Writer.WriteInt32(0x120); //NVGPU_GPU_ARCH_GM200
|
||||
Writer.WriteInt32(0xb); //NVGPU_GPU_IMPL_GM20B
|
||||
Writer.WriteInt32(0xa1);
|
||||
Writer.WriteInt32(1);
|
||||
Writer.WriteInt64(0x40000);
|
||||
Writer.WriteInt64(0);
|
||||
Writer.WriteInt32(2);
|
||||
Writer.WriteInt32(0x20); //NVGPU_GPU_BUS_TYPE_AXI
|
||||
Writer.WriteInt32(0x20000);
|
||||
Writer.WriteInt32(0x20000);
|
||||
Writer.WriteInt32(0x1b);
|
||||
Writer.WriteInt32(0x30000);
|
||||
Writer.WriteInt32(1);
|
||||
Writer.WriteInt32(0x503);
|
||||
Writer.WriteInt32(0x503);
|
||||
Writer.WriteInt32(0x80);
|
||||
Writer.WriteInt32(0x28);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt64(0x55);
|
||||
Writer.WriteInt32(0x902d); //FERMI_TWOD_A
|
||||
Writer.WriteInt32(0xb197); //MAXWELL_B
|
||||
Writer.WriteInt32(0xb1c0); //MAXWELL_COMPUTE_B
|
||||
Writer.WriteInt32(0xb06f); //MAXWELL_CHANNEL_GPFIFO_A
|
||||
Writer.WriteInt32(0xa140); //KEPLER_INLINE_TO_MEMORY_B
|
||||
Writer.WriteInt32(0xb0b5); //MAXWELL_DMA_COPY_A
|
||||
Writer.WriteInt32(1);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(2);
|
||||
Writer.WriteInt32(1);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(1);
|
||||
Writer.WriteInt32(0x21d70);
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteByte((byte)'g');
|
||||
Writer.WriteByte((byte)'m');
|
||||
Writer.WriteByte((byte)'2');
|
||||
Writer.WriteByte((byte)'0');
|
||||
Writer.WriteByte((byte)'b');
|
||||
Writer.WriteByte((byte)'\0');
|
||||
Writer.WriteByte((byte)'\0');
|
||||
Writer.WriteByte((byte)'\0');
|
||||
Writer.WriteInt64(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuIoctlGetTpcMasks(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
|
||||
int MaskBuffSize = Reader.ReadInt32();
|
||||
int Reserved = Reader.ReadInt32();
|
||||
long MaskBuffAddr = Reader.ReadInt64();
|
||||
long Unknown = Reader.ReadInt64();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
Context.Memory.WriteInt32(Position + 0, 7);
|
||||
Context.Memory.WriteInt32(Position + 4, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIoctlChannelSetUserData(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIoctlChannelSetNvMap(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
int Fd = Context.Memory.ReadInt32(Position);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10);
|
||||
|
||||
long GpFifo = Reader.ReadInt64();
|
||||
int Count = Reader.ReadInt32();
|
||||
int Flags = Reader.ReadInt32();
|
||||
int FenceId = Reader.ReadInt32();
|
||||
int FenceVal = Reader.ReadInt32();
|
||||
|
||||
for (int Index = 0; Index < Count; Index++)
|
||||
{
|
||||
long GpFifoHdr = Reader.ReadInt64();
|
||||
|
||||
long GpuAddr = GpFifoHdr & 0xffffffffff;
|
||||
|
||||
int Size = (int)(GpFifoHdr >> 40) & 0x7ffffc;
|
||||
|
||||
long CpuAddr = Context.Ns.Gpu.GetCpuAddr(GpuAddr);
|
||||
|
||||
if (CpuAddr != -1)
|
||||
{
|
||||
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size);
|
||||
|
||||
NsGpuPBEntry[] PushBuffer = NsGpuPBEntry.DecodePushBuffer(Data);
|
||||
|
||||
Context.Ns.Gpu.ProcessPushBuffer(PushBuffer, Context.Memory);
|
||||
}
|
||||
}
|
||||
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
int ClassNum = Context.Memory.ReadInt32(Position + 0);
|
||||
int Flags = Context.Memory.ReadInt32(Position + 4);
|
||||
|
||||
Context.Memory.WriteInt32(Position + 8, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIoctlChannelZcullBind(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
|
||||
long GpuVa = Reader.ReadInt64();
|
||||
int Mode = Reader.ReadInt32();
|
||||
int Padding = Reader.ReadInt32();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
|
||||
long Offset = Reader.ReadInt64();
|
||||
long Size = Reader.ReadInt64();
|
||||
int Mem = Reader.ReadInt32();
|
||||
int Padding = Reader.ReadInt32();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIoctlChannelSetPriority(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
int Priority = Context.Memory.ReadInt32(Position);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.PtrBuff[0].Position;
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc);
|
||||
|
||||
int Count = Reader.ReadInt32();
|
||||
int Flags = Reader.ReadInt32();
|
||||
int Unknown8 = Reader.ReadInt32();
|
||||
long Fence = Reader.ReadInt64();
|
||||
int Unknown14 = Reader.ReadInt32();
|
||||
int Unknown18 = Reader.ReadInt32();
|
||||
|
||||
Writer.WriteInt32(0);
|
||||
Writer.WriteInt32(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIocCreate(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.GetSendBuffPtr();
|
||||
|
||||
int Size = Context.Memory.ReadInt32(Position);
|
||||
|
||||
int Id = Context.Ns.Os.NvMapIds.GenerateId();
|
||||
|
||||
int Handle = Context.Ns.Os.Handles.GenerateId(new HNvMap(Id, Size));
|
||||
|
||||
Context.Memory.WriteInt32(Position + 4, Handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIocFromId(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.GetSendBuffPtr();
|
||||
|
||||
int Id = Context.Memory.ReadInt32(Position);
|
||||
|
||||
int Handle = -1;
|
||||
|
||||
foreach (KeyValuePair<int, object> KV in Context.Ns.Os.Handles)
|
||||
{
|
||||
if (KV.Value is HNvMap NvMap && NvMap.Id == Id)
|
||||
{
|
||||
Handle = KV.Key;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Context.Memory.WriteInt32(Position + 4, Handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIocAlloc(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.GetSendBuffPtr();
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
|
||||
int Handle = Reader.ReadInt32();
|
||||
int HeapMask = Reader.ReadInt32();
|
||||
int Flags = Reader.ReadInt32();
|
||||
int Align = Reader.ReadInt32();
|
||||
byte Kind = (byte)Reader.ReadInt64();
|
||||
long Addr = Reader.ReadInt64();
|
||||
|
||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
||||
|
||||
if (NvMap != null)
|
||||
{
|
||||
NvMap.Address = Addr;
|
||||
NvMap.Align = Align;
|
||||
NvMap.Kind = Kind;
|
||||
}
|
||||
|
||||
Logging.Debug($"NvMapIocAlloc at {NvMap.Address:x16}");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIocParam(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.GetSendBuffPtr();
|
||||
|
||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||
|
||||
int Handle = Reader.ReadInt32();
|
||||
int Param = Reader.ReadInt32();
|
||||
|
||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
||||
|
||||
int Response = 0;
|
||||
|
||||
switch (Param)
|
||||
{
|
||||
case 1: Response = NvMap.Size; break;
|
||||
case 2: Response = NvMap.Align; break;
|
||||
case 4: Response = 0x40000000; break;
|
||||
case 5: Response = NvMap.Kind; break;
|
||||
}
|
||||
|
||||
Context.Memory.WriteInt32(Position + 8, Response);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long NvMapIocGetId(ServiceCtx Context)
|
||||
{
|
||||
long Position = Context.Request.GetSendBuffPtr();
|
||||
|
||||
int Handle = Context.Memory.ReadInt32(Position + 4);
|
||||
|
||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
||||
|
||||
Context.Memory.WriteInt32(Position, NvMap.Id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Core/OsHle/Services/ServicePctl.cs
Normal file
16
Ryujinx.Core/OsHle/Services/ServicePctl.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Ryujinx.Core.OsHle.Objects.Am;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long PctlCreateService(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IParentalControlService());
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
35
Ryujinx.Core/OsHle/Services/ServicePl.cs
Normal file
35
Ryujinx.Core/OsHle/Services/ServicePl.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long PlGetLoadState(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(1); //Loaded
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long PlGetFontSize(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(Horizon.FontSize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long PlGetSharedMemoryAddressOffset(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long PlGetSharedMemoryNativeHandle(ServiceCtx Context)
|
||||
{
|
||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.FontHandle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
32
Ryujinx.Core/OsHle/Services/ServiceSet.cs
Normal file
32
Ryujinx.Core/OsHle/Services/ServiceSet.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using ChocolArm64.Memory;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
private const int LangCodesCount = 13;
|
||||
|
||||
public static long SetGetAvailableLanguageCodes(ServiceCtx Context)
|
||||
{
|
||||
int PtrBuffSize = Context.RequestData.ReadInt32();
|
||||
|
||||
if (Context.Request.RecvListBuff.Count > 0)
|
||||
{
|
||||
long Position = Context.Request.RecvListBuff[0].Position;
|
||||
short Size = Context.Request.RecvListBuff[0].Size;
|
||||
|
||||
//This should return an array of ints with values matching the LanguageCode enum.
|
||||
byte[] Data = new byte[Size];
|
||||
|
||||
Data[0] = 0;
|
||||
Data[1] = 1;
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, Position, Data);
|
||||
}
|
||||
|
||||
Context.ResponseData.Write(LangCodesCount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
48
Ryujinx.Core/OsHle/Services/ServiceSm.cs
Normal file
48
Ryujinx.Core/OsHle/Services/ServiceSm.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
private const int SmNotInitialized = 0x415;
|
||||
|
||||
public static long SmInitialize(ServiceCtx Context)
|
||||
{
|
||||
Context.Session.Initialize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long SmGetService(ServiceCtx Context)
|
||||
{
|
||||
//Only for kernel version > 3.0.0.
|
||||
if (!Context.Session.IsInitialized)
|
||||
{
|
||||
//return SmNotInitialized;
|
||||
}
|
||||
|
||||
string Name = string.Empty;
|
||||
|
||||
for (int Index = 0; Index < 8 &&
|
||||
Context.RequestData.BaseStream.Position <
|
||||
Context.RequestData.BaseStream.Length; Index++)
|
||||
{
|
||||
byte Chr = Context.RequestData.ReadByte();
|
||||
|
||||
if (Chr >= 0x20 && Chr < 0x7f)
|
||||
{
|
||||
Name += (char)Chr;
|
||||
}
|
||||
}
|
||||
|
||||
HSession Session = new HSession(Name);
|
||||
|
||||
int Handle = Context.Ns.Os.Handles.GenerateId(Session);
|
||||
|
||||
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
45
Ryujinx.Core/OsHle/Services/ServiceTime.cs
Normal file
45
Ryujinx.Core/OsHle/Services/ServiceTime.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using Ryujinx.Core.OsHle.Objects.Time;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long TimeGetStandardUserSystemClock(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ISystemClock(SystemClockType.User));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long TimeGetStandardNetworkSystemClock(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ISystemClock(SystemClockType.Network));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long TimeGetStandardSteadyClock(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ISteadyClock());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long TimeGetTimeZoneService(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ITimeZoneService());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long TimeGetStandardLocalSystemClock(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new ISystemClock(SystemClockType.Local));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
18
Ryujinx.Core/OsHle/Services/ServiceVi.cs
Normal file
18
Ryujinx.Core/OsHle/Services/ServiceVi.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using Ryujinx.Core.OsHle.Objects.Vi;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services
|
||||
{
|
||||
static partial class Service
|
||||
{
|
||||
public static long ViGetDisplayService(ServiceCtx Context)
|
||||
{
|
||||
int Unknown = Context.RequestData.ReadInt32();
|
||||
|
||||
MakeObject(Context, new IApplicationDisplayService());
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue