Merge branch 'develop'

This commit is contained in:
2025-06-19 18:32:40 +02:00
578 changed files with 35509 additions and 2721 deletions

4
.clang-format Normal file
View File

@ -0,0 +1,4 @@
---
BasedOnStyle: Google
IndentWidth: 4
---

19
.gitignore vendored
View File

@ -1,9 +1,26 @@
.*.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/
# External dependencies # External dependencies
subprojects/capnhook subprojects/capnhook
# For enabling debug logging on local builds
MesonLocalOptions.mk
# Some meson cache thing
.meson-subproject-wrap-hash.txt

View File

@ -1,3 +1,4 @@
{ {
"editor.formatOnSave": false, "editor.formatOnSave": false,
"mesonbuild.configureOnOpen": false,
} }

View File

@ -5,15 +5,18 @@ 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_GAMES_32 := $(BUILD_DIR_32)/games
BUILD_DIR_GAMES_64 := $(BUILD_DIR_64)/games
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" # Add "-D[option]=[value]" here as necessary
DOCKER_IMAGE_NAME := "segatools:build" MESON_OPTIONS :=
# For options that shouldn't be committed
-include MesonLocalOptions.mk
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Targets # Targets
@ -23,9 +26,9 @@ include Package.mk
.PHONY: build # Build the project .PHONY: build # Build the project
build: build:
$(V)meson --cross cross-mingw-32.txt $(BUILD_DIR_32) $(V)meson setup $(MESON_OPTIONS) --cross cross-mingw-32.txt $(BUILD_DIR_32)
$(V)ninja -C $(BUILD_DIR_32) $(V)ninja -C $(BUILD_DIR_32)
$(V)meson --cross cross-mingw-64.txt $(BUILD_DIR_64) $(V)meson setup $(MESON_OPTIONS) --cross cross-mingw-64.txt $(BUILD_DIR_64)
$(V)ninja -C $(BUILD_DIR_64) $(V)ninja -C $(BUILD_DIR_64)
.PHONY: dist # Build and create a zip distribution package .PHONY: dist # Build and create a zip distribution package
@ -42,15 +45,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
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------

View File

@ -3,9 +3,9 @@ $(BUILD_DIR_ZIP)/chuni.zip:
$(V)mkdir -p $(BUILD_DIR_ZIP)/chuni $(V)mkdir -p $(BUILD_DIR_ZIP)/chuni
$(V)mkdir -p $(BUILD_DIR_ZIP)/chuni/DEVICE $(V)mkdir -p $(BUILD_DIR_ZIP)/chuni/DEVICE
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_32)/chunihook/chunihook.dll \ $(BUILD_DIR_GAMES_32)/chunihook/chunihook.dll \
$(DIST_DIR)/chuni/segatools.ini \ $(DIST_DIR)/chuni/segatools.ini \
$(DIST_DIR)/chuni/start.bat \ $(DIST_DIR)/chuni/launch.bat \
$(BUILD_DIR_ZIP)/chuni $(BUILD_DIR_ZIP)/chuni
$(V)cp pki/billing.pub \ $(V)cp pki/billing.pub \
pki/ca.crt \ pki/ca.crt \
@ -18,9 +18,9 @@ $(BUILD_DIR_ZIP)/cxb.zip:
$(V)mkdir -p $(BUILD_DIR_ZIP)/cxb $(V)mkdir -p $(BUILD_DIR_ZIP)/cxb
$(V)mkdir -p $(BUILD_DIR_ZIP)/cxb/DEVICE $(V)mkdir -p $(BUILD_DIR_ZIP)/cxb/DEVICE
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_32)/cxbhook/cxbhook.dll \ $(BUILD_DIR_GAMES_32)/cxbhook/cxbhook.dll \
$(DIST_DIR)/cxb/segatools.ini \ $(DIST_DIR)/cxb/segatools.ini \
$(DIST_DIR)/cxb/start.bat \ $(DIST_DIR)/cxb/launch.bat \
$(BUILD_DIR_ZIP)/cxb $(BUILD_DIR_ZIP)/cxb
$(V)cp pki/billing.pub \ $(V)cp pki/billing.pub \
pki/ca.crt \ pki/ca.crt \
@ -33,9 +33,9 @@ $(BUILD_DIR_ZIP)/diva.zip:
$(V)mkdir -p $(BUILD_DIR_ZIP)/diva $(V)mkdir -p $(BUILD_DIR_ZIP)/diva
$(V)mkdir -p $(BUILD_DIR_ZIP)/diva/DEVICE $(V)mkdir -p $(BUILD_DIR_ZIP)/diva/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/divahook/divahook.dll \ $(BUILD_DIR_GAMES_64)/divahook/divahook.dll \
$(DIST_DIR)/diva/segatools.ini \ $(DIST_DIR)/diva/segatools.ini \
$(DIST_DIR)/diva/start.bat \ $(DIST_DIR)/diva/launch.bat \
$(BUILD_DIR_ZIP)/diva $(BUILD_DIR_ZIP)/diva
$(V)cp pki/billing.pub \ $(V)cp pki/billing.pub \
pki/ca.crt \ pki/ca.crt \
@ -48,9 +48,9 @@ $(BUILD_DIR_ZIP)/carol.zip:
$(V)mkdir -p $(BUILD_DIR_ZIP)/carol $(V)mkdir -p $(BUILD_DIR_ZIP)/carol
$(V)mkdir -p $(BUILD_DIR_ZIP)/carol/DEVICE $(V)mkdir -p $(BUILD_DIR_ZIP)/carol/DEVICE
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_32)/carolhook/carolhook.dll \ $(BUILD_DIR_GAMES_32)/carolhook/carolhook.dll \
$(DIST_DIR)/carol/segatools.ini \ $(DIST_DIR)/carol/segatools.ini \
$(DIST_DIR)/carol/start.bat \ $(DIST_DIR)/carol/launch.bat \
$(BUILD_DIR_ZIP)/carol $(BUILD_DIR_ZIP)/carol
$(V)cp pki/billing.pub \ $(V)cp pki/billing.pub \
pki/ca.crt \ pki/ca.crt \
@ -63,9 +63,9 @@ $(BUILD_DIR_ZIP)/idz.zip:
$(V)mkdir -p $(BUILD_DIR_ZIP)/idz $(V)mkdir -p $(BUILD_DIR_ZIP)/idz
$(V)mkdir -p $(BUILD_DIR_ZIP)/idz/DEVICE $(V)mkdir -p $(BUILD_DIR_ZIP)/idz/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/idzhook/idzhook.dll \ $(BUILD_DIR_GAMES_64)/idzhook/idzhook.dll \
$(DIST_DIR)/idz/segatools.ini \ $(DIST_DIR)/idz/segatools.ini \
$(DIST_DIR)/idz/start.bat \ $(DIST_DIR)/idz/launch.bat \
$(BUILD_DIR_ZIP)/idz $(BUILD_DIR_ZIP)/idz
$(V)cp pki/billing.pub \ $(V)cp pki/billing.pub \
pki/ca.crt \ pki/ca.crt \
@ -73,14 +73,61 @@ $(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_GAMES_64)/fgohook/fgohook.dll \
$(DIST_DIR)/fgo/segatools.ini \
$(DIST_DIR)/fgo/launch.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_GAMES_64)/idachook/idachook.dll \
$(DIST_DIR)/idac/segatools.ini \
$(DIST_DIR)/idac/config_hook.json \
$(DIST_DIR)/idac/launch.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_GAMES_64)/swdchook/swdchook.dll \
$(DIST_DIR)/swdc/segatools.ini \
$(DIST_DIR)/swdc/config_hook.json \
$(DIST_DIR)/swdc/launch.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
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury/DEVICE $(V)mkdir -p $(BUILD_DIR_ZIP)/mercury/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/mercuryhook/mercuryhook.dll \ $(BUILD_DIR_GAMES_64)/mercuryhook/mercuryhook.dll \
$(DIST_DIR)/mercury/segatools.ini \ $(DIST_DIR)/mercury/segatools.ini \
$(DIST_DIR)/mercury/start.bat \ $(DIST_DIR)/mercury/launch.bat \
$(BUILD_DIR_ZIP)/mercury $(BUILD_DIR_ZIP)/mercury
$(V)cp pki/billing.pub \ $(V)cp pki/billing.pub \
pki/ca.crt \ pki/ca.crt \
@ -88,15 +135,36 @@ $(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/launch.bat \
$(BUILD_DIR_ZIP)/chusan
$(V)cp $(BUILD_DIR_GAMES_32)/chusanhook/chusanhook.dll \
$(BUILD_DIR_ZIP)/chusan/chusanhook_x86.dll
$(V)cp $(BUILD_DIR_GAMES_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 ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/mu3 $(V)mkdir -p $(BUILD_DIR_ZIP)/mu3
$(V)mkdir -p $(BUILD_DIR_ZIP)/mu3/DEVICE $(V)mkdir -p $(BUILD_DIR_ZIP)/mu3/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/mu3hook/mu3hook.dll \ $(BUILD_DIR_GAMES_64)/mu3hook/mu3hook.dll \
$(DIST_DIR)/mu3/segatools.ini \ $(DIST_DIR)/mu3/segatools.ini \
$(DIST_DIR)/mu3/start.bat \ $(DIST_DIR)/mu3/launch.bat \
$(BUILD_DIR_ZIP)/mu3 $(BUILD_DIR_ZIP)/mu3
$(V)cp pki/billing.pub \ $(V)cp pki/billing.pub \
pki/ca.crt \ pki/ca.crt \
@ -104,6 +172,74 @@ $(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_GAMES_64)/mai2hook/mai2hook.dll \
$(DIST_DIR)/mai2/segatools.ini \
$(DIST_DIR)/mai2/launch.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_GAMES_64)/cmhook/cmhook.dll \
$(DIST_DIR)/cm/config_hook.json \
$(DIST_DIR)/cm/segatools.ini \
$(DIST_DIR)/cm/launch.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)/tokyo.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/tokyo
$(V)mkdir -p $(BUILD_DIR_ZIP)/tokyo/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_GAMES_64)/tokyohook/tokyohook.dll \
$(DIST_DIR)/tokyo/config_hook.json \
$(DIST_DIR)/tokyo/segatools.ini \
$(DIST_DIR)/tokyo/launch.bat \
$(BUILD_DIR_ZIP)/tokyo
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/tokyo/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/tokyo/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/tokyo ; zip -r ../tokyo.zip *
$(BUILD_DIR_ZIP)/kemono.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/kemono
$(V)mkdir -p $(BUILD_DIR_ZIP)/kemono/DEVICE
$(V)cp $(DIST_DIR)/kemono/segatools.ini \
$(DIST_DIR)/kemono/launch.bat \
$(BUILD_DIR_ZIP)/kemono
$(V)cp $(BUILD_DIR_GAMES_32)/kemonohook/kemonohook.dll \
$(BUILD_DIR_ZIP)/kemono/kemonohook_x86.dll
$(V)cp $(BUILD_DIR_GAMES_64)/kemonohook/kemonohook.dll \
$(BUILD_DIR_ZIP)/kemono/kemonohook_x64.dll
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_ZIP)/kemono/inject_x86.exe
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_ZIP)/kemono/inject_x64.exe
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/kemono/DEVICE
for x in exe dll; do strip $(BUILD_DIR_ZIP)/kemono/*.$$x; done
$(V)cd $(BUILD_DIR_ZIP)/kemono ; zip -r ../kemono.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 +255,16 @@ $(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)/tokyo.zip \
$(BUILD_DIR_ZIP)/fgo.zip \
$(BUILD_DIR_ZIP)/kemono.zip \
CHANGELOG.md \ CHANGELOG.md \
README.md \ README.md \

View File

@ -1,21 +1,37 @@
# Segatools # Segatools
Version: `v005` Version: `2024-09-30`
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 * Card Maker
* [Chunithm (Plus)](doc/chunihook.md) * starting from Card Maker
* [Chunithm Air (Plus)](doc/chunihook.md) * CHUNITHM
* [Chunithm Star (Plus)](doc/chunihook.md) * up to [CHUNITHM PARADISE LOST](doc/chunihook.md)
* [Chunithm Amazon (Plus)](doc/chunihook.md) * starting from CHUNITHM NEW!!
* [Chunithm Crystal (Plus)](doc/chunihook.md) * crossbeats REV.
* up to crossbeats REV. SUNRISE
* Fate/Grand Order
* Fate/Grand Order Arcade
* Hatsune Miku: Project DIVA Arcade
* up to Future Tone
* 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) * maimai DX
* starting from maimai DX
* Mario & Sonic
* Mario & Sonic at the Tokyo 2020 Olympics Arcade
* O.N.G.E.K.I.
* starting from O.N.G.E.K.I.
* SEGA World Drivers Championship
* SEGA World Drivers Championship 2019
* WACCA
* starting from WACCA
* Kemono Friends
* Kemono Friends 3: Planet Tours
## End-users ## End-users

View File

@ -1,41 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "board/aime-dll.h"
#include "board/config.h"
#include "board/sg-reader.h"
static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"aimeio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
aime_dll_config_load(&cfg->dll, filename);
cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename);
}
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"io4", L"enable", 1, filename);
}

View File

@ -1,62 +0,0 @@
/* This is some sort of LCD display found on various cabinets. It is driven
directly by amdaemon, and it has something to do with displaying the status
of electronic payments.
Part number in schematics is "VFD GP1232A02A FUTABA".
Little else about this board is known. Black-holing the RS232 comms that it
receives seems to be sufficient for the time being. */
#include <windows.h>
#include <assert.h>
#include <stdint.h>
#include "board/vfd.h"
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT vfd_handle_irp(struct irp *irp);
static struct uart vfd_uart;
static uint8_t vfd_written[512];
static uint8_t vfd_readable[512];
HRESULT vfd_hook_init(unsigned int port_no)
{
uart_init(&vfd_uart, port_no);
vfd_uart.written.bytes = vfd_written;
vfd_uart.written.nbytes = sizeof(vfd_written);
vfd_uart.readable.bytes = vfd_readable;
vfd_uart.readable.nbytes = sizeof(vfd_readable);
return iohook_push_handler(vfd_handle_irp);
}
static HRESULT vfd_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&vfd_uart, irp)) {
return iohook_invoke_next(irp);
}
hr = uart_handle_irp(&vfd_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
dprintf("VFD TX:\n");
dump_iobuf(&vfd_uart.written);
vfd_uart.written.pos = 0;
return hr;
}

