2020-07-08 02:07:00 +02:00
/*
* Copyright ( c ) 2020 Adubbz
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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/>.
*/
2020-07-08 05:06:21 +02:00
# include <algorithm>
2020-07-08 02:07:00 +02:00
# include <cstdarg>
# include <cstdio>
# include <cstring>
2021-07-22 03:21:38 +02:00
# include <limits>
2020-07-08 02:07:00 +02:00
# include <dirent.h>
# include "ui.hpp"
# include "ui_util.hpp"
# include "assert.hpp"
namespace dbk {
namespace {
2020-07-09 13:33:52 +02:00
static constexpr u32 ExosphereApiVersionConfigItem = 65000 ;
static constexpr u32 ExosphereHasRcmBugPatch = 65004 ;
static constexpr u32 ExosphereEmummcType = 65007 ;
2021-07-22 03:21:38 +02:00
static constexpr u32 ExosphereSupportedHosVersion = 65011 ;
2020-07-09 13:33:52 +02:00
2020-07-11 06:05:30 +02:00
/* Insets of content within windows. */
static constexpr float HorizontalInset = 20.0f ;
static constexpr float BottomInset = 20.0f ;
/* Insets of content within text areas. */
static constexpr float TextHorizontalInset = 8.0f ;
static constexpr float TextVerticalInset = 8.0f ;
static constexpr float ButtonHeight = 60.0f ;
static constexpr float ButtonHorizontalGap = 10.0f ;
static constexpr float VerticalGap = 10.0f ;
2020-07-08 02:07:00 +02:00
u32 g_screen_width ;
u32 g_screen_height ;
2021-07-22 03:21:38 +02:00
constinit u32 g_supported_version = std : : numeric_limits < u32 > : : max ( ) ;
2020-07-08 02:07:00 +02:00
std : : shared_ptr < Menu > g_current_menu ;
bool g_initialized = false ;
bool g_exit_requested = false ;
2020-12-02 11:21:05 +01:00
PadState g_pad ;
2020-07-08 02:07:00 +02:00
u32 g_prev_touch_count = - 1 ;
2020-12-02 11:21:05 +01:00
HidTouchScreenState g_start_touch ;
2020-07-08 02:07:00 +02:00
bool g_started_touching = false ;
bool g_tapping = false ;
bool g_touches_moving = false ;
bool g_finished_touching = false ;
/* Update install state. */
char g_update_path [ FS_MAX_PATH ] ;
2020-07-11 06:05:30 +02:00
bool g_reset_to_factory = false ;
bool g_exfat_supported = false ;
2020-07-08 02:07:00 +02:00
bool g_use_exfat = false ;
constexpr u32 MaxTapMovement = 20 ;
void UpdateInput ( ) {
/* Scan for input and update touch state. */
2020-12-02 11:21:05 +01:00
padUpdate ( & g_pad ) ;
HidTouchScreenState current_touch ;
hidGetTouchScreenStates ( & current_touch , 1 ) ;
const u32 touch_count = current_touch . count ;
2020-07-08 02:07:00 +02:00
if ( g_prev_touch_count = = 0 & & touch_count > 0 ) {
2020-12-02 11:21:05 +01:00
hidGetTouchScreenStates ( & g_start_touch , 1 ) ;
2020-07-08 02:07:00 +02:00
g_started_touching = true ;
g_tapping = true ;
} else {
g_started_touching = false ;
}
if ( g_prev_touch_count > 0 & & touch_count = = 0 ) {
g_finished_touching = true ;
g_tapping = false ;
} else {
g_finished_touching = false ;
}
/* Check if currently moving. */
if ( g_prev_touch_count > 0 & & touch_count > 0 ) {
2020-12-02 11:21:05 +01:00
if ( ( abs ( current_touch . touches [ 0 ] . x - g_start_touch . touches [ 0 ] . x ) > MaxTapMovement | | abs ( current_touch . touches [ 0 ] . y - g_start_touch . touches [ 0 ] . y ) > MaxTapMovement ) ) {
2020-07-08 02:07:00 +02:00
g_touches_moving = true ;
g_tapping = false ;
} else {
g_touches_moving = false ;
}
} else {
g_touches_moving = false ;
}
2020-12-02 11:21:05 +01:00
/* Update the previous touch count. */
g_prev_touch_count = current_touch . count ;
2020-07-08 02:07:00 +02:00
}
void ChangeMenu ( std : : shared_ptr < Menu > menu ) {
g_current_menu = menu ;
}
void ReturnToPreviousMenu ( ) {
/* Go to the previous menu if there is one. */
if ( g_current_menu - > GetPrevMenu ( ) ! = nullptr ) {
g_current_menu = g_current_menu - > GetPrevMenu ( ) ;
}
}
Result IsPathBottomLevel ( const char * path , bool * out ) {
Result rc = 0 ;
FsFileSystem * fs ;
char translated_path [ FS_MAX_PATH ] = { } ;
DBK_ABORT_UNLESS ( fsdevTranslatePath ( path , & fs , translated_path ) ! = - 1 ) ;
FsDir dir ;
if ( R_FAILED ( rc = fsFsOpenDirectory ( fs , translated_path , FsDirOpenMode_ReadDirs , & dir ) ) ) {
return rc ;
}
s64 entry_count ;
if ( R_FAILED ( rc = fsDirGetEntryCount ( & dir , & entry_count ) ) ) {
return rc ;
}
* out = entry_count = = 0 ;
fsDirClose ( & dir ) ;
return rc ;
}
2021-07-22 03:21:38 +02:00
u32 EncodeVersion ( u32 major , u32 minor , u32 micro , u32 relstep = 0 ) {
return ( ( major & 0xFF ) < < 24 ) | ( ( minor & 0xFF ) < < 16 ) | ( ( micro & 0xFF ) < < 8 ) | ( ( relstep & 0xFF ) < < 8 ) ;
}
2020-07-08 02:07:00 +02:00
}
void Menu : : AddButton ( u32 id , const char * text , float x , float y , float w , float h ) {
DBK_ABORT_UNLESS ( id < MaxButtons ) ;
Button button = {
. id = id ,
. selected = false ,
. enabled = true ,
. x = x ,
. y = y ,
. w = w ,
. h = h ,
} ;
strncpy ( button . text , text , sizeof ( button . text ) - 1 ) ;
m_buttons [ id ] = button ;
}
void Menu : : SetButtonSelected ( u32 id , bool selected ) {
DBK_ABORT_UNLESS ( id < MaxButtons ) ;
auto & button = m_buttons [ id ] ;
if ( button ) {
button - > selected = selected ;
}
}
void Menu : : DeselectAllButtons ( ) {
for ( auto & button : m_buttons ) {
/* Ensure button is present. */
if ( ! button ) {
continue ;
}
button - > selected = false ;
}
}
void Menu : : SetButtonEnabled ( u32 id , bool enabled ) {
DBK_ABORT_UNLESS ( id < MaxButtons ) ;
auto & button = m_buttons [ id ] ;
button - > enabled = enabled ;
}
Button * Menu : : GetButton ( u32 id ) {
DBK_ABORT_UNLESS ( id < MaxButtons ) ;
return ! m_buttons [ id ] ? nullptr : & ( * m_buttons [ id ] ) ;
}
Button * Menu : : GetSelectedButton ( ) {
for ( auto & button : m_buttons ) {
if ( button & & button - > enabled & & button - > selected ) {
return & ( * button ) ;
}
}
return nullptr ;
}
Button * Menu : : GetClosestButtonToSelection ( Direction direction ) {
const Button * selected_button = this - > GetSelectedButton ( ) ;
if ( selected_button = = nullptr | | direction = = Direction : : Invalid ) {
return nullptr ;
}
Button * closest_button = nullptr ;
float closest_distance = 0.0f ;
for ( auto & button : m_buttons ) {
/* Skip absent button. */
if ( ! button | | ! button - > enabled ) {
continue ;
}
/* Skip buttons that are in the wrong direction. */
if ( ( direction = = Direction : : Down & & button - > y < = selected_button - > y ) | |
( direction = = Direction : : Up & & button - > y > = selected_button - > y ) | |
( direction = = Direction : : Right & & button - > x < = selected_button - > x ) | |
( direction = = Direction : : Left & & button - > x > = selected_button - > x ) ) {
continue ;
}
const float x_dist = button - > x - selected_button - > x ;
const float y_dist = button - > y - selected_button - > y ;
const float sq_dist = x_dist * x_dist + y_dist * y_dist ;
/* If we don't already have a closest button, set it. */
if ( closest_button = = nullptr ) {
closest_button = & ( * button ) ;
closest_distance = sq_dist ;
continue ;
}
/* Update the closest button if this one is closer. */
if ( sq_dist < closest_distance ) {
closest_button = & ( * button ) ;
closest_distance = sq_dist ;
}
}
return closest_button ;
}
Button * Menu : : GetTouchedButton ( ) {
2020-12-02 11:21:05 +01:00
HidTouchScreenState current_touch ;
hidGetTouchScreenStates ( & current_touch , 1 ) ;
const u32 touch_count = current_touch . count ;
2020-07-08 02:07:00 +02:00
for ( u32 i = 0 ; i < touch_count & & g_started_touching ; i + + ) {
for ( auto & button : m_buttons ) {
2020-12-02 11:21:05 +01:00
if ( button & & button - > enabled & & button - > IsPositionInBounds ( current_touch . touches [ i ] . x , current_touch . touches [ i ] . y ) ) {
2020-07-08 02:07:00 +02:00
return & ( * button ) ;
}
}
}
return nullptr ;
}
Button * Menu : : GetActivatedButton ( ) {
Button * selected_button = this - > GetSelectedButton ( ) ;
if ( selected_button = = nullptr ) {
return nullptr ;
}
2020-12-02 11:21:05 +01:00
const u64 k_down = padGetButtonsDown ( & g_pad ) ;
2020-07-08 02:07:00 +02:00
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_A | | this - > GetTouchedButton ( ) = = selected_button ) {
2020-07-08 02:07:00 +02:00
return selected_button ;
}
return nullptr ;
}
void Menu : : UpdateButtons ( ) {
2020-12-02 11:21:05 +01:00
const u64 k_down = padGetButtonsDown ( & g_pad ) ;
2020-07-08 02:07:00 +02:00
Direction direction = Direction : : Invalid ;
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_AnyDown ) {
2020-07-08 02:07:00 +02:00
direction = Direction : : Down ;
2020-12-02 11:21:05 +01:00
} else if ( k_down & HidNpadButton_AnyUp ) {
2020-07-08 02:07:00 +02:00
direction = Direction : : Up ;
2020-12-02 11:21:05 +01:00
} else if ( k_down & HidNpadButton_AnyLeft ) {
2020-07-08 02:07:00 +02:00
direction = Direction : : Left ;
2020-12-02 11:21:05 +01:00
} else if ( k_down & HidNpadButton_AnyRight ) {
2020-07-08 02:07:00 +02:00
direction = Direction : : Right ;
}
/* Select the closest button. */
if ( const Button * closest_button = this - > GetClosestButtonToSelection ( direction ) ; closest_button ! = nullptr ) {
this - > DeselectAllButtons ( ) ;
this - > SetButtonSelected ( closest_button - > id , true ) ;
}
/* Select the touched button. */
if ( const Button * touched_button = this - > GetTouchedButton ( ) ; touched_button ! = nullptr ) {
this - > DeselectAllButtons ( ) ;
this - > SetButtonSelected ( touched_button - > id , true ) ;
}
}
void Menu : : DrawButtons ( NVGcontext * vg , u64 ns ) {
for ( auto & button : m_buttons ) {
/* Ensure button is present. */
if ( ! button ) {
continue ;
}
/* Set the button style. */
auto style = ButtonStyle : : StandardDisabled ;
if ( button - > enabled ) {
style = button - > selected ? ButtonStyle : : StandardSelected : ButtonStyle : : Standard ;
}
DrawButton ( vg , button - > text , button - > x , button - > y , button - > w , button - > h , style , ns ) ;
}
}
void Menu : : LogText ( const char * format , . . . ) {
/* Create a temporary string. */
char tmp [ 0x100 ] ;
va_list args ;
va_start ( args , format ) ;
2020-07-11 06:05:30 +02:00
vsnprintf ( tmp , sizeof ( tmp ) , format , args ) ;
2020-07-08 02:07:00 +02:00
va_end ( args ) ;
/* Append the text to the log buffer. */
strncat ( m_log_buffer , tmp , sizeof ( m_log_buffer ) - 1 ) ;
}
std : : shared_ptr < Menu > Menu : : GetPrevMenu ( ) {
return m_prev_menu ;
}
2020-07-11 06:05:30 +02:00
AlertMenu : : AlertMenu ( std : : shared_ptr < Menu > prev_menu , const char * text , const char * subtext , Result rc ) : Menu ( prev_menu ) , m_text { } , m_subtext { } , m_result_text { } , m_rc ( rc ) {
2020-07-09 13:33:52 +02:00
/* Copy the input text. */
strncpy ( m_text , text , sizeof ( m_text ) - 1 ) ;
strncpy ( m_subtext , subtext , sizeof ( m_subtext ) - 1 ) ;
/* Copy result text if there is a result. */
if ( R_FAILED ( rc ) ) {
2020-07-11 06:05:30 +02:00
snprintf ( m_result_text , sizeof ( m_result_text ) , " Result: 0x%08x " , rc ) ;
2020-07-09 13:33:52 +02:00
}
2020-07-11 06:05:30 +02:00
}
void AlertMenu : : Draw ( NVGcontext * vg , u64 ns ) {
const float window_height = WindowHeight + ( R_FAILED ( m_rc ) ? SubTextHeight : 0.0f ) ;
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - window_height / 2.0f ;
DrawWindow ( vg , m_text , x , y , WindowWidth , window_height ) ;
DrawText ( vg , x + HorizontalInset , y + TitleGap , WindowWidth - HorizontalInset * 2.0f , m_subtext ) ;
/* Draw the result if there is one. */
if ( R_FAILED ( m_rc ) ) {
DrawText ( vg , x + HorizontalInset , y + TitleGap + SubTextHeight , WindowWidth - HorizontalInset * 2.0f , m_result_text ) ;
}
this - > DrawButtons ( vg , ns ) ;
}
ErrorMenu : : ErrorMenu ( const char * text , const char * subtext , Result rc ) : AlertMenu ( nullptr , text , subtext , rc ) {
const float window_height = WindowHeight + ( R_FAILED ( m_rc ) ? SubTextHeight : 0.0f ) ;
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - window_height / 2.0f ;
const float button_y = y + TitleGap + SubTextHeight + VerticalGap * 2.0f + ( R_FAILED ( m_rc ) ? SubTextHeight : 0.0f ) ;
const float button_width = WindowWidth - HorizontalInset * 2.0f ;
2020-07-09 13:33:52 +02:00
2020-07-11 06:05:30 +02:00
/* Add buttons. */
this - > AddButton ( ExitButtonId , " Exit " , x + HorizontalInset , button_y , button_width , ButtonHeight ) ;
2020-07-09 13:33:52 +02:00
this - > SetButtonSelected ( ExitButtonId , true ) ;
}
void ErrorMenu : : Update ( u64 ns ) {
2020-12-02 11:21:05 +01:00
u64 k_down = padGetButtonsDown ( & g_pad ) ;
2020-07-09 13:33:52 +02:00
/* Go back if B is pressed. */
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_B ) {
2020-07-09 13:33:52 +02:00
g_exit_requested = true ;
return ;
}
/* Take action if a button has been activated. */
if ( const Button * activated_button = this - > GetActivatedButton ( ) ; activated_button ! = nullptr ) {
switch ( activated_button - > id ) {
case ExitButtonId :
g_exit_requested = true ;
break ;
}
}
this - > UpdateButtons ( ) ;
/* Fallback on selecting the exfat button. */
if ( const Button * selected_button = this - > GetSelectedButton ( ) ; k_down & & selected_button = = nullptr ) {
this - > SetButtonSelected ( ExitButtonId , true ) ;
}
}
2020-07-11 06:05:30 +02:00
WarningMenu : : WarningMenu ( std : : shared_ptr < Menu > prev_menu , std : : shared_ptr < Menu > next_menu , const char * text , const char * subtext , Result rc ) : AlertMenu ( prev_menu , text , subtext , rc ) , m_next_menu ( next_menu ) {
2020-07-09 13:33:52 +02:00
const float window_height = WindowHeight + ( R_FAILED ( m_rc ) ? SubTextHeight : 0.0f ) ;
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - window_height / 2.0f ;
2020-07-11 06:05:30 +02:00
const float button_y = y + TitleGap + SubTextHeight + VerticalGap * 2.0f + ( R_FAILED ( m_rc ) ? SubTextHeight : 0.0f ) ;
const float button_width = ( WindowWidth - HorizontalInset * 2.0f ) / 2.0f - ButtonHorizontalGap ;
this - > AddButton ( BackButtonId , " Back " , x + HorizontalInset , button_y , button_width , ButtonHeight ) ;
this - > AddButton ( ContinueButtonId , " Continue " , x + HorizontalInset + button_width + ButtonHorizontalGap , button_y , button_width , ButtonHeight ) ;
2020-07-09 13:33:52 +02:00
this - > SetButtonSelected ( ContinueButtonId , true ) ;
}
void WarningMenu : : Update ( u64 ns ) {
2020-12-02 11:21:05 +01:00
u64 k_down = padGetButtonsDown ( & g_pad ) ;
2020-07-09 13:33:52 +02:00
/* Go back if B is pressed. */
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_B ) {
2020-07-09 13:33:52 +02:00
ReturnToPreviousMenu ( ) ;
return ;
}
/* Take action if a button has been activated. */
if ( const Button * activated_button = this - > GetActivatedButton ( ) ; activated_button ! = nullptr ) {
switch ( activated_button - > id ) {
case BackButtonId :
ReturnToPreviousMenu ( ) ;
return ;
case ContinueButtonId :
ChangeMenu ( m_next_menu ) ;
return ;
}
}
this - > UpdateButtons ( ) ;
/* Fallback on selecting the exfat button. */
if ( const Button * selected_button = this - > GetSelectedButton ( ) ; k_down & & selected_button = = nullptr ) {
this - > SetButtonSelected ( ContinueButtonId , true ) ;
}
}
2020-07-08 02:07:00 +02:00
MainMenu : : MainMenu ( ) : Menu ( nullptr ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
2020-07-11 06:05:30 +02:00
this - > AddButton ( InstallButtonId , " Install " , x + HorizontalInset , y + TitleGap , WindowWidth - HorizontalInset * 2 , ButtonHeight ) ;
this - > AddButton ( ExitButtonId , " Exit " , x + HorizontalInset , y + TitleGap + ButtonHeight + VerticalGap , WindowWidth - HorizontalInset * 2 , ButtonHeight ) ;
2020-07-08 02:07:00 +02:00
this - > SetButtonSelected ( InstallButtonId , true ) ;
}
void MainMenu : : Update ( u64 ns ) {
2020-12-02 11:21:05 +01:00
u64 k_down = padGetButtonsDown ( & g_pad ) ;
2020-07-08 02:07:00 +02:00
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_B ) {
2020-07-08 02:07:00 +02:00
g_exit_requested = true ;
}
/* Take action if a button has been activated. */
if ( const Button * activated_button = this - > GetActivatedButton ( ) ; activated_button ! = nullptr ) {
switch ( activated_button - > id ) {
case InstallButtonId :
2020-07-09 13:33:52 +02:00
{
const auto file_menu = std : : make_shared < FileMenu > ( g_current_menu , " / " ) ;
Result rc = 0 ;
u64 hardware_type ;
u64 has_rcm_bug_patch ;
u64 is_emummc ;
if ( R_FAILED ( rc = splGetConfig ( SplConfigItem_HardwareType , & hardware_type ) ) ) {
ChangeMenu ( std : : make_shared < ErrorMenu > ( " An error has occurred " , " Failed to get hardware type. " , rc ) ) ;
return ;
}
if ( R_FAILED ( rc = splGetConfig ( static_cast < SplConfigItem > ( ExosphereHasRcmBugPatch ) , & has_rcm_bug_patch ) ) ) {
ChangeMenu ( std : : make_shared < ErrorMenu > ( " An error has occurred " , " Failed to check RCM bug status. " , rc ) ) ;
return ;
}
if ( R_FAILED ( rc = splGetConfig ( static_cast < SplConfigItem > ( ExosphereEmummcType ) , & is_emummc ) ) ) {
2021-03-19 04:14:03 +01:00
ChangeMenu ( std : : make_shared < ErrorMenu > ( " An error has occurred " , " Failed to check emuMMC status. " , rc ) ) ;
2020-07-09 13:33:52 +02:00
return ;
}
/* Warn if we're working with a patched unit. */
const bool is_erista = hardware_type = = 0 | | hardware_type = = 1 ;
if ( is_erista & & has_rcm_bug_patch & & ! is_emummc ) {
ChangeMenu ( std : : make_shared < WarningMenu > ( g_current_menu , file_menu , " Warning: Patched unit detected " , " You may burn fuses or render your switch inoperable. " ) ) ;
} else {
ChangeMenu ( file_menu ) ;
}
return ;
}
2020-07-08 02:07:00 +02:00
case ExitButtonId :
g_exit_requested = true ;
2020-07-09 13:33:52 +02:00
return ;
2020-07-08 02:07:00 +02:00
}
}
this - > UpdateButtons ( ) ;
/* Fallback on selecting the install button. */
if ( const Button * selected_button = this - > GetSelectedButton ( ) ; k_down & & selected_button = = nullptr ) {
this - > SetButtonSelected ( InstallButtonId , true ) ;
}
}
void MainMenu : : Draw ( NVGcontext * vg , u64 ns ) {
DrawWindow ( vg , " Daybreak " , g_screen_width / 2.0f - WindowWidth / 2.0f , g_screen_height / 2.0f - WindowHeight / 2.0f , WindowWidth , WindowHeight ) ;
this - > DrawButtons ( vg , ns ) ;
}
FileMenu : : FileMenu ( std : : shared_ptr < Menu > prev_menu , const char * root ) : Menu ( prev_menu ) , m_current_index ( 0 ) , m_scroll_offset ( 0 ) , m_touch_start_scroll_offset ( 0 ) , m_touch_finalize_selection ( false ) {
Result rc = 0 ;
strncpy ( m_root , root , sizeof ( m_root ) - 1 ) ;
if ( R_FAILED ( rc = this - > PopulateFileEntries ( ) ) ) {
fatalThrow ( rc ) ;
}
}
Result FileMenu : : PopulateFileEntries ( ) {
/* Open the directory. */
DIR * dir = opendir ( m_root ) ;
if ( dir = = nullptr ) {
return fsdevGetLastResult ( ) ;
}
/* Add file entries to the list. */
struct dirent * ent ;
while ( ( ent = readdir ( dir ) ) ! = nullptr ) {
if ( ent - > d_type = = DT_DIR ) {
FileEntry file_entry = { } ;
strncpy ( file_entry . name , ent - > d_name , sizeof ( file_entry . name ) ) ;
m_file_entries . push_back ( file_entry ) ;
}
}
/* Close the directory. */
closedir ( dir ) ;
2020-07-08 05:06:21 +02:00
/* Sort the file entries. */
std : : sort ( m_file_entries . begin ( ) , m_file_entries . end ( ) , [ ] ( const FileEntry & a , const FileEntry & b ) {
return strncmp ( a . name , b . name , sizeof ( a . name ) ) < 0 ;
} ) ;
2020-07-08 02:07:00 +02:00
return 0 ;
}
bool FileMenu : : IsSelectionVisible ( ) {
const float visible_start = m_scroll_offset ;
const float visible_end = visible_start + FileListHeight ;
const float entry_start = static_cast < float > ( m_current_index ) * ( FileRowHeight + FileRowGap ) ;
const float entry_end = entry_start + ( FileRowHeight + FileRowGap ) ;
return entry_start > = visible_start & & entry_end < = visible_end ;
}
void FileMenu : : ScrollToSelection ( ) {
const float visible_start = m_scroll_offset ;
const float visible_end = visible_start + FileListHeight ;
const float entry_start = static_cast < float > ( m_current_index ) * ( FileRowHeight + FileRowGap ) ;
const float entry_end = entry_start + ( FileRowHeight + FileRowGap ) ;
if ( entry_end > visible_end ) {
m_scroll_offset + = entry_end - visible_end ;
} else if ( entry_end < visible_end ) {
m_scroll_offset = entry_start ;
}
}
bool FileMenu : : IsEntryTouched ( u32 i ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
2020-12-02 11:21:05 +01:00
HidTouchScreenState current_touch ;
hidGetTouchScreenStates ( & current_touch , 1 ) ;
2020-07-08 02:07:00 +02:00
/* Check if the tap is within the x bounds. */
2020-12-02 11:21:05 +01:00
if ( current_touch . touches [ 0 ] . x > = x + TextBackgroundOffset + FileRowHorizontalInset & & current_touch . touches [ 0 ] . x < = WindowWidth - ( TextBackgroundOffset + FileRowHorizontalInset ) * 2.0f ) {
2020-07-08 02:07:00 +02:00
const float y_min = y + TitleGap + FileRowGap + i * ( FileRowHeight + FileRowGap ) - m_scroll_offset ;
const float y_max = y_min + FileRowHeight ;
/* Check if the tap is within the y bounds. */
2020-12-02 11:21:05 +01:00
if ( current_touch . touches [ 0 ] . y > = y_min & & current_touch . touches [ 0 ] . y < = y_max ) {
2020-07-08 02:07:00 +02:00
return true ;
}
}
return false ;
}
void FileMenu : : UpdateTouches ( ) {
/* Setup values on initial touch. */
if ( g_started_touching ) {
m_touch_start_scroll_offset = m_scroll_offset ;
/* We may potentially finalize the selection later if we start off touching it. */
if ( this - > IsEntryTouched ( m_current_index ) ) {
m_touch_finalize_selection = true ;
}
}
/* Scroll based on touch movement. */
if ( g_touches_moving ) {
2020-12-02 11:21:05 +01:00
HidTouchScreenState current_touch ;
hidGetTouchScreenStates ( & current_touch , 1 ) ;
2020-07-08 02:07:00 +02:00
2020-12-02 11:21:05 +01:00
const int dist_y = current_touch . touches [ 0 ] . y - g_start_touch . touches [ 0 ] . y ;
2020-07-08 02:07:00 +02:00
float new_scroll_offset = m_touch_start_scroll_offset - static_cast < float > ( dist_y ) ;
float max_scroll = ( FileRowHeight + FileRowGap ) * static_cast < float > ( m_file_entries . size ( ) ) - FileListHeight ;
/* Don't allow scrolling if there is not enough elements. */
if ( max_scroll < 0.0f ) {
max_scroll = 0.0f ;
}
/* Don't allow scrolling before the first element. */
if ( new_scroll_offset < 0.0f ) {
new_scroll_offset = 0.0f ;
}
/* Don't allow scrolling past the last element. */
if ( new_scroll_offset > max_scroll ) {
new_scroll_offset = max_scroll ;
}
m_scroll_offset = new_scroll_offset ;
}
/* Select any tapped entries. */
if ( g_tapping ) {
for ( u32 i = 0 ; i < m_file_entries . size ( ) ; i + + ) {
if ( this - > IsEntryTouched ( i ) ) {
/* The current index is checked later. */
if ( i = = m_current_index ) {
continue ;
}
m_current_index = i ;
/* Don't finalize selection if we touch something else. */
m_touch_finalize_selection = false ;
break ;
}
}
}
/* Don't finalize selection if we aren't finished and we've either stopped tapping or are no longer touching the selection. */
if ( ! g_finished_touching & & ( ! g_tapping | | ! this - > IsEntryTouched ( m_current_index ) ) ) {
m_touch_finalize_selection = false ;
}
/* Finalize selection if the currently selected entry is touched for the second time. */
if ( g_finished_touching & & m_touch_finalize_selection ) {
this - > FinalizeSelection ( ) ;
m_touch_finalize_selection = false ;
}
}
void FileMenu : : FinalizeSelection ( ) {
DBK_ABORT_UNLESS ( m_current_index < m_file_entries . size ( ) ) ;
FileEntry & entry = m_file_entries [ m_current_index ] ;
/* Determine the selected path. */
char current_path [ FS_MAX_PATH ] = { } ;
2020-07-11 06:05:30 +02:00
const int path_len = snprintf ( current_path , sizeof ( current_path ) , " %s%s/ " , m_root , entry . name ) ;
DBK_ABORT_UNLESS ( path_len > = 0 & & path_len < static_cast < int > ( sizeof ( current_path ) ) ) ;
2020-07-08 02:07:00 +02:00
/* Determine if the chosen path is the bottom level. */
Result rc = 0 ;
bool bottom_level ;
if ( R_FAILED ( rc = IsPathBottomLevel ( current_path , & bottom_level ) ) ) {
fatalThrow ( rc ) ;
}
/* Show exfat settings or the next file menu. */
if ( bottom_level ) {
/* Set the update path. */
2020-07-11 06:05:30 +02:00
snprintf ( g_update_path , sizeof ( g_update_path ) , " %s " , current_path ) ;
2020-07-08 02:07:00 +02:00
/* Change the menu. */
ChangeMenu ( std : : make_shared < ValidateUpdateMenu > ( g_current_menu ) ) ;
} else {
ChangeMenu ( std : : make_shared < FileMenu > ( g_current_menu , current_path ) ) ;
}
}
void FileMenu : : Update ( u64 ns ) {
2020-12-02 11:21:05 +01:00
u64 k_down = padGetButtonsDown ( & g_pad ) ;
2020-07-08 02:07:00 +02:00
/* Go back if B is pressed. */
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_B ) {
2020-07-08 02:07:00 +02:00
ReturnToPreviousMenu ( ) ;
return ;
}
/* Finalize selection on pressing A. */
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_A ) {
2020-07-08 02:07:00 +02:00
this - > FinalizeSelection ( ) ;
}
/* Update touch input. */
this - > UpdateTouches ( ) ;
const u32 prev_index = m_current_index ;
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_AnyDown ) {
2020-07-08 02:07:00 +02:00
/* Scroll down. */
if ( m_current_index > = ( m_file_entries . size ( ) - 1 ) ) {
m_current_index = 0 ;
} else {
m_current_index + + ;
}
2020-12-02 11:21:05 +01:00
} else if ( k_down & HidNpadButton_AnyUp ) {
2020-07-08 02:07:00 +02:00
/* Scroll up. */
if ( m_current_index = = 0 ) {
m_current_index = m_file_entries . size ( ) - 1 ;
} else {
m_current_index - - ;
}
}
/* Scroll to the selection if it isn't visible. */
if ( prev_index ! = m_current_index & & ! this - > IsSelectionVisible ( ) ) {
this - > ScrollToSelection ( ) ;
}
}
void FileMenu : : Draw ( NVGcontext * vg , u64 ns ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
DrawWindow ( vg , " Select an update directory " , x , y , WindowWidth , WindowHeight ) ;
DrawTextBackground ( vg , x + TextBackgroundOffset , y + TitleGap , WindowWidth - TextBackgroundOffset * 2.0f , ( FileRowHeight + FileRowGap ) * MaxFileRows + FileRowGap ) ;
nvgSave ( vg ) ;
nvgScissor ( vg , x + TextBackgroundOffset , y + TitleGap , WindowWidth - TextBackgroundOffset * 2.0f , ( FileRowHeight + FileRowGap ) * MaxFileRows + FileRowGap ) ;
for ( u32 i = 0 ; i < m_file_entries . size ( ) ; i + + ) {
FileEntry & entry = m_file_entries [ i ] ;
auto style = ButtonStyle : : FileSelect ;
if ( i = = m_current_index ) {
style = ButtonStyle : : FileSelectSelected ;
}
DrawButton ( vg , entry . name , x + TextBackgroundOffset + FileRowHorizontalInset , y + TitleGap + FileRowGap + i * ( FileRowHeight + FileRowGap ) - m_scroll_offset , WindowWidth - ( TextBackgroundOffset + FileRowHorizontalInset ) * 2.0f , FileRowHeight , style , ns ) ;
}
nvgRestore ( vg ) ;
}
ValidateUpdateMenu : : ValidateUpdateMenu ( std : : shared_ptr < Menu > prev_menu ) : Menu ( prev_menu ) , m_has_drawn ( false ) , m_has_info ( false ) , m_has_validated ( false ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
2020-07-11 06:05:30 +02:00
const float button_width = ( WindowWidth - HorizontalInset * 2.0f ) / 2.0f - ButtonHorizontalGap ;
2020-07-08 02:07:00 +02:00
/* Add buttons. */
2020-07-11 06:05:30 +02:00
this - > AddButton ( BackButtonId , " Back " , x + HorizontalInset , y + WindowHeight - BottomInset - ButtonHeight , button_width , ButtonHeight ) ;
this - > AddButton ( ContinueButtonId , " Continue " , x + HorizontalInset + button_width + ButtonHorizontalGap , y + WindowHeight - BottomInset - ButtonHeight , button_width , ButtonHeight ) ;
2020-07-08 02:07:00 +02:00
this - > SetButtonEnabled ( BackButtonId , false ) ;
this - > SetButtonEnabled ( ContinueButtonId , false ) ;
/* Obtain update information. */
if ( R_FAILED ( this - > GetUpdateInformation ( ) ) ) {
this - > SetButtonEnabled ( BackButtonId , true ) ;
this - > SetButtonSelected ( BackButtonId , true ) ;
} else {
/* Log this early so it is printed out before validation causes stalling. */
this - > LogText ( " Validating update, this may take a moment... \n " ) ;
}
}
Result ValidateUpdateMenu : : GetUpdateInformation ( ) {
Result rc = 0 ;
this - > LogText ( " Directory %s \n " , g_update_path ) ;
/* Attempt to get the update information. */
if ( R_FAILED ( rc = amssuGetUpdateInformation ( & m_update_info , g_update_path ) ) ) {
if ( rc = = 0x1a405 ) {
this - > LogText ( " No update found in folder. \n Ensure your ncas are named correctly! \n Result: 0x%08x \n " , rc ) ;
} else {
this - > LogText ( " Failed to get update information. \n Result: 0x%08x \n " , rc ) ;
}
return rc ;
}
/* Print update information. */
this - > LogText ( " - Version: %d.%d.%d \n " , ( m_update_info . version > > 26 ) & 0x1f , ( m_update_info . version > > 20 ) & 0x1f , ( m_update_info . version > > 16 ) & 0xf ) ;
if ( m_update_info . exfat_supported ) {
this - > LogText ( " - exFAT: Supported \n " ) ;
} else {
this - > LogText ( " - exFAT: Unsupported \n " ) ;
}
this - > LogText ( " - Firmware variations: %d \n " , m_update_info . num_firmware_variations ) ;
/* Mark as having obtained update info. */
m_has_info = true ;
return rc ;
}
void ValidateUpdateMenu : : ValidateUpdate ( ) {
Result rc = 0 ;
/* Validate the update. */
if ( R_FAILED ( rc = amssuValidateUpdate ( & m_validation_info , g_update_path ) ) ) {
this - > LogText ( " Failed to validate update. \n Result: 0x%08x \n " , rc ) ;
return ;
}
/* Check the result. */
if ( R_SUCCEEDED ( m_validation_info . result ) ) {
this - > LogText ( " Update is valid! \n " ) ;
2020-07-28 00:42:17 +02:00
if ( R_FAILED ( m_validation_info . exfat_result ) ) {
const u32 version = m_validation_info . invalid_key . version ;
this - > LogText ( " exFAT Validation failed with result: 0x%08x \n " , m_validation_info . exfat_result ) ;
this - > LogText ( " Missing content: \n - Program id: %016lx \n - Version: %d.%d.%d \n " , m_validation_info . invalid_key . id , ( version > > 26 ) & 0x1f , ( version > > 20 ) & 0x1f , ( version > > 16 ) & 0xf ) ;
/* Log the missing content id. */
this - > LogText ( " - Content id: " ) ;
for ( size_t i = 0 ; i < sizeof ( NcmContentId ) ; i + + ) {
this - > LogText ( " %02x " , m_validation_info . invalid_content_id . c [ i ] ) ;
}
this - > LogText ( " \n " ) ;
}
2020-07-08 02:07:00 +02:00
/* Enable the back and continue buttons and select the continue button. */
this - > SetButtonEnabled ( BackButtonId , true ) ;
this - > SetButtonEnabled ( ContinueButtonId , true ) ;
this - > SetButtonSelected ( ContinueButtonId , true ) ;
} else {
/* Log the missing content info. */
const u32 version = m_validation_info . invalid_key . version ;
this - > LogText ( " Validation failed with result: 0x%08x \n " , m_validation_info . result ) ;
this - > LogText ( " Missing content: \n - Program id: %016lx \n - Version: %d.%d.%d \n " , m_validation_info . invalid_key . id , ( version > > 26 ) & 0x1f , ( version > > 20 ) & 0x1f , ( version > > 16 ) & 0xf ) ;
/* Log the missing content id. */
this - > LogText ( " - Content id: " ) ;
for ( size_t i = 0 ; i < sizeof ( NcmContentId ) ; i + + ) {
this - > LogText ( " %02x " , m_validation_info . invalid_content_id . c [ i ] ) ;
}
this - > LogText ( " \n " ) ;
/* Enable the back button and select it. */
this - > SetButtonEnabled ( BackButtonId , true ) ;
this - > SetButtonSelected ( BackButtonId , true ) ;
}
/* Mark validation as being complete. */
m_has_validated = true ;
}
void ValidateUpdateMenu : : Update ( u64 ns ) {
/* Perform validation if it hasn't been done already. */
if ( m_has_info & & m_has_drawn & & ! m_has_validated ) {
this - > ValidateUpdate ( ) ;
}
2020-12-02 11:21:05 +01:00
u64 k_down = padGetButtonsDown ( & g_pad ) ;
2020-07-08 02:07:00 +02:00
/* Go back if B is pressed. */
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_B ) {
2020-07-08 02:07:00 +02:00
ReturnToPreviousMenu ( ) ;
return ;
}
/* Take action if a button has been activated. */
if ( const Button * activated_button = this - > GetActivatedButton ( ) ; activated_button ! = nullptr ) {
switch ( activated_button - > id ) {
case BackButtonId :
ReturnToPreviousMenu ( ) ;
return ;
case ContinueButtonId :
/* Don't continue if validation hasn't been done or has failed. */
if ( ! m_has_validated | | R_FAILED ( m_validation_info . result ) ) {
break ;
}
2020-07-11 06:05:30 +02:00
/* Check if exfat is supported. */
2020-07-28 00:42:17 +02:00
g_exfat_supported = m_update_info . exfat_supported & & R_SUCCEEDED ( m_validation_info . exfat_result ) ;
2020-07-11 06:05:30 +02:00
if ( ! g_exfat_supported ) {
2020-07-08 02:07:00 +02:00
g_use_exfat = false ;
}
2021-07-22 03:21:38 +02:00
/* Create the next menu. */
std : : shared_ptr < Menu > next_menu = std : : make_shared < ChooseResetMenu > ( g_current_menu ) ;
2020-07-28 00:42:17 +02:00
/* Warn the user if they're updating with exFAT supposed to be supported but not present/corrupted. */
if ( m_update_info . exfat_supported & & R_FAILED ( m_validation_info . exfat_result ) ) {
2021-07-22 03:21:38 +02:00
next_menu = std : : make_shared < WarningMenu > ( g_current_menu , next_menu , " Warning: exFAT firmware is missing or corrupt " , " Are you sure you want to proceed? " ) ;
2020-07-28 00:42:17 +02:00
}
2021-07-22 03:21:38 +02:00
/* Warn the user if they're updating to a version higher than supported. */
const u32 version = m_validation_info . invalid_key . version ;
if ( EncodeVersion ( ( version > > 26 ) & 0x1f , ( version > > 20 ) & 0x1f , ( version > > 16 ) & 0xf ) > g_supported_version ) {
next_menu = std : : make_shared < WarningMenu > ( g_current_menu , next_menu , " Warning: firmware is too new and not known to be supported " , " Are you sure you want to proceed? " ) ;
}
/* Change to the next menu. */
ChangeMenu ( next_menu ) ;
2020-07-08 02:07:00 +02:00
return ;
}
}
this - > UpdateButtons ( ) ;
}
void ValidateUpdateMenu : : Draw ( NVGcontext * vg , u64 ns ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
DrawWindow ( vg , " Update information " , x , y , WindowWidth , WindowHeight ) ;
2020-07-11 06:05:30 +02:00
DrawTextBackground ( vg , x + HorizontalInset , y + TitleGap , WindowWidth - HorizontalInset * 2.0f , TextAreaHeight ) ;
DrawTextBlock ( vg , m_log_buffer , x + HorizontalInset + TextHorizontalInset , y + TitleGap + TextVerticalInset , WindowWidth - ( HorizontalInset + TextHorizontalInset ) * 2.0f , TextAreaHeight - TextVerticalInset * 2.0f ) ;
2020-07-08 02:07:00 +02:00
this - > DrawButtons ( vg , ns ) ;
m_has_drawn = true ;
}
2020-07-11 06:05:30 +02:00
ChooseResetMenu : : ChooseResetMenu ( std : : shared_ptr < Menu > prev_menu ) : Menu ( prev_menu ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
const float button_width = ( WindowWidth - HorizontalInset * 2.0f ) / 2.0f - ButtonHorizontalGap ;
/* Add buttons. */
this - > AddButton ( ResetToFactorySettingsButtonId , " Reset to factory settings " , x + HorizontalInset , y + TitleGap , button_width , ButtonHeight ) ;
this - > AddButton ( PreserveSettingsButtonId , " Preserve settings " , x + HorizontalInset + button_width + ButtonHorizontalGap , y + TitleGap , button_width , ButtonHeight ) ;
this - > SetButtonSelected ( PreserveSettingsButtonId , true ) ;
}
void ChooseResetMenu : : Update ( u64 ns ) {
2020-12-02 11:21:05 +01:00
u64 k_down = padGetButtonsDown ( & g_pad ) ;
2020-07-11 06:05:30 +02:00
/* Go back if B is pressed. */
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_B ) {
2020-07-11 06:05:30 +02:00
ReturnToPreviousMenu ( ) ;
return ;
}
/* Take action if a button has been activated. */
if ( const Button * activated_button = this - > GetActivatedButton ( ) ; activated_button ! = nullptr ) {
switch ( activated_button - > id ) {
case ResetToFactorySettingsButtonId :
g_reset_to_factory = true ;
break ;
case PreserveSettingsButtonId :
g_reset_to_factory = false ;
break ;
}
2021-03-19 04:14:03 +01:00
std : : shared_ptr < Menu > next_menu ;
2020-07-11 06:05:30 +02:00
if ( g_exfat_supported ) {
2021-03-19 04:14:03 +01:00
next_menu = std : : make_shared < ChooseExfatMenu > ( g_current_menu ) ;
} else {
next_menu = std : : make_shared < WarningMenu > ( g_current_menu , std : : make_shared < InstallUpdateMenu > ( g_current_menu ) , " Ready to begin update installation " , " Are you sure you want to proceed? " ) ;
}
if ( g_reset_to_factory ) {
ChangeMenu ( std : : make_shared < WarningMenu > ( g_current_menu , next_menu , " Warning: Factory reset selected " , " Saves and installed games will be permanently deleted. " ) ) ;
2020-07-11 06:05:30 +02:00
} else {
2021-03-19 04:14:03 +01:00
ChangeMenu ( next_menu ) ;
2020-07-11 06:05:30 +02:00
}
}
this - > UpdateButtons ( ) ;
/* Fallback on selecting the exfat button. */
if ( const Button * selected_button = this - > GetSelectedButton ( ) ; k_down & & selected_button = = nullptr ) {
this - > SetButtonSelected ( PreserveSettingsButtonId , true ) ;
}
}
void ChooseResetMenu : : Draw ( NVGcontext * vg , u64 ns ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
DrawWindow ( vg , " Select settings mode " , x , y , WindowWidth , WindowHeight ) ;
this - > DrawButtons ( vg , ns ) ;
}
2020-07-08 02:07:00 +02:00
ChooseExfatMenu : : ChooseExfatMenu ( std : : shared_ptr < Menu > prev_menu ) : Menu ( prev_menu ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
2020-07-11 06:05:30 +02:00
const float button_width = ( WindowWidth - HorizontalInset * 2.0f ) / 2.0f - ButtonHorizontalGap ;
2020-07-08 02:07:00 +02:00
2020-07-11 06:05:30 +02:00
/* Add buttons. */
this - > AddButton ( Fat32ButtonId , " Install (FAT32) " , x + HorizontalInset , y + TitleGap , button_width , ButtonHeight ) ;
this - > AddButton ( ExFatButtonId , " Install (FAT32 + exFAT) " , x + HorizontalInset + button_width + ButtonHorizontalGap , y + TitleGap , button_width , ButtonHeight ) ;
2020-07-09 13:33:52 +02:00
/* Set the default selected button based on the user's current install. We aren't particularly concerned if fsIsExFatSupported fails. */
bool exfat_supported = false ;
fsIsExFatSupported ( & exfat_supported ) ;
if ( exfat_supported ) {
this - > SetButtonSelected ( ExFatButtonId , true ) ;
} else {
this - > SetButtonSelected ( Fat32ButtonId , true ) ;
}
2020-07-08 02:07:00 +02:00
}
void ChooseExfatMenu : : Update ( u64 ns ) {
2020-12-02 11:21:05 +01:00
u64 k_down = padGetButtonsDown ( & g_pad ) ;
2020-07-08 02:07:00 +02:00
/* Go back if B is pressed. */
2020-12-02 11:21:05 +01:00
if ( k_down & HidNpadButton_B ) {
2020-07-08 02:07:00 +02:00
ReturnToPreviousMenu ( ) ;
return ;
}
/* Take action if a button has been activated. */
if ( const Button * activated_button = this - > GetActivatedButton ( ) ; activated_button ! = nullptr ) {
switch ( activated_button - > id ) {
case Fat32ButtonId :
g_use_exfat = false ;
break ;
case ExFatButtonId :
g_use_exfat = true ;
break ;
}
2020-07-10 15:44:32 +02:00
ChangeMenu ( std : : make_shared < WarningMenu > ( g_current_menu , std : : make_shared < InstallUpdateMenu > ( g_current_menu ) , " Ready to begin update installation " , " Are you sure you want to proceed? " ) ) ;
2020-07-08 02:07:00 +02:00
}
this - > UpdateButtons ( ) ;
/* Fallback on selecting the exfat button. */
if ( const Button * selected_button = this - > GetSelectedButton ( ) ; k_down & & selected_button = = nullptr ) {
this - > SetButtonSelected ( ExFatButtonId , true ) ;
}
}
void ChooseExfatMenu : : Draw ( NVGcontext * vg , u64 ns ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
DrawWindow ( vg , " Select driver variant " , x , y , WindowWidth , WindowHeight ) ;
this - > DrawButtons ( vg , ns ) ;
}
InstallUpdateMenu : : InstallUpdateMenu ( std : : shared_ptr < Menu > prev_menu ) : Menu ( prev_menu ) , m_install_state ( InstallState : : NeedsDraw ) , m_progress_percent ( 0.0f ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
2020-07-11 06:05:30 +02:00
const float button_width = ( WindowWidth - HorizontalInset * 2.0f ) / 2.0f - ButtonHorizontalGap ;
2020-07-08 02:07:00 +02:00
/* Add buttons. */
2020-07-11 06:05:30 +02:00
this - > AddButton ( ShutdownButtonId , " Shutdown " , x + HorizontalInset , y + WindowHeight - BottomInset - ButtonHeight , button_width , ButtonHeight ) ;
this - > AddButton ( RebootButtonId , " Reboot " , x + HorizontalInset + button_width + ButtonHorizontalGap , y + WindowHeight - BottomInset - ButtonHeight , button_width , ButtonHeight ) ;
2020-07-08 02:07:00 +02:00
this - > SetButtonEnabled ( ShutdownButtonId , false ) ;
this - > SetButtonEnabled ( RebootButtonId , false ) ;
/* Prevent the home button from being pressed during installation. */
hiddbgDeactivateHomeButton ( ) ;
}
void InstallUpdateMenu : : MarkForReboot ( ) {
this - > SetButtonEnabled ( ShutdownButtonId , true ) ;
this - > SetButtonEnabled ( RebootButtonId , true ) ;
this - > SetButtonSelected ( RebootButtonId , true ) ;
m_install_state = InstallState : : AwaitingReboot ;
}
Result InstallUpdateMenu : : TransitionUpdateState ( ) {
Result rc = 0 ;
if ( m_install_state = = InstallState : : NeedsSetup ) {
/* Setup the update. */
if ( R_FAILED ( rc = amssuSetupUpdate ( nullptr , UpdateTaskBufferSize , g_update_path , g_use_exfat ) ) ) {
this - > LogText ( " Failed to setup update. \n Result: 0x%08x \n " , rc ) ;
this - > MarkForReboot ( ) ;
return rc ;
}
/* Log setup completion. */
this - > LogText ( " Update setup complete. \n " ) ;
m_install_state = InstallState : : NeedsPrepare ;
} else if ( m_install_state = = InstallState : : NeedsPrepare ) {
/* Request update preparation. */
if ( R_FAILED ( rc = amssuRequestPrepareUpdate ( & m_prepare_result ) ) ) {
this - > LogText ( " Failed to request update preparation. \n Result: 0x%08x \n " , rc ) ;
this - > MarkForReboot ( ) ;
return rc ;
}
/* Log awaiting prepare. */
this - > LogText ( " Preparing update... \n " ) ;
m_install_state = InstallState : : AwaitingPrepare ;
} else if ( m_install_state = = InstallState : : AwaitingPrepare ) {
/* Check if preparation has a result. */
if ( R_FAILED ( rc = asyncResultWait ( & m_prepare_result , 0 ) ) & & rc ! = 0xea01 ) {
this - > LogText ( " Failed to check update preparation result. \n Result: 0x%08x \n " , rc ) ;
this - > MarkForReboot ( ) ;
return rc ;
} else if ( R_SUCCEEDED ( rc ) ) {
if ( R_FAILED ( rc = asyncResultGet ( & m_prepare_result ) ) ) {
this - > LogText ( " Failed to prepare update. \n Result: 0x%08x \n " , rc ) ;
this - > MarkForReboot ( ) ;
return rc ;
}
}
/* Check if the update has been prepared. */
bool prepared ;
if ( R_FAILED ( rc = amssuHasPreparedUpdate ( & prepared ) ) ) {
this - > LogText ( " Failed to check if update has been prepared. \n Result: 0x%08x \n " , rc ) ;
this - > MarkForReboot ( ) ;
return rc ;
}
/* Mark for application if preparation complete. */
if ( prepared ) {
this - > LogText ( " Update preparation complete. \n Applying update... \n " ) ;
m_install_state = InstallState : : NeedsApply ;
return rc ;
}
/* Check update progress. */
NsSystemUpdateProgress update_progress = { } ;
if ( R_FAILED ( rc = amssuGetPrepareUpdateProgress ( & update_progress ) ) ) {
this - > LogText ( " Failed to check update progress. \n Result: 0x%08x \n " , rc ) ;
this - > MarkForReboot ( ) ;
return rc ;
}
/* Update progress percent. */
if ( update_progress . total_size > 0.0f ) {
m_progress_percent = static_cast < float > ( update_progress . current_size ) / static_cast < float > ( update_progress . total_size ) ;
} else {
m_progress_percent = 0.0f ;
}
} else if ( m_install_state = = InstallState : : NeedsApply ) {
/* Apply the prepared update. */
if ( R_FAILED ( rc = amssuApplyPreparedUpdate ( ) ) ) {
this - > LogText ( " Failed to apply update. \n Result: 0x%08x \n " , rc ) ;
2020-07-08 05:06:21 +02:00
} else {
/* Log success. */
this - > LogText ( " Update applied successfully. \n " ) ;
2020-07-11 06:05:30 +02:00
if ( g_reset_to_factory ) {
if ( R_FAILED ( rc = nsResetToFactorySettingsForRefurbishment ( ) ) ) {
/* Fallback on ResetToFactorySettings. */
if ( rc = = MAKERESULT ( Module_Libnx , LibnxError_IncompatSysVer ) ) {
if ( R_FAILED ( rc = nsResetToFactorySettings ( ) ) ) {
this - > LogText ( " Failed to reset to factory settings. \n Result: 0x%08x \n " , rc ) ;
this - > MarkForReboot ( ) ;
return rc ;
}
} else {
this - > LogText ( " Failed to reset to factory settings for refurbishment. \n Result: 0x%08x \n " , rc ) ;
this - > MarkForReboot ( ) ;
return rc ;
}
}
this - > LogText ( " Successfully reset to factory settings. \n " , rc ) ;
}
2020-07-08 02:07:00 +02:00
}
this - > MarkForReboot ( ) ;
return rc ;
}
return rc ;
}
void InstallUpdateMenu : : Update ( u64 ns ) {
/* Transition to the next update state. */
if ( m_install_state ! = InstallState : : NeedsDraw & & m_install_state ! = InstallState : : AwaitingReboot ) {
this - > TransitionUpdateState ( ) ;
}
/* Take action if a button has been activated. */
if ( const Button * activated_button = this - > GetActivatedButton ( ) ; activated_button ! = nullptr ) {
switch ( activated_button - > id ) {
case ShutdownButtonId :
if ( R_FAILED ( appletRequestToShutdown ( ) ) ) {
spsmShutdown ( false ) ;
}
break ;
case RebootButtonId :
if ( R_FAILED ( appletRequestToReboot ( ) ) ) {
spsmShutdown ( true ) ;
}
break ;
}
}
this - > UpdateButtons ( ) ;
}
void InstallUpdateMenu : : Draw ( NVGcontext * vg , u64 ns ) {
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f ;
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f ;
DrawWindow ( vg , " Installing update " , x , y , WindowWidth , WindowHeight ) ;
2020-07-11 06:05:30 +02:00
DrawProgressText ( vg , x + HorizontalInset , y + TitleGap , m_progress_percent ) ;
DrawProgressBar ( vg , x + HorizontalInset , y + TitleGap + ProgressTextHeight , WindowWidth - HorizontalInset * 2.0f , ProgressBarHeight , m_progress_percent ) ;
DrawTextBackground ( vg , x + HorizontalInset , y + TitleGap + ProgressTextHeight + ProgressBarHeight + VerticalGap , WindowWidth - HorizontalInset * 2.0f , TextAreaHeight ) ;
DrawTextBlock ( vg , m_log_buffer , x + HorizontalInset + TextHorizontalInset , y + TitleGap + ProgressTextHeight + ProgressBarHeight + VerticalGap + TextVerticalInset , WindowWidth - ( HorizontalInset + TextHorizontalInset ) * 2.0f , TextAreaHeight - TextVerticalInset * 2.0f ) ;
2020-07-08 02:07:00 +02:00
this - > DrawButtons ( vg , ns ) ;
/* We have drawn now, allow setup to occur. */
if ( m_install_state = = InstallState : : NeedsDraw ) {
this - > LogText ( " Beginning update setup... \n " ) ;
m_install_state = InstallState : : NeedsSetup ;
}
}
void InitializeMenu ( u32 screen_width , u32 screen_height ) {
2020-07-09 13:33:52 +02:00
Result rc = 0 ;
2020-12-02 11:21:05 +01:00
/* Configure and initialize the gamepad. */
padConfigureInput ( 1 , HidNpadStyleSet_NpadStandard ) ;
padInitializeDefault ( & g_pad ) ;
/* Initialize the touch screen. */
hidInitializeTouchScreen ( ) ;
2020-07-08 02:07:00 +02:00
/* Set the screen width and height. */
g_screen_width = screen_width ;
g_screen_height = screen_height ;
/* Mark as initialized. */
g_initialized = true ;
2020-07-09 13:33:52 +02:00
/* Attempt to get the exosphere version. */
u64 version ;
if ( R_FAILED ( rc = splGetConfig ( static_cast < SplConfigItem > ( ExosphereApiVersionConfigItem ) , & version ) ) ) {
ChangeMenu ( std : : make_shared < ErrorMenu > ( " Atmosphere not found " , " Daybreak requires Atmosphere to be installed. " , rc ) ) ;
return ;
}
const u32 version_micro = ( version > > 40 ) & 0xff ;
const u32 version_minor = ( version > > 48 ) & 0xff ;
const u32 version_major = ( version > > 56 ) & 0xff ;
/* Validate the exosphere version. */
2021-07-22 03:21:38 +02:00
const bool ams_supports_sysupdate_api = EncodeVersion ( version_major , version_minor , version_micro ) > = EncodeVersion ( 0 , 14 , 0 ) ;
2020-07-09 13:33:52 +02:00
if ( ! ams_supports_sysupdate_api ) {
ChangeMenu ( std : : make_shared < ErrorMenu > ( " Outdated Atmosphere version " , " Daybreak requires Atmosphere 0.14.0 or later. " , rc ) ) ;
return ;
}
2021-07-22 03:21:38 +02:00
/* Attempt to get the supported version. */
if ( R_SUCCEEDED ( rc = splGetConfig ( static_cast < SplConfigItem > ( ExosphereSupportedHosVersion ) , & version ) ) ) {
g_supported_version = static_cast < u32 > ( version ) ;
}
2020-07-09 13:33:52 +02:00
/* Initialize ams:su. */
if ( R_FAILED ( rc = amssuInitialize ( ) ) ) {
fatalThrow ( rc ) ;
}
/* Change the current menu to the main menu. */
g_current_menu = std : : make_shared < MainMenu > ( ) ;
2020-07-08 02:07:00 +02:00
}
void UpdateMenu ( u64 ns ) {
DBK_ABORT_UNLESS ( g_initialized ) ;
DBK_ABORT_UNLESS ( g_current_menu ! = nullptr ) ;
UpdateInput ( ) ;
g_current_menu - > Update ( ns ) ;
}
void RenderMenu ( NVGcontext * vg , u64 ns ) {
DBK_ABORT_UNLESS ( g_initialized ) ;
DBK_ABORT_UNLESS ( g_current_menu ! = nullptr ) ;
/* Draw background. */
DrawBackground ( vg , g_screen_width , g_screen_height ) ;
/* Draw stars. */
DrawStar ( vg , 40.0f , 64.0f , 3.0f ) ;
DrawStar ( vg , 110.0f , 300.0f , 3.0f ) ;
DrawStar ( vg , 200.0f , 150.0f , 4.0f ) ;
DrawStar ( vg , 370.0f , 280.0f , 3.0f ) ;
DrawStar ( vg , 450.0f , 40.0f , 3.5f ) ;
DrawStar ( vg , 710.0f , 90.0f , 3.0f ) ;
DrawStar ( vg , 900.0f , 240.0f , 3.0f ) ;
DrawStar ( vg , 970.0f , 64.0f , 4.0f ) ;
DrawStar ( vg , 1160.0f , 160.0f , 3.5f ) ;
DrawStar ( vg , 1210.0f , 350.0f , 3.0f ) ;
g_current_menu - > Draw ( vg , ns ) ;
}
bool IsExitRequested ( ) {
return g_exit_requested ;
}
}