2021-12-25 20:14:56 +01:00
# include <cstring>
2022-02-02 19:12:52 +01:00
# include "utils/StringTools.h"
# include <coreinit/cache.h>
# include <coreinit/debug.h>
# include <coreinit/dynload.h>
2022-09-04 18:35:38 +02:00
# include <coreinit/filesystem_fsa.h>
2022-02-02 19:12:52 +01:00
# include <coreinit/foreground.h>
# include <coreinit/ios.h>
# include <coreinit/screen.h>
# include <coreinit/title.h>
2021-12-25 20:14:56 +01:00
# include <elfio/elfio.hpp>
2022-02-02 19:12:52 +01:00
# include <fcntl.h>
# include <gx2/state.h>
# include <malloc.h>
# include <memory>
# include <nn/act/client_cpp.h>
2021-12-25 20:14:56 +01:00
# include <proc_ui/procui.h>
# include <sysapp/launch.h>
2021-12-30 16:10:59 +01:00
# include <sysapp/title.h>
2021-12-25 20:14:56 +01:00
# include <vector>
# include <vpad/input.h>
2022-02-02 19:12:52 +01:00
# include <whb/log_cafe.h>
# include <whb/log_module.h>
# include <whb/log_udp.h>
2021-12-25 20:14:56 +01:00
# include "ElfUtils.h"
# include "common/module_defines.h"
2022-02-02 19:12:52 +01:00
# include "fs/DirList.h"
# include "kernel.h"
# include "module/ModuleDataFactory.h"
2021-12-29 16:21:14 +01:00
# include "utils/DrawUtils.h"
2024-04-21 12:31:54 +02:00
# include "utils/FileUtils.h"
2023-06-14 18:46:06 +02:00
# include "utils/InputUtils.h"
2024-04-21 12:31:54 +02:00
# include "utils/OnLeavingScope.h"
2023-06-14 18:46:06 +02:00
# include "utils/PairUtils.h"
2024-04-21 12:31:54 +02:00
# include "utils/utils.h"
# include "utils/wiiu_zlib.hpp"
2023-07-20 09:13:15 +02:00
# include "version.h"
2023-07-20 09:13:44 +02:00
# define ENVIRONMENT_LOADER_VERSION "v0.2.0"
2021-12-25 20:14:56 +01:00
2024-04-21 12:31:54 +02:00
# define MEMORY_REGION_START 0x00800000
# define AUTOBOOT_CONFIG_PATH "fs: / vol / external01 / wiiu / environments / default.cfg"
2021-12-29 16:21:14 +01:00
2021-12-25 20:14:56 +01:00
bool CheckRunning ( ) {
switch ( ProcUIProcessMessages ( true ) ) {
case PROCUI_STATUS_EXITING : {
return false ;
}
case PROCUI_STATUS_RELEASE_FOREGROUND : {
ProcUIDrawDoneRelease ( ) ;
break ;
}
case PROCUI_STATUS_IN_FOREGROUND : {
break ;
}
case PROCUI_STATUS_IN_BACKGROUND :
default :
break ;
}
return true ;
}
extern " C " uint32_t textStart ( ) ;
2021-12-29 16:21:14 +01:00
std : : string EnvironmentSelectionScreen ( const std : : map < std : : string , std : : string > & payloads , int32_t autobootIndex ) ;
std : : optional < std : : string > getFileContent ( const std : : string & path ) {
2022-05-13 16:13:03 +02:00
DEBUG_FUNCTION_LINE_VERBOSE ( " Read from file %s " , path . c_str ( ) ) ;
2021-12-29 16:21:14 +01:00
FILE * f = fopen ( path . c_str ( ) , " r " ) ;
if ( f ) {
char buf [ 128 ] { } ;
fgets ( buf , sizeof ( buf ) , f ) ;
fclose ( f ) ;
return std : : string ( buf ) ;
}
2022-05-13 16:13:03 +02:00
DEBUG_FUNCTION_LINE_ERR ( " Failed to load %s " , path . c_str ( ) ) ;
2021-12-29 16:21:14 +01:00
return { } ;
}
bool writeFileContent ( const std : : string & path , const std : : string & content ) {
2022-05-13 16:13:03 +02:00
DEBUG_FUNCTION_LINE_VERBOSE ( " Write to file %s: %s " , path . c_str ( ) , content . c_str ( ) ) ;
2021-12-29 16:21:14 +01:00
FILE * f = fopen ( path . c_str ( ) , " w " ) ;
if ( f ) {
fputs ( content . c_str ( ) , f ) ;
fclose ( f ) ;
return true ;
}
return false ;
}
2021-12-25 20:14:56 +01:00
2022-01-21 19:02:31 +01:00
extern " C " void __fini ( ) ;
2022-06-05 18:35:52 +02:00
extern " C " void __init_wut_malloc ( ) ;
2024-04-21 12:31:54 +02:00
void LoadAndRunModule ( std : : string_view filepath , std : : string_view environment_path ) ;
2022-01-21 19:02:31 +01:00
2021-12-25 20:14:56 +01:00
int main ( int argc , char * * argv ) {
2022-06-05 18:35:52 +02:00
// We need to call __init_wut_malloc somewhere so wut_malloc will be used for the memory allocation.
__init_wut_malloc ( ) ;
2022-01-30 13:56:33 +01:00
initLogging ( ) ;
2021-12-25 20:14:56 +01:00
2022-01-01 01:34:10 +01:00
if ( IOS_Open ( ( char * ) ( " /dev/iosuhax " ) , static_cast < IOSOpenMode > ( 0 ) ) > = 0 ) {
auto checkTiramisuHBL = fopen ( " fs:/vol/external01/wiiu/environments/tiramisu/modules/setup/50_hbl_installer.rpx " , " r " ) ;
2022-01-01 15:49:01 +01:00
if ( checkTiramisuHBL ! = nullptr ) {
2022-01-01 01:34:10 +01:00
fclose ( checkTiramisuHBL ) ;
OSFatal ( " Don't run the EnvironmentLoader twice. \n \n If you want to open the Homebrew Launcher, launch the Mii Maker \n instead. " ) ;
} else {
OSFatal ( " Don't run the EnvironmentLoader twice. " ) ;
}
}
2021-12-28 15:40:37 +01:00
DEBUG_FUNCTION_LINE ( " Hello from EnvironmentLoader! " ) ;
2024-04-21 12:31:54 +02:00
char environmentPathFromIOSU [ 0x100 ] = { } ;
auto handle = IOS_Open ( " /dev/mcp " , IOS_OPEN_READ ) ;
2021-12-28 15:40:37 +01:00
if ( handle > = 0 ) {
2022-02-03 14:08:08 +01:00
int in = 0xF9 ; // IPC_CUSTOM_COPY_ENVIRONMENT_PATH
2024-04-21 12:31:54 +02:00
if ( IOS_Ioctl ( handle , 100 , & in , sizeof ( in ) , environmentPathFromIOSU , sizeof ( environmentPathFromIOSU ) ) = = IOS_ERROR_OK ) {
DEBUG_FUNCTION_LINE ( " Boot into %s " , environmentPathFromIOSU ) ;
2021-12-28 15:40:37 +01:00
}
IOS_Close ( handle ) ;
}
2021-12-30 16:10:59 +01:00
bool noEnvironmentsFound = false ;
2024-04-21 12:31:54 +02:00
std : : string environmentPath = std : : string ( environmentPathFromIOSU ) ;
if ( ! environmentPath . starts_with ( " fs:/vol/external01/wiiu/environments/ " ) ) { // If the environment path in IOSU is empty or unexpected, read config
2021-12-28 15:40:37 +01:00
DirList environmentDirs ( " fs:/vol/external01/wiiu/environments/ " , nullptr , DirList : : Dirs , 1 ) ;
2021-12-25 20:14:56 +01:00
2022-09-24 13:56:28 +02:00
std : : map < std : : string , std : : string > environmentPaths ;
for ( int i = 0 ; i < environmentDirs . GetFilecount ( ) ; i + + ) {
environmentPaths [ environmentDirs . GetFilename ( i ) ] = environmentDirs . GetFilepath ( i ) ;
}
2022-02-03 14:08:08 +01:00
bool forceMenu = true ;
auto res = getFileContent ( AUTOBOOT_CONFIG_PATH ) ;
2021-12-29 16:21:14 +01:00
auto autobootIndex = - 1 ;
if ( res ) {
2022-05-13 16:13:03 +02:00
DEBUG_FUNCTION_LINE_VERBOSE ( " Got result %s " , res - > c_str ( ) ) ;
2022-09-24 13:56:28 +02:00
int32_t i = 0 ;
for ( auto const & [ key , val ] : environmentPaths ) {
if ( res . value ( ) = = key ) {
2021-12-29 16:21:14 +01:00
DEBUG_FUNCTION_LINE ( " Found environment %s from config at index %d " , res . value ( ) . c_str ( ) , i ) ;
2024-04-21 12:31:54 +02:00
autobootIndex = i ;
environmentPath = val ;
forceMenu = false ;
2021-12-29 16:21:14 +01:00
break ;
}
2022-09-24 13:56:28 +02:00
i + + ;
2021-12-29 16:21:14 +01:00
}
} else {
2022-05-13 16:13:03 +02:00
DEBUG_FUNCTION_LINE_ERR ( " No config found " ) ;
2021-12-29 16:21:14 +01:00
}
2023-06-14 18:46:06 +02:00
InputUtils : : Init ( ) ;
2021-12-25 20:14:56 +01:00
2023-06-14 18:46:06 +02:00
InputUtils : : InputData input = InputUtils : : getControllerInput ( ) ;
2021-12-28 15:40:37 +01:00
2023-06-14 18:46:06 +02:00
if ( forceMenu | | ( ( input . trigger | input . hold ) & VPAD_BUTTON_X ) = = VPAD_BUTTON_X ) {
2022-05-13 16:13:03 +02:00
DEBUG_FUNCTION_LINE_VERBOSE ( " Open menu! " ) ;
2024-04-21 12:31:54 +02:00
environmentPath = EnvironmentSelectionScreen ( environmentPaths , autobootIndex ) ;
2021-12-30 16:10:59 +01:00
if ( environmentPaths . empty ( ) ) {
noEnvironmentsFound = true ;
} else {
2024-04-21 12:31:54 +02:00
DEBUG_FUNCTION_LINE_VERBOSE ( " Selected %s " , environmentPath . c_str ( ) ) ;
2021-12-30 16:10:59 +01:00
}
2021-12-28 15:40:37 +01:00
}
2023-06-14 18:46:06 +02:00
InputUtils : : DeInit ( ) ;
2021-12-25 20:14:56 +01:00
}
2021-12-30 16:10:59 +01:00
RevertMainHook ( ) ;
2021-12-25 20:14:56 +01:00
2021-12-30 16:10:59 +01:00
if ( ! noEnvironmentsFound ) {
2024-04-21 12:31:54 +02:00
DirList setupModules ( environmentPath + " /modules/setup " , " .rpx " , DirList : : Files , 1 ) ;
2021-12-30 16:10:59 +01:00
setupModules . SortList ( ) ;
for ( int i = 0 ; i < setupModules . GetFilecount ( ) ; i + + ) {
2022-05-15 23:29:05 +02:00
//! skip hidden linux and mac files
if ( setupModules . GetFilename ( i ) [ 0 ] = = ' . ' | | setupModules . GetFilename ( i ) [ 0 ] = = ' _ ' ) {
DEBUG_FUNCTION_LINE_ERR ( " Skip file %s " , setupModules . GetFilepath ( i ) ) ;
continue ;
}
2024-04-21 12:31:54 +02:00
LoadAndRunModule ( setupModules . GetFilepath ( i ) , environmentPath ) ;
2021-12-25 20:14:56 +01:00
}
2023-06-16 17:17:19 +02:00
2021-12-30 16:10:59 +01:00
} else {
DEBUG_FUNCTION_LINE ( " Return to Wii U Menu " ) ;
ProcUIInit ( OSSavesDone_ReadyToRelease ) ;
for ( int i = 0 ; i < argc ; i + + ) {
2021-12-31 15:08:33 +01:00
if ( strcmp ( argv [ i ] , " void forceDefaultTitleIDToWiiUMenu(void) " ) = = 0 ) {
if ( ( i + 1 ) < argc ) {
2021-12-30 16:10:59 +01:00
i + + ;
2022-05-13 16:13:03 +02:00
DEBUG_FUNCTION_LINE_VERBOSE ( " call forceDefaultTitleIDToWiiUMenu " ) ;
2022-02-02 19:12:52 +01:00
// clang-format off
auto forceDefaultTitleIDToWiiUMenu = ( void ( * ) ( ) ) argv [ i ] ;
// clang-format on
2021-12-30 16:10:59 +01:00
forceDefaultTitleIDToWiiUMenu ( ) ;
}
}
2021-12-25 20:14:56 +01:00
}
2021-12-30 16:10:59 +01:00
DEBUG_FUNCTION_LINE ( " Launch menu " ) ;
SYSLaunchMenu ( ) ;
2021-12-25 20:14:56 +01:00
}
ProcUIInit ( OSSavesDone_ReadyToRelease ) ;
while ( CheckRunning ( ) ) {
// wait.
OSSleepTicks ( OSMillisecondsToTicks ( 100 ) ) ;
}
ProcUIShutdown ( ) ;
2022-01-30 13:56:33 +01:00
deinitLogging ( ) ;
2022-01-21 19:02:31 +01:00
__fini ( ) ;
2021-12-25 20:14:56 +01:00
return 0 ;
}
2024-04-21 12:31:54 +02:00
std : : optional < HeapWrapper > GetHeapFromMappedMemory ( uint32_t heapSize ) {
void * ( * MEMAllocFromDefaultHeapExForThreads ) ( uint32_t size , int align ) = nullptr ;
void ( * MEMFreeToDefaultHeapForThreads ) ( void * ptr ) = nullptr ;
// Let's try to get the memalign and free functions from the memorymapping module.
OSDynLoad_Module module ;
if ( OSDynLoad_Acquire ( " homebrew_memorymapping " , & module ) ! = OS_DYNLOAD_OK ) {
DEBUG_FUNCTION_LINE ( " Failed to acquire homebrew_memorymapping. " ) ;
return { } ;
}
/* Memory allocation functions */
uint32_t * allocPtr = nullptr , * freePtr = nullptr ;
if ( OSDynLoad_FindExport ( module , OS_DYNLOAD_EXPORT_DATA , " MEMAllocFromMappedMemoryEx " , reinterpret_cast < void * * > ( & allocPtr ) ) ! = OS_DYNLOAD_OK ) {
DEBUG_FUNCTION_LINE ( " OSDynLoad_FindExport for MEMAllocFromDefaultHeapEx failed " ) ;
return { } ;
}
if ( OSDynLoad_FindExport ( module , OS_DYNLOAD_EXPORT_DATA , " MEMFreeToMappedMemory " , reinterpret_cast < void * * > ( & freePtr ) ) ! = OS_DYNLOAD_OK ) {
DEBUG_FUNCTION_LINE ( " OSDynLoad_FindExport for MEMFreeToDefaultHeap failed " ) ;
return { } ;
}
MEMAllocFromDefaultHeapExForThreads = ( void * ( * ) ( uint32_t , int ) ) * allocPtr ;
MEMFreeToDefaultHeapForThreads = ( void ( * ) ( void * ) ) * freePtr ;
if ( ! MEMAllocFromDefaultHeapExForThreads | | ! MEMFreeToDefaultHeapForThreads ) {
DEBUG_FUNCTION_LINE_ERR ( " MEMAllocFromDefaultHeapExForThreads or MEMFreeToDefaultHeapForThreads is null " ) ;
// the mapped memory is not available (yet)
return { } ;
}
uint32_t size = heapSize ;
auto ptr = MEMAllocFromDefaultHeapExForThreads ( size , 0x4 ) ;
if ( ! ptr ) {
DEBUG_FUNCTION_LINE_ERR ( " Failed to alloc memory: %d bytes " , size ) ;
return { } ;
}
DEBUG_FUNCTION_LINE ( " Let's create a memory wrapper for 0x%08X, size: %d " , ptr , size ) ;
return HeapWrapper ( MemoryWrapper ( ptr , size , MEMFreeToDefaultHeapForThreads ) ) ;
}
std : : optional < HeapWrapper > GetHeapForModule ( uint32_t heapSize ) {
// If Aroma is already loaded, we can't use the region between MEMORY_REGION_START and MEMORY_REGION_END anymore because Aroma is using.
// So instead we check before loading a module if aromas memory mapping module is already usable. If yes, we use this to load the module instead
if ( auto heapWrapper = GetHeapFromMappedMemory ( heapSize ) ) {
return heapWrapper ;
}
// If Aroma is not already loaded, we use the existing 0x00800000 - 0x01000000 memory region. This is where aroma is loaded to. Note: this region may be only mapped to the main core.
// The environment loader is loaded to the end of 0x00800000 - 0x01000000 memory region. With this helper we know the start of the .text section
uint32_t textSectionStart = textStart ( ) - 0x100 ;
auto endOfUsableMemory = textSectionStart ;
uint32_t startAddress = ( ( uint32_t ) endOfUsableMemory - heapSize ) & 0xFFFF0000 ;
uint32_t size = endOfUsableMemory - startAddress ;
if ( startAddress < MEMORY_REGION_START ) {
DEBUG_FUNCTION_LINE_ERR ( " Not enough static memory " ) ;
return { } ;
}
DEBUG_FUNCTION_LINE ( " Let's create a memory wrapper for 0x%08X, size: %d " , ptr , size ) ;
auto res = HeapWrapper ( MemoryWrapper ( ( void * ) startAddress , size , /* we don't need to free this memory*/ nullptr ) ) ;
if ( ( uint32_t ) res . GetHeapHandle ( ) ! = startAddress ) {
OSFatal ( " EnvironmentLoader: Unexpected address " ) ;
}
return res ;
}
void SetupKernelModule ( ) {
void * ( * KernelSetupDefaultSyscalls ) ( ) = nullptr ;
OSDynLoad_Module module ;
if ( OSDynLoad_Acquire ( " homebrew_kernel " , & module ) ! = OS_DYNLOAD_OK ) {
DEBUG_FUNCTION_LINE ( " Failed to acquire homebrew_kernel. " ) ;
return ;
}
if ( OSDynLoad_FindExport ( module , OS_DYNLOAD_EXPORT_FUNC , " KernelSetupDefaultSyscalls " , reinterpret_cast < void * * > ( & KernelSetupDefaultSyscalls ) ) ! = OS_DYNLOAD_OK ) {
DEBUG_FUNCTION_LINE ( " OSDynLoad_FindExport for KernelSetupDefaultSyscalls failed " ) ;
OSFatal ( " EnvironmentLoader: KernelModule is missing the export \n "
" \" KernelSetupDefaultSyscalls \" ... Please update Aroma! \n "
" \n "
" See https://wiiu.hacks.guide/ for more information. " ) ;
return ;
}
if ( ! KernelSetupDefaultSyscalls ) {
DEBUG_FUNCTION_LINE_WARN ( " KernelSetupDefaultSyscalls is null " ) ;
OSFatal ( " EnvironmentLoader: KernelModule is missing the export \n "
" \" KernelSetupDefaultSyscalls \" ... Please update Aroma! \n "
" \n "
" See https://wiiu.hacks.guide/ for more information. " ) ;
return ;
}
DEBUG_FUNCTION_LINE ( " Call KernelSetupDefaultSyscalls " ) ;
KernelSetupDefaultSyscalls ( ) ;
OSDynLoad_Release ( module ) ;
}
void LoadAndRunModule ( std : : string_view filepath , std : : string_view environment_path ) {
// Some module may unmount the sd card on exit.
FSAInit ( ) ;
auto client = FSAAddClient ( nullptr ) ;
if ( client ) {
FSAMount ( client , " /dev/sdcard01 " , " /vol/external01 " , static_cast < FSAMountFlags > ( 0 ) , nullptr , 0 ) ;
FSADelClient ( client ) ;
} else {
DEBUG_FUNCTION_LINE_ERR ( " Failed to add FSA client " ) ;
}
DEBUG_FUNCTION_LINE ( " Trying to load %s into memory " , filepath . data ( ) ) ;
uint8_t * buffer = nullptr ;
uint32_t fsize = 0 ;
if ( LoadFileToMem ( filepath . data ( ) , & buffer , & fsize ) < 0 ) {
DEBUG_FUNCTION_LINE_ERR ( " Failed to load file " ) ;
OSFatal ( " EnvironmentLoader: Failed to load file to memory " ) ;
return ;
}
auto cleanupBuffer = onLeavingScope ( [ buffer ] ( ) { free ( buffer ) ; } ) ;
ELFIO : : elfio reader ( new wiiu_zlib ) ;
// Load ELF data
if ( ! reader . load ( reinterpret_cast < const char * > ( buffer ) , fsize ) ) {
DEBUG_FUNCTION_LINE_ERR ( " Can't parse .wms from buffer. " ) ;
OSFatal ( " Can't parse .wms from buffer. " ) ;
return ;
}
uint32_t moduleSize = ModuleDataFactory : : GetSizeOfModule ( reader ) ;
DEBUG_FUNCTION_LINE_VERBOSE ( " Module has size: %d " , moduleSize ) ;
uint32_t requiredHeapSize = moduleSize + sizeof ( module_information_t ) + 0x10000 ; // add another 0x10000 to be safe
DEBUG_FUNCTION_LINE_VERBOSE ( " Allocate %d bytes for heap (%.2f KiB) " , requiredHeapSize , requiredHeapSize / 1024.0f ) ;
if ( auto heapWrapperOpt = GetHeapForModule ( requiredHeapSize ) ; heapWrapperOpt . has_value ( ) ) {
// Frees automatically, must not survive the heapWrapper.
auto moduleInfoOpt = heapWrapperOpt - > Alloc ( sizeof ( module_information_t ) , 0x4 ) ;
if ( ! moduleInfoOpt ) {
DEBUG_FUNCTION_LINE_ERR ( " Failed to alloc module information " ) ;
OSFatal ( " EnvironmentLoader: Failed to alloc module information " ) ;
return ;
}
auto moduleInfo = std : : move ( * moduleInfoOpt ) ;
auto moduleInfoPtr = ( module_information_t * ) moduleInfo . data ( ) ;
2024-04-21 18:19:58 +02:00
* moduleInfoPtr = { } ;
2024-04-21 12:31:54 +02:00
// Frees automatically, must not survive the heapWrapper.
auto moduleData = ModuleDataFactory : : load ( reader , * heapWrapperOpt , moduleInfoPtr - > trampolines , sizeof ( moduleInfoPtr - > trampolines ) / sizeof ( moduleInfoPtr - > trampolines [ 0 ] ) ) ;
if ( ! moduleData ) {
DEBUG_FUNCTION_LINE_ERR ( " Failed to load %s " , filepath ) ;
OSFatal ( " EnvironmentLoader: Failed to load module " ) ;
return ;
}
DEBUG_FUNCTION_LINE ( " Loaded module data " ) ;
std : : map < std : : string , OSDynLoad_Module > usedRPls ;
if ( ! ElfUtils : : doRelocation ( moduleData . value ( ) - > getRelocationDataList ( ) , moduleInfoPtr - > trampolines , sizeof ( moduleInfoPtr - > trampolines ) / sizeof ( moduleInfoPtr - > trampolines [ 0 ] ) , usedRPls ) ) {
DEBUG_FUNCTION_LINE_ERR ( " Relocations failed " ) ;
OSFatal ( " EnvironmentLoader: Relocations failed " ) ;
} else {
DEBUG_FUNCTION_LINE ( " Relocation done " ) ;
}
DCFlushRange ( ( void * ) moduleData . value ( ) - > getStartAddress ( ) , moduleData . value ( ) - > getEndAddress ( ) - moduleData . value ( ) - > getStartAddress ( ) ) ;
ICInvalidateRange ( ( void * ) moduleData . value ( ) - > getStartAddress ( ) , moduleData . value ( ) - > getEndAddress ( ) - moduleData . value ( ) - > getStartAddress ( ) ) ;
char * arr [ 4 ] ;
arr [ 0 ] = ( char * ) environment_path . data ( ) ;
arr [ 1 ] = ( char * ) " EnvironmentLoader " ; //
arr [ 2 ] = ( char * ) 0x02 ; // Version
/*
* This is a hacky work around to tell Aromas Module Loader which memory region it can use safely . After using it , it ' s expected to expose new memory region via the
* custom rpl " homebrew_mappedmemory " ( See : GetHeapFromMappedMemory ) . The returned memory is expected to be RWX for user and kernel .
* Once a custom memory allocator is provided , usable_mem_start and usable_mem_end are set to 0.
*/
auto usable_mem_end = ( uint32_t ) heapWrapperOpt - > GetHeapHandle ( ) ;
if ( heapWrapperOpt - > IsAllocated ( ) ) { // Check if you use memory which is actually allocated. This means we can't give it to the module.
DEBUG_FUNCTION_LINE ( " Don't give the module a usable memory region because it will be loaded on a custom memory region. " ) ;
usable_mem_end = 0 ;
}
arr [ 3 ] = ( char * ) usable_mem_end ; // End of usable memory
DEBUG_FUNCTION_LINE ( " Calling entrypoint @%08X with: \" %s \" , \" %s \" , %08X, %08X " , moduleData . value ( ) - > getEntrypoint ( ) , arr [ 0 ] , arr [ 1 ] , arr [ 2 ] , arr [ 3 ] ) ;
// clang-format off
( ( int ( * ) ( int , char * * ) ) moduleData . value ( ) - > getEntrypoint ( ) ) ( sizeof ( arr ) / sizeof ( arr [ 0 ] ) , arr ) ;
// clang-format on
DEBUG_FUNCTION_LINE ( " Back from module " ) ;
for ( auto & rpl : usedRPls ) {
DEBUG_FUNCTION_LINE_VERBOSE ( " Release %s " , rpl . first . c_str ( ) ) ;
OSDynLoad_Release ( rpl . second ) ;
}
} else {
DEBUG_FUNCTION_LINE_ERR ( " Failed to create heap " ) ;
OSFatal ( " EnvironmentLoader: Failed to create heap " ) ;
}
// module may override the syscalls used by the Aroma KernelModule. This (tries to) re-init(s) the KernelModule after a setup module has been run.
SetupKernelModule ( ) ;
}
2021-12-29 16:21:14 +01:00
std : : string EnvironmentSelectionScreen ( const std : : map < std : : string , std : : string > & payloads , int32_t autobootIndex ) {
2021-12-25 20:14:56 +01:00
OSScreenInit ( ) ;
2022-02-03 14:08:08 +01:00
uint32_t tvBufferSize = OSScreenGetBufferSizeEx ( SCREEN_TV ) ;
2021-12-29 16:21:14 +01:00
uint32_t drcBufferSize = OSScreenGetBufferSizeEx ( SCREEN_DRC ) ;
2021-12-25 20:14:56 +01:00
2021-12-29 16:54:37 +01:00
auto * screenBuffer = ( uint8_t * ) memalign ( 0x100 , tvBufferSize + drcBufferSize ) ;
if ( ! screenBuffer ) {
2023-06-16 17:17:33 +02:00
OSFatal ( " EnvironmentLoader: Fail to allocate screenBuffer " ) ;
2021-12-29 16:54:37 +01:00
}
2022-01-01 15:49:01 +01:00
memset ( screenBuffer , 0 , tvBufferSize + drcBufferSize ) ;
2021-12-25 20:14:56 +01:00
2021-12-29 16:21:14 +01:00
OSScreenSetBufferEx ( SCREEN_TV , screenBuffer ) ;
OSScreenSetBufferEx ( SCREEN_DRC , screenBuffer + tvBufferSize ) ;
2021-12-25 20:14:56 +01:00
2021-12-29 16:21:14 +01:00
OSScreenEnableEx ( SCREEN_TV , TRUE ) ;
OSScreenEnableEx ( SCREEN_DRC , TRUE ) ;
DrawUtils : : initBuffers ( screenBuffer , tvBufferSize , screenBuffer + tvBufferSize , drcBufferSize ) ;
2023-01-05 16:45:52 +01:00
if ( ! DrawUtils : : initFont ( ) ) {
2023-06-16 17:17:33 +02:00
OSFatal ( " EnvironmentLoader: Failed to init font " ) ;
2023-01-05 16:45:52 +01:00
}
2021-12-29 16:21:14 +01:00
2021-12-29 16:54:37 +01:00
uint32_t selected = autobootIndex > 0 ? autobootIndex : 0 ;
2022-02-03 14:08:08 +01:00
int autoBoot = autobootIndex ;
2021-12-29 16:21:14 +01:00
2023-06-14 18:46:06 +02:00
{
PairMenu pairMenu ;
while ( true ) {
if ( pairMenu . ProcessPairScreen ( ) ) {
continue ;
2021-12-29 16:21:14 +01:00
}
2023-06-14 18:46:06 +02:00
InputUtils : : InputData input = InputUtils : : getControllerInput ( ) ;
if ( input . trigger & VPAD_BUTTON_UP ) {
if ( selected > 0 ) {
selected - - ;
}
} else if ( input . trigger & VPAD_BUTTON_DOWN ) {
if ( selected < payloads . size ( ) - 1 ) {
selected + + ;
}
} else if ( input . trigger & VPAD_BUTTON_A ) {
break ;
2023-06-16 17:38:34 +02:00
} else if ( input . trigger & ( VPAD_BUTTON_X | VPAD_BUTTON_MINUS ) ) {
2023-06-14 18:46:06 +02:00
autoBoot = - 1 ;
2023-06-16 17:38:34 +02:00
} else if ( input . trigger & ( VPAD_BUTTON_Y | VPAD_BUTTON_PLUS ) ) {
2023-06-14 18:46:06 +02:00
autoBoot = selected ;
2021-12-29 16:21:14 +01:00
}
2021-12-25 20:14:56 +01:00
2023-06-14 18:46:06 +02:00
2021-12-29 16:21:14 +01:00
DrawUtils : : beginDraw ( ) ;
DrawUtils : : clear ( COLOR_BACKGROUND ) ;
// draw buttons
uint32_t index = 8 + 24 + 8 + 4 ;
2022-02-03 14:08:08 +01:00
uint32_t i = 0 ;
2021-12-30 16:10:59 +01:00
if ( ! payloads . empty ( ) ) {
2022-02-02 19:12:52 +01:00
for ( auto const & [ key , val ] : payloads ) {
2021-12-30 16:10:59 +01:00
if ( i = = selected ) {
DrawUtils : : drawRect ( 16 , index , SCREEN_WIDTH - 16 * 2 , 44 , 4 , COLOR_BORDER_HIGHLIGHTED ) ;
} else {
2022-05-13 16:17:20 +02:00
DrawUtils : : drawRect ( 16 , index , SCREEN_WIDTH - 16 * 2 , 44 , 2 , ( ( int32_t ) i = = autoBoot ) ? COLOR_AUTOBOOT : COLOR_BORDER ) ;
2021-12-30 16:10:59 +01:00
}
DrawUtils : : setFontSize ( 24 ) ;
2022-05-13 16:17:20 +02:00
DrawUtils : : setFontColor ( ( ( int32_t ) i = = autoBoot ) ? COLOR_AUTOBOOT : COLOR_TEXT ) ;
2021-12-30 16:10:59 +01:00
DrawUtils : : print ( 16 * 2 , index + 8 + 24 , key . c_str ( ) ) ;
index + = 42 + 8 ;
i + + ;
2021-12-29 16:21:14 +01:00
}
2021-12-30 16:10:59 +01:00
} else {
2021-12-29 16:21:14 +01:00
DrawUtils : : setFontSize ( 24 ) ;
2021-12-30 16:10:59 +01:00
DrawUtils : : setFontColor ( COLOR_RED ) ;
const char * noEnvironmentsWarning = " No valid environments found. Press \ue000 to launch the Wii U Menu " ;
DrawUtils : : print ( SCREEN_WIDTH / 2 + DrawUtils : : getTextWidth ( noEnvironmentsWarning ) / 2 , SCREEN_HEIGHT / 2 , noEnvironmentsWarning , true ) ;
2021-12-25 20:14:56 +01:00
}
2021-12-29 16:21:14 +01:00
DrawUtils : : setFontColor ( COLOR_TEXT ) ;
// draw top bar
DrawUtils : : setFontSize ( 24 ) ;
DrawUtils : : print ( 16 , 6 + 24 , " Environment Loader " ) ;
DrawUtils : : drawRectFilled ( 8 , 8 + 24 + 4 , SCREEN_WIDTH - 8 * 2 , 3 , COLOR_WHITE ) ;
2023-07-20 09:13:15 +02:00
DrawUtils : : setFontSize ( 16 ) ;
DrawUtils : : print ( SCREEN_WIDTH - 16 , 6 + 24 , ENVIRONMENT_LOADER_VERSION ENVIRONMENT_LOADER_VERSION_EXTRA , true ) ;
2021-12-29 16:21:14 +01:00
// draw bottom bar
DrawUtils : : drawRectFilled ( 8 , SCREEN_HEIGHT - 24 - 8 - 4 , SCREEN_WIDTH - 8 * 2 , 3 , COLOR_WHITE ) ;
DrawUtils : : setFontSize ( 18 ) ;
2021-12-30 16:21:29 +01:00
if ( ! payloads . empty ( ) ) {
DrawUtils : : print ( 16 , SCREEN_HEIGHT - 8 , " \ue07d Navigate " ) ;
DrawUtils : : print ( SCREEN_WIDTH - 16 , SCREEN_HEIGHT - 8 , " \ue000 Choose " , true ) ;
2023-06-16 17:38:34 +02:00
const char * autobootHints = " \ue002 / \ue046 Clear Default / \ue003 / \ue045 Select Default " ;
2021-12-30 16:21:29 +01:00
DrawUtils : : print ( SCREEN_WIDTH / 2 + DrawUtils : : getTextWidth ( autobootHints ) / 2 , SCREEN_HEIGHT - 8 , autobootHints , true ) ;
} else {
DrawUtils : : print ( SCREEN_WIDTH - 20 , SCREEN_HEIGHT - 8 , " \ue000 Wii U Menu " , true ) ;
}
2021-12-29 16:21:14 +01:00
DrawUtils : : endDraw ( ) ;
2021-12-25 20:14:56 +01:00
}
2021-12-29 16:21:14 +01:00
}
2021-12-25 20:14:56 +01:00
2021-12-29 18:07:43 +01:00
DrawUtils : : beginDraw ( ) ;
2021-12-29 16:21:14 +01:00
DrawUtils : : clear ( COLOR_BLACK ) ;
DrawUtils : : endDraw ( ) ;
2021-12-25 20:14:56 +01:00
2021-12-29 16:21:14 +01:00
DrawUtils : : deinitFont ( ) ;
2022-01-01 15:49:01 +01:00
// Call GX2Init to shut down OSScreen
GX2Init ( nullptr ) ;
2021-12-30 14:19:37 +01:00
2021-12-29 16:21:14 +01:00
free ( screenBuffer ) ;
if ( autoBoot ! = autobootIndex ) {
2022-01-03 19:00:35 +01:00
if ( autoBoot = = - 1 ) {
writeFileContent ( AUTOBOOT_CONFIG_PATH , " -1 " ) ;
} else {
int i = 0 ;
2022-02-02 19:12:52 +01:00
for ( auto const & [ key , val ] : payloads ) {
2022-01-03 19:00:35 +01:00
if ( i = = autoBoot ) {
DEBUG_FUNCTION_LINE ( " Save config " ) ;
writeFileContent ( AUTOBOOT_CONFIG_PATH , key ) ;
break ;
}
i + + ;
2021-12-29 16:21:14 +01:00
}
}
2021-12-25 20:14:56 +01:00
}
2021-12-29 16:21:14 +01:00
2022-05-13 16:17:20 +02:00
uint32_t i = 0 ;
2022-02-02 19:12:52 +01:00
for ( auto const & [ key , val ] : payloads ) {
2021-12-25 20:14:56 +01:00
if ( i = = selected ) {
return val ;
}
i + + ;
}
2021-12-29 16:21:14 +01:00
2021-12-25 20:14:56 +01:00
return " " ;
}