View File

@ -1,5 +0,0 @@
#pragma once
#include <windows.h>
HRESULT vfd_hook_init(unsigned int port_no);

View File

@ -1,178 +0,0 @@
#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/controlbd.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT controlbd_handle_irp(struct irp *irp);
static HRESULT controlbd_handle_irp_locked(struct irp *irp);
static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf);
static HRESULT controlbd_frame_dispatch(struct controlbd_req *dest);
static HRESULT controlbd_req_nop(uint8_t cmd);
static CRITICAL_SECTION controlbd_lock;
static struct uart controlbd_uart;
static uint8_t controlbd_written_bytes[520];
static uint8_t controlbd_readable_bytes[520];
HRESULT controlbd_hook_init(const struct controlbd_config *cfg)
{
if (!cfg->enable) {
return S_OK;
}
InitializeCriticalSection(&controlbd_lock);
uart_init(&controlbd_uart, 11);
controlbd_uart.written.bytes = controlbd_written_bytes;
controlbd_uart.written.nbytes = sizeof(controlbd_written_bytes);
controlbd_uart.readable.bytes = controlbd_readable_bytes;
controlbd_uart.readable.nbytes = sizeof(controlbd_readable_bytes);
dprintf("Control Board: Init\n");
return iohook_push_handler(controlbd_handle_irp);
}
static HRESULT controlbd_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&controlbd_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&controlbd_lock);
hr = controlbd_handle_irp_locked(irp);
LeaveCriticalSection(&controlbd_lock);
return hr;
}
static HRESULT controlbd_handle_irp_locked(struct irp *irp)
{
struct controlbd_req req;
HRESULT hr;
assert(carol_dll.controlbd_init != NULL);
if (irp->op == IRP_OP_OPEN) {
dprintf("Control Board: Starting backend DLL\n");
hr = carol_dll.controlbd_init();
if (FAILED(hr)) {
dprintf("Control Board: Backend DLL error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&controlbd_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("Control Board: TX Buffer:\n");
dump_iobuf(&controlbd_uart.written);
#endif
hr = controlbd_frame_decode(&req, &controlbd_uart.written);
if (FAILED(hr)) {
dprintf("Control Board: Deframe Error: %x\n", (int) hr);
return hr;
}
hr = controlbd_frame_dispatch(&req);
if (FAILED(hr)) {
dprintf("Control Board: Dispatch Error: %x\n", (int) hr);
return hr;
}
return hr;
}
}
static HRESULT controlbd_frame_dispatch(struct controlbd_req *req)
{
switch (req->cmd) {
case CONTROLBD_CMD_UNK_11:
return controlbd_req_nop(req->cmd);
default:
dprintf("Unhandled command %#02x\n", req->cmd);
return S_OK;
}
}
static HRESULT controlbd_req_nop(uint8_t cmd)
{
dprintf("Control Board: No-op cmd %#02x\n", cmd);
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0xE0;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x11;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x03;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x10;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01;
controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x27;
return S_OK;
}
/* Decodes the response into a struct that's easier to work with. */
static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf)
{
int initial_pos = iobuf->pos;
uint8_t check = 0;
dest->sync = iobuf->bytes[0];
iobuf->pos--;
dest->cmd = iobuf->bytes[1];
iobuf->pos--;
check += dest->cmd;
dest->checksum = iobuf->bytes[initial_pos - 1];
iobuf->pos--;
dest->data_length = initial_pos - 3; // sync, cmd, checksum
if (dest->data_length > 0) {
for (int i = 0; i < dest->data_length; i++) {
dest->data[i] = iobuf->bytes[i+2];
check += dest->data[i];
}
}
iobuf->pos -= dest->data_length;
if (dest->sync != 0xe0) {
dprintf("Control Board: Sync error, expected 0xe0, got %x\n", dest->sync);
return E_FAIL;
}
if (dest->checksum != check) {
dprintf("Control Board: Checksum error, expected %x, got %x\n", check, dest->checksum);
return E_FAIL;
}
return S_OK;
}

View File

@ -1,21 +0,0 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct controlbd_config {
bool enable;
};
enum controlbd_cmd {
CONTROLBD_CMD_UNK_11 = 0x11
};
struct controlbd_req {
uint8_t sync; // First byte is the sync
uint8_t cmd; // Command byte
uint8_t data[256]; // Request body goes here
uint8_t checksum; // Final byte is all bytes added, except the sync
uint8_t data_length; // Size of the data including command byte
};
HRESULT controlbd_hook_init(const struct controlbd_config *cfg);

View File

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

View File

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

View File

@ -1,118 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "carolhook/carol-dll.h"
#include "carolhook/touch.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT touch_handle_irp(struct irp *irp);
static HRESULT touch_handle_irp_locked(struct irp *irp);
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf);
static CRITICAL_SECTION touch_lock;
static struct uart touch_uart;
static uint8_t touch_written_bytes[520];
static uint8_t touch_readable_bytes[520];
HRESULT touch_hook_init(const struct touch_config *cfg)
{
if (!cfg->enable) {
return S_OK;
}
InitializeCriticalSection(&touch_lock);
uart_init(&touch_uart, 1);
touch_uart.written.bytes = touch_written_bytes;
touch_uart.written.nbytes = sizeof(touch_written_bytes);
touch_uart.readable.bytes = touch_readable_bytes;
touch_uart.readable.nbytes = sizeof(touch_readable_bytes);
dprintf("Touchscreen: Init\n");
return iohook_push_handler(touch_handle_irp);
return S_OK;
}
static HRESULT touch_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&touch_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&touch_lock);
hr = touch_handle_irp_locked(irp);
LeaveCriticalSection(&touch_lock);
return hr;
}
static HRESULT touch_handle_irp_locked(struct irp *irp)
{
struct touch_req req;
HRESULT hr;
assert(carol_dll.touch_init != NULL);
if (irp->op == IRP_OP_OPEN) {
dprintf("Touchscreen: Starting backend DLL\n");
hr = carol_dll.touch_init();
if (FAILED(hr)) {
dprintf("Touchscreen: Backend DLL error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&touch_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 1
dprintf("Touchscreen: TX Buffer:\n");
dump_iobuf(&touch_uart.written);
#endif
hr = touch_frame_decode(&req, &touch_uart.written);
if (FAILED(hr)) {
dprintf("Touchscreen: Deframe Error: %x\n", (int) hr);
return hr;
}
return hr;
}
}
/* Decodes the response into a struct that's easier to work with. */
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf)
{
dest->cmd = iobuf->bytes[0];
iobuf->pos--;
dest->data_length = iobuf->pos;
if (dest->data_length > 0) {
for (int i = 1; i < dest->data_length; i++) {
dest->data[i-1] = iobuf->bytes[i];
}
}
iobuf->pos -= dest->data_length;
return S_OK;
}

View File

@ -1,17 +0,0 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct touch_config {
bool enable;
};
struct touch_req {
uint8_t cmd; // First byte is the command byte
uint8_t data[256]; // rest of the data goes here
uint8_t data_length; // Size of the data including command byte
};
HRESULT touch_hook_init(const struct touch_config *cfg);

View File

@ -1,79 +0,0 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "carolio/carolio.h"
#include "carolio/config.h"
static bool carol_io_coin;
static uint16_t carol_io_coins;
static struct carol_io_config carol_io_cfg;
uint16_t carol_io_get_api_version(void)
{
return 0x0100;
}
HRESULT carol_io_jvs_init(void)
{
carol_io_config_load(&carol_io_cfg, L".\\segatools.ini");
return S_OK;
}
void carol_io_jvs_poll(uint8_t *opbtn_out, uint8_t *gamebtn_out)
{
uint8_t opbtn;
uint8_t gamebtn;
size_t i;
opbtn = 0;
if (GetAsyncKeyState(carol_io_cfg.vk_test) & 0x8000) {
opbtn |= 1;
}
if (GetAsyncKeyState(carol_io_cfg.vk_service) & 0x8000) {
opbtn |= 2;
}
for (i = 0 ; i < _countof(carol_io_cfg.vk_buttons) ; i++) {
if (GetAsyncKeyState(carol_io_cfg.vk_buttons[i]) & 0x8000) {
gamebtn |= 1 << i;
}
}
*opbtn_out = opbtn;
*gamebtn_out = gamebtn;
}
void carol_io_jvs_read_coin_counter(uint16_t *out)
{
if (out == NULL) {
return;
}
if (GetAsyncKeyState(carol_io_cfg.vk_coin) & 0x8000) {
if (!carol_io_coin) {
carol_io_coin = true;
carol_io_coins++;
}
} else {
carol_io_coin = false;
}
*out = carol_io_coins;
}
HRESULT carol_io_touch_init()
{
return S_OK;
}
HRESULT carol_io_controlbd_init()
{
return S_OK;
}

View File

@ -1,61 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include "amex/amex.h"
#include "amex/config.h"
#include "board/config.h"
#include "board/sg-reader.h"
#include "chunihook/config.h"
#include "gfxhook/config.h"
#include "hooklib/config.h"
#include "platform/config.h"
#include "platform/platform.h"
void chuni_dll_config_load(
struct chuni_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"chuniio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
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 chuni_hook_config_load(
struct chuni_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
memset(cfg, 0, sizeof(*cfg));
platform_config_load(&cfg->platform, filename);
amex_config_load(&cfg->amex, filename);
aime_config_load(&cfg->aime, filename);
gfx_config_load(&cfg->gfx, filename);
chuni_dll_config_load(&cfg->dll, filename);
slider_config_load(&cfg->slider, filename);
}

View File

@ -1,138 +0,0 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/chuniio.h"
#include "chuniio/config.h"
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx);
static bool chuni_io_coin;
static uint16_t chuni_io_coins;
static uint8_t chuni_io_hand_pos;
static HANDLE chuni_io_slider_thread;
static bool chuni_io_slider_stop_flag;
static struct chuni_io_config chuni_io_cfg;
uint16_t chuni_io_get_api_version(void)
{
return 0x0101;
}
HRESULT chuni_io_jvs_init(void)
{
chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini");
return S_OK;
}
void chuni_io_jvs_read_coin_counter(uint16_t *out)
{
if (out == NULL) {
return;
}
if (GetAsyncKeyState(chuni_io_cfg.vk_coin)) {
if (!chuni_io_coin) {
chuni_io_coin = true;
chuni_io_coins++;
}
} else {
chuni_io_coin = false;
}
*out = chuni_io_coins;
}
void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
{
size_t i;
if (GetAsyncKeyState(chuni_io_cfg.vk_test)) {
*opbtn |= 0x01; /* Test */
}
if (GetAsyncKeyState(chuni_io_cfg.vk_service)) {
*opbtn |= 0x02; /* Service */
}
if (GetAsyncKeyState(chuni_io_cfg.vk_ir)) {
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);
}
}
}
HRESULT chuni_io_slider_init(void)
{
return S_OK;
}
void chuni_io_slider_start(chuni_io_slider_callback_t callback)
{
if (chuni_io_slider_thread != NULL) {
return;
}
chuni_io_slider_thread = (HANDLE) _beginthreadex(
NULL,
0,
chuni_io_slider_thread_proc,
callback,
0,
NULL);
}
void chuni_io_slider_stop(void)
{
if (chuni_io_slider_thread == NULL) {
return;
}
chuni_io_slider_stop_flag = true;
WaitForSingleObject(chuni_io_slider_thread, INFINITE);
CloseHandle(chuni_io_slider_thread);
chuni_io_slider_thread = NULL;
chuni_io_slider_stop_flag = false;
}
void chuni_io_slider_set_leds(const uint8_t *rgb)
{
}
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx)
{
chuni_io_slider_callback_t callback;
uint8_t pressure[32];
size_t i;
callback = ctx;
while (!chuni_io_slider_stop_flag) {
for (i = 0 ; i < _countof(pressure) ; i++) {
if (GetAsyncKeyState(chuni_io_cfg.vk_cell[i]) & 0x8000) {
pressure[i] = 128;
} else {
pressure[i] = 0;
}
}
callback(pressure);
Sleep(1);
}
return 0;
}

