Compare commits

..

38 Commits

Author SHA1 Message Date
64240aa74c update readme 2024-12-27 14:06:02 -05:00
cf1ab0e13f replace hardcoded enums with #define CTL_CODEs 2024-12-21 23:19:05 -05:00
a16a2338c7 Update supported games list 2024-12-19 16:26:20 -05:00
748b3557ee remove 5gb wasted space by removing precompiled headers 2024-12-19 14:00:05 -05:00
e09bf6571e bump capnhook rev to include serial fixes 2024-10-10 16:15:23 +00:00
b7a961ff71 port clock fixes from dniel segatools 2024-08-20 10:14:10 -04:00
5becf8798c iccard: aime authenticity 2024-06-24 16:51:38 -04:00
7051b849fa Make HKB hook public, with unity option fixes 2024-06-08 00:43:06 -04:00
94f9357382 rehook ShowCursor 2024-04-07 17:55:55 -04:00
434382ce4b vfs: hook System_getAppRootPath for unity games 2024-04-07 17:55:38 -04:00
16f7272751 epay: fix print statement to work with other compilers, fixes #4 2024-03-14 11:12:48 -04:00
67ffde7289 nusec: add NUSEC_IOCTL_TD_ERASE_USED 2024-03-12 13:55:57 -04:00
eb93c6cf75 add vsprintf hook 2024-02-08 01:05:13 -05:00
ba9fb5d0b9 catch epay URLs 2023-12-14 10:36:52 -05:00
2ec0ee4794 update procaddr hook 2023-12-10 20:47:43 -05:00
ccdd07e262 mai2: move serial hook inserters 2023-12-04 03:28:38 -05:00
9d8a38bbf7 mai2: wip hook 2023-11-30 02:29:27 -05:00
4dcf01f643 carol: somewhat-working touch board?? 2023-11-27 23:23:00 -05:00
962e14dc9b ongeki: fix start.bat and segatools.ini 2023-11-05 21:51:36 -05:00
5d04685c73 update gitignore 2023-09-19 10:33:30 -04:00
528ec4379c createprocess: add replace_all flag 2023-09-15 19:57:11 -04:00
5a4e947354 carol: use createprocess hook 2023-09-15 19:52:26 -04:00
157f52da4c platform: add epay hook 2023-09-15 01:35:33 -04:00
0d83977073 hooklib: fill out my_CreateProcessA 2023-09-13 20:23:40 -04:00
dca84e08d0 hooklib: fix createprocess imports 2023-09-13 19:57:10 -04:00
2dbb4aec8c hooklib: add createprocess to meson 2023-09-13 19:54:22 -04:00
3d7d9fcaa5 hooklib: add createprocess hook skeleton 2023-09-13 17:54:40 -04:00
98d2ea1390 vfs: add hook for C:\Users\AppUser 2023-09-13 11:25:29 -04:00
4c67843f08 carol: add touch dll functions 2023-05-31 04:54:38 -04:00
02201dfba5 path: add hooks for PathFileExistsA/W 2023-05-24 01:08:08 -04:00
9113766c22 vfs: add D drive hooks 2023-05-24 01:07:56 -04:00
6fc2482c19 carol: add control board emulation, document touch board more 2023-04-28 04:25:47 -04:00
74c8b312c5 carol: fix prints 2023-04-11 00:22:41 -04:00
ef00932c64 carol: fix ports 2023-04-11 00:20:51 -04:00
8c97dc09c0 carol: fix control board request struct 2023-03-30 03:55:40 -04:00
301a0e0ce7 update build system 2023-03-19 13:29:08 -04:00
5935e322e8 Merge pull request 'fix Makefile failing on strip' (#1) from Yellowberry/segatools:master into master
Reviewed-on: Hay1tsme/segatools#1
2023-02-19 18:59:59 +00:00
05e762d3ce fix Makefile failing on strip 2023-02-18 22:58:00 -07:00
147 changed files with 4908 additions and 447 deletions

13
.gitignore vendored
View File

@ -1,6 +1,17 @@
.*.swp
.vscode/
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Suggested names for build dirs
build/

34
.vscode/settings.json vendored
View File

@ -1,3 +1,37 @@
{
"editor.formatOnSave": false,
"mesonbuild.configureOnOpen": false,
"files.associations": {
"string.h": "c",
"stdbool.h": "c",
"windows.h": "c",
"dprintf.h": "c",
"touch.h": "c",
"mai2-dll.h": "c",
"led.h": "c",
"path.h": "c",
"reg.h": "c",
"platform.h": "c",
"procaddr.h": "c",
"table.h": "c",
"serial.h": "c",
"stdarg.h": "c",
"iphlpapi.h": "c",
"iptypes.h": "c",
"netenv.h": "c",
"nusec.h": "c",
"vfs.h": "c",
"ws2ipdef.h": "c",
"winternl.h": "c",
"wincrypt.h": "c",
"assert.h": "c",
"stdint.h": "c",
"limits.h": "c",
"stdlib.h": "c",
"config.h": "c",
"mu3-dll.h": "c",
"io4.h": "c",
"dvd.h": "c",
"uart.h": "c"
},
}

View File

@ -5,16 +5,12 @@ V ?= @
BUILD_DIR := build
BUILD_DIR_32 := $(BUILD_DIR)/build32
BUILD_DIR_64 := $(BUILD_DIR)/build64
BUILD_DIR_DOCKER := $(BUILD_DIR)/docker
BUILD_DIR_ZIP := $(BUILD_DIR)/zip
DOC_DIR := doc
DIST_DIR := dist
DOCKER_CONTAINER_NAME := "segatools-build"
DOCKER_IMAGE_NAME := "segatools:build"
# -----------------------------------------------------------------------------
# Targets
# -----------------------------------------------------------------------------
@ -42,15 +38,6 @@ zip: $(BUILD_DIR_ZIP)/segatools.zip
clean:
$(V)rm -rf $(BUILD_DIR) subprojects/capnhook
.PHONY: build-docker # Build the project in a docker container
build-docker:
$(V)docker rm -f $(DOCKER_CONTAINER_NAME) 2> /dev/null || true
$(V)docker build -t $(DOCKER_IMAGE_NAME) -f Dockerfile .
$(V)docker create --name $(DOCKER_CONTAINER_NAME) $(DOCKER_IMAGE_NAME)
$(V)rm -rf $(BUILD_DIR_DOCKER)
$(V)mkdir -p $(BUILD_DIR_DOCKER)
$(V)docker cp $(DOCKER_CONTAINER_NAME):/segatools/$(BUILD_DIR_ZIP) $(BUILD_DIR_DOCKER)
# -----------------------------------------------------------------------------
# Utility, combo and alias targets
# -----------------------------------------------------------------------------

View File

@ -104,6 +104,36 @@ $(BUILD_DIR_ZIP)/mu3.zip:
$(V)strip $(BUILD_DIR_ZIP)/mu3/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mu3 ; zip -r ../mu3.zip *
$(BUILD_DIR_ZIP)/hkb.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/hkb
$(V)mkdir -p $(BUILD_DIR_ZIP)/hkb/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/hkbhook/hkbhook.dll \
$(DIST_DIR)/hkb/segatools.ini \
$(DIST_DIR)/hkb/start.bat \
$(BUILD_DIR_ZIP)/hkb
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/hkb/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/hkb/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/hkb ; zip -r ../hkb.zip *
$(BUILD_DIR_ZIP)/mai2.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/mai2
$(V)mkdir -p $(BUILD_DIR_ZIP)/mai2/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/mai2hook/mai2hook.dll \
$(DIST_DIR)/mai2/segatools.ini \
$(DIST_DIR)/mai2/start.bat \
$(BUILD_DIR_ZIP)/mai2
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/mai2/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/mai2/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mai2 ; zip -r ../mai2.zip *
$(BUILD_DIR_ZIP)/doc.zip: \
$(DOC_DIR)/config \
$(DOC_DIR)/chunihook.md \
@ -121,6 +151,8 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
$(BUILD_DIR_ZIP)/idz.zip \
$(BUILD_DIR_ZIP)/mercury.zip \
$(BUILD_DIR_ZIP)/mu3.zip \
$(BUILD_DIR_ZIP)/hkb.zip \
$(BUILD_DIR_ZIP)/mai2.zip \
CHANGELOG.md \
README.md \

View File

@ -1,33 +1,2 @@
# Segatools
Version: `v005`
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
## List of supported games
* Chunithm
* [Chunithm (Plus)](doc/chunihook.md)
* [Chunithm Air (Plus)](doc/chunihook.md)
* [Chunithm Star (Plus)](doc/chunihook.md)
* [Chunithm Amazon (Plus)](doc/chunihook.md)
* [Chunithm Crystal (Plus)](doc/chunihook.md)
* Initial D
* [Initial D Arcade Stage Zero](doc/idzhook.md)
* Wacca
* Wacca Lilly R (WIP)
## End-users
For setup and configuration guides, refer to the dedicated documents available for each game, see
[the links in the previous section](#list-of-supported-games).
## Contributors
If you are/want to be a contributor of any kind, e.g. new features, bug fixes, documentation improvements, ..., please
read the [contributing documentation](CONTRIBUTING.md), first.
## Developers
For development setup and instructions how to build the project, refer to the
[dedicated development documentation](doc/development.md).
# This repository is no longer maintained
Please see the new joint repository from [TeamTofuShop](https://gitea.tendokyu.moe/TeamTofuShop/segatools)

View File

@ -3,7 +3,6 @@ aimeio_lib = static_library(
name_prefix : '',
include_directories: inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
link_with : [
util_lib,
],

View File

@ -1,6 +1,7 @@
#include <windows.h>
#include <devioctl.h>
#include <ntdddisk.h>
#include <stdlib.h>
#include <winioctl.h>
#include <assert.h>
#include <ctype.h>
@ -19,13 +20,11 @@
#include "util/dprintf.h"
#include "util/str.h"
#pragma pack(push, 1)
#define DS_IOCTL_GET_ABI_VERSION CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
#define DS_IOCTL_SETUP CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS)
#define DS_IOCTL_READ_SECTOR CTL_CODE(0x8000, 0x804, METHOD_BUFFERED, FILE_READ_ACCESS)
enum {
DS_IOCTL_GET_ABI_VERSION = 0x80006000,
DS_IOCTL_SETUP = 0x80006004,
DS_IOCTL_READ_SECTOR = 0x80006010,
};
#pragma pack(push, 1)
struct ds_eeprom {
uint32_t crc32;

View File

@ -6,7 +6,7 @@
#include <winnt.h>
#endif
#include <devioctl.h>
#include <ntdddisk.h>
#include <winioctl.h>
#include <assert.h>
@ -20,9 +20,7 @@
#include "util/dprintf.h"
#include "util/str.h"
enum {
EEPROM_IOCTL_GET_ABI_VERSION = 0x80006000,
};
#define EEPROM_IOCTL_GET_ABI_VERSION CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
static HRESULT eeprom_handle_irp(struct irp *irp);
static HRESULT eeprom_handle_open(struct irp *irp);

View File

@ -1,5 +1,6 @@
#include <windows.h>
#include <ntstatus.h>
#include <winioctl.h>
#include <assert.h>
#include <string.h>
@ -13,12 +14,10 @@
#include "util/dprintf.h"
#include "util/str.h"
enum {
GPIO_IOCTL_SET_LEDS = 0x8000A004,
GPIO_IOCTL_GET_PSW = 0x80006008,
GPIO_IOCTL_GET_DIPSW = 0x8000600C,
GPIO_IOCTL_DESCRIBE = 0x80006014,
};
#define GPIO_IOCTL_SET_LEDS CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define GPIO_IOCTL_GET_PSW CTL_CODE(0x8000, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS)
#define GPIO_IOCTL_GET_DIPSW CTL_CODE(0x8000, 0x803, METHOD_BUFFERED, FILE_READ_ACCESS)
#define GPIO_IOCTL_DESCRIBE CTL_CODE(0x8000, 0x805, METHOD_BUFFERED, FILE_READ_ACCESS)
enum {
GPIO_TYPE_NONE = 0,

View File

@ -4,6 +4,7 @@
#include <winternl.h>
#include <ntstatus.h>
#include <winioctl.h>
#include <assert.h>
#include <stddef.h>
@ -21,11 +22,9 @@
#include "util/dump.h"
#include "util/str.h"
enum {
JVS_IOCTL_HELLO = 0x80006004,
JVS_IOCTL_SENSE = 0x8000600C,
JVS_IOCTL_TRANSACT = 0x8000E008,
};
#define JVS_IOCTL_HELLO CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS)
#define JVS_IOCTL_TRANSACT CTL_CODE(0x8000, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define JVS_IOCTL_SENSE CTL_CODE(0x8000, 0x803, METHOD_BUFFERED, FILE_READ_ACCESS)
static HRESULT jvs_handle_irp(struct irp *irp);
static HRESULT jvs_handle_open(struct irp *irp);

View File

@ -2,7 +2,6 @@ amex_lib = static_library(
'amex',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],

View File

@ -6,7 +6,7 @@
#include <winnt.h>
#endif
#include <devioctl.h>
#include <ntdddisk.h>
#include <winioctl.h>
#include <assert.h>
@ -20,9 +20,7 @@
#include "util/dprintf.h"
#include "util/str.h"
enum {
SRAM_IOCTL_GET_ABI_VERSION = 0x80006000,
};
#define SRAM_IOCTL_GET_ABI_VERSION CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
static HRESULT sram_handle_irp(struct irp *irp);
static HRESULT sram_handle_open(struct irp *irp);

View File

@ -16,6 +16,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "board/io3.h"

View File

@ -3,6 +3,7 @@
#include <windows.h>
#include <stdint.h>
#include <stdbool.h>
enum {
/* System buttons in button[0] */

View File

@ -2,7 +2,6 @@ board_lib = static_library(
'board',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],

View File

@ -21,9 +21,18 @@ const struct dll_bind_sym carol_dll_syms[] = {
}, {
.sym = "carol_io_touch_init",
.off = offsetof(struct carol_dll, touch_init),
}, {
.sym = "carol_io_ledbd_init",
.off = offsetof(struct carol_dll, ledbd_init),
}, {
.sym = "carol_io_controlbd_init",
.off = offsetof(struct carol_dll, controlbd_init),
}, {
.sym = "carol_io_touch_start",
.off = offsetof(struct carol_dll, touch_start),
}, {
.sym = "carol_io_touch_stop",
.off = offsetof(struct carol_dll, touch_stop),
}
};

View File

@ -10,7 +10,10 @@ struct carol_dll {
void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams);
void (*jvs_read_coin_counter)(uint16_t *total);
HRESULT (*touch_init)();
HRESULT (*ledbd_init)();
HRESULT (*controlbd_init)();
void (*touch_start)(carol_io_touch_callback_t callback);
void (*touch_stop)();
};
struct carol_dll_config {

View File

@ -16,4 +16,7 @@ EXPORTS
carol_io_jvs_poll
carol_io_jvs_read_coin_counter
carol_io_touch_init
carol_io_ledbd_init
carol_io_controlbd_init
carol_io_touch_start
carol_io_touch_stop

View File

@ -58,6 +58,20 @@ void controlbd_config_load(
filename);
}
void ledbd_config_load(
struct ledbd_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(
L"ledbd",
L"enable",
1,
filename);
}
void carol_hook_config_load(
struct carol_hook_config *cfg,
@ -73,4 +87,5 @@ void carol_hook_config_load(
gfx_config_load(&cfg->gfx, filename);
touch_config_load(&cfg->touch, filename);
controlbd_config_load(&cfg->controlbd, filename);
ledbd_config_load(&cfg->ledbd, filename);
}

View File

@ -13,6 +13,7 @@
#include "gfxhook/gfx.h"
#include "carolhook/touch.h"
#include "carolhook/ledbd.h"
#include "carolhook/controlbd.h"
struct carol_hook_config {
@ -22,6 +23,7 @@ struct carol_hook_config {
struct carol_dll_config dll;
struct gfx_config gfx;
struct touch_config touch;
struct ledbd_config ledbd;
struct controlbd_config controlbd;
};

View File

@ -18,10 +18,18 @@
static HRESULT controlbd_handle_irp(struct irp *irp);
static HRESULT controlbd_handle_irp_locked(struct irp *irp);
static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf);
static HRESULT controlbd_frame_dispatch(struct controlbd_req *dest);
static HRESULT controlbd_frame_decode(struct controlbd_req_any *dest, struct iobuf *src);
static HRESULT controlbd_set_header(struct controlbd_resp_hdr *resp, uint8_t cmd, uint8_t len);
static uint8_t calc_checksum(void *data, size_t len);
static HRESULT controlbd_req_nop(uint8_t cmd);
static HRESULT controlbd_req_dispatch(const struct controlbd_req_any *req);
static HRESULT controlbd_req_ack_any(uint8_t cmd);
static HRESULT controlbd_req_reset(void);
static HRESULT controlbd_req_get_board_info(void);
static HRESULT controlbd_req_firmware_checksum(void);
static HRESULT controlbd_req_polling(const struct controlbd_req_any *req);
const uint8_t CONTROLBD_SYNC_BYTE = 0xE0;
static CRITICAL_SECTION controlbd_lock;
static struct uart controlbd_uart;
@ -36,7 +44,7 @@ HRESULT controlbd_hook_init(const struct controlbd_config *cfg)
InitializeCriticalSection(&controlbd_lock);
uart_init(&controlbd_uart, 11);
uart_init(&controlbd_uart, 12);
controlbd_uart.written.bytes = controlbd_written_bytes;
controlbd_uart.written.nbytes = sizeof(controlbd_written_bytes);
controlbd_uart.readable.bytes = controlbd_readable_bytes;
@ -64,10 +72,49 @@ static HRESULT controlbd_handle_irp(struct irp *irp)
return hr;
}
static HRESULT controlbd_frame_decode(struct controlbd_req_any *req, struct iobuf *src)
{
uint8_t data_len = 0;
uint8_t checksum_pos = src->pos - 1;
uint8_t calculated_checksum = 0;
uint8_t checksum = 0;
if (src->pos < 6) {
dprintf("Control Board: Decode Error, request too short (pos is 0x%08X)\n", (int)src->pos);
return SEC_E_BUFFER_TOO_SMALL;
}
req->hdr.sync = src->bytes[0];
req->hdr.dest = src->bytes[1];
req->hdr.src = src->bytes[2];
req->hdr.len = src->bytes[3];
req->hdr.cmd = src->bytes[4];
data_len = req->hdr.len;
src->pos -= 5;
for (int i = 0; i < data_len; i++) {
if (src->pos == 0) {
break;
}
req->bytes[i] = src->bytes[i + 5];
src->pos --;
}
checksum = src->bytes[checksum_pos];
calculated_checksum = calc_checksum(req, checksum_pos);
if (checksum != calculated_checksum) {
dprintf("Control Board: Decode Error, checksum failure (expected 0x%02X, got 0x%02X)\n", calculated_checksum, checksum);
return HRESULT_FROM_WIN32(ERROR_CRC);
}
return S_OK;
}
static HRESULT controlbd_handle_irp_locked(struct irp *irp)
{
struct controlbd_req req;
HRESULT hr;
struct controlbd_req_any req;
assert(carol_dll.controlbd_init != NULL);
@ -76,7 +123,7 @@ static HRESULT controlbd_handle_irp_locked(struct irp *irp)
hr = carol_dll.controlbd_init();
if (FAILED(hr)) {
dprintf("Control Board: Backend DLL error: %x\n", (int) hr);
dprintf("Control Board: Backend DLL error: 0X%X\n", (int) hr);
return hr;
}
@ -89,90 +136,204 @@ static HRESULT controlbd_handle_irp_locked(struct irp *irp)
}
for (;;) {
if (controlbd_uart.written.bytes[0] == 0xE0) {
#if 0
dprintf("Control Board: TX Buffer:\n");
dump_iobuf(&controlbd_uart.written);
dprintf("Control Board: TX Buffer:\n");
dump_iobuf(&controlbd_uart.written);
#endif
hr = controlbd_frame_decode(&req, &controlbd_uart.written);
if (FAILED(hr)) {
dprintf("Control Board: Deframe Error: %x\n", (int) hr);
hr = controlbd_frame_decode(&req, &controlbd_uart.written);
if (FAILED(hr)) {
dprintf("Control Board: Deframe error: %x\n", (int) hr);
return hr;
}
hr = controlbd_req_dispatch(&req);
if (FAILED(hr)) {
dprintf("Control Board: Dispatch Error: 0X%X\n", (int) hr);
return hr;
}
#if 0
dprintf("Control Board: RX Buffer:\n");
dump_iobuf(&controlbd_uart.readable);
#endif
controlbd_uart.written.pos = 0;
return hr;
}
hr = controlbd_frame_dispatch(&req);
if (FAILED(hr)) {
dprintf("Control Board: Dispatch Error: %x\n", (int) hr);
// The board has a LPC111x bootloader that gets ran over every boot
// this is to account for that.
char cmd[255];
strcpy_s(cmd, 255, (char *)controlbd_uart.written.bytes);
cmd[controlbd_uart.written.pos] = '\0';
return hr;
if (!strcmp(cmd, "?")) {
dprintf("Control Board: Bootloader Hello\n");
}
else if (!strcmp(cmd, "Synchronized\r\n")) {
iobuf_write(&controlbd_uart.readable, "Synchronized\r\nNG\r\n", 19);
// Set this to OK instead of NG to do an update
}
else if (!strcmp(cmd, "12000\r\n")) {
iobuf_write(&controlbd_uart.readable, "12000\r\nOK\r\n", 12);
}
else {
// Everything other then the two commands above just want 0\r\n
// appended to the request as the response. Given that it only checks sometimes,
// it's safe to just run over the readable buffer every response.
controlbd_uart.readable.pos = 0;
cmd[controlbd_uart.written.pos] = '0';
cmd[controlbd_uart.written.pos + 1] = '\r';
cmd[controlbd_uart.written.pos + 2] = '\n';
cmd[controlbd_uart.written.pos + 3] = '\0';
// dprintf("Control Board: Return %s\n", cmd);
iobuf_write(&controlbd_uart.readable, cmd, controlbd_uart.written.pos + 3);
}
controlbd_uart.written.pos = 0;
return hr;
}
}
static HRESULT controlbd_frame_dispatch(struct controlbd_req *req)
static HRESULT controlbd_req_dispatch(const struct controlbd_req_any *req)
{
switch (req->cmd) {
case CONTROLBD_CMD_UNK_11:
return controlbd_req_nop(req->cmd);
switch (req->hdr.cmd) {
case CONTROLBD_CMD_RESET:
return controlbd_req_reset();
case CONTROLBD_CMD_BDINFO:
return controlbd_req_get_board_info();
case CONTROLBD_CMD_FIRM_SUM:
return controlbd_req_firmware_checksum();
case CONTROLBD_CMD_TIMEOUT:
dprintf("Control Board: Acknowledge Timeout\n");
return controlbd_req_ack_any(req->hdr.cmd);
case CONTROLBD_CMD_PORT_SETTING:
dprintf("Control Board: Acknowledge Port Setting\n");
return controlbd_req_ack_any(req->hdr.cmd);
case CONTROLBD_CMD_INITIALIZE:
dprintf("Control Board: Acknowledge Initialize\n");
return controlbd_req_ack_any(req->hdr.cmd);
case CONTROLBD_CMD_POLLING:
return controlbd_req_polling(req);
default:
dprintf("Unhandled command %#02x\n", req->cmd);
return S_OK;
dprintf("Unhandled command 0x%02x\n", req->hdr.cmd);
return controlbd_req_ack_any(req->hdr.cmd);
}
}
static HRESULT controlbd_req_nop(uint8_t cmd)
static HRESULT controlbd_set_header(struct controlbd_resp_hdr *resp, uint8_t cmd, uint8_t len)
{
dprintf("Control Board: No-op cmd %#02x\n", cmd);
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0xE0;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x11;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x03;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x10;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x27;
resp->sync = CONTROLBD_SYNC_BYTE;
resp->dest = 0x01;
resp->src = 0x02;
resp->len = len;
resp->report = 0x01;
resp->cmd = cmd;
return S_OK;
}
/* Decodes the response into a struct that's easier to work with. */
static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf)
static uint8_t calc_checksum(void *data, size_t len)
{
int initial_pos = iobuf->pos;
uint8_t check = 0;
uint8_t *stuff;
stuff = data;
uint8_t checksum = 0;
uint16_t tmp = 0;
dest->sync = iobuf->bytes[0];
iobuf->pos--;
dest->cmd = iobuf->bytes[1];
iobuf->pos--;
check += dest->cmd;
dest->checksum = iobuf->bytes[initial_pos - 1];
iobuf->pos--;
dest->data_length = initial_pos - 3; // sync, cmd, checksum
if (dest->data_length > 0) {
for (int i = 0; i < dest->data_length; i++) {
dest->data[i] = iobuf->bytes[i+2];
check += dest->data[i];
}
}
iobuf->pos -= dest->data_length;
if (dest->sync != 0xe0) {
dprintf("Control Board: Sync error, expected 0xe0, got %x\n", dest->sync);
return E_FAIL;
}
if (dest->checksum != check) {
dprintf("Control Board: Checksum error, expected %x, got %x\n", check, dest->checksum);
return E_FAIL;
for (int i = 1; i < len; i++) {
tmp = checksum + stuff[i];
checksum = tmp & 0xFF;
}
return S_OK;
}
return checksum;
}
static HRESULT controlbd_req_reset(void)
{
struct controlbd_resp_reset resp;
dprintf("Control Board: Reset\n");
controlbd_set_header(&resp.hdr, CONTROLBD_CMD_RESET, 2);
resp.checksum = 0;
// No data, just ack
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}
static HRESULT controlbd_req_get_board_info(void)
{
struct controlbd_resp_bdinfo resp;
memset(&resp, 0, sizeof(resp));
dprintf("Control Board: Get Board Info\n");
controlbd_set_header(&resp.hdr, CONTROLBD_CMD_BDINFO, 21);
resp.rev = 0x90;
resp.bfr_size = 0x0001;
resp.ack = 1;
strcpy_s(resp.bd_no, sizeof(resp.bd_no), "15312 ");
strcpy_s(resp.chip_no, sizeof(resp.chip_no), "6699 ");
resp.chip_no[5] = 0xFF;
resp.bd_no[8] = 0x0A;
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}
static HRESULT controlbd_req_firmware_checksum(void)
{
struct controlbd_resp_fw_checksum resp;
memset(&resp, 0, sizeof(resp));
dprintf("Control Board: Get Firmware Checksum\n");
controlbd_set_header(&resp.hdr, CONTROLBD_CMD_FIRM_SUM, 5);
resp.ack = 1;
resp.fw_checksum = 0x1b36; // This could change with an update... oh well
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}
static HRESULT controlbd_req_polling(const struct controlbd_req_any *req)
{
struct controlbd_req_polling req_struct;
memset(&req_struct, 0, sizeof(req_struct));
memcpy_s(&req_struct, sizeof(req_struct), req, sizeof(req_struct));
struct controlbd_resp_polling resp;
memset(&resp, 0, sizeof(resp));
controlbd_set_header(&resp.hdr, CONTROLBD_CMD_POLLING, 16);
// TODO: Figure out output (pen vibration, etc)
resp.ack = 1;
resp.unk7 = 3;
resp.unk8 = 1;
resp.unk9 = 1;
resp.btns_pressed = 0; // bit 1 is pen button, bit 2 is dodge
resp.coord_x = 0x0;
resp.coord_y = 0x0;
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}
static HRESULT controlbd_req_ack_any(uint8_t cmd)
{
struct controlbd_resp_any_ack resp;
memset(&resp, 0, sizeof(resp));
controlbd_set_header(&resp.hdr, cmd, 3);
resp.ack = 1;
resp.checksum = calc_checksum(&resp, sizeof(resp));
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
@ -7,15 +9,140 @@
struct controlbd_config {
bool enable;
};
enum controlbd_cmd {
CONTROLBD_CMD_UNK_11 = 0x11
};
struct controlbd_req {
uint8_t sync; // First byte is the sync
uint8_t cmd; // Command byte
uint8_t data[256]; // Request body goes here
uint8_t checksum; // Final byte is all bytes added, except the sync
uint8_t data_length; // Size of the data including command byte
#pragma pack(push, 1)
struct controlbd_req_hdr {
uint8_t sync;
uint8_t dest; // unsure
uint8_t src; // unsure
uint8_t len; // length of the rest of the request minus checksum
uint8_t cmd;
};
struct controlbd_req_any {
struct controlbd_req_hdr hdr;
uint8_t bytes[255];
};
struct controlbd_req_reset {
struct controlbd_req_hdr hdr;
uint8_t payload;
uint8_t checksum;
};
struct controlbd_req_bdinfo {
struct controlbd_req_hdr hdr;
uint8_t checksum;
};
struct controlbd_req_fw_checksum {
struct controlbd_req_hdr hdr;
uint8_t checksum;
};
struct controlbd_req_timeout {
struct controlbd_req_hdr hdr;
uint8_t unknown;
uint8_t checksum;
};
struct controlbd_req_polling {
struct controlbd_req_hdr hdr;
uint8_t unknown[20];
uint8_t checksum;
};
struct controlbd_resp_hdr {
uint8_t sync;
uint8_t dest; // unsure
uint8_t src; // unsure
uint8_t len; // length of the rest of the request minus checksum
uint8_t report; // 0x01 for success, anything else for failure
uint8_t cmd;
};
struct controlbd_resp_any {
struct controlbd_resp_hdr hdr;
uint8_t bytes[255];
};
struct controlbd_resp_any_ack {
struct controlbd_resp_hdr hdr;
uint8_t ack;
uint8_t checksum;
};
struct controlbd_resp_reset {
struct controlbd_resp_hdr hdr;
uint8_t checksum;
};
struct controlbd_resp_bdinfo {
struct controlbd_resp_hdr hdr;
uint8_t ack;
char bd_no[9];
char chip_no[6];
uint8_t rev;
uint16_t bfr_size;
uint8_t checksum;
};
struct controlbd_resp_fw_checksum {
struct controlbd_resp_hdr hdr;
uint8_t ack;
uint16_t fw_checksum;
uint8_t checksum;
};
struct controlbd_resp_polling {
struct controlbd_resp_hdr hdr;
uint8_t ack;
uint8_t unk7;
uint8_t unk8;
uint8_t unk9;
uint8_t btns_pressed;
uint8_t blob[7];
int8_t coord_x;
int8_t coord_y;
uint8_t checksum;
};
enum {
PEN_BTN_PRESSED_BIT = 1,
DODGE_BTN_PRESSED_BIT = 2
};
enum {
CONTROLBD_CMD_RESET = 0x10,
CONTROLBD_CMD_TIMEOUT = 0x11,
CONTROLBD_CMD_RETRY = 0x12,
CONTROLBD_CMD_GETIN = 0x20,
CONTROLBD_CMD_GETADI = 0x21,
CONTROLBD_CMD_SETOUTPUT = 0x30,
CONTROLBD_CMD_INITIALIZE = 0x80,
CONTROLBD_CMD_POLLING = 0x81,
CONTROLBD_CMD_CUSTOM_PATTERN = 0x82,
CONTROLBD_CMD_DEBUG_CAROL = 0x83,
CONTROLBD_CMD_POLLING_GENERAL = 0x84,
CONTROLBD_CMD_CMD_STATUS = 0x90,
CONTROLBD_CMD_PORT_SETTING = 0x91,
CONTROLBD_CMD_PWM_DUTY = 0x92,
CONTROLBD_CMD_LED_SET = 0x93,
CONTROLBD_CMD_LED_REFRESH = 0x94,
CONTROLBD_CMD_DEBUG_UART = 0xB0,
CONTROLBD_CMD_DEBUG_I2C = 0xB1,
CONTROLBD_CMD_DEBUG_STATUS = 0xC0,
CONTROLBD_CMD_BDINFO = 0xF0,
CONTROLBD_CMD_FIRM_SUM = 0xF2,
CONTROLBD_CMD_PROTOCOL = 0xF3,
CONTROLBD_CMD_UPDATE = 0xFE,
};
#pragma pack(pop)
HRESULT controlbd_hook_init(const struct controlbd_config *cfg);

View File

@ -12,13 +12,15 @@
#include "carolhook/carol-dll.h"
#include "carolhook/jvs.h"
#include "carolhook/touch.h"
#include "carolhook/ledbd.h"
#include "carolhook/controlbd.h"
#include "carolhook/serial.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "hooklib/createprocess.h"
#include "hooklib/cursor.h"
#include "platform/platform.h"
@ -30,17 +32,43 @@ static struct carol_hook_config carol_hook_cfg;
/*
COM Layout
01:(?) Touchscreen
01: Touchscreen
10: Aime reader
11: Control board
12(?): LED Board
11: LED board
12: Control Board
*/
static DWORD CALLBACK carol_pre_startup(void)
{
HRESULT hr;
HMODULE d3dc;
HMODULE dbghelp;
dprintf("--- Begin carol_pre_startup ---\n");
if ( !SetProcessDPIAware() )
dprintf("Failed to set process DPI awareness level!\n");
/* Pin the D3D shader compiler. This makes startup much faster. */
d3dc = LoadLibraryW(L"D3DCompiler_43.dll");
if (d3dc != NULL) {
dprintf("Pinned shader compiler, hMod=%p\n", d3dc);
} else {
dprintf("Failed to load shader compiler!\n");
}
/* Pin dbghelp so the path hooks apply to it. */
dbghelp = LoadLibraryW(L"dbghelp.dll");
if (dbghelp != NULL) {
dprintf("Pinned debug helper library, hMod=%p\n", dbghelp);
} else {
dprintf("Failed to load debug helper library!\n");
}
cursor_hook_init();
/* Config load */
@ -81,9 +109,7 @@ static DWORD CALLBACK carol_pre_startup(void)
}
gfx_hook_init(&carol_hook_cfg.gfx);
gfx_d3d9_hook_init(&carol_hook_cfg.gfx, carol_hook_mod);
//serial_init();
gfx_d3d9_hook_init(&carol_hook_cfg.gfx, carol_hook_mod);
hr = touch_hook_init(&carol_hook_cfg.touch);
@ -91,12 +117,23 @@ static DWORD CALLBACK carol_pre_startup(void)
goto fail;
}
hr = ledbd_hook_init(&carol_hook_cfg.ledbd);
if (FAILED(hr)) {
goto fail;
}
hr = controlbd_hook_init(&carol_hook_cfg.controlbd);
if (FAILED(hr)) {
goto fail;
}
hr = createprocess_push_hook_a(".\\15312firm\\firmupdate_1113.exe", "inject -d -k carolhook.dll ", NULL, false);
if (FAILED(hr)) {
goto fail;
}
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");

168
carolhook/ledbd.c Normal file
View File

@ -0,0 +1,168 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "hook/iobuf.h"
#include "hook/iohook.h"
#include "carolhook/carol-dll.h"
#include "carolhook/ledbd.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
#include "board/slider-frame.h"
#include "board/slider-cmd.h"
static HRESULT ledbd_handle_irp(struct irp *irp);
static HRESULT ledbd_handle_irp_locked(struct irp *irp);
static HRESULT ledbd_frame_dispatch(const union slider_req_any *dest);
static HRESULT ledbd_req_noop(uint8_t cmd);
static HRESULT ledbd_req_unk7c(uint8_t cmd);
static HRESULT ledbd_req_unkF0(uint8_t cmd);
static CRITICAL_SECTION ledbd_lock;
static struct uart ledbd_uart;
static uint8_t ledbd_written_bytes[520];
static uint8_t ledbd_readable_bytes[520];
HRESULT ledbd_hook_init(const struct ledbd_config *cfg)
{
if (!cfg->enable) {
return S_OK;
}
InitializeCriticalSection(&ledbd_lock);
uart_init(&ledbd_uart, 11);
ledbd_uart.written.bytes = ledbd_written_bytes;
ledbd_uart.written.nbytes = sizeof(ledbd_written_bytes);
ledbd_uart.readable.bytes = ledbd_readable_bytes;
ledbd_uart.readable.nbytes = sizeof(ledbd_readable_bytes);
dprintf("LED Board: Init\n");
return iohook_push_handler(ledbd_handle_irp);
}
static HRESULT ledbd_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&ledbd_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&ledbd_lock);
hr = ledbd_handle_irp_locked(irp);
LeaveCriticalSection(&ledbd_lock);
return hr;
}
static HRESULT ledbd_handle_irp_locked(struct irp *irp)
{
union slider_req_any req;
struct iobuf req_iobuf;
HRESULT hr;
assert(carol_dll.ledbd_init != NULL);
if (irp->op == IRP_OP_OPEN) {
dprintf("LED Board: Starting backend DLL\n");
hr = carol_dll.ledbd_init();
if (FAILED(hr)) {
dprintf("LED Board: Backend DLL error: 0X%X\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&ledbd_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("LED Board: TX Buffer:\n");
dump_iobuf(&ledbd_uart.written);
#endif
req_iobuf.bytes = req.bytes;
req_iobuf.nbytes = sizeof(req.bytes);
req_iobuf.pos = 0;
hr = slider_frame_decode(&req_iobuf, &ledbd_uart.written);
if (FAILED(hr)) {
dprintf("LED Board: Deframe Error: 0X%X\n", (int) hr);
return hr;
}
hr = ledbd_frame_dispatch(&req);
if (FAILED(hr)) {
dprintf("LED Board: Dispatch Error: 0X%X\n", (int) hr);
return hr;
}
return hr;
}
}
static HRESULT ledbd_frame_dispatch(const union slider_req_any *req)
{
switch (req->hdr.cmd) {
case LEDBD_CMD_UNK_10:
return ledbd_req_noop(req->hdr.cmd);
case LEDBD_CMD_UNK_7C:
return ledbd_req_unk7c(req->hdr.cmd);
case LEDBD_CMD_UNK_F0:
return ledbd_req_unkF0(req->hdr.cmd);
case LEDBD_CMD_UNK_30:
return ledbd_req_noop(req->hdr.cmd);
default:
//dprintf("Unhandled command 0x%02X\n", req->cmd);
return ledbd_req_noop(req->hdr.cmd);
}
}
static HRESULT ledbd_req_noop(uint8_t cmd)
{
//dprintf("LED Board: Noop cmd 0x%02X\n", cmd);
uint8_t resp[] = { 0xE0, 0x01, 0x11, 0x03, 0x01, 0x00, 0x01, 0x17 };
resp[5] = cmd;
resp[7] = 0x17 + cmd;
iobuf_write(&ledbd_uart.readable, resp, 8);
return S_OK;
}
static HRESULT ledbd_req_unk7c(uint8_t cmd)
{
//dprintf("LED Board: Cmd 0x7C\n");
uint8_t resp[] = { 0xE0, 0x01, 0x11, 0x04, 0x01, 0x7C, 0x01, 0x07, 0x9B };
iobuf_write(&ledbd_uart.readable, resp, 9);
return S_OK;
}
static HRESULT ledbd_req_unkF0(uint8_t cmd)
{
//dprintf("LED Board: Cmd 0xF0\n");
uint8_t resp[] = { 0xE0, 0x01, 0x11, 0x0A, 0x01, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E };
iobuf_write(&ledbd_uart.readable, resp, 16);
return S_OK;
}

28
carolhook/ledbd.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct ledbd_config {
bool enable;
};
enum ledbd_cmd {
LEDBD_CMD_UNK_10 = 0x10,
LEDBD_CMD_UNK_7C = 0x7C,
LEDBD_CMD_UNK_30 = 0x30,
LEDBD_CMD_UNK_F0 = 0xF0,
};
struct ledbd_req {
uint8_t sync; // Sync byte, always 0xE0
uint8_t dest; // command destination id?
uint8_t src; // command source id?
uint8_t data_len; // length of the proceeding data bytes
uint8_t cmd; // might be the command byte?
uint8_t data[255]; // rest of the data, len = data_len - 1
uint8_t checksum; // final byte is all bytes (excluding sync) added
};
HRESULT ledbd_hook_init(const struct ledbd_config *cfg);

View File

@ -4,7 +4,6 @@ shared_library(
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'carolhook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
@ -32,7 +31,7 @@ shared_library(
'touch.h',
'controlbd.c',
'controlbd.h',
'serial.c',
'serial.h',
'ledbd.c',
'ledbd.h',
],
)

View File

@ -1,39 +0,0 @@
#include <windows.h>
#include <winbase.h>
#include "hook/table.h"
#include "util/dprintf.h"
static BOOL WINAPI my_SetCommState(HANDLE hFile, LPDCB lpDCB);
static BOOL (WINAPI *next_SetCommState)(HANDLE hFile, LPDCB lpDCB);
static void com_hook_insert_hooks(HMODULE target);
static const struct hook_symbol win32_hooks[] = {
{
.name = "SetCommState",
.patch = my_SetCommState,
.link = (void **) &next_SetCommState
}
};
void serial_init()
{
com_hook_insert_hooks(NULL);
dprintf("Serial: Spy init\n");
}
static void com_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
win32_hooks,
_countof(win32_hooks));
}
static BOOL WINAPI my_SetCommState(HANDLE hFile, LPDCB lpDCB)
{
dprintf("Serial: my_SetCommState with baudrate %ld\n", lpDCB->BaudRate);
return next_SetCommState(hFile, lpDCB);
}

View File

@ -1,5 +0,0 @@
#pragma once
#include <windows.h>
#include <winbase.h>
void serial_init();

View File

@ -8,19 +8,46 @@
#include "carolhook/carol-dll.h"
#include "carolhook/touch.h"
#include "hook/table.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
/**
* CMDS for touch thing
* CX -> Calibrate Extend, preform callibration
* MS -> Mode Stream, enters stream mode
* R -> Reset, resets the device
* RD -> Reset Default, Resets the device to factory
* Z -> Null, keepalive command
* NM -> Name, return the name of the device (not documented?)
* OI -> Output Identity, output unique device identity, SC followed by 4 characters
* UT -> Unit Type, returns controller unit type + status
*/
static HRESULT touch_handle_irp(struct irp *irp);
static HRESULT touch_handle_irp_locked(struct irp *irp);
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf);
static HRESULT handle_touch_ack_cmd(const struct touch_req *req);
static HRESULT handle_touch_name_cmd(const struct touch_req *req);
static HRESULT handle_touch_id_cmd(const struct touch_req *req);
static HRESULT handle_touch_unit_type_cmd(const struct touch_req *req);
static void touch_scan_auto(const bool is_pressed, const uint16_t mouse_x, const uint16_t mouse_y);
static CRITICAL_SECTION touch_lock;
static struct uart touch_uart;
static uint8_t touch_written_bytes[520];
static uint8_t touch_readable_bytes[520];
static uint8_t touch_written_bytes[528];
static uint8_t touch_readable_bytes[528];
static bool should_stream = false;
static bool last_pressed;
static uint8_t last_x1;
static uint8_t last_x2;
static uint8_t last_y1;
static uint8_t last_y2;
HRESULT touch_hook_init(const struct touch_config *cfg)
{
@ -69,9 +96,10 @@ static HRESULT touch_handle_irp_locked(struct irp *irp)
if (irp->op == IRP_OP_OPEN) {
dprintf("Touchscreen: Starting backend DLL\n");
hr = carol_dll.touch_init();
carol_dll.touch_start(touch_scan_auto);
if (FAILED(hr)) {
dprintf("Touchscreen: Backend DLL error: %x\n", (int) hr);
dprintf("Touchscreen: Backend DLL error: %X\n", (int) hr);
return hr;
}
@ -84,35 +112,142 @@ static HRESULT touch_handle_irp_locked(struct irp *irp)
}
for (;;) {
#if 1
#if 0
dprintf("Touchscreen: TX Buffer:\n");
dump_iobuf(&touch_uart.written);
#endif
hr = touch_frame_decode(&req, &touch_uart.written);
if (FAILED(hr)) {
dprintf("Touchscreen: Deframe Error: %x\n", (int) hr);
dprintf("Touchscreen: Deframe Error: %X\n", (int) hr);
return hr;
}
if (!strcmp("Z", (char *)req.cmd)) {
hr = handle_touch_ack_cmd(&req);
}
else if (!strcmp("OI", (char *)req.cmd)) {
hr = handle_touch_id_cmd(&req);
}
else if (!strcmp("UT", (char *)req.cmd)) {
hr = handle_touch_unit_type_cmd(&req);
}
else if (!strcmp("NM", (char *)req.cmd)) {
hr = handle_touch_name_cmd(&req);
}
else if (!strcmp("R", (char *)req.cmd)) {
carol_dll.touch_stop();
dprintf("Touch: Reset\n");
hr = handle_touch_ack_cmd(&req);
}
else {
dprintf("Touchscreen: Unhandled cmd %s\n", (char *)req.cmd);
hr = handle_touch_ack_cmd(&req);
}
return hr;
}
}
static HRESULT handle_touch_ack_cmd(const struct touch_req *req)
{
return iobuf_write(&touch_uart.readable, "\0010\015", 3);
}
static HRESULT handle_touch_name_cmd(const struct touch_req *req)
{
dprintf("Touch: Get Name\n");
return iobuf_write(&touch_uart.readable, "\001AD1000\015", 15);
}
static HRESULT handle_touch_id_cmd(const struct touch_req *req)
{
dprintf("Touch: Get ID\n");
return iobuf_write(&touch_uart.readable, "\001AD1000\015", 8);
}
static HRESULT handle_touch_unit_type_cmd(const struct touch_req *req)
{
dprintf("Touch: Get Unit Type\n");
return iobuf_write(&touch_uart.readable, "\001AD****00\015", 8);
}
static void touch_scan_auto(const bool is_pressed, const uint16_t mouse_x, const uint16_t mouse_y)
{
struct touch_auto_resp resp;
uint16_t tmp_x;
uint16_t tmp_y;
bool flg = false;
memset(&resp, 0, sizeof(resp));
resp.touches[0].status |= 1 << 7;
if (is_pressed) {
resp.touches[0].status |= (1 << 7) | (1 << 6);
resp.touches[0].touch_id = 1;
tmp_x = mouse_x & 0x7FFF;
tmp_y = mouse_y & 0x7FFF;
resp.touches[0].x1 = tmp_x & 0x7F;
resp.touches[0].x2 = (tmp_x >> 7) & 0x7F;
resp.touches[0].y1 = tmp_y & 0x7F;
resp.touches[0].y2 = (tmp_y >> 7) & 0x7F;
flg = resp.touches[0].x1 != last_x1 || resp.touches[0].x2 != last_x2 || resp.touches[0].y1 != last_y1 || resp.touches[0].y2 != last_y2;
#if 1
if (flg)
dprintf("Touch: Mouse down! x %02X %02X y: %02X %02X\n", resp.touches[0].x1, resp.touches[0].x2, resp.touches[0].y1, resp.touches[0].y2);
#endif
last_x1 = resp.touches[0].x1;
last_x2 = resp.touches[0].x2;
last_y1 = resp.touches[0].y1;
last_y2 = resp.touches[0].y2;
} else if (last_pressed) {
resp.touches[0].x1 = last_x1;
resp.touches[0].x2 = last_x2;
resp.touches[0].y1 = last_y1;
resp.touches[0].y2 = last_y2;
}
last_pressed = is_pressed;
EnterCriticalSection(&touch_lock);
iobuf_write(&touch_uart.readable, &resp, sizeof(resp));
LeaveCriticalSection(&touch_lock);
#if 0
dprintf("Touch: RX Buffer: (pos %08x)\n", (uint32_t)touch_uart.readable.pos);
dump_iobuf(&touch_uart.readable);
#endif
}
/* Decodes the response into a struct that's easier to work with. */
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf)
{
dest->cmd = iobuf->bytes[0];
iobuf->pos--;
dest->data_length = iobuf->pos;
dest->sync = iobuf->bytes[0];
memset(dest->cmd, 0, sizeof(dest->cmd));
size_t data_len = 0;
if (dest->data_length > 0) {
for (int i = 1; i < dest->data_length; i++) {
dest->data[i-1] = iobuf->bytes[i];
}
for (int i = 1; i < 255; i++) {
if (iobuf->bytes[i] == 0x0D) { break; }
dest->cmd[i - 1] = iobuf->bytes[i];
data_len++;
}
dest->tail = iobuf->bytes[data_len + 1];
dest->data_len = data_len;
iobuf->pos = 0;
if (dest->sync != 1 || dest->tail != 0x0D) {
dprintf("Touch: Data recieve error, sync: 0x%02X (expected 0x01) tail: 0x%02X (expected 0x0D)", dest->sync, dest->tail);
return E_FAIL;
}
iobuf->pos -= dest->data_length;
return S_OK;
}
}

View File

@ -4,14 +4,42 @@
#include <stdbool.h>
#include <stdint.h>
#pragma pack(push, 1)
struct touch_config {
bool enable;
unsigned int port_no;
char board_id[7];
char unit_type[9];
};
// Always starts with 0x01, always ends with 0x0D
struct touch_req {
uint8_t cmd; // First byte is the command byte
uint8_t data[256]; // rest of the data goes here
uint8_t data_length; // Size of the data including command byte
uint8_t sync; // Always 0x01
uint8_t cmd[256]; // rest of the data goes here
uint8_t tail; // Always 0x0D
size_t data_len; // length of data
};
struct touch_report {
uint8_t status;
uint8_t x1;
uint8_t x2;
uint8_t y1;
uint8_t y2;
uint8_t touch_id;
};
struct touch_auto_resp {
struct touch_report touches[10];
};
enum {
TOUCH_MODE_STREAM = 0x01,
TOUCH_MODE_DOWN_UP = 0x02,
TOUCH_MODE_INACTIVE = 0x03,
};
#pragma pack(pop)
HRESULT touch_hook_init(const struct touch_config *cfg);

View File

@ -7,10 +7,16 @@
#include "carolio/carolio.h"
#include "carolio/config.h"
#include "util/dprintf.h"
static unsigned int __stdcall carol_io_touch_thread_proc(void *ctx);
static bool carol_io_coin;
static uint16_t carol_io_coins;
static struct carol_io_config carol_io_cfg;
static bool carol_io_touch_stop_flag;
static HANDLE carol_io_touch_thread;
static bool carol_io_window_focus = false;
uint16_t carol_io_get_api_version(void)
{
@ -73,7 +79,103 @@ HRESULT carol_io_touch_init()
return S_OK;
}
HRESULT carol_io_ledbd_init()
{
return S_OK;
}
HRESULT carol_io_controlbd_init()
{
return S_OK;
}
void carol_io_touch_start(carol_io_touch_callback_t callback)
{
if (carol_io_touch_thread != NULL) {
return;
}
carol_io_touch_stop_flag = false;
carol_io_touch_thread = (HANDLE) _beginthreadex(
NULL,
0,
carol_io_touch_thread_proc,
callback,
0,
NULL
);
}
void carol_io_touch_stop()
{
carol_io_touch_stop_flag = true;
}
void check_fg_wind(void)
{
HWND hwnd = GetForegroundWindow();
wchar_t window_class[MAX_PATH];
/* Unlike every other game, we can't use GetWindowText here. Why?
From MSDN:
"If the window does not have a caption, the return value is a null
string. This behavior is by design. It allows applications to call
GetWindowText without becoming unresponsive if the process that owns
the target window is not responding. However, if the target window
is not responding and it belongs to the calling application,
GetWindowText will cause the calling application to become
unresponsive."
Great, thanks Microsoft, very cool. Luckily Carol sets its class
name to the window title too so we can use that. */
GetClassNameW(hwnd, window_class, MAX_PATH);
if (wcscmp(window_class, L"WONDER Master")) {
if (carol_io_window_focus) {
dprintf("Carol IO: Window focus lost\n");
carol_io_window_focus = false;
}
} else if (!carol_io_window_focus) {
dprintf("Carol IO: Window focus regained\n");
carol_io_window_focus = true;
}
}
static unsigned int __stdcall carol_io_touch_thread_proc(void *ctx)
{
carol_io_touch_callback_t callback;
bool mouse_is_down = false;
uint16_t mX = 0;
uint16_t mY = 0;
POINT lpPoint;
HWND hwnd;
callback = ctx;
while (!carol_io_touch_stop_flag) {
check_fg_wind();
if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) {
mouse_is_down = true;
if (GetCursorPos(&lpPoint)) {
hwnd = GetForegroundWindow();
if (ScreenToClient(hwnd, &lpPoint)) {
if (lpPoint.x < 0) lpPoint.x = 0;
if (lpPoint.y < 0) lpPoint.y = 0;
mX = (uint16_t)lpPoint.x;
mY = (uint16_t)lpPoint.y;
}
}
} else {
mouse_is_down = false;
}
callback(mouse_is_down, mX, mY);
Sleep(1);
}
return 0;
}

View File

@ -5,6 +5,8 @@
#include <stdbool.h>
#include <stdint.h>
typedef void (*carol_io_touch_callback_t)(const bool is_pressed, const uint16_t mouse_x, const uint16_t mouse_y);
/* Get the version of the Project carol 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
@ -49,4 +51,10 @@ void carol_io_jvs_read_coin_counter(uint16_t *out);
HRESULT carol_io_touch_init();
HRESULT carol_io_controlbd_init();
HRESULT carol_io_ledbd_init();
HRESULT carol_io_controlbd_init();
void carol_io_touch_start(carol_io_touch_callback_t callback);
void carol_io_touch_stop();

View File

@ -3,7 +3,6 @@ carolio_lib = static_library(
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'carolio.c',
'carolio.h',

View File

@ -3,6 +3,7 @@
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "amex/amex.h"
#include "amex/config.h"

View File

@ -4,7 +4,6 @@ shared_library(
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'chunihook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),

View File

@ -3,6 +3,7 @@
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "chuniio/chuniio.h"
#include "chuniio/config.h"

View File

@ -1,5 +1,6 @@
#include <windows.h>
#include <stdlib.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>

View File

@ -3,7 +3,6 @@ chuniio_lib = static_library(
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'chuniio.c',
'chuniio.h',

View File

@ -1,11 +1,12 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "cxbhook/led.h"
#include "cxbhook/cxb-dll.h"
#include "hooklib/procaddr.h"
#include "hook/procaddr.h"
#include "hook/table.h"
@ -50,7 +51,7 @@ static struct hook_symbol lamp_syms[] = {
HRESULT led_hook_init(struct led_config *cfg)
{
dprintf("LED: Init\n");
return proc_addr_table_push("CommLamp.dll", lamp_syms, _countof(lamp_syms));
return proc_addr_table_push(NULL, "CommLamp.dll", lamp_syms, _countof(lamp_syms));
}
static int my_cCommLamp_Open(char *port)

View File

@ -4,7 +4,6 @@ shared_library(
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'cxbhook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),

View File

@ -3,6 +3,7 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
struct network_config {
bool enable;

View File

@ -2,11 +2,12 @@
#include <stdbool.h>
#include <stdint.h>
#include <winuser.h>
#include <stdlib.h>
#include "cxbhook/revio.h"
#include "cxbhook/cxb-dll.h"
#include "hooklib/procaddr.h"
#include "hook/procaddr.h"
#include "hook/table.h"
@ -83,7 +84,7 @@ static struct hook_symbol revio_syms[] = {
HRESULT revio_hook_init(struct revio_config *cfg)
{
dprintf("Revio: Init\n");
return proc_addr_table_push("CommIo.dll", revio_syms, _countof(revio_syms));
return proc_addr_table_push(NULL, "CommIo.dll", revio_syms, _countof(revio_syms));
}
static int my_cCommIo_Open(char *port)

View File

@ -3,7 +3,6 @@ cxbio_lib = static_library(
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'cxbio.c',
'cxbio.h',

41
dist/hkb/segatools.ini vendored Normal file
View File

@ -0,0 +1,41 @@
[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.155.0
[gfx]
enable=1
[io4]
test=0x31
service=0x32
coin=0x33

11
dist/hkb/start.bat vendored Normal file
View File

@ -0,0 +1,11 @@
@echo off
pushd %~dp0
taskkill /f /im amdaemon.exe > nul 2>&1
start inject.exe -d -k hkbhook.dll amdaemon.exe -f -c common.json terminal.json satellite.json
inject.exe -d -k hkbhook.dll hkb.exe -screen-fullscreen 0 -screen-quality Fantastic -silent-crashes -logFile gameLog.txt
taskkill /f /im amdaemon.exe > nul 2>&1
echo Game processes have terminated

44
dist/mai2/segatools.ini vendored Normal file
View File

@ -0,0 +1,44 @@
[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.172.0
[gfx]
enable=1
[io4]
; Delete
test=0x2E
; End
service=0x23
; Insert
coin=0x2D

11
dist/mai2/start.bat vendored Normal file
View File

@ -0,0 +1,11 @@
@echo off
pushd %~dp0
taskkill /f /im amdaemon.exe > nul 2>&1
start inject -d -k mai2hook.dll amdaemon.exe -f -c config_client.json config_common.json config_server.json
inject.exe -d -k mai2hook.dll Sinmai.exe -screen-fullscreen 0 -screen-width 2160 -screen-height 1920
taskkill /f /im amdaemon.exe > nul 2>&1
echo Game processes have terminated

View File

@ -30,7 +30,7 @@ enable=1
; 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
subnet=192.168.162.0
[gfx]
enable=1
@ -57,4 +57,4 @@ RIGHT_SIDE=0x55
SLIDER_LEFT=0x54
SLIDER_RIGHT=0x59
;Change move speed of slider when use dinput
SLIDER_SPEED=1000
SLIDER_SPEED=1000

8
dist/mu3/start.bat vendored
View File

@ -3,12 +3,8 @@ pushd %~dp0
taskkill /f /im amdaemon.exe > nul 2>&1
REM USA
REM start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_usa.json
REM JP
start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_jpn.json
inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury/Binaries/Win64/Mercury-Win64-Shipping.exe
start inject -d -k mu3hook.dll amdaemon.exe -f -c config_client.json config_common.json config_server.json
inject -d -k mu3hook.dll mu3.exe
taskkill /f /im amdaemon.exe > nul 2>&1

View File

@ -4,7 +4,6 @@ shared_library(
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'divahook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),

View File

@ -3,7 +3,6 @@ divaio_lib = static_library(
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'divaio.c',
'divaio.h',

View File

@ -2,7 +2,6 @@
setlocal enabledelayedexpansion
:: Static Environment Variables
set BUILD_OUTPUT_PATH=build\docker
set IMAGE_NAME=djhackers/segatools-build:latest
set CONTAINER_NAME=segatools-build

View File

@ -2,7 +2,6 @@ gfxhook_lib = static_library(
'gfxhook',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
dxguid_lib,

53
hkbhook/config.c Normal file
View File

@ -0,0 +1,53 @@
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include "board/config.h"
#include "gfxhook/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "hkbhook/config.h"
#include "platform/config.h"
void hkb_dll_config_load(
struct hkb_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"hkbio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void hkb_led_config_load(
struct hkb_led_config *cfg,
const wchar_t *filename)
{
cfg->enable = GetPrivateProfileIntW(L"led", L"enable", 1, filename);
}
void hkb_hook_config_load(
struct hkb_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);
hkb_dll_config_load(&cfg->dll, filename);
hkb_led_config_load(&cfg->led, filename);
}

33
hkbhook/config.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "gfxhook/gfx.h"
#include "hooklib/dvd.h"
#include "hkbhook/hkb-dll.h"
#include "platform/config.h"
#include "hkbhook/led.h"
struct hkb_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct gfx_config gfx;
struct hkb_dll_config dll;
struct hkb_led_config led;
};
void hkb_dll_config_load(
struct hkb_dll_config *cfg,
const wchar_t *filename);
void hkb_hook_config_load(
struct hkb_hook_config *cfg,
const wchar_t *filename);

154
hkbhook/dllmain.c Normal file
View File

@ -0,0 +1,154 @@
#include <windows.h>
#include <stdlib.h>
#include "board/io4.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "gfxhook/d3d9.h"
#include "gfxhook/d3d11.h"
#include "gfxhook/dxgi.h"
#include "gfxhook/gfx.h"
#include "hook/process.h"
#include "hooklib/dvd.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "hooklib/cursor.h"
#include "hkbhook/config.h"
#include "hkbhook/io4.h"
#include "hkbhook/led.h"
#include "hkbhook/hkb-dll.h"
#include "hkbhook/unity.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE hkb_hook_mod;
static process_entry_t hkb_startup;
static struct hkb_hook_config hkb_hook_cfg;
static DWORD CALLBACK hkb_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin hkb_pre_startup ---\n");
/* Load config */
hkb_hook_config_load(&hkb_hook_cfg, L".\\segatools.ini");
/* Hook Win32 APIs */
dvd_hook_init(&hkb_hook_cfg.dvd, hkb_hook_mod);
gfx_hook_init(&hkb_hook_cfg.gfx);
gfx_d3d9_hook_init(&hkb_hook_cfg.gfx, hkb_hook_mod);
gfx_d3d11_hook_init(&hkb_hook_cfg.gfx, hkb_hook_mod);
gfx_dxgi_hook_init(&hkb_hook_cfg.gfx, hkb_hook_mod);
serial_hook_init();
/* Initialize emulation hooks */
hr = platform_hook_init(
&hkb_hook_cfg.platform,
"SDEC",
"ACA2",
hkb_hook_mod);
if (FAILED(hr)) {
goto fail;
}
cursor_hook_init();
// COM1: Touch
// COM2: LED (Satalite)
// COM3: Reader (Satalite)
// COM3: LED (Terminal)
if (FAILED(hr)) {
goto fail;
}
if (hkb_hook_cfg.platform.nusec.platform_id[0] != '\0' && hkb_hook_cfg.platform.nusec.platform_id[3] == '4') {
hr = led_hook_init(&hkb_hook_cfg.led, 3);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&hkb_hook_cfg.aime, 1, hkb_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(4);
}
else {
hr = led_hook_init(&hkb_hook_cfg.led, 2);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&hkb_hook_cfg.aime, 3, hkb_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(1);
}
if (FAILED(hr)) {
goto fail;
}
hr = hkb_dll_init(&hkb_hook_cfg.dll, hkb_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = hkb_io4_hook_init(&hkb_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
/* Initialize Unity native plugin DLL hooks
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `hkbhook` initialization. */
unity_hook_init();
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
dprintf("--- End hkb_pre_startup ---\n");
/* Jump to EXE start address */
return hkb_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
hkb_hook_mod = mod;
hr = process_hijack_startup(hkb_pre_startup, &hkb_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

103
hkbhook/hkb-dll.c Normal file
View File

@ -0,0 +1,103 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "hkbhook/hkb-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym hkb_dll_syms[] = {
{
.sym = "hkb_io_init",
.off = offsetof(struct hkb_dll, init),
}, {
.sym = "hkb_io_poll",
.off = offsetof(struct hkb_dll, poll),
},
};
struct hkb_dll hkb_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 hkb_dll_init(const struct hkb_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("Ongeki IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Ongeki IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "hkb_io_get_api_version");
if (get_api_version != NULL) {
hkb_dll.api_version = get_api_version();
} else {
hkb_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose hkb_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (hkb_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Ongeki IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
hkb_dll.api_version);
goto end;
}
sym = hkb_dll_syms;
hr = dll_bind(&hkb_dll, src, &sym, _countof(hkb_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Ongeki 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;
}

19
hkbhook/hkb-dll.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <windows.h>
#include "hkbio/hkbio.h"
struct hkb_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(uint8_t *opbtn, uint8_t *gamebtn);
};
struct hkb_dll_config {
wchar_t path[MAX_PATH];
};
extern struct hkb_dll hkb_dll;
HRESULT hkb_dll_init(const struct hkb_dll_config *cfg, HINSTANCE self);

22
hkbhook/hkbhook.def Normal file
View File

@ -0,0 +1,22 @@
LIBRARY hkbhook
EXPORTS
CreateDXGIFactory
CreateDXGIFactory1
CreateDXGIFactory2
D3D11CreateDevice
D3D11CreateDeviceAndSwapChain
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
hkb_io_get_api_version
hkb_io_init
hkb_io_poll

109
hkbhook/io4.c Normal file
View File

@ -0,0 +1,109 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/io4.h"
#include "hkbhook/hkb-dll.h"
#include "util/dprintf.h"
bool hkb_io_coin = false;
uint16_t hkb_io_coins = 0;
static HRESULT hkb_io4_poll(void *ctx, struct io4_state *state);
static const struct io4_ops hkb_io4_ops = {
.poll = hkb_io4_poll,
};
HRESULT hkb_io4_hook_init(const struct io4_config *cfg)
{
HRESULT hr;
assert(hkb_dll.init != NULL);
hr = io4_hook_init(cfg, &hkb_io4_ops, NULL);
if (FAILED(hr)) {
return hr;
}
return hkb_dll.init();
}
static HRESULT hkb_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn;
uint8_t gamebtn;
HRESULT hr;
assert(hkb_dll.poll != NULL);
memset(state, 0, sizeof(*state));
hr = hkb_dll.poll(&opbtn, &gamebtn);
if (FAILED(hr)) {
return hr;
}
if (opbtn & HKB_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & HKB_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
if (opbtn & HKB_IO_OPBTN_COIN) {
if (!hkb_io_coin) {
hkb_io_coin = true;
hkb_io_coins++;
state->buttons[0] |= 1 << 25;
}
}
else {
hkb_io_coin = false;
}
state->chutes[0] = 128 + 256 * hkb_io_coins;
if (gamebtn & HKB_IO_GAMEBTN_RIGHT) {
state->buttons[1] |= 1 << 1;
}
if (gamebtn & HKB_IO_GAMEBTN_LEFT) {
state->buttons[1] |= 1 << 0;
}
if (gamebtn & HKB_IO_GAMEBTN_UP) {
state->buttons[1] |= 1 << 15;
}
if (gamebtn & HKB_IO_GAMEBTN_DOWN) {
state->buttons[1] |= 1 << 14;
}
if (gamebtn & HKB_IO_GAMEBTN_ENTER) {
state->buttons[1] |= 1 << 13;
}
if (gamebtn & HKB_IO_GAMEBTN_CANCEL) {
state->buttons[1] |= 1 << 12;
}
if (gamebtn & HKB_IO_GAMEBTN_ARR_RIGHT) {
state->buttons[1] |= 1 << 2;
}
if (gamebtn & HKB_IO_GAMEBTN_ARR_LEFT) {
state->buttons[1] |= 1 << 3;
}
return S_OK;
}

7
hkbhook/io4.h Normal file
View File

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

99
hkbhook/led.c Normal file
View File

@ -0,0 +1,99 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
#include "hkbhook/led.h"
static HRESULT hkb_led_handle_irp(struct irp *irp);
static HRESULT hkb_led_handle_irp_locked(struct irp *irp);
static CRITICAL_SECTION hkb_led_lock;
static bool hkb_led_started;
static struct uart hkb_led_uart;
static uint8_t hkb_led_written_bytes[520];
static uint8_t hkb_led_readable_bytes[520];
HRESULT led_hook_init(const struct hkb_led_config *cfg, uint8_t port)
{
if (!cfg->enable) {
return S_OK;
}
InitializeCriticalSection(&hkb_led_lock);
uart_init(&hkb_led_uart, port);
hkb_led_uart.written.bytes = hkb_led_written_bytes;
hkb_led_uart.written.nbytes = sizeof(hkb_led_written_bytes);
hkb_led_uart.readable.bytes = hkb_led_readable_bytes;
hkb_led_uart.readable.nbytes = sizeof(hkb_led_readable_bytes);
return iohook_push_handler(hkb_led_handle_irp);
}
static HRESULT hkb_led_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&hkb_led_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&hkb_led_lock);
hr = hkb_led_handle_irp_locked(irp);
LeaveCriticalSection(&hkb_led_lock);
return hr;
}
static HRESULT hkb_led_handle_irp_locked(struct irp *irp)
{
HRESULT hr;
#if 1
if (irp->op == IRP_OP_WRITE) {
dprintf("WRITE:\n");
dump_const_iobuf(&irp->write);
}
#endif
#if 0
if (irp->op == IRP_OP_READ) {
dprintf("READ:\n");
dump_iobuf(&hkb_led_uart.readable);
}
#endif
if (irp->op == IRP_OP_OPEN) {
/* Unfortunately the card reader UART gets opened and closed
repeatedly */
if (!hkb_led_started) {
dprintf("Led: Open\n");
hkb_led_started = true;
} else {
return S_OK;
}
}
hr = uart_handle_irp(&hkb_led_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
hkb_led_uart.written.pos = 0;
return hr;
}

39
hkbhook/led.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#pragma pack(push, 1)
struct hkb_led_config {
bool enable;
};
struct hbk_led_packet_head {
uint8_t sync;
uint8_t dst;
uint8_t src;
uint8_t len;
};
struct hbk_led_req_header {
struct hbk_led_packet_head head;
uint8_t cmd;
};
struct hbk_led_resp_header {
struct hbk_led_packet_head head;
uint8_t status;
uint8_t cmd;
uint8_t report;
};
struct hbk_led_packet_tail {
uint8_t checksum;
};
struct hbk_led_req_any {
struct hbk_led_req_header header;
struct hbk_led_packet_tail tail;
};
struct hbk_led_resp_any {
struct hbk_led_resp_header header;
struct hbk_led_packet_tail tail;
};
#pragma pack(pop)
HRESULT led_hook_init(const struct hkb_led_config *cfg, uint8_t port);

34
hkbhook/meson.build Normal file
View File

@ -0,0 +1,34 @@
shared_library(
'hkbhook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'hkbhook.def',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
xinput_lib,
],
link_with : [
aimeio_lib,
board_lib,
gfxhook_lib,
hooklib_lib,
hkbio_lib,
platform_lib,
util_lib,
],
sources : [
'config.c',
'config.h',
'dllmain.c',
'io4.c',
'io4.h',
'led.c',
'led.h',
'hkb-dll.c',
'hkb-dll.h',
'unity.h',
'unity.c',
],
)

108
hkbhook/unity.c Normal file
View File

@ -0,0 +1,108 @@
#include <stdbool.h>
#include <windows.h>
#include <stdlib.h>
#include "hook/table.h"
#include "hook/iohook.h"
#include "hooklib/dll.h"
#include "hooklib/path.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "hooklib/serial.h"
#include "platform/clock.h"
#include "util/dprintf.h"
static void dll_hook_insert_hooks(HMODULE target);
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name);
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
static const struct hook_symbol unity_kernel32_syms[] = {
{
.name = "LoadLibraryW",
.patch = my_LoadLibraryW,
.link = (void **) &next_LoadLibraryW,
},
};
static const wchar_t *target_modules[] = {
L"mono.dll",
L"cri_ware_unity.dll",
L"HKBSys_api.dll",
L"amdaemon_api.dll",
};
static const size_t target_modules_len = _countof(target_modules);
void unity_hook_init(void)
{
dll_hook_insert_hooks(NULL);
}
static void dll_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
unity_kernel32_syms,
_countof(unity_kernel32_syms));
}
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
{
const wchar_t *name_end;
const wchar_t *target_module;
bool already_loaded;
HMODULE result;
size_t name_len;
size_t target_module_len;
if (name == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
// Check if the module is already loaded
already_loaded = GetModuleHandleW(name) != NULL;
// Must call the next handler so the DLL reference count is incremented
result = next_LoadLibraryW(name);
if (!already_loaded && result != NULL) {
name_len = wcslen(name);
for (size_t i = 0; i < target_modules_len; i++) {
target_module = target_modules[i];
target_module_len = wcslen(target_module);
// Check if the newly loaded library is at least the length of
// the name of the target module
if (name_len < target_module_len) {
continue;
}
name_end = &name[name_len - target_module_len];
// Check if the name of the newly loaded library is one of the
// modules the path hooks should be injected into
if (_wcsicmp(name_end, target_module) != 0) {
continue;
}
dprintf("Unity: Loaded %S\n", target_module);
dll_hook_insert_hooks(result);
path_hook_insert_hooks(result);
reg_hook_insert_hooks(result);
proc_addr_insert_hooks(result);
clock_hook_insert_hooks(result);
serial_hook_apply_hooks(result);
iohook_apply_hooks(result);
}
}
return result;
}

3
hkbhook/unity.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void unity_hook_init(void);

31
hkbio/config.c Normal file
View File

@ -0,0 +1,31 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "hkbio/config.h"
void hkb_io_config_load(
struct hkb_io_config *cfg,
const wchar_t *filename)
{
wchar_t key[240];
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);
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", '3', filename);
cfg->vk_up = GetPrivateProfileIntW(L"io4", L"up", VK_UP, filename);
cfg->vk_down = GetPrivateProfileIntW(L"io4", L"down", VK_DOWN, filename);
cfg->vk_left = GetPrivateProfileIntW(L"io4", L"left", VK_LEFT, filename);
cfg->vk_right = GetPrivateProfileIntW(L"io4", L"right", VK_RIGHT, filename);
cfg->vk_enter = GetPrivateProfileIntW(L"io4", L"enter", VK_SPACE, filename);
cfg->vk_cancel = GetPrivateProfileIntW(L"io4", L"cancel", VK_LSHIFT, filename);
cfg->vk_arr_right = GetPrivateProfileIntW(L"io4", L"right_arrow", 'A', filename);
cfg->vk_arr_left = GetPrivateProfileIntW(L"io4", L"left_arrow", 'D', filename);
}

24
hkbio/config.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct hkb_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
uint8_t vk_up;
uint8_t vk_down;
uint8_t vk_left;
uint8_t vk_right;
uint8_t vk_enter;
uint8_t vk_cancel;
uint8_t vk_arr_right;
uint8_t vk_arr_left;
};
void hkb_io_config_load(
struct hkb_io_config *cfg,
const wchar_t *filename);

