Added wacca stub code

This commit is contained in:
Hay1tsme 2021-12-21 00:02:17 -05:00
parent 68e71c845b
commit 6b2a4e5c65
20 changed files with 764 additions and 1 deletions

View File

@ -27,6 +27,8 @@ COPY hooklib hooklib
COPY iccard iccard
COPY idzhook idzhook
COPY idzio idzio
COPY mercuryhook mercuryhook
COPY mercuryio mercuryio
COPY jvs jvs
COPY minihook minihook
COPY mu3hook mu3hook

View File

@ -28,6 +28,21 @@ $(BUILD_DIR_ZIP)/idz.zip:
$(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip *
$(BUILD_DIR_ZIP)/mercury.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/mercuryhook/mercuryhook.dll \
$(DIST_DIR)/mercury/segatools.ini \
$(DIST_DIR)/mercury/start.bat \
$(BUILD_DIR_ZIP)/mercury
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/mercury/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/mercury/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mercury ; zip -r ../mercury.zip *
$(BUILD_DIR_ZIP)/doc.zip: \
$(DOC_DIR)/config \
$(DOC_DIR)/chunihook.md \
@ -40,6 +55,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
$(BUILD_DIR_ZIP)/chuni.zip \
$(BUILD_DIR_ZIP)/doc.zip \
$(BUILD_DIR_ZIP)/idz.zip \
$(BUILD_DIR_ZIP)/mercury.zip \
CHANGELOG.md \
README.md \

View File

@ -28,6 +28,7 @@ enum {
IO4_CMD_CLEAR_BOARD_STATUS = 0x03,
IO4_CMD_SET_GENERAL_OUTPUT = 0x04,
IO4_CMD_SET_PWM_OUTPUT = 0x05,
IO4_CMD_UNIMPLEMENTED = 0x41,
IO4_CMD_UPDATE_FIRMWARE = 0x85,
};
@ -236,6 +237,11 @@ static HRESULT io4_handle_write(struct irp *irp)
return E_FAIL;
case IO4_CMD_UNIMPLEMENTED:
//dprintf("USB I/O: Unimplemented cmd 41\n");
return S_OK;
default:
dprintf("USB I/O: Unknown command %02x\n", out.cmd);

42
dist/mercury/segatools.ini vendored Normal file
View File

@ -0,0 +1,42 @@
[vfs]
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=amfs
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=appdata
option=option
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[ds]
; Region code on the emulated AMEX board DS EEPROM.
; 1: Japan
; 4: Export (some UI elements in English)
;
; NOTE: Changing this setting causes a factory reset.
region=1
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; SEGA games are somewhat picky about their LAN environment, so leaving this
; setting enabled is recommended.
enable=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.250.0
[io4]
; Input API selection for JVS input emulator.
test=0x31
service=0x32
[gfx]
enable=0

10
dist/mercury/start.bat vendored Normal file
View File

@ -0,0 +1,10 @@
@echo off
pushd %~dp0
taskkill /f /im amdaemon.exe > nul 2>&1
:LOOP
inject -d -k mercuryhook.dll amdaemon.exe -c config.json config_region_jpn.json config_video_clone.json config_video_clone_flip.json config_video_dual.json config_video_dual_flip.json
inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury.exe
taskkill /f /im amdaemon.exe > nul 2>&1
echo.
echo Game processes have terminated
pause

43
mercuryhook/config.c Normal file
View File

@ -0,0 +1,43 @@
#include <assert.h>
#include <stddef.h>
#include "board/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "hooklib/gfx.h"
#include "mercuryhook/config.h"
#include "platform/config.h"
void mercury_dll_config_load(
struct mercury_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"mercuryio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void mercury_hook_config_load(
struct mercury_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
aime_config_load(&cfg->aime, filename);
dvd_config_load(&cfg->dvd, filename);
io4_config_load(&cfg->io4, filename);
gfx_config_load(&cfg->gfx, filename);
mercury_dll_config_load(&cfg->dll, filename);
}

29
mercuryhook/config.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "hooklib/dvd.h"
#include "hooklib/gfx.h"
#include "mercuryhook/mercury-dll.h"
#include "platform/config.h"
struct mercury_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct gfx_config gfx;
struct mercury_dll_config dll;
};
void mercury_dll_config_load(
struct mercury_dll_config *cfg,
const wchar_t *filename);
void mercury_hook_config_load(
struct mercury_hook_config *cfg,
const wchar_t *filename);

