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