├── .dockerignore ├── .editorconfig ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── Dockerfile ├── Makefile ├── Package.mk ├── README.md ├── aimeio ├── aimeio.c ├── aimeio.h └── meson.build ├── amex ├── amex.c ├── amex.h ├── config.c ├── config.h ├── ds.c ├── ds.h ├── eeprom.c ├── eeprom.h ├── gpio.c ├── gpio.h ├── guid.c ├── jvs.c ├── jvs.h ├── meson.build ├── nvram.c ├── nvram.h ├── sram.c └── sram.h ├── board ├── aime-dll.c ├── aime-dll.h ├── config.c ├── config.h ├── guid.c ├── guid.h ├── io3.c ├── io3.h ├── io4.c ├── io4.h ├── meson.build ├── sg-cmd.c ├── sg-cmd.h ├── sg-frame.c ├── sg-frame.h ├── sg-led-cmd.h ├── sg-led.c ├── sg-led.h ├── sg-nfc-cmd.h ├── sg-nfc.c ├── sg-nfc.h ├── sg-reader.c ├── sg-reader.h ├── slider-cmd.h ├── slider-frame.c ├── slider-frame.h ├── vfd.c └── vfd.h ├── chronohook ├── chronohook.def ├── config.c ├── config.h ├── dllmain.c └── meson.build ├── chunihook ├── chuni-dll.c ├── chuni-dll.h ├── chunihook.def ├── config.c ├── config.h ├── dllmain.c ├── jvs.c ├── jvs.h ├── meson.build ├── slider.c └── slider.h ├── chuniio ├── chuniio.c ├── chuniio.h ├── config.c ├── config.h └── meson.build ├── cross-mingw-32.txt ├── cross-mingw-64.txt ├── dist ├── chuni │ ├── segatools.ini │ └── start.bat └── idz │ ├── segatools.ini │ └── start.bat ├── divahook ├── config.c ├── config.h ├── diva-dll.c ├── diva-dll.h ├── divahook.def ├── dllmain.c ├── jvs.c ├── jvs.h ├── meson.build ├── slider.c └── slider.h ├── divaio ├── config.c ├── config.h ├── divaio.c ├── divaio.h └── meson.build ├── doc ├── chunihook.md ├── config │ ├── chunithm.md │ ├── common.md │ └── initiald.md ├── idzhook.md └── nfc.txt ├── docker-build.bat ├── dummy.txt ├── fatehook ├── MinHook.h ├── config.c ├── config.h ├── dllmain.c ├── fatehook.def └── meson.build ├── fateio ├── fateio.c ├── fateio.def ├── fateio.h └── meson.build ├── hooklib ├── config.c ├── config.h ├── dll.c ├── dll.h ├── dns.c ├── dns.h ├── dvd.c ├── dvd.h ├── fdshark.c ├── fdshark.h ├── gfx.c ├── gfx.h ├── meson.build ├── path.c ├── path.h ├── reg.c ├── reg.h ├── setupapi.c ├── setupapi.h ├── spike.c └── spike.h ├── iccard ├── aime.c ├── aime.h ├── felica.c ├── felica.h ├── meson.build └── mifare.h ├── idzhook ├── config.c ├── config.h ├── dllmain.c ├── idz-dll.c ├── idz-dll.h ├── idzhook.def ├── jvs.c ├── jvs.h ├── meson.build ├── zinput.c └── zinput.h ├── idzio ├── backend.h ├── config.c ├── config.h ├── di-dev.c ├── di-dev.h ├── di.c ├── di.h ├── dllmain.c ├── idzio.def ├── idzio.h ├── meson.build ├── shifter.c ├── shifter.h ├── wnd.c ├── wnd.h ├── xi.c └── xi.h ├── initpki ├── jvs ├── jvs-bus.c ├── jvs-bus.h ├── jvs-cmd.h ├── jvs-frame.c ├── jvs-frame.h ├── jvs-util.c ├── jvs-util.h └── meson.build ├── meson.build ├── minihook ├── dllmain.c └── meson.build ├── mu3hook ├── config.c ├── config.h ├── dllmain.c ├── io4.c ├── io4.h ├── meson.build ├── mu3-dll.c ├── mu3-dll.h ├── mu3hook.def ├── unity.c └── unity.h ├── mu3io ├── meson.build ├── mu3io.c └── mu3io.h ├── pki ├── billing.key ├── billing.pub ├── ca.crt ├── ca.key ├── ca.pem ├── server.key └── server.pem ├── platform ├── amvideo.c ├── amvideo.h ├── clock.c ├── clock.h ├── config.c ├── config.h ├── dns.c ├── dns.h ├── hwmon.c ├── hwmon.h ├── meson.build ├── misc.c ├── misc.h ├── netenv.c ├── netenv.h ├── nusec.c ├── nusec.h ├── pcbid.c ├── pcbid.h ├── platform.c ├── platform.h ├── vfs.c └── vfs.h ├── precompiled.h ├── reg └── chunithm.reg ├── spike └── chunithm-1.25.00.txt ├── subprojects └── capnhook.wrap └── util ├── async.c ├── async.h ├── crc.c ├── crc.h ├── dll-bind.c ├── dll-bind.h ├── dprintf.c ├── dprintf.h ├── dump.c ├── dump.h ├── meson.build ├── str.c └── str.h /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | 4 | build/ -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{c,h}] 12 | indent_style = space 13 | indent_size = 4 14 | max_line_length = 79 15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | 3 | .vscode/ 4 | 5 | # Suggested names for build dirs 6 | build/ 7 | 8 | # External dependencies 9 | subprojects/capnhook 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": false 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v004 2 | 3 | * Add initial support for mounting DLC package dumps (contributed by Shiz) 4 | * Fix configuration loading in aimeio.dll 5 | * Build system fixes (contributed by Shiz) 6 | 7 | # v003 8 | 9 | * Add countermeasures for DNS-spamming ISPs (contributed by mon) 10 | * Add option for single-stick steering in IDZ (contributed by BemaniWitch) 11 | * Fix MSVC build (contributed by mariodon) 12 | * Make all 32 Chunithm touch slider cells' keyboard bindings configurable (see 13 | INI) 14 | 15 | # v002 16 | 17 | * Ship correct inject.exe for IDZ 18 | * Fix IDZ main EXE crash in GetIfTable() hook in platform/netenv.c 19 | 20 | # v001 21 | 22 | * Initial release 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fedora:31 2 | 3 | LABEL description="Build environment for segatools" 4 | 5 | RUN yum -y install meson 6 | RUN yum -y install ninja-build 7 | RUN yum -y install make 8 | RUN yum -y install zip 9 | RUN yum -y install clang 10 | RUN yum -y install mingw64-gcc.x86_64 11 | RUN yum -y install mingw32-gcc.x86_64 12 | RUN yum -y install git 13 | 14 | RUN mkdir /segatools 15 | WORKDIR /segatools 16 | 17 | COPY aimeio aimeio 18 | COPY amex amex 19 | COPY board board 20 | COPY chunihook chunihook 21 | COPY chuniio chuniio 22 | COPY dist dist 23 | COPY divahook divahook 24 | COPY divaio divaio 25 | COPY doc doc 26 | COPY hooklib hooklib 27 | COPY iccard iccard 28 | COPY idzhook idzhook 29 | COPY idzio idzio 30 | COPY jvs jvs 31 | COPY minihook minihook 32 | COPY mu3hook mu3hook 33 | COPY mu3io mu3io 34 | COPY pki pki 35 | COPY platform platform 36 | COPY reg reg 37 | COPY spike spike 38 | COPY subprojects subprojects 39 | COPY util util 40 | COPY CHANGELOG.md CHANGELOG.md 41 | COPY cross-mingw-32.txt cross-mingw-32.txt 42 | COPY cross-mingw-64.txt cross-mingw-64.txt 43 | COPY Makefile Makefile 44 | COPY meson.build meson.build 45 | COPY Package.mk Package.mk 46 | COPY precompiled.h precompiled.h 47 | COPY README.md README.md 48 | 49 | RUN make dist -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | V ?= @ 2 | 3 | .DEFAULT_GOAL := help 4 | 5 | BUILD_DIR := build 6 | BUILD_DIR_32 := $(BUILD_DIR)/build32 7 | BUILD_DIR_64 := $(BUILD_DIR)/build64 8 | BUILD_DIR_DOCKER := $(BUILD_DIR)/docker 9 | BUILD_DIR_ZIP := $(BUILD_DIR)/zip 10 | 11 | DOC_DIR := doc 12 | 13 | DIST_DIR := dist 14 | 15 | DOCKER_CONTAINER_NAME := "segatools-build" 16 | DOCKER_IMAGE_NAME := "segatools:build" 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Targets 20 | # ----------------------------------------------------------------------------- 21 | 22 | include Package.mk 23 | 24 | .PHONY: build # Build the project 25 | build: 26 | $(V)meson --cross cross-mingw-32.txt $(BUILD_DIR_32) 27 | $(V)ninja -C $(BUILD_DIR_32) 28 | $(V)meson --cross cross-mingw-64.txt $(BUILD_DIR_64) 29 | $(V)ninja -C $(BUILD_DIR_64) 30 | 31 | .PHONY: dist # Build and create a zip distribution package 32 | dist: build zip 33 | 34 | .PHONY: zip # Create a zip distribution pacakge 35 | zip: $(BUILD_DIR_ZIP)/segatools.zip 36 | 37 | .PHONY: clean # Cleanup build output 38 | clean: 39 | $(V)rm -rf $(BUILD_DIR) subprojects/capnhook 40 | 41 | .PHONY: build-docker # Build the project in a docker container 42 | build-docker: 43 | $(V)docker rm -f $(DOCKER_CONTAINER_NAME) 2> /dev/null || true 44 | $(V)docker build -t $(DOCKER_IMAGE_NAME) -f Dockerfile . 45 | $(V)docker create --name $(DOCKER_CONTAINER_NAME) $(DOCKER_IMAGE_NAME) 46 | $(V)rm -rf $(BUILD_DIR_DOCKER) 47 | $(V)mkdir -p $(BUILD_DIR_DOCKER) 48 | $(V)docker cp $(DOCKER_CONTAINER_NAME):/segatools/$(BUILD_DIR_ZIP) $(BUILD_DIR_DOCKER) 49 | 50 | # ----------------------------------------------------------------------------- 51 | # Utility, combo and alias targets 52 | # ----------------------------------------------------------------------------- 53 | 54 | # Help screen note: 55 | # Variables that need to be displayed in the help screen need to strictly 56 | # follow the pattern "^[A-Z_]+ \?= .* # .*". 57 | # Targets that need to be displayed in the help screen need to add a separate 58 | # phony definition strictly following the pattern "^\.PHONY\: .* # .*". 59 | 60 | .PHONY: help # Print help screen 61 | help: 62 | $(V)echo segatools makefile. 63 | $(V)echo 64 | $(V)echo "Environment variables:" 65 | $(V)grep -E '^[A-Z_]+ \?\= .* #' Makefile | gawk 'match($$0, /([A-Z_]+) \?= [$$\(]*([^\)]*)[\)]{0,1} # (.*)/, a) { printf(" \033[0;35m%-25s \033[0;0m%-45s [%s]\n", a[1], a[3], a[2]) }' 66 | $(V)echo "" 67 | $(V)echo "Targets:" 68 | $(V)grep '^.PHONY: .* #' Makefile | gawk 'match($$0, /\.PHONY: (.*) # (.*)/, a) { printf(" \033[0;32m%-25s \033[0;0m%s\n", a[1], a[2]) }' 69 | -------------------------------------------------------------------------------- /Package.mk: -------------------------------------------------------------------------------- 1 | $(BUILD_DIR_ZIP)/chuni.zip: 2 | $(V)echo ... $@ 3 | $(V)mkdir -p $(BUILD_DIR_ZIP)/chuni 4 | $(V)mkdir -p $(BUILD_DIR_ZIP)/chuni/DEVICE 5 | $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ 6 | $(BUILD_DIR_32)/chunihook/chunihook.dll \ 7 | $(DIST_DIR)/chuni/segatools.ini \ 8 | $(DIST_DIR)/chuni/start.bat \ 9 | $(BUILD_DIR_ZIP)/chuni 10 | $(V)cp pki/billing.pub \ 11 | pki/ca.crt \ 12 | $(BUILD_DIR_ZIP)/chuni/DEVICE 13 | $(V)strip $(BUILD_DIR_ZIP)/chuni/*.{exe,dll} 14 | $(V)cd $(BUILD_DIR_ZIP)/chuni ; zip -r ../chuni.zip * 15 | 16 | $(BUILD_DIR_ZIP)/idz.zip: 17 | $(V)echo ... $@ 18 | $(V)mkdir -p $(BUILD_DIR_ZIP)/idz 19 | $(V)mkdir -p $(BUILD_DIR_ZIP)/idz/DEVICE 20 | $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ 21 | $(BUILD_DIR_64)/idzhook/idzhook.dll \ 22 | $(DIST_DIR)/idz/segatools.ini \ 23 | $(DIST_DIR)/idz/start.bat \ 24 | $(BUILD_DIR_ZIP)/idz 25 | $(V)cp pki/billing.pub \ 26 | pki/ca.crt \ 27 | $(BUILD_DIR_ZIP)/idz/DEVICE 28 | $(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll} 29 | $(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip * 30 | 31 | $(BUILD_DIR_ZIP)/doc.zip: \ 32 | $(DOC_DIR)/config \ 33 | $(DOC_DIR)/chunihook.md \ 34 | $(DOC_DIR)/idzhook.md \ 35 | | $(zipdir)/ 36 | $(V)echo ... $@ 37 | $(V)zip -r $@ $^ 38 | 39 | $(BUILD_DIR_ZIP)/segatools.zip: \ 40 | $(BUILD_DIR_ZIP)/chuni.zip \ 41 | $(BUILD_DIR_ZIP)/doc.zip \ 42 | $(BUILD_DIR_ZIP)/idz.zip \ 43 | CHANGELOG.md \ 44 | README.md \ 45 | 46 | $(V)echo ... $@ 47 | $(V)zip -j $@ $^ 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Segatools 2 | 3 | Version: `v005` 4 | 5 | Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms. 6 | 7 | ## List of supported games 8 | 9 | * Chunithm 10 | * [Chunithm (Plus)](doc/chunihook.md) 11 | * [Chunithm Air (Plus)](doc/chunihook.md) 12 | * [Chunithm Star (Plus)](doc/chunihook.md) 13 | * [Chunithm Amazon (Plus)](doc/chunihook.md) 14 | * [Chunithm Crystal (Plus)](doc/chunihook.md) 15 | * Initial D 16 | * [Initial D Arcade Stage Zero](doc/idzhook.md) 17 | 18 | ## End-users 19 | 20 | For setup and configuration guides, refer to the dedicated documents available for each game, see 21 | [the links in the previous section](#list-of-supported-games). 22 | 23 | ## Developers 24 | 25 | ### Building 26 | 27 | The root `Makefile` contains various targets that allow you to build the project easily. 28 | 29 | #### Local build 30 | 31 | For a local build, you need to install Meson and a recent build of MinGW-w64. Then you can start the 32 | build process: 33 | 34 | ```shell 35 | make build 36 | ``` 37 | 38 | Build output will be located in `build/build32` and `build/build64` folders. 39 | 40 | #### Cleanup local build 41 | 42 | ```shell 43 | make clean 44 | ``` 45 | 46 | #### Create distribution package (zip file) 47 | 48 | ```shell 49 | make dist 50 | ``` 51 | 52 | The output will be located in `build/zip`. 53 | 54 | #### Build and create distribution package using docker 55 | 56 | You can also build using docker which avoids having to setup a full development environment if you 57 | are just interested in building binaries of the latest changes. Naturally, this requires you to 58 | have the docker daemon installed. 59 | 60 | ```shell 61 | make build-docker 62 | ``` 63 | 64 | Once completed successfully, the build output is located in the `build/docker/zip` sub-folder. 65 | 66 | #### Building with Docker Desktop on Windows 67 | 68 | * [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) 69 | * [Install Docker Desktop](https://docs.docker.com/docker-for-windows/install/) 70 | * Run Docker Desktop to start the Docker Engine 71 | * Open a command prompt (`cmd.exe`) and `cd` to your `segatools` folder 72 | * Run `docker-build.bat` 73 | * Once completed successfully, build output is located in the `build/docker/zip` sub-folder. 74 | 75 | #### Building with Docker on Windows using WSL2 76 | 77 | * [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) 78 | * Regarding Linux distribution, we recommend using Ubuntu 20.04 79 | * Run the "Ubuntu 20.04 LTS" App which opens a Linux shell 80 | * Install `make` and `docker` by running 81 | * `sudo apt-get update` 82 | * `sudo apt-get install make docker.io` 83 | * Add the current user to the docker group that you don't have to run docker commands with root: 84 | `sudo usermod -a -G docker $USER` 85 | * Run the docker daemon in the background: `sudo dockerd > /dev/null 2>&1 &` 86 | * Navigate to your segatools folder. If it is located on the `C:` drive, WSL automatically provides 87 | a mountpoint for that under `/mnt/c`, e.g. `cd /mnt/c/segatools` (if the folder `segatools` is 88 | located under `C:\segatools` on Windows). 89 | * Build segatools: `make build-docker` 90 | * Once completed successfully, build output is located in the `build/docker/zip` sub-folder. 91 | -------------------------------------------------------------------------------- /aimeio/aimeio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | /* 9 | Get the version of the Aime IO API that this DLL supports. This function 10 | should return a positive 16-bit integer, where the high byte is the major 11 | version and the low byte is the minor version (as defined by the Semantic 12 | Versioning standard). 13 | 14 | The latest API version as of this writing is 0x0100. 15 | */ 16 | uint16_t aime_io_get_api_version(void); 17 | 18 | /* 19 | Initialize Aime IO provider DLL. Only called once, before any other 20 | functions exported from this DLL are called (except for 21 | aime_io_get_api_version). 22 | 23 | Minimum API version: 0x0100 24 | */ 25 | HRESULT aime_io_init(void); 26 | 27 | /* 28 | Poll for IC cards in the vicinity. 29 | 30 | - unit_no: Always 0 as of the current API version 31 | 32 | Minimum API version: 0x0100 33 | */ 34 | HRESULT aime_io_nfc_poll(uint8_t unit_no); 35 | 36 | /* 37 | Attempt to read out a classic Aime card ID 38 | 39 | - unit_no: Always 0 as of the current API version 40 | - luid: Pointer to a ten-byte buffer that will receive the ID 41 | - luid_size: Size of the buffer at *luid. Always 10. 42 | 43 | Returns: 44 | 45 | - S_OK if a classic Aime is present and was read successfully 46 | - S_FALSE if no classic Aime card is present (*luid will be ignored) 47 | - Any HRESULT error if an error occured. 48 | 49 | Minimum API version: 0x0100 50 | */ 51 | HRESULT aime_io_nfc_get_aime_id( 52 | uint8_t unit_no, 53 | uint8_t *luid, 54 | size_t luid_size); 55 | 56 | /* 57 | Attempt to read out a FeliCa card ID ("IDm"). The following are examples 58 | of FeliCa cards: 59 | 60 | - Amuse IC (which includes new-style Aime-branded cards, among others) 61 | - Smartphones with FeliCa NFC capability (uncommon outside Japan) 62 | - Various Japanese e-cash cards and train passes 63 | 64 | Parameters: 65 | 66 | - unit_no: Always 0 as of the current API version 67 | - IDm: Output parameter that will receive the card ID 68 | 69 | Returns: 70 | 71 | - S_OK if a FeliCa device is present and was read successfully 72 | - S_FALSE if no FeliCa device is present (*IDm will be ignored) 73 | - Any HRESULT error if an error occured. 74 | 75 | Minimum API version: 0x0100 76 | */ 77 | HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm); 78 | 79 | /* 80 | Change the color and brightness of the card reader's RGB lighting 81 | 82 | - unit_no: Always 0 as of the current API version 83 | - r, g, b: Primary color intensity, from 0 to 255 inclusive. 84 | 85 | Minimum API version: 0x0100 86 | */ 87 | void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b); 88 | -------------------------------------------------------------------------------- /aimeio/meson.build: -------------------------------------------------------------------------------- 1 | aimeio_lib = static_library( 2 | 'aimeio', 3 | name_prefix : '', 4 | include_directories: inc, 5 | implicit_include_directories : false, 6 | c_pch : '../precompiled.h', 7 | link_with : [ 8 | util_lib, 9 | ], 10 | sources : [ 11 | 'aimeio.c', 12 | ], 13 | ) 14 | -------------------------------------------------------------------------------- /amex/amex.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "amex/amex.h" 4 | #include "amex/ds.h" 5 | #include "amex/eeprom.h" 6 | #include "amex/gpio.h" 7 | #include "amex/jvs.h" 8 | #include "amex/sram.h" 9 | 10 | #include 11 | 12 | HRESULT amex_hook_init(const struct amex_config *cfg, jvs_provider_t jvs) 13 | { 14 | HRESULT hr; 15 | 16 | assert(cfg != NULL); 17 | 18 | hr = ds_hook_init(&cfg->ds); 19 | 20 | if (FAILED(hr)) { 21 | return hr; 22 | } 23 | 24 | hr = eeprom_hook_init(&cfg->eeprom); 25 | 26 | if (FAILED(hr)) { 27 | return hr; 28 | } 29 | 30 | hr = gpio_hook_init(&cfg->gpio); 31 | 32 | if (FAILED(hr)) { 33 | return hr; 34 | } 35 | 36 | hr = jvs_hook_init(&cfg->jvs, jvs); 37 | 38 | if (FAILED(hr)) { 39 | return hr; 40 | } 41 | 42 | hr = sram_hook_init(&cfg->sram); 43 | 44 | if (FAILED(hr)) { 45 | return hr; 46 | } 47 | 48 | return S_OK; 49 | } 50 | -------------------------------------------------------------------------------- /amex/amex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "amex/ds.h" 6 | #include "amex/eeprom.h" 7 | #include "amex/gpio.h" 8 | #include "amex/jvs.h" 9 | #include "amex/sram.h" 10 | 11 | struct amex_config { 12 | struct ds_config ds; 13 | struct eeprom_config eeprom; 14 | struct gpio_config gpio; 15 | struct jvs_config jvs; 16 | struct sram_config sram; 17 | }; 18 | 19 | HRESULT amex_hook_init( 20 | const struct amex_config *cfg, 21 | jvs_provider_t jvs); 22 | -------------------------------------------------------------------------------- /amex/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "amex/amex.h" 10 | #include "amex/config.h" 11 | #include "amex/ds.h" 12 | #include "amex/eeprom.h" 13 | #include "amex/gpio.h" 14 | #include "amex/jvs.h" 15 | #include "amex/sram.h" 16 | 17 | void ds_config_load(struct ds_config *cfg, const wchar_t *filename) 18 | { 19 | assert(cfg != NULL); 20 | assert(filename != NULL); 21 | 22 | cfg->enable = GetPrivateProfileIntW(L"ds", L"enable", 1, filename); 23 | cfg->region = GetPrivateProfileIntW(L"ds", L"region", 1, filename); 24 | 25 | GetPrivateProfileStringW( 26 | L"ds", 27 | L"serialNo", 28 | L"AAVE-01A99999999", 29 | cfg->serial_no, 30 | _countof(cfg->serial_no), 31 | filename); 32 | } 33 | 34 | void eeprom_config_load(struct eeprom_config *cfg, const wchar_t *filename) 35 | { 36 | assert(cfg != NULL); 37 | assert(filename != NULL); 38 | 39 | cfg->enable = GetPrivateProfileIntW(L"eeprom", L"enable", 1, filename); 40 | 41 | GetPrivateProfileStringW( 42 | L"eeprom", 43 | L"path", 44 | L"DEVICE\\eeprom.bin", 45 | cfg->path, 46 | _countof(cfg->path), 47 | filename); 48 | } 49 | 50 | void gpio_config_load(struct gpio_config *cfg, const wchar_t *filename) 51 | { 52 | wchar_t name[7]; 53 | size_t i; 54 | 55 | assert(cfg != NULL); 56 | assert(filename != NULL); 57 | 58 | cfg->enable = GetPrivateProfileIntW(L"gpio", L"enable", 1, filename); 59 | cfg->vk_sw1 = GetPrivateProfileIntW(L"gpio", L"sw1", VK_F1, filename); 60 | cfg->vk_sw2 = GetPrivateProfileIntW(L"gpio", L"sw2", VK_F2, filename); 61 | 62 | wcscpy_s(name, _countof(name), L"dipsw0"); 63 | 64 | for (i = 0 ; i < 8 ; i++) { 65 | name[5] = L'1' + i; 66 | cfg->dipsw[i] = GetPrivateProfileIntW(L"gpio", name, 0, filename); 67 | } 68 | } 69 | 70 | void jvs_config_load(struct jvs_config *cfg, const wchar_t *filename) 71 | { 72 | assert(cfg != NULL); 73 | assert(filename != NULL); 74 | 75 | cfg->enable = GetPrivateProfileIntW(L"jvs", L"enable", 1, filename); 76 | } 77 | 78 | void sram_config_load(struct sram_config *cfg, const wchar_t *filename) 79 | { 80 | assert(cfg != NULL); 81 | assert(filename != NULL); 82 | 83 | cfg->enable = GetPrivateProfileIntW(L"sram", L"enable", 1, filename); 84 | 85 | GetPrivateProfileStringW( 86 | L"sram", 87 | L"path", 88 | L"DEVICE\\sram.bin", 89 | cfg->path, 90 | _countof(cfg->path), 91 | filename); 92 | } 93 | 94 | void amex_config_load(struct amex_config *cfg, const wchar_t *filename) 95 | { 96 | assert(cfg != NULL); 97 | assert(filename != NULL); 98 | 99 | ds_config_load(&cfg->ds, filename); 100 | eeprom_config_load(&cfg->eeprom, filename); 101 | gpio_config_load(&cfg->gpio, filename); 102 | jvs_config_load(&cfg->jvs, filename); 103 | sram_config_load(&cfg->sram, filename); 104 | } 105 | -------------------------------------------------------------------------------- /amex/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "amex/amex.h" 10 | #include "amex/ds.h" 11 | #include "amex/eeprom.h" 12 | #include "amex/gpio.h" 13 | #include "amex/jvs.h" 14 | #include "amex/sram.h" 15 | 16 | void ds_config_load(struct ds_config *cfg, const wchar_t *filename); 17 | void eeprom_config_load(struct eeprom_config *cfg, const wchar_t *filename); 18 | void gpio_config_load(struct gpio_config *cfg, const wchar_t *filename); 19 | void jvs_config_load(struct jvs_config *cfg, const wchar_t *filename); 20 | void sram_config_load(struct sram_config *cfg, const wchar_t *filename); 21 | void amex_config_load(struct amex_config *cfg, const wchar_t *filename); 22 | -------------------------------------------------------------------------------- /amex/ds.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | struct ds_config { 10 | bool enable; 11 | uint8_t region; 12 | wchar_t serial_no[17]; 13 | }; 14 | 15 | DEFINE_GUID( 16 | ds_guid, 17 | 0x279A9F67, 18 | 0x348F, 19 | 0x41C9, 20 | 0xA4, 0xC4, 0xDF, 0xDB, 0x8A, 0xE8, 0xE5, 0xE0); 21 | 22 | HRESULT ds_hook_init(const struct ds_config *cfg); 23 | -------------------------------------------------------------------------------- /amex/eeprom.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct eeprom_config { 9 | bool enable; 10 | wchar_t path[MAX_PATH]; 11 | }; 12 | 13 | DEFINE_GUID( 14 | eeprom_guid, 15 | 0xB7970F0C, 16 | 0x31C4, 17 | 0x45FF, 18 | 0x96, 0x18, 0x0A, 0x24, 0x00, 0x94, 0xB2, 0x71); 19 | 20 | HRESULT eeprom_hook_init(const struct eeprom_config *cfg); 21 | -------------------------------------------------------------------------------- /amex/gpio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct gpio_config { 9 | bool enable; 10 | uint8_t vk_sw1; 11 | uint8_t vk_sw2; 12 | bool dipsw[8]; 13 | }; 14 | 15 | DEFINE_GUID( 16 | gpio_guid, 17 | 0xE9A26688, 18 | 0xF522, 19 | 0x44FA, 20 | 0xBF, 0xEE, 0x59, 0xDD, 0x16, 0x15, 0x56, 0x6C); 21 | 22 | HRESULT gpio_hook_init(const struct gpio_config *cfg); 23 | -------------------------------------------------------------------------------- /amex/guid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "amex/ds.h" 5 | #include "amex/eeprom.h" 6 | #include "amex/gpio.h" 7 | #include "amex/jvs.h" 8 | #include "amex/sram.h" 9 | -------------------------------------------------------------------------------- /amex/jvs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "jvs/jvs-bus.h" 8 | 9 | DEFINE_GUID( 10 | jvs_guid, 11 | 0xDB6BBB45, 12 | 0xCC96, 13 | 0x4288, 14 | 0xAA, 0x00, 0x6C, 0x00, 0xD7, 0x67, 0xBD, 0xBF); 15 | 16 | struct jvs_config { 17 | bool enable; 18 | }; 19 | 20 | typedef HRESULT (*jvs_provider_t)(struct jvs_node **root); 21 | 22 | HRESULT jvs_hook_init(const struct jvs_config *cfg, jvs_provider_t provider); 23 | -------------------------------------------------------------------------------- /amex/meson.build: -------------------------------------------------------------------------------- 1 | amex_lib = static_library( 2 | 'amex', 3 | include_directories : inc, 4 | implicit_include_directories : false, 5 | c_pch : '../precompiled.h', 6 | dependencies : [ 7 | capnhook.get_variable('hook_dep'), 8 | ], 9 | sources : [ 10 | 'amex.c', 11 | 'amex.h', 12 | 'config.c', 13 | 'config.h', 14 | 'ds.c', 15 | 'ds.h', 16 | 'eeprom.c', 17 | 'eeprom.h', 18 | 'gpio.c', 19 | 'gpio.h', 20 | 'guid.c', 21 | 'jvs.c', 22 | 'jvs.h', 23 | 'nvram.c', 24 | 'nvram.h', 25 | 'sram.c', 26 | 'sram.h', 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /amex/nvram.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "amex/nvram.h" 8 | 9 | #include "util/dprintf.h" 10 | 11 | HRESULT nvram_open_file(HANDLE *out, const wchar_t *path, size_t size) 12 | { 13 | LARGE_INTEGER cur_size; 14 | LARGE_INTEGER pos; 15 | HANDLE file; 16 | HRESULT hr; 17 | BOOL ok; 18 | 19 | assert(out != NULL); 20 | assert(path != NULL); 21 | 22 | *out = NULL; 23 | 24 | file = CreateFileW( 25 | path, 26 | GENERIC_READ | GENERIC_WRITE, 27 | FILE_SHARE_READ, 28 | NULL, 29 | OPEN_ALWAYS, 30 | FILE_ATTRIBUTE_NORMAL, 31 | NULL); 32 | 33 | if (file == INVALID_HANDLE_VALUE) { 34 | hr = HRESULT_FROM_WIN32(GetLastError()); 35 | dprintf("%S: Error opening backing store: %x\n", path, (int) hr); 36 | 37 | goto end; 38 | } 39 | 40 | ok = GetFileSizeEx(file, &cur_size); 41 | 42 | if (!ok) { 43 | hr = HRESULT_FROM_WIN32(GetLastError()); 44 | dprintf("%S: GetFileSizeEx failed: %x\n", path, (int) hr); 45 | 46 | goto end; 47 | } 48 | 49 | if (cur_size.QuadPart != (uint64_t) size) { 50 | pos.QuadPart = (uint64_t) size; 51 | ok = SetFilePointerEx(file, pos, NULL, FILE_BEGIN); 52 | 53 | if (!ok) { 54 | hr = HRESULT_FROM_WIN32(GetLastError()); 55 | dprintf("%S: SetFilePointerEx failed: %x\n", path, (int) hr); 56 | 57 | goto end; 58 | } 59 | 60 | ok = SetEndOfFile(file); 61 | 62 | if (!ok) { 63 | hr = HRESULT_FROM_WIN32(GetLastError()); 64 | dprintf("%S: SetEndOfFile failed: %x\n", path, (int) hr); 65 | 66 | goto end; 67 | } 68 | } 69 | 70 | *out = file; 71 | file = INVALID_HANDLE_VALUE; 72 | 73 | hr = S_OK; 74 | 75 | end: 76 | if (file != INVALID_HANDLE_VALUE) { 77 | CloseHandle(file); 78 | } 79 | 80 | return hr; 81 | } 82 | -------------------------------------------------------------------------------- /amex/nvram.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | HRESULT nvram_open_file(HANDLE *out, const wchar_t *path, size_t size); 8 | -------------------------------------------------------------------------------- /amex/sram.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __GNUC__ 4 | #include 5 | #else 6 | #include 7 | #endif 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "amex/sram.h" 14 | #include "amex/nvram.h" 15 | 16 | #include "hook/iohook.h" 17 | 18 | #include "hooklib/setupapi.h" 19 | 20 | #include "util/dprintf.h" 21 | #include "util/str.h" 22 | 23 | static HRESULT sram_handle_irp(struct irp *irp); 24 | static HRESULT sram_handle_open(struct irp *irp); 25 | static HRESULT sram_handle_close(struct irp *irp); 26 | static HRESULT sram_handle_ioctl(struct irp *irp); 27 | 28 | static HRESULT sram_ioctl_get_geometry(struct irp *irp); 29 | 30 | static struct sram_config sram_config; 31 | static HANDLE sram_file; 32 | 33 | HRESULT sram_hook_init(const struct sram_config *cfg) 34 | { 35 | HRESULT hr; 36 | 37 | assert(cfg != NULL); 38 | 39 | if (!cfg->enable) { 40 | return S_FALSE; 41 | } 42 | 43 | memcpy(&sram_config, cfg, sizeof(*cfg)); 44 | 45 | hr = iohook_push_handler(sram_handle_irp); 46 | 47 | if (FAILED(hr)) { 48 | return hr; 49 | } 50 | 51 | hr = setupapi_add_phantom_dev(&sram_guid, L"$sram"); 52 | 53 | if (FAILED(hr)) { 54 | return hr; 55 | } 56 | 57 | return S_OK; 58 | } 59 | 60 | static HRESULT sram_handle_irp(struct irp *irp) 61 | { 62 | assert(irp != NULL); 63 | 64 | if (irp->op != IRP_OP_OPEN && irp->fd != sram_file) { 65 | return iohook_invoke_next(irp); 66 | } 67 | 68 | switch (irp->op) { 69 | case IRP_OP_OPEN: return sram_handle_open(irp); 70 | case IRP_OP_CLOSE: return sram_handle_close(irp); 71 | case IRP_OP_IOCTL: return sram_handle_ioctl(irp); 72 | default: return iohook_invoke_next(irp); 73 | } 74 | } 75 | 76 | static HRESULT sram_handle_open(struct irp *irp) 77 | { 78 | HRESULT hr; 79 | 80 | if (!wstr_eq(irp->open_filename, L"$sram")) { 81 | return iohook_invoke_next(irp); 82 | } 83 | 84 | if (sram_file != NULL) { 85 | dprintf("SRAM: Already open\n"); 86 | 87 | return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION); 88 | } 89 | 90 | dprintf("SRAM: Open device\n"); 91 | hr = nvram_open_file(&sram_file, sram_config.path, 0x80000); 92 | 93 | if (FAILED(hr)) { 94 | return hr; 95 | } 96 | 97 | irp->fd = sram_file; 98 | 99 | return S_OK; 100 | } 101 | 102 | static HRESULT sram_handle_close(struct irp *irp) 103 | { 104 | dprintf("SRAM: Close device\n"); 105 | sram_file = NULL; 106 | 107 | return iohook_invoke_next(irp); 108 | } 109 | 110 | static HRESULT sram_handle_ioctl(struct irp *irp) 111 | { 112 | switch (irp->ioctl) { 113 | case IOCTL_DISK_GET_DRIVE_GEOMETRY: 114 | return sram_ioctl_get_geometry(irp); 115 | 116 | default: 117 | dprintf("SRAM: Unknown ioctl %x, write %i read %i\n", 118 | irp->ioctl, 119 | (int) irp->write.nbytes, 120 | (int) irp->read.nbytes); 121 | 122 | return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); 123 | } 124 | } 125 | 126 | static HRESULT sram_ioctl_get_geometry(struct irp *irp) 127 | { 128 | DISK_GEOMETRY out; 129 | HRESULT hr; 130 | 131 | dprintf("SRAM: Get geometry\n"); 132 | 133 | memset(&out, 0, sizeof(out)); 134 | out.Cylinders.QuadPart = 0x20000; 135 | out.MediaType = 0; 136 | out.TracksPerCylinder = 1; 137 | out.SectorsPerTrack = 1; 138 | out.BytesPerSector = 4; 139 | 140 | hr = iobuf_write(&irp->read, &out, sizeof(out)); 141 | 142 | if (FAILED(hr)) { 143 | dprintf("SRAM: Get geometry failed: %08x\n", (int) hr); 144 | } 145 | 146 | return hr; 147 | } 148 | -------------------------------------------------------------------------------- /amex/sram.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct sram_config { 9 | bool enable; 10 | wchar_t path[MAX_PATH]; 11 | }; 12 | 13 | DEFINE_GUID( 14 | sram_guid, 15 | 0x741B5FCA, 16 | 0x4635, 17 | 0x4443, 18 | 0xA7, 0xA0, 0x57, 0xCA, 0x7B, 0x50, 0x6A, 0x49); 19 | 20 | HRESULT sram_hook_init(const struct sram_config *cfg); 21 | -------------------------------------------------------------------------------- /board/aime-dll.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "board/aime-dll.h" 7 | 8 | #include "util/dll-bind.h" 9 | #include "util/dprintf.h" 10 | 11 | const struct dll_bind_sym aime_dll_syms[] = { 12 | { 13 | .sym = "aime_io_init", 14 | .off = offsetof(struct aime_dll, init), 15 | }, { 16 | .sym = "aime_io_nfc_poll", 17 | .off = offsetof(struct aime_dll, nfc_poll), 18 | }, { 19 | .sym = "aime_io_nfc_get_aime_id", 20 | .off = offsetof(struct aime_dll, nfc_get_aime_id), 21 | }, { 22 | .sym = "aime_io_nfc_get_felica_id", 23 | .off = offsetof(struct aime_dll, nfc_get_felica_id), 24 | }, { 25 | .sym = "aime_io_led_set_color", 26 | .off = offsetof(struct aime_dll, led_set_color), 27 | } 28 | }; 29 | 30 | struct aime_dll aime_dll; 31 | 32 | // Copypasta DLL binding and diagnostic message boilerplate. 33 | // Not much of this lends itself to being easily factored out. Also there 34 | // will be a lot of API-specific branching code here eventually as new API 35 | // versions get defined, so even though these functions all look the same 36 | // now this won't remain the case forever. 37 | 38 | HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self) 39 | { 40 | uint16_t (*get_api_version)(void); 41 | const struct dll_bind_sym *sym; 42 | HINSTANCE owned; 43 | HINSTANCE src; 44 | HRESULT hr; 45 | 46 | assert(cfg != NULL); 47 | assert(self != NULL); 48 | 49 | if (cfg->path[0] != L'\0') { 50 | owned = LoadLibraryW(cfg->path); 51 | 52 | if (owned == NULL) { 53 | hr = HRESULT_FROM_WIN32(GetLastError()); 54 | dprintf("NFC Assembly: Failed to load IO DLL: %lx: %S\n", 55 | hr, 56 | cfg->path); 57 | 58 | goto end; 59 | } 60 | 61 | dprintf("NFC Assembly: Using custom IO DLL: %S\n", cfg->path); 62 | src = owned; 63 | } else { 64 | owned = NULL; 65 | src = self; 66 | } 67 | 68 | get_api_version = (void *) GetProcAddress(src, "aime_io_get_api_version"); 69 | 70 | if (get_api_version != NULL) { 71 | aime_dll.api_version = get_api_version(); 72 | } else { 73 | aime_dll.api_version = 0x0100; 74 | dprintf("Custom IO DLL does not expose aime_io_get_api_version, " 75 | "assuming API version 1.0.\n" 76 | "Please ask the developer to update their DLL.\n"); 77 | } 78 | 79 | if (aime_dll.api_version >= 0x0200) { 80 | hr = E_NOTIMPL; 81 | dprintf("NFC Assembly: Custom IO DLL implements an unsupported " 82 | "API version (%#04x). Please update Segatools.\n", 83 | aime_dll.api_version); 84 | 85 | goto end; 86 | } 87 | 88 | sym = aime_dll_syms; 89 | hr = dll_bind(&aime_dll, src, &sym, _countof(aime_dll_syms)); 90 | 91 | if (FAILED(hr)) { 92 | if (src != self) { 93 | dprintf("NFC Assembly: Custom IO DLL does not provide function " 94 | "\"%s\". Please contact your IO DLL's developer for " 95 | "further assistance.\n", 96 | sym->sym); 97 | 98 | goto end; 99 | } else { 100 | dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); 101 | } 102 | } 103 | 104 | owned = NULL; 105 | 106 | end: 107 | if (owned != NULL) { 108 | FreeLibrary(owned); 109 | } 110 | 111 | return hr; 112 | } 113 | -------------------------------------------------------------------------------- /board/aime-dll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "aimeio/aimeio.h" 6 | 7 | struct aime_dll { 8 | uint16_t api_version; 9 | HRESULT (*init)(void); 10 | HRESULT (*nfc_poll)(uint8_t unit_no); 11 | HRESULT (*nfc_get_aime_id)( 12 | uint8_t unit_no, 13 | uint8_t *luid, 14 | size_t luid_size); 15 | HRESULT (*nfc_get_felica_id)(uint8_t unit_no, uint64_t *IDm); 16 | void (*led_set_color)(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b); 17 | }; 18 | 19 | struct aime_dll_config { 20 | wchar_t path[MAX_PATH]; 21 | }; 22 | 23 | extern struct aime_dll aime_dll; 24 | 25 | HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self); 26 | -------------------------------------------------------------------------------- /board/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "board/aime-dll.h" 9 | #include "board/config.h" 10 | #include "board/sg-reader.h" 11 | 12 | static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename) 13 | { 14 | assert(cfg != NULL); 15 | assert(filename != NULL); 16 | 17 | GetPrivateProfileStringW( 18 | L"aimeio", 19 | L"path", 20 | L"", 21 | cfg->path, 22 | _countof(cfg->path), 23 | filename); 24 | } 25 | 26 | void aime_config_load(struct aime_config *cfg, const wchar_t *filename) 27 | { 28 | assert(cfg != NULL); 29 | assert(filename != NULL); 30 | 31 | aime_dll_config_load(&cfg->dll, filename); 32 | cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename); 33 | } 34 | -------------------------------------------------------------------------------- /board/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "board/sg-reader.h" 7 | 8 | void aime_config_load(struct aime_config *cfg, const wchar_t *filename); 9 | -------------------------------------------------------------------------------- /board/guid.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "board/guid.h" 4 | -------------------------------------------------------------------------------- /board/guid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | DEFINE_GUID( 6 | hid_guid, 7 | 0x4D1E55B2L, 8 | 0xF16F, 9 | 0x11CF, 10 | 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30); 11 | -------------------------------------------------------------------------------- /board/io3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "jvs/jvs-bus.h" 6 | 7 | struct io3_switch_state { 8 | /* Note: this struct is host-endian. The IO3 emulator handles the conversion 9 | to protocol-endian. */ 10 | 11 | uint8_t system; 12 | uint16_t p1; 13 | uint16_t p2; 14 | }; 15 | 16 | struct io3_ops { 17 | void (*reset)(void *ctx); 18 | void (*write_gpio)(void *ctx, uint32_t state); 19 | void (*read_switches)(void *ctx, struct io3_switch_state *out); 20 | void (*read_analogs)(void *ctx, uint16_t *analogs, uint8_t nanalogs); 21 | void (*read_coin_counter)(void *ctx, uint8_t slot_no, uint16_t *out); 22 | }; 23 | 24 | struct io3 { 25 | struct jvs_node jvs; 26 | uint8_t addr; 27 | const struct io3_ops *ops; 28 | void *ops_ctx; 29 | }; 30 | 31 | void io3_init( 32 | struct io3 *io3, 33 | struct jvs_node *next, 34 | const struct io3_ops *ops, 35 | void *ops_ctx); 36 | 37 | struct jvs_node *io3_to_jvs_node(struct io3 *io3); 38 | -------------------------------------------------------------------------------- /board/io4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | enum { 8 | /* System buttons in button[0] */ 9 | 10 | IO4_BUTTON_TEST = 1 << 9, 11 | IO4_BUTTON_SERVICE = 1 << 6, 12 | }; 13 | 14 | struct io4_state { 15 | uint16_t adcs[8]; 16 | uint16_t spinners[4]; 17 | uint16_t chutes[2]; 18 | uint16_t buttons[2]; 19 | }; 20 | 21 | struct io4_ops { 22 | HRESULT (*poll)(void *ctx, struct io4_state *state); 23 | }; 24 | 25 | HRESULT io4_hook_init(const struct io4_ops *ops, void *ctx); 26 | -------------------------------------------------------------------------------- /board/meson.build: -------------------------------------------------------------------------------- 1 | board_lib = static_library( 2 | 'board', 3 | include_directories : inc, 4 | implicit_include_directories : false, 5 | c_pch : '../precompiled.h', 6 | dependencies : [ 7 | capnhook.get_variable('hook_dep'), 8 | ], 9 | link_with : [ 10 | iccard_lib, 11 | ], 12 | sources : [ 13 | 'aime-dll.c', 14 | 'aime-dll.h', 15 | 'config.c', 16 | 'config.h', 17 | 'guid.c', 18 | 'guid.h', 19 | 'io3.c', 20 | 'io3.h', 21 | 'io4.c', 22 | 'io4.h', 23 | 'sg-cmd.c', 24 | 'sg-cmd.h', 25 | 'sg-frame.c', 26 | 'sg-frame.h', 27 | 'sg-led.c', 28 | 'sg-led.h', 29 | 'sg-led-cmd.h', 30 | 'sg-nfc.c', 31 | 'sg-nfc.h', 32 | 'sg-nfc-cmd.h', 33 | 'sg-reader.c', 34 | 'sg-reader.h', 35 | 'slider-cmd.h', 36 | 'slider-frame.c', 37 | 'slider-frame.h', 38 | 'vfd.c', 39 | 'vfd.h', 40 | ], 41 | ) 42 | -------------------------------------------------------------------------------- /board/sg-cmd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "board/sg-cmd.h" 4 | #include "board/sg-frame.h" 5 | 6 | #include "hook/iobuf.h" 7 | 8 | #include "util/dprintf.h" 9 | 10 | union sg_req_any { 11 | struct sg_req_header req; 12 | uint8_t bytes[256]; 13 | }; 14 | 15 | union sg_res_any { 16 | struct sg_res_header res; 17 | uint8_t bytes[256]; 18 | }; 19 | 20 | static HRESULT sg_req_validate(const void *ptr, size_t nbytes); 21 | 22 | static void sg_res_error( 23 | struct sg_res_header *res, 24 | const struct sg_req_header *req); 25 | 26 | static HRESULT sg_req_validate(const void *ptr, size_t nbytes) 27 | { 28 | const struct sg_req_header *req; 29 | size_t payload_len; 30 | 31 | assert(ptr != NULL); 32 | 33 | if (nbytes < sizeof(*req)) { 34 | dprintf("SG Cmd: Request header truncated\n"); 35 | 36 | return E_FAIL; 37 | } 38 | 39 | req = ptr; 40 | 41 | if (req->hdr.frame_len != nbytes) { 42 | dprintf("SG Cmd: Frame length mismatch: got %i exp %i\n", 43 | req->hdr.frame_len, 44 | (int) nbytes); 45 | 46 | return E_FAIL; 47 | } 48 | 49 | payload_len = req->hdr.frame_len - sizeof(*req); 50 | 51 | if (req->payload_len != payload_len) { 52 | dprintf("SG Cmd: Payload length mismatch: got %i exp %i\n", 53 | req->payload_len, 54 | (int) payload_len); 55 | 56 | return E_FAIL; 57 | } 58 | 59 | return S_OK; 60 | } 61 | 62 | void sg_req_transact( 63 | struct iobuf *res_frame, 64 | const uint8_t *req_bytes, 65 | size_t req_nbytes, 66 | sg_dispatch_fn_t dispatch, 67 | void *ctx) 68 | { 69 | struct iobuf req_span; 70 | union sg_req_any req; 71 | union sg_res_any res; 72 | HRESULT hr; 73 | 74 | assert(res_frame != NULL); 75 | assert(req_bytes != NULL); 76 | assert(dispatch != NULL); 77 | 78 | req_span.bytes = req.bytes; 79 | req_span.nbytes = sizeof(req.bytes); 80 | req_span.pos = 0; 81 | 82 | hr = sg_frame_decode(&req_span, req_bytes, req_nbytes); 83 | 84 | if (FAILED(hr)) { 85 | return; 86 | } 87 | 88 | hr = sg_req_validate(req.bytes, req_span.pos); 89 | 90 | if (FAILED(hr)) { 91 | return; 92 | } 93 | 94 | hr = dispatch(ctx, &req, &res); 95 | 96 | if (hr != S_FALSE) { 97 | if (FAILED(hr)) { 98 | sg_res_error(&res.res, &req.req); 99 | } 100 | 101 | sg_frame_encode(res_frame, res.bytes, res.res.hdr.frame_len); 102 | } 103 | } 104 | 105 | void sg_res_init( 106 | struct sg_res_header *res, 107 | const struct sg_req_header *req, 108 | size_t payload_len) 109 | { 110 | assert(res != NULL); 111 | assert(req != NULL); 112 | 113 | res->hdr.frame_len = sizeof(*res) + payload_len; 114 | res->hdr.addr = req->hdr.addr; 115 | res->hdr.seq_no = req->hdr.seq_no; 116 | res->hdr.cmd = req->hdr.cmd; 117 | res->status = 0; 118 | res->payload_len = payload_len; 119 | } 120 | 121 | static void sg_res_error( 122 | struct sg_res_header *res, 123 | const struct sg_req_header *req) 124 | { 125 | assert(res != NULL); 126 | assert(req != NULL); 127 | 128 | res->hdr.frame_len = sizeof(*res); 129 | res->hdr.addr = req->hdr.addr; 130 | res->hdr.seq_no = req->hdr.seq_no; 131 | res->hdr.cmd = req->hdr.cmd; 132 | res->status = 1; 133 | res->payload_len = 0; 134 | } 135 | -------------------------------------------------------------------------------- /board/sg-cmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "hook/iobuf.h" 9 | 10 | struct sg_header { 11 | uint8_t frame_len; 12 | uint8_t addr; 13 | uint8_t seq_no; 14 | uint8_t cmd; 15 | }; 16 | 17 | struct sg_req_header { 18 | struct sg_header hdr; 19 | uint8_t payload_len; 20 | }; 21 | 22 | struct sg_res_header { 23 | struct sg_header hdr; 24 | uint8_t status; 25 | uint8_t payload_len; 26 | }; 27 | 28 | typedef HRESULT (*sg_dispatch_fn_t)( 29 | void *ctx, 30 | const void *req, 31 | void *res); 32 | 33 | void sg_req_transact( 34 | struct iobuf *res_frame, 35 | const uint8_t *req_bytes, 36 | size_t req_nbytes, 37 | sg_dispatch_fn_t dispatch, 38 | void *ctx); 39 | 40 | void sg_res_init( 41 | struct sg_res_header *res, 42 | const struct sg_req_header *req, 43 | size_t payload_len); 44 | -------------------------------------------------------------------------------- /board/sg-frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "hook/iobuf.h" 9 | 10 | HRESULT sg_frame_decode( 11 | struct iobuf *dest, 12 | const uint8_t *bytes, 13 | size_t nbytes); 14 | 15 | HRESULT sg_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes); 16 | -------------------------------------------------------------------------------- /board/sg-led-cmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "board/sg-cmd.h" 6 | 7 | enum { 8 | SG_RGB_CMD_SET_COLOR = 0x81, 9 | SG_RGB_CMD_RESET = 0xF5, 10 | SG_RGB_CMD_GET_INFO = 0xF0, 11 | }; 12 | 13 | struct sg_led_res_reset { 14 | struct sg_res_header res; 15 | uint8_t payload; 16 | }; 17 | 18 | struct sg_led_res_get_info { 19 | struct sg_res_header res; 20 | uint8_t payload[9]; 21 | }; 22 | 23 | struct sg_led_req_set_color { 24 | struct sg_req_header req; 25 | uint8_t payload[3]; 26 | }; 27 | 28 | union sg_led_req_any { 29 | uint8_t bytes[256]; 30 | struct sg_req_header simple; 31 | struct sg_led_req_set_color set_color; 32 | }; 33 | 34 | union sg_led_res_any { 35 | uint8_t bytes[256]; 36 | struct sg_res_header simple; 37 | struct sg_led_res_reset reset; 38 | struct sg_led_res_get_info get_info; 39 | }; 40 | -------------------------------------------------------------------------------- /board/sg-led.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "hook/iobuf.h" 8 | 9 | struct sg_led_ops { 10 | HRESULT (*reset)(void *ctx); 11 | void (*set_color)(void *ctx, uint8_t r, uint8_t g, uint8_t b); 12 | }; 13 | 14 | struct sg_led { 15 | const struct sg_led_ops *ops; 16 | void *ops_ctx; 17 | uint8_t addr; 18 | }; 19 | 20 | void sg_led_init( 21 | struct sg_led *led, 22 | uint8_t addr, 23 | const struct sg_led_ops *ops, 24 | void *ctx); 25 | 26 | void sg_led_transact( 27 | struct sg_led *led, 28 | struct iobuf *res_frame, 29 | const void *req_bytes, 30 | size_t req_nbytes); 31 | -------------------------------------------------------------------------------- /board/sg-nfc-cmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #pragma pack(push, 1) 6 | 7 | enum { 8 | SG_NFC_CMD_GET_FW_VERSION = 0x30, 9 | SG_NFC_CMD_GET_HW_VERSION = 0x32, 10 | SG_NFC_CMD_RADIO_ON = 0x40, 11 | SG_NFC_CMD_RADIO_OFF = 0x41, 12 | SG_NFC_CMD_POLL = 0x42, 13 | SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43, 14 | SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x50, 15 | SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52, 16 | SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54, 17 | SG_NFC_CMD_MIFARE_AUTHENTICATE = 0x55, /* guess based on time sent */ 18 | SG_NFC_CMD_RESET = 0x62, 19 | SG_NFC_CMD_FELICA_ENCAP = 0x71, 20 | }; 21 | 22 | struct sg_nfc_res_get_fw_version { 23 | struct sg_res_header res; 24 | char version[23]; 25 | }; 26 | 27 | struct sg_nfc_res_get_hw_version { 28 | struct sg_res_header res; 29 | char version[23]; 30 | }; 31 | 32 | struct sg_nfc_req_mifare_set_key { 33 | struct sg_req_header req; 34 | uint8_t key_a[6]; 35 | }; 36 | 37 | struct sg_nfc_req_mifare_50 { 38 | struct sg_req_header req; 39 | uint8_t payload[6]; 40 | }; 41 | 42 | struct sg_nfc_req_poll_40 { 43 | struct sg_req_header req; 44 | uint8_t payload; 45 | }; 46 | 47 | struct sg_nfc_poll_mifare { 48 | uint8_t type; 49 | uint8_t id_len; 50 | uint32_t uid; 51 | }; 52 | 53 | struct sg_nfc_poll_felica { 54 | uint8_t type; 55 | uint8_t id_len; 56 | uint64_t IDm; 57 | uint64_t PMm; 58 | }; 59 | 60 | struct sg_nfc_res_poll { 61 | struct sg_res_header res; 62 | uint8_t count; 63 | uint8_t payload[250]; 64 | }; 65 | 66 | struct sg_nfc_req_mifare_select_tag { 67 | struct sg_res_header res; 68 | uint32_t uid; 69 | }; 70 | 71 | struct sg_nfc_req_mifare_read_block { 72 | struct sg_req_header req; 73 | struct { 74 | uint32_t uid; 75 | uint8_t block_no; 76 | } payload; 77 | }; 78 | 79 | struct sg_nfc_res_mifare_read_block { 80 | struct sg_res_header res; 81 | uint8_t block[16]; 82 | }; 83 | 84 | struct sg_nfc_req_felica_encap { 85 | struct sg_req_header req; 86 | uint64_t IDm; 87 | uint8_t payload[243]; 88 | }; 89 | 90 | struct sg_nfc_res_felica_encap { 91 | struct sg_res_header res; 92 | uint8_t payload[250]; 93 | }; 94 | 95 | union sg_nfc_req_any { 96 | uint8_t bytes[256]; 97 | struct sg_req_header simple; 98 | struct sg_nfc_req_mifare_set_key mifare_set_key; 99 | struct sg_nfc_req_mifare_read_block mifare_read_block; 100 | struct sg_nfc_req_mifare_50 mifare_50; 101 | struct sg_nfc_req_poll_40 poll_40; 102 | struct sg_nfc_req_felica_encap felica_encap; 103 | }; 104 | 105 | union sg_nfc_res_any { 106 | uint8_t bytes[256]; 107 | struct sg_res_header simple; 108 | struct sg_nfc_res_get_fw_version get_fw_version; 109 | struct sg_nfc_res_get_hw_version get_hw_version; 110 | struct sg_nfc_res_poll poll; 111 | struct sg_nfc_res_mifare_read_block mifare_read_block; 112 | struct sg_nfc_res_felica_encap felica_encap; 113 | }; 114 | 115 | #pragma pack(pop) 116 | -------------------------------------------------------------------------------- /board/sg-nfc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "hook/iobuf.h" 9 | 10 | #include "iccard/felica.h" 11 | #include "iccard/mifare.h" 12 | 13 | struct sg_nfc_ops { 14 | HRESULT (*poll)(void *ctx); 15 | HRESULT (*get_aime_id)(void *ctx, uint8_t *luid, size_t nbytes); 16 | HRESULT (*get_felica_id)(void *ctx, uint64_t *IDm); 17 | 18 | // TODO Banapass, AmuseIC 19 | }; 20 | 21 | struct sg_nfc { 22 | const struct sg_nfc_ops *ops; 23 | void *ops_ctx; 24 | uint8_t addr; 25 | struct felica felica; 26 | struct mifare mifare; 27 | }; 28 | 29 | void sg_nfc_init( 30 | struct sg_nfc *nfc, 31 | uint8_t addr, 32 | const struct sg_nfc_ops *ops, 33 | void *ops_ctx); 34 | 35 | void sg_nfc_transact( 36 | struct sg_nfc *nfc, 37 | struct iobuf *res_frame, 38 | const void *req_bytes, 39 | size_t req_nbytes); 40 | -------------------------------------------------------------------------------- /board/sg-reader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "board/aime-dll.h" 8 | 9 | struct aime_config { 10 | struct aime_dll_config dll; 11 | bool enable; 12 | }; 13 | 14 | HRESULT sg_reader_hook_init( 15 | const struct aime_config *cfg, 16 | unsigned int port_no, 17 | HINSTANCE self); 18 | -------------------------------------------------------------------------------- /board/slider-cmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "board/slider-frame.h" 4 | 5 | enum { 6 | SLIDER_CMD_AUTO_SCAN = 0x01, 7 | SLIDER_CMD_SET_LED = 0x02, 8 | SLIDER_CMD_AUTO_SCAN_START = 0x03, 9 | SLIDER_CMD_AUTO_SCAN_STOP = 0x04, 10 | SLIDER_CMD_DIVA_UNK_09 = 0x09, 11 | SLIDER_CMD_DIVA_UNK_0A = 0x0A, 12 | SLIDER_CMD_RESET = 0x10, 13 | SLIDER_CMD_GET_BOARD_INFO = 0xF0, 14 | }; 15 | 16 | struct slider_req_set_led { 17 | struct slider_hdr hdr; 18 | struct { 19 | uint8_t unk; /* 0x28, decimal 40. meaning unknown. */ 20 | uint8_t rgb[96]; 21 | } payload; 22 | }; 23 | 24 | union slider_req_any { 25 | struct slider_hdr hdr; 26 | struct slider_req_set_led set_led; 27 | uint8_t bytes[260]; 28 | }; 29 | 30 | struct slider_resp_get_board_info { 31 | struct slider_hdr hdr; 32 | char version[32]; 33 | }; 34 | 35 | struct slider_resp_auto_scan { 36 | struct slider_hdr hdr; 37 | uint8_t pressure[32]; 38 | }; 39 | -------------------------------------------------------------------------------- /board/slider-frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "hook/iobuf.h" 9 | 10 | enum { 11 | SLIDER_FRAME_SYNC = 0xFF, 12 | }; 13 | 14 | struct slider_hdr { 15 | uint8_t sync; 16 | uint8_t cmd; 17 | uint8_t nbytes; 18 | }; 19 | 20 | HRESULT slider_frame_decode(struct iobuf *dest, struct iobuf *src); 21 | 22 | HRESULT slider_frame_encode( 23 | struct iobuf *dest, 24 | const void *ptr, 25 | size_t nbytes); 26 | -------------------------------------------------------------------------------- /board/vfd.c: -------------------------------------------------------------------------------- 1 | /* This is some sort of LCD display found on various cabinets. It is driven 2 | directly by amdaemon, and it has something to do with displaying the status 3 | of electronic payments. 4 | 5 | Part number in schematics is "VFD GP1232A02A FUTABA". 6 | 7 | Little else about this board is known. Black-holing the RS232 comms that it 8 | receives seems to be sufficient for the time being. */ 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include "board/vfd.h" 16 | 17 | #include "hook/iohook.h" 18 | 19 | #include "hooklib/uart.h" 20 | 21 | #include "util/dprintf.h" 22 | #include "util/dump.h" 23 | 24 | static HRESULT vfd_handle_irp(struct irp *irp); 25 | 26 | static struct uart vfd_uart; 27 | static uint8_t vfd_written[512]; 28 | static uint8_t vfd_readable[512]; 29 | 30 | HRESULT vfd_hook_init(unsigned int port_no) 31 | { 32 | uart_init(&vfd_uart, port_no); 33 | vfd_uart.written.bytes = vfd_written; 34 | vfd_uart.written.nbytes = sizeof(vfd_written); 35 | vfd_uart.readable.bytes = vfd_readable; 36 | vfd_uart.readable.nbytes = sizeof(vfd_readable); 37 | 38 | return iohook_push_handler(vfd_handle_irp); 39 | } 40 | 41 | static HRESULT vfd_handle_irp(struct irp *irp) 42 | { 43 | HRESULT hr; 44 | 45 | assert(irp != NULL); 46 | 47 | if (!uart_match_irp(&vfd_uart, irp)) { 48 | return iohook_invoke_next(irp); 49 | } 50 | 51 | hr = uart_handle_irp(&vfd_uart, irp); 52 | 53 | if (FAILED(hr) || irp->op != IRP_OP_WRITE) { 54 | return hr; 55 | } 56 | 57 | dprintf("VFD TX:\n"); 58 | dump_iobuf(&vfd_uart.written); 59 | vfd_uart.written.pos = 0; 60 | 61 | return hr; 62 | } 63 | -------------------------------------------------------------------------------- /board/vfd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | HRESULT vfd_hook_init(unsigned int port_no); 6 | -------------------------------------------------------------------------------- /chronohook/chronohook.def: -------------------------------------------------------------------------------- 1 | LIBRARY chronohook 2 | 3 | EXPORTS 4 | aime_io_get_api_version 5 | aime_io_init 6 | aime_io_led_set_color 7 | aime_io_nfc_get_aime_id 8 | aime_io_nfc_get_felica_id 9 | aime_io_nfc_poll 10 | amDllVideoClose @2 11 | amDllVideoGetVBiosVersion @4 12 | amDllVideoOpen @1 13 | amDllVideoSetResolution @3 -------------------------------------------------------------------------------- /chronohook/config.c: -------------------------------------------------------------------------------- 1 | /* SoftwareGuy/Coburn's attempt at Chrono Regalia Hook for SegaTools. 2 | * This code is licensed under MIT license; don't be a thief. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "amex/amex.h" 9 | #include "amex/config.h" 10 | 11 | #include "board/config.h" 12 | #include "board/sg-reader.h" 13 | 14 | #include "hooklib/config.h" 15 | // #include "hooklib/gfx.h" 16 | 17 | #include "chronohook/config.h" 18 | 19 | #include "platform/config.h" 20 | #include "platform/platform.h" 21 | 22 | void chrono_hook_config_load(struct chrono_hook_config *cfg, const wchar_t *filename) { 23 | assert(cfg != NULL); 24 | assert(filename != NULL); 25 | 26 | platform_config_load(&cfg->platform, filename); 27 | amex_config_load(&cfg->amex, filename); 28 | aime_config_load(&cfg->aime, filename); 29 | // gfx_config_load(&cfg->gfx, filename); 30 | } -------------------------------------------------------------------------------- /chronohook/config.h: -------------------------------------------------------------------------------- 1 | /* SoftwareGuy/Coburn's attempt at Chrono Regalia Hook for SegaTools. 2 | * This code is licensed under MIT license; don't be a thief. 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "amex/amex.h" 11 | #include "board/sg-reader.h" 12 | 13 | // ... 14 | #include "platform/platform.h" 15 | 16 | struct chrono_hook_config { 17 | struct platform_config platform; 18 | struct amex_config amex; 19 | struct aime_config aime; 20 | }; 21 | 22 | void chrono_hook_config_load( 23 | struct chrono_hook_config *cfg, 24 | const wchar_t *filename); -------------------------------------------------------------------------------- /chronohook/dllmain.c: -------------------------------------------------------------------------------- 1 | /* SoftwareGuy (Coburn)'s attempt at Chrono Regalia Hook for SegaTools. 2 | * This code is licensed under MIT license; don't be a thief. 3 | */ 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // Segatools Libraries 10 | #include "chronohook/config.h" 11 | #include "platform/platform.h" 12 | 13 | #include "board/sg-reader.h" 14 | #include "board/vfd.h" 15 | 16 | #include "hook/process.h" 17 | 18 | #include "hooklib/gfx.h" 19 | #include "hooklib/dvd.h" 20 | #include "hooklib/serial.h" 21 | #include "hooklib/spike.h" 22 | 23 | #include "util/dprintf.h" 24 | 25 | // Statics 26 | static HMODULE chrono_hook_mod; 27 | static process_entry_t chrono_startup; 28 | static struct chrono_hook_config chrono_hook_cfg; 29 | 30 | static int AIME_READER_PORT = 3; 31 | 32 | static DWORD CALLBACK chrono_pre_startup(void) { 33 | HRESULT hr; 34 | 35 | dprintf("-- Begin chrono_pre_startup --\n"); 36 | dprintf("ChronoHook has arrived!\n"); 37 | dprintf("This is very experimental hackery and will likely catch fire.\n"); 38 | 39 | // Load configuration. 40 | chrono_hook_config_load(&chrono_hook_cfg, L".\\segatools.ini"); 41 | 42 | // Hook APIs. 43 | serial_hook_init(); 44 | 45 | hr = platform_hook_init(&chrono_hook_cfg.platform, 46 | "SDEC", // SDEC: Chrono Regalia 47 | "ACA1", // ALLS UX 48 | chrono_hook_mod); 49 | 50 | if(FAILED(hr)) { 51 | dprintf("Configuration file load failure.\n"); 52 | return hr; 53 | } 54 | 55 | // AIME and other payment card reader initialization 56 | hr = sg_reader_hook_init(&chrono_hook_cfg.aime, AIME_READER_PORT, chrono_hook_mod); 57 | 58 | if(FAILED(hr)) { 59 | dprintf("sg reader/aime hook init failure.\n"); 60 | return hr; 61 | } 62 | 63 | // Kickstart debug helpers. 64 | spike_hook_init(L".\\segatools.ini"); 65 | 66 | // Done? 67 | dprintf("--- End chrono_pre_startup ---\n"); 68 | dprintf("Handing control back to the game.\n"); 69 | 70 | return chrono_startup(); 71 | } 72 | 73 | BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) 74 | { 75 | HRESULT hr; 76 | 77 | if (cause != DLL_PROCESS_ATTACH) { 78 | return TRUE; 79 | } 80 | 81 | chrono_hook_mod = mod; 82 | 83 | hr = process_hijack_startup(chrono_pre_startup, &chrono_startup); 84 | 85 | if (!SUCCEEDED(hr)) { 86 | dprintf("Failed to hijack process startup: %x\n", (int) hr); 87 | } 88 | 89 | return SUCCEEDED(hr); 90 | } -------------------------------------------------------------------------------- /chronohook/meson.build: -------------------------------------------------------------------------------- 1 | shared_library( 2 | 'chronohook', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | vs_module_defs : 'chronohook.def', 7 | c_pch : '../precompiled.h', 8 | dependencies : [ 9 | capnhook.get_variable('hook_dep'), 10 | capnhook.get_variable('hooklib_dep'), 11 | ], 12 | link_with : [ 13 | aimeio_lib, 14 | amex_lib, 15 | board_lib, 16 | hooklib_lib, 17 | platform_lib, 18 | util_lib, 19 | ], 20 | sources : [ 21 | 'config.c', 22 | 'config.h', 23 | 'dllmain.c', 24 | ], 25 | ) 26 | -------------------------------------------------------------------------------- /chunihook/chuni-dll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "chuniio/chuniio.h" 6 | 7 | struct chuni_dll { 8 | uint16_t api_version; 9 | HRESULT (*jvs_init)(void); 10 | void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams); 11 | void (*jvs_read_coin_counter)(uint16_t *total); 12 | HRESULT (*slider_init)(void); 13 | void (*slider_start)(chuni_io_slider_callback_t callback); 14 | void (*slider_stop)(void); 15 | void (*slider_set_leds)(const uint8_t *rgb); 16 | }; 17 | 18 | struct chuni_dll_config { 19 | wchar_t path[MAX_PATH]; 20 | }; 21 | 22 | extern struct chuni_dll chuni_dll; 23 | 24 | HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self); 25 | -------------------------------------------------------------------------------- /chunihook/chunihook.def: -------------------------------------------------------------------------------- 1 | LIBRARY chunihook 2 | 3 | EXPORTS 4 | Direct3DCreate9 5 | aime_io_get_api_version 6 | aime_io_init 7 | aime_io_led_set_color 8 | aime_io_nfc_get_aime_id 9 | aime_io_nfc_get_felica_id 10 | aime_io_nfc_poll 11 | amDllVideoClose @2 12 | amDllVideoGetVBiosVersion @4 13 | amDllVideoOpen @1 14 | amDllVideoSetResolution @3 15 | chuni_io_get_api_version 16 | chuni_io_jvs_init 17 | chuni_io_jvs_poll 18 | chuni_io_jvs_read_coin_counter 19 | chuni_io_slider_init 20 | chuni_io_slider_set_leds 21 | chuni_io_slider_start 22 | chuni_io_slider_stop 23 | -------------------------------------------------------------------------------- /chunihook/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "amex/amex.h" 8 | #include "amex/config.h" 9 | 10 | #include "board/config.h" 11 | #include "board/sg-reader.h" 12 | 13 | #include "chunihook/config.h" 14 | 15 | #include "hooklib/config.h" 16 | #include "hooklib/gfx.h" 17 | 18 | #include "platform/config.h" 19 | #include "platform/platform.h" 20 | 21 | void chuni_dll_config_load( 22 | struct chuni_dll_config *cfg, 23 | const wchar_t *filename) 24 | { 25 | assert(cfg != NULL); 26 | assert(filename != NULL); 27 | 28 | GetPrivateProfileStringW( 29 | L"chuniio", 30 | L"path", 31 | L"", 32 | cfg->path, 33 | _countof(cfg->path), 34 | filename); 35 | } 36 | 37 | void slider_config_load(struct slider_config *cfg, const wchar_t *filename) 38 | { 39 | assert(cfg != NULL); 40 | assert(filename != NULL); 41 | 42 | cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename); 43 | } 44 | 45 | void chuni_hook_config_load( 46 | struct chuni_hook_config *cfg, 47 | const wchar_t *filename) 48 | { 49 | assert(cfg != NULL); 50 | assert(filename != NULL); 51 | 52 | memset(cfg, 0, sizeof(*cfg)); 53 | 54 | platform_config_load(&cfg->platform, filename); 55 | amex_config_load(&cfg->amex, filename); 56 | aime_config_load(&cfg->aime, filename); 57 | gfx_config_load(&cfg->gfx, filename); 58 | chuni_dll_config_load(&cfg->dll, filename); 59 | slider_config_load(&cfg->slider, filename); 60 | } 61 | -------------------------------------------------------------------------------- /chunihook/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "amex/amex.h" 7 | 8 | #include "board/sg-reader.h" 9 | 10 | #include "chunihook/chuni-dll.h" 11 | #include "chunihook/slider.h" 12 | 13 | #include "hooklib/gfx.h" 14 | 15 | #include "platform/platform.h" 16 | 17 | struct chuni_hook_config { 18 | struct platform_config platform; 19 | struct amex_config amex; 20 | struct aime_config aime; 21 | struct gfx_config gfx; 22 | struct chuni_dll_config dll; 23 | struct slider_config slider; 24 | }; 25 | 26 | void chuni_dll_config_load( 27 | struct chuni_dll_config *cfg, 28 | const wchar_t *filename); 29 | void slider_config_load(struct slider_config *cfg, const wchar_t *filename); 30 | void chuni_hook_config_load( 31 | struct chuni_hook_config *cfg, 32 | const wchar_t *filename); 33 | -------------------------------------------------------------------------------- /chunihook/dllmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "amex/amex.h" 6 | 7 | #include "board/sg-reader.h" 8 | 9 | #include "chunihook/config.h" 10 | #include "chunihook/jvs.h" 11 | #include "chunihook/slider.h" 12 | 13 | #include "chuniio/chuniio.h" 14 | 15 | #include "hook/process.h" 16 | 17 | #include "hooklib/gfx.h" 18 | #include "hooklib/serial.h" 19 | #include "hooklib/spike.h" 20 | 21 | #include "platform/platform.h" 22 | 23 | #include "util/dprintf.h" 24 | 25 | static HMODULE chuni_hook_mod; 26 | static process_entry_t chuni_startup; 27 | static struct chuni_hook_config chuni_hook_cfg; 28 | 29 | static DWORD CALLBACK chuni_pre_startup(void) 30 | { 31 | HMODULE d3dc; 32 | HMODULE dbghelp; 33 | HRESULT hr; 34 | 35 | dprintf("--- Begin chuni_pre_startup ---\n"); 36 | 37 | /* Pin the D3D shader compiler. This makes startup much faster. */ 38 | 39 | d3dc = LoadLibraryW(L"D3DCompiler_43.dll"); 40 | 41 | if (d3dc != NULL) { 42 | dprintf("Pinned shader compiler, hMod=%p\n", d3dc); 43 | } else { 44 | dprintf("Failed to load shader compiler!\n"); 45 | } 46 | 47 | /* Pin dbghelp so the path hooks apply to it. */ 48 | 49 | dbghelp = LoadLibraryW(L"dbghelp.dll"); 50 | 51 | if (dbghelp != NULL) { 52 | dprintf("Pinned debug helper library, hMod=%p\n", dbghelp); 53 | } else { 54 | dprintf("Failed to load debug helper library!\n"); 55 | } 56 | 57 | /* Config load */ 58 | 59 | chuni_hook_config_load(&chuni_hook_cfg, L".\\segatools.ini"); 60 | 61 | /* Hook Win32 APIs */ 62 | 63 | gfx_hook_init(&chuni_hook_cfg.gfx, chuni_hook_mod); 64 | serial_hook_init(); 65 | 66 | /* Initialize emulation hooks */ 67 | 68 | hr = platform_hook_init( 69 | &chuni_hook_cfg.platform, 70 | "SDBT", 71 | "AAV1", 72 | chuni_hook_mod); 73 | 74 | if (FAILED(hr)) { 75 | goto fail; 76 | } 77 | 78 | hr = chuni_dll_init(&chuni_hook_cfg.dll, chuni_hook_mod); 79 | 80 | if (FAILED(hr)) { 81 | goto fail; 82 | } 83 | 84 | hr = amex_hook_init(&chuni_hook_cfg.amex, chunithm_jvs_init); 85 | 86 | if (FAILED(hr)) { 87 | goto fail; 88 | } 89 | 90 | hr = slider_hook_init(&chuni_hook_cfg.slider); 91 | 92 | if (FAILED(hr)) { 93 | goto fail; 94 | } 95 | 96 | hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, chuni_hook_mod); 97 | 98 | if (FAILED(hr)) { 99 | goto fail; 100 | } 101 | 102 | /* Initialize debug helpers */ 103 | 104 | spike_hook_init(L".\\segatools.ini"); 105 | 106 | dprintf("--- End chuni_pre_startup ---\n"); 107 | 108 | /* Jump to EXE start address */ 109 | 110 | return chuni_startup(); 111 | 112 | fail: 113 | ExitProcess(EXIT_FAILURE); 114 | } 115 | 116 | BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) 117 | { 118 | HRESULT hr; 119 | 120 | if (cause != DLL_PROCESS_ATTACH) { 121 | return TRUE; 122 | } 123 | 124 | chuni_hook_mod = mod; 125 | 126 | hr = process_hijack_startup(chuni_pre_startup, &chuni_startup); 127 | 128 | if (!SUCCEEDED(hr)) { 129 | dprintf("Failed to hijack process startup: %x\n", (int) hr); 130 | } 131 | 132 | return SUCCEEDED(hr); 133 | } 134 | -------------------------------------------------------------------------------- /chunihook/jvs.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "amex/jvs.h" 9 | 10 | #include "board/io3.h" 11 | 12 | #include "chunihook/chuni-dll.h" 13 | 14 | #include "jvs/jvs-bus.h" 15 | 16 | #include "util/dprintf.h" 17 | 18 | struct chunithm_jvs_ir_mask { 19 | uint16_t p1; 20 | uint16_t p2; 21 | }; 22 | 23 | static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out); 24 | static void chunithm_jvs_read_coin_counter( 25 | void *ctx, 26 | uint8_t slot_no, 27 | uint16_t *out); 28 | 29 | static const struct io3_ops chunithm_jvs_io3_ops = { 30 | .read_switches = chunithm_jvs_read_switches, 31 | .read_coin_counter = chunithm_jvs_read_coin_counter, 32 | }; 33 | 34 | // Incorrect IR beam mappings retained for backward compatibility 35 | static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks_v1[] = { 36 | { 0x0000, 0x0020 }, 37 | { 0x0020, 0x0000 }, 38 | { 0x0000, 0x0010 }, 39 | { 0x0010, 0x0000 }, 40 | { 0x0000, 0x0008 }, 41 | { 0x0008, 0x0000 }, 42 | }; 43 | 44 | static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks[] = { 45 | { 0x0020, 0x0000 }, 46 | { 0x0000, 0x0020 }, 47 | { 0x0010, 0x0000 }, 48 | { 0x0000, 0x0010 }, 49 | { 0x0008, 0x0000 }, 50 | { 0x0000, 0x0008 }, 51 | }; 52 | 53 | static struct io3 chunithm_jvs_io3; 54 | 55 | HRESULT chunithm_jvs_init(struct jvs_node **out) 56 | { 57 | HRESULT hr; 58 | 59 | assert(out != NULL); 60 | assert(chuni_dll.jvs_init != NULL); 61 | 62 | dprintf("JVS I/O: Starting IO backend\n"); 63 | hr = chuni_dll.jvs_init(); 64 | 65 | if (FAILED(hr)) { 66 | dprintf("JVS I/O: Backend error, I/O disconnected: %x\n", (int) hr); 67 | 68 | return hr; 69 | } 70 | 71 | io3_init(&chunithm_jvs_io3, NULL, &chunithm_jvs_io3_ops, NULL); 72 | *out = io3_to_jvs_node(&chunithm_jvs_io3); 73 | 74 | return S_OK; 75 | } 76 | 77 | static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out) 78 | { 79 | const struct chunithm_jvs_ir_mask *masks; 80 | uint8_t opbtn; 81 | uint8_t beams; 82 | size_t i; 83 | 84 | assert(out != NULL); 85 | assert(chuni_dll.jvs_poll != NULL); 86 | 87 | if (chuni_dll.api_version >= 0x0101) { 88 | // Use correct mapping 89 | masks = chunithm_jvs_ir_masks; 90 | } else { 91 | // Use backwards-compatible incorrect mapping 92 | masks = chunithm_jvs_ir_masks_v1; 93 | } 94 | 95 | opbtn = 0; 96 | beams = 0; 97 | 98 | chuni_dll.jvs_poll(&opbtn, &beams); 99 | 100 | out->system = 0x00; 101 | out->p1 = 0x0000; 102 | out->p2 = 0x0000; 103 | 104 | if (opbtn & 0x01) { 105 | out->system = 0x80; 106 | } else { 107 | out->system = 0x00; 108 | } 109 | 110 | if (opbtn & 0x02) { 111 | out->p1 |= 0x4000; 112 | } 113 | 114 | for (i = 0 ; i < 6 ; i++) { 115 | /* Beam "press" is active-low hence the ~ */ 116 | if (~beams & (1 << i)) { 117 | out->p1 |= masks[i].p1; 118 | out->p2 |= masks[i].p2; 119 | } 120 | } 121 | } 122 | 123 | static void chunithm_jvs_read_coin_counter( 124 | void *ctx, 125 | uint8_t slot_no, 126 | uint16_t *out) 127 | { 128 | assert(out != NULL); 129 | assert(chuni_dll.jvs_read_coin_counter != NULL); 130 | 131 | if (slot_no > 0) { 132 | return; 133 | } 134 | 135 | chuni_dll.jvs_read_coin_counter(out); 136 | } 137 | -------------------------------------------------------------------------------- /chunihook/jvs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "jvs/jvs-bus.h" 6 | 7 | HRESULT chunithm_jvs_init(struct jvs_node **root); 8 | -------------------------------------------------------------------------------- /chunihook/meson.build: -------------------------------------------------------------------------------- 1 | shared_library( 2 | 'chunihook', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | vs_module_defs : 'chunihook.def', 7 | c_pch : '../precompiled.h', 8 | dependencies : [ 9 | capnhook.get_variable('hook_dep'), 10 | capnhook.get_variable('hooklib_dep'), 11 | ], 12 | link_with : [ 13 | aimeio_lib, 14 | amex_lib, 15 | board_lib, 16 | chuniio_lib, 17 | hooklib_lib, 18 | jvs_lib, 19 | platform_lib, 20 | util_lib, 21 | ], 22 | sources : [ 23 | 'chuni-dll.c', 24 | 'chuni-dll.h', 25 | 'config.c', 26 | 'config.h', 27 | 'dllmain.c', 28 | 'jvs.c', 29 | 'jvs.h', 30 | 'slider.c', 31 | 'slider.h', 32 | ], 33 | ) 34 | -------------------------------------------------------------------------------- /chunihook/slider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct slider_config { 8 | bool enable; 9 | }; 10 | 11 | HRESULT slider_hook_init(const struct slider_config *cfg); 12 | -------------------------------------------------------------------------------- /chuniio/chuniio.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "chuniio/chuniio.h" 8 | #include "chuniio/config.h" 9 | 10 | static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx); 11 | 12 | static bool chuni_io_coin; 13 | static uint16_t chuni_io_coins; 14 | static uint8_t chuni_io_hand_pos; 15 | static HANDLE chuni_io_slider_thread; 16 | static bool chuni_io_slider_stop_flag; 17 | static struct chuni_io_config chuni_io_cfg; 18 | 19 | uint16_t chuni_io_get_api_version(void) 20 | { 21 | return 0x0101; 22 | } 23 | 24 | HRESULT chuni_io_jvs_init(void) 25 | { 26 | chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini"); 27 | 28 | return S_OK; 29 | } 30 | 31 | void chuni_io_jvs_read_coin_counter(uint16_t *out) 32 | { 33 | if (out == NULL) { 34 | return; 35 | } 36 | 37 | if (GetAsyncKeyState(chuni_io_cfg.vk_coin)) { 38 | if (!chuni_io_coin) { 39 | chuni_io_coin = true; 40 | chuni_io_coins++; 41 | } 42 | } else { 43 | chuni_io_coin = false; 44 | } 45 | 46 | *out = chuni_io_coins; 47 | } 48 | 49 | void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams) 50 | { 51 | size_t i; 52 | 53 | if (GetAsyncKeyState(chuni_io_cfg.vk_test)) { 54 | *opbtn |= 0x01; /* Test */ 55 | } 56 | 57 | if (GetAsyncKeyState(chuni_io_cfg.vk_service)) { 58 | *opbtn |= 0x02; /* Service */ 59 | } 60 | 61 | if (GetAsyncKeyState(chuni_io_cfg.vk_ir)) { 62 | if (chuni_io_hand_pos < 6) { 63 | chuni_io_hand_pos++; 64 | } 65 | } else { 66 | if (chuni_io_hand_pos > 0) { 67 | chuni_io_hand_pos--; 68 | } 69 | } 70 | 71 | for (i = 0 ; i < 6 ; i++) { 72 | if (chuni_io_hand_pos > i) { 73 | *beams |= (1 << i); 74 | } 75 | } 76 | } 77 | 78 | HRESULT chuni_io_slider_init(void) 79 | { 80 | return S_OK; 81 | } 82 | 83 | void chuni_io_slider_start(chuni_io_slider_callback_t callback) 84 | { 85 | if (chuni_io_slider_thread != NULL) { 86 | return; 87 | } 88 | 89 | chuni_io_slider_thread = (HANDLE) _beginthreadex( 90 | NULL, 91 | 0, 92 | chuni_io_slider_thread_proc, 93 | callback, 94 | 0, 95 | NULL); 96 | } 97 | 98 | void chuni_io_slider_stop(void) 99 | { 100 | if (chuni_io_slider_thread == NULL) { 101 | return; 102 | } 103 | 104 | chuni_io_slider_stop_flag = true; 105 | 106 | WaitForSingleObject(chuni_io_slider_thread, INFINITE); 107 | CloseHandle(chuni_io_slider_thread); 108 | chuni_io_slider_thread = NULL; 109 | chuni_io_slider_stop_flag = false; 110 | } 111 | 112 | void chuni_io_slider_set_leds(const uint8_t *rgb) 113 | { 114 | } 115 | 116 | static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx) 117 | { 118 | chuni_io_slider_callback_t callback; 119 | uint8_t pressure[32]; 120 | size_t i; 121 | 122 | callback = ctx; 123 | 124 | while (!chuni_io_slider_stop_flag) { 125 | for (i = 0 ; i < _countof(pressure) ; i++) { 126 | if (GetAsyncKeyState(chuni_io_cfg.vk_cell[i]) & 0x8000) { 127 | pressure[i] = 128; 128 | } else { 129 | pressure[i] = 0; 130 | } 131 | } 132 | 133 | callback(pressure); 134 | Sleep(1); 135 | } 136 | 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /chuniio/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "chuniio/config.h" 8 | 9 | static const int chuni_io_default_cells[] = { 10 | 'L', 'L', 'L', 'L', 11 | 'K', 'K', 'K', 'K', 12 | 'J', 'J', 'J', 'J', 13 | 'H', 'H', 'H', 'H', 14 | 'G', 'G', 'G', 'G', 15 | 'F', 'F', 'F', 'F', 16 | 'D', 'D', 'D', 'D', 17 | 'S', 'S', 'S', 'S', 18 | }; 19 | 20 | void chuni_io_config_load( 21 | struct chuni_io_config *cfg, 22 | const wchar_t *filename) 23 | { 24 | wchar_t key[16]; 25 | int i; 26 | 27 | assert(cfg != NULL); 28 | assert(filename != NULL); 29 | 30 | cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename); 31 | cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename); 32 | cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename); 33 | cfg->vk_ir = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename); 34 | 35 | for (i = 0 ; i < 32 ; i++) { 36 | swprintf_s(key, _countof(key), L"cell%i", i + 1); 37 | cfg->vk_cell[i] = GetPrivateProfileIntW( 38 | L"slider", 39 | key, 40 | chuni_io_default_cells[i], 41 | filename); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /chuniio/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct chuni_io_config { 7 | uint8_t vk_test; 8 | uint8_t vk_service; 9 | uint8_t vk_coin; 10 | uint8_t vk_ir; 11 | uint8_t vk_cell[32]; 12 | }; 13 | 14 | void chuni_io_config_load( 15 | struct chuni_io_config *cfg, 16 | const wchar_t *filename); 17 | -------------------------------------------------------------------------------- /chuniio/meson.build: -------------------------------------------------------------------------------- 1 | chuniio_lib = static_library( 2 | 'chuniio', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | c_pch : '../precompiled.h', 7 | sources : [ 8 | 'chuniio.c', 9 | 'chuniio.h', 10 | 'config.c', 11 | 'config.h', 12 | ], 13 | ) 14 | -------------------------------------------------------------------------------- /cross-mingw-32.txt: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'i686-w64-mingw32-gcc' 3 | ar = 'i686-w64-mingw32-ar' 4 | strip = 'i686-w64-mingw32-strip' 5 | 6 | [host_machine] 7 | system = 'windows' 8 | cpu_family = 'x86' 9 | cpu = 'i686' 10 | endian = 'little' 11 | -------------------------------------------------------------------------------- /cross-mingw-64.txt: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'x86_64-w64-mingw32-gcc' 3 | ar = 'x86_64-w64-mingw32-ar' 4 | strip = 'x86_64-w64-mingw32-strip' 5 | 6 | [host_machine] 7 | system = 'windows' 8 | cpu_family = 'x86_64' 9 | cpu = 'x86_64' 10 | endian = 'little' 11 | -------------------------------------------------------------------------------- /dist/chuni/segatools.ini: -------------------------------------------------------------------------------- 1 | [vfs] 2 | ; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) 3 | amfs= 4 | ; Insert the path to the game Option directory here (contains Axxx directories) 5 | option= 6 | ; Create an empty directory somewhere and insert the path here. 7 | ; This directory may be shared between multiple SEGA games. 8 | ; NOTE: This has nothing to do with Windows %APPDATA%. 9 | appdata= 10 | 11 | [dns] 12 | ; Insert the hostname or IP address of the server you wish to use here. 13 | ; Note that 127.0.0.1, localhost etc are specifically rejected. 14 | default=127.0.0.1 15 | 16 | [netenv] 17 | ; Simulate an ideal LAN environment. This may interfere with head-to-head play. 18 | ; Chunithm is extremely picky about its LAN environment, so leaving this 19 | ; setting enabled is strongly recommended. 20 | enable=1 21 | 22 | [keychip] 23 | ; The /24 LAN subnet that the emulated keychip will tell the game to expect. 24 | ; If you disable netenv then you must set this to your LAN's IP subnet, and 25 | ; that subnet must start with 192.168. 26 | subnet=192.168.100.0 27 | 28 | [gfx] 29 | ; Force the game to run windowed. 30 | windowed=1 31 | ; Add a frame to the game window if running windowed. 32 | framed=1 33 | ; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen) 34 | monitor=0 35 | 36 | ; ----------------------------------------------------------------------------- 37 | ; Input settings 38 | ; ----------------------------------------------------------------------------- 39 | 40 | ; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal 41 | ; (not prefixed with 0x) virtual-key codes, a list of which can be found here: 42 | ; 43 | ; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes 44 | ; 45 | ; This is, admittedly, not the most user-friendly configuration method in the 46 | ; world. An improved solution will be provided later. 47 | 48 | [io3] 49 | ; Test button virtual-key code. Default is the 1 key. 50 | test=0x31 51 | ; Service button virtual-key code. Default is the 2 key. 52 | service=0x32 53 | ; Keyboard button to increment coin counter. Default is the 3 key. 54 | coin=0x33 55 | 56 | ; Key bindings for each of the 32 touch cells. The default key map, depicted 57 | ; in left-to-right order, is as follows: 58 | ; 59 | ; SSSSDDDDFFFFGGGGHHHHJJJJKKKKLLLL 60 | ; 61 | ; Touch cells are numbered FROM RIGHT TO LEFT! starting from 1. This is in 62 | ; order to match the numbering used in the operator menu and service manual. 63 | ; 64 | ; Uncomment and complete the following sequence of settings to configure a 65 | ; custom high-precision touch strip controller if you have one. 66 | [slider] 67 | ;cell32=0x53 68 | ;cell31=0x53 69 | ;cell30=0x53 70 | ; ... etc ... 71 | -------------------------------------------------------------------------------- /dist/chuni/start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | pushd %~dp0 4 | 5 | start /min inject -d -k chunihook.dll aimeReaderHost.exe -p 12 6 | inject -d -k chunihook.dll chuniApp.exe 7 | taskkill /f /im aimeReaderHost.exe > nul 2>&1 8 | 9 | echo. 10 | echo Game processes have terminated 11 | pause -------------------------------------------------------------------------------- /dist/idz/start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | pushd %~dp0 4 | 5 | .\inject.exe -k .\idzhook.dll .\InitialD0_DX11_Nu.exe 6 | .\inject.exe -d -k .\idzhook.dll .\amdaemon.exe -c configDHCP_Final_Common.json configDHCP_Final_JP.json configDHCP_Final_JP_ST1.json configDHCP_Final_JP_ST2.json configDHCP_Final_EX.json configDHCP_Final_EX_ST1.json configDHCP_Final_EX_ST2.json 7 | 8 | echo. 9 | echo Game processes have terminated 10 | pause -------------------------------------------------------------------------------- /divahook/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "amex/amex.h" 6 | #include "amex/config.h" 7 | 8 | #include "board/config.h" 9 | #include "board/sg-reader.h" 10 | 11 | #include "divahook/config.h" 12 | 13 | #include "platform/config.h" 14 | #include "platform/platform.h" 15 | 16 | void diva_dll_config_load( 17 | struct diva_dll_config *cfg, 18 | const wchar_t *filename) 19 | { 20 | assert(cfg != NULL); 21 | assert(filename != NULL); 22 | 23 | GetPrivateProfileStringW( 24 | L"divaio", 25 | L"path", 26 | L"", 27 | cfg->path, 28 | _countof(cfg->path), 29 | filename); 30 | } 31 | 32 | void slider_config_load(struct slider_config *cfg, const wchar_t *filename) 33 | { 34 | assert(cfg != NULL); 35 | assert(filename != NULL); 36 | 37 | cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename); 38 | } 39 | 40 | void diva_hook_config_load( 41 | struct diva_hook_config *cfg, 42 | const wchar_t *filename) 43 | { 44 | assert(cfg != NULL); 45 | assert(filename != NULL); 46 | 47 | platform_config_load(&cfg->platform, filename); 48 | amex_config_load(&cfg->amex, filename); 49 | aime_config_load(&cfg->aime, filename); 50 | diva_dll_config_load(&cfg->dll, filename); 51 | slider_config_load(&cfg->slider, filename); 52 | } 53 | -------------------------------------------------------------------------------- /divahook/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "amex/amex.h" 6 | 7 | #include "board/sg-reader.h" 8 | 9 | #include "divahook/diva-dll.h" 10 | #include "divahook/slider.h" 11 | 12 | #include "platform/platform.h" 13 | 14 | struct diva_hook_config { 15 | struct platform_config platform; 16 | struct amex_config amex; 17 | struct aime_config aime; 18 | struct diva_dll_config dll; 19 | struct slider_config slider; 20 | }; 21 | 22 | void diva_dll_config_load( 23 | struct diva_dll_config *cfg, 24 | const wchar_t *filename); 25 | void slider_config_load(struct slider_config *cfg, const wchar_t *filename); 26 | void diva_hook_config_load( 27 | struct diva_hook_config *cfg, 28 | const wchar_t *filename); 29 | -------------------------------------------------------------------------------- /divahook/diva-dll.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "divahook/diva-dll.h" 7 | 8 | #include "util/dll-bind.h" 9 | #include "util/dprintf.h" 10 | 11 | const struct dll_bind_sym diva_dll_syms[] = { 12 | { 13 | .sym = "diva_io_jvs_init", 14 | .off = offsetof(struct diva_dll, jvs_init), 15 | }, { 16 | .sym = "diva_io_jvs_poll", 17 | .off = offsetof(struct diva_dll, jvs_poll), 18 | }, { 19 | .sym = "diva_io_jvs_read_coin_counter", 20 | .off = offsetof(struct diva_dll, jvs_read_coin_counter), 21 | }, { 22 | .sym = "diva_io_slider_init", 23 | .off = offsetof(struct diva_dll, slider_init), 24 | }, { 25 | .sym = "diva_io_slider_start", 26 | .off = offsetof(struct diva_dll, slider_start), 27 | }, { 28 | .sym = "diva_io_slider_stop", 29 | .off = offsetof(struct diva_dll, slider_stop), 30 | }, { 31 | .sym = "diva_io_slider_set_leds", 32 | .off = offsetof(struct diva_dll, slider_set_leds), 33 | } 34 | }; 35 | 36 | struct diva_dll diva_dll; 37 | 38 | // Copypasta DLL binding and diagnostic message boilerplate. 39 | // Not much of this lends itself to being easily factored out. Also there 40 | // will be a lot of API-specific branching code here eventually as new API 41 | // versions get defined, so even though these functions all look the same 42 | // now this won't remain the case forever. 43 | 44 | HRESULT diva_dll_init(const struct diva_dll_config *cfg, HINSTANCE self) 45 | { 46 | uint16_t (*get_api_version)(void); 47 | const struct dll_bind_sym *sym; 48 | HINSTANCE owned; 49 | HINSTANCE src; 50 | HRESULT hr; 51 | 52 | assert(cfg != NULL); 53 | assert(self != NULL); 54 | 55 | if (cfg->path[0] != L'\0') { 56 | owned = LoadLibraryW(cfg->path); 57 | 58 | if (owned == NULL) { 59 | hr = HRESULT_FROM_WIN32(GetLastError()); 60 | dprintf("Diva IO: Failed to load IO DLL: %lx: %S\n", 61 | hr, 62 | cfg->path); 63 | 64 | goto end; 65 | } 66 | 67 | dprintf("Diva IO: Using custom IO DLL: %S\n", cfg->path); 68 | src = owned; 69 | } else { 70 | owned = NULL; 71 | src = self; 72 | } 73 | 74 | get_api_version = (void *) GetProcAddress(src, "diva_io_get_api_version"); 75 | 76 | if (get_api_version != NULL) { 77 | diva_dll.api_version = get_api_version(); 78 | } else { 79 | diva_dll.api_version = 0x0100; 80 | dprintf("Custom IO DLL does not expose diva_io_get_api_version, " 81 | "assuming API version 1.0.\n" 82 | "Please ask the developer to update their DLL.\n"); 83 | } 84 | 85 | if (diva_dll.api_version >= 0x0200) { 86 | hr = E_NOTIMPL; 87 | dprintf("Diva IO: Custom IO DLL implements an unsupported " 88 | "API version (%#04x). Please update Segatools.\n", 89 | diva_dll.api_version); 90 | 91 | goto end; 92 | } 93 | 94 | sym = diva_dll_syms; 95 | hr = dll_bind(&diva_dll, src, &sym, _countof(diva_dll_syms)); 96 | 97 | if (FAILED(hr)) { 98 | if (src != self) { 99 | dprintf("Diva IO: Custom IO DLL does not provide function " 100 | "\"%s\". Please contact your IO DLL's developer for " 101 | "further assistance.\n", 102 | sym->sym); 103 | 104 | goto end; 105 | } else { 106 | dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); 107 | } 108 | } 109 | 110 | owned = NULL; 111 | 112 | end: 113 | if (owned != NULL) { 114 | FreeLibrary(owned); 115 | } 116 | 117 | return hr; 118 | } 119 | -------------------------------------------------------------------------------- /divahook/diva-dll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "divaio/divaio.h" 6 | 7 | struct diva_dll { 8 | uint16_t api_version; 9 | HRESULT (*jvs_init)(void); 10 | void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams); 11 | void (*jvs_read_coin_counter)(uint16_t *total); 12 | HRESULT (*slider_init)(void); 13 | void (*slider_start)(diva_io_slider_callback_t callback); 14 | void (*slider_stop)(void); 15 | void (*slider_set_leds)(const uint8_t *rgb); 16 | }; 17 | 18 | struct diva_dll_config { 19 | wchar_t path[MAX_PATH]; 20 | }; 21 | 22 | extern struct diva_dll diva_dll; 23 | 24 | HRESULT diva_dll_init(const struct diva_dll_config *cfg, HINSTANCE self); 25 | -------------------------------------------------------------------------------- /divahook/divahook.def: -------------------------------------------------------------------------------- 1 | LIBRARY divahook 2 | 3 | EXPORTS 4 | aime_io_get_api_version 5 | aime_io_init 6 | aime_io_led_set_color 7 | aime_io_nfc_get_aime_id 8 | aime_io_nfc_get_felica_id 9 | aime_io_nfc_poll 10 | amDllVideoClose @2 11 | amDllVideoGetVBiosVersion @4 12 | amDllVideoOpen @1 13 | amDllVideoSetResolution @3 14 | diva_io_get_api_version 15 | diva_io_jvs_init 16 | diva_io_jvs_poll 17 | diva_io_jvs_read_coin_counter 18 | diva_io_slider_init 19 | diva_io_slider_set_leds 20 | diva_io_slider_start 21 | diva_io_slider_stop 22 | -------------------------------------------------------------------------------- /divahook/dllmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "amex/amex.h" 6 | 7 | #include "board/sg-reader.h" 8 | 9 | #include "divahook/config.h" 10 | #include "divahook/diva-dll.h" 11 | #include "divahook/jvs.h" 12 | #include "divahook/slider.h" 13 | 14 | #include "hook/process.h" 15 | 16 | #include "hooklib/gfx.h" 17 | #include "hooklib/serial.h" 18 | #include "hooklib/spike.h" 19 | 20 | #include "platform/platform.h" 21 | 22 | #include "util/dprintf.h" 23 | 24 | static HMODULE diva_hook_mod; 25 | static process_entry_t diva_startup; 26 | static struct diva_hook_config diva_hook_cfg; 27 | 28 | static DWORD CALLBACK diva_pre_startup(void) 29 | { 30 | HRESULT hr; 31 | 32 | dprintf("--- Begin diva_pre_startup ---\n"); 33 | 34 | /* Config load */ 35 | 36 | diva_hook_config_load(&diva_hook_cfg, L".\\segatools.ini"); 37 | 38 | /* Hook Win32 APIs */ 39 | 40 | serial_hook_init(); 41 | 42 | /* Initialize emulation hooks */ 43 | 44 | hr = platform_hook_init( 45 | &diva_hook_cfg.platform, 46 | "SBZV", 47 | "AAV0", 48 | diva_hook_mod); 49 | 50 | if (FAILED(hr)) { 51 | goto fail; 52 | } 53 | 54 | hr = diva_dll_init(&diva_hook_cfg.dll, diva_hook_mod); 55 | 56 | if (FAILED(hr)) { 57 | goto fail; 58 | } 59 | 60 | hr = amex_hook_init(&diva_hook_cfg.amex, diva_jvs_init); 61 | 62 | if (FAILED(hr)) { 63 | goto fail; 64 | } 65 | 66 | hr = sg_reader_hook_init(&diva_hook_cfg.aime, 10, diva_hook_mod); 67 | 68 | if (FAILED(hr)) { 69 | goto fail; 70 | } 71 | 72 | hr = slider_hook_init(&diva_hook_cfg.slider); 73 | 74 | if (FAILED(hr)) { 75 | goto fail; 76 | } 77 | 78 | /* Initialize debug helpers */ 79 | 80 | spike_hook_init(L".\\segatools.ini"); 81 | 82 | dprintf("--- End diva_pre_startup ---\n"); 83 | 84 | /* Jump to EXE start address */ 85 | 86 | return diva_startup(); 87 | 88 | fail: 89 | ExitProcess(EXIT_FAILURE); 90 | } 91 | 92 | BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) 93 | { 94 | HRESULT hr; 95 | 96 | if (cause != DLL_PROCESS_ATTACH) { 97 | return TRUE; 98 | } 99 | 100 | diva_hook_mod = mod; 101 | 102 | hr = process_hijack_startup(diva_pre_startup, &diva_startup); 103 | 104 | if (!SUCCEEDED(hr)) { 105 | dprintf("Failed to hijack process startup: %x\n", (int) hr); 106 | } 107 | 108 | return SUCCEEDED(hr); 109 | } 110 | -------------------------------------------------------------------------------- /divahook/jvs.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "amex/jvs.h" 9 | 10 | #include "board/io3.h" 11 | 12 | #include "divahook/diva-dll.h" 13 | 14 | #include "jvs/jvs-bus.h" 15 | 16 | #include "util/dprintf.h" 17 | 18 | static void diva_jvs_read_switches(void *ctx, struct io3_switch_state *out); 19 | static void diva_jvs_read_coin_counter( 20 | void *ctx, 21 | uint8_t slot_no, 22 | uint16_t *out); 23 | 24 | static const struct io3_ops diva_jvs_io3_ops = { 25 | .read_switches = diva_jvs_read_switches, 26 | .read_coin_counter = diva_jvs_read_coin_counter, 27 | }; 28 | 29 | static struct io3 diva_jvs_io3; 30 | 31 | HRESULT diva_jvs_init(struct jvs_node **out) 32 | { 33 | HRESULT hr; 34 | 35 | assert(out != NULL); 36 | assert(diva_dll.jvs_init != NULL); 37 | 38 | dprintf("JVS I/O: Starting Diva backend DLL\n"); 39 | hr = diva_dll.jvs_init(); 40 | 41 | if (FAILED(hr)) { 42 | dprintf("JVS I/O: Backend error, I/O disconnected: %x\n", (int) hr); 43 | 44 | return hr; 45 | } 46 | 47 | io3_init(&diva_jvs_io3, NULL, &diva_jvs_io3_ops, NULL); 48 | *out = io3_to_jvs_node(&diva_jvs_io3); 49 | 50 | return S_OK; 51 | } 52 | 53 | static void diva_jvs_read_switches(void *ctx, struct io3_switch_state *out) 54 | { 55 | uint8_t opbtn; 56 | uint8_t gamebtn; 57 | 58 | assert(out != NULL); 59 | assert(diva_dll.jvs_poll != NULL); 60 | 61 | opbtn = 0; 62 | gamebtn = 0; 63 | 64 | diva_dll.jvs_poll(&opbtn, &gamebtn); 65 | 66 | if (gamebtn & 0x01) { 67 | out->p1 |= 1 << 6; 68 | } 69 | 70 | if (gamebtn & 0x02) { 71 | out->p1 |= 1 << 7; 72 | } 73 | 74 | if (gamebtn & 0x04) { 75 | out->p1 |= 1 << 8; 76 | } 77 | 78 | if (gamebtn & 0x08) { 79 | out->p1 |= 1 << 9; 80 | } 81 | 82 | if (gamebtn & 0x10) { 83 | out->p1 |= 1 << 15; 84 | } 85 | 86 | if (opbtn & 0x01) { 87 | out->system = 0x80; 88 | } else { 89 | out->system = 0; 90 | } 91 | 92 | if (opbtn & 0x02) { 93 | out->p1 |= 1 << 14; 94 | } 95 | } 96 | 97 | static void diva_jvs_read_coin_counter( 98 | void *ctx, 99 | uint8_t slot_no, 100 | uint16_t *out) 101 | { 102 | assert(diva_dll.jvs_read_coin_counter != NULL); 103 | 104 | if (slot_no > 0) { 105 | return; 106 | } 107 | 108 | diva_dll.jvs_read_coin_counter(out); 109 | } 110 | -------------------------------------------------------------------------------- /divahook/jvs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "jvs/jvs-bus.h" 6 | 7 | HRESULT diva_jvs_init(struct jvs_node **root); 8 | -------------------------------------------------------------------------------- /divahook/meson.build: -------------------------------------------------------------------------------- 1 | shared_library( 2 | 'divahook', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | vs_module_defs : 'divahook.def', 7 | c_pch : '../precompiled.h', 8 | dependencies : [ 9 | capnhook.get_variable('hook_dep'), 10 | capnhook.get_variable('hooklib_dep'), 11 | ], 12 | link_with : [ 13 | aimeio_lib, 14 | amex_lib, 15 | board_lib, 16 | divaio_lib, 17 | hooklib_lib, 18 | jvs_lib, 19 | platform_lib, 20 | util_lib, 21 | ], 22 | sources : [ 23 | 'config.c', 24 | 'config.h', 25 | 'diva-dll.c', 26 | 'diva-dll.h', 27 | 'dllmain.c', 28 | 'jvs.c', 29 | 'jvs.h', 30 | 'slider.c', 31 | 'slider.h', 32 | ], 33 | ) 34 | -------------------------------------------------------------------------------- /divahook/slider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct slider_config { 8 | bool enable; 9 | }; 10 | 11 | HRESULT slider_hook_init(const struct slider_config *cfg); 12 | -------------------------------------------------------------------------------- /divaio/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "divaio/config.h" 9 | 10 | static const int diva_io_default_buttons[] = { 11 | VK_RIGHT, VK_DOWN, VK_LEFT, VK_UP, VK_SPACE 12 | }; 13 | 14 | static const int diva_io_default_slider[] = { 15 | 'Q', 'W', 'E', 'R', 'U', 'I', 'O', 'P' 16 | }; 17 | 18 | void diva_io_config_load( 19 | struct diva_io_config *cfg, 20 | const wchar_t *filename) 21 | { 22 | wchar_t key[5]; 23 | wchar_t cell[8]; 24 | int i; 25 | int c; 26 | 27 | assert(cfg != NULL); 28 | assert(filename != NULL); 29 | 30 | cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename); 31 | cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename); 32 | cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename); 33 | 34 | for (i = 0 ; i < _countof(cfg->vk_buttons) ; i++) { 35 | swprintf_s(key, _countof(key), L"key%i", i + 1); 36 | cfg->vk_buttons[i] = GetPrivateProfileIntW( 37 | L"buttons", 38 | key, 39 | diva_io_default_buttons[i], 40 | filename); 41 | } 42 | 43 | for (c = 0 ; c < _countof(cfg->vk_slider) ; c++) { 44 | swprintf_s(cell, _countof(cell), L"cell%i", c + 1); 45 | cfg->vk_slider[c] = GetPrivateProfileIntW( 46 | L"slider", 47 | cell, 48 | diva_io_default_slider[c], 49 | filename); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /divaio/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct diva_io_config { 7 | uint8_t vk_buttons[5]; 8 | uint8_t vk_slider[8]; 9 | uint8_t vk_test; 10 | uint8_t vk_service; 11 | uint8_t vk_coin; 12 | }; 13 | 14 | void diva_io_config_load( 15 | struct diva_io_config *cfg, 16 | const wchar_t *filename); 17 | -------------------------------------------------------------------------------- /divaio/divaio.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "divaio/divaio.h" 9 | #include "divaio/config.h" 10 | 11 | static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx); 12 | 13 | static bool diva_io_coin; 14 | static uint16_t diva_io_coins; 15 | static HANDLE diva_io_slider_thread; 16 | static bool diva_io_slider_stop_flag; 17 | static struct diva_io_config diva_io_cfg; 18 | 19 | uint16_t diva_io_get_api_version(void) 20 | { 21 | return 0x0100; 22 | } 23 | 24 | HRESULT diva_io_jvs_init(void) 25 | { 26 | diva_io_config_load(&diva_io_cfg, L".\\segatools.ini"); 27 | 28 | return S_OK; 29 | } 30 | 31 | void diva_io_jvs_poll(uint8_t *opbtn_out, uint8_t *gamebtn_out) 32 | { 33 | uint8_t opbtn; 34 | uint8_t gamebtn; 35 | size_t i; 36 | 37 | opbtn = 0; 38 | 39 | if (GetAsyncKeyState(diva_io_cfg.vk_test) & 0x8000) { 40 | opbtn |= 1; 41 | } 42 | 43 | if (GetAsyncKeyState(diva_io_cfg.vk_service) & 0x8000) { 44 | opbtn |= 2; 45 | } 46 | 47 | for (i = 0 ; i < _countof(diva_io_cfg.vk_buttons) ; i++) { 48 | if (GetAsyncKeyState(diva_io_cfg.vk_buttons[i]) & 0x8000) { 49 | gamebtn |= 1 << i; 50 | } 51 | } 52 | 53 | *opbtn_out = opbtn; 54 | *gamebtn_out = gamebtn; 55 | } 56 | 57 | void diva_io_jvs_read_coin_counter(uint16_t *out) 58 | { 59 | if (out == NULL) { 60 | return; 61 | } 62 | 63 | if (GetAsyncKeyState(diva_io_cfg.vk_coin) & 0x8000) { 64 | if (!diva_io_coin) { 65 | diva_io_coin = true; 66 | diva_io_coins++; 67 | } 68 | } else { 69 | diva_io_coin = false; 70 | } 71 | 72 | *out = diva_io_coins; 73 | } 74 | 75 | HRESULT diva_io_slider_init(void) 76 | { 77 | return S_OK; 78 | } 79 | 80 | void diva_io_slider_start(diva_io_slider_callback_t callback) 81 | { 82 | if (diva_io_slider_thread != NULL) { 83 | return; 84 | } 85 | 86 | diva_io_slider_thread = (HANDLE) _beginthreadex( 87 | NULL, 88 | 0, 89 | diva_io_slider_thread_proc, 90 | callback, 91 | 0, 92 | NULL); 93 | } 94 | 95 | void diva_io_slider_stop(void) 96 | { 97 | diva_io_slider_stop_flag = true; 98 | 99 | WaitForSingleObject(diva_io_slider_thread, INFINITE); 100 | CloseHandle(diva_io_slider_thread); 101 | diva_io_slider_thread = NULL; 102 | diva_io_slider_stop_flag = false; 103 | } 104 | 105 | void diva_io_slider_set_leds(const uint8_t *rgb) 106 | {} 107 | 108 | static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx) 109 | { 110 | diva_io_slider_callback_t callback; 111 | uint8_t pressure_val; 112 | uint8_t pressure[32]; 113 | size_t i; 114 | 115 | callback = ctx; 116 | 117 | while (!diva_io_slider_stop_flag) { 118 | for (i = 0 ; i < 8 ; i++) { 119 | if (GetAsyncKeyState(diva_io_cfg.vk_slider[i]) & 0x8000) { 120 | pressure_val = 20; 121 | } else { 122 | pressure_val = 0; 123 | } 124 | 125 | memset(&pressure[4 * i], pressure_val, 4); 126 | } 127 | 128 | callback(pressure); 129 | Sleep(1); 130 | } 131 | 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /divaio/meson.build: -------------------------------------------------------------------------------- 1 | divaio_lib = static_library( 2 | 'divaio', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | c_pch : '../precompiled.h', 7 | sources : [ 8 | 'divaio.c', 9 | 'divaio.h', 10 | 'config.c', 11 | 'config.h', 12 | ], 13 | ) 14 | -------------------------------------------------------------------------------- /doc/config/chunithm.md: -------------------------------------------------------------------------------- 1 | # Chunithm configuration settings 2 | 3 | This file describes configuration settings specific to Chunithm. 4 | 5 | Keyboard binding settings use 6 | [Virtual-Key Codes](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). 7 | 8 | ## `[io3]` 9 | 10 | Cabinet specific inputs. 11 | 12 | ### `test` 13 | 14 | Default `0x31` (`1 Key`) 15 | 16 | Key-binding for cabinet test button. 17 | 18 | ### `service` 19 | 20 | Default `0x32` (`2 Key`) 21 | 22 | Key-binding for cabinet service button. 23 | 24 | ### `coin` 25 | 26 | Default `0x33` (`3 Key`) 27 | 28 | Key-binding for cabinet coin switch. 29 | 30 | ## `[slider]` 31 | 32 | Key bindings for each of the 32 touch cells. 33 | 34 | ### `cellX` 35 | 36 | Defaults to key mappings in left-to-right-order as follows: 37 | 38 | ```text 39 | SSSSDDDDFFFFGGGGHHHHJJJJKKKKLLLL 40 | ``` 41 | 42 | Key binding for a single touch cell. Replace the `X` with a value from `1` to `32` to bind keys to 43 | each cell, e.g. `cell32=0x53`, `cell1=0x53` etc. 44 | 45 | Touch cells are numbered FROM RIGHT TO LEFT! starting from 1. This is in order to match the 46 | numbering used in the operator menu and service manual. 47 | -------------------------------------------------------------------------------- /docker-build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | :: Static Environment Variables 5 | set BUILD_OUTPUT_PATH=build\docker 6 | set IMAGE_NAME=djhackers/segatools-build:latest 7 | set CONTAINER_NAME=segatools-build 8 | 9 | :: Main Execution 10 | docker build . -t %IMAGE_NAME% 11 | 12 | if ERRORLEVEL 1 ( 13 | goto failure 14 | ) 15 | 16 | docker create --name %CONTAINER_NAME% %IMAGE_NAME% 17 | 18 | if ERRORLEVEL 1 ( 19 | goto failure 20 | ) 21 | 22 | rd /s /q "!BUILD_OUTPUT_PATH!" 23 | mkdir "!BUILD_OUTPUT_PATH!" 24 | 25 | docker cp %CONTAINER_NAME%:/segatools/build/zip %BUILD_OUTPUT_PATH% 26 | 27 | docker rm -f %CONTAINER_NAME% > nul 28 | 29 | goto success 30 | 31 | :failure 32 | echo segatools Docker build FAILED! 33 | goto finish 34 | 35 | :success 36 | echo segatools Docker build completed successfully. 37 | goto finish 38 | 39 | :finish 40 | pause 41 | -------------------------------------------------------------------------------- /dummy.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareGuy/segatoolsSG/09acf4f41cdb8dc78b50051f7bdf6b5db9c6f178/dummy.txt -------------------------------------------------------------------------------- /fatehook/config.c: -------------------------------------------------------------------------------- 1 | /* SoftwareGuy/Coburn's attempt at FateGO Arcade Hook/IO for SegaTools. 2 | * This code is licensed under MIT license; don't be a thief. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | // #include "amex/amex.h" 9 | #include "amex/config.h" 10 | 11 | #include "board/config.h" 12 | #include "board/sg-reader.h" 13 | 14 | #include "hooklib/config.h" 15 | #include "hooklib/gfx.h" 16 | 17 | #include "fatehook/config.h" 18 | 19 | #include "platform/config.h" 20 | #include "platform/platform.h" 21 | 22 | // Loosely modeled after segatools teknoparrot forks' swdc hook stuff... 23 | void fatego_hook_config_load(struct fatego_hook_config *cfg, const wchar_t *filename) { 24 | assert(cfg != NULL); 25 | assert(filename != NULL); 26 | 27 | platform_config_load(&cfg->platform, filename); 28 | // amex_config_load(&cfg->amex, filename); 29 | aime_config_load(&cfg->aime, filename); 30 | gfx_config_load(&cfg->gfx, filename); 31 | } -------------------------------------------------------------------------------- /fatehook/config.h: -------------------------------------------------------------------------------- 1 | /* SoftwareGuy/Coburn's attempt at FateGO Arcade Hook/IO for SegaTools. 2 | * This code is licensed under MIT license; don't be a thief. 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | // #include "amex/amex.h" 11 | #include "board/sg-reader.h" 12 | #include "hooklib/dvd.h" 13 | 14 | // Probably not required. 15 | // #include "hooklib/dvd.h" 16 | 17 | // ... 18 | 19 | #include "platform/platform.h" 20 | 21 | struct fatego_hook_config { 22 | struct platform_config platform; 23 | // struct amex_config amex; // <- Not needed for ALLS UX series? 24 | struct aime_config aime; // AIME Card (Reader?) configuration 25 | struct gfx_config gfx; // Graphics detection hooks... AMDaemon throws an exception though... 26 | struct dvd_config dvd; // DVD Drive detection hooks 27 | // struct fatego_dll_config dll; 28 | // struct zinput_config zinput; 29 | }; 30 | 31 | void fatego_hook_config_load( 32 | struct fatego_hook_config *cfg, 33 | const wchar_t *filename); -------------------------------------------------------------------------------- /fatehook/fatehook.def: -------------------------------------------------------------------------------- 1 | LIBRARY fategohook 2 | 3 | EXPORTS 4 | aime_io_get_api_version 5 | aime_io_init 6 | aime_io_led_set_color 7 | aime_io_nfc_get_aime_id 8 | aime_io_nfc_get_felica_id 9 | aime_io_nfc_poll 10 | amDllVideoClose @2 11 | amDllVideoGetVBiosVersion @4 12 | amDllVideoOpen @1 13 | amDllVideoSetResolution @3 -------------------------------------------------------------------------------- /fatehook/meson.build: -------------------------------------------------------------------------------- 1 | shared_library( 2 | 'fatehook', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | vs_module_defs : 'fatehook.def', 7 | c_pch : '../precompiled.h', 8 | dependencies : [ 9 | capnhook.get_variable('hook_dep'), 10 | capnhook.get_variable('hooklib_dep'), 11 | ], 12 | link_with : [ 13 | aimeio_lib, 14 | amex_lib, 15 | board_lib, 16 | fateio_dll, 17 | hooklib_lib, 18 | platform_lib, 19 | util_lib, 20 | ], 21 | sources : [ 22 | 'config.c', 23 | 'config.h', 24 | 'dllmain.c', 25 | 'MinHook.h', 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /fateio/fateio.c: -------------------------------------------------------------------------------- 1 | /* SoftwareGuy/Coburn's attempt at FateGO Arcade Hook/IO for SegaTools. 2 | * This code is licensed under MIT license; don't be a thief. 3 | */ 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "fateio/fateio.h" 12 | 13 | #include "util/dprintf.h" 14 | 15 | // TODO: Better implementation. 16 | 17 | HRESULT fatego_io_init(void) 18 | { 19 | dprintf("FateIO Hooks initialized!\n"); 20 | return S_OK; 21 | } 22 | 23 | HRESULT fatego_io_poll(void) 24 | { 25 | // Stubbed. I have no idea what to put here. 26 | return S_OK; 27 | } -------------------------------------------------------------------------------- /fateio/fateio.def: -------------------------------------------------------------------------------- 1 | LIBRARY fateio -------------------------------------------------------------------------------- /fateio/fateio.h: -------------------------------------------------------------------------------- 1 | /* SoftwareGuy/Coburn's attempt at FateGO Arcade Hook/IO for SegaTools. 2 | * This code is licensed under MIT license; don't be a thief. 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | // Operator buttons. 11 | enum { 12 | fatego_IO_OPBTN_TEST = 0x01, 13 | fatego_IO_OPBTN_SERVICE = 0x02, 14 | }; 15 | 16 | HRESULT fatego_io_init(void); 17 | 18 | HRESULT fatego_io_poll(void); -------------------------------------------------------------------------------- /fateio/meson.build: -------------------------------------------------------------------------------- 1 | fateio_dll = shared_library( 2 | 'fateio', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | vs_module_defs : 'fateio.def', 7 | c_pch : '../precompiled.h', 8 | dependencies : [ 9 | xinput_lib, 10 | ], 11 | link_with : [ 12 | util_lib, 13 | ], 14 | sources : [ 15 | 'fateio.c', 16 | 'fateio.h', 17 | ], 18 | ) 19 | -------------------------------------------------------------------------------- /hooklib/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "hooklib/config.h" 8 | #include "hooklib/gfx.h" 9 | #include "hooklib/dvd.h" 10 | 11 | void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename) 12 | { 13 | assert(cfg != NULL); 14 | assert(filename != NULL); 15 | 16 | cfg->enable = GetPrivateProfileIntW(L"gfx", L"enable", 1, filename); 17 | cfg->windowed = GetPrivateProfileIntW(L"gfx", L"windowed", 0, filename); 18 | cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename); 19 | cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename); 20 | } 21 | 22 | void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename) 23 | { 24 | assert(cfg != NULL); 25 | assert(filename != NULL); 26 | 27 | cfg->enable = GetPrivateProfileIntW(L"dvd", L"enable", 1, filename); 28 | } 29 | -------------------------------------------------------------------------------- /hooklib/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "hooklib/gfx.h" 7 | #include "hooklib/dvd.h" 8 | 9 | void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename); 10 | void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename); 11 | -------------------------------------------------------------------------------- /hooklib/dll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | HRESULT dll_hook_push( 7 | HMODULE redir_mod, 8 | const wchar_t *name); 9 | -------------------------------------------------------------------------------- /hooklib/dns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | // if to_src is NULL, all lookups for from_src will fail 8 | HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src); 9 | 10 | -------------------------------------------------------------------------------- /hooklib/dvd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "hook/com-proxy.h" 8 | #include "hook/table.h" 9 | 10 | #include "hooklib/config.h" 11 | #include "hooklib/dll.h" 12 | #include "hooklib/dvd.h" 13 | 14 | #include "util/dprintf.h" 15 | 16 | /* API hooks */ 17 | 18 | static DWORD WINAPI hook_QueryDosDeviceW( 19 | const wchar_t *lpDeviceName, 20 | wchar_t *lpTargetPath, 21 | DWORD ucchMax); 22 | 23 | /* Link pointers */ 24 | 25 | static DWORD (WINAPI *next_QueryDosDeviceW)( 26 | const wchar_t *lpDeviceName, 27 | wchar_t *lpTargetPath, 28 | DWORD ucchMax); 29 | 30 | static bool dvd_hook_initted; 31 | static struct dvd_config dvd_config; 32 | 33 | static const struct hook_symbol dvd_hooks[] = { 34 | { 35 | .name = "QueryDosDeviceW", 36 | .patch = hook_QueryDosDeviceW, 37 | .link = (void **) &next_QueryDosDeviceW 38 | }, 39 | }; 40 | 41 | void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self) 42 | { 43 | assert(cfg != NULL); 44 | 45 | if (!cfg->enable) { 46 | return; 47 | } 48 | 49 | if (dvd_hook_initted) { 50 | return; 51 | } 52 | 53 | dvd_hook_initted = true; 54 | 55 | memcpy(&dvd_config, cfg, sizeof(*cfg)); 56 | hook_table_apply(NULL, "kernel32.dll", dvd_hooks, _countof(dvd_hooks)); 57 | dprintf("DVD: hook enabled.\n"); 58 | } 59 | 60 | DWORD WINAPI hook_QueryDosDeviceW( 61 | const wchar_t *lpDeviceName, 62 | wchar_t *lpTargetPath, 63 | DWORD ucchMax) 64 | { 65 | DWORD ok; 66 | wchar_t *p_dest; 67 | wchar_t *dvd_string = L"CdRom"; 68 | 69 | ok = next_QueryDosDeviceW( 70 | lpDeviceName, 71 | lpTargetPath, 72 | ucchMax); 73 | 74 | p_dest = wcsstr (lpTargetPath, dvd_string); 75 | 76 | if ( p_dest != NULL ) { 77 | dprintf("DVD: Hiding DVD drive.\n"); 78 | return 0; 79 | } 80 | 81 | return ok; 82 | } 83 | -------------------------------------------------------------------------------- /hooklib/dvd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct dvd_config { 8 | bool enable; 9 | }; 10 | 11 | /* Init is not thread safe because API hook init is not thread safe blah 12 | blah blah you know the drill by now. */ 13 | 14 | void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self); 15 | -------------------------------------------------------------------------------- /hooklib/fdshark.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | enum { 9 | FDSHARK_FORCE_SYNC = 0x1, 10 | FDSHARK_TRACE_READ = 0x2, 11 | FDSHARK_TRACE_WRITE = 0x4, 12 | FDSHARK_TRACE_IOCTL = 0x8, 13 | FDSHARK_ALL_FLAGS_ = 0xF, 14 | }; 15 | 16 | HRESULT fdshark_hook_init(const wchar_t *filename, int flags); 17 | -------------------------------------------------------------------------------- /hooklib/gfx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct gfx_config { 8 | bool enable; 9 | bool windowed; 10 | bool framed; 11 | int monitor; 12 | }; 13 | 14 | void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self); 15 | -------------------------------------------------------------------------------- /hooklib/meson.build: -------------------------------------------------------------------------------- 1 | hooklib_lib = static_library( 2 | 'hooklib', 3 | include_directories : inc, 4 | implicit_include_directories : false, 5 | c_pch : '../precompiled.h', 6 | dependencies : [ 7 | capnhook.get_variable('hook_dep'), 8 | ], 9 | sources : [ 10 | 'config.c', 11 | 'config.h', 12 | 'dll.c', 13 | 'dll.h', 14 | 'dns.c', 15 | 'dns.h', 16 | 'dvd.c', 17 | 'dvd.h', 18 | 'fdshark.c', 19 | 'fdshark.h', 20 | 'gfx.c', 21 | 'gfx.h', 22 | 'path.c', 23 | 'path.h', 24 | 'reg.c', 25 | 'reg.h', 26 | 'setupapi.c', 27 | 'setupapi.h', 28 | 'spike.c', 29 | 'spike.h', 30 | ], 31 | ) 32 | -------------------------------------------------------------------------------- /hooklib/path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | typedef HRESULT (*path_hook_t)( 9 | const wchar_t *src, 10 | wchar_t *dest, 11 | size_t *count); 12 | 13 | HRESULT path_hook_push(path_hook_t hook); 14 | void path_hook_insert_hooks(HMODULE target); 15 | int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count); 16 | 17 | static inline bool path_is_separator_w(wchar_t c) 18 | { 19 | return c == L'\\' || c == L'/'; 20 | } 21 | -------------------------------------------------------------------------------- /hooklib/reg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct reg_hook_val { 9 | const wchar_t *name; 10 | HRESULT (*read)(void *bytes, uint32_t *nbytes); 11 | HRESULT (*write)(const void *bytes, uint32_t nbytes); 12 | uint32_t type; 13 | }; 14 | 15 | HRESULT reg_hook_push_key( 16 | HKEY root, 17 | const wchar_t *name, 18 | const struct reg_hook_val *vals, 19 | size_t nvals); 20 | 21 | HRESULT reg_hook_read_bin( 22 | void *bytes, 23 | uint32_t *nbytes, 24 | const void *src_bytes, 25 | size_t src_nbytes); 26 | 27 | HRESULT reg_hook_read_u32( 28 | void *bytes, 29 | uint32_t *nbytes, 30 | uint32_t src); 31 | 32 | HRESULT reg_hook_read_wstr( 33 | void *bytes, 34 | uint32_t *nbytes, 35 | const wchar_t *src); 36 | -------------------------------------------------------------------------------- /hooklib/setupapi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | HRESULT setupapi_add_phantom_dev(const GUID *iface_class, const wchar_t *path); 8 | -------------------------------------------------------------------------------- /hooklib/spike.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void spike_hook_init(const wchar_t *ini_file); 6 | -------------------------------------------------------------------------------- /iccard/aime.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "iccard/aime.h" 6 | #include "iccard/mifare.h" 7 | 8 | #include "util/dprintf.h" 9 | 10 | HRESULT aime_card_populate( 11 | struct mifare *mifare, 12 | const uint8_t *luid, 13 | size_t nbytes) 14 | { 15 | uint8_t b; 16 | size_t i; 17 | 18 | assert(mifare != NULL); 19 | assert(luid != NULL); 20 | 21 | memset(mifare, 0, sizeof(*mifare)); 22 | 23 | if (nbytes != 10) { 24 | dprintf("AiMe IC: LUID must be 10 bytes\n"); 25 | 26 | return E_INVALIDARG; 27 | } 28 | 29 | for (i = 0 ; i < 10 ; i++) { 30 | b = luid[i]; 31 | 32 | if ((b & 0xF0) > 0x90 || (b & 0x0F) > 0x09) { 33 | dprintf("AiMe IC: LUID must be binary-coded decimal\n"); 34 | return E_INVALIDARG; 35 | } 36 | 37 | mifare->sectors[0].blocks[2].bytes[6 + i] = b; 38 | } 39 | 40 | /* TODO An authentic Aime pass has a checksum of the LUID in the last few 41 | bytes of block 1. The output of this function fails authenticity check 42 | in its current form. */ 43 | 44 | dprintf("AiMe IC: WARNING: Authenticity hash not yet implemented!\n"); 45 | 46 | return S_OK; 47 | } 48 | -------------------------------------------------------------------------------- /iccard/aime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "iccard/mifare.h" 9 | 10 | HRESULT aime_card_populate( 11 | struct mifare *mifare, 12 | const uint8_t *luid, 13 | size_t nbytes); 14 | -------------------------------------------------------------------------------- /iccard/felica.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "hook/iobuf.h" 9 | 10 | enum { 11 | FELICA_CMD_POLL = 0x00, 12 | FELICA_CMD_GET_SYSTEM_CODE = 0x0c, 13 | FELICA_CMD_NDA_A4 = 0xa4, 14 | }; 15 | 16 | struct felica { 17 | uint64_t IDm; 18 | uint64_t PMm; 19 | uint16_t system_code; 20 | }; 21 | 22 | HRESULT felica_transact( 23 | struct felica *f, 24 | struct const_iobuf *req, 25 | struct iobuf *res); 26 | 27 | uint64_t felica_get_generic_PMm(void); 28 | -------------------------------------------------------------------------------- /iccard/meson.build: -------------------------------------------------------------------------------- 1 | iccard_lib = static_library( 2 | 'iccard', 3 | include_directories : inc, 4 | implicit_include_directories : false, 5 | c_pch : '../precompiled.h', 6 | dependencies : [ 7 | capnhook.get_variable('hook_dep'), 8 | ], 9 | sources : [ 10 | 'aime.c', 11 | 'aime.h', 12 | 'felica.c', 13 | 'felica.h', 14 | 'mifare.h', 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /iccard/mifare.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct mifare_block { 6 | uint8_t bytes[16]; 7 | }; 8 | 9 | struct mifare_sector { 10 | struct mifare_block blocks[4]; 11 | }; 12 | 13 | struct mifare { 14 | struct mifare_sector sectors[16]; 15 | }; 16 | -------------------------------------------------------------------------------- /idzhook/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "amex/amex.h" 5 | #include "amex/config.h" 6 | 7 | #include "board/config.h" 8 | #include "board/sg-reader.h" 9 | 10 | #include "hooklib/config.h" 11 | #include "hooklib/dvd.h" 12 | 13 | #include "idzhook/config.h" 14 | #include "idzhook/idz-dll.h" 15 | 16 | #include "platform/config.h" 17 | #include "platform/platform.h" 18 | 19 | void idz_dll_config_load( 20 | struct idz_dll_config *cfg, 21 | const wchar_t *filename) 22 | { 23 | assert(cfg != NULL); 24 | assert(filename != NULL); 25 | 26 | GetPrivateProfileStringW( 27 | L"idzio", 28 | L"path", 29 | L"", 30 | cfg->path, 31 | _countof(cfg->path), 32 | filename); 33 | } 34 | 35 | void idz_hook_config_load( 36 | struct idz_hook_config *cfg, 37 | const wchar_t *filename) 38 | { 39 | assert(cfg != NULL); 40 | assert(filename != NULL); 41 | 42 | platform_config_load(&cfg->platform, filename); 43 | amex_config_load(&cfg->amex, filename); 44 | aime_config_load(&cfg->aime, filename); 45 | idz_dll_config_load(&cfg->dll, filename); 46 | zinput_config_load(&cfg->zinput, filename); 47 | dvd_config_load(&cfg->dvd, filename); 48 | } 49 | 50 | void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename) 51 | { 52 | assert(cfg != NULL); 53 | assert(filename != NULL); 54 | 55 | cfg->enable = GetPrivateProfileIntW(L"zinput", L"enable", 1, filename); 56 | } 57 | -------------------------------------------------------------------------------- /idzhook/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "amex/amex.h" 7 | 8 | #include "board/sg-reader.h" 9 | 10 | #include "hooklib/dvd.h" 11 | 12 | #include "idzhook/idz-dll.h" 13 | #include "idzhook/zinput.h" 14 | 15 | #include "platform/platform.h" 16 | 17 | struct idz_hook_config { 18 | struct platform_config platform; 19 | struct amex_config amex; 20 | struct aime_config aime; 21 | struct dvd_config dvd; 22 | struct idz_dll_config dll; 23 | struct zinput_config zinput; 24 | }; 25 | 26 | void idz_dll_config_load( 27 | struct idz_dll_config *cfg, 28 | const wchar_t *filename); 29 | 30 | void idz_hook_config_load( 31 | struct idz_hook_config *cfg, 32 | const wchar_t *filename); 33 | 34 | void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename); 35 | -------------------------------------------------------------------------------- /idzhook/dllmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "amex/amex.h" 6 | 7 | #include "board/sg-reader.h" 8 | 9 | #include "hook/process.h" 10 | 11 | #include "hooklib/dvd.h" 12 | #include "hooklib/serial.h" 13 | #include "hooklib/spike.h" 14 | 15 | #include "idzhook/config.h" 16 | #include "idzhook/idz-dll.h" 17 | #include "idzhook/jvs.h" 18 | #include "idzhook/zinput.h" 19 | 20 | #include "platform/platform.h" 21 | 22 | #include "util/dprintf.h" 23 | 24 | static HMODULE idz_hook_mod; 25 | static process_entry_t idz_startup; 26 | static struct idz_hook_config idz_hook_cfg; 27 | 28 | static DWORD CALLBACK idz_pre_startup(void) 29 | { 30 | HRESULT hr; 31 | 32 | dprintf("--- Begin idz_pre_startup ---\n"); 33 | 34 | /* Config load */ 35 | 36 | idz_hook_config_load(&idz_hook_cfg, L".\\segatools.ini"); 37 | 38 | /* Hook Win32 APIs */ 39 | 40 | serial_hook_init(); 41 | zinput_hook_init(&idz_hook_cfg.zinput); 42 | dvd_hook_init(&idz_hook_cfg.dvd, idz_hook_mod); 43 | 44 | /* Initialize emulation hooks */ 45 | 46 | hr = platform_hook_init( 47 | &idz_hook_cfg.platform, 48 | "SDDF", 49 | "AAV2", 50 | idz_hook_mod); 51 | 52 | if (FAILED(hr)) { 53 | goto fail; 54 | } 55 | 56 | hr = idz_dll_init(&idz_hook_cfg.dll, idz_hook_mod); 57 | 58 | if (FAILED(hr)) { 59 | goto fail; 60 | } 61 | 62 | hr = amex_hook_init(&idz_hook_cfg.amex, idz_jvs_init); 63 | 64 | if (FAILED(hr)) { 65 | goto fail; 66 | } 67 | 68 | hr = sg_reader_hook_init(&idz_hook_cfg.aime, 10, idz_hook_mod); 69 | 70 | if (FAILED(hr)) { 71 | goto fail; 72 | } 73 | 74 | /* Initialize debug helpers */ 75 | 76 | spike_hook_init(L".\\segatools.ini"); 77 | 78 | dprintf("--- End idz_pre_startup ---\n"); 79 | 80 | /* Jump to EXE start address */ 81 | 82 | return idz_startup(); 83 | 84 | fail: 85 | ExitProcess(EXIT_FAILURE); 86 | } 87 | 88 | BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) 89 | { 90 | HRESULT hr; 91 | 92 | if (cause != DLL_PROCESS_ATTACH) { 93 | return TRUE; 94 | } 95 | 96 | idz_hook_mod = mod; 97 | 98 | hr = process_hijack_startup(idz_pre_startup, &idz_startup); 99 | 100 | if (!SUCCEEDED(hr)) { 101 | dprintf("Failed to hijack process startup: %x\n", (int) hr); 102 | } 103 | 104 | return SUCCEEDED(hr); 105 | } 106 | -------------------------------------------------------------------------------- /idzhook/idz-dll.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "idzhook/idz-dll.h" 7 | 8 | #include "util/dll-bind.h" 9 | #include "util/dprintf.h" 10 | 11 | const struct dll_bind_sym idz_dll_syms[] = { 12 | { 13 | .sym = "idz_io_jvs_init", 14 | .off = offsetof(struct idz_dll, jvs_init), 15 | }, { 16 | .sym = "idz_io_jvs_read_analogs", 17 | .off = offsetof(struct idz_dll, jvs_read_analogs), 18 | }, { 19 | .sym = "idz_io_jvs_read_buttons", 20 | .off = offsetof(struct idz_dll, jvs_read_buttons), 21 | }, { 22 | .sym = "idz_io_jvs_read_shifter", 23 | .off = offsetof(struct idz_dll, jvs_read_shifter), 24 | }, { 25 | .sym = "idz_io_jvs_read_coin_counter", 26 | .off = offsetof(struct idz_dll, jvs_read_coin_counter), 27 | } 28 | }; 29 | 30 | struct idz_dll idz_dll; 31 | 32 | // Copypasta DLL binding and diagnostic message boilerplate. 33 | // Not much of this lends itself to being easily factored out. Also there 34 | // will be a lot of API-specific branching code here eventually as new API 35 | // versions get defined, so even though these functions all look the same 36 | // now this won't remain the case forever. 37 | 38 | HRESULT idz_dll_init(const struct idz_dll_config *cfg, HINSTANCE self) 39 | { 40 | uint16_t (*get_api_version)(void); 41 | const struct dll_bind_sym *sym; 42 | HINSTANCE owned; 43 | HINSTANCE src; 44 | HRESULT hr; 45 | 46 | assert(cfg != NULL); 47 | assert(self != NULL); 48 | 49 | if (cfg->path[0] != L'\0') { 50 | owned = LoadLibraryW(cfg->path); 51 | 52 | if (owned == NULL) { 53 | hr = HRESULT_FROM_WIN32(GetLastError()); 54 | dprintf("IDZ IO: Failed to load IO DLL: %lx: %S\n", 55 | hr, 56 | cfg->path); 57 | 58 | goto end; 59 | } 60 | 61 | dprintf("IDZ IO: Using custom IO DLL: %S\n", cfg->path); 62 | src = owned; 63 | } else { 64 | owned = NULL; 65 | src = self; 66 | } 67 | 68 | get_api_version = (void *) GetProcAddress(src, "idz_io_get_api_version"); 69 | 70 | if (get_api_version != NULL) { 71 | idz_dll.api_version = get_api_version(); 72 | } else { 73 | idz_dll.api_version = 0x0100; 74 | dprintf("Custom IO DLL does not expose idz_io_get_api_version, " 75 | "assuming API version 1.0.\n" 76 | "Please ask the developer to update their DLL.\n"); 77 | } 78 | 79 | if (idz_dll.api_version >= 0x0200) { 80 | hr = E_NOTIMPL; 81 | dprintf("IDZ IO: Custom IO DLL implements an unsupported " 82 | "API version (%#04x). Please update Segatools.\n", 83 | idz_dll.api_version); 84 | 85 | goto end; 86 | } 87 | 88 | sym = idz_dll_syms; 89 | hr = dll_bind(&idz_dll, src, &sym, _countof(idz_dll_syms)); 90 | 91 | if (FAILED(hr)) { 92 | if (src != self) { 93 | dprintf("IDZ IO: Custom IO DLL does not provide function " 94 | "\"%s\". Please contact your IO DLL's developer for " 95 | "further assistance.\n", 96 | sym->sym); 97 | 98 | goto end; 99 | } else { 100 | dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); 101 | } 102 | } 103 | 104 | owned = NULL; 105 | 106 | end: 107 | if (owned != NULL) { 108 | FreeLibrary(owned); 109 | } 110 | 111 | return hr; 112 | } 113 | -------------------------------------------------------------------------------- /idzhook/idz-dll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "idzio/idzio.h" 6 | 7 | struct idz_dll { 8 | uint16_t api_version; 9 | HRESULT (*jvs_init)(void); 10 | void (*jvs_read_analogs)(struct idz_io_analog_state *out); 11 | void (*jvs_read_buttons)(uint8_t *opbtn, uint8_t *gamebtn); 12 | void (*jvs_read_shifter)(uint8_t *gear); 13 | void (*jvs_read_coin_counter)(uint16_t *total); 14 | }; 15 | 16 | struct idz_dll_config { 17 | wchar_t path[MAX_PATH]; 18 | }; 19 | 20 | extern struct idz_dll idz_dll; 21 | 22 | HRESULT idz_dll_init(const struct idz_dll_config *cfg, HINSTANCE self); 23 | -------------------------------------------------------------------------------- /idzhook/idzhook.def: -------------------------------------------------------------------------------- 1 | LIBRARY idzhook 2 | 3 | EXPORTS 4 | aime_io_get_api_version 5 | aime_io_init 6 | aime_io_led_set_color 7 | aime_io_nfc_get_aime_id 8 | aime_io_nfc_get_felica_id 9 | aime_io_nfc_poll 10 | amDllVideoClose @2 11 | amDllVideoGetVBiosVersion @4 12 | amDllVideoOpen @1 13 | amDllVideoSetResolution @3 14 | idz_io_jvs_init 15 | idz_io_jvs_read_analogs 16 | idz_io_jvs_read_buttons 17 | idz_io_jvs_read_coin_counter 18 | idz_io_jvs_read_shifter 19 | -------------------------------------------------------------------------------- /idzhook/jvs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "jvs/jvs-bus.h" 6 | 7 | HRESULT idz_jvs_init(struct jvs_node **root); 8 | -------------------------------------------------------------------------------- /idzhook/meson.build: -------------------------------------------------------------------------------- 1 | shared_library( 2 | 'idzhook', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | vs_module_defs : 'idzhook.def', 7 | c_pch : '../precompiled.h', 8 | dependencies : [ 9 | capnhook.get_variable('hook_dep'), 10 | capnhook.get_variable('hooklib_dep'), 11 | xinput_lib, 12 | ], 13 | link_with : [ 14 | aimeio_lib, 15 | amex_lib, 16 | board_lib, 17 | hooklib_lib, 18 | idzio_lib, 19 | jvs_lib, 20 | platform_lib, 21 | util_lib, 22 | ], 23 | sources : [ 24 | 'config.c', 25 | 'config.h', 26 | 'dllmain.c', 27 | 'idz-dll.c', 28 | 'idz-dll.h', 29 | 'jvs.c', 30 | 'jvs.h', 31 | 'zinput.c', 32 | 'zinput.h', 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /idzhook/zinput.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct zinput_config { 8 | bool enable; 9 | }; 10 | 11 | HRESULT zinput_hook_init(struct zinput_config *cfg); 12 | -------------------------------------------------------------------------------- /idzio/backend.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "idzio/idzio.h" 6 | 7 | struct idz_io_backend { 8 | void (*jvs_read_buttons)(uint8_t *gamebtn); 9 | void (*jvs_read_shifter)(uint8_t *gear); 10 | void (*jvs_read_analogs)(struct idz_io_analog_state *state); 11 | }; 12 | -------------------------------------------------------------------------------- /idzio/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct idz_shifter_config { 8 | bool auto_neutral; 9 | }; 10 | 11 | struct idz_di_config { 12 | wchar_t device_name[64]; 13 | wchar_t shifter_name[64]; 14 | wchar_t brake_axis[16]; 15 | wchar_t accel_axis[16]; 16 | uint8_t start; 17 | uint8_t view_chg; 18 | uint8_t shift_dn; 19 | uint8_t shift_up; 20 | uint8_t gear[6]; 21 | bool reverse_brake_axis; 22 | bool reverse_accel_axis; 23 | }; 24 | 25 | struct idz_xi_config { 26 | bool single_stick_steering; 27 | }; 28 | 29 | struct idz_io_config { 30 | uint8_t vk_test; 31 | uint8_t vk_service; 32 | uint8_t vk_coin; 33 | wchar_t mode[8]; 34 | int restrict_; 35 | struct idz_shifter_config shifter; 36 | struct idz_di_config di; 37 | struct idz_xi_config xi; 38 | }; 39 | 40 | void idz_di_config_load(struct idz_di_config *cfg, const wchar_t *filename); 41 | void idz_xi_config_load(struct idz_xi_config *cfg, const wchar_t *filename); 42 | void idz_io_config_load(struct idz_io_config *cfg, const wchar_t *filename); 43 | void idz_shifter_config_load( 44 | struct idz_shifter_config *cfg, 45 | const wchar_t *filename); 46 | -------------------------------------------------------------------------------- /idzio/di-dev.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | union idz_di_state { 9 | DIJOYSTATE st; 10 | uint8_t bytes[sizeof(DIJOYSTATE)]; 11 | }; 12 | 13 | HRESULT idz_di_dev_start(IDirectInputDevice8W *dev, HWND wnd); 14 | void idz_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out); 15 | HRESULT idz_di_dev_poll( 16 | IDirectInputDevice8W *dev, 17 | HWND wnd, 18 | union idz_di_state *out); 19 | 20 | -------------------------------------------------------------------------------- /idzio/di.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "idzio/backend.h" 4 | #include "idzio/config.h" 5 | 6 | HRESULT idz_di_init( 7 | const struct idz_di_config *cfg, 8 | HINSTANCE inst, 9 | const struct idz_io_backend **backend); 10 | -------------------------------------------------------------------------------- /idzio/dllmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "idzio/backend.h" 8 | #include "idzio/config.h" 9 | #include "idzio/di.h" 10 | #include "idzio/idzio.h" 11 | #include "idzio/xi.h" 12 | 13 | #include "util/dprintf.h" 14 | #include "util/str.h" 15 | 16 | static struct idz_io_config idz_io_cfg; 17 | static const struct idz_io_backend *idz_io_backend; 18 | static bool idz_io_coin; 19 | static uint16_t idz_io_coins; 20 | 21 | uint16_t idz_io_get_api_version(void) 22 | { 23 | return 0x0100; 24 | } 25 | 26 | HRESULT idz_io_jvs_init(void) 27 | { 28 | HINSTANCE inst; 29 | HRESULT hr; 30 | 31 | assert(idz_io_backend == NULL); 32 | 33 | inst = GetModuleHandleW(NULL); 34 | 35 | if (inst == NULL) { 36 | hr = HRESULT_FROM_WIN32(GetLastError()); 37 | dprintf("GetModuleHandleW failed: %lx\n", hr); 38 | 39 | return hr; 40 | } 41 | 42 | idz_io_config_load(&idz_io_cfg, L".\\segatools.ini"); 43 | 44 | if (wstr_ieq(idz_io_cfg.mode, L"dinput")) { 45 | hr = idz_di_init(&idz_io_cfg.di, inst, &idz_io_backend); 46 | } else if (wstr_ieq(idz_io_cfg.mode, L"xinput")) { 47 | hr = idz_xi_init(&idz_io_cfg.xi, &idz_io_backend); 48 | } else { 49 | hr = E_INVALIDARG; 50 | dprintf("IDZ IO: Invalid IO mode \"%S\", use dinput or xinput\n", 51 | idz_io_cfg.mode); 52 | } 53 | 54 | return hr; 55 | } 56 | 57 | void idz_io_jvs_read_buttons(uint8_t *opbtn_out, uint8_t *gamebtn_out) 58 | { 59 | uint8_t opbtn; 60 | 61 | assert(idz_io_backend != NULL); 62 | assert(opbtn_out != NULL); 63 | assert(gamebtn_out != NULL); 64 | 65 | opbtn = 0; 66 | 67 | if (GetAsyncKeyState(idz_io_cfg.vk_test) & 0x8000) { 68 | opbtn |= IDZ_IO_OPBTN_TEST; 69 | } 70 | 71 | if (GetAsyncKeyState(idz_io_cfg.vk_service) & 0x8000) { 72 | opbtn |= IDZ_IO_OPBTN_SERVICE; 73 | } 74 | 75 | *opbtn_out = opbtn; 76 | 77 | idz_io_backend->jvs_read_buttons(gamebtn_out); 78 | } 79 | 80 | void idz_io_jvs_read_shifter(uint8_t *gear) 81 | { 82 | assert(gear != NULL); 83 | assert(idz_io_backend != NULL); 84 | 85 | idz_io_backend->jvs_read_shifter(gear); 86 | } 87 | 88 | void idz_io_jvs_read_analogs(struct idz_io_analog_state *out) 89 | { 90 | struct idz_io_analog_state tmp; 91 | 92 | assert(out != NULL); 93 | assert(idz_io_backend != NULL); 94 | 95 | idz_io_backend->jvs_read_analogs(&tmp); 96 | 97 | /* Apply steering wheel restriction. Real cabs only report about 77% of 98 | the IO-3's max ADC output value when the wheel is turned to either of 99 | its maximum positions. To match this behavior we set the default value 100 | for the wheel restriction config parameter to 97 (out of 128). This 101 | scaling factor is applied using fixed-point arithmetic below. */ 102 | 103 | out->wheel = (tmp.wheel * idz_io_cfg.restrict_) / 128; 104 | out->accel = tmp.accel; 105 | out->brake = tmp.brake; 106 | } 107 | 108 | void idz_io_jvs_read_coin_counter(uint16_t *out) 109 | { 110 | assert(out != NULL); 111 | 112 | /* Coin counter is not backend-specific */ 113 | 114 | if ( idz_io_cfg.vk_coin && 115 | (GetAsyncKeyState(idz_io_cfg.vk_coin) & 0x8000)) { 116 | if (!idz_io_coin) { 117 | idz_io_coin = true; 118 | idz_io_coins++; 119 | } 120 | } else { 121 | idz_io_coin = false; 122 | } 123 | 124 | *out = idz_io_coins; 125 | } 126 | -------------------------------------------------------------------------------- /idzio/idzio.def: -------------------------------------------------------------------------------- 1 | LIBRARY idzio 2 | 3 | EXPORTS 4 | idz_io_jvs_init 5 | idz_io_jvs_read_analogs 6 | idz_io_jvs_read_buttons 7 | idz_io_jvs_read_coin_counter 8 | idz_io_jvs_read_shifter 9 | -------------------------------------------------------------------------------- /idzio/meson.build: -------------------------------------------------------------------------------- 1 | idzio_lib = static_library( 2 | 'idzio', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | c_pch : '../precompiled.h', 7 | dependencies : [ 8 | dinput8_lib, 9 | dxguid_lib, 10 | xinput_lib, 11 | ], 12 | link_with : [ 13 | util_lib, 14 | ], 15 | sources : [ 16 | 'backend.h', 17 | 'config.c', 18 | 'config.h', 19 | 'di.c', 20 | 'di.h', 21 | 'di-dev.c', 22 | 'di-dev.h', 23 | 'dllmain.c', 24 | 'idzio.h', 25 | 'shifter.c', 26 | 'shifter.h', 27 | 'wnd.c', 28 | 'wnd.h', 29 | 'xi.c', 30 | 'xi.h', 31 | ], 32 | ) 33 | -------------------------------------------------------------------------------- /idzio/shifter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "idzio/shifter.h" 5 | 6 | static bool idz_shifter_shifting; 7 | static uint8_t idz_shifter_gear; 8 | 9 | void idz_shifter_reset(void) 10 | { 11 | idz_shifter_gear = 0; 12 | } 13 | 14 | void idz_shifter_update(bool shift_dn, bool shift_up) 15 | { 16 | if (!idz_shifter_shifting) { 17 | if (shift_dn && idz_shifter_gear > 0) { 18 | idz_shifter_gear--; 19 | } 20 | 21 | if (shift_up && idz_shifter_gear < 6) { 22 | idz_shifter_gear++; 23 | } 24 | } 25 | 26 | idz_shifter_shifting = shift_dn || shift_up; 27 | } 28 | 29 | uint8_t idz_shifter_current_gear(void) 30 | { 31 | return idz_shifter_gear; 32 | } 33 | -------------------------------------------------------------------------------- /idzio/shifter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void idz_shifter_reset(void); 7 | void idz_shifter_update(bool shift_dn, bool shift_up); 8 | uint8_t idz_shifter_current_gear(void); 9 | -------------------------------------------------------------------------------- /idzio/wnd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "util/dprintf.h" 7 | 8 | /* DirectInput requires a window for correct initialization (and also force 9 | feedback), so this source file provides some utilities for creating a 10 | generic message-only window. */ 11 | 12 | static LRESULT WINAPI idz_io_wnd_proc( 13 | HWND hwnd, 14 | UINT msg, 15 | WPARAM wparam, 16 | LPARAM lparam); 17 | 18 | HRESULT idz_io_wnd_create(HINSTANCE inst, HWND *out) 19 | { 20 | HRESULT hr; 21 | WNDCLASSEXW wcx; 22 | ATOM atom; 23 | HWND hwnd; 24 | 25 | assert(inst != NULL); /* We are not an EXE */ 26 | assert(out != NULL); 27 | 28 | *out = NULL; 29 | 30 | memset(&wcx, 0, sizeof(wcx)); 31 | wcx.cbSize = sizeof(wcx); 32 | wcx.lpfnWndProc = idz_io_wnd_proc; 33 | wcx.hInstance = inst; 34 | wcx.lpszClassName = L"IDZIO"; 35 | 36 | atom = RegisterClassExW(&wcx); 37 | 38 | if (atom == 0) { 39 | hr = HRESULT_FROM_WIN32(GetLastError()); 40 | dprintf("IDZIO: RegisterClassExW failed: %08x\n", (int) hr); 41 | 42 | goto fail; 43 | } 44 | 45 | hwnd = CreateWindowExW( 46 | 0, 47 | (wchar_t *) (intptr_t) atom, 48 | L"", 49 | 0, 50 | CW_USEDEFAULT, 51 | CW_USEDEFAULT, 52 | CW_USEDEFAULT, 53 | CW_USEDEFAULT, 54 | HWND_MESSAGE, 55 | NULL, 56 | inst, 57 | NULL); 58 | 59 | if (hwnd == NULL) { 60 | hr = HRESULT_FROM_WIN32(GetLastError()); 61 | dprintf("IDZIO: CreateWindowExW failed: %08x\n", (int) hr); 62 | 63 | goto fail; 64 | } 65 | 66 | *out = hwnd; 67 | 68 | return S_OK; 69 | 70 | fail: 71 | UnregisterClassW((wchar_t *) (intptr_t) atom, inst); 72 | 73 | return hr; 74 | } 75 | 76 | static LRESULT WINAPI idz_io_wnd_proc( 77 | HWND hwnd, 78 | UINT msg, 79 | WPARAM wparam, 80 | LPARAM lparam) 81 | { 82 | switch (msg) { 83 | default: 84 | return DefWindowProcW(hwnd, msg, wparam, lparam); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /idzio/wnd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | HRESULT idz_io_wnd_create(HINSTANCE inst, HWND *out); 6 | -------------------------------------------------------------------------------- /idzio/xi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* Can't call this xinput.h or it will conflict with */ 4 | 5 | #include 6 | 7 | #include "idzio/backend.h" 8 | #include "idzio/config.h" 9 | 10 | HRESULT idz_xi_init(const struct idz_xi_config *cfg, const struct idz_io_backend **backend); 11 | -------------------------------------------------------------------------------- /initpki: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This shell script documents the process that was used to generate our fake 4 | # P-Ras PKI. It should not need to be run again under normal circumstances. 5 | 6 | set -e 7 | 8 | D=`dirname $0` 9 | DAYS=36524 10 | 11 | pushd "$D" 12 | mkdir -p pki 13 | 14 | # Generate CA 15 | 16 | openssl genpkey \ 17 | -algorithm RSA \ 18 | -out pki/ca.key \ 19 | -pkeyopt rsa_keygen_bits:2048 \ 20 | 21 | openssl req \ 22 | -new \ 23 | -key pki/ca.key \ 24 | -extensions v3_ca \ 25 | -batch \ 26 | -out /tmp/ca.csr \ 27 | -utf8 \ 28 | -subj "/CN=DummyCA/O=DummyPKI" \ 29 | 30 | openssl req \ 31 | -x509 \ 32 | -sha256 \ 33 | -key pki/ca.key \ 34 | -in /tmp/ca.csr \ 35 | -out pki/ca.pem \ 36 | -days $DAYS \ 37 | 38 | # Convert PEM cert to DER form for emulated keychip. 39 | # DER must fit in 1024 bytes so it must be small. 40 | 41 | openssl x509 \ 42 | -in pki/ca.pem \ 43 | -out pki/ca.crt \ 44 | -outform der \ 45 | 46 | # Generate server key 47 | 48 | openssl genpkey \ 49 | -algorithm RSA \ 50 | -out pki/server.key \ 51 | -pkeyopt rsa_keygen_bits:2048 \ 52 | 53 | openssl req \ 54 | -new \ 55 | -key pki/server.key \ 56 | -extensions v3_ca \ 57 | -batch \ 58 | -out /tmp/server.csr \ 59 | -utf8 \ 60 | -subj "/CN=ib.naominet.jp" \ 61 | 62 | openssl x509 \ 63 | -req \ 64 | -sha256 \ 65 | -days $DAYS \ 66 | -in /tmp/server.csr \ 67 | -CAkey pki/ca.key \ 68 | -CA pki/ca.pem \ 69 | -set_serial 0 \ 70 | -out pki/server.pem \ 71 | 72 | # Generate billing key pair 73 | 74 | openssl genpkey \ 75 | -algorithm RSA \ 76 | -out pki/billing.key \ 77 | -pkeyopt rsa_keygen_bits:1024 \ 78 | 79 | openssl rsa \ 80 | -pubout \ 81 | -outform der \ 82 | -in pki/billing.key \ 83 | -out pki/billing.pub \ 84 | 85 | # Clean up 86 | 87 | rm -f /tmp/ca.csr 88 | rm -f /tmp/server.csr 89 | -------------------------------------------------------------------------------- /jvs/jvs-bus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "jvs/jvs-bus.h" 6 | 7 | void jvs_bus_transact( 8 | struct jvs_node *head, 9 | const void *bytes, 10 | size_t nbytes, 11 | struct iobuf *resp) 12 | { 13 | struct jvs_node *node; 14 | 15 | assert(bytes != NULL); 16 | assert(resp != NULL); 17 | 18 | for (node = head ; node != NULL ; node = node->next) { 19 | node->transact(node, bytes, nbytes, resp); 20 | } 21 | } 22 | 23 | bool jvs_node_sense(struct jvs_node *node) 24 | { 25 | if (node != NULL) { 26 | return node->sense(node); 27 | } else { 28 | return false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jvs/jvs-bus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "hook/iobuf.h" 7 | 8 | struct jvs_node { 9 | struct jvs_node *next; 10 | void (*transact)( 11 | struct jvs_node *node, 12 | const void *bytes, 13 | size_t nbytes, 14 | struct iobuf *resp); 15 | bool (*sense)(struct jvs_node *node); 16 | }; 17 | 18 | void jvs_bus_transact( 19 | struct jvs_node *head, 20 | const void *bytes, 21 | size_t nbytes, 22 | struct iobuf *resp); 23 | 24 | bool jvs_node_sense(struct jvs_node *node); 25 | -------------------------------------------------------------------------------- /jvs/jvs-cmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum { 4 | JVS_CMD_READ_ID = 0x10, 5 | JVS_CMD_GET_CMD_VERSION = 0x11, 6 | JVS_CMD_GET_JVS_VERSION = 0x12, 7 | JVS_CMD_GET_COMM_VERSION = 0x13, 8 | JVS_CMD_GET_FEATURES = 0x14, 9 | JVS_CMD_READ_SWITCHES = 0x20, 10 | JVS_CMD_READ_COIN = 0x21, 11 | JVS_CMD_READ_ANALOGS = 0x22, 12 | JVS_CMD_WRITE_GPIO = 0x32, 13 | JVS_CMD_RESET = 0xF0, 14 | JVS_CMD_ASSIGN_ADDR = 0xF1, 15 | }; 16 | 17 | #pragma pack(push, 1) 18 | 19 | struct jvs_req_read_switches { 20 | uint8_t cmd; 21 | uint8_t num_players; 22 | uint8_t bytes_per_player; 23 | }; 24 | 25 | struct jvs_req_read_coin { 26 | uint8_t cmd; 27 | uint8_t nslots; 28 | }; 29 | 30 | struct jvs_req_read_analogs { 31 | uint8_t cmd; 32 | uint8_t nanalogs; 33 | }; 34 | 35 | struct jvs_req_reset { 36 | uint8_t cmd; 37 | uint8_t unknown; 38 | }; 39 | 40 | struct jvs_req_assign_addr { 41 | uint8_t cmd; 42 | uint8_t addr; 43 | }; 44 | 45 | #pragma pack(pop) 46 | -------------------------------------------------------------------------------- /jvs/jvs-frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "hook/iobuf.h" 8 | 9 | HRESULT jvs_frame_decode( 10 | struct iobuf *dest, 11 | const void *bytes, 12 | size_t nbytes); 13 | 14 | HRESULT jvs_frame_encode( 15 | struct iobuf *dest, 16 | const void *bytes, 17 | size_t nbytes); 18 | -------------------------------------------------------------------------------- /jvs/jvs-util.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "hook/iobuf.h" 8 | 9 | #include "jvs/jvs-frame.h" 10 | #include "jvs/jvs-util.h" 11 | 12 | #include "util/dprintf.h" 13 | 14 | typedef HRESULT (*jvs_dispatch_fn_t)( 15 | void *ctx, 16 | struct const_iobuf *req, 17 | struct iobuf *resp); 18 | 19 | void jvs_crack_request( 20 | const void *bytes, 21 | size_t nbytes, 22 | struct iobuf *resp, 23 | uint8_t jvs_addr, 24 | jvs_dispatch_fn_t dispatch_fn, 25 | void *dispatch_ctx) 26 | { 27 | uint8_t req_bytes[128]; 28 | uint8_t resp_bytes[128]; 29 | struct iobuf decode; 30 | struct iobuf encode; 31 | struct const_iobuf segments; 32 | HRESULT hr; 33 | 34 | assert(bytes != NULL); 35 | assert(resp != NULL); 36 | assert(jvs_addr != 0x00 && (jvs_addr < 0x20 || jvs_addr == 0xFF)); 37 | assert(dispatch_fn != NULL); 38 | 39 | decode.bytes = req_bytes; 40 | decode.nbytes = sizeof(req_bytes); 41 | decode.pos = 0; 42 | 43 | hr = jvs_frame_decode(&decode, bytes, nbytes); 44 | 45 | if (FAILED(hr)) { 46 | return; 47 | } 48 | 49 | #if 0 50 | dprintf("Decoded request:\n"); 51 | dump_iobuf(&decode); 52 | #endif 53 | 54 | if (req_bytes[0] != jvs_addr && req_bytes[0] != 0xFF) { 55 | return; 56 | } 57 | 58 | iobuf_flip(&segments, &decode); 59 | segments.pos = 2; 60 | 61 | encode.bytes = resp_bytes; 62 | encode.nbytes = sizeof(resp_bytes); 63 | encode.pos = 3; 64 | 65 | /* +1: Don't try to dispatch the trailing checksum byte */ 66 | 67 | hr = S_OK; /* I guess an empty request packet is technically valid? */ 68 | 69 | while (segments.pos + 1 < segments.nbytes) { 70 | hr = dispatch_fn(dispatch_ctx, &segments, &encode); 71 | 72 | if (FAILED(hr)) { 73 | break; 74 | } 75 | } 76 | 77 | if (FAILED(hr)) { 78 | /* Send an error in the overall status byte */ 79 | encode.pos = 3; 80 | 81 | resp_bytes[0] = 0x00; /* Dest addr (master) */ 82 | resp_bytes[1] = 0x02; /* Payload len: Status byte, checksum byte */ 83 | 84 | if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { 85 | resp_bytes[2] = 0x04; /* Status: "Overflow" */ 86 | } else { 87 | resp_bytes[2] = 0x02; /* Status: Encoutered unsupported command */ 88 | } 89 | } else if (encode.pos == 3) { 90 | /* Probably a reset, don't emit a response frame with empty payload */ 91 | return; 92 | } else { 93 | /* Send success response */ 94 | resp_bytes[0] = 0x00; /* Dest addr (master) */ 95 | resp_bytes[1] = encode.pos - 2 + 1; /* -2 header +1 checksum */ 96 | resp_bytes[2] = 0x01; /* Status: Success */ 97 | } 98 | 99 | #if 0 100 | dprintf("Encoding response:\n"); 101 | dump_iobuf(&encode); 102 | #endif 103 | 104 | hr = jvs_frame_encode(resp, encode.bytes, encode.pos); 105 | 106 | if (FAILED(hr)) { 107 | dprintf("JVS Node: Response encode error: %x\n", (int) hr); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /jvs/jvs-util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "hook/iobuf.h" 9 | 10 | typedef HRESULT (*jvs_dispatch_fn_t)( 11 | void *ctx, 12 | struct const_iobuf *req, 13 | struct iobuf *resp); 14 | 15 | void jvs_crack_request( 16 | const void *bytes, 17 | size_t nbytes, 18 | struct iobuf *resp, 19 | uint8_t jvs_addr, 20 | jvs_dispatch_fn_t dispatch_fn, 21 | void *dispatch_ctx); 22 | -------------------------------------------------------------------------------- /jvs/meson.build: -------------------------------------------------------------------------------- 1 | jvs_lib = static_library( 2 | 'jvs', 3 | include_directories : inc, 4 | implicit_include_directories : false, 5 | c_pch : '../precompiled.h', 6 | dependencies : [ 7 | capnhook.get_variable('hook_dep'), 8 | ], 9 | sources : [ 10 | 'jvs-bus.c', 11 | 'jvs-bus.h', 12 | 'jvs-cmd.h', 13 | 'jvs-frame.c', 14 | 'jvs-frame.h', 15 | 'jvs-util.c', 16 | 'jvs-util.h', 17 | ], 18 | ) 19 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('segatools', 'c', version: '0.1.0') 2 | 3 | add_project_arguments( 4 | '-DCOBJMACROS', 5 | '-DDIRECTINPUT_VERSION=0x0800', 6 | '-DWIN32_LEAN_AND_MEAN', 7 | '-D_WIN32_WINNT=_WIN32_WINNT_WIN7', 8 | '-DMINGW_HAS_SECURE_API=1', 9 | language: 'c', 10 | ) 11 | 12 | # Use get_argument_syntax() instead once Meson 0.49.0 releases 13 | if meson.get_compiler('c').get_id() != 'msvc' 14 | add_project_arguments( 15 | '-ffunction-sections', 16 | '-fdata-sections', 17 | language: 'c', 18 | ) 19 | 20 | add_project_link_arguments( 21 | '-Wl,--enable-stdcall-fixup', 22 | '-Wl,--exclude-all-symbols', 23 | '-Wl,--gc-sections', 24 | '-static-libgcc', 25 | language: 'c', 26 | ) 27 | endif 28 | 29 | cc = meson.get_compiler('c') 30 | shlwapi_lib = cc.find_library('shlwapi') 31 | dinput8_lib = cc.find_library('dinput8') 32 | dxguid_lib = cc.find_library('dxguid') 33 | xinput_lib = cc.find_library('xinput') 34 | 35 | inc = include_directories('.') 36 | capnhook = subproject('capnhook') 37 | 38 | subdir('amex') 39 | subdir('iccard') 40 | subdir('board') 41 | subdir('hooklib') 42 | subdir('jvs') 43 | subdir('platform') 44 | subdir('util') 45 | 46 | subdir('aimeio') 47 | subdir('chuniio') 48 | subdir('divaio') 49 | subdir('idzio') 50 | subdir('mu3io') 51 | subdir('fateio') 52 | 53 | subdir('chunihook') 54 | subdir('divahook') 55 | subdir('idzhook') 56 | subdir('minihook') 57 | subdir('mu3hook') 58 | subdir('fatehook') 59 | subdir('chronohook') -------------------------------------------------------------------------------- /minihook/dllmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "amex/config.h" 6 | #include "amex/ds.h" 7 | 8 | #include "hook/process.h" 9 | 10 | #include "hooklib/spike.h" 11 | 12 | #include "platform/clock.h" 13 | #include "platform/config.h" 14 | #include "platform/nusec.h" 15 | 16 | #include "util/dprintf.h" 17 | 18 | static process_entry_t app_startup; 19 | 20 | static DWORD CALLBACK app_pre_startup(void) 21 | { 22 | struct clock_config clock_cfg; 23 | struct ds_config ds_cfg; 24 | struct nusec_config nusec_cfg; 25 | HRESULT hr; 26 | 27 | dprintf("--- Begin %s ---\n", __func__); 28 | 29 | clock_config_load(&clock_cfg, L".\\segatools.ini"); 30 | ds_config_load(&ds_cfg, L".\\segatools.ini"); 31 | nusec_config_load(&nusec_cfg, L".\\segatools.ini"); 32 | spike_hook_init(L".\\segatools.ini"); 33 | 34 | hr = clock_hook_init(&clock_cfg); 35 | 36 | if (FAILED(hr)) { 37 | goto fail; 38 | } 39 | 40 | hr = nusec_hook_init(&nusec_cfg, "SSSS", "AAV0"); 41 | 42 | if (FAILED(hr)) { 43 | goto fail; 44 | } 45 | 46 | hr = ds_hook_init(&ds_cfg); 47 | 48 | if (FAILED(hr)) { 49 | goto fail; 50 | } 51 | 52 | dprintf("--- End %s ---\n", __func__); 53 | 54 | return app_startup(); 55 | 56 | fail: 57 | ExitProcess(EXIT_FAILURE); 58 | } 59 | 60 | BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) 61 | { 62 | HRESULT hr; 63 | 64 | if (cause != DLL_PROCESS_ATTACH) { 65 | return TRUE; 66 | } 67 | 68 | hr = process_hijack_startup(app_pre_startup, &app_startup); 69 | 70 | if (!SUCCEEDED(hr)) { 71 | dprintf("Failed to hijack process startup: %x\n", (int) hr); 72 | } 73 | 74 | return SUCCEEDED(hr); 75 | } 76 | -------------------------------------------------------------------------------- /minihook/meson.build: -------------------------------------------------------------------------------- 1 | shared_library( 2 | 'minihook', 3 | name_prefix : '', 4 | include_directories: inc, 5 | implicit_include_directories : false, 6 | c_pch : '../precompiled.h', 7 | dependencies : [ 8 | capnhook.get_variable('hook_dep'), 9 | ], 10 | link_with : [ 11 | amex_lib, 12 | hooklib_lib, 13 | platform_lib, 14 | util_lib, 15 | ], 16 | sources : [ 17 | 'dllmain.c', 18 | ], 19 | ) 20 | -------------------------------------------------------------------------------- /mu3hook/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "board/config.h" 5 | 6 | #include "hooklib/config.h" 7 | #include "hooklib/dvd.h" 8 | #include "hooklib/gfx.h" 9 | 10 | #include "mu3hook/config.h" 11 | 12 | #include "platform/config.h" 13 | 14 | void mu3_dll_config_load( 15 | struct mu3_dll_config *cfg, 16 | const wchar_t *filename) 17 | { 18 | assert(cfg != NULL); 19 | assert(filename != NULL); 20 | 21 | GetPrivateProfileStringW( 22 | L"mu3io", 23 | L"path", 24 | L"", 25 | cfg->path, 26 | _countof(cfg->path), 27 | filename); 28 | } 29 | 30 | void mu3_hook_config_load( 31 | struct mu3_hook_config *cfg, 32 | const wchar_t *filename) 33 | { 34 | assert(cfg != NULL); 35 | assert(filename != NULL); 36 | 37 | platform_config_load(&cfg->platform, filename); 38 | aime_config_load(&cfg->aime, filename); 39 | dvd_config_load(&cfg->dvd, filename); 40 | gfx_config_load(&cfg->gfx, filename); 41 | mu3_dll_config_load(&cfg->dll, filename); 42 | } 43 | -------------------------------------------------------------------------------- /mu3hook/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "board/config.h" 6 | 7 | #include "hooklib/dvd.h" 8 | #include "hooklib/gfx.h" 9 | 10 | #include "mu3hook/mu3-dll.h" 11 | 12 | #include "platform/config.h" 13 | 14 | struct mu3_hook_config { 15 | struct platform_config platform; 16 | struct aime_config aime; 17 | struct dvd_config dvd; 18 | struct gfx_config gfx; 19 | struct mu3_dll_config dll; 20 | }; 21 | 22 | void mu3_dll_config_load( 23 | struct mu3_dll_config *cfg, 24 | const wchar_t *filename); 25 | 26 | void mu3_hook_config_load( 27 | struct mu3_hook_config *cfg, 28 | const wchar_t *filename); 29 | -------------------------------------------------------------------------------- /mu3hook/dllmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "board/io4.h" 6 | #include "board/sg-reader.h" 7 | #include "board/vfd.h" 8 | 9 | #include "hook/process.h" 10 | 11 | #include "hooklib/dvd.h" 12 | #include "hooklib/serial.h" 13 | #include "hooklib/spike.h" 14 | 15 | #include "mu3hook/config.h" 16 | #include "mu3hook/io4.h" 17 | #include "mu3hook/mu3-dll.h" 18 | #include "mu3hook/unity.h" 19 | 20 | #include "platform/platform.h" 21 | 22 | #include "util/dprintf.h" 23 | 24 | static HMODULE mu3_hook_mod; 25 | static process_entry_t mu3_startup; 26 | static struct mu3_hook_config mu3_hook_cfg; 27 | 28 | static DWORD CALLBACK mu3_pre_startup(void) 29 | { 30 | HRESULT hr; 31 | 32 | dprintf("--- Begin mu3_pre_startup ---\n"); 33 | 34 | /* Load config */ 35 | 36 | mu3_hook_config_load(&mu3_hook_cfg, L".\\segatools.ini"); 37 | 38 | /* Hook Win32 APIs */ 39 | 40 | dvd_hook_init(&mu3_hook_cfg.dvd, mu3_hook_mod); 41 | gfx_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); 42 | serial_hook_init(); 43 | 44 | /* Initialize emulation hooks */ 45 | 46 | hr = platform_hook_init( 47 | &mu3_hook_cfg.platform, 48 | "SDDT", 49 | "ACA1", 50 | mu3_hook_mod); 51 | 52 | if (FAILED(hr)) { 53 | goto fail; 54 | } 55 | 56 | hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, mu3_hook_mod); 57 | 58 | if (FAILED(hr)) { 59 | goto fail; 60 | } 61 | 62 | hr = vfd_hook_init(2); 63 | 64 | if (FAILED(hr)) { 65 | goto fail; 66 | } 67 | 68 | hr = mu3_dll_init(&mu3_hook_cfg.dll, mu3_hook_mod); 69 | 70 | if (FAILED(hr)) { 71 | goto fail; 72 | } 73 | 74 | hr = mu3_io4_hook_init(); 75 | 76 | if (FAILED(hr)) { 77 | goto fail; 78 | } 79 | 80 | /* Initialize Unity native plugin DLL hooks 81 | 82 | There seems to be an issue with other DLL hooks if `LoadLibraryW` is 83 | hooked earlier in the `mu3hook` initialization. */ 84 | 85 | unity_hook_init(); 86 | 87 | /* Initialize debug helpers */ 88 | 89 | spike_hook_init(L".\\segatools.ini"); 90 | 91 | dprintf("--- End mu3_pre_startup ---\n"); 92 | 93 | /* Jump to EXE start address */ 94 | 95 | return mu3_startup(); 96 | 97 | fail: 98 | ExitProcess(EXIT_FAILURE); 99 | } 100 | 101 | BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) 102 | { 103 | HRESULT hr; 104 | 105 | if (cause != DLL_PROCESS_ATTACH) { 106 | return TRUE; 107 | } 108 | 109 | mu3_hook_mod = mod; 110 | 111 | hr = process_hijack_startup(mu3_pre_startup, &mu3_startup); 112 | 113 | if (!SUCCEEDED(hr)) { 114 | dprintf("Failed to hijack process startup: %x\n", (int) hr); 115 | } 116 | 117 | return SUCCEEDED(hr); 118 | } 119 | -------------------------------------------------------------------------------- /mu3hook/io4.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "board/io4.h" 8 | 9 | #include "mu3hook/mu3-dll.h" 10 | 11 | #include "util/dprintf.h" 12 | 13 | static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state); 14 | 15 | static const struct io4_ops mu3_io4_ops = { 16 | .poll = mu3_io4_poll, 17 | }; 18 | 19 | HRESULT mu3_io4_hook_init(void) 20 | { 21 | HRESULT hr; 22 | 23 | assert(mu3_dll.init != NULL); 24 | 25 | hr = io4_hook_init(&mu3_io4_ops, NULL); 26 | 27 | if (FAILED(hr)) { 28 | return hr; 29 | } 30 | 31 | return mu3_dll.init(); 32 | } 33 | 34 | static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state) 35 | { 36 | uint8_t opbtn; 37 | uint8_t left; 38 | uint8_t right; 39 | int16_t lever; 40 | HRESULT hr; 41 | 42 | assert(mu3_dll.poll != NULL); 43 | assert(mu3_dll.get_opbtns != NULL); 44 | assert(mu3_dll.get_gamebtns != NULL); 45 | assert(mu3_dll.get_lever != NULL); 46 | 47 | memset(state, 0, sizeof(*state)); 48 | 49 | hr = mu3_dll.poll(); 50 | 51 | if (FAILED(hr)) { 52 | return hr; 53 | } 54 | 55 | opbtn = 0; 56 | left = 0; 57 | right = 0; 58 | lever = 0; 59 | 60 | mu3_dll.get_opbtns(&opbtn); 61 | mu3_dll.get_gamebtns(&left, &right); 62 | mu3_dll.get_lever(&lever); 63 | 64 | if (opbtn & MU3_IO_OPBTN_TEST) { 65 | state->buttons[0] |= IO4_BUTTON_TEST; 66 | } 67 | 68 | if (opbtn & MU3_IO_OPBTN_SERVICE) { 69 | state->buttons[0] |= IO4_BUTTON_SERVICE; 70 | } 71 | 72 | if (left & MU3_IO_GAMEBTN_1) { 73 | state->buttons[0] |= 1 << 0; 74 | } 75 | 76 | if (left & MU3_IO_GAMEBTN_2) { 77 | state->buttons[0] |= 1 << 5; 78 | } 79 | 80 | if (left & MU3_IO_GAMEBTN_3) { 81 | state->buttons[0] |= 1 << 4; 82 | } 83 | 84 | if (right & MU3_IO_GAMEBTN_1) { 85 | state->buttons[0] |= 1 << 1; 86 | } 87 | 88 | if (right & MU3_IO_GAMEBTN_2) { 89 | state->buttons[1] |= 1 << 0; 90 | } 91 | 92 | if (right & MU3_IO_GAMEBTN_3) { 93 | state->buttons[0] |= 1 << 15; 94 | } 95 | 96 | if (left & MU3_IO_GAMEBTN_MENU) { 97 | state->buttons[1] |= 1 << 14; 98 | } 99 | 100 | if (right & MU3_IO_GAMEBTN_MENU) { 101 | state->buttons[0] |= 1 << 13; 102 | } 103 | 104 | if (!(left & MU3_IO_GAMEBTN_SIDE)) { 105 | state->buttons[1] |= 1 << 15; /* L-Side, active-low */ 106 | } 107 | 108 | if (!(right & MU3_IO_GAMEBTN_SIDE)) { 109 | state->buttons[0] |= 1 << 14; /* R-Side, active-low */ 110 | } 111 | 112 | /* Lever increases right-to-left, not left-to-right. 113 | 114 | Use 0x7FFF as the center point instead of 0x8000; the latter would 115 | overflow when the lever pos is INT16_MIN. */ 116 | 117 | state->adcs[0] = 0x7FFF - lever; 118 | 119 | return S_OK; 120 | } 121 | -------------------------------------------------------------------------------- /mu3hook/io4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | HRESULT mu3_io4_hook_init(void); 6 | -------------------------------------------------------------------------------- /mu3hook/meson.build: -------------------------------------------------------------------------------- 1 | shared_library( 2 | 'mu3hook', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | vs_module_defs : 'mu3hook.def', 7 | c_pch : '../precompiled.h', 8 | dependencies : [ 9 | capnhook.get_variable('hook_dep'), 10 | capnhook.get_variable('hooklib_dep'), 11 | xinput_lib, 12 | ], 13 | link_with : [ 14 | aimeio_lib, 15 | board_lib, 16 | hooklib_lib, 17 | mu3io_lib, 18 | platform_lib, 19 | util_lib, 20 | ], 21 | sources : [ 22 | 'config.c', 23 | 'config.h', 24 | 'dllmain.c', 25 | 'io4.c', 26 | 'io4.h', 27 | 'mu3-dll.c', 28 | 'mu3-dll.h', 29 | 'unity.h', 30 | 'unity.c', 31 | ], 32 | ) 33 | -------------------------------------------------------------------------------- /mu3hook/mu3-dll.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "mu3hook/mu3-dll.h" 7 | 8 | #include "util/dll-bind.h" 9 | #include "util/dprintf.h" 10 | 11 | const struct dll_bind_sym mu3_dll_syms[] = { 12 | { 13 | .sym = "mu3_io_init", 14 | .off = offsetof(struct mu3_dll, init), 15 | }, { 16 | .sym = "mu3_io_poll", 17 | .off = offsetof(struct mu3_dll, poll), 18 | }, { 19 | .sym = "mu3_io_get_opbtns", 20 | .off = offsetof(struct mu3_dll, get_opbtns), 21 | }, { 22 | .sym = "mu3_io_get_gamebtns", 23 | .off = offsetof(struct mu3_dll, get_gamebtns), 24 | }, { 25 | .sym = "mu3_io_get_lever", 26 | .off = offsetof(struct mu3_dll, get_lever), 27 | } 28 | }; 29 | 30 | struct mu3_dll mu3_dll; 31 | 32 | // Copypasta DLL binding and diagnostic message boilerplate. 33 | // Not much of this lends itself to being easily factored out. Also there 34 | // will be a lot of API-specific branching code here eventually as new API 35 | // versions get defined, so even though these functions all look the same 36 | // now this won't remain the case forever. 37 | 38 | HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self) 39 | { 40 | uint16_t (*get_api_version)(void); 41 | const struct dll_bind_sym *sym; 42 | HINSTANCE owned; 43 | HINSTANCE src; 44 | HRESULT hr; 45 | 46 | assert(cfg != NULL); 47 | assert(self != NULL); 48 | 49 | if (cfg->path[0] != L'\0') { 50 | owned = LoadLibraryW(cfg->path); 51 | 52 | if (owned == NULL) { 53 | hr = HRESULT_FROM_WIN32(GetLastError()); 54 | dprintf("Ongeki IO: Failed to load IO DLL: %lx: %S\n", 55 | hr, 56 | cfg->path); 57 | 58 | goto end; 59 | } 60 | 61 | dprintf("Ongeki IO: Using custom IO DLL: %S\n", cfg->path); 62 | src = owned; 63 | } else { 64 | owned = NULL; 65 | src = self; 66 | } 67 | 68 | get_api_version = (void *) GetProcAddress(src, "mu3_io_get_api_version"); 69 | 70 | if (get_api_version != NULL) { 71 | mu3_dll.api_version = get_api_version(); 72 | } else { 73 | mu3_dll.api_version = 0x0100; 74 | dprintf("Custom IO DLL does not expose mu3_io_get_api_version, " 75 | "assuming API version 1.0.\n" 76 | "Please ask the developer to update their DLL.\n"); 77 | } 78 | 79 | if (mu3_dll.api_version >= 0x0200) { 80 | hr = E_NOTIMPL; 81 | dprintf("Ongeki IO: Custom IO DLL implements an unsupported " 82 | "API version (%#04x). Please update Segatools.\n", 83 | mu3_dll.api_version); 84 | 85 | goto end; 86 | } 87 | 88 | sym = mu3_dll_syms; 89 | hr = dll_bind(&mu3_dll, src, &sym, _countof(mu3_dll_syms)); 90 | 91 | if (FAILED(hr)) { 92 | if (src != self) { 93 | dprintf("Ongeki IO: Custom IO DLL does not provide function " 94 | "\"%s\". Please contact your IO DLL's developer for " 95 | "further assistance.\n", 96 | sym->sym); 97 | 98 | goto end; 99 | } else { 100 | dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); 101 | } 102 | } 103 | 104 | owned = NULL; 105 | 106 | end: 107 | if (owned != NULL) { 108 | FreeLibrary(owned); 109 | } 110 | 111 | return hr; 112 | } 113 | -------------------------------------------------------------------------------- /mu3hook/mu3-dll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "mu3io/mu3io.h" 6 | 7 | struct mu3_dll { 8 | uint16_t api_version; 9 | HRESULT (*init)(void); 10 | HRESULT (*poll)(void); 11 | void (*get_opbtns)(uint8_t *opbtn); 12 | void (*get_gamebtns)(uint8_t *left, uint8_t *right); 13 | void (*get_lever)(int16_t *pos); 14 | }; 15 | 16 | struct mu3_dll_config { 17 | wchar_t path[MAX_PATH]; 18 | }; 19 | 20 | extern struct mu3_dll mu3_dll; 21 | 22 | HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self); 23 | -------------------------------------------------------------------------------- /mu3hook/mu3hook.def: -------------------------------------------------------------------------------- 1 | LIBRARY mu3hook 2 | 3 | EXPORTS 4 | Direct3DCreate9 5 | aime_io_get_api_version 6 | aime_io_init 7 | aime_io_led_set_color 8 | aime_io_nfc_get_aime_id 9 | aime_io_nfc_get_felica_id 10 | aime_io_nfc_poll 11 | amDllVideoClose @2 12 | amDllVideoGetVBiosVersion @4 13 | amDllVideoOpen @1 14 | amDllVideoSetResolution @3 15 | mu3_io_get_gamebtns 16 | mu3_io_get_lever 17 | mu3_io_get_opbtns 18 | mu3_io_init 19 | mu3_io_poll 20 | -------------------------------------------------------------------------------- /mu3hook/unity.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "hook/table.h" 6 | 7 | #include "hooklib/dll.h" 8 | #include "hooklib/path.h" 9 | 10 | #include "util/dprintf.h" 11 | 12 | static void dll_hook_insert_hooks(HMODULE target); 13 | 14 | static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name); 15 | static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name); 16 | 17 | static const struct hook_symbol unity_kernel32_syms[] = { 18 | { 19 | .name = "LoadLibraryW", 20 | .patch = my_LoadLibraryW, 21 | .link = (void **) &next_LoadLibraryW, 22 | }, 23 | }; 24 | 25 | static const wchar_t *target_modules[] = { 26 | L"mono.dll", 27 | L"cri_ware_unity.dll", 28 | }; 29 | static const size_t target_modules_len = _countof(target_modules); 30 | 31 | void unity_hook_init(void) 32 | { 33 | dll_hook_insert_hooks(NULL); 34 | } 35 | 36 | static void dll_hook_insert_hooks(HMODULE target) 37 | { 38 | hook_table_apply( 39 | target, 40 | "kernel32.dll", 41 | unity_kernel32_syms, 42 | _countof(unity_kernel32_syms)); 43 | } 44 | 45 | static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name) 46 | { 47 | const wchar_t *name_end; 48 | const wchar_t *target_module; 49 | bool already_loaded; 50 | HMODULE result; 51 | size_t name_len; 52 | size_t target_module_len; 53 | 54 | if (name == NULL) { 55 | SetLastError(ERROR_INVALID_PARAMETER); 56 | 57 | return NULL; 58 | } 59 | 60 | // Check if the module is already loaded 61 | already_loaded = GetModuleHandleW(name) != NULL; 62 | 63 | // Must call the next handler so the DLL reference count is incremented 64 | result = next_LoadLibraryW(name); 65 | 66 | if (!already_loaded && result != NULL) { 67 | name_len = wcslen(name); 68 | 69 | for (size_t i = 0; i < target_modules_len; i++) { 70 | target_module = target_modules[i]; 71 | target_module_len = wcslen(target_module); 72 | 73 | // Check if the newly loaded library is at least the length of 74 | // the name of the target module 75 | if (name_len < target_module_len) { 76 | continue; 77 | } 78 | 79 | name_end = &name[name_len - target_module_len]; 80 | 81 | // Check if the name of the newly loaded library is one of the 82 | // modules the path hooks should be injected into 83 | if (_wcsicmp(name_end, target_module) != 0) { 84 | continue; 85 | } 86 | 87 | dprintf("Unity: Loaded %S\n", target_module); 88 | 89 | dll_hook_insert_hooks(result); 90 | path_hook_insert_hooks(result); 91 | } 92 | } 93 | 94 | return result; 95 | } 96 | -------------------------------------------------------------------------------- /mu3hook/unity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void unity_hook_init(void); 4 | -------------------------------------------------------------------------------- /mu3io/meson.build: -------------------------------------------------------------------------------- 1 | mu3io_lib = static_library( 2 | 'mu3io', 3 | name_prefix : '', 4 | include_directories : inc, 5 | implicit_include_directories : false, 6 | c_pch : '../precompiled.h', 7 | dependencies : [ 8 | xinput_lib, 9 | ], 10 | sources : [ 11 | 'mu3io.c', 12 | 'mu3io.h', 13 | ], 14 | ) 15 | -------------------------------------------------------------------------------- /mu3io/mu3io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "mu3io/mu3io.h" 8 | 9 | static uint8_t mu3_opbtn; 10 | static uint8_t mu3_left_btn; 11 | static uint8_t mu3_right_btn; 12 | static int16_t mu3_lever_pos; 13 | static int16_t mu3_lever_xpos; 14 | 15 | uint16_t mu3_io_get_api_version(void) 16 | { 17 | return 0x0100; 18 | } 19 | 20 | HRESULT mu3_io_init(void) 21 | { 22 | return S_OK; 23 | } 24 | 25 | HRESULT mu3_io_poll(void) 26 | { 27 | int lever; 28 | int xlever; 29 | XINPUT_STATE xi; 30 | WORD xb; 31 | 32 | mu3_opbtn = 0; 33 | mu3_left_btn = 0; 34 | mu3_right_btn = 0; 35 | 36 | if (GetAsyncKeyState('1') & 0x8000) { 37 | mu3_opbtn |= MU3_IO_OPBTN_TEST; 38 | } 39 | 40 | if (GetAsyncKeyState('2') & 0x8000) { 41 | mu3_opbtn |= MU3_IO_OPBTN_SERVICE; 42 | } 43 | 44 | memset(&xi, 0, sizeof(xi)); 45 | XInputGetState(0, &xi); 46 | xb = xi.Gamepad.wButtons; 47 | 48 | if (xb & XINPUT_GAMEPAD_DPAD_LEFT) { 49 | mu3_left_btn |= MU3_IO_GAMEBTN_1; 50 | } 51 | 52 | if (xb & XINPUT_GAMEPAD_DPAD_UP) { 53 | mu3_left_btn |= MU3_IO_GAMEBTN_2; 54 | } 55 | 56 | if (xb & XINPUT_GAMEPAD_DPAD_RIGHT) { 57 | mu3_left_btn |= MU3_IO_GAMEBTN_3; 58 | } 59 | 60 | if (xb & XINPUT_GAMEPAD_X) { 61 | mu3_right_btn |= MU3_IO_GAMEBTN_1; 62 | } 63 | 64 | if (xb & XINPUT_GAMEPAD_Y) { 65 | mu3_right_btn |= MU3_IO_GAMEBTN_2; 66 | } 67 | 68 | if (xb & XINPUT_GAMEPAD_B) { 69 | mu3_right_btn |= MU3_IO_GAMEBTN_3; 70 | } 71 | 72 | if (xb & XINPUT_GAMEPAD_BACK) { 73 | mu3_left_btn |= MU3_IO_GAMEBTN_MENU; 74 | } 75 | 76 | if (xb & XINPUT_GAMEPAD_START) { 77 | mu3_right_btn |= MU3_IO_GAMEBTN_MENU; 78 | } 79 | 80 | if (xb & XINPUT_GAMEPAD_LEFT_SHOULDER) { 81 | mu3_left_btn |= MU3_IO_GAMEBTN_SIDE; 82 | } 83 | 84 | if (xb & XINPUT_GAMEPAD_RIGHT_SHOULDER) { 85 | mu3_right_btn |= MU3_IO_GAMEBTN_SIDE; 86 | } 87 | 88 | lever = mu3_lever_pos; 89 | 90 | if (abs(xi.Gamepad.sThumbLX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { 91 | lever += xi.Gamepad.sThumbLX / 24; 92 | } 93 | 94 | if (abs(xi.Gamepad.sThumbRX) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { 95 | lever += xi.Gamepad.sThumbRX / 24; 96 | } 97 | 98 | if (lever < INT16_MIN) { 99 | lever = INT16_MIN; 100 | } 101 | 102 | if (lever > INT16_MAX) { 103 | lever = INT16_MAX; 104 | } 105 | 106 | mu3_lever_pos = lever; 107 | 108 | xlever = mu3_lever_pos 109 | - xi.Gamepad.bLeftTrigger * 64 110 | + xi.Gamepad.bRightTrigger * 64; 111 | 112 | if (xlever < INT16_MIN) { 113 | xlever = INT16_MIN; 114 | } 115 | 116 | if (xlever > INT16_MAX) { 117 | xlever = INT16_MAX; 118 | } 119 | 120 | mu3_lever_xpos = xlever; 121 | 122 | return S_OK; 123 | } 124 | 125 | void mu3_io_get_opbtns(uint8_t *opbtn) 126 | { 127 | if (opbtn != NULL) { 128 | *opbtn = mu3_opbtn; 129 | } 130 | } 131 | 132 | void mu3_io_get_gamebtns(uint8_t *left, uint8_t *right) 133 | { 134 | if (left != NULL) { 135 | *left = mu3_left_btn; 136 | } 137 | 138 | if (right != NULL ){ 139 | *right = mu3_right_btn; 140 | } 141 | } 142 | 143 | void mu3_io_get_lever(int16_t *pos) 144 | { 145 | if (pos != NULL) { 146 | *pos = mu3_lever_xpos; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /mu3io/mu3io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | enum { 8 | MU3_IO_OPBTN_TEST = 0x01, 9 | MU3_IO_OPBTN_SERVICE = 0x02, 10 | }; 11 | 12 | enum { 13 | MU3_IO_GAMEBTN_1 = 0x01, 14 | MU3_IO_GAMEBTN_2 = 0x02, 15 | MU3_IO_GAMEBTN_3 = 0x04, 16 | MU3_IO_GAMEBTN_SIDE = 0x08, 17 | MU3_IO_GAMEBTN_MENU = 0x10, 18 | }; 19 | 20 | /* Get the version of the Ongeki IO API that this DLL supports. This 21 | function should return a positive 16-bit integer, where the high byte is 22 | the major version and the low byte is the minor version (as defined by the 23 | Semantic Versioning standard). 24 | 25 | The latest API version as of this writing is 0x0100. */ 26 | 27 | uint16_t mu3_io_get_api_version(void); 28 | 29 | /* Initialize the IO DLL. This is the second function that will be called on 30 | your DLL, after mu3_io_get_api_version. 31 | 32 | All subsequent calls to this API may originate from arbitrary threads. 33 | 34 | Minimum API version: 0x0100 */ 35 | 36 | HRESULT mu3_io_init(void); 37 | 38 | /* Send any queued outputs (of which there are currently none, though this may 39 | change in subsequent API versions) and retrieve any new inputs. 40 | 41 | Minimum API version: 0x0100 */ 42 | 43 | HRESULT mu3_io_poll(void); 44 | 45 | /* Get the state of the cabinet's operator buttons as of the last poll. See 46 | MU3_IO_OPBTN enum above: this contains bit mask definitions for button 47 | states returned in *opbtn. All buttons are active-high. 48 | 49 | Minimum API version: 0x0100 */ 50 | 51 | void mu3_io_get_opbtns(uint8_t *opbtn); 52 | 53 | /* Get the state of the cabinet's gameplay buttons as of the last poll. See 54 | MU3_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into 55 | a left hand side set of inputs and a right hand side set of inputs: the bit 56 | mappings are the same in both cases. 57 | 58 | All buttons are active-high, even though some buttons' electrical signals 59 | on a real cabinet are active-low. 60 | 61 | Minimum API version: 0x0100 */ 62 | 63 | void mu3_io_get_gamebtns(uint8_t *left, uint8_t *right); 64 | 65 | /* Get the position of the cabinet lever as of the last poll. The center 66 | position should be equal to or close to zero. 67 | 68 | The operator will be required to calibrate the lever's range of motion on 69 | first power-on, so the lever position reported through this API does not 70 | need to perfectly centered or cover every single position value possible, 71 | but it should be reasonably close in order to make things easier for the 72 | operator. 73 | 74 | The calibration screen displays the leftmost and rightmost position signal 75 | returned from the cabinet's ADC encoder as a pair of raw two's complement 76 | hexadecimal values. On a real cabinet these leftmost and rightmost 77 | positions are somewhere around 0xB000 and 0x5000 respectively (remember 78 | that negative values i.e. left positions have a high most-significant bit), 79 | although these values can easily vary by +/- 0x1000 across different 80 | cabinets. 81 | 82 | Minimum API version: 0x0100 */ 83 | 84 | void mu3_io_get_lever(int16_t *pos); 85 | -------------------------------------------------------------------------------- /pki/billing.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAML2GPUuzv2N4bYC 3 | xtc5bZSzolHFWdCUbP+whjr3K98FOLnYeoi7mtUSUUYOW8wIqy6WM3c4c0Bp7FcQ 4 | LnZ0zWMm1TfLGHZzZmk5n7Iv6HDPr3ehDgbWLnOpRrVqZDxpAGD2vQb4p2DW4I2x 5 | GUqnqDa++C8dH/0lXqE6cqwGXNGtAgMBAAECgYEAizgPhG4Dk55QkpeTBDfXH3vT 6 | Ko9B3qdO2ptkjxDX/C8PXe7POXq2SvcEoIE6Xg3Gp8LMR5NBAbth8J32f9JSov3P 7 | SiGCGno4k2i2s3jRuVg76FGLDsZH/N1dt4h78VnW0VlInwaM6bQv3zp0u8rXVk/P 8 | wpYh9AGmquBJS3VYUcECQQD0PDRe28SrhollygGZSO321rYbYhoTIstDXZWyQ/y/ 9 | PWKNwNHcYTHIVGmTrJx2AJUyr1tJhwjiOwlsI5Y1Q4/9AkEAzFpFPcs1r/xgSFxB 10 | eYrcNseWYbVajtVxG9t57sayaEQbH2UMNA2vqSYK/nU6oJhj5eLRIsPHlA5ZbIiZ 11 | rvc/cQJAKS0RQ0DX+ncXKQMSm+4wuGHgl+NFNB60mCnp+AEAVpmZyP5OI1J7myOo 12 | HQ6H3lkgzkfEIzRR6ho773BcfaRjXQJAfS4nEE11G9ML4AezjBLGB0CIHF6NlMWn 13 | PhtaPCy3iSt/OeIacaCYpJNLVMjXGx1+xIoG9rbbgRSxLs0W55lJ4QJBALOUVcNw 14 | GKEJdxhIkA8iuUlEyGpKluAgHUNOOKvC3ogRoB0OyH+If/9o8wWDfxgexgM0zGBc 15 | u178W9XDW+IijDA= 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /pki/billing.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareGuy/segatoolsSG/09acf4f41cdb8dc78b50051f7bdf6b5db9c6f178/pki/billing.pub -------------------------------------------------------------------------------- /pki/ca.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareGuy/segatoolsSG/09acf4f41cdb8dc78b50051f7bdf6b5db9c6f178/pki/ca.crt -------------------------------------------------------------------------------- /pki/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDeHEJ2L+FXrcdW 3 | G8LNZ/WknTzE9lsB4I1Gb7LcrXi/Yq+rEnF8iru2h4j1icmDSuwLBS//qENO2LfE 4 | +iU1zmtlhjAfGv8SYSXg1ssAbB6Sv4vvz0U0pNCAvnJ/K8UN7kAXHB1rw5dRvO47 5 | A+M1NO8S6pYTL5jJhK+BaNVmP6w9eOQ4c+0ZvKFOEX0E/uy1qcUuS386ZcPFRxRF 6 | vErUo8WjyMAwV9HZoVa3h3tAywKfDrYjF8DtdNG93G3igzpv4Iro4AuESrPE3t4x 7 | 5uiO4UxwUObolkVsmOAzo7g1QiEi0wlHTQX1ZH3dXgaRAF5vv/V02ZAnmI5keXfP 8 | DDIqURf3AgMBAAECggEAHQD3tNM/y+FHfHkXkRcYPqzBuL6q163pBN+lLagBcoyC 9 | gAZih27eYFGGkvmxNNHdzPqab/oa//rQ1IoNvd78qz9AnW87C71f4uJpk96Kh4M3 10 | 1NLuKJe1GnrEHNMsYktQVzQ2q1HZOrU/LrmtO89zwLadblfyza3j9TQpWbbK2SPn 11 | zVFlBZazX5s5APHgI9PymjPkwC9jsUbSrpe816W9KT8n8cG/CdS7relOXic5PWVL 12 | zgWQdCFMhY/aHFbLWM7RIkQTdX0GopJWgleLQqryKHIVu6Vekyrp/eOpl22wzjku 13 | DMWEK9ZWLHYejbCZJJIRKxSsxRlwAX2D/9kQ9vs2YQKBgQD/UYGD7ovAjTclJzyx 14 | d6dj/C0fHwyFnF+Lknll7AhA1hBYzQ2uaqBSUc2oV19dsxfZfy/tD0gP2ZVSFl1f 15 | iF3ON0xVbbb4r3Drck17c5iQ2+z5PHfOHSD3+AwF+stkKwLiJxIudJJppm1Cn6wc 16 | WiysnqeEhsaklOe0PvdA8BIkDwKBgQDetA7g2m7FKfODHb9Tx7qDtiRjsbCYD1fF 17 | UpF2yQ+twXJAO9AgkxYmmWMQ9/h+yxpsTjE4oDOO5K907x6gGzJFfFrDn0R0zIfw 18 | 0EobfwJ4l28rqUYvQzgYyc/UW2PiuLIr2otXQNmtOnYmPogLIMlgQSvEnseXCs// 19 | 0+TRKKbFmQKBgGVJYVcEeF6P5xOPKE3DCR3qOcBB4gbTTTgiiJR9eZy3D08bMdVC 20 | qY1etHaXCtcyKED5avrheBYJnGovQyWWBJi9aUPuvYqUlvhgpQpXhmvZQ35wlZqo 21 | BZ85wRNSNgPr6D3tgBH73uWYx3mJvI6W22gznIM/sGg0RmNEI3SYiKnTAoGAJ1mx 22 | d1GhWP08peJPuEIGVteMPoFbLsMnQxp/0XldZ9pSkb7/24Gh3FgmBQ2LvvusQ47d 23 | a6AC/DC0P/kwdCHaFCUQ9JfjxK+PJaHoNkuO6Df50MEsQZWjB95A9sjfMWRpNw56 24 | qIQw8kbuMXvDFhRJANUDIs7bfXjPn+iU+dAxB6ECgYEAusdd0y30VXE9Mj6zJ2Ee 25 | Cm1L9THijgcPH/Q7rD9Vk5yPSXuKO9Q2atgY0COOeQxwmwjf8UHYldDxLjor1nz0 26 | CdwQihCexJSXuoQ9LA/XeTLDm/F/M1Eze+MgOE23dT9qEUOwHrjDsyuYwfev7ir9 27 | a11MWJKcdkeKS/lGXeX3MbE= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /pki/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDLTCCAhWgAwIBAgIUQ67cF8bq3m9pcWq6zJeZRc8N60EwDQYJKoZIhvcNAQEL 3 | BQAwJTEQMA4GA1UEAwwHRHVtbXlDQTERMA8GA1UECgwIRHVtbXlQS0kwIBcNMTgx 4 | MDE1MTkzMDUxWhgPMjExODEwMTUxOTMwNTFaMCUxEDAOBgNVBAMMB0R1bW15Q0Ex 5 | ETAPBgNVBAoMCER1bW15UEtJMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC 6 | AQEA3hxCdi/hV63HVhvCzWf1pJ08xPZbAeCNRm+y3K14v2KvqxJxfIq7toeI9YnJ 7 | g0rsCwUv/6hDTti3xPolNc5rZYYwHxr/EmEl4NbLAGwekr+L789FNKTQgL5yfyvF 8 | De5AFxwda8OXUbzuOwPjNTTvEuqWEy+YyYSvgWjVZj+sPXjkOHPtGbyhThF9BP7s 9 | tanFLkt/OmXDxUcURbxK1KPFo8jAMFfR2aFWt4d7QMsCnw62IxfA7XTRvdxt4oM6 10 | b+CK6OALhEqzxN7eMebojuFMcFDm6JZFbJjgM6O4NUIhItMJR00F9WR93V4GkQBe 11 | b7/1dNmQJ5iOZHl3zwwyKlEX9wIDAQABo1MwUTAdBgNVHQ4EFgQUxHdbXQ7dqFqw 12 | jzzl02L+UefEwNgwHwYDVR0jBBgwFoAUxHdbXQ7dqFqwjzzl02L+UefEwNgwDwYD 13 | VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEApcWGs+A06Aaqr9VAxhDp 14 | i4vSV0K++912IJWRv6rRRy7Hz4b7Ov5UOZ7AHshnQNWQvd9Qp+ehhNoLjL5joWk9 15 | BmwMPGAPhBAvidDh8QULUAIDJWDGF8CtDDaW8oHYjsBTBSVjKu1Ma/OVMc9vF0Ej 16 | MUb3LtlKZkNUqmUCjlE/V2bYYjqVLNTogrXO4KxXyqfBFdUzTCB/qX7V+HNnszDq 17 | IFHF+wyTklMSOwTUIrvW4R6zojXL0wjRdvtBqXSvPFq5HWJPdg0dfVyyVDZu7yPO 18 | 3gMJLqkDkHsaYLs2IKHUXqJCbHCJ/whpMWNYYozDFGrhetNSvo3sKht9wvQK08mc 19 | yg== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /pki/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDg1xvj8JLt4nDq 3 | rRQOGzCys0bD2VdGRnQ/TYC3ezg0whYbQRD7bUdkNQm1KeNOlj8yr8kjz30swIY6 4 | 6Ufw82qGH0QjsmbSOTLr13B9K1ogd11aMxErvVnxtNBOkuwtzKL+Zb1D99jvxX0u 5 | e0dIsBcuojemyoz6iNbU7WSNtbIFeQFr+MlZujHIv0Hj76MYyZAqNq2F3Ou1SduV 6 | kw+Acol8KzTfiII38ibZcP+INgK6YvnrJrxbDZ+NXwsXOeg0y/vZ7hc2h1k5wT3E 7 | xWoyMD/bvnVrKTneQfwOI9s4s3pB6o1hGeUGRhPvpJ20+FGOE8LIRzBEixDKl82T 8 | G63DVoMJAgMBAAECggEBANKVBf0+BA8jZ4iUpFT16G1mdZ/W/uPF9viXGThAAwt+ 9 | wH+0ODiUSCo1dqsj2U5wcC6D74pHukBg7RdeCFBHW3zU6dfZLm40vlmfRS8mnFoO 10 | EfP6IlnqFcTJCdSdzPC0WfCUz2hKSPeA61bOhZwxuPSnYCIqUVIROczhrqz/AQYX 11 | ZuuY7ut0h6X11xoddeeTOdfd+rktxPVpHeJDcz9F6Gk/0pg1ezs66TZ5sWt0/ZhP 12 | ZWTB40KPec2lomRGwY36AMOT3uFucnbtJgxhTGdqsRv7Kl0xCTo7xW/85Za7rhXK 13 | +xRdHrYr9w6xYTKHi9Ap4HEmUcx/fGAtddxr0fzdpIECgYEA8nRyYYHMdSr4WI/E 14 | fD/cpIjKLxw9BCdoXhruVSKp1GmhyQTH7Y0zJQ0uvG7GlsEXJ2V9yqTofzWev8iQ 15 | vVzX+14yTKaNUAwc7HCEXMN+xqnq61Bjldx6U54CfdInOhTFuoq8CXu80cHobYMs 16 | 8SjnYuhp5NqABGR+9YTYaHzjBhkCgYEA7Wa9orxkWruXUdROUZrdhlU44ilxvA66 17 | r1vVVLQFNVle2hbie3b93ZQCZrZL8iFsGKOIhXSncQ6pl9GQqDLJvMUiffzUci6G 18 | A4GgGlzXpkj+RmAkIkGYafmQqTZhMHNws35LMYoIabn0l8cWwG0XL1pO2YSjaoYg 19 | 5Kj0TjoNonECgYAcAk3Qa+FFy+ACwyEMxYfkzhSlWprF5xOMg4ny9d0ut8FD6rR6 20 | Aezdo+c5R4bTlZzqJTRh+6kMQRKEz1PBPH+K/3fKGReMHsocmmcAHGmB49FKu++1 21 | OVI8ZK2fAW8cq5eoFCzi35ORm9gRBq1jcrlAWN8a3A8b8swj6uPhNkQ3yQKBgCH+ 22 | HBk5MIVtZvVomO5GZoHdog+AL7DlywVg+OLwA+7npRVFQZi8KQ2ZK97ZK3a4ImpE 23 | wD+bvH4Lw2zhrPzoiMpmz9GKakEPOFE4NlyP/rDossAQ9BuTmOdTvMr95lyxqumI 24 | o+usABhjcAprj25uMGuvWqr6uwt9uSgEqTaqSVmBAoGAMs6/59L8EABC/CkBLp0t 25 | y3sYzjyjZr6gnMCkIi95b2FWyIVjlK53dwjKYg67BUI8qLNbUW1NPhyotc/9GklP 26 | qiNTkUeKcHOlZqOzP0zPquFk2lSfJuuGrMgElZPcEFLsAWRqnjRGp5iP05nk5OQh 27 | foY7svzPedFk/t3eFcOyrPo= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /pki/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICtDCCAZwCAQAwDQYJKoZIhvcNAQELBQAwJTEQMA4GA1UEAwwHRHVtbXlDQTER 3 | MA8GA1UECgwIRHVtbXlQS0kwIBcNMTgxMDE1MTkzMDUxWhgPMjExODEwMTUxOTMw 4 | NTFaMBkxFzAVBgNVBAMMDmliLm5hb21pbmV0LmpwMIIBIjANBgkqhkiG9w0BAQEF 5 | AAOCAQ8AMIIBCgKCAQEA4Ncb4/CS7eJw6q0UDhswsrNGw9lXRkZ0P02At3s4NMIW 6 | G0EQ+21HZDUJtSnjTpY/Mq/JI899LMCGOulH8PNqhh9EI7Jm0jky69dwfStaIHdd 7 | WjMRK71Z8bTQTpLsLcyi/mW9Q/fY78V9LntHSLAXLqI3psqM+ojW1O1kjbWyBXkB 8 | a/jJWboxyL9B4++jGMmQKjathdzrtUnblZMPgHKJfCs034iCN/Im2XD/iDYCumL5 9 | 6ya8Ww2fjV8LFznoNMv72e4XNodZOcE9xMVqMjA/2751ayk53kH8DiPbOLN6QeqN 10 | YRnlBkYT76SdtPhRjhPCyEcwRIsQypfNkxutw1aDCQIDAQABMA0GCSqGSIb3DQEB 11 | CwUAA4IBAQANOqFqjnGf80vvwYEkUsfOWp8rNVat+8rdXl0BShGBXiDItzMOU79K 12 | YkPObniQ5RLBMhrvqlCsSk0Np+ZgvV12J4Wtmf/znLa5ZKyeI4N1FCefU9cl4xpB 13 | 08Fv8YWbYV7SMNr54ZkURdho4FVR1pAnpuittpAEjMT4R4ubbOH8UEbMTbVgxXdn 14 | 086IAlfsYn0gnOlf76RkJFLe4UlWaZB75SaaXnNavBPN9iFnqXLckg6tsFUJnNMC 15 | esq5aHQ9sXWs4oKpJi8SXxt/zNRmgTnQK2ONM38NeZpLmlWPkyNxzsRNlYWo+kWP 16 | C98jVFe1+3K88ISk0DSN4XOQQnrIvn68 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /platform/amvideo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct amvideo_config { 8 | bool enable; 9 | }; 10 | 11 | HRESULT amvideo_hook_init(const struct amvideo_config *cfg, HMODULE redir_mod); 12 | -------------------------------------------------------------------------------- /platform/clock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct clock_config { 8 | bool timezone; 9 | bool timewarp; 10 | bool writeable; 11 | }; 12 | 13 | HRESULT clock_hook_init(const struct clock_config *cfg); 14 | -------------------------------------------------------------------------------- /platform/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "platform/amvideo.h" 10 | #include "platform/clock.h" 11 | #include "platform/dns.h" 12 | #include "platform/hwmon.h" 13 | #include "platform/misc.h" 14 | #include "platform/netenv.h" 15 | #include "platform/nusec.h" 16 | #include "platform/pcbid.h" 17 | #include "platform/platform.h" 18 | #include "platform/vfs.h" 19 | 20 | void platform_config_load( 21 | struct platform_config *cfg, 22 | const wchar_t *filename); 23 | 24 | void amvideo_config_load(struct amvideo_config *cfg, const wchar_t *filename); 25 | void clock_config_load(struct clock_config *cfg, const wchar_t *filename); 26 | void dns_config_load(struct dns_config *cfg, const wchar_t *filename); 27 | void hwmon_config_load(struct hwmon_config *cfg, const wchar_t *filename); 28 | void misc_config_load(struct misc_config *cfg, const wchar_t *filename); 29 | void netenv_config_load(struct netenv_config *cfg, const wchar_t *filename); 30 | void nusec_config_load(struct nusec_config *cfg, const wchar_t *filename); 31 | void pcbid_config_load(struct pcbid_config *cfg, const wchar_t *filename); 32 | void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename); 33 | -------------------------------------------------------------------------------- /platform/dns.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "hooklib/dns.h" 6 | 7 | #include "platform/dns.h" 8 | 9 | HRESULT dns_platform_hook_init(const struct dns_config *cfg) 10 | { 11 | HRESULT hr; 12 | 13 | assert(cfg != NULL); 14 | 15 | if (!cfg->enable) { 16 | return S_FALSE; 17 | } 18 | 19 | hr = dns_hook_push(L"tenporouter.loc", cfg->router); 20 | 21 | if (FAILED(hr)) { 22 | return hr; 23 | } 24 | 25 | hr = dns_hook_push(L"bbrouter.loc", cfg->router); 26 | 27 | if (FAILED(hr)) { 28 | return hr; 29 | } 30 | 31 | hr = dns_hook_push(L"naominet.jp", cfg->startup); 32 | 33 | if (FAILED(hr)) { 34 | return hr; 35 | } 36 | 37 | hr = dns_hook_push(L"anbzvarg.wc", cfg->startup); 38 | 39 | if (FAILED(hr)) { 40 | return hr; 41 | } 42 | 43 | hr = dns_hook_push(L"ib.naominet.jp", cfg->billing); 44 | 45 | if (FAILED(hr)) { 46 | return hr; 47 | } 48 | 49 | hr = dns_hook_push(L"vo.anbzvarg.wc", cfg->billing); 50 | 51 | if (FAILED(hr)) { 52 | return hr; 53 | } 54 | 55 | hr = dns_hook_push(L"aime.naominet.jp", cfg->aimedb); 56 | 57 | if (FAILED(hr)) { 58 | return hr; 59 | } 60 | 61 | hr = dns_hook_push(L"nvzr.anbzvarg.wc", cfg->aimedb); 62 | 63 | if (FAILED(hr)) { 64 | return hr; 65 | } 66 | 67 | // if your ISP resolves bad domains, it will kill the network. These 2 68 | // *cannot* resolve 69 | 70 | hr = dns_hook_push(L"mobirouter.loc", NULL); 71 | 72 | if (FAILED(hr)) { 73 | return hr; 74 | } 75 | 76 | hr = dns_hook_push(L"dslrouter.loc", NULL); 77 | 78 | if (FAILED(hr)) { 79 | return hr; 80 | } 81 | 82 | return S_OK; 83 | } 84 | -------------------------------------------------------------------------------- /platform/dns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct dns_config { 9 | bool enable; 10 | wchar_t router[128]; 11 | wchar_t startup[128]; 12 | wchar_t billing[128]; 13 | wchar_t aimedb[128]; 14 | }; 15 | 16 | HRESULT dns_platform_hook_init(const struct dns_config *cfg); 17 | -------------------------------------------------------------------------------- /platform/hwmon.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "hook/iohook.h" 7 | 8 | #include "platform/hwmon.h" 9 | 10 | #include "util/dprintf.h" 11 | #include "util/str.h" 12 | 13 | enum { 14 | HWMON_IOCTL_READ_CPU_TEMP = 0x80006000, 15 | }; 16 | 17 | static HRESULT hwmon_handle_irp(struct irp *irp); 18 | static HRESULT hwmon_handle_open(struct irp *irp); 19 | static HRESULT hwmon_handle_close(struct irp *irp); 20 | static HRESULT hwmon_handle_ioctl(struct irp *irp); 21 | 22 | static HRESULT hwmon_ioctl_read_cpu_temp(struct irp *irp); 23 | 24 | static HANDLE hwmon_fd; 25 | 26 | HRESULT hwmon_hook_init(const struct hwmon_config *cfg) 27 | { 28 | HRESULT hr; 29 | 30 | assert(cfg != NULL); 31 | 32 | if (!cfg->enable) { 33 | return S_FALSE; 34 | } 35 | 36 | hr = iohook_open_nul_fd(&hwmon_fd); 37 | 38 | if (FAILED(hr)) { 39 | return hr; 40 | } 41 | 42 | hr = iohook_push_handler(hwmon_handle_irp); 43 | 44 | if (FAILED(hr)) { 45 | return hr; 46 | } 47 | 48 | return S_OK; 49 | } 50 | 51 | static HRESULT hwmon_handle_irp(struct irp *irp) 52 | { 53 | assert(irp != NULL); 54 | 55 | if (irp->op != IRP_OP_OPEN && irp->fd != hwmon_fd) { 56 | return iohook_invoke_next(irp); 57 | } 58 | 59 | switch (irp->op) { 60 | case IRP_OP_OPEN: return hwmon_handle_open(irp); 61 | case IRP_OP_CLOSE: return hwmon_handle_close(irp); 62 | case IRP_OP_IOCTL: return hwmon_handle_ioctl(irp); 63 | default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); 64 | } 65 | } 66 | 67 | static HRESULT hwmon_handle_open(struct irp *irp) 68 | { 69 | if (!wstr_ieq(irp->open_filename, L"\\\\.\\sghwmonitor")) { 70 | return iohook_invoke_next(irp); 71 | } 72 | 73 | dprintf("Hwmon: Opened device\n"); 74 | irp->fd = hwmon_fd; 75 | 76 | return S_OK; 77 | } 78 | 79 | static HRESULT hwmon_handle_close(struct irp *irp) 80 | { 81 | dprintf("Hwmon: Closed device\n"); 82 | 83 | return S_OK; 84 | } 85 | 86 | static HRESULT hwmon_handle_ioctl(struct irp *irp) 87 | { 88 | switch (irp->ioctl) { 89 | case HWMON_IOCTL_READ_CPU_TEMP: 90 | return hwmon_ioctl_read_cpu_temp(irp); 91 | 92 | default: 93 | dprintf("Hwmon: Unknown ioctl %08x, write %i read %i\n", 94 | irp->ioctl, 95 | (int) irp->write.nbytes, 96 | (int) irp->read.nbytes); 97 | 98 | return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); 99 | } 100 | } 101 | 102 | static HRESULT hwmon_ioctl_read_cpu_temp(struct irp *irp) 103 | { 104 | /* Assuming this is Celsius. It also seems to be biased by -23 (based on the 105 | caller code) despite being a 32-bit int. */ 106 | 107 | return iobuf_write_le32(&irp->read, 52); 108 | } 109 | -------------------------------------------------------------------------------- /platform/hwmon.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct hwmon_config { 8 | bool enable; 9 | }; 10 | 11 | HRESULT hwmon_hook_init(const struct hwmon_config *cfg); 12 | -------------------------------------------------------------------------------- /platform/meson.build: -------------------------------------------------------------------------------- 1 | platform_lib = static_library( 2 | 'platform', 3 | include_directories : inc, 4 | implicit_include_directories : false, 5 | c_pch : '../precompiled.h', 6 | dependencies : [ 7 | capnhook.get_variable('hook_dep'), 8 | shlwapi_lib, 9 | ], 10 | sources : [ 11 | 'amvideo.c', 12 | 'amvideo.h', 13 | 'clock.c', 14 | 'clock.h', 15 | 'config.c', 16 | 'config.h', 17 | 'dns.c', 18 | 'dns.h', 19 | 'hwmon.c', 20 | 'hwmon.h', 21 | 'misc.c', 22 | 'misc.h', 23 | 'netenv.c', 24 | 'netenv.h', 25 | 'nusec.c', 26 | 'nusec.h', 27 | 'pcbid.c', 28 | 'pcbid.h', 29 | 'platform.c', 30 | 'platform.h', 31 | 'vfs.c', 32 | 'vfs.h', 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /platform/misc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct misc_config { 6 | bool enable; 7 | }; 8 | 9 | HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id); 10 | -------------------------------------------------------------------------------- /platform/netenv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "platform/nusec.h" 9 | 10 | struct netenv_config { 11 | bool enable; 12 | uint8_t addr_suffix; 13 | uint8_t router_suffix; 14 | uint8_t mac_addr[6]; 15 | }; 16 | 17 | HRESULT netenv_hook_init( 18 | const struct netenv_config *cfg, 19 | const struct nusec_config *kc_cfg); 20 | 21 | -------------------------------------------------------------------------------- /platform/nusec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | struct nusec_config { 10 | bool enable; 11 | char keychip_id[16]; 12 | char game_id[4]; 13 | char platform_id[4]; 14 | uint8_t region; 15 | uint8_t system_flag; 16 | uint32_t subnet; 17 | wchar_t billing_ca[MAX_PATH]; 18 | wchar_t billing_pub[MAX_PATH]; 19 | }; 20 | 21 | HRESULT nusec_hook_init( 22 | const struct nusec_config *cfg, 23 | const char *game_id, 24 | const char *platform_id); 25 | -------------------------------------------------------------------------------- /platform/pcbid.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "hook/table.h" 9 | 10 | #include "platform/pcbid.h" 11 | 12 | #include "util/dprintf.h" 13 | 14 | static BOOL WINAPI pcbid_GetComputerNameA(char *dest, uint32_t *len); 15 | 16 | static struct pcbid_config pcbid_cfg; 17 | 18 | static const struct hook_symbol pcbid_syms[] = { 19 | { 20 | .name = "GetComputerNameA", 21 | .patch = pcbid_GetComputerNameA, 22 | } 23 | }; 24 | 25 | HRESULT pcbid_hook_init(const struct pcbid_config *cfg) 26 | { 27 | assert(cfg != NULL); 28 | 29 | if (!cfg->enable) { 30 | return S_FALSE; 31 | } 32 | 33 | if (wcslen(cfg->serial_no) != 15) { 34 | dprintf("Pcbid: ERROR: Must be 15 chars! ex: ACAE01A99999999\n"); 35 | 36 | return E_INVALIDARG; 37 | } 38 | 39 | memcpy(&pcbid_cfg, cfg, sizeof(*cfg)); 40 | hook_table_apply(NULL, "kernel32.dll", pcbid_syms, _countof(pcbid_syms)); 41 | 42 | return S_OK; 43 | } 44 | 45 | static BOOL WINAPI pcbid_GetComputerNameA(char *dest, uint32_t *len) 46 | { 47 | size_t required; 48 | 49 | if (dest == NULL || len == NULL) { 50 | SetLastError(ERROR_INVALID_PARAMETER); 51 | 52 | return FALSE; 53 | } 54 | 55 | wcstombs_s(&required, NULL, 0, pcbid_cfg.serial_no, 0); 56 | 57 | if (required > *len) { 58 | SetLastError(ERROR_INSUFFICIENT_BUFFER); 59 | 60 | return FALSE; 61 | } 62 | 63 | dprintf("Pcbid: Get PCB serial\n"); 64 | 65 | wcstombs_s(NULL, dest, *len, pcbid_cfg.serial_no, *len - 1); 66 | SetLastError(ERROR_SUCCESS); 67 | *len = required - 1; 68 | 69 | return TRUE; 70 | } 71 | -------------------------------------------------------------------------------- /platform/pcbid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct pcbid_config { 9 | bool enable; 10 | wchar_t serial_no[17]; 11 | }; 12 | 13 | HRESULT pcbid_hook_init(const struct pcbid_config *cfg); 14 | -------------------------------------------------------------------------------- /platform/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "platform/amvideo.h" 6 | #include "platform/clock.h" 7 | #include "platform/dns.h" 8 | #include "platform/hwmon.h" 9 | #include "platform/misc.h" 10 | #include "platform/netenv.h" 11 | #include "platform/nusec.h" 12 | #include "platform/pcbid.h" 13 | #include "platform/platform.h" 14 | #include "platform/vfs.h" 15 | 16 | HRESULT platform_hook_init( 17 | const struct platform_config *cfg, 18 | const char *game_id, 19 | const char *platform_id, 20 | HMODULE redir_mod) 21 | { 22 | HRESULT hr; 23 | 24 | assert(cfg != NULL); 25 | assert(game_id != NULL); 26 | assert(platform_id != NULL); 27 | assert(redir_mod != NULL); 28 | 29 | hr = amvideo_hook_init(&cfg->amvideo, redir_mod); 30 | 31 | if (FAILED(hr)) { 32 | return hr; 33 | } 34 | 35 | hr = clock_hook_init(&cfg->clock); 36 | 37 | if (FAILED(hr)) { 38 | return hr; 39 | } 40 | 41 | hr = dns_platform_hook_init(&cfg->dns); 42 | 43 | if (FAILED(hr)) { 44 | return hr; 45 | } 46 | 47 | hr = hwmon_hook_init(&cfg->hwmon); 48 | 49 | if (FAILED(hr)) { 50 | return hr; 51 | } 52 | 53 | hr = misc_hook_init(&cfg->misc, platform_id); 54 | 55 | if (FAILED(hr)) { 56 | return hr; 57 | } 58 | 59 | hr = netenv_hook_init(&cfg->netenv, &cfg->nusec); 60 | 61 | if (FAILED(hr)) { 62 | return hr; 63 | } 64 | 65 | hr = nusec_hook_init(&cfg->nusec, game_id, platform_id); 66 | 67 | if (FAILED(hr)) { 68 | return hr; 69 | } 70 | 71 | hr = pcbid_hook_init(&cfg->pcbid); 72 | 73 | if (FAILED(hr)) { 74 | return hr; 75 | } 76 | 77 | hr = vfs_hook_init(&cfg->vfs); 78 | 79 | if (FAILED(hr)) { 80 | return hr; 81 | } 82 | 83 | return S_OK; 84 | } 85 | -------------------------------------------------------------------------------- /platform/platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "platform/amvideo.h" 6 | #include "platform/clock.h" 7 | #include "platform/dns.h" 8 | #include "platform/hwmon.h" 9 | #include "platform/misc.h" 10 | #include "platform/netenv.h" 11 | #include "platform/nusec.h" 12 | #include "platform/pcbid.h" 13 | #include "platform/vfs.h" 14 | 15 | struct platform_config { 16 | struct amvideo_config amvideo; 17 | struct clock_config clock; 18 | struct dns_config dns; 19 | struct hwmon_config hwmon; 20 | struct misc_config misc; 21 | struct pcbid_config pcbid; 22 | struct netenv_config netenv; 23 | struct nusec_config nusec; 24 | struct vfs_config vfs; 25 | }; 26 | 27 | HRESULT platform_hook_init( 28 | const struct platform_config *cfg, 29 | const char *game_id, 30 | const char *platform_id, 31 | HMODULE redir_mod); 32 | -------------------------------------------------------------------------------- /platform/vfs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct vfs_config { 9 | bool enable; 10 | wchar_t amfs[MAX_PATH]; 11 | wchar_t appdata[MAX_PATH]; 12 | wchar_t option[MAX_PATH]; 13 | }; 14 | 15 | HRESULT vfs_hook_init(const struct vfs_config *config); 16 | -------------------------------------------------------------------------------- /precompiled.h: -------------------------------------------------------------------------------- 1 | /* 2 | Making NTSTATUS available is slightly awkward. See: 3 | https://kirkshoop.github.io/2011/09/20/ntstatus.html 4 | */ 5 | 6 | /* Win32 user-mode API */ 7 | #define WIN32_NO_STATUS 8 | #include 9 | #undef WIN32_NO_STATUS 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | /* Win32 kernel-mode definitions */ 20 | #ifdef __GNUC__ 21 | /* MinGW needs to include this for PHYSICAL_ADDRESS to be defined. 22 | The MS SDK throws a bunch of duplicate symbol errors instead. */ 23 | #include 24 | #else 25 | #include 26 | #endif 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | -------------------------------------------------------------------------------- /reg/chunithm.reg: -------------------------------------------------------------------------------- 1 | Windows Registry Editor Version 5.00 2 | 3 | [HKEY_LOCAL_MACHINE\SYSTEM\SEGA] 4 | 5 | [HKEY_LOCAL_MACHINE\SYSTEM\SEGA\SystemProperty] 6 | 7 | [HKEY_LOCAL_MACHINE\SYSTEM\SEGA\SystemProperty\keychip] 8 | "systemFlag"=dword:00000064 9 | "modelType"=dword:00000001 10 | "region"=dword:00000001 11 | "serverIpIpv4"=hex:c0,a8,8b,00 12 | "keychipId"=hex:41,36,39,45,2d,30,31,41,39,39,39,39,39,39,39,39 13 | "platformId"=hex:41,41,56 14 | "gameId"=hex:53,42,5a,56 15 | 16 | [HKEY_LOCAL_MACHINE\SYSTEM\SEGA\SystemProperty\static] 17 | "CpuTempWarning"=dword:0000003c 18 | "CpuTempError"=dword:00000050 19 | "PlatformId"=hex:41,41,56 20 | 21 | [HKEY_LOCAL_MACHINE\SYSTEM\SEGA\SystemProperty\wirelessNetwork] 22 | "main_nic"=dword:00000000 23 | 24 | [HKEY_LOCAL_MACHINE\SYSTEM\SEGA\SystemProperty\mount] 25 | "AMFS"="E:\\" 26 | "APPDATA"="Y:\\" 27 | 28 | -------------------------------------------------------------------------------- /spike/chunithm-1.25.00.txt: -------------------------------------------------------------------------------- 1 | # Title server WinHTTP 2 | 3 | j_printf 0x00BE5BD2 4 | j_printf 0x00BE7B95 5 | j_printf 0x00BE9B29 6 | 7 | # libam(?) logging functions 8 | 9 | j_printf 0x007E9041 10 | j_wprintf 0x007F9AF1 11 | j_puts 0x00AFBB71 12 | j_perror 0x00AFE3BF 13 | 14 | # Log levels 15 | 16 | levels 0x01907904 2 17 | levels 0x01907B10 1 18 | levels 0x01907C44 1 19 | levels 0x01974CB0 1 20 | levels 0x019751B8 14 21 | levels 0x01975F00 5 22 | levels 0x01A720D4 4 23 | levels 0x01A7B22C 2 24 | levels 0x01A7B608 2 25 | 26 | # ALLNET logging 27 | 28 | c_fputs 0x019763E8 29 | levels 0x019763DC 1 30 | levels 0x019763E4 1 31 | -------------------------------------------------------------------------------- /subprojects/capnhook.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory = capnhook 3 | url = https://github.com/decafcode/capnhook 4 | revision = 69f7e3b48c2e0ff5be1d7a83cdcc2597a458357b 5 | -------------------------------------------------------------------------------- /util/async.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "hook/iohook.h" 8 | 9 | typedef HRESULT (*async_task_t)(void *ctx, struct irp *irp); 10 | 11 | struct async { 12 | CRITICAL_SECTION lock; 13 | CONDITION_VARIABLE pend; 14 | CONDITION_VARIABLE avail; 15 | HANDLE thread; 16 | struct irp irp; 17 | async_task_t task; 18 | void *ctx; 19 | bool stop; 20 | }; 21 | 22 | void async_init(struct async *async, void *ctx); 23 | void async_fini(struct async *async); 24 | HRESULT async_submit(struct async *async, struct irp *irp, async_task_t task); 25 | -------------------------------------------------------------------------------- /util/crc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "util/crc.h" 5 | 6 | uint32_t crc32(const void *src, size_t nbytes, uint32_t in) 7 | { 8 | const uint8_t *bytes; 9 | uint32_t crc; 10 | size_t i; 11 | 12 | bytes = src; 13 | crc = ~in; 14 | 15 | for (i = 0 ; i < nbytes * 8 ; i++) { 16 | if (i % 8 == 0) { 17 | crc ^= *bytes++; 18 | } 19 | 20 | if (crc & 1) { 21 | crc = (crc >> 1) ^ 0xEDB88320; 22 | } else { 23 | crc = (crc >> 1); 24 | } 25 | } 26 | 27 | return ~crc; 28 | } 29 | -------------------------------------------------------------------------------- /util/crc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | uint32_t crc32(const void *src, size_t nbytes, uint32_t in); 7 | -------------------------------------------------------------------------------- /util/dll-bind.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "util/dll-bind.h" 7 | #include "util/dprintf.h" 8 | 9 | HRESULT dll_bind( 10 | void *dest, 11 | HINSTANCE src, 12 | const struct dll_bind_sym **syms_pos, 13 | size_t syms_count) 14 | { 15 | HRESULT hr; 16 | void *src_result; 17 | void **dest_field; 18 | const struct dll_bind_sym *current_sym; 19 | size_t i; 20 | 21 | assert(dest != NULL); 22 | assert(src != NULL); 23 | assert(syms_pos != NULL); 24 | 25 | hr = S_OK; 26 | current_sym = *syms_pos; 27 | 28 | assert(current_sym != NULL); 29 | 30 | for (i = 0; i < syms_count; i++) { 31 | src_result = GetProcAddress(src, current_sym->sym); 32 | 33 | if (src_result == NULL) { 34 | hr = E_NOTIMPL; 35 | 36 | break; 37 | } 38 | 39 | dest_field = (void **)(((uint8_t *)dest) + current_sym->off); 40 | *dest_field = src_result; 41 | current_sym++; 42 | } 43 | 44 | *syms_pos = current_sym; 45 | 46 | return hr; 47 | } 48 | -------------------------------------------------------------------------------- /util/dll-bind.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct dll_bind_sym { 8 | /* Symbol name to locate in the source DLL */ 9 | const char *sym; 10 | 11 | /* Offset where the symbol pointer should be written in the target 12 | structure */ 13 | ptrdiff_t off; 14 | }; 15 | 16 | /* 17 | Bind a list of DLL symbols into a structure that contains function 18 | pointers. 19 | 20 | - dest: Pointer to destination structure. 21 | - src: Handle to source DLL. 22 | - syms_pos: Pointer to a (mutable) pointer which points to the start of an 23 | (immutable) table of symbols and structure offsets. This mutable 24 | pointer is advanced until either a symbol fails to bind, in which case 25 | an error is returned, or the end of the table is reached, in which 26 | case success is returned. 27 | - syms_count: Number of entries in the symbol table. 28 | */ 29 | HRESULT dll_bind( 30 | void *dest, 31 | HINSTANCE src, 32 | const struct dll_bind_sym **syms_pos, 33 | size_t syms_count); 34 | -------------------------------------------------------------------------------- /util/dprintf.c: -------------------------------------------------------------------------------- 1 | #ifndef NDEBUG 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "util/dprintf.h" 11 | 12 | static long dbg_buf_lock_init; 13 | static CRITICAL_SECTION dbg_buf_lock; 14 | static char dbg_buf[16384]; 15 | static size_t dbg_buf_pos; 16 | 17 | void dprintf(const char *fmt, ...) 18 | { 19 | va_list ap; 20 | 21 | va_start(ap, fmt); 22 | dprintfv(fmt, ap); 23 | va_end(ap); 24 | } 25 | 26 | void dprintfv(const char *fmt, va_list ap) 27 | { 28 | long init; 29 | 30 | /* Static constructors in C are difficult to do in a way that works under 31 | both GCC and MSVC, so we have to use atomic ops to ensure that the 32 | buffer mutex is correctly initialized instead. */ 33 | 34 | do { 35 | init = InterlockedCompareExchange(&dbg_buf_lock_init, 0, 1); 36 | 37 | if (init == 0) { 38 | /* We won the init race, global variable is now set to 1, other 39 | threads will spin until it becomes -1. */ 40 | InitializeCriticalSection(&dbg_buf_lock); 41 | dbg_buf_lock_init = -1; 42 | init = -1; 43 | } 44 | } while (init >= 0); 45 | 46 | EnterCriticalSection(&dbg_buf_lock); 47 | 48 | dbg_buf_pos += vsnprintf_s( 49 | dbg_buf + dbg_buf_pos, 50 | sizeof(dbg_buf) - dbg_buf_pos, 51 | sizeof(dbg_buf) - dbg_buf_pos - 1, 52 | fmt, 53 | ap); 54 | 55 | if (dbg_buf_pos + 1 > sizeof(dbg_buf)) { 56 | abort(); 57 | } 58 | 59 | if (strchr(dbg_buf, '\n') != NULL) { 60 | OutputDebugStringA(dbg_buf); 61 | dbg_buf_pos = 0; 62 | dbg_buf[0] = '\0'; 63 | } 64 | 65 | LeaveCriticalSection(&dbg_buf_lock); 66 | } 67 | 68 | void dwprintf(const wchar_t *fmt, ...) 69 | { 70 | va_list ap; 71 | 72 | va_start(ap, fmt); 73 | dwprintfv(fmt, ap); 74 | va_end(ap); 75 | } 76 | 77 | void dwprintfv(const wchar_t *fmt, va_list ap) 78 | { 79 | wchar_t msg[512]; 80 | 81 | _vsnwprintf_s(msg, _countof(msg), _countof(msg) - 1, fmt, ap); 82 | OutputDebugStringW(msg); 83 | } 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /util/dprintf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __GNUC__ 7 | #define DPRINTF_CHK __attribute__(( format(printf, 1, 2) )) 8 | #else 9 | #define DPRINTF_CHK 10 | #endif 11 | 12 | #ifndef NDEBUG 13 | void dprintf(const char *fmt, ...) DPRINTF_CHK; 14 | void dprintfv(const char *fmt, va_list ap); 15 | void dwprintf(const wchar_t *fmt, ...); 16 | void dwprintfv(const wchar_t *fmt, va_list ap); 17 | #else 18 | #define dprintf(...) 19 | #define dprintfv(fmt, ap) 20 | #define dwprintf(...) 21 | #define dwprintfv(fmt, ap) 22 | #endif 23 | -------------------------------------------------------------------------------- /util/dump.c: -------------------------------------------------------------------------------- 1 | #ifndef NDEBUG 2 | 3 | #include 4 | #include 5 | 6 | #include "hook/iobuf.h" 7 | 8 | #include "util/dprintf.h" 9 | #include "util/dump.h" 10 | 11 | void dump(const void *ptr, size_t nbytes) 12 | { 13 | const uint8_t *bytes; 14 | uint8_t c; 15 | size_t i; 16 | size_t j; 17 | 18 | assert(ptr != NULL || nbytes == 0); 19 | 20 | if (nbytes == 0) { 21 | dprintf("\t--- Empty ---\n"); 22 | } 23 | 24 | bytes = ptr; 25 | 26 | for (i = 0 ; i < nbytes ; i += 16) { 27 | dprintf(" %08x:", (int) i); 28 | 29 | for (j = 0 ; i + j < nbytes && j < 16 ; j++) { 30 | dprintf(" %02x", bytes[i + j]); 31 | } 32 | 33 | while (j < 16) { 34 | dprintf(" "); 35 | j++; 36 | } 37 | 38 | dprintf(" "); 39 | 40 | for (j = 0 ; i + j < nbytes && j < 16 ; j++) { 41 | c = bytes[i + j]; 42 | 43 | if (c < 0x20 || c >= 0x7F) { 44 | c = '.'; 45 | } 46 | 47 | dprintf("%c", c); 48 | } 49 | 50 | dprintf("\n"); 51 | } 52 | 53 | dprintf("\n"); 54 | } 55 | 56 | void dump_iobuf(const struct iobuf *iobuf) 57 | { 58 | assert(iobuf != NULL); 59 | assert(iobuf->bytes != NULL || iobuf->nbytes == 0); 60 | assert(iobuf->pos <= iobuf->nbytes); 61 | 62 | dump(iobuf->bytes, iobuf->pos); 63 | } 64 | 65 | void dump_const_iobuf(const struct const_iobuf *iobuf) 66 | { 67 | assert(iobuf != NULL); 68 | assert(iobuf->bytes != NULL || iobuf->nbytes == 0); 69 | assert(iobuf->pos <= iobuf->nbytes); 70 | 71 | dump(&iobuf->bytes[iobuf->pos], iobuf->nbytes - iobuf->pos); 72 | } 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /util/dump.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "hook/iobuf.h" 6 | 7 | #ifndef NDEBUG 8 | void dump(const void *ptr, size_t nbytes); 9 | void dump_iobuf(const struct iobuf *iobuf); 10 | void dump_const_iobuf(const struct const_iobuf *iobuf); 11 | #else 12 | #define dump(ptr, nbytes) 13 | #define dump_iobuf(iobuf) 14 | #define dump_const_iobuf(iobuf) 15 | #endif 16 | -------------------------------------------------------------------------------- /util/meson.build: -------------------------------------------------------------------------------- 1 | util_lib = static_library( 2 | 'util', 3 | include_directories : inc, 4 | implicit_include_directories : false, 5 | c_pch : '../precompiled.h', 6 | dependencies : [ 7 | capnhook.get_variable('hook_dep'), 8 | ], 9 | sources : [ 10 | 'async.c', 11 | 'async.h', 12 | 'crc.c', 13 | 'crc.h', 14 | 'dll-bind.c', 15 | 'dll-bind.h', 16 | 'dprintf.c', 17 | 'dprintf.h', 18 | 'dump.c', 19 | 'dump.h', 20 | 'str.c', 21 | 'str.h', 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /util/str.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "util/str.h" 7 | 8 | extern inline bool str_eq(const char *lhs, const char *rhs); 9 | extern inline bool str_ieq(const char *lhs, const char *rhs); 10 | extern inline bool wstr_eq(const wchar_t *lhs, const wchar_t *rhs); 11 | extern inline bool wstr_ieq(const wchar_t *lhs, const wchar_t *rhs); 12 | -------------------------------------------------------------------------------- /util/str.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | inline bool str_eq(const char *lhs, const char *rhs) 8 | { 9 | if (lhs == NULL || rhs == NULL) { 10 | return lhs == rhs; 11 | } 12 | 13 | return strcmp(lhs, rhs) == 0; 14 | } 15 | 16 | inline bool str_ieq(const char *lhs, const char *rhs) 17 | { 18 | if (lhs == NULL || rhs == NULL) { 19 | return lhs == rhs; 20 | } 21 | 22 | return _stricmp(lhs, rhs) == 0; 23 | } 24 | 25 | inline bool wstr_eq(const wchar_t *lhs, const wchar_t *rhs) 26 | { 27 | if (lhs == NULL || rhs == NULL) { 28 | return lhs == rhs; 29 | } 30 | 31 | return wcscmp(lhs, rhs) == 0; 32 | } 33 | 34 | inline bool wstr_ieq(const wchar_t *lhs, const wchar_t *rhs) 35 | { 36 | if (lhs == NULL || rhs == NULL) { 37 | return lhs == rhs; 38 | } 39 | 40 | return _wcsicmp(lhs, rhs) == 0; 41 | } 42 | --------------------------------------------------------------------------------