109
mercuryhook/dllmain.c Normal file
View File

@ -0,0 +1,109 @@
#include <windows.h>
#include "board/io4.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "mercuryhook/config.h"
#include "mercuryhook/io4.h"
#include "mercuryhook/mercury-dll.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE mercury_hook_mod;
static process_entry_t mercury_startup;
static struct mercury_hook_config mercury_hook_cfg;
/* This hook is based on mu3hook, with leaked mercuryhook i/o codes. */
static DWORD CALLBACK mercury_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin mercury_pre_startup ---\n");
/* Load config */
mercury_hook_config_load(&mercury_hook_cfg, L".\\segatools.ini");
/* Hook Win32 APIs */
dvd_hook_init(&mercury_hook_cfg.dvd, mercury_hook_mod);
gfx_hook_init(&mercury_hook_cfg.gfx, mercury_hook_mod);
serial_hook_init();
/* Initialize emulation hooks */
hr = platform_hook_init(
&mercury_hook_cfg.platform,
"SDFE",
"ACA1",
mercury_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&mercury_hook_cfg.aime, 1, mercury_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(2);
if (FAILED(hr)) {
goto fail;
}
hr = mercury_dll_init(&mercury_hook_cfg.dll, mercury_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = mercury_io4_hook_init(&mercury_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
dprintf("--- End mercury_pre_startup ---\n");
/* Jump to EXE start address */
return mercury_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
mercury_hook_mod = mod;
hr = process_hijack_startup(mercury_pre_startup, &mercury_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

64
mercuryhook/io4.c Normal file
View File

@ -0,0 +1,64 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/io4.h"
#include "mercuryhook/mercury-dll.h"
#include "util/dprintf.h"
static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state);
static const struct io4_ops mercury_io4_ops = {
.poll = mercury_io4_poll,
};
HRESULT mercury_io4_hook_init(const struct io4_config *cfg)
{
HRESULT hr;
assert(mercury_dll.init != NULL);
hr = io4_hook_init(cfg, &mercury_io4_ops, NULL);
if (FAILED(hr)) {
return hr;
}
return mercury_dll.init();
}
static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn;
HRESULT hr;
assert(mercury_dll.poll != NULL);
assert(mercury_dll.get_opbtns != NULL);
assert(mercury_dll.get_gamebtns != NULL);
memset(state, 0, sizeof(*state));
hr = mercury_dll.poll();
if (FAILED(hr)) {
return hr;
}
opbtn = 0;
mercury_dll.get_opbtns(&opbtn);
if (opbtn & MAI2_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & MAI2_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
return S_OK;
}

7
mercuryhook/io4.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "board/io4.h"
HRESULT mercury_io4_hook_init(const struct io4_config *cfg);

109
mercuryhook/mercury-dll.c Normal file
View File

@ -0,0 +1,109 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "mercuryhook/mercury-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym mercury_dll_syms[] = {
{
.sym = "mercury_io_init",
.off = offsetof(struct mercury_dll, init),
}, {
.sym = "mercury_io_poll",
.off = offsetof(struct mercury_dll, poll),
}, {
.sym = "mercury_io_get_opbtns",
.off = offsetof(struct mercury_dll, get_opbtns),
}, {
.sym = "mercury_io_get_gamebtns",
.off = offsetof(struct mercury_dll, get_gamebtns),
}
};
struct mercury_dll mercury_dll;
// Copypasta DLL binding and diagnostic message boilerplate.
// Not much of this lends itself to being easily factored out. Also there
// will be a lot of API-specific branching code here eventually as new API
// versions get defined, so even though these functions all look the same
// now this won't remain the case forever.
HRESULT mercury_dll_init(const struct mercury_dll_config *cfg, HINSTANCE self)
{
uint16_t (*get_api_version)(void);
const struct dll_bind_sym *sym;
HINSTANCE owned;
HINSTANCE src;
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
if (cfg->path[0] != L'\0') {
owned = LoadLibraryW(cfg->path);
if (owned == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Wacca IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Wacca IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "mercury_io_get_api_version");
if (get_api_version != NULL) {
mercury_dll.api_version = get_api_version();
} else {
mercury_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose mercury_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (mercury_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Wacca IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
mercury_dll.api_version);
goto end;
}
sym = mercury_dll_syms;
hr = dll_bind(&mercury_dll, src, &sym, _countof(mercury_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Wacca IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end;
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

21
mercuryhook/mercury-dll.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <windows.h>
#include "mercuryio/mercuryio.h"
struct mercury_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(void);
void (*get_opbtns)(uint8_t *opbtn);
void (*get_gamebtns)(uint16_t *player1, uint16_t *player2);
};
struct mercury_dll_config {
wchar_t path[MAX_PATH];
};
extern struct mercury_dll mercury_dll;
HRESULT mercury_dll_init(const struct mercury_dll_config *cfg, HINSTANCE self);

View File

@ -0,0 +1,19 @@
LIBRARY mercuryhook
EXPORTS
Direct3DCreate9
aime_io_get_api_version
aime_io_init
aime_io_led_set_color
aime_io_nfc_get_aime_id
aime_io_nfc_get_felica_id
aime_io_nfc_poll
amDllVideoClose @2
amDllVideoGetVBiosVersion @4
amDllVideoOpen @1
amDllVideoSetResolution @3
mercury_io_get_api_version
mercury_io_get_gamebtns
mercury_io_get_opbtns
mercury_io_init
mercury_io_poll

29
mercuryhook/meson.build Normal file
View File

@ -0,0 +1,29 @@
shared_library(
'mercuryhook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'mercuryhook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
],
link_with : [
aimeio_lib,
board_lib,
hooklib_lib,
mercuryio_lib,
platform_lib,
util_lib,
],
sources : [
'config.c',
'config.h',
'dllmain.c',
'io4.c',
'io4.h',
'mercury-dll.c',
'mercury-dll.h'
],
)

25
mercuryio/config.c Normal file
View File

@ -0,0 +1,25 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "mercuryio/config.h"
/*
Wacca Default key binding
*/
void mercury_io_config_load(
struct mercury_io_config *cfg,
const wchar_t *filename)
{
wchar_t key[16];
int i;
assert(cfg != NULL);
assert(filename != NULL);
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename);
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename);
}

17
mercuryio/config.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct mercury_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_1p_btn[9];
uint8_t vk_2p_btn[9];
};
void mercury_io_config_load(
struct mercury_io_config *cfg,
const wchar_t *filename);

133
mercuryio/mercuryio.c Normal file
View File

@ -0,0 +1,133 @@
#include <windows.h>
#include <limits.h>
#include <stdint.h>
#include "mercuryio/mercuryio.h"
#include "mercuryio/config.h"
static uint8_t mercury_opbtn;
static uint16_t mercury_player1_btn;
static uint16_t mercury_player2_btn;
static struct mercury_io_config mercury_io_cfg;
uint16_t mercury_io_get_api_version(void)
{
return 0x0100;
}
HRESULT mercury_io_init(void)
{
mercury_io_config_load(&mercury_io_cfg, L".\\segatools.ini");
return S_OK;
}
HRESULT mercury_io_poll(void)
{
mercury_opbtn = 0;
mercury_player1_btn = 0;
mercury_player2_btn = 0;
if (GetAsyncKeyState(mercury_io_cfg.vk_test)) {
mercury_opbtn |= MAI2_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_service)) {
mercury_opbtn |= MAI2_IO_OPBTN_SERVICE;
}
//Player 1
if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[0])) {
mercury_player1_btn |= MAI2_IO_GAMEBTN_1;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[1])) {
mercury_player1_btn |= MAI2_IO_GAMEBTN_2;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[2])) {
mercury_player1_btn |= MAI2_IO_GAMEBTN_3;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[3])) {
mercury_player1_btn |= MAI2_IO_GAMEBTN_4;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[4])) {
mercury_player1_btn |= MAI2_IO_GAMEBTN_5;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[5])) {
mercury_player1_btn |= MAI2_IO_GAMEBTN_6;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[6])) {
mercury_player1_btn |= MAI2_IO_GAMEBTN_7;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[7])) {
mercury_player1_btn |= MAI2_IO_GAMEBTN_8;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[8])) {
mercury_player1_btn |= MAI2_IO_GAMEBTN_SELECT;
}
//Player 2
if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[0])) {
mercury_player2_btn |= MAI2_IO_GAMEBTN_1;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[1])) {
mercury_player2_btn |= MAI2_IO_GAMEBTN_2;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[2])) {
mercury_player2_btn |= MAI2_IO_GAMEBTN_3;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[3])) {
mercury_player2_btn |= MAI2_IO_GAMEBTN_4;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[4])) {
mercury_player2_btn |= MAI2_IO_GAMEBTN_5;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[5])) {
mercury_player2_btn |= MAI2_IO_GAMEBTN_6;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[6])) {
mercury_player2_btn |= MAI2_IO_GAMEBTN_7;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[7])) {
mercury_player2_btn |= MAI2_IO_GAMEBTN_8;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[8])) {
mercury_player2_btn |= MAI2_IO_GAMEBTN_SELECT;
}
return S_OK;
}
void mercury_io_get_opbtns(uint8_t *opbtn)
{
if (opbtn != NULL) {
*opbtn = mercury_opbtn;
}
}
void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2)
{
if (player1 != NULL) {
*player1 = mercury_player1_btn;
}
if (player2 != NULL ){
*player2 = mercury_player2_btn;
}
}

