From 9788a3fbccc71bfae29cb38893134d0f3ceac956 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Sat, 3 Jun 2023 08:25:41 +0200 Subject: [PATCH] More refactoring, remove layer queue from GPU driver --- CMakeLists.txt | 13 +- src/app/app.cpp | 60 +++--- src/app/app.hpp | 22 ++- src/cart.cpp | 164 ++++++++++++++++ src/cart.hpp | 175 +++++++++++++++++ src/cartdata.cpp | 372 ++++++++++++++++--------------------- src/cartdata.hpp | 114 +++--------- src/cartio.cpp | 138 ++++---------- src/cartio.hpp | 180 +++++------------- src/gpu.cpp | 62 +------ src/gpu.hpp | 13 +- tools/convertExecutable.py | 49 +++-- 12 files changed, 684 insertions(+), 678 deletions(-) create mode 100644 src/cart.cpp create mode 100644 src/cart.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9664736..3d80a78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/cmake/toolchain.cmake") project( cart_tool_private LANGUAGES C CXX ASM - VERSION 0.3.0 + VERSION 0.3.1 DESCRIPTION "Konami System 573 security cartridge tool" ) @@ -17,6 +17,7 @@ find_package(Python3 REQUIRED COMPONENTS Interpreter) add_executable( cart_tool src/asset.cpp + src/cart.cpp src/cartdata.cpp src/cartio.cpp src/gpu.cpp @@ -67,21 +68,26 @@ target_compile_definitions( add_custom_command( TARGET cart_tool POST_BUILD COMMAND - "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/tools/convertExecutable.py" + "${Python3_EXECUTABLE}" + "${PROJECT_SOURCE_DIR}/tools/convertExecutable.py" + -r "cart_tool build ${PROJECT_VERSION} - (C) 2022-2023 spicyjpeg" $ cart_tool.psexe BYPRODUCTS cart_tool.psexe COMMENT "Converting executable" + VERBATIM ) ## Default resource archive add_custom_command( COMMAND - "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/tools/buildResourceArchive.py" + "${Python3_EXECUTABLE}" + "${PROJECT_SOURCE_DIR}/tools/buildResourceArchive.py" "${PROJECT_SOURCE_DIR}/resources.json" resources.zip OUTPUT resources.zip DEPENDS resources.json COMMENT "Building resource archive" + VERBATIM ) ps1_target_incbin( cart_tool PRIVATE @@ -99,6 +105,7 @@ ps1_target_incbin( # OUTPUT cart_tool.iso # DEPENDS cd.json cart_tool # COMMENT "Building CD-ROM image" +# VERBATIM #) # Add a dummy target that depends on the CD image to make sure it gets built. diff --git a/src/app/app.cpp b/src/app/app.cpp index 89f94dc..0a6e75d 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -4,6 +4,7 @@ #include "app/app.hpp" #include "ps1/system.h" #include "asset.hpp" +#include "cart.hpp" #include "cartdata.hpp" #include "cartio.hpp" #include "io.hpp" @@ -12,17 +13,18 @@ /* App class */ -App::App(void) -: _cart(nullptr), _cartData(nullptr), _identified(nullptr) { - _workerStack = new uint8_t[WORKER_STACK_SIZE]; -} +void App::_unloadCartData(void) { + if (_driver) { + delete _driver; + _driver = nullptr; + } + if (_parser) { + delete _parser; + _parser = nullptr; + } -App::~App(void) { - //_dbFile.unload(); - delete[] _workerStack; - - if (_cart) - delete _cart; + _db.unload(); + _identified = nullptr; } void App::_setupWorker(void (App::* func)(void)) { @@ -41,7 +43,7 @@ void App::_setupInterrupts(void) { util::forcedCast(&App::_interruptHandler), this ); - setInterruptMask((1 << IRQ_VBLANK) | (1 << IRQ_GPU)); + setInterruptMask(1 << IRQ_VBLANK); } void App::_dummyWorker(void) { @@ -59,27 +61,20 @@ static const char *const _CARTDB_PATHS[cart::NUM_CHIP_TYPES]{ void App::_cartDetectWorker(void) { _workerStatus.update(0, 4, WSTR("App.cartDetectWorker.identifyCart")); - _db.unload(); - if (_cart) - delete _cart; - if (_cartData) - delete _cartData; - - _cart = cart::createCart(_dump); - _cartData = nullptr; - _identified = nullptr; + _unloadCartData(); + _driver = cart::newCartDriver(_dump); if (_dump.chipType) { - LOG("dump @ 0x%08x, cart object @ 0x%08x", &_dump, _cart); + LOG("dump @ 0x%08x, cart driver @ 0x%08x", &_dump, _driver); _workerStatus.update(1, 4, WSTR("App.cartDetectWorker.readCart")); - _cart->readCartID(); - if (!_cart->readPublicData()) - _cartData = cart::createCartData(_dump); - if (!_cartData) + _driver->readCartID(); + if (!_driver->readPublicData()) + _parser = cart::newCartParser(_dump); + if (!_parser) goto _cartInitDone; - LOG("cart data object @ 0x%08x", _cartData); + LOG("cart parser @ 0x%08x", _parser); _workerStatus.update(2, 4, WSTR("App.cartDetectWorker.identifyGame")); if (!_loader->loadAsset(_db, _CARTDB_PATHS[_dump.chipType])) { @@ -115,7 +110,7 @@ _cartInitDone: delayMicroseconds(5000); // Probably not necessary io::initKonamiBitstream(); - _cart->readSystemID(); + _driver->readSystemID(); } _initDone: @@ -126,12 +121,12 @@ _initDone: void App::_cartUnlockWorker(void) { _workerStatus.update(0, 2, WSTR("App.cartUnlockWorker.read")); - //_cart->readPrivateData(); // TODO: implement this + //_driver->readPrivateData(); // TODO: implement this _workerStatus.update(1, 2, WSTR("App.cartUnlockWorker.identify")); //if (_dump.flags & cart::DUMP_PRIVATE_DATA_OK) - //_identifyResult = _db.identifyCart(*_cart); + //_identifyResult = _db.identifyCart(*_driver); _workerStatus.finish(_cartInfoScreen, true); _dummyWorker(); @@ -154,11 +149,10 @@ void App::_interruptHandler(void) { if (acknowledgeInterrupt(IRQ_VBLANK)) { _ctx->tick(); io::clearWatchdog(); - switchThread(nullptr); - } - if (acknowledgeInterrupt(IRQ_GPU)) - _ctx->gpuCtx.drawNextLayer(); + if (gpu::isIdle()) + switchThread(nullptr); + } } void App::run( diff --git a/src/app/app.hpp b/src/app/app.hpp index 5accfb6..770f181 100644 --- a/src/app/app.hpp +++ b/src/app/app.hpp @@ -7,6 +7,7 @@ #include "app/unlock.hpp" #include "ps1/system.h" #include "asset.hpp" +#include "cart.hpp" #include "cartdata.hpp" #include "cartio.hpp" #include "uibase.hpp" @@ -90,11 +91,12 @@ private: Thread _workerThread; WorkerStatus _workerStatus; - uint8_t *_workerStack; - cart::Cart *_cart; - cart::CartData *_cartData; - cart::DBEntry *_identified; + uint8_t *_workerStack; + cart::Driver *_driver; + cart::Parser *_parser; + cart::DBEntry *_identified; + void _unloadCartData(void); void _setupWorker(void (App::* func)(void)); void _setupInterrupts(void); @@ -106,8 +108,16 @@ private: void _interruptHandler(void); public: - App(void); - ~App(void); + inline App(void) + : _driver(nullptr), _parser(nullptr), _identified(nullptr) { + _workerStack = new uint8_t[WORKER_STACK_SIZE]; + } + inline ~App(void) { + _unloadCartData(); + + delete[] _workerStack; + } + void run( ui::Context &ctx, asset::AssetLoader &loader, asset::StringTable &strings diff --git a/src/cart.cpp b/src/cart.cpp new file mode 100644 index 0000000..c6b5002 --- /dev/null +++ b/src/cart.cpp @@ -0,0 +1,164 @@ + +#include +#include +#include "vendor/miniz.h" +#include "cart.hpp" +#include "util.hpp" + +namespace cart { + +/* Common data structures */ + +void Identifier::updateChecksum(void) { + data[7] = (util::sum(data, 7) & 0xff) ^ 0xff; +} + +bool Identifier::validateChecksum(void) const { + uint8_t value = (util::sum(data, 7) & 0xff) ^ 0xff; + + if (value != data[7]) { + LOG("checksum mismatch, exp=0x%02x, got=0x%02x", value, data[7]); + return false; + } + + return true; +} + +void Identifier::updateDSCRC(void) { + data[7] = util::dsCRC8(data, 7); +} + +bool Identifier::validateDSCRC(void) const { + uint8_t value = util::dsCRC8(data, 7); + + if (value != data[7]) { + LOG("CRC mismatch, exp=0x%02x, got=0x%02x", value, data[7]); + return false; + } + + return true; +} + +uint8_t IdentifierSet::getFlags(void) const { + uint8_t flags = 0; + + if (!traceID.isEmpty()) + flags |= DATA_HAS_TRACE_ID; + if (!cartID.isEmpty()) + flags |= DATA_HAS_CART_ID; + if (!installID.isEmpty()) + flags |= DATA_HAS_INSTALL_ID; + if (!systemID.isEmpty()) + flags |= DATA_HAS_SYSTEM_ID; + + return flags; +} + +void IdentifierSet::setInstallID(uint8_t prefix) { + installID.clear(); + + installID.data[0] = prefix; + installID.updateChecksum(); +} + +void IdentifierSet::updateTraceID(uint8_t prefix, int param) { + traceID.clear(); + + uint8_t *input = &cartID.data[1]; + uint16_t checksum = 0; + + switch (prefix) { + case 0x81: + // TODO: reverse engineer this TID format + traceID.data[2] = 0; + traceID.data[5] = 0; + traceID.data[6] = 0; + + LOG("prefix=0x81"); + break; + + case 0x82: + for (size_t i = 0; i < (sizeof(cartID.data) - 2); i++) { + uint8_t value = *(input++); + + for (int j = 0; j < 8; j++, value >>= 1) { + if (value % 2) + checksum ^= 1 << (i % param); + } + } + + traceID.data[1] = checksum >> 8; + traceID.data[2] = checksum & 0xff; + + LOG("prefix=0x82, checksum=%04x", checksum); + break; + + default: + LOG("unknown prefix 0x%02x", prefix); + } + + traceID.data[0] = prefix; + traceID.updateChecksum(); +} + +void BasicHeader::updateChecksum(void) { + auto value = util::sum(reinterpret_cast(this), 4); + + checksum = uint8_t((value & 0xff) ^ 0xff); +} + +bool BasicHeader::validateChecksum(void) const { + auto value = util::sum(reinterpret_cast(this), 4); + + return (checksum == ((value & 0xff) ^ 0xff)); +} + +void ExtendedHeader::updateChecksum(void) { + auto value = util::sum(reinterpret_cast(this), 14); + + checksum = uint16_t(value & 0xffff); +} + +bool ExtendedHeader::validateChecksum(void) const { + auto value = util::sum(reinterpret_cast(this), 14); + + return (checksum == (value & 0xffff)); +} + +/* Dump structure and utilities */ + +const ChipSize CHIP_SIZES[NUM_CHIP_TYPES]{ + { .dataLength = 0, .publicDataOffset = 0, .publicDataLength = 0 }, + { .dataLength = 512, .publicDataOffset = 384, .publicDataLength = 128 }, + { .dataLength = 112, .publicDataOffset = 0, .publicDataLength = 0 }, + { .dataLength = 112, .publicDataOffset = 0, .publicDataLength = 32 } +}; + +size_t Dump::toQRString(char *output) const { + uint8_t compressed[MAX_QR_STRING_LENGTH]; + size_t uncompLength = getDumpLength(); + size_t compLength = MAX_QR_STRING_LENGTH; + + int error = mz_compress2( + compressed, reinterpret_cast(&compLength), + reinterpret_cast(this), uncompLength, + MZ_BEST_COMPRESSION + ); + + if (error != MZ_OK) { + LOG("compression error, code=%d", error); + return 0; + } + LOG( + "dump compressed, size=%d, ratio=%d%%", compLength, + compLength * 100 / uncompLength + ); + + compLength = util::encodeBase45(&output[5], compressed, compLength); + __builtin_memcpy(&output[0], "573::", 5); + __builtin_memcpy(&output[compLength + 5], "::", 3); + + return compLength + 7; +} + +} diff --git a/src/cart.hpp b/src/cart.hpp new file mode 100644 index 0000000..ff53b8a --- /dev/null +++ b/src/cart.hpp @@ -0,0 +1,175 @@ + +#pragma once + +#include +#include +#include "util.hpp" + +namespace cart { + +/* Definitions */ + +enum ChipType : uint8_t { + NONE = 0, + X76F041 = 1, + X76F100 = 2, + ZS01 = 3 +}; + +enum FormatType : uint8_t { + BLANK = 0, + SIMPLE = 1, + BASIC = 2, + EXTENDED = 3 +}; + +enum DumpFlag : uint8_t { + DUMP_HAS_SYSTEM_ID = 1 << 0, + DUMP_HAS_CART_ID = 1 << 1, + DUMP_CONFIG_OK = 1 << 2, + DUMP_SYSTEM_ID_OK = 1 << 3, + DUMP_CART_ID_OK = 1 << 4, + DUMP_ZS_ID_OK = 1 << 5, + DUMP_PUBLIC_DATA_OK = 1 << 6, + DUMP_PRIVATE_DATA_OK = 1 << 7 +}; + +// | | Simple | Basic | Extended | +// | :---------------------- | :-------- | :------- | :-------- | +// | DATA_HAS_CODE_PREFIX | | Optional | Mandatory | +// | DATA_HAS_*_ID | | Optional | Optional | +// | DATA_HAS_PUBLIC_SECTION | Mandatory | | Optional | + +enum DataFlag : uint8_t { + DATA_HAS_CODE_PREFIX = 1 << 0, + DATA_HAS_TRACE_ID = 1 << 1, + DATA_HAS_CART_ID = 1 << 2, + DATA_HAS_INSTALL_ID = 1 << 3, + DATA_HAS_SYSTEM_ID = 1 << 4, + DATA_HAS_PUBLIC_SECTION = 1 << 5 +}; + +static constexpr int NUM_CHIP_TYPES = 4; +static constexpr size_t MAX_QR_STRING_LENGTH = 0x600; + +/* Common data structures */ + +class [[gnu::packed]] Identifier { +public: + uint8_t data[8]; + + inline void copyFrom(const uint8_t *source) { + __builtin_memcpy(data, source, sizeof(data)); + } + inline void copyTo(uint8_t *dest) const { + __builtin_memcpy(dest, data, sizeof(data)); + } + inline void clear(void) { + __builtin_memset(data, 0, sizeof(data)); + } + inline bool isEmpty(void) const { + return (util::sum(data, sizeof(data)) == 0); + } + + inline size_t toString(char *output) const { + return util::hexToString(output, data, sizeof(data), '-'); + } + inline size_t toSerialNumber(char *output) const { + return util::serialNumberToString(output, &data[1]); + } + + void updateChecksum(void); + bool validateChecksum(void) const; + void updateDSCRC(void); + bool validateDSCRC(void) const; +}; + +class [[gnu::packed]] IdentifierSet { +public: + Identifier traceID, cartID, installID, systemID; // aka TID, SID, MID, XID + + inline void clear(void) { + __builtin_memset(this, 0, sizeof(IdentifierSet)); + } + + uint8_t getFlags(void) const; + void setInstallID(uint8_t prefix); + void updateTraceID(uint8_t prefix, int param); +}; + +class [[gnu::packed]] PublicIdentifierSet { +public: + Identifier traceID, cartID; // aka TID, SID + + inline void clear(void) { + __builtin_memset(this, 0, sizeof(PublicIdentifierSet)); + } +}; + +class [[gnu::packed]] SimpleHeader { +public: + char region[4]; +}; + +class [[gnu::packed]] BasicHeader { +public: + char region[2], codePrefix[2]; + uint8_t checksum, _pad[3]; + + void updateChecksum(void); + bool validateChecksum(void) const; +}; + +class [[gnu::packed]] ExtendedHeader { +public: + char code[8]; + uint16_t year; // BCD, can be little endian, big endian or zero + char region[4]; + uint16_t checksum; + + void updateChecksum(void); + bool validateChecksum(void) const; +}; + +/* Cartridge dump structure */ + +struct ChipSize { +public: + size_t dataLength, publicDataOffset, publicDataLength; +}; + +extern const ChipSize CHIP_SIZES[NUM_CHIP_TYPES]; + +class [[gnu::packed]] Dump { +public: + ChipType chipType; + uint8_t flags; + + Identifier systemID, cartID, zsID; + + uint8_t dataKey[8], config[8]; + uint8_t data[512]; + + inline const ChipSize &getChipSize(void) const { + return CHIP_SIZES[chipType]; + } + inline size_t getDumpLength(void) const { + return (sizeof(Dump) - sizeof(data)) + getChipSize().dataLength; + } + inline void clear(void) { + __builtin_memset(this, 0, sizeof(Dump)); + } + inline void clearData(void) { + __builtin_memset(data, 0, getChipSize().dataLength); + } + inline bool isDataEmpty(void) const { + size_t length = getChipSize().dataLength; + auto sum = util::sum(data, length); + + return (!sum || (sum == (0xff * length))); + } + + size_t toQRString(char *output) const; +}; + +} diff --git a/src/cartdata.cpp b/src/cartdata.cpp index 6ea7e52..8a59c46 100644 --- a/src/cartdata.cpp +++ b/src/cartdata.cpp @@ -3,12 +3,151 @@ #include #include #include "asset.hpp" +#include "cart.hpp" #include "cartdata.hpp" -#include "cartio.hpp" namespace cart { -/* Utilities */ +/* Cartridge data parsers */ + +bool Parser::validate(void) { + char region[8]; + + if (getRegion(region) < REGION_MIN_LENGTH) + return false; + if (!isValidRegion(region)) + return false; + + auto id = getIdentifiers(); + + if (id) { + // The system ID is excluded from validation as it is going to be + // missing if the game hasn't yet been installed. + uint8_t idFlags = flags & ( + DATA_HAS_TRACE_ID | DATA_HAS_CART_ID | DATA_HAS_INSTALL_ID + ); + + if (id->getFlags() != idFlags) + return false; + } + + return true; +} + +size_t SimpleParser::getRegion(char *output) const { + auto header = _getHeader(); + + __builtin_memcpy(output, header->region, 4); + output[4] = 0; + + return __builtin_strlen(output); +} + +size_t BasicParser::getRegion(char *output) const { + auto header = _getHeader(); + + output[0] = header->region[0]; + output[1] = header->region[1]; + output[2] = 0; + + return 2; +} + +IdentifierSet *BasicParser::getIdentifiers(void) { + return reinterpret_cast(&_dump.data[sizeof(BasicHeader)]); +} + +bool BasicParser::validate(void) { + return (Parser::validate() && _getHeader()->validateChecksum()); +} + +size_t ExtendedParser::getRegion(char *output) const { + auto header = _getHeader(); + + __builtin_memcpy(output, header->region, 4); + output[4] = 0; + + return __builtin_strlen(output); +} + +IdentifierSet *ExtendedParser::getIdentifiers(void) { + if (!(flags & DATA_HAS_PUBLIC_SECTION)) + return nullptr; + + return reinterpret_cast( + &_dump.data[sizeof(ExtendedHeader) + sizeof(PublicIdentifierSet)] + ); +} + +void ExtendedParser::flush(void) { + // Copy over the private identifiers to the public data area. On X76F041 + // carts this area is in the last sector, while on ZS01 carts it is placed + // in the first 32 bytes. + auto pri = getIdentifiers(); + auto pub = reinterpret_cast( + &_getPublicData()[sizeof(ExtendedHeader)] + ); + + pub->traceID.copyFrom(pri->traceID.data); + pub->cartID.copyFrom(pri->cartID.data); +} + +bool ExtendedParser::validate(void) { + return (Parser::validate() && _getHeader()->validateChecksum()); +} + +/* Data format identification */ + +struct KnownFormat { +public: + const char *name; + FormatType format; + uint8_t flags; +}; + +static constexpr int _NUM_KNOWN_FORMATS = 8; + +static const KnownFormat _KNOWN_FORMATS[_NUM_KNOWN_FORMATS]{ + { + // Used by GCB48 (and possibly other games?) + .name = "region string only", + .format = SIMPLE, + .flags = DATA_HAS_PUBLIC_SECTION + }, { + .name = "basic with no IDs", + .format = BASIC, + .flags = 0 + }, { + .name = "basic with TID", + .format = BASIC, + .flags = DATA_HAS_TRACE_ID + }, { + .name = "basic with TID, SID", + .format = BASIC, + .flags = DATA_HAS_TRACE_ID | DATA_HAS_CART_ID + }, { + .name = "basic with code prefix, TID, SID", + .format = BASIC, + .flags = DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID + }, { + // Used by most pre-ZS01 Bemani games + .name = "basic with code prefix, all IDs", + .format = BASIC, + .flags = DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID + | DATA_HAS_INSTALL_ID | DATA_HAS_SYSTEM_ID + }, { + // Used by early (pre-digital-I/O) Bemani games + .name = "extended with no IDs", + .format = EXTENDED, + .flags = DATA_HAS_CODE_PREFIX + }, { + // Used by GE936/GK936 and all ZS01 Bemani games + .name = "extended with all IDs", + .format = EXTENDED, + .flags = DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID + | DATA_HAS_INSTALL_ID | DATA_HAS_SYSTEM_ID | DATA_HAS_PUBLIC_SECTION + } +}; bool isValidRegion(const char *region) { // Character 0: region (A=Asia?, E=Europe, J=Japan, K=Korea, S=?, U=US) @@ -36,235 +175,38 @@ bool isValidRegion(const char *region) { return true; } -/* Common data structures */ - -void BasicHeader::updateChecksum(void) { - auto value = util::sum(reinterpret_cast(this), 4); - - checksum = uint8_t((value & 0xff) ^ 0xff); -} - -bool BasicHeader::validateChecksum(void) const { - auto value = util::sum(reinterpret_cast(this), 4); - - return (checksum == ((value & 0xff) ^ 0xff)); -} - -void ExtendedHeader::updateChecksum(void) { - auto value = util::sum(reinterpret_cast(this), 14); - - checksum = uint16_t(value & 0xffff); -} - -bool ExtendedHeader::validateChecksum(void) const { - auto value = util::sum(reinterpret_cast(this), 14); - - return (checksum == (value & 0xffff)); -} - -uint8_t IdentifierSet::getFlags(void) const { - uint8_t flags = 0; - - if (!traceID.isEmpty()) - flags |= DATA_HAS_TRACE_ID; - if (!cartID.isEmpty()) - flags |= DATA_HAS_CART_ID; - if (!installID.isEmpty()) - flags |= DATA_HAS_INSTALL_ID; - if (!systemID.isEmpty()) - flags |= DATA_HAS_SYSTEM_ID; - - return flags; -} - -void IdentifierSet::setInstallID(uint8_t prefix) { - installID.clear(); - - installID.data[0] = prefix; - installID.updateChecksum(); -} - -void IdentifierSet::updateTraceID(uint8_t prefix, int param) { - traceID.clear(); - - uint8_t *input = &cartID.data[1]; - uint16_t checksum = 0; - - switch (prefix) { - case 0x81: - // TODO: reverse engineer this TID format - traceID.data[2] = 0; - traceID.data[5] = 0; - traceID.data[6] = 0; - - LOG("prefix=0x81"); - break; - - case 0x82: - for (size_t i = 0; i < (sizeof(cartID.data) - 2); i++) { - uint8_t value = *(input++); - - for (int j = 0; j < 8; j++, value >>= 1) { - if (value % 2) - checksum ^= 1 << (i % param); - } - } - - traceID.data[1] = checksum >> 8; - traceID.data[2] = checksum & 0xff; - - LOG("prefix=0x82, checksum=%04x", checksum); - break; - - default: - LOG("unknown prefix 0x%02x", prefix); - } - - traceID.data[0] = prefix; - traceID.updateChecksum(); -} - -/* Data formats */ - -bool CartData::validate(void) { - char region[8]; - - if (getRegion(region) < REGION_MIN_LENGTH) - return false; - if (!isValidRegion(region)) - return false; - - auto id = getIdentifiers(); - - if (id) { - uint8_t idFlags = flags & ( - DATA_HAS_TRACE_ID | DATA_HAS_CART_ID | DATA_HAS_INSTALL_ID | - DATA_HAS_SYSTEM_ID - ); - - if (id->getFlags() != idFlags) - return false; - } - - return true; -} - -size_t SimpleCartData::getRegion(char *output) const { - auto header = _getHeader(); - - __builtin_memcpy(output, header->region, 4); - output[4] = 0; - - return __builtin_strlen(output); -} - -size_t BasicCartData::getRegion(char *output) const { - auto header = _getHeader(); - - output[0] = header->region[0]; - output[1] = header->region[1]; - output[2] = 0; - - return 2; -} - -IdentifierSet *BasicCartData::getIdentifiers(void) { - return reinterpret_cast(&_dump.data[sizeof(BasicHeader)]); -} - -bool BasicCartData::validate(void) { - return (CartData::validate() && _getHeader()->validateChecksum()); -} - -size_t ExtendedCartData::getRegion(char *output) const { - auto header = _getHeader(); - - __builtin_memcpy(output, header->region, 4); - output[4] = 0; - - return __builtin_strlen(output); -} - -IdentifierSet *ExtendedCartData::getIdentifiers(void) { - if (!(flags & DATA_HAS_PUBLIC_SECTION)) - return nullptr; - - return reinterpret_cast( - &_dump.data[sizeof(ExtendedHeader) + sizeof(PublicIdentifierSet)] - ); -} - -void ExtendedCartData::flush(void) { - // Copy over the private identifiers to the public data area. On X76F041 - // carts this area is in the last sector, while on ZS01 carts it is placed - // in the first 32 bytes. - auto pri = getIdentifiers(); - auto pub = reinterpret_cast( - &_getPublicData()[sizeof(ExtendedHeader)] - ); - - pub->traceID.copyFrom(pri->traceID.data); - pub->cartID.copyFrom(pri->cartID.data); -} - -bool ExtendedCartData::validate(void) { - return (CartData::validate() && _getHeader()->validateChecksum()); -} - -/* Data format identification */ - -struct KnownFormat { -public: - FormatType formatType; - uint8_t flags; -}; - -static constexpr int _NUM_KNOWN_FORMATS = 8; - -// More variants may exist. -static const KnownFormat _KNOWN_FORMATS[_NUM_KNOWN_FORMATS]{ - { SIMPLE, DATA_HAS_PUBLIC_SECTION }, - { BASIC, 0 }, - { BASIC, DATA_HAS_TRACE_ID }, - { BASIC, DATA_HAS_TRACE_ID | DATA_HAS_CART_ID }, - { BASIC, DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID }, - { BASIC, DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID - | DATA_HAS_INSTALL_ID | DATA_HAS_SYSTEM_ID }, - { EXTENDED, 0 }, - { EXTENDED, DATA_HAS_CODE_PREFIX | DATA_HAS_TRACE_ID | DATA_HAS_CART_ID - | DATA_HAS_INSTALL_ID | DATA_HAS_SYSTEM_ID | DATA_HAS_PUBLIC_SECTION }, -}; - -CartData *createCartData(Dump &dump, FormatType formatType, uint8_t flags) { +Parser *newCartParser(Dump &dump, FormatType formatType, uint8_t flags) { switch (formatType) { case SIMPLE: - return new SimpleCartData(dump, flags); + return new SimpleParser(dump, flags); case BASIC: - return new BasicCartData(dump, flags); + return new BasicParser(dump, flags); case EXTENDED: - return new ExtendedCartData(dump, flags); + return new ExtendedParser(dump, flags); default: - return new CartData(dump, flags); + return new Parser(dump, flags); } } -CartData *createCartData(Dump &dump) { - for (int i = 0; i < _NUM_KNOWN_FORMATS; i++) { - auto &fmt = _KNOWN_FORMATS[i]; - CartData *data = createCartData(dump, fmt.formatType, fmt.flags); +Parser *newCartParser(Dump &dump) { + // Try all formats from the most complex one to the simplest. + for (int i = _NUM_KNOWN_FORMATS - 1; i >= 0; i++) { + auto &format = _KNOWN_FORMATS[i]; + Parser *parser = newCartParser(dump, format.format, format.flags); - if (data->validate()) { - LOG("found known format, index=%d", i); - return data; + if (parser->validate()) { + LOG("found known data format"); + LOG("%s, index=%d", format.name, i); + return parser; } - delete data; + delete parser; } - LOG("unrecognized dump format"); + LOG("unrecognized data format"); return nullptr; } diff --git a/src/cartdata.hpp b/src/cartdata.hpp index 53d8b5c..3b171f3 100644 --- a/src/cartdata.hpp +++ b/src/cartdata.hpp @@ -5,92 +5,17 @@ #include #include #include "asset.hpp" -#include "cartio.hpp" +#include "cart.hpp" namespace cart { -/* Definitions */ - -enum FormatType : uint8_t { - BLANK = 0, - SIMPLE = 1, - BASIC = 2, - EXTENDED = 3 -}; - -// | | Simple | Basic | Extended | -// | :---------------------- | :-------- | :------- | :-------- | -// | DATA_HAS_CODE_PREFIX | | Optional | Mandatory | -// | DATA_HAS_*_ID | | Optional | Optional | -// | DATA_HAS_PUBLIC_SECTION | Mandatory | | Optional | - -enum FormatFlags : uint8_t { - DATA_HAS_CODE_PREFIX = 1 << 0, - DATA_HAS_TRACE_ID = 1 << 1, - DATA_HAS_CART_ID = 1 << 2, - DATA_HAS_INSTALL_ID = 1 << 3, - DATA_HAS_SYSTEM_ID = 1 << 4, - DATA_HAS_PUBLIC_SECTION = 1 << 5 -}; +/* Cartridge data parsers */ static constexpr size_t CODE_LENGTH = 5; static constexpr size_t REGION_MIN_LENGTH = 2; static constexpr size_t REGION_MAX_LENGTH = 5; -bool isValidRegion(const char *region); - -/* Common data structures */ - -class [[gnu::packed]] SimpleHeader { -public: - char region[4]; -}; - -class [[gnu::packed]] BasicHeader { -public: - char region[2], codePrefix[2]; - uint8_t checksum, _pad[3]; - - void updateChecksum(void); - bool validateChecksum(void) const; -}; - -class [[gnu::packed]] ExtendedHeader { -public: - char code[8]; - uint16_t year; // BCD, can be little endian, big endian or zero - char region[4]; - uint16_t checksum; - - void updateChecksum(void); - bool validateChecksum(void) const; -}; - -class [[gnu::packed]] IdentifierSet { -public: - Identifier traceID, cartID, installID, systemID; // aka TID, SID, MID, XID - - inline void clear(void) { - __builtin_memset(this, 0, sizeof(IdentifierSet)); - } - - uint8_t getFlags(void) const; - void setInstallID(uint8_t prefix); - void updateTraceID(uint8_t prefix, int param); -}; - -class [[gnu::packed]] PublicIdentifierSet { -public: - Identifier traceID, cartID; // aka TID, SID - - inline void clear(void) { - __builtin_memset(this, 0, sizeof(PublicIdentifierSet)); - } -}; - -/* Data formats */ - -class CartData { +class Parser { protected: Dump &_dump; @@ -104,52 +29,53 @@ protected: public: uint8_t flags; - inline CartData(Dump &dump, uint8_t flags = 0) + inline Parser(Dump &dump, uint8_t flags = 0) : _dump(dump), flags(flags) {} + virtual ~Parser(void) {} virtual size_t getRegion(char *output) const { return 0; } virtual IdentifierSet *getIdentifiers(void) { return nullptr; } virtual void flush(void) {} virtual bool validate(void); }; -class SimpleCartData : public CartData { +class SimpleParser : public Parser { private: inline SimpleHeader *_getHeader(void) const { return reinterpret_cast(_getPublicData()); } public: - inline SimpleCartData(Dump &dump, uint8_t flags = 0) - : CartData(dump, flags | DATA_HAS_PUBLIC_SECTION) {} + inline SimpleParser(Dump &dump, uint8_t flags = 0) + : Parser(dump, flags | DATA_HAS_PUBLIC_SECTION) {} size_t getRegion(char *output) const; }; -class BasicCartData : public CartData { +class BasicParser : public Parser { private: inline BasicHeader *_getHeader(void) const { return reinterpret_cast(_getPublicData()); } public: - inline BasicCartData(Dump &dump, uint8_t flags = 0) - : CartData(dump, flags) {} + inline BasicParser(Dump &dump, uint8_t flags = 0) + : Parser(dump, flags) {} size_t getRegion(char *output) const; IdentifierSet *getIdentifiers(void); bool validate(void); }; -class ExtendedCartData : public CartData { +class ExtendedParser : public Parser { private: inline ExtendedHeader *_getHeader(void) const { return reinterpret_cast(_getPublicData()); } public: - inline ExtendedCartData(Dump &dump, uint8_t flags = 0) - : CartData(dump, flags | DATA_HAS_CODE_PREFIX) {} + inline ExtendedParser(Dump &dump, uint8_t flags = 0) + : Parser(dump, flags | DATA_HAS_CODE_PREFIX) {} size_t getRegion(char *output) const; IdentifierSet *getIdentifiers(void); @@ -157,8 +83,9 @@ public: bool validate(void); }; -CartData *createCartData(Dump &dump, FormatType formatType, uint8_t flags = 0); -CartData *createCartData(Dump &dump); +bool isValidRegion(const char *region); +Parser *newCartParser(Dump &dump, FormatType formatType, uint8_t flags = 0); +Parser *newCartParser(Dump &dump); /* Cartridge database */ @@ -168,9 +95,10 @@ public: FormatType formatType; uint8_t flags; - uint8_t traceIDPrefix, traceIDParam, installIDPrefix, _reserved[2]; - uint8_t dataKey[8]; - char code[8], region[8], name[64]; + uint8_t traceIDPrefix, traceIDParam, installIDPrefix; + uint16_t year; + uint8_t dataKey[8]; + char code[8], region[8], name[64]; inline int compare(const char *_code, const char *_region) const { int diff = __builtin_strncmp(code, _code, CODE_LENGTH + 1); diff --git a/src/cartio.cpp b/src/cartio.cpp index 9f37acd..f85d19d 100644 --- a/src/cartio.cpp +++ b/src/cartio.cpp @@ -1,90 +1,20 @@ -#include #include #include "ps1/system.h" -#include "vendor/miniz.h" +#include "cart.hpp" #include "cartio.hpp" #include "io.hpp" -#include "util.hpp" #include "zs01.hpp" namespace cart { -/* Common data structures */ - -void Identifier::updateChecksum(void) { - data[7] = (util::sum(data, 7) & 0xff) ^ 0xff; -} - -bool Identifier::validateChecksum(void) const { - uint8_t value = (util::sum(data, 7) & 0xff) ^ 0xff; - - if (value != data[7]) { - LOG("checksum mismatch, exp=0x%02x, got=0x%02x", value, data[7]); - return false; - } - - return true; -} - -void Identifier::updateDSCRC(void) { - data[7] = util::dsCRC8(data, 7); -} - -bool Identifier::validateDSCRC(void) const { - uint8_t value = util::dsCRC8(data, 7); - - if (value != data[7]) { - LOG("CRC mismatch, exp=0x%02x, got=0x%02x", value, data[7]); - return false; - } - - return true; -} - -/* Dump structure and utilities */ - -const ChipSize CHIP_SIZES[NUM_CHIP_TYPES]{ - { .dataLength = 0, .publicDataOffset = 0, .publicDataLength = 0 }, - { .dataLength = 512, .publicDataOffset = 384, .publicDataLength = 128 }, - { .dataLength = 112, .publicDataOffset = 0, .publicDataLength = 0 }, - { .dataLength = 112, .publicDataOffset = 0, .publicDataLength = 32 } -}; - -size_t Dump::toQRString(char *output) const { - uint8_t compressed[MAX_QR_STRING_LENGTH]; - size_t uncompLength = getDumpLength(); - size_t compLength = MAX_QR_STRING_LENGTH; - - int error = mz_compress2( - compressed, reinterpret_cast(&compLength), - reinterpret_cast(this), uncompLength, - MZ_BEST_COMPRESSION - ); - - if (error != MZ_OK) { - LOG("compression error, code=%d", error); - return 0; - } - LOG( - "dump compressed, size=%d, ratio=%d%%", compLength, - compLength * 100 / uncompLength - ); - - compLength = util::encodeBase45(&output[5], compressed, compLength); - __builtin_memcpy(&output[0], "573::", 5); - __builtin_memcpy(&output[compLength + 5], "::", 3); - - return compLength + 7; -} - /* Functions common to all cartridge drivers */ static constexpr int _X76_MAX_ACK_POLLS = 5; static constexpr int _X76_WRITE_DELAY = 10000; static constexpr int _ZS01_PACKET_DELAY = 30000; -CartError Cart::readSystemID(void) { +DriverError Driver::readSystemID(void) { auto mask = setInterruptMask(0); if (!io::dsDIOReset()) { @@ -110,7 +40,7 @@ CartError Cart::readSystemID(void) { return NO_ERROR; } -CartError X76Cart::readCartID(void) { +DriverError X76Driver::readCartID(void) { auto mask = setInterruptMask(0); if (!io::dsCartReset()) { @@ -136,7 +66,7 @@ CartError X76Cart::readCartID(void) { return NO_ERROR; } -CartError X76Cart::_x76Command( +DriverError X76Driver::_x76Command( uint8_t cmd, uint8_t param, uint8_t pollByte ) const { io::i2cStartWithCS(); @@ -190,7 +120,7 @@ enum X76F041ConfigOp : uint8_t { _X76F041_CFG_ERASE = 0x70 }; -CartError X76F041Cart::readPrivateData(void) { +DriverError X76F041Driver::readPrivateData(void) { // Reads can be done with any block size, but a single read operation can't // cross 128-byte block boundaries. for (int i = 0; i < 512; i += 128) { @@ -216,7 +146,7 @@ CartError X76F041Cart::readPrivateData(void) { return NO_ERROR; } -CartError X76F041Cart::writeData(void) { +DriverError X76F041Driver::writeData(void) { // Writes can only be done in 8-byte blocks. for (int i = 0; i < 512; i += 8) { auto error = _x76Command( @@ -236,7 +166,7 @@ CartError X76F041Cart::writeData(void) { return NO_ERROR; } -CartError X76F041Cart::erase(void) { +DriverError X76F041Driver::erase(void) { auto error = _x76Command( _X76F041_CONFIG, _X76F041_CFG_ERASE, _X76F041_ACK_POLL ); @@ -247,7 +177,7 @@ CartError X76F041Cart::erase(void) { return NO_ERROR; } -CartError X76F041Cart::setDataKey(const uint8_t *key) { +DriverError X76F041Driver::setDataKey(const uint8_t *key) { auto error = _x76Command( _X76F041_CONFIG, _X76F041_CFG_SET_DATA_KEY, _X76F041_ACK_POLL ); @@ -283,25 +213,27 @@ enum X76F100Command : uint8_t { // TODO: actually implement this (even though no X76F100 carts were ever made) -CartError X76F100Cart::readPrivateData(void) { +DriverError X76F100Driver::readPrivateData(void) { return UNSUPPORTED_OP; } -CartError X76F100Cart::writeData(void) { +DriverError X76F100Driver::writeData(void) { return UNSUPPORTED_OP; } -CartError X76F100Cart::erase(void) { +DriverError X76F100Driver::erase(void) { return UNSUPPORTED_OP; } -CartError X76F100Cart::setDataKey(const uint8_t *key) { +DriverError X76F100Driver::setDataKey(const uint8_t *key) { return UNSUPPORTED_OP; } /* ZS01 driver */ -CartError ZS01Cart::_transact(zs01::Packet &request, zs01::Packet &response) { +DriverError ZS01Driver::_transact( + zs01::Packet &request, zs01::Packet &response +) { io::i2cStart(); if (!io::i2cWriteBytes( @@ -328,9 +260,9 @@ CartError ZS01Cart::_transact(zs01::Packet &request, zs01::Packet &response) { return NO_ERROR; } -CartError ZS01Cart::readCartID(void) { +DriverError ZS01Driver::readCartID(void) { zs01::Packet request, response; - CartError error; + DriverError error; request.address = zs01::ADDR_ZS01_ID; request.encodeReadRequest(); @@ -360,14 +292,14 @@ CartError ZS01Cart::readCartID(void) { return NO_ERROR; } -CartError ZS01Cart::readPublicData(void) { +DriverError ZS01Driver::readPublicData(void) { zs01::Packet request, response; for (int i = zs01::ADDR_PUBLIC; i < zs01::ADDR_PUBLIC_END; i++) { request.address = i; request.encodeReadRequest(); - CartError error = _transact(request, response); + DriverError error = _transact(request, response); if (error) return error; @@ -378,7 +310,7 @@ CartError ZS01Cart::readPublicData(void) { return NO_ERROR; } -CartError ZS01Cart::readPrivateData(void) { +DriverError ZS01Driver::readPrivateData(void) { zs01::Packet request, response; zs01::Key key; @@ -388,7 +320,7 @@ CartError ZS01Cart::readPrivateData(void) { request.address = i; request.encodeReadRequest(key, _encoderState); - CartError error = _transact(request, response); + DriverError error = _transact(request, response); if (error) return error; @@ -400,7 +332,7 @@ CartError ZS01Cart::readPrivateData(void) { request.address = zs01::ADDR_CONFIG; request.encodeReadRequest(key, _encoderState); - CartError error = _transact(request, response); + DriverError error = _transact(request, response); if (error) return error; @@ -410,7 +342,7 @@ CartError ZS01Cart::readPrivateData(void) { return NO_ERROR; } -CartError ZS01Cart::writeData(void) { +DriverError ZS01Driver::writeData(void) { zs01::Packet request, response; zs01::Key key; @@ -421,7 +353,7 @@ CartError ZS01Cart::writeData(void) { request.copyFrom(&_dump.data[i * sizeof(request.data)]); request.encodeWriteRequest(key, _encoderState); - CartError error = _transact(request, response); + DriverError error = _transact(request, response); if (error) return error; } @@ -433,7 +365,7 @@ CartError ZS01Cart::writeData(void) { return _transact(request, response); } -CartError ZS01Cart::erase(void) { +DriverError ZS01Driver::erase(void) { zs01::Packet request, response; zs01::Key key; @@ -446,7 +378,7 @@ CartError ZS01Cart::erase(void) { return _transact(request, response); } -CartError ZS01Cart::setDataKey(const uint8_t *key) { +DriverError ZS01Driver::setDataKey(const uint8_t *key) { zs01::Packet request, response; zs01::Key newKey; @@ -456,7 +388,7 @@ CartError ZS01Cart::setDataKey(const uint8_t *key) { request.copyFrom(key); request.encodeWriteRequest(newKey, _encoderState); - CartError error = _transact(request, response); + DriverError error = _transact(request, response); if (error) return error; @@ -473,30 +405,30 @@ enum ChipIdentifier : uint32_t { _ID_ZS01 = 0x5a530001 }; -Cart *createCart(Dump &dump) { +Driver *newCartDriver(Dump &dump) { if (!io::getCartInsertionStatus()) { LOG("DSR not asserted"); - return new Cart(dump); + return new Driver(dump); } uint32_t id1 = io::i2cResetZS01(); - LOG("id1=0x%08x", id1); + LOG("detecting ZS01, id1=0x%08x", id1); if (id1 == _ID_ZS01) - return new ZS01Cart(dump); + return new ZS01Driver(dump); uint32_t id2 = io::i2cResetX76(); - LOG("id2=0x%08x", id2); + LOG("detecting X76, id2=0x%08x", id2); switch (id2) { case _ID_X76F041: - return new X76F041Cart(dump); + return new X76F041Driver(dump); //case _ID_X76F100: - //return new X76F100Cart(dump); + //return new X76F100Driver(dump); default: - return new Cart(dump); + return new Driver(dump); } } diff --git a/src/cartio.hpp b/src/cartio.hpp index 8d4b4fd..c220412 100644 --- a/src/cartio.hpp +++ b/src/cartio.hpp @@ -1,16 +1,13 @@ #pragma once -#include #include #include "util.hpp" #include "zs01.hpp" namespace cart { -/* Definitions */ - -enum CartError { +enum DriverError { NO_ERROR = 0, UNSUPPORTED_OP = 1, DS2401_NO_RESP = 2, @@ -23,108 +20,14 @@ enum CartError { ZS01_CRC_MISMATCH = 9 }; -enum ChipType : uint8_t { - NONE = 0, - X76F041 = 1, - X76F100 = 2, - ZS01 = 3 -}; - -enum DumpFlag : uint8_t { - DUMP_HAS_SYSTEM_ID = 1 << 0, - DUMP_HAS_CART_ID = 1 << 1, - DUMP_CONFIG_OK = 1 << 2, - DUMP_SYSTEM_ID_OK = 1 << 3, - DUMP_CART_ID_OK = 1 << 4, - DUMP_ZS_ID_OK = 1 << 5, - DUMP_PUBLIC_DATA_OK = 1 << 6, - DUMP_PRIVATE_DATA_OK = 1 << 7 -}; - -static constexpr int NUM_CHIP_TYPES = 4; -static constexpr size_t MAX_QR_STRING_LENGTH = 0x600; - -/* Common data structures */ - -class [[gnu::packed]] Identifier { -public: - uint8_t data[8]; - - inline void copyFrom(const uint8_t *source) { - __builtin_memcpy(data, source, sizeof(data)); - } - inline void copyTo(uint8_t *dest) const { - __builtin_memcpy(dest, data, sizeof(data)); - } - inline void clear(void) { - __builtin_memset(data, 0, sizeof(data)); - } - inline bool isEmpty(void) const { - return (util::sum(data, sizeof(data)) == 0); - } - - inline size_t toString(char *output) const { - return util::hexToString(output, data, sizeof(data), '-'); - } - inline size_t toSerialNumber(char *output) const { - return util::serialNumberToString(output, &data[1]); - } - - void updateChecksum(void); - bool validateChecksum(void) const; - void updateDSCRC(void); - bool validateDSCRC(void) const; -}; - -/* Dump structure and utilities */ - -struct ChipSize { -public: - size_t dataLength, publicDataOffset, publicDataLength; -}; - -extern const ChipSize CHIP_SIZES[NUM_CHIP_TYPES]; - -class [[gnu::packed]] Dump { -public: - ChipType chipType; - uint8_t flags; - - Identifier systemID, cartID, zsID; - - uint8_t dataKey[8], config[8]; - uint8_t data[512]; - - inline const ChipSize &getChipSize(void) const { - return CHIP_SIZES[chipType]; - } - inline size_t getDumpLength(void) const { - return (sizeof(Dump) - sizeof(data)) + getChipSize().dataLength; - } - inline void clear(void) { - __builtin_memset(this, 0, sizeof(Dump)); - } - inline void clearData(void) { - __builtin_memset(data, 0, getChipSize().dataLength); - } - inline bool isDataEmpty(void) const { - size_t length = getChipSize().dataLength; - auto sum = util::sum(data, length); - - return (!sum || (sum == (0xff * length))); - } - - size_t toQRString(char *output) const; -}; - /* Cartridge driver classes */ -class Cart { +class Driver { protected: Dump &_dump; public: - inline Cart(Dump &dump, ChipType chipType = NONE, uint8_t flags = 0) + inline Driver(Dump &dump, ChipType chipType = NONE, uint8_t flags = 0) : _dump(dump) { dump.clear(); @@ -132,67 +35,68 @@ public: dump.flags = flags; } - virtual CartError readSystemID(void); - virtual CartError readCartID(void) { return UNSUPPORTED_OP; } - virtual CartError readPublicData(void) { return UNSUPPORTED_OP; } - virtual CartError readPrivateData(void) { return UNSUPPORTED_OP; } - virtual CartError writeData(void) { return UNSUPPORTED_OP; } - virtual CartError erase(void) { return UNSUPPORTED_OP; } - virtual CartError setDataKey(const uint8_t *key) { return UNSUPPORTED_OP; } + virtual ~Driver(void) {} + virtual DriverError readSystemID(void); + virtual DriverError readCartID(void) { return UNSUPPORTED_OP; } + virtual DriverError readPublicData(void) { return UNSUPPORTED_OP; } + virtual DriverError readPrivateData(void) { return UNSUPPORTED_OP; } + virtual DriverError writeData(void) { return UNSUPPORTED_OP; } + virtual DriverError erase(void) { return UNSUPPORTED_OP; } + virtual DriverError setDataKey(const uint8_t *key) { return UNSUPPORTED_OP; } }; -class [[gnu::packed]] X76Cart : public Cart { +class [[gnu::packed]] X76Driver : public Driver { protected: - CartError _readDS2401(void); - CartError _x76Command(uint8_t cmd, uint8_t param, uint8_t pollByte) const; + DriverError _readDS2401(void); + DriverError _x76Command(uint8_t cmd, uint8_t param, uint8_t pollByte) const; public: - inline X76Cart(Dump &dump, ChipType chipType) - : Cart(dump, chipType) {} + inline X76Driver(Dump &dump, ChipType chipType) + : Driver(dump, chipType) {} - CartError readCartID(void); + DriverError readCartID(void); }; -class [[gnu::packed]] X76F041Cart : public X76Cart { +class [[gnu::packed]] X76F041Driver : public X76Driver { public: - inline X76F041Cart(Dump &dump) - : X76Cart(dump, X76F041) {} + inline X76F041Driver(Dump &dump) + : X76Driver(dump, X76F041) {} - CartError readPrivateData(void); - CartError writeData(void); - CartError erase(void); - CartError setDataKey(const uint8_t *key); + DriverError readPrivateData(void); + DriverError writeData(void); + DriverError erase(void); + DriverError setDataKey(const uint8_t *key); }; -class [[gnu::packed]] X76F100Cart : public X76Cart { +class [[gnu::packed]] X76F100Driver : public X76Driver { public: - inline X76F100Cart(Dump &dump) - : X76Cart(dump, X76F100) {} + inline X76F100Driver(Dump &dump) + : X76Driver(dump, X76F100) {} - CartError readPrivateData(void); - CartError writeData(void); - CartError erase(void); - CartError setDataKey(const uint8_t *key); + DriverError readPrivateData(void); + DriverError writeData(void); + DriverError erase(void); + DriverError setDataKey(const uint8_t *key); }; -class [[gnu::packed]] ZS01Cart : public Cart { +class [[gnu::packed]] ZS01Driver : public Driver { private: uint8_t _encoderState; - CartError _transact(zs01::Packet &request, zs01::Packet &response); + DriverError _transact(zs01::Packet &request, zs01::Packet &response); public: - inline ZS01Cart(Dump &dump) - : Cart(dump, ZS01, DUMP_HAS_CART_ID), _encoderState(0) {} + inline ZS01Driver(Dump &dump) + : Driver(dump, ZS01, DUMP_HAS_CART_ID), _encoderState(0) {} - CartError readCartID(void); - CartError readPublicData(void); - CartError readPrivateData(void); - CartError writeData(void); - CartError erase(void); - CartError setDataKey(const uint8_t *key); + DriverError readCartID(void); + DriverError readPublicData(void); + DriverError readPrivateData(void); + DriverError writeData(void); + DriverError erase(void); + DriverError setDataKey(const uint8_t *key); }; -Cart *createCart(Dump &dump); +Driver *newCartDriver(Dump &dump); } diff --git a/src/gpu.cpp b/src/gpu.cpp index e204af6..7213fac 100644 --- a/src/gpu.cpp +++ b/src/gpu.cpp @@ -1,10 +1,12 @@ #include +#include #include #include "ps1/gpucmd.h" #include "ps1/registers.h" #include "ps1/system.h" #include "gpu.hpp" +#include "util.hpp" namespace gpu { @@ -49,20 +51,6 @@ size_t upload(const RectWH &rect, const void *data, bool wait) { /* Rendering context */ -void Context::_flushLayer(void) { - if (_currentListPtr == _lastListPtr) - return; - - auto layer = _drawBuffer().layers.pushItem(); - assert(layer); - - *(_currentListPtr++) = gp0_endTag(1); - *(_currentListPtr++) = gp0_irq(); - - *layer = _lastListPtr; - _lastListPtr = _currentListPtr; -} - void Context::_applyResolution(VideoMode mode, int shiftX, int shiftY) const { GP1HorizontalRes hres; GP1VerticalRes vres = (height > 256) ? GP1_VRES_512 : GP1_VRES_256; @@ -98,42 +86,17 @@ void Context::flip(void) { auto &oldBuffer = _drawBuffer(), &newBuffer = _dispBuffer(); // Ensure the GPU has finished drawing the previous frame. - while (newBuffer.layers.length) + while (!isIdle()) __asm__ volatile(""); - auto mask = setInterruptMask(0); + *_currentListPtr = gp0_endTag(0); + _currentListPtr = newBuffer.displayList; + _currentBuffer ^= 1; - _flushLayer(); - _currentListPtr = newBuffer.displayList; - _lastListPtr = newBuffer.displayList; - _currentBuffer ^= 1; - - GPU_GP1 = gp1_fbOffset(oldBuffer.clip.x1, oldBuffer.clip.y1); - - // Kick off drawing. - drawNextLayer(); - if (mask) - setInterruptMask(mask); -} - -void Context::drawNextLayer(void) { - //auto mask = setInterruptMask(0); - auto layer = _dispBuffer().layers.popItem(); - - //if (mask) - //setInterruptMask(mask); - if (!layer) - return; - - while (DMA_CHCR(DMA_GPU) & DMA_CHCR_ENABLE) - __asm__ volatile(""); - while (!(GPU_GP1 & GP1_STAT_CMD_READY)) - __asm__ volatile(""); - - GPU_GP1 = gp1_acknowledge(); + GPU_GP1 = gp1_fbOffset(newBuffer.clip.x1, newBuffer.clip.y1); GPU_GP1 = gp1_dmaRequestMode(GP1_DREQ_GP0_WRITE); - DMA_MADR(DMA_GPU) = reinterpret_cast(*layer); + DMA_MADR(DMA_GPU) = reinterpret_cast(oldBuffer.displayList); DMA_CHCR(DMA_GPU) = DMA_CHCR_WRITE | DMA_CHCR_MODE_LIST | DMA_CHCR_ENABLE; } @@ -156,7 +119,6 @@ void Context::setResolution( } _currentListPtr = _buffers[0].displayList; - _lastListPtr = _buffers[0].displayList; _currentBuffer = 0; _applyResolution(mode); @@ -175,13 +137,7 @@ uint32_t *Context::newPacket(size_t length) { } void Context::newLayer(int x, int y, int drawWidth, int drawHeight) { - auto mask = setInterruptMask(0); - - _flushLayer(); - if (mask) - setInterruptMask(mask); - - auto &clip = _dispBuffer().clip; + auto &clip = _drawBuffer().clip; x += clip.x1; y += clip.y1; diff --git a/src/gpu.hpp b/src/gpu.hpp index e7fcc68..2c3127a 100644 --- a/src/gpu.hpp +++ b/src/gpu.hpp @@ -5,7 +5,6 @@ #include #include "ps1/gpucmd.h" #include "ps1/registers.h" -#include "util.hpp" namespace gpu { @@ -41,6 +40,12 @@ static inline void init(void) { TIMER_CTRL(1) = TIMER_CTRL_EXT_CLOCK; } +static inline bool isIdle(void) { + return ( + !(DMA_CHCR(DMA_GPU) & DMA_CHCR_ENABLE) && (GPU_GP1 & GP1_STAT_CMD_READY) + ); +} + static inline void enableDisplay(bool enable) { GPU_GP1 = gp1_dispBlank(!enable); } @@ -56,14 +61,12 @@ struct Buffer { public: Rect clip; uint32_t displayList[DISPLAY_LIST_SIZE]; - - volatile util::RingBuffer layers; }; class Context { private: Buffer _buffers[2]; - uint32_t *_currentListPtr, *_lastListPtr; + uint32_t *_currentListPtr; int _currentBuffer; uint32_t _lastTexpage; @@ -75,7 +78,6 @@ private: return _buffers[_currentBuffer ^ 1]; } - void _flushLayer(void); void _applyResolution(VideoMode mode, int shiftX = 0, int shiftY = 0) const; public: @@ -114,7 +116,6 @@ public: void setResolution( VideoMode mode, int width, int height, bool sideBySide = false ); - void drawNextLayer(void); void flip(void); uint32_t *newPacket(size_t length); diff --git a/tools/convertExecutable.py b/tools/convertExecutable.py index 9d7a2ff..2303577 100755 --- a/tools/convertExecutable.py +++ b/tools/convertExecutable.py @@ -12,10 +12,11 @@ should start in PAL or NTSC mode by default). Requires no external dependencies. __version__ = "0.1.0" __author__ = "spicyjpeg" -from argparse import ArgumentParser, FileType, Namespace -from enum import IntEnum, IntFlag -from struct import Struct -from typing import BinaryIO, ByteString, Generator +from argparse import ArgumentParser, FileType, Namespace +from dataclasses import dataclass +from enum import IntEnum, IntFlag +from struct import Struct +from typing import BinaryIO, ByteString, Generator ## Utilities @@ -67,11 +68,11 @@ class ProgHeaderFlag(IntFlag): WRITE = 1 << 1 READ = 1 << 2 +@dataclass class Segment: - def __init__(self, address: int, data: ByteString, flags: int): - self.address: int = address - self.data: ByteString = data - self.flags: ProgHeaderFlag = ProgHeaderFlag(flags) + address: int + data: bytes + flags: ProgHeaderFlag def isReadOnly(self) -> bool: return not \ @@ -79,41 +80,33 @@ class Segment: class ELF: def __init__(self, _file: BinaryIO): - self.type: ELFType | None = None - self.architecture: ELFArchitecture | None = None - - self.abi: int | None = None - self.entryPoint: int | None = None - self.flags: int | None = None - - self.segments: list[Segment] = [] - - self._parse(_file) - - def _parse(self, _file: BinaryIO): # Parse the file header and perform some minimal validation. _file.seek(0) - magic, wordSize, endianness, _, self.abi, _type, architecture, _, \ - self.entryPoint, progHeaderOffset, secHeaderOffset, self.flags, \ - elfHeaderLength, progHeaderLength, progHeaderCount, secHeaderLength, \ - secHeaderCount, _ = \ + magic, wordSize, endianness, _, abi, _type, architecture, _, \ + entryPoint, progHeaderOffset, secHeaderOffset, flags, elfHeaderSize, \ + progHeaderSize, progHeaderCount, secHeaderSize, secHeaderCount, _ = \ parseStructFromFile(_file, ELF_HEADER_STRUCT) - self.type = ELFType(_type) - self.architecture = ELFArchitecture(architecture) + self.type: ELFType = ELFType(_type) + self.architecture: int = architecture + self.abi: int = abi + self.entryPoint: int = entryPoint + self.flags: int = flags if magic != ELF_HEADER_MAGIC: raise RuntimeError("file is not a valid ELF") if wordSize != 1 or endianness != ELFEndianness.LITTLE: raise RuntimeError("ELF file must be 32-bit little-endian") if ( - elfHeaderLength != ELF_HEADER_STRUCT.size or - progHeaderLength != PROG_HEADER_STRUCT.size + elfHeaderSize != ELF_HEADER_STRUCT.size or + progHeaderSize != PROG_HEADER_STRUCT.size ): raise RuntimeError("unsupported ELF format") # Parse the program headers and extract all loadable segments. + self.segments: list[Segment] = [] + _file.seek(progHeaderOffset) for (