Compare commits

..

12 Commits
pr285 ... pr295

Author SHA1 Message Date
icex2
d72996c5d9 refactor(inject): Use new core thread and log modules
Keep this a separate commit because this also removes
inject's own logging engine and replaces it with the
streamlined core API. The core API provides all the
features of inject's own logging engine which also
performed horribly. The entire logging operation
was locked which included expensive operations
that formatted the log messages and required
memory allocations and copying around data.

The core API's implementation at least only
synchronizes the actual IO operations
(though this can be improved further with an
actual async logging sink, TBD)
2024-02-25 09:30:53 +01:00
icex2
5ac858e15d chore: Delete old log and thread modules in util
The log API stopped scaling already a while ago and needs
considerable refactoring to consider the various use-cases
that emerged since it was first created on alpha versions
of bemanitools.
2024-02-25 09:30:53 +01:00
icex2
e4a221e15b refactor: Entire code base, thread and log usage
Boils down to:
- Include headers
- Reduce boiler plate with helpers
- Swap out explicit usages with core API layer
  and ensure the right API is configured beforehand
2024-02-25 09:30:53 +01:00
icex2
ea835b32ee refactor(api): Thread and log API
Split files and add name spacing.
2024-02-25 09:30:53 +01:00
icex2
fb1442b734 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.
2024-02-25 09:30:53 +01:00
icex2
87b7e53973 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
2024-02-25 09:30:53 +01:00
icex2
2e45f095ba feat(avs-util): Add helper to translate property errors 2024-02-25 09:30:53 +01:00
icex2
e12cd63ba2 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.
2024-02-25 09:30:47 +01:00
icex2
8804e667b3 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.
2024-02-25 09:14:42 +01:00
icex2
f5b8af3f2a 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).
2024-02-25 09:14:42 +01:00
icex2
ab6c3fc8bc fix(hook): Add missing hook_table_revert impl
Allow hooks to cleanup when they are shut down.
2024-02-25 09:07:54 +01:00
icex2
7a56fab96e fix(dist): Incorrect versioning for ddr distribution packages
Apparently forgotten to get updated to reflect the
currently supported versions correctly.
2024-02-25 08:51:25 +01:00
403 changed files with 3165 additions and 1189 deletions

21
Dockerfile.dev Normal file
View 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

View File

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

View File

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

31
src/api/log.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef BEMANITOOLS_API_THREAD_H
#define BEMANITOOLS_API_THREAD_H
#include <stdint.h>
#ifdef __GNUC__
/* Bemanitools is compiled with GCC (MinGW, specifically) as of version 5 */
#define LOG_CHECK_FMT __attribute__((format(printf, 2, 3)))
#else
/* Compile it out for MSVC plebs */
#define LOG_CHECK_FMT
#endif
/* An AVS-style logger function. Comes in four flavors: misc, info, warning,
and fatal, with increasing severity. Fatal loggers do not return, they
abort the running process after writing their message to the log.
"module" is an arbitrary short string identifying the source of the log
message. The name of the calling DLL is a good default choice for this
string, although you might want to identify a module within your DLL here
instead.
"fmt" is a printf-style format string. Depending on the context in which
your DLL is running you might end up calling a logger function exported
from libavs, which has its own printf implementation (including a number of
proprietary extensions), so don't use any overly exotic formats. */
typedef void (*btapi_log_formatter_t)(const char *module, const char *fmt, ...)
LOG_CHECK_FMT;
#endif

20
src/api/thread.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef BEMANITOOLS_API_THREAD_H
#define BEMANITOOLS_API_THREAD_H
#include <stdint.h>
/* An API for spawning threads. This API is defined by libavs, although
Bemanitools itself may supply compatible implementations of these functions
to your DLL, depending on the context in which it runs.
NOTE: You may only use the logging functions from a thread where Bemanitools
calls you, or a thread that you create using this API. Failure to observe
this restriction will cause the process to crash. This is a limitation of
libavs itself, not Bemanitools. */
typedef int (*btapi_thread_create_t)(
int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority);
typedef void (*btapi_thread_join_t)(int thread_id, int *result);
typedef void (*btapi_thread_destroy_t)(int thread_id);
#endif

View File

@@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 ==

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
libs += aciodrv-proc
libs_aciodrv-proc := \
core \
src_aciodrv-proc := \
panb.c \

View File