67
mercuryio/mercuryio.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
MAI2_IO_OPBTN_TEST = 0x01,
MAI2_IO_OPBTN_SERVICE = 0x02,
};
enum {
MAI2_IO_GAMEBTN_1 = 0x01,
MAI2_IO_GAMEBTN_2 = 0x02,
MAI2_IO_GAMEBTN_3 = 0x04,
MAI2_IO_GAMEBTN_4 = 0x08,
MAI2_IO_GAMEBTN_5 = 0x10,
MAI2_IO_GAMEBTN_6 = 0x20,
MAI2_IO_GAMEBTN_7 = 0x40,
MAI2_IO_GAMEBTN_8 = 0x80,
MAI2_IO_GAMEBTN_SELECT = 0x100,
};
/* Get the version of the Wacca IO API that this DLL supports. This
function should return a positive 16-bit integer, where the high byte is
the major version and the low byte is the minor version (as defined by the
Semantic Versioning standard).
The latest API version as of this writing is 0x0100. */
uint16_t mercury_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after mercury_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT mercury_io_init(void);
/* Send any queued outputs (of which there are currently none, though this may
change in subsequent API versions) and retrieve any new inputs.
Minimum API version: 0x0100 */
HRESULT mercury_io_poll(void);
/* Get the state of the cabinet's operator buttons as of the last poll. See
MAI2_IO_OPBTN enum above: this contains bit mask definitions for button
states returned in *opbtn. All buttons are active-high.
Minimum API version: 0x0100 */
void mercury_io_get_opbtns(uint8_t *opbtn);
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
MAI2_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into
a left hand side set of inputs and a right hand side set of inputs: the bit
mappings are the same in both cases.
All buttons are active-high, even though some buttons' electrical signals
on a real cabinet are active-low.
Minimum API version: 0x0100 */
void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2);

13
mercuryio/meson.build Normal file
View File

@ -0,0 +1,13 @@
mercuryio_lib = static_library(
'mercuryio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'mercuryio.c',
'mercuryio.h',
'config.c',
'config.h',
],
)

View File

@ -48,9 +48,11 @@ subdir('chuniio')
subdir('divaio')
subdir('idzio')
subdir('mu3io')
subdir('mercuryio')
subdir('chunihook')
subdir('divahook')
subdir('idzhook')
subdir('minihook')
subdir('mu3hook')
subdir('mercuryhook')