74
hkbio/hkbio.c Normal file
View File

@ -0,0 +1,74 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include "hkbio/hkbio.h"
#include "hkbio/config.h"
static struct hkb_io_config cfg;
uint16_t hkb_io_get_api_version(void)
{
return 0x0100;
}
HRESULT hkb_io_init(void)
{
hkb_io_config_load(&cfg, L".\\segatools.ini");
return S_OK;
}
HRESULT hkb_io_poll(uint8_t *opbtn, uint8_t *gamebtn)
{
*opbtn = 0;
*gamebtn = 0;
if (GetAsyncKeyState(cfg.vk_test) & 0x8000) {
*opbtn |= HKB_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(cfg.vk_service) & 0x8000) {
*opbtn |= HKB_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(cfg.vk_coin) & 0x8000) {
*opbtn |= HKB_IO_OPBTN_COIN;
}
if (GetAsyncKeyState(cfg.vk_right) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_RIGHT;
}
if (GetAsyncKeyState(cfg.vk_left) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_LEFT;
}
if (GetAsyncKeyState(cfg.vk_up) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_UP;
}
if (GetAsyncKeyState(cfg.vk_down) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_DOWN;
}
if (GetAsyncKeyState(cfg.vk_enter) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_ENTER;
}
if (GetAsyncKeyState(cfg.vk_cancel) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_CANCEL;
}
if (GetAsyncKeyState(cfg.vk_arr_right) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_ARR_RIGHT;
}
if (GetAsyncKeyState(cfg.vk_arr_left) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_ARR_LEFT;
}
return S_OK;
}