View File

@ -1,43 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "chuniio/config.h"
static const int chuni_io_default_cells[] = {
'L', 'L', 'L', 'L',
'K', 'K', 'K', 'K',
'J', 'J', 'J', 'J',
'H', 'H', 'H', 'H',
'G', 'G', 'G', 'G',
'F', 'F', 'F', 'F',
'D', 'D', 'D', 'D',
'S', 'S', 'S', 'S',
};
void chuni_io_config_load(
struct chuni_io_config *cfg,
const wchar_t *filename)
{
wchar_t key[16];
int i;
assert(cfg != NULL);
assert(filename != NULL);
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename);
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename);
cfg->vk_ir = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename);
for (i = 0 ; i < 32 ; i++) {
swprintf_s(key, _countof(key), L"cell%i", i + 1);
cfg->vk_cell[i] = GetPrivateProfileIntW(
L"slider",
key,
chuni_io_default_cells[i],
filename);
}
}

View File

@ -12,11 +12,13 @@
#include "util/crc.h" #include "util/crc.h"
#include "util/dprintf.h" #include "util/dprintf.h"
#include "util/env.h"
struct aime_io_config { 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 +42,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 +69,16 @@ 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());
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;
@ -170,7 +223,7 @@ uint16_t aime_io_get_api_version(void)
HRESULT aime_io_init(void) HRESULT aime_io_init(void)
{ {
aime_io_config_read(&aime_io_cfg, L".\\segatools.ini"); aime_io_config_read(&aime_io_cfg, get_config_path());
return S_OK; return S_OK;
} }
@ -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(

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@
#include <winternl.h> #include <winternl.h>
#include <ntstatus.h> #include <ntstatus.h>
#include <winioctl.h>
#include <assert.h> #include <assert.h>
#include <stddef.h> #include <stddef.h>
@ -21,11 +22,9 @@
#include "util/dump.h" #include "util/dump.h"
#include "util/str.h" #include "util/str.h"
enum { #define JVS_IOCTL_HELLO CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS)
JVS_IOCTL_HELLO = 0x80006004, #define JVS_IOCTL_TRANSACT CTL_CODE(0x8000, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
JVS_IOCTL_SENSE = 0x8000600C, #define JVS_IOCTL_SENSE CTL_CODE(0x8000, 0x803, METHOD_BUFFERED, FILE_READ_ACCESS)
JVS_IOCTL_TRANSACT = 0x8000E008,
};
static HRESULT jvs_handle_irp(struct irp *irp); static HRESULT jvs_handle_irp(struct irp *irp);
static HRESULT jvs_handle_open(struct irp *irp); static HRESULT jvs_handle_open(struct irp *irp);
@ -185,14 +184,14 @@ static HRESULT jvs_ioctl_sense(struct irp *irp)
static HRESULT jvs_ioctl_transact(struct irp *irp) static HRESULT jvs_ioctl_transact(struct irp *irp)
{ {
#if 0 #if defined(LOG_JVS)
dprintf("\nJVS Port: Outbound frame:\n"); dprintf("\nJVS Port: Outbound frame:\n");
dump_const_iobuf(&irp->write); dump_const_iobuf(&irp->write);
#endif #endif
jvs_bus_transact(jvs_root, irp->write.bytes, irp->write.nbytes, &irp->read); jvs_bus_transact(jvs_root, irp->write.bytes, irp->write.nbytes, &irp->read);
#if 0 #if defined(LOG_JVS)
dprintf("JVS Port: Inbound frame:\n"); dprintf("JVS Port: Inbound frame:\n");
dump_iobuf(&irp->read); dump_iobuf(&irp->read);
dprintf("\n"); dprintf("\n");

View File

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

View File

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

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <windows.h> #include <windows.h>
#include <stdbool.h>
#include "aimeio/aimeio.h" #include "aimeio/aimeio.h"
@ -18,6 +19,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;

113
common/board/config.c Normal file
View File

@ -0,0 +1,113 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "board/aime-dll.h"
#include "board/config.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)
{
assert(cfg != NULL);
assert(filename != NULL);
// 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"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
}
void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
aime_dll_config_load(&cfg->dll, filename);
cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename);
cfg->port_no = GetPrivateProfileIntW(L"aime", L"portNo", 0, filename);
cfg->high_baudrate = GetPrivateProfileIntW(L"aime", L"highBaud", 1, filename);
cfg->gen = GetPrivateProfileIntW(L"aime", L"gen", 0, filename);
cfg->proxy_flag = GetPrivateProfileIntW(L"aime", L"proxyFlag", 2, filename);
GetPrivateProfileStringW(
L"aime",
L"authdataPath",
L"DEVICE\\authdata.bin",
cfg->authdata_path,
_countof(cfg->authdata_path),
filename);
}
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
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);
cfg->port_no = GetPrivateProfileIntW(L"vfd", L"portNo", 0, filename);
cfg->utf_conversion = GetPrivateProfileIntW(L"vfd", L"utfConversion", 0, filename);
}
void ffb_config_load(struct ffb_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"ffb", L"enable", 1, filename);
}

View File

@ -5,6 +5,10 @@
#include "board/io4.h" #include "board/io4.h"
#include "board/sg-reader.h" #include "board/sg-reader.h"
#include "board/vfd.h"
#include "board/ffb.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);
void ffb_config_load(struct ffb_config *cfg, const wchar_t *filename);

235
common/board/ffb.c Normal file
View File

