jubeat: support p3io games

This commit is contained in:
Will Toohey
2021-12-31 20:23:31 +10:00
parent 8ab92872ad
commit 1105a2a130
68 changed files with 1633 additions and 918 deletions
+13
View File
@@ -0,0 +1,13 @@
libs += jbhook-util
libs_jbhook-util := \
acioemu \
src_jbhook-util := \
acio.c \
eamuse.c \
gfx.c \
mixer.c \
locale.c \
p3io.c \
p4io.c \
+103
View File
@@ -0,0 +1,103 @@
// clang-format off
// Don't format because the order is important here
#include <windows.h>
#include <ntdef.h>
#include <devioctl.h>
#include <ntddser.h>
// clang-format on
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include "acioemu/addr.h"
#include "acioemu/emu.h"
#include "acioemu/h44b.h"
#include "acioemu/icca.h"
#include "hook/iohook.h"
#include "jbhook-util/acio.h"
#include "imports/avs.h"
#include "util/defs.h"
#include "util/hex.h"
#include "util/iobuf.h"
#include "util/log.h"
#include "util/str.h"
static struct ac_io_emu ac_io_emu;
static struct ac_io_emu_icca ac_io_emu_icca;
static struct ac_io_emu_h44b ac_io_emu_h44b;
void jbhook_util_ac_io_port_init(const wchar_t *filename)
{
ac_io_emu_init(&ac_io_emu, filename);
ac_io_emu_icca_init(&ac_io_emu_icca, &ac_io_emu, 0);
ac_io_emu_h44b_init(&ac_io_emu_h44b, &ac_io_emu, 1);
}
void jbhook_util_ac_io_port_fini(void)
{
ac_io_emu_fini(&ac_io_emu);
}
void jbhook_util_ac_io_set_iccb(void) {
ac_io_emu_icca_set_version(&ac_io_emu_icca, v150);
ac_io_emu_icca_set_product_code(&ac_io_emu_icca, AC_IO_EMU_PROD_CODE_ICCB);
}
HRESULT
jbhook_util_ac_io_port_dispatch_irp(struct irp *irp)
{
const struct ac_io_message *msg;
HRESULT hr;
log_assert(irp != NULL);
if (!ac_io_emu_match_irp(&ac_io_emu, irp)) {
return iohook_invoke_next(irp);
}
for (;;) {
hr = ac_io_emu_dispatch_irp(&ac_io_emu, irp);
if (hr != S_OK) {
return hr;
}
msg = ac_io_emu_request_peek(&ac_io_emu);
switch (msg->addr) {
case 0:
ac_io_emu_cmd_assign_addrs(&ac_io_emu, msg, 2);
break;
case 1:
ac_io_emu_icca_dispatch_request(&ac_io_emu_icca, msg);
break;
case 2:
ac_io_emu_h44b_dispatch_request(&ac_io_emu_h44b, msg);
break;
case AC_IO_BROADCAST:
log_warning("Broadcast(?) message on jubeat ACIO bus?");
break;
default:
log_warning(
"ACIO message on unhandled bus address: %d", msg->addr);
break;
}
ac_io_emu_request_pop(&ac_io_emu);
}
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef JBHOOK1_ACIO_H
#define JBHOOK1_ACIO_H
#include "hook/iohook.h"
/**
* Initialize the ACIO backend for jubeat on the specified COM port.
*/
void jbhook_util_ac_io_port_init(const wchar_t *filename);
/**
* Shutdown the ACIO backend.
*/
void jbhook_util_ac_io_port_fini(void);
/**
* ACIO backend dispatch irp function. This needs to be hooked up to the iohook
* module in order to receive system calls to dispatch for emulation.
*/
HRESULT jbhook_util_ac_io_port_dispatch_irp(struct irp *irp);
/**
* Enable ICCB emulation instead of the default ICCA emulation - some jubeats
* will have IO errors unless explicitly set to ICCB mode.
*/
void jbhook_util_ac_io_set_iccb(void);
#endif
+144
View File
@@ -0,0 +1,144 @@
#define LOG_MODULE "eamuse-hook"
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "hook/table.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/net.h"
#include "util/str.h"
static const char *jbhook_eamuse_konami_url = "eamuse.konami.";
static int(STDCALL *real_getaddrinfo)(
PCSTR pNodeName,
PCSTR pServiceName,
const ADDRINFOA *pHints,
PADDRINFOA *ppResult);
static int STDCALL my_getaddrinfo(
PCSTR pNodeName,
PCSTR pServiceName,
const ADDRINFOA *pHints,
PADDRINFOA *ppResult);
static DWORD WINAPI my_GetIpAddrTable(
PMIB_IPADDRTABLE pIpAddrTable,
PULONG pdwSize,
BOOL bOrder
);
static const struct hook_symbol eamuse_hook_syms[] = {
{
.name = "getaddrinfo",
.ordinal = 176,
.patch = my_getaddrinfo,
.link = (void **) &real_getaddrinfo,
},
};
static const struct hook_symbol network_hook_syms[] = {
{
.name = "GetIpAddrTable",
.patch = my_GetIpAddrTable,
},
};
static int STDCALL my_getaddrinfo(
PCSTR pNodeName,
PCSTR pServiceName,
const ADDRINFOA *pHints,
PADDRINFOA *ppResult)
{
log_misc("my_getaddrinfo: %s, %s", pNodeName, pServiceName);
/* resolve eamuse.konami.fun/com to 127.0.0.1 to avoid lag spikes every
150 seconds which might happen in-game as well */
if (!strncmp(
pNodeName,
jbhook_eamuse_konami_url,
strlen(jbhook_eamuse_konami_url))) {
log_info("Resolve konami.eamuse.XXX -> localhost");
pNodeName = "localhost";
}
return real_getaddrinfo(pNodeName, pServiceName, pHints, ppResult);
}
static DWORD get_best_ip(void) {
PIP_ADAPTER_INFO info = NULL;
struct net_addr addr;
// fallback to a very obviously wrong value
DWORD ret = (5 << 24) | (7 << 16) | (3 << 8) | (0 << 0);
ULONG sz = 0;
// this uses our hooked GetAdaptersInfo from hooklib/adapter.c, thus using
// the preferred interface instead of the default order
log_assert(GetAdaptersInfo(info, &sz) == ERROR_BUFFER_OVERFLOW);
info = malloc(sz);
if(GetAdaptersInfo(info, &sz) != NO_ERROR) {
goto CLEANUP;
}
if(!net_str_parse(info->IpAddressList.IpAddress.String, &addr)) {
goto CLEANUP;
}
// MS docs say this should be in network byte order, but we need to return
// host byte order for the game to not throw the IP error
ret = addr.ipv4.addr;
CLEANUP:
free(info);
return ret;
}
/**
* In new games via network.dll:network_addr_is_changed and in old games
* directly, this function is called to compare the current IP address vs the IP
* address when the game was first loaded. If the two are different, an error is
* printed. It can be dismissed but will show up every ~10 seconds, impeding
* gameplay. Because GetAdaptersInfo is hooked to return a (sometimes) different
* adapter, computers with multiple interfaces will often fail this comparison
* check. By also hooking this function, the IP addresses of the two calls will
* agree.
*/
static DWORD WINAPI my_GetIpAddrTable(
PMIB_IPADDRTABLE pIpAddrTable,
PULONG pdwSize,
BOOL bOrder
) {
ULONG in = *pdwSize;
// 1 element return table
*pdwSize = sizeof(MIB_IPADDRTABLE) + sizeof(MIB_IPADDRROW);
if(in < *pdwSize) {
return ERROR_INSUFFICIENT_BUFFER;
}
pIpAddrTable->dwNumEntries = 1;
pIpAddrTable->table[0].dwAddr = get_best_ip();
return NO_ERROR;
}
void jbhook_util_eamuse_hook_init(void)
{
hook_table_apply(
NULL, "ws2_32.dll", eamuse_hook_syms, lengthof(eamuse_hook_syms));
hook_table_apply(
NULL, "iphlpapi.dll", network_hook_syms, lengthof(network_hook_syms));
log_info("Inserted eamuse hooks");
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef JBHOOK_UTIL_EAMUSE_H
#define JBHOOK_UTIL_EAMUSE_H
void jbhook_util_eamuse_hook_init(void);
#endif
+180
View File
@@ -0,0 +1,180 @@
#define LOG_MODULE "jbhook-gfx"
#include <windows.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include "hook/com-proxy.h"
#include "hook/table.h"
#include "imports/glhelper.h"
#include "jbhook-util/gfx.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/str.h"
#include "util/time.h"
static void __stdcall hook_glFlush(void);
static void (__stdcall *real_glFlush)(void);
static void hook_glBindFramebufferEXT(GLenum target, GLuint framebuffer);
static void (*real_glBindFramebufferEXT)(GLenum target, GLuint framebuffer);
static const struct hook_symbol jbhook1_opengl_hook_syms[] = {
{.name = "glFlush",
.patch = hook_glFlush,
.link = (void **) &real_glFlush},
};
static const struct hook_symbol jbhook1_glhelper_hook_syms[] = {
{.name = "glBindFramebufferEXT",
.patch = hook_glBindFramebufferEXT,
.link = (void **) &real_glBindFramebufferEXT},
};
static DWORD STDCALL my_GetGlyphOutline(
HDC hdc,
UINT uChar,
UINT uFormat,
LPGLYPHMETRICS lpgm,
DWORD cbBuffer,
LPVOID lpvBuffer,
const MAT2 *lpmat2);
static DWORD(STDCALL *real_GetGlyphOutline)(
HDC hdc,
UINT uChar,
UINT uFormat,
LPGLYPHMETRICS lpgm,
DWORD cbBuffer,
LPVOID lpvBuffer,
const MAT2 *lpmat2);
static const struct hook_symbol gfx_hook_syms[] = {
{.name = "GetGlyphOutlineA",
.patch = my_GetGlyphOutline,
.link = (void **) &real_GetGlyphOutline},
{
.name = "IsDBCSLeadByteEx",
.patch = my_GetGlyphOutline, // !??
.link = (void **) &real_GetGlyphOutline // !??
},
};
static DWORD STDCALL my_GetGlyphOutline(
HDC hdc,
UINT uChar,
UINT uFormat,
LPGLYPHMETRICS lpgm,
DWORD cbBuffer,
LPVOID lpvBuffer,
const MAT2 *lpmat2)
{
if (IsDBCSLeadByteEx(CP_ACP, uChar & 0xFF)) {
log_warning("!!!!!!!!");
char tmp[2];
tmp[0] = uChar & 0xFF;
tmp[1] = (uChar >> 8) & 0xFF;
MultiByteToWideChar(
CP_ACP, MB_USEGLYPHCHARS, tmp, 2, lpvBuffer, cbBuffer);
}
return real_GetGlyphOutline(
hdc, uChar, uFormat, lpgm, cbBuffer, lpvBuffer, lpmat2);
}
void jbhook_util_gfx_hook_init(void)
{
hook_table_apply(NULL, "gdi32.dll", gfx_hook_syms, lengthof(gfx_hook_syms));
log_info("Inserted gfx hooks");
}
void jbhook_util_gfx_set_windowed(bool framed)
{
}
void jbhook_util_gfx_install_vertical_hooks(void) {
hook_table_apply(
NULL,
"opengl32.dll",
jbhook1_opengl_hook_syms,
lengthof(jbhook1_opengl_hook_syms));
hook_table_apply(
NULL,
"glhelper.dll",
jbhook1_glhelper_hook_syms,
lengthof(jbhook1_glhelper_hook_syms));
log_info("Inserted vertical display hooks");
}
// only jubeat uses openGL, let alone needs rotation at all, so hardcode for now
#define W 768
#define H 1360
static GLuint fb;
static GLuint color;
static void fb_init(void) {
static bool init_done = false;
if(init_done) {
return;
}
init_done = true;
glGenFramebuffersEXT(1, &fb);
glGenTextures(1, &color);
}
static uint8_t pixels_raw[W*H*3];
static uint8_t pixels_rot[W*H*3];
static void __stdcall hook_glFlush(void) {
glReadPixels(0, 0, H, W, GL_RGB, GL_UNSIGNED_BYTE, pixels_raw);
for(size_t x = 0; x < W; x++) {
for(size_t y = 0; y < H; y++) {
memcpy(&pixels_rot[3*(y*W + x)], &pixels_raw[3*((W-x)*H + y)], 3);
}
}
real_glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
glDisable(GL_SCISSOR_TEST);
glDrawPixels(W, H, GL_RGB, GL_UNSIGNED_BYTE, pixels_rot);
real_glFlush();
fb_init();
real_glBindFramebufferEXT(GL_FRAMEBUFFER, fb);
glBindTexture(GL_TEXTURE_2D, color);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, H, W, 0, GL_RGBA,GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
}
static void hook_glBindFramebufferEXT(GLenum target, GLuint framebuffer) {
fb_init();
if(target == GL_FRAMEBUFFER && framebuffer == 0) {
framebuffer = fb;
}
return real_glBindFramebufferEXT(target, framebuffer);
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef JBHOOK_D3D8_H
#define JBHOOK_D3D8_H
#include <stdbool.h>
/**
* Hook some gfx related functions to patch gfx related stuff
* like enabling window mode.
*/
void jbhook_util_gfx_hook_init(void);
/**
* Set the game to window mode.
*
* @param framed True to add a window frame and make the window
* movable, resizable, minizable. False for no frame.
*/
void jbhook_util_gfx_set_windowed(bool framed);
/**
* Rotate a horizontal-by-default jubeat window to vertical
*/
void jbhook_util_gfx_install_vertical_hooks(void);
#endif
+37
View File
@@ -0,0 +1,37 @@
#define LOG_MODULE "jbhook-locale"
#include <windows.h>
#include "hook/table.h"
#include "util/defs.h"
#include "util/log.h"
// ANSI/OEM Japanese; Japanese (Shift-JIS)
#define CODEPAGE_SHIFT_JIS 932
static UINT WINAPI hook_GetACP();
static UINT WINAPI hook_GetOEMCP();
static const struct hook_symbol locale_hook_syms[] = {
{.name = "GetOEMCP",
.patch = hook_GetACP},
{.name = "GetOEMCP",
.patch = hook_GetOEMCP},
};
static UINT WINAPI hook_GetACP() {
return CODEPAGE_SHIFT_JIS;
}
static UINT WINAPI hook_GetOEMCP() {
return CODEPAGE_SHIFT_JIS;
}
void jbhook_util_locale_hook_init(void)
{
hook_table_apply(NULL, "kernel32.dll", locale_hook_syms, lengthof(locale_hook_syms));
log_info("Inserted locale hooks");
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef JBHOOK_LOCALE_H
#define JBHOOK_LOCALE_H
void jbhook_util_locale_hook_init(void);
#endif
+79
View File
@@ -0,0 +1,79 @@
#define LOG_MODULE "jbhook-mixer"
#include <windows.h>
#include <mmsystem.h>
#include "hook/table.h"
#include "util/defs.h"
#include "util/log.h"
MMRESULT STDCALL hook_mixerGetLineControlsA(HMIXEROBJ hmxobj, LPMIXERLINECONTROLSA pmxlc, DWORD fdwControls);
MMRESULT STDCALL hook_mixerGetDevCapsA(UINT_PTR uMxId, LPMIXERCAPSA pmxcaps, UINT cbmxcaps);
MMRESULT STDCALL hook_mixerOpen(LPHMIXER phmx, UINT uMxId, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
MMRESULT STDCALL hook_mixerSetControlDetails(HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails);
MMRESULT STDCALL hook_mixerGetControlDetailsA(HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails);
MMRESULT STDCALL hook_mixerClose(HMIXER hmx);
MMRESULT STDCALL hook_mixerGetLineInfoA(HMIXEROBJ hmxobj, LPMIXERLINEA pmxl, DWORD fdwInfo);
static const struct hook_symbol mixer_hook_syms[] = {
{.name = "mixerGetLineControlsA",
.patch = hook_mixerGetLineControlsA},
{.name = "mixerGetDevCapsA",
.patch = hook_mixerGetDevCapsA},
{.name = "mixerOpen",
.patch = hook_mixerOpen},
{.name = "mixerSetControlDetails",
.patch = hook_mixerSetControlDetails},
{.name = "mixerGetControlDetailsA",
.patch = hook_mixerGetControlDetailsA},
{.name = "mixerClose",
.patch = hook_mixerClose},
{.name = "mixerGetLineInfoA",
.patch = hook_mixerGetLineInfoA},
};
/*
Some of these functions return values which are used as pointer offsets into
memory, but that doesn't matter because the game never *modifies* the
pointed-to value, only passes it to the other mixer functions. So it's safe
to not set any of the returning data.
*/
MMRESULT STDCALL hook_mixerOpen(LPHMIXER phmx, UINT uMxId, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen) {
*(DWORD*)phmx = 1234; // any non-zero value is fine
return MMSYSERR_NOERROR;
}
MMRESULT STDCALL hook_mixerClose(HMIXER hmx) {
return MMSYSERR_NOERROR;
}
MMRESULT STDCALL hook_mixerGetLineControlsA(HMIXEROBJ hmxobj, LPMIXERLINECONTROLSA pmxlc, DWORD fdwControls) {
return MMSYSERR_NOERROR;
}
MMRESULT STDCALL hook_mixerGetDevCapsA(UINT_PTR uMxId, LPMIXERCAPSA pmxcaps, UINT cbmxcaps) {
return MMSYSERR_NOERROR;
}
MMRESULT STDCALL hook_mixerGetControlDetailsA(HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails) {
return MMSYSERR_NOERROR;
}
MMRESULT STDCALL hook_mixerGetLineInfoA(HMIXEROBJ hmxobj, LPMIXERLINEA pmxl, DWORD fdwInfo) {
return MMSYSERR_NOERROR;
}
MMRESULT STDCALL hook_mixerSetControlDetails(HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails) {
return MMSYSERR_NOERROR;
}
void jbhook_util_mixer_hook_init(void)
{
hook_table_apply(NULL, "winmm.dll", mixer_hook_syms, lengthof(mixer_hook_syms));
log_info("Inserted mixer hooks");
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef JBHOOK_MIXER_H
#define JBHOOK_MIXER_H
/**
* Hook some mixer related functions and stub out attempts to change the volume.
* In addition to being annoying, this can fail on Vista+ on some outputs,
* despite not actually preventing sound from playing.
*/
void jbhook_util_mixer_hook_init(void);
#endif
+206
View File
@@ -0,0 +1,206 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "bemanitools/jbio.h"
#include "jbhook-util/p3io.h"
#include "p3ioemu/emu.h"
#include "p3ioemu/uart.h"
#include "security/rp-sign-key.h"
#include "security/rp3.h"
#include "util/log.h"
static HRESULT jbhook_p3io_read_jamma(void *ctx, uint32_t *state);
static HRESULT jbhook_p3io_get_roundplug(
void *ctx, uint8_t plug_id, uint8_t *rom, uint8_t *eeprom);
static HRESULT jbhook_p3io_set_outputs(void *ctx, uint32_t state);
/*
0:0 b13
0:1 -
0:2 b15
0:3 -
0:4 test
0:5 coin
0:6 service
0:7 -
1:0 -
1:1 b4
1:2 b8
1:3 b12
1:4 b16
1:5 b3
1:6 b7
1:7 b11
2:0 -
2:1 b2
2:2 b6
2:3 b10
2:4 b14
2:5 b1
2:6 b5
2:7 b9
3:0 -
3:1 -
3:2 -
3:3 -
3:4 -
3:5 -
3:6 -
3:7 -
*/
static const uint32_t jbhook_p3io_panel_mappings[] = {
(1 << 21),
(1 << 17),
(1 << 13),
(1 << 9),
(1 << 22),
(1 << 18),
(1 << 14),
(1 << 10),
(1 << 23),
(1 << 19),
(1 << 15),
(1 << 11),
(1 << 0),
(1 << 20),
(1 << 2),
(1 << 12),
};
static const uint32_t jbhook_p3io_sys_button_mappings[] = {
(1 << 4),
(1 << 6),
(1 << 5),
};
static struct security_mcode jbhook_p3io_mcode;
static struct security_id jbhook_p3io_pcbid;
static struct security_id jbhook_p3io_eamid;
static const struct p3io_ops p3io_ddr_ops = {
.read_jamma = jbhook_p3io_read_jamma,
.get_roundplug = jbhook_p3io_get_roundplug,
.set_outputs = jbhook_p3io_set_outputs,
};
void jbhook_util_p3io_init(
const struct security_mcode *mcode,
const struct security_id *pcbid,
const struct security_id *eamid)
{
memcpy(&jbhook_p3io_mcode, mcode, sizeof(struct security_mcode));
memcpy(&jbhook_p3io_pcbid, pcbid, sizeof(struct security_id));
memcpy(&jbhook_p3io_eamid, eamid, sizeof(struct security_id));
p3io_emu_init(&p3io_ddr_ops, NULL);
}
void jbhook_util_p3io_fini(void)
{
p3io_emu_fini();
}
static HRESULT jbhook_p3io_read_jamma(void *ctx, uint32_t *state)
{
uint16_t panels;
uint8_t buttons;
log_assert(state != NULL);
*state = 0;
if (!jb_io_read_inputs()) {
log_warning("Reading inputs from jbio failed");
return E_FAIL;
}
panels = jb_io_get_panel_inputs();
buttons = jb_io_get_sys_inputs();
for (uint8_t i = 0; i < 16; i++) {
// panels are active-low
if ((panels & (1 << i)) == 0) {
*state |= jbhook_p3io_panel_mappings[i];
}
}
for (uint8_t i = 0; i < 2; i++) {
// sys buttons are active-high
if (buttons & (1 << i)) {
*state |= jbhook_p3io_sys_button_mappings[i];
}
}
return S_OK;
}
static HRESULT jbhook_p3io_get_roundplug(
void *ctx, uint8_t plug_id, uint8_t *rom, uint8_t *eeprom)
{
struct security_rp3_eeprom eeprom_out;
if (plug_id == 0) {
/* black */
memcpy(rom, jbhook_p3io_pcbid.id, sizeof(jbhook_p3io_pcbid.id));
security_rp3_generate_signed_eeprom_data(
SECURITY_RP_UTIL_RP_TYPE_BLACK,
&security_rp_sign_key_black_gfdmv4,
&jbhook_p3io_mcode,
&jbhook_p3io_pcbid,
&eeprom_out);
} else {
/* white */
memcpy(rom, jbhook_p3io_eamid.id, sizeof(jbhook_p3io_eamid.id));
security_rp3_generate_signed_eeprom_data(
SECURITY_RP_UTIL_RP_TYPE_WHITE,
&security_rp_sign_key_white_eamuse,
&security_mcode_eamuse,
&jbhook_p3io_eamid,
&eeprom_out);
}
memcpy(eeprom, &eeprom_out, sizeof(struct security_rp3_eeprom));
return S_OK;
}
static HRESULT jbhook_p3io_set_outputs(void *ctx, uint32_t state) {
uint8_t p3io_panel = (state & 0x00FF00) >> 8;
uint8_t p3io_coinblocker = (state & 0xFF0000) >> 16;
enum jb_io_panel_mode panel_mode;
switch(p3io_panel) {
case 5:
panel_mode = JB_IO_PANEL_MODE_TOP_LEFT;
break;
case 4:
panel_mode = JB_IO_PANEL_MODE_TOP_RIGHT;
break;
case 6:
panel_mode = JB_IO_PANEL_MODE_BOTTOM_RIGHT;
break;
case 7:
panel_mode = JB_IO_PANEL_MODE_BOTTOM_LEFT;
break;
default:
panel_mode = JB_IO_PANEL_MODE_ALL;
break;
}
jb_io_set_panel_mode(panel_mode);
// 0x40 has been seen to unblock, no other values observed
jb_io_set_coin_blocker(p3io_coinblocker == 0x00);
return S_OK;
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef JBHOOK1_P3IO_H
#define JBHOOK1_P3IO_H
#include <windows.h>
#include "hook/iohook.h"
#include "security/id.h"
#include "security/mcode.h"
/**
* Initialize the jb01-04 specific p3io emulation backend.
*
* @param mcode Mcode of the target game to run. Required for dongle emulation.
* @param pcbid PCBDID
* @param eamid EAMID
*/
void jbhook_util_p3io_init(
const struct security_mcode *mcode,
const struct security_id *pcbid,
const struct security_id *eamid);
/**
* Shutdown the p3io emulation backend.
*/
void jbhook_util_p3io_fini(void);
#endif
+187
View File
@@ -0,0 +1,187 @@
#define LOG_MODULE "jbhook-io"
#include <string.h>
#include "bemanitools/jbio.h"
#include "imports/avs.h"
#include "jbhook-util/p4io.h"
#include "p4io/cmd.h"
#include "util/log.h"
static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes);
static uint32_t jbhook_command_handle(
uint8_t cmd,
const void *payload,
uint32_t payload_len,
void *resp,
uint32_t resp_max_len);
static const struct p4ioemu_device_msg_hook jbhook_p4io_msg = {
.jamma2_read = jbhook_io_jamma2_read,
.command_handle = jbhook_command_handle,
.roundplug_read_id = NULL,
.roundplug_read_mem = NULL};
/*
0:0 ???
0:1 b2
0:2 b6
0:3 b10
0:4 b14
0:5 b1
0:6 b5
0:7 b9
1:0 ???
1:1 b4
1:2 b8
1:3 b12
1:4 b16
1:5 b3
1:6 b7
1:7 b11
2:0 b13
2:1 ???
2:2 ???
2:3 ???
2:4 b15
2:5 ???
2:6 ???
2:7 ???
3:0 coin
3:1 service
3:2 ???
3:3 ???
3:4 test
3:5 ???
3:6 ???
3:7 ???
*/
static const uint32_t jbhook_io_panel_mappings[] = {
(1 << 5),
(1 << 1),
(1 << 13),
(1 << 9),
(1 << 6),
(1 << 2),
(1 << 14),
(1 << 10),
(1 << 7),
(1 << 3),
(1 << 15),
(1 << 11),
(1 << 16),
(1 << 4),
(1 << 20),
(1 << 12),
};
static const uint32_t jbhook_io_sys_button_mappings[] = {
(1 << 28),
(1 << 25),
(1 << 24),
};
static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes)
{
uint32_t *inputs = (uint32_t *) resp;
uint16_t panels;
uint8_t buttons;
/* lower three bytes low active, highest byte high active */
*inputs = 0x00FFFFFF;
if (!jb_io_read_inputs()) {
log_warning("Reading inputs from jbio failed");
return;
}
panels = jb_io_get_panel_inputs();
buttons = jb_io_get_sys_inputs();
for (uint8_t i = 0; i < 16; i++) {
if (panels & (1 << i)) {
*inputs &= ~jbhook_io_panel_mappings[i];
}
}
for (uint8_t i = 0; i < 3; i++) {
if (buttons & (1 << i)) {
*inputs |= jbhook_io_sys_button_mappings[i];
}
}
}
static uint32_t jbhook_command_handle(
uint8_t cmd,
const void *payload,
uint32_t payload_len,
void *resp,
uint32_t resp_max_len)
{
switch (cmd) {
case P4IO_CMD_COINSTOCK: {
// on is 0x00, off is either 0x10 or 0x20 depending on whether it's
// during gameplay (0x10) or test menu (0x20). Both seem to have the
// same effect
jb_io_set_coin_blocker(*(uint8_t*)payload == 0x00);
// this actually returns the coinstock, don't care for it
memset(resp, 0, 4);
return 4;
}
case P4IO_CMD_SET_PORTOUT: {
const struct p4io_req_panel_mode *req =
(const struct p4io_req_panel_mode *) payload;
// always fallback to ALL if input is unknown
enum jb_io_panel_mode mode = JB_IO_PANEL_MODE_ALL;
if(req->is_single) {
switch(req->mode) {
case 0x0001:
mode = JB_IO_PANEL_MODE_TOP_LEFT;
break;
case 0x0000:
mode = JB_IO_PANEL_MODE_TOP_RIGHT;
break;
case 0x0101:
mode = JB_IO_PANEL_MODE_BOTTOM_LEFT;
break;
case 0x0100:
mode = JB_IO_PANEL_MODE_BOTTOM_RIGHT;
break;
default:
mode = JB_IO_PANEL_MODE_ALL;
break;
}
}
jb_io_set_panel_mode(mode);
memset(resp, 0, 1);
return 1;
}
default:
return 0xFFFFFFFF;
}
}
const struct p4ioemu_device_msg_hook *jbhook_p4io_init(void)
{
return &jbhook_p4io_msg;
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef JBHOOK_IO_H
#define JBHOOK_IO_H
#include "p4ioemu/device.h"
const struct p4ioemu_device_msg_hook *jbhook_p4io_init(void);
#endif
+15
View File
@@ -0,0 +1,15 @@
#ifndef JBHOOK_UTIL_SECURITY_H
#define JBHOOK_UTIL_SECURITY_H
#include "security/mcode.h"
static const struct security_mcode jbhook_util_security_default_mcode = {
.header = SECURITY_MCODE_HEADER,
.unkn = SECURITY_MCODE_UNKN_C,
.game = SECURITY_MCODE_GAME_JB_1,
.region = SECURITY_MCODE_REGION_JAPAN,
.cabinet = SECURITY_MCODE_CABINET_A,
.revision = SECURITY_MCODE_REVISION_B,
};
#endif