2016-04-05 05:27:28 +02:00
|
|
|
/*
|
2016-07-05 16:05:53 +02:00
|
|
|
* This file is part of Luma3DS
|
2018-06-14 18:13:57 +02:00
|
|
|
* Copyright (C) 2016-2018 Aurora Wright, TuxSH
|
2016-07-05 16:05:53 +02:00
|
|
|
*
|
|
|
|
* 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 3 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
2017-06-05 02:02:04 +02:00
|
|
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
|
|
|
* * Requiring preservation of specified reasonable legal notices or
|
|
|
|
* author attributions in that material or in the Appropriate Legal
|
|
|
|
* Notices displayed by works containing it.
|
|
|
|
* * Prohibiting misrepresentation of the origin of that material,
|
|
|
|
* or requiring that modified versions of such material be marked in
|
|
|
|
* reasonable ways as different from the original version.
|
2016-04-05 05:27:28 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
2016-08-27 16:00:15 +02:00
|
|
|
#include "memory.h"
|
|
|
|
#include "fs.h"
|
2018-01-05 19:34:05 +01:00
|
|
|
#include "strings.h"
|
2016-04-05 05:27:28 +02:00
|
|
|
#include "utils.h"
|
2016-06-10 21:48:22 +02:00
|
|
|
#include "screen.h"
|
2016-04-05 05:27:28 +02:00
|
|
|
#include "draw.h"
|
2018-08-21 22:07:18 +02:00
|
|
|
#include "emunand.h"
|
2016-04-05 05:27:28 +02:00
|
|
|
#include "buttons.h"
|
2016-08-28 15:50:11 +02:00
|
|
|
#include "pin.h"
|
2016-04-05 05:27:28 +02:00
|
|
|
|
2016-10-10 01:34:53 +02:00
|
|
|
CfgData configData;
|
2017-04-13 03:38:36 +02:00
|
|
|
ConfigurationStatus needConfig;
|
2017-06-18 22:31:21 +02:00
|
|
|
static CfgData oldConfig;
|
2016-10-10 01:34:53 +02:00
|
|
|
|
2016-10-08 01:46:41 +02:00
|
|
|
bool readConfig(void)
|
2016-08-27 16:00:15 +02:00
|
|
|
{
|
2017-04-13 03:38:36 +02:00
|
|
|
bool ret;
|
|
|
|
|
2016-10-12 02:28:08 +02:00
|
|
|
if(fileRead(&configData, CONFIG_FILE, sizeof(CfgData)) != sizeof(CfgData) ||
|
2016-08-27 16:00:15 +02:00
|
|
|
memcmp(configData.magic, "CONF", 4) != 0 ||
|
|
|
|
configData.formatVersionMajor != CONFIG_VERSIONMAJOR ||
|
|
|
|
configData.formatVersionMinor != CONFIG_VERSIONMINOR)
|
|
|
|
{
|
2017-06-18 22:31:21 +02:00
|
|
|
memset(&configData, 0, sizeof(CfgData));
|
2016-11-15 19:29:48 +01:00
|
|
|
|
2017-04-13 03:38:36 +02:00
|
|
|
ret = false;
|
2016-08-27 16:00:15 +02:00
|
|
|
}
|
2017-04-13 03:38:36 +02:00
|
|
|
else ret = true;
|
|
|
|
|
2017-06-18 22:31:21 +02:00
|
|
|
oldConfig = configData;
|
2016-08-27 16:00:15 +02:00
|
|
|
|
2017-04-13 03:38:36 +02:00
|
|
|
return ret;
|
2016-08-27 16:00:15 +02:00
|
|
|
}
|
|
|
|
|
2017-06-06 02:12:18 +02:00
|
|
|
void writeConfig(bool isConfigOptions)
|
2016-08-27 16:00:15 +02:00
|
|
|
{
|
2017-06-22 15:49:23 +02:00
|
|
|
//If the configuration is different from previously, overwrite it.
|
2017-06-18 22:31:21 +02:00
|
|
|
if(needConfig != CREATE_CONFIGURATION && ((isConfigOptions && configData.config == oldConfig.config && configData.multiConfig == oldConfig.multiConfig) ||
|
2017-06-22 15:49:23 +02:00
|
|
|
(!isConfigOptions && configData.bootConfig == oldConfig.bootConfig))) return;
|
2016-11-15 19:29:48 +01:00
|
|
|
|
|
|
|
if(needConfig == CREATE_CONFIGURATION)
|
2016-08-27 16:00:15 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
memcpy(configData.magic, "CONF", 4);
|
|
|
|
configData.formatVersionMajor = CONFIG_VERSIONMAJOR;
|
|
|
|
configData.formatVersionMinor = CONFIG_VERSIONMINOR;
|
2017-06-06 02:12:18 +02:00
|
|
|
|
|
|
|
needConfig = MODIFY_CONFIGURATION;
|
2016-11-15 19:29:48 +01:00
|
|
|
}
|
2016-08-31 16:11:44 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(!fileWrite(&configData, CONFIG_FILE, sizeof(CfgData)))
|
|
|
|
error("Error writing the configuration file");
|
2016-08-27 16:00:15 +02:00
|
|
|
}
|
|
|
|
|
2017-05-20 05:09:48 +02:00
|
|
|
void configMenu(bool oldPinStatus, u32 oldPinMode)
|
2016-04-05 05:27:28 +02:00
|
|
|
{
|
2017-08-16 18:02:35 +02:00
|
|
|
static const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
|
|
|
|
"Screen brightness: 4( ) 3( ) 2( ) 1( )",
|
|
|
|
"Splash: Off( ) Before( ) After( ) payloads",
|
2017-12-29 04:02:06 +01:00
|
|
|
"Splash duration: 1( ) 3( ) 5( ) 7( ) seconds",
|
2017-08-16 18:02:35 +02:00
|
|
|
"PIN lock: Off( ) 4( ) 6( ) 8( ) digits",
|
|
|
|
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *singleOptionsText[] = { "( ) Autoboot EmuNAND",
|
|
|
|
"( ) Use EmuNAND FIRM if booting with R",
|
|
|
|
"( ) Enable loading external FIRMs and modules",
|
|
|
|
"( ) Enable game patching",
|
|
|
|
"( ) Show NAND or user string in System Settings",
|
|
|
|
"( ) Show GBA boot screen in patched AGB_FIRM",
|
|
|
|
"( ) Set developer UNITINFO",
|
|
|
|
"( ) Disable ARM11 exception handlers",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *optionsDescription[] = { "Select the default EmuNAND.\n\n"
|
|
|
|
"It will be booted when no\n"
|
|
|
|
"directional pad buttons are pressed.",
|
|
|
|
|
|
|
|
"Select the screen brightness.",
|
|
|
|
|
|
|
|
"Enable splash screen support.\n\n"
|
|
|
|
"\t* 'Before payloads' displays it\n"
|
|
|
|
"before booting payloads\n"
|
|
|
|
"(intended for splashes that display\n"
|
|
|
|
"button hints).\n\n"
|
|
|
|
"\t* 'After payloads' displays it\n"
|
|
|
|
"afterwards.",
|
|
|
|
|
2017-12-29 03:50:06 +01:00
|
|
|
"Select how long the splash screen\n"
|
|
|
|
"displays.\n\n"
|
|
|
|
"This has no effect if the splash\n"
|
|
|
|
"screen is not enabled.",
|
|
|
|
|
2017-08-16 18:02:35 +02:00
|
|
|
"Activate a PIN lock.\n\n"
|
|
|
|
"The PIN will be asked each time\n"
|
|
|
|
"Luma3DS boots.\n\n"
|
|
|
|
"4, 6 or 8 digits can be selected.\n\n"
|
|
|
|
"The ABXY buttons and the directional\n"
|
|
|
|
"pad buttons can be used as keys.\n\n"
|
|
|
|
"A message can also be displayed\n"
|
|
|
|
"(refer to the wiki for instructions).",
|
|
|
|
|
|
|
|
"Select the New 3DS CPU mode.\n\n"
|
|
|
|
"This won't apply to\n"
|
|
|
|
"New 3DS exclusive/enhanced games.\n\n"
|
|
|
|
"'Clock+L2' can cause issues with some\n"
|
|
|
|
"games.",
|
|
|
|
|
|
|
|
"If enabled, an EmuNAND\n"
|
|
|
|
"will be launched on boot.\n\n"
|
|
|
|
"Otherwise, SysNAND will.\n\n"
|
|
|
|
"Hold L on boot to switch NAND.\n\n"
|
|
|
|
"To use a different EmuNAND from the\n"
|
|
|
|
"default, hold a directional pad button\n"
|
|
|
|
"(Up/Right/Down/Left equal EmuNANDs\n"
|
|
|
|
"1/2/3/4).",
|
|
|
|
|
|
|
|
"If enabled, when holding R on boot\n"
|
|
|
|
"SysNAND will be booted with an\n"
|
|
|
|
"EmuNAND FIRM.\n\n"
|
|
|
|
"Otherwise, an EmuNAND will be booted\n"
|
|
|
|
"with the SysNAND FIRM.\n\n"
|
|
|
|
"To use a different EmuNAND from the\n"
|
|
|
|
"default, hold a directional pad button\n"
|
|
|
|
"(Up/Right/Down/Left equal EmuNANDs\n"
|
|
|
|
"1/2/3/4), also add A if you have\n"
|
|
|
|
"a matching payload.",
|
|
|
|
|
|
|
|
"Enable loading external FIRMs and\n"
|
|
|
|
"system modules.\n\n"
|
|
|
|
"This isn't needed in most cases.\n\n"
|
|
|
|
"Refer to the wiki for instructions.",
|
|
|
|
|
|
|
|
"Enable overriding the region and\n"
|
|
|
|
"language configuration and the usage\n"
|
|
|
|
"of patched code binaries, exHeaders,\n"
|
|
|
|
"IPS code patches and LayeredFS\n"
|
|
|
|
"for specific games.\n\n"
|
|
|
|
"Also makes certain DLCs\n"
|
|
|
|
"for out-of-region games work.\n\n"
|
|
|
|
"Refer to the wiki for instructions.",
|
|
|
|
|
|
|
|
"Enable showing the current NAND/FIRM:\n\n"
|
|
|
|
"\t* Sys = SysNAND\n"
|
|
|
|
"\t* Emu = EmuNAND 1\n"
|
|
|
|
"\t* EmuX = EmuNAND X\n"
|
|
|
|
"\t* SysE = SysNAND with EmuNAND 1 FIRM\n"
|
|
|
|
"\t* SyEX = SysNAND with EmuNAND X FIRM\n"
|
|
|
|
"\t* EmuS = EmuNAND 1 with SysNAND FIRM\n"
|
|
|
|
"\t* EmXS = EmuNAND X with SysNAND FIRM\n\n"
|
|
|
|
"or a user-defined custom string in\n"
|
|
|
|
"System Settings.\n\n"
|
|
|
|
"Refer to the wiki for instructions.",
|
|
|
|
|
|
|
|
"Enable showing the GBA boot screen\n"
|
|
|
|
"when booting GBA games.",
|
|
|
|
|
|
|
|
"Make the console be always detected\n"
|
|
|
|
"as a development unit, and conversely.\n"
|
|
|
|
"(which breaks online features, amiibo\n"
|
|
|
|
"and retail CIAs, but allows installing\n"
|
|
|
|
"and booting some developer software).\n\n"
|
|
|
|
"Only select this if you know what you\n"
|
|
|
|
"are doing!",
|
|
|
|
|
|
|
|
"Disables the fatal error exception\n"
|
|
|
|
"handlers for the ARM11 CPU.\n\n"
|
|
|
|
"Note: Disabling the exception handlers\n"
|
|
|
|
"will disqualify you from submitting\n"
|
|
|
|
"issues or bug reports to the Luma3DS\n"
|
|
|
|
"GitHub repository!"
|
|
|
|
};
|
2016-09-13 23:01:38 +02:00
|
|
|
|
2018-08-21 22:07:18 +02:00
|
|
|
FirmwareSource nandType = FIRMWARE_SYSNAND;
|
|
|
|
if(isSdMode)
|
|
|
|
{
|
|
|
|
nandType = FIRMWARE_EMUNAND;
|
|
|
|
locateEmuNand(&nandType);
|
|
|
|
}
|
|
|
|
|
2016-04-12 23:18:07 +02:00
|
|
|
struct multiOption {
|
2016-09-13 14:54:14 +02:00
|
|
|
u32 posXs[4];
|
|
|
|
u32 posY;
|
2016-04-05 05:27:28 +02:00
|
|
|
u32 enabled;
|
2016-10-08 01:46:41 +02:00
|
|
|
bool visible;
|
2016-04-12 23:18:07 +02:00
|
|
|
} multiOptions[] = {
|
2018-09-28 03:08:35 +02:00
|
|
|
{ .visible = nandType == FIRMWARE_EMUNAND },
|
2018-01-05 19:34:05 +01:00
|
|
|
{ .visible = true },
|
|
|
|
{ .visible = true },
|
|
|
|
{ .visible = true },
|
|
|
|
{ .visible = true },
|
|
|
|
{ .visible = ISN3DS },
|
2016-10-08 01:46:41 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct singleOption {
|
|
|
|
u32 posY;
|
|
|
|
bool enabled;
|
|
|
|
bool visible;
|
|
|
|
} singleOptions[] = {
|
2018-09-28 03:08:35 +02:00
|
|
|
{ .visible = nandType == FIRMWARE_EMUNAND },
|
|
|
|
{ .visible = nandType == FIRMWARE_EMUNAND },
|
2016-10-08 01:46:41 +02:00
|
|
|
{ .visible = true },
|
2016-10-11 16:52:51 +02:00
|
|
|
{ .visible = true },
|
2016-10-08 01:46:41 +02:00
|
|
|
{ .visible = true },
|
|
|
|
{ .visible = true },
|
|
|
|
{ .visible = true },
|
2017-05-18 01:05:56 +02:00
|
|
|
{ .visible = true }
|
2016-04-12 23:18:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
//Calculate the amount of the various kinds of options and pre-select the first single one
|
|
|
|
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
|
2016-10-08 01:46:41 +02:00
|
|
|
singleOptionsAmount = sizeof(singleOptions) / sizeof(struct singleOption),
|
2016-04-13 15:19:35 +02:00
|
|
|
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
|
2016-11-13 18:10:59 +01:00
|
|
|
selectedOption,
|
|
|
|
singleSelected;
|
2016-11-13 18:13:12 +01:00
|
|
|
bool isMultiOption = false;
|
2016-04-05 05:27:28 +02:00
|
|
|
|
2016-04-12 23:18:07 +02:00
|
|
|
//Parse the existing options
|
|
|
|
for(u32 i = 0; i < multiOptionsAmount; i++)
|
2018-01-05 19:34:05 +01:00
|
|
|
{
|
|
|
|
//Detect the positions where the "x" should go
|
|
|
|
u32 optionNum = 0;
|
|
|
|
for(u32 j = 0; optionNum < 4 && j < strlen(multiOptionsText[i]); j++)
|
|
|
|
if(multiOptionsText[i][j] == '(') multiOptions[i].posXs[optionNum++] = j + 1;
|
|
|
|
while(optionNum < 4) multiOptions[i].posXs[optionNum++] = 0;
|
|
|
|
|
2016-04-12 23:18:07 +02:00
|
|
|
multiOptions[i].enabled = MULTICONFIG(i);
|
2018-01-05 19:34:05 +01:00
|
|
|
}
|
2016-04-12 23:18:07 +02:00
|
|
|
for(u32 i = 0; i < singleOptionsAmount; i++)
|
|
|
|
singleOptions[i].enabled = CONFIG(i);
|
2016-04-05 23:01:46 +02:00
|
|
|
|
2016-09-12 18:58:10 +02:00
|
|
|
initScreens();
|
|
|
|
|
2017-08-20 17:00:47 +02:00
|
|
|
static const char *bootTypes[] = { "B9S",
|
2017-09-07 05:24:43 +02:00
|
|
|
"B9S (ntrboot)",
|
2017-08-20 17:00:47 +02:00
|
|
|
"FIRM0",
|
|
|
|
"FIRM1" };
|
|
|
|
|
|
|
|
drawString(true, 10, 10, COLOR_TITLE, CONFIG_TITLE);
|
2017-08-20 16:08:54 +02:00
|
|
|
drawString(true, 10, 10 + SPACING_Y, COLOR_TITLE, "Press A to select, START to save");
|
2017-08-20 17:00:47 +02:00
|
|
|
drawFormattedString(false, 10, SCREEN_HEIGHT - 2 * SPACING_Y, COLOR_YELLOW, "Booted from %s via %s", isSdMode ? "SD" : "CTRNAND", bootTypes[(u32)bootType]);
|
2016-09-12 18:58:10 +02:00
|
|
|
|
2016-04-05 23:01:46 +02:00
|
|
|
//Character to display a selected option
|
|
|
|
char selected = 'x';
|
|
|
|
|
2017-08-20 16:08:54 +02:00
|
|
|
u32 endPos = 10 + 2 * SPACING_Y;
|
|
|
|
|
2016-04-13 00:33:03 +02:00
|
|
|
//Display all the multiple choice options in white
|
2016-04-12 23:18:07 +02:00
|
|
|
for(u32 i = 0; i < multiOptionsAmount; i++)
|
2016-04-05 23:01:46 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
if(!multiOptions[i].visible) continue;
|
|
|
|
|
|
|
|
multiOptions[i].posY = endPos + SPACING_Y;
|
2017-05-07 17:58:44 +02:00
|
|
|
endPos = drawString(true, 10, multiOptions[i].posY, COLOR_WHITE, multiOptionsText[i]);
|
|
|
|
drawCharacter(true, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE, selected);
|
2016-04-05 23:01:46 +02:00
|
|
|
}
|
2016-04-05 05:27:28 +02:00
|
|
|
|
2016-04-13 15:19:35 +02:00
|
|
|
endPos += SPACING_Y / 2;
|
2016-04-05 05:27:28 +02:00
|
|
|
|
2016-04-13 00:33:03 +02:00
|
|
|
//Display all the normal options in white except for the first one
|
2016-10-13 00:52:18 +02:00
|
|
|
for(u32 i = 0, color = COLOR_RED; i < singleOptionsAmount; i++)
|
2016-04-12 23:18:07 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
if(!singleOptions[i].visible) continue;
|
2016-10-13 00:52:18 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
singleOptions[i].posY = endPos + SPACING_Y;
|
2017-05-07 17:58:44 +02:00
|
|
|
endPos = drawString(true, 10, singleOptions[i].posY, color, singleOptionsText[i]);
|
|
|
|
if(singleOptions[i].enabled) drawCharacter(true, 10 + SPACING_X, singleOptions[i].posY, color, selected);
|
2016-11-15 19:29:48 +01:00
|
|
|
|
|
|
|
if(color == COLOR_RED)
|
|
|
|
{
|
|
|
|
singleSelected = i;
|
|
|
|
selectedOption = i + multiOptionsAmount;
|
|
|
|
color = COLOR_WHITE;
|
2016-10-08 01:46:41 +02:00
|
|
|
}
|
2016-04-12 23:18:07 +02:00
|
|
|
}
|
2016-04-05 05:27:28 +02:00
|
|
|
|
2017-05-07 17:58:44 +02:00
|
|
|
drawString(false, 10, 10, COLOR_WHITE, optionsDescription[selectedOption]);
|
2016-09-13 23:01:38 +02:00
|
|
|
|
2016-04-12 23:18:07 +02:00
|
|
|
//Boring configuration menu
|
2016-11-15 19:29:48 +01:00
|
|
|
while(true)
|
2016-04-12 23:18:07 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
u32 pressed;
|
2016-04-13 15:19:35 +02:00
|
|
|
do
|
|
|
|
{
|
2017-12-18 04:27:11 +01:00
|
|
|
pressed = waitInput(true) & MENU_BUTTONS;
|
2016-04-05 05:27:28 +02:00
|
|
|
}
|
2017-12-18 04:27:11 +01:00
|
|
|
while(!pressed);
|
2017-11-22 19:03:39 +01:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(pressed == BUTTON_START) break;
|
|
|
|
|
2016-04-12 23:18:07 +02:00
|
|
|
if(pressed != BUTTON_A)
|
2016-04-05 05:27:28 +02:00
|
|
|
{
|
2016-04-13 15:19:35 +02:00
|
|
|
//Remember the previously selected option
|
|
|
|
u32 oldSelectedOption = selectedOption;
|
|
|
|
|
2016-10-08 01:46:41 +02:00
|
|
|
while(true)
|
2016-04-12 23:18:07 +02:00
|
|
|
{
|
2016-10-08 01:46:41 +02:00
|
|
|
switch(pressed)
|
|
|
|
{
|
|
|
|
case BUTTON_UP:
|
|
|
|
selectedOption = !selectedOption ? totalIndexes : selectedOption - 1;
|
|
|
|
break;
|
|
|
|
case BUTTON_DOWN:
|
|
|
|
selectedOption = selectedOption == totalIndexes ? 0 : selectedOption + 1;
|
|
|
|
break;
|
|
|
|
case BUTTON_LEFT:
|
|
|
|
pressed = BUTTON_DOWN;
|
|
|
|
selectedOption = 0;
|
|
|
|
break;
|
|
|
|
case BUTTON_RIGHT:
|
|
|
|
pressed = BUTTON_UP;
|
|
|
|
selectedOption = totalIndexes;
|
|
|
|
break;
|
2016-11-13 22:31:00 +01:00
|
|
|
default:
|
|
|
|
break;
|
2016-10-08 01:46:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if(selectedOption < multiOptionsAmount)
|
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
if(!multiOptions[selectedOption].visible) continue;
|
|
|
|
|
|
|
|
isMultiOption = true;
|
|
|
|
break;
|
2016-11-13 18:10:59 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
singleSelected = selectedOption - multiOptionsAmount;
|
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(!singleOptions[singleSelected].visible) continue;
|
|
|
|
|
|
|
|
isMultiOption = false;
|
|
|
|
break;
|
2016-10-08 01:46:41 +02:00
|
|
|
}
|
2016-04-12 23:18:07 +02:00
|
|
|
}
|
|
|
|
|
2016-04-13 15:19:35 +02:00
|
|
|
if(selectedOption == oldSelectedOption) continue;
|
|
|
|
|
2016-04-13 01:08:13 +02:00
|
|
|
//The user moved to a different option, print the old option in white and the new one in red. Only print 'x's if necessary
|
2016-04-12 23:18:07 +02:00
|
|
|
if(oldSelectedOption < multiOptionsAmount)
|
|
|
|
{
|
2017-05-07 17:58:44 +02:00
|
|
|
drawString(true, 10, multiOptions[oldSelectedOption].posY, COLOR_WHITE, multiOptionsText[oldSelectedOption]);
|
|
|
|
drawCharacter(true, 10 + multiOptions[oldSelectedOption].posXs[multiOptions[oldSelectedOption].enabled] * SPACING_X, multiOptions[oldSelectedOption].posY, COLOR_WHITE, selected);
|
2016-04-12 23:18:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
u32 singleOldSelected = oldSelectedOption - multiOptionsAmount;
|
2017-05-07 17:58:44 +02:00
|
|
|
drawString(true, 10, singleOptions[singleOldSelected].posY, COLOR_WHITE, singleOptionsText[singleOldSelected]);
|
|
|
|
if(singleOptions[singleOldSelected].enabled) drawCharacter(true, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE, selected);
|
2016-04-12 23:18:07 +02:00
|
|
|
}
|
2016-04-05 05:27:28 +02:00
|
|
|
|
2017-05-07 17:58:44 +02:00
|
|
|
if(isMultiOption) drawString(true, 10, multiOptions[selectedOption].posY, COLOR_RED, multiOptionsText[selectedOption]);
|
|
|
|
else drawString(true, 10, singleOptions[singleSelected].posY, COLOR_RED, singleOptionsText[singleSelected]);
|
2016-09-13 23:01:38 +02:00
|
|
|
|
2017-05-07 17:58:44 +02:00
|
|
|
drawString(false, 10, 10, COLOR_BLACK, optionsDescription[oldSelectedOption]);
|
|
|
|
drawString(false, 10, 10, COLOR_WHITE, optionsDescription[selectedOption]);
|
2016-04-12 23:18:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-13 15:19:35 +02:00
|
|
|
//The selected option's status changed, print the 'x's accordingly
|
2016-11-13 18:10:59 +01:00
|
|
|
if(isMultiOption)
|
2016-04-12 23:18:07 +02:00
|
|
|
{
|
|
|
|
u32 oldEnabled = multiOptions[selectedOption].enabled;
|
2017-05-07 17:58:44 +02:00
|
|
|
drawCharacter(true, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK, selected);
|
2016-05-28 16:13:22 +02:00
|
|
|
multiOptions[selectedOption].enabled = (oldEnabled == 3 || !multiOptions[selectedOption].posXs[oldEnabled + 1]) ? 0 : oldEnabled + 1;
|
2016-07-01 20:27:28 +02:00
|
|
|
|
2016-09-11 19:17:56 +02:00
|
|
|
if(selectedOption == BRIGHTNESS) updateBrightness(multiOptions[BRIGHTNESS].enabled);
|
2016-04-12 23:18:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-11-13 18:10:59 +01:00
|
|
|
bool oldEnabled = singleOptions[singleSelected].enabled;
|
|
|
|
singleOptions[singleSelected].enabled = !oldEnabled;
|
2017-05-07 17:58:44 +02:00
|
|
|
if(oldEnabled) drawCharacter(true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_BLACK, selected);
|
2016-04-12 23:18:07 +02:00
|
|
|
}
|
|
|
|
}
|
2016-04-13 15:19:35 +02:00
|
|
|
|
|
|
|
//In any case, if the current option is enabled (or a multiple choice option is selected) we must display a red 'x'
|
2017-05-07 17:58:44 +02:00
|
|
|
if(isMultiOption) drawCharacter(true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED, selected);
|
|
|
|
else if(singleOptions[singleSelected].enabled) drawCharacter(true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED, selected);
|
2016-04-05 05:27:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//Parse and write the new configuration
|
2017-06-18 22:31:21 +02:00
|
|
|
configData.multiConfig = 0;
|
2016-04-12 23:18:07 +02:00
|
|
|
for(u32 i = 0; i < multiOptionsAmount; i++)
|
2017-06-18 22:31:21 +02:00
|
|
|
configData.multiConfig |= multiOptions[i].enabled << (i * 2);
|
|
|
|
|
|
|
|
configData.config = 0;
|
2016-04-12 23:18:07 +02:00
|
|
|
for(u32 i = 0; i < singleOptionsAmount; i++)
|
2017-06-18 22:31:21 +02:00
|
|
|
configData.config |= (singleOptions[i].enabled ? 1 : 0) << i;
|
2016-04-05 05:27:28 +02:00
|
|
|
|
2017-08-28 02:40:05 +02:00
|
|
|
writeConfig(true);
|
|
|
|
|
2016-09-19 14:57:36 +02:00
|
|
|
u32 newPinMode = MULTICONFIG(PIN);
|
|
|
|
|
|
|
|
if(newPinMode != 0) newPin(oldPinStatus && newPinMode == oldPinMode, newPinMode);
|
2017-08-28 02:40:05 +02:00
|
|
|
else if(oldPinStatus)
|
|
|
|
{
|
|
|
|
if(!fileDelete(PIN_FILE))
|
|
|
|
error("Unable to delete PIN file");
|
|
|
|
}
|
2017-06-06 02:12:18 +02:00
|
|
|
|
2016-08-28 15:50:11 +02:00
|
|
|
while(HID_PAD & PIN_BUTTONS);
|
2016-11-29 20:11:30 +01:00
|
|
|
wait(2000ULL);
|
2017-01-21 21:20:48 +01:00
|
|
|
}
|