diff --git a/Makefile b/Makefile index d5469e9..c01ea8f 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ VRAM_DATA := data VRAM_FLAGS := --make-new --path-limit 99 --size-limit 262144 ifeq ($(OS),Windows_NT) - ifeq ($(TERM),) + ifeq ($(TERM),cygwin) PY3 := py -3 # Windows / CMD/PowerShell else PY3 := python3 # Windows / MSYS2 @@ -38,7 +38,7 @@ export CFLAGS := -DDBUILTS="\"$(DBUILTS)\"" -DDBUILTL="\"$(DBUILTL)\"" -DVERSIO -g -Os -Wall -Wextra -Wcast-align -Wformat=2 -Wno-main \ -fomit-frame-pointer -ffast-math -std=gnu11 -MMD -MP \ -Wno-unused-function -Wno-format-truncation $(INCLUDE) -ffunction-sections -fdata-sections -export LDFLAGS := -Tlink.ld -nostartfiles -Wl,--gc-sections,-z,max-page-size=512 +export LDFLAGS := -Tlink.ld -nostartfiles -Wl,--gc-sections,-z,max-page-size=4096 ELF := arm9/arm9.elf arm11/arm11.elf .PHONY: all firm vram0 elf release clean diff --git a/arm11/Makefile b/arm11/Makefile index 2f321a1..3528587 100644 --- a/arm11/Makefile +++ b/arm11/Makefile @@ -5,7 +5,7 @@ TARGET := $(shell basename $(CURDIR)) SOURCE := source BUILD := build -SUBARCH := -D$(PROCESSOR) -marm -march=armv6k -mtune=mpcore -mfloat-abi=hard -mfpu=vfpv2 -mtp=soft +SUBARCH := -D$(PROCESSOR) -march=armv6k -mtune=mpcore -marm -mfloat-abi=hard -mfpu=vfpv2 -mtp=soft INCDIRS := source INCLUDE := $(foreach dir,$(INCDIRS),-I"$(shell pwd)/$(dir)") diff --git a/arm11/link.ld b/arm11/link.ld index 3b81b9e..3664242 100644 --- a/arm11/link.ld +++ b/arm11/link.ld @@ -4,21 +4,12 @@ ENTRY(__boot) MEMORY { - AXIWRAM (RWX) : ORIGIN = 0x1FF80000, LENGTH = 128K + AXIWRAM (RWX) : ORIGIN = 0x1FF80000, LENGTH = 96K HIGHRAM (RWX) : ORIGIN = 0xFFFF0000, LENGTH = 4K } SECTIONS { - .vector : ALIGN(4K) - { - __vector_pa = LOADADDR(.vector); - __vector_va = ABSOLUTE(.); - KEEP(*(.vector)) - . = ALIGN(4K); - __vector_len = . - __vector_va; - } >HIGHRAM AT>AXIWRAM - .text : ALIGN(4K) { __text_pa = LOADADDR(.text); diff --git a/arm11/source/arm/mmu.c b/arm11/source/arm/mmu.c index ee70bca..8259c03 100755 --- a/arm11/source/arm/mmu.c +++ b/arm11/source/arm/mmu.c @@ -76,7 +76,7 @@ typedef struct { static mmuLevel1Table mmuGlobalTT; // simple watermark allocator for 2nd level page tables -#define MAX_SECOND_LEVEL (4) +#define MAX_SECOND_LEVEL (8) static mmuLevel2Table mmuCoarseTables[MAX_SECOND_LEVEL]; static u32 mmuCoarseAllocated = 0; static mmuLevel2Table *mmuAllocateLevel2Table(void) diff --git a/arm11/source/arm/exception.c b/arm11/source/arm/xrq.c similarity index 97% rename from arm11/source/arm/exception.c rename to arm11/source/arm/xrq.c index a2c99b4..221b526 100644 --- a/arm11/source/arm/exception.c +++ b/arm11/source/arm/xrq.c @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +// kinda hardcoded and all over the place, but it needs to stay simple + #include #include diff --git a/arm11/source/arm/xrq.h b/arm11/source/arm/xrq.h new file mode 100644 index 0000000..fd40ae4 --- /dev/null +++ b/arm11/source/arm/xrq.h @@ -0,0 +1,21 @@ +/* + * This file is part of GodMode9 + * Copyright (C) 2020 Wolfvak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#pragma once + +u32 xrqInstallVectorTable(void); diff --git a/arm11/source/arm/vectors.s b/arm11/source/arm/xrqVectors.s similarity index 72% rename from arm11/source/arm/vectors.s rename to arm11/source/arm/xrqVectors.s index a04a867..03d8267 100644 --- a/arm11/source/arm/vectors.s +++ b/arm11/source/arm/xrqVectors.s @@ -28,47 +28,46 @@ .macro TRAP_ENTRY xrq msr cpsr_f, #(\xrq << 29) - b XRQ_Main + b xrqMain .endm -.section .vector, "ax" -vectors: - b XRQ_Reset - b XRQ_Undefined - b XRQ_SVC - b XRQ_PrefetchAbt - b XRQ_DataAbt - b XRQ_Reserved - b XRQ_IRQ - b XRQ_FIQ +xrqVectorTable: + ldr pc, =xrqReset + ldr pc, =xrqUndefined + ldr pc, =xrqSVC + ldr pc, =xrqPrefetchAbort + ldr pc, =xrqDataAbort + b . @ ignore the reserved exception + ldr pc, =xrqIRQ + ldr pc, =xrqFIQ +.pool +xrqVectorTableEnd: -XRQ_Reset: +xrqReset: TRAP_ENTRY 0 -XRQ_Undefined: +xrqUndefined: TRAP_ENTRY 1 -XRQ_SVC: +xrqSVC: TRAP_ENTRY 2 -XRQ_PrefetchAbt: +xrqPrefetchAbort: TRAP_ENTRY 3 -XRQ_DataAbt: +xrqDataAbort: TRAP_ENTRY 4 -XRQ_Reserved: - TRAP_ENTRY 5 - -XRQ_FIQ: +xrqFIQ: TRAP_ENTRY 7 -XRQ_Main: - ldr sp, =(exception_stack_top - 32*4) - stmia sp, {r0-r7} - +xrqMain: + clrex cpsid aif + ldr sp, =(xrqStackTop - 32*4) + stmia sp, {r0-r7} + mrs r1, cpsr lsr r0, r1, #29 @@ -82,11 +81,7 @@ XRQ_Main: add r3, sp, #8*4 msr cpsr_c, r2 - nop - nop stmia r3!, {r8-r14} - nop - nop msr cpsr_c, r1 mrc p15, 0, r4, c5, c0, 0 @ data fault status register @@ -99,7 +94,8 @@ XRQ_Main: bl do_exception -XRQ_IRQ: +xrqIRQ: + clrex sub lr, lr, #4 @ Fix return address srsfd sp!, #SR_SVC_MODE @ Store IRQ mode LR and SPSR on the SVC stack cps #SR_SVC_MODE @ Switch to SVC mode @@ -108,17 +104,26 @@ XRQ_IRQ: and r4, sp, #7 @ Fix SP to be 8byte aligned sub sp, sp, r4 - mov lr, pc - ldr pc, =gicTopHandler + bl gicTopHandler add sp, sp, r4 pop {r0-r4, r12, lr} rfeia sp! @ Return from exception -.section .bss.xrq_stk +@ u32 xrqInstallVectorTable(void) +.global xrqInstallVectorTable +.type xrqInstallVectorTable, %function +xrqInstallVectorTable: + ldr r0, =xrqPage + ldr r1, =xrqVectorTable + mov r2, #(xrqVectorTableEnd - xrqVectorTable) + b memcpy + +.section .bss.xrqPage .align 12 -exception_stack: @ reserve a single aligned page for the exception stack - .space 4096 -exception_stack_top: -.global exception_stack_top +.global xrqPage +xrqPage: + .space 8192 @ reserve two 4K aligned pages for vectors and abort stack +.global xrqStackTop +xrqStackTop: diff --git a/arm11/source/hw/codec.h b/arm11/source/hw/codec.h index f1d75f6..923b313 100755 --- a/arm11/source/hw/codec.h +++ b/arm11/source/hw/codec.h @@ -23,7 +23,7 @@ typedef struct { s16 cpad_x, cpad_y; - s16 ts_x, ts_y; + u16 ts_x, ts_y; } CODEC_Input; void CODEC_Init(void); diff --git a/arm11/source/hw/gpulcd.c b/arm11/source/hw/gpulcd.c index 69f4fe8..39c5daa 100644 --- a/arm11/source/hw/gpulcd.c +++ b/arm11/source/hw/gpulcd.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "arm/timer.h" @@ -58,7 +59,7 @@ unsigned GFX_init(GfxFbFmt mode) // Reset REG_PDN_GPU_CNT = PDN_GPU_CNT_CLK_E; - waitClks(12); + ARM_WaitCycles(12); REG_PDN_GPU_CNT = PDN_GPU_CNT_CLK_E | PDN_GPU_CNT_RST_ALL; REG_GX_GPU_CLK = 0x100; REG_GX_PSC_VRAM = 0; diff --git a/arm11/source/hw/hid.c b/arm11/source/hw/hid.c index dd2140d..ce535df 100755 --- a/arm11/source/hw/hid.c +++ b/arm11/source/hw/hid.c @@ -30,24 +30,16 @@ static u32 HID_ConvertCPAD(s16 cpad_x, s16 cpad_y) { u32 ret = 0; - switch(int_sign(cpad_x)) { - default: - break; - case 1: - ret |= BUTTON_RIGHT; - break; - case -1: - ret |= BUTTON_LEFT; + if (cpad_x > 0) { + ret |= BUTTON_RIGHT; + } else if (cpad_x < 0) { + ret |= BUTTON_LEFT; } - switch(int_sign(cpad_y)) { - default: - break; - case 1: - ret |= BUTTON_UP; - break; - case -1: - ret |= BUTTON_DOWN; + if (cpad_y > 0) { + ret |= BUTTON_UP; + } else if (cpad_y < 0) { + ret |= BUTTON_DOWN; } return ret; diff --git a/arm11/source/hw/mcu.c b/arm11/source/hw/mcu.c index e20c500..af57dab 100755 --- a/arm11/source/hw/mcu.c +++ b/arm11/source/hw/mcu.c @@ -67,7 +67,7 @@ typedef struct { } PACKED_STRUCT MCU_NotificationLED; static u8 cached_volume_slider = 0; -static u32 spec_hid = 0, shell_state = SHELL_OPEN; +static u32 spec_hid = 0, shell_state = 0; static void MCU_UpdateVolumeSlider(void) { @@ -189,6 +189,8 @@ void MCU_Init(void) { u32 clrpend, mask = 0; + shell_state = SHELL_OPEN; + /* set register mask and clear any pending registers */ MCU_WriteRegBuf(REG_INT_EN, (const u8*)&mask, sizeof(mask)); MCU_ReadRegBuf(REG_INT_MASK, (u8*)&clrpend, sizeof(clrpend)); diff --git a/arm11/source/main.c b/arm11/source/main.c index c4dac2f..db23ada 100644 --- a/arm11/source/main.c +++ b/arm11/source/main.c @@ -39,8 +39,8 @@ static const u8 brightness_lvls[] = { 0x4D, 0x56, 0x60, 0x6B, 0x79, 0x8C, 0xA7, 0xD2 }; -static int prev_bright_lvl = -1; -static bool auto_brightness = true; +static int prev_bright_lvl; +static bool auto_brightness; #endif static SystemSHMEM __attribute__((section(".shared"))) SharedMemoryState; @@ -180,11 +180,11 @@ void __attribute__((noreturn)) MainLoop(void) { #ifdef FIXED_BRIGHTNESS LCD_SetBrightness(FIXED_BRIGHTNESS); + #else + prev_bright_lvl = -1; + auto_brightness = true; #endif - // clear up the shared memory section - memset(&SharedMemoryState, 0, sizeof(SharedMemoryState)); - // configure interrupts gicSetInterruptConfig(PXI_RX_INTERRUPT, BIT(0), GIC_PRIO2, GIC_RISINGEDGE_1N, PXI_RX_Handler); gicSetInterruptConfig(MCU_INTERRUPT, BIT(0), GIC_PRIO1, GIC_RISINGEDGE_1N, MCU_HandleInterrupts); diff --git a/arm11/source/system/sections.h b/arm11/source/system/sections.h index 46fb862..008f93a 100755 --- a/arm11/source/system/sections.h +++ b/arm11/source/system/sections.h @@ -21,7 +21,6 @@ #include #define DEF_SECT_(n) extern u32 __##n##_pa, __##n##_va, __##n##_len; -DEF_SECT_(vector) DEF_SECT_(text) DEF_SECT_(data) DEF_SECT_(rodata) diff --git a/arm11/source/system/sys.c b/arm11/source/system/sys.c index abc92da..6bccc64 100755 --- a/arm11/source/system/sys.c +++ b/arm11/source/system/sys.c @@ -24,7 +24,7 @@ #include "arm/gic.h" #include "arm/mmu.h" #include "arm/scu.h" -#include "arm/timer.h" +#include "arm/xrq.h" #include "hw/codec.h" #include "hw/gpulcd.h" @@ -79,13 +79,18 @@ void SYS_CoreZeroInit(void) SCU_Init(); // Map all sections here - mmuMapArea(SECTION_TRI(vector), MMU_FLAGS(MMU_CACHE_WT, MMU_READ_ONLY, 0, 0)); mmuMapArea(SECTION_TRI(text), MMU_FLAGS(MMU_CACHE_WT, MMU_READ_ONLY, 0, 1)); mmuMapArea(SECTION_TRI(data), MMU_FLAGS(MMU_CACHE_WBA, MMU_READ_WRITE, 1, 1)); mmuMapArea(SECTION_TRI(rodata), MMU_FLAGS(MMU_CACHE_WT, MMU_READ_ONLY, 1, 1)); mmuMapArea(SECTION_TRI(bss), MMU_FLAGS(MMU_CACHE_WBA, MMU_READ_WRITE, 1, 1)); mmuMapArea(SECTION_TRI(shared), MMU_FLAGS(MMU_STRONG_ORDER, MMU_READ_WRITE, 1, 1)); + // High exception vectors + mmuMapArea(0xFFFF0000, xrqInstallVectorTable(), 4UL << 10, MMU_FLAGS(MMU_CACHE_WT, MMU_READ_ONLY, 0, 0)); + + // BootROM + mmuMapArea(0x00010000, 0x00010000, 32UL << 10, MMU_FLAGS(MMU_CACHE_WT, MMU_READ_ONLY, 0, 1)); + // IO Registers mmuMapArea(0x10100000, 0x10100000, 4UL << 20, MMU_FLAGS(MMU_DEV_SHARED, MMU_READ_WRITE, 1, 1)); diff --git a/arm9/Makefile b/arm9/Makefile index e9487f0..0a5e2c5 100644 --- a/arm9/Makefile +++ b/arm9/Makefile @@ -5,7 +5,7 @@ TARGET := $(shell basename $(CURDIR)) SOURCE := source BUILD := build -SUBARCH := -D$(PROCESSOR) -marm -march=armv5te -mtune=arm946e-s -mfloat-abi=soft -mno-thumb-interwork +SUBARCH := -D$(PROCESSOR) -march=armv5te -mtune=arm946e-s -mthumb -mfloat-abi=soft INCDIRS := source source/common source/filesys source/crypto source/fatfs source/nand source/virtual source/game source/gamecart source/lodepng source/qrcodegen source/system source/utils INCLUDE := $(foreach dir,$(INCDIRS),-I"$(shell pwd)/$(dir)") diff --git a/arm9/source/crypto/crc16.c b/arm9/source/crypto/crc16.c index 888414c..3b1ac74 100644 --- a/arm9/source/crypto/crc16.c +++ b/arm9/source/crypto/crc16.c @@ -7,7 +7,7 @@ // see: https://github.com/TASVideos/desmume/blob/master/desmume/src/bios.cpp#L1070tions u16 crc16_quick(const void* src, u32 len) { - const u16 tabval[] = { CRC16_TABVAL }; + static const u16 tabval[] = { CRC16_TABVAL }; u16* data = (u16*) src; u16 crc = 0xFFFF; diff --git a/arm9/source/crypto/keydb.c b/arm9/source/crypto/keydb.c index d654b8e..d55f7b4 100644 --- a/arm9/source/crypto/keydb.c +++ b/arm9/source/crypto/keydb.c @@ -232,7 +232,7 @@ u32 CheckRecommendedKeyDb(const char* path) { // SHA-256 of the recommended aeskeydb.bin file // equals MD5 A5B28945A7C051D7A0CD18AF0E580D1B - const u8 recommended_sha[0x20] = { + static const u8 recommended_sha[0x20] = { 0x40, 0x76, 0x54, 0x3D, 0xA3, 0xFF, 0x91, 0x1C, 0xE1, 0xCC, 0x4E, 0xC7, 0x2F, 0x92, 0xE4, 0xB7, 0x2B, 0x24, 0x00, 0x15, 0xBE, 0x9B, 0xFC, 0xDE, 0x7F, 0xED, 0x95, 0x1D, 0xD5, 0xAB, 0x2D, 0xCB }; diff --git a/arm9/source/filesys/filetype.c b/arm9/source/filesys/filetype.c index 5db4daa..14c4a70 100644 --- a/arm9/source/filesys/filetype.c +++ b/arm9/source/filesys/filetype.c @@ -11,13 +11,13 @@ #include "ui.h" // only for font file detection u64 IdentifyFileType(const char* path) { - const u8 romfs_magic[] = { ROMFS_MAGIC }; - const u8 diff_magic[] = { DIFF_MAGIC }; - const u8 disa_magic[] = { DISA_MAGIC }; - const u8 tickdb_magic[] = { TICKDB_MAGIC }; - const u8 smdh_magic[] = { SMDH_MAGIC }; - const u8 threedsx_magic[] = { THREEDSX_EXT_MAGIC }; - const u8 png_magic[] = { PNG_MAGIC }; + static const u8 romfs_magic[] = { ROMFS_MAGIC }; + static const u8 diff_magic[] = { DIFF_MAGIC }; + static const u8 disa_magic[] = { DISA_MAGIC }; + static const u8 tickdb_magic[] = { TICKDB_MAGIC }; + static const u8 smdh_magic[] = { SMDH_MAGIC }; + static const u8 threedsx_magic[] = { THREEDSX_EXT_MAGIC }; + static const u8 png_magic[] = { PNG_MAGIC }; if (!path) return 0; // safety u8 ALIGN(32) header[0x2C0]; // minimum required size @@ -124,7 +124,7 @@ u64 IdentifyFileType(const char* path) { (memcmp(data, threedsx_magic, sizeof(threedsx_magic)) == 0)) { return GAME_3DSX; // 3DSX (executable) file } else if ((fsize > sizeof(CmdHeader)) && - CheckCmdSize((CmdHeader*) data, fsize) == 0) { + (CMD_SIZE((CmdHeader*) data) == fsize)) { return GAME_CMD; // CMD file } else if ((fsize > sizeof(NcchInfoHeader)) && (GetNcchInfoVersion((NcchInfoHeader*) data)) && diff --git a/arm9/source/filesys/filetype.h b/arm9/source/filesys/filetype.h index 89fbb97..4ecefaf 100644 --- a/arm9/source/filesys/filetype.h +++ b/arm9/source/filesys/filetype.h @@ -50,6 +50,7 @@ #define FTYPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|BIN_KEYDB)) #define FTYPE_CIABUILD(tp) ((tp&(GAME_NCSD|GAME_NCCH|GAME_TMD)) || ((tp&GAME_NDS)&&(tp&(FLAG_DSIW)))) #define FTYPE_CIABUILD_L(tp) (FTYPE_CIABUILD(tp) && (tp&(GAME_TMD))) +#define FTYPE_CIAINSTALL(tp) ((tp&(GAME_NCSD|GAME_NCCH|GAME_CIA)) || ((tp&GAME_NDS)&&(tp&(FLAG_DSIW))) || (tp&(GAME_TMD)&&(tp&(FLAG_NUSCDN)))) #define FTYPE_CXIDUMP(tp) (tp&(GAME_TMD)) #define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB)) #define FTYPE_KEYBUILD(tp) (tp&(BIN_KEYDB|BIN_LEGKEY)) diff --git a/arm9/source/filesys/fsinit.c b/arm9/source/filesys/fsinit.c index ea8bd29..f9f553b 100644 --- a/arm9/source/filesys/fsinit.c +++ b/arm9/source/filesys/fsinit.c @@ -65,9 +65,9 @@ bool InitImgFS(const char* path) { } void DeinitExtFS() { + InitImgFS(NULL); SetupNandSdDrive(NULL, NULL, NULL, 0); SetupNandSdDrive(NULL, NULL, NULL, 1); - InitImgFS(NULL); for (u32 i = NORM_FS - 1; i > 0; i--) { if (fs_mounted[i]) { char fsname[8]; diff --git a/arm9/source/filesys/fsperm.c b/arm9/source/filesys/fsperm.c index 9a9af05..2a68bea 100644 --- a/arm9/source/filesys/fsperm.c +++ b/arm9/source/filesys/fsperm.c @@ -46,7 +46,7 @@ bool CheckWritePermissions(const char* path) { // check drive type, get permission type if (drvtype & DRV_SYSNAND) { - u32 perms[] = { PERM_SYS_LVL0, PERM_SYS_LVL1, PERM_SYS_LVL2, PERM_SYS_LVL3 }; + static const u32 perms[] = { PERM_SYS_LVL0, PERM_SYS_LVL1, PERM_SYS_LVL2, PERM_SYS_LVL3 }; u32 lvl = (drvtype & (DRV_TWLNAND|DRV_ALIAS|DRV_CTRNAND)) ? 1 : 0; if (drvtype & (DRV_CTRNAND|DRV_VIRTUAL)) { // check for paths const char* path_lvl3[] = { PATH_SYS_LVL3 }; @@ -65,7 +65,7 @@ bool CheckWritePermissions(const char* path) { perm = perms[lvl]; snprintf(area_name, 16, "SysNAND (lvl%lu)", lvl); } else if (drvtype & DRV_EMUNAND) { - u32 perms[] = { PERM_EMU_LVL0, PERM_EMU_LVL1 }; + static const u32 perms[] = { PERM_EMU_LVL0, PERM_EMU_LVL1 }; u32 lvl = (drvtype & (DRV_ALIAS|DRV_CTRNAND)) ? 1 : 0; if (drvtype & DRV_VIRTUAL) { // check for paths const char* path_lvl1[] = { PATH_EMU_LVL1 }; @@ -124,7 +124,7 @@ bool CheckWritePermissions(const char* path) { } bool CheckDirWritePermissions(const char* path) { - const char* path_chk[] = { PATH_SYS_LVL3, PATH_SYS_LVL2, PATH_SYS_LVL1, PATH_EMU_LVL1 }; + static const char* path_chk[] = { PATH_SYS_LVL3, PATH_SYS_LVL2, PATH_SYS_LVL1, PATH_EMU_LVL1 }; for (u32 i = 0; i < sizeof(path_chk) / sizeof(char*); i++) { const char* path_cmp = path_chk[i]; u32 p = 0; diff --git a/arm9/source/filesys/image.c b/arm9/source/filesys/image.c index 09a4279..507f9f5 100644 --- a/arm9/source/filesys/image.c +++ b/arm9/source/filesys/image.c @@ -1,11 +1,14 @@ #include "image.h" #include "vff.h" +#include "nandcmac.h" static FIL mount_file; static u64 mount_state = 0; static char mount_path[256] = { 0 }; +static bool fix_cmac = false; + int ReadImageBytes(void* buffer, u64 offset, u64 count) { UINT bytes_read; @@ -28,6 +31,7 @@ int WriteImageBytes(const void* buffer, u64 offset, u64 count) { if (fvx_tell(&mount_file) != offset) fvx_lseek(&mount_file, offset); ret = fvx_write(&mount_file, buffer, count, &bytes_written); + if (ret == 0) fix_cmac = true; return (ret != 0) ? (int) ret : (bytes_written != count) ? -1 : 0; } @@ -59,6 +63,8 @@ u64 MountImage(const char* path) { u64 type = (path) ? IdentifyFileType(path) : 0; if (mount_state) { fvx_close(&mount_file); + if (fix_cmac) FixFileCmac(mount_path, false); + fix_cmac = false; mount_state = 0; *mount_path = 0; } diff --git a/arm9/source/filesys/sddata.c b/arm9/source/filesys/sddata.c index 0bdef29..efaa6f6 100644 --- a/arm9/source/filesys/sddata.c +++ b/arm9/source/filesys/sddata.c @@ -15,10 +15,10 @@ typedef struct { static FilCryptInfo filcrypt[NUM_FILCRYPTINFO] = { 0 }; -char alias_drv[NUM_ALIAS_DRV]; // 1 char ASCII drive number of the alias drive / 0x00 if unused -char alias_path[NUM_ALIAS_DRV][128]; // full path to resolve the alias into +static char alias_drv[NUM_ALIAS_DRV]; // 1 char ASCII drive number of the alias drive / 0x00 if unused +static char alias_path[NUM_ALIAS_DRV][128]; // full path to resolve the alias into -u8 sd_keyy[NUM_ALIAS_DRV][16] __attribute__((aligned(4))); // key Y belonging to alias drive +static u8 sd_keyy[NUM_ALIAS_DRV][16] __attribute__((aligned(4))); // key Y belonging to alias drive int alias_num (const TCHAR* path) { int num = -1; diff --git a/arm9/source/game/bdri.h b/arm9/source/game/bdri.h index 3aa8374..0dc4c65 100644 --- a/arm9/source/game/bdri.h +++ b/arm9/source/game/bdri.h @@ -2,29 +2,9 @@ #include "common.h" #include "ticket.h" - -// There's probably a better place to put this -#define SD_TITLEDB_PATH(emu) ((emu) ? "B:/dbs/title.db" : "A:/dbs/title.db") +#include "tie.h" // https://www.3dbrew.org/wiki/Inner_FAT -// https://www.3dbrew.org/wiki/Title_Database - -typedef struct { - u64 title_size; - u32 title_type; // usually == 0x40 - u32 title_version; - u8 flags_0[4]; - u32 tmd_content_id; - u32 cmd_content_id; - u8 flags_1[4]; - u32 extdata_id_low; // 0 if the title doesn't use extdata - u8 reserved1[4]; - u8 flags_2[8]; - char product_code[16]; - u8 reserved2[16]; - u8 unknown[4]; // appears to not matter what's here - u8 reserved3[44]; -} __attribute__((packed)) TitleInfoEntry; u32 GetNumTitleInfoEntries(const char* path); u32 GetNumTickets(const char* path); diff --git a/arm9/source/game/cia.c b/arm9/source/game/cia.c index 62b3b02..e421089 100644 --- a/arm9/source/game/cia.c +++ b/arm9/source/game/cia.c @@ -50,11 +50,11 @@ u32 FixCiaHeaderForTmd(CiaHeader* header, TitleMetaData* tmd) { } u32 BuildCiaCert(u8* ciacert) { - const u8 cert_hash_expected[0x20] = { + static const u8 cert_hash_expected[0x20] = { 0xC7, 0x2E, 0x1C, 0xA5, 0x61, 0xDC, 0x9B, 0xC8, 0x05, 0x58, 0x58, 0x9C, 0x63, 0x08, 0x1C, 0x8A, 0x10, 0x78, 0xDF, 0x42, 0x99, 0x80, 0x3A, 0x68, 0x58, 0xF0, 0x41, 0xF9, 0xCB, 0x10, 0xE6, 0x35 }; - const u8 cert_hash_expected_dev[0x20] = { + static const u8 cert_hash_expected_dev[0x20] = { 0xFB, 0xD2, 0xC0, 0x47, 0x95, 0xB9, 0x4C, 0xC8, 0x0B, 0x64, 0x58, 0x96, 0xF6, 0x61, 0x0F, 0x52, 0x18, 0x83, 0xAF, 0xE0, 0xF4, 0xE5, 0x62, 0xBA, 0x69, 0xEE, 0x72, 0x2A, 0xC2, 0x4E, 0x95, 0xB3 }; diff --git a/arm9/source/game/cmd.c b/arm9/source/game/cmd.c index 62f4684..fdcdb52 100644 --- a/arm9/source/game/cmd.c +++ b/arm9/source/game/cmd.c @@ -1,11 +1,63 @@ #include "cmd.h" -u32 CheckCmdSize(CmdHeader* cmd, u64 fsize) { - u64 cmdsize = sizeof(CmdHeader) + - (cmd->n_entries * sizeof(u32)) + - (cmd->n_cmacs * sizeof(u32)) + - (cmd->n_entries * 0x10); +CmdHeader* BuildAllocCmdData(TitleMetaData* tmd) { + CmdHeader proto; + CmdHeader* cmd = NULL; + u32 content_count = getbe16(tmd->content_count); + u16 max_cnt_idx = 0; - return (fsize == cmdsize) ? 0 : 1; + // sanity check + if (!content_count) + return NULL; + + // find max content id + TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); + for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++) + if (getbe16(chunk->index) > max_cnt_idx) max_cnt_idx = getbe16(chunk->index); + + // allocate memory for CMD / basic setup + proto.cmd_id = 1; + proto.n_entries = max_cnt_idx + 1; + proto.n_cmacs = content_count; + proto.unknown = 1; + memset(proto.cmac, 0x00, 0x10); + cmd = (CmdHeader*) malloc(CMD_SIZE(&proto)); + if (!cmd) return NULL; + memset(cmd, 0x00, CMD_SIZE(&proto)); + memcpy(cmd, &proto, sizeof(CmdHeader)); + cmd->unknown = 0x0; // this means no CMACs, only valid for NAND + + // copy content ids + u32* cnt_id = (u32*) (cmd + 1); + u32* cnt_id_2nd = cnt_id + cmd->n_entries; + chunk = (TmdContentChunk*) (tmd + 1); + memset(cnt_id, 0xFF, cmd->n_entries * sizeof(u32)); + for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++) { + u32 chunk_id = getbe32(chunk->id); + cnt_id[getbe16(chunk->index)] = chunk_id; + *(cnt_id_2nd++) = chunk_id; + } + + // bubble sort the second content id list + bool bs_finished = false; + cnt_id_2nd = cnt_id + cmd->n_entries; + while (!bs_finished) { + bs_finished = true; + for (u32 b = 1; b < cmd->n_cmacs; b++) { + if (cnt_id_2nd[b] < cnt_id_2nd[b-1]) { + u32 swp = cnt_id_2nd[b]; + cnt_id_2nd[b] = cnt_id_2nd[b-1]; + cnt_id_2nd[b-1] = swp; + bs_finished = false; + } + } + } + + // set CMACs to 0x00 + u8* cnt_cmac = (u8*) (cnt_id_2nd + cmd->n_cmacs); + memset(cnt_cmac, 0x00, 0x10 * cmd->n_entries); + + // we still need to fix / set the CMACs inside the CMD file! + return cmd; } diff --git a/arm9/source/game/cmd.h b/arm9/source/game/cmd.h index b286cbc..404e0fc 100644 --- a/arm9/source/game/cmd.h +++ b/arm9/source/game/cmd.h @@ -1,18 +1,23 @@ #pragma once #include "common.h" +#include "tmd.h" +#define CMD_SIZE(cmd) (sizeof(CmdHeader) + \ + (((cmd)->n_entries) * sizeof(u32)) + \ + (((cmd)->n_cmacs) * sizeof(u32)) + \ + (((cmd)->unknown) ? (((cmd)->n_entries) * 0x10) : 0)) // from: http://3dbrew.org/wiki/Titles#Data_Structure typedef struct { - u32 cmd_id; // same as filename id, .cmd - u32 n_entries; // matches highest content index - u32 n_cmacs; // number of cmacs in file (excluding the one @0x10) - u32 unknown; // usually 1 - u8 cmac[0x10]; // calculated from first 0x10 byte of data, no hashing - // followed by u32 list of content ids (sorted by index, 0xFFFFFFFF for unavailable) - // followed by u32 list of content ids (sorted by id?) - // followed by CMACs (may contain garbage) + u32 cmd_id; // same as filename id, .cmd + u32 n_entries; // matches highest content index + u32 n_cmacs; // number of cmacs in file (excluding the one @0x10) + u32 unknown; // usually 1 + u8 cmac[0x10]; // calculated from first 0x10 byte of data, no hashing + // followed by u32 list of content ids (sorted by index, 0xFFFFFFFF for unavailable) + // followed by u32 list of content ids (sorted by id?) + // followed by CMACs (may contain garbage) } __attribute__((packed, aligned(4))) CmdHeader; -u32 CheckCmdSize(CmdHeader* cmd, u64 fsize); +CmdHeader* BuildAllocCmdData(TitleMetaData* tmd); diff --git a/arm9/source/game/disadiff.c b/arm9/source/game/disadiff.c index a31b6a0..f3814f5 100644 --- a/arm9/source/game/disadiff.c +++ b/arm9/source/game/disadiff.c @@ -159,11 +159,11 @@ inline static FRESULT DisaDiffQWrite(const TCHAR* path, const void* buf, UINT of } u32 GetDisaDiffRWInfo(const char* path, DisaDiffRWInfo* info, bool partitionB) { - const u8 disa_magic[] = { DISA_MAGIC }; - const u8 diff_magic[] = { DIFF_MAGIC }; - const u8 ivfc_magic[] = { IVFC_MAGIC }; - const u8 dpfs_magic[] = { DPFS_MAGIC }; - const u8 difi_magic[] = { DIFI_MAGIC }; + static const u8 disa_magic[] = { DISA_MAGIC }; + static const u8 diff_magic[] = { DIFF_MAGIC }; + static const u8 ivfc_magic[] = { IVFC_MAGIC }; + static const u8 dpfs_magic[] = { DPFS_MAGIC }; + static const u8 difi_magic[] = { DIFI_MAGIC }; // reset reader info memset(info, 0x00, sizeof(DisaDiffRWInfo)); diff --git a/arm9/source/game/game.h b/arm9/source/game/game.h index ccbe7dd..24a534f 100644 --- a/arm9/source/game/game.h +++ b/arm9/source/game/game.h @@ -14,6 +14,9 @@ #include "tad.h" #include "3dsx.h" #include "tmd.h" +#include "ticket.h" +#include "tie.h" #include "cmd.h" +#include "bdri.h" #include "ticketdb.h" #include "ncchinfo.h" diff --git a/arm9/source/game/gba.c b/arm9/source/game/gba.c index 5a86969..f967f9e 100644 --- a/arm9/source/game/gba.c +++ b/arm9/source/game/gba.c @@ -7,7 +7,7 @@ 0x84, 0x9D, 0xA0, 0xD5, 0x6F, 0x5A, 0x34, 0xC4, 0x81, 0x06, 0x0C, 0x9F, 0xF2, 0xFA, 0xD8, 0x18 u32 ValidateAgbSaveHeader(AgbSaveHeader* header) { - u8 magic[] = { AGBSAVE_MAGIC }; + static u8 magic[] = { AGBSAVE_MAGIC }; // basic checks if ((memcmp(header->magic, magic, sizeof(magic)) != 0) || @@ -28,7 +28,7 @@ u32 ValidateAgbSaveHeader(AgbSaveHeader* header) { // http://problemkaputt.de/gbatek.htm#gbacartridgeheader u32 ValidateAgbHeader(AgbHeader* agb) { - const u8 logo_sha[0x20] = { AGBLOGO_SHA256 }; + static const u8 logo_sha[0x20] = { AGBLOGO_SHA256 }; u8 logo[0x9C] __attribute__((aligned(4))); // check fixed value diff --git a/arm9/source/game/ncsd.c b/arm9/source/game/ncsd.c index 6bedfd2..3fb5213 100644 --- a/arm9/source/game/ncsd.c +++ b/arm9/source/game/ncsd.c @@ -2,7 +2,7 @@ #include "ncch.h" u32 ValidateNcsdHeader(NcsdHeader* header) { - u8 zeroes[16] = { 0 }; + static const u8 zeroes[16] = { 0 }; if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number (memcmp(header->partitions_fs_type, zeroes, 8) != 0) || !header->mediaId) // prevent detection of NAND images return 1; diff --git a/arm9/source/game/romfs.c b/arm9/source/game/romfs.c index 3ae41f9..4273e22 100644 --- a/arm9/source/game/romfs.c +++ b/arm9/source/game/romfs.c @@ -19,7 +19,7 @@ u64 GetRomFsLvOffset(RomFsIvfcHeader* ivfc, u32 lvl) { // validate IVFC header by checking offsets and hash sizes u32 ValidateRomFsHeader(RomFsIvfcHeader* ivfc, u32 max_size) { - u8 magic[] = { ROMFS_MAGIC }; + static const u8 magic[] = { ROMFS_MAGIC }; // check magic number if (memcmp(magic, ivfc->magic, sizeof(magic)) != 0) diff --git a/arm9/source/game/smdh.c b/arm9/source/game/smdh.c index 2720131..2a8c053 100644 --- a/arm9/source/game/smdh.c +++ b/arm9/source/game/smdh.c @@ -9,7 +9,7 @@ 36, 37, 44, 45, 38, 39, 46, 47, 52, 53, 60, 61, 54, 55, 62, 63 u32 ConvertSmdhIcon(u16* icon, const u16* smdh_icon, u32 w, u32 h) { - const u32 lut[8*8] = { SMDH_LUT }; + static const u32 lut[8*8] = { SMDH_LUT }; u16* pix565 = (u16*) smdh_icon; for (u32 y = 0; y < h; y += 8) { for (u32 x = 0; x < w; x += 8) { diff --git a/arm9/source/game/ticket.c b/arm9/source/game/ticket.c index f8ab0ec..bc617e4 100644 --- a/arm9/source/game/ticket.c +++ b/arm9/source/game/ticket.c @@ -6,7 +6,7 @@ #include "ff.h" u32 ValidateTicket(Ticket* ticket) { - const u8 magic[] = { TICKET_SIG_TYPE }; + static const u8 magic[] = { TICKET_SIG_TYPE }; if ((memcmp(ticket->sig_type, magic, sizeof(magic)) != 0) || ((strncmp((char*) ticket->issuer, TICKET_ISSUER, 0x40) != 0) && (strncmp((char*) ticket->issuer, TICKET_ISSUER_DEV, 0x40) != 0)) || @@ -39,8 +39,8 @@ u32 ValidateTicketSignature(Ticket* ticket) { } u32 BuildFakeTicket(Ticket* ticket, u8* title_id) { - const u8 sig_type[4] = { TICKET_SIG_TYPE }; // RSA_2048 SHA256 - const u8 ticket_cnt_index[] = { // whatever this is + static const u8 sig_type[4] = { TICKET_SIG_TYPE }; // RSA_2048 SHA256 + static const u8 ticket_cnt_index[] = { // whatever this is 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 @@ -72,11 +72,11 @@ u32 GetTicketSize(const Ticket* ticket) { } u32 BuildTicketCert(u8* tickcert) { - const u8 cert_hash_expected[0x20] = { + static const u8 cert_hash_expected[0x20] = { 0xDC, 0x15, 0x3C, 0x2B, 0x8A, 0x0A, 0xC8, 0x74, 0xA9, 0xDC, 0x78, 0x61, 0x0E, 0x6A, 0x8F, 0xE3, 0xE6, 0xB1, 0x34, 0xD5, 0x52, 0x88, 0x73, 0xC9, 0x61, 0xFB, 0xC7, 0x95, 0xCB, 0x47, 0xE6, 0x97 }; - const u8 cert_hash_expected_dev[0x20] = { + static const u8 cert_hash_expected_dev[0x20] = { 0x97, 0x2A, 0x32, 0xFF, 0x9D, 0x4B, 0xAA, 0x2F, 0x1A, 0x24, 0xCF, 0x21, 0x13, 0x87, 0xF5, 0x38, 0xC6, 0x4B, 0xD4, 0x8F, 0xDF, 0x13, 0x21, 0x3D, 0xFC, 0x72, 0xFC, 0x8D, 0x9F, 0xDD, 0x01, 0x0E }; diff --git a/arm9/source/game/tie.c b/arm9/source/game/tie.c new file mode 100644 index 0000000..cf24d73 --- /dev/null +++ b/arm9/source/game/tie.c @@ -0,0 +1,92 @@ +#include "tie.h" +#include "cmd.h" + +#define CMD_SIZE_ALIGN(sd) (sd ? 0x8000 : 0x4000) + + +u32 BuildTitleInfoEntryTmd(TitleInfoEntry* tie, TitleMetaData* tmd, bool sd) { + u64 title_id = getbe64(tmd->title_id); + u32 has_idx1 = false; + + // set basic values + memset(tie, 0x00, sizeof(TitleInfoEntry)); + tie->title_type = 0x40; + + // title version, product code, cmd id + tie->title_version = getbe16(tmd->title_version); + tie->cmd_content_id = 0x01; + memcpy(tie->unknown, "GM9", 4); // GM9 install magic number + + // calculate base title size + // align size: 0x4000 for TWL and CTRNAND, 0x8000 for SD + u32 align_size = CMD_SIZE_ALIGN(sd); + u32 content_count = getbe16(tmd->content_count); + TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); + tie->title_size = + (align_size * 3) + // base folder + 'content' + 'cmd' + align(TMD_SIZE_N(content_count), align_size) + // TMD + align_size; // CMD, placeholder (!!!) + for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++) { + if (getbe16(chunk->index) == 1) has_idx1 = true; // will be useful later + tie->title_size += align(getbe64(chunk->size), align_size); + } + + // manual? (we need to properly check this later) + if (has_idx1 && (((title_id >> 32) == 0x00040000) || ((title_id >> 32) == 0x00040010))) { + tie->flags_0[0] = 0x1; // this may have a manual + } + + return 0; +} + +u32 BuildTitleInfoEntryTwl(TitleInfoEntry* tie, TitleMetaData* tmd, TwlHeader* twl) { + u64 title_id = getbe64(tmd->title_id); + + if (ValidateTwlHeader(twl) != 0) return 1; + if (BuildTitleInfoEntryTmd(tie, tmd, false) != 0) return 1; + + // product code + memcpy(tie->product_code, twl->game_title, 12); + + // specific flags + // see: http://3dbrew.org/wiki/Titles + if ((title_id >> 32) == 0x00048004) { // TWL app / game + tie->flags_2[0] = 0x01; + tie->flags_2[4] = 0x01; + tie->flags_2[5] = 0x01; + } + + return 0; +} + +u32 BuildTitleInfoEntryNcch(TitleInfoEntry* tie, TitleMetaData* tmd, NcchHeader* ncch, NcchExtHeader* exthdr, bool sd) { + u64 title_id = getbe64(tmd->title_id); + + if (ValidateNcchHeader(ncch) != 0) return 1; + if (BuildTitleInfoEntryTmd(tie, tmd, sd) != 0) return 1; + + // product code, extended title version + memcpy(tie->product_code, ncch->productcode, 0x10); + tie->title_version |= (ncch->version << 16); + + // specific flags + // see: http://3dbrew.org/wiki/Titles + if (!((title_id >> 32) & 0x10)) // not a system title + tie->flags_2[4] = 0x01; + + // stuff from extheader + if (exthdr) { + // add save data size to title size + if (exthdr->savedata_size) { + u32 align_size = CMD_SIZE_ALIGN(sd); + tie->title_size += + align_size + // 'data' folder + align(exthdr->savedata_size, align_size); // savegame + tie->flags_1[0] = 0x01; // has SD save + }; + // extdata ID low (hacky) + tie->extdata_id_low = getle32(exthdr->aci_data + 0x30 - 0x0C + 0x04); + } else tie->flags_0[0] = 0x00; // no manual + + return 0; +} diff --git a/arm9/source/game/tie.h b/arm9/source/game/tie.h new file mode 100644 index 0000000..f6de4dc --- /dev/null +++ b/arm9/source/game/tie.h @@ -0,0 +1,31 @@ +#pragma once + +#include "common.h" +#include "tmd.h" +#include "ncch.h" +#include "nds.h" + +// There's probably a better place to put this +#define SD_TITLEDB_PATH(emu) ((emu) ? "B:/dbs/title.db" : "A:/dbs/title.db") + + +// see: https://www.3dbrew.org/wiki/Title_Database +typedef struct { + u64 title_size; + u32 title_type; // usually == 0x40 + u32 title_version; + u8 flags_0[4]; + u32 tmd_content_id; + u32 cmd_content_id; + u8 flags_1[4]; + u32 extdata_id_low; // 0 if the title doesn't use extdata + u8 reserved1[4]; + u8 flags_2[8]; + char product_code[16]; + u8 reserved2[16]; + u8 unknown[4]; // appears to not matter what's here + u8 reserved3[44]; +} __attribute__((packed)) TitleInfoEntry; + +u32 BuildTitleInfoEntryTwl(TitleInfoEntry* tie, TitleMetaData* tmd, TwlHeader* twl); +u32 BuildTitleInfoEntryNcch(TitleInfoEntry* tie, TitleMetaData* tmd, NcchHeader* ncch, NcchExtHeader* exthdr, bool sd); diff --git a/arm9/source/game/tmd.c b/arm9/source/game/tmd.c index 2eef622..d98f4fa 100644 --- a/arm9/source/game/tmd.c +++ b/arm9/source/game/tmd.c @@ -6,7 +6,7 @@ #include "ff.h" u32 ValidateTmd(TitleMetaData* tmd) { - const u8 magic[] = { TMD_SIG_TYPE }; + static const u8 magic[] = { TMD_SIG_TYPE }; if ((memcmp(tmd->sig_type, magic, sizeof(magic)) != 0) || ((strncmp((char*) tmd->issuer, TMD_ISSUER, 0x40) != 0) && (strncmp((char*) tmd->issuer, TMD_ISSUER_DEV, 0x40) != 0))) @@ -77,7 +77,7 @@ u32 FixTmdHashes(TitleMetaData* tmd) { } u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size, u32 twl_privsave_size) { - const u8 sig_type[4] = { TMD_SIG_TYPE }; + static const u8 sig_type[4] = { TMD_SIG_TYPE }; // safety check: number of contents if (n_contents > TMD_MAX_CONTENTS) return 1; // potential incompatibility here (!) // set TMD all zero for a clean start @@ -102,11 +102,11 @@ u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size } u32 BuildTmdCert(u8* tmdcert) { - const u8 cert_hash_expected[0x20] = { + static const u8 cert_hash_expected[0x20] = { 0x91, 0x5F, 0x77, 0x3A, 0x07, 0x82, 0xD4, 0x27, 0xC4, 0xCE, 0xF5, 0x49, 0x25, 0x33, 0xE8, 0xEC, 0xF6, 0xFE, 0xA1, 0xEB, 0x8C, 0xCF, 0x59, 0x6E, 0x69, 0xBA, 0x2A, 0x38, 0x8D, 0x73, 0x8A, 0xE1 }; - const u8 cert_hash_expected_dev[0x20] = { + static const u8 cert_hash_expected_dev[0x20] = { 0x49, 0xC9, 0x41, 0x56, 0xCA, 0x86, 0xBD, 0x1F, 0x36, 0x51, 0x51, 0x6A, 0x4A, 0x9F, 0x54, 0xA1, 0xC2, 0xE9, 0xCA, 0x93, 0x94, 0xF4, 0x29, 0xA0, 0x38, 0x54, 0x75, 0xFF, 0xAB, 0x6E, 0x8E, 0x71 }; diff --git a/arm9/source/gamecart/command_ak2i.c b/arm9/source/gamecart/command_ak2i.c index 02ce979..6329ff5 100644 --- a/arm9/source/gamecart/command_ak2i.c +++ b/arm9/source/gamecart/command_ak2i.c @@ -9,7 +9,6 @@ #include "command_ak2i.h" #include "protocol_ntr.h" #include "card_ntr.h" -#include "delay.h" u32 AK2I_CmdGetHardwareVersion(void) { diff --git a/arm9/source/gamecart/command_ntr.c b/arm9/source/gamecart/command_ntr.c index 3f2e25a..5d3ab79 100644 --- a/arm9/source/gamecart/command_ntr.c +++ b/arm9/source/gamecart/command_ntr.c @@ -5,18 +5,18 @@ // modifyed by osilloscopion (2 Jul 2016) // +#include + #include "command_ntr.h" #include "protocol_ntr.h" #include "card_ntr.h" -#include "delay.h" - u32 ReadDataFlags = 0; void NTR_CmdReset(void) { cardReset (); - ioDelay2(0xF000); + ARM_WaitCycles(0xF000 * 4); } u32 NTR_CmdGetCartId(void) @@ -34,7 +34,7 @@ void NTR_CmdReadHeader (u8* buffer) { REG_NTRCARDROMCNT=0; REG_NTRCARDMCNT=0; - ioDelay2(167550); + ARM_WaitCycles(167550 * 4); REG_NTRCARDMCNT=NTRCARD_CR1_ENABLE|NTRCARD_CR1_IRQ; REG_NTRCARDROMCNT=NTRCARD_nRESET|NTRCARD_SEC_SEED; while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ; diff --git a/arm9/source/gamecart/delay.h b/arm9/source/gamecart/delay.h deleted file mode 100644 index 98c4b8a..0000000 --- a/arm9/source/gamecart/delay.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common.h" - -void ioDelay(u32 us); -void ioDelay2(u32 us); diff --git a/arm9/source/gamecart/iodelay.s b/arm9/source/gamecart/iodelay.s deleted file mode 100644 index aa37b8c..0000000 --- a/arm9/source/gamecart/iodelay.s +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -.arm -.global ioDelay -.type ioDelay STT_FUNC - -@ioDelay ( u32 us ) -ioDelay: - ldr r1, =0x18000000 @ VRAM -1: - @ Loop doing uncached reads from VRAM to make loop timing more reliable - ldr r2, [r1] - subs r0, #1 - bgt 1b - bx lr - -.global ioDelay2 -.type ioDelay2 STT_FUNC - -@ioDelay2 ( u32 us ) -ioDelay2: -1: - subs r0, #1 - bgt 1b - bx lr diff --git a/arm9/source/gamecart/ndscard.h b/arm9/source/gamecart/ndscard.h index 25fce99..56a543a 100644 --- a/arm9/source/gamecart/ndscard.h +++ b/arm9/source/gamecart/ndscard.h @@ -7,8 +7,8 @@ // #pragma once +#include #include -#include "delay.h" #define u8 uint8_t #define u16 uint16_t @@ -85,7 +85,7 @@ #define CARD_SPICNTH_ENABLE (1<<7) // in byte 1, i.e. 0x8000 #define CARD_SPICNTH_IRQ (1<<6) // in byte 1, i.e. 0x4000 -#define swiDelay(n) ioDelay(n) +#define swiDelay(n) ARM_WaitCycles((n) * 8) #define DMA_SRC(n) (*(vu32*)(0x10002004 + (n * 0x1c))) #define DMA_DEST(n) (*(vu32*)(0x10002008 + (n * 0x1c))) diff --git a/arm9/source/gamecart/protocol.c b/arm9/source/gamecart/protocol.c index db08305..e01fac5 100644 --- a/arm9/source/gamecart/protocol.c +++ b/arm9/source/gamecart/protocol.c @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include + #include "protocol.h" #include "timer.h" @@ -14,7 +16,6 @@ #include "protocol_ntr.h" #include "command_ctr.h" #include "command_ntr.h" -#include "delay.h" // could have been done better, but meh... #define REG_AESCNT (*(vu32*)0x10009000) @@ -177,7 +178,7 @@ void Cart_Secure_Init(u32 *buf, u32 *out) // if (!mac_valid) // ClearScreen(bottomScreen, RGB(255, 0, 0)); - ioDelay(0xF0000); + ARM_WaitCycles(0xF0000 * 8); CTR_SetSecKey(A0_Response); CTR_SetSecSeed(out, true); @@ -207,7 +208,7 @@ void Cart_Secure_Init(u32 *buf, u32 *out) for (int i = 0; i < 5; ++i) { CTR_SendCommand(A2_cmd, 4, 1, 0x701002C, &test); - ioDelay(0xF0000); + ARM_WaitCycles(0xF0000 * 8); } } diff --git a/arm9/source/gamecart/protocol_ctr.c b/arm9/source/gamecart/protocol_ctr.c index b86631b..4597036 100644 --- a/arm9/source/gamecart/protocol_ctr.c +++ b/arm9/source/gamecart/protocol_ctr.c @@ -2,10 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include + #include "protocol_ctr.h" #include "protocol.h" -#include "delay.h" #ifdef VERBOSE_COMMANDS #include "draw.h" #endif @@ -143,7 +144,7 @@ void CTR_SendCommand(const u32 command[4], u32 pageSize, u32 blocks, u32 latency // so we have to wait next data ready do { cardCtrl = REG_CTRCARDCNT; } while(!(cardCtrl & CTRCARD_DATA_READY)); // and this tiny delay is necessary - ioDelay(33); + ARM_WaitCycles(33 * 8); // pull ROM CS high REG_CTRCARDCNT = 0x10000000; REG_CTRCARDCNT = CTRKEY_PARAM | CTRCARD_ACTIVATE | CTRCARD_nRESET; diff --git a/arm9/source/gamecart/secure_ntr.c b/arm9/source/gamecart/secure_ntr.c index 79b5679..29f0ff6 100644 --- a/arm9/source/gamecart/secure_ntr.c +++ b/arm9/source/gamecart/secure_ntr.c @@ -23,7 +23,6 @@ #include "card_ntr.h" // #include "draw.h" #include "timer.h" -#include "delay.h" #define BSWAP32(val) ((((val >> 24) & 0xFF)) | (((val >> 16) & 0xFF) << 8) | (((val >> 8) & 0xFF) << 16) | ((val & 0xFF) << 24)) diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index ccce5d2..c56a0b5 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -417,10 +417,10 @@ void DrawDirContents(DirStruct* contents, u32 cursor, u32* scroll) { } u32 SdFormatMenu(const char* slabel) { - const u32 cluster_size_table[5] = { 0x0, 0x0, 0x4000, 0x8000, 0x10000 }; - const char* option_emunand_size[7] = { "No EmuNAND", "RedNAND size (min)", "GW EmuNAND size (full)", + static const u32 cluster_size_table[5] = { 0x0, 0x0, 0x4000, 0x8000, 0x10000 }; + static const char* option_emunand_size[7] = { "No EmuNAND", "RedNAND size (min)", "GW EmuNAND size (full)", "MultiNAND size (2x)", "MultiNAND size (3x)", "MultiNAND size (4x)", "User input..." }; - const char* option_cluster_size[4] = { "Auto", "16KB Clusters", "32KB Clusters", "64KB Clusters" }; + static const char* option_cluster_size[4] = { "Auto", "16KB Clusters", "32KB Clusters", "64KB Clusters" }; u32 sysnand_min_size_sectors = GetNandMinSizeSectors(NAND_SYSNAND); u64 sysnand_min_size_mb = ((sysnand_min_size_sectors * 0x200) + 0xFFFFF) / 0x100000; u64 sysnand_multi_size_mb = (align(sysnand_min_size_sectors + 1, 0x2000) * 0x200) / 0x100000; @@ -467,13 +467,13 @@ u32 SdFormatMenu(const char* slabel) { u32 emunand_offset = 1; u32 n_emunands = 1; if (emunand_size_mb >= 2 * sysnand_size_mb) { - const char* option_emunand_type[4] = { "RedNAND type (multi)", "RedNAND type (single)", "GW EmuNAND type", "Don't set up" }; + static const char* option_emunand_type[4] = { "RedNAND type (multi)", "RedNAND type (single)", "GW EmuNAND type", "Don't set up" }; user_select = ShowSelectPrompt(4, option_emunand_type, "Choose EmuNAND type to set up:"); if (user_select > 3) return 0; emunand_offset = (user_select == 3) ? 0 : 1; if (user_select == 1) n_emunands = 4; } else if (emunand_size_mb >= sysnand_size_mb) { - const char* option_emunand_type[3] = { "RedNAND type", "GW EmuNAND type", "Don't set up" }; + static const char* option_emunand_type[3] = { "RedNAND type", "GW EmuNAND type", "Don't set up" }; user_select = ShowSelectPrompt(3, option_emunand_type, "Choose EmuNAND type to set up:"); if (user_select > 2) return 0; emunand_offset = (user_select == 2) ? 0 : 1; // 0 -> GW EmuNAND @@ -748,7 +748,7 @@ u32 FileHexViewer(const char* path) { else if (dual_screen) ClearScreen(BOT_SCREEN, COLOR_STD_BG); else memcpy(BOT_SCREEN, bottom_cpy, SCREEN_SIZE_BOT); } else if (pad_state & BUTTON_X) { - const char* optionstr[3] = { "Go to offset", "Search for string", "Search for data" }; + static const char* optionstr[3] = { "Go to offset", "Search for string", "Search for data" }; u32 user_select = ShowSelectPrompt(3, optionstr, "Current offset: %08X\nSelect action:", (unsigned int) offset); if (user_select == 1) { // -> goto offset @@ -892,7 +892,7 @@ u32 CmacCalculator(const char* path) { pathstr, getbe64(cmac + 0), getbe64(cmac + 8), "CMAC verification: ", (identical) ? "passed!" : "failed!", (!identical) ? "\n \nFix CMAC in file?" : "") && - !identical && (WriteFileCmac(path, cmac) != 0)) { + !identical && (WriteFileCmac(path, cmac, true) != 0)) { ShowPrompt(false, "Fixing CMAC: failed!"); } } @@ -901,7 +901,7 @@ u32 CmacCalculator(const char* path) { if (ShowPrompt(!correct, "%s\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n%s%s%s", pathstr, "CMAC verification: ", (correct) ? "passed!" : "failed!", (!correct) ? "\n \nFix CMAC in file?" : "") && - !correct && (FixCmdCmac(path) != 0)) { + !correct && (FixCmdCmac(path, true) != 0)) { ShowPrompt(false, "Fixing CMAC: failed!"); } } @@ -1091,12 +1091,16 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan bool cryptable_inplace = ((encryptable||decryptable) && !in_output_path && (drvtype & DRV_FAT)); bool cia_buildable = (FTYPE_CIABUILD(filetype)); bool cia_buildable_legit = (FTYPE_CIABUILD_L(filetype)); + bool cia_installable = (FTYPE_CIAINSTALL(filetype)) && !(drvtype & DRV_CTRNAND) && + !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_ALIAS); bool cxi_dumpable = (FTYPE_CXIDUMP(filetype)); bool tik_buildable = (FTYPE_TIKBUILD(filetype)) && !in_output_path; - bool key_buildable = (FTYPE_KEYBUILD(filetype)) && !in_output_path && !((drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); + bool key_buildable = (FTYPE_KEYBUILD(filetype)) && !in_output_path && + !((drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); bool titleinfo = (FTYPE_TITLEINFO(filetype)); bool ciacheckable = (FTYPE_CIACHECK(filetype)); - bool renamable = (FTYPE_RENAMABLE(filetype)); + bool renamable = (FTYPE_RENAMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) && + !(drvtype & DRV_CTRNAND) && !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_IMAGE); bool trimable = (FTYPE_TRIMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) && !(drvtype & DRV_CTRNAND) && !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_IMAGE); bool transferable = (FTYPE_TRANSFERABLE(filetype) && IS_UNLOCKED && (drvtype & DRV_FAT)); @@ -1230,7 +1234,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan continue; } if (CheckFileCmac(path) == 0) n_success++; - else if (fix && (FixFileCmac(path) == 0)) n_fixed++; + else if (fix && (FixFileCmac(path, true) == 0)) n_fixed++; else { // on failure: set cursor on failed file *cursor = i; continue; @@ -1308,6 +1312,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan int cia_build = (cia_buildable) ? ++n_opt : -1; int cia_build_legit = (cia_buildable_legit) ? ++n_opt : -1; int cxi_dump = (cxi_dumpable) ? ++n_opt : -1; + int cia_install = (cia_installable) ? ++n_opt : -1; int tik_build_enc = (tik_buildable) ? ++n_opt : -1; int tik_build_dec = (tik_buildable) ? ++n_opt : -1; int key_build = (key_buildable) ? ++n_opt : -1; @@ -1340,6 +1345,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan if (cia_build > 0) optionstr[cia_build-1] = (cia_build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)"; if (cia_build_legit > 0) optionstr[cia_build_legit-1] = "Build CIA (legit)"; if (cxi_dump > 0) optionstr[cxi_dump-1] = "Dump CXI/NDS file"; + if (cia_install > 0) optionstr[cia_install-1] = "Install game file"; if (tik_build_enc > 0) optionstr[tik_build_enc-1] = "Build " TIKDB_NAME_ENC; if (tik_build_dec > 0) optionstr[tik_build_dec-1] = "Build " TIKDB_NAME_DEC; if (key_build > 0) optionstr[key_build-1] = "Build " KEYDB_NAME; @@ -1546,6 +1552,53 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan } return 0; } + else if (user_select == cia_install) { // -> install game file + bool to_emunand = false; + if (CheckVirtualDrive("E:")) { + optionstr[0] = "Install to SysNAND"; + optionstr[1] = "Install to EmuNAND"; + user_select = (int) ShowSelectPrompt(2, optionstr, (n_marked > 1) ? + "%s\n%(%lu files selected)" : "%s", pathstr, n_marked); + if (!user_select) return 0; + else to_emunand = (user_select == 2); + } + if ((n_marked > 1) && ShowPrompt(true, "Try to install all %lu selected files?", n_marked)) { + u32 n_success = 0; + u32 n_other = 0; + ShowString("Trying to install %lu files...", n_marked); + for (u32 i = 0; i < current_dir->n_entries; i++) { + const char* path = current_dir->entry[i].path; + if (!current_dir->entry[i].marked) + continue; + if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { + n_other++; + continue; + } + DrawDirContents(current_dir, (*cursor = i), scroll); + if (InstallGameFile(path, to_emunand) == 0) n_success++; + else { // on failure: show error, continue + char lpathstr[32+1]; + TruncateString(lpathstr, path, 32, 8); + if (ShowPrompt(true, "%s\nInstall failed\n \nContinue?", lpathstr)) continue; + else break; + } + current_dir->entry[i].marked = false; + } + if (n_other) { + ShowPrompt(false, "%lu/%lu files installed ok\n%lu/%lu not of same type", + n_success, n_marked, n_other, n_marked); + } else ShowPrompt(false, "%lu/%lu files installed ok", n_success, n_marked); + } else { + u32 ret = InstallGameFile(file_path, to_emunand); + ShowPrompt(false, "%s\nInstall %s", pathstr, (ret == 0) ? "success" : "failed"); + if ((ret != 0) && (filetype & (GAME_NCCH|GAME_NCSD)) && + ShowPrompt(true, "%s\nfile failed install.\n \nVerify now?", pathstr)) { + ShowPrompt(false, "%s\nVerification %s", pathstr, + (VerifyGameFile(file_path) == 0) ? "success" : "failed"); + } + } + return 0; + } else if (user_select == verify) { // -> verify game / nand file if ((n_marked > 1) && ShowPrompt(true, "Try to verify all %lu selected files?", n_marked)) { u32 n_success = 0; @@ -2329,7 +2382,7 @@ u32 GodMode(int entrypoint) { } else { // one level up u32 user_select = 1; if (curr_drvtype & DRV_SEARCH) { // special menu for search drive - const char* optionstr[2] = { "Open this folder", "Open containing folder" }; + static const char* optionstr[2] = { "Open this folder", "Open containing folder" }; char pathstr[32 + 1]; TruncateString(pathstr, curr_entry->path, 32, 8); user_select = ShowSelectPrompt(2, optionstr, "%s", pathstr); @@ -2490,7 +2543,7 @@ u32 GodMode(int entrypoint) { } else if ((curr_drvtype & DRV_CART) && (pad_state & BUTTON_Y)) { ShowPrompt(false, "Not allowed in gamecart drive"); } else if (pad_state & BUTTON_Y) { // paste files - const char* optionstr[2] = { "Copy path(s)", "Move path(s)" }; + static const char* optionstr[2] = { "Copy path(s)", "Move path(s)" }; char promptstr[64]; u32 flags = 0; u32 user_select; @@ -2542,7 +2595,7 @@ u32 GodMode(int entrypoint) { } } } else if (pad_state & BUTTON_Y) { // create an entry - const char* optionstr[] = { "Create a folder", "Create a dummy file" }; + static const char* optionstr[] = { "Create a folder", "Create a dummy file" }; u32 type = ShowSelectPrompt(2, optionstr, "Create a new entry here?\nSelect type."); if (type) { const char* typestr = (type == 1) ? "folder" : (type == 2) ? "file" : NULL; diff --git a/arm9/source/main.c b/arm9/source/main.c index 4d6e8d7..36be0c0 100644 --- a/arm9/source/main.c +++ b/arm9/source/main.c @@ -7,6 +7,7 @@ #include "hid.h" +SystemSHMEM *shmemGlobalBase; void main(int argc, char** argv, int entrypoint) { diff --git a/arm9/source/nand/nand.c b/arm9/source/nand/nand.c index 99c6634..8748a57 100644 --- a/arm9/source/nand/nand.c +++ b/arm9/source/nand/nand.c @@ -386,7 +386,7 @@ u32 ValidateSecretSector(u8* sector) // see: https://github.com/d0k3/GodMode9/blob/master/source/game/ncsd.c#L4 u32 ValidateNandNcsdHeader(NandNcsdHeader* header) { - u8 zeroes[16] = { 0 }; + static const u8 zeroes[16] = { 0 }; if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number (memcmp(header->partitions_fs_type, zeroes, 8) == 0) || header->mediaId) // prevent detection of cart NCSD images return 1; diff --git a/arm9/source/nand/sdmmc.c b/arm9/source/nand/sdmmc.c index 3654f98..ed9cf71 100644 --- a/arm9/source/nand/sdmmc.c +++ b/arm9/source/nand/sdmmc.c @@ -22,10 +22,10 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ +#include #include #include #include "timer.h" -#include "wait.h" #include "sdmmc.h" #define DATA32_SUPPORT @@ -469,7 +469,7 @@ int SD_Init() // We need to send at least 74 clock pulses. set_target(&handleSD); - wait(2 * 128 * 74); + ARM_WaitCycles(2 * 128 * 74); sdmmc_send_command(&handleSD,0,0); sdmmc_send_command(&handleSD,0x10408,0x1AA); diff --git a/arm9/source/nand/wait.h b/arm9/source/nand/wait.h deleted file mode 100644 index d050a24..0000000 --- a/arm9/source/nand/wait.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "common.h" - -void wait(u32 cycles); diff --git a/arm9/source/nand/wait.s b/arm9/source/nand/wait.s deleted file mode 100644 index 2647d90..0000000 --- a/arm9/source/nand/wait.s +++ /dev/null @@ -1,10 +0,0 @@ -.text -.arm -.align 4 - -.global wait -.type wait, %function -wait: - subs r0, r0, #4 - bcs wait - bx lr diff --git a/arm9/source/start.s b/arm9/source/start.s index 603c4da..547f11b 100644 --- a/arm9/source/start.s +++ b/arm9/source/start.s @@ -3,7 +3,7 @@ .arm #include -#include +#include #include #include "memmap.h" @@ -27,9 +27,9 @@ _start: strlo r2, [r0], #4 blo .LBSS_Clear - ldr r0, =BRF_WB_INV_DCACHE + ldr r0, =BFN_WRITEBACK_INVALIDATE_DCACHE blx r0 @ Writeback & Invalidate Data Cache - ldr r0, =BRF_INVALIDATE_ICACHE + ldr r0, =BFN_INVALIDATE_ICACHE blx r0 @ Invalidate Instruction Cache @ Disable caches / TCMs / MPU diff --git a/arm9/source/system/xrq.c b/arm9/source/system/xrq.c index 18a3f55..aa36af2 100644 --- a/arm9/source/system/xrq.c +++ b/arm9/source/system/xrq.c @@ -60,7 +60,7 @@ XRQ_DUMPDATAFUNC(u16, 4) XRQ_DUMPDATAFUNC(u32, 8) -const char *XRQ_Name[] = { +static const char *XRQ_Name[] = { "Reset", "Undefined", "SWI", "Prefetch Abort", "Data Abort", "Reserved", "IRQ", "FIQ" }; @@ -80,10 +80,9 @@ void XRQ_DumpRegisters(u32 xrq, u32 *regs) wstr += sprintf(wstr, "20%02lX-%02lX-%02lX %02lX:%02lX:%02lX\n \n", (u32) dstime.bcd_Y, (u32) dstime.bcd_M, (u32) dstime.bcd_D, (u32) dstime.bcd_h, (u32) dstime.bcd_m, (u32) dstime.bcd_s); - for (int i = 0; i < 8; i++) { - int i_ = i*2; + for (int i = 0; i < 16; i += 2) { wstr += sprintf(wstr, - "R%02d: %08lX | R%02d: %08lX\n", i_, regs[i_], i_+1, regs[i_+1]); + "R%02d: %08lX | R%02d: %08lX\n", i, regs[i], i+1, regs[i+1]); } wstr += sprintf(wstr, "CPSR: %08lX\n\n", regs[16]); @@ -93,7 +92,7 @@ void XRQ_DumpRegisters(u32 xrq, u32 *regs) u32 draw_x = (SCREEN_WIDTH_MAIN - draw_width) / 2; u32 draw_y = (SCREEN_HEIGHT - draw_height) / 2; u32 draw_y_upd = draw_y + draw_height - 10; - + ClearScreen(MAIN_SCREEN, COLOR_STD_BG); DrawStringF(MAIN_SCREEN, draw_x, draw_y, COLOR_STD_FONT, COLOR_STD_BG, dumpstr); @@ -109,12 +108,11 @@ void XRQ_DumpRegisters(u32 xrq, u32 *regs) pc = regs[15] & ~0xF; if (pc_dumpable(pc, &pc_lower, &pc_upper)) { wstr += sprintf(wstr, "Code:\n"); - wstr += XRQ_DumpData_u32(wstr, pc_lower, pc_upper); - /*if (regs[16] & SR_THUMB) { // no need to take Thumb code into account - wstr += XRQ_DumpData_u16(wstr, pc-PC_DUMPRAD, pc+PC_DUMPRAD); + if (regs[16] & SR_THUMB) { // need to take Thumb code into account + wstr += XRQ_DumpData_u16(wstr, pc_lower, pc_upper); } else { - wstr += XRQ_DumpData_u32(wstr, pc-PC_DUMPRAD, pc+PC_DUMPRAD); - }*/ + wstr += XRQ_DumpData_u32(wstr, pc_lower, pc_upper); + } } /* Draw QR Code */ @@ -127,7 +125,6 @@ void XRQ_DumpRegisters(u32 xrq, u32 *regs) DrawQrCode(ALT_SCREEN, qrcode); } - /* Reinitialize SD */ DrawStringF(MAIN_SCREEN, draw_x, draw_y_upd, COLOR_STD_FONT, COLOR_STD_BG, "%-29.29s", "Reinitializing SD card..."); @@ -145,18 +142,16 @@ void XRQ_DumpRegisters(u32 xrq, u32 *regs) DrawStringF(MAIN_SCREEN, draw_x, draw_y_upd, COLOR_STD_FONT, COLOR_STD_BG, "%-29.29s", "Dumping state to SD card..."); FileSetData(path, dumpstr, wstr - dumpstr, 0, true); - - + /* Deinit SD */ DeinitSDCardFS(); - + /* Done, wait for user power off */ DrawStringF(MAIN_SCREEN, draw_x, draw_y_upd, COLOR_STD_FONT, COLOR_STD_BG, "%-29.29s", "Press POWER to turn off"); while (!(InputWait(0) & BUTTON_POWER)); PowerOff(); - /* We will not return */ return; } diff --git a/arm9/source/system/xrq_handler.s b/arm9/source/system/xrq_handler.s index f017a5c..a35b49a 100644 --- a/arm9/source/system/xrq_handler.s +++ b/arm9/source/system/xrq_handler.s @@ -6,7 +6,6 @@ .arm #include -#include #include "memmap.h" .macro TRAP_ENTRY xrq_id diff --git a/arm9/source/utils/ctrtransfer.c b/arm9/source/utils/ctrtransfer.c index 9f4c6e3..7ac1c91 100644 --- a/arm9/source/utils/ctrtransfer.c +++ b/arm9/source/utils/ctrtransfer.c @@ -97,7 +97,7 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) { } // actual transfer - db files / titles - const char* dbnames[] = { "ticket.db", "certs.db", "title.db", "import.db", "tmp_t.db", "tmp_i.db" }; + static const char* dbnames[] = { "ticket.db", "certs.db", "title.db", "import.db", "tmp_t.db", "tmp_i.db" }; char path_to[32]; char path_from[32]; char path_dbs[32]; @@ -108,7 +108,7 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) { snprintf(path_from, 32, "7:/dbs/%s", dbnames[i]); PathDelete(path_to); PathCopy(path_dbs, path_from, &flags); - FixFileCmac(path_to); + FixFileCmac(path_to, true); } ShowString("Cleaning up titles, please wait..."); snprintf(path_to, 32, "%s/title", drv); diff --git a/arm9/source/utils/gameutil.c b/arm9/source/utils/gameutil.c index 7aaf04a..631de4b 100644 --- a/arm9/source/utils/gameutil.c +++ b/arm9/source/utils/gameutil.c @@ -1,4 +1,5 @@ #include "gameutil.h" +#include "nandcmac.h" #include "disadiff.h" #include "game.h" #include "nand.h" // so that we can trim NAND images @@ -234,7 +235,7 @@ u32 LoadCdnTicketFile(Ticket** ticket, const char* path_cnt) { u32 GetTmdContentPath(char* path_content, const char* path_tmd) { // get path to TMD first content - const u8 dlc_tid_high[] = { DLC_TID_HIGH }; + static const u8 dlc_tid_high[] = { DLC_TID_HIGH }; // content path string char* name_content; @@ -321,6 +322,7 @@ u32 VerifyTmdContent(const char* path, u64 offset, TmdContentChunk* chunk, const } u32 VerifyNcchFile(const char* path, u32 offset, u32 size) { + static bool cryptofix_always = false; bool cryptofix = false; NcchHeader ncch; NcchExtHeader exthdr; @@ -358,9 +360,15 @@ u32 VerifyNcchFile(const char* path, u32 offset, u32 size) { // disable crypto, try again cryptofix = true; fvx_lseek(&file, offset); - if ((GetNcchHeaders(&ncch, NULL, &exefs, &file, cryptofix) == 0) && - ShowPrompt(true, "%s\nError: Bad crypto flags\n \nAttempt to fix?", pathstr)) - borkedflags = true; + if (GetNcchHeaders(&ncch, NULL, &exefs, &file, cryptofix) == 0) { + if (cryptofix_always) borkedflags = true; + else { + const char* optionstr[3] = { "Attempt fix this time", "Attempt fix always", "Abort verification" }; + u32 user_select = ShowSelectPrompt(3, optionstr, "%s\nError: Bad crypto flags", pathstr); + if ((user_select == 1) || (user_select == 2)) borkedflags = true; + if (user_select == 2) cryptofix_always = true; + } + } } if (!borkedflags) { if (!offset) ShowPrompt(false, "%s\nError: Bad ExeFS header", pathstr); @@ -595,7 +603,7 @@ u32 VerifyCiaFile(const char* path) { } u32 VerifyTmdFile(const char* path, bool cdn) { - const u8 dlc_tid_high[] = { DLC_TID_HIGH }; + static const u8 dlc_tid_high[] = { DLC_TID_HIGH }; // path string char pathstr[32 + 1]; @@ -1069,7 +1077,7 @@ u32 CryptCiaFile(const char* orig, const char* dest, u16 crypto) { } u32 DecryptFirmFile(const char* orig, const char* dest) { - const u8 dec_magic[] = { 'D', 'E', 'C', '\0' }; // insert to decrypted firms + static const u8 dec_magic[] = { 'D', 'E', 'C', '\0' }; // insert to decrypted firms void* firm_buffer = (void*) malloc(FIRM_MAX_SIZE); if (!firm_buffer) return 1; @@ -1228,6 +1236,275 @@ u32 CryptGameFile(const char* path, bool inplace, bool encrypt) { return ret; } +u32 GetInstallPath(char* path, const char* drv, const u8* title_id, const u8* content_id, const char* str) { + static const u8 dlc_tid_high[] = { DLC_TID_HIGH }; + bool dlc = (memcmp(title_id, dlc_tid_high, sizeof(dlc_tid_high)) == 0); + u32 tid_high = getbe32(title_id); + u32 tid_low = getbe32(title_id + 4); + + if ((*drv == '2') || (*drv == '5')) // TWL titles need TWL title ID + tid_high = 0x00030000 | (tid_high&0xFF); + + if (content_id) { // app path + snprintf(path, 256, "%2.2s/title/%08lx/%08lx/content/%s%08lx.app", + drv, tid_high, tid_low, dlc ? "00000000/" : "", getbe32(content_id)); + } else if (str) { // other paths (TMD/CMD/Save) + snprintf(path, 256, "%2.2s/title/%08lx/%08lx/%s", + drv, tid_high, tid_low, str); + } else { // base path (useful for uninstall) + snprintf(path, 256, "%2.2s/title/%08lx/%08lx", + drv, tid_high, tid_low); + } + + return 0; +} + +u32 GetInstallSavePath(char* path, const char* drv, const u8* title_id) { + // generate the save path (thanks ihaveamac for system path) + if ((*drv == '1') || (*drv == '4')) { // ooof, system save + // get the id0 + u8 sd_keyy[16] __attribute__((aligned(4))); + char path_movable[32]; + u32 sha256sum[8]; + snprintf(path_movable, 32, "%2.2s/private/movable.sed", drv); + if (fvx_qread(path_movable, sd_keyy, 0x110, 0x10, NULL) != FR_OK) return 1; + memset(sd_keyy, 0x00, 16); + sha_quick(sha256sum, sd_keyy, 0x10, SHA256_MODE); + // build path + u32 tid_low = getbe32(title_id + 4); + snprintf(path, 128, "%2.2s/data/%08lx%08lx%08lx%08lx/sysdata/%08lx/00000000", + drv, sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3], + tid_low | 0x00020000); + return 0; + } else { // SD save, simple + return GetInstallPath(path, drv, title_id, NULL, "data/00000001.sav"); + } +} + +u32 InstallCiaContent(const char* drv, const char* path_content, u32 offset, u32 size, + TmdContentChunk* chunk, const u8* title_id, const u8* titlekey, bool cxi_fix) { + char dest[256]; + + // create destination path and ensure it exists + GetInstallPath(dest, drv, title_id, chunk->id, NULL); + fvx_rmkpath(dest); + + // open file(s) + FIL ofile; + FIL dfile; + FSIZE_t fsize; + UINT bytes_read, bytes_written; + if (fvx_open(&ofile, path_content, FA_READ | FA_OPEN_EXISTING) != FR_OK) + return 1; + fvx_lseek(&ofile, offset); + fsize = fvx_size(&ofile); + if (offset > fsize) return 1; + if (!size) size = fsize - offset; + if (fvx_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { + fvx_close(&ofile); + return 1; + } + + // ensure free space for destination file + if ((fvx_lseek(&dfile, size) != FR_OK) || + (fvx_tell(&dfile) != size) || + (fvx_lseek(&dfile, 0) != FR_OK)) { + fvx_close(&ofile); + fvx_close(&dfile); + fvx_unlink(dest); + return 1; + } + + // allocate buffer + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) { + fvx_close(&ofile); + fvx_close(&dfile); + fvx_unlink(dest); + return 1; + } + + // main loop starts here + u8 ctr_in[16]; + u8 ctr_out[16]; + u32 ret = 0; + bool cia_crypto = getbe16(chunk->type) & 0x1; + GetTmdCtr(ctr_in, chunk); + GetTmdCtr(ctr_out, chunk); + if (!ShowProgress(0, 0, path_content)) ret = 1; + for (u32 i = 0; (i < size) && (ret == 0); i += STD_BUFFER_SIZE) { + u32 read_bytes = min(STD_BUFFER_SIZE, (size - i)); + if (fvx_read(&ofile, buffer, read_bytes, &bytes_read) != FR_OK) ret = 1; + if (cia_crypto && (DecryptCiaContentSequential(buffer, read_bytes, ctr_in, titlekey) != 0)) ret = 1; + if ((i == 0) && cxi_fix && (SetNcchSdFlag(buffer) != 0)) ret = 1; + if (i == 0) sha_init(SHA256_MODE); + sha_update(buffer, read_bytes); + if (fvx_write(&dfile, buffer, read_bytes, &bytes_written) != FR_OK) ret = 1; + if ((read_bytes != bytes_read) || (bytes_read != bytes_written)) ret = 1; + if (!ShowProgress(offset + i + read_bytes, fsize, path_content)) ret = 1; + } + u8 hash[0x20]; + sha_get(hash); + + free(buffer); + fvx_close(&ofile); + fvx_close(&dfile); + + // did something go wrong? + if (ret != 0) fvx_unlink(dest); + + // chunk size / chunk hash + for (u32 i = 0; i < 8; i++) chunk->size[i] = (u8) (size >> (8*(7-i))); + memcpy(chunk->hash, hash, 0x20); + + return ret; +} + +u32 InstallCiaSystemData(CiaStub* cia, const char* drv) { + // this assumes contents already installed(!) + // we use hardcoded IDs for CMD (0x1), TMD (0x0), save (0x1/0x0) + TitleInfoEntry tie; + CmdHeader* cmd; + TicketCommon* ticket = &(cia->ticket); + TitleMetaData* tmd = &(cia->tmd); + TmdContentChunk* content_list = cia->content_list; + u32 content_count = getbe16(tmd->content_count); + u8* title_id = ticket->title_id; + + bool sdtie = ((*drv == 'A') || (*drv == 'B')); + bool syscmd = (((*drv == '1') || (*drv == '4')) || + (((*drv == '2') || (*drv == '5')) && (title_id[3] != 0x04))); + + char path_titledb[32]; + char path_ticketdb[32]; + char path_tmd[64]; + char path_cmd[64]; + + // sanity checks + if (content_count == 0) return 1; + if ((*drv != '1') && (*drv != '2') && (*drv != 'A') && + (*drv != '4') && (*drv != '5') && (*drv != 'B')) + return 1; + + // progress update + if (!ShowProgress(0, 0, "TMD/CMD/TiE/Ticket/Save")) return 1; + + // collect data for title info entry + char path_cnt0[256]; + u8 hdr_cnt0[0x600]; // we don't need more + NcchHeader* ncch = NULL; + NcchExtHeader* exthdr = NULL; + GetInstallPath(path_cnt0, drv, title_id, content_list->id, NULL); + if (fvx_qread(path_cnt0, hdr_cnt0, 0, 0x600, NULL) != FR_OK) + return 1; + if (ValidateNcchHeader((void*) hdr_cnt0) == 0) { + ncch = (void*) hdr_cnt0; + exthdr = (void*) (hdr_cnt0 + sizeof(NcchHeader)); + if (!(ncch->size_exthdr) || + (DecryptNcch((u8*) exthdr, NCCH_EXTHDR_OFFSET, 0x400, ncch, NULL) != 0)) + exthdr = NULL; + } + + // build title info entry + if ((ncch && (BuildTitleInfoEntryNcch(&tie, tmd, ncch, exthdr, sdtie) != 0)) || + (!ncch && (BuildTitleInfoEntryTwl(&tie, tmd, (TwlHeader*) (void*) hdr_cnt0) != 0))) + return 1; + + // build the cmd + cmd = BuildAllocCmdData(tmd); + if (!cmd) return 1; + + // generate all the paths + snprintf(path_titledb, 32, "%2.2s/dbs/title.db", + (*drv == '2') ? "1:" : *drv == '5' ? "4:" : drv); + snprintf(path_ticketdb, 32, "%2.2s/dbs/ticket.db", + ((*drv == 'A') || (*drv == '2')) ? "1:" : + ((*drv == 'B') || (*drv == '5')) ? "4:" : drv); + GetInstallPath(path_tmd, drv, title_id, NULL, "content/00000000.tmd"); + GetInstallPath(path_cmd, drv, title_id, NULL, "content/cmd/00000001.cmd"); + + // progress update + if (!ShowProgress(1, 5, "TMD/CMD")) return 1; + + // copy tmd & cmd + fvx_rmkpath(path_tmd); + fvx_rmkpath(path_cmd); + if ((fvx_qwrite(path_tmd, tmd, 0, TMD_SIZE_N(content_count), NULL) != FR_OK) || + (fvx_qwrite(path_cmd, cmd, 0, CMD_SIZE(cmd), NULL) != FR_OK)) { + free(cmd); + return 1; + } + free(cmd); // we don't need this anymore + + // generate savedata + if (exthdr && (exthdr->savedata_size)) { + char path_save[128]; + + // progress update + if (!ShowProgress(2, 5, "Savegame")) return 1; + + // generate the path + GetInstallSavePath(path_save, drv, title_id); + + // generate the save file, first check if it already exists + if (fvx_qsize(path_save) != exthdr->savedata_size) { + static const u8 zeroes[0x20] = { 0x00 }; + UINT bw; + FIL save; + fvx_rmkpath(path_save); + if (fvx_open(&save, path_save, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) + return 1; + if ((fvx_write(&save, zeroes, 0x20, &bw) != FR_OK) || (bw != 0x20)) + bw = 0; + fvx_lseek(&save, exthdr->savedata_size); + fvx_sync(&save); + fvx_close(&save); + if (bw != 0x20) return 1; + } + } + + // progress update + if (!ShowProgress(3, 5, "TitleDB update")) return 1; + + // write ticket and title databases + // ensure remounting the old mount path + char path_store[256] = { 0 }; + char* path_bak = NULL; + strncpy(path_store, GetMountPath(), 256); + if (*path_store) path_bak = path_store; + + // title database + if (!InitImgFS(path_titledb) || + ((AddTitleInfoEntryToDB("D:/partitionA.bin", title_id, &tie, true)) != 0)) { + InitImgFS(path_bak); + return 1; + } + + // progress update + if (!ShowProgress(4, 5, "TicketDB update")) return 1; + + // ticket database + if (!InitImgFS(path_ticketdb) || + ((AddTicketToDB("D:/partitionA.bin", title_id, (Ticket*) ticket, true)) != 0)) { + InitImgFS(path_bak); + return 1; + } + + // progress update + if (!ShowProgress(5, 5, "TMD/CMD/TiE/Ticket/Save")) return 1; + + // restore old mount path + InitImgFS(path_bak); + + // fix CMACs where required + if (!syscmd) { + cmd->unknown = 0xFFFFFFFE; // mark this as custom built + FixFileCmac(path_cmd, true); + } + + return 0; +} + u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset, u32 size, TmdContentChunk* chunk, const u8* titlekey, bool force_legit, bool cxi_fix, bool cdn_decrypt) { // crypto types / ctr @@ -1330,7 +1607,56 @@ u32 InsertCiaMeta(const char* path_cia, CiaMeta* meta) { return (res) ? 0 : 1; } -u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool force_legit, bool cdn, void* buffer) { +u32 InstallFromCiaFile(const char* path_cia, const char* path_dest) { + CiaInfo info; + u8 titlekey[16]; + + // start operation + if (!ShowProgress(0, 0, path_cia)) return 1; + + // load CIA stub from origin + CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); + if (!cia) return 1; + if ((LoadCiaStub(cia, path_cia) != 0) || + (GetCiaInfo(&info, &(cia->header)) != 0) || + (GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0)) { + free(cia); + return 1; + } + + // install CIA contents + u8* title_id = cia->tmd.title_id; + u32 content_count = getbe16(cia->tmd.content_count); + u64 next_offset = info.offset_content; + u8* cnt_index = cia->header.content_index; + for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) { + TmdContentChunk* chunk = &(cia->content_list[i]); + u64 size = getbe64(chunk->size); + u16 index = getbe16(chunk->index); + if (!(cnt_index[index/8] & (1 << (7-(index%8))))) continue; // don't try to install missing contents + if (InstallCiaContent(path_dest, path_cia, next_offset, size, + chunk, title_id, titlekey, false) != 0) { + free(cia); + return 1; + } + next_offset += size; + } + + // proactive fix for CIA console ID + memset(cia->ticket.console_id, 0x00, 4); + + // fix TMD hashes, install CIA system data + if ((FixTmdHashes(&(cia->tmd)) != 0) || + (InstallCiaSystemData(cia, path_dest) != 0)) { + free(cia); + return 1; + } + + free(cia); + return 0; +} + +u32 BuildInstallFromTmdFileBuffered(const char* path_tmd, const char* path_dest, bool force_legit, bool cdn, void* buffer, bool install) { const u8 dlc_tid_high[] = { DLC_TID_HIGH }; CiaStub* cia = (CiaStub*) buffer; @@ -1387,7 +1713,7 @@ u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool // check the tickets' console id, warn if it isn't zero if (copy && getbe32(ticket_tmp->console_id)) { static u32 default_action = 0; - const char* optionstr[2] = + static const char* optionstr[2] = {"Use generic ticket (not legit)", "Use personalized ticket (legit)"}; if (!default_action) { default_action = ShowSelectPrompt(2, optionstr, @@ -1456,58 +1782,78 @@ u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool } } - // insert contents + // insert / install contents u8 titlekey[16] = { 0xFF }; if ((GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0) && force_legit) return 1; - if (WriteCiaStub(cia, path_cia) != 0) return 1; + if (!install && (WriteCiaStub(cia, path_dest) != 0)) return 1; for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) { TmdContentChunk* chunk = &(content_list[i]); if (present[i / 8] & (1 << (i % 8))) { snprintf(name_content, 256 - (name_content - path_content), (cdn) ? "%08lx" : (dlc && !cdn) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id)); - if (InsertCiaContent(path_cia, path_content, 0, (u32) getbe64(chunk->size), chunk, titlekey, force_legit, false, cdn) != 0) { + if (!install && (InsertCiaContent(path_dest, path_content, 0, (u32) getbe64(chunk->size), + chunk, titlekey, force_legit, false, cdn) != 0)) { ShowPrompt(false, "ID %016llX.%08lX\nInsert content failed", getbe64(title_id), getbe32(chunk->id)); return 1; } + if (install && (InstallCiaContent(path_dest, path_content, 0, (u32) getbe64(chunk->size), + chunk, title_id, titlekey, false) != 0)) { + ShowPrompt(false, "ID %016llX.%08lX\nInstall content failed", getbe64(title_id), getbe32(chunk->id)); + return 1; + } } } // try to build & insert meta, but ignore result - CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); - if (meta) { - if (content_count && cdn) { - if (!force_legit || !(getbe16(content_list->type) & 0x01)) { - CiaInfo info; - GetCiaInfo(&info, &(cia->header)); - if ((LoadNcchMeta(meta, path_cia, info.offset_content) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) + if (!install) { + CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); + if (meta) { + if (content_count && cdn) { + if (!force_legit || !(getbe16(content_list->type) & 0x01)) { + CiaInfo info; + GetCiaInfo(&info, &(cia->header)); + if ((LoadNcchMeta(meta, path_dest, info.offset_content) == 0) && (InsertCiaMeta(path_dest, meta) == 0)) + cia->header.size_meta = CIA_META_SIZE; + } + } else if (content_count) { + snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(content_list->id)); + if ((LoadNcchMeta(meta, path_content, 0) == 0) && (InsertCiaMeta(path_dest, meta) == 0)) cia->header.size_meta = CIA_META_SIZE; } - } else if (content_count) { - snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(content_list->id)); - if ((LoadNcchMeta(meta, path_content, 0) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) - cia->header.size_meta = CIA_META_SIZE; + free(meta); } - free(meta); } // write the CIA stub (take #2) - if ((FixTmdHashes(tmd) != 0) || (WriteCiaStub(cia, path_cia) != 0)) + if ((FixTmdHashes(tmd) != 0) || + (!install && (WriteCiaStub(cia, path_dest) != 0)) || + (install && (InstallCiaSystemData(cia, path_dest) != 0))) return 1; return 0; } -u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_legit, bool cdn) { +u32 InstallFromTmdFile(const char* path_tmd, const char* path_dest) { void* buffer = (void*) malloc(sizeof(CiaStub)); if (!buffer) return 1; - u32 ret = BuildCiaFromTmdFileBuffered(path_tmd, path_cia, force_legit, cdn, buffer); + u32 ret = BuildInstallFromTmdFileBuffered(path_tmd, path_dest, false, true, buffer, true); free(buffer); return ret; } -u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) { +u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_dest, bool force_legit, bool cdn) { + void* buffer = (void*) malloc(sizeof(CiaStub)); + if (!buffer) return 1; + + u32 ret = BuildInstallFromTmdFileBuffered(path_tmd, path_dest, force_legit, cdn, buffer, true); + + free(buffer); + return ret; +} + +u32 BuildInstallFromNcchFile(const char* path_ncch, const char* path_dest, bool install) { NcchExtHeader exthdr; NcchHeader ncch; u8 title_id[8]; @@ -1536,32 +1882,36 @@ u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) { (BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) || (BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, 0)) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || - (WriteCiaStub(cia, path_cia) != 0)) { + (!install && (WriteCiaStub(cia, path_dest) != 0))) { free(cia); return 1; } - // insert NCCH content + // insert / install NCCH content TmdContentChunk* chunk = cia->content_list; memset(chunk, 0, sizeof(TmdContentChunk)); // nothing else to do - if (InsertCiaContent(path_cia, path_ncch, 0, 0, chunk, NULL, false, true, false) != 0) { + if ((!install && (InsertCiaContent(path_dest, path_ncch, 0, 0, chunk, NULL, false, true, false) != 0)) || + (install && (InstallCiaContent(path_dest, path_ncch, 0, 0, chunk, title_id, NULL, true) != 0))) { free(cia); return 1; } // optional stuff (proper titlekey / meta data) - CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); - if (meta && has_exthdr && (BuildCiaMeta(meta, &exthdr, NULL) == 0) && - (LoadExeFsFile(meta->smdh, path_ncch, 0, "icon", sizeof(meta->smdh), NULL) == 0) && - (InsertCiaMeta(path_cia, meta) == 0)) - cia->header.size_meta = CIA_META_SIZE; - free(meta); + if (!install) { + CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); + if (meta && has_exthdr && (BuildCiaMeta(meta, &exthdr, NULL) == 0) && + (LoadExeFsFile(meta->smdh, path_ncch, 0, "icon", sizeof(meta->smdh), NULL) == 0) && + (InsertCiaMeta(path_dest, meta) == 0)) + cia->header.size_meta = CIA_META_SIZE; + free(meta); + } // write the CIA stub (take #2) FindTitleKey((Ticket*)(&cia->ticket), title_id); if ((FixTmdHashes(&(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || - (WriteCiaStub(cia, path_cia) != 0)) { + (!install && (WriteCiaStub(cia, path_dest) != 0)) || + (install && (InstallCiaSystemData(cia, path_dest) != 0))) { free(cia); return 1; } @@ -1570,7 +1920,7 @@ u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) { return 0; } -u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { +u32 BuildInstallFromNcsdFile(const char* path_ncsd, const char* path_dest, bool install) { NcchExtHeader exthdr; NcsdHeader ncsd; NcchHeader ncch; @@ -1602,12 +1952,12 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { (BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) || (BuildFakeTmd(&(cia->tmd), title_id, content_count, save_size, 0)) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || - (WriteCiaStub(cia, path_cia) != 0)) { + (!install && (WriteCiaStub(cia, path_dest) != 0))) { free(cia); return 1; } - // insert NCSD content + // insert / install NCSD content TmdContentChunk* chunk = cia->content_list; for (u32 i = 0; i < 3; i++) { NcchPartition* partition = ncsd.partitions + i; @@ -1615,20 +1965,26 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { u32 size = partition->size * NCSD_MEDIA_UNIT; if (!size) continue; memset(chunk, 0, sizeof(TmdContentChunk)); - chunk->id[3] = chunk->index[1] = i; - if (InsertCiaContent(path_cia, path_ncsd, offset, size, chunk++, NULL, false, (i == 0), false) != 0) { + chunk->id[3] = i; + chunk->index[1] = i; + if ((!install && (InsertCiaContent(path_dest, path_ncsd, + offset, size, chunk++, NULL, false, (i == 0), false) != 0)) || + (install && (InstallCiaContent(path_dest, path_ncsd, + offset, size, chunk++, title_id, NULL, (i == 0)) != 0))) { free(cia); return 1; } } // optional stuff (proper titlekey / meta data) - CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); - if (meta && (BuildCiaMeta(meta, &exthdr, NULL) == 0) && - (LoadExeFsFile(meta->smdh, path_ncsd, NCSD_CNT0_OFFSET, "icon", sizeof(meta->smdh), NULL) == 0) && - (InsertCiaMeta(path_cia, meta) == 0)) - cia->header.size_meta = CIA_META_SIZE; - if (meta) free(meta); + if (!install) { + CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); + if (meta && (BuildCiaMeta(meta, &exthdr, NULL) == 0) && + (LoadExeFsFile(meta->smdh, path_ncsd, NCSD_CNT0_OFFSET, "icon", sizeof(meta->smdh), NULL) == 0) && + (InsertCiaMeta(path_dest, meta) == 0)) + cia->header.size_meta = CIA_META_SIZE; + if (meta) free(meta); + } // update title version from cart header (yeah, that's a bit hacky) u16 title_version; @@ -1644,7 +2000,8 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { FindTitleKey((Ticket*)&(cia->ticket), title_id); if ((FixTmdHashes(&(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || - (WriteCiaStub(cia, path_cia) != 0)) { + (!install && (WriteCiaStub(cia, path_dest) != 0)) || + (install && (InstallCiaSystemData(cia, path_dest) != 0))) { free(cia); return 1; } @@ -1653,7 +2010,7 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { return 0; } -u32 BuildCiaFromNdsFile(const char* path_nds, const char* path_cia) { +u32 BuildInstallFromNdsFile(const char* path_nds, const char* path_dest, bool install) { TwlHeader twl; u8 title_id[8]; u32 save_size = 0; @@ -1673,12 +2030,12 @@ u32 BuildCiaFromNdsFile(const char* path_nds, const char* path_cia) { // some basic sanity checks // see: https://problemkaputt.de/gbatek.htm#dsicartridgeheader // (gamecart dumps are not allowed) - u8 tidhigh_dsiware[4] = { 0x00, 0x03, 0x00, 0x04 }; + static const u8 tidhigh_dsiware[4] = { 0x00, 0x03, 0x00, 0x04 }; if ((memcmp(title_id, tidhigh_dsiware, 3) != 0) || !title_id[3]) return 1; // convert DSi title ID to 3DS title ID - u8 tidhigh_3ds[4] = { 0x00, 0x04, 0x80, 0x04 }; + static const u8 tidhigh_3ds[4] = { 0x00, 0x04, 0x80, 0x04 }; memcpy(title_id, tidhigh_3ds, 3); // build the CIA stub @@ -1690,15 +2047,16 @@ u32 BuildCiaFromNdsFile(const char* path_nds, const char* path_cia) { (BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) || (BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, privsave_size)) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || - (WriteCiaStub(cia, path_cia) != 0)) { + (!install && (WriteCiaStub(cia, path_dest) != 0))) { free(cia); return 1; } - // insert NDS content + // insert / install NDS content TmdContentChunk* chunk = cia->content_list; memset(chunk, 0, sizeof(TmdContentChunk)); // nothing else to do - if (InsertCiaContent(path_cia, path_nds, 0, 0, chunk, NULL, false, false, false) != 0) { + if ((!install && (InsertCiaContent(path_dest, path_nds, 0, 0, chunk, NULL, false, false, false) != 0)) || + (install && (InstallCiaContent(path_dest, path_nds, 0, 0, chunk, title_id, NULL, false) != 0))) { free(cia); return 1; } @@ -1707,7 +2065,8 @@ u32 BuildCiaFromNdsFile(const char* path_nds, const char* path_cia) { FindTitleKey((Ticket*)(&cia->ticket), title_id); if ((FixTmdHashes(&(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || - (WriteCiaStub(cia, path_cia) != 0)) { + (!install && (WriteCiaStub(cia, path_dest) != 0)) || + (install && (InstallCiaSystemData(cia, path_dest) != 0))) { free(cia); return 1; } @@ -1747,11 +2106,11 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) { if (filetype & GAME_TMD) ret = BuildCiaFromTmdFile(path, dest, force_legit, filetype & FLAG_NUSCDN); else if (filetype & GAME_NCCH) - ret = BuildCiaFromNcchFile(path, dest); + ret = BuildInstallFromNcchFile(path, dest, false); else if (filetype & GAME_NCSD) - ret = BuildCiaFromNcsdFile(path, dest); + ret = BuildInstallFromNcsdFile(path, dest, false); else if ((filetype & GAME_NDS) && (filetype & FLAG_DSIW)) - ret = BuildCiaFromNdsFile(path, dest); + ret = BuildInstallFromNdsFile(path, dest, false); else ret = 1; if (ret != 0) // try to get rid of the borked file @@ -1760,6 +2119,86 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) { return ret; } +u64 GetGameFileTitleId(const char* path) { + u64 filetype = IdentifyFileType(path); + u64 tid64 = 0; + + if (filetype & GAME_CIA) { + CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); + if (!cia) return 0; + if (LoadCiaStub(cia, path) == 0) + tid64 = getbe64(cia->tmd.title_id); + free(cia); + } else if (filetype & GAME_TMD) { + TitleMetaData* tmd = (TitleMetaData*) malloc(TMD_SIZE_MAX); + if (!tmd) return 0; + if (LoadTmdFile(tmd, path) == 0) + tid64 = getbe64(tmd->title_id); + free(tmd); + } else if (filetype & GAME_NCCH) { + NcchHeader ncch; + if (LoadNcchHeaders(&ncch, NULL, NULL, path, 0) == 0) + tid64 = ncch.partitionId; + } else if (filetype & GAME_NCSD) { + NcsdHeader ncsd; + if (LoadNcsdHeader(&ncsd, path) == 0) + tid64 = ncsd.mediaId; + } else if ((filetype & GAME_NDS) && (filetype & FLAG_DSIW)) { + TwlHeader twl; + if (fvx_qread(path, &twl, 0, sizeof(TwlHeader), NULL) == FR_OK) + tid64 = 0x0004800000000000ull | (twl.title_id & 0xFFFFFFFFFFull); + } + + return tid64; +} + +u32 InstallGameFile(const char* path, bool to_emunand) { + const char* drv; + u64 filetype = IdentifyFileType(path); + u32 ret = 0; + + // find out the destination + bool to_sd = false; + bool to_twl = false; + u64 tid64 = GetGameFileTitleId(path); + if (!tid64) return 1; + if (((tid64 >> 32) & 0x8000) || (filetype & GAME_NDS)) + to_twl = true; + else if (!((tid64 >> 32) & 0x10)) + to_sd = true; + + // does the title.db exist? + if ((to_sd && !fvx_qsize(to_emunand ? "B:/dbs/title.db" : "A:/dbs/title.db")) || + (!to_sd && !fvx_qsize(to_emunand ? "4:/dbs/title.db" : "1:/dbs/title.db"))) + return 1; + + // now we know the correct drive + drv = to_emunand ? (to_sd ? "B:" : to_twl ? "5:" : "4:") : + (to_sd ? "A:" : to_twl ? "2:" : "1:"); + + // check permissions for SysNAND (this includes everything we need) + if (!CheckWritePermissions(to_emunand ? "4:" : "1:")) return 1; + + // install game file + if (filetype & GAME_CIA) + ret = InstallFromCiaFile(path, drv); + else if ((filetype & GAME_TMD) && (filetype & FLAG_NUSCDN)) + ret = InstallFromTmdFile(path, drv); + else if (filetype & GAME_NCCH) + ret = BuildInstallFromNcchFile(path, drv, true); + else if (filetype & GAME_NCSD) + ret = BuildInstallFromNcsdFile(path, drv, true); + else if ((filetype & GAME_NDS) && (filetype & FLAG_DSIW)) + ret = BuildInstallFromNdsFile(path, drv, true); + else ret = 1; + + // we have no clue what to do on failure + // if (ret != 0) ... + // maybe just uninstall? + + return ret; +} + // this has very limited uses right now u32 DumpCxiSrlFromTmdFile(const char* path) { u64 filetype = 0; @@ -1968,7 +2407,7 @@ u32 TrimGameFile(const char* path) { u32 LoadSmdhFromGameFile(const char* path, Smdh* smdh) { u64 filetype = IdentifyFileType(path); - + if (filetype & GAME_SMDH) { // SMDH file UINT btr; if ((fvx_qread(path, smdh, 0, sizeof(Smdh), &btr) == FR_OK) || (btr == sizeof(Smdh))) return 0; @@ -1977,8 +2416,7 @@ u32 LoadSmdhFromGameFile(const char* path, Smdh* smdh) { } else if (filetype & GAME_NCSD) { // NCSD file if (LoadExeFsFile(smdh, path, NCSD_CNT0_OFFSET, "icon", sizeof(Smdh), NULL) == 0) return 0; } else if (filetype & GAME_CIA) { // CIA file - CiaInfo info; - + CiaInfo info; if ((fvx_qread(path, &info, 0, 0x20, NULL) != FR_OK) || (GetCiaInfo(&info, (CiaHeader*) &info) != 0)) return 1; if ((info.offset_meta) && (fvx_qread(path, smdh, info.offset_meta + 0x400, sizeof(Smdh), NULL) == FR_OK)) return 0; @@ -2000,7 +2438,7 @@ u32 LoadSmdhFromGameFile(const char* path, Smdh* smdh) { } u32 ShowSmdhTitleInfo(Smdh* smdh, u16* screen) { - const u8 smdh_magic[] = { SMDH_MAGIC }; + static const u8 smdh_magic[] = { SMDH_MAGIC }; const u32 lwrap = 24; u16 icon[SMDH_SIZE_ICON_BIG / sizeof(u16)]; char desc_l[SMDH_SIZE_DESC_LONG+1]; @@ -2046,7 +2484,7 @@ u32 ShowGameFileTitleInfoF(const char* path, u16* screen, bool clear) { if (GetTmdContentPath(path_content, path) != 0) return 1; path = path_content; } - + void* buffer = (void*) malloc(max(sizeof(Smdh), sizeof(TwlIconData))); Smdh* smdh = (Smdh*) buffer; TwlIconData* twl_icon = (TwlIconData*) buffer; @@ -2198,8 +2636,8 @@ u32 BuildNcchInfoXorpads(const char* destdir, const char* path) { } u32 GetHealthAndSafetyPaths(const char* drv, char* path_cxi, char* path_bak) { - const u32 tidlow_hs_o3ds[] = { 0x00020300, 0x00021300, 0x00022300, 0, 0x00026300, 0x00027300, 0x00028300 }; - const u32 tidlow_hs_n3ds[] = { 0x20020300, 0x20021300, 0x20022300, 0, 0, 0x20027300, 0 }; + static const u32 tidlow_hs_o3ds[] = { 0x00020300, 0x00021300, 0x00022300, 0, 0x00026300, 0x00027300, 0x00028300 }; + static const u32 tidlow_hs_n3ds[] = { 0x20020300, 0x20021300, 0x20022300, 0, 0, 0x20027300, 0 }; // get H&S title id low u32 tidlow_hs = 0; diff --git a/arm9/source/utils/gameutil.h b/arm9/source/utils/gameutil.h index c315c44..f2c9379 100644 --- a/arm9/source/utils/gameutil.h +++ b/arm9/source/utils/gameutil.h @@ -6,6 +6,7 @@ u32 VerifyGameFile(const char* path); u32 CheckEncryptedGameFile(const char* path); u32 CryptGameFile(const char* path, bool inplace, bool encrypt); u32 BuildCiaFromGameFile(const char* path, bool force_legit); +u32 InstallGameFile(const char* path, bool to_emunand); u32 DumpCxiSrlFromTmdFile(const char* path); u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr); u32 CompressCode(const char* path, const char* path_out); diff --git a/arm9/source/utils/nandcmac.c b/arm9/source/utils/nandcmac.c index 346f28b..6ef5ff5 100644 --- a/arm9/source/utils/nandcmac.c +++ b/arm9/source/utils/nandcmac.c @@ -68,7 +68,7 @@ u32 SetupSlot0x30(char drv) { } u32 LocateAgbSaveSdBottomSlot(const char* path, AgbSaveHeader* agbsave) { - const u32 save_sizes[] = { + static const u32 save_sizes[] = { GBASAVE_EEPROM_512, GBASAVE_EEPROM_8K, GBASAVE_SRAM_32K, @@ -130,7 +130,7 @@ u32 CheckCmacPath(const char* path) { return (CalculateFileCmac(path, NULL)) ? 0 : 1; } -u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write) { +u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write, bool check_perms) { u32 cmac_type = CalculateFileCmac(path, NULL); u32 offset = 0; @@ -141,7 +141,7 @@ u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write) { else if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1; // can't do that here else offset = 0x000; - if (do_write && !CheckWritePermissions(path)) return 1; + if (do_write && check_perms && !CheckWritePermissions(path)) return 1; if (!do_write) return (fvx_qread(path, cmac, offset, 0x10, NULL) != FR_OK) ? 1 : 0; else return (fvx_qwrite(path, cmac, offset, 0x10, NULL) != FR_OK) ? 1 : 0; } @@ -210,7 +210,7 @@ u32 CalculateFileCmac(const char* path, u8* cmac) { else if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1; else if (!cmac_type) return 1; - const u32 cmac_keyslot[] = { CMAC_KEYSLOT }; + static const u32 cmac_keyslot[] = { CMAC_KEYSLOT }; u8 hashdata[0x200] __attribute__((aligned(4))); u32 keyslot = cmac_keyslot[cmac_type]; u32 hashsize = 0; @@ -298,13 +298,13 @@ u32 CheckFileCmac(const char* path) { } else return 1; } -u32 FixFileCmac(const char* path) { +u32 FixFileCmac(const char* path, bool check_perms) { u32 cmac_type = CalculateFileCmac(path, NULL); if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) { - return FixCmdCmac(path); + return FixCmdCmac(path, check_perms); } else if (cmac_type) { u8 ccmac[16]; - return ((CalculateFileCmac(path, ccmac) == 0) && (WriteFileCmac(path, ccmac) == 0)) ? 0 : 1; + return ((CalculateFileCmac(path, ccmac) == 0) && (WriteFileCmac(path, ccmac, check_perms) == 0)) ? 0 : 1; } else return 1; } @@ -344,7 +344,7 @@ u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv) { return 0; } -u32 CheckFixCmdCmac(const char* path, bool fix) { +u32 CheckFixCmdCmac(const char* path, bool fix, bool check_perms) { u8 cmac[16] __attribute__((aligned(4))); u32 keyslot = ((*path == 'A') || (*path == 'B')) ? 0x30 : 0x0B; bool fixed = false; @@ -374,11 +374,18 @@ u32 CheckFixCmdCmac(const char* path, bool fix) { // read the full file to memory and check it (we may write it back later) if ((fvx_qread(path, cmd_data, 0, cmd_size, NULL) != FR_OK) || - (CheckCmdSize(cmd, cmd_size) != 0)) { + (CMD_SIZE(cmd) != cmd_size)) { free(cmd_data); return 1; } + // we abuse the unknown u32 to mark custom, unfinished CMDs + bool fix_missing = false; + if (cmd->unknown == 0xFFFFFFFE) { + fixed = true; + cmd->unknown = 0x1; + fix_missing = true; + } // now, check the CMAC@0x10 use_aeskey(keyslot); @@ -394,7 +401,7 @@ u32 CheckFixCmdCmac(const char* path, bool fix) { } // further checking will be more complicated - // set up pointers to cmd data (pointer arithemtic is hard) + // set up pointers to cmd data (pointer arithmetic is hard) u32 n_entries = cmd->n_entries; u32* cnt_id = (u32*) (cmd + 1); u8* cnt_cmac = (u8*) (cnt_id + cmd->n_entries + cmd->n_cmacs); @@ -406,8 +413,13 @@ u32 CheckFixCmdCmac(const char* path, bool fix) { if (*cnt_id == 0xFFFFFFFF) continue; // unavailable content snprintf(name_content, 32, "%s%08lX.app", (is_dlc) ? "00000000/" : "", *cnt_id); if (fvx_qread(path_content, hashdata, 0x100, 0x100, NULL) != FR_OK) { - free(cmd_data); - return 1; // failed to read content + if (fix_missing) { + *cnt_id = 0xFFFFFFFF; + continue; + } else { + free(cmd_data); + return 1; // failed to read content + } } memcpy(hashdata + 0x100, &cnt_idx, 4); memcpy(hashdata + 0x104, cnt_id, 4); @@ -427,7 +439,7 @@ u32 CheckFixCmdCmac(const char* path, bool fix) { } // if fixing is enabled, write back cmd file - if (fix && fixed && CheckWritePermissions(path) && + if (fix && fixed && (!check_perms || CheckWritePermissions(path)) && (fvx_qwrite(path, cmd_data, 0, cmd_size, NULL) != FR_OK)) { free(cmd_data); return 1; @@ -459,14 +471,14 @@ u32 RecursiveFixFileCmacWorker(char* path) { } else if (fno.fattrib & AM_DIR) { // directory, recurse through it if (RecursiveFixFileCmacWorker(path) != 0) err = 1; } else if (CheckCmacPath(path) == 0) { // file, try to fix the CMAC - if (FixFileCmac(path) != 0) err = 1; + if (FixFileCmac(path, true) != 0) err = 1; ShowString("%s\nFixing CMACs, please wait...", pathstr); } } f_closedir(&pdir); *(--fname) = '\0'; } else if (CheckCmacPath(path) == 0) // fix single file CMAC - return FixFileCmac(path); + return FixFileCmac(path, true); return err; } diff --git a/arm9/source/utils/nandcmac.h b/arm9/source/utils/nandcmac.h index 741c9a1..6d5c5e6 100644 --- a/arm9/source/utils/nandcmac.h +++ b/arm9/source/utils/nandcmac.h @@ -2,16 +2,16 @@ #include "common.h" -#define ReadFileCmac(path, cmac) ReadWriteFileCmac(path, cmac, false) -#define WriteFileCmac(path, cmac) ReadWriteFileCmac(path, cmac, true) -#define CheckCmdCmac(path) CheckFixCmdCmac(path, false) -#define FixCmdCmac(path) CheckFixCmdCmac(path, true) +#define ReadFileCmac(path, cmac) ReadWriteFileCmac(path, cmac, false, true) +#define WriteFileCmac(path, cmac, check_perms) ReadWriteFileCmac(path, cmac, true, check_perms) +#define CheckCmdCmac(path) CheckFixCmdCmac(path, false, true) +#define FixCmdCmac(path, check_perms) CheckFixCmdCmac(path, true, check_perms) u32 CheckCmacPath(const char* path); -u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write); +u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write, bool check_perms); u32 CalculateFileCmac(const char* path, u8* cmac); u32 CheckFileCmac(const char* path); -u32 FixFileCmac(const char* path); +u32 FixFileCmac(const char* path, bool check_perms); u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv); -u32 CheckFixCmdCmac(const char* path, bool fix); +u32 CheckFixCmdCmac(const char* path, bool fix, bool check_perms); u32 RecursiveFixFileCmac(const char* path); diff --git a/arm9/source/utils/nandutil.c b/arm9/source/utils/nandutil.c index 7a7b0c9..ed28070 100644 --- a/arm9/source/utils/nandutil.c +++ b/arm9/source/utils/nandutil.c @@ -607,7 +607,7 @@ u32 SafeInstallFirm(const char* path, u32 slots) { } u32 SafeInstallKeyDb(const char* path) { - const u8 perfect_sha[] = { KEYDB_PERFECT_HASH }; + static const u8 perfect_sha[] = { KEYDB_PERFECT_HASH }; u8 keydb[KEYDB_PERFECT_SIZE] __attribute__((aligned(4))); char pathstr[32 + 1]; // truncated path string diff --git a/arm9/source/utils/scripting.c b/arm9/source/utils/scripting.c index 423db9b..2d24d3f 100644 --- a/arm9/source/utils/scripting.c +++ b/arm9/source/utils/scripting.c @@ -110,6 +110,7 @@ typedef enum { CMD_ID_DECRYPT, CMD_ID_ENCRYPT, CMD_ID_BUILDCIA, + CMD_ID_INSTALL, CMD_ID_EXTRCODE, CMD_ID_CMPRCODE, CMD_ID_SDUMP, @@ -139,7 +140,7 @@ typedef struct { char content[_VAR_CNT_LEN]; } Gm9ScriptVar; -Gm9ScriptCmd cmd_list[] = { +static const Gm9ScriptCmd cmd_list[] = { { CMD_ID_NONE , "#" , 0, 0 }, // dummy entry { CMD_ID_NOT , _CMD_NOT , 0, 0 }, // inverts the output of the following command { CMD_ID_IF , _CMD_IF , 1, 0 }, // control flow commands at the top of the list @@ -183,9 +184,10 @@ Gm9ScriptCmd cmd_list[] = { { CMD_ID_DECRYPT , "decrypt" , 1, 0 }, { CMD_ID_ENCRYPT , "encrypt" , 1, 0 }, { CMD_ID_BUILDCIA, "buildcia", 1, _FLG('l') }, + { CMD_ID_INSTALL , "install" , 1, _FLG('e') }, { CMD_ID_EXTRCODE, "extrcode", 2, 0 }, { CMD_ID_CMPRCODE, "cmprcode", 2, 0 }, - { CMD_ID_SDUMP , "sdump", 1, _FLG('w') }, + { CMD_ID_SDUMP , "sdump" , 1, _FLG('w') }, { CMD_ID_APPLYIPS, "applyips", 3, 0 }, { CMD_ID_APPLYBPS, "applybps", 3, 0 }, { CMD_ID_APPLYBPM, "applybpm", 3, 0 }, @@ -532,7 +534,7 @@ bool expand_arg(char* argex, const char* arg, u32 len) { } cmd_id get_cmd_id(char* cmd, u32 len, u32 flags, u32 argc, char* err_str) { - Gm9ScriptCmd* cmd_entry = NULL; + const Gm9ScriptCmd* cmd_entry = NULL; for (u32 i = 0; i < (sizeof(cmd_list)/sizeof(Gm9ScriptCmd)); i++) { if (strncmp(cmd_list[i].cmd, cmd, len) == 0) { @@ -561,6 +563,7 @@ u32 get_flag(char* str, u32 len, char* err_str) { else if (strncmp(str, "--before", len) == 0) flag_char = 'b'; else if (strncmp(str, "--include_dirs", len) == 0) flag_char = 'd'; else if (strncmp(str, "--flip_endian", len) == 0) flag_char = 'e'; + else if (strncmp(str, "--to_emunand", len) == 0) flag_char = 'e'; else if (strncmp(str, "--first", len) == 0) flag_char = 'f'; else if (strncmp(str, "--hash", len) == 0) flag_char = 'h'; else if (strncmp(str, "--keysel", len) == 0) flag_char = 'k'; @@ -1326,6 +1329,10 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { ret = (BuildCiaFromGameFile(argv[0], (flags & _FLG('l'))) == 0); if (err_str) snprintf(err_str, _ERR_STR_LEN, "build CIA failed"); } + else if (id == CMD_ID_INSTALL) { + ret = (InstallGameFile(argv[0], (flags & _FLG('e'))) == 0); + if (err_str) snprintf(err_str, _ERR_STR_LEN, "install game failed"); + } else if (id == CMD_ID_EXTRCODE) { u64 filetype = IdentifyFileType(argv[0]); if (!FTYPE_HASCODE(filetype)) { diff --git a/arm9/source/virtual/vbdri.c b/arm9/source/virtual/vbdri.c index 5fdd7f5..499c062 100644 --- a/arm9/source/virtual/vbdri.c +++ b/arm9/source/virtual/vbdri.c @@ -4,6 +4,7 @@ #include "vdisadiff.h" #include "bdri.h" #include "vff.h" +#include "ui.h" #define VBDRI_MAX_ENTRIES 8192 // Completely arbitrary @@ -14,7 +15,9 @@ #define VFLAG_TICKDIR (VFLAG_UNKNOWN|VFLAG_HOMEBREW|VFLAG_ESHOP|VFLAG_SYSTEM) #define NAME_TIE "%016llX" +#define NAME_TIE_LEN 16 #define NAME_TIK "%016llX.%08lX.tik" // title id / console id +#define NAME_TIK_LEN (16 + 1 + 8 + 4) #define PART_PATH "D:/partitionA.bin" @@ -39,7 +42,6 @@ static u8* title_ids = NULL; static TickInfoEntry* tick_info = NULL; static u8* cached_entry = NULL; static int cache_index; -//static u32 cache_size; void DeinitVBDRIDrive(void) { free(title_ids); @@ -52,6 +54,38 @@ void DeinitVBDRIDrive(void) { cache_index = -1; } +bool SortVBDRITickets() { + if (!CheckVBDRIDrive() || !is_tickdb) + return false; + + if (tick_info) + return true; + + tick_info = (TickInfoEntry*) malloc(num_entries * sizeof(TickInfoEntry)); + if (!tick_info) + return false; + + ShowString("Sorting tickets, please wait ..."); + + for (u32 i = 0; i < num_entries - 1; i++) { + Ticket* ticket; + if (ReadTicketFromDB(PART_PATH, title_ids + (i * 8), &ticket) != 0) { + free(tick_info); + tick_info = NULL; + return false; + } + tick_info[i].type = (ticket->commonkey_idx > 1) ? 3 : + ((ValidateTicketSignature(ticket) != 0) ? 1 : ((ticket->commonkey_idx == 1) ? 2 : 0)); + tick_info[i].size = GetTicketSize(ticket); + memcpy(tick_info[i].console_id, ticket->console_id, 4); + free(ticket); + } + + ClearScreenF(true, false, COLOR_STD_BG); + + return true; +} + u64 InitVBDRIDrive(void) { // prerequisite: .db file mounted as virtual diff image u64 mount_state = CheckVDisaDiffDrive(); if (!(mount_state & SYS_DIFF)) return 0; @@ -67,34 +101,17 @@ u64 InitVBDRIDrive(void) { // prerequisite: .db file mounted as virtual diff ima return 0; } - if (is_tickdb) { - tick_info = (TickInfoEntry*) malloc(num_entries * sizeof(TickInfoEntry)); - if (!tick_info) { - DeinitVBDRIDrive(); - return 0; - } - - for (u32 i = 0; i < num_entries - 1; i++) { - Ticket* ticket; - if (ReadTicketFromDB(PART_PATH, title_ids + (i * 8), &ticket) != 0) { - DeinitVBDRIDrive(); - return 0; - } - tick_info[i].type = (ticket->commonkey_idx > 1) ? 3 : - ((ValidateTicketSignature(ticket) != 0) ? 1 : ((ticket->commonkey_idx == 1) ? 2 : 0)); - tick_info[i].size = GetTicketSize(ticket); - memcpy(tick_info[i].console_id, ticket->console_id, 4); - free(ticket); - } - } else if ((cached_entry = malloc(sizeof(TitleInfoEntry))) == NULL) + if (!is_tickdb && ((cached_entry = malloc(sizeof(TitleInfoEntry))) == NULL)) { + DeinitVBDRIDrive(); return 0; + } return mount_state; } u64 CheckVBDRIDrive(void) { u64 mount_state = CheckVDisaDiffDrive(); - return (title_ids && (mount_state & SYS_DIFF) && (!is_tickdb || ((mount_state & SYS_TICKDB) && tick_info))) ? + return (title_ids && (mount_state & SYS_DIFF) && (!is_tickdb || (mount_state & SYS_TICKDB))) ? mount_state : 0; } @@ -103,7 +120,7 @@ bool ReadVBDRIDir(VirtualFile* vfile, VirtualDir* vdir) { return false; if (vdir->flags & VFLAG_TICKDIR) { // ticket dir - if (!is_tickdb) + if (!is_tickdb || (!tick_info && !SortVBDRITickets())) return false; while (++vdir->index < (int) num_entries) { @@ -156,22 +173,22 @@ bool ReadVBDRIDir(VirtualFile* vfile, VirtualDir* vdir) { } bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) { - size_t len = strlen(path); - char buf[31]; - - strcpy(buf, path + len - (is_tickdb ? 30 : 17)); - if (is_tickdb && ((strcmp(buf + 26, ".tik") != 0) || buf[17] != '.')) - return false; - - for (char* ptr = buf + 1; ptr < buf + 17; ptr++) - *ptr = toupper(*ptr); + size_t path_len = strlen(path), buf_len = (is_tickdb ? NAME_TIK_LEN : NAME_TIE_LEN) + 2; u64 tid; - if (sscanf(buf, "/%016llX", &tid) != 1) return false; - if (tid == 0) + u32 console_id; + char c; + + char buf[buf_len]; + strcpy(buf, path + path_len - buf_len + 1); + + + if (( is_tickdb && (sscanf(buf, "/" NAME_TIK "%c", &tid, &console_id, &c) != 2)) || + (!is_tickdb && (sscanf(buf, "/" NAME_TIE "%c", &tid, &c) != 1)) || + (tid == 0)) return false; tid = getbe64((u8*)&tid); - + int entry_index = -1; for (u32 i = 0; i < num_entries; i++) { if ((entry_index == -1) && (*((u64*)(void*)(title_ids + 8 * i)) == 0)) @@ -189,7 +206,7 @@ bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) { u8* new_title_ids = realloc(title_ids, new_num_entries * 8); if (!new_title_ids) return false; - if (is_tickdb) { + if (tick_info) { TickInfoEntry* new_tick_info = realloc(tick_info, new_num_entries * sizeof(TickInfoEntry)); if (!new_tick_info) return false; @@ -213,7 +230,7 @@ bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) { memcpy(title_ids + entry_index * 8, &tid, 8); - if (is_tickdb) { + if (tick_info) { tick_info[entry_index].type = 3; tick_info[entry_index].size = TICKET_COMMON_SIZE; memset(tick_info[entry_index].console_id, 0, 4); @@ -250,7 +267,7 @@ int WriteVBDRIFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 count bool resize = false; if (offset + count > vfile->size) { - if (!is_tickdb) + if (!is_tickdb || (!tick_info && !SortVBDRITickets())) return false; vfile->size = offset + count; resize = true; @@ -295,7 +312,7 @@ int WriteVBDRIFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 count if (resize) tick_info[vfile->offset].size = vfile->size; - if (is_tickdb && ((offset <= 0x1F1 && offset + count > 0x1F1) || (cached_entry[0x1F1] == 0 && offset <= 0x104 && offset + count > 4))) + if (tick_info && ((offset <= 0x1F1 && offset + count > 0x1F1) || (cached_entry[0x1F1] == 0 && offset <= 0x104 && offset + count > 4))) tick_info[vfile->offset].type = (cached_entry[0x1F1] > 1) ? 3 : ((ValidateTicketSignature((Ticket*)(void*)cached_entry) != 0) ? 1 : ((cached_entry[0x1F1] == 1) ? 2 : 0)); diff --git a/arm9/source/virtual/vgame.c b/arm9/source/virtual/vgame.c index 544e9a5..798e99e 100644 --- a/arm9/source/virtual/vgame.c +++ b/arm9/source/virtual/vgame.c @@ -672,7 +672,7 @@ bool BuildVGameFirmDir(void) { } bool BuildVGameTadDir(void) { - const char* name_type[] = { NAME_TAD_TYPES }; + static const char* name_type[] = { NAME_TAD_TYPES }; VirtualFile* templates = templates_tad; u32 content_offset = 0; u32 n = 0; diff --git a/arm9/source/virtual/vmem.c b/arm9/source/virtual/vmem.c index c3935cb..b257c6f 100644 --- a/arm9/source/virtual/vmem.c +++ b/arm9/source/virtual/vmem.c @@ -21,11 +21,11 @@ #define HAS_OTP_KEY (HAS_BOOT9 || ((LoadKeyFromFile(NULL, 0x11, 'N', "OTP") == 0) && (LoadKeyFromFile(NULL , 0x11, 'I', "OTP") == 0))) // see: https://www.youtube.com/watch?v=wogNzUypLuI -u8 boot9_sha256[0x20] = { +static const u8 boot9_sha256[0x20] = { 0x2F, 0x88, 0x74, 0x4F, 0xEE, 0xD7, 0x17, 0x85, 0x63, 0x86, 0x40, 0x0A, 0x44, 0xBB, 0xA4, 0xB9, 0xCA, 0x62, 0xE7, 0x6A, 0x32, 0xC7, 0x15, 0xD4, 0xF3, 0x09, 0xC3, 0x99, 0xBF, 0x28, 0x16, 0x6F }; -u8 boot11_sha256[0x20] = { +static const u8 boot11_sha256[0x20] = { 0x74, 0xDA, 0xAC, 0xE1, 0xF8, 0x06, 0x7B, 0x66, 0xCC, 0x81, 0xFC, 0x30, 0x7A, 0x3F, 0xDB, 0x50, 0x9C, 0xBE, 0xDC, 0x32, 0xF9, 0x03, 0xAE, 0xBE, 0x90, 0x61, 0x44, 0xDE, 0xA7, 0xA0, 0x75, 0x12 }; diff --git a/common/arm.h b/common/arm.h index 53843ed..1c4d5c1 100644 --- a/common/arm.h +++ b/common/arm.h @@ -2,6 +2,8 @@ #include "types.h" +#include + /* Status Register flags */ #define SR_USR_MODE (0x10) #define SR_FIQ_MODE (0x11) @@ -69,12 +71,7 @@ #ifndef __ASSEMBLER__ -/* ARM Private Memory Region */ -#ifdef ARM11 - #define REG_ARM_PMR(off, type) ((volatile type*)(0x17E00000 + (off))) -#endif - - +// only accessible from ARM mode #define ARM_MCR(cp, op1, reg, crn, crm, op2) asm_v( \ "MCR " #cp ", " #op1 ", %[R], " #crn ", " #crm ", " #op2 "\n\t" \ :: [R] "r"(reg) : "memory","cc") @@ -91,44 +88,44 @@ "MRS %[R], " #cp "\n\t" \ : [R] "=r"(reg) :: "memory","cc") + +/* ARM Private Memory Region */ #ifdef ARM11 - #define ARM_CPS(m) asm_v("CPS " #m) - #define ARM_CPSID(m) asm_v("CPSID " #m) - #define ARM_CPSIE(m) asm_v("CPSIE " #m) + #define REG_ARM_PMR(off, type) ((volatile type*)(0x17E00000 + (off))) - /* - * An Instruction Synchronization Barrier (ISB) flushes the pipeline in the processor - * so that all instructions following the ISB are fetched from cache or memory - * after the ISB has been completed. - */ static inline void ARM_ISB(void) { - ARM_MCR(p15, 0, 0, c7, c5, 4); + ((void (*)(void))(BFN_INSTSYNCBARRIER))(); } - /* - * A Data Memory Barrier (DMB) ensures that all explicit memory accesses before - * the DMB instruction complete before any explicit memory accesses after the DMB instruction start. - */ static inline void ARM_DMB(void) { - ARM_MCR(p15, 0, 0, c7, c10, 5); + ((void (*)(void))(BFN_DATAMEMBARRIER))(); } - /* Wait For Interrupt */ static inline void ARM_WFI(void) { - asm_v("wfi\n\t"); + // replace with a bootrom call if + // switching to thumb is necessary + asm_v("wfi\n\t":::"memory"); } - /* Wait For Event */ static inline void ARM_WFE(void) { - asm_v("wfe\n\t"); + asm_v("wfe\n\t":::"memory"); // same as above } - /* Send Event */ static inline void ARM_SEV(void) { - asm_v("sev\n\t"); + asm_v("sev\n\t":::"memory"); // same as above + } + + /* Control Registers */ + static inline u32 ARM_GetCR(void) { + u32 cr; + ARM_MRC(p15, 0, cr, c1, c0, 0); + return cr; + } + + static inline void ARM_SetCR(u32 cr) { + ARM_MCR(p15, 0, cr, c1, c0, 0); } - /* Auxiliary Control Registers */ static inline u32 ARM_GetACR(void) { u32 acr; ARM_MRC(p15, 0, acr, c1, c0, 1); @@ -138,46 +135,15 @@ static inline void ARM_SetACR(u32 acr) { ARM_MCR(p15, 0, acr, c1, c0, 1); } -#endif +#endif /* * A Data Synchronization Barrier (DSB) completes when all * instructions before this instruction complete. */ static inline void ARM_DSB(void) { - ARM_MCR(p15, 0, 0, c7, c10, 4); -} - - -/* Control Registers */ -static inline u32 ARM_GetCR(void) { - u32 cr; - ARM_MRC(p15, 0, cr, c1, c0, 0); - return cr; -} - -static inline void ARM_SetCR(u32 cr) { - ARM_MCR(p15, 0, cr, c1, c0, 0); -} - -/* Thread ID Registers */ -static inline u32 ARM_GetTID(void) { - u32 pid; - #ifdef ARM9 - ARM_MRC(p15, 0, pid, c13, c0, 1); - #else - ARM_MRC(p15, 0, pid, c13, c0, 4); - #endif - return pid; -} - -static inline void ARM_SetTID(u32 pid) { - #ifdef ARM9 - ARM_MCR(p15, 0, pid, c13, c0, 1); - #else - ARM_MCR(p15, 0, pid, c13, c0, 4); - #endif + ((void (*)(void))(BFN_DATASYNCBARRIER))(); } /* CPU ID */ @@ -191,131 +157,61 @@ static inline u32 ARM_CoreID(void) { return id & 3; } -/* Status Register */ -static inline u32 ARM_GetCPSR(void) { - u32 sr; - ARM_MRS(sr, cpsr); - return sr; -} - -static inline void ARM_SetCPSR_c(u32 sr) { - ARM_MSR(cpsr_c, sr); -} - -static inline void ARM_DisableInterrupts(void) { - #ifdef ARM9 - ARM_SetCPSR_c(ARM_GetCPSR() | SR_NOINT); - #else - ARM_CPSID(if); - #endif -} - -static inline void ARM_EnableInterrupts(void) { - #ifdef ARM9 - ARM_SetCPSR_c(ARM_GetCPSR() & ~SR_NOINT); - #else - ARM_CPSIE(if); - #endif -} - +/* Status register management */ static inline u32 ARM_EnterCritical(void) { - u32 stat = ARM_GetCPSR(); - ARM_DisableInterrupts(); - return stat & SR_NOINT; + return ((u32 (*)(void))(BFN_ENTERCRITICALSECTION))(); } static inline void ARM_LeaveCritical(u32 stat) { - ARM_SetCPSR_c((ARM_GetCPSR() & ~SR_NOINT) | stat); + ((void (*)(u32))(BFN_LEAVECRITICALSECTION))(stat); } +static inline void ARM_DisableInterrupts(void) { + ARM_LeaveCritical(SR_NOINT); +} + +static inline void ARM_EnableInterrupts(void) { + ARM_LeaveCritical(0x00); +} /* Cache functions */ static inline void ARM_InvIC(void) { - #ifdef ARM9 - ARM_MCR(p15, 0, 0, c7, c5, 0); - #else - ARM_MCR(p15, 0, 0, c7, c7, 0); - #endif + ((void (*)(void))(BFN_INVALIDATE_ICACHE))(); } static inline void ARM_InvIC_Range(void *base, u32 len) { - u32 addr = (u32)base & ~0x1F; - len >>= 5; - - do { - #ifdef ARM9 - ARM_MCR(p15, 0, addr, c7, c5, 1); - #else - ARM_MCR(p15, 0, addr, c7, c7, 1); - #endif - addr += 0x20; - } while(len--); + ((void (*)(u32, u32))(BFN_INVALIDATE_ICACHE_RANGE))((u32)base, len); + #ifdef ARM11 // make sure to also invalidate the branch target cache + ((void (*)(u32, u32))(BFN_INVALIDATE_BT_CACHE_RANGE))((u32)base, len); + #endif } static inline void ARM_InvDC(void) { - ARM_MCR(p15, 0, 0, c7, c6, 0); + ((void (*)(void))(BFN_INVALIDATE_DCACHE))(); } static inline void ARM_InvDC_Range(void *base, u32 len) { - u32 addr = (u32)base & ~0x1F; - len >>= 5; - - do { - ARM_MCR(p15, 0, addr, c7, c6, 1); - addr += 0x20; - } while(len--); + ((void (*)(u32, u32))(BFN_INVALIDATE_DCACHE_RANGE))((u32)base, len); } static inline void ARM_WbDC(void) { - #ifdef ARM9 - u32 seg = 0, ind; - do { - ind = 0; - do { - ARM_MCR(p15, 0, seg | ind, c7, c10, 2); - ind += 0x20; - } while(ind < 0x400); - seg += 0x40000000; - } while(seg != 0); - #else - ARM_MCR(p15, 0, 0, c7, c10, 0); - #endif + ((void (*)(void))(BFN_WRITEBACK_DCACHE))(); } static inline void ARM_WbDC_Range(void *base, u32 len) { - u32 addr = (u32)base & ~0x1F; - len >>= 5; - - do { - ARM_MCR(p15, 0, addr, c7, c10, 1); - addr += 0x20; - } while(len--); + ((void (*)(u32, u32))(BFN_WRITEBACK_DCACHE_RANGE))((u32)base, len); } static inline void ARM_WbInvDC(void) { - #ifdef ARM9 - u32 seg = 0, ind; - do { - ind = 0; - do { - ARM_MCR(p15, 0, seg | ind, c7, c14, 2); - ind += 0x20; - } while(ind < 0x400); - seg += 0x40000000; - } while(seg != 0); - #else - ARM_MCR(p15, 0, 0, c7, c14, 0); - #endif + ((void (*)(void))(BFN_WRITEBACK_INVALIDATE_DCACHE))(); } static inline void ARM_WbInvDC_Range(void *base, u32 len) { - u32 addr = (u32)base & ~0x1F; - len >>= 5; + ((void (*)(u32, u32))(BFN_WRITEBACK_INVALIDATE_DCACHE_RANGE))((u32)base, len); +} - do { - ARM_MCR(p15, 0, addr, c7, c14, 1); - addr += 0x20; - } while(len--); +static inline void ARM_WaitCycles(u32 cycles) { + ((void (*)(u32))(BFN_WAITCYCLES))(cycles); } static inline void ARM_BKPT(void) { diff --git a/common/bfn.h b/common/bfn.h new file mode 100644 index 0000000..05570db --- /dev/null +++ b/common/bfn.h @@ -0,0 +1,135 @@ +#pragma once + +/* + Addresses and declarations of preexisting BootROM functions + All of these functions should follow the standard AAPCS convention +*/ + +#ifdef ARM9 + +// void waitCycles(u32 cycles) +// delays execution time by cycles +#define BFN_WAITCYCLES (0xFFFF0198) + +// void cpuSet(u32 val, u32 *dest, u32 count) +#define BFN_CPUSET (0xFFFF03A4) + +// void cpuCpy(const u32 *src, u32 *dest, u32 count) +#define BFN_CPUCPY (0xFFFF03F0) + +// u32 enterCriticalSection() +// disables interrupts and returns the old irq state +#define BFN_ENTERCRITICALSECTION (0xFFFF06EC) + +// void leaveCriticalSection(u32 irqstate) +// restores the old irq state +#define BFN_LEAVECRITICALSECTION (0xFFFF0700) + +// bool enableDCache() +// enables the data cache and returns the old dcache bit +#define BFN_ENABLE_DCACHE (0xFFFF0798) + +// bool disableDCache() +// disables the data cache +#define BFN_DISABLE_DCACHE (0xFFFF07B0) + +// bool setDCache(bool enable) +// toggles the data cache +#define BFN_SET_DCACHE (0xFFFF07C8) + +// void invalidateDCache() +// invalidates all data cache entries +#define BFN_INVALIDATE_DCACHE (0xFFFF07F0) + +// void writebackDCache() +// writes back all data cache entries +#define BFN_WRITEBACK_DCACHE (0xFFFF07FC) + +// void writebackInvalidateDCache() +// writes back and invalidates all data cache entries +#define BFN_WRITEBACK_INVALIDATE_DCACHE (0xFFFF0830) + +// void invalidateDCacheRange(u32 start, u32 len) +// invalidates data cache entries +#define BFN_INVALIDATE_DCACHE_RANGE (0xFFFF0868) + +// void writebackDCacheRange(u32 start, u32 len) +// writes back data cache entries +#define BFN_WRITEBACK_DCACHE_RANGE (0xFFFF0884) + +// void writebackInvalidateDCacheRange(u32 start, u32 len) +#define BFN_WRITEBACK_INVALIDATE_DCACHE_RANGE (0xFFFF08A8) + +// void dataSynchronizationBarrier() +#define BFN_DATASYNCBARRIER (0xFFFF096C) + +// bool enableICache() +#define BFN_ENABLE_ICACHE (0xFFFF0A5C) + +// bool disableICache() +#define BFN_DISABLE_ICACHE (0xFFFF0A74) + +// bool setICache(bool enable) +#define BFN_SET_ICACHE (0xFFFF0A8C) + +// void invalidateICache() +#define BFN_INVALIDATE_ICACHE (0xFFFF0AB4) + +// void invalidateICacheRange(u32 start, u32 len) +#define BFN_INVALIDATE_ICACHE_RANGE (0xFFFF0AC0) + +// void enableMPU() +#define BFN_ENABLE_MPU (0xFFFF0C38) + +// void disableMPU() +#define BFN_DISABLE_MPU (0xFFFF0C48) + +// void resetControlRegisters() +// set CR0 to its reset state (MPU & caches disabled, high vectors, TCMs enabled) +// invalidates both instruction and data caches (without previously writing back!!) +#define BFN_RESET_CRS (0xFFFF0C58) + +#else + +#define BFN_WAITCYCLES (0x00011A38) + +#define BFN_CPUSET (0x000116E4) +#define BFN_CPUCPY (0x00011730) + +#define BFN_ENTERCRITICALSECTION (0x00011AC4) +#define BFN_LEAVECRITICALSECTION (0x00011AD8) + +#define BFN_ENABLE_DCACHE (0x00011288) +#define BFN_DISABLE_DCACHE (0x000112A0) +#define BFN_SET_DCACHE (0x000112B8) + +#define BFN_INVALIDATE_DCACHE (0x000112E0) +#define BFN_WRITEBACK_DCACHE (0x000112EC) +#define BFN_WRITEBACK_INVALIDATE_DCACHE (0x00011320) + +#define BFN_INVALIDATE_DCACHE_RANGE (0x00011358) +#define BFN_WRITEBACK_DCACHE_RANGE (0x00011374) +#define BFN_WRITEBACK_INVALIDATE_DCACHE_RANGE (0x00011398) + +#define BFN_DATASYNCBARRIER (0x000113C0) + +#define BFN_DATAMEMBARRIER (0x000113E8) + +#define BFN_ENABLE_ICACHE (0x000113F4) +#define BFN_DISABLE_ICACHE (0x0001140C) +#define BFN_SET_ICACHE (0x00011424) + +// also invalidates the branch target cache in ARM11 +#define BFN_INVALIDATE_ICACHE (0x0001144C) + +// WARNING: DOES NOT INVALIDATE THE BRANCH TARGET CACHE +// NEEDS TO INVALIDATE IT AND FLUSH THE PREFETCH BUFFER +#define BFN_INVALIDATE_ICACHE_RANGE (0x00011458) + +// void instructionSynchronizationBarrier() +#define BFN_INSTSYNCBARRIER (0x00011490) + +// void invalidateBranchTargetCache() +#define BFN_INVALIDATE_BT_CACHE_RANGE (0x000114F4) + +#endif diff --git a/common/brf.h b/common/brf.h deleted file mode 100644 index fc6cc8d..0000000 --- a/common/brf.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#ifdef ARM9 - -/* None of these functions require a working stack */ -/* Assume R0-R3, R12 are always clobbered */ -#define BRF_INVALIDATE_DCACHE (0xFFFF07F0) -#define BRF_INVALIDATE_DCACHE_RANGE (0xFFFF0868) -#define BRF_WRITEBACK_DCACHE (0xFFFF07FC) -#define BRF_WRITEBACK_DCACHE_RANGE (0xFFFF0884) -#define BRF_WB_INV_DCACHE (0xFFFF0830) -#define BRF_WB_INV_DCACHE_RANGE (0xFFFF08A8) -#define BRF_INVALIDATE_ICACHE (0xFFFF0AB4) -#define BRF_INVALIDATE_ICACHE_RANGE (0xFFFF0AC0) -#define BRF_RESETCP15 (0xFFFF0C58) - -#else - -#endif diff --git a/common/common.h b/common/common.h index b0e456b..cf23116 100755 --- a/common/common.h +++ b/common/common.h @@ -29,9 +29,6 @@ #define abs(x) \ (((x) >= 0) ? (x) : -(x)) -#define int_sign(x) \ - (((x) > 0) - ((x) < 0)) - #define clamp(x, min, max) \ ((x) < (max) ? ((x) > (min) ? (x) : (min)) : (max)) @@ -61,10 +58,6 @@ #define assert(x) \ (!!(x) ? (void)0 : __builtin_trap()) -static inline void waitClks(unsigned clk) { - asm_v("1: subs %0, %0, #2\n\tbne 1b\n\t":"=r"(clk)::"memory","cc"); -} - #define STATIC_ASSERT(...) \ _Static_assert((__VA_ARGS__), #__VA_ARGS__) diff --git a/common/shmem.h b/common/shmem.h index 39b5255..e377e2f 100755 --- a/common/shmem.h +++ b/common/shmem.h @@ -36,13 +36,15 @@ typedef struct { #ifdef ARM9 #include +extern SystemSHMEM *shmemGlobalBase; + static inline SystemSHMEM *ARM_GetSHMEM(void) { - return (SystemSHMEM*)ARM_GetTID(); + return shmemGlobalBase; } static inline void ARM_InitSHMEM(void) { - ARM_SetTID(PXI_DoCMD(PXI_GET_SHMEM, NULL, 0)); + shmemGlobalBase = (SystemSHMEM*)PXI_DoCMD(PXI_GET_SHMEM, NULL, 0); } #endif