mirror of
https://github.com/BrianPugh/game-and-watch-patch.git
synced 2025-12-16 07:16:26 +01:00
285 lines
7.7 KiB
C
285 lines
7.7 KiB
C
#include "main.h"
|
|
#include "stock_firmware.h"
|
|
#include <inttypes.h>
|
|
#include "cmsis_gcc.h"
|
|
#include <assert.h>
|
|
#include "gw_linker.h"
|
|
#include "stm32h7xx_hal.h"
|
|
#include "LzmaDec.h"
|
|
#include <string.h>
|
|
#include "ips.h"
|
|
|
|
#define MSP_ADDRESS 0x08000000
|
|
|
|
#define BANK_1_STACK_2_ADDRESS 0x08020000
|
|
#define BANK_2_ADDRESS 0x08100000
|
|
#define SD_BOOTLOADER_ADDRESS 0x08032000
|
|
|
|
// Other software (like retro-go) should set this value
|
|
#define BOOTLOADER_MAGIC 0x544F4F42 // "BOOT"
|
|
|
|
// Intended for internal-use only; bypasses other checks
|
|
#define BOOTLOADER_MAGIC_FORCE 0x45435246 // "FRCE"
|
|
|
|
#define BOOTLOADER_MAGIC_ADDRESS ((uint32_t *)0x2001FFF8)
|
|
#define BOOTLOADER_JUMP_ADDRESS ((uint32_t **)0x2001FFFC)
|
|
|
|
static void __attribute__((naked)) start_app(void (* const pc)(void), uint32_t sp) {
|
|
__asm(" \n\
|
|
msr msp, r1 /* load r1 into MSP */\n\
|
|
bx r0 /* branch to the address at r0 */\n\
|
|
");
|
|
}
|
|
|
|
static inline void set_bootloader(uint32_t address){
|
|
*BOOTLOADER_MAGIC_ADDRESS = BOOTLOADER_MAGIC_FORCE;
|
|
*BOOTLOADER_JUMP_ADDRESS = (uint32_t *)address;
|
|
}
|
|
|
|
/*Light sanity checks on what a good stack-pointer and program counter look like */
|
|
static inline bool is_valid(uint32_t pc, uint32_t sp){
|
|
return ((sp >> 24) == 0x20 ) && ((pc >> 24) == 0x08);
|
|
}
|
|
|
|
/**
|
|
* Executed on boot; will jump to a non-default program if:
|
|
* 1. the value at `BOOTLOADER_MAGIC_ADDRESS` is `BOOTLOADER_MAGIC`
|
|
* 2. the value at `BOOTLOADER_JUMP_ADDRESS` is the beginning of
|
|
* the firmware to execute.
|
|
* So to run that app, set those values and execute a reset.
|
|
*/
|
|
void bootloader(){
|
|
if(*BOOTLOADER_MAGIC_ADDRESS == BOOTLOADER_MAGIC_FORCE) {
|
|
*BOOTLOADER_MAGIC_ADDRESS = 0;
|
|
uint32_t sp = (*BOOTLOADER_JUMP_ADDRESS)[0];
|
|
uint32_t pc = (*BOOTLOADER_JUMP_ADDRESS)[1];
|
|
if (!is_valid(pc, sp)) goto start_ofw;
|
|
start_app((void (* const)(void)) pc, (uint32_t) sp);
|
|
}
|
|
|
|
HAL_Init();
|
|
|
|
HAL_PWR_EnableBkUpAccess();
|
|
__HAL_RCC_RTC_ENABLE();
|
|
__HAL_RCC_GPIOC_CLK_ENABLE();
|
|
|
|
RTC_HandleTypeDef hrtc = {0};
|
|
hrtc.Instance = RTC;
|
|
// Note: Don't need to call HAL_RTC_Init() since we're just reading backup register
|
|
|
|
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
|
GPIO_InitStruct.Pin = BTN_GAME_Pin;
|
|
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
|
GPIO_InitStruct.Pull = GPIO_PULLUP; // Button connects to GND.
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
|
|
|
HAL_GPIO_Init(BTN_GAME_GPIO_Port, &GPIO_InitStruct);
|
|
|
|
if(HAL_GPIO_ReadPin(BTN_GAME_GPIO_Port, BTN_GAME_Pin) == GPIO_PIN_RESET) {
|
|
// If GAME is pressed: reset all triggers that might cause us to dual-boot.
|
|
*BOOTLOADER_MAGIC_ADDRESS = 0;
|
|
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0);
|
|
}
|
|
|
|
if(*BOOTLOADER_MAGIC_ADDRESS == BOOTLOADER_MAGIC) {
|
|
*BOOTLOADER_MAGIC_ADDRESS = 0;
|
|
uint32_t sp = (*BOOTLOADER_JUMP_ADDRESS)[0];
|
|
uint32_t pc = (*BOOTLOADER_JUMP_ADDRESS)[1];
|
|
if (!is_valid(pc, sp)) goto start_ofw;
|
|
start_app((void (* const)(void)) pc, (uint32_t) sp);
|
|
}
|
|
|
|
|
|
if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) == BOOTLOADER_MAGIC){
|
|
#if SD_BOOTLOADER
|
|
uint32_t sp = *((uint32_t*)SD_BOOTLOADER_ADDRESS);
|
|
uint32_t pc = *((uint32_t*)SD_BOOTLOADER_ADDRESS + 1);
|
|
#else
|
|
uint32_t sp = *((uint32_t*)BANK_2_ADDRESS);
|
|
uint32_t pc = *((uint32_t*)BANK_2_ADDRESS + 1);
|
|
#endif
|
|
|
|
if (!is_valid(pc, sp)) goto start_ofw;
|
|
start_app((void (* const)(void)) pc, (uint32_t) sp);
|
|
}
|
|
|
|
start_ofw:
|
|
start_app(stock_Reset_Handler, *(uint32_t *) MSP_ADDRESS);
|
|
while(1);
|
|
}
|
|
|
|
|
|
#if ENABLE_SMB1_GRAPHIC_MODS
|
|
#define SMB1_GRAPHIC_MODS_MAX 8
|
|
const uint8_t * const SMB1_GRAPHIC_MODS[SMB1_GRAPHIC_MODS_MAX] = { 0 };
|
|
static volatile uint8_t smb1_graphics_idx = 0;
|
|
|
|
uint8_t * prepare_clock_rom(void *mario_rom, size_t len){
|
|
const uint8_t *patch = NULL;
|
|
|
|
if(smb1_graphics_idx > SMB1_GRAPHIC_MODS_MAX){
|
|
smb1_graphics_idx = 0;
|
|
}
|
|
if(smb1_graphics_idx){
|
|
patch = SMB1_GRAPHIC_MODS[smb1_graphics_idx - 1];
|
|
}
|
|
|
|
memcpy(smb1_clock_working, mario_rom, len);
|
|
|
|
if(patch) {
|
|
// Load custom graphics
|
|
if(IPS_PATCH_WRONG_HEADER == ips_patch(smb1_clock_working, patch)){
|
|
// Attempt a direct graphics override
|
|
memcpy_inflate(smb1_clock_graphics_working, patch, 0x1ec0);
|
|
}
|
|
}
|
|
else{
|
|
smb1_graphics_idx = 0;
|
|
}
|
|
|
|
return stock_prepare_clock_rom(smb1_clock_working, len);
|
|
}
|
|
#endif
|
|
|
|
#if ENABLE_SMB1_GRAPHIC_MODS
|
|
bool is_menu_open(){
|
|
return *ui_draw_status_addr == 5;
|
|
}
|
|
#endif
|
|
|
|
gamepad_t read_buttons() {
|
|
static gamepad_t gamepad_last = 0;
|
|
|
|
gamepad_t gamepad = 0;
|
|
gamepad = stock_read_buttons();
|
|
|
|
|
|
#if TRIPLE_BOOT
|
|
if((gamepad & GAMEPAD_RIGHT) && (gamepad & GAMEPAD_GAME)){
|
|
set_bootloader(BANK_1_STACK_2_ADDRESS);
|
|
NVIC_SystemReset();
|
|
}
|
|
#endif
|
|
|
|
#if CLOCK_ONLY
|
|
if(gamepad & GAMEPAD_GAME){
|
|
#else
|
|
if((gamepad & GAMEPAD_LEFT) && (gamepad & GAMEPAD_GAME)){
|
|
#endif
|
|
uint32_t *target_address;
|
|
#if SD_BOOTLOADER
|
|
target_address = SD_BOOTLOADER_ADDRESS;
|
|
#else
|
|
target_address = BANK_2_ADDRESS;
|
|
#endif
|
|
uint32_t sp = *target_address;
|
|
uint32_t pc = *(target_address + 1);
|
|
|
|
if(is_valid(pc, sp)){
|
|
set_bootloader(target_address);
|
|
NVIC_SystemReset();
|
|
}
|
|
}
|
|
|
|
#if ENABLE_SMB1_GRAPHIC_MODS
|
|
gnw_mode_t mode = get_gnw_mode();
|
|
if(mode == GNW_MODE_CLOCK && !is_menu_open()){
|
|
// Actions to only perform on the clock screen
|
|
if((gamepad & GAMEPAD_DOWN) && !(gamepad_last &GAMEPAD_DOWN)){
|
|
// TODO: detect if menu is up or not
|
|
smb1_graphics_idx++;
|
|
// Force a reload
|
|
*(uint8_t *)0x2000103d = 1; // Not sure the difference between setting 1 or 2.
|
|
}
|
|
}
|
|
#endif
|
|
|
|
gamepad_last = gamepad;
|
|
|
|
return gamepad;
|
|
}
|
|
|
|
|
|
const uint8_t LZMA_PROP_DATA[5] = {0x5d, 0x00, 0x40, 0x00, 0x00};
|
|
#define LZMA_BUF_SIZE 16256
|
|
|
|
static void *SzAlloc(ISzAllocPtr p, size_t size) {
|
|
void* res = p->Mem;
|
|
return res;
|
|
}
|
|
|
|
static void SzFree(ISzAllocPtr p, void *address) {
|
|
}
|
|
|
|
const ISzAlloc g_Alloc = { SzAlloc, SzFree };
|
|
|
|
static unsigned char lzma_heap[LZMA_BUF_SIZE];
|
|
/**
|
|
* Dropin replacement for memcpy for loading compressed assets.
|
|
* @param n Compressed data length. Can be larger than necessary.
|
|
*/
|
|
void *memcpy_inflate(uint8_t *dst, const uint8_t *src, size_t n){
|
|
ISzAlloc allocs = {
|
|
.Alloc=SzAlloc,
|
|
.Free=SzFree,
|
|
.Mem=lzma_heap,
|
|
};
|
|
|
|
ELzmaStatus status;
|
|
size_t dst_len = 393216;
|
|
LzmaDecode(dst, &dst_len, src, &n, LZMA_PROP_DATA, 5, LZMA_FINISH_ANY, &status, &allocs);
|
|
return dst;
|
|
}
|
|
|
|
/**
|
|
* This gets hooked into the rwdata/bss init table.
|
|
*/
|
|
int32_t *rwdata_inflate(int32_t *table){
|
|
uint8_t *data = (uint8_t *)table + table[0];
|
|
int32_t len = table[1];
|
|
uint8_t *ram = (uint8_t *) table[2];
|
|
memcpy_inflate(ram, data, len);
|
|
return table + 3;
|
|
}
|
|
|
|
|
|
/**
|
|
* This gets hooked into the rwdata/bss init table.
|
|
*/
|
|
int32_t *bss_rwdata_init(int32_t *table){
|
|
/* Copy init values from text to data */
|
|
uint32_t *init_values_ptr = &_sidata;
|
|
uint32_t *data_ptr = &_sdata;
|
|
|
|
if (init_values_ptr != data_ptr) {
|
|
for (; data_ptr < &_edata;) {
|
|
*data_ptr++ = *init_values_ptr++;
|
|
}
|
|
}
|
|
|
|
/* Clear the zero segment */
|
|
for (uint32_t *bss_ptr = &_sbss; bss_ptr < &_ebss;) {
|
|
*bss_ptr++ = 0;
|
|
}
|
|
return table;
|
|
}
|
|
|
|
|
|
#if ENABLE_SMB1_GRAPHIC_MODS
|
|
gnw_mode_t get_gnw_mode(){
|
|
uint8_t val = *gnw_mode_addr;
|
|
if(val == 0x20) return GNW_MODE_SMB2;
|
|
else if(val == 0x10) return GNW_MODE_SMB1;
|
|
else if(val == 0x08) return GNW_MODE_BALL;
|
|
else return GNW_MODE_CLOCK;
|
|
}
|
|
#endif
|
|
|
|
void NMI_Handler(void) {
|
|
__BKPT(0);
|
|
}
|
|
|
|
void HardFault_Handler(void) {
|
|
__BKPT(0);
|
|
}
|