This commit is contained in:
Maximilian Downey Twiss 2025-04-19 19:14:41 +01:00 committed by GitHub
commit 8820a45466
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 296 additions and 98 deletions

View File

@ -932,10 +932,6 @@
"title": "Region Select",
"AD12237401ABE9FE4A545AADB5C5AE10355E2076": "RSAPatch"
},
"RELJAB": {
"title": "SegaBoot",
"130F3594CAB57B85616F95C7126F4748AAC5867D": "DI Seed Blanker"
},
"RGQE70": {
"title": "Ghostbusters",
"5F4CF8D4DA19A0FF74FF9EB925AC0236069BFD59": "crashfix"

View File

@ -1,4 +1,5 @@
# GVSE8P, GVSP8P - Virtua Striker 3 ver. 2002
# GVSP8P, GVSE8P, GVSJ8P - Virtua Striker 2002 (GC)
# GVSJ8P - Virtua Striker 2002 (Triforce), Virtua Striker 4, Virtua Striker 4 ver. 2006
[Core]
# Values set here will override the main Dolphin settings.
@ -10,3 +11,7 @@ FPRF = True
[ActionReplay]
# Add action replay cheats here.
[Video]
# Add memory patches to be applied every frame here.
[Video_Settings]

View File

@ -1,18 +0,0 @@
# GVS46E, GVS46J - Virtua Striker 4 ver. 2006
# Because Triforce games have weird IDs, properties are inherited from GVS.ini (Virtua Striker 3 ver. 2002)!
[Core]
# Values set here will override the main Dolphin settings.
FPRF = True
[OnFrame]
# Add memory patches to be applied every frame here.
[ActionReplay]
# Add action replay cheats here.
[Video]
# Add memory patches to be applied every frame here.
[Video_Settings]

View File

@ -1,18 +0,0 @@
# GVS46E, GVS46J - Virtua Striker 4 ver. 2006
# Because Triforce games have weird IDs, properties are inherited from GVS.ini (Virtua Striker 3 ver. 2002)!
[Core]
# Values set here will override the main Dolphin settings.
FPRF = True
[OnFrame]
# Add memory patches to be applied every frame here.
[ActionReplay]
# Add action replay cheats here.
[Video]
# Add memory patches to be applied every frame here.
[Video_Settings]

View File

@ -1,18 +0,0 @@
# RELJAB - SegaBoot
[Core]
# Values set here will override the main Dolphin settings.
[OnFrame]
# Add memory patches to be applied every frame here.
$DI Seed Blanker
0x80000000:dword:0x00000000
0x80000004:dword:0x00000000
0x80000008:dword:0x00000000
[Patches_RetroAchievements_Verified]
$DI Seed Blanker
[ActionReplay]
# Add action replay cheats here.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

10
Data/Sys/tritdb-en.txt Normal file
View File

@ -0,0 +1,10 @@
TITLES = (type: Triforce language: EN version: 20210908)
S000 = Triforce Firmware Update For Compact Flash Box (4.01)
SBEY = Virtua Striker 2002
SBFX = The Key Of Avalon
SBGG = F-Zero AX
SBHZ = Virtua Striker 4 (Asia)
SBJA = Virtua Striker 4 (Export)
SBJN = The Key Of Avalon 2.5: War of the Key
SBLK = Virtua Striker 4 Ver.2006 (Japan)
SBLL = Virtua Striker 4 Ver.2006 (Export)

View File

@ -30,8 +30,8 @@ import java.util.Set;
public final class FileBrowserHelper
{
public static final HashSet<String> GAME_EXTENSIONS = new HashSet<>(Arrays.asList(
"gcm", "tgc", "iso", "ciso", "gcz", "wbfs", "wia", "rvz", "nfs", "wad", "dol", "elf",
"json"));
"gcm", "tgc", "bin", "iso", "ciso", "gcz", "wbfs", "wia", "rvz", "nfs", "wad", "dol",
"elf", "json"));
public static final HashSet<String> GAME_LIKE_EXTENSIONS = new HashSet<>(GAME_EXTENSIONS);

View File

