mirror of
https://github.com/GaryOderNichts/DRXUtil.git
synced 2025-03-12 22:36:42 +01:00
Replace EnableDKMenu with EditDeviceInfo
This commit is contained in:
parent
fe99770abb
commit
8887076dad
4
Makefile
4
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
|
||||
|
||||
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
92
source/MessageBox.cpp
Normal 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
48
source/MessageBox.hpp
Normal 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;
|
||||
};
|
269
source/screens/EditDeviceInfoScreen.cpp
Normal file
269
source/screens/EditDeviceInfoScreen.cpp
Normal 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;
|
||||
}
|
46
source/screens/EditDeviceInfoScreen.hpp
Normal file
46
source/screens/EditDeviceInfoScreen.hpp
Normal 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;
|
||||
};
|
@ -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;
|
||||
}
|
@ -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;
|
||||
};
|
@ -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>();
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user