From 6b33d4edd1a7f55e2db63f81a90ad6a78174ac27 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Thu, 15 Jun 2023 09:13:55 +0200 Subject: [PATCH] Add cartdb files, implement GX706 checksum workaround --- CMakePresets.json | 0 cmake/setup.cmake | 0 data/x76f041.cartdb | Bin 0 -> 9856 bytes data/zs01.cartdb | Bin 0 -> 16896 bytes src/cart.hpp | 4 +++- src/cartdata.cpp | 45 +++++++++++++++++++++++++++++++------------ src/cartdata.hpp | 2 +- src/main.cpp | 12 ++++++------ tools/_common.py | 6 +++++- tools/buildCartDB.py | 13 ++++++++++--- 10 files changed, 58 insertions(+), 24 deletions(-) mode change 100755 => 100644 CMakePresets.json mode change 100755 => 100644 cmake/setup.cmake create mode 100644 data/x76f041.cartdb create mode 100644 data/zs01.cartdb diff --git a/CMakePresets.json b/CMakePresets.json old mode 100755 new mode 100644 diff --git a/cmake/setup.cmake b/cmake/setup.cmake old mode 100755 new mode 100644 diff --git a/data/x76f041.cartdb b/data/x76f041.cartdb new file mode 100644 index 0000000000000000000000000000000000000000..30833800a3f3a10b51ea981920bc7eff827bcceb GIT binary patch literal 9856 zcmd6tUu@e%9LJLk?Tt~Wc<3~t6GECmY;w}FCP5l=64!0hhAwHU!Zxv6cTKD$cIH29 z1&wJNNSlxl0wzr$cmXj4r4LAzP&Ot6h;9?xD2TzsCLT8gNc^dg5D2*Q<*1w3N%L=B z9~|4Y@8j?9e)qed?`Q8CkGIRybMnJ83zsgS7ca_K#<$0 z0X_;+MKmSBeABq98=d%q%Y0^kt9}vY^GKbk($LBYC;f4k^`~zZ-{;xZg$;Q9aq#;; z>fr;}|N0)n$HNio|1l^}AwaiD^rVtEbV-rJU`W=HDkB{vp`4TC446Rb<|aDA>+=l| z{*I+1S4Xd{<1jziNBCv^giM-G=`e%9G=jRJlH>7YS^#k=l>#v#H7+Iiap}=SbNnX= z|Fxe^{_(8bt%=bPNA$DtXRjZml)M509LF_*TyYE5Z_BspueN_%`4dF{*5v6owbQfj zWBq<2%F4I1e@sQNpn<4TKww-o&_ z`gvKx3Zq$AC^n#K^_O~ml)v`=*~Mfic@^{hWJ+Q7Pr*D?XTT_w=|g}Wflb9coI&cY z)kA7rN#h@$|4Qf1Um)|J(7(kT|K;%)ff}B8 zn2iodS{A7VDC7Ar0wo;;!hpwn_^EKG;Q2t#H0^r?fbDO_zo$#ueD@t6AH)29+W(X< z!l^9Gz>awU+ULjyRfwOJy8ht z5sJM=(^SJ^jwSrh5E^s`4M3`M<26PsA$aYsMZ;CzJCELx2DSR{U1!kGrG) z;%{#a{`J5ypO~QeXYKJo!@#oZ92$l3{wh957xzLPzY5Uja%!*9 z^G^J@%l2pacjxU--~XR^_`cisef7aX;{Uzah%Ek#hNMH4`aYnuUZWap$ESX7!zW8Uz`vOzVXEanC;PMYHyeJ-Olt#9d^Z1fo=@{%zy9LR=@Yw; z692cZ|62Lh=Wf?N%Rj35^!>{{zK#F2)A7m%bkc7({@Z19Ydg+-ckM5f=U;38w{_sx zvAuhJZKA?<}9?pKkdd zi@%-c)A%#{%{^E8_Z>>(@gFe1e~8pv|BpizDmesNUJS+kZ`R))nfoMs_v{-3nBPn5 zk954GbX4qE@dYE8|Gl%tcjQ{W>o##a&xP=LmVb;bkAG5nYI_85VK2{+{*QLHHTHJ8UiCIgXF#Sn{4Qc|M{mDE?=C- z^}op=N7kxY{Hy)_O+zT#i<$lZ%szkg#>J&>%ny?OFPA?(mG#Fko(t5xWL9HauRbpO zUnO6l-R~NI*kykDrt#_e>kob3Z+mL-)W70nh$HoP7XJrlig>jnf~(LHsXF7W`7J=( H%JBaIYxuyl literal 0 HcmV?d00001 diff --git a/data/zs01.cartdb b/data/zs01.cartdb new file mode 100644 index 0000000000000000000000000000000000000000..ac760218b9fb27a7042b48e0f6cd01b3baad1825 GIT binary patch literal 16896 zcmd6vdu&rx7{J@j2Qd=S%s3;77bFsin_c%t)O2lkL(6cqtIkIv*X;rgTRZRC2{8u5 zS7Ox2&^Sd+7$#;a5=0kCG*PEuMuHD~WGpCdaSB2VQKJ87yr=hkxA*qk)2;2f=dwTA zExXvg$YA)ilub-4mkqDvOE*N8lj>`ABM z$%Ig=bhoYS6xvtCqM65veksTNnJp)(#mU3x5WiOaYd*|xmzAz`f4|CXRb**nKPDH= z%l#mYugkBbd)uT$Tw)Ez1EKN5oaMi1`8^E>mmGN)>Azkb8O{JWjyga11vf5>6|hxqE>J+b}j z!DlN@DaijDRlfxNQ}QpfNC5xe_0Fd8MbEstT;R)Yc{KXUNg=k7zFAJ@*lu9e;=RmwaA#YyVFGQpPIb+G;8NR@tX+! zXF8Bgf;W-rx}?57IngbI+I;MNdL|1Z_M#;;ung@`#hDONB$DTCB}mJ?uA?5 zZ<=|)BZ>YN59Q{v-5H=0DDSBLCUTf3Nk@ z-TNx1s*(R2`(IQ3X|yf>lo5QZ{KJ!Pm4EExyAR%bq~qGybPVa=)PDvdZNARsxVm&EKmBnlvcGZt!&HBo?5jT$?(pJWKg{gZ`FQ=q zg3r_i!T$*Pt?HkR@)LZk`e!5mh7IYCimSFasrt|C{{;>EkJt06 zf7?gn)BmkFd%4Q5HO{{%>)*l7j!3Yrq?I>q{Fq<9?Z&~W>D^DO^FM!P{f}Dz7~6ox zC-XmJFg72R59^P5{?E|J4}hzoz_O<6&L^ApaD?XK4dEU(>&1@>|tE zp8C(j=c0e?|D5!HfwywU$mcz;pH%%n)BiyIH}h-k^8bSPHt3(^TiTza{CW61PJZxyYYK|G0Hv@`m#-;eW9HWu<>Rkqv+Ho5&9G1y## zR@bQUubn}|+AIAH+n?Zve7XHUW3bu&bLI6bVe(L!X z#%cimw=*L@FBp|4Rr}Xz>t7cAzjo`xF#6KJ1^#bS%Rc_>;%Hn9`n<;Tr>6SH?9^>` z+`!k*|ET=D=ieBQfy$@tKl0+!_8&R%$^6e4j9q}He{%ngGoRRhvzM>mzft|)c>c*0 ze~iz*ArpZ93BJ|-7f-&`{uf6+_`g&72mg1B&sqOxhqjD`{(5sltv_|B{=^LXj~4eI zV^QsRI?f`+zhVD(Ki{x&|KXV*hs0n@op%08tG{iMl8Q?`LW_Fypm+-XlT<$LKRNMT zr@e1IHGKNMjbgZ|5$PZLA8r3tyZ=X*qWIrH{?Y4y*qQr}y8nfp@xQG6j+}ok)A#?w zj_m(u<8QUY@dtdT;%|)>e=z^k;;-OqJ#!97h(A64F(2w5C%*f{z@ry-Z@v9%ag8st z{sr^THmUdiq~H_8WR;klvs80`5b`gvalidateChecksum(flags & DATA_CHECKSUM_INVERTED) - ); + if (!Parser::validate()) + return false; + + return _getHeader()->validateChecksum(flags & DATA_CHECKSUM_INVERTED); } size_t ExtendedParser::getCode(char *output) const { @@ -78,6 +78,9 @@ size_t ExtendedParser::getCode(char *output) const { __builtin_memcpy(output, header->code, sizeof(header->code)); output[sizeof(header->code)] = 0; + if (flags & DATA_GX706_WORKAROUND) + output[1] = 'X'; + return __builtin_strlen(output); } @@ -113,10 +116,21 @@ void ExtendedParser::flush(void) { } bool ExtendedParser::validate(void) { - return ( - Parser::validate() - && _getHeader()->validateChecksum(flags & DATA_CHECKSUM_INVERTED) - ); + if (!Parser::validate()) + return false; + + auto header = _getHeader(); + char region = header->region[1]; + + if (flags & DATA_GX706_WORKAROUND) + header->region[1] = 'X'; + + bool valid = header->validateChecksum(flags & DATA_CHECKSUM_INVERTED); + + if (flags & DATA_GX706_WORKAROUND) + header->region[1] = region; + + return valid; } /* Data format identification */ @@ -128,7 +142,7 @@ public: uint8_t flags; }; -static constexpr int _NUM_KNOWN_FORMATS = 9; +static constexpr int _NUM_KNOWN_FORMATS = 12; static const KnownFormat _KNOWN_FORMATS[_NUM_KNOWN_FORMATS]{ { @@ -144,6 +158,10 @@ static const KnownFormat _KNOWN_FORMATS[_NUM_KNOWN_FORMATS]{ .name = "basic + TID", .format = BASIC, .flags = DATA_HAS_TRACE_ID | DATA_CHECKSUM_INVERTED + }, { + .name = "basic + SID", + .format = BASIC, + .flags = DATA_HAS_CART_ID | DATA_CHECKSUM_INVERTED }, { .name = "basic + TID, SID", .format = BASIC, @@ -160,15 +178,18 @@ static const KnownFormat _KNOWN_FORMATS[_NUM_KNOWN_FORMATS]{ .flags = DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID | DATA_HAS_INSTALL_ID | DATA_HAS_SYSTEM_ID | DATA_CHECKSUM_INVERTED }, { - // Used by early (pre-digital-I/O) Bemani games .name = "extended (no IDs)", .format = EXTENDED, .flags = DATA_HAS_CODE_PREFIX | DATA_CHECKSUM_INVERTED }, { - // Used by early (pre-digital-I/O) Bemani games - .name = "extended alt. (no IDs)", + .name = "extended (no IDs, alt)", .format = EXTENDED, .flags = DATA_HAS_CODE_PREFIX + }, { + // Used by GX706 + .name = "extended (no IDs, GX706)", + .format = EXTENDED, + .flags = DATA_HAS_CODE_PREFIX | DATA_GX706_WORKAROUND }, { // Used by GE936/GK936 and all ZS01 Bemani games .name = "extended + all IDs", diff --git a/src/cartdata.hpp b/src/cartdata.hpp index 282ca73..a0fe35e 100644 --- a/src/cartdata.hpp +++ b/src/cartdata.hpp @@ -101,7 +101,7 @@ public: uint8_t traceIDParam, installIDPrefix; uint16_t year; uint8_t dataKey[8]; - char code[8], region[8], name[64]; + char code[8], region[8], name[96]; inline int compare(const char *_code, const char *_region) const { int diff = __builtin_strncmp(code, _code, CODE_LENGTH + 1); diff --git a/src/main.cpp b/src/main.cpp index 8eacd99..907e43d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,14 +15,14 @@ extern "C" const uint8_t _resources[]; extern "C" const size_t _resourcesSize; -static const char _DEFAULT_RESOURCE_ZIP_PATH[] = "/cartToolResources.zip"; +static const char _DEFAULT_RESOURCE_ZIP_PATH[] = "cartToolResources.zip"; static const char *const _UI_SOUND_PATHS[ui::NUM_UI_SOUNDS]{ - "assets/sound/startup.vag", // ui::SOUND_STARTUP - "assets/sounds/move.vag", // ui::SOUND_MOVE - "assets/sounds/enter.vag", // ui::SOUND_ENTER - "assets/sounds/exit.vag", // ui::SOUND_EXIT - "assets/sounds/click.vag" // ui::SOUND_CLICK + "assets/sounds/startup.vag", // ui::SOUND_STARTUP + "assets/sounds/move.vag", // ui::SOUND_MOVE + "assets/sounds/enter.vag", // ui::SOUND_ENTER + "assets/sounds/exit.vag", // ui::SOUND_EXIT + "assets/sounds/click.vag" // ui::SOUND_CLICK }; int main(int argc, const char **argv) { diff --git a/tools/_common.py b/tools/_common.py index 8281fe2..da1c75b 100644 --- a/tools/_common.py +++ b/tools/_common.py @@ -47,6 +47,7 @@ class DataFlag(IntFlag): DATA_HAS_SYSTEM_ID = 1 << 4 DATA_HAS_PUBLIC_SECTION = 1 << 5 DATA_CHECKSUM_INVERTED = 1 << 6 + DATA_GX706_WORKAROUND = 1 << 7 # Character 0: always G # Character 1: region related? (can be B, C, E, K, L, N, Q, U, X) @@ -242,6 +243,9 @@ class ExtendedParser(Parser): data: bytes = _getPublicData(dump, flags, _EXTENDED_HEADER_STRUCT.size) ids: IdentifierSet = IdentifierSet(dump.data[_EXTENDED_HEADER_STRUCT.size + 16:]) + if flags & DataFlag.DATA_GX706_WORKAROUND: + data = data[0:1] + b"X" + data[2:] + code, year, region, checksum = _EXTENDED_HEADER_STRUCT.unpack(data) code: bytes = code.rstrip(b"\0") @@ -267,7 +271,7 @@ class ExtendedParser(Parser): ## Cartridge database -DB_ENTRY_STRUCT: Struct = Struct("< 6B H 8s 8s 8s 64s") +DB_ENTRY_STRUCT: Struct = Struct("< 6B H 8s 8s 8s 96s") @dataclass class GameEntry: diff --git a/tools/buildCartDB.py b/tools/buildCartDB.py index b7f57d8..369153c 100755 --- a/tools/buildCartDB.py +++ b/tools/buildCartDB.py @@ -129,6 +129,10 @@ _KNOWN_FORMATS: Sequence[tuple[str, Type, DataFlag]] = ( "basic + TID", BasicParser, DataFlag.DATA_HAS_TRACE_ID | DataFlag.DATA_CHECKSUM_INVERTED + ), ( + "basic + SID", + BasicParser, + DataFlag.DATA_HAS_CART_ID | DataFlag.DATA_CHECKSUM_INVERTED ), ( "basic + TID, SID", BasicParser, @@ -147,15 +151,18 @@ _KNOWN_FORMATS: Sequence[tuple[str, Type, DataFlag]] = ( | DataFlag.DATA_HAS_CART_ID | DataFlag.DATA_HAS_INSTALL_ID | DataFlag.DATA_HAS_SYSTEM_ID | DataFlag.DATA_CHECKSUM_INVERTED ), ( - # Used by early (pre-digital-I/O) Bemani games "extended (no IDs)", ExtendedParser, DataFlag.DATA_HAS_CODE_PREFIX | DataFlag.DATA_CHECKSUM_INVERTED ), ( - # Used by early (pre-digital-I/O) Bemani games - "extended alt. (no IDs)", + "extended (no IDs, alt)", ExtendedParser, DataFlag.DATA_HAS_CODE_PREFIX + ), ( + # Used by GX706 + "extended (no IDs, GX706)", + ExtendedParser, + DataFlag.DATA_HAS_CODE_PREFIX | DataFlag.DATA_GX706_WORKAROUND ), ( # Used by GE936/GK936 and all ZS01 Bemani games "extended + all IDs",