From a2db39c58c9596d4ee49d8f5996242371b033cc6 Mon Sep 17 00:00:00 2001 From: Dniel97 Date: Sat, 11 Nov 2023 22:47:47 +0100 Subject: [PATCH] fgo: fgohook finally added Credits: - Coburn - domeori - Mitsuhide - OLEG - rakisaionji --- Package.mk | 16 + dist/fgo/config_hook.json | 12 + dist/fgo/segatools.ini | 84 ++ dist/fgo/start.bat | 11 + fgohook/config.c | 94 ++ fgohook/config.h | 37 + fgohook/deck.c | 511 ++++++++ fgohook/deck.h | 11 + fgohook/dllmain.c | 144 +++ fgohook/fgo-dll.c | 112 ++ fgohook/fgo-dll.h | 22 + fgohook/fgohook.def | 82 ++ fgohook/ftdi.c | 55 + fgohook/ftdi.h | 20 + fgohook/io4.c | 104 ++ fgohook/io4.h | 7 + fgohook/led1509306.c | 379 ++++++ fgohook/led1509306.h | 16 + fgohook/meson.build | 36 + fgoio/config.c | 20 + fgoio/config.h | 16 + fgoio/fgoio.c | 141 +++ fgoio/fgoio.h | 71 ++ fgoio/meson.build | 16 + hooklib/config.c | 59 + hooklib/config.h | 2 + hooklib/meson.build | 2 + hooklib/printer.c | 2380 +++++++++++++++++++++++++++++++++++++ hooklib/printer.h | 16 + meson.build | 2 + 30 files changed, 4478 insertions(+) create mode 100644 dist/fgo/config_hook.json create mode 100644 dist/fgo/segatools.ini create mode 100644 dist/fgo/start.bat create mode 100644 fgohook/config.c create mode 100644 fgohook/config.h create mode 100644 fgohook/deck.c create mode 100644 fgohook/deck.h create mode 100644 fgohook/dllmain.c create mode 100644 fgohook/fgo-dll.c create mode 100644 fgohook/fgo-dll.h create mode 100644 fgohook/fgohook.def create mode 100644 fgohook/ftdi.c create mode 100644 fgohook/ftdi.h create mode 100644 fgohook/io4.c create mode 100644 fgohook/io4.h create mode 100644 fgohook/led1509306.c create mode 100644 fgohook/led1509306.h create mode 100644 fgohook/meson.build create mode 100644 fgoio/config.c create mode 100644 fgoio/config.h create mode 100644 fgoio/fgoio.c create mode 100644 fgoio/fgoio.h create mode 100644 fgoio/meson.build create mode 100644 hooklib/printer.c create mode 100644 hooklib/printer.h diff --git a/Package.mk b/Package.mk index 69cdbc1..6ccc6d0 100644 --- a/Package.mk +++ b/Package.mk @@ -73,6 +73,22 @@ $(BUILD_DIR_ZIP)/idz.zip: $(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll} $(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip * +$(BUILD_DIR_ZIP)/fgo.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/fgo + $(V)mkdir -p $(BUILD_DIR_ZIP)/fgo/DEVICE + $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_64)/fgohook/fgohook.dll \ + $(DIST_DIR)/fgo/config_hook.json \ + $(DIST_DIR)/fgo/segatools.ini \ + $(DIST_DIR)/fgo/start.bat \ + $(BUILD_DIR_ZIP)/fgo + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/fgo/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/fgo/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/fgo ; zip -r ../fgo.zip * + $(BUILD_DIR_ZIP)/idac.zip: $(V)echo ... $@ $(V)mkdir -p $(BUILD_DIR_ZIP)/idac diff --git a/dist/fgo/config_hook.json b/dist/fgo/config_hook.json new file mode 100644 index 0000000..cc1ab5d --- /dev/null +++ b/dist/fgo/config_hook.json @@ -0,0 +1,12 @@ +{ + "aime" : + { + "firmware_path" : + [ + "am\\aime_firm\\update_15396_6728_94.bin", + "am\\aime_firm\\TN32MSEC003S_V12.hex", + "am\\aime_firm\\837-15286-P_LPC1112_NFC_RW_LED_BD_0x92.bin" + ], + "high_baudrate" : true + } +} diff --git a/dist/fgo/segatools.ini b/dist/fgo/segatools.ini new file mode 100644 index 0000000..67c0f91 --- /dev/null +++ b/dist/fgo/segatools.ini @@ -0,0 +1,84 @@ +[vfs] +; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) +amfs= +; Insert the path to the game Option directory here (contains Axxx directories) +option= +; Create an empty directory somewhere and insert the path here. +; This directory may be shared between multiple SEGA games. +; NOTE: This has nothing to do with Windows %APPDATA%. +appdata= + +[aime] +; Controls emulation of the Aime card reader assembly. +enable=1 +aimePath=DEVICE\aime.txt + +[dns] +; Insert the hostname or IP address of the server you wish to use here. +; Note that 127.0.0.1, localhost etc are specifically rejected. +default=127.0.0.1 + +[netenv] +; Simulate an ideal LAN environment. This may interfere with head-to-head play. +; SEGA games are somewhat picky about their LAN environment, so leaving this +; setting enabled is recommended. +enable=1 + +; The final octet of the local host's IP address on the virtualized subnet (so, +; if the keychip subnet is `192.168.32.0` and this value is set to `11`, then the +; local host's virtualized LAN IP is `192.168.32.11`). +addrSuffix=11 + +[keychip] +; The /24 LAN subnet that the emulated keychip will tell the game to expect. +; If you disable netenv then you must set this to your LAN's IP subnet, and +; that subnet must start with 192.168. +subnet=192.168.172.0 + +[touch] +; WinTouch emulation setting. +enable=1 +remap=1 +cursor=1 + +[printer] +; Sinfonia CHC-C330 printer emulation setting. +enable=1 +; Change the printer serial number here. +serial_no="5A-A123" +; Insert the path to the image output directory here. +printerOutPath="DEVICE\print" +; Rotate all printed images by 180 degrees. +rotate180=1 + +[deckReader] +; 837-15345 RFID deck reader emulation setting. +enable=1 + +[ledstrip] +; 837-15093-06 LED strip emulation setting. +enable=1 +; FTDI board currently not working properly. Use com0com (Create a virtual pair +; for 21 and 51) or use any USB to Serial Adapter. +port=21 + +; ----------------------------------------------------------------------------- +; Input settings +; ----------------------------------------------------------------------------- + +; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal +; (not prefixed with 0x) virtual-key codes, a list of which can be found here: +; +; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +; +; This is, admittedly, not the most user-friendly configuration method in the +; world. An improved solution will be provided later. + +[io4] +; Input API selection for JVS input emulator. +; Test button virtual-key code. Default is the 1 key. +test=0x31 +; Service button virtual-key code. Default is the 2 key. +service=0x32 +; Keyboard button to increment coin counter. Default is the 3 key. +coin=0x33 diff --git a/dist/fgo/start.bat b/dist/fgo/start.bat new file mode 100644 index 0000000..3be4f8a --- /dev/null +++ b/dist/fgo/start.bat @@ -0,0 +1,11 @@ +@echo off + +cd /d %~dp0 + +inject -d -k fgohook.dll ago.exe + +taskkill /f /im amdaemon.exe > nul 2>&1 + +echo. +echo Game processes have terminated +pause \ No newline at end of file diff --git a/fgohook/config.c b/fgohook/config.c new file mode 100644 index 0000000..d6971ac --- /dev/null +++ b/fgohook/config.c @@ -0,0 +1,94 @@ +#include +#include + +#include "board/config.h" + +#include "hooklib/config.h" +#include "hooklib/dvd.h" + +#include "fgohook/config.h" + +#include "platform/config.h" + +void fgo_dll_config_load( + struct fgo_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"fgoio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + +void ftdi_config_load(struct ftdi_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"ftdi", L"enable", 1, filename); +} + +void led1509306_config_load(struct led1509306_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + wchar_t tmpstr[16]; + + memset(cfg->board_number, ' ', sizeof(cfg->board_number)); + memset(cfg->chip_number, ' ', sizeof(cfg->chip_number)); + + cfg->enable = GetPrivateProfileIntW(L"ledstrip", L"enable", 1, filename); + cfg->port_no = GetPrivateProfileIntW(L"ledstrip", L"port", 21, filename); + cfg->fw_ver = GetPrivateProfileIntW(L"ledstrip", L"fw_ver", 0xA0, filename); + cfg->fw_sum = GetPrivateProfileIntW(L"ledstrip", L"fw_sum", 0xaa53, filename); + + GetPrivateProfileStringW(L"ledstrip", L"board_number", L"15093-06", tmpstr, _countof(tmpstr), filename); + size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number)); + for (int i = n; i < sizeof(cfg->board_number); i++) + { + cfg->board_number[i] = ' '; + } + + GetPrivateProfileStringW(L"ledstrip", L"chip_number", L"6710A", tmpstr, _countof(tmpstr), filename); + n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number)); + for (int i = n; i < sizeof(cfg->chip_number); i++) + { + cfg->chip_number[i] = ' '; + } +} + +void fgo_deck_config_load( + struct deck_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"deck", L"enable", 1, filename); +} + +void fgo_hook_config_load( + struct fgo_hook_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + platform_config_load(&cfg->platform, filename); + aime_config_load(&cfg->aime, filename); + dvd_config_load(&cfg->dvd, filename); + io4_config_load(&cfg->io4, filename); + touch_screen_config_load(&cfg->touch, filename); + printer_config_load(&cfg->printer, filename); + fgo_deck_config_load(&cfg->deck, filename); + ftdi_config_load(&cfg->ftdi, filename); + led1509306_config_load(&cfg->led1509306, filename); + fgo_dll_config_load(&cfg->dll, filename); +} diff --git a/fgohook/config.h b/fgohook/config.h new file mode 100644 index 0000000..a2cad3b --- /dev/null +++ b/fgohook/config.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "board/config.h" + +#include "hooklib/dvd.h" +#include "hooklib/touch.h" +#include "hooklib/printer.h" + +#include "fgohook/deck.h" +#include "fgohook/ftdi.h" +#include "fgohook/led1509306.h" +#include "fgohook/fgo-dll.h" + +#include "platform/config.h" + +struct fgo_hook_config { + struct platform_config platform; + struct aime_config aime; + struct dvd_config dvd; + struct io4_config io4; + struct touch_screen_config touch; + struct printer_config printer; + struct deck_config deck; + struct ftdi_config ftdi; + struct led1509306_config led1509306; + struct fgo_dll_config dll; +}; + +void fgo_dll_config_load( + struct fgo_dll_config *cfg, + const wchar_t *filename); + +void fgo_hook_config_load( + struct fgo_hook_config *cfg, + const wchar_t *filename); diff --git a/fgohook/deck.c b/fgohook/deck.c new file mode 100644 index 0000000..e712724 --- /dev/null +++ b/fgohook/deck.c @@ -0,0 +1,511 @@ +/* + SEGA 837-15345 RFID Deck Reader emulator + + Credits: + + OLEG + Coburn + Mitsuhide +*/ + +#include + +#include + +#include "board/sg-frame.h" + +#include "fgohook/deck.h" + +#include "hook/iobuf.h" +#include "hook/iohook.h" + +#include "hooklib/uart.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +#define MAX_CARDS 30 + +// request format: +// 0xe0 - sync +// 0x?? - command +// 0x?? - payload length +// ... - payload +// 0x?? - checksum (sum of everything except the sync byte) +// +// response format: +// 0xe0 - sync +// 0x?? - command +// 0x?? - status code +// 0x?? - payload length +// ... - payload +// 0x?? - checksum + +enum { + DECK_CMD_RESET = 0x41, + DECK_CMD_GET_BOOT_FW_VERSION = 0x84, + DECK_CMD_GET_BOARD_INFO = 0x85, + DECK_CMD_INIT_UNK1 = 0x81, + DECK_CMD_GET_APP_FW_VERSION = 0x42, + DECK_CMD_INIT_UNK2 = 0x04, + DECK_CMD_INIT_UNK3 = 0x05, + DECK_CMD_READ = 0x06 +}; + +enum { + DECK_READ_START = 0x81, + DECK_READ_DATA = 0x82, + DECK_READ_END = 0x83 +}; + +struct deck_hdr { + uint8_t sync; + uint8_t cmd; + uint8_t nbytes; +}; + +struct deck_resp_hdr { + uint8_t sync; + uint8_t cmd; + uint8_t status; + uint8_t nbytes; +}; + +struct deck_resp_get_boot_fw_version { + struct deck_resp_hdr hdr; + uint8_t boot_fw_version; +}; + +struct deck_resp_get_app_fw_version { + struct deck_resp_hdr hdr; + uint8_t app_fw_version; +}; + +struct deck_resp_get_board_info { + struct deck_resp_hdr hdr; + uint8_t board[9]; +}; + +struct deck_resp_read { + struct deck_resp_hdr hdr; + uint8_t card_data[44]; +}; + +union deck_req_any { + struct deck_hdr hdr; + uint8_t bytes[520]; +}; + +struct card_collection { + uint8_t nCards; + uint8_t cards[MAX_CARDS][44]; +}; + +static HRESULT init_mmf(void); + +static HRESULT deck_handle_irp(struct irp *irp); +static HRESULT deck_handle_irp_locked(struct irp *irp); +static HRESULT deck_req_dispatch(const union deck_req_any* req); +static HRESULT deck_req_get_boot_fw_version(void); +static HRESULT deck_req_get_app_fw_version(void); +static HRESULT deck_req_get_board_info(void); +static HRESULT deck_req_read(void); +static HRESULT deck_req_nop(uint8_t cmd); +static void deck_read_one(void); + +static HRESULT deck_frame_accept(const struct iobuf* dest); +static void deck_frame_sync(struct iobuf* src); +static HRESULT deck_frame_decode(struct iobuf *dest, struct iobuf *src); +static HRESULT deck_frame_encode(struct iobuf* dest, const void* ptr, size_t nbytes); +static HRESULT deck_frame_encode_byte(struct iobuf* dest, uint8_t byte); + +static CRITICAL_SECTION deck_lock; +static struct uart deck_uart; +static uint8_t deck_written_bytes[1024]; +static uint8_t deck_readable_bytes[1024]; +static HANDLE mutex; +static HANDLE mmf; +static struct card_collection* cards_ptr; +static uint8_t current_card_idx = 0; +static bool read_pending = false; + +HRESULT deck_hook_init(const struct deck_config *cfg, int port) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return S_FALSE; + } + + InitializeCriticalSection(&deck_lock); + + uart_init(&deck_uart, port); + deck_uart.written.bytes = deck_written_bytes; + deck_uart.written.nbytes = sizeof(deck_written_bytes); + deck_uart.readable.bytes = deck_readable_bytes; + deck_uart.readable.nbytes = sizeof(deck_readable_bytes); + + if (FAILED(init_mmf())) { + return S_FALSE; + } + + dprintf("Deck Reader: hook enabled.\n"); + + return iohook_push_handler(deck_handle_irp); +} + +static HRESULT init_mmf(void) { + mutex = CreateMutexA(NULL, FALSE, "FGODeckMutex"); + if (mutex == NULL) { + return S_FALSE; + } + + mmf = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MAX_CARDS * 44 + 1, "FGODeck"); + if (mmf == NULL) { + return S_FALSE; + } + + cards_ptr = (struct card_collection*)MapViewOfFile(mmf, FILE_MAP_ALL_ACCESS, 0, 0, MAX_CARDS * 44 + 1); + + return S_OK; +} + +static HRESULT deck_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + if (!uart_match_irp(&deck_uart, irp)) { + return iohook_invoke_next(irp); + } + + EnterCriticalSection(&deck_lock); + hr = deck_handle_irp_locked(irp); + LeaveCriticalSection(&deck_lock); + + return hr; +} + +static HRESULT deck_handle_irp_locked(struct irp *irp) +{ + uint8_t req[1024]; + struct iobuf req_iobuf; + HRESULT hr; + + if (irp->op == IRP_OP_OPEN) { + dprintf("Deck Reader: Starting backend\n"); + } + + hr = uart_handle_irp(&deck_uart, irp); + + if (SUCCEEDED(hr) && irp->op == IRP_OP_READ && read_pending == true) { + deck_read_one(); + return S_OK; + } + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { + // if (deck_uart.written.pos != 0) { + // dprintf("Deck Reader: TX Buffer:\n"); + // dump_iobuf(&deck_uart.written); + // } + + req_iobuf.bytes = req; + req_iobuf.nbytes = sizeof(req); + req_iobuf.pos = 0; + + hr = deck_frame_decode(&req_iobuf, &deck_uart.written); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("Deck Reader: Deframe error: %x, %d %d\n", (int) hr, (int) req_iobuf.nbytes, (int) req_iobuf.pos); + } + + return hr; + } + + // dprintf("Deck Reader: Deframe Buffer:\n"); + // dump_iobuf(&req_iobuf); + + hr = deck_req_dispatch((const union deck_req_any *) &req); + + if (FAILED(hr)) { + dprintf("Deck Reader: Processing error: %x\n", (int) hr); + } + + // dprintf("Deck Reader: Written bytes:\n"); + // dump_iobuf(&deck_uart.readable); + } +} + +static HRESULT deck_req_dispatch(const union deck_req_any *req) { + switch (req->hdr.cmd) { + case DECK_CMD_RESET: + case DECK_CMD_INIT_UNK1: + case DECK_CMD_INIT_UNK2: + case DECK_CMD_INIT_UNK3: + return deck_req_nop(req->hdr.cmd); + + case DECK_CMD_GET_BOOT_FW_VERSION: + return deck_req_get_boot_fw_version(); + + case DECK_CMD_GET_APP_FW_VERSION: + return deck_req_get_app_fw_version(); + + case DECK_CMD_GET_BOARD_INFO: + return deck_req_get_board_info(); + + case DECK_CMD_READ: + return deck_req_read(); + + default: + dprintf("Deck Reader: Unhandled command %#02x\n", req->hdr.cmd); + + return S_OK; + } +} + +static HRESULT deck_req_get_boot_fw_version(void) { + struct deck_resp_get_boot_fw_version resp; + + dprintf("Deck Reader: Get Boot FW Version\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_GET_BOOT_FW_VERSION; + resp.hdr.status = 0; + resp.hdr.nbytes = 1; + resp.boot_fw_version = 0x90; + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT deck_req_get_app_fw_version(void) { + struct deck_resp_get_app_fw_version resp; + + dprintf("Deck Reader: Get App FW Version\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_GET_APP_FW_VERSION; + resp.hdr.status = 0; + resp.hdr.nbytes = 1; + resp.app_fw_version = 0x91; + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT deck_req_get_board_info(void) { + struct deck_resp_get_board_info resp; + + dprintf("Deck Reader: Get Board Info\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_GET_BOARD_INFO; + resp.hdr.status = 0; + resp.hdr.nbytes = 9; + memcpy(resp.board, (void*)"837-15345", 9); + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT deck_req_read(void) { + struct deck_resp_read resp; + + dprintf("Deck Reader: Read Card\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_READ; + resp.hdr.status = DECK_READ_START; + resp.hdr.nbytes = 0; + + ReleaseMutex(mutex); + WaitForSingleObject(mutex, INFINITE); + current_card_idx = 0; + read_pending = true; + + return deck_frame_encode(&deck_uart.readable, &resp.hdr, sizeof(resp.hdr)); +} + +static void deck_read_one(void) { + struct deck_resp_read resp; + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_READ; + + if (current_card_idx < cards_ptr->nCards) { + resp.hdr.status = DECK_READ_DATA; + resp.hdr.nbytes = 44; + memcpy(resp.card_data, cards_ptr->cards[current_card_idx], 44); + dump(resp.card_data, 44); + + deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); + current_card_idx++; + } else { + resp.hdr.status = DECK_READ_END; + resp.hdr.nbytes = 0; + deck_frame_encode(&deck_uart.readable, &resp.hdr, sizeof(resp.hdr)); + read_pending = false; + ReleaseMutex(mutex); + } +} + +static HRESULT deck_req_nop(uint8_t cmd) { + struct deck_resp_hdr resp; + + dprintf("Deck Reader: No-op cmd %#02x\n", cmd); + + resp.sync = 0xE0; + resp.cmd = cmd; + resp.status = 0; + resp.nbytes = 0; + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + + +static void deck_frame_sync(struct iobuf* src) +{ + size_t i; + + for (i = 0; i < src->pos && src->bytes[i] != 0xE0; i++); + + src->pos -= i; + memmove(&src->bytes[0], &src->bytes[i], i); +} + +static HRESULT deck_frame_accept(const struct iobuf* dest) +{ + if (dest->pos < 2 || dest->pos != dest->bytes[2] + 4) { + return S_FALSE; + } + + return S_OK; +} + +static HRESULT deck_frame_decode(struct iobuf *dest, struct iobuf *src) { + uint8_t byte; + bool escape; + size_t i; + HRESULT hr; + + assert(dest != NULL); + assert(dest->bytes != NULL || dest->nbytes == 0); + assert(dest->pos <= dest->nbytes); + assert(src != NULL); + assert(src->bytes != NULL || src->nbytes == 0); + assert(src->pos <= src->nbytes); + + dest->pos = 0; + escape = false; + + for (i = 0, hr = S_FALSE; i < src->pos && hr == S_FALSE; i++) { + /* Step the FSM to unstuff another byte */ + + byte = src->bytes[i]; + + if (dest->pos >= dest->nbytes) { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else if (i == 0) { + dest->bytes[dest->pos++] = byte; + } + else if (byte == 0xE0) { + hr = E_FAIL; + } + else if (byte == 0xD0) { + if (escape) { + hr = E_FAIL; + } + + escape = true; + } + else if (escape) { + dest->bytes[dest->pos++] = byte + 1; + escape = false; + } + else { + dest->bytes[dest->pos++] = byte; + } + + /* Try to accept the packet we've built up so far */ + + if (SUCCEEDED(hr)) { + hr = deck_frame_accept(dest); + } + } + + /* Handle FSM terminal state */ + + if (hr != S_FALSE) { + /* Frame was either accepted or rejected, remove it from src */ + memmove(&src->bytes[0], &src->bytes[i], src->pos - i); + src->pos -= i; + } + + return hr; +} + +static HRESULT deck_frame_encode( + struct iobuf* dest, + const void* ptr, + size_t nbytes) +{ + const uint8_t* src; + uint8_t checksum; + uint8_t byte; + size_t i; + HRESULT hr; + + assert(dest != NULL); + assert(dest->bytes != NULL || dest->nbytes == 0); + assert(dest->pos <= dest->nbytes); + assert(ptr != NULL); + + src = ptr; + + assert(nbytes >= 2 && src[0] == 0xE0 && src[3] + 4 == nbytes); + + if (dest->pos >= dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = 0xE0; + checksum = 0x0; + + for (i = 1; i < nbytes; i++) { + byte = src[i]; + checksum += byte; + + hr = deck_frame_encode_byte(dest, byte); + + if (FAILED(hr)) { + return hr; + } + } + + return deck_frame_encode_byte(dest, checksum); +} + +static HRESULT deck_frame_encode_byte(struct iobuf* dest, uint8_t byte) +{ + if (byte == 0xD0 || byte == 0xE0) { + if (dest->pos + 2 > dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = 0xD0; + dest->bytes[dest->pos++] = byte - 1; + } + else { + if (dest->pos + 1 > dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = byte; + } + + return S_OK; +} diff --git a/fgohook/deck.h b/fgohook/deck.h new file mode 100644 index 0000000..32c1ef9 --- /dev/null +++ b/fgohook/deck.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +struct deck_config { + bool enable; +}; + +HRESULT deck_hook_init(const struct deck_config *cfg, int port); diff --git a/fgohook/dllmain.c b/fgohook/dllmain.c new file mode 100644 index 0000000..69269b5 --- /dev/null +++ b/fgohook/dllmain.c @@ -0,0 +1,144 @@ +#include + +#include + +#include "board/io4.h" +#include "board/sg-reader.h" +#include "board/vfd.h" + +#include "hook/process.h" + +#include "hooklib/dvd.h" +#include "hooklib/touch.h" +#include "hooklib/printer.h" +#include "hooklib/createprocess.h" +#include "hooklib/serial.h" +#include "hooklib/spike.h" + +#include "fgohook/config.h" +#include "fgohook/io4.h" +#include "fgohook/fgo-dll.h" +#include "fgohook/deck.h" + +#include "platform/platform.h" + +#include "util/dprintf.h" + +static HMODULE fgo_hook_mod; +static process_entry_t fgo_startup; +static struct fgo_hook_config fgo_hook_cfg; + +static DWORD CALLBACK fgo_pre_startup(void) +{ + HRESULT hr; + + dprintf("--- Begin fgo_pre_startup ---\n"); + + /* Load config */ + + fgo_hook_config_load(&fgo_hook_cfg, L".\\segatools.ini"); + + /* Hook Win32 APIs */ + + dvd_hook_init(&fgo_hook_cfg.dvd, fgo_hook_mod); + touch_screen_hook_init(&fgo_hook_cfg.touch, fgo_hook_mod); + serial_hook_init(); + + /* Hook external DLL APIs */ + + printer_hook_init(&fgo_hook_cfg.printer, 4, fgo_hook_mod); + + /* Initialize emulation hooks */ + + hr = platform_hook_init( + &fgo_hook_cfg.platform, + "SDEJ", + "ACA1", + fgo_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = sg_reader_hook_init(&fgo_hook_cfg.aime, 3, fgo_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = vfd_hook_init(1); + + if (FAILED(hr)) { + goto fail; + } + + hr = fgo_dll_init(&fgo_hook_cfg.dll, fgo_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = fgo_io4_hook_init(&fgo_hook_cfg.io4); + + if (FAILED(hr)) { + goto fail; + } + + hr = deck_hook_init(&fgo_hook_cfg.deck, 2); + + if (FAILED(hr)) { + goto fail; + } + + /* + hr = ftdi_hook_init(&fgo_hook_cfg.ftdi); + + if (FAILED(hr)) { + goto fail; + } + */ + + hr = led1509306_hook_init(&fgo_hook_cfg.led1509306); + + if (FAILED(hr)) { + goto fail; + } + + hr = createprocess_push_hook_a("am/amdaemon.exe", "inject -d -k fgohook.dll ", " -c config_hook.json", false); + + if (FAILED(hr)) { + goto fail; + } + + /* Initialize debug helpers */ + + spike_hook_init(L".\\segatools.ini"); + + dprintf("--- End fgo_pre_startup ---\n"); + + /* Jump to EXE start address */ + + return fgo_startup(); + +fail: + ExitProcess(EXIT_FAILURE); +} + +BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) +{ + HRESULT hr; + + if (cause != DLL_PROCESS_ATTACH) { + return TRUE; + } + + fgo_hook_mod = mod; + + hr = process_hijack_startup(fgo_pre_startup, &fgo_startup); + + if (!SUCCEEDED(hr)) { + dprintf("Failed to hijack process startup: %x\n", (int) hr); + } + + return SUCCEEDED(hr); +} diff --git a/fgohook/fgo-dll.c b/fgohook/fgo-dll.c new file mode 100644 index 0000000..2f4ef7d --- /dev/null +++ b/fgohook/fgo-dll.c @@ -0,0 +1,112 @@ +#include + +#include +#include + +#include "fgohook/fgo-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym fgo_dll_syms[] = { + { + .sym = "fgo_io_init", + .off = offsetof(struct fgo_dll, init), + }, { + .sym = "fgo_io_poll", + .off = offsetof(struct fgo_dll, poll), + }, { + .sym = "fgo_io_get_opbtns", + .off = offsetof(struct fgo_dll, get_opbtns), + }, { + .sym = "fgo_io_get_gamebtns", + .off = offsetof(struct fgo_dll, get_gamebtns), + }, { + .sym = "fgo_io_get_analogs", + .off = offsetof(struct fgo_dll, get_analogs), + } +}; + +struct fgo_dll fgo_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT fgo_dll_init(const struct fgo_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Ongeki IO: Failed to load IO DLL: %lx: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Ongeki IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "fgo_io_get_api_version"); + + if (get_api_version != NULL) { + fgo_dll.api_version = get_api_version(); + } else { + fgo_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose fgo_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (fgo_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Ongeki IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + fgo_dll.api_version); + + goto end; + } + + sym = fgo_dll_syms; + hr = dll_bind(&fgo_dll, src, &sym, _countof(fgo_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Ongeki IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/fgohook/fgo-dll.h b/fgohook/fgo-dll.h new file mode 100644 index 0000000..41d6e92 --- /dev/null +++ b/fgohook/fgo-dll.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "fgoio/fgoio.h" + +struct fgo_dll { + uint16_t api_version; + HRESULT (*init)(void); + HRESULT (*poll)(void); + void (*get_opbtns)(uint8_t *opbtn); + void (*get_gamebtns)(uint8_t *gamebtn); + void (*get_analogs)(int16_t *stick_x, int16_t *stick_y); +}; + +struct fgo_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct fgo_dll fgo_dll; + +HRESULT fgo_dll_init(const struct fgo_dll_config *cfg, HINSTANCE self); diff --git a/fgohook/fgohook.def b/fgohook/fgohook.def new file mode 100644 index 0000000..04bf2f0 --- /dev/null +++ b/fgohook/fgohook.def @@ -0,0 +1,82 @@ +LIBRARY fgohook + +EXPORTS + aime_io_get_api_version + aime_io_init + aime_io_led_set_color + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id + aime_io_nfc_poll + amDllVideoClose @2 + amDllVideoGetVBiosVersion @4 + amDllVideoOpen @1 + amDllVideoSetResolution @3 + fgo_io_get_api_version + fgo_io_get_gamebtns + fgo_io_get_analogs + fgo_io_get_opbtns + fgo_io_init + fgo_io_poll + fwdlusb_open + fwdlusb_close + fwdlusb_listupPrinter + fwdlusb_listupPrinterSN + fwdlusb_selectPrinter + fwdlusb_selectPrinterSN + fwdlusb_getPrinterInfo + fwdlusb_status + fwdlusb_statusAll + fwdlusb_resetPrinter + fwdlusb_updateFirmware + fwdlusb_getFirmwareInfo + fwdlusb_MakeThread + fwdlusb_ReleaseThread + fwdlusb_AttachThreadCount + fwdlusb_getErrorLog + chcusb_MakeThread + chcusb_open + chcusb_close + chcusb_ReleaseThread + chcusb_listupPrinter + chcusb_listupPrinterSN + chcusb_selectPrinter + chcusb_selectPrinterSN + chcusb_getPrinterInfo + chcusb_imageformat + chcusb_setmtf + chcusb_makeGamma + chcusb_setIcctable + chcusb_copies + chcusb_status + chcusb_statusAll + chcusb_startpage + chcusb_endpage + chcusb_write + chcusb_writeLaminate + chcusb_writeHolo + chcusb_setPrinterInfo + chcusb_getGamma + chcusb_getMtf + chcusb_cancelCopies + chcusb_setPrinterToneCurve + chcusb_getPrinterToneCurve + chcusb_blinkLED + chcusb_resetPrinter + chcusb_AttachThreadCount + chcusb_getPrintIDStatus + chcusb_setPrintStandby + chcusb_testCardFeed + chcusb_exitCard + chcusb_getCardRfidTID + chcusb_commCardRfidReader + chcusb_updateCardRfidReader + chcusb_getErrorLog + chcusb_getErrorStatus + chcusb_setCutList + chcusb_setLaminatePattern + chcusb_color_adjustment + chcusb_color_adjustmentEx + chcusb_getEEPROM + chcusb_setParameter + chcusb_getParameter + chcusb_universal_command diff --git a/fgohook/ftdi.c b/fgohook/ftdi.c new file mode 100644 index 0000000..50b7d11 --- /dev/null +++ b/fgohook/ftdi.c @@ -0,0 +1,55 @@ +/* + SEGA 837-14509-02 USB -> Serial Adapter hook + + The 837-15093-06 LED controller is connected to the ALLS with an adapter. + This tiny board has a FTDI FT232BL chip, and is referenced in schematics as + "USB-SER I/F BD". + + The game queries the presence of the FTDI board itself, followed by a + registry check to see which port number is assigned to the FTDI board. + If these fail, the "CABINET LED" check on startup will always return "NG". +*/ + +#include + +#include + +#include "fgohook/ftdi.h" + +#include "hook/iohook.h" + +#include "hooklib/setupapi.h" + +#include "util/dprintf.h" + +static struct ftdi_config ftdi_cfg; + +static HANDLE ftdi_fd; + +HRESULT ftdi_hook_init(const struct ftdi_config *cfg) +{ + HRESULT hr; + + assert(cfg != NULL); + + if (!cfg->enable) { + return S_FALSE; + } + + memcpy(&ftdi_cfg, cfg, sizeof(*cfg)); + + hr = iohook_open_nul_fd(&ftdi_fd); + + if (FAILED(hr)) { + return hr; + } + + hr = setupapi_add_phantom_dev(&ftdi_guid, L"$ftdi"); + + if (FAILED(hr)) { + return hr; + } + + dprintf("FTDI: Hook enabled.\n"); + return S_OK; +} diff --git a/fgohook/ftdi.h b/fgohook/ftdi.h new file mode 100644 index 0000000..c40fb3b --- /dev/null +++ b/fgohook/ftdi.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#include +#include + +struct ftdi_config { + bool enable; +}; + +DEFINE_GUID( + ftdi_guid, + 0x86E0D1E0, + 0x8089, + 0x11D0, + 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73); + +HRESULT ftdi_hook_init(const struct ftdi_config *cfg); diff --git a/fgohook/io4.c b/fgohook/io4.c new file mode 100644 index 0000000..b2deb5f --- /dev/null +++ b/fgohook/io4.c @@ -0,0 +1,104 @@ +#include + +#include +#include +#include + +#include "board/io4.h" + +#include "fgohook/fgo-dll.h" + +#include "util/dprintf.h" + +static HRESULT fgo_io4_poll(void *ctx, struct io4_state *state); +static uint16_t coins; + +static const struct io4_ops fgo_io4_ops = { + .poll = fgo_io4_poll, +}; + +HRESULT fgo_io4_hook_init(const struct io4_config *cfg) +{ + HRESULT hr; + + assert(fgo_dll.init != NULL); + + hr = io4_hook_init(cfg, &fgo_io4_ops, NULL); + + if (FAILED(hr)) { + return hr; + } + + return fgo_dll.init(); +} + +static HRESULT fgo_io4_poll(void *ctx, struct io4_state *state) +{ + uint8_t opbtn; + uint8_t gamebtn; + int16_t stick_x; + int16_t stick_y; + HRESULT hr; + + assert(fgo_dll.poll != NULL); + assert(fgo_dll.get_opbtns != NULL); + assert(fgo_dll.get_gamebtns != NULL); + assert(fgo_dll.get_analogs != NULL); + + memset(state, 0, sizeof(*state)); + + hr = fgo_dll.poll(); + + if (FAILED(hr)) { + return hr; + } + + opbtn = 0; + gamebtn = 0; + stick_x = 0; + stick_y = 0; + + fgo_dll.get_opbtns(&opbtn); + fgo_dll.get_gamebtns(&gamebtn); + fgo_dll.get_analogs(&stick_x, &stick_y); + + if (opbtn & FGO_IO_OPBTN_TEST) { + state->buttons[0] |= IO4_BUTTON_TEST; + } + + if (opbtn & FGO_IO_OPBTN_SERVICE) { + state->buttons[0] |= IO4_BUTTON_SERVICE; + } + + if (opbtn & FGO_IO_OPBTN_COIN) { + coins++; + } + state->chutes[0] = coins << 8; + + if (gamebtn & FGO_IO_GAMEBTN_SPEED_UP) { + state->buttons[0] |= 1 << 4; + } + + if (gamebtn & FGO_IO_GAMEBTN_TARGET) { + state->buttons[0] |= 1 << 5; + } + + if (gamebtn & FGO_IO_GAMEBTN_ATTACK) { + state->buttons[0] |= 1 << 1; + } + + if (gamebtn & FGO_IO_GAMEBTN_NOBLE_PHANTASHM) { + state->buttons[0] |= 1 << 0; + } + + if (gamebtn & FGO_IO_GAMEBTN_CAMERA) { + state->buttons[0] |= 1 << 14; + } + + /* Stick x and y movement */ + + state->adcs[0] = 0x8000 - stick_x; + state->adcs[4] = 0x8000 + stick_y; + + return S_OK; +} diff --git a/fgohook/io4.h b/fgohook/io4.h new file mode 100644 index 0000000..9739e89 --- /dev/null +++ b/fgohook/io4.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "board/io4.h" + +HRESULT fgo_io4_hook_init(const struct io4_config *cfg); diff --git a/fgohook/led1509306.c b/fgohook/led1509306.c new file mode 100644 index 0000000..17f3451 --- /dev/null +++ b/fgohook/led1509306.c @@ -0,0 +1,379 @@ +#include + +#include +#include +#include +#include +#include + +#include "board/led1509306-cmd.h" +#include "board/led1509306-frame.h" + +#include "fgohook/led1509306.h" + +#include "hook/iobuf.h" +#include "hook/iohook.h" + +#include "hooklib/uart.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +static HRESULT led1509306_handle_irp(struct irp *irp); +static HRESULT led1509306_handle_irp_locked(int board, struct irp *irp); + +static HRESULT led1509306_req_dispatch(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_reset(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_get_board_info(int board); +static HRESULT led1509306_req_get_fw_sum(int board); +static HRESULT led1509306_req_get_protocol_ver(int board); +static HRESULT led1509306_req_get_board_status(int board); +static HRESULT led1509306_req_set_led(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_set_disable_response(int board, const struct led1509306_req_any *req); +static HRESULT led1509306_req_set_timeout(int board, const struct led1509306_req_any *req); + +static char led1509306_board_num[8]; +static char led1509306_chip_num[5]; +static uint8_t led1509306_fw_ver; +static uint16_t led1509306_fw_sum; +static uint8_t led1509306_board_adr = 2; +static uint8_t led1509306_host_adr = 1; + +#define led1509306_nboards 2 + +typedef struct { + CRITICAL_SECTION lock; + struct uart boarduart; + uint8_t written_bytes[520]; + uint8_t readable_bytes[520]; + bool enable_response; +} _led1509306_per_board_vars; + +_led1509306_per_board_vars led1509306_per_board_vars[led1509306_nboards]; + +HRESULT led1509306_hook_init(const struct led1509306_config *cfg) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return S_FALSE; + } + + memcpy(led1509306_board_num, cfg->board_number, sizeof(led1509306_board_num)); + memcpy(led1509306_chip_num, cfg->chip_number, sizeof(led1509306_chip_num)); + led1509306_fw_ver = cfg->fw_ver; + led1509306_fw_sum = cfg->fw_sum; + + for (int i = 0; i < led1509306_nboards; i++) + { + _led1509306_per_board_vars *v = &led1509306_per_board_vars[i]; + + InitializeCriticalSection(&v->lock); + + uart_init(&v->boarduart, cfg->port_no + i); + v->boarduart.written.bytes = v->written_bytes; + v->boarduart.written.nbytes = sizeof(v->written_bytes); + v->boarduart.readable.bytes = v->readable_bytes; + v->boarduart.readable.nbytes = sizeof(v->readable_bytes); + + v->enable_response = true; + } + + dprintf("LED Strip: hook enabled.\n"); + + return iohook_push_handler(led1509306_handle_irp); +} + +static HRESULT led1509306_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + for (int i = 0; i < led1509306_nboards; i++) + { + _led1509306_per_board_vars *v = &led1509306_per_board_vars[i]; + struct uart *boarduart = &v->boarduart; + + if (uart_match_irp(boarduart, irp)) + { + CRITICAL_SECTION lock = v->lock; + + EnterCriticalSection(&lock); + hr = led1509306_handle_irp_locked(i, irp); + LeaveCriticalSection(&lock); + + return hr; + } + } + + return iohook_invoke_next(irp); +} + +static HRESULT led1509306_handle_irp_locked(int board, struct irp *irp) +{ + struct led1509306_req_any req; + struct iobuf req_iobuf; + HRESULT hr; + + struct uart *boarduart = &led1509306_per_board_vars[board].boarduart; + + hr = uart_handle_irp(boarduart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { +#if 0 + dprintf("TX Buffer:\n"); + dump_iobuf(&boarduart->written); +#endif + + req_iobuf.bytes = (byte*)&req; + req_iobuf.nbytes = sizeof(req.hdr) + sizeof(req.cmd) + sizeof(req.payload); + req_iobuf.pos = 0; + + hr = led1509306_frame_decode(&req_iobuf, &boarduart->written); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("LED Strip: Deframe error: %x\n", (int) hr); + } + + return hr; + } + +#if 0 + dprintf("Deframe Buffer:\n"); + dump_iobuf(&req_iobuf); +#endif + + hr = led1509306_req_dispatch(board, &req); + + if (FAILED(hr)) { + dprintf("LED Strip: Processing error: %x\n", (int) hr); + } + } +} + +static HRESULT led1509306_req_dispatch(int board, const struct led1509306_req_any *req) +{ + switch (req->cmd) { + case LED_15093_06_CMD_RESET: + return led1509306_req_reset(board, req); + + case LED_15093_06_CMD_BOARD_INFO: + return led1509306_req_get_board_info(board); + + case LED_15093_06_CMD_FW_SUM: + return led1509306_req_get_fw_sum(board); + + case LED_15093_06_CMD_PROTOCOL_VER: + return led1509306_req_get_protocol_ver(board); + + case LED_15093_06_CMD_BOARD_STATUS: + return led1509306_req_get_board_status(board); + + case LED_15093_06_CMD_SET_LED: + return led1509306_req_set_led(board, req); + + case LED_15093_06_CMD_SET_DISABLE_RESPONSE: + return led1509306_req_set_disable_response(board, req); + + case LED_15093_06_CMD_SET_TIMEOUT: + return led1509306_req_set_timeout(board, req); + + default: + dprintf("LED Strip: Unhandled command %02x\n", req->cmd); + + return S_OK; + } +} + +static HRESULT led1509306_req_reset(int board, const struct led1509306_req_any *req) +{ + dprintf("LED Strip: Reset (board %u, type %02x)\n", board, req->payload[0]); + + if (req->payload[0] != 0xd9) + dprintf("LED Strip: Warning -- Unknown reset type %02x\n", req->payload[0]); + + led1509306_per_board_vars[board].enable_response = true; + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_RESET; + resp.report = 1; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_board_info(int board) +{ + dprintf("LED Strip: Get board info (board %u)\n", board); + + struct led1509306_resp_board_info resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = sizeof(resp.data) + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_BOARD_INFO; + resp.report = 1; + + memcpy(resp.data.board_num, led1509306_board_num, sizeof(resp.data.board_num)); + resp.data._0a = 0x0a; + memcpy(resp.data.chip_num, led1509306_chip_num, sizeof(resp.data.chip_num)); + resp.data._ff = 0xff; + resp.data.fw_ver = led1509306_fw_ver; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_fw_sum(int board) +{ + dprintf("LED Strip: Get firmware checksum (board %u)\n", board); + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 2 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_FW_SUM; + resp.report = 1; + + resp.data[0] = (led1509306_fw_sum >> 8) & 0xff; + resp.data[1] = led1509306_fw_sum & 0xff; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_protocol_ver(int board) +{ + dprintf("LED Strip: Get protocol version (board %u)\n", board); + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 3 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_PROTOCOL_VER; + resp.report = 1; + + resp.data[0] = 1; + resp.data[1] = 1; + resp.data[2] = 4; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_get_board_status(int board) +{ + dprintf("LED Strip: Get board status (board %u)\n", board); + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 4 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_BOARD_STATUS; + resp.report = 1; + + resp.data[0] = 0; + resp.data[1] = 0; + resp.data[2] = 0; + resp.data[3] = 0; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_set_led(int board, const struct led1509306_req_any *req) +{ + // dprintf("LED Strip: Set LED (board %u)\n", board); + + if (!led1509306_per_board_vars[board].enable_response) + return S_OK; + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_SET_LED; + resp.report = 1; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_set_disable_response(int board, const struct led1509306_req_any *req) +{ + dprintf("LED Strip: Disable LED responses (board %u)\n", board); + + led1509306_per_board_vars[board].enable_response = !req->payload[0]; + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 1 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_SET_DISABLE_RESPONSE; + resp.report = 1; + + resp.data[0] = req->payload[0]; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led1509306_req_set_timeout(int board, const struct led1509306_req_any *req) +{ + dprintf("LED Strip: Set timeout (board %u)\n", board); + + // not actually implemented, but respond correctly anyway + + struct led1509306_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15093_06_FRAME_SYNC; + resp.hdr.dest_adr = led1509306_host_adr; + resp.hdr.src_adr = led1509306_board_adr; + resp.hdr.nbytes = 2 + 3; + + resp.status = 1; + resp.cmd = LED_15093_06_CMD_SET_TIMEOUT; + resp.report = 1; + + resp.data[0] = req->payload[0]; + resp.data[1] = req->payload[1]; + + return led1509306_frame_encode(&led1509306_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} diff --git a/fgohook/led1509306.h b/fgohook/led1509306.h new file mode 100644 index 0000000..f4b3260 --- /dev/null +++ b/fgohook/led1509306.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include + +struct led1509306_config { + bool enable; + unsigned int port_no; + char board_number[8]; + char chip_number[5]; + uint8_t fw_ver; + uint16_t fw_sum; +}; + +HRESULT led1509306_hook_init(const struct led1509306_config *cfg); diff --git a/fgohook/meson.build b/fgohook/meson.build new file mode 100644 index 0000000..11737b2 --- /dev/null +++ b/fgohook/meson.build @@ -0,0 +1,36 @@ +shared_library( + 'fgohook', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + vs_module_defs : 'fgohook.def', + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + capnhook.get_variable('hooklib_dep'), + xinput_lib, + ], + link_with : [ + aimeio_lib, + board_lib, + hooklib_lib, + fgoio_lib, + platform_lib, + util_lib, + ], + sources : [ + 'config.c', + 'config.h', + 'dllmain.c', + 'io4.c', + 'io4.h', + 'fgo-dll.c', + 'fgo-dll.h', + 'deck.c', + 'deck.h', + 'ftdi.c', + 'ftdi.h', + 'led1509306.c', + 'led1509306.h', + ], +) diff --git a/fgoio/config.c b/fgoio/config.c new file mode 100644 index 0000000..7f1f5e4 --- /dev/null +++ b/fgoio/config.c @@ -0,0 +1,20 @@ +#include + +#include +#include +#include + +#include "fgoio/config.h" + + +void fgo_io_config_load( + struct fgo_io_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename); + cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename); + cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", '3', filename); +} diff --git a/fgoio/config.h b/fgoio/config.h new file mode 100644 index 0000000..835e5e8 --- /dev/null +++ b/fgoio/config.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#include + +struct fgo_io_config { + uint8_t vk_test; + uint8_t vk_service; + uint8_t vk_coin; +}; + +void fgo_io_config_load( + struct fgo_io_config *cfg, + const wchar_t *filename); diff --git a/fgoio/fgoio.c b/fgoio/fgoio.c new file mode 100644 index 0000000..c6bd88f --- /dev/null +++ b/fgoio/fgoio.c @@ -0,0 +1,141 @@ +#include +#include +#include + +#include +#include + +#include "fgoio/fgoio.h" +#include "fgoio/config.h" +#include "util/dprintf.h" + +static uint8_t fgo_opbtn; +static uint8_t fgo_gamebtn; +static int16_t fgo_stick_x; +static int16_t fgo_stick_y; +static struct fgo_io_config fgo_io_cfg; +static bool fgo_io_coin; + +uint16_t fgo_io_get_api_version(void) +{ + return 0x0100; +} + +HRESULT fgo_io_init(void) +{ + fgo_io_config_load(&fgo_io_cfg, L".\\segatools.ini"); + + return S_OK; +} + +HRESULT fgo_io_poll(void) +{ + XINPUT_STATE xi; + WORD xb; + + fgo_opbtn = 0; + fgo_gamebtn = 0; + fgo_stick_x = 0; + fgo_stick_y = 0; + + if (GetAsyncKeyState(fgo_io_cfg.vk_test) & 0x8000) { + fgo_opbtn |= FGO_IO_OPBTN_TEST; + } + + if (GetAsyncKeyState(fgo_io_cfg.vk_service) & 0x8000) { + fgo_opbtn |= FGO_IO_OPBTN_SERVICE; + } + + if (GetAsyncKeyState(fgo_io_cfg.vk_coin) & 0x8000) { + if (!fgo_io_coin) { + fgo_io_coin = true; + fgo_opbtn |= FGO_IO_OPBTN_COIN; + } + } else { + fgo_io_coin = false; + } + + memset(&xi, 0, sizeof(xi)); + XInputGetState(0, &xi); + xb = xi.Gamepad.wButtons; + + if (xi.Gamepad.bLeftTrigger > 64) { + fgo_gamebtn |= FGO_IO_GAMEBTN_SPEED_UP; + } + + if (xb & XINPUT_GAMEPAD_LEFT_SHOULDER) { + fgo_gamebtn |= FGO_IO_GAMEBTN_TARGET; + } + + if (xb & XINPUT_GAMEPAD_A) { + fgo_gamebtn |= FGO_IO_GAMEBTN_ATTACK; + } + + if (xb & XINPUT_GAMEPAD_Y) { + fgo_gamebtn |= FGO_IO_GAMEBTN_NOBLE_PHANTASHM; + } + + if (xb & XINPUT_GAMEPAD_LEFT_THUMB) { + fgo_gamebtn |= FGO_IO_GAMEBTN_CAMERA; + } + + float LX = xi.Gamepad.sThumbLX; + float LY = xi.Gamepad.sThumbLY; + + // determine how far the controller is pushed + float magnitude = sqrt(LX*LX + LY*LY); + + // determine the direction the controller is pushed + float normalizedLX = LX / magnitude; + float normalizedLY = LY / magnitude; + + float normalizedMagnitude = 0; + + // check if the controller is outside a circular dead zone + if (magnitude > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) + { + // clip the magnitude at its expected maximum value + if (magnitude > 32767) magnitude = 32767; + + // adjust magnitude relative to the end of the dead zone + magnitude -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; + + // optionally normalize the magnitude with respect to its expected range + // giving a magnitude value of 0.0 to 1.0 + normalizedMagnitude = magnitude / (32767 - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + } else // if the controller is in the deadzone zero out the magnitude + { + magnitude = 0.0; + normalizedMagnitude = 0.0; + } + + fgo_stick_x = normalizedLX * normalizedMagnitude * 32767; + fgo_stick_y = normalizedLY * normalizedMagnitude * 32767; + + return S_OK; +} + +void fgo_io_get_opbtns(uint8_t *opbtn) +{ + if (opbtn != NULL) { + *opbtn = fgo_opbtn; + } +} + +void fgo_io_get_gamebtns(uint8_t *btn) +{ + if (btn != NULL) { + *btn = fgo_gamebtn; + } +} + +void fgo_io_get_analogs(int16_t *stick_x, int16_t *stick_y) +{ + if (stick_x != NULL) { + *stick_x = fgo_stick_x; + } + + if (stick_y != NULL) { + *stick_y = fgo_stick_y; + } +} diff --git a/fgoio/fgoio.h b/fgoio/fgoio.h new file mode 100644 index 0000000..f302dfa --- /dev/null +++ b/fgoio/fgoio.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include + +enum { + FGO_IO_OPBTN_TEST = 0x01, + FGO_IO_OPBTN_SERVICE = 0x02, + FGO_IO_OPBTN_COIN = 0x04, +}; + +enum { + FGO_IO_GAMEBTN_SPEED_UP = 0x01, + FGO_IO_GAMEBTN_TARGET = 0x02, + FGO_IO_GAMEBTN_ATTACK = 0x04, + FGO_IO_GAMEBTN_NOBLE_PHANTASHM = 0x08, + FGO_IO_GAMEBTN_CAMERA = 0x10, +}; + +/* Get the version of the Fate Grand Order IO API that this DLL supports. This + function should return a positive 16-bit integer, where the high byte is + the major version and the low byte is the minor version (as defined by the + Semantic Versioning standard). + + The latest API version as of this writing is 0x0100. */ + +uint16_t fgo_io_get_api_version(void); + +/* Initialize the IO DLL. This is the second function that will be called on + your DLL, after fgo_io_get_api_version. + + All subsequent calls to this API may originate from arbitrary threads. + + Minimum API version: 0x0100 */ + +HRESULT fgo_io_init(void); + +/* Send any queued outputs (of which there are currently none, though this may + change in subsequent API versions) and retrieve any new inputs. + + Minimum API version: 0x0100 */ + +HRESULT fgo_io_poll(void); + +/* Get the state of the cabinet's operator buttons as of the last poll. See + FGO_IO_OPBTN enum above: this contains bit mask definitions for button + states returned in *opbtn. All buttons are active-high. + + Minimum API version: 0x0100 */ + +void fgo_io_get_opbtns(uint8_t *opbtn); + +/* Get the state of the cabinet's gameplay buttons as of the last poll. See + FGO_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into + a left hand side set of inputs and a right hand side set of inputs: the bit + mappings are the same in both cases. + + All buttons are active-high, even though some buttons' electrical signals + on a real cabinet are active-low. + + Minimum API version: 0x0100 */ + +void fgo_io_get_gamebtns(uint8_t *btn); + +/* Get the position of the cabinet stick as of the last poll. The center + position should be equal to or close to 32767. + + Minimum API version: 0x0100 */ + +void fgo_io_get_analogs(int16_t *stick_x, int16_t *stick_y); diff --git a/fgoio/meson.build b/fgoio/meson.build new file mode 100644 index 0000000..d8a4881 --- /dev/null +++ b/fgoio/meson.build @@ -0,0 +1,16 @@ +fgoio_lib = static_library( + 'fgoio', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + c_pch : '../precompiled.h', + dependencies : [ + xinput_lib, + ], + sources : [ + 'fgoio.c', + 'fgoio.h', + 'config.c', + 'config.h', + ], +) diff --git a/hooklib/config.c b/hooklib/config.c index 30f6a4b..584d146 100644 --- a/hooklib/config.c +++ b/hooklib/config.c @@ -21,4 +21,63 @@ void touch_screen_config_load(struct touch_screen_config *cfg, const wchar_t *fi assert(filename != NULL); cfg->enable = GetPrivateProfileIntW(L"touch", L"enable", 1, filename); + cfg->remap = GetPrivateProfileIntW(L"touch", L"remap", 1, filename); + cfg->cursor = GetPrivateProfileIntW(L"touch", L"cursor", 1, filename); +} + +void printer_config_load(struct printer_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + wchar_t tmpstr[16]; + + cfg->enable = GetPrivateProfileIntW(L"printer", L"enable", 0, filename); + cfg->rotate_180 = GetPrivateProfileIntW(L"printer", L"rotate180", 0, filename); + + GetPrivateProfileStringW( + L"printer", + L"serial_no", + L"5A-A123", + tmpstr, + _countof(tmpstr), + filename); + + size_t n = wcstombs(cfg->serial_no, tmpstr, sizeof(cfg->serial_no)); + for (int i = n; i < sizeof(cfg->serial_no); i++) + { + cfg->serial_no[i] = '\0'; + } + + GetPrivateProfileStringW( + L"printer", + L"mainFwPath", + L"DEVICE\\printer_main_fw.bin", + cfg->main_fw_path, + _countof(cfg->main_fw_path), + filename); + + GetPrivateProfileStringW( + L"printer", + L"dspFwPath", + L"DEVICE\\printer_dsp_fw.bin", + cfg->dsp_fw_path, + _countof(cfg->dsp_fw_path), + filename); + + GetPrivateProfileStringW( + L"printer", + L"paramFwPath", + L"DEVICE\\printer_param_fw.bin", + cfg->param_fw_path, + _countof(cfg->param_fw_path), + filename); + + GetPrivateProfileStringW( + L"printer", + L"printerOutPath", + L"DEVICE\\print", + cfg->printer_out_path, + _countof(cfg->printer_out_path), + filename); } diff --git a/hooklib/config.h b/hooklib/config.h index 9763274..9daed18 100644 --- a/hooklib/config.h +++ b/hooklib/config.h @@ -4,6 +4,8 @@ #include "hooklib/dvd.h" #include "hooklib/touch.h" +#include "hooklib/printer.h" void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename); void touch_screen_config_load(struct touch_screen_config *cfg, const wchar_t *filename); +void printer_config_load(struct printer_config *cfg, const wchar_t *filename); diff --git a/hooklib/meson.build b/hooklib/meson.build index 4f2a703..40fe49d 100644 --- a/hooklib/meson.build +++ b/hooklib/meson.build @@ -31,5 +31,7 @@ hooklib_lib = static_library( 'spike.h', 'touch.c', 'touch.h', + 'printer.c', + 'printer.h', ], ) diff --git a/hooklib/printer.c b/hooklib/printer.c new file mode 100644 index 0000000..bdc913c --- /dev/null +++ b/hooklib/printer.c @@ -0,0 +1,2380 @@ +/* + Sinfonia CHC-C3XX Printer emulator + + Credits: + + c310emu (rakisaionji) + segatools-kancolle (OLEG) + c310emu (doremi) +*/ + +#include "hooklib/printer.h" + +#include +#include +#include +#include +#include +#include + +#include "hook/table.h" +#include "hooklib/dll.h" +#include "hooklib/uart.h" +#include "util/dprintf.h" +#include "util/dump.h" + +// #define IMAGE_SIZE 0x24FC00 +#define IMAGE_SIZE 0x1F0800 +#define HOLO_SIZE 0xC5400 + +static const int8_t idNumber = 0x01; +static uint64_t serialNo = 0x333231412D4135; + +static uint16_t WIDTH = 0; +static uint16_t HEIGHT = 0; + +static bool rotate180 = false; +static uint8_t cardRFID[0xC] = {0}; +static uint8_t cardDataBuffer[0x20] = {0}; +static bool awaitingCardExit = false; +static wchar_t printer_out_path[MAX_PATH]; + +/* Firmware data */ + +static uint8_t mainFirmware[0x40] = {0}; +static uint8_t dspFirmware[0x40] = {0}; +static uint8_t paramFirmware[0x40] = {0}; + +/* printerInfo variables */ + +static int32_t PAPERINFO[10]; +static int32_t CURVE[3][3]; +static uint8_t POLISH[2]; +static int32_t MTF[9]; + +/* C3xxFWDLusb API hooks */ + +int fwdlusb_open(uint16_t *rResult); +void fwdlusb_close(); +int fwdlusb_listupPrinter(uint8_t *rIdArray); +int fwdlusb_listupPrinterSN(uint64_t *rSerialArray); +int fwdlusb_selectPrinter(uint8_t printerId, uint16_t *rResult); +int fwdlusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult); +int fwdlusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen); +int fwdlusb_status(uint16_t *rResult); +int fwdlusb_statusAll(uint8_t *idArray, uint16_t *rResultArray); +int fwdlusb_resetPrinter(uint16_t *rResult); +int fwdlusb_updateFirmware(uint8_t update, LPCSTR filename, uint16_t *rResult); +int fwdlusb_getFirmwareInfo(uint8_t update, LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult); +int fwdlusb_MakeThread(uint16_t maxCount); +int fwdlusb_ReleaseThread(uint16_t *rResult); +int fwdlusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount); +int fwdlusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult); + +/* C3xxusb API hooks */ + +int chcusb_MakeThread(uint16_t maxCount); +int chcusb_open(uint16_t *rResult); +void chcusb_close(); +int chcusb_ReleaseThread(uint16_t *rResult); +int chcusb_listupPrinter(uint8_t *rIdArray); +int chcusb_listupPrinterSN(uint64_t *rSerialArray); +int chcusb_selectPrinter(uint8_t printerId, uint16_t *rResult); +int chcusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult); +int chcusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen); +int chcusb_imageformat( + uint16_t format, + uint16_t ncomp, + uint16_t depth, + uint16_t width, + uint16_t height, + uint16_t *rResult); +int chcusb_setmtf(int32_t *mtf); +int chcusb_makeGamma(uint16_t k, uint8_t *intoneR, uint8_t *intoneG, uint8_t *intoneB); +int chcusb_setIcctable( + LPCSTR icc1, + LPCSTR icc2, + uint16_t intents, + uint8_t *intoneR, + uint8_t *intoneG, + uint8_t *intoneB, + uint8_t *outtoneR, + uint8_t *outtoneG, + uint8_t *outtoneB, + uint16_t *rResult); +int chcusb_copies(uint16_t copies, uint16_t *rResult); +int chcusb_status(uint16_t *rResult); +int chcusb_statusAll(uint8_t *idArray, uint16_t *rResultArray); +int chcusb_startpage(uint16_t postCardState, uint16_t *pageId, uint16_t *rResult); +int chcusb_endpage(uint16_t *rResult); +int chcusb_write(uint8_t *data, uint32_t *writeSize, uint16_t *rResult); +int chcusb_writeLaminate(uint8_t *data, uint32_t *writeSize, uint16_t *rResult); +int chcusb_writeHolo(uint8_t *data, uint32_t *writeSize, uint16_t *rResult); +int chcusb_setPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult); +int chcusb_getGamma(LPCSTR filename, uint8_t *r, uint8_t *g, uint8_t *b, uint16_t *rResult); +int chcusb_getMtf(LPCSTR filename, int32_t *mtf, uint16_t *rResult); +int chcusb_cancelCopies(uint16_t pageId, uint16_t *rResult); +int chcusb_setPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult); +int chcusb_getPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult); +int chcusb_blinkLED(uint16_t *rResult); +int chcusb_resetPrinter(uint16_t *rResult); +int chcusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount); +int chcusb_getPrintIDStatus(uint16_t pageId, uint8_t *rBuffer, uint16_t *rResult); +int chcusb_setPrintStandby(uint16_t position, uint16_t *rResult); +int chcusb_testCardFeed(uint16_t mode, uint16_t times, uint16_t *rResult); +int chcusb_exitCard(uint16_t *rResult); +int chcusb_getCardRfidTID(uint8_t *rCardTID, uint16_t *rResult); +int chcusb_commCardRfidReader(uint8_t *sendData, uint8_t *rRecvData, uint32_t sendSize, uint32_t *rRecvSize, uint16_t *rResult); +int chcusb_updateCardRfidReader(uint8_t *data, uint32_t size, uint16_t *rResult); +int chcusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult); +int chcusb_getErrorStatus(uint16_t *rBuffer); +int chcusb_setCutList(uint8_t *rData, uint16_t *rResult); +int chcusb_setLaminatePattern(uint16_t index, uint8_t *rData, uint16_t *rResult); +int chcusb_color_adjustment(LPCSTR filename, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult); +int chcusb_color_adjustmentEx(int32_t a1, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult); +int chcusb_getEEPROM(uint8_t index, uint8_t *rData, uint16_t *rResult); +int chcusb_setParameter(uint8_t a1, uint32_t a2, uint16_t *rResult); +int chcusb_getParameter(uint8_t a1, uint8_t *a2, uint16_t *rResult); +int chcusb_universal_command(int32_t a1, uint8_t a2, int32_t a3, uint8_t *a4, uint16_t *rResult); + +/* Bitmap writing utils */ + +DWORD WriteDataToBitmapFile( + LPCWSTR lpFilePath, DWORD dwBitCount, + DWORD dwWidth, DWORD dwHeight, + PBYTE pbInput, DWORD cbInput, + PBYTE pbMetadata, DWORD cbMetadata, + bool pFlip); +DWORD WriteArrayToFile(LPCSTR lpOutputFilePath, LPVOID lpDataTemp, DWORD nDataSize, BOOL isAppend); + +#define MAX_CARDS 36 + +// big thanks to Coburn and Mitsuhide for helping me figure out this thing + +// request format: +// 0xe0 - sync +// 0x?? - command +// 0x?? - payload length +// ... - payload +// 0x?? - checksum (sum of everything except the sync byte) +// +// response format: +// 0xe0 - sync +// 0x?? - command +// 0x?? - status code +// 0x?? - payload length +// ... - payload +// 0x?? - checksum + +enum { + DECK_CMD_RESET = 0x41, + DECK_CMD_GET_BOOT_FW_VERSION = 0x84, + DECK_CMD_GET_BOARD_INFO = 0x85, + DECK_CMD_INIT_UNK1 = 0x81, + DECK_CMD_GET_APP_FW_VERSION = 0x42, + DECK_CMD_READ_DATA = 0x02, + DECK_CMD_WRITE = 0x03, + DECK_CMD_INIT_UNK2 = 0x04, + DECK_CMD_INIT_UNK3 = 0x05, + DECK_CMD_READ = 0x06 +}; + +enum { + DECK_READ_START = 0x81, + DECK_READ_DATA = 0x82, + DECK_READ_END = 0x83 +}; + +struct deck_hdr { + uint8_t sync; + uint8_t cmd; + uint8_t nbytes; +}; + +struct deck_resp_hdr { + uint8_t sync; + uint8_t cmd; + uint8_t status; + uint8_t nbytes; +}; + +struct deck_resp_get_boot_fw_version { + struct deck_resp_hdr hdr; + uint8_t boot_fw_version; +}; + +struct deck_resp_get_app_fw_version { + struct deck_resp_hdr hdr; + uint8_t app_fw_version; +}; + +struct deck_resp_get_board_info { + struct deck_resp_hdr hdr; + uint8_t board[9]; +}; + +struct deck_resp_read_data { + struct deck_resp_hdr hdr; + uint8_t card_data[32]; +}; + +struct deck_resp_read { + struct deck_resp_hdr hdr; + uint8_t card_data[44]; +}; + +union deck_req_any { + struct deck_hdr hdr; + uint8_t bytes[520]; +}; + +static HRESULT deck_handle_irp(struct irp *irp); +static HRESULT deck_handle_irp_locked(struct irp *irp); +static HRESULT deck_req_dispatch(const union deck_req_any *req); +static HRESULT deck_req_get_boot_fw_version(void); +static HRESULT deck_req_get_app_fw_version(void); +static HRESULT deck_req_get_board_info(void); +static HRESULT deck_req_read(void); +static HRESULT deck_req_read_data(void); +static HRESULT deck_req_write(const uint8_t *req_bytes); +static HRESULT deck_req_nop(uint8_t cmd); +static void deck_read_one(void); + +static HRESULT deck_frame_accept(const struct iobuf *dest); +static void deck_frame_sync(struct iobuf *src); +static HRESULT deck_frame_decode(struct iobuf *dest, struct iobuf *src); +static HRESULT deck_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes); +static HRESULT deck_frame_encode_byte(struct iobuf *dest, uint8_t byte); + +static CRITICAL_SECTION deck_lock; +static struct uart deck_uart; +static uint8_t deck_written_bytes[1024]; +static uint8_t deck_readable_bytes[1024]; +static uint8_t current_card_idx = 0; +static bool read_pending = false; + +/* C3xxFWDLusb hook tbl. */ + +static const struct hook_symbol C3XXFWDLusb_hooks[] = { + {.name = "fwdlusb_open", + .patch = fwdlusb_open, + .link = NULL}, + {.name = "fwdlusb_close", + .patch = fwdlusb_close, + .link = NULL}, + {.name = "fwdlusb_listupPrinter", + .patch = fwdlusb_listupPrinter, + .link = NULL}, + {.name = "fwdlusb_listupPrinterSN", + .patch = fwdlusb_listupPrinterSN, + .link = NULL}, + {.name = "fwdlusb_selectPrinter", + .patch = fwdlusb_selectPrinter, + .link = NULL}, + {.name = "fwdlusb_selectPrinterSN", + .patch = fwdlusb_selectPrinterSN, + .link = NULL}, + {.name = "fwdlusb_getPrinterInfo", + .patch = fwdlusb_getPrinterInfo, + .link = NULL}, + {.name = "fwdlusb_status", + .patch = fwdlusb_status, + .link = NULL}, + {.name = "fwdlusb_statusAll", + .patch = fwdlusb_statusAll, + .link = NULL}, + {.name = "fwdlusb_resetPrinter", + .patch = fwdlusb_resetPrinter, + .link = NULL}, + {.name = "fwdlusb_updateFirmware", + .patch = fwdlusb_updateFirmware, + .link = NULL}, + {.name = "fwdlusb_getFirmwareInfo", + .patch = fwdlusb_getFirmwareInfo, + .link = NULL}, + {.name = "fwdlusb_MakeThread", + .patch = fwdlusb_MakeThread, + .link = NULL}, + {.name = "fwdlusb_ReleaseThread", + .patch = fwdlusb_ReleaseThread, + .link = NULL}, + {.name = "fwdlusb_AttachThreadCount", + .patch = fwdlusb_AttachThreadCount, + .link = NULL}, + {.name = "fwdlusb_getErrorLog", + .patch = fwdlusb_getErrorLog, + .link = NULL}, +}; + +/* C310A-Busb/C320Ausb/C330Ausb hook tbl. */ + +static const struct hook_symbol C3XXusb_hooks[] = { + {.name = "chcusb_MakeThread", + .patch = chcusb_MakeThread, + .link = NULL}, + {.name = "chcusb_open", + .patch = chcusb_open, + .link = NULL}, + {.name = "chcusb_close", + .patch = chcusb_close, + .link = NULL}, + {.name = "chcusb_ReleaseThread", + .patch = chcusb_ReleaseThread, + .link = NULL}, + {.name = "chcusb_listupPrinter", + .patch = chcusb_listupPrinter, + .link = NULL}, + {.name = "chcusb_listupPrinterSN", + .patch = chcusb_listupPrinterSN, + .link = NULL}, + {.name = "chcusb_selectPrinter", + .patch = chcusb_selectPrinter, + .link = NULL}, + {.name = "chcusb_selectPrinterSN", + .patch = chcusb_selectPrinterSN, + .link = NULL}, + {.name = "chcusb_getPrinterInfo", + .patch = chcusb_getPrinterInfo, + .link = NULL}, + {.name = "chcusb_imageformat", + .patch = chcusb_imageformat, + .link = NULL}, + {.name = "chcusb_setmtf", + .patch = chcusb_setmtf, + .link = NULL}, + {.name = "chcusb_makeGamma", + .patch = chcusb_makeGamma, + .link = NULL}, + {.name = "chcusb_setIcctable", + .patch = chcusb_setIcctable, + .link = NULL}, + {.name = "chcusb_copies", + .patch = chcusb_copies, + .link = NULL}, + {.name = "chcusb_status", + .patch = chcusb_status, + .link = NULL}, + {.name = "chcusb_statusAll", + .patch = chcusb_statusAll, + .link = NULL}, + {.name = "chcusb_startpage", + .patch = chcusb_startpage, + .link = NULL}, + {.name = "chcusb_endpage", + .patch = chcusb_endpage, + .link = NULL}, + {.name = "chcusb_write", + .patch = chcusb_write, + .link = NULL}, + {.name = "chcusb_writeLaminate", + .patch = chcusb_writeLaminate, + .link = NULL}, + {.name = "chcusb_writeHolo", + .patch = chcusb_writeHolo, + .link = NULL}, + {.name = "chcusb_setPrinterInfo", + .patch = chcusb_setPrinterInfo, + .link = NULL}, + {.name = "chcusb_getGamma", + .patch = chcusb_getGamma, + .link = NULL}, + {.name = "chcusb_getMtf", + .patch = chcusb_getMtf, + .link = NULL}, + {.name = "chcusb_cancelCopies", + .patch = chcusb_cancelCopies, + .link = NULL}, + {.name = "chcusb_setPrinterToneCurve", + .patch = chcusb_setPrinterToneCurve, + .link = NULL}, + {.name = "chcusb_getPrinterToneCurve", + .patch = chcusb_getPrinterToneCurve, + .link = NULL}, + {.name = "chcusb_blinkLED", + .patch = chcusb_blinkLED, + .link = NULL}, + {.name = "chcusb_resetPrinter", + .patch = chcusb_resetPrinter, + .link = NULL}, + {.name = "chcusb_AttachThreadCount", + .patch = chcusb_AttachThreadCount, + .link = NULL}, + {.name = "chcusb_getPrintIDStatus", + .patch = chcusb_getPrintIDStatus, + .link = NULL}, + {.name = "chcusb_setPrintStandby", + .patch = chcusb_setPrintStandby, + .link = NULL}, + {.name = "chcusb_testCardFeed", + .patch = chcusb_testCardFeed, + .link = NULL}, + {.name = "chcusb_exitCard", + .patch = chcusb_exitCard, + .link = NULL}, + {.name = "chcusb_getCardRfidTID", + .patch = chcusb_getCardRfidTID, + .link = NULL}, + {.name = "chcusb_commCardRfidReader", + .patch = chcusb_commCardRfidReader, + .link = NULL}, + {.name = "chcusb_updateCardRfidReader", + .patch = chcusb_updateCardRfidReader, + .link = NULL}, + {.name = "chcusb_getErrorLog", + .patch = chcusb_getErrorLog, + .link = NULL}, + {.name = "chcusb_getErrorStatus", + .patch = chcusb_getErrorStatus, + .link = NULL}, + {.name = "chcusb_setCutList", + .patch = chcusb_setCutList, + .link = NULL}, + {.name = "chcusb_setLaminatePattern", + .patch = chcusb_setLaminatePattern, + .link = NULL}, + {.name = "chcusb_color_adjustment", + .patch = chcusb_color_adjustment, + .link = NULL}, + {.name = "chcusb_color_adjustmentEx", + .patch = chcusb_color_adjustmentEx, + .link = NULL}, + {.name = "chcusb_getEEPROM", + .patch = chcusb_getEEPROM, + .link = NULL}, + {.name = "chcusb_setParameter", + .patch = chcusb_setParameter, + .link = NULL}, + {.name = "chcusb_getParameter", + .patch = chcusb_getParameter, + .link = NULL}, + {.name = "chcusb_universal_command", + .patch = chcusb_universal_command, + .link = NULL}, +}; + +/* C300usb hook tbl */ + +static struct hook_symbol C300usb_hooks[] = { + {.name = "chcusb_MakeThread", + .patch = chcusb_MakeThread, + .link = NULL}, + {.name = "chcusb_open", + .patch = chcusb_open, + .link = NULL}, + {.name = "chcusb_close", + .patch = chcusb_close, + .link = NULL}, + {.name = "chcusb_ReleaseThread", + .patch = chcusb_ReleaseThread, + .link = NULL}, + {.name = "chcusb_listupPrinter", + .patch = chcusb_listupPrinter, + .link = NULL}, + {.name = "chcusb_listupPrinterSN", + .patch = chcusb_listupPrinterSN, + .link = NULL}, + {.name = "chcusb_selectPrinter", + .patch = chcusb_selectPrinter, + .link = NULL}, + {.name = "chcusb_selectPrinterSN", + .patch = chcusb_selectPrinterSN, + .link = NULL}, + {.name = "chcusb_getPrinterInfo", + .patch = chcusb_getPrinterInfo, + .link = NULL}, + {.name = "chcusb_imageformat", + .patch = chcusb_imageformat, + .link = NULL}, + {.name = "chcusb_setmtf", + .patch = chcusb_setmtf, + .link = NULL}, + {.name = "chcusb_makeGamma", + .patch = chcusb_makeGamma, + .link = NULL}, + {.name = "chcusb_setIcctable", + .patch = chcusb_setIcctable, + .link = NULL}, + {.name = "chcusb_copies", + .patch = chcusb_copies, + .link = NULL}, + {.name = "chcusb_status", + .patch = chcusb_status, + .link = NULL}, + {.name = "chcusb_statusAll", + .patch = chcusb_statusAll, + .link = NULL}, + {.name = "chcusb_startpage", + .patch = chcusb_startpage, + .link = NULL}, + {.name = "chcusb_endpage", + .patch = chcusb_endpage, + .link = NULL}, + {.name = "chcusb_write", + .patch = chcusb_write, + .link = NULL}, + {.name = "chcusb_writeLaminate", + .patch = chcusb_writeLaminate, + .link = NULL}, + {.name = "chcusb_setPrinterInfo", + .patch = chcusb_setPrinterInfo, + .link = NULL}, + {.name = "chcusb_getGamma", + .patch = chcusb_getGamma, + .link = NULL}, + {.name = "chcusb_getMtf", + .patch = chcusb_getMtf, + .link = NULL}, + {.name = "chcusb_cancelCopies", + .patch = chcusb_cancelCopies, + .link = NULL}, + {.name = "chcusb_setPrinterToneCurve", + .patch = chcusb_setPrinterToneCurve, + .link = NULL}, + {.name = "chcusb_getPrinterToneCurve", + .patch = chcusb_getPrinterToneCurve, + .link = NULL}, + {.name = "chcusb_blinkLED", + .patch = chcusb_blinkLED, + .link = NULL}, + {.name = "chcusb_resetPrinter", + .patch = chcusb_resetPrinter, + .link = NULL}, + {.name = "chcusb_AttachThreadCount", + .patch = chcusb_AttachThreadCount, + .link = NULL}, + {.name = "chcusb_getPrintIDStatus", + .patch = chcusb_getPrintIDStatus, + .link = NULL}, + {.name = "chcusb_setPrintStandby", + .patch = chcusb_setPrintStandby, + .link = NULL}, + {.name = "chcusb_testCardFeed", + .patch = chcusb_testCardFeed, + .link = NULL}, + {.name = "chcusb_setParameter", + .patch = chcusb_setParameter, + .link = NULL}, + {.name = "chcusb_getParameter", + .patch = chcusb_getParameter, + .link = NULL}, + {.name = "chcusb_getErrorStatus", + .patch = chcusb_getErrorStatus, + .link = NULL}, + {.name = "chcusb_setCutList", + .patch = chcusb_setCutList, + .link = NULL}, + {.name = "chcusb_setLaminatePattern", + .patch = chcusb_setLaminatePattern, + .link = NULL}, + {.name = "chcusb_color_adjustment", + .patch = chcusb_color_adjustment, + .link = NULL}, + {.name = "chcusb_color_adjustmentEx", + .patch = chcusb_color_adjustmentEx, + .link = NULL}, + {.name = "chcusb_getEEPROM", + .patch = chcusb_getEEPROM, + .link = NULL}, + {.name = "chcusb_universal_command", + .patch = chcusb_universal_command, + .link = NULL}, +}; + +static struct printer_config printer_config; + +void printer_hook_init(const struct printer_config *cfg, int rfid_port_no, HINSTANCE self) { + HANDLE fwFile = NULL; + DWORD bytesRead = 0; + + assert(cfg != NULL); + + if (!cfg->enable) { + return; + } + + /* Set serial and rotate cfg */ + + memcpy(&serialNo, cfg->serial_no, sizeof(serialNo)); + _byteswap_uint64(serialNo); + + rotate180 = cfg->rotate_180; + + memcpy(&printer_config, cfg, sizeof(*cfg)); + hook_table_apply(NULL, "C300usb.dll", C300usb_hooks, _countof(C300usb_hooks)); + hook_table_apply(NULL, "C300FWDLusb.dll", C3XXFWDLusb_hooks, _countof(C3XXFWDLusb_hooks)); + hook_table_apply(NULL, "C310Ausb.dll", C3XXusb_hooks, _countof(C3XXusb_hooks)); + hook_table_apply(NULL, "C310Busb.dll", C3XXusb_hooks, _countof(C3XXusb_hooks)); + hook_table_apply(NULL, "C310FWDLusb.dll", C3XXFWDLusb_hooks, _countof(C3XXFWDLusb_hooks)); + hook_table_apply(NULL, "C310BFWDLusb.dll", C3XXFWDLusb_hooks, _countof(C3XXFWDLusb_hooks)); + hook_table_apply(NULL, "C320Ausb.dll", C3XXusb_hooks, _countof(C3XXusb_hooks)); + hook_table_apply(NULL, "C320AFWDLusb.dll", C3XXFWDLusb_hooks, _countof(C3XXFWDLusb_hooks)); + hook_table_apply(NULL, "C330Ausb.dll", C3XXusb_hooks, _countof(C3XXusb_hooks)); + hook_table_apply(NULL, "C330AFWDLusb.dll", C3XXFWDLusb_hooks, _countof(C3XXFWDLusb_hooks)); + + if (self != NULL) { + dll_hook_push(self, L"C300usb.dll"); // TODO: This doesn't work. Unity moment + dll_hook_push(self, L"C300FWDLusb.dll"); // TODO: ...and this... + dll_hook_push(self, L"C310Ausb.dll"); + dll_hook_push(self, L"C310Busb.dll"); // TODO: ...and this... + dll_hook_push(self, L"C310FWDLusb.dll"); + dll_hook_push(self, L"C310BFWDLusb.dll"); // TODO: ...aaaand this. + dll_hook_push(self, L"C320Ausb.dll"); + dll_hook_push(self, L"C320AFWDLusb.dll"); + dll_hook_push(self, L"C330Ausb.dll"); + dll_hook_push(self, L"C330AFWDLusb.dll"); + } + + // Load firmware if has previously been written to + fwFile = CreateFileW(cfg->main_fw_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fwFile != NULL) { + ReadFile(fwFile, mainFirmware, sizeof(mainFirmware), &bytesRead, NULL); + CloseHandle(fwFile); + } + + fwFile = CreateFileW(cfg->dsp_fw_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fwFile != NULL) { + ReadFile(fwFile, dspFirmware, sizeof(dspFirmware), &bytesRead, NULL); + CloseHandle(fwFile); + } + + fwFile = CreateFileW(cfg->param_fw_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fwFile != NULL) { + ReadFile(fwFile, paramFirmware, sizeof(dspFirmware), &bytesRead, NULL); + CloseHandle(fwFile); + } + + CreateDirectoryW(cfg->printer_out_path, NULL); + memcpy(printer_out_path, cfg->printer_out_path, MAX_PATH); + + if (rfid_port_no != 0) { + InitializeCriticalSection(&deck_lock); + + uart_init(&deck_uart, rfid_port_no); + deck_uart.written.bytes = deck_written_bytes; + deck_uart.written.nbytes = sizeof(deck_written_bytes); + deck_uart.readable.bytes = deck_readable_bytes; + deck_uart.readable.nbytes = sizeof(deck_readable_bytes); + + dprintf("Printer: RFID: hook enabled.\n"); + + if (FAILED(iohook_push_handler(deck_handle_irp))) { + dprintf("Printer: RFID: failed to hook IRP handler.\n"); + } + } + + dprintf("Printer: hook enabled.\n"); +} + +static void generate_rfid(void) { + for (int i = 0; i < sizeof(cardRFID); i++) + cardRFID[i] = rand(); + + dprintf("Printer: New card RFID:\n"); + dump(cardRFID, sizeof(cardRFID)); +} + +static HRESULT deck_handle_irp(struct irp *irp) { + HRESULT hr; + + assert(irp != NULL); + + if (!uart_match_irp(&deck_uart, irp)) { + return iohook_invoke_next(irp); + } + + EnterCriticalSection(&deck_lock); + hr = deck_handle_irp_locked(irp); + LeaveCriticalSection(&deck_lock); + + return hr; +} + +static HRESULT deck_handle_irp_locked(struct irp *irp) { + uint8_t req[1024]; + struct iobuf req_iobuf; + HRESULT hr; + + if (irp->op == IRP_OP_OPEN) { + dprintf("Printer: RFID: Starting backend\n"); + } + + hr = uart_handle_irp(&deck_uart, irp); + + if (SUCCEEDED(hr) && irp->op == IRP_OP_READ && read_pending == true) { + deck_read_one(); + return S_OK; + } + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { + // if (deck_uart.written.pos != 0) { + // dprintf("Printer: RFID: TX Buffer:\n"); + // dump_iobuf(&deck_uart.written); + // } + + req_iobuf.bytes = req; + req_iobuf.nbytes = sizeof(req); + req_iobuf.pos = 0; + + hr = deck_frame_decode(&req_iobuf, &deck_uart.written); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("Printer: RFID: Deframe error: %x, %d %d\n", (int)hr, (int)req_iobuf.nbytes, (int)req_iobuf.pos); + } + + return hr; + } + + // dprintf("Printer: RFID: Deframe Buffer:\n"); + // dump_iobuf(&req_iobuf); + + hr = deck_req_dispatch((const union deck_req_any *)&req); + + if (FAILED(hr)) { + dprintf("Printer: RFID: Processing error: %x\n", (int)hr); + } + + // dprintf("Printer: RFID: Written bytes:\n"); + // dump_iobuf(&deck_uart.readable); + } +} + +static HRESULT deck_req_dispatch(const union deck_req_any *req) { + switch (req->hdr.cmd) { + case DECK_CMD_RESET: + case DECK_CMD_INIT_UNK1: + case DECK_CMD_INIT_UNK2: + case DECK_CMD_INIT_UNK3: + return deck_req_nop(req->hdr.cmd); + + case DECK_CMD_GET_BOOT_FW_VERSION: + return deck_req_get_boot_fw_version(); + + case DECK_CMD_GET_APP_FW_VERSION: + return deck_req_get_app_fw_version(); + + case DECK_CMD_GET_BOARD_INFO: + return deck_req_get_board_info(); + + case DECK_CMD_READ: + return deck_req_read(); + + case DECK_CMD_READ_DATA: + return deck_req_read_data(); + + case DECK_CMD_WRITE: + return deck_req_write(req->bytes); + + default: + dprintf("Printer: RFID: Unhandled command %#02x\n", req->hdr.cmd); + + return S_OK; + } +} + +static HRESULT deck_req_get_boot_fw_version(void) { + struct deck_resp_get_boot_fw_version resp; + + dprintf("Printer: RFID: Get Boot FW Version\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_GET_BOOT_FW_VERSION; + resp.hdr.status = 0; + resp.hdr.nbytes = 1; + resp.boot_fw_version = 0x90; + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT deck_req_get_app_fw_version(void) { + struct deck_resp_get_app_fw_version resp; + + dprintf("Printer: RFID: Get App FW Version\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_GET_APP_FW_VERSION; + resp.hdr.status = 0; + resp.hdr.nbytes = 1; + resp.app_fw_version = 0x90; + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT deck_req_get_board_info(void) { + struct deck_resp_get_board_info resp; + + dprintf("Printer: RFID: Get Board Info\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_GET_BOARD_INFO; + resp.hdr.status = 0; + resp.hdr.nbytes = 9; + memcpy(resp.board, (void *)"837-15347", 9); + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT deck_req_read(void) { + struct deck_resp_read resp; + + dprintf("Printer: RFID: Read Card\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_READ; + resp.hdr.status = DECK_READ_START; + resp.hdr.nbytes = 0; + + current_card_idx = 0; + read_pending = true; + + return deck_frame_encode(&deck_uart.readable, &resp.hdr, sizeof(resp.hdr)); +} + +static HRESULT deck_req_read_data(void) { + struct deck_resp_read_data resp; + + dprintf("Printer: RFID: Read Card Data\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_READ_DATA; + resp.hdr.status = 0; + resp.hdr.nbytes = 32; + memcpy(resp.card_data, cardDataBuffer, 32); + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + +static HRESULT deck_req_write(const uint8_t *req_bytes) { + struct deck_resp_hdr resp; + uint8_t off; + + resp.sync = 0xE0; + resp.cmd = DECK_CMD_WRITE; + resp.status = 0; + resp.nbytes = 0; + + off = (req_bytes[5] - 1) * 2; + cardDataBuffer[off] = req_bytes[6]; + cardDataBuffer[off + 1] = req_bytes[7]; + if (req_bytes[5] == 0x10) { + dprintf("Printer: RFID: Card Data Buffer: \n"); + dump(cardDataBuffer, 0x20); + } + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + +static void deck_read_one(void) { + struct deck_resp_read resp; + + dprintf("Printer: RFID: Read One Card\n"); + + resp.hdr.sync = 0xE0; + resp.hdr.cmd = DECK_CMD_READ; + + if (current_card_idx < 1) { + resp.hdr.status = DECK_READ_DATA; + resp.hdr.nbytes = 44; + memset(resp.card_data, 0, 44); + memcpy(resp.card_data, cardRFID, 0xC); + memcpy(resp.card_data + 0x0C, cardDataBuffer, 0x20); + dump(resp.card_data, 44); + + deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); + current_card_idx++; + } else { + resp.hdr.status = DECK_READ_END; + resp.hdr.nbytes = 0; + deck_frame_encode(&deck_uart.readable, &resp.hdr, sizeof(resp.hdr)); + read_pending = false; + } +} + +static HRESULT deck_req_nop(uint8_t cmd) { + struct deck_resp_hdr resp; + + dprintf("Printer: RFID: No-op cmd %#02x\n", cmd); + + resp.sync = 0xE0; + resp.cmd = cmd; + resp.status = 0; + resp.nbytes = 0; + + return deck_frame_encode(&deck_uart.readable, &resp, sizeof(resp)); +} + +static void deck_frame_sync(struct iobuf *src) { + size_t i; + + for (i = 0; i < src->pos && src->bytes[i] != 0xE0; i++) + ; + + src->pos -= i; + memmove(&src->bytes[0], &src->bytes[i], i); +} + +static HRESULT deck_frame_accept(const struct iobuf *dest) { + if (dest->pos < 2 || dest->pos != dest->bytes[2] + 4) { + return S_FALSE; + } + + return S_OK; +} + +static HRESULT deck_frame_decode(struct iobuf *dest, struct iobuf *src) { + uint8_t byte; + bool escape; + size_t i; + HRESULT hr; + + assert(dest != NULL); + assert(dest->bytes != NULL || dest->nbytes == 0); + assert(dest->pos <= dest->nbytes); + assert(src != NULL); + assert(src->bytes != NULL || src->nbytes == 0); + assert(src->pos <= src->nbytes); + + dest->pos = 0; + escape = false; + + for (i = 0, hr = S_FALSE; i < src->pos && hr == S_FALSE; i++) { + /* Step the FSM to unstuff another byte */ + + byte = src->bytes[i]; + + if (dest->pos >= dest->nbytes) { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } else if (i == 0) { + dest->bytes[dest->pos++] = byte; + } else if (byte == 0xE0) { + hr = E_FAIL; + } else if (byte == 0xD0) { + if (escape) { + hr = E_FAIL; + } + + escape = true; + } else if (escape) { + dest->bytes[dest->pos++] = byte + 1; + escape = false; + } else { + dest->bytes[dest->pos++] = byte; + } + + /* Try to accept the packet we've built up so far */ + + if (SUCCEEDED(hr)) { + hr = deck_frame_accept(dest); + } + } + + /* Handle FSM terminal state */ + + if (hr != S_FALSE) { + /* Frame was either accepted or rejected, remove it from src */ + memmove(&src->bytes[0], &src->bytes[i], src->pos - i); + src->pos -= i; + } + + return hr; +} + +static HRESULT deck_frame_encode( + struct iobuf *dest, + const void *ptr, + size_t nbytes) { + const uint8_t *src; + uint8_t checksum; + uint8_t byte; + size_t i; + HRESULT hr; + + assert(dest != NULL); + assert(dest->bytes != NULL || dest->nbytes == 0); + assert(dest->pos <= dest->nbytes); + assert(ptr != NULL); + + src = ptr; + + assert(nbytes >= 2 && src[0] == 0xE0 && src[3] + 4 == nbytes); + + if (dest->pos >= dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = 0xE0; + checksum = 0x0; + + for (i = 1; i < nbytes; i++) { + byte = src[i]; + checksum += byte; + + hr = deck_frame_encode_byte(dest, byte); + + if (FAILED(hr)) { + return hr; + } + } + + return deck_frame_encode_byte(dest, checksum); +} + +static HRESULT deck_frame_encode_byte(struct iobuf *dest, uint8_t byte) { + if (byte == 0xD0 || byte == 0xE0) { + if (dest->pos + 2 > dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = 0xD0; + dest->bytes[dest->pos++] = byte - 1; + } else { + if (dest->pos + 1 > dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = byte; + } + + return S_OK; +} + +// C310FWDLusb stubs + +int fwdlusb_open(uint16_t *rResult) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +void fwdlusb_close() {} + +int fwdlusb_listupPrinter(uint8_t *rIdArray) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + memset(rIdArray, 0xFF, 0x80); + rIdArray[0] = idNumber; + return 1; +} + +int fwdlusb_listupPrinterSN(uint64_t *rSerialArray) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + memset(rSerialArray, 0xFF, 0x400); + rSerialArray[0] = serialNo; + return 1; +} + +int fwdlusb_selectPrinter(uint8_t printerId, uint16_t *rResult) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int fwdlusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int fwdlusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + switch (tagNumber) { + case 0: // getPaperInfo + if (*rLen != 0x67) *rLen = 0x67; + if (rBuffer) memset(rBuffer, 0, *rLen); + return 1; + case 3: // getFirmwareVersion + if (*rLen != 0x99) *rLen = 0x99; + if (rBuffer) { + memset(rBuffer, 0, *rLen); + // bootFirmware + int i = 1; + memcpy(rBuffer + i, mainFirmware, sizeof(mainFirmware)); + // mainFirmware + i += 0x26; + memcpy(rBuffer + i, mainFirmware, sizeof(mainFirmware)); + // printParameterTable + i += 0x26; + memcpy(rBuffer + i, paramFirmware, sizeof(paramFirmware)); + // dspFirmware (C300 only) + i += 0x26; + memcpy(rBuffer + i, dspFirmware, 0x26); + } + return 1; + case 4: // getPrintCountInfo (C300 only) + if (!rBuffer) { + *rLen = 0x1C; + return 1; + } + int32_t bInfoC300[10] = {0}; + uint16_t printCounterC300; + bInfoC300[0] = 22; // printCounter0 + bInfoC300[1] = 23; // printCounter1 + bInfoC300[2] = 33; // feedRollerCount + bInfoC300[3] = 55; // cutterCount + bInfoC300[4] = 88; // headCount + bInfoC300[5] = 999; // ribbonRemain + bInfoC300[6] = 0; // dummy + if (*rLen <= 0x1Cu) { + memcpy(rBuffer, bInfoC300, *rLen); + } else { + bInfoC300[7] = 0; // TODO + if (*rLen > 0x20u) *rLen = 0x20; + memcpy(rBuffer, bInfoC300, *rLen); + } + break; + case 5: // getPrintCountInfo + if (!rBuffer) { + *rLen = 0x28; + return 1; + } + int32_t bInfo[10] = {0}; + bInfo[0] = 22; // printCounter0 + bInfo[1] = 23; // printCounter1 + bInfo[2] = 33; // feedRollerCount + bInfo[3] = 55; // cutterCount + bInfo[4] = 88; // headCount + bInfo[5] = 999; // ribbonRemain + bInfo[6] = 99; // holoHeadCount + if (*rLen <= 0x1Cu) { + memcpy(rBuffer, bInfo, *rLen); + } else { + bInfo[7] = 69; // paperCount + bInfo[8] = 21; // printCounter2 + bInfo[9] = 20; // holoPrintCounter + if (*rLen > 0x28u) *rLen = 0x28; + memcpy(rBuffer, bInfo, *rLen); + } + break; + case 26: // getPrinterSerial + if (*rLen != 8) *rLen = 8; + if (rBuffer) memcpy(rBuffer, &serialNo, 8); + return 1; + default: + dprintf("Printer: %s: Unknown parameter 'tagNumber' value %d.\n", __func__, tagNumber); + break; + } + return 1; +} + +int fwdlusb_status(uint16_t *rResult) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int fwdlusb_statusAll(uint8_t *idArray, uint16_t *rResultArray) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + for (int i = 0; *(uint8_t *)(idArray + i) != 255 && i < 128; ++i) { + *(uint16_t *)(rResultArray + 2 * i) = 0; + } + + return 1; +} + +int fwdlusb_resetPrinter(uint16_t *rResult) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int fwdlusb_getFirmwareVersion(uint8_t *buffer, int size) { + int8_t a; + uint32_t b = 0; + for (int32_t i = 0; i < size; ++i) { + if (*(int8_t *)(buffer + i) < 0x30 || *(int8_t *)(buffer + i) > 0x39) { + if (*(int8_t *)(buffer + i) < 0x41 || *(int8_t *)(buffer + i) > 0x46) { + if (*(int8_t *)(buffer + i) < 0x61 || *(int8_t *)(buffer + i) > 0x66) { + return 0; + } + a = *(int8_t *)(buffer + i) - 0x57; + } else { + a = *(int8_t *)(buffer + i) - 0x37; + } + } else { + a = *(int8_t *)(buffer + i) - 0x30; + } + b = a + 0x10 * b; + } + return b; +} + +int fwdlusb_updateFirmware_main(uint8_t update, LPCSTR filename, uint16_t *rResult) { + DWORD result; + HANDLE fwFile = NULL; + DWORD bytesWritten = 0; + + if (filename) { + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return 0; + { + if (rResult) *rResult = 1005; + result = 0; + } + + DWORD read; + uint8_t buffer[0x40]; + result = ReadFile(hFile, buffer, 0x40, &read, NULL); + CloseHandle(hFile); + if (result && read > 0x24) { + uint8_t rBuffer[0x40] = {0}; + + memcpy(rBuffer, buffer + 0x2, 0x8); + memcpy(rBuffer + 0x8, buffer + 0xA, 0x10); + memcpy(rBuffer + 0x18, buffer + 0x20, 0xA); + *(rBuffer + 0x22) = (uint8_t)fwdlusb_getFirmwareVersion(buffer + 0x1A, 0x2); + *(rBuffer + 0x23) = (uint8_t)fwdlusb_getFirmwareVersion(buffer + 0x1C, 0x2); + memcpy(rBuffer + 0x24, buffer + 0x2A, 0x2); + + memcpy(mainFirmware, rBuffer, 0x40); + + fwFile = CreateFileW(printer_config.main_fw_path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (fwFile != NULL) { + WriteFile(fwFile, rBuffer, 0x40, &bytesWritten, NULL); + CloseHandle(fwFile); + } + + if (rResult) *rResult = 0; + result = 1; + } else { + if (rResult) *rResult = 1005; + result = 0; + } + } else { + if (rResult) *rResult = 1006; + result = 0; + } + + return result; +} + +int fwdlusb_updateFirmware_dsp(uint8_t update, LPCSTR filename, uint16_t *rResult) { + DWORD result; + HANDLE fwFile = NULL; + DWORD bytesWritten = 0; + + if (filename) { + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return 0; + { + if (rResult) *rResult = 1005; + result = 0; + } + + DWORD read; + uint8_t buffer[0x40]; + result = ReadFile(hFile, buffer, 0x40, &read, NULL); + CloseHandle(hFile); + if (result && read > 0x24) { + uint8_t rBuffer[0x40] = {0}; + + memcpy(rBuffer, buffer + 0x2, 8); + memcpy(rBuffer + 0x8, buffer + 0xA, 0x10); + memcpy(rBuffer + 0x18, buffer + 0x20, 0xA); + memcpy(rBuffer + 0x22, buffer + 0x1A, 0x1); + memcpy(rBuffer + 0x23, buffer + 0x1C, 0x1); + memcpy(rBuffer + 0x24, buffer + 0x2A, 0x2); + + memcpy(dspFirmware, rBuffer, 0x40); + + fwFile = CreateFileW(printer_config.dsp_fw_path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (fwFile != NULL) { + WriteFile(fwFile, rBuffer, 0x40, &bytesWritten, NULL); + CloseHandle(fwFile); + } + + if (rResult) *rResult = 0; + result = 1; + } else { + if (rResult) *rResult = 1005; + result = 0; + } + } else { + if (rResult) *rResult = 1006; + result = 0; + } + + return result; +} + +int fwdlusb_updateFirmware_param(uint8_t update, LPCSTR filename, uint16_t *rResult) { + DWORD result; + HANDLE fwFile = NULL; + DWORD bytesWritten = 0; + + if (filename) { + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return 0; + { + if (rResult) *rResult = 1005; + result = 0; + } + + DWORD read; + uint8_t buffer[0x40]; + result = ReadFile(hFile, buffer, 0x40, &read, NULL); + CloseHandle(hFile); + if (result && read > 0x24) { + uint8_t rBuffer[0x40] = {0}; + + memcpy(rBuffer, buffer + 0x2, 8); + memcpy(rBuffer + 0x8, buffer + 0xA, 0x10); + memcpy(rBuffer + 0x18, buffer + 0x20, 0xA); + memcpy(rBuffer + 0x22, buffer + 0x1A, 0x1); + memcpy(rBuffer + 0x23, buffer + 0x1C, 0x1); + memcpy(rBuffer + 0x24, buffer + 0x2A, 0x2); + + memcpy(paramFirmware, rBuffer, 0x40); + + fwFile = CreateFileW(printer_config.param_fw_path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (fwFile != NULL) { + WriteFile(fwFile, rBuffer, 0x40, &bytesWritten, NULL); + CloseHandle(fwFile); + } + + if (rResult) *rResult = 0; + result = 1; + } else { + if (rResult) *rResult = 1005; + result = 0; + } + } else { + if (rResult) *rResult = 1006; + result = 0; + } + + return result; +} + +int fwdlusb_updateFirmware(uint8_t update, LPCSTR filename, uint16_t *rResult) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + if (update == 1) { + return fwdlusb_updateFirmware_main(update, filename, rResult); + } else if (update == 2) { + return fwdlusb_updateFirmware_dsp(update, filename, rResult); + } else if (update == 3) { + return fwdlusb_updateFirmware_param(update, filename, rResult); + } else { + *rResult = 0; + return 1; + } +} + +int fwdlusb_getFirmwareInfo_main(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) { + DWORD result; + + if (filename) { + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return 0; + { + if (rResult) *rResult = 1005; + result = 0; + } + + DWORD read; + uint8_t buffer[0x40]; + result = ReadFile(hFile, buffer, 0x40, &read, NULL); + if (result && read > 0x24) { + memcpy(rBuffer, buffer + 0x2, 0x8); + memcpy(rBuffer + 0x8, buffer + 0xA, 0x10); + memcpy(rBuffer + 0x18, buffer + 0x20, 0xA); + *(rBuffer + 0x22) = (uint8_t)fwdlusb_getFirmwareVersion(buffer + 0x1A, 0x2); + *(rBuffer + 0x23) = (uint8_t)fwdlusb_getFirmwareVersion(buffer + 0x1C, 0x2); + memcpy(rBuffer + 0x24, buffer + 0x2A, 0x2); + + if (rResult) *rResult = 0; + result = 1; + } else { + if (rResult) *rResult = 1005; + result = 0; + } + } else { + if (rResult) *rResult = 1006; + result = 0; + } + + return result; +} + +int fwdlusb_getFirmwareInfo_dsp(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) { + DWORD result; + + if (filename) { + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return 0; + { + if (rResult) *rResult = 1005; + result = 0; + } + + DWORD read; + uint8_t buffer[0x40]; + result = ReadFile(hFile, buffer, 0x40, &read, NULL); + if (result && read > 0x24) { + memcpy(rBuffer, buffer + 0x2, 8); + memcpy(rBuffer + 0x8, buffer + 0xA, 0x10); + memcpy(rBuffer + 0x18, buffer + 0x20, 0xA); + memcpy(rBuffer + 0x22, buffer + 0x1A, 0x1); + memcpy(rBuffer + 0x23, buffer + 0x1C, 0x1); + memcpy(rBuffer + 0x24, buffer + 0x2A, 0x2); + + if (rResult) *rResult = 0; + result = 1; + } else { + if (rResult) *rResult = 1005; + result = 0; + } + } else { + if (rResult) *rResult = 1006; + result = 0; + } + + return result; +} + +int fwdlusb_getFirmwareInfo_param(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) { + DWORD result; + + if (filename) { + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return 0; + { + if (rResult) *rResult = 1005; + result = 0; + } + + DWORD read; + uint8_t buffer[0x40]; + result = ReadFile(hFile, buffer, 0x40, &read, NULL); + if (result && read > 0x24) { + memcpy(rBuffer, buffer + 0x2, 8); + memcpy(rBuffer + 0x8, buffer + 0xA, 0x10); + memcpy(rBuffer + 0x18, buffer + 0x20, 0xA); + memcpy(rBuffer + 0x22, buffer + 0x1A, 0x1); + memcpy(rBuffer + 0x23, buffer + 0x1C, 0x1); + memcpy(rBuffer + 0x24, buffer + 0x2A, 0x2); + + if (rResult) *rResult = 0; + result = 1; + } else { + if (rResult) *rResult = 1005; + result = 0; + } + } else { + if (rResult) *rResult = 1006; + result = 0; + } + + return result; +} + +int fwdlusb_getFirmwareInfo(uint8_t update, LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) { + dprintf("Printer: C3XXFWDLusb: %s(update: %d, filename: %s)\n", __func__, update, filename); + if (!rBuffer) { + *rLen = 38; + return 1; + } + if (*rLen > 38) *rLen = 38; + if (update == 1) { + return fwdlusb_getFirmwareInfo_main(filename, rBuffer, rLen, rResult); + } else if (update == 2) { + return fwdlusb_getFirmwareInfo_dsp(filename, rBuffer, rLen, rResult); + } else if (update == 3) { + return fwdlusb_getFirmwareInfo_param(filename, rBuffer, rLen, rResult); + } else { + if (rResult) *rResult = 0; + return 1; + } +} + +int fwdlusb_MakeThread(uint16_t maxCount) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + return 1; +} + +int fwdlusb_ReleaseThread(uint16_t *rResult) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int fwdlusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + *rCount = 0; + *rMaxCount = 1; + return 1; +} + +int fwdlusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult) { + dprintf("Printer: C3XXFWDLusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +// C3XXusb stubs + +int chcusb_MakeThread(uint16_t maxCount) { + dprintf("Printer: C3XXusb: %s\n", __func__); + return 1; +} + +int chcusb_open(uint16_t *rResult) { + // Seed random for card id generation + srand(time(NULL)); + generate_rfid(); + + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +void chcusb_close() { + dprintf("Printer: C3XXusb: %s\n", __func__); +} + +int chcusb_ReleaseThread(uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + return 1; +} + +int chcusb_listupPrinter(uint8_t *rIdArray) { + dprintf("Printer: C3XXusb: %s\n", __func__); + memset(rIdArray, 0xFF, 0x80); + rIdArray[0] = idNumber; + return 1; +} + +int chcusb_listupPrinterSN(uint64_t *rSerialArray) { + dprintf("Printer: C3XXusb: %s\n", __func__); + memset(rSerialArray, 0xFF, 0x400); + rSerialArray[0] = serialNo; + return 1; +} + +int chcusb_selectPrinter(uint8_t printerId, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen) { + // dprintf("Printer: C3XXusb: %s\n", __func__); + + switch (tagNumber) { + // getPaperInfo + case 0: + if (*rLen != 0x67) *rLen = 0x67; + if (rBuffer) memset(rBuffer, 0, *rLen); + break; + + case 3: // getFirmwareVersion + if (*rLen != 0x99) *rLen = 0x99; + if (rBuffer) { + memset(rBuffer, 0, *rLen); + // bootFirmware + int i = 1; + memcpy(rBuffer + i, mainFirmware, sizeof(mainFirmware)); + // mainFirmware + i += 0x26; + memcpy(rBuffer + i, mainFirmware, sizeof(mainFirmware)); + // printParameterTable + i += 0x26; + memcpy(rBuffer + i, paramFirmware, sizeof(paramFirmware)); + // dspFirmware (C300 only) + i += 0x26; + memcpy(rBuffer + i, dspFirmware, 0x26); + } + break; + + case 4: // getPrintCountInfo (C300 only) + if (!rBuffer) { + *rLen = 0x1C; + return 1; + } + int32_t bInfoC300[10] = {0}; + bInfoC300[0] = 22; // printCounter0 + bInfoC300[1] = 23; // printCounter1 + bInfoC300[2] = 33; // feedRollerCount + bInfoC300[3] = 55; // cutterCount + bInfoC300[4] = 88; // headCount + bInfoC300[5] = 999; // ribbonRemain + bInfoC300[6] = 0; // dummy + if (*rLen <= 0x1Cu) { + memcpy(rBuffer, bInfoC300, *rLen); + } else { + bInfoC300[7] = 0; // TODO + if (*rLen > 0x20u) *rLen = 0x20; + memcpy(rBuffer, bInfoC300, *rLen); + } + break; + + case 5: // getPrintCountInfo2 (C310A and later) + if (!rBuffer) { + *rLen = 0x28; + return 1; + } + int32_t bInfo[10] = {0}; + bInfo[0] = 22; // printCounter0 + bInfo[1] = 23; // printCounter1 + bInfo[2] = 33; // feedRollerCount + bInfo[3] = 55; // cutterCount + bInfo[4] = 88; // headCount + bInfo[5] = 999; // ribbonRemain + bInfo[6] = 99; // holoHeadCount + if (*rLen <= 0x1Cu) { + memcpy(rBuffer, bInfo, *rLen); + } else { + bInfo[7] = 69; // paperCount + bInfo[8] = 21; // printCounter2 + bInfo[9] = 20; // holoPrintCounter + if (*rLen > 0x28u) *rLen = 0x28; + memcpy(rBuffer, bInfo, *rLen); + } + break; + + case 6: // pageStatusInfo + *rLen = 32; + if (rBuffer) { + memset(rBuffer, 0, 32); + rBuffer[0] = 88; // holoRemain + rBuffer[1] = 0; + rBuffer[2] = 0; + rBuffer[3] = 0; + } + break; + + case 7: // svc + *rLen = 2; + if (rBuffer) { + memset(rBuffer, 0, 2); + rBuffer[0] = 0; // mainError + rBuffer[1] = 0; // subError + } + break; + + case 8: // printStandby + *rLen = 1; + if (awaitingCardExit) + *rBuffer = 0xF0; + else + *rBuffer = 0; + break; + + case 16: // memoryInfo + *rLen = 18; + if (rBuffer) memset(rBuffer, 0, 18); + break; + + case 20: // printMode + dprintf("Printer: C3xxusb: Unimpl tagNumber 20\n"); + break; + + case 26: // getPrinterSerial + if (*rLen != 8) *rLen = 8; + if (rBuffer) memcpy(rBuffer, &serialNo, 8); + break; + + case 30: // TODO + dprintf("Printer: C3xxusb: Unimpl tagNumber 30\n"); + break; + + case 31: // TODO, possibly CardRFIDCheck? + *rLen = 1; + if (rBuffer) memset(rBuffer, 0, 1); + break; + + case 40: // temperature + *rLen = 10; + if (rBuffer) { + memset(rBuffer, 0, 10); + rBuffer[0] = 1; + rBuffer[1] = 2; + rBuffer[2] = 3; + } + break; + + case 50: // errHistory + *rLen = 61; + if (rBuffer) memset(rBuffer, 0, 61); + break; + + case 60: // getToneTable + *rLen = 6; + if (rBuffer) memset(rBuffer, 0, 6); + break; + + default: + dprintf("Printer: unknown tagNumber, %d", tagNumber); + break; + } + + return 1; +} + +int chcusb_imageformat( + uint16_t format, + uint16_t ncomp, + uint16_t depth, + uint16_t width, + uint16_t height, + uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + WIDTH = width; + HEIGHT = height; + + *rResult = 0; + return 1; +} + +int chcusb_setmtf(int32_t *mtf) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + memcpy(MTF, mtf, sizeof(MTF)); + return 1; +} + +int chcusb_makeGamma(uint16_t k, uint8_t *intoneR, uint8_t *intoneG, uint8_t *intoneB) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + uint8_t tone; + int32_t value; + double power; + + double factor = (double)k / 100.0; + + for (int i = 0; i < 256; ++i) { + power = pow((double)i, factor); + value = (int)(power / pow(255.0, factor) * 255.0); + + if (value > 255) + tone = 255; + if (value >= 0) + tone = value; + else + tone = 0; + + if (intoneR) + *(uint8_t *)(intoneR + i) = tone; + if (intoneG) + *(uint8_t *)(intoneG + i) = tone; + if (intoneB) + *(uint8_t *)(intoneB + i) = tone; + } + + return 1; +} + +int chcusb_setIcctable( + LPCSTR icc1, + LPCSTR icc2, + uint16_t intents, + uint8_t *intoneR, + uint8_t *intoneG, + uint8_t *intoneB, + uint8_t *outtoneR, + uint8_t *outtoneG, + uint8_t *outtoneB, + uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + for (int i = 0; i < 256; ++i) { + intoneR[i] = i; + intoneG[i] = i; + intoneB[i] = i; + outtoneR[i] = i; + outtoneG[i] = i; + outtoneB[i] = i; + } + + *rResult = 0; + return 1; +} + +int chcusb_copies(uint16_t copies, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_status(uint16_t *rResult) { + // dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_statusAll(uint8_t *idArray, uint16_t *rResultArray) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + for (int i = 0; *(uint8_t *)(idArray + i) != 255 && i < 128; ++i) { + *(uint16_t *)(rResultArray + 2 * i) = 0; + } + + return 1; +} + +int chcusb_startpage(uint16_t postCardState, uint16_t *pageId, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + *pageId = 1; + *rResult = 0; + return 1; +} + +int chcusb_endpage(uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + awaitingCardExit = true; + + *rResult = 0; + return 1; +} + +int chcusb_write(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) { + SYSTEMTIME t; + GetLocalTime(&t); + + wchar_t dumpPath[MAX_PATH]; + uint8_t metadata[44]; + swprintf_s( + dumpPath, MAX_PATH, + L"%s\\C3XX_%04d%02d%02d_%02d%02d%02d.bmp", + printer_out_path, t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); + + memcpy(metadata, cardRFID, 12); + memcpy(metadata + 12, cardDataBuffer, 32); + + WriteDataToBitmapFile(dumpPath, 24, WIDTH, HEIGHT, data, *writeSize, metadata, 44, rotate180); + // WriteArrayToFile(dumpPath, data, IMAGE_SIZE, FALSE); + dprintf("Printer: C3XXusb: %s\n", __func__); + dwprintf(L"Printer: File written: %s\n", dumpPath); + + *rResult = 0; + + return 1; +} + +int chcusb_writeLaminate(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) { + SYSTEMTIME t; + GetLocalTime(&t); + + char dumpPath[0x80]; + sprintf_s( + dumpPath, 0x80, + "C3XXusb_%04d%02d%02d_%02d%02d%02d_writeLaminate.bin", + t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); + + // WriteArrayToFile(dumpPath, data, *writeSize, FALSE); + dprintf("Printer: C3XXusb: %s\n", __func__); + + // *writeSize = written; + *rResult = 0; + + return 1; +} + +int chcusb_writeHolo(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) { + SYSTEMTIME t; + GetLocalTime(&t); + + char dumpPath[0x80]; + sprintf_s( + dumpPath, 0x80, + "C3XXusb_%04d%02d%02d_%02d%02d%02d_writeHolo.bin", + t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); + + // WriteArrayToFile(dumpPath, data, HOLO_SIZE, FALSE); + dprintf("Printer: C3XXusb: %s\n", __func__); + + *writeSize = HOLO_SIZE; + *rResult = 0; + + return 1; +} + +int chcusb_setPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + switch (tagNumber) { + case 0: // setPaperInfo + memcpy(PAPERINFO, rBuffer, sizeof(PAPERINFO)); + break; + case 20: // setPolishInfo + memcpy(POLISH, rBuffer, sizeof(POLISH)); + break; + default: + break; + } + + *rResult = 0; + return 1; +} + +int chcusb_getGamma(LPCSTR filename, uint8_t *r, uint8_t *g, uint8_t *b, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + for (int i = 0; i < 256; ++i) { + r[i] = i; + g[i] = i; + b[i] = i; + } + + *rResult = 0; + return 1; +} + +int chcusb_getMtf(LPCSTR filename, int32_t *mtf, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return 0; + + DWORD read; + uint8_t buffer[0x40]; + BOOL result = ReadFile(hFile, buffer, 0x40, &read, NULL); + if (!result) return 0; + + int a, c; + int b = 1; + int d = c = 0; + + memset(mtf, 0, sizeof(MTF)); + + for (DWORD i = 0; i < read; i++) { + a = buffer[i] - 0x30; + if (a == -3 && c == 0) { + b = -1; + } else if (a >= 0 && a <= 9) { + mtf[d] = mtf[d] * 10 + a; + c++; + } else if (c > 0) { + mtf[d] *= b; + b = 1; + c = 0; + d++; + } + if (d > 9) break; + } + + *rResult = 0; + return 1; +} + +int chcusb_cancelCopies(uint16_t pageId, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_setPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + if ((type > 0 && type < 3) && (number > 0 && number < 3)) { + CURVE[type][number] = *data; + } + *rResult = 0; + return 1; +} + +int chcusb_getPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + if ((type > 0 && type < 3) && (number > 0 && number < 3)) { + *data = CURVE[type][number]; + } + *rResult = 0; + return 1; +} + +int chcusb_blinkLED(uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_resetPrinter(uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rCount = 0; + *rMaxCount = 1; + return 1; +} + +int chcusb_getPrintIDStatus(uint16_t pageId, uint8_t *rBuffer, uint16_t *rResult) { + // dprintf("Printer: C3XXusb: %s\n", __func__); + memset(rBuffer, 0, 8); + *((uint16_t *)(rBuffer + 6)) = 2300; + *rResult = 0; + return 1; +} + +int chcusb_setPrintStandby(uint16_t position, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_testCardFeed(uint16_t mode, uint16_t times, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_exitCard(uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + awaitingCardExit = false; + generate_rfid(); + + *rResult = 0; + return 1; +} + +int chcusb_getCardRfidTID(uint8_t *rCardTID, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + + memcpy(rCardTID, cardRFID, sizeof(cardRFID)); + *rResult = 2405; + return 1; +} + +int chcusb_commCardRfidReader(uint8_t *sendData, uint8_t *rRecvData, uint32_t sendSize, uint32_t *rRecvSize, uint16_t *rResult) { + uint8_t off; + + dprintf("Printer: C3XXusb: %s\n", __func__); + // dprintf("Printer: C3XXusb: commCardRfidReader send buffer: \n"); + // dump(sendData, sendSize); + + *rResult = 0; + + switch (sendData[0]) { + case 0x02: + memset(rRecvData, 0, 35); + rRecvData[0] = 2; + rRecvData[2] = 32; + memcpy(rRecvData + 3, cardDataBuffer, 32); + *rRecvSize = 35; + break; + case 0x03: + off = (sendData[4] - 1) * 2; + cardDataBuffer[off] = sendData[5]; + cardDataBuffer[off + 1] = sendData[6]; + if (sendData[4] == 0x10) { + dprintf("Printer: C3XXusb: commCardRfidReader card data buffer: \n"); + dump(cardDataBuffer, 0x20); + } + memset(rRecvData, 0, 3); + rRecvData[0] = 0x03; + *rRecvSize = 3; + break; + case 0x84: + memset(rRecvData, 0, 4); + rRecvData[0] = 0x84; + rRecvData[2] = 1; + rRecvData[3] = 0x90; + *rRecvSize = 4; + break; + case 0x85: + memset(rRecvData, 0, 12); + rRecvData[0] = 0x85; + rRecvData[2] = 9; + memcpy(rRecvData + 3, (void *)"837-15345", 9); + *rRecvSize = 12; + break; + case 0x42: + memset(rRecvData, 0, 4); + rRecvData[0] = 0x42; + rRecvData[2] = 1; + rRecvData[3] = 0x91; + *rRecvSize = 4; + break; + default: + memset(rRecvData, 0, 3); + rRecvData[0] = sendData[0]; + *rRecvSize = 3; + break; + } + + // dprintf("Printer: C3XXusb: commCardRfidReader recv buffer: \n"); + // dump(rRecvData, *rRecvSize); + + return 1; +} + +int chcusb_updateCardRfidReader(uint8_t *data, uint32_t size, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_getErrorStatus(uint16_t *rBuffer) { + dprintf("Printer: C3XXusb: %s\n", __func__); + memset(rBuffer, 0, 0x80); + return 1; +} + +int chcusb_setCutList(uint8_t *rData, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_setLaminatePattern(uint16_t index, uint8_t *rData, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_color_adjustment(LPCSTR filename, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_color_adjustmentEx(int32_t a1, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_getEEPROM(uint8_t index, uint8_t *rData, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_setParameter(uint8_t a1, uint32_t a2, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_getParameter(uint8_t a1, uint8_t *a2, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +int chcusb_universal_command(int32_t a1, uint8_t a2, int32_t a3, uint8_t *a4, uint16_t *rResult) { + dprintf("Printer: C3XXusb: %s\n", __func__); + *rResult = 0; + return 1; +} + +// copy pasted from https://dev.s-ul.net/domeori/c310emu +#define BITMAPHEADERSIZE 0x36 + +DWORD ConvertDataToBitmap( + DWORD dwBitCount, + DWORD dwWidth, DWORD dwHeight, + PBYTE pbInput, DWORD cbInput, + PBYTE pbOutput, DWORD cbOutput, + PDWORD pcbResult, + bool pFlip) { + if (!pbInput || !pbOutput || dwBitCount < 8) return -3; + + if (cbInput < (dwWidth * dwHeight * dwBitCount / 8)) return -3; + + PBYTE pBuffer = (PBYTE)malloc(cbInput); + if (!pBuffer) return -2; + + BYTE dwColors = (BYTE)(dwBitCount / 8); + if (!dwColors) return -1; + + UINT16 cbColors; + RGBQUAD pbColors[256]; + + switch (dwBitCount) { + case 1: + cbColors = 1; + break; + case 2: + cbColors = 4; + break; + case 4: + cbColors = 16; + break; + case 8: + cbColors = 256; + break; + default: + cbColors = 0; + break; + } + + if (cbColors) { + BYTE dwStep = (BYTE)(256 / cbColors); + + for (UINT16 i = 0; i < cbColors; ++i) { + pbColors[i].rgbRed = dwStep * i; + pbColors[i].rgbGreen = dwStep * i; + pbColors[i].rgbBlue = dwStep * i; + pbColors[i].rgbReserved = 0; + } + } + + DWORD dwTable = cbColors * sizeof(RGBQUAD); + DWORD dwOffset = BITMAPHEADERSIZE + dwTable; + + // calculate the padded row size, again + DWORD dwLineSize = (dwWidth * dwBitCount / 8 + 3) & ~3; + + BITMAPFILEHEADER bFile = {0}; + BITMAPINFOHEADER bInfo = {0}; + + bFile.bfType = 0x4D42; // MAGIC + bFile.bfSize = dwOffset + cbInput; + bFile.bfOffBits = dwOffset; + + bInfo.biSize = sizeof(BITMAPINFOHEADER); + bInfo.biWidth = dwWidth; + bInfo.biHeight = dwHeight; + bInfo.biPlanes = 1; + bInfo.biBitCount = (WORD)dwBitCount; + bInfo.biCompression = BI_RGB; + bInfo.biSizeImage = cbInput; + + if (cbOutput < bFile.bfSize) return -1; + + // Flip the image (if necessary) and add padding to each row + if (pFlip) { + for (size_t i = 0; i < dwHeight; i++) { + for (size_t j = 0; j < dwWidth; j++) { + for (size_t k = 0; k < dwColors; k++) { + // Calculate the position in the padded buffer + // Make sure to also flip the colors from RGB to BRG + size_t x = (dwHeight - i - 1) * dwLineSize + (dwWidth - j - 1) * dwColors + (dwColors - k - 1); + size_t y = (dwHeight - i - 1) * dwWidth * dwColors + j * dwColors + k; + *(pBuffer + x) = *(pbInput + y); + } + } + } + } else { + for (size_t i = 0; i < dwHeight; i++) { + for (size_t j = 0; j < dwWidth; j++) { + for (size_t k = 0; k < dwColors; k++) { + // Calculate the position in the padded buffer + size_t x = i * dwLineSize + j * dwColors + (dwColors - k - 1); + size_t y = (dwHeight - i - 1) * dwWidth * dwColors + j * dwColors + k; + *(pBuffer + x) = *(pbInput + y); + } + } + } + } + + memcpy(pbOutput, &bFile, sizeof(BITMAPFILEHEADER)); + memcpy(pbOutput + sizeof(BITMAPFILEHEADER), &bInfo, sizeof(BITMAPINFOHEADER)); + if (cbColors) memcpy(pbOutput + BITMAPHEADERSIZE, pbColors, dwTable); + memcpy(pbOutput + dwOffset, pBuffer, cbInput); + + *pcbResult = bFile.bfSize; + + free(pBuffer); + return 0; +} + +DWORD WriteDataToBitmapFile( + LPCWSTR lpFilePath, DWORD dwBitCount, + DWORD dwWidth, DWORD dwHeight, + PBYTE pbInput, DWORD cbInput, + PBYTE pbMetadata, DWORD cbMetadata, + bool pFlip) { + if (!lpFilePath || !pbInput) return -3; + + HANDLE hFile; + DWORD dwBytesWritten; + + hFile = CreateFileW( + lpFilePath, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, + NULL); + if (hFile == INVALID_HANDLE_VALUE) return -1; + + // calculate the padded row size and padded image size + DWORD dwLineSize = (dwWidth * dwBitCount / 8 + 3) & ~3; + DWORD dwImageSize = dwLineSize * dwHeight; + + DWORD cbResult; + DWORD cbBuffer = dwImageSize + 0x500; + PBYTE pbBuffer = (PBYTE)calloc(cbBuffer, 1); + if (!pbBuffer) return -2; + + if (ConvertDataToBitmap(dwBitCount, dwWidth, dwHeight, pbInput, dwImageSize, pbBuffer, cbBuffer, &cbResult, pFlip) < 0) { + cbResult = -1; + goto WriteDataToBitmapFile_End; + } + + WriteFile(hFile, pbBuffer, cbResult, &dwBytesWritten, NULL); + + if (pbMetadata) + WriteFile(hFile, pbMetadata, cbMetadata, &dwBytesWritten, NULL); + + CloseHandle(hFile); + + cbResult = dwBytesWritten; + +WriteDataToBitmapFile_End: + free(pbBuffer); + return cbResult; +} + +DWORD WriteArrayToFile(LPCSTR lpOutputFilePath, LPVOID lpDataTemp, DWORD nDataSize, BOOL isAppend) { +#ifdef NDEBUG + + return nDataSize; + +#else + + HANDLE hFile; + DWORD dwBytesWritten; + DWORD dwDesiredAccess; + DWORD dwCreationDisposition; + + if (isAppend) { + dwDesiredAccess = FILE_APPEND_DATA; + dwCreationDisposition = OPEN_ALWAYS; + } else { + dwDesiredAccess = GENERIC_WRITE; + dwCreationDisposition = CREATE_ALWAYS; + } + + hFile = CreateFileA( + lpOutputFilePath, + dwDesiredAccess, + FILE_SHARE_READ, + NULL, + dwCreationDisposition, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, + NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return FALSE; + } + + WriteFile(hFile, lpDataTemp, nDataSize, &dwBytesWritten, NULL); + CloseHandle(hFile); + + return dwBytesWritten; + +#endif +} diff --git a/hooklib/printer.h b/hooklib/printer.h new file mode 100644 index 0000000..e83ef17 --- /dev/null +++ b/hooklib/printer.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +struct printer_config { + bool enable; + bool rotate_180; + char serial_no[8]; + wchar_t main_fw_path[MAX_PATH]; + wchar_t dsp_fw_path[MAX_PATH]; + wchar_t param_fw_path[MAX_PATH]; + wchar_t printer_out_path[MAX_PATH]; +}; + +void printer_hook_init(const struct printer_config *cfg, int rfid_port_no, HINSTANCE self); diff --git a/meson.build b/meson.build index 2032f0e..c4936be 100644 --- a/meson.build +++ b/meson.build @@ -65,6 +65,7 @@ subdir('mai2io') subdir('cmio') subdir('mercuryio') subdir('cxbio') +subdir('fgoio') subdir('chunihook') subdir('divahook') @@ -79,3 +80,4 @@ subdir('mai2hook') subdir('cmhook') subdir('mercuryhook') subdir('cxbhook') +subdir('fgohook')