From 7ee9a2e2194eabd20e39b8e25f91c9b717cfb7a6 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 08:51:22 +0100 Subject: [PATCH 01/13] fix(dist): Incorrect versioning for ddr distribution packages Apparently forgotten to get updated to reflect the currently supported versions correctly. --- Module.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Module.mk b/Module.mk index 2a948a8..35e690d 100644 --- a/Module.mk +++ b/Module.mk @@ -710,6 +710,8 @@ $(zipdir)/ddr-14-to-18.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ dist/ddr/config.bat \ + dist/ddr/gamestart-17.bat \ + dist/ddr/gamestart-18.bat \ dist/ddr/gamestart-14.bat \ dist/ddr/gamestart-15.bat \ dist/ddr/gamestart-16.bat \ @@ -728,6 +730,8 @@ $(zipdir)/ddr-16-to-18-x64.zip: \ build/bin/indep-64/eamio.dll \ build/bin/indep-64/geninput.dll \ dist/ddr/config.bat \ + dist/ddr/gamestart-17.bat \ + dist/ddr/gamestart-18.bat \ dist/ddr/gamestart-16.bat \ dist/ddr/gamestart-17.bat \ dist/ddr/gamestart-18.bat \ -- 2.54.0 From 5833197b036d3821597f9d412a10aeccf60f43b3 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:07:54 +0100 Subject: [PATCH 02/13] fix(hook): Add missing hook_table_revert impl Allow hooks to cleanup when they are shut down. --- src/main/hook/table.c | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/main/hook/table.c b/src/main/hook/table.c index ed67b3b..067ea75 100644 --- a/src/main/hook/table.c +++ b/src/main/hook/table.c @@ -15,12 +15,21 @@ static const size_t apiset_prefix_len = sizeof(apiset_prefix) - 1; static void hook_table_apply_to_all( const char *depname, const struct hook_symbol *syms, size_t nsyms); +static void hook_table_revert_to_all( + const char *depname, const struct hook_symbol *syms, size_t nsyms); + static void hook_table_apply_to_iid( HMODULE target, const pe_iid_t *iid, const struct hook_symbol *syms, size_t nsyms); +static void hook_table_revert_to_iid( + HMODULE target, + const pe_iid_t *iid, + const struct hook_symbol *syms, + size_t nsyms); + static bool hook_table_match_module( HMODULE target, const char *iid_name, const char *depname); @@ -44,6 +53,23 @@ static void hook_table_apply_to_all( } } +static void hook_table_revert_to_all( + const char *depname, const struct hook_symbol *syms, size_t nsyms) +{ + const peb_dll_t *dll; + HMODULE pe; + + for (dll = peb_dll_get_first(); dll != NULL; dll = peb_dll_get_next(dll)) { + pe = peb_dll_get_base(dll); + + if (pe == NULL) { + continue; /* ?? Happens sometimes. */ + } + + hook_table_revert(pe, depname, syms, nsyms); + } +} + void hook_table_apply( HMODULE target, const char *depname, @@ -73,6 +99,35 @@ void hook_table_apply( } } +void hook_table_revert( + HMODULE target, + const char *depname, + const struct hook_symbol *syms, + size_t nsyms) +{ + const pe_iid_t *iid; + const char *iid_name; + + assert(depname != NULL); + assert(syms != NULL || nsyms == 0); + + if (target == NULL) { + /* Call out, which will then call us back repeatedly. Awkward, but + viewed from the outside it's good for usability. */ + + hook_table_revert_to_all(depname, syms, nsyms); + } else { + for (iid = pe_iid_get_first(target); iid != NULL; + iid = pe_iid_get_next(target, iid)) { + iid_name = pe_iid_get_name(target, iid); + + if (hook_table_match_module(target, iid_name, depname)) { + hook_table_revert_to_iid(target, iid, syms, nsyms); + } + } + } +} + static void hook_table_apply_to_iid( HMODULE target, const pe_iid_t *iid, @@ -101,6 +156,33 @@ static void hook_table_apply_to_iid( } } +static void hook_table_revert_to_iid( + HMODULE target, + const pe_iid_t *iid, + const struct hook_symbol *syms, + size_t nsyms) +{ + struct pe_iat_entry iate; + size_t i; + size_t j; + const struct hook_symbol *sym; + + i = 0; + + while (pe_iid_get_iat_entry(target, iid, i++, &iate) == S_OK) { + for (j = 0; j < nsyms; j++) { + sym = &syms[j]; + + if (hook_table_match_proc(&iate, sym)) { + // Only revert-able if the original pointer was stored previously + if (sym->link != NULL && *sym->link != NULL) { + pe_patch(iate.ppointer, sym->link, sizeof(*sym->link)); + } + } + } + } +} + static bool hook_table_match_module( HMODULE target, const char *iid_name, const char *depname) { -- 2.54.0 From 6a98ce1f59e43352ad5d4c9acc75f60bb8e726df Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:14:42 +0100 Subject: [PATCH 03/13] feat(dev): Add a separate docker dev container Improve the development experience by providing an additional docker container that can be started and used as an interactive development environment. It provides all the tools and a stable environment for building (identical to the build container). --- Dockerfile => Dockerfile.build | 0 Dockerfile.dev | 21 +++++++++++++++++++ GNUmakefile | 38 ++++++++++++++++++++++++++-------- 3 files changed, 50 insertions(+), 9 deletions(-) rename Dockerfile => Dockerfile.build (100%) create mode 100644 Dockerfile.dev diff --git a/Dockerfile b/Dockerfile.build similarity index 100% rename from Dockerfile rename to Dockerfile.build diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..00c5108 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,21 @@ +FROM --platform=amd64 debian:11.6-slim@sha256:f7d141c1ec6af549958a7a2543365a7829c2cdc4476308ec2e182f8a7c59b519 + +LABEL description="Development environment for bemanitools" + +# mingw-w64-gcc has 32-bit and 64-bit toolchains +RUN apt-get update && apt-get install -y --no-install-recommends \ + mingw-w64 \ + mingw-w64-common \ + make \ + zip \ + git \ + clang-format \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip3 install mdformat + +RUN mkdir /bemanitools +WORKDIR /bemanitools + +ENV SHELL /bin/bash \ No newline at end of file diff --git a/GNUmakefile b/GNUmakefile index 8328962..9aef0b1 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -13,8 +13,10 @@ BUILDDIR ?= build builddir_docker := $(BUILDDIR)/docker -docker_container_name := "bemanitools-build" -docker_image_name := "bemanitools-build:latest" +docker_build_container_name := "bemanitools-build" +docker_build_image_name := "bemanitools-build:latest" +docker_dev_container_name := "bemanitools-dev" +docker_dev_image_name := "bemanitools-dev:latest" depdir := $(BUILDDIR)/dep objdir := $(BUILDDIR)/obj @@ -41,6 +43,7 @@ FORCE: .PHONY: \ build-docker \ +dev-docker \ clean \ code-format \ doc-format \ @@ -89,21 +92,38 @@ version: $(V)echo "$(gitrev)" > version build-docker: - $(V)docker rm -f $(docker_container_name) 2> /dev/null || true + $(V)docker rm -f $(docker_build_container_name) 2> /dev/null || true $(V)docker \ build \ - -t $(docker_image_name) \ - -f Dockerfile \ + -t $(docker_build_image_name) \ + -f Dockerfile.build \ . $(V)docker \ run \ --volume $(shell pwd):/bemanitools \ - --name $(docker_container_name) \ - $(docker_image_name) + --name $(docker_build_container_name) \ + $(docker_build_image_name) + +dev-docker: + $(V)docker rm -f $(docker_dev_container_name) 2> /dev/null || true + $(V)docker \ + build \ + -t $(docker_dev_image_name) \ + -f Dockerfile.dev \ + . + $(V)docker \ + run \ + --interactive \ + --tty \ + --volume $(shell pwd):/bemanitools \ + --name $(docker_dev_container_name) \ + $(docker_dev_image_name) clean-docker: - $(V)docker rm -f $(docker_container_name) || true - $(V)docker image rm -f $(docker_image_name) || true + $(V)docker rm -f $(docker_dev_container_name) || true + $(V)docker image rm -f $(docker_dev_image_name) || true + $(V)docker rm -f $(docker_build_container_name) || true + $(V)docker image rm -f $(docker_build_image_name) || true $(V)rm -rf $(BUILDDIR) # -- 2.54.0 From a4b3f9293550e4ea9d69e46714b6979ecac86bd9 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:14:42 +0100 Subject: [PATCH 04/13] feat(avs): Add property get and clear error functions Use these to improve error handling by allowing one to provide additional error information on property related operations. --- src/imports/avs.h | 3 +++ src/imports/import_32_0_avs.def | 2 ++ src/imports/import_32_1002_avs.def | 2 ++ src/imports/import_32_1101_avs.def | 2 ++ src/imports/import_32_1304_avs.def | 2 ++ src/imports/import_32_1306_avs.def | 2 ++ src/imports/import_32_1403_avs.def | 2 ++ src/imports/import_32_1508_avs.def | 2 ++ src/imports/import_32_1601_avs.def | 2 ++ src/imports/import_32_1603_avs.def | 2 ++ src/imports/import_32_1700_avs.def | 2 ++ src/imports/import_32_803_avs.def | 2 ++ src/imports/import_64_1508_avs.def | 2 ++ src/imports/import_64_1509_avs.def | 2 ++ src/imports/import_64_1601_avs.def | 2 ++ src/imports/import_64_1603_avs.def | 4 +++- src/imports/import_64_1700_avs.def | 2 ++ 17 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/imports/avs.h b/src/imports/avs.h index a2d439e..9dce5be 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -220,6 +220,9 @@ void property_file_write(struct property *prop, const char *path); int property_set_flag(struct property *prop, int flags, int mask); void property_destroy(struct property *prop); +avs_error property_get_error(struct property *prop); +struct property *property_clear_error(struct property *prop); + int property_psmap_import( struct property *prop, struct property_node *root, diff --git a/src/imports/import_32_0_avs.def b/src/imports/import_32_0_avs.def index 273e5be..9632ecb 100644 --- a/src/imports/import_32_0_avs.def +++ b/src/imports/import_32_0_avs.def @@ -25,6 +25,8 @@ EXPORTS property_destroy property_file_write property_insert_read + property_clear_error + property_get_error property_mem_write property_read_query_memsize property_search diff --git a/src/imports/import_32_1002_avs.def b/src/imports/import_32_1002_avs.def index e2ff5ae..cc4ee29 100644 --- a/src/imports/import_32_1002_avs.def +++ b/src/imports/import_32_1002_avs.def @@ -28,6 +28,8 @@ EXPORTS property_destroy property_file_write property_insert_read + property_clear_error + property_get_error property_mem_write property_read_query_memsize property_search diff --git a/src/imports/import_32_1101_avs.def b/src/imports/import_32_1101_avs.def index 10c59e4..776dc71 100644 --- a/src/imports/import_32_1101_avs.def +++ b/src/imports/import_32_1101_avs.def @@ -26,6 +26,8 @@ EXPORTS property_desc_to_buffer @246 NONAME property_destroy @247 NONAME property_insert_read @255 NONAME + property_clear_error @573 NONAME + property_get_error @573 NONAME property_node_create @266 NONAME property_node_datasize @267 NONAME property_node_name @274 NONAME diff --git a/src/imports/import_32_1304_avs.def b/src/imports/import_32_1304_avs.def index f83d5a5..18dad23 100644 --- a/src/imports/import_32_1304_avs.def +++ b/src/imports/import_32_1304_avs.def @@ -25,6 +25,8 @@ EXPORTS property_desc_to_buffer @201 NONAME property_destroy @264 NONAME property_insert_read @23 NONAME + property_clear_error @573 NONAME + property_get_error @573 NONAME property_node_create @316 NONAME property_node_datasize @249 NONAME property_node_name @255 NONAME diff --git a/src/imports/import_32_1306_avs.def b/src/imports/import_32_1306_avs.def index f963ec8..f6deed7 100644 --- a/src/imports/import_32_1306_avs.def +++ b/src/imports/import_32_1306_avs.def @@ -25,6 +25,8 @@ EXPORTS property_desc_to_buffer @201 NONAME == XC058ba50000cd property_destroy @264 NONAME == XC058ba500010f property_insert_read @23 NONAME == XC058ba5000016 + property_clear_error @573 NONAME + property_get_error @573 NONAME property_node_create @316 NONAME == XC058ba5000143 property_node_datasize @249 NONAME == XC058ba5000100 property_node_name @255 NONAME == XC058ba5000106 diff --git a/src/imports/import_32_1403_avs.def b/src/imports/import_32_1403_avs.def index de171c1..716d081 100644 --- a/src/imports/import_32_1403_avs.def +++ b/src/imports/import_32_1403_avs.def @@ -24,6 +24,8 @@ EXPORTS property_desc_to_buffer @131 NONAME property_destroy @130 NONAME property_insert_read @133 NONAME + property_clear_error @573 NONAME + property_get_error @573 NONAME property_node_name @573 NONAME == property_node_read @573 NONAME == property_node_remove @148 NONAME diff --git a/src/imports/import_32_1508_avs.def b/src/imports/import_32_1508_avs.def index d162512..504a303 100644 --- a/src/imports/import_32_1508_avs.def +++ b/src/imports/import_32_1508_avs.def @@ -26,6 +26,8 @@ EXPORTS property_desc_to_buffer @129 NONAME property_destroy @128 NONAME property_insert_read @131 NONAME + property_clear_error @573 NONAME + property_get_error @573 NONAME property_node_create @145 NONAME property_node_name @150 NONAME property_node_read @154 NONAME == XCd229cc0000f3 diff --git a/src/imports/import_32_1601_avs.def b/src/imports/import_32_1601_avs.def index 74332d4..286806b 100644 --- a/src/imports/import_32_1601_avs.def +++ b/src/imports/import_32_1601_avs.def @@ -19,6 +19,8 @@ EXPORTS property_destroy @125 NONAME property_desc_to_buffer @126 NONAME property_insert_read @128 NONAME + property_clear_error @573 NONAME + property_get_error @573 NONAME property_search @141 NONAME property_node_create @142 NONAME property_node_name @147 NONAME == XCnbrep7000092 diff --git a/src/imports/import_32_1603_avs.def b/src/imports/import_32_1603_avs.def index 21f55c1..93fd07d 100644 --- a/src/imports/import_32_1603_avs.def +++ b/src/imports/import_32_1603_avs.def @@ -19,6 +19,8 @@ EXPORTS property_destroy @146 NONAME property_desc_to_buffer @147 NONAME property_insert_read @149 NONAME + property_clear_error @158 NONAME == XCnbrep700009d + property_get_error @159 NONAME == XCnbrep700009e property_search @162 NONAME property_node_create @163 NONAME property_node_name @168 NONAME == XCnbrep70000a7 diff --git a/src/imports/import_32_1700_avs.def b/src/imports/import_32_1700_avs.def index 7dc4eaa..f01bc39 100644 --- a/src/imports/import_32_1700_avs.def +++ b/src/imports/import_32_1700_avs.def @@ -21,6 +21,8 @@ EXPORTS property_destroy @146 NONAME property_desc_to_buffer @147 NONAME property_insert_read @149 NONAME + property_clear_error @158 NONAME == XCgsqzn000009d + property_get_error @159 NONAME == XCgsqzn000009e property_search @162 NONAME property_node_create @163 NONAME property_node_name @168 NONAME == XCgsqzn00000a7 diff --git a/src/imports/import_32_803_avs.def b/src/imports/import_32_803_avs.def index 37f2b3e..9340dad 100644 --- a/src/imports/import_32_803_avs.def +++ b/src/imports/import_32_803_avs.def @@ -25,6 +25,8 @@ EXPORTS property_destroy property_file_write property_insert_read + property_clear_error + property_get_error property_mem_write property_read_query_memsize property_search diff --git a/src/imports/import_64_1508_avs.def b/src/imports/import_64_1508_avs.def index 6aeaba1..1a93e7e 100644 --- a/src/imports/import_64_1508_avs.def +++ b/src/imports/import_64_1508_avs.def @@ -26,6 +26,8 @@ EXPORTS property_desc_to_buffer @129 NONAME property_destroy @128 NONAME property_insert_read @131 NONAME + property_clear_error @573 NONAME + property_get_error @573 NONAME property_node_create @145 NONAME property_node_name @150 NONAME property_node_read @154 NONAME == XCd229cc0000f3 diff --git a/src/imports/import_64_1509_avs.def b/src/imports/import_64_1509_avs.def index fde35d6..a596513 100644 --- a/src/imports/import_64_1509_avs.def +++ b/src/imports/import_64_1509_avs.def @@ -26,6 +26,8 @@ EXPORTS property_desc_to_buffer @129 NONAME property_destroy @128 NONAME property_insert_read @131 NONAME + property_clear_error @573 NONAME + property_get_error @573 NONAME property_node_create @145 NONAME property_node_name @573 NONAME == property_node_read @573 NONAME == diff --git a/src/imports/import_64_1601_avs.def b/src/imports/import_64_1601_avs.def index 418395f..960fb94 100644 --- a/src/imports/import_64_1601_avs.def +++ b/src/imports/import_64_1601_avs.def @@ -19,6 +19,8 @@ EXPORTS property_destroy @125 NONAME property_desc_to_buffer @126 NONAME property_insert_read @128 NONAME + property_clear_error @573 NONAME + property_get_error @573 NONAME property_search @141 NONAME property_node_create @142 NONAME property_node_name @147 NONAME == XCnbrep7000092 diff --git a/src/imports/import_64_1603_avs.def b/src/imports/import_64_1603_avs.def index b106ab1..14f42b7 100644 --- a/src/imports/import_64_1603_avs.def +++ b/src/imports/import_64_1603_avs.def @@ -19,12 +19,14 @@ EXPORTS property_destroy @146 NONAME property_desc_to_buffer @147 NONAME property_insert_read @149 NONAME + property_clear_error @158 NONAME == XCnbrep700009d + property_get_error @159 NONAME == XCnbrep700009e property_search @162 NONAME property_node_create @163 NONAME property_node_name @168 NONAME == XCnbrep70000a7 property_node_remove @164 NONAME property_node_type @169 NONAME == XCnbrep70000a8 - property_node_clone @165 NONAME + property_node_clone @165 NONAME == XCnbrep70000a4 property_node_traversal @167 NONAME property_node_refdata @166 NONAME == XCnbrep70000a5 property_node_datasize @171 NONAME == XCnbrep70000aa diff --git a/src/imports/import_64_1700_avs.def b/src/imports/import_64_1700_avs.def index 8cddba8..3e456b5 100644 --- a/src/imports/import_64_1700_avs.def +++ b/src/imports/import_64_1700_avs.def @@ -21,6 +21,8 @@ EXPORTS property_destroy @146 NONAME property_desc_to_buffer @147 NONAME property_insert_read @149 NONAME + property_clear_error @158 NONAME == XCgsqzn000009d + property_get_error @159 NONAME == XCgsqzn000009e property_search @162 NONAME property_node_create @163 NONAME property_node_name @168 NONAME == XCgsqzn00000a7 -- 2.54.0 From e81bd6f9cd0e782bf2313cf1df62c28d53dd8fb6 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:34:37 +0100 Subject: [PATCH 05/13] fix(avs): Incorrect function signature After getting doubts, I looked this one up again on the assembly. The decompiled output confused me and no actual value is being returned there. --- src/imports/avs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imports/avs.h b/src/imports/avs.h index 9dce5be..ae054e5 100644 --- a/src/imports/avs.h +++ b/src/imports/avs.h @@ -221,7 +221,7 @@ int property_set_flag(struct property *prop, int flags, int mask); void property_destroy(struct property *prop); avs_error property_get_error(struct property *prop); -struct property *property_clear_error(struct property *prop); +void property_clear_error(struct property *prop); int property_psmap_import( struct property *prop, -- 2.54.0 From 2d4d5fa53599d3f95583860f9978618427c34dfc Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:34:37 +0100 Subject: [PATCH 06/13] feat(avs-util): Add helper to translate property errors --- src/main/avs-util/error.c | 10 ++++++++++ src/main/avs-util/error.h | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/avs-util/error.c b/src/main/avs-util/error.c index ab09c01..d31b2e1 100644 --- a/src/main/avs-util/error.c +++ b/src/main/avs-util/error.c @@ -96,4 +96,14 @@ const char *avs_util_error_str(avs_error error) } return avs_util_error_unknown; +} + +const char *avs_util_property_error_get_and_clear(struct property *prop) +{ + avs_error error; + + error = property_get_error(prop); + property_clear_error(prop); + + return avs_util_error_str(error); } \ No newline at end of file diff --git a/src/main/avs-util/error.h b/src/main/avs-util/error.h index 3598851..88d68c5 100644 --- a/src/main/avs-util/error.h +++ b/src/main/avs-util/error.h @@ -5,4 +5,6 @@ const char *avs_util_error_str(avs_error error); -#endif \ No newline at end of file +const char *avs_util_property_error_get_and_clear(struct property *prop); + +#endif -- 2.54.0 From 7d397dc54c6d1087d8bda6e5c7702061d481a0e6 Mon Sep 17 00:00:00 2001 From: garbage Date: Mon, 4 Mar 2024 08:49:59 +0900 Subject: [PATCH 07/13] iidxhook3: Add hook for ea3_boot func --- Module.mk | 2 +- src/main/iidxhook-util/eamuse.h | 2 +- src/main/iidxhook3/Module.mk | 4 + src/main/iidxhook3/avs-boot.c | 178 ++++++++++++++++++++++++++++++++ src/main/iidxhook3/avs-boot.h | 19 ++++ src/main/iidxhook3/dllmain.c | 10 +- 6 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 src/main/iidxhook3/avs-boot.c create mode 100644 src/main/iidxhook3/avs-boot.h diff --git a/Module.mk b/Module.mk index 35e690d..16a1177 100644 --- a/Module.mk +++ b/Module.mk @@ -292,7 +292,7 @@ $(zipdir)/iidx-13.zip: \ $(V)zip -j $@ $^ $(zipdir)/iidx-14-to-17.zip: \ - build/bin/avs2_0-32/iidxhook3.dll \ + build/bin/avs2_803-32/iidxhook3.dll \ build/bin/indep-32/config.exe \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ diff --git a/src/main/iidxhook-util/eamuse.h b/src/main/iidxhook-util/eamuse.h index 5929ed8..111ffe3 100644 --- a/src/main/iidxhook-util/eamuse.h +++ b/src/main/iidxhook-util/eamuse.h @@ -5,7 +5,7 @@ /** * Hook various calls resolving the service address to connect to - * the eamuse server for the old IIDX games (9th to Sirius) + * the eamuse server for the old IIDX games (9th to DistorteD) */ void eamuse_hook_init(void); diff --git a/src/main/iidxhook3/Module.mk b/src/main/iidxhook3/Module.mk index e328bec..a575a13 100644 --- a/src/main/iidxhook3/Module.mk +++ b/src/main/iidxhook3/Module.mk @@ -4,6 +4,9 @@ ldflags_iidxhook3 := \ -lws2_32 \ -liphlpapi \ +deplibs_iidxhook3 := \ + avs \ + libs_iidxhook3 := \ iidxhook-util \ ezusb-emu \ @@ -22,4 +25,5 @@ libs_iidxhook3 := \ ezusb \ src_iidxhook3 := \ + avs-boot.c \ dllmain.c \ diff --git a/src/main/iidxhook3/avs-boot.c b/src/main/iidxhook3/avs-boot.c new file mode 100644 index 0000000..196439f --- /dev/null +++ b/src/main/iidxhook3/avs-boot.c @@ -0,0 +1,178 @@ +#define LOG_MODULE "avs-boot" + +#include +#include + +#include "hook/table.h" + +#include "imports/avs.h" + +#include "iidxhook3/avs-boot.h" + +#include "util/log.h" + +static void (*real_avs_boot)( + struct property_node *config, + void *std_heap, + size_t sz_std_heap, + void *avs_heap, + size_t sz_avs_heap, + avs_log_writer_t log_writer, + void *log_context); +static int (*real_ea3_boot_avs)(struct property_node *config); +static int (*real_ea3_boot)(struct property_node *config); + +static void my_avs_boot( + struct property_node *config, + void *std_heap, + size_t sz_std_heap, + void *avs_heap, + size_t sz_avs_heap, + avs_log_writer_t log_writer, + void *log_context); +static int my_ea3_boot_avs(struct property_node *config); +static int my_ea3_boot(struct property_node *config); + +static struct net_addr iidxhook3_avs_boot_eamuse_server_addr; + +static const struct hook_symbol iidxhook3_avs_hook_syms[] = { + {.name = "avs_boot", + .patch = my_avs_boot, + .link = (void **) &real_avs_boot}, + {.name = "ea3_boot", + .patch = my_ea3_boot_avs, + .link = (void **) &real_ea3_boot_avs}, +}; + +static const struct hook_symbol iidxhook3_ea3_hook_syms[] = { + {.name = "ea3_boot", + .patch = my_ea3_boot, + .link = (void **) &real_ea3_boot}, +}; + +static void avs_boot_replace_property_uint32( + struct property_node *node, const char *name, uint32_t val) +{ + struct property_node *tmp; + + tmp = property_search(NULL, node, name); + + if (tmp) { + property_node_remove(tmp); + } + + property_node_create(NULL, node, PSMAP_TYPE_U32, name, val); +} + +static void avs_boot_replace_property_str( + struct property_node *node, const char *name, const char *val) +{ + struct property_node *tmp; + + tmp = property_search(NULL, node, name); + + if (tmp) { + property_node_remove(tmp); + } + + tmp = property_node_create(NULL, node, PROPERTY_TYPE_STR, name, val); + + if (tmp) { + property_node_datasize(tmp); + } else { + log_fatal("Could not avs_boot_replace_property_str(%s, %s)", name, val); + } +} + +static void my_avs_boot( + struct property_node *config, + void *std_heap, + size_t sz_std_heap, + void *avs_heap, + size_t sz_avs_heap, + avs_log_writer_t log_writer, + void *log_context) +{ + log_info("Called my_avs_boot"); + + avs_boot_replace_property_uint32(config, "log/level", 4); + + real_avs_boot( + config, + std_heap, + sz_std_heap, + avs_heap, + sz_avs_heap, + log_writer_debug, + NULL); +} + +static void insert_eamuse_addr(struct property_node *config) +{ + char *server_addr; + + if (iidxhook3_avs_boot_eamuse_server_addr.type != NET_ADDR_TYPE_INVALID) { + log_misc("Injecting network server address"); + + server_addr = net_addr_to_str(&iidxhook3_avs_boot_eamuse_server_addr); + + // Remove protocol to avoid errors during ea3_boot. + if (!strncmp(server_addr, "http://", strlen("http://"))) { + server_addr += strlen("http://"); + } else if (!strncmp(server_addr, "https://", strlen("https://"))) { + server_addr += strlen("https://"); + } + + avs_boot_replace_property_str(config, "network/services", server_addr); + + free(server_addr); + } +} + +static int my_ea3_boot_avs(struct property_node *config) +{ + log_info("Called my_ea3_boot_avs"); + insert_eamuse_addr(config); + return real_ea3_boot_avs(config); +} + +static int my_ea3_boot(struct property_node *config) +{ + log_info("Called my_ea3_boot"); + insert_eamuse_addr(config); + return real_ea3_boot(config); +} + +void iidxhook3_avs_boot_init() +{ + // IIDX 14 and 15 have the ea3_boot in libavs-win32.dll. + hook_table_apply( + NULL, + "libavs-win32.dll", + iidxhook3_avs_hook_syms, + lengthof(iidxhook3_avs_hook_syms)); + + hook_table_apply( + NULL, + "libavs-win32-ea3.dll", + iidxhook3_ea3_hook_syms, + lengthof(iidxhook3_ea3_hook_syms)); + + memset(&iidxhook3_avs_boot_eamuse_server_addr, 0, sizeof(struct net_addr)); + + log_info("Inserted avs log hooks"); +} + +void iidxhook3_avs_boot_set_eamuse_addr(const struct net_addr *server_addr) +{ + char *str; + + str = net_addr_to_str(server_addr); + log_info("Setting eamuse server: %s", str); + free(str); + + memcpy( + &iidxhook3_avs_boot_eamuse_server_addr, + server_addr, + sizeof(struct net_addr)); +} diff --git a/src/main/iidxhook3/avs-boot.h b/src/main/iidxhook3/avs-boot.h new file mode 100644 index 0000000..d4fffab --- /dev/null +++ b/src/main/iidxhook3/avs-boot.h @@ -0,0 +1,19 @@ +#ifndef IIDXHOOK3_AVS_BOOT_H +#define IIDXHOOK3_AVS_BOOT_H + +#include "util/net.h" + +/** + * Initialize hooking of avs_boot and ea3_boot. This re-enables avs logging + * and injects a few important settings. + */ +void iidxhook3_avs_boot_init(); + +/** + * Set the target eamuse server address. + * + * @param server_addr Address to target eamuse server. + */ +void iidxhook3_avs_boot_set_eamuse_addr(const struct net_addr *server_addr); + +#endif diff --git a/src/main/iidxhook3/dllmain.c b/src/main/iidxhook3/dllmain.c index b2e0e41..49882b8 100644 --- a/src/main/iidxhook3/dllmain.c +++ b/src/main/iidxhook3/dllmain.c @@ -27,6 +27,8 @@ #include "hooklib/rs232.h" #include "hooklib/setupapi.h" +#include "iidxhook3/avs-boot.h" + #include "iidxhook-util/acio.h" #include "iidxhook-util/chart-patch.h" #include "iidxhook-util/clock.h" @@ -35,7 +37,6 @@ #include "iidxhook-util/config-misc.h" #include "iidxhook-util/config-sec.h" #include "iidxhook-util/d3d9.h" -#include "iidxhook-util/eamuse.h" #include "iidxhook-util/settings.h" #include "security/rp-sign-key.h" @@ -176,10 +177,8 @@ my_OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) ezusb_iidx_emu_node_security_plug_set_pcbid(&config_eamuse.pcbid); ezusb_iidx_emu_node_security_plug_set_eamid(&config_eamuse.eamid); - /* eAmusement server IP */ - - eamuse_set_addr(&config_eamuse.server); - eamuse_check_connection(); + iidxhook3_avs_boot_init(); + iidxhook3_avs_boot_set_eamuse_addr(&config_eamuse.server); /* Settings paths */ @@ -259,7 +258,6 @@ BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx) acp_hook_init(); adapter_hook_init(); - eamuse_hook_init(); settings_hook_init(); } -- 2.54.0 From f7142df84d7110f2e4e357dbb436cad6aab29cb6 Mon Sep 17 00:00:00 2001 From: garbage Date: Mon, 4 Mar 2024 09:59:14 +0900 Subject: [PATCH 08/13] iidxhook-util: Remove inet_addr hook --- src/main/iidxhook-util/eamuse.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/main/iidxhook-util/eamuse.c b/src/main/iidxhook-util/eamuse.c index 15d77da..65561b3 100644 --- a/src/main/iidxhook-util/eamuse.c +++ b/src/main/iidxhook-util/eamuse.c @@ -18,12 +18,10 @@ /* ------------------------------------------------------------------------- */ -static unsigned long STDCALL my_inet_addr(const char *cp); static int STDCALL my_connect(SOCKET s, const struct sockaddr *addr, int addrlen); static struct hostent FAR *STDCALL my_gethostbyname(const char *nameB); -static unsigned long(STDCALL *real_inet_addr)(const char *cp); static int(STDCALL *real_connect)( SOCKET s, const struct sockaddr *addr, int addrlen); static struct hostent FAR *(STDCALL *real_gethostbyname)(const char *nameB); @@ -33,10 +31,6 @@ static const struct hook_symbol eamuse_hook_syms[] = { /* WS2_32.DLL's SDK import lib generates ordinal imports, so these ordinals are a frozen aspect of the Win32 ABI. */ - {.name = "inet_addr", - .ordinal = 11, - .patch = my_inet_addr, - .link = (void **) &real_inet_addr}, {.name = "connect", .ordinal = 4, .patch = my_connect, @@ -54,26 +48,6 @@ static struct net_addr eamuse_server_addr_resolved; /* ------------------------------------------------------------------------- */ -static unsigned long STDCALL my_inet_addr(const char *cp) -{ - char *tmp; - - /* for a stock machine connected to the eamuse router, - the game wants to connect to the standard domain - services.konami.eamuse.fun - depending on the router you got, it will be services.hostname.my.router - so we catch that and turn it into any ip we want */ - - /* bugfix win10: don't just catch services.konami... because - win10 is doing some weird stuff and this call also contains - various IP addresses. Always return the server address */ - tmp = net_addr_to_str(&eamuse_server_addr_resolved); - log_misc("my_inet_addr: '%s' -> %s", cp, tmp); - free(tmp); - - return eamuse_server_addr_resolved.ipv4.addr; -} - static int STDCALL my_connect(SOCKET s, const struct sockaddr *addr, int addrlen) { @@ -104,9 +78,6 @@ static struct hostent FAR *STDCALL my_gethostbyname(const char *name) { char *tmp; - /* for doc, checkout the other detour of inetaddr above - this call is used starting GOLD (not used on pre GOLD) */ - /* bugfix win10: don't just catch services.konami... because win10 is doing some weird stuff and this call also contains various IP addresses. Always return the server address */ -- 2.54.0 From ca42257fa26ea4944b48f036036012cc23a37e40 Mon Sep 17 00:00:00 2001 From: garbage Date: Mon, 4 Mar 2024 10:08:53 +0900 Subject: [PATCH 09/13] iidxhook-util: Add domain check to my_gethostbyname func --- src/main/iidxhook-util/eamuse.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/iidxhook-util/eamuse.c b/src/main/iidxhook-util/eamuse.c index 65561b3..bd56c77 100644 --- a/src/main/iidxhook-util/eamuse.c +++ b/src/main/iidxhook-util/eamuse.c @@ -76,11 +76,13 @@ my_connect(SOCKET s, const struct sockaddr *addr, int addrlen) static struct hostent FAR *STDCALL my_gethostbyname(const char *name) { + // IIDX 9-13 use the `services` domain (not `services.eamuse.konami.fun`). + if (strcmp(name, "services") != 0) { + return real_gethostbyname(name); + } + char *tmp; - /* bugfix win10: don't just catch services.konami... because - win10 is doing some weird stuff and this call also contains - various IP addresses. Always return the server address */ tmp = net_addr_to_str(&eamuse_server_addr_resolved); log_misc("my_gethostbyname: '%s' to ip %s", name, tmp); free(tmp); -- 2.54.0 From a03c4e0d93ab46ff182a757934da5b02cc15fa3c Mon Sep 17 00:00:00 2001 From: Will Xyen Date: Sat, 11 May 2024 01:45:20 -0700 Subject: [PATCH 10/13] iidxhook9: add fs hook for F drive --- src/main/iidxhook9/Module.mk | 1 + src/main/iidxhook9/dllmain.c | 4 +++ src/main/iidxhook9/fs-hook.c | 50 ++++++++++++++++++++++++++++++++++++ src/main/iidxhook9/fs-hook.h | 6 +++++ 4 files changed, 61 insertions(+) create mode 100644 src/main/iidxhook9/fs-hook.c create mode 100644 src/main/iidxhook9/fs-hook.h diff --git a/src/main/iidxhook9/Module.mk b/src/main/iidxhook9/Module.mk index 30384f1..09e56a8 100644 --- a/src/main/iidxhook9/Module.mk +++ b/src/main/iidxhook9/Module.mk @@ -29,4 +29,5 @@ libs_iidxhook9 := \ src_iidxhook9 := \ config-io.c \ + fs-hook.c \ dllmain.c \ diff --git a/src/main/iidxhook9/dllmain.c b/src/main/iidxhook9/dllmain.c index b7ae85a..577d35b 100644 --- a/src/main/iidxhook9/dllmain.c +++ b/src/main/iidxhook9/dllmain.c @@ -26,6 +26,7 @@ #include "bio2emu-iidx/bi2a.h" #include "iidxhook9/config-io.h" +#include "iidxhook9/fs-hook.h" #include "camhook/cam.h" #include "camhook/config-cam.h" @@ -159,6 +160,9 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) } else { memfile_hook_add_fd("d:\\\\001rom.txt", ABSOLUTE_MATCH, "LDJ", 3); } + + // redirect F:\ drive to vfs (used for video recording) + iidxhook9_fs_hooks_init(); } rs232_hook_init(); diff --git a/src/main/iidxhook9/fs-hook.c b/src/main/iidxhook9/fs-hook.c new file mode 100644 index 0000000..b7e0df2 --- /dev/null +++ b/src/main/iidxhook9/fs-hook.c @@ -0,0 +1,50 @@ +#define LOG_MODULE "fs-hook" + +#include +#include + +#include "hook/table.h" + +#include "imports/avs.h" + +#include "iidxhook9/fs-hook.h" + +#include "util/log.h" +#include "util/str.h" + +static void *(*real_avs_fs_mount)(const char *dest, const char *src, const char *fs_type, const char *options); +static void *my_avs_fs_mount(const char *dest, const char *src, const char *fs_type, const char *options); + +static const struct hook_symbol avs_fs_hook_syms[] = { + {.name = "XCgsqzn000004b", // avs_fs_mount + .ordinal = 76, + .patch = my_avs_fs_mount, + .link = (void **) &real_avs_fs_mount}, +}; + +static void *my_avs_fs_mount(const char *dest, const char *src, const char *fs_type, const char *options) +{ + // quick check for "F:\" + if (src[0] == 'F' && src[1] == ':' && src[2] == '\0') { + const char* dev_folder_drive = "dev/vfs/drive_f/"; + log_misc("Redirecting %s to %s", src, dev_folder_drive); + + CreateDirectoryA("dev/vfs/", NULL); + CreateDirectoryA("dev/vfs/drive_f/", NULL); + + return real_avs_fs_mount(dest, dev_folder_drive, fs_type, options); + } + + return real_avs_fs_mount(dest, src, fs_type, options); +} + +void iidxhook9_fs_hooks_init() +{ + hook_table_apply( + NULL, + "avs2-core.dll", + avs_fs_hook_syms, + lengthof(avs_fs_hook_syms)); + + log_info("Inserted avs fs hooks"); +} diff --git a/src/main/iidxhook9/fs-hook.h b/src/main/iidxhook9/fs-hook.h new file mode 100644 index 0000000..4b0d514 --- /dev/null +++ b/src/main/iidxhook9/fs-hook.h @@ -0,0 +1,6 @@ +#ifndef IIDXHOOK9_FS_HOOKS_H +#define IIDXHOOK9_FS_HOOKS_H + +void iidxhook9_fs_hooks_init(); + +#endif -- 2.54.0 From 8307837995134f2e6e7128ee5aabe9a9edce3804 Mon Sep 17 00:00:00 2001 From: Will Xyen Date: Sat, 11 May 2024 19:42:59 -0700 Subject: [PATCH 11/13] camhook: split cam-detect code to different file --- src/main/camhook/Module.mk | 1 + src/main/camhook/cam-detect.c | 375 +++++++++++++++++++++++++++++++++ src/main/camhook/cam-detect.h | 29 +++ src/main/camhook/cam.c | 378 +--------------------------------- src/main/camhook/cam.h | 7 + 5 files changed, 418 insertions(+), 372 deletions(-) create mode 100644 src/main/camhook/cam-detect.c create mode 100644 src/main/camhook/cam-detect.h diff --git a/src/main/camhook/Module.mk b/src/main/camhook/Module.mk index 6584380..0a6b19e 100644 --- a/src/main/camhook/Module.mk +++ b/src/main/camhook/Module.mk @@ -8,4 +8,5 @@ libs_camhook := \ src_camhook := \ cam.c \ + cam-detect.c \ config-cam.c \ diff --git a/src/main/camhook/cam-detect.c b/src/main/camhook/cam-detect.c new file mode 100644 index 0000000..586206c --- /dev/null +++ b/src/main/camhook/cam-detect.c @@ -0,0 +1,375 @@ +#define LOG_MODULE "cam-hook" + +// clang-format off +// Don't format because the order is important here +#include +#include +// clang-format on + +#include +#include +#include + +#include +#include + +#include + +#include "camhook/cam-detect.h" + +#include "util/defs.h" +#include "util/log.h" +#include "util/str.h" + +EXTERN_GUID( + MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + 0xc60ac5fe, + 0x252a, + 0x478f, + 0xa0, + 0xef, + 0xbc, + 0x8f, + 0xa5, + 0xf7, + 0xca, + 0xd3); +EXTERN_GUID( + MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID, + 0x8ac3587a, + 0x4ae7, + 0x42d8, + 0x99, + 0xe0, + 0x0a, + 0x60, + 0x13, + 0xee, + 0xf9, + 0x0f); +EXTERN_GUID( + MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, + 0x58f0aad8, + 0x22bf, + 0x4f8a, + 0xbb, + 0x3d, + 0xd2, + 0xc4, + 0x97, + 0x8c, + 0x6e, + 0x2f); +// define ourselves cause mingw has these wrong + + +bool check_four(const char inA[4], const char inB[4]) +{ + return (*(uint32_t *) inA == *(uint32_t *) inB); +} + +char *grab_next_camera_id(char *buffer, size_t bsz) +{ + static size_t gotten = 0; + + IMFAttributes *pAttributes = NULL; + IMFActivate **ppDevices = NULL; + + buffer[0] = '\0'; + + HRESULT hr = MFCreateAttributes(&pAttributes, 1); + + if (FAILED(hr)) { + log_info("MFCreateAttributes failed: %ld", hr); + goto done; + } + + hr = pAttributes->lpVtbl->SetGUID( + pAttributes, + &MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + &MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); + if (FAILED(hr)) { + log_info("SetGUID failed: %ld", hr); + goto done; + } + + UINT32 count; + hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count); + + if (FAILED(hr)) { + log_info("MFEnumDeviceSources failed: %ld", hr); + goto done; + } + + if (count <= gotten) { + log_info("gotten failed: %d < %d", count, (int) gotten); + // not enough remaining + goto done; + } + + wchar_t wSymLink[CAMERA_DATA_STRING_SIZE]; + UINT32 sz; + + hr = ppDevices[gotten]->lpVtbl->GetString( + ppDevices[gotten], + &MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, + wSymLink, + CAMERA_DATA_STRING_SIZE, + &sz); + + if (FAILED(hr)) { + log_info("GetString failed: %ld", hr); + goto done; + } + + wcstombs(buffer, wSymLink, bsz); + log_info("Detected webcam: %s\n", buffer); + ++gotten; + +done: + if (pAttributes) { + pAttributes->lpVtbl->Release(pAttributes); + } + + for (DWORD i = 0; i < count; i++) { + if (ppDevices != NULL && ppDevices[i]) { + ppDevices[i]->lpVtbl->Release(ppDevices[i]); + } + } + + CoTaskMemFree(ppDevices); + + return buffer; +} + +bool convert_sym_to_path(const char *sym, char *path) +{ + HDEVINFO DeviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL); + + if (DeviceInfoSet == INVALID_HANDLE_VALUE) { + log_info("Could not open SetupDiCreateDeviceInfoList\n"); + return 0; + } + + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = {0}; + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + if (!SetupDiOpenDeviceInterfaceA( + DeviceInfoSet, sym, 0, &DeviceInterfaceData)) { + log_info("Could not SetupDiOpenDeviceInterfaceA\n"); + return 0; + } + + SP_DEVINFO_DATA DeviceInfoData = {0}; + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + if (!SetupDiGetDeviceInterfaceDetailA( + DeviceInfoSet, + &DeviceInterfaceData, + NULL, + 0, + NULL, + &DeviceInfoData)) { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + log_info("Could not SetupDiGetDeviceInterfaceDetailA\n"); + return 0; + } + } + + DWORD sz; + + if (!SetupDiGetDeviceInstanceIdA( + DeviceInfoSet, + &DeviceInfoData, + path, + CAMERA_DATA_STRING_SIZE, + &sz)) { + log_info("Could not SetupDiGetDeviceInstanceIdA\n"); + return 0; + } + + if (DeviceInfoSet != INVALID_HANDLE_VALUE) { + if (!SetupDiDeleteDeviceInterfaceData( + DeviceInfoSet, &DeviceInterfaceData)) { + log_info("Could not SetupDiDeleteDeviceInterfaceData\n"); + return 0; + } + + SetupDiDestroyDeviceInfoList(DeviceInfoSet); + } + + return 1; +} + +void strtolower(char *str) +{ + for (size_t i = 0; str[i]; i++) { + str[i] = tolower(str[i]); + } +} + +bool convert_path_to_fakesym(const char *path, wchar_t *sym, char *extra_o) +{ + char root[16] = {0}; + char vidstr[16] = {0}; + char pidstr[16] = {0}; + char mistr[16] = {0}; + char extra[64] = {0}; + sscanf( + path, + "%[^\\]\\%[^&]&%[^&]&%[^\\]\\%s", + root, + vidstr, + pidstr, + mistr, + extra); + strcpy(extra_o, extra); + + strtolower(root); + strtolower(vidstr); + strtolower(pidstr); + strtolower(mistr); + strtolower(extra); + + char buffer[CAMERA_DATA_STRING_SIZE]; + snprintf( + buffer, + CAMERA_DATA_STRING_SIZE, + "\\\\?\\%s#%s&%s&%s#%s", + root, + vidstr, + pidstr, + mistr, + extra); + + mbstowcs(sym, buffer, CAMERA_DATA_STRING_SIZE); + + return true; +} + +void fill_cam_struct(struct CameraData *data, const char *devid) +{ + char buffer[CAMERA_DATA_STRING_SIZE]; + + data->setup = false; + + if (!devid || strlen(devid) == 0) { + devid = grab_next_camera_id(buffer, CAMERA_DATA_STRING_SIZE); + } + + if (!devid || strlen(devid) == 0) { + // no more cameras remain? + return; + } + + if (strlen(devid) >= CAMERA_DATA_STRING_SIZE) { + // error probably log something? + return; + } + + // detect input type + if (check_four(devid, "\\\\?\\")) { + // SYMBOLIC_LINK + if (!convert_sym_to_path(devid, data->deviceInstancePath)) { + log_info("Could not convert %s to path", devid); + return; + } + } else if (check_four(devid, "USB\\")) { + // Device instance path + strcpy(data->deviceInstancePath, devid); + // continue + } else { + // UNKNOWN ENTRY + log_info("UNK: %s", devid); + log_info("Please enter the device instance path"); + return; + } + + if (!convert_path_to_fakesym( + data->deviceInstancePath, + data->deviceSymbolicLink, + data->extra_upper)) { + log_info("Could not convert %s to sym", data->deviceInstancePath); + return; + } + + log_info("dev path: %s", data->deviceInstancePath); + + // locate device nodes + DEVINST dnDevInst; + DEVINST parentDev; + CONFIGRET cmret; + + cmret = CM_Locate_DevNodeA( + &dnDevInst, data->deviceInstancePath, CM_LOCATE_DEVNODE_NORMAL); + + if (cmret != CR_SUCCESS) { + log_info("CM_Locate_DevNodeA fail: %s", data->deviceInstancePath); + return; + } + + cmret = CM_Get_Parent(&parentDev, dnDevInst, 0); + + if (cmret != CR_SUCCESS) { + log_info("CM_Get_Parent fail: %s", data->deviceInstancePath); + return; + } + cmret = CM_Get_Device_IDA( + parentDev, data->parent_deviceInstancePath, CAMERA_DATA_STRING_SIZE, 0); + + if (cmret != CR_SUCCESS) { + log_info("CM_Get_Device_IDA parent fail: %s", data->deviceInstancePath); + return; + } + + ULONG szAddr; + ULONG szDesc; + + szAddr = 4; + szDesc = CAMERA_DATA_STRING_SIZE; + cmret = CM_Get_DevNode_Registry_PropertyA( + dnDevInst, CM_DRP_ADDRESS, NULL, &data->address, &szAddr, 0); + + if (cmret != CR_SUCCESS) { + log_info( + "CM_Get_DevNode_Registry_PropertyA fail: %s", + data->deviceInstancePath); + return; + } + + cmret = CM_Get_DevNode_Registry_PropertyA( + dnDevInst, CM_DRP_DEVICEDESC, NULL, &data->name, &szDesc, 0); + + if (cmret != CR_SUCCESS) { + log_info( + "CM_Get_DevNode_Registry_PropertyA fail: %s", + data->deviceInstancePath); + return; + } + + szAddr = 4; + szDesc = CAMERA_DATA_STRING_SIZE; + cmret = CM_Get_DevNode_Registry_PropertyA( + parentDev, CM_DRP_ADDRESS, NULL, &data->parent_address, &szAddr, 0); + + if (cmret != CR_SUCCESS) { + log_info( + "CM_Get_DevNode_Registry_PropertyA parent fail: %s", + data->deviceInstancePath); + return; + } + + cmret = CM_Get_DevNode_Registry_PropertyA( + parentDev, CM_DRP_DEVICEDESC, NULL, &data->parent_name, &szDesc, 0); + + if (cmret != CR_SUCCESS) { + log_info( + "CM_Get_DevNode_Registry_PropertyA parent fail: %s", + data->deviceInstancePath); + return; + } + + log_info("Found %s @ %d", data->name, data->address); + log_info("Parent %s @ %d", data->parent_name, data->parent_address); + data->setup = true; +} diff --git a/src/main/camhook/cam-detect.h b/src/main/camhook/cam-detect.h new file mode 100644 index 0000000..75fd10f --- /dev/null +++ b/src/main/camhook/cam-detect.h @@ -0,0 +1,29 @@ +#ifndef CAMHOOK_CAM_DETECT_H +#define CAMHOOK_CAM_DETECT_H + +#include +#include + +#define CAMERA_DATA_STRING_SIZE 0x100 + +struct CameraData { + bool setup; + char name[CAMERA_DATA_STRING_SIZE]; + char deviceInstancePath[CAMERA_DATA_STRING_SIZE]; + wchar_t deviceSymbolicLink[CAMERA_DATA_STRING_SIZE]; + char extra_upper[CAMERA_DATA_STRING_SIZE]; + int address; + char parent_name[CAMERA_DATA_STRING_SIZE]; + char parent_deviceInstancePath[CAMERA_DATA_STRING_SIZE]; + int parent_address; + + bool fake_addressed; + int fake_address; + + bool fake_located; + size_t fake_located_node; +}; + +void fill_cam_struct(struct CameraData *data, const char *devid); + +#endif diff --git a/src/main/camhook/cam.c b/src/main/camhook/cam.c index 4ce9260..0ae144c 100644 --- a/src/main/camhook/cam.c +++ b/src/main/camhook/cam.c @@ -19,72 +19,11 @@ #include "hook/table.h" #include "camhook/cam.h" +#include "camhook/cam-detect.h" #include "util/defs.h" #include "util/log.h" #include "util/str.h" -#include "util/time.h" - -#define CAMERA_DATA_STRING_SIZE 0x100 - -EXTERN_GUID( - MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, - 0xc60ac5fe, - 0x252a, - 0x478f, - 0xa0, - 0xef, - 0xbc, - 0x8f, - 0xa5, - 0xf7, - 0xca, - 0xd3); -EXTERN_GUID( - MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID, - 0x8ac3587a, - 0x4ae7, - 0x42d8, - 0x99, - 0xe0, - 0x0a, - 0x60, - 0x13, - 0xee, - 0xf9, - 0x0f); -EXTERN_GUID( - MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, - 0x58f0aad8, - 0x22bf, - 0x4f8a, - 0xbb, - 0x3d, - 0xd2, - 0xc4, - 0x97, - 0x8c, - 0x6e, - 0x2f); -// define ourselves cause mingw has these wrong - -struct CameraData { - bool setup; - char name[CAMERA_DATA_STRING_SIZE]; - char deviceInstancePath[CAMERA_DATA_STRING_SIZE]; - wchar_t deviceSymbolicLink[CAMERA_DATA_STRING_SIZE]; - char extra_upper[CAMERA_DATA_STRING_SIZE]; - int address; - char parent_name[CAMERA_DATA_STRING_SIZE]; - char parent_deviceInstancePath[CAMERA_DATA_STRING_SIZE]; - int parent_address; - - bool fake_addressed; - int fake_address; - - bool fake_located; - size_t fake_located_node; -}; static struct CameraData camData[CAMHOOK_CONFIG_CAM_MAX]; int camAddresses[CAMHOOK_CONFIG_CAM_MAX] = { @@ -94,6 +33,8 @@ int camAddresses[CAMHOOK_CONFIG_CAM_MAX] = { static size_t num_addressed_cams = 0; static size_t num_located_cams = 0; +static enum camhook_version camhook_version = CAMHOOK_OLD; + static CONFIGRET my_CM_Locate_DevNodeA( PDEVINST pdnDevInst, DEVINSTID_A pDeviceID, ULONG ulFlags); @@ -280,7 +221,7 @@ HRESULT my_GetAllocatedString( HRESULT ret; log_info("Inside: %s", __FUNCTION__); - // should probably check GUID, oh well + // should probably check GUID == MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, oh well ret = real_GetAllocatedString(self, guidKey, ppwszValue, pcchLength); char *pMBBuffer = (char *) malloc(0x100); wcstombs(pMBBuffer, *ppwszValue, 0x100); @@ -491,315 +432,8 @@ static HDEVINFO my_SetupDiGetClassDevsA( return real_SetupDiGetClassDevsA(ClassGuid, Enumerator, hwndParent, Flags); } -bool check_four(const char inA[4], const char inB[4]) -{ - return (*(uint32_t *) inA == *(uint32_t *) inB); -} - -char *grab_next_camera_id(char *buffer, size_t bsz) -{ - static size_t gotten = 0; - - IMFAttributes *pAttributes = NULL; - IMFActivate **ppDevices = NULL; - - buffer[0] = '\0'; - - HRESULT hr = MFCreateAttributes(&pAttributes, 1); - - if (FAILED(hr)) { - log_info("MFCreateAttributes failed: %ld", hr); - goto done; - } - - hr = pAttributes->lpVtbl->SetGUID( - pAttributes, - &MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, - &MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); - if (FAILED(hr)) { - log_info("SetGUID failed: %ld", hr); - goto done; - } - - UINT32 count; - hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count); - - if (FAILED(hr)) { - log_info("MFEnumDeviceSources failed: %ld", hr); - goto done; - } - - if (count <= gotten) { - log_info("gotten failed: %d < %d", count, (int) gotten); - // not enough remaining - goto done; - } - - wchar_t wSymLink[CAMERA_DATA_STRING_SIZE]; - UINT32 sz; - - hr = ppDevices[gotten]->lpVtbl->GetString( - ppDevices[gotten], - &MY_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, - wSymLink, - CAMERA_DATA_STRING_SIZE, - &sz); - - if (FAILED(hr)) { - log_info("GetString failed: %ld", hr); - goto done; - } - - wcstombs(buffer, wSymLink, bsz); - log_info("Detected webcam: %s\n", buffer); - ++gotten; - -done: - if (pAttributes) { - pAttributes->lpVtbl->Release(pAttributes); - } - - for (DWORD i = 0; i < count; i++) { - if (ppDevices != NULL && ppDevices[i]) { - ppDevices[i]->lpVtbl->Release(ppDevices[i]); - } - } - - CoTaskMemFree(ppDevices); - - return buffer; -} - -bool convert_sym_to_path(const char *sym, char *path) -{ - HDEVINFO DeviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL); - - if (DeviceInfoSet == INVALID_HANDLE_VALUE) { - log_info("Could not open SetupDiCreateDeviceInfoList\n"); - return 0; - } - - SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = {0}; - DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - - if (!SetupDiOpenDeviceInterfaceA( - DeviceInfoSet, sym, 0, &DeviceInterfaceData)) { - log_info("Could not SetupDiOpenDeviceInterfaceA\n"); - return 0; - } - - SP_DEVINFO_DATA DeviceInfoData = {0}; - DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - - if (!SetupDiGetDeviceInterfaceDetailA( - DeviceInfoSet, - &DeviceInterfaceData, - NULL, - 0, - NULL, - &DeviceInfoData)) { - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - log_info("Could not SetupDiGetDeviceInterfaceDetailA\n"); - return 0; - } - } - - DWORD sz; - - if (!SetupDiGetDeviceInstanceIdA( - DeviceInfoSet, - &DeviceInfoData, - path, - CAMERA_DATA_STRING_SIZE, - &sz)) { - log_info("Could not SetupDiGetDeviceInstanceIdA\n"); - return 0; - } - - if (DeviceInfoSet != INVALID_HANDLE_VALUE) { - if (!SetupDiDeleteDeviceInterfaceData( - DeviceInfoSet, &DeviceInterfaceData)) { - log_info("Could not SetupDiDeleteDeviceInterfaceData\n"); - return 0; - } - - SetupDiDestroyDeviceInfoList(DeviceInfoSet); - } - - return 1; -} - -void strtolower(char *str) -{ - for (size_t i = 0; str[i]; i++) { - str[i] = tolower(str[i]); - } -} - -bool convert_path_to_fakesym(const char *path, wchar_t *sym, char *extra_o) -{ - char root[16] = {0}; - char vidstr[16] = {0}; - char pidstr[16] = {0}; - char mistr[16] = {0}; - char extra[64] = {0}; - sscanf( - path, - "%[^\\]\\%[^&]&%[^&]&%[^\\]\\%s", - root, - vidstr, - pidstr, - mistr, - extra); - strcpy(extra_o, extra); - - strtolower(root); - strtolower(vidstr); - strtolower(pidstr); - strtolower(mistr); - strtolower(extra); - - char buffer[CAMERA_DATA_STRING_SIZE]; - snprintf( - buffer, - CAMERA_DATA_STRING_SIZE, - "\\\\?\\%s#%s&%s&%s#%s", - root, - vidstr, - pidstr, - mistr, - extra); - - mbstowcs(sym, buffer, CAMERA_DATA_STRING_SIZE); - - return true; -} - -void fill_cam_struct(struct CameraData *data, const char *devid) -{ - char buffer[CAMERA_DATA_STRING_SIZE]; - - data->setup = false; - - if (!devid || strlen(devid) == 0) { - devid = grab_next_camera_id(buffer, CAMERA_DATA_STRING_SIZE); - } - - if (!devid || strlen(devid) == 0) { - // no more cameras remain? - return; - } - - if (strlen(devid) >= CAMERA_DATA_STRING_SIZE) { - // error probably log something? - return; - } - - // detect input type - if (check_four(devid, "\\\\?\\")) { - // SYMBOLIC_LINK - if (!convert_sym_to_path(devid, data->deviceInstancePath)) { - log_info("Could not convert %s to path", devid); - return; - } - } else if (check_four(devid, "USB\\")) { - // Device instance path - strcpy(data->deviceInstancePath, devid); - // continue - } else { - // UNKNOWN ENTRY - log_info("UNK: %s", devid); - log_info("Please enter the device instance path"); - return; - } - - if (!convert_path_to_fakesym( - data->deviceInstancePath, - data->deviceSymbolicLink, - data->extra_upper)) { - log_info("Could not convert %s to sym", data->deviceInstancePath); - return; - } - - log_info("dev path: %s", data->deviceInstancePath); - - // locate device nodes - DEVINST dnDevInst; - DEVINST parentDev; - CONFIGRET cmret; - - cmret = CM_Locate_DevNodeA( - &dnDevInst, data->deviceInstancePath, CM_LOCATE_DEVNODE_NORMAL); - - if (cmret != CR_SUCCESS) { - log_info("CM_Locate_DevNodeA fail: %s", data->deviceInstancePath); - return; - } - - cmret = CM_Get_Parent(&parentDev, dnDevInst, 0); - - if (cmret != CR_SUCCESS) { - log_info("CM_Get_Parent fail: %s", data->deviceInstancePath); - return; - } - cmret = CM_Get_Device_IDA( - parentDev, data->parent_deviceInstancePath, CAMERA_DATA_STRING_SIZE, 0); - - if (cmret != CR_SUCCESS) { - log_info("CM_Get_Device_IDA parent fail: %s", data->deviceInstancePath); - return; - } - - ULONG szAddr; - ULONG szDesc; - - szAddr = 4; - szDesc = CAMERA_DATA_STRING_SIZE; - cmret = CM_Get_DevNode_Registry_PropertyA( - dnDevInst, CM_DRP_ADDRESS, NULL, &data->address, &szAddr, 0); - - if (cmret != CR_SUCCESS) { - log_info( - "CM_Get_DevNode_Registry_PropertyA fail: %s", - data->deviceInstancePath); - return; - } - - cmret = CM_Get_DevNode_Registry_PropertyA( - dnDevInst, CM_DRP_DEVICEDESC, NULL, &data->name, &szDesc, 0); - - if (cmret != CR_SUCCESS) { - log_info( - "CM_Get_DevNode_Registry_PropertyA fail: %s", - data->deviceInstancePath); - return; - } - - szAddr = 4; - szDesc = CAMERA_DATA_STRING_SIZE; - cmret = CM_Get_DevNode_Registry_PropertyA( - parentDev, CM_DRP_ADDRESS, NULL, &data->parent_address, &szAddr, 0); - - if (cmret != CR_SUCCESS) { - log_info( - "CM_Get_DevNode_Registry_PropertyA parent fail: %s", - data->deviceInstancePath); - return; - } - - cmret = CM_Get_DevNode_Registry_PropertyA( - parentDev, CM_DRP_DEVICEDESC, NULL, &data->parent_name, &szDesc, 0); - - if (cmret != CR_SUCCESS) { - log_info( - "CM_Get_DevNode_Registry_PropertyA parent fail: %s", - data->deviceInstancePath); - return; - } - - log_info("Found %s @ %d", data->name, data->address); - log_info("Parent %s @ %d", data->parent_name, data->parent_address); - data->setup = true; +void camhook_set_version(enum camhook_version version) { + camhook_version = version; } void camhook_init(struct camhook_config_cam *config_cam) diff --git a/src/main/camhook/cam.h b/src/main/camhook/cam.h index d3039c6..6d56a36 100644 --- a/src/main/camhook/cam.h +++ b/src/main/camhook/cam.h @@ -3,6 +3,13 @@ #include "camhook/config-cam.h" +enum camhook_version { + CAMHOOK_OLD, + CAMHOOK_NEW, +}; + +void camhook_set_version(enum camhook_version version); + void camhook_init(struct camhook_config_cam *config_cam); void camhook_fini(void); -- 2.54.0 From 48668837a8f68fe2e2a5b11f5caf378f3d5422d3 Mon Sep 17 00:00:00 2001 From: Will Xyen Date: Mon, 20 May 2024 23:23:48 -0700 Subject: [PATCH 12/13] camhook: fix camhook for new style camera detection --- dist/iidx/iidxhook-27.conf | 3 + dist/iidx/iidxhook-28.conf | 3 + dist/iidx/iidxhook-29.conf | 3 + dist/iidx/iidxhook-30.conf | 3 + doc/iidxhook/iidxhook9.md | 6 + src/main/camhook/cam-detect.c | 43 ++- src/main/camhook/cam-detect.h | 9 +- src/main/camhook/cam.c | 351 ++++++++++++++++-- src/main/camhook/cam.h | 6 +- src/main/camhook/config-cam.c | 31 +- src/main/camhook/config-cam.h | 9 +- src/main/iidxhook8/dllmain.c | 4 +- src/main/iidxhook9/dllmain.c | 5 +- src/main/sdvxhook2-cn/dllmain.c | 4 +- src/main/sdvxhook2/dllmain.c | 4 +- .../iidxhook8/iidxhook8-config-cam-test.c | 12 +- 16 files changed, 426 insertions(+), 70 deletions(-) diff --git a/dist/iidx/iidxhook-27.conf b/dist/iidx/iidxhook-27.conf index 62e879c..008c3fa 100644 --- a/dist/iidx/iidxhook-27.conf +++ b/dist/iidx/iidxhook-27.conf @@ -22,6 +22,9 @@ io.tt_multiplier=1.0 # Disables the camera emulation cam.disable_emu=true +# Camera port layout (0 = LDJ, 1 = CLDJ/TDJ-JA, 2 = TDJ-JB) +cam.port_layout=1 + # Disable camera 1. Use, i.e., if you only have one camera and want it to be mapped to camera 2 ingame. cam.disable_camera1=false diff --git a/dist/iidx/iidxhook-28.conf b/dist/iidx/iidxhook-28.conf index 62e879c..008c3fa 100644 --- a/dist/iidx/iidxhook-28.conf +++ b/dist/iidx/iidxhook-28.conf @@ -22,6 +22,9 @@ io.tt_multiplier=1.0 # Disables the camera emulation cam.disable_emu=true +# Camera port layout (0 = LDJ, 1 = CLDJ/TDJ-JA, 2 = TDJ-JB) +cam.port_layout=1 + # Disable camera 1. Use, i.e., if you only have one camera and want it to be mapped to camera 2 ingame. cam.disable_camera1=false diff --git a/dist/iidx/iidxhook-29.conf b/dist/iidx/iidxhook-29.conf index eb7e6bc..23fe34e 100644 --- a/dist/iidx/iidxhook-29.conf +++ b/dist/iidx/iidxhook-29.conf @@ -22,6 +22,9 @@ io.tt_multiplier=1.0 # Disables the camera emulation cam.disable_emu=true +# Camera port layout (0 = LDJ, 1 = CLDJ/TDJ-JA, 2 = TDJ-JB) +cam.port_layout=1 + # Disable camera 1. Use, i.e., if you only have one camera and want it to be mapped to camera 2 ingame. cam.disable_camera1=true diff --git a/dist/iidx/iidxhook-30.conf b/dist/iidx/iidxhook-30.conf index eb7e6bc..23fe34e 100644 --- a/dist/iidx/iidxhook-30.conf +++ b/dist/iidx/iidxhook-30.conf @@ -22,6 +22,9 @@ io.tt_multiplier=1.0 # Disables the camera emulation cam.disable_emu=true +# Camera port layout (0 = LDJ, 1 = CLDJ/TDJ-JA, 2 = TDJ-JB) +cam.port_layout=1 + # Disable camera 1. Use, i.e., if you only have one camera and want it to be mapped to camera 2 ingame. cam.disable_camera1=true diff --git a/doc/iidxhook/iidxhook9.md b/doc/iidxhook/iidxhook9.md index c89159b..71f363a 100644 --- a/doc/iidxhook/iidxhook9.md +++ b/doc/iidxhook/iidxhook9.md @@ -93,6 +93,12 @@ The syntax for the "key=value" is the same as in the config file. Make sure to h However, if a parameter is specifed in the configuration file and as a command line argument, the command line argument overrides the config file's value. +# Note on camhook + +The cammera hook (camhook) is used to allow other class compliant cameras to be used. Depending on the variant of the game, you will need to select the correct camera port layout. + +For original LDJ, this is always 0, for conversion kit LDJ and original TDJ-JA, this is 1, and for TDJ-JB this will be 2 (this case is rare / you will know if this is needed on your setup). + # Eamuse network setup If you want to run the games online, you need a valid PCBID and the service URL. Open diff --git a/src/main/camhook/cam-detect.c b/src/main/camhook/cam-detect.c index 586206c..cbff8be 100644 --- a/src/main/camhook/cam-detect.c +++ b/src/main/camhook/cam-detect.c @@ -247,7 +247,7 @@ bool convert_path_to_fakesym(const char *path, wchar_t *sym, char *extra_o) return true; } -void fill_cam_struct(struct CameraData *data, const char *devid) +void fill_cam_struct(struct camera_data *data, const char *devid) { char buffer[CAMERA_DATA_STRING_SIZE]; @@ -295,6 +295,21 @@ void fill_cam_struct(struct CameraData *data, const char *devid) log_info("dev path: %s", data->deviceInstancePath); + char* vid_str = strstr(data->deviceInstancePath, "VID_"); + char* pid_str = strstr(data->deviceInstancePath, "PID_"); + + if (!vid_str || !pid_str) { + log_info("Could not parse VID/PID in %s", data->deviceInstancePath); + return; + } + + data->vid = strtol(vid_str + 4, NULL, 16); + data->pid = strtol(pid_str + 4, NULL, 16); + + log_info("vid: %04x", data->vid); + log_info("pid: %04x", data->pid); + + // locate device nodes DEVINST dnDevInst; DEVINST parentDev; @@ -324,52 +339,64 @@ void fill_cam_struct(struct CameraData *data, const char *devid) ULONG szAddr; ULONG szDesc; + ULONG szDriverKey; szAddr = 4; - szDesc = CAMERA_DATA_STRING_SIZE; cmret = CM_Get_DevNode_Registry_PropertyA( dnDevInst, CM_DRP_ADDRESS, NULL, &data->address, &szAddr, 0); if (cmret != CR_SUCCESS) { log_info( - "CM_Get_DevNode_Registry_PropertyA fail: %s", + "CM_Get_DevNode_Registry_PropertyA CM_DRP_ADDRESS fail: %s", data->deviceInstancePath); return; } + szDesc = CAMERA_DATA_STRING_SIZE; cmret = CM_Get_DevNode_Registry_PropertyA( dnDevInst, CM_DRP_DEVICEDESC, NULL, &data->name, &szDesc, 0); if (cmret != CR_SUCCESS) { log_info( - "CM_Get_DevNode_Registry_PropertyA fail: %s", + "CM_Get_DevNode_Registry_PropertyA CM_DRP_DEVICEDESC fail: %s", data->deviceInstancePath); return; } szAddr = 4; - szDesc = CAMERA_DATA_STRING_SIZE; cmret = CM_Get_DevNode_Registry_PropertyA( parentDev, CM_DRP_ADDRESS, NULL, &data->parent_address, &szAddr, 0); if (cmret != CR_SUCCESS) { log_info( - "CM_Get_DevNode_Registry_PropertyA parent fail: %s", + "CM_Get_DevNode_Registry_PropertyA CM_DRP_ADDRESS parent fail: %s", data->deviceInstancePath); return; } + szDesc = CAMERA_DATA_STRING_SIZE; cmret = CM_Get_DevNode_Registry_PropertyA( parentDev, CM_DRP_DEVICEDESC, NULL, &data->parent_name, &szDesc, 0); if (cmret != CR_SUCCESS) { log_info( - "CM_Get_DevNode_Registry_PropertyA parent fail: %s", + "CM_Get_DevNode_Registry_PropertyA CM_DRP_DEVICEDESC parent fail: %s", + data->deviceInstancePath); + return; + } + + szDriverKey = CAMERA_DATA_STRING_SIZE; + cmret = CM_Get_DevNode_Registry_PropertyA( + parentDev, CM_DRP_DRIVER, NULL, &data->parent_driverKey, &szDriverKey, 0); + + if (cmret != CR_SUCCESS) { + log_info( + "CM_Get_DevNode_Registry_PropertyA CM_DRP_DRIVER parent fail: %s", data->deviceInstancePath); return; } log_info("Found %s @ %d", data->name, data->address); - log_info("Parent %s @ %d", data->parent_name, data->parent_address); + log_info("Parent %s @ %d %s", data->parent_name, data->parent_address, data->parent_driverKey); data->setup = true; } diff --git a/src/main/camhook/cam-detect.h b/src/main/camhook/cam-detect.h index 75fd10f..f7a8dbe 100644 --- a/src/main/camhook/cam-detect.h +++ b/src/main/camhook/cam-detect.h @@ -2,11 +2,12 @@ #define CAMHOOK_CAM_DETECT_H #include +#include #include #define CAMERA_DATA_STRING_SIZE 0x100 -struct CameraData { +struct camera_data { bool setup; char name[CAMERA_DATA_STRING_SIZE]; char deviceInstancePath[CAMERA_DATA_STRING_SIZE]; @@ -14,9 +15,13 @@ struct CameraData { char extra_upper[CAMERA_DATA_STRING_SIZE]; int address; char parent_name[CAMERA_DATA_STRING_SIZE]; + char parent_driverKey[CAMERA_DATA_STRING_SIZE]; char parent_deviceInstancePath[CAMERA_DATA_STRING_SIZE]; int parent_address; + int16_t vid; + int16_t pid; + bool fake_addressed; int fake_address; @@ -24,6 +29,6 @@ struct CameraData { size_t fake_located_node; }; -void fill_cam_struct(struct CameraData *data, const char *devid); +void fill_cam_struct(struct camera_data *data, const char *devid); #endif diff --git a/src/main/camhook/cam.c b/src/main/camhook/cam.c index 0ae144c..db380f1 100644 --- a/src/main/camhook/cam.c +++ b/src/main/camhook/cam.c @@ -4,6 +4,8 @@ // Don't format because the order is important here #include #include +#include +#include // clang-format on #include @@ -25,15 +27,19 @@ #include "util/log.h" #include "util/str.h" -static struct CameraData camData[CAMHOOK_CONFIG_CAM_MAX]; -int camAddresses[CAMHOOK_CONFIG_CAM_MAX] = { - 1, - 7, +#define CAMHOOK_NUM_LAYOUTS 3 + +static struct camera_data camData[CAMHOOK_CONFIG_CAM_MAX]; +int camAddresses[CAMHOOK_NUM_LAYOUTS][CAMHOOK_CONFIG_CAM_MAX] = { + {1, 7}, + {4, 9}, + {4, 3}, }; static size_t num_addressed_cams = 0; static size_t num_located_cams = 0; +static int camhook_port_layout = 0; -static enum camhook_version camhook_version = CAMHOOK_OLD; +static enum camhook_version camhook_version = CAMHOOK_VERSION_OLD; static CONFIGRET my_CM_Locate_DevNodeA( PDEVINST pdnDevInst, DEVINSTID_A pDeviceID, ULONG ulFlags); @@ -97,6 +103,26 @@ static HDEVINFO my_SetupDiGetClassDevsA( static HDEVINFO (*real_SetupDiGetClassDevsA)( CONST GUID *ClassGuid, PCSTR Enumerator, HWND hwndParent, DWORD Flags); +static BOOL STDCALL my_DeviceIoControl( + HANDLE hFile, + uint32_t dwIoControlCode, + void *lpInBuffer, + uint32_t nInBufferSize, + void *lpOutBuffer, + uint32_t nOutBufferSize, + uint32_t *lpBytesReturned, + OVERLAPPED *lpOverlapped); + +static BOOL(STDCALL *real_DeviceIoControl)( + HANDLE fd, + uint32_t code, + void *in_bytes, + uint32_t in_nbytes, + void *out_bytes, + uint32_t out_nbytes, + uint32_t *out_returned, + OVERLAPPED *ovl); + static const struct hook_symbol camhook_cfgmgr32_syms[] = { {.name = "CM_Locate_DevNodeA", .patch = my_CM_Locate_DevNodeA, @@ -121,12 +147,24 @@ static const struct hook_symbol camhook_cfgmgr32_syms[] = { .link = (void **) &real_SetupDiGetClassDevsA}, }; +static const struct hook_symbol camhook_cfgmgr32_syms_new[] = { + {.name = "CM_Locate_DevNodeA", + .patch = my_CM_Locate_DevNodeA, + .link = (void **) &real_CM_Locate_DevNodeA}, +}; + static const struct hook_symbol camhook_mf_syms[] = { {.name = "MFEnumDeviceSources", .patch = my_MFEnumDeviceSources, .link = (void **) &real_MFEnumDeviceSources}, }; +static struct hook_symbol camhook_ioctl_syms[] = { + {.name = "DeviceIoControl", + .patch = my_DeviceIoControl, + .link = (void *) &real_DeviceIoControl}, +}; + DEVINST camhook_custom_nodes[CAMHOOK_CONFIG_CAM_MAX] = { 0x04040004, 0x04040008, @@ -150,25 +188,42 @@ my_CM_Locate_DevNodeA(PDEVINST pdnDevInst, DEVINSTID_A pDeviceID, ULONG ulFlags) log_info("seeking: %s", pDeviceID); for (size_t i = 0; i < CAMHOOK_CONFIG_CAM_MAX; ++i) { if (camData[i].setup) { - snprintf( - builtString, - CAMERA_DATA_STRING_SIZE, - "USB\\VID_288C&PID_0002&MI_00\\%s", - camData[i].extra_upper); + if (camhook_version == CAMHOOK_VERSION_OLD) { + snprintf( + builtString, + CAMERA_DATA_STRING_SIZE, + "USB\\VID_288C&PID_0002&MI_00\\%s", + camData[i].extra_upper); + } else if (camhook_version == CAMHOOK_VERSION_NEW) { + snprintf( + builtString, + CAMERA_DATA_STRING_SIZE, + "USB\\VID_05A3&PID_9230&MI_00\\%s", + camData[i].extra_upper); + } + log_info("built: %s", builtString); if (strcmp(pDeviceID, builtString) == 0) { - if (!camData[i].fake_located) { - camData[i].fake_located_node = num_located_cams; - camData[i].fake_located = true; - ++num_located_cams; + if (camhook_version == CAMHOOK_VERSION_OLD) { + if (!camData[i].fake_located) { + camData[i].fake_located_node = num_located_cams; + camData[i].fake_located = true; + ++num_located_cams; + } + log_info( + "Injecting custom device %d to node %x", + (int) i, + (int) camData[i].fake_located_node); + *pdnDevInst = + camhook_custom_nodes[camData[i].fake_located_node]; + return CR_SUCCESS; + } else if (camhook_version == CAMHOOK_VERSION_NEW) { + // inject original device + log_info( + "Injecting original device %d %s -> %s", + (int) i, pDeviceID, camData[i].deviceInstancePath); + pDeviceID = camData[i].deviceInstancePath; } - log_info( - "Injecting custom device %d to node %x", - (int) i, - (int) camData[i].fake_located_node); - *pdnDevInst = - camhook_custom_nodes[camData[i].fake_located_node]; - return CR_SUCCESS; } } } @@ -219,7 +274,7 @@ HRESULT my_GetAllocatedString( IMFActivate *self, REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength) { HRESULT ret; - log_info("Inside: %s", __FUNCTION__); + // log_info("Inside: %s", __FUNCTION__); // should probably check GUID == MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, oh well ret = real_GetAllocatedString(self, guidKey, ppwszValue, pcchLength); @@ -240,19 +295,35 @@ HRESULT my_GetAllocatedString( // if matches, replace with target device ID if (pwc) { - // \\?\usb#vid_288c&pid_0002&mi_00 - pwc[12] = L'2'; - pwc[13] = L'8'; - pwc[14] = L'8'; - pwc[15] = L'c'; + if (camhook_version == CAMHOOK_VERSION_OLD) { + // \\?\usb#vid_288c&pid_0002&mi_00 + pwc[12] = L'2'; + pwc[13] = L'8'; + pwc[14] = L'8'; + pwc[15] = L'c'; - pwc[21] = L'0'; - pwc[22] = L'0'; - pwc[23] = L'0'; - pwc[24] = L'2'; + pwc[21] = L'0'; + pwc[22] = L'0'; + pwc[23] = L'0'; + pwc[24] = L'2'; - pwc[29] = L'0'; - pwc[30] = L'0'; + pwc[29] = L'0'; + pwc[30] = L'0'; + } else if (camhook_version == CAMHOOK_VERSION_NEW) { + // \\?\usb#vid_05a3&pid_9230&mi_00 + pwc[12] = L'0'; + pwc[13] = L'5'; + pwc[14] = L'a'; + pwc[15] = L'3'; + + pwc[21] = L'9'; + pwc[22] = L'2'; + pwc[23] = L'3'; + pwc[24] = L'0'; + + pwc[29] = L'0'; + pwc[30] = L'0'; + } wcstombs(pMBBuffer, *ppwszValue, 0x100); log_info("Replaced: %s", pMBBuffer); @@ -275,7 +346,7 @@ static HRESULT my_MFEnumDeviceSources( HRESULT ret; - log_info("Inside: %s", __FUNCTION__); + // log_info("Inside: %s", __FUNCTION__); ret = real_MFEnumDeviceSources( pAttributes, pppSourceActivate, pcSourceActivate); nsrcs = *pcSourceActivate; @@ -378,8 +449,9 @@ static BOOL my_SetupDiGetDeviceRegistryPropertyA( if (camData[i].setup) { if (addr == camData[i].parent_address) { if (!camData[i].fake_addressed) { + // old style always uses set 0 camData[i].fake_address = - camAddresses[num_addressed_cams]; + camAddresses[0][num_addressed_cams]; camData[i].fake_addressed = true; ++num_addressed_cams; } @@ -432,6 +504,178 @@ static HDEVINFO my_SetupDiGetClassDevsA( return real_SetupDiGetClassDevsA(ClassGuid, Enumerator, hwndParent, Flags); } +ULONG get_matching_device_replacement_id( + HANDLE hFile, + ULONG ConnectionIndex, + USB_DEVICE_DESCRIPTOR* desc) +{ + ULONG replacement_id = 0; + for (size_t i = 0; i < CAMHOOK_CONFIG_CAM_MAX; ++i) { + if (camData[i].setup) { + // log_info("Checking %lu %04x %04x vs %lu %04x %04x", + // ConnectionIndex, desc->idVendor, desc->idProduct, + // camData[i].address, camData[i].vid, camData[i].pid); + if ( + ConnectionIndex == camData[i].parent_address && + desc->idVendor == camData[i].vid && + desc->idProduct == camData[i].pid + ) { + // do secondary check for driver key using + // IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME + USB_NODE_CONNECTION_DRIVERKEY_NAME req; + req.ActualLength = 0; + req.ConnectionIndex = ConnectionIndex; + req.DriverKeyName[0] = '\0'; + + DWORD nBytes; + + if (!DeviceIoControl( + hFile, + IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, + &req, + sizeof(req), + &req, + sizeof(req), + &nBytes, + 0LL + )) { + log_warning( + "Failed to get driver key name size for device %04x %04x", + desc->idVendor, + desc->idProduct); + continue; + } + + if (req.ActualLength <= sizeof(req)) { + log_warning( + "Driver key name size too small for device %04x %04x", + desc->idVendor, + desc->idProduct); + continue; + } + + nBytes = req.ActualLength; + + USB_NODE_CONNECTION_DRIVERKEY_NAME *driverKeyNameW = malloc(nBytes); + if (!driverKeyNameW) { + log_warning("Failed to allocate driver key name buffer for device %04x %04x", + desc->idVendor, + desc->idProduct); + continue; + } + driverKeyNameW->ActualLength = 0; + driverKeyNameW->ConnectionIndex = ConnectionIndex; + driverKeyNameW->DriverKeyName[0] = '\0'; + + if (!DeviceIoControl( + hFile, + IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, + driverKeyNameW, + nBytes, + driverKeyNameW, + nBytes, + &nBytes, + 0LL + )) { + log_warning( + "Failed to get driver key name for device %04x %04x", + desc->idVendor, + desc->idProduct); + continue; + } + + + char *driverKeyNameA = NULL; + wstr_narrow(driverKeyNameW->DriverKeyName, &driverKeyNameA); + + free(driverKeyNameW); + + + if (strcmp(driverKeyNameA, camData[i].parent_driverKey) != 0) { + // log just in case? + log_info( + "Driver key name mismatch for device %04x %04x %s != %s", + desc->idVendor, + desc->idProduct, + driverKeyNameA, + camData[i].parent_driverKey); + if (driverKeyNameA) { + free(driverKeyNameA); + } + continue; + } + + log_info( + "Replacing device @ %lu with %d for %04x %04x %s", + ConnectionIndex, + camAddresses[camhook_port_layout][i], + desc->idVendor, + desc->idProduct, + driverKeyNameA); + + if (driverKeyNameA) { + free(driverKeyNameA); + } + + replacement_id = camAddresses[camhook_port_layout][i]; + break; + } + } + } + + return replacement_id; +} + +static BOOL STDCALL my_DeviceIoControl( + HANDLE hFile, + uint32_t dwIoControlCode, + void *lpInBuffer, + uint32_t nInBufferSize, + void *lpOutBuffer, + uint32_t nOutBufferSize, + uint32_t *lpBytesReturned, + OVERLAPPED *lpOverlapped) +{ + BOOL res; + + res = real_DeviceIoControl( + hFile, + dwIoControlCode, + lpInBuffer, + nInBufferSize, + lpOutBuffer, + nOutBufferSize, + lpBytesReturned, + lpOverlapped); + + // if error just return + if (!res) { + return res; + } + + // detect IOCTL_USB_GET_NODE_CONNECTION_INFORMATION (_EX) + // we don't bother faking the rest as it's never read + if (dwIoControlCode == IOCTL_USB_GET_NODE_CONNECTION_INFORMATION) { + USB_NODE_CONNECTION_INFORMATION *connectionInfo = lpOutBuffer; + + ULONG replacement_id = get_matching_device_replacement_id( + hFile, connectionInfo->ConnectionIndex, &connectionInfo->DeviceDescriptor); + if (replacement_id > 0) { + connectionInfo->ConnectionIndex = replacement_id; + } + } else if (dwIoControlCode == IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX) { + USB_NODE_CONNECTION_INFORMATION_EX *connectionInfoEx = lpOutBuffer; + + ULONG replacement_id = get_matching_device_replacement_id( + hFile, connectionInfoEx->ConnectionIndex, &connectionInfoEx->DeviceDescriptor); + if (replacement_id > 0) { + connectionInfoEx->ConnectionIndex = replacement_id; + } + } + + return res; +} + void camhook_set_version(enum camhook_version version) { camhook_version = version; } @@ -461,15 +705,42 @@ void camhook_init(struct camhook_config_cam *config_cam) } } + camhook_port_layout = config_cam->port_layout; + if (camhook_port_layout < 0 || camhook_port_layout >= CAMHOOK_NUM_LAYOUTS) { + camhook_port_layout = 0; + } + if (num_setup > 0) { - hook_table_apply( - NULL, - "setupapi.dll", - camhook_cfgmgr32_syms, - lengthof(camhook_cfgmgr32_syms)); + // always hook_table_apply( NULL, "Mf.dll", camhook_mf_syms, lengthof(camhook_mf_syms)); + if (camhook_version == CAMHOOK_VERSION_OLD) { + hook_table_apply( + NULL, + "cfgmgr32.dll", + camhook_cfgmgr32_syms, + lengthof(camhook_cfgmgr32_syms)); + } else if (camhook_version == CAMHOOK_VERSION_NEW) { + // for CAMHOOK_VERSION_NEW we restore original VID/PID + // so the parent lookup succeeds later + // yes the DLL moved??? + hook_table_apply( + NULL, + "setupapi.dll", + camhook_cfgmgr32_syms_new, + lengthof(camhook_cfgmgr32_syms_new)); + + // they copied / forked usb/usbview/enum.c + // however they don't use most of it + // so we only need DeviceIoControl to inject port # + hook_table_apply( + NULL, + "kernel32.dll", + camhook_ioctl_syms, + lengthof(camhook_ioctl_syms)); + } + log_info("Inserted cam hooks for %d cams", (int) num_setup); // If the user has manually disabled all cams, don't print this in the // log diff --git a/src/main/camhook/cam.h b/src/main/camhook/cam.h index 6d56a36..f2429fc 100644 --- a/src/main/camhook/cam.h +++ b/src/main/camhook/cam.h @@ -3,9 +3,11 @@ #include "camhook/config-cam.h" +// unused to control which camhook version is being used +// defaults to CAMHOOK_VERSION_OLD enum camhook_version { - CAMHOOK_OLD, - CAMHOOK_NEW, + CAMHOOK_VERSION_OLD, + CAMHOOK_VERSION_NEW, }; void camhook_set_version(enum camhook_version version); diff --git a/src/main/camhook/config-cam.c b/src/main/camhook/config-cam.c index 6a55852..43a852b 100644 --- a/src/main/camhook/config-cam.c +++ b/src/main/camhook/config-cam.c @@ -7,6 +7,9 @@ #define CAMHOOK_CONFIG_CAM_DISABLE_EMU_KEY "cam.disable_emu" #define CAMHOOK_CONFIG_CAM_DEFAULT_DISABLE_EMU_VALUE false +#define CAMHOOK_CONFIG_CAM_PORT_LAYOUT_KEY "cam.port_layout" +#define CAMHOOK_CONFIG_CAM_DEFAULT_PORT_LAYOUT_VALUE 0 + // the following four arrays are based on CAMHOOK_CONFIG_CAM_MAX // please insert more elements if more cams are added const char *camhook_config_disable_camera[CAMHOOK_CONFIG_CAM_MAX] = { @@ -30,7 +33,7 @@ const char *camhook_config_device_default_values[CAMHOOK_CONFIG_CAM_MAX] = { "", }; -void camhook_config_cam_init(struct cconfig *config, size_t num_cams) +void camhook_config_cam_init(struct cconfig *config, size_t num_cams, bool use_port_layout) { cconfig_util_set_bool( config, @@ -38,6 +41,14 @@ void camhook_config_cam_init(struct cconfig *config, size_t num_cams) CAMHOOK_CONFIG_CAM_DEFAULT_DISABLE_EMU_VALUE, "Disables the camera emulation"); + if (use_port_layout) { + cconfig_util_set_int( + config, + CAMHOOK_CONFIG_CAM_PORT_LAYOUT_KEY, + CAMHOOK_CONFIG_CAM_DEFAULT_PORT_LAYOUT_VALUE, + "Camera port layout (0 = LDJ, 1 = CLDJ/TDJ-JA, 2 = TDJ-JB)"); + } + for (size_t i = 0; i < num_cams; ++i) { cconfig_util_set_bool( config, @@ -56,7 +67,8 @@ void camhook_config_cam_init(struct cconfig *config, size_t num_cams) void camhook_config_cam_get( struct camhook_config_cam *config_cam, struct cconfig *config, - size_t num_cams) + size_t num_cams, + bool use_port_layout) { config_cam->num_devices = num_cams; @@ -71,6 +83,21 @@ void camhook_config_cam_get( CAMHOOK_CONFIG_CAM_DISABLE_EMU_KEY, CAMHOOK_CONFIG_CAM_DEFAULT_DISABLE_EMU_VALUE); } + + if (use_port_layout) { + if (!cconfig_util_get_int( + config, + CAMHOOK_CONFIG_CAM_PORT_LAYOUT_KEY, + &config_cam->port_layout, + CAMHOOK_CONFIG_CAM_DEFAULT_PORT_LAYOUT_VALUE)) { + log_warning( + "Invalid value for key '%s' specified, fallback " + "to default '%d'", + CAMHOOK_CONFIG_CAM_PORT_LAYOUT_KEY, + CAMHOOK_CONFIG_CAM_DEFAULT_PORT_LAYOUT_VALUE); + } + } + for (size_t i = 0; i < num_cams; ++i) { if (!cconfig_util_get_bool( config, diff --git a/src/main/camhook/config-cam.h b/src/main/camhook/config-cam.h index 893a20b..df0ad7d 100644 --- a/src/main/camhook/config-cam.h +++ b/src/main/camhook/config-cam.h @@ -10,15 +10,20 @@ struct camhook_config_cam { bool disable_emu; size_t num_devices; + int port_layout; char device_id[CAMHOOK_CONFIG_CAM_MAX][MAX_PATH]; bool disable_camera[CAMHOOK_CONFIG_CAM_MAX]; }; -void camhook_config_cam_init(struct cconfig *config, size_t num_cams); +void camhook_config_cam_init( + struct cconfig *config, + size_t num_cams, + bool use_port_layout); void camhook_config_cam_get( struct camhook_config_cam *config_cam, struct cconfig *config, - size_t num_cams); + size_t num_cams, + bool use_port_layout); #endif \ No newline at end of file diff --git a/src/main/iidxhook8/dllmain.c b/src/main/iidxhook8/dllmain.c index 13509be..7bf7e98 100644 --- a/src/main/iidxhook8/dllmain.c +++ b/src/main/iidxhook8/dllmain.c @@ -106,7 +106,7 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) iidxhook_config_gfx_init(config); iidxhook8_config_io_init(config); - camhook_config_cam_init(config, 2); + camhook_config_cam_init(config, 2, false); if (!cconfig_hook_config_init( config, @@ -119,7 +119,7 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) iidxhook_config_gfx_get(&config_gfx, config); iidxhook8_config_io_get(&iidxhook8_config_io, config); - camhook_config_cam_get(&config_cam, config, 2); + camhook_config_cam_get(&config_cam, config, 2, false); cconfig_finit(config); diff --git a/src/main/iidxhook9/dllmain.c b/src/main/iidxhook9/dllmain.c index 577d35b..7044d21 100644 --- a/src/main/iidxhook9/dllmain.c +++ b/src/main/iidxhook9/dllmain.c @@ -68,7 +68,7 @@ static bool load_configs() config = cconfig_init(); iidxhook9_config_io_init(config); - camhook_config_cam_init(config, 2); + camhook_config_cam_init(config, 2, true); d3d9exhook_config_gfx_init(config); @@ -84,7 +84,7 @@ static bool load_configs() } iidxhook9_config_io_get(&iidxhook9_config_io, config); - camhook_config_cam_get(&config_cam, config, 2); + camhook_config_cam_get(&config_cam, config, 2, true); d3d9exhook_config_gfx_get(&config_gfx, config); @@ -188,6 +188,7 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) // camera hooks if (!config_cam.disable_emu) { + camhook_set_version(CAMHOOK_VERSION_NEW); camhook_init(&config_cam); } diff --git a/src/main/sdvxhook2-cn/dllmain.c b/src/main/sdvxhook2-cn/dllmain.c index 3a20217..d94cade 100644 --- a/src/main/sdvxhook2-cn/dllmain.c +++ b/src/main/sdvxhook2-cn/dllmain.c @@ -51,7 +51,7 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) sdvxhook2_cn_config_init(config); d3d9exhook_config_gfx_init(config); - camhook_config_cam_init(config, 1); + camhook_config_cam_init(config, 1, false); if (!cconfig_hook_config_init( config, @@ -62,7 +62,7 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) } sdvxhook2_cn_config_get(&config_cn, config); - camhook_config_cam_get(&config_cam, config, 1); + camhook_config_cam_get(&config_cam, config, 1, false); d3d9exhook_config_gfx_get(&config_gfx, config); cconfig_finit(config); diff --git a/src/main/sdvxhook2/dllmain.c b/src/main/sdvxhook2/dllmain.c index e601e3a..f19142b 100644 --- a/src/main/sdvxhook2/dllmain.c +++ b/src/main/sdvxhook2/dllmain.c @@ -79,7 +79,7 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) sdvxhook2_config_io_init(config); d3d9exhook_config_gfx_init(config); - camhook_config_cam_init(config, 1); + camhook_config_cam_init(config, 1, false); hooklib_config_adapter_init(config); if (!cconfig_hook_config_init( @@ -91,7 +91,7 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) } sdvxhook2_config_io_get(&config_io, config); - camhook_config_cam_get(&config_cam, config, 1); + camhook_config_cam_get(&config_cam, config, 1, false); d3d9exhook_config_gfx_get(&config_gfx, config); hooklib_config_adapter_get(&config_adapter, config); diff --git a/src/test/iidxhook8/iidxhook8-config-cam-test.c b/src/test/iidxhook8/iidxhook8-config-cam-test.c index b7a1a06..bc7f3ae 100644 --- a/src/test/iidxhook8/iidxhook8-config-cam-test.c +++ b/src/test/iidxhook8/iidxhook8-config-cam-test.c @@ -13,8 +13,8 @@ static void test_config_cam_defaults() config = cconfig_init(); - camhook_config_cam_init(config, 2); - camhook_config_cam_get(&config_cam, config, 2); + camhook_config_cam_init(config, 2, false); + camhook_config_cam_get(&config_cam, config, 2, false); cconfig_finit(config); @@ -30,13 +30,13 @@ static void test_config_cam() config = cconfig_init(); - camhook_config_cam_init(config, 2); + camhook_config_cam_init(config, 2, false); cconfig_set2(config, "cam.disable_emu", "true"); cconfig_set2(config, "cam.device_id1", "asdjkasd"); cconfig_set2(config, "cam.device_id2", "1234"); - camhook_config_cam_get(&config_cam, config, 2); + camhook_config_cam_get(&config_cam, config, 2, false); cconfig_finit(config); @@ -52,13 +52,13 @@ static void test_config_cam_invalid_values() config = cconfig_init(); - camhook_config_cam_init(config, 2); + camhook_config_cam_init(config, 2, false); cconfig_set2(config, "cam.disable_emu", "123"); cconfig_set2(config, "cam.device_id1", "asdjkasd"); cconfig_set2(config, "cam.device_id2", "1234"); - camhook_config_cam_get(&config_cam, config, 2); + camhook_config_cam_get(&config_cam, config, 2, false); cconfig_finit(config); -- 2.54.0 From 8b22ef1e8c53ec56dfbc45542ac89a4fe9657f66 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 8 Feb 2025 22:42:52 +0100 Subject: [PATCH 13/13] feat: Add iidxio-async implementation A shim library implementing the same concept as the already existing ddrio-async library. The iidxio implementation takes another iidxio library and runs it asynchronously which may improve performance for certain iidxio implementations, e.g. if the send and receive functions are driving actual IO calls synchrously and are expensive. This is not a replacement for a well engineered and proper implementation of a iidxio library for any specific use-case. It does not fix bad performance of existing implementations, i.e. if the poll rate is too low because actual IO is too slow. Use with caution and know why and when you need to use it. --- Module.mk | 3 + doc/iidxhook/README.md | 2 + doc/iidxhook/iidxio-async.md | 22 ++ src/main/iidxio-async/Module.mk | 9 + src/main/iidxio-async/iidxio-async.def | 18 ++ src/main/iidxio-async/iidxio.c | 407 +++++++++++++++++++++++++ 6 files changed, 461 insertions(+) create mode 100644 doc/iidxhook/iidxio-async.md create mode 100644 src/main/iidxio-async/Module.mk create mode 100644 src/main/iidxio-async/iidxio-async.def create mode 100644 src/main/iidxio-async/iidxio.c diff --git a/Module.mk b/Module.mk index 2a948a8..6754895 100644 --- a/Module.mk +++ b/Module.mk @@ -154,6 +154,7 @@ include src/main/iidxhook6/Module.mk include src/main/iidxhook7/Module.mk include src/main/iidxhook8/Module.mk include src/main/iidxhook9/Module.mk +include src/main/iidxio-async/Module.mk include src/main/iidxio-bio2/Module.mk include src/main/iidxio-ezusb/Module.mk include src/main/iidxio-ezusb2/Module.mk @@ -456,6 +457,7 @@ $(zipdir)/iidx-27-to-30.zip: \ $(zipdir)/iidx-hwio-x86.zip: \ build/bin/indep-32/aciomgr.dll \ build/bin/indep-32/eamio-icca.dll \ + build/bin/indep-32/iidxio-async.dll \ build/bin/indep-32/iidxio-bio2.dll \ build/bin/indep-32/iidxio-ezusb.dll \ build/bin/indep-32/iidxio-ezusb2.dll \ @@ -469,6 +471,7 @@ $(zipdir)/iidx-hwio-x86.zip: \ $(zipdir)/iidx-hwio-x64.zip: \ build/bin/indep-64/aciomgr.dll \ build/bin/indep-64/eamio-icca.dll \ + build/bin/indep-64/iidxio-async.dll \ build/bin/indep-64/iidxio-bio2.dll \ build/bin/indep-64/iidxio-ezusb.dll \ build/bin/indep-64/iidxio-ezusb2.dll \ diff --git a/doc/iidxhook/README.md b/doc/iidxhook/README.md index cd1e68d..e39f56e 100644 --- a/doc/iidxhook/README.md +++ b/doc/iidxhook/README.md @@ -48,6 +48,8 @@ Available implementations that can be swapped out depending on which kind of IO use: - `iidxio`: Default implementation supporting keyboard, mouse and USB game controllers +- [iidxio-async](iidxhook/iidxio-async.md): Shim implementation that runs another iidxio implementation in a dedicated + thread - [iidxio-bio2](iidxhook/iidxio-bio2.md): Support BIO2 hardware - [iidxio-ezusb](iidxhook/iidxio-ezusb.md): Support C02 ezusb FX hardware - [iidxio-ezusb2](iidxhook/iidxio-ezusb2.md): Support IO2 ezusb FX2 hardware diff --git a/doc/iidxhook/iidxio-async.md b/doc/iidxhook/iidxio-async.md new file mode 100644 index 0000000..82f7c21 --- /dev/null +++ b/doc/iidxhook/iidxio-async.md @@ -0,0 +1,22 @@ +# IIDXIO async API implementation + +This implementation of the iidxio API is a shim library that takes another iidxio library and +runs the functions `iidx_io_ep1_send`, `iidx_io_ep2_recv` and `iidx_io_ep3_write_16seg` in a +dedicated thread. State synchronization to the getter and setter functions is also handled +transparently. + +Usage of this **may** improve performance of certain iidxio implementations or when using them +in certain integrations, e.g. the send and receive functions of a iidxio implementation for some +target IO hardware calls are synchronous and expensive. + +This is not a fix/solution to a badly implemented iidxio library with poor performance as it cannot +make it go faster and address potential latency issues, for example. + +Use with caution and know why and when you need to use it. + +## Setup + +* Add `iidxio-async.dll` in the same folder as your `iidxhookX.dll` +* Rename your `iidxio.dll` to `iidxio-async-child.dll` +* Rename `iidxio-async.dll` to `iidxio.dll` +* Run the game \ No newline at end of file diff --git a/src/main/iidxio-async/Module.mk b/src/main/iidxio-async/Module.mk new file mode 100644 index 0000000..d852e4e --- /dev/null +++ b/src/main/iidxio-async/Module.mk @@ -0,0 +1,9 @@ +dlls += iidxio-async + +ldflags_iidxio-async := \ + +libs_iidxio-async := \ + util \ + +src_iidxio-async := \ + iidxio.c \ diff --git a/src/main/iidxio-async/iidxio-async.def b/src/main/iidxio-async/iidxio-async.def new file mode 100644 index 0000000..403900d --- /dev/null +++ b/src/main/iidxio-async/iidxio-async.def @@ -0,0 +1,18 @@ +LIBRARY iidxio + +EXPORTS + iidx_io_ep1_send + iidx_io_ep1_set_deck_lights + iidx_io_ep1_set_panel_lights + iidx_io_ep1_set_top_lamps + iidx_io_ep1_set_top_neons + iidx_io_ep2_get_keys + iidx_io_ep2_get_panel + iidx_io_ep2_get_sys + iidx_io_ep2_get_slider + iidx_io_ep2_get_turntable + iidx_io_ep2_recv + iidx_io_ep3_write_16seg + iidx_io_fini + iidx_io_init + iidx_io_set_loggers diff --git a/src/main/iidxio-async/iidxio.c b/src/main/iidxio-async/iidxio.c new file mode 100644 index 0000000..b81ec6b --- /dev/null +++ b/src/main/iidxio-async/iidxio.c @@ -0,0 +1,407 @@ +#define LOG_MODULE "iidxio-async" + +#include + +#include +#include +#include +#include + +#include + +#include "bemanitools/iidxio.h" + +#include "util/log.h" +#include "util/thread.h" +#include "util/time.h" + +typedef void (*iidx_io_set_loggers_t)( + log_formatter_t misc, + log_formatter_t info, + log_formatter_t warning, + log_formatter_t fatal); +typedef bool (*iidx_io_init_t)( + thread_create_t thread_create, + thread_join_t thread_join, + thread_destroy_t thread_destroy); +typedef void (*iidx_io_fini_t)(void); +typedef void (*iidx_io_ep1_set_deck_lights_t)(uint16_t deck_lights); +typedef void (*iidx_io_ep1_set_panel_lights_t)(uint8_t panel_lights); +typedef void (*iidx_io_ep1_set_top_lamps_t)(uint8_t top_lamps); +typedef void (*iidx_io_ep1_set_top_neons_t)(bool top_neons); +typedef bool (*iidx_io_ep1_send_t)(void); +typedef bool (*iidx_io_ep2_recv_t)(void); +typedef uint8_t (*iidx_io_ep2_get_turntable_t)(uint8_t player_no); +typedef uint8_t (*iidx_io_ep2_get_slider_t)(uint8_t slider_no); +typedef uint8_t (*iidx_io_ep2_get_sys_t)(void); +typedef uint8_t (*iidx_io_ep2_get_panel_t)(void); +typedef uint16_t (*iidx_io_ep2_get_keys_t)(void); +typedef bool (*iidx_io_ep3_write_16seg_t)(const char *text); + +static HMODULE _child_iidx_io_module; + +static iidx_io_set_loggers_t _child_iidx_io_set_loggers; +static iidx_io_init_t _child_iidx_io_init; +static iidx_io_fini_t _child_iidx_io_fini; + +static iidx_io_ep1_set_deck_lights_t _child_iidx_io_ep1_set_deck_lights; +static iidx_io_ep1_set_panel_lights_t _child_iidx_io_ep1_set_panel_lights; +static iidx_io_ep1_set_top_lamps_t _child_iidx_io_ep1_set_top_lamps; +static iidx_io_ep1_set_top_neons_t _child_iidx_io_ep1_set_top_neons; +static iidx_io_ep1_send_t _child_iidx_io_ep1_send; +static iidx_io_ep2_recv_t _child_iidx_io_ep2_recv; +static iidx_io_ep2_get_turntable_t _child_iidx_io_ep2_get_turntable; +static iidx_io_ep2_get_slider_t _child_iidx_io_ep2_get_slider; +static iidx_io_ep2_get_sys_t _child_iidx_io_ep2_get_sys; +static iidx_io_ep2_get_panel_t _child_iidx_io_ep2_get_panel; +static iidx_io_ep2_get_keys_t _child_iidx_io_ep2_get_keys; +static iidx_io_ep3_write_16seg_t _child_iidx_io_ep3_write_16seg; + +static log_formatter_t _log_formatter_misc; +static log_formatter_t _log_formatter_info; +static log_formatter_t _log_formatter_warning; +static log_formatter_t _log_formatter_fatal; + +static _Atomic(bool) _io_thread_proc_loop; +static _Atomic(bool) _io_thread_proc_running; + +static _Atomic(uint16_t) _child_iidx_io_deck_lights; +static _Atomic(uint8_t) _child_iidx_io_panel_lights; +static _Atomic(uint8_t) _child_iidx_io_top_lamps; +static _Atomic(bool) _child_iidx_io_top_neons; + +static _Atomic(uint8_t) _child_iidx_io_turntable_p1; +static _Atomic(uint8_t) _child_iidx_io_turntable_p2; +static _Atomic(uint8_t) _child_iidx_io_slider_1; +static _Atomic(uint8_t) _child_iidx_io_slider_2; +static _Atomic(uint8_t) _child_iidx_io_slider_3; +static _Atomic(uint8_t) _child_iidx_io_slider_4; +static _Atomic(uint8_t) _child_iidx_io_slider_5; +static _Atomic(uint8_t) _child_iidx_io_sys; +static _Atomic(uint8_t) _child_iidx_io_panel; +static _Atomic(uint16_t) _child_iidx_io_keys; + +static HANDLE _child_iidx_io_16seg_mutex; +static char _child_iidx_16seg[9]; +static bool _child_iidx_io_16seg_dirty; + +static int _io_thread_proc(void *ctx) +{ + uint64_t time_start; + uint64_t time_end; + uint64_t loop_counter; + uint64_t total_time; + char text_16seg[9]; + + bool result; + + atomic_store_explicit(&_io_thread_proc_running, true, memory_order_seq_cst); + + log_info("IO thread running"); + + time_start = time_get_counter(); + loop_counter = 0; + + while (atomic_load_explicit(&_io_thread_proc_loop, memory_order_seq_cst)) { + result = _child_iidx_io_ep2_recv(); + + if (!result) { + log_warning("_child_iidx_io_ep2_recv returned false"); + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; + } + + atomic_store_explicit(&_child_iidx_io_turntable_p1, _child_iidx_io_ep2_get_turntable(0), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_turntable_p2, _child_iidx_io_ep2_get_turntable(1), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_1, _child_iidx_io_ep2_get_slider(0), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_2, _child_iidx_io_ep2_get_slider(1), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_3, _child_iidx_io_ep2_get_slider(2), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_4, _child_iidx_io_ep2_get_slider(3), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_5, _child_iidx_io_ep2_get_slider(4), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_sys, _child_iidx_io_ep2_get_sys(), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_panel, _child_iidx_io_ep2_get_panel(), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_keys, _child_iidx_io_ep2_get_keys(), memory_order_relaxed); + + _child_iidx_io_ep1_set_deck_lights(atomic_load_explicit(&_child_iidx_io_deck_lights, memory_order_relaxed)); + _child_iidx_io_ep1_set_panel_lights(atomic_load_explicit(&_child_iidx_io_panel_lights, memory_order_relaxed)); + _child_iidx_io_ep1_set_top_lamps(atomic_load_explicit(&_child_iidx_io_top_lamps, memory_order_relaxed)); + _child_iidx_io_ep1_set_top_neons(atomic_load_explicit(&_child_iidx_io_top_neons, memory_order_relaxed)); + + result = _child_iidx_io_ep1_send(); + + if (!result) { + log_warning("_child_iidx_io_ep1_send returned false"); + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; + } + + if (_child_iidx_io_16seg_dirty) { + WaitForSingleObject(_child_iidx_io_16seg_mutex, INFINITE); + memcpy(text_16seg, _child_iidx_16seg, sizeof(text_16seg)); + _child_iidx_io_16seg_dirty = false; + ReleaseMutex(_child_iidx_io_16seg_mutex); + + result = _child_iidx_io_ep3_write_16seg(text_16seg); + + if (!result) { + log_warning("_child_iidx_io_ep3_write_16seg returned false"); + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; + } + } + + // Don't hog the CPU + SwitchToThread(); + + loop_counter++; + } + + time_end = time_get_counter(); + total_time = time_get_elapsed_us(time_end - time_start); + + log_info( + "IO thread performance: total iterations %lld, avg. loop cycle time %f " + "us", + loop_counter, + ((double) total_time) / loop_counter); + + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; +} + +static void *_load_function(HMODULE module, const char *name) +{ + void *ptr; + + ptr = GetProcAddress(module, name); + + if (ptr == NULL) { + log_fatal("Could not find function %s in iidxio child library", name); + } + + return ptr; +} + +void iidx_io_set_loggers( + log_formatter_t misc, + log_formatter_t info, + log_formatter_t warning, + log_formatter_t fatal) +{ + _log_formatter_misc = misc; + _log_formatter_info = info; + _log_formatter_warning = warning; + _log_formatter_fatal = fatal; + + log_to_external(misc, info, warning, fatal); +} + +bool iidx_io_init( + thread_create_t thread_create, + thread_join_t thread_join, + thread_destroy_t thread_destroy) +{ + log_info("Loading iidxio-async-child.dll as child iidxio library..."); + + _child_iidx_io_module = LoadLibraryA("iidxio-async-child.dll"); + + if (_child_iidx_io_module == NULL) { + log_warning("Loading iidxio-async-child.dll failed"); + return false; + } + + _child_iidx_io_set_loggers = + _load_function(_child_iidx_io_module, "iidx_io_set_loggers"); + _child_iidx_io_init = _load_function(_child_iidx_io_module, "iidx_io_init"); + _child_iidx_io_fini = _load_function(_child_iidx_io_module, "iidx_io_fini"); + + _child_iidx_io_ep1_set_deck_lights = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_deck_lights"); + _child_iidx_io_ep1_set_panel_lights = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_panel_lights"); + _child_iidx_io_ep1_set_top_lamps = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_top_lamps"); + _child_iidx_io_ep1_set_top_neons = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_top_neons"); + _child_iidx_io_ep1_send = + _load_function(_child_iidx_io_module, "iidx_io_ep1_send"); + _child_iidx_io_ep2_recv = + _load_function(_child_iidx_io_module, "iidx_io_ep2_recv"); + _child_iidx_io_ep2_get_turntable = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_turntable"); + _child_iidx_io_ep2_get_slider = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_slider"); + _child_iidx_io_ep2_get_sys = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_sys"); + _child_iidx_io_ep2_get_panel = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_panel"); + _child_iidx_io_ep2_get_keys = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_keys"); + _child_iidx_io_ep3_write_16seg = + _load_function(_child_iidx_io_module, "iidx_io_ep3_write_16seg"); + + _child_iidx_io_set_loggers( + _log_formatter_misc, + _log_formatter_info, + _log_formatter_warning, + _log_formatter_fatal); + + _child_iidx_io_16seg_mutex = CreateMutex(NULL, FALSE, NULL); + + log_info("Calling child iidx_io_init..."); + + if (!_child_iidx_io_init(thread_create, thread_join, thread_destroy)) { + log_warning("Child iidx_io_init failed"); + FreeLibrary(_child_iidx_io_module); + CloseHandle(_child_iidx_io_16seg_mutex); + + return false; + } + + atomic_store_explicit(&_io_thread_proc_loop, true, memory_order_seq_cst); + + if (!thread_create(_io_thread_proc, NULL, 16384, 0)) { + log_warning("Creating IO thread failed"); + + _child_iidx_io_fini(); + FreeLibrary(_child_iidx_io_module); + CloseHandle(_child_iidx_io_16seg_mutex); + return false; + } + + return true; +} + +void iidx_io_fini(void) +{ + atomic_store_explicit(&_io_thread_proc_loop, false, memory_order_seq_cst); + + log_info("Shutting down IO thread and waiting for it to finish..."); + + while ( + atomic_load_explicit(&_io_thread_proc_running, memory_order_seq_cst)) { + Sleep(1); + } + + log_info("IO thread finished"); + + _child_iidx_io_fini(); + + FreeLibrary(_child_iidx_io_module); + CloseHandle(_child_iidx_io_16seg_mutex); +} + +void iidx_io_ep1_set_deck_lights(uint16_t deck_lights) +{ + atomic_store_explicit( + &_child_iidx_io_deck_lights, deck_lights, memory_order_relaxed); +} + +void iidx_io_ep1_set_panel_lights(uint8_t panel_lights) +{ + atomic_store_explicit( + &_child_iidx_io_panel_lights, panel_lights, memory_order_relaxed); +} + +void iidx_io_ep1_set_top_lamps(uint8_t top_lamps) +{ + atomic_store_explicit( + &_child_iidx_io_top_lamps, top_lamps, memory_order_relaxed); +} + +void iidx_io_ep1_set_top_neons(bool top_neons) +{ + atomic_store_explicit( + &_child_iidx_io_top_neons, top_neons, memory_order_relaxed); +} + +bool iidx_io_ep1_send(void) +{ + // Any sending and receiving is executed async in a separate thread + return true; +} + +bool iidx_io_ep2_recv(void) +{ + // Any sending and receiving is executed async in a separate thread + return true; +} + +uint8_t iidx_io_ep2_get_turntable(uint8_t player_no) +{ + switch (player_no) + { + case 0: + return atomic_load_explicit(&_child_iidx_io_turntable_p1, memory_order_relaxed); + case 1: + return atomic_load_explicit(&_child_iidx_io_turntable_p2, memory_order_relaxed); + default: + return 0; + } +} + +uint8_t iidx_io_ep2_get_slider(uint8_t slider_no) +{ + switch (slider_no) + { + case 0: + return atomic_load_explicit(&_child_iidx_io_slider_1, memory_order_relaxed); + case 1: + return atomic_load_explicit(&_child_iidx_io_slider_2, memory_order_relaxed); + case 2: + return atomic_load_explicit(&_child_iidx_io_slider_3, memory_order_relaxed); + case 3: + return atomic_load_explicit(&_child_iidx_io_slider_4, memory_order_relaxed); + case 4: + return atomic_load_explicit(&_child_iidx_io_slider_5, memory_order_relaxed); + default: + return 0; + } +} + +uint8_t iidx_io_ep2_get_sys(void) +{ + return atomic_load_explicit(&_child_iidx_io_sys, memory_order_relaxed); +} + +uint8_t iidx_io_ep2_get_panel(void) +{ + return atomic_load_explicit(&_child_iidx_io_panel, memory_order_relaxed); +} + +uint16_t iidx_io_ep2_get_keys(void) +{ + return atomic_load_explicit(&_child_iidx_io_keys, memory_order_relaxed); +} + +bool iidx_io_ep3_write_16seg(const char *text) +{ + // This section is only producer while the thread is the only consumer + // Utilize this to optimize the 16seg writing and only write if the text has actually changed + if (!strcmp(text, _child_iidx_16seg)) { + return true; + } + + WaitForSingleObject(_child_iidx_io_16seg_mutex, INFINITE); + memcpy(_child_iidx_16seg, text, sizeof(_child_iidx_16seg)); + _child_iidx_io_16seg_dirty = true; + ReleaseMutex(_child_iidx_io_16seg_mutex); + + return true; +} \ No newline at end of file -- 2.54.0