Replace EnableDKMenu with EditDeviceInfo

This commit is contained in:
GaryOderNichts 2025-03-02 00:08:40 +01:00
parent fe99770abb
commit 8887076dad
11 changed files with 531 additions and 232 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
@ -54,7 +54,7 @@ CXXFLAGS := $(CFLAGS) -std=gnu++20
ASFLAGS := $(ARCH)
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
LIBS := -lSDL2 -lSDL2_ttf -lfreetype -lharfbuzz -lfreetype -lpng -lbz2 -lz -lmocha -lwut
LIBS := -lSDL2 -lSDL2_ttf -lSDL2_gfx -lfreetype -lharfbuzz -lfreetype -lpng -lbz2 -lz -lmocha -lwut
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level

View File

@ -1,5 +1,22 @@
/*
* Copyright (C) 2024 GaryOderNichts
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Gfx.hpp"
#include "SDL_FontCache.h"
#include <SDL2_gfxPrimitives.h>
#include <map>
#include <cstdarg>
@ -106,13 +123,13 @@ bool Init()
return false;
}
if (!FC_LoadFont_RW(monospaceFont, renderer, SDL_RWFromMem((void*)ter_u32b_bdf, ter_u32b_bdf_size), 1, 32, Gfx::COLOR_BLACK, TTF_STYLE_NORMAL)) {
if (!FC_LoadFont_RW(monospaceFont, renderer, SDL_RWFromConstMem(ter_u32b_bdf, ter_u32b_bdf_size), 1, 32, Gfx::COLOR_BLACK, TTF_STYLE_NORMAL)) {
FC_FreeFont(monospaceFont);
return false;
}
// icons @256 should be large enough for our needs
iconFont = TTF_OpenFontRW(SDL_RWFromMem((void*)fa_solid_900_ttf, fa_solid_900_ttf_size), 1, 256);
iconFont = TTF_OpenFontRW(SDL_RWFromConstMem(fa_solid_900_ttf, fa_solid_900_ttf_size), 1, 256);
if (!iconFont) {
return false;
}
@ -151,9 +168,7 @@ void Render()
void DrawRectFilled(int x, int y, int w, int h, SDL_Color color)
{
SDL_Rect rect{x, y, w, h};
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderFillRect(renderer, &rect);
boxRGBA(renderer, x, y, x + w, y + h, color.r, color.g, color.b, color.a);
}
void DrawRect(int x, int y, int w, int h, int borderSize, SDL_Color color)
@ -164,6 +179,28 @@ void DrawRect(int x, int y, int w, int h, int borderSize, SDL_Color color)
DrawRectFilled(x + w - borderSize, y, borderSize, h, color);
}
void DrawRectRoundedFilled(int x, int y, int w, int h, int radius, SDL_Color color)
{
roundedBoxRGBA(renderer, x, y, x + w, y + h, radius, color.r, color.g, color.b, color.a);
}
void DrawCircleFilled(int x, int y, int radius, SDL_Color color)
{
filledCircleRGBA(renderer, x, y, radius, color.r, color.g, color.b, color.a);
}
void DrawCircle(int x, int y, int radius, int borderSize, SDL_Color color)
{
if (borderSize <= 0) {
return;
}
// TODO eh this is not nice
while (borderSize--) {
circleRGBA(renderer, x, y, radius - borderSize, color.r, color.g, color.b, color.a);
}
}
void DrawIcon(int x, int y, int size, SDL_Color color, Uint16 icon, AlignFlags align, double angle)
{
SDL_Texture* iconTex = LoadIcon(icon);

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2024 GaryOderNichts
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <SDL.h>
@ -48,6 +64,12 @@ void DrawRectFilled(int x, int y, int w, int h, SDL_Color color);
void DrawRect(int x, int y, int w, int h, int borderSize, SDL_Color color);
void DrawRectRoundedFilled(int x, int y, int w, int h, int radius, SDL_Color color);
void DrawCircleFilled(int x, int y, int radius, SDL_Color color);
void DrawCircle(int x, int y, int radius, int borderSize, SDL_Color color);
void DrawIcon(int x, int y, int size, SDL_Color color, Uint16 icon, AlignFlags align = ALIGN_CENTER, double angle = 0.0);
int GetIconWidth(int size, Uint16 icon);

92
source/MessageBox.cpp Normal file
View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2024 GaryOderNichts
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "MessageBox.hpp"
#include "Gfx.hpp"
MessageBox::MessageBox(const std::string& title, const std::string& message, const std::vector<Option> options)
: mTitle(title),
mMessage(message),
mOptions(std::move(options)),
mSelected(0)
{
}
MessageBox::~MessageBox()
{
}
void MessageBox::Draw()
{
// Dim the background
Gfx::DrawRectFilled(0, 0, Gfx::SCREEN_WIDTH, Gfx::SCREEN_WIDTH, { 0, 0, 0, 0xa0 });
// Draw the background
Gfx::DrawRectRoundedFilled(128, 128, Gfx::SCREEN_WIDTH - 256, Gfx::SCREEN_HEIGHT - 256, 60, Gfx::COLOR_ALT_BACKGROUND);
// Print message
Gfx::Print(Gfx::SCREEN_WIDTH / 2, 128 + 64, 80, Gfx::COLOR_TEXT, mTitle, Gfx::ALIGN_HORIZONTAL | Gfx::ALIGN_TOP);
Gfx::Print(Gfx::SCREEN_WIDTH / 2, 128 + 64 + Gfx::GetTextHeight(80, mTitle) + 8, 40, Gfx::COLOR_TEXT, mMessage, Gfx::ALIGN_HORIZONTAL | Gfx::ALIGN_TOP);
uint32_t xSize = Gfx::SCREEN_WIDTH - 256 - 32 - 8 * (mOptions.size() - 1);
xSize /= mOptions.size();
uint32_t xOff = 128 + 16;
for (size_t i = 0; i < mOptions.size(); i++) {
Gfx::DrawRectFilled(xOff, Gfx::SCREEN_HEIGHT - 300, xSize, 128, Gfx::COLOR_BACKGROUND);
uint32_t iconWidth = 0;
if (mOptions[i].icon) {
iconWidth = Gfx::GetIconWidth(64, mOptions[i].icon);
}
uint32_t textStart = xOff + (xSize - iconWidth - Gfx::GetTextWidth(64, mOptions[i].text)) / 2;
if (iconWidth) {
Gfx::DrawIcon(textStart, Gfx::SCREEN_HEIGHT - 300 + 64, 64, Gfx::COLOR_TEXT, mOptions[i].icon);
}
Gfx::Print(textStart + iconWidth, Gfx::SCREEN_HEIGHT - 300 + 64, 64, Gfx::COLOR_TEXT, mOptions[i].text, Gfx::ALIGN_VERTICAL | Gfx::ALIGN_LEFT);
if (i == mSelected) {
Gfx::DrawRect(xOff, Gfx::SCREEN_HEIGHT - 300, xSize, 128, 8, Gfx::COLOR_HIGHLIGHTED);
}
xOff += xSize + 8;
}
}
bool MessageBox::Update(const VPADStatus& input)
{
if (input.trigger & VPAD_BUTTON_B) {
return false;
}
if (input.trigger & VPAD_BUTTON_A) {
mOptions[mSelected].callback();
return false;
}
if (input.trigger & VPAD_BUTTON_RIGHT) {
if (mSelected < mOptions.size() - 1) {
mSelected++;
}
} else if (input.trigger & VPAD_BUTTON_LEFT) {
if (mSelected > 0) {
mSelected--;
}
}
return true;
}

48
source/MessageBox.hpp Normal file
View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2024 GaryOderNichts
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <utility>
#include <vector>
#include <string>
#include <functional>
#include <vpad/input.h>
class MessageBox {
public:
struct Option {
uint16_t icon;
std::string text;
std::function<void(void)> callback;
};
public:
MessageBox(const std::string& title, const std::string& message, const std::vector<Option> options);
virtual ~MessageBox();
virtual void Draw();
virtual bool Update(const VPADStatus& input);
protected:
std::string mTitle;
std::string mMessage;
std::vector<Option> mOptions;
size_t mSelected;
};

View File

@ -0,0 +1,269 @@
#include "EditDeviceInfoScreen.hpp"
#include "Utils.hpp"
#include "Gfx.hpp"
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
namespace {
bool SetDeviceInfo(uint8_t byte)
{
CCRCDCUicConfig cfg{};
// custom config id which was added by the gamepad cfw
cfg.configId = 4;
// device info byte + crc16
cfg.size = 3;
cfg.data[0] = byte;
uint16_t crc = CCRCDCCalcCRC16(cfg.data, 1);
cfg.data[1] = crc & 0xff;
cfg.data[2] = (crc >> 8) & 0xff;
if (CCRCDCPerSetUicConfig(CCR_CDC_DESTINATION_DRC0, &cfg) != 0) {
return false;
}
// Also update the cached eeprom
return CCRCFGSetCachedEeprom(0, 0x106, cfg.data, cfg.size) == 0;
}
bool GetDeviceInfo(uint8_t& deviceInfo)
{
uint8_t data[3];
if (CCRCFGGetCachedEeprom(0, 0x106, data, 3) != 0) {
return false;
}
uint16_t crc = (uint16_t) data[2] << 8 | data[1];
if (CCRCDCCalcCRC16(&data[0], 1) != crc) {
return false;
}
deviceInfo = data[0];
return true;
}
}
EditDeviceInfoScreen::EditDeviceInfoScreen()
: mSelected(OPTION_ID_MIN),
mDeviceInfo(0),
mIsDevelopment(false),
mIsUnshipped(false),
mHasChanged(false),
mIsApplying(false),
mErrorText(),
mMessageBox()
{
}
EditDeviceInfoScreen::~EditDeviceInfoScreen()
{
}
void EditDeviceInfoScreen::Draw()
{
DrawTopBar("EditDeviceInfoScreen");
switch (mState)
{
case STATE_GET_DEVICE_INFO:
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Retrieving device info...", Gfx::ALIGN_CENTER);
break;
case STATE_SELECT: {
int yOff = 75;
// OPTION_ID_PRODUCTION_TYPE
Gfx::DrawRectFilled(0, yOff, Gfx::SCREEN_WIDTH, 150, Gfx::COLOR_ALT_BACKGROUND);
Gfx::Print(128 + 8, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, "Production Type", Gfx::ALIGN_VERTICAL);
if (mIsDevelopment) {
Gfx::Print(Gfx::SCREEN_WIDTH - (128 + 8), yOff + 150 / 2, 60, Gfx::COLOR_TEXT, "< Development >", Gfx::ALIGN_VERTICAL | Gfx::ALIGN_RIGHT);
} else {
Gfx::Print(Gfx::SCREEN_WIDTH - (128 + 8), yOff + 150 / 2, 60, Gfx::COLOR_TEXT, "< Mass >", Gfx::ALIGN_VERTICAL | Gfx::ALIGN_RIGHT);
}
yOff += 150;
// OPTION_ID_SHIPMENT_STATUS
Gfx::DrawRectFilled(0, yOff, Gfx::SCREEN_WIDTH, 150, Gfx::COLOR_ALT_BACKGROUND);
Gfx::Print(128 + 8, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, "Shipment Status", Gfx::ALIGN_VERTICAL);
if (mIsUnshipped) {
Gfx::Print(Gfx::SCREEN_WIDTH - (128 + 8), yOff + 150 / 2, 60, Gfx::COLOR_TEXT, "< Unshipped >", Gfx::ALIGN_VERTICAL | Gfx::ALIGN_RIGHT);
} else {
Gfx::Print(Gfx::SCREEN_WIDTH - (128 + 8), yOff + 150 / 2, 60, Gfx::COLOR_TEXT, "< Shipped >", Gfx::ALIGN_VERTICAL | Gfx::ALIGN_RIGHT);
}
yOff += 150;
// OPTION_ID_APPLY_CHANGES
if (mHasChanged) {
Gfx::DrawRectFilled(0, yOff, Gfx::SCREEN_WIDTH, 150, Gfx::COLOR_ALT_BACKGROUND);
Gfx::Print(128 + 8, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, "Apply changes", Gfx::ALIGN_VERTICAL);
Gfx::Print(Gfx::SCREEN_WIDTH - (128 + 8), yOff + 150 / 2, 60, Gfx::COLOR_TEXT, "\ue000", Gfx::ALIGN_VERTICAL | Gfx::ALIGN_RIGHT);
yOff += 150;
}
// Draw selected indicator
Gfx::DrawRect(0, 75 + mSelected * 150, Gfx::SCREEN_WIDTH, 150, 8, Gfx::COLOR_HIGHLIGHTED);
break;
}
case STATE_ERROR:
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error!\n" + mErrorText, Gfx::ALIGN_CENTER);
break;
}
if (mState == STATE_SELECT) {
if (mSelected == OPTION_ID_APPLY_CHANGES) {
DrawBottomBar("\ue07d Navigate", "\ue044 Exit", "\ue000 Confirm / \ue001 Back");
} else {
DrawBottomBar("\ue07d Navigate", "\ue044 Exit", "\ue07e Modify / \ue001 Back");
}
} else if (mState == STATE_GET_DEVICE_INFO) {
DrawBottomBar(nullptr, "Please wait...", nullptr);
} else {
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back");
}
if (mMessageBox) {
mMessageBox->Draw();
}
if (mIsApplying) {
Gfx::DrawRectFilled(0, 0, Gfx::SCREEN_WIDTH, Gfx::SCREEN_WIDTH, { 0, 0, 0, 0xa0 });
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Updating device info...", Gfx::ALIGN_CENTER);
}
}
bool EditDeviceInfoScreen::Update(VPADStatus& input)
{
if (mMessageBox) {
if (!mMessageBox->Update(input)) {
mMessageBox.reset();
}
return true;
}
switch (mState)
{
case STATE_GET_DEVICE_INFO:
if (!GetDeviceInfo(mDeviceInfo)) {
mErrorText = "Failed to retrieve current device info.";
mState = STATE_ERROR;
break;
}
mIsDevelopment = (mDeviceInfo & 0x0C) == 0;
mIsUnshipped = (mDeviceInfo & 0x03) == 0;
mState = STATE_SELECT;
break;
case STATE_SELECT: {
if (mIsApplying) {
break;
}
if (input.trigger & VPAD_BUTTON_B) {
return false;
}
if (input.trigger & (VPAD_BUTTON_LEFT | VPAD_BUTTON_RIGHT)) {
switch (mSelected) {
case OPTION_ID_PRODUCTION_TYPE:
mIsDevelopment = !mIsDevelopment;
break;
case OPTION_ID_SHIPMENT_STATUS:
mIsUnshipped = !mIsUnshipped;
break;
default: break;
}
if (mSelected == OPTION_ID_SHIPMENT_STATUS && mIsUnshipped) {
mMessageBox = std::make_unique<MessageBox>(
"You are about to unship your Gamepad!",
"Are you sure you want to set your Gamepad to an unshipped state?\n"
"Only do this if you know what you're doing.\n"
"If your Gamepad is set to unshipped, it will only boot into the Service Menu.\n"
"If your Service Menu doesn't work or is corrupted, your Gamepad will be bricked.\n"
"\n"
"To get out of the Service Menu navigate to \"UIC Menu\" and select\n"
"\"Write Shipping Flag On\" using the \ue000 button. Use the \ue045 button to execute it.\n"
"Afterwards, restart the Gamepad.",
std::vector{
MessageBox::Option{0, "\ue000 OK", [this]() {} },
}
);
}
mHasChanged = true;
}
if (input.trigger & VPAD_BUTTON_A) {
if (mSelected == OPTION_ID_APPLY_CHANGES) {
mMessageBox = std::make_unique<MessageBox>(
"Are you sure?",
"Are you sure you want to update the device info?\n"
"Only do this if you know what you're doing.",
std::vector{
MessageBox::Option{0, "\ue001 Back", [this]() {} },
MessageBox::Option{0xf00c, "Update", [this]() {
mIsApplying = true;
}},
}
);
}
break;
}
if (input.trigger & VPAD_BUTTON_DOWN) {
if (mSelected < (OPTION_ID_MAX - 1) || (mSelected < OPTION_ID_MAX && mHasChanged)) {
mSelected = static_cast<OptionID>(mSelected + 1);
}
} else if (input.trigger & VPAD_BUTTON_UP) {
if (mSelected > OPTION_ID_MIN) {
mSelected = static_cast<OptionID>(mSelected - 1);
}
}
break;
}
case STATE_ERROR:
if (input.trigger & VPAD_BUTTON_B) {
return false;
}
break;
}
if (mIsApplying) {
uint8_t newDeviceConfig = mDeviceInfo;
// Clear/Set the production type mass bit
if (mIsDevelopment) {
newDeviceConfig &= ~0x0C;
} else {
newDeviceConfig |= 0x04;
}
// Clear/Set the shipment flag
if (mIsUnshipped) {
newDeviceConfig &= ~0x03;
} else {
newDeviceConfig |= 0x01;
}
if (!SetDeviceInfo(newDeviceConfig)) {
mErrorText = "Failed to set device info.\n(Is the firmware modified properly?)";
mState = STATE_ERROR;
} else {
mMessageBox = std::make_unique<MessageBox>(
"Device info updated!",
"Restart the Gamepad to apply changes.",
std::vector{
MessageBox::Option{0, "\ue000 OK", [this]() {
mSelected = OPTION_ID_MIN;
mHasChanged = false;
}},
}
);
}
mIsApplying = false;
}
return true;
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "Screen.hpp"
#include <map>
#include <memory>
#include <nsysccr/cdc.h>
#include "MessageBox.hpp"
class EditDeviceInfoScreen : public Screen
{
public:
EditDeviceInfoScreen();
virtual ~EditDeviceInfoScreen();
void Draw();
bool Update(VPADStatus& input);
private:
enum OptionID {
OPTION_ID_PRODUCTION_TYPE,
OPTION_ID_SHIPMENT_STATUS,
OPTION_ID_APPLY_CHANGES,
OPTION_ID_MIN = OPTION_ID_PRODUCTION_TYPE,
OPTION_ID_MAX = OPTION_ID_APPLY_CHANGES,
};
size_t mSelected;
enum State {
STATE_GET_DEVICE_INFO,
STATE_SELECT,
STATE_ERROR,
} mState = STATE_GET_DEVICE_INFO;
uint8_t mDeviceInfo;
bool mIsDevelopment;
bool mIsUnshipped;
bool mHasChanged;
bool mIsApplying;
std::string mErrorText;
std::unique_ptr<MessageBox> mMessageBox;
};

View File

@ -1,184 +0,0 @@
#include "EnableDKMenuScreen.hpp"
#include "Utils.hpp"
#include "Gfx.hpp"
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
namespace {
bool SetBoardConfig(uint8_t byte)
{
CCRCDCUicConfig cfg{};
// custom config id which was added by the gamepad cfw
cfg.configId = 4;
// board config byte + crc16
cfg.size = 3;
cfg.data[0] = byte;
uint16_t crc = CCRCDCCalcCRC16(cfg.data, 1);
cfg.data[1] = crc & 0xff;
cfg.data[2] = (crc >> 8) & 0xff;
if (CCRCDCPerSetUicConfig(CCR_CDC_DESTINATION_DRC0, &cfg) != 0) {
return false;
}
// Also update the cached eeprom
return CCRCFGSetCachedEeprom(0, 0x106, cfg.data, cfg.size) == 0;
}
bool GetBoardConfig(uint8_t& boardConfig)
{
uint8_t data[3];
if (CCRCFGGetCachedEeprom(0, 0x106, data, 3) != 0) {
return false;
}
uint16_t crc = (uint16_t) data[2] << 8 | data[1];
if (CCRCDCCalcCRC16(&data[0], 1) != crc) {
return false;
}
boardConfig = data[0];
return true;
}
}
EnableDKMenuScreen::EnableDKMenuScreen()
: mConfirm(false),
mBoardConfig(0),
mDKMenuEnabled(false),
mErrorText()
{
}
EnableDKMenuScreen::~EnableDKMenuScreen()
{
}
void EnableDKMenuScreen::Draw()
{
DrawTopBar("EnableDKMenuScreen");
switch (mState)
{
case STATE_GET_BOARD_CONFIG:
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Retrieving board config...", Gfx::ALIGN_CENTER);
break;
case STATE_SELECT:
Gfx::Print(Gfx::SCREEN_WIDTH / 2, 128, 64, Gfx::COLOR_TEXT,
Utils::sprintf("DK Menu is currently %s.\nDo you want to %s it?",
mDKMenuEnabled ? "enabled" : "disabled",
mDKMenuEnabled ? "disable" : "enable"),
Gfx::ALIGN_HORIZONTAL | Gfx::ALIGN_TOP);
for (int i = 0; i < 2; i++) {
int yOff = 360 + static_cast<int>(i) * 100;
Gfx::DrawRectFilled(0, yOff, Gfx::SCREEN_WIDTH, 100, Gfx::COLOR_ALT_BACKGROUND);
Gfx::Print(68, yOff + 100 / 2, 50, Gfx::COLOR_TEXT, i == 0 ? "Back" : (mDKMenuEnabled ? "Disable" : "Enable"), Gfx::ALIGN_VERTICAL);
if (mConfirm == !!i) {
Gfx::DrawRect(0, yOff, Gfx::SCREEN_WIDTH, 100, 8, Gfx::COLOR_HIGHLIGHTED);
}
}
break;
case STATE_CONFIRM:
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT,
Utils::sprintf("Are you sure you want to %s the DK Menu?\n"
"(Note that updating the board config\nonly works with a modified firmware)",
mDKMenuEnabled ? "disable" : "enable"), Gfx::ALIGN_CENTER);
break;
case STATE_UPDATE:
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Updating board config...", Gfx::ALIGN_CENTER);
break;
case STATE_DONE:
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT,
Utils::sprintf("Done! DK Menu has been %s.\n%s",
mDKMenuEnabled ? "disabled" : "enabled",
mDKMenuEnabled ? "" : "(Hold L + ZL while powering on the Gamepad to open.)"),
Gfx::ALIGN_CENTER);
break;
case STATE_ERROR:
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error!\n" + mErrorText, Gfx::ALIGN_CENTER);
break;
}
if (mState == STATE_SELECT) {
DrawBottomBar("\ue07d Navigate", "\ue044 Exit", "\ue000 Confirm / \ue001 Back");
} else if (mState == STATE_CONFIRM) {
DrawBottomBar(nullptr, "\ue044 Exit", "\ue000 Confirm / \ue001 Back");
} else if (mState == STATE_UPDATE || mState == STATE_GET_BOARD_CONFIG) {
DrawBottomBar(nullptr, "Please wait...", nullptr);
} else {
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back");
}
}
bool EnableDKMenuScreen::Update(VPADStatus& input)
{
switch (mState)
{
case STATE_GET_BOARD_CONFIG:
if (!GetBoardConfig(mBoardConfig)) {
mErrorText = "Failed to retrieve current board configuration.";
mState = STATE_ERROR;
break;
}
mDKMenuEnabled = (mBoardConfig & 0x0C) == 0;
mState = STATE_SELECT;
break;
case STATE_SELECT:
if (input.trigger & VPAD_BUTTON_B) {
return false;
}
if (input.trigger & VPAD_BUTTON_A) {
if (mConfirm) {
mState = STATE_CONFIRM;
} else {
return false;
}
break;
}
if (input.trigger & (VPAD_BUTTON_DOWN | VPAD_BUTTON_UP)) {
mConfirm = !mConfirm;
}
break;
case STATE_CONFIRM:
if (input.trigger & VPAD_BUTTON_B) {
return false;
}
if (input.trigger & VPAD_BUTTON_A) {
mState = STATE_UPDATE;
break;
}
break;
case STATE_UPDATE: {
uint8_t newBoardConfig = mBoardConfig;
// Clear/Set the is retail bits
if (mDKMenuEnabled) {
newBoardConfig |= 0x04;
} else {
newBoardConfig &= ~0x0C;
}
if (!SetBoardConfig(newBoardConfig)) {
mErrorText = "Failed to set board config.\n(Is the firmware modified properly?)";
mState = STATE_ERROR;
break;
}
mState = STATE_DONE;
break;
}
case STATE_DONE:
case STATE_ERROR:
if (input.trigger & VPAD_BUTTON_B) {
return false;
}
break;
}
return true;
}

View File

@ -1,31 +0,0 @@
#pragma once
#include "Screen.hpp"
#include <map>
#include <nsysccr/cdc.h>
class EnableDKMenuScreen : public Screen
{
public:
EnableDKMenuScreen();
virtual ~EnableDKMenuScreen();
void Draw();
bool Update(VPADStatus& input);
private:
enum State {
STATE_GET_BOARD_CONFIG,
STATE_SELECT,
STATE_CONFIRM,
STATE_UPDATE,
STATE_DONE,
STATE_ERROR,
} mState = STATE_GET_BOARD_CONFIG;
bool mConfirm;
uint8_t mBoardConfig;
bool mDKMenuEnabled;
std::string mErrorText;
};

View File

@ -4,18 +4,18 @@
#include "FlashScreen.hpp"
#include "InfoScreen.hpp"
#include "SetRegionScreen.hpp"
#include "EnableDKMenuScreen.hpp"
#include "EditDeviceInfoScreen.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, { 0xf1c9, "Flash firmware" }},
{ MENU_ID_SET_REGION, { 0xf0ac, "Set region" }},
{ MENU_ID_EDIT_DEVICE_INFO, { 0xf1de, "Edit device info" }},
{ MENU_ID_ABOUT, { 0xf05a, "About DRXUtil" }},
// { MENU_ID_EXIT, { 0xf057, "Exit" }},
})
{
@ -80,8 +80,8 @@ bool MenuScreen::Update(VPADStatus& input)
case MENU_ID_SET_REGION:
mSubscreen = std::make_unique<SetRegionScreen>();
break;
case MENU_ID_ENABLE_DKMENU:
mSubscreen = std::make_unique<EnableDKMenuScreen>();
case MENU_ID_EDIT_DEVICE_INFO:
mSubscreen = std::make_unique<EditDeviceInfoScreen>();
break;
case MENU_ID_ABOUT:
mSubscreen = std::make_unique<AboutScreen>();

View File

@ -21,7 +21,7 @@ private:
MENU_ID_INFO,
MENU_ID_FLASH,
MENU_ID_SET_REGION,
MENU_ID_ENABLE_DKMENU,
MENU_ID_EDIT_DEVICE_INFO,
MENU_ID_ABOUT,
MENU_ID_MIN = MENU_ID_INFO,