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 0000000..3083380 Binary files /dev/null and b/data/x76f041.cartdb differ diff --git a/data/zs01.cartdb b/data/zs01.cartdb new file mode 100644 index 0000000..ac76021 Binary files /dev/null and b/data/zs01.cartdb differ diff --git a/src/cart.hpp b/src/cart.hpp index f96d9a6..c793c2e 100644 --- a/src/cart.hpp +++ b/src/cart.hpp @@ -46,6 +46,7 @@ enum DumpFlag : uint8_t { // | DATA_HAS_CODE_PREFIX | | Optional | Mandatory | // | DATA_HAS_*_ID | | Optional | Optional | // | DATA_HAS_PUBLIC_SECTION | Mandatory | | Optional | +// | DATA_GX706_WORKAROUND | | | Optional | enum DataFlag : uint8_t { DATA_HAS_CODE_PREFIX = 1 << 0, @@ -54,7 +55,8 @@ enum DataFlag : uint8_t { DATA_HAS_INSTALL_ID = 1 << 3, DATA_HAS_SYSTEM_ID = 1 << 4, DATA_HAS_PUBLIC_SECTION = 1 << 5, - DATA_CHECKSUM_INVERTED = 1 << 6 + DATA_CHECKSUM_INVERTED = 1 << 6, + DATA_GX706_WORKAROUND = 1 << 7 }; static constexpr int NUM_CHIP_TYPES = 4; diff --git a/src/cartdata.cpp b/src/cartdata.cpp index 25ee502..1239b6f 100644 --- a/src/cartdata.cpp +++ b/src/cartdata.cpp @@ -66,10 +66,10 @@ IdentifierSet *BasicParser::getIdentifiers(void) { } bool BasicParser::validate(void) { - return ( - Parser::validate() - && _getHeader()->validateChecksum(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",