Ex-Board support
- Based on work by zxmarcos - Requires clean-up - Requires button injection from TPUI - Requires AH30 and Suggoi Arcana Heart 2 fixing (does not boot and/or crashes) - Daemon Bride boots ok
This commit is contained in:
parent
5b70d00348
commit
574f456460
480
OpenParrot/src/Functions/Games/Ex-Board/Ex-BoardGeneric.cpp
Normal file
480
OpenParrot/src/Functions/Games/Ex-Board/Ex-BoardGeneric.cpp
Normal file
@ -0,0 +1,480 @@
|
||||
#include <StdInc.h>
|
||||
#include "Utility/InitFunction.h"
|
||||
#include "Functions/Global.h"
|
||||
#include <deque>
|
||||
// BASED ON xb_monitor by zxmarcos https://github.com/zxmarcos/xb_monitor
|
||||
#if _M_IX86
|
||||
|
||||
#define SRAM_SIZE 0xffff
|
||||
|
||||
using namespace std;
|
||||
|
||||
static int isAddressedExBoard = 0;
|
||||
int is_addressedExBoard() {
|
||||
return isAddressedExBoard;
|
||||
}
|
||||
void reset_addressedExBoard()
|
||||
{
|
||||
isAddressedExBoard = 0;
|
||||
}
|
||||
|
||||
static vector<char> r(1024);
|
||||
|
||||
unsigned char SRAM[SRAM_SIZE];
|
||||
|
||||
void SRAM_save()
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
fp = fopen("sram.bin", "wb");
|
||||
if (!fp) {
|
||||
return;
|
||||
}
|
||||
fwrite(SRAM, 1, SRAM_SIZE, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void SRAM_load()
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
fp = fopen("sram.bin", "rb");
|
||||
memset(SRAM, 0, SRAM_SIZE);
|
||||
if (!fp) {
|
||||
return;
|
||||
}
|
||||
fread(SRAM, 1, SRAM_SIZE, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
DWORD process_streamExBoard(UINT8 *stream, DWORD srcsize, BYTE *dst, DWORD dstsize)
|
||||
{
|
||||
|
||||
r.clear();
|
||||
|
||||
switch (stream[1]) {
|
||||
case 0xfa:
|
||||
{
|
||||
unsigned int addr = 0;
|
||||
unsigned int size = 0;
|
||||
r.push_back(0x76);
|
||||
r.push_back(0xfa);
|
||||
r.push_back(0x05);
|
||||
r.push_back(0x70);
|
||||
r.push_back(0x42);
|
||||
//A5 FA 17 00 58 10 02 03 04 05 06 00 01 02 03 04 05 06 00 00 00 00 5A
|
||||
addr = (stream[3] << 8) | (stream[4]);
|
||||
size = stream[5];
|
||||
|
||||
if (size >= (srcsize - 1))
|
||||
size = srcsize - 1;
|
||||
if ((addr + size) >= 0xffff)
|
||||
size = 0xffff - addr;
|
||||
|
||||
//logmsg("SRAM WRITE %d : %x\n", size, addr);
|
||||
memcpy(&SRAM[addr], &stream[6], size);
|
||||
break;
|
||||
}
|
||||
#if 0
|
||||
case 0xfb:
|
||||
r.push_back(0x76);
|
||||
r.push_back(0xfb);
|
||||
|
||||
if (stream[4] == 0)
|
||||
{
|
||||
r.push_back(80 + 80 + 5);
|
||||
int a3 = 0;
|
||||
int a5 = 3;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
// 3
|
||||
if (i & 1) {
|
||||
for (int k = 0; k < 3; k++)
|
||||
r.push_back(tbl0[a3 + k]);
|
||||
a3 += 12;
|
||||
}
|
||||
else {
|
||||
for (int k = 0; k < 5; k++)
|
||||
r.push_back(tbl0[a5 + k]);
|
||||
a5 += 12;
|
||||
}
|
||||
}
|
||||
|
||||
a3 = 1;
|
||||
a5 = 4;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
// 3
|
||||
if (i & 1) {
|
||||
for (int k = 0; k < 3; k++)
|
||||
r.push_back(tbl0[a3 + k]);
|
||||
a3 += 12;
|
||||
}
|
||||
else {
|
||||
for (int k = 0; k < 5; k++)
|
||||
r.push_back(tbl0[a5 + k]);
|
||||
a5 += 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
r.push_back(0xf4);
|
||||
for (int i = 0; i < 0xef; i++)
|
||||
r.push_back(0);
|
||||
}
|
||||
r.push_back(0x70);
|
||||
r.push_back(0x42);
|
||||
break;
|
||||
#else
|
||||
case 0xfb:
|
||||
{
|
||||
//A5 FB 07 00 00 EF 5A
|
||||
r.push_back(0x76);
|
||||
r.push_back(0xfb);
|
||||
|
||||
unsigned int addr = 0;
|
||||
unsigned int size = stream[5];
|
||||
unsigned pos = r.size();
|
||||
r.push_back(5);
|
||||
|
||||
addr = (stream[3] << 8) | (stream[4]);
|
||||
size = stream[5];
|
||||
|
||||
if ((addr + size) >= 0xffff)
|
||||
size = 0xffff - addr;
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
r.push_back(SRAM[addr++]);
|
||||
|
||||
r[pos] += size;
|
||||
//logmsg("SRAM READ %d : %x\n", size, addr);
|
||||
|
||||
r.push_back(0x70);
|
||||
r.push_back(0x42);
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
case 0xfe:
|
||||
r.push_back(0x76);
|
||||
r.push_back(0xfe);
|
||||
r.push_back(0x06);
|
||||
if (stream[3])
|
||||
r.push_back(0x01);
|
||||
else
|
||||
r.push_back(0);
|
||||
r.push_back(0x70);
|
||||
r.push_back(0x42);
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
r.push_back(0x76);
|
||||
r.push_back(0x01);
|
||||
r.push_back(0x06);
|
||||
r.push_back(0x00);
|
||||
r.push_back(0x70);
|
||||
r.push_back(0x42);
|
||||
isAddressedExBoard = 1;
|
||||
break;
|
||||
case 0x08: // error
|
||||
isAddressedExBoard = 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
BYTE *pdst = dst;
|
||||
unsigned i = 0;
|
||||
unsigned maxv = r.size();
|
||||
|
||||
if (maxv > dstsize)
|
||||
maxv = dstsize;
|
||||
|
||||
for (i = 0; i < maxv; i++)
|
||||
*pdst++ = r[i];
|
||||
|
||||
unsigned sz = r.size();
|
||||
//r.clear();
|
||||
return sz;
|
||||
}
|
||||
|
||||
using namespace std::string_literals;
|
||||
static std::map<HANDLE, std::deque<BYTE>> g_replyBuffers;
|
||||
void AddCommOverride(HANDLE hFile);
|
||||
|
||||
static BOOL __stdcall ReadFileWrapExBoard(HANDLE hFile,
|
||||
LPVOID lpBuffer,
|
||||
DWORD nNumberOfBytesToRead,
|
||||
LPDWORD lpNumberOfBytesRead,
|
||||
LPOVERLAPPED lpOverlapped)
|
||||
{
|
||||
if (hFile == (HANDLE)0x8001)
|
||||
{
|
||||
OutputDebugStringA("COM1 READ");
|
||||
auto& outQueue = g_replyBuffers[hFile];
|
||||
|
||||
int toRead = min(outQueue.size(), nNumberOfBytesToRead);
|
||||
|
||||
std::copy(outQueue.begin(), outQueue.begin() + toRead, reinterpret_cast<uint8_t*>(lpBuffer));
|
||||
outQueue.erase(outQueue.begin(), outQueue.begin() + toRead);
|
||||
|
||||
*lpNumberOfBytesRead = toRead;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
return ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
|
||||
}
|
||||
|
||||
|
||||
static HANDLE __stdcall CreateFileAWrapExBoard(LPCSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes,
|
||||
HANDLE hTemplateFile)
|
||||
{
|
||||
if (strnicmp(lpFileName, "D:\\", 3) == 0)
|
||||
{
|
||||
if (GetFileAttributesA(lpFileName) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
wchar_t pathRoot[MAX_PATH];
|
||||
GetModuleFileNameW(GetModuleHandle(nullptr), pathRoot, _countof(pathRoot));
|
||||
|
||||
wcsrchr(pathRoot, L'\\')[0] = L'\0';
|
||||
|
||||
// assume just ASCII
|
||||
std::string fn = lpFileName;
|
||||
std::wstring wfn(fn.begin(), fn.end());
|
||||
|
||||
CreateDirectoryW((pathRoot + L"\\TeknoParrot\\"s).c_str(), nullptr);
|
||||
|
||||
return CreateFileW((pathRoot + L"\\TeknoParrot\\"s + wfn.substr(3)).c_str(),
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
lpSecurityAttributes,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
hTemplateFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp(lpFileName, "COM1", 4) == 0)
|
||||
{
|
||||
OutputDebugStringA("COM1 HOOK");
|
||||
HANDLE hFile = (HANDLE)0x8001;
|
||||
|
||||
AddCommOverride(hFile);
|
||||
|
||||
return hFile;
|
||||
}
|
||||
|
||||
return CreateFileA(lpFileName,
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
lpSecurityAttributes,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
hTemplateFile);
|
||||
}
|
||||
|
||||
extern int* wheelSection;
|
||||
|
||||
BOOL __stdcall WriteFileWrapExBoard(HANDLE hFile,
|
||||
LPVOID lpBuffer,
|
||||
DWORD nNumberOfBytesToWrite,
|
||||
LPDWORD lpNumberOfBytesWritten,
|
||||
LPOVERLAPPED lpOverlapped)
|
||||
{
|
||||
if (hFile == (HANDLE)0x8001)
|
||||
{
|
||||
OutputDebugStringA("COM1 WRITE");
|
||||
static BYTE rbuffer[1024];
|
||||
DWORD sz = process_streamExBoard((LPBYTE)lpBuffer, nNumberOfBytesToWrite, rbuffer, 1024);
|
||||
if (sz != 1) {
|
||||
for (DWORD i = 0; i < sz; i++)
|
||||
g_replyBuffers[hFile].push_back(rbuffer[i]);
|
||||
}
|
||||
|
||||
*lpNumberOfBytesWritten = nNumberOfBytesToWrite;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
return WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
|
||||
}
|
||||
|
||||
BOOL __stdcall ClearCommErrorWrap(HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT lpStat)
|
||||
{
|
||||
if (hFile != (HANDLE)0x8001)
|
||||
{
|
||||
return ClearCommError(hFile, lpErrors, lpStat);
|
||||
}
|
||||
|
||||
if(lpStat)
|
||||
{
|
||||
OutputDebugStringA("CLEAR COMM ERROR COM1");
|
||||
if(g_replyBuffers[hFile].empty())
|
||||
{
|
||||
lpStat->cbInQue = g_replyBuffers[hFile].size();
|
||||
}
|
||||
else
|
||||
{
|
||||
lpStat->cbInQue = 0;
|
||||
}
|
||||
|
||||
lpStat->cbInQue += 8;
|
||||
|
||||
g_replyBuffers[hFile].push_back(0x76);
|
||||
g_replyBuffers[hFile].push_back(0xFD);
|
||||
g_replyBuffers[hFile].push_back(0x08);
|
||||
g_replyBuffers[hFile].push_back(0x00); // Control Byte 1
|
||||
g_replyBuffers[hFile].push_back(0x00); // Control Byte 2
|
||||
g_replyBuffers[hFile].push_back(0x00); // Control Byte 3
|
||||
g_replyBuffers[hFile].push_back(0x00); // Control Byte 4
|
||||
g_replyBuffers[hFile].push_back(0x42);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL __stdcall GetCommModemStatusWrap(HANDLE hFile, LPDWORD lpModemStat)
|
||||
{
|
||||
if (hFile != (HANDLE)0x8001) {
|
||||
return GetCommModemStatus(hFile, lpModemStat);
|
||||
}
|
||||
|
||||
if (is_addressedExBoard())
|
||||
*lpModemStat = 0x10;
|
||||
else
|
||||
*lpModemStat = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WINAPI CloseHandleWrap(
|
||||
_In_ HANDLE hObject
|
||||
)
|
||||
{
|
||||
if (hObject == (HANDLE)0x8001)
|
||||
return TRUE;
|
||||
CloseHandle(hObject);
|
||||
}
|
||||
|
||||
int __stdcall GetKeyLicenseWrap(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
LONG __stdcall ChangeDisplaySettingsWrap(DEVMODE *lpDevMode, DWORD dwflags)
|
||||
{
|
||||
return DISP_CHANGE_SUCCESSFUL;
|
||||
}
|
||||
|
||||
BOOL __stdcall ExitWindowsExWrap(UINT uFlags, DWORD dwReason)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int __stdcall ShowCursorWrap(BOOL bShow)
|
||||
{
|
||||
return ShowCursor(TRUE);
|
||||
}
|
||||
|
||||
HCURSOR __stdcall SetCursorWrap(HCURSOR hCursor)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
SHORT __stdcall GetAsyncKeyStateWrap(int vKey)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL __stdcall SetWindowPosWrap(HWND hWnd,
|
||||
HWND hWndInsertAfter,
|
||||
int X,
|
||||
int Y,
|
||||
int cx,
|
||||
int cy,
|
||||
UINT uFlags)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HWND __stdcall CreateWindowExWWrap(DWORD dwExStyle,
|
||||
LPCWSTR lpClassName,
|
||||
LPCWSTR lpWindowName,
|
||||
DWORD dwStyle,
|
||||
int x,
|
||||
int y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
HWND hWndParent,
|
||||
HMENU hMenu,
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam)
|
||||
{
|
||||
dwExStyle = 0;
|
||||
dwStyle = WS_OVERLAPPEDWINDOW;
|
||||
|
||||
RECT r;
|
||||
r.bottom = nHeight;
|
||||
r.top = 0;
|
||||
r.right = nWidth;
|
||||
r.left = 0;
|
||||
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE);
|
||||
|
||||
return CreateWindowExW(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, r.right, r.bottom, hWndParent,
|
||||
hMenu, hInstance, lpParam);
|
||||
}
|
||||
|
||||
HWND __stdcall CreateWindowExAWrap(DWORD dwExStyle,
|
||||
LPCSTR lpClassName,
|
||||
LPCSTR lpWindowName,
|
||||
DWORD dwStyle,
|
||||
int x,
|
||||
int y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
HWND hWndParent,
|
||||
HMENU hMenu,
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam)
|
||||
{
|
||||
dwExStyle = 0;
|
||||
dwStyle = WS_OVERLAPPEDWINDOW;
|
||||
|
||||
RECT r;
|
||||
r.right = nWidth;
|
||||
r.bottom = nHeight;
|
||||
r.top = 0;
|
||||
r.left = 0;
|
||||
AdjustWindowRect(&r, dwStyle, FALSE);
|
||||
|
||||
nWidth = r.right - r.left;
|
||||
nHeight = r.bottom - r.top;
|
||||
|
||||
return CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, 0, 0, nWidth, nHeight, hWndParent,
|
||||
hMenu, hInstance, lpParam);
|
||||
}
|
||||
|
||||
static InitFunction ExBoardGenericFunc([]()
|
||||
{
|
||||
iatHook("kernel32.dll", CreateFileAWrapExBoard, "CreateFileA");
|
||||
iatHook("kernel32.dll", ReadFileWrapExBoard, "ReadFile");
|
||||
iatHook("kernel32.dll", WriteFileWrapExBoard, "WriteFile");
|
||||
iatHook("kernel32.dll", ClearCommErrorWrap, "ClearCommError");
|
||||
iatHook("kernel32.dll", GetCommModemStatusWrap, "GetCommModemStatus");
|
||||
iatHook("kernel32.dll", CloseHandleWrap, "CloseHandle");
|
||||
iatHook("IpgExKey.dll", GetKeyLicenseWrap, "_GetKeyLicense@0");
|
||||
iatHook("user32.dll", ChangeDisplaySettingsWrap, "ChangeDisplaySettingsA");
|
||||
iatHook("user32.dll", ExitWindowsExWrap, "ExitWindowsEx");
|
||||
iatHook("user32.dll", ShowCursorWrap, "ShowCursor");
|
||||
iatHook("user32.dll", SetCursorWrap, "SetCursor");
|
||||
iatHook("user32.dll", GetAsyncKeyStateWrap, "GetAsyncKeyState");
|
||||
iatHook("user32.dll", SetWindowPosWrap, "SetWindowPos");
|
||||
iatHook("user32.dll", CreateWindowExWWrap, "CreateWindowExW");
|
||||
iatHook("user32.dll", CreateWindowExAWrap, "CreateWindowExA");
|
||||
|
||||
|
||||
|
||||
SRAM_load();
|
||||
|
||||
}, GameID::ExBoardGeneric);
|
||||
#endif
|
@ -197,6 +197,18 @@ void GameDetect::DetectCurrentGame()
|
||||
currentGame = GameID::TypeXGeneric;
|
||||
X2Type = X2Type::BattleFantasia;
|
||||
break;
|
||||
case 0x521d6765: // Arcana Heart 3
|
||||
currentGame = GameID::ExBoardGeneric;
|
||||
break;
|
||||
case 0x581aa812: // Daemon Bride
|
||||
currentGame = GameID::ExBoardGeneric;
|
||||
break;
|
||||
case 0xbb359a1a: // Suggoi! Arcana Heart 2
|
||||
currentGame = GameID::ExBoardGeneric;
|
||||
break;
|
||||
//case 0xea1984ff:
|
||||
// currentGame = GameID::ExBoardGeneric;
|
||||
// break;
|
||||
#endif
|
||||
default:
|
||||
auto moduleBase = (uintptr_t)GetModuleHandle(nullptr);
|
||||
|
@ -39,4 +39,5 @@ enum class GameID
|
||||
VirtuaRLimit,
|
||||
SchoolOfRagnarok,
|
||||
PokkenTournament,
|
||||
ExBoardGeneric
|
||||
};
|
Loading…
Reference in New Issue
Block a user