diff --git a/data/gameinfo.json b/data/gameinfo.json index e8249cd..c568e10 100644 --- a/data/gameinfo.json +++ b/data/gameinfo.json @@ -9,9 +9,7 @@ "identifiers": [ "darkhleg" ], "name": "Dark Horse Legend", - "year": 1998, - - "flags": {} + "year": 1998 }, { "specifications": [ "GQ" ], @@ -70,9 +68,7 @@ "identifiers": [ "powyakex" ], "name": "Jikkyou Powerful Pro Yakyuu EX", - "year": 1998, - - "flags": {} + "year": 1998 }, { "specifications": [ "GC" ], @@ -81,9 +77,7 @@ "identifiers": [ "jppyex98" ], "name": "Jikkyou Powerful Pro Yakyuu EX '98", - "year": 1998, - - "flags": {} + "year": 1998 }, { "specifications": [ "GC" ], @@ -92,9 +86,7 @@ "identifiers": [ "konam80sa", "konam80s", "konam80sk", "konam80su" ], "name": "Konami 80's AC Special", - "year": 1998, - - "flags": {} + "year": 1998 }, { "specifications": [ "GC" ], @@ -103,9 +95,7 @@ "identifiers": [ "konam80sj" ], "name": "Konami 80's Gallery", - "year": 1998, - - "flags": {} + "year": 1998 }, { "specifications": [ "GC", "GN" ], @@ -244,9 +234,7 @@ "identifiers": [ "gchgchmp" ], "name": "Gachaga Champ", - "year": 1999, - - "flags": {} + "year": 1999 }, { "specifications": [ "GQ" ], @@ -506,9 +494,7 @@ "identifiers": [ "hypbbc2p", "hypbbc2pk" ], "name": "Hyper Bishi Bashi Champ [2-player]", - "year": 1999, - - "flags": {} + "year": 1999 }, { "specifications": [ "GC" ], @@ -580,7 +566,11 @@ "series": "PunchMania", "year": 2000, - "ioBoard": "GX700-PWB(F)" + "ioBoard": "GX700-PWB(F)", + + "gameCart": { + "pcb": "GX700-PWB(J)" + } }, { "specifications": [ "GQ" ], @@ -592,7 +582,11 @@ "series": "PunchMania", "year": 2000, - "ioBoard": "GX700-PWB(F)" + "ioBoard": "GX700-PWB(F)", + + "gameCart": { + "pcb": "GX700-PWB(J)" + } }, { "specifications": [ "GQ" ], @@ -636,9 +630,7 @@ "identifiers": [ "stepchmp" ], "name": "Step Champ", - "year": 1999, - - "flags": {} + "year": 1999 }, { "specifications": [ "GE", "GK" ], @@ -706,9 +698,7 @@ "identifiers": [ "animechmp" ], "name": "Anime Champ", - "year": 2000, - - "flags": {} + "year": 2000 }, { "specifications": [ "GQ" ], @@ -720,7 +710,11 @@ "series": "PunchMania", "year": 2000, - "ioBoard": "GX700-PWB(F)" + "ioBoard": "GX700-PWB(F)", + + "gameCart": { + "pcb": "GX700-PWB(J)" + } }, { "specifications": [ "GQ" ], @@ -732,7 +726,11 @@ "series": "PunchMania", "year": 2000, - "ioBoard": "GX700-PWB(F)" + "ioBoard": "GX700-PWB(F)", + + "gameCart": { + "pcb": "GX700-PWB(J)" + } }, { "specifications": [ "GC" ], @@ -754,7 +752,9 @@ "name": "Salary Man Champ [2-player]", "year": 2000, - "flags": {} + "gameCart": { + "pcb": "PWB0000088954" + } }, { "specifications": [ "GC" ], @@ -1047,9 +1047,7 @@ "identifiers": [ "gbbchmp" ], "name": "Great Bishi Bashi Champ", - "year": 2002, - - "flags": {} + "year": 2002 }, { "specifications": [ "GC" ], diff --git a/src/common/fs/fat.cpp b/src/common/fs/fat.cpp index ca72e0c..2b88e8d 100644 --- a/src/common/fs/fat.cpp +++ b/src/common/fs/fat.cpp @@ -258,6 +258,17 @@ File *FATProvider::openFile(const char *path, uint32_t flags) { return file; } +bool FATProvider::deleteFile(const char *path) { + auto error = f_unlink(&_fs, path); + + if (error) { + LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], path); + return false; + } + + return true; +} + /* FatFs library API glue */ static constexpr int _MUTEX_TIMEOUT = 30000000; diff --git a/src/common/fs/fat.hpp b/src/common/fs/fat.hpp index 07ab79d..1b87b26 100644 --- a/src/common/fs/fat.hpp +++ b/src/common/fs/fat.hpp @@ -72,6 +72,7 @@ public: bool createDirectory(const char *path); File *openFile(const char *path, uint32_t flags); + bool deleteFile(const char *path); }; } diff --git a/src/common/fs/file.hpp b/src/common/fs/file.hpp index 5dd6843..7f9e6ee 100644 --- a/src/common/fs/file.hpp +++ b/src/common/fs/file.hpp @@ -148,6 +148,7 @@ public: virtual bool createDirectory(const char *path) { return false; } virtual File *openFile(const char *path, uint32_t flags) { return nullptr; } + virtual bool deleteFile(const char *path) { return false; } virtual size_t loadData(util::Data &output, const char *path); virtual size_t loadData(void *output, size_t length, const char *path); virtual size_t saveData(const void *input, size_t length, const char *path); diff --git a/src/common/fs/iso9660.cpp b/src/common/fs/iso9660.cpp index 0498359..8ba5d33 100644 --- a/src/common/fs/iso9660.cpp +++ b/src/common/fs/iso9660.cpp @@ -385,7 +385,8 @@ bool ISO9660Provider::_getRecord( } bool ISO9660Provider::init(storage::Device &dev) { - _dev = &dev; + if (type) + return false; // Locate and parse the primary volume descriptor. size_t numPVDSectors = util::min( @@ -425,6 +426,7 @@ bool ISO9660Provider::init(storage::Device &dev) { type = ISO9660; capacity = uint64_t(pvd->volumeLength.le) * _dev->sectorLength; + _dev = &dev; LOG_FS("mounted ISO: %s", volumeLabel); return true; diff --git a/src/common/fs/misc.cpp b/src/common/fs/misc.cpp index 32feb9b..3a7c685 100644 --- a/src/common/fs/misc.cpp +++ b/src/common/fs/misc.cpp @@ -182,6 +182,17 @@ File *HostProvider::openFile(const char *path, uint32_t flags) { return file; } +bool HostProvider::deleteFile(const char *path) { + int error = pcdrvUnlink(path); + + if (error < 0) { + LOG_FS("PCDRV error %d: %s", error, path); + return false; + } + + return true; +} + /* Virtual filesystem driver */ VFSMountPoint *VFSProvider::_getMounted(const char *path) { @@ -311,6 +322,15 @@ File *VFSProvider::openFile(const char *path, uint32_t flags) { return mp->provider->openFile(&path[mp->pathOffset], flags); } +bool VFSProvider::deleteFile(const char *path) { + auto mp = _getMounted(path); + + if (!mp) + return false; + + return mp->provider->deleteFile(&path[mp->pathOffset]); +} + size_t VFSProvider::loadData(util::Data &output, const char *path) { auto mp = _getMounted(path); diff --git a/src/common/fs/misc.hpp b/src/common/fs/misc.hpp index 4cc9410..eeb8861 100644 --- a/src/common/fs/misc.hpp +++ b/src/common/fs/misc.hpp @@ -61,6 +61,7 @@ public: bool createDirectory(const char *path); File *openFile(const char *path, uint32_t flags); + bool deleteFile(const char *path); }; /* Virtual filesystem driver */ @@ -98,6 +99,7 @@ public: bool createDirectory(const char *path); File *openFile(const char *path, uint32_t flags); + bool deleteFile(const char *path); size_t loadData(util::Data &output, const char *path); size_t loadData(void *output, size_t length, const char *path); size_t saveData(const void *input, size_t length, const char *path); diff --git a/src/common/fs/package.cpp b/src/common/fs/package.cpp index 6ebb320..832bdcc 100644 --- a/src/common/fs/package.cpp +++ b/src/common/fs/package.cpp @@ -129,6 +129,7 @@ bool PackageProvider::getFileInfo(FileInfo &output, const char *path) { __builtin_strncpy(output.name, ptr, sizeof(output.name)); output.size = entry->uncompLength; output.attributes = READ_ONLY | ARCHIVE; + return true; } diff --git a/src/common/gpu.cpp b/src/common/gpu.cpp index 3ca74e8..94b0ff6 100644 --- a/src/common/gpu.cpp +++ b/src/common/gpu.cpp @@ -32,6 +32,18 @@ namespace gpu { static constexpr int _DMA_CHUNK_SIZE = 1; static constexpr int _DMA_TIMEOUT = 10000; +void init(void) { + DMA_DPCR |= 0 + | (DMA_DPCR_ENABLE << (DMA_GPU * 4)) + | (DMA_DPCR_ENABLE << (DMA_OTC * 4)); + + TIMER_CTRL(0) = TIMER_CTRL_EXT_CLOCK; + TIMER_CTRL(1) = TIMER_CTRL_EXT_CLOCK; + + GPU_GP1 = gp1_resetGPU(); + GPU_GP1 = gp1_resetFIFO(); +} + size_t upload(const RectWH &rect, const void *data, bool wait) { size_t length = (rect.w * rect.h) / 2; diff --git a/src/common/gpu.hpp b/src/common/gpu.hpp index a66fc05..ee9eb02 100644 --- a/src/common/gpu.hpp +++ b/src/common/gpu.hpp @@ -48,14 +48,6 @@ public: /* Basic API */ -static inline void init(void) { - GPU_GP1 = gp1_resetGPU(); - GPU_GP1 = gp1_resetFIFO(); - - TIMER_CTRL(0) = TIMER_CTRL_EXT_CLOCK; - 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) @@ -66,6 +58,7 @@ static inline void enableDisplay(bool enable) { GPU_GP1 = gp1_dispBlank(!enable); } +void init(void); size_t upload(const RectWH &rect, const void *data, bool wait = false); size_t download(const RectWH &rect, void *data, bool wait = false); diff --git a/src/common/io.cpp b/src/common/io.cpp index b28acca..608131c 100644 --- a/src/common/io.cpp +++ b/src/common/io.cpp @@ -46,6 +46,7 @@ void init(void) { | (23 << 16) // Number of address lines | ( 4 << 24) // DMA read/write delay | BIU_CTRL_DMA_DELAY; + DMA_DPCR |= DMA_DPCR_ENABLE << (DMA_PIO * 4); #if 0 // Revision D of the main board has footprints for either eight 8-bit RAM @@ -158,12 +159,12 @@ void getRTCTime(util::Date &output) { SYS573_RTC_CTRL &= ~SYS573_RTC_CTRL_READ; - output.year = (year & 15) + 10 * ((year >> 4) & 15); // 0-99 - output.month = (month & 15) + 10 * ((month >> 4) & 1); // 1-12 - output.day = (day & 15) + 10 * ((day >> 4) & 3); // 1-31 - output.hour = (hour & 15) + 10 * ((hour >> 4) & 3); // 0-23 - output.minute = (minute & 15) + 10 * ((minute >> 4) & 7); // 0-59 - output.second = (second & 15) + 10 * ((second >> 4) & 7); // 0-59 + output.year = util::decodeBCD(year); // 0-99 + output.month = util::decodeBCD(month); // 1-12 + output.day = util::decodeBCD(day); // 1-31 + output.hour = util::decodeBCD(hour); // 0-23 + output.minute = util::decodeBCD(minute); // 0-59 + output.second = util::decodeBCD(second); // 0-59 output.year += (output.year < 70) ? 2000 : 1900; } @@ -171,15 +172,13 @@ void getRTCTime(util::Date &output) { void setRTCTime(const util::Date &value, bool stop) { assert((value.year >= 1970) && (value.year <= 2069)); - int _year = value.year % 100; int weekday = value.getDayOfWeek() + 1; - - int year = (_year % 10) | (((_year / 10) & 15) << 4); - int month = (value.month % 10) | (((value.month / 10) & 1) << 4); - int day = (value.day % 10) | (((value.day / 10) & 3) << 4); - int hour = (value.hour % 10) | (((value.hour / 10) & 3) << 4); - int minute = (value.minute % 10) | (((value.minute / 10) & 7) << 4); - int second = (value.second % 10) | (((value.second / 10) & 7) << 4); + int year = util::encodeBCD(value.year % 100); + int month = util::encodeBCD(value.month); + int day = util::encodeBCD(value.day); + int hour = util::encodeBCD(value.hour); + int minute = util::encodeBCD(value.minute); + int second = util::encodeBCD(value.second); SYS573_RTC_CTRL |= SYS573_RTC_CTRL_WRITE; diff --git a/src/common/mdec.cpp b/src/common/mdec.cpp index f339794..67f0ca9 100644 --- a/src/common/mdec.cpp +++ b/src/common/mdec.cpp @@ -67,6 +67,10 @@ static constexpr int _DMA_CHUNK_SIZE_OUT = 32; static constexpr int _DMA_TIMEOUT = 100000; void init(void) { + DMA_DPCR |= 0 + | (DMA_DPCR_ENABLE << (DMA_MDEC_IN * 4)) + | (DMA_DPCR_ENABLE << (DMA_MDEC_OUT * 4)); + MDEC1 = MDEC_CTRL_RESET; MDEC1 = MDEC_CTRL_DMA_OUT | MDEC_CTRL_DMA_IN; diff --git a/src/common/rom.cpp b/src/common/rom.cpp index 7ce1edc..06307d3 100644 --- a/src/common/rom.cpp +++ b/src/common/rom.cpp @@ -16,7 +16,6 @@ #include #include -#include "common/util/hash.hpp" #include "common/util/log.hpp" #include "common/util/templates.hpp" #include "common/io.hpp" @@ -417,22 +416,6 @@ static const ShellInfo _KONAMI_SHELLS[]{ } }; -bool SonyKernelHeader::validateMagic(void) const { - return ( - util::hash(magic, sizeof(magic)) == "Sony Computer Entertainment Inc."_h - ); -} - -bool OpenBIOSHeader::validateMagic(void) const { - return (util::hash(magic, sizeof(magic)) == "OpenBIOS"_h); -} - -bool ShellInfo::validateHash(void) const { - return (util::hash( - reinterpret_cast(header), sizeof(util::ExecutableHeader) - ) == headerHash); -} - bool getShellInfo(ShellInfo &output) { for (auto &shell : _KONAMI_SHELLS) { if (!shell.validateHash()) diff --git a/src/common/rom.hpp b/src/common/rom.hpp index eb1c4ae..9c1a98d 100644 --- a/src/common/rom.hpp +++ b/src/common/rom.hpp @@ -21,6 +21,7 @@ #include "common/util/hash.hpp" #include "common/util/misc.hpp" #include "common/util/string.hpp" +#include "common/util/templates.hpp" #include "ps1/registers.h" namespace rom { @@ -117,20 +118,28 @@ public: uint32_t flags; uint8_t magic[32], _pad[4], version[36]; - bool validateMagic(void) const; + inline bool validateMagic(void) const { + return ( + util::hash(magic, sizeof(magic)) == + "Sony Computer Entertainment Inc."_h + ); + } + + }; class OpenBIOSHeader { public: - uint8_t magic[8]; + uint32_t magic[2]; uint32_t idNameLength, idDescLength, idType; uint8_t idData[24]; + inline bool validateMagic(void) const { + return (magic[0] == "Open"_c) && (magic[1] == "BIOS"_c); + } inline size_t getBuildID(char *output) const { return util::hexToString(output, &idData[idNameLength], idDescLength); } - - bool validateMagic(void) const; }; class ShellInfo { @@ -140,7 +149,12 @@ public: const util::ExecutableHeader *header; - bool validateHash(void) const; + inline bool validateHash(void) const { + return (util::hash( + reinterpret_cast(header), + sizeof(util::ExecutableHeader) + ) == headerHash); + } }; static const auto &sonyKernelHeader = diff --git a/src/common/spu.cpp b/src/common/spu.cpp index 0449021..d79bc58 100644 --- a/src/common/spu.cpp +++ b/src/common/spu.cpp @@ -51,6 +51,7 @@ void init(void) { | (9 << 16) // Number of address lines | (0 << 24) // DMA read/write delay | BIU_CTRL_DMA_DELAY; + DMA_DPCR |= DMA_DPCR_ENABLE << (DMA_SPU * 4); SPU_CTRL = 0; _waitForStatus(0x3f, 0); diff --git a/src/common/storage/device.hpp b/src/common/storage/device.hpp index 6d09214..165c539 100644 --- a/src/common/storage/device.hpp +++ b/src/common/storage/device.hpp @@ -23,6 +23,48 @@ namespace storage { +/* CD-ROM definitions */ + +static constexpr uint32_t CDROM_TOC_PREGAP = 150; + +class MSF { +public: + uint8_t minute, second, frame; + + inline void fromLBA(uint32_t lba) { + lba += CDROM_TOC_PREGAP; + + minute = lba / 4500; + second = (lba / 75) % 60; + frame = lba % 75; + } + inline uint32_t toLBA(void) const { + return -CDROM_TOC_PREGAP + + minute * 4500 + + second * 75 + + frame; + } +}; + +class BCDMSF { +public: + uint8_t minute, second, frame; + + inline void fromLBA(uint32_t lba) { + lba += CDROM_TOC_PREGAP; + + minute = util::encodeBCD(lba / 4500); + second = util::encodeBCD((lba / 75) % 60); + frame = util::encodeBCD(lba % 75); + } + inline uint32_t toLBA(void) const { + return -CDROM_TOC_PREGAP + + util::decodeBCD(minute) * 4500 + + util::decodeBCD(second) * 75 + + util::decodeBCD(frame); + } +}; + /* IDE register definitions */ enum IDECS0Register { diff --git a/src/common/util/templates.hpp b/src/common/util/templates.hpp index bd75e57..b33e9f6 100644 --- a/src/common/util/templates.hpp +++ b/src/common/util/templates.hpp @@ -91,6 +91,8 @@ template static constexpr inline size_t countOf(T &array) { return sizeof(array) / sizeof(array[0]); } +/* Concatenation and BCD conversion */ + static constexpr inline uint16_t concat2(uint8_t low, uint8_t high) { return low | (high << 8); } @@ -130,6 +132,20 @@ static constexpr inline uint64_t concat8( | (uint64_t(g) << 48) | (uint64_t(h) << 56); } +static constexpr inline uint8_t encodeBCD(uint8_t value) { + // output = units + tens * 16 + // = units + tens * 10 + tens * 6 + // = value + tens * 6 + return value + (value / 10) * 6; +} + +static constexpr inline uint8_t decodeBCD(uint8_t value) { + // output = low + high * 10 + // = low + high * 16 - high * 6 + // = value - high * 6 + return value - (value >> 4) * 6; +} + /* Simple "smart" pointer */ class Data { diff --git a/tools/common/gamedb.py b/tools/common/gamedb.py index ff5036a..31c079a 100644 --- a/tools/common/gamedb.py +++ b/tools/common/gamedb.py @@ -18,7 +18,7 @@ from collections.abc import Mapping from dataclasses import dataclass from enum import IntEnum, IntFlag from struct import Struct -from typing import Any, Self +from typing import Any from .util import JSONGroupedObject @@ -57,7 +57,7 @@ class CartPCBType(IntEnum): CART_PWB0000088954 = 13 @staticmethod - def fromJSONObject(obj: str) -> Self: + def fromJSONObject(obj: str): return { "unknown-x76f041": CartPCBType.CART_UNKNOWN_X76F041, "unknown-x76f041-ds2401": CartPCBType.CART_UNKNOWN_X76F041_DS2401, @@ -107,7 +107,7 @@ class HeaderFlag(IntFlag): REGION_LOWERCASE = 1 << 7 @staticmethod - def fromJSONObject(obj: Mapping[str, Any]) -> Self: + def fromJSONObject(obj: Mapping[str, Any]): flags: HeaderFlag = HeaderFlag(0) flags |= { @@ -167,7 +167,7 @@ class ChecksumFlag(IntFlag): CHECKSUM_FORCE_GX_SPEC = 1 << 5 @staticmethod - def fromJSONObject(obj: Mapping[str, Any]) -> Self: + def fromJSONObject(obj: Mapping[str, Any]): flags: ChecksumFlag = ChecksumFlag(0) flags |= { @@ -221,7 +221,7 @@ class IdentifierFlag(IntFlag): PUBLIC_XID_PRESENT = 1 << 7 @staticmethod - def fromJSONObject(obj: Mapping[str, Any]) -> Self: + def fromJSONObject(obj: Mapping[str, Any]): flags: IdentifierFlag = IdentifierFlag(0) flags |= { @@ -274,7 +274,7 @@ class SignatureFlag(IntFlag): SIGNATURE_PAD_WITH_FF = 1 << 2 @staticmethod - def fromJSONObject(obj: Mapping[str, Any]) -> Self: + def fromJSONObject(obj: Mapping[str, Any]): flags: SignatureFlag = SignatureFlag(0) flags |= { @@ -316,7 +316,7 @@ class IOBoardType(IntEnum): IO_BOARD_GUNMANIA = 6 @staticmethod - def fromJSONObject(obj: str | None) -> Self: + def fromJSONObject(obj: str | None): return { None: IOBoardType.IO_BOARD_NONE, "GX700-PWB(F)": IOBoardType.IO_BOARD_ANALOG, @@ -347,7 +347,7 @@ class PCMCIADeviceType(IntEnum): PCMCIA_FLASH_CARD_64 = 5 @staticmethod - def fromJSONObject(obj: str | None) -> Self: + def fromJSONObject(obj: str | None): return { None: PCMCIADeviceType.PCMCIA_NONE, "PWB0000100991": PCMCIADeviceType.PCMCIA_NETWORK_PCB, @@ -372,7 +372,7 @@ class GameFlag(IntFlag): GAME_RTC_HEADER_REQUIRED = 1 << 1 @staticmethod - def fromJSONObject(obj: Mapping[str, Any]) -> Self: + def fromJSONObject(obj: Mapping[str, Any]): flags: GameFlag = GameFlag(0) for key, flag in { @@ -412,7 +412,7 @@ class ROMHeaderInfo: signatureFlags: SignatureFlag @staticmethod - def fromJSONObject(obj: Mapping[str, Any]) -> Self: + def fromJSONObject(obj: Mapping[str, Any]): return ROMHeaderInfo( bytes.fromhex(obj.get("signatureField", "").replace("-", " ")), bytes.fromhex(obj.get("yearField", "").replace("-", " ")), @@ -457,7 +457,7 @@ class CartInfo: idFlags: IdentifierFlag @staticmethod - def fromJSONObject(obj: Mapping[str, Any]) -> Self: + def fromJSONObject(obj: Mapping[str, Any]): return CartInfo( CartPCBType.fromJSONObject(obj["pcb"]), @@ -523,7 +523,7 @@ class GameInfo: gameCart: CartInfo | None = None @staticmethod - def fromJSONObject(obj: Mapping[str, Any]) -> Self: + def fromJSONObject(obj: Mapping[str, Any]): rtcHeader: Mapping[str, Any] | None = obj.get("rtcHeader", None) flashHeader: Mapping[str, Any] | None = obj.get("flashHeader", None) installCart: Mapping[str, Any] | None = obj.get("installCart", None)