2022-08-22 22:21:23 +02:00
# include "Cafe/OS/common/OSCommon.h"
# include "gui/wxgui.h"
# include "Cafe/OS/libs/gx2/GX2.h"
# include "Cafe/GameProfile/GameProfile.h"
# include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h"
# include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
# include "audio/IAudioAPI.h"
# include "Cafe/HW/Espresso/Debugger/Debugger.h"
# include "config/ActiveSettings.h"
# include "Cafe/TitleList/GameInfo.h"
# include "util/helpers/SystemException.h"
2022-09-04 01:27:44 +02:00
# include "Cafe/GraphicPack/GraphicPack2.h"
2022-08-22 22:21:23 +02:00
# include "input/InputManager.h"
# include "Cafe/CafeSystem.h"
# include "Cafe/TitleList/TitleList.h"
# include "Cafe/TitleList/GameInfo.h"
# include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
# include "Cafe/OS/libs/snd_core/ax.h"
# include "Cafe/OS/RPL/rpl.h"
# include "Cafe/HW/Latte/Core/Latte.h"
# include "Cafe/Filesystem/FST/FST.h"
2022-09-07 02:42:25 +02:00
# include "Common/FileStream.h"
2022-08-22 22:21:23 +02:00
# include "GamePatch.h"
# include <time.h>
# include "Cafe/IOSU/legacy/iosu_ioctl.h"
# include "Cafe/IOSU/legacy/iosu_act.h"
# include "Cafe/IOSU/legacy/iosu_fpd.h"
# include "Cafe/IOSU/legacy/iosu_crypto.h"
# include "Cafe/IOSU/legacy/iosu_mcp.h"
# include "Cafe/IOSU/legacy/iosu_acp.h"
# include "Cafe/IOSU/legacy/iosu_boss.h"
# include "Cafe/IOSU/legacy/iosu_nim.h"
# include "Cafe/IOSU/PDM/iosu_pdm.h"
// IOSU initializer functions
# include "Cafe/IOSU/kernel/iosu_kernel.h"
# include "Cafe/IOSU/fsa/iosu_fsa.h"
// Cafe OS initializer functions
# include "Cafe/OS/libs/avm/avm.h"
# include "Cafe/OS/libs/drmapp/drmapp.h"
# include "Cafe/OS/libs/TCL/TCL.h"
# include "Cafe/OS/libs/snd_user/snd_user.h"
# include "Cafe/OS/libs/h264_avc/h264dec.h"
# include "Cafe/OS/libs/snd_core/ax.h"
# include "Cafe/OS/libs/gx2/GX2.h"
# include "Cafe/OS/libs/gx2/GX2_Misc.h"
# include "Cafe/OS/libs/mic/mic.h"
# include "Cafe/OS/libs/nn_aoc/nn_aoc.h"
# include "Cafe/OS/libs/nn_pdm/nn_pdm.h"
# include "Cafe/OS/libs/nn_cmpt/nn_cmpt.h"
# include "Cafe/OS/libs/nn_ccr/nn_ccr.h"
# include "Cafe/OS/libs/nn_temp/nn_temp.h"
// HW interfaces
# include "Cafe/HW/SI/si.h"
// dependency to be removed
# include "gui/guiWrapper.h"
std : : string _pathToExecutable ;
std : : string _pathToBaseExecutable ;
RPLModule * applicationRPX = nullptr ;
uint32 currentBaseApplicationHash = 0 ;
uint32 currentUpdatedApplicationHash = 0 ;
bool isLaunchTypeELF = false ;
MPTR _entryPoint = MPTR_NULL ;
uint32 generateHashFromRawRPXData ( uint8 * rpxData , sint32 size )
{
uint32 h = 0x3416DCBF ;
for ( sint32 i = 0 ; i < size ; i + + )
{
uint32 c = rpxData [ i ] ;
h = ( h < < 3 ) | ( h > > 29 ) ;
h + = c ;
}
return h ;
}
bool ScanForRPX ( )
{
bool rpxFound = false ;
sint32 fscStatus = 0 ;
FSCVirtualFile * fscDirItr = fsc_openDirIterator ( " /internal/current_title/code/ " , & fscStatus ) ;
if ( fscDirItr )
{
FSCDirEntry dirEntry ;
while ( fsc_nextDir ( fscDirItr , & dirEntry ) )
{
sint32 dirItrPathLen = strlen ( dirEntry . path ) ;
if ( dirItrPathLen < 4 )
continue ;
if ( boost : : iequals ( dirEntry . path + dirItrPathLen - 4 , " .rpx " ) )
{
rpxFound = true ;
_pathToExecutable = fmt : : format ( " /internal/current_title/code/{} " , dirEntry . path ) ;
break ;
}
}
fsc_close ( fscDirItr ) ;
}
return rpxFound ;
}
void SetEntryPoint ( MPTR entryPoint )
{
_entryPoint = entryPoint ;
}
// load executable into virtual memory and set entrypoint
void LoadMainExecutable ( )
{
isLaunchTypeELF = false ;
// when launching from a disc image _pathToExecutable is initially empty
if ( _pathToExecutable . empty ( ) )
{
// try to get the RPX path from the meta files
// todo
// otherwise search for first file with .rpx extension in the code folder
if ( ! ScanForRPX ( ) )
{
forceLog_printf ( " Unable to find RPX executable " ) ;
cemuLog_waitForFlush ( ) ;
cemu_assert ( false ) ;
}
}
// extract and load RPX
uint32 rpxSize = 0 ;
uint8 * rpxData = fsc_extractFile ( _pathToExecutable . c_str ( ) , & rpxSize ) ;
if ( rpxData = = nullptr )
{
forceLog_printf ( " Failed to load \" %s \" " , _pathToExecutable . c_str ( ) ) ;
cemuLog_waitForFlush ( ) ;
cemu_assert ( false ) ;
}
currentUpdatedApplicationHash = generateHashFromRawRPXData ( rpxData , rpxSize ) ;
// determine if this file is an ELF
const uint8 elfHeaderMagic [ 9 ] = { 0x7F , 0x45 , 0x4C , 0x46 , 0x01 , 0x02 , 0x01 , 0x00 , 0x00 } ;
if ( rpxSize > = 10 & & memcmp ( rpxData , elfHeaderMagic , sizeof ( elfHeaderMagic ) ) = = 0 )
{
// ELF
SetEntryPoint ( ELF_LoadFromMemory ( rpxData , rpxSize , _pathToExecutable . c_str ( ) ) ) ;
isLaunchTypeELF = true ;
}
else
{
// RPX
RPLLoader_AddDependency ( _pathToExecutable . c_str ( ) ) ;
applicationRPX = rpl_loadFromMem ( rpxData , rpxSize , ( char * ) _pathToExecutable . c_str ( ) ) ;
if ( ! applicationRPX )
{
wxMessageBox ( _ ( " Failed to run this title because the executable is damaged " ) ) ;
cemuLog_createLogFile ( false ) ;
cemuLog_waitForFlush ( ) ;
exit ( 0 ) ;
}
RPLLoader_SetMainModule ( applicationRPX ) ;
SetEntryPoint ( RPLLoader_GetModuleEntrypoint ( applicationRPX ) ) ;
}
free ( rpxData ) ;
// get RPX hash of game without update
uint32 baseRpxSize = 0 ;
uint8 * baseRpxData = fsc_extractFile ( ! _pathToBaseExecutable . empty ( ) ? _pathToBaseExecutable . c_str ( ) : _pathToExecutable . c_str ( ) , & baseRpxSize , FSC_PRIORITY_BASE ) ;
if ( baseRpxData = = nullptr )
{
currentBaseApplicationHash = currentUpdatedApplicationHash ;
}
else
{
currentBaseApplicationHash = generateHashFromRawRPXData ( baseRpxData , baseRpxSize ) ;
}
free ( baseRpxData ) ;
debug_printf ( " RPXHash: 0x%08x \n " , currentBaseApplicationHash ) ;
}
fs : : path getTitleSavePath ( )
{
const uint64 titleId = CafeSystem : : GetForegroundTitleId ( ) ;
return ActiveSettings : : GetMlcPath ( " usr/save/{:08X}/{:08X}/user/ " , ( uint32 ) ( titleId > > 32 ) , ( uint32 ) ( titleId & 0xFFFFFFFF ) ) ;
}
void InfoLog_TitleLoaded ( )
{
cemuLog_createLogFile ( false ) ;
uint64 titleId = CafeSystem : : GetForegroundTitleId ( ) ;
cemuLog_log ( LogType : : Force , " ------- Loaded title ------- " ) ;
cemuLog_log ( LogType : : Force , " TitleId: {:08x}-{:08x} " , ( uint32 ) ( titleId > > 32 ) , ( uint32 ) ( titleId & 0xFFFFFFFF ) ) ;
cemuLog_log ( LogType : : Force , " TitleVersion: v{} " , CafeSystem : : GetForegroundTitleVersion ( ) ) ;
CafeConsoleRegion region = CafeSystem : : GetForegroundTitleRegion ( ) ;
if ( region = = CafeConsoleRegion : : JPN )
cemuLog_log ( LogType : : Force , " TitleRegion: JP " ) ;
else if ( region = = CafeConsoleRegion : : EUR )
cemuLog_log ( LogType : : Force , " TitleRegion: EU " ) ;
else if ( region = = CafeConsoleRegion : : USA )
cemuLog_log ( LogType : : Force , " TitleRegion: US " ) ;
fs : : path effectiveSavePath = getTitleSavePath ( ) ;
std : : error_code ec ;
const bool saveDirExists = fs : : exists ( effectiveSavePath , ec ) ;
cemuLog_force ( " Save path: {}{} " , _utf8Wrapper ( effectiveSavePath ) , saveDirExists ? " " : " (not present) " ) ;
// log shader cache name
cemuLog_log ( LogType : : Force , " Shader cache file: shaderCache/transferable/{:016x}.bin " , titleId ) ;
// game profile info
std : : string gameProfilePath ;
if ( g_current_game_profile - > IsDefaultProfile ( ) )
gameProfilePath = fmt : : format ( " gameProfiles/default/{:016x}.ini " , titleId ) ;
else
gameProfilePath = fmt : : format ( " gameProfiles/{:016x}.ini " , titleId ) ;
cemuLog_log ( LogType : : Force , " gameprofile path: {} " , g_current_game_profile - > IsLoaded ( ) ? gameProfilePath : std : : string ( " (not present) " ) ) ;
// rpx hash of updated game
cemuLog_log ( LogType : : Force , " RPX hash (updated): {:08x} " , currentUpdatedApplicationHash ) ;
cemuLog_log ( LogType : : Force , " RPX hash (base): {:08x} " , currentBaseApplicationHash ) ;
memory_logModifiedMemoryRanges ( ) ;
}
void InfoLog_PrintActiveSettings ( )
{
const auto & config = GetConfig ( ) ;
forceLog_printf ( " ------- Active settings ------- " ) ;
// settings to log:
forceLog_printf ( " CPU-Mode: %s%s " , fmt : : format ( " {} " , ActiveSettings : : GetCPUMode ( ) ) . c_str ( ) , g_current_game_profile - > GetCPUMode ( ) . has_value ( ) ? " (gameprofile) " : " " ) ;
forceLog_printf ( " Load shared libraries: %s%s " , ActiveSettings : : LoadSharedLibrariesEnabled ( ) ? " true " : " false " , g_current_game_profile - > ShouldLoadSharedLibraries ( ) . has_value ( ) ? " (gameprofile) " : " " ) ;
forceLog_printf ( " Use precompiled shaders: %s%s " , fmt : : format ( " {} " , ActiveSettings : : GetPrecompiledShadersOption ( ) ) . c_str ( ) , g_current_game_profile - > GetPrecompiledShadersState ( ) . has_value ( ) ? " (gameprofile) " : " " ) ;
forceLog_printf ( " Full sync at GX2DrawDone: %s " , ActiveSettings : : WaitForGX2DrawDoneEnabled ( ) ? " true " : " false " ) ;
if ( ActiveSettings : : GetGraphicsAPI ( ) = = GraphicAPI : : kVulkan )
{
forceLog_printf ( " Async compile: %s " , GetConfig ( ) . async_compile . GetValue ( ) ? " true " : " false " ) ;
if ( ! GetConfig ( ) . vk_accurate_barriers . GetValue ( ) )
forceLog_printf ( " Accurate barriers are disabled! " ) ;
}
forceLog_printf ( " Console language: %s " , fmt : : format ( " {} " , config . console_language ) . c_str ( ) ) ;
}
void PPCCore_setupSPR ( PPCInterpreter_t * hCPU , uint32 coreIndex )
{
hCPU - > sprExtended . PVR = 0x70010001 ;
hCPU - > spr . UPIR = coreIndex ;
hCPU - > sprExtended . msr | = MSR_FP ; // enable floating point
}
struct SharedDataEntry
{
/* +0x00 */ uint32be name ;
/* +0x04 */ uint32be fileType ; // 2 = font
/* +0x08 */ uint32be kernelFilenamePtr ;
/* +0x0C */ MEMPTR < void > data ;
/* +0x10 */ uint32be size ;
/* +0x14 */ uint32be ukn14 ;
/* +0x18 */ uint32be ukn18 ;
} ;
struct
{
uint32 name ;
uint32 fileType ;
const char * fileName ;
const char * resourcePath ;
const char * mlcPath ;
} shareddataDef [ ] =
{
0xFFCAFE01 , 2 , " CafeCn.ttf " , " resources/sharedFonts/CafeCn.ttf " , " sys/title/0005001b/10042400/content/CafeCn.ttf " ,
0xFFCAFE02 , 2 , " CafeKr.ttf " , " resources/sharedFonts/CafeKr.ttf " , " sys/title/0005001b/10042400/content/CafeKr.ttf " ,
0xFFCAFE03 , 2 , " CafeStd.ttf " , " resources/sharedFonts/CafeStd.ttf " , " sys/title/0005001b/10042400/content/CafeStd.ttf " ,
0xFFCAFE04 , 2 , " CafeTw.ttf " , " resources/sharedFonts/CafeTw.ttf " , " sys/title/0005001b/10042400/content/CafeTw.ttf "
} ;
static_assert ( sizeof ( SharedDataEntry ) = = 0x1C ) ;
2022-08-26 19:41:42 +02:00
uint32 loadSharedData ( )
2022-08-22 22:21:23 +02:00
{
// check if font files are dumped
bool hasAllShareddataFiles = true ;
for ( sint32 i = 0 ; i < sizeof ( shareddataDef ) / sizeof ( shareddataDef [ 0 ] ) ; i + + )
{
bool existsInMLC = fs : : exists ( ActiveSettings : : GetMlcPath ( shareddataDef [ i ] . mlcPath ) ) ;
bool existsInResources = fs : : exists ( ActiveSettings : : GetPath ( shareddataDef [ i ] . resourcePath ) ) ;
if ( ! existsInMLC & & ! existsInResources )
{
cemuLog_log ( LogType : : Force , " Shared font {} is not present " , shareddataDef [ i ] . fileName ) ;
hasAllShareddataFiles = false ;
break ;
}
}
sint32 numEntries = sizeof ( shareddataDef ) / sizeof ( shareddataDef [ 0 ] ) ;
if ( hasAllShareddataFiles )
{
// all shareddata font files are present -> load them
SharedDataEntry * shareddataTable = ( SharedDataEntry * ) memory_getPointerFromVirtualOffset ( 0xF8000000 ) ;
memset ( shareddataTable , 0 , sizeof ( SharedDataEntry ) * numEntries ) ;
uint8 * dataWritePtr = memory_getPointerFromVirtualOffset ( 0xF8000000 + sizeof ( SharedDataEntry ) * numEntries ) ;
// setup entries
for ( sint32 i = 0 ; i < numEntries ; i + + )
{
// try to read font from MLC first
auto path = ActiveSettings : : GetMlcPath ( shareddataDef [ i ] . mlcPath ) ;
FileStream * fontFile = FileStream : : openFile2 ( path ) ;
// alternatively fall back to our shared fonts
if ( ! fontFile )
{
path = ActiveSettings : : GetPath ( shareddataDef [ i ] . resourcePath ) ;
fontFile = FileStream : : openFile2 ( path ) ;
}
if ( ! fontFile )
{
cemuLog_log ( LogType : : Force , " Failed to load shared font {} " , shareddataDef [ i ] . fileName ) ;
continue ;
}
uint32 fileSize = fontFile - > GetSize ( ) ;
fontFile - > readData ( dataWritePtr , fileSize ) ;
delete fontFile ;
// setup entry
shareddataTable [ i ] . name = shareddataDef [ i ] . name ;
shareddataTable [ i ] . fileType = shareddataDef [ i ] . fileType ;
shareddataTable [ i ] . kernelFilenamePtr = 0x00000000 ;
shareddataTable [ i ] . data = dataWritePtr ;
shareddataTable [ i ] . size = fileSize ;
shareddataTable [ i ] . ukn14 = 0x00000000 ;
shareddataTable [ i ] . ukn18 = 0x00000000 ;
// advance write offset and pad to 16 byte alignment
dataWritePtr + = ( ( fileSize + 15 ) & ~ 15 ) ;
}
forceLog_printfW ( L " COS: System fonts found. Generated shareddata (%dKB) " , ( uint32 ) ( dataWritePtr - ( uint8 * ) shareddataTable ) / 1024 ) ;
return memory_getVirtualOffsetFromPointer ( dataWritePtr ) ;
}
// alternative method: load RAM dump
const auto path = ActiveSettings : : GetPath ( " shareddata.bin " ) ;
FileStream * ramDumpFile = FileStream : : openFile2 ( path ) ;
if ( ramDumpFile )
{
ramDumpFile - > readData ( memory_getPointerFromVirtualOffset ( 0xF8000000 ) , 0x02000000 ) ;
delete ramDumpFile ;
return ( mmuRange_SHARED_AREA . getBase ( ) + 0x02000000 ) ;
}
return mmuRange_SHARED_AREA . getBase ( ) + sizeof ( SharedDataEntry ) * numEntries ;
}
void cemu_initForGame ( )
{
gui_updateWindowTitles ( false , true , 0.0 ) ;
// input manager apply game profile
InputManager : : instance ( ) . apply_game_profile ( ) ;
// log info for launched title
InfoLog_TitleLoaded ( ) ;
// determine cycle offset since 1.1.2000
uint64 secondsSince2000_UTC = ( uint64 ) ( time ( NULL ) - 946684800 ) ;
ppcCyclesSince2000_UTC = secondsSince2000_UTC * ( uint64 ) ESPRESSO_CORE_CLOCK ;
time_t theTime = ( time ( NULL ) - 946684800 ) ;
{
tm * lt = localtime ( & theTime ) ;
2022-08-26 04:03:26 +02:00
# if BOOST_OS_WINDOWS
2022-08-22 22:21:23 +02:00
theTime = _mkgmtime ( lt ) ;
# else
theTime = timegm ( lt ) ;
# endif
}
ppcCyclesSince2000 = theTime * ( uint64 ) ESPRESSO_CORE_CLOCK ;
ppcCyclesSince2000TimerClock = ppcCyclesSince2000 / 20ULL ;
PPCTimer_start ( ) ;
// this must happen after the RPX/RPL files are mapped to memory (coreinit sets up heaps so that they don't overwrite RPX/RPL data)
osLib_load ( ) ;
// link all modules
uint32 linkTimeStart = GetTickCount ( ) ;
RPLLoader_UpdateDependencies ( ) ;
RPLLoader_Link ( ) ;
RPLLoader_NotifyControlPassedToApplication ( ) ;
uint32 linkTime = GetTickCount ( ) - linkTimeStart ;
forceLog_printf ( " RPL link time: %dms " , linkTime ) ;
// for HBL ELF: Setup OS-specifics struct
if ( isLaunchTypeELF )
{
memory_writeU32 ( 0x801500 , rpl_mapHLEImport ( nullptr , " coreinit " , " OSDynLoad_Acquire " , true ) ) ;
memory_writeU32 ( 0x801504 , rpl_mapHLEImport ( nullptr , " coreinit " , " OSDynLoad_FindExport " , true ) ) ;
}
else
{
// replace any known function signatures with our HLE implementations and patch bugs in the games
GamePatch_scan ( ) ;
}
InfoLog_PrintActiveSettings ( ) ;
Latte_Start ( ) ;
// check for debugger entrypoint bp
debugger_handleEntryBreakpoint ( _entryPoint ) ;
// load graphic packs
forceLog_printf ( " ------- Activate graphic packs ------- " ) ;
2022-09-04 01:27:44 +02:00
GraphicPack2 : : ActivateForCurrentTitle ( ) ;
2022-08-22 22:21:23 +02:00
// print audio log
IAudioAPI : : PrintLogging ( ) ;
// everything initialized
forceLog_printf ( " ------- Run title ------- " ) ;
// wait till GPU thread is initialized
while ( g_isGPUInitFinished = = false ) std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 50 ) ) ;
// init initial thread
OSThread_t * initialThread = coreinit : : OSGetDefaultThread ( 1 ) ;
coreinit : : OSSetThreadPriority ( initialThread , 16 ) ;
coreinit : : OSRunThread ( initialThread , PPCInterpreter_makeCallableExportDepr ( coreinit_start ) , 0 , nullptr ) ;
// init AX and start AX I/O thread
snd_core : : AXOut_init ( ) ;
// init ppc recompiler
PPCRecompiler_init ( ) ;
}
void cemu_deinitForGame ( )
{
// reset audio
snd_core : : AXOut_reset ( ) ;
snd_core : : reset ( ) ;
// reset alarms
coreinit : : OSAlarm_resetAll ( ) ;
// delete all threads
PPCCore_deleteAllThreads ( ) ;
// reset mount paths
fsc_unmountAll ( ) ;
// reset RPL loader
RPLLoader_ResetState ( ) ;
// reset GX2
GX2 : : _GX2DriverReset ( ) ;
}
namespace CafeSystem
{
void InitVirtualMlcStorage ( ) ;
void MlcStorageMountTitle ( TitleInfo & titleInfo ) ;
bool sLaunchModeIsStandalone = false ;
bool sSystemRunning = false ;
TitleId sForegroundTitleId = 0 ;
GameInfo2 sGameInfo_ForegroundTitle ;
void Initialize ( )
{
static bool s_initialized = false ;
if ( s_initialized )
return ;
s_initialized = true ;
// allocate memory for all SysAllocators
// must happen before all COS modules, but also before iosu::kernel::Init()
SysAllocatorContainer : : GetInstance ( ) . Initialize ( ) ;
// init IOSU
iosu : : kernel : : Initialize ( ) ;
iosu : : fsa : : Initialize ( ) ;
iosuIoctl_init ( ) ;
iosuAct_init_depr ( ) ;
iosu : : act : : Initialize ( ) ;
iosu : : fpd : : Initialize ( ) ;
iosu : : iosuMcp_init ( ) ;
iosu : : mcp : : Init ( ) ;
iosu : : iosuAcp_init ( ) ;
iosu : : boss_init ( ) ;
iosu : : nim : : Initialize ( ) ;
iosu : : pdm : : Initialize ( ) ;
// init Cafe OS
avm : : Initialize ( ) ;
drmapp : : Initialize ( ) ;
TCL : : Initialize ( ) ;
nn : : cmpt : : Initialize ( ) ;
nn : : ccr : : Initialize ( ) ;
nn : : temp : : Initialize ( ) ;
nn : : aoc : : Initialize ( ) ;
nn : : pdm : : Initialize ( ) ;
snd : : user : : Initialize ( ) ;
H264 : : Initialize ( ) ;
snd_core : : Initialize ( ) ;
mic : : Initialize ( ) ;
// init hardware register interfaces
HW_SI : : Initialize ( ) ;
}
std : : string GetInternalVirtualCodeFolder ( )
{
return " /internal/current_title/code/ " ;
}
STATUS_CODE LoadAndMountForegroundTitle ( TitleId titleId )
{
cemuLog_log ( LogType : : Force , " Mounting title {:016x} " , ( uint64 ) titleId ) ;
sGameInfo_ForegroundTitle = CafeTitleList : : GetGameInfo ( titleId ) ;
if ( ! sGameInfo_ForegroundTitle . IsValid ( ) )
{
cemuLog_log ( LogType : : Force , " Mounting failed: Game meta information is either missing, inaccessible or not valid (missing or invalid .xml files in code and meta folder) " ) ;
return STATUS_CODE : : UNABLE_TO_MOUNT ;
}
// check base
TitleInfo & titleBase = sGameInfo_ForegroundTitle . GetBase ( ) ;
if ( ! titleBase . IsValid ( ) )
return STATUS_CODE : : UNABLE_TO_MOUNT ;
if ( ! titleBase . ParseXmlInfo ( ) )
return STATUS_CODE : : UNABLE_TO_MOUNT ;
cemuLog_log ( LogType : : Force , " Base: {} " , titleBase . GetPrintPath ( ) ) ;
// mount base
if ( ! titleBase . Mount ( " /vol/content " , " content " , FSC_PRIORITY_BASE ) | | ! titleBase . Mount ( GetInternalVirtualCodeFolder ( ) , " code " , FSC_PRIORITY_BASE ) )
{
cemuLog_log ( LogType : : Force , " Mounting failed " ) ;
return STATUS_CODE : : UNABLE_TO_MOUNT ;
}
// check update
TitleInfo & titleUpdate = sGameInfo_ForegroundTitle . GetUpdate ( ) ;
if ( titleUpdate . IsValid ( ) )
{
if ( ! titleUpdate . ParseXmlInfo ( ) )
return STATUS_CODE : : UNABLE_TO_MOUNT ;
cemuLog_log ( LogType : : Force , " Update: {} " , titleUpdate . GetPrintPath ( ) ) ;
// mount update
if ( ! titleUpdate . Mount ( " /vol/content " , " content " , FSC_PRIORITY_PATCH ) | | ! titleUpdate . Mount ( GetInternalVirtualCodeFolder ( ) , " code " , FSC_PRIORITY_PATCH ) )
{
cemuLog_log ( LogType : : Force , " Mounting failed " ) ;
return STATUS_CODE : : UNABLE_TO_MOUNT ;
}
}
else
cemuLog_log ( LogType : : Force , " Update: Not present " ) ;
// check AOC
auto aocList = sGameInfo_ForegroundTitle . GetAOC ( ) ;
if ( ! aocList . empty ( ) )
{
// todo - support for multi-title AOC
TitleInfo & titleAOC = aocList [ 0 ] ;
if ( ! titleAOC . ParseXmlInfo ( ) )
return STATUS_CODE : : UNABLE_TO_MOUNT ;
cemu_assert_debug ( titleAOC . IsValid ( ) ) ;
cemuLog_log ( LogType : : Force , " DLC: {} " , titleAOC . GetPrintPath ( ) ) ;
// mount AOC
if ( ! titleAOC . Mount ( fmt : : format ( " /vol/aoc{:016x} " , titleAOC . GetAppTitleId ( ) ) , " content " , FSC_PRIORITY_PATCH ) )
{
cemuLog_log ( LogType : : Force , " Mounting failed " ) ;
return STATUS_CODE : : UNABLE_TO_MOUNT ;
}
}
else
cemuLog_log ( LogType : : Force , " DLC: Not present " ) ;
sForegroundTitleId = titleId ;
return STATUS_CODE : : SUCCESS ;
}
STATUS_CODE SetupExecutable ( )
{
// mount mlc directories
fscDeviceHostFS_mapBaseDirectories_deprecated ( ) ;
// set rpx path from cos.xml if available
_pathToBaseExecutable = _pathToExecutable ;
if ( ! sLaunchModeIsStandalone )
{
std : : string _argstr = CafeSystem : : GetForegroundTitleArgStr ( ) ;
const char * argstr = _argstr . c_str ( ) ;
if ( argstr & & * argstr ! = ' \0 ' )
{
const std : : string tmp = argstr ;
const auto index = tmp . find ( " .rpx " ) ;
if ( index ! = std : : string : : npos )
{
fs : : path rpx = _pathToExecutable ;
rpx . replace_filename ( tmp . substr ( 0 , index + 4 ) ) ; // cut off after .rpx
std : : string rpxPath ;
rpxPath = " /internal/current_title/code/ " ;
rpxPath . append ( rpx . generic_string ( ) ) ;
int status ;
const auto file = fsc_open ( rpxPath . c_str ( ) , FSC_ACCESS_FLAG : : OPEN_FILE | FSC_ACCESS_FLAG : : READ_PERMISSION , & status ) ;
if ( file )
{
_pathToExecutable = rpxPath ;
fsc_close ( file ) ;
}
}
}
}
LoadMainExecutable ( ) ;
gameProfile_load ( ) ;
return STATUS_CODE : : SUCCESS ;
}
STATUS_CODE PrepareForegroundTitle ( TitleId titleId )
{
CafeTitleList : : WaitForMandatoryScan ( ) ;
sLaunchModeIsStandalone = false ;
TitleIdParser tip ( titleId ) ;
if ( tip . GetType ( ) = = TitleIdParser : : TITLE_TYPE : : AOC | | tip . GetType ( ) = = TitleIdParser : : TITLE_TYPE : : BASE_TITLE_UPDATE )
cemuLog_log ( LogType : : Force , " Launched titleId is not the base of a title " ) ;
// mount title folders
STATUS_CODE r = LoadAndMountForegroundTitle ( titleId ) ;
if ( r ! = STATUS_CODE : : SUCCESS )
return r ;
// map memory
memory_mapForCurrentTitle ( ) ;
// load RPX
r = SetupExecutable ( ) ;
if ( r ! = STATUS_CODE : : SUCCESS )
return r ;
loadSharedData ( ) ;
InitVirtualMlcStorage ( ) ;
return STATUS_CODE : : SUCCESS ;
}
STATUS_CODE PrepareForegroundTitleFromStandaloneRPX ( const fs : : path & path )
{
sLaunchModeIsStandalone = true ;
cemuLog_log ( LogType : : Force , " Launching executable in standalone mode due to incorrect layout or missing meta files " ) ;
fs : : path executablePath = path ;
std : : string dirName = _utf8Wrapper ( executablePath . parent_path ( ) . filename ( ) ) ;
if ( boost : : iequals ( dirName , " code " ) )
{
// check for content folder
fs : : path contentPath = executablePath . parent_path ( ) . parent_path ( ) . append ( " content " ) ;
std : : error_code ec ;
if ( fs : : is_directory ( contentPath , ec ) )
{
// mounting content folder
bool r = FSCDeviceHostFS_Mount ( std : : string ( " /vol/content " ) . c_str ( ) , boost : : nowide : : widen ( _utf8Wrapper ( contentPath ) ) . c_str ( ) , FSC_PRIORITY_BASE ) ;
if ( ! r )
{
cemuLog_log ( LogType : : Force , " Failed to mount {} " , _utf8Wrapper ( contentPath ) . c_str ( ) ) ;
return STATUS_CODE : : UNABLE_TO_MOUNT ;
}
}
}
// mount code folder to a virtual temporary path
FSCDeviceHostFS_Mount ( std : : string ( " /internal/code/ " ) . c_str ( ) , boost : : nowide : : widen ( _utf8Wrapper ( executablePath . parent_path ( ) ) ) . c_str ( ) , FSC_PRIORITY_BASE ) ;
std : : string internalExecutablePath = " /internal/code/ " ;
internalExecutablePath . append ( _utf8Wrapper ( executablePath . filename ( ) ) ) ;
_pathToExecutable = internalExecutablePath ;
// since a lot of systems (including save folder location) rely on a TitleId, we derive a placeholder id from the executable hash
auto execData = fsc_extractFile ( _pathToExecutable . c_str ( ) ) ;
if ( ! execData )
return STATUS_CODE : : INVALID_RPX ;
uint32 h = generateHashFromRawRPXData ( execData - > data ( ) , execData - > size ( ) ) ;
sForegroundTitleId = 0xFFFFFFFF00000000ULL | ( uint64 ) h ;
cemuLog_log ( LogType : : Force , " Generated placeholder TitleId: {:016x} " , sForegroundTitleId ) ;
// load executable
memory_mapForCurrentTitle ( ) ;
SetupExecutable ( ) ;
loadSharedData ( ) ;
InitVirtualMlcStorage ( ) ;
return STATUS_CODE : : SUCCESS ;
}
void _LaunchTitleThread ( )
{
// init
cemu_initForGame ( ) ;
// enter scheduler
if ( ActiveSettings : : GetCPUMode ( ) = = CPUMode : : MulticoreRecompiler )
coreinit : : OSSchedulerBegin ( 3 ) ;
else
coreinit : : OSSchedulerBegin ( 1 ) ;
iosu : : pdm : : StartTrackingTime ( GetForegroundTitleId ( ) ) ;
}
void LaunchForegroundTitle ( )
{
PPCTimer_waitForInit ( ) ;
// start system
sSystemRunning = true ;
gui_notifyGameLoaded ( ) ;
std : : thread t ( _LaunchTitleThread ) ;
t . detach ( ) ;
}
bool IsTitleRunning ( )
{
return sSystemRunning ;
}
TitleId GetForegroundTitleId ( )
{
cemu_assert_debug ( sForegroundTitleId ! = 0 ) ;
return sForegroundTitleId ;
}
uint16 GetForegroundTitleVersion ( )
{
if ( sLaunchModeIsStandalone )
return 0 ;
return sGameInfo_ForegroundTitle . GetVersion ( ) ;
}
CafeConsoleRegion GetForegroundTitleRegion ( )
{
if ( sLaunchModeIsStandalone )
return CafeConsoleRegion : : USA ;
return sGameInfo_ForegroundTitle . GetRegion ( ) ;
}
std : : string GetForegroundTitleName ( )
{
if ( sLaunchModeIsStandalone )
return " Missing meta data " ;
// todo - use language based on Cemu console language
return sGameInfo_ForegroundTitle . GetBase ( ) . GetMetaInfo ( ) - > GetShortName ( CafeConsoleLanguage : : EN ) ;
}
std : : string GetForegroundTitleArgStr ( )
{
if ( sLaunchModeIsStandalone )
return " " ;
auto & update = sGameInfo_ForegroundTitle . GetUpdate ( ) ;
if ( update . IsValid ( ) )
return update . GetArgStr ( ) ;
return sGameInfo_ForegroundTitle . GetBase ( ) . GetArgStr ( ) ;
}
// pick platform region based on title region
CafeConsoleRegion GetPlatformRegion ( )
{
CafeConsoleRegion titleRegion = GetForegroundTitleRegion ( ) ;
CafeConsoleRegion platformRegion = CafeConsoleRegion : : USA ;
if ( HAS_FLAG ( titleRegion , CafeConsoleRegion : : JPN ) )
platformRegion = CafeConsoleRegion : : JPN ;
else if ( HAS_FLAG ( titleRegion , CafeConsoleRegion : : EUR ) )
platformRegion = CafeConsoleRegion : : EUR ;
else if ( HAS_FLAG ( titleRegion , CafeConsoleRegion : : USA ) )
platformRegion = CafeConsoleRegion : : USA ;
return platformRegion ;
}
void UnmountCurrentTitle ( )
{
TitleInfo & titleBase = sGameInfo_ForegroundTitle . GetBase ( ) ;
if ( titleBase . IsValid ( ) )
titleBase . UnmountAll ( ) ;
if ( sGameInfo_ForegroundTitle . HasUpdate ( ) )
{
TitleInfo & titleUpdate = sGameInfo_ForegroundTitle . GetUpdate ( ) ;
if ( titleUpdate . IsValid ( ) )
titleUpdate . UnmountAll ( ) ;
}
if ( sGameInfo_ForegroundTitle . HasAOC ( ) )
{
auto titleInfoList = sGameInfo_ForegroundTitle . GetAOC ( ) ;
for ( auto & it : titleInfoList )
{
if ( it . IsValid ( ) )
it . UnmountAll ( ) ;
}
}
fsc_unmount ( " /internal/code/ " , FSC_PRIORITY_BASE ) ;
}
void ShutdownTitle ( )
{
if ( ! sSystemRunning )
return ;
coreinit : : OSSchedulerEnd ( ) ;
Latte_Stop ( ) ;
iosu : : pdm : : Stop ( ) ;
iosu : : act : : Stop ( ) ;
iosu : : mcp : : Shutdown ( ) ;
iosu : : fsa : : Shutdown ( ) ;
2022-09-04 01:27:44 +02:00
GraphicPack2 : : Reset ( ) ;
2022-08-22 22:21:23 +02:00
UnmountCurrentTitle ( ) ;
sSystemRunning = false ;
}
/* Virtual mlc storage */
void InitVirtualMlcStorage ( )
{
// starting with Cemu 1.27.0 /vol/storage_mlc01/ is virtualized, meaning that it doesn't point to one singular host os folder anymore
// instead it now uses a more complex solution to source titles with various formats (folder, wud, wua) from the game paths and host mlc path
// todo - mount /vol/storage_mlc01/ with base priority to the host mlc?
// since mounting titles is an expensive operation we have to avoid mounting all titles at once
// only the current title gets mounted immediately, every other title should be mounted lazily on first access
// always mount the currently running title
if ( sGameInfo_ForegroundTitle . GetBase ( ) . IsValid ( ) )
MlcStorageMountTitle ( sGameInfo_ForegroundTitle . GetBase ( ) ) ;
if ( sGameInfo_ForegroundTitle . GetUpdate ( ) . IsValid ( ) )
MlcStorageMountTitle ( sGameInfo_ForegroundTitle . GetUpdate ( ) ) ;
for ( auto & it : sGameInfo_ForegroundTitle . GetAOC ( ) )
MlcStorageMountTitle ( it ) ;
// setup system for lazy-mounting of other known titles
// todo - how to handle this?
// when something iterates /vol/storage_mlc01/usr/title/ we can use a fake FS device mounted to /vol/storage_mlc01/usr/title and sys/title that simulates the title id folders
// the same device would then have to mount titles when their folders are actually accessed
}
// /vol/storage_mlc01/<usr or sys>/title/<titleIdHigh>/<titleIdLow>
std : : string GetMlcStoragePath ( TitleId titleId )
{
TitleIdParser tip ( titleId ) ;
return fmt : : format ( " /vol/storage_mlc01/{}/title/{:08x}/{:08x} " , tip . IsSystemTitle ( ) ? " sys " : " usr " , ( uint32 ) ( titleId > > 32 ) , ( uint32 ) ( titleId & 0xFFFFFFFF ) ) ;
}
std : : map < TitleId , TitleInfo * > m_mlcMountedTitles ;
// mount title to our virtual MLC storage
// /vol/storage_mlc01/<usr or sys>/title/<titleIdHigh>/<titleIdLow>
void MlcStorageMountTitle ( TitleInfo & titleInfo )
{
if ( ! titleInfo . IsValid ( ) )
{
cemu_assert_suspicious ( ) ;
return ;
}
TitleId titleId = titleInfo . GetAppTitleId ( ) ;
if ( m_mlcMountedTitles . find ( titleId ) ! = m_mlcMountedTitles . end ( ) )
{
cemu_assert_suspicious ( ) ; // already mounted
return ;
}
std : : string mlcStoragePath = GetMlcStoragePath ( titleId ) ;
TitleInfo * mountTitleInfo = new TitleInfo ( titleInfo ) ;
if ( ! mountTitleInfo - > Mount ( mlcStoragePath , " " , FSC_PRIORITY_BASE ) )
{
cemuLog_log ( LogType : : Force , " Failed to mount title to virtual storage " ) ;
delete mountTitleInfo ;
return ;
}
m_mlcMountedTitles . emplace ( titleId , mountTitleInfo ) ;
}
void MlcStorageMountTitle ( TitleId titleId )
{
TitleInfo titleInfo ;
if ( ! CafeTitleList : : GetFirstByTitleId ( titleId , titleInfo ) )
return ;
MlcStorageMountTitle ( titleInfo ) ;
}
void MlcStorageMountAllTitles ( )
{
std : : vector < uint64 > titleIds = CafeTitleList : : GetAllTitleIds ( ) ;
for ( auto & it : titleIds )
MlcStorageMountTitle ( it ) ;
}
uint32 GetRPXHashBase ( )
{
return currentBaseApplicationHash ;
}
uint32 GetRPXHashUpdated ( )
{
return currentUpdatedApplicationHash ;
}
}