@ -236,7 +236,8 @@ std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(std::vector<std
#endif
static const std::unordered_set<std::string> disc_image_extensions = {
{".gcm", ".iso", ".tgc", ".wbfs", ".ciso", ".gcz", ".wia", ".rvz", ".nfs", ".dol", ".elf"}};
{".gcm", ".bin", ".iso", ".tgc", ".wbfs", ".ciso", ".gcz", ".wia", ".rvz", ".nfs", ".dol",
".elf"}};
if (disc_image_extensions.contains(extension))
{
std::unique_ptr<DiscIO::VolumeDisc> disc = DiscIO::CreateDisc(path);
@ -406,6 +407,7 @@ bool CBoot::Load_BS2(Core::System& system, const std::string& boot_rom_filename)
constexpr u32 PAL_v1_0 = 0x4F319F43;
// DOL-101(EUR) (PAL Revision 1.2)
constexpr u32 PAL_v1_2 = 0xAD1B7F16;
constexpr u32 Triforce = 0xD1883221; // The Triforce's special IPL
// Load the IPL ROM dump, limited to 2MiB which is the size of the official IPLs.
constexpr size_t max_ipl_size = 2 * 1024 * 1024;
@ -423,6 +425,7 @@ bool CBoot::Load_BS2(Core::System& system, const std::string& boot_rom_filename)
const u32 ipl_hash = Common::ComputeCRC32(data.data(), data.size());
bool known_ipl = false;
bool pal_ipl = false;
bool triforce_ipl = false;
switch (ipl_hash)
{
case NTSC_v1_0:
@ -437,6 +440,9 @@ bool CBoot::Load_BS2(Core::System& system, const std::string& boot_rom_filename)
pal_ipl = true;
known_ipl = true;
break;
case Triforce:
known_ipl = true;
triforce_ipl = true;
default:
PanicAlertFmtT("The IPL file is not a known good dump. (CRC32: {0:x})", ipl_hash);
break;

View File

@ -227,6 +227,13 @@ bool CBoot::RunApploader(Core::System& system, const Core::CPUThreadGuard& guard
ppc_state.pc = ppc_state.gpr[3];
branch_watch.SetRecordingActive(guard, resume_branch_watch);
// Blank out session key (https://debugmo.de/2008/05/part-2-dumping-the-media-board/)
if (volume.GetVolumeType() == DiscIO::Platform::Triforce)
{
auto& memory = system.GetMemory();
memory.Memset(0, 0, 12);
}
return true;
}

View File

@ -203,6 +203,8 @@ add_library(core
HW/EXI/EXI_DeviceAD16.h
HW/EXI/EXI_DeviceAGP.cpp
HW/EXI/EXI_DeviceAGP.h
HW/EXI/EXI_DeviceBaseboard.cpp
HW/EXI/EXI_DeviceBaseboard.h
HW/EXI/EXI_DeviceDummy.cpp
HW/EXI/EXI_DeviceDummy.h
HW/EXI/EXI_DeviceEthernet.cpp

View File

@ -437,6 +437,7 @@ const Info<bool> MAIN_GAMELIST_LIST_WAD{{System::Main, "GameList", "ListWad"}, t
const Info<bool> MAIN_GAMELIST_LIST_ELF_DOL{{System::Main, "GameList", "ListElfDol"}, true};
const Info<bool> MAIN_GAMELIST_LIST_WII{{System::Main, "GameList", "ListWii"}, true};
const Info<bool> MAIN_GAMELIST_LIST_GC{{System::Main, "GameList", "ListGC"}, true};
const Info<bool> MAIN_GAMELIST_LIST_TRI{{System::Main, "GameList", "ListTriforce"}, true};
const Info<bool> MAIN_GAMELIST_LIST_JPN{{System::Main, "GameList", "ListJap"}, true};
const Info<bool> MAIN_GAMELIST_LIST_PAL{{System::Main, "GameList", "ListPal"}, true};
const Info<bool> MAIN_GAMELIST_LIST_USA{{System::Main, "GameList", "ListUsa"}, true};

View File

@ -269,6 +269,7 @@ extern const Info<bool> MAIN_GAMELIST_LIST_WAD;
extern const Info<bool> MAIN_GAMELIST_LIST_ELF_DOL;
extern const Info<bool> MAIN_GAMELIST_LIST_WII;
extern const Info<bool> MAIN_GAMELIST_LIST_GC;
extern const Info<bool> MAIN_GAMELIST_LIST_TRI;
extern const Info<bool> MAIN_GAMELIST_LIST_JPN;
extern const Info<bool> MAIN_GAMELIST_LIST_PAL;
extern const Info<bool> MAIN_GAMELIST_LIST_USA;

View File

@ -126,6 +126,12 @@ const std::string SConfig::GetTitleDescription() const
return m_title_description;
}
std::string SConfig::GetTriID() const
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
return m_tri_id;
}
u64 SConfig::GetTitleID() const
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
@ -141,7 +147,7 @@ u16 SConfig::GetRevision() const
void SConfig::ResetRunningGameMetadata()
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown);
SetRunningGameMetadata("00000000", "", "", 0, 0, DiscIO::Region::Unknown);
}
void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume,
@ -150,14 +156,14 @@ void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume,
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
if (partition == volume.GetGamePartition())
{
SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(),
SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(), volume.GetTriID(),
volume.GetTitleID().value_or(0), volume.GetRevision().value_or(0),
volume.GetRegion());
}
else
{
SetRunningGameMetadata(volume.GetGameID(partition), volume.GetGameTDBID(partition),
volume.GetTitleID(partition).value_or(0),
volume.GetTriID(), volume.GetTitleID(partition).value_or(0),
volume.GetRevision(partition).value_or(0), volume.GetRegion());
}
}
@ -175,25 +181,27 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Plat
!Core::System::GetInstance().GetDVDInterface().UpdateRunningGameMetadata(tmd_title_id))
{
// If not launching a disc game, just read everything from the TMD.
SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(),
tmd.GetRegion());
SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), "", tmd_title_id,
tmd.GetTitleVersion(), tmd.GetRegion());
}
}
void SConfig::SetRunningGameMetadata(const std::string& game_id)
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
SetRunningGameMetadata(game_id, "", 0, 0, DiscIO::Region::Unknown);
SetRunningGameMetadata(game_id, "", "", 0, 0, DiscIO::Region::Unknown);
}
void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
u64 title_id, u16 revision, DiscIO::Region region)
std::string tri_id, u64 title_id, u16 revision,
DiscIO::Region region)
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id ||
m_title_id != title_id || m_revision != revision;
m_tri_id != tri_id || m_title_id != title_id || m_revision != revision;
m_game_id = game_id;
m_gametdb_id = gametdb_id;
m_tri_id = tri_id;
m_title_id = title_id;
m_revision = revision;
@ -226,7 +234,7 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
const Core::TitleDatabase title_database;
auto& system = Core::System::GetInstance();
const DiscIO::Language language = GetLanguageAdjustedForRegion(system.IsWii(), region);
m_title_name = title_database.GetTitleName(m_gametdb_id, language);
m_title_name = title_database.GetTitleName(m_gametdb_id, m_tri_id, language);
m_title_description = title_database.Describe(m_gametdb_id, language);
NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description);
Host_TitleChanged();
@ -298,6 +306,7 @@ void SConfig::LoadDefaults()
auto& system = Core::System::GetInstance();
system.SetIsWii(false);
system.SetIsTriforce(false);
ResetRunningGameMetadata();
}
@ -321,6 +330,7 @@ struct SetGameMetadata
{
*region = disc.volume->GetRegion();
system.SetIsWii(disc.volume->GetVolumeType() == DiscIO::Platform::WiiDisc);
system.SetIsTriforce(disc.volume->GetVolumeType() == DiscIO::Platform::Triforce);
config->m_disc_booted_from_game_list = true;
config->SetRunningGameMetadata(*disc.volume, disc.volume->GetGamePartition());
return true;

View File

@ -67,8 +67,10 @@ struct SConfig
const std::string GetGameTDBID() const;
const std::string GetTitleName() const;
const std::string GetTitleDescription() const;
std::string GetTriID() const;
u64 GetTitleID() const;
u16 GetRevision() const;
void ResetRunningGameMetadata();
void SetRunningGameMetadata(const DiscIO::Volume& volume, const DiscIO::Partition& partition);
void SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform);
@ -121,13 +123,15 @@ private:
static void ReloadTextures(Core::System& system);
void SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
u64 title_id, u16 revision, DiscIO::Region region);
std::string tri_id, u64 title_id, u16 revision,
DiscIO::Region region);
static SConfig* m_Instance;
mutable std::recursive_mutex m_metadata_lock;
std::string m_game_id;
std::string m_gametdb_id;
std::string m_tri_id;
std::string m_title_name;
std::string m_title_description;
u64 m_title_id;

