commit 320617e2e50d1ebaf35da287c5cdd8a04a40ff49 Author: Bottersnike Date: Mon Jun 13 04:22:52 2022 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..73fabf0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +dist/ +build/ +builddir/ +srcdir/ +.vscode/ + +# Don't distribute the libpcp sources +src/micetools/lib/libpcp/ +# And keep build artifacts out of git! +src/libpcp.lib diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..09290fb --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +BUILD_DIR := build +BUILD_DIR_32 := $(BUILD_DIR)/build32 +BUILD_DIR_64 := $(BUILD_DIR)/build64 +DIST_DIR := dist + +MICE_32 = "$(BUILD_DIR_32)/src\mice.exe" +MICE_64 = "$(BUILD_DIR_64)/src\mice.exe" + +VCVARS_32 = C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars32.bat +VCVARS_64 = C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat + +.PHONY: all +all: mice86 dist + +mice86: + "$(VCVARS_32)" \ + & meson setup --backend ninja --cross cross-32.ini $(BUILD_DIR_32) \ + & meson compile -C $(BUILD_DIR_32) + +mice64: + "$(VCVARS_64)" \ + & meson setup --cross cross-64.ini $(BUILD_DIR_64) \ + & meson compile -C $(BUILD_DIR_64) + +.PHONY: clean +clean: + @del /S /F /Q $(BUILD_DIR) + @rmdir /S /Q $(BUILD_DIR) + @del /S /F /Q $(DIST_DIR) + @rmdir /S /Q $(DIST_DIR) + +.PHONY: dist +dist: + @-mkdir $(DIST_DIR) + @copy /Y "$(BUILD_DIR_32)/src/micetools/micekeychip\micekeychip.exe" "$(DIST_DIR)/micekeychip.exe" + @copy /Y "$(BUILD_DIR_32)/src/micetools/micepatch\micepatch.exe" "$(DIST_DIR)/micepatch.exe" + @copy /Y "$(BUILD_DIR_32)/src/micetools/lib/libpcp\libpcp.lib" "$(DIST_DIR)/libpcp.lib" + + @copy /Y "$(BUILD_DIR_32)/src/micetools/launcher\mice.exe" "$(DIST_DIR)/mice86.exe" + @copy /Y "$(BUILD_DIR_32)/src/micetools/dll\mice.dll" "$(DIST_DIR)/mice86.dll" +# @copy /Y "$(BUILD_DIR_64)/src/micetools/launcher\mice.exe" "$(DIST_DIR)/mice64.exe" +# @copy /Y "$(BUILD_DIR_64)/src/micetools/dll\mice.dll" "$(DIST_DIR)/mice64.dll" + + @xcopy /E /H /C /R /Q /Y src\system "$(DIST_DIR)\system/*" + @xcopy /E /H /C /R /Q /Y src\tools "$(DIST_DIR)\tools/*" diff --git a/README.md b/README.md new file mode 100644 index 0000000..f71f9c2 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# micetools + +For a complete build, ensure to download `libpcp.lib` and place it in `src/` +prior to running `make`. + +**Note:** exe hashes will not match the distributed binaries when compiling +using `libpcp.lib` as the distributed binaries have been compiled directly +against libpcp's source. diff --git a/cross-32.ini b/cross-32.ini new file mode 100644 index 0000000..2c8e66f --- /dev/null +++ b/cross-32.ini @@ -0,0 +1,11 @@ +[binaries] +c = 'cl' +cpp = 'cl' +strip = 'cl' + +[host_machine] +system = 'windows' +cpu_family = 'x86' +cpu = 'i686' +endian = 'little' + diff --git a/cross-64.ini b/cross-64.ini new file mode 100644 index 0000000..63ba9c9 --- /dev/null +++ b/cross-64.ini @@ -0,0 +1,10 @@ +[binaries] +c = 'cl' +cpp = 'cl' +strip = 'cl' + +[host_machine] +system = 'windows' +cpu_family = 'x86_64' +cpu = 'x86_64' +endian = 'little' diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..e997b50 --- /dev/null +++ b/meson.build @@ -0,0 +1,14 @@ +project('micetools', 'c') + +if (host_machine.cpu_family() == 'x86') + add_project_arguments('-DMICE_WIN32', language: 'c') +endif + +add_project_arguments( + '-DWIN32_LEAN_AND_MEAN', # Strip out headers we don't really need + '-D_WIN32_WINNT=_WIN32_WINNT_WINXP', # hahahahaha I hate it + # '/O1', # Optimise size + language: 'c', +) + +subdir('src') diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..ce9a913 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('vsenv', type: 'boolean', value: false) diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..3724ab2 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,4 @@ +inih = subproject('inih_dep') + +libs_dir = meson.current_source_dir() +subdir('micetools') diff --git a/src/micetools/dll/com.c b/src/micetools/dll/com.c new file mode 100644 index 0000000..c6de1cf --- /dev/null +++ b/src/micetools/dll/com.c @@ -0,0 +1,141 @@ +#include "com.h" + +com_hook_t* com_hook_list = NULL; +com_hook_t* new_com_hook(BYTE port) { + com_hook_t* hook = (com_hook_t*)malloc(sizeof *hook); + + swprintf(hook->wName, (sizeof hook->wName) / (sizeof hook->wName[0]), L"COM%d", port); + hook->com = port; + + hook->GetCommState = NULL; + hook->SetCommState = NULL; + hook->GetCommTimeouts = NULL; + hook->SetCommTimeouts = NULL; + hook->SetupComm = NULL; + hook->PurgeComm = NULL; + hook->GetCommModemStatus = NULL; + hook->WaitCommEvent = NULL; + hook->ClearCommError = NULL; + + return hook; +}; +void hook_com(com_hook_t* hook) { + hook->next = NULL; + hook->virtual_handle = NULL; + if (com_hook_list == NULL) { + com_hook_list = hook; + return; + } + + com_hook_t* hl = com_hook_list; + while (hl->next != NULL) hl = hl->next; + hl->next = hook; +}; + +BOOL WINAPI FakeGetCommState(HANDLE hFile, LPDCB lpDCB) { + log_misc("comm", "GetCommState(0x%p, 0x%p)", hFile, lpDCB); + + com_hook_t* hook = com_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile && hook->GetCommState != NULL) { + return hook->GetCommState(hook->data, lpDCB); + } + } + return TrueGetCommState(hFile, lpDCB); +} +BOOL WINAPI FakeSetCommState(HANDLE hFile, LPDCB lpDCB) { + log_misc("comm", "SetCommState(0x%p, 0x%p)", hFile, lpDCB); + + com_hook_t* hook = com_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile && hook->SetCommState != NULL) { + return hook->SetCommState(hook->data, lpDCB); + } + } + return TrueSetCommState(hFile, lpDCB); +} +BOOL WINAPI FakeGetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) { + log_misc("comm", "GetCommTimeouts(0x%p, 0x%p)", hFile, lpCommTimeouts); + com_hook_t* hook = com_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile && hook->GetCommTimeouts != NULL) { + return hook->GetCommTimeouts(hook->data, lpCommTimeouts); + } + } + return TrueGetCommTimeouts(hFile, lpCommTimeouts); +} +BOOL WINAPI FakeSetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) { + log_misc("comm", "SetCommTimeouts(0x%p, 0x%p)", hFile, lpCommTimeouts); + com_hook_t* hook = com_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile && hook->SetCommTimeouts != NULL) { + return hook->SetCommTimeouts(hook->data, lpCommTimeouts); + } + } + return TrueSetCommTimeouts(hFile, lpCommTimeouts); +} +BOOL WINAPI FakeSetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue) { + log_misc("comm", "SetupCom(0x%p, 0x%08x, 0x%08x)", hFile, dwInQueue, dwOutQueue); + com_hook_t* hook = com_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile && hook->SetupComm != NULL) { + return hook->SetupComm(hook->data, dwInQueue, dwOutQueue); + } + } + return TrueSetupComm(hFile, dwInQueue, dwOutQueue); +} +BOOL WINAPI FakePurgeComm(HANDLE hFile, DWORD dwFlags) { + log_misc("comm", "PurgeComm(0x%p, 0x%08x)", hFile, dwFlags); + com_hook_t* hook = com_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile && hook->PurgeComm != NULL) { + return hook->PurgeComm(hook->data, dwFlags); + } + } + return TruePurgeComm(hFile, dwFlags); +} +BOOL WINAPI FakeGetCommModemStatus(HANDLE hFile, LPDWORD lpModelStat) { + log_misc("comm", "GetCommModemStatus(0x%p, 0x%p)", hFile, lpModelStat); + + com_hook_t* hook = com_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile && hook->GetCommModemStatus != NULL) { + return hook->GetCommModemStatus(hook->data, lpModelStat); + } + } + return TrueGetCommModemStatus(hFile, lpModelStat); +} +BOOL WINAPI FakeWaitCommEvent(HANDLE hFile, LPDWORD lpEvtMask, LPOVERLAPPED lpOverlapped) { + log_misc("comm", "WaitCommEvent"); + + com_hook_t* hook = com_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile && hook->WaitCommEvent != NULL) { + return hook->WaitCommEvent(hook->data, lpEvtMask, lpOverlapped); + } + } + return TrueWaitCommEvent(hFile, lpEvtMask, lpOverlapped); +} +BOOL WINAPI FakeClearCommError(HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT lpStat) { + log_misc("comm", "ClearCommError"); + + com_hook_t* hook = com_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile && hook->ClearCommError != NULL) { + return hook->ClearCommError(hook->data, lpErrors, lpStat); + } + } + return TrueClearCommError(hFile, lpErrors, lpStat); +} + +void hook_commio() { + hook("Kernel32.dll", "GetCommState", FakeGetCommState, (void**)&TrueGetCommState, 6); + hook("Kernel32.dll", "SetCommState", FakeSetCommState, (void**)&TrueSetCommState, 6); + hook("Kernel32.dll", "GetCommTimeouts", FakeGetCommTimeouts, (void**)&TrueGetCommTimeouts, 6); + hook("Kernel32.dll", "SetCommTimeouts", FakeSetCommTimeouts, (void**)&TrueSetCommTimeouts, 6); + hook("Kernel32.dll", "SetupComm", FakeSetupComm, (void**)&TrueSetupComm, 6); + hook("Kernel32.dll", "PurgeComm", FakePurgeComm, (void**)&TruePurgeComm, 6); + hook("Kernel32.dll", "GetCommModemStatus", FakeGetCommModemStatus, (void**)&TrueGetCommModemStatus, 6); + hook("Kernel32.dll", "WaitCommEvent", FakeWaitCommEvent, (void**)&TrueWaitCommEvent, 6); + hook("Kernel32.dll", "ClearCommError", FakeClearCommError, (void**)&TrueClearCommError, 6); +} \ No newline at end of file diff --git a/src/micetools/dll/com.h b/src/micetools/dll/com.h new file mode 100644 index 0000000..9360979 --- /dev/null +++ b/src/micetools/dll/com.h @@ -0,0 +1,47 @@ +#include +#include +#include + +static BOOL(WINAPI* TrueGetCommState)(HANDLE hFile, LPDCB lpDCB); +static BOOL(WINAPI* TrueSetCommState)(HANDLE hFile, LPDCB lpDCB); +static BOOL(WINAPI* TrueGetCommTimeouts)(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts); +static BOOL(WINAPI* TrueSetCommTimeouts)(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts); +static BOOL(WINAPI* TrueSetupComm)(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue); +static BOOL(WINAPI* TruePurgeComm)(HANDLE hFile, DWORD dwFlags); +static BOOL(WINAPI* TrueGetCommModemStatus)(HANDLE hFile, LPDWORD lpModelStat); +static BOOL(WINAPI* TrueWaitCommEvent)(HANDLE hFile, LPDWORD lpEvtMask, LPOVERLAPPED lpOverlapped); +static BOOL(WINAPI* TrueClearCommError)(HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT lpStat); + +typedef BOOL(FnGetCommState)(void* com, LPDCB lpDCB); +typedef BOOL(FnSetCommState)(void* com, LPDCB lpDCB); +typedef BOOL(FnGetCommTimeouts)(void* com, LPCOMMTIMEOUTS lpCommTimeouts); +typedef BOOL(FnSetCommTimeouts)(void* com, LPCOMMTIMEOUTS lpCommTimeouts); +typedef BOOL(FnSetupComm)(void* com, DWORD dwInQueue, DWORD dwOutQueue); +typedef BOOL(FnPurgeComm)(void* com, DWORD dwFlags); +typedef BOOL(FnGetCommModemStatus)(void* com, LPDWORD lpModelStat); +typedef BOOL(FnWaitCommEvent)(void* com, LPDWORD lpEvtMask, LPOVERLAPPED lpOverlapped); +typedef BOOL(FnClearCommError)(void* com, LPDWORD lpErrors, LPCOMSTAT lpStat); + +typedef struct com_hook { + HANDLE virtual_handle; + WCHAR wName[7]; // max is COM255 + BYTE com; + + FnGetCommState* GetCommState; + FnSetCommState* SetCommState; + FnGetCommTimeouts* GetCommTimeouts; + FnSetCommTimeouts* SetCommTimeouts; + FnSetupComm* SetupComm; + FnPurgeComm* PurgeComm; + FnGetCommModemStatus* GetCommModemStatus; + FnWaitCommEvent* WaitCommEvent; + FnClearCommError* ClearCommError; + + void* data; + + struct com_hook* next; +} com_hook_t; + +com_hook_t* new_com_hook(BYTE port); +void hook_com(com_hook_t* hook); +void hook_com(); diff --git a/src/micetools/dll/comdevice.c b/src/micetools/dll/comdevice.c new file mode 100644 index 0000000..0a370bb --- /dev/null +++ b/src/micetools/dll/comdevice.c @@ -0,0 +1,44 @@ +#include "comdevice.h" + +BOOL DevGetCommState(void* data, LPDCB lpDCB) { return TRUE; } +BOOL DevSetCommState(void* data, LPDCB lpDCB) { return TRUE; } +BOOL DevGetCommTimeouts(void* data, LPCOMMTIMEOUTS lpCommTimeouts) { return TRUE; } +BOOL DevSetCommTimeouts(void* data, LPCOMMTIMEOUTS lpCommTimeouts) { return TRUE; } +BOOL DevSetupComm(void* data, DWORD dwInQueue, DWORD dwOutQueue) { return TRUE; } +BOOL DevPurgeComm(void* data, DWORD dwFlags) { + if (dwFlags & PURGE_RXCLEAR) ((com_device_t*)data)->filled = 0; + return TRUE; +} +BOOL DevGetCommModemStatus(void* data, LPDWORD lpModelStat) { + // TODO: JVS SENSE + return TRUE; +} +BOOL DevWaitCommEvent(void* data, LPDWORD lpEvtMask, LPOVERLAPPED lpOverlapped) { + WaitForSingleObject(((com_device_t*)data)->event, INFINITE); + if (lpOverlapped != NULL) SetEvent(lpOverlapped->hEvent); + return TRUE; +} +BOOL DevClearCommError(void* data, LPDWORD lpErrors, LPCOMSTAT lpStat) { return TRUE; } + +com_device_t* new_com_device(BYTE port) { + com_device_t* hook = (com_device_t*)malloc(sizeof *hook); + com_hook_t* com = new_com_hook(port); + file_hook_t* file = new_file_hook(com->wName); + com->data = hook; + + com->GetCommState = DevGetCommState; + com->SetCommState = DevSetCommState; + com->GetCommTimeouts = DevGetCommTimeouts; + com->SetCommTimeouts = DevSetCommTimeouts; + com->SetupComm = DevSetupComm; + com->PurgeComm = DevPurgeComm; + com->GetCommModemStatus = DevGetCommModemStatus; + com->WaitCommEvent = DevWaitCommEvent; + com->ClearCommError = DevClearCommError; + + hook->filled = 0; + hook->event = CreateEventW(NULL, TRUE, FALSE, hook->com->wName); + hook->com = com; + + return hook; +} diff --git a/src/micetools/dll/comdevice.h b/src/micetools/dll/comdevice.h new file mode 100644 index 0000000..1b908f1 --- /dev/null +++ b/src/micetools/dll/comdevice.h @@ -0,0 +1,11 @@ +#include "com.h" +#include "files.h" + +typedef struct com_device { + com_hook_t* com; + file_hook_t* file; + + BYTE queue[2048]; + DWORD filled; + HANDLE event; +} com_device_t; diff --git a/src/micetools/dll/dllmain.c b/src/micetools/dll/dllmain.c new file mode 100644 index 0000000..6bb2d7d --- /dev/null +++ b/src/micetools/dll/dllmain.c @@ -0,0 +1,147 @@ +#include +#include +#pragma comment(lib, "Shlwapi.lib") + +#include + +#include "../lib/mice/mice.h" +#include "drivers/mx.h" +#include "files.h" +#include "com.h" +#include "micesetupapi.h" + +WCHAR exePath[MAX_PATH + 1]; + +void enable_traces() { + if (wcscmp(exePath, L"Game.exe") == 0) { + *(DWORD*)(0x008717a0) = 1; + *(DWORD*)(0x00871728) = 1; + *(DWORD*)(0x00871cd8) = 1; + *(DWORD*)(0x00872660) = 1; + *(DWORD*)(0x0087297c) = 1; // LOG_EN_EEPROM + *(DWORD*)(0x00872980) = 1; + *(DWORD*)(0x00872988) = 1; + *(DWORD*)(0x00873540) = 1; + *(DWORD*)(0x00873538) = 1; + *(DWORD*)(0x0087f890) = 1; + *(DWORD*)(0x00882ce4) = 1; + *(DWORD*)(0x00882cec) = 1; + *(DWORD*)(0x00883018) = 1; + *(DWORD*)(0x00886ff8) = 1; + *(DWORD*)(0x0088b1b8) = 1; + *(DWORD*)(0x0088b1c0) = 1; + } else if (wcscmp(exePath, L"ALLNetProc_Win.exe") == 0) { + *(DWORD*)(0x004f48b0) = 1; // LOG_EN_DONGLE + *(DWORD*)(0x004f989c) = 1; // LOG_EN_PCP + *(DWORD*)(0x004f7d04) = 1; // LOG_EN_EEPROM + *(DWORD*)(0x004f7d08) = 1; // LOG_EN_BACKUP + *(DWORD*)(0x004f8118) = 1; // LOG_EN_RTC + *(DWORD*)(0x004f96dc) = 1; // LOG_EN_GFETCHER + *(DWORD*)(0x004f8338) = 1; // LOG_EN_NETWORK + *(DWORD*)(0x004f88c0) = 1; // LOG_EN_MASTER + *(DWORD*)(0x004f88b0) = 1; // LOG_EN_HM + *(DWORD*)(0x004f8330) = 1; // LOG_EN_SRAM + *(DWORD*)(0x004f9728) = 1; // LOG_EN_PLATFORM + + //*(DWORD*)(0x004f9e7c) = 5; // LOG_EN_ASSERTS (handled by the bellow + // instead) + *(DWORD*)(0x004e884c) = 10; // ALPB_LOG_LEVEL (0=None, 1=Error, + // 2=Warning, 3=Most, 4=Debug) + } else if (wcscmp(exePath, L"ORIG_mxsegaboot.exe") == 0 || wcscmp(exePath, L"mxsegaboot.exe") == 0) { + // start of am + *(DWORD*)(0x0054fcd8) = 1; // LOG_EN_JVS_DRIVER + *(DWORD*)(0x00538810) = 1; // LOG_EN_DIPSW + *(DWORD*)(0x0054afc8) = 1; // LOG_EN_ATA + *(DWORD*)(0x00544b78) = 1; // LOG_EN_DONGLE + *(DWORD*)(0x00536c98) = 1; // LOG_EN_EEPROM + *(DWORD*)(0x00537834) = 1; // LOG_EN_GCATCHER + *(DWORD*)(0x00537d4c) = 1; // LOG_EN_GDELIVER + *(DWORD*)(0x0053828c) = 1; // LOG_EN_GFETCHER + *(DWORD*)(0x00547fd0) = 1; // LOG_EN_HWRESET + *(DWORD*)(0x00547fd8) = 1; // LOG_EN_PLATFORM + *(DWORD*)(0x00548268) = 1; // LOG_EN_HM + *(DWORD*)(0x00536f30) = 1; // LOG_EN_BACKUP + *(DWORD*)(0x00547fcc) = 1; // LOG_EN_CMOS + *(DWORD*)(0x005382d8) = 1; // LOG_EN_INSTALL + *(DWORD*)(0x00538818) = 1; // LOG_EN_JVS + *(DWORD*)(0x00544b68) = 1; // LOG_EN_JVSP + *(DWORD*)(0x0054b3d8) = 1; // LOG_EN_MASTER + *(DWORD*)(0x00548280) = 1; // LOG_EN_NETWORK + *(DWORD*)(0x00548050) = 1; // LOG_EN_RTC + *(DWORD*)(0x00536c94) = 1; // LOG_EN_SRAM + *(DWORD*)(0x005487f8) = 1; // LOG_EN_STORAGE + *(DWORD*)(0x00536c90) = 1; // LOG_EN_SYS + // end of am + *(DWORD*)(0x00536c70) = 1; // LOG_EN_PCPT + } else if (wcscmp(exePath, L"maimai_dump_.exe") == 0) { + *(DWORD*)(0x00c8ab7c) = 1; // LOG_EN_SRAM + *(DWORD*)(0x00f406f0) = 1; // LOG_EN_CABINET_DL + *(DWORD*)(0x00c88680) = 1; // LOG_EN_INSTALL + *(DWORD*)(0x00c8ab78) = 1; // LOG_EN_EEPROM + *(DWORD*)(0x00c88608) = 1; // LOG_EN_PLATFORM + *(DWORD*)(0x00c88dd0) = 1; // LOG_EN_STORAGE + *(DWORD*)(0x00ca25ec) = 1; // LOG_EN_PCP + *(DWORD*)(0x00c96ed0) = 1; // LOG_EN_HM + *(DWORD*)(0x00f406ec) = 1; // LOG_EN_HTTP + *(DWORD*)(0x00c89ca8) = 1; // LOG_EN_DIPSW + *(DWORD*)(0x00c96ed8) = 1; // LOG_EN_DONGLE + *(DWORD*)(0x00c9a83c) = 1; // LOG_EN_JVSP + *(DWORD*)(0x00c89730) = 1; // LOG_EN_NETWORK + *(DWORD*)(0x00c9a440) = 1; // LOG_EN_JVST_THREAD + *(DWORD*)(0x00c8ab80) = 1; // LOG_EN_JVS + *(DWORD*)(0x00c89fc8) = 1; // LOG_EN_MASTER + *(DWORD*)(0x00c88bb8) = 1; // LOG_EN_RTC + *(DWORD*)(0x00c9e420) = 1; // LOG_EN_JVST_DRIVER + *(DWORD*)(0x00c89320) = 1; // LOG_EN_BACKUP + *(DWORD*)(0x00c88bb4) = 1; // LOG_EN_HWRESET + *(DWORD*)(0x00c830e8) = 1; // LOG_EN + } else if (wcscmp(exePath, L"mxnetwork.exe") == 0) { + *(DWORD*)(0x004438e0) = 1; // LOG_EN_PCP + *(DWORD*)(0x004433f8) = 1; // LOG_EN_UTL + } else { + log_warning("traces", "No known traces for %ls", exePath); + } +} + +void prebind_hooks() { + hook_setupapi(); + hook_commio(); + hook_io(); +} + +void init_injection() { + // We're in a new context now, so need to reconfigure + setup_logging(); + log_info(BOOT_LOGGER, "Handover complete. Now executing within %ls", exePath); + + dmi_build_default(); + + enable_traces(); + + setup_columba(); + setup_mxsram(); + setup_mxsuperio(); + setup_mxjvs(); + setup_mxhwreset(); + setup_mxsmbus(); + + if (!add_fake_device(&PLATFORM_GUID, L"\\\\.\\platform")) { + log_error("platform", "failed to install platform device"); + } + + // Must be the last thing called! + // register_devices(); + prebind_hooks(); + setup_hooks(); +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { + if (ul_reason_for_call != DLL_PROCESS_ATTACH) return TRUE; + + GetModuleFileNameW(NULL, exePath, MAX_PATH); + wcscpy_s(exePath, MAX_PATH + 1, PathFindFileNameW(exePath)); + + init_injection(); + + return TRUE; +} diff --git a/src/micetools/dll/drivers/columba.c b/src/micetools/dll/drivers/columba.c new file mode 100644 index 0000000..3e3265f --- /dev/null +++ b/src/micetools/dll/drivers/columba.c @@ -0,0 +1,51 @@ +#include + +#include "../lib/dmi/dmi.h" +#include "../lib/mice/mice.h" +#include "files.h" + +BOOL columba_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, + DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { + switch (dwIoControlCode) { + case IOCTL_COLUMBA_READ_DMI: + log_misc("columba", + "DeviceIoControl(, , 0x%p, 0x%x, -, " + "0x%x, -, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + memset(lpOutBuffer, 0, nOutBufferSize); + + if (((LPWORD)lpInBuffer)[0] == 0x0000) { + DMI_HEADER dmi = { + .Signature = {'_', 'D', 'M', 'I', '_'}, + .Checksum = 0, + .StructLength = dmi_size, + .StructAddr = 0xdeeabeef, + .NumberOfStructs = 0x20, + .BCDRevision = 0, + .Reserved = 0, + }; + dmi.Checksum = dmi_calc_checksum((char*)&dmi, 15); + + memcpy(lpOutBuffer, &dmi, sizeof(DMI_HEADER)); + if (lpBytesReturned) *lpBytesReturned = sizeof(DMI_HEADER); + } else { + memcpy(lpOutBuffer, dmi_table, dmi_size); + if (lpBytesReturned) *lpBytesReturned = dmi_size; + } + + break; + default: + log_warning("columba", "unhandled 0x%08x", dwIoControlCode); + return FALSE; + } + + return FALSE; +} + +void setup_columba() { + file_hook_t* columba = new_file_hook(L"\\\\.\\columba"); + columba->DeviceIoControl = &columba_DeviceIoControl; + + hook_file(columba); +} diff --git a/src/micetools/dll/drivers/mx.h b/src/micetools/dll/drivers/mx.h new file mode 100644 index 0000000..8254514 --- /dev/null +++ b/src/micetools/dll/drivers/mx.h @@ -0,0 +1,23 @@ +#include "../files.h" +#include + +FnDeviceIoControl mxhwreset_DeviceIoControl; +void setup_mxhwreset(); + +FnDeviceIoControl mxjvs_DeviceIoControl; +void setup_mxjvs(); + +FnDeviceIoControl mxsmbus_DeviceIoControl; +void setup_mxsmbus(); + +FnDeviceIoControl mxsram_DeviceIoControl; +FnSetFilePointer mxsram_SetFilePointer; +FnWriteFile mxsram_WriteFile; +FnReadFile mxsram_ReadFile; +void setup_mxsram(); + +FnDeviceIoControl mxsuperio_DeviceIoControl; +void setup_mxsuperio(); + +DEFINE_GUID(MXSMBUS_GUID, 0x5C49E1FE, 0x3FEC, 0x4B8D, 0xA4, 0xB5, 0x76, 0xBE, 0x70, 0x25, 0xD8, 0x42); +DEFINE_GUID(PLATFORM_GUID, 0x86E0D1E0, 0x8089, 0x11D0, 0x9C, 0xE4, 0x08, 0x00, 0x3e, 0x30, 0x1F, 0x73); diff --git a/src/micetools/dll/drivers/mxhwreset.c b/src/micetools/dll/drivers/mxhwreset.c new file mode 100644 index 0000000..ff1ce13 --- /dev/null +++ b/src/micetools/dll/drivers/mxhwreset.c @@ -0,0 +1,24 @@ +#include + +#include "../lib/mice/mice.h" +#include "mx.h" + +BOOL mxhwreset_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, + DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { + switch (dwIoControlCode) { + case IOCTL_MXHWRESET_RESET: + if (lpBytesReturned) *lpBytesReturned = 0; + break; + default: + log_warning("mxhwreset", "unhandled 0x%08x", dwIoControlCode); + return FALSE; + } + return TRUE; +} + +void setup_mxhwreset() { + file_hook_t* mxhwreset = new_file_hook(L"\\\\.\\mxhwreset"); + mxhwreset->DeviceIoControl = &mxhwreset_DeviceIoControl; + + hook_file(mxhwreset); +} diff --git a/src/micetools/dll/drivers/mxjvs.c b/src/micetools/dll/drivers/mxjvs.c new file mode 100644 index 0000000..cf1f7e5 --- /dev/null +++ b/src/micetools/dll/drivers/mxjvs.c @@ -0,0 +1,37 @@ +#include + +#include "../lib/mice/mice.h" +#include "mx.h" + +BOOL mxjvs_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, + DWORD nInBufferSize, LPVOID lpOutBuffer, + DWORD nOutBufferSize, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped) { + switch (dwIoControlCode) { + case IOCTL_MXJVS_EXCHANGE: + log_misc("mxjvs", + "DeviceIoControl(, , 0x%p, 0x%x, -, " + "0x%x, -, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + // mxjvsDevice->exchange( + // lpbyte(lpInBuffer), nInBufferSize & 0xFFFF, + // lpbyte(lpOutBuffer), lpBytesReturned + // ); + + if (lpBytesReturned) *lpBytesReturned = 0; + break; + default: + log_warning("mxjvs", "unhandled 0x%08x", dwIoControlCode); + return FALSE; + } + + return TRUE; +} + +void setup_mxjvs() { + file_hook_t* mxjvs = new_file_hook(L"\\\\.\\mxjvs"); + mxjvs->DeviceIoControl = &mxjvs_DeviceIoControl; + + hook_file(mxjvs); +} diff --git a/src/micetools/dll/drivers/mxparallel.c b/src/micetools/dll/drivers/mxparallel.c new file mode 100644 index 0000000..e69de29 diff --git a/src/micetools/dll/drivers/mxsmbus.c b/src/micetools/dll/drivers/mxsmbus.c new file mode 100644 index 0000000..151d461 --- /dev/null +++ b/src/micetools/dll/drivers/mxsmbus.c @@ -0,0 +1,363 @@ +#include + +#include "../lib/dmi/dmi.h" +#include "../lib/mice/mice.h" +#include "mx.h" +#include "smbus.h" + +// PCA9535 (DIPSW) +#define PCA9535_WRITE 0x04 +#define PCA9535_READ 0x05 + +#define PCA9535_IN0 0x00 +#define PCA9535_IN1 0x01 +#define PCA9535_OUT0 0x02 +#define PCA9535_OUT1 0x03 +#define PCA9535_INV0 0x04 +#define PCA9535_INV1 0x05 +#define PCA9535_CONF0 0x06 +#define PCA9535_CONF1 0x07 + +#define PCA9535 0x20 +#define EEPROM 0x57 + +#define EEPROM_DUMP L"dev/eeprom.bin" +typedef struct eeprom_reg { + BYTE data[32]; +} eeprom_reg_t; +typedef struct eeprom_bank { + eeprom_reg_t reg[0x100]; +} eeprom_bank_t; + +// 256 registers, 32 bytes each +eeprom_bank_t EEPROM_DATA; +/* + * Known registers: + * - Reg 0x00: Stores ??? in [00] + * - Reg 0x08: Stores LPC address in [00, 01] + * - Reg 0x16: Stores ??? in [00, 01] + * - Reg 0x0e: Stores + */ + +typedef uint_fast32_t crc_t; + +static const crc_t crc_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, + 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, + 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, + 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, + 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, + 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, + 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, + 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, + 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, + 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, + 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, + 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; +#define CRC_ALGO_TABLE_DRIVEN 1 +static inline crc_t crc_init(void) { return 0xffffffff; } +crc_t crc_update(crc_t crc, const void* data, size_t data_len) { + const unsigned char* d = (const unsigned char*)data; + unsigned int tbl_idx; + + while (data_len--) { + tbl_idx = (crc ^ *d) & 0xff; + crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffffffff; + d++; + } + return crc & 0xffffffff; +} +static inline crc_t crc_finalize(crc_t crc) { return crc ^ 0xffffffff; } + +void eeprom_dump() { + HANDLE dump = _CreateFileW(EEPROM_DUMP, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); + if (dump == INVALID_HANDLE_VALUE) { + log_error("eeprom", "CreateFileA(EEPROM_DUMP) failed"); + return; + } + _WriteFile(dump, &EEPROM_DATA, sizeof EEPROM_DATA, NULL, NULL); + FlushFileBuffers(dump); + _CloseHandle(dump); +} +void eeprom_restore() { + HANDLE dump = + _CreateFileW(EEPROM_DUMP, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (dump == INVALID_HANDLE_VALUE) return; + DWORD read; + if (!_ReadFile(dump, &EEPROM_DATA, sizeof EEPROM_DATA, &read, NULL)) + log_error("eeprom", "failed to restore (%d)", GetLastError()); + _CloseHandle(dump); +} + +DWORD eeprom_crc(BYTE reg) { + if (reg == 0x80 || reg == 0x280) { + // Some registers are only treated as 16 byte values + return crc_finalize(crc_update(crc_init(), EEPROM_DATA.reg[reg].data + 4, 12)); + } + return crc_finalize(crc_update(crc_init(), EEPROM_DATA.reg[reg].data + 4, 28)); +} +void eeprom_read(BYTE reg, BYTE index, BYTE* data, BYTE length) { + eeprom_restore(); + for (BYTE i = index; i < index + length; i++) { + if (i > 0x1f) break; + + BYTE byte = EEPROM_DATA.reg[reg].data[i]; + + // If register has a CRC + // if (reg == 0x00 || reg == 0x01 || reg == 0x02 || reg == 0x10 || reg + // == 0x11 || reg == 0x12 || reg == 0x200) { + if (true) { + // Intercept the read and inject a CRC instead + if (i == 0x00 || i == 0x01 || i == 0x02 || i == 0x03) { + DWORD crc = eeprom_crc(reg); + byte = crc >> 8 * i & 0xff; + } + } + + data[i - index] = byte; + } +} +void eeprom_write(BYTE reg, BYTE index, BYTE* data, BYTE length) { + for (BYTE i = index; i < index + length; i++) { + if (i > 0x1f) break; + EEPROM_DATA.reg[reg].data[i] = data[i - index]; + } + eeprom_dump(); +} + +BYTE eeprom_read_one(BYTE reg, BYTE index) { + BYTE data; + eeprom_read(reg, index, &data, 1); + return data; +} +void eeprom_write_one(BYTE reg, BYTE index, BYTE data) { eeprom_write(reg, index, &data, 1); } + +BOOL mxsmbus_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, + DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { + mxsmbus_i2c_packet* i2c_packet = (mxsmbus_i2c_packet*)lpInBuffer; + mxsmbus_i2c_packet* i2c_out = (mxsmbus_i2c_packet*)lpOutBuffer; + + mxsmbus_request_packet* request_packet = (mxsmbus_request_packet*)lpInBuffer; + mxsmbus_request_packet* request_out = (mxsmbus_request_packet*)lpOutBuffer; + + BYTE dlen; + + switch (dwIoControlCode) { + case IOCTL_MXSMBUS_GET_VERSION: + log_misc("mxsmbus", + "DeviceIoControl(, , 0x%p, 0x%x, " + "-, 0x%x, -, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + ((LPDWORD)lpOutBuffer)[0] = 0x01020001; + if (lpBytesReturned) *lpBytesReturned = 4; + break; + case IOCTL_MXSMBUS_REQUEST: // dip r/w + log_misc("mxsmbus", + "DeviceIoControl(, , 0x%p, 0x%x, -, " + "0x%x, -, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + // Game trace: + // 05 for 20 + // ^[x3] + // 04 for 20 + // ^[x3] + // 05 for 20 + // 03 for 57 + // ^[after every eeprom read/write] + + // Address 0x20 = PCA9535 = DIP switches + // Address 0x30 = Keychip? "N2" + // Address 0x55 = Keychip? + // Address 0x57 = EEPROM + + // dyn = 0x57 + // 03: addr dyn: amEepromWait + // 04: addr dyn: amHmI2CWriteByte (via IOCTL_MXSMBUS_I2C instead) + // 05: addr dyn: amHmI2CWriteByte (via IOCTL_MXSMBUS_I2C instead) + // + // dyn2 = 0x20 + // 04: addr dyn2: amDipswWriteByteInternal + // 05: addr dyn2: amDipswReadByteInternal[Ex] + + // 0B: addr 0x20: amHmGetLPCChipId + // 0B: addr 0x21: amHmGetLPCChipId + // 48: addr 0x00: amHmGetLPCChipId + + switch (i2c_packet->addr) { + case PCA9535: + switch (i2c_packet->prt) { + case PCA9535_WRITE: + switch (i2c_packet->reg) { + case PCA9535_OUT1: + log_error("mxsmbus", "pca9535 OUT1: %02x %02x", i2c_packet->data[0], + i2c_packet->data[1]); + break; + case PCA9535_INV0: + log_error("mxsmbus", "pca9535 INV0: %02x %02x", i2c_packet->data[0], + i2c_packet->data[1]); + break; + case PCA9535_INV1: + log_error("mxsmbus", "pca9535 INV1: %02x %02x", i2c_packet->data[0], + i2c_packet->data[1]); + break; + default: + log_error("mxsmbux", + "(write) Undefined pca9535 " + "register: 0x%02x", + i2c_packet->reg); + exit(1); + } + break; + case PCA9535_READ: + switch (i2c_packet->reg) { + case PCA9535_IN0: // DIPSW + i2c_packet->data[0] = 0x00; + break; + case PCA9535_IN1: // What's this one? + i2c_packet->data[0] = 0x00; + break; + case PCA9535_INV0: + case PCA9535_INV1: + case PCA9535_OUT1: + i2c_packet->data[0] = 0x00; + break; + default: + log_error("mxsmbux", + "(read) Undefined pca9535 " + "register: 0x%02x", + i2c_packet->reg); + exit(1); + } + i2c_packet->status = 0x00; + break; + default: + log_error("mxsmbux", "Unknown pca9535 command: 0x%02x", i2c_packet->prt); + exit(1); + } + break; + case EEPROM: + switch (i2c_packet->prt) { + case 3: // Wait + // 0x18 = wait, 0x00 = done + i2c_packet->status = 0; + break; + default: + log_error("mxsmbux", "Unknown eeprom command: 0x%02x", i2c_packet->prt); + exit(1); + } + break; + default: + log_error("mxsmbus", "Unknown smbus device: 0x%02x", i2c_packet->addr); + exit(1); + } + + i2c_out->status = MXSBUS_OKAY; + if (lpBytesReturned) *lpBytesReturned = sizeof(mxsmbus_i2c_packet); + break; + case IOCTL_MXSMBUS_I2C: // i2c r/w + log_misc("mxsmbus", + "DeviceIoControl(, , 0x%p, 0x%x, -, 0x%x, " + "-, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + log_misc("mxsmbus", "SMBus I2C request for %02x: 0x%02x (%02x bytes @ %02x)", request_packet->addr, + request_packet->prt, request_packet->dlen, request_packet->reg); + // log_warning("eeprom", "%08x %08x %08x %08x", dwordInBuffer[0], + // dwordInBuffer[1], dwordInBuffer[2], dwordInBuffer[3]); + // log_warning("eeprom", "%08x %08x %08x %08x", dwordInBuffer[4], + // dwordInBuffer[5], dwordInBuffer[6], dwordInBuffer[7]); + // log_warning("eeprom", "%08x %08x", dwordInBuffer[8], + // dwordInBuffer[9]); + + // for (int i = 0; i < nInBufferSize; i++) { + // printf("%02x ", ((LPBYTE)lpInBuffer)[i]); + // } + // puts(""); + + // prt = byteInBuffer[1]; + // addr = wordInBuffer[1]; + // reg = wordInBuffer[2] & 0xFF; + dlen = request_packet->dlen; + if (dlen > 0x20) dlen = 0x20; + + /* + * Known addresses: + * - 0x57: EEPROM + **/ + + request_packet->status = MXSBUS_OKAY; + + if (request_packet->status != 0) { + log_error("mxsmbus", "invalid i2c packet"); + return FALSE; + } + + if (request_packet->addr != EEPROM) { + log_error("mxsmbus", "Unexpected I2C device: 0x%02x", request_packet->addr); + exit(1); + } + // 04 = Write byte + // 05 = Read byte + // + // 08 = Write block + // 09 = Read block + + if (request_packet->prt == 0x08) { + // Write + log_misc("mxsmbus", "eeprom write %02x (0x%04x)", dlen, request_packet->reg); + // for (int i = 0; i < dlen; i++) printf("%02x ", + // request_packet->data[i]); puts(""); + + eeprom_write(request_packet->reg >> 5, request_packet->reg & 0x1f, request_packet->data, + request_packet->dlen); + request_out->status = 0; + } else if (i2c_packet->prt == 0x09) { + // Read + log_misc("mxsmbus", "eeprom read %02x (0x%04x)", dlen, request_packet->reg); + eeprom_read(request_packet->reg >> 5, request_packet->reg & 0x1f, request_out->data, dlen); + // for (int i = 0; i < dlen; i++) printf("%02x ", + // request_out->data[i]); puts(""); + request_out->status = 0; + } else { + log_warning("mxsmbus", "UNHANDLED MXSMBUS I2C %02x", request_packet->prt); + exit(1); + } + + if (lpBytesReturned) *lpBytesReturned = sizeof(mxsmbus_request_packet); + break; + default: + log_warning("mxsmbus", "unhandled 0x%08x", dwIoControlCode); + return FALSE; + } + return TRUE; +} + +void setup_mxsmbus() { + file_hook_t* mxsmbus = new_file_hook(L"\\\\.\\mxsmbus"); + mxsmbus->DeviceIoControl = &mxsmbus_DeviceIoControl; + + hook_file(mxsmbus); + + if (!add_fake_device(&MXSMBUS_GUID, L"\\\\.\\mxsmbus")) { + log_error("mxsmbus", "failed to install mxsmbus device"); + } +} diff --git a/src/micetools/dll/drivers/mxsram.c b/src/micetools/dll/drivers/mxsram.c new file mode 100644 index 0000000..30275ec --- /dev/null +++ b/src/micetools/dll/drivers/mxsram.c @@ -0,0 +1,126 @@ +#include + +#include "../lib/mice/mice.h" +#include "mx.h" + +#define SRAM_DUMP L"dev/sram.bin" +#define SRAM_SIZE 1024 * 2084 +LPBYTE SRAM; +DWORD SRAM_POINTER = 0; + +void sram_dump() { + HANDLE dump = _CreateFileW(SRAM_DUMP, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); + if (dump == INVALID_HANDLE_VALUE) { + log_error("sram", "CreateFileA(SRAM_DUMP) failed"); + return; + } + _WriteFile(dump, SRAM, SRAM_SIZE, NULL, NULL); + _CloseHandle(dump); +} + +void sram_restore() { + HANDLE dump = + _CreateFileW(SRAM_DUMP, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (dump == INVALID_HANDLE_VALUE) return; + DWORD read; + if (!_ReadFile(dump, SRAM, SRAM_SIZE, &read, NULL)) log_error("sram", "failed to restore (%d)", GetLastError()); + _CloseHandle(dump); +} + +BOOL mxsram_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, + DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { + DWORD SRAM_VERSION = 0x0001; + DWORD SRAM_SECTOR_SIZE = 0x100; // Max is 0x800 + + switch (dwIoControlCode) { + case IOCTL_MXSRAM_PING: // Get version + log_misc("mxsram", + "DeviceIoControl(, , 0x%p, 0x%x, -, 0x%x, " + "-, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + ((LPDWORD)lpOutBuffer)[0] = SRAM_VERSION; + if (lpBytesReturned) *lpBytesReturned = 4; + break; + case IOCTL_DISK_GET_DRIVE_GEOMETRY: + log_misc("mxsram", + "DeviceIoControl(, , 0x%p, " + "0x%x, -, 0x%x, -, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + DISK_GEOMETRY out = *(PDISK_GEOMETRY)lpOutBuffer; + memset(&out, 0, sizeof(out)); + out.Cylinders.QuadPart = 1; + out.MediaType = FixedMedia; + out.TracksPerCylinder = 224; + out.SectorsPerTrack = 32; + out.BytesPerSector = 1; + if (lpBytesReturned) *lpBytesReturned = sizeof(DISK_GEOMETRY); + break; + case IOCTL_MXSRAM_GET_SECTOR_SIZE: + log_misc("mxsram", + "DeviceIoControl(, , 0x%p, " + "0x%x, -, 0x%x, -, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + ((LPDWORD)lpOutBuffer)[0] = SRAM_SECTOR_SIZE; + if (lpBytesReturned) *lpBytesReturned = 4; + break; + default: + log_warning("mxsram", "unhandled 0x%08x", dwIoControlCode); + return FALSE; + } + + return TRUE; +} + +DWORD mxsram_SetFilePointer(LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) { + if (dwMoveMethod == FILE_BEGIN) { + SRAM_POINTER = lDistanceToMove; + } else if (dwMoveMethod == FILE_CURRENT) { + SRAM_POINTER += lDistanceToMove; + } + return SRAM_POINTER; +} + +BOOL mxsram_WriteFile(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, + LPOVERLAPPED lpOverlapped) { + log_misc("mxsram", "sram write 0x%08x (0x%04x bytes)", SRAM_POINTER, nNumberOfBytesToWrite); + if (SRAM_POINTER + nNumberOfBytesToWrite >= SRAM_SIZE) { + nNumberOfBytesToWrite = SRAM_SIZE - SRAM_POINTER; + } + memcpy(SRAM + SRAM_POINTER, lpBuffer, nNumberOfBytesToWrite); + sram_dump(); + *lpNumberOfBytesWritten = nNumberOfBytesToWrite; + return TRUE; +} + +BOOL mxsram_ReadFile(LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, + LPOVERLAPPED lpOverlapped) { + log_misc("mxsram", "sram read 0x%08x (0x%04x bytes)", SRAM_POINTER, nNumberOfBytesToRead); + if (SRAM_POINTER + nNumberOfBytesToRead >= SRAM_SIZE) { + nNumberOfBytesToRead = SRAM_SIZE - SRAM_POINTER; + } + sram_restore(); + memcpy((LPVOID)lpBuffer, SRAM + SRAM_POINTER, nNumberOfBytesToRead); + *lpNumberOfBytesRead = nNumberOfBytesToRead; + return TRUE; +} + +void setup_mxsram() { + // Allocate 2MB of SRAM + SRAM = (LPBYTE)malloc(SRAM_SIZE); + if (!SRAM) { + log_error(BOOT_LOGGER, "unable to allocate 2MiB for SRAM"); + exit(1); + } + memset(SRAM, 0, SRAM_SIZE); + sram_restore(); + + file_hook_t* mxsram = new_file_hook(L"\\\\.\\mxsram"); + mxsram->SetFilePointer = &mxsram_SetFilePointer; + mxsram->ReadFile = &mxsram_ReadFile; + mxsram->WriteFile = &mxsram_WriteFile; + + hook_file(mxsram); +} diff --git a/src/micetools/dll/drivers/mxsuperio.c b/src/micetools/dll/drivers/mxsuperio.c new file mode 100644 index 0000000..aff8b52 --- /dev/null +++ b/src/micetools/dll/drivers/mxsuperio.c @@ -0,0 +1,289 @@ +#include + +#include "../lib/mice/mice.h" +#include "mx.h" +#include "smbus.h" +#include "w83791d.h" + +BYTE w83791d_bank = 0x00; +BYTE w83791d_config = W83791D_CONFIG_START; +BYTE w83791d_noncrit_t1 = 75; // C +BYTE w83791d_crit_t1 = 80; // C +BYTE w83791d_noncrit_t2 = 75; // C +BYTE w83791d_crit_t2 = 80; // C +BYTE w83791d_noncrit_t3 = 75; // C +BYTE w83791d_crit_t3 = 80; // C +BYTE w83791d_vbat_monitor_control = 0x00; +BOOL w83791d_4f_high = 0; + +BOOL mxsuperio_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, + DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { + mxsuperio_lpc_packet* lpc_packet = (mxsuperio_lpc_packet*)lpInBuffer; + mxsuperio_lpc_packet* lpc_out = (mxsuperio_lpc_packet*)lpOutBuffer; + + switch (dwIoControlCode) { + case IOCTL_MXSUPERIO_PING: + log_misc("mxsuperio", + "DeviceIoControl(, , 0x%p, 0x%x, -, " + "0x%x, -, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + ((LPDWORD)lpOutBuffer)[0] = 0x01000001; + if (lpBytesReturned) *lpBytesReturned = 4; + break; + case IOCTL_MXSUPERIO_READ: + log_warning("mxsuperio", + "DeviceIoControl(, , 0x%p, 0x%x, -, " + "0x%x, -, -)", + lpInBuffer, nInBufferSize, nOutBufferSize); + + /* + BYTE chipId = 0|1 + BYTE ?? = 0x0b + BYTE ?? = 0x20|0x21 + BYTE ret = 0 + */ + + switch (((LPBYTE)lpInBuffer)[2]) { + case 0x20: + ((LPBYTE)lpInBuffer)[3] = 0xa0; + break; + case 0x21: + ((LPBYTE)lpInBuffer)[3] = 0x20; + break; + default: + ((LPBYTE)lpInBuffer)[3] = 0x00; + break; + } + + if (lpBytesReturned) *lpBytesReturned = 4; + break; + case IOCTL_MXSUPERIO_HWMONITOR_LPC_READ: { + BYTE index = lpc_packet->index; + BYTE reg = lpc_packet->reg; + + switch (w83791d_bank) { + case 0x00: { + switch (reg) { + case W83791D_REG_WCHIPID: + lpc_out->data = 0x71; + break; + case W83791D_REG_CHIPMAN: + if (w83791d_4f_high) + lpc_out->data = 0x5c; // High byte + else + lpc_out->data = 0xa3; // Low byte + break; + case W83791D_REG_I2C_ADDR: + lpc_out->data = 0x11; + break; + case W83791D_REG_BANK: + lpc_out->data = w83791d_bank; + break; + + case W83791D_REG_CONFIG: + lpc_out->data = w83791d_config; + break; + + case W83791D_REG_BEEP_CTRL_0: + lpc_out->data = 0xff; // All the beeps! (see main.h) + break; + + case W83791D_REG_TEMP1_0: + lpc_out->data = 0x00; // TODO: Figure out the temp val lol + break; + case W83791D_RAM_VCOREA: + case W83791D_RAM_VNIR0: + case W83791D_RAM_12VIN: + lpc_out->data = 0x00; // TODO: Figure out the batt val + break; + + case W83791D_RAM_FAN1: + case W83791D_RAM_FAN2: + case W83791D_RAM_FAN3: + lpc_out->data = 0x00; // Fan no spinny! + break; + + case W83791D_VID_FAN_DIV: + // Boths fans divide by 2 (01), VDD 5 latched + // good. + lpc_out->data = 0b01011111; // Let's not /0! + break; + + case W83791D_VBAT_MONITOR_CONTROL: + lpc_out->data = w83791d_vbat_monitor_control; + break; + + default: + log_error("mxsuperio", "Unknown HM b0 register: 0x%02x", reg); + exit(1); + } + } break; + case 0x01: { + switch (reg) { + case W83791D_REG_BANK: + lpc_out->data = w83791d_bank; + break; + + case W83791D_NONCRIT_TEMP_1: + lpc_out->data = w83791d_noncrit_t1; + break; + case W83791D_CRIT_TEMP_1: + lpc_out->data = w83791d_crit_t1; + break; + case W83791D_NONCRIT_TEMP_2: + lpc_out->data = w83791d_noncrit_t2; + break; + case W83791D_CRIT_TEMP_2: + lpc_out->data = w83791d_crit_t2; + break; + case W83791D_NONCRIT_TEMP_3: + lpc_out->data = w83791d_noncrit_t3; + break; + case W83791D_CRIT_TEMP_3: + lpc_out->data = w83791d_crit_t3; + break; + + case W83791D_VIN0: + lpc_out->data = W83791D_ENTITY_CPU; + break; + case W83791D_VIN1: + lpc_out->data = W83791D_ENTITY_SYSTEM; + break; + + default: + log_error("mxsuperio", "Unknown HM b1 register: 0x%02x", reg); + exit(1); + } + } break; + case 0x05: { + switch (reg) { + case W83791D_REG_BANK: + lpc_out->data = w83791d_bank; + break; + + // Used in amHmReadVoltageInternal + // What are these?? + case 0x51: + case 0x40: + lpc_out->data = 0xff; + break; + + default: + log_error("mxsuperio", "Unknown HM b5 register: 0x%02x", reg); + return FALSE; + // exit(1); + } + } break; + default: + log_error("mxsuperio", "Unknown HM bank: 0x%02x", w83791d_bank); + exit(1); + } + + // reg 0x48 = LPC Chip ID + // index = 0,1 + // data > 0x0f, data < 0x70 + + // lpc_out->data = eeprom_read_one(reg, index); + log_misc("mxsuperio", "amHmLpcReadByte Index=0x%02x Reg=0x%02x Data=0x%02x", index, reg, lpc_out->data); + + if (lpBytesReturned) *lpBytesReturned = sizeof(mxsuperio_lpc_packet); + } break; + case IOCTL_MXSUPERIO_HWMONITOR_LPC_WRITE: { + BYTE index = lpc_packet->index; + BYTE reg = lpc_packet->reg; + log_misc("mxsuperio", "amHmLpcWriteByte Index=0x%02x Reg=0x%02x Data=0x%02b", index, reg, lpc_packet->data); + + switch (w83791d_bank) { + case 0x00: { + switch (reg) { + case W83791D_REG_BEEP_CTRL_0: + // Ignore for now + break; + + case W83791D_REG_BANK: + w83791d_4f_high = !!(lpc_packet->data & 0x80); + w83791d_bank = lpc_packet->data & 7; + break; + case W83791D_REG_CONFIG: + w83791d_config = lpc_packet->data; + break; + case W83791D_VBAT_MONITOR_CONTROL: + w83791d_vbat_monitor_control = lpc_packet->data; + break; + default: + log_error("mxsuperio", "Unknown HM b0 register: 0x%02x", reg); + exit(1); + } + break; + } + case 0x01: { + switch (reg) { + case W83791D_REG_BANK: + w83791d_4f_high = !!(lpc_packet->data & 0x80); + w83791d_bank = lpc_packet->data & 7; + break; + + case W83791D_NONCRIT_TEMP_1: + w83791d_noncrit_t1 = lpc_packet->data; + break; + case W83791D_CRIT_TEMP_1: + w83791d_crit_t1 = lpc_packet->data; + break; + case W83791D_NONCRIT_TEMP_2: + w83791d_noncrit_t2 = lpc_packet->data; + break; + case W83791D_CRIT_TEMP_2: + w83791d_crit_t2 = lpc_packet->data; + break; + case W83791D_NONCRIT_TEMP_3: + w83791d_noncrit_t3 = lpc_packet->data; + break; + case W83791D_CRIT_TEMP_3: + w83791d_crit_t3 = lpc_packet->data; + break; + + default: + log_error("mxsuperio", "Unknown HM b1 register: 0x%02x", reg); + exit(1); + } + } break; + case 0x05: { + switch (reg) { + case W83791D_REG_BANK: + w83791d_4f_high = !!(lpc_packet->data & 0x80); + w83791d_bank = lpc_packet->data & 7; + break; + + // Used in amHmReadVoltageInternal + // What is this?? + case 0x40: + break; + + default: + log_error("mxsuperio", "Unknown HM b5 register: 0x%02x", reg); + return FALSE; + // exit(1); + } + } break; + default: + log_error("mxsuperio", "Unknown HM bank: 0x%02x", w83791d_bank); + exit(1); + } + + if (lpBytesReturned) *lpBytesReturned = 0; + } break; + default: + log_warning("mxsuperio", "unhandled 0x%08x", dwIoControlCode); + return FALSE; + } + + return TRUE; +} + +void setup_mxsuperio() { + file_hook_t* mxsuperio = new_file_hook(L"\\\\.\\mxsuperio"); + mxsuperio->DeviceIoControl = &mxsuperio_DeviceIoControl; + + hook_file(mxsuperio); +} diff --git a/src/micetools/dll/files.c b/src/micetools/dll/files.c new file mode 100644 index 0000000..c27dfb5 --- /dev/null +++ b/src/micetools/dll/files.c @@ -0,0 +1,205 @@ +#include "files.h" + +#include "../lib/mice/mice.h" + +HANDLE fake_handle = (HANDLE)0x10000000; + +file_hook_t* file_hook_list = NULL; +file_hook_t* new_file_hook(LPCWSTR filename) { + file_hook_t* hook = (file_hook_t*)malloc(sizeof(file_hook_t)); + + hook->filename = filename; + + hook->DeviceIoControl = NULL; + hook->SetFilePointer = NULL; + hook->WriteFile = NULL; + hook->ReadFile = NULL; + + return hook; +} +void hook_file(file_hook_t* hook) { + hook->next = NULL; + hook->virtual_handle = NULL; + if (file_hook_list == NULL) { + file_hook_list = hook; + return; + } + + file_hook_t* hl = file_hook_list; + while (hl->next != NULL) hl = hl->next; + hl->next = hook; +}; + +drive_redirect_t DRIVE_REDIRECT_TABLE[] = {{"Y:\\", ".\\dev\\Y\\"}, + {"C:\\Documents and Settings\\AppUser\\temp\\", ".\\dev\\temp\\"}}; + +char* redirect_path(char* path) { + for (int i = 0; i < sizeof DRIVE_REDIRECT_TABLE / sizeof DRIVE_REDIRECT_TABLE[0]; i++) { + drive_redirect_t row = DRIVE_REDIRECT_TABLE[i]; + if (strstr(path, row.drive)) { + log_misc(HOOKS_LOGGER, "Redirecting '%s' to '%s'", path, row.path); + + size_t new_len = strlen(path) - strlen(row.drive) + strlen(row.path); + // TODO: Make this not leak memory! + char* new_str = (char*)malloc(new_len + 1); + strcpy_s(new_str, new_len + 1, row.path); + + char* dst = new_str + strlen(row.path); + size_t len = strlen(path) - strlen(row.drive); + char* src = path + strlen(row.drive); + + for (; len > 0; len--) (dst++)[0] = (src++)[0]; + dst[0] = 0; + log_misc(HOOKS_LOGGER, "New filename: '%s'", new_str); + return new_str; + } + } + return path; +} + +HANDLE WINAPI FakeCreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { + // for (int i = 0; i < (sizeof HOOKED_HANDLES) / (sizeof HANDLE_HOOK); + // i++) { HANDLE_HOOK hook = HOOKED_HANDLES[i]; if (lpFileName && + // wcscmp(lpFileName, hook.wName) == 0) { + // log_warning(HOOKS_LOGGER, "CreateFileW intercepting driver file %ls", + // hook.wName); return hook.handle; + // } + // } + + // HANDLE result = TrueCreateFileW(lpFileName, dwDesiredAccess, + // dwShareMode, lpSecurityAttributes, dwCreationDisposition, + // dwFlagsAndAttributes, hTemplateFile); log_misc(HOOKS_LOGGER, + // "CreateFileW(%ls) -> 0x%p", lpFileName, result); + + HANDLE handle = NULL; + + file_hook_t* hook = file_hook_list; + while (hook != NULL) { + if (wcscmp(lpFileName, hook->filename) == 0) { + if (hook->virtual_handle == NULL) { + // TODO: Assign handles better! + hook->virtual_handle = fake_handle; + ((size_t)fake_handle)++; + } + handle = hook->virtual_handle; + break; + } + hook = hook->next; + } + + if (handle == NULL) { + handle = TrueCreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, + dwFlagsAndAttributes, hTemplateFile); + } + + log_misc(HOOKS_LOGGER, "CreateFileW(%ls) -> 0x%p", lpFileName, handle); + return handle; +} +HANDLE WINAPI FakeCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { + lpFileName = redirect_path((char*)lpFileName); + + WCHAR wideFileName[MAX_PATH + 1]; + MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, (LPWSTR)&wideFileName, MAX_PATH + 1); + + HANDLE result = FakeCreateFileW((LPCWSTR)&wideFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, + dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + log_misc(HOOKS_LOGGER, "^-> CreateFileA(%s) -> 0x%p", lpFileName, result); + return result; +} + +BOOL WINAPI FakeDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, + LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped) { + log_misc(HOOKS_LOGGER, "DeviceIoControl(0x%p, 0x%08x, 0x%p, 0x%x, -, 0x%x, 0, 0)", hDevice, dwIoControlCode, + lpInBuffer, nInBufferSize, nOutBufferSize); + + file_hook_t* hook = file_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hDevice) { + if (hook->DeviceIoControl) { + // TODO: Less jank + if (lpOverlapped != NULL) SetEvent(lpOverlapped->hEvent); + + return hook->DeviceIoControl(dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, + lpBytesReturned, lpOverlapped); + } + } + hook = hook->next; + } + + return TrueDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, + lpBytesReturned, lpOverlapped); +} + +DWORD WINAPI FakeSetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) { + file_hook_t* hook = file_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile) { + if (hook->SetFilePointer) { + return hook->SetFilePointer(lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod); + } + } + hook = hook->next; + } + + return TrueSetFilePointer(hFile, lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod); +} + +DWORD WINAPI FakeWriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, + LPOVERLAPPED lpOverlapped) { + file_hook_t* hook = file_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile) { + if (hook->WriteFile) { + return hook->WriteFile(lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped); + } + } + hook = hook->next; + } + + return TrueWriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped); +} + +BOOL WINAPI FakeReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, + LPOVERLAPPED lpOverlapped) { + file_hook_t* hook = file_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hFile) { + if (hook->WriteFile) { + return hook->ReadFile(lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped); + } + } + hook = hook->next; + } + + return TrueReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped); +} + +BOOL WINAPI FakeCloseHandle(HANDLE hObject) { + file_hook_t* hook = file_hook_list; + while (hook != NULL) { + if (hook->virtual_handle == hObject) { + return TRUE; + } + hook = hook->next; + } + return TrueCloseHandle(hObject); +} + +void hook_io() { + hook("Kernel32.dll", "DeviceIoControl", FakeDeviceIoControl, (void**)&TrueDeviceIoControl, 5); + + hook("Kernel32.dll", "CreateFileA", FakeCreateFileA, (void**)&TrueCreateFileA, 6); + hook("Kernel32.dll", "CreateFileW", FakeCreateFileW, (void**)&TrueCreateFileW, 6); + + hook("Kernel32.dll", "CloseHandle", FakeCloseHandle, (void**)&TrueCloseHandle, 6); + hook("Kernel32.dll", "SetFilePointer", FakeSetFilePointer, (void**)&TrueSetFilePointer, 6); + hook("Kernel32.dll", "WriteFile", FakeWriteFile, (void**)&TrueWriteFile, 6); + hook("Kernel32.dll", "ReadFile", FakeReadFile, (void**)&TrueReadFile, 6); + + // hook("MSVCR90.DLL", "_stat64i32", Fake_stat64i32, &True_stat64i32, 5); +} diff --git a/src/micetools/dll/files.h b/src/micetools/dll/files.h new file mode 100644 index 0000000..856918b --- /dev/null +++ b/src/micetools/dll/files.h @@ -0,0 +1,63 @@ +#pragma once +#include + +static HANDLE(WINAPI* TrueCreateFileA)(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile); +static HANDLE(WINAPI* TrueCreateFileW)(LPCWSTR lpFileName, DWORD dwDesiredAccess, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile); + +static BOOL(WINAPI* TrueDeviceIoControl)(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, + DWORD nInBufferSize, LPVOID lpOutBuffer, + DWORD nOutBufferSize, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped); + +static DWORD(WINAPI* TrueSetFilePointer)(HANDLE hFile, LONG lDistanceToMove, + PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod); +static BOOL(WINAPI* TrueWriteFile)(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); +static BOOL(WINAPI* TrueReadFile)(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); + +static BOOL(WINAPI* TrueCloseHandle)(HANDLE hObject); + +typedef BOOL(FnDeviceIoControl)(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, + LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped); + +typedef DWORD(FnSetFilePointer)(LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, + DWORD dwMoveMethod); +typedef BOOL(FnWriteFile)(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); +typedef BOOL(FnReadFile)(LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, + LPOVERLAPPED lpOverlapped); + +#define _WriteFile (TrueWriteFile ? TrueWriteFile : WriteFile) +#define _ReadFile (TrueReadFile ? TrueReadFile : ReadFile) +#define _CloseHandle (TrueCloseHandle ? TrueCloseHandle : CloseHandle) +#define _CreateFileW (TrueCreateFileW ? TrueCreateFileW : CreateFileW) + +typedef struct drive_redirect { + const CHAR* drive; + const CHAR* path; +} drive_redirect_t; + +typedef struct file_hook { + LPCWSTR filename; + + FnDeviceIoControl* DeviceIoControl; + FnSetFilePointer* SetFilePointer; + FnWriteFile* WriteFile; + FnReadFile* ReadFile; + + HANDLE virtual_handle; + struct file_hook* next; +} file_hook_t; + +file_hook_t* new_file_hook(LPCWSTR filename); +void hook_file(file_hook_t* hook); +void hook_io(); diff --git a/src/micetools/dll/meson.build b/src/micetools/dll/meson.build new file mode 100644 index 0000000..a3e1737 --- /dev/null +++ b/src/micetools/dll/meson.build @@ -0,0 +1,22 @@ +shared_library( + 'mice', + name_prefix: '', + vs_module_defs: 'mice.def', + sources: [ + 'dllmain.c', + 'files.c', + 'drivers/columba.c', + 'drivers/mxsram.c', + 'drivers/mxsuperio.c', + 'drivers/mxjvs.c', + 'drivers/mxsmbus.c', + 'drivers/mxhwreset.c', + 'setupapi.c', + 'com.c', + 'comdevice.c', + ], + link_with: [ + dmi_lib, + mice_lib, + ], +) diff --git a/src/micetools/dll/mice.def b/src/micetools/dll/mice.def new file mode 100644 index 0000000..897defd --- /dev/null +++ b/src/micetools/dll/mice.def @@ -0,0 +1 @@ +LIBRARY mice diff --git a/src/micetools/dll/micesetupapi.h b/src/micetools/dll/micesetupapi.h new file mode 100644 index 0000000..09abffb --- /dev/null +++ b/src/micetools/dll/micesetupapi.h @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "../lib/mice/mice.h" + +static HDEVINFO(WINAPI* TrueSetupDiGetClassDevsA)(const GUID* ClassGuid, PCWSTR Enumerator, HWND hwndParent, + DWORD Flags); +static BOOL(WINAPI* TrueSetupDiEnumDeviceInterfaces)(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, + const GUID* InterfaceClassGuid, DWORD MemberIndex, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData); +static BOOL(WINAPI* TrueSetupDiGetDeviceInterfaceDetailA)(HDEVINFO DeviceInfoSet, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData, + DWORD DeviceInterfaceDetailDataSize, PDWORD RequiredSize, + PSP_DEVINFO_DATA DeviceInfoData); +static BOOL(WINAPI* TrueSetupDiDestroyDeviceInfoList)(HDEVINFO DevicesInfoSet); + +typedef struct FAKE_DEVICE_ { + HDEVINFO handle; + const GUID* guid; + const WCHAR* path; + struct FAKE_DEVICE_* next; +} FAKE_DEVICE; +extern FAKE_DEVICE* fake_devices; + +BOOL add_fake_device(const GUID* guid, const WCHAR* path); + +void hook_setupapi(); diff --git a/src/micetools/dll/setupapi.c b/src/micetools/dll/setupapi.c new file mode 100644 index 0000000..5d831a7 --- /dev/null +++ b/src/micetools/dll/setupapi.c @@ -0,0 +1,119 @@ +#include "micesetupapi.h" + +FAKE_DEVICE* fake_devices = NULL; + +BOOL add_fake_device(const GUID* guid, const WCHAR* path) { + FAKE_DEVICE* new_device = (FAKE_DEVICE*)malloc(sizeof *new_device); + if (!new_device) return FALSE; + new_device->guid = guid; + new_device->path = path; + new_device->handle = NULL; + new_device->next = NULL; + FAKE_DEVICE** tail = &fake_devices; + while (*tail) tail = &((*tail)->next); + *tail = new_device; + return TRUE; +} + +HDEVINFO WINAPI FakeSetupDiGetClassDevsA(const GUID* ClassGuid, PCWSTR Enumerator, HWND hwndParent, DWORD Flags) { + log_misc("setupapi", "SetupDiGetClassDevsA(%p, %s, %d, %d)", ClassGuid, Enumerator, hwndParent, Flags); + HDEVINFO res = TrueSetupDiGetClassDevsA(ClassGuid, Enumerator, hwndParent, Flags); + + if (res != INVALID_HANDLE_VALUE && ClassGuid != NULL) { + FAKE_DEVICE* device = fake_devices; + while (device != NULL) { + if (memcmp(device->guid, ClassGuid, sizeof(*ClassGuid)) == 0) { + log_misc("setupapi", "injecting %ls into class devs list", device->path); + device->handle = res; + } + device = device->next; + } + } else { + log_warning("setupapi", "upstream SetupDiGetClassDevsA failed: %d", GetLastError()); + } + + return res; +} + +BOOL WINAPI FakeSetupDiEnumDeviceInterfaces(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, + const GUID* InterfaceClassGuid, DWORD MemberIndex, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) { + log_misc("setupapi", "SetupDiEnumDeviceInterfaces"); + FAKE_DEVICE* device = fake_devices; + if (DeviceInterfaceData) { + while (device != NULL) { + if (device->handle == DeviceInfoSet) { + log_info("setupapi", "hooking fake device: %ls", device->path); + memcpy(&DeviceInterfaceData->InterfaceClassGuid, device->guid, sizeof *device->guid); + DeviceInterfaceData->Flags = SPINT_ACTIVE; + DeviceInterfaceData->Reserved = (ULONG_PTR)device->path; + SetLastError(ERROR_SUCCESS); + return TRUE; + } + device = device->next; + } + } + + // Fallback + log_misc("setupapi", "device info fallthrough"); + return TrueSetupDiEnumDeviceInterfaces(DeviceInfoSet, DeviceInfoData, InterfaceClassGuid, MemberIndex, + DeviceInterfaceData); +} + +BOOL WINAPI FakeSetupDiGetDeviceInterfaceDetailA(HDEVINFO DeviceInfoSet, PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData, + DWORD DeviceInterfaceDetailDataSize, PDWORD RequiredSize, + PSP_DEVINFO_DATA DeviceInfoData) { + log_misc("setupapi", "SetupDiGetDeviceInterfaceDetailA"); + FAKE_DEVICE* device = fake_devices; + while (device != NULL) { + if (device->handle == DeviceInfoSet && (ULONG_PTR)device->path == DeviceInterfaceData->Reserved) { + log_info("setupapi", "Yoinked SetupDiGetDeviceInterfaceDetailA"); + + const WCHAR* res = (WCHAR*)DeviceInterfaceData->Reserved; + int new_len = (wcslen(res) + 1) * (sizeof *res); + char* new_res = (char*)malloc(new_len); + size_t convertedChars = 0; + wcstombs_s(&convertedChars, new_res, new_len, res, _TRUNCATE); + + size_t len = offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath) + convertedChars; + if (RequiredSize) *RequiredSize = len; + if (!DeviceInterfaceDetailData && DeviceInterfaceDetailDataSize < len) { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + if (!DeviceInterfaceDetailData || sizeof *DeviceInterfaceDetailData != DeviceInterfaceDetailData->cbSize) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + memcpy(DeviceInterfaceDetailData->DevicePath, new_res, len); + free(new_res); + SetLastError(ERROR_SUCCESS); + return TRUE; + } + device = device->next; + } + + log_misc("setupapi", "TrueSetupDiGetDeviceInterfaceDetailA fallthrough"); + return TrueSetupDiGetDeviceInterfaceDetailA(DeviceInfoSet, DeviceInterfaceData, DeviceInterfaceDetailData, + DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData); +} + +BOOL WINAPI FakeSetupDiDestroyDeviceInfoList(HDEVINFO DevicesInfoSet) { + FAKE_DEVICE* device = fake_devices; + while (device != NULL) { + if (device->handle == DevicesInfoSet) device->handle = NULL; + device = device->next; + } + return TrueSetupDiDestroyDeviceInfoList(DevicesInfoSet); +} + +void hook_setupapi() { + hook("Setupapi.DLL", "SetupDiGetClassDevsA", FakeSetupDiGetClassDevsA, (void**)&TrueSetupDiGetClassDevsA, 6); + hook("Setupapi.DLL", "SetupDiEnumDeviceInterfaces", FakeSetupDiEnumDeviceInterfaces, + (void**)&TrueSetupDiEnumDeviceInterfaces, 7); + hook("Setupapi.DLL", "SetupDiGetDeviceInterfaceDetailA", FakeSetupDiGetDeviceInterfaceDetailA, + (void**)&TrueSetupDiGetDeviceInterfaceDetailA, 6); + hook("Setupapi.DLL", "SetupDiDestroyDeviceInfoList", FakeSetupDiDestroyDeviceInfoList, + (void**)&TrueSetupDiDestroyDeviceInfoList, 7); +} diff --git a/src/micetools/dll/smbus.h b/src/micetools/dll/smbus.h new file mode 100644 index 0000000..807b203 --- /dev/null +++ b/src/micetools/dll/smbus.h @@ -0,0 +1,29 @@ +#include + +#pragma pack(1) +typedef struct mxsmbus_request_packet_ { + BYTE status; + BYTE prt; + WORD addr; + WORD reg; + BYTE dlen; + BYTE data[32]; +} mxsmbus_request_packet; + +#pragma pack(1) +typedef struct mxsmbus_i2c_packet_ { + BYTE status; + BYTE prt; + BYTE addr; + BYTE reg; + BYTE dlen; + BYTE data[32]; +} mxsmbus_i2c_packet; + +#pragma pack(1) +typedef struct mxsuperio_lpc_packet_ { + BYTE index; + BYTE reg; + BYTE data; +} mxsuperio_lpc_packet; +enum mxsbus_status { MXSBUS_OKAY = 0 }; diff --git a/src/micetools/dll/w83791d.h b/src/micetools/dll/w83791d.h new file mode 100644 index 0000000..f79ab4b --- /dev/null +++ b/src/micetools/dll/w83791d.h @@ -0,0 +1,143 @@ +// W83791D (H/W monitor) +// https://elixir.bootlin.com/linux/latest/source/drivers/hwmon/w83791d.c +// https://datasheetspdf.com/pdf-file/641504/Winbond/W83791D/1 +// Bank 0 +#define W83791D_REG_BANK 0x4E +#define W83791D_REG_TEMP2_CONFIG 0xC2 +#define W83791D_REG_TEMP3_CONFIG 0xCA +#define W83791D_REG_TEMP1_0 0x27 +#define W83791D_REG_TEMP1_1 0x39 +#define W83791D_REG_TEMP1_2 0x3A +#define W83791D_REG_BEEP_CONFIG 0x4D +#define W83791D_REG_BEEP_CTRL_0 0x56 +#define W83791D_REG_BEEP_CTRL_1 0x57 +#define W83791D_REG_BEEP_CTRL_2 0xA3 +#define W83791D_REG_GPIO 0x15 +#define W83791D_REG_CONFIG 0x40 +#define W83791D_REG_VID_FANDIV 0x47 +#define W83791D_REG_DID_VID4 0x49 +#define W83791D_REG_WCHIPID 0x58 +#define W83791D_REG_CHIPMAN 0x4F // +#define W83791D_REG_PIN 0x4B +#define W83791D_REG_I2C_SUBADDR 0x4A +#define W83791D_REG_ALARM1 0xA9 +#define W83791D_REG_ALARM2 0xAA +#define W83791D_REG_ALARM3 0xAB +#define W83791D_REG_VBAT 0x5D +#define W83791D_REG_I2C_ADDR 0x48 // Used + +#define W83791D_RAM_VCOREA 0x20 +#define W83791D_RAM_VNIR0 0x21 +#define W83791D_RAM_3V3 0x22 +#define W83791D_RAM_VDD5 0x23 +#define W83791D_RAM_12VIN 0x24 +#define W83791D_RAM_N12VIN 0x25 +#define W83791D_RAM_N5VIN 0x26 +#define W83791D_RAM_TEMP 0x27 +#define W83791D_RAM_FAN1 0x28 +#define W83791D_RAM_FAN2 0x29 +#define W83791D_RAM_FAN3 0x2a +#define W83791D_RAM_VCOREA_HIGH 0x2b +#define W83791D_RAM_VCOREA_LOW 0x2c +#define W83791D_RAM_VINR0_HIGH 0x2d +#define W83791D_RAM_VINR0_LOW 0x2e +#define W83791D_RAM_3V3_HIGH 0x2f +#define W83791D_RAM_3V3_LOW 0x30 +#define W83791D_RAM_VDD5_HIGH 0x31 +#define W83791D_RAM_VDD5_LOW 0x32 +#define W83791D_RAM_12VIN_HIGH 0x33 +#define W83791D_RAM_12VIN_LOW 0x34 +#define W83791D_RAM_N12VIN_HIGH 0x35 +#define W83791D_RAM_N12VIN_LOW 0x36 +#define W83791D_RAM_N5VIN_HIGH 0x37 +#define W83791D_RAM_N5VIN_LOW 0x38 +#define W83791D_RAM_VTIN1_HIGH 0x39 +#define W83791D_RAM_VTIN1_HYST 0x3a +#define W83791D_RAM_FAN1_LIMIT 0x3b +#define W83791D_RAM_FAN2_LIMIT 0x3c +#define W83791D_RAM_FAN3_LIMIT 0x3d + +#define W83791D_VID_FAN_DIV 0x47 +#define W83791D_VBAT_MONITOR_CONTROL 0x5d + +// Beep flags +#define W83791D_BEEP0_EN_FAN2 0x80 +#define W83791D_BEEP0_EN_FAN1 0x40 +#define W83791D_BEEP0_EN_T2 0x20 +#define W83791D_BEEP0_EN_T1 0x10 +#define W83791D_BEEP0_EN_V5 0x08 +#define W83791D_BEEP0_EN_V33 0x04 +#define W83791D_BEEP0_EN_T3 0x02 +#define W83791D_BEEP0_EN_V25A 0x01 + +#define W83791D_BEEP1_EN_G 0x80 +#define W83791D_BEEP1_EN_VR1 0x40 +#define W83791D_BEEP1_EN_VR0 0x20 +#define W83791D_BEEP1_EN_CASO 0x10 +#define W83791D_BEEP1_EN_FAN3 0x08 +#define W83791D_BEEP1_EN_NV5 0x04 +#define W83791D_BEEP1_EN_NV12 0x02 +#define W83791D_BEEP1_EN_V13 0x01 + +#define W83791D_BEEP2_EN_USER 0x80 +#define W83791D_BEEP2_EN_FAN5 0x40 +#define W83791D_BEEP2_EN_FAN4 0x20 +#define W83791D_BEEP2_EN_TART3 0x10 +#define W83791D_BEEP2_EN_TART2 0x08 +#define W83791D_BEEP2_EN_TART1 0x04 +#define W83791D_BEEP2_EN_VBAT 0x02 +#define W83791D_BEEP2_EN_VSB 0x01 + +// Config flags +#define W83791D_CONFIG_INIT 0x80 +#define W83791D_CONFIG_IRQ_OUT 0x40 +#define W83791D_CONFIG_IRQ_POL 0x40 +#define W83791D_CONFIG_RESERVED 0x10 +#define W83791D_CONFIG_INT_CLEAR 0x08 +#define W83791D_CONFIG_IRQ_EN 0x04 +#define W83791D_CONFIG_SMI_EN 0x02 +#define W83791D_CONFIG_START 0x01 + +// VBAT monitor control +#define W83791D_VBMC_FANDIV3 0x80 +#define W83791D_VBMC_FANDIV2 0x40 +#define W83791D_VBMC_FANDIV1 0x20 +#define W83791D_VBMC_EN_ALM_RSP 0x10 +#define W83791D_VBMC_BJTS3 0x08 +#define W83791D_VBMC_BJTS2 0x04 +#define W83791D_VBMC_BJTS1 0x02 +#define W83791D_VBMC_EN_VBAT_MINT 0x01 + +// Bank 1 +#define W83791D_NONCRIT_TEMP_1 0x40 +#define W83791D_CRIT_TEMP_1 0x41 +#define W83791D_NONCRIT_TEMP_2 0x42 +#define W83791D_CRIT_TEMP_2 0x43 +#define W83791D_NONCRIT_TEMP_3 0x44 +#define W83791D_CRIT_TEMP_3 0x45 +#define W83791D_SENSOR_SBMUS_ADDRESS 0x4F + +#define W83791D_VIN0 0x50 // VCORE +#define W83791D_VIN1 0x51 // VINR0 +#define W83791D_VIN2 0x52 // +3.3 VIN +#define W83791D_VIN3 0x53 // +5 VIN +#define W83791D_VIN4 0x54 // +12 VIN +#define W83791D_VIN5 0x55 // -12 VIN +#define W83791D_VIN6 0x56 // -5 VIN +#define W83791D_VSB 0x57 +#define W83791D_VBAT 0x58 +#define W83791D_VINR1 0x59 +#define W83791D_FAN1 0x5a +#define W83791D_FAN2 0x5b +#define W83791D_FAN3 0x5c +#define W83791D_TEMP1 0x5d +#define W83791D_TEMP2 0x5e +#define W83791D_TEMP3 0x5f +#define W83791D_CHASIS 0x50 + +#define W83791D_ENTITY_CPU 0x03 +#define W83791D_ENTITY_SYSTEM 0x07 +#define W83791D_ENTITY_MEMORY_MOD 0x08 +#define W83791D_ENTITY_CHASIS 0x17 +#define W83791D_ENTITY_FAN 0x1d +#define W83791D_ENTITY_MEMORY_DEV 0x20 diff --git a/src/micetools/launcher/exe.c b/src/micetools/launcher/exe.c new file mode 100644 index 0000000..2ba097e --- /dev/null +++ b/src/micetools/launcher/exe.c @@ -0,0 +1,123 @@ +#include "exe.h" + +#include "../lib/mice/mice.h" + +bool inject_debug_wait(HANDLE process) { + BOOL present; + + log_info(BOOT_LOGGER, "Waiting for debugger to attach."); + do { + Sleep(1000); + if (FAILED(CheckRemoteDebuggerPresent(process, &present))) { + log_error(BOOT_LOGGER, "CheckRemoteDebuggerPresent failed: %03x", GetLastError()); + return false; + } + } while (!present); + log_info(BOOT_LOGGER, "Debugger attached, resuming"); + return true; +} +bool remote_call(HANDLE process, LPVOID function, LPCSTR argument) { + int nchars = strlen(argument); + + LPVOID arg_addr = VirtualAllocEx(process, NULL, nchars + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (arg_addr == NULL) { + log_error(BOOT_LOGGER, "VirtualAllocEx failed: %03x", GetLastError()); + return false; + } + + if (FAILED(WriteProcessMemory(process, arg_addr, argument, nchars + 1, NULL))) { + log_error(BOOT_LOGGER, "WriteProcessMemory failed: %03x", GetLastError()); + return false; + } + + HANDLE remote_thread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)function, arg_addr, 0, NULL); + if (remote_thread == NULL) { + log_error(BOOT_LOGGER, "CreateRemoteThread failed: %03x", GetLastError()); + return false; + } + + if (WaitForSingleObject(remote_thread, INFINITE) != WAIT_OBJECT_0) { + log_error(BOOT_LOGGER, "WaitForSingleObject failed: %03x", GetLastError()); + return false; + } + + DWORD result; + if (FAILED(GetExitCodeThread(remote_thread, &result))) { + log_error(BOOT_LOGGER, "GetExitCodeThread failed: %03x", GetLastError()); + return false; + } + if (result == 0) { + log_error(BOOT_LOGGER, "GetExitCodeThread failed: result == 0"); + return false; + } + + return true; +} +bool inject_dll(HANDLE process, LPCSTR inject) { + HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); + if (kernel32 == NULL) { + log_error(BOOT_LOGGER, "GetModuleHandleA failed: %03x", GetLastError()); + return false; + } + + LPVOID addr_LoadLibraryA = (LPVOID)GetProcAddress(kernel32, "LoadLibraryA"); + if (addr_LoadLibraryA == NULL) { + log_error(BOOT_LOGGER, "GetProcAddress failed: %03x", GetLastError()); + return false; + } + + return remote_call(process, addr_LoadLibraryA, inject); +} + +HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject) { + log_misc(BOOT_LOGGER, "Using %s for hooks", inject); + STARTUPINFOA startupInfo; + PROCESS_INFORMATION processInformation; + + memset(&startupInfo, 0, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + + // char real_inject_path[MAX_PATH + 1]; + // snprintf(real_inject_path, MAX_PATH + 1, ".\\%s", inject); + // GetFullPathNameA(real_inject_path, MAX_PATH + 1, &real_inject_path, NULL); + + // Validate that we're not about to try something insane + DWORD found = SearchPathA(NULL, inject, NULL, 0, NULL, NULL); + if (found == 0) { + log_error(BOOT_LOGGER, "Cannot inject %s: not found: %03x", inject, GetLastError()); + goto abort; + } + + // Start the binary + if (FAILED(CreateProcessA(path, cmdline, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, + &processInformation))) { + log_error(BOOT_LOGGER, "CreateProcessA failed: %03x", GetLastError()); + goto abort; + } + + // if (!inject_debug_wait(processInformation.hProcess)) goto abort; + if (!inject_dll(processInformation.hProcess, inject)) goto abort; + + // Injection completed, let the program continue execution + if (FAILED(ResumeThread(processInformation.hThread))) { + log_error(BOOT_LOGGER, "ResumeThread failed: %03x", GetLastError()); + goto abort; + } + + return processInformation.hProcess; + +abort: + if (processInformation.hProcess) { + if (FAILED(CloseHandle(processInformation.hThread))) + log_error(BOOT_LOGGER, "CloseHandle(hProcess) failed: %03x", GetLastError()); + + if (FAILED(TerminateProcess(processInformation.hProcess, 1))) + log_error(BOOT_LOGGER, "TerminateProcess failed: %03x", GetLastError()); + } + if (processInformation.hThread) { + if (FAILED(CloseHandle(processInformation.hThread))) + log_error(BOOT_LOGGER, "CloseHandle(hThread) failed: %03x", GetLastError()); + } + + return NULL; +} diff --git a/src/micetools/launcher/exe.h b/src/micetools/launcher/exe.h new file mode 100644 index 0000000..9f53cd0 --- /dev/null +++ b/src/micetools/launcher/exe.h @@ -0,0 +1,4 @@ +#include +#include + +HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject); diff --git a/src/micetools/launcher/locate.c b/src/micetools/launcher/locate.c new file mode 100644 index 0000000..92177ca --- /dev/null +++ b/src/micetools/launcher/locate.c @@ -0,0 +1,65 @@ +#include "locate.h" + +const char* KNOWN_GAMES[] = { + // Preferentially use a decrypted dump if present + "maimai_dump_.exe", + "maimai.exe", +}; + +#ifndef MICELIB +#ifdef MICE_WIN32 +#define MICELIB "mice86.dll" +#else // MICE_WIN32 +#define MICELIB "mice64.dll" +#endif // MICE_WIN32 +#endif // MICELIB + +bool locate_file(char* path, size_t len, const char* exe) { + WIN32_FIND_DATA fdFile; + HANDLE hFind = NULL; + + char work_path[2048]; + sprintf(work_path, ".\\*.*"); + + if ((hFind = FindFirstFile(work_path, &fdFile)) == INVALID_HANDLE_VALUE) return false; + + do { + if (!strcmp(fdFile.cFileName, ".") || !strcmp(fdFile.cFileName, "..")) continue; + + if (fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; + + if (!strcmp(fdFile.cFileName, exe)) { + snprintf(path, len, ".\\%s", fdFile.cFileName); + FindClose(hFind); + return true; + } + } while (FindNextFile(hFind, &fdFile)); + + FindClose(hFind); + return false; +} + +bool locate_game(char* path, size_t len) { + // This is slightly wasteful, but is needed to ensure priority! + for (size_t i = 0; i < ((sizeof KNOWN_GAMES) / (sizeof KNOWN_GAMES[0])); i++) { + if (locate_file(path, len, KNOWN_GAMES[i])) { + return true; + } + } + return false; +} + +bool locate_library(char* path, size_t len) { + if (locate_file(path, len, MICELIB)) { + return true; + } + + // Don't call DllMain! We don't want to hook ourself! + HANDLE mice = LoadLibraryExA(MICELIB, NULL, DONT_RESOLVE_DLL_REFERENCES); + if (mice != NULL) { + GetModuleFileNameA(mice, path, len); + return true; + } + + return false; +} diff --git a/src/micetools/launcher/locate.h b/src/micetools/launcher/locate.h new file mode 100644 index 0000000..18ef402 --- /dev/null +++ b/src/micetools/launcher/locate.h @@ -0,0 +1,8 @@ +#include +#include +#include +#include +#include + +bool locate_exe(char* path, size_t len, const char* exe); +bool locate_game(char* path, size_t len); diff --git a/src/micetools/launcher/main.c b/src/micetools/launcher/main.c new file mode 100644 index 0000000..a0e707f --- /dev/null +++ b/src/micetools/launcher/main.c @@ -0,0 +1,41 @@ +#include +#include + +#include "../lib/mice/mice.h" +#include "exe.h" +#include "locate.h" + +const char* VERSION = "0.0-pre"; + +int main(int argc, CHAR** argv) { + setup_logging(); + + log_info(BOOT_LOGGER, "Micetools version: %s", VERSION); + + char path[MAX_PATH + 1]; + if (!locate_game(path, MAX_PATH + 1)) { + log_error(BOOT_LOGGER, "Failed to locate a game"); + return 0; + } + + char* cmdline = ""; + + log_info(BOOT_LOGGER, "%s %s", path, cmdline); + + char micepath[MAX_PATH + 1]; + if (!locate_library(micepath, MAX_PATH + 1)) { + log_error(BOOT_LOGGER, "Failed to locate micelib"); + return 0; + } + + HANDLE game_proc = start_and_inject(path, cmdline, micepath); + if (!game_proc) return -1; + + if (FAILED(WaitForSingleObject(game_proc, INFINITE))) { + log_error(BOOT_LOGGER, "WaitForSingleObject failed: %03x", GetLastError()); + } else { + log_info(BOOT_LOGGER, "Shutting down"); + CloseHandle(game_proc); + } + return 0; +} diff --git a/src/micetools/launcher/meson.build b/src/micetools/launcher/meson.build new file mode 100644 index 0000000..55225b6 --- /dev/null +++ b/src/micetools/launcher/meson.build @@ -0,0 +1,12 @@ +executable( + 'mice', + win_subsystem: 'console', + sources: [ + 'locate.c', + 'exe.c', + 'main.c', + ], + link_with: [ + mice_lib, + ], +) diff --git a/src/micetools/lib/am/amtimer.c b/src/micetools/lib/am/amtimer.c new file mode 100644 index 0000000..d45d248 --- /dev/null +++ b/src/micetools/lib/am/amtimer.c @@ -0,0 +1,19 @@ +#include + +int frequency_loaded = 0; +LARGE_INTEGER frequency; + +amtime_t* amiTimerGet(amtime_t* time) { + LARGE_INTEGER counter; + + if (time == NULL) return NULL; + + if (frequency_loaded == 0) { + QueryPerformanceFrequency(&frequency); + frequency_loaded = 1; + } + QueryPerformanceCounter(&counter); + time->microseconds = (counter.QuadPart * 1000000) / frequency.QuadPart; + time->seconds = counter.QuadPart / frequency.QuadPart; + return time; +} diff --git a/src/micetools/lib/am/amtimer.h b/src/micetools/lib/am/amtimer.h new file mode 100644 index 0000000..f8cfb62 --- /dev/null +++ b/src/micetools/lib/am/amtimer.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +typedef struct amtime { + unsigned int seconds; + unsigned int microseconds; +} amtime_t; + +amtime_t* amiTimerGet(amtime_t* time); + +#define _amTimeMs(time) (((time).microseconds / 1000 + (time).seconds * 1000)) +#define _amTimeDelta(now, start) \ + (((now).microseconds - (start).microseconds) / 1000 + \ + ((now).seconds - (start).seconds) * 1000) diff --git a/src/micetools/lib/am/meson.build b/src/micetools/lib/am/meson.build new file mode 100644 index 0000000..1ff4ae7 --- /dev/null +++ b/src/micetools/lib/am/meson.build @@ -0,0 +1,6 @@ +amlib = static_library( + 'am', + sources: [ + 'amtimer.c', + ], +) diff --git a/src/micetools/lib/dmi/dmi.c b/src/micetools/lib/dmi/dmi.c new file mode 100644 index 0000000..5e7dbd7 --- /dev/null +++ b/src/micetools/lib/dmi/dmi.c @@ -0,0 +1,110 @@ +#include "dmi.h" + +LPBYTE dmi_table = NULL; +WORD dmi_size = 0; +size_t _dmi_max = 0; + +DMI_BIOS deafult_dmi_bios = { + .Type = 0x00, + .Length = 0x12, + .Handle = 0x0000, + .Vendor = 0x00, + .Version = 0x00, + .StartSegment = 0x0000, + .ReleaseDate = 0x00, + .ROMSize = 0x00, + .Chars = 0x04, +}; + +DMI_SYSTEM default_dmi_system = { + .Type = 0x01, + .Length = 0x08, + .Handle = 0x0000, + .Manufacturer = 0x00, + .ProductName = 0x00, + .Version = 0x00, + .Serial = 0x00, +}; + +DMI_STRING deafult_dmi_string = { + .Type = 0x0b, + .Length = 0x05, + .Handle = 0x0000, + .NoStrings = 0x00, +}; + +static void dmi_init(void) { + if (dmi_table) free(dmi_table); + dmi_table = (LPBYTE)malloc(0xff); + dmi_size = 0; + _dmi_max = 1024; +} + +static void dmi_append(void* data, size_t size) { + if (!dmi_table) return; + while (dmi_size + (size + 1) >= _dmi_max) { + LPBYTE new_table = (LPBYTE)realloc(dmi_table, _dmi_max += 0xff); + if (!new_table) return; + dmi_table = new_table; + } + memcpy(dmi_table + dmi_size, &data, size); + dmi_size += size; + dmi_table[dmi_size++] = 0; + dmi_table[dmi_size++] = 0; +} + +static void dmi_append_with_strings(void* data, size_t size, int num_strings, ...) { + va_list args; + va_start(args, num_strings); + + dmi_append(data, size); + dmi_size -= 2; + for (int i = 0; i < num_strings; i++) { + char* str = va_arg(args, char*); + int len = strlen(str); +// #pragma warning(disable : 4996) + strcpy((char*)dmi_table + dmi_size, str); + dmi_size += len + 1; + dmi_table[dmi_size - 1] = 0; + } + dmi_table[dmi_size++] = 0; + + va_end(args); +} + +void dmi_build_default() { + dmi_init(); + dmi_append(&deafult_dmi_bios, sizeof deafult_dmi_bios); + // Platform AAM: Board type one of "Supermicro"(=1) or "Advantech"(=2) + // Platform AAL: Board type one of "NEC"(=0) or "AAL2"(=3) + + dmi_append_with_strings(&default_dmi_system, sizeof default_dmi_system, 1, "Supermicro"); + + deafult_dmi_string.NoStrings = 3; + + dmi_append_with_strings(&deafult_dmi_string, sizeof deafult_dmi_string, + // OEM strings: + // 0: ?? + // 1: ?? + // 2: Platform ID (AAL, AAM) + // AAL = Nvidia drivers + // AAM = AMD drivers + // *** = No dedicated drivers + // 3: ?? + // 4: Board type (AAL, NEC, AAL2, " ", AAM, Supermicro, Advantech) + // If present -> makes board = 4 + // AAL = 4 + // AAM = 4 + // Supermicro = 4 + // Advantech = 4 + 5, ".", ".", "AAM", ".", "AAL" + ); +} + +BYTE dmi_calc_checksum(const char* buf, int len) { + int sum = 0; + int a; + for (a = 0; a < len; a++) + sum += buf[a]; + return (BYTE)(sum == 0); +} \ No newline at end of file diff --git a/src/micetools/lib/dmi/dmi.h b/src/micetools/lib/dmi/dmi.h new file mode 100644 index 0000000..7c93c2b --- /dev/null +++ b/src/micetools/lib/dmi/dmi.h @@ -0,0 +1,58 @@ +#include +#include + +extern LPBYTE dmi_table; +extern WORD dmi_size; + +// #define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop)) + +#pragma pack(1) +typedef struct DMI_HEADER_ { + CHAR Signature[5]; + BYTE Checksum; + WORD StructLength; + DWORD StructAddr; + WORD NumberOfStructs; + BYTE BCDRevision; + BYTE Reserved; +} DMI_HEADER; + +#pragma pack(1) +typedef struct DMI_BIOS_ { + BYTE Type; + BYTE Length; + WORD Handle; + BYTE Vendor; + BYTE Version; + WORD StartSegment; + BYTE ReleaseDate; + BYTE ROMSize; + uint64_t Chars; +} DMI_BIOS; + +#pragma pack(1) +typedef struct DMI_SYSTEM_ { + BYTE Type; + BYTE Length; + WORD Handle; + BYTE Manufacturer; + BYTE ProductName; + BYTE Version; + BYTE Serial; +} DMI_SYSTEM; + +#pragma pack(1) +typedef struct DMI_STRING_ { + BYTE Type; + BYTE Length; + WORD Handle; + BYTE NoStrings; +} DMI_STRING; + +static void dmi_init(void); +static void dmi_append(void* data, size_t size); +static void dmi_append_with_strings(void* data, size_t size, int num_strings, + ...); + +void dmi_build_default(void); +BYTE dmi_calc_checksum(const char* buf, int len); diff --git a/src/micetools/lib/dmi/meson.build b/src/micetools/lib/dmi/meson.build new file mode 100644 index 0000000..dbf5c28 --- /dev/null +++ b/src/micetools/lib/dmi/meson.build @@ -0,0 +1,6 @@ +dmi_lib = static_library( + 'dmi', + sources: [ + 'dmi.c' + ], +) diff --git a/src/micetools/lib/json/LICESNSE b/src/micetools/lib/json/LICESNSE new file mode 100644 index 0000000..d6d6972 --- /dev/null +++ b/src/micetools/lib/json/LICESNSE @@ -0,0 +1,25 @@ + + Copyright (C) 2012-2021 the json-parser authors All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. \ No newline at end of file diff --git a/src/micetools/lib/json/README.txt b/src/micetools/lib/json/README.txt new file mode 100644 index 0000000..20cf08d --- /dev/null +++ b/src/micetools/lib/json/README.txt @@ -0,0 +1 @@ +https://github.com/json-parser/json-parser diff --git a/src/micetools/lib/json/json.c b/src/micetools/lib/json/json.c new file mode 100644 index 0000000..6162f97 --- /dev/null +++ b/src/micetools/lib/json/json.c @@ -0,0 +1,813 @@ +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012-2021 the json-parser authors All rights reserved. + * https://github.com/json-parser/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "json.h" +#include "../util/hex.h" + +#include +#include +#include +#include +#include +#include +#include + +#define JSON_INT_MAX 9223372036854775807LL + +typedef unsigned int json_uchar; + +const struct _json_value json_value_none; + +static int would_overflow(json_int_t value, json_char b) { return ((JSON_INT_MAX - (b - '0')) / 10) < value; } + +typedef struct { + size_t used_memory; + + json_settings settings; + int first_pass; + + const json_char *ptr; + unsigned int cur_line, cur_col; +} json_state; + +static void *default_alloc(size_t size, int zero) { return zero ? calloc(1, size) : malloc(size); } + +static void *json_alloc(json_state *state, size_t size, int zero) { + if ((ULONG_MAX - 8 - state->used_memory) < size) return 0; + + if (state->settings.max_memory && (state->used_memory += size) > state->settings.max_memory) { + return 0; + } + + return default_alloc(size, zero); +} + +static int new_value(json_state *state, json_value **top, json_value **root, json_value **alloc, json_type type) { + json_value *value; + size_t values_size; + + if (!state->first_pass) { + value = *top = *alloc; + *alloc = (*alloc)->_reserved.next_alloc; + + if (!*root) *root = value; + + switch (value->type) { + case json_array: + if (value->u.array.length == 0) break; + if (!(value->u.array.values = + (json_value **)json_alloc(state, value->u.array.length * sizeof(json_value *), 0))) { + return 0; + } + value->u.array.length = 0; + break; + case json_object: + if (value->u.object.length == 0) break; + + values_size = sizeof(*value->u.object.values) * value->u.object.length; + + if (!(value->u.object.values = (json_object_entry *)json_alloc( + state, values_size + ((uintptr_t)value->u.object.values), 0))) { + return 0; + } + value->_reserved.object_mem = (void *)(((char *)value->u.object.values) + values_size); + value->u.object.length = 0; + break; + case json_string: + if (!(value->u.string.ptr = + (json_char *)json_alloc(state, (value->u.string.length + 1) * sizeof(json_char), 0))) { + return 0; + } + value->u.string.length = 0; + break; + default: + break; + }; + + return 1; + } + + if (!(value = (json_value *)json_alloc(state, sizeof(json_value) + state->settings.value_extra, 1))) { + return 0; + } + + if (!*root) *root = value; + + value->type = type; + value->parent = *top; + +#ifdef JSON_TRACK_SOURCE + value->line = state->cur_line; + value->col = state->cur_col; +#endif + + if (*alloc) (*alloc)->_reserved.next_alloc = value; + + *alloc = *top = value; + + return 1; +} + +#define whitespace \ + case '\n': \ + ++state.cur_line; \ + state.cur_col = 0; /* FALLTHRU */ \ + case ' ': /* FALLTHRU */ \ + case '\t': /* FALLTHRU */ \ + case '\r' + +#define string_add(b) \ + do { \ + if (!state.first_pass) string[string_length] = b; \ + ++string_length; \ + } while (0); + +#define line_and_col state.cur_line, state.cur_col + +static const long flag_next = 1 << 0, flag_reproc = 1 << 1, flag_need_comma = 1 << 2, flag_seek_value = 1 << 3, + flag_escaped = 1 << 4, flag_string = 1 << 5, flag_need_colon = 1 << 6, flag_done = 1 << 7, + flag_num_negative = 1 << 8, flag_num_zero = 1 << 9, flag_num_e = 1 << 10, + flag_num_e_got_sign = 1 << 11, flag_num_e_negative = 1 << 12, flag_line_comment = 1 << 13, + flag_block_comment = 1 << 14, flag_num_got_decimal = 1 << 15; + +json_value *json_parse_ex(json_settings *settings, const json_char *json, size_t length, char *error_buf) { + char error[json_error_max]; + const json_char *end; + json_value *top, *root, *alloc = 0; + json_state state = {0}; + long flags = 0; + int num_digits = 0; + double num_e = 0, num_fraction = 0; + + /* Skip UTF-8 BOM + */ + if (length >= 3 && ((unsigned char)json[0]) == 0xEF && ((unsigned char)json[1]) == 0xBB && + ((unsigned char)json[2]) == 0xBF) { + json += 3; + length -= 3; + } + + error[0] = '\0'; + end = (json + length); + + memcpy(&state.settings, settings, sizeof(json_settings)); + + for (state.first_pass = 1; state.first_pass >= 0; --state.first_pass) { + json_uchar uchar; + unsigned char uc_b1, uc_b2, uc_b3, uc_b4; + json_char *string = 0; + unsigned int string_length = 0; + + top = root = 0; + flags = flag_seek_value; + + state.cur_line = 1; + + for (state.ptr = json;; ++state.ptr) { + json_char b = (state.ptr == end ? 0 : *state.ptr); + + if (flags & flag_string) { + if (!b) { + sprintf(error, "%u:%u: Unexpected EOF in string", line_and_col); + goto e_failed; + } + + if (string_length > UINT_MAX - 8) goto e_overflow; + + if (flags & flag_escaped) { + flags &= ~flag_escaped; + + switch (b) { + case 'b': + string_add('\b'); + break; + case 'f': + string_add('\f'); + break; + case 'n': + string_add('\n'); + break; + case 'r': + string_add('\r'); + break; + case 't': + string_add('\t'); + break; + case 'u': + + if (end - state.ptr <= 4 || (uc_b1 = hex_value(*++state.ptr)) == 0xFF || + (uc_b2 = hex_value(*++state.ptr)) == 0xFF || + (uc_b3 = hex_value(*++state.ptr)) == 0xFF || + (uc_b4 = hex_value(*++state.ptr)) == 0xFF) { + sprintf(error, "%u:%u: Invalid character value `%c`", line_and_col, b); + goto e_failed; + } + + uc_b1 = (uc_b1 << 4) | uc_b2; + uc_b2 = (uc_b3 << 4) | uc_b4; + uchar = (uc_b1 << 8) | uc_b2; + + if ((uchar & 0xF800) == 0xD800) { + json_uchar uchar2; + + if (end - state.ptr <= 6 || (*++state.ptr) != '\\' || (*++state.ptr) != 'u' || + (uc_b1 = hex_value(*++state.ptr)) == 0xFF || + (uc_b2 = hex_value(*++state.ptr)) == 0xFF || + (uc_b3 = hex_value(*++state.ptr)) == 0xFF || + (uc_b4 = hex_value(*++state.ptr)) == 0xFF) { + sprintf(error, "%u:%u: Invalid character value `%c`", line_and_col, b); + goto e_failed; + } + + uc_b1 = (uc_b1 << 4) | uc_b2; + uc_b2 = (uc_b3 << 4) | uc_b4; + uchar2 = (uc_b1 << 8) | uc_b2; + + uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF); + } + + if (sizeof(json_char) >= sizeof(json_uchar) || (uchar <= 0x7F)) { + string_add((json_char)uchar); + break; + } + + if (uchar <= 0x7FF) { + if (state.first_pass) + string_length += 2; + else { + string[string_length++] = 0xC0 | (uchar >> 6); + string[string_length++] = 0x80 | (uchar & 0x3F); + } + + break; + } + + if (uchar <= 0xFFFF) { + if (state.first_pass) + string_length += 3; + else { + string[string_length++] = 0xE0 | (uchar >> 12); + string[string_length++] = 0x80 | ((uchar >> 6) & 0x3F); + string[string_length++] = 0x80 | (uchar & 0x3F); + } + + break; + } + + if (state.first_pass) + string_length += 4; + else { + string[string_length++] = 0xF0 | (uchar >> 18); + string[string_length++] = 0x80 | ((uchar >> 12) & 0x3F); + string[string_length++] = 0x80 | ((uchar >> 6) & 0x3F); + string[string_length++] = 0x80 | (uchar & 0x3F); + } + + break; + + default: + string_add(b); + }; + + continue; + } + + if (b == '\\') { + flags |= flag_escaped; + continue; + } + + if (b == '"') { + if (!state.first_pass) string[string_length] = 0; + + flags &= ~flag_string; + string = 0; + + switch (top->type) { + case json_string: + top->u.string.length = string_length; + flags |= flag_next; + + break; + + case json_object: + if (state.first_pass) { + json_char **chars = (json_char **)&top->u.object.values; + chars[0] += string_length + 1; + } else { + top->u.object.values[top->u.object.length].name = + (json_char *)top->_reserved.object_mem; + + top->u.object.values[top->u.object.length].name_length = string_length; + + (*(json_char **)&top->_reserved.object_mem) += string_length + 1; + } + + flags |= flag_seek_value | flag_need_colon; + continue; + + default: + break; + }; + } else { + string_add(b); + continue; + } + } + + if (state.settings.settings & json_enable_comments) { + if (flags & (flag_line_comment | flag_block_comment)) { + if (flags & flag_line_comment) { + if (b == '\r' || b == '\n' || !b) { + flags &= ~flag_line_comment; + --state.ptr; /* so null can be reproc'd */ + } + + continue; + } + + if (flags & flag_block_comment) { + if (!b) { + sprintf(error, "%u:%u: Unexpected EOF in block comment", line_and_col); + goto e_failed; + } + + if (b == '*' && state.ptr < (end - 1) && state.ptr[1] == '/') { + flags &= ~flag_block_comment; + ++state.ptr; /* skip closing sequence */ + } + + continue; + } + } else if (b == '/') { + if (!(flags & (flag_seek_value | flag_done)) && top->type != json_object) { + sprintf(error, "%u:%u: Comment not allowed here", line_and_col); + goto e_failed; + } + + if (++state.ptr == end) { + sprintf(error, "%u:%u: EOF unexpected", line_and_col); + goto e_failed; + } + + switch (b = *state.ptr) { + case '/': + flags |= flag_line_comment; + continue; + + case '*': + flags |= flag_block_comment; + continue; + + default: + sprintf(error, "%u:%u: Unexpected `%c` in comment opening sequence", line_and_col, b); + goto e_failed; + }; + } + } + + if (flags & flag_done) { + if (!b) break; + + switch (b) { + whitespace: + continue; + + default: + sprintf(error, "%u:%u: Trailing garbage: `%c`", line_and_col, b); + goto e_failed; + }; + } + + if (flags & flag_seek_value) { + switch (b) { + whitespace: + continue; + + case ']': + if (top && top->type == json_array) + flags = (flags & ~(flag_need_comma | flag_seek_value)) | flag_next; + else { + sprintf(error, "%u:%u: Unexpected `]`", line_and_col); + goto e_failed; + } + + break; + + default: + if (flags & flag_need_comma) { + if (b == ',') { + flags &= ~flag_need_comma; + continue; + } else { + sprintf(error, "%u:%u: Expected `,` before `%c`", line_and_col, b); + + goto e_failed; + } + } + + if (flags & flag_need_colon) { + if (b == ':') { + flags &= ~flag_need_colon; + continue; + } else { + sprintf(error, "%u:%u: Expected `:` before `%c`", line_and_col, b); + + goto e_failed; + } + } + + flags &= ~flag_seek_value; + + switch (b) { + case '{': + if (!new_value(&state, &top, &root, &alloc, json_object)) goto e_alloc_failure; + + continue; + + case '[': + if (!new_value(&state, &top, &root, &alloc, json_array)) goto e_alloc_failure; + + flags |= flag_seek_value; + continue; + + case '"': + if (!new_value(&state, &top, &root, &alloc, json_string)) goto e_alloc_failure; + + flags |= flag_string; + + string = top->u.string.ptr; + string_length = 0; + + continue; + + case 't': + if ((end - state.ptr) <= 3 || *(++state.ptr) != 'r' || *(++state.ptr) != 'u' || + *(++state.ptr) != 'e') { + goto e_unknown_value; + } + + if (!new_value(&state, &top, &root, &alloc, json_boolean)) goto e_alloc_failure; + + top->u.boolean = 1; + + flags |= flag_next; + break; + + case 'f': + if ((end - state.ptr) <= 4 || *(++state.ptr) != 'a' || *(++state.ptr) != 'l' || + *(++state.ptr) != 's' || *(++state.ptr) != 'e') { + goto e_unknown_value; + } + + if (!new_value(&state, &top, &root, &alloc, json_boolean)) goto e_alloc_failure; + + flags |= flag_next; + break; + + case 'n': + if ((end - state.ptr) <= 3 || *(++state.ptr) != 'u' || *(++state.ptr) != 'l' || + *(++state.ptr) != 'l') { + goto e_unknown_value; + } + + if (!new_value(&state, &top, &root, &alloc, json_null)) goto e_alloc_failure; + + flags |= flag_next; + break; + default: + if (isdigit((unsigned char)b) || b == '-') { + if (!new_value(&state, &top, &root, &alloc, json_integer)) goto e_alloc_failure; + + if (!state.first_pass) { + while (isdigit((unsigned char)b) || b == '+' || b == '-' || b == 'e' || + b == 'E' || b == '.') { + if ((++state.ptr) == end) { + b = 0; + break; + } + + b = *state.ptr; + } + + flags |= flag_next | flag_reproc; + break; + } + + flags &= ~(flag_num_negative | flag_num_e | flag_num_e_got_sign | + flag_num_e_negative | flag_num_zero); + + num_digits = 0; + num_fraction = 0; + num_e = 0; + + if (b != '-') { + flags |= flag_reproc; + break; + } + + flags |= flag_num_negative; + continue; + } else { + sprintf(error, "%u:%u: Unexpected `%c` when seeking value", line_and_col, b); + goto e_failed; + } + }; + }; + } else { + switch (top->type) { + case json_object: + switch (b) { + whitespace: + continue; + case '"': + if (flags & flag_need_comma) { + sprintf(error, "%u:%u: Expected `,` before `\"`", line_and_col); + goto e_failed; + } + + flags |= flag_string; + + string = (json_char *)top->_reserved.object_mem; + string_length = 0; + + break; + + case '}': + flags = (flags & ~flag_need_comma) | flag_next; + break; + + case ',': + if (flags & flag_need_comma) { + flags &= ~flag_need_comma; + break; + } /* FALLTHRU */ + + default: + sprintf(error, "%u:%u: Unexpected `%c` in object", line_and_col, b); + goto e_failed; + }; + break; + + case json_integer: + case json_double: + if (isdigit((unsigned char)b)) { + ++num_digits; + + if (top->type == json_integer || flags & flag_num_e) { + if (!(flags & flag_num_e)) { + if (flags & flag_num_zero) { + sprintf(error, "%u:%u: Unexpected `0` before `%c`", line_and_col, b); + goto e_failed; + } + + if (num_digits == 1 && b == '0') flags |= flag_num_zero; + } else { + flags |= flag_num_e_got_sign; + num_e = (num_e * 10) + (b - '0'); + continue; + } + + if (would_overflow(top->u.integer, b)) { + json_int_t integer = top->u.integer; + --num_digits; + --state.ptr; + top->type = json_double; + top->u.dbl = (double)integer; + continue; + } + + top->u.integer = (top->u.integer * 10) + (b - '0'); + continue; + } + + if (flags & flag_num_got_decimal) + num_fraction = (num_fraction * 10) + (b - '0'); + else + top->u.dbl = (top->u.dbl * 10) + (b - '0'); + + continue; + } + + if (b == '+' || b == '-') { + if ((flags & flag_num_e) && !(flags & flag_num_e_got_sign)) { + flags |= flag_num_e_got_sign; + + if (b == '-') flags |= flag_num_e_negative; + + continue; + } + } else if (b == '.' && top->type == json_integer) { + json_int_t integer = top->u.integer; + + if (!num_digits) { + sprintf(error, "%u:%u: Expected digit before `.`", line_and_col); + goto e_failed; + } + + top->type = json_double; + top->u.dbl = (double)integer; + + flags |= flag_num_got_decimal; + num_digits = 0; + continue; + } + + if (!(flags & flag_num_e)) { + if (top->type == json_double) { + if (!num_digits) { + sprintf(error, "%u:%u: Expected digit after `.`", line_and_col); + goto e_failed; + } + + top->u.dbl += num_fraction / pow(10.0, num_digits); + } + + if (b == 'e' || b == 'E') { + flags |= flag_num_e; + + if (top->type == json_integer) { + json_int_t integer = top->u.integer; + top->type = json_double; + top->u.dbl = (double)integer; + } + + num_digits = 0; + flags &= ~flag_num_zero; + + continue; + } + } else { + if (!num_digits) { + sprintf(error, "%u:%u: Expected digit after `e`", line_and_col); + goto e_failed; + } + + top->u.dbl *= pow(10.0, (flags & flag_num_e_negative ? -num_e : num_e)); + } + + if (flags & flag_num_negative) { + if (top->type == json_integer) + top->u.integer = -top->u.integer; + else + top->u.dbl = -top->u.dbl; + } + + flags |= flag_next | flag_reproc; + break; + + default: + break; + }; + } + + if (flags & flag_reproc) { + flags &= ~flag_reproc; + --state.ptr; + } + + if (flags & flag_next) { + flags = (flags & ~flag_next) | flag_need_comma; + + if (!top->parent) { + /* root value done */ + + flags |= flag_done; + continue; + } + + if (top->parent->type == json_array) flags |= flag_seek_value; + + if (!state.first_pass) { + json_value *parent = top->parent; + + switch (parent->type) { + case json_object: + parent->u.object.values[parent->u.object.length].value = top; + break; + case json_array: + parent->u.array.values[parent->u.array.length] = top; + break; + default: + break; + }; + } + + if ((++top->parent->u.array.length) > UINT_MAX - 8) goto e_overflow; + top = top->parent; + continue; + } + } + + alloc = root; + } + + return root; + +e_unknown_value: + sprintf(error, "%u:%u: Unknown value", line_and_col); + goto e_failed; + +e_alloc_failure: + strcpy(error, "Memory allocation failure"); + goto e_failed; + +e_overflow: + sprintf(error, "%u:%u: Too long (caught overflow)", line_and_col); + goto e_failed; + +e_failed: + if (error_buf) { + if (*error) + strcpy(error_buf, error); + else + strcpy(error_buf, "Unknown error"); + } + + if (state.first_pass) alloc = root; + + while (alloc) { + top = alloc->_reserved.next_alloc; + free(alloc); + alloc = top; + } + + if (!state.first_pass) json_value_free(root); + + return 0; +} + +json_value *json_parse(const json_char *json, size_t length) { + json_settings settings = {0}; + return json_parse_ex(&settings, json, length, 0); +} + +void json_value_free(json_value *value) { + json_value *cur_value; + + if (!value) return; + + value->parent = 0; + + while (value) { + switch (value->type) { + case json_array: + + if (!value->u.array.length) { + free(value->u.array.values); + break; + } + + value = value->u.array.values[--value->u.array.length]; + continue; + + case json_object: + + if (!value->u.object.length) { + free(value->u.object.values); + break; + } + + value = value->u.object.values[--value->u.object.length].value; + continue; + + case json_string: + + free(value->u.string.ptr); + break; + + default: + break; + }; + + cur_value = value; + value = value->parent; + free(cur_value); + } +} diff --git a/src/micetools/lib/json/json.h b/src/micetools/lib/json/json.h new file mode 100644 index 0000000..269c091 --- /dev/null +++ b/src/micetools/lib/json/json.h @@ -0,0 +1,114 @@ +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012-2021 the json-parser authors All rights reserved. + * https://github.com/json-parser/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _JSON_H +#define _JSON_H + +#ifndef json_char +#define json_char char +#endif + +#ifndef json_int_t +#define json_int_t __int64 +#endif + +#include + +typedef struct { + unsigned long max_memory; /* should be size_t, but would modify the API */ + int settings; + size_t value_extra; /* how much extra space to allocate for values? */ +} json_settings; + +#define json_enable_comments 0x01 + +typedef enum { + json_none, + json_object, + json_array, + json_integer, + json_double, + json_string, + json_boolean, + json_null +} json_type; + +extern const struct _json_value json_value_none; + +typedef struct _json_object_entry { + json_char *name; + unsigned int name_length; + struct _json_value *value; +} json_object_entry; + +typedef struct _json_value { + struct _json_value *parent; + + json_type type; + + union { + int boolean; + json_int_t integer; + double dbl; + + struct { + unsigned int length; + json_char *ptr; /* null terminated */ + } string; + + struct { + unsigned int length; + json_object_entry *values; + } object; + + struct { + unsigned int length; + struct _json_value **values; + } array; + } u; + + union { + struct _json_value *next_alloc; + void *object_mem; + } _reserved; + +#ifdef JSON_TRACK_SOURCE + unsigned int line, col; +#endif + +} json_value; + +json_value *json_parse(const json_char *json, size_t length); + +#define json_error_max 128 +json_value *json_parse_ex(json_settings *settings, const json_char *json, size_t length, char *error); + +void json_value_free(json_value *); + +#endif \ No newline at end of file diff --git a/src/micetools/lib/json/meson.build b/src/micetools/lib/json/meson.build new file mode 100644 index 0000000..511c164 --- /dev/null +++ b/src/micetools/lib/json/meson.build @@ -0,0 +1,7 @@ +json_lib = static_library( + 'micejson', + sources: [ + 'json.c', + ], + link_with: [util_lib] +) diff --git a/src/micetools/lib/meson.build b/src/micetools/lib/meson.build new file mode 100644 index 0000000..f3f5de5 --- /dev/null +++ b/src/micetools/lib/meson.build @@ -0,0 +1,16 @@ +subdir('util') # This is the only lib that should ever be link_with'd by another lib +subdir('am') +subdir('mice') +subdir('dmi') +subdir('json') + +fs = import('fs') +# Handle the fact we aren't distributing the libpcp source +if fs.is_dir('libpcp') + subdir('libpcp') + libpcp_is_static = true +else + libpcp = meson.get_compiler('c').find_library('libpcp', dirs: libs_dir, required: false) + assert(libpcp.found(), 'Unable to locate libpcp.lib. Make sure to place it in the src/ folder prior to building.') + libpcp_is_static = false +endif diff --git a/src/micetools/lib/mice/hook.c b/src/micetools/lib/mice/hook.c new file mode 100644 index 0000000..46722ea --- /dev/null +++ b/src/micetools/lib/mice/hook.c @@ -0,0 +1,95 @@ +#include "hook.h" + +function_hook_t* hook_list = NULL; + +void append_hook(function_hook_t* hook) { + hook->next = NULL; + if (hook_list == NULL) { + hook_list = hook; + return; + } + + function_hook_t* hl = hook_list; + while (hl->next != NULL) hl = hl->next; + hl->next = hook; +} +void hook(LPCSTR dll, LPCSTR name, void* patch, void** store, UINT length) { + function_hook_t* hook = (function_hook_t*)malloc(sizeof(struct function_hook)); + hook->dll = dll; + hook->name = name; + hook->patch = patch; + hook->store = store; + hook->length = length; + append_hook(hook); +} + +void patch_at(PVOID addr, const char* patch, DWORD length) { + DWORD oldProt; + VirtualProtect(addr, length, PAGE_EXECUTE_READWRITE, &oldProt); + memcpy(addr, patch, length); + VirtualProtect(addr, length, oldProt, &oldProt); +} +void clear_at(PVOID addr, BYTE clearVal, DWORD length) { + DWORD oldProt; + VirtualProtect(addr, length, PAGE_EXECUTE_READWRITE, &oldProt); + memset(addr, clearVal, length); + VirtualProtect(addr, length, oldProt, &oldProt); +} + +bool Detour(PVOID src, PVOID dst, const intptr_t len) { + if (len < 5) return false; + + DWORD oldProt; + VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &oldProt); + + *(PCHAR)src = '\xE9'; + *(PINT)((int)src + 1) = (int)dst - (int)src - 5; + + VirtualProtect(src, len, oldProt, &oldProt); + return true; +} + +void* CreateHook(PVOID src, PVOID dst, const intptr_t len) { + if (len < 5) return NULL; + + PVOID gateway = VirtualAlloc(0, len + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (!gateway) return NULL; + memcpy(gateway, src, len); + + *(PCHAR)((int)gateway + len) = '\xE9'; + *(PINT)((int)gateway + len + 1) = (int)src - (int)gateway - 5; + + Detour(src, dst, len); + + return gateway; +} + +void setup_hooks() { + log_info(HOOKS_LOGGER, "attaching"); + + if (hook_list == NULL) { + log_warning(HOOKS_LOGGER, "No hooks to register!"); + return; + } + function_hook_t* hook = hook_list; + do { + if (hook->dll == NULL) continue; + + HMODULE dll = LoadLibraryA(hook->dll); + if (dll == NULL) { + log_error(HOOKS_LOGGER, "failed to load dll %s. %s skipped", hook->dll, hook->name); + continue; + } + + if ((*(hook->store) = GetProcAddress(dll, hook->name)) == NULL) { + log_warning(HOOKS_LOGGER, "failed to get original %s", hook->name); + } else { + *((void**)hook->store) = CreateHook(*hook->store, hook->patch, hook->length); + log_misc(HOOKS_LOGGER, "hooked %s", hook->name); + } + + hook = hook->next; + } while (hook != NULL); + + log_info(HOOKS_LOGGER, "attach complete"); +} diff --git a/src/micetools/lib/mice/hook.h b/src/micetools/lib/mice/hook.h new file mode 100644 index 0000000..09b608c --- /dev/null +++ b/src/micetools/lib/mice/hook.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "./mice.h" + +typedef struct function_hook { + LPCSTR dll; + LPCSTR name; + PVOID patch; + PVOID* store; + UINT length; + struct function_hook* next; +} function_hook_t; + +void patch_at(PVOID addr, const char* patch, DWORD length); +void clear_at(PVOID addr, BYTE clearVal, DWORD length); +void* CreateHook(PVOID src, PVOID dst, const intptr_t len); + +void hook(LPCSTR dll, LPCSTR name, void* patch, void** store, UINT length); + +void setup_hooks(); diff --git a/src/micetools/lib/mice/ioctl.h b/src/micetools/lib/mice/ioctl.h new file mode 100644 index 0000000..4903361 --- /dev/null +++ b/src/micetools/lib/mice/ioctl.h @@ -0,0 +1,56 @@ +#include + +#define FILE_DEVICE_SEGA 0x9c40 + +// amSramInit +#define IOCTL_MXSRAM_PING CTL_CODE(FILE_DEVICE_SEGA, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS) +// amSramInit +#define IOCTL_MXSRAM_GET_SECTOR_SIZE CTL_CODE(FILE_DEVICE_SEGA, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MXSRAM_GET_VERSION CTL_CODE(FILE_DEVICE_SEGA, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS) + +// EEPROM uses MXSMBUS_GUID device class +// DIPSW uses MXSMBUS_GUID device class +// Platform uses PLATFORM_GUID device class + +// amDipswRequestReadByteInternal +// amDipswReadByteInternal +// amDipswWriteByteInternal +// /\ all either use SUPERIO or SMBUS. Which is unknown atm. All use 0x801 +// +// amDipswGetDriverVersion +// /\ uses either SUPERIO or SMBUS. Which is unknown atm. Uses 0x802 + +// Same as IOCTL_MXSRAM_PING +// amHmProbeSuperIoDevice +#define IOCTL_MXSUPERIO_PING CTL_CODE(FILE_DEVICE_SEGA, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS) +// amHmGetLPCChipId +#define IOCTL_MXSUPERIO_READ CTL_CODE(FILE_DEVICE_SEGA, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_MXSUPERIO_UNKNOWN CTL_CODE(FILE_DEVICE_SEGA, 0x802, METHOD_BUFFERED, FILE_WRITE_ACCESS) +// amHmLpcReadByte +#define IOCTL_MXSUPERIO_HWMONITOR_LPC_READ CTL_CODE(FILE_DEVICE_SEGA, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) +// amHmLpcWriteByte +#define IOCTL_MXSUPERIO_HWMONITOR_LPC_WRITE CTL_CODE(FILE_DEVICE_SEGA, 0x804, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +#define IOCTL_MXJVS_EXCHANGE CTL_CODE(FILE_DEVICE_SEGA, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) + +// Same as IOCTL_MXSUPERIO_READ +// amHmI2CReadByte,amHmI2CWriteByte,amEepromWait +#define IOCTL_MXSMBUS_REQUEST CTL_CODE(FILE_DEVICE_SEGA, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) +// amEepromGetDriverVerision +#define IOCTL_MXSMBUS_GET_VERSION CTL_CODE(FILE_DEVICE_SEGA, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS) +// amEepromI2CReadBlock,amEepromI2CWriteBlock,amHmGetLPCChipId +#define IOCTL_MXSMBUS_I2C CTL_CODE(FILE_DEVICE_SEGA, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_MXSMBUS_IDK CTL_CODE(FILE_DEVICE_SEGA, 0x807, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_COLUMBA_READ_DMI CTL_CODE(FILE_DEVICE_SEGA, 0x841, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_MXHWRESET_RESET CTL_CODE(FILE_DEVICE_SEGA, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_MXPARALLEL_WRITE_DATA CTL_CODE(FILE_DEVICE_SEGA, 0x800, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MXPARALLEL_READ_DATA CTL_CODE(FILE_DEVICE_SEGA, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MXPARALLEL_WRITE_STATUS CTL_CODE(FILE_DEVICE_SEGA, 0x802, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MXPARALLEL_READ_STATUS CTL_CODE(FILE_DEVICE_SEGA, 0x803, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MXPARALLEL_WRITE_CTRL_PORT CTL_CODE(FILE_DEVICE_SEGA, 0x804, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MXPARALLEL_READ_CTRL_PORT CTL_CODE(FILE_DEVICE_SEGA, 0x805, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MXPARALLEL_WRITE_FLAGS CTL_CODE(FILE_DEVICE_SEGA, 0x806, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MXPARALLEL_READ_FLAGS CTL_CODE(FILE_DEVICE_SEGA, 0x807, METHOD_BUFFERED, FILE_READ_ACCESS) diff --git a/src/micetools/lib/mice/log.c b/src/micetools/lib/mice/log.c new file mode 100644 index 0000000..27f1c0e --- /dev/null +++ b/src/micetools/lib/mice/log.c @@ -0,0 +1,168 @@ +#include "log.h" + +BOOL HAS_COLOUR = FALSE; + +char _log_prelude[32]; +char* log_prelude() { + time_t rawtime; + struct tm* timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + strftime(_log_prelude, 32, "[%Y/%m/%d %H:%M:%S] ", timeinfo); + return _log_prelude; +} + +HANDLE LOG_FILE = NULL; +VOID trace_hook(char* output); +CRITICAL_SECTION logger_lock; + +int _do_log(const char* caller, const char* level, const char* format, va_list args, bool toStdout) { + EnterCriticalSection(&logger_lock); + + int len = snprintf(NULL, 0, "%s%s:%s:", log_prelude(), level, caller) + vsnprintf(NULL, 0, format, args); + char* buf = (char*)malloc(len + 2); + if (!buf) { + LeaveCriticalSection(&logger_lock); + return 0; + } + + int wrote_a = snprintf(buf, len, "%s%s:%s:", log_prelude(), level, caller); + int wrote_b = vsnprintf(buf + wrote_a, len - wrote_a + 1, format, args); + buf[len] = '\n'; + buf[len + 1] = '\0'; + + // No +1 here to not get the \n + if (toStdout) WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, len, NULL, NULL); +#ifdef LOG_TO_FILE + if (LOG_FILE && LOG_FILE != INVALID_HANDLE_VALUE) WriteFile(LOG_FILE, buf, len + 1, NULL, NULL); +#endif + + free(buf); + + LeaveCriticalSection(&logger_lock); + return wrote_b; +} +int vlog_misc(const char* caller, const char* format, va_list args) { +#ifdef LOG_MISC + if (HAS_COLOUR) WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\033[90m", 5, NULL, NULL); + int ret = _do_log(caller, "M", format, args, true); + puts(HAS_COLOUR ? "\033[0m" : ""); + return ret; +#else + return _do_log(caller, "M", format, args, false); +#endif +} +int log_misc(const char* caller, const char* format, ...) { + va_list args; + va_start(args, format); + int ret = vlog_misc(caller, format, args); + va_end(args); + return ret; +} +int vlog_info(const char* caller, const char* format, va_list args) { +#ifdef LOG_INFO + if (HAS_COLOUR) WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\033[97m", 5, NULL, NULL); + int ret = _do_log(caller, "I", format, args, true); + puts(HAS_COLOUR ? "\033[0m" : ""); + return ret; +#else + return _do_log(caller, "I", format, args, false); +#endif +} +int log_info(const char* caller, const char* format, ...) { + va_list args; + va_start(args, format); + int ret = vlog_info(caller, format, args); + va_end(args); + return ret; +} +int vlog_warning(const char* caller, const char* format, va_list args) { +#ifdef LOG_WARNING + if (HAS_COLOUR) WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\033[33m", 5, NULL, NULL); + int ret = _do_log(caller, "W", format, args, true); + puts(HAS_COLOUR ? "\033[0m" : ""); + return ret; +#else + return _do_log(caller, "W", format, args, false); +#endif +} +int log_warning(const char* caller, const char* format, ...) { + va_list args; + va_start(args, format); + int ret = vlog_warning(caller, format, args); + va_end(args); + return ret; +} +int vlog_error(const char* caller, const char* format, va_list args) { +#ifdef LOG_ERROR + if (HAS_COLOUR) WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\033[91m", 5, NULL, NULL); + int ret = _do_log(caller, "E", format, args, true); + puts(HAS_COLOUR ? "\033[0m" : ""); + return ret; +#else + return _do_log(caller, "E", format, args, false); +#endif +} +int log_error(const char* caller, const char* format, ...) { + va_list args; + va_start(args, format); + int ret = vlog_error(caller, format, args); + va_end(args); + return ret; +} + +// char format_buf[1024]; // Will do. We guard against overflow in +// Fake[f]printf int WINAPIV Fakeprintf(const char* _Format, ...) { int +// flen = strlen(_Format); if (flen == strcspn(_Format, "\n") + 1 && flen +// < (sizeof format_buf)) { strcpy_s(format_buf, flen, _Format); +// format_buf[flen - 1] = 0; _Format = format_buf; +// } +// va_list args; +// va_start(args, _Format); +// int ret = vlog_info("printf", _Format, args); +// va_end(args); +// return ret; +// }; +// int WINAPIV Fakefprintf(FILE* _File, const char* _Format, ...) { +// int flen = strlen(_Format); +// if (flen == strcspn(_Format, "\n") + 1 && flen < (sizeof format_buf)) +// { strcpy_s(format_buf, flen, _Format); format_buf[flen +// - 1] = 0; _Format = format_buf; +// } +// va_list args; +// va_start(args, _Format); +// int ret = vlog_error("fprintf", _Format, args); +// va_end(args); +// return ret; +// }; + +// int WINAPIV Fakefprintf_s(FILE* _Stream, const char* _Format, ...) { +// va_list args; +// va_start(args, _Format); +// int ret = vlog_error("fprintf_s", _Format, args); +// va_end(args); +// return ret; +// }; + +static VOID(WINAPI* TrueOutputDebugStringA)(LPCSTR lpOutputString); +VOID WINAPI FakeOutputDebugStringA(LPCSTR lpOutputString) { log_info("debug", "%s", lpOutputString); } + +VOID trace_hook(char* output) { + output[strcspn(output, "\n")] = 0; + log_error("trace", output); +} + +void setup_logging() { + // Enable colour in CMD + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwMode = 0; + if (GetConsoleMode(hConsole, &dwMode)) + HAS_COLOUR = SetConsoleMode(hConsole, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + InitializeCriticalSection(&logger_lock); + + if (LOG_FILE == NULL) + LOG_FILE = CreateFileA("log.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); +} diff --git a/src/micetools/lib/mice/log.h b/src/micetools/lib/mice/log.h new file mode 100644 index 0000000..296dfa9 --- /dev/null +++ b/src/micetools/lib/mice/log.h @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +#define LOG_MISC +#define LOG_INFO +#define LOG_WARNING +#define LOG_ERROR + +#define HOOKS_LOGGER "hooks" +#define BOOT_LOGGER "boot" + +extern CRITICAL_SECTION logger_lock; + +int log_misc(const char* caller, const char* format, ...); +int log_info(const char* caller, const char* format, ...); +int log_warning(const char* caller, const char* format, ...); +int log_error(const char* caller, const char* format, ...); + +int vlog_misc(const char* caller, const char* format, va_list args); +int vlog_info(const char* caller, const char* format, va_list args); +int vlog_warning(const char* caller, const char* format, va_list args); +int vlog_error(const char* caller, const char* format, va_list args); + +void setup_logging(); diff --git a/src/micetools/lib/mice/meson.build b/src/micetools/lib/mice/meson.build new file mode 100644 index 0000000..0e87780 --- /dev/null +++ b/src/micetools/lib/mice/meson.build @@ -0,0 +1,7 @@ +mice_lib = static_library( + 'mice', + sources: [ + 'hook.c', + 'log.c', + ], +) diff --git a/src/micetools/lib/mice/mice.h b/src/micetools/lib/mice/mice.h new file mode 100644 index 0000000..6984fcb --- /dev/null +++ b/src/micetools/lib/mice/mice.h @@ -0,0 +1,3 @@ +#include "hook.h" +#include "ioctl.h" +#include "log.h" diff --git a/src/micetools/lib/util/hex.c b/src/micetools/lib/util/hex.c new file mode 100644 index 0000000..5c6eb85 --- /dev/null +++ b/src/micetools/lib/util/hex.c @@ -0,0 +1,57 @@ +unsigned char hex_value(char c) { + if (isdigit((unsigned char)c)) return c - '0'; + + switch (c) { + case 'a': + case 'A': + return 0x0A; + case 'b': + case 'B': + return 0x0B; + case 'c': + case 'C': + return 0x0C; + case 'd': + case 'D': + return 0x0D; + case 'e': + case 'E': + return 0x0E; + case 'f': + case 'F': + return 0x0F; + default: + return 0xFF; + } +} +int hex_to_bin(char *hex, unsigned char *bin, int hex_len) { + if (hex_len == 0) return 1; + + int work_len = 0; + for (int hex_ptr = 0; hex_ptr < hex_len; hex_ptr += 2, work_len++) { + char msn = hex[hex_ptr]; + + if ('0' <= msn && msn <= '9') + msn = msn - '0'; + else if ('A' <= msn && msn <= 'F') + msn = msn - 'A' + 10; + else if ('a' <= msn && msn <= 'f') + msn = msn - 'a' + 10; + else + return 0; + + char lsn = hex_ptr + 1 == hex_len ? '0' : hex[hex_ptr + 1]; + + if ('0' <= lsn && lsn <= '9') + lsn = lsn - '0'; + else if ('A' <= lsn && lsn <= 'F') + lsn = lsn - 'A' + 10; + else if ('a' <= lsn && lsn <= 'f') + lsn = lsn - 'a' + 10; + else + return 0; + + bin[work_len] = msn << 4 | lsn; + } + return 1; +} diff --git a/src/micetools/lib/util/hex.h b/src/micetools/lib/util/hex.h new file mode 100644 index 0000000..df20a2b --- /dev/null +++ b/src/micetools/lib/util/hex.h @@ -0,0 +1,2 @@ +unsigned char hex_value(char c); +int hex_to_bin(char *hex, unsigned char *bin, int hex_len); diff --git a/src/micetools/lib/util/meson.build b/src/micetools/lib/util/meson.build new file mode 100644 index 0000000..f8bc41c --- /dev/null +++ b/src/micetools/lib/util/meson.build @@ -0,0 +1,6 @@ +util_lib = static_library( + 'miceutil', + sources: [ + 'hex.c', + ], +) diff --git a/src/micetools/meson.build b/src/micetools/meson.build new file mode 100644 index 0000000..896fb30 --- /dev/null +++ b/src/micetools/meson.build @@ -0,0 +1,6 @@ +subdir('lib') + +subdir('micepatch') +subdir('micekeychip') +subdir('launcher') +subdir('dll') diff --git a/src/micetools/micekeychip/callbacks/appboot.c b/src/micetools/micekeychip/callbacks/appboot.c new file mode 100644 index 0000000..ce14f8c --- /dev/null +++ b/src/micetools/micekeychip/callbacks/appboot.c @@ -0,0 +1,77 @@ +#include "../config.h" +#include "callbacks.h" + +void mxkPcpAbGameId(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, AB_GAMEID, Config.appboot_gameid); +} +void mxkPcpAbSystemFlag(pcpa_t* stream, void* data) { + char sf[3]; + snprintf(sf, 3, "%02X", Config.appboot_systemflag); + pcpaSetSendPacket(stream, AB_SYSTEMFLAG, sf); +} +void mxkPcpAbModelType(pcpa_t* stream, void* data) { + char mt[16]; + snprintf(mt, 16, "%d", Config.appboot_modeltype); + pcpaSetSendPacket(stream, AB_MODELTYPE, mt); +} +void mxkPcpAbFormatType(pcpa_t* stream, void* data) { + char ft[16]; + snprintf(ft, 16, "%d", Config.appboot_formattype); + pcpaSetSendPacket(stream, AB_FORMATTYPE, ft); +} +void mxkPcpAbRegion(pcpa_t* stream, void* data) { + char rg[16]; + snprintf(rg, 16, "%d", Config.appboot_region); + pcpaSetSendPacket(stream, AB_REGION, rg); +} +void mxkPcpAbPlatformId(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, AB_PLATFORMID, Config.appboot_platformid); +} +void mxkPcpAbNetworkAddress(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, AB_NETWORKADDRESS, Config.appboot_network); +} +void mxkPcpAbDvd(pcpa_t* stream, void* data) { + char dvd[16]; + snprintf(dvd, 16, "%d", Config.appboot_dvdflag); + pcpaSetSendPacket(stream, AB_DVD, dvd); +} + +#define SEED_BUF_MAX 1024 +byte SEED_BUF[SEED_BUF_MAX]; +int SEED_BUF_LEN = 0; +void mxkPcpAbSeed(pcpa_t* stream, void* data) { + if (SEED_BUF_LEN == 0) { + FILE* fCert = fopen(Config.appboot_seed, "r"); + if (fCert == NULL) + SEED_BUF_LEN = -1; + else { + fseek(fCert, 0, SEEK_END); + SEED_BUF_LEN = ftell(fCert); + if (SEED_BUF_LEN > SEED_BUF_MAX) SEED_BUF_LEN = SEED_BUF_MAX; + rewind(fCert); + + fread(SEED_BUF, SEED_BUF_LEN, 1, fCert); + fclose(fCert); + } + } + + if (SEED_BUF_LEN == -1) { + // TODO: Fix this maybe? + pcpaSetBinaryMode(stream, binary_mode_none); + pcpaSetSendPacket(stream, AB_SEED, "-1"); + return; + } + + pcpaSetBinaryMode(stream, binary_mode_send); + pcpaSetBeforeBinaryModeCallBackFunc(stream, mxkBinaryCallback, NULL); + + BINARY_DATA_LEN = SEED_BUF_LEN; + memcpy(BINARY_DATA, SEED_BUF, SEED_BUF_LEN); + pcpaSetRecvBinaryBuffer(stream, BINARY_DATA, BINARY_DATA_LEN); + + pcpaSetSendPacket(stream, AB_SEED, "0"); + pcpaAddSendPacket(stream, "port", "40107"); + char sSize[16]; // todo: nicer lol + itoa(SEED_BUF_LEN, sSize, 10); + pcpaAddSendPacket(stream, "size", sSize); +} diff --git a/src/micetools/micekeychip/callbacks/billing.c b/src/micetools/micekeychip/callbacks/billing.c new file mode 100644 index 0000000..47317f9 --- /dev/null +++ b/src/micetools/micekeychip/callbacks/billing.c @@ -0,0 +1,112 @@ +#include "../config.h" +#include "callbacks.h" + +void mxkPcpPbKeyId(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, BIL_KEYID, Config.billing_keyid); +} +void mxkPcpPbMainId(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, BIL_MAINID, Config.billing_mainid); +} +void mxkPcpPbPlayCount(pcpa_t* stream, void* data) { + char pc[9]; + snprintf(pc, sizeof pc, "%08X", Config.billing_playcount); + pcpaSetSendPacket(stream, BIL_PLAYCOUNT, pc); +} +void mxkPcpPbPlayLimit(pcpa_t* stream, void* data) { + char pl[9]; + snprintf(pl, sizeof pl, "%08X", Config.billing_playlimit); + + char* playlimit = pcpaGetCommand(stream, BIL_PLAYLIMIT); + pcpaSetSendPacket(stream, BIL_PLAYLIMIT, pl); + + if (!strcmp(playlimit, "?")) return; + + // TODO: This + pcpaSetSendPacket(stream, "code", "-1"); +} +void mxkPcpPbNearfull(pcpa_t* stream, void* data) { + char nf[9]; + snprintf(nf, sizeof nf, "%08X", Config.billing_nearfull); + + pcpaSetSendPacket(stream, BIL_NEARFUL, nf); +} + +#define PUBKEY_BUF_MAX 1024 +byte PUBKEY_BUF[PUBKEY_BUF_MAX]; +int PUBKEY_LEN = 0; +void mxkPcpPbSignaturePubKey(pcpa_t* stream, void* data) { + if (PUBKEY_LEN == 0) { + FILE* fPubkey = fopen(Config.billing_pubkey, "r"); + if (fPubkey == NULL) + PUBKEY_LEN = -1; + else { + fseek(fPubkey, 0, SEEK_END); + PUBKEY_LEN = ftell(fPubkey); + if (PUBKEY_LEN > PUBKEY_BUF_MAX) PUBKEY_LEN = PUBKEY_BUF_MAX; + rewind(fPubkey); + + fread(PUBKEY_BUF, PUBKEY_LEN, 1, fPubkey); + fclose(fPubkey); + } + } + + if (PUBKEY_LEN == -1) { + // TODO: Fix this maybe? + pcpaSetBinaryMode(stream, binary_mode_none); + pcpaSetSendPacket(stream, BIL_SIGNATURE, "-1"); + return; + } + + pcpaSetBinaryMode(stream, binary_mode_send); + pcpaSetBeforeBinaryModeCallBackFunc(stream, mxkBinaryCallback, NULL); + + BINARY_DATA_LEN = PUBKEY_LEN; + memcpy(BINARY_DATA, PUBKEY_BUF, PUBKEY_LEN); + pcpaSetRecvBinaryBuffer(stream, BINARY_DATA, BINARY_DATA_LEN); + + pcpaSetSendPacket(stream, BIL_SIGNATURE, "0"); + pcpaAddSendPacket(stream, "port", "40107"); + char sSize[16]; + snprintf(sSize, 16, "%d", PUBKEY_LEN); + pcpaAddSendPacket(stream, "size", sSize); +} + +#define CA_CERT_BUF_MAX 1024 +byte CA_CERTIFICATION[CA_CERT_BUF_MAX]; +int CA_CERT_LEN = 0; +void mxkPcpPbCaCertification(pcpa_t* stream, void* data) { + if (CA_CERT_LEN == 0) { + FILE* fCert = fopen(Config.billing_cacert, "r"); + if (fCert == NULL) + CA_CERT_LEN = -1; + else { + fseek(fCert, 0, SEEK_END); + CA_CERT_LEN = ftell(fCert); + if (CA_CERT_LEN > CA_CERT_BUF_MAX) CA_CERT_LEN = CA_CERT_BUF_MAX; + rewind(fCert); + + fread(CA_CERTIFICATION, CA_CERT_LEN, 1, fCert); + fclose(fCert); + } + } + + if (CA_CERT_LEN == -1) { + // TODO: Fix this maybe? + pcpaSetBinaryMode(stream, binary_mode_none); + pcpaSetSendPacket(stream, BIL_CACERT, "-1"); + return; + } + + pcpaSetBinaryMode(stream, binary_mode_send); + pcpaSetBeforeBinaryModeCallBackFunc(stream, mxkBinaryCallback, NULL); + + BINARY_DATA_LEN = CA_CERT_LEN; + memcpy(BINARY_DATA, CA_CERTIFICATION, CA_CERT_LEN); + pcpaSetRecvBinaryBuffer(stream, BINARY_DATA, BINARY_DATA_LEN); + + pcpaSetSendPacket(stream, BIL_CACERT, "0"); + pcpaAddSendPacket(stream, "port", "40107"); + char sSize[16]; // todo: nicer lol + itoa(CA_CERT_LEN, sSize, 10); + pcpaAddSendPacket(stream, "size", sSize); +} diff --git a/src/micetools/micekeychip/callbacks/callbacks.h b/src/micetools/micekeychip/callbacks/callbacks.h new file mode 100644 index 0000000..601efeb --- /dev/null +++ b/src/micetools/micekeychip/callbacks/callbacks.h @@ -0,0 +1,94 @@ +#include "../lib/libpcp/libpcp.h" + +pcpa_callback mxkBinaryCallback; +extern byte BINARY_DATA[4096]; +extern size_t BINARY_DATA_LEN; + +#define KEYCHIP "keychip." +#define APPBOOT KEYCHIP##"appboot." +#define BILLING KEYCHIP##"billing." +#define TRACEDATA KEYCHIP##"tracedata." + +// Misc +#define KC_VERSION KEYCHIP##"version" +#define KC_STATUS KEYCHIP##"status" +pcpa_callback mxkPcpVersion; +pcpa_callback mxkPcpStatus; + +// Crypto +#define DS_COMPUTE KEYCHIP##"ds.compute" +#define SSD_PROOF KEYCHIP##"ssd.proof" +#define SSD_HOSTPROOF KEYCHIP##"ssd.hostproof" +#define KC_ENCRYPT KEYCHIP##"encrypt" +#define KC_DECRYPT KEYCHIP##"decrypt" +#define KC_SETIV KEYCHIP##"setiv" +pcpa_callback mxkPcpDsCompute; +pcpa_callback mxkPcpSsdProof; +pcpa_callback mxkPcpSsdHostProof; +pcpa_callback mkxPcpEncrypt; +pcpa_callback mxkPcpDecrypt; +pcpa_callback mxkPcpSetIv; + +// Appboot +#define AB_GAMEID APPBOOT##"gameid" +#define AB_SYSTEMFLAG APPBOOT##"systemflag" +#define AB_MODELTYPE APPBOOT##"modeltype" +#define AB_FORMATTYPE APPBOOT##"formattype" +#define AB_REGION APPBOOT##"region" +#define AB_PLATFORMID APPBOOT##"platformid" +#define AB_NETWORKADDRESS APPBOOT##"networkaddr" +#define AB_DVD APPBOOT##"dvdflag" +#define AB_SEED APPBOOT##"seed" +pcpa_callback mxkPcpAbGameId; +pcpa_callback mxkPcpAbSystemFlag; +pcpa_callback mxkPcpAbModelType; +pcpa_callback mxkPcpAbFormatType; +pcpa_callback mxkPcpAbRegion; +pcpa_callback mxkPcpAbPlatformId; +pcpa_callback mxkPcpAbNetworkAddress; +pcpa_callback mxkPcpAbDvd; +pcpa_callback mxkPcpAbSeed; + +// Billing +#define BIL_KEYID BILLING##"keyid" +#define BIL_MAINID BILLING##"mainid" +#define BIL_PLAYCOUNT BILLING##"playcount" +#define BIL_PLAYLIMIT BILLING##"playlimit" +#define BIL_NEARFUL BILLING##"nearful" +#define BIL_SIGNATURE BILLING##"signaturepubkey" +#define BIL_CACERT BILLING##"cacertification" +pcpa_callback mxkPcpPbKeyId; +pcpa_callback mxkPcpPbMainId; +pcpa_callback mxkPcpPbPlayCount; +pcpa_callback mxkPcpPbPlayLimit; +pcpa_callback mxkPcpPbNearfull; +pcpa_callback mxkPcpPbSignaturePubKey; +pcpa_callback mxkPcpPbCaCertification; + +// Tracedata +#define TRA_RESTORE TRACEDATA##"restore" +#define TRA_PUT TRACEDATA##"put" +#define TRA_GET TRACEDATA##"get" +#define TRA_LOGICALERASE TRACEDATA##"logicalerase" +#define TRA_SECTOREERASE TRACEDATA##"sectorerase" +pcpa_callback mxkPcpTdRestore; +pcpa_callback mxkPcpTdPut; +pcpa_callback mxkPcpTdGet; +pcpa_callback mxkPcpTdLogicalErase; +pcpa_callback mxkPcpTdSectorErase; + +// Storage +#define KC_EEPROM KEYCHIP##"eeprom" +#define KC_NVRAM KEYCHIP##"nvram" +#define KC_NVRAM0 KC_NVRAM##"0" +#define KC_NVRAM1 KC_NVRAM##"1" +#define KC_NVRAM2 KC_NVRAM##"2" +#define KC_NVRAM3 KC_NVRAM##"3" +#define KC_NVRAM4 KC_NVRAM##"4" +#define KC_NVRAM5 KC_NVRAM##"5" +#define KC_NVRAM6 KC_NVRAM##"6" +#define KC_NVRAM7 KC_NVRAM##"7" +#define KC_NVRAM8 KC_NVRAM##"8" +#define KC_NVRAM9 KC_NVRAM##"9" +pcpa_callback mxkPcpEeprom; +pcpa_callback mxkPcpNvram; diff --git a/src/micetools/micekeychip/callbacks/crypto.c b/src/micetools/micekeychip/callbacks/crypto.c new file mode 100644 index 0000000..d3bdceb --- /dev/null +++ b/src/micetools/micekeychip/callbacks/crypto.c @@ -0,0 +1,12 @@ +#include "callbacks.h" + +void mxkPcpDsCompute(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, "code", "54"); +} +void mxkPcpSsdProof(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, "code", "54"); +} +void mxkPcpSsdHostProof(pcpa_t* stream, void* data) {} +void mkxPcpEncrypt(pcpa_t* stream, void* data) {} +void mxkPcpDecrypt(pcpa_t* stream, void* data) {} +void mxkPcpSetIv(pcpa_t* stream, void* data) {} diff --git a/src/micetools/micekeychip/callbacks/misc.c b/src/micetools/micekeychip/callbacks/misc.c new file mode 100644 index 0000000..2558daf --- /dev/null +++ b/src/micetools/micekeychip/callbacks/misc.c @@ -0,0 +1,20 @@ +#include "../config.h" +#include "callbacks.h" + +void mxkPcpVersion(pcpa_t* stream, void* data) { + char* sCache = pcpaGetCommand(stream, "cache"); + char* sDevice = pcpaGetCommand(stream, "device"); + + bool cache = sCache != NULL && strcmp(sCache, "0") == 0; + + char version[5]; + if (sDevice && strcmp(sDevice, "n2") == 0) + snprintf(version, 5, "%04X", Config.keychip_dongle_version); + else + snprintf(version, 5, "%04X", Config.keychip_pic_version); + pcpaSetSendPacket(stream, KC_VERSION, version); +} + +void mxkPcpStatus(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, KC_STATUS, Config.keychip_status); +} diff --git a/src/micetools/micekeychip/callbacks/storage.c b/src/micetools/micekeychip/callbacks/storage.c new file mode 100644 index 0000000..7bbc2a3 --- /dev/null +++ b/src/micetools/micekeychip/callbacks/storage.c @@ -0,0 +1,10 @@ +#include "callbacks.h" + +void mxkPcpEeprom(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, KC_EEPROM, "0"); +} + +void mxkPcpNvram(pcpa_t* stream, void* data) { + char* keyword = pcpaGetKeyword(stream, 0); + pcpaSetSendPacket(stream, keyword, "0"); +} diff --git a/src/micetools/micekeychip/callbacks/tracedata.c b/src/micetools/micekeychip/callbacks/tracedata.c new file mode 100644 index 0000000..7f081bb --- /dev/null +++ b/src/micetools/micekeychip/callbacks/tracedata.c @@ -0,0 +1,35 @@ +#include "callbacks.h" + +void mxkPcpTdRestore(pcpa_t* stream, void* data) { + char* sUpdate = pcpaGetCommand(stream, "update"); + char* sRestart = pcpaGetCommand(stream, "restart"); + bool update = sUpdate != NULL && strcmp(sUpdate, "1") == 0; + bool restart = sRestart != NULL && strcmp(sRestart, "1") == 0; + + if (restart) { + pcpaSetSendPacket(stream, TRA_RESTORE, "1"); + } else { + pcpaSetSendPacket(stream, TRA_RESTORE, "2"); + } +} +void mxkPcpTdPut(pcpa_t* stream, void* data) { + char* sPut = pcpaGetCommand(stream, TRA_PUT); + + if (strcmp(sPut, "?")) { + pcpaSetSendPacket(stream, TRA_PUT, "0"); + return; + } + + // Process packet maybe? + pcpaSetSendPacket(stream, TRA_PUT, "0"); +} +void mxkPcpTdGet(pcpa_t* stream, void* data) { + // TODO: lol + pcpaSetSendPacket(stream, TRA_GET, ""); +} +void mxkPcpTdLogicalErase(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, TRA_LOGICALERASE, ""); +} +void mxkPcpTdSectorErase(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, TRA_SECTOREERASE, ""); +} diff --git a/src/micetools/micekeychip/config.def b/src/micetools/micekeychip/config.def new file mode 100644 index 0000000..0d4efca --- /dev/null +++ b/src/micetools/micekeychip/config.def @@ -0,0 +1,50 @@ +#ifndef SECTION +#define SECTION(n, comment) +#endif +#ifndef HEADER +#define HEADER(comment) +#endif + +HEADER("The main config file for micekeychip") + +SECTION(pcp, "") +CFG_int(pcp, control_port, 40106, "The port to bind for control") +CFG_int(pcp, binary_port, 40107, "The port to bind for binary transfer") +CFG_bool(pcp, bind_global, false, "Should we bind to 0.0.0.0 instead of 127.0.0.1") + +SECTION(keychip, "") +CFG_hex(keychip, pic_version, 4, 0104, "The version to report for the pic") +CFG_hex(keychip, dongle_version, 4, 0104, "The version to report for the dongle") +CFG_str(keychip, status, "available", "Status to report") + +SECTION(appboot, "") +CFG_str(appboot, gameid, "SDEY", "4-letter game ID this keychip is for") +CFG_hex(appboot, systemflag, 2, 6C, "System flags") +CFG_int(appboot, modeltype, 2, "System model this keychip is for") +CFG_int(appboot, formattype, 1, "Not totally sure about this") +CFG_int(appboot, region, 1, "Region bitmask\n; 8 4 2 1\n; CN EX US JP") +CFG_str(appboot, platformid, "AAS", "The platform this keychip is for. AAM=AMD, AAS=Nvidia") +CFG_str(appboot, network, "192.168.103.0", "The subnet this keychip allows for networking. Must be 192.168.103.0 for network checks to pass") +CFG_int(appboot, dvdflag, 1, "") +CFG_str(appboot, seed, "C:\\system\\device\\seed.bin", "Path to constant appboot.seed (indev)") + +SECTION(crypto, "") +CFG_bool(crypto, ds_enable, false, "Process keychip.ds.compute?") +CFG_bool(crypto, ssd_enable, false, "Process keychip.ssd.proof?") +CFG_str(crypto, table, "C:\\system\\device\\SDEY_Table.dat", "Path to the *_Table.dat file") + +SECTION(billing, "") +CFG_str(billing, keyid, "A69E-01A8888", "This keychip's ID") +CFG_str(billing, mainid, "", "The board ID") +CFG_int(billing, playcount, 0, "Initial playcount value") +CFG_hex(billing, playlimit, 8, 00001400, "Max playcount before lockout") +CFG_hex(billing, nearfull, 8, 00066048, "Number of plays before reporting to billing") +CFG_str(billing, pubkey, "C:\\system\\device\\billing.pub", "Path to the billing pubkey (billing.pub)") +CFG_str(billing, cacert, "C:\\system\\device\\ca.crt", "Path to the root certificate authority (ca.crt)") + +#undef CFG_str +#undef CFG_int +#undef CFG_bool +#undef CFG_hex +#undef SECTION +#undef HEADER diff --git a/src/micetools/micekeychip/config.h b/src/micetools/micekeychip/config.h new file mode 100644 index 0000000..139bd57 --- /dev/null +++ b/src/micetools/micekeychip/config.h @@ -0,0 +1,14 @@ +#include + +#define CONFIG_PATH "config.ini" + +typedef struct config { +#define CFG_str(s, n, default, comment) char* s##_##n; +#define CFG_bool(s, n, default, comment) bool s##_##n; +#define CFG_int(s, n, default, comment) int s##_##n; +#define CFG_hex(s, n, precision, default, comment) int s##_##n; +#include "config.def" + bool _keep_linter_happy; +} config_t; + +extern config_t Config; diff --git a/src/micetools/micekeychip/main.c b/src/micetools/micekeychip/main.c new file mode 100644 index 0000000..b0a3c4c --- /dev/null +++ b/src/micetools/micekeychip/main.c @@ -0,0 +1,111 @@ +#include "mxk.h" + +config_t Config = { +#define CFG_str(s, n, default, comment) .s##_##n = default, +#define CFG_bool(s, n, default, comment) .s##_##n = default, +#define CFG_int(s, n, default, comment) .s##_##n = default, +#define CFG_hex(s, n, precision, default, comment) .s##_##n = 0x##default, +#include "config.def" + ._keep_linter_happy = true}; + +void make_default_config() { + FILE *config_file = fopen(CONFIG_PATH, "w"); + if (config_file == NULL) { + puts("Failed to create config file!"); + return; + }; + int first_section = true; + +#define CFG_str(s, n, default, comment) \ + if (strlen(comment) != 0) fprintf(config_file, "; %s\n", comment); \ + fprintf(config_file, "; (string) default = %s\n", default); \ + fprintf(config_file, "%s = %s\n", #n, default); + +#define CFG_bool(s, n, default, comment) \ + if (strlen(comment) != 0) fprintf(config_file, "; %s\n", comment); \ + fprintf(config_file, "; (bool) default = %s\n", \ + default ? "true" : "false"); \ + fprintf(config_file, "%s = %s\n", #n, default ? "true" : "false"); + +#define CFG_int(s, n, default, comment) \ + if (strlen(comment) != 0) fprintf(config_file, "; %s\n", comment); \ + fprintf(config_file, "; (int) default = %d\n", default); \ + fprintf(config_file, "%s = %d\n", #n, default); + +#define CFG_hex(s, n, precision, default, comment) \ + if (strlen(comment) != 0) fprintf(config_file, "; %s\n", comment); \ + fprintf(config_file, "; (hex) default = %.*X\n", precision, \ + 0x##default); \ + fprintf(config_file, "%s = %.*X\n", #n, precision, 0x##default); + +#define SECTION(s, comment) \ + if (!first_section) fprintf(config_file, "\n"); \ + first_section = false; \ + if (strlen(comment) != 0) fprintf(config_file, "; %s\n", comment); \ + fprintf(config_file, "[%s]\n", #s); + +#define HEADER(comment) \ + fprintf(config_file, "; %s\n", comment); \ + first_section = false; + +#include "config.def" + + fclose(config_file); +} + +int handler(void *user, const char *section, const char *name, + const char *value) { + config_t *cfg = (config_t *)user; + + char *end; + + if (false) + ; +#define CFG_str(s, n, default, comment) \ + else if (stricmp(section, #s) == 0 && stricmp(name, #n) == 0) \ + cfg->s##_##n = strdup(value); +#define CFG_bool(s, n, default, comment) \ + else if (stricmp(section, #s) == 0 && stricmp(name, #n) == 0) \ + cfg->s##_##n = strcmp(value, "true") == 0; +#define CFG_int(s, n, default, comment) \ + else if (stricmp(section, #s) == 0 && stricmp(name, #n) == 0) { \ + cfg->s##_##n = strtol(value, &end, 10); \ + if (end == value || *end != '\0' || errno == ERANGE) \ + cfg->s##_##n = default; \ + } +#define CFG_hex(s, n, precision, default, comment) \ + else if (stricmp(section, #s) == 0 && stricmp(name, #n) == 0) { \ + cfg->s##_##n = strtol(value, &end, 16); \ + if (end == value || *end != '\0' || errno == ERANGE) \ + cfg->s##_##n = 0x##default; \ + } + +#include "config.def" + + return 1; +} + +void load_config() { + if (ini_parse(CONFIG_PATH, handler, &Config) < 0) + printf("Can't load '%s', using defaults\n", CONFIG_PATH); +} + +int main() { + DWORD dwAttrib = GetFileAttributes(CONFIG_PATH); + if (dwAttrib == INVALID_FILE_ATTRIBUTES || + (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) + make_default_config(); + + load_config(); + + int err = mxkInit(); + if (err != 0) { + PCP_LOG("Error mxkInit. Code %d\n", err); + return -1; + } + + while (1) { + err = mxkPcpServer(); + if (err != e_pcpp_ok) PCP_LOG("Server tick: %d\n", err); + } +} diff --git a/src/micetools/micekeychip/meson.build b/src/micetools/micekeychip/meson.build new file mode 100644 index 0000000..880d6ef --- /dev/null +++ b/src/micetools/micekeychip/meson.build @@ -0,0 +1,28 @@ +dependencies = [] +link_with = [inih.get_variable('lib_inih')] + +# Depending on how we're getting access to libpcp, we need some extra work here +if libpcp_is_static + link_with += libpcp +else + link_with += amlib + dependencies += libpcp + dependencies += meson.get_compiler('c').find_library('ws2_32') +endif + +executable( + 'micekeychip', + win_subsystem: 'console', + sources: [ + 'main.c', + 'mxk.c', + 'callbacks/appboot.c', + 'callbacks/billing.c', + 'callbacks/crypto.c', + 'callbacks/misc.c', + 'callbacks/tracedata.c', + 'callbacks/storage.c', + ], + link_with: link_with, + dependencies: dependencies, +) diff --git a/src/micetools/micekeychip/mxk.c b/src/micetools/micekeychip/mxk.c new file mode 100644 index 0000000..8736200 --- /dev/null +++ b/src/micetools/micekeychip/mxk.c @@ -0,0 +1,154 @@ +#include "mxk.h" + +byte SERVER_STATE = 0; +pcpa_t PCP; +pcpa_cb_table_t CALLBACK_FUNCTION_BUFFER[40]; + +byte BINARY_DATA[4096]; +size_t BINARY_DATA_LEN; + +void mxkBinaryCallback(pcpa_t* stream, void* data) { + pcpaSetSendBinaryBuffer(stream, BINARY_DATA, BINARY_DATA_LEN); +} + +int mxkInit() { + // Enable colour + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwMode = 0; + if (GetConsoleMode(hConsole, &dwMode)) + SetConsoleMode(hConsole, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + WSADATA wsaData; + int err = WSAStartup(2, &wsaData); + SERVER_STATE = 1; + return err; +} + +e_pcpa_t mxkPcpStreamInit() { + e_pcpa_t err; + + err = pcpaInitStream(&PCP); + if (err != e_pcpa_ok) { + printf("pcpaInitStream Error. Code:%d\n", err); + return err; + } + + err = pcpaSetCallbackFuncBuffer(&PCP, CALLBACK_FUNCTION_BUFFER, + (sizeof CALLBACK_FUNCTION_BUFFER) / + (sizeof CALLBACK_FUNCTION_BUFFER[0])); + if (err != e_pcpa_ok) { + printf("pcpaSetCallBackFuncBuffer Error. Code:%d\n", err); + return err; + } + + // Misc + pcpaSetCallbackFunc(&PCP, KC_VERSION, mxkPcpVersion, NULL); + pcpaSetCallbackFunc(&PCP, KC_STATUS, mxkPcpStatus, NULL); + // Crypto + pcpaSetCallbackFunc(&PCP, DS_COMPUTE, mxkPcpDsCompute, NULL); + pcpaSetCallbackFunc(&PCP, SSD_PROOF, mxkPcpSsdProof, NULL); + pcpaSetCallbackFunc(&PCP, SSD_HOSTPROOF, mxkPcpSsdHostProof, NULL); + pcpaSetCallbackFunc(&PCP, KC_ENCRYPT, mkxPcpEncrypt, NULL); + pcpaSetCallbackFunc(&PCP, KC_DECRYPT, mxkPcpDecrypt, NULL); + pcpaSetCallbackFunc(&PCP, KC_SETIV, mxkPcpSetIv, NULL); + // Appboot + pcpaSetCallbackFunc(&PCP, AB_GAMEID, mxkPcpAbGameId, NULL); + pcpaSetCallbackFunc(&PCP, AB_SYSTEMFLAG, mxkPcpAbSystemFlag, NULL); + pcpaSetCallbackFunc(&PCP, AB_MODELTYPE, mxkPcpAbModelType, NULL); + pcpaSetCallbackFunc(&PCP, AB_FORMATTYPE, mxkPcpAbFormatType, NULL); + pcpaSetCallbackFunc(&PCP, AB_REGION, mxkPcpAbRegion, NULL); + pcpaSetCallbackFunc(&PCP, AB_PLATFORMID, mxkPcpAbPlatformId, NULL); + pcpaSetCallbackFunc(&PCP, AB_NETWORKADDRESS, mxkPcpAbNetworkAddress, + NULL); + pcpaSetCallbackFunc(&PCP, AB_DVD, mxkPcpAbDvd, NULL); + pcpaSetCallbackFunc(&PCP, AB_SEED, mxkPcpAbSeed, NULL); + // Billing + pcpaSetCallbackFunc(&PCP, BIL_KEYID, mxkPcpPbKeyId, NULL); + pcpaSetCallbackFunc(&PCP, BIL_MAINID, mxkPcpPbMainId, NULL); + pcpaSetCallbackFunc(&PCP, BIL_PLAYCOUNT, mxkPcpPbPlayCount, NULL); + pcpaSetCallbackFunc(&PCP, BIL_PLAYLIMIT, mxkPcpPbPlayLimit, NULL); + pcpaSetCallbackFunc(&PCP, BIL_NEARFUL, mxkPcpPbNearfull, NULL); + pcpaSetCallbackFunc(&PCP, BIL_SIGNATURE, mxkPcpPbSignaturePubKey, NULL); + pcpaSetCallbackFunc(&PCP, BIL_CACERT, mxkPcpPbCaCertification, NULL); + // Tracedata + pcpaSetCallbackFunc(&PCP, TRA_RESTORE, mxkPcpTdRestore, NULL); + pcpaSetCallbackFunc(&PCP, TRA_PUT, mxkPcpTdPut, NULL); + pcpaSetCallbackFunc(&PCP, TRA_GET, mxkPcpTdGet, NULL); + pcpaSetCallbackFunc(&PCP, TRA_LOGICALERASE, mxkPcpTdLogicalErase, NULL); + pcpaSetCallbackFunc(&PCP, TRA_SECTOREERASE, mxkPcpTdSectorErase, NULL); + // Storage + pcpaSetCallbackFunc(&PCP, KC_EEPROM, mxkPcpEeprom, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM0, mxkPcpNvram, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM1, mxkPcpNvram, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM2, mxkPcpNvram, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM3, mxkPcpNvram, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM4, mxkPcpNvram, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM5, mxkPcpNvram, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM6, mxkPcpNvram, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM7, mxkPcpNvram, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM8, mxkPcpNvram, NULL); + pcpaSetCallbackFunc(&PCP, KC_NVRAM9, mxkPcpNvram, NULL); + + long text_port = Config.pcp_control_port; + if (text_port > 0xffff) { + puts("PCP control port invalid"); + exit(-1); + } + long binary_port = Config.pcp_binary_port; + if (binary_port > 0xffff) { + puts("PCP binary port invalid"); + exit(-1); + } + int open_mode = Config.pcp_bind_global ? OPEN_MODE_GLOBAL : OPEN_MODE_1; + + err = pcpaOpenServerWithBinary(&PCP, open_mode, text_port, binary_port, + 300000); + if (err != e_pcpa_ok && err != e_pcpa_to) { + printf("pcpaOpenServerWithBinary Error. Code %d\n", err); + return e_pcpa_not_open; + } + if (open_mode == OPEN_MODE_GLOBAL) + printf("Listening on 0.0.0.0:%d (:%d)\n", text_port, binary_port); + else + printf("Listening on 127.0.0.1:%d (:%d)\n", text_port, binary_port); + return e_pcpa_ok; +} + +#define TICK_MS 16 +#define PRINT_DEBUG +#ifdef PRINT_DEBUG +// Larger TICK_MS for testing +#undef TICK_MS +#define TICK_MS 100 +#endif + +e_pcpa_t mxkPcpServer() { + int err; + if (SERVER_STATE == 1) { + err = mxkPcpStreamInit(); + if (err == 0) { + SERVER_STATE = 2; + return err; + } + } else { + if (SERVER_STATE != 2) { + return (SERVER_STATE == 0) ? e_pcpa_cannot_open : e_pcpa_not_open; + } + err = pcpaServer(&PCP, TICK_MS); + if (err == e_pcpa_to || err == e_pcpa_closed) err = e_pcpa_ok; + + if (err) { + printf("Error pcpaServer. Code %d\n", err); + pcpaClose(&PCP); + SERVER_STATE = 1; + } + } + +#ifdef PRINT_DEBUG + puts("\033[H\033[J\033[H"); + pcpaPrint(&PCP); + puts("\033[J"); +#endif + + return err; +} \ No newline at end of file diff --git a/src/micetools/micekeychip/mxk.h b/src/micetools/micekeychip/mxk.h new file mode 100644 index 0000000..e133d2e --- /dev/null +++ b/src/micetools/micekeychip/mxk.h @@ -0,0 +1,11 @@ +#include + +#include "../../../subprojects/inih_dep/ini.h" +#include "../lib/libpcp/libpcp.h" +#include "callbacks/callbacks.h" +#include "config.h" + +void mxkBinaryCallback(pcpa_t* stream, void* data); +int mxkInit(); +e_pcpa_t mxkPcpStreamInit(); +e_pcpa_t mxkPcpServer(); \ No newline at end of file diff --git a/src/micetools/micepatch/main.c b/src/micetools/micepatch/main.c new file mode 100644 index 0000000..db28ae4 --- /dev/null +++ b/src/micetools/micepatch/main.c @@ -0,0 +1,303 @@ +#include "../lib/json/json.h" +#include "../lib/util/hex.h" +#include "stdbool.h" +#include "stdio.h" +#include "string.h" + +typedef struct { + size_t offset; + size_t count; + unsigned char* from; + unsigned char* to; +} patch_t; + +typedef struct { + char* name; + char* description; + bool apply; + size_t nopatches; + patch_t patches[]; +} patchset_t; + +typedef struct { + size_t nopatchsets; + patchset_t** patchsets; +} patches_t; + +bool fetch(json_value* object, char* name, json_value** value) { + if (object->type != json_object) return false; + for (size_t j = 0; j < object->u.object.length; j++) { + json_object_entry* entry = &(object->u.object.values[j]); + if (strncmp(name, entry->name, entry->name_length) == 0) { + *value = entry->value; + return true; + } + } + return false; +} +bool fetch_string(json_value* object, char* name, char** value) { + json_value* fetched; + if (!fetch(object, name, &fetched)) return false; + if (fetched->type != json_string) return false; + *value = fetched->u.string.ptr; + return true; +} +bool fetch_int(json_value* object, char* name, size_t* value) { + json_value* fetched; + if (!fetch(object, name, &fetched)) return false; + if (fetched->type != json_integer) return false; + *value = fetched->u.integer; + return true; +} +bool fetch_bool(json_value* object, char* name, bool* value) { + json_value* fetched; + if (!fetch(object, name, &fetched)) return false; + if (fetched->type != json_boolean) return false; + *value = fetched->u.boolean; + return true; +} +void free_patches(patches_t* patches) { + if (patches->nopatchsets == 0) return; + for (int i = patches->nopatchsets - 1; i >= 0; i--) { + if (patches->patchsets[i] != NULL) { + for (int j = 0; j < patches->patchsets[i]->nopatches; j++) { + if (patches->patchsets[i]->patches[j].from != NULL) free(patches->patchsets[i]->patches[j].from); + if (patches->patchsets[i]->patches[j].to != NULL) free(patches->patchsets[i]->patches[j].to); + } + free(patches->patchsets[i]); + } + } + patches->nopatchsets = 0; +} + +bool parse_patches(patches_t* patches, json_value** set_json, int set_count, char* error) { + patches->nopatchsets = set_count; + patches->patchsets = (patchset_t**)malloc(set_count * sizeof(patchset_t*)); + for (int i = 0; i < set_count; i++) patches->patchsets[i] = NULL; + + error[0] = '\0'; + for (int i = 0; i < set_count; i++) { + char *name, *description; + if (!fetch_string(set_json[i], "name", &name)) { + snprintf(error, json_error_max, "'name' missing for patch %d", i); + goto failed; + } + if (!fetch_string(set_json[i], "description", &description)) { + snprintf(error, json_error_max, "'description' missing for patch %d (%s)", i, name); + goto failed; + } + bool apply; + if (!fetch_bool(set_json[i], "apply", &apply)) { + snprintf(error, json_error_max, "'apply' missing for patch %d (%s)", i, name); + goto failed; + } + json_value* set_patches; + if (!fetch(set_json[i], "patches", &set_patches) || set_patches->type != json_array) { + snprintf(error, json_error_max, "'patches' missing for patch %d (%s)", i, name); + goto failed; + } + + int count = set_patches->u.array.length; + patchset_t* patchset = (patchset_t*)malloc(sizeof(patchset_t) + (sizeof(patch_t) * count)); + patches->patchsets[i] = patchset; + patchset->name = name; + patchset->apply = apply; + patchset->description = description; + patchset->nopatches = count; + + for (int j = 0; j < count; j++) { + json_value* this_patch = set_patches->u.array.values[j]; + size_t at; + char *from, *to; + if (!fetch_int(this_patch, "at", &at)) { + snprintf(error, json_error_max, "'at' missing for patch %s[%d]", name, j); + goto failed; + } + if (!fetch_string(this_patch, "from", &from)) { + snprintf(error, json_error_max, "'from' missing for patch %s[%d]", name, j); + goto failed; + } + if (!fetch_string(this_patch, "to", &to)) { + snprintf(error, json_error_max, "'to' missing for patch %s[%d]", name, j); + goto failed; + } + + patchset->patches[j].offset = at; + size_t size = strlen(from); + if (size != strlen(to)) { + snprintf(error, json_error_max, "'from' and 'to' lengths differ in patch %s[%d]", name, j); + goto failed; + } + if (size % 2 != 0) { + snprintf(error, json_error_max, "invalid hex string in patch %s[%d]", name, j); + goto failed; + } + char* bin_from = patchset->patches[j].from = (char*)malloc(size / 2); + char* bin_to = patchset->patches[j].to = (char*)malloc(size / 2); + if (!hex_to_bin(from, bin_from, size)) { + snprintf(error, json_error_max, "invalid hex string in patch %s[%d].from", name, j); + goto failed; + }; + if (!hex_to_bin(to, bin_to, size)) { + snprintf(error, json_error_max, "invalid hex string in patch %s[%d].to", name, j); + goto failed; + }; + + patchset->patches[j].count = size / 2; + } + } + + return true; + +failed: + free_patches(patches); + return false; +} + +void print_patches(patches_t* patches) { + for (int i = 0; i < patches->nopatchsets; i++) { + patchset_t* patchset = patches->patchsets[i]; + + printf("Patch: %s (%s)\n", patchset->name, patchset->apply ? "applied" : "unapplied"); + printf("- %s\n", patchset->description); + for (int i = 0; i < patchset->nopatches; i++) { + printf(":: %d bytes at %08x\n", patchset->patches[i].count, patchset->patches[i].offset); + } + puts("======================"); + } +} + +void apply_patches(patches_t* patches, char* filename) { + FILE* fp = fopen(filename, "r+b"); + if (fp == NULL) { + fprintf(stderr, "Failed to open %s for modification\n", filename); + return; + } + + fseek(fp, 0L, SEEK_END); + size_t sz = ftell(fp); + + for (int i = 0; i < patches->nopatchsets; i++) { + patchset_t* patchset = patches->patchsets[i]; + for (int j = 0; j < patchset->nopatches; j++) { + patch_t patch = patchset->patches[j]; + if (patch.offset + patch.count > sz) { + fprintf(stderr, "E: Patch %s[%d] outside file bounds\n", patchset->name, j); + goto done; + } + fseek(fp, patch.offset, SEEK_SET); + bool matches_from = true; + bool matches_to = true; + for (int i = 0; i < patch.count; i++) { + unsigned char seen; + if (!fread(&seen, 1, 1, fp)) { + matches_from = false; + matches_to = false; + break; + } + if (seen != patch.from[i]) matches_from = false; + if (seen != patch.to[i]) matches_to = false; + if (!(matches_from || matches_to)) break; + } + + printf("%s[%d]: ", patchset->name, j); + if (patchset->apply) { + if (matches_to) { + puts("Already applied"); + continue; + } + if (!matches_from) { + puts("From value missmatch! Ignoring"); + continue; + } + fseek(fp, patch.offset, SEEK_SET); + fwrite(patch.to, 1, patch.count, fp); + puts("Patch applied"); + } else { + if (matches_from) { + puts("Not applied"); + continue; + } + if (!matches_to) { + puts("We didn't perform this patch. Leaving patched"); + continue; + } + fseek(fp, patch.offset, SEEK_SET); + fwrite(patch.from, 1, patch.count, fp); + puts("Patch removed"); + } + } + } + +done: + fclose(fp); +} + +int main(int argc, char** argv) { + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argc > 0 ? argv[0] : "micepatch.exe"); + return -1; + } + + FILE* fp = fopen(argv[1], "r"); + if (fp == NULL) { + fprintf(stderr, "Failed to open '%s' (%d)\n", argv[1], errno); + return -1; + } + + fseek(fp, 0L, SEEK_END); + size_t sz = ftell(fp); + rewind(fp); + + char* json_buf = (char*)malloc(sz); + if (json_buf == NULL) { + fprintf(stderr, "Failed to allocate %d bytes\n", sz, errno); + fclose(fp); + return -1; + } + if (!(sz = fread(json_buf, 1, sz, fp))) { + fprintf(stderr, "Failed to read from '%s' (%d)\n", argv[1], errno); + fclose(fp); + return -1; + }; + fclose(fp); + + json_settings settings = {0}; + char error[json_error_max]; + json_value* parsed = json_parse_ex(&settings, json_buf, sz, error); + + if (parsed == NULL) { + fprintf(stderr, "Parse error: %s\n", error); + return -1; + } + + int loaded_patches = 0; + json_value** patches_json; + if (parsed->type == json_array) { + loaded_patches = parsed->u.array.length; + patches_json = parsed->u.array.values; + } else if (parsed->type == json_object) { + loaded_patches = 1; + patches_json = &parsed; + } else { + fprintf(stderr, "Patch file format error\n"); + json_value_free(parsed); + return; + } + + patches_t all_patches; + if (!parse_patches(&all_patches, patches_json, loaded_patches, error)) { + fprintf(stderr, "Patch file format error: %s\n", error); + return; + } + + puts(""); + puts("Loaded patches:"); + puts("---------------"); + print_patches(&all_patches); + + apply_patches(&all_patches, argv[2]); + + free_patches(&all_patches); + return 0; +} diff --git a/src/micetools/micepatch/meson.build b/src/micetools/micepatch/meson.build new file mode 100644 index 0000000..bee3926 --- /dev/null +++ b/src/micetools/micepatch/meson.build @@ -0,0 +1,15 @@ +executable( + 'micepatch', + win_subsystem: 'console', + sources: [ + 'main.c', + ], + link_with: [ + mice_lib, + json_lib, + ], + link_args: [ + '/MANIFEST:EMBED', + '/MANIFESTUAC:level="asInvoker" uiAccess="false"', + ] +) diff --git a/src/micetools/traces.c b/src/micetools/traces.c new file mode 100644 index 0000000..e69de29 diff --git a/src/system/billing.pub b/src/system/billing.pub new file mode 100644 index 0000000..b119750 Binary files /dev/null and b/src/system/billing.pub differ diff --git a/src/system/ca.crt b/src/system/ca.crt new file mode 100644 index 0000000..8c131b5 Binary files /dev/null and b/src/system/ca.crt differ diff --git a/src/tools/keys.py b/src/tools/keys.py new file mode 100644 index 0000000..392565a --- /dev/null +++ b/src/tools/keys.py @@ -0,0 +1,40 @@ +import json + +patches = [] +patchset = { + "name": "Rebind keys", + "description": "Automatically generated", + "apply": True, + "patches": patches +} + +original = ['W', 'E', 'D', 'C', 'X', 'Z', 'A', 'Q'] + +base = 1517787 +for i in range(8): + key = input(f"Key for {i+1}: ") + if len(key) != 1: + print("That's not one key!") + quit() + + key = f"{ord(key.upper()):02x}" + orig = f"{ord(original[i].upper()):02x}" + + if i == 0: + patches.append({ + "at": base, + "from": f"c70424{orig}000000", + "to": f"c70424{key}000000", + }) + else: + instr = f"c74424{4 * i:02x}" + patches.append({ + "at": base + 7 + 8 * (i - 1), + "from": instr + orig + "000000", + "to": instr + key + "000000", + }) + +with open("keybinds.json", "w") as f: + json.dump([patchset], f) + +print("Wrote patches to keybinds.json") diff --git a/subprojects/inih_dep/LICENSE.txt b/subprojects/inih_dep/LICENSE.txt new file mode 100644 index 0000000..cb7ee2d --- /dev/null +++ b/subprojects/inih_dep/LICENSE.txt @@ -0,0 +1,27 @@ + +The "inih" library is distributed under the New BSD license: + +Copyright (c) 2009, Ben Hoyt +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Ben Hoyt nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/subprojects/inih_dep/README.md b/subprojects/inih_dep/README.md new file mode 100644 index 0000000..d99d421 --- /dev/null +++ b/subprojects/inih_dep/README.md @@ -0,0 +1,159 @@ +# inih (INI Not Invented Here) + +[![Tests](https://github.com/benhoyt/inih/actions/workflows/tests.yml/badge.svg)](https://github.com/benhoyt/inih/actions/workflows/tests.yml) + +**inih (INI Not Invented Here)** is a simple [.INI file](http://en.wikipedia.org/wiki/INI_file) parser written in C. It's only a couple of pages of code, and it was designed to be _small and simple_, so it's good for embedded systems. It's also more or less compatible with Python's [ConfigParser](http://docs.python.org/library/configparser.html) style of .INI files, including RFC 822-style multi-line syntax and `name: value` entries. + +To use it, just give `ini_parse()` an INI file, and it will call a callback for every `name=value` pair parsed, giving you strings for the section, name, and value. It's done this way ("SAX style") because it works well on low-memory embedded systems, but also because it makes for a KISS implementation. + +You can also call `ini_parse_file()` to parse directly from a `FILE*` object, `ini_parse_string()` to parse data from a string, or `ini_parse_stream()` to parse using a custom fgets-style reader function for custom I/O. + +Download a release, browse the source, or read about [how to use inih in a DRY style](http://blog.brush.co.nz/2009/08/xmacros/) with X-Macros. + + +## Compile-time options ## + +You can control various aspects of inih using preprocessor defines: + +### Syntax options ### + + * **Multi-line entries:** By default, inih supports multi-line entries in the style of Python's ConfigParser. To disable, add `-DINI_ALLOW_MULTILINE=0`. + * **UTF-8 BOM:** By default, inih allows a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of INI files. To disable, add `-DINI_ALLOW_BOM=0`. + * **Inline comments:** By default, inih allows inline comments with the `;` character. To disable, add `-DINI_ALLOW_INLINE_COMMENTS=0`. You can also specify which character(s) start an inline comment using `INI_INLINE_COMMENT_PREFIXES`. + * **Start-of-line comments:** By default, inih allows both `;` and `#` to start a comment at the beginning of a line. You can override this by changing `INI_START_COMMENT_PREFIXES`. + * **Allow no value:** By default, inih treats a name with no value (no `=` or `:` on the line) as an error. To allow names with no values, add `-DINI_ALLOW_NO_VALUE=1`, and inih will call your handler function with value set to NULL. + +### Parsing options ### + + * **Stop on first error:** By default, inih keeps parsing the rest of the file after an error. To stop parsing on the first error, add `-DINI_STOP_ON_FIRST_ERROR=1`. + * **Report line numbers:** By default, the `ini_handler` callback doesn't receive the line number as a parameter. If you need that, add `-DINI_HANDLER_LINENO=1`. + * **Call handler on new section:** By default, inih only calls the handler on each `name=value` pair. To detect new sections (e.g., the INI file has multiple sections with the same name), add `-DINI_CALL_HANDLER_ON_NEW_SECTION=1`. Your handler function will then be called each time a new section is encountered, with `section` set to the new section name but `name` and `value` set to NULL. + +### Memory options ### + + * **Stack vs heap:** By default, inih creates a fixed-sized line buffer on the stack. To allocate on the heap using `malloc` instead, specify `-DINI_USE_STACK=0`. + * **Maximum line length:** The default maximum line length (for stack or heap) is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`. Note that `INI_MAX_LINE` must be 3 more than the longest line (due to `\r`, `\n`, and the NUL). + * **Initial malloc size:** `INI_INITIAL_ALLOC` specifies the initial malloc size when using the heap. It defaults to 200 bytes. + * **Allow realloc:** By default when using the heap (`-DINI_USE_STACK=0`), inih allocates a fixed-sized buffer of `INI_INITIAL_ALLOC` bytes. To allow this to grow to `INI_MAX_LINE` bytes, doubling if needed, set `-DINI_ALLOW_REALLOC=1`. + * **Custom allocator:** By default when using the heap, the standard library's `malloc`, `free`, and `realloc` functions are used; to use a custom allocator, specify `-DINI_CUSTOM_ALLOCATOR=1` (and `-DINI_USE_STACK=0`). You must define and link functions named `ini_malloc`, `ini_free`, and (if `INI_ALLOW_REALLOC` is set) `ini_realloc`, which must have the same signatures as the `stdlib.h` memory allocation functions. + +## Simple example in C ## + +```c +#include +#include +#include +#include "../ini.h" + +typedef struct +{ + int version; + const char* name; + const char* email; +} configuration; + +static int handler(void* user, const char* section, const char* name, + const char* value) +{ + configuration* pconfig = (configuration*)user; + + #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + if (MATCH("protocol", "version")) { + pconfig->version = atoi(value); + } else if (MATCH("user", "name")) { + pconfig->name = strdup(value); + } else if (MATCH("user", "email")) { + pconfig->email = strdup(value); + } else { + return 0; /* unknown section/name, error */ + } + return 1; +} + +int main(int argc, char* argv[]) +{ + configuration config; + + if (ini_parse("test.ini", handler, &config) < 0) { + printf("Can't load 'test.ini'\n"); + return 1; + } + printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n", + config.version, config.name, config.email); + return 0; +} +``` + + +## C++ example ## + +If you're into C++ and the STL, there is also an easy-to-use [INIReader class](https://github.com/benhoyt/inih/blob/master/cpp/INIReader.h) that stores values in a `map` and lets you `Get()` them: + +```cpp +#include +#include "INIReader.h" + +int main() +{ + INIReader reader("../examples/test.ini"); + + if (reader.ParseError() < 0) { + std::cout << "Can't load 'test.ini'\n"; + return 1; + } + std::cout << "Config loaded from 'test.ini': version=" + << reader.GetInteger("protocol", "version", -1) << ", name=" + << reader.Get("user", "name", "UNKNOWN") << ", email=" + << reader.Get("user", "email", "UNKNOWN") << ", pi=" + << reader.GetReal("user", "pi", -1) << ", active=" + << reader.GetBoolean("user", "active", true) << "\n"; + return 0; +} +``` + +This simple C++ API works fine, but it's not very fully-fledged. I'm not planning to work more on the C++ API at the moment, so if you want a bit more power (for example `GetSections()` and `GetFields()` functions), see these forks: + + * https://github.com/Blandinium/inih + * https://github.com/OSSystems/inih + + +## Differences from ConfigParser ## + +Some differences between inih and Python's [ConfigParser](http://docs.python.org/library/configparser.html) standard library module: + +* INI name=value pairs given above any section headers are treated as valid items with no section (section name is an empty string). In ConfigParser having no section is an error. +* Line continuations are handled with leading whitespace on continued lines (like ConfigParser). However, instead of concatenating continued lines together, they are treated as separate values for the same key (unlike ConfigParser). + + +## Platform-specific notes ## + +* Windows/Win32 uses UTF-16 filenames natively, so to handle Unicode paths you need to call `_wfopen()` to open a file and then `ini_parse_file()` to parse it; inih does not include `wchar_t` or Unicode handling. + +## Meson notes ## + +* The `meson.build` file is not required to use or compile inih, its main purpose is for distributions. +* By default Meson is set up for distro installation, but this behavior can be configured for embedded use cases: + * with `-Ddefault_library=static` static libraries are built. + * with `-Ddistro_install=false` libraries, headers and pkg-config files won't be installed. + * with `-Dwith_INIReader=false` you can disable building the C++ library. +* All compile-time options are implemented in Meson as well, you can take a look at [meson_options.txt](https://github.com/benhoyt/inih/blob/master/meson_options.txt) for their definition. These won't work if `distro_install` is set to `true`. +* If you want to use inih for programs which may be shipped in a distro, consider linking against the shared libraries. The pkg-config entries are `inih` and `INIReader`. +* In case you use inih as a Meson subproject, you can use the `inih_dep` and `INIReader_dep` dependency variables. You might want to set `default_library=static` and `distro_install=false` for the subproject. An official Wrap is provided on [WrapDB](https://wrapdb.mesonbuild.com/inih). +* For packagers: if you want to tag the version in the pkg-config file, you will need to do this downstream. Add `version : '',` after the `license` tag in the `project()` function and `version : meson.project_version(),` after the `soversion` tag in both `library()` functions. + +## Building from vcpkg ## + +You can build and install inih using [vcpkg](https://github.com/microsoft/vcpkg/) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + ./vcpkg install inih + +The inih port in vcpkg is kept up to date by microsoft team members and community contributors. +If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +## Related links ## + +* [Conan package for inih](https://github.com/mohamedghita/conan-inih) (Conan is a C/C++ package manager) diff --git a/subprojects/inih_dep/ini.c b/subprojects/inih_dep/ini.c new file mode 100644 index 0000000..f8a3ea3 --- /dev/null +++ b/subprojects/inih_dep/ini.c @@ -0,0 +1,298 @@ +/* inih -- simple .INI file parser + +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#if INI_CUSTOM_ALLOCATOR +#include +void* ini_malloc(size_t size); +void ini_free(void* ptr); +void* ini_realloc(void* ptr, size_t size); +#else +#include +#define ini_malloc malloc +#define ini_free free +#define ini_realloc realloc +#endif +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Used by ini_parse_string() to keep track of string parsing state. */ +typedef struct { + const char* ptr; + size_t num_left; +} ini_parse_string_ctx; + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char (of chars) or inline comment in given string, + or pointer to NUL at end of string if neither found. Inline comment must + be prefixed by a whitespace character to register as a comment. */ +static char* find_chars_or_comment(const char* s, const char* chars) +{ +#if INI_ALLOW_INLINE_COMMENTS + int was_space = 0; + while (*s && (!chars || !strchr(chars, *s)) && + !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { + was_space = isspace((unsigned char)(*s)); + s++; + } +#else + while (*s && (!chars || !strchr(chars, *s))) { + s++; + } +#endif + return (char*)s; +} + +/* Similar to strncpy, but ensures dest (size bytes) is + NUL-terminated, and doesn't pad with NULs. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ + size_t i; + for (i = 0; i < size - 1 && src[i]; i++) + dest[i] = src[i]; + dest[i] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; + int max_line = INI_MAX_LINE; +#else + char* line; + size_t max_line = INI_INITIAL_ALLOC; +#endif +#if INI_ALLOW_REALLOC && !INI_USE_STACK + char* new_line; + size_t offset; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)ini_malloc(INI_INITIAL_ALLOC); + if (!line) { + return -2; + } +#endif + +#if INI_HANDLER_LINENO +#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) +#else +#define HANDLER(u, s, n, v) handler(u, s, n, v) +#endif + + /* Scan through stream line by line */ + while (reader(line, (int)max_line, stream) != NULL) { +#if INI_ALLOW_REALLOC && !INI_USE_STACK + offset = strlen(line); + while (offset == max_line - 1 && line[offset - 1] != '\n') { + max_line *= 2; + if (max_line > INI_MAX_LINE) + max_line = INI_MAX_LINE; + new_line = ini_realloc(line, max_line); + if (!new_line) { + ini_free(line); + return -2; + } + line = new_line; + if (reader(line + offset, (int)(max_line - offset), stream) == NULL) + break; + if (max_line >= INI_MAX_LINE) + break; + offset += strlen(line + offset); + } +#endif + + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (strchr(INI_START_COMMENT_PREFIXES, *start)) { + /* Start-of-line comment */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-blank line with leading whitespace, treat as continuation + of previous name's value (as per Python configparser). */ + if (!HANDLER(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_chars_or_comment(start + 1, "]"); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; +#if INI_CALL_HANDLER_ON_NEW_SECTION + if (!HANDLER(user, section, NULL, NULL) && !error) + error = lineno; +#endif + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start) { + /* Not a comment, must be a name[=:]value pair */ + end = find_chars_or_comment(start, "=:"); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = end + 1; +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(value, NULL); + if (*end) + *end = '\0'; +#endif + value = lskip(value); + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!HANDLER(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ +#if INI_ALLOW_NO_VALUE + *end = '\0'; + name = rstrip(start); + if (!HANDLER(user, section, name, NULL) && !error) + error = lineno; +#else + error = lineno; +#endif + } + } + +#if INI_STOP_ON_FIRST_ERROR + if (error) + break; +#endif + } + +#if !INI_USE_STACK + ini_free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, ini_handler handler, void* user) +{ + return ini_parse_stream((ini_reader)fgets, file, handler, user); +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, ini_handler handler, void* user) +{ + FILE* file; + int error; + + file = fopen(filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} + +/* An ini_reader function to read the next line from a string buffer. This + is the fgets() equivalent used by ini_parse_string(). */ +static char* ini_reader_string(char* str, int num, void* stream) { + ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; + const char* ctx_ptr = ctx->ptr; + size_t ctx_num_left = ctx->num_left; + char* strp = str; + char c; + + if (ctx_num_left == 0 || num < 2) + return NULL; + + while (num > 1 && ctx_num_left != 0) { + c = *ctx_ptr++; + ctx_num_left--; + *strp++ = c; + if (c == '\n') + break; + num--; + } + + *strp = '\0'; + ctx->ptr = ctx_ptr; + ctx->num_left = ctx_num_left; + return str; +} + +/* See documentation in header file. */ +int ini_parse_string(const char* string, ini_handler handler, void* user) { + ini_parse_string_ctx ctx; + + ctx.ptr = string; + ctx.num_left = strlen(string); + return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, + user); +} diff --git a/subprojects/inih_dep/ini.h b/subprojects/inih_dep/ini.h new file mode 100644 index 0000000..d1a2ba8 --- /dev/null +++ b/subprojects/inih_dep/ini.h @@ -0,0 +1,178 @@ +/* inih -- simple .INI file parser + +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef INI_H +#define INI_H + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Nonzero if ini_handler callback should accept lineno parameter. */ +#ifndef INI_HANDLER_LINENO +#define INI_HANDLER_LINENO 0 +#endif + +/* Visibility symbols, required for Windows DLLs */ +#ifndef INI_API +#if defined _WIN32 || defined __CYGWIN__ +# ifdef INI_SHARED_LIB +# ifdef INI_SHARED_LIB_BUILDING +# define INI_API __declspec(dllexport) +# else +# define INI_API __declspec(dllimport) +# endif +# else +# define INI_API +# endif +#else +# if defined(__GNUC__) && __GNUC__ >= 4 +# define INI_API __attribute__ ((visibility ("default"))) +# else +# define INI_API +# endif +#endif +#endif + +/* Typedef for prototype of handler function. */ +#if INI_HANDLER_LINENO +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value, + int lineno); +#else +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value); +#endif + +/* Typedef for prototype of fgets-style reader function. */ +typedef char* (*ini_reader)(char* str, int num, void* stream); + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's configparser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +INI_API int ini_parse(const char* filename, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +INI_API int ini_parse_file(FILE* file, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes an ini_reader function pointer instead of + filename. Used for implementing custom or string-based I/O (see also + ini_parse_string). */ +INI_API int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user); + +/* Same as ini_parse(), but takes a zero-terminated string with the INI data +instead of a file. Useful for parsing INI data from a network socket or +already in memory. */ +INI_API int ini_parse_string(const char* string, ini_handler handler, void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + configparser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See https://github.com/benhoyt/inih/issues/21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Chars that begin a start-of-line comment. Per Python configparser, allow + both ; and # comments at the start of a line by default. */ +#ifndef INI_START_COMMENT_PREFIXES +#define INI_START_COMMENT_PREFIXES ";#" +#endif + +/* Nonzero to allow inline comments (with valid inline comment characters + specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match + Python 3.2+ configparser behaviour. */ +#ifndef INI_ALLOW_INLINE_COMMENTS +#define INI_ALLOW_INLINE_COMMENTS 1 +#endif +#ifndef INI_INLINE_COMMENT_PREFIXES +#define INI_INLINE_COMMENT_PREFIXES ";" +#endif + +/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file (stack or heap). Note that + this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +/* Nonzero to allow heap line buffer to grow via realloc(), zero for a + fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is + zero. */ +#ifndef INI_ALLOW_REALLOC +#define INI_ALLOW_REALLOC 0 +#endif + +/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK + is zero. */ +#ifndef INI_INITIAL_ALLOC +#define INI_INITIAL_ALLOC 200 +#endif + +/* Stop parsing on first error (default is to keep parsing). */ +#ifndef INI_STOP_ON_FIRST_ERROR +#define INI_STOP_ON_FIRST_ERROR 0 +#endif + +/* Nonzero to call the handler at the start of each new section (with + name and value NULL). Default is to only call the handler on + each name=value pair. */ +#ifndef INI_CALL_HANDLER_ON_NEW_SECTION +#define INI_CALL_HANDLER_ON_NEW_SECTION 0 +#endif + +/* Nonzero to allow a name without a value (no '=' or ':' on the line) and + call the handler with value NULL in this case. Default is to treat + no-value lines as an error. */ +#ifndef INI_ALLOW_NO_VALUE +#define INI_ALLOW_NO_VALUE 0 +#endif + +/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory + allocation functions (INI_USE_STACK must also be 0). These functions must + have the same signatures as malloc/free/realloc and behave in a similar + way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ +#ifndef INI_CUSTOM_ALLOCATOR +#define INI_CUSTOM_ALLOCATOR 0 +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* INI_H */ diff --git a/subprojects/inih_dep/meson.build b/subprojects/inih_dep/meson.build new file mode 100644 index 0000000..4f16b33 --- /dev/null +++ b/subprojects/inih_dep/meson.build @@ -0,0 +1,96 @@ +project('inih', + ['c'], + license : 'BSD-3-Clause', + version : '55', +) + +#### options #### +arg_static = [] +distro_install = get_option('distro_install') +extra_args = [] + +if distro_install + pkg = import('pkgconfig') +else + if not get_option('multi-line_entries') + arg_static += ['-DINI_ALLOW_MULTILINE=0'] + endif + if not get_option('utf-8_bom') + arg_static += ['-DINI_ALLOW_BOM=0'] + endif + if not get_option('inline_comments') + arg_static += ['-DINI_ALLOW_INLINE_COMMENTS=0'] + endif + inline_comment_prefix = get_option('inline_comment_prefix') + if inline_comment_prefix != ';' + arg_static += ['-DINI_INLINE_COMMENT_PREFIXES="' + inline_comment_prefix + '"'] + endif + sol_comment_prefix = get_option('start-of-line_comment_prefix') + if sol_comment_prefix != ';#' + arg_static += ['-DINI_START_COMMENT_PREFIXES="' + start-of-line_comment_prefix + '"'] + endif + if get_option('allow_no_value') + arg_static += ['-DINI_ALLOW_NO_VALUE=1'] + endif + if get_option('stop_on_first_error') + arg_static += ['-DINI_STOP_ON_FIRST_ERROR=1'] + endif + if get_option('report_line_numbers') + arg_static += ['-DINI_HANDLER_LINENO=1'] + endif + if get_option('call_handler_on_new_section') + arg_static += ['-DINI_CALL_HANDLER_ON_NEW_SECTION=1'] + endif + if get_option('use_heap') + arg_static += ['-DINI_USE_STACK=0'] + endif + max_line_length = get_option('max_line_length') + if max_line_length != 200 + arg_static += ['-DINI_MAX_LINE=' + max_line_length.to_string()] + endif + initial_malloc_size = get_option('initial_malloc_size') + if initial_malloc_size != 200 + arg_static += ['-DINI_INITIAL_ALLOC=' + initial_malloc_size.to_string()] + endif + if get_option('allow_realloc') + arg_static += ['-DINI_ALLOW_REALLOC=1'] + endif +endif + +if host_machine.system() == 'windows' + lib = get_option('default_library') + if lib == 'both' + error('default_library=both is not supported on Windows') + elif lib == 'shared' + extra_args += '-DINI_SHARED_LIB' + add_project_arguments('-DINI_SHARED_LIB_BUILDING', language: ['c']) + endif +endif + +#### inih #### +inc_inih = include_directories('.') + +lib_inih = static_library('inih', + ['ini.c'], + include_directories : inc_inih, + c_args : [arg_static, extra_args], + install : distro_install, + # soversion : '0', + gnu_symbol_visibility: 'hidden' +) + +if distro_install + install_headers('ini.h') + + pkg.generate(lib_inih, + name : 'inih', + description : 'simple .INI file parser', + extra_cflags : extra_args, + ) +endif + +inih_dep = declare_dependency( + link_with : lib_inih, + compile_args : arg_static + extra_args, + include_directories : inc_inih +) diff --git a/subprojects/inih_dep/meson_options.txt b/subprojects/inih_dep/meson_options.txt new file mode 100644 index 0000000..0b3aa7e --- /dev/null +++ b/subprojects/inih_dep/meson_options.txt @@ -0,0 +1,70 @@ +option('distro_install', + type : 'boolean', + value : true, + description : 'install shared libs, headers and pkg-config entries' +) +option('multi-line_entries', + type : 'boolean', + value : true, + description : 'support for multi-line entries in the style of Python\'s ConfigParser' +) +option('utf-8_bom', + type : 'boolean', + value : true, + description : 'allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of INI files' +) +option('inline_comments', + type : 'boolean', + value : true, + description : 'allow inline comments with the comment prefix character' +) +option('inline_comment_prefix', + type : 'string', + value : ';', + description : 'character(s) to start an inline comment (if enabled)' +) +option('start-of-line_comment_prefix', + type : 'string', + value : ';#', + description : 'character(s) to start a comment at the beginning of a line' +) +option('allow_no_value', + type : 'boolean', + value : false, + description : 'allow name with no value' +) +option('stop_on_first_error', + type : 'boolean', + value : false, + description : 'stop parsing after an error' +) +option('report_line_numbers', + type : 'boolean', + value : false, + description : 'report line number on ini_handler callback' +) +option('call_handler_on_new_section', + type : 'boolean', + value : false, + description : 'call the handler each time a new section is encountered' +) +option('use_heap', + type : 'boolean', + value : false, + description : 'allocate memory on the heap using malloc instead using a fixed-sized line buffer on the stack' +) +option('max_line_length', + type : 'integer', + value : 200, + description : 'maximum line length in bytes' +) +option('initial_malloc_size', + type : 'integer', + value : 200, + description : 'initial malloc size in bytes (when using the heap)' +) +option('allow_realloc', + type : 'boolean', + value : false, + description : 'allow initial malloc size to grow to max line length (when using the heap)' +)