From 0d4ea76c7f923e6a8ce38ed7e74d34ed582d6b20 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 31 Dec 2016 20:46:52 -1000 Subject: [PATCH] CGBASupport implementations --- Runtime/CBasics.hpp | 2 + Runtime/CBasicsPC.cpp | 7 + Runtime/CGameOptions.cpp | 16 +- Runtime/CGameOptions.hpp | 20 +- Runtime/CGameState.cpp | 20 +- Runtime/MP1/CFrontEndUI.cpp | 390 ++++++++++++++++++++++++++++-- Runtime/MP1/CFrontEndUI.hpp | 71 ++++-- Runtime/MP1/CGBASupport.cpp | 191 ++++++++++++++- Runtime/MP1/CGBASupport.hpp | 37 ++- Runtime/MP1/CMemoryCardDriver.cpp | 4 +- Runtime/MP1/MP1.cpp | 6 +- 11 files changed, 683 insertions(+), 81 deletions(-) diff --git a/Runtime/CBasics.hpp b/Runtime/CBasics.hpp index 5ed539f72..9a02674b8 100644 --- a/Runtime/CBasics.hpp +++ b/Runtime/CBasics.hpp @@ -39,6 +39,8 @@ public: static OSTime ToWiiTime(std::chrono::system_clock::time_point time); static std::chrono::system_clock::time_point FromWiiTime(OSTime wiiTime); + static u64 GetGCTicks(); + static OSCalendarTime ToCalendarTime(OSTime time) { return ToCalendarTime(FromWiiTime(time)); } static OSCalendarTime ToCalendarTime(std::chrono::system_clock::time_point time); }; diff --git a/Runtime/CBasicsPC.cpp b/Runtime/CBasicsPC.cpp index 184d2f25a..52b5fa83a 100644 --- a/Runtime/CBasicsPC.cpp +++ b/Runtime/CBasicsPC.cpp @@ -21,6 +21,13 @@ const char* CBasics::Stringize(const char* fmt, ...) return STRINGIZE_STR; } +u64 CBasics::GetGCTicks() +{ + auto nanos = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); + return nanos * 486000000 / 1000000000; +} + const u64 CBasics::SECONDS_TO_2000 = 946684800LL; const u64 CBasics::TICKS_PER_SECOND = 60750000LL; diff --git a/Runtime/CGameOptions.cpp b/Runtime/CGameOptions.cpp index 663847e9d..b90530a3f 100644 --- a/Runtime/CGameOptions.cpp +++ b/Runtime/CGameOptions.cpp @@ -20,11 +20,11 @@ CPersistentOptions::CPersistentOptions(CBitStreamReader& stream) xc4_ = stream.ReadEncoded(2); xc8_ = stream.ReadEncoded(1); xcc_logScanCount = stream.ReadEncoded(7); - xd0_24_ = stream.ReadEncoded(1); - xd0_25_hasHardMode = stream.ReadEncoded(1); + xd0_24_fusionLinked = stream.ReadEncoded(1); + xd0_25_normalModeBeat = stream.ReadEncoded(1); xd0_26_hardModeBeat = stream.ReadEncoded(1); - xd0_27_ = stream.ReadEncoded(1); - xd0_28_hasFusion = stream.ReadEncoded(1); + xd0_27_fusionBeat = stream.ReadEncoded(1); + xd0_28_fusionSuitActive = stream.ReadEncoded(1); xd0_29_allItemsCollected = stream.ReadEncoded(1); xbc_ = stream.ReadEncoded(2); @@ -66,11 +66,11 @@ void CPersistentOptions::PutTo(CBitStreamWriter& w) const w.WriteEncoded(xc4_, 2); w.WriteEncoded(xc8_, 1); w.WriteEncoded(xcc_logScanCount, 7); - w.WriteEncoded(xd0_24_, 1); - w.WriteEncoded(xd0_25_hasHardMode, 1); + w.WriteEncoded(xd0_24_fusionLinked, 1); + w.WriteEncoded(xd0_25_normalModeBeat, 1); w.WriteEncoded(xd0_26_hardModeBeat, 1); - w.WriteEncoded(xd0_27_, 1); - w.WriteEncoded(xd0_28_hasFusion, 1); + w.WriteEncoded(xd0_27_fusionBeat, 1); + w.WriteEncoded(xd0_28_fusionSuitActive, 1); w.WriteEncoded(xd0_29_allItemsCollected, 1); w.WriteEncoded(xbc_, 2); diff --git a/Runtime/CGameOptions.hpp b/Runtime/CGameOptions.hpp index 4fa886976..e4240c8ac 100644 --- a/Runtime/CGameOptions.hpp +++ b/Runtime/CGameOptions.hpp @@ -23,11 +23,11 @@ class CPersistentOptions { struct { - bool xd0_24_ : 1; - bool xd0_25_hasHardMode : 1; + bool xd0_24_fusionLinked : 1; + bool xd0_25_normalModeBeat : 1; bool xd0_26_hardModeBeat : 1; - bool xd0_27_ : 1; - bool xd0_28_hasFusion : 1; + bool xd0_27_fusionBeat : 1; + bool xd0_28_fusionSuitActive : 1; bool xd0_29_allItemsCollected : 1; }; u16 _dummy = 0; @@ -39,12 +39,16 @@ public: bool GetCinematicState(ResId mlvlId, TEditorId cineId) const; void SetCinematicState(ResId mlvlId, TEditorId cineId, bool state); - bool GetPlayerHasHardMode() const { return xd0_25_hasHardMode; } - void SetPlayerHasHardMode(bool v) { xd0_25_hasHardMode = v; } + bool GetPlayerLinkedFusion() const { return xd0_24_fusionLinked; } + void SetPlayerLinkedFusion(bool v) { xd0_24_fusionLinked = v; } + bool GetPlayerBeatNormalMode() const { return xd0_25_normalModeBeat; } + void SetPlayerBeatNormalMode(bool v) { xd0_25_normalModeBeat = v; } bool GetPlayerBeatHardMode() const { return xd0_26_hardModeBeat; } void SetPlayerBeatHardMode(bool v) { xd0_26_hardModeBeat = v; } - bool GetPlayerHasFusion() const { return xd0_28_hasFusion; } - void SetPlayerHasFusion(bool v) { xd0_28_hasFusion = v; } + bool GetPlayerBeatFusion() const { return xd0_27_fusionBeat; } + void SetPlayerBeatFusion(bool v) { xd0_27_fusionBeat = v; } + bool GetPlayerFusionSuitActive() const { return xd0_28_fusionSuitActive; } + void SetPlayerFusionSuitActive(bool v) { xd0_28_fusionSuitActive = v; } bool GetAllItemsCollected() const { return xd0_29_allItemsCollected; } void SetAllItemsCollected(bool v) { xd0_29_allItemsCollected = v; } u32 GetLogScanCount() const { return xcc_logScanCount; } diff --git a/Runtime/CGameState.cpp b/Runtime/CGameState.cpp index fa7f4e13a..7a3b3fde7 100644 --- a/Runtime/CGameState.cpp +++ b/Runtime/CGameState.cpp @@ -191,27 +191,27 @@ void CGameState::ReadPersistentOptions(CBitStreamReader& r) void CGameState::ImportPersistentOptions(const CPersistentOptions& opts) { - if (opts.xd0_24_) - xa8_systemOptions.xd0_24_ = true; - if (opts.xd0_27_) - xa8_systemOptions.xd0_27_ = true; + if (opts.xd0_24_fusionLinked) + xa8_systemOptions.xd0_24_fusionLinked = true; + if (opts.xd0_27_fusionBeat) + xa8_systemOptions.xd0_27_fusionBeat = true; if (&opts != &xa8_systemOptions) memcpy(xa8_systemOptions.x0_, opts.x0_, 98); xa8_systemOptions.SetLogScanCount(opts.GetLogScanCount()); xa8_systemOptions.SetAllItemsCollected(opts.GetAllItemsCollected()); - xa8_systemOptions.SetPlayerHasHardMode(opts.GetPlayerHasHardMode()); + xa8_systemOptions.SetPlayerBeatNormalMode(opts.GetPlayerBeatNormalMode()); xa8_systemOptions.SetPlayerBeatHardMode(opts.GetPlayerBeatHardMode()); } void CGameState::ExportPersistentOptions(CPersistentOptions& opts) const { - if (xa8_systemOptions.xd0_24_) - opts.xd0_24_ = true; - if (xa8_systemOptions.xd0_27_) - opts.xd0_27_ = true; + if (xa8_systemOptions.xd0_24_fusionLinked) + opts.xd0_24_fusionLinked = true; + if (xa8_systemOptions.xd0_27_fusionBeat) + opts.xd0_27_fusionBeat = true; if (&opts != &xa8_systemOptions) memcpy(opts.x0_, xa8_systemOptions.x0_, 98); - opts.SetPlayerHasFusion(xa8_systemOptions.GetPlayerHasFusion()); + opts.SetPlayerFusionSuitActive(xa8_systemOptions.GetPlayerFusionSuitActive()); } void CGameState::WriteBackupBuf() diff --git a/Runtime/MP1/CFrontEndUI.cpp b/Runtime/MP1/CFrontEndUI.cpp index 9d5d31e89..dac81bf9d 100644 --- a/Runtime/MP1/CFrontEndUI.cpp +++ b/Runtime/MP1/CFrontEndUI.cpp @@ -281,7 +281,7 @@ void CFrontEndUI::SNewFileSelectFrame::ActivateNewGamePopup() PlayAdvanceSfx(); - if (g_GameState->SystemOptions().GetPlayerHasHardMode()) + if (g_GameState->SystemOptions().GetPlayerBeatNormalMode()) { x48_textpane_popupadvance.SetPairText(g_MainStringTable->GetString(102)); x50_textpane_popupcancel.SetPairText(g_MainStringTable->GetString(94)); @@ -418,7 +418,7 @@ void CFrontEndUI::SNewFileSelectFrame::DoPopupAdvance(CGuiTableGroup* caller) } else { - if (g_GameState->SystemOptions().GetPlayerHasHardMode()) + if (g_GameState->SystemOptions().GetPlayerBeatNormalMode()) { if (x40_tablegroup_popup->GetUserSelection() == 1) { @@ -488,32 +488,239 @@ CFrontEndUI::SGBASupportFrame::SGBASupportFrame() void CFrontEndUI::SGBASupportFrame::SGBALinkFrame::SetUIText(EUIType tp) { + int instructions = -1; + int yes = -1; + int no = -1; + bool cableVisible = false; + bool circleGcVisible = false; + bool circleGbaVisible = false; + bool circleStartVisible = false; + bool pakoutVisible = false; + bool gbaScreenVisible = false; + bool connectVisible = false; + + switch (tp) + { + case EUIType::InsertPak: + instructions = 73; // Insert Game Pak + no = 82; + yes = 83; + pakoutVisible = true; + circleGbaVisible = true; + break; + case EUIType::ConnectSocket: + instructions = 68; // Connect socket + no = 82; + yes = 83; + cableVisible = true; + circleGcVisible = true; + circleGbaVisible = true; + break; + case EUIType::PressStartAndSelect: + instructions = 74; // Hold start and select + no = 82; + yes = 83; + cableVisible = true; + circleStartVisible = true; + gbaScreenVisible = true; + break; + case EUIType::BeginLink: + instructions = 75; // Begin link? + no = 82; + yes = 83; + cableVisible = true; + gbaScreenVisible = true; + break; + case EUIType::TurnOffGBA: + instructions = 76; // Turn off GBA + no = 82; + yes = 83; + cableVisible = true; + gbaScreenVisible = true; + circleStartVisible = true; + break; + case EUIType::Linking: + x4_gbaSupport->StartLink(); + instructions = 72; // Linking + cableVisible = true; + gbaScreenVisible = true; + connectVisible = true; + break; + case EUIType::LinkFailed: + instructions = 69; // Link failed + no = 82; + yes = 83; + cableVisible = true; + circleGcVisible = true; + circleGbaVisible = true; + circleStartVisible = true; + gbaScreenVisible = true; + break; + case EUIType::LinkCompleteOrLinking: + yes = 83; + instructions = x40_linkInProgress + 71; // Complete or linking + cableVisible = true; + gbaScreenVisible = true; + break; + case EUIType::Complete: + case EUIType::Cancelled: + default: + break; + } + + std::wstring instructionsStr; + if (instructions != -1) + instructionsStr = g_MainStringTable->GetString(instructions); + xc_textpane_instructions.SetPairText(instructionsStr); + + std::wstring yesStr; + if (yes != -1) + yesStr = g_MainStringTable->GetString(yes); + x14_textpane_yes->TextSupport()->SetText(yesStr); + + std::wstring noStr; + if (no != -1) + noStr = g_MainStringTable->GetString(no); + x18_textpane_no->TextSupport()->SetText(noStr); + + x1c_model_gc->SetVisibility(true, ETraversalMode::Children); + x20_model_gba->SetVisibility(true, ETraversalMode::Children); + x24_model_cable->SetVisibility(cableVisible, ETraversalMode::Children); + x28_model_circlegcport->SetVisibility(circleGcVisible, ETraversalMode::Children); + x2c_model_circlegbaport->SetVisibility(circleGbaVisible, ETraversalMode::Children); + x30_model_circlestartselect->SetVisibility(circleStartVisible, ETraversalMode::Children); + x34_model_pakout->SetVisibility(pakoutVisible, ETraversalMode::Children); + x38_model_gbascreen->SetVisibility(gbaScreenVisible, ETraversalMode::Children); + x3c_model_connect->SetVisibility(connectVisible, ETraversalMode::Children); + + x0_uiType = tp; } -void CFrontEndUI::SGBASupportFrame::SGBALinkFrame::ProcessUserInput(const CFinalInput &input, bool sui) +static const CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType NextLinkUI[] = { + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::ConnectSocket, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::PressStartAndSelect, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::BeginLink, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Linking, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Empty, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::TurnOffGBA, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Complete, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::InsertPak, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Empty, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Empty +}; +static const CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType PrevLinkUI[] = +{ + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Cancelled, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Cancelled, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Cancelled, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Cancelled, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Empty, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Cancelled, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Empty, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Cancelled, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Empty, + CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EUIType::Empty +}; + +CFrontEndUI::SGBASupportFrame::SGBALinkFrame::EAction +CFrontEndUI::SGBASupportFrame::SGBALinkFrame::ProcessUserInput(const CFinalInput &input, bool linkInProgress) +{ + if (linkInProgress != x40_linkInProgress) + { + x40_linkInProgress = linkInProgress; + SetUIText(x0_uiType); + } + + switch (x0_uiType) + { + case EUIType::InsertPak: + case EUIType::ConnectSocket: + case EUIType::PressStartAndSelect: + case EUIType::BeginLink: + case EUIType::LinkFailed: + case EUIType::LinkCompleteOrLinking: + case EUIType::TurnOffGBA: + if (input.PA()) + { + PlayAdvanceSfx(); + SetUIText(NextLinkUI[int(x0_uiType)]); + } + else if (input.PB()) + { + EUIType prevUi = PrevLinkUI[int(x0_uiType)]; + if (prevUi == EUIType::Empty) + break; + CSfxManager::SfxStart(1094, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); + SetUIText(prevUi); + } + break; + case EUIType::Linking: + if (x4_gbaSupport->GetPhase() == CGBASupport::EPhase::Complete) + { + if (x4_gbaSupport->IsFusionLinked()) + g_GameState->SystemOptions().SetPlayerLinkedFusion(true); + if (x4_gbaSupport->IsFusionBeat()) + g_GameState->SystemOptions().SetPlayerBeatFusion(true); + if (x4_gbaSupport->IsFusionLinked()) + { + PlayAdvanceSfx(); + SetUIText(EUIType::LinkCompleteOrLinking); + } + else + { + CSfxManager::SfxStart(1094, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); + SetUIText(EUIType::LinkFailed); + } + } + break; + case EUIType::Complete: + return EAction::Complete; + case EUIType::Cancelled: + return EAction::Cancelled; + default: break; + } + + return EAction::None; } void CFrontEndUI::SGBASupportFrame::SGBALinkFrame::Update(float dt) { - + x4_gbaSupport->Update(dt); + x8_frme->Update(dt); } void CFrontEndUI::SGBASupportFrame::SGBALinkFrame::FinishedLoading() { - + xc_textpane_instructions = FindTextPanePair(x8_frme, "textpane_instructions"); + x14_textpane_yes = static_cast(x8_frme->FindWidget("textpane_yes")); + x18_textpane_no = static_cast(x8_frme->FindWidget("textpane_no")); + x1c_model_gc = static_cast(x8_frme->FindWidget("model_gc")); + x20_model_gba = static_cast(x8_frme->FindWidget("model_gba")); + x24_model_cable = static_cast(x8_frme->FindWidget("model_cable")); + x28_model_circlegcport = static_cast(x8_frme->FindWidget("model_circlegcport")); + x2c_model_circlegbaport = static_cast(x8_frme->FindWidget("model_circlegbaport")); + x30_model_circlestartselect = static_cast(x8_frme->FindWidget("model_circlestartselect")); + x34_model_pakout = static_cast(x8_frme->FindWidget("model_pakout")); + x38_model_gbascreen = static_cast(x8_frme->FindWidget("model_gbascreen")); + x3c_model_connect = static_cast(x8_frme->FindWidget("model_connect")); + SetUIText(EUIType::InsertPak); } void CFrontEndUI::SGBASupportFrame::SGBALinkFrame::Draw() { - + x8_frme->Draw(CGuiWidgetDrawParms::Default); } -CFrontEndUI::SGBASupportFrame::SGBALinkFrame::SGBALinkFrame(const CGuiFrame* linkFrame, CGBASupport* support, bool) +CFrontEndUI::SGBASupportFrame::SGBALinkFrame::SGBALinkFrame(CGuiFrame* linkFrame, + CGBASupport* support, + bool linkInProgress) +: x4_gbaSupport(support), x8_frme(linkFrame), x40_linkInProgress(linkInProgress) { - + support->InitializeSupport(); + FinishedLoading(); } void CFrontEndUI::SGBASupportFrame::FinishedLoading() @@ -536,17 +743,17 @@ void CFrontEndUI::SGBASupportFrame::FinishedLoading() x2c_tablegroup_fusionsuit->SetIsActive(false); x2c_tablegroup_fusionsuit->SetIsVisible(false); x2c_tablegroup_fusionsuit->SetD1(false); - x2c_tablegroup_fusionsuit->SetUserSelection(g_GameState->SystemOptions().GetPlayerHasFusion()); + x2c_tablegroup_fusionsuit->SetUserSelection(g_GameState->SystemOptions().GetPlayerFusionSuitActive()); SetTableColors(x28_tablegroup_options); SetTableColors(x2c_tablegroup_fusionsuit); x28_tablegroup_options->SetMenuAdvanceCallback( - std::bind(&SGBASupportFrame::DoOptionsAdvance, this, std::placeholders::_1)); + std::bind(&SGBASupportFrame::DoAdvance, this, std::placeholders::_1)); x28_tablegroup_options->SetMenuSelectionChangeCallback( std::bind(&SGBASupportFrame::DoSelectionChange, this, std::placeholders::_1)); x28_tablegroup_options->SetMenuCancelCallback( - std::bind(&SGBASupportFrame::DoOptionsCancel, this, std::placeholders::_1)); + std::bind(&SGBASupportFrame::DoCancel, this, std::placeholders::_1)); x2c_tablegroup_fusionsuit->SetMenuSelectionChangeCallback( std::bind(&SGBASupportFrame::DoSelectionChange, this, std::placeholders::_1)); } @@ -574,35 +781,172 @@ void CFrontEndUI::SGBASupportFrame::SetTableColors(CGuiTableGroup* tbgp) const zeus::CColor{0.627450f, 0.627450f, 0.627450f, 0.784313f}); } +void CFrontEndUI::SGBASupportFrame::Update(float dt, CSaveUI* saveUI) +{ + bool doUpdate = false; + if (saveUI) + if (saveUI->GetUIType() == CSaveUI::EUIType::SaveProgress) + doUpdate = true; + + if (doUpdate != x38_lastDoUpdate) + { + x38_lastDoUpdate = doUpdate; + ResetCompletionFlags(); + } + + if (x0_gbaLinkFrame) + x0_gbaLinkFrame->Update(dt); + else if (x24_loadedFrame) + x24_loadedFrame->Update(dt); + + bool showFusionSuit = g_GameState->SystemOptions().GetPlayerLinkedFusion() && + g_GameState->SystemOptions().GetPlayerBeatNormalMode(); + bool showFusionSuitProceed = showFusionSuit && x28_tablegroup_options->GetUserSelection() == 1; + x2c_tablegroup_fusionsuit->SetIsActive(showFusionSuitProceed); + x2c_tablegroup_fusionsuit->SetIsVisible(showFusionSuitProceed); + x24_loadedFrame->FindWidget("textpane_proceed")->SetIsVisible(showFusionSuitProceed); + + std::wstring instructionStr; + if (x28_tablegroup_options->GetUserSelection() == 1) + { + /* Fusion Suit */ + if (x3a_mpNotComplete) + instructionStr = g_MainStringTable->GetString(80); // MP not complete + else if (!showFusionSuit) + instructionStr = g_MainStringTable->GetString(78); // To enable fusion suit + } + else + { + /* NES Metroid */ + if (x39_fusionNotComplete) + instructionStr = g_MainStringTable->GetString(79); // You have not completed fusion + else if (!g_GameState->SystemOptions().GetPlayerBeatFusion()) + instructionStr = g_MainStringTable->GetString(77); // To play NES Metroid + } + + x30_textpane_instructions.SetPairText(instructionStr); +} + CFrontEndUI::SGBASupportFrame::EAction CFrontEndUI::SGBASupportFrame::ProcessUserInput(const CFinalInput& input, CSaveUI* sui) { - return EAction::Zero; + x8_action = EAction::None; + + if (sui) + sui->ProcessUserInput(input); + + if (x38_lastDoUpdate) + { + if (x0_gbaLinkFrame) + { + SGBALinkFrame::EAction action = x0_gbaLinkFrame->ProcessUserInput(input, sui); + if (action != SGBALinkFrame::EAction::None) + { + x0_gbaLinkFrame.reset(); + if (action == SGBALinkFrame::EAction::Complete) + { + if (x28_tablegroup_options->GetUserSelection() == 0 && + !g_GameState->SystemOptions().GetPlayerBeatFusion()) + x39_fusionNotComplete = true; + else if (sui) + sui->SaveNESState(); + else if (x24_loadedFrame) + x24_loadedFrame->ProcessUserInput(input); + } + } + } + } + + return x8_action; } void CFrontEndUI::SGBASupportFrame::Draw() const { - if (!x38_) + if (!x38_lastDoUpdate) return; if (x0_gbaLinkFrame) - { - - } + x0_gbaLinkFrame->Draw(); + else if (x24_loadedFrame) + x24_loadedFrame->Draw(CGuiWidgetDrawParms::Default); } -void CFrontEndUI::SGBASupportFrame::DoOptionsCancel(CGuiTableGroup* caller) +void CFrontEndUI::SGBASupportFrame::DoCancel(CGuiTableGroup* caller) { - + if (x39_fusionNotComplete || x3a_mpNotComplete) + { + CSfxManager::SfxStart(1094, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); + } + else + { + x8_action = EAction::GoBack; + x28_tablegroup_options->SetUserSelection(0); + x2c_tablegroup_fusionsuit->SetIsActive(false); + x30_textpane_instructions.SetPairText(L""); + SetTableColors(x28_tablegroup_options); + } } void CFrontEndUI::SGBASupportFrame::DoSelectionChange(CGuiTableGroup* caller) { - + if (caller == x28_tablegroup_options) + { + CSfxManager::SfxStart(1093, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); + x3a_mpNotComplete = false; + x39_fusionNotComplete = false; + } + else + { + CSfxManager::SfxStart(1095, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); + bool fusionActive = x2c_tablegroup_fusionsuit->GetUserSelection() == 1; + g_GameState->SystemOptions().SetPlayerFusionSuitActive(fusionActive); + g_GameState->GetPlayerState()->SetIsFusionEnabled(fusionActive); + } + SetTableColors(caller); } -void CFrontEndUI::SGBASupportFrame::DoOptionsAdvance(CGuiTableGroup* caller) +void CFrontEndUI::SGBASupportFrame::DoAdvance(CGuiTableGroup* caller) { - + switch (x28_tablegroup_options->GetUserSelection()) + { + case 1: + /* Fusion Suit */ + if (x3a_mpNotComplete) + { + x3a_mpNotComplete = false; + PlayAdvanceSfx(); + } + else if (g_GameState->SystemOptions().GetPlayerBeatNormalMode()) + { + if (g_GameState->SystemOptions().GetPlayerLinkedFusion()) + return; + x0_gbaLinkFrame = std::make_unique(x18_gbaLink.GetObj(), x4_gbaSupport.get(), false); + PlayAdvanceSfx(); + } + else + { + x3a_mpNotComplete = true; + CSfxManager::SfxStart(1094, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); + } + break; + case 0: + /* NES Metroid */ + if (x39_fusionNotComplete) + { + x39_fusionNotComplete = false; + PlayAdvanceSfx(); + } + else if (g_GameState->SystemOptions().GetPlayerBeatFusion()) + { + x8_action = EAction::PlayNESMetroid; + } + else + { + x0_gbaLinkFrame = std::make_unique(x18_gbaLink.GetObj(), x4_gbaSupport.get(), false); + PlayAdvanceSfx(); + } + break; + default: break; + } } void CFrontEndUI::SGuiTextPair::SetPairText(const std::wstring& str) @@ -1311,10 +1655,10 @@ void CFrontEndUI::ProcessUserInput(const CFinalInput& input, CArchitectureQueue& { switch (xe4_gbaSupportFrme->ProcessUserInput(input, xdc_saveUI.get())) { - case SGBASupportFrame::EAction::One: + case SGBASupportFrame::EAction::GoBack: StartStateTransition(EScreen::Three); return; - case SGBASupportFrame::EAction::Two: + case SGBASupportFrame::EAction::PlayNESMetroid: xf4_curAudio->StopMixing(); xec_emuFrme = std::make_unique(); if (xdc_saveUI) diff --git a/Runtime/MP1/CFrontEndUI.hpp b/Runtime/MP1/CFrontEndUI.hpp index c39f29850..3bd407571 100644 --- a/Runtime/MP1/CFrontEndUI.hpp +++ b/Runtime/MP1/CFrontEndUI.hpp @@ -175,53 +175,88 @@ public: { enum class EUIType { - Zero, - One, - Two, - Three, - Four, - Five, - Six, - Seven + Empty = -1, + InsertPak = 0, + ConnectSocket = 1, + PressStartAndSelect = 2, + BeginLink = 3, + Linking = 4, + LinkFailed = 5, + LinkCompleteOrLinking = 6, + TurnOffGBA = 7, + Complete = 8, + Cancelled = 9 }; + enum class EAction + { + None = 0, + Complete = 1, + Cancelled = 2 + }; + + EUIType x0_uiType; + CGBASupport* x4_gbaSupport; + CGuiFrame* x8_frme; + SGuiTextPair xc_textpane_instructions; + CGuiTextPane* x14_textpane_yes = nullptr; + CGuiTextPane* x18_textpane_no = nullptr; + CGuiModel* x1c_model_gc = nullptr; + CGuiModel* x20_model_gba = nullptr; + CGuiModel* x24_model_cable = nullptr; + CGuiModel* x28_model_circlegcport = nullptr; + CGuiModel* x2c_model_circlegbaport = nullptr; + CGuiModel* x30_model_circlestartselect = nullptr; + CGuiModel* x34_model_pakout = nullptr; + CGuiModel* x38_model_gbascreen = nullptr; + CGuiModel* x3c_model_connect = nullptr; + bool x40_linkInProgress; + void SetUIText(EUIType tp); - void ProcessUserInput(const CFinalInput &input, bool sui); + EAction ProcessUserInput(const CFinalInput &input, bool linkInProgress); void Update(float dt); void FinishedLoading(); void Draw(); - SGBALinkFrame(const CGuiFrame* linkFrame, CGBASupport* support, bool); + SGBALinkFrame(CGuiFrame* linkFrame, CGBASupport* support, bool linkInProgress); }; enum class EAction { - Zero, - One, - Two + None, + GoBack, + PlayNESMetroid }; std::unique_ptr x0_gbaLinkFrame; std::unique_ptr x4_gbaSupport; + EAction x8_action = EAction::None; TLockedToken xc_gbaScreen; TLockedToken x18_gbaLink; CGuiFrame* x24_loadedFrame = nullptr; CGuiTableGroup* x28_tablegroup_options = nullptr; CGuiTableGroup* x2c_tablegroup_fusionsuit = nullptr; SGuiTextPair x30_textpane_instructions; - bool x38_ = false; - bool x39_ = false; - bool x3a_ = false; + bool x38_lastDoUpdate = false; + bool x39_fusionNotComplete = false; + bool x3a_mpNotComplete = false; SGBASupportFrame(); void FinishedLoading(); bool PumpLoad(); void SetTableColors(CGuiTableGroup* tbgp) const; + void Update(float dt, CSaveUI* saveUI); EAction ProcessUserInput(const CFinalInput& input, CSaveUI* sui); void Draw() const; - void DoOptionsCancel(CGuiTableGroup* caller); + void ResetCompletionFlags() + { + x39_fusionNotComplete = false; + x3a_mpNotComplete = false; + } + + void DoCancel(CGuiTableGroup* caller); void DoSelectionChange(CGuiTableGroup* caller); - void DoOptionsAdvance(CGuiTableGroup* caller); + void DoAdvance(CGuiTableGroup* caller); }; struct SFrontEndFrame diff --git a/Runtime/MP1/CGBASupport.cpp b/Runtime/MP1/CGBASupport.cpp index 799d01c1a..4a8733980 100644 --- a/Runtime/MP1/CGBASupport.cpp +++ b/Runtime/MP1/CGBASupport.cpp @@ -1,10 +1,57 @@ #include "CGBASupport.hpp" +#include "CDvdRequest.hpp" +#include "CBasics.hpp" namespace urde { namespace MP1 { +#define GBA_JSTAT_MASK 0x3a +#define GBA_JSTAT_FLAGS_SHIFT 4 +#define GBA_JSTAT_FLAGS_MASK 0x30 +#define GBA_JSTAT_PSF1 0x20 +#define GBA_JSTAT_PSF0 0x10 +#define GBA_JSTAT_SEND 0x08 +#define GBA_JSTAT_RECV 0x02 + +#define GBA_READY 0 +#define GBA_NOT_READY 1 +#define GBA_BUSY 2 +#define GBA_JOYBOOT_UNKNOWN_STATE 3 +#define GBA_JOYBOOT_ERR_INVALID 4 + +static s32 GBAJoyBootAsync(s32 chan, s32 paletteColor, s32 paletteSpeed, + u8* programp, s32 length, u8* status) +{ + return GBA_READY; +} + +static s32 GBAGetProcessStatus(s32 chan, u8* percentp) +{ + return GBA_READY; +} + +static s32 GBAGetStatus(s32 chan, u8* status) +{ + return GBA_READY; +} + +static s32 GBAReset(s32 chan, u8* status) +{ + return GBA_READY; +} + +static s32 GBARead(s32 chan, u8* dst, u8* status) +{ + return GBA_READY; +} + +static s32 GBAWrite(s32 chan, u8* src, u8* status) +{ + return GBA_READY; +} + CGBASupport* CGBASupport::SharedInstance = nullptr; CGBASupport::CGBASupport() @@ -13,7 +60,7 @@ CGBASupport::CGBASupport() x28_fileSize = ROUND_UP_32(Length()); x2c_buffer.reset(new u8[x28_fileSize]); x30_dvdReq = AsyncRead(x2c_buffer.get(), x28_fileSize); - //InitDSPComm(); + //GBAInit(); SharedInstance = this; } @@ -22,10 +69,150 @@ CGBASupport::~CGBASupport() SharedInstance = nullptr; } -bool CGBASupport::IsReady() const +bool CGBASupport::PollResponse() { + u8 status; + if (GBAReset(x40_siChan, &status) == GBA_NOT_READY) + if (GBAReset(x40_siChan, &status) == GBA_NOT_READY) + return false; + + if (GBAGetStatus(x40_siChan, &status) == GBA_NOT_READY) + return false; + if (status != (GBA_JSTAT_PSF1 | GBA_JSTAT_SEND)) + return false; + + u8 bytes[4]; + if (GBARead(x40_siChan, bytes, &status) == GBA_NOT_READY) + return false; + if (reinterpret_cast(bytes) != SBIG('AMTE')) + return false; + + if (GBAGetStatus(x40_siChan, &status) == GBA_NOT_READY) + return false; + if (status != GBA_JSTAT_PSF1) + return false; + + if (GBAWrite(x40_siChan, (unsigned char*)"AMTE", &status) == GBA_NOT_READY) + return false; + + if (GBAGetStatus(x40_siChan, &status) == GBA_NOT_READY) + return false; + if ((status & GBA_JSTAT_FLAGS_MASK) != GBA_JSTAT_FLAGS_MASK) + return false; + + u64 profStart = CBasics::GetGCTicks(); + const u64 timeToSpin = 486000000 / 8000; + for (;;) + { + u64 curTime = CBasics::GetGCTicks(); + if (curTime - profStart > timeToSpin) + return true; + + if (GBAGetStatus(x40_siChan, &status) == GBA_NOT_READY) + continue; + if (!(status & GBA_JSTAT_SEND)) + continue; + + if (GBAGetStatus(x40_siChan, &status) == GBA_NOT_READY) + continue; + if (status != (GBA_JSTAT_FLAGS_MASK | GBA_JSTAT_SEND)) + continue; + break; + } + + if (GBARead(x40_siChan, bytes, &status) != GBA_READY) + return false; + + /* CRC Here */ + + x44_fusionLinked = (bytes[2] & 0x2) != 0; + if (x44_fusionLinked && (bytes[2] & 0x1) != 0) + x45_fusionBeat = true; + + return true; +} + +void CGBASupport::Update(float dt) +{ + switch (x34_phase) + { + case EPhase::LoadClientPad: + IsReady(); + break; + + case EPhase::StartProbeTimeout: + x38_timeout = 4.f; + x34_phase = EPhase::PollProbe; + + case EPhase::PollProbe: + /* SIProbe poll normally occurs here with 4 second timeout */ + x34_phase = EPhase::StartJoyBusBoot; + + case EPhase::StartJoyBusBoot: + x34_phase = EPhase::PollJoyBusBoot; + GBAJoyBootAsync(x40_siChan, x40_siChan * 2, 2, x2c_buffer.get(), x28_fileSize, &x3c_status); + break; + + case EPhase::PollJoyBusBoot: + if (GBAGetProcessStatus(x40_siChan, &x3c_status) == GBA_BUSY) + break; + if (GBAGetStatus(x40_siChan, &x3c_status) == GBA_NOT_READY) + { + x34_phase = EPhase::Failed; + break; + } + x38_timeout = 4.f; + x34_phase = EPhase::DataTransfer; + break; + + case EPhase::DataTransfer: + if (PollResponse()) + { + x34_phase = EPhase::Complete; + break; + } + x38_timeout = std::max(0.f, x38_timeout - dt); + if (x38_timeout == 0.f) + x34_phase = EPhase::Failed; + break; + + default: break; + } +} + +bool CGBASupport::IsReady() +{ + if (x34_phase != EPhase::LoadClientPad) + return true; + + if (x30_dvdReq->IsComplete()) + { + x30_dvdReq.reset(); + x34_phase = EPhase::Standby; + /* Conveniently already little-endian */ + reinterpret_cast(x2c_buffer[0xc8]) = u32(CBasics::GetGCTicks()); + x2c_buffer[0xaf] = 'E'; + x2c_buffer[0xbd] = 0xc9; + return true; + } return false; } +void CGBASupport::InitializeSupport() +{ + x34_phase = EPhase::Standby; + x38_timeout = 0.f; + x3c_status = false; + x40_siChan = -1; + x44_fusionLinked = false; + x45_fusionBeat = false; +} + +void CGBASupport::StartLink() +{ + x34_phase = EPhase::StartProbeTimeout; + x40_siChan = -1; +} + } } diff --git a/Runtime/MP1/CGBASupport.hpp b/Runtime/MP1/CGBASupport.hpp index a81f0e6e8..5b9385c6a 100644 --- a/Runtime/MP1/CGBASupport.hpp +++ b/Runtime/MP1/CGBASupport.hpp @@ -10,20 +10,43 @@ namespace MP1 class CGBASupport : public CDvdFile { +public: + enum class EPhase + { + LoadClientPad, + Standby, + StartProbeTimeout, + PollProbe, + StartJoyBusBoot, + PollJoyBusBoot, + DataTransfer, + Complete, + Failed + }; + +private: u32 x28_fileSize; std::unique_ptr x2c_buffer; std::shared_ptr x30_dvdReq; - u32 x34_ = 0; - float x38_ = 0.f; - bool x3c_ = false; - u32 x40_ = -1; - bool x44_ = false; - bool x45_ = false; + EPhase x34_phase = EPhase::LoadClientPad; + float x38_timeout = 0.f; + u8 x3c_status = 0; + u32 x40_siChan = -1; + bool x44_fusionLinked = false; + bool x45_fusionBeat = false; static CGBASupport* SharedInstance; + public: CGBASupport(); ~CGBASupport(); - bool IsReady() const; + bool PollResponse(); + void Update(float dt); + bool IsReady(); + void InitializeSupport(); + void StartLink(); + EPhase GetPhase() const { return x34_phase; } + bool IsFusionLinked() const { return x44_fusionLinked; } + bool IsFusionBeat() const { return x45_fusionBeat; } }; } diff --git a/Runtime/MP1/CMemoryCardDriver.cpp b/Runtime/MP1/CMemoryCardDriver.cpp index 73ad43715..ce310956a 100644 --- a/Runtime/MP1/CMemoryCardDriver.cpp +++ b/Runtime/MP1/CMemoryCardDriver.cpp @@ -754,7 +754,7 @@ void CMemoryCardDriver::UpdateCardFormat(ECardResult result) void CMemoryCardDriver::BuildNewFileSlot(u32 saveIdx) { g_GameState->SetFileIdx(saveIdx); - bool fusionBackup = g_GameState->SystemOptions().GetPlayerHasFusion(); + bool fusionBackup = g_GameState->SystemOptions().GetPlayerFusionSuitActive(); std::unique_ptr& slot = xe4_fileSlots[saveIdx]; if (!slot) @@ -765,7 +765,7 @@ void CMemoryCardDriver::BuildNewFileSlot(u32 saveIdx) g_GameState->ReadPersistentOptions(r); ImportPersistentOptions(); g_GameState->SetCardSerial(x28_cardSerial); - g_GameState->SystemOptions().SetPlayerHasFusion(fusionBackup); + g_GameState->SystemOptions().SetPlayerFusionSuitActive(fusionBackup); } void CMemoryCardDriver::EraseFileSlot(u32 saveIdx) diff --git a/Runtime/MP1/MP1.cpp b/Runtime/MP1/MP1.cpp index 5a2f1d7b4..647ac4447 100644 --- a/Runtime/MP1/MP1.cpp +++ b/Runtime/MP1/MP1.cpp @@ -108,7 +108,7 @@ void CMain::ResetGameState() x128_globalObjects.ResetGameState(); g_GameState->ImportPersistentOptions(sysOpts); g_GameState->SetGameOptions(gameOpts); - g_GameState->GetPlayerState()->SetIsFusionEnabled(g_GameState->SystemOptions().GetPlayerHasFusion()); + g_GameState->GetPlayerState()->SetIsFusionEnabled(g_GameState->SystemOptions().GetPlayerFusionSuitActive()); } void CMain::InitializeSubsystems(const hecl::Runtime::FileStoreManager& storeMgr) @@ -130,10 +130,10 @@ void CMain::LoadAudio() void CMain::StreamNewGameState(CBitStreamReader& r, u32 idx) { - bool fusionBackup = g_GameState->SystemOptions().GetPlayerHasFusion(); + bool fusionBackup = g_GameState->SystemOptions().GetPlayerFusionSuitActive(); x128_globalObjects.x134_gameState = std::make_unique(r, idx); g_GameState = x128_globalObjects.x134_gameState.get(); - g_GameState->SystemOptions().SetPlayerHasFusion(fusionBackup); + g_GameState->SystemOptions().SetPlayerFusionSuitActive(fusionBackup); g_GameState->GetPlayerState()->SetIsFusionEnabled(fusionBackup); g_GameState->HintOptions().SetNextHintTime(); }