diff --git a/Overlay/Makefile b/Overlay/Makefile index 9656834..3b2ebd5 100644 --- a/Overlay/Makefile +++ b/Overlay/Makefile @@ -38,7 +38,7 @@ include $(DEVKITPRO)/libnx/switch_rules # NACP building is skipped as well. #--------------------------------------------------------------------------------- APP_TITLE := ReverseNX-RT -APP_VERSION := 1.1.1 +APP_VERSION := 1.1.1-OC TARGET := ReverseNX-RT-ovl BUILD := build diff --git a/Overlay/source/main.cpp b/Overlay/source/main.cpp index 810295c..2b3aa52 100644 --- a/Overlay/source/main.cpp +++ b/Overlay/source/main.cpp @@ -1,7 +1,202 @@ #define TESLA_INIT_IMPL // If you have more than one file using the tesla header, only define this in the main one #include // The Tesla Header +#include #include "SaltyNX.h" +class ModeSync { +public: + enum ReverseNXMode { + ReverseNX_NotValid = 0, + ReverseNX_SystemDefault = 0, + ReverseNX_Handheld, + ReverseNX_Docked, + ReverseNX_Undefined, + }; + + void SetMode(bool isDefault, bool setDock, char* descBuf, size_t bufSize) { + auto changeHandler = [this](ReverseNXMode newMode) { + if (this->currentMode == ReverseNX_Undefined || this->currentMode != newMode) { + if (R_FAILED(this->ipc->SetMode(newMode))) { + this->ipc->ipcStatus = Ipc::IpcStatus_ConnectFailed; + return; + } + this->currentMode = newMode; + } + }; + + tsl::hlp::ScopeGuard updateBufGuard([&] { this->ipc->UpdateStatusDesc(descBuf, bufSize); }); + + if (isDefault) { + changeHandler(ReverseNX_SystemDefault); + return; + } + + changeHandler(setDock ? ReverseNX_Docked : ReverseNX_Handheld); + } + + ModeSync() { + this->ipc = new Ipc; + this->ipc->Init(); + } + + ~ModeSync() { + this->ipc->Exit(); + delete this->ipc; + } + +protected: + struct Ipc { + #define API_VER 2 + #define SERVICE_NAME "sysclkOC" + + enum SysClkIpcCmd { + SysClkIpcCmd_GetApiVersion = 0, + SysClkIpcCmd_GetConfigValues = 9, + SysClkIpcCmd_SetReverseNXRTMode = 11, + }; + + enum SysClkConfigValue { + SysClkConfigValue_SyncReverseNXMode = 4, + SysClkConfigValue_EnumMax = 8, + }; + + struct SysClkConfigValueList { + uint64_t values[SysClkConfigValue_EnumMax]; + }; + + enum IpcStatus { + IpcStatus_OK, + + IpcStatus_Unknown, + IpcStatus_NotRunning, + IpcStatus_InitFailed, + IpcStatus_ConnectFailed, + IpcStatus_UnsupportedVer, + + IpcStatus_Count, + }; + + static constexpr const char* IpcStatusStr[IpcStatus_Count] = { + "", + + "Unknown", + "Err: Not running", + "Err: Failed to init", + "Err: Failed to connect", + "Err: Unsupported version", + }; + + Result Init() { + Result rc = 0; + + rc = IpcInitialize(); + + rc = GetStatus(); + + return rc; + } + + void Exit() { + if (--refCnt == 0) + serviceClose(&service); + } + + bool IsServiceRunning() { + Handle handle; + SmServiceName name = smEncodeName(SERVICE_NAME); + if (R_FAILED(smRegisterService(&handle, name, false, 1))) { + return true; + } + svcCloseHandle(handle); + smUnregisterService(name); + return false; + } + + Result IpcInitialize(void) { + Result rc = 0; + refCnt++; + + if (serviceIsActive(&service)) + return 0; + + rc = smGetService(&service, SERVICE_NAME); + + if (R_FAILED(rc)) { + this->ipcStatus = IpcStatus_InitFailed; + rc = this->ipcStatus; + this->Exit(); + return rc; + } + + return rc; + } + + Result GetApiVersion(u32* outVer) { + return serviceDispatchOut(&service, SysClkIpcCmd_GetApiVersion, *outVer); + } + + Result GetConfigValues(SysClkConfigValueList* outConfigValues) { + return serviceDispatchOut(&service, SysClkIpcCmd_GetConfigValues, *outConfigValues); + } + + Result SetMode(ReverseNXMode mode) { + return serviceDispatchIn(&service, SysClkIpcCmd_SetReverseNXRTMode, mode); + } + + Result GetStatus() { + if (!IsServiceRunning()) { + this->ipcStatus = IpcStatus_NotRunning; + return this->ipcStatus; + } + + tsl::hlp::ScopeGuard exitSrvGuard([&] { this->Exit(); }); + + uint32_t api_ver; + if (R_FAILED(GetApiVersion(&api_ver))) { + this->ipcStatus = IpcStatus_ConnectFailed; + return this->ipcStatus; + } + + if (api_ver != API_VER) { + this->ipcStatus = IpcStatus_UnsupportedVer; + return this->ipcStatus; + } + + SysClkConfigValueList* list = new SysClkConfigValueList; + tsl::hlp::ScopeGuard listGuard([&] { delete list; }); + + if (R_FAILED(GetConfigValues(list))) { + this->ipcStatus = IpcStatus_ConnectFailed; + return this->ipcStatus; + } + + exitSrvGuard.dismiss(); + + shouldSync = bool(list->values[SysClkConfigValue_SyncReverseNXMode]); + this->ipcStatus = IpcStatus_OK; + return this->ipcStatus; + } + + void UpdateStatusDesc(char* buffer, size_t size) { + snprintf(buffer, size, + "Mode Sync: %s%s", + IpcStatusStr[ipcStatus], + (ipcStatus == IpcStatus_OK) ? ( + shouldSync ? "ON" : "OFF" + ) : "" + ); + } + + Service service = {}; + std::atomic refCnt = 0; + IpcStatus ipcStatus = IpcStatus_Unknown; + bool shouldSync = false; + }; + + Ipc* ipc = nullptr; + ReverseNXMode currentMode = ReverseNX_Undefined; +}; + bool* def = 0; bool* isDocked = 0; bool* pluginActive = 0; @@ -17,6 +212,7 @@ bool plugin = false; char DockedChar[32]; char SystemChar[32]; char PluginChar[36]; +char SysclkChar[0x40]; uint64_t PID = 0; Handle remoteSharedMemory = 1; SharedMemory _sharedmemory = {}; @@ -73,7 +269,7 @@ bool CheckPort () { class GuiTest : public tsl::Gui { public: - GuiTest(u8 arg1, u8 arg2, bool arg3) { } + GuiTest(ModeSync* p) : modeSync(p) { } // Called when this Gui gets loaded to create the UI // Allocate all elements on the heap. libtesla will make sure to clean them up when not needed anymore @@ -112,6 +308,7 @@ public: else { renderer->drawString(SystemChar, false, x, y+40, 20, renderer->a(0xFFFF)); renderer->drawString(DockedChar, false, x, y+60, 20, renderer->a(0xFFFF)); + renderer->drawString(SysclkChar, false, x, y+80, 20, renderer->a(0xFFFF)); } } }), 100); @@ -190,6 +387,8 @@ public: if (_def) sprintf(SystemChar, "Controlled by system: Yes"); else sprintf(SystemChar, "Controlled by system: No"); + + modeSync->SetMode(_def, _isDocked, SysclkChar, sizeof(SysclkChar)); } else i++; } @@ -200,6 +399,8 @@ public: virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override { return false; // Return true here to singal the inputs have been consumed } + + ModeSync* modeSync; }; class OverlayTest : public tsl::Overlay { @@ -248,9 +449,11 @@ public: }); + modeSync = new ModeSync; } // Called at the start to initialize all services necessary for this Overlay virtual void exitServices() override { + delete modeSync; shmemClose(&_sharedmemory); fsdevUnmountDevice("sdmc"); } // Callet at the end to clean up all services previously initialized @@ -260,8 +463,10 @@ public: virtual void onHide() override {} // Called before overlay wants to change from visible to invisible state virtual std::unique_ptr loadInitialGui() override { - return initially(1, 2, true); // Initial Gui to load. It's possible to pass arguments to it's constructor like this + return initially(modeSync); // Initial Gui to load. It's possible to pass arguments to it's constructor like this } + + ModeSync* modeSync = nullptr; }; int main(int argc, char **argv) {