forked from TeamTofuShop/segatools
Compare commits
102 Commits
master
...
2024-02-27
Author | SHA1 | Date | |
---|---|---|---|
9c66488906
|
|||
f570869946
|
|||
629ded4018 | |||
ca36a879cb
|
|||
ae199502e8
|
|||
e40e1dffe3
|
|||
56e971c6a4
|
|||
3e9303e043
|
|||
451a7ec49d
|
|||
dae3018411
|
|||
cadf20040c
|
|||
cc0b6b0953
|
|||
0affc96e3e
|
|||
a8bd98706f | |||
aa2184f947 | |||
d0165b1eb0 | |||
477dad2667
|
|||
63320f8456 | |||
926493290b | |||
f4a3a5f78d
|
|||
f5f275c8e9
|
|||
b38dea23ac
|
|||
16bbd87c73 | |||
ee414d122b | |||
d4372fa5c2
|
|||
ac9b889d71
|
|||
3bf223c04e
|
|||
8ebdf67d6e
|
|||
ed042176d7
|
|||
ad154a83e5
|
|||
1cbc33d97d
|
|||
72db08ac93
|
|||
4ffcf25555
|
|||
3dd6054a1e
|
|||
793417e891
|
|||
8c12853051
|
|||
a3fd2fb926
|
|||
4dcf01f643 | |||
8b1d0cfefa
|
|||
d86baab852
|
|||
b9fd59fd70
|
|||
146fac9287
|
|||
3cf5cbb793
|
|||
a2db39c58c
|
|||
946ea7ef3b
|
|||
962e14dc9b | |||
25562e37f9
|
|||
d521eeb43e
|
|||
6c45d0995b
|
|||
a4bd570cfc
|
|||
37793fc051
|
|||
5d04685c73 | |||
528ec4379c | |||
5a4e947354 | |||
f5a7e5b821
|
|||
5ef0cf6181
|
|||
157f52da4c | |||
0d83977073 | |||
dca84e08d0 | |||
2dbb4aec8c | |||
3d7d9fcaa5 | |||
98d2ea1390 | |||
31203daa09
|
|||
80718104f6
|
|||
eb2eef927a
|
|||
9bef9e49f8
|
|||
d5a551482b
|
|||
49f729c501
|
|||
91f69beae6
|
|||
51b11469d0
|
|||
f0dc51d01e
|
|||
ca4a8bd84d
|
|||
f6e961d4f4
|
|||
a69a9b5917
|
|||
97234f26d7
|
|||
2277bf7526
|
|||
80d5fc4bb2
|
|||
608c9ac1a6
|
|||
3dc2ec6e69
|
|||
28ef2d719a
|
|||
600f795104
|
|||
e5d17b82b2
|
|||
0ee081ca1b
|
|||
01be6ee33c
|
|||
2a6a8bf8b2
|
|||
90a6f1be7c
|
|||
ec072667b3
|
|||
89195ed60b
|
|||
c27ef9674d
|
|||
da97d23b51
|
|||
ee6675dd73
|
|||
4c67843f08 | |||
02201dfba5 | |||
9113766c22 | |||
6fc2482c19 | |||
555784258a
|
|||
74c8b312c5 | |||
ef00932c64 | |||
8c97dc09c0 | |||
301a0e0ce7 | |||
5935e322e8 | |||
05e762d3ce |
13
.gitignore
vendored
13
.gitignore
vendored
@ -1,6 +1,17 @@
|
|||||||
.*.swp
|
.*.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
|
# Suggested names for build dirs
|
||||||
build/
|
build/
|
||||||
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": false,
|
"editor.formatOnSave": false,
|
||||||
|
"mesonbuild.configureOnOpen": false,
|
||||||
}
|
}
|
||||||
|
13
Makefile
13
Makefile
@ -5,16 +5,12 @@ V ?= @
|
|||||||
BUILD_DIR := build
|
BUILD_DIR := build
|
||||||
BUILD_DIR_32 := $(BUILD_DIR)/build32
|
BUILD_DIR_32 := $(BUILD_DIR)/build32
|
||||||
BUILD_DIR_64 := $(BUILD_DIR)/build64
|
BUILD_DIR_64 := $(BUILD_DIR)/build64
|
||||||
BUILD_DIR_DOCKER := $(BUILD_DIR)/docker
|
|
||||||
BUILD_DIR_ZIP := $(BUILD_DIR)/zip
|
BUILD_DIR_ZIP := $(BUILD_DIR)/zip
|
||||||
|
|
||||||
DOC_DIR := doc
|
DOC_DIR := doc
|
||||||
|
|
||||||
DIST_DIR := dist
|
DIST_DIR := dist
|
||||||
|
|
||||||
DOCKER_CONTAINER_NAME := "segatools-build"
|
|
||||||
DOCKER_IMAGE_NAME := "segatools:build"
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Targets
|
# Targets
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@ -42,15 +38,6 @@ zip: $(BUILD_DIR_ZIP)/segatools.zip
|
|||||||
clean:
|
clean:
|
||||||
$(V)rm -rf $(BUILD_DIR) subprojects/capnhook
|
$(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
|
# Utility, combo and alias targets
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
105
Package.mk
105
Package.mk
@ -73,6 +73,53 @@ $(BUILD_DIR_ZIP)/idz.zip:
|
|||||||
$(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll}
|
$(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll}
|
||||||
$(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip *
|
$(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip *
|
||||||
|
|
||||||
|
$(BUILD_DIR_ZIP)/fgo.zip:
|
||||||
|
$(V)echo ... $@
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/fgo
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/fgo/DEVICE
|
||||||
|
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||||
|
$(BUILD_DIR_64)/fgohook/fgohook.dll \
|
||||||
|
$(DIST_DIR)/fgo/segatools.ini \
|
||||||
|
$(DIST_DIR)/fgo/start.bat \
|
||||||
|
$(BUILD_DIR_ZIP)/fgo
|
||||||
|
$(V)cp pki/billing.pub \
|
||||||
|
pki/ca.crt \
|
||||||
|
$(BUILD_DIR_ZIP)/fgo/DEVICE
|
||||||
|
$(V)strip $(BUILD_DIR_ZIP)/fgo/*.{exe,dll}
|
||||||
|
$(V)cd $(BUILD_DIR_ZIP)/fgo ; zip -r ../fgo.zip *
|
||||||
|
|
||||||
|
$(BUILD_DIR_ZIP)/idac.zip:
|
||||||
|
$(V)echo ... $@
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/idac
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/idac/DEVICE
|
||||||
|
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||||
|
$(BUILD_DIR_64)/idachook/idachook.dll \
|
||||||
|
$(DIST_DIR)/idac/segatools.ini \
|
||||||
|
$(DIST_DIR)/idac/config_hook.json \
|
||||||
|
$(DIST_DIR)/idac/start.bat \
|
||||||
|
$(BUILD_DIR_ZIP)/idac
|
||||||
|
$(V)cp pki/billing.pub \
|
||||||
|
pki/ca.crt \
|
||||||
|
$(BUILD_DIR_ZIP)/idac/DEVICE
|
||||||
|
$(V)strip $(BUILD_DIR_ZIP)/idac/*.{exe,dll}
|
||||||
|
$(V)cd $(BUILD_DIR_ZIP)/idac ; zip -r ../idac.zip *
|
||||||
|
|
||||||
|
$(BUILD_DIR_ZIP)/swdc.zip:
|
||||||
|
$(V)echo ... $@
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/swdc
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/swdc/DEVICE
|
||||||
|
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||||
|
$(BUILD_DIR_64)/swdchook/swdchook.dll \
|
||||||
|
$(DIST_DIR)/swdc/segatools.ini \
|
||||||
|
$(DIST_DIR)/swdc/config_hook.json \
|
||||||
|
$(DIST_DIR)/swdc/start.bat \
|
||||||
|
$(BUILD_DIR_ZIP)/swdc
|
||||||
|
$(V)cp pki/billing.pub \
|
||||||
|
pki/ca.crt \
|
||||||
|
$(BUILD_DIR_ZIP)/swdc/DEVICE
|
||||||
|
$(V)strip $(BUILD_DIR_ZIP)/swdc/*.{exe,dll}
|
||||||
|
$(V)cd $(BUILD_DIR_ZIP)/swdc ; zip -r ../swdc.zip *
|
||||||
|
|
||||||
$(BUILD_DIR_ZIP)/mercury.zip:
|
$(BUILD_DIR_ZIP)/mercury.zip:
|
||||||
$(V)echo ... $@
|
$(V)echo ... $@
|
||||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury
|
||||||
@ -88,6 +135,27 @@ $(BUILD_DIR_ZIP)/mercury.zip:
|
|||||||
$(V)strip $(BUILD_DIR_ZIP)/mercury/*.{exe,dll}
|
$(V)strip $(BUILD_DIR_ZIP)/mercury/*.{exe,dll}
|
||||||
$(V)cd $(BUILD_DIR_ZIP)/mercury ; zip -r ../mercury.zip *
|
$(V)cd $(BUILD_DIR_ZIP)/mercury ; zip -r ../mercury.zip *
|
||||||
|
|
||||||
|
$(BUILD_DIR_ZIP)/chusan.zip:
|
||||||
|
$(V)echo ... $@
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/chusan
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/chusan/DEVICE
|
||||||
|
$(V)cp $(DIST_DIR)/chusan/segatools.ini \
|
||||||
|
$(DIST_DIR)/chusan/config_hook.json \
|
||||||
|
$(DIST_DIR)/chusan/start.bat \
|
||||||
|
$(BUILD_DIR_ZIP)/chusan
|
||||||
|
$(V)cp $(BUILD_DIR_32)/chusanhook/chusanhook.dll \
|
||||||
|
$(BUILD_DIR_ZIP)/chusan/chusanhook_x86.dll
|
||||||
|
$(V)cp $(BUILD_DIR_64)/chusanhook/chusanhook.dll \
|
||||||
|
$(BUILD_DIR_ZIP)/chusan/chusanhook_x64.dll
|
||||||
|
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
|
||||||
|
$(BUILD_DIR_ZIP)/chusan/inject_x86.exe
|
||||||
|
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||||
|
$(BUILD_DIR_ZIP)/chusan/inject_x64.exe
|
||||||
|
$(V)cp pki/billing.pub \
|
||||||
|
pki/ca.crt \
|
||||||
|
$(BUILD_DIR_ZIP)/chusan/DEVICE
|
||||||
|
for x in exe dll; do strip $(BUILD_DIR_ZIP)/chusan/*.$$x; done
|
||||||
|
$(V)cd $(BUILD_DIR_ZIP)/chusan ; zip -r ../chusan.zip *
|
||||||
|
|
||||||
$(BUILD_DIR_ZIP)/mu3.zip:
|
$(BUILD_DIR_ZIP)/mu3.zip:
|
||||||
$(V)echo ... $@
|
$(V)echo ... $@
|
||||||
@ -104,6 +172,37 @@ $(BUILD_DIR_ZIP)/mu3.zip:
|
|||||||
$(V)strip $(BUILD_DIR_ZIP)/mu3/*.{exe,dll}
|
$(V)strip $(BUILD_DIR_ZIP)/mu3/*.{exe,dll}
|
||||||
$(V)cd $(BUILD_DIR_ZIP)/mu3 ; zip -r ../mu3.zip *
|
$(V)cd $(BUILD_DIR_ZIP)/mu3 ; zip -r ../mu3.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)/cm.zip:
|
||||||
|
$(V)echo ... $@
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/cm
|
||||||
|
$(V)mkdir -p $(BUILD_DIR_ZIP)/cm/DEVICE
|
||||||
|
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||||
|
$(BUILD_DIR_64)/cmhook/cmhook.dll \
|
||||||
|
$(DIST_DIR)/cm/config_hook.json \
|
||||||
|
$(DIST_DIR)/cm/segatools.ini \
|
||||||
|
$(DIST_DIR)/cm/start.bat \
|
||||||
|
$(BUILD_DIR_ZIP)/cm
|
||||||
|
$(V)cp pki/billing.pub \
|
||||||
|
pki/ca.crt \
|
||||||
|
$(BUILD_DIR_ZIP)/cm/DEVICE
|
||||||
|
$(V)strip $(BUILD_DIR_ZIP)/cm/*.{exe,dll}
|
||||||
|
$(V)cd $(BUILD_DIR_ZIP)/cm ; zip -r ../cm.zip *
|
||||||
|
|
||||||
$(BUILD_DIR_ZIP)/doc.zip: \
|
$(BUILD_DIR_ZIP)/doc.zip: \
|
||||||
$(DOC_DIR)/config \
|
$(DOC_DIR)/config \
|
||||||
$(DOC_DIR)/chunihook.md \
|
$(DOC_DIR)/chunihook.md \
|
||||||
@ -119,8 +218,14 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
|
|||||||
$(BUILD_DIR_ZIP)/diva.zip \
|
$(BUILD_DIR_ZIP)/diva.zip \
|
||||||
$(BUILD_DIR_ZIP)/doc.zip \
|
$(BUILD_DIR_ZIP)/doc.zip \
|
||||||
$(BUILD_DIR_ZIP)/idz.zip \
|
$(BUILD_DIR_ZIP)/idz.zip \
|
||||||
|
$(BUILD_DIR_ZIP)/idac.zip \
|
||||||
|
$(BUILD_DIR_ZIP)/swdc.zip \
|
||||||
$(BUILD_DIR_ZIP)/mercury.zip \
|
$(BUILD_DIR_ZIP)/mercury.zip \
|
||||||
|
$(BUILD_DIR_ZIP)/chusan.zip \
|
||||||
$(BUILD_DIR_ZIP)/mu3.zip \
|
$(BUILD_DIR_ZIP)/mu3.zip \
|
||||||
|
$(BUILD_DIR_ZIP)/mai2.zip \
|
||||||
|
$(BUILD_DIR_ZIP)/cm.zip \
|
||||||
|
$(BUILD_DIR_ZIP)/fgo.zip \
|
||||||
CHANGELOG.md \
|
CHANGELOG.md \
|
||||||
README.md \
|
README.md \
|
||||||
|
|
||||||
|
28
README.md
28
README.md
@ -1,21 +1,31 @@
|
|||||||
# Segatools
|
# Segatools
|
||||||
|
|
||||||
Version: `v005`
|
Version: `2024-02-27`
|
||||||
|
|
||||||
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
|
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
|
||||||
|
|
||||||
## List of supported games
|
## List of supported games
|
||||||
|
|
||||||
* Chunithm
|
* CHUNITHM
|
||||||
* [Chunithm (Plus)](doc/chunihook.md)
|
* up to [CHUNITHM PARADISE LOST](doc/chunihook.md)
|
||||||
* [Chunithm Air (Plus)](doc/chunihook.md)
|
* starting from CHUNITHM NEW!!
|
||||||
* [Chunithm Star (Plus)](doc/chunihook.md)
|
|
||||||
* [Chunithm Amazon (Plus)](doc/chunihook.md)
|
|
||||||
* [Chunithm Crystal (Plus)](doc/chunihook.md)
|
|
||||||
* Initial D
|
* Initial D
|
||||||
* [Initial D Arcade Stage Zero](doc/idzhook.md)
|
* [Initial D Arcade Stage Zero](doc/idzhook.md)
|
||||||
* Wacca
|
* Initial D THE ARCADE
|
||||||
* Wacca Lilly R (WIP)
|
* Hatsune Miku: Project DIVA Arcade
|
||||||
|
* up to Future Tone
|
||||||
|
* SEGA World Drivers Championship
|
||||||
|
* SEGA World Drivers Championship 2019
|
||||||
|
* Fate/Grand Order
|
||||||
|
* Fate/Grand Order Arcade
|
||||||
|
* O.N.G.E.K.I.
|
||||||
|
* starting from O.N.G.E.K.I.
|
||||||
|
* maimai DX
|
||||||
|
* starting from maimai DX
|
||||||
|
* Card Maker
|
||||||
|
* starting from Card Maker
|
||||||
|
* WACCA
|
||||||
|
* starting from WACCA
|
||||||
|
|
||||||
## End-users
|
## End-users
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ struct aime_io_config {
|
|||||||
wchar_t aime_path[MAX_PATH];
|
wchar_t aime_path[MAX_PATH];
|
||||||
wchar_t felica_path[MAX_PATH];
|
wchar_t felica_path[MAX_PATH];
|
||||||
bool felica_gen;
|
bool felica_gen;
|
||||||
|
bool aime_gen;
|
||||||
uint8_t vk_scan;
|
uint8_t vk_scan;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,6 +41,11 @@ static HRESULT aime_io_generate_felica(
|
|||||||
uint8_t *bytes,
|
uint8_t *bytes,
|
||||||
size_t nbytes);
|
size_t nbytes);
|
||||||
|
|
||||||
|
static HRESULT aime_io_generate_aime(
|
||||||
|
const wchar_t *path,
|
||||||
|
uint8_t *bytes,
|
||||||
|
size_t nbytes);
|
||||||
|
|
||||||
static void aime_io_config_read(
|
static void aime_io_config_read(
|
||||||
struct aime_io_config *cfg,
|
struct aime_io_config *cfg,
|
||||||
const wchar_t *filename)
|
const wchar_t *filename)
|
||||||
@ -62,11 +68,17 @@ static void aime_io_config_read(
|
|||||||
cfg->felica_path,
|
cfg->felica_path,
|
||||||
_countof(cfg->felica_path),
|
_countof(cfg->felica_path),
|
||||||
filename);
|
filename);
|
||||||
dprintf("NFC: felicaPath GetLastError %lx\n", GetLastError());
|
// dprintf("NFC: felicaPath GetLastError %lx\n", GetLastError());
|
||||||
|
|
||||||
cfg->felica_gen = GetPrivateProfileIntW(
|
cfg->felica_gen = GetPrivateProfileIntW(
|
||||||
L"aime",
|
L"aime",
|
||||||
L"felicaGen",
|
L"felicaGen",
|
||||||
|
0,
|
||||||
|
filename);
|
||||||
|
|
||||||
|
cfg->aime_gen = GetPrivateProfileIntW(
|
||||||
|
L"aime",
|
||||||
|
L"aimeGen",
|
||||||
1,
|
1,
|
||||||
filename);
|
filename);
|
||||||
|
|
||||||
@ -136,7 +148,7 @@ static HRESULT aime_io_generate_felica(
|
|||||||
|
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
for (i = 0 ; i < nbytes ; i++) {
|
for (i = 0; i < nbytes; i++) {
|
||||||
bytes[i] = rand();
|
bytes[i] = rand();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +163,7 @@ static HRESULT aime_io_generate_felica(
|
|||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0 ; i < nbytes ; i++) {
|
for (i = 0; i < nbytes; i++) {
|
||||||
fprintf(f, "%02X", bytes[i]);
|
fprintf(f, "%02X", bytes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +175,47 @@ static HRESULT aime_io_generate_felica(
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HRESULT aime_io_generate_aime(
|
||||||
|
const wchar_t *path,
|
||||||
|
uint8_t *bytes,
|
||||||
|
size_t nbytes)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
assert(path != NULL);
|
||||||
|
assert(bytes != NULL);
|
||||||
|
assert(nbytes > 0);
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
/* AiMe IDs should not start with 3, due to a missing check for BananaPass IDs */
|
||||||
|
do {
|
||||||
|
for (i = 0; i < nbytes; i++) {
|
||||||
|
bytes[i] = rand() % 10 << 4 | rand() % 10;
|
||||||
|
}
|
||||||
|
} while (bytes[0] >> 4 == 3);
|
||||||
|
|
||||||
|
f = _wfopen(path, L"w");
|
||||||
|
|
||||||
|
if (f == NULL) {
|
||||||
|
dprintf("AimeIO DLL: %S: fopen failed: %i\n", path, (int) errno);
|
||||||
|
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nbytes; i++) {
|
||||||
|
fprintf(f, "%02x", bytes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "\n");
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
dprintf("AimeIO DLL: Generated random AiMe ID\n");
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t aime_io_get_api_version(void)
|
uint16_t aime_io_get_api_version(void)
|
||||||
{
|
{
|
||||||
return 0x0100;
|
return 0x0100;
|
||||||
@ -210,6 +263,22 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try generating AiMe IC (if enabled) */
|
||||||
|
|
||||||
|
if (aime_io_cfg.aime_gen) {
|
||||||
|
hr = aime_io_generate_aime(
|
||||||
|
aime_io_cfg.aime_path,
|
||||||
|
aime_io_aime_id,
|
||||||
|
sizeof(aime_io_aime_id));
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
aime_io_aime_id_present = true;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* Try FeliCa IC */
|
/* Try FeliCa IC */
|
||||||
|
|
||||||
hr = aime_io_read_id_file(
|
hr = aime_io_read_id_file(
|
||||||
|
@ -18,6 +18,7 @@ struct aime_dll {
|
|||||||
|
|
||||||
struct aime_dll_config {
|
struct aime_dll_config {
|
||||||
wchar_t path[MAX_PATH];
|
wchar_t path[MAX_PATH];
|
||||||
|
bool path64;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct aime_dll aime_dll;
|
extern struct aime_dll aime_dll;
|
||||||
|
@ -8,19 +8,61 @@
|
|||||||
#include "board/aime-dll.h"
|
#include "board/aime-dll.h"
|
||||||
#include "board/config.h"
|
#include "board/config.h"
|
||||||
#include "board/sg-reader.h"
|
#include "board/sg-reader.h"
|
||||||
|
#include "board/vfd.h"
|
||||||
|
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
// Check windows
|
||||||
|
#if _WIN32 || _WIN64
|
||||||
|
#if _WIN64
|
||||||
|
#define ENV64BIT
|
||||||
|
#else
|
||||||
|
#define ENV32BIT
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check GCC
|
||||||
|
#if __GNUC__
|
||||||
|
#if __x86_64__ || __ppc64__
|
||||||
|
#define ENV64BIT
|
||||||
|
#else
|
||||||
|
#define ENV32BIT
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename)
|
static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename)
|
||||||
{
|
{
|
||||||
assert(cfg != NULL);
|
assert(cfg != NULL);
|
||||||
assert(filename != NULL);
|
assert(filename != NULL);
|
||||||
|
|
||||||
GetPrivateProfileStringW(
|
// Workaround for x64/x86 external IO dlls
|
||||||
|
// path32 for 32bit, path64 for 64bit
|
||||||
|
// for else.. is that possible? idk
|
||||||
|
|
||||||
|
if (cfg->path64) {
|
||||||
|
#if defined(ENV32BIT)
|
||||||
|
// Always empty, due to amdaemon being 64 bit in 32 bit mode
|
||||||
|
memset(cfg->path, 0, sizeof(cfg->path));
|
||||||
|
#elif defined(ENV64BIT)
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"aimeio",
|
||||||
|
L"path",
|
||||||
|
L"",
|
||||||
|
cfg->path,
|
||||||
|
_countof(cfg->path),
|
||||||
|
filename);
|
||||||
|
#else
|
||||||
|
#error "Unknown environment"
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
GetPrivateProfileStringW(
|
||||||
L"aimeio",
|
L"aimeio",
|
||||||
L"path",
|
L"path",
|
||||||
L"",
|
L"",
|
||||||
cfg->path,
|
cfg->path,
|
||||||
_countof(cfg->path),
|
_countof(cfg->path),
|
||||||
filename);
|
filename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
|
void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
|
||||||
@ -30,6 +72,8 @@ void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
|
|||||||
|
|
||||||
aime_dll_config_load(&cfg->dll, filename);
|
aime_dll_config_load(&cfg->dll, filename);
|
||||||
cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename);
|
cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename);
|
||||||
|
cfg->high_baudrate = GetPrivateProfileIntW(L"aime", L"highBaud", 1, filename);
|
||||||
|
cfg->gen = GetPrivateProfileIntW(L"aime", L"gen", 0, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
|
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
|
||||||
@ -39,3 +83,11 @@ void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
|
|||||||
|
|
||||||
cfg->enable = GetPrivateProfileIntW(L"io4", L"enable", 1, filename);
|
cfg->enable = GetPrivateProfileIntW(L"io4", L"enable", 1, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vfd_config_load(struct vfd_config *cfg, const wchar_t *filename)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
cfg->enable = GetPrivateProfileIntW(L"vfd", L"enable", 1, filename);
|
||||||
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include "board/io4.h"
|
#include "board/io4.h"
|
||||||
#include "board/sg-reader.h"
|
#include "board/sg-reader.h"
|
||||||
|
#include "board/vfd.h"
|
||||||
|
|
||||||
void aime_config_load(struct aime_config *cfg, const wchar_t *filename);
|
void aime_config_load(struct aime_config *cfg, const wchar_t *filename);
|
||||||
void io4_config_load(struct io4_config *cfg, const wchar_t *filename);
|
void io4_config_load(struct io4_config *cfg, const wchar_t *filename);
|
||||||
|
void vfd_config_load(struct vfd_config *cfg, const wchar_t *filename);
|
||||||
|
18
board/io4.c
18
board/io4.c
@ -28,7 +28,7 @@ enum {
|
|||||||
IO4_CMD_CLEAR_BOARD_STATUS = 0x03,
|
IO4_CMD_CLEAR_BOARD_STATUS = 0x03,
|
||||||
IO4_CMD_SET_GENERAL_OUTPUT = 0x04,
|
IO4_CMD_SET_GENERAL_OUTPUT = 0x04,
|
||||||
IO4_CMD_SET_PWM_OUTPUT = 0x05,
|
IO4_CMD_SET_PWM_OUTPUT = 0x05,
|
||||||
IO4_CMD_UNIMPLEMENTED = 0x41,
|
IO4_CMD_SET_UNIQUE_OUTPUT = 0x41,
|
||||||
IO4_CMD_UPDATE_FIRMWARE = 0x85,
|
IO4_CMD_UPDATE_FIRMWARE = 0x85,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ struct io4_report_in {
|
|||||||
uint16_t buttons[2];
|
uint16_t buttons[2];
|
||||||
uint8_t system_status;
|
uint8_t system_status;
|
||||||
uint8_t usb_status;
|
uint8_t usb_status;
|
||||||
uint8_t unknown[29];
|
uint8_t unique_input[29];
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(struct io4_report_in) == 0x40, "IO4 IN report size");
|
static_assert(sizeof(struct io4_report_in) == 0x40, "IO4 IN report size");
|
||||||
@ -232,15 +232,15 @@ static HRESULT io4_handle_write(struct irp *irp)
|
|||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
|
case IO4_CMD_SET_UNIQUE_OUTPUT:
|
||||||
|
// dprintf("USB I/O: Unique Out\n");
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
case IO4_CMD_UPDATE_FIRMWARE:
|
case IO4_CMD_UPDATE_FIRMWARE:
|
||||||
dprintf("USB I/O: Update firmware..?\n");
|
dprintf("USB I/O: Update firmware..?\n");
|
||||||
|
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
|
|
||||||
case IO4_CMD_UNIMPLEMENTED:
|
|
||||||
//dprintf("USB I/O: Unimplemented cmd 41\n");
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dprintf("USB I/O: Unknown command %02x\n", out.cmd);
|
dprintf("USB I/O: Unknown command %02x\n", out.cmd);
|
||||||
@ -316,7 +316,7 @@ static HRESULT io4_async_poll(void *ctx, struct irp *irp)
|
|||||||
/* Delay long enough for the instigating thread in amdaemon to be satisfied
|
/* Delay long enough for the instigating thread in amdaemon to be satisfied
|
||||||
that all queued-up reports have been drained. */
|
that all queued-up reports have been drained. */
|
||||||
|
|
||||||
Sleep(1);
|
// Sleep(1);
|
||||||
|
|
||||||
/* Call into ops to poll the underlying inputs */
|
/* Call into ops to poll the underlying inputs */
|
||||||
|
|
||||||
|
222
board/led15093-cmd.h
Normal file
222
board/led15093-cmd.h
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "board/led15093-frame.h"
|
||||||
|
|
||||||
|
/* Command IDs */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LED_15093_CMD_RESET = 0x10,
|
||||||
|
LED_15093_CMD_SET_TIMEOUT = 0x11,
|
||||||
|
LED_15093_CMD_UNK1 = 0x12,
|
||||||
|
LED_15093_CMD_SET_DISABLE_RESPONSE = 0x14,
|
||||||
|
LED_15093_CMD_SET_ID = 0x18,
|
||||||
|
LED_15093_CMD_CLEAR_ID = 0x19,
|
||||||
|
LED_15093_CMD_SET_MAX_BRIGHT = 0x3F, // TODO
|
||||||
|
LED_15093_CMD_UPDATE_LED = 0x80,
|
||||||
|
LED_15093_CMD_SET_LED = 0x81,
|
||||||
|
LED_15093_CMD_SET_IMM_LED = 0x82,
|
||||||
|
LED_15093_CMD_SET_FADE_LED = 0x83,
|
||||||
|
LED_15093_CMD_SET_FADE_LEVEL = 0x84,
|
||||||
|
LED_15093_CMD_SET_FADE_SHIFT = 0x85,
|
||||||
|
LED_15093_CMD_SET_AUTO_SHIFT = 0x86,
|
||||||
|
LED_15093_CMD_GET_BOARD_INFO = 0xF0,
|
||||||
|
LED_15093_CMD_GET_BOARD_STATUS = 0xF1,
|
||||||
|
LED_15093_CMD_GET_FW_SUM = 0xF2,
|
||||||
|
LED_15093_CMD_GET_PROTOCOL_VER = 0xF3,
|
||||||
|
LED_15093_CMD_SET_BOOTMODE = 0xFD,
|
||||||
|
LED_15093_CMD_FW_UPDATE = 0xFE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Response codes */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LED_15093_STATUS_OK = 0x01,
|
||||||
|
LED_15093_STATUS_ERR_SUM = 0x02,
|
||||||
|
LED_15093_STATUS_ERR_PARITY = 0x03,
|
||||||
|
LED_15093_STATUS_ERR_FRAMING = 0x04,
|
||||||
|
LED_15093_STATUS_ERR_OVERRUN = 0x05,
|
||||||
|
LED_15093_STATUS_ERR_BUFFER_OVERFLOW = 0x06,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LED_15093_REPORT_OK = 0x01,
|
||||||
|
LED_15093_REPORT_WAIT = 0x02,
|
||||||
|
LED_15093_REPORT_ERR1 = 0x03,
|
||||||
|
LED_15093_REPORT_ERR2 = 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Status bitmasks */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LED_15093_STATUS_UART_ERR_SUM = 0x01,
|
||||||
|
LED_15093_STATUS_UART_ERR_PARITY = 0x02,
|
||||||
|
LED_15093_STATUS_UART_ERR_FRAMING = 0x04,
|
||||||
|
LED_15093_STATUS_UART_ERR_OVERRUN = 0x08,
|
||||||
|
LED_15093_STATUS_UART_ERR_BUFFER_OVERFLOW = 0x10,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LED_15093_STATUS_BOARD_ERR_WDT = 0x01,
|
||||||
|
LED_15093_STATUS_BOARD_ERR_TIMEOUT = 0x02,
|
||||||
|
LED_15093_STATUS_BOARD_ERR_RESET = 0x04,
|
||||||
|
LED_15093_STATUS_BOARD_ERR_BOR = 0x08,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LED_15093_STATUS_CMD_ERR_BUSY = 0x01,
|
||||||
|
LED_15093_STATUS_CMD_ERR_UNKNOWN = 0x02,
|
||||||
|
LED_15093_STATUS_CMD_ERR_PARAM = 0x04,
|
||||||
|
LED_15093_STATUS_CMD_ERR_EXE = 0x08,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Status types for internal use */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LED_15093_STATUS_TYPE_BOARD = 1,
|
||||||
|
LED_15093_STATUS_TYPE_UART = 2,
|
||||||
|
LED_15093_STATUS_TYPE_CMD = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Request data structures */
|
||||||
|
|
||||||
|
struct led15093_req_reset {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t r_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_req_set_timeout {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_req_set_disable_response {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
uint8_t cmd;
|
||||||
|
bool sw;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_req_set_id {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_req_set_led {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t data[198];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_req_set_fade_level {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t depth;
|
||||||
|
uint8_t cycle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_req_set_fade_shift {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t target;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_req_set_auto_shift {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t count;
|
||||||
|
uint8_t target;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_req_get_board_status {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
uint8_t cmd;
|
||||||
|
bool clear;
|
||||||
|
};
|
||||||
|
|
||||||
|
union led15093_req_any {
|
||||||
|
struct led15093_req_hdr hdr;
|
||||||
|
struct led15093_req_reset reset;
|
||||||
|
struct led15093_req_set_timeout set_timeout;
|
||||||
|
struct led15093_req_set_disable_response set_disable_response;
|
||||||
|
struct led15093_req_set_id set_id;
|
||||||
|
struct led15093_req_set_led set_led;
|
||||||
|
struct led15093_req_set_fade_level set_fade_level;
|
||||||
|
struct led15093_req_set_fade_shift set_fade_shift;
|
||||||
|
struct led15093_req_set_auto_shift set_auto_shift;
|
||||||
|
struct led15093_req_get_board_status get_board_status;
|
||||||
|
uint8_t payload[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Response data structures */
|
||||||
|
|
||||||
|
struct led15093_resp_any {
|
||||||
|
struct led15093_resp_hdr hdr;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t report;
|
||||||
|
uint8_t data[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_resp_timeout {
|
||||||
|
struct led15093_resp_hdr hdr;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t report;
|
||||||
|
uint8_t count_upper;
|
||||||
|
uint8_t count_lower;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_resp_fw_sum {
|
||||||
|
struct led15093_resp_hdr hdr;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t report;
|
||||||
|
uint8_t sum_upper;
|
||||||
|
uint8_t sum_lower;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_resp_board_info_legacy {
|
||||||
|
struct led15093_resp_hdr hdr;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t report;
|
||||||
|
char board_num[8];
|
||||||
|
uint8_t lf; // 0x0A (ASCII LF)
|
||||||
|
char chip_num[5];
|
||||||
|
uint8_t endcode; // Always 0xFF
|
||||||
|
uint8_t fw_ver;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_resp_board_info {
|
||||||
|
struct led15093_resp_hdr hdr;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t report;
|
||||||
|
char board_num[8];
|
||||||
|
uint8_t lf; // 0x0A (ASCII LF)
|
||||||
|
char chip_num[5];
|
||||||
|
uint8_t endcode; // Always 0xFF
|
||||||
|
uint8_t fw_ver;
|
||||||
|
uint8_t rx_buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_resp_protocol_ver {
|
||||||
|
struct led15093_resp_hdr hdr;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t report;
|
||||||
|
uint8_t mode;
|
||||||
|
uint8_t major_ver;
|
||||||
|
uint8_t minor_ver;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_resp_set_auto_shift {
|
||||||
|
struct led15093_resp_hdr hdr;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t report;
|
||||||
|
uint8_t count;
|
||||||
|
uint8_t target;
|
||||||
|
};
|
196
board/led15093-frame.c
Normal file
196
board/led15093-frame.c
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "board/led15093-frame.h"
|
||||||
|
|
||||||
|
#include "hook/iobuf.h"
|
||||||
|
|
||||||
|
static void led15093_frame_sync(struct iobuf *src);
|
||||||
|
static HRESULT led15093_frame_accept(const struct iobuf *dest);
|
||||||
|
static HRESULT led15093_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||||
|
|
||||||
|
/* Frame structure:
|
||||||
|
|
||||||
|
[0] Sync byte (0xE0)
|
||||||
|
[1] Destination address
|
||||||
|
[2] Source Address
|
||||||
|
[3] Length of data/payload
|
||||||
|
[4] Data/payload
|
||||||
|
For requests (host to board):
|
||||||
|
[0] Command
|
||||||
|
... Payload
|
||||||
|
For responses (board to host):
|
||||||
|
[0] Status
|
||||||
|
[1] Command
|
||||||
|
[2] Report
|
||||||
|
... Payload
|
||||||
|
[n] Checksum: Sum of all prior bytes (excluding sync byte)
|
||||||
|
|
||||||
|
Byte stuffing:
|
||||||
|
|
||||||
|
0xD0 is an escape byte. Un-escape the subsequent byte by adding 1. */
|
||||||
|
|
||||||
|
static void led15093_frame_sync(struct iobuf *src)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0 ; i < src->pos && src->bytes[i] != LED_15093_FRAME_SYNC ; i++);
|
||||||
|
|
||||||
|
src->pos -= i;
|
||||||
|
memmove(&src->bytes[0], &src->bytes[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT led15093_frame_accept(const struct iobuf *dest)
|
||||||
|
{
|
||||||
|
uint8_t checksum;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (dest->pos < 3 || dest->pos != dest->bytes[3] + 5) {
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = 0;
|
||||||
|
|
||||||
|
for (i = 1 ; i < dest->pos - 1 ; i++) {
|
||||||
|
checksum += dest->bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// dprintf("LED checksum %02x, expected %02x\n", checksum, dest->bytes[dest->pos - 1]);
|
||||||
|
|
||||||
|
if (checksum != dest->bytes[dest->pos - 1]) {
|
||||||
|
return HRESULT_FROM_WIN32(ERROR_CRC);
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT led15093_frame_decode(struct iobuf *dest, struct iobuf *src)
|
||||||
|
{
|
||||||
|
uint8_t byte;
|
||||||
|
bool escape;
|
||||||
|
size_t i;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
assert(dest != NULL);
|
||||||
|
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||||
|
assert(dest->pos <= dest->nbytes);
|
||||||
|
assert(src != NULL);
|
||||||
|
assert(src->bytes != NULL || src->nbytes == 0);
|
||||||
|
assert(src->pos <= src->nbytes);
|
||||||
|
|
||||||
|
led15093_frame_sync(src);
|
||||||
|
|
||||||
|
dest->pos = 0;
|
||||||
|
escape = false;
|
||||||
|
|
||||||
|
for (i = 0, hr = S_FALSE ; i < src->pos && hr == S_FALSE ; i++) {
|
||||||
|
/* Step the FSM to unstuff another byte */
|
||||||
|
|
||||||
|
byte = src->bytes[i];
|
||||||
|
|
||||||
|
if (dest->pos >= dest->nbytes) {
|
||||||
|
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||||
|
} else if (i == 0) {
|
||||||
|
dest->bytes[dest->pos++] = byte;
|
||||||
|
} else if (byte == LED_15093_FRAME_SYNC) {
|
||||||
|
hr = E_FAIL;
|
||||||
|
} else if (byte == LED_15093_FRAME_ESC) {
|
||||||
|
if (escape) {
|
||||||
|
hr = E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
escape = true;
|
||||||
|
} else if (escape) {
|
||||||
|
dest->bytes[dest->pos++] = byte + 1;
|
||||||
|
escape = false;
|
||||||
|
} else {
|
||||||
|
dest->bytes[dest->pos++] = byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to accept the packet we've built up so far */
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
hr = led15093_frame_accept(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle FSM terminal state */
|
||||||
|
|
||||||
|
if (hr != S_FALSE) {
|
||||||
|
/* Frame was either accepted or rejected, remove it from src */
|
||||||
|
memmove(&src->bytes[0], &src->bytes[i], src->pos - i);
|
||||||
|
src->pos -= i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT led15093_frame_encode(
|
||||||
|
struct iobuf *dest,
|
||||||
|
const void *ptr,
|
||||||
|
size_t nbytes)
|
||||||
|
{
|
||||||
|
const uint8_t *src;
|
||||||
|
uint8_t checksum;
|
||||||
|
uint8_t byte;
|
||||||
|
size_t i;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
assert(dest != NULL);
|
||||||
|
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||||
|
assert(dest->pos <= dest->nbytes);
|
||||||
|
assert(ptr != NULL);
|
||||||
|
|
||||||
|
src = ptr;
|
||||||
|
|
||||||
|
assert(nbytes >= 3 &&
|
||||||
|
src[0] == LED_15093_FRAME_SYNC &&
|
||||||
|
src[3] + 4 == nbytes);
|
||||||
|
|
||||||
|
if (dest->pos >= dest->nbytes) {
|
||||||
|
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
dest->bytes[dest->pos++] = LED_15093_FRAME_SYNC;
|
||||||
|
checksum = 0;
|
||||||
|
// dprintf("%02x ", LED_15093_FRAME_SYNC);
|
||||||
|
|
||||||
|
for (i = 1 ; i < nbytes ; i++) {
|
||||||
|
byte = src[i];
|
||||||
|
checksum += byte;
|
||||||
|
// dprintf("%02x ", byte);
|
||||||
|
|
||||||
|
hr = led15093_frame_encode_byte(dest, byte);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dprintf("%02x \n", checksum);
|
||||||
|
|
||||||
|
return led15093_frame_encode_byte(dest, checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT led15093_frame_encode_byte(struct iobuf *dest, uint8_t byte)
|
||||||
|
{
|
||||||
|
if (byte == LED_15093_FRAME_SYNC || byte == LED_15093_FRAME_ESC) {
|
||||||
|
if (dest->pos + 2 > dest->nbytes) {
|
||||||
|
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
dest->bytes[dest->pos++] = LED_15093_FRAME_ESC;
|
||||||
|
dest->bytes[dest->pos++] = byte - 1;
|
||||||
|
} else {
|
||||||
|
if (dest->pos + 1 > dest->nbytes) {
|
||||||
|
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
dest->bytes[dest->pos++] = byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
34
board/led15093-frame.h
Normal file
34
board/led15093-frame.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "hook/iobuf.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LED_15093_FRAME_SYNC = 0xE0,
|
||||||
|
LED_15093_FRAME_ESC = 0xD0,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_req_hdr {
|
||||||
|
uint8_t sync;
|
||||||
|
uint8_t dest_adr;
|
||||||
|
uint8_t src_adr;
|
||||||
|
uint8_t nbytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led15093_resp_hdr {
|
||||||
|
uint8_t sync;
|
||||||
|
uint8_t dest_adr;
|
||||||
|
uint8_t src_adr;
|
||||||
|
uint8_t nbytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
HRESULT led15093_frame_decode(struct iobuf *dest, struct iobuf *src);
|
||||||
|
|
||||||
|
HRESULT led15093_frame_encode(
|
||||||
|
struct iobuf *dest,
|
||||||
|
const void *ptr,
|
||||||
|
size_t nbytes);
|
1149
board/led15093.c
Normal file
1149
board/led15093.c
Normal file
File diff suppressed because it is too large
Load Diff
24
board/led15093.h
Normal file
24
board/led15093.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct led15093_config {
|
||||||
|
bool enable;
|
||||||
|
bool high_baudrate;
|
||||||
|
unsigned int port_no;
|
||||||
|
char board_number[8];
|
||||||
|
char chip_number[5];
|
||||||
|
char boot_chip_number[5];
|
||||||
|
uint8_t fw_ver;
|
||||||
|
uint16_t fw_sum;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef HRESULT (*io_led_init_t)(void);
|
||||||
|
typedef void (*io_led_set_leds_t)(uint8_t board, uint8_t *rgb);
|
||||||
|
|
||||||
|
HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led_init,
|
||||||
|
io_led_set_leds_t _set_leds, unsigned int first_port, unsigned int num_boards, uint8_t board_adr, uint8_t host_adr);
|
||||||
|
|
@ -20,6 +20,11 @@ board_lib = static_library(
|
|||||||
'io3.h',
|
'io3.h',
|
||||||
'io4.c',
|
'io4.c',
|
||||||
'io4.h',
|
'io4.h',
|
||||||
|
'led15093-cmd.h',
|
||||||
|
'led15093-frame.c',
|
||||||
|
'led15093-frame.h',
|
||||||
|
'led15093.c',
|
||||||
|
'led15093.h',
|
||||||
'sg-cmd.c',
|
'sg-cmd.c',
|
||||||
'sg-cmd.h',
|
'sg-cmd.h',
|
||||||
'sg-frame.c',
|
'sg-frame.c',
|
||||||
|
@ -17,7 +17,7 @@ struct sg_led_res_reset {
|
|||||||
|
|
||||||
struct sg_led_res_get_info {
|
struct sg_led_res_get_info {
|
||||||
struct sg_res_header res;
|
struct sg_res_header res;
|
||||||
uint8_t payload[9];
|
char payload[12];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sg_led_req_set_color {
|
struct sg_led_req_set_color {
|
||||||
|
@ -27,14 +27,18 @@ static HRESULT sg_led_cmd_set_color(
|
|||||||
const struct sg_led *led,
|
const struct sg_led *led,
|
||||||
const struct sg_led_req_set_color *req);
|
const struct sg_led_req_set_color *req);
|
||||||
|
|
||||||
static const uint8_t sg_led_info[] = {
|
const char *sg_led_info[] = {
|
||||||
'1', '5', '0', '8', '4', 0xFF, 0x10, 0x00, 0x12,
|
"15084\xFF\x10\x00\x12",
|
||||||
|
"000-00000\xFF\x11\x40",
|
||||||
|
// maybe the same?
|
||||||
|
"000-00000\xFF\x11\x40"
|
||||||
};
|
};
|
||||||
|
|
||||||
void sg_led_init(
|
void sg_led_init(
|
||||||
struct sg_led *led,
|
struct sg_led *led,
|
||||||
uint8_t addr,
|
uint8_t addr,
|
||||||
const struct sg_led_ops *ops,
|
const struct sg_led_ops *ops,
|
||||||
|
unsigned int gen,
|
||||||
void *ctx)
|
void *ctx)
|
||||||
{
|
{
|
||||||
assert(led != NULL);
|
assert(led != NULL);
|
||||||
@ -43,6 +47,7 @@ void sg_led_init(
|
|||||||
led->ops = ops;
|
led->ops = ops;
|
||||||
led->ops_ctx = ctx;
|
led->ops_ctx = ctx;
|
||||||
led->addr = addr;
|
led->addr = addr;
|
||||||
|
led->gen = gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sg_led_transact(
|
void sg_led_transact(
|
||||||
@ -150,8 +155,11 @@ static HRESULT sg_led_cmd_get_info(
|
|||||||
struct sg_led_res_get_info *res)
|
struct sg_led_res_get_info *res)
|
||||||
{
|
{
|
||||||
sg_led_dprintf(led, "Get info\n");
|
sg_led_dprintf(led, "Get info\n");
|
||||||
sg_res_init(&res->res, req, sizeof(res->payload));
|
|
||||||
memcpy(res->payload, sg_led_info, sizeof(sg_led_info));
|
unsigned int len = strlen(sg_led_info[led->gen - 1]);
|
||||||
|
|
||||||
|
sg_res_init(&res->res, req, len);
|
||||||
|
memcpy(res->payload, sg_led_info[led->gen - 1], len);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,14 @@ struct sg_led {
|
|||||||
const struct sg_led_ops *ops;
|
const struct sg_led_ops *ops;
|
||||||
void *ops_ctx;
|
void *ops_ctx;
|
||||||
uint8_t addr;
|
uint8_t addr;
|
||||||
|
unsigned int gen;
|
||||||
};
|
};
|
||||||
|
|
||||||
void sg_led_init(
|
void sg_led_init(
|
||||||
struct sg_led *led,
|
struct sg_led *led,
|
||||||
uint8_t addr,
|
uint8_t addr,
|
||||||
const struct sg_led_ops *ops,
|
const struct sg_led_ops *ops,
|
||||||
|
unsigned int gen,
|
||||||
void *ctx);
|
void *ctx);
|
||||||
|
|
||||||
void sg_led_transact(
|
void sg_led_transact(
|
||||||
|
@ -15,6 +15,7 @@ enum {
|
|||||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54,
|
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54,
|
||||||
SG_NFC_CMD_MIFARE_AUTHENTICATE = 0x55, /* guess based on time sent */
|
SG_NFC_CMD_MIFARE_AUTHENTICATE = 0x55, /* guess based on time sent */
|
||||||
|
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||||
SG_NFC_CMD_RESET = 0x62,
|
SG_NFC_CMD_RESET = 0x62,
|
||||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||||
};
|
};
|
||||||
|
@ -65,10 +65,23 @@ static HRESULT sg_nfc_cmd_dummy(
|
|||||||
const struct sg_req_header *req,
|
const struct sg_req_header *req,
|
||||||
struct sg_res_header *res);
|
struct sg_res_header *res);
|
||||||
|
|
||||||
|
static const char *hw_version[] = {
|
||||||
|
"TN32MSEC003S H/W Ver3.0",
|
||||||
|
"837-15286",
|
||||||
|
"837-15396"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *fw_version[] = {
|
||||||
|
"TN32MSEC003S F/W Ver1.2",
|
||||||
|
"\x94",
|
||||||
|
"\x94"
|
||||||
|
};
|
||||||
|
|
||||||
void sg_nfc_init(
|
void sg_nfc_init(
|
||||||
struct sg_nfc *nfc,
|
struct sg_nfc *nfc,
|
||||||
uint8_t addr,
|
uint8_t addr,
|
||||||
const struct sg_nfc_ops *ops,
|
const struct sg_nfc_ops *ops,
|
||||||
|
unsigned int gen,
|
||||||
void *ops_ctx)
|
void *ops_ctx)
|
||||||
{
|
{
|
||||||
assert(nfc != NULL);
|
assert(nfc != NULL);
|
||||||
@ -77,6 +90,7 @@ void sg_nfc_init(
|
|||||||
nfc->ops = ops;
|
nfc->ops = ops;
|
||||||
nfc->ops_ctx = ops_ctx;
|
nfc->ops_ctx = ops_ctx;
|
||||||
nfc->addr = addr;
|
nfc->addr = addr;
|
||||||
|
nfc->gen = gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
@ -176,6 +190,7 @@ static HRESULT sg_nfc_dispatch(
|
|||||||
case SG_NFC_CMD_MIFARE_SET_KEY_BANA:
|
case SG_NFC_CMD_MIFARE_SET_KEY_BANA:
|
||||||
case SG_NFC_CMD_RADIO_ON:
|
case SG_NFC_CMD_RADIO_ON:
|
||||||
case SG_NFC_CMD_RADIO_OFF:
|
case SG_NFC_CMD_RADIO_OFF:
|
||||||
|
case SG_NFC_CMD_SEND_HEX_DATA: // TODO: implement?
|
||||||
return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple);
|
return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -202,9 +217,11 @@ static HRESULT sg_nfc_cmd_get_fw_version(
|
|||||||
const struct sg_req_header *req,
|
const struct sg_req_header *req,
|
||||||
struct sg_nfc_res_get_fw_version *res)
|
struct sg_nfc_res_get_fw_version *res)
|
||||||
{
|
{
|
||||||
|
unsigned int len = strlen(fw_version[nfc->gen - 1]);
|
||||||
|
|
||||||
/* Dest version is not NUL terminated, this is intentional */
|
/* Dest version is not NUL terminated, this is intentional */
|
||||||
sg_res_init(&res->res, req, sizeof(res->version));
|
sg_res_init(&res->res, req, len);
|
||||||
memcpy(res->version, "TN32MSEC003S F/W Ver1.2E", sizeof(res->version));
|
memcpy(res->version, fw_version[nfc->gen - 1], len);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
@ -214,9 +231,11 @@ static HRESULT sg_nfc_cmd_get_hw_version(
|
|||||||
const struct sg_req_header *req,
|
const struct sg_req_header *req,
|
||||||
struct sg_nfc_res_get_hw_version *res)
|
struct sg_nfc_res_get_hw_version *res)
|
||||||
{
|
{
|
||||||
|
unsigned int len = strlen(hw_version[nfc->gen - 1]);
|
||||||
|
|
||||||
/* Dest version is not NUL terminated, this is intentional */
|
/* Dest version is not NUL terminated, this is intentional */
|
||||||
sg_res_init(&res->res, req, sizeof(res->version));
|
sg_res_init(&res->res, req, len);
|
||||||
memcpy(res->version, "TN32MSEC003S H/W Ver3.0J", sizeof(res->version));
|
memcpy(res->version, hw_version[nfc->gen - 1], len);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ struct sg_nfc {
|
|||||||
const struct sg_nfc_ops *ops;
|
const struct sg_nfc_ops *ops;
|
||||||
void *ops_ctx;
|
void *ops_ctx;
|
||||||
uint8_t addr;
|
uint8_t addr;
|
||||||
|
unsigned int gen;
|
||||||
struct felica felica;
|
struct felica felica;
|
||||||
struct mifare mifare;
|
struct mifare mifare;
|
||||||
};
|
};
|
||||||
@ -30,6 +31,7 @@ void sg_nfc_init(
|
|||||||
struct sg_nfc *nfc,
|
struct sg_nfc *nfc,
|
||||||
uint8_t addr,
|
uint8_t addr,
|
||||||
const struct sg_nfc_ops *ops,
|
const struct sg_nfc_ops *ops,
|
||||||
|
unsigned int gen,
|
||||||
void *ops_ctx);
|
void *ops_ctx);
|
||||||
|
|
||||||
void sg_nfc_transact(
|
void sg_nfc_transact(
|
||||||
|
@ -48,6 +48,7 @@ static struct sg_led sg_reader_led;
|
|||||||
HRESULT sg_reader_hook_init(
|
HRESULT sg_reader_hook_init(
|
||||||
const struct aime_config *cfg,
|
const struct aime_config *cfg,
|
||||||
unsigned int port_no,
|
unsigned int port_no,
|
||||||
|
unsigned int gen,
|
||||||
HINSTANCE self)
|
HINSTANCE self)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
@ -65,11 +66,25 @@ HRESULT sg_reader_hook_init(
|
|||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, NULL);
|
if (cfg->gen != 0) {
|
||||||
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, NULL);
|
gen = cfg->gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gen < 1 || gen > 3) {
|
||||||
|
dprintf("NFC Assembly: Invalid reader generation: %u\n", gen);
|
||||||
|
|
||||||
|
return E_INVALIDARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, gen, NULL);
|
||||||
|
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, gen, NULL);
|
||||||
|
|
||||||
InitializeCriticalSection(&sg_reader_lock);
|
InitializeCriticalSection(&sg_reader_lock);
|
||||||
|
|
||||||
|
if (!cfg->high_baudrate) {
|
||||||
|
sg_reader_uart.baud.BaudRate = 38400;
|
||||||
|
}
|
||||||
|
|
||||||
uart_init(&sg_reader_uart, port_no);
|
uart_init(&sg_reader_uart, port_no);
|
||||||
sg_reader_uart.written.bytes = sg_reader_written_bytes;
|
sg_reader_uart.written.bytes = sg_reader_written_bytes;
|
||||||
sg_reader_uart.written.nbytes = sizeof(sg_reader_written_bytes);
|
sg_reader_uart.written.nbytes = sizeof(sg_reader_written_bytes);
|
||||||
|
@ -9,9 +9,12 @@
|
|||||||
struct aime_config {
|
struct aime_config {
|
||||||
struct aime_dll_config dll;
|
struct aime_dll_config dll;
|
||||||
bool enable;
|
bool enable;
|
||||||
|
bool high_baudrate;
|
||||||
|
unsigned int gen;
|
||||||
};
|
};
|
||||||
|
|
||||||
HRESULT sg_reader_hook_init(
|
HRESULT sg_reader_hook_init(
|
||||||
const struct aime_config *cfg,
|
const struct aime_config *cfg,
|
||||||
unsigned int port_no,
|
unsigned int port_no,
|
||||||
|
unsigned int gen,
|
||||||
HINSTANCE self);
|
HINSTANCE self);
|
||||||
|
10
board/vfd.c
10
board/vfd.c
@ -27,14 +27,22 @@ static struct uart vfd_uart;
|
|||||||
static uint8_t vfd_written[512];
|
static uint8_t vfd_written[512];
|
||||||
static uint8_t vfd_readable[512];
|
static uint8_t vfd_readable[512];
|
||||||
|
|
||||||
HRESULT vfd_hook_init(unsigned int port_no)
|
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no)
|
||||||
{
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
|
||||||
|
if (!cfg->enable) {
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
uart_init(&vfd_uart, port_no);
|
uart_init(&vfd_uart, port_no);
|
||||||
vfd_uart.written.bytes = vfd_written;
|
vfd_uart.written.bytes = vfd_written;
|
||||||
vfd_uart.written.nbytes = sizeof(vfd_written);
|
vfd_uart.written.nbytes = sizeof(vfd_written);
|
||||||
vfd_uart.readable.bytes = vfd_readable;
|
vfd_uart.readable.bytes = vfd_readable;
|
||||||
vfd_uart.readable.nbytes = sizeof(vfd_readable);
|
vfd_uart.readable.nbytes = sizeof(vfd_readable);
|
||||||
|
|
||||||
|
dprintf("VFD: hook enabled.\n");
|
||||||
|
|
||||||
return iohook_push_handler(vfd_handle_irp);
|
return iohook_push_handler(vfd_handle_irp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,4 +2,9 @@
|
|||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
HRESULT vfd_hook_init(unsigned int port_no);
|
struct vfd_config {
|
||||||
|
bool enable;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no);
|
||||||
|
@ -21,9 +21,18 @@ const struct dll_bind_sym carol_dll_syms[] = {
|
|||||||
}, {
|
}, {
|
||||||
.sym = "carol_io_touch_init",
|
.sym = "carol_io_touch_init",
|
||||||
.off = offsetof(struct carol_dll, 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",
|
.sym = "carol_io_controlbd_init",
|
||||||
.off = offsetof(struct carol_dll, 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),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,7 +10,10 @@ struct carol_dll {
|
|||||||
void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams);
|
void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams);
|
||||||
void (*jvs_read_coin_counter)(uint16_t *total);
|
void (*jvs_read_coin_counter)(uint16_t *total);
|
||||||
HRESULT (*touch_init)();
|
HRESULT (*touch_init)();
|
||||||
|
HRESULT (*ledbd_init)();
|
||||||
HRESULT (*controlbd_init)();
|
HRESULT (*controlbd_init)();
|
||||||
|
void (*touch_start)(carol_io_touch_callback_t callback);
|
||||||
|
void (*touch_stop)();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct carol_dll_config {
|
struct carol_dll_config {
|
||||||
|
@ -16,4 +16,7 @@ EXPORTS
|
|||||||
carol_io_jvs_poll
|
carol_io_jvs_poll
|
||||||
carol_io_jvs_read_coin_counter
|
carol_io_jvs_read_coin_counter
|
||||||
carol_io_touch_init
|
carol_io_touch_init
|
||||||
|
carol_io_ledbd_init
|
||||||
carol_io_controlbd_init
|
carol_io_controlbd_init
|
||||||
|
carol_io_touch_start
|
||||||
|
carol_io_touch_stop
|
||||||
|
@ -58,6 +58,20 @@ void controlbd_config_load(
|
|||||||
filename);
|
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(
|
void carol_hook_config_load(
|
||||||
struct carol_hook_config *cfg,
|
struct carol_hook_config *cfg,
|
||||||
@ -73,4 +87,5 @@ void carol_hook_config_load(
|
|||||||
gfx_config_load(&cfg->gfx, filename);
|
gfx_config_load(&cfg->gfx, filename);
|
||||||
touch_config_load(&cfg->touch, filename);
|
touch_config_load(&cfg->touch, filename);
|
||||||
controlbd_config_load(&cfg->controlbd, filename);
|
controlbd_config_load(&cfg->controlbd, filename);
|
||||||
|
ledbd_config_load(&cfg->ledbd, filename);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "gfxhook/gfx.h"
|
#include "gfxhook/gfx.h"
|
||||||
|
|
||||||
#include "carolhook/touch.h"
|
#include "carolhook/touch.h"
|
||||||
|
#include "carolhook/ledbd.h"
|
||||||
#include "carolhook/controlbd.h"
|
#include "carolhook/controlbd.h"
|
||||||
|
|
||||||
struct carol_hook_config {
|
struct carol_hook_config {
|
||||||
@ -22,6 +23,7 @@ struct carol_hook_config {
|
|||||||
struct carol_dll_config dll;
|
struct carol_dll_config dll;
|
||||||
struct gfx_config gfx;
|
struct gfx_config gfx;
|
||||||
struct touch_config touch;
|
struct touch_config touch;
|
||||||
|
struct ledbd_config ledbd;
|
||||||
struct controlbd_config controlbd;
|
struct controlbd_config controlbd;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,10 +18,18 @@
|
|||||||
|
|
||||||
static HRESULT controlbd_handle_irp(struct irp *irp);
|
static HRESULT controlbd_handle_irp(struct irp *irp);
|
||||||
static HRESULT controlbd_handle_irp_locked(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_decode(struct controlbd_req_any *dest, struct iobuf *src);
|
||||||
static HRESULT controlbd_frame_dispatch(struct controlbd_req *dest);
|
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 CRITICAL_SECTION controlbd_lock;
|
||||||
static struct uart controlbd_uart;
|
static struct uart controlbd_uart;
|
||||||
@ -36,7 +44,7 @@ HRESULT controlbd_hook_init(const struct controlbd_config *cfg)
|
|||||||
|
|
||||||
InitializeCriticalSection(&controlbd_lock);
|
InitializeCriticalSection(&controlbd_lock);
|
||||||
|
|
||||||
uart_init(&controlbd_uart, 11);
|
uart_init(&controlbd_uart, 12);
|
||||||
controlbd_uart.written.bytes = controlbd_written_bytes;
|
controlbd_uart.written.bytes = controlbd_written_bytes;
|
||||||
controlbd_uart.written.nbytes = sizeof(controlbd_written_bytes);
|
controlbd_uart.written.nbytes = sizeof(controlbd_written_bytes);
|
||||||
controlbd_uart.readable.bytes = controlbd_readable_bytes;
|
controlbd_uart.readable.bytes = controlbd_readable_bytes;
|
||||||
@ -64,10 +72,49 @@ static HRESULT controlbd_handle_irp(struct irp *irp)
|
|||||||
return hr;
|
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)
|
static HRESULT controlbd_handle_irp_locked(struct irp *irp)
|
||||||
{
|
{
|
||||||
struct controlbd_req req;
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
struct controlbd_req_any req;
|
||||||
|
|
||||||
assert(carol_dll.controlbd_init != NULL);
|
assert(carol_dll.controlbd_init != NULL);
|
||||||
|
|
||||||
@ -76,7 +123,7 @@ static HRESULT controlbd_handle_irp_locked(struct irp *irp)
|
|||||||
hr = carol_dll.controlbd_init();
|
hr = carol_dll.controlbd_init();
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
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;
|
return hr;
|
||||||
}
|
}
|
||||||
@ -89,90 +136,204 @@ static HRESULT controlbd_handle_irp_locked(struct irp *irp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
if (controlbd_uart.written.bytes[0] == 0xE0) {
|
||||||
#if 0
|
#if 0
|
||||||
dprintf("Control Board: TX Buffer:\n");
|
dprintf("Control Board: TX Buffer:\n");
|
||||||
dump_iobuf(&controlbd_uart.written);
|
dump_iobuf(&controlbd_uart.written);
|
||||||
#endif
|
#endif
|
||||||
hr = controlbd_frame_decode(&req, &controlbd_uart.written);
|
hr = controlbd_frame_decode(&req, &controlbd_uart.written);
|
||||||
|
if (FAILED(hr)) {
|
||||||
if (FAILED(hr)) {
|
dprintf("Control Board: Deframe error: %x\n", (int) 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;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = controlbd_frame_dispatch(&req);
|
// The board has a LPC111x bootloader that gets ran over every boot
|
||||||
if (FAILED(hr)) {
|
// this is to account for that.
|
||||||
dprintf("Control Board: Dispatch Error: %x\n", (int) hr);
|
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;
|
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) {
|
switch (req->hdr.cmd) {
|
||||||
case CONTROLBD_CMD_UNK_11:
|
case CONTROLBD_CMD_RESET:
|
||||||
return controlbd_req_nop(req->cmd);
|
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:
|
default:
|
||||||
dprintf("Unhandled command %#02x\n", req->cmd);
|
dprintf("Unhandled command 0x%02x\n", req->hdr.cmd);
|
||||||
|
return controlbd_req_ack_any(req->hdr.cmd);
|
||||||
return S_OK;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
resp->sync = CONTROLBD_SYNC_BYTE;
|
||||||
|
resp->dest = 0x01;
|
||||||
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0xE0;
|
resp->src = 0x02;
|
||||||
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
|
resp->len = len;
|
||||||
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x11;
|
resp->report = 0x01;
|
||||||
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x03;
|
resp->cmd = cmd;
|
||||||
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;
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decodes the response into a struct that's easier to work with. */
|
static uint8_t calc_checksum(void *data, size_t len)
|
||||||
static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf)
|
|
||||||
{
|
{
|
||||||
int initial_pos = iobuf->pos;
|
uint8_t *stuff;
|
||||||
uint8_t check = 0;
|
stuff = data;
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
uint16_t tmp = 0;
|
||||||
|
|
||||||
dest->sync = iobuf->bytes[0];
|
for (int i = 1; i < len; i++) {
|
||||||
iobuf->pos--;
|
tmp = checksum + stuff[i];
|
||||||
|
checksum = tmp & 0xFF;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
||||||
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -7,15 +9,140 @@
|
|||||||
struct controlbd_config {
|
struct controlbd_config {
|
||||||
bool enable;
|
bool enable;
|
||||||
};
|
};
|
||||||
enum controlbd_cmd {
|
|
||||||
CONTROLBD_CMD_UNK_11 = 0x11
|
#pragma pack(push, 1)
|
||||||
};
|
struct controlbd_req_hdr {
|
||||||
struct controlbd_req {
|
uint8_t sync;
|
||||||
uint8_t sync; // First byte is the sync
|
uint8_t dest; // unsure
|
||||||
uint8_t cmd; // Command byte
|
uint8_t src; // unsure
|
||||||
uint8_t data[256]; // Request body goes here
|
uint8_t len; // length of the rest of the request minus checksum
|
||||||
uint8_t checksum; // Final byte is all bytes added, except the sync
|
uint8_t cmd;
|
||||||
uint8_t data_length; // Size of the data including command byte
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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);
|
HRESULT controlbd_hook_init(const struct controlbd_config *cfg);
|
@ -12,13 +12,15 @@
|
|||||||
#include "carolhook/carol-dll.h"
|
#include "carolhook/carol-dll.h"
|
||||||
#include "carolhook/jvs.h"
|
#include "carolhook/jvs.h"
|
||||||
#include "carolhook/touch.h"
|
#include "carolhook/touch.h"
|
||||||
|
#include "carolhook/ledbd.h"
|
||||||
#include "carolhook/controlbd.h"
|
#include "carolhook/controlbd.h"
|
||||||
#include "carolhook/serial.h"
|
|
||||||
|
|
||||||
#include "hook/process.h"
|
#include "hook/process.h"
|
||||||
|
|
||||||
#include "hooklib/serial.h"
|
#include "hooklib/serial.h"
|
||||||
#include "hooklib/spike.h"
|
#include "hooklib/spike.h"
|
||||||
|
#include "hooklib/createprocess.h"
|
||||||
|
#include "hooklib/cursor.h"
|
||||||
|
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
|
|
||||||
@ -30,17 +32,43 @@ static struct carol_hook_config carol_hook_cfg;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
COM Layout
|
COM Layout
|
||||||
01:(?) Touchscreen
|
01: Touchscreen
|
||||||
10: Aime reader
|
10: Aime reader
|
||||||
11: Control board
|
11: LED board
|
||||||
12(?): LED Board
|
12: Control Board
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static DWORD CALLBACK carol_pre_startup(void)
|
static DWORD CALLBACK carol_pre_startup(void)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
HMODULE d3dc;
|
||||||
|
HMODULE dbghelp;
|
||||||
|
|
||||||
dprintf("--- Begin carol_pre_startup ---\n");
|
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 */
|
/* Config load */
|
||||||
|
|
||||||
@ -74,16 +102,14 @@ static DWORD CALLBACK carol_pre_startup(void)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = sg_reader_hook_init(&carol_hook_cfg.aime, 10, carol_hook_mod);
|
hr = sg_reader_hook_init(&carol_hook_cfg.aime, 10, 1, carol_hook_mod);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx_hook_init(&carol_hook_cfg.gfx);
|
gfx_hook_init(&carol_hook_cfg.gfx);
|
||||||
gfx_d3d9_hook_init(&carol_hook_cfg.gfx, carol_hook_mod);
|
gfx_d3d9_hook_init(&carol_hook_cfg.gfx, carol_hook_mod);
|
||||||
//serial_init();
|
|
||||||
|
|
||||||
|
|
||||||
hr = touch_hook_init(&carol_hook_cfg.touch);
|
hr = touch_hook_init(&carol_hook_cfg.touch);
|
||||||
|
|
||||||
@ -91,12 +117,23 @@ static DWORD CALLBACK carol_pre_startup(void)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr = ledbd_hook_init(&carol_hook_cfg.ledbd);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
hr = controlbd_hook_init(&carol_hook_cfg.controlbd);
|
hr = controlbd_hook_init(&carol_hook_cfg.controlbd);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
goto fail;
|
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 */
|
/* Initialize debug helpers */
|
||||||
|
|
||||||
spike_hook_init(L".\\segatools.ini");
|
spike_hook_init(L".\\segatools.ini");
|
||||||
|
168
carolhook/ledbd.c
Normal file
168
carolhook/ledbd.c
Normal 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
28
carolhook/ledbd.h
Normal 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);
|
@ -32,7 +32,7 @@ shared_library(
|
|||||||
'touch.h',
|
'touch.h',
|
||||||
'controlbd.c',
|
'controlbd.c',
|
||||||
'controlbd.h',
|
'controlbd.h',
|
||||||
'serial.c',
|
'ledbd.c',
|
||||||
'serial.h',
|
'ledbd.h',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <windows.h>
|
|
||||||
#include <winbase.h>
|
|
||||||
|
|
||||||
void serial_init();
|
|
@ -8,19 +8,46 @@
|
|||||||
#include "carolhook/carol-dll.h"
|
#include "carolhook/carol-dll.h"
|
||||||
#include "carolhook/touch.h"
|
#include "carolhook/touch.h"
|
||||||
|
|
||||||
|
#include "hook/table.h"
|
||||||
|
|
||||||
#include "hooklib/uart.h"
|
#include "hooklib/uart.h"
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
#include "util/dump.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(struct irp *irp);
|
||||||
static HRESULT touch_handle_irp_locked(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 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 CRITICAL_SECTION touch_lock;
|
||||||
static struct uart touch_uart;
|
static struct uart touch_uart;
|
||||||
static uint8_t touch_written_bytes[520];
|
static uint8_t touch_written_bytes[528];
|
||||||
static uint8_t touch_readable_bytes[520];
|
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)
|
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) {
|
if (irp->op == IRP_OP_OPEN) {
|
||||||
dprintf("Touchscreen: Starting backend DLL\n");
|
dprintf("Touchscreen: Starting backend DLL\n");
|
||||||
hr = carol_dll.touch_init();
|
hr = carol_dll.touch_init();
|
||||||
|
carol_dll.touch_start(touch_scan_auto);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
dprintf("Touchscreen: Backend DLL error: %x\n", (int) hr);
|
dprintf("Touchscreen: Backend DLL error: %X\n", (int) hr);
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
@ -84,35 +112,142 @@ static HRESULT touch_handle_irp_locked(struct irp *irp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
#if 1
|
#if 0
|
||||||
dprintf("Touchscreen: TX Buffer:\n");
|
dprintf("Touchscreen: TX Buffer:\n");
|
||||||
dump_iobuf(&touch_uart.written);
|
dump_iobuf(&touch_uart.written);
|
||||||
#endif
|
#endif
|
||||||
hr = touch_frame_decode(&req, &touch_uart.written);
|
hr = touch_frame_decode(&req, &touch_uart.written);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
dprintf("Touchscreen: Deframe Error: %x\n", (int) hr);
|
dprintf("Touchscreen: Deframe Error: %X\n", (int) hr);
|
||||||
|
|
||||||
return 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;
|
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. */
|
/* Decodes the response into a struct that's easier to work with. */
|
||||||
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf)
|
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf)
|
||||||
{
|
{
|
||||||
dest->cmd = iobuf->bytes[0];
|
dest->sync = iobuf->bytes[0];
|
||||||
iobuf->pos--;
|
memset(dest->cmd, 0, sizeof(dest->cmd));
|
||||||
dest->data_length = iobuf->pos;
|
size_t data_len = 0;
|
||||||
|
|
||||||
if (dest->data_length > 0) {
|
for (int i = 1; i < 255; i++) {
|
||||||
for (int i = 1; i < dest->data_length; i++) {
|
if (iobuf->bytes[i] == 0x0D) { break; }
|
||||||
dest->data[i-1] = iobuf->bytes[i];
|
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;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,42 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
struct touch_config {
|
struct touch_config {
|
||||||
bool enable;
|
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 {
|
struct touch_req {
|
||||||
uint8_t cmd; // First byte is the command byte
|
uint8_t sync; // Always 0x01
|
||||||
uint8_t data[256]; // rest of the data goes here
|
uint8_t cmd[256]; // rest of the data goes here
|
||||||
uint8_t data_length; // Size of the data including command byte
|
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);
|
HRESULT touch_hook_init(const struct touch_config *cfg);
|
@ -7,10 +7,16 @@
|
|||||||
|
|
||||||
#include "carolio/carolio.h"
|
#include "carolio/carolio.h"
|
||||||
#include "carolio/config.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 bool carol_io_coin;
|
||||||
static uint16_t carol_io_coins;
|
static uint16_t carol_io_coins;
|
||||||
static struct carol_io_config carol_io_cfg;
|
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)
|
uint16_t carol_io_get_api_version(void)
|
||||||
{
|
{
|
||||||
@ -73,7 +79,103 @@ HRESULT carol_io_touch_init()
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT carol_io_ledbd_init()
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT carol_io_controlbd_init()
|
HRESULT carol_io_controlbd_init()
|
||||||
{
|
{
|
||||||
return S_OK;
|
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;
|
||||||
}
|
}
|
@ -5,6 +5,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.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
|
/* 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
|
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
|
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_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();
|
@ -14,7 +14,7 @@ void carol_io_config_load(
|
|||||||
assert(cfg != NULL);
|
assert(cfg != NULL);
|
||||||
assert(filename != NULL);
|
assert(filename != NULL);
|
||||||
|
|
||||||
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename);
|
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", VK_F1, filename);
|
||||||
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename);
|
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", VK_F2, filename);
|
||||||
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename);
|
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", VK_F3, filename);
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,28 @@ const struct dll_bind_sym chuni_dll_syms[] = {
|
|||||||
}, {
|
}, {
|
||||||
.sym = "chuni_io_slider_set_leds",
|
.sym = "chuni_io_slider_set_leds",
|
||||||
.off = offsetof(struct chuni_dll, slider_set_leds),
|
.off = offsetof(struct chuni_dll, slider_set_leds),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_led_init",
|
||||||
|
.off = offsetof(struct chuni_dll, led_init),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_led_set_colors",
|
||||||
|
.off = offsetof(struct chuni_dll, led_set_leds),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Helper function to determine upon dll_bind failure whether the required functions were found
|
||||||
|
NOTE: relies on symbols order declared above */
|
||||||
|
static HRESULT has_enough_symbols(uint16_t version, uint8_t count)
|
||||||
|
{
|
||||||
|
if ( version <= 0x0101 && count == 7 )
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
if ( version >= 0x0102 && count == 9 )
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
struct chuni_dll chuni_dll;
|
struct chuni_dll chuni_dll;
|
||||||
|
|
||||||
// Copypasta DLL binding and diagnostic message boilerplate.
|
// Copypasta DLL binding and diagnostic message boilerplate.
|
||||||
@ -92,16 +111,24 @@ HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sym = chuni_dll_syms;
|
sym = chuni_dll_syms;
|
||||||
|
const struct dll_bind_sym *init_sym = &sym[0];
|
||||||
hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms));
|
hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms));
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
if (src != self) {
|
if (src != self) {
|
||||||
dprintf("Chunithm IO: Custom IO DLL does not provide function "
|
// Might still be ok depending on external dll API version
|
||||||
"\"%s\". Please contact your IO DLL's developer for "
|
int bind_count = sym - init_sym;
|
||||||
"further assistance.\n",
|
if ( has_enough_symbols(chuni_dll.api_version, bind_count) == S_OK )
|
||||||
sym->sym);
|
{
|
||||||
|
hr = S_OK;
|
||||||
|
} else {
|
||||||
|
dprintf("Chunithm IO: Custom IO DLL does not provide function "
|
||||||
|
"\"%s\". Please contact your IO DLL's developer for "
|
||||||
|
"further assistance.\n",
|
||||||
|
sym->sym);
|
||||||
|
|
||||||
goto end;
|
goto end;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
|
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ struct chuni_dll {
|
|||||||
void (*slider_start)(chuni_io_slider_callback_t callback);
|
void (*slider_start)(chuni_io_slider_callback_t callback);
|
||||||
void (*slider_stop)(void);
|
void (*slider_stop)(void);
|
||||||
void (*slider_set_leds)(const uint8_t *rgb);
|
void (*slider_set_leds)(const uint8_t *rgb);
|
||||||
|
HRESULT (*led_init)(void);
|
||||||
|
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct chuni_dll_config {
|
struct chuni_dll_config {
|
||||||
|
@ -20,3 +20,5 @@ EXPORTS
|
|||||||
chuni_io_slider_set_leds
|
chuni_io_slider_set_leds
|
||||||
chuni_io_slider_start
|
chuni_io_slider_start
|
||||||
chuni_io_slider_stop
|
chuni_io_slider_stop
|
||||||
|
chuni_io_led_init
|
||||||
|
chuni_io_led_set_colors
|
||||||
|
@ -43,6 +43,66 @@ void slider_config_load(struct slider_config *cfg, const wchar_t *filename)
|
|||||||
cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename);
|
cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
wchar_t tmpstr[16];
|
||||||
|
|
||||||
|
memset(cfg->board_number, ' ', sizeof(cfg->board_number));
|
||||||
|
memset(cfg->chip_number, ' ', sizeof(cfg->chip_number));
|
||||||
|
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
|
||||||
|
|
||||||
|
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
|
||||||
|
cfg->port_no = 0;
|
||||||
|
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
|
||||||
|
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0x90, filename);
|
||||||
|
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xadf7, filename);
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led15093",
|
||||||
|
L"boardNumber",
|
||||||
|
L"15093-06",
|
||||||
|
tmpstr,
|
||||||
|
_countof(tmpstr),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number));
|
||||||
|
for (int i = n; i < sizeof(cfg->board_number); i++)
|
||||||
|
{
|
||||||
|
cfg->board_number[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led15093",
|
||||||
|
L"chipNumber",
|
||||||
|
L"6710 ",
|
||||||
|
tmpstr,
|
||||||
|
_countof(tmpstr),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number));
|
||||||
|
for (int i = n; i < sizeof(cfg->chip_number); i++)
|
||||||
|
{
|
||||||
|
cfg->chip_number[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led15093",
|
||||||
|
L"bootChipNumber",
|
||||||
|
L"6709 ",
|
||||||
|
tmpstr,
|
||||||
|
_countof(tmpstr),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
n = wcstombs(cfg->boot_chip_number, tmpstr, sizeof(cfg->boot_chip_number));
|
||||||
|
for (int i = n; i < sizeof(cfg->boot_chip_number); i++)
|
||||||
|
{
|
||||||
|
cfg->boot_chip_number[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void chuni_hook_config_load(
|
void chuni_hook_config_load(
|
||||||
struct chuni_hook_config *cfg,
|
struct chuni_hook_config *cfg,
|
||||||
const wchar_t *filename)
|
const wchar_t *filename)
|
||||||
@ -58,4 +118,5 @@ void chuni_hook_config_load(
|
|||||||
gfx_config_load(&cfg->gfx, filename);
|
gfx_config_load(&cfg->gfx, filename);
|
||||||
chuni_dll_config_load(&cfg->dll, filename);
|
chuni_dll_config_load(&cfg->dll, filename);
|
||||||
slider_config_load(&cfg->slider, filename);
|
slider_config_load(&cfg->slider, filename);
|
||||||
|
led15093_config_load(&cfg->led15093, filename);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "amex/amex.h"
|
#include "amex/amex.h"
|
||||||
|
|
||||||
#include "board/sg-reader.h"
|
#include "board/sg-reader.h"
|
||||||
|
#include "board/led15093.h"
|
||||||
|
|
||||||
#include "chunihook/chuni-dll.h"
|
#include "chunihook/chuni-dll.h"
|
||||||
#include "chunihook/slider.h"
|
#include "chunihook/slider.h"
|
||||||
@ -21,6 +22,7 @@ struct chuni_hook_config {
|
|||||||
struct gfx_config gfx;
|
struct gfx_config gfx;
|
||||||
struct chuni_dll_config dll;
|
struct chuni_dll_config dll;
|
||||||
struct slider_config slider;
|
struct slider_config slider;
|
||||||
|
struct led15093_config led15093;
|
||||||
};
|
};
|
||||||
|
|
||||||
void chuni_dll_config_load(
|
void chuni_dll_config_load(
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "amex/amex.h"
|
#include "amex/amex.h"
|
||||||
|
|
||||||
|
#include "board/led15093.h"
|
||||||
#include "board/sg-reader.h"
|
#include "board/sg-reader.h"
|
||||||
|
|
||||||
#include "chunihook/config.h"
|
#include "chunihook/config.h"
|
||||||
@ -96,7 +97,19 @@ static DWORD CALLBACK chuni_pre_startup(void)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, chuni_hook_mod);
|
if ( chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL )
|
||||||
|
{
|
||||||
|
dprintf("IO DLL doesn't support led_init/led_set_leds, cannot start LED15093 hook\n");
|
||||||
|
} else {
|
||||||
|
hr = led15093_hook_init(&chuni_hook_cfg.led15093,
|
||||||
|
chuni_dll.led_init, chuni_dll.led_set_leds, 10, 2, 2, 1);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, 1, chuni_hook_mod);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -101,13 +101,13 @@ static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out)
|
|||||||
out->p1 = 0x0000;
|
out->p1 = 0x0000;
|
||||||
out->p2 = 0x0000;
|
out->p2 = 0x0000;
|
||||||
|
|
||||||
if (opbtn & 0x01) {
|
if (opbtn & CHUNI_IO_OPBTN_TEST) {
|
||||||
out->system = 0x80;
|
out->system = 0x80;
|
||||||
} else {
|
} else {
|
||||||
out->system = 0x00;
|
out->system = 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opbtn & 0x02) {
|
if (opbtn & CHUNI_IO_OPBTN_SERVICE) {
|
||||||
out->p1 |= 0x4000;
|
out->p1 |= 0x4000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
358
chuniio/chu2to3.c
Normal file
358
chuniio/chu2to3.c
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "chuniio/chu2to3.h"
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
// Check windows
|
||||||
|
#if _WIN32 || _WIN64
|
||||||
|
#if _WIN64
|
||||||
|
#define ENV64BIT
|
||||||
|
#else
|
||||||
|
#define ENV32BIT
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check GCC
|
||||||
|
#if __GNUC__
|
||||||
|
#if __x86_64__ || __ppc64__
|
||||||
|
#define ENV64BIT
|
||||||
|
#else
|
||||||
|
#define ENV32BIT
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* chuniio.dll dynamic loading */
|
||||||
|
HMODULE hinstLib;
|
||||||
|
typedef uint16_t (*chuni_io_get_api_version_t)(void);
|
||||||
|
typedef HRESULT (*chuni_io_jvs_init_t)(void);
|
||||||
|
typedef void (*chuni_io_jvs_poll_t)(uint8_t*, uint8_t*);
|
||||||
|
typedef void (*chuni_io_jvs_read_coin_counter_t)(uint16_t *);
|
||||||
|
typedef HRESULT (*chuni_io_slider_init_t)(void);
|
||||||
|
typedef void (*chuni_io_slider_set_leds_t)(const uint8_t *);
|
||||||
|
typedef void (*chuni_io_slider_start_t)(chuni_io_slider_callback_t);
|
||||||
|
typedef void (*chuni_io_slider_stop_t)(void);
|
||||||
|
typedef HRESULT (*chuni_io_led_init_t)(void);
|
||||||
|
typedef void (*chuni_io_led_set_colors_t)(uint8_t, uint8_t *);
|
||||||
|
|
||||||
|
chuni_io_get_api_version_t _chuni_io_get_api_version;
|
||||||
|
chuni_io_jvs_init_t _chuni_io_jvs_init;
|
||||||
|
chuni_io_jvs_poll_t _chuni_io_jvs_poll;
|
||||||
|
chuni_io_jvs_read_coin_counter_t _chuni_io_jvs_read_coin_counter;
|
||||||
|
chuni_io_slider_init_t _chuni_io_slider_init;
|
||||||
|
chuni_io_slider_set_leds_t _chuni_io_slider_set_leds;
|
||||||
|
chuni_io_slider_start_t _chuni_io_slider_start;
|
||||||
|
chuni_io_slider_stop_t _chuni_io_slider_stop;
|
||||||
|
chuni_io_led_init_t _chuni_io_led_init;
|
||||||
|
chuni_io_led_set_colors_t _chuni_io_led_set_colors;
|
||||||
|
|
||||||
|
/* SHMEM Handling */
|
||||||
|
#define BUF_SIZE 1024
|
||||||
|
#define SHMEM_WRITE(buf, size) CopyMemory((PVOID)g_pBuf, buf, size)
|
||||||
|
#define SHMEM_READ(buf, size) CopyMemory(buf,(PVOID)g_pBuf, size)
|
||||||
|
TCHAR g_shmem_name[]=TEXT("Local\\Chu2to3Shmem");
|
||||||
|
HANDLE g_hMapFile;
|
||||||
|
LPVOID g_pBuf;
|
||||||
|
|
||||||
|
#pragma pack(1)
|
||||||
|
typedef struct shared_data_s {
|
||||||
|
uint16_t coin_counter;
|
||||||
|
uint8_t opbtn;
|
||||||
|
uint8_t beams;
|
||||||
|
uint16_t version;
|
||||||
|
} shared_data_t;
|
||||||
|
|
||||||
|
shared_data_t g_shared_data;
|
||||||
|
|
||||||
|
bool shmem_create()
|
||||||
|
{
|
||||||
|
g_hMapFile = CreateFileMapping(
|
||||||
|
INVALID_HANDLE_VALUE, // use paging file
|
||||||
|
NULL, // default security
|
||||||
|
PAGE_READWRITE, // read/write access
|
||||||
|
0, // maximum object size (high-order DWORD)
|
||||||
|
BUF_SIZE, // maximum object size (low-order DWORD)
|
||||||
|
g_shmem_name); // name of mapping object
|
||||||
|
|
||||||
|
if (g_hMapFile == NULL)
|
||||||
|
{
|
||||||
|
dprintf("shmem_create : Could not create file mapping object (%ld).\n",
|
||||||
|
GetLastError());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
g_pBuf = MapViewOfFile(g_hMapFile, // handle to map object
|
||||||
|
FILE_MAP_ALL_ACCESS, // read/write permission
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
BUF_SIZE);
|
||||||
|
|
||||||
|
if (g_pBuf == NULL)
|
||||||
|
{
|
||||||
|
dprintf("shmem_create : Could not map view of file (%ld).\n",
|
||||||
|
GetLastError());
|
||||||
|
|
||||||
|
CloseHandle(g_hMapFile);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shmem_load()
|
||||||
|
{
|
||||||
|
g_hMapFile = OpenFileMapping(
|
||||||
|
FILE_MAP_ALL_ACCESS, // read/write access
|
||||||
|
FALSE, // do not inherit the name
|
||||||
|
g_shmem_name); // name of mapping object
|
||||||
|
|
||||||
|
if (g_hMapFile == NULL)
|
||||||
|
{
|
||||||
|
dprintf("shmem_load : Could not open file mapping object (%ld).\n", GetLastError());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pBuf = MapViewOfFile(g_hMapFile, // handle to map object
|
||||||
|
FILE_MAP_ALL_ACCESS, // read/write permission
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
BUF_SIZE);
|
||||||
|
|
||||||
|
if (g_pBuf == NULL)
|
||||||
|
{
|
||||||
|
dprintf("shmem_load : Could not map view of file (%ld).\n", GetLastError());
|
||||||
|
CloseHandle(g_hMapFile);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("shmem_load : shmem loaded succesfully.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shmem_free()
|
||||||
|
{
|
||||||
|
UnmapViewOfFile(g_pBuf);
|
||||||
|
CloseHandle(g_hMapFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* jvs polling thread (to forward info to x64 dll) */
|
||||||
|
static HANDLE jvs_poll_thread;
|
||||||
|
static bool jvs_poll_stop_flag;
|
||||||
|
|
||||||
|
static unsigned int __stdcall jvs_poll_thread_proc(void *ctx)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
_chuni_io_jvs_read_coin_counter(&g_shared_data.coin_counter);
|
||||||
|
g_shared_data.opbtn = 0;
|
||||||
|
g_shared_data.beams = 0;
|
||||||
|
_chuni_io_jvs_poll(&g_shared_data.opbtn, &g_shared_data.beams);
|
||||||
|
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t chu2to3_load_dll(const wchar_t *dllname)
|
||||||
|
{
|
||||||
|
#if defined(ENV64BIT)
|
||||||
|
/* x64 must just open the shmem and do nothing else */
|
||||||
|
int errcount = 0;
|
||||||
|
while (!shmem_load())
|
||||||
|
{
|
||||||
|
if (errcount >= 10)
|
||||||
|
return -1;
|
||||||
|
Sleep(5000);
|
||||||
|
errcount++;
|
||||||
|
}
|
||||||
|
Sleep(1000);
|
||||||
|
return S_OK;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* this is the first function called so let's setup the chuniio forwarding */
|
||||||
|
hinstLib = LoadLibraryW(dllname);
|
||||||
|
if (hinstLib == NULL) {
|
||||||
|
dprintf("ERROR: unable to load %S (error %ld)\n",dllname, GetLastError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_chuni_io_get_api_version = (chuni_io_get_api_version_t)GetProcAddress(hinstLib, "chuni_io_get_api_version");
|
||||||
|
_chuni_io_jvs_init = (chuni_io_jvs_init_t)GetProcAddress(hinstLib, "chuni_io_jvs_init");
|
||||||
|
_chuni_io_jvs_poll = (chuni_io_jvs_poll_t)GetProcAddress(hinstLib, "chuni_io_jvs_poll");
|
||||||
|
_chuni_io_jvs_read_coin_counter = (chuni_io_jvs_read_coin_counter_t)GetProcAddress(hinstLib, "chuni_io_jvs_read_coin_counter");
|
||||||
|
_chuni_io_slider_init = (chuni_io_slider_init_t)GetProcAddress(hinstLib, "chuni_io_slider_init");
|
||||||
|
_chuni_io_slider_set_leds = (chuni_io_slider_set_leds_t)GetProcAddress(hinstLib, "chuni_io_slider_set_leds");
|
||||||
|
_chuni_io_slider_start = (chuni_io_slider_start_t)GetProcAddress(hinstLib, "chuni_io_slider_start");
|
||||||
|
_chuni_io_slider_stop = (chuni_io_slider_stop_t)GetProcAddress(hinstLib, "chuni_io_slider_stop");
|
||||||
|
_chuni_io_led_init = (chuni_io_led_init_t)GetProcAddress(hinstLib, "chuni_io_led_init");
|
||||||
|
_chuni_io_led_set_colors = (chuni_io_led_set_colors_t)GetProcAddress(hinstLib, "chuni_io_led_set_colors");
|
||||||
|
|
||||||
|
/* x86 has to create the shmem */
|
||||||
|
if (!shmem_create())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* chuniio exports */
|
||||||
|
uint16_t chu2to3_io_get_api_version(void)
|
||||||
|
{
|
||||||
|
#if defined(ENV64BIT)
|
||||||
|
/* This might be called too soon so let's make sure x86 has time to write to the shmem */
|
||||||
|
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
|
||||||
|
int errcount = 0;
|
||||||
|
while (g_shared_data.version == 0)
|
||||||
|
{
|
||||||
|
if (errcount >= 3)
|
||||||
|
{
|
||||||
|
dprintf("CHU2TO3 X64: Couldn't retrieve api version from shmem, assuming 0x0100\n");
|
||||||
|
return 0x0100;
|
||||||
|
}
|
||||||
|
Sleep(5000);
|
||||||
|
errcount++;
|
||||||
|
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
|
||||||
|
}
|
||||||
|
dprintf("CHU2TO3 X64: api version is %04X\n", g_shared_data.version);
|
||||||
|
return g_shared_data.version;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ( _chuni_io_get_api_version == NULL )
|
||||||
|
{
|
||||||
|
g_shared_data.version = 0x0100;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_shared_data.version = _chuni_io_get_api_version();
|
||||||
|
}
|
||||||
|
dprintf("CHU2TO3: api version is %04X\n", g_shared_data.version);
|
||||||
|
|
||||||
|
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
|
||||||
|
|
||||||
|
return g_shared_data.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT chu2to3_io_jvs_init(void)
|
||||||
|
{
|
||||||
|
#if defined(ENV64BIT)
|
||||||
|
/* x86 only */
|
||||||
|
return S_OK;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_chuni_io_jvs_init();
|
||||||
|
|
||||||
|
/* start jvs poll thread now that jvs_init is done */
|
||||||
|
if (jvs_poll_thread != NULL) {
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
jvs_poll_thread = (HANDLE) _beginthreadex(NULL,
|
||||||
|
0,
|
||||||
|
jvs_poll_thread_proc,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void chu2to3_io_jvs_read_coin_counter(uint16_t *out)
|
||||||
|
{
|
||||||
|
#if defined(ENV32BIT)
|
||||||
|
/* x86 can perform the call and update shmem (although this call never happens) */
|
||||||
|
_chuni_io_jvs_read_coin_counter(&g_shared_data.coin_counter);
|
||||||
|
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* x64 must read value from shmem and update arg */
|
||||||
|
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
|
||||||
|
if (out == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*out = g_shared_data.coin_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void chu2to3_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
|
||||||
|
{
|
||||||
|
#if defined(ENV32BIT)
|
||||||
|
/* x86 can perform the call and update shmem (although this call never happens) */
|
||||||
|
_chuni_io_jvs_poll(&g_shared_data.opbtn, &g_shared_data.beams);
|
||||||
|
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* x64 must read value from shmem and update args */
|
||||||
|
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
|
||||||
|
*opbtn = g_shared_data.opbtn;
|
||||||
|
*beams = g_shared_data.beams;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT chu2to3_io_slider_init(void)
|
||||||
|
{
|
||||||
|
#if defined(ENV64BIT)
|
||||||
|
/* x86 only */
|
||||||
|
return S_OK;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return _chuni_io_slider_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void chu2to3_io_slider_start(chuni_io_slider_callback_t callback)
|
||||||
|
{
|
||||||
|
#if defined(ENV64BIT)
|
||||||
|
/* x86 only */
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_chuni_io_slider_start(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void chu2to3_io_slider_stop(void)
|
||||||
|
{
|
||||||
|
#if defined(ENV64BIT)
|
||||||
|
/* x86 only */
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_chuni_io_slider_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void chu2to3_io_slider_set_leds(const uint8_t *rgb)
|
||||||
|
{
|
||||||
|
#if defined(ENV64BIT)
|
||||||
|
/* x86 only */
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_chuni_io_slider_set_leds(rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT chu2to3_io_led_init(void)
|
||||||
|
{
|
||||||
|
#if defined(ENV64BIT)
|
||||||
|
/* x86 only */
|
||||||
|
return S_OK;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (_chuni_io_led_init != NULL)
|
||||||
|
return _chuni_io_led_init();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void chu2to3_io_led_set_colors(uint8_t board, uint8_t *rgb)
|
||||||
|
{
|
||||||
|
#if defined(ENV64BIT)
|
||||||
|
/* x86 only */
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (_chuni_io_led_set_colors != NULL)
|
||||||
|
{
|
||||||
|
_chuni_io_led_set_colors(board, rgb);
|
||||||
|
}
|
||||||
|
}
|
26
chuniio/chu2to3.h
Normal file
26
chuniio/chu2to3.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
CHU2TO3 CUSTOM IO API
|
||||||
|
|
||||||
|
This dll just mirrors chuniio dll binds but with a dynamic library loading and
|
||||||
|
a SHMEM system to let a single 32bit dll talk with x86 and x64 processes at once
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint16_t chu2to3_io_get_api_version(void);
|
||||||
|
HRESULT chu2to3_io_jvs_init(void);
|
||||||
|
void chu2to3_io_jvs_poll(uint8_t *opbtn, uint8_t *beams);
|
||||||
|
void chu2to3_io_jvs_read_coin_counter(uint16_t *total);
|
||||||
|
HRESULT chu2to3_io_slider_init(void);
|
||||||
|
typedef void (*chuni_io_slider_callback_t)(const uint8_t *state);
|
||||||
|
void chu2to3_io_slider_start(chuni_io_slider_callback_t callback);
|
||||||
|
void chu2to3_io_slider_stop(void);
|
||||||
|
void chu2to3_io_slider_set_leds(const uint8_t *rgb);
|
||||||
|
HRESULT chu2to3_io_led_init(void);
|
||||||
|
void chu2to3_io_led_set_colors(uint8_t board, uint8_t *rgb);
|
||||||
|
uint16_t chu2to3_load_dll(const wchar_t *dllname);
|
@ -3,9 +3,13 @@
|
|||||||
#include <process.h>
|
#include <process.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "chuniio/chuniio.h"
|
#include "chuniio/chuniio.h"
|
||||||
#include "chuniio/config.h"
|
#include "chuniio/config.h"
|
||||||
|
#include "chuniio/ledoutput.h"
|
||||||
|
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx);
|
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx);
|
||||||
|
|
||||||
@ -18,13 +22,23 @@ static struct chuni_io_config chuni_io_cfg;
|
|||||||
|
|
||||||
uint16_t chuni_io_get_api_version(void)
|
uint16_t chuni_io_get_api_version(void)
|
||||||
{
|
{
|
||||||
return 0x0101;
|
return 0x0102;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT chuni_io_jvs_init(void)
|
HRESULT chuni_io_jvs_init(void)
|
||||||
{
|
{
|
||||||
chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini");
|
chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini");
|
||||||
|
|
||||||
|
led_init_mutex = CreateMutex(
|
||||||
|
NULL, // default security attributes
|
||||||
|
FALSE, // initially not owned
|
||||||
|
NULL); // unnamed mutex
|
||||||
|
|
||||||
|
if (led_init_mutex == NULL)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +48,7 @@ void chuni_io_jvs_read_coin_counter(uint16_t *out)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetAsyncKeyState(chuni_io_cfg.vk_coin)) {
|
if (GetAsyncKeyState(chuni_io_cfg.vk_coin) & 0x8000) {
|
||||||
if (!chuni_io_coin) {
|
if (!chuni_io_coin) {
|
||||||
chuni_io_coin = true;
|
chuni_io_coin = true;
|
||||||
chuni_io_coins++;
|
chuni_io_coins++;
|
||||||
@ -50,38 +64,52 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
|
|||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (GetAsyncKeyState(chuni_io_cfg.vk_test)) {
|
if (GetAsyncKeyState(chuni_io_cfg.vk_test) & 0x8000) {
|
||||||
*opbtn |= 0x01; /* Test */
|
*opbtn |= CHUNI_IO_OPBTN_TEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetAsyncKeyState(chuni_io_cfg.vk_service)) {
|
if (GetAsyncKeyState(chuni_io_cfg.vk_service) & 0x8000) {
|
||||||
*opbtn |= 0x02; /* Service */
|
*opbtn |= CHUNI_IO_OPBTN_SERVICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetAsyncKeyState(chuni_io_cfg.vk_ir)) {
|
if (chuni_io_cfg.vk_ir_emu) {
|
||||||
if (chuni_io_hand_pos < 6) {
|
// Use emulated AIR
|
||||||
chuni_io_hand_pos++;
|
if (GetAsyncKeyState(chuni_io_cfg.vk_ir_emu)) {
|
||||||
|
if (chuni_io_hand_pos < 6) {
|
||||||
|
chuni_io_hand_pos++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (chuni_io_hand_pos > 0) {
|
||||||
|
chuni_io_hand_pos--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0 ; i < 6 ; i++) {
|
||||||
|
if (chuni_io_hand_pos > i) {
|
||||||
|
*beams |= (1 << i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (chuni_io_hand_pos > 0) {
|
// Use actual AIR
|
||||||
chuni_io_hand_pos--;
|
for (i = 0; i < 6; i++) {
|
||||||
}
|
if(GetAsyncKeyState(chuni_io_cfg.vk_ir[i]) & 0x8000) {
|
||||||
}
|
*beams |= (1 << i);
|
||||||
|
} else {
|
||||||
for (i = 0 ; i < 6 ; i++) {
|
*beams &= ~(1 << i);
|
||||||
if (chuni_io_hand_pos > i) {
|
}
|
||||||
*beams |= (1 << i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT chuni_io_slider_init(void)
|
HRESULT chuni_io_slider_init(void)
|
||||||
{
|
{
|
||||||
return S_OK;
|
return led_output_init(&chuni_io_cfg); // because of slider LEDs
|
||||||
}
|
}
|
||||||
|
|
||||||
void chuni_io_slider_start(chuni_io_slider_callback_t callback)
|
void chuni_io_slider_start(chuni_io_slider_callback_t callback)
|
||||||
{
|
{
|
||||||
|
BOOL status;
|
||||||
|
|
||||||
if (chuni_io_slider_thread != NULL) {
|
if (chuni_io_slider_thread != NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -111,6 +139,7 @@ void chuni_io_slider_stop(void)
|
|||||||
|
|
||||||
void chuni_io_slider_set_leds(const uint8_t *rgb)
|
void chuni_io_slider_set_leds(const uint8_t *rgb)
|
||||||
{
|
{
|
||||||
|
led_output_update(2, rgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx)
|
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx)
|
||||||
@ -136,3 +165,13 @@ static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT chuni_io_led_init(void)
|
||||||
|
{
|
||||||
|
return led_output_init(&chuni_io_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb)
|
||||||
|
{
|
||||||
|
led_output_update(board, rgb);
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
- 0x0100: Initial API version (assumed if chuni_io_get_api_version is not
|
- 0x0100: Initial API version (assumed if chuni_io_get_api_version is not
|
||||||
exported)
|
exported)
|
||||||
- 0x0101: Fix IR beam mappings
|
- 0x0101: Fix IR beam mappings
|
||||||
|
- 0x0102: Add air tower led and billboard support
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@ -15,6 +16,12 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CHUNI_IO_OPBTN_TEST = 0x01,
|
||||||
|
CHUNI_IO_OPBTN_SERVICE = 0x02,
|
||||||
|
CHUNI_IO_OPBTN_COIN = 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
/* Get the version of the Chunithm IO API that this DLL supports. This
|
/* Get the version of the Chunithm IO API that this DLL supports. This
|
||||||
function should return a positive 16-bit integer, where the high byte is
|
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
|
the major version and the low byte is the minor version (as defined by the
|
||||||
@ -139,3 +146,30 @@ void chuni_io_slider_stop(void);
|
|||||||
Minimum API version: 0x0100 */
|
Minimum API version: 0x0100 */
|
||||||
|
|
||||||
void chuni_io_slider_set_leds(const uint8_t *rgb);
|
void chuni_io_slider_set_leds(const uint8_t *rgb);
|
||||||
|
|
||||||
|
/* Initialize LED emulation. This function will be called before any
|
||||||
|
other chuni_io_led_*() function calls.
|
||||||
|
|
||||||
|
All subsequent calls may originate from arbitrary threads and some may
|
||||||
|
overlap with each other. Ensuring synchronization inside your IO DLL is
|
||||||
|
your responsibility.
|
||||||
|
|
||||||
|
Minimum API version: 0x0102 */
|
||||||
|
|
||||||
|
HRESULT chuni_io_led_init(void);
|
||||||
|
|
||||||
|
/* Update the RGB LEDs. rgb is a pointer to an array of up to 63 * 3 = 189 bytes.
|
||||||
|
|
||||||
|
Chunithm uses two chains/boards with WS2811 protocol (each logical led corresponds to 3 physical leds).
|
||||||
|
board 0 is on the left side and board 1 on the right side of the cab
|
||||||
|
|
||||||
|
left side has 5*10 rgb values for the billboard, followed by 3 rgb values for the air tower
|
||||||
|
right side has 6*10 rgb values for the billboard, followed by 3 rgb values for the air tower
|
||||||
|
|
||||||
|
Each rgb value is comprised of 3 bytes in R,G,B order
|
||||||
|
|
||||||
|
NOTE: billboard strips have alternating direction (bottom to top, top to bottom, ...)
|
||||||
|
|
||||||
|
Minimum API version: 0x0102 */
|
||||||
|
|
||||||
|
void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "chuniio/config.h"
|
#include "chuniio/config.h"
|
||||||
|
|
||||||
@ -17,20 +18,35 @@ static const int chuni_io_default_cells[] = {
|
|||||||
'S', 'S', 'S', 'S',
|
'S', 'S', 'S', 'S',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const int chuni_io_default_ir[] = {
|
||||||
|
'4', '5', '6', '7', '8', '9'
|
||||||
|
};
|
||||||
|
|
||||||
void chuni_io_config_load(
|
void chuni_io_config_load(
|
||||||
struct chuni_io_config *cfg,
|
struct chuni_io_config *cfg,
|
||||||
const wchar_t *filename)
|
const wchar_t *filename)
|
||||||
{
|
{
|
||||||
wchar_t key[16];
|
wchar_t key[16];
|
||||||
int i;
|
int i;
|
||||||
|
wchar_t port_input[6];
|
||||||
|
|
||||||
assert(cfg != NULL);
|
assert(cfg != NULL);
|
||||||
assert(filename != NULL);
|
assert(filename != NULL);
|
||||||
|
|
||||||
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename);
|
// Technically it's io4 but leave this for compatibility with old configs.
|
||||||
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename);
|
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", VK_F1, filename);
|
||||||
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename);
|
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", VK_F2, filename);
|
||||||
cfg->vk_ir = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename);
|
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", VK_F3, filename);
|
||||||
|
cfg->vk_ir_emu = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename);
|
||||||
|
|
||||||
|
for (i = 0 ; i < 6 ; i++) {
|
||||||
|
swprintf_s(key, _countof(key), L"ir%i", i + 1);
|
||||||
|
cfg->vk_ir[i] = GetPrivateProfileIntW(
|
||||||
|
L"ir",
|
||||||
|
key,
|
||||||
|
chuni_io_default_ir[i],
|
||||||
|
filename);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0 ; i < 32 ; i++) {
|
for (i = 0 ; i < 32 ; i++) {
|
||||||
swprintf_s(key, _countof(key), L"cell%i", i + 1);
|
swprintf_s(key, _countof(key), L"cell%i", i + 1);
|
||||||
@ -40,4 +56,25 @@ void chuni_io_config_load(
|
|||||||
chuni_io_default_cells[i],
|
chuni_io_default_cells[i],
|
||||||
filename);
|
filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg->led_output_pipe = GetPrivateProfileIntW(L"led", L"cabLedOutputPipe", 1, filename);
|
||||||
|
cfg->led_output_serial = GetPrivateProfileIntW(L"led", L"cabLedOutputSerial", 0, filename);
|
||||||
|
|
||||||
|
cfg->slider_led_output_pipe = GetPrivateProfileIntW(L"led", L"controllerLedOutputPipe", 1, filename);
|
||||||
|
cfg->slider_led_output_serial = GetPrivateProfileIntW(L"led", L"controllerLedOutputSerial", 0, filename);
|
||||||
|
|
||||||
|
cfg->led_serial_baud = GetPrivateProfileIntW(L"led", L"serialBaud", 921600, filename);
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led",
|
||||||
|
L"serialPort",
|
||||||
|
L"COM5",
|
||||||
|
port_input,
|
||||||
|
6,
|
||||||
|
filename);
|
||||||
|
|
||||||
|
// Sanitize the output path. If it's a serial COM port, it needs to be prefixed
|
||||||
|
// with `\\.\`.
|
||||||
|
wcsncpy(cfg->led_serial_port, L"\\\\.\\", 4);
|
||||||
|
wcsncat_s(cfg->led_serial_port, 11, port_input, 6);
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,21 @@ struct chuni_io_config {
|
|||||||
uint8_t vk_test;
|
uint8_t vk_test;
|
||||||
uint8_t vk_service;
|
uint8_t vk_service;
|
||||||
uint8_t vk_coin;
|
uint8_t vk_coin;
|
||||||
uint8_t vk_ir;
|
uint8_t vk_ir_emu;
|
||||||
|
uint8_t vk_ir[6];
|
||||||
uint8_t vk_cell[32];
|
uint8_t vk_cell[32];
|
||||||
|
|
||||||
|
// Which ways to output LED information are enabled
|
||||||
|
bool led_output_pipe;
|
||||||
|
bool led_output_serial;
|
||||||
|
|
||||||
|
bool slider_led_output_pipe;
|
||||||
|
bool slider_led_output_serial;
|
||||||
|
|
||||||
|
// The name of a COM port to output LED data on, in serial mode
|
||||||
|
wchar_t led_serial_port[12];
|
||||||
|
int32_t led_serial_baud;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void chuni_io_config_load(
|
void chuni_io_config_load(
|
||||||
|
22
chuniio/leddata.h
Normal file
22
chuniio/leddata.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define LED_PACKET_FRAMING 0xE0
|
||||||
|
#define LED_PACKET_ESCAPE 0xD0
|
||||||
|
#define LED_NUM_MAX 66
|
||||||
|
#define LED_BOARDS_TOTAL 3
|
||||||
|
#define LED_OUTPUT_HEADER_SIZE 2
|
||||||
|
#define LED_OUTPUT_DATA_SIZE_MAX LED_NUM_MAX * 3 * 2 // max if every byte's escaped
|
||||||
|
#define LED_OUTPUT_TOTAL_SIZE_MAX LED_OUTPUT_HEADER_SIZE + LED_OUTPUT_DATA_SIZE_MAX
|
||||||
|
|
||||||
|
// This struct is used to send data related to the slider and billboard LEDs
|
||||||
|
struct _chuni_led_data_buf_t {
|
||||||
|
byte framing; // Sync byte
|
||||||
|
uint8_t board; // LED output the data is for (0-1: billboard, 2: slider)
|
||||||
|
byte data[LED_OUTPUT_DATA_SIZE_MAX]; // Buffer for LEDs
|
||||||
|
byte data_len; // How many bytes to output from the buffer
|
||||||
|
};
|
||||||
|
|
||||||
|
static byte chuni_led_board_data_lens[LED_BOARDS_TOTAL] = {53*3, 63*3, 31*3};
|
133
chuniio/ledoutput.c
Normal file
133
chuniio/ledoutput.c
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "chuniio/config.h"
|
||||||
|
#include "chuniio/leddata.h"
|
||||||
|
#include "chuniio/ledoutput.h"
|
||||||
|
#include "chuniio/pipeimpl.h"
|
||||||
|
#include "chuniio/serialimpl.h"
|
||||||
|
|
||||||
|
static struct _chuni_led_data_buf_t led_unescaped_buf[LED_BOARDS_TOTAL];
|
||||||
|
static struct _chuni_led_data_buf_t led_escaped_buf[LED_BOARDS_TOTAL];
|
||||||
|
|
||||||
|
static bool led_output_is_init = false;
|
||||||
|
static struct chuni_io_config* config;
|
||||||
|
static bool any_outputs_enabled;
|
||||||
|
|
||||||
|
HANDLE led_init_mutex;
|
||||||
|
|
||||||
|
HRESULT led_output_init(struct chuni_io_config* const cfg)
|
||||||
|
{
|
||||||
|
DWORD dwWaitResult = WaitForSingleObject(led_init_mutex, INFINITE);
|
||||||
|
if (dwWaitResult == WAIT_FAILED)
|
||||||
|
{
|
||||||
|
return HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
// return 1;
|
||||||
|
}
|
||||||
|
else if (dwWaitResult != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
// return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!led_output_is_init)
|
||||||
|
{
|
||||||
|
config = cfg;
|
||||||
|
|
||||||
|
// Setup the framing bytes for the packets
|
||||||
|
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
|
||||||
|
led_unescaped_buf[i].framing = LED_PACKET_FRAMING;
|
||||||
|
led_unescaped_buf[i].board = i;
|
||||||
|
led_unescaped_buf[i].data_len = chuni_led_board_data_lens[i];
|
||||||
|
|
||||||
|
led_escaped_buf[i].framing = LED_PACKET_FRAMING;
|
||||||
|
led_escaped_buf[i].board = i;
|
||||||
|
led_escaped_buf[i].data_len = chuni_led_board_data_lens[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
any_outputs_enabled = config->led_output_pipe || config->slider_led_output_pipe
|
||||||
|
|| config->led_output_serial || config->slider_led_output_serial;
|
||||||
|
|
||||||
|
if (config->led_output_pipe || config->slider_led_output_pipe)
|
||||||
|
{
|
||||||
|
led_pipe_init(); // don't really care about errors here tbh
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->led_output_serial || config->slider_led_output_serial)
|
||||||
|
{
|
||||||
|
led_serial_init(config->led_serial_port, config->led_serial_baud);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
led_output_is_init = true;
|
||||||
|
|
||||||
|
ReleaseMutex(led_init_mutex);
|
||||||
|
return S_OK;
|
||||||
|
// return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _chuni_led_data_buf_t* escape_led_data(struct _chuni_led_data_buf_t* unescaped)
|
||||||
|
{
|
||||||
|
struct _chuni_led_data_buf_t* out_struct = &led_escaped_buf[unescaped->board];
|
||||||
|
|
||||||
|
byte* in_buf = unescaped->data;
|
||||||
|
byte* out_buf = out_struct->data;
|
||||||
|
int i = 0;
|
||||||
|
int o = 0;
|
||||||
|
|
||||||
|
while (i < unescaped->data_len)
|
||||||
|
{
|
||||||
|
byte b = in_buf[i++];
|
||||||
|
if (b == LED_PACKET_FRAMING || b == LED_PACKET_ESCAPE)
|
||||||
|
{
|
||||||
|
out_buf[o++] = LED_PACKET_ESCAPE;
|
||||||
|
b--;
|
||||||
|
}
|
||||||
|
out_buf[o++] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_struct->data_len = o;
|
||||||
|
|
||||||
|
return out_struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_output_update(uint8_t board, const byte* rgb)
|
||||||
|
{
|
||||||
|
if (board < 0 || board > 2 || !any_outputs_enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(led_unescaped_buf[board].data, rgb, led_unescaped_buf[board].data_len);
|
||||||
|
struct _chuni_led_data_buf_t* escaped_data = escape_led_data(&led_unescaped_buf[board]);
|
||||||
|
|
||||||
|
if (board < 2)
|
||||||
|
{
|
||||||
|
// billboard
|
||||||
|
if (config->led_output_pipe)
|
||||||
|
{
|
||||||
|
led_pipe_update(escaped_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->led_output_serial)
|
||||||
|
{
|
||||||
|
led_serial_update(escaped_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// slider
|
||||||
|
if (config->slider_led_output_pipe)
|
||||||
|
{
|
||||||
|
led_pipe_update(escaped_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->slider_led_output_serial)
|
||||||
|
{
|
||||||
|
led_serial_update(escaped_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
chuniio/ledoutput.h
Normal file
19
chuniio/ledoutput.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
LED output functions
|
||||||
|
|
||||||
|
Credits:
|
||||||
|
somewhatlurker, skogaby
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "chuniio/config.h"
|
||||||
|
|
||||||
|
extern HANDLE led_init_mutex;
|
||||||
|
HRESULT led_output_init(struct chuni_io_config* const cfg);
|
||||||
|
void led_output_update(uint8_t board, const byte* rgb);
|
@ -5,9 +5,18 @@ chuniio_lib = static_library(
|
|||||||
implicit_include_directories : false,
|
implicit_include_directories : false,
|
||||||
c_pch : '../precompiled.h',
|
c_pch : '../precompiled.h',
|
||||||
sources : [
|
sources : [
|
||||||
|
'chu2to3.c',
|
||||||
|
'chu2to3.h',
|
||||||
'chuniio.c',
|
'chuniio.c',
|
||||||
'chuniio.h',
|
'chuniio.h',
|
||||||
'config.c',
|
'config.c',
|
||||||
'config.h',
|
'config.h',
|
||||||
|
'leddata.h',
|
||||||
|
'ledoutput.c',
|
||||||
|
'ledoutput.h',
|
||||||
|
'pipeimpl.c',
|
||||||
|
'pipeimpl.h',
|
||||||
|
'serialimpl.c',
|
||||||
|
'serialimpl.h'
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
160
chuniio/pipeimpl.c
Normal file
160
chuniio/pipeimpl.c
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "chuniio/leddata.h"
|
||||||
|
#include "chuniio/pipeimpl.h"
|
||||||
|
|
||||||
|
static bool pipe_update[LED_BOARDS_TOTAL];
|
||||||
|
|
||||||
|
// incoming data is copied into these to ensure it isn't written during output
|
||||||
|
static struct _chuni_led_data_buf_t pipe_write_buf[LED_BOARDS_TOTAL];
|
||||||
|
static HANDLE pipe_write_mutex;
|
||||||
|
|
||||||
|
static HRESULT pipe_create(LPHANDLE hPipe, LPCWSTR lpszPipename, DWORD dwBufSize)
|
||||||
|
{
|
||||||
|
*hPipe = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
*hPipe = CreateNamedPipeW(
|
||||||
|
lpszPipename, // pipe name
|
||||||
|
PIPE_ACCESS_OUTBOUND, // read/write access
|
||||||
|
PIPE_TYPE_BYTE | // byte type pipe
|
||||||
|
PIPE_WAIT, // blocking mode
|
||||||
|
PIPE_UNLIMITED_INSTANCES, // max. instances
|
||||||
|
dwBufSize, // output buffer size
|
||||||
|
0, // input buffer size
|
||||||
|
0, // client time-out
|
||||||
|
NULL); // default security attribute
|
||||||
|
|
||||||
|
if (*hPipe == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT pipe_write(HANDLE hPipe, LPCVOID lpBuffer, DWORD dwSize)
|
||||||
|
{
|
||||||
|
DWORD cbWritten = 0;
|
||||||
|
|
||||||
|
bool fSuccess = WriteFile(
|
||||||
|
hPipe,
|
||||||
|
lpBuffer,
|
||||||
|
dwSize,
|
||||||
|
&cbWritten,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!fSuccess || cbWritten != dwSize)
|
||||||
|
{
|
||||||
|
DWORD last_err = GetLastError();
|
||||||
|
return (last_err == ERROR_BROKEN_PIPE) ? E_ABORT : E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int __stdcall chuni_io_led_pipe_thread_proc(void *ctx)
|
||||||
|
{
|
||||||
|
HANDLE hPipe;
|
||||||
|
LPCWSTR lpszPipename = L"\\\\.\\pipe\\chuni_led";
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
hPipe = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
if (pipe_create(&hPipe, lpszPipename, LED_OUTPUT_TOTAL_SIZE_MAX) != S_OK)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for a connection to the pipe
|
||||||
|
bool fConnected = ConnectNamedPipe(hPipe, NULL) ?
|
||||||
|
true : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||||
|
|
||||||
|
while (fConnected)
|
||||||
|
{
|
||||||
|
if (WaitForSingleObject(pipe_write_mutex, INFINITE) != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
|
||||||
|
if (pipe_update[i])
|
||||||
|
{
|
||||||
|
HRESULT result = pipe_write(
|
||||||
|
hPipe,
|
||||||
|
&pipe_write_buf[i],
|
||||||
|
LED_OUTPUT_HEADER_SIZE + pipe_write_buf[i].data_len);
|
||||||
|
|
||||||
|
if (result != S_OK)
|
||||||
|
{
|
||||||
|
//if (result == E_ABORT)
|
||||||
|
//{
|
||||||
|
fConnected = false;
|
||||||
|
//}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe_update[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseMutex(pipe_write_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlushFileBuffers(hPipe);
|
||||||
|
DisconnectNamedPipe(hPipe);
|
||||||
|
CloseHandle(hPipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT led_pipe_init()
|
||||||
|
{
|
||||||
|
pipe_write_mutex = CreateMutex(
|
||||||
|
NULL, // default security attributes
|
||||||
|
FALSE, // initially not owned
|
||||||
|
NULL); // unnamed mutex
|
||||||
|
|
||||||
|
if (pipe_write_mutex == NULL)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear out update bools
|
||||||
|
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
|
||||||
|
pipe_update[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_beginthreadex(
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
chuni_io_led_pipe_thread_proc,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_pipe_update(struct _chuni_led_data_buf_t* data)
|
||||||
|
{
|
||||||
|
if (data->board > 2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WaitForSingleObject(pipe_write_mutex, INFINITE) != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&pipe_write_buf[data->board], data, sizeof(struct _chuni_led_data_buf_t));
|
||||||
|
pipe_update[data->board] = true;
|
||||||
|
|
||||||
|
ReleaseMutex(pipe_write_mutex);
|
||||||
|
}
|
14
chuniio/pipeimpl.h
Normal file
14
chuniio/pipeimpl.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Pipe implementation for chuniio
|
||||||
|
|
||||||
|
Credits:
|
||||||
|
somewhatlurker, skogaby
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "chuniio/leddata.h"
|
||||||
|
|
||||||
|
HRESULT led_pipe_init();
|
||||||
|
void led_pipe_update(struct _chuni_led_data_buf_t* data);
|
99
chuniio/serialimpl.c
Normal file
99
chuniio/serialimpl.c
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "chuniio/leddata.h"
|
||||||
|
#include "chuniio/serialimpl.h"
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
static HANDLE serial_port;
|
||||||
|
static HANDLE serial_write_mutex;
|
||||||
|
|
||||||
|
HRESULT led_serial_init(wchar_t led_com[12], DWORD baud)
|
||||||
|
{
|
||||||
|
// Setup the serial communications
|
||||||
|
BOOL status;
|
||||||
|
|
||||||
|
serial_port = CreateFileW(led_com,
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (serial_port == INVALID_HANDLE_VALUE)
|
||||||
|
dprintf("Chunithm Serial LEDs: Failed to open COM port (Attempted on %S)\n", led_com);
|
||||||
|
else
|
||||||
|
dprintf("Chunithm Serial LEDs: COM port success!\n");
|
||||||
|
|
||||||
|
DCB dcb_serial_params = { 0 };
|
||||||
|
dcb_serial_params.DCBlength = sizeof(dcb_serial_params);
|
||||||
|
status = GetCommState(serial_port, &dcb_serial_params);
|
||||||
|
|
||||||
|
dcb_serial_params.BaudRate = baud;
|
||||||
|
dcb_serial_params.ByteSize = 8;
|
||||||
|
dcb_serial_params.StopBits = ONESTOPBIT;
|
||||||
|
dcb_serial_params.Parity = NOPARITY;
|
||||||
|
SetCommState(serial_port, &dcb_serial_params);
|
||||||
|
|
||||||
|
COMMTIMEOUTS timeouts = { 0 };
|
||||||
|
timeouts.ReadIntervalTimeout = 50;
|
||||||
|
timeouts.ReadTotalTimeoutConstant = 50;
|
||||||
|
timeouts.ReadTotalTimeoutMultiplier = 10;
|
||||||
|
timeouts.WriteTotalTimeoutConstant = 50;
|
||||||
|
timeouts.WriteTotalTimeoutMultiplier = 10;
|
||||||
|
|
||||||
|
SetCommTimeouts(serial_port, &timeouts);
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
serial_write_mutex = CreateMutex(
|
||||||
|
NULL, // default security attributes
|
||||||
|
FALSE, // initially not owned
|
||||||
|
NULL); // unnamed mutex
|
||||||
|
|
||||||
|
if (serial_write_mutex == NULL)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_serial_update(struct _chuni_led_data_buf_t* data)
|
||||||
|
{
|
||||||
|
if (data->board > 2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WaitForSingleObject(serial_write_mutex, INFINITE) != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL status = true;
|
||||||
|
DWORD bytes_written = 0;
|
||||||
|
|
||||||
|
if (serial_port != INVALID_HANDLE_VALUE) {
|
||||||
|
status = WriteFile(
|
||||||
|
serial_port,
|
||||||
|
data,
|
||||||
|
LED_OUTPUT_HEADER_SIZE + data->data_len,
|
||||||
|
&bytes_written,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
DWORD last_err = GetLastError();
|
||||||
|
// dprintf("Chunithm Serial LEDs: Serial port write failed -- %d\n", last_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseMutex(serial_write_mutex);
|
||||||
|
}
|
15
chuniio/serialimpl.h
Normal file
15
chuniio/serialimpl.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
Serial LED implementation for chuniio
|
||||||
|
|
||||||
|
Credits:
|
||||||
|
somewhatlurker, skogaby
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "chuniio/leddata.h"
|
||||||
|
|
||||||
|
HRESULT led_serial_init(wchar_t led_com[12], DWORD baud);
|
||||||
|
void led_serial_update(struct _chuni_led_data_buf_t* data);
|
189
chusanhook/chuni-dll.c
Normal file
189
chusanhook/chuni-dll.c
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "chuniio/chu2to3.h"
|
||||||
|
#include "chusanhook/chuni-dll.h"
|
||||||
|
|
||||||
|
#include "util/dll-bind.h"
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
const struct dll_bind_sym chuni_dll_syms[] = {
|
||||||
|
{
|
||||||
|
.sym = "chuni_io_jvs_init",
|
||||||
|
.off = offsetof(struct chuni_dll, jvs_init),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_jvs_poll",
|
||||||
|
.off = offsetof(struct chuni_dll, jvs_poll),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_jvs_read_coin_counter",
|
||||||
|
.off = offsetof(struct chuni_dll, jvs_read_coin_counter),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_slider_init",
|
||||||
|
.off = offsetof(struct chuni_dll, slider_init),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_slider_start",
|
||||||
|
.off = offsetof(struct chuni_dll, slider_start),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_slider_stop",
|
||||||
|
.off = offsetof(struct chuni_dll, slider_stop),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_slider_set_leds",
|
||||||
|
.off = offsetof(struct chuni_dll, slider_set_leds),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_led_init",
|
||||||
|
.off = offsetof(struct chuni_dll, led_init),
|
||||||
|
}, {
|
||||||
|
.sym = "chuni_io_led_set_colors",
|
||||||
|
.off = offsetof(struct chuni_dll, led_set_leds),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct dll_bind_sym chu2to3_dll_syms[] = {
|
||||||
|
{
|
||||||
|
.sym = "chu2to3_io_jvs_init",
|
||||||
|
.off = offsetof(struct chuni_dll, jvs_init),
|
||||||
|
}, {
|
||||||
|
.sym = "chu2to3_io_jvs_poll",
|
||||||
|
.off = offsetof(struct chuni_dll, jvs_poll),
|
||||||
|
}, {
|
||||||
|
.sym = "chu2to3_io_jvs_read_coin_counter",
|
||||||
|
.off = offsetof(struct chuni_dll, jvs_read_coin_counter),
|
||||||
|
}, {
|
||||||
|
.sym = "chu2to3_io_slider_init",
|
||||||
|
.off = offsetof(struct chuni_dll, slider_init),
|
||||||
|
}, {
|
||||||
|
.sym = "chu2to3_io_slider_start",
|
||||||
|
.off = offsetof(struct chuni_dll, slider_start),
|
||||||
|
}, {
|
||||||
|
.sym = "chu2to3_io_slider_stop",
|
||||||
|
.off = offsetof(struct chuni_dll, slider_stop),
|
||||||
|
}, {
|
||||||
|
.sym = "chu2to3_io_slider_set_leds",
|
||||||
|
.off = offsetof(struct chuni_dll, slider_set_leds),
|
||||||
|
}, {
|
||||||
|
.sym = "chu2to3_io_led_init",
|
||||||
|
.off = offsetof(struct chuni_dll, led_init),
|
||||||
|
}, {
|
||||||
|
.sym = "chu2to3_io_led_set_colors",
|
||||||
|
.off = offsetof(struct chuni_dll, led_set_leds),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Helper function to determine upon dll_bind failure whether the required functions were found
|
||||||
|
NOTE: relies on symbols order declared above */
|
||||||
|
static HRESULT has_enough_symbols(uint16_t version, uint8_t count)
|
||||||
|
{
|
||||||
|
if ( version <= 0x0101 && count == 7 )
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
if ( version >= 0x0102 && count == 9 )
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct chuni_dll chuni_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 chuni_dll_init(const struct chuni_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);
|
||||||
|
|
||||||
|
owned = NULL;
|
||||||
|
src = self;
|
||||||
|
|
||||||
|
if (cfg->chu2to3) {
|
||||||
|
dprintf("Chunithm IO: using chu2to3 engine for IO DLL: %S\n", cfg->path);
|
||||||
|
} else if (cfg->path[0] != L'\0') {
|
||||||
|
owned = LoadLibraryW(cfg->path);
|
||||||
|
|
||||||
|
if (owned == NULL) {
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
dprintf("Chunithm IO: Failed to load IO DLL: %lx: %S\n",
|
||||||
|
hr,
|
||||||
|
cfg->path);
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("Chunithm IO: Using custom IO DLL: %S\n", cfg->path);
|
||||||
|
src = owned;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->chu2to3) {
|
||||||
|
if (chu2to3_load_dll(cfg->path) != 0)
|
||||||
|
dprintf("Could not init chu2to3 engine\n");
|
||||||
|
|
||||||
|
get_api_version = (void *) GetProcAddress(src, "chu2to3_io_get_api_version");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
get_api_version = (void *) GetProcAddress(src, "chuni_io_get_api_version");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_api_version != NULL) {
|
||||||
|
chuni_dll.api_version = get_api_version();
|
||||||
|
} else {
|
||||||
|
chuni_dll.api_version = 0x0100;
|
||||||
|
dprintf("Custom IO DLL does not expose chuni_io_get_api_version, "
|
||||||
|
"assuming API version 1.0.\n"
|
||||||
|
"Please ask the developer to update their DLL.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chuni_dll.api_version >= 0x0200) {
|
||||||
|
hr = E_NOTIMPL;
|
||||||
|
dprintf("Chunithm IO: Custom IO DLL implements an unsupported "
|
||||||
|
"API version (%#04x). Please update Segatools.\n",
|
||||||
|
chuni_dll.api_version);
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
sym = cfg->chu2to3 ? chu2to3_dll_syms : chuni_dll_syms;
|
||||||
|
const struct dll_bind_sym *init_sym = &sym[0];
|
||||||
|
|
||||||
|
hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms));
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
if (src != self) {
|
||||||
|
// Might still be ok depending on external dll API version
|
||||||
|
int bind_count = sym - init_sym;
|
||||||
|
if ( has_enough_symbols(chuni_dll.api_version, bind_count) == S_OK )
|
||||||
|
{
|
||||||
|
hr = S_OK;
|
||||||
|
} else {
|
||||||
|
dprintf("Chunithm IO: Custom IO DLL does not provide function "
|
||||||
|
"\"%s\". Please contact your IO DLL's developer for "
|
||||||
|
"further assistance.\n",
|
||||||
|
sym->sym);
|
||||||
|
dprintf("imported %d symbols\n",bind_count);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
owned = NULL;
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (owned != NULL) {
|
||||||
|
FreeLibrary(owned);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
27
chusanhook/chuni-dll.h
Normal file
27
chusanhook/chuni-dll.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "chuniio/chuniio.h"
|
||||||
|
|
||||||
|
struct chuni_dll {
|
||||||
|
uint16_t api_version;
|
||||||
|
HRESULT (*jvs_init)(void);
|
||||||
|
void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams);
|
||||||
|
void (*jvs_read_coin_counter)(uint16_t *total);
|
||||||
|
HRESULT (*slider_init)(void);
|
||||||
|
void (*slider_start)(chuni_io_slider_callback_t callback);
|
||||||
|
void (*slider_stop)(void);
|
||||||
|
void (*slider_set_leds)(const uint8_t *rgb);
|
||||||
|
HRESULT (*led_init)(void);
|
||||||
|
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct chuni_dll_config {
|
||||||
|
wchar_t path[MAX_PATH];
|
||||||
|
uint8_t chu2to3;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct chuni_dll chuni_dll;
|
||||||
|
|
||||||
|
HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self);
|
34
chusanhook/chusanhook.def
Normal file
34
chusanhook/chusanhook.def
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
LIBRARY chusanhook
|
||||||
|
|
||||||
|
EXPORTS
|
||||||
|
Direct3DCreate9
|
||||||
|
aime_io_get_api_version
|
||||||
|
aime_io_init
|
||||||
|
aime_io_led_set_color
|
||||||
|
aime_io_nfc_get_aime_id
|
||||||
|
aime_io_nfc_get_felica_id
|
||||||
|
aime_io_nfc_poll
|
||||||
|
amDllVideoClose @2
|
||||||
|
amDllVideoGetVBiosVersion @4
|
||||||
|
amDllVideoOpen @1
|
||||||
|
amDllVideoSetResolution @3
|
||||||
|
chuni_io_get_api_version
|
||||||
|
chuni_io_jvs_init
|
||||||
|
chuni_io_jvs_poll
|
||||||
|
chuni_io_jvs_read_coin_counter
|
||||||
|
chuni_io_slider_init
|
||||||
|
chuni_io_slider_set_leds
|
||||||
|
chuni_io_slider_start
|
||||||
|
chuni_io_slider_stop
|
||||||
|
chuni_io_led_init
|
||||||
|
chuni_io_led_set_colors
|
||||||
|
chu2to3_io_get_api_version
|
||||||
|
chu2to3_io_jvs_init
|
||||||
|
chu2to3_io_jvs_poll
|
||||||
|
chu2to3_io_jvs_read_coin_counter
|
||||||
|
chu2to3_io_slider_init
|
||||||
|
chu2to3_io_slider_set_leds
|
||||||
|
chu2to3_io_slider_start
|
||||||
|
chu2to3_io_slider_stop
|
||||||
|
chu2to3_io_led_init
|
||||||
|
chu2to3_io_led_set_colors
|
167
chusanhook/config.c
Normal file
167
chusanhook/config.c
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "board/config.h"
|
||||||
|
|
||||||
|
#include "hooklib/config.h"
|
||||||
|
#include "hooklib/dvd.h"
|
||||||
|
|
||||||
|
#include "gfxhook/config.h"
|
||||||
|
|
||||||
|
#include "platform/config.h"
|
||||||
|
|
||||||
|
#include "chusanhook/config.h"
|
||||||
|
|
||||||
|
// Check windows
|
||||||
|
#if _WIN32 || _WIN64
|
||||||
|
#if _WIN64
|
||||||
|
#define ENV64BIT
|
||||||
|
#else
|
||||||
|
#define ENV32BIT
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check GCC
|
||||||
|
#if __GNUC__
|
||||||
|
#if __x86_64__ || __ppc64__
|
||||||
|
#define ENV64BIT
|
||||||
|
#else
|
||||||
|
#define ENV32BIT
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void chuni_dll_config_load(
|
||||||
|
struct chuni_dll_config *cfg,
|
||||||
|
const wchar_t *filename)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
// Workaround for x64/x86 external IO dlls
|
||||||
|
// path32 for 32bit, path64 for 64bit
|
||||||
|
// path for 32bit only dlls (internal chu2to3 engine)
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"chuniio",
|
||||||
|
L"path",
|
||||||
|
L"",
|
||||||
|
cfg->path,
|
||||||
|
_countof(cfg->path),
|
||||||
|
filename);
|
||||||
|
if (cfg->path[0] != L'\0') {
|
||||||
|
cfg->chu2to3 = 1;
|
||||||
|
} else {
|
||||||
|
cfg->chu2to3 = 0;
|
||||||
|
#if defined(ENV32BIT)
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"chuniio",
|
||||||
|
L"path32",
|
||||||
|
L"",
|
||||||
|
cfg->path,
|
||||||
|
_countof(cfg->path),
|
||||||
|
filename);
|
||||||
|
#elif defined(ENV64BIT)
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"chuniio",
|
||||||
|
L"path64",
|
||||||
|
L"",
|
||||||
|
cfg->path,
|
||||||
|
_countof(cfg->path),
|
||||||
|
filename);
|
||||||
|
#else
|
||||||
|
#error "Unknown environment"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void slider_config_load(struct slider_config *cfg, const wchar_t *filename)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
wchar_t tmpstr[16];
|
||||||
|
bool cvt_port;
|
||||||
|
|
||||||
|
memset(cfg->board_number, ' ', sizeof(cfg->board_number));
|
||||||
|
memset(cfg->chip_number, ' ', sizeof(cfg->chip_number));
|
||||||
|
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
|
||||||
|
|
||||||
|
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
|
||||||
|
cfg->port_no = 0;
|
||||||
|
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
|
||||||
|
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0x90, filename);
|
||||||
|
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xadf7, filename);
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led15093",
|
||||||
|
L"boardNumber",
|
||||||
|
L"15093-06",
|
||||||
|
tmpstr,
|
||||||
|
_countof(tmpstr),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number));
|
||||||
|
for (int i = n; i < sizeof(cfg->board_number); i++)
|
||||||
|
{
|
||||||
|
cfg->board_number[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led15093",
|
||||||
|
L"chipNumber",
|
||||||
|
L"6710 ",
|
||||||
|
tmpstr,
|
||||||
|
_countof(tmpstr),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number));
|
||||||
|
for (int i = n; i < sizeof(cfg->chip_number); i++)
|
||||||
|
{
|
||||||
|
cfg->chip_number[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led15093",
|
||||||
|
L"bootChipNumber",
|
||||||
|
L"6709 ",
|
||||||
|
tmpstr,
|
||||||
|
_countof(tmpstr),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
n = wcstombs(cfg->boot_chip_number, tmpstr, sizeof(cfg->boot_chip_number));
|
||||||
|
for (int i = n; i < sizeof(cfg->boot_chip_number); i++)
|
||||||
|
{
|
||||||
|
cfg->boot_chip_number[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void chusan_hook_config_load(
|
||||||
|
struct chusan_hook_config *cfg,
|
||||||
|
const wchar_t *filename)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
memset(cfg, 0, sizeof(*cfg));
|
||||||
|
|
||||||
|
// Force load the 64bit Aime DLL instead of the 32bit one
|
||||||
|
cfg->aime.dll.path64 = true;
|
||||||
|
|
||||||
|
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);
|
||||||
|
vfd_config_load(&cfg->vfd, filename);
|
||||||
|
chuni_dll_config_load(&cfg->dll, filename);
|
||||||
|
slider_config_load(&cfg->slider, filename);
|
||||||
|
led15093_config_load(&cfg->led15093, filename);
|
||||||
|
}
|
35
chusanhook/config.h
Normal file
35
chusanhook/config.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "board/config.h"
|
||||||
|
#include "board/led15093.h"
|
||||||
|
|
||||||
|
#include "hooklib/dvd.h"
|
||||||
|
|
||||||
|
#include "gfxhook/config.h"
|
||||||
|
|
||||||
|
#include "platform/config.h"
|
||||||
|
|
||||||
|
#include "chusanhook/chuni-dll.h"
|
||||||
|
#include "chusanhook/slider.h"
|
||||||
|
|
||||||
|
struct chusan_hook_config {
|
||||||
|
struct platform_config platform;
|
||||||
|
struct aime_config aime;
|
||||||
|
struct dvd_config dvd;
|
||||||
|
struct io4_config io4;
|
||||||
|
struct gfx_config gfx;
|
||||||
|
struct vfd_config vfd;
|
||||||
|
struct chuni_dll_config dll;
|
||||||
|
struct slider_config slider;
|
||||||
|
struct led15093_config led15093;
|
||||||
|
};
|
||||||
|
|
||||||
|
void chuni_dll_config_load(
|
||||||
|
struct chuni_dll_config *cfg,
|
||||||
|
const wchar_t *filename);
|
||||||
|
void slider_config_load(struct slider_config *cfg, const wchar_t *filename);
|
||||||
|
void chusan_hook_config_load(
|
||||||
|
struct chusan_hook_config *cfg,
|
||||||
|
const wchar_t *filename);
|
181
chusanhook/dllmain.c
Normal file
181
chusanhook/dllmain.c
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "amex/amex.h"
|
||||||
|
|
||||||
|
#include "board/sg-reader.h"
|
||||||
|
#include "board/vfd.h"
|
||||||
|
|
||||||
|
#include "chusanhook/config.h"
|
||||||
|
#include "chusanhook/io4.h"
|
||||||
|
#include "chusanhook/slider.h"
|
||||||
|
|
||||||
|
#include "chuniio/chuniio.h"
|
||||||
|
|
||||||
|
#include "hook/process.h"
|
||||||
|
|
||||||
|
#include "gfxhook/d3d9.h"
|
||||||
|
#include "gfxhook/gfx.h"
|
||||||
|
|
||||||
|
#include "hooklib/serial.h"
|
||||||
|
#include "hooklib/spike.h"
|
||||||
|
|
||||||
|
#include "platform/platform.h"
|
||||||
|
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
static HMODULE chusan_hook_mod;
|
||||||
|
static process_entry_t chusan_startup;
|
||||||
|
static struct chusan_hook_config chusan_hook_cfg;
|
||||||
|
|
||||||
|
static DWORD CALLBACK chusan_pre_startup(void)
|
||||||
|
{
|
||||||
|
HMODULE d3dc;
|
||||||
|
HMODULE dbghelp;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
dprintf("--- Begin chusan_pre_startup ---\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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Config load */
|
||||||
|
|
||||||
|
chusan_hook_config_load(&chusan_hook_cfg, L".\\segatools.ini");
|
||||||
|
|
||||||
|
/* Hook Win32 APIs */
|
||||||
|
|
||||||
|
dvd_hook_init(&chusan_hook_cfg.dvd, chusan_hook_mod);
|
||||||
|
gfx_hook_init(&chusan_hook_cfg.gfx);
|
||||||
|
gfx_d3d9_hook_init(&chusan_hook_cfg.gfx, chusan_hook_mod);
|
||||||
|
serial_hook_init();
|
||||||
|
|
||||||
|
/* Initialize emulation hooks */
|
||||||
|
|
||||||
|
hr = platform_hook_init(
|
||||||
|
&chusan_hook_cfg.platform,
|
||||||
|
"SDHD",
|
||||||
|
"ACA2",
|
||||||
|
chusan_hook_mod);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = chuni_dll_init(&chusan_hook_cfg.dll, chusan_hook_mod);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = chusan_io4_hook_init(&chusan_hook_cfg.io4);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = slider_hook_init(&chusan_hook_cfg.slider);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool *dipsw = &chusan_hook_cfg.platform.dipsw.dipsw[0];
|
||||||
|
bool is_cvt = dipsw[2];
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
dprintf("DipSw: NetInstall: %s\n", dipsw[0] ? "Server" : "Client");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
dprintf("DipSw: Monitor Type: %dFPS\n", dipsw[1] ? 60 : 120);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
dprintf("DipSw: Cab Type: %s\n", is_cvt ? "CVT" : "SP");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int first_port = is_cvt ? 2 : 20;
|
||||||
|
|
||||||
|
if (!is_cvt) {
|
||||||
|
hr = vfd_hook_init(&chusan_hook_cfg.vfd, 2);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL )
|
||||||
|
{
|
||||||
|
dprintf("IO DLL doesn't support led_init/led_set_leds, cannot start LED15093 hook\n");
|
||||||
|
} else {
|
||||||
|
hr = led15093_hook_init(&chusan_hook_cfg.led15093,
|
||||||
|
chuni_dll.led_init, chuni_dll.led_set_leds, first_port, 2, 2, 1);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, is_cvt ? 2: 3, chusan_hook_mod);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize debug helpers */
|
||||||
|
|
||||||
|
spike_hook_init(L".\\segatools.ini");
|
||||||
|
|
||||||
|
dprintf("--- End chusan_pre_startup ---\n");
|
||||||
|
|
||||||
|
/* Jump to EXE start address */
|
||||||
|
|
||||||
|
return chusan_startup();
|
||||||
|
|
||||||
|
fail:
|
||||||
|
ExitProcess(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
if (cause != DLL_PROCESS_ATTACH) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
chusan_hook_mod = mod;
|
||||||
|
|
||||||
|
hr = process_hijack_startup(chusan_pre_startup, &chusan_startup);
|
||||||
|
|
||||||
|
if (!SUCCEEDED(hr)) {
|
||||||
|
dprintf("Failed to hijack process startup: %x\n", (int) hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCEEDED(hr);
|
||||||
|
}
|
106
chusanhook/io4.c
Normal file
106
chusanhook/io4.c
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "board/io4.h"
|
||||||
|
|
||||||
|
#include "chusanhook/chuni-dll.h"
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
struct chunithm_jvs_ir_mask {
|
||||||
|
uint16_t p1;
|
||||||
|
uint16_t p2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Incorrect IR beam mappings retained for backward compatibility
|
||||||
|
static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks_v1[] = {
|
||||||
|
{ 0, 1 << 13 },
|
||||||
|
{ 1 << 13, 0 },
|
||||||
|
{ 0, 1 << 12 },
|
||||||
|
{ 1 << 12, 0 },
|
||||||
|
{ 0, 1 << 11 },
|
||||||
|
{ 1 << 11, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks[] = {
|
||||||
|
{ 1 << 13, 0 },
|
||||||
|
{ 0, 1 << 13 },
|
||||||
|
{ 1 << 12, 0 },
|
||||||
|
{ 0, 1 << 12 },
|
||||||
|
{ 1 << 11, 0 },
|
||||||
|
{ 0, 1 << 11 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static HRESULT chusan_io4_poll(void* ctx, struct io4_state* state);
|
||||||
|
static uint16_t coins;
|
||||||
|
|
||||||
|
static const struct io4_ops chusan_io4_ops = {
|
||||||
|
.poll = chusan_io4_poll,
|
||||||
|
};
|
||||||
|
|
||||||
|
HRESULT chusan_io4_hook_init(const struct io4_config* cfg)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
assert(chuni_dll.jvs_init != NULL);
|
||||||
|
|
||||||
|
dprintf("USB I/O: Starting IO backend\n");
|
||||||
|
hr = chuni_dll.jvs_init();
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
dprintf("USB I/O: Backend error, I/O disconnected: %x\n", (int)hr);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
io4_hook_init(cfg, &chusan_io4_ops, NULL);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT chusan_io4_poll(void* ctx, struct io4_state* state)
|
||||||
|
{
|
||||||
|
const struct chunithm_jvs_ir_mask *masks;
|
||||||
|
uint8_t opbtn;
|
||||||
|
uint8_t beams;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
memset(state, 0, sizeof(*state));
|
||||||
|
|
||||||
|
opbtn = 0;
|
||||||
|
beams = 0;
|
||||||
|
|
||||||
|
chuni_dll.jvs_poll(&opbtn, &beams);
|
||||||
|
chuni_dll.jvs_read_coin_counter(&coins);
|
||||||
|
|
||||||
|
if (chuni_dll.api_version >= 0x0101) {
|
||||||
|
// Use correct mapping
|
||||||
|
masks = chunithm_jvs_ir_masks;
|
||||||
|
} else {
|
||||||
|
// Use backwards-compatible incorrect mapping
|
||||||
|
masks = chunithm_jvs_ir_masks_v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opbtn & CHUNI_IO_OPBTN_TEST) {
|
||||||
|
state->buttons[0] |= IO4_BUTTON_TEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opbtn & CHUNI_IO_OPBTN_SERVICE) {
|
||||||
|
state->buttons[0] |= IO4_BUTTON_SERVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the coin counter with the value from jvs_read_coin_counter
|
||||||
|
state->chutes[0] = coins << 8;
|
||||||
|
|
||||||
|
for (i = 0; i < 6; i++) {
|
||||||
|
/* Beam "press" is active-low hence the ~ */
|
||||||
|
if (~beams & (1 << i)) {
|
||||||
|
state->buttons[0] |= masks[i].p1;
|
||||||
|
state->buttons[1] |= masks[i].p2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
5
chusanhook/io4.h
Normal file
5
chusanhook/io4.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
HRESULT chusan_io4_hook_init(const struct io4_config *cfg);
|
32
chusanhook/meson.build
Normal file
32
chusanhook/meson.build
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
shared_library(
|
||||||
|
'chusanhook',
|
||||||
|
name_prefix : '',
|
||||||
|
include_directories : inc,
|
||||||
|
implicit_include_directories : false,
|
||||||
|
vs_module_defs : 'chusanhook.def',
|
||||||
|
c_pch : '../precompiled.h',
|
||||||
|
dependencies : [
|
||||||
|
capnhook.get_variable('hook_dep'),
|
||||||
|
capnhook.get_variable('hooklib_dep'),
|
||||||
|
],
|
||||||
|
link_with : [
|
||||||
|
aimeio_lib,
|
||||||
|
board_lib,
|
||||||
|
chuniio_lib,
|
||||||
|
gfxhook_lib,
|
||||||
|
hooklib_lib,
|
||||||
|
platform_lib,
|
||||||
|
util_lib,
|
||||||
|
],
|
||||||
|
sources : [
|
||||||
|
'chuni-dll.c',
|
||||||
|
'chuni-dll.h',
|
||||||
|
'config.c',
|
||||||
|
'config.h',
|
||||||
|
'dllmain.c',
|
||||||
|
'io4.c',
|
||||||
|
'io4.h',
|
||||||
|
'slider.c',
|
||||||
|
'slider.h',
|
||||||
|
],
|
||||||
|
)
|
249
chusanhook/slider.c
Normal file
249
chusanhook/slider.c
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <process.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "board/slider-cmd.h"
|
||||||
|
#include "board/slider-frame.h"
|
||||||
|
|
||||||
|
#include "chusanhook/chuni-dll.h"
|
||||||
|
#include "chusanhook/slider.h"
|
||||||
|
|
||||||
|
#include "hook/iobuf.h"
|
||||||
|
#include "hook/iohook.h"
|
||||||
|
|
||||||
|
#include "hooklib/uart.h"
|
||||||
|
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
#include "util/dump.h"
|
||||||
|
|
||||||
|
static HRESULT slider_handle_irp(struct irp *irp);
|
||||||
|
static HRESULT slider_handle_irp_locked(struct irp *irp);
|
||||||
|
|
||||||
|
static HRESULT slider_req_dispatch(const union slider_req_any *req);
|
||||||
|
static HRESULT slider_req_reset(void);
|
||||||
|
static HRESULT slider_req_get_board_info(void);
|
||||||
|
static HRESULT slider_req_auto_scan_start(void);
|
||||||
|
static HRESULT slider_req_auto_scan_stop(void);
|
||||||
|
static HRESULT slider_req_set_led(const struct slider_req_set_led *req);
|
||||||
|
|
||||||
|
static void slider_res_auto_scan(const uint8_t *state);
|
||||||
|
|
||||||
|
static CRITICAL_SECTION slider_lock;
|
||||||
|
static struct uart slider_uart;
|
||||||
|
static uint8_t slider_written_bytes[520];
|
||||||
|
static uint8_t slider_readable_bytes[520];
|
||||||
|
|
||||||
|
HRESULT slider_hook_init(const struct slider_config *cfg)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(chuni_dll.slider_init != NULL);
|
||||||
|
|
||||||
|
if (!cfg->enable) {
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeCriticalSection(&slider_lock);
|
||||||
|
|
||||||
|
uart_init(&slider_uart, 1);
|
||||||
|
slider_uart.written.bytes = slider_written_bytes;
|
||||||
|
slider_uart.written.nbytes = sizeof(slider_written_bytes);
|
||||||
|
slider_uart.readable.bytes = slider_readable_bytes;
|
||||||
|
slider_uart.readable.nbytes = sizeof(slider_readable_bytes);
|
||||||
|
|
||||||
|
return iohook_push_handler(slider_handle_irp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT slider_handle_irp(struct irp *irp)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
assert(irp != NULL);
|
||||||
|
|
||||||
|
if (!uart_match_irp(&slider_uart, irp)) {
|
||||||
|
return iohook_invoke_next(irp);
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterCriticalSection(&slider_lock);
|
||||||
|
hr = slider_handle_irp_locked(irp);
|
||||||
|
LeaveCriticalSection(&slider_lock);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT slider_handle_irp_locked(struct irp *irp)
|
||||||
|
{
|
||||||
|
union slider_req_any req;
|
||||||
|
struct iobuf req_iobuf;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
if (irp->op == IRP_OP_OPEN) {
|
||||||
|
dprintf("Chunithm slider: Starting backend\n");
|
||||||
|
hr = chuni_dll.slider_init();
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
dprintf("Chunithm slider: Backend error: %x\n", (int) hr);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = uart_handle_irp(&slider_uart, irp);
|
||||||
|
|
||||||
|
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
#if 0
|
||||||
|
dprintf("TX Buffer:\n");
|
||||||
|
dump_iobuf(&slider_uart.written);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
req_iobuf.bytes = req.bytes;
|
||||||
|
req_iobuf.nbytes = sizeof(req.bytes);
|
||||||
|
req_iobuf.pos = 0;
|
||||||
|
|
||||||
|
hr = slider_frame_decode(&req_iobuf, &slider_uart.written);
|
||||||
|
|
||||||
|
if (hr != S_OK) {
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
dprintf("Chunithm slider: Deframe error: %x\n", (int) hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
dprintf("Deframe Buffer:\n");
|
||||||
|
dump_iobuf(&req_iobuf);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
hr = slider_req_dispatch(&req);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
dprintf("Chunithm slider: Processing error: %x\n", (int) hr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT slider_req_dispatch(const union slider_req_any *req)
|
||||||
|
{
|
||||||
|
switch (req->hdr.cmd) {
|
||||||
|
case SLIDER_CMD_RESET:
|
||||||
|
return slider_req_reset();
|
||||||
|
|
||||||
|
case SLIDER_CMD_GET_BOARD_INFO:
|
||||||
|
return slider_req_get_board_info();
|
||||||
|
|
||||||
|
case SLIDER_CMD_SET_LED:
|
||||||
|
return slider_req_set_led(&req->set_led);
|
||||||
|
|
||||||
|
case SLIDER_CMD_AUTO_SCAN_START:
|
||||||
|
return slider_req_auto_scan_start();
|
||||||
|
|
||||||
|
case SLIDER_CMD_AUTO_SCAN_STOP:
|
||||||
|
return slider_req_auto_scan_stop();
|
||||||
|
|
||||||
|
default:
|
||||||
|
dprintf("Unhandled command %02x\n", req->hdr.cmd);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT slider_req_reset(void)
|
||||||
|
{
|
||||||
|
struct slider_hdr resp;
|
||||||
|
|
||||||
|
dprintf("Chunithm slider: Reset\n");
|
||||||
|
|
||||||
|
resp.sync = 0xFF;
|
||||||
|
resp.cmd = SLIDER_CMD_RESET;
|
||||||
|
resp.nbytes = 0;
|
||||||
|
|
||||||
|
return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT slider_req_get_board_info(void)
|
||||||
|
{
|
||||||
|
struct slider_resp_get_board_info resp;
|
||||||
|
|
||||||
|
dprintf("Chunithm slider: Get firmware version\n");
|
||||||
|
|
||||||
|
memset(&resp, 0, sizeof(resp));
|
||||||
|
resp.hdr.sync = SLIDER_FRAME_SYNC;
|
||||||
|
resp.hdr.cmd = SLIDER_CMD_GET_BOARD_INFO;
|
||||||
|
resp.hdr.nbytes = sizeof(resp.version);
|
||||||
|
|
||||||
|
strcpy_s(
|
||||||
|
resp.version,
|
||||||
|
sizeof(resp.version),
|
||||||
|
"15330 \xA0" "06712\xFF" "\x90");
|
||||||
|
|
||||||
|
return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT slider_req_auto_scan_start(void)
|
||||||
|
{
|
||||||
|
assert(chuni_dll.slider_start != NULL);
|
||||||
|
|
||||||
|
dprintf("Chunithm slider: Start slider notifications\n");
|
||||||
|
chuni_dll.slider_start(slider_res_auto_scan);
|
||||||
|
|
||||||
|
/* This message is not acknowledged */
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT slider_req_auto_scan_stop(void)
|
||||||
|
{
|
||||||
|
struct slider_hdr resp;
|
||||||
|
|
||||||
|
assert(chuni_dll.slider_stop != NULL);
|
||||||
|
|
||||||
|
dprintf("Chunithm slider: Stop slider notifications\n");
|
||||||
|
|
||||||
|
/* IO DLL worker thread might attempt to invoke the callback (which needs
|
||||||
|
to take slider_lock, which we are currently holding) before noticing that
|
||||||
|
it needs to shut down. Unlock here so that we don't deadlock in that
|
||||||
|
situation. */
|
||||||
|
|
||||||
|
LeaveCriticalSection(&slider_lock);
|
||||||
|
chuni_dll.slider_stop();
|
||||||
|
EnterCriticalSection(&slider_lock);
|
||||||
|
|
||||||
|
resp.sync = SLIDER_FRAME_SYNC;
|
||||||
|
resp.cmd = SLIDER_CMD_AUTO_SCAN_STOP;
|
||||||
|
resp.nbytes = 0;
|
||||||
|
|
||||||
|
return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT slider_req_set_led(const struct slider_req_set_led *req)
|
||||||
|
{
|
||||||
|
assert(chuni_dll.slider_set_leds != NULL);
|
||||||
|
|
||||||
|
chuni_dll.slider_set_leds(req->payload.rgb);
|
||||||
|
|
||||||
|
/* This message is not acknowledged */
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void slider_res_auto_scan(const uint8_t *state)
|
||||||
|
{
|
||||||
|
struct slider_resp_auto_scan resp;
|
||||||
|
|
||||||
|
resp.hdr.sync = SLIDER_FRAME_SYNC;
|
||||||
|
resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN;
|
||||||
|
resp.hdr.nbytes = sizeof(resp.pressure);
|
||||||
|
memcpy(resp.pressure, state, sizeof(resp.pressure));
|
||||||
|
|
||||||
|
EnterCriticalSection(&slider_lock);
|
||||||
|
slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
|
||||||
|
LeaveCriticalSection(&slider_lock);
|
||||||
|
}
|
11
chusanhook/slider.h
Normal file
11
chusanhook/slider.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct slider_config {
|
||||||
|
bool enable;
|
||||||
|
};
|
||||||
|
|
||||||
|
HRESULT slider_hook_init(const struct slider_config *cfg);
|
106
cmhook/cm-dll.c
Normal file
106
cmhook/cm-dll.c
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "cmhook/cm-dll.h"
|
||||||
|
|
||||||
|
#include "util/dll-bind.h"
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
const struct dll_bind_sym cm_dll_syms[] = {
|
||||||
|
{
|
||||||
|
.sym = "cm_io_init",
|
||||||
|
.off = offsetof(struct cm_dll, init),
|
||||||
|
}, {
|
||||||
|
.sym = "cm_io_poll",
|
||||||
|
.off = offsetof(struct cm_dll, poll),
|
||||||
|
}, {
|
||||||
|
.sym = "cm_io_get_opbtns",
|
||||||
|
.off = offsetof(struct cm_dll, get_opbtns),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cm_dll cm_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 cm_dll_init(const struct cm_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("CardMaker IO: Failed to load IO DLL: %lx: %S\n",
|
||||||
|
hr,
|
||||||
|
cfg->path);
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("CardMaker IO: Using custom IO DLL: %S\n", cfg->path);
|
||||||
|
src = owned;
|
||||||
|
} else {
|
||||||
|
owned = NULL;
|
||||||
|
src = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_api_version = (void *) GetProcAddress(src, "cm_io_get_api_version");
|
||||||
|
|
||||||
|
if (get_api_version != NULL) {
|
||||||
|
cm_dll.api_version = get_api_version();
|
||||||
|
} else {
|
||||||
|
cm_dll.api_version = 0x0100;
|
||||||
|
dprintf("Custom IO DLL does not expose cm_io_get_api_version, "
|
||||||
|
"assuming API version 1.0.\n"
|
||||||
|
"Please ask the developer to update their DLL.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cm_dll.api_version >= 0x0200) {
|
||||||
|
hr = E_NOTIMPL;
|
||||||
|
dprintf("CardMaker IO: Custom IO DLL implements an unsupported "
|
||||||
|
"API version (%#04x). Please update Segatools.\n",
|
||||||
|
cm_dll.api_version);
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
sym = cm_dll_syms;
|
||||||
|
hr = dll_bind(&cm_dll, src, &sym, _countof(cm_dll_syms));
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
if (src != self) {
|
||||||
|
dprintf("CardMaker 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;
|
||||||
|
}
|
20
cmhook/cm-dll.h
Normal file
20
cmhook/cm-dll.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "cmio/cmio.h"
|
||||||
|
|
||||||
|
struct cm_dll {
|
||||||
|
uint16_t api_version;
|
||||||
|
HRESULT (*init)(void);
|
||||||
|
HRESULT (*poll)(void);
|
||||||
|
void (*get_opbtns)(uint8_t *opbtn);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cm_dll_config {
|
||||||
|
wchar_t path[MAX_PATH];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct cm_dll cm_dll;
|
||||||
|
|
||||||
|
HRESULT cm_dll_init(const struct cm_dll_config *cfg, HINSTANCE self);
|
17
cmhook/cmhook.def
Normal file
17
cmhook/cmhook.def
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
LIBRARY cmhook
|
||||||
|
|
||||||
|
EXPORTS
|
||||||
|
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
|
||||||
|
cm_io_get_api_version
|
||||||
|
cm_io_get_opbtns
|
||||||
|
cm_io_init
|
||||||
|
cm_io_poll
|
43
cmhook/config.c
Normal file
43
cmhook/config.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "board/config.h"
|
||||||
|
|
||||||
|
#include "hooklib/config.h"
|
||||||
|
#include "hooklib/dvd.h"
|
||||||
|
|
||||||
|
#include "cmhook/config.h"
|
||||||
|
|
||||||
|
#include "platform/config.h"
|
||||||
|
|
||||||
|
void cm_dll_config_load(
|
||||||
|
struct cm_dll_config *cfg,
|
||||||
|
const wchar_t *filename)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"cmio",
|
||||||
|
L"path",
|
||||||
|
L"",
|
||||||
|
cfg->path,
|
||||||
|
_countof(cfg->path),
|
||||||
|
filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_hook_config_load(
|
||||||
|
struct cm_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);
|
||||||
|
vfd_config_load(&cfg->vfd, filename);
|
||||||
|
touch_screen_config_load(&cfg->touch, filename);
|
||||||
|
cm_dll_config_load(&cfg->dll, filename);
|
||||||
|
}
|
30
cmhook/config.h
Normal file
30
cmhook/config.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "board/config.h"
|
||||||
|
|
||||||
|
#include "hooklib/dvd.h"
|
||||||
|
#include "hooklib/touch.h"
|
||||||
|
|
||||||
|
#include "cmhook/cm-dll.h"
|
||||||
|
|
||||||
|
#include "platform/config.h"
|
||||||
|
|
||||||
|
struct cm_hook_config {
|
||||||
|
struct platform_config platform;
|
||||||
|
struct aime_config aime;
|
||||||
|
struct dvd_config dvd;
|
||||||
|
struct io4_config io4;
|
||||||
|
struct vfd_config vfd;
|
||||||
|
struct cm_dll_config dll;
|
||||||
|
struct touch_screen_config touch;
|
||||||
|
};
|
||||||
|
|
||||||
|
void cm_dll_config_load(
|
||||||
|
struct cm_dll_config *cfg,
|
||||||
|
const wchar_t *filename);
|
||||||
|
|
||||||
|
void cm_hook_config_load(
|
||||||
|
struct cm_hook_config *cfg,
|
||||||
|
const wchar_t *filename);
|
119
cmhook/dllmain.c
Normal file
119
cmhook/dllmain.c
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "board/io4.h"
|
||||||
|
#include "board/sg-reader.h"
|
||||||
|
#include "board/vfd.h"
|
||||||
|
|
||||||
|
#include "hook/process.h"
|
||||||
|
|
||||||
|
#include "hooklib/dvd.h"
|
||||||
|
#include "hooklib/touch.h"
|
||||||
|
#include "hooklib/serial.h"
|
||||||
|
#include "hooklib/spike.h"
|
||||||
|
|
||||||
|
#include "cmhook/config.h"
|
||||||
|
#include "cmhook/io4.h"
|
||||||
|
#include "cmhook/cm-dll.h"
|
||||||
|
#include "cmhook/unity.h"
|
||||||
|
|
||||||
|
#include "platform/platform.h"
|
||||||
|
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
static HMODULE cm_hook_mod;
|
||||||
|
static process_entry_t cm_startup;
|
||||||
|
static struct cm_hook_config cm_hook_cfg;
|
||||||
|
|
||||||
|
static DWORD CALLBACK cm_pre_startup(void)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
dprintf("--- Begin cm_pre_startup ---\n");
|
||||||
|
|
||||||
|
/* Load config */
|
||||||
|
|
||||||
|
cm_hook_config_load(&cm_hook_cfg, L".\\segatools.ini");
|
||||||
|
|
||||||
|
/* Hook Win32 APIs */
|
||||||
|
|
||||||
|
dvd_hook_init(&cm_hook_cfg.dvd, cm_hook_mod);
|
||||||
|
touch_screen_hook_init(&cm_hook_cfg.touch, cm_hook_mod);
|
||||||
|
serial_hook_init();
|
||||||
|
|
||||||
|
/* Initialize emulation hooks */
|
||||||
|
|
||||||
|
hr = platform_hook_init(
|
||||||
|
&cm_hook_cfg.platform,
|
||||||
|
"SDED",
|
||||||
|
"ACA1",
|
||||||
|
cm_hook_mod);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = sg_reader_hook_init(&cm_hook_cfg.aime, 1, 1, cm_hook_mod);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = vfd_hook_init(&cm_hook_cfg.vfd, 2);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = cm_dll_init(&cm_hook_cfg.dll, cm_hook_mod);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = cm_io4_hook_init(&cm_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 `cmhook` initialization. */
|
||||||
|
|
||||||
|
unity_hook_init();
|
||||||
|
|
||||||
|
/* Initialize debug helpers */
|
||||||
|
|
||||||
|
spike_hook_init(L".\\segatools.ini");
|
||||||
|
|
||||||
|
dprintf("--- End cm_pre_startup ---\n");
|
||||||
|
|
||||||
|
/* Jump to EXE start address */
|
||||||
|
|
||||||
|
return cm_startup();
|
||||||
|
|
||||||
|
fail:
|
||||||
|
ExitProcess(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
if (cause != DLL_PROCESS_ATTACH) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cm_hook_mod = mod;
|
||||||
|
|
||||||
|
hr = process_hijack_startup(cm_pre_startup, &cm_startup);
|
||||||
|
|
||||||
|
if (!SUCCEEDED(hr)) {
|
||||||
|
dprintf("Failed to hijack process startup: %x\n", (int) hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCEEDED(hr);
|
||||||
|
}
|
69
cmhook/io4.c
Normal file
69
cmhook/io4.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "board/io4.h"
|
||||||
|
|
||||||
|
#include "cmhook/cm-dll.h"
|
||||||
|
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
static HRESULT cm_io4_poll(void *ctx, struct io4_state *state);
|
||||||
|
static uint16_t coins;
|
||||||
|
|
||||||
|
static const struct io4_ops cm_io4_ops = {
|
||||||
|
.poll = cm_io4_poll,
|
||||||
|
};
|
||||||
|
|
||||||
|
HRESULT cm_io4_hook_init(const struct io4_config *cfg)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
assert(cm_dll.init != NULL);
|
||||||
|
|
||||||
|
hr = io4_hook_init(cfg, &cm_io4_ops, NULL);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cm_dll.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT cm_io4_poll(void *ctx, struct io4_state *state)
|
||||||
|
{
|
||||||
|
uint8_t opbtn;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
assert(cm_dll.poll != NULL);
|
||||||
|
assert(cm_dll.get_opbtns != NULL);
|
||||||
|
|
||||||
|
memset(state, 0, sizeof(*state));
|
||||||
|
|
||||||
|
hr = cm_dll.poll();
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
opbtn = 0;
|
||||||
|
|
||||||
|
cm_dll.get_opbtns(&opbtn);
|
||||||
|
|
||||||
|
if (opbtn & CM_IO_OPBTN_TEST) {
|
||||||
|
state->buttons[0] |= IO4_BUTTON_TEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opbtn & CM_IO_OPBTN_SERVICE) {
|
||||||
|
state->buttons[0] |= IO4_BUTTON_SERVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opbtn & CM_IO_OPBTN_COIN) {
|
||||||
|
coins++;
|
||||||
|
}
|
||||||
|
state->chutes[0] = coins << 8;
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
7
cmhook/io4.h
Normal file
7
cmhook/io4.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "board/io4.h"
|
||||||
|
|
||||||
|
HRESULT cm_io4_hook_init(const struct io4_config *cfg);
|
32
cmhook/meson.build
Normal file
32
cmhook/meson.build
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
shared_library(
|
||||||
|
'cmhook',
|
||||||
|
name_prefix : '',
|
||||||
|
include_directories : inc,
|
||||||
|
implicit_include_directories : false,
|
||||||
|
vs_module_defs : 'cmhook.def',
|
||||||
|
c_pch : '../precompiled.h',
|
||||||
|
dependencies : [
|
||||||
|
capnhook.get_variable('hook_dep'),
|
||||||
|
capnhook.get_variable('hooklib_dep'),
|
||||||
|
xinput_lib,
|
||||||
|
],
|
||||||
|
link_with : [
|
||||||
|
aimeio_lib,
|
||||||
|
board_lib,
|
||||||
|
hooklib_lib,
|
||||||
|
cmio_lib,
|
||||||
|
platform_lib,
|
||||||
|
util_lib,
|
||||||
|
],
|
||||||
|
sources : [
|
||||||
|
'config.c',
|
||||||
|
'config.h',
|
||||||
|
'dllmain.c',
|
||||||
|
'io4.c',
|
||||||
|
'io4.h',
|
||||||
|
'cm-dll.c',
|
||||||
|
'cm-dll.h',
|
||||||
|
'unity.h',
|
||||||
|
'unity.c',
|
||||||
|
],
|
||||||
|
)
|
95
cmhook/unity.c
Normal file
95
cmhook/unity.c
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "hook/table.h"
|
||||||
|
|
||||||
|
#include "hooklib/dll.h"
|
||||||
|
#include "hooklib/path.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",
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
3
cmhook/unity.h
Normal file
3
cmhook/unity.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void unity_hook_init(void);
|
54
cmio/cmio.c
Normal file
54
cmio/cmio.c
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
#include <xinput.h>
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "cmio/cmio.h"
|
||||||
|
#include "cmio/config.h"
|
||||||
|
|
||||||
|
static uint8_t cm_opbtn;
|
||||||
|
static struct cm_io_config cm_io_cfg;
|
||||||
|
static bool cm_io_coin;
|
||||||
|
|
||||||
|
uint16_t cm_io_get_api_version(void)
|
||||||
|
{
|
||||||
|
return 0x0100;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT cm_io_init(void)
|
||||||
|
{
|
||||||
|
cm_io_config_load(&cm_io_cfg, L".\\segatools.ini");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT cm_io_poll(void)
|
||||||
|
{
|
||||||
|
cm_opbtn = 0;
|
||||||
|
|
||||||
|
if (GetAsyncKeyState(cm_io_cfg.vk_test) & 0x8000) {
|
||||||
|
cm_opbtn |= CM_IO_OPBTN_TEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetAsyncKeyState(cm_io_cfg.vk_service) & 0x8000) {
|
||||||
|
cm_opbtn |= CM_IO_OPBTN_SERVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetAsyncKeyState(cm_io_cfg.vk_coin) & 0x8000) {
|
||||||
|
if (!cm_io_coin) {
|
||||||
|
cm_io_coin = true;
|
||||||
|
cm_opbtn |= CM_IO_OPBTN_COIN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cm_io_coin = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_io_get_opbtns(uint8_t *opbtn)
|
||||||
|
{
|
||||||
|
if (opbtn != NULL) {
|
||||||
|
*opbtn = cm_opbtn;
|
||||||
|
}
|
||||||
|
}
|
44
cmio/cmio.h
Normal file
44
cmio/cmio.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CM_IO_OPBTN_TEST = 0x01,
|
||||||
|
CM_IO_OPBTN_SERVICE = 0x02,
|
||||||
|
CM_IO_OPBTN_COIN = 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Get the version of the CardMaker 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 cm_io_get_api_version(void);
|
||||||
|
|
||||||
|
/* Initialize the IO DLL. This is the second function that will be called on
|
||||||
|
your DLL, after cm_io_get_api_version.
|
||||||
|
|
||||||
|
All subsequent calls to this API may originate from arbitrary threads.
|
||||||
|
|
||||||
|
Minimum API version: 0x0100 */
|
||||||
|
|
||||||
|
HRESULT cm_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 cm_io_poll(void);
|
||||||
|
|
||||||
|
/* Get the state of the cabinet's operator buttons as of the last poll. See
|
||||||
|
cm_IO_OPBTN enum above: this contains bit mask definitions for button
|
||||||
|
states returned in *opbtn. All buttons are active-high.
|
||||||
|
|
||||||
|
Minimum API version: 0x0100 */
|
||||||
|
|
||||||
|
void cm_io_get_opbtns(uint8_t *opbtn);
|
22
cmio/config.c
Normal file
22
cmio/config.c
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "cmio/config.h"
|
||||||
|
|
||||||
|
void cm_io_config_load(
|
||||||
|
struct cm_io_config *cfg,
|
||||||
|
const wchar_t *filename)
|
||||||
|
{
|
||||||
|
wchar_t key[16];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", VK_F1, filename);
|
||||||
|
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", VK_F2, filename);
|
||||||
|
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", VK_F3, filename);
|
||||||
|
}
|
16
cmio/config.h
Normal file
16
cmio/config.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct cm_io_config {
|
||||||
|
uint8_t vk_test;
|
||||||
|
uint8_t vk_service;
|
||||||
|
uint8_t vk_coin;
|
||||||
|
};
|
||||||
|
|
||||||
|
void cm_io_config_load(
|
||||||
|
struct cm_io_config *cfg,
|
||||||
|
const wchar_t *filename);
|
13
cmio/meson.build
Normal file
13
cmio/meson.build
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
cmio_lib = static_library(
|
||||||
|
'cmio',
|
||||||
|
name_prefix : '',
|
||||||
|
include_directories : inc,
|
||||||
|
implicit_include_directories : false,
|
||||||
|
c_pch : '../precompiled.h',
|
||||||
|
sources : [
|
||||||
|
'cmio.c',
|
||||||
|
'cmio.h',
|
||||||
|
'config.c',
|
||||||
|
'config.h',
|
||||||
|
],
|
||||||
|
)
|
@ -91,7 +91,7 @@ static DWORD CALLBACK cxb_pre_startup(void)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = sg_reader_hook_init(&cxb_hook_cfg.aime, 12, cxb_hook_mod);
|
hr = sg_reader_hook_init(&cxb_hook_cfg.aime, 12, 1, cxb_hook_mod);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
69
dist/carol/segatools.ini
vendored
69
dist/carol/segatools.ini
vendored
@ -1,23 +1,56 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Path settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[vfs]
|
[vfs]
|
||||||
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
|
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
|
||||||
amfs=
|
amfs=
|
||||||
; Insert the path to the game Option directory here (contains Axxx directories)
|
; Insert the path to the game Option directory here (contains MOV1, PAR0,
|
||||||
|
; PAR1, RES0 and RES1 directories)
|
||||||
option=
|
option=
|
||||||
; Create an empty directory somewhere and insert the path here.
|
; Create an empty directory somewhere and insert the path here.
|
||||||
; This directory may be shared between multiple SEGA games.
|
; This directory may be shared between multiple SEGA games.
|
||||||
; NOTE: This has nothing to do with Windows %APPDATA%.
|
; NOTE: This has nothing to do with Windows %APPDATA%.
|
||||||
appdata=
|
appdata=
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Device settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[aime]
|
||||||
|
; Enable Aime card reader assembly emulation. Disable to use a real SEGA Aime
|
||||||
|
; reader.
|
||||||
|
enable=1
|
||||||
|
aimePath=DEVICE\aime.txt
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Network settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[dns]
|
[dns]
|
||||||
; Insert the hostname or IP address of the server you wish to use here.
|
; 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.
|
; Note that 127.0.0.1, localhost etc are specifically rejected.
|
||||||
default=127.0.0.1
|
default=127.0.0.1
|
||||||
|
|
||||||
[netenv]
|
[netenv]
|
||||||
; Simulate an ideal LAN environment.
|
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
|
||||||
|
; Chunithm is extremely picky about its LAN environment, so leaving this
|
||||||
|
; setting enabled is strongly recommended.
|
||||||
enable=1
|
enable=1
|
||||||
|
; The final octet of the local host's IP address on the virtualized subnet (so,
|
||||||
|
; if the keychip subnet is `192.168.32.0` and this value is set to `11`, then the
|
||||||
|
; local host's virtualized LAN IP is `192.168.32.11`).
|
||||||
|
addrSuffix=11
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Board settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[gpio]
|
[gpio]
|
||||||
|
; Emulated Nu DIP switch for Distribution Server setting.
|
||||||
|
;
|
||||||
|
; If multiple machines are present on the same LAN then set this to 1 on
|
||||||
|
; exactly one machine and set this to 0 on all others.
|
||||||
dipsw1=1
|
dipsw1=1
|
||||||
|
|
||||||
[keychip]
|
[keychip]
|
||||||
@ -26,6 +59,10 @@ dipsw1=1
|
|||||||
; that subnet must start with 192.168.
|
; that subnet must start with 192.168.
|
||||||
subnet=192.168.126.0
|
subnet=192.168.126.0
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Misc. hooks settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[gfx]
|
[gfx]
|
||||||
; Force the game to run windowed.
|
; Force the game to run windowed.
|
||||||
windowed=1
|
windowed=1
|
||||||
@ -34,15 +71,31 @@ framed=1
|
|||||||
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||||
monitor=0
|
monitor=0
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Custom IO settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[aimeio]
|
[aimeio]
|
||||||
; To use a custom card reader IO DLL enter its path here.
|
; To use a custom card reader IO DLL enter its path here.
|
||||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
path=
|
path=
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Input settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal
|
||||||
|
; (not prefixed with 0x) virtual-key codes, a list of which can be found here:
|
||||||
|
;
|
||||||
|
; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||||
|
;
|
||||||
|
; This is, admittedly, not the most user-friendly configuration method in the
|
||||||
|
; world. An improved solution will be provided later.
|
||||||
|
|
||||||
[io3]
|
[io3]
|
||||||
; Test button virtual-key code. Default is the 1 key.
|
; Test button virtual-key code. Default is the F1 key.
|
||||||
test=0x31
|
test=0x70
|
||||||
; Service button virtual-key code. Default is the 2 key.
|
; Service button virtual-key code. Default is the F2 key.
|
||||||
service=0x32
|
service=0x71
|
||||||
; Keyboard button to increment coin counter. Default is the 3 key.
|
; Keyboard button to increment coin counter. Default is the F3 key.
|
||||||
coin=0x33
|
coin=0x72
|
||||||
|
113
dist/chuni/segatools.ini
vendored
113
dist/chuni/segatools.ini
vendored
@ -1,3 +1,7 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Path settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[vfs]
|
[vfs]
|
||||||
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
|
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
|
||||||
amfs=
|
amfs=
|
||||||
@ -8,6 +12,20 @@ option=
|
|||||||
; NOTE: This has nothing to do with Windows %APPDATA%.
|
; NOTE: This has nothing to do with Windows %APPDATA%.
|
||||||
appdata=
|
appdata=
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Device settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[aime]
|
||||||
|
; Enable Aime card reader assembly emulation. Disable to use a real SEGA Aime
|
||||||
|
; reader.
|
||||||
|
enable=1
|
||||||
|
aimePath=DEVICE\aime.txt
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Network settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[dns]
|
[dns]
|
||||||
; Insert the hostname or IP address of the server you wish to use here.
|
; 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.
|
; Note that 127.0.0.1, localhost etc are specifically rejected.
|
||||||
@ -18,6 +36,14 @@ default=127.0.0.1
|
|||||||
; Chunithm is extremely picky about its LAN environment, so leaving this
|
; Chunithm is extremely picky about its LAN environment, so leaving this
|
||||||
; setting enabled is strongly recommended.
|
; setting enabled is strongly recommended.
|
||||||
enable=1
|
enable=1
|
||||||
|
; The final octet of the local host's IP address on the virtualized subnet (so,
|
||||||
|
; if the keychip subnet is `192.168.32.0` and this value is set to `11`, then the
|
||||||
|
; local host's virtualized LAN IP is `192.168.32.11`).
|
||||||
|
addrSuffix=11
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Board settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[keychip]
|
[keychip]
|
||||||
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
|
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
|
||||||
@ -25,6 +51,10 @@ enable=1
|
|||||||
; that subnet must start with 192.168.
|
; that subnet must start with 192.168.
|
||||||
subnet=192.168.139.0
|
subnet=192.168.139.0
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Misc. hooks settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[gfx]
|
[gfx]
|
||||||
; Force the game to run windowed.
|
; Force the game to run windowed.
|
||||||
windowed=1
|
windowed=1
|
||||||
@ -33,6 +63,10 @@ framed=1
|
|||||||
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||||
monitor=0
|
monitor=0
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Custom IO settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[aimeio]
|
[aimeio]
|
||||||
; To use a custom card reader IO DLL enter its path here.
|
; To use a custom card reader IO DLL enter its path here.
|
||||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
@ -43,6 +77,51 @@ path=
|
|||||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
path=
|
path=
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; LED settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[led15093]
|
||||||
|
; Enable emulation of the 15093-06 controlled lights, which handle the air tower
|
||||||
|
; RGBs and the rear LED panel (billboard) on the cabinet.
|
||||||
|
enable=1
|
||||||
|
|
||||||
|
[led]
|
||||||
|
; Output billboard LED strip data to a named pipe called "\\.\pipe\chuni_ledstrip"
|
||||||
|
cabLedOutputPipe=1
|
||||||
|
; Output billboard LED strip data to serial
|
||||||
|
cabLedOutputSerial=0
|
||||||
|
|
||||||
|
; Output slider LED data to the named pipe
|
||||||
|
controllerLedOutputPipe=1
|
||||||
|
; Output slider LED data to the serial port
|
||||||
|
controllerLedOutputSerial=0
|
||||||
|
|
||||||
|
; Serial port to send data to if using serial output. Default is COM5.
|
||||||
|
;serialPort=COM5
|
||||||
|
; Baud rate for serial data
|
||||||
|
;serialBaud=921600
|
||||||
|
|
||||||
|
; Data output a sequence of bytes, with JVS-like framing.
|
||||||
|
; Each "packet" starts with 0xE0 as a sync. To avoid E0 appearing elsewhere,
|
||||||
|
; 0xD0 is used as an escape character -- if you receive D0 in the output, ignore
|
||||||
|
; it and use the next sent byte plus one instead.
|
||||||
|
;
|
||||||
|
; After the sync is one byte for the board number that was updated, followed by
|
||||||
|
; the red, green and blue values for each LED.
|
||||||
|
;
|
||||||
|
; Board 0 has 53 LEDs:
|
||||||
|
; [0]-[49]: snakes through left half of billboard (first column starts at top)
|
||||||
|
; [50]-[52]: left side partition LEDs
|
||||||
|
;
|
||||||
|
; Board 1 has 63 LEDs:
|
||||||
|
; [0]-[59]: right half of billboard (first column starts at bottom)
|
||||||
|
; [60]-[62]: right side partition LEDs
|
||||||
|
;
|
||||||
|
; Board 2 is the slider and has 31 LEDs:
|
||||||
|
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
|
||||||
|
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; Input settings
|
; Input settings
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
@ -56,12 +135,26 @@ path=
|
|||||||
; world. An improved solution will be provided later.
|
; world. An improved solution will be provided later.
|
||||||
|
|
||||||
[io3]
|
[io3]
|
||||||
; Test button virtual-key code. Default is the 1 key.
|
; Test button virtual-key code. Default is the F1 key.
|
||||||
test=0x31
|
test=0x70
|
||||||
; Service button virtual-key code. Default is the 2 key.
|
; Service button virtual-key code. Default is the F2 key.
|
||||||
service=0x32
|
service=0x71
|
||||||
; Keyboard button to increment coin counter. Default is the 3 key.
|
; Keyboard button to increment coin counter. Default is the F3 key.
|
||||||
coin=0x33
|
coin=0x72
|
||||||
|
; Set to 0 for enable separate ir control. Deafult is space key.
|
||||||
|
ir=0x20
|
||||||
|
|
||||||
|
[ir]
|
||||||
|
; Uncomment and complete the following sequence of settings to configure a
|
||||||
|
; custom ir-cappable controller if you have one.
|
||||||
|
;ir6=0x53
|
||||||
|
; ... etc ...
|
||||||
|
;ir1=0x53
|
||||||
|
|
||||||
|
[slider]
|
||||||
|
; Enable slider emulation. If you have real AC slider, set this to 0.
|
||||||
|
; Slider serial port must be COM1.
|
||||||
|
;enable=1
|
||||||
|
|
||||||
; Key bindings for each of the 32 touch cells. The default key map, depicted
|
; Key bindings for each of the 32 touch cells. The default key map, depicted
|
||||||
; in left-to-right order, is as follows:
|
; in left-to-right order, is as follows:
|
||||||
@ -73,8 +166,8 @@ coin=0x33
|
|||||||
;
|
;
|
||||||
; Uncomment and complete the following sequence of settings to configure a
|
; Uncomment and complete the following sequence of settings to configure a
|
||||||
; custom high-precision touch strip controller if you have one.
|
; custom high-precision touch strip controller if you have one.
|
||||||
[slider]
|
;cell1=0x53
|
||||||
;cell32=0x53
|
;cell2=0x53
|
||||||
;cell31=0x53
|
|
||||||
;cell30=0x53
|
|
||||||
; ... etc ...
|
; ... etc ...
|
||||||
|
;cell31=0x53
|
||||||
|
;cell32=0x53
|
||||||
|
6
dist/chusan/config_hook.json
vendored
Normal file
6
dist/chusan/config_hook.json
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"allnet_auth":
|
||||||
|
{
|
||||||
|
"type": "1.0"
|
||||||
|
}
|
||||||
|
}
|
207
dist/chusan/segatools.ini
vendored
Normal file
207
dist/chusan/segatools.ini
vendored
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Path settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[vfs]
|
||||||
|
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
|
||||||
|
amfs=
|
||||||
|
; Insert the path to the game Option directory here (contains Axxx directories)
|
||||||
|
option=
|
||||||
|
; 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=
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Device settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[aime]
|
||||||
|
; Enable Aime card reader assembly emulation. Disable to use a real SEGA Aime
|
||||||
|
; reader.
|
||||||
|
enable=1
|
||||||
|
aimePath=DEVICE\aime.txt
|
||||||
|
; Enable high baud rate.
|
||||||
|
;highBaud=1
|
||||||
|
|
||||||
|
[vfd]
|
||||||
|
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||||
|
; GP1232A02A FUTABA assembly.
|
||||||
|
enable=1
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Network settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
|
[netenv]
|
||||||
|
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
|
||||||
|
; Chunithm is extremely picky about its LAN environment, so leaving this
|
||||||
|
; setting enabled is strongly recommended.
|
||||||
|
enable=1
|
||||||
|
; The final octet of the local host's IP address on the virtualized subnet (so,
|
||||||
|
; if the keychip subnet is `192.168.32.0` and this value is set to `11`, then the
|
||||||
|
; local host's virtualized LAN IP is `192.168.32.11`).
|
||||||
|
addrSuffix=11
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Board settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[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.139.0
|
||||||
|
|
||||||
|
[gpio]
|
||||||
|
; ALLS DIP switches.
|
||||||
|
enable=1
|
||||||
|
|
||||||
|
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||||
|
; freeplay. Keep in mind that some game modes (e.g. Freedom/Time Modes) will not
|
||||||
|
; allow you to start a game in freeplay mode.
|
||||||
|
freeplay=0
|
||||||
|
|
||||||
|
; LAN Install: If multiple machines are present on the same LAN then set
|
||||||
|
; this to 1 on exactly one machine and set this to 0 on all others.
|
||||||
|
dipsw1=1
|
||||||
|
; Monitor type: 0 = 120FPS, 1 = 60FPS
|
||||||
|
dipsw2=1
|
||||||
|
; Cab type: 0 = SP, 1 = CVT. SP will enable VFD and eMoney. This setting will switch
|
||||||
|
; the LED 837-15093-06 COM port and the AiMe reder hardware generation as well.
|
||||||
|
dipsw3=1
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Misc. hooks settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[gfx]
|
||||||
|
; Force the game to run windowed.
|
||||||
|
windowed=1
|
||||||
|
; Add a frame to the game window if running windowed.
|
||||||
|
framed=0
|
||||||
|
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||||
|
monitor=0
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Custom IO settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[aimeio]
|
||||||
|
; To use a custom card reader IO DLL (x64) enter its path here.
|
||||||
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
|
path=
|
||||||
|
|
||||||
|
[chuniio]
|
||||||
|
; Uncomment this if you have custom chuniio implementation comprised of a single 32bit DLL.
|
||||||
|
; (will use chu2to3 engine internally)
|
||||||
|
;path=
|
||||||
|
|
||||||
|
; Uncomment both of these if you have custom chuniio implementation comprised of two DLLs.
|
||||||
|
; x86 chuniio to path32, x64 to path64. Both are necessary.
|
||||||
|
;path32=
|
||||||
|
;path64=
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; LED settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[led15093]
|
||||||
|
; Enable emulation of the 15093-06 controlled lights, which handle the air tower
|
||||||
|
; RGBs and the rear LED panel (billboard) on the cabinet.
|
||||||
|
enable=1
|
||||||
|
|
||||||
|
[led]
|
||||||
|
; Output billboard LED strip data to a named pipe called "\\.\pipe\chuni_led"
|
||||||
|
cabLedOutputPipe=1
|
||||||
|
; Output billboard LED strip data to serial
|
||||||
|
cabLedOutputSerial=0
|
||||||
|
|
||||||
|
; Output slider LED data to the named pipe
|
||||||
|
controllerLedOutputPipe=1
|
||||||
|
; Output slider LED data to the serial port
|
||||||
|
controllerLedOutputSerial=0
|
||||||
|
|
||||||
|
; Serial port to send data to if using serial output. Default is COM5.
|
||||||
|
;serialPort=COM5
|
||||||
|
; Baud rate for serial data
|
||||||
|
;serialBaud=921600
|
||||||
|
|
||||||
|
; Data output a sequence of bytes, with JVS-like framing.
|
||||||
|
; Each "packet" starts with 0xE0 as a sync. To avoid E0 appearing elsewhere,
|
||||||
|
; 0xD0 is used as an escape character -- if you receive D0 in the output, ignore
|
||||||
|
; it and use the next sent byte plus one instead.
|
||||||
|
;
|
||||||
|
; After the sync is one byte for the board number that was updated, followed by
|
||||||
|
; the red, green and blue values for each LED.
|
||||||
|
;
|
||||||
|
; Board 0 has 53 LEDs:
|
||||||
|
; [0]-[49]: snakes through left half of billboard (first column starts at top)
|
||||||
|
; [50]-[52]: left side partition LEDs
|
||||||
|
;
|
||||||
|
; Board 1 has 63 LEDs:
|
||||||
|
; [0]-[59]: right half of billboard (first column starts at bottom)
|
||||||
|
; [60]-[62]: right side partition LEDs
|
||||||
|
;
|
||||||
|
; Board 2 is the slider and has 31 LEDs:
|
||||||
|
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
|
||||||
|
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Input settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal
|
||||||
|
; (not prefixed with 0x) virtual-key codes, a list of which can be found here:
|
||||||
|
;
|
||||||
|
; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||||
|
;
|
||||||
|
; This is, admittedly, not the most user-friendly configuration method in the
|
||||||
|
; world. An improved solution will be provided later.
|
||||||
|
|
||||||
|
[io3]
|
||||||
|
; Test button virtual-key code. Default is the F1 key.
|
||||||
|
test=0x70
|
||||||
|
; Service button virtual-key code. Default is the F2 key.
|
||||||
|
service=0x71
|
||||||
|
; Keyboard button to increment coin counter. Default is the F3 key.
|
||||||
|
coin=0x72
|
||||||
|
; Set to 0 for enable separate ir control. Deafult is space key.
|
||||||
|
ir=0x20
|
||||||
|
|
||||||
|
[ir]
|
||||||
|
; Uncomment and complete the following sequence of settings to configure a
|
||||||
|
; custom ir-cappable controller if you have one.
|
||||||
|
;ir6=0x53
|
||||||
|
; ... etc ...
|
||||||
|
;ir1=0x53
|
||||||
|
|
||||||
|
[slider]
|
||||||
|
; Enable slider emulation. If you have real AC slider, set this to 0.
|
||||||
|
; Slider serial port must be COM1.
|
||||||
|
;enable=1
|
||||||
|
|
||||||
|
; Key bindings for each of the 32 touch cells. The default key map, depicted
|
||||||
|
; in left-to-right order, is as follows:
|
||||||
|
;
|
||||||
|
; SSSSDDDDFFFFGGGGHHHHJJJJKKKKLLLL
|
||||||
|
;
|
||||||
|
; Touch cells are numbered FROM RIGHT TO LEFT! starting from 1. This is in
|
||||||
|
; order to match the numbering used in the operator menu and service manual.
|
||||||
|
;
|
||||||
|
; Uncomment and complete the following sequence of settings to configure a
|
||||||
|
; custom high-precision touch strip controller if you have one.
|
||||||
|
;cell1=0x53
|
||||||
|
;cell2=0x53
|
||||||
|
; ... etc ...
|
||||||
|
;cell31=0x53
|
||||||
|
;cell32=0x53
|
||||||
|
|
||||||
|
; Enable slider LED serial output. This follows OpeNITHM Serial LED Protocol.
|
||||||
|
; eg. COM5
|
||||||
|
;ledport=
|
12
dist/chusan/start.bat
vendored
Normal file
12
dist/chusan/start.bat
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
start "AM Daemon" /min inject_x64 -d -k chusanhook_x64.dll amdaemon.exe -c config_common.json config_server.json config_client.json config_cvt.json config_sp.json config_hook.json
|
||||||
|
inject_x86 -d -k chusanhook_x86.dll chusanApp.exe
|
||||||
|
|
||||||
|
taskkill /f /im amdaemon.exe > nul 2>&1
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Game processes have terminated
|
||||||
|
pause
|
9
dist/cm/config_hook.json
vendored
Normal file
9
dist/cm/config_hook.json
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"credit" :
|
||||||
|
{
|
||||||
|
"coin_selector_AS6DB" :
|
||||||
|
{
|
||||||
|
"enable" : false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
dist/cm/segatools.ini
vendored
Normal file
98
dist/cm/segatools.ini
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Path settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[vfs]
|
||||||
|
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
|
||||||
|
amfs=
|
||||||
|
; Insert the path to the game Option directory here (contains Axxx directories)
|
||||||
|
option=
|
||||||
|
; 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=
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Device settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[aime]
|
||||||
|
; Enable Aime card reader assembly emulation. Disable to use a real SEGA Aime
|
||||||
|
; reader.
|
||||||
|
enable=1
|
||||||
|
aimePath=DEVICE\aime.txt
|
||||||
|
|
||||||
|
[vfd]
|
||||||
|
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||||
|
; GP1232A02A FUTABA assembly.
|
||||||
|
enable=1
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Network settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Board settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[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.100.0
|
||||||
|
|
||||||
|
[gpio]
|
||||||
|
; ALLS DIP switches.
|
||||||
|
enable=1
|
||||||
|
|
||||||
|
; LAN Install: If multiple machines are present on the same LAN then set
|
||||||
|
; this to 0 on exactly one machine and set this to 1 on all others.
|
||||||
|
dipsw1=0
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Misc. hooks settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[touch]
|
||||||
|
; Enable/Disable WinTouch emulation
|
||||||
|
enable=0
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Custom IO settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[aimeio]
|
||||||
|
; To use a custom card reader IO DLL enter its path here.
|
||||||
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
|
path=
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Input settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
; Keyboard bindings are as hexadecimal (prefixed with 0x) or decimal
|
||||||
|
; (not prefixed with 0x) virtual-key codes, a list of which can be found here:
|
||||||
|
;
|
||||||
|
; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||||
|
;
|
||||||
|
; This is, admittedly, not the most user-friendly configuration method in the
|
||||||
|
; world. An improved solution will be provided later.
|
||||||
|
|
||||||
|
[io4]
|
||||||
|
; Test button virtual-key code. Default is the F1 key.
|
||||||
|
test=0x70
|
||||||
|
; Service button virtual-key code. Default is the F2 key.
|
||||||
|
service=0x71
|
||||||
|
; Keyboard button to increment coin counter. Default is the F3 key.
|
||||||
|
coin=0x72
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user