mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2024-11-12 01:10:49 +01:00
chore(doc): Add dev journal about 10th style IO and security boot
This commit is contained in:
parent
5c4afb8a2a
commit
47bfb4e9b2
289
doc/dev/journal/2023-04-03-d01-ezusb-io-boot-security.md
Normal file
289
doc/dev/journal/2023-04-03-d01-ezusb-io-boot-security.md
Normal file
@ -0,0 +1,289 @@
|
||||
# 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;
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue
Block a user