mirror of
https://github.com/zeldaret/st
synced 2026-09-04 10:12:27 -04:00
Update dsd to v0.12.0; dsprot OK (#135)
* `Actor::mType` field * Remove fake symbol `data_027e0254` * `dsd format` * eur: Enable dsprot decomp * jp: Enable dsprot decomp * dsprot OK * `dsd format` * Use new `weak` attribute * Add relocations for exception table link-time constants * Add exception table symbols * Bump dsd to v0.12.0 * `#define FALSE 1` ??? Why did I make it 1? * Truncate `data_ov001_020c27a8` That data belongs to dsprot
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
- -Wno-int-to-pointer-cast
|
||||
Diagnostics:
|
||||
Suppress:
|
||||
- sizeof_alignof_function_type
|
||||
- bad_reinterpret_cast_small_int
|
||||
- typecheck_pointer_arith_void_type
|
||||
@@ -0,0 +1 @@
|
||||
Credits to taxicat1 aka Mow: https://github.com/taxicat1/dsprot/
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#ifndef DSP_NO_NITRO
|
||||
|
||||
#include <types.h> // For u32
|
||||
#include <nitro/types.h> // For u32
|
||||
|
||||
#else /* DSP_NO_NITRO */
|
||||
|
||||
@@ -113,8 +113,14 @@ extern u32 DSProt_DetectNotDummy(void *callback);
|
||||
|
||||
#else /* SDK_ASM */
|
||||
|
||||
.public DSProt_DetectFlashcart.public DSProt_DetectNotFlashcart.public DSProt_DetectEmulator.public DSProt_DetectNotEmulator
|
||||
.public DSProt_DetectDummy.public DSProt_DetectNotDummy
|
||||
// clang-format off
|
||||
.public DSProt_DetectFlashcart
|
||||
.public DSProt_DetectNotFlashcart
|
||||
.public DSProt_DetectEmulator
|
||||
.public DSProt_DetectNotEmulator
|
||||
.public DSProt_DetectDummy
|
||||
.public DSProt_DetectNotDummy
|
||||
// clang-format on
|
||||
|
||||
#endif /* SDK_ASM */
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
#include "dsprot/encoding_constants.h"
|
||||
|
||||
.macro arm_func_start name
|
||||
.balign 4, 0
|
||||
.global \name
|
||||
.type \name, @function
|
||||
.arm
|
||||
.endm
|
||||
|
||||
.macro local_arm_func_start name
|
||||
.balign 4, 0
|
||||
.type \name, @function
|
||||
.arm
|
||||
.endm
|
||||
|
||||
.macro arm_func_end name
|
||||
.size \name, .-\name
|
||||
.endm
|
||||
|
||||
|
||||
.macro sinit sinit_func
|
||||
.type NitroStaticInit_\@, @object
|
||||
NitroStaticInit_\@:
|
||||
.word \sinit_func
|
||||
.size NitroStaticInit_\@, .-NitroStaticInit_\@
|
||||
.endm
|
||||
|
||||
|
||||
.public DSProt_BSS
|
||||
.public Encryptor_DecryptFunction
|
||||
.public Encryptor_EncryptFunction
|
||||
|
||||
.macro run_encrypted_func func, length, key
|
||||
stmfd sp!, {r4-r7} ; `r0`-`r3` contain the arguments that must be passed into the inner function.
|
||||
stmfd sp!, {r0-r3} ; Stack manipulation to move `r0`-`r3` into `r4`-`r7` for storage.
|
||||
ldmfd sp!, {r4-r7} ; Original values of `r4`-`r7` get pushed onto the stack.
|
||||
ldr r1, func_\@ ; Get second function decrypter argument, obfuscated function address.
|
||||
adr r3, storage_\@ ; `lr` cannot be stored on the stack when we call the inner function,
|
||||
str lr, [r3] ; so it is stored in the pool below.
|
||||
ldr r2, length_\@ ; Get third function decrypter argument, obfuscated length.
|
||||
ldr r0, key_\@ ; Get first function decrypter argument, obfuscated key.
|
||||
bl Encryptor_DecryptFunction ; Call function decrypter with args (key, addr, len).
|
||||
mov ip, r0 ; Function decrypter returns the de-obfuscated function address. Save it in `ip`.
|
||||
stmfd sp!, {r4-r7} ; Stack manipulation again to restore the original `r0`-`r3`.
|
||||
ldmfd sp!, {r0-r3} ;
|
||||
ldmfd sp!, {r4-r7} ; Stack is now restored, so `sp` is correct for the inner function call.
|
||||
blx ip ; Call inner function.
|
||||
stmfd sp!, {r4} ; `r4` about to be used as storage space for the inner function return, so save it on the stack.
|
||||
mov r4, r0 ; Copy the return from the inner function into `r4` so we can can run the function re-encrypter.
|
||||
ldr r1, func_\@ ; Get second function encrypter argument, obfuscated function address.
|
||||
ldr r2, length_\@ ; Get third function encrypter argument, obfuscated length.
|
||||
ldr r0, key_\@ ; Get first function encrypter argument, obfuscated key.
|
||||
bl Encryptor_EncryptFunction ; Call function encrypter.
|
||||
str r0, key_\@ ; Function encrypter returns the new key it used, store this back into the pool for next time.
|
||||
mov r0, r4 ; Restore the return value from the inner function from `r4` to `r0`.
|
||||
ldmfd sp!, {r4} ; Restore the original value of `r4` from the stack.
|
||||
ldr lr, storage_\@ ; Load `lr` return address back from the storage space in the pool.
|
||||
str pc, storage_\@ ; Store `pc` in its place, presumably just to overwrite the return address.
|
||||
bx lr ; Return with return value from inner function.
|
||||
storage_\@:
|
||||
.word DSProt_BSS + 1
|
||||
length_\@:
|
||||
.word DSProt_BSS + \length + ENC_VAL_1
|
||||
key_\@:
|
||||
.word DSProt_BSS + \key + ENC_VAL_1
|
||||
func_\@:
|
||||
.word \func + ENC_VAL_1
|
||||
.endm
|
||||
|
||||
|
||||
.public Encryptor_DecodeFunctionTable
|
||||
|
||||
.macro decode_func_table table
|
||||
stmdb sp!, {lr}
|
||||
adr r0, \table
|
||||
bl Encryptor_DecodeFunctionTable
|
||||
ldmia sp!, {pc}
|
||||
.endm
|
||||
|
||||
.macro func_table_entry func, size
|
||||
.word \func + ENC_VAL_1, DSProt_BSS + \size + ENC_VAL_1
|
||||
.endm
|
||||
|
||||
.macro func_table_end
|
||||
.word 0, 0
|
||||
.endm
|
||||
|
||||
.macro garbage_ref ref
|
||||
.word \ref + ENC_VAL_1
|
||||
.endm
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef BSS_H
|
||||
#define BSS_H
|
||||
|
||||
#include <nitro/types.h>
|
||||
|
||||
extern u8 DSProt_BSS[4];
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef ENCODING_CONSTANTS_H
|
||||
#define ENCODING_CONSTANTS_H
|
||||
|
||||
#define INS_OPCODE_LINKBIT (0x01)
|
||||
#define INS_OPCODE_MASK (0xFF000000)
|
||||
#define INS_OPCODE_SHIFT (24)
|
||||
#define INS_OPERANDS_MASK (0x00FFFFFF)
|
||||
|
||||
#define ADDR_PLUS_ADDEND(ref, addend) ((u32) (&ref + ((addend) / sizeof(ref))))
|
||||
|
||||
#define ENC_VAL_1 (0x1400)
|
||||
#define ENC_VAL_2 ((ENC_VAL_1 >> 2) + 2)
|
||||
|
||||
#define ENC_BYTE_A (0x65)
|
||||
#define ENC_BYTE_B (0x56)
|
||||
#define ENC_BYTE_C (0x68)
|
||||
#define ENC_BYTE_D (0xD0)
|
||||
|
||||
#define ENC_SBOX_XOR (0x01)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef ENCRYPTOR_H
|
||||
#define ENCRYPTOR_H
|
||||
|
||||
#include <nitro/types.h>
|
||||
|
||||
typedef struct {
|
||||
u32 obfs_addr;
|
||||
u32 obfs_size;
|
||||
} FuncInfo;
|
||||
|
||||
enum {
|
||||
INS_TYPE_OTHER = 0,
|
||||
INS_TYPE_BLXIMM,
|
||||
INS_TYPE_BL,
|
||||
INS_TYPE_B
|
||||
};
|
||||
|
||||
u32 Encryptor_CategorizeInstruction(u32 instruction);
|
||||
void Encryptor_DecodeFunctionTable(FuncInfo *functions);
|
||||
void *Encryptor_DecryptFunction(u32 key, u32 func_addr, u32 size);
|
||||
u32 Encryptor_EncryptFunction(u32 key, u32 func_addr, u32 size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef GARBAGE_H
|
||||
#define GARBAGE_H
|
||||
|
||||
#include <nitro/types.h>
|
||||
|
||||
extern const u32 DSProt_Garbage[6];
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef INTEGRITY_H
|
||||
#define INTEGRITY_H
|
||||
|
||||
#include <nitro/types.h>
|
||||
|
||||
// Assembly decryption wrappers
|
||||
extern u32 RunEncrypted_Integrity_MACOwner_IsBad(void);
|
||||
extern u32 RunEncrypted_Integrity_MACOwner_IsGood(void);
|
||||
extern u32 RunEncrypted_Integrity_ROMTest_IsBad(void);
|
||||
extern u32 RunEncrypted_Integrity_ROMTest_IsGood(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef MAC_OWNER_H
|
||||
#define MAC_OWNER_H
|
||||
|
||||
#include <nitro/types.h>
|
||||
|
||||
// Assembly decryption wrappers
|
||||
extern u32 RunEncrypted_MACOwner_IsBad(void);
|
||||
extern u32 RunEncrypted_MACOwner_IsGood(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef RC4_H
|
||||
#define RC4_H
|
||||
|
||||
#include <nitro/types.h>
|
||||
|
||||
u32 RC4_InitAndEncryptInstructions(void *key, void *dst, void *src, u32 size);
|
||||
u32 RC4_InitAndDecryptInstructions(void *key, void *dst, void *src, u32 size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef ROM_TEST_H
|
||||
#define ROM_TEST_H
|
||||
|
||||
#include <nitro/types.h>
|
||||
|
||||
// Assembly decryption wrappers
|
||||
extern u32 RunEncrypted_ROMTest_IsBad(void);
|
||||
extern u32 RunEncrypted_ROMTest_IsGood(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef ROM_UTIL_H
|
||||
#define ROM_UTIL_H
|
||||
|
||||
#include <nitro/types.h>
|
||||
|
||||
// Assembly decryption wrappers
|
||||
extern void RunEncrypted_ROMUtil_Read(void *dest, u32 addr, s32 num_bytes);
|
||||
extern u32 RunEncrypted_ROMUtil_CRC32(void *buf, u32 size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
/* No dedicated header */
|
||||
|
||||
#include "dsprot.h"
|
||||
#include "dsprot/encoding_constants.h"
|
||||
#include "dsprot/encryptor.h"
|
||||
#include "dsprot/integrity.h"
|
||||
#include "dsprot/mac_owner.h"
|
||||
#include "dsprot/rom_test.h"
|
||||
|
||||
// Functions to be encrypted (cannot be called directly)
|
||||
u32 DetectFlashcart(void *callback);
|
||||
u32 DetectNotFlashcart(void *callback);
|
||||
u32 DetectEmulator(void *callback);
|
||||
u32 DetectNotEmulator(void *callback);
|
||||
u32 DetectDummy(void *callback);
|
||||
u32 DetectNotDummy(void *callback);
|
||||
|
||||
#define DSP_OBFS_OFFSET (0x320)
|
||||
|
||||
#define FUNC_QUEUE_END (0)
|
||||
|
||||
typedef u32 (*TaskFunc)(void);
|
||||
typedef void (*CallbackFunc)(void);
|
||||
|
||||
enum {
|
||||
EXPECT_FALSE,
|
||||
EXPECT_TRUE
|
||||
};
|
||||
|
||||
// This was likely not originally an inline, but an inline is able to match here nicely
|
||||
static inline u32 dsprotMain(u32 *func_queue, int expected_result, void *callback) {
|
||||
BOOL ret;
|
||||
BOOL func_result;
|
||||
s32 compare_sum;
|
||||
u32 i;
|
||||
s32 func_result_sum;
|
||||
|
||||
// These two bit arrays must be signed to match
|
||||
compare_sum = 0;
|
||||
func_result_sum = 0;
|
||||
|
||||
for (i = 0; func_queue[i] != FUNC_QUEUE_END; i++) {
|
||||
func_result = ((TaskFunc) (func_queue[i] - ENC_VAL_1 - DSP_OBFS_OFFSET))() != 0;
|
||||
|
||||
func_result_sum += func_result;
|
||||
func_result_sum <<= 1;
|
||||
|
||||
compare_sum += TRUE;
|
||||
compare_sum <<= 1;
|
||||
}
|
||||
|
||||
if (expected_result == EXPECT_TRUE) {
|
||||
ret = (func_result_sum >> 1) == (compare_sum >> 1);
|
||||
} else {
|
||||
ret = ((func_result_sum & compare_sum) >> 1) != 0;
|
||||
}
|
||||
|
||||
if (callback != NULL && ret) {
|
||||
((CallbackFunc) callback)();
|
||||
}
|
||||
|
||||
return (u32) ret;
|
||||
}
|
||||
|
||||
u32 DetectFlashcart(void *callback) {
|
||||
u32 func_queue[32];
|
||||
|
||||
func_queue[2] = FUNC_QUEUE_END;
|
||||
func_queue[0] = ADDR_PLUS_ADDEND(RunEncrypted_ROMTest_IsBad, ENC_VAL_1) + DSP_OBFS_OFFSET;
|
||||
func_queue[1] = ADDR_PLUS_ADDEND(RunEncrypted_Integrity_ROMTest_IsBad, ENC_VAL_1) + DSP_OBFS_OFFSET;
|
||||
|
||||
return dsprotMain(&func_queue[0], EXPECT_FALSE, callback);
|
||||
}
|
||||
|
||||
u32 DetectNotFlashcart(void *callback) {
|
||||
u32 func_queue[32];
|
||||
|
||||
func_queue[2] = FUNC_QUEUE_END;
|
||||
func_queue[0] = ADDR_PLUS_ADDEND(RunEncrypted_ROMTest_IsGood, ENC_VAL_1) + DSP_OBFS_OFFSET;
|
||||
func_queue[1] = ADDR_PLUS_ADDEND(RunEncrypted_Integrity_ROMTest_IsGood, ENC_VAL_1) + DSP_OBFS_OFFSET;
|
||||
|
||||
return dsprotMain(&func_queue[0], EXPECT_TRUE, callback);
|
||||
}
|
||||
|
||||
u32 DetectEmulator(void *callback) {
|
||||
u32 func_queue[32];
|
||||
|
||||
func_queue[2] = FUNC_QUEUE_END;
|
||||
func_queue[0] = ADDR_PLUS_ADDEND(RunEncrypted_MACOwner_IsBad, ENC_VAL_1) + DSP_OBFS_OFFSET;
|
||||
func_queue[1] = ADDR_PLUS_ADDEND(RunEncrypted_Integrity_MACOwner_IsBad, ENC_VAL_1) + DSP_OBFS_OFFSET;
|
||||
|
||||
return dsprotMain(&func_queue[0], EXPECT_FALSE, callback);
|
||||
}
|
||||
|
||||
u32 DetectNotEmulator(void *callback) {
|
||||
u32 func_queue[32];
|
||||
|
||||
func_queue[2] = FUNC_QUEUE_END;
|
||||
func_queue[0] = ADDR_PLUS_ADDEND(RunEncrypted_MACOwner_IsGood, ENC_VAL_1) + DSP_OBFS_OFFSET;
|
||||
func_queue[1] = ADDR_PLUS_ADDEND(RunEncrypted_Integrity_MACOwner_IsGood, ENC_VAL_1) + DSP_OBFS_OFFSET;
|
||||
|
||||
return dsprotMain(&func_queue[0], EXPECT_TRUE, callback);
|
||||
}
|
||||
|
||||
u32 DetectDummy(void *callback) {
|
||||
u32 func_queue[32];
|
||||
|
||||
// Prevent optimization of the function queue processing
|
||||
*(u32 *) &func_queue[0] = FUNC_QUEUE_END;
|
||||
|
||||
return dsprotMain(&func_queue[0], EXPECT_FALSE, callback);
|
||||
}
|
||||
|
||||
u32 DetectNotDummy(void *callback) {
|
||||
u32 func_queue[32];
|
||||
|
||||
// Prevent optimization of the function queue processing
|
||||
*(u32 *) &func_queue[0] = FUNC_QUEUE_END;
|
||||
|
||||
return dsprotMain(&func_queue[0], EXPECT_TRUE, callback);
|
||||
}
|
||||
|
||||
// DECRYPTION_WRAPPER(DetectFlashcart, 0xac, 0x2e8b, DSProt_)
|
||||
// DECRYPTION_WRAPPER(DetectNotFlashcart, 0xac, 0x2e8b, DSProt_)
|
||||
// DECRYPTION_WRAPPER(DetectEmulator, 0xac, 0x2e8b, DSProt_)
|
||||
// DECRYPTION_WRAPPER(DetectNotEmulator, 0xac, 0x2e8b, DSProt_)
|
||||
// DECRYPTION_WRAPPER(DetectDummy, 0x94, 0x2e8b, DSProt_)
|
||||
// DECRYPTION_WRAPPER(DetectNotDummy, 0x94, 0x2e8b, DSProt_)
|
||||
|
||||
// // clang-format off
|
||||
// asm void NitroStaticInit() {
|
||||
// stmdb sp!, {lr};
|
||||
// add r0, pc, #0x4;
|
||||
// bl Encryptor_DecodeFunctionTable;
|
||||
// ldmia sp!, {pc};
|
||||
// dcd DSProt_DetectFlashcart + ENC_VAL_1
|
||||
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
|
||||
// dcd DSProt_DetectNotFlashcart + ENC_VAL_1
|
||||
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
|
||||
// dcd DSProt_DetectEmulator + ENC_VAL_1
|
||||
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
|
||||
// dcd DSProt_DetectNotEmulator + ENC_VAL_1
|
||||
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
|
||||
// dcd DSProt_DetectDummy + ENC_VAL_1
|
||||
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
|
||||
// dcd DSProt_DetectNotDummy + ENC_VAL_1
|
||||
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
|
||||
// dcd 0
|
||||
// dcd 0
|
||||
// dcd DSProt_Garbage + 0x1400
|
||||
// }
|
||||
// // clang-format on
|
||||
@@ -0,0 +1,137 @@
|
||||
#include "dsprot/encryptor.h"
|
||||
|
||||
#include <nitro/os/cache.h>
|
||||
|
||||
#include "dsprot/bss.h"
|
||||
#include "dsprot/encoding_constants.h"
|
||||
#include "dsprot/rc4.h"
|
||||
|
||||
#define ROTL(x, a) ((a) == 0 ? (x) : (((x) << (a)) | ((x) >> (32 - (a)))))
|
||||
|
||||
static void clearDataAndInstructionCache(void *start_addr, u32 num_bytes);
|
||||
|
||||
static void clearDataAndInstructionCache(void *start_addr, u32 num_bytes) {
|
||||
DC_FlushRange(start_addr, num_bytes);
|
||||
IC_InvalidateRange(start_addr, num_bytes);
|
||||
}
|
||||
|
||||
u32 Encryptor_CategorizeInstruction(u32 instruction) {
|
||||
u8 opcode;
|
||||
|
||||
opcode = instruction >> INS_OPCODE_SHIFT;
|
||||
|
||||
// Branch instruction
|
||||
if ((opcode & 0x0E) == 0x0A) {
|
||||
// BLX immediate type
|
||||
if ((opcode & 0xF0) == 0xF0) {
|
||||
return INS_TYPE_BLXIMM;
|
||||
}
|
||||
|
||||
// Link bit
|
||||
if (opcode & INS_OPCODE_LINKBIT) {
|
||||
return INS_TYPE_BL;
|
||||
} else {
|
||||
return INS_TYPE_B;
|
||||
}
|
||||
}
|
||||
|
||||
return INS_TYPE_OTHER;
|
||||
}
|
||||
|
||||
void Encryptor_DecodeFunctionTable(FuncInfo *functions) {
|
||||
u32 *addr;
|
||||
u32 size;
|
||||
u32 *end_addr;
|
||||
|
||||
if (functions == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (; functions->obfs_addr != 0; functions++) {
|
||||
addr = (u32 *) functions->obfs_addr;
|
||||
size = functions->obfs_size - (u32) &DSProt_BSS - ENC_VAL_1;
|
||||
|
||||
if (addr == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
addr = (u32 *) ((u32) addr - ENC_VAL_1);
|
||||
end_addr = addr + (size / 4);
|
||||
|
||||
for (; addr < end_addr; addr++) {
|
||||
switch (Encryptor_CategorizeInstruction(*addr)) {
|
||||
case INS_TYPE_BLXIMM:
|
||||
case INS_TYPE_BL: {
|
||||
u32 opcode = (*addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
|
||||
u32 operands = ((*addr & INS_OPERANDS_MASK) - ENC_VAL_1) & INS_OPERANDS_MASK;
|
||||
|
||||
*addr = opcode | operands;
|
||||
} break;
|
||||
|
||||
case INS_TYPE_B: {
|
||||
u32 opcode = (*addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
|
||||
u32 operands = ((*addr & INS_OPERANDS_MASK) - ENC_VAL_2) & INS_OPERANDS_MASK;
|
||||
|
||||
*addr = opcode | operands;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
u8 *addr_bytes = (u8 *) addr;
|
||||
*addr = (addr_bytes[0] ^ ENC_BYTE_A) | ((addr_bytes[1] ^ ENC_BYTE_B) << 8) |
|
||||
((addr_bytes[2] ^ ENC_BYTE_C) << 16) | ((addr_bytes[3] ^ ENC_BYTE_D) << 24);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
clearDataAndInstructionCache((void *) (functions->obfs_addr - ENC_VAL_1), size);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void expandRC4Key(u32 seed_key, u32 size, u32 *expanded_key) {
|
||||
expanded_key[0] = ROTL(seed_key, 0) ^ size;
|
||||
expanded_key[1] = ROTL(seed_key, 8) ^ size;
|
||||
expanded_key[2] = ROTL(seed_key, 16) ^ size;
|
||||
expanded_key[3] = ROTL(seed_key, 24) ^ size;
|
||||
}
|
||||
|
||||
void *Encryptor_DecryptFunction(u32 key, u32 func_addr, u32 size) {
|
||||
u32 expanded_key[4];
|
||||
void *func_ptr;
|
||||
|
||||
// Deobfuscate arguments
|
||||
func_ptr = (void *) func_addr;
|
||||
func_ptr -= ENC_VAL_1;
|
||||
|
||||
size -= (u32) &DSProt_BSS + ENC_VAL_1;
|
||||
key -= (u32) &DSProt_BSS + ENC_VAL_1;
|
||||
|
||||
expandRC4Key(key, size, &expanded_key[0]);
|
||||
RC4_InitAndDecryptInstructions(&expanded_key[0], func_ptr, func_ptr, size);
|
||||
clearDataAndInstructionCache(func_ptr, size);
|
||||
|
||||
return func_ptr;
|
||||
}
|
||||
|
||||
u32 Encryptor_EncryptFunction(u32 key, u32 func_addr, u32 size) {
|
||||
u32 expanded_key[4];
|
||||
void *func_ptr;
|
||||
|
||||
// Deobfuscate arguments
|
||||
func_ptr = (void *) func_addr;
|
||||
func_ptr -= ENC_VAL_1;
|
||||
|
||||
size -= (u32) &DSProt_BSS + ENC_VAL_1;
|
||||
key -= (u32) &DSProt_BSS + ENC_VAL_1;
|
||||
|
||||
// Change key
|
||||
key += func_addr >> 20;
|
||||
|
||||
expandRC4Key(key, size, &expanded_key[0]);
|
||||
RC4_InitAndEncryptInstructions(&expanded_key[0], func_ptr, func_ptr, size);
|
||||
clearDataAndInstructionCache(func_ptr, size);
|
||||
|
||||
// Re-obfuscate key
|
||||
key += (u32) &DSProt_BSS + ENC_VAL_1;
|
||||
|
||||
return key;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "dsprot/bss.h"
|
||||
#include "dsprot/garbage.h"
|
||||
|
||||
u8 DSProt_BSS[4];
|
||||
|
||||
const u32 DSProt_Garbage[6] = {
|
||||
0xebaa0114, 0x40064eb7, 0x5f013696, 0xe5211f83, 0xe7ef335b, 0xe84b197c,
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
#include "dsprot/integrity.h"
|
||||
|
||||
#include "dsprot/encoding_constants.h"
|
||||
#include "dsprot/mac_owner.h"
|
||||
#include "dsprot/rom_test.h"
|
||||
|
||||
// Functions to be encrypted (cannot be called directly)
|
||||
u32 Integrity_MACOwner_IsBad(void);
|
||||
u32 Integrity_MACOwner_IsGood(void);
|
||||
u32 Integrity_ROMTest_IsBad(void);
|
||||
u32 Integrity_ROMTest_IsGood(void);
|
||||
|
||||
#define INTEGRITY_OBFS_OFFSET (ENC_VAL_1 & ~0xFFF)
|
||||
|
||||
// This was likely not originally an inline, but an inline is able to match here nicely
|
||||
static inline u32 checkDecryptionWrapper(u8 *addr, u32 match_ret, u32 mismatch_ret) {
|
||||
u32 offset;
|
||||
|
||||
addr += INTEGRITY_OBFS_OFFSET;
|
||||
offset = ENC_VAL_1 - INTEGRITY_OBFS_OFFSET;
|
||||
|
||||
// The bytes checked here are from the `run_encrypted_func` macro defined in asm_macro.inc:
|
||||
// e92d00f0 stmfd sp!, {r4-r7}
|
||||
// e92d000f stmfd sp!, {r0-r3}
|
||||
// e8bd00f0 ldmfd sp!, {r4-r7}
|
||||
// e59f103c ldr r1, [pc, #60]
|
||||
if (addr[offset++] != 0xF0) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0x00) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0x2D) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0xE9) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
|
||||
if (addr[offset++] != 0x0F) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0x00) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0x2D) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0xE9) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
|
||||
if (addr[offset++] != 0xF0) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0x00) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0xBD) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0xE8) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
|
||||
if (addr[offset++] != 0x60) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0x10) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0x9F) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
if (addr[offset++] != 0xE5) {
|
||||
return mismatch_ret;
|
||||
}
|
||||
|
||||
return match_ret;
|
||||
}
|
||||
|
||||
u32 Integrity_MACOwner_IsBad(void) {
|
||||
u8 *addr;
|
||||
u32 ret;
|
||||
|
||||
addr = (u8 *) ADDR_PLUS_ADDEND(RunEncrypted_MACOwner_IsBad, ENC_VAL_1) - (ENC_VAL_1 * 2);
|
||||
ret = (u32) addr + 1;
|
||||
|
||||
return checkDecryptionWrapper(addr, 0, ret);
|
||||
}
|
||||
|
||||
u32 Integrity_MACOwner_IsGood(void) {
|
||||
u8 *addr;
|
||||
u32 ret;
|
||||
|
||||
addr = (u8 *) ADDR_PLUS_ADDEND(RunEncrypted_MACOwner_IsGood, ENC_VAL_1) - (ENC_VAL_1 * 2);
|
||||
ret = (u32) addr;
|
||||
|
||||
return checkDecryptionWrapper(addr, ret + 1, 0);
|
||||
}
|
||||
|
||||
u32 Integrity_ROMTest_IsBad(void) {
|
||||
u8 *addr;
|
||||
u32 ret;
|
||||
|
||||
addr = (u8 *) ADDR_PLUS_ADDEND(RunEncrypted_ROMTest_IsBad, ENC_VAL_1) - (ENC_VAL_1 * 2);
|
||||
ret = (u32) addr + 1;
|
||||
|
||||
return checkDecryptionWrapper(addr, 0, ret);
|
||||
}
|
||||
|
||||
u32 Integrity_ROMTest_IsGood(void) {
|
||||
u8 *addr;
|
||||
u32 ret;
|
||||
|
||||
addr = (u8 *) ADDR_PLUS_ADDEND(RunEncrypted_ROMTest_IsGood, ENC_VAL_1) - (ENC_VAL_1 * 2);
|
||||
ret = (u32) addr;
|
||||
|
||||
return checkDecryptionWrapper(addr, ret + 1, 0);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "dsprot/mac_owner.h"
|
||||
|
||||
#include <nitro/os.h>
|
||||
|
||||
// Functions to be encrypted (cannot be called directly)
|
||||
u32 MACOwner_IsBad(void);
|
||||
u32 MACOwner_IsGood(void);
|
||||
|
||||
#define MAC_ADDR_SIZE (6)
|
||||
|
||||
#define ENC_MAC_ADDR_BYTE (0xFF)
|
||||
|
||||
static const u8 bad_mac_addr[MAC_ADDR_SIZE] = {0x00 ^ ENC_MAC_ADDR_BYTE, 0x09 ^ ENC_MAC_ADDR_BYTE, 0xBF ^ ENC_MAC_ADDR_BYTE,
|
||||
0x00 ^ ENC_MAC_ADDR_BYTE, 0x00 ^ ENC_MAC_ADDR_BYTE, 0x31 ^ ENC_MAC_ADDR_BYTE};
|
||||
|
||||
static inline u32 testMACOwner(u32 pass_ret, u32 fail_ret) {
|
||||
u8 mac_addr[MAC_ADDR_SIZE];
|
||||
OSOwnerInfo owner_info;
|
||||
int i;
|
||||
u32 ret;
|
||||
|
||||
OS_func_0176(&mac_addr[0]);
|
||||
for (i = 0; i < MAC_ADDR_SIZE; i++) {
|
||||
if (bad_mac_addr[i] != (mac_addr[i] ^ ENC_MAC_ADDR_BYTE)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OS_func_0177(&owner_info);
|
||||
if (i == MAC_ADDR_SIZE && owner_info.birthday.month == 1 && owner_info.birthday.day == 1 &&
|
||||
owner_info.nickNameLength == 0) {
|
||||
ret = fail_ret;
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAC_ADDR_SIZE; i++) {
|
||||
if (mac_addr[i] != 0x00) {
|
||||
ret = pass_ret;
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
ret = fail_ret;
|
||||
|
||||
EXIT:
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 MACOwner_IsBad(void) {
|
||||
return testMACOwner(0, 1);
|
||||
}
|
||||
|
||||
u32 MACOwner_IsGood(void) {
|
||||
return testMACOwner(1, 0);
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
#include "dsprot/rc4.h"
|
||||
#include "dsprot/encoding_constants.h"
|
||||
#include "dsprot/encryptor.h"
|
||||
|
||||
#define RC4_KEY_SIZE (16)
|
||||
|
||||
typedef struct {
|
||||
int i;
|
||||
int j;
|
||||
u8 S[256];
|
||||
} RC4_Ctx;
|
||||
|
||||
// Functions to be encoded (cannot be static)
|
||||
void RC4_Init(RC4_Ctx *ctx, const void *key, u32 key_len);
|
||||
u8 RC4_Byte(RC4_Ctx *ctx);
|
||||
u32 RC4_InitSBox(u8 *sbox);
|
||||
u32 RC4_EncryptInstructions(RC4_Ctx *ctx, void *src, void *dst, u32 size);
|
||||
u32 RC4_DecryptInstructions(RC4_Ctx *ctx, void *src, void *dst, u32 size);
|
||||
|
||||
void RC4_Init(RC4_Ctx *ctx, const void *key, u32 key_len) {
|
||||
u8 tmp1;
|
||||
u8 tmp2;
|
||||
int Ki;
|
||||
u8 Si;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
Ki = 0;
|
||||
Si = 0;
|
||||
ctx->i = 0;
|
||||
ctx->j = 0;
|
||||
|
||||
for (j = 0; j < 256; j++) {
|
||||
ctx->S[j] = j;
|
||||
}
|
||||
|
||||
// Modification to RC4: i = 255 -> 0, instead of 0 -> 255
|
||||
for (i = 255; i >= 0; i--) {
|
||||
tmp1 = ctx->S[i];
|
||||
Si = Si + ((u8 *) key)[Ki] + tmp1;
|
||||
tmp2 = ctx->S[Si];
|
||||
|
||||
ctx->S[Si] = tmp1;
|
||||
ctx->S[i] = tmp2;
|
||||
|
||||
Ki++;
|
||||
if (Ki >= key_len) {
|
||||
Ki = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 RC4_Byte(RC4_Ctx *ctx) {
|
||||
u8 i;
|
||||
u8 ival;
|
||||
u8 j;
|
||||
u8 jval;
|
||||
|
||||
i = ctx->i + 1;
|
||||
ival = ctx->S[i];
|
||||
j = ival + ctx->j;
|
||||
jval = ctx->S[j];
|
||||
|
||||
ctx->i = i;
|
||||
ctx->j = j;
|
||||
|
||||
ctx->S[j] = ival;
|
||||
ctx->S[i] = jval;
|
||||
|
||||
return ctx->S[(ival + jval) & 0xFF];
|
||||
}
|
||||
|
||||
u32 RC4_InitSBox(u8 *sbox) {
|
||||
int i;
|
||||
for (i = 0; i < 256; i++) {
|
||||
sbox[i] = (u8) i ^ ENC_SBOX_XOR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 RC4_EncryptInstructions(RC4_Ctx *ctx, void *src, void *dst, u32 size) {
|
||||
u8 sbox[256];
|
||||
ulong offset;
|
||||
u8 *src_bytes;
|
||||
u8 *dst_bytes;
|
||||
|
||||
if (size & 3) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
src_bytes = (u8 *) src;
|
||||
dst_bytes = (u8 *) dst;
|
||||
|
||||
RC4_InitSBox(&sbox[0]);
|
||||
|
||||
for (offset = 0; offset < size; offset += 4) {
|
||||
switch (Encryptor_CategorizeInstruction(*(u32 *) (src_bytes + offset))) {
|
||||
case INS_TYPE_BLXIMM:
|
||||
case INS_TYPE_BL: {
|
||||
u32 opcode;
|
||||
u32 operands;
|
||||
u32 *src_addr = (u32 *) (src_bytes + offset);
|
||||
u32 *dst_addr = (u32 *) (dst_bytes + offset);
|
||||
|
||||
*dst_addr = *src_addr;
|
||||
|
||||
opcode = (*dst_addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
|
||||
operands = ((*dst_addr & INS_OPERANDS_MASK) + ENC_VAL_2) & INS_OPERANDS_MASK;
|
||||
|
||||
*dst_addr = opcode | operands;
|
||||
} break;
|
||||
|
||||
case INS_TYPE_B: {
|
||||
u32 opcode;
|
||||
u32 operands;
|
||||
u32 *src_addr = (u32 *) (src_bytes + offset);
|
||||
u32 *dst_addr = (u32 *) (dst_bytes + offset);
|
||||
|
||||
*dst_addr = *src_addr;
|
||||
|
||||
opcode = (*dst_addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
|
||||
operands = ((*dst_addr & INS_OPERANDS_MASK) + ENC_VAL_1) & INS_OPERANDS_MASK;
|
||||
|
||||
*dst_addr = opcode | operands;
|
||||
} break;
|
||||
|
||||
default:
|
||||
dst_bytes[offset] = src_bytes[offset] ^ RC4_Byte(ctx);
|
||||
dst_bytes[offset + 1] = src_bytes[offset + 1] ^ RC4_Byte(ctx);
|
||||
dst_bytes[offset + 2] = sbox[src_bytes[offset + 2]];
|
||||
dst_bytes[offset + 3] = src_bytes[offset + 3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 RC4_DecryptInstructions(RC4_Ctx *ctx, void *src, void *dst, u32 size) {
|
||||
u8 sbox[256];
|
||||
ulong offset;
|
||||
u8 *src_bytes;
|
||||
u8 *dst_bytes;
|
||||
|
||||
if (size & 3) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
src_bytes = (u8 *) src;
|
||||
dst_bytes = (u8 *) dst;
|
||||
|
||||
RC4_InitSBox(&sbox[0]);
|
||||
|
||||
for (offset = 0; offset < size; offset += 4) {
|
||||
switch (Encryptor_CategorizeInstruction(*(u32 *) (src_bytes + offset))) {
|
||||
case INS_TYPE_BLXIMM:
|
||||
case INS_TYPE_BL: {
|
||||
u32 opcode;
|
||||
u32 operands;
|
||||
u32 *src_addr = (u32 *) (src_bytes + offset);
|
||||
u32 *dst_addr = (u32 *) (dst_bytes + offset);
|
||||
|
||||
*dst_addr = *src_addr;
|
||||
|
||||
opcode = (*dst_addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
|
||||
operands = ((*dst_addr & INS_OPERANDS_MASK) - ENC_VAL_1) & INS_OPERANDS_MASK;
|
||||
|
||||
*dst_addr = opcode | operands;
|
||||
} break;
|
||||
|
||||
case INS_TYPE_B: {
|
||||
u32 opcode;
|
||||
u32 operands;
|
||||
u32 *src_addr = (u32 *) (src_bytes + offset);
|
||||
u32 *dst_addr = (u32 *) (dst_bytes + offset);
|
||||
|
||||
*dst_addr = *src_addr;
|
||||
|
||||
opcode = (*dst_addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
|
||||
operands = ((*dst_addr & INS_OPERANDS_MASK) - ENC_VAL_2) & INS_OPERANDS_MASK;
|
||||
|
||||
*dst_addr = opcode | operands;
|
||||
} break;
|
||||
|
||||
default:
|
||||
dst_bytes[offset] = src_bytes[offset] ^ RC4_Byte(ctx);
|
||||
dst_bytes[offset + 1] = src_bytes[offset + 1] ^ RC4_Byte(ctx);
|
||||
dst_bytes[offset + 2] = sbox[src_bytes[offset + 2]];
|
||||
dst_bytes[offset + 3] = src_bytes[offset + 3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 RC4_InitAndEncryptInstructions(void *key, void *dst, void *src, u32 size) {
|
||||
RC4_Ctx ctx;
|
||||
RC4_Init(&ctx, key, RC4_KEY_SIZE);
|
||||
// Must coerce output to -1 or 0 like this to match
|
||||
return RC4_EncryptInstructions(&ctx, dst, src, size) == -1 ? -1 : 0;
|
||||
}
|
||||
|
||||
u32 RC4_InitAndDecryptInstructions(void *key, void *dst, void *src, u32 size) {
|
||||
RC4_Ctx ctx;
|
||||
RC4_Init(&ctx, key, RC4_KEY_SIZE);
|
||||
// Must coerce output to -1 or 0 like this to match
|
||||
return RC4_DecryptInstructions(&ctx, dst, src, size) == -1 ? -1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "dsprot/rom_test.h"
|
||||
#include "dsprot/rom_util.h"
|
||||
|
||||
#include <nitro/card.h>
|
||||
|
||||
// Functions to be encrypted (cannot be called directly)
|
||||
u32 ROMTest_IsBad(void);
|
||||
u32 ROMTest_IsGood(void);
|
||||
|
||||
#define ROM_BLOCK_SIZE CARD_ROM_PAGE_SIZE
|
||||
|
||||
static inline u32 testROM(u32 pass_ret, u32 fail_ret) {
|
||||
// Extra CRC entry is required to match
|
||||
u32 crcs[7];
|
||||
u8 rom_buf[ROM_BLOCK_SIZE];
|
||||
int i;
|
||||
u32 rom_addr;
|
||||
u32 ret;
|
||||
|
||||
rom_addr = 0x1000;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
RunEncrypted_ROMUtil_Read(&rom_buf[0], rom_addr, ROM_BLOCK_SIZE);
|
||||
crcs[i] = RunEncrypted_ROMUtil_CRC32(&rom_buf[0], ROM_BLOCK_SIZE);
|
||||
|
||||
if (i == 2) {
|
||||
// Has to be like this to match
|
||||
rom_addr = 1;
|
||||
rom_addr <<= 15;
|
||||
} else {
|
||||
rom_addr += ROM_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Checking the ROM reading results were as expected:
|
||||
// 0 == 1 == 2 == 3
|
||||
// 3 != 4 and 3 != 5
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (crcs[i] != crcs[3]) {
|
||||
ret = fail_ret;
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
if (crcs[3] == crcs[4] && crcs[3] == crcs[5]) {
|
||||
ret = fail_ret;
|
||||
} else {
|
||||
ret = pass_ret;
|
||||
}
|
||||
|
||||
EXIT:
|
||||
// Erasing read buffer
|
||||
for (i = 0; i < ROM_BLOCK_SIZE; i++) {
|
||||
rom_buf[i] = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 ROMTest_IsBad(void) {
|
||||
return testROM(0, 1);
|
||||
}
|
||||
|
||||
u32 ROMTest_IsGood(void) {
|
||||
return testROM(1, 0);
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
#include "dsprot/rom_util.h"
|
||||
|
||||
#include <nitro/card.h>
|
||||
#include <nitro/os.h>
|
||||
#include <nitro/reg.h>
|
||||
|
||||
// Functions to be encrypted (cannot be called directly)
|
||||
void ROMUtil_Read(void *dest, u32 addr, s32 num_bytes);
|
||||
u32 ROMUtil_CRC32(void *buf, u32 size);
|
||||
|
||||
void ROMUtil_Read(void *dest, u32 addr, s32 num_bytes) {
|
||||
u8 buffer[8];
|
||||
vu8 *vnull;
|
||||
u32 register_base_1;
|
||||
vu8 *register_base_2;
|
||||
u32 card_ctrl_13;
|
||||
s32 addr_offset;
|
||||
u16 lock_id;
|
||||
u16 ext_mem_register_val_original;
|
||||
u32 output;
|
||||
s32 card_ctrl_cmd;
|
||||
int i;
|
||||
|
||||
lock_id = OS_GetLockID();
|
||||
CARD_func_0010(lock_id);
|
||||
|
||||
// Alias for volatile null pointer
|
||||
vnull = NULL;
|
||||
|
||||
// Alias for register base (0x04000000)
|
||||
register_base_1 = 1;
|
||||
register_base_1 <<= 26;
|
||||
|
||||
// Another alias for register base (0x04000000)
|
||||
register_base_2 = (vu8 *) REG_BASE;
|
||||
|
||||
// External memory control register (0x04000204)
|
||||
// Save value to rewrite later
|
||||
ext_mem_register_val_original = REG_EXMEM_CNT;
|
||||
|
||||
// Set current processor accessing the gamecard bus to the ARM9
|
||||
OS_SetNdsSlotAccess(OS_CPU_ARM9);
|
||||
|
||||
// Obfuscated, create address 0x027FFE60
|
||||
// This is offset 0x60 in the ROM header: port 0x040001A4 / setting for normal commands
|
||||
card_ctrl_13 = 5;
|
||||
card_ctrl_13 <<= 18;
|
||||
card_ctrl_13 -= 13;
|
||||
|
||||
// Detect if the system is in DSi mode. If so, change the address to 0x02FFFE60
|
||||
if (*(vu8 *) (register_base_1 + REG_A9ROM_OFFSET) & 1) {
|
||||
card_ctrl_13 |= 0x40000;
|
||||
}
|
||||
|
||||
card_ctrl_13 <<= 5;
|
||||
|
||||
// Read port setting and set page read flags
|
||||
card_ctrl_cmd = (*(vs32 *) card_ctrl_13 & ~CARD_CTL_CMD_MASK) | (CARD_CTL_CMD_PAGE | CARD_CTL_READ | CARD_CTL_START);
|
||||
|
||||
// Calculate offset to round back to nearest 0x200-byte block.
|
||||
// E.G. if we want to read starting from 0x1208, we actually need to
|
||||
// request the block at 0x1200 and then ignore the first 8 bytes of the result.
|
||||
// This would set `addr_offset` to -8.
|
||||
addr_offset = 0 - (addr & (CARD_ROM_PAGE_SIZE - 1));
|
||||
|
||||
// Wait for card to not be busy
|
||||
while (*(vu32 *) (register_base_1 + REG_CARD_CNT_OFFSET) & CARD_CTL_START) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Write enable flag to card ROM and SPI control register
|
||||
*(vu8 *) (register_base_1 + REG_CARD_AUX_SPI_CNT_OFFSET + 1) = CARDMST_ENABLE;
|
||||
|
||||
// Read 8-byte command out from gamecard bus, write this back later
|
||||
for (i = 0; i < 8; i++) {
|
||||
buffer[i] = *(vnull + _MAIN_REG_BASE + REG_CARD_CMD_OFFSET + i);
|
||||
}
|
||||
|
||||
addr += addr_offset;
|
||||
|
||||
while (addr_offset < num_bytes) {
|
||||
// Read a 0x200-byte data block from ROM
|
||||
|
||||
// Write 8-byte command to registers
|
||||
// B7XXXXXXXX000000 -> 0x200-byte encrypted data read from address XXXXXXXX
|
||||
register_base_2[REG_CARD_CMD_OFFSET + 0] = CARD_CMD_ENCRYPTED_READ;
|
||||
register_base_2[REG_CARD_CMD_OFFSET + 1] = addr >> 24;
|
||||
register_base_2[REG_CARD_CMD_OFFSET + 2] = addr >> 16;
|
||||
register_base_2[REG_CARD_CMD_OFFSET + 3] = addr >> 8;
|
||||
register_base_2[REG_CARD_CMD_OFFSET + 4] = addr;
|
||||
register_base_2[REG_CARD_CMD_OFFSET + 5] = 0x00;
|
||||
register_base_2[REG_CARD_CMD_OFFSET + 6] = 0x00;
|
||||
register_base_2[REG_CARD_CMD_OFFSET + 7] = 0x00;
|
||||
|
||||
// Submit command
|
||||
*(vu32 *) (register_base_1 + REG_CARD_CNT_OFFSET) = card_ctrl_cmd;
|
||||
|
||||
// Copy the output into the destination buffer, within the bounds of num_bytes
|
||||
// (Must read the output out of the I/O register regardless)
|
||||
do {
|
||||
if (*(vu32 *) (register_base_1 + REG_CARD_CNT_OFFSET) & CARD_DATA_READY) {
|
||||
output = *(vu32 *) (register_base_1 + REG_CARD_DATA_OFFSET);
|
||||
if (addr_offset >= 0 && addr_offset < num_bytes) {
|
||||
*(u32 *) ((u32) dest + addr_offset) = output;
|
||||
}
|
||||
|
||||
addr_offset += 4;
|
||||
}
|
||||
} while (*(vu32 *) (register_base_1 + REG_CARD_CNT_OFFSET) & CARD_CTL_START);
|
||||
|
||||
// Advance address to next block
|
||||
addr += CARD_ROM_PAGE_SIZE;
|
||||
}
|
||||
|
||||
// Done reading, restore everything how it was before
|
||||
|
||||
// Write original command back to gamecard bus
|
||||
for (i = 0; i < 8; i++) {
|
||||
*(vnull + _MAIN_REG_BASE + REG_CARD_CMD_OFFSET + i) = buffer[i];
|
||||
}
|
||||
|
||||
// Write original value back to to external memory control register
|
||||
*(vu16 *) (register_base_1 + REG_EXMEM_CNT_OFFSET) = ext_mem_register_val_original;
|
||||
|
||||
CARD_func_0011(lock_id);
|
||||
OS_func_0178(lock_id);
|
||||
}
|
||||
|
||||
u32 ROMUtil_CRC32(void *buf, u32 size) {
|
||||
int i;
|
||||
u32 crc;
|
||||
u32 poly;
|
||||
u8 *byte_ptr;
|
||||
|
||||
byte_ptr = (u8 *) buf;
|
||||
crc = 0xFFFFFFFF;
|
||||
poly = 0xEDB88320;
|
||||
while (size-- != 0) {
|
||||
crc ^= *byte_ptr++;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (crc & 1) {
|
||||
crc = (crc >> 1);
|
||||
} else {
|
||||
crc = poly ^ (crc >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
import sys
|
||||
from argparse import ArgumentParser, Namespace
|
||||
from bisect import bisect_left, bisect_right
|
||||
from collections.abc import Generator
|
||||
from io import TextIOWrapper
|
||||
from pathlib import Path
|
||||
from typing import Never
|
||||
|
||||
import elftools.construct
|
||||
import elftools.elf.sections
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from elftools.elf.sections import SymbolTableSection
|
||||
|
||||
ASM_COMMON_INCLUDE = "dsprot/asm_macro.inc"
|
||||
STATIC_INIT_FN_NAME = "NitroStaticInit"
|
||||
|
||||
|
||||
def address(symbol: elftools.elf.sections.Symbol) -> int:
|
||||
value = symbol.entry.get("st_value")
|
||||
if type(value) is not int:
|
||||
panic(
|
||||
f"Expected st_value of symbol {symbol.name} to be an int but got {type(value)} = {value}"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
class Symbol:
|
||||
def __init__(self, symbol: elftools.elf.sections.Symbol):
|
||||
self.symbol = symbol
|
||||
self.address = address(symbol)
|
||||
self.section: Section | None = None
|
||||
|
||||
def name(self) -> str:
|
||||
return self.symbol.name
|
||||
|
||||
def size(self) -> int:
|
||||
size = self.symbol.entry.get("st_size")
|
||||
if type(size) is not int:
|
||||
panic(
|
||||
f"Expected st_size of symbol {self.symbol.name} to be an int but got {type(size)} = {size}"
|
||||
)
|
||||
return size
|
||||
|
||||
def _st_info(self) -> elftools.construct.Container:
|
||||
st_info = self.symbol.entry.get("st_info")
|
||||
if type(st_info) is not elftools.construct.Container:
|
||||
panic(
|
||||
f"Expected st_info of symbol {self.symbol.name} to be a Container but got {type(st_info)} = {st_info}"
|
||||
)
|
||||
return st_info
|
||||
|
||||
def section_index(self) -> int | None:
|
||||
shndx = self.symbol.entry.get("st_shndx")
|
||||
if shndx in ["SHN_UNDEF", "SHN_ABS"]:
|
||||
return None
|
||||
if type(shndx) is not int:
|
||||
panic(
|
||||
f"Expected st_shndx of symbol {self.symbol.name} to be an int but got {type(shndx)} = {shndx}"
|
||||
)
|
||||
return shndx
|
||||
|
||||
def type(self) -> str:
|
||||
st_info = self._st_info()
|
||||
st_type = st_info.get("type")
|
||||
if type(st_type) is not str:
|
||||
panic(
|
||||
f"Expected st_info.type of symbol {self.symbol.name} to be a str but got {type(st_type)} = {st_type}"
|
||||
)
|
||||
return st_type
|
||||
|
||||
def code_size(self) -> int:
|
||||
if self.type() != "STT_FUNC":
|
||||
return 0
|
||||
|
||||
address = self.address
|
||||
size = self.size()
|
||||
end_address = address + size
|
||||
|
||||
# Look for constant pool inside this function
|
||||
if self.section is not None:
|
||||
for next_symbol in self.section.symbols_by_address(address + 1):
|
||||
if next_symbol.address >= end_address:
|
||||
break
|
||||
if next_symbol.name() == "$d":
|
||||
return next_symbol.address - self.address
|
||||
|
||||
# If no constant pool exists, this function's size is st_size
|
||||
return size
|
||||
|
||||
|
||||
class Section:
|
||||
def __init__(self, elf: "Elf", section: elftools.elf.sections.Section):
|
||||
self.elf = elf
|
||||
self.section = section
|
||||
self.symbols: list[Symbol] = []
|
||||
self.symbol_indices_by_address: list[int] = []
|
||||
|
||||
def name(self) -> str:
|
||||
return self.section.name
|
||||
|
||||
def add_symbol(self, symbol: Symbol):
|
||||
index = len(self.symbols)
|
||||
self.symbols.append(symbol)
|
||||
pos = bisect_right(
|
||||
self.symbol_indices_by_address,
|
||||
symbol.address,
|
||||
key=lambda s: self.symbols[s].address,
|
||||
)
|
||||
self.symbol_indices_by_address.insert(pos, index)
|
||||
|
||||
def symbols_by_address(self, start_address=0) -> Generator[Symbol]:
|
||||
start_index = bisect_left(
|
||||
self.symbol_indices_by_address,
|
||||
start_address,
|
||||
key=lambda i: self.symbols[i].address,
|
||||
)
|
||||
for i in range(start_index, len(self.symbols)):
|
||||
yield self.symbols[self.symbol_indices_by_address[i]]
|
||||
|
||||
|
||||
class Elf:
|
||||
def __init__(self, elf: ELFFile):
|
||||
self.elf = elf
|
||||
self.sections: list[Section] = [
|
||||
Section(self, section) for section in elf.iter_sections()
|
||||
]
|
||||
for section in elf.iter_sections():
|
||||
if not isinstance(section, SymbolTableSection):
|
||||
continue
|
||||
for symbol in section.iter_symbols():
|
||||
symbol = Symbol(symbol)
|
||||
section_index = symbol.section_index()
|
||||
if section_index is not None:
|
||||
symbol.section = self.sections[section_index]
|
||||
symbol.section.add_symbol(symbol)
|
||||
|
||||
def symbol_by_name(self, name: str) -> Generator[Symbol]:
|
||||
for section in self.sections:
|
||||
for symbol in section.symbols:
|
||||
if symbol.name() == name:
|
||||
yield symbol
|
||||
|
||||
|
||||
class Args:
|
||||
def __init__(self, args: Namespace):
|
||||
self.output = Path(args.output)
|
||||
self.garbage: str | None = args.garbage
|
||||
self.prefix: str = args.prefix
|
||||
|
||||
self.inputs = list(map(Path, args.input))
|
||||
for input in self.inputs:
|
||||
if not input.exists():
|
||||
raise ValueError(f"Input file '{input}' does not exist")
|
||||
|
||||
key: str | None = args.key
|
||||
if key is None:
|
||||
self.key = None
|
||||
elif key.startswith("0x"):
|
||||
self.key = int(key, base=16)
|
||||
else:
|
||||
self.key = int(key, base=10)
|
||||
|
||||
if args.key is None:
|
||||
self.symbols = [f"{args.prefix}{symbol}" for symbol in args.symbols]
|
||||
else:
|
||||
self.symbols = list(map(str, args.symbols))
|
||||
|
||||
|
||||
def panic(message) -> Never:
|
||||
print(message)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def write_assembly(f: TextIOWrapper, args: Args, symbols: list[Symbol]) -> None:
|
||||
f.write("; Generated by libs/dsprot/tools/asmwriter.py\n")
|
||||
f.write("\n")
|
||||
|
||||
f.write(f'.include "{ASM_COMMON_INCLUDE}"\n')
|
||||
f.write("\n")
|
||||
|
||||
for symbol in symbols:
|
||||
f.write(f".public {symbol.name()}\n")
|
||||
if args.garbage is not None:
|
||||
f.write(f".public {args.garbage}\n")
|
||||
f.write("\n")
|
||||
|
||||
f.write(" .text\n")
|
||||
f.write(" .balign 4, 0\n")
|
||||
f.write("\n")
|
||||
|
||||
if args.key is not None:
|
||||
for symbol in symbols:
|
||||
f.write(f" arm_func_start {args.prefix}{symbol.name()}\n")
|
||||
f.write(f"{args.prefix}{symbol.name()}:\n")
|
||||
f.write(
|
||||
f" run_encrypted_func {symbol.name()}, {symbol.code_size():#x}, {args.key:#x}\n"
|
||||
)
|
||||
f.write(f" arm_func_end {args.prefix}{symbol.name()}\n")
|
||||
f.write("\n")
|
||||
else:
|
||||
f.write(f" local_arm_func_start {STATIC_INIT_FN_NAME}\n")
|
||||
f.write(f"{STATIC_INIT_FN_NAME}:\n")
|
||||
f.write(" decode_func_table encoded_func_table\n")
|
||||
f.write("encoded_func_table:\n")
|
||||
for symbol in symbols:
|
||||
symbol_name = symbol.name()
|
||||
if args.key is not None:
|
||||
symbol_name = args.prefix + symbol_name
|
||||
f.write(f" func_table_entry {symbol_name}, {symbol.code_size():#x}\n")
|
||||
f.write(" func_table_end\n")
|
||||
if args.garbage is not None:
|
||||
f.write(f" garbage_ref {args.garbage}\n")
|
||||
f.write(f" arm_func_end {STATIC_INIT_FN_NAME}\n")
|
||||
f.write("\n")
|
||||
|
||||
f.write(" .section .ctor, 4\n")
|
||||
f.write(f" sinit {STATIC_INIT_FN_NAME}\n")
|
||||
f.write("\n")
|
||||
|
||||
|
||||
def main():
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("-i", "--input", required=True, nargs="+")
|
||||
parser.add_argument("-o", "--output", required=True)
|
||||
parser.add_argument("-s", "--symbols", required=True, nargs="+")
|
||||
parser.add_argument("-g", "--garbage")
|
||||
parser.add_argument("-k", "--key")
|
||||
parser.add_argument("-p", "--prefix", default="RunEncrypted_")
|
||||
try:
|
||||
args = Args(parser.parse_args())
|
||||
except ValueError as e:
|
||||
panic(e)
|
||||
|
||||
symbols = {}
|
||||
missing_symbols = args.symbols.copy()
|
||||
for input in args.inputs:
|
||||
with input.open("rb") as f:
|
||||
elf = Elf(ELFFile(f))
|
||||
for symbol_name in missing_symbols.copy():
|
||||
matches = list(elf.symbol_by_name(symbol_name))
|
||||
if len(matches) == 0:
|
||||
continue
|
||||
symbols[symbol_name] = matches[0]
|
||||
missing_symbols.remove(symbol_name)
|
||||
if len(missing_symbols) > 0:
|
||||
panic(f"Symbol {missing_symbols[0]} does not exist")
|
||||
|
||||
symbols = [symbols[name] for name in args.symbols]
|
||||
with args.output.open("w") as f:
|
||||
write_assembly(f, args, symbols)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "nitro/card.h"
|
||||
#include "nitro/dc.h"
|
||||
#include "nitro/fs.h"
|
||||
#include "nitro/fx.h"
|
||||
#include "nitro/g2.h"
|
||||
|
||||
@@ -29,6 +29,19 @@ extern "C" {
|
||||
#define CARD_RESULT_NO_RESPONSE
|
||||
#define CARD_RESULT_ERROR
|
||||
|
||||
#define CARD_ROM_PAGE_SIZE 0x200
|
||||
|
||||
#define CARD_DATA_READY 0x800000
|
||||
|
||||
#define CARD_CTL_CMD_MASK 0x7000000
|
||||
#define CARD_CTL_CMD_PAGE 0x1000000
|
||||
#define CARD_CTL_READ 0x20000000
|
||||
#define CARD_CTL_START 0x80000000
|
||||
|
||||
#define CARD_CMD_ENCRYPTED_READ 0xb7
|
||||
|
||||
#define CARDMST_ENABLE 0x80
|
||||
|
||||
typedef u32 CARDBackupType;
|
||||
typedef u32 CARDResult;
|
||||
|
||||
@@ -45,6 +58,9 @@ CARDResult CARD_GetResultCode(void);
|
||||
BOOL CARD_func_0033();
|
||||
void CARD_func_0034();
|
||||
|
||||
void CARD_func_0010(u32);
|
||||
void CARD_func_0011(u32);
|
||||
|
||||
inline BOOL CARD_ReadEepromAsync(u32 offset, void *buf, u32 size, void *param4, void *param5) {
|
||||
return CARD_ReadWriteBackupAsync(offset, buf, size, param4, param5, 1, 6, 1, 0);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ extern "C" {
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nitro/os/cache.h"
|
||||
#include "nitro/os/context.h"
|
||||
#include "nitro/os/mutex.h"
|
||||
#include "nitro/os/owner.h"
|
||||
#include "nitro/os/thread.h"
|
||||
#include "nitro/reg.h"
|
||||
|
||||
@@ -30,6 +32,12 @@ extern "C" {
|
||||
|
||||
#define OS_THREAD_LAUNCHER_PRIORITY 0x10
|
||||
|
||||
#define OS_EXMEM_CNT_NDS_SLOT_ACCESS_SHIFT 11
|
||||
#define OS_EXMEM_CNT_NDS_SLOT_ACCESS (1 << OS_EXMEM_CNT_NDS_SLOT_ACCESS_SHIFT)
|
||||
|
||||
#define OS_CPU_ARM9 0
|
||||
#define OS_CPU_ARM7 1
|
||||
|
||||
typedef struct OSLinkedList {
|
||||
/* 00 */ void *head;
|
||||
/* 04 */ void *tail;
|
||||
@@ -166,6 +174,9 @@ s32 OS_func_0171(u32, u32, u32);
|
||||
s32 OS_func_0174(void);
|
||||
BOOL OS_func_0065(void);
|
||||
|
||||
void OS_func_0176(u8 *);
|
||||
void OS_func_0178(u32);
|
||||
|
||||
inline void OS_SetIrqCheckFlag(void) {
|
||||
REG_IRQ |= 1;
|
||||
}
|
||||
@@ -228,9 +239,16 @@ inline BOOL OS_IsRunOnTwl(void) {
|
||||
return false;
|
||||
#else
|
||||
// Probably checks some reg here
|
||||
#define REG_A9ROM_OFFSET 0x4000
|
||||
#define REG_SCFG_A9ROM_SEC_MASK 1
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void OS_SetNdsSlotAccess(u32 processor) {
|
||||
REG_EXMEM_CNT = (REG_EXMEM_CNT & ~OS_EXMEM_CNT_NDS_SLOT_ACCESS) | (processor << OS_EXMEM_CNT_NDS_SLOT_ACCESS_SHIFT);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#ifndef _NITRO_DC_H
|
||||
#define _NITRO_DC_H
|
||||
|
||||
#include "nitro/types.h"
|
||||
#ifndef _NITRO_OS_CACHE_H
|
||||
#define _NITRO_OS_CACHE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "nitro/types.h"
|
||||
|
||||
#define OS_CACHE_LINE_SIZE 0x20
|
||||
|
||||
void DC_StoreAll(void);
|
||||
void DC_StoreRange(void *ptr, u32 size);
|
||||
|
||||
@@ -15,9 +17,10 @@ void DC_FlushRange(void *ptr, u32 size);
|
||||
|
||||
void DC_InvalidateRange(void *ptr, u32 size);
|
||||
|
||||
void DC_func_0004(void *, int);
|
||||
void DC_func_0002();
|
||||
|
||||
void IC_InvalidateRange(void *ptr, u32 size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef _NITRO_CONTEXT_H
|
||||
#define _NITRO_CONTEXT_H
|
||||
|
||||
#include <types.h>
|
||||
#include "nitro/types.h"
|
||||
|
||||
typedef struct OSContext {
|
||||
/* 0x00 */ u32 cpsr;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef NITROSDK_OS_MUTEX_H
|
||||
#define NITROSDK_OS_MUTEX_H
|
||||
|
||||
#include <nitro/os/thread.h>
|
||||
#include <types.h>
|
||||
#include "nitro/os/thread.h"
|
||||
#include "nitro/types.h"
|
||||
|
||||
typedef struct OSMutex {
|
||||
/* 0x00 */ OSThreadQueue queue;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef _NITRO_OS_OWNER_H
|
||||
#define _NITRO_OS_OWNER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "nitro/types.h"
|
||||
|
||||
typedef struct OSOwnerInfo {
|
||||
/* 00 */ u8 unk_00[0x2];
|
||||
struct {
|
||||
/* 02 */ u8 month;
|
||||
/* 03 */ u8 day;
|
||||
} birthday;
|
||||
/* 04 */ u8 unk_04[0x16];
|
||||
/* 1a */ u16 nickNameLength;
|
||||
/* 1c */ u8 unk_1c[0x38];
|
||||
/* 54 */
|
||||
} OSOwnerInfo;
|
||||
|
||||
void OS_func_0177(OSOwnerInfo *info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -7,32 +7,37 @@ extern "C" {
|
||||
|
||||
#include "nitro/types.h"
|
||||
|
||||
#define REG_POWER_CNT (*(vu16 *) 0x04000304)
|
||||
#define REG_IME (*(vu16 *) 0x04000208)
|
||||
#define REG_BASE 0x4000000
|
||||
|
||||
#define REG_DISPSTAT (*(vu16 *) 0x4000004)
|
||||
#define REG_VCOUNT (*(vu16 *) 0x04000006)
|
||||
#define REG_DISP3DCNT (*(vu16 *) 0x04000060)
|
||||
#define REG_DISPCAPCNT (*(vu32 *) 0x04000064)
|
||||
#define REG_GFX_STATUS (*(vu32 *) 0x04000600)
|
||||
#define REG_POWER_CNT (*(vu16 *) (REG_BASE | 0x304))
|
||||
#define REG_IME (*(vu16 *) (REG_BASE | 0x208))
|
||||
|
||||
#define REG_DMA ((OSDma *) 0x040000B0)
|
||||
#define REG_DMA0SAD (*(vu32 *) 0x040000B0)
|
||||
#define REG_DMA0DAD (*(vu32 *) 0x040000B4)
|
||||
#define REG_DMA0CNT (*(vu32 *) 0x040000B8)
|
||||
#define REG_DISPSTAT (*(vu16 *) (REG_BASE | 0x4))
|
||||
#define REG_VCOUNT (*(vu16 *) (REG_BASE | 0x6))
|
||||
#define REG_DISP3DCNT (*(vu16 *) (REG_BASE | 0x60))
|
||||
#define REG_DISPCAPCNT (*(vu32 *) (REG_BASE | 0x64))
|
||||
#define REG_GFX_STATUS (*(vu32 *) (REG_BASE | 0x600))
|
||||
|
||||
#define REG_VRAM_CNT_ABCD (*(vu32 *) 0x04000240)
|
||||
#define REG_VRAM_CNT_A (*(vu8 *) 0x04000240)
|
||||
#define REG_VRAM_CNT_B (*(vu8 *) 0x04000241)
|
||||
#define REG_VRAM_CNT_C (*(vu8 *) 0x04000242)
|
||||
#define REG_VRAM_CNT_D (*(vu8 *) 0x04000243)
|
||||
#define REG_VRAM_CNT_E (*(vu8 *) 0x04000244)
|
||||
#define REG_VRAM_CNT_F (*(vu8 *) 0x04000245)
|
||||
#define REG_VRAM_CNT_G (*(vu8 *) 0x04000246)
|
||||
#define REG_WRAM_CNT (*(vu8 *) 0x04000247)
|
||||
#define REG_VRAM_CNT_HI (*(vu16 *) 0x04000248)
|
||||
#define REG_VRAM_CNT_H (*(vu8 *) 0x04000248)
|
||||
#define REG_VRAM_CNT_I (*(vu8 *) 0x04000249)
|
||||
#define REG_DMA ((OSDma *) (REG_BASE | 0xB0))
|
||||
#define REG_DMA0SAD (*(vu32 *) (REG_BASE | 0xB0))
|
||||
#define REG_DMA0DAD (*(vu32 *) (REG_BASE | 0xB4))
|
||||
#define REG_DMA0CNT (*(vu32 *) (REG_BASE | 0xB8))
|
||||
|
||||
#define REG_VRAM_CNT_ABCD (*(vu32 *) (REG_BASE | 0x240))
|
||||
#define REG_VRAM_CNT_A (*(vu8 *) (REG_BASE | 0x240))
|
||||
#define REG_VRAM_CNT_B (*(vu8 *) (REG_BASE | 0x241))
|
||||
#define REG_VRAM_CNT_C (*(vu8 *) (REG_BASE | 0x242))
|
||||
#define REG_VRAM_CNT_D (*(vu8 *) (REG_BASE | 0x243))
|
||||
#define REG_VRAM_CNT_E (*(vu8 *) (REG_BASE | 0x244))
|
||||
#define REG_VRAM_CNT_F (*(vu8 *) (REG_BASE | 0x245))
|
||||
#define REG_VRAM_CNT_G (*(vu8 *) (REG_BASE | 0x246))
|
||||
#define REG_WRAM_CNT (*(vu8 *) (REG_BASE | 0x247))
|
||||
#define REG_VRAM_CNT_HI (*(vu16 *) (REG_BASE | 0x248))
|
||||
#define REG_VRAM_CNT_H (*(vu8 *) (REG_BASE | 0x248))
|
||||
#define REG_VRAM_CNT_I (*(vu8 *) (REG_BASE | 0x249))
|
||||
|
||||
#define REG_EXMEM_CNT_OFFSET 0x204
|
||||
#define REG_EXMEM_CNT (*(vu16 *) (REG_BASE | REG_EXMEM_CNT_OFFSET))
|
||||
|
||||
#if NITRO_VERSION >= 0x05057533
|
||||
#define _BIOS_REG_BASE 0x02FFF000
|
||||
@@ -41,7 +46,16 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#define REG_PAD (*(u16 *) (_BIOS_REG_BASE | 0xFA8))
|
||||
#define REG_KEYINPUT (*(u16 *) 0x04000130)
|
||||
#define REG_KEYINPUT (*(u16 *) (REG_BASE | 0x130))
|
||||
|
||||
#define REG_CARD_AUX_SPI_CNT_OFFSET 0x1A0
|
||||
#define REG_CARD_AUX_SPI_CNT (*(vu16 *) (REG_BASE | 0x1A0))
|
||||
#define REG_CARD_CNT_OFFSET 0x1A4
|
||||
#define REG_CARD_CNT (*(vu32 *) (REG_BASE | REG_CARD_CNT_OFFSET))
|
||||
#define REG_CARD_CMD_OFFSET 0x1A8
|
||||
#define REG_CARD_CMD (*(vu8 *) (REG_BASE | REG_CARD_CMD_OFFSET))
|
||||
#define REG_CARD_DATA_OFFSET 0x100010
|
||||
#define REG_CARD_DATA (*(vu32 *) (REG_BASE | REG_CARD_DATA_OFFSET))
|
||||
|
||||
#define REG_FRAME_COUNTER (*(u32 *) (_BIOS_REG_BASE | 0xC3C))
|
||||
#define REG_027FFC40 (*(u16 *) (_BIOS_REG_BASE | 0xC40))
|
||||
@@ -56,33 +70,33 @@ extern "C" {
|
||||
#define REG_027FFF9C (*(u32 *) (_BIOS_REG_BASE | 0xF9C))
|
||||
#define REG_027FFFA0 (*(u32 *) (_BIOS_REG_BASE | 0xFA0))
|
||||
|
||||
#define REG_GFX_FIFO (*(vu32 *) 0x04000400)
|
||||
#define REG_GFX_FIFO_MATRIX_MODE (*(vu32 *) 0x04000440)
|
||||
#define REG_GFX_FIFO_MATRIX_PUSH (*(vu32 *) 0x04000444)
|
||||
#define REG_GFX_FIFO_MATRIX_POP (*(vu32 *) 0x04000448)
|
||||
#define REG_GFX_FIFO_MATRIX_STORE (*(vu32 *) 0x0400044C)
|
||||
#define REG_GFX_FIFO_MATRIX_RESTORE (*(vu32 *) 0x04000450)
|
||||
#define REG_GFX_FIFO_MATRIX_IDENTITY (*(vu32 *) 0x04000454)
|
||||
#define REG_GFX_FIFO_MATRIX_TRANSLATE (*(vu32 *) 0x04000470)
|
||||
#define REG_GFX_FIFO_VERTEX_COLOR (*(vu32 *) 0x04000480)
|
||||
#define REG_GFX_FIFO_VERTEX_TEXCOORD (*(vu32 *) 0x04000488)
|
||||
#define REG_GFX_FIFO_VERTEX_16 (*(vu32 *) 0x0400048C)
|
||||
#define REG_GFX_FIFO_VERTEX_XZ (*(vu32 *) 0x04000498)
|
||||
#define REG_GFX_FIFO_POLYGON_ATTR (*(vu32 *) 0x040004A4)
|
||||
#define REG_GFX_FIFO_TEXTURE_PARAM (*(vu32 *) 0x040004A8)
|
||||
#define REG_GFX_FIFO_TEXTURE_PALETTE (*(vu32 *) 0x040004AC)
|
||||
#define REG_GFX_FIFO_DIFFUSE_AMBIENT_REFLECT (*(vu32 *) 0x040004C0)
|
||||
#define REG_GFX_FIFO_POLYGONS_BEGIN (*(vu32 *) 0x04000500)
|
||||
#define REG_GFX_FIFO_POLYGONS_END (*(vu32 *) 0x04000504)
|
||||
#define REG_GFX_FIFO_SWAP_BUFFERS (*(vu32 *) 0x04000540)
|
||||
#define REG_GFX_FIFO_VIEWPORT (*(vu32 *) 0x04000580)
|
||||
#define REG_GFX_FIFO (*(vu32 *) (REG_BASE | 0x400))
|
||||
#define REG_GFX_FIFO_MATRIX_MODE (*(vu32 *) (REG_BASE | 0x440))
|
||||
#define REG_GFX_FIFO_MATRIX_PUSH (*(vu32 *) (REG_BASE | 0x444))
|
||||
#define REG_GFX_FIFO_MATRIX_POP (*(vu32 *) (REG_BASE | 0x448))
|
||||
#define REG_GFX_FIFO_MATRIX_STORE (*(vu32 *) (REG_BASE | 0x44C))
|
||||
#define REG_GFX_FIFO_MATRIX_RESTORE (*(vu32 *) (REG_BASE | 0x450))
|
||||
#define REG_GFX_FIFO_MATRIX_IDENTITY (*(vu32 *) (REG_BASE | 0x454))
|
||||
#define REG_GFX_FIFO_MATRIX_TRANSLATE (*(vu32 *) (REG_BASE | 0x470))
|
||||
#define REG_GFX_FIFO_VERTEX_COLOR (*(vu32 *) (REG_BASE | 0x480))
|
||||
#define REG_GFX_FIFO_VERTEX_TEXCOORD (*(vu32 *) (REG_BASE | 0x488))
|
||||
#define REG_GFX_FIFO_VERTEX_16 (*(vu32 *) (REG_BASE | 0x48C))
|
||||
#define REG_GFX_FIFO_VERTEX_XZ (*(vu32 *) (REG_BASE | 0x498))
|
||||
#define REG_GFX_FIFO_POLYGON_ATTR (*(vu32 *) (REG_BASE | 0x4A4))
|
||||
#define REG_GFX_FIFO_TEXTURE_PARAM (*(vu32 *) (REG_BASE | 0x4A8))
|
||||
#define REG_GFX_FIFO_TEXTURE_PALETTE (*(vu32 *) (REG_BASE | 0x4AC))
|
||||
#define REG_GFX_FIFO_DIFFUSE_AMBIENT_REFLECT (*(vu32 *) (REG_BASE | 0x4C0))
|
||||
#define REG_GFX_FIFO_POLYGONS_BEGIN (*(vu32 *) (REG_BASE | 0x500))
|
||||
#define REG_GFX_FIFO_POLYGONS_END (*(vu32 *) (REG_BASE | 0x504))
|
||||
#define REG_GFX_FIFO_SWAP_BUFFERS (*(vu32 *) (REG_BASE | 0x540))
|
||||
#define REG_GFX_FIFO_VIEWPORT (*(vu32 *) (REG_BASE | 0x580))
|
||||
|
||||
extern u32 __DTCM_LO;
|
||||
#define DTCM_LO ((u8 *) &__DTCM_LO)
|
||||
#define REG_IRQ (*(u32 *) (DTCM_LO + 0x3FF8))
|
||||
|
||||
#define _MAIN_REG_BASE 0x04000000
|
||||
#define _SUB_REG_BASE 0x04001000
|
||||
#define _MAIN_REG_BASE REG_BASE
|
||||
#define _SUB_REG_BASE (REG_BASE | 0x1000)
|
||||
|
||||
#define _REG_DISPCNT(base) (*(u32 *) ((base) | 0x0))
|
||||
#define _REG_BG0CNT(base) (*(vu16 *) ((base) | 0x8))
|
||||
@@ -171,7 +185,10 @@ extern u32 __DTCM_LO;
|
||||
#define REG_BLDALPHA_SUB _REG_BLDALPHA(_SUB_REG_BASE)
|
||||
#define REG_MASTER_BRIGHT_SUB _REG_MASTER_BRIGHT(_SUB_REG_BASE)
|
||||
|
||||
#define REG_04FFF200 (*(vu32 *) 0x04FFF200)
|
||||
#define REG_A9ROM_OFFSET 0x4000
|
||||
#define REG_A9ROM (*(vu16 *) (REG_BASE | REG_A9ROM_OFFSET))
|
||||
|
||||
#define REG_04FFF200 (*(vu32 *) (REG_BASE | 0xFFF200))
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@@ -9,11 +9,13 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
typedef unsigned long long u64;
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned char u8;
|
||||
|
||||
typedef long long s64;
|
||||
typedef long long slong;
|
||||
typedef int s32;
|
||||
typedef short s16;
|
||||
typedef char s8;
|
||||
@@ -22,11 +24,13 @@ typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
typedef volatile u64 vu64;
|
||||
typedef volatile u64 vulong;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u8 vu8;
|
||||
|
||||
typedef volatile s64 vs64;
|
||||
typedef volatile s64 vslong;
|
||||
typedef volatile s32 vs32;
|
||||
typedef volatile s16 vs16;
|
||||
typedef volatile s8 vs8;
|
||||
@@ -35,6 +39,8 @@ typedef volatile f32 vf32;
|
||||
typedef volatile f64 vf64;
|
||||
|
||||
typedef s32 BOOL;
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define ATTRIBUTE_ALIGN(x) __attribute__((aligned(x)))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user