diff --git a/Makefile b/Makefile index e975133..ac395a3 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ TOPDIR ?= $(CURDIR) APP_NAME := DRXUtil APP_SHORTNAME := DRXUtil APP_AUTHOR := GaryOderNichts -APP_VERSION := 1.1 +APP_VERSION := 1.2 include $(DEVKITPRO)/wut/share/wut_rules @@ -37,7 +37,7 @@ SOURCES := source source/screens DATA := data INCLUDES := source include CONTENT := -ICON := +ICON := TV_SPLASH := DRC_SPLASH := diff --git a/source/screens/DrcFFlashScreen.cpp b/source/screens/DrcFFlashScreen.cpp new file mode 100644 index 0000000..7160712 --- /dev/null +++ b/source/screens/DrcFFlashScreen.cpp @@ -0,0 +1,284 @@ +#include "DrcFFlashScreen.hpp" +#include "Gfx.hpp" +#include "ProcUI.hpp" +#include "Utils.hpp" +#include + +#include +#include +#include +#include +#include +#include + +namespace { + +void SoftwareUpdateCallback(IOSError error, void* arg) +{ + DrcFFlashScreen* drcFFlashScreen = static_cast(arg); + + drcFFlashScreen->OnUpdateCompleted(error); +} + +} + +DrcFFlashScreen::~DrcFFlashScreen() +{ +} + +void DrcFFlashScreen::Draw() +{ + DrawTopBar("DrcFFlashScreen"); + + switch (mState) + { + case STATE_PREPARE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Preparing...", Gfx::ALIGN_CENTER); + break; + } + case STATE_CONFIRM2: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, + Utils::sprintf("Are you really really really sure?\n" + "About to flash all firmware.\n" + "You may brick the DRC!!\n"), + Gfx::ALIGN_CENTER); + break; + } + case STATE_UPDATE_LANG: + case STATE_UPDATE_FIRM: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Starting update...", Gfx::ALIGN_CENTER); + break; + } + case STATE_FLASHING_LANG: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2 - 32, 64, Gfx::COLOR_TEXT, Utils::sprintf("Updating language... %d%% done.\nDo not turn the power off.", mFlashingProgress), Gfx::ALIGN_CENTER); + Gfx::DrawRect(64, Gfx::SCREEN_HEIGHT / 2 + 80, Gfx::SCREEN_WIDTH - 128, 64, 5, Gfx::COLOR_ACCENT); + Gfx::DrawRectFilled(64, Gfx::SCREEN_HEIGHT / 2 + 80, (Gfx::SCREEN_WIDTH - 128) * (mFlashingProgress / 100.0f), 64, Gfx::COLOR_ACCENT); + break; + } + case STATE_FLASHING_FIRM: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2 - 32, 64, Gfx::COLOR_TEXT, Utils::sprintf("Updating firmware... %d%% done.\nDo not turn the power off.", mFlashingProgress), Gfx::ALIGN_CENTER); + Gfx::DrawRect(64, Gfx::SCREEN_HEIGHT / 2 + 80, Gfx::SCREEN_WIDTH - 128, 64, 5, Gfx::COLOR_ACCENT); + Gfx::DrawRectFilled(64, Gfx::SCREEN_HEIGHT / 2 + 80, (Gfx::SCREEN_WIDTH - 128) * (mFlashingProgress / 100.0f), 64, Gfx::COLOR_ACCENT); + break; + } + case STATE_ACTIVATE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Activating update...", Gfx::ALIGN_CENTER); + break; + } + case STATE_DONE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, + Utils::sprintf("Done!\n" + "DRC was updated successfully."), + Gfx::ALIGN_CENTER); + break; + } + case STATE_ERROR: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error:\n" + mErrorString, Gfx::ALIGN_CENTER); + break; + } + } + + if (mState == STATE_CONFIRM2) { + DrawBottomBar(nullptr, "\ue044 Exit", "\ue000 Confirm / \ue001 Back"); + } else if (mState == STATE_PREPARE || mState == STATE_UPDATE_FIRM || mState == STATE_UPDATE_LANG || mState == STATE_FLASHING_FIRM || mState == STATE_FLASHING_LANG || mState == STATE_ACTIVATE) { + DrawBottomBar(nullptr, "Please wait...", nullptr); + } else if (mState == STATE_DONE || mState == STATE_ERROR) { + DrawBottomBar(nullptr, nullptr, "\ue001 Back"); + } +} + +bool DrcFFlashScreen::Update(VPADStatus& input) // Here is the core logic +{ + flashUtils.ReadFirmwareHeader("/vol/external01/lang.bin", mLanguageHeader); + uint32_t targetVersion = mLanguageHeader.version; // We will read this value later + flashUtils.ReadFirmwareHeader("/vol/external01/drc_fw.bin", mFirmwareHeader); + uint32_t firmwareVersion = mFirmwareHeader.version; + switch (mState) + { + case STATE_CONFIRM2: { // Ask the user before starting writes + if (input.trigger & VPAD_BUTTON_B) { + return false; + } + + if (input.trigger & VPAD_BUTTON_A) { + mState = STATE_UPDATE_LANG; + break; + } + break; + } + case STATE_PREPARE: { // When picked, copy firmware and language to MLC. + if (!flashUtils.CheckVersionSafety(firmwareVersion, targetVersion)) { + mErrorString = "Language version not valid for the DRC firmware!"; + mState = STATE_ERROR; + break; + } + // Copy to MLC so IOS-PAD can install it + mFirmwarePath = "/vol/storage_mlc01/usr/tmp/drc_fw.bin"; // copy firmware + if (!flashUtils.CopyFile("/vol/external01/drc_fw.bin", "storage_mlc01:/usr/tmp/drc_fw.bin")) { + mErrorString = "Failed to copy firmware to MLC"; + mState = STATE_ERROR; + break; + } + mLangPath = "/vol/storage_mlc01/usr/tmp/lang.bin"; // copy language + if (!flashUtils.CopyFile("/vol/external01/lang.bin", "storage_mlc01:/usr/tmp/lang.bin")) { + mErrorString = "Failed to copy language to MLC"; + mState = STATE_ERROR; + break; + } + mState = STATE_CONFIRM2; // ask user + break; + } + case STATE_UPDATE_LANG: { + ProcUI::SetHomeButtonMenuEnabled(false); // avoid funny situatuions + + if (!flashUtils.CaffeineInvalidate()) { + mErrorString = "Failed to invalidate caffeine."; + mState = STATE_ERROR; + break; + } + + // Abort any potential pending software updates + CCRCDCSoftwareAbort(CCR_CDC_DESTINATION_DRC0); // avoid funny situations (this shouldnt matter afaict) + + // Reattach the DRC in update mode + if (!flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_FWUPDATE, FALSE)) { + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Failed to reattach DRC in update mode."; + mState = STATE_ERROR; + break; + } + mFlashingProgress = 0; + mUpdateComplete = false; + mUpdateResult = 0; + if (CCRCDCSoftwareLangUpdate(CCR_CDC_DESTINATION_DRC0, mLangPath.c_str(), &targetVersion, SoftwareUpdateCallback, this) != 0) { // Flash language first because it's forgivable somewhat. + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Failed to start language update."; + mState = STATE_ERROR; + break; + } + + mState = STATE_FLASHING_LANG; // Track progress + break; + } + case STATE_FLASHING_LANG: { + // Update progress + CCRCDCFWInfo fwInfo{}; + if (CCRCDCGetFWInfo(CCR_CDC_DESTINATION_DRC0, &fwInfo) == 0) { + mFlashingProgress = fwInfo.updateProgress; + } + + OSSleepTicks(OSMillisecondsToTicks(200)); + + // Check if update complete + if (mUpdateComplete) { + if (mUpdateResult == IOS_ERROR_OK) { + mState = STATE_UPDATE_FIRM; + } else { + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Software update failed."; + mState = STATE_ERROR; + } + } + break; + } + case STATE_UPDATE_FIRM: { + ProcUI::SetHomeButtonMenuEnabled(false); // keep avoiding funnies in case there are any. + // Hey do you like the idea of making that ErrEula "No HOME" popup? + if (!flashUtils.CaffeineInvalidate()) { + mErrorString = "Failed to invalidate caffeine."; + mState = STATE_ERROR; + break; + } + + mFlashingProgress = 0; + mUpdateComplete = false; + mUpdateResult = 0; + if (CCRCDCSoftwareUpdate(CCR_CDC_DESTINATION_DRC0, mFirmwarePath.c_str(), SoftwareUpdateCallback, this) != 0) { // Now flash firmware. + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Failed to start software update."; + mState = STATE_ERROR; + break; + } + + mState = STATE_FLASHING_FIRM; // Track progress + break; + } + case STATE_FLASHING_FIRM: { + // Update progress + CCRCDCFWInfo fwInfo{}; + if (CCRCDCGetFWInfo(CCR_CDC_DESTINATION_DRC0, &fwInfo) == 0) { + mFlashingProgress = fwInfo.updateProgress; + } + + OSSleepTicks(OSMillisecondsToTicks(200)); + + // Check if update complete + if (mUpdateComplete) { + if (mUpdateResult == IOS_ERROR_OK) { + mState = STATE_ACTIVATE; + } else { + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Software update failed."; + mState = STATE_ERROR; + } + } + break; + } + case STATE_ACTIVATE: { + // Activate the newly flashed language + uint32_t langActivateSuccess = 0; + CCRCDCSoftwareLangActivate(CCR_CDC_DESTINATION_DRC0, targetVersion, &langActivateSuccess); + if (langActivateSuccess != 0 || CCRCDCSoftwareActivate(CCR_CDC_DESTINATION_DRC0) != 0) { + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Failed to activate one of the updates."; + mState = STATE_ERROR; + break; + } + + // Put the gamepad back into active mode + OSTime startTime = OSGetSystemTime(); + while (!flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE)) { + // 10 second timeout + if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 10) { + // At this point we don't really care if it times out or not + break; + } + + OSSleepTicks(OSMillisecondsToTicks(1000)); + } + + mState = STATE_DONE; + break; + } + case STATE_DONE: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + case STATE_ERROR: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + + default: + break; + } + + return true; +} + +void DrcFFlashScreen::OnUpdateCompleted(int32_t result) +{ + mUpdateComplete = true; + mUpdateResult = result; +} diff --git a/source/screens/DrcFFlashScreen.hpp b/source/screens/DrcFFlashScreen.hpp new file mode 100644 index 0000000..45b2139 --- /dev/null +++ b/source/screens/DrcFFlashScreen.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include "Screen.hpp" +#include "FlashUtils.hpp" + +class DrcFFlashScreen : public Screen +{ +public: +// DrcLangScreen(); + virtual ~DrcFFlashScreen(); + + void Draw(); + + bool Update(VPADStatus& input); + + void OnUpdateCompleted(int32_t result); +private: + enum State { + STATE_PREPARE, + STATE_CONFIRM2, + STATE_UPDATE_LANG, + STATE_FLASHING_LANG, + STATE_UPDATE_FIRM, + STATE_FLASHING_FIRM, + STATE_ACTIVATE, + STATE_DONE, + STATE_ERROR, + } mState = STATE_PREPARE; + + struct FileEntry { + uint16_t icon; + const char* name; + }; + + std::string mErrorString; + std::string mFirmwarePath; + std::string mLangPath; + FlashUtils::FirmwareHeader mLanguageHeader; + FlashUtils::FirmwareHeader mFirmwareHeader; + int32_t mFlashingProgress; + bool mUpdateComplete; + int32_t mUpdateResult; +}; diff --git a/source/screens/FlashScreen.cpp b/source/screens/DrcFlashScreen.cpp similarity index 69% rename from source/screens/FlashScreen.cpp rename to source/screens/DrcFlashScreen.cpp index a691057..5cb6cc9 100644 --- a/source/screens/FlashScreen.cpp +++ b/source/screens/DrcFlashScreen.cpp @@ -1,4 +1,4 @@ -#include "FlashScreen.hpp" +#include "DrcFlashScreen.hpp" #include "Gfx.hpp" #include "ProcUI.hpp" #include "Utils.hpp" @@ -33,161 +33,16 @@ bool GetDRCFirmwarePath(std::string& path) return true; } -bool ReadFirmwareHeader(const std::string& path, FlashScreen::FirmwareHeader& header) -{ - FILE* f = fopen(path.c_str(), "rb"); - if (!f) { - return false; - } - - if (fread(&header, 1, sizeof(header), f) != sizeof(header)) { - fclose(f); - return false; - } - - fclose(f); - return true; -} - -bool CopyFile(const std::string& srcPath, const std::string& dstPath) -{ - FILE* inf = fopen(srcPath.c_str(), "rb"); - if (!inf) { - return false; - } - - FILE* outf = fopen(dstPath.c_str(), "wb"); - if (!outf) { - fclose(inf); - return false; - } - - uint8_t buf[4096]; - size_t bytesRead; - while ((bytesRead = fread(buf, 1, sizeof(buf), inf)) > 0) { - if (fwrite(buf, 1, bytesRead, outf) != bytesRead) { - fclose(inf); - fclose(outf); - return false; - } - } - - if (ferror(inf)) { - fclose(inf); - fclose(outf); - return false; - } - - fclose(inf); - fclose(outf); - return true; -} - -bool CaffeineInvalidate() -{ - CCRCDCSoftwareVersion version; - CCRCDCSoftwareGetVersion(CCR_CDC_DESTINATION_DRC0, &version); - - // Only newer versions have caffeine - if (version.runningVersion >= 0x180a0000) { - return CCRSysCaffeineSetCaffeineSlot(0xff) == 0; - } - - return true; -} - -bool WaitForEeprom(uint32_t drcSlot) -{ - uint8_t val; - OSTime startTime = OSGetSystemTime(); - while (CCRCFGGetCachedEeprom(drcSlot, 0, &val, sizeof(val)) == -1) { - // 2 second timeout - if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 2) { - return false; - } - - OSSleepTicks(OSMillisecondsToTicks(200)); - } - - return true; -} - -bool ReattachDRC(CCRCDCDestination dest, CCRCDCDrcStateEnum targetState, BOOL unknown) -{ - // Get the current DRC state - CCRCDCDrcState state; - int32_t res = CCRCDCSysGetDrcState(dest, &state); - if (res != 0) { - return false; - } - - if (state.state == CCR_CDC_DRC_STATE_STANDALONE) { - state.state = CCR_CDC_DRC_STATE_ACTIVE; - } - - // Nothing to do if we're already in the target state - if (state.state == targetState) { - return true; - } - - __CCRSysInitReattach(dest - CCR_CDC_DESTINATION_DRC0); - - // Set target state - state.state = targetState; - res = CCRCDCSysSetDrcState(dest, &state); - if (res != 0) { - return false; - } - - // Wait for the DRC to reattach - res = __CCRSysWaitReattach(dest - CCR_CDC_DESTINATION_DRC0, unknown); - if (res != 0) { - return false; - } - - // Wait for EEPROM - if (!WaitForEeprom(dest - CCR_CDC_DESTINATION_DRC0)) { - return false; - } - - // Check if we're in the state we want - res = CCRCDCSysGetDrcState(dest, &state); - if (res != 0) { - return false; - } - - if (state.state != targetState) { - return false; - } - - return true; -} - -bool AbortUpdate(CCRCDCDestination dest) -{ - OSTime startTime = OSGetSystemTime(); - while (CCRCDCSoftwareAbort(dest) != 0) { - // 3 second timeout - if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 3) { - return false; - } - - OSSleepTicks(OSMillisecondsToTicks(200)); - } - - return true; -} - void SoftwareUpdateCallback(IOSError error, void* arg) { - FlashScreen* flashScreen = static_cast(arg); + DrcFlashScreen* drcFlashScreen = static_cast(arg); - flashScreen->OnUpdateCompleted(error); + drcFlashScreen->OnUpdateCompleted(error); } } -FlashScreen::FlashScreen() +DrcFlashScreen::DrcFlashScreen() : mFileEntries({ {FILE_ORIGINAL, {0xf187, "Original Firmware"}}, {FILE_SDCARD, {0xf7c2, "From SD Card (\"sd:/drc_fw.bin\")"}}, @@ -195,13 +50,13 @@ FlashScreen::FlashScreen() { } -FlashScreen::~FlashScreen() +DrcFlashScreen::~DrcFlashScreen() { } -void FlashScreen::Draw() +void DrcFlashScreen::Draw() { - DrawTopBar("FlashScreen"); + DrawTopBar("DrcFlashScreen"); switch (mState) { @@ -275,7 +130,7 @@ void FlashScreen::Draw() } } -bool FlashScreen::Update(VPADStatus& input) +bool DrcFlashScreen::Update(VPADStatus& input) { switch (mState) { @@ -330,8 +185,8 @@ bool FlashScreen::Update(VPADStatus& input) std::string doptPath = originalFirmwarePath; doptPath.replace(prefixPos, sizeof("/vol/storage_mlc01") - 1, "storage_mlc01:"); - FirmwareHeader originalFirmwareHeader; - if (!ReadFirmwareHeader(doptPath, originalFirmwareHeader)) { + FlashUtils::FirmwareHeader originalFirmwareHeader; + if (!flashUtils.ReadFirmwareHeader(doptPath, originalFirmwareHeader)) { mErrorString = "Failed to read original DRC firmware header"; mState = STATE_ERROR; break; @@ -341,23 +196,23 @@ bool FlashScreen::Update(VPADStatus& input) mFirmwarePath = originalFirmwarePath; mFirmwareHeader = originalFirmwareHeader; } else if (mFile == FILE_SDCARD) { - if (!ReadFirmwareHeader("/vol/external01/drc_fw.bin", mFirmwareHeader)) { + if (!flashUtils.ReadFirmwareHeader("/vol/external01/drc_fw.bin", mFirmwareHeader)) { mErrorString = "Failed to read DRC firmware header"; mState = STATE_ERROR; break; } - +/* // Don't allow downgrading lower than the version on NAND, // otherwise this might cause bricks without flashing the language files? if (mFirmwareHeader.version < originalFirmwareHeader.version) { mErrorString = Utils::sprintf("Not allowing versions lower than version on NAND.\n(Firmware 0x%08x Original 0x%08x)", mFirmwareHeader.version, originalFirmwareHeader.version); mState = STATE_ERROR; break; - } + }*/ // Copy to MLC so IOS-PAD can install it mFirmwarePath = "/vol/storage_mlc01/usr/tmp/drc_fw.bin"; - if (!CopyFile("/vol/external01/drc_fw.bin", "storage_mlc01:/usr/tmp/drc_fw.bin")) { + if (!flashUtils.CopyFile("/vol/external01/drc_fw.bin", "storage_mlc01:/usr/tmp/drc_fw.bin")) { mErrorString = "Failed to copy firmware to MLC"; mState = STATE_ERROR; break; @@ -366,6 +221,18 @@ bool FlashScreen::Update(VPADStatus& input) mState = STATE_ERROR; break; } + + uint32_t extId = 0; + if (CCRCDCSoftwareGetExtId(CCR_CDC_DESTINATION_DRC0, CCR_CDC_EXT_LANGUAGE, &extId) != 0) { + mErrorString = "Failed to get DRC language version!"; + mState = STATE_ERROR; + break; + } + if (!flashUtils.CheckVersionSafety(mFirmwareHeader.version, extId)) { + mErrorString = "Firmware version not valid for the DRC language!"; + mState = STATE_ERROR; + break; + } mState = STATE_CONFIRM2; break; @@ -373,7 +240,7 @@ bool FlashScreen::Update(VPADStatus& input) case STATE_UPDATE: { ProcUI::SetHomeButtonMenuEnabled(false); - if (!CaffeineInvalidate()) { + if (!flashUtils.CaffeineInvalidate()) { mErrorString = "Failed to invalidate caffeine."; mState = STATE_ERROR; break; @@ -383,8 +250,8 @@ bool FlashScreen::Update(VPADStatus& input) CCRCDCSoftwareAbort(CCR_CDC_DESTINATION_DRC0); // Reattach the DRC in update mode - if (!ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_FWUPDATE, FALSE)) { - ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + if (!flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_FWUPDATE, FALSE)) { + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); mErrorString = "Failed to reattach DRC in update mode."; mState = STATE_ERROR; break; @@ -394,8 +261,8 @@ bool FlashScreen::Update(VPADStatus& input) mUpdateComplete = false; mUpdateResult = 0; if (CCRCDCSoftwareUpdate(CCR_CDC_DESTINATION_DRC0, mFirmwarePath.c_str(), SoftwareUpdateCallback, this) != 0) { - AbortUpdate(CCR_CDC_DESTINATION_DRC0); - ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); mErrorString = "Failed to start software update."; mState = STATE_ERROR; break; @@ -418,8 +285,8 @@ bool FlashScreen::Update(VPADStatus& input) if (mUpdateResult == IOS_ERROR_OK) { mState = STATE_ACTIVATE; } else { - AbortUpdate(CCR_CDC_DESTINATION_DRC0); - ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); mErrorString = "Software update failed."; mState = STATE_ERROR; } @@ -429,8 +296,8 @@ bool FlashScreen::Update(VPADStatus& input) case STATE_ACTIVATE: { // Activate the newly flashed firmware if (CCRCDCSoftwareActivate(CCR_CDC_DESTINATION_DRC0) != 0) { - AbortUpdate(CCR_CDC_DESTINATION_DRC0); - ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); mErrorString = "Failed to activate software update."; mState = STATE_ERROR; break; @@ -438,7 +305,7 @@ bool FlashScreen::Update(VPADStatus& input) // Put the gamepad back into active mode OSTime startTime = OSGetSystemTime(); - while (!ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE)) { + while (!flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE)) { // 10 second timeout if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 10) { // At this point we don't really care if it times out or not @@ -473,7 +340,7 @@ bool FlashScreen::Update(VPADStatus& input) return true; } -void FlashScreen::OnUpdateCompleted(int32_t result) +void DrcFlashScreen::OnUpdateCompleted(int32_t result) { mUpdateComplete = true; mUpdateResult = result; diff --git a/source/screens/FlashScreen.hpp b/source/screens/DrcFlashScreen.hpp similarity index 74% rename from source/screens/FlashScreen.hpp rename to source/screens/DrcFlashScreen.hpp index a278f47..c66d347 100644 --- a/source/screens/FlashScreen.hpp +++ b/source/screens/DrcFlashScreen.hpp @@ -1,21 +1,15 @@ #pragma once #include "Screen.hpp" +#include "FlashUtils.hpp" #include -class FlashScreen : public Screen +class DrcFlashScreen : public Screen { -public: - struct FirmwareHeader { - uint32_t version; - uint32_t blockSize; - uint32_t sequencePerSession; - uint32_t imageSize; - }; public: - FlashScreen(); - virtual ~FlashScreen(); + DrcFlashScreen(); + virtual ~DrcFlashScreen(); void Draw(); @@ -48,7 +42,7 @@ private: std::string mErrorString; std::string mFirmwarePath; - FirmwareHeader mFirmwareHeader; + FlashUtils::FirmwareHeader mFirmwareHeader; int32_t mFlashingProgress; bool mUpdateComplete; diff --git a/source/screens/DrcLangScreen.cpp b/source/screens/DrcLangScreen.cpp new file mode 100644 index 0000000..77a175a --- /dev/null +++ b/source/screens/DrcLangScreen.cpp @@ -0,0 +1,229 @@ +#include "DrcLangScreen.hpp" +#include "Gfx.hpp" +#include "ProcUI.hpp" +#include "Utils.hpp" +#include + +#include +#include +#include +#include +#include +#include + +namespace { + +void SoftwareUpdateCallback(IOSError error, void* arg) +{ + DrcLangScreen* drcLangScreen = static_cast(arg); + + drcLangScreen->OnUpdateCompleted(error); +} + +} + +DrcLangScreen::~DrcLangScreen() +{ +} + +void DrcLangScreen::Draw() +{ + DrawTopBar("DrcLangScreen"); + + switch (mState) + { + case STATE_PREPARE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Preparing...", Gfx::ALIGN_CENTER); + break; + } + case STATE_CONFIRM2: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, + Utils::sprintf("Are you really really really sure?\n" + "About to flash langpack.\n" + "Lanuage packs mismatching the firmware\nwill brick your GamePad!\n"), + Gfx::ALIGN_CENTER); + break; + } + case STATE_UPDATE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Starting update...", Gfx::ALIGN_CENTER); + break; + } + case STATE_FLASHING: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2 - 32, 64, Gfx::COLOR_TEXT, Utils::sprintf("Flashing... %d%%", mFlashingProgress), Gfx::ALIGN_CENTER); + Gfx::DrawRect(64, Gfx::SCREEN_HEIGHT / 2 + 32, Gfx::SCREEN_WIDTH - 128, 64, 5, Gfx::COLOR_ACCENT); + Gfx::DrawRectFilled(64, Gfx::SCREEN_HEIGHT / 2 + 32, (Gfx::SCREEN_WIDTH - 128) * (mFlashingProgress / 100.0f), 64, Gfx::COLOR_ACCENT); + break; + } + case STATE_ACTIVATE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Activating language pack...", Gfx::ALIGN_CENTER); + break; + } + case STATE_DONE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, + Utils::sprintf("Done!\n" + "Flashed new language pack"), + Gfx::ALIGN_CENTER); + break; + } + case STATE_ERROR: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error:\n" + mErrorString, Gfx::ALIGN_CENTER); + break; + } + } + + if (mState == STATE_CONFIRM2) { + DrawBottomBar(nullptr, "\ue044 Exit", "\ue000 Confirm / \ue001 Back"); + } else if (mState == STATE_PREPARE || mState == STATE_UPDATE || mState == STATE_FLASHING || mState == STATE_ACTIVATE) { + DrawBottomBar(nullptr, "Please wait...", nullptr); + } else if (mState == STATE_DONE || mState == STATE_ERROR) { + DrawBottomBar(nullptr, nullptr, "\ue001 Back"); + } +} + +bool DrcLangScreen::Update(VPADStatus& input) +{ + flashUtils.ReadFirmwareHeader("/vol/external01/lang.bin", mFirmwareHeader); + uint32_t targetVersion = mFirmwareHeader.version; + switch (mState) + { + case STATE_CONFIRM2: { + if (input.trigger & VPAD_BUTTON_B) { + return false; + } + + if (input.trigger & VPAD_BUTTON_A) { + mState = STATE_UPDATE; + break; + } + break; + } + case STATE_PREPARE: { + // Perform compatibility check + // Writing only language, must obtain firmware from DRC + CCRCDCSoftwareVersion deviceVersion; + CCRCDCSoftwareGetVersion(CCR_CDC_DESTINATION_DRC0, &deviceVersion); + if (!flashUtils.CheckVersionSafety(deviceVersion.runningVersion, targetVersion)) { + mErrorString = "Language version not valid for the DRC firmware!"; + mState = STATE_ERROR; + break; + } + // Copy to MLC so IOS-PAD can install it + mFirmwarePath = "/vol/storage_mlc01/usr/tmp/lang.bin"; + if (!flashUtils.CopyFile("/vol/external01/lang.bin", "storage_mlc01:/usr/tmp/lang.bin")) { + mErrorString = "Failed to copy firmware to MLC"; + mState = STATE_ERROR; + break; + } + mState = STATE_CONFIRM2; + break; + } + case STATE_UPDATE: { + ProcUI::SetHomeButtonMenuEnabled(false); + + if (!flashUtils.CaffeineInvalidate()) { + mErrorString = "Failed to invalidate caffeine."; + mState = STATE_ERROR; + break; + } + + // Abort any potential pending software updates + CCRCDCSoftwareAbort(CCR_CDC_DESTINATION_DRC0); + + // Reattach the DRC in update mode + if (!flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_FWUPDATE, FALSE)) { + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Failed to reattach DRC in update mode."; + mState = STATE_ERROR; + break; + } + + mFlashingProgress = 0; + mUpdateComplete = false; + mUpdateResult = 0; + if (CCRCDCSoftwareLangUpdate(CCR_CDC_DESTINATION_DRC0, mFirmwarePath.c_str(), &targetVersion, SoftwareUpdateCallback, this) != 0) { + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Failed to start software update."; + mState = STATE_ERROR; + break; + } + + mState = STATE_FLASHING; + break; + } + case STATE_FLASHING: { + // Update progress + CCRCDCFWInfo fwInfo{}; + if (CCRCDCGetFWInfo(CCR_CDC_DESTINATION_DRC0, &fwInfo) == 0) { + mFlashingProgress = fwInfo.updateProgress; + } + + OSSleepTicks(OSMillisecondsToTicks(200)); + + // Check if update complete + if (mUpdateComplete) { + if (mUpdateResult == IOS_ERROR_OK) { + mState = STATE_ACTIVATE; + } else { + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Software update failed."; + mState = STATE_ERROR; + } + } + break; + } + case STATE_ACTIVATE: { + // Activate the newly flashed language + uint32_t langActivateSuccess = 0; + CCRCDCSoftwareLangActivate(CCR_CDC_DESTINATION_DRC0, targetVersion, &langActivateSuccess); + if (langActivateSuccess != 0) { + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRC0); + flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE); + mErrorString = "Failed to activate software update."; + mState = STATE_ERROR; + break; + } + + // Put the gamepad back into active mode + OSTime startTime = OSGetSystemTime(); + while (!flashUtils.ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, FALSE)) { + // 10 second timeout + if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 10) { + // At this point we don't really care if it times out or not + break; + } + + OSSleepTicks(OSMillisecondsToTicks(1000)); + } + + mState = STATE_DONE; + break; + } + case STATE_DONE: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + case STATE_ERROR: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + + default: + break; + } + + return true; +} + +void DrcLangScreen::OnUpdateCompleted(int32_t result) +{ + mUpdateComplete = true; + mUpdateResult = result; +} diff --git a/source/screens/DrcLangScreen.hpp b/source/screens/DrcLangScreen.hpp new file mode 100644 index 0000000..bec5891 --- /dev/null +++ b/source/screens/DrcLangScreen.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "Screen.hpp" +#include "FlashUtils.hpp" + +class DrcLangScreen : public Screen +{ +public: +// DrcLangScreen(); + virtual ~DrcLangScreen(); + + void Draw(); + + bool Update(VPADStatus& input); + + void OnUpdateCompleted(int32_t result); +private: + enum State { + STATE_PREPARE, + STATE_CONFIRM2, + STATE_UPDATE, + STATE_FLASHING, + STATE_ACTIVATE, + STATE_DONE, + STATE_ERROR, + } mState = STATE_PREPARE; + + struct FileEntry { + uint16_t icon; + const char* name; + }; + + std::string mErrorString; + std::string mFirmwarePath; + FlashUtils::FirmwareHeader mFirmwareHeader; + int32_t mFlashingProgress; + bool mUpdateComplete; + int32_t mUpdateResult; +}; diff --git a/source/screens/DrcScreenPicker.cpp b/source/screens/DrcScreenPicker.cpp new file mode 100644 index 0000000..7eb9432 --- /dev/null +++ b/source/screens/DrcScreenPicker.cpp @@ -0,0 +1,95 @@ +#include "DrcScreenPicker.hpp" +#include "SetRegionScreen.hpp" +#include "EepromScreen.hpp" +#include "PairScreen.hpp" +#include "EnableDKMenuScreen.hpp" +#include "FormatScreen.hpp" +#include "Gfx.hpp" + +#include + +DrcScreenPicker::DrcScreenPicker() + : mEntries({ + { MENU_ID_SET_REGION, { 0xf0ac, "Set region" }}, + { MENU_ID_DUMP_EEPROM, { 0xf08b, "Dump EEPROMs" }}, + { MENU_ID_DRC_PAIR, { 0xf0c1, "Pair DRC..." }}, + { MENU_ID_ENABLE_DKMENU, { 0xf188, "Enable DK Menu" }}, + { MENU_ID_DRC_RESET, { 0xf079, "Reset DRC" }}, + }) +{ + +} + +DrcScreenPicker::~DrcScreenPicker() +{ +} + +void DrcScreenPicker::Draw() +{ + if (mSubscreen) { + mSubscreen->Draw(); + return; + } + + DrawTopBar("DrcScreenPicker"); + + // draw entries + for (MenuID id = MENU_ID_MIN; id <= MENU_ID_MAX; id = static_cast(id + 1)) { + int yOff = 75 + static_cast(id) * 150; + Gfx::DrawRectFilled(0, yOff, Gfx::SCREEN_WIDTH, 150, Gfx::COLOR_ALT_BACKGROUND); + Gfx::DrawIcon(68, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, mEntries[id].icon); + Gfx::Print(128 + 8, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, mEntries[id].name, Gfx::ALIGN_VERTICAL); + + if (id == mSelected) { + Gfx::DrawRect(0, yOff, Gfx::SCREEN_WIDTH, 150, 8, Gfx::COLOR_HIGHLIGHTED); + } + } + + DrawBottomBar("\ue07d Navigate", "\ue044 Exit", "\ue000 Select / \ue001 Back"); +} + +bool DrcScreenPicker::Update(VPADStatus& input) +{ + if (mSubscreen) { + if (!mSubscreen->Update(input)) { + // subscreen wants to exit + mSubscreen.reset(); + } + return true; + } + + if (input.trigger & VPAD_BUTTON_DOWN) { + if (mSelected < MENU_ID_MAX) { + mSelected = static_cast(mSelected + 1); + } + } else if (input.trigger & VPAD_BUTTON_UP) { + if (mSelected > MENU_ID_MIN) { + mSelected = static_cast(mSelected - 1); + } + } + + if (input.trigger & VPAD_BUTTON_A) { + switch (mSelected) { + case MENU_ID_SET_REGION: + mSubscreen = std::make_unique(); + break; + case MENU_ID_DUMP_EEPROM: + mSubscreen = std::make_unique(); + break; + case MENU_ID_DRC_PAIR: + mSubscreen = std::make_unique(); + break; + case MENU_ID_ENABLE_DKMENU: + mSubscreen = std::make_unique(); + break; + case MENU_ID_DRC_RESET: + mSubscreen = std::make_unique(); + break; + } + } + + if (input.trigger & VPAD_BUTTON_B) { + return false; + } + return true; +} diff --git a/source/screens/DrcScreenPicker.hpp b/source/screens/DrcScreenPicker.hpp new file mode 100644 index 0000000..9b94e04 --- /dev/null +++ b/source/screens/DrcScreenPicker.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "Screen.hpp" +#include +#include + +class DrcScreenPicker : public Screen +{ +public: + DrcScreenPicker(); + virtual ~DrcScreenPicker(); + + void Draw(); + + bool Update(VPADStatus& input); + +private: + std::unique_ptr mSubscreen; + + enum MenuID { + MENU_ID_SET_REGION, + MENU_ID_DUMP_EEPROM, + MENU_ID_DRC_PAIR, + MENU_ID_ENABLE_DKMENU, + MENU_ID_DRC_RESET, + + MENU_ID_MIN = MENU_ID_SET_REGION, + MENU_ID_MAX = MENU_ID_DRC_RESET, + }; + + struct MenuEntry { + uint16_t icon; + const char* name; + }; + std::map mEntries; + MenuID mSelected = MENU_ID_MIN; +}; diff --git a/source/screens/DrhFlashScreen.cpp b/source/screens/DrhFlashScreen.cpp new file mode 100644 index 0000000..81c7183 --- /dev/null +++ b/source/screens/DrhFlashScreen.cpp @@ -0,0 +1,335 @@ +#include "DrhFlashScreen.hpp" +#include "Gfx.hpp" +#include "ProcUI.hpp" +#include "Utils.hpp" +#include + +#include +#include +#include +#include +#include +#include +#include + +#define OS_TITLE_ID_REBOOT 0xFFFFFFFFFFFFFFFEllu + +namespace { +/* +bool ReadFirmwareHeader(const std::string& path, DrhFlashScreen::FirmwareHeader& header) +{ + FILE* f = fopen(path.c_str(), "rb"); + if (!f) { + return false; + } + + if (fread(&header, 1, sizeof(header), f) != sizeof(header)) { + fclose(f); + return false; + } + + fclose(f); + return true; +} + +bool CopyFile(const std::string& srcPath, const std::string& dstPath) +{ + FILE* inf = fopen(srcPath.c_str(), "rb"); + if (!inf) { + return false; + } + + FILE* outf = fopen(dstPath.c_str(), "wb"); + if (!outf) { + fclose(inf); + return false; + } + + uint8_t buf[4096]; + size_t bytesRead; + while ((bytesRead = fread(buf, 1, sizeof(buf), inf)) > 0) { + if (fwrite(buf, 1, bytesRead, outf) != bytesRead) { + fclose(inf); + fclose(outf); + return false; + } + } + + if (ferror(inf)) { + fclose(inf); + fclose(outf); + return false; + } + + fclose(inf); + fclose(outf); + return true; +} + +bool CaffeineInvalidate() +{ + CCRCDCSoftwareVersion version; + CCRCDCSoftwareGetVersion(CCR_CDC_DESTINATION_DRH, &version); + + // Only newer versions have caffeine + if (version.runningVersion >= 0x180a0000) { + return CCRSysCaffeineSetCaffeineSlot(0xff) == 0; + } + + return true; +} + +bool ReattachDRH(CCRCDCDrhStateEnum targetState, BOOL unknown) +{ + // Get the current DRC state + CCRCDCDrhState state; + int32_t res = CCRCDCSysGetDrhState(&state); + if (res != 0) { + return false; + } + + // Nothing to do if we're already in the target state + if (state.state == targetState) { + return true; + } + + // Set target state + state.state = targetState; + res = CCRCDCSysSetDrhState(&state); + if (res != 0) { + return false; + } + + // Check if we're in the state we want + res = CCRCDCSysGetDrhState(&state); + if (res != 0) { + return false; + } + + if (state.state != targetState) { + return false; + } + + return true; +} + +bool AbortUpdate(CCRCDCDestination dest) +{ + OSTime startTime = OSGetSystemTime(); + while (CCRCDCSoftwareAbort(dest) != 0) { + // 3 second timeout + if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 3) { + return false; + } + + OSSleepTicks(OSMillisecondsToTicks(200)); + } + + return true; +}*/ + +void SoftwareUpdateCallback(IOSError error, void* arg) +{ + DrhFlashScreen* drhFlashScreen = static_cast(arg); + + drhFlashScreen->OnUpdateCompleted(error); +} + +} + +DrhFlashScreen::DrhFlashScreen() +{ +} + +DrhFlashScreen::~DrhFlashScreen() +{ +} + +void DrhFlashScreen::Draw() +{ + DrawTopBar("DrhFlashScreen"); + + switch (mState) + { + case STATE_PREPARE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Preparing...", Gfx::ALIGN_CENTER); + break; + } + case STATE_CONFIRM2: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, + Utils::sprintf("DRH update is ready.\n" + "About to flash firmware version 0x%08x.\n" + "Would you like to start the update now?\n", mFirmwareHeader.version), + Gfx::ALIGN_CENTER); + break; + } + case STATE_UPDATE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Starting update...", Gfx::ALIGN_CENTER); + break; + } + case STATE_FLASHING: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2 - 32, 64, Gfx::COLOR_TEXT, Utils::sprintf("Updating... %d%%", mFlashingProgress), Gfx::ALIGN_CENTER); + Gfx::DrawRect(64, Gfx::SCREEN_HEIGHT / 2 + 32, Gfx::SCREEN_WIDTH - 128, 64, 5, Gfx::COLOR_ACCENT); + Gfx::DrawRectFilled(64, Gfx::SCREEN_HEIGHT / 2 + 32, (Gfx::SCREEN_WIDTH - 128) * (mFlashingProgress / 100.0f), 64, Gfx::COLOR_ACCENT); + break; + } + case STATE_ACTIVATE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Activating firmware...", Gfx::ALIGN_CENTER); + break; + } + case STATE_DONE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, + Utils::sprintf("The update is complete.\n" + "The console will now restart."), + Gfx::ALIGN_CENTER); + break; + } + case STATE_ERROR: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error:\n" + mErrorString, Gfx::ALIGN_CENTER); + break; + } + } + + if (mState == STATE_CONFIRM2) { + DrawBottomBar(nullptr, "\ue044 Exit", "\ue000 Confirm / \ue001 Back"); + } else if (mState == STATE_PREPARE || mState == STATE_UPDATE || mState == STATE_FLASHING || mState == STATE_ACTIVATE) { + DrawBottomBar(nullptr, "Please wait...", nullptr); + } else if (mState == STATE_DONE) { + DrawBottomBar(nullptr, nullptr, "\ue000 Reboot"); + } else if (mState == STATE_ERROR) { + DrawBottomBar(nullptr, nullptr, "\ue001 Back"); + } +} + +bool DrhFlashScreen::Update(VPADStatus& input) +{ + switch (mState) + { + case STATE_CONFIRM2: { + if (input.trigger & VPAD_BUTTON_B) { + return false; + } + + if (input.trigger & VPAD_BUTTON_A) { + mState = STATE_UPDATE; + break; + } + break; + } + case STATE_PREPARE: { + if (!flashUtils.ReadFirmwareHeader("/vol/external01/drh_fw.bin", mFirmwareHeader)) { + mErrorString = "Failed to read DRH firmware header"; + mState = STATE_ERROR; + break; + } + // Copy to MLC so IOS-PAD can install it + mFirmwarePath = "/vol/storage_mlc01/usr/tmp/drh_fw.bin"; + if (!flashUtils.CopyFile("/vol/external01/drh_fw.bin", "storage_mlc01:/usr/tmp/drh_fw.bin")) { + mErrorString = "Failed to copy firmware to MLC"; + mState = STATE_ERROR; + break; + } + mState = STATE_CONFIRM2; + break; + } + case STATE_UPDATE: { + ProcUI::SetHomeButtonMenuEnabled(false); + + if (!flashUtils.CaffeineInvalidate()) { + mErrorString = "Failed to invalidate caffeine."; + mState = STATE_ERROR; + break; + } + + // Abort any potential pending software updates + CCRCDCSoftwareAbort(CCR_CDC_DESTINATION_DRH); + + mFlashingProgress = 0; + mUpdateComplete = false; + mUpdateResult = 0; + if (CCRCDCSoftwareUpdate(CCR_CDC_DESTINATION_DRH, mFirmwarePath.c_str(), SoftwareUpdateCallback, this) != 0) { + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRH); + flashUtils.ReattachDRH(CCR_CDC_SYS_DRH_STATE_CAFE, FALSE); + mErrorString = "Failed to start software update."; + mState = STATE_ERROR; + break; + } + + mState = STATE_FLASHING; + break; + } + case STATE_FLASHING: { + // Update progress + CCRCDCFWInfo fwInfo{}; + if (CCRCDCGetFWInfo(CCR_CDC_DESTINATION_DRH, &fwInfo) == 0) { + mFlashingProgress = fwInfo.updateProgress; + } + + OSSleepTicks(OSMillisecondsToTicks(200)); + + // Check if update complete + if (mUpdateComplete) { + if (mUpdateResult == IOS_ERROR_OK) { + mState = STATE_ACTIVATE; + } else { + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRH); + flashUtils.ReattachDRH(CCR_CDC_SYS_DRH_STATE_CAFE, FALSE); + mErrorString = "Software update failed - error " + std::to_string(mUpdateResult); + mState = STATE_ERROR; + } + } + break; + } + case STATE_ACTIVATE: { + // Activate the newly flashed firmware + if (CCRCDCSoftwareActivate(CCR_CDC_DESTINATION_DRH) != 0) { + flashUtils.AbortUpdate(CCR_CDC_DESTINATION_DRH); + flashUtils.ReattachDRH(CCR_CDC_SYS_DRH_STATE_CAFE, FALSE); + mErrorString = "Failed to activate software update."; + mState = STATE_ERROR; + break; + } + + // Put the DRH back into active mode + OSTime startTime = OSGetSystemTime(); + while (!flashUtils.ReattachDRH(CCR_CDC_SYS_DRH_STATE_CAFE, FALSE)) { + // 10 second timeout + if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 10) { + // At this point we don't really care if it times out or not + break; + } + + OSSleepTicks(OSMillisecondsToTicks(1000)); + } + + mState = STATE_DONE; + break; + } + case STATE_DONE: { + if (input.trigger & VPAD_BUTTON_A) { + // Reboot system to apply changes + ProcUI::SetHomeButtonMenuEnabled(true); + OSLaunchTitlev(OS_TITLE_ID_REBOOT, 0, NULL); + } + break; + } + case STATE_ERROR: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + + default: + break; + } + + return true; +} + +void DrhFlashScreen::OnUpdateCompleted(int32_t result) +{ + mUpdateComplete = true; + mUpdateResult = result; +} diff --git a/source/screens/DrhFlashScreen.hpp b/source/screens/DrhFlashScreen.hpp new file mode 100644 index 0000000..bdb3593 --- /dev/null +++ b/source/screens/DrhFlashScreen.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "Screen.hpp" +#include "FlashUtils.hpp" +#include + +class DrhFlashScreen : public Screen +{ + +public: + DrhFlashScreen(); + virtual ~DrhFlashScreen(); + + void Draw(); + + bool Update(VPADStatus& input); + + void OnUpdateCompleted(int32_t result); + +private: + enum State { + STATE_PREPARE, + STATE_CONFIRM2, + STATE_UPDATE, + STATE_FLASHING, + STATE_ACTIVATE, + STATE_DONE, + STATE_ERROR, + } mState = STATE_PREPARE; + + enum FileID { + FILE_ORIGINAL, + FILE_SDCARD, + } mFile = FILE_ORIGINAL; + struct FileEntry { + uint16_t icon; + const char* name; + }; + std::map mFileEntries; + + std::string mErrorString; + std::string mFirmwarePath; + FlashUtils::FirmwareHeader mFirmwareHeader; + + int32_t mFlashingProgress; + bool mUpdateComplete; + int32_t mUpdateResult; +}; diff --git a/source/screens/EepromScreen.cpp b/source/screens/EepromScreen.cpp new file mode 100644 index 0000000..481db51 --- /dev/null +++ b/source/screens/EepromScreen.cpp @@ -0,0 +1,119 @@ +#include "EepromScreen.hpp" +#include "Gfx.hpp" +#include "ProcUI.hpp" +#include "Utils.hpp" +#include + +#include +#include +#include +#include +#include + +namespace { + +bool WriteFile(const uint8_t *source, const std::string& dstPath) +{ + FILE* outf = fopen(dstPath.c_str(), "wb"); + if (!outf) { + return false; + } + if (fwrite(source, 1, 4096, outf) != 0) { // DRC EEPROM size up until "DEADBABE" string appears to be 5E0h bytes, unless you dump the 2nd DRC. What the hell does it add..? + fclose(outf); + return false; + } + + fclose(outf); + return true; +} + +} + +EepromScreen::EepromScreen(){} +EepromScreen::~EepromScreen(){} +void EepromScreen::Draw() +{ + DrawTopBar("EepromScreen"); + + switch (mState) + { + case STATE_DUMP: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Reading data", Gfx::ALIGN_CENTER); + break; + } + case STATE_DONE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, + Utils::sprintf("Saved to eeprom.bin\nPress B"), + Gfx::ALIGN_CENTER); + break; + } + case STATE_ERROR: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error:\n" + mErrorString, Gfx::ALIGN_CENTER); + break; + } + } + if (mState == STATE_ERROR || STATE_DONE) + DrawBottomBar(nullptr, nullptr, "\ue001 Back"); +} + +bool EepromScreen::Update(VPADStatus& input) // This is the core logic part +{ + switch (mState) + { + case STATE_DUMP: { + CCRCDCEepromData readout0; + uint8_t readoutarray0[0x304]; + CCRCDCEepromData readout1; // If a 2nd DRC is present, we will write this + uint8_t readoutarray1[0x304]; + // Read EEPROM + if(CCRCDCPerGetUicEeprom(CCR_CDC_DESTINATION_DRC0, &readout0) != 0){ + mErrorString = "Read failed!"; + mState = STATE_ERROR; + break; + } + // Write to SD card + // Convert to uint8_t so that this can be processed by fwrite + uint8_t versionarray0[4]; + for (int i=0; i<4 ;++i) + versionarray0[i] = ((uint8_t*)&readout0.version)[3-i]; + memcpy(&readoutarray0[0], versionarray0, 4); + memcpy(&readoutarray0[4], readout0.data, 0x300); + WriteFile(readoutarray0, "/vol/external01/eeprom0.bin"); + // If we read the DRC1 EEPROM + if(CCRCDCPerGetUicEeprom(CCR_CDC_DESTINATION_DRC1, &readout1) == 0){ + uint8_t versionarray1[4]; + for (int i=0; i<4 ;++i) + versionarray1[i] = ((uint8_t*)&readout1.version)[3-i]; + memcpy(&readoutarray1[0], versionarray1, 4); + memcpy(&readoutarray1[4], readout1.data, 0x300); + WriteFile(readoutarray1, "/vol/external01/eeprom1.bin"); + } + mState = STATE_DONE; + break; + } + case STATE_DONE: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + case STATE_ERROR: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + + default: + break; + } + + return true; +} + +void EepromScreen::OnDumpCompleted() +{ + mDumpComplete = true; +} diff --git a/source/screens/EepromScreen.hpp b/source/screens/EepromScreen.hpp new file mode 100644 index 0000000..1b449e2 --- /dev/null +++ b/source/screens/EepromScreen.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "Screen.hpp" +#include + +class EepromScreen : public Screen +{ +public: + EepromScreen(); + virtual ~EepromScreen(); + + void Draw(); + + bool Update(VPADStatus& input); + + void OnDumpCompleted(); + +private: + enum State { + STATE_DUMP, + STATE_DONE, + STATE_ERROR, + } mState = STATE_DUMP; + + std::string mErrorString; + + + bool mDumpComplete; +}; diff --git a/source/screens/EnableDKMenuScreen.cpp b/source/screens/EnableDKMenuScreen.cpp index 0f478ba..b088a01 100644 --- a/source/screens/EnableDKMenuScreen.cpp +++ b/source/screens/EnableDKMenuScreen.cpp @@ -181,4 +181,4 @@ bool EnableDKMenuScreen::Update(VPADStatus& input) } return true; -} +} \ No newline at end of file diff --git a/source/screens/EnableDKMenuScreen.hpp b/source/screens/EnableDKMenuScreen.hpp index c10439f..99ec40b 100644 --- a/source/screens/EnableDKMenuScreen.hpp +++ b/source/screens/EnableDKMenuScreen.hpp @@ -28,4 +28,4 @@ private: uint8_t mBoardConfig; bool mDKMenuEnabled; std::string mErrorText; -}; +}; \ No newline at end of file diff --git a/source/screens/FlashScreenPicker.cpp b/source/screens/FlashScreenPicker.cpp new file mode 100644 index 0000000..b2fdb1b --- /dev/null +++ b/source/screens/FlashScreenPicker.cpp @@ -0,0 +1,91 @@ +#include "FlashScreenPicker.hpp" +#include "DrhFlashScreen.hpp" +#include "DrcFlashScreen.hpp" +#include "DrcLangScreen.hpp" +#include "DrcFFlashScreen.hpp" +#include "Gfx.hpp" + +#include + +FlashScreenPicker::FlashScreenPicker() + : mEntries({ + { MENU_ID_DRHFLASH, { 0xf085, "Flash DRH firmware" }}, + { MENU_ID_DRCFLASH, { 0xf1c9, "Flash DRC firmware" }}, + { MENU_ID_DRCLANG, { 0xf302, "Flash DRC language" }}, + { MENU_ID_DRCFFLASH, { 0xe4c7, "Flash DRC fully" }}, + // { MENU_ID_EXIT, { 0xf057, "Exit" }}, + }) +{ + +} + +FlashScreenPicker::~FlashScreenPicker() +{ +} + +void FlashScreenPicker::Draw() +{ + if (mSubscreen) { + mSubscreen->Draw(); + return; + } + + DrawTopBar("FlashScreenPicker"); + + // draw entries + for (MenuID id = MENU_ID_MIN; id <= MENU_ID_MAX; id = static_cast(id + 1)) { + int yOff = 75 + static_cast(id) * 150; + Gfx::DrawRectFilled(0, yOff, Gfx::SCREEN_WIDTH, 150, Gfx::COLOR_ALT_BACKGROUND); + Gfx::DrawIcon(68, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, mEntries[id].icon); + Gfx::Print(128 + 8, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, mEntries[id].name, Gfx::ALIGN_VERTICAL); + + if (id == mSelected) { + Gfx::DrawRect(0, yOff, Gfx::SCREEN_WIDTH, 150, 8, Gfx::COLOR_HIGHLIGHTED); + } + } + + DrawBottomBar("\ue07d Navigate", "\ue044 Exit", "\ue000 Select / \ue001 Back"); +} + +bool FlashScreenPicker::Update(VPADStatus& input) +{ + if (mSubscreen) { + if (!mSubscreen->Update(input)) { + // subscreen wants to exit + mSubscreen.reset(); + } + return true; + } + + if (input.trigger & VPAD_BUTTON_DOWN) { + if (mSelected < MENU_ID_MAX) { + mSelected = static_cast(mSelected + 1); + } + } else if (input.trigger & VPAD_BUTTON_UP) { + if (mSelected > MENU_ID_MIN) { + mSelected = static_cast(mSelected - 1); + } + } + + if (input.trigger & VPAD_BUTTON_A) { + switch (mSelected) { + case MENU_ID_DRHFLASH: + mSubscreen = std::make_unique(); + break; + case MENU_ID_DRCFLASH: + mSubscreen = std::make_unique(); + break; + case MENU_ID_DRCLANG: + mSubscreen = std::make_unique(); + break; + case MENU_ID_DRCFFLASH: + mSubscreen = std::make_unique(); + break; + } + } + + if (input.trigger & VPAD_BUTTON_B) { + return false; + } + return true; +} diff --git a/source/screens/FlashScreenPicker.hpp b/source/screens/FlashScreenPicker.hpp new file mode 100644 index 0000000..d079e91 --- /dev/null +++ b/source/screens/FlashScreenPicker.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "Screen.hpp" +#include +#include + +class FlashScreenPicker : public Screen +{ +public: + FlashScreenPicker(); + virtual ~FlashScreenPicker(); + + void Draw(); + + bool Update(VPADStatus& input); + +private: + std::unique_ptr mSubscreen; + + enum MenuID { + MENU_ID_DRHFLASH, + MENU_ID_DRCFLASH, + MENU_ID_DRCLANG, + MENU_ID_DRCFFLASH, + + MENU_ID_MIN = MENU_ID_DRHFLASH, + MENU_ID_MAX = MENU_ID_DRCFFLASH, + }; + + struct MenuEntry { + uint16_t icon; + const char* name; + }; + std::map mEntries; + MenuID mSelected = MENU_ID_MIN; +}; diff --git a/source/screens/FlashUtils.cpp b/source/screens/FlashUtils.cpp new file mode 100644 index 0000000..b9dd8a7 --- /dev/null +++ b/source/screens/FlashUtils.cpp @@ -0,0 +1,217 @@ +#include "FlashUtils.hpp" +#include + +#include +#include +#include +#include +#include +#include + +FlashUtils flashUtils; + +bool FlashUtils::CopyFile(const std::string& srcPath, const std::string& dstPath) +{ + FILE* inf = fopen(srcPath.c_str(), "rb"); + if (!inf) { + return false; + } + + FILE* outf = fopen(dstPath.c_str(), "wb"); + if (!outf) { + fclose(inf); + return false; + } + + uint8_t buf[4096]; + size_t bytesRead; + while ((bytesRead = fread(buf, 1, sizeof(buf), inf)) > 0) { + if (fwrite(buf, 1, bytesRead, outf) != bytesRead) { + fclose(inf); + fclose(outf); + return false; + } + } + + if (ferror(inf)) { + fclose(inf); + fclose(outf); + return false; + } + + fclose(inf); + fclose(outf); + return true; +} + +bool FlashUtils::ReadFirmwareHeader(const std::string& path, FlashUtils::FirmwareHeader& header) +{ + FILE* f = fopen(path.c_str(), "rb"); + if (!f) { + return false; + } + + if (fread(&header, 1, sizeof(header), f) != sizeof(header)) { + fclose(f); + return false; + } + + fclose(f); + return true; +} + +bool FlashUtils::CheckVersionSafety(const uint32_t firmver, const uint32_t langver) +{ + // Very crude way to do the checks universally, but it works well enough + // We take the firmware version as well as the language version (w/o region - am I stupid?) and compare them + if ((firmver == 0x190c0117 || firmver == 0xfe000000) && ((langver & 0xFFFF00) ^ 0x170200)==0){ // Only patched firmware uses language v5890 + return true; + } else if (firmver == 0x18140116 && not ((langver & 0xFFFF00) ^ 0x160400)){ + return true; + } else if (firmver == 0x17080114 && not ((langver & 0xFFFF00) ^ 0x140000)){ + return true; + } else if ((firmver == 0x151e0113 || firmver == 0x15060113 || firmver == 0x14060113) && not ((langver & 0xFFFF00) ^ 0x130000)){ // 3 versions have used language v4864 + return true; + } else { + return false; + } +} + +bool FlashUtils::CaffeineInvalidate() +{ + CCRCDCSoftwareVersion version; + CCRCDCSoftwareGetVersion(CCR_CDC_DESTINATION_DRC0, &version); + + // Only newer versions have caffeine + if (version.runningVersion >= 0x180a0000) { + return CCRSysCaffeineSetCaffeineSlot(0xff) == 0; + } + + return true; +} + +bool FlashUtils::WaitForEeprom(uint32_t drcSlot) +{ + uint8_t val; + OSTime startTime = OSGetSystemTime(); + while (CCRCFGGetCachedEeprom(drcSlot, 0, &val, sizeof(val)) == -1) { + // 2 second timeout + if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 2) { + return false; + } + + OSSleepTicks(OSMillisecondsToTicks(200)); + } + + return true; +} + +bool FlashUtils::ReattachDRC(CCRCDCDestination dest, CCRCDCDrcStateEnum targetState, BOOL unknown) +{ + // Get the current DRC state + CCRCDCDrcState state; + int32_t res = CCRCDCSysGetDrcState(dest, &state); + if (res != 0) { + return false; + } + + // Not sure what state 3 is + if (state.state == CCR_CDC_DRC_STATE_STANDALONE) { + state.state = CCR_CDC_DRC_STATE_ACTIVE; + } + + // Nothing to do if we're already in the target state + if (state.state == targetState) { + return true; + } + + __CCRSysInitReattach(dest - CCR_CDC_DESTINATION_DRC0); + + // Set target state + state.state = targetState; + res = CCRCDCSysSetDrcState(dest, &state); + if (res != 0) { + return false; + } + + // Wait for the DRC to reattach + res = __CCRSysWaitReattach(dest - CCR_CDC_DESTINATION_DRC0, unknown); + if (res != 0) { + return false; + } + + // Wait for EEPROM + if (!WaitForEeprom(dest - CCR_CDC_DESTINATION_DRC0)) { + return false; + } + + // Check if we're in the state we want + res = CCRCDCSysGetDrcState(dest, &state); + if (res != 0) { + return false; + } + + if (state.state != targetState) { + return false; + } + + return true; +} + +bool FlashUtils::ReattachDRH(CCRCDCDrhStateEnum targetState, BOOL unknown) +{ + // Get the current DRC state + CCRCDCDrhState state; + int32_t res = CCRCDCSysGetDrhState(&state); + if (res != 0) { + return false; + } + + // Nothing to do if we're already in the target state + if (state.state == targetState) { + return true; + } + + // Set target state + state.state = targetState; + res = CCRCDCSysSetDrhState(&state); + if (res != 0) { + return false; + } + + // Check if we're in the state we want + res = CCRCDCSysGetDrhState(&state); + if (res != 0) { + return false; + } + + if (state.state != targetState) { + return false; + } + + return true; +} + + +bool FlashUtils::AbortUpdate(CCRCDCDestination dest) +{ + OSTime startTime = OSGetSystemTime(); + while (CCRCDCSoftwareAbort(dest) != 0) { + // 3 second timeout + if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 3) { + return false; + } + + OSSleepTicks(OSMillisecondsToTicks(200)); + } + + return true; +} +/* +void SoftwareUpdateCallback(IOSError error, void* arg) +{ + FlashUtils* flashUtils = static_cast(arg); + + flashUtils->OnUpdateCompleted(error); +} +*/ \ No newline at end of file diff --git a/source/screens/FlashUtils.hpp b/source/screens/FlashUtils.hpp new file mode 100644 index 0000000..31ae4bd --- /dev/null +++ b/source/screens/FlashUtils.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "Screen.hpp" + +#include +#include +#include +#include +#include +#include + +class FlashUtils +{ +public: + void OnUpdateCompleted(int32_t result); + struct FirmwareHeader { + uint32_t version; + uint32_t blockSize; + uint32_t sequencePerSession; + uint32_t imageSize; + }; + bool CopyFile(const std::string& srcPath, const std::string& dstPath); + bool ReadFirmwareHeader(const std::string& path, FlashUtils::FirmwareHeader& header); + bool CheckVersionSafety(const uint32_t firmver, const uint32_t langver); + bool CaffeineInvalidate(); + bool WaitForEeprom(uint32_t drcSlot); + bool ReattachDRC(CCRCDCDestination dest, CCRCDCDrcStateEnum targetState, BOOL unknown); + bool ReattachDRH(CCRCDCDrhStateEnum targetState, BOOL unknown); + bool AbortUpdate(CCRCDCDestination dest); + +}; + +extern FlashUtils flashUtils; + diff --git a/source/screens/FormatScreen.cpp b/source/screens/FormatScreen.cpp new file mode 100644 index 0000000..f393aa3 --- /dev/null +++ b/source/screens/FormatScreen.cpp @@ -0,0 +1,83 @@ +#include "FormatScreen.hpp" +#include "Gfx.hpp" +#include "ProcUI.hpp" +#include "Utils.hpp" +#include + +#include +#include +#include + +namespace {} + +FormatScreen::FormatScreen(){ + CCRCDCSetMultiDrc(1); // Having multiple DRCs enabled will make it impossible to erase a gamepad. +} +FormatScreen::~FormatScreen(){} +void FormatScreen::Draw() +{ + DrawTopBar("FormatScreen"); + + switch (mState) + { + case STATE_UPDATE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Resetting data", Gfx::ALIGN_CENTER); + break; + } + case STATE_DONE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, + Utils::sprintf("Done!"), + Gfx::ALIGN_CENTER); + break; + } + case STATE_ERROR: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error:\n" + mErrorString, Gfx::ALIGN_CENTER); + break; + } + } +} + +bool FormatScreen::Update(VPADStatus& input) // This is the core logic part +{ + switch (mState) + { + case STATE_UPDATE: { + ProcUI::SetHomeButtonMenuEnabled(false); + + + // Abort any potential pending software updates + CCRCDCSoftwareAbort(CCR_CDC_DESTINATION_DRC0); + + // Erase DRC + if(CCRSysInitializeSettings() != 0){ + mErrorString = "Erase failed."; + mState = STATE_ERROR; + break; + } + mState = STATE_DONE; + break; + } + case STATE_DONE: { + CCRCDCSysConsoleShutdownInd(CCR_CDC_DESTINATION_DRC0); // Power off device to apply changes + SYSLaunchMenu(); + break; + } + case STATE_ERROR: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + + default: + break; + } + + return true; +} + +void FormatScreen::OnEraseCompleted() +{ + mEraseComplete = true; +} diff --git a/source/screens/FormatScreen.hpp b/source/screens/FormatScreen.hpp new file mode 100644 index 0000000..fd40fda --- /dev/null +++ b/source/screens/FormatScreen.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "Screen.hpp" +#include + +class FormatScreen : public Screen +{ +public: + FormatScreen(); + virtual ~FormatScreen(); + + void Draw(); + + bool Update(VPADStatus& input); + + void OnEraseCompleted(); + +private: + enum State { + STATE_UPDATE, + STATE_DONE, + STATE_ERROR, + } mState = STATE_UPDATE; + + std::string mErrorString; + + + bool mEraseComplete; +}; diff --git a/source/screens/MainScreen.cpp b/source/screens/MainScreen.cpp index 414370a..e32448e 100644 --- a/source/screens/MainScreen.cpp +++ b/source/screens/MainScreen.cpp @@ -153,4 +153,4 @@ bool MainScreen::Update(VPADStatus& input) void MainScreen::DrawStatus(std::string status, SDL_Color color) { Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, color, status, Gfx::ALIGN_CENTER); -} +} \ No newline at end of file diff --git a/source/screens/MenuScreen.cpp b/source/screens/MenuScreen.cpp index f1f502a..8387ded 100644 --- a/source/screens/MenuScreen.cpp +++ b/source/screens/MenuScreen.cpp @@ -1,21 +1,19 @@ #include "MenuScreen.hpp" #include "Gfx.hpp" #include "AboutScreen.hpp" -#include "FlashScreen.hpp" +#include "FlashScreenPicker.hpp" +#include "DrcScreenPicker.hpp" #include "InfoScreen.hpp" -#include "SetRegionScreen.hpp" -#include "EnableDKMenuScreen.hpp" #include MenuScreen::MenuScreen() : mEntries({ - { MENU_ID_INFO, { 0xf085, "Show DRC/DRH information" }}, - { MENU_ID_FLASH, { 0xf1c9, "Flash firmware" }}, - { MENU_ID_SET_REGION, { 0xf0ac, "Set region" }}, - { MENU_ID_ENABLE_DKMENU, { 0xf188, "Enable DK Menu" }}, - { MENU_ID_ABOUT, { 0xf05a, "About DRXUtil" }}, - // { MENU_ID_EXIT, { 0xf057, "Exit" }}, + { MENU_ID_INFO, { 0xf085, "Show DRC/DRH information" }}, + { MENU_ID_FLASH, { 0xf019, "Flash..."}}, + { MENU_ID_DRCOPS, { 0xf10a, "DRC operations..."}}, + { MENU_ID_ABOUT, { 0xf05a, "About DRXUtil" }}, + // { MENU_ID_EXIT, { 0xf057, "Exit" }}, }) { @@ -75,13 +73,10 @@ bool MenuScreen::Update(VPADStatus& input) mSubscreen = std::make_unique(); break; case MENU_ID_FLASH: - mSubscreen = std::make_unique(); + mSubscreen = std::make_unique(); break; - case MENU_ID_SET_REGION: - mSubscreen = std::make_unique(); - break; - case MENU_ID_ENABLE_DKMENU: - mSubscreen = std::make_unique(); + case MENU_ID_DRCOPS: + mSubscreen = std::make_unique(); break; case MENU_ID_ABOUT: mSubscreen = std::make_unique(); diff --git a/source/screens/MenuScreen.hpp b/source/screens/MenuScreen.hpp index 9b3ac46..cd6a668 100644 --- a/source/screens/MenuScreen.hpp +++ b/source/screens/MenuScreen.hpp @@ -20,8 +20,7 @@ private: enum MenuID { MENU_ID_INFO, MENU_ID_FLASH, - MENU_ID_SET_REGION, - MENU_ID_ENABLE_DKMENU, + MENU_ID_DRCOPS, MENU_ID_ABOUT, MENU_ID_MIN = MENU_ID_INFO, diff --git a/source/screens/PairScreen.cpp b/source/screens/PairScreen.cpp new file mode 100644 index 0000000..f678df6 --- /dev/null +++ b/source/screens/PairScreen.cpp @@ -0,0 +1,195 @@ +#include "PairScreen.hpp" +#include "Gfx.hpp" +#include "ProcUI.hpp" +#include "Utils.hpp" +#include + +#include +#include +#include +#include + +#define TIMEOUT_SECONDS 45 +namespace {} + +PairScreen::PairScreen():mTargetEntries({{DRC0, {0x31, "Main DRC"}}, {DRC1, {0x32, "Additional DRC"}},}){ + + // Initialize IM + imHandle = IM_Open(); + imRequest = (IMRequest*) memalign(0x40, sizeof(IMRequest)); + // Allocate a separate request for IM_CancelGetEventNotify to avoid conflict with the pending IM_GetEventNotify request + imCancelRequest = (IMRequest*) memalign(0x40, sizeof(IMRequest)); + + // Init CCRSys + CCRSysInit(); +} +PairScreen::~PairScreen(){ + CCRSysExit(); + + // Close IM + IM_CancelGetEventNotify(imHandle, imCancelRequest, nullptr, nullptr); + IM_Close(imHandle); + free(imCancelRequest); + free(imRequest); +} +void PairScreen::Draw() +{ + DrawTopBar("PairScreen"); + + switch (mState) + { + case STATE_SELECT_TARGET: { + for (TargetID id = DRC0; id <= DRC1; id = static_cast(id + 1)) { + int yOff = 75 + static_cast(id) * 150; + Gfx::DrawRectFilled(0, yOff, Gfx::SCREEN_WIDTH, 150, Gfx::COLOR_ALT_BACKGROUND); + Gfx::DrawIcon(68, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, mTargetEntries[id].icon); + Gfx::Print(128 + 8, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, mTargetEntries[id].name, Gfx::ALIGN_VERTICAL); + + if (id == mTarget) { + Gfx::DrawRect(0, yOff, Gfx::SCREEN_WIDTH, 150, 8, Gfx::COLOR_HIGHLIGHTED); + } + } + break; + } + case STATE_GETPIN: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Getting pincode...", Gfx::ALIGN_CENTER); + break; + } + case STATE_WAITING: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, + Utils::sprintf("Got WPS PIN! \n" + "Pincode: %i\n" + "GamePad keyboard: 0213", pincodebuffer), Gfx::ALIGN_CENTER); + break; + } + case STATE_DONE: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, + Utils::sprintf("DRC paired successfully"), + Gfx::ALIGN_CENTER); + break; + } + case STATE_ERROR: { + Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error:\n" + mErrorString, Gfx::ALIGN_CENTER); + break; + } + } + if (mState == STATE_SELECT_TARGET) { + DrawBottomBar("\ue07d Navigate", "\ue044 Exit", "\ue000 Confirm / \ue001 Back"); + } else if (mState == STATE_GETPIN) { + DrawBottomBar(nullptr, "Please wait...", nullptr); + } else if (mState == STATE_WAITING) { + DrawBottomBar(nullptr, "Press SYNC to cancel pairing", nullptr); + } else { + DrawBottomBar(nullptr, nullptr, "\ue001 Back"); + } +} + +bool PairScreen::Update(VPADStatus& input) // This is the core logic part +{ + switch (mState) + { + case STATE_SELECT_TARGET: { + if (input.trigger & VPAD_BUTTON_B) { + return false; + } + + if (input.trigger & VPAD_BUTTON_A) { + mState = STATE_GETPIN; + break; + } + + if (input.trigger & VPAD_BUTTON_DOWN) { + if (mTarget < DRC1) { + mTarget = static_cast(mTarget + 1); + } + } else if (input.trigger & VPAD_BUTTON_UP) { + if (mTarget > DRC0) { + mTarget = static_cast(mTarget - 1); + } + } + break; + } + case STATE_GETPIN: { + cancelPairing = false; + imEventMask = IM_EVENT_SYNC; + IM_GetEventNotify(imHandle, imRequest, &imEventMask, PairScreen::SyncButtonCallback, &imEventMask); + // Get a PIN code to display. + if (CCRSysGetPincode(&pincodebuffer) != 0){ + mErrorString = "Could not obtain the PIN!"; + mState = STATE_ERROR; + break; + } + switch (mTarget) { + case DRC0: { + if (CCRSysStartPairing(0, TIMEOUT_SECONDS) != 0) { + mErrorString = "Could not start pairing!"; + mState = STATE_ERROR; + break; + } + mState = STATE_WAITING; + break; + } + case DRC1: { + if (CCRCDCSetMultiDrc(2) != 0) { + mErrorString = "Could not enable MultiDRC mode!"; + mState = STATE_ERROR; + break; + } + if (CCRSysStartPairing(1, TIMEOUT_SECONDS) != 0) { + mErrorString = "Could not start pairing!"; + mState = STATE_ERROR; + break; + } + mState = STATE_WAITING; + break; + } + } + } + case STATE_WAITING: { + CCRSysPairingState pairingState = CCRSysGetPairingState(); + if (pairingState == CCR_SYS_PAIRING_TIMED_OUT || cancelPairing) { + // Pairing has timed out or was cancelled + CCRSysStopPairing(); + mErrorString = "Pairing was stopped!"; + mState = STATE_ERROR; + break; + } else if (pairingState == CCR_SYS_PAIRING_FINISHED) { + // DRC was paired + mState = STATE_DONE; + } + break; + } + case STATE_DONE: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + case STATE_ERROR: { + if (input.trigger & VPAD_BUTTON_B) { + ProcUI::SetHomeButtonMenuEnabled(true); + return false; + } + break; + } + default: + break; + } + return true; +} + +void PairScreen::SyncButtonCallback(IOSError error, void* arg) +{ + uint32_t event = *(uint32_t*) arg; + + // Cancel pairing if the sync button was pressed + if (error == IOS_ERROR_OK && (event & IM_EVENT_SYNC)) { + cancelPairing = true; + } +} + +void PairScreen::OnPairingCompleted() +{ + mPairingComplete = true; +} diff --git a/source/screens/PairScreen.hpp b/source/screens/PairScreen.hpp new file mode 100644 index 0000000..fa8d59d --- /dev/null +++ b/source/screens/PairScreen.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "Screen.hpp" +#include +#include + +class PairScreen : public Screen +{ +public: + PairScreen(); + virtual ~PairScreen(); + + void Draw(); + + bool Update(VPADStatus& input); + + void OnPairingCompleted(); + +private: + static void SyncButtonCallback(IOSError error, void* arg); + static inline bool cancelPairing; + IOSHandle imHandle; + IMRequest* imRequest; + IMRequest* imCancelRequest; + IMEventMask imEventMask; + enum State { + STATE_SELECT_TARGET, + STATE_GETPIN, + STATE_WAITING, + STATE_DONE, + STATE_ERROR, + } mState = STATE_SELECT_TARGET; + struct TargetEntry { + uint16_t icon; + const char* name; + }; + enum TargetID { + DRC0, + DRC1, + } mTarget = DRC0; + std::map mTargetEntries; + std::string mErrorString; + uint32_t pincodebuffer; + bool mPairingComplete; +}; diff --git a/source/screens/SetRegionScreen.hpp b/source/screens/SetRegionScreen.hpp index 46861ed..69b05e4 100644 --- a/source/screens/SetRegionScreen.hpp +++ b/source/screens/SetRegionScreen.hpp @@ -34,4 +34,6 @@ private: REGION_AUSTRALIA = 6, } mRegion = REGION_JAPAN; std::map mRegionEntries; + + std::string mErrorText; };