Added Triforce support

This commit is contained in:
crediar
2026-02-15 20:14:14 -06:00
committed by Jordan Woyak
parent d6b5af57d0
commit 2c62214875
61 changed files with 5627 additions and 174 deletions
@@ -42,6 +42,7 @@ static constexpr std::array s_gc_types = {
#endif
SIDeviceName{SerialInterface::SIDEVICE_GC_GBA, _trans("GBA (TCP)")},
SIDeviceName{SerialInterface::SIDEVICE_GC_KEYBOARD, _trans("Keyboard Controller")},
SIDeviceName{SerialInterface::SIDEVICE_AM_BASEBOARD, _trans("Triforce Baseboard")},
};
static std::optional<int> ToGCMenuIndex(const SerialInterface::SIDevices sidevice)
@@ -132,6 +133,7 @@ void GamecubeControllersWidget::OnGCPadConfigure(size_t index)
case SerialInterface::SIDEVICE_NONE:
case SerialInterface::SIDEVICE_GC_GBA:
return;
case SerialInterface::SIDEVICE_AM_BASEBOARD:
case SerialInterface::SIDEVICE_GC_CONTROLLER:
type = MappingWindow::Type::MAPPING_GCPAD;
break;
@@ -122,12 +122,6 @@ QGroupBox* InfoWidget::CreateGameDetails()
m_game.GetMakerID() + ")");
layout->addRow(tr("Name:"), internal_name);
if (m_game.GetPlatform() == DiscIO::Platform::Triforce)
{
const auto triforce_id_string = QString::fromStdString(m_game.GetTriforceID());
auto* const triforce_id = CreateValueDisplay(triforce_id_string);
layout->addRow(tr("Triforce ID:"), triforce_id);
}
layout->addRow(tr("Game ID:"), game_id);
layout->addRow(tr("Country:"), country);
layout->addRow(tr("Maker:"), maker);
@@ -8,6 +8,10 @@
#include "Core/HW/GCPad.h"
#include "Core/HW/GCPadEmu.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/SI/SI_DeviceAMBaseboard.h"
#include "Core/Config/MainSettings.h"
#include "InputCommon/InputConfig.h"
@@ -22,8 +26,15 @@ void GCPadEmu::CreateMainLayout()
layout->addWidget(CreateGroupBox(tr("Buttons"), Pad::GetGroup(GetPort(), PadGroup::Buttons)), 0,
0);
layout->addWidget(CreateGroupBox(tr("D-Pad"), Pad::GetGroup(GetPort(), PadGroup::DPad)), 1, 0, -1,
1);
layout->addWidget(CreateGroupBox(tr("D-Pad"), Pad::GetGroup(GetPort(), PadGroup::DPad)), 1, 0);
if (Config::Get(Config::GetInfoForSIDevice(0)) ==
SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD)
{
layout->addWidget(CreateGroupBox(tr("Triforce"), Pad::GetGroup(GetPort(), PadGroup::Triforce)),
2, 0);
}
layout->addWidget(
CreateGroupBox(tr("Control Stick"), Pad::GetGroup(GetPort(), PadGroup::MainStick)), 0, 1, -1,
1);
@@ -26,6 +26,9 @@
#include "Common/IniFile.h"
#include "Common/StringUtil.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/SI/SI_DeviceAMBaseboard.h"
#include "DolphinQt/Config/Mapping/FreeLookGeneral.h"
#include "DolphinQt/Config/Mapping/FreeLookRotation.h"
#include "DolphinQt/Config/Mapping/GBAPadEmu.h"
@@ -458,8 +461,17 @@ void MappingWindow::SetMappingType(MappingWindow::Type type)
case Type::MAPPING_GC_DANCEMAT:
case Type::MAPPING_GCPAD:
widget = new GCPadEmu(this);
setWindowTitle(tr("GameCube Controller at Port %1").arg(GetPort() + 1));
AddWidget(tr("GameCube Controller"), widget);
if (Config::Get(Config::GetInfoForSIDevice(GetPort())) ==
SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD)
{
setWindowTitle(tr("Triforce Baseboard at Port %1").arg(GetPort() + 1));
AddWidget(tr("Triforce Baseboard"), widget);
}
else
{
setWindowTitle(tr("GameCube Controller at Port %1").arg(GetPort() + 1));
AddWidget(tr("GameCube Controller"), widget);
}
break;
case Type::MAPPING_GC_MICROPHONE:
widget = new GCMicrophone(this);
+44
View File
@@ -250,7 +250,12 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr<BootParameters> boo
});
#endif
connect(m_cheats_manager, &CheatsManager::OpenGeneralSettings, this,
&MainWindow::ShowGeneralWindow);
#ifdef USE_RETRO_ACHIEVEMENTS
connect(m_cheats_manager, &CheatsManager::OpenAchievementSettings, this,
&MainWindow::ShowAchievementSettings);
connect(m_game_list, &GameList::OpenAchievementSettings, this,
&MainWindow::ShowAchievementSettings);
#endif // USE_RETRO_ACHIEVEMENTS
@@ -511,6 +516,8 @@ void MainWindow::CreateComponents()
});
connect(m_breakpoint_widget, &BreakpointWidget::ShowMemory, m_memory_widget,
&MemoryWidget::SetAddress);
connect(m_cheats_manager, &CheatsManager::ShowMemory, m_memory_widget, &MemoryWidget::SetAddress);
connect(m_cheats_manager, &CheatsManager::RequestWatch, request_watch);
}
void MainWindow::ConnectMenuBar()
@@ -1151,6 +1158,43 @@ void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
if (!NKitWarningDialog::ShowUnlessDisabled())
return;
}
/*
When booting Triforce games, we need to ensure that the hardware is set up correctly.
*/
const auto volume_type =
std::get<BootParameters::Disc>(parameters->parameters).volume->GetVolumeType();
const bool triforce_hardware_sp1 =
Config::Get(Config::MAIN_SERIAL_PORT_1) == ExpansionInterface::EXIDeviceType::Baseboard;
const bool triforce_hardware_port_1 = Config::Get(Config::GetInfoForSIDevice(0)) ==
SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD;
if (volume_type == DiscIO::Platform::Triforce)
{
if (!triforce_hardware_sp1 || !triforce_hardware_port_1)
{
ModalMessageBox::critical(
this, tr("Error"),
tr("To boot a Triforce game, SP1 and Port 1 must be set to Triforce Baseboard."),
QMessageBox::Ok);
HideRenderWidget();
return;
}
}
else
{
/*
Some Triforce tools don't include a boot.id file, but they can still be launched.
*/
if (triforce_hardware_sp1 || triforce_hardware_port_1)
{
ModalMessageBox::warning(
this, tr("Warning"),
tr("Non-Triforce games cannot be booted with Triforce hardware attached."),
QMessageBox::Ok);
}
}
}
// If we're running, only start a new game once we've stopped the last.
+4
View File
@@ -319,6 +319,9 @@ void MenuBar::AddToolsMenu()
m_pal_ipl =
gc_ipl->addAction(tr("PAL"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::PAL); });
m_dev_ipl = gc_ipl->addAction(tr("Triforce"), this,
[this] { emit BootGameCubeIPL(DiscIO::Region::Unknown); });
tools_menu->addAction(tr("Memory Card Manager"), this, [this] { emit ShowMemcardManager(); });
tools_menu->addSeparator();
@@ -1091,6 +1094,7 @@ void MenuBar::UpdateToolsMenu(const Core::State state)
m_ntscj_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(JAP_DIR)));
m_ntscu_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(USA_DIR)));
m_pal_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(EUR_DIR)));
m_dev_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(DEV_DIR)));
m_wad_install_action->setEnabled(is_uninitialized);
m_import_backup->setEnabled(is_uninitialized);
m_check_nand->setEnabled(is_uninitialized);
+1
View File
@@ -227,6 +227,7 @@ private:
QAction* m_ntscj_ipl;
QAction* m_ntscu_ipl;
QAction* m_pal_ipl;
QAction* m_dev_ipl;
QMenu* m_manage_nand_menu;
QAction* m_import_backup;
QAction* m_check_nand;
+28 -15
View File
@@ -151,6 +151,7 @@ void GameCubePane::CreateWidgets()
EXIDeviceType::EthernetIPC,
#endif
EXIDeviceType::ModemTapServer,
EXIDeviceType::Baseboard,
})
{
m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()),
@@ -418,6 +419,10 @@ void GameCubePane::OnConfigPressed(ExpansionInterface::Slot slot)
dialog.exec();
return;
}
case ExpansionInterface::EXIDeviceType::Baseboard:
{
return;
}
default:
PanicAlertFmt("Unknown settings pressed for {}", device);
return;
@@ -452,19 +457,23 @@ bool GameCubePane::SetMemcard(ExpansionInterface::Slot slot, const QString& file
const std::string jp_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::NTSC_J);
const std::string us_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::NTSC_U);
const std::string eu_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::PAL);
const bool raw_path_valid = raw_path == jp_path || raw_path == us_path || raw_path == eu_path;
const std::string dv_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::DEV);
const bool raw_path_valid =
raw_path == jp_path || raw_path == us_path || raw_path == eu_path || raw_path == dv_path;
if (!raw_path_valid)
{
// TODO: We could try to autodetect the card region here and offer automatic renaming.
ModalMessageBox::critical(this, tr("Error"),
tr("The filename %1 does not conform to Dolphin's region code format "
"for memory cards. Please rename this file to either %2, %3, or "
"%4, matching the region of the save files that are on it.")
.arg(QString::fromStdString(PathToFileName(raw_path)))
.arg(QString::fromStdString(PathToFileName(us_path)))
.arg(QString::fromStdString(PathToFileName(eu_path)))
.arg(QString::fromStdString(PathToFileName(jp_path))));
ModalMessageBox::critical(
this, tr("Error"),
tr("The filename %1 does not conform to Dolphin's region code format "
"for memory cards. Please rename this file to either %2, %3, %4, or "
"%5, matching the region of the save files that are on it.")
.arg(QString::fromStdString(PathToFileName(raw_path)))
.arg(QString::fromStdString(PathToFileName(us_path)))
.arg(QString::fromStdString(PathToFileName(eu_path)))
.arg(QString::fromStdString(PathToFileName(jp_path)))
.arg(QString::fromStdString(PathToFileName(dv_path))));
return false;
}
@@ -556,8 +565,9 @@ bool GameCubePane::SetGCIFolder(ExpansionInterface::Slot slot, const QString& pa
const std::string default_jp_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::NTSC_J);
const std::string default_us_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::NTSC_U);
const std::string default_eu_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::PAL);
const bool is_default_path =
raw_path == default_jp_path || raw_path == default_us_path || raw_path == default_eu_path;
const std::string default_dv_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::DEV);
const bool is_default_path = raw_path == default_jp_path || raw_path == default_us_path ||
raw_path == default_eu_path || raw_path == default_dv_path;
bool path_changed;
if (is_default_path)
@@ -574,7 +584,9 @@ bool GameCubePane::SetGCIFolder(ExpansionInterface::Slot slot, const QString& pa
const std::string jp_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::NTSC_J);
const std::string us_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::NTSC_U);
const std::string eu_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::PAL);
const bool raw_path_valid = raw_path == jp_path || raw_path == us_path || raw_path == eu_path;
const std::string dv_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::DEV);
const bool raw_path_valid =
raw_path == jp_path || raw_path == us_path || raw_path == eu_path || raw_path == dv_path;
if (!raw_path_valid)
{
@@ -582,12 +594,13 @@ bool GameCubePane::SetGCIFolder(ExpansionInterface::Slot slot, const QString& pa
ModalMessageBox::critical(
this, tr("Error"),
tr("The folder %1 does not conform to Dolphin's region code format "
"for GCI folders. Please rename this folder to either %2, %3, or "
"%4, matching the region of the save files that are in it.")
"for GCI folders. Please rename this folder to either %2, %3, %4, or "
"%5, matching the region of the save files that are in it.")
.arg(QString::fromStdString(PathToFileName(raw_path)))
.arg(QString::fromStdString(PathToFileName(us_path)))
.arg(QString::fromStdString(PathToFileName(eu_path)))
.arg(QString::fromStdString(PathToFileName(jp_path))));
.arg(QString::fromStdString(PathToFileName(jp_path)))
.arg(QString::fromStdString(PathToFileName(dv_path))));
return false;
}