47
hkbio/hkbio.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
HKB_IO_OPBTN_TEST = 0x01,
HKB_IO_OPBTN_SERVICE = 0x02,
HKB_IO_OPBTN_COIN = 0x04,
};
enum {
HKB_IO_GAMEBTN_RIGHT = 0x01,
HKB_IO_GAMEBTN_LEFT = 0x02,
HKB_IO_GAMEBTN_UP = 0x04,
HKB_IO_GAMEBTN_DOWN = 0x08,
HKB_IO_GAMEBTN_ENTER = 0x10,
HKB_IO_GAMEBTN_CANCEL = 0x20,
HKB_IO_GAMEBTN_ARR_RIGHT = 0x40,
HKB_IO_GAMEBTN_ARR_LEFT = 0x80,
};
/* Get the version of the Ongeki 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 hkb_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after hkb_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT hkb_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 hkb_io_poll(uint8_t *opbtn, uint8_t *gamebtn);

12
hkbio/meson.build Normal file
View File

@ -0,0 +1,12 @@
hkbio_lib = static_library(
'hkbio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
sources : [
'hkbio.c',
'hkbio.h',
'config.c',
'config.h',
],
)

257
hooklib/createprocess.c Normal file
View File

@ -0,0 +1,257 @@
#include <windows.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "hook/table.h"
#include "hooklib/createprocess.h"
#include "util/dprintf.h"
void createprocess_hook_init();
static BOOL WINAPI my_CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
BOOL my_CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
static BOOL (WINAPI *next_CreateProcessA)(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
static BOOL (WINAPI *next_CreateProcessW)(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
static const struct hook_symbol win32_hooks[] = {
{
.name = "CreateProcessA",
.patch = my_CreateProcessA,
.link = (void **) &next_CreateProcessA
},
{
.name = "CreateProcessW",
.patch = my_CreateProcessW,
.link = (void **) &next_CreateProcessW
},
};
static bool did_init = false;
static struct process_hook_sym_w *process_syms_w;
static struct process_hook_sym_a *process_syms_a;
static size_t process_nsyms_a = 0;
static size_t process_nsyms_w = 0;
static CRITICAL_SECTION createproc_lock;
HRESULT createprocess_push_hook_w(const wchar_t *name, const wchar_t *head, const wchar_t *tail, bool replace_all) {
struct process_hook_sym_w *new_mem;
struct process_hook_sym_w *new_proc;
HRESULT hr;
assert(name != NULL);
assert(head != NULL);
createprocess_hook_init();
EnterCriticalSection(&createproc_lock);
new_mem = realloc(
process_syms_w,
(process_nsyms_w + 1) * sizeof(struct process_hook_sym_w));
if (new_mem == NULL) {
LeaveCriticalSection(&createproc_lock);
return E_OUTOFMEMORY;
}
new_proc = &new_mem[process_nsyms_w];
memset(new_proc, 0, sizeof(*new_proc));
new_proc->name = name;
new_proc->head = head;
new_proc->tail = tail;
new_proc->replace_all = replace_all;
process_syms_w = new_mem;
process_nsyms_w++;
LeaveCriticalSection(&createproc_lock);
return S_OK;
}
HRESULT createprocess_push_hook_a(const char *name, const char *head, const char *tail, bool replace_all) {
struct process_hook_sym_a *new_mem;
struct process_hook_sym_a *new_proc;
assert(name != NULL);
assert(head != NULL);
createprocess_hook_init();
EnterCriticalSection(&createproc_lock);
new_mem = realloc(
process_syms_a,
(process_nsyms_a + 1) * sizeof(struct process_hook_sym_a));
if (new_mem == NULL) {
LeaveCriticalSection(&createproc_lock);
return E_OUTOFMEMORY;
}
new_proc = &new_mem[process_nsyms_a];
memset(new_proc, 0, sizeof(*new_proc));
new_proc->name = name;
new_proc->head = head;
new_proc->tail = tail;
new_proc->replace_all = replace_all;
process_syms_a = new_mem;
process_nsyms_a++;
LeaveCriticalSection(&createproc_lock);
return S_OK;
}
void createprocess_hook_init() {
if (did_init) {
return;
}
did_init = true;
hook_table_apply(
NULL,
"kernel32.dll",
win32_hooks,
_countof(win32_hooks));
InitializeCriticalSection(&createproc_lock);
dprintf("CreateProcess: Init\n");
}
static BOOL WINAPI my_CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
for (int i = 0; i < process_nsyms_a; i++) {
if (strncmp(process_syms_a[i].name, lpCommandLine, strlen(process_syms_a[i].name))) {
continue;
}
dprintf("CreateProcess: Hooking child process %s %s\n", lpApplicationName, lpCommandLine);
char new_cmd[MAX_PATH] = {0};
strcat_s(new_cmd, MAX_PATH, process_syms_a[i].head);
if (!process_syms_a[i].replace_all) {
strcat_s(new_cmd, MAX_PATH, lpCommandLine);
}
if (process_syms_a[i].tail != NULL) {
strcat_s(new_cmd, MAX_PATH, process_syms_a[i].tail);
}
dprintf("CreateProcess: Replaced CreateProcessA %s\n", new_cmd);
return next_CreateProcessA(
lpApplicationName,
new_cmd,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
}
return next_CreateProcessA(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
}
BOOL my_CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation)
{
return next_CreateProcessW(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
}

21
hooklib/createprocess.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
HRESULT createprocess_push_hook_w(const wchar_t *name, const wchar_t *head, const wchar_t *tail, bool replace_all);
HRESULT createprocess_push_hook_a(const char *name, const char *head, const char *tail, bool replace_all);
struct process_hook_sym_w {
const wchar_t *name;
const wchar_t *head;
const wchar_t *tail;
bool replace_all;
};
struct process_hook_sym_a {
const char *name;
const char *head;
const char *tail;
bool replace_all;
};

71
hooklib/cursor.c Normal file
View File

@ -0,0 +1,71 @@
#include <windows.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "hook/table.h"
#include "util/dprintf.h"
static HCURSOR my_SetCursor(HCURSOR hCursor);
static HCURSOR (*next_SetCursor)(HCURSOR hCursor);
static BOOL my_SetCursorPos(int x, int y);
static BOOL my_SetPhysicalCursorPos(int x, int y);
static int my_ShowCursor(BOOL bShow);
static const struct hook_symbol cursor_syms[] = {
{
.name = "SetCursor",
.patch = my_SetCursor,
.link = (void **) &next_SetCursor
},/*{
.name = "SetCursorPos",
.patch = my_SetCursorPos,
},*/ {
.name = "SetPhysicalCursorPos",
.patch = my_SetPhysicalCursorPos
}, {
.name = "ShowCursor",
.patch = my_ShowCursor
}
};
void cursor_hook_init()
{
hook_table_apply(
NULL,
"user32.dll",
cursor_syms,
_countof(cursor_syms));
dprintf("Cursor: Init\n");
}
static BOOL my_SetCursorPos(int x, int y)
{
dprintf("my_SetCursorPos Hit! x %d y %d\n", x, y);
return true;
}
static BOOL my_SetPhysicalCursorPos(int x, int y)
{
dprintf("my_SetPhysicalCursorPos Hit! x %d y %d\n", x, y);
return true;
}
static int my_ShowCursor(BOOL bShow)
{
return 0;
}
static HCURSOR my_SetCursor(HCURSOR hCursor)
{
HCURSOR fake_cursor;
if ( hCursor )
return next_SetCursor(hCursor);
fake_cursor = LoadCursorA(0, (LPCSTR)0x7F00);
next_SetCursor(fake_cursor);
return 0;
}

