New functionality

This commit is contained in:
team-orangeBlue 2024-08-31 16:50:15 +03:00
parent fe99770abb
commit 609a314aba
27 changed files with 2048 additions and 203 deletions

View File

@ -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 :=

View File

@ -0,0 +1,284 @@
#include "DrcFFlashScreen.hpp"
#include "Gfx.hpp"
#include "ProcUI.hpp"
#include "Utils.hpp"
#include <cstdio>
#include <coreinit/mcp.h>
#include <coreinit/thread.h>
#include <coreinit/filesystem_fsa.h>
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
#include <nn/ccr.h>
namespace {
void SoftwareUpdateCallback(IOSError error, void* arg)
{
DrcFFlashScreen* drcFFlashScreen = static_cast<DrcFFlashScreen*>(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;
}

View File

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

View File

@ -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<FlashScreen*>(arg);
DrcFlashScreen* drcFlashScreen = static_cast<DrcFlashScreen*>(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;

View File

@ -1,21 +1,15 @@
#pragma once
#include "Screen.hpp"
#include "FlashUtils.hpp"
#include <map>
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;

View File

@ -0,0 +1,229 @@
#include "DrcLangScreen.hpp"
#include "Gfx.hpp"
#include "ProcUI.hpp"
#include "Utils.hpp"
#include <cstdio>
#include <coreinit/mcp.h>
#include <coreinit/thread.h>
#include <coreinit/filesystem_fsa.h>
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
#include <nn/ccr.h>
namespace {
void SoftwareUpdateCallback(IOSError error, void* arg)
{
DrcLangScreen* drcLangScreen = static_cast<DrcLangScreen*>(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;
}

View File

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

View File

@ -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 <vector>
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<MenuID>(id + 1)) {
int yOff = 75 + static_cast<int>(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<MenuID>(mSelected + 1);
}
} else if (input.trigger & VPAD_BUTTON_UP) {
if (mSelected > MENU_ID_MIN) {
mSelected = static_cast<MenuID>(mSelected - 1);
}
}
if (input.trigger & VPAD_BUTTON_A) {
switch (mSelected) {
case MENU_ID_SET_REGION:
mSubscreen = std::make_unique<SetRegionScreen>();
break;
case MENU_ID_DUMP_EEPROM:
mSubscreen = std::make_unique<EepromScreen>();
break;
case MENU_ID_DRC_PAIR:
mSubscreen = std::make_unique<PairScreen>();
break;
case MENU_ID_ENABLE_DKMENU:
mSubscreen = std::make_unique<EnableDKMenuScreen>();
break;
case MENU_ID_DRC_RESET:
mSubscreen = std::make_unique<FormatScreen>();
break;
}
}
if (input.trigger & VPAD_BUTTON_B) {
return false;
}
return true;
}

View File

@ -0,0 +1,37 @@
#pragma once
#include "Screen.hpp"
#include <memory>
#include <map>
class DrcScreenPicker : public Screen
{
public:
DrcScreenPicker();
virtual ~DrcScreenPicker();
void Draw();
bool Update(VPADStatus& input);
private:
std::unique_ptr<Screen> 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<MenuID, MenuEntry> mEntries;
MenuID mSelected = MENU_ID_MIN;
};

View File

@ -0,0 +1,335 @@
#include "DrhFlashScreen.hpp"
#include "Gfx.hpp"
#include "ProcUI.hpp"
#include "Utils.hpp"
#include <cstdio>
#include <coreinit/mcp.h>
#include <coreinit/thread.h>
#include <coreinit/filesystem_fsa.h>
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
#include <nn/ccr.h>
#include <coreinit/launch.h>
#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<DrhFlashScreen*>(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;
}

View File

@ -0,0 +1,48 @@
#pragma once
#include "Screen.hpp"
#include "FlashUtils.hpp"
#include <map>
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<FileID, FileEntry> mFileEntries;
std::string mErrorString;
std::string mFirmwarePath;
FlashUtils::FirmwareHeader mFirmwareHeader;
int32_t mFlashingProgress;
bool mUpdateComplete;
int32_t mUpdateResult;
};

View File

@ -0,0 +1,119 @@
#include "EepromScreen.hpp"
#include "Gfx.hpp"
#include "ProcUI.hpp"
#include "Utils.hpp"
#include <cstdio>
#include <coreinit/mcp.h>
#include <coreinit/thread.h>
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
#include <nn/ccr.h>
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;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include "Screen.hpp"
#include <map>
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;
};

View File

@ -181,4 +181,4 @@ bool EnableDKMenuScreen::Update(VPADStatus& input)
}
return true;
}
}

