2020-04-16 01:06:41 +01:00
/*
2021-03-26 04:35:14 +00:00
* nxdt_utils . c
2020-04-16 01:06:41 +01:00
*
2023-04-08 12:42:22 +01:00
* Copyright ( c ) 2020 - 2023 , DarkMatterCore < pabloacurielz @ gmail . com > .
2020-07-03 10:31:22 +01:00
*
* This file is part of nxdumptool ( https : //github.com/DarkMatterCore/nxdumptool).
*
2021-03-25 19:26:58 +00:00
* nxdumptool 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 .
2020-04-16 01:06:41 +01:00
*
2021-03-25 19:26:58 +00:00
* nxdumptool 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 .
2020-04-16 01:06:41 +01:00
*
* You should have received a copy of the GNU General Public License
2021-03-25 19:26:58 +00:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2020-04-16 01:06:41 +01:00
*/
2020-12-02 06:28:35 +00:00
# include <sys/statvfs.h>
2020-11-08 19:08:30 +00:00
2021-03-26 04:35:14 +00:00
# include "nxdt_utils.h"
2020-04-16 11:13:11 +01:00
# include "keys.h"
2020-04-15 21:50:07 +01:00
# include "gamecard.h"
2020-04-16 11:13:11 +01:00
# include "services.h"
2020-04-21 11:23:33 +01:00
# include "nca.h"
2020-05-05 16:22:16 +01:00
# include "usb.h"
2020-07-25 06:56:35 +01:00
# include "title.h"
2020-10-14 14:23:49 +01:00
# include "bfttf.h"
2021-06-23 19:27:06 +01:00
# include "nxdt_bfsar.h"
2020-04-17 22:59:05 +01:00
# include "fatfs/ff.h"
2020-04-15 21:50:07 +01:00
2021-06-01 02:12:15 +01:00
/* Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits. */
2022-07-27 23:53:52 +01:00
/* Actually expressed in bytes, not codepoints. */
2021-06-01 02:12:15 +01:00
# define NT_MAX_FILENAME_LENGTH 255
2021-08-07 10:44:36 +01:00
/* Type definitions. */
typedef struct {
u32 major ;
u32 minor ;
u32 micro ;
} UtilsApplicationVersion ;
2020-04-15 21:50:07 +01:00
/* Global variables. */
2021-05-18 13:32:43 +01:00
static bool g_resourcesInit = false ;
2020-05-03 00:40:50 +01:00
static Mutex g_resourcesMutex = 0 ;
2022-07-12 01:27:03 +01:00
static const char * g_appLaunchPath = NULL ;
2020-05-03 00:40:50 +01:00
static FsFileSystem * g_sdCardFileSystem = NULL ;
2022-07-12 01:27:03 +01:00
static int g_nxLinkSocketFd = - 1 ;
2020-05-03 00:40:50 +01:00
2020-04-16 11:13:11 +01:00
static u8 g_customFirmwareType = UtilsCustomFirmwareType_Unknown ;
2023-04-08 12:34:53 +01:00
static u8 g_productModel = SetSysProductModel_Invalid ;
2021-05-18 13:32:43 +01:00
static bool g_isDevUnit = false ;
static AppletType g_programAppletType = AppletType_None ;
static FsStorage g_emmcBisSystemPartitionStorage = { 0 } ;
static FATFS * g_emmcBisSystemPartitionFatFsObj = NULL ;
2020-04-15 21:50:07 +01:00
static AppletHookCookie g_systemOverclockCookie = { 0 } ;
2021-07-29 17:48:32 +01:00
static bool g_longRunningProcess = false ;
2021-05-18 13:32:43 +01:00
2022-07-27 23:53:52 +01:00
static const char * g_sizeSuffixes [ ] = { " B " , " KiB " , " MiB " , " GiB " , " TiB " } ;
2020-07-26 05:57:12 +01:00
static const u32 g_sizeSuffixesCount = MAX_ELEMENTS ( g_sizeSuffixes ) ;
2021-05-11 07:00:33 +01:00
static const char g_illegalFileSystemChars [ ] = " \\ /:*? \" <>| " ;
static const size_t g_illegalFileSystemCharsLength = ( MAX_ELEMENTS ( g_illegalFileSystemChars ) - 1 ) ;
2021-02-12 20:35:23 +00:00
2021-07-21 16:04:18 +01:00
static const char * g_outputDirs [ ] = {
HBMENU_BASE_PATH ,
APP_BASE_PATH ,
GAMECARD_PATH ,
CERT_PATH ,
HFS_PATH ,
NSP_PATH ,
TICKET_PATH ,
NCA_PATH ,
NCA_FS_PATH
} ;
static const size_t g_outputDirsCount = MAX_ELEMENTS ( g_outputDirs ) ;
2021-08-07 09:42:03 +01:00
static bool g_appUpdated = false ;
2020-04-15 21:50:07 +01:00
/* Function prototypes. */
2021-05-18 13:32:43 +01:00
static void _utilsGetLaunchPath ( int program_argc , const char * * program_argv ) ;
2021-02-25 03:36:53 +00:00
static void _utilsGetCustomFirmwareType ( void ) ;
2020-08-15 22:22:49 +01:00
2023-04-08 12:34:53 +01:00
static bool _utilsGetProductModel ( void ) ;
2020-08-15 22:22:49 +01:00
2023-04-08 12:34:53 +01:00
static bool _utilsIsDevelopmentUnit ( void ) ;
2021-05-18 13:32:43 +01:00
2020-05-03 00:40:50 +01:00
static bool utilsMountEmmcBisSystemPartitionStorage ( void ) ;
static void utilsUnmountEmmcBisSystemPartitionStorage ( void ) ;
2022-07-12 01:27:03 +01:00
static void utilsOverclockSystem ( bool overclock ) ;
2020-04-15 21:50:07 +01:00
static void utilsOverclockSystemAppletHook ( AppletHookType hook , void * param ) ;
2021-07-29 17:48:32 +01:00
static void utilsChangeHomeButtonBlockStatus ( bool block ) ;
2022-07-27 23:53:52 +01:00
static size_t utilsGetUtf8StringLimit ( const char * str , size_t str_size , size_t byte_limit ) ;
2021-06-01 02:12:15 +01:00
2021-05-18 13:32:43 +01:00
bool utilsInitializeResources ( const int program_argc , const char * * program_argv )
2020-05-03 00:40:50 +01:00
{
2021-03-30 20:30:10 +01:00
Result rc = 0 ;
2021-07-21 16:04:18 +01:00
bool ret = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_resourcesMutex )
2021-03-08 11:11:28 +00:00
{
2021-05-18 13:32:43 +01:00
ret = g_resourcesInit ;
if ( ret ) break ;
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Lock applet exit. */
appletLockExit ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Retrieve pointer to the application launch path. */
_utilsGetLaunchPath ( program_argc , program_argv ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Retrieve pointer to the SD card FsFileSystem element. */
2021-08-07 09:42:03 +01:00
if ( ! ( g_sdCardFileSystem = fsdevGetDeviceFileSystem ( DEVOPTAB_SDMC_DEVICE ) ) )
2021-03-08 11:11:28 +00:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to retrieve FsFileSystem object for the SD card! " ) ;
2021-05-18 13:32:43 +01:00
break ;
2021-03-08 11:11:28 +00:00
}
2022-07-05 02:04:28 +01:00
2022-07-12 01:27:03 +01:00
/* Initialize needed services. */
if ( ! servicesInitialize ( ) ) break ;
/* Check if a valid nxlink host IP address was set by libnx. */
/* If so, initialize nxlink connection without redirecting stdout and/or stderr. */
if ( __nxlink_host . s_addr ! = 0 & & __nxlink_host . s_addr ! = INADDR_NONE ) g_nxLinkSocketFd = nxlinkConnectToHost ( false , false ) ;
2022-07-05 02:04:28 +01:00
2022-07-12 04:27:30 +01:00
# if LOG_LEVEL <= LOG_LEVEL_INFO
2022-07-12 01:27:03 +01:00
/* Log info messages. */
2021-05-18 13:32:43 +01:00
u32 hos_version = hosversionGet ( ) ;
2022-07-12 04:27:30 +01:00
LOG_MSG_INFO ( APP_TITLE " v " APP_VERSION " starting ( " GIT_REV " ). Built on " BUILD_TIMESTAMP " . " ) ;
if ( g_nxLinkSocketFd > = 0 ) LOG_MSG_INFO ( " nxlink enabled! Host IP address: %s. " , inet_ntoa ( __nxlink_host ) ) ;
LOG_MSG_INFO ( " Horizon OS version: %u.%u.%u. " , HOSVER_MAJOR ( hos_version ) , HOSVER_MINOR ( hos_version ) , HOSVER_MICRO ( hos_version ) ) ;
# endif
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Retrieve custom firmware type. */
_utilsGetCustomFirmwareType ( ) ;
2022-07-12 04:27:30 +01:00
if ( g_customFirmwareType ! = UtilsCustomFirmwareType_Unknown ) LOG_MSG_INFO ( " Detected %s CFW. " , ( g_customFirmwareType = = UtilsCustomFirmwareType_Atmosphere ? " Atmosphère " : \
( g_customFirmwareType = = UtilsCustomFirmwareType_SXOS ? " SX OS " : " ReiNX " ) ) ) ;
2022-07-05 02:04:28 +01:00
2023-04-08 12:34:53 +01:00
/* Get product model. */
if ( ! _utilsGetProductModel ( ) ) break ;
/* Get development unit flag. */
2021-05-18 13:32:43 +01:00
if ( ! _utilsIsDevelopmentUnit ( ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Get applet type. */
g_programAppletType = appletGetAppletType ( ) ;
2023-04-08 12:34:53 +01:00
LOG_MSG_INFO ( " Running under %s %s unit in %s mode. " , g_isDevUnit ? " development " : " retail " , utilsIsMarikoUnit ( ) ? " Mariko " : " Erista " , utilsIsAppletMode ( ) ? " applet " : " title override " ) ;
2022-07-05 02:04:28 +01:00
2021-07-21 16:04:18 +01:00
/* Create output directories (SD card only). */
2022-03-18 16:48:16 +00:00
/* TODO: remove the APP_TITLE check whenever we're ready for a release. */
if ( ! strcasecmp ( APP_TITLE , " nxdumptool " ) ) utilsCreateOutputDirectories ( NULL ) ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
if ( g_appLaunchPath )
{
2022-07-12 04:27:30 +01:00
LOG_MSG_INFO ( " Launch path: \" %s \" . " , g_appLaunchPath ) ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Move NRO if the launch path isn't the right one, then return. */
/* TODO: uncomment this block whenever we are ready for a release. */
/*if (strcmp(g_appLaunchPath, NRO_PATH) != 0)
{
remove ( NRO_PATH ) ;
rename ( g_appLaunchPath , NRO_PATH ) ;
2022-07-05 02:04:28 +01:00
2022-07-12 04:27:30 +01:00
LOG_MSG_INFO ( " Moved NRO to \" %s \" . Please reload the application. " , NRO_PATH ) ;
2021-08-07 09:42:03 +01:00
break ;
} */
}
2022-07-05 02:04:28 +01:00
2021-07-25 23:23:44 +01:00
/* Initialize HTTP interface. */
2023-04-08 12:34:53 +01:00
/* cURL must be initialized before starting any other threads. */
2021-07-25 23:23:44 +01:00
if ( ! httpInitialize ( ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Initialize USB interface. */
if ( ! usbInitialize ( ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Initialize USB Mass Storage interface. */
if ( ! umsInitialize ( ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-22 09:45:40 +01:00
/* Load keyset. */
2021-07-23 08:24:00 +01:00
if ( ! keysLoadKeyset ( ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to load keyset! \n Update your keys file with Lockpick_RCM: \n " LOCKPICK_RCM_URL ) ;
2021-07-23 08:24:00 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Allocate NCA crypto buffer. */
if ( ! ncaAllocateCryptoBuffer ( ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Unable to allocate memory for NCA crypto buffer! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Initialize gamecard interface. */
if ( ! gamecardInitialize ( ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Initialize title interface. */
if ( ! titleInitialize ( ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Initialize BFTTF interface. */
if ( ! bfttfInitialize ( ) ) break ;
2022-07-05 02:04:28 +01:00
2021-06-23 19:27:06 +01:00
/* Initialize BFSAR interface. */
//if (!bfsarInitialize()) break;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Mount eMMC BIS System partition. */
if ( ! utilsMountEmmcBisSystemPartitionStorage ( ) ) break ;
2022-07-05 02:04:28 +01:00
2021-07-21 16:04:18 +01:00
/* Mount application RomFS. */
rc = romfsInit ( ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to mount " APP_TITLE " 's RomFS container! " ) ;
2021-07-21 16:04:18 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-07-25 23:23:44 +01:00
/* Initialize configuration interface. */
2021-07-21 16:04:18 +01:00
if ( ! configInitialize ( ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Setup an applet hook to change the hardware clocks after a system mode change (docked <-> undocked). */
appletHook ( & g_systemOverclockCookie , utilsOverclockSystemAppletHook , NULL ) ;
2022-07-05 02:04:28 +01:00
2021-07-21 16:04:18 +01:00
/* Enable video recording if we're running under title override mode. */
2023-04-08 12:34:53 +01:00
if ( ! utilsIsAppletMode ( ) )
2021-07-21 16:04:18 +01:00
{
bool flag = false ;
rc = appletIsGamePlayRecordingSupported ( & flag ) ;
if ( R_SUCCEEDED ( rc ) & & flag ) appletInitializeGamePlayRecording ( ) ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Update flags. */
ret = g_resourcesInit = true ;
2021-03-08 11:11:28 +00:00
}
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
if ( ! ret )
{
2022-07-12 04:27:30 +01:00
char * msg = NULL ;
2021-08-25 21:48:01 +01:00
size_t msg_size = 0 ;
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Generate error message. */
utilsAppendFormattedStringToBuffer ( & msg , & msg_size , " An error occurred while initializing resources. " ) ;
2022-07-12 04:27:30 +01:00
# if LOG_LEVEL <= LOG_LEVEL_ERROR
/* Get last log message. */
char log_msg [ 0x100 ] = { 0 } ;
logGetLastMessage ( log_msg , sizeof ( log_msg ) ) ;
2021-08-25 21:48:01 +01:00
if ( * log_msg ) utilsAppendFormattedStringToBuffer ( & msg , & msg_size , " \n \n %s " , log_msg ) ;
2022-07-12 04:27:30 +01:00
# endif
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Print error message. */
utilsPrintConsoleError ( msg ) ;
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Free error message. */
if ( msg ) free ( msg ) ;
}
2022-07-05 02:04:28 +01:00
2020-05-03 00:40:50 +01:00
return ret ;
}
void utilsCloseResources ( void )
{
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_resourcesMutex )
2021-03-30 20:30:10 +01:00
{
2021-07-29 17:48:32 +01:00
/* Unset long running process state. */
utilsSetLongRunningProcessState ( false ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Unset our overclock applet hook. */
appletUnhook ( & g_systemOverclockCookie ) ;
2022-07-05 02:04:28 +01:00
2021-07-21 16:04:18 +01:00
/* Close configuration interface. */
configExit ( ) ;
2022-07-05 02:04:28 +01:00
2021-07-21 16:04:18 +01:00
/* Unmount application RomFS. */
romfsExit ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Unmount eMMC BIS System partition. */
utilsUnmountEmmcBisSystemPartitionStorage ( ) ;
2022-07-05 02:04:28 +01:00
2021-06-23 19:27:06 +01:00
/* Deinitialize BFSAR interface. */
2021-07-25 23:23:44 +01:00
//bfsarExit();
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Deinitialize BFTTF interface. */
bfttfExit ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Deinitialize title interface. */
titleExit ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Deinitialize gamecard interface. */
gamecardExit ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Free NCA crypto buffer. */
ncaFreeCryptoBuffer ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Close USB Mass Storage interface. */
umsExit ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Close USB interface. */
usbExit ( ) ;
2022-07-05 02:04:28 +01:00
2021-07-25 23:23:44 +01:00
/* Close HTTP interface. */
httpExit ( ) ;
2022-07-05 02:04:28 +01:00
2022-07-12 01:27:03 +01:00
/* Close nxlink socket. */
if ( g_nxLinkSocketFd > = 0 )
{
close ( g_nxLinkSocketFd ) ;
g_nxLinkSocketFd = - 1 ;
}
2021-05-18 13:32:43 +01:00
/* Close initialized services. */
servicesClose ( ) ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Replace application NRO (if needed). */
/* TODO: uncomment this block whenever we're ready for a release. */
/*if (g_appUpdated)
{
remove ( NRO_PATH ) ;
rename ( NRO_TMP_PATH , NRO_PATH ) ;
} */
2022-07-05 02:04:28 +01:00
2022-07-12 04:27:30 +01:00
# if LOG_LEVEL <= LOG_LEVEL_ERROR
2021-05-18 13:32:43 +01:00
/* Close logfile. */
logCloseLogFile ( ) ;
2022-07-12 04:27:30 +01:00
# endif
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Unlock applet exit. */
appletUnlockExit ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
g_resourcesInit = false ;
}
}
const char * utilsGetLaunchPath ( void )
{
return g_appLaunchPath ;
}
2022-07-12 01:27:03 +01:00
int utilsGetNxLinkFileDescriptor ( void )
{
return g_nxLinkSocketFd ;
}
2021-05-18 13:32:43 +01:00
FsFileSystem * utilsGetSdCardFileSystemObject ( void )
{
return g_sdCardFileSystem ;
}
bool utilsCommitSdCardFileSystemChanges ( void )
{
return ( g_sdCardFileSystem ? R_SUCCEEDED ( fsFsCommit ( g_sdCardFileSystem ) ) : false ) ;
}
u8 utilsGetCustomFirmwareType ( void )
{
return g_customFirmwareType ;
}
2023-04-08 12:34:53 +01:00
bool utilsIsMarikoUnit ( void )
{
return ( g_productModel > SetSysProductModel_Copper ) ;
}
2021-05-18 13:32:43 +01:00
bool utilsIsDevelopmentUnit ( void )
{
return g_isDevUnit ;
}
2023-04-08 12:34:53 +01:00
bool utilsIsAppletMode ( void )
2021-05-18 13:32:43 +01:00
{
2023-04-08 12:34:53 +01:00
return ( g_programAppletType > AppletType_Application & & g_programAppletType < AppletType_SystemApplication ) ;
2021-05-18 13:32:43 +01:00
}
FsStorage * utilsGetEmmcBisSystemPartitionStorage ( void )
{
return & g_emmcBisSystemPartitionStorage ;
}
2021-07-29 17:48:32 +01:00
void utilsSetLongRunningProcessState ( bool state )
2021-05-18 13:32:43 +01:00
{
SCOPED_LOCK ( & g_resourcesMutex )
{
2022-07-12 01:27:03 +01:00
/* Don't proceed if resources haven't been initialized, or if the requested state matches the current one. */
if ( ! g_resourcesInit | | state = = g_longRunningProcess ) break ;
2022-07-05 02:04:28 +01:00
2021-07-29 17:48:32 +01:00
/* Change HOME button block status. */
utilsChangeHomeButtonBlockStatus ( state ) ;
2022-07-05 02:04:28 +01:00
2022-07-12 01:27:03 +01:00
/* Enable/disable screen dimming and auto sleep. */
2021-07-29 17:48:32 +01:00
appletSetMediaPlaybackState ( state ) ;
2022-07-05 02:04:28 +01:00
2022-07-12 01:27:03 +01:00
/* Enable/disable system overclock. */
utilsOverclockSystem ( configGetBoolean ( " overclock " ) & state ) ;
2021-07-29 17:48:32 +01:00
/* Update flag. */
g_longRunningProcess = state ;
2021-03-30 20:30:10 +01:00
}
2020-05-03 00:40:50 +01:00
}
2020-04-17 22:59:05 +01:00
2020-08-18 06:04:13 +01:00
bool utilsCreateThread ( Thread * out_thread , ThreadFunc func , void * arg , int cpu_id )
{
/* Core 3 is reserved for HOS, so we can only use cores 0, 1 and 2. */
/* -2 can be provided to use the default process core. */
if ( ! out_thread | | ! func | | ( cpu_id < 0 & & cpu_id ! = - 2 ) | | cpu_id > 2 )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-08-18 06:04:13 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
Result rc = 0 ;
u64 core_mask = 0 ;
size_t stack_size = 0x20000 ; /* Same value as libnx's newlib. */
bool success = false ;
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
memset ( out_thread , 0 , sizeof ( Thread ) ) ;
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
/* Get process core mask. */
rc = svcGetInfo ( & core_mask , InfoType_CoreMask , CUR_PROCESS_HANDLE , 0 ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " svcGetInfo failed! (0x%X). " , rc ) ;
2020-08-18 06:04:13 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
/* Create thread. */
/* Enable preemptive multithreading by using priority 0x3B. */
rc = threadCreate ( out_thread , func , arg , NULL , stack_size , 0x3B , cpu_id ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " threadCreate failed! (0x%X). " , rc ) ;
2020-08-18 06:04:13 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
/* Set thread core mask. */
rc = svcSetThreadCoreMask ( out_thread - > handle , cpu_id = = - 2 ? - 1 : cpu_id , core_mask ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " svcSetThreadCoreMask failed! (0x%X). " , rc ) ;
2020-08-18 06:04:13 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
/* Start thread. */
rc = threadStart ( out_thread ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " threadStart failed! (0x%X). " , rc ) ;
2020-08-18 06:04:13 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
success = true ;
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
end :
if ( ! success & & out_thread - > handle ! = INVALID_HANDLE ) threadClose ( out_thread ) ;
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
return success ;
}
void utilsJoinThread ( Thread * thread )
{
if ( ! thread | | thread - > handle = = INVALID_HANDLE )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-08-18 06:04:13 +01:00
return ;
}
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
Result rc = threadWaitForExit ( thread ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " threadWaitForExit failed! (0x%X). " , rc ) ;
2020-08-18 06:04:13 +01:00
return ;
}
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
threadClose ( thread ) ;
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
memset ( thread , 0 , sizeof ( Thread ) ) ;
}
2021-03-29 19:27:35 +01:00
__attribute__ ( ( format ( printf , 3 , 4 ) ) ) bool utilsAppendFormattedStringToBuffer ( char * * dst , size_t * dst_size , const char * fmt , . . . )
2020-10-02 10:53:58 +01:00
{
2021-03-07 23:22:49 +00:00
if ( ! dst | | ! dst_size | | ( ! * dst & & * dst_size ) | | ( * dst & & ! * dst_size ) | | ! fmt | | ! * fmt )
2020-10-02 10:53:58 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-10-02 10:53:58 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-10-02 10:53:58 +01:00
va_list args ;
2022-07-05 02:04:28 +01:00
2021-03-08 11:11:28 +00:00
int formatted_str_len = 0 ;
size_t formatted_str_len_cast = 0 ;
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
char * dst_ptr = * dst , * tmp_str = NULL ;
size_t dst_cur_size = * dst_size , dst_str_len = ( dst_ptr ? strlen ( dst_ptr ) : 0 ) ;
2022-07-05 02:04:28 +01:00
2020-10-02 10:53:58 +01:00
bool success = false ;
2022-07-05 02:04:28 +01:00
2021-03-08 11:11:28 +00:00
if ( dst_cur_size & & dst_str_len > = dst_cur_size )
2020-10-02 10:53:58 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " String length is equal to or greater than the provided buffer size! (0x%lX >= 0x%lX). " , dst_str_len , dst_cur_size ) ;
2021-03-07 23:22:49 +00:00
return false ;
2020-10-02 10:53:58 +01:00
}
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
va_start ( args , fmt ) ;
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
/* Get formatted string length. */
2020-10-02 10:53:58 +01:00
formatted_str_len = vsnprintf ( NULL , 0 , fmt , args ) ;
2021-03-08 11:11:28 +00:00
if ( formatted_str_len < = 0 )
2020-10-02 10:53:58 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to retrieve formatted string length! " ) ;
2020-10-02 10:53:58 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-08 11:11:28 +00:00
formatted_str_len_cast = ( size_t ) ( formatted_str_len + 1 ) ;
2022-07-05 02:04:28 +01:00
2021-03-08 11:11:28 +00:00
if ( ! dst_cur_size | | formatted_str_len_cast > ( dst_cur_size - dst_str_len ) )
2020-10-02 10:53:58 +01:00
{
2021-03-07 23:22:49 +00:00
/* Update buffer size. */
2021-03-08 11:11:28 +00:00
dst_cur_size = ( dst_str_len + formatted_str_len_cast ) ;
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
/* Reallocate buffer. */
tmp_str = realloc ( dst_ptr , dst_cur_size ) ;
if ( ! tmp_str )
2020-10-02 10:53:58 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to resize buffer to 0x%lX byte(s). " , dst_cur_size ) ;
2020-10-02 10:53:58 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
dst_ptr = tmp_str ;
tmp_str = NULL ;
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
/* Clear allocated area. */
2021-03-08 11:11:28 +00:00
memset ( dst_ptr + dst_str_len , 0 , formatted_str_len_cast ) ;
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
/* Update pointers. */
* dst = dst_ptr ;
* dst_size = dst_cur_size ;
2020-10-02 10:53:58 +01:00
}
2022-07-05 02:04:28 +01:00
2021-03-07 23:22:49 +00:00
/* Generate formatted string. */
vsprintf ( dst_ptr + dst_str_len , fmt , args ) ;
2020-10-02 10:53:58 +01:00
success = true ;
2022-07-05 02:04:28 +01:00
2020-10-02 10:53:58 +01:00
end :
va_end ( args ) ;
2022-07-05 02:04:28 +01:00
2020-10-02 10:53:58 +01:00
return success ;
}
2020-05-05 19:04:23 +01:00
void utilsReplaceIllegalCharacters ( char * str , bool ascii_only )
2020-05-03 00:40:50 +01:00
{
2021-06-01 02:12:15 +01:00
size_t str_size = 0 , cur_pos = 0 ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( ! str | | ! ( str_size = strlen ( str ) ) ) return ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
u8 * ptr1 = ( u8 * ) str , * ptr2 = ptr1 ;
ssize_t units = 0 ;
u32 code = 0 ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
while ( cur_pos < str_size )
2020-04-21 11:23:33 +01:00
{
2021-06-01 02:12:15 +01:00
units = decode_utf8 ( & code , ptr1 ) ;
if ( units < 0 ) break ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
if ( memchr ( g_illegalFileSystemChars , ( int ) code , g_illegalFileSystemCharsLength ) | | code < 0x20 | | ( ! ascii_only & & code = = 0x7F ) | | ( ascii_only & & code > = 0x7F ) )
{
* ptr2 + + = ' _ ' ;
} else {
if ( ptr2 ! = ptr1 ) memmove ( ptr2 , ptr1 , ( size_t ) units ) ;
ptr2 + = units ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
ptr1 + = units ;
cur_pos + = ( size_t ) units ;
2020-04-21 11:23:33 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
* ptr2 = ' \0 ' ;
2020-04-15 21:50:07 +01:00
}
2020-05-03 00:40:50 +01:00
void utilsTrimString ( char * str )
2020-04-15 21:50:07 +01:00
{
2020-05-03 00:40:50 +01:00
size_t strsize = 0 ;
char * start = NULL , * end = NULL ;
2022-07-05 02:04:28 +01:00
2020-05-03 00:40:50 +01:00
if ( ! str | | ! ( strsize = strlen ( str ) ) ) return ;
2022-07-05 02:04:28 +01:00
2020-05-03 00:40:50 +01:00
start = str ;
end = ( start + strsize ) ;
2022-07-05 02:04:28 +01:00
2020-05-03 00:40:50 +01:00
while ( - - end > = start )
{
if ( ! isspace ( ( unsigned char ) * end ) ) break ;
}
2022-07-05 02:04:28 +01:00
2020-05-03 00:40:50 +01:00
* ( + + end ) = ' \0 ' ;
2022-07-05 02:04:28 +01:00
2020-05-03 00:40:50 +01:00
while ( isspace ( ( unsigned char ) * start ) ) start + + ;
2022-07-05 02:04:28 +01:00
2020-05-03 00:40:50 +01:00
if ( start ! = str ) memmove ( str , start , end - start + 1 ) ;
2020-04-16 11:13:11 +01:00
}
2021-05-11 23:36:15 +01:00
void utilsGenerateHexStringFromData ( char * dst , size_t dst_size , const void * src , size_t src_size , bool uppercase )
2020-04-17 22:59:05 +01:00
{
if ( ! src | | ! src_size | | ! dst | | dst_size < ( ( src_size * 2 ) + 1 ) ) return ;
2022-07-05 02:04:28 +01:00
2020-04-17 22:59:05 +01:00
size_t i , j ;
const u8 * src_u8 = ( const u8 * ) src ;
2022-07-05 02:04:28 +01:00
2020-04-17 22:59:05 +01:00
for ( i = 0 , j = 0 ; i < src_size ; i + + )
{
2020-04-19 23:44:22 +01:00
char h_nib = ( ( src_u8 [ i ] > > 4 ) & 0xF ) ;
char l_nib = ( src_u8 [ i ] & 0xF ) ;
2022-07-05 02:04:28 +01:00
2021-05-11 23:36:15 +01:00
dst [ j + + ] = ( h_nib + ( h_nib < 0xA ? 0x30 : ( uppercase ? 0x37 : 0x57 ) ) ) ;
dst [ j + + ] = ( l_nib + ( l_nib < 0xA ? 0x30 : ( uppercase ? 0x37 : 0x57 ) ) ) ;
2020-04-17 22:59:05 +01:00
}
2022-07-05 02:04:28 +01:00
2020-04-17 22:59:05 +01:00
dst [ j ] = ' \0 ' ;
}
2021-07-29 08:50:17 +01:00
void utilsGenerateFormattedSizeString ( double size , char * dst , size_t dst_size )
2020-07-26 05:57:12 +01:00
{
if ( ! dst | | dst_size < 2 ) return ;
2022-07-05 02:04:28 +01:00
2021-07-29 08:50:17 +01:00
size = fabs ( size ) ;
2022-07-05 02:04:28 +01:00
2020-07-26 05:57:12 +01:00
for ( u32 i = 0 ; i < g_sizeSuffixesCount ; i + + )
{
2021-07-29 08:50:17 +01:00
if ( size > = pow ( 1024.0 , i + 1 ) & & ( i + 1 ) < g_sizeSuffixesCount ) continue ;
2022-07-05 02:04:28 +01:00
2021-07-29 08:50:17 +01:00
size / = pow ( 1024.0 , i ) ;
snprintf ( dst , dst_size , " %.2F %s " , size , g_sizeSuffixes [ i ] ) ;
2020-07-26 05:57:12 +01:00
break ;
}
}
2020-12-24 08:49:38 +00:00
bool utilsGetFileSystemStatsByPath ( const char * path , u64 * out_total , u64 * out_free )
2020-08-01 05:43:55 +01:00
{
2020-12-02 06:28:35 +00:00
char * name_end = NULL , stat_path [ 32 ] = { 0 } ;
struct statvfs info = { 0 } ;
int ret = - 1 ;
2022-07-05 02:04:28 +01:00
2020-12-24 08:49:38 +00:00
if ( ! path | | ! * path | | ! ( name_end = strchr ( path , ' : ' ) ) | | * ( name_end + 1 ) ! = ' / ' | | ( ! out_total & & ! out_free ) )
2020-12-02 06:28:35 +00:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-12-02 06:28:35 +00:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-12-02 06:28:35 +00:00
name_end + = 2 ;
sprintf ( stat_path , " %.*s " , ( int ) ( name_end - path ) , path ) ;
2022-07-05 02:04:28 +01:00
2020-12-02 06:28:35 +00:00
if ( ( ret = statvfs ( stat_path , & info ) ) ! = 0 )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " statvfs failed for \" %s \" ! (%d) (errno: %d). " , stat_path , ret , errno ) ;
2020-12-02 06:28:35 +00:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-12-24 08:49:38 +00:00
if ( out_total ) * out_total = ( ( u64 ) info . f_blocks * ( u64 ) info . f_frsize ) ;
if ( out_free ) * out_free = ( ( u64 ) info . f_bfree * ( u64 ) info . f_frsize ) ;
2022-07-05 02:04:28 +01:00
2020-12-02 06:28:35 +00:00
return true ;
2020-08-01 05:43:55 +01:00
}
2021-07-21 16:04:18 +01:00
void utilsCreateOutputDirectories ( const char * device )
{
size_t device_len = 0 ;
char path [ FS_MAX_PATH ] = { 0 } ;
2022-07-05 02:04:28 +01:00
2021-07-21 16:04:18 +01:00
if ( device & & ( ! ( device_len = strlen ( device ) ) | | device [ device_len - 1 ] ! = ' : ' ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-07-21 16:04:18 +01:00
return ;
}
2022-07-05 02:04:28 +01:00
2021-07-21 16:04:18 +01:00
for ( size_t i = 0 ; i < g_outputDirsCount ; i + + )
{
2021-08-07 09:42:03 +01:00
sprintf ( path , " %s%s " , ( device ? device : DEVOPTAB_SDMC_DEVICE ) , g_outputDirs [ i ] ) ;
2021-07-21 16:04:18 +01:00
mkdir ( path , 0744 ) ;
}
}
2020-05-03 00:40:50 +01:00
bool utilsCheckIfFileExists ( const char * path )
{
2020-10-15 01:06:53 +01:00
if ( ! path | | ! * path ) return false ;
2022-07-05 02:04:28 +01:00
2020-05-03 00:40:50 +01:00
FILE * chkfile = fopen ( path , " rb " ) ;
if ( chkfile )
2020-04-16 11:13:11 +01:00
{
2020-05-03 00:40:50 +01:00
fclose ( chkfile ) ;
return true ;
2020-04-16 11:13:11 +01:00
}
2022-07-05 02:04:28 +01:00
2020-05-03 00:40:50 +01:00
return false ;
2020-04-16 11:13:11 +01:00
}
2020-10-22 05:38:14 +01:00
void utilsRemoveConcatenationFile ( const char * path )
{
if ( ! path | | ! * path ) return ;
remove ( path ) ;
fsdevDeleteDirectoryRecursively ( path ) ;
}
2020-05-03 00:40:50 +01:00
bool utilsCreateConcatenationFile ( const char * path )
2020-04-15 21:50:07 +01:00
{
2020-10-15 01:06:53 +01:00
if ( ! path | | ! * path )
2020-05-03 00:40:50 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-05-03 00:40:50 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Safety measure: remove any existant file/directory at the destination path. */
2020-10-22 05:38:14 +01:00
utilsRemoveConcatenationFile ( path ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Create ConcatenationFile. */
2020-07-06 01:10:07 +01:00
/* If the call succeeds, the caller function will be able to operate on this file using stdio calls. */
2020-08-01 05:43:55 +01:00
Result rc = fsdevCreateFile ( path , 0 , FsCreateOption_BigFile ) ;
2022-07-12 17:34:49 +01:00
if ( R_FAILED ( rc ) ) LOG_MSG_ERROR ( " fsdevCreateFile failed for \" %s \" ! (0x%X). " , path , rc ) ;
2022-07-05 02:04:28 +01:00
2020-08-01 05:43:55 +01:00
return R_SUCCEEDED ( rc ) ;
2020-05-03 00:40:50 +01:00
}
2020-07-29 22:02:21 +01:00
void utilsCreateDirectoryTree ( const char * path , bool create_last_element )
{
char * ptr = NULL , * tmp = NULL ;
size_t path_len = 0 ;
2022-07-05 02:04:28 +01:00
2020-07-29 22:02:21 +01:00
if ( ! path | | ! ( path_len = strlen ( path ) ) ) return ;
2022-07-05 02:04:28 +01:00
2020-07-29 22:02:21 +01:00
tmp = calloc ( path_len + 1 , sizeof ( char ) ) ;
if ( ! tmp ) return ;
2022-07-05 02:04:28 +01:00
2020-07-29 22:02:21 +01:00
ptr = strchr ( path , ' / ' ) ;
while ( ptr )
{
sprintf ( tmp , " %.*s " , ( int ) ( ptr - path ) , path ) ;
mkdir ( tmp , 0777 ) ;
ptr = strchr ( + + ptr , ' / ' ) ;
}
2022-07-05 02:04:28 +01:00
2020-07-29 22:02:21 +01:00
if ( create_last_element ) mkdir ( path , 0777 ) ;
2022-07-05 02:04:28 +01:00
2020-07-29 22:02:21 +01:00
free ( tmp ) ;
}
2020-10-21 05:27:48 +01:00
char * utilsGeneratePath ( const char * prefix , const char * filename , const char * extension )
{
2021-06-01 02:12:15 +01:00
if ( ! filename | | ! * filename )
2020-10-21 05:27:48 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-10-21 05:27:48 +01:00
return NULL ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
bool use_prefix = ( prefix & & * prefix ) ;
size_t prefix_len = ( use_prefix ? strlen ( prefix ) : 0 ) ;
bool append_path_sep = ( use_prefix & & prefix [ prefix_len - 1 ] ! = ' / ' ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
bool use_extension = ( extension & & * extension ) ;
size_t extension_len = ( use_extension ? strlen ( extension ) : 0 ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
size_t path_len = ( prefix_len + strlen ( filename ) + extension_len ) ;
if ( append_path_sep ) path_len + + ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
char * path = NULL , * ptr1 = NULL , * ptr2 = NULL ;
bool filename_only = false , success = false ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Allocate memory for the output path. */
if ( ! ( path = calloc ( path_len + 1 , sizeof ( char ) ) ) )
2020-10-21 05:27:48 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate 0x%lX bytes for output path! " , path_len ) ;
2021-06-01 02:12:15 +01:00
goto end ;
2020-10-21 05:27:48 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Generate output path. */
if ( use_prefix ) strcat ( path , prefix ) ;
if ( append_path_sep ) strcat ( path , " / " ) ;
2020-10-26 06:39:33 +00:00
strcat ( path , filename ) ;
2021-06-01 02:12:15 +01:00
if ( use_extension ) strcat ( path , extension ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Retrieve pointer to the first path separator. */
ptr1 = strchr ( path , ' / ' ) ;
if ( ! ptr1 )
{
filename_only = true ;
ptr1 = path ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Make sure each path element doesn't exceed NT_MAX_FILENAME_LENGTH. */
while ( ptr1 )
{
2022-03-17 12:37:24 +00:00
if ( ! filename_only )
{
/* End loop if we find a NULL terminator. */
if ( ! * ptr1 + + ) break ;
2022-07-05 02:04:28 +01:00
2022-03-17 12:37:24 +00:00
/* Get pointer to next path separator. */
ptr2 = strchr ( ptr1 , ' / ' ) ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Get current path element size. */
size_t element_size = ( ptr2 ? ( size_t ) ( ptr2 - ptr1 ) : ( path_len - ( size_t ) ( ptr1 - path ) ) ) ;
2022-07-05 02:04:28 +01:00
2022-07-27 23:53:52 +01:00
/* Get UTF-8 string limit. */
/* Use NT_MAX_FILENAME_LENGTH as the byte count limit. */
size_t last_cp_pos = utilsGetUtf8StringLimit ( ptr1 , element_size , NT_MAX_FILENAME_LENGTH ) ;
if ( last_cp_pos < element_size )
2021-06-01 02:12:15 +01:00
{
if ( ptr2 )
{
/* Truncate current element by moving the rest of the path to the current position. */
memmove ( ptr1 + last_cp_pos , ptr2 , path_len - ( size_t ) ( ptr2 - path ) ) ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update pointer. */
ptr2 - = ( element_size - last_cp_pos ) ;
} else
if ( use_extension )
{
/* Truncate last element. Make sure to preserve the provided file extension. */
2022-03-17 12:37:24 +00:00
if ( extension_len > = last_cp_pos )
2021-06-01 02:12:15 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " File extension length is >= truncated filename length! (0x%lX >= 0x%lX). " , extension_len , last_cp_pos ) ;
2021-06-01 02:12:15 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2022-03-17 12:37:24 +00:00
memmove ( ptr1 + last_cp_pos - extension_len , ptr1 + element_size - extension_len , extension_len ) ;
2021-06-01 02:12:15 +01:00
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
path_len - = ( element_size - last_cp_pos ) ;
path [ path_len ] = ' \0 ' ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
ptr1 = ptr2 ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Check if the full length for the generated path is >= FS_MAX_PATH. */
if ( path_len > = FS_MAX_PATH )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Generated path length is >= FS_MAX_PATH! (0x%lX). " , path_len ) ;
2021-06-01 02:12:15 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
/* Update flag. */
success = true ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
end :
if ( ! success & & path )
{
free ( path ) ;
path = NULL ;
}
2022-07-05 02:04:28 +01:00
2020-10-21 05:27:48 +01:00
return path ;
}
2021-08-25 21:48:01 +01:00
void utilsPrintConsoleError ( const char * msg )
{
PadState pad = { 0 } ;
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Don't consider stick movement as button inputs. */
u64 flag = ~ ( HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \
HidNpadButton_StickRUp | HidNpadButton_StickRDown ) ;
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Configure input. */
/* Up to 8 different, full controller inputs. */
/* Individual Joy-Cons not supported. */
padConfigureInput ( 8 , HidNpadStyleSet_NpadFullCtrl ) ;
padInitializeWithMask ( & pad , 0x1000000FFUL ) ;
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Initialize console output. */
consoleInit ( NULL ) ;
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Print message. */
if ( msg & & * msg )
{
printf ( " %s " , msg ) ;
} else {
printf ( " An error occurred. " ) ;
}
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
printf ( " \n \n For more information, please check the logfile. Press any button to exit. " ) ;
consoleUpdate ( NULL ) ;
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Wait until the user presses a button. */
while ( appletMainLoop ( ) )
{
padUpdate ( & pad ) ;
if ( padGetButtonsDown ( & pad ) & flag ) break ;
}
2022-07-05 02:04:28 +01:00
2021-08-25 21:48:01 +01:00
/* Deinitialize console output. */
consoleExit ( NULL ) ;
}
2021-08-07 10:44:36 +01:00
bool utilsGetApplicationUpdatedState ( void )
{
bool ret = false ;
SCOPED_LOCK ( & g_resourcesMutex ) ret = g_appUpdated ;
return ret ;
}
void utilsSetApplicationUpdatedState ( void )
{
SCOPED_LOCK ( & g_resourcesMutex ) g_appUpdated = true ;
}
2021-08-07 09:42:03 +01:00
bool utilsParseGitHubReleaseJsonData ( const char * json_buf , size_t json_buf_size , UtilsGitHubReleaseJsonData * out )
{
if ( ! json_buf | | ! json_buf_size | | ! out )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-08-07 09:42:03 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
bool ret = false ;
const char * published_at = NULL ;
struct json_object * assets = NULL ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Free output buffer beforehand. */
utilsFreeGitHubReleaseJsonData ( out ) ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Parse JSON object. */
out - > obj = jsonParseFromString ( json_buf , json_buf_size ) ;
if ( ! out - > obj )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to parse JSON object! " ) ;
2021-08-07 09:42:03 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Get required JSON elements. */
out - > version = jsonGetString ( out - > obj , " tag_name " ) ;
out - > commit_hash = jsonGetString ( out - > obj , " target_commitish " ) ;
published_at = jsonGetString ( out - > obj , " published_at " ) ;
out - > changelog = jsonGetString ( out - > obj , " body " ) ;
assets = jsonGetArray ( out - > obj , " assets " ) ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
if ( ! out - > version | | ! out - > commit_hash | | ! published_at | | ! out - > changelog | | ! assets )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to retrieve required elements from the provided JSON! " ) ;
2021-08-07 09:42:03 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Parse release date. */
if ( ! strptime ( published_at , " %Y-%m-%dT%H:%M:%SZ " , & ( out - > date ) ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to parse release date \" %s \" ! " , published_at ) ;
2021-08-07 09:42:03 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Loop through the assets array until we find the NRO. */
size_t assets_len = json_object_array_length ( assets ) ;
for ( size_t i = 0 ; i < assets_len ; i + + )
{
struct json_object * cur_asset = NULL ;
const char * asset_name = NULL ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Get current asset object. */
cur_asset = json_object_array_get_idx ( assets , i ) ;
if ( ! cur_asset ) continue ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Get current asset name. */
asset_name = jsonGetString ( cur_asset , " name " ) ;
if ( ! asset_name | | strcmp ( asset_name , NRO_NAME ) ! = 0 ) continue ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Jackpot. Get the download URL. */
out - > download_url = jsonGetString ( cur_asset , " browser_download_url " ) ;
break ;
}
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
if ( ! out - > download_url )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to retrieve required elements from the provided JSON! " ) ;
2021-08-07 09:42:03 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
/* Update return value. */
ret = true ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
end :
if ( ! ret ) utilsFreeGitHubReleaseJsonData ( out ) ;
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
return ret ;
}
2021-08-07 10:44:36 +01:00
bool utilsIsApplicationUpdatable ( const char * version , const char * commit_hash )
2021-08-07 09:42:03 +01:00
{
2021-08-07 10:44:36 +01:00
if ( ! version | | ! * version | | * version ! = ' v ' | | ! commit_hash | | ! * commit_hash )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-08-07 10:44:36 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
bool ret = false ;
2021-08-07 10:44:36 +01:00
UtilsApplicationVersion cur_version = { VERSION_MAJOR , VERSION_MINOR , VERSION_MICRO } , new_version = { 0 } ;
2022-07-05 02:04:28 +01:00
2021-08-07 10:44:36 +01:00
/* Parse version string. */
sscanf ( version , " v%u.%u.%u " , & ( new_version . major ) , & ( new_version . minor ) , & ( new_version . micro ) ) ;
2022-07-05 02:04:28 +01:00
2021-08-07 10:44:36 +01:00
/* Compare versions. */
if ( cur_version . major = = new_version . major )
{
if ( cur_version . minor = = new_version . minor )
{
if ( cur_version . micro = = new_version . micro )
{
/* Versions are equal. Let's compare the commit hashes and return true if they're different. */
ret = ( strncasecmp ( commit_hash , GIT_COMMIT , 7 ) ! = 0 ) ;
} else
if ( cur_version . micro < new_version . micro )
{
ret = true ;
}
} else
if ( cur_version . minor < new_version . minor )
{
ret = true ;
}
} else
if ( cur_version . major < new_version . major )
{
ret = true ;
}
2022-07-05 02:04:28 +01:00
2021-08-07 09:42:03 +01:00
return ret ;
}
2021-05-18 13:32:43 +01:00
static void _utilsGetLaunchPath ( int program_argc , const char * * program_argv )
2020-05-03 00:40:50 +01:00
{
2021-05-18 13:32:43 +01:00
if ( program_argc < = 0 | | ! program_argv ) return ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
for ( int i = 0 ; i < program_argc ; i + + )
2020-05-03 00:40:50 +01:00
{
2021-08-07 09:42:03 +01:00
if ( program_argv [ i ] & & ! strncmp ( program_argv [ i ] , DEVOPTAB_SDMC_DEVICE " / " , 6 ) )
2020-05-03 00:40:50 +01:00
{
2021-05-18 13:32:43 +01:00
g_appLaunchPath = program_argv [ i ] ;
break ;
2020-05-03 00:40:50 +01:00
}
}
2020-04-15 21:50:07 +01:00
}
2020-04-17 22:59:05 +01:00
2021-02-25 03:36:53 +00:00
static void _utilsGetCustomFirmwareType ( void )
2020-08-15 22:22:49 +01:00
{
2021-04-20 23:43:37 +01:00
bool tx_srv = servicesCheckRunningServiceByName ( " tx " ) ;
bool rnx_srv = servicesCheckRunningServiceByName ( " rnx " ) ;
2022-07-05 02:04:28 +01:00
2020-08-15 22:22:49 +01:00
g_customFirmwareType = ( rnx_srv ? UtilsCustomFirmwareType_ReiNX : ( tx_srv ? UtilsCustomFirmwareType_SXOS : UtilsCustomFirmwareType_Atmosphere ) ) ;
}
2023-04-08 12:34:53 +01:00
static bool _utilsGetProductModel ( void )
{
Result rc = 0 ;
bool ret = false ;
SetSysProductModel model = SetSysProductModel_Invalid ;
rc = setsysGetProductModel ( & model ) ;
if ( R_SUCCEEDED ( rc ) & & model ! = SetSysProductModel_Invalid )
{
g_productModel = model ;
ret = true ;
} else {
LOG_MSG_ERROR ( " setsysGetProductModel failed! (0x%X) (%d). " , rc , model ) ;
}
return ret ;
}
2020-08-15 22:22:49 +01:00
static bool _utilsIsDevelopmentUnit ( void )
{
Result rc = 0 ;
bool tmp = false ;
2022-07-05 02:04:28 +01:00
2020-08-15 22:22:49 +01:00
rc = splIsDevelopment ( & tmp ) ;
if ( R_SUCCEEDED ( rc ) )
{
g_isDevUnit = tmp ;
} else {
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " splIsDevelopment failed! (0x%X). " , rc ) ;
2020-08-15 22:22:49 +01:00
}
2022-07-05 02:04:28 +01:00
2020-08-15 22:22:49 +01:00
return R_SUCCEEDED ( rc ) ;
}
2020-04-17 22:59:05 +01:00
static bool utilsMountEmmcBisSystemPartitionStorage ( void )
{
Result rc = 0 ;
FRESULT fr = FR_OK ;
2022-07-05 02:04:28 +01:00
2020-04-17 22:59:05 +01:00
rc = fsOpenBisStorage ( & g_emmcBisSystemPartitionStorage , FsBisPartitionId_System ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to open eMMC BIS System partition storage! (0x%X). " , rc ) ;
2020-04-17 22:59:05 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-04-19 23:44:22 +01:00
g_emmcBisSystemPartitionFatFsObj = calloc ( 1 , sizeof ( FATFS ) ) ;
if ( ! g_emmcBisSystemPartitionFatFsObj )
2020-04-17 22:59:05 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Unable to allocate memory for FatFs element! " ) ;
2020-04-17 22:59:05 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-11-28 06:38:01 +00:00
fr = f_mount ( g_emmcBisSystemPartitionFatFsObj , BIS_SYSTEM_PARTITION_MOUNT_NAME , 1 ) ;
2020-04-17 22:59:05 +01:00
if ( fr ! = FR_OK )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to mount eMMC BIS System partition! (%u). " , fr ) ;
2020-04-17 22:59:05 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-04-17 22:59:05 +01:00
return true ;
}
static void utilsUnmountEmmcBisSystemPartitionStorage ( void )
{
2020-04-19 23:44:22 +01:00
if ( g_emmcBisSystemPartitionFatFsObj )
2020-04-17 22:59:05 +01:00
{
2020-11-28 06:38:01 +00:00
f_unmount ( BIS_SYSTEM_PARTITION_MOUNT_NAME ) ;
2020-04-19 23:44:22 +01:00
free ( g_emmcBisSystemPartitionFatFsObj ) ;
g_emmcBisSystemPartitionFatFsObj = NULL ;
2020-04-17 22:59:05 +01:00
}
2022-07-05 02:04:28 +01:00
2020-04-17 22:59:05 +01:00
if ( serviceIsActive ( & ( g_emmcBisSystemPartitionStorage . s ) ) )
{
fsStorageClose ( & g_emmcBisSystemPartitionStorage ) ;
memset ( & g_emmcBisSystemPartitionStorage , 0 , sizeof ( FsStorage ) ) ;
}
}
2020-05-03 00:40:50 +01:00
2022-07-12 01:27:03 +01:00
static void utilsOverclockSystem ( bool overclock )
{
u32 cpu_rate = ( ( overclock ? CPU_CLKRT_OVERCLOCKED : CPU_CLKRT_NORMAL ) * 1000000 ) ;
u32 mem_rate = ( ( overclock ? MEM_CLKRT_OVERCLOCKED : MEM_CLKRT_NORMAL ) * 1000000 ) ;
servicesChangeHardwareClockRates ( cpu_rate , mem_rate ) ;
}
2020-05-03 00:40:50 +01:00
static void utilsOverclockSystemAppletHook ( AppletHookType hook , void * param )
{
( void ) param ;
2022-07-05 02:04:28 +01:00
2022-07-12 01:27:03 +01:00
/* Don't proceed if we're not dealing with a desired hook type. */
2020-05-03 00:40:50 +01:00
if ( hook ! = AppletHookType_OnOperationMode & & hook ! = AppletHookType_OnPerformanceMode ) return ;
2022-07-05 02:04:28 +01:00
2022-07-12 01:27:03 +01:00
/* Overclock the system based on the overclock setting and the current long running state value. */
SCOPED_LOCK ( & g_resourcesMutex ) utilsOverclockSystem ( configGetBoolean ( " overclock " ) & g_longRunningProcess ) ;
2020-05-03 00:40:50 +01:00
}
2021-03-08 14:44:11 +00:00
2021-07-29 17:48:32 +01:00
static void utilsChangeHomeButtonBlockStatus ( bool block )
{
/* Only change HOME button blocking status if we're running as a regular application or a system application. */
2023-04-08 12:34:53 +01:00
if ( utilsIsAppletMode ( ) ) return ;
2022-07-05 02:04:28 +01:00
2021-07-29 17:48:32 +01:00
if ( block )
{
appletBeginBlockingHomeButtonShortAndLongPressed ( 0 ) ;
} else {
appletEndBlockingHomeButtonShortAndLongPressed ( ) ;
}
}
2021-08-25 21:48:01 +01:00
NX_INLINE void utilsCloseFileDescriptor ( int * fd )
2021-03-08 14:44:11 +00:00
{
2021-08-25 21:48:01 +01:00
if ( ! fd | | * fd < 0 ) return ;
close ( * fd ) ;
* fd = - 1 ;
}
2022-07-27 23:53:52 +01:00
static size_t utilsGetUtf8StringLimit ( const char * str , size_t str_size , size_t byte_limit )
2021-06-01 02:12:15 +01:00
{
2022-07-27 23:53:52 +01:00
if ( ! str | | ! * str | | ! str_size | | ! byte_limit ) return 0 ;
if ( byte_limit > str_size ) return str_size ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
u32 code = 0 ;
ssize_t units = 0 ;
2022-07-27 23:53:52 +01:00
size_t cur_pos = 0 , last_cp_pos = 0 ;
2021-06-01 02:12:15 +01:00
const u8 * str_u8 = ( const u8 * ) str ;
2022-07-05 02:04:28 +01:00
2022-07-27 23:53:52 +01:00
while ( cur_pos < str_size & & cur_pos < byte_limit )
2021-06-01 02:12:15 +01:00
{
units = decode_utf8 ( & code , str_u8 + cur_pos ) ;
size_t new_pos = ( cur_pos + ( size_t ) units ) ;
if ( units < 0 | | ! code | | new_pos > str_size ) break ;
2022-07-05 02:04:28 +01:00
2021-06-01 02:12:15 +01:00
cur_pos = new_pos ;
2022-07-27 23:53:52 +01:00
if ( cur_pos < byte_limit ) last_cp_pos = cur_pos ;
2021-06-01 02:12:15 +01:00
}
2022-07-05 02:04:28 +01:00
2022-07-27 23:53:52 +01:00
return last_cp_pos ;
2021-06-01 02:12:15 +01:00
}