@ -0,0 +1,235 @@
/*
Force Feedback Board (FFB)
This board is used by many SEGA games to provide force feedback to the player.
It is driven by the game software over a serial connection and is used by many
games such as SEGA World Drivers Championship, Initial D Arcade, ...
Part number in schematics is "838-15069 MOTOR DRIVE BD RS232/422 Board".
Some observations:
The maximal strength for any effect is 127, except Damper which maxes out at 40.
The period for rumble effects is in the range 0-40.
*/
#include "board/ffb.h"
#include <assert.h>
#include <stdint.h>
#include <windows.h>
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
// request format:
// 0x?? - sync + command
// 0x?? - direction/additional command
// 0x?? - strength
// 0x?? - checksum (sum of everything except the sync byte)
enum {
FFB_CMD_TOGGLE = 0x80,
FFB_CMD_CONSTANT_FORCE = 0x84,
FFB_CMD_RUMBLE = 0x85,
FFB_CMD_DAMPER = 0x86,
};
struct ffb_hdr {
uint8_t cmd;
};
union ffb_req_any {
struct ffb_hdr hdr;
uint8_t bytes[3];
};
static HRESULT ffb_handle_irp(struct irp *irp);
static HRESULT ffb_req_dispatch(const union ffb_req_any *req);
static HRESULT ffb_req_toggle(const uint8_t *bytes);
static HRESULT ffb_req_constant_force(const uint8_t *bytes);
static HRESULT ffb_req_rumble(const uint8_t *bytes);
static HRESULT ffb_req_damper(const uint8_t *bytes);
static const struct ffb_ops *ffb_ops;
static struct uart ffb_uart;
static bool ffb_started;
static HRESULT ffb_start_hr;
static uint8_t ffb_written[4];
static uint8_t ffb_readable[4];
/* Static variables to store maximum strength values */
static uint8_t max_constant_force = 0;
static uint8_t max_rumble = 0;
static uint8_t max_period = 0;
static uint8_t max_damper = 0;
HRESULT ffb_hook_init(
const struct ffb_config *cfg,
const struct ffb_ops *ops,
unsigned int port_no)
{
assert(cfg != NULL);
assert(ops != NULL);
if (!cfg->enable) {
return S_FALSE;
}
ffb_ops = ops;
uart_init(&ffb_uart, port_no);
ffb_uart.written.bytes = ffb_written;
ffb_uart.written.nbytes = sizeof(ffb_written);
ffb_uart.readable.bytes = ffb_readable;
ffb_uart.readable.nbytes = sizeof(ffb_readable);
dprintf("FFB: hook enabled.\n");
return iohook_push_handler(ffb_handle_irp);
}
static HRESULT ffb_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&ffb_uart, irp)) {
return iohook_invoke_next(irp);
}
hr = uart_handle_irp(&ffb_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
assert(&ffb_uart.written != NULL);
assert(ffb_uart.written.bytes != NULL || ffb_uart.written.nbytes == 0);
assert(ffb_uart.written.pos <= ffb_uart.written.nbytes);
// dprintf("FFB TX:\n");
hr = ffb_req_dispatch((const union ffb_req_any *) ffb_uart.written.bytes);
if (FAILED(hr)) {
dprintf("FFB: Processing error: %x\n", (int)hr);
}
// dump_iobuf(&ffb_uart.written);
ffb_uart.written.pos = 0;
return hr;
}
static HRESULT ffb_req_dispatch(const union ffb_req_any *req)
{
switch (req->hdr.cmd) {
case FFB_CMD_TOGGLE:
return ffb_req_toggle(req->bytes);
case FFB_CMD_CONSTANT_FORCE:
return ffb_req_constant_force(req->bytes);
case FFB_CMD_RUMBLE:
return ffb_req_rumble(req->bytes);
case FFB_CMD_DAMPER:
return ffb_req_damper(req->bytes);
/* There are some test mode specfic commands which doesn't seem to be used in
game at all. The same is true for the initialization phase. */
default:
dprintf("FFB: Unhandled command %02x\n", req->hdr.cmd);
return S_OK;
}
}
static HRESULT ffb_req_toggle(const uint8_t *bytes)
{
uint8_t activate = bytes[2];
if (activate == 0x01) {
dprintf("FFB: Activated\n");
} else {
dprintf("FFB: Deactivated\n");
}
if (ffb_ops->toggle != NULL) {
ffb_ops->toggle(activate == 0x01);
}
return S_OK;
}
static HRESULT ffb_req_constant_force(const uint8_t *bytes)
{
// dprintf("FFB: Constant force\n");
uint8_t direction = bytes[1];
uint8_t force = bytes[2];
if (direction == 0x0) {
// Right
force = 128 - force;
}
// Update max strength if the current force is greater
if (force > max_constant_force) {
max_constant_force = force;
}
// dprintf("FFB: Constant Force Strength: %d (Max: %d)\n", force, max_constant_force);
if (ffb_ops->constant_force != NULL) {
ffb_ops->constant_force(direction, force);
}
return S_OK;
}
static HRESULT ffb_req_rumble(const uint8_t *bytes)
{
// dprintf("FFB: Rumble\n");
uint8_t force = bytes[1];
uint8_t period = bytes[2];
// Update max strength if the current force is greater
if (force > max_rumble) {
max_rumble = force;
}
if (period > max_period) {
max_period = period;
}
// dprintf("FFB: Rumble Period: %d (Max %d), Strength: %d (Max: %d)\n", period, max_period, force, max_rumble);
if (ffb_ops->rumble != NULL) {
ffb_ops->rumble(force, period);
}
return S_OK;
}
static HRESULT ffb_req_damper(const uint8_t *bytes)
{
// dprintf("FFB: Damper\n");
uint8_t force = bytes[2];
// Update max strength if the current force is greater
if (force > max_damper) {
max_damper = force;
}
// dprintf("FFB: Damper Strength: %d (Max: %d)\n", force, max_damper);
if (ffb_ops->damper != NULL) {
ffb_ops->damper(force);
}
return S_OK;
}

21
common/board/ffb.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct ffb_config {
bool enable;
};
struct ffb_ops {
void (*toggle)(bool active);
void (*constant_force)(uint8_t direction, uint8_t force);
void (*rumble)(uint8_t force, uint8_t period);
void (*damper)(uint8_t force);
};
HRESULT ffb_hook_init(
const struct ffb_config *cfg,
const struct ffb_ops *ops,
unsigned int port_no);

View File