3
hooklib/cursor.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void cursor_hook_init();

View File

@ -2,13 +2,16 @@ hooklib_lib = static_library(
'hooklib',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],
sources : [
'cursor.c',
'cursor.h',
'config.c',
'config.h',
'createprocess.c',
'createprocess.h',
'dll.c',
'dll.h',
'dns.c',
@ -19,8 +22,6 @@ hooklib_lib = static_library(
'fdshark.h',
'path.c',
'path.h',
'procaddr.c',
'procaddr.h',
'reg.c',
'reg.h',
'setupapi.c',

View File

@ -97,6 +97,10 @@ static BOOL WINAPI hook_RemoveDirectoryA(const char *lpFileName);
static BOOL WINAPI hook_RemoveDirectoryW(const wchar_t *lpFileName);
static BOOL WINAPI hook_PathFileExistsA(LPCSTR pszPath);
static BOOL WINAPI hook_PathFileExistsW(LPCWSTR pszPath);
/* Link pointers */
static BOOL (WINAPI *next_CreateDirectoryA)(
@ -177,6 +181,10 @@ static BOOL (WINAPI *next_RemoveDirectoryA)(const char *lpFileName);
static BOOL (WINAPI *next_RemoveDirectoryW)(const wchar_t *lpFileName);
static BOOL (WINAPI *next_PathFileExistsA)(LPCSTR pszPath);
static BOOL (WINAPI *next_PathFileExistsW)(LPCWSTR pszPath);
/* Hook table */
static const struct hook_symbol path_hook_syms[] = {
@ -244,6 +252,14 @@ static const struct hook_symbol path_hook_syms[] = {
.name = "RemoveDirectoryW",
.patch = hook_RemoveDirectoryW,
.link = (void **) &next_RemoveDirectoryW,
}, {
.name = "PathFileExistsA",
.patch = hook_PathFileExistsA,
.link = (void **) &next_PathFileExistsA,
}, {
.name = "PathFileExistsW",
.patch = hook_PathFileExistsW,
.link = (void **) &next_PathFileExistsW,
}
};
@ -854,3 +870,39 @@ static BOOL WINAPI hook_RemoveDirectoryW(const wchar_t *lpFileName)
return ok;
}
static BOOL WINAPI hook_PathFileExistsA(LPCSTR pszPath)
{
char *trans;
BOOL ok;
ok = path_transform_a(&trans, pszPath);
if (!ok) {
return FALSE;
}
ok = next_PathFileExistsA(trans ? trans : pszPath);
free(trans);
return ok;
}
static BOOL WINAPI hook_PathFileExistsW(LPCWSTR pszPath)
{
wchar_t *trans;
BOOL ok;
ok = path_transform_w(&trans, pszPath);
if (!ok) {
return FALSE;
}
ok = next_PathFileExistsW(trans ? trans : pszPath);
free(trans);
return ok;
}

View File

@ -1,125 +0,0 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <libgen.h>
#include "hooklib/procaddr.h"
#include "hook/table.h"
#include "util/dprintf.h"
static struct proc_addr_table *proc_addr_hook_list;
static size_t proc_addr_hook_count;
static CRITICAL_SECTION proc_addr_hook_lock;
static bool proc_addr_hook_initted;
static FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name);
static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name);
static void proc_addr_hook_init(void);
static const struct hook_symbol win32_hooks[] = {
{
.name = "GetProcAddress",
.patch = my_GetProcAddress,
.link = (void **) &next_GetProcAddress
}
};
HRESULT proc_addr_table_push(
const char *target,
struct hook_symbol *syms,
size_t nsyms
)
{
HRESULT hr;
struct proc_addr_table *new_item;
struct proc_addr_table *new_mem;
proc_addr_hook_init();
EnterCriticalSection(&proc_addr_hook_lock);
new_mem = realloc(
proc_addr_hook_list,
(proc_addr_hook_count + 1) * sizeof(struct proc_addr_table));
if (new_mem == NULL) {
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&proc_addr_hook_lock);
return hr;
}
new_item = &new_mem[proc_addr_hook_count];
new_item->name = target;
new_item->nsyms = nsyms;
new_item->syms = syms;
proc_addr_hook_list = new_mem;
proc_addr_hook_count++;
hr = S_OK;
LeaveCriticalSection(&proc_addr_hook_lock);
return hr;
}
static void proc_addr_hook_init(void)
{
if (proc_addr_hook_initted) {
return;
}
dprintf("ProcAddr: Hook init\n");
proc_addr_hook_initted = true;
InitializeCriticalSection(&proc_addr_hook_lock);
hook_table_apply(
NULL,
"kernel32.dll",
win32_hooks,
_countof(win32_hooks));
}
FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name)
{
uintptr_t ordinal = (uintptr_t) name;
char mod_path[PATH_MAX];
char *mod_name;
const struct hook_symbol *sym;
FARPROC result = next_GetProcAddress(hModule, name);
GetModuleFileNameA(hModule, mod_path, PATH_MAX);
mod_name = basename(mod_path);
for (int i = 0; i < proc_addr_hook_count; i++) {
if (strcmp(proc_addr_hook_list[i].name, mod_name) == 0) {
for (int j = 0; j < proc_addr_hook_list[i].nsyms; j++) {
sym = &proc_addr_hook_list[i].syms[j];
if (ordinal > 0xFFFF) {
if (strcmp(sym->name, name) == 0) {
dprintf("ProcAddr: Hooking %s from %s\n", name, mod_name);
result = (FARPROC) sym->patch;
}
}
else {
if (sym->ordinal == ordinal) {
dprintf("ProcAddr: Hooking Ord %p from %s\n", (void *)ordinal, mod_name);
result = (FARPROC) sym->patch;
}
}
}
}
}
return result;
}

