# Beatmania IIDX 10th Style - D01 IO boot code and security init Date: 2023-04-03 Author: icex2 Documenting decompiled and reverse engineered code snippets from the D01 JAE `bm2dx.exe`. These helped me figuring out the two different security boot modes the game supports. In summary, it supports booting with the C02 IO with a C02 black dongle and a D01 IO board with a D01 dongle. No other combination is valid because they didn't make sense back then. You either had an upgraded old style/twinkle cabinet to C02 (GEC02) or you bought a new dedicated cabinet (GQD01). With 10th style supporting the old C02 dongle, it appears that all owners of a C02 cabinet with IO board and C02 dongle received a free software update/HDD. This might make sense considering the short life span of C02 and the game being super buggy, especially in earlier/initial revisions. The game expects the following "configurations" from bemanitools: - Booting as upgraded C02 with free D01 upgrade - `sec.boot_version=GEC02 ` - `sec.boot_seeds=0:0:1` - `sec.black_plug_mcode=GEC02JAA` - "D01 IO pin" on IO board not active - Booting as dedicated - `sec.boot_version=GEC02 ` - `sec.boot_seeds=0:1:1` - `sec.black_plug_mcode=GQD01JAA` - "D01 IO pin" on IO board ACTIVE All the above was derived from reading and understanding the documented code excerpts below. ## io_boot - sub_402700 Called as the first step in the boot statemachine by the "boot" function `sub_40F9C0`. ```c // sub_402700 int __stdcall io_boot(int a1) { DWORD (__stdcall *timeGetTime)(); // esi int usb_boot_security_res; // ebp DWORD time_start; // ebx int io_boot_state; // edi DWORD now; // esi BOOL is_d01_boot_mode; // esi BOOL v7; // esi int result; // eax int v9; // [esp+10h] [ebp-2Ch] int firmware_version; // [esp+14h] [ebp-28h] int io_pad_read; // [esp+18h] [ebp-24h] BYREF char v12[32]; // [esp+1Ch] [ebp-20h] BYREF dword_4D0768 = 0; strcpy(byte_4D076C, &byte_4D0858); timeGetTime = ::timeGetTime; v9 = 0; usb_boot_security_res = io_pad_read; time_start = ::timeGetTime(); io_boot_state = 0; while ( 2 ) { switch ( io_boot_state ) { case 0: // "start io", firmware download and wait for reconnect if ( usbStart(0) ) { if ( time_exceeded(time_start, 20000) ) set_io_error_type(1, aErrorUsbioStar);// ERROR(USBIO START) } else { io_boot_state = 1; } goto reset_io_boot_state_label; case 1: // check firmware firmware_version = usbFirmResult(); if ( firmware_version == 96 ) { if ( time_exceeded(time_start, 25000) ) set_io_error_type(1, aErrorFm); // ERROR(FM), FM = firmware } else { io_boot_state = 2; } goto reset_io_boot_state_label; case 2: if ( !firmware_version ) { usbMute(1); time_start = timeGetTime(); now = timeGetTime(); io_input_is_d01_ezusb = 0; do { usbPadRead(&io_pad_read); if ( (io_pad_read & 0x10) != 0 ) io_input_is_d01_ezusb = 1; } while ( !time_exceeded(now, 2000) ); io_boot_state = 3; reset_io_boot_state_label: timeGetTime = ::timeGetTime; if ( io_boot_state != v9 ) { v9 = io_boot_state; time_start = ::timeGetTime(); } continue; // retry } if ( firmware_version > 128 ) { if ( firmware_version == 254 ) { set_io_error_type(1, aErrorFmTrnsOut);// ERROR(FM TRNS-OUT) return 1; } if ( firmware_version == 255 ) { set_io_error_type(1, aErrorFmTimeOut);// ERROR(FM TIME-OUT) return 1; } } else { switch ( firmware_version ) { case 128: set_io_error_type(1, aErrorFmReadErr);// ERROR(FM READ-ERR) return 1; case -113: set_io_error_type(1, aErrorFmDlErr);// ERROR(FM DL-ERR) return 1; case -33: set_io_error_type(1, aErrorFmCmprReq);// ERROR(FM CMPR-REQ) return 1; } } UNKNOWN_ERROR_LABEL: set_io_error_type(1, aErrorUnknown); // ERROR(UNKNOWN) return 1; case 3: if ( io_init_security() ) { set_io_error_type(1, aErrorSqInit); // ERROR(SQ-INIT) return 1; } io_boot_state = 4; goto reset_io_boot_state_label; case 4: is_d01_boot_mode = io_input_is_d01_ezusb != 0; if ( !strncmp(black_dongle_mcode, MCODE_GED01, 5u) ) is_d01_boot_mode = 1; usb_boot_security_res = usbBootSecurity(aGec02, 0, is_d01_boot_mode, 1); if ( usb_boot_security_res == 96 ) { if ( time_exceeded(time_start, 20000) ) set_io_error_type(1, aErrorBtSqInit);// ERROR(BT-SQ-INIT)' } else { io_boot_state = 5; } goto reset_io_boot_state_label; case 5: if ( usb_boot_security_res ) { switch ( usb_boot_security_res ) { case 0xFFFFFE9F: set_io_error_type(1, aErrorSecurityE);// ERROR(SECURITY EEP) result = 1; break; case 0xFFFFFECF: case 0xFFFFFEDF: set_io_error_type(1, aErrorSecurityI);// ERROR(SECURITY ID or EEP) result = 1; break; case 0xFFFFFEEF: set_io_error_type(1, aErrorSecurityN);// 'ERROR(SECURITY No Match)' result = 1; break; case 0xFFFFFEFF: io_boot_state = 6; goto reset_io_boot_state_label; default: goto UNKNOWN_ERROR_LABEL; } } else { Sleep(0x3E8u); result = 0; } return result; case 6: // security conversion/upgrading security dongles v7 = io_input_is_d01_ezusb != 0; if ( !usbGetSecurityKey(v12) ) { if ( usbSetupSecurityComplete(v12, 0, v7, 1) ) set_io_error_type(1, aErrorSecurityC);// ERROR(SECURITY CONVERSION FAILED) return 1; } set_io_error_type(1, aErrorSecurityC); // ERROR(SECURITY CONVERSION FAILED) goto reset_io_boot_state_label; default: goto UNKNOWN_ERROR_LABEL; } } } ``` ## io_init_security - sub_402BB0 Sub-function of the [io_boot](#io_boot---sub_402700) function. ```c // sub_402BB0 int io_init_security() { int v0; // esi int v2; // esi int mcode_mismatch_cnt; // esi char white_dongle_data_maybe[10]; // [esp+Ch] [ebp-20h] BYREF usbSecurityInit(); v0 = 0; while ( usbSecurityInitDone() ) { if ( v0 > 1200 ) { set_io_error_type(-1, aNgSi); return -1; } ++v0; Sleep(16u); } pcbid = 0; dword_4D0754 = 0; word_4D0758 = 0; while ( usbGetPCBID(&pcbid) ) Sleep(1u); *(_DWORD *)black_dongle_mcode = 0; v2 = 0; *(_DWORD *)&black_dongle_mcode[4] = 0; while ( usbGetSecurity(black_dongle_mcode) ) // black_dongle_mcode = GQD01JAA { if ( v2 > 1200 ) { set_io_error_type(-1, aNgPd); return -1; } ++v2; Sleep(16u); } set_cabinet_type(black_dongle_mcode[6]); mcode_mismatch_cnt = black_dongle_mcode[0] != 'G'; if ( io_input_is_d01_ezusb || !strncmp(black_dongle_mcode, MCODE_GED01, 5u) )// Path for using the D01 dongle with D01 IO board { if ( (unsigned __int8)black_dongle_mcode[2] != (char)MCODE_D01 ) ++mcode_mismatch_cnt; if ( (unsigned __int8)black_dongle_mcode[3] != SBYTE1(MCODE_D01) ) ++mcode_mismatch_cnt; if ( (unsigned __int8)black_dongle_mcode[4] != SBYTE2(MCODE_D01) ) ++mcode_mismatch_cnt; } else // Path for using the C02 dongle with C02 IO board { if ( (unsigned __int8)black_dongle_mcode[2] != MCODE_C02[0] ) ++mcode_mismatch_cnt; if ( (unsigned __int8)black_dongle_mcode[3] != MCODE_C02[1] ) ++mcode_mismatch_cnt; if ( (unsigned __int8)black_dongle_mcode[4] != MCODE_C02[2] ) ++mcode_mismatch_cnt; if ( !usbGetSecurityKey(white_dongle_data_maybe) ) { black_dongle_mcode[6] = white_dongle_data_maybe[6]; set_cabinet_type(white_dongle_data_maybe[6]); } } if ( black_dongle_mcode[5] != 'J' ) ++mcode_mismatch_cnt; if ( black_dongle_mcode[6] != 'A' && black_dongle_mcode[6] != 'B' && black_dongle_mcode[6] != 'C' ) ++mcode_mismatch_cnt; if ( !mcode_mismatch_cnt ) return 0; set_io_error_type(-1, aNgSecurity); return -1; } ```