@ -16,6 +16,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include "board/io3.h" #include "board/io3.h"
@ -79,6 +80,11 @@ static HRESULT io3_cmd_read_analogs(
struct const_iobuf *req_buf, struct const_iobuf *req_buf,
struct iobuf *resp_buf); struct iobuf *resp_buf);
static HRESULT io3_cmd_read_rotarys(
struct io3 *io3,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT io3_cmd_write_gpio( static HRESULT io3_cmd_write_gpio(
struct io3 *io3, struct io3 *io3,
struct const_iobuf *req_buf, struct const_iobuf *req_buf,
@ -116,6 +122,13 @@ static uint8_t io3_features[] = {
0x03, 8, 10, 0, 0x03, 8, 10, 0,
/* Feature : 0x04 : Rotary inputs
Param1 : 4 : Number of rotary channels
Param2 : 0 : N/A
Param3 : 0 : N/A */
0x04, 4, 0, 0,
/* Feature : 0x12 : GPIO outputs /* Feature : 0x12 : GPIO outputs
Param1 : 3 : Number of ports (8 bits per port) Param1 : 3 : Number of ports (8 bits per port)
Param2 : 0 : N/A Param2 : 0 : N/A
@ -219,6 +232,9 @@ static HRESULT io3_cmd(
case JVS_CMD_READ_ANALOGS: case JVS_CMD_READ_ANALOGS:
return io3_cmd_read_analogs(io3, req, resp); return io3_cmd_read_analogs(io3, req, resp);
case JVS_CMD_READ_ROTARYS:
return io3_cmd_read_rotarys(io3, req, resp);
case JVS_CMD_WRITE_GPIO: case JVS_CMD_WRITE_GPIO:
return io3_cmd_write_gpio(io3, req, resp); return io3_cmd_write_gpio(io3, req, resp);
@ -375,7 +391,7 @@ static HRESULT io3_cmd_read_switches(
return hr; return hr;
} }
#if 0 #if defined(LOG_IO3)
dprintf("JVS I/O: Read switches, np=%i, bpp=%i\n", dprintf("JVS I/O: Read switches, np=%i, bpp=%i\n",
req.num_players, req.num_players,
req.bytes_per_player); req.bytes_per_player);
@ -536,6 +552,60 @@ static HRESULT io3_cmd_read_analogs(
} }
static HRESULT io3_cmd_read_rotarys(
struct io3 *io3,
struct const_iobuf *req_buf,
struct iobuf *resp_buf)
{
struct jvs_req_read_rotarys req;
uint16_t rotarys[4];
uint8_t i;
HRESULT hr;
/* Read req */
hr = iobuf_read(req_buf, &req, sizeof(req));
if (FAILED(hr)) {
return hr;
}
if (req.nrotarys > _countof(rotarys)) {
dprintf("JVS I/O: Invalid analog count %i\n", req.nrotarys);
return E_FAIL;
}
//dprintf("JVS I/O: Read rotarys, nrotarys=%i\n", req.nrotarys);
/* Write report byte */
hr = iobuf_write_8(resp_buf, 0x01);
if (FAILED(hr)) {
return hr;
}
/* Write analogs */
memset(rotarys, 0, sizeof(rotarys));
if (io3->ops->read_rotarys != NULL) {
io3->ops->read_rotarys(io3->ops_ctx, rotarys, req.nrotarys);
}
for (i = 0 ; i < req.nrotarys ; i++) {
hr = iobuf_write_be16(resp_buf, rotarys[i]);
if (FAILED(hr)) {
return hr;
}
}
return hr;
}
static HRESULT io3_cmd_write_gpio( static HRESULT io3_cmd_write_gpio(
struct io3 *io3, struct io3 *io3,
struct const_iobuf *req_buf, struct const_iobuf *req_buf,

View File

@ -18,6 +18,7 @@ struct io3_ops {
void (*write_gpio)(void *ctx, uint32_t state); void (*write_gpio)(void *ctx, uint32_t state);
void (*read_switches)(void *ctx, struct io3_switch_state *out); void (*read_switches)(void *ctx, struct io3_switch_state *out);
void (*read_analogs)(void *ctx, uint16_t *analogs, uint8_t nanalogs); void (*read_analogs)(void *ctx, uint16_t *analogs, uint8_t nanalogs);
void (*read_rotarys)(void *ctx, uint16_t *rotaries, uint8_t nrotaries);
void (*read_coin_counter)(void *ctx, uint8_t slot_no, uint16_t *out); void (*read_coin_counter)(void *ctx, uint8_t slot_no, uint16_t *out);
}; };

View File

@ -7,6 +7,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include "board/config.h" #include "board/config.h"
#include "board/guid.h" #include "board/guid.h"
@ -28,7 +29,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 +41,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");
@ -48,7 +49,7 @@ static_assert(sizeof(struct io4_report_in) == 0x40, "IO4 IN report size");
struct io4_report_out { struct io4_report_out {
uint8_t report_id; uint8_t report_id;
uint8_t cmd; uint8_t cmd;
uint8_t payload[62]; uint8_t payload[IO4_REPORT_OUT_PAYLOAD_LEN];
}; };
static_assert(sizeof(struct io4_report_out) == 0x40, "IO4 OUT report size"); static_assert(sizeof(struct io4_report_out) == 0x40, "IO4 OUT report size");
@ -223,7 +224,11 @@ static HRESULT io4_handle_write(struct irp *irp)
return S_OK; return S_OK;
case IO4_CMD_SET_GENERAL_OUTPUT: case IO4_CMD_SET_GENERAL_OUTPUT:
dprintf("USB I/O: GPIO Out\n"); // dprintf("USB I/O: GPIO Out\n");
if (io4_ops->write_gpio != NULL) {
return io4_ops->write_gpio(out.payload, IO4_REPORT_OUT_PAYLOAD_LEN);
}
return S_OK; return S_OK;
@ -232,15 +237,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 +321,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 */

View File

@ -3,6 +3,9 @@
#include <windows.h> #include <windows.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#define IO4_REPORT_OUT_PAYLOAD_LEN 62
enum { enum {
/* System buttons in button[0] */ /* System buttons in button[0] */
@ -24,6 +27,7 @@ struct io4_state {
struct io4_ops { struct io4_ops {
HRESULT (*poll)(void *ctx, struct io4_state *state); HRESULT (*poll)(void *ctx, struct io4_state *state);
HRESULT (*write_gpio)(uint8_t* payload, size_t len);
}; };
HRESULT io4_hook_init( HRESULT io4_hook_init(

View File

@ -0,0 +1,81 @@
#pragma once
#include "board/led15070-frame.h"
/* Command IDs */
enum {
LED_15070_CMD_RESET = 0x10,
LED_15070_CMD_SET_INPUT = 0x28, // No known use case
LED_15070_CMD_SET_NORMAL_12BIT = 0x30, // TODO
LED_15070_CMD_SET_NORMAL_8BIT = 0x31,
LED_15070_CMD_SET_MULTI_FLASH_8BIT = 0x32,
LED_15070_CMD_SET_MULTI_FADE_8BIT = 0x33,
LED_15070_CMD_SET_PALETTE_7_NORMAL_LED = 0x34, // No known use case
LED_15070_CMD_SET_PALETTE_6_FLASH_LED = 0x35, // No known use case
LED_15070_CMD_SET_15DC_OUT = 0x36, // No known use case
LED_15070_CMD_SET_15GS_OUT = 0x37, // No known use case
LED_15070_CMD_SET_PSC_MAX = 0x38, // No known use case
LED_15070_CMD_SET_FET_OUTPUT = 0x39,
LED_15070_CMD_SET_GS_PALETTE = 0x3A,
LED_15070_CMD_DC_UPDATE = 0x3B,
LED_15070_CMD_GS_UPDATE = 0x3C,
LED_15070_CMD_ROTATE = 0x3E, // No known use case, wtf is this?
LED_15070_CMD_SET_DC_DATA = 0x3F,
LED_15070_CMD_EEPROM_WRITE = 0x7B,
LED_15070_CMD_EEPROM_READ = 0x7C,
LED_15070_CMD_ACK_ON = 0x7D,
LED_15070_CMD_ACK_OFF = 0x7E,
LED_15070_CMD_BOARD_INFO = 0xF0,
LED_15070_CMD_BOARD_STATUS = 0xF1,
LED_15070_CMD_FW_SUM = 0xF2,
LED_15070_CMD_PROTOCOL_VER = 0xF3,
LED_15070_CMD_TO_BOOT_MODE = 0xFD,
LED_15070_CMD_FW_UPDATE = 0xFE,
};
/* Response codes */
enum {
LED_15070_STATUS_OK = 0x01,
LED_15070_STATUS_SUM_ERR = 0x02,
LED_15070_STATUS_PARITY_ERR = 0x03,
LED_15070_STATUS_FRAMING_ERR = 0x04,
LED_15070_STATUS_OVERRUN_ERR = 0x05,
LED_15070_STATUS_BUFFER_OVERFLOW = 0x06,
};
enum {
LED_15070_REPORT_OK = 0x01,
LED_15070_REPORT_WAIT = 0x02,
LED_15070_REPORT_ERR1 = 0x03,
LED_15070_REPORT_ERR2 = 0x04,
};
/* Request data structures */
struct led15070_req_any {
struct led15070_hdr hdr;
uint8_t cmd;
uint8_t payload[256];
};
/* Response data structures */
struct led15070_resp_any {
struct led15070_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
uint8_t data[32];
};
struct led15070_resp_board_info {
struct led15070_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
char board_num[8];
uint8_t endcode; // Always 0xFF
uint8_t fw_ver;
};

View File

@ -0,0 +1,194 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "board/led15070-frame.h"
#include "hook/iobuf.h"
static void led15070_frame_sync(struct iobuf *src);
static HRESULT led15070_frame_accept(const struct iobuf *dest);
static HRESULT led15070_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 led15070_frame_sync(struct iobuf *src)
{
size_t i;
for (i = 0 ; i < src->pos && src->bytes[i] != 0xE0 ; i++);
src->pos -= i;
memmove(&src->bytes[0], &src->bytes[i], i);
}
static HRESULT led15070_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 led15070_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);
led15070_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 == 0xE0) {
hr = E_FAIL;
} else if (byte == 0xD0) {
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 = led15070_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 led15070_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] == 0xE0 && src[3] + 4 == nbytes);
if (dest->pos >= dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = 0xE0;
checksum = 0;
// dprintf("%02x ", 0xe0);
for (i = 1 ; i < nbytes ; i++) {
byte = src[i];
checksum += byte;
// dprintf("%02x ", byte);
hr = led15070_frame_encode_byte(dest, byte);
if (FAILED(hr)) {
return hr;
}
}
// dprintf("%02x \n", checksum);
return led15070_frame_encode_byte(dest, checksum);
}
static HRESULT led15070_frame_encode_byte(struct iobuf *dest, uint8_t byte)
{
if (byte == 0xE0 || byte == 0xD0) {
if (dest->pos + 2 > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = 0xD0;
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;
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
enum {
LED_15070_FRAME_SYNC = 0xE0,
};
struct led15070_hdr {
uint8_t sync;
uint8_t dest_adr;
uint8_t src_adr;
uint8_t nbytes;
};
HRESULT led15070_frame_decode(struct iobuf *dest, struct iobuf *src);
HRESULT led15070_frame_encode(
struct iobuf *dest,
const void *ptr,
size_t nbytes);

1300
common/board/led15070.c Normal file

File diff suppressed because it is too large Load Diff

28
common/board/led15070.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct led15070_config {
bool enable;
unsigned int port_no[2];
char board_number[8];
uint8_t fw_ver;
uint16_t fw_sum;
wchar_t eeprom_path[MAX_PATH];
};
typedef HRESULT (*io_led_init_t)(void);
typedef void (*io_led_set_fet_output_t)(uint8_t board, const uint8_t *rgb);
typedef void (*io_led_dc_update_t)(uint8_t board, const uint8_t *rgb);
typedef void (*io_led_gs_update_t)(uint8_t board, const uint8_t *rgb);
HRESULT led15070_hook_init(
const struct led15070_config *cfg,
io_led_init_t _led_init,
io_led_set_fet_output_t _led_set_fet_output,
io_led_dc_update_t _led_dc_update,
io_led_gs_update_t _led_gs_update,
unsigned int port_no[2]);

222
common/board/led15093-cmd.h Normal file
View 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;
uint16_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;
uint16_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;
};

View 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;
}

View 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);

1129
common/board/led15093.c Normal file

File diff suppressed because it is too large Load Diff

24
common/board/led15093.h Normal file
View 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[2];
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 port_no[2]);

View File

@ -2,7 +2,6 @@ board_lib = static_library(
'board', 'board',
include_directories : inc, include_directories : inc,
implicit_include_directories : false, implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [ dependencies : [
capnhook.get_variable('hook_dep'), capnhook.get_variable('hook_dep'),
], ],
@ -20,6 +19,16 @@ 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',
'led15070-cmd.h',
'led15070-frame.c',
'led15070-frame.h',
'led15070.c',
'led15070.h',
'sg-cmd.c', 'sg-cmd.c',
'sg-cmd.h', 'sg-cmd.h',
'sg-frame.c', 'sg-frame.c',
@ -37,5 +46,10 @@ board_lib = static_library(
'slider-frame.h', 'slider-frame.h',
'vfd.c', 'vfd.c',
'vfd.h', 'vfd.h',
'vfd-cmd.h',
'vfd-frame.c',
'vfd-frame.h',
'ffb.c',
'ffb.h'
], ],
) )

View File

@ -25,6 +25,13 @@ struct sg_res_header {
uint8_t payload_len; uint8_t payload_len;
}; };
/* struct to save the version string with its length
to fix NUL terminator issues */
struct version_info {
const char *version;
uint8_t length;
};
typedef HRESULT (*sg_dispatch_fn_t)( typedef HRESULT (*sg_dispatch_fn_t)(
void *ctx, void *ctx,
const void *req, const void *req,

View File

@ -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 {

View File

@ -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[] = { static const struct version_info led_version[] = {
'1', '5', '0', '8', '4', 0xFF, 0x10, 0x00, 0x12, {"15084\xFF\x10\x00\x12", 9},
{"000-00000\xFF\x11\x40", 12},
// maybe the same?
{"000-00000\xFF\x11\x40", 12}
}; };
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)); const struct version_info *fw = &led_version[led->gen - 1];
sg_res_init(&res->res, req, fw->length);
memcpy(res->payload, fw->version, fw->length);
return S_OK; return S_OK;
} }

View File

@ -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(

View File

@ -5,18 +5,21 @@
#pragma pack(push, 1) #pragma pack(push, 1)
enum { enum {
SG_NFC_CMD_GET_FW_VERSION = 0x30, SG_NFC_CMD_GET_FW_VERSION = 0x30,
SG_NFC_CMD_GET_HW_VERSION = 0x32, SG_NFC_CMD_GET_HW_VERSION = 0x32,
SG_NFC_CMD_RADIO_ON = 0x40, SG_NFC_CMD_RADIO_ON = 0x40,
SG_NFC_CMD_RADIO_OFF = 0x41, SG_NFC_CMD_RADIO_OFF = 0x41,
SG_NFC_CMD_POLL = 0x42, SG_NFC_CMD_POLL = 0x42,
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43, SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x50, SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x50,
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52, SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME = 0x51,
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54, SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
SG_NFC_CMD_MIFARE_AUTHENTICATE = 0x55, /* guess based on time sent */ SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x54,
SG_NFC_CMD_RESET = 0x62, SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA = 0x55,
SG_NFC_CMD_FELICA_ENCAP = 0x71, SG_NFC_CMD_TO_UPDATE_MODE = 0x60,
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
SG_NFC_CMD_RESET = 0x62,
SG_NFC_CMD_FELICA_ENCAP = 0x71,
}; };
struct sg_nfc_res_get_fw_version { struct sg_nfc_res_get_fw_version {
@ -31,7 +34,7 @@ struct sg_nfc_res_get_hw_version {
struct sg_nfc_req_mifare_set_key { struct sg_nfc_req_mifare_set_key {
struct sg_req_header req; struct sg_req_header req;
uint8_t key_a[6]; uint8_t key[6];
}; };
struct sg_nfc_req_mifare_50 { struct sg_nfc_req_mifare_50 {

View File

@ -2,6 +2,7 @@
#include <assert.h> #include <assert.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@ -16,6 +17,7 @@
#include "util/dprintf.h" #include "util/dprintf.h"
#include "util/dump.h" #include "util/dump.h"
#include "util/slurp.h"
static HRESULT sg_nfc_dispatch( static HRESULT sg_nfc_dispatch(
void *ctx, void *ctx,
@ -60,15 +62,35 @@ static HRESULT sg_nfc_cmd_felica_encap(
const struct sg_nfc_req_felica_encap *req, const struct sg_nfc_req_felica_encap *req,
struct sg_nfc_res_felica_encap *res); struct sg_nfc_res_felica_encap *res);
static HRESULT sg_nfc_cmd_send_hex_data(
struct sg_nfc *nfc,
const struct sg_req_header *req,
struct sg_res_header *res);
static HRESULT sg_nfc_cmd_dummy( static HRESULT sg_nfc_cmd_dummy(
struct sg_nfc *nfc, struct sg_nfc *nfc,
const struct sg_req_header *req, const struct sg_req_header *req,
struct sg_res_header *res); struct sg_res_header *res);
static const struct version_info hw_version[] = {
{"TN32MSEC003S H/W Ver3.0", 23},
{"837-15286", 9},
{"837-15396", 9}
};
static const struct version_info fw_version[] = {
{"TN32MSEC003S F/W Ver1.2", 23},
{"\x94", 1},
{"\x94", 1}
};
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,
unsigned int proxy_flag,
const wchar_t* authdata_path,
void *ops_ctx) void *ops_ctx)
{ {
assert(nfc != NULL); assert(nfc != NULL);
@ -77,6 +99,9 @@ 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;
nfc->proxy_flag = proxy_flag;
nfc->authdata_path = authdata_path;
} }
#ifdef NDEBUG #ifdef NDEBUG
@ -170,12 +195,17 @@ static HRESULT sg_nfc_dispatch(
&req->felica_encap, &req->felica_encap,
&res->felica_encap); &res->felica_encap);
case SG_NFC_CMD_MIFARE_AUTHENTICATE: case SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME:
case SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA:
case SG_NFC_CMD_SEND_HEX_DATA:
return sg_nfc_cmd_send_hex_data(nfc, &req->simple, &res->simple);
case SG_NFC_CMD_MIFARE_SELECT_TAG: case SG_NFC_CMD_MIFARE_SELECT_TAG:
case SG_NFC_CMD_MIFARE_SET_KEY_AIME: case SG_NFC_CMD_MIFARE_SET_KEY_AIME:
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_TO_UPDATE_MODE:
return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple); return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple);
default: default:
@ -202,9 +232,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)
{ {
const struct version_info *fw = &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, fw->length);
memcpy(res->version, "TN32MSEC003S F/W Ver1.2E", sizeof(res->version)); memcpy(res->version, fw->version, fw->length);
return S_OK; return S_OK;
} }
@ -214,9 +246,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)
{ {
const struct version_info *hw = &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, hw->length);
memcpy(res->version, "TN32MSEC003S H/W Ver3.0J", sizeof(res->version)); memcpy(res->version, hw->version, hw->length);
return S_OK; return S_OK;
} }
@ -287,6 +321,7 @@ static HRESULT sg_nfc_poll_aime(
mifare->type = 0x10; mifare->type = 0x10;
mifare->id_len = sizeof(mifare->uid); mifare->id_len = sizeof(mifare->uid);
// mifare->uid = _byteswap_ulong(0x8FBECBFF);
mifare->uid = _byteswap_ulong(0x01020304); mifare->uid = _byteswap_ulong(0x01020304);
/* Initialize MIFARE IC emulator */ /* Initialize MIFARE IC emulator */
@ -326,13 +361,13 @@ static HRESULT sg_nfc_poll_felica(
felica->type = 0x20; felica->type = 0x20;
felica->id_len = sizeof(felica->IDm) + sizeof(felica->PMm); felica->id_len = sizeof(felica->IDm) + sizeof(felica->PMm);
felica->IDm = _byteswap_uint64(IDm); felica->IDm = _byteswap_uint64(IDm);
felica->PMm = _byteswap_uint64(felica_get_generic_PMm()); felica->PMm = _byteswap_uint64(felica_get_amusement_ic_PMm());
/* Initialize FeliCa IC emulator */ /* Initialize FeliCa IC emulator */
nfc->felica.IDm = IDm; nfc->felica.IDm = IDm;
nfc->felica.PMm = felica_get_generic_PMm(); nfc->felica.PMm = felica_get_amusement_ic_PMm();
nfc->felica.system_code = 0x0000; nfc->felica.system_code = 0x88b4;
return S_OK; return S_OK;
} }
@ -354,18 +389,62 @@ static HRESULT sg_nfc_cmd_mifare_read_block(
sg_nfc_dprintf(nfc, "Read uid %08x block %i\n", uid, req->payload.block_no); sg_nfc_dprintf(nfc, "Read uid %08x block %i\n", uid, req->payload.block_no);
if (req->payload.block_no > 3) { if (req->payload.block_no > 14) {
sg_nfc_dprintf(nfc, "MIFARE block number out of range\n"); sg_nfc_dprintf(nfc, "MIFARE block number out of range\n");
return E_FAIL; return E_FAIL;
} else if (req->payload.block_no >= 5){ // emoney auth encrypted
sg_res_init(&res->res, &req->req, sizeof(res->block));
char* auth;
long size = wslurp(nfc->authdata_path, &auth, false);
if (size < 0){
sg_nfc_dprintf(nfc, "Failed to read %ls: %lx!\n", nfc->authdata_path, GetLastError());
return E_FAIL;
}
int offset = 0;
if (req->payload.block_no == 6){
offset = 16;
} else if (req->payload.block_no == 8){
offset = 32;
} else if (req->payload.block_no == 9){
offset = 48;
} else if (req->payload.block_no == 10){
offset = 64;
} else if (req->payload.block_no == 12){
offset = 82;
} else if (req->payload.block_no == 13){
offset = 98;
} else if (req->payload.block_no == 14){
offset = 114;
}
for (int i = 0; i < 16 && offset + i < size; i++){
res->block[i] = auth[offset + i];
}
free(auth);
} else if (req->payload.block_no == 4){ // emoney auth plain
sg_res_init(&res->res, &req->req, sizeof(res->block));
res->block[0] = 0x54; // header
res->block[1] = 0x43;
res->block[2] = nfc->proxy_flag; // 2 or 3 depending on game (useProxy in env.json)
res->block[3] = 0x01; // unknown flag
} else { // read all other blocks normally
sg_res_init(&res->res, &req->req, sizeof(res->block));
memcpy( res->block,
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
sizeof(res->block));
} }
sg_res_init(&res->res, &req->req, sizeof(res->block));
memcpy( res->block,
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
sizeof(res->block));
return S_OK; return S_OK;
} }
@ -401,7 +480,7 @@ static HRESULT sg_nfc_cmd_felica_encap(
f_res.nbytes = sizeof(res->payload); f_res.nbytes = sizeof(res->payload);
f_res.pos = 1; f_res.pos = 1;
#if 0 #if defined(LOG_NFC)
dprintf("FELICA OUTBOUND:\n"); dprintf("FELICA OUTBOUND:\n");
dump_const_iobuf(&f_req); dump_const_iobuf(&f_req);
#endif #endif
@ -415,7 +494,7 @@ static HRESULT sg_nfc_cmd_felica_encap(
sg_res_init(&res->res, &req->req, f_res.pos); sg_res_init(&res->res, &req->req, f_res.pos);
res->payload[0] = f_res.pos; res->payload[0] = f_res.pos;
#if 0 #if defined(LOG_NFC)
dprintf("FELICA INBOUND:\n"); dprintf("FELICA INBOUND:\n");
dump_iobuf(&f_res); dump_iobuf(&f_res);
#endif #endif
@ -423,6 +502,22 @@ static HRESULT sg_nfc_cmd_felica_encap(
return S_OK; return S_OK;
} }
static HRESULT sg_nfc_cmd_send_hex_data(
struct sg_nfc *nfc,
const struct sg_req_header *req,
struct sg_res_header *res)
{
sg_res_init(res, req, 0);
/* Firmware checksum length? */
if (req->payload_len == 0x2b) {
/* The firmware is identical flag? */
res->status = 0x20;
}
return S_OK;
}
static HRESULT sg_nfc_cmd_dummy( static HRESULT sg_nfc_cmd_dummy(
struct sg_nfc *nfc, struct sg_nfc *nfc,
const struct sg_req_header *req, const struct sg_req_header *req,

View File

@ -22,14 +22,20 @@ 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;
unsigned int proxy_flag;
struct felica felica; struct felica felica;
struct mifare mifare; struct mifare mifare;
const wchar_t* authdata_path;
}; };
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,
unsigned int proxy_flag,
const wchar_t* authdata_path,
void *ops_ctx); void *ops_ctx);
void sg_nfc_transact( void sg_nfc_transact(

View File

@ -47,7 +47,8 @@ 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 default_port_no,
unsigned int gen,
HINSTANCE self) HINSTANCE self)
{ {
HRESULT hr; HRESULT hr;
@ -65,11 +66,31 @@ HRESULT sg_reader_hook_init(
return hr; return hr;
} }
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, NULL); unsigned int port_no = cfg->port_no;
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, NULL); if (port_no == 0){
port_no = default_port_no;
}
if (cfg->gen != 0) {
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, cfg->proxy_flag, cfg->authdata_path, 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;
}
dprintf("NFC Assembly: enabling (port=%d)\n", port_no);
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);
@ -100,14 +121,14 @@ static HRESULT sg_reader_handle_irp_locked(struct irp *irp)
{ {
HRESULT hr; HRESULT hr;
#if 0 #if defined(LOG_NFC)
if (irp->op == IRP_OP_WRITE) { if (irp->op == IRP_OP_WRITE) {
dprintf("WRITE:\n"); dprintf("WRITE:\n");
dump_const_iobuf(&irp->write); dump_const_iobuf(&irp->write);
} }
#endif #endif
#if 0 #if defined(LOG_NFC)
if (irp->op == IRP_OP_READ) { if (irp->op == IRP_OP_READ) {
dprintf("READ:\n"); dprintf("READ:\n");
dump_iobuf(&sg_reader_uart.readable); dump_iobuf(&sg_reader_uart.readable);

View File

@ -9,9 +9,15 @@
struct aime_config { struct aime_config {
struct aime_dll_config dll; struct aime_dll_config dll;
bool enable; bool enable;
unsigned int port_no;
bool high_baudrate;
unsigned int gen;
unsigned int proxy_flag;
wchar_t authdata_path[MAX_PATH];
}; };
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 default_port_no,
unsigned int gen,
HINSTANCE self); HINSTANCE self);

123
common/board/vfd-cmd.h Normal file
View File

@ -0,0 +1,123 @@
#pragma once
#include "board/vfd-frame.h"
enum {
VFD_CMD_GET_VERSION = 0x5B,
VFD_CMD_RESET = 0x0B,
VFD_CMD_CLEAR_SCREEN = 0x0C,
VFD_CMD_SET_BRIGHTNESS = 0x20,
VFD_CMD_SET_SCREEN_ON = 0x21,
VFD_CMD_SET_H_SCROLL = 0x22,
VFD_CMD_DRAW_IMAGE = 0x2E,
VFD_CMD_SET_CURSOR = 0x30,
VFD_CMD_SET_ENCODING = 0x32,
VFD_CMD_SET_TEXT_WND = 0x40,
VFD_CMD_SET_TEXT_SPEED = 0x41,
VFD_CMD_WRITE_TEXT = 0x50,
VFD_CMD_ENABLE_SCROLL = 0x51,
VFD_CMD_DISABLE_SCROLL = 0x52,
VFD_CMD_ROTATE = 0x5D,
VFD_CMD_CREATE_CHAR = 0xA3,
VFD_CMD_CREATE_CHAR2 = 0xA4,
};
enum {
VFD_ENC_GB2312 = 0,
VFD_ENC_BIG5 = 1,
VFD_ENC_SHIFT_JIS = 2,
VFD_ENC_KSC5601 = 3,
VFD_ENC_MAX = 3,
};
struct vfd_req_hdr {
uint8_t sync;
uint8_t cmd;
};
struct vfd_req_any {
struct vfd_req_hdr hdr;
uint8_t payload[2054];
};
struct vfd_req_board_info {
struct vfd_req_hdr hdr;
uint8_t unk1;
};
struct vfd_resp_board_info { // \x0201.20\x03
uint8_t unk1;
char version[5];
uint8_t unk2;
};
struct vfd_req_reset {
struct vfd_req_hdr hdr;
};
struct vfd_req_cls {
struct vfd_req_hdr hdr;
};
struct vfd_req_brightness {
struct vfd_req_hdr hdr;
uint8_t brightness;
};
struct vfd_req_power {
struct vfd_req_hdr hdr;
uint8_t power_state;
};
struct vfd_req_hscroll {
struct vfd_req_hdr hdr;
uint8_t x_pos;
};
struct vfd_req_draw {
struct vfd_req_hdr hdr;
uint16_t x0;
uint8_t y0;
uint16_t x1;
uint8_t y1;
uint8_t image[2048];
};
struct vfd_req_cursor {
struct vfd_req_hdr hdr;
uint16_t x;
uint8_t y;
};
struct vfd_req_encoding {
struct vfd_req_hdr hdr;
uint8_t encoding;
};
struct vfd_req_wnd {
struct vfd_req_hdr hdr;
uint16_t x0;
uint8_t y0;
uint16_t x1;
uint8_t y1;
};
struct vfd_req_speed {
struct vfd_req_hdr hdr;
uint8_t encoding;
};
struct vfd_req_scroll {
struct vfd_req_hdr hdr;
};
struct vfd_req_rotate {
struct vfd_req_hdr hdr;
uint8_t unk1;
};
struct vfd_req_create_char {
struct vfd_req_hdr hdr;
uint8_t type;
uint8_t pixels[32];
};

88
common/board/vfd-frame.c Normal file
View File

@ -0,0 +1,88 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define SUPER_VERBOSE 1
#include "board/vfd-frame.h"
#include "hook/iobuf.h"
#include "util/dprintf.h"
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte);
/* Frame structure:
REQUEST:
[0] Sync byte (0x1A or 0x1B)
[1] Packet ID
[2...n-1] Data/payload
--- OR ---
if no sync byte is given, plain static text in the currently configured encoding is expected.
RESPONSE:
This thing never responds, unless it's VFD_CMD_GET_VERSION
*/
bool vfd_frame_sync(struct const_iobuf *src) {
return src->bytes[src->pos] == VFD_SYNC_BYTE || src->bytes[src->pos] == VFD_SYNC_BYTE2;
}
HRESULT vfd_frame_encode(
struct iobuf *dest,
const void *ptr,
size_t nbytes) {
const uint8_t *src;
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;
if (dest->pos >= dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
#if SUPER_VERBOSE
dprintf("VFD: RX Buffer:\n");
#endif
for (i = 1; i < nbytes; i++) {
byte = src[i];
#if SUPER_VERBOSE
dprintf("%02x ", byte);
#endif
hr = vfd_frame_encode_byte(dest, byte);
if (FAILED(hr)) {
return hr;
}
}
#if SUPER_VERBOSE
dprintf("\n");
#endif
return hr;
}
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte) {
if (dest->pos + 1 > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = byte;
return S_OK;
}

20
common/board/vfd-frame.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
enum {
VFD_SYNC_BYTE = 0x1B,
VFD_SYNC_BYTE2 = 0x1A,
};
bool vfd_frame_sync(struct const_iobuf *src);
HRESULT vfd_frame_encode(
struct iobuf *dest,
const void *ptr,
size_t nbytes);

399
common/board/vfd.c Normal file
View File

@ -0,0 +1,399 @@
/* This is some sort of LCD display found on various cabinets. It is driven
directly by amdaemon, and it has something to do with displaying the status
of electronic payments.
Part number in schematics is "VFD GP1232A02A FUTABA". */
#include <windows.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include "board/config.h"
#include "board/vfd.h"
#include "board/vfd-cmd.h"
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
#define SUPER_VERBOSE 0
static HRESULT vfd_handle_irp(struct irp *irp);
static struct uart vfd_uart;
static uint8_t vfd_written[4096];
static uint8_t vfd_readable[4096];
static int encoding = VFD_ENC_SHIFT_JIS;
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
static bool utf_enabled;
HRESULT vfd_hook_init(struct vfd_config *cfg, unsigned int default_port_no)
{
if (!cfg->enable){
return S_FALSE;
}
utf_enabled = cfg->utf_conversion;
unsigned int port_no = cfg->port_no;
if (port_no == 0){
port_no = default_port_no;
}
dprintf("VFD: enabling (port=%d)\n", port_no);
uart_init(&vfd_uart, port_no);
vfd_uart.written.bytes = vfd_written;
vfd_uart.written.nbytes = sizeof(vfd_written);
vfd_uart.readable.bytes = vfd_readable;
vfd_uart.readable.nbytes = sizeof(vfd_readable);
return iohook_push_handler(vfd_handle_irp);
}
const char* get_encoding_name(int b){
switch (b){
case 0: return "gb2312";
case 1: return "big5";
case 2: return "shift-jis";
case 3: return "ks_c_5601-1987";
default: return "unknown";
}
}
void print_vfd_text(const char* str, int len){
if (utf_enabled){
wchar_t encoded[1024];
memset(encoded, 0, 1024 * sizeof(wchar_t));
int codepage = 0;
if (encoding == VFD_ENC_GB2312){
codepage = 936;
} else if (encoding == VFD_ENC_BIG5){
codepage = 950;
} else if (encoding == VFD_ENC_SHIFT_JIS){
codepage = 932;
} else if (encoding == VFD_ENC_KSC5601) {
codepage = 949;
}
if (!MultiByteToWideChar(codepage, MB_USEGLYPHCHARS, str, len, encoded, 1024)){
dprintf("VFD: Text conversion failed: %ld", GetLastError());
return;
}
dprintf("VFD: Text: %ls\n", encoded);
} else {
dprintf("VFD: Text: %s\n", str);
}
}
static HRESULT vfd_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&vfd_uart, irp)) {
return iohook_invoke_next(irp);
}
if (irp->op == IRP_OP_OPEN){
dprintf("VFD: Open\n");
} else if (irp->op == IRP_OP_CLOSE){
dprintf("VFD: Close\n");
}
hr = uart_handle_irp(&vfd_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
#if SUPER_VERBOSE
dprintf("VFD TX:\n");
dump_iobuf(&vfd_uart.written);
#endif
struct const_iobuf reader;
iobuf_flip(&reader, &vfd_uart.written);
struct iobuf* writer = &vfd_uart.readable;
for (; reader.pos < reader.nbytes ; ){
if (vfd_frame_sync(&reader)) {
reader.pos++; // get the sync byte out of the way
uint8_t cmd;
iobuf_read_8(&reader, &cmd);
if (cmd == VFD_CMD_GET_VERSION) {
hr = vfd_handle_get_version(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_RESET) {
hr = vfd_handle_reset(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_CLEAR_SCREEN) {
hr = vfd_handle_clear_screen(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_SET_BRIGHTNESS) {
hr = vfd_handle_set_brightness(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_SET_SCREEN_ON) {
hr = vfd_handle_set_screen_on(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_SET_H_SCROLL) {
hr = vfd_handle_set_h_scroll(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_DRAW_IMAGE) {
hr = vfd_handle_draw_image(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_SET_CURSOR) {
hr = vfd_handle_set_cursor(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_SET_ENCODING) {
hr = vfd_handle_set_encoding(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_SET_TEXT_WND) {
hr = vfd_handle_set_text_wnd(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_SET_TEXT_SPEED) {
hr = vfd_handle_set_text_speed(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_WRITE_TEXT) {
hr = vfd_handle_write_text(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_ENABLE_SCROLL) {
hr = vfd_handle_enable_scroll(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_DISABLE_SCROLL) {
hr = vfd_handle_disable_scroll(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_ROTATE) {
hr = vfd_handle_rotate(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_CREATE_CHAR) {
hr = vfd_handle_create_char(&reader, writer, &vfd_uart);
} else if (cmd == VFD_CMD_CREATE_CHAR2) {
hr = vfd_handle_create_char2(&reader, writer, &vfd_uart);
} else {
dprintf("VFD: Unknown command 0x%x\n", cmd);
dump_const_iobuf(&reader);
hr = S_FALSE;
}
} else {
// if no sync byte is sent, we are just getting plain text...
if (reader.pos < reader.nbytes){
int len = 0;
// read chars until we hit a new sync byte or the data ends
while (reader.pos + len + 1 < reader.nbytes && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE2){
len++;
}
char* str = malloc(len);
memset(str, 0, len);
iobuf_read(&reader, str, len);
print_vfd_text(str, len);
free(str);
reader.pos += len;
}
}
if (!SUCCEEDED(hr)){
return hr;
}
}
vfd_uart.written.pos = 0;
return hr;
}
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
dprintf("VFD: Get Version\n");
struct vfd_resp_board_info resp;
memset(&resp, 0, sizeof(resp));
resp.unk1 = 2;
strcpy(resp.version, "01.20");
resp.unk2 = 1;
return vfd_frame_encode(writer, &resp, sizeof(resp));
}
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
dprintf("VFD: Reset\n");
encoding = VFD_ENC_SHIFT_JIS;
return S_FALSE;
}
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
dprintf("VFD: Clear Screen\n");
return S_FALSE;
}
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint8_t b;
iobuf_read_8(reader, &b);
if (b > 4){
dprintf("VFD: Brightness, invalid argument\n");
return E_FAIL;
}
dprintf("VFD: Brightness, %d\n", b);
return S_FALSE;
}
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint8_t b;
iobuf_read_8(reader, &b);
if (b > 1){
dprintf("VFD: Screen Power, invalid argument\n");
return E_FAIL;
}
dprintf("VFD: Screen Power, %d\n", b);
return S_FALSE;
}
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint8_t x;
iobuf_read_8(reader, &x);
dprintf("VFD: Horizontal Scroll, X=%d\n", x);
return S_FALSE;
}
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
int w, h;
uint16_t x0, x1;
uint8_t y0, y1;
uint8_t image[2048];
iobuf_read_be16(reader, &x0);
iobuf_read_8(reader, &y0);
iobuf_read_be16(reader, &x1);
iobuf_read_8(reader, &y1);
w = x1 - x0;
h = y1 - y0;
iobuf_read(reader, image, w*h);
dprintf("VFD: Draw image, %dx%d\n", w, h);
return S_FALSE;
}
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint16_t x;
uint8_t y;
iobuf_read_be16(reader, &x);
iobuf_read_8(reader, &y);
dprintf("VFD: Set Cursor, x=%d,y=%d\n", x, y);
return S_FALSE;
}
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint8_t b;
iobuf_read_8(reader, &b);
dprintf("VFD: Set Encoding, %d (%s)\n", b, get_encoding_name(b));
if (b < 0 || b > VFD_ENC_MAX){
dprintf("Invalid encoding specified\n");
return E_FAIL;
}
encoding = b;
return S_FALSE;
}
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint16_t x0, x1;
uint8_t y0, y1;
iobuf_read_be16(reader, &x0);
iobuf_read_8(reader, &y0);
iobuf_read_be16(reader, &x1);
iobuf_read_8(reader, &y1);
dprintf("VFD: Set Text Window, p0:%d,%d, p1:%d,%d\n", x0, y0, x1, y1);
return S_FALSE;
}
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint8_t b;
iobuf_read_8(reader, &b);
dprintf("VFD: Set Text Speed, %d\n", b);
return S_FALSE;
}
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint8_t len;
iobuf_read_8(reader, &len);
char* str = malloc(len);
iobuf_read(reader, str, len);
print_vfd_text(str, len);
free(str);
return S_FALSE;
}
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
dprintf("VFD: Enable Scrolling\n");
return S_FALSE;
}
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
dprintf("VFD: Disable Scrolling\n");
return S_FALSE;
}
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint8_t b;
iobuf_read_8(reader, &b);
dprintf("VFD: Rotate, %d\n", b);
return S_FALSE;
}
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint8_t b;
iobuf_read_8(reader, &b);
char buf[32];
iobuf_read(reader, buf, 32);
dprintf("VFD: Create character, %d\n", b);
return S_FALSE;
}
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
uint8_t b, b2;
iobuf_read_8(reader, &b);
iobuf_read_8(reader, &b2);
char buf[16];
iobuf_read(reader, buf, 16);
dprintf("VFD: Create character, %d, %d\n", b, b2);
return S_FALSE;
}

