Merge branch 'thumb'

This commit is contained in:
d0k3 2020-08-02 15:43:28 +02:00
commit 8863979a99
69 changed files with 1237 additions and 571 deletions

View File

@ -21,7 +21,7 @@ VRAM_DATA := data
VRAM_FLAGS := --make-new --path-limit 99 --size-limit 262144 VRAM_FLAGS := --make-new --path-limit 99 --size-limit 262144
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
ifeq ($(TERM),) ifeq ($(TERM),cygwin)
PY3 := py -3 # Windows / CMD/PowerShell PY3 := py -3 # Windows / CMD/PowerShell
else else
PY3 := python3 # Windows / MSYS2 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 \ -g -Os -Wall -Wextra -Wcast-align -Wformat=2 -Wno-main \
-fomit-frame-pointer -ffast-math -std=gnu11 -MMD -MP \ -fomit-frame-pointer -ffast-math -std=gnu11 -MMD -MP \
-Wno-unused-function -Wno-format-truncation $(INCLUDE) -ffunction-sections -fdata-sections -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 ELF := arm9/arm9.elf arm11/arm11.elf
.PHONY: all firm vram0 elf release clean .PHONY: all firm vram0 elf release clean

View File

@ -5,7 +5,7 @@ TARGET := $(shell basename $(CURDIR))
SOURCE := source SOURCE := source
BUILD := build 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 INCDIRS := source
INCLUDE := $(foreach dir,$(INCDIRS),-I"$(shell pwd)/$(dir)") INCLUDE := $(foreach dir,$(INCDIRS),-I"$(shell pwd)/$(dir)")

View File

