diff --git a/common/board/io4.c b/common/board/io4.c index 8001457..d8a5c33 100644 --- a/common/board/io4.c +++ b/common/board/io4.c @@ -241,13 +241,21 @@ static HRESULT io4_handle_write(struct irp *irp) return S_OK; case IO4_CMD_SET_PWM_OUTPUT: - dprintf("USB I/O: PWM Out\n"); + // dprintf("USB I/O: PWM Out\n"); + + if (io4_ops->write_pwm != NULL) { + return io4_ops->write_pwm(out.payload, IO4_REPORT_OUT_PAYLOAD_LEN); + } return S_OK; case IO4_CMD_SET_UNIQUE_OUTPUT: // dprintf("USB I/O: Unique Out\n"); + if (io4_ops->write_unique != NULL) { + return io4_ops->write_unique(out.payload, IO4_REPORT_OUT_PAYLOAD_LEN); + } + return S_OK; case IO4_CMD_UPDATE_FIRMWARE: diff --git a/common/board/io4.h b/common/board/io4.h index 1f93cf1..db83219 100644 --- a/common/board/io4.h +++ b/common/board/io4.h @@ -30,6 +30,8 @@ struct io4_ops { HRESULT (*poll)(void* ctx, struct io4_state* state); HRESULT (*write_gpio)(uint8_t* payload, size_t len); + HRESULT (*write_pwm)(uint8_t* payload, size_t len); + HRESULT (*write_unique)(uint8_t* payload, size_t len); }; HRESULT io4_hook_init( diff --git a/common/board/led15070.c b/common/board/led15070.c index 43a015d..1cd9be5 100644 --- a/common/board/led15070.c +++ b/common/board/led15070.c @@ -71,6 +71,7 @@ static uint16_t led15070_fw_sum; static uint8_t led15070_host_adr = 0x01; #define led15070_nboards 2 +#define led15070_nleds 32 typedef struct { CRITICAL_SECTION lock; @@ -79,8 +80,10 @@ typedef struct { struct uart boarduart; uint8_t written_bytes[520]; uint8_t readable_bytes[520]; - uint8_t gs[32][4]; - uint8_t dc[32][3]; + uint8_t gs[led15070_nleds][4]; + uint8_t gs_fade[led15070_nleds][4]; + bool gs_fade_pending[led15070_nleds]; + uint8_t dc[led15070_nleds][3]; uint8_t fet[3]; uint8_t gs_palette[8][3]; wchar_t eeprom_path[MAX_PATH]; @@ -150,6 +153,8 @@ HRESULT led15070_hook_init( v->boarduart.readable.nbytes = sizeof(v->readable_bytes); memset(v->gs, 0, sizeof(v->gs)); + memset(v->gs_fade, 0, sizeof(v->gs_fade)); + memset(v->gs_fade_pending, 0, sizeof(v->gs_fade_pending)); memset(v->dc, 0, sizeof(v->dc)); memset(v->fet, 0, sizeof(v->fet)); memset(v->gs_palette, 0, sizeof(v->gs_palette)); @@ -454,6 +459,8 @@ static HRESULT led15070_req_set_normal_8bit(int board, const struct led15070_req led15070_per_board_vars[board].gs[idx][0] = req->payload[1]; // R led15070_per_board_vars[board].gs[idx][1] = req->payload[2]; // G led15070_per_board_vars[board].gs[idx][2] = req->payload[3]; // B + led15070_per_board_vars[board].gs[idx][3] = 0; + led15070_per_board_vars[board].gs_fade_pending[idx] = false; if (!led15070_per_board_vars[board].enable_response) return S_OK; @@ -473,31 +480,65 @@ static HRESULT led15070_req_set_normal_8bit(int board, const struct led15070_req return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); } +static void led15070_calc_range( + uint8_t start, + uint8_t count, + uint8_t skip, + uint8_t *out_start, + uint8_t *out_end) +{ + uint16_t s = start; + uint16_t c = count; + + if (c == 0) { + *out_start = 0; + *out_end = 0; + return; + } + + if (c >= led15070_nleds) { + c = led15070_nleds; + } + + if (skip > 0 && skip <= c) { + s += skip; + c -= skip; + } + + if (s >= led15070_nleds || c == 0) { + *out_start = led15070_nleds; + *out_end = led15070_nleds; + return; + } + + *out_start = (uint8_t) s; + *out_end = (s + c > led15070_nleds) ? led15070_nleds : (uint8_t) (s + c); +} + static HRESULT led15070_req_set_multi_flash_8bit(int board, const struct led15070_req_any *req) { uint8_t idx_start = req->payload[0]; - uint8_t idx_end = req->payload[1]; + uint8_t idx_count = req->payload[1]; uint8_t idx_skip = req->payload[2]; + uint8_t start; + uint8_t end; // TODO: useful? #if defined(LOG_LED15070) - dprintf("LED 15070: Set LED - Multi flash 8bit (board %u, start %u, end %u, skip %u)\n", - board, idx_start, idx_end, idx_skip); + dprintf("LED 15070: Set LED - Multi flash 8bit (board %u, start %u, count %u, skip %u)\n", + board, idx_start, idx_count, idx_skip); #endif - if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) { - idx_start += idx_skip; - } + led15070_calc_range(idx_start, idx_count, idx_skip, &start, &end); - int i = idx_start; - do { + for (int i = start; i < end; i++) { led15070_per_board_vars[board].gs[i][0] = req->payload[3]; // R led15070_per_board_vars[board].gs[i][1] = req->payload[4]; // G led15070_per_board_vars[board].gs[i][2] = req->payload[5]; // B /* Always 0, tells the controller to immediately change to this color */ led15070_per_board_vars[board].gs[i][3] = req->payload[6]; // Speed - i++; - } while (i < idx_end); + led15070_per_board_vars[board].gs_fade_pending[i] = false; + } if (!led15070_per_board_vars[board].enable_response) return S_OK; @@ -520,25 +561,24 @@ static HRESULT led15070_req_set_multi_flash_8bit(int board, const struct led1507 static HRESULT led15070_req_set_multi_fade_8bit(int board, const struct led15070_req_any *req) { uint8_t idx_start = req->payload[0]; - uint8_t idx_end = req->payload[1]; + uint8_t idx_count = req->payload[1]; uint8_t idx_skip = req->payload[2]; + uint8_t start; + uint8_t end; #if defined(LOG_LED15070) - dprintf("LED 15070: Set LED - Multi fade 8bit (board %u, start %u, end %u, skip %u)\n", - board, idx_start, idx_end, idx_skip); + dprintf("LED 15070: Set LED - Multi fade 8bit (board %u, start %u, count %u, skip %u)\n", + board, idx_start, idx_count, idx_skip); #endif - if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) { - idx_start += idx_skip; - } + led15070_calc_range(idx_start, idx_count, idx_skip, &start, &end); - int i = idx_start; - do { - led15070_per_board_vars[board].gs[i][0] = req->payload[3]; // R - led15070_per_board_vars[board].gs[i][1] = req->payload[4]; // G - led15070_per_board_vars[board].gs[i][2] = req->payload[5]; // B - led15070_per_board_vars[board].gs[i][3] = req->payload[6]; // Speed - i++; - } while (i < idx_end); + for (int i = start; i < end; i++) { + led15070_per_board_vars[board].gs_fade[i][0] = req->payload[3]; // R + led15070_per_board_vars[board].gs_fade[i][1] = req->payload[4]; // G + led15070_per_board_vars[board].gs_fade[i][2] = req->payload[5]; // B + led15070_per_board_vars[board].gs_fade[i][3] = req->payload[6]; // Speed + led15070_per_board_vars[board].gs_fade_pending[i] = true; + } if (!led15070_per_board_vars[board].enable_response) return S_OK; @@ -767,12 +807,56 @@ static HRESULT led15070_req_dc_update(int board, const struct led15070_req_any * static HRESULT led15070_req_gs_update(int board, const struct led15070_req_any *req) { + _led15070_per_board_vars *v = &led15070_per_board_vars[board]; #if defined(LOG_LED15070) dprintf("LED 15070: GS update (board %u)\n", board); #endif - if (led_gs_update) - led_gs_update(board, (const uint8_t*)led15070_per_board_vars[board].gs); + if (led_gs_update) { + bool has_fade = false; + uint8_t payload[led15070_nleds][4]; + + for (int i = 0; i < led15070_nleds; i++) { + if (v->gs_fade_pending[i]) { + has_fade = true; + break; + } + } + + if (has_fade) { + for (int i = 0; i < led15070_nleds; i++) { + payload[i][0] = v->gs[i][0]; + payload[i][1] = v->gs[i][1]; + payload[i][2] = v->gs[i][2]; + payload[i][3] = 0; + } + + led_gs_update(board, (const uint8_t*)payload); + + for (int i = 0; i < led15070_nleds; i++) { + if (v->gs_fade_pending[i]) { + payload[i][0] = v->gs_fade[i][0]; + payload[i][1] = v->gs_fade[i][1]; + payload[i][2] = v->gs_fade[i][2]; + payload[i][3] = v->gs_fade[i][3]; + v->gs_fade_pending[i] = false; + } else { + payload[i][0] = v->gs[i][0]; + payload[i][1] = v->gs[i][1]; + payload[i][2] = v->gs[i][2]; + payload[i][3] = v->gs[i][3]; + } + } + + led_gs_update(board, (const uint8_t*)payload); + } else { + led_gs_update(board, (const uint8_t*)v->gs); + } + } else { + for (int i = 0; i < led15070_nleds; i++) { + v->gs_fade_pending[i] = false; + } + } if (!led15070_per_board_vars[board].enable_response) return S_OK; diff --git a/games/mai2hook/io4.c b/games/mai2hook/io4.c index cb0b38d..a1903d2 100644 --- a/games/mai2hook/io4.c +++ b/games/mai2hook/io4.c @@ -11,11 +11,17 @@ #include "util/dprintf.h" static HRESULT mai2_io4_poll(void* ctx, struct io4_state* state); +static HRESULT mai2_io4_write_gpio(uint8_t* payload, size_t len); +static HRESULT mai2_io4_write_pwm(uint8_t* payload, size_t len); +static HRESULT mai2_io4_write_unique(uint8_t* payload, size_t len); static uint16_t coins; static const struct io4_ops mai2_io4_ops = { .poll = mai2_io4_poll, + .write_gpio = mai2_io4_write_gpio, + .write_pwm = mai2_io4_write_pwm, + .write_unique = mai2_io4_write_unique, }; HRESULT mai2_io4_hook_init(const struct io4_config* cfg) { @@ -148,5 +154,62 @@ static HRESULT mai2_io4_poll(void* ctx, struct io4_state* state) { state->buttons[1] |= 1 << 4; } + return S_OK; +} + +static HRESULT mai2_io4_write_gpio(uint8_t* payload, size_t len) { + if (len >= 4) { + char bin0[9]; + char bin1[9]; + for (int i = 0; i < 8; i++) { + bin0[7 - i] = (payload[0] & (1 << i)) ? '1' : '0'; + bin1[7 - i] = (payload[1] & (1 << i)) ? '1' : '0'; + } + bin0[8] = '\0'; + bin1[8] = '\0'; + +#if defined(LOG_IO4) + dprintf("IO4 LED: [%02X %02X %02X %02X] (Byte0: %s, Byte1: %s)\n", + payload[0], payload[1], payload[2], payload[3], bin0, bin1); + + dprintf(" 1P Code Reader: %s\n", (payload[0] & 0x20) ? "ON" : "OFF"); + dprintf(" 2P Code Reader: %s\n", (payload[0] & 0x04) ? "ON" : "OFF"); + dprintf(" Camera Ring: %s\n", (payload[1] & 0x80) ? "ON" : "OFF"); + dprintf(" Camera Rec: %s\n", (payload[1] & 0x40) ? "ON" : "OFF"); +#endif + } + return S_OK; +} + +static HRESULT mai2_io4_write_pwm(uint8_t* payload, size_t len) { + if (len >= 16) { +#if defined(LOG_IO4) + dprintf("IO4 PWM: %02X %02X %02X %02X %02X %02X %02X %02X ...\n", + payload[0], payload[1], payload[2], payload[3], + payload[4], payload[5], payload[6], payload[7]); +#endif + } + return S_OK; +} + +static HRESULT mai2_io4_write_unique(uint8_t* payload, size_t len) { + if (len < 8) { + return S_OK; + } + +#if defined(LOG_IO4) + dprintf("IO4 Unique: %02X %02X %02X %02X %02X %02X %02X %02X ...\n", + payload[0], payload[1], payload[2], payload[3], + payload[4], payload[5], payload[6], payload[7]); +#endif + + if (mai2_dll.led_set_leds) { + uint8_t rgb_1p[3] = { payload[2], payload[4], payload[6] }; + uint8_t rgb_2p[3] = { payload[3], payload[5], payload[7] }; + + mai2_dll.led_set_leds(0, rgb_1p); + mai2_dll.led_set_leds(1, rgb_2p); + } + return S_OK; } \ No newline at end of file diff --git a/games/mai2hook/mai2-dll.c b/games/mai2hook/mai2-dll.c index 121750e..9fa591d 100644 --- a/games/mai2hook/mai2-dll.c +++ b/games/mai2hook/mai2-dll.c @@ -8,6 +8,11 @@ #include "util/dll-bind.h" #include "util/dprintf.h" +enum { + MAI2_DLL_SYM_COUNT_V101 = 11, + MAI2_DLL_SYM_COUNT_V102 = 12, +}; + const struct dll_bind_sym mai2_dll_syms[] = { { .sym = "mai2_io_init", @@ -42,6 +47,9 @@ const struct dll_bind_sym mai2_dll_syms[] = { }, { .sym = "mai2_io_led_gs_update", .off = offsetof(struct mai2_dll, led_gs_update), + }, { + .sym = "mai2_io_led_billboard_set", + .off = offsetof(struct mai2_dll, led_set_leds), }, }; @@ -60,6 +68,7 @@ HRESULT mai2_dll_init(const struct mai2_dll_config *cfg, HINSTANCE self) HINSTANCE owned; HINSTANCE src; HRESULT hr; + size_t sym_count; assert(cfg != NULL); assert(self != NULL); @@ -104,7 +113,11 @@ HRESULT mai2_dll_init(const struct mai2_dll_config *cfg, HINSTANCE self) } sym = mai2_dll_syms; - hr = dll_bind(&mai2_dll, src, &sym, _countof(mai2_dll_syms)); + sym_count = (mai2_dll.api_version < 0x0102) + ? MAI2_DLL_SYM_COUNT_V101 + : MAI2_DLL_SYM_COUNT_V102; + + hr = dll_bind(&mai2_dll, src, &sym, sym_count); if (FAILED(hr)) { if (src != self) { diff --git a/games/mai2hook/mai2-dll.h b/games/mai2hook/mai2-dll.h index 599d44a..ac456e8 100644 --- a/games/mai2hook/mai2-dll.h +++ b/games/mai2hook/mai2-dll.h @@ -17,6 +17,7 @@ struct mai2_dll { void (*led_set_fet_output)(uint8_t board, const uint8_t *rgb); void (*led_dc_update)(uint8_t board, const uint8_t *rgb); void (*led_gs_update)(uint8_t board, const uint8_t *rgb); + void (*led_set_leds)(uint8_t board, const uint8_t *rgb); }; struct mai2_dll_config { diff --git a/games/mai2hook/mai2hook.def b/games/mai2hook/mai2hook.def index 50e4058..a3b3df8 100644 --- a/games/mai2hook/mai2hook.def +++ b/games/mai2hook/mai2hook.def @@ -23,3 +23,4 @@ EXPORTS mai2_io_led_set_fet_output mai2_io_led_dc_update mai2_io_led_gs_update + mai2_io_led_billboard_set diff --git a/games/mai2io/mai2io.c b/games/mai2io/mai2io.c index 57404cb..4864887 100644 --- a/games/mai2io/mai2io.c +++ b/games/mai2io/mai2io.c @@ -19,7 +19,7 @@ static bool mai2_io_touch_1p_stop_flag; static HANDLE mai2_io_touch_2p_thread; static bool mai2_io_touch_2p_stop_flag; -uint16_t mai2_io_get_api_version(void) { return 0x0101; } +uint16_t mai2_io_get_api_version(void) { return 0x0102; } HRESULT mai2_io_init(void) { mai2_io_config_load(&mai2_io_cfg, get_config_path()); @@ -265,3 +265,11 @@ void mai2_io_led_gs_update(uint8_t board, const uint8_t *rgb) { #endif return; } + +void mai2_io_led_billboard_set(uint8_t board, const uint8_t *rgb) { +#if 0 + uint8_t player = board + 1; + dprintf("Mai2 LED %dP: Billboard set R:%02X G:%02X B:%02X\n", player, rgb[0], rgb[1], rgb[2]); +#endif + return; +} diff --git a/games/mai2io/mai2io.h b/games/mai2io/mai2io.h index 9c0a7f0..4519a01 100644 --- a/games/mai2io/mai2io.h +++ b/games/mai2io/mai2io.h @@ -28,7 +28,7 @@ enum { the major version and the low byte is the minor version (as defined by the Semantic Versioning standard). - The latest API version as of this writing is 0x0100. */ + The latest API version as of this writing is 0x0102. */ uint16_t mai2_io_get_api_version(void); @@ -190,3 +190,12 @@ void mai2_io_led_dc_update(uint8_t board, const uint8_t *rgb); Minimum API version: 0x0101 */ void mai2_io_led_gs_update(uint8_t board, const uint8_t *rgb); + +/* Update the Billboard LEDs. rgb is a pointer to an array. + + maimai DX uses two boards. Board 0 is for the player 1 side (left) and board 1 + is for the player 2 side (right). + + Minimum API version: 0x0102 */ + +void mai2_io_led_billboard_set(uint8_t board, const uint8_t *rgb);