diff --git a/common/hooklib/path.c b/common/hooklib/path.c index 0480e77..3e43fed 100644 --- a/common/hooklib/path.c +++ b/common/hooklib/path.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -11,6 +12,8 @@ #include "hooklib/path.h" +#include +#include #include /* Helpers */ @@ -208,6 +211,16 @@ static BOOL WINAPI hook_GetDiskFreeSpaceW( LPDWORD lpTotalNumberOfClusters ); +static BOOL WINAPI hook_DeviceIoControl( + HANDLE hFile, + uint32_t dwIoControlCode, + void *lpInBuffer, + uint32_t nInBufferSize, + void *lpOutBuffer, + uint32_t nOutBufferSize, + uint32_t *lpBytesReturned, + OVERLAPPED *lpOverlapped); + /* Link pointers */ static BOOL (WINAPI *next_CreateDirectoryA)( @@ -411,6 +424,16 @@ static BOOL (WINAPI *next_GetDiskFreeSpaceW)( LPDWORD lpTotalNumberOfClusters ); +static BOOL (WINAPI *next_DeviceIoControl)( + HANDLE hFile, + uint32_t dwIoControlCode, + void *lpInBuffer, + uint32_t nInBufferSize, + void *lpOutBuffer, + uint32_t nOutBufferSize, + uint32_t *lpBytesReturned, + OVERLAPPED *lpOverlapped); + /* Hook table */ static const struct hook_symbol path_hook_syms[] = { @@ -558,6 +581,10 @@ static const struct hook_symbol path_hook_syms[] = { .name = "GetDiskFreeSpaceW", .patch = hook_GetDiskFreeSpaceW, .link = (void **) &next_GetDiskFreeSpaceW, + }, { + .name = "DeviceIoControl", + .patch = hook_DeviceIoControl, + .link = (void **) &next_DeviceIoControl, } }; @@ -1764,6 +1791,65 @@ static BOOL WINAPI hook_GetDiskFreeSpaceW( return result; } +static BOOL WINAPI hook_DeviceIoControl( + HANDLE hFile, + uint32_t dwIoControlCode, + void *lpInBuffer, + uint32_t nInBufferSize, + void *lpOutBuffer, + uint32_t nOutBufferSize, + uint32_t *lpBytesReturned, + OVERLAPPED *lpOverlapped) { + + if (dwIoControlCode != FSCTL_SET_REPARSE_POINT) { + return next_DeviceIoControl(hFile, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped); + } + + if (hFile == INVALID_HANDLE_VALUE || nInBufferSize == 0 || nInBufferSize > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + REPARSE_DATA_BUFFER* mount = lpInBuffer; + + wchar_t *trans; + wchar_t *src; + + if (wcsstr(mount->SymbolicLinkReparseBuffer.PathBuffer, L"?\\") != NULL) { // strip prefix + src = mount->SymbolicLinkReparseBuffer.PathBuffer + 2; + } else { + src = mount->SymbolicLinkReparseBuffer.PathBuffer; + } + + BOOL ok = path_transform_w(&trans, src); + + if (!ok || trans == NULL) { + return next_DeviceIoControl(hFile, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped); + } + + wchar_t tmp[MAX_PATH]; + swprintf_s(tmp, MAX_PATH, L"\\??\\%ls", trans); + + size_t pathLen = wcslen(tmp); + size_t pathByteLen = pathLen * sizeof(wchar_t); + + REPARSE_DATA_BUFFER* newMount = malloc(sizeof(REPARSE_DATA_BUFFER) + pathByteLen); + newMount->Reserved = mount->Reserved; + newMount->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + newMount->ReparseDataLength = 12 + pathByteLen; + newMount->MountPointReparseBuffer.SubstituteNameOffset = 0; + newMount->MountPointReparseBuffer.SubstituteNameLength = pathByteLen; + newMount->MountPointReparseBuffer.PrintNameOffset = pathByteLen + 2; + newMount->MountPointReparseBuffer.PrintNameLength = 0; + memcpy(newMount->MountPointReparseBuffer.PathBuffer, tmp, pathByteLen); + + BOOL result = next_DeviceIoControl(hFile, dwIoControlCode, newMount, 8 + 12 + pathByteLen, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped); + + free(trans); + + return result; +} + char** str_split_a(char* a_str, const char a_delim) { char** result = 0; size_t count = 0; diff --git a/dist/apm3/launch.bat b/dist/apm3/launch.bat index 7b153e4..14e1dbe 100644 --- a/dist/apm3/launch.bat +++ b/dist/apm3/launch.bat @@ -14,12 +14,23 @@ if exist %tmp%\SequenceSetting.json ( del %tmp%\SequenceSetting.json ) +rem detect model and dipswitch for server/client + +set AMDAEMON_CONFIG=standalone.json +inject -w -k apm3hook.dll hwvalue.exe modeltype +if %ERRORLEVEL% == 5 goto BEGIN + +inject -w -k apm3hook.dll hwvalue.exe dipsw +set AMDAEMON_CONFIG=server.json +if %ERRORLEVEL% == 1 set AMDAEMON_CONFIG=client.json + :BEGIN + pushd %~dp0 set DOORSTOP_DISABLE=TRUE qprocess amdaemon.exe > NUL -IF %ERRORLEVEL% NEQ 0 start /min "AM Daemon" inject -d -k apm3hook.dll amdaemon.exe -c daemon_config\common.json daemon_config\server.json config_hook.json +IF %ERRORLEVEL% NEQ 0 start /min "AM Daemon" inject -d -k apm3hook.dll amdaemon.exe -c daemon_config\common.json daemon_config\%AMDAEMON_CONFIG% config_hook.json set DOORSTOP_DISABLE= REM Add "-screen-fullscreen 0 -popupWindow" if you want to run in windowed mode @@ -44,12 +55,18 @@ goto END :APP +pushd C:\ call W:\game.bat +popd + taskkill /f /im emoneyUI.exe > nul 2>&1 goto BEGIN :APPTEST +pushd C:\ call W:\gametest.bat +popd + goto BEGIN :END diff --git a/dist/apm3/segatools.ini b/dist/apm3/segatools.ini index 2df4cc9..d607322 100644 --- a/dist/apm3/segatools.ini +++ b/dist/apm3/segatools.ini @@ -238,6 +238,9 @@ deviceName= start=9 home=10 +service=11 +test=12 + button1=1 button2=4 button3=6 @@ -246,3 +249,10 @@ button5=2 button6=3 button7=8 button8=7 + +; Additional directional keys. These only have to be set if your +; controller reports them as buttons rather than a hat switch. +up=13 +right=14 +down=15 +left=16 diff --git a/games/apm3hook/mount.c b/games/apm3hook/mount.c index c1b855e..5a9533a 100644 --- a/games/apm3hook/mount.c +++ b/games/apm3hook/mount.c @@ -114,7 +114,7 @@ bool hook_ApmSys_mountVhd(const wchar_t* vhdOriginalPath, const wchar_t* vhdPatc // Game drive wchar_t device[3]; - device[0] = mountDriveLetter; + device[0] = (unsigned char) mountDriveLetter; device[1] = ':'; device[2] = '\0'; @@ -136,9 +136,19 @@ bool hook_ApmSys_mountVhd(const wchar_t* vhdOriginalPath, const wchar_t* vhdPatc return 1; } + // Appdata drive + device[0] = 'Y'; + + dprintf("Mount: Mapping %ls to %ls\n", device, vcfg->appdata); + if (!DefineDosDeviceW(0, device, vcfg->appdata)) { + dprintf("DefineDosDevice failed: %lx\n", GetLastError()); + return 1; + } + if (mcfg->delay) { Sleep(1500); } + return 0; } bool hook_ApmSys_unmountVhd(char mountDriveLetter) { @@ -161,12 +171,20 @@ bool hook_ApmSys_unmountVhd(char mountDriveLetter) { dprintf("DefineDosDevice failed: %lx\n", GetLastError()); return 1; } + + // Appdata drive + device[0] = 'Y'; + if (!DefineDosDevice(DDD_REMOVE_DEFINITION, device, NULL)) { + dprintf("DefineDosDevice failed: %lx\n", GetLastError()); + return 1; + } return 0; } void CALLBACK UnmountApmDrives(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) { hook_ApmSys_unmountVhd('W'); hook_ApmSys_unmountVhd('X'); + hook_ApmSys_unmountVhd('Y'); } bool hook_ApmSys_unmountFscrypt(const wchar_t* mountFolderPath) { diff --git a/games/apm3io/config.c b/games/apm3io/config.c index c487dce..ae2d47c 100644 --- a/games/apm3io/config.c +++ b/games/apm3io/config.c @@ -47,6 +47,14 @@ void apm3_di_config_load(struct apm3_di_config *cfg, const wchar_t *filename) _countof(cfg->device_name), filename); + cfg->test = GetPrivateProfileIntW(L"dinput", L"test", 0, filename); + cfg->service = GetPrivateProfileIntW(L"dinput", L"service", 0, filename); + + cfg->up = GetPrivateProfileIntW(L"dinput", L"up", 0, filename); + cfg->right = GetPrivateProfileIntW(L"dinput", L"right", 0, filename); + cfg->down = GetPrivateProfileIntW(L"dinput", L"down", 0, filename); + cfg->left = GetPrivateProfileIntW(L"dinput", L"left", 0, filename); + cfg->home = GetPrivateProfileIntW(L"dinput", L"home", 0, filename); cfg->start = GetPrivateProfileIntW(L"dinput", L"start", 0, filename); diff --git a/games/apm3io/config.h b/games/apm3io/config.h index 72b3a13..a6d4884 100644 --- a/games/apm3io/config.h +++ b/games/apm3io/config.h @@ -8,9 +8,15 @@ struct apm3_di_config { wchar_t device_name[64]; + uint8_t test; + uint8_t service; uint8_t start; uint8_t home; uint8_t button[8]; + uint8_t up; + uint8_t right; + uint8_t down; + uint8_t left; }; struct apm3_xi_config { diff --git a/games/apm3io/di.c b/games/apm3io/di.c index 29bac6e..4c8de32 100644 --- a/games/apm3io/di.c +++ b/games/apm3io/di.c @@ -21,11 +21,13 @@ static BOOL CALLBACK apm3_di_enum_callback( const DIDEVICEINSTANCEW *dev, void *ctx); static void apm3_di_get_gamebtns(uint16_t *gamebtn_out); +static void apm3_di_get_opbtns(uint8_t *opbtn_out); static uint8_t apm3_di_decode_pov(DWORD pov); static const struct apm3_io_backend apm3_di_backend = { .get_gamebtns = apm3_di_get_gamebtns, + .get_opbtns = apm3_di_get_opbtns }; static HWND apm3_di_wnd; @@ -34,8 +36,16 @@ static IDirectInputDevice8W *apm3_di_dev; static IDirectInputEffect *apm3_di_fx; static uint8_t apm3_di_home; static uint8_t apm3_di_start; +static uint8_t apm3_di_service; +static uint8_t apm3_di_test; +static uint8_t apm3_di_extra_up; +static uint8_t apm3_di_extra_right; +static uint8_t apm3_di_extra_down; +static uint8_t apm3_di_extra_left; static uint8_t apm3_di_button[APM3_BUTTON_COUNT]; +static union apm3_di_state state = {0}; + HRESULT apm3_di_init( const struct apm3_di_config *cfg, HINSTANCE inst, @@ -136,6 +146,8 @@ static HRESULT apm3_di_config_apply(const struct apm3_di_config *cfg) cfg->device_name); dprintf("Stick: Home button . . . : %i\n", cfg->home); dprintf("Stick: Start button . . . : %i\n", cfg->start); + dprintf("Stick: Service button . . : %i\n", cfg->service); + dprintf("Stick: Test button . . . : %i\n", cfg->test); /* Print the configuration for all 8 buttons */ for (i = 0; i < APM3_BUTTON_COUNT; i++) { @@ -146,7 +158,14 @@ static HRESULT apm3_di_config_apply(const struct apm3_di_config *cfg) apm3_di_start = cfg->start; apm3_di_home = cfg->home; - + apm3_di_service = cfg->service; + apm3_di_test = cfg->test; + + apm3_di_extra_up = cfg->up; + apm3_di_extra_right = cfg->right; + apm3_di_extra_down = cfg->down; + apm3_di_extra_left = cfg->left; + for (i = 0; i < APM3_BUTTON_COUNT; i++) { apm3_di_button[i] = cfg->button[i]; } @@ -184,7 +203,6 @@ static BOOL CALLBACK apm3_di_enum_callback( static void apm3_di_get_gamebtns(uint16_t *gamebtn_out) { - union apm3_di_state state; uint16_t gamebtn; HRESULT hr; @@ -198,6 +216,22 @@ static void apm3_di_get_gamebtns(uint16_t *gamebtn_out) gamebtn = apm3_di_decode_pov(state.st.rgdwPOV[0]); + if (apm3_di_extra_up && state.st.rgbButtons[apm3_di_extra_up - 1]) { + gamebtn |= APM3_IO_GAMEBTN_UP; + } + + if (apm3_di_extra_right && state.st.rgbButtons[apm3_di_extra_right - 1]) { + gamebtn |= APM3_IO_GAMEBTN_RIGHT; + } + + if (apm3_di_extra_down && state.st.rgbButtons[apm3_di_extra_down - 1]) { + gamebtn |= APM3_IO_GAMEBTN_DOWN; + } + + if (apm3_di_extra_left && state.st.rgbButtons[apm3_di_extra_left - 1]) { + gamebtn |= APM3_IO_GAMEBTN_LEFT; + } + if (apm3_di_start && state.st.rgbButtons[apm3_di_start - 1]) { gamebtn |= APM3_IO_GAMEBTN_START; } @@ -255,3 +289,17 @@ static uint8_t apm3_di_decode_pov(DWORD pov) default: return 0; } } + +static void apm3_di_get_opbtns(uint8_t *opbtn_out) { + + uint8_t opbtn = 0; + + if (apm3_di_service && state.st.rgbButtons[apm3_di_service - 1]) { + opbtn |= APM3_IO_OPBTN_SERVICE; + } + if (apm3_di_test && state.st.rgbButtons[apm3_di_test - 1]) { + opbtn |= APM3_IO_OPBTN_TEST; + } + + *opbtn_out = opbtn; +} \ No newline at end of file diff --git a/games/apm3io/dllmain.c b/games/apm3io/dllmain.c index bf4dff9..4b7922b 100644 --- a/games/apm3io/dllmain.c +++ b/games/apm3io/dllmain.c @@ -83,6 +83,10 @@ void apm3_io_get_opbtns(uint8_t *opbtn_out) apm3_io_coin = false; } + if (apm3_io_backend->get_opbtns != NULL) { + apm3_io_backend->get_opbtns(&opbtn); + } + *opbtn_out = opbtn; } diff --git a/subprojects/capnhook.wrap b/subprojects/capnhook.wrap index b862df9..3293316 100644 --- a/subprojects/capnhook.wrap +++ b/subprojects/capnhook.wrap @@ -1,4 +1,4 @@ [wrap-git] directory = capnhook url = https://gitea.tendokyu.moe/TeamTofuShop/capnhook -revision = 35169a505788ad414f831fd707fffd0b6d025477 +revision = afece371fb046cf9c540512663c02fa77242d12c