@@ -5,8 +5,9 @@
#include "aciodrv/device.h"
#include "aciodrv/panb.h"
#include "util/log.h"
#include "util/thread.h"
#include "core/thread.h"
#include "core/log.h"
static int auto_poll_proc(void *auto_poll_param);
static int auto_poll_threadid;
@@ -50,7 +51,7 @@ bool aciodrv_proc_panb_init(struct aciodrv_device_ctx *device)
InitializeCriticalSection(&keypair_lock);
InitializeCriticalSection(&auto_poll_stop_lock);
auto_poll_threadid =
thread_create(auto_poll_proc, (void *) device, 0x4000, 0);
core_thread_create(auto_poll_proc, (void *) device, 0x4000, 0);
return true;
}
@@ -80,8 +81,8 @@ void aciodrv_proc_panb_fini(struct aciodrv_device_ctx *device)
auto_poll_stop = true;
LeaveCriticalSection(&auto_poll_stop_lock);
thread_join(auto_poll_threadid, NULL);
thread_destroy(auto_poll_threadid);
core_thread_join(auto_poll_threadid, NULL);
core_thread_destroy(auto_poll_threadid);
DeleteCriticalSection(&keypair_lock);
DeleteCriticalSection(&auto_poll_stop_lock);

View File

@@ -7,8 +7,9 @@
#include "aciodrv/port.h"
#include "core/log.h"
#include "util/hex.h"
#include "util/log.h"
#include "util/mem.h"
/* Enable to dump all data to the logger */

View File

