diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 98730deb..170726a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -77,7 +77,7 @@ jobs: # continue-on-error: true - name: Upload rolling release - uses: softprops/action-gh-release@v0.1.15 + uses: softprops/action-gh-release@v2 if: github.ref == 'refs/heads/main' with: name: Rolling release @@ -105,7 +105,7 @@ jobs: doxyfile-path: './Doxyfile' - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 if: github.ref == 'refs/heads/main' with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/Makefile b/Makefile index 9d8acdc1..88c21b0e 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ N64_CFLAGS += -iquote $(SOURCE_DIR) -iquote $(ASSETS_DIR) -I $(SOURCE_DIR)/libs SRCS = \ main.c \ boot/boot.c \ + boot/cheats.c \ boot/cic.c \ boot/reboot.S \ flashcart/64drive/64drive_ll.c \ diff --git a/README.md b/README.md index a0112659..dd3624c5 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,8 @@ If required, you can manually adjust the file on the SD card using your computer - Download the latest `sc64menu.n64` file from the releases page, then put it in the root directory of your SD card. ##### 64DD disk support -For the ability to load and run 64DD disk images, you need to add the folder `/menu/64ddipl` on the SD card. -Download and add the relevant ipl files and rename them before adding them to the folder: -- `NDDE0.n64` the US Prototype IPL can be downloaded from [here](https://64dd.org/dumps/64DD_IPL_US_MJR.n64) -- `NDXJ0.n64` the JPN Development IPL can be downloaded from [here](https://64dd.org/dumps/64DD_IPL_DEV_H4G.n64) -- `NDDJ2.n64` the JPN Retail IPL can be downloaded from [here](https://64dd.org/dumps/N64DD_IPLROM_(J).zip) +For the ability to load and run 64DD disk images, you need to place required 64DD IPL dumps in the `/menu/64ddipl` folder on the SD card. +For more details follow [this guide on the 64dd.org website](https://64dd.org/tutorial_sc64.html). Note: to load an expansion disk (e.g. F-Zero X) browse to the N64 ROM and load it (but not start it) and then browse to the DD expansion file and press the `R` button. @@ -125,7 +122,6 @@ The ROMs can be found in the `output` directory. Make sure that your firmware is compatible (currently v2.18.0+) See: [here](https://github.com/Polprzewodnikowy/SummerCart64/blob/v2.18.0/docs/00_quick_startup_guide.md#firmware-backupupdate) - #### From the devcontainer It is not currently possible to directly communicate with USB devices. BUT, as a work around you can use a proxy TCP/IP connection @@ -143,6 +139,11 @@ Toggle the N64 power switch to load the ROM. #### From the SD Card A "release" version of the SC64 menu is called `sc64menu.n64` and can be created for when you want to add it directly to the root folder on the SDCard. This is generated by running `make all` or running `make sc64`. +### Ares Emulator +For ease of development and debugging, the menu ROM is able to run in the Ares emulator (without most flashcart features). + +* Ensure you have the Ares emulator on you computer. +* Load the `N64FlashcartMenu.n64` ROM. ### ED64 V3 over USB from your host (Windows) OS * Download the UNFLoader [here](https://github.com/buu342/N64-UNFLoader/releases/download/v2.11/UNFLoader-Windows-x86.zip) @@ -157,7 +158,7 @@ A "release" version of the SC64 menu is called `sc64menu.n64` and can be created # Update Libdragon submodule -This repo currently uses the `unstable` branch as a submodule at a specific commit. +This repo currently uses the `preview` branch as a submodule at a specific commit. To update to the latest version, use `git submodule update --remote ` from the terminal. # Generate documentation diff --git a/src/boot/boot.c b/src/boot/boot.c index d9d2eca3..9aaac778 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -2,7 +2,9 @@ #include "boot_io.h" #include "boot.h" +#include "cheats.h" #include "cic.h" +#include "reboot.h" #define C0_STATUS_FR (1 << 26) @@ -10,10 +12,6 @@ #define C0_STATUS_CU1 (1 << 29) -extern uint32_t reboot_start __attribute__((section(".text"))); -extern size_t reboot_size __attribute__((section(".text"))); - - static io32_t *boot_get_device_base (boot_params_t *params) { io32_t *device_base_address = ROM_CART; if (params->device_type == BOOT_DEVICE_TYPE_64DD) { @@ -22,7 +20,7 @@ static io32_t *boot_get_device_base (boot_params_t *params) { return device_base_address; } -static void boot_detect_cic_seed (boot_params_t *params) { +static cic_type_t boot_detect_cic (boot_params_t *params) { io32_t *base = boot_get_device_base(params); uint8_t ipl3[IPL3_LENGTH] __attribute__((aligned(8))); @@ -31,11 +29,17 @@ static void boot_detect_cic_seed (boot_params_t *params) { dma_read_raw_async(ipl3, (uint32_t) (&base[16]), sizeof(ipl3)); dma_wait(); - params->cic_seed = cic_get_seed(cic_detect(ipl3)); + return cic_detect(ipl3); } void boot (boot_params_t *params) { + cic_type_t cic_type = boot_detect_cic(params); + + if (params->detect_cic_seed) { + params->cic_seed = cic_get_seed(cic_type); + } + if (params->tv_type == BOOT_TV_TYPE_PASSTHROUGH) { switch (get_tv_type()) { case TV_PAL: @@ -53,10 +57,6 @@ void boot (boot_params_t *params) { } } - if (params->detect_cic_seed) { - boot_detect_cic_seed(params); - } - C0_WRITE_STATUS(C0_STATUS_CU1 | C0_STATUS_CU0 | C0_STATUS_FR); while (!(cpu_io_read(&SP->SR) & SP_SR_HALT)); @@ -123,12 +123,16 @@ void boot (boot_params_t *params) { cpu_io_write(&ipl3_dst[i], io_read((uint32_t) (&ipl3_src[i]))); } + bool cheats_installed = cheats_install(cic_type, params->cheat_list); + + register uint32_t skip_rdram_reset asm ("a0"); register uint32_t boot_device asm ("s3"); register uint32_t tv_type asm ("s4"); register uint32_t reset_type asm ("s5"); register uint32_t cic_seed asm ("s6"); register uint32_t version asm ("s7"); + skip_rdram_reset = cheats_installed; boot_device = (params->device_type & 0x01); tv_type = (params->tv_type & 0x03); reset_type = BOOT_RESET_TYPE_COLD; @@ -141,6 +145,7 @@ void boot (boot_params_t *params) { asm volatile ( "la $t3, reboot \n" "jr $t3 \n" :: + [skip_rdram_reset] "r" (skip_rdram_reset), [boot_device] "r" (boot_device), [tv_type] "r" (tv_type), [reset_type] "r" (reset_type), diff --git a/src/boot/boot.h b/src/boot/boot.h index 0bbd910d..3abd87da 100644 --- a/src/boot/boot.h +++ b/src/boot/boot.h @@ -38,6 +38,7 @@ typedef struct { boot_tv_type_t tv_type; uint8_t cic_seed; bool detect_cic_seed; + uint32_t *cheat_list; } boot_params_t; diff --git a/src/boot/cheats.c b/src/boot/cheats.c new file mode 100644 index 00000000..fd76eedc --- /dev/null +++ b/src/boot/cheats.c @@ -0,0 +1,335 @@ +#include + +#include "boot_io.h" +#include "cheats.h" +#include "vr4300_asm.h" + +#define HIT_INVALIDATE_I ((4 << 2) | 0) +#define HIT_WRITE_BACK_D ((6 << 2) | 1) + +#define D_CACHE_LINE_SIZE (16) + +#define CAUSE_IRQ_PRE_NMI (1 << 12) +#define CAUSE_EXC_CODE_MASK (0x7C) +#define CAUSE_EXC_CODE_WATCH (0x5C) + +#define WATCHLO_W (1 << 0) + +#define RELOCATED_EXCEPTION_HANDLER_ADDRESS (0x80000120) +#define EXCEPTION_HANDLER_ADDRESS (0x80000180) +#define PATCHER_ADDRESS (0x80700000) +#define ENGINE_TEMPORARY_ADDRESS (PATCHER_ADDRESS + 0x10000) +#define DEFAULT_ENGINE_ADDRESS (0x807C5C00) + +typedef struct { + uint8_t type; + uint32_t address; + uint16_t value; +} cheat_t; + +typedef struct { + cheat_t main; + cheat_t sub; +} cheat_entry_t; + +typedef enum { + SPECIAL_DISABLE_EXPANSION_PAK = 0xEE, + SPECIAL_WRITE_BYTE_ON_BOOT = 0xF0, + SPECIAL_WRITE_SHORT_ON_BOOT = 0xF1, + SPECIAL_SET_STORE_LOCATION = 0xFF, +} cheat_type_special_t; + +#define IS_WIDTH_16(t) ((t) & (1 << 0)) +#define IS_CONDITION_NOT_EQUAL(t) ((t) & (1 << 1)) +#define IS_CONDITION_GS_BUTTON(t) ((t) & (1 << 3)) + +#define IS_TYPE_REPEATER(t) ((t) == 0x50) +#define IS_TYPE_WRITE(t) ((((t)&0xF0) == 0x80) || (((t)&0xF0) == 0xA0)) +#define IS_TYPE_CONDITIONAL(t) (((t)&0xF0) == 0xD0) + +#define IS_DOUBLE_ENTRY(t) (IS_TYPE_CONDITIONAL(t) || IS_TYPE_REPEATER(t)) + +static bool cheats_patch_ipl3 (cic_type_t cic_type, io32_t *target) { + uint32_t patch_offset = 0; + uint32_t j_instruction = I_J((uint32_t)(target)); + + io32_t *ipl3 = SP_MEM->DMEM; + + switch (cic_type) { + case CIC_5101: patch_offset = 476; break; + case CIC_6101: + case CIC_7102: patch_offset = 476; break; + case CIC_x102: patch_offset = 475; break; + case CIC_x103: patch_offset = 472; break; + case CIC_x105: patch_offset = 499; break; + case CIC_x106: patch_offset = 488; break; + default: return true; + } + + // NOTE: Check for "jr $t1" instruction + // Libdragon IPL3 could be brute-force signed with any retail + // CIC seed and checksum, and we support only retail libultra IPL3 + if (cpu_io_read(&ipl3[patch_offset]) != I_JR(REG_T1)) { + return false; + } + + switch (cic_type) { + case CIC_x105: + // NOTE: This disables game code checksum verification + cpu_io_write(&ipl3[486], I_NOP()); + break; + + case CIC_x106: + // NOTE: CIC x106 IPL3 is partially scrambled + j_instruction ^= 0x8188764A; + break; + + default: break; + } + + cpu_io_write(&ipl3[patch_offset], j_instruction); + + return false; +} + +static bool cheats_get_next (uint32_t **cheat_list, cheat_entry_t *cheat) { + cheat_t *c = &cheat->main; + cheat->sub.type = 0; + + for (int i = 0; i < 2; i++) { + uint32_t raw[2] = {(*cheat_list)[0], (*cheat_list)[1]}; + + (*cheat_list) += 2; + + if ((raw[0] == 0) && (raw[1] == 0)) { + return false; + } + + c->type = ((raw[0] >> 24) & 0xFF); + c->address = (raw[0] & 0xA07FFFFF); + c->value = (raw[1] & 0xFFFF); + + if (!IS_DOUBLE_ENTRY(c->type)) { + break; + } + + c = &cheat->sub; + } + + return true; +} + +static io32_t *cheats_get_engine_address (uint32_t *cheat_list) { + cheat_entry_t cheat; + while (cheats_get_next(&cheat_list, &cheat)) { + if (cheat.main.type == SPECIAL_SET_STORE_LOCATION) { + return (io32_t *)(cheat.main.address & 0x807FFFFF); + } + } + return (io32_t *)(DEFAULT_ENGINE_ADDRESS); +} + +static void cheats_update_cache (volatile void *start, volatile void *end) { + data_cache_hit_writeback(start, (end - start)); + inst_cache_hit_invalidate(start, (end - start)); +} + +bool cheats_install (cic_type_t cic_type, uint32_t *cheat_list) { + if (!cheat_list) { + return false; + } + + io32_t *engine_start = (io32_t *)(ENGINE_TEMPORARY_ADDRESS); + io32_t *engine_p = engine_start; + + io32_t *patcher_start = (io32_t *)(PATCHER_ADDRESS); + io32_t *patcher_p = patcher_start; + + if (cheats_patch_ipl3(cic_type, patcher_start)) { + return false; + } + + io32_t *final_engine_address = cheats_get_engine_address(cheat_list); + + // Original watch exception handler code written by Jay Oster 'Parasyte' + // https://github.com/parasyte/alt64/blob/master/utils.c#L1024-L1054 + + uint32_t ori_placeholder_instruction = I_ORI(REG_ZERO, REG_K0, A_OFFSET(RELOCATED_EXCEPTION_HANDLER_ADDRESS)); + uint32_t ori_placeholder_address = (uint32_t)(final_engine_address + 20); + + // Load cause register + *engine_p++ = I_MFC0(REG_K0, C0_REG_CAUSE); + + // Disable watch exception when reset button is pressed + *engine_p++ = I_ANDI(REG_K1, REG_K0, CAUSE_IRQ_PRE_NMI); + *engine_p++ = I_BNEL(REG_K1, REG_ZERO, 1); + *engine_p++ = I_MTC0(REG_ZERO, C0_REG_WATCH_LO); + + // Check if watch exception ocurred, if yes then proceed to relocate the game exception handler + *engine_p++ = I_ANDI(REG_K0, REG_K0, CAUSE_EXC_CODE_MASK); + *engine_p++ = I_ORI(REG_K1, REG_ZERO, CAUSE_EXC_CODE_WATCH); + *engine_p++ = I_BNE(REG_K0, REG_K1, 15); // Skips to after the 'eret' instruction + + // Extract base register number from the store instruction + *engine_p++ = I_MFC0(REG_K1, C0_REG_EPC); + *engine_p++ = I_LW(REG_K1, 0, REG_K1); + *engine_p++ = I_LUI(REG_K0, 0x03E0); + *engine_p++ = I_AND(REG_K1, REG_K0, REG_K1); + *engine_p++ = I_SRL(REG_K1, REG_K1, 5); + + // Update create final instruction and update its target register number + *engine_p++ = I_LUI(REG_K0, ori_placeholder_instruction >> 16); + *engine_p++ = I_ORI(REG_K0, REG_K0, ori_placeholder_instruction); + *engine_p++ = I_OR(REG_K0, REG_K0, REG_K1); + + // Write created instruction into placeholder + *engine_p++ = I_LUI(REG_K1, A_BASE(ori_placeholder_address)); + *engine_p++ = I_SW(REG_K0, A_OFFSET(ori_placeholder_address), REG_K1); + + // Force write and instruction cache invalidation + *engine_p++ = I_CACHE(HIT_WRITE_BACK_D, A_OFFSET(ori_placeholder_address), REG_K1); + *engine_p++ = I_CACHE(HIT_INVALIDATE_I, A_OFFSET(ori_placeholder_address), REG_K1); + + // Load address base and execute created instruction + *engine_p++ = I_LUI(REG_K0, A_BASE(RELOCATED_EXCEPTION_HANDLER_ADDRESS)); + *engine_p++ = I_NOP(); + + // Return from the exception + *engine_p++ = I_ERET(); + + cheat_entry_t cheat; + + while (cheats_get_next(&cheat_list, &cheat)) { + cheat_t *c = &cheat.main; + + if (IS_TYPE_REPEATER(c->type)) { + if ((!IS_TYPE_WRITE(cheat.sub.type)) || IS_CONDITION_GS_BUTTON(cheat.sub.type)) { + continue; + } + + int count = ((c->address >> 8) & 0xFF); + int step = (c->address & 0xFF); + int16_t increment = (int16_t)(c->value); + + c = &cheat.sub; + + for (int i = 0; i < count; i++) { + *engine_p++ = I_LUI(REG_K0, A_BASE(c->address)); + *engine_p++ = I_ORI(REG_K1, REG_ZERO, c->value); + *engine_p++ = IS_WIDTH_16(c->type) ? I_SH(REG_K1, A_OFFSET(c->address), REG_K0) + : I_SB(REG_K1, A_OFFSET(c->address), REG_K0); + + c->address += step; + c->value += increment; + } + + continue; + } + + if (IS_TYPE_CONDITIONAL(c->type)) { + if ((!IS_TYPE_WRITE(cheat.sub.type)) || IS_CONDITION_GS_BUTTON(cheat.sub.type)) { + continue; + } + + *engine_p++ = I_LUI(REG_K0, A_BASE(c->address)); + *engine_p++ = IS_WIDTH_16(c->type) ? I_LHU(REG_K0, A_OFFSET(c->address), REG_K0) + : I_LBU(REG_K0, A_OFFSET(c->address), REG_K0); + *engine_p++ = I_ORI(REG_K1, REG_ZERO, c->value & (IS_WIDTH_16(c->type) ? 0xFFFF : 0xFF)); + *engine_p++ = IS_CONDITION_NOT_EQUAL(c->type) ? I_BEQ(REG_K0, REG_K1, 3) : I_BNE(REG_K0, REG_K1, 3); + + c = &cheat.sub; + } + + if (IS_TYPE_WRITE(c->type)) { + if (IS_CONDITION_GS_BUTTON(c->type)) { + continue; + } + + *engine_p++ = I_LUI(REG_K0, A_BASE(c->address)); + *engine_p++ = I_ORI(REG_K1, REG_ZERO, c->value); + *engine_p++ = IS_WIDTH_16(c->type) ? I_SH(REG_K1, A_OFFSET(c->address), REG_K0) + : I_SB(REG_K1, A_OFFSET(c->address), REG_K0); + + continue; + } + + switch (c->type) { + case SPECIAL_WRITE_BYTE_ON_BOOT: + case SPECIAL_WRITE_SHORT_ON_BOOT: { + *patcher_p++ = I_LUI(REG_K0, A_BASE(c->address)); + *patcher_p++ = I_ORI(REG_K1, REG_ZERO, c->value); + *patcher_p++ = IS_WIDTH_16(c->type) ? I_SH(REG_K1, A_OFFSET(c->address), REG_K0) + : I_SB(REG_K1, A_OFFSET(c->address), REG_K0); + break; + } + case SPECIAL_DISABLE_EXPANSION_PAK: { + *patcher_p++ = I_LUI(REG_K0, 0xA000); + *patcher_p++ = I_LUI(REG_K1, 0x0040); + *patcher_p++ = I_SW(REG_K1, 0x318, REG_K0); + *patcher_p++ = I_SW(REG_K1, 0x3F0, REG_K0); + break; + } + default: break; + } + } + + *engine_p++ = I_J(RELOCATED_EXCEPTION_HANDLER_ADDRESS); + *engine_p++ = I_NOP(); + + uint32_t j_engine_from_handler = I_J((uint32_t)(final_engine_address)); + + // Copy engine to the final location + *patcher_p++ = I_LUI(REG_T3, A_BASE((uint32_t)(engine_start))); + *patcher_p++ = I_ADDIU(REG_T3, REG_T3, A_OFFSET((uint32_t)(engine_start))); + + *patcher_p++ = I_LUI(REG_T4, A_BASE((uint32_t)(engine_p))); + *patcher_p++ = I_ADDIU(REG_T4, REG_T4, A_OFFSET((uint32_t)(engine_p))); + + *patcher_p++ = I_LUI(REG_T5, A_BASE((uint32_t)(final_engine_address))); + *patcher_p++ = I_ADDIU(REG_T5, REG_T5, A_OFFSET((uint32_t)(final_engine_address))); + + *patcher_p++ = I_ORI(REG_T6, REG_ZERO, 0); + + *patcher_p++ = I_LW(REG_K1, 0, REG_T3); + *patcher_p++ = I_SW(REG_K1, 0, REG_T5); + *patcher_p++ = I_ADDIU(REG_T3, REG_T3, 4); + *patcher_p++ = I_ADDIU(REG_T5, REG_T5, 4); + *patcher_p++ = I_BNE(REG_T3, REG_T4, -5); + *patcher_p++ = I_ADDIU(REG_T6, REG_T6, 4); + + // Force write and invalidate instruction cache + *patcher_p++ = I_LUI(REG_T5, A_BASE((uint32_t)(final_engine_address))); + *patcher_p++ = I_ADDIU(REG_T5, REG_T5, A_OFFSET((uint32_t)(final_engine_address))); + + *patcher_p++ = I_CACHE(HIT_WRITE_BACK_D, 0, REG_T5); + *patcher_p++ = I_CACHE(HIT_INVALIDATE_I, 0, REG_T5); + *patcher_p++ = I_ADDIU(REG_T6, REG_T6, -D_CACHE_LINE_SIZE); + *patcher_p++ = I_BGTZ(REG_T6, -4); + *patcher_p++ = I_ADDIU(REG_T5, REG_T5, D_CACHE_LINE_SIZE); + + // Write jump instruction to the exception handler + *patcher_p++ = I_LUI(REG_K0, A_BASE(EXCEPTION_HANDLER_ADDRESS)); + *patcher_p++ = I_ADDIU(REG_K0, REG_K0, A_OFFSET(EXCEPTION_HANDLER_ADDRESS)); + + *patcher_p++ = I_LUI(REG_K1, j_engine_from_handler >> 16); + *patcher_p++ = I_ORI(REG_K1, REG_K1, j_engine_from_handler); + *patcher_p++ = I_SW(REG_K1, 0, REG_K0); + *patcher_p++ = I_SW(REG_ZERO, 4, REG_K0); + + *patcher_p++ = I_CACHE(HIT_WRITE_BACK_D, 0, REG_K0); + *patcher_p++ = I_CACHE(HIT_INVALIDATE_I, 0, REG_K0); + + // Set watch exception on address 0x80000180 + *patcher_p++ = I_ORI(REG_K1, REG_ZERO, EXCEPTION_HANDLER_ADDRESS | WATCHLO_W); + *patcher_p++ = I_MTC0(REG_K1, C0_REG_WATCH_LO); + *patcher_p++ = I_MTC0(REG_ZERO, C0_REG_WATCH_HI); + + // Jump back to the game code + *patcher_p++ = I_JR(REG_T1); + *patcher_p++ = I_NOP(); + + cheats_update_cache(engine_start, engine_p); + cheats_update_cache(patcher_start, patcher_p); + + return true; +} diff --git a/src/boot/cheats.h b/src/boot/cheats.h new file mode 100644 index 00000000..53cbbe5e --- /dev/null +++ b/src/boot/cheats.h @@ -0,0 +1,10 @@ +#ifndef CHEATS_H__ +#define CHEATS_H__ + +#include + +#include "cic.h" + +bool cheats_install (cic_type_t cic_type, uint32_t *cheat_list); + +#endif diff --git a/src/boot/reboot.S b/src/boot/reboot.S index 5d0ab6de..e78cbbc9 100644 --- a/src/boot/reboot.S +++ b/src/boot/reboot.S @@ -1,3 +1,6 @@ +#include "reboot.h" + + #define IPL3_ENTRY 0xA4000040 #define REBOOT_ADDRESS 0xA4001000 #define STACK_ADDRESS 0xA4001FF0 @@ -34,9 +37,10 @@ reboot_entry: li $sp, STACK_ADDRESS -reset_rdram: - bnez $s5, reset_rdram_skip + bnez $a0, reset_rdram_skip # Skip when cheats are enabled + bnez $s5, reset_rdram_skip # Skip when reset type is set to NMI +reset_rdram: li $t0, RI_ADDRESS sw $zero, RI_REFRESH($t0) diff --git a/src/boot/reboot.h b/src/boot/reboot.h new file mode 100644 index 00000000..d8ec302b --- /dev/null +++ b/src/boot/reboot.h @@ -0,0 +1,17 @@ +#ifndef REBOOT_H__ +#define REBOOT_H__ + + +#ifndef __ASSEMBLER__ + +#include +#include + + +extern uint32_t reboot_start __attribute__((section(".text"))); +extern size_t reboot_size __attribute__((section(".text"))); + +#endif + + +#endif diff --git a/src/boot/vr4300_asm.h b/src/boot/vr4300_asm.h new file mode 100644 index 00000000..59dfd77c --- /dev/null +++ b/src/boot/vr4300_asm.h @@ -0,0 +1,397 @@ +#ifndef VR4300_ASM_H__ +#define VR4300_ASM_H__ + +#include + +typedef union { + uint32_t raw; + + struct { + uint32_t op : 6; + uint32_t rs : 5; + uint32_t rt : 5; + uint32_t imm : 16; + } i_type; + + struct { + uint32_t op : 6; + uint32_t target : 26; + } j_type; + + struct { + uint32_t op : 6; + uint32_t rs : 5; + uint32_t rt : 5; + uint32_t rd : 5; + uint32_t sa : 5; + uint32_t funct : 6; + } r_type; + + struct { + uint32_t op : 6; + uint32_t co : 1; + uint32_t funct : 25; + } c_type; +} vr4300_instruction_t; + +typedef enum { + OP_SPECIAL, + OP_REGIMM, + OP_J, + OP_JAL, + OP_BEQ, + OP_BNE, + OP_BLEZ, + OP_BGTZ, + OP_ADDI, + OP_ADDIU, + OP_SLTI, + OP_SLTIU, + OP_ANDI, + OP_ORI, + OP_XORI, + OP_LUI, + OP_COP0, + OP_COP1, + OP_COP2, + __OP_RESERVED_19, + OP_BEQL, + OP_BNEL, + OP_BLEZL, + OP_BGTZL, + OP_DADDI, + OP_DADDIU, + OP_LDL, + OP_LDR, + __OP_RESERVED_28, + __OP_RESERVED_29, + __OP_RESERVED_30, + __OP_RESERVED_31, + OP_LB, + OP_LH, + OP_LWL, + OP_LW, + OP_LBU, + OP_LHU, + OP_LWR, + OP_LWU, + OP_SB, + OP_SH, + OP_SWL, + OP_SW, + OP_SDL, + OP_SDR, + OP_SWR, + OP_CACHE, + OP_LL, + OP_LWC1, + OP_LWC2, + __OP_RESERVED_51, + OP_LLD, + OP_LDC1, + OP_LDC2, + OP_LD, + OP_SC, + OP_SWC1, + OP_SWC2, + __OP_RESERVED_59, + OP_SCD, + OP_SDC1, + OP_SDC2, + OP_SD, +} vr4300_op_t; + +typedef enum { + FUNCT_SSL, + __FUNCT_RESERVED_1, + FUNCT_SRL, + FUNCT_SRA, + FUNCT_SLLV, + __FUNCT_RESERVED_5, + FUNCT_SRLV, + FUNCT_SRAV, + FUNCT_JR, + FUNCT_JALR, + __FUNCT_RESERVED_10, + __FUNCT_RESERVED_11, + FUNCT_SYSCALL, + FUNCT_BREAK, + __FUNCT_RESERVED_14, + FUNCT_SYNC, + FUNCT_MFHI, + FUNCT_MTHI, + FUNCT_MFLO, + FUNCT_MTLO, + FUNCT_DSLLV, + __FUNCT_RESERVED_21, + FUNCT_DSRLV, + FUNCT_DSRAV, + FUNCT_MULT, + FUNCT_MULTU, + FUNCT_DIV, + FUNCT_DIVU, + FUNCT_DMULT, + FUNCT_DMULTU, + FUNCT_DDIV, + FUNCT_DDIVU, + FUNCT_ADD, + FUNCT_ADDU, + FUNCT_SUB, + FUNCT_SUBU, + FUNCT_AND, + FUNCT_OR, + FUNCT_XOR, + FUNCT_NOR, + __FUNCT_RESERVED_40, + __FUNCT_RESERVED_41, + FUNCT_SLT, + FUNCT_SLTU, + FUNCT_DADD, + FUNCT_DADDU, + FUNCT_DSUB, + FUNCT_DSUBU, + FUNCT_TGE, + FUNCT_TGEU, + FUNCT_TLT, + FUNCT_TLTU, + FUNCT_TEQ, + __FUNCT_RESERVED_53, + FUNCT_TNE, + __FUNCT_RESERVED_55, + FUNCT_DSLL, + __FUNCT_RESERVED_57, + FUNCT_DSRL, + FUNCT_DSRA, + FUNCT_DSLL32, + __FUNCT_RESERVED_61, + FUNCT_DSRL32, + FUNCT_DSRA32, +} vr4300_funct_t; + +typedef enum { + REGIMM_BLTZ, + REGIMM_BGEZ, + REGIMM_BLTZL, + REGIMM_BGEZL, + __REGIMM_RESERVED_4, + __REGIMM_RESERVED_5, + __REGIMM_RESERVED_6, + __REGIMM_RESERVED_7, + REGIMM_TGEI, + REGIMM_TGEIU, + REGIMM_TLTI, + REGIMM_TLTIU, + REGIMM_TEQI, + __REGIMM_RESERVED_13, + REGIMM_TNEI, + __REGIMM_RESERVED_15, + REGIMM_BLTZAL, + REGIMM_BGEZAL, + REGIMM_BLTZALL, + REGIMM_BGEZALL, + __REGIMM_RESERVED_20, + __REGIMM_RESERVED_21, + __REGIMM_RESERVED_22, + __REGIMM_RESERVED_23, + __REGIMM_RESERVED_24, + __REGIMM_RESERVED_25, + __REGIMM_RESERVED_26, + __REGIMM_RESERVED_27, + __REGIMM_RESERVED_28, + __REGIMM_RESERVED_29, + __REGIMM_RESERVED_30, + __REGIMM_RESERVED_31, +} vr4300_regimm_t; + +typedef enum { + REG_ZERO, + REG_AT, + REG_V0, + REG_V1, + REG_A0, + REG_A1, + REG_A2, + REG_A3, + REG_T0, + REG_T1, + REG_T2, + REG_T3, + REG_T4, + REG_T5, + REG_T6, + REG_T7, + REG_S0, + REG_S1, + REG_S2, + REG_S3, + REG_S4, + REG_S5, + REG_S6, + REG_S7, + REG_T8, + REG_T9, + REG_K0, + REG_K1, + REG_GP, + REG_SP, + REG_FP, + REG_RA, +} vr4300_reg_t; + +typedef enum { + C0_REG_INDEX, + C0_REG_RANDOM, + C0_REG_ENTRY_LO_0, + C0_REG_ENTRY_LO_1, + C0_REG_CONTEXT, + C0_REG_PAGE_MASK, + C0_REG_WIRED, + __C0_REG_RESERVED_7, + C0_REG_BAD_V_ADDR, + C0_REG_COUNT, + C0_REG_ENTRY_HI, + C0_REG_COMPARE, + C0_REG_STATUS, + C0_REG_CAUSE, + C0_REG_EPC, + C0_REG_PR_ID, + C0_REG_CONFIG, + C0_REG_LL_ADDR, + C0_REG_WATCH_LO, + C0_REG_WATCH_HI, + C0_REG_X_CONTEXT, + __C0_REG_RESERVED_21, + __C0_REG_RESERVED_22, + __C0_REG_RESERVED_23, + __C0_REG_RESERVED_24, + __C0_REG_RESERVED_25, + C0_REG_PARITY_ERROR, + C0_REG_CACHE_ERROR, + C0_REG_TAG_LO, + C0_REG_TAG_HI, + C0_REG_ERROR_EPC, + __C0_REG_RESERVED_31, +} vr4300_c0_reg_t; + +typedef enum { + COPZ_RS_MF, + COPZ_RS_DMF, + COPZ_RS_CF, + __COPZ_RS_RESERVED_3, + COPZ_RS_MT, + COPZ_RS_DMT, + COPZ_RS_CT, + __COPZ_RS_RESERVED_7, + COPZ_RS_BC, + __COPZ_RS_RESERVED_9, + __COPZ_RS_RESERVED_10, + __COPZ_RS_RESERVED_11, + __COPZ_RS_RESERVED_12, + __COPZ_RS_RESERVED_13, + __COPZ_RS_RESERVED_14, + __COPZ_RS_RESERVED_15, +} vr4300_copz_rs_t; + +typedef enum { + __C0_FUNCT_RESERVED_0, + C0_FUNCT_TLBR, + C0_FUNCT_TLBWI, + __C0_FUNCT_RESERVED_3, + __C0_FUNCT_RESERVED_4, + __C0_FUNCT_RESERVED_5, + C0_FUNCT_TLBWR, + __C0_FUNCT_RESERVED_7, + C0_FUNCT_TLBP, + __C0_FUNCT_RESERVED_9, + __C0_FUNCT_RESERVED_10, + __C0_FUNCT_RESERVED_11, + __C0_FUNCT_RESERVED_12, + __C0_FUNCT_RESERVED_13, + __C0_FUNCT_RESERVED_14, + __C0_FUNCT_RESERVED_15, + __C0_FUNCT_RESERVED_16, + __C0_FUNCT_RESERVED_17, + __C0_FUNCT_RESERVED_18, + __C0_FUNCT_RESERVED_19, + __C0_FUNCT_RESERVED_20, + __C0_FUNCT_RESERVED_21, + __C0_FUNCT_RESERVED_22, + __C0_FUNCT_RESERVED_23, + C0_FUNCT_ERET, + __C0_FUNCT_RESERVED_25, + __C0_FUNCT_RESERVED_26, + __C0_FUNCT_RESERVED_27, + __C0_FUNCT_RESERVED_28, + __C0_FUNCT_RESERVED_29, + __C0_FUNCT_RESERVED_30, + __C0_FUNCT_RESERVED_31, + __C0_FUNCT_RESERVED_32, + __C0_FUNCT_RESERVED_33, + __C0_FUNCT_RESERVED_34, + __C0_FUNCT_RESERVED_35, + __C0_FUNCT_RESERVED_36, + __C0_FUNCT_RESERVED_37, + __C0_FUNCT_RESERVED_38, + __C0_FUNCT_RESERVED_39, + __C0_FUNCT_RESERVED_40, + __C0_FUNCT_RESERVED_41, + __C0_FUNCT_RESERVED_42, + __C0_FUNCT_RESERVED_43, + __C0_FUNCT_RESERVED_44, + __C0_FUNCT_RESERVED_45, + __C0_FUNCT_RESERVED_46, + __C0_FUNCT_RESERVED_47, + __C0_FUNCT_RESERVED_48, + __C0_FUNCT_RESERVED_49, + __C0_FUNCT_RESERVED_50, + __C0_FUNCT_RESERVED_51, + __C0_FUNCT_RESERVED_52, + __C0_FUNCT_RESERVED_53, + __C0_FUNCT_RESERVED_54, + __C0_FUNCT_RESERVED_55, + __C0_FUNCT_RESERVED_56, + __C0_FUNCT_RESERVED_57, + __C0_FUNCT_RESERVED_58, + __C0_FUNCT_RESERVED_59, + __C0_FUNCT_RESERVED_60, + __C0_FUNCT_RESERVED_61, + __C0_FUNCT_RESERVED_62, + __C0_FUNCT_RESERVED_63, +} vr4300_c0_funct; + +#define __ASM_I_INST(o, s, t, i) \ + (((vr4300_instruction_t){.i_type = {.op = (o), .rs = (s), .rt = (t), .imm = (i)&0xFFFF}}).raw) +#define __ASM_J_INST(o, t) (((vr4300_instruction_t){.j_type = {.op = (o), .target = (t)&0x3FFFFFF}}).raw) +#define __ASM_R_INST(o, s, t, d, a, f) \ + (((vr4300_instruction_t){.r_type = {.op = (o), .rs = (s), .rt = (t), .rd = (d), .sa = (a), .funct = (f)}}).raw) +#define __ASM_C_INST(o, c, f) (((vr4300_instruction_t){.c_type = {.op = (o), .co = (c), .funct = (f)}}).raw) + +#define A_OFFSET(a) ((int16_t)((a)&0xFFFF)) +#define A_BASE(a) ((uint16_t)((((a) >> 16) & 0xFFFF) + (A_OFFSET(a) < 0 ? 1 : 0))) + +#define I_ADDIU(rt, rs, immediate) __ASM_I_INST(OP_ADDIU, rs, rt, immediate) +#define I_AND(rd, rs, rt) __ASM_R_INST(OP_SPECIAL, rs, rt, rd, 0, FUNCT_AND) +#define I_ANDI(rt, rs, immediate) __ASM_I_INST(OP_ANDI, rs, rt, immediate) +#define I_BEQ(rs, rt, offset) __ASM_I_INST(OP_BEQ, rs, rt, offset) +#define I_BGTZ(rs, offset) __ASM_I_INST(OP_BGTZ, rs, 0, offset) +#define I_BNE(rs, rt, offset) __ASM_I_INST(OP_BNE, rs, rt, offset) +#define I_BNEL(rs, rt, offset) __ASM_I_INST(OP_BNEL, rs, rt, offset) +#define I_CACHE(op, offset, base) __ASM_I_INST(OP_CACHE, base, op, offset) +#define I_ERET() __ASM_C_INST(OP_COP0, 1, C0_FUNCT_ERET) +#define I_J(target) __ASM_J_INST(OP_J, (target >> 2)) +#define I_JR(rs) __ASM_R_INST(OP_SPECIAL, rs, REG_ZERO, REG_ZERO, 0, FUNCT_JR) +#define I_LBU(rt, offset, base) __ASM_I_INST(OP_LBU, base, rt, offset) +#define I_LHU(rt, offset, base) __ASM_I_INST(OP_LHU, base, rt, offset) +#define I_LUI(rt, immediate) __ASM_I_INST(OP_LUI, 0, rt, immediate) +#define I_LW(rt, offset, base) __ASM_I_INST(OP_LW, base, rt, offset) +#define I_MFC0(rt, rd) __ASM_R_INST(OP_COP0, COPZ_RS_MF, rt, rd, 0, 0) +#define I_MTC0(rt, rd) __ASM_R_INST(OP_COP0, COPZ_RS_MT, rt, rd, 0, 0) +#define I_NOP() __ASM_R_INST(OP_SPECIAL, REG_ZERO, REG_ZERO, REG_ZERO, 0, FUNCT_SSL) +#define I_OR(rd, rs, rt) __ASM_R_INST(OP_SPECIAL, rs, rt, rd, 0, FUNCT_OR) +#define I_ORI(rt, rs, immediate) __ASM_I_INST(OP_ORI, rs, rt, immediate) +#define I_SB(rt, offset, base) __ASM_I_INST(OP_SB, base, rt, offset) +#define I_SH(rt, offset, base) __ASM_I_INST(OP_SH, base, rt, offset) +#define I_SRL(rd, rt, sa) __ASM_R_INST(OP_SPECIAL, 0, rt, rd, sa, FUNCT_SRL) +#define I_SW(rt, offset, base) __ASM_I_INST(OP_SW, base, rt, offset) + +#endif diff --git a/src/flashcart/64drive/README.md b/src/flashcart/64drive/README.md index bd961b85..6809afcc 100644 --- a/src/flashcart/64drive/README.md +++ b/src/flashcart/64drive/README.md @@ -2,7 +2,7 @@ ### Official documentation -http://64drive.retroactive.be/64drive_hardware_spec.pdf +https://web.archive.org/web/20220611032320/http://64drive.retroactive.be/64drive_hardware_spec.pdf ### Save location offset in SDRAM diff --git a/src/menu/rom_info.c b/src/menu/rom_info.c index 4713eee7..14026ee9 100644 --- a/src/menu/rom_info.c +++ b/src/menu/rom_info.c @@ -147,12 +147,26 @@ static const match_t database[] = { MATCH_HOMEBREW_HEADER("ED"), // Homebrew header (ED) MATCH_CHECK_CODE(0x000000004CBC3B56, SAVE_TYPE_SRAM, FEAT_EXP_PAK_REQUIRED | FEAT_64DD_CONVERSION), // DMTJ 64DD cartridge conversion + MATCH_CHECK_CODE(0x0DD4ABABB5A2A91E, SAVE_TYPE_EEPROM_16K, FEAT_EXP_PAK_REQUIRED), // DK Retail kiosk demo MATCH_CHECK_CODE(0xEB85EBC9596682AF, SAVE_TYPE_FLASHRAM, FEAT_NONE), // Doubutsu Banchou + MATCH_CHECK_CODE(0x9A746EBF2802EA99, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Toon panic + MATCH_CHECK_CODE(0x21548CA921548CA9, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Mini racers + MATCH_CHECK_CODE(0xBC9B2CC34ED04DA5, SAVE_TYPE_FLASHRAM, FEAT_NONE), // Starcraft 64 [Prototype 2000] + MATCH_CHECK_CODE(0x5D40ED2C10D6ABCF, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Viewpoint 2064 + MATCH_CHECK_CODE(0x7280E03F497689BA, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Viewpoint 2064 [ENG patch] + + MATCH_CHECK_CODE(0xCDB8B4D08832352D, SAVE_TYPE_SRAM, FEAT_RPAK), // Jet Force Gemini [USA CRACK] + MATCH_CHECK_CODE(0xB66E0F7C2709C22F, SAVE_TYPE_SRAM, FEAT_RPAK), // Jet Force Gemini [PAL CRACK] + + MATCH_CHECK_CODE(0xCE84793D27ECC1AD, SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_EXP_PAK_REQUIRED), // Donkey kong 64 [USA CRACK] + MATCH_CHECK_CODE(0x1F95CAAA047FC22A, SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_EXP_PAK_REQUIRED), // Donkey kong 64 [PAL CRACK] + + MATCH_CHECK_CODE(0xE3FF09DFCAE4B0ED, SAVE_TYPE_SRAM, FEAT_RPAK), // Banjo tooie [USA CRACK] MATCH_ID_REGION_VERSION("NK4J", 0, SAVE_TYPE_SRAM, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)] MATCH_ID_REGION_VERSION("NK4J", 1, SAVE_TYPE_SRAM, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)] - MATCH_ID("NK4", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)] + MATCH_ID("NK4", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)] MATCH_ID_REGION_VERSION("NSMJ", 3, SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Super Mario 64 Shindou Edition MATCH_ID("NSM", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Super Mario 64 @@ -177,6 +191,7 @@ static const match_t database[] = { MATCH_ID_REGION("NWTJ", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Wetrix MATCH_ID("NWT", SAVE_TYPE_NONE, FEAT_CPAK), // Wetrix + // EEPROM 4K MATCH_ID("CLB", SAVE_TYPE_EEPROM_4K, FEAT_RPAK | FEAT_64DD_ENHANCED), // Mario Party (NTSC) MATCH_ID("NAB", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Air Boarder 64 MATCH_ID("NAD", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Worms Armageddon (U) @@ -271,6 +286,7 @@ static const match_t database[] = { MATCH_ID("NXO", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Cruis'n Exotica MATCH_ID("NYK", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Yakouchuu II: Satsujin Kouro + // EEPROM 16K MATCH_ID("N3D", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Doraemon 3: Nobita no Machi SOS! MATCH_ID("NB7", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Banjo-Tooie [Banjo to Kazooie no Daiboken 2 (J)] MATCH_ID("NCW", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Cruis'n World @@ -295,12 +311,14 @@ static const match_t database[] = { MATCH_ID("NUB", SAVE_TYPE_EEPROM_16K, FEAT_CPAK | FEAT_TPAK), // PD Ultraman Battle Collection 64 MATCH_ID("NYS", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Yoshi's Story + // SRAM 256K MATCH_ID("CFZ", SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_64DD_ENHANCED), // F-Zero X (J) MATCH_ID("CPS", SAVE_TYPE_SRAM, FEAT_TPAK | FEAT_64DD_ENHANCED), // Pocket Monsters Stadium (J) MATCH_ID("CZL", SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_64DD_ENHANCED), // Legend of Zelda: Ocarina of Time [Zelda no Densetsu - Toki no Ocarina (J)] MATCH_ID("NA2", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_RPAK), // Virtual Pro Wrestling 2 MATCH_ID("NAL", SAVE_TYPE_SRAM, FEAT_RPAK), // Super Smash Bros. [Nintendo All-Star! Dairantou Smash Brothers (J)] MATCH_ID("NB5", SAVE_TYPE_SRAM, FEAT_RPAK), // Biohazard 2 (J) + MATCH_ID("NDD", SAVE_TYPE_SRAM, FEAT_EXP_PAK_REQUIRED | FEAT_64DD_CONVERSION), // 64DD Conversion Rom MATCH_ID("NFZ", SAVE_TYPE_SRAM, FEAT_RPAK), // F-Zero X (U + E) MATCH_ID("NG6", SAVE_TYPE_SRAM, FEAT_RPAK), // Ganmare Goemon: Dero Dero Douchuu Obake Tenkomori MATCH_ID("NGP", SAVE_TYPE_SRAM, FEAT_CPAK), // Goemon: Mononoke Sugoroku @@ -333,11 +351,14 @@ static const match_t database[] = { MATCH_ID("NYW", SAVE_TYPE_SRAM, FEAT_NONE), // Harvest Moon 64 MATCH_ID("NZL", SAVE_TYPE_SRAM, FEAT_RPAK), // Legend of Zelda: Ocarina of Time (E) + // SRAM 768K MATCH_ID("CDZ", SAVE_TYPE_SRAM_BANKED, FEAT_RPAK | FEAT_64DD_ENHANCED), // Dezaemon 3D + // FLASHRAM MATCH_ID("CP2", SAVE_TYPE_FLASHRAM, FEAT_TPAK | FEAT_64DD_ENHANCED), // Pocket Monsters Stadium 2 (J) MATCH_ID("NAF", SAVE_TYPE_FLASHRAM, FEAT_CPAK | FEAT_RTC), // Doubutsu no Mori MATCH_ID("NCC", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // Command & Conquer + MATCH_ID("NCV", SAVE_TYPE_FLASHRAM, FEAT_NONE), // Cubivore (Translation) MATCH_ID("NCK", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // NBA Courtside 2 featuring Kobe Bryant MATCH_ID("NDA", SAVE_TYPE_FLASHRAM, FEAT_CPAK), // Derby Stallion 64 MATCH_ID("NDP", SAVE_TYPE_FLASHRAM, FEAT_EXP_PAK_REQUIRED), // Dinosaur Planet (Unlicensed) @@ -356,6 +377,7 @@ static const match_t database[] = { MATCH_ID("NP3", SAVE_TYPE_FLASHRAM_PKST2, FEAT_TPAK), // Pokemon Stadium 2 [Pocket Monsters Stadium - Kin Gin (J)] + // CONTROLLER PAK / NONE MATCH_ID("N22", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ready 2 Rumble Boxing - Round 2 MATCH_ID("N2M", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football 2002 MATCH_ID("N32", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Army Men - Sarge's Heroes 2 diff --git a/src/menu/usb_comm.c b/src/menu/usb_comm.c index 743ddee2..2cf7cf8a 100644 --- a/src/menu/usb_comm.c +++ b/src/menu/usb_comm.c @@ -71,6 +71,7 @@ static void command_reboot (menu_t *menu) { menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; menu->boot_params->detect_cic_seed = true; + menu->boot_params->cheat_list = NULL; }; static void command_send_file (menu_t *menu) { diff --git a/src/menu/views/file_info.c b/src/menu/views/file_info.c index f4819de8..211f5791 100644 --- a/src/menu/views/file_info.c +++ b/src/menu/views/file_info.c @@ -8,7 +8,7 @@ static const char *n64_rom_extensions[] = { "z64", "n64", "v64", "rom", NULL }; static const char *text_extensions[] = { "txt", NULL }; static const char *config_extensions[] = { "ini", "cfg", "yml", "yaml", "toml", NULL }; static const char *save_extensions[] = { "sav", "eep", "eeprom", "sra", "srm", "ram", "fla", "flashram", NULL }; -static const char *patch_extensions[] = { "ips", "aps", "pps", "xdelta", NULL }; +static const char *patch_extensions[] = { "aps", "bps", "ips", "pps", "ups", "xdelta", NULL }; static const char *archive_extensions[] = { "zip", "rar", "7z", "tar", "gz", NULL }; static const char *image_extensions[] = { "png", "jpg", "gif", NULL }; static const char *music_extensions[] = { "mp3", "wav", "ogg", "wma", "flac", NULL }; diff --git a/src/menu/views/load_disk.c b/src/menu/views/load_disk.c index 986a18b6..7216e97d 100644 --- a/src/menu/views/load_disk.c +++ b/src/menu/views/load_disk.c @@ -136,10 +136,12 @@ static void load (menu_t *menu) { case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break; default: menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; break; } + menu->boot_params->cheat_list = NULL; } else { menu->boot_params->device_type = BOOT_DEVICE_TYPE_64DD; menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC; menu->boot_params->detect_cic_seed = true; + menu->boot_params->cheat_list = NULL; } } diff --git a/src/menu/views/load_emulator.c b/src/menu/views/load_emulator.c index b791aa02..60f8d114 100644 --- a/src/menu/views/load_emulator.c +++ b/src/menu/views/load_emulator.c @@ -100,6 +100,7 @@ static void load (menu_t *menu) { menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; menu->boot_params->detect_cic_seed = true; + menu->boot_params->cheat_list = NULL; } diff --git a/src/menu/views/load_rom.c b/src/menu/views/load_rom.c index 3e4e295c..1d4f41be 100644 --- a/src/menu/views/load_rom.c +++ b/src/menu/views/load_rom.c @@ -310,6 +310,7 @@ static void load (menu_t *menu) { case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break; default: menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; break; } + menu->boot_params->cheat_list = NULL; } static void deinit (void) {