mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-09-15 03:45:09 -05:00
Compare commits
170 Commits
pr290
...
sapling-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18f84e4751 | ||
|
|
19f0776627 | ||
|
|
aad38449e3 | ||
|
|
1c0092c9be | ||
|
|
1db5260a59 | ||
|
|
c337bdb7c0 | ||
|
|
9cb37ccc11 | ||
|
|
549e50dfa7 | ||
|
|
d5e4cc3447 | ||
|
|
ae02d6556b | ||
|
|
071a3994ed | ||
|
|
5bee76e553 | ||
|
|
3c2dda505e | ||
|
|
d840f7bb8f | ||
|
|
df4f646b70 | ||
|
|
ca9cac9c19 | ||
|
|
7943ecffcd | ||
|
|
51ce7c739a | ||
|
|
c6180bdf47 | ||
|
|
c3a337ceb4 | ||
|
|
a3d890dcd7 | ||
|
|
ee3d1d2592 | ||
|
|
4159813873 | ||
|
|
c5083a6d4a | ||
|
|
8752c7dcc5 | ||
|
|
6276cb4e97 | ||
|
|
fcbb4160ee | ||
|
|
9bcbe9170f | ||
|
|
e74e3f1472 | ||
|
|
2062b803ff | ||
|
|
7391cfa555 | ||
|
|
06d4467d5a | ||
|
|
b0b380e70c | ||
|
|
f0b3556ac5 | ||
|
|
54284adf5f | ||
|
|
451f9d4021 | ||
|
|
1b0426941d | ||
|
|
f5a82fe049 | ||
|
|
2a5ddacb36 | ||
|
|
9246f72b80 | ||
|
|
f6d8e30671 | ||
|
|
8afab5c232 | ||
|
|
c0be02a24f | ||
|
|
e7459659dd | ||
|
|
3aa128e525 | ||
|
|
74bdd65098 | ||
|
|
a66aba45b9 | ||
|
|
1373e9f1f4 | ||
|
|
9e2319bd13 | ||
|
|
5687392a92 | ||
|
|
addcdf8d16 | ||
|
|
1fbb5ac39f | ||
|
|
191e1e74f0 | ||
|
|
34807f0b9d | ||
|
|
d442177f21 | ||
|
|
af372df06b | ||
|
|
4ed3c97c2a | ||
|
|
0d876fc806 | ||
|
|
2b08a0cc1a | ||
|
|
d8e604cdd9 | ||
|
|
95f792f655 | ||
|
|
7b8eec72d3 | ||
|
|
83621fa5d5 | ||
|
|
563d6dd793 | ||
|
|
e69c75bf7f | ||
|
|
847cc3322e | ||
|
|
48cd2bc687 | ||
|
|
3db8bc4898 | ||
|
|
9b5f627dd1 | ||
|
|
29f57065fe | ||
|
|
05889947fe | ||
|
|
2a34bb19da | ||
|
|
8b22ef1e8c | ||
|
|
b2006ec9be | ||
|
|
db6e7a6ad9 | ||
|
|
f15eec50fe | ||
|
|
27bb8a0e2d | ||
|
|
e0c7141896 | ||
|
|
5f5824f9f8 | ||
|
|
9cf9cac4e3 | ||
|
|
a11c7a0704 | ||
|
|
83473a83be | ||
|
|
aa3e7d307d | ||
|
|
1800f75f3d | ||
|
|
c0289b14d3 | ||
|
|
d17dcea594 | ||
|
|
002a5727ef | ||
|
|
1e4fffb5f9 | ||
|
|
93fe44b46e | ||
|
|
e12eb2b969 | ||
|
|
0afadc6576 | ||
|
|
8b82b32894 | ||
|
|
8efeb68c75 | ||
|
|
d482acd533 | ||
|
|
8d49bd8f40 | ||
|
|
d977a0bf35 | ||
|
|
dab2230a1c | ||
|
|
d25d49b668 | ||
|
|
8282a9f3f4 | ||
|
|
2c00da6232 | ||
|
|
cf8df078f5 | ||
|
|
4d8c9359a1 | ||
|
|
c33e9bb06a | ||
|
|
77ff211c68 | ||
|
|
0257ad6b42 | ||
|
|
15a1762837 | ||
|
|
d92987b229 | ||
|
|
5f9aaeddf9 | ||
|
|
4cbef617a8 | ||
|
|
2ca95ab6d8 | ||
|
|
61691f06a7 | ||
|
|
218ffe72c1 | ||
|
|
8c5cf383b6 | ||
|
|
075381481d | ||
|
|
94aa0bdda3 | ||
|
|
70e8e2a965 | ||
|
|
d1db1d816f | ||
|
|
d3273bf1d8 | ||
|
|
f4f8e7d8e2 | ||
|
|
84ff799024 | ||
|
|
c369412f70 | ||
|
|
26d1da8660 | ||
|
|
04310f49fd | ||
|
|
d72996c5d9 | ||
|
|
5ac858e15d | ||
|
|
e4a221e15b | ||
|
|
ea835b32ee | ||
|
|
fb1442b734 | ||
|
|
a4b3f92935 | ||
|
|
1126011d99 | ||
|
|
6a98ce1f59 | ||
|
|
dc6e745533 | ||
|
|
5833197b03 | ||
|
|
339bf0dfed | ||
|
|
6a320a38c0 | ||
|
|
d8a5619a51 | ||
|
|
215a4bdb15 | ||
|
|
4ac1399923 | ||
|
|
f688eead44 | ||
|
|
e8221631f9 | ||
|
|
0afca1da49 | ||
|
|
734c17a0a2 | ||
|
|
6940a9de4b | ||
|
|
1d489643d1 | ||
|
|
33fee83aff | ||
|
|
8b377f256a | ||
|
|
64cc7084dc | ||
|
|
7a086ea60b | ||
|
|
7ee9a2e219 | ||
|
|
fa5a50167f | ||
|
|
0c08d19276 | ||
|
|
c78639f094 | ||
|
|
2ebfd39bea | ||
|
|
2fe21ae6b7 | ||
|
|
daba638bb0 | ||
|
|
359ab5a541 | ||
|
|
6dd3669559 | ||
|
|
fbadcc9b41 | ||
|
|
ca91e4c4ae | ||
|
|
08a72abb58 | ||
|
|
7e49fad175 | ||
|
|
0adec478cc | ||
|
|
bc9b8b508e | ||
|
|
33372f3ef2 | ||
|
|
dedbdaf729 | ||
|
|
3865e09f06 | ||
|
|
517f8e885f | ||
|
|
560aba3133 | ||
|
|
6b75b33253 | ||
|
|
312aa619c6 |
@@ -1,21 +0,0 @@
|
||||
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,10 +13,8 @@ BUILDDIR ?= build
|
||||
|
||||
builddir_docker := $(BUILDDIR)/docker
|
||||
|
||||
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"
|
||||
docker_container_name := "bemanitools-build"
|
||||
docker_image_name := "bemanitools-build:latest"
|
||||
|
||||
depdir := $(BUILDDIR)/dep
|
||||
objdir := $(BUILDDIR)/obj
|
||||
@@ -43,7 +41,6 @@ FORCE:
|
||||
|
||||
.PHONY: \
|
||||
build-docker \
|
||||
dev-docker \
|
||||
clean \
|
||||
code-format \
|
||||
doc-format \
|
||||
@@ -92,38 +89,21 @@ version:
|
||||
$(V)echo "$(gitrev)" > version
|
||||
|
||||
build-docker:
|
||||
$(V)docker rm -f $(docker_build_container_name) 2> /dev/null || true
|
||||
$(V)docker rm -f $(docker_container_name) 2> /dev/null || true
|
||||
$(V)docker \
|
||||
build \
|
||||
-t $(docker_build_image_name) \
|
||||
-f Dockerfile.build \
|
||||
-t $(docker_image_name) \
|
||||
-f Dockerfile \
|
||||
.
|
||||
$(V)docker \
|
||||
run \
|
||||
--volume $(shell pwd):/bemanitools \
|
||||
--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)
|
||||
--name $(docker_container_name) \
|
||||
$(docker_image_name)
|
||||
|
||||
clean-docker:
|
||||
$(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)docker rm -f $(docker_container_name) || true
|
||||
$(V)docker image rm -f $(docker_image_name) || true
|
||||
$(V)rm -rf $(BUILDDIR)
|
||||
|
||||
#
|
||||
|
||||
@@ -100,7 +100,6 @@ 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
|
||||
@@ -711,8 +710,6 @@ $(zipdir)/ddr-14-to-18.zip: \
|
||||
build/bin/indep-32/eamio.dll \
|
||||
build/bin/indep-32/geninput.dll \
|
||||
dist/ddr/config.bat \
|
||||
dist/ddr/gamestart-17.bat \
|
||||
dist/ddr/gamestart-18.bat \
|
||||
dist/ddr/gamestart-14.bat \
|
||||
dist/ddr/gamestart-15.bat \
|
||||
dist/ddr/gamestart-16.bat \
|
||||
@@ -731,8 +728,6 @@ $(zipdir)/ddr-16-to-18-x64.zip: \
|
||||
build/bin/indep-64/eamio.dll \
|
||||
build/bin/indep-64/geninput.dll \
|
||||
dist/ddr/config.bat \
|
||||
dist/ddr/gamestart-17.bat \
|
||||
dist/ddr/gamestart-18.bat \
|
||||
dist/ddr/gamestart-16.bat \
|
||||
dist/ddr/gamestart-17.bat \
|
||||
dist/ddr/gamestart-18.bat \
|
||||
|
||||
319
doc/dev/journal/2025-02-09-iidx-engine.md
Normal file
319
doc/dev/journal/2025-02-09-iidx-engine.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# IIDX engine overview and how game-play is impacted by hardware and software
|
||||
|
||||
Date: 2025-02-09 Author: icex2
|
||||
|
||||
This document provides information and explains the following:
|
||||
|
||||
* A sort-of taxonomy of the different major IIDX game engine versions
|
||||
* How the core game-play part of the engine works regarding
|
||||
* Threading model
|
||||
* Main render loop
|
||||
* Timing and synchronization
|
||||
|
||||
The goal is to capture key knowledge about how the game's engine works to enable a better
|
||||
understanding for developing bemanitools and how certain features can impact performance and
|
||||
synchronization during game-play.
|
||||
|
||||
This document is not claiming to be complete or 100% accurate. It is based on my own personal
|
||||
research and understanding of the IIDX game engine. It is very likely that some of the information
|
||||
is wrong or misunderstood. Feel free to raise any questions or concerns in an issue as I want
|
||||
this document to be as accurate as possible.
|
||||
|
||||
## Taxonomy of IIDX game engine versions
|
||||
|
||||
With every version, there have been numerous changes and improvements to the game engine. The
|
||||
following is a rough taxonomy that is focused on cabinet and hardware changes. As these are likely
|
||||
the main driver for key changes to the software and game engine, they create fairly distinct
|
||||
stages throughout the different versions of the game. The key attributes to be considered here are:
|
||||
|
||||
* Main PCB
|
||||
* IO board
|
||||
* Monitor
|
||||
|
||||
All of these had significant impact on key software features appearing or changing.
|
||||
|
||||
### Twinkle hardware era: 1st Style to 8th Style
|
||||
|
||||
* [Twinkle PCB](https://github.com/shizmob/arcade-docs/blob/main/konami/boards.md#twinkle)
|
||||
* [Original rear projection monitor](https://github.com/shizmob/arcade-docs/blob/main/konami/products.md#iidx-rear-projection-monitor)
|
||||
* Homogenous hardware and software
|
||||
|
||||
### 1st gen PC-based upgrade only: 9th Style
|
||||
|
||||
* 9th Style came as an upgrade kit only to existing twinkle-based cabinet hardware
|
||||
* [KNM-845G3-A02 PCB](https://github.com/shizmob/arcade-docs/blob/main/konami/boards.md#knm-845g3-a02)
|
||||
* [C02 EZUSB FX USB IO board](https://github.com/shizmob/arcade-docs/blob/main/konami/io.md#gec02-pwbaa)
|
||||
* Magnetic card readers connected to ezusb IO board
|
||||
|
||||
### 1st gen PC-based with dedicated cabinet: 10th Style to DistorteD (13)
|
||||
|
||||
* From here on, old cabinets that received the "Twinkle hardware" upgrade kit could always receive software only
|
||||
upgrades
|
||||
* [CRT monitor](https://github.com/shizmob/arcade-docs/blob/main/konami/products.md#iidx-crt-monitor) with new dedicated
|
||||
cabinets
|
||||
* [D01 EZUSB FX USB IO board](https://github.com/shizmob/arcade-docs/blob/main/konami/io.md#d01-io)
|
||||
* Slotted card readers starting version 13
|
||||
|
||||
### 2nd gen PC: GOLD (14) to Lincle (19)
|
||||
|
||||
* [FAB-e945-KN205](https://github.com/shizmob/arcade-docs/blob/main/konami/boards.md#fab-e945-kn205)
|
||||
* All prior cabinet configurations can be upgraded with a PCB and software upgrade
|
||||
* New LCD monitor starting version 15
|
||||
* Further variants of the LCD monitor appear starting version 18
|
||||
* [IO2 EZUSB FX2 USB IO board](https://github.com/shizmob/arcade-docs/blob/main/konami/io.md#usbio2) introduced with
|
||||
version 14
|
||||
* Switch to wave pass readers on version 19
|
||||
|
||||
### 3rd gen PC: Tricoro (20) to Sinobuz (24)
|
||||
|
||||
* All prior cabinet configurations can be upgraded with a PCB and software upgrade
|
||||
* [ADE-HM65](https://github.com/shizmob/arcade-docs/blob/main/konami/boards.md#hm65)
|
||||
* Game engine supports HD mode (720p)
|
||||
* Many new LCD monitor variants appear with every new version
|
||||
|
||||
### 4th gen PC: CANNON BALLERS (25) to RESIDENT (30)
|
||||
|
||||
* All prior cabinet configurations can be upgraded with a PCB and software upgrade
|
||||
* [ADE-6291](https://github.com/shizmob/arcade-docs/blob/main/konami/boards.md#ade-6291)
|
||||
* [BIO2 IO board](https://github.com/shizmob/arcade-docs/blob/main/konami/boards.md#hm65)
|
||||
* Upgrade cabinets required the [BIO2 sub-IO board](https://github.com/shizmob/arcade-docs/blob/main/konami/io.md#bio2-ldj-sub-io)
|
||||
* Drop of SD mode support starting version 27
|
||||
* Supporting 120 hz monitors starting version 27
|
||||
* Many new LCD monitor variants appear with every new version
|
||||
* 42" 120 hz for lightning cabinets starting version 27
|
||||
* 42" 60 hz for non-lightning cabinets starting version 25
|
||||
|
||||
### 5th gen PC: RESIDENT (30) to EPOLIS (31)
|
||||
|
||||
* All prior cabinet configurations can be upgraded with a PCB and software upgrade
|
||||
* [C300-xonarae](https://github.com/shizmob/arcade-docs/blob/main/konami/boards.md#c300-xonarae)
|
||||
* Game engine supports FHD mode (1080p)
|
||||
|
||||
## Threading model and core responsibilities in PC-based games
|
||||
|
||||
Since the early days of 9th Style to today (as of time of writing this: EPOLIS), the threading model
|
||||
hasn't changed significantly:
|
||||
|
||||
* Main and rendering thread
|
||||
* Runs your typical D3D9 render loop
|
||||
* Using D3D9ex starting version 27
|
||||
* Starting and managing other threads
|
||||
* Any file and game asset loading
|
||||
* Handles all the core game logic of the different screens starting from the initial boot screen
|
||||
to the core game-play screen
|
||||
* IO thread
|
||||
* Polls the main IO hardware and synchronizes the in-memory input and output state with the
|
||||
hardware
|
||||
* Executes commands issued by the main thread to the IO board such as dongle or card reader
|
||||
commands (ezusb generation of IO only)
|
||||
* Card reader thread (not applicable to versions 9 to 12)
|
||||
* Runs the protocol to read/write the hardware of the readers
|
||||
* Async execution of higher level commands issued by the main thread to the card reader hardware,
|
||||
e.g. card read, card eject etc.
|
||||
* Audio thread
|
||||
* Streaming of audio data to the sound API
|
||||
* DirectSound for versions 9 to 26
|
||||
* WASAPI for versions 27 to 30 for non-lighting cabinets with ADE-6291 hardware
|
||||
* ASIO for versions 27 and newer for lighting cabinets
|
||||
* ASIO for version 30/31 and newer for non-lighting cabinets with C300-xonarae hardware
|
||||
* Async execution of higher level commands issued by the main thread to the audio backend, e.g.
|
||||
play audio, stop audio, etc.
|
||||
* Network thread
|
||||
* Network plumbing with the xrpc protocol
|
||||
* Async execution of request-response commands issued by the main thread
|
||||
|
||||
## Main and rendering thread
|
||||
|
||||
With the game having transitioning to different screens throughout it's lifecycle, the main render
|
||||
loop might contain different logic such as asset loading. The following focuses on the main
|
||||
game-play screen, only. Common rendering logic still applies to other screens.
|
||||
|
||||
This follows your typical D3D9 single threaded rendering loop which boils down to the following:
|
||||
|
||||
* Begin the scene (`BeginScene`)
|
||||
* Set the render target (`SetRenderTarget`)
|
||||
* Clear the back buffer (`Clear`)
|
||||
* Run engine step. This includes and is mixed with
|
||||
* IO input evaluation
|
||||
* Game engine state and (re-) drawing the scene
|
||||
* Commands to the audio playback, e.g. play key sounds
|
||||
* Setting IO outputs
|
||||
* End the scene (`EndScene`)
|
||||
* Target a minimum frame time
|
||||
* Introduced with version 11
|
||||
* Sleep (`Sleep` or `SleepEx`) to fill up the frame up to either 13 ms or 14 ms (depending on the
|
||||
version)
|
||||
* This was likely introduced to not have the game run at hundreds of FPS if v-sync was disabled
|
||||
(probably when running in window mode for development/testing purposes)
|
||||
* Swapping the back buffers (`Present`)
|
||||
* D3D9 is configured with v-sync enabled by default and targets 60 hz
|
||||
* With this configuration, the call to `Present` fills up the remaining frame time to target the
|
||||
configured 60 hz refresh rate
|
||||
|
||||
### Rendering resolutions
|
||||
|
||||
The game engine went through a couple of different rendering resolutions:
|
||||
|
||||
* 640x240: version 1 to 8 (all twinkle hardware)
|
||||
* 640x480: version 9 to 19 (1st and 2nd gen PC-based)
|
||||
* 1280x720 (HD mode) and 640x480 (SD mode): version 20 to 27 (3rd and 4th gen PC-based)
|
||||
* 1280x720 (HD mode) only for version 28 to 29 (4th gen PC-based)
|
||||
* 1280x720 (HD mode) and 1920x1080 (FHD mode) for version 30 (4th gen PC-based)
|
||||
* 1920x1080 (FHD mode) only for version 30+ (5th gen PC-based)
|
||||
|
||||
## Core engine timing and synchronization
|
||||
|
||||
The following outlines and explains the core engine concepts related to timing and synchronization.
|
||||
|
||||
### The origins, twinkle hardware
|
||||
|
||||
* It appears that the game has been on the same codebase since it's inception.
|
||||
* This can be reasoned by the following observations
|
||||
* 9th style as the first PC-based game very buggy on initial release
|
||||
* 9th style threading model very clunky with various thread settings showing they tried to
|
||||
prioritize different processing on a single core CPU
|
||||
* Coming from twinkle hardware that is a realtime system vs. a Windows XP system
|
||||
* Fixed playstation 1 based hardware with a refresh rate of
|
||||
[59.8260978565 hz](https://github.com/mamedev/mame/blob/bf9df04e005697788ae988ef5aea91a1878fd631/src/devices/video/psx.cpp#L435)
|
||||
* No need to assume getting anything else if the resource budget per frame is not exceeded
|
||||
* All sub-systems were driven synchronously to that fixed time step
|
||||
* No need for the engine to synchronize audio during the song. When the song is started
|
||||
correctly, it stayed sync throughout the song
|
||||
* Observations and assumptions
|
||||
* It appears the developers struggled a lot with the significant platform shift to PC as indicated
|
||||
by various core engine improvements throughout the years still
|
||||
* Switches of hardware generations further complicated the situation as these forced them to further
|
||||
drift away from how the game engine was initially designed on the twinkle system
|
||||
|
||||
### Frame time/timing and engine step
|
||||
|
||||
* The game's engine step and timing is frame based
|
||||
* The main render loop defines the minimum valid timing window for the game engine to be able to
|
||||
evaluate
|
||||
* 60 hz/fps = 16.667 ms (rounded to three decimal places)
|
||||
* 120 hz/fps = 8.333 ms (rounded to three decimal places), only relevant for lightning cabinets with their 120 hz
|
||||
screen/mode
|
||||
* Depending on the GPU hardware and configuration, the GPU might drive the screen not exactly at
|
||||
perfectly 60 hz/fps = 16.667 ms
|
||||
* As outlined in the [main render loop](#main-and-rendering-thread), the `Present` call is the
|
||||
key function call to determine the frame time for the game engine
|
||||
* Just having v-sync enabled and telling D3D9 to **target** 60 hz, it still depends on how the GPU
|
||||
driver is implemented and how the GPU driver's implementation of D3D9 executes on the `Present`
|
||||
call
|
||||
* This is primarily determined by how the GPU driver is operating and driving connected display
|
||||
hardware
|
||||
* The following factors can contribute to that either directly or indirectly
|
||||
* Different monitor types analog vs. digital (CRT vs. LCD)
|
||||
* Different monitor connections VGA vs. DVI/HDMI/DP
|
||||
* Different GPU drivers
|
||||
* Different GPU hardware and vendors
|
||||
* Different GPU settings
|
||||
|
||||
### Monitor check screen
|
||||
|
||||
The monitor check screen was introduced with version 14. This was a solution to the problem of not
|
||||
guaranteeing a single fixed refresh rate throughout different GPU and monitor combinations. See
|
||||
a detailed explanation in the previous sub-subsection about
|
||||
[what influences the frame time](#frame-timetiming-and-engine-step) of the game.
|
||||
|
||||
The goal of the monitor check is to measure the frame time of the main render loop and use the
|
||||
results for [driving the core engine step](#engine-step-and-synchronization).
|
||||
|
||||
The monitor check evolved slightly as different GPU and monitor hardware, and GPU drivers yielded
|
||||
varying refresh rates that determined the frame times of the main game loop.
|
||||
|
||||
Actual values are outlined further down in the [engine step time](#engine-step-time) section.
|
||||
|
||||
There are two main versions of the monitor check, all of them have always been part of the boot
|
||||
process:
|
||||
|
||||
* 14
|
||||
* Measures a total of 3000 frames
|
||||
* If total time this took < 50004 ms -> so average of 16.668 ms per frame = 59.9952 hz
|
||||
* S-Video mode -> 59.95 hz (also shows S-VIDEO when the monitor check completes)
|
||||
* Otherwise VGA mode -> 60.05 hz
|
||||
* If VGA is determined, the game patches the chart event data when loading
|
||||
* `event_timestamp * 0.99817199 + 0.40000001`
|
||||
* When S-Video no charts are patched and the timing data in the chart is used as is
|
||||
* 15 to 18
|
||||
* Identical to 14 except that chart patching now happens in milliseconds instead of frames
|
||||
* VGA mode: `16.65279`
|
||||
* S-Video mode: `16.680567`
|
||||
* `event_timestamp * (1.0 / frame_time) + 0.40000001)`
|
||||
* 19
|
||||
* Identical to previous version
|
||||
* Monitor check screen shows current FPS on the last revision of that version. However, it doesn't
|
||||
use the value shown there by any means. Assuming this was added before the introduction of the
|
||||
new monitor check on 20.
|
||||
* 20 to 26
|
||||
* Runs on boot or when switching monitor modes SD vs. HD
|
||||
* Measures only 1200 frames
|
||||
* Takes the average refresh rate of 1200 frames
|
||||
* Applies the measured avg. frame time directly to the charts before song
|
||||
* 27 to 30
|
||||
* Frame rate measuring identical to prior versions
|
||||
* Added additional logic after the frame time measurements for handling of 120 hz displays
|
||||
* Includes checking the monitor and if the framerate matches as expected
|
||||
* 30/31 LDJ-010 upgrade
|
||||
* Added NvDisplayConfigLDJ tool which runs on system start and configures a custom timing in the
|
||||
NVIDIA GPU driver to ensure a fixed monitor refresh rate
|
||||
* 31
|
||||
* Same code as before
|
||||
* Consider the first 460 frames of the measurement as warm-up frames
|
||||
* Discard these for the actual measurement to get more stable frame times
|
||||
* Use a base avg. value of 119.982 for the 120 hz monitors and 59.9 for 60 hz monitors. Further
|
||||
frames measured are counted towards the average with the given base value
|
||||
|
||||
### Engine core game-play with visual and audio synchronization
|
||||
|
||||
#### Chart data
|
||||
|
||||
* Chart data encodes note timing and timing window information
|
||||
* Chart data consists of *note events* encoding a timestamp and note information such as type
|
||||
and key-sound information
|
||||
* Timing windows are defined per chart
|
||||
* Most infamous example: GAMBOL (7Key/Another)
|
||||
* Game engine timestamp evaluation and note event timestamp encoding
|
||||
* 9 to 14: Refresh rate in hz
|
||||
* 15 to 31: Milliseconds (equivalent to assuming 1000 fps)
|
||||
* Fundamentally, that doesn't change game play experience as it's just a different way of
|
||||
evaluating the timestamps since refresh rate in hz/frame rate in fps can be converted lossless to
|
||||
frame time in ms
|
||||
|
||||
#### Engine step time
|
||||
|
||||
The engine uses the following source for frame rate/time to drive internal logic on the following
|
||||
versions
|
||||
|
||||
* 9 to 13: Hardcoded 59.95 hz
|
||||
* 14: Either 59.95 hz or 60.05 hz determined by which value is determined to be closer to the
|
||||
measured one during the monitor check screen on startup
|
||||
* 15 to 19: 59.95 hz or 60.05 hz
|
||||
* Monitor check on startup measures the frame time and picks the value closest to what's been
|
||||
measured
|
||||
* Monitor check result can be overridden with the *Output Type* setting in the operator menu
|
||||
* *S-Video* to force to 59.95 hz
|
||||
* *VGA* to force to 60.05 hz
|
||||
* 20 to 31: Monitor check on boot determines the frame time and uses the result throughout the
|
||||
entire game session
|
||||
|
||||
#### Engine step and synchronization
|
||||
|
||||
Per frame engine step advancing and synchronization during game-play
|
||||
|
||||
* Game-play needs synchronization of the following aspects
|
||||
* Drawn content on the screen
|
||||
* Background audio playback track
|
||||
* Key sound effects
|
||||
* The game engine synchronizes these only at the start of a song
|
||||
* It takes known/determined refresh rate/frame time as the fixed time for every frame cycle
|
||||
throughout the entire song
|
||||
* It starts playback of the background audio track together with playback of the chart data
|
||||
* With every engine step/frame it advances internal calculations by that fixed amount of time.
|
||||
Thus, the game engine expects this to be constant/stable throughout an entire song
|
||||
* The chart data is advanced every frame by the engine
|
||||
* Key sound events are turned into audio playback on command as the chart advances
|
||||
* No re-synchronization or check-pointing between main rendering thread and audio is happening
|
||||
throughout the song
|
||||
* Remark: The above does not consider any kind of offsetting and timing adjustments for the sake
|
||||
of keeping this simple and focus on the absolute core concepts
|
||||
224
doc/iidxhook/iidx-syncbook.md
Normal file
224
doc/iidxhook/iidx-syncbook.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# IIDX "Syncbook": A guide to ensuring sync and performant gameplay
|
||||
|
||||
This is a guide applicable to all IIDX versions that are native Windows PC-based. The goal is to
|
||||
enable proper configuration of those games to ensure the game engine "runs in-sync" with any of its
|
||||
IO (visual GPU/display output, audio output, user input) resulting in high and stable performance
|
||||
for smooth game-play.
|
||||
|
||||
This document won't go into the implementation details of the game engine. Please refer to the
|
||||
[IIDX engine details journal](../dev/journal/2025-02-09-iidx-engine.md) for a more in-depth
|
||||
information.
|
||||
|
||||
## Understanding the problems and options to address them
|
||||
|
||||
First, let's understand what the key problems are and how they contribute to different issues
|
||||
that impact game-play.
|
||||
|
||||
### Song de-syncing
|
||||
|
||||
Symptom: Throughout playing a song, the audio is de-syncing even it was fine when the song started.
|
||||
|
||||
Cause: The game engine is fundamentally designed to run on a fixed and consistent frame
|
||||
rate/time per engine step. A mismatch of expected frame time by the game engine and actually delivered
|
||||
frame time by the GPU driver. This can come either as a stable frame time that just mismatches in
|
||||
general or (highly) fluctuating frame times.
|
||||
|
||||
* G-sync on modern displays causes known fluctuations because it adjusts dynamically the monitor
|
||||
refresh rate
|
||||
* V-sync not correctly configured/enforced or even vsync-off override in driver is causing
|
||||
inconsistent frame times
|
||||
* Incorrect display timings causing refresh rate fluctuations resulting in inconsistent frame times
|
||||
|
||||
Stable frame times but not having the correct ones that the game engine expects is a different
|
||||
problem that can be addressed by:
|
||||
|
||||
* Knowing the refresh rates expected by the game engine of the different game versions
|
||||
* Configuring your GPU driver's display timings to match the expected frame times
|
||||
* Or, patch the game's chart data with your custom display timings so the game engine gets uses
|
||||
*your* "correct" frame times
|
||||
* Note: Offline patching the chart data doesn't work for several game versions as the monitor
|
||||
check does override this data, but not as flexible as required
|
||||
* Use the chart patch feature of bemanitools instead
|
||||
|
||||
### Micro-stuttering and non-smooth note scrolling
|
||||
|
||||
This is a trickier problem as it can be caused by a few different things.
|
||||
|
||||
* GPU related
|
||||
* Fluctuating refresh rates result in fluctuating frame times in the main game loop
|
||||
* GPU power configuration in non-performance mode (e.g. "power saving") causes processing latency
|
||||
due to sleep states
|
||||
* Display related
|
||||
* Display firmware buggy and/or internal scalers that expect to run at very specific refresh rates
|
||||
and cannot adapt to any custom display timings enforced by the GPU
|
||||
* A known example is the official
|
||||
[PENDUAL LCD monitor](https://github.com/shizmob/arcade-docs/blob/6ac99975cdb2bf668362f65fa9fa3ffb2127308b/konami/product/GULDJ-JI.md#notes-and-known-issues) that does not v-sync correctly to any other refresh-rate than 60.000 hz
|
||||
* CPU related
|
||||
* CPU power configuration on Windows in non-performance mode (e.g. "power saving") causes
|
||||
processing latency due to sleep states
|
||||
* Correct CPU C-states configuration in BIOS/UEFI
|
||||
* These cannot be configured in Windows and are separate from Windows's power configuration
|
||||
* Other stuff running in the background of the system causing fluctuations
|
||||
* (Synchronous) Disk I/O heavy applications stealing CPU time
|
||||
* Or any other process creating a considerable amount of CPU or GPU load
|
||||
|
||||
## Configuration guide for different game versions
|
||||
|
||||
### Measuring monitor refresh rate
|
||||
|
||||
Let's start with configuring the system to meet the game engine's expectation regarding the
|
||||
target monitor refresh rate and requiring vsync.
|
||||
|
||||
First, use the [d3d9-monitor-check tool](../tools/d3d9-monitor-check.md) to measure the currently
|
||||
configured refresh rate of your monitor. Note that your monitor might yield different timings on
|
||||
different resolutions and different game versions run on
|
||||
[different rendering resolutions](../dev/journal/2025-02-09-iidx-engine.md#rendering-resolutions).
|
||||
|
||||
For example, the game version is 31 with the rendering resolution of 1920x1080 in FHD mode that you
|
||||
want to target:
|
||||
|
||||
```bat
|
||||
d3d9-monitor-check.exe cmdline refresh-rate-test 1920 1080 60
|
||||
```
|
||||
|
||||
The test shows the current monitor refresh rate and the average refresh rate over the course of
|
||||
the test. Check that the avg. refresh rate is stable during the measuring phase (during warm-up,
|
||||
it's ok if it fluctuates).
|
||||
|
||||
This test allows you to determine/check:
|
||||
|
||||
* Refresh rate/frame time fluctuations: if you see (high) fluctuations on the first or second
|
||||
decimal place
|
||||
* What's your current target refresh rate the GPU is driving your monitor at
|
||||
* If vsync isn't overridden by some other driver setting as you want to see a refresh rate around
|
||||
the 60 hz mark
|
||||
|
||||
### Measuring vsync issues
|
||||
|
||||
Some displays or the GPU configuration might lead to vsync issues which can be detected with the
|
||||
`d3d9-monitor-check.exe` tool as well.
|
||||
|
||||
```bat
|
||||
d3d9-monitor-check.exe cmdline vsync-test 1920 1080 60
|
||||
```
|
||||
|
||||
This runs a test with a VSYNC text that alternates red and cyan colors. If the text appears grey
|
||||
all the time, everything's fine. If you spot brief red or cyan text either occasionally or
|
||||
periodically, your current configuration has vsync issues resulting in skipped frames or old
|
||||
frames displaying longer.
|
||||
|
||||
### Configuration
|
||||
|
||||
The following are recommended configurations for the different game versions. If any of these
|
||||
don't work as expected, you have to troubleshoot and tweak these potentially. Please refer to the
|
||||
[guidelines above](#configuration-guide-for-different-game-versions) to understand how to use
|
||||
the tooling and test your changes.
|
||||
|
||||
#### Recommended configuration with NVIDIA GPU and driver
|
||||
|
||||
We want to achieve the following configuration on the GPU driver:
|
||||
|
||||
* Turn-off g-sync if you have a monitor that supports it
|
||||
* Disable power saving mode on GPU driver
|
||||
* V-sync is not forced off
|
||||
* Run the display on a target refresh rate of `59.950 hz` which has been determined to be
|
||||
[the most compatible across all PC-based versions](../dev/journal/2025-02-09-iidx-engine.md#engine-step-time).
|
||||
|
||||
##### Turn-off g-sync
|
||||
|
||||
Create a new GPU profile for IIDX, if it doesn't exist yet:
|
||||
|
||||
```bat
|
||||
nvgpu.exe profile create iidx
|
||||
```
|
||||
|
||||
Add the application names when the profile needs to be applied:
|
||||
|
||||
```bat
|
||||
:: For iidx versions 9 to 17
|
||||
nvgpu.exe profile application-add iidx bm2dx.exe
|
||||
:: For iidx versions 18+
|
||||
nvgpu.exe profile application-add iidx launcher.exe
|
||||
```
|
||||
|
||||
Disable G-SYNC for the profile:
|
||||
|
||||
```bat
|
||||
nvgpu.exe profile gsync-disable iidx
|
||||
```
|
||||
|
||||
##### Disable power saving mode
|
||||
|
||||
Set GPU power state to maximum for the profile (assumes you have a
|
||||
[GPU profile called `iidx` already created](#turn-off-g-sync)):
|
||||
|
||||
```bat
|
||||
nvgpu.exe profile gpu-power-state-max iidx
|
||||
```
|
||||
|
||||
##### Configure and verify target refresh rate of 59.950 hz and vsync
|
||||
|
||||
Get display ID of your primary display:
|
||||
|
||||
```bat
|
||||
nvgpu.exe display primary-display-id
|
||||
```
|
||||
|
||||
Copy the hex-value of the display ID and use it in the following commands.
|
||||
|
||||
Test the custom display timing first, e.g. for versions that run in 1920x1080
|
||||
(Replace `<DISPLAY_ID> ` with the primary display ID you got):
|
||||
|
||||
```bat
|
||||
nvgpu.exe display custom-resolution-test <DISPLAY_ID> 1920 1080 59.95 10
|
||||
```
|
||||
|
||||
This will test it for 10 seconds and then revert. If the result looked good, i.e. the screen didn't
|
||||
go blank or the display output was garbage, apply it:
|
||||
|
||||
```bat
|
||||
nvgpu.exe display custom-resolution-set <DISPLAY_ID> 1920 1080 59.95
|
||||
```
|
||||
|
||||
Test and verify the configuration is applied and works correctly:
|
||||
|
||||
```bat
|
||||
d3d9-monitor-check.exe cmdline refresh-rate-test 1920 1080 60
|
||||
```
|
||||
|
||||
Test if the enforced refresh rate is compatible with your display and vsync is working correctly:
|
||||
|
||||
```bat
|
||||
d3d9-monitor-check.exe cmdline vsync-test 1920 1080 60
|
||||
```
|
||||
|
||||
##### Alternative refresh rate configuration
|
||||
|
||||
If for some reason your display doesn't support 59.950 hz properly, i.e. display artifacts or vsync
|
||||
issues, you can use any other 60 hz compatible refresh rate, e.g. anything between 59.900 hz and
|
||||
60.100 hz that your display supports properly.
|
||||
|
||||
Follow the same steps [as above](#configure-and-verify-target-refresh-rate-of-59950-hz-and-vsync)
|
||||
but use a different refresh rate, e.g. 60.000 hz.
|
||||
|
||||
Verify using the `d3d9-monitor-check.exe` with the outlined steps if you need to play around to find
|
||||
a refresh rate that works for your setup.
|
||||
|
||||
For the game versions 9 to 19, you have to use the chart patch feature of bemanitools to patch
|
||||
the charts to your custom refresh rate. Otherwise, the game engine will run on your configured
|
||||
refresh rate, but it will take the non-matching refresh rate from the chart data which causes
|
||||
song de-syncing.
|
||||
|
||||
For example, to configure a refresh rate of 60.000 hz in the iidxhook config file:
|
||||
|
||||
```text
|
||||
gfx.monitor_check=60.000
|
||||
```
|
||||
|
||||
##### ATI/AMD
|
||||
|
||||
Currently, there is no tooling provided by bemanitools to configure the AMD GPU driver. The basic
|
||||
concepts of what needs to be configured still applies as outlined in the NVIDIA sections above
|
||||
though, e.g. through AMD's GPU control panel.
|
||||
|
||||
Further steps and instructions TBD
|
||||
@@ -146,16 +146,7 @@ This can be related to various issues:
|
||||
start "" /relatime "gamestart.bat"
|
||||
```
|
||||
|
||||
- Enforce v-sync enabled in your GPU settings.
|
||||
- Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx) that is not
|
||||
jumping around. Use the timebase feature of one of the newer games to check that or enable
|
||||
iidxhook's timebase and check the log output for the determined value. Run this a few times and
|
||||
check if the results differ.
|
||||
- Use iidxhook's frame rate limiter feature (see further below) to software lock the refresh rate.
|
||||
This might be necessary on Windows 7 and newer for D3D8 games, e.g. iidx 9 to 12, which seem to
|
||||
ignore GPU side v-sync.
|
||||
- Use iidxhook's auto timebase feature (see further below) or set a pre-determined value to cut down
|
||||
start-up times.
|
||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
### The game still stutters (randomly) and drifts off-sync
|
||||
|
||||
@@ -173,9 +164,7 @@ This can be caused by:
|
||||
|
||||
## My songs are offsync
|
||||
|
||||
- Make sure your machine's refresh rate is stable
|
||||
- If you don't get a close to 59.94hz refresh rate, use the software monitor check/auto timebase
|
||||
that's built into iidxhook (refer to help/config file)
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## The game crashes instantly (10th, RED, HAPPY SKY)
|
||||
|
||||
@@ -233,6 +222,8 @@ file) or use a CLVSD.ax codec which has the debugger checks removed.
|
||||
|
||||
## I used the auto timebase option and/or limited my refresh rate but the songs are still going offsync
|
||||
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md) if you haven't already.
|
||||
|
||||
There aren't many options left. The old games were developed for specific hardware and are not
|
||||
guaranteed to work well on (especially) newer hardware. Multiple monitor setups can also have a bad
|
||||
impact on a stable refresh rate. Try a setup with just a single monitor you want to use for gameplay
|
||||
|
||||
@@ -115,16 +115,7 @@ This can be related to various issues:
|
||||
start "" /relatime "gamestart.bat"
|
||||
```
|
||||
|
||||
- Enforce v-sync enabled in your GPU settings.
|
||||
- Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx) that is not
|
||||
jumping around. Use the timebase feature of one of the newer games to check that or enable
|
||||
iidxhook's timebase and check the log output for the determined value. Run this a few times and
|
||||
check if the results differ.
|
||||
- Use iidxhook's frame rate limiter feature (see further below) to software lock the refresh rate.
|
||||
This might be necessary on Windows 7 and newer for D3D8 games, e.g. iidx 9 to 12, which seem to
|
||||
ignore GPU side v-sync.
|
||||
- Use iidxhook's auto timebase feature (see further below) or set a pre-determined value to cut down
|
||||
start-up times.
|
||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
### The game still stutters (randomly) and drifts off-sync
|
||||
|
||||
@@ -142,9 +133,7 @@ This can be caused by:
|
||||
|
||||
## My songs are offsync
|
||||
|
||||
- Make sure your machine's refresh rate is stable
|
||||
- If you don't get a close to 59.94hz refresh rate, use the software monitor check/auto timebase
|
||||
that's built into iidxhook (refer to help/config file)
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## The game crashes instantly
|
||||
|
||||
@@ -192,6 +181,8 @@ CLVSD.ax codec which has the debugger checks removed.
|
||||
|
||||
## I used the auto timebase option and/or limited my refresh rate but the songs are still going offsync
|
||||
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md) if you haven't already.
|
||||
|
||||
There aren't many options left. The old games were developed for specific hardware and are not
|
||||
guaranteed to work well on (especially) newer hardware. Multiple monitor setups can also have a bad
|
||||
impact on a stable refresh rate. Try a setup with just a single monitor you want to use for gameplay
|
||||
|
||||
@@ -132,16 +132,7 @@ This can be related to various issues:
|
||||
start "" /relatime "gamestart.bat"
|
||||
```
|
||||
|
||||
- Enforce v-sync enabled in your GPU settings.
|
||||
- Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx) that is not
|
||||
jumping around. Use the timebase feature of one of the newer games to check that or enable
|
||||
iidxhook's timebase and check the log output for the determined value. Run this a few times and
|
||||
check if the results differ.
|
||||
- Use iidxhook's frame rate limiter feature (see further below) to software lock the refresh rate.
|
||||
This might be necessary on Windows 7 and newer for D3D8 games, e.g. iidx 9 to 12, which seem to
|
||||
ignore GPU side v-sync.
|
||||
- Use iidxhook's auto timebase feature (see further below) or set a pre-determined value to cut down
|
||||
start-up times.
|
||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||
|
||||
@@ -154,15 +145,7 @@ This can be caused by:
|
||||
|
||||
## My songs are offsync
|
||||
|
||||
The built-in monitor check just determines if the game should sync to either 59.94 hz (S-Video
|
||||
setting) or 60.04 hz (VGA setting). If you don't have a setup that runs on (as close as possible)
|
||||
these values:
|
||||
|
||||
- Make sure your machine's refresh rate is stable, e.g. 60.00x hz.
|
||||
- If you don't get a close to 59.94hz (S-Video setting) or 60.04 hz (VGA setting) refresh rate, go
|
||||
an set the output mode in the operator menu to "VGA" to enforce the game to run chart syncing on
|
||||
60.04 hz refresh rate (even if your setup does not have that value). Next, use the software
|
||||
monitor check/auto timebase that's built into iidxhook (refer to cmd help/configfile).
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## My game runs too fast
|
||||
|
||||
@@ -205,6 +188,8 @@ CLVSD.ax codec which has the debugger checks removed.
|
||||
|
||||
## I used the auto timebase option and/or limited my refresh rate but the songs are still going offsync
|
||||
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md) if you haven't already.
|
||||
|
||||
There aren't many options left. The old games were developed for specific hardware and are not
|
||||
guaranteed to work well on (especially) newer hardware. Multiple monitor setups can also have a bad
|
||||
impact on a stable refresh rate. Try a setup with just a single monitor you want to use for gameplay
|
||||
|
||||
@@ -143,16 +143,7 @@ This can be related to various issues:
|
||||
start "" /relatime "gamestart.bat"
|
||||
```
|
||||
|
||||
- Enforce v-sync enabled in your GPU settings.
|
||||
- Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx) that is not
|
||||
jumping around. Use the timebase feature of one of the newer games to check that or enable
|
||||
iidxhook's timebase and check the log output for the determined value. Run this a few times and
|
||||
check if the results differ.
|
||||
- Use iidxhook's frame rate limiter feature (see further below) to software lock the refresh rate.
|
||||
This might be necessary on Windows 7 and newer for D3D8 games, e.g. iidx 9 to 12, which seem to
|
||||
ignore GPU side v-sync.
|
||||
- Use iidxhook's auto timebase feature (see further below) or set a pre-determined value to cut down
|
||||
start-up times.
|
||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||
|
||||
@@ -165,15 +156,7 @@ This can be caused by:
|
||||
|
||||
## My songs are offsync
|
||||
|
||||
The built-in monitor check just determines if the game should sync to either 59.94 hz (S-Video
|
||||
setting) or 60.04 hz (VGA setting). If you don't have a setup that runs on (as close as possible)
|
||||
these values:
|
||||
|
||||
- Make sure your machine's refresh rate is stable, e.g. 60.00x hz.
|
||||
- If you don't get a close to 59.94hz (S-Video setting) or 60.04 hz (VGA setting) refresh rate, go
|
||||
an set the output mode in the operator menu to "VGA" to enforce the game to run chart syncing on
|
||||
60.04 hz refresh rate (even if your setup does not have that value). Next, use the software
|
||||
monitor check/auto timebase that's built into iidxhook (refer to cmd help/configfile).
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## My game runs too fast
|
||||
|
||||
@@ -211,6 +194,8 @@ CLVSD.ax codec which has the debugger checks removed.
|
||||
|
||||
## I used the auto timebase option and/or limited my refresh rate but the songs are still going offsync
|
||||
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md) if you haven't already.
|
||||
|
||||
There aren't many options left. The old games were developed for specific hardware and are not
|
||||
guaranteed to work well on (especially) newer hardware. Multiple monitor setups can also have a bad
|
||||
impact on a stable refresh rate. Try a setup with just a single monitor you want to use for gameplay
|
||||
|
||||
@@ -143,16 +143,7 @@ This can be related to various issues:
|
||||
start "" /relatime "gamestart.bat"
|
||||
```
|
||||
|
||||
- Enforce v-sync enabled in your GPU settings.
|
||||
- Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx) that is not
|
||||
jumping around. Use the timebase feature of one of the newer games to check that or enable
|
||||
iidxhook's timebase and check the log output for the determined value. Run this a few times and
|
||||
check if the results differ.
|
||||
- Use iidxhook's frame rate limiter feature (see further below) to software lock the refresh rate.
|
||||
This might be necessary on Windows 7 and newer for D3D8 games, e.g. iidx 9 to 12, which seem to
|
||||
ignore GPU side v-sync.
|
||||
- Use iidxhook's auto timebase feature (see further below) or set a pre-determined value to cut down
|
||||
start-up times.
|
||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||
|
||||
@@ -165,11 +156,7 @@ This can be caused by:
|
||||
|
||||
## My songs are offsync
|
||||
|
||||
From this version onwards (if you use the very final data of Lincle), the game comes with a built-in
|
||||
auto timebase option ("monitor check" on startup) which dynamically, detects the refresh rate of
|
||||
your current setup. Thus, BT5's timebase option is not included from this hook version onwards,
|
||||
anymore. Ensure that refresh rate displayed is very stable, e.g. 60.00x hz, and the game should be
|
||||
able to provide you with a smooth and sync game experience.
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## My game runs too fast
|
||||
|
||||
|
||||
@@ -155,11 +155,7 @@ This can be related to various issues:
|
||||
start "" /relatime "gamestart.bat"
|
||||
```
|
||||
|
||||
- Enforce v-sync enabled in your GPU settings.
|
||||
- Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx) that is not
|
||||
jumping around. Use the timebase feature of one of the newer games to check that or enable
|
||||
iidxhook's timebase and check the log output for the determined value. Run this a few times and
|
||||
check if the results differ.
|
||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||
|
||||
@@ -172,11 +168,7 @@ This can be caused by:
|
||||
|
||||
## My songs are offsync
|
||||
|
||||
From this version onwards (or Lincle very final revision), the game comes with a built-in auto
|
||||
timebase option ("monitor check" on startup) which dynamically, detects the refresh rate of your
|
||||
current setup. Thus, BT5's timebase option is not included from this hook version onwards, anymore.
|
||||
Ensure that refresh rate displayed is very stable, e.g. 60.00x hz, and the game should be able to
|
||||
provide you with a smooth and sync game experience.
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## My game runs too fast
|
||||
|
||||
|
||||
@@ -159,11 +159,7 @@ This can be related to various issues:
|
||||
start "" /relatime "gamestart.bat"
|
||||
```
|
||||
|
||||
- Enforce v-sync enabled in your GPU settings.
|
||||
- Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx) that is not
|
||||
jumping around. Use the timebase feature of one of the newer games to check that or enable
|
||||
iidxhook's timebase and check the log output for the determined value. Run this a few times and
|
||||
check if the results differ.
|
||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||
|
||||
@@ -176,11 +172,7 @@ This can be caused by:
|
||||
|
||||
## My songs are offsync
|
||||
|
||||
From IIDX 20 (or Lincle very final revision) onwards, the game comes with a built-in auto timebase
|
||||
option ("monitor check" on startup) which dynamically, detects the refresh rate of your current
|
||||
setup. Thus, BT5's timebase option is not included from this hook version onwards, anymore. Ensure
|
||||
that refresh rate displayed is very stable, e.g. 60.00x hz, and the game should be able to provide
|
||||
you with a smooth and sync game experience.
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## My game runs too fast
|
||||
|
||||
|
||||
@@ -158,11 +158,7 @@ This can be related to various issues:
|
||||
start "" /relatime "gamestart.bat"
|
||||
```
|
||||
|
||||
- Enforce v-sync enabled in your GPU settings.
|
||||
- Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx) that is not
|
||||
jumping around. Use the timebase feature of one of the newer games to check that or enable
|
||||
iidxhook's timebase and check the log output for the determined value. Run this a few times and
|
||||
check if the results differ.
|
||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||
|
||||
@@ -175,11 +171,7 @@ This can be caused by:
|
||||
|
||||
## My songs are offsync
|
||||
|
||||
From IIDX 20 (or Lincle very final revision) onwards, the game comes with a built-in auto timebase
|
||||
option ("monitor check" on startup) which dynamically, detects the refresh rate of your current
|
||||
setup. Thus, BT5's timebase option is not included from this hook version onwards, anymore. Ensure
|
||||
that refresh rate displayed is very stable, e.g. 60.00x hz, and the game should be able to provide
|
||||
you with a smooth and sync game experience.
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## My game runs too fast
|
||||
|
||||
|
||||
@@ -205,11 +205,7 @@ This can be related to various issues:
|
||||
start "" /relatime "gamestart.bat"
|
||||
```
|
||||
|
||||
- Enforce v-sync enabled in your GPU settings.
|
||||
- Ensure that you have a constant refresh rate around the 60 hz (59.9xx or 60.0xx) that is not
|
||||
jumping around. Use the timebase feature of one of the newer games to check that or enable
|
||||
iidxhook's timebase and check the log output for the determined value. Run this a few times and
|
||||
check if the results differ.
|
||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||
|
||||
@@ -222,11 +218,7 @@ This can be caused by:
|
||||
|
||||
## My songs are offsync
|
||||
|
||||
From IIDX 20 (or Lincle very final revision) onwards, the game comes with a built-in auto timebase
|
||||
option ("monitor check" on startup) which dynamically, detects the refresh rate of your current
|
||||
setup. Thus, BT5's timebase option is not included from this hook version onwards, anymore. Ensure
|
||||
that refresh rate displayed is very stable, e.g. 60.00x hz, and the game should be able to provide
|
||||
you with a smooth and sync game experience.
|
||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
||||
|
||||
## My game runs too fast
|
||||
|
||||
|
||||
@@ -220,9 +220,6 @@ 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,8 +25,6 @@ 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,8 +28,6 @@ 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,8 +26,6 @@ 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,8 +25,6 @@ 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,8 +25,6 @@ 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,8 +24,6 @@ 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,8 +26,6 @@ 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,8 +19,6 @@ 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,8 +19,6 @@ 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,8 +21,6 @@ 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,8 +25,6 @@ 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,8 +26,6 @@ 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,8 +26,6 @@ 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,8 +19,6 @@ 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,14 +19,12 @@ 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 == XCnbrep70000a4
|
||||
property_node_clone @165 NONAME
|
||||
property_node_traversal @167 NONAME
|
||||
property_node_refdata @166 NONAME == XCnbrep70000a5
|
||||
property_node_datasize @171 NONAME == XCnbrep70000aa
|
||||
|
||||
@@ -21,8 +21,6 @@ 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
|
||||
|
||||
@@ -96,14 +96,4 @@ 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,6 +5,4 @@
|
||||
|
||||
const char *avs_util_error_str(avs_error error);
|
||||
|
||||
const char *avs_util_property_error_get_and_clear(struct property *prop);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
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 \
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#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
|
||||
@@ -1,129 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
#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
|
||||
@@ -1,23 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#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
|
||||
@@ -1,23 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#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
|
||||
@@ -1,92 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#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
|
||||
@@ -1,66 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#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
|
||||
@@ -1,53 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#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
|
||||
@@ -1,21 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#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
|
||||
@@ -1,193 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#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
|
||||
@@ -1,45 +0,0 @@
|
||||
#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
|
||||
@@ -1,74 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
#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
|
||||
@@ -1,8 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#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
|
||||
@@ -1,62 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#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
|
||||
@@ -1,78 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
#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,21 +15,12 @@ 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);
|
||||
|
||||
@@ -53,23 +44,6 @@ 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,
|
||||
@@ -99,35 +73,6 @@ 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,
|
||||
@@ -156,33 +101,6 @@ 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