diff options
| author | emmauss <emmausssss@gmail.com> | 2018-02-20 22:09:23 +0200 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-02-20 17:09:23 -0300 |
| commit | 62b827f474f0aa2152dd339fcc7cf31084e16a0b (patch) | |
| tree | 0e5c55b341aee4db0ccb841a084f253ec5e05657 /Ryujinx.Core/OsHle/Objects | |
| parent | cb665bb715834526d73c9469d16114b287faaecd (diff) | |
Split main project into core,graphics and chocolarm4 subproject (#29)
Diffstat (limited to 'Ryujinx.Core/OsHle/Objects')
34 files changed, 2068 insertions, 0 deletions
diff --git a/Ryujinx.Core/OsHle/Objects/Acc/IManagerForApplication.cs b/Ryujinx.Core/OsHle/Objects/Acc/IManagerForApplication.cs new file mode 100644 index 00000000..afbfab24 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Acc/IManagerForApplication.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Acc/IProfile.cs b/Ryujinx.Core/OsHle/Objects/Acc/IProfile.cs new file mode 100644 index 00000000..94d17183 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Acc/IProfile.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/IApplicationFunctions.cs b/Ryujinx.Core/OsHle/Objects/Am/IApplicationFunctions.cs new file mode 100644 index 00000000..939ad248 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/IApplicationFunctions.cs @@ -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(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/IApplicationProxy.cs b/Ryujinx.Core/OsHle/Objects/Am/IApplicationProxy.cs new file mode 100644 index 00000000..4a164daf --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/IApplicationProxy.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/IAudioController.cs b/Ryujinx.Core/OsHle/Objects/Am/IAudioController.cs new file mode 100644 index 00000000..c37042fd --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/IAudioController.cs @@ -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>() + { + //... + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/ICommonStateGetter.cs b/Ryujinx.Core/OsHle/Objects/Am/ICommonStateGetter.cs new file mode 100644 index 00000000..83d61fa6 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/ICommonStateGetter.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/IDebugFunctions.cs b/Ryujinx.Core/OsHle/Objects/Am/IDebugFunctions.cs new file mode 100644 index 00000000..d04d8363 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/IDebugFunctions.cs @@ -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>() + { + //... + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/IDisplayController.cs b/Ryujinx.Core/OsHle/Objects/Am/IDisplayController.cs new file mode 100644 index 00000000..9eafa70d --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/IDisplayController.cs @@ -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>() + { + //... + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/ILibraryAppletCreator.cs b/Ryujinx.Core/OsHle/Objects/Am/ILibraryAppletCreator.cs new file mode 100644 index 00000000..10e0f4f4 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/ILibraryAppletCreator.cs @@ -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>() + { + //... + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/IParentalControlService.cs b/Ryujinx.Core/OsHle/Objects/Am/IParentalControlService.cs new file mode 100644 index 00000000..1feacfa6 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/IParentalControlService.cs @@ -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>() + { + //... + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/ISelfController.cs b/Ryujinx.Core/OsHle/Objects/Am/ISelfController.cs new file mode 100644 index 00000000..691bb202 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/ISelfController.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/IStorage.cs b/Ryujinx.Core/OsHle/Objects/Am/IStorage.cs new file mode 100644 index 00000000..b30059ba --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/IStorage.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/IStorageAccessor.cs b/Ryujinx.Core/OsHle/Objects/Am/IStorageAccessor.cs new file mode 100644 index 00000000..df260cc3 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/IStorageAccessor.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Am/IWindowController.cs b/Ryujinx.Core/OsHle/Objects/Am/IWindowController.cs new file mode 100644 index 00000000..aa6e961e --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Am/IWindowController.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Apm/ISession.cs b/Ryujinx.Core/OsHle/Objects/Apm/ISession.cs new file mode 100644 index 00000000..3ab33005 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Apm/ISession.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Aud/IAudioOut.cs b/Ryujinx.Core/OsHle/Objects/Aud/IAudioOut.cs new file mode 100644 index 00000000..061c6376 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Aud/IAudioOut.cs @@ -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; + } + } +} diff --git a/Ryujinx.Core/OsHle/Objects/Aud/IAudioRenderer.cs b/Ryujinx.Core/OsHle/Objects/Aud/IAudioRenderer.cs new file mode 100644 index 00000000..05356477 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Aud/IAudioRenderer.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Friend/IFriendService.cs b/Ryujinx.Core/OsHle/Objects/Friend/IFriendService.cs new file mode 100644 index 00000000..e98e27ca --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Friend/IFriendService.cs @@ -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>() + { + //... + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs new file mode 100644 index 00000000..88fce28e --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs @@ -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; + } + } +} diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs new file mode 100644 index 00000000..95fbc650 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs @@ -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(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs new file mode 100644 index 00000000..68b15845 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IStorage.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IStorage.cs new file mode 100644 index 00000000..4eca37e8 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IStorage.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs b/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs new file mode 100644 index 00000000..d6e8947b --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/IIpcInterface.cs b/Ryujinx.Core/OsHle/Objects/IIpcInterface.cs new file mode 100644 index 00000000..c57a0974 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/IIpcInterface.cs @@ -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; } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/ObjHelper.cs b/Ryujinx.Core/OsHle/Objects/ObjHelper.cs new file mode 100644 index 00000000..a151a287 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/ObjHelper.cs @@ -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); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Parcel.cs b/Ryujinx.Core/OsHle/Objects/Parcel.cs new file mode 100644 index 00000000..cd7179e1 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Parcel.cs @@ -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(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Time/ISteadyClock.cs b/Ryujinx.Core/OsHle/Objects/Time/ISteadyClock.cs new file mode 100644 index 00000000..1116b849 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Time/ISteadyClock.cs @@ -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>() + { + //... + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Time/ISystemClock.cs b/Ryujinx.Core/OsHle/Objects/Time/ISystemClock.cs new file mode 100644 index 00000000..3aa70691 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Time/ISystemClock.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Time/ITimeZoneService.cs b/Ryujinx.Core/OsHle/Objects/Time/ITimeZoneService.cs new file mode 100644 index 00000000..c083628b --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Time/ITimeZoneService.cs @@ -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>() + { + //... + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Time/SystemClockType.cs b/Ryujinx.Core/OsHle/Objects/Time/SystemClockType.cs new file mode 100644 index 00000000..f447ca1c --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Time/SystemClockType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Core.OsHle.Objects.Time +{ + enum SystemClockType + { + User, + Network, + Local + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Vi/IApplicationDisplayService.cs b/Ryujinx.Core/OsHle/Objects/Vi/IApplicationDisplayService.cs new file mode 100644 index 00000000..b3ec0e90 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Vi/IApplicationDisplayService.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs b/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs new file mode 100644 index 00000000..cfd271e8 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Vi/IManagerDisplayService.cs b/Ryujinx.Core/OsHle/Objects/Vi/IManagerDisplayService.cs new file mode 100644 index 00000000..f1b3cdd0 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Vi/IManagerDisplayService.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Vi/ISystemDisplayService.cs b/Ryujinx.Core/OsHle/Objects/Vi/ISystemDisplayService.cs new file mode 100644 index 00000000..4c83c25f --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Vi/ISystemDisplayService.cs @@ -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; + } + } +}
\ No newline at end of file |