View File

@ -753,6 +753,15 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type)
DIInterruptType interrupt_type = DIInterruptType::TCINT;
bool command_handled_by_thread = false;
// Swaps endian of Triforce DI commands, and zeroes out random bytes to prevent unknown read
// subcommand errors
auto& dvd_thread = m_system.GetDVDThread();
if (dvd_thread.GetDiscType() == DiscIO::Platform::Triforce)
{
// TODO(C++23): Use std::byteswap and a bitwise AND for increased clarity
m_DICMDBUF[0] <<= 24;
}
// DVDLowRequestError needs access to the error code set by the previous command
if (static_cast<DICommand>(m_DICMDBUF[0] >> 24) != DICommand::RequestError)
SetDriveError(DriveError::None);

View File

@ -66,6 +66,18 @@ void ExpansionInterfaceManager::AddMemoryCard(Slot slot)
m_channels[SlotToEXIChannel(slot)]->AddDevice(memorycard_device, SlotToEXIDevice(slot));
}
void ExpansionInterfaceManager::AddSP1Device()
{
EXIDeviceType sp1_device = EXIDeviceType::Baseboard;
auto& system = Core::System::GetInstance();
if (system.IsTriforce())
{
sp1_device = Config::Get(Config::MAIN_SERIAL_PORT_1);
}
m_channels[0]->AddDevice(sp1_device, SlotToEXIDevice(Slot::SP1));
}
u8 SlotToEXIChannel(Slot slot)
{
switch (slot)
@ -145,6 +157,7 @@ void ExpansionInterfaceManager::Init(const Sram* override_sram)
AddMemoryCard(slot);
m_channels[0]->AddDevice(EXIDeviceType::MaskROM, 1);
AddSP1Device();
m_channels[SlotToEXIChannel(Slot::SP1)]->AddDevice(Config::Get(Config::MAIN_SERIAL_PORT_1),
SlotToEXIDevice(Slot::SP1));
m_channels[SlotToEXIChannel(Slot::SP2)]->AddDevice(Config::Get(Config::MAIN_SERIAL_PORT_2),

View File

@ -89,6 +89,7 @@ public:
private:
void AddMemoryCard(Slot slot);
void AddSP1Device();
static void ChangeDeviceCallback(Core::System& system, u64 userdata, s64 cycles_late);
static void UpdateInterruptsCallback(Core::System& system, u64 userdata, s64 cycles_late);

View File

@ -9,6 +9,7 @@
#include "Common/MsgHandler.h"
#include "Core/HW/EXI/EXI_DeviceAD16.h"
#include "Core/HW/EXI/EXI_DeviceAGP.h"
#include "Core/HW/EXI/EXI_DeviceBaseboard.h"
#include "Core/HW/EXI/EXI_DeviceDummy.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
#include "Core/HW/EXI/EXI_DeviceGecko.h"
@ -171,7 +172,10 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(Core::System& system, const EXIDevi
result = std::make_unique<CEXIAgp>(system, slot);
break;
case EXIDeviceType::AMBaseboard:
case EXIDeviceType::Baseboard:
result = std::make_unique<CEXIBaseboard>(system);
break;
case EXIDeviceType::None:
default:
result = std::make_unique<IEXIDevice>(system);

View File

@ -30,9 +30,7 @@ enum class EXIDeviceType : int
AD16,
Microphone,
Ethernet,
// Was used for Triforce in the past, but the implementation is no longer in Dolphin.
// It's kept here so that values below will stay constant.
AMBaseboard,
Baseboard,
Gecko,
// Only used when creating a device by EXIDevice_Create.
// Converted to MemoryCard internally.
@ -98,7 +96,7 @@ struct fmt::formatter<ExpansionInterface::EXIDeviceType>
_trans("AD16"),
_trans("Microphone"),
_trans("Broadband Adapter (TAP)"),
_trans("Triforce AM Baseboard"),
_trans("Triforce Baseboard"),
_trans("USB Gecko"),
_trans("GCI Folder"),
_trans("Advance Game Port"),

View File

@ -0,0 +1,57 @@
// Copyright 2013 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HW/EXI/EXI_DeviceBaseboard.h"
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
namespace ExpansionInterface
{
CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system)
{
}
void CEXIBaseboard::SetCS(int cs)
{
if (cs)
m_position = 0;
}
bool CEXIBaseboard::IsPresent() const
{
return true;
}
void CEXIBaseboard::TransferByte(u8& byte)
{
if (m_position == 0)
{
m_command = byte;
}
else
{
switch (m_command)
{
case 0x00:
{
static constexpr std::array<u8, 4> ID = {0x06, 0x04, 0x10, 0x00};
byte = ID[(m_position - 2) & 3];
break;
}
default:
ERROR_LOG_FMT(EXPANSIONINTERFACE, "EXI BASEBOARD: Unhandled command {:#x} {:#x}", m_command,
m_position);
}
}
m_position++;
}
void CEXIBaseboard::DoState(PointerWrap& p)
{
p.Do(m_position);
p.Do(m_command);
}
} // namespace ExpansionInterface

View File

@ -0,0 +1,27 @@
// Copyright 2013 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Core/HW/EXI/EXI_Device.h"
class PointerWrap;
namespace ExpansionInterface
{
class CEXIBaseboard : public IEXIDevice
{
public:
CEXIBaseboard(Core::System& system);
void SetCS(int cs) override;
bool IsPresent() const override;
void DoState(PointerWrap& p) override;
private:
// STATE_TO_SAVE
u32 m_position = 0;
u32 m_command = 0;
void TransferByte(u8& byte) override;
};
} // namespace ExpansionInterface

View File

@ -99,7 +99,7 @@ static size_t s_state_writes_in_queue;
static std::condition_variable s_state_write_queue_is_empty;
// Don't forget to increase this after doing changes on the savestate system
constexpr u32 STATE_VERSION = 172; // Last changed in PR 13385
constexpr u32 STATE_VERSION = 173; // Last changed in PR 10084
// Increase this if the StateExtendedHeader definition changes
constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217

View File

@ -141,10 +141,12 @@ public:
bool IsMMUMode() const { return m_mmu_enabled; }
bool IsPauseOnPanicMode() const { return m_pause_on_panic_enabled; }
bool IsMIOS() const { return m_is_mios; }
bool IsTriforce() const { return m_is_triforce; }
bool IsWii() const { return m_is_wii; }
bool IsBranchWatchIgnoreApploader() { return m_branch_watch_ignore_apploader; }
void SetIsMIOS(bool is_mios) { m_is_mios = is_mios; }
void SetIsTriforce(bool is_triforce) { m_is_triforce = is_triforce; }
void SetIsWii(bool is_wii) { m_is_wii = is_wii; }
void SetIsBranchWatchIgnoreApploader(bool enable) { m_branch_watch_ignore_apploader = enable; }
@ -205,6 +207,7 @@ private:
bool m_mmu_enabled = false;
bool m_pause_on_panic_enabled = false;
bool m_is_mios = false;
bool m_is_triforce = false;
bool m_is_wii = false;
bool m_branch_watch_ignore_apploader = false;
};

View File

@ -56,6 +56,9 @@ void TitleDatabase::AddLazyMap(DiscIO::Language language, const std::string& lan
m_title_maps[language] = [language_code]() -> Map {
return LoadMap(File::GetSysDirectory() + "wiitdb-" + language_code + ".txt");
};
m_trititle_maps[language] = [language_code]() -> Map {
return LoadMap(File::GetSysDirectory() + "tritdb-" + language_code + ".txt");
};
}
TitleDatabase::TitleDatabase()
@ -91,8 +94,32 @@ TitleDatabase::TitleDatabase()
TitleDatabase::~TitleDatabase() = default;
const std::string& TitleDatabase::GetTitleName(const std::string& gametdb_id,
const std::string& tri_id,
DiscIO::Language language) const
{
if (tri_id != "")
{
const Map& map = *m_trititle_maps.at(DiscIO::Language::English);
auto it = map.find(tri_id);
if (it != map.end())
return it->second;
// This code has been commented out as there is currently only a english title map, and all
// Triforce games are detected as Japanese.
// if (language != DiscIO::Language::English)
//{
// const Map& english_trimap = *m_trititle_maps.at(DiscIO::Language::English);
// it = english_trimap.find(tri_id);
// if (it != english_trimap.end())
// return it->second;
//}
// it = m_base_map.find(tri_id);
// if (it != m_base_map.end())
// return it->second;
}
auto it = m_user_title_map.find(gametdb_id);
if (it != m_user_title_map.end())
return it->second;
@ -125,12 +152,12 @@ const std::string& TitleDatabase::GetChannelName(u64 title_id, DiscIO::Language
const std::string id{
{static_cast<char>((title_id >> 24) & 0xff), static_cast<char>((title_id >> 16) & 0xff),
static_cast<char>((title_id >> 8) & 0xff), static_cast<char>(title_id & 0xff)}};
return GetTitleName(id, language);
return GetTitleName(id, "", language);
}
std::string TitleDatabase::Describe(const std::string& gametdb_id, DiscIO::Language language) const
{
const std::string& title_name = GetTitleName(gametdb_id, language);
const std::string& title_name = GetTitleName(gametdb_id, "", language);
if (title_name.empty())
return gametdb_id;
return fmt::format("{} ({})", title_name, gametdb_id);

View File

@ -25,7 +25,8 @@ public:
// Get a user friendly title name for a GameTDB ID.
// This falls back to returning an empty string if none could be found.
const std::string& GetTitleName(const std::string& gametdb_id, DiscIO::Language language) const;
const std::string& GetTitleName(const std::string& gametdb_id, const std::string& tri_id,
DiscIO::Language language) const;
// Same as above, but takes a title ID instead of a GameTDB ID, and only works for channels.
const std::string& GetChannelName(u64 title_id, DiscIO::Language language) const;
@ -38,6 +39,8 @@ private:
std::unordered_map<DiscIO::Language, Common::Lazy<std::unordered_map<std::string, std::string>>>
m_title_maps;
std::unordered_map<DiscIO::Language, Common::Lazy<std::unordered_map<std::string, std::string>>>
m_trititle_maps;
std::unordered_map<std::string, std::string> m_base_map;
std::unordered_map<std::string, std::string> m_user_title_map;
};

View File

@ -139,7 +139,8 @@ std::string GetName(Region region, bool translate)
bool IsDisc(Platform volume_type)
{
return volume_type == Platform::GameCubeDisc || volume_type == Platform::WiiDisc;
return volume_type == Platform::GameCubeDisc || volume_type == Platform::Triforce ||
volume_type == Platform::WiiDisc;
}
bool IsWii(Platform volume_type)

View File

@ -15,6 +15,7 @@ namespace DiscIO
enum class Platform
{
GameCubeDisc = 0,
Triforce,
WiiDisc,
WiiWAD,
ELFOrDOL,

View File

@ -96,6 +96,7 @@ 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 GetTriID() 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;

View File

@ -29,7 +29,8 @@
namespace DiscIO
{
VolumeGC::VolumeGC(std::unique_ptr<BlobReader> reader) : m_reader(std::move(reader))
VolumeGC::VolumeGC(std::unique_ptr<BlobReader> reader)
: m_reader(std::move(reader)), m_is_triforce(false)
{
ASSERT(m_reader);
@ -39,6 +40,23 @@ VolumeGC::VolumeGC(std::unique_ptr<BlobReader> reader) : m_reader(std::move(read
};
m_converted_banner = [this] { return LoadBannerFile(); };
constexpr u32 BTID_MAGIC = 0x44495442;
auto tmp_fs = GetFileSystem(PARTITION_NONE);
if (tmp_fs)
{
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)
{
m_is_triforce = true;
m_tri_id = triforce_header.id;
}
}
}
VolumeGC::~VolumeGC()
@ -77,6 +95,14 @@ std::string VolumeGC::GetGameTDBID(const Partition& partition) const
return GetGameID(partition);
}
std::string VolumeGC::GetTriID() const
{
if (m_is_triforce)
return (std::string(m_tri_id.data(), m_tri_id.size()));
else
return "";
}
Region VolumeGC::GetRegion() const
{
return RegionCodeToRegion(m_reader->ReadSwapped<u32>(0x458));
@ -141,7 +167,10 @@ const BlobReader& VolumeGC::GetBlobReader() const
Platform VolumeGC::GetVolumeType() const
{
return Platform::GameCubeDisc;
if (m_is_triforce)
return Platform::Triforce;
else
return Platform::GameCubeDisc;
}
bool VolumeGC::IsDatelDisc() const

View File

@ -34,6 +34,7 @@ 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 GetTriID() 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;
@ -76,6 +77,13 @@ private:
// (only one for BNR1 type)
};
struct BootID
{
u32 magic; // "BTID"
u32 padding[11];
std::array<char, 4> id;
};
struct ConvertedGCBanner
{
ConvertedGCBanner();
@ -103,6 +111,9 @@ private:
Common::Lazy<std::unique_ptr<FileSystem>> m_file_system;
std::unique_ptr<BlobReader> m_reader;
bool m_is_triforce;
std::array<char, 4> m_tri_id;
};
} // namespace DiscIO

View File

@ -402,6 +402,7 @@ void VolumeVerifier::Start()
m_is_tgc = m_volume.GetBlobType() == BlobType::TGC;
m_is_datel = m_volume.IsDatelDisc();
m_is_triforce = m_volume.GetVolumeType() == Platform::Triforce;
m_is_not_retail = (m_volume.GetVolumeType() == Platform::WiiDisc && !m_volume.HasWiiHashes()) ||
IsDebugSigned();
@ -1373,6 +1374,13 @@ void VolumeVerifier::Finish()
return;
}
if (m_is_triforce)
{
m_result.summary_text =
Common::GetStringT("Dolphin is currently unable to verify Triforce games.");
return;
}
if (m_result.redump.status == RedumpVerifier::Status::BadDump &&
highest_severity <= Severity::Low)
{

View File

@ -165,6 +165,7 @@ private:
Result m_result;
bool m_is_tgc = false;
bool m_is_datel = false;
bool m_is_triforce = false;
bool m_is_not_retail = false;
bool m_redump_verification;

View File

@ -281,6 +281,7 @@
<ClInclude Include="Core\HW\EXI\EXI_Device.h" />
<ClInclude Include="Core\HW\EXI\EXI_DeviceAD16.h" />
<ClInclude Include="Core\HW\EXI\EXI_DeviceAGP.h" />
<ClInclude Include="Core\HW\EXI\EXI_DeviceBaseboard.h" />
<ClInclude Include="Core\HW\EXI\EXI_DeviceDummy.h" />
<ClInclude Include="Core\HW\EXI\EXI_DeviceEthernet.h" />
<ClInclude Include="Core\HW\EXI\EXI_DeviceGecko.h" />
@ -948,6 +949,7 @@
<ClCompile Include="Core\HW\EXI\EXI_Device.cpp" />
<ClCompile Include="Core\HW\EXI\EXI_DeviceAD16.cpp" />
<ClCompile Include="Core\HW\EXI\EXI_DeviceAGP.cpp" />
<ClCompile Include="Core\HW\EXI\EXI_DeviceBaseboard.cpp" />
<ClCompile Include="Core\HW\EXI\EXI_DeviceDummy.cpp" />
<ClCompile Include="Core\HW\EXI\EXI_DeviceEthernet.cpp" />
<ClCompile Include="Core\HW\EXI\EXI_DeviceGecko.cpp" />

View File

@ -93,6 +93,7 @@ QGroupBox* InfoWidget::CreateGameDetails()
const QString game_name = QString::fromStdString(m_game.GetInternalName());
bool is_disc_based = m_game.GetPlatform() == DiscIO::Platform::GameCubeDisc ||
m_game.GetPlatform() == DiscIO::Platform::Triforce ||
m_game.GetPlatform() == DiscIO::Platform::WiiDisc;
QLineEdit* internal_name =
@ -105,11 +106,13 @@ QGroupBox* InfoWidget::CreateGameDetails()
.arg(m_game.GetRevision()));
QString game_id_string = QString::fromStdString(m_game.GetGameID());
QString tri_id_string = QString::fromStdString(m_game.GetTriID());
if (const u64 title_id = m_game.GetTitleID())
game_id_string += QStringLiteral(" (%1)").arg(title_id, 16, 16, QLatin1Char('0'));
QLineEdit* game_id = CreateValueDisplay(game_id_string);
QLineEdit* tri_id = CreateValueDisplay(tri_id_string);
QLineEdit* country = CreateValueDisplay(DiscIO::GetName(m_game.GetCountry(), true));
@ -120,6 +123,8 @@ QGroupBox* InfoWidget::CreateGameDetails()
m_game.GetMakerID() + ")");
layout->addRow(tr("Name:"), internal_name);
if (m_game.GetPlatform() == DiscIO::Platform::Triforce)
layout->addRow(tr("Boot (Triforce) ID:"), tri_id);
layout->addRow(tr("Game ID:"), game_id);
layout->addRow(tr("Country:"), country);
layout->addRow(tr("Maker:"), maker);

