From 7a56fab96e5639de37700937b3107116b58dae8f Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 08:51:22 +0100 Subject: [PATCH 1/8] 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 2a948a8d..35e690dd 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 \ From ab6c3fc8bc03542a4466154ed398fe74bf233d21 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:07:54 +0100 Subject: [PATCH 2/8] 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 ed67b3b6..067ea75f 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) { From f5b8af3f2acc820597ecccc69d52675119d79cde Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:14:42 +0100 Subject: [PATCH 3/8] 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 00000000..00c51086 --- /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 83289629..9aef0b1b 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) # From 8804e667b34e0ce3efc9ba73819d7c7a0ae5d100 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:14:42 +0100 Subject: [PATCH 4/8] 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 a2d439ec..9dce5beb 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 273e5be8..9632ecb4 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 e2ff5ae5..cc4ee29c 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 10c59e4f..776dc712 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 f83d5a5f..18dad23a 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 f963ec88..f6deed72 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 de171c12..716d0817 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 d1625126..504a303f 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 74332d48..286806bf 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 21f55c1e..93fd07d2 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 7dc4eaa8..f01bc391 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 37f2b3e6..9340dad5 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 6aeaba1d..1a93e7e5 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 fde35d65..a596513a 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 418395f7..960fb947 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 b106ab1b..14f42b7b 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 8cddba8a..3e456b50 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 From e12cd63ba2ec72a3d6ee2cec431df9ff6800d7a7 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:30:47 +0100 Subject: [PATCH 5/8] 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 9dce5beb..ae054e5c 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, From 2e45f095ba1c0892a76a45f8f7b50e4a526e31ed Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:30:53 +0100 Subject: [PATCH 6/8] 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 ab09c014..d31b2e18 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 35988515..88d68c5b 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 From 87b7e53973ed58b4be0a38b097a9090a57f8058f Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:30:53 +0100 Subject: [PATCH 7/8] feat: Add core module This module contains the "core" (API) of bemanitools which includes an abstraction layer for threads and logging at this time. The threads API is very close to what util/thread already was with some structural enhancements which make it easier to understand and work with the API, I hope. Some additional helpers (*-ext module) support in doing common tasks, e.g. setting up the thread API with other modules. The log(ging) part receives a major overhaul to address known limitations and issues with the util/log module: - Cleaner API layer - Separate sinks from actual logging engine - Sinks are composable - Improved and cleaner compatibility layer with AVS logging API Additional "extensions" (*-ext modules) add various helper functions for common tasks like setting up the logging engine with a file and stdout sink. The sinks also improved significantly with the file sink now supporting proper appending and log rotation. Logging to stdout/stderr supports coloring of log messages which works across logging engines. Overall, this refactored foundation is expected to support future developments and removes known limitations at the current scale of bemanitools such as: - Reducing boiler plate code across hooks - Interop of bemanitools and AVS (and setting the foundation for addressing currently missing interop, e.g. for dealing with property structures without AVS) - Addressing performance issues in the logging engine due to incorrect interop with AVS --- Module.mk | 1 + src/main/core/Module.mk | 20 ++++ src/main/core/log-bt-ext.c | 67 +++++++++++ src/main/core/log-bt-ext.h | 59 ++++++++++ src/main/core/log-bt.c | 129 +++++++++++++++++++++ src/main/core/log-bt.h | 87 +++++++++++++++ src/main/core/log-sink-async.c | 23 ++++ src/main/core/log-sink-async.h | 19 ++++ src/main/core/log-sink-debug.c | 23 ++++ src/main/core/log-sink-debug.h | 15 +++ src/main/core/log-sink-file.c | 92 +++++++++++++++ src/main/core/log-sink-file.h | 28 +++++ src/main/core/log-sink-list.c | 66 +++++++++++ src/main/core/log-sink-list.h | 24 ++++ src/main/core/log-sink-mutex.c | 53 +++++++++ src/main/core/log-sink-mutex.h | 21 ++++ src/main/core/log-sink-null.c | 21 ++++ src/main/core/log-sink-null.h | 17 +++ src/main/core/log-sink-std.c | 193 ++++++++++++++++++++++++++++++++ src/main/core/log-sink-std.h | 24 ++++ src/main/core/log-sink.h | 45 ++++++++ src/main/core/log.c | 74 +++++++++++++ src/main/core/log.h | 197 +++++++++++++++++++++++++++++++++ src/main/core/thread-crt-ext.c | 8 ++ src/main/core/thread-crt-ext.h | 9 ++ src/main/core/thread-crt.c | 62 +++++++++++ src/main/core/thread-crt.h | 15 +++ src/main/core/thread.c | 78 +++++++++++++ src/main/core/thread.h | 117 ++++++++++++++++++++ 29 files changed, 1587 insertions(+) create mode 100644 src/main/core/Module.mk create mode 100644 src/main/core/log-bt-ext.c create mode 100644 src/main/core/log-bt-ext.h create mode 100644 src/main/core/log-bt.c create mode 100644 src/main/core/log-bt.h create mode 100644 src/main/core/log-sink-async.c create mode 100644 src/main/core/log-sink-async.h create mode 100644 src/main/core/log-sink-debug.c create mode 100644 src/main/core/log-sink-debug.h create mode 100644 src/main/core/log-sink-file.c create mode 100644 src/main/core/log-sink-file.h create mode 100644 src/main/core/log-sink-list.c create mode 100644 src/main/core/log-sink-list.h create mode 100644 src/main/core/log-sink-mutex.c create mode 100644 src/main/core/log-sink-mutex.h create mode 100644 src/main/core/log-sink-null.c create mode 100644 src/main/core/log-sink-null.h create mode 100644 src/main/core/log-sink-std.c create mode 100644 src/main/core/log-sink-std.h create mode 100644 src/main/core/log-sink.h create mode 100644 src/main/core/log.c create mode 100644 src/main/core/log.h create mode 100644 src/main/core/thread-crt-ext.c create mode 100644 src/main/core/thread-crt-ext.h create mode 100644 src/main/core/thread-crt.c create mode 100644 src/main/core/thread-crt.h create mode 100644 src/main/core/thread.c create mode 100644 src/main/core/thread.h diff --git a/Module.mk b/Module.mk index 35e690dd..41d179a1 100644 --- a/Module.mk +++ b/Module.mk @@ -100,6 +100,7 @@ include src/main/bstio/Module.mk include src/main/camhook/Module.mk include src/main/cconfig/Module.mk include src/main/config/Module.mk +include src/main/core/Module.mk include src/main/d3d9-util/Module.mk include src/main/d3d9exhook/Module.mk include src/main/ddrhook-util/Module.mk diff --git a/src/main/core/Module.mk b/src/main/core/Module.mk new file mode 100644 index 00000000..bd35e6d0 --- /dev/null +++ b/src/main/core/Module.mk @@ -0,0 +1,20 @@ +libs += core + +libs_core := \ + util \ + +src_core := \ + log-bt-ext.c \ + log-bt.c \ + log-sink-async.c \ + log-sink-debug.c \ + log-sink-file.c \ + log-sink-list.c \ + log-sink-mutex.c \ + log-sink-null.c \ + log-sink-std.c \ + log.c \ + thread-crt-ext.c \ + thread-crt.c \ + thread.c \ + diff --git a/src/main/core/log-bt-ext.c b/src/main/core/log-bt-ext.c new file mode 100644 index 00000000..b1aa4f46 --- /dev/null +++ b/src/main/core/log-bt-ext.c @@ -0,0 +1,67 @@ +#include + +#include "core/log-bt.h" +#include "core/log-sink-debug.h" +#include "core/log-sink-file.h" +#include "core/log-sink-list.h" +#include "core/log-sink-mutex.h" +#include "core/log-sink-std.h" +#include "core/log.h" + +void core_log_bt_ext_impl_set() +{ + core_log_impl_set( + core_log_bt_log_misc, + core_log_bt_log_info, + core_log_bt_log_warning, + core_log_bt_log_fatal); +} + +void core_log_bt_ext_init_with_stdout() +{ + struct core_log_sink sink; + + core_log_sink_std_out_open(true, &sink); + core_log_bt_init(&sink); +} + +void core_log_bt_ext_init_with_stderr() +{ + struct core_log_sink sink; + + core_log_sink_std_err_open(true, &sink); + core_log_bt_init(&sink); +} + +void core_log_bt_ext_init_with_debug() +{ + struct core_log_sink sink; + + core_log_sink_debug_open(&sink); + core_log_bt_init(&sink); +} + +void core_log_bt_ext_init_with_file( + const char *path, bool append, bool rotate, uint8_t max_rotations) +{ + struct core_log_sink sink; + + core_log_sink_file_open(path, append, rotate, max_rotations, &sink); + core_log_bt_init(&sink); +} + +void core_log_bt_ext_init_with_stdout_and_file( + const char *path, bool append, bool rotate, uint8_t max_rotations) +{ + struct core_log_sink sinks[2]; + struct core_log_sink sink_composed; + struct core_log_sink sink_mutex; + + core_log_sink_std_out_open(true, &sinks[0]); + core_log_sink_file_open(path, append, rotate, max_rotations, &sinks[1]); + core_log_sink_list_open(sinks, 2, &sink_composed); + + core_log_sink_mutex_open(&sink_composed, &sink_mutex); + + core_log_bt_init(&sink_mutex); +} \ No newline at end of file diff --git a/src/main/core/log-bt-ext.h b/src/main/core/log-bt-ext.h new file mode 100644 index 00000000..dcb9a9a6 --- /dev/null +++ b/src/main/core/log-bt-ext.h @@ -0,0 +1,59 @@ +#ifndef CORE_LOG_BT_EXT_H +#define CORE_LOG_BT_EXT_H + +#include +#include + +/** + * Set the current thread API implementation to use the bemanitools log + * implementation + */ +void core_log_bt_ext_impl_set(); + +/** + * Helper to setup the bemanitools log implementation with a stdout sink. + */ +void core_log_bt_ext_init_with_stdout(); + +/** + * Helper to setup the bemanitools log implementation with a stderr sink. + */ +void core_log_bt_ext_init_with_stderr(); + +/** + * Helper to setup the bemanitools log implementation with a OutputDebugStr + * sink. + */ +void core_log_bt_ext_init_with_debug(); + +/** + * Helper to setup the bemanitools log implementation with a file sink + * + * @param path Path to the log file to write the log output to + * @param append If true, then append to an existing file, false to overwrite + * any existing file + * @param rotate If true, rotates an existing log file and creates a new one + * for this session + * @param max_rotations Max number of rotations for the log files + */ +void core_log_bt_ext_init_with_file( + const char *path, bool append, bool rotate, uint8_t max_rotations); + +/** + * Helper to setup the bemanitools log implementation with a stdout and file + * sink + * + * Important: This combined sink is guarded by a mutex to avoid data races on + * logging to two different sinks. + * + * @param path Path to the log file to write the log output to + * @param append If true, then append to an existing file, false to overwrite + * any existing file + * @param rotate If true, rotates an existing log file and creates a new one + * for this session + * @param max_rotations Max number of rotations for the log files + */ +void core_log_bt_ext_init_with_stdout_and_file( + const char *path, bool append, bool rotate, uint8_t max_rotations); + +#endif \ No newline at end of file diff --git a/src/main/core/log-bt.c b/src/main/core/log-bt.c new file mode 100644 index 00000000..fd868979 --- /dev/null +++ b/src/main/core/log-bt.c @@ -0,0 +1,129 @@ +#include +#include +#include + +#include "core/log-bt.h" +#include "core/log-sink.h" +#include "core/log.h" + +#include "util/mem.h" +#include "util/str.h" + +static enum core_log_bt_log_level _core_log_bt_log_level; +static struct core_log_sink *_core_log_bt_sink; + +static void _core_log_bt_vformat_write( + enum core_log_bt_log_level level, + const char *module, + const char *fmt, + va_list ap) +{ + static const char chars[] = "FFWIM"; + + char timestamp[64]; + /* 64k so we can log data dumps of rs232 without crashing */ + char msg[65536]; + char line[65536]; + int result; + + time_t curtime; + struct tm *tm; + + curtime = 0; + tm = NULL; + + curtime = time(NULL); + tm = localtime(&curtime); + + strftime(timestamp, sizeof(timestamp), "[%Y/%m/%d %H:%M:%S]", tm); + + str_vformat(msg, sizeof(msg), fmt, ap); + + result = str_format( + line, + sizeof(line), + "%s %c:%s: %s\n", + timestamp, + chars[level], + module, + msg); + + _core_log_bt_sink->write(_core_log_bt_sink->ctx, line, result); +} + +void core_log_bt_init(const struct core_log_sink *sink) +{ + if (sink == NULL) { + abort(); + } + + _core_log_bt_sink = xmalloc(sizeof(struct core_log_sink)); + memcpy(_core_log_bt_sink, sink, sizeof(struct core_log_sink)); + + _core_log_bt_log_level = CORE_LOG_BT_LOG_LEVEL_OFF; +} + +void core_log_bt_level_set(enum core_log_bt_log_level level) +{ + _core_log_bt_log_level = level; +} + +void core_log_bt_fini() +{ + log_assert(_core_log_bt_sink); + + _core_log_bt_sink->close(_core_log_bt_sink->ctx); + + free(_core_log_bt_sink); +} + +void core_log_bt_log_fatal(const char *module, const char *fmt, ...) +{ + va_list ap; + + if (_core_log_bt_log_level >= CORE_LOG_BT_LOG_LEVEL_FATAL) { + va_start(ap, fmt); + _core_log_bt_vformat_write( + CORE_LOG_BT_LOG_LEVEL_FATAL, module, fmt, ap); + va_end(ap); + } +} + +void core_log_bt_log_warning(const char *module, const char *fmt, ...) +{ + va_list ap; + + if (_core_log_bt_log_level >= CORE_LOG_BT_LOG_LEVEL_WARNING) { + va_start(ap, fmt); + _core_log_bt_vformat_write( + CORE_LOG_BT_LOG_LEVEL_WARNING, module, fmt, ap); + va_end(ap); + } +} + +void core_log_bt_log_info(const char *module, const char *fmt, ...) +{ + va_list ap; + + if (_core_log_bt_log_level >= CORE_LOG_BT_LOG_LEVEL_INFO) { + va_start(ap, fmt); + _core_log_bt_vformat_write(CORE_LOG_BT_LOG_LEVEL_INFO, module, fmt, ap); + va_end(ap); + } +} + +void core_log_bt_log_misc(const char *module, const char *fmt, ...) +{ + va_list ap; + + if (_core_log_bt_log_level >= CORE_LOG_BT_LOG_LEVEL_MISC) { + va_start(ap, fmt); + _core_log_bt_vformat_write(CORE_LOG_BT_LOG_LEVEL_MISC, module, fmt, ap); + va_end(ap); + } +} + +void core_log_bt_direct_sink_write(const char *chars, size_t nchars) +{ + _core_log_bt_sink->write(_core_log_bt_sink->ctx, chars, nchars); +} \ No newline at end of file diff --git a/src/main/core/log-bt.h b/src/main/core/log-bt.h new file mode 100644 index 00000000..2a8052f3 --- /dev/null +++ b/src/main/core/log-bt.h @@ -0,0 +1,87 @@ +#ifndef CORE_LOG_BT_H +#define CORE_LOG_BT_H + +#include "core/log-sink.h" + +/** + * Log API implementation for games/applications without AVS + */ + +enum core_log_bt_log_level { + CORE_LOG_BT_LOG_LEVEL_OFF = 0, + CORE_LOG_BT_LOG_LEVEL_FATAL = 1, + CORE_LOG_BT_LOG_LEVEL_WARNING = 2, + CORE_LOG_BT_LOG_LEVEL_INFO = 3, + CORE_LOG_BT_LOG_LEVEL_MISC = 4, +}; + +/** + * Initialize the logging backend + * + * This must be called as early as possible in your application to setup + * a logging sink according to your needs. Until this is finished, no + * log output is available. + * + * By default, logging is turned off entirely and must be enabled by setting + * a desired logging level explicitly. + * + * @param sink Pointer to a log sink implementation. The caller owns the memory + * of this. + */ +void core_log_bt_init(const struct core_log_sink *sink); + +/** + * Set the current logging level. This can be changed at any given time, e.g. + * to increase/decrease verbosity. + * + * @param level The logging level to set. + */ +void core_log_bt_level_set(enum core_log_bt_log_level level); + +/** + * Cleanup the logging backend. + * + * Ensure to call this on application exit and cleanup. + */ +void core_log_bt_fini(); + +/** + * Implementation of the log API. + */ +void core_log_bt_log_fatal(const char *module, const char *fmt, ...); + +/** + * Implementation of the log API. + */ +void core_log_bt_log_warning(const char *module, const char *fmt, ...); + +/** + * Implementation of the log API. + */ +void core_log_bt_log_info(const char *module, const char *fmt, ...); + +/** + * Implementation of the log API. + */ +void core_log_bt_log_misc(const char *module, const char *fmt, ...); + +/** + * Allow AVS to by-pass the core log API/engine. + * + * This function must only be called by AVS in an appropriate log callback + * function that is passed to avs_boot. + * + * AVS has it's own logging engine and manages aspects such as async logging, + * log levels and decorating log messages. + * + * Thus, proper interoperability only requires the writer/sink part to be shared + * with AVS. + * + * @param chars Buffer with text data to write to the configured sinks. The + * buffer might contain several log messages separated by newline + * characters. + * @param nchars Number of chars to write to the sink. + */ +void core_log_bt_direct_sink_write(const char *chars, size_t nchars); + +#endif \ No newline at end of file diff --git a/src/main/core/log-sink-async.c b/src/main/core/log-sink-async.c new file mode 100644 index 00000000..f327aad8 --- /dev/null +++ b/src/main/core/log-sink-async.c @@ -0,0 +1,23 @@ +#include + +#include "core/log-sink.h" + +static void +_core_log_sink_file_write(void *ctx, const char *chars, size_t nchars) +{ + // TODO +} + +static void _core_log_sink_file_close(void *ctx) +{ + // TODO +} + +void core_log_sink_async_open(struct core_log_sink *sink) +{ + // TODO + + sink->ctx = NULL; + sink->write = _core_log_sink_file_write; + sink->close = _core_log_sink_file_close; +} \ No newline at end of file diff --git a/src/main/core/log-sink-async.h b/src/main/core/log-sink-async.h new file mode 100644 index 00000000..47aa49e7 --- /dev/null +++ b/src/main/core/log-sink-async.h @@ -0,0 +1,19 @@ +#ifndef CORE_LOG_SINK_ASYNC_H +#define CORE_LOG_SINK_ASYNC_H + +#include +#include + +#include "core/log-sink.h" + +/** + * Open a async log sink + * + * The sink passes data to log to a separate thread which executes the actual + * logging of the data. + * + * @param sink Pointer to allocated memory that receives the opened sink + */ +void core_log_sink_async_open(struct core_log_sink *sink); + +#endif \ No newline at end of file diff --git a/src/main/core/log-sink-debug.c b/src/main/core/log-sink-debug.c new file mode 100644 index 00000000..c1dd7a20 --- /dev/null +++ b/src/main/core/log-sink-debug.c @@ -0,0 +1,23 @@ +#include + +#include + +#include "core/log-sink.h" + +static void +_core_log_sink_debug_write(void *ctx, const char *chars, size_t nchars) +{ + OutputDebugStringA(chars); +} + +static void _core_log_sink_debug_close(void *ctx) +{ + // noop +} + +void core_log_sink_debug_open(struct core_log_sink *sink) +{ + sink->ctx = NULL; + sink->write = _core_log_sink_debug_write; + sink->close = _core_log_sink_debug_close; +} \ No newline at end of file diff --git a/src/main/core/log-sink-debug.h b/src/main/core/log-sink-debug.h new file mode 100644 index 00000000..4c21d50c --- /dev/null +++ b/src/main/core/log-sink-debug.h @@ -0,0 +1,15 @@ +#ifndef CORE_LOG_SINK_DEBUG_H +#define CORE_LOG_SINK_DEBUG_H + +#include + +#include "core/log-sink.h" + +/** + * Open a log sink that uses OutputDebugStr + * + * @param sink Pointer to allocated memory that receives the opened sink + */ +void core_log_sink_debug_open(struct core_log_sink *sink); + +#endif \ No newline at end of file diff --git a/src/main/core/log-sink-file.c b/src/main/core/log-sink-file.c new file mode 100644 index 00000000..f2ca188e --- /dev/null +++ b/src/main/core/log-sink-file.c @@ -0,0 +1,92 @@ +#include + +#include +#include +#include +#include + +#include "core/log-sink.h" + +#include "util/fs.h" +#include "util/str.h" + +static void _core_log_sink_file_rotate(const char *path, uint8_t max_rotations) +{ + uint8_t i; + char rotate_file[MAX_PATH]; + char rotate_file_next[MAX_PATH]; + char version[8]; + char version_next[8]; + + for (i = max_rotations; i > 0; i++) { + str_cpy(rotate_file, sizeof(rotate_file), path); + str_cpy(rotate_file_next, sizeof(rotate_file_next), path); + + if (i - 1 != 0) { + sprintf(version, ".%d", i); + } else { + memset(version, 0, sizeof(version)); + } + + sprintf(version_next, ".%d", i); + + str_cat(rotate_file, sizeof(rotate_file), version); + str_cat(rotate_file_next, sizeof(rotate_file_next), version_next); + + if (path_exists(rotate_file)) { + CopyFile(rotate_file, rotate_file_next, FALSE); + } + } +} + +static void +_core_log_sink_file_write(void *ctx, const char *chars, size_t nchars) +{ + FILE *file; + + file = (FILE *) ctx; + + fwrite(chars, 1, nchars, file); +} + +static void _core_log_sink_file_close(void *ctx) +{ + FILE *file; + + file = (FILE *) ctx; + + fflush(file); + fclose(file); +} + +void core_log_sink_file_open( + const char *path, + bool append, + bool rotate, + uint8_t max_rotations, + struct core_log_sink *sink) +{ + FILE *file; + + if (rotate) { + _core_log_sink_file_rotate(path, max_rotations); + + // Appending doesn't matter when file is rotated anyway + file = fopen(path, "w+"); + } else { + if (append) { + file = fopen(path, "a+"); + } else { + file = fopen(path, "w+"); + } + } + + if (!file) { + printf("Cannot open log file: %s", path); + abort(); + } + + sink->ctx = (void *) file; + sink->write = _core_log_sink_file_write; + sink->close = _core_log_sink_file_close; +} \ No newline at end of file diff --git a/src/main/core/log-sink-file.h b/src/main/core/log-sink-file.h new file mode 100644 index 00000000..968a1fed --- /dev/null +++ b/src/main/core/log-sink-file.h @@ -0,0 +1,28 @@ +#ifndef CORE_LOG_SINK_FILE_H +#define CORE_LOG_SINK_FILE_H + +#include +#include +#include + +#include "core/log-sink.h" + +/** + * Open a log sink writing data to a file + * + * @param path Path to the log file to write the log output to + * @param append If true, then append to an existing file, false to overwrite + * any existing file + * @param rotate If true, rotates an existing log file and creates a new one + * for this session + * @param max_rotations Max number of rotations for the log files + * @param sink Pointer to allocated memory that receives the opened sink + */ +void core_log_sink_file_open( + const char *path, + bool append, + bool rotate, + uint8_t max_rotations, + struct core_log_sink *sink); + +#endif \ No newline at end of file diff --git a/src/main/core/log-sink-list.c b/src/main/core/log-sink-list.c new file mode 100644 index 00000000..38e32112 --- /dev/null +++ b/src/main/core/log-sink-list.c @@ -0,0 +1,66 @@ +#include +#include + +#include "core/log-sink-list.h" +#include "core/log-sink.h" + +#include "util/mem.h" + +#define MAX_SINKS 8 + +struct core_log_sink_list { + struct core_log_sink entries[MAX_SINKS]; + uint8_t num; +}; + +static void +_core_log_sink_list_write(void *ctx, const char *chars, size_t nchars) +{ + struct core_log_sink_list *sink_list; + int i; + + sink_list = (struct core_log_sink_list *) ctx; + + for (i = 0; i < sink_list->num; i++) { + sink_list->entries[i].write(sink_list->entries[i].ctx, chars, nchars); + } +} + +static void _core_log_sink_list_close(void *ctx) +{ + struct core_log_sink_list *sink_list; + int i; + + sink_list = (struct core_log_sink_list *) ctx; + + for (i = 0; i < sink_list->num; i++) { + sink_list->entries[i].close(sink_list->entries[i].ctx); + } + + free(sink_list); +} + +void core_log_sink_list_open( + const struct core_log_sink *entry, uint8_t num, struct core_log_sink *sink) +{ + struct core_log_sink_list *sink_list; + int i; + + if (num > MAX_SINKS) { + abort(); + } + + sink_list = xmalloc(sizeof(struct core_log_sink_list)); + + for (i = 0; i < num; i++) { + sink_list->entries[i].ctx = entry[i].ctx; + sink_list->entries[i].write = entry[i].write; + sink_list->entries[i].close = entry[i].close; + } + + sink_list->num = num; + + sink->ctx = (void *) sink_list; + sink->write = _core_log_sink_list_write; + sink->close = _core_log_sink_list_close; +} \ No newline at end of file diff --git a/src/main/core/log-sink-list.h b/src/main/core/log-sink-list.h new file mode 100644 index 00000000..f1e99f35 --- /dev/null +++ b/src/main/core/log-sink-list.h @@ -0,0 +1,24 @@ +#ifndef CORE_LOG_SINK_LIST_H +#define CORE_LOG_SINK_LIST_H + +#include +#include + +#include "core/log-sink.h" + +/** + * Combine multiple log sinks into a list of sinks. + * + * Upon invoking a list sink, all sinks contained within the list are + * being invoked in the configured order. + * + * @param entry A pointer to allocated memory with a sequence of opened sinks + * that you want to add to the list. Ownership of these sinks + * is transferred, i.e. closing the list sink closes its children. + * @param num The number of elements in the sequence of opened sinks pointed to. + * @param sink Pointer to allocated memory that receives the opened sink + */ +void core_log_sink_list_open( + const struct core_log_sink *entry, uint8_t num, struct core_log_sink *sink); + +#endif \ No newline at end of file diff --git a/src/main/core/log-sink-mutex.c b/src/main/core/log-sink-mutex.c new file mode 100644 index 00000000..13c8b207 --- /dev/null +++ b/src/main/core/log-sink-mutex.c @@ -0,0 +1,53 @@ +#include + +#include + +#include "core/log-sink.h" + +#include "util/mem.h" + +struct core_log_sink_mutex_ctx { + struct core_log_sink *child; + HANDLE mutex; +}; + +static void +_core_log_sink_mutex_write(void *ctx_, const char *chars, size_t nchars) +{ + struct core_log_sink_mutex_ctx *ctx; + + ctx = (struct core_log_sink_mutex_ctx *) ctx_; + + WaitForSingleObject(ctx->mutex, INFINITE); + + ctx->child->write(ctx->child->ctx, chars, nchars); + + ReleaseMutex(ctx->mutex); +} + +static void _core_log_sink_mutex_close(void *ctx_) +{ + struct core_log_sink_mutex_ctx *ctx; + + ctx = (struct core_log_sink_mutex_ctx *) ctx_; + + CloseHandle(ctx->mutex); + + ctx->child->close(ctx->child->ctx); + free(ctx); +} + +void core_log_sink_mutex_open( + const struct core_log_sink *child_sink, struct core_log_sink *sink) +{ + struct core_log_sink_mutex_ctx *ctx; + + ctx = xmalloc(sizeof(struct core_log_sink_mutex_ctx)); + + memcpy(ctx->child, child_sink, sizeof(struct core_log_sink)); + ctx->mutex = CreateMutex(NULL, FALSE, NULL); + + sink->ctx = ctx; + sink->write = _core_log_sink_mutex_write; + sink->close = _core_log_sink_mutex_close; +} \ No newline at end of file diff --git a/src/main/core/log-sink-mutex.h b/src/main/core/log-sink-mutex.h new file mode 100644 index 00000000..e30357e4 --- /dev/null +++ b/src/main/core/log-sink-mutex.h @@ -0,0 +1,21 @@ +#ifndef CORE_LOG_SINK_MUTEX_H +#define CORE_LOG_SINK_MUTEX_H + +#include + +#include "core/log-sink.h" + +/** + * Create a sink that surrounds another sink with a mutex. + * + * Use this to make other sink implementations thread-safe. + * + * @param child_sink Another opened sink to surround with the mutex. Ownership + * of the sink is transferred, i.e. closing the mutex sink + * also closes the wrapped child sink. + * @param sink Pointer to allocated memory that receives the opened sink + */ +void core_log_sink_mutex_open( + const struct core_log_sink *child_sink, struct core_log_sink *sink); + +#endif \ No newline at end of file diff --git a/src/main/core/log-sink-null.c b/src/main/core/log-sink-null.c new file mode 100644 index 00000000..7655a208 --- /dev/null +++ b/src/main/core/log-sink-null.c @@ -0,0 +1,21 @@ +#include + +#include "core/log-sink.h" + +static void +_core_log_sink_null_write(void *ctx, const char *chars, size_t nchars) +{ + // noop +} + +static void _core_log_sink_null_close(void *ctx) +{ + // noop +} + +void core_log_sink_null_open(struct core_log_sink *sink) +{ + sink->ctx = NULL; + sink->write = _core_log_sink_null_write; + sink->close = _core_log_sink_null_close; +} \ No newline at end of file diff --git a/src/main/core/log-sink-null.h b/src/main/core/log-sink-null.h new file mode 100644 index 00000000..ebd2babe --- /dev/null +++ b/src/main/core/log-sink-null.h @@ -0,0 +1,17 @@ +#ifndef CORE_LOG_SINK_NULL_H +#define CORE_LOG_SINK_NULL_H + +#include + +#include "core/log-sink.h" + +/** + * Create a null/dummy sink. + * + * Use this to disable any logging entirely. + * + * @param sink Pointer to allocated memory that receives the opened sink + */ +void core_log_sink_null_open(struct core_log_sink *sink); + +#endif \ No newline at end of file diff --git a/src/main/core/log-sink-std.c b/src/main/core/log-sink-std.c new file mode 100644 index 00000000..197fd12a --- /dev/null +++ b/src/main/core/log-sink-std.c @@ -0,0 +1,193 @@ +#include + +#include + +#include "core/log-sink.h" + +#include "util/mem.h" + +struct core_log_sink_std_ctx { + HANDLE handle; + bool color; +}; + +static char _core_log_sink_std_determine_color(const char *str) +{ + /* Add some color to make spotting warnings/errors easier. + Based on debug output level identifier. */ + + /* Avoids colored output on strings like "Windows" */ + if (str[1] != ':') { + return 15; + } + + switch (str[0]) { + /* green */ + case 'M': + return 10; + /* blue */ + case 'I': + return 9; + /* yellow */ + case 'W': + return 14; + /* red */ + case 'F': + return 12; + /* default console color */ + default: + return 15; + } +} + +static size_t _core_log_sink_std_msg_coloring_len(const char *str) +{ + // Expected format example: "I:boot: my log message" + + const char *ptr; + size_t len; + int colon_count; + + ptr = str; + len = 0; + colon_count = 0; + + while (true) { + // End of string = invalid log format + if (*ptr == '\0') { + return 0; + } + + if (*ptr == ':') { + colon_count++; + } + + if (colon_count == 2) { + // Skip current colon, next char is a space + return len + 1; + } + + len++; + ptr++; + } + + return 0; +} + +static void +_core_log_sink_std_write(void *ctx_, const char *chars, size_t nchars) +{ + static const size_t timestamp_len = strlen("[----/--/-- --:--:--]"); + + struct core_log_sink_std_ctx *ctx; + + char color; + size_t color_len; + size_t msg_len; + const char *msg_start; + const char *msg_end; + DWORD written; + DWORD write_pos; + + ctx = (struct core_log_sink_std_ctx *) ctx_; + + if (ctx->color) { + write_pos = 0; + + // Support multiple buffered log messages, e.g. from the AVS logging + // engine + while (write_pos < nchars) { + // Expects the AVS timestamp format + msg_start = chars + timestamp_len + 1; // +1 is the space + + color_len = _core_log_sink_std_msg_coloring_len(msg_start); + + // Check if we could detect which part to color, otherwise just + // write the whole log message without any coloring logic + if (color_len > 0) { + color = _core_log_sink_std_determine_color(msg_start); + + // Timestamp + WriteConsole( + ctx->handle, chars, timestamp_len + 1, &written, NULL); + write_pos += written; + chars += written; + + // Log level + module colored + SetConsoleTextAttribute(ctx->handle, color); + WriteConsole(ctx->handle, chars, color_len, &written, NULL); + write_pos += written; + chars += written; + SetConsoleTextAttribute(ctx->handle, 15); + + msg_end = strchr(chars, '\n'); + + if (msg_end != NULL) { + msg_len = msg_end - chars; + + // Write \n as well + msg_len++; + + // Write actual message non colored + WriteConsole(ctx->handle, chars, msg_len, &written, NULL); + write_pos += written; + chars += written; + } else { + WriteConsole( + ctx->handle, chars, nchars - write_pos, &written, NULL); + write_pos += written; + chars += written; + } + } else { + WriteConsole( + ctx->handle, + chars + write_pos, + nchars - write_pos, + &written, + NULL); + write_pos += written; + } + } + } else { + WriteConsole(ctx->handle, chars, nchars, &written, NULL); + } +} + +static void _core_log_sink_std_close(void *ctx_) +{ + struct core_log_sink_std_ctx *ctx; + + ctx = (struct core_log_sink_std_ctx *) ctx_; + + // Remark: Don't close the ctx->handle, see win API docs + + free(ctx); +} + +void core_log_sink_std_out_open(bool color, struct core_log_sink *sink) +{ + struct core_log_sink_std_ctx *ctx; + + ctx = xmalloc(sizeof(struct core_log_sink_std_ctx)); + + ctx->handle = GetStdHandle(STD_OUTPUT_HANDLE); + ctx->color = color; + + sink->ctx = (void *) ctx; + sink->write = _core_log_sink_std_write; + sink->close = _core_log_sink_std_close; +} + +void core_log_sink_std_err_open(bool color, struct core_log_sink *sink) +{ + struct core_log_sink_std_ctx *ctx; + + ctx = xmalloc(sizeof(struct core_log_sink_std_ctx)); + + ctx->handle = GetStdHandle(STD_ERROR_HANDLE); + ctx->color = color; + + sink->ctx = (void *) ctx; + sink->write = _core_log_sink_std_write; + sink->close = _core_log_sink_std_close; +} \ No newline at end of file diff --git a/src/main/core/log-sink-std.h b/src/main/core/log-sink-std.h new file mode 100644 index 00000000..e0e006bf --- /dev/null +++ b/src/main/core/log-sink-std.h @@ -0,0 +1,24 @@ +#ifndef CORE_LOG_SINK_STD_H +#define CORE_LOG_SINK_STD_H + +#include + +#include "core/log-sink.h" + +/** + * Create a sink that writes to stdout. + * + * @param color If true, messages are colored by log level. + * @param sink Pointer to allocated memory that receives the opened sink + */ +void core_log_sink_std_out_open(bool color, struct core_log_sink *sink); + +/** + * Create a sink that writes to stderr. + * + * @param color If true, messages are colored by log level. + * @param sink Pointer to allocated memory that receives the opened sink + */ +void core_log_sink_std_err_open(bool color, struct core_log_sink *sink); + +#endif \ No newline at end of file diff --git a/src/main/core/log-sink.h b/src/main/core/log-sink.h new file mode 100644 index 00000000..7fdd42fb --- /dev/null +++ b/src/main/core/log-sink.h @@ -0,0 +1,45 @@ +#ifndef CORE_LOG_SINK_H +#define CORE_LOG_SINK_H + +#include + +/** + * Write function for a log sink implementation. + * + * Write the given data to your target output destination. + * + * @param ctx Context defined by the implementation when opening the sink. + * @param chars Buffer with text data to log. This can contain partial data of + * a single log line, a full log line terminated by a newline + * character or multiple log lines (each terminated by a newline + * character). + * @param nchars Number of characters to write. + */ +typedef void (*core_log_sink_write_t)( + void *ctx, const char *chars, size_t nchars); + +/** + * Close your log sink and cleanup resources + * + * Depending on your implementation, you might want to flush any + * outstanding/buffered data. + * + * @param ctx Context defined by the implementation when opening the sink. + */ +typedef void (*core_log_sink_close_t)(void *ctx); + +/** + * Log sink structure. + * + * This must be set-up and populated when opening your log sink implementation. + * The ctx field contains any arbitrary data that you need for your log sink + * to operate, e.g. a file handle, additional buffers etc. Make sure these + * resources are cleaned up upon closing the sink. + */ +struct core_log_sink { + void *ctx; + core_log_sink_write_t write; + core_log_sink_close_t close; +}; + +#endif \ No newline at end of file diff --git a/src/main/core/log.c b/src/main/core/log.c new file mode 100644 index 00000000..9d2d6d76 --- /dev/null +++ b/src/main/core/log.c @@ -0,0 +1,74 @@ +#include + +#include "core/log.h" + +core_log_message_t _core_log_misc_impl; +core_log_message_t _core_log_info_impl; +core_log_message_t _core_log_warning_impl; +core_log_message_t _core_log_fatal_impl; + +void core_log_impl_set( + core_log_message_t misc, + core_log_message_t info, + core_log_message_t warning, + core_log_message_t fatal) +{ + if (misc == NULL || info == NULL || warning == NULL || fatal == NULL) { + abort(); + } + + _core_log_misc_impl = misc; + _core_log_info_impl = info; + _core_log_warning_impl = warning; + _core_log_fatal_impl = fatal; +} + +void core_log_impl_assign(core_log_impl_set_t impl_set) +{ + if (_core_log_misc_impl == NULL || _core_log_info_impl == NULL || + _core_log_warning_impl == NULL || _core_log_fatal_impl == NULL) { + abort(); + } + + impl_set( + _core_log_misc_impl, + _core_log_info_impl, + _core_log_warning_impl, + _core_log_fatal_impl); +} + +core_log_message_t core_log_misc_impl_get() +{ + if (_core_log_misc_impl == NULL) { + abort(); + } + + return _core_log_misc_impl; +} + +core_log_message_t core_log_info_impl_get() +{ + if (_core_log_info_impl == NULL) { + abort(); + } + + return _core_log_info_impl; +} + +core_log_message_t core_log_warning_impl_get() +{ + if (_core_log_warning_impl == NULL) { + abort(); + } + + return _core_log_warning_impl; +} + +core_log_message_t core_log_fatal_impl_get() +{ + if (_core_log_fatal_impl == NULL) { + abort(); + } + + return _core_log_fatal_impl; +} \ No newline at end of file diff --git a/src/main/core/log.h b/src/main/core/log.h new file mode 100644 index 00000000..5b5cd9da --- /dev/null +++ b/src/main/core/log.h @@ -0,0 +1,197 @@ +#ifndef CORE_LOG_H +#define CORE_LOG_H + +#include +#include + +#include "util/defs.h" + +/** + * The core log API of bemanitools. + * + * To a large extent, this reflects the AVS logging API and allows for swapping + * out the backends with different implementations. Most games should have some + * version of the AVS API available while some (legacy) games do not. These + * can use a bemanitools private logging implementation by configuring it + * in the bootstrapping process. + */ + +/* BUILD_MODULE is passed in as a command-line #define by the makefile */ + +#ifndef LOG_MODULE +#define LOG_MODULE STRINGIFY(BUILD_MODULE) +#endif + +/** + * Log a message on misc level + * + * Always use this interface in your application which hides the currently + * configured implementation. + * + * The macro is required to make things work with varargs. + * The log message is only printed if the log level is set to misc + * + * @param fmt printf format string + * @param ... Additional arguments according to the specified arguments in the + * printf format string + */ +#define log_misc(...) _core_log_misc_impl(LOG_MODULE, __VA_ARGS__) + +/** + * Log a message on info level + * + * Always use this interface in your application which hides the currently + * configured implementation. + * + * The macro is required to make things work with varargs. + * The log message is only printed if the log level is set to info or lower + * + * @param fmt printf format string + * @param ... Additional arguments according to the specified arguments in the + * printf format string + */ +#define log_info(...) _core_log_info_impl(LOG_MODULE, __VA_ARGS__) + +/** + * Log a message on warning level + * + * Always use this interface in your application which hides the currently + * configured implementation. + * + * The macro is required to make things work with varargs. + * The log message is only printed if the log level is set to warning or lower + * + * @param fmt printf format string + * @param ... Additional arguments according to the specified arguments in the + * printf format string + */ +#define log_warning(...) _core_log_warning_impl(LOG_MODULE, __VA_ARGS__) + +/** + * Log a message on fatal level + * + * Always use this interface in your application which hides the currently + * configured implementation. + * + * The macro is required to make things work with varargs. + * The log message is only printed if the log level is set to fatal. + * + * This call will also terminate the application. + * + * @param fmt printf format string + * @param ... Additional arguments according to the specified arguments in the + * printf format string + */ +#define log_fatal(...) \ + do { \ + _core_log_fatal_impl(LOG_MODULE, __VA_ARGS__); \ + abort(); \ + } while (0) + +/** + * Log a message and terminate the application if given condition fails + * + * Always use this interface in your application which hides the currently + * configured implementation. + * + * The macro is required to make things work with varargs. + * + * @param x Condition to evaluate. If false, the application terminates + */ +#define log_assert(x) \ + do { \ + if (!(x)) { \ + _core_log_fatal_impl( \ + "assert", \ + "%s:%d: function `%s'", \ + __FILE__, \ + __LINE__, \ + __FUNCTION__); \ + abort(); \ + } \ + } while (0) + +/** + * Log a message in an exception handler + * + * Only use this function in an exception handler, e.g. for stack traces. It + * logs the message on fatal level but does not terminate. + * + * @param fmt printf format string + * @param ... Additional arguments according to the specified arguments in the + * printf format string + */ +#define log_exception_handler(...) \ + _core_log_fatal_impl("exception", __VA_ARGS__) + +typedef void (*core_log_message_t)(const char *module, const char *fmt, ...); + +typedef void (*core_log_impl_set_t)( + core_log_message_t misc, + core_log_message_t info, + core_log_message_t warning, + core_log_message_t fatal); + +/** + * Configure the log API implementations + * + * Advised to do this as early in your application/library module as possible + * as calls to the getter functions below will return the currently configured + * implementations. + * + * @param misc Pointer to a function implementing logging on misc level + * @param info Pointer to a function implementing logging on info level + * @param warning Pointer to a function implementing logging on warning level + * @param fatal Pointer to a function implementing logging on fatal level + */ +void core_log_impl_set( + core_log_message_t misc, + core_log_message_t info, + core_log_message_t warning, + core_log_message_t fatal); + +/** + * Supporting function to inject/assign the currently set implementation + * with the given setter function. + * + * @param impl_set Setter function to call with the currently configured log + * function implementations + */ +void core_log_impl_assign(core_log_impl_set_t impl_set); + +/** + * Get the currently configured implementation of the misc level log function + * + * @return Pointer to the currently configured implementation of the function + */ +core_log_message_t core_log_misc_impl_get(); + +/** + * Get the currently configured implementation of the info level log function + * + * @return Pointer to the currently configured implementation of the function + */ +core_log_message_t core_log_info_impl_get(); + +/** + * Get the currently configured implementation of the warning level log function + * + * @return Pointer to the currently configured implementation of the function + */ +core_log_message_t core_log_warning_impl_get(); + +/** + * Get the currently configured implementation of the fatal level log function + * + * @return Pointer to the currently configured implementation of the function + */ +core_log_message_t core_log_fatal_impl_get(); + +// Do not use these directly. +// These are only here to allow usage in the macros above. +extern core_log_message_t _core_log_misc_impl; +extern core_log_message_t _core_log_info_impl; +extern core_log_message_t _core_log_warning_impl; +extern core_log_message_t _core_log_fatal_impl; + +#endif \ No newline at end of file diff --git a/src/main/core/thread-crt-ext.c b/src/main/core/thread-crt-ext.c new file mode 100644 index 00000000..eb403ade --- /dev/null +++ b/src/main/core/thread-crt-ext.c @@ -0,0 +1,8 @@ +#include "core/thread-crt.h" +#include "core/thread.h" + +void core_thread_crt_ext_impl_set() +{ + core_thread_impl_set( + core_thread_crt_create, core_thread_crt_join, core_thread_crt_destroy); +} \ No newline at end of file diff --git a/src/main/core/thread-crt-ext.h b/src/main/core/thread-crt-ext.h new file mode 100644 index 00000000..a17b29b6 --- /dev/null +++ b/src/main/core/thread-crt-ext.h @@ -0,0 +1,9 @@ +#ifndef CORE_THREAD_CRT_EXT_H +#define CORE_THREAD_CRT_EXT_H + +/** + * Set the current thread API implementation to use the C runtime thread API + */ +void core_thread_crt_ext_impl_set(); + +#endif \ No newline at end of file diff --git a/src/main/core/thread-crt.c b/src/main/core/thread-crt.c new file mode 100644 index 00000000..0251cf12 --- /dev/null +++ b/src/main/core/thread-crt.c @@ -0,0 +1,62 @@ +#include +#include + +#include +#include + +#include "core/thread-crt.h" +#include "core/thread.h" + +#include "util/defs.h" + +struct shim_ctx { + HANDLE barrier; + int (*proc)(void *); + void *ctx; +}; + +static unsigned int STDCALL crt_thread_shim(void *outer_ctx) +{ + struct shim_ctx *sctx = outer_ctx; + int (*proc)(void *); + void *inner_ctx; + + proc = sctx->proc; + inner_ctx = sctx->ctx; + + SetEvent(sctx->barrier); + + return proc(inner_ctx); +} + +int core_thread_crt_create( + int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority) +{ + struct shim_ctx sctx; + uintptr_t thread_id; + + sctx.barrier = CreateEvent(NULL, TRUE, FALSE, NULL); + sctx.proc = proc; + sctx.ctx = ctx; + + thread_id = _beginthreadex(NULL, stack_sz, crt_thread_shim, &sctx, 0, NULL); + + WaitForSingleObject(sctx.barrier, INFINITE); + CloseHandle(sctx.barrier); + + return (int) thread_id; +} + +void core_thread_crt_destroy(int thread_id) +{ + CloseHandle((HANDLE) (uintptr_t) thread_id); +} + +void core_thread_crt_join(int thread_id, int *result) +{ + WaitForSingleObject((HANDLE) (uintptr_t) thread_id, INFINITE); + + if (result) { + GetExitCodeThread((HANDLE) (uintptr_t) thread_id, (DWORD *) result); + } +} diff --git a/src/main/core/thread-crt.h b/src/main/core/thread-crt.h new file mode 100644 index 00000000..e9c1e8c0 --- /dev/null +++ b/src/main/core/thread-crt.h @@ -0,0 +1,15 @@ +#ifndef CORE_THREAD_CRT_H +#define CORE_THREAD_CRT_H + +#include + +/** + * Thread API implementation using the C runtime API + */ + +int core_thread_crt_create( + int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority); +void core_thread_crt_join(int thread_id, int *result); +void core_thread_crt_destroy(int thread_id); + +#endif diff --git a/src/main/core/thread.c b/src/main/core/thread.c new file mode 100644 index 00000000..f4e5bfeb --- /dev/null +++ b/src/main/core/thread.c @@ -0,0 +1,78 @@ +#include + +#include "core/log.h" +#include "core/thread.h" + +core_thread_create_t core_thread_create_impl; +core_thread_join_t core_thread_join_impl; +core_thread_destroy_t core_thread_destroy_impl; + +int core_thread_create( + int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority) +{ + log_assert(core_thread_create_impl); + + return core_thread_create_impl(proc, ctx, stack_sz, priority); +} + +void core_thread_join(int thread_id, int *result) +{ + log_assert(core_thread_join_impl); + + core_thread_join_impl(thread_id, result); +} + +void core_thread_destroy(int thread_id) +{ + log_assert(core_thread_destroy_impl); + + core_thread_destroy_impl(thread_id); +} + +void core_thread_impl_set( + core_thread_create_t create, + core_thread_join_t join, + core_thread_destroy_t destroy) +{ + if (create == NULL || join == NULL || destroy == NULL) { + abort(); + } + + core_thread_create_impl = create; + core_thread_join_impl = join; + core_thread_destroy_impl = destroy; +} + +void core_thread_impl_assign(core_thread_impl_set_t impl_set) +{ + if (core_thread_create_impl == NULL || core_thread_join_impl == NULL || + core_thread_destroy_impl == NULL) { + abort(); + } + + impl_set( + core_thread_create_impl, + core_thread_join_impl, + core_thread_destroy_impl); +} + +core_thread_create_t core_thread_create_impl_get() +{ + log_assert(core_thread_create_impl); + + return core_thread_create_impl; +} + +core_thread_join_t core_thread_join_impl_get() +{ + log_assert(core_thread_join_impl); + + return core_thread_join_impl; +} + +core_thread_destroy_t core_thread_destroy_impl_get() +{ + log_assert(core_thread_destroy_impl); + + return core_thread_destroy_impl; +} diff --git a/src/main/core/thread.h b/src/main/core/thread.h new file mode 100644 index 00000000..88115665 --- /dev/null +++ b/src/main/core/thread.h @@ -0,0 +1,117 @@ +#ifndef CORE_THREAD_H +#define CORE_THREAD_H + +#include + +/** + * The core thread API of bemanitools. + * + * This essentially reflects the AVS threading API and allows for swapping out + * the backends with different implementations. Most games should have some + * version of the AVS API available while some (legacy) games do not. These + * can use a bemanitools private threading implementation by configuring it + * in the bootstrapping process. + */ + +/** + * Create a thread + * + * Always use this interface in your application which hides the currently + * configured implementation. + * + * @param proc The function to run in a separate thread + * @param ctx Additional data to pass to the function as a parameter + * @param stack_sz The stack size to allocate for the thread in bytes + * @param priority The thread's priority + * @return The ID of the thread once created and started + */ +int core_thread_create( + int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority); + +/** + * Wait for a thread to finish + * + * Always use this interface in your application which hides the currently + * configured implementation. + * + * The caller of this function blocks until the thread has finished executing. + * + * @param thread_id ID of the thread to wait for + * @param result Pointer to a variable to write the return value of the function + * the thread executed to + */ +void core_thread_join(int thread_id, int *result); + +/** + * Destroy a thread + * + * Always use this interface in your application which hides the currently + * configured implementation. + * + * The thread must have finished execution before calling this. It is advised + * to make threads terminate their execution flow, join them and destroy. + * + * @param thread_id The ID of the thread to destroy. + */ +void core_thread_destroy(int thread_id); + +typedef int (*core_thread_create_t)( + int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority); +typedef void (*core_thread_join_t)(int thread_id, int *result); +typedef void (*core_thread_destroy_t)(int thread_id); + +typedef void (*core_thread_impl_set_t)( + core_thread_create_t create, + core_thread_join_t join, + core_thread_destroy_t destroy); + +/** + * Configure the thread API implementations + * + * Advised to do this as early in your application/library module as possible + * as calls to the getter functions below will return the currently configured + * implementations. + * + * @param create Pointer to a function implementing thread creation + * @param join Pointer to a function implementing joining of a thread + * @param destroy Pointer to a function implementing destroying of a thread + */ +void core_thread_impl_set( + core_thread_create_t create, + core_thread_join_t join, + core_thread_destroy_t destroy); + +/** + * Supporting function to inject/assign the currently set implementation + * with the given setter function. + * + * @param impl_set Setter function to call with the currently configured thread + * function implementations + */ +void core_thread_impl_assign(core_thread_impl_set_t impl_set); + +/** + * Get the currently configured implementation for thread_create + * + * @return Pointer to the currently configured implementation of the + * thread_create function + */ +core_thread_create_t core_thread_create_impl_get(); + +/** + * Get the currently configured implementation for thread_join + * + * @return Pointer to the currently configured implementation of the thread_join + * function + */ +core_thread_join_t core_thread_join_impl_get(); + +/** + * Get the currently configured implementation for thread_destroy + * + * @return Pointer to the currently configured implementation of the + * thread_destroy function + */ +core_thread_destroy_t core_thread_destroy_impl_get(); + +#endif From fb1442b7342b3cef0f906eac3fef21c849213fab Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 25 Feb 2024 09:30:53 +0100 Subject: [PATCH 8/8] feat: Add helper to set avs implementations Doesn't really reduce boiler plate but adds clarity with a more meaningful function name what the operation does. --- src/main/avs-util/Module.mk | 1 + src/main/avs-util/core-interop.c | 16 ++++++++++++++++ src/main/avs-util/core-interop.h | 7 +++++++ 3 files changed, 24 insertions(+) create mode 100644 src/main/avs-util/core-interop.c create mode 100644 src/main/avs-util/core-interop.h diff --git a/src/main/avs-util/Module.mk b/src/main/avs-util/Module.mk index 8db06918..2e59f579 100644 --- a/src/main/avs-util/Module.mk +++ b/src/main/avs-util/Module.mk @@ -3,4 +3,5 @@ libs += avs-util libs_avs-util := \ src_avs-util := \ + core-interop.c \ error.c \ diff --git a/src/main/avs-util/core-interop.c b/src/main/avs-util/core-interop.c new file mode 100644 index 00000000..555930f1 --- /dev/null +++ b/src/main/avs-util/core-interop.c @@ -0,0 +1,16 @@ +#include "core/log.h" +#include "core/thread.h" + +#include "imports/avs.h" + +void avs_util_core_interop_log_avs_impl_set() +{ + core_log_impl_set( + log_body_misc, log_body_info, log_body_warning, log_body_fatal); +} + +void avs_util_core_interop_thread_avs_impl_set() +{ + core_thread_impl_set( + avs_thread_create, avs_thread_join, avs_thread_destroy); +} \ No newline at end of file diff --git a/src/main/avs-util/core-interop.h b/src/main/avs-util/core-interop.h new file mode 100644 index 00000000..51b1e119 --- /dev/null +++ b/src/main/avs-util/core-interop.h @@ -0,0 +1,7 @@ +#ifndef AVS_UTIL_CORE_INTEROP_H +#define AVS_UTIL_CORE_INTEROP_H + +void avs_util_core_interop_log_avs_impl_set(); +void avs_util_core_interop_thread_avs_impl_set(); + +#endif \ No newline at end of file