diff --git a/Makefile.ctr b/Makefile.ctr index d47cc66c25..a86a78b886 100644 --- a/Makefile.ctr +++ b/Makefile.ctr @@ -31,6 +31,12 @@ OBJ += ctr/ctr_system.o OBJ += ctr/ctr_memory.o OBJ += ctr/ctr_linear.o OBJ += ctr/gpu_old.o +OBJ += ctr/exec-3dsx/exec_cia.o \ + ctr/exec-3dsx/exec_3dsx.o \ + ctr/exec-3dsx/mini-hb-menu/launch.o \ + ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.o \ + ctr/exec-3dsx/mini-hb-menu/loaders/hax2.o \ + ctr/exec-3dsx/mini-hb-menu/loaders/ninjhax1.o ifeq ($(APP_BIG_TEXT_SECTION), 1) APP_USE_SVCHAX = 1 diff --git a/Makefile.ctr.salamander b/Makefile.ctr.salamander index f356cd9e78..39fb610a45 100644 --- a/Makefile.ctr.salamander +++ b/Makefile.ctr.salamander @@ -39,6 +39,13 @@ OBJ := ctr/ctr_system.o \ libretro-common/hash/rhash.o \ file_path_str.o \ verbosity.o + +OBJ += ctr/exec-3dsx/exec_cia.o \ + ctr/exec-3dsx/exec_3dsx.o \ + ctr/exec-3dsx/mini-hb-menu/launch.o \ + ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.o \ + ctr/exec-3dsx/mini-hb-menu/loaders/hax2.o \ + ctr/exec-3dsx/mini-hb-menu/loaders/ninjhax1.o ifeq ($(strip $(DEVKITPRO)),) $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitpro") diff --git a/ctr/exec-3dsx/exec_3dsx.c b/ctr/exec-3dsx/exec_3dsx.c new file mode 100644 index 0000000000..07595996e7 --- /dev/null +++ b/ctr/exec-3dsx/exec_3dsx.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +#include "mini-hb-menu/common.h" + + +extern const loaderFuncs_s loader_Ninjhax1; +extern const loaderFuncs_s loader_Ninjhax2; +extern const loaderFuncs_s loader_Rosalina; + +static argData_s newProgramArgs;;//the argv variable must remain in memory even when the application exits for the new program to load properly + + +int exec_3dsx(const char* path, const char* args){ + struct stat sBuff; + bool fileExists; + bool inited; + + if(path == NULL || path[0] == '\0'){ + errno = EINVAL; + return -1; + } + + fileExists = stat(path, &sBuff) == 0; + if(!fileExists){ + errno = ENOENT; + return -1; + } + else if(S_ISDIR(sBuff.st_mode)){ + errno = EINVAL;; + return -1; + } + + if(args == NULL || args[0] == '\0') + args = path; + + int argsSize = strlen(args); + strncpy((char*)newProgramArgs.buf , args, ENTRY_ARGBUFSIZE); + if(argsSize >= ENTRY_ARGBUFSIZE) + ((char*)&newProgramArgs.buf[0])[ENTRY_ARGBUFSIZE - 1] = '\0'; + newProgramArgs.dst = (char*)newProgramArgs.buf + (argsSize < ENTRY_ARGBUFSIZE ? argsSize : ENTRY_ARGBUFSIZE); + + inited = loader_Rosalina.init(); + if(inited){ + loader_Rosalina.launchFile(path, &newProgramArgs, NULL); + exit(0); + } + + inited = loader_Ninjhax2.init(); + if(inited){ + loader_Ninjhax2.launchFile(path, &newProgramArgs, NULL); + exit(0); + } + + inited = loader_Ninjhax1.init(); + if(inited){ + loader_Ninjhax1.launchFile(path, &newProgramArgs, NULL); + exit(0); + } + + //should never be reached + errno = ENOTSUP; + return -1; +} \ No newline at end of file diff --git a/ctr/exec-3dsx/exec_3dsx.h b/ctr/exec-3dsx/exec_3dsx.h new file mode 100644 index 0000000000..3aa5b6a98b --- /dev/null +++ b/ctr/exec-3dsx/exec_3dsx.h @@ -0,0 +1 @@ +int exec_3dsx(const char* path, const char* args); diff --git a/ctr/exec-3dsx/exec_cia.c b/ctr/exec-3dsx/exec_cia.c new file mode 100644 index 0000000000..428fd1c293 --- /dev/null +++ b/ctr/exec-3dsx/exec_cia.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include <3ds.h> + + +#define FILE_CHUNK_SIZE 4096 + + +typedef struct{ + u32 argc; + char args[0x300 - 0x4]; +}ciaParam; + + +static int isCiaInstalled(u64 titleId){ + u32 titlesToRetrieve; + u32 titlesRetrieved; + u64* titleIds; + Result failed; + + failed = AM_GetTitleCount(MEDIATYPE_SD, &titlesToRetrieve); + if(R_FAILED(failed)) + return -1; + + titleIds = malloc(titlesToRetrieve * sizeof(uint64_t)); + if(titleIds == NULL) + return -1; + + failed = AM_GetTitleList(&titlesRetrieved, MEDIATYPE_SD, titlesToRetrieve, titleIds); + if(R_FAILED(failed)) + return -1; + + for(u32 titlesToCheck = 0; titlesToCheck < titlesRetrieved; titlesToCheck++){ + if(titleIds[titlesToCheck] == titleId){ + free(titleIds); + return 1; + } + } + + free(titleIds); + return 0; +} + +static int installCia(Handle ciaFile){ + Result failed; + Handle outputHandle; + u64 fileSize; + u64 fileOffset = 0; + u32 bytesRead; + u32 bytesWritten; + u8 transferBuffer[FILE_CHUNK_SIZE]; + + failed = AM_StartCiaInstall(MEDIATYPE_SD, &outputHandle); + if(R_FAILED(failed)) + return -1; + + failed = FSFILE_GetSize(ciaFile, &fileSize); + if(R_FAILED(failed)) + return -1; + + while(fileOffset < fileSize){ + u64 bytesRemaining = fileSize - fileOffset; + failed = FSFILE_Read(ciaFile, &bytesRead, fileOffset, transferBuffer, bytesRemaining < FILE_CHUNK_SIZE ? bytesRemaining : FILE_CHUNK_SIZE); + if(R_FAILED(failed)){ + AM_CancelCIAInstall(outputHandle); + return -1; + } + + failed = FSFILE_Write(outputHandle, &bytesWritten, fileOffset, transferBuffer, bytesRead, 0); + if(R_FAILED(failed)){ + AM_CancelCIAInstall(outputHandle); + return -1; + } + + if(bytesWritten != bytesRead){ + AM_CancelCIAInstall(outputHandle); + return -1; + } + + fileOffset += bytesWritten; + } + + failed = AM_FinishCiaInstall(outputHandle); + if(R_FAILED(failed)) + return -1; + + return 1; +} + +static u64 getCiaTitleId(Handle ciaFile){ + Result failed; + AM_TitleEntry ciaInfo; + + failed = AM_GetCiaFileInfo(MEDIATYPE_SD, &ciaInfo, ciaFile); + if(R_FAILED(failed)) + return 0x0000000000000000; + + return ciaInfo.titleID; +} + +static void errorAndQuit(const char* errorStr){ + errorConf error; + + errorInit(&error, ERROR_TEXT, CFG_LANGUAGE_EN); + errorText(&error, errorStr); + errorDisp(&error); + exit(0); +} + +int exec_cia(const char* path, const char* args){ + struct stat sBuff; + bool fileExists; + bool inited; + + if(path == NULL || path[0] == '\0'){ + errno = EINVAL; + return -1; + } + + fileExists = stat(path, &sBuff) == 0; + if(!fileExists){ + errno = ENOENT; + return -1; + } + else if(S_ISDIR(sBuff.st_mode)){ + errno = EINVAL;; + return -1; + } + + inited = R_SUCCEEDED(amInit()) && R_SUCCEEDED(fsInit()); + if(inited){ + Result res; + FS_Archive ciaArchive; + Handle ciaFile; + u64 titleId; + int ciaInstalled; + ciaParam param; + int argsLength; + extern char __argv_hmac[0x20]; + + //open cia file + res = FSUSER_OpenArchive(&ciaArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")); + if(R_FAILED(res)) + errorAndQuit("Cant open SD FS archive."); + + res = FSUSER_OpenFile(&ciaFile, ciaArchive, fsMakePath(PATH_ASCII, path + 5/*skip "sdmc:"*/), FS_OPEN_READ, 0); + if(R_FAILED(res)) + errorAndQuit("Cant open CIA file."); + + titleId = getCiaTitleId(ciaFile); + if(titleId == 0x0000000000000000) + errorAndQuit("Cant get CIA file title id."); + + ciaInstalled = isCiaInstalled(titleId); + if(ciaInstalled == -1){ + //error + errorAndQuit("Could not read title id list."); + } + else if(ciaInstalled == 0){ + //not installed + int error = installCia(ciaFile); + + if(error == -1) + errorAndQuit("Cant install CIA."); + } + + FSFILE_Close(ciaFile); + FSUSER_CloseArchive(ciaArchive); + + if(args == NULL || args[0] == '\0'){ + param.argc = 0; + argsLength = 0; + } + else{ + bool inSingleQuotes = false; + bool inDoubleQuotes = false; + int argStringLength = strlen(args); + int argPtr; + param.argc = 0; + argsLength = 0; + + //build argument list like a terminal command on linux + for(unsigned int argPtr = 0; argPtr < argStringLength; argPtr++){ + if(args[argPtr] == '\'') + inSingleQuotes = !inSingleQuotes; + else if(args[argPtr] == '\"') + inDoubleQuotes = !inDoubleQuotes; + else{ + if(!inSingleQuotes && !inDoubleQuotes && args[argPtr] == ' '){ + param.argc++; + param.args[argsLength] = '\0'; + } + else{ + param.args[argsLength] = args[argPtr]; + } + + argsLength++; + } + } + } + + res = APT_PrepareToDoApplicationJump(0, titleId, 0x1); + if(R_SUCCEEDED(res)) + res = APT_DoApplicationJump(¶m, sizeof(param.argc) + argsLength, __argv_hmac); + } + + //should never be reached + amExit(); + fsExit(); + errno = ENOTSUP; + return -1; +} diff --git a/ctr/exec-3dsx/exec_cia.h b/ctr/exec-3dsx/exec_cia.h new file mode 100644 index 0000000000..4544fb94ef --- /dev/null +++ b/ctr/exec-3dsx/exec_cia.h @@ -0,0 +1 @@ +int exec_cia(const char* path, const char* args); diff --git a/ctr/exec-3dsx/mini-hb-menu/common.h b/ctr/exec-3dsx/mini-hb-menu/common.h new file mode 100644 index 0000000000..633a297f9d --- /dev/null +++ b/ctr/exec-3dsx/mini-hb-menu/common.h @@ -0,0 +1,95 @@ +#pragma once + +// C stdlib includes +#include +#include +#include +#include +#include +#include +#include +#include + +// 3DS includes +#include <3ds.h> + + +#define ENTRY_ARGBUFSIZE 0x400 +#define NUM_SERVICESTHATMATTER 5 + +typedef enum +{ + StrId_Loading = 0, + StrId_Directory, + StrId_DefaultLongTitle, + StrId_DefaultPublisher, + StrId_IOError, + StrId_CouldNotOpenFile, + + StrId_NoAppsFound_Title, + StrId_NoAppsFound_Msg, + + StrId_Reboot, + StrId_ReturnToHome, + + StrId_TitleSelector, + StrId_ErrorReadingTitleMetadata, + StrId_NoTitlesFound, + StrId_SelectTitle, + + StrId_NoTargetTitleSupport, + StrId_MissingTargetTitle, + + StrId_NetLoader, + StrId_NetLoaderUnavailable, + StrId_NetLoaderOffline, + StrId_NetLoaderError, + StrId_NetLoaderActive, + StrId_NetLoaderTransferring, + + StrId_Max, +} StrId; + + +typedef struct +{ + char* dst; + u32 buf[ENTRY_ARGBUFSIZE/sizeof(u32)]; +} argData_s; + +typedef struct +{ + bool scanned; + u32 sectionSizes[3]; + bool servicesThatMatter[NUM_SERVICESTHATMATTER]; +} executableMetadata_s; + +typedef struct +{ + u32 num; + u32 text_end; + u32 data_address; + u32 data_size; + u32 processLinearOffset; + u32 processHookAddress; + u32 processAppCodeAddress; + u32 processHookTidLow, processHookTidHigh; + u32 mediatype; + bool capabilities[0x10]; // {socuAccess, csndAccess, qtmAccess, nfcAccess, httpcAccess, reserved...} +} memmap_header_t; + +typedef struct +{ + u32 src, dst, size; +} memmap_entry_t; + +typedef struct +{ + memmap_header_t header; + memmap_entry_t map[]; +} memmap_t; + +#define memmapSize(m) (sizeof(memmap_header_t) + sizeof(memmap_entry_t)*(m)->header.num) + + +#include "launch.h" diff --git a/ctr/exec-3dsx/mini-hb-menu/launch.c b/ctr/exec-3dsx/mini-hb-menu/launch.c new file mode 100644 index 0000000000..e5adf30581 --- /dev/null +++ b/ctr/exec-3dsx/mini-hb-menu/launch.c @@ -0,0 +1,22 @@ +#include "common.h" + +Handle launchOpenFile(const char* path) +{ + if (strncmp(path, "sdmc:/", 6) == 0) + path += 5; + else if (*path != '/') + return 0; + + // Convert the executable path to UTF-16 + static uint16_t __utf16path[PATH_MAX+1]; + ssize_t units = utf8_to_utf16(__utf16path, (const uint8_t*)path, PATH_MAX); + if (units < 0 || units >= PATH_MAX) return 0; + __utf16path[units] = 0; + + // Open the file directly + FS_Path apath = { PATH_EMPTY, 1, (u8*)"" }; + FS_Path fpath = { PATH_UTF16, (units+1)*2, (u8*)__utf16path }; + Handle file; + Result res = FSUSER_OpenFileDirectly(&file, ARCHIVE_SDMC, apath, fpath, FS_OPEN_READ, 0); + return R_SUCCEEDED(res) ? file : 0; +} diff --git a/ctr/exec-3dsx/mini-hb-menu/launch.h b/ctr/exec-3dsx/mini-hb-menu/launch.h new file mode 100644 index 0000000000..feace6428e --- /dev/null +++ b/ctr/exec-3dsx/mini-hb-menu/launch.h @@ -0,0 +1,24 @@ +#include "common.h" + +extern void (*__system_retAddr)(void); + +enum +{ + LOADER_SHOW_REBOOT = 0x01, + LOADER_NEED_SCAN = 0x02 +}; + +typedef struct +{ + // Mandatory fields + const char* name; + u32 flags; + bool (* init)(void); + void (* deinit)(void); + void (* launchFile)(const char* path, argData_s* args, executableMetadata_s* em); + + // Optional fields + void (* useTitle)(u64 tid, u8 mediatype); +} loaderFuncs_s; + +Handle launchOpenFile(const char* path); \ No newline at end of file diff --git a/ctr/exec-3dsx/mini-hb-menu/loaders/hax2.c b/ctr/exec-3dsx/mini-hb-menu/loaders/hax2.c new file mode 100644 index 0000000000..08522f3b7f --- /dev/null +++ b/ctr/exec-3dsx/mini-hb-menu/loaders/hax2.c @@ -0,0 +1,73 @@ +#include "../common.h" + +typedef struct +{ + s32 processId; + bool capabilities[0x10]; +} processEntry_s; + +typedef void (*callBootloader_2x_fn)(Handle file, u32* argbuf, u32 arglength); +typedef void (*callBootloaderNewProcess_2x_fn)(s32 processId, u32* argbuf, u32 arglength); +typedef void (*callBootloaderRunTitle_2x_fn)(u8 mediatype, u32* argbuf, u32 argbuflength, u32 tid_low, u32 tid_high); +typedef void (*callBootloaderRunTitleCustom_2x_fn)(u8 mediatype, u32* argbuf, u32 argbuflength, u32 tid_low, u32 tid_high, memmap_t* mmap); +typedef void (*getBestProcess_2x_fn)(u32 sectionSizes[3], bool* requirements, int num_requirements, processEntry_s* out, int out_size, int* out_len); + +#define callBootloader_2x ((callBootloader_2x_fn)0x00100000) +#define callBootloaderNewProcess_2x ((callBootloaderNewProcess_2x_fn)0x00100008) +#define callBootloaderRunTitle_2x ((callBootloaderRunTitle_2x_fn)0x00100010) +#define callBootloaderRunTitleCustom_2x ((callBootloaderRunTitleCustom_2x_fn)0x00100014) +#define getBestProcess_2x ((getBestProcess_2x_fn)0x0010000C) + +static s32 targetProcess = -1; +static u64 targetTid; +static u8 targetMediatype; +static Handle fileHandle; +static u32 argBuf[ENTRY_ARGBUFSIZE/sizeof(u32)]; +static u32 argBufLen; +static u32 memMapBuf[0x40]; +static bool useMemMap; + +static bool init(void) +{ + return R_SUCCEEDED(amInit()); +} + +static void deinit(void) +{ + amExit(); +} + +static void bootloaderJump(void) +{ + if (targetProcess == -1) + callBootloader_2x(fileHandle, argBuf, argBufLen); + else if (targetProcess == -2) + { + if (useMemMap) + callBootloaderRunTitleCustom_2x(targetMediatype, argBuf, argBufLen, (u32)targetTid, (u32)(targetTid>>32), (memmap_t*)memMapBuf); + else + callBootloaderRunTitle_2x(targetMediatype, argBuf, argBufLen, (u32)targetTid, (u32)(targetTid>>32)); + } + else + callBootloaderNewProcess_2x(targetProcess, argBuf, argBufLen); +} + +static void launchFile(const char* path, argData_s* args, executableMetadata_s* em) +{ + fileHandle = launchOpenFile(path); + if (fileHandle==0) + return; + argBufLen = args->dst - (char*)args->buf; + memcpy(argBuf, args->buf, argBufLen); + __system_retAddr = bootloaderJump; +} + +const loaderFuncs_s loader_Ninjhax2 = +{ + .name = "hax 2.x", + .flags = LOADER_SHOW_REBOOT | LOADER_NEED_SCAN, + .init = init, + .deinit = deinit, + .launchFile = launchFile, + //.useTitle = useTitle, +}; diff --git a/ctr/exec-3dsx/mini-hb-menu/loaders/ninjhax1.c b/ctr/exec-3dsx/mini-hb-menu/loaders/ninjhax1.c new file mode 100644 index 0000000000..8fd05663d3 --- /dev/null +++ b/ctr/exec-3dsx/mini-hb-menu/loaders/ninjhax1.c @@ -0,0 +1,43 @@ +#include "../common.h" + +static void (*callBootloader_1x)(Handle hb, Handle file); +static void (*setArgs_1x)(u32* src, u32 length); +static Handle fileHandle; + +static bool init(void) +{ + Result res = hbInit(); + if (R_FAILED(res)) + return false; + + HB_GetBootloaderAddresses((void**)&callBootloader_1x, (void**)&setArgs_1x); + return true; +} + +static void deinit(void) +{ + hbExit(); +} + +static void bootloaderJump(void) +{ + callBootloader_1x(0x00000000, fileHandle); +} + +static void launchFile(const char* path, argData_s* args, executableMetadata_s* em) +{ + fileHandle = launchOpenFile(path); + if (fileHandle==0) + return; + setArgs_1x(args->buf, sizeof(args->buf)); + __system_retAddr = bootloaderJump; +} + +const loaderFuncs_s loader_Ninjhax1 = +{ + .name = "ninjhax 1.x", + .flags = LOADER_SHOW_REBOOT, + .init = init, + .deinit = deinit, + .launchFile = launchFile, +}; diff --git a/ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.c b/ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.c new file mode 100644 index 0000000000..fd9046e8fa --- /dev/null +++ b/ctr/exec-3dsx/mini-hb-menu/loaders/rosalina.c @@ -0,0 +1,56 @@ +#include "../common.h" + +static Handle hbldrHandle; + +static bool init(void) +{ + return R_SUCCEEDED(svcConnectToPort(&hbldrHandle, "hb:ldr")); +} + +static Result HBLDR_SetTarget(const char* path) +{ + u32 pathLen = strlen(path) + 1; + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(2, 0, 2); //0x20002 + cmdbuf[1] = IPC_Desc_StaticBuffer(pathLen, 0); + cmdbuf[2] = (u32)path; + + Result rc = svcSendSyncRequest(hbldrHandle); + if (R_SUCCEEDED(rc)) rc = cmdbuf[1]; + return rc; +} + +static Result HBLDR_SetArgv(const void* buffer, u32 size) +{ + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(3, 0, 2); //0x30002 + cmdbuf[1] = IPC_Desc_StaticBuffer(size, 1); + cmdbuf[2] = (u32)buffer; + + Result rc = svcSendSyncRequest(hbldrHandle); + if (R_SUCCEEDED(rc)) rc = cmdbuf[1]; + return rc; +} + +static void deinit(void) +{ + svcCloseHandle(hbldrHandle); +} + +static void launchFile(const char* path, argData_s* args, executableMetadata_s* em) +{ + if (strncmp(path, "sdmc:/",6) == 0) + path += 5; + HBLDR_SetTarget(path); + HBLDR_SetArgv(args->buf, sizeof(args->buf)); +} + +const loaderFuncs_s loader_Rosalina = +{ + .name = "Rosalina", + .init = init, + .deinit = deinit, + .launchFile = launchFile, +}; diff --git a/frontend/drivers/platform_ctr.c b/frontend/drivers/platform_ctr.c index 8f376c97fe..917d92bb00 100644 --- a/frontend/drivers/platform_ctr.c +++ b/frontend/drivers/platform_ctr.c @@ -1,564 +1,570 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2014-2017 - Ali Bouhlel - * Copyright (C) 2011-2017 - Daniel De Matteis - * - * RetroArch 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch 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. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include -#include -#include -#include -#include - -#include <3ds.h> -#include <3ds/svc.h> -#include <3ds/os.h> -#include <3ds/services/cfgu.h> -#include <3ds/services/ptmu.h> -#include <3ds/services/mcuhwc.h> - -#include - -#ifdef HAVE_CONFIG_H -#include "../../config.h" -#endif - -#ifndef IS_SALAMANDER -#include -#endif - -#include "../frontend_driver.h" -#include "../../verbosity.h" -#include "../../defaults.h" -#include "../../paths.h" -#include "retroarch.h" -#include "file_path_special.h" -#include "audio/audio_driver.h" - -#include "ctr/ctr_debug.h" - -#ifndef IS_SALAMANDER -#ifdef HAVE_MENU -#include "../../menu/menu_driver.h" -#endif -#endif - -static enum frontend_fork ctr_fork_mode = FRONTEND_FORK_NONE; -static const char* elf_path_cst = "sdmc:/retroarch/test.3dsx"; - -static void frontend_ctr_get_environment_settings(int *argc, char *argv[], - void *args, void *params_data) -{ - (void)args; - -#ifndef IS_SALAMANDER -#if defined(HAVE_LOGGER) - logger_init(); -#elif defined(HAVE_FILE_LOGGER) - retro_main_log_file_init("sdmc:/retroarch/retroarch-log.txt"); -#endif -#endif - - fill_pathname_basedir(g_defaults.dirs[DEFAULT_DIR_PORT], elf_path_cst, sizeof(g_defaults.dirs[DEFAULT_DIR_PORT])); - RARCH_LOG("port dir: [%s]\n", g_defaults.dirs[DEFAULT_DIR_PORT]); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT], - "downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT], - "media", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], g_defaults.dirs[DEFAULT_DIR_PORT], - "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], g_defaults.dirs[DEFAULT_DIR_CORE], - "info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], g_defaults.dirs[DEFAULT_DIR_CORE], - "savestates", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM], g_defaults.dirs[DEFAULT_DIR_CORE], - "savefiles", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], g_defaults.dirs[DEFAULT_DIR_CORE], - "system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], g_defaults.dirs[DEFAULT_DIR_CORE], - "playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], g_defaults.dirs[DEFAULT_DIR_PORT], - "config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP], g_defaults.dirs[DEFAULT_DIR_PORT], - "config/remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], g_defaults.dirs[DEFAULT_DIR_PORT], - "filters", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE], g_defaults.dirs[DEFAULT_DIR_PORT], - "database/rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE])); - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CURSOR], g_defaults.dirs[DEFAULT_DIR_PORT], - "database/cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR])); - fill_pathname_join(g_defaults.path.config, g_defaults.dirs[DEFAULT_DIR_PORT], - file_path_str(FILE_PATH_MAIN_CONFIG), sizeof(g_defaults.path.config)); -} - -static void frontend_ctr_deinit(void *data) -{ - Handle lcd_handle; - u32 parallax_layer_reg_state; - u8 not_2DS; - - extern PrintConsole* currentConsole; - - (void)data; - -#ifndef IS_SALAMANDER - verbosity_enable(); - -#ifdef HAVE_FILE_LOGGER - command_event(CMD_EVENT_LOG_FILE_DEINIT, NULL); -#endif - - if((gfxBottomFramebuffers[0] == (u8*)currentConsole->frameBuffer) - && (ctr_fork_mode == FRONTEND_FORK_NONE)) - wait_for_input(); - - CFGU_GetModelNintendo2DS(¬_2DS); - - if(not_2DS && srvGetServiceHandle(&lcd_handle, "gsp::Lcd") >= 0) - { - u32 *cmdbuf = getThreadCommandBuffer(); - cmdbuf[0] = 0x00110040; - cmdbuf[1] = 2; - svcSendSyncRequest(lcd_handle); - svcCloseHandle(lcd_handle); - } - - parallax_layer_reg_state = (*(float*)0x1FF81080 == 0.0)? 0x0 : 0x00010001; - GSPGPU_WriteHWRegs(0x202000, ¶llax_layer_reg_state, 4); - - mcuHwcExit(); - ptmuExit(); - cfguExit(); - ndspExit(); - csndExit(); - gfxTopRightFramebuffers[0] = NULL; - gfxTopRightFramebuffers[1] = NULL; - gfxExit(); -#endif -} - -static void frontend_ctr_exec(const char *path, bool should_load_game) -{ - struct - { - u32 argc; - char args[0x300 - 0x4]; - }param; - int len; - uint64_t app_ID; - Result res; - extern char __argv_hmac[0x20]; - - DEBUG_VAR(path); - DEBUG_STR(path); - - strlcpy(param.args, elf_path_cst, sizeof(param.args)); - len = strlen(param.args) + 1; - param.argc = 1; - - RARCH_LOG("Attempt to load core: [%s].\n", path); -#ifndef IS_SALAMANDER - if (should_load_game && !path_is_empty(RARCH_PATH_CONTENT)) - { - strlcpy(param.args + len, path_get(RARCH_PATH_CONTENT), sizeof(param.args) - len); - len += strlen(param.args + len) + 1; - param.argc++; - RARCH_LOG("content path: [%s].\n", path_get(RARCH_PATH_CONTENT)); - } -#endif - if(!path || !*path) - { - APT_GetProgramID(&app_ID); - RARCH_LOG("APP_ID 0x%016llX.\n", app_ID); - } - else - { - char app_ID_str[11]; - u32 app_ID_low = 0; - FILE* fp = fopen(path, "rb"); - size_t bytes_read = fread(app_ID_str, 1, sizeof(app_ID_str), fp); - - fclose(fp); - - if(bytes_read <= 0) - { - RARCH_LOG("error reading APP_ID from: [%s].\n", path); - return; - } - app_ID_str[bytes_read] = '\0'; - sscanf(app_ID_str, "0x%x", &app_ID_low); - app_ID_low <<= 8; - app_ID = 0x0004000000000000ULL | app_ID_low; - RARCH_LOG("APP_ID [%s] -- > 0x%016llX.\n", app_ID_str, app_ID); - } - - res = APT_PrepareToDoApplicationJump(0, app_ID, 0x1); - if(R_SUCCEEDED(res)) - res = APT_DoApplicationJump(¶m, sizeof(param.argc) + len, __argv_hmac); - - if(res) - { - RARCH_ERR("Failed to load core\n"); - dump_result_value(res); - } - - svcSleepThread(INT64_MAX); -} - -#ifndef IS_SALAMANDER -static bool frontend_ctr_set_fork(enum frontend_fork fork_mode) -{ - switch (fork_mode) - { - case FRONTEND_FORK_CORE: - RARCH_LOG("FRONTEND_FORK_CORE\n"); - ctr_fork_mode = fork_mode; - break; - case FRONTEND_FORK_CORE_WITH_ARGS: - RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n"); - ctr_fork_mode = fork_mode; - break; - case FRONTEND_FORK_RESTART: - RARCH_LOG("FRONTEND_FORK_RESTART\n"); - /* NOTE: We don't implement Salamander, so just turn - * this into FRONTEND_FORK_CORE. */ - ctr_fork_mode = FRONTEND_FORK_CORE; - break; - case FRONTEND_FORK_NONE: - default: - return false; - } - - return true; -} -#endif - -static void frontend_ctr_exitspawn(char *s, size_t len) -{ - bool should_load_game = false; -#ifndef IS_SALAMANDER - if (ctr_fork_mode == FRONTEND_FORK_NONE) - return; - - switch (ctr_fork_mode) - { - case FRONTEND_FORK_CORE_WITH_ARGS: - should_load_game = true; - break; - default: - break; - } -#endif - frontend_ctr_exec(s, should_load_game); -} - -static void frontend_ctr_shutdown(bool unused) -{ - (void)unused; -} - -static void ctr_check_dspfirm(void) -{ - FILE* dsp_fp = fopen("sdmc:/3ds/dspfirm.cdc", "rb"); - - if(dsp_fp) - fclose(dsp_fp); - else - { - size_t code_size; - uint32_t* code_buffer = NULL; - uint32_t* ptr = NULL; - const uint32_t dsp1_magic = 0x31505344; /* "DSP1" */ - FILE *code_fp = fopen("sdmc:/3ds/code.bin", "rb"); - - if(code_fp) - { - fseek(code_fp, 0, SEEK_END); - code_size = ftell(code_fp); - fseek(code_fp, 0, SEEK_SET); - - code_buffer = (uint32_t*) malloc(code_size); - if(code_buffer) - { - fread(code_buffer, 1, code_size, code_fp); - - for (ptr = code_buffer + 0x40; ptr < (code_buffer + (code_size >> 2)); ptr++) - { - if (*ptr == dsp1_magic) - { - size_t dspfirm_size = ptr[1]; - ptr -= 0x40; - if ((ptr + (dspfirm_size >> 2)) > (code_buffer + (code_size >> 2))) - break; - - dsp_fp = fopen("sdmc:/3ds/dspfirm.cdc", "wb"); - if(!dsp_fp) - break; - fwrite(ptr, 1, dspfirm_size, dsp_fp); - fclose(dsp_fp); - break; - } - } - free(code_buffer); - } - fclose(code_fp); - } - } -} - -__attribute__((weak)) Result svchax_init(bool patch_srv); -__attribute__((weak)) u32 __ctr_patch_services; - -void gfxSetFramebufferInfo(gfxScreen_t screen, u8 id); - -static void frontend_ctr_init(void *data) -{ -#ifndef IS_SALAMANDER - (void)data; - - extern void* __service_ptr; - if (__service_ptr) - { - frontend_ctx_ctr.exec = NULL; - frontend_ctx_ctr.exitspawn = NULL; - frontend_ctx_ctr.set_fork = NULL; - } - - verbosity_enable(); - - gfxInit(GSP_BGR8_OES,GSP_RGB565_OES,false); - - u32 topSize = 400 * 240 * 3; - u32 bottomSize = 320 * 240 * 2; - linearFree(gfxTopLeftFramebuffers[0]); - linearFree(gfxTopLeftFramebuffers[1]); - linearFree(gfxBottomFramebuffers[0]); - linearFree(gfxBottomFramebuffers[1]); - linearFree(gfxTopRightFramebuffers[0]); - linearFree(gfxTopRightFramebuffers[1]); - - gfxTopLeftFramebuffers[0]=linearAlloc(topSize * 2); - gfxTopRightFramebuffers[0] = gfxTopLeftFramebuffers[0] + topSize; - - gfxTopLeftFramebuffers[1]=linearAlloc(topSize * 2); - gfxTopRightFramebuffers[1] = gfxTopLeftFramebuffers[1] + topSize; - - gfxBottomFramebuffers[0]=linearAlloc(bottomSize); - gfxBottomFramebuffers[1]=linearAlloc(bottomSize); - - gfxSetFramebufferInfo(GFX_TOP, 0); - gfxSetFramebufferInfo(GFX_BOTTOM, 0); - - gfxSet3D(true); - consoleInit(GFX_BOTTOM, NULL); - - /* enable access to all service calls when possible. */ - if(svchax_init) - { - osSetSpeedupEnable(false); - svchax_init(__ctr_patch_services); - } - osSetSpeedupEnable(true); - - if(csndInit() != 0) - audio_ctr_csnd = audio_null; - ctr_check_dspfirm(); - if(ndspInit() != 0) - audio_ctr_dsp = audio_null; - cfguInit(); - ptmuInit(); - mcuHwcInit(); -#endif -} - - -static int frontend_ctr_get_rating(void) -{ - u8 device_model = 0xFF; - CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/ - - switch (device_model) - { - case 0: - case 1: - case 3: - /*Old 3/2DS*/ - return 3; - - case 2: - case 4: - case 5: - /*New 3/2DS*/ - return 6; - - default: - /*Unknown Device Or Check Failed*/ - break; - } - - return -1; -} - -enum frontend_architecture frontend_ctr_get_architecture(void) -{ - return FRONTEND_ARCH_ARM; -} - -static int frontend_ctr_parse_drive_list(void *data, bool load_content) -{ -#ifndef IS_SALAMANDER - file_list_t *list = (file_list_t*)data; - enum msg_hash_enums enum_idx = load_content ? - MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR : - MSG_UNKNOWN; - - if (!list) - return -1; - - menu_entries_append_enum(list, - "sdmc:/", - msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR), - enum_idx, - FILE_TYPE_DIRECTORY, 0, 0); -#endif - - return 0; -} - -static uint64_t frontend_ctr_get_mem_total(void) -{ - return osGetMemRegionSize(MEMREGION_ALL); -} - -static uint64_t frontend_ctr_get_mem_used(void) -{ - return osGetMemRegionUsed(MEMREGION_ALL); -} - -static enum frontend_powerstate frontend_ctr_get_powerstate(int *seconds, int *percent) -{ - u8 battery_percent = 0; - u8 charging = 0; - enum frontend_powerstate pwr_state = FRONTEND_POWERSTATE_NONE; - - mcuHwcGetBatteryLevel(&battery_percent); - *percent = battery_percent; - - /* 3ds does not support seconds of charge remaining */ - *seconds = -1; - - PTMU_GetBatteryChargeState(&charging); - if (charging) - { - if (battery_percent == 100) - { - pwr_state = FRONTEND_POWERSTATE_CHARGED; - } - else - { - pwr_state = FRONTEND_POWERSTATE_CHARGING; - } - } - else - { - pwr_state = FRONTEND_POWERSTATE_ON_POWER_SOURCE; - } - - return pwr_state; -} - -static void frontend_ctr_get_os(char *s, size_t len, int *major, int *minor) -{ - OS_VersionBin cver; - OS_VersionBin nver; - - strlcpy(s, "3DS OS", len); - Result data_invalid = osGetSystemVersionData(&nver, &cver); - if (data_invalid == 0) - { - *major = cver.mainver; - *minor = cver.minor; - } - else - { - *major = 0; - *minor = 0; - } - -} - -static void frontend_ctr_get_name(char *s, size_t len) -{ - u8 device_model = 0xFF; - CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/ - - switch (device_model) - { - case 0: - strlcpy(s, "Old 3DS", len); - break; - case 1: - strlcpy(s, "Old 3DS XL", len); - break; - case 2: - strlcpy(s, "New 3DS", len); - break; - case 3: - strlcpy(s, "Old 2DS", len); - break; - case 4: - strlcpy(s, "New 3DS XL", len); - break; - case 5: - strlcpy(s, "New 2DS XL", len); - break; - - default: - strlcpy(s, "Unknown Device", len); - break; - } -} - -frontend_ctx_driver_t frontend_ctx_ctr = { - frontend_ctr_get_environment_settings, - frontend_ctr_init, - frontend_ctr_deinit, - frontend_ctr_exitspawn, - NULL, /* process_args */ - frontend_ctr_exec, -#ifdef IS_SALAMANDER - NULL, -#else - frontend_ctr_set_fork, -#endif - frontend_ctr_shutdown, - frontend_ctr_get_name, - frontend_ctr_get_os, - frontend_ctr_get_rating, - NULL, /* load_content */ - frontend_ctr_get_architecture, - frontend_ctr_get_powerstate, - frontend_ctr_parse_drive_list, - frontend_ctr_get_mem_total, - frontend_ctr_get_mem_used, - NULL, /* install_signal_handler */ - NULL, /* get_signal_handler_state */ - NULL, /* set_signal_handler_state */ - NULL, /* destroy_signal_handler_state */ - NULL, /* attach_console */ - NULL, /* detach_console */ - NULL, /* watch_path_for_changes */ - NULL, /* check_for_path_changes */ - "ctr", -}; +/* RetroArch - A frontend for libretro. + * Copyright (C) 2014-2017 - Ali Bouhlel + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include <3ds.h> +#include <3ds/svc.h> +#include <3ds/os.h> +#include <3ds/services/cfgu.h> +#include <3ds/services/ptmu.h> +#include <3ds/services/mcuhwc.h> + +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#ifndef IS_SALAMANDER +#include +#endif + +#include "../frontend_driver.h" +#include "../../verbosity.h" +#include "../../defaults.h" +#include "../../paths.h" +#include "retroarch.h" +#include "file_path_special.h" +#include "audio/audio_driver.h" + +#include "ctr/ctr_debug.h" +#include "ctr/exec-3dsx/exec_3dsx.h" +#include "ctr/exec-3dsx/exec_cia.h" + +#ifndef IS_SALAMANDER +#ifdef HAVE_MENU +#include "../../menu/menu_driver.h" +#endif +#endif + +static enum frontend_fork ctr_fork_mode = FRONTEND_FORK_NONE; +static const char* elf_path_cst = "sdmc:/retroarch/retroarch.3dsx"; + +static void get_first_valid_core(char* path_return) +{ + DIR* dir; + struct dirent* ent; + const char* extension = envIsHomebrew() ? "3dsx" : "cia"; + + path_return[0] = '\0'; + + dir = opendir("sdmc:/retroarch/cores"); + if (dir != NULL) + { + while (ent = readdir(dir)) + { + if (ent == NULL) + break; + if (strlen(ent->d_name) > strlen(extension) && !strcmp(ent->d_name + strlen(ent->d_name) - strlen(extension), extension)) + { + strcpy(path_return, "sdmc:/retroarch/cores"); + strcat(path_return, "/"); + strcat(path_return, ent->d_name); + break; + } + } + closedir(dir); + } +} + +static void frontend_ctr_get_environment_settings(int* argc, char* argv[], + void* args, void* params_data) +{ + (void)args; + +#ifndef IS_SALAMANDER +#if defined(HAVE_LOGGER) + logger_init(); +#elif defined(HAVE_FILE_LOGGER) + retro_main_log_file_init("sdmc:/retroarch/retroarch-log.txt"); +#endif +#endif + + fill_pathname_basedir(g_defaults.dirs[DEFAULT_DIR_PORT], elf_path_cst, sizeof(g_defaults.dirs[DEFAULT_DIR_PORT])); + RARCH_LOG("port dir: [%s]\n", g_defaults.dirs[DEFAULT_DIR_PORT]); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT], + "downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT], + "media", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], g_defaults.dirs[DEFAULT_DIR_PORT], + "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], g_defaults.dirs[DEFAULT_DIR_CORE], + "info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], g_defaults.dirs[DEFAULT_DIR_CORE], + "savestates", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM], g_defaults.dirs[DEFAULT_DIR_CORE], + "savefiles", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], g_defaults.dirs[DEFAULT_DIR_CORE], + "system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], g_defaults.dirs[DEFAULT_DIR_CORE], + "playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], g_defaults.dirs[DEFAULT_DIR_PORT], + "config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP], g_defaults.dirs[DEFAULT_DIR_PORT], + "config/remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], g_defaults.dirs[DEFAULT_DIR_PORT], + "filters", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE], g_defaults.dirs[DEFAULT_DIR_PORT], + "database/rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE])); + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CURSOR], g_defaults.dirs[DEFAULT_DIR_PORT], + "database/cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR])); + fill_pathname_join(g_defaults.path.config, g_defaults.dirs[DEFAULT_DIR_PORT], + file_path_str(FILE_PATH_MAIN_CONFIG), sizeof(g_defaults.path.config)); +} + +static void frontend_ctr_deinit(void* data) +{ + Handle lcd_handle; + u32 parallax_layer_reg_state; + u8 not_2DS; + + extern PrintConsole* currentConsole; + + (void)data; + +#ifndef IS_SALAMANDER + verbosity_enable(); + +#ifdef HAVE_FILE_LOGGER + command_event(CMD_EVENT_LOG_FILE_DEINIT, NULL); +#endif + + if ((gfxBottomFramebuffers[0] == (u8*)currentConsole->frameBuffer) + && (ctr_fork_mode == FRONTEND_FORK_NONE)) + wait_for_input(); + + CFGU_GetModelNintendo2DS(¬_2DS); + + if (not_2DS && srvGetServiceHandle(&lcd_handle, "gsp::Lcd") >= 0) + { + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00110040; + cmdbuf[1] = 2; + svcSendSyncRequest(lcd_handle); + svcCloseHandle(lcd_handle); + } + + parallax_layer_reg_state = (*(float*)0x1FF81080 == 0.0) ? 0x0 : 0x00010001; + GSPGPU_WriteHWRegs(0x202000, ¶llax_layer_reg_state, 4); + + mcuHwcExit(); + ptmuExit(); + cfguExit(); + ndspExit(); + csndExit(); + gfxTopRightFramebuffers[0] = NULL; + gfxTopRightFramebuffers[1] = NULL; + gfxExit(); +#endif +} + +static void frontend_ctr_exec(const char* path, bool should_load_game) +{ + char args[0x300 - 0x4]; + + DEBUG_VAR(path); + DEBUG_STR(path); + + strlcpy(args, elf_path_cst, sizeof(args)); + + RARCH_LOG("Attempt to load core: [%s].\n", path); +#ifndef IS_SALAMANDER + if (should_load_game && !path_is_empty(RARCH_PATH_CONTENT)) + { + strcat(args, path_get(RARCH_PATH_CONTENT)); + RARCH_LOG("content path: [%s].\n", path_get(RARCH_PATH_CONTENT)); + } +#endif + + if (path && path[0]) + { +#ifdef IS_SALAMANDER + struct stat sbuff; + bool file_exists; + + fileExists = stat(path, &sBuff) == 0; + if (!fileExists) + { + char core_path[512]; + + /* find first valid core and load it if the target core doesnt exist */ + get_first_valid_core(&core_path[0]); + + if (core_path[0] == '\0') + { + errorConf error; + + errorInit(&error, ERROR_TEXT, CFG_LANGUAGE_EN); + errorText(&error, "There are no cores installed, install a core to continue."); + errorDisp(&error); + exit(0); + } + } +#endif + int error; + if (envIsHomebrew()) + error = exec_3dsx(path, args); + else + error = exec_cia(path, args); + if (error) + RARCH_LOG("Cant execute new core:%s.\n", strerror(errno)); + } + + svcSleepThread(INT64_MAX); +} + +#ifndef IS_SALAMANDER +static bool frontend_ctr_set_fork(enum frontend_fork fork_mode) +{ + switch (fork_mode) + { + case FRONTEND_FORK_CORE: + RARCH_LOG("FRONTEND_FORK_CORE\n"); + ctr_fork_mode = fork_mode; + break; + case FRONTEND_FORK_CORE_WITH_ARGS: + RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n"); + ctr_fork_mode = fork_mode; + break; + case FRONTEND_FORK_RESTART: + RARCH_LOG("FRONTEND_FORK_RESTART\n"); + /* NOTE: We don't implement Salamander, so just turn + this into FRONTEND_FORK_CORE. */ + ctr_fork_mode = FRONTEND_FORK_CORE; + break; + case FRONTEND_FORK_NONE: + default: + return false; + } + + return true; +} +#endif + +static void frontend_ctr_exitspawn(char* s, size_t len) +{ + bool should_load_game = false; +#ifndef IS_SALAMANDER + if (ctr_fork_mode == FRONTEND_FORK_NONE) + return; + + switch (ctr_fork_mode) + { + case FRONTEND_FORK_CORE_WITH_ARGS: + should_load_game = true; + break; + default: + break; + } +#endif + frontend_ctr_exec(s, should_load_game); +} + +static void frontend_ctr_shutdown(bool unused) +{ + (void)unused; +} + +static void ctr_check_dspfirm(void) +{ + FILE* dsp_fp = fopen("sdmc:/3ds/dspfirm.cdc", "rb"); + + if (dsp_fp) + fclose(dsp_fp); + else + { + size_t code_size; + uint32_t* code_buffer = NULL; + uint32_t* ptr = NULL; + const uint32_t dsp1_magic = 0x31505344; /* "DSP1" */ + FILE* code_fp = fopen("sdmc:/3ds/code.bin", "rb"); + + if (code_fp) + { + fseek(code_fp, 0, SEEK_END); + code_size = ftell(code_fp); + fseek(code_fp, 0, SEEK_SET); + + code_buffer = (uint32_t*) malloc(code_size); + if (code_buffer) + { + fread(code_buffer, 1, code_size, code_fp); + + for (ptr = code_buffer + 0x40; ptr < (code_buffer + (code_size >> 2)); ptr++) + { + if (*ptr == dsp1_magic) + { + size_t dspfirm_size = ptr[1]; + ptr -= 0x40; + if ((ptr + (dspfirm_size >> 2)) > (code_buffer + (code_size >> 2))) + break; + + dsp_fp = fopen("sdmc:/3ds/dspfirm.cdc", "wb"); + if (!dsp_fp) + break; + fwrite(ptr, 1, dspfirm_size, dsp_fp); + fclose(dsp_fp); + break; + } + } + free(code_buffer); + } + fclose(code_fp); + } + } +} + +__attribute__((weak)) Result svchax_init(bool patch_srv); +__attribute__((weak)) u32 __ctr_patch_services; + +void gfxSetFramebufferInfo(gfxScreen_t screen, u8 id); + +static void frontend_ctr_init(void* data) +{ +#ifndef IS_SALAMANDER + (void)data; + + verbosity_enable(); + + gfxInit(GSP_BGR8_OES, GSP_RGB565_OES, false); + + u32 topSize = 400 * 240 * 3; + u32 bottomSize = 320 * 240 * 2; + linearFree(gfxTopLeftFramebuffers[0]); + linearFree(gfxTopLeftFramebuffers[1]); + linearFree(gfxBottomFramebuffers[0]); + linearFree(gfxBottomFramebuffers[1]); + linearFree(gfxTopRightFramebuffers[0]); + linearFree(gfxTopRightFramebuffers[1]); + + gfxTopLeftFramebuffers[0] = linearAlloc(topSize * 2); + gfxTopRightFramebuffers[0] = gfxTopLeftFramebuffers[0] + topSize; + + gfxTopLeftFramebuffers[1] = linearAlloc(topSize * 2); + gfxTopRightFramebuffers[1] = gfxTopLeftFramebuffers[1] + topSize; + + gfxBottomFramebuffers[0] = linearAlloc(bottomSize); + gfxBottomFramebuffers[1] = linearAlloc(bottomSize); + + gfxSetFramebufferInfo(GFX_TOP, 0); + gfxSetFramebufferInfo(GFX_BOTTOM, 0); + + gfxSet3D(true); + consoleInit(GFX_BOTTOM, NULL); + + /* enable access to all service calls when possible. */ + if (svchax_init) + { + osSetSpeedupEnable(false); + svchax_init(__ctr_patch_services); + } + osSetSpeedupEnable(true); + + if (csndInit() != 0) + audio_ctr_csnd = audio_null; + ctr_check_dspfirm(); + if (ndspInit() != 0) + audio_ctr_dsp = audio_null; + cfguInit(); + ptmuInit(); + mcuHwcInit(); +#endif +} + + +static int frontend_ctr_get_rating(void) +{ + u8 device_model = 0xFF; + CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/ + + switch (device_model) + { + case 0: + case 1: + case 3: + /*Old 3/2DS*/ + return 3; + + case 2: + case 4: + case 5: + /*New 3/2DS*/ + return 6; + + default: + /*Unknown Device Or Check Failed*/ + break; + } + + return -1; +} + +enum frontend_architecture frontend_ctr_get_architecture(void) +{ + return FRONTEND_ARCH_ARM; +} + +static int frontend_ctr_parse_drive_list(void* data, bool load_content) +{ +#ifndef IS_SALAMANDER + file_list_t* list = (file_list_t*)data; + enum msg_hash_enums enum_idx = load_content ? + MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR : + MSG_UNKNOWN; + + if (!list) + return -1; + + menu_entries_append_enum(list, + "sdmc:/", + msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR), + enum_idx, + FILE_TYPE_DIRECTORY, 0, 0); +#endif + + return 0; +} + +static uint64_t frontend_ctr_get_mem_total(void) +{ + return osGetMemRegionSize(MEMREGION_ALL); +} + +static uint64_t frontend_ctr_get_mem_used(void) +{ + return osGetMemRegionUsed(MEMREGION_ALL); +} + +static enum frontend_powerstate frontend_ctr_get_powerstate(int* seconds, int* percent) +{ + u8 battery_percent = 0; + u8 charging = 0; + enum frontend_powerstate pwr_state = FRONTEND_POWERSTATE_NONE; + + mcuHwcGetBatteryLevel(&battery_percent); + *percent = battery_percent; + + /* 3ds does not support seconds of charge remaining */ + *seconds = -1; + + PTMU_GetBatteryChargeState(&charging); + if (charging) + { + if (battery_percent == 100) + pwr_state = FRONTEND_POWERSTATE_CHARGED; + else + pwr_state = FRONTEND_POWERSTATE_CHARGING; + } + else + pwr_state = FRONTEND_POWERSTATE_ON_POWER_SOURCE; + + return pwr_state; +} + +static void frontend_ctr_get_os(char* s, size_t len, int* major, int* minor) +{ + OS_VersionBin cver; + OS_VersionBin nver; + + strlcpy(s, "3DS OS", len); + Result data_invalid = osGetSystemVersionData(&nver, &cver); + if (data_invalid == 0) + { + *major = cver.mainver; + *minor = cver.minor; + } + else + { + *major = 0; + *minor = 0; + } + +} + +static void frontend_ctr_get_name(char* s, size_t len) +{ + u8 device_model = 0xFF; + CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/ + + switch (device_model) + { + case 0: + strlcpy(s, "Old 3DS", len); + break; + case 1: + strlcpy(s, "Old 3DS XL", len); + break; + case 2: + strlcpy(s, "New 3DS", len); + break; + case 3: + strlcpy(s, "Old 2DS", len); + break; + case 4: + strlcpy(s, "New 3DS XL", len); + break; + case 5: + strlcpy(s, "New 2DS XL", len); + break; + + default: + strlcpy(s, "Unknown Device", len); + break; + } +} + +frontend_ctx_driver_t frontend_ctx_ctr = +{ + frontend_ctr_get_environment_settings, + frontend_ctr_init, + frontend_ctr_deinit, + frontend_ctr_exitspawn, + NULL, /* process_args */ + frontend_ctr_exec, +#ifdef IS_SALAMANDER + NULL, +#else + frontend_ctr_set_fork, +#endif + frontend_ctr_shutdown, + frontend_ctr_get_name, + frontend_ctr_get_os, + frontend_ctr_get_rating, + NULL, /* load_content */ + frontend_ctr_get_architecture, + frontend_ctr_get_powerstate, + frontend_ctr_parse_drive_list, + frontend_ctr_get_mem_total, + frontend_ctr_get_mem_used, + NULL, /* install_signal_handler */ + NULL, /* get_signal_handler_state */ + NULL, /* set_signal_handler_state */ + NULL, /* destroy_signal_handler_state */ + NULL, /* attach_console */ + NULL, /* detach_console */ + NULL, /* watch_path_for_changes */ + NULL, /* check_for_path_changes */ + "ctr", +}; diff --git a/frontend/frontend_driver.c b/frontend/frontend_driver.c index 83e4da8a64..a59482b096 100644 --- a/frontend/frontend_driver.c +++ b/frontend/frontend_driver.c @@ -19,6 +19,10 @@ #include #include +#if defined(_3DS) +#include <3ds.h> +#endif + #ifdef HAVE_CONFIG_H #include "../config.h" #endif @@ -147,7 +151,10 @@ bool frontend_driver_get_core_extension(char *s, size_t len) strlcpy(s, "elf", len); return true; #elif defined(_3DS) - strlcpy(s, "core", len); + if (envIsHomebrew()) + strlcpy(s, "3dsx", len); + else + strlcpy(s, "cia", len); return true; #else return false;