Initial Commit

This commit is contained in:
Hay1tsme 2023-01-02 23:35:53 -05:00
parent a302b49950
commit ceee973ad7
150 changed files with 12229 additions and 89 deletions

4
.dockerignore Normal file
View File

@ -0,0 +1,4 @@
.git
.gitignore
build/

14
.editorconfig Normal file
View File

@ -0,0 +1,14 @@
# http://editorconfig.org/
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.{c,h}]
indent_style = space
indent_size = 4
max_line_length = 79

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.*.swp
.vscode/
# Suggested names for build dirs
build/
# External dependencies
subprojects/capnhook

38
Dockerfile Normal file
View File

@ -0,0 +1,38 @@
FROM fedora:31
LABEL description="Build environment for bananatools"
RUN yum -y install meson
RUN yum -y install ninja-build
RUN yum -y install make
RUN yum -y install zip
RUN yum -y install clang
RUN yum -y install mingw64-gcc.x86_64
RUN yum -y install mingw32-gcc.x86_64
RUN yum -y install git
RUN mkdir /bananatools
WORKDIR /bananatools
COPY hooklib hooklib
COPY ferrumhook ferrumhook
COPY ferrumio ferrumio
COPY taikohook taikohook
COPY taikoio taikoio
COPY platform platform
COPY subprojects subprojects
COPY gfxhook gfxhook
COPY jvs jvs
COPY amcus amcus
COPY board board
COPY util util
COPY dist dist
COPY cross-mingw-32.txt cross-mingw-32.txt
COPY cross-mingw-64.txt cross-mingw-64.txt
COPY Makefile Makefile
COPY meson.build meson.build
COPY Package.mk Package.mk
COPY precompiled.h precompiled.h
COPY README.md README.md
RUN make dist

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

68
Makefile Normal file
View File

@ -0,0 +1,68 @@
V ?= @
.DEFAULT_GOAL := help
BUILD_DIR := build
BUILD_DIR_32 := $(BUILD_DIR)/build32
BUILD_DIR_64 := $(BUILD_DIR)/build64
BUILD_DIR_DOCKER := $(BUILD_DIR)/docker
BUILD_DIR_ZIP := $(BUILD_DIR)/zip
DOC_DIR := doc
DIST_DIR := dist
DOCKER_CONTAINER_NAME := "bananatools-build"
DOCKER_IMAGE_NAME := "bananatools:build"
# -----------------------------------------------------------------------------
# Targets
# -----------------------------------------------------------------------------
include Package.mk
.PHONY: build # Build the project
build:
$(V)meson --cross cross-mingw-32.txt $(BUILD_DIR_32)
$(V)ninja -C $(BUILD_DIR_32)
$(V)meson --cross cross-mingw-64.txt $(BUILD_DIR_64)
$(V)ninja -C $(BUILD_DIR_64)
.PHONY: dist # Build and create a zip distribution package
dist: build zip
.PHONY: zip # Create a zip distribution pacakge
zip: $(BUILD_DIR_ZIP)/bananatools.zip
.PHONY: clean # Cleanup build output
clean:
$(V)rm -rf $(BUILD_DIR) subprojects/capnhook
.PHONY: build-docker # Build the project in a docker container
build-docker:
$(V)docker rm -f $(DOCKER_CONTAINER_NAME) 2> /dev/null || true
$(V)docker build -t $(DOCKER_IMAGE_NAME) -f Dockerfile .
$(V)docker create --name $(DOCKER_CONTAINER_NAME) $(DOCKER_IMAGE_NAME)
$(V)rm -rf $(BUILD_DIR_DOCKER)
$(V)mkdir -p $(BUILD_DIR_DOCKER)
$(V)docker cp $(DOCKER_CONTAINER_NAME):/bananatools/$(BUILD_DIR_ZIP) $(BUILD_DIR_DOCKER)
# -----------------------------------------------------------------------------
# Utility, combo and alias targets
# -----------------------------------------------------------------------------
# Help screen note:
# Variables that need to be displayed in the help screen need to strictly
# follow the pattern "^[A-Z_]+ \?= .* # .*".
# Targets that need to be displayed in the help screen need to add a separate
# phony definition strictly following the pattern "^\.PHONY\: .* # .*".
.PHONY: help # Print help screen
help:
$(V)echo bananatools makefile.
$(V)echo
$(V)echo "Environment variables:"
$(V)grep -E '^[A-Z_]+ \?\= .* #' Makefile | gawk 'match($$0, /([A-Z_]+) \?= [$$\(]*([^\)]*)[\)]{0,1} # (.*)/, a) { printf(" \033[0;35m%-25s \033[0;0m%-45s [%s]\n", a[1], a[3], a[2]) }'
$(V)echo ""
$(V)echo "Targets:"
$(V)grep '^.PHONY: .* #' Makefile | gawk 'match($$0, /\.PHONY: (.*) # (.*)/, a) { printf(" \033[0;32m%-25s \033[0;0m%s\n", a[1], a[2]) }'

29
Package.mk Normal file
View File

@ -0,0 +1,29 @@
$(BUILD_DIR_ZIP)/ferrum.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/ferrum
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/ferrumhook/ferrumhook.dll \
$(DIST_DIR)/ferrum/bananatools.ini \
$(DIST_DIR)/ferrum/start.bat \
$(BUILD_DIR_ZIP)/ferrum
$(V)strip $(BUILD_DIR_ZIP)/ferrum/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/ferrum ; zip -r ../ferrum.zip *
$(BUILD_DIR_ZIP)/taiko.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/taiko
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/taikohook/taikohook.dll \
$(DIST_DIR)/taiko/bananatools.ini \
$(DIST_DIR)/taiko/start.bat \
$(BUILD_DIR_ZIP)/taiko
$(V)strip $(BUILD_DIR_ZIP)/taiko/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/taiko ; zip -r ../taiko.zip *
$(BUILD_DIR_ZIP)/bananatools.zip: \
$(BUILD_DIR_ZIP)/ferrum.zip \
$(BUILD_DIR_ZIP)/taiko.zip \
README.md \
$(V)echo ... $@
$(V)zip -j $@ $^

View File

@ -1,92 +1,9 @@
# Bananatools
# bananatools
Tools for working with games made for **Ba**ndai **Na**mco's windows-based arecade hardware
Based on [segatools](https://dev.s-ul.net/djhackers/segatools)
## Games Supported
## Getting started
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
## Add your files
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
```
cd existing_repo
git remote add origin https://dev.s-ul.net/Hay1tsme/bananatools.git
git branch -M master
git push -uf origin master
```
## Integrate with your tools
- [ ] [Set up project integrations](https://dev.s-ul.net/Hay1tsme/bananatools/-/settings/integrations)
## Collaborate with your team
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy
Use the built-in continuous integration in GitLab.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
***
# Editing this README
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name
Choose a self-explaining name for your project.
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
### Pokken Tournament
* [A** (2018 final online build)](doc/ferrumhook.md)

77
amcus/amcus.c Normal file
View File

@ -0,0 +1,77 @@
#include <windows.h>
#include <unknwnbase.h>
#include <combaseapi.h>
#include <rpcproxy.h>
#include "hook/table.h"
#include "amcus/amcus.h"
#include "amcus/config.h"
#include "amcus/iauth.h"
#include "util/dprintf.h"
// https://dev.s-ul.net/djhackers/bemanitools/-/blob/master/src/main/dinput/device_dinput8.c
static HRESULT STDAPICALLTYPE my_CoCreateInstance(
const IID *const rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
const IID *const riid,
LPVOID *ppv);
static HRESULT (STDAPICALLTYPE *next_CoCreateInstance)(
const IID *const rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
const IID *const riid,
LPVOID *ppv);
static const struct hook_symbol amcus_hook_syms[] = {
{
.name = "CoCreateInstance",
.patch = my_CoCreateInstance,
.link = (void **) &next_CoCreateInstance,
}
};
IAuth *test;
struct amcus_updater_state *a;
HRESULT amcus_hook_init(struct amcus_config *cfg)
{
assert(cfg != NULL);
amcus_config_load(cfg, L".\\bananatools.ini");
iauth_set_config(cfg);
if (!cfg->enable) {
return S_OK;
}
dprintf("AMCUS: init\n");
hook_table_apply(
NULL,
"ole32.dll",
amcus_hook_syms,
_countof(amcus_hook_syms));
return S_OK;
}
static HRESULT STDAPICALLTYPE my_CoCreateInstance(
const IID *const rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
const IID *const riid,
LPVOID *ppv)
{
if (IsEqualGUID(rclsid, &amcus_rclsid)) {
dprintf("AMCUS: CoCreateInstance GUID match\n");
*ppv = iauth_stub;
return S_OK;
}
return next_CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv);
}

4
amcus/amcus.def Normal file
View File

@ -0,0 +1,4 @@
LIBRARY amcus
EXPORTS
iauth_stub

15
amcus/amcus.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <windows.h>
#include "amcus/config.h"
#include "amcus/iauth.h"
HRESULT amcus_hook_init(struct amcus_config *cfg);
DEFINE_GUID(
amcus_rclsid,
0x4603BB03,
0x058D,
0x43D9,
0xB9, 0x6F, 0x63, 0x9B, 0xE9, 0x08, 0xC1, 0xED);

18
amcus/config.c Normal file
View File

@ -0,0 +1,18 @@
#include "amcus/amcus.h"
#include "platform/config.h"
void amcus_config_load(struct amcus_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"amcus", L"enable", 1, filename);
GetPrivateProfileStringW(L"amcus", L"game_id", L"SXXX", cfg->game_id, _countof(cfg->game_id), filename);
GetPrivateProfileStringW(L"amcus", L"am_game_ver", L"1.00", cfg->am_game_ver, _countof(cfg->am_game_ver), filename);
GetPrivateProfileStringW(L"amcus", L"cacfg_game_ver", L"00.01", cfg->cacfg_game_ver, _countof(cfg->cacfg_game_ver), filename);
GetPrivateProfileStringW(L"amcus", L"server_uri", L"localhost", cfg->server_uri, _countof(cfg->server_uri), filename);
GetPrivateProfileStringW(L"amcus", L"server_host", L"localhost", cfg->server_host, _countof(cfg->server_host), filename);
GetPrivateProfileStringW(L"amcus", L"am_serial", L"ABLN6789012", cfg->am_serial, _countof(cfg->am_serial), filename);
es3sec_config_load(&cfg->dongle, filename);
}

18
amcus/config.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "platform/es3sec.h"
struct amcus_config {
bool enable;
wchar_t game_id[5];
wchar_t am_game_ver[6];
wchar_t cacfg_game_ver[6];
wchar_t server_uri[257];
wchar_t server_host[257];
wchar_t am_serial[12];
struct es3sec_config dongle;
};
void amcus_config_load(struct amcus_config *cfg, const wchar_t *filename);

4
amcus/guid.c Normal file
View File

@ -0,0 +1,4 @@
#include <windows.h>
#include <initguid.h>
#include "amcus.h"

420
amcus/iauth.c Normal file
View File

