Compare commits

...

20 Commits
5.45 ... 5.47

Author SHA1 Message Date
icex2
bdf493d2fb chore: Update changelog for 5.47 release 2024-01-29 22:08:28 +01:00
icex2
bec007d52a fix(p3ioemu): Incorrect dispatching of unknown p3io commands
Using ddrhook1, this caused DDR X to crash on startup when the
P3io client sends the currently unknown command 2B. Handling it
with the incorrect p3io command struct, any following reading
attempts from the P3IO by the game fail.

Handle the 2B case explicitly with a generic response that worked
previously before the restructuring of the code. Apply the same
to any further unknown commands with improved logging warning
about this.
2023-11-30 20:33:36 +01:00
icex2
a177913bd6 fix(p3io): Off-by-one error on assert
Buffers are allowed to have the exact size as the max defined
P3IO buffer size. Cutting it short by one byte causes this
to fail incorrectly when using the pure raw buffer structure
2023-11-30 20:33:36 +01:00
ahnada
0fdde8b32d Allow generic HID devices with broken(?) outputs to still work as inputs.
Fixes Sony DualShock 4 not working
2023-11-13 21:35:28 +01:00
ahnada
09b3a6c24a Fix incorrect SetupDiGetClassDevs error checking 2023-11-13 19:37:29 +01:00
icex2
f8b2f5f40d chore: Bump version to 5.47 to start next development cycle 2023-11-03 15:48:04 +01:00
icex2
882a3557c9 chore(doc): Fix release process notes
Update outdated references and improve some phrasing
2023-11-03 15:46:11 +01:00
icex2
2454141cab chore: Housekeeping, remove dead link, fix description 2023-11-03 15:46:11 +01:00
icex2
4b740c13e8 chore: Remove obsolete gitlab-ci yaml
Repository is not on gitlab anymore, left over
from migration
2023-11-03 15:46:11 +01:00
icex2
ec4e81d9d3 chore: Update changelog 2023-11-03 15:46:11 +01:00
icex2
9a964e6bdb doc: Add info/instructions regarding ddrio-async 2023-11-03 15:46:11 +01:00
icex2
9b2ed6910e feat: ddrio-async wrapper/shim driver for async IO
Wrapper/shim library to drive another ddrio in a dedicated
IO thread. Depending on the other ddrio backend used, this
can significantly improve performance while staying
compatible to the existing ddrio API interface

This turned out to be a good solution to solve performance
problems when running MAME with ddrio-p3io that is
(currently) implemented with synchronous IO calls that are
very costly: ~12 ms for a write over the ACIO protocol, ~4 ms
for a read using an IOCTL. As this already adds up to nearly
a full frame (60 fps) regarding latency, there isn't a lot
of time left to do other stuff in a synchronous update loop.
MAME's performance was unstable and dropped all the time below
100%. The result was a choppy gameplay experience.

Combining ddrio-async with ddrio-p3io, the combined backend
is able to drive inputs/outputs at a rate of ~250hz = ~4
updates per frame. This results in an average input latency
of ~4 ms which is as good as it can get with the p3io
hardware's performance limitations that I measured (see
the 4 ms for the IOCTL mentioned above).

This is more than good enough as as update frequency of
the 573 hardware is slightly less than that (I got told
something ~180 hz?).