@ -4,21 +4,12 @@ ENTRY(__boot)
MEMORY MEMORY
{ {
AXIWRAM (RWX) : ORIGIN = 0x1FF80000, LENGTH = 128K AXIWRAM (RWX) : ORIGIN = 0x1FF80000, LENGTH = 96K
HIGHRAM (RWX) : ORIGIN = 0xFFFF0000, LENGTH = 4K HIGHRAM (RWX) : ORIGIN = 0xFFFF0000, LENGTH = 4K
} }
SECTIONS 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 : ALIGN(4K)
{ {
__text_pa = LOADADDR(.text); __text_pa = LOADADDR(.text);

View File

@ -76,7 +76,7 @@ typedef struct {
static mmuLevel1Table mmuGlobalTT; static mmuLevel1Table mmuGlobalTT;
// simple watermark allocator for 2nd level page tables // 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 mmuLevel2Table mmuCoarseTables[MAX_SECOND_LEVEL];
static u32 mmuCoarseAllocated = 0; static u32 mmuCoarseAllocated = 0;
static mmuLevel2Table *mmuAllocateLevel2Table(void) static mmuLevel2Table *mmuAllocateLevel2Table(void)

View File

@ -16,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// kinda hardcoded and all over the place, but it needs to stay simple
#include <types.h> #include <types.h>
#include <arm.h> #include <arm.h>

21
arm11/source/arm/xrq.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
u32 xrqInstallVectorTable(void);

View File

@ -28,47 +28,46 @@
.macro TRAP_ENTRY xrq .macro TRAP_ENTRY xrq
msr cpsr_f, #(\xrq << 29) msr cpsr_f, #(\xrq << 29)
b XRQ_Main b xrqMain
.endm .endm
.section .vector, "ax" xrqVectorTable:
vectors: ldr pc, =xrqReset
b XRQ_Reset ldr pc, =xrqUndefined
b XRQ_Undefined ldr pc, =xrqSVC
b XRQ_SVC ldr pc, =xrqPrefetchAbort
b XRQ_PrefetchAbt ldr pc, =xrqDataAbort
b XRQ_DataAbt b . @ ignore the reserved exception
b XRQ_Reserved ldr pc, =xrqIRQ
b XRQ_IRQ ldr pc, =xrqFIQ
b XRQ_FIQ .pool
xrqVectorTableEnd:
XRQ_Reset: xrqReset:
TRAP_ENTRY 0 TRAP_ENTRY 0
XRQ_Undefined: xrqUndefined:
TRAP_ENTRY 1 TRAP_ENTRY 1
XRQ_SVC: xrqSVC:
TRAP_ENTRY 2 TRAP_ENTRY 2
XRQ_PrefetchAbt: xrqPrefetchAbort:
TRAP_ENTRY 3 TRAP_ENTRY 3
XRQ_DataAbt: xrqDataAbort:
TRAP_ENTRY 4 TRAP_ENTRY 4
XRQ_Reserved: xrqFIQ:
TRAP_ENTRY 5
XRQ_FIQ:
TRAP_ENTRY 7 TRAP_ENTRY 7
XRQ_Main: xrqMain:
ldr sp, =(exception_stack_top - 32*4) clrex
stmia sp, {r0-r7}
cpsid aif cpsid aif
ldr sp, =(xrqStackTop - 32*4)
stmia sp, {r0-r7}
mrs r1, cpsr mrs r1, cpsr
lsr r0, r1, #29 lsr r0, r1, #29
@ -82,11 +81,7 @@ XRQ_Main:
add r3, sp, #8*4 add r3, sp, #8*4
msr cpsr_c, r2 msr cpsr_c, r2
nop
nop
stmia r3!, {r8-r14} stmia r3!, {r8-r14}
nop
nop
msr cpsr_c, r1 msr cpsr_c, r1
mrc p15, 0, r4, c5, c0, 0 @ data fault status register mrc p15, 0, r4, c5, c0, 0 @ data fault status register
@ -99,7 +94,8 @@ XRQ_Main:
bl do_exception bl do_exception
XRQ_IRQ: xrqIRQ:
clrex
sub lr, lr, #4 @ Fix return address sub lr, lr, #4 @ Fix return address
srsfd sp!, #SR_SVC_MODE @ Store IRQ mode LR and SPSR on the SVC stack srsfd sp!, #SR_SVC_MODE @ Store IRQ mode LR and SPSR on the SVC stack
cps #SR_SVC_MODE @ Switch to SVC mode cps #SR_SVC_MODE @ Switch to SVC mode
@ -108,17 +104,26 @@ XRQ_IRQ:
and r4, sp, #7 @ Fix SP to be 8byte aligned and r4, sp, #7 @ Fix SP to be 8byte aligned
sub sp, sp, r4 sub sp, sp, r4
mov lr, pc bl gicTopHandler
ldr pc, =gicTopHandler
add sp, sp, r4 add sp, sp, r4
pop {r0-r4, r12, lr} pop {r0-r4, r12, lr}
rfeia sp! @ Return from exception 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 .align 12
exception_stack: @ reserve a single aligned page for the exception stack .global xrqPage
.space 4096 xrqPage:
exception_stack_top: .space 8192 @ reserve two 4K aligned pages for vectors and abort stack
.global exception_stack_top .global xrqStackTop
xrqStackTop:

View File

@ -23,7 +23,7 @@
typedef struct { typedef struct {
s16 cpad_x, cpad_y; s16 cpad_x, cpad_y;
s16 ts_x, ts_y; u16 ts_x, ts_y;
} CODEC_Input; } CODEC_Input;
void CODEC_Init(void); void CODEC_Init(void);

View File

@ -19,6 +19,7 @@
#include <common.h> #include <common.h>
#include <types.h> #include <types.h>
#include <vram.h> #include <vram.h>
#include <arm.h>
#include "arm/timer.h" #include "arm/timer.h"
@ -58,7 +59,7 @@ unsigned GFX_init(GfxFbFmt mode)
// Reset // Reset
REG_PDN_GPU_CNT = PDN_GPU_CNT_CLK_E; 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_PDN_GPU_CNT = PDN_GPU_CNT_CLK_E | PDN_GPU_CNT_RST_ALL;
REG_GX_GPU_CLK = 0x100; REG_GX_GPU_CLK = 0x100;
REG_GX_PSC_VRAM = 0; REG_GX_PSC_VRAM = 0;

View File

@ -30,23 +30,15 @@ static u32 HID_ConvertCPAD(s16 cpad_x, s16 cpad_y)
{ {
u32 ret = 0; u32 ret = 0;
switch(int_sign(cpad_x)) { if (cpad_x > 0) {
default:
break;
case 1:
ret |= BUTTON_RIGHT; ret |= BUTTON_RIGHT;
break; } else if (cpad_x < 0) {
case -1:
ret |= BUTTON_LEFT; ret |= BUTTON_LEFT;
} }
switch(int_sign(cpad_y)) { if (cpad_y > 0) {
default:
break;
case 1:
ret |= BUTTON_UP; ret |= BUTTON_UP;
break; } else if (cpad_y < 0) {
case -1:
ret |= BUTTON_DOWN; ret |= BUTTON_DOWN;
} }

View File

@ -67,7 +67,7 @@ typedef struct {
} PACKED_STRUCT MCU_NotificationLED; } PACKED_STRUCT MCU_NotificationLED;
static u8 cached_volume_slider = 0; 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) static void MCU_UpdateVolumeSlider(void)
{ {
@ -189,6 +189,8 @@ void MCU_Init(void)
{ {
u32 clrpend, mask = 0; u32 clrpend, mask = 0;
shell_state = SHELL_OPEN;
/* set register mask and clear any pending registers */ /* set register mask and clear any pending registers */
MCU_WriteRegBuf(REG_INT_EN, (const u8*)&mask, sizeof(mask)); MCU_WriteRegBuf(REG_INT_EN, (const u8*)&mask, sizeof(mask));
MCU_ReadRegBuf(REG_INT_MASK, (u8*)&clrpend, sizeof(clrpend)); MCU_ReadRegBuf(REG_INT_MASK, (u8*)&clrpend, sizeof(clrpend));

View File

@ -39,8 +39,8 @@ static const u8 brightness_lvls[] = {
0x4D, 0x56, 0x60, 0x6B, 0x4D, 0x56, 0x60, 0x6B,
0x79, 0x8C, 0xA7, 0xD2 0x79, 0x8C, 0xA7, 0xD2
}; };
static int prev_bright_lvl = -1; static int prev_bright_lvl;
static bool auto_brightness = true; static bool auto_brightness;
#endif #endif
static SystemSHMEM __attribute__((section(".shared"))) SharedMemoryState; static SystemSHMEM __attribute__((section(".shared"))) SharedMemoryState;
@ -180,11 +180,11 @@ void __attribute__((noreturn)) MainLoop(void)
{ {
#ifdef FIXED_BRIGHTNESS #ifdef FIXED_BRIGHTNESS
LCD_SetBrightness(FIXED_BRIGHTNESS); LCD_SetBrightness(FIXED_BRIGHTNESS);
#else
prev_bright_lvl = -1;
auto_brightness = true;
#endif #endif
// clear up the shared memory section
memset(&SharedMemoryState, 0, sizeof(SharedMemoryState));
// configure interrupts // configure interrupts
gicSetInterruptConfig(PXI_RX_INTERRUPT, BIT(0), GIC_PRIO2, GIC_RISINGEDGE_1N, PXI_RX_Handler); 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); gicSetInterruptConfig(MCU_INTERRUPT, BIT(0), GIC_PRIO1, GIC_RISINGEDGE_1N, MCU_HandleInterrupts);

View File

@ -21,7 +21,6 @@
#include <types.h> #include <types.h>
#define DEF_SECT_(n) extern u32 __##n##_pa, __##n##_va, __##n##_len; #define DEF_SECT_(n) extern u32 __##n##_pa, __##n##_va, __##n##_len;
DEF_SECT_(vector)
DEF_SECT_(text) DEF_SECT_(text)
DEF_SECT_(data) DEF_SECT_(data)
DEF_SECT_(rodata) DEF_SECT_(rodata)

View File

@ -24,7 +24,7 @@
#include "arm/gic.h" #include "arm/gic.h"
#include "arm/mmu.h" #include "arm/mmu.h"
#include "arm/scu.h" #include "arm/scu.h"
#include "arm/timer.h" #include "arm/xrq.h"
#include "hw/codec.h" #include "hw/codec.h"
#include "hw/gpulcd.h" #include "hw/gpulcd.h"
@ -79,13 +79,18 @@ void SYS_CoreZeroInit(void)
SCU_Init(); SCU_Init();
// Map all sections here // 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(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(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(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(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)); 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 // IO Registers
mmuMapArea(0x10100000, 0x10100000, 4UL << 20, MMU_FLAGS(MMU_DEV_SHARED, MMU_READ_WRITE, 1, 1)); mmuMapArea(0x10100000, 0x10100000, 4UL << 20, MMU_FLAGS(MMU_DEV_SHARED, MMU_READ_WRITE, 1, 1));

View File

@ -5,7 +5,7 @@ TARGET := $(shell basename $(CURDIR))
SOURCE := source SOURCE := source
BUILD := build 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 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)") INCLUDE := $(foreach dir,$(INCDIRS),-I"$(shell pwd)/$(dir)")

View File

@ -7,7 +7,7 @@
// see: https://github.com/TASVideos/desmume/blob/master/desmume/src/bios.cpp#L1070tions // see: https://github.com/TASVideos/desmume/blob/master/desmume/src/bios.cpp#L1070tions
u16 crc16_quick(const void* src, u32 len) { u16 crc16_quick(const void* src, u32 len) {
const u16 tabval[] = { CRC16_TABVAL }; static const u16 tabval[] = { CRC16_TABVAL };
u16* data = (u16*) src; u16* data = (u16*) src;
u16 crc = 0xFFFF; u16 crc = 0xFFFF;

View File

@ -232,7 +232,7 @@ u32 CheckRecommendedKeyDb(const char* path)
{ {
// SHA-256 of the recommended aeskeydb.bin file // SHA-256 of the recommended aeskeydb.bin file
// equals MD5 A5B28945A7C051D7A0CD18AF0E580D1B // 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, 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 0x2B, 0x24, 0x00, 0x15, 0xBE, 0x9B, 0xFC, 0xDE, 0x7F, 0xED, 0x95, 0x1D, 0xD5, 0xAB, 0x2D, 0xCB
}; };

View File

@ -11,13 +11,13 @@
#include "ui.h" // only for font file detection #include "ui.h" // only for font file detection
u64 IdentifyFileType(const char* path) { u64 IdentifyFileType(const char* path) {
const u8 romfs_magic[] = { ROMFS_MAGIC }; static const u8 romfs_magic[] = { ROMFS_MAGIC };
const u8 diff_magic[] = { DIFF_MAGIC }; static const u8 diff_magic[] = { DIFF_MAGIC };
const u8 disa_magic[] = { DISA_MAGIC }; static const u8 disa_magic[] = { DISA_MAGIC };
const u8 tickdb_magic[] = { TICKDB_MAGIC }; static const u8 tickdb_magic[] = { TICKDB_MAGIC };
const u8 smdh_magic[] = { SMDH_MAGIC }; static const u8 smdh_magic[] = { SMDH_MAGIC };
const u8 threedsx_magic[] = { THREEDSX_EXT_MAGIC }; static const u8 threedsx_magic[] = { THREEDSX_EXT_MAGIC };
const u8 png_magic[] = { PNG_MAGIC }; static const u8 png_magic[] = { PNG_MAGIC };
if (!path) return 0; // safety if (!path) return 0; // safety
u8 ALIGN(32) header[0x2C0]; // minimum required size 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)) { (memcmp(data, threedsx_magic, sizeof(threedsx_magic)) == 0)) {
return GAME_3DSX; // 3DSX (executable) file return GAME_3DSX; // 3DSX (executable) file
} else if ((fsize > sizeof(CmdHeader)) && } else if ((fsize > sizeof(CmdHeader)) &&
CheckCmdSize((CmdHeader*) data, fsize) == 0) { (CMD_SIZE((CmdHeader*) data) == fsize)) {
return GAME_CMD; // CMD file return GAME_CMD; // CMD file
} else if ((fsize > sizeof(NcchInfoHeader)) && } else if ((fsize > sizeof(NcchInfoHeader)) &&
(GetNcchInfoVersion((NcchInfoHeader*) data)) && (GetNcchInfoVersion((NcchInfoHeader*) data)) &&

View File

@ -50,6 +50,7 @@
#define FTYPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|BIN_KEYDB)) #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(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_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_CXIDUMP(tp) (tp&(GAME_TMD))
#define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB)) #define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB))
#define FTYPE_KEYBUILD(tp) (tp&(BIN_KEYDB|BIN_LEGKEY)) #define FTYPE_KEYBUILD(tp) (tp&(BIN_KEYDB|BIN_LEGKEY))

View File

@ -65,9 +65,9 @@ bool InitImgFS(const char* path) {
} }
void DeinitExtFS() { void DeinitExtFS() {
InitImgFS(NULL);
SetupNandSdDrive(NULL, NULL, NULL, 0); SetupNandSdDrive(NULL, NULL, NULL, 0);
SetupNandSdDrive(NULL, NULL, NULL, 1); SetupNandSdDrive(NULL, NULL, NULL, 1);
InitImgFS(NULL);
for (u32 i = NORM_FS - 1; i > 0; i--) { for (u32 i = NORM_FS - 1; i > 0; i--) {
if (fs_mounted[i]) { if (fs_mounted[i]) {
char fsname[8]; char fsname[8];

View File

@ -46,7 +46,7 @@ bool CheckWritePermissions(const char* path) {
// check drive type, get permission type // check drive type, get permission type
if (drvtype & DRV_SYSNAND) { 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; u32 lvl = (drvtype & (DRV_TWLNAND|DRV_ALIAS|DRV_CTRNAND)) ? 1 : 0;
if (drvtype & (DRV_CTRNAND|DRV_VIRTUAL)) { // check for paths if (drvtype & (DRV_CTRNAND|DRV_VIRTUAL)) { // check for paths
const char* path_lvl3[] = { PATH_SYS_LVL3 }; const char* path_lvl3[] = { PATH_SYS_LVL3 };
@ -65,7 +65,7 @@ bool CheckWritePermissions(const char* path) {
perm = perms[lvl]; perm = perms[lvl];
snprintf(area_name, 16, "SysNAND (lvl%lu)", lvl); snprintf(area_name, 16, "SysNAND (lvl%lu)", lvl);
} else if (drvtype & DRV_EMUNAND) { } 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; u32 lvl = (drvtype & (DRV_ALIAS|DRV_CTRNAND)) ? 1 : 0;
if (drvtype & DRV_VIRTUAL) { // check for paths if (drvtype & DRV_VIRTUAL) { // check for paths
const char* path_lvl1[] = { PATH_EMU_LVL1 }; const char* path_lvl1[] = { PATH_EMU_LVL1 };
@ -124,7 +124,7 @@ bool CheckWritePermissions(const char* path) {
} }
bool CheckDirWritePermissions(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++) { for (u32 i = 0; i < sizeof(path_chk) / sizeof(char*); i++) {
const char* path_cmp = path_chk[i]; const char* path_cmp = path_chk[i];
u32 p = 0; u32 p = 0;

View File

@ -1,11 +1,14 @@
#include "image.h" #include "image.h"
#include "vff.h" #include "vff.h"
#include "nandcmac.h"
static FIL mount_file; static FIL mount_file;
static u64 mount_state = 0; static u64 mount_state = 0;
static char mount_path[256] = { 0 }; static char mount_path[256] = { 0 };
static bool fix_cmac = false;
int ReadImageBytes(void* buffer, u64 offset, u64 count) { int ReadImageBytes(void* buffer, u64 offset, u64 count) {
UINT bytes_read; UINT bytes_read;
@ -28,6 +31,7 @@ int WriteImageBytes(const void* buffer, u64 offset, u64 count) {
if (fvx_tell(&mount_file) != offset) if (fvx_tell(&mount_file) != offset)
fvx_lseek(&mount_file, offset); fvx_lseek(&mount_file, offset);
ret = fvx_write(&mount_file, buffer, count, &bytes_written); 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; 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; u64 type = (path) ? IdentifyFileType(path) : 0;
if (mount_state) { if (mount_state) {
fvx_close(&mount_file); fvx_close(&mount_file);
if (fix_cmac) FixFileCmac(mount_path, false);
fix_cmac = false;
mount_state = 0; mount_state = 0;
*mount_path = 0; *mount_path = 0;
} }

View File

@ -15,10 +15,10 @@ typedef struct {
static FilCryptInfo filcrypt[NUM_FILCRYPTINFO] = { 0 }; static FilCryptInfo filcrypt[NUM_FILCRYPTINFO] = { 0 };
char alias_drv[NUM_ALIAS_DRV]; // 1 char ASCII drive number of the alias drive / 0x00 if unused static 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_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 alias_num (const TCHAR* path) {
int num = -1; int num = -1;

View File

@ -2,29 +2,9 @@
#include "common.h" #include "common.h"
#include "ticket.h" #include "ticket.h"
#include "tie.h"
// There's probably a better place to put this
#define SD_TITLEDB_PATH(emu) ((emu) ? "B:/dbs/title.db" : "A:/dbs/title.db")
// https://www.3dbrew.org/wiki/Inner_FAT // 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 GetNumTitleInfoEntries(const char* path);
u32 GetNumTickets(const char* path); u32 GetNumTickets(const char* path);

View File

@ -50,11 +50,11 @@ u32 FixCiaHeaderForTmd(CiaHeader* header, TitleMetaData* tmd) {
} }
u32 BuildCiaCert(u8* ciacert) { 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, 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 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, 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 0x18, 0x83, 0xAF, 0xE0, 0xF4, 0xE5, 0x62, 0xBA, 0x69, 0xEE, 0x72, 0x2A, 0xC2, 0x4E, 0x95, 0xB3
}; };

View File

@ -1,11 +1,63 @@
#include "cmd.h" #include "cmd.h"
u32 CheckCmdSize(CmdHeader* cmd, u64 fsize) { CmdHeader* BuildAllocCmdData(TitleMetaData* tmd) {
u64 cmdsize = sizeof(CmdHeader) + CmdHeader proto;
(cmd->n_entries * sizeof(u32)) + CmdHeader* cmd = NULL;
(cmd->n_cmacs * sizeof(u32)) + u32 content_count = getbe16(tmd->content_count);
(cmd->n_entries * 0x10); 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;
} }

View File

@ -1,7 +1,12 @@
#pragma once #pragma once
#include "common.h" #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 // from: http://3dbrew.org/wiki/Titles#Data_Structure
typedef struct { typedef struct {
@ -15,4 +20,4 @@ typedef struct {
// followed by <n_entries> CMACs (may contain garbage) // followed by <n_entries> CMACs (may contain garbage)
} __attribute__((packed, aligned(4))) CmdHeader; } __attribute__((packed, aligned(4))) CmdHeader;
u32 CheckCmdSize(CmdHeader* cmd, u64 fsize); CmdHeader* BuildAllocCmdData(TitleMetaData* tmd);

View File

@ -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) { u32 GetDisaDiffRWInfo(const char* path, DisaDiffRWInfo* info, bool partitionB) {
const u8 disa_magic[] = { DISA_MAGIC }; static const u8 disa_magic[] = { DISA_MAGIC };
const u8 diff_magic[] = { DIFF_MAGIC }; static const u8 diff_magic[] = { DIFF_MAGIC };
const u8 ivfc_magic[] = { IVFC_MAGIC }; static const u8 ivfc_magic[] = { IVFC_MAGIC };
const u8 dpfs_magic[] = { DPFS_MAGIC }; static const u8 dpfs_magic[] = { DPFS_MAGIC };
const u8 difi_magic[] = { DIFI_MAGIC }; static const u8 difi_magic[] = { DIFI_MAGIC };
// reset reader info // reset reader info
memset(info, 0x00, sizeof(DisaDiffRWInfo)); memset(info, 0x00, sizeof(DisaDiffRWInfo));

View File

@ -14,6 +14,9 @@
#include "tad.h" #include "tad.h"
#include "3dsx.h" #include "3dsx.h"
#include "tmd.h" #include "tmd.h"
#include "ticket.h"
#include "tie.h"
#include "cmd.h" #include "cmd.h"
#include "bdri.h"
#include "ticketdb.h" #include "ticketdb.h"
#include "ncchinfo.h" #include "ncchinfo.h"

View File

@ -7,7 +7,7 @@
0x84, 0x9D, 0xA0, 0xD5, 0x6F, 0x5A, 0x34, 0xC4, 0x81, 0x06, 0x0C, 0x9F, 0xF2, 0xFA, 0xD8, 0x18 0x84, 0x9D, 0xA0, 0xD5, 0x6F, 0x5A, 0x34, 0xC4, 0x81, 0x06, 0x0C, 0x9F, 0xF2, 0xFA, 0xD8, 0x18
u32 ValidateAgbSaveHeader(AgbSaveHeader* header) { u32 ValidateAgbSaveHeader(AgbSaveHeader* header) {
u8 magic[] = { AGBSAVE_MAGIC }; static u8 magic[] = { AGBSAVE_MAGIC };
// basic checks // basic checks
if ((memcmp(header->magic, magic, sizeof(magic)) != 0) || if ((memcmp(header->magic, magic, sizeof(magic)) != 0) ||
@ -28,7 +28,7 @@ u32 ValidateAgbSaveHeader(AgbSaveHeader* header) {
// http://problemkaputt.de/gbatek.htm#gbacartridgeheader // http://problemkaputt.de/gbatek.htm#gbacartridgeheader
u32 ValidateAgbHeader(AgbHeader* agb) { 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))); u8 logo[0x9C] __attribute__((aligned(4)));
// check fixed value // check fixed value

View File

@ -2,7 +2,7 @@
#include "ncch.h" #include "ncch.h"
u32 ValidateNcsdHeader(NcsdHeader* header) { u32 ValidateNcsdHeader(NcsdHeader* header) {
u8 zeroes[16] = { 0 }; static const u8 zeroes[16] = { 0 };
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number 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 (memcmp(header->partitions_fs_type, zeroes, 8) != 0) || !header->mediaId) // prevent detection of NAND images
return 1; return 1;

View File

@ -19,7 +19,7 @@ u64 GetRomFsLvOffset(RomFsIvfcHeader* ivfc, u32 lvl) {
// validate IVFC header by checking offsets and hash sizes // validate IVFC header by checking offsets and hash sizes
u32 ValidateRomFsHeader(RomFsIvfcHeader* ivfc, u32 max_size) { u32 ValidateRomFsHeader(RomFsIvfcHeader* ivfc, u32 max_size) {
u8 magic[] = { ROMFS_MAGIC }; static const u8 magic[] = { ROMFS_MAGIC };
// check magic number // check magic number
if (memcmp(magic, ivfc->magic, sizeof(magic)) != 0) if (memcmp(magic, ivfc->magic, sizeof(magic)) != 0)

View File

@ -9,7 +9,7 @@
36, 37, 44, 45, 38, 39, 46, 47, 52, 53, 60, 61, 54, 55, 62, 63 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) { 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; u16* pix565 = (u16*) smdh_icon;
for (u32 y = 0; y < h; y += 8) { for (u32 y = 0; y < h; y += 8) {
for (u32 x = 0; x < w; x += 8) { for (u32 x = 0; x < w; x += 8) {

View File

@ -6,7 +6,7 @@
#include "ff.h" #include "ff.h"
u32 ValidateTicket(Ticket* ticket) { 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) || if ((memcmp(ticket->sig_type, magic, sizeof(magic)) != 0) ||
((strncmp((char*) ticket->issuer, TICKET_ISSUER, 0x40) != 0) && ((strncmp((char*) ticket->issuer, TICKET_ISSUER, 0x40) != 0) &&
(strncmp((char*) ticket->issuer, TICKET_ISSUER_DEV, 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) { u32 BuildFakeTicket(Ticket* ticket, u8* title_id) {
const u8 sig_type[4] = { TICKET_SIG_TYPE }; // RSA_2048 SHA256 static const u8 sig_type[4] = { TICKET_SIG_TYPE }; // RSA_2048 SHA256
const u8 ticket_cnt_index[] = { // whatever this is 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, 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, 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 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) { 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, 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 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, 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 0xC6, 0x4B, 0xD4, 0x8F, 0xDF, 0x13, 0x21, 0x3D, 0xFC, 0x72, 0xFC, 0x8D, 0x9F, 0xDD, 0x01, 0x0E
}; };

92
arm9/source/game/tie.c Normal file
View File

@ -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;
}

31
arm9/source/game/tie.h Normal file
View File

@ -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);

View File

@ -6,7 +6,7 @@
#include "ff.h" #include "ff.h"
u32 ValidateTmd(TitleMetaData* tmd) { 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) || if ((memcmp(tmd->sig_type, magic, sizeof(magic)) != 0) ||
((strncmp((char*) tmd->issuer, TMD_ISSUER, 0x40) != 0) && ((strncmp((char*) tmd->issuer, TMD_ISSUER, 0x40) != 0) &&
(strncmp((char*) tmd->issuer, TMD_ISSUER_DEV, 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) { 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 // safety check: number of contents
if (n_contents > TMD_MAX_CONTENTS) return 1; // potential incompatibility here (!) if (n_contents > TMD_MAX_CONTENTS) return 1; // potential incompatibility here (!)
// set TMD all zero for a clean start // 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) { 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, 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 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, 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 0xC2, 0xE9, 0xCA, 0x93, 0x94, 0xF4, 0x29, 0xA0, 0x38, 0x54, 0x75, 0xFF, 0xAB, 0x6E, 0x8E, 0x71
}; };

View File

@ -9,7 +9,6 @@
#include "command_ak2i.h" #include "command_ak2i.h"
#include "protocol_ntr.h" #include "protocol_ntr.h"
#include "card_ntr.h" #include "card_ntr.h"
#include "delay.h"
u32 AK2I_CmdGetHardwareVersion(void) u32 AK2I_CmdGetHardwareVersion(void)
{ {

View File

@ -5,18 +5,18 @@
// modifyed by osilloscopion (2 Jul 2016) // modifyed by osilloscopion (2 Jul 2016)
// //
#include <arm.h>
#include "command_ntr.h" #include "command_ntr.h"
#include "protocol_ntr.h" #include "protocol_ntr.h"
#include "card_ntr.h" #include "card_ntr.h"
#include "delay.h"
u32 ReadDataFlags = 0; u32 ReadDataFlags = 0;
void NTR_CmdReset(void) void NTR_CmdReset(void)
{ {
cardReset (); cardReset ();
ioDelay2(0xF000); ARM_WaitCycles(0xF000 * 4);
} }
u32 NTR_CmdGetCartId(void) u32 NTR_CmdGetCartId(void)
@ -34,7 +34,7 @@ void NTR_CmdReadHeader (u8* buffer)
{ {
REG_NTRCARDROMCNT=0; REG_NTRCARDROMCNT=0;
REG_NTRCARDMCNT=0; REG_NTRCARDMCNT=0;
ioDelay2(167550); ARM_WaitCycles(167550 * 4);
REG_NTRCARDMCNT=NTRCARD_CR1_ENABLE|NTRCARD_CR1_IRQ; REG_NTRCARDMCNT=NTRCARD_CR1_ENABLE|NTRCARD_CR1_IRQ;
REG_NTRCARDROMCNT=NTRCARD_nRESET|NTRCARD_SEC_SEED; REG_NTRCARDROMCNT=NTRCARD_nRESET|NTRCARD_SEC_SEED;
while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ; while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ;

View File

@ -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);

View File

@ -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

View File

@ -7,8 +7,8 @@
// //
#pragma once #pragma once
#include <arm.h>
#include <inttypes.h> #include <inttypes.h>
#include "delay.h"
#define u8 uint8_t #define u8 uint8_t
#define u16 uint16_t #define u16 uint16_t
@ -85,7 +85,7 @@
#define CARD_SPICNTH_ENABLE (1<<7) // in byte 1, i.e. 0x8000 #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 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_SRC(n) (*(vu32*)(0x10002004 + (n * 0x1c)))
#define DMA_DEST(n) (*(vu32*)(0x10002008 + (n * 0x1c))) #define DMA_DEST(n) (*(vu32*)(0x10002008 + (n * 0x1c)))

View File

@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <arm.h>
#include "protocol.h" #include "protocol.h"
#include "timer.h" #include "timer.h"
@ -14,7 +16,6 @@
#include "protocol_ntr.h" #include "protocol_ntr.h"
#include "command_ctr.h" #include "command_ctr.h"
#include "command_ntr.h" #include "command_ntr.h"
#include "delay.h"
// could have been done better, but meh... // could have been done better, but meh...
#define REG_AESCNT (*(vu32*)0x10009000) #define REG_AESCNT (*(vu32*)0x10009000)
@ -177,7 +178,7 @@ void Cart_Secure_Init(u32 *buf, u32 *out)
// if (!mac_valid) // if (!mac_valid)
// ClearScreen(bottomScreen, RGB(255, 0, 0)); // ClearScreen(bottomScreen, RGB(255, 0, 0));
ioDelay(0xF0000); ARM_WaitCycles(0xF0000 * 8);
CTR_SetSecKey(A0_Response); CTR_SetSecKey(A0_Response);
CTR_SetSecSeed(out, true); CTR_SetSecSeed(out, true);
@ -207,7 +208,7 @@ void Cart_Secure_Init(u32 *buf, u32 *out)
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 5; ++i) {
CTR_SendCommand(A2_cmd, 4, 1, 0x701002C, &test); CTR_SendCommand(A2_cmd, 4, 1, 0x701002C, &test);
ioDelay(0xF0000); ARM_WaitCycles(0xF0000 * 8);
} }
} }

View File

@ -2,10 +2,11 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <arm.h>
#include "protocol_ctr.h" #include "protocol_ctr.h"
#include "protocol.h" #include "protocol.h"
#include "delay.h"
#ifdef VERBOSE_COMMANDS #ifdef VERBOSE_COMMANDS
#include "draw.h" #include "draw.h"
#endif #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 // so we have to wait next data ready
do { cardCtrl = REG_CTRCARDCNT; } while(!(cardCtrl & CTRCARD_DATA_READY)); do { cardCtrl = REG_CTRCARDCNT; } while(!(cardCtrl & CTRCARD_DATA_READY));
// and this tiny delay is necessary // and this tiny delay is necessary
ioDelay(33); ARM_WaitCycles(33 * 8);
// pull ROM CS high // pull ROM CS high
REG_CTRCARDCNT = 0x10000000; REG_CTRCARDCNT = 0x10000000;
REG_CTRCARDCNT = CTRKEY_PARAM | CTRCARD_ACTIVATE | CTRCARD_nRESET; REG_CTRCARDCNT = CTRKEY_PARAM | CTRCARD_ACTIVATE | CTRCARD_nRESET;

View File

@ -23,7 +23,6 @@
#include "card_ntr.h" #include "card_ntr.h"
// #include "draw.h" // #include "draw.h"
#include "timer.h" #include "timer.h"
#include "delay.h"
#define BSWAP32(val) ((((val >> 24) & 0xFF)) | (((val >> 16) & 0xFF) << 8) | (((val >> 8) & 0xFF) << 16) | ((val & 0xFF) << 24)) #define BSWAP32(val) ((((val >> 24) & 0xFF)) | (((val >> 16) & 0xFF) << 8) | (((val >> 8) & 0xFF) << 16) | ((val & 0xFF) << 24))

View File

@ -417,10 +417,10 @@ void DrawDirContents(DirStruct* contents, u32 cursor, u32* scroll) {
} }
u32 SdFormatMenu(const char* slabel) { u32 SdFormatMenu(const char* slabel) {
const u32 cluster_size_table[5] = { 0x0, 0x0, 0x4000, 0x8000, 0x10000 }; static 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 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..." }; "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); u32 sysnand_min_size_sectors = GetNandMinSizeSectors(NAND_SYSNAND);
u64 sysnand_min_size_mb = ((sysnand_min_size_sectors * 0x200) + 0xFFFFF) / 0x100000; 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; 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 emunand_offset = 1;
u32 n_emunands = 1; u32 n_emunands = 1;
if (emunand_size_mb >= 2 * sysnand_size_mb) { 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:"); user_select = ShowSelectPrompt(4, option_emunand_type, "Choose EmuNAND type to set up:");
if (user_select > 3) return 0; if (user_select > 3) return 0;
emunand_offset = (user_select == 3) ? 0 : 1; emunand_offset = (user_select == 3) ? 0 : 1;
if (user_select == 1) n_emunands = 4; if (user_select == 1) n_emunands = 4;
} else if (emunand_size_mb >= sysnand_size_mb) { } 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:"); user_select = ShowSelectPrompt(3, option_emunand_type, "Choose EmuNAND type to set up:");
if (user_select > 2) return 0; if (user_select > 2) return 0;
emunand_offset = (user_select == 2) ? 0 : 1; // 0 -> GW EmuNAND 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 if (dual_screen) ClearScreen(BOT_SCREEN, COLOR_STD_BG);
else memcpy(BOT_SCREEN, bottom_cpy, SCREEN_SIZE_BOT); else memcpy(BOT_SCREEN, bottom_cpy, SCREEN_SIZE_BOT);
} else if (pad_state & BUTTON_X) { } 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:", u32 user_select = ShowSelectPrompt(3, optionstr, "Current offset: %08X\nSelect action:",
(unsigned int) offset); (unsigned int) offset);
if (user_select == 1) { // -> goto offset if (user_select == 1) { // -> goto offset
@ -892,7 +892,7 @@ u32 CmacCalculator(const char* path) {
pathstr, getbe64(cmac + 0), getbe64(cmac + 8), pathstr, getbe64(cmac + 0), getbe64(cmac + 8),
"CMAC verification: ", (identical) ? "passed!" : "failed!", "CMAC verification: ", (identical) ? "passed!" : "failed!",
(!identical) ? "\n \nFix CMAC in file?" : "") && (!identical) ? "\n \nFix CMAC in file?" : "") &&
!identical && (WriteFileCmac(path, cmac) != 0)) { !identical && (WriteFileCmac(path, cmac, true) != 0)) {
ShowPrompt(false, "Fixing CMAC: failed!"); ShowPrompt(false, "Fixing CMAC: failed!");
} }
} }
@ -901,7 +901,7 @@ u32 CmacCalculator(const char* path) {
if (ShowPrompt(!correct, "%s\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n%s%s%s", if (ShowPrompt(!correct, "%s\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n%s%s%s",
pathstr, "CMAC verification: ", (correct) ? "passed!" : "failed!", pathstr, "CMAC verification: ", (correct) ? "passed!" : "failed!",
(!correct) ? "\n \nFix CMAC in file?" : "") && (!correct) ? "\n \nFix CMAC in file?" : "") &&
!correct && (FixCmdCmac(path) != 0)) { !correct && (FixCmdCmac(path, true) != 0)) {
ShowPrompt(false, "Fixing CMAC: failed!"); 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 cryptable_inplace = ((encryptable||decryptable) && !in_output_path && (drvtype & DRV_FAT));
bool cia_buildable = (FTYPE_CIABUILD(filetype)); bool cia_buildable = (FTYPE_CIABUILD(filetype));
bool cia_buildable_legit = (FTYPE_CIABUILD_L(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 cxi_dumpable = (FTYPE_CXIDUMP(filetype));
bool tik_buildable = (FTYPE_TIKBUILD(filetype)) && !in_output_path; 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 titleinfo = (FTYPE_TITLEINFO(filetype));
bool ciacheckable = (FTYPE_CIACHECK(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) && bool trimable = (FTYPE_TRIMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) &&
!(drvtype & DRV_CTRNAND) && !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_IMAGE); !(drvtype & DRV_CTRNAND) && !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_IMAGE);
bool transferable = (FTYPE_TRANSFERABLE(filetype) && IS_UNLOCKED && (drvtype & DRV_FAT)); 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; continue;
} }
if (CheckFileCmac(path) == 0) n_success++; 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 else { // on failure: set cursor on failed file
*cursor = i; *cursor = i;
continue; 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 = (cia_buildable) ? ++n_opt : -1;
int cia_build_legit = (cia_buildable_legit) ? ++n_opt : -1; int cia_build_legit = (cia_buildable_legit) ? ++n_opt : -1;
int cxi_dump = (cxi_dumpable) ? ++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_enc = (tik_buildable) ? ++n_opt : -1;
int tik_build_dec = (tik_buildable) ? ++n_opt : -1; int tik_build_dec = (tik_buildable) ? ++n_opt : -1;
int key_build = (key_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 > 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 (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 (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_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 (tik_build_dec > 0) optionstr[tik_build_dec-1] = "Build " TIKDB_NAME_DEC;
if (key_build > 0) optionstr[key_build-1] = "Build " KEYDB_NAME; 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; 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 else if (user_select == verify) { // -> verify game / nand file
if ((n_marked > 1) && ShowPrompt(true, "Try to verify all %lu selected files?", n_marked)) { if ((n_marked > 1) && ShowPrompt(true, "Try to verify all %lu selected files?", n_marked)) {
u32 n_success = 0; u32 n_success = 0;
@ -2329,7 +2382,7 @@ u32 GodMode(int entrypoint) {
} else { // one level up } else { // one level up
u32 user_select = 1; u32 user_select = 1;
if (curr_drvtype & DRV_SEARCH) { // special menu for search drive 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]; char pathstr[32 + 1];
TruncateString(pathstr, curr_entry->path, 32, 8); TruncateString(pathstr, curr_entry->path, 32, 8);
user_select = ShowSelectPrompt(2, optionstr, "%s", pathstr); 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)) { } else if ((curr_drvtype & DRV_CART) && (pad_state & BUTTON_Y)) {
ShowPrompt(false, "Not allowed in gamecart drive"); ShowPrompt(false, "Not allowed in gamecart drive");
} else if (pad_state & BUTTON_Y) { // paste files } 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]; char promptstr[64];
u32 flags = 0; u32 flags = 0;
u32 user_select; u32 user_select;
@ -2542,7 +2595,7 @@ u32 GodMode(int entrypoint) {
} }
} }
} else if (pad_state & BUTTON_Y) { // create an entry } 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."); u32 type = ShowSelectPrompt(2, optionstr, "Create a new entry here?\nSelect type.");
if (type) { if (type) {
const char* typestr = (type == 1) ? "folder" : (type == 2) ? "file" : NULL; const char* typestr = (type == 1) ? "folder" : (type == 2) ? "file" : NULL;

View File

@ -7,6 +7,7 @@
#include "hid.h" #include "hid.h"
SystemSHMEM *shmemGlobalBase;
void main(int argc, char** argv, int entrypoint) void main(int argc, char** argv, int entrypoint)
{ {

View File

@ -386,7 +386,7 @@ u32 ValidateSecretSector(u8* sector)
// see: https://github.com/d0k3/GodMode9/blob/master/source/game/ncsd.c#L4 // see: https://github.com/d0k3/GodMode9/blob/master/source/game/ncsd.c#L4
u32 ValidateNandNcsdHeader(NandNcsdHeader* header) u32 ValidateNandNcsdHeader(NandNcsdHeader* header)
{ {
u8 zeroes[16] = { 0 }; static const u8 zeroes[16] = { 0 };
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number 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 (memcmp(header->partitions_fs_type, zeroes, 8) == 0) || header->mediaId) // prevent detection of cart NCSD images
return 1; return 1;

View File

@ -22,10 +22,10 @@
* along with this program. If not, see http://www.gnu.org/licenses/. * along with this program. If not, see http://www.gnu.org/licenses/.
*/ */
#include <arm.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "timer.h" #include "timer.h"
#include "wait.h"
#include "sdmmc.h" #include "sdmmc.h"
#define DATA32_SUPPORT #define DATA32_SUPPORT
@ -469,7 +469,7 @@ int SD_Init()
// We need to send at least 74 clock pulses. // We need to send at least 74 clock pulses.
set_target(&handleSD); set_target(&handleSD);
wait(2 * 128 * 74); ARM_WaitCycles(2 * 128 * 74);
sdmmc_send_command(&handleSD,0,0); sdmmc_send_command(&handleSD,0,0);
sdmmc_send_command(&handleSD,0x10408,0x1AA); sdmmc_send_command(&handleSD,0x10408,0x1AA);

View File

@ -1,5 +0,0 @@
#pragma once
#include "common.h"
void wait(u32 cycles);

View File

@ -1,10 +0,0 @@
.text
.arm
.align 4
.global wait
.type wait, %function
wait:
subs r0, r0, #4
bcs wait
bx lr

View File

@ -3,7 +3,7 @@
.arm .arm
#include <arm.h> #include <arm.h>
#include <brf.h> #include <bfn.h>
#include <entrypoints.h> #include <entrypoints.h>
#include "memmap.h" #include "memmap.h"
@ -27,9 +27,9 @@ _start:
strlo r2, [r0], #4 strlo r2, [r0], #4
blo .LBSS_Clear blo .LBSS_Clear
ldr r0, =BRF_WB_INV_DCACHE ldr r0, =BFN_WRITEBACK_INVALIDATE_DCACHE
blx r0 @ Writeback & Invalidate Data Cache blx r0 @ Writeback & Invalidate Data Cache
ldr r0, =BRF_INVALIDATE_ICACHE ldr r0, =BFN_INVALIDATE_ICACHE
blx r0 @ Invalidate Instruction Cache blx r0 @ Invalidate Instruction Cache
@ Disable caches / TCMs / MPU @ Disable caches / TCMs / MPU

View File

@ -60,7 +60,7 @@ XRQ_DUMPDATAFUNC(u16, 4)
XRQ_DUMPDATAFUNC(u32, 8) XRQ_DUMPDATAFUNC(u32, 8)
const char *XRQ_Name[] = { static const char *XRQ_Name[] = {
"Reset", "Undefined", "SWI", "Prefetch Abort", "Reset", "Undefined", "SWI", "Prefetch Abort",
"Data Abort", "Reserved", "IRQ", "FIQ" "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", 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_Y, (u32) dstime.bcd_M, (u32) dstime.bcd_D,
(u32) dstime.bcd_h, (u32) dstime.bcd_m, (u32) dstime.bcd_s); (u32) dstime.bcd_h, (u32) dstime.bcd_m, (u32) dstime.bcd_s);
for (int i = 0; i < 8; i++) { for (int i = 0; i < 16; i += 2) {
int i_ = i*2;
wstr += sprintf(wstr, 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]); wstr += sprintf(wstr, "CPSR: %08lX\n\n", regs[16]);
@ -109,12 +108,11 @@ void XRQ_DumpRegisters(u32 xrq, u32 *regs)
pc = regs[15] & ~0xF; pc = regs[15] & ~0xF;
if (pc_dumpable(pc, &pc_lower, &pc_upper)) { if (pc_dumpable(pc, &pc_lower, &pc_upper)) {
wstr += sprintf(wstr, "Code:\n"); wstr += sprintf(wstr, "Code:\n");
wstr += XRQ_DumpData_u32(wstr, pc_lower, pc_upper); if (regs[16] & SR_THUMB) { // need to take Thumb code into account
/*if (regs[16] & SR_THUMB) { // no need to take Thumb code into account wstr += XRQ_DumpData_u16(wstr, pc_lower, pc_upper);
wstr += XRQ_DumpData_u16(wstr, pc-PC_DUMPRAD, pc+PC_DUMPRAD);
} else { } else {
wstr += XRQ_DumpData_u32(wstr, pc-PC_DUMPRAD, pc+PC_DUMPRAD); wstr += XRQ_DumpData_u32(wstr, pc_lower, pc_upper);
}*/ }
} }
/* Draw QR Code */ /* Draw QR Code */
@ -127,7 +125,6 @@ void XRQ_DumpRegisters(u32 xrq, u32 *regs)
DrawQrCode(ALT_SCREEN, qrcode); DrawQrCode(ALT_SCREEN, qrcode);
} }
/* Reinitialize SD */ /* Reinitialize SD */
DrawStringF(MAIN_SCREEN, draw_x, draw_y_upd, COLOR_STD_FONT, COLOR_STD_BG, DrawStringF(MAIN_SCREEN, draw_x, draw_y_upd, COLOR_STD_FONT, COLOR_STD_BG,
"%-29.29s", "Reinitializing SD card..."); "%-29.29s", "Reinitializing SD card...");
@ -146,7 +143,6 @@ void XRQ_DumpRegisters(u32 xrq, u32 *regs)
"%-29.29s", "Dumping state to SD card..."); "%-29.29s", "Dumping state to SD card...");
FileSetData(path, dumpstr, wstr - dumpstr, 0, true); FileSetData(path, dumpstr, wstr - dumpstr, 0, true);
/* Deinit SD */ /* Deinit SD */
DeinitSDCardFS(); DeinitSDCardFS();
@ -156,7 +152,6 @@ void XRQ_DumpRegisters(u32 xrq, u32 *regs)
while (!(InputWait(0) & BUTTON_POWER)); while (!(InputWait(0) & BUTTON_POWER));
PowerOff(); PowerOff();
/* We will not return */ /* We will not return */
return; return;
} }

View File

@ -6,7 +6,6 @@
.arm .arm
#include <arm.h> #include <arm.h>
#include <brf.h>
#include "memmap.h" #include "memmap.h"
.macro TRAP_ENTRY xrq_id .macro TRAP_ENTRY xrq_id

View File

@ -97,7 +97,7 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) {
} }
// actual transfer - db files / titles // 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_to[32];
char path_from[32]; char path_from[32];
char path_dbs[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]); snprintf(path_from, 32, "7:/dbs/%s", dbnames[i]);
PathDelete(path_to); PathDelete(path_to);
PathCopy(path_dbs, path_from, &flags); PathCopy(path_dbs, path_from, &flags);
FixFileCmac(path_to); FixFileCmac(path_to, true);
} }
ShowString("Cleaning up titles, please wait..."); ShowString("Cleaning up titles, please wait...");
snprintf(path_to, 32, "%s/title", drv); snprintf(path_to, 32, "%s/title", drv);

View File

@ -1,4 +1,5 @@
#include "gameutil.h" #include "gameutil.h"
#include "nandcmac.h"
#include "disadiff.h" #include "disadiff.h"
#include "game.h" #include "game.h"
#include "nand.h" // so that we can trim NAND images #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) { u32 GetTmdContentPath(char* path_content, const char* path_tmd) {
// get path to TMD first content // 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 // content path string
char* name_content; 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) { u32 VerifyNcchFile(const char* path, u32 offset, u32 size) {
static bool cryptofix_always = false;
bool cryptofix = false; bool cryptofix = false;
NcchHeader ncch; NcchHeader ncch;
NcchExtHeader exthdr; NcchExtHeader exthdr;
@ -358,9 +360,15 @@ u32 VerifyNcchFile(const char* path, u32 offset, u32 size) {
// disable crypto, try again // disable crypto, try again
cryptofix = true; cryptofix = true;
fvx_lseek(&file, offset); fvx_lseek(&file, offset);
if ((GetNcchHeaders(&ncch, NULL, &exefs, &file, cryptofix) == 0) && if (GetNcchHeaders(&ncch, NULL, &exefs, &file, cryptofix) == 0) {
ShowPrompt(true, "%s\nError: Bad crypto flags\n \nAttempt to fix?", pathstr)) if (cryptofix_always) borkedflags = true;
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 (!borkedflags) {
if (!offset) ShowPrompt(false, "%s\nError: Bad ExeFS header", pathstr); 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) { 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 // path string
char pathstr[32 + 1]; 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) { 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); void* firm_buffer = (void*) malloc(FIRM_MAX_SIZE);
if (!firm_buffer) return 1; if (!firm_buffer) return 1;
@ -1228,6 +1236,275 @@ u32 CryptGameFile(const char* path, bool inplace, bool encrypt) {
return ret; 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, 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) { TmdContentChunk* chunk, const u8* titlekey, bool force_legit, bool cxi_fix, bool cdn_decrypt) {
// crypto types / ctr // crypto types / ctr
@ -1330,7 +1607,56 @@ u32 InsertCiaMeta(const char* path_cia, CiaMeta* meta) {
return (res) ? 0 : 1; 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 }; const u8 dlc_tid_high[] = { DLC_TID_HIGH };
CiaStub* cia = (CiaStub*) buffer; 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 // check the tickets' console id, warn if it isn't zero
if (copy && getbe32(ticket_tmp->console_id)) { if (copy && getbe32(ticket_tmp->console_id)) {
static u32 default_action = 0; static u32 default_action = 0;
const char* optionstr[2] = static const char* optionstr[2] =
{"Use generic ticket (not legit)", "Use personalized ticket (legit)"}; {"Use generic ticket (not legit)", "Use personalized ticket (legit)"};
if (!default_action) { if (!default_action) {
default_action = ShowSelectPrompt(2, optionstr, 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 }; u8 titlekey[16] = { 0xFF };
if ((GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0) && force_legit) return 1; 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++) { for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) {
TmdContentChunk* chunk = &(content_list[i]); TmdContentChunk* chunk = &(content_list[i]);
if (present[i / 8] & (1 << (i % 8))) { if (present[i / 8] & (1 << (i % 8))) {
snprintf(name_content, 256 - (name_content - path_content), snprintf(name_content, 256 - (name_content - path_content),
(cdn) ? "%08lx" : (dlc && !cdn) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id)); (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)); ShowPrompt(false, "ID %016llX.%08lX\nInsert content failed", getbe64(title_id), getbe32(chunk->id));
return 1; 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 // try to build & insert meta, but ignore result
if (!install) {
CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta));
if (meta) { if (meta) {
if (content_count && cdn) { if (content_count && cdn) {
if (!force_legit || !(getbe16(content_list->type) & 0x01)) { if (!force_legit || !(getbe16(content_list->type) & 0x01)) {
CiaInfo info; CiaInfo info;
GetCiaInfo(&info, &(cia->header)); GetCiaInfo(&info, &(cia->header));
if ((LoadNcchMeta(meta, path_cia, info.offset_content) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) if ((LoadNcchMeta(meta, path_dest, info.offset_content) == 0) && (InsertCiaMeta(path_dest, meta) == 0))
cia->header.size_meta = CIA_META_SIZE; cia->header.size_meta = CIA_META_SIZE;
} }
} else if (content_count) { } else if (content_count) {
snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(content_list->id)); 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)) if ((LoadNcchMeta(meta, path_content, 0) == 0) && (InsertCiaMeta(path_dest, meta) == 0))
cia->header.size_meta = CIA_META_SIZE; cia->header.size_meta = CIA_META_SIZE;
} }
free(meta); free(meta);
} }
}
// write the CIA stub (take #2) // 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 1;
return 0; 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)); void* buffer = (void*) malloc(sizeof(CiaStub));
if (!buffer) return 1; 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); free(buffer);
return ret; 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; NcchExtHeader exthdr;
NcchHeader ncch; NcchHeader ncch;
u8 title_id[8]; 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) || (BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) ||
(BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, 0)) || (BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, 0)) ||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
(WriteCiaStub(cia, path_cia) != 0)) { (!install && (WriteCiaStub(cia, path_dest) != 0))) {
free(cia); free(cia);
return 1; return 1;
} }
// insert NCCH content // insert / install NCCH content
TmdContentChunk* chunk = cia->content_list; TmdContentChunk* chunk = cia->content_list;
memset(chunk, 0, sizeof(TmdContentChunk)); // nothing else to do 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); free(cia);
return 1; return 1;
} }
// optional stuff (proper titlekey / meta data) // optional stuff (proper titlekey / meta data)
if (!install) {
CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta));
if (meta && has_exthdr && (BuildCiaMeta(meta, &exthdr, NULL) == 0) && if (meta && has_exthdr && (BuildCiaMeta(meta, &exthdr, NULL) == 0) &&
(LoadExeFsFile(meta->smdh, path_ncch, 0, "icon", sizeof(meta->smdh), NULL) == 0) && (LoadExeFsFile(meta->smdh, path_ncch, 0, "icon", sizeof(meta->smdh), NULL) == 0) &&
(InsertCiaMeta(path_cia, meta) == 0)) (InsertCiaMeta(path_dest, meta) == 0))
cia->header.size_meta = CIA_META_SIZE; cia->header.size_meta = CIA_META_SIZE;
free(meta); free(meta);
}
// write the CIA stub (take #2) // write the CIA stub (take #2)
FindTitleKey((Ticket*)(&cia->ticket), title_id); FindTitleKey((Ticket*)(&cia->ticket), title_id);
if ((FixTmdHashes(&(cia->tmd)) != 0) || if ((FixTmdHashes(&(cia->tmd)) != 0) ||
(FixCiaHeaderForTmd(&(cia->header), &(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); free(cia);
return 1; return 1;
} }
@ -1570,7 +1920,7 @@ u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) {
return 0; 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; NcchExtHeader exthdr;
NcsdHeader ncsd; NcsdHeader ncsd;
NcchHeader ncch; NcchHeader ncch;
@ -1602,12 +1952,12 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) {
(BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) || (BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) ||
(BuildFakeTmd(&(cia->tmd), title_id, content_count, save_size, 0)) || (BuildFakeTmd(&(cia->tmd), title_id, content_count, save_size, 0)) ||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
(WriteCiaStub(cia, path_cia) != 0)) { (!install && (WriteCiaStub(cia, path_dest) != 0))) {
free(cia); free(cia);
return 1; return 1;
} }
// insert NCSD content // insert / install NCSD content
TmdContentChunk* chunk = cia->content_list; TmdContentChunk* chunk = cia->content_list;
for (u32 i = 0; i < 3; i++) { for (u32 i = 0; i < 3; i++) {
NcchPartition* partition = ncsd.partitions + 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; u32 size = partition->size * NCSD_MEDIA_UNIT;
if (!size) continue; if (!size) continue;
memset(chunk, 0, sizeof(TmdContentChunk)); memset(chunk, 0, sizeof(TmdContentChunk));
chunk->id[3] = chunk->index[1] = i; chunk->id[3] = i;
if (InsertCiaContent(path_cia, path_ncsd, offset, size, chunk++, NULL, false, (i == 0), false) != 0) { 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); free(cia);
return 1; return 1;
} }
} }
// optional stuff (proper titlekey / meta data) // optional stuff (proper titlekey / meta data)
if (!install) {
CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta));
if (meta && (BuildCiaMeta(meta, &exthdr, NULL) == 0) && if (meta && (BuildCiaMeta(meta, &exthdr, NULL) == 0) &&
(LoadExeFsFile(meta->smdh, path_ncsd, NCSD_CNT0_OFFSET, "icon", sizeof(meta->smdh), NULL) == 0) && (LoadExeFsFile(meta->smdh, path_ncsd, NCSD_CNT0_OFFSET, "icon", sizeof(meta->smdh), NULL) == 0) &&
(InsertCiaMeta(path_cia, meta) == 0)) (InsertCiaMeta(path_dest, meta) == 0))
cia->header.size_meta = CIA_META_SIZE; cia->header.size_meta = CIA_META_SIZE;
if (meta) free(meta); if (meta) free(meta);
}
// update title version from cart header (yeah, that's a bit hacky) // update title version from cart header (yeah, that's a bit hacky)
u16 title_version; u16 title_version;
@ -1644,7 +2000,8 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) {
FindTitleKey((Ticket*)&(cia->ticket), title_id); FindTitleKey((Ticket*)&(cia->ticket), title_id);
if ((FixTmdHashes(&(cia->tmd)) != 0) || if ((FixTmdHashes(&(cia->tmd)) != 0) ||
(FixCiaHeaderForTmd(&(cia->header), &(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); free(cia);
return 1; return 1;
} }
@ -1653,7 +2010,7 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) {
return 0; 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; TwlHeader twl;
u8 title_id[8]; u8 title_id[8];
u32 save_size = 0; u32 save_size = 0;
@ -1673,12 +2030,12 @@ u32 BuildCiaFromNdsFile(const char* path_nds, const char* path_cia) {
// some basic sanity checks // some basic sanity checks
// see: https://problemkaputt.de/gbatek.htm#dsicartridgeheader // see: https://problemkaputt.de/gbatek.htm#dsicartridgeheader
// (gamecart dumps are not allowed) // (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]) if ((memcmp(title_id, tidhigh_dsiware, 3) != 0) || !title_id[3])
return 1; return 1;
// convert DSi title ID to 3DS title ID // 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); memcpy(title_id, tidhigh_3ds, 3);
// build the CIA stub // 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) || (BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) ||
(BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, privsave_size)) || (BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, privsave_size)) ||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
(WriteCiaStub(cia, path_cia) != 0)) { (!install && (WriteCiaStub(cia, path_dest) != 0))) {
free(cia); free(cia);
return 1; return 1;
} }
// insert NDS content // insert / install NDS content
TmdContentChunk* chunk = cia->content_list; TmdContentChunk* chunk = cia->content_list;
memset(chunk, 0, sizeof(TmdContentChunk)); // nothing else to do 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); free(cia);
return 1; return 1;
} }
@ -1707,7 +2065,8 @@ u32 BuildCiaFromNdsFile(const char* path_nds, const char* path_cia) {
FindTitleKey((Ticket*)(&cia->ticket), title_id); FindTitleKey((Ticket*)(&cia->ticket), title_id);
if ((FixTmdHashes(&(cia->tmd)) != 0) || if ((FixTmdHashes(&(cia->tmd)) != 0) ||
(FixCiaHeaderForTmd(&(cia->header), &(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); free(cia);
return 1; return 1;
} }
@ -1747,11 +2106,11 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) {
if (filetype & GAME_TMD) if (filetype & GAME_TMD)
ret = BuildCiaFromTmdFile(path, dest, force_legit, filetype & FLAG_NUSCDN); ret = BuildCiaFromTmdFile(path, dest, force_legit, filetype & FLAG_NUSCDN);
else if (filetype & GAME_NCCH) else if (filetype & GAME_NCCH)
ret = BuildCiaFromNcchFile(path, dest); ret = BuildInstallFromNcchFile(path, dest, false);
else if (filetype & GAME_NCSD) else if (filetype & GAME_NCSD)
ret = BuildCiaFromNcsdFile(path, dest); ret = BuildInstallFromNcsdFile(path, dest, false);
else if ((filetype & GAME_NDS) && (filetype & FLAG_DSIW)) else if ((filetype & GAME_NDS) && (filetype & FLAG_DSIW))
ret = BuildCiaFromNdsFile(path, dest); ret = BuildInstallFromNdsFile(path, dest, false);
else ret = 1; else ret = 1;
if (ret != 0) // try to get rid of the borked file 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; 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 // this has very limited uses right now
u32 DumpCxiSrlFromTmdFile(const char* path) { u32 DumpCxiSrlFromTmdFile(const char* path) {
u64 filetype = 0; u64 filetype = 0;
@ -1978,7 +2417,6 @@ u32 LoadSmdhFromGameFile(const char* path, Smdh* smdh) {
if (LoadExeFsFile(smdh, path, NCSD_CNT0_OFFSET, "icon", sizeof(Smdh), NULL) == 0) return 0; if (LoadExeFsFile(smdh, path, NCSD_CNT0_OFFSET, "icon", sizeof(Smdh), NULL) == 0) return 0;
} else if (filetype & GAME_CIA) { // CIA file } else if (filetype & GAME_CIA) { // CIA file
CiaInfo info; CiaInfo info;
if ((fvx_qread(path, &info, 0, 0x20, NULL) != FR_OK) || if ((fvx_qread(path, &info, 0, 0x20, NULL) != FR_OK) ||
(GetCiaInfo(&info, (CiaHeader*) &info) != 0)) return 1; (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; 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) { u32 ShowSmdhTitleInfo(Smdh* smdh, u16* screen) {
const u8 smdh_magic[] = { SMDH_MAGIC }; static const u8 smdh_magic[] = { SMDH_MAGIC };
const u32 lwrap = 24; const u32 lwrap = 24;
u16 icon[SMDH_SIZE_ICON_BIG / sizeof(u16)]; u16 icon[SMDH_SIZE_ICON_BIG / sizeof(u16)];
char desc_l[SMDH_SIZE_DESC_LONG+1]; char desc_l[SMDH_SIZE_DESC_LONG+1];
@ -2198,8 +2636,8 @@ u32 BuildNcchInfoXorpads(const char* destdir, const char* path) {
} }
u32 GetHealthAndSafetyPaths(const char* drv, char* path_cxi, char* path_bak) { u32 GetHealthAndSafetyPaths(const char* drv, char* path_cxi, char* path_bak) {
const u32 tidlow_hs_o3ds[] = { 0x00020300, 0x00021300, 0x00022300, 0, 0x00026300, 0x00027300, 0x00028300 }; static 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_n3ds[] = { 0x20020300, 0x20021300, 0x20022300, 0, 0, 0x20027300, 0 };
// get H&S title id low // get H&S title id low
u32 tidlow_hs = 0; u32 tidlow_hs = 0;

View File

@ -6,6 +6,7 @@ u32 VerifyGameFile(const char* path);
u32 CheckEncryptedGameFile(const char* path); u32 CheckEncryptedGameFile(const char* path);
u32 CryptGameFile(const char* path, bool inplace, bool encrypt); u32 CryptGameFile(const char* path, bool inplace, bool encrypt);
u32 BuildCiaFromGameFile(const char* path, bool force_legit); u32 BuildCiaFromGameFile(const char* path, bool force_legit);
u32 InstallGameFile(const char* path, bool to_emunand);
u32 DumpCxiSrlFromTmdFile(const char* path); u32 DumpCxiSrlFromTmdFile(const char* path);
u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr); u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr);
u32 CompressCode(const char* path, const char* path_out); u32 CompressCode(const char* path, const char* path_out);

View File

@ -68,7 +68,7 @@ u32 SetupSlot0x30(char drv) {
} }
u32 LocateAgbSaveSdBottomSlot(const char* path, AgbSaveHeader* agbsave) { u32 LocateAgbSaveSdBottomSlot(const char* path, AgbSaveHeader* agbsave) {
const u32 save_sizes[] = { static const u32 save_sizes[] = {
GBASAVE_EEPROM_512, GBASAVE_EEPROM_512,
GBASAVE_EEPROM_8K, GBASAVE_EEPROM_8K,
GBASAVE_SRAM_32K, GBASAVE_SRAM_32K,
@ -130,7 +130,7 @@ u32 CheckCmacPath(const char* path) {
return (CalculateFileCmac(path, NULL)) ? 0 : 1; 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 cmac_type = CalculateFileCmac(path, NULL);
u32 offset = 0; 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 if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1; // can't do that here
else offset = 0x000; 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; 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; 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 == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1;
else if (!cmac_type) 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))); u8 hashdata[0x200] __attribute__((aligned(4)));
u32 keyslot = cmac_keyslot[cmac_type]; u32 keyslot = cmac_keyslot[cmac_type];
u32 hashsize = 0; u32 hashsize = 0;
@ -298,13 +298,13 @@ u32 CheckFileCmac(const char* path) {
} else return 1; } else return 1;
} }
u32 FixFileCmac(const char* path) { u32 FixFileCmac(const char* path, bool check_perms) {
u32 cmac_type = CalculateFileCmac(path, NULL); u32 cmac_type = CalculateFileCmac(path, NULL);
if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) { if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) {
return FixCmdCmac(path); return FixCmdCmac(path, check_perms);
} else if (cmac_type) { } else if (cmac_type) {
u8 ccmac[16]; 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; } else return 1;
} }
@ -344,7 +344,7 @@ u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv) {
return 0; 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))); u8 cmac[16] __attribute__((aligned(4)));
u32 keyslot = ((*path == 'A') || (*path == 'B')) ? 0x30 : 0x0B; u32 keyslot = ((*path == 'A') || (*path == 'B')) ? 0x30 : 0x0B;
bool fixed = false; 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) // 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) || 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); free(cmd_data);
return 1; 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 // now, check the CMAC@0x10
use_aeskey(keyslot); use_aeskey(keyslot);
@ -394,7 +401,7 @@ u32 CheckFixCmdCmac(const char* path, bool fix) {
} }
// further checking will be more complicated // 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 n_entries = cmd->n_entries;
u32* cnt_id = (u32*) (cmd + 1); u32* cnt_id = (u32*) (cmd + 1);
u8* cnt_cmac = (u8*) (cnt_id + cmd->n_entries + cmd->n_cmacs); u8* cnt_cmac = (u8*) (cnt_id + cmd->n_entries + cmd->n_cmacs);
@ -406,9 +413,14 @@ u32 CheckFixCmdCmac(const char* path, bool fix) {
if (*cnt_id == 0xFFFFFFFF) continue; // unavailable content if (*cnt_id == 0xFFFFFFFF) continue; // unavailable content
snprintf(name_content, 32, "%s%08lX.app", (is_dlc) ? "00000000/" : "", *cnt_id); snprintf(name_content, 32, "%s%08lX.app", (is_dlc) ? "00000000/" : "", *cnt_id);
if (fvx_qread(path_content, hashdata, 0x100, 0x100, NULL) != FR_OK) { if (fvx_qread(path_content, hashdata, 0x100, 0x100, NULL) != FR_OK) {
if (fix_missing) {
*cnt_id = 0xFFFFFFFF;
continue;
} else {
free(cmd_data); free(cmd_data);
return 1; // failed to read content return 1; // failed to read content
} }
}
memcpy(hashdata + 0x100, &cnt_idx, 4); memcpy(hashdata + 0x100, &cnt_idx, 4);
memcpy(hashdata + 0x104, cnt_id, 4); memcpy(hashdata + 0x104, cnt_id, 4);
// hash block complete, check it // hash block complete, check it
@ -427,7 +439,7 @@ u32 CheckFixCmdCmac(const char* path, bool fix) {
} }
// if fixing is enabled, write back cmd file // 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)) { (fvx_qwrite(path, cmd_data, 0, cmd_size, NULL) != FR_OK)) {
free(cmd_data); free(cmd_data);
return 1; return 1;
@ -459,14 +471,14 @@ u32 RecursiveFixFileCmacWorker(char* path) {
} else if (fno.fattrib & AM_DIR) { // directory, recurse through it } else if (fno.fattrib & AM_DIR) { // directory, recurse through it
if (RecursiveFixFileCmacWorker(path) != 0) err = 1; if (RecursiveFixFileCmacWorker(path) != 0) err = 1;
} else if (CheckCmacPath(path) == 0) { // file, try to fix the CMAC } 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); ShowString("%s\nFixing CMACs, please wait...", pathstr);
} }
} }
f_closedir(&pdir); f_closedir(&pdir);
*(--fname) = '\0'; *(--fname) = '\0';
} else if (CheckCmacPath(path) == 0) // fix single file CMAC } else if (CheckCmacPath(path) == 0) // fix single file CMAC
return FixFileCmac(path); return FixFileCmac(path, true);
return err; return err;
} }

View File

@ -2,16 +2,16 @@
#include "common.h" #include "common.h"
#define ReadFileCmac(path, cmac) ReadWriteFileCmac(path, cmac, false) #define ReadFileCmac(path, cmac) ReadWriteFileCmac(path, cmac, false, true)
#define WriteFileCmac(path, cmac) ReadWriteFileCmac(path, cmac, true) #define WriteFileCmac(path, cmac, check_perms) ReadWriteFileCmac(path, cmac, true, check_perms)
#define CheckCmdCmac(path) CheckFixCmdCmac(path, false) #define CheckCmdCmac(path) CheckFixCmdCmac(path, false, true)
#define FixCmdCmac(path) CheckFixCmdCmac(path, true) #define FixCmdCmac(path, check_perms) CheckFixCmdCmac(path, true, check_perms)
u32 CheckCmacPath(const char* path); 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 CalculateFileCmac(const char* path, u8* cmac);
u32 CheckFileCmac(const char* path); 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 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); u32 RecursiveFixFileCmac(const char* path);

View File

@ -607,7 +607,7 @@ u32 SafeInstallFirm(const char* path, u32 slots) {
} }
u32 SafeInstallKeyDb(const char* path) { 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))); u8 keydb[KEYDB_PERFECT_SIZE] __attribute__((aligned(4)));
char pathstr[32 + 1]; // truncated path string char pathstr[32 + 1]; // truncated path string

View File

@ -110,6 +110,7 @@ typedef enum {
CMD_ID_DECRYPT, CMD_ID_DECRYPT,
CMD_ID_ENCRYPT, CMD_ID_ENCRYPT,
CMD_ID_BUILDCIA, CMD_ID_BUILDCIA,
CMD_ID_INSTALL,
CMD_ID_EXTRCODE, CMD_ID_EXTRCODE,
CMD_ID_CMPRCODE, CMD_ID_CMPRCODE,
CMD_ID_SDUMP, CMD_ID_SDUMP,
@ -139,7 +140,7 @@ typedef struct {
char content[_VAR_CNT_LEN]; char content[_VAR_CNT_LEN];
} Gm9ScriptVar; } Gm9ScriptVar;
Gm9ScriptCmd cmd_list[] = { static const Gm9ScriptCmd cmd_list[] = {
{ CMD_ID_NONE , "#" , 0, 0 }, // dummy entry { CMD_ID_NONE , "#" , 0, 0 }, // dummy entry
{ CMD_ID_NOT , _CMD_NOT , 0, 0 }, // inverts the output of the following command { 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 { 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_DECRYPT , "decrypt" , 1, 0 },
{ CMD_ID_ENCRYPT , "encrypt" , 1, 0 }, { CMD_ID_ENCRYPT , "encrypt" , 1, 0 },
{ CMD_ID_BUILDCIA, "buildcia", 1, _FLG('l') }, { CMD_ID_BUILDCIA, "buildcia", 1, _FLG('l') },
{ CMD_ID_INSTALL , "install" , 1, _FLG('e') },
{ CMD_ID_EXTRCODE, "extrcode", 2, 0 }, { CMD_ID_EXTRCODE, "extrcode", 2, 0 },
{ CMD_ID_CMPRCODE, "cmprcode", 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_APPLYIPS, "applyips", 3, 0 },
{ CMD_ID_APPLYBPS, "applybps", 3, 0 }, { CMD_ID_APPLYBPS, "applybps", 3, 0 },
{ CMD_ID_APPLYBPM, "applybpm", 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) { 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++) { for (u32 i = 0; i < (sizeof(cmd_list)/sizeof(Gm9ScriptCmd)); i++) {
if (strncmp(cmd_list[i].cmd, cmd, len) == 0) { 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, "--before", len) == 0) flag_char = 'b';
else if (strncmp(str, "--include_dirs", len) == 0) flag_char = 'd'; 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, "--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, "--first", len) == 0) flag_char = 'f';
else if (strncmp(str, "--hash", len) == 0) flag_char = 'h'; else if (strncmp(str, "--hash", len) == 0) flag_char = 'h';
else if (strncmp(str, "--keysel", len) == 0) flag_char = 'k'; 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); ret = (BuildCiaFromGameFile(argv[0], (flags & _FLG('l'))) == 0);
if (err_str) snprintf(err_str, _ERR_STR_LEN, "build CIA failed"); 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) { else if (id == CMD_ID_EXTRCODE) {
u64 filetype = IdentifyFileType(argv[0]); u64 filetype = IdentifyFileType(argv[0]);
if (!FTYPE_HASCODE(filetype)) { if (!FTYPE_HASCODE(filetype)) {

View File

@ -4,6 +4,7 @@
#include "vdisadiff.h" #include "vdisadiff.h"
#include "bdri.h" #include "bdri.h"
#include "vff.h" #include "vff.h"
#include "ui.h"
#define VBDRI_MAX_ENTRIES 8192 // Completely arbitrary #define VBDRI_MAX_ENTRIES 8192 // Completely arbitrary
@ -14,7 +15,9 @@
#define VFLAG_TICKDIR (VFLAG_UNKNOWN|VFLAG_HOMEBREW|VFLAG_ESHOP|VFLAG_SYSTEM) #define VFLAG_TICKDIR (VFLAG_UNKNOWN|VFLAG_HOMEBREW|VFLAG_ESHOP|VFLAG_SYSTEM)
#define NAME_TIE "%016llX" #define NAME_TIE "%016llX"
#define NAME_TIE_LEN 16
#define NAME_TIK "%016llX.%08lX.tik" // title id / console id #define NAME_TIK "%016llX.%08lX.tik" // title id / console id
#define NAME_TIK_LEN (16 + 1 + 8 + 4)
#define PART_PATH "D:/partitionA.bin" #define PART_PATH "D:/partitionA.bin"
@ -39,7 +42,6 @@ static u8* title_ids = NULL;
static TickInfoEntry* tick_info = NULL; static TickInfoEntry* tick_info = NULL;
static u8* cached_entry = NULL; static u8* cached_entry = NULL;
static int cache_index; static int cache_index;
//static u32 cache_size;
void DeinitVBDRIDrive(void) { void DeinitVBDRIDrive(void) {
free(title_ids); free(title_ids);
@ -52,6 +54,38 @@ void DeinitVBDRIDrive(void) {
cache_index = -1; 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 InitVBDRIDrive(void) { // prerequisite: .db file mounted as virtual diff image
u64 mount_state = CheckVDisaDiffDrive(); u64 mount_state = CheckVDisaDiffDrive();
if (!(mount_state & SYS_DIFF)) return 0; if (!(mount_state & SYS_DIFF)) return 0;
@ -67,34 +101,17 @@ u64 InitVBDRIDrive(void) { // prerequisite: .db file mounted as virtual diff ima
return 0; return 0;
} }
if (is_tickdb) { if (!is_tickdb && ((cached_entry = malloc(sizeof(TitleInfoEntry))) == NULL)) {
tick_info = (TickInfoEntry*) malloc(num_entries * sizeof(TickInfoEntry));
if (!tick_info) {
DeinitVBDRIDrive(); DeinitVBDRIDrive();
return 0; 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)
return 0;
return mount_state; return mount_state;
} }
u64 CheckVBDRIDrive(void) { u64 CheckVBDRIDrive(void) {
u64 mount_state = CheckVDisaDiffDrive(); 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; mount_state : 0;
} }
@ -103,7 +120,7 @@ bool ReadVBDRIDir(VirtualFile* vfile, VirtualDir* vdir) {
return false; return false;
if (vdir->flags & VFLAG_TICKDIR) { // ticket dir if (vdir->flags & VFLAG_TICKDIR) { // ticket dir
if (!is_tickdb) if (!is_tickdb || (!tick_info && !SortVBDRITickets()))
return false; return false;
while (++vdir->index < (int) num_entries) { while (++vdir->index < (int) num_entries) {
@ -156,19 +173,19 @@ bool ReadVBDRIDir(VirtualFile* vfile, VirtualDir* vdir) {
} }
bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) { 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; u64 tid;
if (sscanf(buf, "/%016llX", &tid) != 1) return false; u32 console_id;
if (tid == 0) 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; return false;
tid = getbe64((u8*)&tid); tid = getbe64((u8*)&tid);
@ -189,7 +206,7 @@ bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) {
u8* new_title_ids = realloc(title_ids, new_num_entries * 8); u8* new_title_ids = realloc(title_ids, new_num_entries * 8);
if (!new_title_ids) if (!new_title_ids)
return false; return false;
if (is_tickdb) { if (tick_info) {
TickInfoEntry* new_tick_info = realloc(tick_info, new_num_entries * sizeof(TickInfoEntry)); TickInfoEntry* new_tick_info = realloc(tick_info, new_num_entries * sizeof(TickInfoEntry));
if (!new_tick_info) if (!new_tick_info)
return false; return false;
@ -213,7 +230,7 @@ bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) {
memcpy(title_ids + entry_index * 8, &tid, 8); memcpy(title_ids + entry_index * 8, &tid, 8);
if (is_tickdb) { if (tick_info) {
tick_info[entry_index].type = 3; tick_info[entry_index].type = 3;
tick_info[entry_index].size = TICKET_COMMON_SIZE; tick_info[entry_index].size = TICKET_COMMON_SIZE;
memset(tick_info[entry_index].console_id, 0, 4); 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; bool resize = false;
if (offset + count > vfile->size) { if (offset + count > vfile->size) {
if (!is_tickdb) if (!is_tickdb || (!tick_info && !SortVBDRITickets()))
return false; return false;
vfile->size = offset + count; vfile->size = offset + count;
resize = true; 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 (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 : tick_info[vfile->offset].type = (cached_entry[0x1F1] > 1) ? 3 :
((ValidateTicketSignature((Ticket*)(void*)cached_entry) != 0) ? 1 : ((cached_entry[0x1F1] == 1) ? 2 : 0)); ((ValidateTicketSignature((Ticket*)(void*)cached_entry) != 0) ? 1 : ((cached_entry[0x1F1] == 1) ? 2 : 0));

View File

@ -672,7 +672,7 @@ bool BuildVGameFirmDir(void) {
} }
bool BuildVGameTadDir(void) { bool BuildVGameTadDir(void) {
const char* name_type[] = { NAME_TAD_TYPES }; static const char* name_type[] = { NAME_TAD_TYPES };
VirtualFile* templates = templates_tad; VirtualFile* templates = templates_tad;
u32 content_offset = 0; u32 content_offset = 0;
u32 n = 0; u32 n = 0;

View File

@ -21,11 +21,11 @@
#define HAS_OTP_KEY (HAS_BOOT9 || ((LoadKeyFromFile(NULL, 0x11, 'N', "OTP") == 0) && (LoadKeyFromFile(NULL , 0x11, 'I', "OTP") == 0))) #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 // 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, 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 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, 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 0x9C, 0xBE, 0xDC, 0x32, 0xF9, 0x03, 0xAE, 0xBE, 0x90, 0x61, 0x44, 0xDE, 0xA7, 0xA0, 0x75, 0x12
}; };

View File

@ -2,6 +2,8 @@
#include "types.h" #include "types.h"
#include <bfn.h>
/* Status Register flags */ /* Status Register flags */
#define SR_USR_MODE (0x10) #define SR_USR_MODE (0x10)
#define SR_FIQ_MODE (0x11) #define SR_FIQ_MODE (0x11)
@ -69,12 +71,7 @@
#ifndef __ASSEMBLER__ #ifndef __ASSEMBLER__
/* ARM Private Memory Region */ // only accessible from ARM mode
#ifdef ARM11
#define REG_ARM_PMR(off, type) ((volatile type*)(0x17E00000 + (off)))
#endif
#define ARM_MCR(cp, op1, reg, crn, crm, op2) asm_v( \ #define ARM_MCR(cp, op1, reg, crn, crm, op2) asm_v( \
"MCR " #cp ", " #op1 ", %[R], " #crn ", " #crm ", " #op2 "\n\t" \ "MCR " #cp ", " #op1 ", %[R], " #crn ", " #crm ", " #op2 "\n\t" \
:: [R] "r"(reg) : "memory","cc") :: [R] "r"(reg) : "memory","cc")
@ -91,44 +88,44 @@
"MRS %[R], " #cp "\n\t" \ "MRS %[R], " #cp "\n\t" \
: [R] "=r"(reg) :: "memory","cc") : [R] "=r"(reg) :: "memory","cc")
/* ARM Private Memory Region */
#ifdef ARM11 #ifdef ARM11
#define ARM_CPS(m) asm_v("CPS " #m) #define REG_ARM_PMR(off, type) ((volatile type*)(0x17E00000 + (off)))
#define ARM_CPSID(m) asm_v("CPSID " #m)
#define ARM_CPSIE(m) asm_v("CPSIE " #m)
/*
* 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) { 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) { 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) { 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) { 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) { 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) { static inline u32 ARM_GetACR(void) {
u32 acr; u32 acr;
ARM_MRC(p15, 0, acr, c1, c0, 1); ARM_MRC(p15, 0, acr, c1, c0, 1);
@ -138,46 +135,15 @@
static inline void ARM_SetACR(u32 acr) { static inline void ARM_SetACR(u32 acr) {
ARM_MCR(p15, 0, acr, c1, c0, 1); ARM_MCR(p15, 0, acr, c1, c0, 1);
} }
#endif
#endif
/* /*
* A Data Synchronization Barrier (DSB) completes when all * A Data Synchronization Barrier (DSB) completes when all
* instructions before this instruction complete. * instructions before this instruction complete.
*/ */
static inline void ARM_DSB(void) { static inline void ARM_DSB(void) {
ARM_MCR(p15, 0, 0, c7, c10, 4); ((void (*)(void))(BFN_DATASYNCBARRIER))();
}
/* 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
} }
/* CPU ID */ /* CPU ID */
@ -191,131 +157,61 @@ static inline u32 ARM_CoreID(void) {
return id & 3; return id & 3;
} }
/* Status Register */ /* Status register management */
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
}
static inline u32 ARM_EnterCritical(void) { static inline u32 ARM_EnterCritical(void) {
u32 stat = ARM_GetCPSR(); return ((u32 (*)(void))(BFN_ENTERCRITICALSECTION))();
ARM_DisableInterrupts();
return stat & SR_NOINT;
} }
static inline void ARM_LeaveCritical(u32 stat) { 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 */ /* Cache functions */
static inline void ARM_InvIC(void) { static inline void ARM_InvIC(void) {
#ifdef ARM9 ((void (*)(void))(BFN_INVALIDATE_ICACHE))();
ARM_MCR(p15, 0, 0, c7, c5, 0);
#else
ARM_MCR(p15, 0, 0, c7, c7, 0);
#endif
} }
static inline void ARM_InvIC_Range(void *base, u32 len) { static inline void ARM_InvIC_Range(void *base, u32 len) {
u32 addr = (u32)base & ~0x1F; ((void (*)(u32, u32))(BFN_INVALIDATE_ICACHE_RANGE))((u32)base, len);
len >>= 5; #ifdef ARM11 // make sure to also invalidate the branch target cache
((void (*)(u32, u32))(BFN_INVALIDATE_BT_CACHE_RANGE))((u32)base, len);
do {
#ifdef ARM9
ARM_MCR(p15, 0, addr, c7, c5, 1);
#else
ARM_MCR(p15, 0, addr, c7, c7, 1);
#endif #endif
addr += 0x20;
} while(len--);
} }
static inline void ARM_InvDC(void) { 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) { static inline void ARM_InvDC_Range(void *base, u32 len) {
u32 addr = (u32)base & ~0x1F; ((void (*)(u32, u32))(BFN_INVALIDATE_DCACHE_RANGE))((u32)base, len);
len >>= 5;
do {
ARM_MCR(p15, 0, addr, c7, c6, 1);
addr += 0x20;
} while(len--);
} }
static inline void ARM_WbDC(void) { static inline void ARM_WbDC(void) {
#ifdef ARM9 ((void (*)(void))(BFN_WRITEBACK_DCACHE))();
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
} }
static inline void ARM_WbDC_Range(void *base, u32 len) { static inline void ARM_WbDC_Range(void *base, u32 len) {
u32 addr = (u32)base & ~0x1F; ((void (*)(u32, u32))(BFN_WRITEBACK_DCACHE_RANGE))((u32)base, len);
len >>= 5;
do {
ARM_MCR(p15, 0, addr, c7, c10, 1);
addr += 0x20;
} while(len--);
} }
static inline void ARM_WbInvDC(void) { static inline void ARM_WbInvDC(void) {
#ifdef ARM9 ((void (*)(void))(BFN_WRITEBACK_INVALIDATE_DCACHE))();
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
} }
static inline void ARM_WbInvDC_Range(void *base, u32 len) { static inline void ARM_WbInvDC_Range(void *base, u32 len) {
u32 addr = (u32)base & ~0x1F; ((void (*)(u32, u32))(BFN_WRITEBACK_INVALIDATE_DCACHE_RANGE))((u32)base, len);
len >>= 5; }
do { static inline void ARM_WaitCycles(u32 cycles) {
ARM_MCR(p15, 0, addr, c7, c14, 1); ((void (*)(u32))(BFN_WAITCYCLES))(cycles);
addr += 0x20;
} while(len--);
} }
static inline void ARM_BKPT(void) { static inline void ARM_BKPT(void) {

135
common/bfn.h Normal file
View File

@ -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

View File

@ -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

View File

@ -29,9 +29,6 @@
#define abs(x) \ #define abs(x) \
(((x) >= 0) ? (x) : -(x)) (((x) >= 0) ? (x) : -(x))
#define int_sign(x) \
(((x) > 0) - ((x) < 0))
#define clamp(x, min, max) \ #define clamp(x, min, max) \
((x) < (max) ? ((x) > (min) ? (x) : (min)) : (max)) ((x) < (max) ? ((x) > (min) ? (x) : (min)) : (max))
@ -61,10 +58,6 @@
#define assert(x) \ #define assert(x) \
(!!(x) ? (void)0 : __builtin_trap()) (!!(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(...) \ #define STATIC_ASSERT(...) \
_Static_assert((__VA_ARGS__), #__VA_ARGS__) _Static_assert((__VA_ARGS__), #__VA_ARGS__)

View File

@ -36,13 +36,15 @@ typedef struct {
#ifdef ARM9 #ifdef ARM9
#include <pxi.h> #include <pxi.h>
extern SystemSHMEM *shmemGlobalBase;
static inline SystemSHMEM *ARM_GetSHMEM(void) static inline SystemSHMEM *ARM_GetSHMEM(void)
{ {
return (SystemSHMEM*)ARM_GetTID(); return shmemGlobalBase;
} }
static inline void ARM_InitSHMEM(void) 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 #endif