mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-09-15 11:55:10 -05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb1442b734 | ||
|
|
87b7e53973 | ||
|
|
2e45f095ba | ||
|
|
e12cd63ba2 | ||
|
|
8804e667b3 | ||
|
|
f5b8af3f2a | ||
|
|
ab6c3fc8bc | ||
|
|
7a56fab96e | ||
|
|
ce1a004bf6 | ||
|
|
e72e38eda2 |
21
Dockerfile.dev
Normal file
21
Dockerfile.dev
Normal file
@@ -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
|
||||
38
GNUmakefile
38
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)
|
||||
|
||||
#
|
||||
|
||||
@@ -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
|
||||
@@ -715,6 +716,8 @@ $(zipdir)/ddr-14-to-18.zip: \
|
||||
dist/ddr/gamestart-14.bat \
|
||||
dist/ddr/gamestart-15.bat \
|
||||
dist/ddr/gamestart-16.bat \
|
||||
dist/ddr/gamestart-17.bat \
|
||||
dist/ddr/gamestart-18.bat \
|
||||
| $(zipdir)/
|
||||
$(V)echo ... $@
|
||||
$(V)zip -j $@ $^
|
||||
@@ -731,6 +734,8 @@ $(zipdir)/ddr-16-to-18-x64.zip: \
|
||||
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 \
|
||||
| $(zipdir)/
|
||||
$(V)echo ... $@
|
||||
$(V)zip -j $@ $^
|
||||
|
||||
15
dist/ddr/gamestart-17.bat
vendored
Normal file
15
dist/ddr/gamestart-17.bat
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
@echo off
|
||||
|
||||
cd /d %~dp0
|
||||
|
||||
if not exist conf\nvram mkdir conf\nvram
|
||||
if not exist conf\nvram\ea3-config.xml copy prop\eamuse-config.xml conf\nvram\ea3-config.xml
|
||||
if not exist conf\nvram\coin.xml copy prop\coin.xml conf\nvram\coin.xml
|
||||
if not exist conf\nvram\eacoin.xml copy prop\eacoin.xml conf\nvram\eacoin.xml
|
||||
if not exist conf\nvram\testmode-v.xml copy prop\testmode-v.xml conf\nvram\testmode-v.xml
|
||||
if not exist conf\raw mkdir conf\raw
|
||||
|
||||
regsvr32 /s com\k-clvsd.dll
|
||||
regsvr32 /s com\xactengine2_10.dll
|
||||
|
||||
.\launcher.exe -H 33554432 -K .\ddrhook2.dll .\arkmdxp3.dll %*
|
||||
15
dist/ddr/gamestart-18.bat
vendored
Normal file
15
dist/ddr/gamestart-18.bat
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
@echo off
|
||||
|
||||
cd /d %~dp0
|
||||
|
||||
if not exist conf\nvram mkdir conf\nvram
|
||||
if not exist conf\nvram\ea3-config.xml copy prop\eamuse-config.xml conf\nvram\ea3-config.xml
|
||||
if not exist conf\nvram\coin.xml copy prop\coin.xml conf\nvram\coin.xml
|
||||
if not exist conf\nvram\eacoin.xml copy prop\eacoin.xml conf\nvram\eacoin.xml
|
||||
if not exist conf\nvram\testmode-v.xml copy prop\testmode-v.xml conf\nvram\testmode-v.xml
|
||||
if not exist conf\raw mkdir conf\raw
|
||||
|
||||
regsvr32 /s com\k-clvsd.dll
|
||||
regsvr32 /s com\xactengine2_10.dll
|
||||
|
||||
.\launcher.exe -H 33554432 -K .\ddrhook2.dll .\arkmdxp3.dll %*
|
||||
@@ -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);
|
||||
void property_clear_error(struct property *prop);
|
||||
|
||||
int property_psmap_import(
|
||||
struct property *prop,
|
||||
struct property_node *root,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ==
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,4 +3,5 @@ libs += avs-util
|
||||
libs_avs-util := \
|
||||
|
||||
src_avs-util := \
|
||||
core-interop.c \
|
||||
error.c \
|
||||
|
||||
16
src/main/avs-util/core-interop.c
Normal file
16
src/main/avs-util/core-interop.c
Normal file
@@ -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);
|
||||
}
|
||||
7
src/main/avs-util/core-interop.h
Normal file
7
src/main/avs-util/core-interop.h
Normal file
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -5,4 +5,6 @@
|
||||
|
||||
const char *avs_util_error_str(avs_error error);
|
||||
|
||||
#endif
|
||||
const char *avs_util_property_error_get_and_clear(struct property *prop);
|
||||
|
||||
#endif
|
||||
|
||||
20
src/main/core/Module.mk
Normal file
20
src/main/core/Module.mk
Normal file
@@ -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 \
|
||||
|
||||
67
src/main/core/log-bt-ext.c
Normal file
67
src/main/core/log-bt-ext.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
59
src/main/core/log-bt-ext.h
Normal file
59
src/main/core/log-bt-ext.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef CORE_LOG_BT_EXT_H
|
||||
#define CORE_LOG_BT_EXT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
129
src/main/core/log-bt.c
Normal file
129
src/main/core/log-bt.c
Normal file
@@ -0,0 +1,129 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
87
src/main/core/log-bt.h
Normal file
87
src/main/core/log-bt.h
Normal file
@@ -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
|
||||
23
src/main/core/log-sink-async.c
Normal file
23
src/main/core/log-sink-async.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
19
src/main/core/log-sink-async.h
Normal file
19
src/main/core/log-sink-async.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef CORE_LOG_SINK_ASYNC_H
|
||||
#define CORE_LOG_SINK_ASYNC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
23
src/main/core/log-sink-debug.c
Normal file
23
src/main/core/log-sink-debug.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <debugapi.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
15
src/main/core/log-sink-debug.h
Normal file
15
src/main/core/log-sink-debug.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef CORE_LOG_SINK_DEBUG_H
|
||||
#define CORE_LOG_SINK_DEBUG_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
92
src/main/core/log-sink-file.c
Normal file
92
src/main/core/log-sink-file.c
Normal file
@@ -0,0 +1,92 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
28
src/main/core/log-sink-file.h
Normal file
28
src/main/core/log-sink-file.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef CORE_LOG_SINK_FILE_H
|
||||
#define CORE_LOG_SINK_FILE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
66
src/main/core/log-sink-list.c
Normal file
66
src/main/core/log-sink-list.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
24
src/main/core/log-sink-list.h
Normal file
24
src/main/core/log-sink-list.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CORE_LOG_SINK_LIST_H
|
||||
#define CORE_LOG_SINK_LIST_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
53
src/main/core/log-sink-mutex.c
Normal file
53
src/main/core/log-sink-mutex.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
21
src/main/core/log-sink-mutex.h
Normal file
21
src/main/core/log-sink-mutex.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef CORE_LOG_SINK_MUTEX_H
|
||||
#define CORE_LOG_SINK_MUTEX_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
21
src/main/core/log-sink-null.c
Normal file
21
src/main/core/log-sink-null.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
17
src/main/core/log-sink-null.h
Normal file
17
src/main/core/log-sink-null.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef CORE_LOG_SINK_NULL_H
|
||||
#define CORE_LOG_SINK_NULL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
193
src/main/core/log-sink-std.c
Normal file
193
src/main/core/log-sink-std.c
Normal file
@@ -0,0 +1,193 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
24
src/main/core/log-sink-std.h
Normal file
24
src/main/core/log-sink-std.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CORE_LOG_SINK_STD_H
|
||||
#define CORE_LOG_SINK_STD_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
45
src/main/core/log-sink.h
Normal file
45
src/main/core/log-sink.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef CORE_LOG_SINK_H
|
||||
#define CORE_LOG_SINK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
74
src/main/core/log.c
Normal file
74
src/main/core/log.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
197
src/main/core/log.h
Normal file
197
src/main/core/log.h
Normal file
@@ -0,0 +1,197 @@
|
||||
#ifndef CORE_LOG_H
|
||||
#define CORE_LOG_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
8
src/main/core/thread-crt-ext.c
Normal file
8
src/main/core/thread-crt-ext.c
Normal file
@@ -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);
|
||||
}
|
||||
9
src/main/core/thread-crt-ext.h
Normal file
9
src/main/core/thread-crt-ext.h
Normal file
@@ -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
|
||||
62
src/main/core/thread-crt.c
Normal file
62
src/main/core/thread-crt.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <process.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
15
src/main/core/thread-crt.h
Normal file
15
src/main/core/thread-crt.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef CORE_THREAD_CRT_H
|
||||
#define CORE_THREAD_CRT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
78
src/main/core/thread.c
Normal file
78
src/main/core/thread.c
Normal file
@@ -0,0 +1,78 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
117
src/main/core/thread.h
Normal file
117
src/main/core/thread.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef CORE_THREAD_H
|
||||
#define CORE_THREAD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user