@@ -7,7 +7,7 @@
#include "aciodrv/device.h"
#include "util/log.h"
#include "core/log.h"
bool aciodrv_h44b_init(struct aciodrv_device_ctx *device, uint8_t node_id)
{

View File

@@ -5,7 +5,7 @@
#include "aciodrv/device.h"
#include "aciodrv/icca.h"
#include "util/log.h"
#include "core/log.h"
static bool aciodrv_icca_queue_loop_start(
struct aciodrv_device_ctx *device, uint8_t node_id)

View File

@@ -5,7 +5,7 @@
#include "aciodrv/device.h"
#include "util/log.h"
#include "core/log.h"
static bool
aciodrv_kfca_watchdog_start(struct aciodrv_device_ctx *device, uint8_t node_id)

View File

@@ -5,8 +5,7 @@
#include "aciodrv/device.h"
#include "aciodrv/panb.h"
#include "util/log.h"
#include "util/thread.h"
#include "core/log.h"
bool aciodrv_panb_start_auto_input(
struct aciodrv_device_ctx *device, uint8_t node_id, uint8_t node_count)

View File

@@ -6,7 +6,7 @@
#include <windows.h>
#include "util/log.h"
#include "core/log.h"
HANDLE aciodrv_port_open(const char *port_path, int baud)
{

View File

@@ -5,7 +5,7 @@
#include "aciodrv/device.h"
#include "util/log.h"
#include "core/log.h"
static bool aciodrv_rvol_change_expand_mode(
struct aciodrv_device_ctx *device, uint8_t node_id, uint8_t mode)

View File

@@ -5,7 +5,7 @@
#include "acioemu/addr.h"
#include "acioemu/emu.h"
#include "util/log.h"
#include "core/log.h"
void ac_io_emu_cmd_assign_addrs(
struct ac_io_emu *emu, const struct ac_io_message *req, uint8_t node_count)

View File

@@ -14,9 +14,10 @@
#include "acioemu/emu.h"
#include "acioemu/pipe.h"
#include "core/log.h"
#include "hook/iohook.h"
#include "util/log.h"
#include "util/str.h"
static HRESULT ac_io_emu_open(struct ac_io_emu *emu, struct irp *irp);

View File

@@ -6,7 +6,7 @@
#include "acioemu/emu.h"
#include "acioemu/hdxs.h"
#include "util/log.h"
#include "core/log.h"
static void ac_io_emu_hdxs_cmd_send_version(
struct ac_io_emu_hdxs *hdxs, const struct ac_io_message *req);

View File

@@ -7,9 +7,10 @@
#include "acio/acio.h"
#include "core/log.h"
#include "util/iobuf.h"
#include "util/list.h"
#include "util/log.h"
/* This uses the USB convention where OUT and IN are from the host's (game's)
perspective. So an OUT transaction comes in to us and vice versa.

View File

@@ -1,6 +1,7 @@
dlls += aciomgr
libs_aciomgr := \
core \
aciodrv \
util \

View File

@@ -5,14 +5,15 @@
#include <stdatomic.h>
#include "aciomgr/manager-init.h"
#include "aciomgr/manager.h"
#include "acio/acio.h"
#include "aciodrv/device.h"
#include "core/log.h"
#include "util/array.h"
#include "util/log.h"
#define MAX_PORT_PATH_LENGTH 256
@@ -90,7 +91,7 @@ void aciomgr_set_loggers(
log_formatter_t warning,
log_formatter_t fatal)
{
log_to_external(misc, info, warning, fatal);
core_log_impl_set(misc, warning, info, fatal);
}
struct aciomgr_port_dispatcher *aciomgr_port_init(const char *path, int baud)

View File

@@ -1,6 +1,7 @@
exes += aciotest
libs_aciotest := \
core \
bio2drv \
aciodrv \
aciodrv-proc \

View File

@@ -15,7 +15,14 @@
#include "aciotest/panb.h"
#include "aciotest/rvol.h"
#include "util/log.h"
#include "core/log-bt-ext.h"
#include "core/log-bt.h"
#include "core/log.h"
#include "core/thread-crt-ext.h"
#include "core/thread-crt.h"
#include "core/thread.h"
#include "core/log.h"
static uint8_t aciotest_cnt = 0;
static uint8_t bi2a_mode = 255;
@@ -112,7 +119,10 @@ int main(int argc, char **argv)
}
}
log_to_writer(log_writer_stdout, NULL);
core_thread_crt_ext_impl_set();
core_log_bt_ext_impl_set();
core_log_bt_ext_init_with_stdout();
struct aciodrv_device_ctx *device =
aciodrv_device_open_path(argv[1], atoi(argv[2]));

View File

@@ -10,13 +10,14 @@
#include <stdio.h>
#include "asio/asio-reghook.h"
#include "core/log.h"
#include "hook/com-proxy.h"
#include "hook/table.h"
#include "asio/asio-reghook.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/str.h"
#include "util/time.h"

View File

@@ -1,8 +1,8 @@
#include "cconfig/cconfig-util.h"
#include "asio/config-asio.h"
#include "core/log.h"
#include "util/log.h"
#include "asio/config-asio.h"
#define ASIOHOOK_CONFIG_IO_FORCE_ASIO_KEY "asio.force_asio"
#define ASIOHOOK_CONFIG_IO_FORCE_WASAPI_KEY "asio.force_wasapi"

View File

@@ -3,4 +3,5 @@ libs += avs-util
libs_avs-util := \
src_avs-util := \
core-interop.c \
error.c \

View 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);
}

View 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

View File

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

View File

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

View File

@@ -1,10 +1,11 @@
libs += bio2drv
libs_bio2drv := \
aciodrv
core \
aciodrv \
src_bio2drv := \
detect.c \
config-bio2.c \
bi2a-iidx.c \
bi2a-sdvx.c
bi2a-sdvx.c \

View File

@@ -7,7 +7,7 @@
#include "aciodrv/device.h"
#include "util/log.h"
#include "core/log.h"
// Must be provided on init command. Actual meaning unknown right now.
// Not providing this will not initialize the IO correctly resulting

View File

@@ -7,7 +7,7 @@
#include "aciodrv/device.h"
#include "util/log.h"
#include "core/log.h"
static const uint8_t _BIO2DR_BI2A_SDVX_INIT_DATA = 0x3B;

View File

@@ -1,8 +1,8 @@
#include "cconfig/cconfig-util.h"
#include "bio2drv/config-bio2.h"
#include "core/log.h"
#include "util/log.h"
#include "bio2drv/config-bio2.h"
#define BIO2DRV_CONFIG_BIO2_AUTO_KEY "bio2.autodetect"
#define BIO2DRV_CONFIG_BIO2_PORT_KEY "bio2.port"

View File

@@ -8,11 +8,11 @@
#include "bio2drv/detect.h"
#include "core/log.h"
#include <stdio.h>
#include <string.h>
#include "util/log.h"
DEFINE_GUID(
GUID_COM_BUS_ENUMERATOR,
0x4D36E978,
@@ -142,7 +142,7 @@ void bio2drv_set_loggers(
log_formatter_t warning,
log_formatter_t fatal)
{
log_to_external(misc, info, warning, fatal);
core_log_impl_set(misc, warning, info, fatal);
}
bool bio2drv_detect(

View File

@@ -14,6 +14,8 @@
#include "acioemu/addr.h"
#include "acioemu/emu.h"
#include "core/log.h"
#include "hook/iohook.h"
#include "hooklib/rs232.h"
@@ -25,7 +27,6 @@
#include "util/array.h"
#include "util/defs.h"
#include "util/iobuf.h"
#include "util/log.h"
#include "util/str.h"
static struct array bio2_active_ports;

View File

@@ -6,13 +6,14 @@
#include <cfgmgr32.h>
#include <setupapi.h>
#include "core/log.h"
#include "hook/table.h"
#include "bio2emu/emu.h"
#include "bio2emu/setupapi.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/str.h"
#include "util/time.h"

View File

@@ -4,6 +4,8 @@ deplibs_bsthook := \
avs \
libs_bsthook := \
avs-util \
core \
acioemu \
bstio \
hook \

View File

@@ -19,13 +19,14 @@
#include "bsthook/acio.h"
#include "bsthook/kfca.h"
#include "core/log.h"
#include "hook/iohook.h"
#include "imports/avs.h"
#include "util/defs.h"
#include "util/iobuf.h"
#include "util/log.h"
#include "util/str.h"
static struct ac_io_emu ac_io_emu;

View File

@@ -2,9 +2,14 @@
#include <stdbool.h>
#include "avs-util/core-interop.h"
#include "bemanitools/bstio.h"
#include "bemanitools/eamio.h"
#include "core/log.h"
#include "core/thread.h"
#include "hook/iohook.h"
#include "hooklib/app.h"
@@ -18,7 +23,6 @@
#include "util/cmdline.h"
#include "util/defs.h"
#include "util/log.h"
static bool my_dll_entry_init(char *sidcode, struct property_node *config);
static bool my_dll_entry_main(void);
@@ -33,19 +37,23 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *config)
log_info("Starting up BeatStream IO backend");
bst_io_set_loggers(
log_body_misc, log_body_info, log_body_warning, log_body_fatal);
core_log_impl_assign(bst_io_set_loggers);
ok = bst_io_init(avs_thread_create, avs_thread_join, avs_thread_destroy);
ok = bst_io_init(
core_thread_create_impl_get(),
core_thread_join_impl_get(),
core_thread_destroy_impl_get());
if (!ok) {
goto bst_io_fail;
}
eam_io_set_loggers(
log_body_misc, log_body_info, log_body_warning, log_body_fatal);
core_log_impl_assign(eam_io_set_loggers);
ok = eam_io_init(avs_thread_create, avs_thread_join, avs_thread_destroy);
ok = eam_io_init(
core_thread_create_impl_get(),
core_thread_join_impl_get(),
core_thread_destroy_impl_get());
if (!ok) {
goto eam_io_fail;
@@ -91,8 +99,9 @@ BOOL WINAPI DllMain(HMODULE self, DWORD reason, void *ctx)
return TRUE;
}
log_to_external(
log_body_misc, log_body_info, log_body_warning, log_body_fatal);
// Use AVS APIs
avs_util_core_interop_thread_avs_impl_set();
avs_util_core_interop_log_avs_impl_set();
args_recover(&argc, &argv);

View File

@@ -3,6 +3,8 @@
#include <stdbool.h>
#include "core/log.h"
#include "hook/com-proxy.h"
#include "hook/pe.h"
#include "hook/table.h"
@@ -10,7 +12,6 @@
#include "sdvxhook/gfx.h"
#include "util/defs.h"
#include "util/log.h"
static HRESULT STDCALL my_CreateDevice(
IDirect3D9 *self,

View File

@@ -7,10 +7,11 @@
#include <stdio.h>
#include <string.h>
#include "core/log.h"
#include "hook/table.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/str.h"
/* ------------------------------------------------------------------------- */

View File

@@ -15,13 +15,14 @@
#include <stdio.h>
#include "camhook/cam.h"
#include "core/log.h"
#include "hook/com-proxy.h"
#include "hook/table.h"
#include "camhook/cam.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/str.h"
#include "util/time.h"

View File

@@ -1,8 +1,8 @@
#include "cconfig/cconfig-util.h"
#include "camhook/config-cam.h"
#include "core/log.h"
#include "util/log.h"
#include "camhook/config-cam.h"
#define CAMHOOK_CONFIG_CAM_DISABLE_EMU_KEY "cam.disable_emu"
#define CAMHOOK_CONFIG_CAM_DEFAULT_DISABLE_EMU_VALUE false

View File

@@ -6,8 +6,9 @@
#include "cconfig/cconfig-main.h"
#include "core/log.h"
#include "util/cmdline.h"
#include "util/log.h"
bool cconfig_main_config_init(
struct cconfig *config,
@@ -84,7 +85,7 @@ bool cconfig_main_config_init(
}
log_misc("Config state after file loading:");
cconfig_util_log(config, log_impl_misc);
cconfig_util_log(config, core_log_misc_impl_get());
}
log_misc("Parsing override config parameters from cmd");
@@ -96,7 +97,7 @@ bool cconfig_main_config_init(
}
log_misc("Config state after cmd parameter overrides:");
cconfig_util_log(config, log_impl_misc);
cconfig_util_log(config, core_log_misc_impl_get());
goto success;

View File

@@ -5,8 +5,9 @@
#include "cconfig/cconfig-util.h"
#include "core/log.h"
#include "util/hex.h"
#include "util/log.h"
#include "util/mem.h"
bool cconfig_util_get_int(
@@ -220,10 +221,10 @@ void cconfig_util_set_data(
free(str);
}
void cconfig_util_log(struct cconfig *config, log_formatter_t log_formatter)
void cconfig_util_log(struct cconfig *config, core_log_message_t log_message)
{
for (uint32_t i = 0; i < config->nentries; i++) {
log_formatter(
log_message(
LOG_MODULE,
"%s=%s",
config->entries[i].key,

View File

@@ -7,7 +7,7 @@
#include "cconfig/cconfig.h"
#include "util/log.h"
#include "core/log.h"
bool cconfig_util_get_int(
struct cconfig *config,
@@ -57,6 +57,6 @@ void cconfig_util_set_data(
size_t len,
const char *desc);
void cconfig_util_log(struct cconfig *config, log_formatter_t log_formatter);
void cconfig_util_log(struct cconfig *config, core_log_message_t log_message);
#endif

View File

@@ -2,7 +2,8 @@
#include "cconfig/cconfig.h"
#include "util/log.h"
#include "core/log.h"
#include "util/mem.h"
#include "util/str.h"

View File

@@ -7,8 +7,9 @@
#include "cconfig/cmd.h"
#include "core/log.h"
#include "util/hex.h"
#include "util/log.h"
#include "util/str.h"
static void

View File

@@ -6,8 +6,9 @@
#include "cconfig/conf.h"
#include "core/log.h"
#include "util/fs.h"
#include "util/log.h"
#include "util/str.h"
enum cconfig_conf_error cconfig_conf_load_from_file(

View File

@@ -3,6 +3,7 @@ rc_config := config.rc
cppflags_config := -DUNICODE
libs_config := \
core \
eamio \
geninput \
util \

View File

@@ -11,13 +11,14 @@
#include "config/schema.h"
#include "config/usages.h"
#include "core/log.h"
#include "geninput/hid-mgr.h"
#include "geninput/input-config.h"
#include "geninput/mapper.h"
#include "util/array.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"

View File

@@ -15,11 +15,12 @@
#include "config/schema.h"
#include "config/usages.h"
#include "core/log.h"
#include "geninput/input-config.h"
#include "geninput/mapper.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/winres.h"

View File

@@ -13,6 +13,8 @@
#include "config/resource.h"
#include "config/schema.h"
#include "core/log.h"
#include "eamio/eam-config.h"
#include "geninput/hid-mgr.h"
@@ -21,7 +23,6 @@
#include "util/array.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"

View File

@@ -6,8 +6,9 @@
#include "config/resource.h"
#include "config/schema.h"
#include "core/log.h"
#include "util/defs.h"
#include "util/log.h"
static INT_PTR CALLBACK
game_type_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

View File

@@ -14,14 +14,19 @@
#include "config/spinner.h"
#include "config/usages.h"
#include "core/log-bt.h"
#include "core/log-sink-debug.h"
#include "core/log.h"
#include "core/thread-crt-ext.h"
#include "core/thread-crt.h"
#include "core/thread.h"
#include "eamio/eam-config.h"
#include "geninput/input-config.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/str.h"
#include "util/thread.h"
#include "util/winres.h"
HPROPSHEETPAGE
@@ -61,11 +66,22 @@ int main(int argc, char **argv)
wchar_t text[1024];
int max_light;
size_t i;
struct core_log_sink sink;
inst = GetModuleHandle(NULL);
log_to_writer(log_writer_debug, NULL);
log_to_external(log_impl_misc, log_impl_info, log_impl_warning, my_fatal);
core_thread_crt_ext_impl_set();
core_log_impl_set(
core_log_bt_log_misc,
core_log_bt_log_info,
core_log_bt_log_warning,
my_fatal);
core_log_sink_debug_open(&sink);
core_log_bt_init(&sink);
core_log_bt_level_set(CORE_LOG_BT_LOG_LEVEL_MISC);
usages_init(inst);
@@ -96,13 +112,17 @@ int main(int argc, char **argv)
}
}
input_set_loggers(
log_impl_misc, log_impl_info, log_impl_warning, log_impl_fatal);
input_init(crt_thread_create, crt_thread_join, crt_thread_destroy);
core_log_impl_assign(input_set_loggers);
input_init(
core_thread_create_impl_get(),
core_thread_join_impl_get(),
core_thread_destroy_impl_get());
eam_io_set_loggers(
log_impl_misc, log_impl_info, log_impl_warning, log_impl_fatal);
eam_io_init(crt_thread_create, crt_thread_join, crt_thread_destroy);
core_log_impl_assign(eam_io_set_loggers);
eam_io_init(
core_thread_create_impl_get(),
core_thread_join_impl_get(),
core_thread_destroy_impl_get());
eam_io_config_api = eam_io_get_config_api();
// calculate these and check against the loaded config

View File

@@ -6,10 +6,11 @@
#include "config/snap.h"
#include "core/log.h"
#include "geninput/hid-mgr.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
enum snap_control_heuristic { CONTROL_CENTERING_AXIS, CONTROL_MULTISWITCH };

View File

@@ -9,8 +9,9 @@
#include "config/resource.h"
#include "core/log.h"
#include "util/array.h"
#include "util/log.h"
#include "util/str.h"
#include "util/winres.h"

20
src/main/core/Module.mk Normal file
View 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 \

View 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);
}

View 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
View 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
View 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

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

View 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

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

View 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

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

View 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

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

View 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

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

View 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

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

View 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

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

View 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
View 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
View 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
View 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

View 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);
}

View 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

View File

@@ -4,8 +4,10 @@
#include <stddef.h>
#include <stdint.h>
#include "core/thread-crt.h"
#include "core/thread.h"
#include "util/defs.h"
#include "util/thread.h"
struct shim_ctx {
HANDLE barrier;
@@ -13,10 +15,6 @@ struct shim_ctx {
void *ctx;
};
thread_create_t thread_impl_create = crt_thread_create;
thread_join_t thread_impl_join = crt_thread_join;
thread_destroy_t thread_impl_destroy = crt_thread_destroy;
static unsigned int STDCALL crt_thread_shim(void *outer_ctx)
{
struct shim_ctx *sctx = outer_ctx;
@@ -31,7 +29,7 @@ static unsigned int STDCALL crt_thread_shim(void *outer_ctx)
return proc(inner_ctx);
}
int crt_thread_create(
int core_thread_crt_create(
int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority)
{
struct shim_ctx sctx;
@@ -49,12 +47,12 @@ int crt_thread_create(
return (int) thread_id;
}
void crt_thread_destroy(int thread_id)
void core_thread_crt_destroy(int thread_id)
{
CloseHandle((HANDLE) (uintptr_t) thread_id);
}
void crt_thread_join(int thread_id, int *result)
void core_thread_crt_join(int thread_id, int *result)
{
WaitForSingleObject((HANDLE) (uintptr_t) thread_id, INFINITE);
@@ -62,31 +60,3 @@ void crt_thread_join(int thread_id, int *result)
GetExitCodeThread((HANDLE) (uintptr_t) thread_id, (DWORD *) result);
}
}
void thread_api_init(
thread_create_t create, thread_join_t join, thread_destroy_t destroy)
{
if (create == NULL || join == NULL || destroy == NULL) {
abort();
}
thread_impl_create = create;
thread_impl_join = join;
thread_impl_destroy = destroy;
}
int thread_create(
int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority)
{
return thread_impl_create(proc, ctx, stack_sz, priority);
}
void thread_join(int thread_id, int *result)
{
thread_impl_join(thread_id, result);
}
void thread_destroy(int thread_id)
{
thread_impl_destroy(thread_id);
}

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