CGBASupport implementations

This commit is contained in:
Jack Andersen 2016-12-31 20:46:52 -10:00
parent db7c2aeaf2
commit 0d4ea76c7f
11 changed files with 683 additions and 81 deletions

View File

@ -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);
};

View File

@ -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::nanoseconds>(
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;

View File

@ -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);

View File

@ -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; }

View File

@ -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()

View File

@ -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<CGuiTextPane*>(x8_frme->FindWidget("textpane_yes"));
x18_textpane_no = static_cast<CGuiTextPane*>(x8_frme->FindWidget("textpane_no"));
x1c_model_gc = static_cast<CGuiModel*>(x8_frme->FindWidget("model_gc"));
x20_model_gba = static_cast<CGuiModel*>(x8_frme->FindWidget("model_gba"));
x24_model_cable = static_cast<CGuiModel*>(x8_frme->FindWidget("model_cable"));
x28_model_circlegcport = static_cast<CGuiModel*>(x8_frme->FindWidget("model_circlegcport"));
x2c_model_circlegbaport = static_cast<CGuiModel*>(x8_frme->FindWidget("model_circlegbaport"));
x30_model_circlestartselect = static_cast<CGuiModel*>(x8_frme->FindWidget("model_circlestartselect"));
x34_model_pakout = static_cast<CGuiModel*>(x8_frme->FindWidget("model_pakout"));
x38_model_gbascreen = static_cast<CGuiModel*>(x8_frme->FindWidget("model_gbascreen"));
x3c_model_connect = static_cast<CGuiModel*>(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<SGBALinkFrame>(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<SGBALinkFrame>(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<SNesEmulatorFrame>();
if (xdc_saveUI)

View File

@ -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<SGBALinkFrame> x0_gbaLinkFrame;
std::unique_ptr<CGBASupport> x4_gbaSupport;
EAction x8_action = EAction::None;
TLockedToken<CGuiFrame> xc_gbaScreen;
TLockedToken<CGuiFrame> 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

View File

@ -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<u32&>(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<u32&>(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;
}
}
}

View File

@ -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<u8[]> x2c_buffer;
std::shared_ptr<IDvdRequest> 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; }
};
}

View File

@ -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<SGameFileSlot>& 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)

View File

@ -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<CGameState>(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();
}