mirror of
https://gitea.tendokyu.moe/TeamTofuShop/segatools.git
synced 2026-10-14 04:19:02 +03:00
refactor all common parts and games
This commit is contained in:
@@ -0,0 +1,353 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "aimeio/aimeio.h"
|
||||
|
||||
#include "util/crc.h"
|
||||
#include "util/dprintf.h"
|
||||
#include "util/env.h"
|
||||
|
||||
struct aime_io_config {
|
||||
wchar_t aime_path[MAX_PATH];
|
||||
wchar_t felica_path[MAX_PATH];
|
||||
bool felica_gen;
|
||||
bool aime_gen;
|
||||
uint8_t vk_scan;
|
||||
};
|
||||
|
||||
static struct aime_io_config aime_io_cfg;
|
||||
static uint8_t aime_io_aime_id[10];
|
||||
static uint8_t aime_io_felica_id[8];
|
||||
static bool aime_io_aime_id_present;
|
||||
static bool aime_io_felica_id_present;
|
||||
|
||||
static void aime_io_config_read(
|
||||
struct aime_io_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
static HRESULT aime_io_read_id_file(
|
||||
const wchar_t *path,
|
||||
uint8_t *bytes,
|
||||
size_t nbytes);
|
||||
|
||||
static HRESULT aime_io_generate_felica(
|
||||
const wchar_t *path,
|
||||
uint8_t *bytes,
|
||||
size_t nbytes);
|
||||
|
||||
static HRESULT aime_io_generate_aime(
|
||||
const wchar_t *path,
|
||||
uint8_t *bytes,
|
||||
size_t nbytes);
|
||||
|
||||
static void aime_io_config_read(
|
||||
struct aime_io_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"aime",
|
||||
L"aimePath",
|
||||
L"DEVICE\\aime.txt",
|
||||
cfg->aime_path,
|
||||
_countof(cfg->aime_path),
|
||||
filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"aime",
|
||||
L"felicaPath",
|
||||
L"DEVICE\\felica.txt",
|
||||
cfg->felica_path,
|
||||
_countof(cfg->felica_path),
|
||||
filename);
|
||||
|
||||
cfg->felica_gen = GetPrivateProfileIntW(
|
||||
L"aime",
|
||||
L"felicaGen",
|
||||
0,
|
||||
filename);
|
||||
|
||||
cfg->aime_gen = GetPrivateProfileIntW(
|
||||
L"aime",
|
||||
L"aimeGen",
|
||||
1,
|
||||
filename);
|
||||
|
||||
cfg->vk_scan = GetPrivateProfileIntW(
|
||||
L"aime",
|
||||
L"scan",
|
||||
VK_RETURN,
|
||||
filename);
|
||||
}
|
||||
|
||||
static HRESULT aime_io_read_id_file(
|
||||
const wchar_t *path,
|
||||
uint8_t *bytes,
|
||||
size_t nbytes)
|
||||
{
|
||||
HRESULT hr;
|
||||
FILE *f;
|
||||
size_t i;
|
||||
int byte;
|
||||
int r;
|
||||
|
||||
f = _wfopen(path, L"r");
|
||||
|
||||
if (f == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
memset(bytes, 0, nbytes);
|
||||
|
||||
for (i = 0 ; i < nbytes ; i++) {
|
||||
r = fscanf(f, "%02x ", &byte);
|
||||
|
||||
if (r != 1) {
|
||||
hr = E_FAIL;
|
||||
dprintf("AimeIO DLL: %S: fscanf[%i] failed: %i\n",
|
||||
path,
|
||||
(int) i,
|
||||
r);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
bytes[i] = byte;
|
||||
}
|
||||
|
||||
hr = S_OK;
|
||||
|
||||
end:
|
||||
if (f != NULL) {
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT aime_io_generate_felica(
|
||||
const wchar_t *path,
|
||||
uint8_t *bytes,
|
||||
size_t nbytes)
|
||||
{
|
||||
size_t i;
|
||||
FILE *f;
|
||||
|
||||
assert(path != NULL);
|
||||
assert(bytes != NULL);
|
||||
assert(nbytes > 0);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
for (i = 0; i < nbytes; i++) {
|
||||
bytes[i] = rand();
|
||||
}
|
||||
|
||||
/* FeliCa IDm values should have a 0 in their high nibble. I think. */
|
||||
bytes[0] &= 0x0F;
|
||||
|
||||
f = _wfopen(path, L"w");
|
||||
|
||||
if (f == NULL) {
|
||||
dprintf("AimeIO DLL: %S: fopen failed: %i\n", path, (int) errno);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nbytes; i++) {
|
||||
fprintf(f, "%02X", bytes[i]);
|
||||
}
|
||||
|
||||
fprintf(f, "\n");
|
||||
fclose(f);
|
||||
|
||||
dprintf("AimeIO DLL: Generated random FeliCa ID\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT aime_io_generate_aime(
|
||||
const wchar_t *path,
|
||||
uint8_t *bytes,
|
||||
size_t nbytes)
|
||||
{
|
||||
size_t i;
|
||||
FILE *f;
|
||||
|
||||
assert(path != NULL);
|
||||
assert(bytes != NULL);
|
||||
assert(nbytes > 0);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
/* AiMe IDs should not start with 3, due to a missing check for BananaPass IDs */
|
||||
do {
|
||||
for (i = 0; i < nbytes; i++) {
|
||||
bytes[i] = rand() % 10 << 4 | rand() % 10;
|
||||
}
|
||||
} while (bytes[0] >> 4 == 3);
|
||||
|
||||
f = _wfopen(path, L"w");
|
||||
|
||||
if (f == NULL) {
|
||||
dprintf("AimeIO DLL: %S: fopen failed: %i\n", path, (int) errno);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nbytes; i++) {
|
||||
fprintf(f, "%02x", bytes[i]);
|
||||
}
|
||||
|
||||
fprintf(f, "\n");
|
||||
fclose(f);
|
||||
|
||||
dprintf("AimeIO DLL: Generated random AiMe ID\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
uint16_t aime_io_get_api_version(void)
|
||||
{
|
||||
return 0x0100;
|
||||
}
|
||||
|
||||
HRESULT aime_io_init(void)
|
||||
{
|
||||
aime_io_config_read(&aime_io_cfg, get_config_path());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
||||
{
|
||||
bool sense;
|
||||
HRESULT hr;
|
||||
|
||||
if (unit_no != 0) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Reset presence flags */
|
||||
|
||||
aime_io_aime_id_present = false;
|
||||
aime_io_felica_id_present = false;
|
||||
|
||||
/* Don't do anything more if the scan key is not held */
|
||||
|
||||
sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000;
|
||||
|
||||
if (!sense) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Try AiMe IC */
|
||||
|
||||
hr = aime_io_read_id_file(
|
||||
aime_io_cfg.aime_path,
|
||||
aime_io_aime_id,
|
||||
sizeof(aime_io_aime_id));
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE) {
|
||||
aime_io_aime_id_present = true;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Try generating AiMe IC (if enabled) */
|
||||
|
||||
if (aime_io_cfg.aime_gen) {
|
||||
hr = aime_io_generate_aime(
|
||||
aime_io_cfg.aime_path,
|
||||
aime_io_aime_id,
|
||||
sizeof(aime_io_aime_id));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
aime_io_aime_id_present = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Try FeliCa IC */
|
||||
|
||||
hr = aime_io_read_id_file(
|
||||
aime_io_cfg.felica_path,
|
||||
aime_io_felica_id,
|
||||
sizeof(aime_io_felica_id));
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE) {
|
||||
aime_io_felica_id_present = true;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Try generating FeliCa IC (if enabled) */
|
||||
|
||||
if (aime_io_cfg.felica_gen) {
|
||||
hr = aime_io_generate_felica(
|
||||
aime_io_cfg.felica_path,
|
||||
aime_io_felica_id,
|
||||
sizeof(aime_io_felica_id));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
aime_io_felica_id_present = true;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_get_aime_id(
|
||||
uint8_t unit_no,
|
||||
uint8_t *luid,
|
||||
size_t luid_size)
|
||||
{
|
||||
assert(luid != NULL);
|
||||
assert(luid_size == sizeof(aime_io_aime_id));
|
||||
|
||||
if (unit_no != 0 || !aime_io_aime_id_present) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
memcpy(luid, aime_io_aime_id, luid_size);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm)
|
||||
{
|
||||
uint64_t val;
|
||||
size_t i;
|
||||
|
||||
assert(IDm != NULL);
|
||||
|
||||
if (unit_no != 0 || !aime_io_felica_id_present) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
val = 0;
|
||||
|
||||
for (i = 0 ; i < 8 ; i++) {
|
||||
val = (val << 8) | aime_io_felica_id[i];
|
||||
}
|
||||
|
||||
*IDm = val;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b)
|
||||
{}
|
||||
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
Get the version of the Aime 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 aime_io_get_api_version(void);
|
||||
|
||||
/*
|
||||
Initialize Aime IO provider DLL. Only called once, before any other
|
||||
functions exported from this DLL are called (except for
|
||||
aime_io_get_api_version).
|
||||
|
||||
Minimum API version: 0x0100
|
||||
*/
|
||||
HRESULT aime_io_init(void);
|
||||
|
||||
/*
|
||||
Poll for IC cards in the vicinity.
|
||||
|
||||
- unit_no: Always 0 as of the current API version
|
||||
|
||||
Minimum API version: 0x0100
|
||||
*/
|
||||
HRESULT aime_io_nfc_poll(uint8_t unit_no);
|
||||
|
||||
/*
|
||||
Attempt to read out a classic Aime card ID
|
||||
|
||||
- unit_no: Always 0 as of the current API version
|
||||
- luid: Pointer to a ten-byte buffer that will receive the ID
|
||||
- luid_size: Size of the buffer at *luid. Always 10.
|
||||
|
||||
Returns:
|
||||
|
||||
- S_OK if a classic Aime is present and was read successfully
|
||||
- S_FALSE if no classic Aime card is present (*luid will be ignored)
|
||||
- Any HRESULT error if an error occured.
|
||||
|
||||
Minimum API version: 0x0100
|
||||
*/
|
||||
HRESULT aime_io_nfc_get_aime_id(
|
||||
uint8_t unit_no,
|
||||
uint8_t *luid,
|
||||
size_t luid_size);
|
||||
|
||||
/*
|
||||
Attempt to read out a FeliCa card ID ("IDm"). The following are examples
|
||||
of FeliCa cards:
|
||||
|
||||
- Amuse IC (which includes new-style Aime-branded cards, among others)
|
||||
- Smartphones with FeliCa NFC capability (uncommon outside Japan)
|
||||
- Various Japanese e-cash cards and train passes
|
||||
|
||||
Parameters:
|
||||
|
||||
- unit_no: Always 0 as of the current API version
|
||||
- IDm: Output parameter that will receive the card ID
|
||||
|
||||
Returns:
|
||||
|
||||
- S_OK if a FeliCa device is present and was read successfully
|
||||
- S_FALSE if no FeliCa device is present (*IDm will be ignored)
|
||||
- Any HRESULT error if an error occured.
|
||||
|
||||
Minimum API version: 0x0100
|
||||
*/
|
||||
HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm);
|
||||
|
||||
/*
|
||||
Change the color and brightness of the card reader's RGB lighting
|
||||
|
||||
- unit_no: Always 0 as of the current API version
|
||||
- r, g, b: Primary color intensity, from 0 to 255 inclusive.
|
||||
|
||||
Minimum API version: 0x0100
|
||||
*/
|
||||
void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b);
|
||||
@@ -0,0 +1,12 @@
|
||||
aimeio_lib = static_library(
|
||||
'aimeio',
|
||||
name_prefix : '',
|
||||
include_directories: inc,
|
||||
implicit_include_directories : false,
|
||||
link_with : [
|
||||
util_lib,
|
||||
],
|
||||
sources : [
|
||||
'aimeio.c',
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "amex/amex.h"
|
||||
#include "amex/ds.h"
|
||||
#include "amex/eeprom.h"
|
||||
#include "amex/gpio.h"
|
||||
#include "amex/jvs.h"
|
||||
#include "amex/sram.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
HRESULT amex_hook_init(const struct amex_config *cfg, jvs_provider_t jvs)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
hr = ds_hook_init(&cfg->ds);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = eeprom_hook_init(&cfg->eeprom);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = gpio_hook_init(&cfg->gpio);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = jvs_hook_init(&cfg->jvs, jvs);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = sram_hook_init(&cfg->sram);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "amex/ds.h"
|
||||
#include "amex/eeprom.h"
|
||||
#include "amex/gpio.h"
|
||||
#include "amex/jvs.h"
|
||||
#include "amex/sram.h"
|
||||
|
||||
struct amex_config {
|
||||
struct ds_config ds;
|
||||
struct eeprom_config eeprom;
|
||||
struct gpio_config gpio;
|
||||
struct jvs_config jvs;
|
||||
struct sram_config sram;
|
||||
};
|
||||
|
||||
HRESULT amex_hook_init(
|
||||
const struct amex_config *cfg,
|
||||
jvs_provider_t jvs);
|
||||
@@ -0,0 +1,104 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "amex/amex.h"
|
||||
#include "amex/config.h"
|
||||
#include "amex/ds.h"
|
||||
#include "amex/eeprom.h"
|
||||
#include "amex/gpio.h"
|
||||
#include "amex/jvs.h"
|
||||
#include "amex/sram.h"
|
||||
|
||||
void ds_config_load(struct ds_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"ds", L"enable", 1, filename);
|
||||
cfg->region = GetPrivateProfileIntW(L"ds", L"region", 1, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"ds",
|
||||
L"serialNo",
|
||||
L"AAVE-01A99999999",
|
||||
cfg->serial_no,
|
||||
_countof(cfg->serial_no),
|
||||
filename);
|
||||
}
|
||||
|
||||
void eeprom_config_load(struct eeprom_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"eeprom", L"enable", 1, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"eeprom",
|
||||
L"path",
|
||||
L"DEVICE\\eeprom.bin",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
}
|
||||
|
||||
void gpio_config_load(struct gpio_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
wchar_t name[7];
|
||||
size_t i;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"gpio", L"enable", 1, filename);
|
||||
cfg->vk_sw1 = GetPrivateProfileIntW(L"gpio", L"sw1", VK_F1, filename);
|
||||
cfg->vk_sw2 = GetPrivateProfileIntW(L"gpio", L"sw2", VK_F2, filename);
|
||||
|
||||
wcscpy_s(name, _countof(name), L"dipsw0");
|
||||
|
||||
for (i = 0 ; i < 8 ; i++) {
|
||||
name[5] = L'1' + i;
|
||||
cfg->dipsw[i] = GetPrivateProfileIntW(L"gpio", name, 0, filename);
|
||||
}
|
||||
}
|
||||
|
||||
void jvs_config_load(struct jvs_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"jvs", L"enable", 1, filename);
|
||||
}
|
||||
|
||||
void sram_config_load(struct sram_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"sram", L"enable", 1, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"sram",
|
||||
L"path",
|
||||
L"DEVICE\\sram.bin",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
}
|
||||
|
||||
void amex_config_load(struct amex_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
ds_config_load(&cfg->ds, filename);
|
||||
eeprom_config_load(&cfg->eeprom, filename);
|
||||
gpio_config_load(&cfg->gpio, filename);
|
||||
jvs_config_load(&cfg->jvs, filename);
|
||||
sram_config_load(&cfg->sram, filename);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "amex/amex.h"
|
||||
#include "amex/ds.h"
|
||||
#include "amex/eeprom.h"
|
||||
#include "amex/gpio.h"
|
||||
#include "amex/jvs.h"
|
||||
#include "amex/sram.h"
|
||||
|
||||
void ds_config_load(struct ds_config *cfg, const wchar_t *filename);
|
||||
void eeprom_config_load(struct eeprom_config *cfg, const wchar_t *filename);
|
||||
void gpio_config_load(struct gpio_config *cfg, const wchar_t *filename);
|
||||
void jvs_config_load(struct jvs_config *cfg, const wchar_t *filename);
|
||||
void sram_config_load(struct sram_config *cfg, const wchar_t *filename);
|
||||
void amex_config_load(struct amex_config *cfg, const wchar_t *filename);
|
||||
@@ -0,0 +1,213 @@
|
||||
#include <windows.h>
|
||||
#include <devioctl.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "amex/ds.h"
|
||||
#include "amex/nvram.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/setupapi.h"
|
||||
|
||||
#include "util/crc.h"
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define DS_IOCTL_GET_ABI_VERSION CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define DS_IOCTL_SETUP CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define DS_IOCTL_READ_SECTOR CTL_CODE(0x8000, 0x804, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct ds_eeprom {
|
||||
uint32_t crc32;
|
||||
uint8_t unk_04[4];
|
||||
uint8_t region;
|
||||
char serial_no[17];
|
||||
uint8_t unk_1A[6];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct ds_eeprom) == 0x20, "DS EEPROM size");
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
static HRESULT ds_handle_irp(struct irp *irp);
|
||||
static HRESULT ds_handle_open(struct irp *irp);
|
||||
static HRESULT ds_handle_close(struct irp *irp);
|
||||
static HRESULT ds_handle_ioctl(struct irp *irp);
|
||||
|
||||
static HRESULT ds_ioctl_get_geometry(struct irp *irp);
|
||||
static HRESULT ds_ioctl_get_abi_version(struct irp *irp);
|
||||
static HRESULT ds_ioctl_setup(struct irp *irp);
|
||||
static HRESULT ds_ioctl_read_sector(struct irp *irp);
|
||||
|
||||
static struct ds_eeprom ds_eeprom;
|
||||
static HANDLE ds_fd;
|
||||
|
||||
HRESULT ds_hook_init(const struct ds_config *cfg)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
memset(&ds_eeprom, 0, sizeof(ds_eeprom));
|
||||
|
||||
wcstombs_s(
|
||||
NULL,
|
||||
ds_eeprom.serial_no,
|
||||
_countof(ds_eeprom.serial_no),
|
||||
cfg->serial_no,
|
||||
_countof(cfg->serial_no) - 1);
|
||||
|
||||
ds_eeprom.region = cfg->region;
|
||||
ds_eeprom.crc32 = crc32(&ds_eeprom.unk_04, 0x1C, 0);
|
||||
|
||||
hr = iohook_push_handler(ds_handle_irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = setupapi_add_phantom_dev(&ds_guid, L"$ds");
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = iohook_open_nul_fd(&ds_fd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ds_handle_irp(struct irp *irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != ds_fd) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return ds_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return ds_handle_close(irp);
|
||||
case IRP_OP_IOCTL: return ds_handle_ioctl(irp);
|
||||
default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT ds_handle_open(struct irp *irp)
|
||||
{
|
||||
if (!wstr_eq(irp->open_filename, L"$ds")) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("DS: Open device\n");
|
||||
irp->fd = ds_fd;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ds_handle_close(struct irp *irp)
|
||||
{
|
||||
dprintf("DS: Close device\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ds_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
||||
return ds_ioctl_get_geometry(irp);
|
||||
|
||||
case DS_IOCTL_GET_ABI_VERSION:
|
||||
return ds_ioctl_get_abi_version(irp);
|
||||
|
||||
case DS_IOCTL_SETUP:
|
||||
return ds_ioctl_setup(irp);
|
||||
|
||||
case DS_IOCTL_READ_SECTOR:
|
||||
return ds_ioctl_read_sector(irp);
|
||||
|
||||
default:
|
||||
dprintf("DS: Unknown ioctl %08x, write %i read %i\n",
|
||||
irp->ioctl,
|
||||
(int) irp->write.nbytes,
|
||||
(int) irp->read.nbytes);
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT ds_ioctl_get_geometry(struct irp *irp)
|
||||
{
|
||||
DISK_GEOMETRY out;
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("DS: Get geometry\n");
|
||||
|
||||
memset(&out, 0, sizeof(out));
|
||||
out.Cylinders.QuadPart = 1;
|
||||
out.MediaType = 0;
|
||||
out.TracksPerCylinder = 1;
|
||||
out.SectorsPerTrack = 2;
|
||||
out.BytesPerSector = 32;
|
||||
|
||||
hr = iobuf_write(&irp->read, &out, sizeof(out));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DS: Get geometry failed: %08x\n", (int) hr);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT ds_ioctl_get_abi_version(struct irp *irp)
|
||||
{
|
||||
return iobuf_write_le16(&irp->read, 256);
|
||||
}
|
||||
|
||||
static HRESULT ds_ioctl_setup(struct irp *irp)
|
||||
{
|
||||
dprintf("DS: Setup IOCTL\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ds_ioctl_read_sector(struct irp *irp)
|
||||
{
|
||||
struct const_iobuf src;
|
||||
uint32_t sector_no;
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read_le32(&irp->write, §or_no);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("DS: Read sector %08x\n", sector_no);
|
||||
|
||||
src.bytes = (const uint8_t *) &ds_eeprom;
|
||||
src.nbytes = sizeof(ds_eeprom);
|
||||
src.pos = 0;
|
||||
|
||||
iobuf_move(&irp->read, &src);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct ds_config {
|
||||
bool enable;
|
||||
uint8_t region;
|
||||
wchar_t serial_no[17];
|
||||
};
|
||||
|
||||
DEFINE_GUID(
|
||||
ds_guid,
|
||||
0x279A9F67,
|
||||
0x348F,
|
||||
0x41C9,
|
||||
0xA4, 0xC4, 0xDF, 0xDB, 0x8A, 0xE8, 0xE5, 0xE0);
|
||||
|
||||
HRESULT ds_hook_init(const struct ds_config *cfg);
|
||||
@@ -0,0 +1,192 @@
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <ntdef.h>
|
||||
#else
|
||||
#include <winnt.h>
|
||||
#endif
|
||||
#include <devioctl.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "amex/eeprom.h"
|
||||
#include "amex/nvram.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/setupapi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define EEPROM_IOCTL_GET_ABI_VERSION CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
static HRESULT eeprom_handle_irp(struct irp *irp);
|
||||
static HRESULT eeprom_handle_open(struct irp *irp);
|
||||
static HRESULT eeprom_handle_close(struct irp *irp);
|
||||
static HRESULT eeprom_handle_ioctl(struct irp *irp);
|
||||
static HRESULT eeprom_handle_read(struct irp *irp);
|
||||
static HRESULT eeprom_handle_write(struct irp *irp);
|
||||
|
||||
static HRESULT eeprom_ioctl_get_geometry(struct irp *irp);
|
||||
static HRESULT eeprom_ioctl_get_abi_version(struct irp *irp);
|
||||
|
||||
static struct eeprom_config eeprom_config;
|
||||
static HANDLE eeprom_file;
|
||||
|
||||
HRESULT eeprom_hook_init(const struct eeprom_config *cfg)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
memcpy(&eeprom_config, cfg, sizeof(*cfg));
|
||||
|
||||
hr = iohook_push_handler(eeprom_handle_irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = setupapi_add_phantom_dev(&eeprom_guid, L"$eeprom");
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT eeprom_handle_irp(struct irp *irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != eeprom_file) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return eeprom_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return eeprom_handle_close(irp);
|
||||
case IRP_OP_IOCTL: return eeprom_handle_ioctl(irp);
|
||||
case IRP_OP_READ: return eeprom_handle_read(irp);
|
||||
case IRP_OP_WRITE: return eeprom_handle_write(irp);
|
||||
default: return iohook_invoke_next(irp);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT eeprom_handle_open(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!wstr_eq(irp->open_filename, L"$eeprom") != 0) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
if (eeprom_file != NULL) {
|
||||
dprintf("EEPROM: Already open\n");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
|
||||
}
|
||||
|
||||
dprintf("EEPROM: Open device\n");
|
||||
hr = nvram_open_file(&eeprom_file, eeprom_config.path, 0x2000);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
irp->fd = eeprom_file;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT eeprom_handle_close(struct irp *irp)
|
||||
{
|
||||
dprintf("EEPROM: Close device\n");
|
||||
eeprom_file = NULL;
|
||||
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static HRESULT eeprom_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
||||
return eeprom_ioctl_get_geometry(irp);
|
||||
|
||||
case EEPROM_IOCTL_GET_ABI_VERSION:
|
||||
return eeprom_ioctl_get_abi_version(irp);
|
||||
|
||||
default:
|
||||
dprintf("EEPROM: Unknown ioctl %x, write %i read %i\n",
|
||||
irp->ioctl,
|
||||
(int) irp->write.nbytes,
|
||||
(int) irp->read.nbytes);
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT eeprom_ioctl_get_geometry(struct irp *irp)
|
||||
{
|
||||
DISK_GEOMETRY out;
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("EEPROM: Get geometry\n");
|
||||
|
||||
memset(&out, 0, sizeof(out));
|
||||
out.Cylinders.QuadPart = 1;
|
||||
out.MediaType = FixedMedia;
|
||||
out.TracksPerCylinder = 224;
|
||||
out.SectorsPerTrack = 32;
|
||||
out.BytesPerSector = 1;
|
||||
|
||||
hr = iobuf_write(&irp->read, &out, sizeof(out));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("EEPROM: Get geometry failed: %08x\n", (int) hr);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT eeprom_ioctl_get_abi_version(struct irp *irp)
|
||||
{
|
||||
return iobuf_write_le16(&irp->read, 256);
|
||||
}
|
||||
|
||||
static HRESULT eeprom_handle_read(struct irp *irp)
|
||||
{
|
||||
if (irp->ovl == NULL) {
|
||||
dprintf("EEPROM: Synchronous read..?\n");
|
||||
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
dprintf("EEPROM: Read off %x len %x\n",
|
||||
(int) irp->ovl->Offset,
|
||||
(int) irp->read.nbytes);
|
||||
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static HRESULT eeprom_handle_write(struct irp *irp)
|
||||
{
|
||||
if (irp->ovl == NULL) {
|
||||
dprintf("EEPROM: Synchronous write..?\n");
|
||||
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
dprintf("EEPROM: Write off %x len %x\n",
|
||||
(int) irp->ovl->Offset,
|
||||
(int) irp->write.nbytes);
|
||||
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct eeprom_config {
|
||||
bool enable;
|
||||
wchar_t path[MAX_PATH];
|
||||
};
|
||||
|
||||
DEFINE_GUID(
|
||||
eeprom_guid,
|
||||
0xB7970F0C,
|
||||
0x31C4,
|
||||
0x45FF,
|
||||
0x96, 0x18, 0x0A, 0x24, 0x00, 0x94, 0xB2, 0x71);
|
||||
|
||||
HRESULT eeprom_hook_init(const struct eeprom_config *cfg);
|
||||
@@ -0,0 +1,226 @@
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "amex/gpio.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/setupapi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define GPIO_IOCTL_SET_LEDS CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_WRITE_ACCESS)
|
||||
#define GPIO_IOCTL_GET_PSW CTL_CODE(0x8000, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define GPIO_IOCTL_GET_DIPSW CTL_CODE(0x8000, 0x803, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define GPIO_IOCTL_DESCRIBE CTL_CODE(0x8000, 0x805, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
enum {
|
||||
GPIO_TYPE_NONE = 0,
|
||||
GPIO_TYPE_LED = 1,
|
||||
GPIO_TYPE_DIPSW = 2,
|
||||
GPIO_TYPE_PSW = 3,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct gpio_port {
|
||||
uint8_t unknown;
|
||||
|
||||
/* Number of distinct instances of this thing..? */
|
||||
uint8_t count;
|
||||
|
||||
/* Type of GPIO port */
|
||||
uint16_t type;
|
||||
};
|
||||
|
||||
struct gpio_ports {
|
||||
uint8_t unknown; /* Maybe a count of valid items in the array idk */
|
||||
struct gpio_port ports[32];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
static HRESULT gpio_handle_irp(struct irp *irp);
|
||||
static HRESULT gpio_handle_open(struct irp *irp);
|
||||
static HRESULT gpio_handle_close(struct irp *irp);
|
||||
static HRESULT gpio_handle_ioctl(struct irp *irp);
|
||||
|
||||
static HRESULT gpio_ioctl_get_psw(struct irp *irp);
|
||||
static HRESULT gpio_ioctl_get_dipsw(struct irp *irp);
|
||||
static HRESULT gpio_ioctl_describe(struct irp *irp);
|
||||
static HRESULT gpio_ioctl_set_leds(struct irp *irp);
|
||||
|
||||
static const struct gpio_ports gpio_ports = {
|
||||
.ports = {
|
||||
{
|
||||
.type = GPIO_TYPE_LED,
|
||||
.count = 2,
|
||||
}, {
|
||||
.type = GPIO_TYPE_DIPSW,
|
||||
.count = 8,
|
||||
}, {
|
||||
.type = GPIO_TYPE_PSW,
|
||||
.count = 2,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static_assert(sizeof(gpio_ports) == 129, "GPIO port map size");
|
||||
|
||||
static HANDLE gpio_fd;
|
||||
static struct gpio_config gpio_config;
|
||||
|
||||
HRESULT gpio_hook_init(const struct gpio_config *cfg)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
memcpy(&gpio_config, cfg, sizeof(*cfg));
|
||||
|
||||
hr = iohook_open_nul_fd(&gpio_fd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = iohook_push_handler(gpio_handle_irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = setupapi_add_phantom_dev(&gpio_guid, L"$gpio");
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT gpio_handle_irp(struct irp *irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != gpio_fd) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return gpio_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return gpio_handle_close(irp);
|
||||
case IRP_OP_IOCTL: return gpio_handle_ioctl(irp);
|
||||
default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT gpio_handle_open(struct irp *irp)
|
||||
{
|
||||
if (!wstr_eq(irp->open_filename, L"$gpio")) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("GPIO: Open device\n");
|
||||
irp->fd = gpio_fd;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT gpio_handle_close(struct irp *irp)
|
||||
{
|
||||
dprintf("GPIO: Close device\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT gpio_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
switch (irp->ioctl) {
|
||||
case GPIO_IOCTL_SET_LEDS:
|
||||
return gpio_ioctl_set_leds(irp);
|
||||
|
||||
case GPIO_IOCTL_GET_PSW:
|
||||
return gpio_ioctl_get_psw(irp);
|
||||
|
||||
case GPIO_IOCTL_GET_DIPSW:
|
||||
return gpio_ioctl_get_dipsw(irp);
|
||||
|
||||
case GPIO_IOCTL_DESCRIBE:
|
||||
return gpio_ioctl_describe(irp);
|
||||
|
||||
default:
|
||||
dprintf("GPIO: Unknown ioctl %08x, write %i read %i\n",
|
||||
irp->ioctl,
|
||||
(int) irp->write.nbytes,
|
||||
(int) irp->read.nbytes);
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT gpio_ioctl_get_dipsw(struct irp *irp)
|
||||
{
|
||||
uint32_t dipsw;
|
||||
size_t i;
|
||||
|
||||
dipsw = 0;
|
||||
|
||||
for (i = 0 ; i < 8 ; i++) {
|
||||
if (gpio_config.dipsw[i]) {
|
||||
dipsw |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
//dprintf("GPIO: Get dipsw %08x\n", dipsw);
|
||||
|
||||
return iobuf_write_le32(&irp->read, dipsw);
|
||||
}
|
||||
|
||||
static HRESULT gpio_ioctl_get_psw(struct irp *irp)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
result = 0;
|
||||
|
||||
/* Bit 0 == SW1 == Alt. Test */
|
||||
/* Bit 1 == SW2 == Alt. Service */
|
||||
|
||||
if (GetAsyncKeyState(gpio_config.vk_sw1) & 0x8000) {
|
||||
result |= 1 << 0;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(gpio_config.vk_sw2) & 0x8000) {
|
||||
result |= 1 << 1;
|
||||
}
|
||||
|
||||
return iobuf_write_le32(&irp->read, result);
|
||||
}
|
||||
|
||||
static HRESULT gpio_ioctl_describe(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("GPIO: Describe GPIO ports\n");
|
||||
|
||||
hr = iobuf_write(&irp->read, &gpio_ports, sizeof(gpio_ports));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("GPIO: Describe GPIO ports failed: %08x\n", (int) hr);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT gpio_ioctl_set_leds(struct irp *irp)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct gpio_config {
|
||||
bool enable;
|
||||
uint8_t vk_sw1;
|
||||
uint8_t vk_sw2;
|
||||
bool dipsw[8];
|
||||
};
|
||||
|
||||
DEFINE_GUID(
|
||||
gpio_guid,
|
||||
0xE9A26688,
|
||||
0xF522,
|
||||
0x44FA,
|
||||
0xBF, 0xEE, 0x59, 0xDD, 0x16, 0x15, 0x56, 0x6C);
|
||||
|
||||
HRESULT gpio_hook_init(const struct gpio_config *cfg);
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <windows.h>
|
||||
#include <initguid.h>
|
||||
|
||||
#include "amex/ds.h"
|
||||
#include "amex/eeprom.h"
|
||||
#include "amex/gpio.h"
|
||||
#include "amex/jvs.h"
|
||||
#include "amex/sram.h"
|
||||
@@ -0,0 +1,217 @@
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
#include <winternl.h>
|
||||
|
||||
#include <ntstatus.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "amex/jvs.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/setupapi.h"
|
||||
|
||||
#include "jvs/jvs-bus.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define JVS_IOCTL_HELLO CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define JVS_IOCTL_TRANSACT CTL_CODE(0x8000, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define JVS_IOCTL_SENSE CTL_CODE(0x8000, 0x803, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
static HRESULT jvs_handle_irp(struct irp *irp);
|
||||
static HRESULT jvs_handle_open(struct irp *irp);
|
||||
static HRESULT jvs_handle_close(struct irp *irp);
|
||||
static HRESULT jvs_handle_ioctl(struct irp *irp);
|
||||
|
||||
static HRESULT jvs_ioctl_hello(struct irp *irp);
|
||||
static HRESULT jvs_ioctl_sense(struct irp *irp);
|
||||
static HRESULT jvs_ioctl_transact(struct irp *irp);
|
||||
|
||||
static HANDLE jvs_fd;
|
||||
static struct jvs_node *jvs_root;
|
||||
static jvs_provider_t jvs_provider;
|
||||
|
||||
HRESULT jvs_hook_init(const struct jvs_config *cfg, jvs_provider_t provider)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
hr = iohook_push_handler(jvs_handle_irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = setupapi_add_phantom_dev(&jvs_guid, L"$jvs");
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
jvs_provider = provider;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT jvs_handle_irp(struct irp *irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != jvs_fd) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return jvs_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return jvs_handle_close(irp);
|
||||
case IRP_OP_IOCTL: return jvs_handle_ioctl(irp);
|
||||
default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT jvs_handle_open(struct irp *irp)
|
||||
{
|
||||
struct jvs_node *root;
|
||||
HRESULT hr;
|
||||
|
||||
if (!wstr_eq(irp->open_filename, L"$jvs")) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
if (jvs_fd != NULL) {
|
||||
dprintf("JVS Port: Already open\n");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
|
||||
}
|
||||
|
||||
hr = iohook_open_nul_fd(&jvs_fd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("JVS Port: Open device\n");
|
||||
|
||||
if (jvs_provider != NULL) {
|
||||
hr = jvs_provider(&root);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
jvs_root = root;
|
||||
}
|
||||
}
|
||||
|
||||
irp->fd = jvs_fd;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT jvs_handle_close(struct irp *irp)
|
||||
{
|
||||
dprintf("JVS Port: Close device\n");
|
||||
jvs_fd = NULL;
|
||||
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static HRESULT jvs_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
switch (irp->ioctl) {
|
||||
case JVS_IOCTL_HELLO:
|
||||
return jvs_ioctl_hello(irp);
|
||||
|
||||
case JVS_IOCTL_SENSE:
|
||||
return jvs_ioctl_sense(irp);
|
||||
|
||||
case JVS_IOCTL_TRANSACT:
|
||||
return jvs_ioctl_transact(irp);
|
||||
|
||||
default:
|
||||
dprintf("JVS Port: Unknown ioctl %#x\n", irp->ioctl);
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT jvs_ioctl_hello(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
// uuh fucked if i know
|
||||
|
||||
dprintf("JVS Port: Port startup (?)\n");
|
||||
|
||||
iobuf_write_8(&irp->read, 0);
|
||||
hr = iobuf_write_8(&irp->read, 0);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT jvs_ioctl_sense(struct irp *irp)
|
||||
{
|
||||
uint8_t code;
|
||||
bool sense;
|
||||
|
||||
if (jvs_root != NULL) {
|
||||
sense = jvs_root->sense(jvs_root);
|
||||
|
||||
if (sense) {
|
||||
dprintf("JVS Port: Sense line 2.5 V (address unassigned)\n");
|
||||
code = 3;
|
||||
} else {
|
||||
dprintf("JVS Port: Sense line 0.0 V (address assigned)\n");
|
||||
code = 2;
|
||||
}
|
||||
} else {
|
||||
dprintf("JVS Port: Sense line 5.0 V (no downstream PCB)\n");
|
||||
code = 1;
|
||||
}
|
||||
|
||||
return iobuf_write_8(&irp->read, code);
|
||||
}
|
||||
|
||||
static HRESULT jvs_ioctl_transact(struct irp *irp)
|
||||
{
|
||||
#if defined(LOG_JVS)
|
||||
dprintf("\nJVS Port: Outbound frame:\n");
|
||||
dump_const_iobuf(&irp->write);
|
||||
#endif
|
||||
|
||||
jvs_bus_transact(jvs_root, irp->write.bytes, irp->write.nbytes, &irp->read);
|
||||
|
||||
#if defined(LOG_JVS)
|
||||
dprintf("JVS Port: Inbound frame:\n");
|
||||
dump_iobuf(&irp->read);
|
||||
dprintf("\n");
|
||||
#endif
|
||||
|
||||
if (irp->read.pos == 0) {
|
||||
/* The un-acked JVS reset command must return ERROR_NO_DATA_DETECTED,
|
||||
and this error must always be returned asynchronously. And since
|
||||
async I/O comes from the NT kernel, we have to return that win32
|
||||
error as the equivalent NTSTATUS. */
|
||||
|
||||
if (irp->ovl == NULL || irp->ovl->hEvent == NULL) {
|
||||
return HRESULT_FROM_WIN32(ERROR_NO_DATA_DETECTED);
|
||||
}
|
||||
|
||||
irp->ovl->Internal = STATUS_NO_DATA_DETECTED;
|
||||
SetEvent(irp->ovl->hEvent);
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_IO_PENDING);
|
||||
} else {
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "jvs/jvs-bus.h"
|
||||
|
||||
DEFINE_GUID(
|
||||
jvs_guid,
|
||||
0xDB6BBB45,
|
||||
0xCC96,
|
||||
0x4288,
|
||||
0xAA, 0x00, 0x6C, 0x00, 0xD7, 0x67, 0xBD, 0xBF);
|
||||
|
||||
struct jvs_config {
|
||||
bool enable;
|
||||
};
|
||||
|
||||
typedef HRESULT (*jvs_provider_t)(struct jvs_node **root);
|
||||
|
||||
HRESULT jvs_hook_init(const struct jvs_config *cfg, jvs_provider_t provider);
|
||||
@@ -0,0 +1,27 @@
|
||||
amex_lib = static_library(
|
||||
'amex',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
],
|
||||
sources : [
|
||||
'amex.c',
|
||||
'amex.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'ds.c',
|
||||
'ds.h',
|
||||
'eeprom.c',
|
||||
'eeprom.h',
|
||||
'gpio.c',
|
||||
'gpio.h',
|
||||
'guid.c',
|
||||
'jvs.c',
|
||||
'jvs.h',
|
||||
'nvram.c',
|
||||
'nvram.h',
|
||||
'sram.c',
|
||||
'sram.h',
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "amex/nvram.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
HRESULT nvram_open_file(HANDLE *out, const wchar_t *path, size_t size)
|
||||
{
|
||||
LARGE_INTEGER cur_size;
|
||||
LARGE_INTEGER pos;
|
||||
HANDLE file;
|
||||
HRESULT hr;
|
||||
BOOL ok;
|
||||
|
||||
assert(out != NULL);
|
||||
assert(path != NULL);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
file = CreateFileW(
|
||||
path,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("%S: Error opening backing store: %x\n", path, (int) hr);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
ok = GetFileSizeEx(file, &cur_size);
|
||||
|
||||
if (!ok) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("%S: GetFileSizeEx failed: %x\n", path, (int) hr);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (cur_size.QuadPart != (uint64_t) size) {
|
||||
pos.QuadPart = (uint64_t) size;
|
||||
ok = SetFilePointerEx(file, pos, NULL, FILE_BEGIN);
|
||||
|
||||
if (!ok) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("%S: SetFilePointerEx failed: %x\n", path, (int) hr);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
ok = SetEndOfFile(file);
|
||||
|
||||
if (!ok) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("%S: SetEndOfFile failed: %x\n", path, (int) hr);
|
||||
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
*out = file;
|
||||
file = INVALID_HANDLE_VALUE;
|
||||
|
||||
hr = S_OK;
|
||||
|
||||
end:
|
||||
if (file != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(file);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
HRESULT nvram_open_file(HANDLE *out, const wchar_t *path, size_t size);
|
||||
@@ -0,0 +1,158 @@
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <ntdef.h>
|
||||
#else
|
||||
#include <winnt.h>
|
||||
#endif
|
||||
#include <devioctl.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "amex/sram.h"
|
||||
#include "amex/nvram.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/setupapi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define SRAM_IOCTL_GET_ABI_VERSION CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
|
||||
static HRESULT sram_handle_irp(struct irp *irp);
|
||||
static HRESULT sram_handle_open(struct irp *irp);
|
||||
static HRESULT sram_handle_close(struct irp *irp);
|
||||
static HRESULT sram_handle_ioctl(struct irp *irp);
|
||||
|
||||
static HRESULT sram_ioctl_get_geometry(struct irp *irp);
|
||||
static HRESULT sram_ioctl_get_abi_version(struct irp *irp);
|
||||
|
||||
static struct sram_config sram_config;
|
||||
static HANDLE sram_file;
|
||||
|
||||
HRESULT sram_hook_init(const struct sram_config *cfg)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
memcpy(&sram_config, cfg, sizeof(*cfg));
|
||||
|
||||
hr = iohook_push_handler(sram_handle_irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = setupapi_add_phantom_dev(&sram_guid, L"$sram");
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sram_handle_irp(struct irp *irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != sram_file) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return sram_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return sram_handle_close(irp);
|
||||
case IRP_OP_IOCTL: return sram_handle_ioctl(irp);
|
||||
default: return iohook_invoke_next(irp);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT sram_handle_open(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!wstr_eq(irp->open_filename, L"$sram")) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
if (sram_file != NULL) {
|
||||
dprintf("SRAM: Already open\n");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
|
||||
}
|
||||
|
||||
dprintf("SRAM: Open device\n");
|
||||
hr = nvram_open_file(&sram_file, sram_config.path, 0x80000);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
irp->fd = sram_file;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sram_handle_close(struct irp *irp)
|
||||
{
|
||||
dprintf("SRAM: Close device\n");
|
||||
sram_file = NULL;
|
||||
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static HRESULT sram_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
||||
return sram_ioctl_get_geometry(irp);
|
||||
|
||||
case SRAM_IOCTL_GET_ABI_VERSION:
|
||||
return sram_ioctl_get_abi_version(irp);
|
||||
|
||||
default:
|
||||
dprintf("SRAM: Unknown ioctl %x, write %i read %i\n",
|
||||
irp->ioctl,
|
||||
(int) irp->write.nbytes,
|
||||
(int) irp->read.nbytes);
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT sram_ioctl_get_geometry(struct irp *irp)
|
||||
{
|
||||
DISK_GEOMETRY out;
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("SRAM: Get geometry\n");
|
||||
|
||||
memset(&out, 0, sizeof(out));
|
||||
out.Cylinders.QuadPart = 0x20000;
|
||||
out.MediaType = 0;
|
||||
out.TracksPerCylinder = 1;
|
||||
out.SectorsPerTrack = 1;
|
||||
out.BytesPerSector = 4;
|
||||
|
||||
hr = iobuf_write(&irp->read, &out, sizeof(out));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("SRAM: Get geometry failed: %08x\n", (int) hr);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT sram_ioctl_get_abi_version(struct irp *irp)
|
||||
{
|
||||
return iobuf_write_le16(&irp->read, 256);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct sram_config {
|
||||
bool enable;
|
||||
wchar_t path[MAX_PATH];
|
||||
};
|
||||
|
||||
DEFINE_GUID(
|
||||
sram_guid,
|
||||
0x741B5FCA,
|
||||
0x4635,
|
||||
0x4443,
|
||||
0xA7, 0xA0, 0x57, 0xCA, 0x7B, 0x50, 0x6A, 0x49);
|
||||
|
||||
HRESULT sram_hook_init(const struct sram_config *cfg);
|
||||
@@ -0,0 +1,112 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/aime-dll.h"
|
||||
|
||||
#include "util/dll-bind.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
const struct dll_bind_sym aime_dll_syms[] = {
|
||||
{
|
||||
.sym = "aime_io_init",
|
||||
.off = offsetof(struct aime_dll, init),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_poll",
|
||||
.off = offsetof(struct aime_dll, nfc_poll),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_get_aime_id",
|
||||
.off = offsetof(struct aime_dll, nfc_get_aime_id),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_get_felica_id",
|
||||
.off = offsetof(struct aime_dll, nfc_get_felica_id),
|
||||
}, {
|
||||
.sym = "aime_io_led_set_color",
|
||||
.off = offsetof(struct aime_dll, led_set_color),
|
||||
}
|
||||
};
|
||||
|
||||
struct aime_dll aime_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 aime_dll_init(const struct aime_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("NFC Assembly: Failed to load IO DLL: %lx: %S\n",
|
||||
hr,
|
||||
cfg->path);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
dprintf("NFC Assembly: Using custom IO DLL: %S\n", cfg->path);
|
||||
src = owned;
|
||||
} else {
|
||||
owned = NULL;
|
||||
src = self;
|
||||
}
|
||||
|
||||
get_api_version = (void *) GetProcAddress(src, "aime_io_get_api_version");
|
||||
|
||||
if (get_api_version != NULL) {
|
||||
aime_dll.api_version = get_api_version();
|
||||
} else {
|
||||
aime_dll.api_version = 0x0100;
|
||||
dprintf("Custom IO DLL does not expose aime_io_get_api_version, "
|
||||
"assuming API version 1.0.\n"
|
||||
"Please ask the developer to update their DLL.\n");
|
||||
}
|
||||
|
||||
if (aime_dll.api_version >= 0x0200) {
|
||||
hr = E_NOTIMPL;
|
||||
dprintf("NFC Assembly: Custom IO DLL implements an unsupported "
|
||||
"API version (%#04x). Please update Segatools.\n",
|
||||
aime_dll.api_version);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
sym = aime_dll_syms;
|
||||
hr = dll_bind(&aime_dll, src, &sym, _countof(aime_dll_syms));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (src != self) {
|
||||
dprintf("NFC Assembly: 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;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "aimeio/aimeio.h"
|
||||
|
||||
struct aime_dll {
|
||||
uint16_t api_version;
|
||||
HRESULT (*init)(void);
|
||||
HRESULT (*nfc_poll)(uint8_t unit_no);
|
||||
HRESULT (*nfc_get_aime_id)(
|
||||
uint8_t unit_no,
|
||||
uint8_t *luid,
|
||||
size_t luid_size);
|
||||
HRESULT (*nfc_get_felica_id)(uint8_t unit_no, uint64_t *IDm);
|
||||
void (*led_set_color)(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b);
|
||||
};
|
||||
|
||||
struct aime_dll_config {
|
||||
wchar_t path[MAX_PATH];
|
||||
bool path64;
|
||||
};
|
||||
|
||||
extern struct aime_dll aime_dll;
|
||||
|
||||
HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self);
|
||||
@@ -0,0 +1,113 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/aime-dll.h"
|
||||
#include "board/config.h"
|
||||
#include "board/sg-reader.h"
|
||||
#include "board/vfd.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
// Check windows
|
||||
#if _WIN32 || _WIN64
|
||||
#if _WIN64
|
||||
#define ENV64BIT
|
||||
#else
|
||||
#define ENV32BIT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Check GCC
|
||||
#if __GNUC__
|
||||
#if __x86_64__ || __ppc64__
|
||||
#define ENV64BIT
|
||||
#else
|
||||
#define ENV32BIT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
// Workaround for x64/x86 external IO dlls
|
||||
// path32 for 32bit, path64 for 64bit
|
||||
// for else.. is that possible? idk
|
||||
|
||||
if (cfg->path64) {
|
||||
#if defined(ENV32BIT)
|
||||
// Always empty, due to amdaemon being 64 bit in 32 bit mode
|
||||
memset(cfg->path, 0, sizeof(cfg->path));
|
||||
#elif defined(ENV64BIT)
|
||||
GetPrivateProfileStringW(
|
||||
L"aimeio",
|
||||
L"path",
|
||||
L"",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
#else
|
||||
#error "Unknown environment"
|
||||
#endif
|
||||
} else {
|
||||
GetPrivateProfileStringW(
|
||||
L"aimeio",
|
||||
L"path",
|
||||
L"",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
}
|
||||
}
|
||||
|
||||
void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
aime_dll_config_load(&cfg->dll, filename);
|
||||
cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"aime", L"portNo", 0, filename);
|
||||
cfg->high_baudrate = GetPrivateProfileIntW(L"aime", L"highBaud", 1, filename);
|
||||
cfg->gen = GetPrivateProfileIntW(L"aime", L"gen", 0, filename);
|
||||
cfg->proxy_flag = GetPrivateProfileIntW(L"aime", L"proxyFlag", 2, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"aime",
|
||||
L"authdataPath",
|
||||
L"DEVICE\\authdata.bin",
|
||||
cfg->authdata_path,
|
||||
_countof(cfg->authdata_path),
|
||||
filename);
|
||||
}
|
||||
|
||||
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"io4", L"enable", 1, filename);
|
||||
}
|
||||
|
||||
void vfd_config_load(struct vfd_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"vfd", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"vfd", L"portNo", 0, filename);
|
||||
cfg->utf_conversion = GetPrivateProfileIntW(L"vfd", L"utfConversion", 0, filename);
|
||||
}
|
||||
|
||||
void ffb_config_load(struct ffb_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"ffb", L"enable", 1, filename);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
#include "board/sg-reader.h"
|
||||
#include "board/vfd.h"
|
||||
#include "board/ffb.h"
|
||||
|
||||
void aime_config_load(struct aime_config *cfg, const wchar_t *filename);
|
||||
void io4_config_load(struct io4_config *cfg, const wchar_t *filename);
|
||||
void vfd_config_load(struct vfd_config *cfg, const wchar_t *filename);
|
||||
void ffb_config_load(struct ffb_config *cfg, const wchar_t *filename);
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
Force Feedback Board (FFB)
|
||||
|
||||
This board is used by many SEGA games to provide force feedback to the player.
|
||||
It is driven by the game software over a serial connection and is used by many
|
||||
games such as SEGA World Drivers Championship, Initial D Arcade, ...
|
||||
|
||||
Part number in schematics is "838-15069 MOTOR DRIVE BD RS232/422 Board".
|
||||
|
||||
Some observations:
|
||||
The maximal strength for any effect is 127, except Damper which maxes out at 40.
|
||||
The period for rumble effects is in the range 0-40.
|
||||
*/
|
||||
|
||||
#include "board/ffb.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
#include "hooklib/uart.h"
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
|
||||
// request format:
|
||||
// 0x?? - sync + command
|
||||
// 0x?? - direction/additional command
|
||||
// 0x?? - strength
|
||||
// 0x?? - checksum (sum of everything except the sync byte)
|
||||
|
||||
enum {
|
||||
FFB_CMD_TOGGLE = 0x80,
|
||||
FFB_CMD_CONSTANT_FORCE = 0x84,
|
||||
FFB_CMD_RUMBLE = 0x85,
|
||||
FFB_CMD_DAMPER = 0x86,
|
||||
};
|
||||
|
||||
struct ffb_hdr {
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
union ffb_req_any {
|
||||
struct ffb_hdr hdr;
|
||||
uint8_t bytes[3];
|
||||
};
|
||||
|
||||
static HRESULT ffb_handle_irp(struct irp *irp);
|
||||
|
||||
static HRESULT ffb_req_dispatch(const union ffb_req_any *req);
|
||||
static HRESULT ffb_req_toggle(const uint8_t *bytes);
|
||||
static HRESULT ffb_req_constant_force(const uint8_t *bytes);
|
||||
static HRESULT ffb_req_rumble(const uint8_t *bytes);
|
||||
static HRESULT ffb_req_damper(const uint8_t *bytes);
|
||||
|
||||
static const struct ffb_ops *ffb_ops;
|
||||
static struct uart ffb_uart;
|
||||
|
||||
static bool ffb_started;
|
||||
static HRESULT ffb_start_hr;
|
||||
static uint8_t ffb_written[4];
|
||||
static uint8_t ffb_readable[4];
|
||||
|
||||
/* Static variables to store maximum strength values */
|
||||
static uint8_t max_constant_force = 0;
|
||||
static uint8_t max_rumble = 0;
|
||||
static uint8_t max_period = 0;
|
||||
static uint8_t max_damper = 0;
|
||||
|
||||
HRESULT ffb_hook_init(
|
||||
const struct ffb_config *cfg,
|
||||
const struct ffb_ops *ops,
|
||||
unsigned int port_no)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(ops != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
ffb_ops = ops;
|
||||
|
||||
uart_init(&ffb_uart, port_no);
|
||||
ffb_uart.written.bytes = ffb_written;
|
||||
ffb_uart.written.nbytes = sizeof(ffb_written);
|
||||
ffb_uart.readable.bytes = ffb_readable;
|
||||
ffb_uart.readable.nbytes = sizeof(ffb_readable);
|
||||
|
||||
dprintf("FFB: hook enabled.\n");
|
||||
|
||||
return iohook_push_handler(ffb_handle_irp);
|
||||
}
|
||||
|
||||
static HRESULT ffb_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
if (!uart_match_irp(&ffb_uart, irp)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(&ffb_uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
assert(&ffb_uart.written != NULL);
|
||||
assert(ffb_uart.written.bytes != NULL || ffb_uart.written.nbytes == 0);
|
||||
assert(ffb_uart.written.pos <= ffb_uart.written.nbytes);
|
||||
|
||||
// dprintf("FFB TX:\n");
|
||||
|
||||
hr = ffb_req_dispatch((const union ffb_req_any *) ffb_uart.written.bytes);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("FFB: Processing error: %x\n", (int)hr);
|
||||
}
|
||||
|
||||
// dump_iobuf(&ffb_uart.written);
|
||||
ffb_uart.written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_dispatch(const union ffb_req_any *req)
|
||||
{
|
||||
switch (req->hdr.cmd) {
|
||||
case FFB_CMD_TOGGLE:
|
||||
return ffb_req_toggle(req->bytes);
|
||||
case FFB_CMD_CONSTANT_FORCE:
|
||||
return ffb_req_constant_force(req->bytes);
|
||||
case FFB_CMD_RUMBLE:
|
||||
return ffb_req_rumble(req->bytes);
|
||||
case FFB_CMD_DAMPER:
|
||||
return ffb_req_damper(req->bytes);
|
||||
|
||||
/* There are some test mode specfic commands which doesn't seem to be used in
|
||||
game at all. The same is true for the initialization phase. */
|
||||
|
||||
default:
|
||||
dprintf("FFB: Unhandled command %02x\n", req->hdr.cmd);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_toggle(const uint8_t *bytes)
|
||||
{
|
||||
uint8_t activate = bytes[2];
|
||||
|
||||
if (activate == 0x01) {
|
||||
dprintf("FFB: Activated\n");
|
||||
} else {
|
||||
dprintf("FFB: Deactivated\n");
|
||||
}
|
||||
|
||||
if (ffb_ops->toggle != NULL) {
|
||||
ffb_ops->toggle(activate == 0x01);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_constant_force(const uint8_t *bytes)
|
||||
{
|
||||
// dprintf("FFB: Constant force\n");
|
||||
|
||||
uint8_t direction = bytes[1];
|
||||
uint8_t force = bytes[2];
|
||||
|
||||
if (direction == 0x0) {
|
||||
// Right
|
||||
force = 128 - force;
|
||||
}
|
||||
|
||||
// Update max strength if the current force is greater
|
||||
if (force > max_constant_force) {
|
||||
max_constant_force = force;
|
||||
}
|
||||
|
||||
// dprintf("FFB: Constant Force Strength: %d (Max: %d)\n", force, max_constant_force);
|
||||
if (ffb_ops->constant_force != NULL) {
|
||||
ffb_ops->constant_force(direction, force);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_rumble(const uint8_t *bytes)
|
||||
{
|
||||
// dprintf("FFB: Rumble\n");
|
||||
|
||||
uint8_t force = bytes[1];
|
||||
uint8_t period = bytes[2];
|
||||
|
||||
// Update max strength if the current force is greater
|
||||
if (force > max_rumble) {
|
||||
max_rumble = force;
|
||||
}
|
||||
|
||||
if (period > max_period) {
|
||||
max_period = period;
|
||||
}
|
||||
|
||||
// dprintf("FFB: Rumble Period: %d (Max %d), Strength: %d (Max: %d)\n", period, max_period, force, max_rumble);
|
||||
if (ffb_ops->rumble != NULL) {
|
||||
ffb_ops->rumble(force, period);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT ffb_req_damper(const uint8_t *bytes)
|
||||
{
|
||||
// dprintf("FFB: Damper\n");
|
||||
|
||||
uint8_t force = bytes[2];
|
||||
|
||||
// Update max strength if the current force is greater
|
||||
if (force > max_damper) {
|
||||
max_damper = force;
|
||||
}
|
||||
|
||||
// dprintf("FFB: Damper Strength: %d (Max: %d)\n", force, max_damper);
|
||||
if (ffb_ops->damper != NULL) {
|
||||
ffb_ops->damper(force);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct ffb_config {
|
||||
bool enable;
|
||||
};
|
||||
|
||||
struct ffb_ops {
|
||||
void (*toggle)(bool active);
|
||||
void (*constant_force)(uint8_t direction, uint8_t force);
|
||||
void (*rumble)(uint8_t force, uint8_t period);
|
||||
void (*damper)(uint8_t force);
|
||||
};
|
||||
|
||||
HRESULT ffb_hook_init(
|
||||
const struct ffb_config *cfg,
|
||||
const struct ffb_ops *ops,
|
||||
unsigned int port_no);
|
||||
@@ -0,0 +1,3 @@
|
||||
#include <initguid.h>
|
||||
|
||||
#include "board/guid.h"
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
DEFINE_GUID(
|
||||
hid_guid,
|
||||
0x4D1E55B2L,
|
||||
0xF16F,
|
||||
0x11CF,
|
||||
0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
|
||||
@@ -0,0 +1,713 @@
|
||||
/*
|
||||
Sega "Type 3" JVS I/O emulator
|
||||
|
||||
Credits:
|
||||
|
||||
Protocol docs:
|
||||
https://github.com/TheOnlyJoey/openjvs/wiki/ (a/o October 2018)
|
||||
|
||||
Capability dumps:
|
||||
https://wiki.arcadeotaku.com/w/JVS#Sega_837-14572 (a/o October 2018)
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/io3.h"
|
||||
|
||||
#include "jvs/jvs-bus.h"
|
||||
#include "jvs/jvs-cmd.h"
|
||||
#include "jvs/jvs-util.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
static void io3_transact(
|
||||
struct jvs_node *node,
|
||||
const void *bytes,
|
||||
size_t nbytes,
|
||||
struct iobuf *resp);
|
||||
|
||||
static bool io3_sense(struct jvs_node *node);
|
||||
|
||||
static HRESULT io3_cmd(
|
||||
void *ctx,
|
||||
struct const_iobuf *req,
|
||||
struct iobuf *resp);
|
||||
|
||||
static HRESULT io3_cmd_read_id(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_get_cmd_version(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_get_jvs_version(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_get_comm_version(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_get_features(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_read_switches(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_read_coin(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_read_analogs(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_read_rotarys(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_write_gpio(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static HRESULT io3_cmd_reset(struct io3 *io3, struct const_iobuf *buf);
|
||||
|
||||
static HRESULT io3_cmd_assign_addr(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf);
|
||||
|
||||
static const uint8_t io3_ident[] =
|
||||
"SEGA CORPORATION;I/O BD JVS;837-14572;Ver1.00;2005/10";
|
||||
|
||||
static uint8_t io3_features[] = {
|
||||
/* Feature : 0x01 : Players and switches
|
||||
Param1 : 2 : Number of players
|
||||
Param2 : 14 : Number of switches per player
|
||||
Param3 : 0 : N/A */
|
||||
|
||||
0x01, 2, 14, 0,
|
||||
|
||||
/* Feature : 0x02 : Coin slots
|
||||
Param1 : 2 : Number of coin slots
|
||||
Param2 : 0 : N/A
|
||||
Param3 : 0 : N/A */
|
||||
|
||||
0x02, 2, 0, 0,
|
||||
|
||||
/* Feature : 0x03 : Analog inputs
|
||||
Param1 : 8 : Number of ADC channels
|
||||
Param2 : 10 : Effective bits of resolution per ADC
|
||||
Param3 : 0 : N/A */
|
||||
|
||||
0x03, 8, 10, 0,
|
||||
|
||||
/* Feature : 0x04 : Rotary inputs
|
||||
Param1 : 4 : Number of rotary channels
|
||||
Param2 : 0 : N/A
|
||||
Param3 : 0 : N/A */
|
||||
|
||||
0x04, 4, 0, 0,
|
||||
|
||||
/* Feature : 0x12 : GPIO outputs
|
||||
Param1 : 3 : Number of ports (8 bits per port)
|
||||
Param2 : 0 : N/A
|
||||
Param3 : 0 : N/A
|
||||
|
||||
NOTE: This particular port count is what an IO-4 attached over JVS
|
||||
advertises, an IO-3 only advertises 3. Still, this seems to be backwards
|
||||
compatible with games that expect an IO-3, and the protocols seem to be
|
||||
identical otherwise. */
|
||||
|
||||
0x12, 20, 0, 0,
|
||||
|
||||
/* Feature : 0x00 : End of capabilities */
|
||||
|
||||
0x00,
|
||||
};
|
||||
|
||||
void io3_init(
|
||||
struct io3 *io3,
|
||||
struct jvs_node *next,
|
||||
const struct io3_ops *ops,
|
||||
void *ops_ctx)
|
||||
{
|
||||
assert(io3 != NULL);
|
||||
assert(ops != NULL);
|
||||
|
||||
io3->jvs.next = next;
|
||||
io3->jvs.transact = io3_transact;
|
||||
io3->jvs.sense = io3_sense;
|
||||
io3->addr = 0xFF;
|
||||
io3->ops = ops;
|
||||
io3->ops_ctx = ops_ctx;
|
||||
}
|
||||
|
||||
struct jvs_node *io3_to_jvs_node(struct io3 *io3)
|
||||
{
|
||||
assert(io3 != NULL);
|
||||
|
||||
return &io3->jvs;
|
||||
}
|
||||
|
||||
static void io3_transact(
|
||||
struct jvs_node *node,
|
||||
const void *bytes,
|
||||
size_t nbytes,
|
||||
struct iobuf *resp)
|
||||
{
|
||||
struct io3 *io3;
|
||||
|
||||
assert(node != NULL);
|
||||
assert(bytes != NULL);
|
||||
assert(resp != NULL);
|
||||
|
||||
io3 = CONTAINING_RECORD(node, struct io3, jvs);
|
||||
|
||||
jvs_crack_request(bytes, nbytes, resp, io3->addr, io3_cmd, io3);
|
||||
}
|
||||
|
||||
static bool io3_sense(struct jvs_node *node)
|
||||
{
|
||||
struct io3 *io3;
|
||||
|
||||
assert(node != NULL);
|
||||
|
||||
io3 = CONTAINING_RECORD(node, struct io3, jvs);
|
||||
|
||||
return io3->addr == 0xFF;
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd(
|
||||
void *ctx,
|
||||
struct const_iobuf *req,
|
||||
struct iobuf *resp)
|
||||
{
|
||||
struct io3 *io3;
|
||||
|
||||
io3 = ctx;
|
||||
|
||||
switch (req->bytes[req->pos]) {
|
||||
case JVS_CMD_READ_ID:
|
||||
return io3_cmd_read_id(io3, req, resp);
|
||||
|
||||
case JVS_CMD_GET_CMD_VERSION:
|
||||
return io3_cmd_get_cmd_version(io3, req, resp);
|
||||
|
||||
case JVS_CMD_GET_JVS_VERSION:
|
||||
return io3_cmd_get_jvs_version(io3, req, resp);
|
||||
|
||||
case JVS_CMD_GET_COMM_VERSION:
|
||||
return io3_cmd_get_comm_version(io3, req, resp);
|
||||
|
||||
case JVS_CMD_GET_FEATURES:
|
||||
return io3_cmd_get_features(io3, req, resp);
|
||||
|
||||
case JVS_CMD_READ_SWITCHES:
|
||||
return io3_cmd_read_switches(io3, req, resp);
|
||||
|
||||
case JVS_CMD_READ_COIN:
|
||||
return io3_cmd_read_coin(io3, req, resp);
|
||||
|
||||
case JVS_CMD_READ_ANALOGS:
|
||||
return io3_cmd_read_analogs(io3, req, resp);
|
||||
|
||||
case JVS_CMD_READ_ROTARYS:
|
||||
return io3_cmd_read_rotarys(io3, req, resp);
|
||||
|
||||
case JVS_CMD_WRITE_GPIO:
|
||||
return io3_cmd_write_gpio(io3, req, resp);
|
||||
|
||||
case JVS_CMD_RESET:
|
||||
return io3_cmd_reset(io3, req);
|
||||
|
||||
case JVS_CMD_ASSIGN_ADDR:
|
||||
return io3_cmd_assign_addr(io3, req, resp);
|
||||
|
||||
default:
|
||||
dprintf("JVS I/O: Node %02x: Unhandled command byte %02x\n",
|
||||
io3->addr,
|
||||
req->bytes[req->pos]);
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_read_id(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
uint8_t req;
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read_8(req_buf, &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("JVS I/O: Read ID\n");
|
||||
|
||||
/* Write report byte */
|
||||
|
||||
hr = iobuf_write_8(resp_buf, 0x01);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Write the identification string. The NUL terminator at the end of this C
|
||||
string is also sent, and it naturally terminates the response chunk. */
|
||||
|
||||
return iobuf_write(resp_buf, io3_ident, sizeof(io3_ident));
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_get_cmd_version(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
uint8_t req;
|
||||
uint8_t resp[2];
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read_8(req_buf, &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("JVS I/O: Get command format version\n");
|
||||
resp[0] = 0x01; /* Report byte */
|
||||
resp[1] = 0x13; /* Command format version BCD */
|
||||
|
||||
return iobuf_write(resp_buf, resp, sizeof(resp));
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_get_jvs_version(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
uint8_t req;
|
||||
uint8_t resp[2];
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read_8(req_buf, &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("JVS I/O: Get JVS version\n");
|
||||
resp[0] = 0x01; /* Report byte */
|
||||
resp[1] = 0x20; /* JVS version BCD */
|
||||
|
||||
return iobuf_write(resp_buf, resp, sizeof(resp));
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_get_comm_version(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
uint8_t req;
|
||||
uint8_t resp[2];
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read_8(req_buf, &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("JVS I/O: Get communication version\n");
|
||||
resp[0] = 0x01; /* Report byte */
|
||||
resp[1] = 0x10; /* "Communication version" BCD */
|
||||
|
||||
return iobuf_write(resp_buf, resp, sizeof(resp));
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_get_features(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
uint8_t req;
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read_8(req_buf, &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("JVS I/O: Get features\n");
|
||||
|
||||
hr = iobuf_write_8(resp_buf, 0x01); /* Write report byte */
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return iobuf_write(resp_buf, io3_features, sizeof(io3_features));
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_read_switches(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
struct jvs_req_read_switches req;
|
||||
struct io3_switch_state state;
|
||||
HRESULT hr;
|
||||
|
||||
/* Read req */
|
||||
|
||||
hr = iobuf_read(req_buf, &req, sizeof(req));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
#if defined(LOG_IO3)
|
||||
dprintf("JVS I/O: Read switches, np=%i, bpp=%i\n",
|
||||
req.num_players,
|
||||
req.bytes_per_player);
|
||||
#endif
|
||||
|
||||
if (req.num_players > 2 || req.bytes_per_player != 2) {
|
||||
dprintf("JVS I/O: Invalid read size "
|
||||
"num_players=%i "
|
||||
"bytes_per_player=%i\n",
|
||||
req.num_players,
|
||||
req.bytes_per_player);
|
||||
hr = iobuf_write_8(resp_buf, 0x02);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
/* Build response */
|
||||
|
||||
hr = iobuf_write_8(resp_buf, 0x01); /* Report byte */
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
if (io3->ops != NULL) {
|
||||
io3->ops->read_switches(io3->ops_ctx, &state);
|
||||
}
|
||||
|
||||
hr = iobuf_write_8(resp_buf, state.system); /* Test, Tilt lines */
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (req.num_players > 0) {
|
||||
hr = iobuf_write_be16(resp_buf, state.p1);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.num_players > 1) {
|
||||
hr = iobuf_write_be16(resp_buf, state.p2);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_read_coin(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
struct jvs_req_read_coin req;
|
||||
uint16_t ncoins;
|
||||
uint8_t i;
|
||||
HRESULT hr;
|
||||
|
||||
/* Read req */
|
||||
|
||||
hr = iobuf_read(req_buf, &req, sizeof(req));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
//dprintf("JVS I/O: Read coin, nslots=%i\n", req.nslots);
|
||||
|
||||
/* Write report byte */
|
||||
|
||||
hr = iobuf_write_8(resp_buf, 0x01);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Write slot detail */
|
||||
|
||||
for (i = 0 ; i < req.nslots ; i++) {
|
||||
ncoins = 0;
|
||||
|
||||
if (io3->ops->read_coin_counter != NULL) {
|
||||
io3->ops->read_coin_counter(io3->ops_ctx, i, &ncoins);
|
||||
}
|
||||
|
||||
hr = iobuf_write_be16(resp_buf, ncoins);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_read_analogs(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
struct jvs_req_read_analogs req;
|
||||
uint16_t analogs[8];
|
||||
uint8_t i;
|
||||
HRESULT hr;
|
||||
|
||||
/* Read req */
|
||||
|
||||
hr = iobuf_read(req_buf, &req, sizeof(req));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (req.nanalogs > _countof(analogs)) {
|
||||
dprintf("JVS I/O: Invalid analog count %i\n", req.nanalogs);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
//dprintf("JVS I/O: Read analogs, nanalogs=%i\n", req.nanalogs);
|
||||
|
||||
/* Write report byte */
|
||||
|
||||
hr = iobuf_write_8(resp_buf, 0x01);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Write analogs */
|
||||
|
||||
memset(analogs, 0, sizeof(analogs));
|
||||
|
||||
if (io3->ops->read_analogs != NULL) {
|
||||
io3->ops->read_analogs(io3->ops_ctx, analogs, req.nanalogs);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < req.nanalogs ; i++) {
|
||||
hr = iobuf_write_be16(resp_buf, analogs[i]);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_read_rotarys(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
struct jvs_req_read_rotarys req;
|
||||
uint16_t rotarys[4];
|
||||
uint8_t i;
|
||||
HRESULT hr;
|
||||
|
||||
/* Read req */
|
||||
|
||||
hr = iobuf_read(req_buf, &req, sizeof(req));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (req.nrotarys > _countof(rotarys)) {
|
||||
dprintf("JVS I/O: Invalid analog count %i\n", req.nrotarys);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
//dprintf("JVS I/O: Read rotarys, nrotarys=%i\n", req.nrotarys);
|
||||
|
||||
/* Write report byte */
|
||||
|
||||
hr = iobuf_write_8(resp_buf, 0x01);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Write analogs */
|
||||
|
||||
memset(rotarys, 0, sizeof(rotarys));
|
||||
|
||||
if (io3->ops->read_rotarys != NULL) {
|
||||
io3->ops->read_rotarys(io3->ops_ctx, rotarys, req.nrotarys);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < req.nrotarys ; i++) {
|
||||
hr = iobuf_write_be16(resp_buf, rotarys[i]);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_write_gpio(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
uint8_t cmd;
|
||||
uint8_t nbytes;
|
||||
uint8_t bytes[3];
|
||||
HRESULT hr;
|
||||
|
||||
/* Read request header */
|
||||
|
||||
hr = iobuf_read_8(req_buf, &cmd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = iobuf_read_8(req_buf, &nbytes);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (nbytes > 3) {
|
||||
dprintf("JVS I/O: Invalid GPIO write size %i\n", nbytes);
|
||||
hr = iobuf_write_8(resp_buf, 0x02);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
/* Read payload */
|
||||
|
||||
memset(bytes, 0, sizeof(bytes));
|
||||
hr = iobuf_read(req_buf, bytes, nbytes);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (io3->ops->write_gpio != NULL) {
|
||||
io3->ops->write_gpio(
|
||||
io3->ops_ctx,
|
||||
bytes[0] | (bytes[1] << 8) | (bytes[2] << 16));
|
||||
}
|
||||
|
||||
/* Write report byte */
|
||||
|
||||
return iobuf_write_8(resp_buf, 0x01);
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_reset(struct io3 *io3, struct const_iobuf *req_buf)
|
||||
{
|
||||
struct jvs_req_reset req;
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read(req_buf, &req, sizeof(req));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("JVS I/O: Reset (param %02x)\n", req.unknown);
|
||||
io3->addr = 0xFF;
|
||||
|
||||
if (io3->ops->reset != NULL) {
|
||||
io3->ops->reset(io3->ops_ctx);
|
||||
}
|
||||
|
||||
/* No ack for this since it really is addressed to everybody */
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io3_cmd_assign_addr(
|
||||
struct io3 *io3,
|
||||
struct const_iobuf *req_buf,
|
||||
struct iobuf *resp_buf)
|
||||
{
|
||||
struct jvs_req_assign_addr req;
|
||||
bool sense;
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read(req_buf, &req, sizeof(req));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
sense = jvs_node_sense(io3->jvs.next);
|
||||
dprintf("JVS I/O: Assign addr %02x sense %i\n", req.addr, sense);
|
||||
|
||||
if (sense) {
|
||||
/* That address is for somebody else */
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
io3->addr = req.addr;
|
||||
|
||||
return iobuf_write_8(resp_buf, 0x01);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jvs/jvs-bus.h"
|
||||
|
||||
struct io3_switch_state {
|
||||
/* Note: this struct is host-endian. The IO3 emulator handles the conversion
|
||||
to protocol-endian. */
|
||||
|
||||
uint8_t system;
|
||||
uint16_t p1;
|
||||
uint16_t p2;
|
||||
};
|
||||
|
||||
struct io3_ops {
|
||||
void (*reset)(void *ctx);
|
||||
void (*write_gpio)(void *ctx, uint32_t state);
|
||||
void (*read_switches)(void *ctx, struct io3_switch_state *out);
|
||||
void (*read_analogs)(void *ctx, uint16_t *analogs, uint8_t nanalogs);
|
||||
void (*read_rotarys)(void *ctx, uint16_t *rotaries, uint8_t nrotaries);
|
||||
void (*read_coin_counter)(void *ctx, uint8_t slot_no, uint16_t *out);
|
||||
};
|
||||
|
||||
struct io3 {
|
||||
struct jvs_node jvs;
|
||||
uint8_t addr;
|
||||
const struct io3_ops *ops;
|
||||
void *ops_ctx;
|
||||
};
|
||||
|
||||
void io3_init(
|
||||
struct io3 *io3,
|
||||
struct jvs_node *next,
|
||||
const struct io3_ops *ops,
|
||||
void *ops_ctx);
|
||||
|
||||
struct jvs_node *io3_to_jvs_node(struct io3 *io3);
|
||||
@@ -0,0 +1,358 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <devioctl.h>
|
||||
#include <hidclass.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/guid.h"
|
||||
#include "board/io4.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/setupapi.h"
|
||||
|
||||
#include "util/async.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
enum {
|
||||
IO4_CMD_SET_COMM_TIMEOUT = 0x01,
|
||||
IO4_CMD_SET_SAMPLING_COUNT = 0x02,
|
||||
IO4_CMD_CLEAR_BOARD_STATUS = 0x03,
|
||||
IO4_CMD_SET_GENERAL_OUTPUT = 0x04,
|
||||
IO4_CMD_SET_PWM_OUTPUT = 0x05,
|
||||
IO4_CMD_SET_UNIQUE_OUTPUT = 0x41,
|
||||
IO4_CMD_UPDATE_FIRMWARE = 0x85,
|
||||
};
|
||||
|
||||
struct io4_report_in {
|
||||
uint8_t report_id;
|
||||
uint16_t adcs[8];
|
||||
uint16_t spinners[4];
|
||||
uint16_t chutes[2];
|
||||
uint16_t buttons[2];
|
||||
uint8_t system_status;
|
||||
uint8_t usb_status;
|
||||
uint8_t unique_input[29];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct io4_report_in) == 0x40, "IO4 IN report size");
|
||||
|
||||
struct io4_report_out {
|
||||
uint8_t report_id;
|
||||
uint8_t cmd;
|
||||
uint8_t payload[IO4_REPORT_OUT_PAYLOAD_LEN];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct io4_report_out) == 0x40, "IO4 OUT report size");
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
static HRESULT io4_handle_irp(struct irp *irp);
|
||||
static HRESULT io4_handle_open(struct irp *irp);
|
||||
static HRESULT io4_handle_close(struct irp *irp);
|
||||
static HRESULT io4_handle_read(struct irp *irp);
|
||||
static HRESULT io4_handle_write(struct irp *irp);
|
||||
static HRESULT io4_handle_ioctl(struct irp *irp);
|
||||
|
||||
static HRESULT io4_ioctl_get_manufacturer_string(struct irp *irp);
|
||||
static HRESULT io4_ioctl_get_product_string(struct irp *irp);
|
||||
|
||||
static HRESULT io4_async_poll(void *ctx, struct irp *irp);
|
||||
|
||||
/* Device node path must contain substring "vid_0ca3" (case-insensitive). */
|
||||
static const wchar_t io4_path[] = L"$io4\\vid_0ca3";
|
||||
|
||||
static const wchar_t io4_manf[] = L"SEGA";
|
||||
static const wchar_t io4_prod[] =
|
||||
/* "Product" (N.B. numbers are in hex) */
|
||||
|
||||
L"I/O CONTROL BD;" /* Board type */
|
||||
L"15257;" /* Board number */
|
||||
L"01;" /* "Mode" (prob. USB vs JVS?) */
|
||||
L"90;" /* Firmware revision */
|
||||
L"1831;" /* Firmware checksum */
|
||||
L"6679A;" /* "Custom chip no" */
|
||||
L"00;" /* "Config" */
|
||||
|
||||
/* "Function" (N.B. all values are in hex) */
|
||||
|
||||
L"GOUT=14_" /* General-purpose output */
|
||||
L"ADIN=8,E_" /* ADC inputs */
|
||||
L"ROTIN=4_" /* Rotary inputs */
|
||||
L"COININ=2_" /* Coin inputs */
|
||||
L"SWIN=2,E_" /* Switch inputs */
|
||||
L"UQ1=41,6" /* "Unique function 1" */
|
||||
;
|
||||
|
||||
static HANDLE io4_fd;
|
||||
static struct async io4_async;
|
||||
static uint8_t io4_system_status;
|
||||
static const struct io4_ops *io4_ops;
|
||||
static void *io4_ops_ctx;
|
||||
|
||||
HRESULT io4_hook_init(
|
||||
const struct io4_config *cfg,
|
||||
const struct io4_ops *ops,
|
||||
void *ctx)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(ops != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
async_init(&io4_async, NULL);
|
||||
|
||||
hr = iohook_open_nul_fd(&io4_fd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
io4_ops = ops;
|
||||
io4_ops_ctx = ctx;
|
||||
io4_system_status = 0x02; /* idk */
|
||||
iohook_push_handler(io4_handle_irp);
|
||||
|
||||
hr = setupapi_add_phantom_dev(&hid_guid, io4_path);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_irp(struct irp *irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != io4_fd) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return io4_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return io4_handle_close(irp);
|
||||
case IRP_OP_READ: return io4_handle_read(irp);
|
||||
case IRP_OP_WRITE: return io4_handle_write(irp);
|
||||
case IRP_OP_IOCTL: return io4_handle_ioctl(irp);
|
||||
default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_open(struct irp *irp)
|
||||
{
|
||||
if (wcscmp(irp->open_filename, io4_path) != 0) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("USB I/O: Device opened\n");
|
||||
irp->fd = io4_fd;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_close(struct irp *irp)
|
||||
{
|
||||
dprintf("USB I/O: Device closed\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_read(struct irp *irp)
|
||||
{
|
||||
/* The amdaemon USBIO driver will continuously poll the IO until the IO
|
||||
call returns an async operation in progress. We have to return and then
|
||||
signal the OVERLAPPED event object "a little bit later" in order to avoid
|
||||
an infinite loop. */
|
||||
|
||||
return async_submit(&io4_async, irp, io4_async_poll);
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_write(struct irp *irp)
|
||||
{
|
||||
struct io4_report_out out;
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read(&irp->write, &out, sizeof(out));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (out.report_id != 0x10) {
|
||||
dprintf("USB I/O: OUT Report ID is incorrect");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
switch (out.cmd) {
|
||||
case IO4_CMD_SET_COMM_TIMEOUT:
|
||||
dprintf("USB I/O: Set comm timeout\n");
|
||||
|
||||
// Ongeki Summer expects the system status to be 0x30 at this point
|
||||
io4_system_status = 0x30;
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_SET_SAMPLING_COUNT:
|
||||
dprintf("USB I/O: Set sampling count\n");
|
||||
|
||||
// Ongeki Summer expects the system status to be 0x30 at this point
|
||||
io4_system_status = 0x30;
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_CLEAR_BOARD_STATUS:
|
||||
dprintf("USB I/O: Clear board status\n");
|
||||
io4_system_status = 0x00;
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_SET_GENERAL_OUTPUT:
|
||||
// dprintf("USB I/O: GPIO Out\n");
|
||||
|
||||
if (io4_ops->write_gpio != NULL) {
|
||||
return io4_ops->write_gpio(out.payload, IO4_REPORT_OUT_PAYLOAD_LEN);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_SET_PWM_OUTPUT:
|
||||
dprintf("USB I/O: PWM Out\n");
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_SET_UNIQUE_OUTPUT:
|
||||
// dprintf("USB I/O: Unique Out\n");
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_UPDATE_FIRMWARE:
|
||||
dprintf("USB I/O: Update firmware..?\n");
|
||||
|
||||
return E_FAIL;
|
||||
|
||||
default:
|
||||
dprintf("USB I/O: Unknown command %02x\n", out.cmd);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_HID_GET_MANUFACTURER_STRING:
|
||||
return io4_ioctl_get_manufacturer_string(irp);
|
||||
|
||||
case IOCTL_HID_GET_PRODUCT_STRING:
|
||||
return io4_ioctl_get_product_string(irp);
|
||||
|
||||
case IOCTL_HID_GET_INPUT_REPORT:
|
||||
dprintf("USB I/O: Control IN (untested!!)\n");
|
||||
|
||||
return io4_handle_read(irp);
|
||||
|
||||
case IOCTL_HID_SET_OUTPUT_REPORT:
|
||||
dprintf("USB I/O: Control OUT (untested!!)\n");
|
||||
|
||||
return io4_handle_write(irp);
|
||||
|
||||
default:
|
||||
dprintf("USB I/O: Unknown ioctl %#08x, write %i read %i\n",
|
||||
irp->ioctl,
|
||||
(int) irp->write.nbytes,
|
||||
(int) irp->read.nbytes);
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT io4_ioctl_get_manufacturer_string(struct irp *irp)
|
||||
{
|
||||
dprintf("USB I/O: Get manufacturer string\n");
|
||||
|
||||
if (irp->read.nbytes < sizeof(io4_manf)) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
memcpy(irp->read.bytes, io4_manf, sizeof(io4_manf));
|
||||
irp->read.pos = sizeof(io4_manf);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_ioctl_get_product_string(struct irp *irp)
|
||||
{
|
||||
dprintf("USB I/O: Get product string\n");
|
||||
|
||||
if (irp->read.nbytes < sizeof(io4_prod)) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
memcpy(irp->read.bytes, io4_prod, sizeof(io4_prod));
|
||||
irp->read.pos = sizeof(io4_prod);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_async_poll(void *ctx, struct irp *irp)
|
||||
{
|
||||
struct io4_report_in in;
|
||||
struct io4_state state;
|
||||
HRESULT hr;
|
||||
size_t i;
|
||||
|
||||
/* Delay long enough for the instigating thread in amdaemon to be satisfied
|
||||
that all queued-up reports have been drained. */
|
||||
|
||||
// Sleep(1);
|
||||
|
||||
/* Call into ops to poll the underlying inputs */
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
hr = io4_ops->poll(io4_ops_ctx, &state);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Construct IN report. Values are all little-endian, unlike JVS. */
|
||||
|
||||
memset(&in, 0, sizeof(in));
|
||||
in.report_id = 0x01;
|
||||
in.system_status = io4_system_status;
|
||||
|
||||
for (i = 0 ; i < 8 ; i++) {
|
||||
in.adcs[i] = state.adcs[i];
|
||||
}
|
||||
|
||||
for (i = 0 ; i < 4 ; i++) {
|
||||
in.spinners[i] = state.spinners[i];
|
||||
}
|
||||
|
||||
for (i = 0 ; i < 2 ; i++) {
|
||||
in.chutes[i] = state.chutes[i];
|
||||
}
|
||||
|
||||
for (i = 0 ; i < 2 ; i++) {
|
||||
in.buttons[i] = state.buttons[i];
|
||||
}
|
||||
|
||||
return iobuf_write(&irp->read, &in, sizeof(in));
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define IO4_REPORT_OUT_PAYLOAD_LEN 62
|
||||
|
||||
enum {
|
||||
/* System buttons in button[0] */
|
||||
|
||||
IO4_BUTTON_TEST = 1 << 9,
|
||||
IO4_BUTTON_SERVICE = 1 << 6,
|
||||
};
|
||||
|
||||
struct io4_config {
|
||||
bool enable;
|
||||
};
|
||||
|
||||
struct io4_state {
|
||||
uint16_t adcs[8];
|
||||
uint16_t spinners[4];
|
||||
uint16_t chutes[2];
|
||||
uint16_t buttons[2];
|
||||
};
|
||||
|
||||
struct io4_ops {
|
||||
HRESULT (*poll)(void *ctx, struct io4_state *state);
|
||||
HRESULT (*write_gpio)(uint8_t* payload, size_t len);
|
||||
};
|
||||
|
||||
HRESULT io4_hook_init(
|
||||
const struct io4_config *cfg,
|
||||
const struct io4_ops *ops,
|
||||
void *ctx);
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/led15070-frame.h"
|
||||
|
||||
/* Command IDs */
|
||||
|
||||
enum {
|
||||
LED_15070_CMD_RESET = 0x10,
|
||||
LED_15070_CMD_SET_INPUT = 0x28, // No known use case
|
||||
LED_15070_CMD_SET_NORMAL_12BIT = 0x30, // TODO
|
||||
LED_15070_CMD_SET_NORMAL_8BIT = 0x31,
|
||||
LED_15070_CMD_SET_MULTI_FLASH_8BIT = 0x32,
|
||||
LED_15070_CMD_SET_MULTI_FADE_8BIT = 0x33,
|
||||
LED_15070_CMD_SET_PALETTE_7_NORMAL_LED = 0x34, // No known use case
|
||||
LED_15070_CMD_SET_PALETTE_6_FLASH_LED = 0x35, // No known use case
|
||||
LED_15070_CMD_SET_15DC_OUT = 0x36, // No known use case
|
||||
LED_15070_CMD_SET_15GS_OUT = 0x37, // No known use case
|
||||
LED_15070_CMD_SET_PSC_MAX = 0x38, // No known use case
|
||||
LED_15070_CMD_SET_FET_OUTPUT = 0x39,
|
||||
LED_15070_CMD_SET_GS_PALETTE = 0x3A,
|
||||
LED_15070_CMD_DC_UPDATE = 0x3B,
|
||||
LED_15070_CMD_GS_UPDATE = 0x3C,
|
||||
LED_15070_CMD_ROTATE = 0x3E, // No known use case, wtf is this?
|
||||
LED_15070_CMD_SET_DC_DATA = 0x3F,
|
||||
LED_15070_CMD_EEPROM_WRITE = 0x7B,
|
||||
LED_15070_CMD_EEPROM_READ = 0x7C,
|
||||
LED_15070_CMD_ACK_ON = 0x7D,
|
||||
LED_15070_CMD_ACK_OFF = 0x7E,
|
||||
LED_15070_CMD_BOARD_INFO = 0xF0,
|
||||
LED_15070_CMD_BOARD_STATUS = 0xF1,
|
||||
LED_15070_CMD_FW_SUM = 0xF2,
|
||||
LED_15070_CMD_PROTOCOL_VER = 0xF3,
|
||||
LED_15070_CMD_TO_BOOT_MODE = 0xFD,
|
||||
LED_15070_CMD_FW_UPDATE = 0xFE,
|
||||
};
|
||||
|
||||
/* Response codes */
|
||||
|
||||
enum {
|
||||
LED_15070_STATUS_OK = 0x01,
|
||||
LED_15070_STATUS_SUM_ERR = 0x02,
|
||||
LED_15070_STATUS_PARITY_ERR = 0x03,
|
||||
LED_15070_STATUS_FRAMING_ERR = 0x04,
|
||||
LED_15070_STATUS_OVERRUN_ERR = 0x05,
|
||||
LED_15070_STATUS_BUFFER_OVERFLOW = 0x06,
|
||||
};
|
||||
|
||||
enum {
|
||||
LED_15070_REPORT_OK = 0x01,
|
||||
LED_15070_REPORT_WAIT = 0x02,
|
||||
LED_15070_REPORT_ERR1 = 0x03,
|
||||
LED_15070_REPORT_ERR2 = 0x04,
|
||||
};
|
||||
|
||||
/* Request data structures */
|
||||
|
||||
struct led15070_req_any {
|
||||
struct led15070_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint8_t payload[256];
|
||||
};
|
||||
|
||||
/* Response data structures */
|
||||
|
||||
struct led15070_resp_any {
|
||||
struct led15070_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
uint8_t data[32];
|
||||
};
|
||||
|
||||
struct led15070_resp_board_info {
|
||||
struct led15070_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
char board_num[8];
|
||||
uint8_t endcode; // Always 0xFF
|
||||
uint8_t fw_ver;
|
||||
};
|
||||
@@ -0,0 +1,194 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/led15070-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
static void led15070_frame_sync(struct iobuf *src);
|
||||
static HRESULT led15070_frame_accept(const struct iobuf *dest);
|
||||
static HRESULT led15070_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
[0] Sync byte (0xE0)
|
||||
[1] Destination address
|
||||
[2] Source Address
|
||||
[3] Length of data/payload
|
||||
[4] Data/payload
|
||||
For requests (host to board):
|
||||
[0] Command
|
||||
... Payload
|
||||
For responses (board to host):
|
||||
[0] Status
|
||||
[1] Command
|
||||
[2] Report
|
||||
... Payload
|
||||
[n] Checksum: Sum of all prior bytes (excluding sync byte)
|
||||
|
||||
Byte stuffing:
|
||||
|
||||
0xD0 is an escape byte. Un-escape the subsequent byte by adding 1. */
|
||||
|
||||
static void led15070_frame_sync(struct iobuf *src)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < src->pos && src->bytes[i] != 0xE0 ; i++);
|
||||
|
||||
src->pos -= i;
|
||||
memmove(&src->bytes[0], &src->bytes[i], i);
|
||||
}
|
||||
|
||||
static HRESULT led15070_frame_accept(const struct iobuf *dest)
|
||||
{
|
||||
uint8_t checksum;
|
||||
size_t i;
|
||||
|
||||
if (dest->pos < 3 || dest->pos != dest->bytes[3] + 5) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
|
||||
for (i = 1 ; i < dest->pos - 1 ; i++) {
|
||||
checksum += dest->bytes[i];
|
||||
}
|
||||
|
||||
//dprintf("LED checksum %02x, expected %02x\n", checksum, dest->bytes[dest->pos - 1]);
|
||||
|
||||
if (checksum != dest->bytes[dest->pos - 1]) {
|
||||
return HRESULT_FROM_WIN32(ERROR_CRC);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT led15070_frame_decode(struct iobuf *dest, struct iobuf *src)
|
||||
{
|
||||
uint8_t byte;
|
||||
bool escape;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(src != NULL);
|
||||
assert(src->bytes != NULL || src->nbytes == 0);
|
||||
assert(src->pos <= src->nbytes);
|
||||
|
||||
led15070_frame_sync(src);
|
||||
|
||||
dest->pos = 0;
|
||||
escape = false;
|
||||
|
||||
for (i = 0, hr = S_FALSE ; i < src->pos && hr == S_FALSE ; i++) {
|
||||
/* Step the FSM to unstuff another byte */
|
||||
|
||||
byte = src->bytes[i];
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
} else if (i == 0) {
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
} else if (byte == 0xE0) {
|
||||
hr = E_FAIL;
|
||||
} else if (byte == 0xD0) {
|
||||
if (escape) {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
|
||||
escape = true;
|
||||
} else if (escape) {
|
||||
dest->bytes[dest->pos++] = byte + 1;
|
||||
escape = false;
|
||||
} else {
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
}
|
||||
|
||||
/* Try to accept the packet we've built up so far */
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = led15070_frame_accept(dest);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle FSM terminal state */
|
||||
|
||||
if (hr != S_FALSE) {
|
||||
/* Frame was either accepted or rejected, remove it from src */
|
||||
memmove(&src->bytes[0], &src->bytes[i], src->pos - i);
|
||||
src->pos -= i;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT led15070_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes)
|
||||
{
|
||||
const uint8_t *src;
|
||||
uint8_t checksum;
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(ptr != NULL);
|
||||
|
||||
src = ptr;
|
||||
|
||||
assert(nbytes >= 3 && src[0] == 0xE0 && src[3] + 4 == nbytes);
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = 0xE0;
|
||||
checksum = 0;
|
||||
// dprintf("%02x ", 0xe0);
|
||||
|
||||
for (i = 1 ; i < nbytes ; i++) {
|
||||
byte = src[i];
|
||||
checksum += byte;
|
||||
// dprintf("%02x ", byte);
|
||||
|
||||
hr = led15070_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
// dprintf("%02x \n", checksum);
|
||||
|
||||
return led15070_frame_encode_byte(dest, checksum);
|
||||
}
|
||||
|
||||
static HRESULT led15070_frame_encode_byte(struct iobuf *dest, uint8_t byte)
|
||||
{
|
||||
if (byte == 0xE0 || byte == 0xD0) {
|
||||
if (dest->pos + 2 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = 0xD0;
|
||||
dest->bytes[dest->pos++] = byte - 1;
|
||||
} else {
|
||||
if (dest->pos + 1 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
enum {
|
||||
LED_15070_FRAME_SYNC = 0xE0,
|
||||
};
|
||||
|
||||
struct led15070_hdr {
|
||||
uint8_t sync;
|
||||
uint8_t dest_adr;
|
||||
uint8_t src_adr;
|
||||
uint8_t nbytes;
|
||||
};
|
||||
|
||||
HRESULT led15070_frame_decode(struct iobuf *dest, struct iobuf *src);
|
||||
|
||||
HRESULT led15070_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct led15070_config {
|
||||
bool enable;
|
||||
unsigned int port_no[2];
|
||||
char board_number[8];
|
||||
uint8_t fw_ver;
|
||||
uint16_t fw_sum;
|
||||
wchar_t eeprom_path[MAX_PATH];
|
||||
};
|
||||
|
||||
typedef HRESULT (*io_led_init_t)(void);
|
||||
typedef void (*io_led_set_fet_output_t)(uint8_t board, const uint8_t *rgb);
|
||||
typedef void (*io_led_dc_update_t)(uint8_t board, const uint8_t *rgb);
|
||||
typedef void (*io_led_gs_update_t)(uint8_t board, const uint8_t *rgb);
|
||||
|
||||
HRESULT led15070_hook_init(
|
||||
const struct led15070_config *cfg,
|
||||
io_led_init_t _led_init,
|
||||
io_led_set_fet_output_t _led_set_fet_output,
|
||||
io_led_dc_update_t _led_dc_update,
|
||||
io_led_gs_update_t _led_gs_update,
|
||||
unsigned int port_no[2]);
|
||||
@@ -0,0 +1,222 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/led15093-frame.h"
|
||||
|
||||
/* Command IDs */
|
||||
|
||||
enum {
|
||||
LED_15093_CMD_RESET = 0x10,
|
||||
LED_15093_CMD_SET_TIMEOUT = 0x11,
|
||||
LED_15093_CMD_UNK1 = 0x12,
|
||||
LED_15093_CMD_SET_DISABLE_RESPONSE = 0x14,
|
||||
LED_15093_CMD_SET_ID = 0x18,
|
||||
LED_15093_CMD_CLEAR_ID = 0x19,
|
||||
LED_15093_CMD_SET_MAX_BRIGHT = 0x3F, // TODO
|
||||
LED_15093_CMD_UPDATE_LED = 0x80,
|
||||
LED_15093_CMD_SET_LED = 0x81,
|
||||
LED_15093_CMD_SET_IMM_LED = 0x82,
|
||||
LED_15093_CMD_SET_FADE_LED = 0x83,
|
||||
LED_15093_CMD_SET_FADE_LEVEL = 0x84,
|
||||
LED_15093_CMD_SET_FADE_SHIFT = 0x85,
|
||||
LED_15093_CMD_SET_AUTO_SHIFT = 0x86,
|
||||
LED_15093_CMD_GET_BOARD_INFO = 0xF0,
|
||||
LED_15093_CMD_GET_BOARD_STATUS = 0xF1,
|
||||
LED_15093_CMD_GET_FW_SUM = 0xF2,
|
||||
LED_15093_CMD_GET_PROTOCOL_VER = 0xF3,
|
||||
LED_15093_CMD_SET_BOOTMODE = 0xFD,
|
||||
LED_15093_CMD_FW_UPDATE = 0xFE,
|
||||
};
|
||||
|
||||
/* Response codes */
|
||||
|
||||
enum {
|
||||
LED_15093_STATUS_OK = 0x01,
|
||||
LED_15093_STATUS_ERR_SUM = 0x02,
|
||||
LED_15093_STATUS_ERR_PARITY = 0x03,
|
||||
LED_15093_STATUS_ERR_FRAMING = 0x04,
|
||||
LED_15093_STATUS_ERR_OVERRUN = 0x05,
|
||||
LED_15093_STATUS_ERR_BUFFER_OVERFLOW = 0x06,
|
||||
};
|
||||
|
||||
enum {
|
||||
LED_15093_REPORT_OK = 0x01,
|
||||
LED_15093_REPORT_WAIT = 0x02,
|
||||
LED_15093_REPORT_ERR1 = 0x03,
|
||||
LED_15093_REPORT_ERR2 = 0x04,
|
||||
};
|
||||
|
||||
/* Status bitmasks */
|
||||
|
||||
enum {
|
||||
LED_15093_STATUS_UART_ERR_SUM = 0x01,
|
||||
LED_15093_STATUS_UART_ERR_PARITY = 0x02,
|
||||
LED_15093_STATUS_UART_ERR_FRAMING = 0x04,
|
||||
LED_15093_STATUS_UART_ERR_OVERRUN = 0x08,
|
||||
LED_15093_STATUS_UART_ERR_BUFFER_OVERFLOW = 0x10,
|
||||
};
|
||||
|
||||
enum {
|
||||
LED_15093_STATUS_BOARD_ERR_WDT = 0x01,
|
||||
LED_15093_STATUS_BOARD_ERR_TIMEOUT = 0x02,
|
||||
LED_15093_STATUS_BOARD_ERR_RESET = 0x04,
|
||||
LED_15093_STATUS_BOARD_ERR_BOR = 0x08,
|
||||
};
|
||||
|
||||
enum {
|
||||
LED_15093_STATUS_CMD_ERR_BUSY = 0x01,
|
||||
LED_15093_STATUS_CMD_ERR_UNKNOWN = 0x02,
|
||||
LED_15093_STATUS_CMD_ERR_PARAM = 0x04,
|
||||
LED_15093_STATUS_CMD_ERR_EXE = 0x08,
|
||||
};
|
||||
|
||||
/* Status types for internal use */
|
||||
|
||||
enum {
|
||||
LED_15093_STATUS_TYPE_BOARD = 1,
|
||||
LED_15093_STATUS_TYPE_UART = 2,
|
||||
LED_15093_STATUS_TYPE_CMD = 3,
|
||||
};
|
||||
|
||||
/* Request data structures */
|
||||
|
||||
struct led15093_req_reset {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint8_t r_type;
|
||||
};
|
||||
|
||||
struct led15093_req_set_timeout {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint16_t count;
|
||||
};
|
||||
|
||||
struct led15093_req_set_disable_response {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
bool sw;
|
||||
};
|
||||
|
||||
struct led15093_req_set_id {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint8_t id;
|
||||
};
|
||||
|
||||
struct led15093_req_set_led {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint8_t data[198];
|
||||
};
|
||||
|
||||
struct led15093_req_set_fade_level {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint8_t depth;
|
||||
uint8_t cycle;
|
||||
};
|
||||
|
||||
struct led15093_req_set_fade_shift {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint8_t target;
|
||||
};
|
||||
|
||||
struct led15093_req_set_auto_shift {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
uint8_t count;
|
||||
uint8_t target;
|
||||
};
|
||||
|
||||
struct led15093_req_get_board_status {
|
||||
struct led15093_req_hdr hdr;
|
||||
uint8_t cmd;
|
||||
bool clear;
|
||||
};
|
||||
|
||||
union led15093_req_any {
|
||||
struct led15093_req_hdr hdr;
|
||||
struct led15093_req_reset reset;
|
||||
struct led15093_req_set_timeout set_timeout;
|
||||
struct led15093_req_set_disable_response set_disable_response;
|
||||
struct led15093_req_set_id set_id;
|
||||
struct led15093_req_set_led set_led;
|
||||
struct led15093_req_set_fade_level set_fade_level;
|
||||
struct led15093_req_set_fade_shift set_fade_shift;
|
||||
struct led15093_req_set_auto_shift set_auto_shift;
|
||||
struct led15093_req_get_board_status get_board_status;
|
||||
uint8_t payload[256];
|
||||
};
|
||||
|
||||
/* Response data structures */
|
||||
|
||||
struct led15093_resp_any {
|
||||
struct led15093_resp_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
uint8_t data[32];
|
||||
};
|
||||
|
||||
struct led15093_resp_timeout {
|
||||
struct led15093_resp_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
uint8_t count_upper;
|
||||
uint8_t count_lower;
|
||||
};
|
||||
|
||||
struct led15093_resp_fw_sum {
|
||||
struct led15093_resp_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
uint8_t sum_upper;
|
||||
uint8_t sum_lower;
|
||||
};
|
||||
|
||||
struct led15093_resp_board_info_legacy {
|
||||
struct led15093_resp_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
char board_num[8];
|
||||
uint8_t lf; // 0x0A (ASCII LF)
|
||||
char chip_num[5];
|
||||
uint8_t endcode; // Always 0xFF
|
||||
uint8_t fw_ver;
|
||||
};
|
||||
|
||||
struct led15093_resp_board_info {
|
||||
struct led15093_resp_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
char board_num[8];
|
||||
uint8_t lf; // 0x0A (ASCII LF)
|
||||
char chip_num[5];
|
||||
uint8_t endcode; // Always 0xFF
|
||||
uint8_t fw_ver;
|
||||
uint16_t rx_buf;
|
||||
};
|
||||
|
||||
struct led15093_resp_protocol_ver {
|
||||
struct led15093_resp_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
uint8_t mode;
|
||||
uint8_t major_ver;
|
||||
uint8_t minor_ver;
|
||||
};
|
||||
|
||||
struct led15093_resp_set_auto_shift {
|
||||
struct led15093_resp_hdr hdr;
|
||||
uint8_t status;
|
||||
uint8_t cmd;
|
||||
uint8_t report;
|
||||
uint8_t count;
|
||||
uint8_t target;
|
||||
};
|
||||
@@ -0,0 +1,196 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/led15093-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
static void led15093_frame_sync(struct iobuf *src);
|
||||
static HRESULT led15093_frame_accept(const struct iobuf *dest);
|
||||
static HRESULT led15093_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
[0] Sync byte (0xE0)
|
||||
[1] Destination address
|
||||
[2] Source Address
|
||||
[3] Length of data/payload
|
||||
[4] Data/payload
|
||||
For requests (host to board):
|
||||
[0] Command
|
||||
... Payload
|
||||
For responses (board to host):
|
||||
[0] Status
|
||||
[1] Command
|
||||
[2] Report
|
||||
... Payload
|
||||
[n] Checksum: Sum of all prior bytes (excluding sync byte)
|
||||
|
||||
Byte stuffing:
|
||||
|
||||
0xD0 is an escape byte. Un-escape the subsequent byte by adding 1. */
|
||||
|
||||
static void led15093_frame_sync(struct iobuf *src)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < src->pos && src->bytes[i] != LED_15093_FRAME_SYNC ; i++);
|
||||
|
||||
src->pos -= i;
|
||||
memmove(&src->bytes[0], &src->bytes[i], i);
|
||||
}
|
||||
|
||||
static HRESULT led15093_frame_accept(const struct iobuf *dest)
|
||||
{
|
||||
uint8_t checksum;
|
||||
size_t i;
|
||||
|
||||
if (dest->pos < 3 || dest->pos != dest->bytes[3] + 5) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
|
||||
for (i = 1 ; i < dest->pos - 1 ; i++) {
|
||||
checksum += dest->bytes[i];
|
||||
}
|
||||
|
||||
// dprintf("LED checksum %02x, expected %02x\n", checksum, dest->bytes[dest->pos - 1]);
|
||||
|
||||
if (checksum != dest->bytes[dest->pos - 1]) {
|
||||
return HRESULT_FROM_WIN32(ERROR_CRC);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT led15093_frame_decode(struct iobuf *dest, struct iobuf *src)
|
||||
{
|
||||
uint8_t byte;
|
||||
bool escape;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(src != NULL);
|
||||
assert(src->bytes != NULL || src->nbytes == 0);
|
||||
assert(src->pos <= src->nbytes);
|
||||
|
||||
led15093_frame_sync(src);
|
||||
|
||||
dest->pos = 0;
|
||||
escape = false;
|
||||
|
||||
for (i = 0, hr = S_FALSE ; i < src->pos && hr == S_FALSE ; i++) {
|
||||
/* Step the FSM to unstuff another byte */
|
||||
|
||||
byte = src->bytes[i];
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
} else if (i == 0) {
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
} else if (byte == LED_15093_FRAME_SYNC) {
|
||||
hr = E_FAIL;
|
||||
} else if (byte == LED_15093_FRAME_ESC) {
|
||||
if (escape) {
|
||||
hr = E_FAIL;
|
||||
}
|
||||
|
||||
escape = true;
|
||||
} else if (escape) {
|
||||
dest->bytes[dest->pos++] = byte + 1;
|
||||
escape = false;
|
||||
} else {
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
}
|
||||
|
||||
/* Try to accept the packet we've built up so far */
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = led15093_frame_accept(dest);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle FSM terminal state */
|
||||
|
||||
if (hr != S_FALSE) {
|
||||
/* Frame was either accepted or rejected, remove it from src */
|
||||
memmove(&src->bytes[0], &src->bytes[i], src->pos - i);
|
||||
src->pos -= i;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT led15093_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes)
|
||||
{
|
||||
const uint8_t *src;
|
||||
uint8_t checksum;
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(ptr != NULL);
|
||||
|
||||
src = ptr;
|
||||
|
||||
assert(nbytes >= 3 &&
|
||||
src[0] == LED_15093_FRAME_SYNC &&
|
||||
src[3] + 4 == nbytes);
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = LED_15093_FRAME_SYNC;
|
||||
checksum = 0;
|
||||
// dprintf("%02x ", LED_15093_FRAME_SYNC);
|
||||
|
||||
for (i = 1 ; i < nbytes ; i++) {
|
||||
byte = src[i];
|
||||
checksum += byte;
|
||||
// dprintf("%02x ", byte);
|
||||
|
||||
hr = led15093_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
// dprintf("%02x \n", checksum);
|
||||
|
||||
return led15093_frame_encode_byte(dest, checksum);
|
||||
}
|
||||
|
||||
static HRESULT led15093_frame_encode_byte(struct iobuf *dest, uint8_t byte)
|
||||
{
|
||||
if (byte == LED_15093_FRAME_SYNC || byte == LED_15093_FRAME_ESC) {
|
||||
if (dest->pos + 2 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = LED_15093_FRAME_ESC;
|
||||
dest->bytes[dest->pos++] = byte - 1;
|
||||
} else {
|
||||
if (dest->pos + 1 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
enum {
|
||||
LED_15093_FRAME_SYNC = 0xE0,
|
||||
LED_15093_FRAME_ESC = 0xD0,
|
||||
};
|
||||
|
||||
struct led15093_req_hdr {
|
||||
uint8_t sync;
|
||||
uint8_t dest_adr;
|
||||
uint8_t src_adr;
|
||||
uint8_t nbytes;
|
||||
};
|
||||
|
||||
struct led15093_resp_hdr {
|
||||
uint8_t sync;
|
||||
uint8_t dest_adr;
|
||||
uint8_t src_adr;
|
||||
uint8_t nbytes;
|
||||
};
|
||||
|
||||
HRESULT led15093_frame_decode(struct iobuf *dest, struct iobuf *src);
|
||||
|
||||
HRESULT led15093_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct led15093_config {
|
||||
bool enable;
|
||||
bool high_baudrate;
|
||||
unsigned int port_no[2];
|
||||
char board_number[8];
|
||||
char chip_number[5];
|
||||
char boot_chip_number[5];
|
||||
uint8_t fw_ver;
|
||||
uint16_t fw_sum;
|
||||
};
|
||||
|
||||
typedef HRESULT (*io_led_init_t)(void);
|
||||
typedef void (*io_led_set_leds_t)(uint8_t board, uint8_t *rgb);
|
||||
|
||||
HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led_init,
|
||||
io_led_set_leds_t _set_leds, unsigned int port_no[2]);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
board_lib = static_library(
|
||||
'board',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
],
|
||||
link_with : [
|
||||
iccard_lib,
|
||||
],
|
||||
sources : [
|
||||
'aime-dll.c',
|
||||
'aime-dll.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'guid.c',
|
||||
'guid.h',
|
||||
'io3.c',
|
||||
'io3.h',
|
||||
'io4.c',
|
||||
'io4.h',
|
||||
'led15093-cmd.h',
|
||||
'led15093-frame.c',
|
||||
'led15093-frame.h',
|
||||
'led15093.c',
|
||||
'led15093.h',
|
||||
'led15070-cmd.h',
|
||||
'led15070-frame.c',
|
||||
'led15070-frame.h',
|
||||
'led15070.c',
|
||||
'led15070.h',
|
||||
'sg-cmd.c',
|
||||
'sg-cmd.h',
|
||||
'sg-frame.c',
|
||||
'sg-frame.h',
|
||||
'sg-led.c',
|
||||
'sg-led.h',
|
||||
'sg-led-cmd.h',
|
||||
'sg-nfc.c',
|
||||
'sg-nfc.h',
|
||||
'sg-nfc-cmd.h',
|
||||
'sg-reader.c',
|
||||
'sg-reader.h',
|
||||
'slider-cmd.h',
|
||||
'slider-frame.c',
|
||||
'slider-frame.h',
|
||||
'vfd.c',
|
||||
'vfd.h',
|
||||
'vfd-cmd.h',
|
||||
'vfd-frame.c',
|
||||
'vfd-frame.h',
|
||||
'ffb.c',
|
||||
'ffb.h'
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,134 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "board/sg-cmd.h"
|
||||
#include "board/sg-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
union sg_req_any {
|
||||
struct sg_req_header req;
|
||||
uint8_t bytes[256];
|
||||
};
|
||||
|
||||
union sg_res_any {
|
||||
struct sg_res_header res;
|
||||
uint8_t bytes[256];
|
||||
};
|
||||
|
||||
static HRESULT sg_req_validate(const void *ptr, size_t nbytes);
|
||||
|
||||
static void sg_res_error(
|
||||
struct sg_res_header *res,
|
||||
const struct sg_req_header *req);
|
||||
|
||||
static HRESULT sg_req_validate(const void *ptr, size_t nbytes)
|
||||
{
|
||||
const struct sg_req_header *req;
|
||||
size_t payload_len;
|
||||
|
||||
assert(ptr != NULL);
|
||||
|
||||
if (nbytes < sizeof(*req)) {
|
||||
dprintf("SG Cmd: Request header truncated\n");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
req = ptr;
|
||||
|
||||
if (req->hdr.frame_len != nbytes) {
|
||||
dprintf("SG Cmd: Frame length mismatch: got %i exp %i\n",
|
||||
req->hdr.frame_len,
|
||||
(int) nbytes);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
payload_len = req->hdr.frame_len - sizeof(*req);
|
||||
|
||||
if (req->payload_len != payload_len) {
|
||||
dprintf("SG Cmd: Payload length mismatch: got %i exp %i\n",
|
||||
req->payload_len,
|
||||
(int) payload_len);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void sg_req_transact(
|
||||
struct iobuf *res_frame,
|
||||
const uint8_t *req_bytes,
|
||||
size_t req_nbytes,
|
||||
sg_dispatch_fn_t dispatch,
|
||||
void *ctx)
|
||||
{
|
||||
struct iobuf req_span;
|
||||
union sg_req_any req;
|
||||
union sg_res_any res;
|
||||
HRESULT hr;
|
||||
|
||||
assert(res_frame != NULL);
|
||||
assert(req_bytes != NULL);
|
||||
assert(dispatch != NULL);
|
||||
|
||||
req_span.bytes = req.bytes;
|
||||
req_span.nbytes = sizeof(req.bytes);
|
||||
req_span.pos = 0;
|
||||
|
||||
hr = sg_frame_decode(&req_span, req_bytes, req_nbytes);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = sg_req_validate(req.bytes, req_span.pos);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = dispatch(ctx, &req, &res);
|
||||
|
||||
if (hr != S_FALSE) {
|
||||
if (FAILED(hr)) {
|
||||
sg_res_error(&res.res, &req.req);
|
||||
}
|
||||
|
||||
sg_frame_encode(res_frame, res.bytes, res.res.hdr.frame_len);
|
||||
}
|
||||
}
|
||||
|
||||
void sg_res_init(
|
||||
struct sg_res_header *res,
|
||||
const struct sg_req_header *req,
|
||||
size_t payload_len)
|
||||
{
|
||||
assert(res != NULL);
|
||||
assert(req != NULL);
|
||||
|
||||
res->hdr.frame_len = sizeof(*res) + payload_len;
|
||||
res->hdr.addr = req->hdr.addr;
|
||||
res->hdr.seq_no = req->hdr.seq_no;
|
||||
res->hdr.cmd = req->hdr.cmd;
|
||||
res->status = 0;
|
||||
res->payload_len = payload_len;
|
||||
}
|
||||
|
||||
static void sg_res_error(
|
||||
struct sg_res_header *res,
|
||||
const struct sg_req_header *req)
|
||||
{
|
||||
assert(res != NULL);
|
||||
assert(req != NULL);
|
||||
|
||||
res->hdr.frame_len = sizeof(*res);
|
||||
res->hdr.addr = req->hdr.addr;
|
||||
res->hdr.seq_no = req->hdr.seq_no;
|
||||
res->hdr.cmd = req->hdr.cmd;
|
||||
res->status = 1;
|
||||
res->payload_len = 0;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
struct sg_header {
|
||||
uint8_t frame_len;
|
||||
uint8_t addr;
|
||||
uint8_t seq_no;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
struct sg_req_header {
|
||||
struct sg_header hdr;
|
||||
uint8_t payload_len;
|
||||
};
|
||||
|
||||
struct sg_res_header {
|
||||
struct sg_header hdr;
|
||||
uint8_t status;
|
||||
uint8_t payload_len;
|
||||
};
|
||||
|
||||
/* struct to save the version string with its length
|
||||
to fix NUL terminator issues */
|
||||
struct version_info {
|
||||
const char *version;
|
||||
uint8_t length;
|
||||
};
|
||||
|
||||
typedef HRESULT (*sg_dispatch_fn_t)(
|
||||
void *ctx,
|
||||
const void *req,
|
||||
void *res);
|
||||
|
||||
void sg_req_transact(
|
||||
struct iobuf *res_frame,
|
||||
const uint8_t *req_bytes,
|
||||
size_t req_nbytes,
|
||||
sg_dispatch_fn_t dispatch,
|
||||
void *ctx);
|
||||
|
||||
void sg_res_init(
|
||||
struct sg_res_header *res,
|
||||
const struct sg_req_header *req,
|
||||
size_t payload_len);
|
||||
@@ -0,0 +1,165 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/sg-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT sg_frame_accept(struct iobuf *dest);
|
||||
static HRESULT sg_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
[0] Sync byte (0xE0)
|
||||
[1] Frame size (including self)
|
||||
[2] Address
|
||||
[3] Sequence no
|
||||
... Body
|
||||
[n] Checksum: Sum of all non-framing bytes
|
||||
|
||||
Byte stuffing:
|
||||
|
||||
0xD0 is an escape byte. Un-escape the subsequent byte by adding 1. */
|
||||
|
||||
static HRESULT sg_frame_accept(struct iobuf *dest)
|
||||
{
|
||||
uint8_t checksum;
|
||||
size_t i;
|
||||
|
||||
if (dest->pos < 1 || dest->pos != dest->bytes[0] + 1) {
|
||||
dprintf("SG Frame: Size mismatch\n");
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
|
||||
for (i = 0 ; i < dest->pos - 1 ; i++) {
|
||||
checksum += dest->bytes[i];
|
||||
}
|
||||
|
||||
if (checksum != dest->bytes[dest->pos - 1]) {
|
||||
dprintf("SG Frame: Checksum mismatch\n");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_CRC);
|
||||
}
|
||||
|
||||
/* Discard checksum */
|
||||
dest->pos--;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT sg_frame_decode(struct iobuf *dest, const uint8_t *bytes, size_t nbytes)
|
||||
{
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(bytes != NULL);
|
||||
|
||||
if (nbytes < 1 || bytes[0] != 0xE0) {
|
||||
dprintf("SG Frame: Bad sync\n");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dest->pos = 0;
|
||||
i = 1;
|
||||
|
||||
while (i < nbytes) {
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
byte = bytes[i++];
|
||||
|
||||
if (byte == 0xE0) {
|
||||
dprintf("SG Frame: Unescaped sync\n");
|
||||
|
||||
return E_FAIL;
|
||||
} else if (byte == 0xD0) {
|
||||
if (i >= nbytes) {
|
||||
dprintf("SG Frame: Trailing escape\n");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
byte = bytes[i++];
|
||||
dest->bytes[dest->pos++] = byte + 1;
|
||||
} else {
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
}
|
||||
}
|
||||
|
||||
return sg_frame_accept(dest);
|
||||
}
|
||||
|
||||
HRESULT sg_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 != 0 && src[0] == nbytes);
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = 0xE0;
|
||||
checksum = 0;
|
||||
|
||||
for (i = 0 ; i < nbytes ; i++) {
|
||||
byte = src[i];
|
||||
checksum += byte;
|
||||
|
||||
hr = sg_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return sg_frame_encode_byte(dest, checksum);
|
||||
}
|
||||
|
||||
static HRESULT sg_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;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
HRESULT sg_frame_decode(
|
||||
struct iobuf *dest,
|
||||
const uint8_t *bytes,
|
||||
size_t nbytes);
|
||||
|
||||
HRESULT sg_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes);
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/sg-cmd.h"
|
||||
|
||||
enum {
|
||||
SG_RGB_CMD_SET_COLOR = 0x81,
|
||||
SG_RGB_CMD_RESET = 0xF5,
|
||||
SG_RGB_CMD_GET_INFO = 0xF0,
|
||||
};
|
||||
|
||||
struct sg_led_res_reset {
|
||||
struct sg_res_header res;
|
||||
uint8_t payload;
|
||||
};
|
||||
|
||||
struct sg_led_res_get_info {
|
||||
struct sg_res_header res;
|
||||
char payload[12];
|
||||
};
|
||||
|
||||
struct sg_led_req_set_color {
|
||||
struct sg_req_header req;
|
||||
uint8_t payload[3];
|
||||
};
|
||||
|
||||
union sg_led_req_any {
|
||||
uint8_t bytes[256];
|
||||
struct sg_req_header simple;
|
||||
struct sg_led_req_set_color set_color;
|
||||
};
|
||||
|
||||
union sg_led_res_any {
|
||||
uint8_t bytes[256];
|
||||
struct sg_res_header simple;
|
||||
struct sg_led_res_reset reset;
|
||||
struct sg_led_res_get_info get_info;
|
||||
};
|
||||
@@ -0,0 +1,186 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "board/sg-cmd.h"
|
||||
#include "board/sg-led.h"
|
||||
#include "board/sg-led-cmd.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT sg_led_dispatch(
|
||||
void *ctx,
|
||||
const void *v_req,
|
||||
void *v_res);
|
||||
|
||||
static HRESULT sg_led_cmd_reset(
|
||||
const struct sg_led *led,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_led_res_reset *res);
|
||||
|
||||
static HRESULT sg_led_cmd_get_info(
|
||||
const struct sg_led *led,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_led_res_get_info *res);
|
||||
|
||||
static HRESULT sg_led_cmd_set_color(
|
||||
const struct sg_led *led,
|
||||
const struct sg_led_req_set_color *req);
|
||||
|
||||
static const struct version_info led_version[] = {
|
||||
{"15084\xFF\x10\x00\x12", 9},
|
||||
{"000-00000\xFF\x11\x40", 12},
|
||||
// maybe the same?
|
||||
{"000-00000\xFF\x11\x40", 12}
|
||||
};
|
||||
|
||||
void sg_led_init(
|
||||
struct sg_led *led,
|
||||
uint8_t addr,
|
||||
const struct sg_led_ops *ops,
|
||||
unsigned int gen,
|
||||
void *ctx)
|
||||
{
|
||||
assert(led != NULL);
|
||||
assert(ops != NULL);
|
||||
|
||||
led->ops = ops;
|
||||
led->ops_ctx = ctx;
|
||||
led->addr = addr;
|
||||
led->gen = gen;
|
||||
}
|
||||
|
||||
void sg_led_transact(
|
||||
struct sg_led *led,
|
||||
struct iobuf *res_frame,
|
||||
const void *req_bytes,
|
||||
size_t req_nbytes)
|
||||
{
|
||||
assert(led != NULL);
|
||||
assert(res_frame != NULL);
|
||||
assert(req_bytes != NULL);
|
||||
|
||||
sg_req_transact(res_frame, req_bytes, req_nbytes, sg_led_dispatch, led);
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define sg_led_dprintfv(led, fmt, ap)
|
||||
#define sg_led_dprintf(led, fmt, ...)
|
||||
#else
|
||||
static void sg_led_dprintfv(
|
||||
const struct sg_led *led,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
dprintf("RGB LED %02x: ", led->addr);
|
||||
dprintfv(fmt, ap);
|
||||
}
|
||||
|
||||
static void sg_led_dprintf(
|
||||
const struct sg_led *led,
|
||||
const char *fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
sg_led_dprintfv(led, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#endif
|
||||
|
||||
static HRESULT sg_led_dispatch(
|
||||
void *ctx,
|
||||
const void *v_req,
|
||||
void *v_res)
|
||||
{
|
||||
const struct sg_led *led;
|
||||
const union sg_led_req_any *req;
|
||||
union sg_led_res_any *res;
|
||||
|
||||
led = ctx;
|
||||
req = v_req;
|
||||
res = v_res;
|
||||
|
||||
if (req->simple.hdr.addr != led->addr) {
|
||||
/* Not addressed to us, don't send a response */
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
switch (req->simple.hdr.cmd) {
|
||||
case SG_RGB_CMD_RESET:
|
||||
return sg_led_cmd_reset(led, &req->simple, &res->reset);
|
||||
|
||||
case SG_RGB_CMD_GET_INFO:
|
||||
return sg_led_cmd_get_info(led, &req->simple, &res->get_info);
|
||||
|
||||
case SG_RGB_CMD_SET_COLOR:
|
||||
return sg_led_cmd_set_color(led, &req->set_color);
|
||||
|
||||
default:
|
||||
sg_led_dprintf(led, "Unimpl command %02x\n", req->simple.hdr.cmd);
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT sg_led_cmd_reset(
|
||||
const struct sg_led *led,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_led_res_reset *res)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
sg_led_dprintf(led, "Reset\n");
|
||||
sg_res_init(&res->res, req, sizeof(res->payload));
|
||||
res->payload = 0;
|
||||
|
||||
if (led->ops->reset != NULL) {
|
||||
hr = led->ops->reset(led->ops_ctx);
|
||||
} else {
|
||||
hr = S_OK;
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
sg_led_dprintf(led, "led->ops->reset: Error %x\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_led_cmd_get_info(
|
||||
const struct sg_led *led,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_led_res_get_info *res)
|
||||
{
|
||||
sg_led_dprintf(led, "Get info\n");
|
||||
|
||||
const struct version_info *fw = &led_version[led->gen - 1];
|
||||
|
||||
sg_res_init(&res->res, req, fw->length);
|
||||
memcpy(res->payload, fw->version, fw->length);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_led_cmd_set_color(
|
||||
const struct sg_led *led,
|
||||
const struct sg_led_req_set_color *req)
|
||||
{
|
||||
if (req->req.payload_len != sizeof(req->payload)) {
|
||||
sg_led_dprintf(led, "%s: Payload size is incorrect\n", __func__);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
led->ops->set_color(
|
||||
led->ops_ctx,
|
||||
req->payload[0],
|
||||
req->payload[1],
|
||||
req->payload[2]);
|
||||
|
||||
fail:
|
||||
/* No response */
|
||||
return S_FALSE;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
struct sg_led_ops {
|
||||
HRESULT (*reset)(void *ctx);
|
||||
void (*set_color)(void *ctx, uint8_t r, uint8_t g, uint8_t b);
|
||||
};
|
||||
|
||||
struct sg_led {
|
||||
const struct sg_led_ops *ops;
|
||||
void *ops_ctx;
|
||||
uint8_t addr;
|
||||
unsigned int gen;
|
||||
};
|
||||
|
||||
void sg_led_init(
|
||||
struct sg_led *led,
|
||||
uint8_t addr,
|
||||
const struct sg_led_ops *ops,
|
||||
unsigned int gen,
|
||||
void *ctx);
|
||||
|
||||
void sg_led_transact(
|
||||
struct sg_led *led,
|
||||
struct iobuf *res_frame,
|
||||
const void *req_bytes,
|
||||
size_t req_nbytes);
|
||||
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
enum {
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x50,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME = 0x51,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA = 0x55,
|
||||
SG_NFC_CMD_TO_UPDATE_MODE = 0x60,
|
||||
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
};
|
||||
|
||||
struct sg_nfc_res_get_fw_version {
|
||||
struct sg_res_header res;
|
||||
char version[23];
|
||||
};
|
||||
|
||||
struct sg_nfc_res_get_hw_version {
|
||||
struct sg_res_header res;
|
||||
char version[23];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_set_key {
|
||||
struct sg_req_header req;
|
||||
uint8_t key[6];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_50 {
|
||||
struct sg_req_header req;
|
||||
uint8_t payload[6];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_poll_40 {
|
||||
struct sg_req_header req;
|
||||
uint8_t payload;
|
||||
};
|
||||
|
||||
struct sg_nfc_poll_mifare {
|
||||
uint8_t type;
|
||||
uint8_t id_len;
|
||||
uint32_t uid;
|
||||
};
|
||||
|
||||
struct sg_nfc_poll_felica {
|
||||
uint8_t type;
|
||||
uint8_t id_len;
|
||||
uint64_t IDm;
|
||||
uint64_t PMm;
|
||||
};
|
||||
|
||||
struct sg_nfc_res_poll {
|
||||
struct sg_res_header res;
|
||||
uint8_t count;
|
||||
uint8_t payload[250];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_select_tag {
|
||||
struct sg_res_header res;
|
||||
uint32_t uid;
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_read_block {
|
||||
struct sg_req_header req;
|
||||
struct {
|
||||
uint32_t uid;
|
||||
uint8_t block_no;
|
||||
} payload;
|
||||
};
|
||||
|
||||
struct sg_nfc_res_mifare_read_block {
|
||||
struct sg_res_header res;
|
||||
uint8_t block[16];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_felica_encap {
|
||||
struct sg_req_header req;
|
||||
uint64_t IDm;
|
||||
uint8_t payload[243];
|
||||
};
|
||||
|
||||
struct sg_nfc_res_felica_encap {
|
||||
struct sg_res_header res;
|
||||
uint8_t payload[250];
|
||||
};
|
||||
|
||||
union sg_nfc_req_any {
|
||||
uint8_t bytes[256];
|
||||
struct sg_req_header simple;
|
||||
struct sg_nfc_req_mifare_set_key mifare_set_key;
|
||||
struct sg_nfc_req_mifare_read_block mifare_read_block;
|
||||
struct sg_nfc_req_mifare_50 mifare_50;
|
||||
struct sg_nfc_req_poll_40 poll_40;
|
||||
struct sg_nfc_req_felica_encap felica_encap;
|
||||
};
|
||||
|
||||
union sg_nfc_res_any {
|
||||
uint8_t bytes[256];
|
||||
struct sg_res_header simple;
|
||||
struct sg_nfc_res_get_fw_version get_fw_version;
|
||||
struct sg_nfc_res_get_hw_version get_hw_version;
|
||||
struct sg_nfc_res_poll poll;
|
||||
struct sg_nfc_res_mifare_read_block mifare_read_block;
|
||||
struct sg_nfc_res_felica_encap felica_encap;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
@@ -0,0 +1,529 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "board/sg-cmd.h"
|
||||
#include "board/sg-nfc.h"
|
||||
#include "board/sg-nfc-cmd.h"
|
||||
|
||||
#include "iccard/aime.h"
|
||||
#include "iccard/felica.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
#include "util/slurp.h"
|
||||
|
||||
static HRESULT sg_nfc_dispatch(
|
||||
void *ctx,
|
||||
const void *v_req,
|
||||
void *v_res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_reset(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_get_fw_version(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_fw_version *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_get_hw_version(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_hw_version *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_poll(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_poll *res);
|
||||
|
||||
static HRESULT sg_nfc_poll_aime(
|
||||
struct sg_nfc *nfc,
|
||||
struct sg_nfc_poll_mifare *mifare);
|
||||
|
||||
static HRESULT sg_nfc_poll_felica(
|
||||
struct sg_nfc *nfc,
|
||||
struct sg_nfc_poll_felica *felica);
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_read_block(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_mifare_read_block *req,
|
||||
struct sg_nfc_res_mifare_read_block *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_felica_encap(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_felica_encap *req,
|
||||
struct sg_nfc_res_felica_encap *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_send_hex_data(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_dummy(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static const struct version_info hw_version[] = {
|
||||
{"TN32MSEC003S H/W Ver3.0", 23},
|
||||
{"837-15286", 9},
|
||||
{"837-15396", 9}
|
||||
};
|
||||
|
||||
static const struct version_info fw_version[] = {
|
||||
{"TN32MSEC003S F/W Ver1.2", 23},
|
||||
{"\x94", 1},
|
||||
{"\x94", 1}
|
||||
};
|
||||
|
||||
void sg_nfc_init(
|
||||
struct sg_nfc *nfc,
|
||||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
unsigned int gen,
|
||||
unsigned int proxy_flag,
|
||||
const wchar_t* authdata_path,
|
||||
void *ops_ctx)
|
||||
{
|
||||
assert(nfc != NULL);
|
||||
assert(ops != NULL);
|
||||
|
||||
nfc->ops = ops;
|
||||
nfc->ops_ctx = ops_ctx;
|
||||
nfc->addr = addr;
|
||||
nfc->gen = gen;
|
||||
nfc->proxy_flag = proxy_flag;
|
||||
nfc->authdata_path = authdata_path;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define sg_nfc_dprintfv(nfc, fmt, ap)
|
||||
#define sg_nfc_dprintf(nfc, fmt, ...)
|
||||
#else
|
||||
static void sg_nfc_dprintfv(
|
||||
const struct sg_nfc *nfc,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
dprintf("NFC %02x: ", nfc->addr);
|
||||
dprintfv(fmt, ap);
|
||||
}
|
||||
|
||||
static void sg_nfc_dprintf(
|
||||
const struct sg_nfc *nfc,
|
||||
const char *fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
sg_nfc_dprintfv(nfc, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#endif
|
||||
|
||||
void sg_nfc_transact(
|
||||
struct sg_nfc *nfc,
|
||||
struct iobuf *res_frame,
|
||||
const void *req_bytes,
|
||||
size_t req_nbytes)
|
||||
{
|
||||
assert(nfc != NULL);
|
||||
assert(res_frame != NULL);
|
||||
assert(req_bytes != NULL);
|
||||
|
||||
sg_req_transact(res_frame, req_bytes, req_nbytes, sg_nfc_dispatch, nfc);
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_dispatch(
|
||||
void *ctx,
|
||||
const void *v_req,
|
||||
void *v_res)
|
||||
{
|
||||
struct sg_nfc *nfc;
|
||||
const union sg_nfc_req_any *req;
|
||||
union sg_nfc_res_any *res;
|
||||
|
||||
nfc = ctx;
|
||||
req = v_req;
|
||||
res = v_res;
|
||||
|
||||
if (req->simple.hdr.addr != nfc->addr) {
|
||||
/* Not addressed to us, don't send a response */
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
switch (req->simple.hdr.cmd) {
|
||||
case SG_NFC_CMD_RESET:
|
||||
return sg_nfc_cmd_reset(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_GET_FW_VERSION:
|
||||
return sg_nfc_cmd_get_fw_version(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->get_fw_version);
|
||||
|
||||
case SG_NFC_CMD_GET_HW_VERSION:
|
||||
return sg_nfc_cmd_get_hw_version(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->get_hw_version);
|
||||
|
||||
case SG_NFC_CMD_POLL:
|
||||
return sg_nfc_cmd_poll(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->poll);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_READ_BLOCK:
|
||||
return sg_nfc_cmd_mifare_read_block(
|
||||
nfc,
|
||||
&req->mifare_read_block,
|
||||
&res->mifare_read_block);
|
||||
|
||||
case SG_NFC_CMD_FELICA_ENCAP:
|
||||
return sg_nfc_cmd_felica_encap(
|
||||
nfc,
|
||||
&req->felica_encap,
|
||||
&res->felica_encap);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME:
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA:
|
||||
case SG_NFC_CMD_SEND_HEX_DATA:
|
||||
return sg_nfc_cmd_send_hex_data(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_SELECT_TAG:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_AIME:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_BANA:
|
||||
case SG_NFC_CMD_RADIO_ON:
|
||||
case SG_NFC_CMD_RADIO_OFF:
|
||||
case SG_NFC_CMD_TO_UPDATE_MODE:
|
||||
return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple);
|
||||
|
||||
default:
|
||||
sg_nfc_dprintf(nfc, "Unimpl command %02x\n", req->simple.hdr.cmd);
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_reset(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
sg_nfc_dprintf(nfc, "Reset\n");
|
||||
sg_res_init(res, req, 0);
|
||||
res->status = 3;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_get_fw_version(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_fw_version *res)
|
||||
{
|
||||
const struct version_info *fw = &fw_version[nfc->gen - 1];
|
||||
|
||||
/* Dest version is not NUL terminated, this is intentional */
|
||||
sg_res_init(&res->res, req, fw->length);
|
||||
memcpy(res->version, fw->version, fw->length);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_get_hw_version(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_hw_version *res)
|
||||
{
|
||||
const struct version_info *hw = &hw_version[nfc->gen - 1];
|
||||
|
||||
/* Dest version is not NUL terminated, this is intentional */
|
||||
sg_res_init(&res->res, req, hw->length);
|
||||
memcpy(res->version, hw->version, hw->length);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_poll(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_poll *res)
|
||||
{
|
||||
struct sg_nfc_poll_mifare mifare;
|
||||
struct sg_nfc_poll_felica felica;
|
||||
HRESULT hr;
|
||||
|
||||
hr = nfc->ops->poll(nfc->ops_ctx);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = sg_nfc_poll_felica(nfc, &felica);
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE) {
|
||||
sg_res_init(&res->res, req, 1 + sizeof(felica));
|
||||
memcpy(res->payload, &felica, sizeof(felica));
|
||||
res->count = 1;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
hr = sg_nfc_poll_aime(nfc, &mifare);
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE) {
|
||||
sg_res_init(&res->res, req, 1 + sizeof(mifare));
|
||||
memcpy(res->payload, &mifare, sizeof(mifare));
|
||||
res->count = 1;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
sg_res_init(&res->res, req, 1);
|
||||
res->count = 0;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_poll_aime(
|
||||
struct sg_nfc *nfc,
|
||||
struct sg_nfc_poll_mifare *mifare)
|
||||
{
|
||||
uint8_t luid[10];
|
||||
HRESULT hr;
|
||||
|
||||
/* Call backend */
|
||||
|
||||
if (nfc->ops->get_aime_id != NULL) {
|
||||
hr = nfc->ops->get_aime_id(nfc->ops_ctx, luid, sizeof(luid));
|
||||
} else {
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
if (FAILED(hr) || hr == S_FALSE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
sg_nfc_dprintf(nfc, "AiMe card is present\n");
|
||||
|
||||
/* Construct response (use an arbitrary UID) */
|
||||
|
||||
mifare->type = 0x10;
|
||||
mifare->id_len = sizeof(mifare->uid);
|
||||
// mifare->uid = _byteswap_ulong(0x8FBECBFF);
|
||||
mifare->uid = _byteswap_ulong(0x01020304);
|
||||
|
||||
/* Initialize MIFARE IC emulator */
|
||||
|
||||
hr = aime_card_populate(&nfc->mifare, luid, sizeof(luid));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_poll_felica(
|
||||
struct sg_nfc *nfc,
|
||||
struct sg_nfc_poll_felica *felica)
|
||||
{
|
||||
uint64_t IDm;
|
||||
HRESULT hr;
|
||||
|
||||
/* Call backend */
|
||||
|
||||
if (nfc->ops->get_felica_id != NULL) {
|
||||
hr = nfc->ops->get_felica_id(nfc->ops_ctx, &IDm);
|
||||
} else {
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
if (FAILED(hr) || hr == S_FALSE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
sg_nfc_dprintf(nfc, "FeliCa card is present\n");
|
||||
|
||||
/* Construct poll response */
|
||||
|
||||
felica->type = 0x20;
|
||||
felica->id_len = sizeof(felica->IDm) + sizeof(felica->PMm);
|
||||
felica->IDm = _byteswap_uint64(IDm);
|
||||
felica->PMm = _byteswap_uint64(felica_get_amusement_ic_PMm());
|
||||
|
||||
/* Initialize FeliCa IC emulator */
|
||||
|
||||
nfc->felica.IDm = IDm;
|
||||
nfc->felica.PMm = felica_get_amusement_ic_PMm();
|
||||
nfc->felica.system_code = 0x88b4;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_read_block(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_mifare_read_block *req,
|
||||
struct sg_nfc_res_mifare_read_block *res)
|
||||
{
|
||||
uint32_t uid;
|
||||
|
||||
if (req->req.payload_len != sizeof(req->payload)) {
|
||||
sg_nfc_dprintf(nfc, "%s: Payload size is incorrect\n", __func__);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
uid = _byteswap_ulong(req->payload.uid);
|
||||
|
||||
sg_nfc_dprintf(nfc, "Read uid %08x block %i\n", uid, req->payload.block_no);
|
||||
|
||||
if (req->payload.block_no > 14) {
|
||||
sg_nfc_dprintf(nfc, "MIFARE block number out of range\n");
|
||||
|
||||
return E_FAIL;
|
||||
} else if (req->payload.block_no >= 5){ // emoney auth encrypted
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
char* auth;
|
||||
long size = wslurp(nfc->authdata_path, &auth, false);
|
||||
if (size < 0){
|
||||
sg_nfc_dprintf(nfc, "Failed to read %ls: %lx!\n", nfc->authdata_path, GetLastError());
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
if (req->payload.block_no == 6){
|
||||
offset = 16;
|
||||
} else if (req->payload.block_no == 8){
|
||||
offset = 32;
|
||||
} else if (req->payload.block_no == 9){
|
||||
offset = 48;
|
||||
} else if (req->payload.block_no == 10){
|
||||
offset = 64;
|
||||
} else if (req->payload.block_no == 12){
|
||||
offset = 82;
|
||||
} else if (req->payload.block_no == 13){
|
||||
offset = 98;
|
||||
} else if (req->payload.block_no == 14){
|
||||
offset = 114;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16 && offset + i < size; i++){
|
||||
res->block[i] = auth[offset + i];
|
||||
}
|
||||
|
||||
free(auth);
|
||||
|
||||
} else if (req->payload.block_no == 4){ // emoney auth plain
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
res->block[0] = 0x54; // header
|
||||
res->block[1] = 0x43;
|
||||
res->block[2] = nfc->proxy_flag; // 2 or 3 depending on game (useProxy in env.json)
|
||||
res->block[3] = 0x01; // unknown flag
|
||||
|
||||
} else { // read all other blocks normally
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
memcpy( res->block,
|
||||
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
|
||||
sizeof(res->block));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_felica_encap(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_felica_encap *req,
|
||||
struct sg_nfc_res_felica_encap *res)
|
||||
{
|
||||
struct const_iobuf f_req;
|
||||
struct iobuf f_res;
|
||||
HRESULT hr;
|
||||
|
||||
/* First byte of encapsulated request and response is a length byte
|
||||
(inclusive of itself). The FeliCa emulator expects its caller to handle
|
||||
that length byte on its behalf (we adopt the convention that the length
|
||||
byte is part of the FeliCa protocol's framing layer). */
|
||||
|
||||
if (req->req.payload_len != 8 + req->payload[0]) {
|
||||
sg_nfc_dprintf(
|
||||
nfc,
|
||||
"FeliCa encap payload length mismatch: sg %i != felica %i + 8",
|
||||
req->req.payload_len,
|
||||
req->payload[0]);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
f_req.bytes = req->payload;
|
||||
f_req.nbytes = req->payload[0];
|
||||
f_req.pos = 1;
|
||||
|
||||
f_res.bytes = res->payload;
|
||||
f_res.nbytes = sizeof(res->payload);
|
||||
f_res.pos = 1;
|
||||
|
||||
#if defined(LOG_NFC)
|
||||
dprintf("FELICA OUTBOUND:\n");
|
||||
dump_const_iobuf(&f_req);
|
||||
#endif
|
||||
|
||||
hr = felica_transact(&nfc->felica, &f_req, &f_res);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
sg_res_init(&res->res, &req->req, f_res.pos);
|
||||
res->payload[0] = f_res.pos;
|
||||
|
||||
#if defined(LOG_NFC)
|
||||
dprintf("FELICA INBOUND:\n");
|
||||
dump_iobuf(&f_res);
|
||||
#endif
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_send_hex_data(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
/* Firmware checksum length? */
|
||||
if (req->payload_len == 0x2b) {
|
||||
/* The firmware is identical flag? */
|
||||
res->status = 0x20;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_dummy(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "iccard/felica.h"
|
||||
#include "iccard/mifare.h"
|
||||
|
||||
struct sg_nfc_ops {
|
||||
HRESULT (*poll)(void *ctx);
|
||||
HRESULT (*get_aime_id)(void *ctx, uint8_t *luid, size_t nbytes);
|
||||
HRESULT (*get_felica_id)(void *ctx, uint64_t *IDm);
|
||||
|
||||
// TODO Banapass, AmuseIC
|
||||
};
|
||||
|
||||
struct sg_nfc {
|
||||
const struct sg_nfc_ops *ops;
|
||||
void *ops_ctx;
|
||||
uint8_t addr;
|
||||
unsigned int gen;
|
||||
unsigned int proxy_flag;
|
||||
struct felica felica;
|
||||
struct mifare mifare;
|
||||
const wchar_t* authdata_path;
|
||||
};
|
||||
|
||||
void sg_nfc_init(
|
||||
struct sg_nfc *nfc,
|
||||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
unsigned int gen,
|
||||
unsigned int proxy_flag,
|
||||
const wchar_t* authdata_path,
|
||||
void *ops_ctx);
|
||||
|
||||
void sg_nfc_transact(
|
||||
struct sg_nfc *nfc,
|
||||
struct iobuf *res_frame,
|
||||
const void *req_bytes,
|
||||
size_t req_nbytes);
|
||||
@@ -0,0 +1,207 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/aime-dll.h"
|
||||
#include "board/sg-led.h"
|
||||
#include "board/sg-nfc.h"
|
||||
#include "board/sg-reader.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/uart.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
static HRESULT sg_reader_handle_irp(struct irp *irp);
|
||||
static HRESULT sg_reader_handle_irp_locked(struct irp *irp);
|
||||
static HRESULT sg_reader_nfc_poll(void *ctx);
|
||||
static HRESULT sg_reader_nfc_get_aime_id(
|
||||
void *ctx,
|
||||
uint8_t *luid,
|
||||
size_t luid_size);
|
||||
static HRESULT sg_reader_nfc_get_felica_id(void *ctx, uint64_t *IDm);
|
||||
static void sg_reader_led_set_color(void *ctx, uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
static const struct sg_nfc_ops sg_reader_nfc_ops = {
|
||||
.poll = sg_reader_nfc_poll,
|
||||
.get_aime_id = sg_reader_nfc_get_aime_id,
|
||||
.get_felica_id = sg_reader_nfc_get_felica_id,
|
||||
};
|
||||
|
||||
static const struct sg_led_ops sg_reader_led_ops = {
|
||||
.set_color = sg_reader_led_set_color,
|
||||
};
|
||||
|
||||
static CRITICAL_SECTION sg_reader_lock;
|
||||
static bool sg_reader_started;
|
||||
static HRESULT sg_reader_start_hr;
|
||||
static struct uart sg_reader_uart;
|
||||
static uint8_t sg_reader_written_bytes[520];
|
||||
static uint8_t sg_reader_readable_bytes[520];
|
||||
static struct sg_nfc sg_reader_nfc;
|
||||
static struct sg_led sg_reader_led;
|
||||
|
||||
HRESULT sg_reader_hook_init(
|
||||
const struct aime_config *cfg,
|
||||
unsigned int default_port_no,
|
||||
unsigned int gen,
|
||||
HINSTANCE self)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(self != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
hr = aime_dll_init(&cfg->dll, self);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
unsigned int port_no = cfg->port_no;
|
||||
if (port_no == 0){
|
||||
port_no = default_port_no;
|
||||
}
|
||||
|
||||
if (cfg->gen != 0) {
|
||||
gen = cfg->gen;
|
||||
}
|
||||
|
||||
if (gen < 1 || gen > 3) {
|
||||
dprintf("NFC Assembly: Invalid reader generation: %u\n", gen);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, gen, cfg->proxy_flag, cfg->authdata_path, NULL);
|
||||
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, gen, NULL);
|
||||
|
||||
InitializeCriticalSection(&sg_reader_lock);
|
||||
|
||||
if (!cfg->high_baudrate) {
|
||||
sg_reader_uart.baud.BaudRate = 38400;
|
||||
}
|
||||
|
||||
dprintf("NFC Assembly: enabling (port=%d)\n", port_no);
|
||||
uart_init(&sg_reader_uart, port_no);
|
||||
sg_reader_uart.written.bytes = sg_reader_written_bytes;
|
||||
sg_reader_uart.written.nbytes = sizeof(sg_reader_written_bytes);
|
||||
sg_reader_uart.readable.bytes = sg_reader_readable_bytes;
|
||||
sg_reader_uart.readable.nbytes = sizeof(sg_reader_readable_bytes);
|
||||
|
||||
return iohook_push_handler(sg_reader_handle_irp);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
if (!uart_match_irp(&sg_reader_uart, irp)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
EnterCriticalSection(&sg_reader_lock);
|
||||
hr = sg_reader_handle_irp_locked(irp);
|
||||
LeaveCriticalSection(&sg_reader_lock);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_handle_irp_locked(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
#if defined(LOG_NFC)
|
||||
if (irp->op == IRP_OP_WRITE) {
|
||||
dprintf("WRITE:\n");
|
||||
dump_const_iobuf(&irp->write);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LOG_NFC)
|
||||
if (irp->op == IRP_OP_READ) {
|
||||
dprintf("READ:\n");
|
||||
dump_iobuf(&sg_reader_uart.readable);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (irp->op == IRP_OP_OPEN) {
|
||||
/* Unfortunately the card reader UART gets opened and closed
|
||||
repeatedly */
|
||||
|
||||
if (!sg_reader_started) {
|
||||
dprintf("NFC Assembly: Starting backend DLL\n");
|
||||
hr = aime_dll.init();
|
||||
|
||||
sg_reader_started = true;
|
||||
sg_reader_start_hr = hr;
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("NFC Assembly: Backend error: %x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
} else {
|
||||
hr = sg_reader_start_hr;
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(&sg_reader_uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
sg_nfc_transact(
|
||||
&sg_reader_nfc,
|
||||
&sg_reader_uart.readable,
|
||||
sg_reader_uart.written.bytes,
|
||||
sg_reader_uart.written.pos);
|
||||
|
||||
sg_led_transact(
|
||||
&sg_reader_led,
|
||||
&sg_reader_uart.readable,
|
||||
sg_reader_uart.written.bytes,
|
||||
sg_reader_uart.written.pos);
|
||||
|
||||
sg_reader_uart.written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_poll(void *ctx)
|
||||
{
|
||||
return aime_dll.nfc_poll(0);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_get_aime_id(
|
||||
void *ctx,
|
||||
uint8_t *luid,
|
||||
size_t luid_size)
|
||||
{
|
||||
return aime_dll.nfc_get_aime_id(0, luid, luid_size);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_get_felica_id(void *ctx, uint64_t *IDm)
|
||||
{
|
||||
return aime_dll.nfc_get_felica_id(0, IDm);
|
||||
}
|
||||
|
||||
static void sg_reader_led_set_color(void *ctx, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
aime_dll.led_set_color(0, r, g, b);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "board/aime-dll.h"
|
||||
|
||||
struct aime_config {
|
||||
struct aime_dll_config dll;
|
||||
bool enable;
|
||||
unsigned int port_no;
|
||||
bool high_baudrate;
|
||||
unsigned int gen;
|
||||
unsigned int proxy_flag;
|
||||
wchar_t authdata_path[MAX_PATH];
|
||||
};
|
||||
|
||||
HRESULT sg_reader_hook_init(
|
||||
const struct aime_config *cfg,
|
||||
unsigned int default_port_no,
|
||||
unsigned int gen,
|
||||
HINSTANCE self);
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/slider-frame.h"
|
||||
|
||||
enum {
|
||||
SLIDER_CMD_AUTO_SCAN = 0x01,
|
||||
SLIDER_CMD_SET_LED = 0x02,
|
||||
SLIDER_CMD_AUTO_SCAN_START = 0x03,
|
||||
SLIDER_CMD_AUTO_SCAN_STOP = 0x04,
|
||||
SLIDER_CMD_DIVA_UNK_09 = 0x09,
|
||||
SLIDER_CMD_DIVA_UNK_0A = 0x0A,
|
||||
SLIDER_CMD_RESET = 0x10,
|
||||
SLIDER_CMD_GET_BOARD_INFO = 0xF0,
|
||||
};
|
||||
|
||||
struct slider_req_set_led {
|
||||
struct slider_hdr hdr;
|
||||
struct {
|
||||
uint8_t unk; /* 0x28, decimal 40. meaning unknown. */
|
||||
uint8_t rgb[96];
|
||||
} payload;
|
||||
};
|
||||
|
||||
union slider_req_any {
|
||||
struct slider_hdr hdr;
|
||||
struct slider_req_set_led set_led;
|
||||
uint8_t bytes[260];
|
||||
};
|
||||
|
||||
struct slider_resp_get_board_info {
|
||||
struct slider_hdr hdr;
|
||||
char version[32];
|
||||
};
|
||||
|
||||
struct slider_resp_auto_scan {
|
||||
struct slider_hdr hdr;
|
||||
uint8_t pressure[32];
|
||||
};
|
||||
@@ -0,0 +1,180 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/slider-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
static void slider_frame_sync(struct iobuf *src);
|
||||
static HRESULT slider_frame_accept(const struct iobuf *dest);
|
||||
static HRESULT slider_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
[0] Sync byte (0xFF)
|
||||
[1] Command
|
||||
[2] Payload size
|
||||
... Payload
|
||||
[n] Checksum: Negate the sum of all prior bytes (including sync byte!)
|
||||
|
||||
Byte stuffing:
|
||||
|
||||
0xFD is an escape byte. Un-escape the subsequent byte by adding 1. */
|
||||
|
||||
static void slider_frame_sync(struct iobuf *src)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < src->pos && src->bytes[i] != 0xFF ; i++);
|
||||
|
||||
src->pos -= i;
|
||||
memmove(&src->bytes[0], &src->bytes[i], i);
|
||||
}
|
||||
|
||||
static HRESULT slider_frame_accept(const struct iobuf *dest)
|
||||
{
|
||||
uint8_t checksum;
|
||||
size_t i;
|
||||
|
||||
if (dest->pos < 2 || dest->pos != dest->bytes[2] + 4) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
|
||||
for (i = 0 ; i < dest->pos ; i++) {
|
||||
checksum += dest->bytes[i];
|
||||
}
|
||||
|
||||
if (checksum != 0) {
|
||||
return HRESULT_FROM_WIN32(ERROR_CRC);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT slider_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);
|
||||
|
||||
slider_frame_sync(src);
|
||||
|
||||
dest->pos = 0;
|
||||
escape = false;
|
||||
|
||||
for (i = 0, hr = S_FALSE ; i < src->pos && hr == S_FALSE ; i++) {
|
||||
/* Step the FSM to unstuff another byte */
|
||||
|
||||
byte = src->bytes[i];
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
} else if (i == 0) {
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
} else if (byte == 0xFF) {
|
||||
hr = E_FAIL;
|
||||
} else if (byte == 0xFD) {
|
||||
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 = slider_frame_accept(dest);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle FSM terminal state */
|
||||
|
||||
if (hr != S_FALSE) {
|
||||
/* Frame was either accepted or rejected, remove it from src */
|
||||
memmove(&src->bytes[0], &src->bytes[i], src->pos - i);
|
||||
src->pos -= i;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT slider_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] == 0xFF && src[2] + 3 == nbytes);
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = 0xFF;
|
||||
checksum = 0xFF;
|
||||
|
||||
for (i = 1 ; i < nbytes ; i++) {
|
||||
byte = src[i];
|
||||
checksum += byte;
|
||||
|
||||
hr = slider_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return slider_frame_encode_byte(dest, -checksum);
|
||||
}
|
||||
|
||||
static HRESULT slider_frame_encode_byte(struct iobuf *dest, uint8_t byte)
|
||||
{
|
||||
if (byte == 0xFF || byte == 0xFD) {
|
||||
if (dest->pos + 2 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = 0xFD;
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
enum {
|
||||
SLIDER_FRAME_SYNC = 0xFF,
|
||||
};
|
||||
|
||||
struct slider_hdr {
|
||||
uint8_t sync;
|
||||
uint8_t cmd;
|
||||
uint8_t nbytes;
|
||||
};
|
||||
|
||||
HRESULT slider_frame_decode(struct iobuf *dest, struct iobuf *src);
|
||||
|
||||
HRESULT slider_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes);
|
||||
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/vfd-frame.h"
|
||||
|
||||
enum {
|
||||
VFD_CMD_GET_VERSION = 0x5B,
|
||||
VFD_CMD_RESET = 0x0B,
|
||||
VFD_CMD_CLEAR_SCREEN = 0x0C,
|
||||
VFD_CMD_SET_BRIGHTNESS = 0x20,
|
||||
VFD_CMD_SET_SCREEN_ON = 0x21,
|
||||
VFD_CMD_SET_H_SCROLL = 0x22,
|
||||
VFD_CMD_DRAW_IMAGE = 0x2E,
|
||||
VFD_CMD_SET_CURSOR = 0x30,
|
||||
VFD_CMD_SET_ENCODING = 0x32,
|
||||
VFD_CMD_SET_TEXT_WND = 0x40,
|
||||
VFD_CMD_SET_TEXT_SPEED = 0x41,
|
||||
VFD_CMD_WRITE_TEXT = 0x50,
|
||||
VFD_CMD_ENABLE_SCROLL = 0x51,
|
||||
VFD_CMD_DISABLE_SCROLL = 0x52,
|
||||
VFD_CMD_ROTATE = 0x5D,
|
||||
VFD_CMD_CREATE_CHAR = 0xA3,
|
||||
VFD_CMD_CREATE_CHAR2 = 0xA4,
|
||||
};
|
||||
|
||||
enum {
|
||||
VFD_ENC_GB2312 = 0,
|
||||
VFD_ENC_BIG5 = 1,
|
||||
VFD_ENC_SHIFT_JIS = 2,
|
||||
VFD_ENC_KSC5601 = 3,
|
||||
VFD_ENC_MAX = 3,
|
||||
};
|
||||
|
||||
struct vfd_req_hdr {
|
||||
uint8_t sync;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
struct vfd_req_any {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t payload[2054];
|
||||
};
|
||||
|
||||
struct vfd_req_board_info {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t unk1;
|
||||
};
|
||||
|
||||
struct vfd_resp_board_info { // \x0201.20\x03
|
||||
uint8_t unk1;
|
||||
char version[5];
|
||||
uint8_t unk2;
|
||||
};
|
||||
|
||||
struct vfd_req_reset {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_cls {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_brightness {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t brightness;
|
||||
};
|
||||
|
||||
struct vfd_req_power {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t power_state;
|
||||
};
|
||||
|
||||
struct vfd_req_hscroll {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t x_pos;
|
||||
};
|
||||
|
||||
struct vfd_req_draw {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x0;
|
||||
uint8_t y0;
|
||||
uint16_t x1;
|
||||
uint8_t y1;
|
||||
uint8_t image[2048];
|
||||
};
|
||||
|
||||
struct vfd_req_cursor {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x;
|
||||
uint8_t y;
|
||||
};
|
||||
|
||||
struct vfd_req_encoding {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t encoding;
|
||||
};
|
||||
|
||||
struct vfd_req_wnd {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x0;
|
||||
uint8_t y0;
|
||||
uint16_t x1;
|
||||
uint8_t y1;
|
||||
};
|
||||
|
||||
struct vfd_req_speed {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t encoding;
|
||||
};
|
||||
|
||||
struct vfd_req_scroll {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_rotate {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t unk1;
|
||||
};
|
||||
|
||||
struct vfd_req_create_char {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t type;
|
||||
uint8_t pixels[32];
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define SUPER_VERBOSE 1
|
||||
|
||||
#include "board/vfd-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
REQUEST:
|
||||
[0] Sync byte (0x1A or 0x1B)
|
||||
[1] Packet ID
|
||||
[2...n-1] Data/payload
|
||||
|
||||
--- OR ---
|
||||
|
||||
if no sync byte is given, plain static text in the currently configured encoding is expected.
|
||||
|
||||
RESPONSE:
|
||||
This thing never responds, unless it's VFD_CMD_GET_VERSION
|
||||
*/
|
||||
|
||||
bool vfd_frame_sync(struct const_iobuf *src) {
|
||||
return src->bytes[src->pos] == VFD_SYNC_BYTE || src->bytes[src->pos] == VFD_SYNC_BYTE2;
|
||||
}
|
||||
|
||||
HRESULT vfd_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes) {
|
||||
const uint8_t *src;
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(ptr != NULL);
|
||||
|
||||
src = ptr;
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("VFD: RX Buffer:\n");
|
||||
#endif
|
||||
|
||||
for (i = 1; i < nbytes; i++) {
|
||||
byte = src[i];
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("%02x ", byte);
|
||||
#endif
|
||||
|
||||
hr = vfd_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("\n");
|
||||
#endif
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte) {
|
||||
if (dest->pos + 1 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
enum {
|
||||
VFD_SYNC_BYTE = 0x1B,
|
||||
VFD_SYNC_BYTE2 = 0x1A,
|
||||
};
|
||||
|
||||
bool vfd_frame_sync(struct const_iobuf *src);
|
||||
|
||||
HRESULT vfd_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes);
|
||||
@@ -0,0 +1,399 @@
|
||||
/* This is some sort of LCD display found on various cabinets. It is driven
|
||||
directly by amdaemon, and it has something to do with displaying the status
|
||||
of electronic payments.
|
||||
|
||||
Part number in schematics is "VFD GP1232A02A FUTABA". */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/vfd.h"
|
||||
#include "board/vfd-cmd.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/uart.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
#define SUPER_VERBOSE 0
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp);
|
||||
|
||||
static struct uart vfd_uart;
|
||||
static uint8_t vfd_written[4096];
|
||||
static uint8_t vfd_readable[4096];
|
||||
|
||||
static int encoding = VFD_ENC_SHIFT_JIS;
|
||||
|
||||
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
|
||||
static bool utf_enabled;
|
||||
|
||||
HRESULT vfd_hook_init(struct vfd_config *cfg, unsigned int default_port_no)
|
||||
{
|
||||
if (!cfg->enable){
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
utf_enabled = cfg->utf_conversion;
|
||||
|
||||
unsigned int port_no = cfg->port_no;
|
||||
if (port_no == 0){
|
||||
port_no = default_port_no;
|
||||
}
|
||||
|
||||
dprintf("VFD: enabling (port=%d)\n", port_no);
|
||||
uart_init(&vfd_uart, port_no);
|
||||
vfd_uart.written.bytes = vfd_written;
|
||||
vfd_uart.written.nbytes = sizeof(vfd_written);
|
||||
vfd_uart.readable.bytes = vfd_readable;
|
||||
vfd_uart.readable.nbytes = sizeof(vfd_readable);
|
||||
|
||||
return iohook_push_handler(vfd_handle_irp);
|
||||
}
|
||||
|
||||
|
||||
const char* get_encoding_name(int b){
|
||||
switch (b){
|
||||
case 0: return "gb2312";
|
||||
case 1: return "big5";
|
||||
case 2: return "shift-jis";
|
||||
case 3: return "ks_c_5601-1987";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void print_vfd_text(const char* str, int len){
|
||||
|
||||
if (utf_enabled){
|
||||
|
||||
wchar_t encoded[1024];
|
||||
memset(encoded, 0, 1024 * sizeof(wchar_t));
|
||||
|
||||
int codepage = 0;
|
||||
if (encoding == VFD_ENC_GB2312){
|
||||
codepage = 936;
|
||||
} else if (encoding == VFD_ENC_BIG5){
|
||||
codepage = 950;
|
||||
} else if (encoding == VFD_ENC_SHIFT_JIS){
|
||||
codepage = 932;
|
||||
} else if (encoding == VFD_ENC_KSC5601) {
|
||||
codepage = 949;
|
||||
}
|
||||
|
||||
if (!MultiByteToWideChar(codepage, MB_USEGLYPHCHARS, str, len, encoded, 1024)){
|
||||
dprintf("VFD: Text conversion failed: %ld", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("VFD: Text: %ls\n", encoded);
|
||||
} else {
|
||||
|
||||
dprintf("VFD: Text: %s\n", str);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
if (!uart_match_irp(&vfd_uart, irp)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
if (irp->op == IRP_OP_OPEN){
|
||||
dprintf("VFD: Open\n");
|
||||
} else if (irp->op == IRP_OP_CLOSE){
|
||||
dprintf("VFD: Close\n");
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(&vfd_uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("VFD TX:\n");
|
||||
dump_iobuf(&vfd_uart.written);
|
||||
#endif
|
||||
|
||||
struct const_iobuf reader;
|
||||
iobuf_flip(&reader, &vfd_uart.written);
|
||||
|
||||
struct iobuf* writer = &vfd_uart.readable;
|
||||
for (; reader.pos < reader.nbytes ; ){
|
||||
|
||||
if (vfd_frame_sync(&reader)) {
|
||||
|
||||
reader.pos++; // get the sync byte out of the way
|
||||
|
||||
uint8_t cmd;
|
||||
iobuf_read_8(&reader, &cmd);
|
||||
|
||||
if (cmd == VFD_CMD_GET_VERSION) {
|
||||
hr = vfd_handle_get_version(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_RESET) {
|
||||
hr = vfd_handle_reset(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CLEAR_SCREEN) {
|
||||
hr = vfd_handle_clear_screen(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_BRIGHTNESS) {
|
||||
hr = vfd_handle_set_brightness(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_SCREEN_ON) {
|
||||
hr = vfd_handle_set_screen_on(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_H_SCROLL) {
|
||||
hr = vfd_handle_set_h_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_DRAW_IMAGE) {
|
||||
hr = vfd_handle_draw_image(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_CURSOR) {
|
||||
hr = vfd_handle_set_cursor(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_ENCODING) {
|
||||
hr = vfd_handle_set_encoding(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_TEXT_WND) {
|
||||
hr = vfd_handle_set_text_wnd(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_TEXT_SPEED) {
|
||||
hr = vfd_handle_set_text_speed(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_WRITE_TEXT) {
|
||||
hr = vfd_handle_write_text(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_ENABLE_SCROLL) {
|
||||
hr = vfd_handle_enable_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_DISABLE_SCROLL) {
|
||||
hr = vfd_handle_disable_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_ROTATE) {
|
||||
hr = vfd_handle_rotate(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CREATE_CHAR) {
|
||||
hr = vfd_handle_create_char(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CREATE_CHAR2) {
|
||||
hr = vfd_handle_create_char2(&reader, writer, &vfd_uart);
|
||||
} else {
|
||||
dprintf("VFD: Unknown command 0x%x\n", cmd);
|
||||
dump_const_iobuf(&reader);
|
||||
hr = S_FALSE;
|
||||
}
|
||||
} else {
|
||||
|
||||
// if no sync byte is sent, we are just getting plain text...
|
||||
|
||||
if (reader.pos < reader.nbytes){
|
||||
int len = 0;
|
||||
|
||||
// read chars until we hit a new sync byte or the data ends
|
||||
while (reader.pos + len + 1 < reader.nbytes && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE2){
|
||||
len++;
|
||||
}
|
||||
|
||||
char* str = malloc(len);
|
||||
memset(str, 0, len);
|
||||
iobuf_read(&reader, str, len);
|
||||
print_vfd_text(str, len);
|
||||
free(str);
|
||||
|
||||
reader.pos += len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(hr)){
|
||||
return hr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vfd_uart.written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Get Version\n");
|
||||
|
||||
struct vfd_resp_board_info resp;
|
||||
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
resp.unk1 = 2;
|
||||
strcpy(resp.version, "01.20");
|
||||
resp.unk2 = 1;
|
||||
|
||||
return vfd_frame_encode(writer, &resp, sizeof(resp));
|
||||
}
|
||||
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Reset\n");
|
||||
|
||||
encoding = VFD_ENC_SHIFT_JIS;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Clear Screen\n");
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
if (b > 4){
|
||||
dprintf("VFD: Brightness, invalid argument\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dprintf("VFD: Brightness, %d\n", b);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
if (b > 1){
|
||||
dprintf("VFD: Screen Power, invalid argument\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dprintf("VFD: Screen Power, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t x;
|
||||
iobuf_read_8(reader, &x);
|
||||
|
||||
dprintf("VFD: Horizontal Scroll, X=%d\n", x);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
int w, h;
|
||||
uint16_t x0, x1;
|
||||
uint8_t y0, y1;
|
||||
uint8_t image[2048];
|
||||
|
||||
iobuf_read_be16(reader, &x0);
|
||||
iobuf_read_8(reader, &y0);
|
||||
iobuf_read_be16(reader, &x1);
|
||||
iobuf_read_8(reader, &y1);
|
||||
w = x1 - x0;
|
||||
h = y1 - y0;
|
||||
iobuf_read(reader, image, w*h);
|
||||
|
||||
dprintf("VFD: Draw image, %dx%d\n", w, h);
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint16_t x;
|
||||
uint8_t y;
|
||||
|
||||
iobuf_read_be16(reader, &x);
|
||||
iobuf_read_8(reader, &y);
|
||||
|
||||
dprintf("VFD: Set Cursor, x=%d,y=%d\n", x, y);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Set Encoding, %d (%s)\n", b, get_encoding_name(b));
|
||||
|
||||
if (b < 0 || b > VFD_ENC_MAX){
|
||||
dprintf("Invalid encoding specified\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
encoding = b;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint16_t x0, x1;
|
||||
uint8_t y0, y1;
|
||||
|
||||
iobuf_read_be16(reader, &x0);
|
||||
iobuf_read_8(reader, &y0);
|
||||
iobuf_read_be16(reader, &x1);
|
||||
iobuf_read_8(reader, &y1);
|
||||
|
||||
dprintf("VFD: Set Text Window, p0:%d,%d, p1:%d,%d\n", x0, y0, x1, y1);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Set Text Speed, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t len;
|
||||
iobuf_read_8(reader, &len);
|
||||
|
||||
char* str = malloc(len);
|
||||
iobuf_read(reader, str, len);
|
||||
|
||||
print_vfd_text(str, len);
|
||||
free(str);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Enable Scrolling\n");
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Disable Scrolling\n");
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Rotate, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
char buf[32];
|
||||
|
||||
iobuf_read(reader, buf, 32);
|
||||
|
||||
dprintf("VFD: Create character, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b, b2;
|
||||
iobuf_read_8(reader, &b);
|
||||
iobuf_read_8(reader, &b2);
|
||||
char buf[16];
|
||||
|
||||
iobuf_read(reader, buf, 16);
|
||||
|
||||
dprintf("VFD: Create character, %d, %d\n", b, b2);
|
||||
return S_FALSE;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
struct vfd_config {
|
||||
bool enable;
|
||||
unsigned int port_no;
|
||||
bool utf_conversion;
|
||||
};
|
||||
|
||||
|
||||
HRESULT vfd_hook_init(struct vfd_config *cfg, unsigned int default_port_no);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "gfxhook/config.h"
|
||||
|
||||
void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"gfx", L"enable", 1, filename);
|
||||
cfg->windowed = GetPrivateProfileIntW(L"gfx", L"windowed", 0, filename);
|
||||
cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename);
|
||||
cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename);
|
||||
cfg->dpiAware = GetPrivateProfileIntW(L"gfx", L"dpiAware", 1, filename);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
|
||||
void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename);
|
||||
@@ -0,0 +1,195 @@
|
||||
#include <windows.h>
|
||||
#include <dxgi.h>
|
||||
#include <d3d11.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gfxhook/d3d11.h"
|
||||
#include "gfxhook/gfx.h"
|
||||
#include "gfxhook/util.h"
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/dll.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
typedef HRESULT (WINAPI *D3D11CreateDevice_t)(
|
||||
IDXGIAdapter *pAdapter,
|
||||
D3D_DRIVER_TYPE DriverType,
|
||||
HMODULE Software,
|
||||
UINT Flags,
|
||||
const D3D_FEATURE_LEVEL *ppFeatureLevels,
|
||||
UINT FeatureLevels,
|
||||
UINT SDKVersion,
|
||||
ID3D11Device **ppDevice,
|
||||
D3D_FEATURE_LEVEL *pFeatureLevel,
|
||||
ID3D11DeviceContext **ppImmediateContext);
|
||||
typedef HRESULT (WINAPI *D3D11CreateDeviceAndSwapChain_t)(
|
||||
IDXGIAdapter *pAdapter,
|
||||
D3D_DRIVER_TYPE DriverType,
|
||||
HMODULE Software,
|
||||
UINT Flags,
|
||||
const D3D_FEATURE_LEVEL *ppFeatureLevels,
|
||||
UINT FeatureLevels,
|
||||
UINT SDKVersion,
|
||||
const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
|
||||
IDXGISwapChain **ppSwapChain,
|
||||
ID3D11Device **ppDevice,
|
||||
D3D_FEATURE_LEVEL *pFeatureLevel,
|
||||
ID3D11DeviceContext **ppImmediateContext);
|
||||
|
||||
static struct gfx_config gfx_config;
|
||||
static D3D11CreateDevice_t next_D3D11CreateDevice;
|
||||
static D3D11CreateDeviceAndSwapChain_t next_D3D11CreateDeviceAndSwapChain;
|
||||
|
||||
static const struct hook_symbol d3d11_hooks[] = {
|
||||
{
|
||||
.name = "D3D11CreateDevice",
|
||||
.patch = D3D11CreateDevice,
|
||||
.link = (void **) &next_D3D11CreateDevice,
|
||||
}, {
|
||||
.name = "D3D11CreateDeviceAndSwapChain",
|
||||
.patch = D3D11CreateDeviceAndSwapChain,
|
||||
.link = (void **) &next_D3D11CreateDeviceAndSwapChain,
|
||||
},
|
||||
};
|
||||
|
||||
void gfx_d3d11_hook_init(const struct gfx_config *cfg, HINSTANCE self)
|
||||
{
|
||||
HMODULE d3d11;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&gfx_config, cfg, sizeof(*cfg));
|
||||
hook_table_apply(NULL, "d3d11.dll", d3d11_hooks, _countof(d3d11_hooks));
|
||||
|
||||
if (next_D3D11CreateDevice == NULL || next_D3D11CreateDeviceAndSwapChain == NULL) {
|
||||
d3d11 = LoadLibraryW(L"d3d11.dll");
|
||||
|
||||
if (d3d11 == NULL) {
|
||||
dprintf("D3D11: d3d11.dll not found or failed initialization\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (next_D3D11CreateDevice == NULL) {
|
||||
next_D3D11CreateDevice = (D3D11CreateDevice_t) GetProcAddress(
|
||||
d3d11,
|
||||
"D3D11CreateDevice");
|
||||
}
|
||||
if (next_D3D11CreateDeviceAndSwapChain == NULL) {
|
||||
next_D3D11CreateDeviceAndSwapChain = (D3D11CreateDeviceAndSwapChain_t) GetProcAddress(
|
||||
d3d11,
|
||||
"D3D11CreateDeviceAndSwapChain");
|
||||
}
|
||||
|
||||
if (next_D3D11CreateDevice == NULL) {
|
||||
dprintf("D3D11: D3D11CreateDevice not found in loaded d3d11.dll\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
if (next_D3D11CreateDeviceAndSwapChain == NULL) {
|
||||
dprintf("D3D11: D3D11CreateDeviceAndSwapChain not found in loaded d3d11.dll\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (self != NULL) {
|
||||
dll_hook_push(self, L"d3d11.dll");
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (d3d11 != NULL) {
|
||||
FreeLibrary(d3d11);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT WINAPI D3D11CreateDevice(
|
||||
IDXGIAdapter *pAdapter,
|
||||
D3D_DRIVER_TYPE DriverType,
|
||||
HMODULE Software,
|
||||
UINT Flags,
|
||||
const D3D_FEATURE_LEVEL *ppFeatureLevels,
|
||||
UINT FeatureLevels,
|
||||
UINT SDKVersion,
|
||||
ID3D11Device **ppDevice,
|
||||
D3D_FEATURE_LEVEL *pFeatureLevel,
|
||||
ID3D11DeviceContext **ppImmediateContext)
|
||||
{
|
||||
dprintf("D3D11: D3D11CreateDevice hook hit\n");
|
||||
|
||||
return next_D3D11CreateDevice(
|
||||
pAdapter,
|
||||
DriverType,
|
||||
Software,
|
||||
Flags,
|
||||
ppFeatureLevels,
|
||||
FeatureLevels,
|
||||
SDKVersion,
|
||||
ppDevice,
|
||||
pFeatureLevel,
|
||||
ppImmediateContext);
|
||||
}
|
||||
|
||||
HRESULT WINAPI D3D11CreateDeviceAndSwapChain(
|
||||
IDXGIAdapter *pAdapter,
|
||||
D3D_DRIVER_TYPE DriverType,
|
||||
HMODULE Software,
|
||||
UINT Flags,
|
||||
const D3D_FEATURE_LEVEL *ppFeatureLevels,
|
||||
UINT FeatureLevels,
|
||||
UINT SDKVersion,
|
||||
const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
|
||||
IDXGISwapChain **ppSwapChain,
|
||||
ID3D11Device **ppDevice,
|
||||
D3D_FEATURE_LEVEL *pFeatureLevel,
|
||||
ID3D11DeviceContext **ppImmediateContext)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC *desc;
|
||||
HWND hwnd;
|
||||
UINT width;
|
||||
UINT height;
|
||||
|
||||
dprintf("D3D11: D3D11CreateDeviceAndSwapChain hook hit\n");
|
||||
|
||||
desc = (DXGI_SWAP_CHAIN_DESC *) pSwapChainDesc;
|
||||
|
||||
if (desc != NULL) {
|
||||
desc->Windowed = gfx_config.windowed;
|
||||
|
||||
hwnd = desc->OutputWindow;
|
||||
width = desc->BufferDesc.Width;
|
||||
height = desc->BufferDesc.Height;
|
||||
} else {
|
||||
hwnd = NULL;
|
||||
}
|
||||
|
||||
if (hwnd != NULL) {
|
||||
gfx_util_ensure_win_visible(hwnd);
|
||||
gfx_util_borderless_fullscreen_windowed(hwnd, width, height);
|
||||
}
|
||||
|
||||
return next_D3D11CreateDeviceAndSwapChain(
|
||||
pAdapter,
|
||||
DriverType,
|
||||
Software,
|
||||
Flags,
|
||||
ppFeatureLevels,
|
||||
FeatureLevels,
|
||||
SDKVersion,
|
||||
pSwapChainDesc,
|
||||
ppSwapChain,
|
||||
ppDevice,
|
||||
pFeatureLevel,
|
||||
ppImmediateContext);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
|
||||
void gfx_d3d11_hook_init(const struct gfx_config *cfg, HINSTANCE self);
|
||||
@@ -0,0 +1,261 @@
|
||||
#include <windows.h>
|
||||
#include <d3d9.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hook/com-proxy.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/dll.h"
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
#include "gfxhook/util.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
typedef IDirect3D9 * (WINAPI *Direct3DCreate9_t)(UINT sdk_ver);
|
||||
typedef HRESULT (WINAPI *Direct3DCreate9Ex_t)(UINT sdk_ver, IDirect3D9Ex **d3d9ex);
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_IDirect3D9_CreateDevice(
|
||||
IDirect3D9 *self,
|
||||
UINT adapter,
|
||||
D3DDEVTYPE type,
|
||||
HWND hwnd,
|
||||
DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp,
|
||||
IDirect3DDevice9 **pdev);
|
||||
static HRESULT STDMETHODCALLTYPE my_IDirect3D9Ex_CreateDevice(
|
||||
IDirect3D9Ex *self,
|
||||
UINT adapter,
|
||||
D3DDEVTYPE type,
|
||||
HWND hwnd,
|
||||
DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp,
|
||||
IDirect3DDevice9 **pdev);
|
||||
|
||||
static struct gfx_config gfx_config;
|
||||
static Direct3DCreate9_t next_Direct3DCreate9;
|
||||
static Direct3DCreate9Ex_t next_Direct3DCreate9Ex;
|
||||
|
||||
static const struct hook_symbol gfx_hooks[] = {
|
||||
{
|
||||
.name = "Direct3DCreate9",
|
||||
.patch = Direct3DCreate9,
|
||||
.link = (void **) &next_Direct3DCreate9,
|
||||
}, {
|
||||
.name = "Direct3DCreate9Ex",
|
||||
.patch = Direct3DCreate9Ex,
|
||||
.link = (void **) &next_Direct3DCreate9Ex,
|
||||
},
|
||||
};
|
||||
|
||||
void gfx_d3d9_hook_init(const struct gfx_config *cfg, HINSTANCE self)
|
||||
{
|
||||
HMODULE d3d9;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&gfx_config, cfg, sizeof(*cfg));
|
||||
hook_table_apply(NULL, "d3d9.dll", gfx_hooks, _countof(gfx_hooks));
|
||||
|
||||
if (next_Direct3DCreate9 == NULL || next_Direct3DCreate9Ex == NULL) {
|
||||
d3d9 = LoadLibraryW(L"d3d9.dll");
|
||||
|
||||
if (d3d9 == NULL) {
|
||||
dprintf("Gfx: d3d9.dll not found or failed initialization\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (next_Direct3DCreate9 == NULL) {
|
||||
next_Direct3DCreate9 = (Direct3DCreate9_t) GetProcAddress(d3d9, "Direct3DCreate9");
|
||||
}
|
||||
if (next_Direct3DCreate9Ex == NULL) {
|
||||
next_Direct3DCreate9Ex = (Direct3DCreate9Ex_t) GetProcAddress(d3d9, "Direct3DCreate9Ex");
|
||||
}
|
||||
|
||||
if (next_Direct3DCreate9 == NULL) {
|
||||
dprintf("Gfx: Direct3DCreate9 not found in loaded d3d9.dll\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
if (next_Direct3DCreate9Ex == NULL) {
|
||||
dprintf("Gfx: Direct3DCreate9Ex not found in loaded d3d9.dll\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (self != NULL) {
|
||||
dll_hook_push(self, L"d3d9.dll");
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (d3d9 != NULL) {
|
||||
FreeLibrary(d3d9);
|
||||
}
|
||||
}
|
||||
|
||||
IDirect3D9 * WINAPI Direct3DCreate9(UINT sdk_ver)
|
||||
{
|
||||
struct com_proxy *proxy;
|
||||
IDirect3D9Vtbl *vtbl;
|
||||
IDirect3D9 *api;
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("Gfx: Direct3DCreate9 hook hit\n");
|
||||
|
||||
api = NULL;
|
||||
|
||||
if (next_Direct3DCreate9 == NULL) {
|
||||
dprintf("Gfx: next_Direct3DCreate9 == NULL\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
api = next_Direct3DCreate9(sdk_ver);
|
||||
|
||||
if (api == NULL) {
|
||||
dprintf("Gfx: next_Direct3DCreate9 returned NULL\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("Gfx: com_proxy_wrap returned %x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vtbl = proxy->vptr;
|
||||
vtbl->CreateDevice = my_IDirect3D9_CreateDevice;
|
||||
|
||||
return (IDirect3D9 *) proxy;
|
||||
|
||||
fail:
|
||||
if (api != NULL) {
|
||||
IDirect3D9_Release(api);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HRESULT WINAPI Direct3DCreate9Ex(UINT sdk_ver, IDirect3D9Ex **d3d9ex)
|
||||
{
|
||||
struct com_proxy *proxy;
|
||||
IDirect3D9ExVtbl *vtbl;
|
||||
IDirect3D9Ex *api;
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("Gfx: Direct3DCreate9Ex hook hit\n");
|
||||
|
||||
api = NULL;
|
||||
|
||||
if (next_Direct3DCreate9Ex == NULL) {
|
||||
dprintf("Gfx: next_Direct3DCreate9Ex == NULL\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = next_Direct3DCreate9Ex(sdk_ver, d3d9ex);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("Gfx: next_Direct3DCreate9Ex returned %x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
api = *d3d9ex;
|
||||
hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("Gfx: com_proxy_wrap returned %x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vtbl = proxy->vptr;
|
||||
vtbl->CreateDevice = my_IDirect3D9Ex_CreateDevice;
|
||||
|
||||
*d3d9ex = (IDirect3D9Ex *) proxy;
|
||||
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
if (api != NULL) {
|
||||
IDirect3D9Ex_Release(api);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_IDirect3D9_CreateDevice(
|
||||
IDirect3D9 *self,
|
||||
UINT adapter,
|
||||
D3DDEVTYPE type,
|
||||
HWND hwnd,
|
||||
DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp,
|
||||
IDirect3DDevice9 **pdev)
|
||||
{
|
||||
struct com_proxy *proxy;
|
||||
IDirect3D9 *real;
|
||||
|
||||
dprintf("Gfx: IDirect3D9::CreateDevice hook hit\n");
|
||||
|
||||
proxy = com_proxy_downcast(self);
|
||||
real = proxy->real;
|
||||
|
||||
if (gfx_config.windowed) {
|
||||
pp->Windowed = TRUE;
|
||||
pp->FullScreen_RefreshRateInHz = 0;
|
||||
}
|
||||
|
||||
if (gfx_config.framed) {
|
||||
gfx_util_frame_window(hwnd);
|
||||
}
|
||||
|
||||
UINT max_adapter = IDirect3D9_GetAdapterCount(real);
|
||||
adapter = gfx_config.monitor;
|
||||
if (adapter >= max_adapter) {
|
||||
dprintf(
|
||||
"Gfx: Requested adapter %d but maximum is %d. Using primary monitor\n",
|
||||
gfx_config.monitor, max_adapter - 1
|
||||
);
|
||||
adapter = D3DADAPTER_DEFAULT;
|
||||
} else {
|
||||
dprintf("Gfx: Using adapter %d\n", gfx_config.monitor);
|
||||
}
|
||||
|
||||
return IDirect3D9_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev);
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_IDirect3D9Ex_CreateDevice(
|
||||
IDirect3D9Ex *self,
|
||||
UINT adapter,
|
||||
D3DDEVTYPE type,
|
||||
HWND hwnd,
|
||||
DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp,
|
||||
IDirect3DDevice9 **pdev)
|
||||
{
|
||||
dprintf("Gfx: IDirect3D9Ex::CreateDevice hook forwarding to my_IDirect3D9_CreateDevice\n");
|
||||
|
||||
return my_IDirect3D9_CreateDevice(
|
||||
(IDirect3D9 *) self,
|
||||
adapter,
|
||||
type,
|
||||
hwnd,
|
||||
flags,
|
||||
pp,
|
||||
pdev);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
|
||||
void gfx_d3d9_hook_init(const struct gfx_config *cfg, HINSTANCE self);
|
||||
@@ -0,0 +1,364 @@
|
||||
#include <windows.h>
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gfxhook/dxgi.h"
|
||||
#include "gfxhook/gfx.h"
|
||||
|
||||
#include "hook/com-proxy.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/dll.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
typedef HRESULT (WINAPI *CreateDXGIFactory_t)(REFIID riid, void **factory);
|
||||
typedef HRESULT (WINAPI *CreateDXGIFactory1_t)(REFIID riid, void **factory);
|
||||
typedef HRESULT (WINAPI *CreateDXGIFactory2_t)(
|
||||
UINT flags,
|
||||
REFIID riid,
|
||||
void **factory);
|
||||
|
||||
static HRESULT hook_factory(REFIID riid, void **factory);
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain(
|
||||
IDXGIFactory *self,
|
||||
IUnknown *device,
|
||||
DXGI_SWAP_CHAIN_DESC *desc,
|
||||
IDXGISwapChain **swapchain);
|
||||
static HRESULT STDMETHODCALLTYPE my_IDXGIFactory1_CreateSwapChain(
|
||||
IDXGIFactory1 *self,
|
||||
IUnknown *device,
|
||||
DXGI_SWAP_CHAIN_DESC *desc,
|
||||
IDXGISwapChain **swapchain);
|
||||
static HRESULT STDMETHODCALLTYPE my_IDXGIFactory2_CreateSwapChain(
|
||||
IDXGIFactory2 *self,
|
||||
IUnknown *device,
|
||||
DXGI_SWAP_CHAIN_DESC *desc,
|
||||
IDXGISwapChain **swapchain);
|
||||
|
||||
static struct gfx_config gfx_config;
|
||||
static CreateDXGIFactory_t next_CreateDXGIFactory;
|
||||
static CreateDXGIFactory1_t next_CreateDXGIFactory1;
|
||||
static CreateDXGIFactory2_t next_CreateDXGIFactory2;
|
||||
|
||||
static const struct hook_symbol dxgi_hooks[] = {
|
||||
{
|
||||
.name = "CreateDXGIFactory",
|
||||
.patch = CreateDXGIFactory,
|
||||
.link = (void **) &next_CreateDXGIFactory,
|
||||
}, {
|
||||
.name = "CreateDXGIFactory1",
|
||||
.patch = CreateDXGIFactory1,
|
||||
.link = (void **) &next_CreateDXGIFactory1,
|
||||
}, {
|
||||
.name = "CreateDXGIFactory2",
|
||||
.patch = CreateDXGIFactory2,
|
||||
.link = (void **) &next_CreateDXGIFactory2,
|
||||
},
|
||||
};
|
||||
|
||||
void gfx_dxgi_hook_init(const struct gfx_config *cfg, HINSTANCE self)
|
||||
{
|
||||
HMODULE dxgi;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&gfx_config, cfg, sizeof(*cfg));
|
||||
hook_table_apply(NULL, "dxgi.dll", dxgi_hooks, _countof(dxgi_hooks));
|
||||
|
||||
if (next_CreateDXGIFactory == NULL || next_CreateDXGIFactory1 == NULL) {
|
||||
dxgi = LoadLibraryW(L"dxgi.dll");
|
||||
|
||||
if (dxgi == NULL) {
|
||||
dprintf("DXGI: dxgi.dll not found or failed initialization\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (next_CreateDXGIFactory == NULL) {
|
||||
next_CreateDXGIFactory = (CreateDXGIFactory_t) GetProcAddress(
|
||||
dxgi,
|
||||
"CreateDXGIFactory");
|
||||
}
|
||||
if (next_CreateDXGIFactory1 == NULL) {
|
||||
next_CreateDXGIFactory1 = (CreateDXGIFactory1_t) GetProcAddress(
|
||||
dxgi,
|
||||
"CreateDXGIFactory1");
|
||||
}
|
||||
if (next_CreateDXGIFactory2 == NULL) {
|
||||
next_CreateDXGIFactory2 = (CreateDXGIFactory2_t) GetProcAddress(
|
||||
dxgi,
|
||||
"CreateDXGIFactory2");
|
||||
}
|
||||
|
||||
if (next_CreateDXGIFactory == NULL) {
|
||||
dprintf("DXGI: CreateDXGIFactory not found in loaded dxgi.dll\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
if (next_CreateDXGIFactory1 == NULL) {
|
||||
dprintf("DXGI: CreateDXGIFactory1 not found in loaded dxgi.dll\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* `CreateDXGIFactory2` was introduced in Windows 8.1 and the original
|
||||
* Nu runs Windows 8, so do not require it to exist */
|
||||
}
|
||||
|
||||
if (self != NULL) {
|
||||
dll_hook_push(self, L"dxgi.dll");
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (dxgi != NULL) {
|
||||
FreeLibrary(dxgi);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT WINAPI CreateDXGIFactory(REFIID riid, void **factory)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("DXGI: CreateDXGIFactory hook hit\n");
|
||||
|
||||
hr = next_CreateDXGIFactory(riid, factory);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DXGI: CreateDXGIFactory returned %x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = hook_factory(riid, factory);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT WINAPI CreateDXGIFactory1(REFIID riid, void **factory)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("DXGI: CreateDXGIFactory1 hook hit\n");
|
||||
|
||||
hr = next_CreateDXGIFactory1(riid, factory);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DXGI: CreateDXGIFactory1 returned %x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = hook_factory(riid, factory);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID riid, void **factory)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("DXGI: CreateDXGIFactory2 hook hit\n");
|
||||
|
||||
if (next_CreateDXGIFactory2 == NULL) {
|
||||
dprintf("DXGI: CreateDXGIFactory2 not available, forwarding to CreateDXGIFactory1\n");
|
||||
|
||||
return CreateDXGIFactory1(riid, factory);
|
||||
}
|
||||
|
||||
hr = next_CreateDXGIFactory2(flags, riid, factory);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DXGI: CreateDXGIFactory2 returned %x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = hook_factory(riid, factory);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT hook_factory(REFIID riid, void **factory)
|
||||
{
|
||||
struct com_proxy *proxy;
|
||||
IDXGIFactory *api_0;
|
||||
IDXGIFactory1 *api_1;
|
||||
IDXGIFactory2 *api_2;
|
||||
IDXGIFactoryVtbl *vtbl_0;
|
||||
IDXGIFactory1Vtbl *vtbl_1;
|
||||
IDXGIFactory2Vtbl *vtbl_2;
|
||||
HRESULT hr;
|
||||
|
||||
api_0 = NULL;
|
||||
api_1 = NULL;
|
||||
api_2 = NULL;
|
||||
|
||||
if (memcmp(riid, &IID_IDXGIFactory, sizeof(*riid)) == 0) {
|
||||
api_0 = *factory;
|
||||
hr = com_proxy_wrap(&proxy, api_0, sizeof(*api_0->lpVtbl));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vtbl_0 = proxy->vptr;
|
||||
vtbl_0->CreateSwapChain = my_IDXGIFactory_CreateSwapChain;
|
||||
|
||||
*factory = proxy;
|
||||
} else if (memcmp(riid, &IID_IDXGIFactory1, sizeof(*riid)) == 0) {
|
||||
api_1 = *factory;
|
||||
hr = com_proxy_wrap(&proxy, api_1, sizeof(*api_1->lpVtbl));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vtbl_1 = proxy->vptr;
|
||||
vtbl_1->CreateSwapChain = my_IDXGIFactory1_CreateSwapChain;
|
||||
|
||||
*factory = proxy;
|
||||
} else if (memcmp(riid, &IID_IDXGIFactory2, sizeof(*riid)) == 0) {
|
||||
api_2 = *factory;
|
||||
hr = com_proxy_wrap(&proxy, api_2, sizeof(*api_2->lpVtbl));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vtbl_2 = proxy->vptr;
|
||||
vtbl_2->CreateSwapChain = my_IDXGIFactory2_CreateSwapChain;
|
||||
|
||||
*factory = proxy;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
if (api_0 != NULL) {
|
||||
IDXGIFactory_Release(api_0);
|
||||
}
|
||||
if (api_1 != NULL) {
|
||||
IDXGIFactory1_Release(api_1);
|
||||
}
|
||||
if (api_2 != NULL) {
|
||||
IDXGIFactory2_Release(api_2);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain(
|
||||
IDXGIFactory *self,
|
||||
IUnknown *device,
|
||||
DXGI_SWAP_CHAIN_DESC *desc,
|
||||
IDXGISwapChain **swapchain)
|
||||
{
|
||||
struct com_proxy *proxy;
|
||||
IDXGIFactory *real;
|
||||
HWND hwnd;
|
||||
UINT width;
|
||||
UINT height;
|
||||
|
||||
dprintf("DXGI: IDXGIFactory::CreateSwapChain hook hit\n");
|
||||
|
||||
proxy = com_proxy_downcast(self);
|
||||
real = proxy->real;
|
||||
|
||||
if (desc != NULL) {
|
||||
desc->Windowed = gfx_config.windowed;
|
||||
|
||||
hwnd = desc->OutputWindow;
|
||||
width = desc->BufferDesc.Width;
|
||||
height = desc->BufferDesc.Height;
|
||||
} else {
|
||||
hwnd = NULL;
|
||||
}
|
||||
|
||||
if (hwnd != NULL) {
|
||||
/*
|
||||
* Ensure window is maximized to avoid a Windows 10 issue where a
|
||||
* fullscreen swap chain is not created because the window is minimized
|
||||
* at the time of creation.
|
||||
*/
|
||||
ShowWindow(hwnd, SW_RESTORE);
|
||||
|
||||
if (!gfx_config.framed && width > 0 && height > 0) {
|
||||
dprintf("DXGI: Resizing window to %ux%u\n", width, height);
|
||||
|
||||
SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP);
|
||||
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST);
|
||||
|
||||
SetWindowPos(
|
||||
hwnd,
|
||||
HWND_TOP,
|
||||
0,
|
||||
0,
|
||||
(int) width,
|
||||
(int) height,
|
||||
SWP_FRAMECHANGED | SWP_NOSENDCHANGING);
|
||||
|
||||
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
|
||||
}
|
||||
}
|
||||
|
||||
return IDXGIFactory_CreateSwapChain(real, device, desc, swapchain);
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_IDXGIFactory1_CreateSwapChain(
|
||||
IDXGIFactory1 *self,
|
||||
IUnknown *device,
|
||||
DXGI_SWAP_CHAIN_DESC *desc,
|
||||
IDXGISwapChain **swapchain)
|
||||
{
|
||||
dprintf("DXGI: IDXGIFactory1::CreateSwapChain hook forwarding to my_IDXGIFactory_CreateSwapChain\n");
|
||||
|
||||
return my_IDXGIFactory_CreateSwapChain(
|
||||
(IDXGIFactory *) self,
|
||||
device,
|
||||
desc,
|
||||
swapchain);
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_IDXGIFactory2_CreateSwapChain(
|
||||
IDXGIFactory2 *self,
|
||||
IUnknown *device,
|
||||
DXGI_SWAP_CHAIN_DESC *desc,
|
||||
IDXGISwapChain **swapchain)
|
||||
{
|
||||
dprintf("DXGI: IDXGIFactory2::CreateSwapChain hook forwarding to my_IDXGIFactory_CreateSwapChain\n");
|
||||
|
||||
return my_IDXGIFactory_CreateSwapChain(
|
||||
(IDXGIFactory *) self,
|
||||
device,
|
||||
desc,
|
||||
swapchain);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
|
||||
void gfx_dxgi_hook_init(const struct gfx_config *cfg, HINSTANCE self);
|
||||
@@ -0,0 +1,147 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
/* Hook functions */
|
||||
|
||||
static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow);
|
||||
static HWND WINAPI hook_CreateWindowExA(
|
||||
DWORD dwExStyle,
|
||||
LPCSTR lpClassName,
|
||||
LPCSTR lpWindowName,
|
||||
DWORD dwStyle,
|
||||
int X,
|
||||
int Y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
HWND hWndParent,
|
||||
HMENU hMenu,
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam
|
||||
);
|
||||
|
||||
/* Link pointers */
|
||||
|
||||
static BOOL (WINAPI *next_ShowWindow)(HWND hWnd, int nCmdShow);
|
||||
static HWND (WINAPI *next_CreateWindowExA)(
|
||||
DWORD dwExStyle,
|
||||
LPCSTR lpClassName,
|
||||
LPCSTR lpWindowName,
|
||||
DWORD dwStyle,
|
||||
int X,
|
||||
int Y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
HWND hWndParent,
|
||||
HMENU hMenu,
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam
|
||||
);
|
||||
|
||||
static struct gfx_config gfx_config;
|
||||
|
||||
static const struct hook_symbol gfx_hooks[] = {
|
||||
{
|
||||
.name = "ShowWindow",
|
||||
.patch = hook_ShowWindow,
|
||||
.link = (void **) &next_ShowWindow,
|
||||
}, {
|
||||
.name = "CreateWindowExA",
|
||||
.patch = hook_CreateWindowExA,
|
||||
.link = (void **) &next_CreateWindowExA,
|
||||
},
|
||||
};
|
||||
|
||||
void gfx_hook_init(const struct gfx_config *cfg)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg->dpiAware) {
|
||||
if (SetProcessDPIAware()) {
|
||||
dprintf("Gfx: Game process set to DPI aware.\n");
|
||||
} else {
|
||||
dprintf("Gfx: Failed to set process DPI aware\n");
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&gfx_config, cfg, sizeof(*cfg));
|
||||
hook_table_apply(NULL, "user32.dll", gfx_hooks, _countof(gfx_hooks));
|
||||
}
|
||||
|
||||
static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow)
|
||||
{
|
||||
dprintf("Gfx: ShowWindow hook hit\n");
|
||||
|
||||
if (!gfx_config.framed && nCmdShow == SW_RESTORE) {
|
||||
nCmdShow = SW_SHOW;
|
||||
}
|
||||
|
||||
return next_ShowWindow(hWnd, nCmdShow);
|
||||
}
|
||||
|
||||
static HWND WINAPI hook_CreateWindowExA(
|
||||
DWORD dwExStyle,
|
||||
LPCSTR lpClassName,
|
||||
LPCSTR lpWindowName,
|
||||
DWORD dwStyle,
|
||||
int X,
|
||||
int Y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
HWND hWndParent,
|
||||
HMENU hMenu,
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam
|
||||
)
|
||||
{
|
||||
RECT rect;
|
||||
|
||||
dprintf("Gfx: CreateWindowExA hook hit\n");
|
||||
|
||||
if (gfx_config.windowed)
|
||||
{
|
||||
if (gfx_config.framed)
|
||||
dwStyle |= WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
|
||||
else
|
||||
dwStyle = WS_POPUP;
|
||||
|
||||
rect.left = ((X == CW_USEDEFAULT) ? 0 : X);
|
||||
rect.top = ((Y == CW_USEDEFAULT) ? 0 : Y);
|
||||
rect.right = rect.left + nWidth;
|
||||
rect.bottom = rect.top + nHeight;
|
||||
|
||||
// Don't care if it's ok or not, since we are creating window and we can't just return a NULL
|
||||
AdjustWindowRect(&rect, dwStyle, !!hMenu);
|
||||
|
||||
X = ((X == CW_USEDEFAULT) ? X : rect.left);
|
||||
Y = ((Y == CW_USEDEFAULT) ? Y : rect.top);
|
||||
nWidth = rect.right - rect.left;
|
||||
nHeight = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
return next_CreateWindowExA(
|
||||
dwExStyle,
|
||||
lpClassName,
|
||||
lpWindowName,
|
||||
dwStyle,
|
||||
X,
|
||||
Y,
|
||||
nWidth,
|
||||
nHeight,
|
||||
hWndParent,
|
||||
hMenu,
|
||||
hInstance,
|
||||
lpParam
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct gfx_config {
|
||||
bool enable;
|
||||
bool windowed;
|
||||
bool framed;
|
||||
int monitor;
|
||||
bool dpiAware;
|
||||
};
|
||||
|
||||
void gfx_hook_init(const struct gfx_config *cfg);
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
#include "gfxhook/gl.h"
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/dll.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
/* Hook functions */
|
||||
|
||||
static void WINAPI hook_glutFullScreen(void);
|
||||
static void WINAPI hook_glutInitDisplayMode(unsigned int mode);
|
||||
|
||||
/* Link pointers */
|
||||
|
||||
static void (WINAPI *next_glutFullScreen)(void);
|
||||
static void (WINAPI *next_glutInitDisplayMode)(unsigned int mode);
|
||||
|
||||
static struct gfx_config gfx_config;
|
||||
|
||||
static const struct hook_symbol glut_hooks[] = {
|
||||
{
|
||||
.name = "glutFullScreen",
|
||||
.patch = hook_glutFullScreen,
|
||||
.link = (void **) &next_glutFullScreen,
|
||||
}, {
|
||||
.name = "glutInitDisplayMode",
|
||||
.patch = hook_glutInitDisplayMode,
|
||||
.link = (void **) &next_glutInitDisplayMode,
|
||||
},
|
||||
};
|
||||
|
||||
void gfx_gl_hook_init(const struct gfx_config *cfg, HINSTANCE self)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&gfx_config, cfg, sizeof(*cfg));
|
||||
hook_table_apply(NULL, "glut32.dll", glut_hooks, _countof(glut_hooks));
|
||||
|
||||
if (self != NULL) {
|
||||
dll_hook_push(self, L"glut32.dll");
|
||||
}
|
||||
}
|
||||
|
||||
static void WINAPI hook_glutFullScreen(void)
|
||||
{
|
||||
dprintf("Gfx: glutFullScreen hook hit\n");
|
||||
|
||||
if (gfx_config.windowed) {
|
||||
return;
|
||||
}
|
||||
|
||||
next_glutFullScreen();
|
||||
}
|
||||
|
||||
static void WINAPI hook_glutInitDisplayMode(unsigned int mode)
|
||||
{
|
||||
dprintf("Gfx: glutInitDisplayMode hook hit\n");
|
||||
|
||||
// GLUT adds a frame when going windowed
|
||||
if (gfx_config.windowed && !gfx_config.framed) {
|
||||
// GLUT_BORDERLESS
|
||||
mode |= 0x0800;
|
||||
}
|
||||
|
||||
next_glutInitDisplayMode(mode);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void gfx_gl_hook_init(const struct gfx_config *cfg, HINSTANCE self);
|
||||
@@ -0,0 +1,29 @@
|
||||
gfxhook_lib = static_library(
|
||||
'gfxhook',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
dxguid_lib,
|
||||
],
|
||||
link_with : [
|
||||
hooklib_lib,
|
||||
util_lib,
|
||||
],
|
||||
sources : [
|
||||
'config.c',
|
||||
'config.h',
|
||||
'd3d9.c',
|
||||
'd3d9.h',
|
||||
'd3d11.c',
|
||||
'd3d11.h',
|
||||
'dxgi.c',
|
||||
'dxgi.h',
|
||||
'gfx.c',
|
||||
'gfx.h',
|
||||
'gl.c',
|
||||
'gl.h',
|
||||
'util.c',
|
||||
'util.h',
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,116 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "gfxhook/util.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
void gfx_util_ensure_win_visible(HWND hwnd)
|
||||
{
|
||||
/*
|
||||
* Ensure window is maximized to avoid a Windows 10 issue where a
|
||||
* fullscreen swap chain is not created because the window is minimized
|
||||
* at the time of creation.
|
||||
*/
|
||||
ShowWindow(hwnd, SW_RESTORE);
|
||||
}
|
||||
|
||||
void gfx_util_borderless_fullscreen_windowed(HWND hwnd, UINT width, UINT height)
|
||||
{
|
||||
BOOL ok;
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("Gfx: Resizing window to %ux%u\n", width, height);
|
||||
|
||||
SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP);
|
||||
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST);
|
||||
|
||||
ok = SetWindowPos(
|
||||
hwnd,
|
||||
HWND_TOP,
|
||||
0,
|
||||
0,
|
||||
(int) width,
|
||||
(int) height,
|
||||
SWP_FRAMECHANGED | SWP_NOSENDCHANGING);
|
||||
|
||||
if (!ok) {
|
||||
/* come on... */
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("Gfx: SetWindowPos failed: %x\n", (int) hr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ok = ShowWindow(hwnd, SW_SHOWMAXIMIZED);
|
||||
|
||||
if (!ok) {
|
||||
/* come on... */
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("Gfx: ShowWindow failed: %x\n", (int) hr);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT gfx_util_frame_window(HWND hwnd)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD error;
|
||||
LONG style;
|
||||
RECT rect;
|
||||
BOOL ok;
|
||||
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
style = GetWindowLongW(hwnd, GWL_STYLE);
|
||||
error = GetLastError();
|
||||
|
||||
if (error != ERROR_SUCCESS) {
|
||||
hr = HRESULT_FROM_WIN32(error);
|
||||
dprintf("Gfx: GetWindowLongPtrW(%p, GWL_STYLE) failed: %x\n",
|
||||
hwnd,
|
||||
(int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
ok = GetClientRect(hwnd, &rect);
|
||||
|
||||
if (!ok) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("Gfx: GetClientRect(%p) failed: %x\n", hwnd, (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
style |= WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
|
||||
ok = AdjustWindowRect(&rect, style, FALSE);
|
||||
|
||||
if (!ok) {
|
||||
/* come on... */
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("Gfx: AdjustWindowRect failed: %x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* This... always seems to set an error, even though it works? idk */
|
||||
SetWindowLongW(hwnd, GWL_STYLE, style);
|
||||
|
||||
ok = SetWindowPos(
|
||||
hwnd,
|
||||
HWND_TOP,
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
SWP_FRAMECHANGED | SWP_NOMOVE);
|
||||
|
||||
if (!ok) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("Gfx: SetWindowPos(%p) failed: %x\n", hwnd, (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void gfx_util_ensure_win_visible(HWND hwnd);
|
||||
void gfx_util_borderless_fullscreen_windowed(HWND hwnd, UINT width, UINT height);
|
||||
HRESULT gfx_util_frame_window(HWND hwnd);
|
||||
@@ -0,0 +1,86 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "hooklib/config.h"
|
||||
#include "hooklib/dvd.h"
|
||||
|
||||
void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"dvd", L"enable", 1, filename);
|
||||
}
|
||||
|
||||
void touch_screen_config_load(struct touch_screen_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"touch", L"enable", 1, filename);
|
||||
cfg->remap = GetPrivateProfileIntW(L"touch", L"remap", 1, filename);
|
||||
cfg->cursor = GetPrivateProfileIntW(L"touch", L"cursor", 1, filename);
|
||||
}
|
||||
|
||||
void printer_config_load(struct printer_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
wchar_t tmpstr[16];
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"printer", L"enable", 0, filename);
|
||||
cfg->rotate_180 = GetPrivateProfileIntW(L"printer", L"rotate180", 0, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"printer",
|
||||
L"serial_no",
|
||||
L"5A-A123",
|
||||
tmpstr,
|
||||
_countof(tmpstr),
|
||||
filename);
|
||||
|
||||
size_t n = wcstombs(cfg->serial_no, tmpstr, sizeof(cfg->serial_no));
|
||||
for (int i = n; i < sizeof(cfg->serial_no); i++)
|
||||
{
|
||||
cfg->serial_no[i] = '\0';
|
||||
}
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"printer",
|
||||
L"mainFwPath",
|
||||
L"DEVICE\\printer_main_fw.bin",
|
||||
cfg->main_fw_path,
|
||||
_countof(cfg->main_fw_path),
|
||||
filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"printer",
|
||||
L"dspFwPath",
|
||||
L"DEVICE\\printer_dsp_fw.bin",
|
||||
cfg->dsp_fw_path,
|
||||
_countof(cfg->dsp_fw_path),
|
||||
filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"printer",
|
||||
L"paramFwPath",
|
||||
L"DEVICE\\printer_param_fw.bin",
|
||||
cfg->param_fw_path,
|
||||
_countof(cfg->param_fw_path),
|
||||
filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"printer",
|
||||
L"printerOutPath",
|
||||
L"DEVICE\\print",
|
||||
cfg->printer_out_path,
|
||||
_countof(cfg->printer_out_path),
|
||||
filename);
|
||||
|
||||
cfg->wait_time = GetPrivateProfileIntW(L"printer", L"waitTime", 0, filename);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "hooklib/dvd.h"
|
||||
#include "hooklib/touch.h"
|
||||
#include "hooklib/printer.h"
|
||||
|
||||
void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename);
|
||||
void touch_screen_config_load(struct touch_screen_config *cfg, const wchar_t *filename);
|
||||
void printer_config_load(struct printer_config *cfg, const wchar_t *filename);
|
||||
@@ -0,0 +1,258 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/createprocess.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
void createprocess_hook_init();
|
||||
static BOOL WINAPI my_CreateProcessA(
|
||||
LPCSTR lpApplicationName,
|
||||
LPSTR lpCommandLine,
|
||||
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
BOOL bInheritHandles,
|
||||
DWORD dwCreationFlags,
|
||||
LPVOID lpEnvironment,
|
||||
LPCSTR lpCurrentDirectory,
|
||||
LPSTARTUPINFOA lpStartupInfo,
|
||||
LPPROCESS_INFORMATION lpProcessInformation
|
||||
);
|
||||
BOOL my_CreateProcessW(
|
||||
LPCWSTR lpApplicationName,
|
||||
LPWSTR lpCommandLine,
|
||||
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
BOOL bInheritHandles,
|
||||
DWORD dwCreationFlags,
|
||||
LPVOID lpEnvironment,
|
||||
LPCWSTR lpCurrentDirectory,
|
||||
LPSTARTUPINFOW lpStartupInfo,
|
||||
LPPROCESS_INFORMATION lpProcessInformation
|
||||
);
|
||||
|
||||
static BOOL (WINAPI *next_CreateProcessA)(
|
||||
LPCSTR lpApplicationName,
|
||||
LPSTR lpCommandLine,
|
||||
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
BOOL bInheritHandles,
|
||||
DWORD dwCreationFlags,
|
||||
LPVOID lpEnvironment,
|
||||
LPCSTR lpCurrentDirectory,
|
||||
LPSTARTUPINFOA lpStartupInfo,
|
||||
LPPROCESS_INFORMATION lpProcessInformation
|
||||
);
|
||||
|
||||
static BOOL (WINAPI *next_CreateProcessW)(
|
||||
LPCWSTR lpApplicationName,
|
||||
LPWSTR lpCommandLine,
|
||||
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
BOOL bInheritHandles,
|
||||
DWORD dwCreationFlags,
|
||||
LPVOID lpEnvironment,
|
||||
LPCWSTR lpCurrentDirectory,
|
||||
LPSTARTUPINFOW lpStartupInfo,
|
||||
LPPROCESS_INFORMATION lpProcessInformation
|
||||
);
|
||||
|
||||
static const struct hook_symbol win32_hooks[] = {
|
||||
{
|
||||
.name = "CreateProcessA",
|
||||
.patch = my_CreateProcessA,
|
||||
.link = (void **) &next_CreateProcessA
|
||||
},
|
||||
{
|
||||
.name = "CreateProcessW",
|
||||
.patch = my_CreateProcessW,
|
||||
.link = (void **) &next_CreateProcessW
|
||||
},
|
||||
};
|
||||
|
||||
static bool did_init = false;
|
||||
|
||||
static struct process_hook_sym_w *process_syms_w;
|
||||
static struct process_hook_sym_a *process_syms_a;
|
||||
|
||||
static size_t process_nsyms_a = 0;
|
||||
static size_t process_nsyms_w = 0;
|
||||
|
||||
static CRITICAL_SECTION createproc_lock;
|
||||
|
||||
HRESULT createprocess_push_hook_w(const wchar_t *name, const wchar_t *head, const wchar_t *tail, bool replace_all) {
|
||||
struct process_hook_sym_w *new_mem;
|
||||
struct process_hook_sym_w *new_proc;
|
||||
HRESULT hr;
|
||||
|
||||
assert(name != NULL);
|
||||
assert(head != NULL);
|
||||
|
||||
createprocess_hook_init();
|
||||
EnterCriticalSection(&createproc_lock);
|
||||
|
||||
new_mem = realloc(
|
||||
process_syms_w,
|
||||
(process_nsyms_w + 1) * sizeof(struct process_hook_sym_w));
|
||||
|
||||
if (new_mem == NULL) {
|
||||
|
||||
LeaveCriticalSection(&createproc_lock);
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
new_proc = &new_mem[process_nsyms_w];
|
||||
memset(new_proc, 0, sizeof(*new_proc));
|
||||
new_proc->name = name;
|
||||
new_proc->head = head;
|
||||
new_proc->tail = tail;
|
||||
new_proc->replace_all = replace_all;
|
||||
|
||||
process_syms_w = new_mem;
|
||||
process_nsyms_w++;
|
||||
|
||||
LeaveCriticalSection(&createproc_lock);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT createprocess_push_hook_a(const char *name, const char *head, const char *tail, bool replace_all) {
|
||||
struct process_hook_sym_a *new_mem;
|
||||
struct process_hook_sym_a *new_proc;
|
||||
|
||||
assert(name != NULL);
|
||||
assert(head != NULL);
|
||||
|
||||
createprocess_hook_init();
|
||||
|
||||
EnterCriticalSection(&createproc_lock);
|
||||
|
||||
new_mem = realloc(
|
||||
process_syms_a,
|
||||
(process_nsyms_a + 1) * sizeof(struct process_hook_sym_a));
|
||||
|
||||
if (new_mem == NULL) {
|
||||
|
||||
LeaveCriticalSection(&createproc_lock);
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
new_proc = &new_mem[process_nsyms_a];
|
||||
memset(new_proc, 0, sizeof(*new_proc));
|
||||
new_proc->name = name;
|
||||
new_proc->head = head;
|
||||
new_proc->tail = tail;
|
||||
new_proc->replace_all = replace_all;
|
||||
|
||||
process_syms_a = new_mem;
|
||||
process_nsyms_a++;
|
||||
|
||||
LeaveCriticalSection(&createproc_lock);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void createprocess_hook_init() {
|
||||
if (did_init) {
|
||||
return;
|
||||
}
|
||||
did_init = true;
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"kernel32.dll",
|
||||
win32_hooks,
|
||||
_countof(win32_hooks));
|
||||
InitializeCriticalSection(&createproc_lock);
|
||||
dprintf("CreateProcess: Init\n");
|
||||
}
|
||||
|
||||
|
||||
static BOOL WINAPI my_CreateProcessA(
|
||||
LPCSTR lpApplicationName,
|
||||
LPSTR lpCommandLine,
|
||||
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
BOOL bInheritHandles,
|
||||
DWORD dwCreationFlags,
|
||||
LPVOID lpEnvironment,
|
||||
LPCSTR lpCurrentDirectory,
|
||||
LPSTARTUPINFOA lpStartupInfo,
|
||||
LPPROCESS_INFORMATION lpProcessInformation
|
||||
)
|
||||
{
|
||||
for (int i = 0; i < process_nsyms_a; i++) {
|
||||
if (strncmp(process_syms_a[i].name, lpCommandLine, strlen(process_syms_a[i].name))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dprintf("CreateProcess: Hooking child process %s %s\n", lpApplicationName, lpCommandLine);
|
||||
char new_cmd[MAX_PATH] = {0};
|
||||
strcat_s(new_cmd, MAX_PATH, process_syms_a[i].head);
|
||||
|
||||
if (!process_syms_a[i].replace_all) {
|
||||
strcat_s(new_cmd, MAX_PATH, lpCommandLine);
|
||||
}
|
||||
|
||||
if (process_syms_a[i].tail != NULL) {
|
||||
strcat_s(new_cmd, MAX_PATH, process_syms_a[i].tail);
|
||||
}
|
||||
|
||||
dprintf("CreateProcess: Replaced CreateProcessA %s\n", new_cmd);
|
||||
return next_CreateProcessA(
|
||||
lpApplicationName,
|
||||
new_cmd,
|
||||
lpProcessAttributes,
|
||||
lpThreadAttributes,
|
||||
bInheritHandles,
|
||||
dwCreationFlags,
|
||||
lpEnvironment,
|
||||
lpCurrentDirectory,
|
||||
lpStartupInfo,
|
||||
lpProcessInformation
|
||||
);
|
||||
}
|
||||
return next_CreateProcessA(
|
||||
lpApplicationName,
|
||||
lpCommandLine,
|
||||
lpProcessAttributes,
|
||||
lpThreadAttributes,
|
||||
bInheritHandles,
|
||||
dwCreationFlags,
|
||||
lpEnvironment,
|
||||
lpCurrentDirectory,
|
||||
lpStartupInfo,
|
||||
lpProcessInformation
|
||||
);
|
||||
}
|
||||
|
||||
BOOL my_CreateProcessW(
|
||||
LPCWSTR lpApplicationName,
|
||||
LPWSTR lpCommandLine,
|
||||
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
BOOL bInheritHandles,
|
||||
DWORD dwCreationFlags,
|
||||
LPVOID lpEnvironment,
|
||||
LPCWSTR lpCurrentDirectory,
|
||||
LPSTARTUPINFOW lpStartupInfo,
|
||||
LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
return next_CreateProcessW(
|
||||
lpApplicationName,
|
||||
lpCommandLine,
|
||||
lpProcessAttributes,
|
||||
lpThreadAttributes,
|
||||
bInheritHandles,
|
||||
dwCreationFlags,
|
||||
lpEnvironment,
|
||||
lpCurrentDirectory,
|
||||
lpStartupInfo,
|
||||
lpProcessInformation
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
HRESULT createprocess_push_hook_w(const wchar_t *name, const wchar_t *head, const wchar_t *tail, bool replace_all);
|
||||
HRESULT createprocess_push_hook_a(const char *name, const char *head, const char *tail, bool replace_all);
|
||||
|
||||
struct process_hook_sym_w {
|
||||
const wchar_t *name;
|
||||
const wchar_t *head;
|
||||
const wchar_t *tail;
|
||||
bool replace_all;
|
||||
};
|
||||
|
||||
struct process_hook_sym_a {
|
||||
const char *name;
|
||||
const char *head;
|
||||
const char *tail;
|
||||
bool replace_all;
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HCURSOR my_SetCursor(HCURSOR hCursor);
|
||||
static HCURSOR (*next_SetCursor)(HCURSOR hCursor);
|
||||
static BOOL my_SetCursorPos(int x, int y);
|
||||
static BOOL my_SetPhysicalCursorPos(int x, int y);
|
||||
static int my_ShowCursor(BOOL bShow);
|
||||
static int cursor_track = -1; // If no mouse is connected, this starts as -1
|
||||
|
||||
static const struct hook_symbol cursor_syms[] = {
|
||||
{
|
||||
.name = "SetCursor",
|
||||
.patch = my_SetCursor,
|
||||
.link = (void **) &next_SetCursor
|
||||
},/*{
|
||||
.name = "SetCursorPos",
|
||||
.patch = my_SetCursorPos,
|
||||
},*/ {
|
||||
.name = "SetPhysicalCursorPos",
|
||||
.patch = my_SetPhysicalCursorPos
|
||||
}, /*{
|
||||
.name = "ShowCursor",
|
||||
.patch = my_ShowCursor
|
||||
}*/
|
||||
};
|
||||
|
||||
void cursor_hook_init()
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"user32.dll",
|
||||
cursor_syms,
|
||||
_countof(cursor_syms));
|
||||
|
||||
dprintf("Cursor: Init\n");
|
||||
}
|
||||
|
||||
static BOOL my_SetCursorPos(int x, int y)
|
||||
{
|
||||
// dprintf("my_SetCursorPos Hit! x %d y %d\n", x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
static BOOL my_SetPhysicalCursorPos(int x, int y)
|
||||
{
|
||||
dprintf("my_SetPhysicalCursorPos Hit! x %d y %d\n", x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int my_ShowCursor(BOOL bShow)
|
||||
{
|
||||
dprintf("my_ShowCursor Hit!\n");
|
||||
if (bShow) {
|
||||
cursor_track++;
|
||||
} else {
|
||||
cursor_track--;
|
||||
}
|
||||
return cursor_track;
|
||||
}
|
||||
|
||||
static HCURSOR my_SetCursor(HCURSOR hCursor)
|
||||
{
|
||||
dprintf("my_SetCursor Hit!\n");
|
||||
HCURSOR fake_cursor;
|
||||
|
||||
if ( hCursor )
|
||||
return next_SetCursor(hCursor);
|
||||
fake_cursor = LoadCursorA(0, (LPCSTR)0x7F00);
|
||||
next_SetCursor(fake_cursor);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void cursor_hook_init();
|
||||
@@ -0,0 +1,351 @@
|
||||
/* This is general enough to break out into capnhook eventually.
|
||||
Don't introduce util/ dependencies here. */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/dll.h"
|
||||
|
||||
struct dll_hook_reg {
|
||||
const wchar_t *name;
|
||||
HMODULE redir_mod;
|
||||
};
|
||||
|
||||
/* Helper functions */
|
||||
|
||||
static void dll_hook_init(void);
|
||||
static HMODULE dll_hook_search_dll(const wchar_t *name);
|
||||
|
||||
/* Hook functions */
|
||||
|
||||
static BOOL WINAPI hook_FreeLibrary(HMODULE mod);
|
||||
static HMODULE WINAPI hook_GetModuleHandleA(const char *name);
|
||||
static HMODULE WINAPI hook_GetModuleHandleW(const wchar_t *name);
|
||||
static HMODULE WINAPI hook_LoadLibraryA(const char *name);
|
||||
static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name);
|
||||
static HMODULE WINAPI hook_LoadLibraryExA(const char *name, HANDLE file, DWORD flags);
|
||||
static HMODULE WINAPI hook_LoadLibraryExW(const wchar_t *name, HANDLE file, DWORD flags);
|
||||
|
||||
/* Link pointers */
|
||||
|
||||
static BOOL (WINAPI *next_FreeLibrary)(HMODULE mod);
|
||||
static HMODULE (WINAPI *next_GetModuleHandleA)(const char *name);
|
||||
static HMODULE (WINAPI *next_GetModuleHandleW)(const wchar_t *name);
|
||||
static HMODULE (WINAPI *next_LoadLibraryA)(const char *name);
|
||||
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
|
||||
static HMODULE (WINAPI *next_LoadLibraryExA)(const char *name, HANDLE file, DWORD flags);
|
||||
static HMODULE (WINAPI *next_LoadLibraryExW)(const wchar_t *name, HANDLE file, DWORD flags);
|
||||
|
||||
static const struct hook_symbol dll_loader_syms[] = {
|
||||
{
|
||||
.name = "FreeLibrary",
|
||||
.patch = hook_FreeLibrary,
|
||||
.link = (void **) &next_FreeLibrary,
|
||||
}, {
|
||||
.name = "GetModuleHandleA",
|
||||
.patch = hook_GetModuleHandleA,
|
||||
.link = (void **) &next_GetModuleHandleA,
|
||||
}, {
|
||||
.name = "GetModuleHandleW",
|
||||
.patch = hook_GetModuleHandleW,
|
||||
.link = (void **) &next_GetModuleHandleW,
|
||||
}, {
|
||||
.name = "LoadLibraryA",
|
||||
.patch = hook_LoadLibraryA,
|
||||
.link = (void **) &next_LoadLibraryA,
|
||||
}, {
|
||||
.name = "LoadLibraryW",
|
||||
.patch = hook_LoadLibraryW,
|
||||
.link = (void **) &next_LoadLibraryW,
|
||||
}, {
|
||||
.name = "LoadLibraryExA",
|
||||
.patch = hook_LoadLibraryExA,
|
||||
.link = (void **) &next_LoadLibraryExA,
|
||||
}, {
|
||||
.name = "LoadLibraryExW",
|
||||
.patch = hook_LoadLibraryExW,
|
||||
.link = (void **) &next_LoadLibraryExW,
|
||||
}
|
||||
};
|
||||
|
||||
static bool dll_hook_initted;
|
||||
static CRITICAL_SECTION dll_hook_lock;
|
||||
static struct dll_hook_reg *dll_hook_list;
|
||||
static size_t dll_hook_count;
|
||||
|
||||
HRESULT dll_hook_push(
|
||||
HMODULE redir_mod,
|
||||
const wchar_t *name)
|
||||
{
|
||||
struct dll_hook_reg *new_item;
|
||||
struct dll_hook_reg *new_mem;
|
||||
HRESULT hr;
|
||||
|
||||
assert(name != NULL);
|
||||
|
||||
dll_hook_init();
|
||||
|
||||
EnterCriticalSection(&dll_hook_lock);
|
||||
|
||||
new_mem = realloc(
|
||||
dll_hook_list,
|
||||
(dll_hook_count + 1) * sizeof(struct dll_hook_reg));
|
||||
|
||||
if (new_mem == NULL) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
new_item = &new_mem[dll_hook_count];
|
||||
new_item->name = name;
|
||||
new_item->redir_mod = redir_mod;
|
||||
|
||||
dll_hook_list = new_mem;
|
||||
dll_hook_count++;
|
||||
hr = S_OK;
|
||||
|
||||
end:
|
||||
LeaveCriticalSection(&dll_hook_lock);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static void dll_hook_init(void)
|
||||
{
|
||||
HMODULE kernel32;
|
||||
|
||||
/* Init is not thread safe, because API hooking is not thread safe. */
|
||||
|
||||
if (dll_hook_initted) {
|
||||
return;
|
||||
}
|
||||
|
||||
dll_hook_initted = true;
|
||||
InitializeCriticalSection(&dll_hook_lock);
|
||||
|
||||
/* Protect against the (probably impossible) scenario where nothing in the
|
||||
process imports LoadLibraryW but something imports LoadLibraryA. Also
|
||||
do the same with LoadLibraryExW.
|
||||
|
||||
We know something imports GetModuleHandleW because we do, right here.
|
||||
|
||||
(we're about to hook these APIs of course, so we have to set this up
|
||||
before the hooks go in) */
|
||||
|
||||
kernel32 = GetModuleHandleW(L"kernel32.dll");
|
||||
next_LoadLibraryW = (void *) GetProcAddress(kernel32, "LoadLibraryW");
|
||||
next_LoadLibraryExW = (void *) GetProcAddress(kernel32, "LoadLibraryExW");
|
||||
|
||||
/* Now we can apply the hook table */
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"kernel32.dll",
|
||||
dll_loader_syms,
|
||||
_countof(dll_loader_syms));
|
||||
}
|
||||
|
||||
static HMODULE dll_hook_search_dll(const wchar_t *name)
|
||||
{
|
||||
HMODULE result;
|
||||
size_t i;
|
||||
|
||||
result = NULL;
|
||||
|
||||
EnterCriticalSection(&dll_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dll_hook_count ; i++) {
|
||||
if (wcsicmp(name, dll_hook_list[i].name) == 0) {
|
||||
result = dll_hook_list[i].redir_mod;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dll_hook_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BOOL WINAPI hook_FreeLibrary(HMODULE mod)
|
||||
{
|
||||
bool match;
|
||||
size_t i;
|
||||
|
||||
match = false;
|
||||
EnterCriticalSection(&dll_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dll_hook_count ; i++) {
|
||||
if (mod == dll_hook_list[i].redir_mod) {
|
||||
match = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dll_hook_lock);
|
||||
|
||||
if (match) {
|
||||
/* Block attempts to unload redirected modules, since this could cause
|
||||
a hook DLL to unexpectedly vanish and crash the whole application.
|
||||
|
||||
Reference counting might be another solution, although it is
|
||||
possible that a buggy application might cause a hook DLL unload in
|
||||
that case. */
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return next_FreeLibrary(mod);
|
||||
}
|
||||
|
||||
static HMODULE WINAPI hook_GetModuleHandleA(const char *name)
|
||||
{
|
||||
HMODULE result;
|
||||
wchar_t *name_w;
|
||||
size_t name_c;
|
||||
|
||||
if (name == NULL) {
|
||||
return next_GetModuleHandleA(NULL);
|
||||
}
|
||||
|
||||
mbstowcs_s(&name_c, NULL, 0, name, 0);
|
||||
name_w = malloc(name_c * sizeof(wchar_t));
|
||||
|
||||
if (name_w == NULL) {
|
||||
SetLastError(ERROR_OUTOFMEMORY);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mbstowcs_s(NULL, name_w, name_c, name, name_c - 1);
|
||||
result = hook_GetModuleHandleW(name_w);
|
||||
free(name_w);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static HMODULE WINAPI hook_GetModuleHandleW(const wchar_t *name)
|
||||
{
|
||||
HMODULE result;
|
||||
|
||||
if (name == NULL) {
|
||||
return next_GetModuleHandleW(NULL);
|
||||
}
|
||||
|
||||
result = dll_hook_search_dll(name);
|
||||
|
||||
if (result != NULL) {
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
} else {
|
||||
result = next_GetModuleHandleW(name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static HMODULE WINAPI hook_LoadLibraryA(const char *name)
|
||||
{
|
||||
HMODULE result;
|
||||
wchar_t *name_w;
|
||||
size_t name_c;
|
||||
|
||||
if (name == NULL) {
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mbstowcs_s(&name_c, NULL, 0, name, 0);
|
||||
name_w = malloc(name_c * sizeof(wchar_t));
|
||||
|
||||
if (name_w == NULL) {
|
||||
SetLastError(ERROR_OUTOFMEMORY);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mbstowcs_s(NULL, name_w, name_c, name, name_c - 1);
|
||||
result = hook_LoadLibraryW(name_w);
|
||||
free(name_w);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name)
|
||||
{
|
||||
HMODULE result;
|
||||
|
||||
if (name == NULL) {
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = dll_hook_search_dll(name);
|
||||
|
||||
if (result != NULL) {
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
} else {
|
||||
result = next_LoadLibraryW(name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static HMODULE WINAPI hook_LoadLibraryExA(const char *name, HANDLE file, DWORD flags)
|
||||
{
|
||||
HMODULE result;
|
||||
wchar_t *name_w;
|
||||
size_t name_c;
|
||||
|
||||
if (name == NULL) {
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mbstowcs_s(&name_c, NULL, 0, name, 0);
|
||||
name_w = malloc(name_c * sizeof(wchar_t));
|
||||
|
||||
if (name_w == NULL) {
|
||||
SetLastError(ERROR_OUTOFMEMORY);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mbstowcs_s(NULL, name_w, name_c, name, name_c - 1);
|
||||
result = hook_LoadLibraryExW(name_w, file, flags);
|
||||
free(name_w);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static HMODULE WINAPI hook_LoadLibraryExW(const wchar_t *name, HANDLE file, DWORD flags)
|
||||
{
|
||||
HMODULE result;
|
||||
|
||||
if (name == NULL) {
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = dll_hook_search_dll(name);
|
||||
|
||||
if (result != NULL) {
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
} else {
|
||||
result = next_LoadLibraryExW(name, file, flags);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
HRESULT dll_hook_push(
|
||||
HMODULE redir_mod,
|
||||
const wchar_t *name);
|
||||
@@ -0,0 +1,783 @@
|
||||
/* Might push this to capnhook, don't add any util dependencies. */
|
||||
|
||||
#include <windows.h>
|
||||
#include <windns.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <winhttp.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/hr.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/get_function_ordinal.h"
|
||||
|
||||
#include "hooklib/dns.h"
|
||||
|
||||
/* Latest w32headers does not include DnsQueryEx, so we'll have to "polyfill"
|
||||
its associated data types here for the time being.
|
||||
|
||||
Results and cancel handle are passed through, so we'll just use void
|
||||
pointers for those args. So are most of the fields in this structure, for
|
||||
that matter. */
|
||||
|
||||
typedef struct POLYFILL_DNS_QUERY_REQUEST {
|
||||
ULONG Version;
|
||||
PCWSTR QueryName;
|
||||
WORD QueryType;
|
||||
ULONG64 QueryOptions;
|
||||
void* pDnsServerList;
|
||||
ULONG InterfaceIndex;
|
||||
void* pQueryCompletionCallback;
|
||||
PVOID pQueryContext;
|
||||
} POLYFILL_DNS_QUERY_REQUEST;
|
||||
|
||||
struct dns_hook_entry {
|
||||
wchar_t *from;
|
||||
wchar_t *to;
|
||||
};
|
||||
|
||||
/* Hook funcs */
|
||||
|
||||
static DNS_STATUS WINAPI hook_DnsQuery_A(
|
||||
const char *pszName,
|
||||
WORD wType,
|
||||
DWORD Options,
|
||||
void *pExtra,
|
||||
DNS_RECORD **ppQueryResults,
|
||||
void *pReserved);
|
||||
|
||||
static DNS_STATUS WINAPI hook_DnsQuery_W(
|
||||
const wchar_t *pszName,
|
||||
WORD wType,
|
||||
DWORD Options,
|
||||
void *pExtra,
|
||||
DNS_RECORD **ppQueryResults,
|
||||
void *pReserved);
|
||||
|
||||
static DNS_STATUS WINAPI hook_DnsQueryEx(
|
||||
POLYFILL_DNS_QUERY_REQUEST *pRequest,
|
||||
void *pQueryResults,
|
||||
void *pCancelHandle);
|
||||
|
||||
static int WSAAPI hook_getaddrinfo(
|
||||
const char *pNodeName,
|
||||
const char *pServiceName,
|
||||
const ADDRINFOA *pHints,
|
||||
ADDRINFOA **ppResult);
|
||||
|
||||
static HINTERNET WINAPI hook_WinHttpConnect(
|
||||
HINTERNET hSession,
|
||||
const wchar_t *pwszServerName,
|
||||
INTERNET_PORT nServerPort,
|
||||
DWORD dwReserved);
|
||||
|
||||
static bool WINAPI hook_WinHttpCrackUrl(
|
||||
const wchar_t *pwszUrl,
|
||||
DWORD dwUrlLength,
|
||||
DWORD dwFlags,
|
||||
LPURL_COMPONENTS lpUrlComponents);
|
||||
|
||||
static DWORD WINAPI hook_send(
|
||||
SOCKET s,
|
||||
const char* buf,
|
||||
int len,
|
||||
int flags);
|
||||
|
||||
static int WINAPI hook_connect(
|
||||
SOCKET s,
|
||||
const struct sockaddr *name,
|
||||
int namelen);
|
||||
|
||||
/* Link pointers */
|
||||
|
||||
static DNS_STATUS (WINAPI *next_DnsQuery_A)(
|
||||
const char *pszName,
|
||||
WORD wType,
|
||||
DWORD Options,
|
||||
void *pExtra,
|
||||
DNS_RECORD **ppQueryResults,
|
||||
void *pReserved);
|
||||
|
||||
static DNS_STATUS (WINAPI *next_DnsQuery_W)(
|
||||
const wchar_t *pszName,
|
||||
WORD wType,
|
||||
DWORD Options,
|
||||
void *pExtra,
|
||||
DNS_RECORD **ppQueryResults,
|
||||
void *pReserved);
|
||||
|
||||
static DNS_STATUS (WINAPI *next_DnsQueryEx)(
|
||||
POLYFILL_DNS_QUERY_REQUEST *pRequest,
|
||||
void *pQueryResults,
|
||||
void *pCancelHandle);
|
||||
|
||||
static int (WSAAPI *next_getaddrinfo)(
|
||||
const char *pNodeName,
|
||||
const char *pServiceName,
|
||||
const ADDRINFOA *pHints,
|
||||
ADDRINFOA **ppResult);
|
||||
|
||||
static HINTERNET (WINAPI *next_WinHttpConnect)(
|
||||
HINTERNET hSession,
|
||||
const wchar_t *pwszServerName,
|
||||
INTERNET_PORT nServerPort,
|
||||
DWORD dwReserved);
|
||||
|
||||
static bool (WINAPI *next_WinHttpCrackUrl)(
|
||||
const wchar_t *pwszUrl,
|
||||
DWORD dwUrlLength,
|
||||
DWORD dwFlags,
|
||||
LPURL_COMPONENTS lpUrlComponents);
|
||||
|
||||
static DWORD (WINAPI *next_send)(
|
||||
SOCKET s,
|
||||
const char* buf,
|
||||
int len,
|
||||
int flags);
|
||||
|
||||
static int (__stdcall *next_connect)(
|
||||
SOCKET s,
|
||||
const struct sockaddr *name,
|
||||
int namelen);
|
||||
|
||||
static const struct hook_symbol dns_hook_syms_dnsapi[] = {
|
||||
{
|
||||
.name = "DnsQuery_A",
|
||||
.patch = hook_DnsQuery_A,
|
||||
.link = (void **) &next_DnsQuery_A,
|
||||
}, {
|
||||
.name = "DnsQuery_W",
|
||||
.patch = hook_DnsQuery_W,
|
||||
.link = (void **) &next_DnsQuery_W,
|
||||
}, {
|
||||
.name = "DnsQueryEx",
|
||||
.patch = hook_DnsQueryEx,
|
||||
.link = (void **) &next_DnsQueryEx,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct hook_symbol dns_hook_syms_ws2[] = {
|
||||
{
|
||||
.name = "getaddrinfo",
|
||||
.ordinal = 176,
|
||||
.patch = hook_getaddrinfo,
|
||||
.link = (void **) &next_getaddrinfo,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct hook_symbol dns_hook_syms_winhttp[] = {
|
||||
{
|
||||
.name = "WinHttpConnect",
|
||||
.patch = hook_WinHttpConnect,
|
||||
.link = (void **) &next_WinHttpConnect,
|
||||
}, {
|
||||
.name = "WinHttpCrackUrl",
|
||||
.patch = hook_WinHttpCrackUrl,
|
||||
.link = (void **) &next_WinHttpCrackUrl,
|
||||
}
|
||||
};
|
||||
|
||||
static struct hook_symbol http_hook_syms_ws2[] = {
|
||||
{
|
||||
.name = "send",
|
||||
.patch = hook_send,
|
||||
.link = (void **) &next_send
|
||||
},
|
||||
};
|
||||
|
||||
static struct hook_symbol port_hook_syms_ws2[] = {
|
||||
{
|
||||
.name = "connect",
|
||||
.patch = hook_connect,
|
||||
.link = (void **) &next_connect
|
||||
},
|
||||
};
|
||||
|
||||
static bool dns_hook_initted;
|
||||
static CRITICAL_SECTION dns_hook_lock;
|
||||
static struct dns_hook_entry *dns_hook_entries;
|
||||
static size_t dns_hook_nentries;
|
||||
static char received_title_url[255];
|
||||
static unsigned short startup_port;
|
||||
static unsigned short billing_port;
|
||||
static unsigned short aimedb_port;
|
||||
|
||||
static void dns_hook_init(void)
|
||||
{
|
||||
if (dns_hook_initted) {
|
||||
return;
|
||||
}
|
||||
|
||||
dns_hook_initted = true;
|
||||
InitializeCriticalSection(&dns_hook_lock);
|
||||
|
||||
dns_hook_apply_hooks(NULL);
|
||||
}
|
||||
|
||||
void dns_hook_apply_hooks(HMODULE mod){
|
||||
hook_table_apply(
|
||||
mod,
|
||||
"dnsapi.dll",
|
||||
dns_hook_syms_dnsapi,
|
||||
_countof(dns_hook_syms_dnsapi));
|
||||
|
||||
hook_table_apply(
|
||||
mod,
|
||||
"ws2_32.dll",
|
||||
dns_hook_syms_ws2,
|
||||
_countof(dns_hook_syms_ws2));
|
||||
|
||||
hook_table_apply(
|
||||
mod,
|
||||
"winhttp.dll",
|
||||
dns_hook_syms_winhttp,
|
||||
_countof(dns_hook_syms_winhttp));
|
||||
}
|
||||
|
||||
void http_hook_init(){
|
||||
for (size_t i = 0; i < _countof(http_hook_syms_ws2); ++i) {
|
||||
http_hook_syms_ws2[i].ordinal = get_function_ordinal("ws2_32.dll", http_hook_syms_ws2[i].name);
|
||||
}
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"ws2_32.dll",
|
||||
http_hook_syms_ws2,
|
||||
_countof(http_hook_syms_ws2));
|
||||
}
|
||||
|
||||
void port_hook_init(unsigned short _startup_port, unsigned short _billing_port, unsigned short _aimedb_port){
|
||||
startup_port = _startup_port;
|
||||
billing_port = _billing_port;
|
||||
aimedb_port = _aimedb_port;
|
||||
for (size_t i = 0; i < _countof(port_hook_syms_ws2); ++i) {
|
||||
port_hook_syms_ws2[i].ordinal = get_function_ordinal("ws2_32.dll", port_hook_syms_ws2[i].name);
|
||||
}
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"ws2_32.dll",
|
||||
port_hook_syms_ws2,
|
||||
_countof(port_hook_syms_ws2));
|
||||
}
|
||||
|
||||
// This function match domain and subdomains like *.naominet.jp.
|
||||
bool match_domain(const wchar_t* target, const wchar_t* pattern) {
|
||||
if (_wcsicmp(pattern, target) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int pattern_ptr_index = 0;
|
||||
int target_ptr_index = 0;
|
||||
|
||||
while (pattern[pattern_ptr_index] != '\0' && target[target_ptr_index] != '\0') {
|
||||
if (pattern[pattern_ptr_index] == '*') {
|
||||
pattern_ptr_index++; // Check next character for wildcard match.
|
||||
|
||||
while (pattern[pattern_ptr_index] != target[target_ptr_index]) {
|
||||
target_ptr_index++;
|
||||
|
||||
if (target[target_ptr_index] == '\0') return false;
|
||||
}
|
||||
}
|
||||
else if (pattern[pattern_ptr_index] != target[target_ptr_index]) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
pattern_ptr_index++;
|
||||
target_ptr_index++;
|
||||
}
|
||||
}
|
||||
|
||||
return pattern[pattern_ptr_index] == '\0' && target[target_ptr_index] == '\0';
|
||||
}
|
||||
|
||||
HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src)
|
||||
{
|
||||
HRESULT hr;
|
||||
struct dns_hook_entry *newmem;
|
||||
struct dns_hook_entry *newitem;
|
||||
wchar_t *from;
|
||||
wchar_t *to;
|
||||
|
||||
assert(from_src != NULL);
|
||||
|
||||
to = NULL;
|
||||
from = NULL;
|
||||
dns_hook_init();
|
||||
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
from = _wcsdup(from_src);
|
||||
|
||||
if (from == NULL) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (to_src != NULL) {
|
||||
to = _wcsdup(to_src);
|
||||
|
||||
if (to == NULL) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
newmem = realloc(
|
||||
dns_hook_entries,
|
||||
(dns_hook_nentries + 1) * sizeof(struct dns_hook_entry));
|
||||
|
||||
if (newmem == NULL) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
dns_hook_entries = newmem;
|
||||
newitem = &newmem[dns_hook_nentries++];
|
||||
newitem->from = from;
|
||||
newitem->to = to;
|
||||
|
||||
from = NULL;
|
||||
to = NULL;
|
||||
hr = S_OK;
|
||||
|
||||
end:
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
|
||||
free(to);
|
||||
free(from);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static DNS_STATUS WINAPI hook_DnsQuery_A(
|
||||
const char *pszName,
|
||||
WORD wType,
|
||||
DWORD Options,
|
||||
void *pExtra,
|
||||
DNS_RECORD **ppQueryResults,
|
||||
void *pReserved)
|
||||
{
|
||||
const struct dns_hook_entry *pos;
|
||||
size_t i;
|
||||
size_t wstr_c;
|
||||
wchar_t *wstr;
|
||||
size_t str_c;
|
||||
char *str;
|
||||
DNS_STATUS code;
|
||||
HRESULT hr;
|
||||
|
||||
wstr = NULL;
|
||||
str = NULL;
|
||||
|
||||
if (pszName == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
mbstowcs_s(&wstr_c, NULL, 0, pszName, 0);
|
||||
wstr = malloc(wstr_c * sizeof(wchar_t));
|
||||
|
||||
if (wstr == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
mbstowcs_s(NULL, wstr, wstr_c, pszName, wstr_c - 1);
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (match_domain(wstr, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
hr = HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
wcstombs_s(&str_c, NULL, 0, pos->to, 0);
|
||||
str = malloc(str_c * sizeof(char));
|
||||
|
||||
if (str == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
wcstombs_s(NULL, str, str_c, pos->to, str_c - 1);
|
||||
pszName = str;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
|
||||
code = next_DnsQuery_A(
|
||||
pszName,
|
||||
wType,
|
||||
Options,
|
||||
pExtra,
|
||||
ppQueryResults,
|
||||
pReserved);
|
||||
|
||||
hr = HRESULT_FROM_WIN32(code);
|
||||
|
||||
end:
|
||||
free(str);
|
||||
free(wstr);
|
||||
|
||||
return hr_to_win32_error(hr);
|
||||
}
|
||||
|
||||
static DNS_STATUS WINAPI hook_DnsQuery_W(
|
||||
const wchar_t *pszName,
|
||||
WORD wType,
|
||||
DWORD Options,
|
||||
void *pExtra,
|
||||
DNS_RECORD **ppQueryResults,
|
||||
void *pReserved)
|
||||
{
|
||||
const struct dns_hook_entry *pos;
|
||||
size_t i;
|
||||
|
||||
if (pszName == NULL) {
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (match_domain(pszName, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
||||
}
|
||||
|
||||
pszName = pos->to;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
|
||||
return next_DnsQuery_W(
|
||||
pszName,
|
||||
wType,
|
||||
Options,
|
||||
pExtra,
|
||||
ppQueryResults,
|
||||
pReserved);
|
||||
|
||||
}
|
||||
|
||||
static DNS_STATUS WINAPI hook_DnsQueryEx(
|
||||
POLYFILL_DNS_QUERY_REQUEST *pRequest,
|
||||
void *pQueryResults,
|
||||
void *pCancelHandle)
|
||||
{
|
||||
const wchar_t *orig;
|
||||
const struct dns_hook_entry *pos;
|
||||
DNS_STATUS code;
|
||||
size_t i;
|
||||
|
||||
if (pRequest == NULL) {
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
orig = pRequest->QueryName;
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (match_domain(pRequest->QueryName, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
||||
}
|
||||
|
||||
pRequest->QueryName = pos->to;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
|
||||
code = next_DnsQueryEx(pRequest, pQueryResults, pCancelHandle);
|
||||
|
||||
/* Caller might not appreciate QueryName changing under its feet. It is
|
||||
strongly implied by MSDN that a copy of *pRequest is taken by WINAPI,
|
||||
so we can change it back after the call has been issued with no ill
|
||||
effect... we hope.
|
||||
|
||||
Hopefully the completion callback is issued from an APC or something
|
||||
(or otherwise happens after this returns) or we're in trouble. */
|
||||
|
||||
pRequest->QueryName = orig;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static int WSAAPI hook_getaddrinfo(
|
||||
const char *pNodeName,
|
||||
const char *pServiceName,
|
||||
const ADDRINFOA *pHints,
|
||||
ADDRINFOA **ppResult)
|
||||
{
|
||||
const struct dns_hook_entry *pos;
|
||||
char *str;
|
||||
size_t str_c;
|
||||
wchar_t *wstr;
|
||||
size_t wstr_c;
|
||||
int result;
|
||||
size_t i;
|
||||
|
||||
str = NULL;
|
||||
wstr = NULL;
|
||||
|
||||
if (pNodeName == NULL) {
|
||||
result = WSA_INVALID_PARAMETER;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
mbstowcs_s(&wstr_c, NULL, 0, pNodeName, 0);
|
||||
wstr = malloc(wstr_c * sizeof(wchar_t));
|
||||
|
||||
if (wstr == NULL) {
|
||||
result = WSA_NOT_ENOUGH_MEMORY;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
mbstowcs_s(NULL, wstr, wstr_c, pNodeName, wstr_c - 1);
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (match_domain(wstr, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
result = EAI_NONAME;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
wcstombs_s(&str_c, NULL, 0, pos->to, 0);
|
||||
str = malloc(str_c * sizeof(char));
|
||||
|
||||
if (str == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
result = WSA_NOT_ENOUGH_MEMORY;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
wcstombs_s(NULL, str, str_c, pos->to, str_c - 1);
|
||||
pNodeName = str;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
|
||||
result = next_getaddrinfo(pNodeName, pServiceName, pHints, ppResult);
|
||||
|
||||
end:
|
||||
free(wstr);
|
||||
free(str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static HINTERNET WINAPI hook_WinHttpConnect(
|
||||
HINTERNET hSession,
|
||||
const wchar_t *pwszServerName,
|
||||
INTERNET_PORT nServerPort,
|
||||
DWORD dwReserved)
|
||||
{
|
||||
const struct dns_hook_entry *pos;
|
||||
size_t i;
|
||||
|
||||
if (pwszServerName == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (match_domain(pwszServerName, pos->from)) {
|
||||
if (pos->to == NULL) {
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pwszServerName = pos->to;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
|
||||
return next_WinHttpConnect(hSession, pwszServerName, nServerPort, dwReserved);
|
||||
}
|
||||
|
||||
// Hook to replace CXB title url
|
||||
static bool WINAPI hook_WinHttpCrackUrl(
|
||||
const wchar_t *pwszUrl,
|
||||
DWORD dwUrlLength,
|
||||
DWORD dwFlags,
|
||||
LPURL_COMPONENTS lpUrlComponents)
|
||||
{
|
||||
const struct dns_hook_entry *pos;
|
||||
size_t i;
|
||||
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||
pos = &dns_hook_entries[i];
|
||||
|
||||
if (match_domain(pwszUrl, pos->from)) {
|
||||
wchar_t* toAddr = pos->to;
|
||||
wchar_t titleBuffer[255];
|
||||
|
||||
if (wcscmp(toAddr, L"title") == 0) {
|
||||
size_t wstr_c;
|
||||
mbstowcs_s(&wstr_c, titleBuffer, 255, received_title_url, strlen(received_title_url));
|
||||
toAddr = titleBuffer;
|
||||
}
|
||||
|
||||
bool result = next_WinHttpCrackUrl(
|
||||
toAddr,
|
||||
wcslen(toAddr),
|
||||
dwFlags,
|
||||
lpUrlComponents
|
||||
);
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return next_WinHttpCrackUrl(
|
||||
pwszUrl,
|
||||
dwUrlLength,
|
||||
dwFlags,
|
||||
lpUrlComponents
|
||||
);
|
||||
}
|
||||
|
||||
int WINAPI hook_connect(SOCKET s, const struct sockaddr *name, int namelen) {
|
||||
const struct sockaddr_in *n;
|
||||
struct sockaddr_in new_name;
|
||||
unsigned ip;
|
||||
unsigned short port, new_port;
|
||||
|
||||
EnterCriticalSection(&dns_hook_lock);
|
||||
|
||||
n = (const struct sockaddr_in *)name;
|
||||
ip = n->sin_addr.S_un.S_addr;
|
||||
if (WSANtohs(s, n->sin_port, &port)) return SOCKET_ERROR;
|
||||
|
||||
if (port == 80 && startup_port) {
|
||||
new_port = startup_port;
|
||||
} else if (port == 8443 && billing_port) {
|
||||
new_port = billing_port;
|
||||
} else if (port == 22345 && aimedb_port) {
|
||||
new_port = aimedb_port;
|
||||
} else { // No match
|
||||
dprintf("TCP Connect: %u.%u.%u.%u:%hu\n", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff, port);
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return next_connect(
|
||||
s,
|
||||
name,
|
||||
namelen
|
||||
);
|
||||
}
|
||||
|
||||
// matched
|
||||
new_name = *n;
|
||||
if (WSAHtons(s, new_port, &new_name.sin_port)) return SOCKET_ERROR;
|
||||
|
||||
dprintf("TCP Connect: %u.%u.%u.%u:%hu, mapped to port %hu\n", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff, port, new_port);
|
||||
|
||||
LeaveCriticalSection(&dns_hook_lock);
|
||||
return next_connect(
|
||||
s,
|
||||
(const struct sockaddr *)&new_name,
|
||||
sizeof(new_name)
|
||||
);
|
||||
}
|
||||
|
||||
DWORD WINAPI hook_send(SOCKET s, const char* buf, int len, int flags) {
|
||||
if (strstr(buf, "HTTP/") != NULL) {
|
||||
char *new_buf = malloc(len + 1);
|
||||
if (new_buf == NULL) return SOCKET_ERROR;
|
||||
|
||||
memcpy(new_buf, buf, len);
|
||||
new_buf[len] = '\0';
|
||||
|
||||
char *host_start = strstr(new_buf, "Host: ");
|
||||
if (host_start != NULL) {
|
||||
char *host_end = strstr(host_start, "\r\n");
|
||||
if (host_end != NULL) {
|
||||
host_end += 2;
|
||||
int host_len = host_end - host_start;
|
||||
|
||||
char *host_value_start = host_start + 6;
|
||||
char *host_value_end = strstr(host_value_start, "\r\n");
|
||||
if (host_value_end != NULL) {
|
||||
int value_len = host_value_end - host_value_start;
|
||||
char* host_value = (char*)malloc(value_len + 1);
|
||||
strncpy(host_value, host_value_start, value_len);
|
||||
host_value[value_len] = '\0';
|
||||
|
||||
for (struct dns_hook_entry *entry = dns_hook_entries; entry && entry->from; entry++) {
|
||||
char from_value[256];
|
||||
wcstombs(from_value, entry->from, sizeof(from_value));
|
||||
|
||||
if (strcmp(host_value, from_value) == 0) {
|
||||
char to_value[256];
|
||||
wcstombs(to_value, entry->to, sizeof(to_value));
|
||||
snprintf(host_start, len - (host_start - new_buf), "Host: %s\r\n", to_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(host_value);
|
||||
}
|
||||
len = (int)strlen(new_buf);
|
||||
}
|
||||
}
|
||||
DWORD result = next_send(s, new_buf, len, flags);
|
||||
free(new_buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
return next_send(s, buf, len, flags);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
void http_hook_init();
|
||||
void port_hook_init(unsigned short _startup_port, unsigned short _billing_port, unsigned short _aimedb_port);
|
||||
// if to_src is NULL, all lookups for from_src will fail
|
||||
HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src);
|
||||
|
||||
void dns_hook_apply_hooks(HMODULE mod);
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hook/com-proxy.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/config.h"
|
||||
#include "hooklib/dll.h"
|
||||
#include "hooklib/dvd.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
/* API hooks */
|
||||
|
||||
static DWORD WINAPI hook_QueryDosDeviceW(
|
||||
const wchar_t *lpDeviceName,
|
||||
wchar_t *lpTargetPath,
|
||||
DWORD ucchMax);
|
||||
|
||||
/* Link pointers */
|
||||
|
||||
static DWORD (WINAPI *next_QueryDosDeviceW)(
|
||||
const wchar_t *lpDeviceName,
|
||||
wchar_t *lpTargetPath,
|
||||
DWORD ucchMax);
|
||||
|
||||
static bool dvd_hook_initted;
|
||||
static struct dvd_config dvd_config;
|
||||
|
||||
static const struct hook_symbol dvd_hooks[] = {
|
||||
{
|
||||
.name = "QueryDosDeviceW",
|
||||
.patch = hook_QueryDosDeviceW,
|
||||
.link = (void **) &next_QueryDosDeviceW
|
||||
},
|
||||
};
|
||||
|
||||
void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dvd_hook_initted) {
|
||||
return;
|
||||
}
|
||||
|
||||
dvd_hook_initted = true;
|
||||
|
||||
memcpy(&dvd_config, cfg, sizeof(*cfg));
|
||||
hook_table_apply(NULL, "kernel32.dll", dvd_hooks, _countof(dvd_hooks));
|
||||
dprintf("DVD: hook enabled.\n");
|
||||
}
|
||||
|
||||
DWORD WINAPI hook_QueryDosDeviceW(
|
||||
const wchar_t *lpDeviceName,
|
||||
wchar_t *lpTargetPath,
|
||||
DWORD ucchMax)
|
||||
{
|
||||
DWORD ok;
|
||||
wchar_t *p_dest;
|
||||
wchar_t *dvd_string = L"CdRom";
|
||||
|
||||
ok = next_QueryDosDeviceW(
|
||||
lpDeviceName,
|
||||
lpTargetPath,
|
||||
ucchMax);
|
||||
|
||||
p_dest = wcsstr (lpTargetPath, dvd_string);
|
||||
|
||||
if ( p_dest != NULL ) {
|
||||
dprintf("DVD: Hiding DVD drive.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct dvd_config {
|
||||
bool enable;
|
||||
};
|
||||
|
||||
/* Init is not thread safe because API hook init is not thread safe blah
|
||||
blah blah you know the drill by now. */
|
||||
|
||||
void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self);
|
||||
@@ -0,0 +1,218 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/fdshark.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
static const wchar_t *fdshark_path;
|
||||
static HANDLE fdshark_target_fd;
|
||||
static int fdshark_flags;
|
||||
|
||||
static HRESULT fdshark_handle_irp(struct irp *irp);
|
||||
static HRESULT fdshark_handle_open(struct irp *irp);
|
||||
static HRESULT fdshark_handle_close(struct irp *irp);
|
||||
static HRESULT fdshark_handle_read(struct irp *irp);
|
||||
static HRESULT fdshark_handle_write(struct irp *irp);
|
||||
static HRESULT fdshark_handle_ioctl(struct irp *irp);
|
||||
static bool fdshark_force_sync(struct irp *irp, HRESULT hr);
|
||||
|
||||
HRESULT fdshark_hook_init(const wchar_t *path, int flags)
|
||||
{
|
||||
assert(path != NULL);
|
||||
assert(!(flags & ~FDSHARK_ALL_FLAGS_));
|
||||
|
||||
fdshark_path = path;
|
||||
fdshark_flags = flags;
|
||||
|
||||
return iohook_push_handler(fdshark_handle_irp);
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_irp(struct irp *irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != fdshark_target_fd) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return fdshark_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return fdshark_handle_close(irp);
|
||||
case IRP_OP_READ: return fdshark_handle_read(irp);
|
||||
case IRP_OP_WRITE: return fdshark_handle_write(irp);
|
||||
case IRP_OP_IOCTL: return fdshark_handle_ioctl(irp);
|
||||
default: return iohook_invoke_next(irp);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_open(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (_wcsicmp(irp->open_filename, fdshark_path) != 0) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
hr = iohook_invoke_next(irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("FdShark: Opened %S\n", fdshark_path);
|
||||
fdshark_target_fd = irp->fd;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_close(struct irp *irp)
|
||||
{
|
||||
dprintf("FdShark: Closed %S\n", fdshark_path);
|
||||
fdshark_target_fd = NULL;
|
||||
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_read(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!(fdshark_flags & FDSHARK_TRACE_READ)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("FdShark: Read %p:%i/%i\n",
|
||||
irp->read.bytes,
|
||||
(int) irp->read.pos,
|
||||
(int) irp->read.nbytes);
|
||||
|
||||
hr = iohook_invoke_next(irp);
|
||||
|
||||
if (FAILED(hr) && !fdshark_force_sync(irp, hr)) {
|
||||
dprintf("FdShark: FAILED: %x\n", (int) hr);
|
||||
} else {
|
||||
dprintf("FdShark: Read %p:%i/%i OK\n",
|
||||
irp->read.bytes,
|
||||
(int) irp->read.pos,
|
||||
(int) irp->read.nbytes);
|
||||
dump_iobuf(&irp->read);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_write(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!(fdshark_flags & FDSHARK_TRACE_WRITE)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("FdShark: Write %p:%i/%i\n",
|
||||
irp->write.bytes,
|
||||
(int) irp->write.pos,
|
||||
(int) irp->write.nbytes);
|
||||
dump_const_iobuf(&irp->write);
|
||||
|
||||
hr = iohook_invoke_next(irp);
|
||||
|
||||
if (FAILED(hr) && !fdshark_force_sync(irp, hr)) {
|
||||
dprintf("FdShark: FAILED: %x\n", (int) hr);
|
||||
} else {
|
||||
dprintf("FdShark: Write %p:%i/%i OK\n",
|
||||
irp->write.bytes,
|
||||
(int) irp->write.pos,
|
||||
(int) irp->write.nbytes);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!(fdshark_flags & FDSHARK_TRACE_IOCTL)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("FdShark: Ioctl %08x w:%p:%i/%i r:%p:%i/%i\n",
|
||||
irp->ioctl,
|
||||
irp->write.bytes,
|
||||
(int) irp->write.pos,
|
||||
(int) irp->write.nbytes,
|
||||
irp->read.bytes,
|
||||
(int) irp->read.pos,
|
||||
(int) irp->read.nbytes);
|
||||
dump_const_iobuf(&irp->write);
|
||||
|
||||
hr = iohook_invoke_next(irp);
|
||||
|
||||
if (FAILED(hr) && !fdshark_force_sync(irp, hr)) {
|
||||
dprintf("FdShark: FAILED: %x\n", (int) hr);
|
||||
} else {
|
||||
dprintf("FdShark: Ioctl %08x w:%p:%i/%i r:%p:%i/%i OK\n",
|
||||
irp->ioctl,
|
||||
irp->write.bytes,
|
||||
(int) irp->write.pos,
|
||||
(int) irp->write.nbytes,
|
||||
irp->read.bytes,
|
||||
(int) irp->read.pos,
|
||||
(int) irp->read.nbytes);
|
||||
dump_iobuf(&irp->read);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static bool fdshark_force_sync(struct irp *irp, HRESULT hr)
|
||||
{
|
||||
DWORD xferred;
|
||||
BOOL ok;
|
||||
|
||||
if (!(fdshark_flags & FDSHARK_FORCE_SYNC)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING) &&
|
||||
hr != HRESULT_FROM_NT(STATUS_PENDING)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = GetOverlappedResult(irp->fd, irp->ovl, &xferred, TRUE);
|
||||
|
||||
if (!ok) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("FdShark: Synchronous block failed: %x\n", (int) hr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_READ:
|
||||
case IRP_OP_IOCTL:
|
||||
irp->read.pos += xferred;
|
||||
|
||||
break;
|
||||
|
||||
case IRP_OP_WRITE:
|
||||
irp->write.pos += xferred;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
enum {
|
||||
FDSHARK_FORCE_SYNC = 0x1,
|
||||
FDSHARK_TRACE_READ = 0x2,
|
||||
FDSHARK_TRACE_WRITE = 0x4,
|
||||
FDSHARK_TRACE_IOCTL = 0x8,
|
||||
FDSHARK_ALL_FLAGS_ = 0xF,
|
||||
};
|
||||
|
||||
HRESULT fdshark_hook_init(const wchar_t *filename, int flags);
|
||||
@@ -0,0 +1,37 @@
|
||||
hooklib_lib = static_library(
|
||||
'hooklib',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
ws2_32_lib
|
||||
],
|
||||
sources : [
|
||||
'cursor.c',
|
||||
'cursor.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'createprocess.c',
|
||||
'createprocess.h',
|
||||
'dll.c',
|
||||
'dll.h',
|
||||
'dns.c',
|
||||
'dns.h',
|
||||
'dvd.c',
|
||||
'dvd.h',
|
||||
'fdshark.c',
|
||||
'fdshark.h',
|
||||
'path.c',
|
||||
'path.h',
|
||||
'reg.c',
|
||||
'reg.h',
|
||||
'setupapi.c',
|
||||
'setupapi.h',
|
||||
'spike.c',
|
||||
'spike.h',
|
||||
'touch.c',
|
||||
'touch.h',
|
||||
'printer.c',
|
||||
'printer.h',
|
||||
],
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef HRESULT (*path_hook_t)(
|
||||
const wchar_t *src,
|
||||
wchar_t *dest,
|
||||
size_t *count);
|
||||
|
||||
HRESULT path_hook_push(path_hook_t hook);
|
||||
void path_hook_insert_hooks(HMODULE target);
|
||||
int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count);
|
||||
|
||||
static inline bool path_is_separator_w(wchar_t c)
|
||||
{
|
||||
return c == L'\\' || c == L'/';
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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];
|
||||
uint32_t wait_time;
|
||||
};
|
||||
|
||||
void printer_hook_init(const struct printer_config *cfg, int rfid_port_no, HINSTANCE self);
|
||||
void printer_hook_insert_hooks(HMODULE target);
|
||||
|
||||
void printer_set_dimensions(int width, int height);
|
||||
int WINAPI fwdlusb_updateFirmware_main(uint8_t update, LPCSTR filename, uint16_t *rResult);
|
||||
int WINAPI fwdlusb_updateFirmware_dsp(uint8_t update, LPCSTR filename, uint16_t *rResult);
|
||||
int WINAPI fwdlusb_updateFirmware_param(uint8_t update, LPCSTR filename, uint16_t *rResult);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct reg_hook_val {
|
||||
const wchar_t *name;
|
||||
HRESULT (*read)(void *bytes, uint32_t *nbytes);
|
||||
HRESULT (*write)(const void *bytes, uint32_t nbytes);
|
||||
uint32_t type;
|
||||
};
|
||||
|
||||
void reg_hook_insert_hooks(HMODULE target);
|
||||
|
||||
HRESULT reg_hook_push_key(
|
||||
HKEY root,
|
||||
const wchar_t *name,
|
||||
const struct reg_hook_val *vals,
|
||||
size_t nvals);
|
||||
|
||||
HRESULT reg_hook_read_bin(
|
||||
void *bytes,
|
||||
uint32_t *nbytes,
|
||||
const void *src_bytes,
|
||||
size_t src_nbytes);
|
||||
|
||||
HRESULT reg_hook_read_u32(
|
||||
void *bytes,
|
||||
uint32_t *nbytes,
|
||||
uint32_t src);
|
||||
|
||||
HRESULT reg_hook_read_wstr(
|
||||
void *bytes,
|
||||
uint32_t *nbytes,
|
||||
const wchar_t *src);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user