View File

@ -28,4 +28,4 @@ private:
uint8_t mBoardConfig;
bool mDKMenuEnabled;
std::string mErrorText;
};
};

View File

@ -0,0 +1,91 @@
#include "FlashScreenPicker.hpp"
#include "DrhFlashScreen.hpp"
#include "DrcFlashScreen.hpp"
#include "DrcLangScreen.hpp"
#include "DrcFFlashScreen.hpp"
#include "Gfx.hpp"
#include <vector>
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<MenuID>(id + 1)) {
int yOff = 75 + static_cast<int>(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<MenuID>(mSelected + 1);
}
} else if (input.trigger & VPAD_BUTTON_UP) {
if (mSelected > MENU_ID_MIN) {
mSelected = static_cast<MenuID>(mSelected - 1);
}
}
if (input.trigger & VPAD_BUTTON_A) {
switch (mSelected) {
case MENU_ID_DRHFLASH:
mSubscreen = std::make_unique<DrhFlashScreen>();
break;
case MENU_ID_DRCFLASH:
mSubscreen = std::make_unique<DrcFlashScreen>();
break;
case MENU_ID_DRCLANG:
mSubscreen = std::make_unique<DrcLangScreen>();
break;
case MENU_ID_DRCFFLASH:
mSubscreen = std::make_unique<DrcFFlashScreen>();
break;
}
}
if (input.trigger & VPAD_BUTTON_B) {
return false;
}
return true;
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "Screen.hpp"
#include <memory>
#include <map>
class FlashScreenPicker : public Screen
{
public:
FlashScreenPicker();
virtual ~FlashScreenPicker();
void Draw();
bool Update(VPADStatus& input);
private:
std::unique_ptr<Screen> 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<MenuID, MenuEntry> mEntries;
MenuID mSelected = MENU_ID_MIN;
};

View File

@ -0,0 +1,217 @@
#include "FlashUtils.hpp"
#include <cstdio>
#include <coreinit/mcp.h>
#include <coreinit/thread.h>
#include <coreinit/filesystem_fsa.h>
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
#include <nn/ccr.h>
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<FlashUtils*>(arg);
flashUtils->OnUpdateCompleted(error);
}
*/

View File

@ -0,0 +1,34 @@
#pragma once
#include "Screen.hpp"
#include <coreinit/mcp.h>
#include <coreinit/thread.h>
#include <coreinit/filesystem_fsa.h>
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
#include <nn/ccr.h>
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;

View File

@ -0,0 +1,83 @@
#include "FormatScreen.hpp"
#include "Gfx.hpp"
#include "ProcUI.hpp"
#include "Utils.hpp"
#include <cstdio>
#include <nsysccr/cdc.h>
#include <sysapp/launch.h>
#include <nn/ccr.h>
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;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include "Screen.hpp"
#include <map>
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;
};

View File

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

View File

@ -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 <vector>
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<InfoScreen>();
break;
case MENU_ID_FLASH:
mSubscreen = std::make_unique<FlashScreen>();
mSubscreen = std::make_unique<FlashScreenPicker>();
break;
case MENU_ID_SET_REGION:
mSubscreen = std::make_unique<SetRegionScreen>();
break;
case MENU_ID_ENABLE_DKMENU:
mSubscreen = std::make_unique<EnableDKMenuScreen>();
case MENU_ID_DRCOPS:
mSubscreen = std::make_unique<DrcScreenPicker>();
break;
case MENU_ID_ABOUT:
mSubscreen = std::make_unique<AboutScreen>();

View File

@ -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,

View File

@ -0,0 +1,195 @@
#include "PairScreen.hpp"
#include "Gfx.hpp"
#include "ProcUI.hpp"
#include "Utils.hpp"
#include <cstdio>
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
#include <nn/ccr.h>
#include <malloc.h>
#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<TargetID>(id + 1)) {
int yOff = 75 + static_cast<int>(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<TargetID>(mTarget + 1);
}
} else if (input.trigger & VPAD_BUTTON_UP) {
if (mTarget > DRC0) {
mTarget = static_cast<TargetID>(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;
}

View File

@ -0,0 +1,45 @@
#pragma once
#include "Screen.hpp"
#include <map>
#include <coreinit/im.h>
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<TargetID, TargetEntry> mTargetEntries;
std::string mErrorString;
uint32_t pincodebuffer;
bool mPairingComplete;
};

View File

@ -34,4 +34,6 @@ private:
REGION_AUSTRALIA = 6,
} mRegion = REGION_JAPAN;
std::map<Region, std::string> mRegionEntries;
std::string mErrorText;
};