tl;dr: Gameplay on MAME is great, smooth frame rate, IO feels
amazing and responsive.
2023-11-03 15:46:11 +01:00
Will Toohey
82343d8cf1 Fix missing xml for jb04. Fixes booting Ave. 2023-10-18 23:29:19 +02:00
Will Toohey
c0101cb614 Fix missing xml for jb03. Closes #258. 2023-10-18 23:29:19 +02:00
Will Toohey
820036087d Revert "Make roundplug PCBID/EAMID verification fail a hard error"
This reverts commit 67de6639ef.
2023-10-18 23:29:19 +02:00
Will Toohey
ebd80d3749 Make roundplug PCBID/EAMID verification fail a hard error
This was previously a soft error, and also said "reverting to default"
without actually doing so. We should be forcing correct format.
2023-10-18 23:29:19 +02:00
Will Toohey
3bff7f8a5e jbhook1: If gfx.vertical is set, rotate error messages too
By hooking CreateProcessA, we can adjust the commandline to
disable any rotation, which displays the error box at the correct
place on screen (no additional x/y adjustment is needed)
2023-10-18 23:29:19 +02:00
Will Toohey
9ce47e7050 jbhook1: More effectively setup avs boot config
The NVRAM redirection was silently failing because the prop
delete/recreate pair didn't work. This just recreates the entire
config from scratch instead, which guarantees success every time.
By making the NVRAM type "fs", we also make it easier to debug
any issues that people may have.
2023-10-18 23:29:19 +02:00
MatryoshkaLJY
13c87eaf8d feat: Add bi2a-iidx support for aciotest (#263) 2023-10-18 23:06:10 +02:00
icex2
f8d394e124 chore: Bump version to 5.46 to start next development cycle 2023-10-18 01:24:15 +02:00
26 changed files with 725 additions and 188 deletions

View File

@@ -1,136 +0,0 @@
#
# This pipeline requires packages to be switched on under the repository settings. Otherwise, you will 403s when
# uploading to the package repo is triggered.
#
# Variables to setup in GitLab CI/CD settings of the project
#
# The variables with BASE64 postfixes need to contain the base64 encoded data. Otherwise, masking
# in GitLab won't work due to not matching their pre-defined regex
#
# CI_PIGSTALL_DATA_PREFIX_BASE64
# CI_PIGSTALL_LINK_BASE64
# CI_PIGSTALL_PHP_SESSION_ID
# CI_PIGSTALL_SESSION_BASE64
#
# CI_TOOLS_UPLOAD_KEY
# CI_TOOLS_UPLOAD_URL
# CI_TOOLS_URL
image: docker:stable
variables:
DOCKER_TLS_CERTDIR: "/certs"
DIST_PACKAGE_RELATIVE_PATH: "build/docker/bemanitools.zip"
PACKAGE_REGISTRY_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/bemanitools"
services:
- docker:19.03.8-dind
stages:
- build
- upload
- release
build:
stage: build
only:
refs:
- master
- tags
before_script:
- apk update && apk add make > /dev/null
script:
- make build-docker
artifacts:
paths:
- build
expire_in: 1 week
upload-package-registry:
stage: upload
image: curlimages/curl:latest
only:
refs:
- master
- tags
dependencies:
- build
script:
- |
if [ "${CI_COMMIT_TAG}" ]; then
version="${CI_COMMIT_TAG}"
else
version="${CI_COMMIT_SHORT_SHA}"
fi
- |
curl \
--silent \
--fail \
--show-error \
--header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
--upload-file "${DIST_PACKAGE_RELATIVE_PATH}" \
$PACKAGE_REGISTRY_URL/${version}/bemanitools.zip
upload-tools-page:
stage: upload
image: curlimages/curl:latest
only:
refs:
- tags
dependencies:
- build
script:
- |
curl \
--silent \
--fail \
--show-error \
--connect-timeout 5 \
--max-time 10 \
--retry 5 \
-F "key=${CI_TOOLS_UPLOAD_KEY}" \
-F "filename=${CI_PROJECT_NAME}-v${CI_COMMIT_TAG}.zip" \
-F "file=@${DIST_PACKAGE_RELATIVE_PATH}" \
${CI_TOOLS_UPLOAD_URL}
release-gitlab:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:v0.8.0
only:
refs:
- tags
script:
- version="$CI_COMMIT_TAG"
- release_message="$(scripts/ci/create-release-message.sh "${version}" < CHANGELOG.md)"
- |
release-cli create \
--name "bemanitools ${version}" \
--description="${release_message}" \
--tag-name ${version} \
--assets-link "{\"name\":\"Distribution binaries\",\"url\":\"${PACKAGE_REGISTRY_URL}/${version}/bemanitools.zip\"}"
release-pigstall:
stage: release
image: curlimages/curl:latest
only:
refs:
- tags
script:
- version="${CI_COMMIT_TAG}"
- changelog_excerpt="$(scripts/ci/create-release-message.sh "${version}" < CHANGELOG.md)"
- release_message="$(printf "bemanitools ${version} released\n${CI_TOOLS_URL}/bemanitools-v${version}.zip\n${changelog_excerpt}")"
- session="$(echo "$CI_PIGSTALL_SESSION_BASE64" | base64 -d)"
- data_prefix="$(echo "$CI_PIGSTALL_DATA_PREFIX_BASE64" | base64 -d)"
- link="$(echo "$CI_PIGSTALL_LINK_BASE64" | base64 -d)"
- |
curl \
--silent \
--fail \
--connect-timeout 5 \
--max-time 10 \
--retry 5 \
--show-error \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H "Cookie: PHPSESSID=$CI_PIGSTALL_PHP_SESSION_ID; session=${session}" \
--data-raw "${data_prefix}&body=${release_message}" \
"${link}"

View File

@@ -2,6 +2,26 @@
Note for CI/CD: Ensure the version formatting in the sections is kept identical to the versions
given in tags. The pipeline will pick this up and cuts out the relevant section for release notes.
## 5.47
### Features
N/A
### Fixes
* fix(ddr/p3ioemu): Handle unknown 2B command to fix DDR X IO errors
* fix(ddr/p3io): Crash on all supported DDR games due to incorrect p3io message size validation
* fix(geninput): Sony DualShock 4 not working
## 5.46
### Features
* feat(ddrio): Wrapper/shim library to drive another ddrio in a dedicated IO thread. Improves performance for highly IO
bound ddrio implementations, e.g. ddrio-p3io
* feat(iidxiotest): bi2a-iidx support
### Fixes
* fix(jb03/04): Missing XML in gamestart script
* fix(jbhook1): Rotate error message box when screen is rotated
* fix(jbhook1): Improve/fix automatic config/nvram fs initialization
## 5.45
### Features
* feat(iidx 30): Added support

View File

@@ -104,6 +104,7 @@ include src/main/d3d9exhook/Module.mk
include src/main/ddrhook-util/Module.mk
include src/main/ddrhook1/Module.mk
include src/main/ddrhook2/Module.mk
include src/main/ddrio-async/Module.mk
include src/main/ddrio-p3io/Module.mk
include src/main/ddrio-mm/Module.mk
include src/main/ddrio-smx/Module.mk
@@ -730,6 +731,7 @@ $(zipdir)/ddr-16-x64.zip: \
$(V)zip -j $@ $^
$(zipdir)/ddr-hwio-x86.zip: \
build/bin/indep-32/ddrio-async.dll \
build/bin/indep-32/ddrio-p3io.dll \
build/bin/indep-32/ddrio-mm.dll \
build/bin/indep-32/ddrio-smx.dll \
@@ -741,6 +743,7 @@ $(zipdir)/ddr-hwio-x86.zip: \
$(V)zip -j $@ $^
$(zipdir)/ddr-hwio-x64.zip: \
build/bin/indep-64/ddrio-async.dll \
build/bin/indep-64/ddrio-p3io.dll \
build/bin/indep-64/ddrio-mm.dll \
build/bin/indep-64/ddrio-smx.dll \

View File

@@ -1,9 +1,8 @@
# Bemanitools 5
[![pipeline status](https://dev.s-ul.net/djhackers/bemanitools/badges/master/pipeline.svg)](https://dev.s-ul.net/djhackers/bemanitools/commits/master)
Version: 5.45
Version: `5.47`
[Release history](CHANGELOG.md)
[Changelog](CHANGELOG.md)
A collection of tools to run [various Bemani arcade games](#supported-games).

View File

@@ -3,6 +3,7 @@
cd /d %~dp0
if not exist dev\nvram mkdir dev\nvram
if not exist dev\nvram\ea3-config.xml copy prop\ea3-config.xml dev\nvram\ea3-config.xml
if not exist dev\nvram\coin.xml copy prop\defaults\coin.xml dev\nvram\coin.xml
if not exist dev\nvram\eacoin.xml copy prop\defaults\eacoin.xml dev\nvram\eacoin.xml
if not exist dev\raw mkdir dev\raw

View File

@@ -3,6 +3,7 @@
cd /d %~dp0
if not exist dev\nvram mkdir dev\nvram
if not exist dev\nvram\ea3-config.xml copy prop\ea3-config.xml dev\nvram\ea3-config.xml
if not exist dev\nvram\coin.xml copy prop\defaults\coin.xml dev\nvram\coin.xml
if not exist dev\nvram\eacoin.xml copy prop\defaults\eacoin.xml dev\nvram\eacoin.xml
if not exist dev\raw mkdir dev\raw

View File

@@ -18,7 +18,8 @@ Table of contents:
* [popnhook](popnhook/README.md): Documentation relevant to `popnhook` implementations
* [sdvxhook](sdvxhook/README.md): Documentation relevant to `sdvxhook` implementations
* Development
* [API](api.md): Available APIs for BT5 and instructions how to use them
* [API](api.md): Available APIs and IO (hardware) implementations for BT5 and instructions how
to use them
* [Architecture](architecture.md): Outline of BT5's architecture, how things are designed and why
* [Development](development.md): Development environment, building, releasing, etc.
* [Developer documentation](dev/README.md): Various lose documentation/notes by developers

View File

@@ -25,6 +25,8 @@ The following implementations are already shipped with BT5.
* [ddrio-p3io.dll](ddrhook/ddrio-p3io.md): DDR P3IO (Dragon PCB) + EXTIO hardware
* ddrio-mm.dll: Minimaid hardware
* [ddrio-smx.dll](ddrhook/ddrio-smx.md): StepManiaX platforms
* [ddrio-async](ddrhook/ddrio-async.md): Wrapper/shim library to drive another ddrio in
a dedicated IO thread
* Beatmania IIDX
* iidxio.dll (default): Keyboard, joystick and mouse input
* [iidxio-bio2.dll](iidxhook/iidxio-bio2.md): BIO2 driver

View File

@@ -46,7 +46,10 @@ IO hardware you want to use:
* `ddrio`: Default implementation supporting keyboard, mouse and USB
game controllers
* ddrio-mm: Support Minimaid custom interface
* [ddrio-smx](ddrhook/ddrio-smx.md): Support for StepManiaX dance platforms
* [ddrio-smx](ddrio-smx.md): Support for StepManiaX dance platforms
* [ddrio-p3io](ddrio-p3io.md): P3IO + EXTIO driver implementation
* [ddrio-async](ddrio-async.md): Wrapper/shim library to drive another ddrio in
a dedicated IO thread
## Unicorntail

View File

@@ -0,0 +1,36 @@
# Asynchronous proxy wrapper for other ddrio implementations
This implementation of the Bemanitools API is not implementing support for any
specific IO hardware. It is a proxy/shim library that loads another ddrio
library, e.g. ddrio-p3io, and drives the entire backend in a dedicated IO
thread. By implementing this behind the ddrio API, it is fully transparent to
any existing application using it.
The main benefit is the improved IO polling performance depending on how
expensive the synchronous calls of the actual hardware are. For example,
*ddrio-p3io* has very expensive write calls with ~12 ms duration while read
calls take ~4 ms. Therefore, a full update cycle is already about as costly
as rendering an entire frame (at 60 fps).
## Setup
For hook libraries, i.e. ddrhookX, but likely applicable to 3rd party
applications (consolidate their manuals).
* Have `ddrio-async.dll` in the same folder as your `ddrhookX.dll`
* Rename `ddrio-async.dll` to `ddrio.dll`
* Pick another ddrio library as the backend of your choice, e.g. `ddrio-p3io.dl`
and put it next to the async `ddrio.dll`
* Rename it to `ddrio-async-child.dll`, ddrio-async is looking for that filename
in the same folder
* Ensure that your `gamestart.bat` actually injects the appropriate `ddrhook.dll`, for example:
```bat
inject ddrhook1.dll ddr.exe ...*
```
or
```bat
launcher -K ddrhook2.dll arkmdxp3.dll ...*
```

View File

@@ -1,18 +1,24 @@
# Release process
For bemanitools maintainers, steps for the release process:
For maintainers, steps for the release process:
1. Update the corresponding section in the [changelog](CHANGELOG.md) with bullet points reflecting
major features merged into master since the previous release
major features merged into master since the previous release
1. Make sure you have the latest `master` state pulled to your local copy
1. Tag the release with the next release number which also be found at the
[top of the readme](#bemanitools-5) as it always reflects the current version beinged worked on
on the `master` branch , e.g. 5.35: `git tag 5.35`
[top of the readme](../README.md) as it always reflects the current version being worked on
on the `master` branch , e.g. 0.04: `git tag 5.35`
1. Push the tag to upstream: `git push origin 5.35`
1. The [build pipeline](https://dev.s-ul.net/djhackers/bemanitools/-/pipelines) should start
automatically once the tag is pushed including the steps `build` and `upload-release`
1. Once completed successfully, the release is uploaded
1. Take the changelog of the published version and notify the pigs in the stall about it:
1. The [build pipeline](https://github.com/djhackersdev/bemanitools/actions) should start
automatically once the tag is pushed including the steps `build` and `publish-release`
1. Once completed successfully, the release is published on the
[releases page](https://github.com/djhackersdev/bemanitools/releases/)
1. Edit the latest release
1. Copy-pate the change log section of the respective version into the description
1. Un-check *Set as pre-release*
1. Check *Set as the latest release*
1. Click *Update release*
1. Notify any other channels, e.g. the pigs in the stall, about the latest release:
1. New post in thread
```
<insert version here> released: <direct link to published version>
@@ -23,7 +29,7 @@ automatically once the tag is pushed including the steps `build` and `upload-rel
1. Update the OP title by bumping the version number in it
1. Update the OP post by extending it accordingly (see previous entries)
1. Bump the version number at the [top of this readme](#bemanitools-5) and add a new empty section
in the [changelog](CHANGELOG.md) with the new version number as title. Commit changes and push to
1. Bump the version number at the [top of the readme](../README.md) and add a new empty section
in the [changelog](../CHANGELOG.md) with the new version number as title. Commit changes and push to
`master` branch
1. Continue developing and merging MRs until you decide its time for another release
1. Continue developing and merging PRs until you decide its time for another release

View File

@@ -11,5 +11,6 @@ src_aciotest := \
kfca.c \
panb.c \
rvol.c \
bi2a-iidx.c \
bi2a-sdvx.c \
main.c \

View File

@@ -0,0 +1,109 @@
#include "aciotest/bi2a-iidx.h"
#include "acio/acio.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bio2drv/bi2a-iidx.h"
bool aciotest_bi2a_iidx_handler_init(
struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx)
{
*ctx = malloc(sizeof(uint32_t));
*((uint32_t *) *ctx) = 0;
return bio2drv_bi2a_iidx_init(device, node_id);
}
bool aciotest_bi2a_iidx_handler_update(
struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx)
{
struct bi2a_iidx_state_in pin;
struct bi2a_iidx_state_out pout;
memset(&pout, 0, sizeof(pout));
if (!bio2drv_bi2a_iidx_poll(device, node_id, &pout, &pin)) {
return false;
}
printf(
"|---------------------------------------|\n"
"| R Y G B Neons B G Y R |\n"
"| %d %d %d %d %d %d %d %d %d |\n"
"|---------------------------------------|\n"
"| NOW PLAYING: %c%c%c%c%c%c%c%c%c |\n"
"|---------------------------------------|\n"
"| Effect %d S1 S2 S3 S4 S5 Test %d|\n"
"|StartP1 %d %02d %02d %02d %02d %02d StartP2 %d|\n"
"| VEFX %d Service %d|\n"
"_________________________________________\n"
"| __ __ |\n"
"| / \\ _ / \\ |\n"
"| | %03d| %d %d %d |%d| %d %d %d | %03d| |\n"
"| \\___/ %d %d %d %d |_| %d %d %d %d \\___/ |\n"
"| |\n"
"|---------------------------------------|\n"
"|---------------------------------------|\n",
pout.SPOTLIGHT2[0].l_state,
pout.SPOTLIGHT2[1].l_state,
pout.SPOTLIGHT2[2].l_state,
pout.SPOTLIGHT2[3].l_state,
pout.NEONLAMP.l_state,
pout.SPOTLIGHT1[0].l_state,
pout.SPOTLIGHT1[1].l_state,
pout.SPOTLIGHT1[2].l_state,
pout.SPOTLIGHT1[3].l_state,
pout.SEG16[0],
pout.SEG16[1],
pout.SEG16[2],
pout.SEG16[3],
pout.SEG16[4],
pout.SEG16[5],
pout.SEG16[6],
pout.SEG16[7],
pout.SEG16[8],
pin.PANEL.y_effect,
pin.SYSTEM.v_test,
pin.PANEL.y_start1,
pin.SLIDER1.s_val,
pin.SLIDER2.s_val,
pin.SLIDER3.s_val,
pin.SLIDER4.s_val,
pin.SLIDER5.s_val,
pin.PANEL.y_start2,
pin.PANEL.y_vefx,
pin.SYSTEM.v_service,
pin.TURNTABLE1,
pin.P1SW2.b_val,
pin.P1SW4.b_val,
pin.P1SW6.b_val,
pin.SYSTEM.v_coin,
pin.P2SW2.b_val,
pin.P2SW4.b_val,
pin.P2SW6.b_val,
pin.TURNTABLE2,
pin.P1SW1.b_val,
pin.P1SW3.b_val,
pin.P1SW5.b_val,
pin.P1SW7.b_val,
pin.P2SW1.b_val,
pin.P2SW3.b_val,
pin.P2SW5.b_val,
pin.P2SW7.b_val
);
return true;
}

View File

@@ -0,0 +1,14 @@
#ifndef ACIOTEST_BI2A_IIDX_H
#define ACIOTEST_BI2A_IIDX_H
#include <stdbool.h>
#include <stdint.h>
#include "aciodrv/device.h"
bool aciotest_bi2a_iidx_handler_init(
struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx);
bool aciotest_bi2a_iidx_handler_update(
struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx);
#endif

View File

@@ -1,5 +1,6 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
@@ -7,6 +8,7 @@
#include "aciodrv/device.h"
#include "aciotest/bi2a-sdvx.h"
#include "aciotest/bi2a-iidx.h"
#include "aciotest/handler.h"
#include "aciotest/icca.h"
#include "aciotest/kfca.h"
@@ -16,7 +18,7 @@
#include "util/log.h"
static uint8_t aciotest_cnt = 0;
static uint8_t bi2a_mode = 0;
static uint8_t bi2a_mode = 255;
/**
* Enumerate supported ACIO nodes based on their product id.
@@ -53,14 +55,30 @@ static bool aciotest_assign_handler(
}
if (product_type == AC_IO_NODE_TYPE_BI2A) {
if (bi2a_mode == 0) {
if (bi2a_mode == 255) {
printf(
"Unknown BI2A mode specified, please check your command.\n"
"Using bi2a-sdvx mode as default, press ENTER to continue\n"
);
bi2a_mode = 0;
getchar();
}
switch (bi2a_mode)
{
case 0:
handler->init = aciotest_bi2a_sdvx_handler_init;
handler->update = aciotest_bi2a_sdvx_handler_update;
break;
case 1:
handler->init = aciotest_bi2a_iidx_handler_init;
handler->update = aciotest_bi2a_iidx_handler_update;
break;
return true;
} else {
printf("Unknown BI2A device specified");
default:
break;
}
return true;
}
return false;
@@ -76,13 +94,26 @@ int main(int argc, char **argv)
"aciotest, build "__DATE__
" " __TIME__
"\n"
"Usage: %s <com port str> <baud rate>\n"
"Example for two slotted readers: %s COM1 57600\n",
"Usage: %s <com port str> <baud rate> <bi2a mode(optional)>\n"
"Example:\n"
"\"%s COM1 57600\" for generic acio device\n"
"\"%s COM1 57600 bi2a-iidx\" for the iidx BI2A mode\n"
"\"%s COM1 57600 bi2a-sdvx\" for the sdvx BI2A mode\n",
argv[0],
argv[0],
argv[0],
argv[0]);
return -1;
}
if (argc == 4) {
if(!strcmp(argv[3],"bi2a-iidx")) {
bi2a_mode = 1;
} else if(!strcmp(argv[3],"bi2a-sdvx")) {
bi2a_mode = 0;
}
}
log_to_writer(log_writer_stdout, NULL);
struct aciodrv_device_ctx *device =
@@ -93,7 +124,8 @@ int main(int argc, char **argv)
return -1;
}
printf("Opening acio device successful\n");
printf("Opening acio device successful, press ENTER to continue\n");
getchar();
uint8_t node_count = aciodrv_device_get_node_count(device);
printf("Enumerated %d nodes\n", node_count);

View File

@@ -0,0 +1,9 @@
dlls += ddrio-async
ldflags_ddrio-async:= \
libs_ddrio-async := \
util \
src_ddrio-async := \
ddrio.c \

View File

@@ -0,0 +1,11 @@
LIBRARY ddrio-async
EXPORTS
ddr_io_set_loggers
ddr_io_fini
ddr_io_init
ddr_io_read_pad
ddr_io_set_lights_extio
ddr_io_set_lights_p3io
ddr_io_set_lights_hdxs_panel
ddr_io_set_lights_hdxs_rgb

View File

@@ -0,0 +1,268 @@
#define LOG_MODULE "ddrio-async"
#include <windows.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "bemanitools/ddrio.h"
#include "util/log.h"
#include "util/thread.h"
#include "util/time.h"
typedef void (*ddr_io_set_loggers_t)(
log_formatter_t misc,
log_formatter_t info,
log_formatter_t warning,
log_formatter_t fatal);
typedef bool (*ddr_io_init_t)(
thread_create_t thread_create,
thread_join_t thread_join,
thread_destroy_t thread_destroy);
typedef uint32_t (*ddr_io_read_pad_t)(void);
typedef void (*ddr_io_set_lights_extio_t)(uint32_t extio_lights);
typedef void (*ddr_io_set_lights_p3io_t)(uint32_t p3io_lights);
typedef void (*ddr_io_set_lights_hdxs_panel_t)(uint32_t hdxs_lights);
typedef void (*ddr_io_set_lights_hdxs_rgb_t)(uint8_t idx, uint8_t r, uint8_t g, uint8_t b);
typedef void (*ddr_io_fini_t)(void);
static HMODULE _child_ddr_io_module;
static ddr_io_set_loggers_t _child_ddr_io_set_loggers;
static ddr_io_init_t _child_ddr_io_init;
static ddr_io_read_pad_t _child_ddr_io_read_pad;
static ddr_io_set_lights_extio_t _child_ddr_io_set_lights_extio;
static ddr_io_set_lights_p3io_t _child_ddr_io_set_lights_p3io;
static ddr_io_set_lights_hdxs_panel_t _child_ddr_io_set_lights_hdxs_panel;
static ddr_io_set_lights_hdxs_rgb_t _child_ddr_io_set_lights_hdxs_rgb;
static ddr_io_fini_t _child_ddr_io_fini;
static log_formatter_t _log_formatter_misc;
static log_formatter_t _log_formatter_info;
static log_formatter_t _log_formatter_warning;
static log_formatter_t _log_formatter_fatal;
static _Atomic(bool) _io_thread_proc_loop;
static _Atomic(bool) _io_thread_proc_running;
static _Atomic(uint32_t) _child_ddr_io_data_pad;
static _Atomic(uint32_t) _child_ddr_io_data_extio_lights;
static _Atomic(uint32_t) _child_ddr_io_data_p3io_lights;
static int _io_thread_proc(void *ctx)
{
uint64_t time_start;
uint64_t time_end;
uint64_t loop_counter;
uint64_t total_time;
uint32_t prev_child_ddr_io_data_extio_lights;
uint32_t prev_child_ddr_io_data_p3io_lights;
uint32_t local_tmp;
atomic_store_explicit(
&_io_thread_proc_running,
true,
memory_order_seq_cst);
log_info("IO thread running");
prev_child_ddr_io_data_extio_lights = atomic_load_explicit(
&_child_ddr_io_data_extio_lights, memory_order_seq_cst);
prev_child_ddr_io_data_p3io_lights = atomic_load_explicit(
&_child_ddr_io_data_p3io_lights, memory_order_seq_cst);
time_start = time_get_counter();
loop_counter = 0;
while (atomic_load_explicit(&_io_thread_proc_loop, memory_order_seq_cst)) {
local_tmp = _child_ddr_io_read_pad();
atomic_store_explicit(
&_child_ddr_io_data_pad,
local_tmp,
memory_order_relaxed);
// Only update outputs when they change gives this loop a major performance boost
// The write calls on a p3io for the outputs are very expensive (~12 ms) as they
// are executed over the ACIO protocol compared to only the input read
// calls (~4 ms) which have a dedicated IOCTL call/endpoint
local_tmp = atomic_load_explicit(
&_child_ddr_io_data_extio_lights,
memory_order_relaxed);
if (local_tmp != prev_child_ddr_io_data_extio_lights) {
_child_ddr_io_set_lights_extio(local_tmp);
prev_child_ddr_io_data_extio_lights = local_tmp;
}
local_tmp = atomic_load_explicit(
&_child_ddr_io_data_p3io_lights,
memory_order_relaxed);
if (local_tmp != prev_child_ddr_io_data_p3io_lights) {
_child_ddr_io_set_lights_p3io(local_tmp);
prev_child_ddr_io_data_p3io_lights = local_tmp;
}
// Don't hog the CPU
SwitchToThread();
loop_counter++;
}
time_end = time_get_counter();
total_time = time_get_elapsed_us(time_end - time_start);
log_info("IO thread performance: total iterations %lld, avg. loop cycle time %f us",
loop_counter, ((double) total_time) / loop_counter);
atomic_store_explicit(
&_io_thread_proc_running,
false,
memory_order_seq_cst);
log_info("IO thread shut down");
return 0;
}
static void* _load_function(HMODULE module, const char* name)
{
void* ptr;
ptr = GetProcAddress(module, name);
if (ptr == NULL) {
log_fatal("Could not find function %s in ddr3io child library", name);
}
return ptr;
}
void ddr_io_set_loggers(
log_formatter_t misc,
log_formatter_t info,
log_formatter_t warning,
log_formatter_t fatal)
{
_log_formatter_misc = misc;
_log_formatter_info = info;
_log_formatter_warning = warning;
_log_formatter_fatal = fatal;
log_to_external(misc, info, warning, fatal);
}
bool ddr_io_init(
thread_create_t thread_create,
thread_join_t thread_join,
thread_destroy_t thread_destroy)
{
log_info("Loading ddrio-async-child.dll as child ddrio library...");
_child_ddr_io_module = LoadLibraryA("ddrio-async-child.dll");
if (_child_ddr_io_module == NULL) {
log_warning("Loading ddrio-async-child.dll failed");
return false;
}
_child_ddr_io_set_loggers = _load_function(_child_ddr_io_module, "ddr_io_set_loggers");
_child_ddr_io_init = _load_function(_child_ddr_io_module, "ddr_io_init");
_child_ddr_io_read_pad = _load_function(_child_ddr_io_module, "ddr_io_read_pad");
_child_ddr_io_set_lights_extio = _load_function(_child_ddr_io_module, "ddr_io_set_lights_extio");
_child_ddr_io_set_lights_p3io = _load_function(_child_ddr_io_module, "ddr_io_set_lights_p3io");
_child_ddr_io_set_lights_hdxs_panel = _load_function(_child_ddr_io_module, "ddr_io_set_lights_hdxs_panel");
_child_ddr_io_set_lights_hdxs_rgb = _load_function(_child_ddr_io_module, "ddr_io_set_lights_hdxs_rgb");
_child_ddr_io_fini = _load_function(_child_ddr_io_module, "ddr_io_fini");
_child_ddr_io_set_loggers(
_log_formatter_misc,
_log_formatter_info,
_log_formatter_warning,
_log_formatter_fatal);
log_info("Calling child ddr_io_init...");
if (!_child_ddr_io_init(thread_create, thread_join, thread_destroy)) {
log_warning("Child ddr_io_init failed");
FreeLibrary(_child_ddr_io_module);
return false;
}
atomic_store_explicit(
&_io_thread_proc_loop,
true,
memory_order_seq_cst);
if (!thread_create(_io_thread_proc, NULL, 16384, 0)) {
log_warning("Creating IO thread failed");
_child_ddr_io_fini();
FreeLibrary(_child_ddr_io_module);
return false;
}
return true;
}
uint32_t ddr_io_read_pad(void)
{
return atomic_load_explicit(&_child_ddr_io_data_pad, memory_order_relaxed);
}
void ddr_io_set_lights_extio(uint32_t extio_lights)
{
atomic_store_explicit(
&_child_ddr_io_data_extio_lights,
extio_lights,
memory_order_relaxed);
}
void ddr_io_set_lights_p3io(uint32_t p3io_lights)
{
atomic_store_explicit(
&_child_ddr_io_data_p3io_lights,
p3io_lights,
memory_order_relaxed);
}
void ddr_io_set_lights_hdxs_panel(uint32_t lights)
{
// Not implemented for now
}
void ddr_io_set_lights_hdxs_rgb(uint8_t idx, uint8_t r, uint8_t g, uint8_t b)
{
// Not implemented for now
}
void ddr_io_fini(void)
{
atomic_store_explicit(
&_io_thread_proc_loop,
false,
memory_order_seq_cst);
log_info("Shutting down IO thread and waiting for it to finish...");
while (atomic_load_explicit(&_io_thread_proc_running, memory_order_seq_cst)) {
Sleep(1);
}
log_info("IO thread finished");
_child_ddr_io_fini();
FreeLibrary(_child_ddr_io_module);
}

View File

@@ -20,7 +20,7 @@ void dev_list_init(struct dev_list *devs, const GUID *class_guid)
devs->infolist = SetupDiGetClassDevs(
devs->class_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (devs->infolist == NULL) {
if (devs->infolist == INVALID_HANDLE_VALUE) {
log_fatal(
"SetupDiGetClassDevs failed: %08x", (unsigned int) GetLastError());
}

View File

@@ -48,7 +48,12 @@ arrays_fail:
hid_meta_out_fini_caps(meta);
caps_fail:
return false;
/**
* Still allow the device to be used as input.
* Useful for devices like the Sony DUALSHOCK 4, where it indicates it can receive outputs
* but it errors out when trying to initialize an output report.
*/
return true;
}
static bool
@@ -99,9 +104,11 @@ hid_meta_out_init_caps(struct hid_meta_out *meta, PHIDP_PREPARSED_DATA ppd)
caps_val_fail:
free(meta->caps_val);
meta->caps_val = NULL;
caps_btn_fail:
free(meta->caps_btn);
meta->caps_btn = NULL;
caps_tlc_fail:
return false;
@@ -114,7 +121,6 @@ static bool hid_meta_out_init_arrays(struct hid_meta_out *meta)
bool *report_presence;
unsigned int nreports;
unsigned int i;
uint8_t *bytes;
size_t nbytes;
size_t count;
@@ -174,7 +180,6 @@ static bool hid_meta_out_init_arrays(struct hid_meta_out *meta)
for (i = 0; i < 0x100; i++) {
if (report_presence[i]) {
nbytes = meta->caps_tlc.OutputReportByteLength;
bytes = xmalloc(meta->caps_tlc.OutputReportByteLength);
if (!hid_report_out_init(
&meta->reports[meta->nreports],
@@ -198,14 +203,22 @@ static bool hid_meta_out_init_arrays(struct hid_meta_out *meta)
return true;
r_init_fail:
free(bytes);
for (i = meta->nreports; i > 0; i--) {
hid_report_out_fini(&meta->reports[i - 1]);
}
free(report_presence);
// Clear out all output/light data
meta->nreports = 0;
free(meta->reports);
meta->reports = NULL;
meta->nlights = 0;
meta->nbuttons = 0;
// Failed out before allocating memory
meta->lights = NULL;
meta->priv_lights = NULL;
return false;
}
@@ -433,5 +446,8 @@ static void hid_meta_out_fini_arrays(struct hid_meta_out *meta)
static void hid_meta_out_fini_caps(struct hid_meta_out *meta)
{
free(meta->caps_btn);
meta->caps_btn = NULL;
free(meta->caps_val);
meta->caps_val = NULL;
}

View File

@@ -52,18 +52,18 @@ static const struct hook_symbol jbhook1_log_gftools_hook_syms2[] = {
.link = (void **) &real_ea3_boot},
};
static void avs_boot_replace_property_uint32(
struct property_node *node, const char *name, uint32_t val)
static void avs_boot_create_property_str(
struct property *config, const char *name, const char *val)
{
struct property_node *tmp;
tmp = property_search(NULL, node, name);
tmp = property_node_create(config, NULL, PROPERTY_TYPE_STR, name, val);
if (tmp) {
property_node_remove(tmp);
property_node_datasize(tmp);
} else {
log_fatal("Could not avs_boot_create_prop_str(%s, %s)", name, val);
}
property_node_create(NULL, node, PSMAP_TYPE_U32, name, val);
}
static void avs_boot_replace_property_str(
@@ -81,11 +81,13 @@ static void avs_boot_replace_property_str(
if (tmp) {
property_node_datasize(tmp);
} else {
log_fatal("Could not avs_boot_replace_property_str(%s, %s)", name, val);
}
}
static void my_avs_boot(
struct property_node *config,
struct property_node *_config,
void *std_heap,
size_t sz_std_heap,
void *avs_heap,
@@ -95,14 +97,44 @@ static void my_avs_boot(
{
log_info("Called my_avs_boot");
avs_boot_replace_property_uint32(config, "log/level", 4);
avs_boot_replace_property_str(
// avshelper gates this behind a check that might not always be true
CreateDirectoryA("./CONF", 0);
CreateDirectoryA("./CONF/NVRAM", 0);
CreateDirectoryA("./CONF/RAW", 0);
// The default config isn't very exciting, and we need to replace the NVRAM
// settings with our own anyway. Trying to edit the config almost always
// fails because of some weird fragmentation issues causing node insert to
// fail, so just recreate the entire thing from scratch.
uint8_t cfg_buf[2048];
struct property *config = property_create(
PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE | PROPERTY_FLAG_CREATE,
cfg_buf,
sizeof(cfg_buf));
log_assert(config);
property_node_create(
config, NULL, PROPERTY_TYPE_U32, "/config/log/level", 4);
avs_boot_create_property_str(config, "/config/fs/root/device", ".");
avs_boot_create_property_str(config, "/config/fs/raw/device", "./CONF/RAW");
avs_boot_create_property_str(
config, "/config/fs/nvram/device", "./CONF/NVRAM");
avs_boot_replace_property_str(
config, "/config/fs/raw/device", "./CONF/RAW");
avs_boot_create_property_str(config, "/config/fs/nvram/fstype", "fs");
avs_boot_create_property_str(config, "/config/fs/nvram/option", "");
property_node_create(
config, NULL, PROPERTY_TYPE_U16, "/config/net/nr_protocol", 8);
property_node_create(
config, NULL, PROPERTY_TYPE_U16, "/config/net/nr_socket", 8);
property_node_create(
config, NULL, PROPERTY_TYPE_U8, "/config/net/enable_raw", 1);
struct property_node *config_node =
property_search(config, NULL, "/config");
log_assert(config_node);
real_avs_boot(
config,
config_node,
std_heap,
sz_std_heap,
avs_heap,
@@ -168,4 +200,4 @@ void jbhook1_avs_boot_set_eamuse_addr(const struct net_addr *server_addr)
&jbhook1_avs_boot_eamuse_server_addr,
server_addr,
sizeof(struct net_addr));
}
}

View File

@@ -47,6 +47,29 @@ my_mwindow_create(HINSTANCE, void *, const char *, DWORD, DWORD, BOOL);
static HWND(CDECL *real_mwindow_create)(
HINSTANCE, void *, const char *, DWORD, DWORD, BOOL);
static BOOL STDCALL my_CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
static BOOL(STDCALL *real_CreateProcessA)(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
static const struct hook_symbol init_hook_syms[] = {
{
.name = "mwindow_create",
@@ -55,6 +78,17 @@ static const struct hook_symbol init_hook_syms[] = {
},
};
static const struct hook_symbol kernel32_hook_syms[] = {
{
.name = "CreateProcessA",
.patch = my_CreateProcessA,
.link = (void **) &real_CreateProcessA,
},
};
// so our CreateProcessA hook can check
static bool vertical;
/**
* This seems to be a good entry point to intercept before the game calls
* anything important (very close to the start of WinMain).
@@ -107,6 +141,7 @@ static HWND CDECL my_mwindow_create(
jbhook1_log_gftools_init();
fullscreen = !config_gfx.windowed;
vertical = config_gfx.vertical;
if (config_gfx.vertical) {
DWORD tmp = window_width;
@@ -175,9 +210,53 @@ BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
/* Actual hooks for game specific stuff */
hook_table_apply(
NULL,
"kernel32.dll",
kernel32_hook_syms,
lengthof(kernel32_hook_syms));
acp_hook_init();
adapter_hook_init();
}
return TRUE;
}
static BOOL STDCALL my_CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation)
{
LPSTR rot_arg;
// de-rotate the error window if needed. In theory this should parse the
// commandline properly, in practice this works on all the .exe jubeats.
if (vertical && lpCommandLine) {
if ((rot_arg = strstr(lpCommandLine, " -R90 "))) { // jubeat 01
memcpy(rot_arg, " -R0 ", strlen(" -R0 "));
} else if ((rot_arg =
strstr(lpCommandLine, " -rot:90 "))) { // jubeat 02
memcpy(rot_arg, " -rot:0 ", strlen(" -rot:0 "));
}
}
return real_CreateProcessA(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
}

View File

@@ -57,7 +57,7 @@ static HANDLE mm_open_device(void)
dev_info = SetupDiGetClassDevsW(
&hid_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (dev_info == NULL) {
if (dev_info == INVALID_HANDLE_VALUE) {
log_fatal("SetupDiGetClassDevs failed");
}

View File

@@ -41,7 +41,7 @@ void p3io_resp_hdr_init(
{
log_assert(resp_hdr != NULL);
log_assert(req_hdr != NULL);
log_assert(nbytes < P3IO_MAX_MESSAGE_SIZE);
log_assert(nbytes <= P3IO_MAX_MESSAGE_SIZE);
/* Length byte in this packet format counts everything from the length
byte onwards. The length byte itself occurs at the start of the frame. */

View File

@@ -161,6 +161,11 @@ struct p3io_req_rs232_write {
uint8_t bytes[128];
};
struct p3io_req_unknown_generic {
struct p3io_hdr hdr;
uint8_t unknown;
};
struct p3io_req_raw {
uint8_t data[P3IO_MAX_MESSAGE_SIZE];
};
@@ -182,6 +187,7 @@ union p3io_req_any {
struct p3io_req_rs232_open_close rs232_open_close;
struct p3io_req_rs232_read rs232_read;
struct p3io_req_rs232_write rs232_write;
struct p3io_req_unknown_generic unknown_generic;
struct p3io_req_raw raw;
};
@@ -266,6 +272,11 @@ struct p3io_resp_rs232_write {
uint8_t nbytes;
};
struct p3io_resp_unknown_generic {
struct p3io_hdr hdr;
uint8_t unknown;
};
struct p3io_resp_raw {
uint8_t data[P3IO_MAX_MESSAGE_SIZE];
};
@@ -286,6 +297,7 @@ union p3io_resp_any {
struct p3io_resp_rs232_open_close rs232_open_close;
struct p3io_resp_rs232_read rs232_read;
struct p3io_resp_rs232_write rs232_write;
struct p3io_resp_unknown_generic unknown_generic;
struct p3io_resp_raw raw;
};

View File

@@ -49,6 +49,8 @@ static void p3io_cmd_get_video_freq(
const struct p3io_req_get_video_freq *req,
struct p3io_resp_get_video_freq *resp);
static void
p3io_cmd_unknown_2b(const struct p3io_req_unknown_2b *req, struct p3io_resp_unknown_2b *resp);
static void
p3io_cmd_init(const struct p3io_req_init *req, struct p3io_resp_init *resp);
static void p3io_cmd_get_coinstock(
const struct p3io_req_coin_stock *req, struct p3io_resp_coin_stock *resp);
@@ -56,7 +58,7 @@ static void p3io_cmd_set_coin_counter(
const struct p3io_req_set_coin_counter *req,
struct p3io_resp_set_coin_counter *resp);
static void
p3io_cmd_unknown(const union p3io_req_any *req, struct p3io_resp_raw *resp);
p3io_cmd_unknown(const struct p3io_req_unknown_generic *req, struct p3io_resp_unknown_generic *resp);
void p3io_emu_init(const struct p3io_ops *ops, void *ctx)
{
@@ -268,6 +270,11 @@ static HRESULT p3io_cmd_dispatch(const union p3io_req_any *req)
break;
case P3IO_CMD_UNKNOWN_2B:
p3io_cmd_unknown_2b(&req->unknown_2b, &resp.unknown_2b);
break;
case P3IO_CMD_INIT:
p3io_cmd_init(&req->init, &resp.init);
@@ -301,7 +308,7 @@ static HRESULT p3io_cmd_dispatch(const union p3io_req_any *req)
break;
default:
p3io_cmd_unknown(req, &resp.raw);
p3io_cmd_unknown(&req->unknown_generic, &resp.unknown_generic);
break;
}
@@ -511,6 +518,16 @@ static void p3io_cmd_get_video_freq(
}
}
static void
p3io_cmd_unknown_2b(const struct p3io_req_unknown_2b *req, struct p3io_resp_unknown_2b *resp)
{
log_misc("Unknown 2b");
p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr);
resp->unknown = 0;
}
static void
p3io_cmd_init(const struct p3io_req_init *req, struct p3io_resp_init *resp)
{
@@ -557,15 +574,16 @@ static void p3io_cmd_set_coin_counter(
}
static void
p3io_cmd_unknown(const union p3io_req_any *req, struct p3io_resp_raw *resp)
p3io_cmd_unknown(const struct p3io_req_unknown_generic *req, struct p3io_resp_unknown_generic *resp)
{
log_warning("Unsupported P3IO command: %02x", req->hdr.cmd);
log_warning("Unsupported P3IO command, sending default response (might not work/crash though): %02x",
req->hdr.cmd);
p3io_resp_hdr_init(
(struct p3io_hdr *) &resp->data, sizeof(*resp), &req->hdr);
p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr);
// Not always applicable/correct as there are several commands not
// responding with any data, but fine for the majority of (unsupported)
// commands
resp->data[sizeof(struct p3io_hdr) + 0] = 0;
// Remark: This might also lead to unpredictable behaviour or crashes
resp->unknown = 0;
}