Merge branch 'develop' into add-rom-patcher

This commit is contained in:
Robin Jones 2024-08-06 20:11:07 +01:00 committed by GitHub
commit f505135c97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 1157 additions and 705 deletions

View File

@ -21,6 +21,7 @@
- [ ] Improvement (non-breaking change that adds a new feature) - [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue) - [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change) - [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [ ] Config and build (change in the configuration and build system, has no impact on code or features) - [ ] Config and build (change in the configuration and build system, has no impact on code or features)
## Checklist: ## Checklist:

View File

@ -11,6 +11,9 @@ on:
jobs: jobs:
build-menu: build-menu:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -68,21 +71,31 @@ jobs:
name: SC64 name: SC64
path: ./output/sc64menu.n64 path: ./output/sc64menu.n64
# - name: Delete rolling-release tag and release
# uses: dev-drprasad/delete-tag-and-release@v1.0
# if: github.ref == 'refs/heads/main'
# with:
# github_token: ${{ secrets.GITHUB_TOKEN }}
# tag_name: rolling-release
# continue-on-error: true
- name: Upload rolling release - name: Upload rolling release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
with: with:
name: Rolling release name: Rolling release
body: Rolling release built from latest commit on `main` branch. body: Rolling release built from latest commit on `main` branch.
tag_name: rolling-release tag_name: 'rolling_release'
make_latest: true
files: |
./output/N64FlashcartMenu.n64
./output/menu.bin
# ./output/OS64.v64
# ./output/OS64P.v64
./output/sc64menu.n64
continue-on-error: true
- name: Upload dev rolling release
uses: softprops/action-gh-release@v2
if: github.ref == 'refs/heads/develop'
with:
name: 'Rolling pre-release'
body: Experimental pre-release built from latest commit on `develop` branch.
target_commitish: develop
tag_name: 'rolling_pre-release'
prerelease: true
files: | files: |
./output/N64FlashcartMenu.n64 ./output/N64FlashcartMenu.n64
./output/menu.bin ./output/menu.bin

1
.gitignore vendored
View File

@ -7,6 +7,7 @@
# Ignore generated files in the libdragon FS # Ignore generated files in the libdragon FS
/filesystem/FiraMonoBold.font64 /filesystem/FiraMonoBold.font64
/filesystem/*.wav64
# Ignore external development tools # Ignore external development tools
/tools/* /tools/*

View File

@ -905,7 +905,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = README.md ./src INPUT = README.md ./src ./docs
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

View File

@ -79,17 +79,27 @@ SRCS = \
FONTS = \ FONTS = \
FiraMonoBold.ttf FiraMonoBold.ttf
SOUNDS = \
cursorsound.wav \
back.wav \
enter.wav \
error.wav \
settings.wav
OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o,$(basename $(SRCS)))) OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o,$(basename $(SRCS))))
MINIZ_OBJS = $(filter $(BUILD_DIR)/libs/miniz/%.o,$(OBJS)) MINIZ_OBJS = $(filter $(BUILD_DIR)/libs/miniz/%.o,$(OBJS))
SPNG_OBJS = $(filter $(BUILD_DIR)/libs/libspng/%.o,$(OBJS)) SPNG_OBJS = $(filter $(BUILD_DIR)/libs/libspng/%.o,$(OBJS))
DEPS = $(OBJS:.o=.d) DEPS = $(OBJS:.o=.d)
FILESYSTEM = \ FILESYSTEM = \
$(addprefix $(FILESYSTEM_DIR)/, $(notdir $(FONTS:%.ttf=%.font64))) $(addprefix $(FILESYSTEM_DIR)/, $(notdir $(FONTS:%.ttf=%.font64))) \
$(addprefix $(FILESYSTEM_DIR)/, $(notdir $(SOUNDS:%.wav=%.wav64)))
$(MINIZ_OBJS): N64_CFLAGS+=-DMINIZ_NO_TIME -fcompare-debug-second $(MINIZ_OBJS): N64_CFLAGS+=-DMINIZ_NO_TIME -fcompare-debug-second
$(SPNG_OBJS): N64_CFLAGS+=-isystem $(SOURCE_DIR)/libs/miniz -DSPNG_USE_MINIZ -fcompare-debug-second $(SPNG_OBJS): N64_CFLAGS+=-isystem $(SOURCE_DIR)/libs/miniz -DSPNG_USE_MINIZ -fcompare-debug-second
$(FILESYSTEM_DIR)/FiraMonoBold.font64: MKFONT_FLAGS+=-c 1 --size 16 -r 20-1FF -r 2026-2026 --ellipsis 2026,1 $(FILESYSTEM_DIR)/FiraMonoBold.font64: MKFONT_FLAGS+=-c 1 --size 16 -r 20-7F -r 80-1FF -r 2026-2026 --ellipsis 2026,1
$(FILESYSTEM_DIR)/%.wav64: AUDIOCONV_FLAGS=--wav-compress 1
$(@info $(shell mkdir -p ./$(FILESYSTEM_DIR) &> /dev/null)) $(@info $(shell mkdir -p ./$(FILESYSTEM_DIR) &> /dev/null))
@ -97,6 +107,10 @@ $(FILESYSTEM_DIR)/%.font64: $(ASSETS_DIR)/%.ttf
@echo " [FONT] $@" @echo " [FONT] $@"
@$(N64_MKFONT) $(MKFONT_FLAGS) -o $(FILESYSTEM_DIR) "$<" @$(N64_MKFONT) $(MKFONT_FLAGS) -o $(FILESYSTEM_DIR) "$<"
$(FILESYSTEM_DIR)/%.wav64: $(ASSETS_DIR)/%.wav
@echo " [AUDIO] $@"
@$(N64_AUDIOCONV) $(AUDIOCONV_FLAGS) -o $(FILESYSTEM_DIR) "$<"
$(BUILD_DIR)/$(PROJECT_NAME).dfs: $(FILESYSTEM) $(BUILD_DIR)/$(PROJECT_NAME).dfs: $(FILESYSTEM)
$(BUILD_DIR)/menu/views/credits.o: .FORCE $(BUILD_DIR)/menu/views/credits.o: .FORCE

135
README.md
View File

@ -25,9 +25,15 @@ An open source menu for N64 flashcarts.
* Comprehensive ROM information display. * Comprehensive ROM information display.
* Real Time Clock support. * Real Time Clock support.
* Music playback (MP3). * Music playback (MP3).
* Menu sound effects.
### Video showcase (as of Oct 12 2023) ## Documentation
* [Getting started guide](./docs/00_getting_started_sd.md)
* [Menu controls](./docs/01_menu_controls.md)
* [Developer guide](./docs/99_developer_guide.md)
## Video showcase (as of Oct 12 2023)
[![N64FlashcartMenu Showcase](http://img.youtube.com/vi/6CKImHTifDA/0.jpg)](http://www.youtube.com/watch?v=6CKImHTifDA "N64FlashcartMenu Showcase (Oct 12 2023)") [![N64FlashcartMenu Showcase](http://img.youtube.com/vi/6CKImHTifDA/0.jpg)](http://www.youtube.com/watch?v=6CKImHTifDA "N64FlashcartMenu Showcase (Oct 12 2023)")
@ -40,123 +46,60 @@ An open source menu for N64 flashcarts.
* Support as many common mods and features as possible. * Support as many common mods and features as possible.
## Getting started ## Experimental features
Using your PC, insert the SD card and ensure it is formatted for compatibility (We recommend FAT32 in most instances). These features are subject to change:
### Save files
By default, all save files (whether `FlashRam`, `SRAM` or `EEPROM`) use the `.sav` extension and match the filename of the ROM.
Each save file can be found in the `/saves` folder located in the same directory as the ROM and shares the same file name, apart from the extension.
If transfering a file from a different flashcart such as the ED64, it will be necessary to change the extension of the file to `sav`.
i.e. for `Glover (USA).eep` you would need to change the extension to `Glover (USA).sav`
**NOTE:** certain emulator saves or saves created for a different ROM version or region may be incompatible.
### ROM Boxart ### ROM Boxart
To use boxart, you need to place png files of size 158x112 in the folder `/menu/boxart` on the SD card. To use boxart, place PNG files in the `/menu/boxart` folder on the SD card with the following dimensions:
* Standard covers: 158x112
* 64DD covers: 129x112
* Japanese covers: 112x158
Each file must be named according to the 2 letter ROM ID, or 3 letter ROM ID including media type. Each file must be named according to the 2 letter ROM ID, or 3 letter ROM ID including media type.
i.e. for GoldenEye 2 letters, this would be `GE.png`. i.e. for GoldenEye 2 letters, this would be `GE.png`.
i.e. for GoldenEye 3 letters, this would be `NGE.png`. i.e. for GoldenEye 3 letters, this would be `NGE.png`.
A known set of PNG files using 2 letter ID's can be downloaded [here](https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w). You can download these boxart packs:
[American Boxart](https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w)
### Emulator support [European Boxart](https://mega.nz/file/O7AjDbRJ#VnVU10dq8HQvBUQptppI6PAcQMb8-Zembqav8WtAQ_M)
Emulators should be added to the `/menu/emulators` directory on the SD card.
[64DD Boxart](https://mega.nz/file/O3JzwD7B#BYl1aV-pbrJ-MxWUbM_K0yGVIRbmSoxJJZqQInRzZyM)
Menu currently supports the following emulators and associated ROM file names:
- **NES**: [neon64v2](https://github.com/hcs64/neon64v2) by *hcs64* - `neon64bu.rom`
- **SNES**: [sodium64](https://github.com/Hydr8gon/sodium64) by *Hydr8gon* - `sodium64.z64`
- **Game Boy** / **GB Color**: [gb64](https://lambertjamesd.github.io/gb64/romwrapper/romwrapper.html) by *lambertjamesd* - `gb.v64` / `gbc.v64`
- **Sega Master System** / **Sega Game Gear** / **Sg1000**: [TotalSMS](https://github.com/ITotalJustice/TotalSMS) - `TotalSMS.z64` (Currently broken)
### Menu Settings ### Menu Settings
The Menu creates a `config.ini` file in `sd:/menu/` which contains various settings that are used by the menu. The Menu creates a `config.ini` file in `sd:/menu/` which contains various settings that are used by the menu.
Currently these are read-only (can be viewed in the menu by pressing `L` on the Joypad).
If required, you can manually adjust the file on the SD card using your computer. If required, you can manually adjust the file on the SD card using your computer.
### SC64 Specific
- Ensure the cart has the latest [firmware](https://github.com/Polprzewodnikowy/SummerCart64/releases/latest) installed.
- Download the latest `sc64menu.n64` file from the releases page, then put it in the root directory of your SD card.
##### 64DD disk support ## Flashcart specific
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. ### SC64
* Ensure the cart has the latest [firmware](https://github.com/Polprzewodnikowy/SummerCart64/releases/latest) installed.
* Download the latest `sc64menu.n64` file from the [releases](https://github.com/Polprzewodnikowy/N64FlashcartMenu/releases/) page, then put it in the root directory of your SD card.
### 64drive Specific ### 64drive
- Ensure the cart has the latest [firmware](https://64drive.retroactive.be/support.php) installed. * Ensure the cart has the latest [firmware](https://64drive.retroactive.be/support.php) installed.
- Download the latest `menu.bin` file from the releases page, then put it in the root directory of your SD card. * Download the latest `menu.bin` file from the [releases](https://github.com/Polprzewodnikowy/N64FlashcartMenu/releases/) page, then put it in the root directory of your SD card.
### ED64 & ED64P Specific ### ED64 & ED64P
Currently not supported, but work is in progress (See [PR's](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls)). Currently not supported, but work is in progress (See [PR's](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls)).
The aim is to replace [Altra64](https://github.com/networkfusion/altra64) and [ED64-UnofficialOS](https://github.com/n64-tools/ED64-UnofficialOS-binaries). The aim is to replace [Altra64](https://github.com/networkfusion/altra64) and [ED64-UnofficialOS](https://github.com/n64-tools/ED64-UnofficialOS-binaries).
# Developer documentation
You can use a dev container in VSCode to ease development.
## To deploy:
### SC64
* Download the deployer [here](https://github.com/Polprzewodnikowy/SummerCart64/releases/download/v2.18.0/sc64-deployer-windows-v2.18.0.zip)
* Extract and place `sc64deployer.exe` in the `tools/sc64` 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
Set up a proxy: open a terminal window, `cd ./tools/sc64` and then `./sc64deployer.exe server`
Then in the dev container, use `make run` or `make run-debug`
#### From your host (Windows) OS
* Run `./localdeploy.bat` from the terminal
Toggle the N64 power switch to load the ROM.
`ms-vscode.makefile-tools` will help (installed automatically in dev container).
TODO: it does not yet work with `F5`: see https://devblogs.microsoft.com/cppblog/now-announcing-makefile-support-in-visual-studio-code/
WORKAROUND: in the dev container terminal, use make directly, i.e.: `make`
The ROM can be found in the `output` directory.
NOTE: 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 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.
### Others
* Add the required file to the correct folder on your SD card.
# Update Libdragon submodule
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
Run `doxygen` from the dev container terminal.
Make sure you fix the warnings before creating a PR!
Generated documentation is located in `output/docs` folder and auto published to the `gh-pages` branch when merged with `main`.
Once merged, they can be viewed [here](https://polprzewodnikowy.github.io/N64FlashcartMenu/)
# Open source software and licenses used # Open source software and licenses used
- [libdragon](https://github.com/DragonMinded/libdragon) (UNLICENSE License)
- [libspng](https://github.com/randy408/libspng) (BSD 2-Clause License) * [libdragon](https://github.com/DragonMinded/libdragon) (UNLICENSE License)
- [mini.c](https://github.com/univrsal/mini.c) (BSD 2-Clause License) * [libspng](https://github.com/randy408/libspng) (BSD 2-Clause License)
- [minimp3](https://github.com/lieff/minimp3) (CC0 1.0 Universal) * [mini.c](https://github.com/univrsal/mini.c) (BSD 2-Clause License)
- [miniz](https://github.com/richgel999/miniz) (MIT License) * [minimp3](https://github.com/lieff/minimp3) (CC0 1.0 Universal)
* [miniz](https://github.com/richgel999/miniz) (MIT License)
## Sounds
See [License](https://pixabay.com/en/service/license-summary/) for the following sounds:
* [Cursor sound](https://pixabay.com/en/sound-effects/click-buttons-ui-menu-sounds-effects-button-7-203601/) by Skyscraper_seven (Free to use)
* [Actions (Enter, back) sound](https://pixabay.com/en/sound-effects/menu-button-user-interface-pack-190041/) by Liecio (Free to use)
* [Error sound](https://pixabay.com/en/sound-effects/error-call-to-attention-129258/) by Universfield (Free to use)

BIN
assets/back.wav Normal file

Binary file not shown.

BIN
assets/cursorsound.wav Normal file

Binary file not shown.

BIN
assets/enter.wav Normal file

Binary file not shown.

BIN
assets/error.wav Normal file

Binary file not shown.

BIN
assets/settings.wav Normal file

Binary file not shown.

View File

@ -0,0 +1,82 @@
## First time setup of SD card
Using your PC, insert the SD card and ensure it is formatted for compatibility with your flashcart (*FAT32 and EXFAT are fully supported on the SC64*).
- Download the latest `sc64menu.n64` (assuming you are using an *sc64*) file from the [releases](https://github.com/Polprzewodnikowy/N64FlashcartMenu/releases/) page, then put it in the root directory of your SD card.
- Create a folder in the root of your SD card called `menu`.
- Place your ROMs on the SD Card, in any folder (**except for `menu`**).
### Emulator support
Emulators should be added to the `/menu/emulators` directory on the SD card.
Menu currently supports the following emulators and associated ROM file names:
- **NES**: [neon64v2](https://github.com/hcs64/neon64v2/releases) by *hcs64* - `neon64bu.rom`
- **SNES**: [sodium64](https://github.com/Hydr8gon/sodium64/releases) by *Hydr8gon* - `sodium64.z64`
- **Game Boy** / **GB Color**: [gb64](https://lambertjamesd.github.io/gb64/romwrapper/romwrapper.html) by *lambertjamesd* - `gb.v64` / `gbc.v64` ("Download Emulator" button)
### 64DD disk support
For the ability to load and run 64DD disk images, you need to place the 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).
#### So what would the layout of the SD Card look like?
```plaintext
SD:\
├── sc64menu.n64
├── menu\
│ │
│ │
│ ├── 64ddipl\
│ │ ├── NDDE0.n64
│ │ ├── NDDJ2.n64
│ │ └── NDXJ0.n64
│ │
│ └── emulators
│ ├── neon64bu.rom
│ ├── sodium64.z64
│ ├── gb.v64
│ └── gbc.v64
├── (a rom).z64
├── (a rom).n64
├── (some folder with roms)\
│ └── (some folder with roms)\
| └── (Some supported ROM files)
├── (Some supported ROM files)
|
└── (Some folder with 64DD disk images)\
└── (Some 64DD disk images)
```
## Save Files
All save files (whether `FlashRam`, `SRAM` or `EEPROM`) use the `.sav` extension and match the filename of a ROM.
Each save file can be found in the `/saves` folder located in the **same directory** as the ROM and shares the same file name, apart from the extension.
These files are created and modified when a "game" saves.
```plaintext
├── (some folder with roms)\
├── a_rom.z64
├── b_rom.n64
└── saves\
├── a_rom.sav
└── b_rom.sav
```
### Transfering saves from an ED64
If transferring a file from a different flashcart, such as the ED64, it will be necessary to change the extension of the file to `sav`.
i.e. for `Glover (USA).eep` you would need to change the extension to `Glover (USA).sav`
You may also need to pad/trim the files to their original size:
- For EEPROM 4Kbit games, remove the padding.
- For others, use a tool such as Ninjiteu's N64 Save converter.
**NOTE:** certain emulator saves or saves created for a different ROM version or region may be incompatible.

14
docs/01_menu_controls.md Normal file
View File

@ -0,0 +1,14 @@
## Menu Controls
### Fast scroll
Use the C-Up and C-Down buttons
### DD ROMs
#### Expansion Disks
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.
#### Disk swapping
This feature is not currently available in the menu.

View File

@ -0,0 +1,69 @@
## Developer documentation
You can use a dev container in VSCode to ease development.
### A quickstart video tutorial on how to set up your environment
[![Devcontainer quickstart guide](http://img.youtube.com/vi/h05ufOsRgZU/0.jpg)](http://www.youtube.com/watch?v=h05ufOsRgZU "Devcontainer quickstart guide").
### To deploy:
#### SC64
* Download the deployer [here](https://github.com/Polprzewodnikowy/SummerCart64/releases/download/v2.18.0/sc64-deployer-windows-v2.18.0.zip)
* Extract and place `sc64deployer.exe` in the `tools/sc64` 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 workaround you can use a proxy TCP/IP connection
Set up a proxy: open a terminal window, `cd ./tools/sc64` and then `./sc64deployer.exe server`
Then in the dev container, use `make run` or `make run-debug`
##### From your host (Windows) OS
* Run `./localdeploy.bat` from the terminal
Toggle the N64 power switch to load the ROM.
`ms-vscode.makefile-tools` will help (installed automatically in dev container).
NOTE: it does not yet work with `F5`: see [this blog post](https://devblogs.microsoft.com/cppblog/now-announcing-makefile-support-in-visual-studio-code/)
WORKAROUND: in the dev container terminal, use make directly, i.e.: `make`
The ROM can be found in the `output` directory.
NOTE: 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 SDCard. This is generated by running `make all` or running `make sc64`.
#### Ares Emulator
For ease of development and debugging, the menu ROM can run in the [Ares emulator](https://ares-emu.net/) (without most flashcart features).
* Ensure you have the Ares emulator on your computer.
* Load the `N64FlashcartMenu.n64` ROM.
#### Others
* Add the required file to the correct folder on your SD card.
## Update Libdragon submodule
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
Run `doxygen` from the dev container terminal.
Make sure you fix the warnings before creating a PR!
Generated documentation is located in the `output/docs` folder and auto-published to the `gh-pages` branch when merged with `main`.
Once merged, they can be viewed [here](https://polprzewodnikowy.github.io/N64FlashcartMenu/)
### Test generated docs in the dev-container
Install Prerequisites:
```bash
apt-get install ruby-full build-essential zlib1g-dev
gem install jekyll bundler
```
You can then serve the webpage:
```bash
cd output/docs && jekyll serve
```

@ -1 +1 @@
Subproject commit af650428e9615f4e08d8e7eae187929a90c15ccc Subproject commit 9bae49994bf1c796f9939ea1aa7c133813b9053f

View File

@ -170,7 +170,7 @@ static flashcart_err_t d64_load_save (char *save_path) {
size_t save_size = f_size(&fil); size_t save_size = f_size(&fil);
bool is_eeprom_save = (current_save_type == SAVE_TYPE_EEPROM_4K || current_save_type == SAVE_TYPE_EEPROM_16K); bool is_eeprom_save = (current_save_type == SAVE_TYPE_EEPROM_4KBIT || current_save_type == SAVE_TYPE_EEPROM_16KBIT);
void *address = (void *) (SAVE_ADDRESS_DEV_B); void *address = (void *) (SAVE_ADDRESS_DEV_B);
if (is_eeprom_save) { if (is_eeprom_save) {
@ -212,27 +212,27 @@ static flashcart_err_t d64_set_save_type (flashcart_save_type_t save_type) {
case FLASHCART_SAVE_TYPE_NONE: case FLASHCART_SAVE_TYPE_NONE:
type = SAVE_TYPE_NONE; type = SAVE_TYPE_NONE;
break; break;
case FLASHCART_SAVE_TYPE_EEPROM_4K: case FLASHCART_SAVE_TYPE_EEPROM_4KBIT:
type = SAVE_TYPE_EEPROM_4K; type = SAVE_TYPE_EEPROM_4KBIT;
break; break;
case FLASHCART_SAVE_TYPE_EEPROM_16K: case FLASHCART_SAVE_TYPE_EEPROM_16KBIT:
type = SAVE_TYPE_EEPROM_16K; type = SAVE_TYPE_EEPROM_16KBIT;
break; break;
case FLASHCART_SAVE_TYPE_SRAM: case FLASHCART_SAVE_TYPE_SRAM_256KBIT:
type = SAVE_TYPE_SRAM; type = SAVE_TYPE_SRAM_256KBIT;
break; break;
case FLASHCART_SAVE_TYPE_SRAM_BANKED: case FLASHCART_SAVE_TYPE_SRAM_BANKED:
type = SAVE_TYPE_SRAM_BANKED; type = SAVE_TYPE_SRAM_BANKED;
break; break;
case FLASHCART_SAVE_TYPE_SRAM_128K: case FLASHCART_SAVE_TYPE_SRAM_1MBIT:
// NOTE: 64drive doesn't support 128 kiB SRAM save type, fallback to 32 kiB SRAM save type // NOTE: 64drive doesn't support 128 kiB SRAM save type, fallback to 32 kiB SRAM save type
type = SAVE_TYPE_SRAM; type = SAVE_TYPE_SRAM_256KBIT;
break; break;
case FLASHCART_SAVE_TYPE_FLASHRAM: case FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT:
type = SAVE_TYPE_FLASHRAM; type = SAVE_TYPE_FLASHRAM_1MBIT;
break; break;
case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2: case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2:
type = (device_variant == DEVICE_VARIANT_A) ? SAVE_TYPE_FLASHRAM_PKST2 : SAVE_TYPE_FLASHRAM; type = (device_variant == DEVICE_VARIANT_A) ? SAVE_TYPE_FLASHRAM_PKST2 : SAVE_TYPE_FLASHRAM_1MBIT;
break; break;
default: default:
return FLASHCART_ERR_ARGS; return FLASHCART_ERR_ARGS;

View File

@ -79,10 +79,10 @@ typedef enum {
/** @brief Save Type Enumeration. */ /** @brief Save Type Enumeration. */
typedef enum { typedef enum {
SAVE_TYPE_NONE, SAVE_TYPE_NONE,
SAVE_TYPE_EEPROM_4K, SAVE_TYPE_EEPROM_4KBIT,
SAVE_TYPE_EEPROM_16K, SAVE_TYPE_EEPROM_16KBIT,
SAVE_TYPE_SRAM, SAVE_TYPE_SRAM_256KBIT,
SAVE_TYPE_FLASHRAM, SAVE_TYPE_FLASHRAM_1MBIT,
SAVE_TYPE_SRAM_BANKED, SAVE_TYPE_SRAM_BANKED,
SAVE_TYPE_FLASHRAM_PKST2, SAVE_TYPE_FLASHRAM_PKST2,
} d64_save_type_t; } d64_save_type_t;

View File

@ -34,12 +34,12 @@ typedef enum {
/** @brief Flashcart save type enumeration */ /** @brief Flashcart save type enumeration */
typedef enum { typedef enum {
FLASHCART_SAVE_TYPE_NONE, FLASHCART_SAVE_TYPE_NONE,
FLASHCART_SAVE_TYPE_EEPROM_4K, FLASHCART_SAVE_TYPE_EEPROM_4KBIT,
FLASHCART_SAVE_TYPE_EEPROM_16K, FLASHCART_SAVE_TYPE_EEPROM_16KBIT,
FLASHCART_SAVE_TYPE_SRAM, FLASHCART_SAVE_TYPE_SRAM_256KBIT,
FLASHCART_SAVE_TYPE_SRAM_BANKED, FLASHCART_SAVE_TYPE_SRAM_BANKED,
FLASHCART_SAVE_TYPE_SRAM_128K, FLASHCART_SAVE_TYPE_SRAM_1MBIT,
FLASHCART_SAVE_TYPE_FLASHRAM, FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT,
FLASHCART_SAVE_TYPE_FLASHRAM_PKST2, FLASHCART_SAVE_TYPE_FLASHRAM_PKST2,
__FLASHCART_SAVE_TYPE_END __FLASHCART_SAVE_TYPE_END
} flashcart_save_type_t; } flashcart_save_type_t;

View File

@ -51,7 +51,7 @@ bool fatfs_get_file_sectors (char *path, uint32_t *address, address_type_t type,
uint32_t cluster = fil.clust; uint32_t cluster = fil.clust;
if (cluster >= fs->n_fatent) { if ((cluster < 2) || (cluster >= fs->n_fatent)) {
error = true; error = true;
break; break;
} }

View File

@ -388,12 +388,12 @@ static flashcart_err_t sc64_load_save (char *save_path) {
sc64_save_type_t type = (sc64_save_type_t) (value); sc64_save_type_t type = (sc64_save_type_t) (value);
switch (type) { switch (type) {
case SAVE_TYPE_EEPROM_4K: case SAVE_TYPE_EEPROM_4KBIT:
case SAVE_TYPE_EEPROM_16K: case SAVE_TYPE_EEPROM_16KBIT:
address = (void *) (EEPROM_ADDRESS); address = (void *) (EEPROM_ADDRESS);
break; break;
case SAVE_TYPE_SRAM: case SAVE_TYPE_SRAM_256KBIT:
case SAVE_TYPE_FLASHRAM: case SAVE_TYPE_FLASHRAM_1MBIT:
case SAVE_TYPE_SRAM_BANKED: case SAVE_TYPE_SRAM_BANKED:
address = (void *) (SRAM_FLASHRAM_ADDRESS); address = (void *) (SRAM_FLASHRAM_ADDRESS);
break; break;
@ -515,26 +515,26 @@ static flashcart_err_t sc64_set_save_type (flashcart_save_type_t save_type) {
case FLASHCART_SAVE_TYPE_NONE: case FLASHCART_SAVE_TYPE_NONE:
type = SAVE_TYPE_NONE; type = SAVE_TYPE_NONE;
break; break;
case FLASHCART_SAVE_TYPE_EEPROM_4K: case FLASHCART_SAVE_TYPE_EEPROM_4KBIT:
type = SAVE_TYPE_EEPROM_4K; type = SAVE_TYPE_EEPROM_4KBIT;
break; break;
case FLASHCART_SAVE_TYPE_EEPROM_16K: case FLASHCART_SAVE_TYPE_EEPROM_16KBIT:
type = SAVE_TYPE_EEPROM_16K; type = SAVE_TYPE_EEPROM_16KBIT;
break; break;
case FLASHCART_SAVE_TYPE_SRAM: case FLASHCART_SAVE_TYPE_SRAM_256KBIT:
type = SAVE_TYPE_SRAM; type = SAVE_TYPE_SRAM_256KBIT;
break; break;
case FLASHCART_SAVE_TYPE_SRAM_BANKED: case FLASHCART_SAVE_TYPE_SRAM_BANKED:
type = SAVE_TYPE_SRAM_BANKED; type = SAVE_TYPE_SRAM_BANKED;
break; break;
case FLASHCART_SAVE_TYPE_SRAM_128K: case FLASHCART_SAVE_TYPE_SRAM_1MBIT:
type = SAVE_TYPE_SRAM_128K; type = SAVE_TYPE_SRAM_1MBIT;
break; break;
case FLASHCART_SAVE_TYPE_FLASHRAM: case FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT:
type = SAVE_TYPE_FLASHRAM; type = SAVE_TYPE_FLASHRAM_1MBIT;
break; break;
case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2: case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2:
type = SAVE_TYPE_FLASHRAM; type = SAVE_TYPE_FLASHRAM_1MBIT;
break; break;
default: default:
return FLASHCART_ERR_ARGS; return FLASHCART_ERR_ARGS;

View File

@ -76,12 +76,12 @@ typedef enum {
/** @brief The SC64 Save Type Enumeration. */ /** @brief The SC64 Save Type Enumeration. */
typedef enum { typedef enum {
SAVE_TYPE_NONE, SAVE_TYPE_NONE,
SAVE_TYPE_EEPROM_4K, SAVE_TYPE_EEPROM_4KBIT,
SAVE_TYPE_EEPROM_16K, SAVE_TYPE_EEPROM_16KBIT,
SAVE_TYPE_SRAM, SAVE_TYPE_SRAM_256KBIT,
SAVE_TYPE_FLASHRAM, SAVE_TYPE_FLASHRAM_1MBIT,
SAVE_TYPE_SRAM_BANKED, SAVE_TYPE_SRAM_BANKED,
SAVE_TYPE_SRAM_128K, SAVE_TYPE_SRAM_1MBIT,
} sc64_save_type_t; } sc64_save_type_t;
typedef enum { typedef enum {

View File

@ -21,6 +21,7 @@ static void actions_clear (menu_t *menu) {
menu->actions.back = false; menu->actions.back = false;
menu->actions.options = false; menu->actions.options = false;
menu->actions.settings = false; menu->actions.settings = false;
menu->actions.lz_context = false;
} }
static void actions_update_direction (menu_t *menu) { static void actions_update_direction (menu_t *menu) {
@ -91,6 +92,8 @@ static void actions_update_buttons (menu_t *menu) {
menu->actions.options = true; menu->actions.options = true;
} else if (pressed.start) { } else if (pressed.start) {
menu->actions.settings = true; menu->actions.settings = true;
} else if (pressed.l || pressed.z) {
menu->actions.lz_context = true;
} }
} }

View File

@ -35,12 +35,12 @@ static bool create_saves_subdirectory (path_t *path) {
static flashcart_save_type_t convert_save_type (rom_save_type_t save_type) { static flashcart_save_type_t convert_save_type (rom_save_type_t save_type) {
switch (save_type) { switch (save_type) {
case SAVE_TYPE_EEPROM_4K: return FLASHCART_SAVE_TYPE_EEPROM_4K; case SAVE_TYPE_EEPROM_4KBIT: return FLASHCART_SAVE_TYPE_EEPROM_4KBIT;
case SAVE_TYPE_EEPROM_16K: return FLASHCART_SAVE_TYPE_EEPROM_16K; case SAVE_TYPE_EEPROM_16KBIT: return FLASHCART_SAVE_TYPE_EEPROM_16KBIT;
case SAVE_TYPE_SRAM: return FLASHCART_SAVE_TYPE_SRAM; case SAVE_TYPE_SRAM_256KBIT: return FLASHCART_SAVE_TYPE_SRAM_256KBIT;
case SAVE_TYPE_SRAM_BANKED: return FLASHCART_SAVE_TYPE_SRAM_BANKED; case SAVE_TYPE_SRAM_BANKED: return FLASHCART_SAVE_TYPE_SRAM_BANKED;
case SAVE_TYPE_SRAM_128K: return FLASHCART_SAVE_TYPE_SRAM_128K; case SAVE_TYPE_SRAM_1MBIT: return FLASHCART_SAVE_TYPE_SRAM_1MBIT;
case SAVE_TYPE_FLASHRAM: return FLASHCART_SAVE_TYPE_FLASHRAM; case SAVE_TYPE_FLASHRAM_1MBIT: return FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT;
case SAVE_TYPE_FLASHRAM_PKST2: return FLASHCART_SAVE_TYPE_FLASHRAM_PKST2; case SAVE_TYPE_FLASHRAM_PKST2: return FLASHCART_SAVE_TYPE_FLASHRAM_PKST2;
default: return FLASHCART_SAVE_TYPE_NONE; default: return FLASHCART_SAVE_TYPE_NONE;
} }
@ -166,19 +166,19 @@ cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type,
break; break;
case CART_LOAD_EMU_TYPE_SNES: case CART_LOAD_EMU_TYPE_SNES:
path_push(path, "sodium64.z64"); path_push(path, "sodium64.z64");
save_type = FLASHCART_SAVE_TYPE_SRAM; save_type = FLASHCART_SAVE_TYPE_SRAM_256KBIT;
break; break;
case CART_LOAD_EMU_TYPE_GAMEBOY: case CART_LOAD_EMU_TYPE_GAMEBOY:
path_push(path, "gb.v64"); path_push(path, "gb.v64");
save_type = FLASHCART_SAVE_TYPE_FLASHRAM; save_type = FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT;
break; break;
case CART_LOAD_EMU_TYPE_GAMEBOY_COLOR: case CART_LOAD_EMU_TYPE_GAMEBOY_COLOR:
path_push(path, "gbc.v64"); path_push(path, "gbc.v64");
save_type = FLASHCART_SAVE_TYPE_FLASHRAM; save_type = FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT;
break; break;
case CART_LOAD_EMU_TYPE_SEGA_GENERIC_8BIT: case CART_LOAD_EMU_TYPE_SEGA_GENERIC_8BIT:
path_push(path, "TotalSMS.z64"); path_push(path, "TotalSMS.z64");
save_type = FLASHCART_SAVE_TYPE_SRAM; save_type = FLASHCART_SAVE_TYPE_SRAM_256KBIT;
break; break;
} }

View File

@ -31,7 +31,7 @@ component_boxart_t *component_boxart_init (const char *storage_prefix, char *gam
sprintf(file_name, "%.3s.png", game_code); sprintf(file_name, "%.3s.png", game_code);
path_push(path, file_name); path_push(path, file_name);
if (png_decoder_start(path_get(path), BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) == PNG_OK) { if (png_decoder_start(path_get(path), BOXART_WIDTH_MAX, BOXART_HEIGHT_MAX, png_decoder_callback, b) == PNG_OK) {
path_free(path); path_free(path);
return b; return b;
} }
@ -40,7 +40,7 @@ component_boxart_t *component_boxart_init (const char *storage_prefix, char *gam
// TODO: This is bad, we should only check for 3 letter codes // TODO: This is bad, we should only check for 3 letter codes
sprintf(file_name, "%.2s.png", game_code + 1); sprintf(file_name, "%.2s.png", game_code + 1);
path_push(path, file_name); path_push(path, file_name);
if (png_decoder_start(path_get(path), BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) == PNG_OK) { if (png_decoder_start(path_get(path), BOXART_WIDTH_MAX, BOXART_HEIGHT_MAX, png_decoder_callback, b) == PNG_OK) {
path_free(path); path_free(path);
return b; return b;
} }
@ -65,15 +65,20 @@ void component_boxart_free (component_boxart_t *b) {
} }
void component_boxart_draw (component_boxart_t *b) { void component_boxart_draw (component_boxart_t *b) {
if (b && b->image && b->image->width == BOXART_WIDTH && b->image->height == BOXART_HEIGHT) { int box_x = BOXART_X;
int box_y = BOXART_Y;
if (b && b->image && b->image->width <= BOXART_WIDTH_MAX && b->image->height <= BOXART_HEIGHT_MAX) {
rdpq_mode_push(); rdpq_mode_push();
rdpq_set_mode_copy(false); rdpq_set_mode_copy(false);
rdpq_tex_blit( if (b->image->height == BOXART_HEIGHT_MAX) {
b->image, box_x = BOXART_X_JP;
BOXART_X, box_y = BOXART_Y_JP;
BOXART_Y, } else if (b->image->width == BOXART_WIDTH_DD && b->image->height == BOXART_HEIGHT_DD) {
NULL box_x = BOXART_X_DD;
); box_y = BOXART_Y_DD;
}
rdpq_tex_blit(b->image, box_x, box_y, NULL);
rdpq_mode_pop(); rdpq_mode_pop();
} else { } else {
component_box_draw( component_box_draw(

View File

@ -118,7 +118,8 @@ void component_messagebox_draw (char *fmt, ...) {
.height = VISIBLE_AREA_HEIGHT, .height = VISIBLE_AREA_HEIGHT,
.align = ALIGN_CENTER, .align = ALIGN_CENTER,
.valign = VALIGN_CENTER, .valign = VALIGN_CENTER,
.wrap = WRAP_WORD .wrap = WRAP_WORD,
.line_spacing = TEXT_LINE_SPACING_ADJUST,
}, FNT_DEFAULT, formatted, &paragraph_nbytes); }, FNT_DEFAULT, formatted, &paragraph_nbytes);
if (formatted != buffer) { if (formatted != buffer) {
@ -151,10 +152,11 @@ void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *f
.align = align, .align = align,
.valign = valign, .valign = valign,
.wrap = WRAP_ELLIPSES, .wrap = WRAP_ELLIPSES,
.line_spacing = TEXT_LINE_SPACING_ADJUST,
}, },
FNT_DEFAULT, FNT_DEFAULT,
VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL, VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL,
VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL, VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + TEXT_OFFSET_VERTICAL,
formatted, formatted,
nbytes nbytes
); );
@ -180,10 +182,11 @@ void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign,
.align = align, .align = align,
.valign = valign, .valign = valign,
.wrap = WRAP_ELLIPSES, .wrap = WRAP_ELLIPSES,
.line_spacing = TEXT_LINE_SPACING_ADJUST,
}, },
FNT_DEFAULT, FNT_DEFAULT,
VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL, VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL,
LAYOUT_ACTIONS_SEPARATOR_Y + BORDER_THICKNESS + TEXT_MARGIN_VERTICAL, LAYOUT_ACTIONS_SEPARATOR_Y + BORDER_THICKNESS + TEXT_MARGIN_VERTICAL + TEXT_OFFSET_VERTICAL,
formatted, formatted,
nbytes nbytes
); );

View File

@ -65,16 +65,38 @@
#define MESSAGEBOX_MARGIN (32) #define MESSAGEBOX_MARGIN (32)
#define TEXT_MARGIN_HORIZONTAL (10) #define TEXT_MARGIN_HORIZONTAL (10)
#define TEXT_MARGIN_VERTICAL (7) #define TEXT_MARGIN_VERTICAL (6)
#define TEXT_OFFSET_VERTICAL (1)
#define TEXT_LINE_SPACING_ADJUST (0)
/** @brief The boxart picture width. */ /** @brief The boxart picture width. */
#define BOXART_WIDTH (158) #define BOXART_WIDTH (158)
/** @brief The boxart picture height. */ /** @brief The boxart picture height. */
#define BOXART_HEIGHT (112) #define BOXART_HEIGHT (112)
/** @brief The boxart picture width (64DD). */
#define BOXART_WIDTH_DD (129)
/** @brief The boxart picture height. */
#define BOXART_HEIGHT_DD (112)
/** @brief The boxart picture maximum width. */
#define BOXART_WIDTH_MAX (158)
/** @brief The boxart picture maximum height. */
#define BOXART_HEIGHT_MAX (158)
/** @brief The box art position on the X axis. */ /** @brief The box art position on the X axis. */
#define BOXART_X (VISIBLE_AREA_X1 - BOXART_WIDTH - 24) #define BOXART_X (VISIBLE_AREA_X1 - BOXART_WIDTH - 24)
/** @brief The box art position on the Y axis. */ /** @brief The box art position on the Y axis. */
#define BOXART_Y (LAYOUT_ACTIONS_SEPARATOR_Y - BOXART_HEIGHT - 24) #define BOXART_Y (LAYOUT_ACTIONS_SEPARATOR_Y - BOXART_HEIGHT - 24)
/** @brief The box art position on the X axis for japanese caratules.*/
#define BOXART_X_JP (VISIBLE_AREA_X1 - BOXART_WIDTH_MAX + 21)
/** @brief The box art position on the Y axis for japanese caratules. */
#define BOXART_Y_JP (LAYOUT_ACTIONS_SEPARATOR_Y - BOXART_HEIGHT_MAX - 24)
/** @brief The box art position on the X axis for 64DD caratules.*/
#define BOXART_X_DD (VISIBLE_AREA_X1 - BOXART_WIDTH_DD - 23)
/** @brief The box art position on the Y axis for 64DD caratules. */
#define BOXART_Y_DD (LAYOUT_ACTIONS_SEPARATOR_Y - BOXART_HEIGHT_DD - 24)
/** @brief The scroll bar width. */ /** @brief The scroll bar width. */
#define LIST_SCROLLBAR_WIDTH (12) #define LIST_SCROLLBAR_WIDTH (12)
@ -86,7 +108,7 @@
#define LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0) #define LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0)
/** @brief The maximum amount of file list entries. */ /** @brief The maximum amount of file list entries. */
#define LIST_ENTRIES (20) #define LIST_ENTRIES (19)
/** @brief The maximum width available for a file list entry. */ /** @brief The maximum width available for a file list entry. */
#define FILE_LIST_MAX_WIDTH (480) #define FILE_LIST_MAX_WIDTH (480)
#define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - LIST_SCROLLBAR_WIDTH) #define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - LIST_SCROLLBAR_WIDTH)

View File

@ -1,5 +1,6 @@
#include "../components.h" #include "../components.h"
#include "../fonts.h" #include "../fonts.h"
#include "../sound.h"
#include "constants.h" #include "constants.h"
@ -41,6 +42,7 @@ bool component_context_menu_process (menu_t *menu, component_context_menu_t *cm)
} else { } else {
cm->hide_pending = true; cm->hide_pending = true;
} }
sound_play_effect(SFX_EXIT);
} else if (menu->actions.enter) { } else if (menu->actions.enter) {
if (cm->list[cm->selected].submenu) { if (cm->list[cm->selected].submenu) {
cm->submenu = cm->list[cm->selected].submenu; cm->submenu = cm->list[cm->selected].submenu;
@ -51,16 +53,19 @@ bool component_context_menu_process (menu_t *menu, component_context_menu_t *cm)
cm->list[cm->selected].action(menu, cm->list[cm->selected].arg); cm->list[cm->selected].action(menu, cm->list[cm->selected].arg);
top->hide_pending = true; top->hide_pending = true;
} }
sound_play_effect(SFX_ENTER);
} else if (menu->actions.go_up) { } else if (menu->actions.go_up) {
cm->selected -= 1; cm->selected -= 1;
if (cm->selected < 0) { if (cm->selected < 0) {
cm->selected = 0; cm->selected = 0;
} }
sound_play_effect(SFX_CURSOR);
} else if (menu->actions.go_down) { } else if (menu->actions.go_down) {
cm->selected += 1; cm->selected += 1;
if (cm->selected >= cm->count) { if (cm->selected >= cm->count) {
cm->selected = (cm->count - 1); cm->selected = (cm->count - 1);
} }
sound_play_effect(SFX_CURSOR);
} }
return true; return true;
@ -81,6 +86,7 @@ void component_context_menu_draw (component_context_menu_t *cm) {
.height = VISIBLE_AREA_HEIGHT, .height = VISIBLE_AREA_HEIGHT,
.align = ALIGN_CENTER, .align = ALIGN_CENTER,
.valign = VALIGN_CENTER, .valign = VALIGN_CENTER,
.line_spacing = TEXT_LINE_SPACING_ADJUST,
}, },
FNT_DEFAULT, FNT_DEFAULT,
NULL NULL

View File

@ -71,6 +71,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) {
.width = FILE_LIST_MAX_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2), .width = FILE_LIST_MAX_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2),
.height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2), .height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2),
.wrap = WRAP_ELLIPSES, .wrap = WRAP_ELLIPSES,
.line_spacing = TEXT_LINE_SPACING_ADJUST,
}, },
FNT_DEFAULT, FNT_DEFAULT,
file_list_layout file_list_layout
@ -114,7 +115,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) {
layout = rdpq_paragraph_builder_end(); layout = rdpq_paragraph_builder_end();
int highlight_height = (layout->bbox.y1 - layout->bbox.y0) / layout->nlines; int highlight_height = (layout->bbox.y1 - layout->bbox.y0) / layout->nlines;
int highlight_y = VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + ((selected - starting_position) * highlight_height); int highlight_y = VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + TEXT_OFFSET_VERTICAL + ((selected - starting_position) * highlight_height);
component_box_draw( component_box_draw(
FILE_LIST_HIGHLIGHT_X, FILE_LIST_HIGHLIGHT_X,
@ -127,7 +128,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) {
rdpq_paragraph_render( rdpq_paragraph_render(
layout, layout,
VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL, VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL,
VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + TEXT_OFFSET_VERTICAL
); );
rdpq_paragraph_free(layout); rdpq_paragraph_free(layout);
@ -138,6 +139,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) {
.height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2), .height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2),
.align = ALIGN_RIGHT, .align = ALIGN_RIGHT,
.wrap = WRAP_ELLIPSES, .wrap = WRAP_ELLIPSES,
.line_spacing = TEXT_LINE_SPACING_ADJUST,
}, },
FNT_DEFAULT, FNT_DEFAULT,
NULL NULL
@ -164,7 +166,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) {
rdpq_paragraph_render( rdpq_paragraph_render(
layout, layout,
VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL, VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL,
VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + TEXT_OFFSET_VERTICAL
); );
rdpq_paragraph_free(layout); rdpq_paragraph_free(layout);

View File

@ -67,6 +67,10 @@ static void menu_init (boot_params_t *boot_params) {
sound_init_default(); sound_init_default();
JOYPAD_PORT_FOREACH (port) {
joypad_set_rumble_active(port, false);
}
menu = calloc(1, sizeof(menu_t)); menu = calloc(1, sizeof(menu_t));
assert(menu != NULL); assert(menu != NULL);
@ -115,6 +119,11 @@ static void menu_init (boot_params_t *boot_params) {
__boot_tvtype = TV_NTSC; __boot_tvtype = TV_NTSC;
} }
sound_init_sfx();
if (menu->settings.sound_enabled) {
sound_use_sfx(true);
}
display_init(RESOLUTION_640x480, DEPTH_16_BPP, 2, GAMMA_NONE, FILTERS_DISABLED); display_init(RESOLUTION_640x480, DEPTH_16_BPP, 2, GAMMA_NONE, FILTERS_DISABLED);
register_VI_handler(frame_counter_handler); register_VI_handler(frame_counter_handler);

View File

@ -88,6 +88,7 @@ typedef struct {
bool back; bool back;
bool options; bool options;
bool settings; bool settings;
bool lz_context;
} actions; } actions;
struct { struct {

View File

@ -1,3 +1,4 @@
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -144,459 +145,459 @@ typedef struct {
// clang-format off // clang-format off
static const match_t database[] = { static const match_t database[] = {
MATCH_HOMEBREW_HEADER("ED"), // Homebrew header (ED) 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(0x000000004CBC3B56, SAVE_TYPE_SRAM_256KBIT, 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(0x0DD4ABABB5A2A91E, SAVE_TYPE_EEPROM_16KBIT, FEAT_EXP_PAK_REQUIRED), // DK Retail kiosk demo
MATCH_CHECK_CODE(0xEB85EBC9596682AF, SAVE_TYPE_FLASHRAM, FEAT_NONE), // Doubutsu Banchou MATCH_CHECK_CODE(0xEB85EBC9596682AF, SAVE_TYPE_FLASHRAM_1MBIT, FEAT_NONE), // Doubutsu Banchou
MATCH_CHECK_CODE(0x9A746EBF2802EA99, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Toon panic MATCH_CHECK_CODE(0x9A746EBF2802EA99, SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Toon panic
MATCH_CHECK_CODE(0x21548CA921548CA9, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Mini racers MATCH_CHECK_CODE(0x21548CA921548CA9, SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Mini racers
MATCH_CHECK_CODE(0xBC9B2CC34ED04DA5, SAVE_TYPE_FLASHRAM, FEAT_NONE), // Starcraft 64 [Prototype 2000] MATCH_CHECK_CODE(0xBC9B2CC34ED04DA5, SAVE_TYPE_FLASHRAM_1MBIT, FEAT_NONE), // Starcraft 64 [Prototype 2000]
MATCH_CHECK_CODE(0x5D40ED2C10D6ABCF, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Viewpoint 2064 MATCH_CHECK_CODE(0x5D40ED2C10D6ABCF, SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Viewpoint 2064
MATCH_CHECK_CODE(0x7280E03F497689BA, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Viewpoint 2064 [ENG patch] MATCH_CHECK_CODE(0x7280E03F497689BA, SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Viewpoint 2064 [ENG patch]
MATCH_CHECK_CODE(0xCDB8B4D08832352D, SAVE_TYPE_SRAM, FEAT_RPAK), // Jet Force Gemini [USA CRACK] MATCH_CHECK_CODE(0xCDB8B4D08832352D, SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // Jet Force Gemini [USA CRACK]
MATCH_CHECK_CODE(0xB66E0F7C2709C22F, SAVE_TYPE_SRAM, FEAT_RPAK), // Jet Force Gemini [PAL CRACK] MATCH_CHECK_CODE(0xB66E0F7C2709C22F, SAVE_TYPE_SRAM_256KBIT, 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(0xCE84793D27ECC1AD, SAVE_TYPE_SRAM_256KBIT, 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(0x1F95CAAA047FC22A, SAVE_TYPE_SRAM_256KBIT, 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_CHECK_CODE(0xE3FF09DFCAE4B0ED, SAVE_TYPE_SRAM_256KBIT, 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", 0, SAVE_TYPE_SRAM_256KBIT, 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_REGION_VERSION("NK4J", 1, SAVE_TYPE_SRAM_256KBIT, 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("NK4", SAVE_TYPE_EEPROM_4KBIT, 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_REGION_VERSION("NSMJ", 3, SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Super Mario 64 Shindou Edition
MATCH_ID("NSM", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Super Mario 64 MATCH_ID("NSM", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Super Mario 64
MATCH_ID_REGION_VERSION("NWRJ", 2, SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Wave Race 64 Shindou Edition MATCH_ID_REGION_VERSION("NWRJ", 2, SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Wave Race 64 Shindou Edition
MATCH_ID("NWR", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Wave Race 64 MATCH_ID("NWR", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Wave Race 64
MATCH_ID_REGION("N3HJ", SAVE_TYPE_SRAM, FEAT_NONE), // Ganbare! Nippon! Olympics 2000 MATCH_ID_REGION("N3HJ", SAVE_TYPE_SRAM_256KBIT, FEAT_NONE), // Ganbare! Nippon! Olympics 2000
MATCH_ID("N3H", SAVE_TYPE_NONE, FEAT_CPAK), // International Track & Field 2000 MATCH_ID("N3H", SAVE_TYPE_NONE, FEAT_CPAK), // International Track & Field 2000
MATCH_ID_REGION("ND3J", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Akumajou Dracula Mokushiroku (J) MATCH_ID_REGION("ND3J", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Akumajou Dracula Mokushiroku (J)
MATCH_ID("ND3", SAVE_TYPE_NONE, FEAT_CPAK), // Castlevania MATCH_ID("ND3", SAVE_TYPE_NONE, FEAT_CPAK), // Castlevania
MATCH_ID_REGION("ND4J", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Akumajou Dracula Mokushiroku Gaiden: Legend of Cornell (J) MATCH_ID_REGION("ND4J", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Akumajou Dracula Mokushiroku Gaiden: Legend of Cornell (J)
MATCH_ID("ND4", SAVE_TYPE_NONE, FEAT_CPAK), // Castlevania - Legacy of Darkness MATCH_ID("ND4", SAVE_TYPE_NONE, FEAT_CPAK), // Castlevania - Legacy of Darkness
MATCH_ID_REGION("NDKJ", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Dark Rift [Space Dynamites (J)] MATCH_ID_REGION("NDKJ", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Dark Rift [Space Dynamites (J)]
MATCH_ID_REGION("NSVE", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Space Station Silicon Valley MATCH_ID_REGION("NSVE", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Space Station Silicon Valley
MATCH_ID("NSV", SAVE_TYPE_EEPROM_4K, FEAT_RPAK | FEAT_EXP_PAK_BROKEN), // Space Station Silicon Valley MATCH_ID("NSV", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK | FEAT_EXP_PAK_BROKEN), // Space Station Silicon Valley
MATCH_ID_REGION("NWTJ", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Wetrix MATCH_ID_REGION("NWTJ", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Wetrix
MATCH_ID("NWT", SAVE_TYPE_NONE, FEAT_CPAK), // Wetrix MATCH_ID("NWT", SAVE_TYPE_NONE, FEAT_CPAK), // Wetrix
// EEPROM 4K // EEPROM 4K
MATCH_ID("CLB", SAVE_TYPE_EEPROM_4K, FEAT_RPAK | FEAT_64DD_ENHANCED), // Mario Party (NTSC) MATCH_ID("CLB", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK | FEAT_64DD_ENHANCED), // Mario Party (NTSC)
MATCH_ID("NAB", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Air Boarder 64 MATCH_ID("NAB", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Air Boarder 64
MATCH_ID("NAD", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Worms Armageddon (U) MATCH_ID("NAD", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Worms Armageddon (U)
MATCH_ID("NAG", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // AeroGauge MATCH_ID("NAG", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // AeroGauge
MATCH_ID("NB6", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_TPAK), // Super B-Daman: Battle Phoenix 64 MATCH_ID("NB6", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_TPAK), // Super B-Daman: Battle Phoenix 64
MATCH_ID("NBC", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Blast Corps MATCH_ID("NBC", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Blast Corps
MATCH_ID("NBD", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Bomberman Hero [Mirian Ojo o Sukue! (J)] MATCH_ID("NBD", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Bomberman Hero [Mirian Ojo o Sukue! (J)]
MATCH_ID("NBH", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Body Harvest MATCH_ID("NBH", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Body Harvest
MATCH_ID("NBK", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Banjo-Kazooie [Banjo to Kazooie no Daiboken (J)] MATCH_ID("NBK", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Banjo-Kazooie [Banjo to Kazooie no Daiboken (J)]
MATCH_ID("NBM", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Bomberman 64 [Baku Bomberman (J)] MATCH_ID("NBM", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Bomberman 64 [Baku Bomberman (J)]
MATCH_ID("NBN", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Bakuretsu Muteki Bangaioh MATCH_ID("NBN", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Bakuretsu Muteki Bangaioh
MATCH_ID("NBV", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Bomberman 64: The Second Attack! [Baku Bomberman 2 (J)] MATCH_ID("NBV", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Bomberman 64: The Second Attack! [Baku Bomberman 2 (J)]
MATCH_ID("NCG", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK | FEAT_TPAK), // Choro Q 64 II - Hacha Mecha Grand Prix Race (J) MATCH_ID("NCG", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK | FEAT_TPAK), // Choro Q 64 II - Hacha Mecha Grand Prix Race (J)
MATCH_ID("NCH", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Chopper Attack MATCH_ID("NCH", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Chopper Attack
MATCH_ID("NCR", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Penny Racers [Choro Q 64 (J)] MATCH_ID("NCR", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Penny Racers [Choro Q 64 (J)]
MATCH_ID("NCT", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Chameleon Twist MATCH_ID("NCT", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Chameleon Twist
MATCH_ID("NCU", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Cruis'n USA MATCH_ID("NCU", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Cruis'n USA
MATCH_ID("NCX", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Custom Robo MATCH_ID("NCX", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Custom Robo
MATCH_ID("NDQ", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Disney's Donald Duck - Goin' Quackers [Quack Attack (E)] MATCH_ID("NDQ", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Disney's Donald Duck - Goin' Quackers [Quack Attack (E)]
MATCH_ID("NDR", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Doraemon: Nobita to 3tsu no Seireiseki MATCH_ID("NDR", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Doraemon: Nobita to 3tsu no Seireiseki
MATCH_ID("NDU", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Duck Dodgers starring Daffy Duck MATCH_ID("NDU", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Duck Dodgers starring Daffy Duck
MATCH_ID("NDY", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Diddy Kong Racing MATCH_ID("NDY", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Diddy Kong Racing
MATCH_ID("NEA", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // PGA European Tour MATCH_ID("NEA", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // PGA European Tour
MATCH_ID("NER", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Aero Fighters Assault [Sonic Wings Assault (J)] MATCH_ID("NER", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Aero Fighters Assault [Sonic Wings Assault (J)]
MATCH_ID("NF2", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // F-1 World Grand Prix II MATCH_ID("NF2", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // F-1 World Grand Prix II
MATCH_ID("NFG", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Fighter Destiny 2 MATCH_ID("NFG", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Fighter Destiny 2
MATCH_ID("NFH", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // In-Fisherman Bass Hunter 64 MATCH_ID("NFH", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // In-Fisherman Bass Hunter 64
MATCH_ID("NFW", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // F-1 World Grand Prix MATCH_ID("NFW", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // F-1 World Grand Prix
MATCH_ID("NFX", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Star Fox 64 [Lylat Wars (E)] MATCH_ID("NFX", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Star Fox 64 [Lylat Wars (E)]
MATCH_ID("NFY", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Kakutou Denshou: F-Cup Maniax MATCH_ID("NFY", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Kakutou Denshou: F-Cup Maniax
MATCH_ID("NGE", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // GoldenEye 007 MATCH_ID("NGE", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // GoldenEye 007
MATCH_ID("NGL", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Getter Love!! MATCH_ID("NGL", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Getter Love!!
MATCH_ID("NGU", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Tsumi to Batsu: Hoshi no Keishousha (Sin and Punishment) MATCH_ID("NGU", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Tsumi to Batsu: Hoshi no Keishousha (Sin and Punishment)
MATCH_ID("NGV", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Glover MATCH_ID("NGV", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Glover
MATCH_ID("NHA", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Bomberman 64: Arcade Edition (J) MATCH_ID("NHA", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Bomberman 64: Arcade Edition (J)
MATCH_ID("NHF", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // 64 Hanafuda: Tenshi no Yakusoku MATCH_ID("NHF", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // 64 Hanafuda: Tenshi no Yakusoku
MATCH_ID("NHP", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Heiwa Pachinko World 64 MATCH_ID("NHP", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Heiwa Pachinko World 64
MATCH_ID("NIC", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Indy Racing 2000 MATCH_ID("NIC", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Indy Racing 2000
MATCH_ID("NIJ", SAVE_TYPE_EEPROM_4K, FEAT_RPAK | FEAT_EXP_PAK_RECOMMENDED), // Indiana Jones and the Infernal Machine MATCH_ID("NIJ", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK | FEAT_EXP_PAK_RECOMMENDED), // Indiana Jones and the Infernal Machine
MATCH_ID("NIR", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Utchan Nanchan no Hono no Challenger: Denryuu Ira Ira Bou MATCH_ID("NIR", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Utchan Nanchan no Hono no Challenger: Denryuu Ira Ira Bou
MATCH_ID("NJM", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Earthworm Jim 3D MATCH_ID("NJM", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Earthworm Jim 3D
MATCH_ID("NK2", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Snowboard Kids 2 [Chou Snobow Kids (J)] MATCH_ID("NK2", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Snowboard Kids 2 [Chou Snobow Kids (J)]
MATCH_ID("NKA", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Fighters Destiny [Fighting Cup (J)] MATCH_ID("NKA", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Fighters Destiny [Fighting Cup (J)]
MATCH_ID("NKI", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Killer Instinct Gold MATCH_ID("NKI", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Killer Instinct Gold
MATCH_ID("NKT", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Mario Kart 64 MATCH_ID("NKT", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Mario Kart 64
MATCH_ID("NLB", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Mario Party (PAL) MATCH_ID("NLB", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Mario Party (PAL)
MATCH_ID("NLL", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Last Legion UX MATCH_ID("NLL", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Last Legion UX
MATCH_ID("NLR", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Lode Runner 3-D MATCH_ID("NLR", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Lode Runner 3-D
MATCH_ID("NMG", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Monaco Grand Prix [Racing Simulation 2 (G)] MATCH_ID("NMG", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Monaco Grand Prix [Racing Simulation 2 (G)]
MATCH_ID("NMI", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Mission: Impossible MATCH_ID("NMI", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Mission: Impossible
MATCH_ID("NML", SAVE_TYPE_EEPROM_4K, FEAT_RPAK | FEAT_TPAK), // Mickey's Speedway USA [Mickey no Racing Challenge USA (J)] MATCH_ID("NML", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK | FEAT_TPAK), // Mickey's Speedway USA [Mickey no Racing Challenge USA (J)]
MATCH_ID("NMO", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Monopoly MATCH_ID("NMO", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Monopoly
MATCH_ID("NMR", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Multi-Racing Championship MATCH_ID("NMR", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Multi-Racing Championship
MATCH_ID("NMS", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // Morita Shougi 64 MATCH_ID("NMS", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // Morita Shougi 64
MATCH_ID("NMU", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Big Mountain 2000 MATCH_ID("NMU", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Big Mountain 2000
MATCH_ID("NMW", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Mario Party 2 MATCH_ID("NMW", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Mario Party 2
MATCH_ID("NMZ", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Zool - Majou Tsukai Densetsu (J) MATCH_ID("NMZ", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Zool - Majou Tsukai Densetsu (J)
MATCH_ID("NN6", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Dr. Mario 64 MATCH_ID("NN6", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Dr. Mario 64
MATCH_ID("NNA", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Star Wars Episode I: Battle for Naboo MATCH_ID("NNA", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Star Wars Episode I: Battle for Naboo
MATCH_ID("NOS", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // 64 Oozumou MATCH_ID("NOS", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // 64 Oozumou
MATCH_ID("NP2", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Chou Kuukan Night Pro Yakyuu King 2 (J) MATCH_ID("NP2", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Chou Kuukan Night Pro Yakyuu King 2 (J)
MATCH_ID("NPG", SAVE_TYPE_EEPROM_4K, FEAT_RPAK | FEAT_VRU), // Hey You, Pikachu! [Pikachu Genki Dechu (J)] MATCH_ID("NPG", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK | FEAT_VRU), // Hey You, Pikachu! [Pikachu Genki Dechu (J)]
MATCH_ID("NPT", SAVE_TYPE_EEPROM_4K, FEAT_RPAK | FEAT_TPAK), // Puyo Puyon Party MATCH_ID("NPT", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK | FEAT_TPAK), // Puyo Puyon Party
MATCH_ID("NPW", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Pilotwings 64 MATCH_ID("NPW", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Pilotwings 64
MATCH_ID("NPY", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Puyo Puyo Sun 64 MATCH_ID("NPY", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Puyo Puyo Sun 64
MATCH_ID("NRA", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Rally '99 (J) MATCH_ID("NRA", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Rally '99 (J)
MATCH_ID("NRC", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Top Gear Overdrive MATCH_ID("NRC", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Top Gear Overdrive
MATCH_ID("NRS", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Star Wars: Rogue Squadron [Shutsugeki! Rogue Chuutai (J)] MATCH_ID("NRS", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Star Wars: Rogue Squadron [Shutsugeki! Rogue Chuutai (J)]
MATCH_ID("NS3", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // AI Shougi 3 MATCH_ID("NS3", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // AI Shougi 3
MATCH_ID("NS6", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Star Soldier: Vanishing Earth MATCH_ID("NS6", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Star Soldier: Vanishing Earth
MATCH_ID("NSA", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Sonic Wings Assault (J) MATCH_ID("NSA", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Sonic Wings Assault (J)
MATCH_ID("NSC", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Starshot: Space Circus Fever MATCH_ID("NSC", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Starshot: Space Circus Fever
MATCH_ID("NSN", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Snow Speeder (J) MATCH_ID("NSN", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Snow Speeder (J)
MATCH_ID("NSS", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Super Robot Spirits MATCH_ID("NSS", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Super Robot Spirits
MATCH_ID("NSU", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Rocket: Robot on Wheels MATCH_ID("NSU", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Rocket: Robot on Wheels
MATCH_ID("NSW", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Star Wars: Shadows of the Empire [Teikoku no Kage (J)] MATCH_ID("NSW", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Star Wars: Shadows of the Empire [Teikoku no Kage (J)]
MATCH_ID("NT6", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Tetris 64 MATCH_ID("NT6", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Tetris 64
MATCH_ID("NTB", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Transformers: Beast Wars Metals 64 (J) MATCH_ID("NTB", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Transformers: Beast Wars Metals 64 (J)
MATCH_ID("NTC", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // 64 Trump Collection MATCH_ID("NTC", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // 64 Trump Collection
MATCH_ID("NTJ", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Tom & Jerry in Fists of Fury MATCH_ID("NTJ", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Tom & Jerry in Fists of Fury
MATCH_ID("NTM", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Mischief Makers [Yuke Yuke!! Trouble Makers (J)] MATCH_ID("NTM", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Mischief Makers [Yuke Yuke!! Trouble Makers (J)]
MATCH_ID("NTN", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // All Star Tennis '99 MATCH_ID("NTN", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // All Star Tennis '99
MATCH_ID("NTP", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Tetrisphere MATCH_ID("NTP", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Tetrisphere
MATCH_ID("NTR", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Top Gear Rally (J + E) MATCH_ID("NTR", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Top Gear Rally (J + E)
MATCH_ID("NTW", SAVE_TYPE_EEPROM_4K, FEAT_CPAK), // 64 de Hakken!! Tamagotchi MATCH_ID("NTW", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK), // 64 de Hakken!! Tamagotchi
MATCH_ID("NTX", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Taz Express MATCH_ID("NTX", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Taz Express
MATCH_ID("NVL", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // V-Rally Edition '99 MATCH_ID("NVL", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // V-Rally Edition '99
MATCH_ID("NVY", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // V-Rally Edition '99 (J) MATCH_ID("NVY", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // V-Rally Edition '99 (J)
MATCH_ID("NWC", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Wild Choppers MATCH_ID("NWC", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Wild Choppers
MATCH_ID("NWQ", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Rally Challenge 2000 MATCH_ID("NWQ", SAVE_TYPE_EEPROM_4KBIT, FEAT_CPAK | FEAT_RPAK), // Rally Challenge 2000
MATCH_ID("NWU", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Worms Armageddon (E) MATCH_ID("NWU", SAVE_TYPE_EEPROM_4KBIT, FEAT_NONE), // Worms Armageddon (E)
MATCH_ID("NXO", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Cruis'n Exotica MATCH_ID("NXO", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Cruis'n Exotica
MATCH_ID("NYK", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Yakouchuu II: Satsujin Kouro MATCH_ID("NYK", SAVE_TYPE_EEPROM_4KBIT, FEAT_RPAK), // Yakouchuu II: Satsujin Kouro
// EEPROM 16K // EEPROM 16K
MATCH_ID("N3D", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Doraemon 3: Nobita no Machi SOS! MATCH_ID("N3D", SAVE_TYPE_EEPROM_16KBIT, 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("NB7", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Banjo-Tooie [Banjo to Kazooie no Daiboken 2 (J)]
MATCH_ID("NCW", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Cruis'n World MATCH_ID("NCW", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Cruis'n World
MATCH_ID("NCZ", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Custom Robo V2 MATCH_ID("NCZ", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Custom Robo V2
MATCH_ID("ND2", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Doraemon 2: Nobita to Hikari no Shinden MATCH_ID("ND2", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Doraemon 2: Nobita to Hikari no Shinden
MATCH_ID("ND6", SAVE_TYPE_EEPROM_16K, FEAT_RPAK | FEAT_VRU), // Densha de Go! 64 MATCH_ID("ND6", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK | FEAT_VRU), // Densha de Go! 64
MATCH_ID("NDO", SAVE_TYPE_EEPROM_16K, FEAT_RPAK | FEAT_EXP_PAK_REQUIRED), // Donkey Kong 64 MATCH_ID("NDO", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK | FEAT_EXP_PAK_REQUIRED), // Donkey Kong 64
MATCH_ID("NEP", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Star Wars Episode I: Racer MATCH_ID("NEP", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Star Wars Episode I: Racer
MATCH_ID("NEV", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Neon Genesis Evangelion MATCH_ID("NEV", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Neon Genesis Evangelion
MATCH_ID("NFU", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Conker's Bad Fur Day MATCH_ID("NFU", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Conker's Bad Fur Day
MATCH_ID("NGC", SAVE_TYPE_EEPROM_16K, FEAT_CPAK | FEAT_RPAK), // GT 64: Championship Edition MATCH_ID("NGC", SAVE_TYPE_EEPROM_16KBIT, FEAT_CPAK | FEAT_RPAK), // GT 64: Championship Edition
MATCH_ID("NGT", SAVE_TYPE_EEPROM_16K, FEAT_CPAK | FEAT_RPAK), // City Tour GrandPrix - Zen Nihon GT Senshuken MATCH_ID("NGT", SAVE_TYPE_EEPROM_16KBIT, FEAT_CPAK | FEAT_RPAK), // City Tour GrandPrix - Zen Nihon GT Senshuken
MATCH_ID("NIM", SAVE_TYPE_EEPROM_16K, FEAT_NONE), // Ide Yosuke no Mahjong Juku MATCH_ID("NIM", SAVE_TYPE_EEPROM_16KBIT, FEAT_NONE), // Ide Yosuke no Mahjong Juku
MATCH_ID("NM8", SAVE_TYPE_EEPROM_16K, FEAT_RPAK | FEAT_TPAK), // Mario Tennis MATCH_ID("NM8", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK | FEAT_TPAK), // Mario Tennis
MATCH_ID("NMV", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Mario Party 3 MATCH_ID("NMV", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Mario Party 3
MATCH_ID("NMX", SAVE_TYPE_EEPROM_16K, FEAT_CPAK | FEAT_RPAK), // Excitebike 64 MATCH_ID("NMX", SAVE_TYPE_EEPROM_16KBIT, FEAT_CPAK | FEAT_RPAK), // Excitebike 64
MATCH_ID("NNB", SAVE_TYPE_EEPROM_16K, FEAT_CPAK | FEAT_RPAK), // Kobe Bryant in NBA Courtside MATCH_ID("NNB", SAVE_TYPE_EEPROM_16KBIT, FEAT_CPAK | FEAT_RPAK), // Kobe Bryant in NBA Courtside
MATCH_ID("NPD", SAVE_TYPE_EEPROM_16K, FEAT_CPAK | FEAT_RPAK | FEAT_TPAK | FEAT_EXP_PAK_RECOMMENDED), // Perfect Dark MATCH_ID("NPD", SAVE_TYPE_EEPROM_16KBIT, FEAT_CPAK | FEAT_RPAK | FEAT_TPAK | FEAT_EXP_PAK_RECOMMENDED), // Perfect Dark
MATCH_ID("NPP", SAVE_TYPE_EEPROM_16K, FEAT_CPAK), // Parlor! Pro 64: Pachinko Jikki Simulation Game MATCH_ID("NPP", SAVE_TYPE_EEPROM_16KBIT, FEAT_CPAK), // Parlor! Pro 64: Pachinko Jikki Simulation Game
MATCH_ID("NR7", SAVE_TYPE_EEPROM_16K, FEAT_TPAK), // Robot Poncots 64: 7tsu no Umi no Caramel MATCH_ID("NR7", SAVE_TYPE_EEPROM_16KBIT, FEAT_TPAK), // Robot Poncots 64: 7tsu no Umi no Caramel
MATCH_ID("NRZ", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Ridge Racer 64 MATCH_ID("NRZ", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Ridge Racer 64
MATCH_ID("NUB", SAVE_TYPE_EEPROM_16K, FEAT_CPAK | FEAT_TPAK), // PD Ultraman Battle Collection 64 MATCH_ID("NUB", SAVE_TYPE_EEPROM_16KBIT, FEAT_CPAK | FEAT_TPAK), // PD Ultraman Battle Collection 64
MATCH_ID("NYS", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Yoshi's Story MATCH_ID("NYS", SAVE_TYPE_EEPROM_16KBIT, FEAT_RPAK), // Yoshi's Story
// SRAM 256K // SRAM 256K
MATCH_ID("CFZ", SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_64DD_ENHANCED), // F-Zero X (J) MATCH_ID("CFZ", SAVE_TYPE_SRAM_256KBIT, 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("CPS", SAVE_TYPE_SRAM_256KBIT, 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("CZL", SAVE_TYPE_SRAM_256KBIT, 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("NA2", SAVE_TYPE_SRAM_256KBIT, 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("NAL", SAVE_TYPE_SRAM_256KBIT, 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("NB5", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // Biohazard 2 (J)
MATCH_ID("NDD", SAVE_TYPE_SRAM, FEAT_EXP_PAK_REQUIRED | FEAT_64DD_CONVERSION), // 64DD Conversion Rom MATCH_ID("NDD", SAVE_TYPE_SRAM_256KBIT, 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("NFZ", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // F-Zero X (U + E)
MATCH_ID("NG6", SAVE_TYPE_SRAM, FEAT_RPAK), // Ganmare Goemon: Dero Dero Douchuu Obake Tenkomori MATCH_ID("NG6", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // Ganmare Goemon: Dero Dero Douchuu Obake Tenkomori
MATCH_ID("NGP", SAVE_TYPE_SRAM, FEAT_CPAK), // Goemon: Mononoke Sugoroku MATCH_ID("NGP", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK), // Goemon: Mononoke Sugoroku
MATCH_ID("NHY", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_RPAK), // Hybrid Heaven (J) MATCH_ID("NHY", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK | FEAT_RPAK), // Hybrid Heaven (J)
MATCH_ID("NIB", SAVE_TYPE_SRAM, FEAT_RPAK), // Itoi Shigesato no Bass Tsuri No. 1 Kettei Ban! MATCH_ID("NIB", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // Itoi Shigesato no Bass Tsuri No. 1 Kettei Ban!
MATCH_ID("NJ5", SAVE_TYPE_SRAM, FEAT_CPAK), // Jikkyou Powerful Pro Yakyuu 5 MATCH_ID("NJ5", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK), // Jikkyou Powerful Pro Yakyuu 5
MATCH_ID("NJG", SAVE_TYPE_SRAM, FEAT_RPAK), // Jinsei Game 64 MATCH_ID("NJG", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // Jinsei Game 64
MATCH_ID("NKG", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_RPAK), // Major League Baseball featuring Ken Griffey Jr. MATCH_ID("NKG", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK | FEAT_RPAK), // Major League Baseball featuring Ken Griffey Jr.
MATCH_ID("NMF", SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_TPAK), // Mario Golf 64 MATCH_ID("NMF", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK | FEAT_TPAK), // Mario Golf 64
MATCH_ID("NOB", SAVE_TYPE_SRAM, FEAT_NONE), // Ogre Battle 64: Person of Lordly Caliber MATCH_ID("NOB", SAVE_TYPE_SRAM_256KBIT, FEAT_NONE), // Ogre Battle 64: Person of Lordly Caliber
MATCH_ID("NP4", SAVE_TYPE_SRAM, FEAT_CPAK), // Jikkyou Powerful Pro Yakyuu 4 MATCH_ID("NP4", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK), // Jikkyou Powerful Pro Yakyuu 4
MATCH_ID("NP6", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_TPAK), // Jikkyou Powerful Pro Yakyuu 6 MATCH_ID("NP6", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK | FEAT_TPAK), // Jikkyou Powerful Pro Yakyuu 6
MATCH_ID("NPA", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_TPAK), // Jikkyou Powerful Pro Yakyuu 2000 MATCH_ID("NPA", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK | FEAT_TPAK), // Jikkyou Powerful Pro Yakyuu 2000
MATCH_ID("NPE", SAVE_TYPE_SRAM, FEAT_CPAK), // Jikkyou Powerful Pro Yakyuu Basic Ban 2001 MATCH_ID("NPE", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK), // Jikkyou Powerful Pro Yakyuu Basic Ban 2001
MATCH_ID("NPM", SAVE_TYPE_SRAM, FEAT_CPAK), // Premier Manager 64 MATCH_ID("NPM", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK), // Premier Manager 64
MATCH_ID("NPS", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_RPAK), // Jikkyou J.League 1999: Perfect Striker 2 MATCH_ID("NPS", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK | FEAT_RPAK), // Jikkyou J.League 1999: Perfect Striker 2
MATCH_ID("NRE", SAVE_TYPE_SRAM, FEAT_RPAK), // Resident Evil 2 MATCH_ID("NRE", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // Resident Evil 2
MATCH_ID("NRI", SAVE_TYPE_SRAM, FEAT_CPAK), // New Tetris, The MATCH_ID("NRI", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK), // New Tetris, The
MATCH_ID("NS4", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_TPAK), // Super Robot Taisen 64 MATCH_ID("NS4", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK | FEAT_TPAK), // Super Robot Taisen 64
MATCH_ID("NSI", SAVE_TYPE_SRAM, FEAT_CPAK), // Fushigi no Dungeon: Fuurai no Shiren 2 MATCH_ID("NSI", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK), // Fushigi no Dungeon: Fuurai no Shiren 2
MATCH_ID("NT3", SAVE_TYPE_SRAM, FEAT_CPAK), // Shin Nihon Pro Wrestling - Toukon Road 2 - The Next Generation (J) MATCH_ID("NT3", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK), // Shin Nihon Pro Wrestling - Toukon Road 2 - The Next Generation (J)
MATCH_ID("NTE", SAVE_TYPE_SRAM, FEAT_RPAK), // 1080 Snowboarding MATCH_ID("NTE", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // 1080 Snowboarding
MATCH_ID("NUM", SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_TPAK), // Nushi Zuri 64: Shiokaze ni Notte MATCH_ID("NUM", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK | FEAT_TPAK), // Nushi Zuri 64: Shiokaze ni Notte
MATCH_ID("NUT", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_RPAK | FEAT_TPAK), // Nushi Zuri 64 MATCH_ID("NUT", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK | FEAT_RPAK | FEAT_TPAK), // Nushi Zuri 64
MATCH_ID("NVB", SAVE_TYPE_SRAM, FEAT_RPAK), // Bass Rush - ECOGEAR PowerWorm Championship (J) MATCH_ID("NVB", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // Bass Rush - ECOGEAR PowerWorm Championship (J)
MATCH_ID("NVP", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_RPAK), // Virtual Pro Wrestling 64 MATCH_ID("NVP", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK | FEAT_RPAK), // Virtual Pro Wrestling 64
MATCH_ID("NW2", SAVE_TYPE_SRAM, FEAT_RPAK), // WCW-nWo Revenge MATCH_ID("NW2", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // WCW-nWo Revenge
MATCH_ID("NWL", SAVE_TYPE_SRAM, FEAT_RPAK), // Waialae Country Club: True Golf Classics MATCH_ID("NWL", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // Waialae Country Club: True Golf Classics
MATCH_ID("NWX", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_RPAK), // WWF WrestleMania 2000 MATCH_ID("NWX", SAVE_TYPE_SRAM_256KBIT, FEAT_CPAK | FEAT_RPAK), // WWF WrestleMania 2000
MATCH_ID("NYW", SAVE_TYPE_SRAM, FEAT_NONE), // Harvest Moon 64 MATCH_ID("NYW", SAVE_TYPE_SRAM_256KBIT, FEAT_NONE), // Harvest Moon 64
MATCH_ID("NZL", SAVE_TYPE_SRAM, FEAT_RPAK), // Legend of Zelda: Ocarina of Time (E) MATCH_ID("NZL", SAVE_TYPE_SRAM_256KBIT, FEAT_RPAK), // Legend of Zelda: Ocarina of Time (E)
// SRAM 768K // SRAM 768K
MATCH_ID("CDZ", SAVE_TYPE_SRAM_BANKED, FEAT_RPAK | FEAT_64DD_ENHANCED), // Dezaemon 3D MATCH_ID("CDZ", SAVE_TYPE_SRAM_BANKED, FEAT_RPAK | FEAT_64DD_ENHANCED), // Dezaemon 3D
// FLASHRAM // FLASHRAM
MATCH_ID("CP2", SAVE_TYPE_FLASHRAM, FEAT_TPAK | FEAT_64DD_ENHANCED), // Pocket Monsters Stadium 2 (J) MATCH_ID("CP2", SAVE_TYPE_FLASHRAM_1MBIT, 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("NAF", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_CPAK | FEAT_RTC), // Doubutsu no Mori
MATCH_ID("NCC", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // Command & Conquer MATCH_ID("NCC", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_RPAK), // Command & Conquer
MATCH_ID("NCV", SAVE_TYPE_FLASHRAM, FEAT_NONE), // Cubivore (Translation) MATCH_ID("NCV", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_NONE), // Cubivore (Translation)
MATCH_ID("NCK", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // NBA Courtside 2 featuring Kobe Bryant MATCH_ID("NCK", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_RPAK), // NBA Courtside 2 featuring Kobe Bryant
MATCH_ID("NDA", SAVE_TYPE_FLASHRAM, FEAT_CPAK), // Derby Stallion 64 MATCH_ID("NDA", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_CPAK), // Derby Stallion 64
MATCH_ID("NDP", SAVE_TYPE_FLASHRAM, FEAT_EXP_PAK_REQUIRED), // Dinosaur Planet (Unlicensed) MATCH_ID("NDP", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_EXP_PAK_REQUIRED), // Dinosaur Planet (Unlicensed)
MATCH_ID("NJF", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // Jet Force Gemini [Star Twins (J)] MATCH_ID("NJF", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_RPAK), // Jet Force Gemini [Star Twins (J)]
MATCH_ID("NKJ", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // Ken Griffey Jr.'s Slugfest MATCH_ID("NKJ", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_RPAK), // Ken Griffey Jr.'s Slugfest
MATCH_ID("NM6", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // Mega Man 64 MATCH_ID("NM6", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_RPAK), // Mega Man 64
MATCH_ID("NMQ", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // Paper Mario MATCH_ID("NMQ", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_RPAK), // Paper Mario
MATCH_ID("NPF", SAVE_TYPE_FLASHRAM, FEAT_NONE), // Pokemon Snap [Pocket Monsters Snap (J)] MATCH_ID("NPF", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_NONE), // Pokemon Snap [Pocket Monsters Snap (J)]
MATCH_ID("NPN", SAVE_TYPE_FLASHRAM, FEAT_NONE), // Pokemon Puzzle League MATCH_ID("NPN", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_NONE), // Pokemon Puzzle League
MATCH_ID("NPO", SAVE_TYPE_FLASHRAM, FEAT_TPAK), // Pokemon Stadium MATCH_ID("NPO", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_TPAK), // Pokemon Stadium
MATCH_ID("NRH", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // Rockman Dash - Hagane no Boukenshin (J) MATCH_ID("NRH", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_RPAK), // Rockman Dash - Hagane no Boukenshin (J)
MATCH_ID("NSQ", SAVE_TYPE_FLASHRAM, FEAT_RPAK | FEAT_EXP_PAK_RECOMMENDED), // StarCraft 64 MATCH_ID("NSQ", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_RPAK | FEAT_EXP_PAK_RECOMMENDED), // StarCraft 64
MATCH_ID("NT9", SAVE_TYPE_FLASHRAM, FEAT_NONE), // Tigger's Honey Hunt MATCH_ID("NT9", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_NONE), // Tigger's Honey Hunt
MATCH_ID("NW4", SAVE_TYPE_FLASHRAM, FEAT_CPAK | FEAT_RPAK), // WWF No Mercy MATCH_ID("NW4", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_CPAK | FEAT_RPAK), // WWF No Mercy
MATCH_ID("NZS", SAVE_TYPE_FLASHRAM, FEAT_RPAK | FEAT_EXP_PAK_REQUIRED), // Legend of Zelda: Majora's Mask [Zelda no Densetsu - Mujura no Kamen (J)] MATCH_ID("NZS", SAVE_TYPE_FLASHRAM_1MBIT, FEAT_RPAK | FEAT_EXP_PAK_REQUIRED), // Legend of Zelda: Majora's Mask [Zelda no Densetsu - Mujura no Kamen (J)]
MATCH_ID("NP3", SAVE_TYPE_FLASHRAM_PKST2, FEAT_TPAK), // Pokemon Stadium 2 [Pocket Monsters Stadium - Kin Gin (J)] MATCH_ID("NP3", SAVE_TYPE_FLASHRAM_PKST2, FEAT_TPAK), // Pokemon Stadium 2 [Pocket Monsters Stadium - Kin Gin (J)]
// CONTROLLER PAK / NONE // CONTROLLER PAK / NONE
MATCH_ID("N22", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ready 2 Rumble Boxing - Round 2 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("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 MATCH_ID("N32", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Army Men - Sarge's Heroes 2
MATCH_ID("N3P", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Triple Play 2000 MATCH_ID("N3P", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Triple Play 2000
MATCH_ID("N64", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Kira to Kaiketsu! 64 Tanteidan MATCH_ID("N64", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Kira to Kaiketsu! 64 Tanteidan
MATCH_ID("N7I", SAVE_TYPE_NONE, FEAT_CPAK), // FIFA Soccer 64 [FIFA 64 (E)] MATCH_ID("N7I", SAVE_TYPE_NONE, FEAT_CPAK), // FIFA Soccer 64 [FIFA 64 (E)]
MATCH_ID("N8I", SAVE_TYPE_NONE, FEAT_CPAK), // FIFA - Road to World Cup 98 [World Cup e no Michi (J)] MATCH_ID("N8I", SAVE_TYPE_NONE, FEAT_CPAK), // FIFA - Road to World Cup 98 [World Cup e no Michi (J)]
MATCH_ID("N8M", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football 64 MATCH_ID("N8M", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football 64
MATCH_ID("N8W", SAVE_TYPE_NONE, FEAT_CPAK), // World Cup '98 MATCH_ID("N8W", SAVE_TYPE_NONE, FEAT_CPAK), // World Cup '98
MATCH_ID("N9B", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA Live '99 MATCH_ID("N9B", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA Live '99
MATCH_ID("N9C", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Nascar '99 MATCH_ID("N9C", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Nascar '99
MATCH_ID("N9F", SAVE_TYPE_NONE, FEAT_CPAK), // FIFA 99 MATCH_ID("N9F", SAVE_TYPE_NONE, FEAT_CPAK), // FIFA 99
MATCH_ID("N9H", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NHL '99 MATCH_ID("N9H", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NHL '99
MATCH_ID("N9M", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football '99 MATCH_ID("N9M", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football '99
MATCH_ID("NAC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Army Men - Air Combat MATCH_ID("NAC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Army Men - Air Combat
MATCH_ID("NAH", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Asteroids Hyper 64 MATCH_ID("NAH", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Asteroids Hyper 64
MATCH_ID("NAI", SAVE_TYPE_NONE, FEAT_CPAK), // Midway's Greatest Arcade Hits Volume 1 MATCH_ID("NAI", SAVE_TYPE_NONE, FEAT_CPAK), // Midway's Greatest Arcade Hits Volume 1
MATCH_ID("NAM", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Army Men - Sarge's Heroes MATCH_ID("NAM", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Army Men - Sarge's Heroes
MATCH_ID("NAR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Armorines - Project S.W.A.R.M. MATCH_ID("NAR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Armorines - Project S.W.A.R.M.
MATCH_ID("NAS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // All-Star Baseball 2001 MATCH_ID("NAS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // All-Star Baseball 2001
MATCH_ID("NAY", SAVE_TYPE_NONE, FEAT_CPAK), // Aidyn Chronicles - The First Mage MATCH_ID("NAY", SAVE_TYPE_NONE, FEAT_CPAK), // Aidyn Chronicles - The First Mage
MATCH_ID("NB2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA In the Zone '99 [NBA Pro '99 (E)] MATCH_ID("NB2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA In the Zone '99 [NBA Pro '99 (E)]
MATCH_ID("NB3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Bust-A-Move '99 [Bust-A-Move 3 DX (E)] MATCH_ID("NB3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Bust-A-Move '99 [Bust-A-Move 3 DX (E)]
MATCH_ID("NB4", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Bass Masters 2000 MATCH_ID("NB4", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Bass Masters 2000
MATCH_ID("NB8", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Beetle Adventure Racing (J) MATCH_ID("NB8", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Beetle Adventure Racing (J)
MATCH_ID("NB9", SAVE_TYPE_NONE, FEAT_CPAK), // NBA Jam '99 MATCH_ID("NB9", SAVE_TYPE_NONE, FEAT_CPAK), // NBA Jam '99
MATCH_ID("NBA", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA In the Zone '98 [NBA Pro '98 (E)] MATCH_ID("NBA", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA In the Zone '98 [NBA Pro '98 (E)]
MATCH_ID("NBE", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // All-Star Baseball 2000 MATCH_ID("NBE", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // All-Star Baseball 2000
MATCH_ID("NBF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Bio F.R.E.A.K.S. MATCH_ID("NBF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Bio F.R.E.A.K.S.
MATCH_ID("NBI", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Blitz 2000 MATCH_ID("NBI", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Blitz 2000
MATCH_ID("NBJ", SAVE_TYPE_NONE, FEAT_CPAK), // Bakushou Jinsei 64 - Mezase! Resort Ou MATCH_ID("NBJ", SAVE_TYPE_NONE, FEAT_CPAK), // Bakushou Jinsei 64 - Mezase! Resort Ou
MATCH_ID("NBL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Buck Bumble MATCH_ID("NBL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Buck Bumble
MATCH_ID("NBO", SAVE_TYPE_NONE, FEAT_CPAK), // Bottom of the 9th MATCH_ID("NBO", SAVE_TYPE_NONE, FEAT_CPAK), // Bottom of the 9th
MATCH_ID("NBP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Blues Brothers 2000 MATCH_ID("NBP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Blues Brothers 2000
MATCH_ID("NBQ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Battletanx - Global Assault MATCH_ID("NBQ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Battletanx - Global Assault
MATCH_ID("NBR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Milo's Astro Lanes MATCH_ID("NBR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Milo's Astro Lanes
MATCH_ID("NBS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // All-Star Baseball '99 MATCH_ID("NBS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // All-Star Baseball '99
MATCH_ID("NBU", SAVE_TYPE_NONE, FEAT_CPAK), // Bust-A-Move 2 - Arcade Edition MATCH_ID("NBU", SAVE_TYPE_NONE, FEAT_CPAK), // Bust-A-Move 2 - Arcade Edition
MATCH_ID("NBW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Super Bowling MATCH_ID("NBW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Super Bowling
MATCH_ID("NBX", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Battletanx MATCH_ID("NBX", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Battletanx
MATCH_ID("NBY", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Bug's Life, A MATCH_ID("NBY", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Bug's Life, A
MATCH_ID("NBZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Blitz MATCH_ID("NBZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Blitz
MATCH_ID("NCB", SAVE_TYPE_NONE, FEAT_RPAK), // Charlie Blast's Territory MATCH_ID("NCB", SAVE_TYPE_NONE, FEAT_RPAK), // Charlie Blast's Territory
MATCH_ID("NCD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Carmageddon 64 MATCH_ID("NCD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Carmageddon 64
MATCH_ID("NCE", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Nuclear Strike 64 MATCH_ID("NCE", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Nuclear Strike 64
MATCH_ID("NCL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // California Speed MATCH_ID("NCL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // California Speed
MATCH_ID("NCO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Jeremy McGrath Supercross 2000 MATCH_ID("NCO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Jeremy McGrath Supercross 2000
MATCH_ID("NCS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // S.C.A.R.S. MATCH_ID("NCS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // S.C.A.R.S.
MATCH_ID("NDC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // SD Hiryuu no Ken Densetsu (J) MATCH_ID("NDC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // SD Hiryuu no Ken Densetsu (J)
MATCH_ID("NDE", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Destruction Derby 64 MATCH_ID("NDE", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Destruction Derby 64
MATCH_ID("NDF", SAVE_TYPE_NONE, FEAT_RPAK), // Dance Dance Revolution - Disney Dancing Museum MATCH_ID("NDF", SAVE_TYPE_NONE, FEAT_RPAK), // Dance Dance Revolution - Disney Dancing Museum
MATCH_ID("NDH", SAVE_TYPE_NONE, FEAT_CPAK), // Duel Heroes MATCH_ID("NDH", SAVE_TYPE_NONE, FEAT_CPAK), // Duel Heroes
MATCH_ID("NDM", SAVE_TYPE_NONE, FEAT_CPAK), // Doom 64 MATCH_ID("NDM", SAVE_TYPE_NONE, FEAT_CPAK), // Doom 64
MATCH_ID("NDN", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Duke Nukem 64 MATCH_ID("NDN", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Duke Nukem 64
MATCH_ID("NDQ", SAVE_TYPE_NONE, FEAT_CPAK), // Disney's Donald Duck - Goin' Quackers [Quack Attack (E)] MATCH_ID("NDQ", SAVE_TYPE_NONE, FEAT_CPAK), // Disney's Donald Duck - Goin' Quackers [Quack Attack (E)]
MATCH_ID("NDS", SAVE_TYPE_NONE, FEAT_CPAK), // J.League Dynamite Soccer 64 MATCH_ID("NDS", SAVE_TYPE_NONE, FEAT_CPAK), // J.League Dynamite Soccer 64
MATCH_ID("NDT", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // South Park MATCH_ID("NDT", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // South Park
MATCH_ID("NDW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Daikatana, John Romero's MATCH_ID("NDW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Daikatana, John Romero's
MATCH_ID("NDZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Duke Nukem - Zero Hour MATCH_ID("NDZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Duke Nukem - Zero Hour
MATCH_ID("NEG", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Extreme-G MATCH_ID("NEG", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Extreme-G
MATCH_ID("NET", SAVE_TYPE_NONE, FEAT_CPAK), // Quest 64 [Eltale Monsters (J) Holy Magic Century (E)] MATCH_ID("NET", SAVE_TYPE_NONE, FEAT_CPAK), // Quest 64 [Eltale Monsters (J) Holy Magic Century (E)]
MATCH_ID("NF9", SAVE_TYPE_NONE, FEAT_CPAK), // Fox Sports College Hoops '99 MATCH_ID("NF9", SAVE_TYPE_NONE, FEAT_CPAK), // Fox Sports College Hoops '99
MATCH_ID("NFB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Blitz 2001 MATCH_ID("NFB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Blitz 2001
MATCH_ID("NFD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Flying Dragon MATCH_ID("NFD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Flying Dragon
MATCH_ID("NFF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Fighting Force 64 MATCH_ID("NFF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Fighting Force 64
MATCH_ID("NFL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football 2001 MATCH_ID("NFL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football 2001
MATCH_ID("NFO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Forsaken 64 MATCH_ID("NFO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Forsaken 64
MATCH_ID("NFQ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Razor Freestyle Scooter MATCH_ID("NFQ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Razor Freestyle Scooter
MATCH_ID("NFR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // F-1 Racing Championship MATCH_ID("NFR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // F-1 Racing Championship
MATCH_ID("NFS", SAVE_TYPE_NONE, FEAT_CPAK), // Famista 64 MATCH_ID("NFS", SAVE_TYPE_NONE, FEAT_CPAK), // Famista 64
MATCH_ID("NG2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Extreme-G XG2 MATCH_ID("NG2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Extreme-G XG2
MATCH_ID("NG5", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ganbare Goemon - Neo Momoyama Bakufu no Odori [Mystical Ninja Starring Goemon] MATCH_ID("NG5", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ganbare Goemon - Neo Momoyama Bakufu no Odori [Mystical Ninja Starring Goemon]
MATCH_ID("NGA", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Deadly Arts [G.A.S.P!! Fighter's NEXTream (E-J)] MATCH_ID("NGA", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Deadly Arts [G.A.S.P!! Fighter's NEXTream (E-J)]
MATCH_ID("NGB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Top Gear Hyper Bike MATCH_ID("NGB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Top Gear Hyper Bike
MATCH_ID("NGD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Gauntlet Legends (J) MATCH_ID("NGD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Gauntlet Legends (J)
MATCH_ID("NGM", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Goemon's Great Adventure [Mystical Ninja 2 Starring Goemon] MATCH_ID("NGM", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Goemon's Great Adventure [Mystical Ninja 2 Starring Goemon]
MATCH_ID("NGN", SAVE_TYPE_NONE, FEAT_CPAK), // Golden Nugget 64 MATCH_ID("NGN", SAVE_TYPE_NONE, FEAT_CPAK), // Golden Nugget 64
MATCH_ID("NGR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Top Gear Rally (U) MATCH_ID("NGR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Top Gear Rally (U)
MATCH_ID("NGS", SAVE_TYPE_NONE, FEAT_CPAK), // Jikkyou G1 Stable MATCH_ID("NGS", SAVE_TYPE_NONE, FEAT_CPAK), // Jikkyou G1 Stable
MATCH_ID("NGX", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Gauntlet Legends MATCH_ID("NGX", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Gauntlet Legends
MATCH_ID("NH5", SAVE_TYPE_NONE, FEAT_CPAK), // Nagano Winter Olympics '98 [Hyper Olympics in Nagano (J)] MATCH_ID("NH5", SAVE_TYPE_NONE, FEAT_CPAK), // Nagano Winter Olympics '98 [Hyper Olympics in Nagano (J)]
MATCH_ID("NH9", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NHL Breakaway '99 MATCH_ID("NH9", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NHL Breakaway '99
MATCH_ID("NHC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hercules - The Legendary Journeys MATCH_ID("NHC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hercules - The Legendary Journeys
MATCH_ID("NHG", SAVE_TYPE_NONE, FEAT_CPAK), // F-1 Pole Position 64 MATCH_ID("NHG", SAVE_TYPE_NONE, FEAT_CPAK), // F-1 Pole Position 64
MATCH_ID("NHG", SAVE_TYPE_NONE, FEAT_CPAK), // Human Grand Prix - New Generation MATCH_ID("NHG", SAVE_TYPE_NONE, FEAT_CPAK), // Human Grand Prix - New Generation
MATCH_ID("NHK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hiryuu no Ken Twin MATCH_ID("NHK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hiryuu no Ken Twin
MATCH_ID("NHL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NHL Breakaway '98 MATCH_ID("NHL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NHL Breakaway '98
MATCH_ID("NHM", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Mia Hamm Soccer 64 MATCH_ID("NHM", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Mia Hamm Soccer 64
MATCH_ID("NHN", SAVE_TYPE_NONE, FEAT_CPAK), // Olympic Hockey Nagano '98 MATCH_ID("NHN", SAVE_TYPE_NONE, FEAT_CPAK), // Olympic Hockey Nagano '98
MATCH_ID("NHO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NHL Blades of Steel '99 [NHL Pro '99 (E)] MATCH_ID("NHO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NHL Blades of Steel '99 [NHL Pro '99 (E)]
MATCH_ID("NHS", SAVE_TYPE_NONE, FEAT_CPAK), // Hamster Monogatari 64 MATCH_ID("NHS", SAVE_TYPE_NONE, FEAT_CPAK), // Hamster Monogatari 64
MATCH_ID("NHT", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hydro Thunder MATCH_ID("NHT", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hydro Thunder
MATCH_ID("NHV", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hybrid Heaven (U + E) MATCH_ID("NHV", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hybrid Heaven (U + E)
MATCH_ID("NHW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hot Wheels Turbo Racing MATCH_ID("NHW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Hot Wheels Turbo Racing
MATCH_ID("NHX", SAVE_TYPE_NONE, FEAT_CPAK), // Hexen MATCH_ID("NHX", SAVE_TYPE_NONE, FEAT_CPAK), // Hexen
MATCH_ID("NIS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // International Superstar Soccer 2000 MATCH_ID("NIS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // International Superstar Soccer 2000
MATCH_ID("NIV", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Space Invaders MATCH_ID("NIV", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Space Invaders
MATCH_ID("NJ2", SAVE_TYPE_NONE, FEAT_CPAK), // Wonder Project J2 - Koruro no Mori no Jozet (J) MATCH_ID("NJ2", SAVE_TYPE_NONE, FEAT_CPAK), // Wonder Project J2 - Koruro no Mori no Jozet (J)
MATCH_ID("NJ3", SAVE_TYPE_NONE, FEAT_CPAK), // Jikkyou World Soccer 3 MATCH_ID("NJ3", SAVE_TYPE_NONE, FEAT_CPAK), // Jikkyou World Soccer 3
MATCH_ID("NJA", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA Jam 2000 MATCH_ID("NJA", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA Jam 2000
MATCH_ID("NJE", SAVE_TYPE_NONE, FEAT_CPAK), // J.League Eleven Beat 1997 MATCH_ID("NJE", SAVE_TYPE_NONE, FEAT_CPAK), // J.League Eleven Beat 1997
MATCH_ID("NJL", SAVE_TYPE_NONE, FEAT_CPAK), // J.League Live 64 MATCH_ID("NJL", SAVE_TYPE_NONE, FEAT_CPAK), // J.League Live 64
MATCH_ID("NJP", SAVE_TYPE_NONE, FEAT_CPAK), // International Superstar Soccer 64 [Jikkyo J-League Perfect Striker (J)] MATCH_ID("NJP", SAVE_TYPE_NONE, FEAT_CPAK), // International Superstar Soccer 64 [Jikkyo J-League Perfect Striker (J)]
MATCH_ID("NJQ", SAVE_TYPE_NONE, FEAT_RPAK), // Batman Beyond - Return of the Joker [Batman of the Future - Return of the Joker (E)] MATCH_ID("NJQ", SAVE_TYPE_NONE, FEAT_RPAK), // Batman Beyond - Return of the Joker [Batman of the Future - Return of the Joker (E)]
MATCH_ID("NKE", SAVE_TYPE_NONE, FEAT_RPAK), // Knife Edge - Nose Gunner MATCH_ID("NKE", SAVE_TYPE_NONE, FEAT_RPAK), // Knife Edge - Nose Gunner
MATCH_ID("NKK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Knockout Kings 2000 MATCH_ID("NKK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Knockout Kings 2000
MATCH_ID("NKM", SAVE_TYPE_NONE, FEAT_CPAK), // Pro Mahjong Kiwame 64 (J) MATCH_ID("NKM", SAVE_TYPE_NONE, FEAT_CPAK), // Pro Mahjong Kiwame 64 (J)
MATCH_ID("NKR", SAVE_TYPE_NONE, FEAT_CPAK), // Rakuga Kids (E) MATCH_ID("NKR", SAVE_TYPE_NONE, FEAT_CPAK), // Rakuga Kids (E)
MATCH_ID("NL2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Top Gear Rally 2 MATCH_ID("NL2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Top Gear Rally 2
MATCH_ID("NLC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Automobili Lamborghini [Super Speed Race 64 (J)] MATCH_ID("NLC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Automobili Lamborghini [Super Speed Race 64 (J)]
MATCH_ID("NLG", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // LEGO Racers MATCH_ID("NLG", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // LEGO Racers
MATCH_ID("NM3", SAVE_TYPE_NONE, FEAT_RPAK), // Monster Truck Madness 64 MATCH_ID("NM3", SAVE_TYPE_NONE, FEAT_RPAK), // Monster Truck Madness 64
MATCH_ID("NM4", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Mortal Kombat 4 MATCH_ID("NM4", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Mortal Kombat 4
MATCH_ID("NM9", SAVE_TYPE_NONE, FEAT_CPAK), // Harukanaru Augusta Masters 98 MATCH_ID("NM9", SAVE_TYPE_NONE, FEAT_CPAK), // Harukanaru Augusta Masters 98
MATCH_ID("NMA", SAVE_TYPE_NONE, FEAT_CPAK), // Jangou Simulation Mahjong Do 64 MATCH_ID("NMA", SAVE_TYPE_NONE, FEAT_CPAK), // Jangou Simulation Mahjong Do 64
MATCH_ID("NMB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Mike Piazza's Strike Zone MATCH_ID("NMB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Mike Piazza's Strike Zone
MATCH_ID("NMD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football 2000 MATCH_ID("NMD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football 2000
MATCH_ID("NMJ", SAVE_TYPE_NONE, FEAT_CPAK), // Mahjong 64 MATCH_ID("NMJ", SAVE_TYPE_NONE, FEAT_CPAK), // Mahjong 64
MATCH_ID("NMM", SAVE_TYPE_NONE, FEAT_CPAK), // Mahjong Master MATCH_ID("NMM", SAVE_TYPE_NONE, FEAT_CPAK), // Mahjong Master
MATCH_ID("NMT", SAVE_TYPE_NONE, FEAT_RPAK), // Magical Tetris Challenge MATCH_ID("NMT", SAVE_TYPE_NONE, FEAT_RPAK), // Magical Tetris Challenge
MATCH_ID("NMY", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Mortal Kombat Mythologies - Sub-Zero MATCH_ID("NMY", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Mortal Kombat Mythologies - Sub-Zero
MATCH_ID("NN2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Nascar 2000 MATCH_ID("NN2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Nascar 2000
MATCH_ID("NNC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Nightmare Creatures MATCH_ID("NNC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Nightmare Creatures
MATCH_ID("NNL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA Live 2000 MATCH_ID("NNL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA Live 2000
MATCH_ID("NNM", SAVE_TYPE_NONE, FEAT_CPAK), // Namco Museum 64 MATCH_ID("NNM", SAVE_TYPE_NONE, FEAT_CPAK), // Namco Museum 64
MATCH_ID("NNR", SAVE_TYPE_NONE, FEAT_CPAK), // Pro Mahjong Tsuwamono 64 - Jansou Battle ni Chousen (J) MATCH_ID("NNR", SAVE_TYPE_NONE, FEAT_CPAK), // Pro Mahjong Tsuwamono 64 - Jansou Battle ni Chousen (J)
MATCH_ID("NNS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Beetle Adventure Racing MATCH_ID("NNS", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Beetle Adventure Racing
MATCH_ID("NO7", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // The World Is Not Enough MATCH_ID("NO7", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // The World Is Not Enough
MATCH_ID("NOF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Offroad Challenge MATCH_ID("NOF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Offroad Challenge
MATCH_ID("NOH", SAVE_TYPE_NONE, FEAT_RPAK | FEAT_TPAK), // Transformers Beast Wars - Transmetals MATCH_ID("NOH", SAVE_TYPE_NONE, FEAT_RPAK | FEAT_TPAK), // Transformers Beast Wars - Transmetals
MATCH_ID("NOM", SAVE_TYPE_NONE, FEAT_CPAK), // Onegai Monsters MATCH_ID("NOM", SAVE_TYPE_NONE, FEAT_CPAK), // Onegai Monsters
MATCH_ID("NOW", SAVE_TYPE_NONE, FEAT_CPAK), // Brunswick Circuit Pro Bowling MATCH_ID("NOW", SAVE_TYPE_NONE, FEAT_CPAK), // Brunswick Circuit Pro Bowling
MATCH_ID("NP9", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ms. Pac-Man - Maze Madness MATCH_ID("NP9", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ms. Pac-Man - Maze Madness
MATCH_ID("NPB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Puzzle Bobble 64 (J) MATCH_ID("NPB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Puzzle Bobble 64 (J)
MATCH_ID("NPC", SAVE_TYPE_NONE, FEAT_CPAK), // Pachinko 365 Nichi (J) MATCH_ID("NPC", SAVE_TYPE_NONE, FEAT_CPAK), // Pachinko 365 Nichi (J)
MATCH_ID("NPK", SAVE_TYPE_NONE, FEAT_CPAK), // Chou Kuukan Night Pro Yakyuu King (J) MATCH_ID("NPK", SAVE_TYPE_NONE, FEAT_CPAK), // Chou Kuukan Night Pro Yakyuu King (J)
MATCH_ID("NPL", SAVE_TYPE_NONE, FEAT_CPAK), // Power League 64 (J) MATCH_ID("NPL", SAVE_TYPE_NONE, FEAT_CPAK), // Power League 64 (J)
MATCH_ID("NPR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // South Park Rally MATCH_ID("NPR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // South Park Rally
MATCH_ID("NPU", SAVE_TYPE_NONE, FEAT_CPAK), // Power Rangers - Lightspeed Rescue MATCH_ID("NPU", SAVE_TYPE_NONE, FEAT_CPAK), // Power Rangers - Lightspeed Rescue
MATCH_ID("NPX", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Polaris SnoCross MATCH_ID("NPX", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Polaris SnoCross
MATCH_ID("NPZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Susume! Taisen Puzzle Dama Toukon! Marumata Chou (J) MATCH_ID("NPZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Susume! Taisen Puzzle Dama Toukon! Marumata Chou (J)
MATCH_ID("NQ2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Quake 2 MATCH_ID("NQ2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Quake 2
MATCH_ID("NQ8", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Quarterback Club '98 MATCH_ID("NQ8", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Quarterback Club '98
MATCH_ID("NQ9", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Quarterback Club '99 MATCH_ID("NQ9", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Quarterback Club '99
MATCH_ID("NQB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Quarterback Club 2000 MATCH_ID("NQB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Quarterback Club 2000
MATCH_ID("NQC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Quarterback Club 2001 MATCH_ID("NQC", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Quarterback Club 2001
MATCH_ID("NQK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Quake 64 MATCH_ID("NQK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Quake 64
MATCH_ID("NR2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Rush 2 - Extreme Racing USA MATCH_ID("NR2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Rush 2 - Extreme Racing USA
MATCH_ID("NR3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Stunt Racer 64 MATCH_ID("NR3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Stunt Racer 64
MATCH_ID("NR6", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Tom Clancy's Rainbow Six MATCH_ID("NR6", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Tom Clancy's Rainbow Six
MATCH_ID("NRD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ready 2 Rumble Boxing MATCH_ID("NRD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ready 2 Rumble Boxing
MATCH_ID("NRG", SAVE_TYPE_NONE, FEAT_RPAK), // Rugrats - Scavenger Hunt [Treasure Hunt (E)] MATCH_ID("NRG", SAVE_TYPE_NONE, FEAT_RPAK), // Rugrats - Scavenger Hunt [Treasure Hunt (E)]
MATCH_ID("NRK", SAVE_TYPE_NONE, FEAT_CPAK), // Rugrats in Paris - The Movie MATCH_ID("NRK", SAVE_TYPE_NONE, FEAT_CPAK), // Rugrats in Paris - The Movie
MATCH_ID("NRO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Road Rash 64 MATCH_ID("NRO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Road Rash 64
MATCH_ID("NRP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Rampage - World Tour MATCH_ID("NRP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Rampage - World Tour
MATCH_ID("NRP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Rampage 2 - Universal Tour MATCH_ID("NRP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Rampage 2 - Universal Tour
MATCH_ID("NRR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Roadster's Trophy MATCH_ID("NRR", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Roadster's Trophy
MATCH_ID("NRT", SAVE_TYPE_NONE, FEAT_CPAK), // Rat Attack MATCH_ID("NRT", SAVE_TYPE_NONE, FEAT_CPAK), // Rat Attack
MATCH_ID("NRT", SAVE_TYPE_NONE, FEAT_CPAK), // Robotron 64 MATCH_ID("NRT", SAVE_TYPE_NONE, FEAT_CPAK), // Robotron 64
MATCH_ID("NRU", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // San Francisco Rush 2049 MATCH_ID("NRU", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // San Francisco Rush 2049
MATCH_ID("NRV", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Re-Volt MATCH_ID("NRV", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Re-Volt
MATCH_ID("NRW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Turok: Rage Wars MATCH_ID("NRW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Turok: Rage Wars
MATCH_ID("NS2", SAVE_TYPE_NONE, FEAT_CPAK), // Simcity 2000 MATCH_ID("NS2", SAVE_TYPE_NONE, FEAT_CPAK), // Simcity 2000
MATCH_ID("NSB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Twisted Edge - Extreme Snowboarding [King Hill 64 - Extreme Snowboarding (J)] MATCH_ID("NSB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Twisted Edge - Extreme Snowboarding [King Hill 64 - Extreme Snowboarding (J)]
MATCH_ID("NSD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Shadow Man MATCH_ID("NSD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Shadow Man
MATCH_ID("NSF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // San Francisco Rush - Extreme Racing MATCH_ID("NSF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // San Francisco Rush - Extreme Racing
MATCH_ID("NSG", SAVE_TYPE_NONE, FEAT_CPAK), // Shadowgate 64 - Trials Of The Four Towers MATCH_ID("NSG", SAVE_TYPE_NONE, FEAT_CPAK), // Shadowgate 64 - Trials Of The Four Towers
MATCH_ID("NSH", SAVE_TYPE_NONE, FEAT_CPAK), // Saikyou Habu Shougi (J) MATCH_ID("NSH", SAVE_TYPE_NONE, FEAT_CPAK), // Saikyou Habu Shougi (J)
MATCH_ID("NSK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Snowboard Kids [Snobow Kids (J)] MATCH_ID("NSK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Snowboard Kids [Snobow Kids (J)]
MATCH_ID("NSL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Spider-Man MATCH_ID("NSL", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Spider-Man
MATCH_ID("NSO", SAVE_TYPE_NONE, FEAT_CPAK), // NBA Showtime - NBA on NBC MATCH_ID("NSO", SAVE_TYPE_NONE, FEAT_CPAK), // NBA Showtime - NBA on NBC
MATCH_ID("NSP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Superman MATCH_ID("NSP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Superman
MATCH_ID("NST", SAVE_TYPE_NONE, FEAT_CPAK), // Eikou no Saint Andrews MATCH_ID("NST", SAVE_TYPE_NONE, FEAT_CPAK), // Eikou no Saint Andrews
MATCH_ID("NSX", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Supercross 2000 MATCH_ID("NSX", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Supercross 2000
MATCH_ID("NSY", SAVE_TYPE_NONE, FEAT_CPAK), // Scooby-Doo! - Classic Creep Capers MATCH_ID("NSY", SAVE_TYPE_NONE, FEAT_CPAK), // Scooby-Doo! - Classic Creep Capers
MATCH_ID("NSZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Blitz - Special Edition MATCH_ID("NSZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NFL Blitz - Special Edition
MATCH_ID("NT2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Turok 2 - Seeds of Evil [Violence Killer - Turok New Generation (J)] MATCH_ID("NT2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Turok 2 - Seeds of Evil [Violence Killer - Turok New Generation (J)]
MATCH_ID("NT3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Tony Hawk's Pro Skater 3 MATCH_ID("NT3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Tony Hawk's Pro Skater 3
MATCH_ID("NT4", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // CyberTiger MATCH_ID("NT4", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // CyberTiger
MATCH_ID("NTA", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Disney's Tarzan MATCH_ID("NTA", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Disney's Tarzan
MATCH_ID("NTF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Tony Hawk's Pro Skater MATCH_ID("NTF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Tony Hawk's Pro Skater
MATCH_ID("NTH", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Toy Story 2 - Buzz Lightyear to the Rescue! MATCH_ID("NTH", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Toy Story 2 - Buzz Lightyear to the Rescue!
MATCH_ID("NTI", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WWF: Attitude MATCH_ID("NTI", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WWF: Attitude
MATCH_ID("NTK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Turok 3 - Shadow of Oblivion MATCH_ID("NTK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Turok 3 - Shadow of Oblivion
MATCH_ID("NTO", SAVE_TYPE_NONE, FEAT_CPAK), // Shin Nihon Pro Wrestling - Toukon Road - Brave Spirits (J) MATCH_ID("NTO", SAVE_TYPE_NONE, FEAT_CPAK), // Shin Nihon Pro Wrestling - Toukon Road - Brave Spirits (J)
MATCH_ID("NTQ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Tony Hawk's Pro Skater 2 MATCH_ID("NTQ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Tony Hawk's Pro Skater 2
MATCH_ID("NTS", SAVE_TYPE_NONE, FEAT_CPAK), // Centre Court Tennis [Let's Smash (J)] MATCH_ID("NTS", SAVE_TYPE_NONE, FEAT_CPAK), // Centre Court Tennis [Let's Smash (J)]
MATCH_ID("NTT", SAVE_TYPE_NONE, FEAT_CPAK), // Tonic Trouble MATCH_ID("NTT", SAVE_TYPE_NONE, FEAT_CPAK), // Tonic Trouble
MATCH_ID("NTU", SAVE_TYPE_NONE, FEAT_CPAK), // Turok: Dinosaur Hunter [Turok: Jikuu Senshi (J)] MATCH_ID("NTU", SAVE_TYPE_NONE, FEAT_CPAK), // Turok: Dinosaur Hunter [Turok: Jikuu Senshi (J)]
MATCH_ID("NV2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Chameleon Twist 2 MATCH_ID("NV2", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Chameleon Twist 2
MATCH_ID("NV3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Micro Machines 64 Turbo MATCH_ID("NV3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Micro Machines 64 Turbo
MATCH_ID("NV8", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Vigilante 8 MATCH_ID("NV8", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Vigilante 8
MATCH_ID("NVC", SAVE_TYPE_NONE, FEAT_CPAK), // Virtual Chess 64 MATCH_ID("NVC", SAVE_TYPE_NONE, FEAT_CPAK), // Virtual Chess 64
MATCH_ID("NVG", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Vigilante 8 - Second Offense MATCH_ID("NVG", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Vigilante 8 - Second Offense
MATCH_ID("NVR", SAVE_TYPE_NONE, FEAT_CPAK), // Virtual Pool 64 MATCH_ID("NVR", SAVE_TYPE_NONE, FEAT_CPAK), // Virtual Pool 64
MATCH_ID("NW3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WCW: Nitro MATCH_ID("NW3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WCW: Nitro
MATCH_ID("NW8", SAVE_TYPE_NONE, FEAT_CPAK), // Wayne Gretzky's 3D Hockey '98 MATCH_ID("NW8", SAVE_TYPE_NONE, FEAT_CPAK), // Wayne Gretzky's 3D Hockey '98
MATCH_ID("NWB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Iggy's Reckin' Balls [Iggy-kun no Bura Bura Poyon (J)] MATCH_ID("NWB", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Iggy's Reckin' Balls [Iggy-kun no Bura Bura Poyon (J)]
MATCH_ID("NWD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Winback - Covert Operations MATCH_ID("NWD", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Winback - Covert Operations
MATCH_ID("NWF", SAVE_TYPE_NONE, FEAT_RPAK), // Wheel of Fortune MATCH_ID("NWF", SAVE_TYPE_NONE, FEAT_RPAK), // Wheel of Fortune
MATCH_ID("NWG", SAVE_TYPE_NONE, FEAT_CPAK), // Wayne Gretzky's 3D Hockey MATCH_ID("NWG", SAVE_TYPE_NONE, FEAT_CPAK), // Wayne Gretzky's 3D Hockey
MATCH_ID("NWI", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // ECW Hardcore Revolution MATCH_ID("NWI", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // ECW Hardcore Revolution
MATCH_ID("NWK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Michael Owens WLS 2000 [World League Soccer 2000 (E) / Telefoot Soccer 2000 (F)] MATCH_ID("NWK", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Michael Owens WLS 2000 [World League Soccer 2000 (E) / Telefoot Soccer 2000 (F)]
MATCH_ID("NWM", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WCW: Mayhem MATCH_ID("NWM", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WCW: Mayhem
MATCH_ID("NWN", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WCW vs. nWo - World Tour MATCH_ID("NWN", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WCW vs. nWo - World Tour
MATCH_ID("NWO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // World Driver Championship MATCH_ID("NWO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // World Driver Championship
MATCH_ID("NWP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Wipeout 64 MATCH_ID("NWP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Wipeout 64
MATCH_ID("NWS", SAVE_TYPE_NONE, FEAT_CPAK), // International Superstar Soccer '98 [Jikkyo World Soccer - World Cup France '98 (J)] MATCH_ID("NWS", SAVE_TYPE_NONE, FEAT_CPAK), // International Superstar Soccer '98 [Jikkyo World Soccer - World Cup France '98 (J)]
MATCH_ID("NWV", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WCW: Backstage Assault MATCH_ID("NWV", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WCW: Backstage Assault
MATCH_ID("NWW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WWF: War Zone MATCH_ID("NWW", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // WWF: War Zone
MATCH_ID("NWZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA In the Zone 2000 MATCH_ID("NWZ", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // NBA In the Zone 2000
MATCH_ID("NX2", SAVE_TYPE_NONE, FEAT_CPAK), // Gex 64 - Enter the Gecko MATCH_ID("NX2", SAVE_TYPE_NONE, FEAT_CPAK), // Gex 64 - Enter the Gecko
MATCH_ID("NX3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Gex 3 - Deep Cover Gecko MATCH_ID("NX3", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Gex 3 - Deep Cover Gecko
MATCH_ID("NXF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Xena Warrior Princess - The Talisman of Fate MATCH_ID("NXF", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Xena Warrior Princess - The Talisman of Fate
MATCH_ID("NXG", SAVE_TYPE_NONE, FEAT_CPAK), // NBA Hangtime MATCH_ID("NXG", SAVE_TYPE_NONE, FEAT_CPAK), // NBA Hangtime
MATCH_ID("NY2", SAVE_TYPE_NONE, FEAT_CPAK), // Rayman 2 - The Great Escape MATCH_ID("NY2", SAVE_TYPE_NONE, FEAT_CPAK), // Rayman 2 - The Great Escape
MATCH_ID("NYP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Paperboy MATCH_ID("NYP", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Paperboy
MATCH_ID("NYW", SAVE_TYPE_NONE, FEAT_CPAK), // Bokujou Monogatari 2 MATCH_ID("NYW", SAVE_TYPE_NONE, FEAT_CPAK), // Bokujou Monogatari 2
MATCH_ID("NZO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Battlezone - Rise of the Black Dogs MATCH_ID("NZO", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Battlezone - Rise of the Black Dogs
MATCH_END, MATCH_END,
}; };
@ -749,12 +750,12 @@ static void extract_rom_info (match_t *match, rom_header_t *rom_header, rom_info
} }
switch ((rom_header->version & 0xF0) >> 4) { switch ((rom_header->version & 0xF0) >> 4) {
case 0: match->data.save = SAVE_TYPE_NONE; break; case 0: match->data.save = SAVE_TYPE_NONE; break;
case 1: match->data.save = SAVE_TYPE_EEPROM_4K; break; case 1: match->data.save = SAVE_TYPE_EEPROM_4KBIT; break;
case 2: match->data.save = SAVE_TYPE_EEPROM_16K; break; case 2: match->data.save = SAVE_TYPE_EEPROM_16KBIT; break;
case 3: match->data.save = SAVE_TYPE_SRAM; break; case 3: match->data.save = SAVE_TYPE_SRAM_256KBIT; break;
case 4: match->data.save = SAVE_TYPE_SRAM_BANKED; break; case 4: match->data.save = SAVE_TYPE_SRAM_BANKED; break;
case 5: match->data.save = SAVE_TYPE_FLASHRAM; break; case 5: match->data.save = SAVE_TYPE_FLASHRAM_1MBIT; break;
case 6: match->data.save = SAVE_TYPE_SRAM_128K; break; case 6: match->data.save = SAVE_TYPE_SRAM_1MBIT; break;
default: match->data.save = SAVE_TYPE_NONE; break; default: match->data.save = SAVE_TYPE_NONE; break;
} }
} }
@ -832,13 +833,21 @@ static rom_err_t save_override (path_t *path, const char *id, int value, int def
mini_t *ini = mini_try_load(path_get(overrides_path)); mini_t *ini = mini_try_load(path_get(overrides_path));
if (!ini) { if (!ini) {
return ROM_ERR_IO; return ROM_ERR_SAVE_IO;
} }
int mini_err;
if (value == default_value) { if (value == default_value) {
mini_delete_value(ini, NULL, id); mini_err = mini_delete_value(ini, NULL, id);
} else { } else {
mini_set_int(ini, NULL, id, value); mini_err = mini_set_int(ini, NULL, id, value);
}
if ((mini_err != MINI_OK) && (mini_err != MINI_VALUE_NOT_FOUND)) {
path_free(overrides_path);
mini_free(ini);
return ROM_ERR_SAVE_IO;
} }
bool empty = mini_empty(ini); bool empty = mini_empty(ini);
@ -847,16 +856,16 @@ static rom_err_t save_override (path_t *path, const char *id, int value, int def
if (mini_save(ini, MINI_FLAGS_NONE) != MINI_OK) { if (mini_save(ini, MINI_FLAGS_NONE) != MINI_OK) {
path_free(overrides_path); path_free(overrides_path);
mini_free(ini); mini_free(ini);
return ROM_ERR_IO; return ROM_ERR_SAVE_IO;
} }
} }
mini_free(ini); mini_free(ini);
if (empty) { if (empty) {
if (remove(path_get(overrides_path))) { if (remove(path_get(overrides_path)) && (errno != ENOENT)) {
path_free(overrides_path); path_free(overrides_path);
return ROM_ERR_IO; return ROM_ERR_SAVE_IO;
} }
} }
@ -946,10 +955,10 @@ rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info) {
setbuf(f, NULL); setbuf(f, NULL);
if (fread(&rom_header, sizeof(rom_header), 1, f) != 1) { if (fread(&rom_header, sizeof(rom_header), 1, f) != 1) {
fclose(f); fclose(f);
return ROM_ERR_IO; return ROM_ERR_LOAD_IO;
} }
if (fclose(f)) { if (fclose(f)) {
return ROM_ERR_IO; return ROM_ERR_LOAD_IO;
} }
fix_rom_header_endianness(&rom_header, rom_info); fix_rom_header_endianness(&rom_header, rom_info);

View File

@ -18,7 +18,8 @@
/** @brief ROM error enumeration. */ /** @brief ROM error enumeration. */
typedef enum { typedef enum {
ROM_OK, ROM_OK,
ROM_ERR_IO, ROM_ERR_LOAD_IO,
ROM_ERR_SAVE_IO,
ROM_ERR_NO_FILE, ROM_ERR_NO_FILE,
} rom_err_t; } rom_err_t;
@ -113,12 +114,12 @@ typedef enum {
typedef enum { typedef enum {
/** @brief There is no expected save type. */ /** @brief There is no expected save type. */
SAVE_TYPE_NONE = 0, SAVE_TYPE_NONE = 0,
SAVE_TYPE_EEPROM_4K = 1, SAVE_TYPE_EEPROM_4KBIT = 1,
SAVE_TYPE_EEPROM_16K = 2, SAVE_TYPE_EEPROM_16KBIT = 2,
SAVE_TYPE_SRAM = 3, SAVE_TYPE_SRAM_256KBIT = 3,
SAVE_TYPE_SRAM_BANKED = 4, SAVE_TYPE_SRAM_BANKED = 4,
SAVE_TYPE_SRAM_128K = 5, SAVE_TYPE_SRAM_1MBIT = 5,
SAVE_TYPE_FLASHRAM = 6, SAVE_TYPE_FLASHRAM_1MBIT = 6,
SAVE_TYPE_FLASHRAM_PKST2 = 7, SAVE_TYPE_FLASHRAM_PKST2 = 7,
SAVE_TYPE_AUTOMATIC = -1, SAVE_TYPE_AUTOMATIC = -1,
} rom_save_type_t; } rom_save_type_t;

View File

@ -13,10 +13,10 @@ static settings_t init = {
.show_protected_entries = false, .show_protected_entries = false,
.default_directory = "/", .default_directory = "/",
.use_saves_folder = true, .use_saves_folder = true,
.sound_enabled = true,
/* Beta feature flags (should always init to off) */ /* Beta feature flags (should always init to off) */
.bgm_enabled = false, .bgm_enabled = false,
.sound_enabled = false,
.rumble_enabled = false, .rumble_enabled = false,
}; };
@ -39,10 +39,10 @@ void settings_load (settings_t *settings) {
settings->show_protected_entries = mini_get_bool(ini, "menu", "show_protected_entries", init.show_protected_entries); settings->show_protected_entries = mini_get_bool(ini, "menu", "show_protected_entries", init.show_protected_entries);
settings->default_directory = strdup(mini_get_string(ini, "menu", "default_directory", init.default_directory)); settings->default_directory = strdup(mini_get_string(ini, "menu", "default_directory", init.default_directory));
settings->use_saves_folder = mini_get_bool(ini, "menu", "use_saves_folder", init.use_saves_folder); settings->use_saves_folder = mini_get_bool(ini, "menu", "use_saves_folder", init.use_saves_folder);
settings->sound_enabled = mini_get_bool(ini, "menu", "sound_enabled", init.sound_enabled);
/* Beta feature flags, they might not be in the file */ /* Beta feature flags, they might not be in the file */
settings->bgm_enabled = mini_get_bool(ini, "menu_beta_flag", "bgm_enabled", init.bgm_enabled); settings->bgm_enabled = mini_get_bool(ini, "menu_beta_flag", "bgm_enabled", init.bgm_enabled);
settings->sound_enabled = mini_get_bool(ini, "menu_beta_flag", "sound_enabled", init.sound_enabled);
settings->rumble_enabled = mini_get_bool(ini, "menu_beta_flag", "rumble_enabled", init.rumble_enabled); settings->rumble_enabled = mini_get_bool(ini, "menu_beta_flag", "rumble_enabled", init.rumble_enabled);
mini_free(ini); mini_free(ini);
@ -55,10 +55,10 @@ void settings_save (settings_t *settings) {
mini_set_bool(ini, "menu", "show_protected_entries", settings->show_protected_entries); mini_set_bool(ini, "menu", "show_protected_entries", settings->show_protected_entries);
mini_set_string(ini, "menu", "default_directory", settings->default_directory); mini_set_string(ini, "menu", "default_directory", settings->default_directory);
mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder); mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder);
mini_set_bool(ini, "menu", "sound_enabled", settings->sound_enabled);
/* Beta feature flags, they should not save until production ready! */ /* Beta feature flags, they should not save until production ready! */
// mini_set_bool(ini, "menu_beta_flag", "bgm_enabled", settings->bgm_enabled); // mini_set_bool(ini, "menu_beta_flag", "bgm_enabled", settings->bgm_enabled);
// mini_set_bool(ini, "menu_beta_flag", "sound_enabled", settings->sound_enabled);
// mini_set_bool(ini, "menu_beta_flag", "rumble_enabled", settings->rumble_enabled); // mini_set_bool(ini, "menu_beta_flag", "rumble_enabled", settings->rumble_enabled);
mini_save(ini, MINI_FLAGS_SKIP_EMPTY_GROUPS); mini_save(ini, MINI_FLAGS_SKIP_EMPTY_GROUPS);

View File

@ -3,14 +3,18 @@
#include <libdragon.h> #include <libdragon.h>
#include "mp3_player.h" #include "mp3_player.h"
#include "sound.h"
#define DEFAULT_FREQUENCY (44100) #define DEFAULT_FREQUENCY (44100)
#define NUM_BUFFERS (4) #define NUM_BUFFERS (4)
#define NUM_CHANNELS (2) #define NUM_CHANNELS (3)
static wav64_t sfx_cursor, sfx_error, sfx_enter, sfx_exit, sfx_setting;
static bool sound_initialized = false; static bool sound_initialized = false;
static bool sfx_enabled = false;
static void sound_reconfigure (int frequency) { static void sound_reconfigure (int frequency) {
@ -35,6 +39,51 @@ void sound_init_mp3_playback (void) {
sound_reconfigure(mp3player_get_samplerate()); sound_reconfigure(mp3player_get_samplerate());
} }
void sound_init_sfx (void) {
mixer_ch_set_vol(SOUND_SFX_CHANNEL, 0.5f, 0.5f);
wav64_open(&sfx_cursor, "rom:/cursorsound.wav64");
wav64_open(&sfx_exit, "rom:/back.wav64");
wav64_open(&sfx_setting, "rom:/settings.wav64");
wav64_open(&sfx_enter, "rom:/enter.wav64");
wav64_open(&sfx_error, "rom:/error.wav64");
sfx_enabled = true;
}
void sound_use_sfx(bool state) {
if (state) {
sfx_enabled = true;
}
else {
sfx_enabled = false;
}
}
void sound_play_effect(sound_effect_t sfx) {
if(sfx_enabled) {
switch (sfx) {
case SFX_CURSOR:
wav64_play(&sfx_cursor, SOUND_SFX_CHANNEL);
break;
case SFX_EXIT:
wav64_play(&sfx_exit, SOUND_SFX_CHANNEL);
break;
case SFX_SETTING:
wav64_play(&sfx_setting, SOUND_SFX_CHANNEL);
break;
case SFX_ENTER:
wav64_play(&sfx_enter, SOUND_SFX_CHANNEL);
break;
case SFX_ERROR:
wav64_play(&sfx_error, SOUND_SFX_CHANNEL);
break;
default:
break;
}
}
}
void sound_deinit (void) { void sound_deinit (void) {
if (sound_initialized) { if (sound_initialized) {
mixer_close(); mixer_close();

View File

@ -7,14 +7,26 @@
#ifndef SOUND_H__ #ifndef SOUND_H__
#define SOUND_H__ #define SOUND_H__
#include <stdbool.h>
#define SOUND_MP3_PLAYER_CHANNEL (0) #define SOUND_MP3_PLAYER_CHANNEL (0)
#define SOUND_SFX_CHANNEL (2)
typedef enum {
SFX_CURSOR,
SFX_ERROR,
SFX_ENTER,
SFX_EXIT,
SFX_SETTING,
} sound_effect_t;
void sound_init_default (void); void sound_init_default (void);
void sound_init_mp3_playback (void); void sound_init_mp3_playback (void);
void sound_init_sfx (void);
void sound_use_sfx(bool);
void sound_play_effect(sound_effect_t sfx);
void sound_deinit (void); void sound_deinit (void);
void sound_poll (void); void sound_poll (void);
#endif #endif

View File

@ -1,11 +1,12 @@
#include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/errno.h>
#include <time.h> #include <time.h>
#include "../fonts.h" #include "../fonts.h"
#include "utils/fs.h" #include "utils/fs.h"
#include "views.h" #include "views.h"
#include "../sound.h"
static const char *rom_extensions[] = { "z64", "n64", "v64", "rom", NULL }; static const char *rom_extensions[] = { "z64", "n64", "v64", "rom", NULL };
@ -22,10 +23,17 @@ static const char *hidden_paths[] = {
"/menu.bin", "/menu.bin",
"/menu", "/menu",
"/N64FlashcartMenu.n64", "/N64FlashcartMenu.n64",
"/OS64.v64", "/ED64",
"/OS64P.v64", "/ED64P",
"/sc64menu.n64", "/sc64menu.n64",
// Windows garbage
"/System Volume Information", "/System Volume Information",
// macOS garbage
"/.fseventsd",
"/.Spotlight-V100",
"/.Trashes",
"/.VolumeIcon.icns",
"/.metadata_never_index",
NULL, NULL,
}; };
@ -303,16 +311,19 @@ static void process (menu_t *menu) {
if (menu->browser.selected < 0) { if (menu->browser.selected < 0) {
menu->browser.selected = 0; menu->browser.selected = 0;
} }
sound_play_effect(SFX_CURSOR);
} else if (menu->actions.go_down) { } else if (menu->actions.go_down) {
menu->browser.selected += scroll_speed; menu->browser.selected += scroll_speed;
if (menu->browser.selected >= menu->browser.entries) { if (menu->browser.selected >= menu->browser.entries) {
menu->browser.selected = menu->browser.entries - 1; menu->browser.selected = menu->browser.entries - 1;
} }
sound_play_effect(SFX_CURSOR);
} }
menu->browser.entry = &menu->browser.list[menu->browser.selected]; menu->browser.entry = &menu->browser.list[menu->browser.selected];
} }
if (menu->actions.enter && menu->browser.entry) { if (menu->actions.enter && menu->browser.entry) {
sound_play_effect(SFX_ENTER);
switch (menu->browser.entry->type) { switch (menu->browser.entry->type) {
case ENTRY_TYPE_DIR: case ENTRY_TYPE_DIR:
if (push_directory(menu, menu->browser.entry->name)) { if (push_directory(menu, menu->browser.entry->name)) {
@ -350,10 +361,13 @@ static void process (menu_t *menu) {
menu->browser.valid = false; menu->browser.valid = false;
menu_show_error(menu, "Couldn't open last directory"); menu_show_error(menu, "Couldn't open last directory");
} }
sound_play_effect(SFX_EXIT);
} else if (menu->actions.options && menu->browser.entry) { } else if (menu->actions.options && menu->browser.entry) {
component_context_menu_show(&entry_context_menu); component_context_menu_show(&entry_context_menu);
sound_play_effect(SFX_SETTING);
} else if (menu->actions.settings) { } else if (menu->actions.settings) {
component_context_menu_show(&settings_context_menu); component_context_menu_show(&settings_context_menu);
sound_play_effect(SFX_SETTING);
} }
} }

View File

@ -1,5 +1,5 @@
#include "views.h" #include "views.h"
#include "../sound.h"
#ifndef MENU_VERSION #ifndef MENU_VERSION
#define MENU_VERSION "Unknown" #define MENU_VERSION "Unknown"
@ -13,6 +13,7 @@
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} }
} }

View File

@ -1,9 +1,11 @@
#include "views.h" #include "views.h"
#include "../sound.h"
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} }
} }
@ -48,6 +50,7 @@ void view_error_display (menu_t *menu, surface_t *display) {
} }
void menu_show_error (menu_t *menu, char *error_message) { void menu_show_error (menu_t *menu, char *error_message) {
sound_play_effect(SFX_ERROR);
menu->next_mode = MENU_MODE_ERROR; menu->next_mode = MENU_MODE_ERROR;
menu->error_message = error_message; menu->error_message = error_message;
} }

View File

@ -1,4 +1,5 @@
#include <sys/stat.h> #include <sys/stat.h>
#include "../sound.h"
#include "utils/fs.h" #include "utils/fs.h"
#include "views.h" #include "views.h"
@ -12,8 +13,8 @@ static const char *patch_extensions[] = { "aps", "bps", "ips", "pps", "ups", "xd
static const char *archive_extensions[] = { "zip", "rar", "7z", "tar", "gz", NULL }; static const char *archive_extensions[] = { "zip", "rar", "7z", "tar", "gz", NULL };
static const char *image_extensions[] = { "png", "jpg", "gif", NULL }; static const char *image_extensions[] = { "png", "jpg", "gif", NULL };
static const char *music_extensions[] = { "mp3", "wav", "ogg", "wma", "flac", NULL }; static const char *music_extensions[] = { "mp3", "wav", "ogg", "wma", "flac", NULL };
static const char *dexdrive_extensions[] = { "mpk", NULL }; static const char *controller_pak_extensions[] = { "mpk", "pak", NULL };
static const char *emulator_extensions[] = { "emu", NULL }; static const char *emulator_extensions[] = { "nes", "smc", "gb", "gbc", "sms", "gg", NULL };
static struct stat st; static struct stat st;
@ -38,10 +39,10 @@ static char *format_file_type (char *name, bool is_directory) {
return " Type: Image file\n"; return " Type: Image file\n";
} else if (file_has_extensions(name, music_extensions)) { } else if (file_has_extensions(name, music_extensions)) {
return " Type: Music file\n"; return " Type: Music file\n";
} else if (file_has_extensions(name, dexdrive_extensions)) { } else if (file_has_extensions(name, controller_pak_extensions)) {
return " Type: DexDrive CPak backup file\n"; return " Type: Controller Pak file\n";
} else if (file_has_extensions(name, emulator_extensions)) { } else if (file_has_extensions(name, emulator_extensions)) {
return " Type: Emulator file\n"; return " Type: Emulator ROM file\n";
} }
return " Type: Unknown file\n"; return " Type: Unknown file\n";
} }
@ -50,6 +51,7 @@ static char *format_file_type (char *name, bool is_directory) {
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} }
} }

View File

@ -1,9 +1,11 @@
#include "views.h" #include "views.h"
#include "../sound.h"
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} }
} }
@ -17,8 +19,17 @@ static void draw (menu_t *menu, surface_t *d) {
component_main_text_draw( component_main_text_draw(
ALIGN_CENTER, VALIGN_TOP, ALIGN_CENTER, VALIGN_TOP,
"FLASHCART INFORMATION\n" "FLASHCART INFORMATION\n"
"\n"
"\n"
"This feature is not yet supported.\n\n"
); );
// FIXME: Display:
// * cart_type
// * Firmware version
// * supported features (flashcart_features_t)
component_main_text_draw( component_main_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"\n" "\n"

View File

@ -1,4 +1,5 @@
#include <stdlib.h> #include <stdlib.h>
#include "../sound.h"
#include "../png_decoder.h" #include "../png_decoder.h"
#include "views.h" #include "views.h"
@ -40,6 +41,7 @@ static void process (menu_t *menu) {
} else { } else {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
} }
sound_play_effect(SFX_EXIT);
} else if (menu->actions.enter && image) { } else if (menu->actions.enter && image) {
if (show_message) { if (show_message) {
show_message = false; show_message = false;
@ -48,6 +50,7 @@ static void process (menu_t *menu) {
} else { } else {
show_message = true; show_message = true;
} }
sound_play_effect(SFX_ENTER);
} }
} }

View File

@ -1,11 +1,13 @@
#include "../cart_load.h" #include "../cart_load.h"
#include "../disk_info.h" #include "../disk_info.h"
#include "boot/boot.h" #include "boot/boot.h"
#include "../sound.h"
#include "views.h" #include "views.h"
static bool load_pending; static bool load_pending;
static bool load_rom; static bool load_rom;
static component_boxart_t *boxart;
static char *convert_error_message (disk_err_t err) { static char *convert_error_message (disk_err_t err) {
@ -34,8 +36,10 @@ static void process (menu_t *menu) {
} else if (menu->actions.options && menu->load.rom_path) { } else if (menu->actions.options && menu->load.rom_path) {
load_pending = true; load_pending = true;
load_rom = true; load_rom = true;
sound_play_effect(SFX_SETTING);
} else if (menu->actions.back) { } else if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} }
} }
@ -89,6 +93,8 @@ static void draw (menu_t *menu, surface_t *d) {
"R: Load with ROM" "R: Load with ROM"
); );
} }
component_boxart_draw(boxart);
} }
rdpq_detach_show(); rdpq_detach_show();
@ -145,6 +151,9 @@ static void load (menu_t *menu) {
} }
} }
static void deinit (void) {
component_boxart_free(boxart);
}
void view_load_disk_init (menu_t *menu) { void view_load_disk_init (menu_t *menu) {
if (menu->load.disk_path) { if (menu->load.disk_path) {
@ -160,6 +169,8 @@ void view_load_disk_init (menu_t *menu) {
if (err != DISK_OK) { if (err != DISK_OK) {
menu_show_error(menu, convert_error_message(err)); menu_show_error(menu, convert_error_message(err));
} }
boxart = component_boxart_init(menu->storage_prefix, menu->load.disk_info.id);
} }
void view_load_disk_display (menu_t *menu, surface_t *display) { void view_load_disk_display (menu_t *menu, surface_t *display) {
@ -171,4 +182,8 @@ void view_load_disk_display (menu_t *menu, surface_t *display) {
load_pending = false; load_pending = false;
load(menu); load(menu);
} }
if (menu->next_mode != MENU_MODE_LOAD_DISK) {
deinit();
}
} }

View File

@ -1,6 +1,7 @@
#include "../cart_load.h" #include "../cart_load.h"
#include "boot/boot.h" #include "boot/boot.h"
#include "utils/fs.h" #include "utils/fs.h"
#include "../sound.h"
#include "views.h" #include "views.h"
@ -36,6 +37,7 @@ static void process (menu_t *menu) {
load_pending = true; load_pending = true;
} else if (menu->actions.back) { } else if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} }
} }

View File

@ -1,16 +1,18 @@
#include "../cart_load.h" #include "../cart_load.h"
#include "../rom_info.h" #include "../rom_info.h"
#include "boot/boot.h" #include "boot/boot.h"
#include "../sound.h"
#include "views.h" #include "views.h"
static bool show_extra_info_message = false;
static bool load_pending; static bool load_pending;
static component_boxart_t *boxart; static component_boxart_t *boxart;
static char *convert_error_message (rom_err_t err) { static char *convert_error_message (rom_err_t err) {
switch (err) { switch (err) {
case ROM_ERR_IO: return "I/O error during loading/storing ROM information"; case ROM_ERR_LOAD_IO: return "I/O error during loading ROM information and/or options";
case ROM_ERR_SAVE_IO: return "I/O error during storing ROM options";
case ROM_ERR_NO_FILE: return "Couldn't open ROM file"; case ROM_ERR_NO_FILE: return "Couldn't open ROM file";
default: return "Unknown ROM info load error"; default: return "Unknown ROM info load error";
} }
@ -64,16 +66,16 @@ static const char *format_rom_destination_market (rom_destination_type_t market_
} }
} }
static const char *format_rom_save_type (rom_save_type_t save_type) { static const char *format_rom_save_type (rom_save_type_t save_type, bool supports_cpak) {
switch (save_type) { switch (save_type) {
case SAVE_TYPE_NONE: return "None"; case SAVE_TYPE_NONE: return supports_cpak ? "Controller PAK" : "None";
case SAVE_TYPE_EEPROM_4K: return "EEPROM 4K"; case SAVE_TYPE_EEPROM_4KBIT: return supports_cpak ? "EEPROM 4kbit | Controller PAK" : "EEPROM 4kbit";
case SAVE_TYPE_EEPROM_16K: return "EEPROM 16K"; case SAVE_TYPE_EEPROM_16KBIT: return supports_cpak ? "EEPROM 16kbit | Controller PAK" : "EEPROM 16kbit";
case SAVE_TYPE_SRAM: return "SRAM"; case SAVE_TYPE_SRAM_256KBIT: return supports_cpak ? "SRAM 256kbit | Controller PAK" : "SRAM 256kbit";
case SAVE_TYPE_SRAM_BANKED: return "SRAM Banked"; case SAVE_TYPE_SRAM_BANKED: return supports_cpak ? "SRAM 768kbit / 3 banks | Controller PAK" : "SRAM 768kbit / 3 banks";
case SAVE_TYPE_SRAM_128K: return "SRAM 128K"; case SAVE_TYPE_SRAM_1MBIT: return supports_cpak ? "SRAM 1Mbit | Controller PAK" : "SRAM 1Mbit";
case SAVE_TYPE_FLASHRAM: return "FlashRAM"; case SAVE_TYPE_FLASHRAM_1MBIT: return supports_cpak ? "FlashRAM 1Mbit | Controller PAK" : "FlashRAM 1Mbit";
case SAVE_TYPE_FLASHRAM_PKST2: return "FlashRAM (Pokemon Stadium 2)"; case SAVE_TYPE_FLASHRAM_PKST2: return supports_cpak ? "FlashRAM (Pokemon Stadium 2) | Controller PAK" : "FlashRAM (Pokemon Stadium 2)";
default: return "Unknown"; default: return "Unknown";
} }
} }
@ -164,12 +166,12 @@ static component_context_menu_t set_cic_type_context_menu = { .list = {
static component_context_menu_t set_save_type_context_menu = { .list = { static component_context_menu_t set_save_type_context_menu = { .list = {
{ .text = "Automatic", .action = set_save_type, .arg = (void *) (SAVE_TYPE_AUTOMATIC) }, { .text = "Automatic", .action = set_save_type, .arg = (void *) (SAVE_TYPE_AUTOMATIC) },
{ .text = "None", .action = set_save_type, .arg = (void *) (SAVE_TYPE_NONE) }, { .text = "None", .action = set_save_type, .arg = (void *) (SAVE_TYPE_NONE) },
{ .text = "EEPROM 4kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_EEPROM_4K) }, { .text = "EEPROM 4kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_EEPROM_4KBIT) },
{ .text = "EEPROM 16kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_EEPROM_16K) }, { .text = "EEPROM 16kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_EEPROM_16KBIT) },
{ .text = "SRAM 256kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM) }, { .text = "SRAM 256kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM_256KBIT) },
{ .text = "SRAM 768kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM_BANKED) }, { .text = "SRAM 768kbit / 3 banks", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM_BANKED) },
{ .text = "SRAM 1Mbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM_128K) }, { .text = "SRAM 1Mbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM_1MBIT) },
{ .text = "FlashRAM 1Mbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_FLASHRAM) }, { .text = "FlashRAM 1Mbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_FLASHRAM_1MBIT) },
COMPONENT_CONTEXT_MENU_LIST_END, COMPONENT_CONTEXT_MENU_LIST_END,
}}; }};
@ -197,8 +199,17 @@ static void process (menu_t *menu) {
load_pending = true; load_pending = true;
} else if (menu->actions.back) { } else if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} else if (menu->actions.options) { } else if (menu->actions.options) {
component_context_menu_show(&options_context_menu); component_context_menu_show(&options_context_menu);
sound_play_effect(SFX_SETTING);
} else if (menu->actions.lz_context) {
if (show_extra_info_message) {
show_extra_info_message = false;
} else {
show_extra_info_message = true;
}
sound_play_effect(SFX_SETTING);
} }
} }
@ -226,52 +237,61 @@ static void draw (menu_t *menu, surface_t *d) {
"\n" "\n"
"\n" "\n"
"\n" "\n"
" Endianness: %s\n" "Description:\n None.\n\n\n\n\n\n\n\n"
" Title: %.20s\n" "Expansion PAK: %s\n"
" Game code: %c%c%c%c\n" "TV type: %s\n"
" Media type: %s\n" "CIC: %s\n"
" Destination market: %s\n" "GS/AR Cheats: Off\n"
" Version: %hhu\n" "Patches: Off\n"
" Check code: 0x%016llX\n" "Save type: %s\n",
" Save type: %s\n"
" TV type: %s\n"
" Expansion PAK: %s\n"
"\n"
" Extra information:\n"
" CIC: %s\n"
" Boot address: 0x%08lX\n"
" SDK version: %.1f%c\n"
" Clock Rate: %.2fMHz\n",
format_rom_endianness(menu->load.rom_info.endianness),
menu->load.rom_info.title,
menu->load.rom_info.game_code[0], menu->load.rom_info.game_code[1], menu->load.rom_info.game_code[2], menu->load.rom_info.game_code[3],
format_rom_media_type(menu->load.rom_info.category_code),
format_rom_destination_market(menu->load.rom_info.destination_code),
menu->load.rom_info.version,
menu->load.rom_info.check_code,
format_rom_save_type(rom_info_get_save_type(&menu->load.rom_info)),
format_rom_tv_type(rom_info_get_tv_type(&menu->load.rom_info)),
format_rom_expansion_pak_info(menu->load.rom_info.features.expansion_pak), format_rom_expansion_pak_info(menu->load.rom_info.features.expansion_pak),
format_rom_tv_type(rom_info_get_tv_type(&menu->load.rom_info)),
format_cic_type(rom_info_get_cic_type(&menu->load.rom_info)), format_cic_type(rom_info_get_cic_type(&menu->load.rom_info)),
menu->load.rom_info.boot_address, format_rom_save_type(rom_info_get_save_type(&menu->load.rom_info), menu->load.rom_info.features.controller_pak)
(menu->load.rom_info.libultra.version / 10.0f), menu->load.rom_info.libultra.revision,
menu->load.rom_info.clock_rate
); );
component_actions_bar_text_draw( component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"A: Load and run ROM\n" "A: Load and run ROM\n"
"B: Exit" "B: Back"
); );
component_actions_bar_text_draw( component_actions_bar_text_draw(
ALIGN_RIGHT, VALIGN_TOP, ALIGN_RIGHT, VALIGN_TOP,
"\n" "L|Z: Extra Info\n"
"R: Options" "R: Options"
); );
component_boxart_draw(boxart); component_boxart_draw(boxart);
if (show_extra_info_message) {
component_messagebox_draw(
"EXTRA ROM INFO\n"
"\n"
"Endianness: %s\n"
"Title: %.20s\n"
"Game code: %c%c%c%c\n"
"Media type: %s\n"
"Variant: %s\n"
"Version: %hhu\n"
"Check code: 0x%016llX\n"
"Boot address: 0x%08lX\n"
"SDK version: %.1f%c\n"
"Clock Rate: %.2fMHz\n\n\n"
"Press L|Z to return.\n",
format_rom_endianness(menu->load.rom_info.endianness),
menu->load.rom_info.title,
menu->load.rom_info.game_code[0], menu->load.rom_info.game_code[1], menu->load.rom_info.game_code[2], menu->load.rom_info.game_code[3],
format_rom_media_type(menu->load.rom_info.category_code),
format_rom_destination_market(menu->load.rom_info.destination_code),
menu->load.rom_info.version,
menu->load.rom_info.check_code,
menu->load.rom_info.boot_address,
(menu->load.rom_info.libultra.version / 10.0f), menu->load.rom_info.libultra.revision,
menu->load.rom_info.clock_rate
);
}
component_context_menu_draw(&options_context_menu); component_context_menu_draw(&options_context_menu);
} }

View File

@ -42,11 +42,13 @@ static void process (menu_t *menu) {
menu_show_error(menu, convert_error_message(err)); menu_show_error(menu, convert_error_message(err));
} else if (menu->actions.back) { } else if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} else if (menu->actions.enter) { } else if (menu->actions.enter) {
err = mp3player_toggle(); err = mp3player_toggle();
if (err != MP3PLAYER_OK) { if (err != MP3PLAYER_OK) {
menu_show_error(menu, convert_error_message(err)); menu_show_error(menu, convert_error_message(err));
} }
sound_play_effect(SFX_ENTER);
} else if (menu->actions.go_left || menu->actions.go_right) { } else if (menu->actions.go_left || menu->actions.go_right) {
int seconds = menu->actions.go_fast ? SEEK_SECONDS_FAST : SEEK_SECONDS; int seconds = menu->actions.go_fast ? SEEK_SECONDS_FAST : SEEK_SECONDS;
err = mp3player_seek(menu->actions.go_left ? (-seconds) : seconds); err = mp3player_seek(menu->actions.go_left ? (-seconds) : seconds);

View File

@ -1,4 +1,5 @@
#include <time.h> #include <time.h>
#include "../sound.h"
#include "views.h" #include "views.h"
// FIXME: add implementation! // FIXME: add implementation!
@ -18,6 +19,7 @@
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} }
} }
@ -34,7 +36,7 @@ static void draw (menu_t *menu, surface_t *d) {
"\n" "\n"
"\n" "\n"
"To set the date and time, please use the PC terminal\n" "To set the date and time, please use the PC terminal\n"
"application and set via USB.\n\n" "application and set via USB or a game that uses it.\n\n"
"Current date & time: %s\n", "Current date & time: %s\n",
menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown\n" menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown\n"
); );

View File

@ -1,3 +1,6 @@
#include <stdbool.h>
#include "../sound.h"
#include "../settings.h"
#include "views.h" #include "views.h"
@ -8,10 +11,111 @@ static const char *format_switch (bool state) {
} }
} }
static void set_pal60_type (menu_t *menu, void *arg) {
menu->settings.pal60_enabled = (bool) (arg);
settings_save(&menu->settings);
}
static void set_protected_entries_type (menu_t *menu, void *arg) {
menu->settings.show_protected_entries = (bool) (arg);
settings_save(&menu->settings);
menu->browser.reload = true;
}
static void set_use_saves_folder_type (menu_t *menu, void *arg) {
menu->settings.use_saves_folder = (bool) (arg);
settings_save(&menu->settings);
}
static void set_sound_enabled_type (menu_t *menu, void *arg) {
menu->settings.sound_enabled = (bool) (arg);
sound_use_sfx(menu->settings.sound_enabled);
settings_save(&menu->settings);
}
#ifdef BETA_SETTINGS
static void set_bgm_enabled_type (menu_t *menu, void *arg) {
menu->settings.bgm_enabled = (bool) (arg);
settings_save(&menu->settings);
}
static void set_rumble_enabled_type (menu_t *menu, void *arg) {
menu->settings.rumble_enabled = (bool) (arg);
settings_save(&menu->settings);
}
// static void set_use_default_settings (menu_t *menu, void *arg) {
// // FIXME: add implementation
// menu->browser.reload = true;
// }
#endif
static component_context_menu_t set_pal60_type_context_menu = { .list = {
{.text = "On", .action = set_pal60_type, .arg = (void *) (true) },
{.text = "Off", .action = set_pal60_type, .arg = (void *) (false) },
COMPONENT_CONTEXT_MENU_LIST_END,
}};
static component_context_menu_t set_protected_entries_type_context_menu = { .list = {
{.text = "On", .action = set_protected_entries_type, .arg = (void *) (true) },
{.text = "Off", .action = set_protected_entries_type, .arg = (void *) (false) },
COMPONENT_CONTEXT_MENU_LIST_END,
}};
static component_context_menu_t set_sound_enabled_type_context_menu = { .list = {
{.text = "On", .action = set_sound_enabled_type, .arg = (void *) (true) },
{.text = "Off", .action = set_sound_enabled_type, .arg = (void *) (false) },
COMPONENT_CONTEXT_MENU_LIST_END,
}};
static component_context_menu_t set_use_saves_folder_type_context_menu = { .list = {
{.text = "On", .action = set_use_saves_folder_type, .arg = (void *) (true) },
{.text = "Off", .action = set_use_saves_folder_type, .arg = (void *) (false) },
COMPONENT_CONTEXT_MENU_LIST_END,
}};
#ifdef BETA_SETTINGS
static component_context_menu_t set_bgm_enabled_type_context_menu = { .list = {
{.text = "On", .action = set_bgm_enabled_type, .arg = (void *) (true) },
{.text = "Off", .action = set_bgm_enabled_type, .arg = (void *) (false) },
COMPONENT_CONTEXT_MENU_LIST_END,
}};
static component_context_menu_t set_rumble_enabled_type_context_menu = { .list = {
{.text = "On", .action = set_rumble_enabled_type, .arg = (void *) (true) },
{.text = "Off", .action = set_rumble_enabled_type, .arg = (void *) (false) },
COMPONENT_CONTEXT_MENU_LIST_END,
}};
#endif
static component_context_menu_t options_context_menu = { .list = {
{ .text = "PAL60 Mode", .submenu = &set_pal60_type_context_menu },
{ .text = "Show Hidden Files", .submenu = &set_protected_entries_type_context_menu },
{ .text = "Sound Effects", .submenu = &set_sound_enabled_type_context_menu },
{ .text = "Use Saves Folder", .submenu = &set_use_saves_folder_type_context_menu },
#ifdef BETA_SETTINGS
{ .text = "Background Music", .submenu = &set_bgm_enabled_type_context_menu },
{ .text = "Rumble Feedback", .submenu = &set_rumble_enabled_type_context_menu },
// { .text = "Restore Defaults", .action = set_use_default_settings },
#endif
COMPONENT_CONTEXT_MENU_LIST_END,
}};
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (component_context_menu_process(menu, &options_context_menu)) {
return;
}
if (menu->actions.enter) {
component_context_menu_show(&options_context_menu);
sound_play_effect(SFX_SETTING);
} else if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} }
} }
@ -24,44 +128,54 @@ static void draw (menu_t *menu, surface_t *d) {
component_main_text_draw( component_main_text_draw(
ALIGN_CENTER, VALIGN_TOP, ALIGN_CENTER, VALIGN_TOP,
"SETTINGS EDITOR\n" "MENU SETTINGS EDITOR\n"
"\n" "\n"
); );
component_main_text_draw( component_main_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"\n" "\n\n"
"\n" " Default Directory : %s\n\n"
"To change the settings, please adjust them\n" "To change the following menu settings, press 'A':\n"
"directly in the 'menu/config.ini' file.\n\n" "* PAL60 Mode : %s\n"
"pal60_enabled: %s\n" " Show Hidden Files : %s\n"
"show_protected_entries: %s\n" " Use Saves folder : %s\n"
"default_directory: %s\n" " Sound Effects : %s\n"
"use_saves_folder: %s\n" #ifdef BETA_SETTINGS
"bgm_enabled: %s\n" " Background Music : %s\n"
"sound_enabled: %s\n" " Rumble Feedback : %s\n"
"rumble_enabled: %s\n", #endif
"Note: Certain settings have the following caveats:\n\n"
"* Requires a flashcart reboot.\n",
menu->settings.default_directory,
format_switch(menu->settings.pal60_enabled), format_switch(menu->settings.pal60_enabled),
format_switch(menu->settings.show_protected_entries), format_switch(menu->settings.show_protected_entries),
menu->settings.default_directory,
format_switch(menu->settings.use_saves_folder), format_switch(menu->settings.use_saves_folder),
format_switch(menu->settings.sound_enabled)
#ifdef BETA_SETTINGS
,
format_switch(menu->settings.bgm_enabled), format_switch(menu->settings.bgm_enabled),
format_switch(menu->settings.sound_enabled),
format_switch(menu->settings.rumble_enabled) format_switch(menu->settings.rumble_enabled)
#endif
); );
component_actions_bar_text_draw( component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"\n" "A: Change\n"
"B: Back" "B: Back"
); );
component_context_menu_draw(&options_context_menu);
rdpq_detach_show(); rdpq_detach_show();
} }
void view_settings_init (menu_t *menu) { void view_settings_init (menu_t *menu) {
// Nothing to initialize (yet)
component_context_menu_init(&options_context_menu);
} }
void view_settings_display (menu_t *menu, surface_t *display) { void view_settings_display (menu_t *menu, surface_t *display) {

View File

@ -1,5 +1,6 @@
#include <time.h> #include <time.h>
#include "../sound.h"
#include "views.h" #include "views.h"
@ -28,6 +29,7 @@ static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} }
} }

View File

@ -3,6 +3,7 @@
#include "../components/constants.h" #include "../components/constants.h"
#include "../fonts.h" #include "../fonts.h"
#include "../sound.h"
#include "utils/utils.h" #include "utils/utils.h"
#include "views.h" #include "views.h"
@ -55,6 +56,7 @@ static void perform_vertical_scroll (int lines) {
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
sound_play_effect(SFX_EXIT);
} else if (text) { } else if (text) {
if (menu->actions.go_up) { if (menu->actions.go_up) {
perform_vertical_scroll(menu->actions.go_fast ? -10 : -1); perform_vertical_scroll(menu->actions.go_fast ? -10 : -1);

View File

@ -1,7 +1,7 @@
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "fs.h" #include "fs.h"