View File

@ -1,18 +0,0 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "hook/table.h"
struct proc_addr_table {
const char *name;
size_t nsyms;
struct hook_symbol *syms;
};
HRESULT proc_addr_table_push(
const char *target,
struct hook_symbol *syms,
size_t nsyms
);

View File

@ -7,6 +7,7 @@
#include "hook/table.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "util/dprintf.h"
#include "util/str.h"
@ -99,6 +100,29 @@ static LSTATUS WINAPI hook_RegGetValueW(
uint32_t *numData
);
static LSTATUS WINAPI hook_RegQueryInfoKeyW(
HKEY hKey,
LPWSTR lpClass,
LPDWORD lpcchClass,
LPDWORD lpReserved,
LPDWORD lpcSubKeys,
LPDWORD lpcbMaxSubKeyLen,
LPDWORD lpcbMaxClassLen,
LPDWORD lpcValues,
LPDWORD lpcbMaxValueNameLen,
LPDWORD lpcbMaxValueLen,
LPDWORD lpcbSecurityDescriptor,
PFILETIME lpftLastWriteTime);
static LSTATUS WINAPI hook_RegEnumValueW(
HKEY hkey,
DWORD dwIndex,
LPWSTR lpValueName,
LPDWORD lpcchValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData);
/* Link pointers */
static LSTATUS (WINAPI *next_RegOpenKeyExW)(
@ -155,6 +179,30 @@ static LSTATUS (WINAPI *next_RegGetValueW)(
uint32_t *numData
);
static LSTATUS (WINAPI *next_RegQueryInfoKeyW)(
HKEY hKey,
LPWSTR lpClass,
LPDWORD lpcchClass,
LPDWORD lpReserved,
LPDWORD lpcSubKeys,
LPDWORD lpcbMaxSubKeyLen,
LPDWORD lpcbMaxClassLen,
LPDWORD lpcValues,
LPDWORD lpcbMaxValueNameLen,
LPDWORD lpcbMaxValueLen,
LPDWORD lpcbSecurityDescriptor,
PFILETIME lpftLastWriteTime);
static LSTATUS (WINAPI *next_RegEnumValueW)(
HKEY hkey,
DWORD dwIndex,
LPWSTR lpValueName,
LPDWORD lpcchValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData);
static const struct hook_symbol reg_hook_syms[] = {
{
.name = "RegOpenKeyExW",
@ -184,6 +232,14 @@ static const struct hook_symbol reg_hook_syms[] = {
.name = "RegGetValueW",
.patch = hook_RegGetValueW,
.link = (void **) &next_RegGetValueW,
}, {
.name = "RegQueryInfoKeyW",
.patch = hook_RegQueryInfoKeyW,
.link = (void **) &next_RegQueryInfoKeyW,
}, {
.name = "RegEnumValueW",
.patch = hook_RegEnumValueW,
.link = (void **) &next_RegEnumValueW,
}
};
@ -254,11 +310,24 @@ static void reg_hook_init(void)
InitializeCriticalSection(&reg_hook_lock);
dprintf("Reg hook init\n");
reg_hook_insert_hooks(NULL);
proc_addr_table_push(
NULL,
"ADVAPI32.dll",
(struct hook_symbol *) reg_hook_syms,
_countof(reg_hook_syms));
}
void reg_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
NULL,
target,
"advapi32.dll",
reg_hook_syms,
_countof(reg_hook_syms));
}
static LRESULT reg_hook_propagate_hr(HRESULT hr)
@ -331,6 +400,7 @@ static LSTATUS reg_hook_open_locked(
/* Assume reg keys are referenced from a root key and not from some
intermediary key */
key = &reg_hook_keys[i];
//dprintf("Reg: %ls vs %ls\n", name, key->name);
if (key->root == parent && wstr_ieq(key->name, name)) {
break;
@ -821,6 +891,99 @@ static LSTATUS WINAPI hook_RegGetValueW(
return err;
}
static LSTATUS WINAPI hook_RegQueryInfoKeyW(
HKEY hKey,
LPWSTR lpClass,
LPDWORD lpcchClass,
LPDWORD lpReserved,
LPDWORD lpcSubKeys,
LPDWORD lpcbMaxSubKeyLen,
LPDWORD lpcbMaxClassLen,
LPDWORD lpcValues,
LPDWORD lpcbMaxValueNameLen,
LPDWORD lpcbMaxValueLen,
LPDWORD lpcbSecurityDescriptor,
PFILETIME lpftLastWriteTime)
{
struct reg_hook_key *key;
LSTATUS err;
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(hKey);
/* Check if this is a virtualized registry key */
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegQueryInfoKeyW(
hKey,
lpClass,
lpcchClass,
lpReserved,
lpcSubKeys,
lpcbMaxSubKeyLen,
lpcbMaxClassLen,
lpcValues,
lpcbMaxValueNameLen,
lpcbMaxValueLen,
lpcbSecurityDescriptor,
lpftLastWriteTime);
}
// This is the only one I've seen even be changed, so it's all I'm doing
// until I see otherwise.
*lpcValues = key->nvals;
LeaveCriticalSection(&reg_hook_lock);
return ERROR_SUCCESS;
}
static LSTATUS WINAPI hook_RegEnumValueW(
HKEY hkey,
DWORD dwIndex,
LPWSTR lpValueName,
LPDWORD lpcchValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData)
{
struct reg_hook_key *key;
HRESULT hr;
LSTATUS err;
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(hkey);
/* Check if this is a virtualized registry key */
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegEnumValueW(
hkey,
dwIndex,
lpValueName,
lpcchValueName,
lpReserved,
lpType,
lpData,
lpcbData);
}
if (dwIndex >= key->nvals) {
LeaveCriticalSection(&reg_hook_lock);
return ERROR_NO_MORE_ITEMS; // Pretty sure this is what it actually returns here?
}
wcscpy_s(lpValueName, *lpcchValueName, key->vals[dwIndex].name);
*lpcchValueName = wcslen(key->vals[dwIndex].name);
LeaveCriticalSection(&reg_hook_lock);
return ERROR_SUCCESS;
}
HRESULT reg_hook_read_bin(
void *bytes,
uint32_t *nbytes,

View File

@ -12,6 +12,8 @@ struct reg_hook_val {
uint32_t type;
};
void reg_hook_insert_hooks(HMODULE target);
HRESULT reg_hook_push_key(
HKEY root,
const wchar_t *name,

View File

@ -1,5 +1,6 @@
#include <windows.h>
#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>

View File

@ -1,9 +1,12 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "iccard/aime.h"
#include "iccard/mifare.h"
#include "iccard/solitaire.h"
#include "util/dprintf.h"
@ -14,6 +17,9 @@ HRESULT aime_card_populate(
{
uint8_t b;
size_t i;
char accessCode[21];
char hashed_id_wrk[9];
char id_wrk[9];
assert(mifare != NULL);
assert(luid != NULL);
@ -36,12 +42,30 @@ HRESULT aime_card_populate(
mifare->sectors[0].blocks[2].bytes[6 + i] = b;
}
// Set the card ID, nothing else matters in the first block
mifare->sectors[0].blocks[0].bytes[0] = luid[0];
mifare->sectors[0].blocks[0].bytes[1] = luid[1];
mifare->sectors[0].blocks[0].bytes[2] = luid[2];
mifare->sectors[0].blocks[0].bytes[3] = luid[3];
/* TODO An authentic Aime pass has a checksum of the LUID in the last few
bytes of block 1. The output of this function fails authenticity check
in its current form. */
sprintf_s(accessCode, sizeof accessCode, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
luid[0], luid[1], luid[2], luid[3], luid[4], luid[5], luid[6], luid[7], luid[8], luid[9]);
dprintf("AiMe IC: WARNING: Authenticity hash not yet implemented!\n");
memcpy_s(hashed_id_wrk, sizeof(hashed_id_wrk), &accessCode[5],
8);
hashed_id_wrk[8] = '\0';
SolitaireCipherDecode(&accessCode[13], hashed_id_wrk, id_wrk);
DWORD nSerial = atoi(id_wrk);
mifare->sectors[0].blocks[1].bytes[12] = (nSerial >> 24) & 0xff;
mifare->sectors[0].blocks[1].bytes[13] = (nSerial >> 16) & 0xff;
mifare->sectors[0].blocks[1].bytes[14] = (nSerial >> 8) & 0xff;
mifare->sectors[0].blocks[1].bytes[15] = nSerial & 0xff;
return S_OK;
}

View File

@ -2,7 +2,6 @@ iccard_lib = static_library(
'iccard',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],
@ -12,5 +11,7 @@ iccard_lib = static_library(
'felica.c',
'felica.h',
'mifare.h',
'solitaire.c',
'solitaire.h',
],
)

143
iccard/solitaire.c Normal file
View File

@ -0,0 +1,143 @@
#include "solitaire.h"
#include <memory.h>
#define DECK_SIZE 22
#define JOKER_A 21
#define JOKER_B 22
typedef struct {
char m_Deck[DECK_SIZE];
} DECK, *PDECK;
static DECK SOL_INIT_DECK = {
.m_Deck = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 },
};
#define char2num(c) ((c) - '0' + 1)
static inline char num2char(char num) {
while (num < 1) num = num + 10;
return (num - 1) % 10 + '0';
}
static void SolMoveCard(PDECK lpDeck, char card) {
int p = 0;
for (int i = 0; i < DECK_SIZE; i++) {
if (lpDeck->m_Deck[i] == card) {
p = i;
break;
}
}
if (p < DECK_SIZE - 1) {
lpDeck->m_Deck[p] = lpDeck->m_Deck[p + 1];
lpDeck->m_Deck[p + 1] = card;
} else {
for (int i = DECK_SIZE - 1; i > 1; i--) lpDeck->m_Deck[i] = lpDeck->m_Deck[i - 1];
lpDeck->m_Deck[1] = card;
}
}
static void SolCutDeck(PDECK lpDeck, char point) {
DECK tmp;
memcpy(tmp.m_Deck, &lpDeck->m_Deck[(size_t)point], DECK_SIZE - point - 1);
memcpy(&tmp.m_Deck[DECK_SIZE - point - 1], lpDeck->m_Deck, point);
memcpy(lpDeck->m_Deck, tmp.m_Deck, DECK_SIZE - 1);
}
static void SolSwapOutsideJoker(PDECK lpDeck) {
int j1 = -1;
int j2 = -1;
DECK tmp;
for (int i = 0; i < DECK_SIZE; i++) {
if (lpDeck->m_Deck[i] == JOKER_A || lpDeck->m_Deck[i] == JOKER_B) {
if (j1 == -1) {
j1 = i;
} else {
j2 = i;
}
}
}
if (0 < DECK_SIZE - j2 - 1) memcpy(tmp.m_Deck, &lpDeck->m_Deck[j2 + 1], DECK_SIZE - j2 - 1);
tmp.m_Deck[DECK_SIZE - j2 - 1] = lpDeck->m_Deck[j1];
if (0 < j2 - j1 - 1) memcpy(&tmp.m_Deck[DECK_SIZE - j2], &lpDeck->m_Deck[j1 + 1], j2 - j1 - 1);
tmp.m_Deck[DECK_SIZE - j1 - 1] = lpDeck->m_Deck[j2];
if (0 < j1) memcpy(&tmp.m_Deck[DECK_SIZE - j1], lpDeck->m_Deck, j1);
memcpy(lpDeck->m_Deck, tmp.m_Deck, DECK_SIZE);
}
static void SolCutByBottomCard(PDECK lpDeck) {
char p = lpDeck->m_Deck[DECK_SIZE - 1];
if (p == JOKER_B) p = JOKER_A;
SolCutDeck(lpDeck, p);
}
static char SolGetTopCardNum(PDECK lpDeck) {
char p = lpDeck->m_Deck[0];
if (p == JOKER_B) p = JOKER_A;
return lpDeck->m_Deck[(size_t)p];
}
static void SolDeckHash(PDECK lpDeck) {
char p;
do {
SolMoveCard(lpDeck, JOKER_A);
SolMoveCard(lpDeck, JOKER_B);
SolMoveCard(lpDeck, JOKER_B);
SolSwapOutsideJoker(lpDeck);
SolCutByBottomCard(lpDeck);
p = SolGetTopCardNum(lpDeck);
} while (p == JOKER_A || p == JOKER_B);
}
static void SolCreateDeck(PDECK lpDeck, const char *key) {
memcpy_s(lpDeck, sizeof *lpDeck, &SOL_INIT_DECK, sizeof SOL_INIT_DECK);
int p = 0;
while (key[p] != '\0') {
SolDeckHash(lpDeck);
char c = char2num(key[p]);
SolCutDeck(lpDeck, c);
p++;
}
}
void SolitaireCipherEncode(const char *szKey, const char *szSrc, char *szDst) {
DECK deck;
SolCreateDeck(&deck, szKey);
int i = 0;
while (szSrc[i] != '\0') {
SolDeckHash(&deck);
char p = SolGetTopCardNum(&deck);
szDst[i] = num2char(char2num(szSrc[i]) + p);
i++;
}
szDst[i] = '\0';
}
void SolitaireCipherDecode(const char *szKey, const char *szSrc, char *szDst) {
DECK deck;
SolCreateDeck(&deck, szKey);
int i = 0;
while (szSrc[i] != '\0') {
SolDeckHash(&deck);
char p = SolGetTopCardNum(&deck);
szDst[i] = num2char(char2num(szSrc[i]) - p);
i++;
}
szDst[i] = '\0';
}
void SolitaireCipher(int nMode, const char *szKey, const char *szSrc, char *szDst) {
if (nMode == 0)
SolitaireCipherEncode(szKey, szSrc, szDst);
else if (nMode == 1)
SolitaireCipherDecode(szKey, szSrc, szDst);
}

3
iccard/solitaire.h Normal file
View File

@ -0,0 +1,3 @@
void SolitaireCipherDecode(const char *szKey, const char *szSrc, char *szDst);
void SolitaireCipherEncode(const char *szKey, const char *szSrc, char *szDst);
void SolitaireCipher(int mode, const char *key, const char *src_str, char *dest_str);

View File

@ -1,5 +1,6 @@
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include "amex/amex.h"
#include "amex/config.h"

View File

@ -4,7 +4,6 @@ shared_library(
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'idzhook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),

View File

@ -4,6 +4,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "idzhook/config.h"
#include "idzhook/zinput.h"

View File

@ -4,6 +4,7 @@
#include <stddef.h>
#include <stdint.h>
#include <wchar.h>
#include <assert.h>
#include "idzio/backend.h"
#include "idzio/config.h"

View File

@ -3,7 +3,6 @@ idzio_lib = static_library(
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
dinput8_lib,
dxguid_lib,

View File

@ -2,7 +2,6 @@ jvs_lib = static_library(
'jvs',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],

67
mai2hook/config.c Normal file
View File

@ -0,0 +1,67 @@
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include "board/config.h"
#include "gfxhook/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "mai2hook/config.h"
#include "platform/config.h"
void mai2_dll_config_load(
struct mai2_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"mai2io",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void touch_config_load(
struct touch_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"touch", L"enable", 1, filename);
}
void led_config_load(
struct led_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"led", L"enable", 1, filename);
}
void mai2_hook_config_load(
struct mai2_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);
mai2_dll_config_load(&cfg->dll, filename);
touch_config_load(&cfg->touch, filename);
led_config_load(&cfg->led, filename);
}

34
mai2hook/config.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "gfxhook/gfx.h"
#include "hooklib/dvd.h"
#include "mai2hook/mai2-dll.h"
#include "mai2hook/touch.h"
#include "mai2hook/led.h"
#include "platform/config.h"
struct mai2_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct gfx_config gfx;
struct mai2_dll_config dll;
struct touch_config touch;
struct led_config led;
};
void mai2_dll_config_load(
struct mai2_dll_config *cfg,
const wchar_t *filename);
void mai2_hook_config_load(
struct mai2_hook_config *cfg,
const wchar_t *filename);

