mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-09-15 11:55:10 -05:00
Compare commits
3 Commits
latest
...
example_li
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a00e3b82c7 | ||
|
|
2a71845df7 | ||
|
|
58bf2e6bb8 |
2
.github/workflows/build-tag.yaml
vendored
2
.github/workflows/build-tag.yaml
vendored
@@ -49,7 +49,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# This already extracts the contents of the artifact
|
# This already extracts the contents of the artifact
|
||||||
- name: "Download artifact"
|
- name: "Download artifact"
|
||||||
uses: "actions/download-artifact@v4"
|
uses: "actions/download-artifact@v2"
|
||||||
with:
|
with:
|
||||||
name: bemanitools-${{ github.sha }}
|
name: bemanitools-${{ github.sha }}
|
||||||
|
|
||||||
|
|||||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -7,27 +7,8 @@ given in tags. The pipeline will pick this up and cuts out the relevant section
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- Major overhaul of [IIDX sync and performance settings](doc/iidxhook/iidx-syncbook.md)
|
|
||||||
documentation. Supports users in configuring their systems to run any IIDX version with proper
|
|
||||||
display timings, smooth frame rates and sync game-play
|
|
||||||
- Add `iidxio-async.dll` wrapper/shim library to drive another iidxio in a dedicated IO thread, see
|
|
||||||
[documentation](doc/iidxhook/iidxio-async.md) for details
|
|
||||||
- Add `nvgpu` tool to configure NVIDIA GPUs and create a custom timing profile, see
|
|
||||||
[documentation](doc/tools/nvgpu.md) for details
|
|
||||||
- Add `d3d9-frame-graph-hook.dll` that can be added as a hook to
|
|
||||||
either `launcher.exe` or `inject.exe` to display a graph of the frame times of the game's render
|
|
||||||
for debugging and performance analysis
|
|
||||||
- feat: Add `d3d9-monitor-check` tool to run different music game relevant monitor tests including
|
|
||||||
a re-implementation of the infamous IIDX “monitor check” as a separate tool for testing and
|
|
||||||
debugging. See [documentation](doc/tools/d3d9-monitor-check.md) for details
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
- iidxhook9: Work around reverb issue from Windows 26100.3476
|
|
||||||
- iidxhook9: fix camhook for new style camera detection
|
|
||||||
- `cam.port_layout` added to .conf files of iidx 27 to 30
|
|
||||||
- In general, a lot of internal cleanup
|
|
||||||
|
|
||||||
## 5.48
|
## 5.48
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ include src/main/iidxio-async/Module.mk
|
|||||||
include src/main/iidxio-bio2/Module.mk
|
include src/main/iidxio-bio2/Module.mk
|
||||||
include src/main/iidxio-ezusb/Module.mk
|
include src/main/iidxio-ezusb/Module.mk
|
||||||
include src/main/iidxio-ezusb2/Module.mk
|
include src/main/iidxio-ezusb2/Module.mk
|
||||||
|
include src/main/iidxio-lightning/Module.mk
|
||||||
include src/main/iidxio/Module.mk
|
include src/main/iidxio/Module.mk
|
||||||
include src/main/iidxiotest/Module.mk
|
include src/main/iidxiotest/Module.mk
|
||||||
include src/main/imgui/Module.mk
|
include src/main/imgui/Module.mk
|
||||||
@@ -195,6 +196,8 @@ include src/main/sdvxhook2-cn/Module.mk
|
|||||||
include src/main/sdvxhook2/Module.mk
|
include src/main/sdvxhook2/Module.mk
|
||||||
include src/main/sdvxio-bio2/Module.mk
|
include src/main/sdvxio-bio2/Module.mk
|
||||||
include src/main/sdvxio-kfca/Module.mk
|
include src/main/sdvxio-kfca/Module.mk
|
||||||
|
include src/main/sdvxio-tbs/Module.mk
|
||||||
|
include src/main/sdvxio-valk/Module.mk
|
||||||
include src/main/sdvxio/Module.mk
|
include src/main/sdvxio/Module.mk
|
||||||
include src/main/security/Module.mk
|
include src/main/security/Module.mk
|
||||||
include src/main/unicorntail/Module.mk
|
include src/main/unicorntail/Module.mk
|
||||||
@@ -490,6 +493,7 @@ $(zipdir)/iidx-hwio-x64.zip: \
|
|||||||
build/bin/indep-64/iidxio-bio2.dll \
|
build/bin/indep-64/iidxio-bio2.dll \
|
||||||
build/bin/indep-64/iidxio-ezusb.dll \
|
build/bin/indep-64/iidxio-ezusb.dll \
|
||||||
build/bin/indep-64/iidxio-ezusb2.dll \
|
build/bin/indep-64/iidxio-ezusb2.dll \
|
||||||
|
build/bin/indep-64/iidxio-lightning.dll \
|
||||||
dist/iidx/iidxio-bio2.conf \
|
dist/iidx/iidxio-bio2.conf \
|
||||||
build/bin/indep-64/vigem-iidxio.exe \
|
build/bin/indep-64/vigem-iidxio.exe \
|
||||||
dist/iidx/vigem-iidxio.conf \
|
dist/iidx/vigem-iidxio.conf \
|
||||||
@@ -644,6 +648,8 @@ $(zipdir)/sdvx-hwio-x64.zip: \
|
|||||||
build/bin/indep-64/eamio-icca.dll \
|
build/bin/indep-64/eamio-icca.dll \
|
||||||
build/bin/indep-64/sdvxio-kfca.dll \
|
build/bin/indep-64/sdvxio-kfca.dll \
|
||||||
build/bin/indep-64/sdvxio-bio2.dll \
|
build/bin/indep-64/sdvxio-bio2.dll \
|
||||||
|
build/bin/indep-64/sdvxio-tbs.dll \
|
||||||
|
build/bin/indep-64/sdvxio-valk.dll \
|
||||||
build/bin/indep-64/vigem-sdvxio.exe \
|
build/bin/indep-64/vigem-sdvxio.exe \
|
||||||
| $(zipdir)/
|
| $(zipdir)/
|
||||||
$(V)echo ... $@
|
$(V)echo ... $@
|
||||||
|
|||||||
@@ -1,319 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
# 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,7 +146,16 @@ This can be related to various issues:
|
|||||||
start "" /relatime "gamestart.bat"
|
start "" /relatime "gamestart.bat"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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.
|
||||||
|
|
||||||
### The game still stutters (randomly) and drifts off-sync
|
### The game still stutters (randomly) and drifts off-sync
|
||||||
|
|
||||||
@@ -164,7 +173,9 @@ This can be caused by:
|
|||||||
|
|
||||||
## My songs are offsync
|
## My songs are offsync
|
||||||
|
|
||||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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)
|
||||||
|
|
||||||
## The game crashes instantly (10th, RED, HAPPY SKY)
|
## The game crashes instantly (10th, RED, HAPPY SKY)
|
||||||
|
|
||||||
@@ -222,8 +233,6 @@ 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
|
## 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
|
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
|
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
|
impact on a stable refresh rate. Try a setup with just a single monitor you want to use for gameplay
|
||||||
|
|||||||
@@ -115,7 +115,16 @@ This can be related to various issues:
|
|||||||
start "" /relatime "gamestart.bat"
|
start "" /relatime "gamestart.bat"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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.
|
||||||
|
|
||||||
### The game still stutters (randomly) and drifts off-sync
|
### The game still stutters (randomly) and drifts off-sync
|
||||||
|
|
||||||
@@ -133,7 +142,9 @@ This can be caused by:
|
|||||||
|
|
||||||
## My songs are offsync
|
## My songs are offsync
|
||||||
|
|
||||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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)
|
||||||
|
|
||||||
## The game crashes instantly
|
## The game crashes instantly
|
||||||
|
|
||||||
@@ -181,8 +192,6 @@ 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
|
## 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
|
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
|
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
|
impact on a stable refresh rate. Try a setup with just a single monitor you want to use for gameplay
|
||||||
|
|||||||
@@ -132,7 +132,16 @@ This can be related to various issues:
|
|||||||
start "" /relatime "gamestart.bat"
|
start "" /relatime "gamestart.bat"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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.
|
||||||
|
|
||||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||||
|
|
||||||
@@ -145,7 +154,15 @@ This can be caused by:
|
|||||||
|
|
||||||
## My songs are offsync
|
## My songs are offsync
|
||||||
|
|
||||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
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).
|
||||||
|
|
||||||
## My game runs too fast
|
## My game runs too fast
|
||||||
|
|
||||||
@@ -188,8 +205,6 @@ 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
|
## 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
|
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
|
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
|
impact on a stable refresh rate. Try a setup with just a single monitor you want to use for gameplay
|
||||||
|
|||||||
@@ -143,7 +143,16 @@ This can be related to various issues:
|
|||||||
start "" /relatime "gamestart.bat"
|
start "" /relatime "gamestart.bat"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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.
|
||||||
|
|
||||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||||
|
|
||||||
@@ -156,7 +165,15 @@ This can be caused by:
|
|||||||
|
|
||||||
## My songs are offsync
|
## My songs are offsync
|
||||||
|
|
||||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
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).
|
||||||
|
|
||||||
## My game runs too fast
|
## My game runs too fast
|
||||||
|
|
||||||
@@ -194,8 +211,6 @@ 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
|
## 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
|
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
|
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
|
impact on a stable refresh rate. Try a setup with just a single monitor you want to use for gameplay
|
||||||
|
|||||||
@@ -143,7 +143,16 @@ This can be related to various issues:
|
|||||||
start "" /relatime "gamestart.bat"
|
start "" /relatime "gamestart.bat"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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.
|
||||||
|
|
||||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||||
|
|
||||||
@@ -156,7 +165,11 @@ This can be caused by:
|
|||||||
|
|
||||||
## My songs are offsync
|
## My songs are offsync
|
||||||
|
|
||||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
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.
|
||||||
|
|
||||||
## My game runs too fast
|
## My game runs too fast
|
||||||
|
|
||||||
|
|||||||
@@ -155,7 +155,11 @@ This can be related to various issues:
|
|||||||
start "" /relatime "gamestart.bat"
|
start "" /relatime "gamestart.bat"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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.
|
||||||
|
|
||||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||||
|
|
||||||
@@ -168,7 +172,11 @@ This can be caused by:
|
|||||||
|
|
||||||
## My songs are offsync
|
## My songs are offsync
|
||||||
|
|
||||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
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.
|
||||||
|
|
||||||
## My game runs too fast
|
## My game runs too fast
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,11 @@ This can be related to various issues:
|
|||||||
start "" /relatime "gamestart.bat"
|
start "" /relatime "gamestart.bat"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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.
|
||||||
|
|
||||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||||
|
|
||||||
@@ -172,7 +176,11 @@ This can be caused by:
|
|||||||
|
|
||||||
## My songs are offsync
|
## My songs are offsync
|
||||||
|
|
||||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
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.
|
||||||
|
|
||||||
## My game runs too fast
|
## My game runs too fast
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,11 @@ This can be related to various issues:
|
|||||||
start "" /relatime "gamestart.bat"
|
start "" /relatime "gamestart.bat"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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.
|
||||||
|
|
||||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||||
|
|
||||||
@@ -171,7 +175,11 @@ This can be caused by:
|
|||||||
|
|
||||||
## My songs are offsync
|
## My songs are offsync
|
||||||
|
|
||||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
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.
|
||||||
|
|
||||||
## My game runs too fast
|
## My game runs too fast
|
||||||
|
|
||||||
|
|||||||
@@ -212,7 +212,11 @@ This can be related to various issues:
|
|||||||
start "" /relatime "gamestart.bat"
|
start "" /relatime "gamestart.bat"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also refer to the [iidx-syncbook](iidx-syncbook.md).
|
- 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.
|
||||||
|
|
||||||
## "NETWORK WARNING" instead of "NETWORK OK"
|
## "NETWORK WARNING" instead of "NETWORK OK"
|
||||||
|
|
||||||
@@ -225,7 +229,11 @@ This can be caused by:
|
|||||||
|
|
||||||
## My songs are offsync
|
## My songs are offsync
|
||||||
|
|
||||||
Refer to the [iidx-syncbook](iidx-syncbook.md).
|
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.
|
||||||
|
|
||||||
## My game runs too fast
|
## My game runs too fast
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
# D3D9 Monitor Check
|
# D3D9 Monitor Check
|
||||||
|
|
||||||
A separate application providing various music game relevant tests, e.g. the infamous IIDX “monitor
|
A separate application to run the infamous IIDX “monitor check” without having to run the actual
|
||||||
check” without having to run the actual game. The tool can be used to debug and test your monitor
|
game. The tool can be used to test measure the current avg. monitor refresh rate or debug/check if
|
||||||
regarding refresh rate calibration (custom monitor timings), v-sync issues or ghosting issues.
|
that value is fluctuating for some reason.
|
||||||
|
|
||||||
|
The final avg. value that is provided at the end of the test can be used as input for other tooling
|
||||||
|
or settings (e.g. patching charts to a different refresh rate on older games with bemanitools).
|
||||||
|
|
||||||
Simply run the tool without any arguments to get a full synopsis with usage instructions.
|
Simply run the tool without any arguments to get a full synopsis with usage instructions.
|
||||||
|
|
||||||
## "Accuracy" remarks refresh rate test/monitor check
|
## "Accuracy" remarks
|
||||||
|
|
||||||
The tool has been tested on an actual cabinet with `nvgpu` setting different custom timings. The
|
The tool has been tested on an actual cabinet with `nvgpu` setting different custom timings. The
|
||||||
accuracy seems to be even higher than what IIDX’s monitor check is actually showing. For example,
|
accuracy seems to be even higher than what IIDX’s monitor check is actually showing. For example,
|
||||||
with a custom timing of 59.900 hz, this tool yields fairly accurate and stable avg. 59.902 hz.
|
with a custom timing of 59.900 hz, this tool yields fairly accurate and stable avg. 59.902 hz.
|
||||||
|
|
||||||
The monitor check of IIDX 29 showed results of 59.8981 hz to 59.8997 hz on screen. As these are the
|
The monitor check of IIDX 29 shows results of 59.8981 hz to 59.8997 hz on screen. As these are the
|
||||||
only visible values to the user, determining a specific (avg.) value that can be used as input for
|
only visible values to the user, determining a specific (avg.) value that can be used as input for
|
||||||
other tooling or settings (e.g. patching charts to a different refresh rate on older games with
|
other tooling or settings (e.g. patching charts to a different refresh rate on older games with
|
||||||
bemanitools) is difficult. This doesn't mean that the game's monitor checks are actually
|
bemanitools) is difficult. This doesn't mean that the game's monitor checks are actually
|
||||||
inaccurate or wrong. Once the check is finished it uses the
|
inaccurate or wrong. Modern games with a built-in monitor check (starting IIDX 20) are syncing up
|
||||||
[avg. value it determined](../dev/journal/2025-02-09-iidx-engine.md) to patch the chart data
|
|
||||||
correctly. The value displayed to the user is just the floating value.
|
|
||||||
|
|
||||||
Thus, modern games with a built-in monitor check (starting IIDX 20) are syncing up
|
|
||||||
fine and don't need any further patching or modifications.
|
fine and don't need any further patching or modifications.
|
||||||
|
|
||||||
For older games, picking a value that is not as close as possible to an accurate avg. value can
|
For older games, picking a value that is not as close as possible to an accurate avg. value can
|
||||||
|
|||||||
@@ -18,8 +18,61 @@ cannot be measured on application level.
|
|||||||
|
|
||||||
Simply run the tool without any arguments to get a full synopsis of available commands.
|
Simply run the tool without any arguments to get a full synopsis of available commands.
|
||||||
|
|
||||||
## Example usage for IIDX
|
## Example usage for modern IIDX
|
||||||
|
|
||||||
Refer to the dedicated [iidx-syncbook](../iidxhook/iidx-syncbook.md) documentation for details on
|
### Custom GPU profile
|
||||||
how to use `nvgpu` to configure your system to run IIDX versions with proper display timings, smooth
|
|
||||||
frame rates and sync game-play.
|
The following creates a custom profile to address potential performance concerns such as not 100% smooth scrolling and
|
||||||
|
micro stuttering.
|
||||||
|
|
||||||
|
* Create a custom profile: `nvgpu profile create launcher`
|
||||||
|
* Add the launcher application to the profile: `nvgpu profile application-add launcher launcher.exe`
|
||||||
|
* This will apply to any (IIDX) game running with `launcher.exe`
|
||||||
|
* Set GPU power state to maximum for the profile: `nvgpu profile gpu-power-state-max launcher`
|
||||||
|
|
||||||
|
### Custom timing
|
||||||
|
|
||||||
|
* Run the game and observe the monitor check screen (requires IIDX 20+)
|
||||||
|
* Take note of the refresh rate of the monitor that is determined by the game
|
||||||
|
* Exit the game
|
||||||
|
* Run `nvgpu display list` to get the display ID of the monitor you want to change use that ID in the following commands
|
||||||
|
* Run `nvgpu display custom-resolution-test` with your display ID and monitor settings to test the custom configuration
|
||||||
|
first
|
||||||
|
* For example, for IIDX 31 which runs in native 1920x1080 with a monitor also having that as its native resolution,
|
||||||
|
having the monitor id `0x12345678` and the monitor check yielding a value of ~`59.9345`, run
|
||||||
|
`nvgpu display custom-resolution-test 1920 1080 59.9345`
|
||||||
|
* Observe if the test is successful and the display doesn't turn blank or displays a glitched image for ~10 seconds
|
||||||
|
* Run `nvgpu display custom-resolution-set` with the previously tested settings to apply the custom display mode
|
||||||
|
* For example, `nvgpu display custom-resolution-set 0x12345678 1920 1080 59.9345`
|
||||||
|
|
||||||
|
## Example usage for legacy IIDX
|
||||||
|
|
||||||
|
Legacy IIDX concerns any game prior to IIDX 20 that introduced the monitor check screen. The game engine was expecting
|
||||||
|
a the display/GPU/driver to perform at specific refresh rate timings in order to provide correct timing and audio
|
||||||
|
playback for the game. To this date (i.e. IIDX 31), the game engine never re-syncs audio playback during gameplay.
|
||||||
|
Therefore any flaky and incorrect timing will result in audio desynchronization either early on or throughout a song.
|
||||||
|
|
||||||
|
### Custom GPU profile
|
||||||
|
|
||||||
|
The following creates a custom profile to address potential performance concerns such as not 100% smooth scrolling and
|
||||||
|
micro stuttering.
|
||||||
|
|
||||||
|
* Create a custom profile: `nvgpu profile create bm2dx`
|
||||||
|
* Add the launcher application to the profile: `nvgpu profile application-add bm2dx bm2dx.exe`
|
||||||
|
* This will apply to any (IIDX) game running with an executable named `bm2fx.exe`
|
||||||
|
* Set GPU power state to maximum for the profile: `nvgpu profile gpu-power-state-max bm2dx`
|
||||||
|
|
||||||
|
### Custom timing
|
||||||
|
|
||||||
|
* Use a modern game and it to observe the monitor check screen (requires IIDX 20+)
|
||||||
|
* Take note of the refresh rate of the monitor that is determined by the game
|
||||||
|
* Exit the game
|
||||||
|
* Run `nvgpu display list` to get the display ID of the monitor you want to change use that ID in the following commands
|
||||||
|
* Run `nvgpu display custom-resolution-test` with your display ID and monitor settings to test the custom configuration
|
||||||
|
first
|
||||||
|
* For example, for IIDX 31 which runs in native 1920x1080 with a monitor also having that as its native resolution,
|
||||||
|
having the monitor id `0x12345678` and the monitor check yielding a value of ~`59.9345`, run
|
||||||
|
`nvgpu display custom-resolution-test 1920 1080 59.9345`
|
||||||
|
* Observe if the test is successful and the display doesn't turn blank or displays a glitched image for ~10 seconds
|
||||||
|
* Run `nvgpu display custom-resolution-set` with the previously tested settings to apply the custom display mode
|
||||||
|
* For example, `nvgpu display custom-resolution-set 0x12345678 1920 1080 59.9345`
|
||||||
12
src/main/iidxio-lightning/Module.mk
Normal file
12
src/main/iidxio-lightning/Module.mk
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
dlls += iidxio-lightning
|
||||||
|
|
||||||
|
ldflags_iidxio-lightning := \
|
||||||
|
-lsetupapi \
|
||||||
|
|
||||||
|
libs_iidxio-lightning := \
|
||||||
|
cconfig \
|
||||||
|
util \
|
||||||
|
|
||||||
|
src_iidxio-lightning := \
|
||||||
|
iidxio.c \
|
||||||
|
load_aio.c \
|
||||||
48
src/main/iidxio-lightning/aio.h
Normal file
48
src/main/iidxio-lightning/aio.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct AIO_NODE;
|
||||||
|
struct AIO_NMGR;
|
||||||
|
struct AIO_SCI;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct AIO_IOB2_BI2X_TDJ__DEVSTATUS {
|
||||||
|
uint8_t unk1[4];
|
||||||
|
uint8_t b_test;
|
||||||
|
uint8_t b_service;
|
||||||
|
uint8_t b_coinmech;
|
||||||
|
uint8_t unk2;
|
||||||
|
uint8_t b_start[2];
|
||||||
|
uint8_t b_vefx;
|
||||||
|
uint8_t b_effect;
|
||||||
|
uint8_t b_headphone[2];
|
||||||
|
|
||||||
|
uint8_t unk3[6]; // 0x10-0x13
|
||||||
|
uint8_t a_turntable[2];
|
||||||
|
uint8_t unk4;
|
||||||
|
uint8_t unk6[4];
|
||||||
|
uint8_t b_p1[7];
|
||||||
|
uint8_t b_p2[7];
|
||||||
|
uint8_t unk7[7];
|
||||||
|
|
||||||
|
uint8_t unk8[0x9A]; // 0x30-0xC9
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
_Static_assert(
|
||||||
|
sizeof(struct AIO_IOB2_BI2X_TDJ__DEVSTATUS) == 202,
|
||||||
|
"bi2x_device_status is the wrong size");
|
||||||
|
|
||||||
|
typedef void (*t_aioNodeCtl_UpdateDevicesStatus)(void);
|
||||||
|
typedef unsigned int (*t_aioNodeCtl_Destroy)(struct AIO_NODE *node);
|
||||||
|
typedef unsigned int (*t_aioNodeMgr_Destroy)(struct AIO_NMGR *mgr);
|
||||||
|
typedef unsigned int (*t_aioSci_Destroy)(struct AIO_SCI *sci);
|
||||||
|
|
||||||
|
typedef struct AIO_SCI* (*t_aioIob2Bi2x_OpenSciUsbCdc)(uint8_t unk);
|
||||||
|
typedef struct AIO_NMGR* (*t_aioNMgrIob2_Create)(struct AIO_SCI *port, unsigned int delay);
|
||||||
|
|
||||||
|
typedef struct AIO_NODE* (*t_aioIob2Bi2xTDJ_Create)(struct AIO_NMGR *node_mgr, uint8_t param);
|
||||||
|
typedef void (*t_aioIob2Bi2xTDJ_IoReset)(struct AIO_NODE *AIO_IOB2_BI2X_TDJ_this, int value);
|
||||||
|
typedef void (*t_AIO_IOB2_BI2X_TDJ__GetDeviceStatus)(struct AIO_NODE *AIO_IOB2_BI2X_TDJ_this, struct AIO_IOB2_BI2X_TDJ__DEVSTATUS *status);
|
||||||
|
typedef void (*t_AIO_IOB2_BI2X_TDJ__SetTurnTableResist)(struct AIO_NODE *AIO_IOB2_BI2X_TDJ_this, int side, uint8_t resist);
|
||||||
18
src/main/iidxio-lightning/iidxio-lightning.def
Normal file
18
src/main/iidxio-lightning/iidxio-lightning.def
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
LIBRARY iidxio
|
||||||
|
|
||||||
|
EXPORTS
|
||||||
|
iidx_io_ep1_send
|
||||||
|
iidx_io_ep1_set_deck_lights
|
||||||
|
iidx_io_ep1_set_panel_lights
|
||||||
|
iidx_io_ep1_set_top_lamps
|
||||||
|
iidx_io_ep1_set_top_neons
|
||||||
|
iidx_io_ep2_get_keys
|
||||||
|
iidx_io_ep2_get_panel
|
||||||
|
iidx_io_ep2_get_sys
|
||||||
|
iidx_io_ep2_get_slider
|
||||||
|
iidx_io_ep2_get_turntable
|
||||||
|
iidx_io_ep2_recv
|
||||||
|
iidx_io_ep3_write_16seg
|
||||||
|
iidx_io_fini
|
||||||
|
iidx_io_init
|
||||||
|
iidx_io_set_loggers
|
||||||
205
src/main/iidxio-lightning/iidxio.c
Normal file
205
src/main/iidxio-lightning/iidxio.c
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "iidxio-lightning"
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bemanitools/glue.h"
|
||||||
|
#include "bemanitools/iidxio.h"
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "load_aio.h"
|
||||||
|
|
||||||
|
static atomic_bool running;
|
||||||
|
static atomic_bool processing_io;
|
||||||
|
|
||||||
|
static struct AIO_IOB2_BI2X_TDJ__DEVSTATUS pin_cur;
|
||||||
|
|
||||||
|
static struct bi2x_ctx *bi2x_ctx;
|
||||||
|
|
||||||
|
|
||||||
|
static bool _bio2_iidx_io_poll(struct AIO_IOB2_BI2X_TDJ__DEVSTATUS *pin)
|
||||||
|
{
|
||||||
|
if (!running) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
processing_io = true;
|
||||||
|
|
||||||
|
poll_bi2x(bi2x_ctx, pin);
|
||||||
|
|
||||||
|
processing_io = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iidx_io_set_loggers(
|
||||||
|
log_formatter_t misc,
|
||||||
|
log_formatter_t info,
|
||||||
|
log_formatter_t warning,
|
||||||
|
log_formatter_t fatal)
|
||||||
|
{
|
||||||
|
log_to_external(misc, info, warning, fatal);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iidx_io_init(
|
||||||
|
thread_create_t thread_create,
|
||||||
|
thread_join_t thread_join,
|
||||||
|
thread_destroy_t thread_destroy)
|
||||||
|
{
|
||||||
|
init_aio_handles();
|
||||||
|
|
||||||
|
bi2x_ctx = setup_bi2x();
|
||||||
|
if (!bi2x_ctx) {
|
||||||
|
log_warning("Unable to start BI2X?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iidx_io_fini(void)
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
|
||||||
|
while (processing_io) {
|
||||||
|
// avoid banging
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_bi2x(bi2x_ctx);
|
||||||
|
|
||||||
|
bi2x_ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iidx_io_ep1_set_deck_lights(uint16_t deck_lights)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iidx_io_ep1_set_panel_lights(uint8_t panel_lights)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iidx_io_ep1_set_top_lamps(uint8_t top_lamps)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iidx_io_ep1_set_top_neons(bool top_neons)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iidx_io_ep1_send(void)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iidx_io_ep2_recv(void)
|
||||||
|
{
|
||||||
|
if (!_bio2_iidx_io_poll(&pin_cur)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t iidx_io_ep2_get_turntable(uint8_t player_no)
|
||||||
|
{
|
||||||
|
switch (player_no) {
|
||||||
|
case 0:
|
||||||
|
return pin_cur.a_turntable[0];
|
||||||
|
case 1:
|
||||||
|
return pin_cur.a_turntable[1];
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t iidx_io_ep2_get_slider(uint8_t slider_no)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t iidx_io_ep2_get_sys(void)
|
||||||
|
{
|
||||||
|
uint8_t state;
|
||||||
|
|
||||||
|
state = 0;
|
||||||
|
|
||||||
|
if (pin_cur.b_test) {
|
||||||
|
state |= (1 << IIDX_IO_SYS_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_cur.b_service) {
|
||||||
|
state |= (1 << IIDX_IO_SYS_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_cur.b_coinmech) {
|
||||||
|
state |= (1 << IIDX_IO_SYS_COIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t iidx_io_ep2_get_panel(void)
|
||||||
|
{
|
||||||
|
uint8_t state;
|
||||||
|
|
||||||
|
state = 0;
|
||||||
|
|
||||||
|
if (pin_cur.b_start[0]) {
|
||||||
|
state |= (1 << IIDX_IO_PANEL_P1_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_cur.b_start[1]) {
|
||||||
|
state |= (1 << IIDX_IO_PANEL_P2_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_cur.b_vefx) {
|
||||||
|
state |= (1 << IIDX_IO_PANEL_VEFX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_cur.b_effect) {
|
||||||
|
state |= (1 << IIDX_IO_PANEL_EFFECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t iidx_io_ep2_get_keys(void)
|
||||||
|
{
|
||||||
|
uint16_t state;
|
||||||
|
|
||||||
|
state = 0;
|
||||||
|
|
||||||
|
state |= ((pin_cur.b_p1[0] > 0) << IIDX_IO_KEY_P1_1);
|
||||||
|
state |= ((pin_cur.b_p1[1] > 0) << IIDX_IO_KEY_P1_2);
|
||||||
|
state |= ((pin_cur.b_p1[2] > 0) << IIDX_IO_KEY_P1_3);
|
||||||
|
state |= ((pin_cur.b_p1[3] > 0) << IIDX_IO_KEY_P1_4);
|
||||||
|
state |= ((pin_cur.b_p1[4] > 0) << IIDX_IO_KEY_P1_5);
|
||||||
|
state |= ((pin_cur.b_p1[5] > 0) << IIDX_IO_KEY_P1_6);
|
||||||
|
state |= ((pin_cur.b_p1[6] > 0) << IIDX_IO_KEY_P1_7);
|
||||||
|
|
||||||
|
state |= ((pin_cur.b_p2[0] > 0) << IIDX_IO_KEY_P2_1);
|
||||||
|
state |= ((pin_cur.b_p2[1] > 0) << IIDX_IO_KEY_P2_2);
|
||||||
|
state |= ((pin_cur.b_p2[2] > 0) << IIDX_IO_KEY_P2_3);
|
||||||
|
state |= ((pin_cur.b_p2[3] > 0) << IIDX_IO_KEY_P2_4);
|
||||||
|
state |= ((pin_cur.b_p2[4] > 0) << IIDX_IO_KEY_P2_5);
|
||||||
|
state |= ((pin_cur.b_p2[5] > 0) << IIDX_IO_KEY_P2_6);
|
||||||
|
state |= ((pin_cur.b_p2[6] > 0) << IIDX_IO_KEY_P2_7);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iidx_io_ep3_write_16seg(const char *text)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
161
src/main/iidxio-lightning/load_aio.c
Normal file
161
src/main/iidxio-lightning/load_aio.c
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "load_aio"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "load_aio.h"
|
||||||
|
|
||||||
|
#define DECLARE_MODULE_FUNCTION(NAME) t_ ## NAME NAME
|
||||||
|
|
||||||
|
struct aio_module {
|
||||||
|
// "libaio.dll"
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNodeCtl_UpdateDevicesStatus);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNodeCtl_Destroy);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNodeMgr_Destroy);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioSci_Destroy);
|
||||||
|
// "libaio-iob.dll"
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2x_OpenSciUsbCdc);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNMgrIob2_Create);
|
||||||
|
// "libaio-iob2_video.dll"
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTDJ_Create);
|
||||||
|
// technically the game uses the C++ one, but this one doesn't incur a penalty unlike the C version of AIO_IOB2_BI2X_TDJ__GetDeviceStatus
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTDJ_IoReset);
|
||||||
|
DECLARE_MODULE_FUNCTION(AIO_IOB2_BI2X_TDJ__GetDeviceStatus);
|
||||||
|
DECLARE_MODULE_FUNCTION(AIO_IOB2_BI2X_TDJ__SetTurnTableResist);
|
||||||
|
};
|
||||||
|
|
||||||
|
void warn_module(const char *function_name) {
|
||||||
|
log_warning("Could not load %s!", function_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOAD_MODULE_FUNCTION(TARGET, HANDLE, NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, #NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
|
||||||
|
#define LOAD_MODULE_FUNCTION_EX(TARGET, HANDLE, NAME, REAL_NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, REAL_NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
|
||||||
|
|
||||||
|
static struct aio_module module_handles;
|
||||||
|
|
||||||
|
void init_aio_handles(void) {
|
||||||
|
HMODULE h_aio = LoadLibrary("libaio.dll");
|
||||||
|
HMODULE h_aio_iob = LoadLibrary("libaio-iob.dll");
|
||||||
|
HMODULE h_aio_iob2_video = LoadLibrary("libaio-iob2_video.dll");
|
||||||
|
|
||||||
|
if (!h_aio) {
|
||||||
|
log_warning("Could not load libaio.dll!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h_aio_iob) {
|
||||||
|
log_warning("Could not load libaio-iob.dll!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h_aio_iob2_video) {
|
||||||
|
log_warning("Could not load libaio-iob2_video.dll!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "libaio.dll"
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_UpdateDevicesStatus);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_Destroy);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeMgr_Destroy);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioSci_Destroy);
|
||||||
|
|
||||||
|
// "libaio-iob.dll"
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioIob2Bi2x_OpenSciUsbCdc);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioNMgrIob2_Create);
|
||||||
|
|
||||||
|
|
||||||
|
// "libaio-iob2_video.dll"
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTDJ_Create);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTDJ_IoReset);
|
||||||
|
LOAD_MODULE_FUNCTION_EX(module_handles, h_aio_iob2_video, AIO_IOB2_BI2X_TDJ__GetDeviceStatus, "?GetDeviceStatus@AIO_IOB2_BI2X_TDJ@@QEBAXAEAUDEVSTATUS@1@@Z");
|
||||||
|
LOAD_MODULE_FUNCTION_EX(module_handles, h_aio_iob2_video, AIO_IOB2_BI2X_TDJ__SetTurnTableResist, "?SetTurnTableResist@AIO_IOB2_BI2X_TDJ@@QEAAXIE@Z");
|
||||||
|
|
||||||
|
log_misc("Done loading all libaio functions required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AIO_NMGR_VTABLE {
|
||||||
|
void *_dtor;
|
||||||
|
void *AddNodeCtl;
|
||||||
|
void *RemoveNodeCtl;
|
||||||
|
void *PacketCreate;
|
||||||
|
void *PacketSend;
|
||||||
|
void *QueryPktRecvTimeout;
|
||||||
|
void *PacketRecv;
|
||||||
|
void *_GetCommStatus;
|
||||||
|
void *_Reconnect;
|
||||||
|
void *_unk;
|
||||||
|
void (*BeginManage)(struct AIO_NMGR *);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_NMGR {
|
||||||
|
struct AIO_NMGR_VTABLE *vtbl;
|
||||||
|
uint8_t data[0x9F0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bi2x_ctx {
|
||||||
|
struct AIO_SCI *sci_usb;
|
||||||
|
struct AIO_NMGR *iob2_mgr;
|
||||||
|
struct AIO_NODE *iob_bi2x_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bi2x_ctx *setup_bi2x(void) {
|
||||||
|
struct bi2x_ctx *ctx = malloc(sizeof(struct bi2x_ctx));
|
||||||
|
|
||||||
|
ctx->sci_usb = module_handles.aioIob2Bi2x_OpenSciUsbCdc(0);
|
||||||
|
if (!ctx->sci_usb) {
|
||||||
|
log_warning("Unable to aioIob2Bi2x_OpenSciUsbCdc");
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->iob2_mgr = module_handles.aioNMgrIob2_Create(ctx->sci_usb, 0);
|
||||||
|
if (!ctx->iob2_mgr) {
|
||||||
|
log_warning("Unable to aioNMgrIob2_Create");
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->iob_bi2x_node = module_handles.aioIob2Bi2xTDJ_Create(ctx->iob2_mgr, 1u);
|
||||||
|
if (!ctx->iob_bi2x_node) {
|
||||||
|
log_warning("Unable to aioIob2Bi2xTDJ_Create?");
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->iob2_mgr->vtbl->BeginManage(ctx->iob2_mgr);
|
||||||
|
log_misc("BeginManage iob2_mgr");
|
||||||
|
|
||||||
|
// never actually called
|
||||||
|
// module_handles.aioIob2Bi2xTDJ_IoReset(ctx->iob_bi2x_node, 0x11);
|
||||||
|
|
||||||
|
// TODO: set these
|
||||||
|
// module_handles.AIO_IOB2_BI2X_TDJ__SetTurnTableResist(ctx->iob_bi2x_node, 0, 127);
|
||||||
|
// module_handles.AIO_IOB2_BI2X_TDJ__SetTurnTableResist(ctx->iob_bi2x_node, 1, 127);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_bi2x(struct bi2x_ctx *ctx) {
|
||||||
|
if (ctx->iob_bi2x_node) {
|
||||||
|
module_handles.aioNodeCtl_Destroy(ctx->iob_bi2x_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->iob2_mgr) {
|
||||||
|
module_handles.aioNodeMgr_Destroy(ctx->iob2_mgr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->sci_usb) {
|
||||||
|
module_handles.aioSci_Destroy(ctx->sci_usb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TDJ__DEVSTATUS *status) {
|
||||||
|
module_handles.aioNodeCtl_UpdateDevicesStatus();
|
||||||
|
|
||||||
|
// technically we should do some checking here
|
||||||
|
|
||||||
|
module_handles.AIO_IOB2_BI2X_TDJ__GetDeviceStatus(ctx->iob_bi2x_node, status);
|
||||||
|
}
|
||||||
13
src/main/iidxio-lightning/load_aio.h
Normal file
13
src/main/iidxio-lightning/load_aio.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "aio.h"
|
||||||
|
|
||||||
|
void init_aio_handles(void);
|
||||||
|
|
||||||
|
struct bi2x_ctx;
|
||||||
|
|
||||||
|
struct bi2x_ctx *setup_bi2x(void);
|
||||||
|
|
||||||
|
void close_bi2x(struct bi2x_ctx *ctx);
|
||||||
|
|
||||||
|
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TDJ__DEVSTATUS *status);
|
||||||
12
src/main/sdvxio-tbs/Module.mk
Normal file
12
src/main/sdvxio-tbs/Module.mk
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
dlls += sdvxio-tbs
|
||||||
|
|
||||||
|
ldflags_sdvxio-tbs := \
|
||||||
|
-lsetupapi \
|
||||||
|
|
||||||
|
libs_sdvxio-tbs := \
|
||||||
|
cconfig \
|
||||||
|
util \
|
||||||
|
|
||||||
|
src_sdvxio-tbs := \
|
||||||
|
sdvxio.c \
|
||||||
|
load_aio.c \
|
||||||
72
src/main/sdvxio-tbs/aio.h
Normal file
72
src/main/sdvxio-tbs/aio.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct AIO_NODE;
|
||||||
|
struct AIO_NMGR;
|
||||||
|
struct AIO_SCI;
|
||||||
|
|
||||||
|
struct INPUT
|
||||||
|
{
|
||||||
|
char DevIoCounter;
|
||||||
|
char bExIoAErr;
|
||||||
|
char bExIoBErr;
|
||||||
|
char bPcPowerOn;
|
||||||
|
char bPcPowerCheck;
|
||||||
|
char CoinCount;
|
||||||
|
char bTest;
|
||||||
|
char bService;
|
||||||
|
char bCoinSw;
|
||||||
|
char bCoinJam;
|
||||||
|
char bHPDetect;
|
||||||
|
uint16_t StickX;
|
||||||
|
uint16_t StickY;
|
||||||
|
char bStickBtn;
|
||||||
|
char bTrigger1;
|
||||||
|
char bTrigger2;
|
||||||
|
char bButton0;
|
||||||
|
char bButton1;
|
||||||
|
char bButton2;
|
||||||
|
char bButton3;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct INPUTDATA
|
||||||
|
{
|
||||||
|
char Data[10];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OUTPUTDATA
|
||||||
|
{
|
||||||
|
char Data[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IORESETDATA
|
||||||
|
{
|
||||||
|
char Data[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB2_BI2X_TBS__DEVSTATUS {
|
||||||
|
char InputCounter;
|
||||||
|
char OutputCounter;
|
||||||
|
char TapeLedCounter;
|
||||||
|
struct INPUT Input;
|
||||||
|
struct INPUTDATA InputData;
|
||||||
|
struct OUTPUTDATA OutputData;
|
||||||
|
struct IORESETDATA IoResetData;
|
||||||
|
};
|
||||||
|
|
||||||
|
_Static_assert(
|
||||||
|
sizeof(struct AIO_IOB2_BI2X_TBS__DEVSTATUS) == 50,
|
||||||
|
"bi2x_device_status is the wrong size");
|
||||||
|
|
||||||
|
typedef void (*t_aioNodeCtl_UpdateDevicesStatus)(void);
|
||||||
|
typedef unsigned int (*t_aioNodeCtl_Destroy)(struct AIO_NODE *node);
|
||||||
|
typedef unsigned int (*t_aioNodeMgr_Destroy)(struct AIO_NMGR *mgr);
|
||||||
|
typedef unsigned int (*t_aioSci_Destroy)(struct AIO_SCI *sci);
|
||||||
|
|
||||||
|
typedef struct AIO_SCI* (*t_aioIob2Bi2x_OpenSciUsbCdc)(uint8_t unk);
|
||||||
|
typedef struct AIO_NMGR* (*t_aioNMgrIob2_Create)(struct AIO_SCI *port, unsigned int delay);
|
||||||
|
|
||||||
|
typedef struct AIO_NODE* (*t_aioIob2Bi2xTBS_Create)(struct AIO_NMGR *node_mgr, uint8_t param);
|
||||||
|
typedef void (*t_aioIob2Bi2xTBS_IoReset)(struct AIO_NODE *i_pNodeCtl, int value);
|
||||||
|
typedef void (*t_aioIob2Bi2xTBS_GetDeviceStatus)(struct AIO_NODE *i_pNodeCtl, struct AIO_IOB2_BI2X_TBS__DEVSTATUS *status, int size);
|
||||||
156
src/main/sdvxio-tbs/load_aio.c
Normal file
156
src/main/sdvxio-tbs/load_aio.c
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "load_aio"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "load_aio.h"
|
||||||
|
|
||||||
|
#define DECLARE_MODULE_FUNCTION(NAME) t_ ## NAME NAME
|
||||||
|
|
||||||
|
struct aio_module {
|
||||||
|
// "libaio.dll"
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNodeCtl_UpdateDevicesStatus);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNodeCtl_Destroy);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNodeMgr_Destroy);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioSci_Destroy);
|
||||||
|
// "libaio-iob.dll"
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2x_OpenSciUsbCdc);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNMgrIob2_Create);
|
||||||
|
// "libaio-iob2_video.dll"
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTBS_Create);
|
||||||
|
// technically the game uses the C++ one, but this one doesn't incur a penalty unlike the C version of aioIob2Bi2xTBS_GetDeviceStatus
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTBS_IoReset);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTBS_GetDeviceStatus);
|
||||||
|
};
|
||||||
|
|
||||||
|
void warn_module(const char *function_name) {
|
||||||
|
log_warning("Could not load %s!", function_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOAD_MODULE_FUNCTION(TARGET, HANDLE, NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, #NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
|
||||||
|
#define LOAD_MODULE_FUNCTION_EX(TARGET, HANDLE, NAME, REAL_NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, REAL_NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
|
||||||
|
|
||||||
|
static struct aio_module module_handles;
|
||||||
|
|
||||||
|
void init_aio_handles(void) {
|
||||||
|
HMODULE h_aio = LoadLibrary("libaio.dll");
|
||||||
|
HMODULE h_aio_iob = LoadLibrary("libaio-iob.dll");
|
||||||
|
HMODULE h_aio_iob2_video = LoadLibrary("libaio-iob2_video.dll");
|
||||||
|
|
||||||
|
if (!h_aio) {
|
||||||
|
log_warning("Could not load libaio.dll!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h_aio_iob) {
|
||||||
|
log_warning("Could not load libaio-iob.dll!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h_aio_iob2_video) {
|
||||||
|
log_warning("Could not load libaio-iob2_video.dll!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "libaio.dll"
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_UpdateDevicesStatus);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_Destroy);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeMgr_Destroy);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioSci_Destroy);
|
||||||
|
|
||||||
|
// "libaio-iob.dll"
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioIob2Bi2x_OpenSciUsbCdc);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioNMgrIob2_Create);
|
||||||
|
|
||||||
|
|
||||||
|
// "libaio-iob2_video.dll"
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTBS_Create);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTBS_IoReset);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTBS_GetDeviceStatus);
|
||||||
|
// LOAD_MODULE_FUNCTION_EX(module_handles, h_aio_iob2_video, aioIob2Bi2xTBS_GetDeviceStatus, "?GetDeviceStatus@AIO_IOB2_BI2X_TDJ@@QEBAXAEAUDEVSTATUS@1@@Z");
|
||||||
|
|
||||||
|
log_misc("Done loading all libaio functions required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AIO_NMGR_VTABLE {
|
||||||
|
void *_dtor;
|
||||||
|
void *AddNodeCtl;
|
||||||
|
void *RemoveNodeCtl;
|
||||||
|
void *PacketCreate;
|
||||||
|
void *PacketSend;
|
||||||
|
void *QueryPktRecvTimeout;
|
||||||
|
void *PacketRecv;
|
||||||
|
void *_GetCommStatus;
|
||||||
|
void *_Reconnect;
|
||||||
|
void *_unk;
|
||||||
|
void (*BeginManage)(struct AIO_NMGR *);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_NMGR {
|
||||||
|
struct AIO_NMGR_VTABLE *vtbl;
|
||||||
|
uint8_t data[0x9F0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bi2x_ctx {
|
||||||
|
struct AIO_SCI *sci_usb;
|
||||||
|
struct AIO_NMGR *iob2_mgr;
|
||||||
|
struct AIO_NODE *iob_bi2x_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bi2x_ctx *setup_bi2x(void) {
|
||||||
|
struct bi2x_ctx *ctx = malloc(sizeof(struct bi2x_ctx));
|
||||||
|
|
||||||
|
ctx->sci_usb = module_handles.aioIob2Bi2x_OpenSciUsbCdc(0);
|
||||||
|
if (!ctx->sci_usb) {
|
||||||
|
log_warning("Unable to aioIob2Bi2x_OpenSciUsbCdc");
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->iob2_mgr = module_handles.aioNMgrIob2_Create(ctx->sci_usb, 0);
|
||||||
|
if (!ctx->iob2_mgr) {
|
||||||
|
log_warning("Unable to aioNMgrIob2_Create");
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->iob_bi2x_node = module_handles.aioIob2Bi2xTBS_Create(ctx->iob2_mgr, 1u);
|
||||||
|
if (!ctx->iob_bi2x_node) {
|
||||||
|
log_warning("Unable to aioIob2Bi2xTBS_Create?");
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->iob2_mgr->vtbl->BeginManage(ctx->iob2_mgr);
|
||||||
|
log_misc("BeginManage iob2_mgr");
|
||||||
|
|
||||||
|
// never actually called
|
||||||
|
// module_handles.aioIob2Bi2xTBS_IoReset(ctx->iob_bi2x_node, 0x11);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_bi2x(struct bi2x_ctx *ctx) {
|
||||||
|
if (ctx->iob_bi2x_node) {
|
||||||
|
module_handles.aioNodeCtl_Destroy(ctx->iob_bi2x_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->iob2_mgr) {
|
||||||
|
module_handles.aioNodeMgr_Destroy(ctx->iob2_mgr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->sci_usb) {
|
||||||
|
module_handles.aioSci_Destroy(ctx->sci_usb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TBS__DEVSTATUS *status) {
|
||||||
|
module_handles.aioNodeCtl_UpdateDevicesStatus();
|
||||||
|
|
||||||
|
// technically we should do some checking here
|
||||||
|
|
||||||
|
module_handles.aioIob2Bi2xTBS_GetDeviceStatus(ctx->iob_bi2x_node, status, sizeof(struct AIO_IOB2_BI2X_TBS__DEVSTATUS));
|
||||||
|
}
|
||||||
13
src/main/sdvxio-tbs/load_aio.h
Normal file
13
src/main/sdvxio-tbs/load_aio.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "aio.h"
|
||||||
|
|
||||||
|
void init_aio_handles(void);
|
||||||
|
|
||||||
|
struct bi2x_ctx;
|
||||||
|
|
||||||
|
struct bi2x_ctx *setup_bi2x(void);
|
||||||
|
|
||||||
|
void close_bi2x(struct bi2x_ctx *ctx);
|
||||||
|
|
||||||
|
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TBS__DEVSTATUS *status);
|
||||||
14
src/main/sdvxio-tbs/sdvxio-tbs.def
Normal file
14
src/main/sdvxio-tbs/sdvxio-tbs.def
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
LIBRARY sdvxio
|
||||||
|
|
||||||
|
EXPORTS
|
||||||
|
sdvx_io_fini
|
||||||
|
sdvx_io_get_input_gpio
|
||||||
|
sdvx_io_get_input_gpio_sys
|
||||||
|
sdvx_io_get_spinner_pos
|
||||||
|
sdvx_io_init
|
||||||
|
sdvx_io_read_input
|
||||||
|
sdvx_io_set_gpio_lights
|
||||||
|
sdvx_io_set_loggers
|
||||||
|
sdvx_io_set_pwm_light
|
||||||
|
sdvx_io_write_output
|
||||||
|
sdvx_io_set_amp_volume
|
||||||
173
src/main/sdvxio-tbs/sdvxio.c
Normal file
173
src/main/sdvxio-tbs/sdvxio.c
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "iidxio-lightning"
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bemanitools/glue.h"
|
||||||
|
#include "bemanitools/sdvxio.h"
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "load_aio.h"
|
||||||
|
|
||||||
|
static atomic_bool running;
|
||||||
|
static atomic_bool processing_io;
|
||||||
|
|
||||||
|
static struct AIO_IOB2_BI2X_TBS__DEVSTATUS pin_cur;
|
||||||
|
|
||||||
|
static struct bi2x_ctx *bi2x_ctx;
|
||||||
|
|
||||||
|
|
||||||
|
static bool _bio2_iidx_io_poll(struct AIO_IOB2_BI2X_TBS__DEVSTATUS *pin)
|
||||||
|
{
|
||||||
|
if (!running) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
processing_io = true;
|
||||||
|
|
||||||
|
poll_bi2x(bi2x_ctx, pin);
|
||||||
|
|
||||||
|
processing_io = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdvx_io_set_loggers(
|
||||||
|
log_formatter_t misc,
|
||||||
|
log_formatter_t info,
|
||||||
|
log_formatter_t warning,
|
||||||
|
log_formatter_t fatal)
|
||||||
|
{
|
||||||
|
log_to_external(misc, info, warning, fatal);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdvx_io_init(
|
||||||
|
thread_create_t thread_create,
|
||||||
|
thread_join_t thread_join,
|
||||||
|
thread_destroy_t thread_destroy)
|
||||||
|
{
|
||||||
|
init_aio_handles();
|
||||||
|
|
||||||
|
bi2x_ctx = setup_bi2x();
|
||||||
|
if (!bi2x_ctx) {
|
||||||
|
log_warning("Unable to start BI2X?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdvx_io_fini(void)
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
|
||||||
|
while (processing_io) {
|
||||||
|
// avoid banging
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_bi2x(bi2x_ctx);
|
||||||
|
|
||||||
|
bi2x_ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdvx_io_set_gpio_lights(uint32_t gpio_lights)
|
||||||
|
{
|
||||||
|
// wow ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdvx_io_set_pwm_light(uint8_t light_no, uint8_t intensity)
|
||||||
|
{
|
||||||
|
// wow ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdvx_io_write_output(void)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdvx_io_read_input(void)
|
||||||
|
{
|
||||||
|
if (!_bio2_iidx_io_poll(&pin_cur)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t sdvx_io_get_spinner_pos(uint8_t spinner_no)
|
||||||
|
{
|
||||||
|
// spinner_no
|
||||||
|
int32_t raw_value = 0;
|
||||||
|
if (spinner_no == 0) {
|
||||||
|
raw_value = pin_cur.Input.StickX;
|
||||||
|
}
|
||||||
|
if (spinner_no == 1) {
|
||||||
|
raw_value = pin_cur.Input.StickY;
|
||||||
|
}
|
||||||
|
int32_t value = raw_value - 32767;
|
||||||
|
|
||||||
|
float converted = value / 32767.0f;
|
||||||
|
|
||||||
|
converted *= 2.0;
|
||||||
|
|
||||||
|
if (converted > 1.0f) {
|
||||||
|
converted = 1.0f;
|
||||||
|
} else if (converted < -1.0f) {
|
||||||
|
converted = -1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t final = - 32767 * converted;
|
||||||
|
|
||||||
|
return (uint16_t)final/64;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t shift_pin(uint16_t value, uint8_t pin)
|
||||||
|
{
|
||||||
|
if (value) {
|
||||||
|
return (1 << pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sdvx_io_get_input_gpio_sys(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t sdvx_io_get_input_gpio(uint8_t gpio_bank)
|
||||||
|
{
|
||||||
|
if (gpio_bank > 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t data = 0;
|
||||||
|
|
||||||
|
if (gpio_bank == 0) {
|
||||||
|
data |= shift_pin(pin_cur.Input.bStickBtn, SDVX_IO_IN_GPIO_0_START);
|
||||||
|
data |= shift_pin(pin_cur.Input.bButton0, SDVX_IO_IN_GPIO_0_A);
|
||||||
|
data |= shift_pin(pin_cur.Input.bButton1, SDVX_IO_IN_GPIO_0_B);
|
||||||
|
data |= shift_pin(pin_cur.Input.bButton2, SDVX_IO_IN_GPIO_0_C);
|
||||||
|
} else if (gpio_bank == 1) {
|
||||||
|
data |= shift_pin(pin_cur.Input.bButton3, SDVX_IO_IN_GPIO_1_D);
|
||||||
|
data |= shift_pin(pin_cur.Input.bTrigger1, SDVX_IO_IN_GPIO_1_FX_L);
|
||||||
|
data |= shift_pin(pin_cur.Input.bTrigger2, SDVX_IO_IN_GPIO_1_FX_R);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdvx_io_set_amp_volume(
|
||||||
|
uint8_t primary, uint8_t headphone, uint8_t subwoofer)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
12
src/main/sdvxio-valk/Module.mk
Normal file
12
src/main/sdvxio-valk/Module.mk
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
dlls += sdvxio-valk
|
||||||
|
|
||||||
|
ldflags_sdvxio-valk := \
|
||||||
|
-lsetupapi \
|
||||||
|
|
||||||
|
libs_sdvxio-valk := \
|
||||||
|
cconfig \
|
||||||
|
util \
|
||||||
|
|
||||||
|
src_sdvxio-valk := \
|
||||||
|
sdvxio.c \
|
||||||
|
load_aio.c \
|
||||||
27
src/main/sdvxio-valk/aio.h
Normal file
27
src/main/sdvxio-valk/aio.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct AIO_NODE;
|
||||||
|
struct AIO_NMGR;
|
||||||
|
struct AIO_SCI;
|
||||||
|
|
||||||
|
struct AIO_IOB2_BI2X_UFC__DEVSTATUS {
|
||||||
|
char buffer[414];
|
||||||
|
};
|
||||||
|
|
||||||
|
_Static_assert(
|
||||||
|
sizeof(struct AIO_IOB2_BI2X_UFC__DEVSTATUS) == 414,
|
||||||
|
"bi2x_device_status is the wrong size");
|
||||||
|
|
||||||
|
typedef void (*t_aioNodeCtl_UpdateDevicesStatus)(void);
|
||||||
|
typedef unsigned int (*t_aioNodeCtl_Destroy)(struct AIO_NODE *node);
|
||||||
|
typedef unsigned int (*t_aioNodeMgr_Destroy)(struct AIO_NMGR *mgr);
|
||||||
|
typedef unsigned int (*t_aioSci_Destroy)(struct AIO_SCI *sci);
|
||||||
|
|
||||||
|
typedef struct AIO_SCI* (*t_aioIob2Bi2x_OpenSciUsbCdc)(uint8_t unk);
|
||||||
|
typedef struct AIO_NMGR* (*t_aioNMgrIob2_Create)(struct AIO_SCI *port, unsigned int delay);
|
||||||
|
|
||||||
|
typedef struct AIO_NODE* (*t_aioIob2Bi2xUFC_Create)(struct AIO_NMGR *node_mgr, uint8_t param);
|
||||||
|
typedef void (*t_aioIob2Bi2xUFC_IoReset)(struct AIO_NODE *i_pNodeCtl, int value);
|
||||||
|
typedef void (*t_aioIob2Bi2xUFC_GetDeviceStatus)(struct AIO_NODE *i_pNodeCtl, struct AIO_IOB2_BI2X_UFC__DEVSTATUS *status, int size);
|
||||||
155
src/main/sdvxio-valk/load_aio.c
Normal file
155
src/main/sdvxio-valk/load_aio.c
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "load_aio"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "load_aio.h"
|
||||||
|
|
||||||
|
#define DECLARE_MODULE_FUNCTION(NAME) t_ ## NAME NAME
|
||||||
|
|
||||||
|
struct aio_module {
|
||||||
|
// "libaio.dll"
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNodeCtl_UpdateDevicesStatus);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNodeCtl_Destroy);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNodeMgr_Destroy);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioSci_Destroy);
|
||||||
|
// "libaio-iob.dll"
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2x_OpenSciUsbCdc);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioNMgrIob2_Create);
|
||||||
|
// "libaio-iob2_video.dll"
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2xUFC_Create);
|
||||||
|
// technically the game uses the C++ one, but this one doesn't incur an extra function call unlike the C version
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2xUFC_IoReset);
|
||||||
|
DECLARE_MODULE_FUNCTION(aioIob2Bi2xUFC_GetDeviceStatus);
|
||||||
|
};
|
||||||
|
|
||||||
|
void warn_module(const char *function_name) {
|
||||||
|
log_warning("Could not load %s!", function_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOAD_MODULE_FUNCTION(TARGET, HANDLE, NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, #NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
|
||||||
|
#define LOAD_MODULE_FUNCTION_EX(TARGET, HANDLE, NAME, REAL_NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, REAL_NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
|
||||||
|
|
||||||
|
static struct aio_module module_handles;
|
||||||
|
|
||||||
|
void init_aio_handles(void) {
|
||||||
|
HMODULE h_aio = LoadLibrary("libaio.dll");
|
||||||
|
HMODULE h_aio_iob = LoadLibrary("libaio-iob.dll");
|
||||||
|
HMODULE h_aio_iob2_video = LoadLibrary("libaio-iob2_video.dll");
|
||||||
|
|
||||||
|
if (!h_aio) {
|
||||||
|
log_warning("Could not load libaio.dll!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h_aio_iob) {
|
||||||
|
log_warning("Could not load libaio-iob.dll!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h_aio_iob2_video) {
|
||||||
|
log_warning("Could not load libaio-iob2_video.dll!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "libaio.dll"
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_UpdateDevicesStatus);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_Destroy);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeMgr_Destroy);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioSci_Destroy);
|
||||||
|
|
||||||
|
// "libaio-iob.dll"
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioIob2Bi2x_OpenSciUsbCdc);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioNMgrIob2_Create);
|
||||||
|
|
||||||
|
|
||||||
|
// "libaio-iob2_video.dll"
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xUFC_Create);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xUFC_IoReset);
|
||||||
|
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xUFC_GetDeviceStatus);
|
||||||
|
|
||||||
|
log_misc("Done loading all libaio functions required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AIO_NMGR_VTABLE {
|
||||||
|
void *_dtor;
|
||||||
|
void *AddNodeCtl;
|
||||||
|
void *RemoveNodeCtl;
|
||||||
|
void *PacketCreate;
|
||||||
|
void *PacketSend;
|
||||||
|
void *QueryPktRecvTimeout;
|
||||||
|
void *PacketRecv;
|
||||||
|
void *_GetCommStatus;
|
||||||
|
void *_Reconnect;
|
||||||
|
void *_unk;
|
||||||
|
void (*BeginManage)(struct AIO_NMGR *);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_NMGR {
|
||||||
|
struct AIO_NMGR_VTABLE *vtbl;
|
||||||
|
uint8_t data[0x9F0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bi2x_ctx {
|
||||||
|
struct AIO_SCI *sci_usb;
|
||||||
|
struct AIO_NMGR *iob2_mgr;
|
||||||
|
struct AIO_NODE *iob_bi2x_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bi2x_ctx *setup_bi2x(void) {
|
||||||
|
struct bi2x_ctx *ctx = malloc(sizeof(struct bi2x_ctx));
|
||||||
|
|
||||||
|
ctx->sci_usb = module_handles.aioIob2Bi2x_OpenSciUsbCdc(0);
|
||||||
|
if (!ctx->sci_usb) {
|
||||||
|
log_warning("Unable to aioIob2Bi2x_OpenSciUsbCdc");
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->iob2_mgr = module_handles.aioNMgrIob2_Create(ctx->sci_usb, 0);
|
||||||
|
if (!ctx->iob2_mgr) {
|
||||||
|
log_warning("Unable to aioNMgrIob2_Create");
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->iob_bi2x_node = module_handles.aioIob2Bi2xUFC_Create(ctx->iob2_mgr, 1u);
|
||||||
|
if (!ctx->iob_bi2x_node) {
|
||||||
|
log_warning("Unable to aioIob2Bi2xUFC_Create?");
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->iob2_mgr->vtbl->BeginManage(ctx->iob2_mgr);
|
||||||
|
log_misc("BeginManage iob2_mgr");
|
||||||
|
|
||||||
|
// never actually called
|
||||||
|
// module_handles.aioIob2Bi2xUFC_IoReset(ctx->iob_bi2x_node, 0x11);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_bi2x(struct bi2x_ctx *ctx) {
|
||||||
|
if (ctx->iob_bi2x_node) {
|
||||||
|
module_handles.aioNodeCtl_Destroy(ctx->iob_bi2x_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->iob2_mgr) {
|
||||||
|
module_handles.aioNodeMgr_Destroy(ctx->iob2_mgr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->sci_usb) {
|
||||||
|
module_handles.aioSci_Destroy(ctx->sci_usb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_UFC__DEVSTATUS *status) {
|
||||||
|
module_handles.aioNodeCtl_UpdateDevicesStatus();
|
||||||
|
|
||||||
|
// technically we should do some checking here
|
||||||
|
|
||||||
|
module_handles.aioIob2Bi2xUFC_GetDeviceStatus(ctx->iob_bi2x_node, status, sizeof(struct AIO_IOB2_BI2X_UFC__DEVSTATUS));
|
||||||
|
}
|
||||||
13
src/main/sdvxio-valk/load_aio.h
Normal file
13
src/main/sdvxio-valk/load_aio.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "aio.h"
|
||||||
|
|
||||||
|
void init_aio_handles(void);
|
||||||
|
|
||||||
|
struct bi2x_ctx;
|
||||||
|
|
||||||
|
struct bi2x_ctx *setup_bi2x(void);
|
||||||
|
|
||||||
|
void close_bi2x(struct bi2x_ctx *ctx);
|
||||||
|
|
||||||
|
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TBS__DEVSTATUS *status);
|
||||||
14
src/main/sdvxio-valk/sdvxio-valk.def
Normal file
14
src/main/sdvxio-valk/sdvxio-valk.def
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
LIBRARY sdvxio-valk
|
||||||
|
|
||||||
|
EXPORTS
|
||||||
|
sdvx_io_fini
|
||||||
|
sdvx_io_get_input_gpio
|
||||||
|
sdvx_io_get_input_gpio_sys
|
||||||
|
sdvx_io_get_spinner_pos
|
||||||
|
sdvx_io_init
|
||||||
|
sdvx_io_read_input
|
||||||
|
sdvx_io_set_gpio_lights
|
||||||
|
sdvx_io_set_loggers
|
||||||
|
sdvx_io_set_pwm_light
|
||||||
|
sdvx_io_write_output
|
||||||
|
sdvx_io_set_amp_volume
|
||||||
137
src/main/sdvxio-valk/sdvxio.c
Normal file
137
src/main/sdvxio-valk/sdvxio.c
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "iidxio-lightning"
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bemanitools/glue.h"
|
||||||
|
#include "bemanitools/sdvxio.h"
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "load_aio.h"
|
||||||
|
|
||||||
|
static atomic_bool running;
|
||||||
|
static atomic_bool processing_io;
|
||||||
|
|
||||||
|
static struct AIO_IOB2_BI2X_UFC__DEVSTATUS pin_cur;
|
||||||
|
|
||||||
|
static struct bi2x_ctx *bi2x_ctx;
|
||||||
|
|
||||||
|
|
||||||
|
static bool _bio2_iidx_io_poll(struct AIO_IOB2_BI2X_UFC__DEVSTATUS *pin)
|
||||||
|
{
|
||||||
|
if (!running) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
processing_io = true;
|
||||||
|
|
||||||
|
poll_bi2x(bi2x_ctx, pin);
|
||||||
|
|
||||||
|
processing_io = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdvx_io_set_loggers(
|
||||||
|
log_formatter_t misc,
|
||||||
|
log_formatter_t info,
|
||||||
|
log_formatter_t warning,
|
||||||
|
log_formatter_t fatal)
|
||||||
|
{
|
||||||
|
log_to_external(misc, info, warning, fatal);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdvx_io_init(
|
||||||
|
thread_create_t thread_create,
|
||||||
|
thread_join_t thread_join,
|
||||||
|
thread_destroy_t thread_destroy)
|
||||||
|
{
|
||||||
|
init_aio_handles();
|
||||||
|
|
||||||
|
bi2x_ctx = setup_bi2x();
|
||||||
|
if (!bi2x_ctx) {
|
||||||
|
log_warning("Unable to start BI2X?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdvx_io_fini(void)
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
|
||||||
|
while (processing_io) {
|
||||||
|
// avoid banging
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_bi2x(bi2x_ctx);
|
||||||
|
|
||||||
|
bi2x_ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdvx_io_set_gpio_lights(uint32_t gpio_lights)
|
||||||
|
{
|
||||||
|
// wow ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdvx_io_set_pwm_light(uint8_t light_no, uint8_t intensity)
|
||||||
|
{
|
||||||
|
// wow ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdvx_io_write_output(void)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdvx_io_read_input(void)
|
||||||
|
{
|
||||||
|
if (!_bio2_iidx_io_poll(&pin_cur)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t sdvx_io_get_spinner_pos(uint8_t spinner_no)
|
||||||
|
{
|
||||||
|
// todo: implement
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t shift_pin(uint16_t value, uint8_t pin)
|
||||||
|
{
|
||||||
|
if (value) {
|
||||||
|
return (1 << pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sdvx_io_get_input_gpio_sys(void)
|
||||||
|
{
|
||||||
|
// todo: implement
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t sdvx_io_get_input_gpio(uint8_t gpio_bank)
|
||||||
|
{
|
||||||
|
// todo: implement
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sdvx_io_set_amp_volume(
|
||||||
|
uint8_t primary, uint8_t headphone, uint8_t subwoofer)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user