MDX: Fixed Ring Buffer Traversal Direction (P4IO Timing Fix) (#441)

## Description of change
In the real `libacio2.dll` implementation,
`ac_io_mdxf_get_control_status_buffer` returns the ring buffer entry at
`(head - index) mod size` but this function was using `(head + index)
mod size` instead. Basically, `index` is supposed to indicate how many
entries back, not forward. This was causing the polling history of each
player to be returned backwards. A few other things were updated to
bring the function's behavior closer to the real implementation:

1. Number of ring buffer entries per player were increased to 16 (this
is the size of them in the real implementation)
2. `ac_io_mdxf_get_control_status_buffer` now returns the current head
of the ring buffer (instead of a success/failure boolean), which is what
`arkmdxp4.dll` expects.

## Testing
I played a few songs to make sure nothing was broken. I also attached a
debugger and verified the ring buffer entries are *actually* now being
returned in reverse chronological order as intended.
This commit is contained in:
cchike
2025-12-11 10:38:47 -08:00
committed by GitHub
parent e100093ead
commit dc7d8515d3
+45 -25
View File
@@ -11,14 +11,14 @@
const size_t STATUS_BUFFER_SIZE = 32;
// static stuff
static uint8_t COUNTER_P1 = 0;
static uint8_t COUNTER_P2 = 0;
static uint8_t HEAD_P1 = 0;
static uint8_t HEAD_P2 = 0;
// buffers
#pragma pack(push, 1)
static struct {
uint8_t STATUS_BUFFER_P1[7][STATUS_BUFFER_SIZE] {};
uint8_t STATUS_BUFFER_P2[7][STATUS_BUFFER_SIZE] {};
uint8_t STATUS_BUFFER_P1[16][STATUS_BUFFER_SIZE] {};
uint8_t STATUS_BUFFER_P2[16][STATUS_BUFFER_SIZE] {};
} BUFFERS {};
#pragma pack(pop)
@@ -41,30 +41,50 @@ static uint64_t arkGetTickTime64() {
* Implementations
*/
static bool __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *buffer, uint8_t a3, uint8_t a4) {
static uint64_t __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *out, uint8_t index, uint8_t head_in) {
// Default error value (matches original mask behavior)
auto error_ret = static_cast<uint64_t>(node - 0x11) & 0xFFFFFFFFFFFFFF00;
// Dance Dance Revolution
if (avs::game::is_model("MDX")) {
// copy buffer
if (node == 17 || node == 25) {
// get buffer index
auto i = (COUNTER_P1 + a3) % std::size(BUFFERS.STATUS_BUFFER_P1);
memcpy(buffer, BUFFERS.STATUS_BUFFER_P1[i], STATUS_BUFFER_SIZE);
} else if (node == 18 || node == 26) {
// get buffer index
auto i = (COUNTER_P2 + a3) % std::size(BUFFERS.STATUS_BUFFER_P2);
memcpy(buffer, BUFFERS.STATUS_BUFFER_P2[i], STATUS_BUFFER_SIZE);
} else {
// Select player-specific state
uint8_t head;
uint8_t (*buffer)[STATUS_BUFFER_SIZE];
size_t size;
// fill with zeros on unknown node
memset(buffer, 0, STATUS_BUFFER_SIZE);
return false;
}
if (node == 17 || node == 25) {
head = HEAD_P1;
buffer = BUFFERS.STATUS_BUFFER_P1;
size = std::size(BUFFERS.STATUS_BUFFER_P1);
} else if (node == 18 || node == 26) {
head = HEAD_P2;
buffer = BUFFERS.STATUS_BUFFER_P2;
size = std::size(BUFFERS.STATUS_BUFFER_P2);
} else {
memset(out, 0, STATUS_BUFFER_SIZE);
return error_ret;
}
if (head_in != 0xFF) {
head = head_in;
}
// Compute ring index: walk backwards from head as index increases
// Assumes ring buffer size is a power of two
const size_t mask = size - 1;
const size_t offset = static_cast<size_t>(index) & mask;
const size_t i = (static_cast<size_t>(head) - offset + size) & mask;
// Copy the chosen entry
memcpy(out, buffer[i], STATUS_BUFFER_SIZE);
// Return the head value actually used
return static_cast<uint64_t>(head);
}
// return success
return true;
return error_ret;
}
static bool __cdecl ac_io_mdxf_set_output_level(unsigned int a1, unsigned int a2, uint8_t value) {
@@ -119,14 +139,14 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) {
case 17:
case 25:
// increase counter
COUNTER_P1 = (COUNTER_P1 + 1) % std::size(BUFFERS.STATUS_BUFFER_P1);
buffer = BUFFERS.STATUS_BUFFER_P1[COUNTER_P1];
HEAD_P1 = (HEAD_P1 + 1) % std::size(BUFFERS.STATUS_BUFFER_P1);
buffer = BUFFERS.STATUS_BUFFER_P1[HEAD_P1];
break;
case 18:
case 26:
// increase counter
COUNTER_P2 = (COUNTER_P2 + 1) % std::size(BUFFERS.STATUS_BUFFER_P2);
buffer = BUFFERS.STATUS_BUFFER_P2[COUNTER_P2];
HEAD_P2 = (HEAD_P2 + 1) % std::size(BUFFERS.STATUS_BUFFER_P2);
buffer = BUFFERS.STATUS_BUFFER_P2[HEAD_P2];
break;
default: