Files
pico-launcher/arm9/source/main.cpp
2025-11-25 17:41:31 +01:00

220 lines
5.9 KiB
C++

#include "common.h"
#include "core/di.h"
#include <libtwl/gfx/gfx.h>
#include <libtwl/ipc/ipcFifoSystem.h>
#include <libtwl/ipc/ipcSync.h>
#include <libtwl/rtos/rtosIrq.h>
#include <libtwl/rtos/rtosThread.h>
#include "services/process/ProcessManager.h"
#include "logger/PlainLogger.h"
#include "logger/NocashOutputStream.h"
#include "logger/SemihostingOutputStream.h"
#include "logger/NitroEmulatorOutputStream.h"
#include "logger/PicoAgbAdapterOutputStream.h"
#include "logger/NullLogger.h"
#include "logger/ThreadSafeLogger.h"
#include "dsiSdIpc.h"
#include "dldiIpc.h"
#include "fat/ff.h"
#include "services/settings/JsonAppSettingsService.h"
#include "App.h"
#include "core/Environment.h"
#include "rng/RandomGenerator.h"
#include "rng/LinearCongruentialGenerator.h"
#include "rng/ThreadSafeRandomGenerator.h"
#include "NotoSansJP-Regular-10_nft2.h"
#include "NotoSansJP-Medium-7_5_nft2.h"
#include "NotoSansJP-Medium-10_nft2.h"
#include "NotoSansJP-Medium-11_nft2.h"
#include "gui/font/nitroFont2.h"
#include "picoLoaderBootstrap.h"
#include "rtcIpc.h"
ProcessManager gProcessManager;
ILogger* gLogger;
FATFS gFatFs;
RandomGenerator* gRandomGenerator;
static void initLogger()
{
std::unique_ptr<IOutputStream> outputStream;
if (Environment::IsIsNitroEmulator() && Environment::SupportsAgbSemihosting())
{
outputStream = std::make_unique<NitroEmulatorOutputStream>();
}
else if (Environment::SupportsJtagSemihosting())
{
outputStream = std::make_unique<SemihostingOutputStream>();
}
else if (Environment::HasPicoAgbAdapter())
{
outputStream = std::make_unique<PicoAgbAdapterOutputStream>();
}
else if (Environment::SupportsNocashPrint())
{
outputStream = std::make_unique<NocashOutputStream>();
}
else
{
gLogger = new NullLogger();
return;
}
gLogger = new ThreadSafeLogger(std::make_unique<PlainLogger>(LogLevel::All, std::move(outputStream)));
}
static void initRandomGenerator()
{
u64 seed = gTickCounter.GetValue() + 1430287ULL;
rtc_datetime_t dateTime;
rtc_readDateTime(&dateTime);
seed = seed * 7302013ULL + dateTime.date.year;
seed = seed * 7302013ULL + dateTime.date.month;
seed = seed * 7302013ULL + dateTime.date.monthDay;
seed = seed * 7302013ULL + dateTime.time.hour;
seed = seed * 7302013ULL + dateTime.time.minute;
seed = seed * 7302013ULL + dateTime.time.second;
gRandomGenerator = new ThreadSafeRandomGenerator<LinearCongruentialGenerator>(seed);
}
static bool mountDldi()
{
FRESULT res = f_mount(&gFatFs, "fat:", 1);
if (res != FR_OK)
{
LOG_ERROR("dldi mount failed: %d\n", res);
return false;
}
f_chdrive("fat:");
return true;
}
static bool mountDsiSd()
{
FRESULT res = f_mount(&gFatFs, "sd:", 1);
if (res != FR_OK)
{
LOG_ERROR("dsi sd mount failed: %d\n", res);
return false;
}
f_chdrive("sd:");
return true;
}
static bool mountSemihosting()
{
FRESULT res = f_mount(&gFatFs, "pc:", 1);
if (res != FR_OK)
{
LOG_ERROR("pc sd mount failed: %d\n", res);
return false;
}
f_chdrive("pc:");
return true;
}
static bool mountAgbSemihosting()
{
FRESULT res = f_mount(&gFatFs, "pc2:", 1);
if (res != FR_OK)
{
LOG_ERROR("pc2 sd mount failed: %d\n", res);
return false;
}
f_chdrive("pc2:");
return true;
}
static bool shouldMountDsiSd(int argc, char* argv[], bool dldiInitSuccessfull)
{
if (!Environment::IsDsiMode())
return false;
if (argc == 0)
return !dldiInitSuccessfull;
if (argv[0][0] == 'f' && argv[0][1] == 'a' && argv[0][2] == 't' && argv[0][3] == ':')
return false;
return true;
}
int main(int argc, char* argv[])
{
REG_MASTER_BRIGHT = 0x4010;
REG_MASTER_BRIGHT_SUB = 0x4010;
bool dldiInitSuccessful = false;
Environment::Initialize();
heap_init();
rtos_initIrq();
rtos_startMainThread();
// Insert bkpt #0 instruction in prefetch abort and data abort vectors for debugging purposes
*(vu32*)0x0100000C = 0xE1200070;
*(vu32*)0x01000010 = 0xE1200070;
ipc_initFifoSystem();
if (Environment::IsDsiMode())
dsisd_init();
VBlank::Init();
initLogger();
gTickCounter.Start();
while (ipc_getArm7SyncBits() != 7);
rtc_init();
memset(&gFatFs, 0, sizeof(gFatFs));
if (dldi_init())
{
mountDldi();
pload_setBootDrive(PLOAD_BOOT_DRIVE_DLDI);
dldiInitSuccessful = true;
}
LOG_DEBUG("ARM9 Start\n");
if (Environment::IsDsiMode())
LOG_DEBUG("Running in DSi mode\n");
if (Environment::IsIsNitroEmulator())
LOG_DEBUG("Running on IS-NITRO-EMULATOR\n");
if (Environment::SupportsJtagSemihosting())
LOG_DEBUG("JTAG semihosting supported\n");
if (Environment::SupportsAgbSemihosting())
LOG_DEBUG("AGB semihosting supported\n");
if (Environment::SupportsNocashPrint())
LOG_DEBUG("Nocash print supported\n");
if (Environment::HasPicoAgbAdapter())
LOG_DEBUG("Pico Agb Adapter connected\n");
if (shouldMountDsiSd(argc, argv, dldiInitSuccessful))
{
mountDsiSd();
pload_setBootDrive(PLOAD_BOOT_DRIVE_DSI_SD);
}
else if (Environment::SupportsAgbSemihosting())
{
mountAgbSemihosting();
pload_setBootDrive(PLOAD_BOOT_DRIVE_AGB_SEMIHOSTING);
}
else if (Environment::SupportsJtagSemihosting())
{
mountSemihosting();
}
initRandomGenerator();
// todo: make sure _pico folder exists
// maybe warn if important files are missing as well?
nft2_unpack((nft2_header_t*)NotoSansJP_Regular_10_nft2);
nft2_unpack((nft2_header_t*)NotoSansJP_Medium_10_nft2);
nft2_unpack((nft2_header_t*)NotoSansJP_Medium_11_nft2);
nft2_unpack((nft2_header_t*)NotoSansJP_Medium_7_5_nft2);
gProcessManager.Goto<App>();
gProcessManager.MainLoop();
return 0;
}