diff options
| author | crediar <crediar@rypp.net> | 2025-07-16 19:55:59 +0200 |
|---|---|---|
| committer | Jordan Woyak <jordan.woyak@gmail.com> | 2026-02-15 20:14:14 -0600 |
| commit | 2c62214875e4d409136229421c44aa8b88fbb635 (patch) | |
| tree | 43d2e45b6e763fcf2f7ab0fbb8a6d474dd2e7e6f /Source/Core/DiscIO | |
| parent | d6b5af57d01a04918e9ec849689b6d39b8e33591 (diff) | |
Added Triforce support
Diffstat (limited to 'Source/Core/DiscIO')
| -rw-r--r-- | Source/Core/DiscIO/Enums.h | 9 | ||||
| -rw-r--r-- | Source/Core/DiscIO/Volume.h | 1 | ||||
| -rw-r--r-- | Source/Core/DiscIO/VolumeDisc.cpp | 107 | ||||
| -rw-r--r-- | Source/Core/DiscIO/VolumeDisc.h | 24 | ||||
| -rw-r--r-- | Source/Core/DiscIO/VolumeGC.cpp | 33 | ||||
| -rw-r--r-- | Source/Core/DiscIO/VolumeGC.h | 14 |
6 files changed, 160 insertions, 28 deletions
diff --git a/Source/Core/DiscIO/Enums.h b/Source/Core/DiscIO/Enums.h index 9b5e274de9..62ad9e846a 100644 --- a/Source/Core/DiscIO/Enums.h +++ b/Source/Core/DiscIO/Enums.h @@ -19,7 +19,7 @@ enum class Platform WiiDisc, WiiWAD, ELFOrDOL, - NumberOfPlatforms + NumberOfPlatforms, }; enum class Country @@ -38,7 +38,7 @@ enum class Country Taiwan, World, Unknown, - NumberOfCountries + NumberOfCountries, }; // This numbering matches Nintendo's GameCube/Wii region numbering. @@ -48,7 +48,8 @@ enum class Region NTSC_U = 1, // Mainly North America PAL = 2, // Mainly Europe and Oceania Unknown = 3, // Nintendo uses this to mean region free, but we also use it for unknown regions - NTSC_K = 4 // South Korea (Wii only) + NTSC_K = 4, // South Korea (Wii only) + DEV = 5, // Triforce has no regions }; // Languages 0 - 9 match Nintendo's Wii language numbering. @@ -66,7 +67,7 @@ enum class Language SimplifiedChinese = 7, // Not selectable on any unmodded retail Wii TraditionalChinese = 8, // Not selectable on any unmodded retail Wii Korean = 9, - Unknown + Unknown, }; std::string GetName(Country country, bool translate); diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 3f80cd2e3d..3d161811e4 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -96,7 +96,6 @@ public: } virtual std::string GetGameID(const Partition& partition = PARTITION_NONE) const = 0; virtual std::string GetGameTDBID(const Partition& partition = PARTITION_NONE) const = 0; - virtual std::string GetTriforceID() const { return ""; } virtual std::string GetMakerID(const Partition& partition = PARTITION_NONE) const = 0; virtual std::optional<u16> GetRevision(const Partition& partition = PARTITION_NONE) const = 0; virtual std::string GetInternalName(const Partition& partition = PARTITION_NONE) const = 0; diff --git a/Source/Core/DiscIO/VolumeDisc.cpp b/Source/Core/DiscIO/VolumeDisc.cpp index 882bd3ac96..a915df1224 100644 --- a/Source/Core/DiscIO/VolumeDisc.cpp +++ b/Source/Core/DiscIO/VolumeDisc.cpp @@ -12,6 +12,7 @@ #include "DiscIO/DiscUtils.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" +#include "DiscIO/VolumeGC.h" namespace DiscIO { @@ -19,6 +20,55 @@ std::string VolumeDisc::GetGameID(const Partition& partition) const { char id[6]; + if (GetVolumeType() == Platform::Triforce) + { + // Triforce games have their Game ID stored in the boot.id file + const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID(); + + // Construct game ID from the BTID + id[0] = 'G'; + + memcpy(id + 1, boot_id->gameId + 2, 2); + + switch (boot_id->regionFlags) + { + default: + case 0x02: // JAPAN + id[3] = 'J'; + break; + case 0x08: // ASIA + id[3] = 'W'; + break; + case 0x0E: // USA + id[3] = 'E'; + break; + case 0x0C: // EXPORT + id[3] = 'P'; + break; + } + + // There only seem to be three different makers here, + // so we can just check the first char to difference between them. + // + // NAMCO CORPORATION, SEGA CORPORATION and Hitmaker co,ltd. + + switch (boot_id->manufacturer[0]) + { + case 'N': // NAMCO CORPORATION + id[4] = '8'; + id[5] = '2'; + break; + default: + case 'H': // Hitmaker co,ltd. + case 'S': // SEGA CORPORATION + id[4] = '6'; + id[5] = 'E'; + break; + } + + return DecodeString(id); + } + if (!Read(0, sizeof(id), reinterpret_cast<u8*>(id), partition)) return std::string(); @@ -27,8 +77,35 @@ std::string VolumeDisc::GetGameID(const Partition& partition) const Country VolumeDisc::GetCountry(const Partition& partition) const { - // The 0 that we use as a default value is mapped to Country::Unknown and Region::Unknown - const u8 country_byte = ReadSwapped<u8>(3, partition).value_or(0); + u8 country_byte = 0; + + if (GetVolumeType() == Platform::Triforce) + { + const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID(); + + switch (boot_id->regionFlags) + { + default: + case 0x02: // JAPAN + country_byte = 'J'; + break; + case 0x08: // ASIA + country_byte = 'W'; + break; + case 0x0E: // USA + country_byte = 'E'; + break; + case 0x0C: // EXPORT + country_byte = 'P'; + break; + } + } + else + { + // The 0 that we use as a default value is mapped to Country::Unknown and Region::Unknown + country_byte = ReadSwapped<u8>(3, partition).value_or(0); + } + const Region region = GetRegion(); const std::optional<u16> revision = GetRevision(); @@ -51,6 +128,24 @@ std::string VolumeDisc::GetMakerID(const Partition& partition) const { char maker_id[2]; + // Triforce games have their maker stored in the boot.id file as an actual string + if (GetVolumeType() == Platform::Triforce) + { + const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID(); + + switch (boot_id->manufacturer[0]) + { + case 'S': // SEGA CORPORATION + case 'H': // Hitmaker co,ltd + return DecodeString("6E"); + case 'N': // NAMCO CORPORATION + return DecodeString("82"); + default: + break; + } + // Fall back to normal maker from header + } + if (!Read(0x4, sizeof(maker_id), reinterpret_cast<u8*>(&maker_id), partition)) return std::string(); @@ -66,6 +161,14 @@ std::optional<u16> VolumeDisc::GetRevision(const Partition& partition) const std::string VolumeDisc::GetInternalName(const Partition& partition) const { char name[0x60]; + + // Triforce games have their Title stored in the boot.id file + if (GetVolumeType() == Platform::Triforce) + { + const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID(); + return DecodeString(boot_id->gameName); + } + if (!Read(0x20, sizeof(name), reinterpret_cast<u8*>(&name), partition)) return std::string(); diff --git a/Source/Core/DiscIO/VolumeDisc.h b/Source/Core/DiscIO/VolumeDisc.h index 17c76ae689..f263c40c16 100644 --- a/Source/Core/DiscIO/VolumeDisc.h +++ b/Source/Core/DiscIO/VolumeDisc.h @@ -15,6 +15,30 @@ namespace DiscIO class VolumeDisc : public Volume { public: + struct BootID + { + u32 magic; // 0x00 (Always BTID) + uint32_t unknown0[3]; + uint32_t unknown1[4]; + char mediaboardType[4]; // 0x20 (GCAM for Triforce) + uint32_t unknown2; + uint16_t year; // 0x28 + uint8_t month; // 0x2A + uint8_t day; // 0x2B + uint8_t videoMode; // 0x2C unknown bitmask, resolutions + horizontal/vertical + uint8_t unknown3; + uint8_t type3Compatible; // 0x2E (Type-3 compatible titles have this set to 1) + uint8_t unknown4; + char gameId[8]; // 0x30 + uint32_t regionFlags; // 0x38 + uint32_t unknown6[9]; + char manufacturer[0x20]; // 0x60 + char gameName[0x20]; // 0x80 + char gameExecutable[0x20]; // 0xA0 + char testExecutable[0x20]; // 0xC0 + char creditTypes[8][0x20]; // 0xE0 + }; + std::string GetGameID(const Partition& partition = PARTITION_NONE) const override; Country GetCountry(const Partition& partition = PARTITION_NONE) const override; std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override; diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index ad3d8ec74e..52dacd59a4 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -45,13 +45,11 @@ VolumeGC::VolumeGC(std::unique_ptr<BlobReader> reader) std::unique_ptr<FileInfo> file_info = tmp_fs->FindFileInfo("boot.id"); if (!file_info) return; - BootID triforce_header; const u64 file_size = ReadFile(*this, PARTITION_NONE, file_info.get(), - reinterpret_cast<u8*>(&triforce_header), sizeof(BootID)); - if (file_size >= 4 && triforce_header.magic == BTID_MAGIC) + reinterpret_cast<u8*>(&m_triforce_header), sizeof(BootID)); + if (file_size >= 4 && m_triforce_header.magic == BTID_MAGIC) { m_is_triforce = true; - m_triforce_id = triforce_header.id; } } } @@ -90,16 +88,23 @@ std::string VolumeGC::GetGameTDBID(const Partition& partition) const return GetGameID(partition); } -std::string VolumeGC::GetTriforceID() const +Region VolumeGC::GetRegion() const { if (m_is_triforce) - return (std::string(m_triforce_id.data(), m_triforce_id.size())); - else - return ""; -} + { + switch (m_triforce_header.regionFlags) + { + default: + case 0x02: // JAPAN + case 0x08: // ASIA + return Region::NTSC_J; + case 0x0E: // USA + return Region::NTSC_U; + case 0x0C: // EXPORT + return Region::PAL; + } + } -Region VolumeGC::GetRegion() const -{ return RegionCodeToRegion(m_reader->ReadSwapped<u32>(0x458)); } @@ -184,6 +189,12 @@ std::array<u8, 20> VolumeGC::GetSyncHash() const VolumeGC::ConvertedGCBanner VolumeGC::LoadBannerFile() const { + // There is at least one Triforce game that has an opening.bnr file but from a different game. + if (m_is_triforce) + { + return {}; + } + GCBanner banner_file; const u64 file_size = ReadFile(*this, PARTITION_NONE, "opening.bnr", reinterpret_cast<u8*>(&banner_file), sizeof(GCBanner)); diff --git a/Source/Core/DiscIO/VolumeGC.h b/Source/Core/DiscIO/VolumeGC.h index ff768c2e2a..2f85a75535 100644 --- a/Source/Core/DiscIO/VolumeGC.h +++ b/Source/Core/DiscIO/VolumeGC.h @@ -34,7 +34,6 @@ public: const Partition& partition = PARTITION_NONE) const override; const FileSystem* GetFileSystem(const Partition& partition = PARTITION_NONE) const override; std::string GetGameTDBID(const Partition& partition = PARTITION_NONE) const override; - std::string GetTriforceID() const override; std::map<Language, std::string> GetShortNames() const override; std::map<Language, std::string> GetLongNames() const override; std::map<Language, std::string> GetShortMakers() const override; @@ -50,9 +49,10 @@ public: DataSizeType GetDataSizeType() const override; u64 GetRawSize() const override; const BlobReader& GetBlobReader() const override; - std::array<u8, 20> GetSyncHash() const override; + const BootID* GetTriforceBootID() const { return &m_triforce_header; } + private: static constexpr u32 GC_BANNER_WIDTH = 96; static constexpr u32 GC_BANNER_HEIGHT = 32; @@ -77,13 +77,6 @@ private: // (only one for BNR1 type) }; - struct BootID - { - u32 magic; // "BTID" - u32 padding[11]; - std::array<char, 4> id; - }; - struct ConvertedGCBanner { ConvertedGCBanner(); @@ -112,8 +105,9 @@ private: std::unique_ptr<BlobReader> m_reader; + BootID m_triforce_header; + bool m_is_triforce; - std::array<char, 4> m_triforce_id; }; } // namespace DiscIO |
