mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
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:
@@ -11,14 +11,14 @@
|
|||||||
const size_t STATUS_BUFFER_SIZE = 32;
|
const size_t STATUS_BUFFER_SIZE = 32;
|
||||||
|
|
||||||
// static stuff
|
// static stuff
|
||||||
static uint8_t COUNTER_P1 = 0;
|
static uint8_t HEAD_P1 = 0;
|
||||||
static uint8_t COUNTER_P2 = 0;
|
static uint8_t HEAD_P2 = 0;
|
||||||
|
|
||||||
// buffers
|
// buffers
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
static struct {
|
static struct {
|
||||||
uint8_t STATUS_BUFFER_P1[7][STATUS_BUFFER_SIZE] {};
|
uint8_t STATUS_BUFFER_P1[16][STATUS_BUFFER_SIZE] {};
|
||||||
uint8_t STATUS_BUFFER_P2[7][STATUS_BUFFER_SIZE] {};
|
uint8_t STATUS_BUFFER_P2[16][STATUS_BUFFER_SIZE] {};
|
||||||
} BUFFERS {};
|
} BUFFERS {};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
@@ -41,30 +41,50 @@ static uint64_t arkGetTickTime64() {
|
|||||||
* Implementations
|
* 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
|
// Dance Dance Revolution
|
||||||
if (avs::game::is_model("MDX")) {
|
if (avs::game::is_model("MDX")) {
|
||||||
|
|
||||||
// copy buffer
|
// Select player-specific state
|
||||||
if (node == 17 || node == 25) {
|
uint8_t head;
|
||||||
// get buffer index
|
uint8_t (*buffer)[STATUS_BUFFER_SIZE];
|
||||||
auto i = (COUNTER_P1 + a3) % std::size(BUFFERS.STATUS_BUFFER_P1);
|
size_t size;
|
||||||
memcpy(buffer, BUFFERS.STATUS_BUFFER_P1[i], STATUS_BUFFER_SIZE);
|
|
||||||
} else if (node == 18 || node == 26) {
|
if (node == 17 || node == 25) {
|
||||||
// get buffer index
|
head = HEAD_P1;
|
||||||
auto i = (COUNTER_P2 + a3) % std::size(BUFFERS.STATUS_BUFFER_P2);
|
buffer = BUFFERS.STATUS_BUFFER_P1;
|
||||||
memcpy(buffer, BUFFERS.STATUS_BUFFER_P2[i], STATUS_BUFFER_SIZE);
|
size = std::size(BUFFERS.STATUS_BUFFER_P1);
|
||||||
} else {
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
// fill with zeros on unknown node
|
if (head_in != 0xFF) {
|
||||||
memset(buffer, 0, STATUS_BUFFER_SIZE);
|
head = head_in;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
// 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 error_ret;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool __cdecl ac_io_mdxf_set_output_level(unsigned int a1, unsigned int a2, uint8_t value) {
|
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 17:
|
||||||
case 25:
|
case 25:
|
||||||
// increase counter
|
// increase counter
|
||||||
COUNTER_P1 = (COUNTER_P1 + 1) % std::size(BUFFERS.STATUS_BUFFER_P1);
|
HEAD_P1 = (HEAD_P1 + 1) % std::size(BUFFERS.STATUS_BUFFER_P1);
|
||||||
buffer = BUFFERS.STATUS_BUFFER_P1[COUNTER_P1];
|
buffer = BUFFERS.STATUS_BUFFER_P1[HEAD_P1];
|
||||||
break;
|
break;
|
||||||
case 18:
|
case 18:
|
||||||
case 26:
|
case 26:
|
||||||
// increase counter
|
// increase counter
|
||||||
COUNTER_P2 = (COUNTER_P2 + 1) % std::size(BUFFERS.STATUS_BUFFER_P2);
|
HEAD_P2 = (HEAD_P2 + 1) % std::size(BUFFERS.STATUS_BUFFER_P2);
|
||||||
buffer = BUFFERS.STATUS_BUFFER_P2[COUNTER_P2];
|
buffer = BUFFERS.STATUS_BUFFER_P2[HEAD_P2];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user