148
mai2hook/dllmain.c Normal file
View File

@ -0,0 +1,148 @@
#include <windows.h>
#include <stdlib.h>
#include "board/io4.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "gfxhook/d3d9.h"
#include "gfxhook/d3d11.h"
#include "gfxhook/dxgi.h"
#include "gfxhook/gfx.h"
#include "hook/process.h"
#include "hook/table.h"
#include "hooklib/dvd.h"
#include "hooklib/spike.h"
#include "hooklib/path.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "hooklib/serial.h"
#include "mai2hook/config.h"
#include "mai2hook/io4.h"
#include "mai2hook/mai2-dll.h"
#include "mai2hook/unity.h"
#include "mai2hook/touch.h"
#include "mai2hook/led.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE mai2_hook_mod;
static process_entry_t mai2_startup;
static struct mai2_hook_config mai2_hook_cfg;
static DWORD CALLBACK mai2_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin mai2_pre_startup ---\n");
/* Load config */
mai2_hook_config_load(&mai2_hook_cfg, L".\\segatools.ini");
/* Hook Win32 APIs */
dvd_hook_init(&mai2_hook_cfg.dvd, mai2_hook_mod);
gfx_hook_init(&mai2_hook_cfg.gfx);
gfx_d3d9_hook_init(&mai2_hook_cfg.gfx, mai2_hook_mod);
gfx_d3d11_hook_init(&mai2_hook_cfg.gfx, mai2_hook_mod);
gfx_dxgi_hook_init(&mai2_hook_cfg.gfx, mai2_hook_mod);
serial_hook_init();
/* Initialize emulation hooks */
hr = platform_hook_init(
&mai2_hook_cfg.platform,
"SDEZ",
"ACA1",
mai2_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&mai2_hook_cfg.aime, 1, mai2_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(2);
if (FAILED(hr)) {
goto fail;
}
hr = mai2_dll_init(&mai2_hook_cfg.dll, mai2_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = mai2_io4_hook_init(&mai2_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
// TODO: The handling of the fake registry COM values is extraordinarly lazy
// and I need to make a better method for tacking fake values onto common keys
// that multiple modules may try to use.
hr = led_hook_init(&mai2_hook_cfg.led);
if (FAILED(hr)) {
goto fail;
}
hr = touch_hook_init(&mai2_hook_cfg.touch);
if (FAILED(hr)) {
goto fail;
}
/* Initialize Unity native plugin DLL hooks
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `mai2hook` initialization. */
unity_hook_init();
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
dprintf("--- End mai2_pre_startup ---\n");
/* Jump to EXE start address */
return mai2_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
mai2_hook_mod = mod;
hr = process_hijack_startup(mai2_pre_startup, &mai2_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

147
mai2hook/io4.c Normal file
View File

@ -0,0 +1,147 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "board/io4.h"
#include "mai2hook/mai2-dll.h"
#include "util/dprintf.h"
bool mai2_io_coin = false;
uint16_t mai2_io_coins = 0;
static HRESULT mai2_io4_poll(void *ctx, struct io4_state *state);
static const struct io4_ops mai2_io4_ops = {
.poll = mai2_io4_poll,
};
HRESULT mai2_io4_hook_init(const struct io4_config *cfg)
{
HRESULT hr;
assert(mai2_dll.init != NULL);
hr = io4_hook_init(cfg, &mai2_io4_ops, NULL);
if (FAILED(hr)) {
return hr;
}
return mai2_dll.init();
}
static HRESULT mai2_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn = 0;
uint8_t player1 = 0;
uint8_t player2 = 0;
HRESULT hr;
assert(mai2_dll.poll != NULL);
hr = mai2_dll.poll(&opbtn, &player1, &player2);
if (FAILED(hr)) {
return hr;
}
if (opbtn & MAI2_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & MAI2_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
if (opbtn & MAI2_IO_P1_START) {
state->buttons[0] |= 1 << 1;
}
if (opbtn & MAI2_IO_P2_START) {
state->buttons[1] |= 1 << 4;
}
if (!(player1 & MAI2_IO_GAMEBTN_1)) {
state->buttons[0] |= 1 << 2;
}
if (!(player1 & MAI2_IO_GAMEBTN_2)) {
state->buttons[0] |= 1 << 3;
}
if (!(player1 & MAI2_IO_GAMEBTN_3)) {
state->buttons[0] |= 1 << 0;
}
if (!(player1 & MAI2_IO_GAMEBTN_4)) {
state->buttons[0] |= 1 << 15;
}
if (!(player1 & MAI2_IO_GAMEBTN_5)) {
state->buttons[0] |= 1 << 14;
}
if (!(player1 & MAI2_IO_GAMEBTN_6)) {
state->buttons[0] |= 1 << 13;
}
if (!(player1 & MAI2_IO_GAMEBTN_7)) {
state->buttons[0] |= 1 << 12;
}
if (!(player1 & MAI2_IO_GAMEBTN_8)) {
state->buttons[0] |= 1 << 11;
}
if (!(player2 & MAI2_IO_GAMEBTN_1)) {
state->buttons[1] |= 1 << 2;
}
if (!(player2 & MAI2_IO_GAMEBTN_2)) {
state->buttons[1] |= 1 << 3;
}
if (!(player2 & MAI2_IO_GAMEBTN_3)) {
state->buttons[1] |= 1 << 0;
}
if (!(player2 & MAI2_IO_GAMEBTN_4)) {
state->buttons[1] |= 1 << 15;
}
if (!(player2 & MAI2_IO_GAMEBTN_5)) {
state->buttons[1] |= 1 << 14;
}
if (!(player2 & MAI2_IO_GAMEBTN_6)) {
state->buttons[1] |= 1 << 13;
}
if (!(player2 & MAI2_IO_GAMEBTN_7)) {
state->buttons[1] |= 1 << 12;
}
if (!(player2 & MAI2_IO_GAMEBTN_8)) {
state->buttons[1] |= 1 << 11;
}
if (opbtn & MAI2_IO_OPBTN_COIN) {
if (!mai2_io_coin) {
mai2_io_coin = true;
mai2_io_coins++;
}
}
else {
mai2_io_coin = false;
}
state->chutes[0] = 128 + 256 * mai2_io_coins;
return S_OK;
}

7
mai2hook/io4.h Normal file
View File

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

212
mai2hook/led.c Normal file
View File

@ -0,0 +1,212 @@
#include <windows.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#include "hooklib/reg.h"
#include "hooklib/uart.h"
#include "hooklib/fdshark.h"
#include "mai2hook/led.h"
#include "util/dprintf.h"
static HRESULT read_fake_com0(void *bytes, uint32_t *nbytes);
static HRESULT read_fake_com1(void *bytes, uint32_t *nbytes);
static HRESULT read_fake_com2(void *bytes, uint32_t *nbytes);
static HRESULT led_handle_irp(struct irp *irp);
static HRESULT led0_handle_irp_locked(struct irp *irp);
static HRESULT led1_handle_irp_locked(struct irp *irp);
static CRITICAL_SECTION led0_lock;
static struct uart led0_uart;
static uint8_t led0_written_bytes[520];
static uint8_t led0_readable_bytes[520];
static CRITICAL_SECTION led1_lock;
static struct uart led1_uart;
static uint8_t led1_written_bytes[520];
static uint8_t led1_readable_bytes[520];
static const struct reg_hook_val fake_com_keys[] = {
{
.name = L"\\Device\\RealTouchBoard0",
.read = read_fake_com0,
.type = REG_SZ,
},{
.name = L"\\Device\\RealTouchBoard1",
.read = read_fake_com1,
.type = REG_SZ,
},{
.name = L"\\Device\\RealLedBoard0",
.read = read_fake_com2,
.type = REG_SZ,
},
};
HRESULT led_hook_init(const struct led_config *cfg)
{
HRESULT hr;
if (!cfg->enable) {
return S_FALSE;
}
dprintf("Mai2 LED: Init\n");
InitializeCriticalSection(&led0_lock);
InitializeCriticalSection(&led1_lock);
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"HARDWARE\\DEVICEMAP\\SERIALCOMM",
fake_com_keys,
_countof(fake_com_keys));
if (FAILED(hr)) {
return hr;
}
uart_init(&led0_uart, 21);
led0_uart.written.bytes = led0_written_bytes;
led0_uart.written.nbytes = sizeof(led0_written_bytes);
led0_uart.readable.bytes = led0_readable_bytes;
led0_uart.readable.nbytes = sizeof(led0_readable_bytes);
uart_init(&led1_uart, 23);
led1_uart.written.bytes = led1_written_bytes;
led1_uart.written.nbytes = sizeof(led1_written_bytes);
led1_uart.readable.bytes = led1_readable_bytes;
led1_uart.readable.nbytes = sizeof(led1_readable_bytes);
return iohook_push_handler(led_handle_irp);
}
static HRESULT read_fake_com0(void *bytes, uint32_t *nbytes)
{
//dprintf("Mai2 Touch: Read COM3 reg val\n");
return reg_hook_read_wstr(bytes, nbytes, L"COM3");
}
static HRESULT read_fake_com1(void *bytes, uint32_t *nbytes)
{
//dprintf("Mai2 Touch: Read COM4 reg val\n");
return reg_hook_read_wstr(bytes, nbytes, L"COM4");
}
static HRESULT read_fake_com2(void *bytes, uint32_t *nbytes)
{
//dprintf("Mai2 LED: Read COM20 reg val\n");
return reg_hook_read_wstr(bytes, nbytes, L"COM20");
}
static HRESULT led_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (uart_match_irp(&led0_uart, irp)) {
EnterCriticalSection(&led0_lock);
hr = led0_handle_irp_locked(irp);
LeaveCriticalSection(&led0_lock);
}
else if (uart_match_irp(&led1_uart, irp)) {
EnterCriticalSection(&led1_lock);
hr = led1_handle_irp_locked(irp);
LeaveCriticalSection(&led1_lock);
}
else {
return iohook_invoke_next(irp);
}
return hr;
}
static HRESULT led0_handle_irp_locked(struct irp *irp)
{
HRESULT hr = S_OK;
if (irp->op == IRP_OP_OPEN) {
dprintf("Mai2 led0: Starting backend\n");
//hr = mai2_dll.led_init();
if (FAILED(hr)) {
dprintf("Mai2 led: Backend error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&led0_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("TX0 Buffer:\n");
dump_iobuf(&led0_uart.written);
#endif
//hr = led_frame_decode(&req, &led0_uart.written, 0);
if (hr != S_OK) {
if (FAILED(hr)) {
dprintf("Mai2 led: Deframe error: %x\n", (int) hr);
}
return hr;
}
//hr = led_req_dispatch(&req);
if (FAILED(hr)) {
dprintf("Mai2 led: Processing error: %x\n", (int) hr);
}
return hr;
}
}
static HRESULT led1_handle_irp_locked(struct irp *irp)
{
HRESULT hr = S_OK;
if (irp->op == IRP_OP_OPEN) {
dprintf("Mai2 led1: Starting backend\n");
//hr = mai2_dll.led_init();
if (FAILED(hr)) {
dprintf("Mai2 led: Backend error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&led0_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("TX0 Buffer:\n");
dump_iobuf(&led0_uart.written);
#endif
//hr = led_frame_decode(&req, &led0_uart.written, 0);
if (hr != S_OK) {
if (FAILED(hr)) {
dprintf("Mai2 led: Deframe error: %x\n", (int) hr);
}
return hr;
}
//hr = led_req_dispatch(&req);
if (FAILED(hr)) {
dprintf("Mai2 led: Processing error: %x\n", (int) hr);
}
return hr;
}
}

9
mai2hook/led.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct led_config {
bool enable;
};
HRESULT led_hook_init(const struct led_config *cfg);

103
mai2hook/mai2-dll.c Normal file
View File

@ -0,0 +1,103 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "mai2hook/mai2-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym mai2_dll_syms[] = {
{
.sym = "mai2_io_init",
.off = offsetof(struct mai2_dll, init),
}, {
.sym = "mai2_io_poll",
.off = offsetof(struct mai2_dll, poll),
},
};
struct mai2_dll mai2_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 mai2_dll_init(const struct mai2_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("Mai2 IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Mai2 IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "mai2_io_get_api_version");
if (get_api_version != NULL) {
mai2_dll.api_version = get_api_version();
} else {
mai2_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose mai2_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (mai2_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Mai2 IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
mai2_dll.api_version);
goto end;
}
sym = mai2_dll_syms;
hr = dll_bind(&mai2_dll, src, &sym, _countof(mai2_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Mai2 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;
}

Some files were not shown because too many files have changed in this diff Show More