@ -0,0 +1,420 @@
#include <windows.h>
#include <combaseapi.h>
#include <stdint.h>
#include "amcus/iauth.h"
#include "amcus/amcus.h"
#include "amcus/config.h"
#include "util/dprintf.h"
static struct amcus_config config;
void iauth_set_config(struct amcus_config *cfg)
{
memcpy(&config, cfg, sizeof(*cfg));
}
static ULONG REF_COUNT = 0;
static HRESULT STDMETHODCALLTYPE IAuth_QueryInterface(IAuth FAR *This, REFIID riid, void **ppvObj)
{
dprintf("IAuth: QueryInterface\n");
if (ppvObj == NULL) {
return E_POINTER;
}
if (IsEqualGUID(riid, &amcus_rclsid)) {
This->lpVtbl->AddRef(This);
*ppvObj = This;
return S_OK;
}
return E_NOINTERFACE;
}
static ULONG STDMETHODCALLTYPE IAuth_AddRef(IAuth FAR *This)
{
// dprintf("IAuth: AddRef\n");
return ++REF_COUNT;
}
static ULONG STDMETHODCALLTYPE IAuth_Release(IAuth FAR *This)
{
// dprintf("IAuth: Release\n");
return --REF_COUNT;
}
static HRESULT STDMETHODCALLTYPE IAuth_Func3(IAuth FAR *This, int64_t p0)
{
dprintf("IAuth: %s hit! p0 %I64d\n", __func__, p0);
return 0;
}
static HRESULT STDMETHODCALLTYPE IAuth_InvalidateAuth(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return 0;
}
static HRESULT STDMETHODCALLTYPE IAuth_Func5(IAuth FAR *This, int64_t p0)
{
dprintf("IAuth: %s hit! p0 %I64d\n", __func__, p0);
return S_OK;
}
static LONG STDMETHODCALLTYPE IAuth_Func6(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return 1;
}
static HRESULT STDMETHODCALLTYPE IAuth_Func7(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE IAuth_Func8(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// Likely has to do with mucha and the updater
static HRESULT STDMETHODCALLTYPE IAuth_GetUpdaterState(IAuth FAR *This, struct amcus_updater_state *arr)
{
// dprintf("IAuth: %s hit!\n", __func__);
// these work with taiko, pokken never hits this
char cacfg_ver[6];
wcstombs_s(NULL, cacfg_ver, sizeof(cacfg_ver), config.cacfg_game_ver, sizeof(config.cacfg_game_ver));
double ver_d = atof(cacfg_ver);
int ver_top = (int)ver_d;
int ver_btm = (int)(ver_d * 100);
if (ver_top != 0) {
ver_btm %= (ver_top * 100);
}
memset(arr, 0, sizeof(*arr));
arr->Member0.Member0 = 1;
arr->Member0.Member4 = 0;
arr->Member0.Member8 = 2;
arr->Member0.MemberC = 1;
arr->Member0.Member10 = 0;
arr->Member18.Member0 = 1;
arr->Member18.Member4 = 0;
arr->Member18.Member8 = 1;
arr->Member18.MemberC = 0;
arr->Member18.Member10 = 28;
arr->Member18.Member14 = 31;
arr->Member18.Member18 = 41;
arr->Member18.Member1C = 50;
arr->Member18.Member20 = 59;
arr->Member18.cacfg_ver_whole = ver_top;
arr->Member18.cacfg_ver_decimal = ver_btm;
arr->Member18.app_ver_whole = ver_top;
arr->Member18.app_ver_decimal = ver_btm;
arr->Member18.Member2C = 0;
arr->Member18.Member2E = 0;
arr->Member18.Member30 = 0;
arr->Member18.Member34 = 0;
arr->Member18.Member38 = 0;
arr->Member18.Member40 = 0;
arr->Member18.Member48 = 0;
arr->Member18.Member50 = 0;
arr->Member18.Member58 = 0;
arr->Member18.Member60 = 1;
arr->Member18.Member64 = 0;
arr->Member18.Member68 = 0;
arr->Member18.Member6C = 0;
arr->Member18.Member70 = 0;
arr->Member18.Member74 = 0;
arr->Member18.Member78 = 0;
arr->Member18.Member7C = 0;
arr->Member18.Member80 = 0;
arr->Member18.Member84 = 0;
arr->Member18.Member88 = 0;
arr->MemberA8 = 1;
arr->MemberB0 = 0;
arr->MemberB8 = 1;
arr->MemberBC = 3;
arr->MemberC0 = 15;
arr->MemberC4 = 0;
return S_OK;
}
// Valid valnues for mode are STANDALONE, CLIENT, SERVER
static HRESULT STDMETHODCALLTYPE IAuth_GetCabinetConfig(IAuth FAR *This, struct amcus_cab_config *arr)
{
// dprintf("IAuth: %s hit!\n", __func__);
memset(arr, 0, sizeof(*arr));
char am_serial[12];
char dongle_serial[13];
wcstombs_s(NULL, am_serial, sizeof(am_serial), config.am_serial, sizeof(config.am_serial));
// TODO: read the serial off a real dongle if enabled is 0
wcstombs_s(NULL, dongle_serial, sizeof(dongle_serial), config.dongle.serial, sizeof(config.dongle.serial));
strcpy_s(arr->mode, sizeof(arr->mode), "STANDALONE");
strcpy_s(arr->pcbid, sizeof(arr->pcbid), am_serial);
strcpy_s(arr->dongle_serial, sizeof(arr->dongle_serial), dongle_serial);
strcpy_s(arr->shop_router_ip, sizeof(arr->shop_router_ip), "192.168.123.254");
strcpy_s(arr->auth_server_ip, sizeof(arr->auth_server_ip), "192.168.1.114");
strcpy_s(arr->local_ip, sizeof(arr->local_ip), "192.168.123.11");
strcpy_s(arr->subnet_mask, sizeof(arr->subnet_mask), "255.255.225.0");
strcpy_s(arr->gateway, sizeof(arr->gateway), "192.168.123.254");
strcpy_s(arr->primary_dns, sizeof(arr->primary_dns), "192.168.1.3");
arr->hop_count = 1;
arr->line_type = 1;
return S_OK;
}
// Gets things like version and game id
static HRESULT STDMETHODCALLTYPE IAuth_GetVersionInfo(IAuth FAR *This, struct amcus_version_info *arr)
{
// dprintf("IAuth: %s hit!\n", __func__);
memset(arr, 0, sizeof(*arr));
char game_id[5];
char am_ver[6];
char cacfg_ver[6];
wcstombs_s(NULL, game_id, sizeof(game_id), config.game_id, sizeof(config.game_id));
wcstombs_s(NULL, am_ver, sizeof(am_ver), config.am_game_ver, sizeof(config.am_game_ver));
wcstombs_s(NULL, cacfg_ver, sizeof(cacfg_ver), config.cacfg_game_ver, sizeof(config.cacfg_game_ver));
strcpy_s(arr->game_rev, sizeof(arr->game_rev), "1");
strcpy_s(arr->auth_type, sizeof(arr->auth_type), "ALL.NET");
strcpy_s(arr->game_id, sizeof(arr->game_id), game_id);
strcpy_s(arr->game_ver, sizeof(arr->game_ver), am_ver); // version sent in allnet request
strcpy_s(arr->game_cd, sizeof(arr->game_cd), "S121"); // S121
strcpy_s(arr->cacfg_game_ver, sizeof(arr->cacfg_game_ver), cacfg_ver); // first 2 are 0 = A - 25 = Z, 2nd two are the two digits
strcpy_s(arr->game_board_type, sizeof(arr->game_board_type), "0");
strcpy_s(arr->game_board_id, sizeof(arr->game_board_id), "PCB");
strcpy_s(arr->auth_url, sizeof(arr->auth_url), "http://titles.hay1ts.me:8080/");
return S_OK;
}
static HRESULT STDMETHODCALLTYPE IAuth_Func12(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE IAuth_Func13(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// Response from All.net, thanks mon!
static HRESULT STDMETHODCALLTYPE IAuth_GetAuthServerResp(IAuth FAR *This, struct amcus_auth_server_resp *arr)
{
// dprintf("IAuth: %s hit!\n", __func__);
memset(arr, 0, sizeof(*arr));
char uri[257];
char host[257];
wcstombs_s(NULL, uri, sizeof(uri), config.server_uri, sizeof(config.server_uri));
wcstombs_s(NULL, host, sizeof(host), config.server_host, sizeof(config.server_host));
strcpy_s(arr->uri, sizeof(arr->uri), uri);
strcpy_s(arr->host, sizeof(arr->host), host);
strcpy_s(arr->shop_name, sizeof(arr->shop_name), "Test Shop!");
strcpy_s(arr->shop_nickname, sizeof(arr->shop_nickname), "Test Shop");
strcpy_s(arr->region0, sizeof(arr->region0), "1");
strcpy_s(arr->region_name0, sizeof(arr->region_name0), "W");
strcpy_s(arr->region_name1, sizeof(arr->region_name1), "X");
strcpy_s(arr->region_name2, sizeof(arr->region_name2), "Y");
strcpy_s(arr->region_name3, sizeof(arr->region_name3), "Z");
strcpy_s(arr->place_id, sizeof(arr->place_id), "123");
strcpy_s(arr->setting, sizeof(arr->setting), "1");
strcpy_s(arr->country, sizeof(arr->country), "JPN");
strcpy_s(arr->timezone, sizeof(arr->timezone), "+0900");
strcpy_s(arr->res_class, sizeof(arr->res_class), "PowerOnResponseVer2");
return S_OK;
}
static HRESULT STDMETHODCALLTYPE IAuth_Func15(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE IAuth_Func16(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE IAuth_Func17(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// Another struct
static HRESULT STDMETHODCALLTYPE IAuth_Func18(IAuth FAR *This, struct amcus_arr18 *arr)
{
// dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// Another struct
static HRESULT STDMETHODCALLTYPE IAuth_Func19(IAuth FAR *This, struct amcus_arr19 *arr)
{
// dprintf("IAuth: %s hit!\n", __func__);
memset(arr, 0, sizeof(*arr));
arr->Member0 = 1; // if this isn't 1 taiko thinks it isn't auth'd
return S_OK;
}
// Converts allnet responsed to mucha info
static HRESULT STDMETHODCALLTYPE IAuth_Func20(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// dl_mode_lan and dl_mode_wan enable/disable?
static HRESULT STDMETHODCALLTYPE IAuth_Func21(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// similar to 18 but different...
static HRESULT STDMETHODCALLTYPE IAuth_Func22(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// similar to 18 but different...
static HRESULT STDMETHODCALLTYPE IAuth_Func23(IAuth FAR *This, int64_t p0)
{
dprintf("IAuth: %s hit! %I64d\n", __func__, p0);
return S_OK;
}
// more dtmode stuff
static HRESULT STDMETHODCALLTYPE IAuth_Func24(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// something to do with fileio and version and the like?
static HRESULT STDMETHODCALLTYPE IAuth_Func25(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return 1;
}
// no clue
static HRESULT STDMETHODCALLTYPE IAuth_Func26(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// something to do with downloaded patches
static HRESULT STDMETHODCALLTYPE IAuth_Func27(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return 1;
}
// no clue
static HRESULT STDMETHODCALLTYPE IAuth_Func28(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// no clue
static HRESULT STDMETHODCALLTYPE IAuth_Func29(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// no clue
static HRESULT STDMETHODCALLTYPE IAuth_Func30(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// no clue
static HRESULT STDMETHODCALLTYPE IAuth_Func31(IAuth FAR *This)
{
dprintf("IAuth: %s hit!\n", __func__);
return S_OK;
}
// just seems to allocate stuff
static HRESULT STDMETHODCALLTYPE IAuth_Func32(IAuth FAR *This, int64_t p0, int64_t* p1)
{
dprintf("IAuth: %s hit! p0 %I64d p1 %I64d\n", __func__, p0, *p1);
return S_OK;
}
// no clue
static HRESULT STDMETHODCALLTYPE IAuth_Func33(IAuth FAR *This, int64_t p0, int64_t* p1, int64_t* p2, int64_t* p3, int64_t* p4)
{
dprintf("IAuth: %s hit! p0 %I64d p1 %I64d p2 %I64d p3 %I64d p4 %I64d\n", __func__, p0, *p1, *p2, *p3, *p4);
return S_OK;
}
IAuthVtbl iauth_vtbl = {
.QueryInterface = IAuth_QueryInterface,
.AddRef = IAuth_AddRef,
.Release = IAuth_Release,
.Func3 = IAuth_Func3,
.InvalidateAuth = IAuth_InvalidateAuth,
.Func5 = IAuth_Func5,
.Func6 = IAuth_Func6,
.Func7 = IAuth_Func7,
.Func8 = IAuth_Func8,
.GetUpdaterState = IAuth_GetUpdaterState,
.GetCabinetConfig = IAuth_GetCabinetConfig,
.GetVersionInfo = IAuth_GetVersionInfo,
.Func12 = IAuth_Func12,
.Func13 = IAuth_Func13,
.GetAuthServerResp = IAuth_GetAuthServerResp,
.Func15 = IAuth_Func15,
.Func16 = IAuth_Func16,
.Func17 = IAuth_Func17,
.Func18 = IAuth_Func18,
.Func19 = IAuth_Func19,
.Func20 = IAuth_Func20,
.Func21 = IAuth_Func21,
.Func22 = IAuth_Func22,
.Func23 = IAuth_Func23,
.Func24 = IAuth_Func24,
.Func25 = IAuth_Func25,
.Func26 = IAuth_Func26,
.Func27 = IAuth_Func27,
.Func28 = IAuth_Func28,
.Func29 = IAuth_Func29,
.Func30 = IAuth_Func30,
.Func31 = IAuth_Func31,
.Func32 = IAuth_Func32,
.Func33 = IAuth_Func33,
};
IAuth iauth_stub_object = {
.lpVtbl = &iauth_vtbl,
};
IAuth *iauth_stub = &iauth_stub_object;

211
amcus/iauth.h Normal file
View File

@ -0,0 +1,211 @@
#pragma once
#include <windows.h>
#include <combaseapi.h>
#include <rpcproxy.h>
#include <stdint.h>
#include "amcus/config.h"
void iauth_set_config(struct amcus_config *cfg);
// member names are wrong lol
/* Memory Size: 20 */
struct Struct_1 {
/* Offset: 0 */ /* ENUM32 */ uint32_t Member0;
/* Offset: 4 */ /* ENUM32 */ uint32_t Member4;
/* Offset: 8 */ int32_t Member8;
/* Offset: 12 */ int32_t MemberC;
/* Offset: 16 */ int32_t Member10;
};
/* Memory Size: 144 */
struct Struct_2 {
/* Offset: 0 */ /* ENUM32 */ uint32_t Member0;
/* Offset: 4 */ /* ENUM32 */ uint32_t Member4;
/* Offset: 8 */ int32_t Member8;
/* Offset: 12 */ int32_t MemberC;
/* Offset: 16 */ int32_t Member10;
/* Offset: 20 */ int32_t Member14;
/* Offset: 24 */ int32_t Member18;
/* Offset: 28 */ int32_t Member1C;
/* Offset: 32 */ int32_t Member20;
/* Offset: 36 */ int16_t cacfg_ver_whole;
/* Offset: 38 */ int16_t cacfg_ver_decimal;
/* Offset: 40 */ int16_t app_ver_whole;
/* Offset: 42 */ int16_t app_ver_decimal;
/* Offset: 44 */ int16_t Member2C;
/* Offset: 46 */ int16_t Member2E;
/* Offset: 48 */ int32_t Member30;
/* Offset: 52 */ int32_t Member34;
/* Offset: 56 */ int64_t Member38;
/* Offset: 64 */ int64_t Member40;
/* Offset: 72 */ int64_t Member48;
/* Offset: 80 */ int64_t Member50;
/* Offset: 88 */ int64_t Member58;
/* Offset: 96 */ int32_t Member60;
/* Offset: 100 */ int32_t Member64;
/* Offset: 104 */ int32_t Member68;
/* Offset: 108 */ int32_t Member6C;
/* Offset: 112 */ int32_t Member70;
/* Offset: 116 */ int32_t Member74;
/* Offset: 120 */ int32_t Member78;
/* Offset: 124 */ int32_t Member7C;
/* Offset: 128 */ int32_t Member80;
/* Offset: 132 */ int32_t Member84;
/* Offset: 136 */ int32_t Member88;
};
/* Memory Size: 200 */
struct amcus_updater_state {
/* Offset: 0 */ struct Struct_1 Member0;
/* Offset: 24 */ struct Struct_2 Member18;
/* Offset: 168 */ int64_t MemberA8;
/* Offset: 176 */ int64_t MemberB0;
/* Offset: 184 */ /* ENUM32 */ uint32_t MemberB8;
/* Offset: 188 */ /* ENUM32 */ uint32_t MemberBC;
/* Offset: 192 */ /* ENUM32 */ uint32_t MemberC0;
/* Offset: 196 */ /* ENUM32 */ uint32_t MemberC4;
};
/* Memory Size: 316 */
struct amcus_version_info {
char game_rev[4];
char auth_type[16];
char game_id[8];
char game_ver[8];
char game_cd[8];
char cacfg_game_ver[8];
char game_board_type[4];
char game_board_id[4];
char auth_url[256];
};
/* Memory Size: 168 */
struct amcus_cab_config {
char mode[16];
char pcbid[16];
char dongle_serial[16];
char shop_router_ip[16];
char auth_server_ip[16];
char local_ip[16];
char subnet_mask[16];
char gateway[16];
char primary_dns[16];
int hop_count;
uint32_t line_type;
uint32_t line_status;
uint32_t unknown9C;
uint32_t router_status;
uint32_t unknownA4;
};
/* Memory Size: 3808 */
struct amcus_arr18 {
/* Offset: 0 */ char Member0[256];
/* Offset: 256 */ char Member100[256];
/* Offset: 512 */ char Member200[256];
/* Offset: 768 */ char Member300[256];
/* Offset: 1024 */ char Member400[256];
/* Offset: 1280 */ char Member500[16];
/* Offset: 1296 */ char Member510[16];
/* Offset: 1312 */ char Member520[256];
/* Offset: 1568 */ char Member620[128];
/* Offset: 1696 */ char Member6A0[64];
/* Offset: 1760 */ char Member6E0[64];
/* Offset: 1824 */ char Member720[64];
/* Offset: 1888 */ char Member760[64];
/* Offset: 1952 */ char Member7A0[256];
/* Offset: 2208 */ char Member8A0[256];
/* Offset: 2464 */ char Member9A0[256];
/* Offset: 2720 */ char MemberAA0[256];
/* Offset: 2976 */ char MemberBA0[64];
/* Offset: 3040 */ char MemberBE0[32];
/* Offset: 3072 */ char MemberC00[32];
/* Offset: 3104 */ char MemberC20[32];
/* Offset: 3136 */ char MemberC40[32];
/* Offset: 3168 */ char MemberC60[32];
/* Offset: 3200 */ char MemberC80[128];
/* Offset: 3328 */ char MemberD00[128];
/* Offset: 3456 */ char MemberD80[128];
/* Offset: 3584 */ char MemberE00[128];
/* Offset: 3712 */ char MemberE80[16];
/* Offset: 3728 */ char MemberE90[16];
/* Offset: 3744 */ char MemberEA0[16];
/* Offset: 3760 */ char MemberEB0[32];
/* Offset: 3792 */ char MemberED0[8];
/* Offset: 3800 */ char MemberED8[8];
};
/* Memory Size: 56 */
struct amcus_arr19 {
/* Offset: 0 */ /* ENUM32 */ uint32_t Member0;
/* Offset: 8 */ int64_t Member8;
/* Offset: 16 */ int64_t Member10;
/* Offset: 24 */ int64_t Member18;
/* Offset: 32 */ int64_t Member20;
/* Offset: 40 */ int64_t Member28;
/* Offset: 48 */ int16_t Member30;
/* Offset: 50 */ int16_t Member32;
};
/* Memory Size: 2210 */
struct amcus_auth_server_resp {
char uri[257];
char host[257];
char shop_name[256];
char shop_nickname[256];
char region0[16];
char region_name0[256];
char region_name1[256];
char region_name2[256];
char region_name3[256];
char place_id[16];
char setting[16];
char country[16];
char timezone[32];
char res_class[64];
};
#undef INTERFACE
#define INTERFACE IAuth
DECLARE_INTERFACE_(IAuth,CStdStubBuffer)
{
STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
STDMETHOD_(HRESULT,Func3)(THIS_ int64_t p0) PURE;
STDMETHOD_(HRESULT,InvalidateAuth)(THIS) PURE;
STDMETHOD_(HRESULT,Func5)(THIS_ int64_t p0) PURE;
STDMETHOD_(LONG,Func6)() PURE;
STDMETHOD_(HRESULT,Func7)() PURE;
STDMETHOD_(HRESULT,Func8)() PURE;
STDMETHOD_(HRESULT,GetUpdaterState)(THIS_ struct amcus_updater_state *arr) PURE;
STDMETHOD_(HRESULT,GetCabinetConfig)(THIS_ struct amcus_cab_config *arr) PURE;
STDMETHOD_(HRESULT,GetVersionInfo)(THIS_ struct amcus_version_info *arr) PURE;
STDMETHOD_(HRESULT,Func12)() PURE;
STDMETHOD_(HRESULT,Func13)() PURE;
STDMETHOD_(HRESULT,GetAuthServerResp)(THIS_ struct amcus_auth_server_resp *arr) PURE;
STDMETHOD_(HRESULT,Func15)() PURE;
STDMETHOD_(HRESULT,Func16)() PURE;
STDMETHOD_(HRESULT,Func17)() PURE;
STDMETHOD_(HRESULT,Func18)(THIS_ struct amcus_arr18 *arr) PURE;
STDMETHOD_(HRESULT,Func19)(THIS_ struct amcus_arr19 *arr) PURE;
STDMETHOD_(HRESULT,Func20)() PURE;
STDMETHOD_(HRESULT,Func21)() PURE;
STDMETHOD_(HRESULT,Func22)() PURE;
STDMETHOD_(HRESULT,Func23)(THIS_ int64_t p0) PURE;
STDMETHOD_(HRESULT,Func24)() PURE;
STDMETHOD_(HRESULT,Func25)() PURE;
STDMETHOD_(HRESULT,Func26)() PURE;
STDMETHOD_(HRESULT,Func27)() PURE;
STDMETHOD_(HRESULT,Func28)() PURE;
STDMETHOD_(HRESULT,Func29)() PURE;
STDMETHOD_(HRESULT,Func30)() PURE;
STDMETHOD_(HRESULT,Func31)() PURE;
STDMETHOD_(HRESULT,Func32)(THIS_ int64_t p0, int64_t* p1) PURE;
STDMETHOD_(HRESULT,Func33)(THIS_ int64_t p0, int64_t* p1, int64_t* p2, int64_t* p3, int64_t* p4) PURE;
};
#undef INTERFACE
extern IAuth *iauth_stub;

24
amcus/meson.build Normal file
View File

@ -0,0 +1,24 @@
amcus_lib = static_library(
'gfxhook',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
vs_module_defs : 'amcus.def',
dependencies : [
capnhook.get_variable('hook_dep'),
],
link_with : [
hooklib_lib,
util_lib,
platform_lib,
],
sources : [
'config.c',
'config.h',
'iauth.c',
'iauth.h',
'amcus.c',
'amcus.h',
'guid.c',
],
)

17
board/bpreader.c Normal file
View File

@ -0,0 +1,17 @@
#include <windows.h>
#include <stdint.h>
#include <stdbool.h>
#include "board/bpreader.h"
static struct bpreader_config config;
HRESULT bpreader_init(uint16_t port)
{
return S_OK;
}
void bpreader_congif_init(struct bpreader_config *cfg)
{
}

12
board/bpreader.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <windows.h>
#include <stdint.h>
#include <stdbool.h>
struct bpreader_config {
bool enable;
char access_code[21];
};
HRESULT bpreader_init(uint16_t port);
void bpreader_congif_init(struct bpreader_config *cfg);

20
board/meson.build Normal file
View File

@ -0,0 +1,20 @@
board_lib = static_library(
'gfxhook',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],
link_with : [
hooklib_lib,
util_lib,
jvs_lib,
],
sources : [
'bpreader.c',
'bpreader.h',
'najv4.c',
'najv4.h'
],
)

768
board/najv4.c Normal file
View File

@ -0,0 +1,768 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "board/najv4.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 najv4_transact(
struct jvs_node *node,
const void *bytes,
size_t nbytes,
struct iobuf *resp);
static bool najv4_sense(struct jvs_node *node);
static HRESULT najv4_cmd(
void *ctx,
struct const_iobuf *req,
struct iobuf *resp);
static HRESULT najv4_cmd_read_id(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_get_cmd_version(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_get_jvs_version(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_get_comm_version(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_get_features(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_write_pcb_info(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_read_switches(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_read_coin(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_read_analogs(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_write_gpio(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_reset(struct najv4 *najv4, struct const_iobuf *buf);
static HRESULT najv4_cmd_assign_addr(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_namco_extend_cmd(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static HRESULT najv4_cmd_namco_extend_noop_cmd(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf);
static const uint8_t najv4_ident[] = "namco ltd.;NA-JV;Ver4.00;JPN,Multipurpose";
static uint8_t najv4_features[] = {
/* Feature : 0x01 : Players and switches
Param1 : 1 : Number of players
Param2 : 18 : Number of switches per player
Param3 : 0 : N/A */
0x01,
0x01,
0x18,
0x00,
/* Feature : 0x02 : Coin slots
Param1 : 2 : Number of coin slots
Param2 : 0 : N/A
Param3 : 0 : N/A */
0x02,
0x02,
0x00,
0x00,
/* Feature : 0x03 : Analog inputs
Param1 : 8 : Number of ADC channels
Param2 : 10 : Effective bits of resolution per ADC
Param3 : 0 : N/A */
0x03,
0x08,
0x10,
0x00,
/* Feature : 0x04 : Rotary Input
Param1 : 4 : Number of Rotary channels
Param2 : 0 : N/A
Param3 : 0 : N/A */
0x04,
0x04,
0x00,
0x00,
/* Feature : 0x12 : GPIO outputs
Param1 : 3 : Number of ports (8 bits per port)
Param2 : 0 : N/A
Param3 : 0 : N/A
*/
0x12,
0x12,
0x00,
0x00,
/* Feature : 0x13 : Analog outputs
Param1 : 2 : Number of channels
Param2 : 0 : N/A
Param3 : 0 : N/A
*/
0x13,
0x02,
0x00,
0x00,
/* Feature : 0x14 : Character Output
Param1 : 0x50 : Width
Param2 : 0x19 : Height
Param3 : 0 : Type
*/
0x14,
0x50,
0x19,
0x00,
/* Feature : 0x00 : End of capabilities */
0x00,
};
void najv4_init(
struct najv4 *najv4,
struct jvs_node *next,
const struct najv4_ops *ops,
void *ops_ctx)
{
assert(najv4 != NULL);
assert(ops != NULL);
najv4->jvs.next = next;
najv4->jvs.transact = najv4_transact;
najv4->jvs.sense = najv4_sense;
najv4->addr = 0xFF;
najv4->ops = ops;
najv4->ops_ctx = ops_ctx;
}
struct jvs_node *najv4_to_jvs_node(struct najv4 *najv4)
{
assert(najv4 != NULL);
return &najv4->jvs;
}
static void najv4_transact(
struct jvs_node *node,
const void *bytes,
size_t nbytes,
struct iobuf *resp)
{
struct najv4 *najv4;
assert(node != NULL);
assert(bytes != NULL);
assert(resp != NULL);
najv4 = CONTAINING_RECORD(node, struct najv4, jvs);
jvs_crack_request(bytes, nbytes, resp, najv4->addr, najv4_cmd, najv4);
}
static bool najv4_sense(struct jvs_node *node)
{
struct najv4 *najv4;
assert(node != NULL);
najv4 = CONTAINING_RECORD(node, struct najv4, jvs);
return najv4->addr == 0xFF;
}
static HRESULT najv4_cmd(
void *ctx,
struct const_iobuf *req,
struct iobuf *resp)
{
struct najv4 *najv4;
najv4 = ctx;
switch (req->bytes[req->pos]) {
case JVS_CMD_READ_ID:
return najv4_cmd_read_id(najv4, req, resp);
case JVS_CMD_GET_CMD_VERSION:
return najv4_cmd_get_cmd_version(najv4, req, resp);
case JVS_CMD_GET_JVS_VERSION:
return najv4_cmd_get_jvs_version(najv4, req, resp);
case JVS_CMD_GET_COMM_VERSION:
return najv4_cmd_get_comm_version(najv4, req, resp);
case JVS_CMD_GET_FEATURES:
return najv4_cmd_get_features(najv4, req, resp);
case JVS_CMD_WRITE_PCB_INFO:
return najv4_cmd_write_pcb_info(najv4, req, resp);
case JVS_CMD_READ_SWITCHES:
return najv4_cmd_read_switches(najv4, req, resp);
case JVS_CMD_READ_COIN:
return najv4_cmd_read_coin(najv4, req, resp);
case JVS_CMD_READ_ANALOGS:
return najv4_cmd_read_analogs(najv4, req, resp);
case JVS_CMD_WRITE_GPIO:
return najv4_cmd_write_gpio(najv4, req, resp);
case JVS_CMD_RESET:
return najv4_cmd_reset(najv4, req);
case JVS_CMD_ASSIGN_ADDR:
return najv4_cmd_assign_addr(najv4, req, resp);
case JVS_CMD_NAMCO_EXTEND:
return najv4_cmd_namco_extend_cmd(najv4, req, resp);
default:
dprintf("JVS I/O: Node %02x: Unhandled command byte %02x\n",
najv4->addr,
req->bytes[req->pos]);
return E_NOTIMPL;
}
}
static HRESULT najv4_cmd_read_id(
struct najv4 *najv4,
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, najv4_ident, sizeof(najv4_ident));
}
static HRESULT najv4_cmd_get_cmd_version(
struct najv4 *najv4,
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 najv4_cmd_get_jvs_version(
struct najv4 *najv4,
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] = 0x31; /* JVS version BCD */
return iobuf_write(resp_buf, resp, sizeof(resp));
}
static HRESULT najv4_cmd_get_comm_version(
struct najv4 *najv4,
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 najv4_cmd_get_features(
struct najv4 *najv4,
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, najv4_features, sizeof(najv4_features));
}
static HRESULT najv4_cmd_write_pcb_info(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf)
{
uint8_t strlen = (int)req_buf->nbytes - 4; // subtract 2 bytes at the begining, command byte, and sync byte
uint8_t cmd;
uint8_t info[100]; // No more then 100 characters
dprintf("JVS I/O: PCB Info ");
iobuf_read_8(req_buf, &cmd);
iobuf_read(req_buf, info, strlen);
dprintf("%s\n", info);
// Just acknowlage we read it
return iobuf_write_8(resp_buf, 0x01);
}
static HRESULT najv4_cmd_read_switches(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf)
{
struct jvs_req_read_switches req;
struct najv4_switch_state state;
HRESULT hr;
/* Read req */
hr = iobuf_read(req_buf, &req, sizeof(req));
if (FAILED(hr)) {
return hr;
}
#if 0
dprintf("JVS I/O: Read switches, np=%i, bpp=%i\n",
req.num_players,
req.bytes_per_player);
#endif
/* Build response */
hr = iobuf_write_8(resp_buf, 0x01); /* Report byte */
if (FAILED(hr)) {
return hr;
}
memset(&state, 0, sizeof(state));
if (najv4->ops != NULL) {
najv4->ops->read_switches(najv4->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;
}
}
hr = iobuf_write_8(resp_buf, 0x00); // Not sure what this byte is but it needs to be here
return hr;
}
static HRESULT najv4_cmd_read_coin(
struct najv4 *najv4,
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 (najv4->ops->read_coin_counter != NULL) {
najv4->ops->read_coin_counter(najv4->ops_ctx, i, &ncoins);
}
hr = iobuf_write_be16(resp_buf, ncoins);
if (FAILED(hr)) {
return hr;
}
}
return hr;
}
static HRESULT najv4_cmd_read_analogs(
struct najv4 *najv4,
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 (najv4->ops->read_analogs != NULL) {
najv4->ops->read_analogs(najv4->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 najv4_cmd_write_gpio(
struct najv4 *najv4,
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 (najv4->ops->write_gpio != NULL) {
najv4->ops->write_gpio(
najv4->ops_ctx,
bytes[0] | (bytes[1] << 8) | (bytes[2] << 16));
}
/* Write report byte */
return iobuf_write_8(resp_buf, 0x01);
}
static HRESULT najv4_cmd_reset(struct najv4 *najv4, 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);
najv4->addr = 0xFF;
if (najv4->ops->reset != NULL) {
najv4->ops->reset(najv4->ops_ctx);
}
/* No ack for this since it really is addressed to everybody */
return S_OK;
}
static HRESULT najv4_cmd_assign_addr(
struct najv4 *najv4,
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(najv4->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;
}
najv4->addr = req.addr;
return iobuf_write_8(resp_buf, 0x01);
}
static HRESULT najv4_cmd_namco_extend_cmd(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf)
{
switch(req_buf->bytes[req_buf->pos + 1]) {
case JVS_NAMCO_EXTEND_CMD_NOOP_18:
return najv4_cmd_namco_extend_noop_cmd(najv4, req_buf, resp_buf);
default:
dprintf("JVS I/O: Node %X Unknown Namco Extended command %X\n",
najv4->addr,
req_buf->bytes[req_buf->pos + 1]);
return E_NOTIMPL;
}
}
static HRESULT najv4_cmd_namco_extend_noop_cmd(
struct najv4 *najv4,
struct const_iobuf *req_buf,
struct iobuf *resp_buf)
{
uint8_t strlen = (int)req_buf->nbytes - 9;
uint8_t info[100];
uint8_t cmd;
uint8_t subcmd;
uint16_t hardcode;
uint16_t param;
HRESULT hr;
hr = iobuf_read_8(req_buf, &cmd); // cmd (0x70)
hr = iobuf_read_8(req_buf, &subcmd); // subcmd (0x18)
hr = iobuf_read_be16(req_buf, &hardcode); // some constant (0x504c)
hr = iobuf_read_be16(req_buf, &param); // param1
if (FAILED(hr)) {
return hr;
}
dprintf("JVS I/O: Namco Extended Command 0x18 param: %X", param);
switch (param) {
case 0x14d0: // read some string
hr = iobuf_read(req_buf, info, strlen);
if (FAILED(hr)) {
return hr;
}
dprintf("\t%s\n", info);
return iobuf_write_8(resp_buf, 1);
case 0x143c: // Run it back??
hr = iobuf_read_8(req_buf, &cmd); // cmd (0x70)
hr = iobuf_read_8(req_buf, &subcmd); // subcmd (0x18)
hr = iobuf_read_be16(req_buf, &hardcode); // some constant (0x504c)
hr = iobuf_read_be16(req_buf, &param); // param1
dprintf("\tsecond param: %X\n", param);
return iobuf_write_8(resp_buf, 1);
default:
dprintf("\nJVS I/O: Namco Extended Command 0x18 param unknown!\n");
dump_const_iobuf(req_buf);
return iobuf_write_8(resp_buf, 1);
}
}

37
board/najv4.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <stdint.h>
#include "jvs/jvs-bus.h"
struct najv4_switch_state {
/* Note: this struct is host-endian. The NAJV4 emulator handles the conversion
to protocol-endian. */
uint8_t system;
uint16_t p1;
uint16_t p2;
};
struct najv4_ops {
void (*reset)(void *ctx);
void (*write_gpio)(void *ctx, uint32_t state);
void (*read_switches)(void *ctx, struct najv4_switch_state *out);
void (*read_analogs)(void *ctx, uint16_t *analogs, uint8_t nanalogs);
void (*read_coin_counter)(void *ctx, uint8_t slot_no, uint16_t *out);
};
struct najv4 {
struct jvs_node jvs;
uint8_t addr;
const struct najv4_ops *ops;
void *ops_ctx;
};
void najv4_init(
struct najv4 *najv4,
struct jvs_node *next,
const struct najv4_ops *ops,
void *ops_ctx);
struct jvs_node *najv4_to_jvs_node(struct najv4 *najv4);

10
cross-mingw-32.txt Normal file
View File

@ -0,0 +1,10 @@
[binaries]
c = 'i686-w64-mingw32-gcc'
ar = 'i686-w64-mingw32-ar'
strip = 'i686-w64-mingw32-strip'
[host_machine]
system = 'windows'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'

10
cross-mingw-64.txt Normal file
View File

@ -0,0 +1,10 @@
[binaries]
c = 'x86_64-w64-mingw32-gcc'
ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip'
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'

66
dist/ferrum/bananatools.ini vendored Normal file
View File

@ -0,0 +1,66 @@
; Controls the virtual file system hooks. These redirect file i/o
; requests to a folder specified below, instead of the drive.
; These are all required, even if the game doesn't use one of them.
[vfs]
path=
; Security dongle emulation, disable if you have a
; real dongle connected that you want to use
[dongle]
enable=1
serial=123456789012
; Set the network environment. Most games seem to want 192.168.123.X
[netenv]
enable=1
subnet=192.168.123.0
; Graphics hook, may cause crashes in some games
[gfx]
enable=1
windowed=1
framed=0
monitor=0
; Control the AMCUS replacement class
[amcus]
enable=1
game_id=SDAK
am_game_ver=1.00
cacfg_game_ver=00.01
server_uri=localhost
server_host=localhost
; Controlls the xinput hooks
[xinput]
enable=1
; JVS config
[jvs]
enable=1
port=3
; Mappings for the najv4 IO board. To disable JVS emulation and use
; a real board, set enable to 0 in the "jvs" section.
[najv4]
test=0x24 ; "Home" key
coin=0x2D ; "Insert" key
service=0x2E ; "Delete" key
up=0x26 ; Up arrow
down=0x28 ; Down arrow
enter=0x0D ; "Enter" key
; Mappings for the gamepad. To disable gamepad eumlation and use
; a real pokken arcade controller, set enable to 0 in the "xinput" section
[gamepad]
dpad_up=0x57 ; W
dpad_down=0x53 ; A
dpad_left=0x41 ; S
dpad_right=0x44 ; D
button_a=0x4F ; O
button_b=0x4B ; K
button_x=0x49 ; I
button_y=0x4A ; J
trigger_l=0x51 ; Q
trigger_r=0x45 ; E
button_start=0xA0 ; Left Shift

9
dist/ferrum/start.bat vendored Normal file
View File

@ -0,0 +1,9 @@
@echo off
pushd %~dp0
inject.exe -d -k ferrumhook.dll ferrum_app.exe
echo.
echo The game process has terminated
pause

31
dist/taiko/bananatools.ini vendored Normal file
View File

@ -0,0 +1,31 @@
; Controls the virtual file system hooks. Redirects all drive to
; [path you set here]\\[drive letter]
[vfs]
path=
; Security dongle emulation, disable if you have a
; real dongle connected that you want to use
[dongle]
enable=1
serial=12345678-90123456
; Set the network environment. Most games seem to want 192.168.123.X
[netenv]
enable=1
subnet=192.168.123.0
; Graphics hook, may cause crashes in some games
[gfx]
enable=1
windowed=1
framed=0
monitor=0
; Control the AMCUS replacement class
[amcus]
enable=1
game_id=SBWY
am_game_ver=12.00
cacfg_game_ver=08.18
server_uri=localhost
server_host=localhost

9
dist/taiko/start.bat vendored Normal file
View File

@ -0,0 +1,9 @@
@echo off
pushd %~dp0
inject.exe -d -k taikohook.dll Taiko.exe
echo.
echo The game process has terminated
pause

17
doc/ferrumhook.md Normal file
View File

@ -0,0 +1,17 @@
# ferrumhook
# Supported games
* Pokken Tournamen A**
## General remarks
* The minimum version of Windows that this game supports is Windows 7
* The entire game is in Japanese except for the operator menu
* Running the game with locale hooks disabled with anything but japanese locale on your PC will cause a crash at startup
* The gamepad is just a regular xinput device
## Known issues
* gfxhook doesn't work as the dxgi/dx11 hooks are currently non-functional.
* Networking functionality is untested

8
doc/taikohook.md Normal file
View File

@ -0,0 +1,8 @@
# ferrumhook
# Supported games
* Taiko Nijiro v8.18
## General remarks
* Network or an error screen bypass hex edit is required to boot the game.

41
docker-build.bat Normal file
View File

@ -0,0 +1,41 @@
@echo off
setlocal enabledelayedexpansion
:: Static Environment Variables
set BUILD_OUTPUT_PATH=build\docker
set IMAGE_NAME=hay1tsme/bananatools-build:latest
set CONTAINER_NAME=bananatools-build
:: Main Execution
docker build . -t %IMAGE_NAME%
if ERRORLEVEL 1 (
goto failure
)
docker create --name %CONTAINER_NAME% %IMAGE_NAME%
if ERRORLEVEL 1 (
goto failure
)
rd /s /q "!BUILD_OUTPUT_PATH!"
mkdir "!BUILD_OUTPUT_PATH!"
docker cp %CONTAINER_NAME%:/bananatools/build/zip %BUILD_OUTPUT_PATH%
docker rm -f %CONTAINER_NAME%
docker image rm -f %IMAGE_NAME%
goto success
:failure
echo bananatools Docker build FAILED!
goto finish
:success
echo bananatools Docker build completed successfully.
goto finish
:finish
pause

57
ferrumhook/config.c Normal file
View File

@ -0,0 +1,57 @@
#include <assert.h>
#include <stddef.h>
#include "ferrumhook/config.h"
#include "platform/config.h"
void ferrum_dll_config_load(
struct ferrum_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"ferrumio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void ferrum_xinput_config_load(
struct ferrum_xinput_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"xinput", L"enable", 1, filename);
}
void ferrum_network_config_load(
struct ferrum_network_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"network", L"enable", 1, filename);
}
void ferrum_hook_config_load(
struct ferrum_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
ferrum_dll_config_load(&cfg->dll, filename);
ferrum_xinput_config_load(&cfg->xinput, filename);
gfx_config_load(&cfg->gfx, filename);
ferrum_network_config_load(&cfg->network, filename);
}

38
ferrumhook/config.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include <stddef.h>
#include "ferrumhook/ferrum-dll.h"
#include "ferrumhook/xinput.h"
#include "ferrumhook/jvs.h"
#include "ferrumhook/network.h"
#include "platform/config.h"
#include "gfxhook/config.h"
#include "amcus/config.h"
struct ferrum_hook_config {
struct platform_config platform;
struct ferrum_dll_config dll;
struct ferrum_xinput_config xinput;
struct gfx_config gfx;
struct amcus_config amcus;
struct ferrum_network_config network;
};
void ferrum_dll_config_load(
struct ferrum_dll_config *cfg,
const wchar_t *filename);
void ferrum_xinput_config_load(
struct ferrum_xinput_config *cfg,
const wchar_t *filename);
void ferrum_network_config_load(
struct ferrum_network_config *cfg,
const wchar_t *filename);
void ferrum_hook_config_load(
struct ferrum_hook_config *cfg,
const wchar_t *filename);

94
ferrumhook/dllmain.c Normal file
View File

@ -0,0 +1,94 @@
#include <windows.h>
#include <stdlib.h>
#include "ferrumhook/config.h"
#include "ferrumhook/ferrum-dll.h"
#include "ferrumhook/xinput.h"
#include "ferrumhook/jvs.h"
#include "ferrumhook/network.h"
#include "amcus/amcus.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "platform/platform.h"
#include "gfxhook/gfx.h"
#include "gfxhook/dxgi.h"
#include "gfxhook/d3d11.h"
#include "util/dprintf.h"
static HMODULE ferrum_hook_mod;
static process_entry_t ferrum_startup;
static struct ferrum_hook_config ferrum_hook_cfg;
static DWORD CALLBACK ferrum_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin ferrum_pre_startup ---\n");
ferrum_hook_config_load(&ferrum_hook_cfg, L".\\bananatools.ini");
serial_hook_init();
hr = platform_hook_init(&ferrum_hook_cfg.platform, PLATFORM_ES3, ferrum_jvs_init, ferrum_hook_mod);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = ferrum_dll_init(&ferrum_hook_cfg.dll, ferrum_hook_mod);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = ferrum_xinput_init(&ferrum_hook_cfg.xinput);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = amcus_hook_init(&ferrum_hook_cfg.amcus);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = network_hook_init(&ferrum_hook_cfg.network);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
gfx_hook_init(&ferrum_hook_cfg.gfx);
gfx_d3d11_hook_init(&ferrum_hook_cfg.gfx, ferrum_hook_mod);
gfx_dxgi_hook_init(&ferrum_hook_cfg.gfx, ferrum_hook_mod);
dprintf("--- End ferrum_pre_startup ---\n");
return ferrum_startup();
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
ferrum_hook_mod = mod;
hr = process_hijack_startup(ferrum_pre_startup, &ferrum_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

106
ferrumhook/ferrum-dll.c Normal file
View File

@ -0,0 +1,106 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "ferrumhook/ferrum-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym ferrum_dll_syms[] = {
{
.sym = "ferrum_io_jvs_init",
.off = offsetof(struct ferrum_dll, jvs_init),
},{
.sym = "ferrum_io_gamepad_init",
.off = offsetof(struct ferrum_dll, gamepad_init),
}, {
.sym = "ferrum_io_jvs_poll",
.off = offsetof(struct ferrum_dll, jvs_poll),
}, {
.sym = "ferrum_io_gamepad_poll",
.off = offsetof(struct ferrum_dll, gamepad_poll),
}, {
.sym = "ferrum_io_jvs_read_coin_counter",
.off = offsetof(struct ferrum_dll, jvs_read_coin_counter),
}
};
struct ferrum_dll ferrum_dll;
HRESULT ferrum_dll_init(const struct ferrum_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("Ferrum IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Ferrum IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "ferrum_io_get_api_version");
if (get_api_version != NULL) {
ferrum_dll.api_version = get_api_version();
} else {
ferrum_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose ferrum_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (ferrum_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Ferrum IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
ferrum_dll.api_version);
goto end;
}
sym = ferrum_dll_syms;
hr = dll_bind(&ferrum_dll, src, &sym, _countof(ferrum_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Ferrum IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end;
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

22
ferrumhook/ferrum-dll.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <windows.h>
#include "ferrumio/ferrumio.h"
struct ferrum_dll {
uint16_t api_version;
HRESULT (*jvs_init)(void);
HRESULT (*gamepad_init)(void);
HRESULT (*jvs_poll)(uint8_t *opbtn);
HRESULT (*gamepad_poll)(uint16_t *gamebtn);
void (*jvs_read_coin_counter)(uint16_t *coins);
};
struct ferrum_dll_config {
wchar_t path[MAX_PATH];
};
extern struct ferrum_dll ferrum_dll;
HRESULT ferrum_dll_init(const struct ferrum_dll_config *cfg, HINSTANCE self);

View File

@ -0,0 +1,9 @@
LIBRARY ferrumhook
EXPORTS
ferrum_io_get_api_version
ferrum_io_jvs_init
ferrum_io_gamepad_init
ferrum_io_jvs_poll
ferrum_io_gamepad_poll
ferrum_io_jvs_read_coin_counter

100
ferrumhook/jvs.c Normal file
View File

@ -0,0 +1,100 @@
#include <windows.h>
#include <assert.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "hook/iobuf.h"
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "hooklib/fdshark.h"
#include "util/dprintf.h"
#include "util/dump.h"
#include "board/najv4.h"
#include "ferrumhook/jvs.h"
#include "ferrumhook/ferrum-dll.h"
static void ferrum_jvs_read_switches(void *ctx, struct najv4_switch_state *out);
static void ferrum_jvs_read_coin_counter(void *ctx, uint8_t slot_no, uint16_t *out);
static const struct najv4_ops ferrum_jvs_najv4_ops = {
.read_switches = ferrum_jvs_read_switches,
.read_coin_counter = ferrum_jvs_read_coin_counter,
};
static struct najv4 ferrum_jvs_najv4;
HRESULT ferrum_jvs_init(struct jvs_node **out)
{
HRESULT hr;
assert(out != NULL);
assert(ferrum_dll.jvs_init != NULL);
dprintf("Ferrum JVS: Starting IO backend\n");
hr = ferrum_dll.jvs_init();
if (FAILED(hr)) {
dprintf("Ferrum JVS: Backend error, I/O disconnected: %x\n", (int) hr);
return hr;
}
najv4_init(&ferrum_jvs_najv4, NULL, &ferrum_jvs_najv4_ops, NULL);
*out = najv4_to_jvs_node(&ferrum_jvs_najv4);
return S_OK;
}
static void ferrum_jvs_read_switches(void *ctx, struct najv4_switch_state *out)
{
uint8_t opbtn = 0;
//dprintf("Ferrum JVS: Read Switches\n");
assert(out != NULL);
assert(ferrum_dll.jvs_poll != NULL);
ferrum_dll.jvs_poll(&opbtn);
out->system = 0;
out->p1 = 0;
out->p2 = 0;
if (opbtn & 0x01) { // Test
out->system = 0x80;
}
if (opbtn & 0x02) { // Service
out->p1 |= 0x4000;
}
if (opbtn & 0x04) { // Up
out->p1 |= 0x2000;
}
if (opbtn & 0x08) { // Down
out->p1 |= 0x1000;
}
if (opbtn & 0x10) { // Enter
out->p1 |= 0x0200;
}
}
static void ferrum_jvs_read_coin_counter(void *ctx, uint8_t slot_no, uint16_t *out)
{
//dprintf("Ferrum JVS: Read coin counter\n");
assert(out != NULL);
assert(ferrum_dll.jvs_read_coin_counter != NULL);
if (slot_no > 0) {
return;
}
ferrum_dll.jvs_read_coin_counter(out);
}

8
ferrumhook/jvs.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "jvs/jvs-bus.h"
HRESULT ferrum_jvs_init(struct jvs_node **root);

35
ferrumhook/meson.build Normal file
View File

@ -0,0 +1,35 @@
shared_library(
'ferrumhook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'ferrumhook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
xinput_lib,
],
link_with : [
ferrumio_lib,
amcus_lib,
platform_lib,
util_lib,
hooklib_lib,
gfxhook_lib,
jvs_lib,
],
sources : [
'dllmain.c',
'config.c',
'config.h',
'ferrum-dll.c',
'ferrum-dll.h',
'xinput.c',
'xinput.h',
'jvs.c',
'jvs.h',
'network.c',
'network.h',
],
)

66
ferrumhook/network.c Normal file
View File

@ -0,0 +1,66 @@
#include <windows.h>
#include "taikohook/network.h"
#include "hook/table.h"
#include "util/dprintf.h"
void network_insert_hooks(HMODULE target);
static uint64_t my_TLSv1_method();
static uint64_t my_SSL_CTX_new(void *method);
static uint64_t (*next_TLSv1_2_method)();
static uint64_t (*next_TLSv1_method)();
static uint64_t (*next_SSL_CTX_new)(void *method);
static const struct hook_symbol nethook_syms[] = {
{
.link = (void *) &next_TLSv1_2_method,
.ordinal = 350
},
{
.patch = my_TLSv1_method,
.link = (void *) &next_TLSv1_method,
.ordinal = 170
},
{
.patch = my_SSL_CTX_new,
.link = (void *) &next_SSL_CTX_new,
.ordinal = 12
},
};
HRESULT network_hook_init(const struct ferrum_network_config *cfg)
{
if (!cfg->enable) {
return S_FALSE;
}
dprintf("Nethook: Init\n");
network_insert_hooks(NULL);
return S_OK;
}
void network_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"ssleay32.dll",
nethook_syms,
_countof(nethook_syms)
);
}
static uint64_t my_TLSv1_method()
{
dprintf("Nethook: Redirect TLS v1.0 to v1.2\n");
return next_TLSv1_2_method();
}
static uint64_t my_SSL_CTX_new(void *method)
{
dprintf("Nethook: my_SSL_CTX_new\n");
return next_SSL_CTX_new(method);
}

10
ferrumhook/network.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct ferrum_network_config {
bool enable;
};
HRESULT network_hook_init(const struct ferrum_network_config *cfg);

154
ferrumhook/xinput.c Normal file
View File

@ -0,0 +1,154 @@
#include <windows.h>
#include <xinput.h>
#include <stdint.h>
#ifdef __GNUC__
#include <ntdef.h>
#else
#include <winnt.h>
#endif
#include "hook/table.h"
#include "ferrumhook/xinput.h"
#include "ferrumhook/ferrum-dll.h"
#include "util/dprintf.h"
#include "util/str.h"
static DWORD WINAPI my_XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities);
static DWORD WINAPI my_XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState);
static DWORD WINAPI my_XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
static DWORD my_driverUtilGetControllerUsbIdPairs(uint64_t qwUnknown, unsigned int *numControllers);
static DWORD (WINAPI *next_XInputGetCapabilities)(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities);
static DWORD (WINAPI *next_XInputGetState)(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities);
static DWORD (WINAPI *next_XInputSetState)(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities);
static DWORD (*next_driverUtilGetControllerUsbIdPairs)(uint64_t qwUnknown, unsigned int *numControllers);
uint8_t lastBtnState = 0;
DWORD packetNum = 0;
static const struct hook_symbol xinput_hook_syms[] = {
{
.ordinal = 4,
.patch = my_XInputGetCapabilities,
.link = (void **) &next_XInputGetCapabilities,
}, {
.ordinal = 2,
.patch = my_XInputGetState,
.link = (void **) &next_XInputGetState,
}, {
.ordinal = 3,
.patch = my_XInputSetState,
.link = (void **) &next_XInputSetState,
},
};
static const struct hook_symbol driverutil_hook_syms[] = {
{
.name = "driverUtilGetControllerUsbIdPairs",
.ordinal = 1,
.patch = my_driverUtilGetControllerUsbIdPairs,
.link = (void **) &next_driverUtilGetControllerUsbIdPairs,
}
};
HRESULT ferrum_xinput_init(struct ferrum_xinput_config *cfg)
{
if (!cfg->enable) {
dprintf("Xinput: Emulation disabled\n");
return S_OK;
}
dprintf("Xinput: init\n");
hook_table_apply(
NULL,
"XINPUT1_3.dll",
xinput_hook_syms,
_countof(xinput_hook_syms));
hook_table_apply(
NULL,
"driverUtil.dll",
driverutil_hook_syms,
_countof(driverutil_hook_syms));
return S_OK;
}
static DWORD WINAPI my_XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities)
{
//dprintf("Xinput: %s dwUserIndex %li dwFlags %li \n", __func__, dwUserIndex, dwFlags);
if (!dwUserIndex) {
HRESULT hr = ferrum_dll.gamepad_init();
if (FAILED(hr)) {
return ERROR_DEVICE_NOT_CONNECTED;
}
pCapabilities->Flags = XINPUT_CAPS_VOICE_SUPPORTED;
pCapabilities->Type = XINPUT_DEVTYPE_GAMEPAD;
pCapabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD;
pCapabilities->Gamepad.wButtons = 0xF3FF;
pCapabilities->Gamepad.bLeftTrigger = 0xFF;
pCapabilities->Gamepad.bRightTrigger = 0xFF;
pCapabilities->Gamepad.sThumbLX = (SHORT)0xFFC0;
pCapabilities->Gamepad.sThumbLY = (SHORT)0xFFC0;
pCapabilities->Gamepad.sThumbRX = (SHORT)0xFFC0;
pCapabilities->Gamepad.sThumbRY = (SHORT)0xFFC0;
pCapabilities->Vibration.wLeftMotorSpeed = 0xFF;
pCapabilities->Vibration.wRightMotorSpeed = 0xFF;
return ERROR_SUCCESS;
} else {
return ERROR_DEVICE_NOT_CONNECTED;
}
}
static DWORD WINAPI my_XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState)
{
//dprintf("Xinput: %s dwUserIndex %li\n", __func__, dwUserIndex);
assert(ferrum_dll.gamepad_poll != NULL);
if (!dwUserIndex) {
uint16_t gamebtn = 0;
ferrum_dll.gamepad_poll(&gamebtn);
pState->Gamepad.wButtons = gamebtn;
if (gamebtn == lastBtnState) {
pState->dwPacketNumber = packetNum;
} else {
pState->dwPacketNumber = packetNum++;
}
return ERROR_SUCCESS;
}
else {
return ERROR_DEVICE_NOT_CONNECTED;
}
}
static DWORD WINAPI my_XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration)
{
//dprintf("Xinput: %s dwUserIndex %li left %i right %i\n", __func__, dwUserIndex, pVibration->wLeftMotorSpeed, pVibration->wRightMotorSpeed);
if (!dwUserIndex)
return ERROR_SUCCESS;
else
return ERROR_DEVICE_NOT_CONNECTED;
}
static DWORD my_driverUtilGetControllerUsbIdPairs(uint64_t qwUnknown, unsigned int *numControllers)
{
dprintf("Xinput: %s hit!\n", __func__);
return 1;
}

12
ferrumhook/xinput.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
struct ferrum_xinput_config {
bool enable;
};
HRESULT ferrum_xinput_init(struct ferrum_xinput_config *cfg);

32
ferrumio/config.c Normal file
View File

@ -0,0 +1,32 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "ferrumio/config.h"
void ferrum_io_najv4_config_load(struct ferrum_najv4_config *cfg, const wchar_t *filename)
{
cfg->test = GetPrivateProfileIntW(L"najv4", L"test", VK_HOME, filename);
cfg->service = GetPrivateProfileIntW(L"najv4", L"service", VK_DELETE, filename);
cfg->coin = GetPrivateProfileIntW(L"najv4", L"coin", VK_INSERT, filename);
cfg->up = GetPrivateProfileIntW(L"najv4", L"up", VK_UP, filename);
cfg->down = GetPrivateProfileIntW(L"najv4", L"down", VK_DOWN, filename);
cfg->enter = GetPrivateProfileIntW(L"najv4", L"enter", VK_RETURN, filename);
}
void ferrum_io_gamepad_config_load(struct ferrum_gamepad_config *cfg, const wchar_t *filename)
{
cfg->dpad_up = GetPrivateProfileIntW(L"gamepad", L"dpad_up", 'W', filename);
cfg->dpad_down = GetPrivateProfileIntW(L"gamepad", L"dpad_down", 'S', filename);
cfg->dpad_left = GetPrivateProfileIntW(L"gamepad", L"dpad_left", 'A', filename);
cfg->dpad_right = GetPrivateProfileIntW(L"gamepad", L"dpad_right", 'D', filename);
cfg->btn_a = GetPrivateProfileIntW(L"gamepad", L"button_a", 'O', filename);
cfg->btn_b = GetPrivateProfileIntW(L"gamepad", L"button_b", 'K', filename);
cfg->btn_x = GetPrivateProfileIntW(L"gamepad", L"button_x", 'I', filename);
cfg->btn_y = GetPrivateProfileIntW(L"gamepad", L"button_y", 'J', filename);
cfg->trigger_l = GetPrivateProfileIntW(L"gamepad", L"trigger_l", 'Q', filename);
cfg->trigger_r = GetPrivateProfileIntW(L"gamepad", L"trigger_r", 'E', filename);
cfg->btn_start = GetPrivateProfileIntW(L"gamepad", L"button_start", VK_LSHIFT, filename);
}

30
ferrumio/config.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
struct ferrum_najv4_config {
uint8_t test;
uint8_t service;
uint8_t up;
uint8_t down;
uint8_t enter;
uint8_t coin;
};
struct ferrum_gamepad_config {
uint8_t dpad_up;
uint8_t dpad_down;
uint8_t dpad_left;
uint8_t dpad_right;
uint8_t btn_a;
uint8_t btn_b;
uint8_t btn_x;
uint8_t btn_y;
uint8_t trigger_l;
uint8_t trigger_r;
uint8_t btn_start;
};
void ferrum_io_najv4_config_load(struct ferrum_najv4_config *cfg, const wchar_t *filename);
void ferrum_io_gamepad_config_load(struct ferrum_gamepad_config *cfg, const wchar_t *filename);

128
ferrumio/ferrumio.c Normal file
View File

@ -0,0 +1,128 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include <stdbool.h>
#include "ferrumio/ferrumio.h"
#include "ferrumio/config.h"
#include "util/dprintf.h"
static bool ferrum_io_coin = false;
static bool ferrum_test_toggle = false;
static uint16_t ferrum_coin_ct = 0;
static struct ferrum_gamepad_config gamepad_cfg;
static struct ferrum_najv4_config najv4_cfg;
uint16_t ferrum_io_get_api_version(void)
{
return 0x0100;
}
HRESULT ferrum_io_jvs_init(void)
{
dprintf("Ferrum IO: JVS Init\n");
ferrum_io_najv4_config_load(&najv4_cfg, L".\\bananatools.ini");
return S_OK;
}
HRESULT ferrum_io_gamepad_init(void)
{
dprintf("Ferrum IO: Gamepad Init\n");
ferrum_io_gamepad_config_load(&gamepad_cfg, L".\\bananatools.ini");
return S_OK;
}
HRESULT ferrum_io_jvs_poll(uint8_t *opbtn)
{
*opbtn = 0;
if ((GetAsyncKeyState(najv4_cfg.test) & 0x8000)) {
*opbtn |= FERRUM_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(najv4_cfg.service) & 0x8000) {
*opbtn |= FERRUM_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(najv4_cfg.up) & 0x8000) {
*opbtn |= FERRUM_IO_OPBTN_UP;
}
if (GetAsyncKeyState(najv4_cfg.down) & 0x8000) {
*opbtn |= FERRUM_IO_OPBTN_DOWN;
}
if (GetAsyncKeyState(najv4_cfg.enter) & 0x8000) {
*opbtn |= FERRUM_IO_OPBTN_ENTER;
}
return S_OK;
}
HRESULT ferrum_io_gamepad_poll(uint16_t *gamepad)
{
*gamepad = 0;
if (GetAsyncKeyState(gamepad_cfg.dpad_up) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_UP;
}
if (GetAsyncKeyState(gamepad_cfg.dpad_left) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_LEFT;
}
if (GetAsyncKeyState(gamepad_cfg.dpad_down) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_DOWN;
}
if (GetAsyncKeyState(gamepad_cfg.dpad_right) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_RIGHT;
}
if (GetAsyncKeyState(gamepad_cfg.trigger_l) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_TRIGGER_L;
}
if (GetAsyncKeyState(gamepad_cfg.trigger_r) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_TRIGGER_R;
}
if (GetAsyncKeyState(gamepad_cfg.btn_a) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_A;
}
if (GetAsyncKeyState(gamepad_cfg.btn_b) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_B;
}
if (GetAsyncKeyState(gamepad_cfg.btn_x) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_X;
}
if (GetAsyncKeyState(gamepad_cfg.btn_y) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_Y;
}
if (GetAsyncKeyState(gamepad_cfg.btn_start) & 0x8000) {
*gamepad |= FERRUM_IO_GAMEBTN_START;
}
return S_OK;
}
void ferrum_io_jvs_read_coin_counter(uint16_t *coins)
{
if (GetAsyncKeyState(VK_INSERT) & 0x8000) {
if (!ferrum_io_coin) {
ferrum_io_coin = true;
ferrum_coin_ct++;
}
} else {
ferrum_io_coin = false;
}
*coins = ferrum_coin_ct;
}

9
ferrumio/ferrumio.def Normal file
View File

@ -0,0 +1,9 @@
LIBRARY ferrumhook
EXPORTS
ferrum_io_get_api_version
ferrum_io_jvs_init
ferrum_io_gamepad_init
ferrum_io_jvs_poll
ferrum_io_gamepad_poll
ferrum_io_read_coin_counter

68
ferrumio/ferrumio.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include <windows.h>
#include <stdint.h>
#include "ferrumio/config.h"
enum {
FERRUM_IO_OPBTN_TEST = 0x01,
FERRUM_IO_OPBTN_SERVICE = 0x02,
FERRUM_IO_OPBTN_UP = 0x04,
FERRUM_IO_OPBTN_DOWN = 0x08,
FERRUM_IO_OPBTN_ENTER = 0x10,
FERRUM_IO_OPBTN_COIN = 0x20,
};
// Chagned to match xinput masks for ease of use
enum {
FERRUM_IO_GAMEBTN_UP = 0x0001,
FERRUM_IO_GAMEBTN_DOWN = 0x0002,
FERRUM_IO_GAMEBTN_LEFT = 0x0004,
FERRUM_IO_GAMEBTN_RIGHT = 0x0008,
FERRUM_IO_GAMEBTN_START = 0x0010,
FERRUM_IO_GAMEBTN_TRIGGER_L = 0x0100,
FERRUM_IO_GAMEBTN_TRIGGER_R = 0x0200,
FERRUM_IO_GAMEBTN_A = 0x2000,
FERRUM_IO_GAMEBTN_B = 0x1000,
FERRUM_IO_GAMEBTN_X = 0x8000,
FERRUM_IO_GAMEBTN_Y = 0x4000,
};
/* Get the version of the Pokken 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 ferrum_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after ferrum_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT ferrum_io_jvs_init(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after ferrum_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT ferrum_io_gamepad_init(void);
/* Send any queued outputs (of which there are currently none, though this may
change in subsequent API versions) and retrieve any new inputs.
Minimum API version: 0x0100 */
HRESULT ferrum_io_jvs_poll(uint8_t *opbtn);
HRESULT ferrum_io_gamepad_poll(uint16_t *gamebtn);
void ferrum_io_jvs_read_coin_counter(uint16_t *coins);

16
ferrumio/meson.build Normal file
View File

@ -0,0 +1,16 @@
ferrumio_lib = static_library(
'ferrumio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
xinput_lib,
],
sources : [
'ferrumio.c',
'ferrumio.h',
'config.c',
'config.h',
],
)

17
gfxhook/config.c Normal file
View File

@ -0,0 +1,17 @@
#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);
}

7
gfxhook/config.h Normal file
View File

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

199
gfxhook/d3d11.c Normal file
View File

@ -0,0 +1,199 @@
#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");
if (next_D3D11CreateDevice == NULL) {
dprintf("D3D11: next_D3D11CreateDevice is null!!\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);
}

7
gfxhook/d3d11.h Normal file
View File

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

251
gfxhook/d3d9.c Normal file
View File

@ -0,0 +1,251 @@
#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);
}
dprintf("Gfx: Using adapter %d\n", gfx_config.monitor);
return IDirect3D9_CreateDevice(real, gfx_config.monitor, 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);
}

7
gfxhook/d3d9.h Normal file
View File

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

367
gfxhook/dxgi.c Normal file
View File

@ -0,0 +1,367 @@
#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;
dprintf("DXGI: hook_factory\n");
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;
}
dprintf("DXGI: hook_factory done\n");
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);
}

7
gfxhook/dxgi.h Normal file
View File

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

162
gfxhook/gfx.c Normal file
View File

@ -0,0 +1,162 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "gfxhook/gfx.h"
#include "hook/table.h"
#include "util/dprintf.h"
typedef BOOL (WINAPI *ShowWindow_t)(HWND hWnd, int nCmdShow);
typedef HWND (WINAPI *CreateWindowExA_t)(
DWORD dwExStyle,
LPCSTR lpClassName,
LPCSTR lpWindowName,
DWORD dwStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
typedef HWND (WINAPI *CreateWindowExW_t)(
DWORD dwExStyle,
LPCWSTR lpClassName,
LPCWSTR lpWindowName,
DWORD dwStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
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
);
static HWND WINAPI hook_CreateWindowExW(
DWORD dwExStyle,
LPCWSTR lpClassName,
LPCWSTR 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 ShowWindow_t next_ShowWindow;
static CreateWindowExA_t next_CreateWindowExA;
static CreateWindowExW_t next_CreateWindowExW;
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,
},
{
.name = "CreateWindowExW",
.patch = hook_CreateWindowExW,
.link = (void **) &next_CreateWindowExW,
},
};
void gfx_hook_init(const struct gfx_config *cfg)
{
assert(cfg != NULL);
if (!cfg->enable) {
return;
}
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
)
{
dprintf("Gfx: CreateWindowExA hook hit\n");
DWORD windowStyle = 0;
windowStyle = WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
return next_CreateWindowExA(dwExStyle, lpClassName, lpWindowName, windowStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
}
static HWND WINAPI hook_CreateWindowExW(
DWORD dwExStyle,
LPCWSTR lpClassName,
LPCWSTR lpWindowName,
DWORD dwStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
)
{
dprintf("Gfx: CreateWindowExW hook hit\n");
DWORD windowStyle = 0;
windowStyle = WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
return next_CreateWindowExW(dwExStyle, lpClassName, lpWindowName, windowStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
}

12
gfxhook/gfx.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <stdbool.h>
struct gfx_config {
bool enable;
bool windowed;
bool framed;
int monitor;
};
void gfx_hook_init(const struct gfx_config *cfg);

28
gfxhook/meson.build Normal file
View File

@ -0,0 +1,28 @@
gfxhook_lib = static_library(
'gfxhook',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
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',
'util.c',
'util.h',
],
)

116
gfxhook/util.c Normal file
View File

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

7
gfxhook/util.h Normal file
View File

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

16
hooklib/config.c Normal file
View File

@ -0,0 +1,16 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.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);
}

7
hooklib/config.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <stddef.h>
#include "hooklib/dvd.h"
void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename);

351
hooklib/dll.c Normal file
View File

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

10
hooklib/dll.h Normal file
View File

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

472
hooklib/dns.c Normal file
View File

@ -0,0 +1,472 @@
/* Might push this to capnhook, don't add any util dependencies. */
#include <windows.h>
#include <windns.h>
#include <ws2tcpip.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "hook/hr.h"
#include "hook/table.h"
#include "hooklib/dns.h"
#include "util/dprintf.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);
/* 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 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 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 void dns_hook_init(void)
{
if (dns_hook_initted) {
return;
}
dns_hook_initted = true;
InitializeCriticalSection(&dns_hook_lock);
hook_table_apply(
NULL,
"dnsapi.dll",
dns_hook_syms_dnsapi,
_countof(dns_hook_syms_dnsapi));
hook_table_apply(
NULL,
"ws2_32.dll",
dns_hook_syms_ws2,
_countof(dns_hook_syms_ws2));
}
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;
}
dprintf("DNS: %s Lookup %s", __func__, pszName);
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 (_wcsicmp(wstr, pos->from) == 0) {
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;
}
dprintf("DNS: %s Lookup %ls", __func__, pszName);
EnterCriticalSection(&dns_hook_lock);
for (i = 0 ; i < dns_hook_nentries ; i++) {
pos = &dns_hook_entries[i];
if (_wcsicmp(pszName, pos->from) == 0) {
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;
dprintf("DNS: %s Lookup %ls", __func__, orig);
EnterCriticalSection(&dns_hook_lock);
for (i = 0 ; i < dns_hook_nentries ; i++) {
pos = &dns_hook_entries[i];
if (_wcsicmp(pRequest->QueryName, pos->from) == 0) {
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;
dprintf("DNS: getaddrinfo pNodeName %s pServiceName %s\n", pNodeName, pServiceName);
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 (_wcsicmp(wstr, pos->from) == 0) {
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;
}

9
hooklib/dns.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <windows.h>
#include <stddef.h>
// 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);

82
hooklib/dvd.c Normal file
View File

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

14
hooklib/dvd.h Normal file
View File

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

218
hooklib/fdshark.c Normal file
View File

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

16
hooklib/fdshark.h Normal file
View File

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

29
hooklib/meson.build Normal file
View File

@ -0,0 +1,29 @@
hooklib_lib = static_library(
'hooklib',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],
sources : [
'config.c',
'config.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',
],
)

856
hooklib/path.c Normal file
View File

@ -0,0 +1,856 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "hook/hr.h"
#include "hook/table.h"
#include "hooklib/path.h"
/* Helpers */
static void path_hook_init(void);
static BOOL path_transform_a(char **out, const char *src);
static BOOL path_transform_w(wchar_t **out, const wchar_t *src);
/* API hooks */
static BOOL WINAPI hook_CreateDirectoryA(
const char *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL WINAPI hook_CreateDirectoryW(
const wchar_t *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL WINAPI hook_CreateDirectoryExA(
const char *lpTemplateDirectory,
const char *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL WINAPI hook_CreateDirectoryExW(
const wchar_t *lpTemplateDirectory,
const wchar_t *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static HANDLE WINAPI hook_CreateFileA(
const char *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static HANDLE WINAPI hook_CreateFileW(
const wchar_t *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static HANDLE WINAPI hook_FindFirstFileA(
const char *lpFileName,
LPWIN32_FIND_DATAA lpFindFileData);
static HANDLE WINAPI hook_FindFirstFileW(
const wchar_t *lpFileName,
LPWIN32_FIND_DATAW lpFindFileData);
static HANDLE WINAPI hook_FindFirstFileExA(
const char *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static HANDLE WINAPI hook_FindFirstFileExW(
const wchar_t *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static DWORD WINAPI hook_GetFileAttributesA(const char *lpFileName);
static DWORD WINAPI hook_GetFileAttributesW(const wchar_t *lpFileName);
static BOOL WINAPI hook_GetFileAttributesExA(
const char *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation);
static BOOL WINAPI hook_GetFileAttributesExW(
const wchar_t *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation);
static BOOL WINAPI hook_RemoveDirectoryA(const char *lpFileName);
static BOOL WINAPI hook_RemoveDirectoryW(const wchar_t *lpFileName);
/* Link pointers */
static BOOL (WINAPI *next_CreateDirectoryA)(
const char *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL (WINAPI *next_CreateDirectoryW)(
const wchar_t *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL (WINAPI *next_CreateDirectoryExA)(
const char *lpTemplateDirectory,
const char *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL (WINAPI *next_CreateDirectoryExW)(
const wchar_t *lpTemplateDirectory,
const wchar_t *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static HANDLE (WINAPI *next_CreateFileA)(
const char *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static HANDLE (WINAPI *next_CreateFileW)(
const wchar_t *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static HANDLE (WINAPI *next_FindFirstFileA)(
const char *lpFileName,
LPWIN32_FIND_DATAA lpFindFileData);
static HANDLE (WINAPI *next_FindFirstFileW)(
const wchar_t *lpFileName,
LPWIN32_FIND_DATAW lpFindFileData);
static HANDLE (WINAPI *next_FindFirstFileExA)(
const char *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static HANDLE (WINAPI *next_FindFirstFileExW)(
const wchar_t *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static DWORD (WINAPI *next_GetFileAttributesA)(const char *lpFileName);
static DWORD (WINAPI *next_GetFileAttributesW)(const wchar_t *lpFileName);
static BOOL (WINAPI *next_GetFileAttributesExA)(
const char *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation);
static BOOL (WINAPI *next_GetFileAttributesExW)(
const wchar_t *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation);
static BOOL (WINAPI *next_RemoveDirectoryA)(const char *lpFileName);
static BOOL (WINAPI *next_RemoveDirectoryW)(const wchar_t *lpFileName);
/* Hook table */
static const struct hook_symbol path_hook_syms[] = {
{
.name = "CreateDirectoryA",
.patch = hook_CreateDirectoryA,
.link = (void **) &next_CreateDirectoryA,
}, {
.name = "CreateDirectoryW",
.patch = hook_CreateDirectoryW,
.link = (void **) &next_CreateDirectoryW,
}, {
.name = "CreateDirectoryExA",
.patch = hook_CreateDirectoryExA,
.link = (void **) &next_CreateDirectoryExA,
}, {
.name = "CreateDirectoryExW",
.patch = hook_CreateDirectoryExW,
.link = (void **) &next_CreateDirectoryExW,
}, {
.name = "CreateFileA",
.patch = hook_CreateFileA,
.link = (void **) &next_CreateFileA,
}, {
.name = "CreateFileW",
.patch = hook_CreateFileW,
.link = (void **) &next_CreateFileW,
}, {
.name = "FindFirstFileA",
.patch = hook_FindFirstFileA,
.link = (void **) &next_FindFirstFileA,
}, {
.name = "FindFirstFileW",
.patch = hook_FindFirstFileW,
.link = (void **) &next_FindFirstFileW,
}, {
.name = "FindFirstFileExA",
.patch = hook_FindFirstFileExA,
.link = (void **) &next_FindFirstFileExA,
}, {
.name = "FindFirstFileExW",
.patch = hook_FindFirstFileExW,
.link = (void **) &next_FindFirstFileExW,
}, {
.name = "GetFileAttributesA",
.patch = hook_GetFileAttributesA,
.link = (void **) &next_GetFileAttributesA,
}, {
.name = "GetFileAttributesW",
.patch = hook_GetFileAttributesW,
.link = (void **) &next_GetFileAttributesW,
}, {
.name = "GetFileAttributesExA",
.patch = hook_GetFileAttributesExA,
.link = (void **) &next_GetFileAttributesExA,
}, {
.name = "GetFileAttributesExW",
.patch = hook_GetFileAttributesExW,
.link = (void **) &next_GetFileAttributesExW,
}, {
.name = "RemoveDirectoryA",
.patch = hook_RemoveDirectoryA,
.link = (void **) &next_RemoveDirectoryA,
}, {
.name = "RemoveDirectoryW",
.patch = hook_RemoveDirectoryW,
.link = (void **) &next_RemoveDirectoryW,
}
};
static bool path_hook_initted;
static CRITICAL_SECTION path_hook_lock;
static path_hook_t *path_hook_list;
static size_t path_hook_count;
HRESULT path_hook_push(path_hook_t hook)
{
path_hook_t *tmp;
HRESULT hr;
assert(hook != NULL);
path_hook_init();
EnterCriticalSection(&path_hook_lock);
tmp = realloc(
path_hook_list,
(path_hook_count + 1) * sizeof(path_hook_t));
if (tmp == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
path_hook_list = tmp;
path_hook_list[path_hook_count++] = hook;
hr = S_OK;
end:
LeaveCriticalSection(&path_hook_lock);
return hr;
}
static void path_hook_init(void)
{
/* Init is not thread safe because API hook init is not thread safe blah
blah blah you know the drill by now. */
if (path_hook_initted) {
return;
}
path_hook_initted = true;
InitializeCriticalSection(&path_hook_lock);
path_hook_insert_hooks(NULL);
}
void path_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
path_hook_syms,
_countof(path_hook_syms));
}
static BOOL path_transform_a(char **out, const char *src)
{
wchar_t *src_w;
size_t src_c;
wchar_t *dest_w;
char *dest_a;
size_t dest_s;
BOOL ok;
assert(out != NULL);
src_w = NULL;
dest_w = NULL;
dest_a = NULL;
*out = NULL;
if (src == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
ok = FALSE;
goto end;
}
/* Widen the path */
mbstowcs_s(&src_c, NULL, 0, src, 0);
src_w = malloc(src_c * sizeof(wchar_t));
if (src_w == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
ok = FALSE;
goto end;
}
mbstowcs_s(NULL, src_w, src_c, src, src_c - 1);
/* Try applying a path transform */
ok = path_transform_w(&dest_w, src_w); /* Take ownership! */
if (!ok || dest_w == NULL) {
goto end;
}
/* Narrow the transformed path */
wcstombs_s(&dest_s, NULL, 0, dest_w, 0);
dest_a = malloc(dest_s * sizeof(char));
if (dest_a == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
ok = FALSE;
goto end;
}
wcstombs_s(NULL, dest_a, dest_s, dest_w, dest_s - 1);
*out = dest_a; /* Relinquish ownership to caller! */
ok = TRUE;
end:
free(dest_w);
free(src_w);
return ok;
}
static BOOL path_transform_w(wchar_t **out, const wchar_t *src)
{
BOOL ok;
HRESULT hr;
wchar_t *dest;
size_t dest_c;
size_t i;
assert(out != NULL);
dest = NULL;
*out = NULL;
EnterCriticalSection(&path_hook_lock);
for (i = 0 ; i < path_hook_count ; i++) {
hr = path_hook_list[i](src, NULL, &dest_c);
if (FAILED(hr)) {
ok = hr_propagate_win32(hr, FALSE);
goto end;
}
if (hr == S_FALSE) {
continue;
}
dest = malloc(dest_c * sizeof(wchar_t));
if (dest == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
ok = FALSE;
goto end;
}
hr = path_hook_list[i](src, dest, &dest_c);
if (FAILED(hr)) {
ok = hr_propagate_win32(hr, FALSE);
goto end;
}
break;
}
*out = dest;
dest = NULL;
ok = TRUE;
end:
LeaveCriticalSection(&path_hook_lock);
free(dest);
return ok;
}
int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count)
{
size_t i;
wchar_t c1, c2;
assert(string1 != NULL);
assert(string2 != NULL);
for (i = 0; i < count && string1[i] && string2[i]; i++) {
c1 = towlower(string1[i]);
if (c1 == '/') {
c1 = '\\';
}
c2 = towlower(string2[i]);
if (c2 == '/') {
c2 = '\\';
}
if (c1 != c2) {
break;
}
}
return i == count ? 0 : string2[i] - string1[i];
}
/* Dumping ground for kernel32 file system ops whose path parameters we have to
hook into and translate. This list will grow over time as we go back and
fix up older games that don't pay attention to the mount point registry. */
static BOOL WINAPI hook_CreateDirectoryA(
const char *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes)
{
char *trans;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_CreateDirectoryA(
trans ? trans : lpFileName,
lpSecurityAttributes);
free(trans);
return ok;
}
static BOOL WINAPI hook_CreateDirectoryW(
const wchar_t *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes)
{
wchar_t *trans;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_CreateDirectoryW(
trans ? trans : lpFileName,
lpSecurityAttributes);
free(trans);
return ok;
}
static BOOL WINAPI hook_CreateDirectoryExA(
const char *lpTemplateDirectory,
const char *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes)
{
char *trans;
BOOL ok;
ok = path_transform_a(&trans, lpNewDirectory);
if (!ok) {
return FALSE;
}
ok = next_CreateDirectoryExA(
lpTemplateDirectory,
trans ? trans : lpNewDirectory,
lpSecurityAttributes);
free(trans);
return ok;
}
static BOOL WINAPI hook_CreateDirectoryExW(
const wchar_t *lpTemplateDirectory,
const wchar_t *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes)
{
wchar_t *trans;
BOOL ok;
ok = path_transform_w(&trans, lpNewDirectory);
if (!ok) {
return FALSE;
}
ok = next_CreateDirectoryExW(
lpTemplateDirectory,
trans ? trans : lpNewDirectory,
lpSecurityAttributes);
free(trans);
return ok;
}
/* Don't pull in the entire iohook framework just for CreateFileA/CreateFileW */
static HANDLE WINAPI hook_CreateFileA(
const char *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
char *trans;
HANDLE result;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_CreateFileA(
trans ? trans : lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
free(trans);
return result;
}
static HANDLE WINAPI hook_CreateFileW(
const wchar_t *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
wchar_t *trans;
HANDLE result;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_CreateFileW(
trans ? trans : lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
free(trans);
return result;
}
static HANDLE WINAPI hook_FindFirstFileA(
const char *lpFileName,
LPWIN32_FIND_DATAA lpFindFileData)
{
char *trans;
HANDLE result;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileA(trans ? trans : lpFileName, lpFindFileData);
free(trans);
return result;
}
static HANDLE WINAPI hook_FindFirstFileW(
const wchar_t *lpFileName,
LPWIN32_FIND_DATAW lpFindFileData)
{
wchar_t *trans;
HANDLE result;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileW(trans ? trans : lpFileName, lpFindFileData);
free(trans);
return result;
}
static HANDLE WINAPI hook_FindFirstFileExA(
const char *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags)
{
char *trans;
HANDLE result;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileExA(
trans ? trans : lpFileName,
fInfoLevelId,
lpFindFileData,
fSearchOp,
lpSearchFilter,
dwAdditionalFlags);
free(trans);
return result;
}
static HANDLE WINAPI hook_FindFirstFileExW(
const wchar_t *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags)
{
wchar_t *trans;
HANDLE result;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileExW(
trans ? trans : lpFileName,
fInfoLevelId,
lpFindFileData,
fSearchOp,
lpSearchFilter,
dwAdditionalFlags);
free(trans);
return result;
}
static DWORD WINAPI hook_GetFileAttributesA(const char *lpFileName)
{
char *trans;
DWORD result;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_FILE_ATTRIBUTES;
}
result = next_GetFileAttributesA(trans ? trans : lpFileName);
free(trans);
return result;
}
static DWORD WINAPI hook_GetFileAttributesW(const wchar_t *lpFileName)
{
wchar_t *trans;
DWORD result;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_FILE_ATTRIBUTES;
}
result = next_GetFileAttributesW(trans ? trans : lpFileName);
free(trans);
return result;
}
static BOOL WINAPI hook_GetFileAttributesExA(
const char *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation)
{
char *trans;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_FILE_ATTRIBUTES;
}
ok = next_GetFileAttributesExA(
trans ? trans : lpFileName,
fInfoLevelId,
lpFileInformation);
free(trans);
return ok;
}
static BOOL WINAPI hook_GetFileAttributesExW(
const wchar_t *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation)
{
wchar_t *trans;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_FILE_ATTRIBUTES;
}
ok = next_GetFileAttributesExW(
trans ? trans : lpFileName,
fInfoLevelId,
lpFileInformation);
free(trans);
return ok;
}
static BOOL WINAPI hook_RemoveDirectoryA(const char *lpFileName)
{
char *trans;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_RemoveDirectoryA(trans ? trans : lpFileName);
free(trans);
return ok;
}
static BOOL WINAPI hook_RemoveDirectoryW(const wchar_t *lpFileName)
{
wchar_t *trans;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_RemoveDirectoryW(trans ? trans : lpFileName);
free(trans);
return ok;
}

20
hooklib/path.h Normal file
View File

@ -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'/';
}

891
hooklib/reg.c Normal file
View File

@ -0,0 +1,891 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include "hook/table.h"
#include "hooklib/reg.h"
#include "util/dprintf.h"
#include "util/str.h"
struct reg_hook_key {
HKEY root;
const wchar_t *name;
const struct reg_hook_val *vals;
size_t nvals;
HKEY handle;
};
/* Helper functions */
static void reg_hook_init(void);
static LRESULT reg_hook_propagate_hr(HRESULT hr);
static struct reg_hook_key *reg_hook_match_key_locked(HKEY handle);
static const struct reg_hook_val *reg_hook_match_val_locked(
struct reg_hook_key *key,
const wchar_t *name);
static LSTATUS reg_hook_open_locked(
HKEY parent,
const wchar_t *name,
HKEY *out);
static LSTATUS reg_hook_query_val_locked(
struct reg_hook_key *key,
const wchar_t *name,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
/* API hooks */
static LSTATUS WINAPI hook_RegOpenKeyExW(
HKEY parent,
const wchar_t *name,
uint32_t flags,
uint32_t access,
HKEY *out);
static LSTATUS WINAPI hook_RegCreateKeyExW(
HKEY parent,
const wchar_t *name,
uint32_t reserved,
const wchar_t *class_,
uint32_t options,
uint32_t access,
const SECURITY_ATTRIBUTES *sa,
HKEY *out,
uint32_t *disposition);
static LSTATUS WINAPI hook_RegCloseKey(HKEY handle);
static LSTATUS WINAPI hook_RegQueryValueExA(
HKEY handle,
const char *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS WINAPI hook_RegQueryValueExW(
HKEY handle,
const wchar_t *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS WINAPI hook_RegSetValueExW(
HKEY handle,
const wchar_t *name,
uint32_t reserved,
uint32_t type,
const void *bytes,
uint32_t nbytes);
static LSTATUS WINAPI hook_RegGetValueW(
HKEY hkey,
LPCWSTR lpSubKey,
LPCWSTR lpValue,
uint32_t flags,
uint32_t *type,
void *pData,
uint32_t *numData
);
/* Link pointers */
static LSTATUS (WINAPI *next_RegOpenKeyExW)(
HKEY parent,
const wchar_t *name,
uint32_t flags,
uint32_t access,
HKEY *out);
static LSTATUS (WINAPI *next_RegCreateKeyExW)(
HKEY parent,
const wchar_t *name,
uint32_t reserved,
const wchar_t *class_,
uint32_t options,
uint32_t access,
const SECURITY_ATTRIBUTES *sa,
HKEY *out,
uint32_t *disposition);
static LSTATUS (WINAPI *next_RegCloseKey)(HKEY handle);
static LSTATUS (WINAPI *next_RegQueryValueExA)(
HKEY handle,
const char *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS (WINAPI *next_RegQueryValueExW)(
HKEY handle,
const wchar_t *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS (WINAPI *next_RegSetValueExW)(
HKEY handle,
const wchar_t *name,
uint32_t reserved,
uint32_t type,
const void *bytes,
uint32_t nbytes);
static LSTATUS (WINAPI *next_RegGetValueW)(
HKEY hkey,
LPCWSTR lpSubKey,
LPCWSTR lpValue,
uint32_t flags,
uint32_t *type,
void *pData,
uint32_t *numData
);
static const struct hook_symbol reg_hook_syms[] = {
{
.name = "RegOpenKeyExW",
.patch = hook_RegOpenKeyExW,
.link = (void **) &next_RegOpenKeyExW,
}, {
.name = "RegCreateKeyExW",
.patch = hook_RegCreateKeyExW,
.link = (void **) &next_RegCreateKeyExW,
}, {
.name = "RegCloseKey",
.patch = hook_RegCloseKey,
.link = (void **) &next_RegCloseKey,
}, {
.name = "RegQueryValueExA",
.patch = hook_RegQueryValueExA,
.link = (void **) &next_RegQueryValueExA,
}, {
.name = "RegQueryValueExW",
.patch = hook_RegQueryValueExW,
.link = (void **) &next_RegQueryValueExW,
}, {
.name = "RegSetValueExW",
.patch = hook_RegSetValueExW,
.link = (void **) &next_RegSetValueExW,
}, {
.name = "RegGetValueW",
.patch = hook_RegGetValueW,
.link = (void **) &next_RegGetValueW,
}
};
static bool reg_hook_initted;
static CRITICAL_SECTION reg_hook_lock;
static struct reg_hook_key *reg_hook_keys;
static size_t reg_hook_nkeys;
HRESULT reg_hook_push_key(
HKEY root,
const wchar_t *name,
const struct reg_hook_val *vals,
size_t nvals)
{
struct reg_hook_key *new_mem;
struct reg_hook_key *new_key;
HRESULT hr;
assert(root != NULL);
assert(name != NULL);
assert(vals != NULL || nvals == 0);
reg_hook_init();
/*dprintf("Pushing reg key %ls:\n", name);
for (int i = 0; i < nvals; i++) {
dprintf("\t%ls\n", vals[i].name);
} */
EnterCriticalSection(&reg_hook_lock);
new_mem = realloc(
reg_hook_keys,
(reg_hook_nkeys + 1) * sizeof(struct reg_hook_key));
if (new_mem == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
new_key = &new_mem[reg_hook_nkeys];
memset(new_key, 0, sizeof(*new_key));
new_key->root = root;
new_key->name = name; /* Expect this to be statically allocated */
new_key->vals = vals;
new_key->nvals = nvals;
reg_hook_keys = new_mem;
reg_hook_nkeys++;
hr = S_OK;
end:
LeaveCriticalSection(&reg_hook_lock);
return hr;
}
static void reg_hook_init(void)
{
if (reg_hook_initted) {
return;
}
reg_hook_initted = true;
InitializeCriticalSection(&reg_hook_lock);
dprintf("Reg hook init\n");
hook_table_apply(
NULL,
"advapi32.dll",
reg_hook_syms,
_countof(reg_hook_syms));
}
static LRESULT reg_hook_propagate_hr(HRESULT hr)
{
if (SUCCEEDED(hr)) {
return ERROR_SUCCESS;
} else if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
return HRESULT_CODE(hr);
} else {
return ERROR_GEN_FAILURE;
}
}
static struct reg_hook_key *reg_hook_match_key_locked(HKEY handle)
{
struct reg_hook_key *key;
size_t i;
if (handle == NULL || handle == INVALID_HANDLE_VALUE) {
return NULL;
}
for (i = 0 ; i < reg_hook_nkeys ; i++) {
key = &reg_hook_keys[i];
if (key->handle == handle) {
return key;
}
}
return NULL;
}
static const struct reg_hook_val *reg_hook_match_val_locked(
struct reg_hook_key *key,
const wchar_t *name)
{
const struct reg_hook_val *val;
size_t i;
/* Watch out for accesses to the key's default value */
if (name == NULL) {
name = L"";
}
for (i = 0 ; i < key->nvals ; i++) {
val = &key->vals[i];
if (wstr_ieq(val->name, name)) {
return val;
}
}
return NULL;
}
static LSTATUS reg_hook_open_locked(
HKEY parent,
const wchar_t *name,
HKEY *out)
{
struct reg_hook_key *key;
LSTATUS err;
size_t i;
*out = NULL;
for (i = 0 ; i < reg_hook_nkeys ; i++) {
/* Assume reg keys are referenced from a root key and not from some
intermediary key */
key = &reg_hook_keys[i];
if (key->root == parent && wstr_ieq(key->name, name)) {
break;
}
}
/* (Bail out if we didn't find anything; this causes the open/create call
to be passed onward down the hook chain) */
if (i >= reg_hook_nkeys) {
return ERROR_SUCCESS;
}
/* Assume only one handle will be open at a time */
if (key->handle != NULL) {
return ERROR_SHARING_VIOLATION;
}
/* Open a unique HKEY handle that we can use to identify accesses to
this virtual registry key. We open a read-only handle to an arbitrary
registry key that we can reliably assume exists and isn't one of the
hardcoded root handles. HKLM\SOFTWARE will suffice for this purpose. */
err = next_RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE",
0,
KEY_READ,
out);
if (err == ERROR_SUCCESS) {
key->handle = *out;
}
return err;
}
static LSTATUS WINAPI hook_RegOpenKeyExW(
HKEY parent,
const wchar_t *name,
uint32_t flags,
uint32_t access,
HKEY *out)
{
LSTATUS err;
if (out == NULL) {
return ERROR_INVALID_PARAMETER;
}
EnterCriticalSection(&reg_hook_lock);
err = reg_hook_open_locked(parent, name, out);
LeaveCriticalSection(&reg_hook_lock);
if (err == ERROR_SUCCESS) {
if (*out != NULL) {
//dprintf("Registry: Opened virtual key %S\n", name);
} else {
err = next_RegOpenKeyExW(parent, name, flags, access, out);
}
}
return err;
}
static LSTATUS WINAPI hook_RegCreateKeyExW(
HKEY parent,
const wchar_t *name,
uint32_t reserved,
const wchar_t *class_,
uint32_t options,
uint32_t access,
const SECURITY_ATTRIBUTES *sa,
HKEY *out,
uint32_t *disposition)
{
LSTATUS err;
if (out == NULL) {
return ERROR_INVALID_PARAMETER;
}
EnterCriticalSection(&reg_hook_lock);
err = reg_hook_open_locked(parent, name, out);
LeaveCriticalSection(&reg_hook_lock);
if (err == ERROR_SUCCESS) {
if (*out != NULL) {
//dprintf("Registry: Created virtual key %S\n", name);
} else {
err = next_RegCreateKeyExW(
parent,
name,
reserved,
class_,
options,
access,
sa,
out,
disposition);
}
}
return err;
}
static LSTATUS WINAPI hook_RegCloseKey(HKEY handle)
{
struct reg_hook_key *key;
size_t i;
EnterCriticalSection(&reg_hook_lock);
for (i = 0 ; i < reg_hook_nkeys ; i++) {
key = &reg_hook_keys[i];
if (key->handle == handle) {
//dprintf("Registry: Closed virtual key %S\n", key->name);
key->handle = NULL;
}
}
LeaveCriticalSection(&reg_hook_lock);
return next_RegCloseKey(handle);
}
static LSTATUS WINAPI hook_RegQueryValueExW(
HKEY handle,
const wchar_t *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes)
{
struct reg_hook_key *key;
LSTATUS err;
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(handle);
/* Check if this is a virtualized registry key */
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegQueryValueExW(
handle,
name,
reserved,
type,
bytes,
nbytes);
}
/* Call the factored out core of this function because RegQueryValueExA
has to be a blight upon my existence */
err = reg_hook_query_val_locked(key, name, type, bytes, nbytes);
LeaveCriticalSection(&reg_hook_lock);
return err;
}
/* now this right here is a pain in my ass */
static LSTATUS WINAPI hook_RegQueryValueExA(
HKEY handle,
const char *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes)
{
/* _s: sizeof, _c: _countof(), _w: widened */
struct reg_hook_key *key;
wchar_t *name_w;
size_t name_c;
wchar_t *content;
uint32_t content_s;
size_t content_c;
uint32_t type_site;
LSTATUS err;
name_w = NULL;
content = NULL;
/* Normalize inconvenient inputs */
if (name == NULL) {
name = "";
}
if (type == NULL) {
type = &type_site;
}
/* Look up key handle, early exit if no match */
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(handle);
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegQueryValueExA(
handle,
name,
reserved,
type,
bytes,
nbytes);
}
/* OK, first off we need to widen the name. This requires a temporary
buffer allocation. */
mbstowcs_s(&name_c, NULL, 0, name, 0);
name_w = malloc(name_c * sizeof(wchar_t));
if (name_w == NULL) {
err = ERROR_OUTOFMEMORY;
goto end;
}
mbstowcs_s(NULL, name_w, name_c, name, name_c - 1);
/* Next, check to see if the caller even cares about the content. We can
pass through if they don't. */
if (bytes == NULL && nbytes == NULL) {
err = reg_hook_query_val_locked(key, name_w, type, NULL, NULL);
goto end;
}
/* Next, we need to check the key type to see if it's REG_SZ. */
err = reg_hook_query_val_locked(key, name_w, type, NULL, NULL);
if (err != ERROR_SUCCESS) {
goto end;
}
/* If it is not REG_SZ then pass the content directly.
(We ignore the REG_MULTI_SZ case here). */
assert(*type != REG_MULTI_SZ);
if (*type != REG_SZ) {
err = reg_hook_query_val_locked(key, name_w, type, bytes, nbytes);
goto end;
}
/* Otherwise things get more complicated. First we must measure the wide-
character length of the value (hopefully said value does not change
under our feet, of course). */
err = reg_hook_query_val_locked(key, name_w, type, NULL, &content_s);
if (err != ERROR_SUCCESS) {
goto end;
}
/* Next, allocate a scratch buffer. Even if the caller doesn't supply an
output buffer we need to know the actual content to be able to size the
narrow version. */
content = malloc(content_s);
if (content == NULL) {
err = ERROR_OUTOFMEMORY;
goto end;
}
/* Get the data... */
err = reg_hook_query_val_locked(key, name_w, type, content, &content_s);
if (err != ERROR_SUCCESS) {
goto end;
}
/* Now size the corresponding narrow form and return it to the caller */
wcstombs_s(&content_c, NULL, 0, content, 0);
if (bytes != NULL) {
if (nbytes == NULL) {
err = ERROR_INVALID_PARAMETER;
goto end;
}
if (*nbytes < content_c) {
err = ERROR_MORE_DATA;
goto end;
}
wcstombs_s(NULL, bytes, *nbytes, content, content_c - 1);
}
if (nbytes != NULL) { /* It really should be, based on earlier checks ... */
*nbytes = content_c;
}
err = ERROR_SUCCESS;
end:
LeaveCriticalSection(&reg_hook_lock);
free(content);
free(name_w);
return err;
}
static LSTATUS reg_hook_query_val_locked(
struct reg_hook_key *key,
const wchar_t *name,
uint32_t *type,
void *bytes,
uint32_t *nbytes)
{
const struct reg_hook_val *val;
LSTATUS err;
HRESULT hr;
val = reg_hook_match_val_locked(key, name);
if (val != NULL) {
if (type != NULL) {
*type = val->type;
}
if (val->read != NULL) {
hr = val->read(bytes, nbytes);
err = reg_hook_propagate_hr(hr);
} else {
dprintf("Registry: %S: Val %S has no read handler\n",
key->name,
name);
err = ERROR_ACCESS_DENIED;
}
} else {
dprintf("Registry: Key %S: Val %S not found\n", key->name, name);
err = ERROR_FILE_NOT_FOUND;
}
return err;
}
static LSTATUS WINAPI hook_RegSetValueExW(
HKEY handle,
const wchar_t *name,
uint32_t reserved,
uint32_t type,
const void *bytes,
uint32_t nbytes)
{
struct reg_hook_key *key;
const struct reg_hook_val *val;
LSTATUS err;
HRESULT hr;
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(handle);
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegSetValueExW(
handle,
name,
reserved,
type,
bytes,
nbytes);
}
val = reg_hook_match_val_locked(key, name);
if (val != NULL) {
if (val->write != NULL) {
if (type != val->type) {
dprintf( "Registry: Key %S: Val %S: Type mismatch "
"(expected %i got %i)\n",
key->name,
name,
val->type,
type);
err = ERROR_ACCESS_DENIED;
} else {
dprintf("Registry: Write virtual key %S value %S\n",
key->name,
val->name);
hr = val->write(bytes, nbytes);
err = reg_hook_propagate_hr(hr);
}
} else {
/* No write handler (the common case), black-hole whatever gets
written. */
err = ERROR_SUCCESS;
}
} else {
dprintf("Registry: Key %S: Val %S not found\n", key->name, name);
err = ERROR_FILE_NOT_FOUND;
}
LeaveCriticalSection(&reg_hook_lock);
return err;
}
static LSTATUS WINAPI hook_RegGetValueW(
HKEY handle,
LPCWSTR subkey,
LPCWSTR name,
uint32_t flags,
uint32_t *type,
void *pData,
uint32_t *numData)
{
struct reg_hook_key *key;
HKEY tmp = NULL;
const struct reg_hook_val *val;
LSTATUS err;
EnterCriticalSection(&reg_hook_lock);
//dprintf("Registry: RegGetValueW lookup for %ls\\%ls\n", subkey, name);
if (subkey == NULL) {
key = reg_hook_match_key_locked(handle);
} else {
err = hook_RegOpenKeyExW(handle, subkey, flags, 1, &tmp);
key = reg_hook_match_key_locked(tmp);
}
//dprintf("Registry: RegGetValueW key is %ls", key->name);
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
dprintf("Registry: RegGetValueW Failed to find %ls\\%ls, passing on\n", subkey, name);
return next_RegGetValueW(
handle,
subkey,
name,
flags,
type,
pData,
numData);
}
val = reg_hook_match_val_locked(key, name);
if (val != NULL) {
//dprintf("Registry: RegGetValueW found %ls\\%ls!\n", subkey, name);
if (val->read != NULL) {
val->read(pData, numData);
if (tmp != NULL) {
hook_RegCloseKey(tmp);
}
LeaveCriticalSection(&reg_hook_lock);
err = ERROR_SUCCESS;
return err;
}
}
LeaveCriticalSection(&reg_hook_lock);
err = ERROR_NOT_FOUND;
return err;
}
HRESULT reg_hook_read_bin(
void *bytes,
uint32_t *nbytes,
const void *src_bytes,
size_t src_nbytes)
{
assert(src_bytes != NULL || src_nbytes == 0);
if (bytes != NULL) {
if (nbytes == NULL || *nbytes < src_nbytes) {
return HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
memcpy(bytes, src_bytes, src_nbytes);
}
if (nbytes != NULL) {
*nbytes = src_nbytes;
}
return S_OK;
}
HRESULT reg_hook_read_u32(
void *bytes,
uint32_t *nbytes,
uint32_t src)
{
if (bytes != NULL) {
if (nbytes == NULL || *nbytes < sizeof(uint32_t)) {
return HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
memcpy(bytes, &src, sizeof(uint32_t));
}
if (nbytes != NULL) {
*nbytes = sizeof(uint32_t);
}
return S_OK;
}
HRESULT reg_hook_read_wstr(
void *bytes,
uint32_t *nbytes,
const wchar_t *src)
{
size_t src_nbytes;
assert(src != NULL);
src_nbytes = (wcslen(src) + 1) * sizeof(wchar_t);
if (bytes != NULL) {
if (nbytes == NULL || *nbytes < src_nbytes) {
return HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
memcpy(bytes, src, src_nbytes);
}
if (nbytes != NULL) {
*nbytes = src_nbytes;
}
return S_OK;
}

35
hooklib/reg.h Normal file
View File

@ -0,0 +1,35 @@
#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;
};
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);

340
hooklib/setupapi.c Normal file
View File

@ -0,0 +1,340 @@
#include <windows.h>
#include <setupapi.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "hook/table.h"
#include "hooklib/setupapi.h"
#include "util/dprintf.h"
struct setupapi_class {
const GUID *guid;
const wchar_t *path;
HDEVINFO cur_handle;
};
static void setupapi_hook_init(void);
/* API hooks */
static HDEVINFO WINAPI my_SetupDiGetClassDevsW(
const GUID *ClassGuid,
wchar_t *Enumerator,
HWND hwndParent,
DWORD Flags);
static BOOL WINAPI my_SetupDiEnumDeviceInterfaces(
HDEVINFO DeviceInfoSet,
SP_DEVINFO_DATA *DeviceInfoData,
const GUID *InterfaceClassGuid,
DWORD MemberIndex,
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData);
static BOOL WINAPI my_SetupDiGetDeviceInterfaceDetailW(
HDEVINFO DeviceInfoSet,
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData,
SP_DEVICE_INTERFACE_DETAIL_DATA_W *DeviceInterfaceDetailData,
DWORD DeviceInterfaceDetailDataSize,
DWORD *RequiredSize,
SP_DEVINFO_DATA *DeviceInfoData);
static BOOL WINAPI my_SetupDiDestroyDeviceInfoList(HDEVINFO DeviceInfoSet);
/* Links */
static HDEVINFO (WINAPI *next_SetupDiGetClassDevsW)(
const GUID *ClassGuid,
wchar_t *Enumerator,
HWND hwndParent,
DWORD Flags);
static BOOL (WINAPI *next_SetupDiEnumDeviceInterfaces)(
HDEVINFO DeviceInfoSet,
SP_DEVINFO_DATA *DeviceInfoData,
const GUID *InterfaceClassGuid,
DWORD MemberIndex,
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData);
static BOOL (WINAPI *next_SetupDiGetDeviceInterfaceDetailW)(
HDEVINFO DeviceInfoSet,
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData,
SP_DEVICE_INTERFACE_DETAIL_DATA_W *DeviceInterfaceDetailData,
DWORD DeviceInterfaceDetailDataSize,
DWORD *RequiredSize,
SP_DEVINFO_DATA *DeviceInfoData);
static BOOL (WINAPI *next_SetupDiDestroyDeviceInfoList)(HDEVINFO DeviceInfoSet);
/* Hook tbl */
static const struct hook_symbol setupapi_syms[] = {
{
.name = "SetupDiGetClassDevsW",
.patch = my_SetupDiGetClassDevsW,
.link = (void *) &next_SetupDiGetClassDevsW,
}, {
.name = "SetupDiEnumDeviceInterfaces",
.patch = my_SetupDiEnumDeviceInterfaces,
.link = (void *) &next_SetupDiEnumDeviceInterfaces,
}, {
.name = "SetupDiGetDeviceInterfaceDetailW",
.patch = my_SetupDiGetDeviceInterfaceDetailW,
.link = (void *) &next_SetupDiGetDeviceInterfaceDetailW,
}, {
.name = "SetupDiDestroyDeviceInfoList",
.patch = my_SetupDiDestroyDeviceInfoList,
.link = (void *) &next_SetupDiDestroyDeviceInfoList,
}
};
static bool setupapi_initted;
static CRITICAL_SECTION setupapi_lock;
static struct setupapi_class *setupapi_classes;
static size_t setupapi_nclasses;
HRESULT setupapi_add_phantom_dev(const GUID *iface_class, const wchar_t *path)
{
struct setupapi_class *class_;
struct setupapi_class *new_array;
HRESULT hr;
assert(iface_class != NULL);
assert(path != NULL);
setupapi_hook_init();
EnterCriticalSection(&setupapi_lock);
new_array = realloc(
setupapi_classes,
(setupapi_nclasses + 1) * sizeof(struct setupapi_class));
if (new_array == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
setupapi_classes = new_array;
class_ = &setupapi_classes[setupapi_nclasses++];
class_->guid = iface_class;
class_->path = path;
hr = S_OK;
end:
LeaveCriticalSection(&setupapi_lock);
return hr;
}
static void setupapi_hook_init()
{
if (setupapi_initted) {
return;
}
setupapi_hook_insert_hooks(NULL);
InitializeCriticalSection(&setupapi_lock);
setupapi_initted = true;
}
void setupapi_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"setupapi.dll",
setupapi_syms,
_countof(setupapi_syms));
}
static HDEVINFO WINAPI my_SetupDiGetClassDevsW(
const GUID *ClassGuid,
wchar_t *Enumerator,
HWND hwndParent,
DWORD Flags)
{
struct setupapi_class *class_;
HDEVINFO result;
size_t i;
result = next_SetupDiGetClassDevsW(
ClassGuid,
Enumerator,
hwndParent,
Flags);
if (result == INVALID_HANDLE_VALUE || ClassGuid == NULL) {
return result;
}
EnterCriticalSection(&setupapi_lock);
for (i = 0 ; i < setupapi_nclasses ; i++) {
class_ = &setupapi_classes[i];
if (memcmp(ClassGuid, class_->guid, sizeof(*ClassGuid)) == 0) {
class_->cur_handle = result;
}
}
LeaveCriticalSection(&setupapi_lock);
return result;
}
static BOOL WINAPI my_SetupDiEnumDeviceInterfaces(
HDEVINFO DeviceInfoSet,
SP_DEVINFO_DATA *DeviceInfoData,
const GUID *InterfaceClassGuid,
DWORD MemberIndex,
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData)
{
const struct setupapi_class *class_;
size_t i;
if ( DeviceInfoSet == INVALID_HANDLE_VALUE ||
DeviceInterfaceData == NULL ||
DeviceInterfaceData->cbSize != sizeof(*DeviceInterfaceData)) {
goto pass;
}
if (MemberIndex > 0) {
MemberIndex--;
goto pass;
}
EnterCriticalSection(&setupapi_lock);
for ( i = 0, class_ = NULL ;
i < setupapi_nclasses && class_ == NULL ;
i++) {
if (DeviceInfoSet == setupapi_classes[i].cur_handle) {
class_ = &setupapi_classes[i];
dprintf("SetupAPI: Interface {%08lx-...} -> Device node %S\n",
class_->guid->Data1,
class_->path);
memcpy( &DeviceInterfaceData->InterfaceClassGuid,
class_->guid,
sizeof(GUID));
DeviceInterfaceData->Flags = SPINT_ACTIVE;
DeviceInterfaceData->Reserved = (ULONG_PTR) class_->path;
}
}
LeaveCriticalSection(&setupapi_lock);
if (class_ == NULL) {
goto pass;
}
SetLastError(ERROR_SUCCESS);
return TRUE;
pass:
return next_SetupDiEnumDeviceInterfaces(
DeviceInfoSet,
DeviceInfoData,
InterfaceClassGuid,
MemberIndex,
DeviceInterfaceData);
}
static BOOL WINAPI my_SetupDiGetDeviceInterfaceDetailW(
HDEVINFO DeviceInfoSet,
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData,
SP_DEVICE_INTERFACE_DETAIL_DATA_W *DeviceInterfaceDetailData,
DWORD DeviceInterfaceDetailDataSize,
DWORD *RequiredSize,
SP_DEVINFO_DATA *DeviceInfoData)
{
const wchar_t *wstr;
size_t nbytes_wstr;
size_t nbytes_total;
size_t i;
bool match;
if (DeviceInfoSet == INVALID_HANDLE_VALUE || DeviceInterfaceData == NULL) {
goto pass;
}
EnterCriticalSection(&setupapi_lock);
for ( i = 0, match = false ;
i < setupapi_nclasses && !match ;
i++) {
if ( DeviceInfoSet == setupapi_classes[i].cur_handle &&
DeviceInterfaceData->Reserved == (ULONG_PTR) setupapi_classes[i].path) {
match = true;
}
}
LeaveCriticalSection(&setupapi_lock);
if (!match) {
goto pass;
}
wstr = (const wchar_t *) DeviceInterfaceData->Reserved;
nbytes_wstr = (wcslen(wstr) + 1) * sizeof(wchar_t);
nbytes_total = offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath);
nbytes_total += nbytes_wstr;
if (RequiredSize != NULL) {
*RequiredSize = (DWORD) nbytes_total;
}
if ( DeviceInterfaceDetailData == NULL &&
DeviceInterfaceDetailDataSize < nbytes_total) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (DeviceInterfaceDetailData->cbSize!=sizeof(*DeviceInterfaceDetailData)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
memcpy(DeviceInterfaceDetailData->DevicePath, wstr, nbytes_wstr);
SetLastError(ERROR_SUCCESS);
return TRUE;
pass:
return next_SetupDiGetDeviceInterfaceDetailW(
DeviceInfoSet,
DeviceInterfaceData,
DeviceInterfaceDetailData,
DeviceInterfaceDetailDataSize,
RequiredSize,
DeviceInfoData);
}
static BOOL WINAPI my_SetupDiDestroyDeviceInfoList(HDEVINFO DeviceInfoSet)
{
size_t i;
EnterCriticalSection(&setupapi_lock);
for (i = 0 ; i < setupapi_nclasses ; i++) {
if (setupapi_classes[i].cur_handle == DeviceInfoSet) {
setupapi_classes[i].cur_handle = NULL;
}
}
LeaveCriticalSection(&setupapi_lock);
return next_SetupDiDestroyDeviceInfoList(DeviceInfoSet);
}

8
hooklib/setupapi.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <windows.h>
#include <stddef.h>
HRESULT setupapi_add_phantom_dev(const GUID *iface_class, const wchar_t *path);
void setupapi_hook_insert_hooks(HMODULE target);

249
hooklib/spike.c Normal file
View File

@ -0,0 +1,249 @@
#include <windows.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include "hook/pe.h"
#include "hooklib/spike.h"
#include "util/dprintf.h"
static void spike_hook_read_config(const wchar_t *spike_file);
/* Spike functions. Their "style" is named after the libc function they bear
the closest resemblance to. */
static void spike_fn_puts(const char *msg)
{
char line[512];
sprintf_s(line, _countof(line), "%s\n", msg);
OutputDebugStringA(line);
}
static void spike_fn_fputs(const char *msg)
{
OutputDebugStringA(msg);
}
static void spike_fn_printf(const char *fmt, ...)
{
char line[512];
va_list ap;
va_start(ap, fmt);
vsprintf_s(line, _countof(line), fmt, ap);
strcat(line, "\n");
OutputDebugStringA(line);
}
static void spike_fn_vprintf(
const char *proc,
int line_no,
const char *fmt,
va_list ap)
{
char msg[512];
char line[512];
vsprintf_s(msg, _countof(msg), fmt, ap);
sprintf_s(line, _countof(line), "%s:%i: %s", proc, line_no, msg);
OutputDebugStringA(line);
}
static void spike_fn_vwprintf(
const wchar_t *proc,
int line_no,
const wchar_t *fmt,
va_list ap)
{
wchar_t msg[512];
wchar_t line[512];
vswprintf_s(msg, _countof(msg), fmt, ap);
swprintf_s(line, _countof(line), L"%s:%i: %s", proc, line_no, msg);
OutputDebugStringW(line);
}
static void spike_fn_perror(
int a1,
int a2,
int error,
const char *file,
int line_no,
const char *msg)
{
char line[512];
sprintf_s(
line,
_countof(line),
"%s:%i:%08x: %s\n",
file,
line_no,
error,
msg);
OutputDebugStringA(line);
}
/* Spike inserters */
static void spike_insert_jmp(ptrdiff_t rva, void *proc)
{
uint8_t *base;
uint8_t *target;
uint8_t *func_ptr;
uint32_t delta;
base = (uint8_t *) GetModuleHandleW(NULL);
target = base + rva;
func_ptr = proc;
delta = func_ptr - target - 4; /* -4: EIP delta, after end of target insn */
pe_patch(target, &delta, sizeof(delta));
}
static void spike_insert_ptr(ptrdiff_t rva, void *ptr)
{
uint8_t *base;
uint8_t *target;
base = (uint8_t *) GetModuleHandleW(NULL);
target = base + rva;
pe_patch(target, &ptr, sizeof(ptr));
}
static void spike_insert_log_levels(ptrdiff_t rva, size_t count)
{
uint8_t *base;
uint32_t *levels;
size_t i;
base = (uint8_t *) GetModuleHandleW(NULL);
levels = (uint32_t *) (base + rva);
for (i = 0 ; i < count ; i++) {
levels[i] = 255;
}
}
/* Config reader */
void spike_hook_init(const wchar_t *ini_file)
{
wchar_t module[MAX_PATH];
wchar_t path[MAX_PATH];
const wchar_t *basename;
const wchar_t *slash;
assert(ini_file != NULL);
/* Get the filename (strip path) of the host EXE */
GetModuleFileNameW(NULL, module, _countof(module));
slash = wcsrchr(module, L'\\');
if (slash != NULL) {
basename = slash + 1;
} else {
basename = module;
}
/* Check our INI file to see if any spikes are configured for this EXE.
Normally we separate out config reading into a separate module... */
GetPrivateProfileStringW(
L"spike",
basename,
L"",
path,
_countof(path),
ini_file);
if (path[0] != L'\0') {
dprintf("Spiking %S using config from %S\n", basename, path);
spike_hook_read_config(path);
}
}
static void spike_hook_read_config(const wchar_t *spike_file)
{
int match;
int count;
int rva;
char line[80];
char *ret;
FILE *f;
f = _wfopen(spike_file, L"r");
if (f == NULL) {
dprintf("Error opening spike file %S\n", spike_file);
return;
}
for (;;) {
ret = fgets(line, sizeof(line), f);
if (ret == NULL) {
break;
}
if (line[0] == '#' || line[0] == '\r' || line[0] == '\n') {
continue;
}
match = sscanf(line, "levels %i %i", &rva, &count);
if (match == 2) {
spike_insert_log_levels((ptrdiff_t) rva, count);
}
match = sscanf(line, "j_vprintf %i", &rva);
if (match == 1) {
spike_insert_jmp((ptrdiff_t) rva, spike_fn_vprintf);
}
match = sscanf(line, "j_vwprintf %i", &rva);
if (match == 1) {
spike_insert_jmp((ptrdiff_t) rva, spike_fn_vwprintf);
}
match = sscanf(line, "j_printf %i", &rva);
if (match == 1) {
spike_insert_jmp((ptrdiff_t) rva, spike_fn_printf);
}
match = sscanf(line, "j_puts %i", &rva);
if (match == 1) {
spike_insert_jmp((ptrdiff_t) rva, spike_fn_puts);
}
match = sscanf(line, "j_perror %i", &rva);
if (match == 1) {
spike_insert_jmp((ptrdiff_t) rva, spike_fn_perror);
}
match = sscanf(line, "c_fputs %i", &rva); /* c == "callback" */
if (match == 1) {
spike_insert_ptr((ptrdiff_t) rva, spike_fn_fputs);
}
}
dprintf("Spike insertion complete\n");
fclose(f);
}

5
hooklib/spike.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <stddef.h>
void spike_hook_init(const wchar_t *ini_file);

30
jvs/jvs-bus.c Normal file
View File

@ -0,0 +1,30 @@
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include "jvs/jvs-bus.h"
void jvs_bus_transact(
struct jvs_node *head,
const void *bytes,
size_t nbytes,
struct iobuf *resp)
{
struct jvs_node *node;
assert(bytes != NULL);
assert(resp != NULL);
for (node = head ; node != NULL ; node = node->next) {
node->transact(node, bytes, nbytes, resp);
}
}
bool jvs_node_sense(struct jvs_node *node)
{
if (node != NULL) {
return node->sense(node);
} else {
return false;
}
}

24
jvs/jvs-bus.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include "hook/iobuf.h"
struct jvs_node {
struct jvs_node *next;
void (*transact)(
struct jvs_node *node,
const void *bytes,
size_t nbytes,
struct iobuf *resp);
bool (*sense)(struct jvs_node *node);
};
void jvs_bus_transact(
struct jvs_node *head,
const void *bytes,
size_t nbytes,
struct iobuf *resp);
bool jvs_node_sense(struct jvs_node *node);

51
jvs/jvs-cmd.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
enum {
JVS_CMD_READ_ID = 0x10,
JVS_CMD_GET_CMD_VERSION = 0x11,
JVS_CMD_GET_JVS_VERSION = 0x12,
JVS_CMD_GET_COMM_VERSION = 0x13,
JVS_CMD_GET_FEATURES = 0x14,
JVS_CMD_WRITE_PCB_INFO = 0x15,
JVS_CMD_READ_SWITCHES = 0x20,
JVS_CMD_READ_COIN = 0x21,
JVS_CMD_READ_ANALOGS = 0x22,
JVS_CMD_WRITE_GPIO = 0x32,
JVS_CMD_RESET = 0xF0,
JVS_CMD_ASSIGN_ADDR = 0xF1,
JVS_CMD_NAMCO_EXTEND = 0x70,
};
enum {
JVS_NAMCO_EXTEND_CMD_NOOP_18 = 0x18,
};
#pragma pack(push, 1)
struct jvs_req_read_switches {
uint8_t cmd;
uint8_t num_players;
uint8_t bytes_per_player;
};
struct jvs_req_read_coin {
uint8_t cmd;
uint8_t nslots;
};
struct jvs_req_read_analogs {
uint8_t cmd;
uint8_t nanalogs;
};
struct jvs_req_reset {
uint8_t cmd;
uint8_t unknown;
};
struct jvs_req_assign_addr {
uint8_t cmd;
uint8_t addr;
};
#pragma pack(pop)

153
jvs/jvs-frame.c Normal file
View File

@ -0,0 +1,153 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
#include "jvs/jvs-frame.h"
#include "util/dprintf.h"
static HRESULT jvs_frame_check_sum(const struct iobuf *dest);
static HRESULT jvs_frame_encode_byte(struct iobuf *dest, uint8_t byte);
/* Deals in whole frames only for simplicity's sake, since that's all we need
to emulate the Nu's kernel driver interface. This could of course be
extended later for other arcade hardware platforms. */
HRESULT jvs_frame_decode(
struct iobuf *dest,
const void *ptr,
size_t nbytes)
{
const uint8_t *bytes;
bool escape;
size_t i;
assert(dest != NULL);
assert(ptr != NULL);
bytes = ptr;
if (nbytes == 0) {
dprintf("JVS Frame: Empty frame\n");
return E_FAIL;
}
if (bytes[0] != 0xE0) {
dprintf("JVS Frame: Sync byte was expected\n");
return E_FAIL;
}
escape = false;
for (i = 1 ; i < nbytes ; i++) {
if (bytes[i] == 0xE0) {
dprintf("JVS Frame: Unexpected sync byte\n");
return E_FAIL;
} else if (bytes[i] == 0xD0) {
if (escape) {
dprintf("JVS Frame: Escaping fault\n");
return E_FAIL;
}
escape = true;
} else {
if (dest->pos >= dest->nbytes) {
dprintf("JVS Frame: Insufficiant Buffer\n");
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
if (escape) {
escape = false;
dest->bytes[dest->pos++] = bytes[i] + 1;
} else {
dest->bytes[dest->pos++] = bytes[i];
}
}
}
return jvs_frame_check_sum(dest);
}
static HRESULT jvs_frame_check_sum(const struct iobuf *dest)
{
uint8_t checksum;
size_t i;
checksum = 0;
for (i = 0 ; i < dest->pos - 1 ; i++) {
checksum += dest->bytes[i];
}
if (checksum != dest->bytes[dest->pos - 1]) {
dprintf("JVS Frame: Checksum failure\n");
return HRESULT_FROM_WIN32(ERROR_CRC);
}
return S_OK;
}
HRESULT jvs_frame_encode(
struct iobuf *dest,
const void *ptr,
size_t nbytes)
{
const uint8_t *bytes;
uint8_t checksum;
size_t i;
HRESULT hr;
assert(dest != NULL);
assert(ptr != NULL);
bytes = ptr;
checksum = 0;
if (dest->pos + 1 > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = 0xE0;
for (i = 0 ; i < nbytes ; i++) {
hr = jvs_frame_encode_byte(dest, bytes[i]);
if (FAILED(hr)) {
return hr;
}
checksum += bytes[i];
}
return jvs_frame_encode_byte(dest, checksum);
}
static HRESULT jvs_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;
}

17
jvs/jvs-frame.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include "hook/iobuf.h"
HRESULT jvs_frame_decode(
struct iobuf *dest,
const void *bytes,
size_t nbytes);
HRESULT jvs_frame_encode(
struct iobuf *dest,
const void *bytes,
size_t nbytes);

111
jvs/jvs-util.c Normal file
View File

@ -0,0 +1,111 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
#include "jvs/jvs-frame.h"
#include "jvs/jvs-util.h"
#include "util/dprintf.h"
#include "util/dump.h"
typedef HRESULT (*jvs_dispatch_fn_t)(
void *ctx,
struct const_iobuf *req,
struct iobuf *resp);
void jvs_crack_request(
const void *bytes,
size_t nbytes,
struct iobuf *resp,
uint8_t jvs_addr,
jvs_dispatch_fn_t dispatch_fn,
void *dispatch_ctx)
{
uint8_t req_bytes[128];
uint8_t resp_bytes[128];
struct iobuf decode;
struct iobuf encode;
struct const_iobuf segments;
HRESULT hr;
assert(bytes != NULL);
assert(resp != NULL);
assert(jvs_addr != 0x00 && (jvs_addr < 0x20 || jvs_addr == 0xFF));
assert(dispatch_fn != NULL);
decode.bytes = req_bytes;
decode.nbytes = sizeof(req_bytes);
decode.pos = 0;
hr = jvs_frame_decode(&decode, bytes, nbytes);
if (FAILED(hr)) {
return;
}
#if 0
dprintf("Decoded request:\n");
dump_iobuf(&decode);
#endif
if (req_bytes[0] != jvs_addr && req_bytes[0] != 0xFF) {
return;
}
iobuf_flip(&segments, &decode);
segments.pos = 2;
encode.bytes = resp_bytes;
encode.nbytes = sizeof(resp_bytes);
encode.pos = 3;
/* +1: Don't try to dispatch the trailing checksum byte */
hr = S_OK; /* I guess an empty request packet is technically valid? */
while (segments.pos + 1 < segments.nbytes) {
hr = dispatch_fn(dispatch_ctx, &segments, &encode);
if (FAILED(hr)) {
break;
}
}
if (FAILED(hr)) {
/* Send an error in the overall status byte */
dprintf("JVS I/O: HR Failed %lX\n", hr);
encode.pos = 3;
resp_bytes[0] = 0x00; /* Dest addr (master) */
resp_bytes[1] = 0x02; /* Payload len: Status byte, checksum byte */
if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
resp_bytes[2] = 0x04; /* Status: "Overflow" */
} else {
resp_bytes[2] = 0x02; /* Status: Encoutered unsupported command */
}
} else if (encode.pos == 3) {
/* Probably a reset, don't emit a response frame with empty payload */
return;
} else {
/* Send success response */
resp_bytes[0] = 0x00; /* Dest addr (master) */
resp_bytes[1] = encode.pos - 2 + 1; /* -2 header +1 checksum */
resp_bytes[2] = 0x01; /* Status: Success */
}
#if 0
dprintf("Encoding response:\n");
dump_iobuf(&encode);
#endif
hr = jvs_frame_encode(resp, encode.bytes, encode.pos);
if (FAILED(hr)) {
dprintf("JVS Node: Response encode error: %x\n", (int) hr);
}
}

21
jvs/jvs-util.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
typedef HRESULT (*jvs_dispatch_fn_t)(
void *ctx,
struct const_iobuf *req,
struct iobuf *resp);
void jvs_crack_request(
const void *bytes,
size_t nbytes,
struct iobuf *resp,
uint8_t jvs_addr,
jvs_dispatch_fn_t dispatch_fn,
void *dispatch_ctx);

18
jvs/meson.build Normal file
View File

@ -0,0 +1,18 @@
jvs_lib = static_library(
'jvs',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],
sources : [
'jvs-bus.c',
'jvs-bus.h',
'jvs-cmd.h',
'jvs-frame.c',
'jvs-frame.h',
'jvs-util.c',
'jvs-util.h',
],
)

58
meson.build Normal file
View File

@ -0,0 +1,58 @@
project(
'bananatools',
'c',
version: '0.1.0',
default_options: [
'werror=true',
],
)
add_project_arguments(
'-DCOBJMACROS',
'-DDIRECTINPUT_VERSION=0x0800',
'-DWIN32_LEAN_AND_MEAN',
'-D_WIN32_WINNT=_WIN32_WINNT_WIN7',
'-DMINGW_HAS_SECURE_API=1',
'-Wno-unused',
language: 'c',
)
if meson.get_compiler('c').get_id() != 'msvc'
add_project_arguments(
'-ffunction-sections',
'-fdata-sections',
language: 'c',
)
add_project_link_arguments(
'-Wl,--enable-stdcall-fixup',
'-Wl,--exclude-all-symbols',
'-Wl,--gc-sections',
'-static-libgcc',
language: 'c',
)
endif
cc = meson.get_compiler('c')
shlwapi_lib = cc.find_library('shlwapi')
dinput8_lib = cc.find_library('dinput8')
dxguid_lib = cc.find_library('dxguid')
xinput_lib = cc.find_library('xinput')
inc = include_directories('.')
capnhook = subproject('capnhook')
subdir('hooklib')
subdir('util')
subdir('gfxhook')
subdir('jvs')
subdir('board')
subdir('platform')
subdir('amcus')
subdir('ferrumio')
subdir('taikoio')
subdir('taikohook')
subdir('ferrumhook')

291
platform/clock.c Normal file
View File

@ -0,0 +1,291 @@
#include <windows.h>
#include <assert.h>
#include <stdint.h>
#include "hook/table.h"
#include "platform/clock.h"
#include "util/dprintf.h"
static void WINAPI my_GetSystemTimeAsFileTime(FILETIME *out);
static BOOL WINAPI my_GetLocalTime(SYSTEMTIME *out);
static BOOL WINAPI my_GetSystemTime(SYSTEMTIME *out);
static DWORD WINAPI my_GetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo);
static BOOL WINAPI my_SetLocalTime(SYSTEMTIME *in);
static BOOL WINAPI my_SetSystemTime(SYSTEMTIME *in);
static BOOL WINAPI my_SetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo);
static BOOL WINAPI my_AdjustTokenPrivileges(
HANDLE TokenHandle,
BOOL DisableAllPrivileges,
PTOKEN_PRIVILEGES NewState,
DWORD BufferLength,
PTOKEN_PRIVILEGES PreviousState,
PDWORD ReturnLength);
static BOOL (WINAPI * next_GetSystemTimeAsFileTime)(FILETIME *out);
static int64_t clock_current_day;
static bool clock_time_warp;
static const struct hook_symbol clock_base_hook_syms[] = {
{
.name = "GetSystemTimeAsFileTime",
.patch = my_GetSystemTimeAsFileTime,
.link = (void **) &next_GetSystemTimeAsFileTime,
}
};
static const struct hook_symbol clock_perm_hook_syms[] = {
{
.name = "AdjustTokenPrivileges",
.patch = my_AdjustTokenPrivileges,
}
};
static const struct hook_symbol clock_read_hook_syms[] = {
{
.name = "GetLocalTime",
.patch = my_GetLocalTime,
}, {
.name = "GetSystemTime",
.patch = my_GetSystemTime,
}, {
.name = "GetTimeZoneInformation",
.patch = my_GetTimeZoneInformation,
},
};
static const struct hook_symbol clock_write_hook_syms[] = {
{
.name = "SetLocalTime",
.patch = my_SetLocalTime,
}, {
.name = "SetSystemTime",
.patch = my_SetSystemTime,
}, {
.name = "SetTimeZoneInformation",
.patch = my_SetTimeZoneInformation,
},
};
/* FILETIME is expressed in 100ns i.e. 0.1us i.e. 10^-7 sec units.
No official name for these units is given so let's call them "jiffies". */
#define jiffies_per_sec 10000000LL
#define jiffies_per_hour (jiffies_per_sec * 3600LL)
#define jiffies_per_day (jiffies_per_hour * 24LL)
static void WINAPI my_GetSystemTimeAsFileTime(FILETIME *out)
{
FILETIME in;
int64_t day;
int64_t real_jiffies;
int64_t real_jiffies_biased;
int64_t real_time;
int64_t fake_time;
int64_t fake_jiffies_biased;
int64_t fake_jiffies;
if (!clock_time_warp) {
next_GetSystemTimeAsFileTime(out);
return;
}
if (out == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return;
}
/* Get and convert real jiffies */
next_GetSystemTimeAsFileTime(&in);
real_jiffies = (((int64_t) in.dwHighDateTime) << 32) | in.dwLowDateTime;
/* Keepout period is JST [02:00, 07:00), which is equivalent to
UTC [17:00, 22:00). Bias UTC forward by 2 hours, changing this interval
to [19:00, 00:00) to make the math easier. We revert this bias later. */
real_jiffies_biased = real_jiffies + 2LL * jiffies_per_hour;
/* Split date and time */
day = real_jiffies_biased / jiffies_per_day;
real_time = real_jiffies_biased % jiffies_per_day;
/* Debug log */
if (clock_current_day != 0 && clock_current_day != day) {
dprintf("\n*** CLOCK JUMP! ***\n\n");
}
clock_current_day = day;
/* We want to skip the final five hours of our UTC+2 biased reference frame,
so scale time-of-day by 19/24. */
fake_time = (real_time * 19LL) / 24LL;
/* Un-split date and time */
fake_jiffies_biased = day * jiffies_per_day + fake_time;
/* Revert bias */
fake_jiffies = fake_jiffies_biased - 2LL * jiffies_per_hour;
/* Return result */
out->dwLowDateTime = fake_jiffies;
out->dwHighDateTime = fake_jiffies >> 32;
}
static BOOL WINAPI my_GetLocalTime(SYSTEMTIME *out)
{
ULARGE_INTEGER arith;
FILETIME linear;
/* Force JST */
my_GetSystemTimeAsFileTime(&linear);
arith.LowPart = linear.dwLowDateTime;
arith.HighPart = linear.dwHighDateTime;
arith.QuadPart += 9ULL * jiffies_per_hour;
linear.dwLowDateTime = arith.LowPart;
linear.dwHighDateTime = arith.HighPart;
return FileTimeToSystemTime(&linear, out);
}
static BOOL WINAPI my_GetSystemTime(SYSTEMTIME *out)
{
FILETIME linear;
BOOL ok;
my_GetSystemTimeAsFileTime(&linear);
ok = FileTimeToSystemTime(&linear, out);
if (!ok) {
return ok;
}
#if 0
static int last_second;
if (out->wSecond != last_second) {
dprintf("%04i/%02i/%02i %02i:%02i:%02i\n",
out->wYear,
out->wMonth,
out->wDay,
out->wHour,
out->wMinute,
out->wSecond);
}
last_second = out->wSecond;
#endif
return TRUE;
}
static DWORD WINAPI my_GetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo)
{
dprintf("Clock: Returning JST timezone\n");
if (tzinfo == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return TIME_ZONE_ID_INVALID;
}
/* Force JST (UTC+9), SEGA games malfunction in any other time zone.
Strings and boundary times don't matter, we only set the offset. */
memset(tzinfo, 0, sizeof(*tzinfo));
tzinfo->Bias = -9 * 60;
SetLastError(ERROR_SUCCESS);
/* "Unknown" here means that this region does not observe DST */
return TIME_ZONE_ID_UNKNOWN;
}
static BOOL WINAPI my_SetLocalTime(SYSTEMTIME *in)
{
dprintf("Clock: Blocked local time update\n");
return TRUE;
}
static BOOL WINAPI my_SetSystemTime(SYSTEMTIME *in)
{
dprintf("Clock: Blocked system time update\n");
return TRUE;
}
static BOOL WINAPI my_SetTimeZoneInformation(TIME_ZONE_INFORMATION *in)
{
dprintf("Clock: Blocked timezone update\n");
return TRUE;
}
static BOOL WINAPI my_AdjustTokenPrivileges(
HANDLE TokenHandle,
BOOL DisableAllPrivileges,
PTOKEN_PRIVILEGES NewState,
DWORD BufferLength,
PTOKEN_PRIVILEGES PreviousState,
PDWORD ReturnLength)
{
dprintf("Clock: Blocked privilege update\n");
return false; // ....why???
}
HRESULT clock_hook_init(const struct clock_config *cfg)
{
assert(cfg != NULL);
clock_time_warp = cfg->timewarp;
if (cfg->timezone || cfg->timewarp || !cfg->writeable) {
/* All the clock hooks require the core GSTAFT hook to be installed */
/* Note the ! up there btw. */
hook_table_apply(
NULL,
"kernel32.dll",
clock_base_hook_syms,
_countof(clock_base_hook_syms));
hook_table_apply(
NULL,
"ADVAPI32.dll",
clock_perm_hook_syms,
_countof(clock_perm_hook_syms));
}
if (cfg->timezone) {
hook_table_apply(
NULL,
"kernel32.dll",
clock_read_hook_syms,
_countof(clock_read_hook_syms));
}
if (!cfg->writeable) {
/* Install hook if this config parameter is FALSE! */
hook_table_apply(
NULL,
"kernel32.dll",
clock_write_hook_syms,
_countof(clock_write_hook_syms));
}
return S_OK;
}

13
platform/clock.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct clock_config {
bool timezone;
bool timewarp;
bool writeable;
};
HRESULT clock_hook_init(const struct clock_config *cfg);

232
platform/config.c Normal file
View File

@ -0,0 +1,232 @@
#include <windows.h>
#include <winsock2.h>
#include <strsafe.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "platform/vfs.h"
#include "platform/config.h"
#include "platform/platform.h"
#include "platform/clock.h"
#include "platform/dns.h"
#include "platform/es3sec.h"
void platform_config_load(struct platform_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
vfs_config_load(&cfg->vfs, filename);
clock_config_load(&cfg->clock, filename);
netenv_config_load(&cfg->netenv, filename);
locale_config_load(&cfg->locale, filename);
dns_config_load(&cfg->dns, filename);
jvs_config_load(&cfg->jvs, filename);
misc_config_load(&cfg->misc, filename);
es3sec_config_load(&cfg->dongle, filename);
}
void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename)
{
wchar_t keychip_sn[18];
size_t len;
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"vfs", L"enable", 1, filename);
GetPrivateProfileStringW(
L"vfs",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
len = wcslen(cfg->path);
if (cfg->path[len - 2] != '\\' && cfg->path[len - 2] != '/') { // -2 for null terminator
StringCbCatW(cfg->path, sizeof(cfg->path), L"\\\0");
}
wcscpy_s(cfg->d, sizeof(cfg->d), cfg->path);
wcscpy_s(cfg->e, sizeof(cfg->d), cfg->path);
wcscpy_s(cfg->f, sizeof(cfg->d), cfg->path);
wcscpy_s(cfg->g, sizeof(cfg->d), cfg->path);
wcscpy_s(cfg->h, sizeof(cfg->d), cfg->path);
wcscpy_s(cfg->j, sizeof(cfg->d), cfg->path);
StringCbCatW(cfg->d, sizeof(cfg->d), L"d\0");
StringCbCatW(cfg->e, sizeof(cfg->e), L"e\0");
StringCbCatW(cfg->f, sizeof(cfg->f), L"f\0");
StringCbCatW(cfg->g, sizeof(cfg->g), L"g\0");
StringCbCatW(cfg->h, sizeof(cfg->h), L"h\0");
StringCbCatW(cfg->j, sizeof(cfg->j), L"j\0");
}
void clock_config_load(struct clock_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->timezone = GetPrivateProfileIntW(L"clock", L"timezone", 1, filename);
cfg->timewarp = GetPrivateProfileIntW(L"clock", L"timewarp", 0, filename);
cfg->writeable = GetPrivateProfileIntW(
L"clock",
L"writeable",
0,
filename);
}
void netenv_config_load(struct netenv_config *cfg, const wchar_t *filename)
{
wchar_t mac_addr[18];
wchar_t subnet[16];
unsigned int ip[4];
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"netenv", L"enable", 0, filename);
cfg->addr_suffix = GetPrivateProfileIntW(
L"netenv",
L"addrSuffix",
11,
filename);
cfg->router_suffix = GetPrivateProfileIntW(
L"netenv",
L"routerSuffix",
254,
filename);
GetPrivateProfileStringW(
L"netenv",
L"macAddr",
L"01:02:03:04:05:06",
mac_addr,
_countof(mac_addr),
filename);
GetPrivateProfileStringW(
L"netenv",
L"subnet",
L"192.168.123.0",
subnet,
_countof(subnet),
filename);
swscanf(mac_addr,
L"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&cfg->mac_addr[0],
&cfg->mac_addr[1],
&cfg->mac_addr[2],
&cfg->mac_addr[3],
&cfg->mac_addr[4],
&cfg->mac_addr[5],
&cfg->mac_addr[6]);
swscanf(subnet, L"%u.%u.%u.%u", &ip[0], &ip[1], &ip[2], &ip[3]);
cfg->subnet = (ip[0] << 24) | (ip[1] << 16) | (ip[2] << 8) | ip[3];
}
void locale_config_load(struct locale_config *cfg, const wchar_t *filename)
{
cfg->enable = GetPrivateProfileIntW(L"locale", L"enable", 1, filename);
}
void dns_config_load(struct dns_config *cfg, const wchar_t *filename)
{
wchar_t default_[128];
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"dns", L"enable", 1, filename);
GetPrivateProfileStringW(
L"dns",
L"default",
L"localhost",
default_,
_countof(default_),
filename);
GetPrivateProfileStringW(
L"dns",
L"router",
default_,
cfg->router,
_countof(cfg->router),
filename);
GetPrivateProfileStringW(
L"dns",
L"startup",
default_,
cfg->startup,
_countof(cfg->startup),
filename);
GetPrivateProfileStringW(
L"dns",
L"billing",
default_,
cfg->billing,
_countof(cfg->billing),
filename);
GetPrivateProfileStringW(
L"dns",
L"aimedb",
default_,
cfg->aimedb,
_countof(cfg->aimedb),
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);
cfg->port = GetPrivateProfileIntW(L"jvs", L"port", 3, filename);
}
void misc_config_load(
struct misc_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->block_input_hook = GetPrivateProfileIntW(L"misc", L"blockInputHook", 1, filename);
cfg->show_cursor_hook = GetPrivateProfileIntW(L"misc", L"showCursorHook", 1, filename);
}
void es3sec_config_load(struct es3sec_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"dongle", L"enable", 1, filename);
GetPrivateProfileStringW(
L"dongle",
L"serial",
L"123456789012",
cfg->serial,
_countof(cfg->serial),
filename);
}

36
platform/config.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "platform/vfs.h"
#include "platform/netenv.h"
#include "platform/locale.h"
#include "platform/clock.h"
#include "platform/platform.h"
#include "platform/dns.h"
#include "platform/misc.h"
#include "platform/es3sec.h"
void platform_config_load(
struct platform_config *cfg,
const wchar_t *filename);
void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename);
void netenv_config_load(struct netenv_config *cfg, const wchar_t *filename);
void locale_config_load(struct locale_config *cfg, const wchar_t *filename);
void clock_config_load(struct clock_config *cfg, const wchar_t *filename);
void dns_config_load(struct dns_config *cfg, const wchar_t *filename);
void jvs_config_load(struct jvs_config *cfg, const wchar_t *filename);
void misc_config_load(struct misc_config *cfg, const wchar_t *filename);
void es3sec_config_load(struct es3sec_config *cfg, const wchar_t *filename);

92
platform/dns.c Normal file
View File

@ -0,0 +1,92 @@
#include <windows.h>
#include <assert.h>
#include "hooklib/dns.h"
#include "platform/dns.h"
#include "util/dprintf.h"
HRESULT dns_platform_hook_init(const struct dns_config *cfg)
{
HRESULT hr;
assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
dprintf("DNS: init\n");
hr = dns_hook_push(L"tenporouter.loc", cfg->router);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"nbgi.loc", cfg->router);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"bbrouter.loc", cfg->router);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"naominet.jp", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"anbzvarg.wc", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"ib.naominet.jp", cfg->billing);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"vo.anbzvarg.wc", cfg->billing);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"aime.naominet.jp", cfg->aimedb);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"nvzr.anbzvarg.wc", cfg->aimedb);
if (FAILED(hr)) {
return hr;
}
// if your ISP resolves bad domains, it will kill the network. These 2
// *cannot* resolve
hr = dns_hook_push(L"mobirouter.loc", NULL);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"dslrouter.loc", NULL);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}

16
platform/dns.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
struct dns_config {
bool enable;
wchar_t router[128];
wchar_t startup[128];
wchar_t billing[128];
wchar_t aimedb[128];
};
HRESULT dns_platform_hook_init(const struct dns_config *cfg);

4
platform/es3sec.c Normal file
View File

@ -0,0 +1,4 @@
#include <windows.h>
#include <cfgmgr32.h>
#include "es3sec.h"

8
platform/es3sec.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
struct es3sec_config {
bool enable;
wchar_t serial[13];
};

106
platform/jvs.c Normal file
View File

@ -0,0 +1,106 @@
#include <windows.h>
#include <assert.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "hook/iobuf.h"
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "hooklib/fdshark.h"
#include "util/dprintf.h"
#include "util/dump.h"
#include "platform/jvs.h"
static HRESULT jvs_handle_irp(struct irp *irp);
static HRESULT jvs_handle_irp_locked(struct irp *irp);
static struct uart jvs_uart;
static CRITICAL_SECTION jvs_lock;
static uint8_t jvs_written_bytes[520];
static uint8_t jvs_readable_bytes[520];
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)
{
if (!cfg->enable || provider == NULL) {
dprintf("JVS I/O: Disabled\n");
return S_OK;
}
InitializeCriticalSection(&jvs_lock);
dprintf("JVS I/O: init\n");
uart_init(&jvs_uart, cfg->port);
jvs_uart.written.bytes = jvs_written_bytes;
jvs_uart.written.nbytes = sizeof(jvs_written_bytes);
jvs_uart.readable.bytes = jvs_readable_bytes;
jvs_uart.readable.nbytes = sizeof(jvs_readable_bytes);
jvs_provider = provider;
return iohook_push_handler(jvs_handle_irp);
}
static HRESULT jvs_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (uart_match_irp(&jvs_uart, irp)) {
EnterCriticalSection(&jvs_lock);
hr = jvs_handle_irp_locked(irp);
LeaveCriticalSection(&jvs_lock);
}
else {
return iohook_invoke_next(irp);
}
return hr;
}
static HRESULT jvs_handle_irp_locked(struct irp *irp)
{
HRESULT hr;
struct jvs_node *root;
if (irp->op == IRP_OP_OPEN) {
dprintf("JVS I/O: Starting backend\n");
if (jvs_provider != NULL) {
hr = jvs_provider(&root);
if (SUCCEEDED(hr)) {
jvs_root = root;
}
}
}
hr = uart_handle_irp(&jvs_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("JVS I/O: Written\n");
dump_iobuf(&jvs_uart.written);
#endif
jvs_bus_transact(jvs_root, jvs_uart.written.bytes, jvs_uart.written.pos, &jvs_uart.readable);
jvs_uart.written.pos = 0;
return S_OK;
}
}

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