This repository has been archived on 2022-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
Luma3DS-3GX/source/config.c

352 lines
16 KiB
C
Raw Normal View History

2016-04-05 05:27:28 +02:00
/*
2016-07-05 16:05:53 +02:00
* This file is part of Luma3DS
* Copyright (C) 2016 Aurora Wright, TuxSH
*
* 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/>.
*
* Additional Terms 7.b of GPLv3 applies 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.
2016-04-05 05:27:28 +02:00
*/
#include "config.h"
#include "memory.h"
#include "fs.h"
2016-04-05 05:27:28 +02:00
#include "utils.h"
#include "screen.h"
2016-04-05 05:27:28 +02:00
#include "draw.h"
#include "buttons.h"
#include "pin.h"
2016-04-05 05:27:28 +02:00
bool readConfig(void)
{
if(fileRead(&configData, CONFIG_PATH, sizeof(CfgData)) != sizeof(CfgData) ||
memcmp(configData.magic, "CONF", 4) != 0 ||
configData.formatVersionMajor != CONFIG_VERSIONMAJOR ||
configData.formatVersionMinor != CONFIG_VERSIONMINOR)
{
configData.config = 0;
return false;
}
return true;
}
void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
{
/* If the configuration is different from previously, overwrite it.
Just the no-forcing flag being set is not enough */
if(needConfig == CREATE_CONFIGURATION || (configTemp & 0xFFFFFF7F) != configData.config)
{
if(needConfig == CREATE_CONFIGURATION)
{
memcpy(configData.magic, "CONF", 4);
configData.formatVersionMajor = CONFIG_VERSIONMAJOR;
configData.formatVersionMinor = CONFIG_VERSIONMINOR;
}
//Merge the new options and new boot configuration
configData.config = (configData.config & 0xFFFFFE00) | (configTemp & 0x1FF);
2016-09-08 02:12:29 +02:00
if(!fileWrite(&configData, CONFIG_PATH, sizeof(CfgData)))
error("Error writing the configuration file");
}
}
2016-09-19 14:57:36 +02:00
void configMenu(bool oldPinStatus, u32 oldPinMode)
2016-04-05 05:27:28 +02:00
{
const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
"Screen brightness: 4( ) 3( ) 2( ) 1( )",
2016-09-19 14:03:55 +02:00
"Splash: Off( ) Before( ) After( ) payloads",
"PIN lock: Off( ) 4( ) 6( ) 8( ) digits",
2016-09-19 14:03:55 +02:00
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )",
"Dev. features: Off( ) ErrDisp( ) UNITINFO( )"
2016-09-08 18:47:13 +02:00
};
const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
"( ) Use SysNAND FIRM if booting with R (A9LH)",
2016-09-19 14:03:55 +02:00
"( ) Enable FIRMs and modules loading from SD",
2016-07-01 20:36:43 +02:00
"( ) Enable region/language emu. and ext. .code",
"( ) Show NAND or user string in System Settings",
"( ) Show GBA boot screen in patched AGB_FIRM",
2016-09-19 14:03:55 +02:00
"( ) Patch SVC/service/archive/ARM9 access"
2016-09-08 18:47:13 +02:00
};
const char *optionsDescription[] = { "Select the default EmuNAND.\n\n"
2016-09-19 18:36:47 +02:00
"It will be booted when no\n"
"directional pad buttons are pressed.",
2016-09-18 18:05:51 +02:00
"Select the screen brightness.",
2016-09-19 14:03:55 +02:00
"Enable splash screen support.\n\n"
"\t* 'After payloads' displays it\n"
"before booting payloads.\n\n"
"\t* 'Before payloads' displays it\n"
"afterwards (intended for splashes\n"
"that display button hints).",
"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.",
"Select the New 3DS CPU mode.\n\n"
"It will be always enabled.\n\n"
"'Clock+L2' can cause issues with some\n"
"games.",
2016-09-19 14:03:55 +02:00
"Select the developer features.\n\n"
2016-09-19 18:36:47 +02:00
"\t* 'Off' disables exception handlers.\n"
2016-09-18 18:05:51 +02:00
"\t* 'ErrDisp' displays debug info\n"
"on the 'An error has occurred' screen.\n"
"\t* 'UNITINFO' makes the console be\n"
2016-09-18 18:05:51 +02:00
"always detected as a development unit\n"
"(which breaks online features and\n"
"allows booting some developer\n"
2016-09-19 14:03:55 +02:00
"software).\n\n"
"Only change this if you know what you\n"
"are doing!",
2016-09-19 18:36:47 +02:00
"If enabled, SysNAND will be launched\n"
"on boot.\n\n"
"Otherwise, an EmuNAND 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"
"EmuNAND will be booted with the\n"
2016-09-19 18:36:47 +02:00
"SysNAND FIRM.\n\n"
"Otherwise, SysNAND will be booted\n"
"with an EmuNAND 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"
2016-09-19 18:36:47 +02:00
"1/2/3/4), also add A if you have\n"
"a matching payload.",
2016-09-19 18:36:47 +02:00
"Enable loading FIRMs and\n"
"system modules from the SD card.\n\n"
"This isn't needed in most cases.",
2016-09-19 14:03:55 +02:00
"Enable overriding the region and\n"
"language configuration and the usage\n"
"of patched code binaries for specific\n"
"games.\n\n"
"Also makes certain DLCs for\n"
"out-of-region games work.\n\n"
"Refer to the wiki for instructions.",
2016-09-19 18:36:47 +02:00
"Enable showing the current NAND:\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* EmXS = EmuNAND X with SysNAND FIRM\n\n"
2016-09-14 22:31:25 +02:00
"or an user-defined custom string in\n"
"System Settings.\n\n"
"Refer to the wiki for instructions.",
2016-09-19 18:36:47 +02:00
"Enable showing the GBA boot screen\n"
"when booting GBA games.",
2016-09-19 14:03:55 +02:00
"Disable SVC, service, archive and ARM9\n"
"exheader access checks.\n\n"
"The service and archive patches\n"
"don't work on New 3DS FIRMs between\n"
"9.3 and 10.4.\n\n"
2016-09-19 14:03:55 +02:00
"Only change this if you know what you\n"
"are doing!",
};
struct multiOption {
u32 posXs[4];
u32 posY;
2016-04-05 05:27:28 +02:00
u32 enabled;
} multiOptions[] = {
{ .posXs = {19, 24, 29, 34} },
2016-08-15 21:37:25 +02:00
{ .posXs = {21, 26, 31, 36} },
2016-09-19 14:03:55 +02:00
{ .posXs = {12, 22, 31, 0} },
{ .posXs = {14, 19, 24, 29} },
2016-09-19 14:03:55 +02:00
{ .posXs = {17, 26, 32, 44} },
{ .posXs = {19, 30, 42, 0} }
};
//Calculate the amount of the various kinds of options and pre-select the first single one
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
singleOptionsAmount = sizeof(singleOptionsText) / sizeof(char *),
2016-04-13 15:19:35 +02:00
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
selectedOption = multiOptionsAmount;
struct singleOption {
u32 posY;
bool enabled;
} singleOptions[singleOptionsAmount];
2016-04-05 05:27:28 +02:00
//Parse the existing options
for(u32 i = 0; i < multiOptionsAmount; i++)
multiOptions[i].enabled = MULTICONFIG(i);
for(u32 i = 0; i < singleOptionsAmount; i++)
singleOptions[i].enabled = CONFIG(i);
initScreens();
drawString(CONFIG_TITLE, true, 10, 10, COLOR_TITLE);
drawString("Press A to select, START to save", true, 10, 30, COLOR_WHITE);
//Character to display a selected option
char selected = 'x';
u32 endPos = 42;
//Display all the multiple choice options in white
for(u32 i = 0; i < multiOptionsAmount; i++)
{
2016-09-11 19:17:56 +02:00
if(!(i == NEWCPU && !isN3DS))
2016-09-11 18:42:59 +02:00
{
multiOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(multiOptionsText[i], true, 10, multiOptions[i].posY, COLOR_WHITE);
drawCharacter(selected, true, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE);
2016-09-11 18:42:59 +02:00
}
}
2016-04-05 05:27:28 +02:00
2016-04-13 15:19:35 +02:00
endPos += SPACING_Y / 2;
u32 color = COLOR_RED;
2016-04-05 05:27:28 +02:00
//Display all the normal options in white except for the first one
2016-04-13 15:19:35 +02:00
for(u32 i = 0; i < singleOptionsAmount; i++)
{
singleOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color);
if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
2016-04-13 15:19:35 +02:00
color = COLOR_WHITE;
}
2016-04-05 05:27:28 +02:00
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
u32 pressed = 0;
//Boring configuration menu
while(pressed != BUTTON_START)
{
2016-04-13 15:19:35 +02:00
do
{
2016-04-05 05:27:28 +02:00
pressed = waitInput();
}
while(!(pressed & MENU_BUTTONS));
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;
switch(pressed)
{
case BUTTON_UP:
2016-09-11 18:42:59 +02:00
if(!selectedOption) selectedOption = totalIndexes;
2016-09-11 19:17:56 +02:00
else selectedOption = (selectedOption == NEWCPU + 1 && !isN3DS) ? selectedOption - 2 : selectedOption - 1;
break;
case BUTTON_DOWN:
2016-09-11 18:42:59 +02:00
if(selectedOption == totalIndexes) selectedOption = 0;
2016-09-11 19:17:56 +02:00
else selectedOption = (selectedOption == NEWCPU - 1 && !isN3DS) ? selectedOption + 2 : selectedOption + 1;
break;
case BUTTON_LEFT:
selectedOption = 0;
break;
case BUTTON_RIGHT:
2016-04-13 15:19:35 +02:00
selectedOption = totalIndexes;
break;
default:
continue;
}
2016-04-13 15:19:35 +02:00
if(selectedOption == oldSelectedOption) continue;
//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
if(oldSelectedOption < multiOptionsAmount)
{
drawString(multiOptionsText[oldSelectedOption], true, 10, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
drawCharacter(selected, true, 10 + multiOptions[oldSelectedOption].posXs[multiOptions[oldSelectedOption].enabled] * SPACING_X, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
}
else
{
u32 singleOldSelected = oldSelectedOption - multiOptionsAmount;
drawString(singleOptionsText[singleOldSelected], true, 10, singleOptions[singleOldSelected].posY, COLOR_WHITE);
if(singleOptions[singleOldSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE);
}
2016-04-05 05:27:28 +02:00
if(selectedOption < multiOptionsAmount)
drawString(multiOptionsText[selectedOption], true, 10, multiOptions[selectedOption].posY, COLOR_RED);
else
{
u32 singleSelected = selectedOption - multiOptionsAmount;
drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED);
}
clearScreens(false, true);
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
}
else
{
2016-04-13 15:19:35 +02:00
//The selected option's status changed, print the 'x's accordingly
if(selectedOption < multiOptionsAmount)
{
u32 oldEnabled = multiOptions[selectedOption].enabled;
drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK);
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);
}
else
{
bool oldEnabled = singleOptions[selectedOption - multiOptionsAmount].enabled;
singleOptions[selectedOption - multiOptionsAmount].enabled = !oldEnabled;
if(oldEnabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_BLACK);
}
}
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'
if(selectedOption < multiOptionsAmount)
drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
2016-04-13 15:19:35 +02:00
else
{
u32 singleSelected = selectedOption - multiOptionsAmount;
if(singleOptions[singleSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED);
2016-04-13 15:19:35 +02:00
}
2016-04-05 05:27:28 +02:00
}
//Preserve the last-used boot options (first 9 bits)
configData.config &= 0x1FF;
2016-04-05 05:27:28 +02:00
//Parse and write the new configuration
for(u32 i = 0; i < multiOptionsAmount; i++)
configData.config |= multiOptions[i].enabled << (i * 2 + 9);
for(u32 i = 0; i < singleOptionsAmount; i++)
configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 21);
2016-04-05 05:27:28 +02:00
2016-09-19 14:57:36 +02:00
u32 newPinMode = MULTICONFIG(PIN);
if(newPinMode != 0) newPin(oldPinStatus && newPinMode == oldPinMode, newPinMode);
else if(oldPinStatus) fileDelete(PIN_PATH);
//Wait for the pressed buttons to change
while(HID_PAD & PIN_BUTTONS);
chrono(2);
}