13
common/board/vfd.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <windows.h>
struct vfd_config {
bool enable;
unsigned int port_no;
bool utf_conversion;
};
HRESULT vfd_hook_init(struct vfd_config *cfg, unsigned int default_port_no);

View File

@ -14,4 +14,5 @@ void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename)
cfg->windowed = GetPrivateProfileIntW(L"gfx", L"windowed", 0, filename); cfg->windowed = GetPrivateProfileIntW(L"gfx", L"windowed", 0, filename);
cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename); cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename);
cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename); cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename);
cfg->dpiAware = GetPrivateProfileIntW(L"gfx", L"dpiAware", 1, filename);
} }

View File

@ -224,9 +224,19 @@ static HRESULT STDMETHODCALLTYPE my_IDirect3D9_CreateDevice(
gfx_util_frame_window(hwnd); gfx_util_frame_window(hwnd);
} }
dprintf("Gfx: Using adapter %d\n", gfx_config.monitor); UINT max_adapter = IDirect3D9_GetAdapterCount(real);
adapter = gfx_config.monitor;
if (adapter >= max_adapter) {
dprintf(
"Gfx: Requested adapter %d but maximum is %d. Using primary monitor\n",
gfx_config.monitor, max_adapter - 1
);
adapter = D3DADAPTER_DEFAULT;
} else {
dprintf("Gfx: Using adapter %d\n", gfx_config.monitor);
}
return IDirect3D9_CreateDevice(real, gfx_config.monitor, type, hwnd, flags, pp, pdev); return IDirect3D9_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev);
} }
static HRESULT STDMETHODCALLTYPE my_IDirect3D9Ex_CreateDevice( static HRESULT STDMETHODCALLTYPE my_IDirect3D9Ex_CreateDevice(

147
common/gfxhook/gfx.c Normal file
View File

@ -0,0 +1,147 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "gfxhook/gfx.h"
#include "hook/table.h"
#include "util/dprintf.h"
/* Hook functions */
static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow);
static HWND WINAPI hook_CreateWindowExA(
DWORD dwExStyle,
LPCSTR lpClassName,
LPCSTR lpWindowName,
DWORD dwStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
/* Link pointers */
static BOOL (WINAPI *next_ShowWindow)(HWND hWnd, int nCmdShow);
static HWND (WINAPI *next_CreateWindowExA)(
DWORD dwExStyle,
LPCSTR lpClassName,
LPCSTR lpWindowName,
DWORD dwStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
static struct gfx_config gfx_config;
static const struct hook_symbol gfx_hooks[] = {
{
.name = "ShowWindow",
.patch = hook_ShowWindow,
.link = (void **) &next_ShowWindow,
}, {
.name = "CreateWindowExA",
.patch = hook_CreateWindowExA,
.link = (void **) &next_CreateWindowExA,
},
};
void gfx_hook_init(const struct gfx_config *cfg)
{
assert(cfg != NULL);
if (!cfg->enable) {
return;
}
if (cfg->dpiAware) {
if (SetProcessDPIAware()) {
dprintf("Gfx: Game process set to DPI aware.\n");
} else {
dprintf("Gfx: Failed to set process DPI aware\n");
}
}
memcpy(&gfx_config, cfg, sizeof(*cfg));
hook_table_apply(NULL, "user32.dll", gfx_hooks, _countof(gfx_hooks));
}
static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow)
{
dprintf("Gfx: ShowWindow hook hit\n");
if (!gfx_config.framed && nCmdShow == SW_RESTORE) {
nCmdShow = SW_SHOW;
}
return next_ShowWindow(hWnd, nCmdShow);
}
static HWND WINAPI hook_CreateWindowExA(
DWORD dwExStyle,
LPCSTR lpClassName,
LPCSTR lpWindowName,
DWORD dwStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
)
{
RECT rect;
dprintf("Gfx: CreateWindowExA hook hit\n");
if (gfx_config.windowed)
{
if (gfx_config.framed)
dwStyle |= WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
else
dwStyle = WS_POPUP;
rect.left = ((X == CW_USEDEFAULT) ? 0 : X);
rect.top = ((Y == CW_USEDEFAULT) ? 0 : Y);
rect.right = rect.left + nWidth;
rect.bottom = rect.top + nHeight;
// Don't care if it's ok or not, since we are creating window and we can't just return a NULL
AdjustWindowRect(&rect, dwStyle, !!hMenu);
X = ((X == CW_USEDEFAULT) ? X : rect.left);
Y = ((Y == CW_USEDEFAULT) ? Y : rect.top);
nWidth = rect.right - rect.left;
nHeight = rect.bottom - rect.top;
}
return next_CreateWindowExA(
dwExStyle,
lpClassName,
lpWindowName,
dwStyle,
X,
Y,
nWidth,
nHeight,
hWndParent,
hMenu,
hInstance,
lpParam
);
}

View File

@ -7,6 +7,7 @@ struct gfx_config {
bool windowed; bool windowed;
bool framed; bool framed;
int monitor; int monitor;
bool dpiAware;
}; };
void gfx_hook_init(const struct gfx_config *cfg); void gfx_hook_init(const struct gfx_config *cfg);

77
common/gfxhook/gl.c Normal file
View File

@ -0,0 +1,77 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "gfxhook/gfx.h"
#include "gfxhook/gl.h"
#include "hook/table.h"
#include "hooklib/dll.h"
#include "util/dprintf.h"
/* Hook functions */
static void WINAPI hook_glutFullScreen(void);
static void WINAPI hook_glutInitDisplayMode(unsigned int mode);
/* Link pointers */
static void (WINAPI *next_glutFullScreen)(void);
static void (WINAPI *next_glutInitDisplayMode)(unsigned int mode);
static struct gfx_config gfx_config;
static const struct hook_symbol glut_hooks[] = {
{
.name = "glutFullScreen",
.patch = hook_glutFullScreen,
.link = (void **) &next_glutFullScreen,
}, {
.name = "glutInitDisplayMode",
.patch = hook_glutInitDisplayMode,
.link = (void **) &next_glutInitDisplayMode,
},
};
void gfx_gl_hook_init(const struct gfx_config *cfg, HINSTANCE self)
{
assert(cfg != NULL);
if (!cfg->enable) {
return;
}
memcpy(&gfx_config, cfg, sizeof(*cfg));
hook_table_apply(NULL, "glut32.dll", glut_hooks, _countof(glut_hooks));
if (self != NULL) {
dll_hook_push(self, L"glut32.dll");
}
}
static void WINAPI hook_glutFullScreen(void)
{
dprintf("Gfx: glutFullScreen hook hit\n");
if (gfx_config.windowed) {
return;
}
next_glutFullScreen();
}
static void WINAPI hook_glutInitDisplayMode(unsigned int mode)
{
dprintf("Gfx: glutInitDisplayMode hook hit\n");
// GLUT adds a frame when going windowed
if (gfx_config.windowed && !gfx_config.framed) {
// GLUT_BORDERLESS
mode |= 0x0800;
}
next_glutInitDisplayMode(mode);
}

3
common/gfxhook/gl.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void gfx_gl_hook_init(const struct gfx_config *cfg, HINSTANCE self);

View File

@ -2,7 +2,6 @@ gfxhook_lib = static_library(
'gfxhook', 'gfxhook',
include_directories : inc, include_directories : inc,
implicit_include_directories : false, implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [ dependencies : [
capnhook.get_variable('hook_dep'), capnhook.get_variable('hook_dep'),
dxguid_lib, dxguid_lib,
@ -22,6 +21,8 @@ gfxhook_lib = static_library(
'dxgi.h', 'dxgi.h',
'gfx.c', 'gfx.c',
'gfx.h', 'gfx.h',
'gl.c',
'gl.h',
'util.c', 'util.c',
'util.h', 'util.h',
], ],

86
common/hooklib/config.c Normal file
View File

@ -0,0 +1,86 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stddef.h>
#include "hooklib/config.h"
#include "hooklib/dvd.h"
void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"dvd", L"enable", 1, filename);
}
void touch_screen_config_load(struct touch_screen_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"touch", L"enable", 1, filename);
cfg->remap = GetPrivateProfileIntW(L"touch", L"remap", 1, filename);
cfg->cursor = GetPrivateProfileIntW(L"touch", L"cursor", 1, filename);
}
void printer_config_load(struct printer_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
wchar_t tmpstr[16];
cfg->enable = GetPrivateProfileIntW(L"printer", L"enable", 0, filename);
cfg->rotate_180 = GetPrivateProfileIntW(L"printer", L"rotate180", 0, filename);
GetPrivateProfileStringW(
L"printer",
L"serial_no",
L"5A-A123",
tmpstr,
_countof(tmpstr),
filename);
size_t n = wcstombs(cfg->serial_no, tmpstr, sizeof(cfg->serial_no));
for (int i = n; i < sizeof(cfg->serial_no); i++)
{
cfg->serial_no[i] = '\0';
}
GetPrivateProfileStringW(
L"printer",
L"mainFwPath",
L"DEVICE\\printer_main_fw.bin",
cfg->main_fw_path,
_countof(cfg->main_fw_path),
filename);
GetPrivateProfileStringW(
L"printer",
L"dspFwPath",
L"DEVICE\\printer_dsp_fw.bin",
cfg->dsp_fw_path,
_countof(cfg->dsp_fw_path),
filename);
GetPrivateProfileStringW(
L"printer",
L"paramFwPath",
L"DEVICE\\printer_param_fw.bin",
cfg->param_fw_path,
_countof(cfg->param_fw_path),
filename);
GetPrivateProfileStringW(
L"printer",
L"printerOutPath",
L"DEVICE\\print",
cfg->printer_out_path,
_countof(cfg->printer_out_path),
filename);
cfg->wait_time = GetPrivateProfileIntW(L"printer", L"waitTime", 0, filename);
}

11
common/hooklib/config.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <stddef.h>
#include "hooklib/dvd.h"
#include "hooklib/touch.h"
#include "hooklib/printer.h"
void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename);
void touch_screen_config_load(struct touch_screen_config *cfg, const wchar_t *filename);
void printer_config_load(struct printer_config *cfg, const wchar_t *filename);

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