View File

@ -303,6 +303,8 @@ bool GameListModel::ShouldDisplayGameListItem(int index) const
{
case DiscIO::Platform::GameCubeDisc:
return Config::Get(Config::MAIN_GAMELIST_LIST_GC);
case DiscIO::Platform::Triforce:
return Config::Get(Config::MAIN_GAMELIST_LIST_TRI);
case DiscIO::Platform::WiiDisc:
return Config::Get(Config::MAIN_GAMELIST_LIST_WII);
case DiscIO::Platform::WiiWAD:

View File

@ -22,13 +22,13 @@
// NOTE: Qt likes to be case-sensitive here even though it shouldn't be thus this ugly regex hack
static const QStringList game_filters{
QStringLiteral("*.[gG][cC][mM]"), QStringLiteral("*.[iI][sS][oO]"),
QStringLiteral("*.[tT][gG][cC]"), QStringLiteral("*.[cC][iI][sS][oO]"),
QStringLiteral("*.[gG][cC][zZ]"), QStringLiteral("*.[wW][bB][fF][sS]"),
QStringLiteral("*.[wW][iI][aA]"), QStringLiteral("*.[rR][vV][zZ]"),
QStringLiteral("hif_000000.nfs"), QStringLiteral("*.[wW][aA][dD]"),
QStringLiteral("*.[eE][lL][fF]"), QStringLiteral("*.[dD][oO][lL]"),
QStringLiteral("*.[jJ][sS][oO][nN]")};
QStringLiteral("*.[gG][cC][mM]"), QStringLiteral("*.[bB][iI][nN]"),
QStringLiteral("*.[iI][sS][oO]"), QStringLiteral("*.[tT][gG][cC]"),
QStringLiteral("*.[cC][iI][sS][oO]"), QStringLiteral("*.[gG][cC][zZ]"),
QStringLiteral("*.[wW][bB][fF][sS]"), QStringLiteral("*.[wW][iI][aA]"),
QStringLiteral("*.[rR][vV][zZ]"), QStringLiteral("hif_000000.nfs"),
QStringLiteral("*.[wW][aA][dD]"), QStringLiteral("*.[eE][lL][fF]"),
QStringLiteral("*.[dD][oO][lL]"), QStringLiteral("*.[jJ][sS][oO][nN]")};
GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
{

View File

@ -7,6 +7,7 @@
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>bin</string>
<string>ciso</string>
<string>dol</string>
<string>elf</string>

View File

@ -787,7 +787,7 @@ QStringList MainWindow::PromptFileNames()
QStringList paths = DolphinFileDialog::getOpenFileNames(
this, tr("Select a File"),
settings.value(QStringLiteral("mainwindow/lastdir"), QString{}).toString(),
QStringLiteral("%1 (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz "
QStringLiteral("%1 (*.elf *.dol *.gcm *.bin *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz "
"hif_000000.nfs *.wad *.dff *.m3u *.json);;%2 (*)")
.arg(tr("All GC/Wii files"))
.arg(tr("All Files")));

View File

@ -726,6 +726,7 @@ void MenuBar::AddShowPlatformsMenu(QMenu* view_menu)
static const QMap<QString, const Config::Info<bool>*> platform_map{
{tr("Show Wii"), &Config::MAIN_GAMELIST_LIST_WII},
{tr("Show GameCube"), &Config::MAIN_GAMELIST_LIST_GC},
{tr("Show Triforce"), &Config::MAIN_GAMELIST_LIST_TRI},
{tr("Show WAD"), &Config::MAIN_GAMELIST_LIST_WAD},
{tr("Show ELF/DOL"), &Config::MAIN_GAMELIST_LIST_ELF_DOL}};

View File

@ -80,7 +80,7 @@ void Resources::Init()
m_svg_supported = QImageReader::supportedImageFormats().contains("svg");
for (std::string_view platform :
{"Platform_Gamecube", "Platform_Wii", "Platform_Wad", "Platform_File"})
{"Platform_Gamecube", "Platform_Triforce", "Platform_Wii", "Platform_Wad", "Platform_File"})
{
m_platforms.append(GetResourceIcon(platform));
}

View File

@ -45,7 +45,7 @@ void PathPane::BrowseDefaultGame()
{
QString file = QDir::toNativeSeparators(DolphinFileDialog::getOpenFileName(
this, tr("Select a Game"), Settings::Instance().GetDefaultGame(),
QStringLiteral("%1 (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz "
QStringLiteral("%1 (*.elf *.dol *.gcm *.bin *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz "
"hif_000000.nfs *.wad *.m3u *.json);;%2 (*)")
.arg(tr("All GC/Wii files"))
.arg(tr("All Files"))));

View File

@ -131,6 +131,7 @@ GameFile::GameFile(std::string path) : m_file_path(std::move(path))
m_internal_name = volume->GetInternalName();
m_game_id = volume->GetGameID();
m_gametdb_id = volume->GetGameTDBID();
m_tri_id = volume->GetTriID();
m_title_id = volume->GetTitleID().value_or(0);
m_maker_id = volume->GetMakerID();
m_revision = volume->GetRevision().value_or(0);
@ -311,6 +312,7 @@ void GameFile::DoState(PointerWrap& p)
p.Do(m_internal_name);
p.Do(m_game_id);
p.Do(m_gametdb_id);
p.Do(m_tri_id);
p.Do(m_title_id);
p.Do(m_maker_id);
@ -499,7 +501,8 @@ const std::string& GameFile::GetName(const Core::TitleDatabase& title_database)
if (IsModDescriptor())
return GetName(Variant::LongAndPossiblyCustom);
const std::string& database_name = title_database.GetTitleName(m_gametdb_id, GetConfigLanguage());
const std::string& database_name =
title_database.GetTitleName(m_gametdb_id, m_tri_id, GetConfigLanguage());
return database_name.empty() ? GetName(Variant::LongAndPossiblyCustom) : database_name;
}

View File

@ -76,6 +76,7 @@ public:
const std::string& GetInternalName() const { return m_internal_name; }
const std::string& GetGameID() const { return m_game_id; }
const std::string& GetGameTDBID() const { return m_gametdb_id; }
std::string GetTriID() const { return m_tri_id; }
u64 GetTitleID() const { return m_title_id; }
const std::string& GetMakerID() const { return m_maker_id; }
u16 GetRevision() const { return m_revision; }
@ -159,6 +160,7 @@ private:
std::string m_internal_name;
std::string m_game_id;
std::string m_gametdb_id;
std::string m_tri_id;
u64 m_title_id{};
std::string m_maker_id;

View File

@ -26,14 +26,14 @@
namespace UICommon
{
static constexpr u32 CACHE_REVISION = 25; // Last changed in PR 12702
static constexpr u32 CACHE_REVISION = 26; // Last changed in PR 10084
std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& directories_to_scan,
bool recursive_scan)
{
static const std::vector<std::string> search_extensions = {
".gcm", ".tgc", ".iso", ".ciso", ".gcz", ".wbfs", ".wia",
".rvz", ".nfs", ".wad", ".dol", ".elf", ".json"};
".gcm", ".tgc", ".bin", ".iso", ".ciso", ".gcz", ".wbfs",
".wia", ".rvz", ".nfs", ".wad", ".dol", ".elf", ".json"};
// TODO: We could process paths iteratively as they are found
return Common::DoFileSearch(directories_to_scan, search_extensions, recursive_scan);