Compare commits

..

97 Commits

Author SHA1 Message Date
Robin Jones
22515a6fdf
[main] Next release changes (#225)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->

### Release Notes 2025-03-31

- **New Features**
- Introduced tabs in main menu for ROM favorites and recently played ROM
history.
- Introduced first run check to ensure users are aware of latest
changes.
	- Introduced ability to turn off GUI loading bar.
	- BETA_FEATURE: Introduces ROM descriptions from files.
	- BETA_FEATURE: Enabled setting for fast ROM reboots on the SC64.
	- Add macOS metadata to hidden files.
	- Added settings schema version for future change versioning.
	- Added setting for PAL60 compatibility mode (see breaking changes).
- BETA_FEATURE: Added setting for line doublers that need progressive
output, enable using "force_progressive_scan" setting in `config.ini`.


- **Bug Fixes**
	- Menu sound FX issues (hissing, popping and white noise).
- RTC not showing or setting correct date parameters in certain
circumstances.
	- GB / GBC emulator not saving in certain circumstances.


- **Documentation**
	- Re-orginised and improved user documentation.
	- Added a lot of doxygen compatible code comments.
	- Added project license.


- **Refactor**
	- RTC subsystem (align with libDragon improvements).
	- Boxart images (Deprecates old boxart image folder layout).
- Settings (PAL60 compatibility, schema version, fast reboot, first run,
progress bar).

- **Other**
	- Updated libDragon SDK.
	- Updated miniz library.

### Breaking changes
* GB /GBC emulator changed save type to SRAM (from FRAM) to improve
compatibility with Summercart64 (which only uses H/W compatible FRAM),
this may break your ability to load existing saves.
* For similar PAL60 functionality, you may need to also enable the new
"pal60_compatibility_mode" setting in `config.ini`.


### Current known Issues
* The RTC UI requires improvement (awaiting UI developer).
* Menu sound FX may not work properly when a 64 Disk Drive is also
attached (work around: turn sound FX off).
* Fast Rebooting a 64DD disk once will result in a blank screen. Twice
will return to menu. This is expected until disk swapping is
implemented.
* MP3 Player crashes menu if the MP3 file's sample rate is less than
44100 hz.


### Deprecation notices
* Autoload ROM's will be deprecated in favor of Fast Reboot in a future
menu version.
* Old boxart images using filenames for game ID is deprecated and the
compatibility mode will be removed in a future release.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Works towards next release to main.

## How Has This Been Tested?
On a SummerCart64

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [x] Bug fix (fixes an issue)
- [x] Breaking change (breaking change)
- [x] Documentation Improvement
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **New Features & Enhancements**  
- Introduced dynamic history and favorites management for ROM and disk
selections with a new tabbed interface.
- Added support for autoloading ROMs, 64DD disk emulation, emulator
integration, MP3 playback, and custom background images.
- Expanded settings options—including PAL60 compatibility and fast ROM
reboots—and improved startup behavior with an introductory credits
display.
- Added a feature to toggle the loading progress bar and enhanced the
display of ROM information.
- Implemented a bookkeeping system for managing history and favorites,
along with new context menu entries for toggling settings.
- Added a new user guide for N64FlashcartMenu and introduced a FAQ
section addressing common issues.
- Enhanced the user interface with new tabs for managing ROM favorites
and recently played ROMs.
  - Introduced first run checks for user awareness of changes.

- **Bug Fixes**  
- Resolved issues related to menu sound effects, RTC date parameters,
and saving functionality in the GB/GBC emulator.

- **Documentation**  
- Overhauled and expanded user guides, FAQs, and README materials to
provide clearer instructions on ROM configuration, cheats, flashcart
support, and new features.
- Added detailed documentation comments throughout the codebase to
improve clarity and maintainability, including updates to the pull
request template and license information.
- Included a new section in the CHANGELOG detailing various updates and
breaking changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
Co-authored-by: Suprapote <111246491+Suprapote@users.noreply.github.com>
Co-authored-by: Christopher Bonhage <me@christopherbonhage.com>
Co-authored-by: Mateusz Faderewski <sc@mateuszfaderewski.pl>
Co-authored-by: Fazana <52551480+FazanaJ@users.noreply.github.com>
Co-authored-by: Guillermo Horacio Romero Villa <65469983+E1ite007@users.noreply.github.com>
Co-authored-by: Ross Gouldthorpe <github@thegouldfish.co.uk>
Co-authored-by: Víctor "IlDucci <IlDucci@users.noreply.github.com>
Co-authored-by: XLuma <39510265+XLuma@users.noreply.github.com>
Co-authored-by: thekovic <72971433+thekovic@users.noreply.github.com>
2025-03-31 16:28:20 +01:00
Robin Jones
276efa38ea Minor readme improvements
Co-Authored-By: Robin Jones <networkfusion@users.noreply.github.com>
2025-01-25 00:06:48 +00:00
Robin Jones
8a6df4210e [main] Readme improvements 2025-01-16 23:52:40 +00:00
Robin Jones
3eccdfa18c
[main] Hotfix display issues (#189)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Fixed menu display (PAL60) by reverted libdragon to a known working
point and re-applying old hacks.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Media coverage. #183 #184

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
Tested locally on a SC64 on an
* NTSC console (composite only)
* PAL console (composite)
* PAL console (HDMI mod with Pal60 enabled).

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [x] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2025-01-10 22:18:46 +00:00
Robin Jones
52599e92e0
[main] Add changelog (#188)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Add changelog so that rolling release description does not need to be
updated manually.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Hard to maintain.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

- **New Features**
	- Added menu sound effects (default off)
	- Introduced N64 ROM autoload functionality
	- Implemented a menu boot hotkey
	- Added a context menu for settings management
	- Enhanced real-time clock (RTC) editing capabilities
	- Improved flashcart information display
	- Optimized joypad input handling for responsiveness
	- Added support for additional ROM types (`SMS`, `GG`, and `CHF`)

- **Improvements**
	- Enhanced UI layout with new drawing functions and better organization
	- Optimized boxart image loading
	- Improved memory management to prevent leaks

- **Bug Fixes**
	- Enhanced error handling in save loading and ROM management
	- Fixed text flickering issues

- **Documentation**
	- Updated CHANGELOG with latest release notes
	- Revised README for new features and setup instructions
	- Improved clarity regarding RTC settings and menu customization

- **Breaking Changes**
- Loading of disk drive expansion ROMs now requires a different key
combination
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-01-10 21:59:19 +00:00
Robin Jones
d18b669b8f
[main] Improve bug_report template (#185)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Improve the bug report template.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Recently reported issues lacked the required content for diagnostics.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Documentation**
- Updated GitHub issue bug report template with new optional input
fields
- Added fields for N64 console region and configuration file details to
help improve issue reporting clarity

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-01-08 19:49:14 +00:00
floppyD
30a0f2925d
Update README.md (#182)
Fixes some typos

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Documentation**
	- Corrected grammatical errors and typos in README.md
	- Fixed capitalization and formatting issues
	- Improved overall readability of documentation

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Robin Jones <networkfusion@users.noreply.github.com>
2025-01-04 18:16:31 +00:00
Víctor "IlDucci
75b14b6c20
[main] Improve documentation (#179)
## Description
Documentaion has been rewritten/improved/expanded in certain areas
including typo and formatting fixes.

## Motivation and Context
Tidier documentation, to make things a bit more dumb-proof for end
users.

## How Has This Been Tested?
Not applicable.

## Types of changes
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [x] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

Signed-off-by: IlDucci

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **Documentation**
- Updated documentation for initial SD card setup, menu controls, and
menu customization.
	- Improved clarity and readability of instructions.
- Refined section headings and formatting across multiple documentation
files.
- Added new sections explaining menu settings, ROM information, and
control details.
- Emphasized warnings regarding filename compatibility and save file
issues.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-12-31 21:33:30 +00:00
Robin Jones
9113d1c949
[main] Next release changes (#162)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
This PR merges `develop` and `main` for a tagged release so it can be
used by most users (utilising debug flags where required).

## Release Notes

- **New Features**
- Introduced menu sound effects for enhanced user experience (the
default is off).
- Added N64 ROM autoload functionality, allowing users to set a specific
ROM to load automatically.
- Added menu boot hotkey (hold `start` to return to menu when autoload
is enabled).
- Added context menu and settings management options GUI for managing
various settings in `config.ini`.
- Added functionality for editing the real-time clock (RTC) within the
RTC menu view.
- Improved flashcart info view for showing supported flashcart features
and version.
- Enhanced UI components with new drawing functions and improved
organization.
	- Added emulator support for `SMS`, `GG`, and `CHF` ROMs.
- Enhanced joypad input handling for menu actions, improving
responsiveness.
	- Optimized boxart image loading from filesystem.
	- Improved various text to make the functionality more clear.

- **Bug Fixes**
- Improved error handling in multiple areas, particularly in save
loading and ROM management.
- Enhanced memory management to prevent potential leaks during error
conditions.
	- Fixed text flickering in certain circumstances.

- **Documentation**
- Updated README and various documentation files to reflect new features
and usage instructions.
- Added detailed setup instructions for SD cards and menu customization.
- Enhanced clarity in documentation for RTC settings and menu
customization.
- Improved organization and clarity of SD card setup instructions for
various flashcarts.

- **Refactor**
- Standardized naming conventions across UI components for better
organization.
- Restructured sound management and input handling for improved
responsiveness.
- Streamlined the loading state management for ROMs and disks within the
menu system.
- Improved clarity and usability of the developer guide and other
documentation files.

### Current known Issues
* The RTC UI requires improvement (awaiting UI developer).
* BETA_SETTING: PAL60 when using HDMI mods has regressed (awaiting
libdragon fix).
* ALPHA_FEATURE: ED64 X Series detection does not occur properly
(however this is not a problem as not tag released asset).
* ALPHA_FEATURE: ED64 V Series only supports loading ROMs (however this
is not a problem as not tag released asset).
* Menu sound FX may not work properly when a 64 Disk Drive is also
attached (work around: turn sound FX off).

### Breaking changes
* Disk drive expansion ROMs are now loaded with `Z|L` instead of `R` to
align with ROM info context menu (and future functionality).

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Merge the changes:
#110 
#106 
#101 
#89 
#74
and other improvements.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
Locally on an SC64.

## Screenshots
<!-- (if appropriate): -->

![image](https://github.com/user-attachments/assets/799fc21c-8743-4c21-8449-b4f531d5c62c)


## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [x] Bug fix (fixes an issue)
- [x] Breaking change (breaking change)
- [x] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **New Features**
- Enhanced documentation for N64 Flashcart Menu, including new emulator
support and features like menu sound effects and N64 ROM autoload.
- Added support for additional flashcart features and firmware version
retrieval in the flashcart subsystem.
- Introduced new settings for sound effects and ROM autoloading in the
menu system.

- **Bug Fixes**
- Improved error handling in various menu functions to prevent memory
leaks and ensure proper resource management.

- **Refactor**
- Updated naming conventions for UI component functions to improve
clarity and organization.
- Restructured the sound management functionality to enhance user
interaction feedback.

- **Documentation**
- Expanded sections in README and other documentation files for clarity
on emulator support and usage instructions.

- **Chores**
- Updated dependencies and version references in Dockerfile and other
configuration files.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
Co-authored-by: Suprapote <111246491+Suprapote@users.noreply.github.com>
Co-authored-by: Christopher Bonhage <me@christopherbonhage.com>
Co-authored-by: Mateusz Faderewski <sc@mateuszfaderewski.pl>
Co-authored-by: Fazana <52551480+FazanaJ@users.noreply.github.com>
Co-authored-by: Guillermo Horacio Romero Villa <65469983+E1ite007@users.noreply.github.com>
2024-12-30 15:08:28 +00:00
Robin Jones
49ea127dd7
Integer type fixes (#145)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Improve integer type under certain conditions.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
When running the flags `-Wall -Wextra`.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved handling of loop variables to prevent negative overflow in
various functions, enhancing stability and reliability when processing
larger values.
  
- **Refactor**
- Updated loop variable types to `unsigned int` for better performance
and accuracy in ROM data handling across multiple components.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-10-20 15:52:02 +01:00
Robin Jones
c49b9ed330
Update docs for newer firmware (#143)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Update the "supported" SC64 firmware version.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Although the latest firmware is not "required", it is best to keep it
aligned.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Updated the supported firmware version for "SummerCart64" to 2.20.0+
in error messages.
- **Documentation**
- Revised developer documentation to reflect the upgrade of the SC64
deployer to v2.20.0, including updated links and compatibility
requirements.
- Clarified instructions for using the dev container and added a
workaround for USB device communication.
	- Expanded guidance on generating and serving documentation locally.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-09-30 22:16:40 +01:00
Robin Jones
305a15d114
Update 00_getting_started_sd.md
Fix missing backslash
2024-09-02 20:13:17 +01:00
Robin Jones
3b2fddcc2a
Fix Perfect Dark JP DB entry (#139)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Perfect Dark (JP) release.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Database entry (requires Expansion Pak).

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->
https://imgur.com/a/perfect-dark-without-expansion-vPOYVMB

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added detailed match information for the game "Perfect Dark,"
enhancing recognition and handling by the system.
- **Improvements**
- Updated the entry for "Perfect Dark" to improve database organization
and clarity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-09-02 19:06:51 +01:00
Robin Jones
6d1f3570c7
Update build.yml
Fix CI
2024-08-04 20:28:03 +01:00
Robin Jones
e65f46625b
Remove ED64 from rolling release (#132)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Remove ED64/ED64P from rolling release.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
It is not ready and causes confusion.
It is still available in the dev release.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
- Updated build workflow to exclude specific output files from
processing, which may impact the overall build process.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-08-04 20:24:59 +01:00
Robin Jones
c68be66236
Improve docs (#131)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Improve documentation.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [x] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit


- **Documentation**
- Updated URLs in the README for direct access to firmware downloads for
SC64 and 64drive.
- Enhanced hyperlink for the releases page in the Getting Started guide
for better user accessibility.
- Added a new section with a quickstart video tutorial in the Developer
Guide to assist new developers.
- Improved clarity and formatting in the Developer Guide for
installation prerequisites and webpage serving instructions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-08-04 19:50:12 +01:00
Robin Jones
f10097de70
Improve documentation (#128)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Split documentation into chunks that users can understand.
Improve them to be more idiot proof!

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Allows an easier way to point people towards specific documentation.
#127 
#76 

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [x] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2024-08-04 17:56:54 +01:00
Robin Jones
8b624f684f
Update PULL_REQUEST_TEMPLATE.md
***no_ci***
2024-08-01 15:31:23 +01:00
Robin Jones
1e70188ede
Improve rumble default state (#117)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
When loading the menu, ensure that any connected rumble paks are not
rumbling.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
It is possible that they would continue to rumble after a ROM has
crashed or the console was reset from a game where the accessory was
currently rumbling.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [x] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2024-07-03 23:12:18 +01:00
Robin Jones
0b612d0219
Improve emulators in readme.
Remove TotalSMS, it is currently broken and can lead to confusion.
Update URL's to release page or specify action.
2024-06-29 19:33:18 +01:00
Christopher Bonhage
ee615254ac
Update save type labels to use unambigous units (#114)
<!--- Provide a general summary of your changes in the Title above -->

## Description
Prefer `kbit` and `Mbit` instead of ambiguous `K` for save type units

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Reduces confusion, notably the inconsistency between EEPROM using
kilobits and SRAM using kibibytes.

## How Has This Been Tested?
Tested on SummerCart64 with an NTSC console
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [X] Improvement (non-breaking change that adds a new feature)
- [X] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [X] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [X] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: meeq <me@christopherbonhage.com>
2024-06-13 00:09:13 +02:00
Christopher Bonhage
f044cd8621
Hide macOS system files in the root of the SD card (#113)
<!--- Provide a general summary of your changes in the Title above -->

## Description
Make the root of the SD card a little cleaner by hiding the crap that
macOS puts there.

## Motivation and Context
Whenever an SD card touches macOS, a handful of hidden files get created
that cannot be touched. N64FlashcartMenu has no need for these files, so
they should be hidden.
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->

## How Has This Been Tested?
Built and run on SummerCart64 with an NTSC N64
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [X] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [X] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: meeq <me@christopherbonhage.com>
2024-06-11 12:13:06 +02:00
Mateusz Faderewski
7fa60f4253 Fixed unnecessary error message when resulting ROM override config file was already empty 2024-06-02 19:24:36 +02:00
Mateusz Faderewski
3ffe7087b1 Merge branch 'main' into develop 2024-06-02 18:56:23 +02:00
Robin Jones
184d84872c
Update README.md
Add note about EXFAT support
2024-05-30 12:35:47 +01:00
Robin Jones
386906d38f
Correct hidden paths (#105)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
For ED64 based carts

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
The ROMs are not based in the root path.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2024-05-22 00:06:07 +02:00
Mateusz Faderewski
c920fe953d Fix github action build 2024-05-21 18:20:12 +02:00
Mateusz Faderewski
66466804de Fix github action build 2024-05-21 18:11:43 +02:00
Mateusz Faderewski
2dc83c49ed Add additional check in fatfs_get_file_sectors 2024-05-21 18:06:57 +02:00
Mateusz Faderewski
7670ffe6ba Fix issues with new font rendering in libdragon 2024-05-21 18:06:33 +02:00
Robin Jones
91f2e02d34
Add Dev rolling prerelease (#104)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Adds a pre-release (dev) release and branch.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Ensures testing before release to main.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2024-05-21 18:02:16 +02:00
Robin Jones
5bf16e29a8
Update libdragon submodule (#102)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Building using a dev container broke after the deb package was updated.

## Motivation and Context
Required for GCC 14.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2024-05-19 14:57:12 +01:00
Mateusz Faderewski
01968b55db
Cheats support (backend only) (#94)
<!--- Provide a general summary of your changes in the Title above -->

## Description
This PR implements cheat support (patcher + engine) with a simple API to
provide Action Replay/Game Shark compatible cheats (with exception of
cheats that utilize GS button).

API consist of a single pointer to an array of the cheats ended with a
double zero entry,
For example, if you want to pass these cheats to the patcher:
```
D01F9B91 0020 // Majora's Mask (USA) Inventory Editor
803FDA3F 0002
```
Put cheats in a `uint32_t` array as such (notice last two entries are
zeros):
```
uint32_t cheats[] = {
    0xD01F9B91,
    0x0020,
    0x803FDA3F,
    0x0002,
    0,
    0,
};
```
And pass this array as a boot parameter: `menu->boot_params->cheat_list
= cheats;`

<!--- Describe your changes in detail -->

## Motivation and Context
To provide users with ability to run game modifications in a easy way.
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->

## How Has This Been Tested?
On a SummerCart64 flashcart + assembly instructions generation verified
in ares emulator via GDB.
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
No screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: Polprzewodnikowy <sc@mateuszfaderewski.pl>
2024-05-05 00:19:20 +02:00
Robin Jones
24f49f1447
Update 64drive README.md
Use archived URL as website link no longer exists.
2024-05-01 22:43:45 +01:00
Robin Jones
9e7a47476e
Improve patch recognition 2024-04-29 22:42:33 +01:00
Robin Jones
4bcaced461
Add ares emulator support to readme
Fix libdragon branch used.
2024-04-29 20:14:59 +01:00
Robin Jones
4995fe55e1
Bump actions-gh-pages version (#99)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Bumps peaceiris/actions-gh-pages to latest release

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Keep workflow up-to-date.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>

[skip ci]
2024-04-29 18:57:02 +01:00
Robin Jones
e5c1f34142
Update rolling release GH action version (#98)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Bumps softprops/action-gh-release

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Although this might not solve it, currently our rolling release shows
the release date far behind that actually released.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2024-04-29 19:43:58 +02:00
Suprapote
9645ad1b5b
Add more saves (#97)
Add extra save type.
2024-04-29 18:06:23 +01:00
Suprapote
53fdcc5979
More savetypes (#96)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Add more saves to the list

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [X] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2024-04-29 16:41:59 +02:00
Mateusz Faderewski
6b8baa19e7
Link to the 64DD SummerCart64 guide in the readme 2024-04-29 14:08:31 +02:00
Mateusz Faderewski
23af174811 Implement vertical scrolling for text viewer 2024-04-27 13:12:09 +02:00
Mateusz Faderewski
a189e139b2 Extend character set in the font 2024-04-26 23:04:39 +02:00
Mateusz Faderewski
fc410da891 clang-format WIP 2024-04-26 23:04:19 +02:00
Mateusz Faderewski
cbe69e2659 Init BBFS on iQue 2024-04-26 23:03:15 +02:00
Mateusz Faderewski
3becd1ff59
Use stdio calls for file/directory interaction instead of fatfs (#95)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
This PR changes most of the calls to the fatfs lib with the standard C
ones.
Additionally, there's couple of changes required to adapt to new
interface and several bug fixes.
As a bonus menu can now be run in ares emulator and on the iQue player,
adapting to the available storage options - DragonFS in the ROM and iQue
flash modules (bbfs).

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
To make it easier to use storage medium other than SD cards on platforms
other than N64 with flashcart.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
SummerCart64 flashcart and ares emulator

## Screenshots
<!-- (if appropriate): -->
N/A

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [x] Bug fix (fixes an issue)
- [x] Breaking change (breaking change)
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: Polprzewodnikowy <sc@mateuszfaderewski.pl>
2024-04-24 02:45:09 +02:00
Robin Jones
c310c5f9e1
Update submodules (#93)
## Description
Updates libpng and libdragon to latest.
2024-03-19 17:11:22 +00:00
Robin Jones
6906cff1b2
Improve save note
Add caveats
2024-02-03 00:55:40 +00:00
Robin Jones
757ee22677
Documentation fixes 2024-02-03 00:49:58 +00:00
Robin Jones
2e962bbf22
Improve save documentation 2024-02-03 00:43:43 +00:00
Mateusz Faderewski
b283f48de2 Added color for text files 2024-01-19 04:21:05 +01:00
Robin Jones
14e45c0230
Add Text viewer (#87)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Adds a basic text viewer. It does not (yet) support full file content.
superseeds #21 

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
There were too many changes to merge from the original PR.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
On an SC64 via devcontainer.

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>

---------

Co-authored-by: Mateusz Faderewski <sc@mateuszfaderewski.pl>
Co-authored-by: Mateusz Faderewski <polprzewodnikowy@gmail.com>
2024-01-17 17:03:00 +01:00
Robin Jones
a2e50a9c82
Add flashcart submenu view (#86)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Adds a view to show the flashcart information

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Will be useful to show its features etc.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>

Co-authored-by: Mateusz Faderewski <sc@mateuszfaderewski.pl>
2024-01-17 15:54:30 +01:00
Robin Jones
cc46a3b9fb
Add USB feature for flashcart (#85)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Adds a feature to the flashcart to determine whether USB is supported.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Some flashcarts (ED64 X5, V2.5, V2.0, V1.0 & ED64P ) do not support it.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>

Co-authored-by: Mateusz Faderewski <sc@mateuszfaderewski.pl>
2024-01-17 15:51:10 +01:00
Robin Jones
d85f24a0d8
Add RTC feature for flashcart (#58)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Adds a feature to the flashcart to determine whether RTC is supported.
This is at minimum required for correct handling of what to show on the
main menu.
It also will help with a future context menu to set the RTC time.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Some flashcarts (ED64 X5, V2.5, V2.0, V1.0 & ED64P ) do not support it.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2024-01-17 15:47:31 +01:00
Mateusz Faderewski
80eb7bbd9c Added CIC selection menu + minor fixes and code cleanup 2024-01-17 15:43:31 +01:00
Robin Jones
0996e88fa1
Fix link
by removing it
2024-01-10 17:42:54 +00:00
Robin Jones
5532ae10a4
Update README.md
Improve supported carts
2024-01-10 17:20:50 +00:00
Robin Jones
ff478bccc7
Update .gitignore
Ignore whole tools folder, it might contain other flashcart tools such as UnfLoader
2024-01-10 16:06:20 +00:00
Mateusz Faderewski
661f89c870 Add CNAME to GitHub pages 2024-01-07 05:24:26 +01:00
Mateusz Faderewski
d2b2db1511 Fixed PAL60 mode + updated libdragon 2024-01-04 11:14:29 +01:00
Robin Jones
26ea9f0ea0
Improve settings editor text (#79)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Until settings editor is able to change settings, tell users what to do.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Improve user experience.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
Tested locally.

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-12-23 00:54:04 +00:00
Robin Jones
c4a6d1515d
Rtc context menu (#78)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Add basic RTC context menu.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Improves user experience.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-12-22 20:53:38 +00:00
Robin Jones
70675d085c
Minor improvements for maintainability (#77)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
* Update SC64 F/W version.
* Update build actions.
* Improve credits (use my name rather than twitter pseudonym).
* Improve function name for bool conversion.
* Use arg for SC64 tools version in dockerfile.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Improves code maintainability.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
Works on my local devcontainer, cart and fork.

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-12-22 20:50:54 +00:00
Robin Jones
be87588b85
Update some GH actions to latest. 2023-12-22 17:28:23 +00:00
Mateusz Faderewski
3846547707 Cleaned up navigation 2023-12-21 20:14:01 +01:00
Mateusz Faderewski
621e50cf7b Added manual override options for save and TV type 2023-12-21 19:58:30 +01:00
Mateusz Faderewski
ae37f92c7f Updated libdragon 2023-12-19 18:17:55 +01:00
Robin Jones
edad53805a
Update bug_report.yml 2023-12-17 21:13:41 +00:00
Robin Jones
c38f02ea7c
Update bug_report.yml 2023-12-17 21:11:39 +00:00
Robin Jones
157e1a530e
Update README.md 2023-12-17 20:57:45 +00:00
Robin Jones
10e5c3eed1
Add settings viewer (#23)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Adds a skeleton for enabling a settings editor.
For now, it just shows the settings (as a viewer).
Fixes descrepency with view for credits.
Improve text to show available actions on the browser.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
It will help others to finalise the editor implementation in future and
enables current users to view the settings.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-12-17 21:47:56 +01:00
Robin Jones
3e13e886c7
Add ability to force tv region based on detected ROM (#71)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Add ability to force tv region based on detected ROM based on the ROM's
destination code.
Adds a setting so that this can be turned off (on by default).

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
#66 

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-12-17 17:51:24 +01:00
Mateusz Faderewski
fc1aa31383 Remove inaccurate Aleck 64 CIC detection 2023-12-14 18:42:10 +01:00
Mateusz Faderewski
c866fd7dbb Updated libdragon 2023-12-12 15:54:28 +01:00
Mateusz Faderewski
2345c612d8 Moved assets to the DragonFS 2023-12-08 19:49:50 +01:00
Mateusz Faderewski
6ee348fa57 Updated libdragon (now incorporating open source IPL3) 2023-12-07 18:22:30 +01:00
Mateusz Faderewski
cceb37e694 Updated libdragon (now incorporating open source IPL3) 2023-12-07 18:20:49 +01:00
Mateusz Faderewski
8e50e4c1e1 Improved CIC detection algorithm
Now it incorporates the same algorithm as is included in the N64 IPL2. IPL3 that have checksum collision are now correctly detected.
2023-12-07 00:36:16 +01:00
Mateusz Faderewski
cfe4f01e30 Changed boot type to cold + RI/RDRAM reset (should improve game compatibility) 2023-12-06 12:57:01 +01:00
Robin Jones
553e986a38
Update 64drive_ll.c (#63)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Changes the names of some defines related to hardware variants.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Some of the defines are specific and may cause issues when adding other
flashcarts.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-11-10 17:36:06 +00:00
Robin Jones
3e7c4cec42
Add build stamp (#56)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Make sure that a menu build is identifiable.

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
It is not always obvious what version of a menu is running.
In future, the build date should be used as the start time for carts
that do not have a RTC.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
Works on the SC64, although I am sure it can be improved.

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-11-10 16:41:10 +00:00
Robin Jones
07fd93cb2a
Documentation updates (#65)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-11-10 16:39:09 +00:00
Robin Jones
d287c191ee
Update submodules (#64)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Updates the library submodules.

NOTE: `minic` has a breaking change which required adding
`MINI_FLAGS_SKIP_EMPTY_GROUPS`. See:
6229658b9d

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Keeps the project up-to-date

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [x] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-11-01 21:18:26 +00:00
Robin Jones
67debb3f0c
Add build badge 2023-11-01 18:47:08 +00:00
Robin Jones
387ad30183
Improve readme 2023-11-01 18:33:25 +00:00
Robin Jones
cdbca76269
Improve readme for DD expansions 2023-10-14 23:08:26 +01:00
Mateusz Faderewski
7e5418a6e9
Added video showcase in the README.md 2023-10-13 08:29:36 +02:00
Robin Jones
e9ee294327
Improve readme. 2023-10-10 20:52:19 +01:00
Mateusz Faderewski
1eb654d1c1
64DD disk loading support for SummerCart64 (#49) (and other fixes/improvements)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>

---------

Signed-off-by: Polprzewodnikowy <sc@mateuszfaderewski.pl>
Co-authored-by: Robin Jones <networkfusion@users.noreply.github.com>
2023-10-10 21:12:53 +02:00
Robin Jones
dfa4a7c237
Update README.md 2023-09-25 22:41:39 +01:00
Robin Jones
3044219fcd
Update README.md 2023-09-25 22:40:53 +01:00
Robin Jones
c36bd4197e
Add ability for 3 letter ID for boxart (#47)
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Allows boxart to be 3 letters by checking the "media type" if required.
It continues to be compatible with 2 letters.
Also update sodium64 rom address (changed in
d006af4870).

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
Fixes an issue for at least `DZ` which shares the same ID between
Dezaemon 3D for the 64DD and Duke Nukem: Zero Hour Cartridge.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-09-17 10:38:42 +02:00
Mateusz Faderewski
fc2f3aa42c
64drive support (#27)
<!--- Provide a general summary of your changes in the Title above -->

## Description
This PR implements 64drive support

## Motivation and Context
Goal of this PR is to support more devices
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->

## How Has This Been Tested?
Not tested at all
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
2023-09-08 21:49:50 +02:00
Mateusz Faderewski
b15be6467b Improve game boot compatibility 2023-09-06 00:49:06 +02:00
Mateusz Faderewski
5ef09def69 Disable release tag deletion 2023-09-04 23:32:36 +02:00
Mateusz Faderewski
15bca14b98 Improve reboot compatibility 2023-09-04 23:31:19 +02:00
158 changed files with 12918 additions and 3075 deletions

20
.clang-format Normal file
View File

@ -0,0 +1,20 @@
BasedOnStyle: LLVM
AlignAfterOpenBracket: BlockIndent
AlignEscapedNewlines: DontAlign
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AlwaysBreakBeforeMultilineStrings: true
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Attach
ColumnLimit: 120
IndentWidth: 4
SpaceBeforeParens: Custom
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterFunctionDeclarationName: true
AfterFunctionDefinitionName: true
UseTab: Never

View File

@ -1,14 +1,15 @@
FROM debian:bookworm-slim
ARG SC64_DEPLOYER_VERSION=v2.20.2
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install build-essential doxygen git python3 wget -y && \
wget https://github.com/DragonMinded/libdragon/releases/download/toolchain-continuous-prerelease/gcc-toolchain-mips64-x86_64.deb && \
dpkg -i gcc-toolchain-mips64-x86_64.deb && \
rm gcc-toolchain-mips64-x86_64.deb && \
wget https://github.com/Polprzewodnikowy/SummerCart64/releases/download/v2.16.0/sc64-deployer-linux-v2.16.0.tar.gz && \
tar -xf sc64-deployer-linux-v2.16.0.tar.gz -C /usr/local/bin && \
rm sc64-deployer-linux-v2.16.0.tar.gz && \
wget https://github.com/Polprzewodnikowy/SummerCart64/releases/download/$SC64_DEPLOYER_VERSION/sc64-deployer-linux-$SC64_DEPLOYER_VERSION.tar.gz && \
tar -xf sc64-deployer-linux-$SC64_DEPLOYER_VERSION.tar.gz -C /usr/local/bin && \
rm sc64-deployer-linux-$SC64_DEPLOYER_VERSION.tar.gz && \
git config --global --add safe.directory "*" && \
SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" && \
echo "$SNIPPET" >> "/root/.bashrc"

View File

@ -6,11 +6,10 @@
"mounts": [
"source=n64flashcartmenu-bashhistory,target=/commandhistory,type=volume"
],
"postCreateCommand": "git submodule update --init && cd ./libdragon && ./build.sh",
"postCreateCommand": "git submodule update --init && cd ./libdragon && make clobber -j && make libdragon tools -j && make install tools-install -j",
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
],
"settings": {
@ -24,4 +23,4 @@
}
}
}
}
}

View File

@ -28,12 +28,21 @@ body:
- type: input
id: menu_version
attributes:
label: Menu version used
description: The menu version used
label: Menu revision used
description: Press (Start) button on the menu to show the current menu version.
placeholder: e.g 1.2.3.456, N/A
validations:
required: false
- type: input
id: n64_region
attributes:
label: N64 console region
description: PAL, NTSC-U, NTSC-J, MPAL, etc.
placeholder: NTSC
validations:
required: false
- type: textarea
id: description
attributes:
@ -71,6 +80,16 @@ body:
validations:
required: false
- type: textarea
id: configuration_file
attributes:
label: configuration file(s)
description: The text of the configuration file(s) used. If applicable.
placeholder: |
for example paste the content of the configuration file (config.ini) here.
validations:
required: false
- type: textarea
id: other_things
attributes:

View File

@ -22,6 +22,15 @@ body:
validations:
required: true
- type: textarea
id: release-context
attributes:
label: Release Context
description: Is your feature request related to the latest release or pre-release version.
placeholder: This is still not available on the latest release or the pre-release versions, so it is applicable to both (and not a regression).
validations:
required: true
- type: textarea
id: solution
attributes:

View File

@ -21,6 +21,7 @@
- [ ] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [ ] Config and build (change in the configuration and build system, has no impact on code or features)
## Checklist:
@ -32,5 +33,8 @@
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
You agree with the license terms and that other license types may be granted with permission of the original `N64FlashcartMenu` project license holders.
<!--- It would be nice if you could sign off your contribution by replacing the name with your GitHub user name and GitHub email contact. -->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>

View File

@ -11,14 +11,17 @@ on:
jobs:
build-menu:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@ -37,7 +40,7 @@ jobs:
FLAGS: -DNDEBUG
- name: Upload artifact (Standard ROM)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: N64FlashcartMenu
path: |
@ -45,49 +48,59 @@ jobs:
./build/N64FlashcartMenu.elf
- name: Upload artifact (64drive version)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: 64drive
path: ./output/menu.bin
- name: Upload artifact (ED64 version)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ED64
path: ./output/OS64.v64
- name: Upload artifact (ED64P version)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ED64P
path: ./output/OS64P.v64
- name: Upload artifact (SC64 version)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: SC64
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
uses: softprops/action-gh-release@v0.1.15
uses: softprops/action-gh-release@v2
if: github.ref == 'refs/heads/main'
with:
name: Rolling release
body: Rolling release built from latest commit on `main` branch.
tag_name: rolling-release
body_path: ${{ github.workspace }}/CHANGELOG.md
tag_name: 'rolling_release'
make_latest: true
files: |
./output/N64FlashcartMenu.n64
./output/menu.bin
./output/OS64.v64
./output/OS64P.v64
# ./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. Check the commit history for more details.
target_commitish: develop
tag_name: 'rolling_pre-release'
prerelease: true
files: |
./output/N64FlashcartMenu.n64
./output/menu.bin
# ./output/OS64.v64
# ./output/OS64P.v64
./output/sc64menu.n64
continue-on-error: true
@ -97,16 +110,17 @@ jobs:
contents: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Run Doxygen
uses: mattnotmitt/doxygen-action@1.9.5
uses: mattnotmitt/doxygen-action@v1
with:
doxyfile-path: './Doxyfile'
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
uses: peaceiris/actions-gh-pages@v4
if: github.ref == 'refs/heads/main'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./output/docs
cname: menu.summercart64.dev

18
.gitignore vendored
View File

@ -1,8 +1,22 @@
# Ignore editor specific config folders/files
/.vscode
# Ignore compilation result directories
/build
/output
/tools/sc64/*
# There should never be ROMs uploaded, but just incase, make sure they are excluded.
# Ignore generated files in the libdragon FS
/filesystem/FiraMonoBold.font64
/filesystem/*.wav64
/filesystem/*.sprite
# Ignore external development tools
/tools/*
# Ignore any N64 ROM
**/*.n64
**/*.v64
**/*.z64
# Ignore macOS filesystem cruft
.DS_Store

2
.gitmodules vendored
View File

@ -1,7 +1,7 @@
[submodule "libdragon"]
path = libdragon
url = https://github.com/DragonMinded/libdragon
branch = unstable
branch = preview
ignore = dirty
[submodule "src/libs/libspng"]
path = src/libs/libspng

115
CHANGELOG.md Normal file
View File

@ -0,0 +1,115 @@
# Rolling release
built from latest commit on main branch.
**Note**: the attached source code files may be out of date.
- For the SummerCart64, use the `sc64menu.n64` file in the root of your SD card.
- For the 64Drive, use the `menu.bin` file in the root of your SD card.
- For the ares emulator, use the `N64FlashcartMenu.n64` file.
## Release Notes 2025-03-31
- **New Features**
- Introduced tabs in main menu for ROM favorites and recently played ROM history.
- Introduced first run check to ensure users are aware of latest changes.
- Introduced ability to turn off GUI loading bar.
- BETA_FEATURE: Introduces ROM descriptions from files.
- BETA_FEATURE: Enabled setting for fast ROM reboots on the SC64.
- Add macOS metadata to hidden files.
- Added settings schema version for future change versioning.
- Added setting for PAL60 compatibility mode (see breaking changes).
- BETA_FEATURE: Added setting for line doublers that need progressive output, enable using "force_progressive_scan" setting in `config.ini`.
- **Bug Fixes**
- Menu sound FX issues (hissing, popping and white noise).
- RTC not showing or setting correct date parameters in certain circumstances.
- GB / GBC emulator not saving in certain circumstances.
- **Documentation**
- Re-orginised and improved user documentation.
- Added a lot of doxygen compatible code comments.
- Added project license.
- **Refactor**
- RTC subsystem (align with libDragon improvements).
- Boxart images (Deprecates old boxart image folder layout).
- Settings (PAL60 compatibility, schema version, fast reboot, first run, progress bar).
- **Other**
- Updated libDragon SDK.
- Updated miniz library.
### Breaking changes
* GB /GBC emulator changed save type to SRAM (from FRAM) to improve compatibility with Summercart64 (which only uses H/W compatible FRAM), this may break your ability to load existing saves.
* For similar PAL60 functionality, you may need to also enable the new "pal60_compatibility_mode" setting in `config.ini`.
### Current known Issues
* The RTC UI requires improvement (awaiting UI developer).
* Menu sound FX may not work properly when a 64 Disk Drive is also attached (work around: turn sound FX off).
* Fast Rebooting a 64DD disk once will result in a blank screen. Twice will return to menu. This is expected until disk swapping is implemented.
* MP3 Player crashes menu if the MP3 file's sample rate is less than 44100 hz.
### Deprecation notices
* Autoload ROM's will be deprecated in favor of Fast Reboot in a future menu version.
* Old boxart images using filenames for game ID is deprecated and the compatibility mode will be removed in a future release.
## Release Notes 2025-01-10
- **Bug Fixes**
- Fixed menu display (PAL60) by reverted libdragon to a known working point and re-applying old hacks.
### Current known Issues
* The RTC UI requires improvement (awaiting UI developer).
* Menu sound FX may not work properly when a 64 Disk Drive is also attached (work around: turn sound FX off).
[Pre-release menu]:
* BETA_SETTING: PAL60 when using HDMI mods has regressed (awaiting libdragon fix).
* ALPHA_FEATURE: ED64 X Series detection does not occur properly (however this is not a problem as not tag released asset).
* ALPHA_FEATURE: ED64 V Series only supports loading ROMs (however this is not a problem as not tag released asset).
## Release Notes 2024-12-30
- **New Features**
- Introduced menu sound effects for enhanced user experience (the default is off).
- Added N64 ROM autoload functionality, allowing users to set a specific ROM to load automatically.
- Added menu boot hotkey (hold `start` to return to menu when autoload is enabled).
- Added context menu and settings management options GUI for managing various settings in `config.ini`.
- Added functionality for editing the real-time clock (RTC) within the RTC menu view.
- Improved flashcart info view for showing supported flashcart features and version.
- Enhanced UI components with new drawing functions and improved organization.
- Added emulator support for `SMS`, `GG`, and `CHF` ROMs.
- Enhanced joypad input handling for menu actions, improving responsiveness.
- Optimized boxart image loading from filesystem.
- Improved various text to make the functionality more clear.
- **Bug Fixes**
- Improved error handling in multiple areas, particularly in save loading and ROM management.
- Enhanced memory management to prevent potential leaks during error conditions.
- Fixed text flickering in certain circumstances.
- **Documentation**
- Updated README and various documentation files to reflect new features and usage instructions.
- Added detailed setup instructions for SD cards and menu customization.
- Enhanced clarity in documentation for RTC settings and menu customization.
- Improved organization and clarity of SD card setup instructions for various flashcarts.
- **Refactor**
- Standardized naming conventions across UI components for better organization.
- Restructured sound management and input handling for improved responsiveness.
- Streamlined the loading state management for ROMs and disks within the menu system.
- Improved clarity and usability of the developer guide and other documentation files.
### Current known Issues
* BETA_SETTING: PAL60 when using HDMI mods has regressed (awaiting libdragon fix).
* The RTC UI requires improvement (awaiting UI developer).
* Menu sound FX may not work properly when a 64 Disk Drive is also attached (work around: turn sound FX off).
* ALPHA_FEATURE: ED64 X Series detection does not occur properly (however this is not a problem as not tag released asset).
* ALPHA_FEATURE: ED64 V Series only supports loading ROMs (however this is not a problem as not tag released asset).
### Breaking changes
* Disk drive expansion ROMs are now loaded with `Z|L` instead of `R` to align with ROM info context menu (and future functionality).

11
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,11 @@
# Contributing to N64FlashcartMenu
This document is for people wanting to contribute to the implementation of N64FlashcartMenu.
This involves interacting with implementation changes that are proposed using [GitHub](https://github.com/) [pull requests](https://docs.github.com/pull-requests) to the [N64FlashcartMenu](https://github.com/Polprzewodnikowy/N64FlashcartMenu/) repository (which you're in right now).
As such, a GitHub account is recommended, which you can sign up for [here](https://github.com/signup).
Additionally this document assumes that you already know how to use GitHub and Git.
If that's not the case, we recommend learning about it first [here](https://docs.github.com/en/get-started/quickstart/hello-world).
**Help us by creating a PR.**

View File

@ -905,7 +905,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# 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
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -1002,7 +1002,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH =
IMAGE_PATH = ./docs/images
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program

661
LICENSE.md Normal file
View File

@ -0,0 +1,661 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

106
Makefile
View File

@ -4,23 +4,36 @@ PROJECT_NAME = N64FlashcartMenu
SOURCE_DIR = src
ASSETS_DIR = assets
FILESYSTEM_DIR = filesystem
BUILD_DIR = build
OUTPUT_DIR = output
MENU_VERSION ?= "Rolling release"
BUILD_TIMESTAMP = "$(shell TZ='UTC' date "+%Y-%m-%d %H:%M:%S %:z")"
include $(N64_INST)/include/n64.mk
N64_ROM_SAVETYPE = none
N64_ROM_RTC = 1
N64_ROM_REGIONFREE = 1
N64_ROM_REGION = E
N64_CFLAGS += -iquote $(SOURCE_DIR) -iquote $(ASSETS_DIR) -I $(SOURCE_DIR)/libs -flto=auto $(FLAGS)
SRCS = \
main.c \
boot/boot.c \
boot/crc32.c \
boot/cheats.c \
boot/cic.c \
boot/reboot.S \
flashcart/64drive/64drive_ll.c \
flashcart/64drive/64drive.c \
flashcart/flashcart_utils.c \
flashcart/ed64/ed64_vseries.c \
flashcart/ed64/ed64_xseries.c \
flashcart/flashcart.c \
flashcart/sc64/sc64_ll.c \
flashcart/sc64/sc64.c \
hdmi/hdmi.c \
libs/libspng/spng/spng.c \
libs/mini.c/src/mini.c \
libs/miniz/miniz_tdef.c \
@ -28,54 +41,87 @@ SRCS = \
libs/miniz/miniz_zip.c \
libs/miniz/miniz.c \
menu/actions.c \
menu/bookkeeping.c \
menu/cart_load.c \
menu/components/background.c \
menu/components/boxart.c \
menu/components/common.c \
menu/components/context_menu.c \
menu/components/file_list.c \
menu/disk_info.c \
menu/fonts.c \
menu/hdmi.c \
menu/menu.c \
menu/mp3_player.c \
menu/path.c \
menu/png_decoder.c \
menu/rom_database.c \
menu/rom_info.c \
menu/settings.c \
menu/sound.c \
menu/ui_components/background.c \
menu/ui_components/boxart.c \
menu/ui_components/common.c \
menu/ui_components/context_menu.c \
menu/ui_components/file_list.c \
menu/ui_components/tabs.c \
menu/usb_comm.c \
menu/views/browser.c \
menu/views/credits.c \
menu/views/error.c \
menu/views/fault.c \
menu/views/file_info.c \
menu/views/history_favorites.c \
menu/views/image_viewer.c \
menu/views/text_viewer.c \
menu/views/load_disk.c \
menu/views/load_emulator.c \
menu/views/load_rom.c \
menu/views/music_player.c \
menu/views/startup.c \
menu/views/system_info.c \
menu/views/settings_editor.c \
menu/views/rtc.c \
menu/views/flashcart_info.c \
utils/fs.c
ASSETS = \
FONTS = \
FiraMonoBold.ttf
OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o,$(basename $(SRCS) $(ASSETS))))
SOUNDS = \
cursorsound.wav \
back.wav \
enter.wav \
error.wav \
settings.wav
OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o,$(basename $(SRCS))))
MINIZ_OBJS = $(filter $(BUILD_DIR)/libs/miniz/%.o,$(OBJS))
SPNG_OBJS = $(filter $(BUILD_DIR)/libs/libspng/%.o,$(OBJS))
DEPS = $(OBJS:.o=.d)
$(MINIZ_OBJS): N64_CFLAGS+=-DMINIZ_NO_TIME -fcompare-debug-second
FILESYSTEM = \
$(addprefix $(FILESYSTEM_DIR)/, $(notdir $(FONTS:%.ttf=%.font64))) \
$(addprefix $(FILESYSTEM_DIR)/, $(notdir $(SOUNDS:%.wav=%.wav64))) \
$(addprefix $(FILESYSTEM_DIR)/, $(notdir $(IMAGES:%.png=%.sprite)))
$(MINIZ_OBJS): N64_CFLAGS+=-DMINIZ_NO_TIME -Wno-unused-function -fcompare-debug-second
$(SPNG_OBJS): N64_CFLAGS+=-isystem $(SOURCE_DIR)/libs/miniz -DSPNG_USE_MINIZ -fcompare-debug-second
$(BUILD_DIR)/FiraMonoBold.asset: MKFONT_FLAGS+=-c 0 --size 16 -r 20-7F -r 2026-2026 --ellipsis 2026,1
$(FILESYSTEM_DIR)/FiraMonoBold.font64: MKFONT_FLAGS+=--compress 1 --outline 1 --size 16 --range 20-7F --range 80-1FF --range 2026-2026 --ellipsis 2026,1
$(FILESYSTEM_DIR)/%.wav64: AUDIOCONV_FLAGS=--wav-compress 1
$(BUILD_DIR)/%.asset: $(ASSETS_DIR)/%.ttf
@echo " [FONT] $(basename $@).font64"
@$(N64_MKFONT) $(MKFONT_FLAGS) -o $(BUILD_DIR) "$<"
@mv $(basename $@).font64 $@
$(@info $(shell mkdir -p ./$(FILESYSTEM_DIR) &> /dev/null))
$(BUILD_DIR)/%.o: $(BUILD_DIR)/%.asset $(ASSETS_DIR)/assets.S
@sed -e "s,@sym@,$*,g" -e "s,@file@,$(basename $<).asset," < $(ASSETS_DIR)/assets.S | \
$(CC) -x assembler-with-cpp $(ASFLAGS) -c - -o $@
$(FILESYSTEM_DIR)/%.font64: $(ASSETS_DIR)/fonts/%.ttf
@echo " [FONT] $@"
@$(N64_MKFONT) $(MKFONT_FLAGS) -o $(FILESYSTEM_DIR) "$<"
$(FILESYSTEM_DIR)/%.wav64: $(ASSETS_DIR)/sounds/%.wav
@echo " [AUDIO] $@"
@$(N64_AUDIOCONV) $(AUDIOCONV_FLAGS) -o $(FILESYSTEM_DIR) "$<"
$(FILESYSTEM_DIR)/%.sprite: $(ASSETS_DIR)/images/%.png
@echo " [SPRITE] $@"
@$(N64_MKSPRITE) $(MKSPRITE_FLAGS) -o $(dir $@) "$<"
$(BUILD_DIR)/$(PROJECT_NAME).dfs: $(FILESYSTEM)
$(BUILD_DIR)/menu/views/credits.o: .FORCE
$(BUILD_DIR)/menu/views/credits.o: FLAGS+=-DMENU_VERSION=\"$(MENU_VERSION)\" -DBUILD_TIMESTAMP=\"$(BUILD_TIMESTAMP)\"
$(BUILD_DIR)/$(PROJECT_NAME).elf: $(OBJS)
@ -84,29 +130,26 @@ disassembly: $(BUILD_DIR)/$(PROJECT_NAME).elf
.PHONY: disassembly
$(PROJECT_NAME).z64: N64_ROM_TITLE=$(PROJECT_NAME)
$(PROJECT_NAME).z64: $(BUILD_DIR)/$(PROJECT_NAME).dfs
$(@info $(shell mkdir -p ./$(OUTPUT_DIR) &> /dev/null))
$(OUTPUT_DIR)/$(PROJECT_NAME).n64: $(PROJECT_NAME).z64
@mv $< $@
$(BUILD_DIR)/$(PROJECT_NAME)_stripped.n64: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
python3 ./tools/strip_debug_data.py $(BUILD_DIR)/$(PROJECT_NAME).elf $< $@
@$(N64_CHKSUM) $@ > /dev/null
64drive: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
@cp $< $(OUTPUT_DIR)/menu.bin
.PHONY: 64drive
ed64: $(BUILD_DIR)/$(PROJECT_NAME)_stripped.n64
ed64: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
@cp $< $(OUTPUT_DIR)/OS64.v64
.PHONY: ed64
ed64-clone: $(BUILD_DIR)/$(PROJECT_NAME)_stripped.n64
ed64-clone: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
@cp $< $(OUTPUT_DIR)/OS64P.v64
.PHONY: ed64-clone
sc64: $(BUILD_DIR)/$(PROJECT_NAME)_stripped.n64
sc64: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
@cp $< $(OUTPUT_DIR)/sc64menu.n64
.PHONY: sc64
@ -114,9 +157,18 @@ all: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 64drive ed64 ed64-clone sc64
.PHONY: all
clean:
@rm -f ./$(FILESYSTEM)
@find ./$(FILESYSTEM_DIR) -type d -empty -delete
@rm -rf ./$(BUILD_DIR) ./$(OUTPUT_DIR)
.PHONY: clean
format:
@find ./$(SOURCE_DIR) \
-path \./$(SOURCE_DIR)/libs -prune \
-o -iname *.c -print \
-o -iname *.h -print \
| xargs clang-format -i
run: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
ifeq ($(OS),Windows_NT)
./localdeploy.bat
@ -136,4 +188,6 @@ endif
# test:
# TODO: run tests
.FORCE:
-include $(DEPS)

166
README.md
View File

@ -1,104 +1,108 @@
# N64 Flashcart Menu
An open source menu for N64 flashcarts.
![Build](https://github.com/polprzewodnikowy/N64FlashcartMenu/actions/workflows/build.yml/badge.svg)
![GitHub Org's stars](https://img.shields.io/github/stars/Polprzewodnikowy/N64FlashcartMenu)
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/Polprzewodnikowy/N64FlashcartMenu.svg)](http://isitmaintained.com/project/Polprzewodnikowy/N64FlashcartMenu "Average time to resolve an issue")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/Polprzewodnikowy/N64FlashcartMenu.svg)](http://isitmaintained.com/project/Polprzewodnikowy/N64FlashcartMenu "Percentage of issues still open")
[![#yourfirstpr](https://img.shields.io/badge/first--timers--only-friendly-blue.svg)](https://github.com/Polprzewodnikowy/N64FlashcartMenu/blob/main/CONTRIBUTING.md)
## Aims
* Support as many N64 FlashCarts as possible.
* Be open source, using permissive licensed third party licensed libraries.
* Be testable, using unit and smoke tests in ares emulated environment.
* Encourage active development from community members and N64 FlashCart owners.
* Support as many common mods and features as possible.
# N64 Flashcart Menu
An open source menu for N64 flashcarts and aims to support as many as possible. The menu is not affiliated with any partuclar flashcart and does not necessarily expose all capable firmware features.
**This project considers flashcart sellers that include this menu or copyrighted ROM's as part of their product offering as licence offenders which may result in future action.**
## Flashcart Supported
This menu aims to support as many N64 flashcarts as possible. The current state is:
### Supported
* SummerCart64
* 64Drive
### Work in Progress
* ED64 (X and V series)
* ED64P (clones)
### Not yet planned
* Doctor V64
* PicoCart
* DaisyDrive
## Current (notable) menu features
* Fully Open Source.
* Loads all known N64 games (including iQue and Aleck64 ROMs (even if they are byteswapped)).
* Emulator support (NES, SNES, GB, GBC) ROMs.
* N64 ROM box image support.
* Loads all known N64 games, even if they are byteswapped.
* Fully emulates the 64DD and loads 64DD disks (SummerCart64 only).
* Emulator support (NES, SNES, GB, GBC, SMS, GG, CHF) ROMs.
* N64 ROM box art image support.
* Background image (PNG) support.
* Comprehensive ROM save database (including HomeBrew headers).
* Comprehensive ROM save database (including homebrew headers).
* Comprehensive ROM information display.
* Real Time Clock support.
* Music playback (MP3).
* Menu sound effects.
* N64 ROM autoload option (on power).
* N64 ROM fast reboot option (on reset).
* ROM information descriptions.
* ROM history and favorites.
## Getting started
## Documentation
Please take a moment to browse the current documentation / user guide:
[User Guide - Index](./docs/00_index.md)
## Aims
* Support as many N64 Flashcarts as possible.
* Be open source, using permissively licensed third-party libraries.
* Be testable in an emulated environment (Ares).
* Encourage active development from community members and N64 FlashCart owners.
* Support as many common mods and features as possible (flashcart dependent).
## Flashcart specific
### SC64
Ensure the cart is running the latest [firmware](https://github.com/Polprzewodnikowy/SummerCart64/releases/latest).
Download the `sc64menu.n64` ROM from the latest action run assets.
Add it to the root folder on your SD card.
* 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.
### ED64 & ED64P
Currently not supported, but there is an aim to do so.
The aim is to replace [Altra64](https://github.com/networkfusion/altra64) and [ED64-UnofficialOS](https://github.com/n64-tools/ED64-UnofficialOS-binaries).
![SC64 flashcart information](./docs/images/sc64-flashcart-information.png "example SC64 flashcart information")
### Common to all
#### ROM Boxart
To use boxart, you need to place png files of size 158x112 in the folder `/menu/boxart` on the SD card.
Each file must be named according to the 2 letter ROM ID. e.g. for goldeneye, this would be `GE.png`.
A known set of PNG files can be downloaded [here](https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w).
#### Emulator support
Emulators should be added to the `/emulators` directory on the SD card.
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`
### 64drive
* Ensure the cart has the latest [firmware](https://64drive.retroactive.be/support.php) installed.
* 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.
# Developer documentation
**Work in progress!**
### ED64 - WIP - UNTESTED AND UNSUPPORTED - USE AT OWN RISK
Currently not supported, but work is in progress (See [PRs](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls)).
**Warning**: The menu may be able to load ROMs but cannot guarantee save functionality. Existing saves may be corrupted.
You can use a dev container in VSCode to ease development.
#### ED64 (Vseries)
The aim is to reach feature parity with [ED64-UnofficialOS](https://github.com/n64-tools/ED64-UnofficialOS-binaries) / [ED64-OfficialOS](https://krikzz.com/pub/support/everdrive-64/v2x-v3x/os-bin/).
Download the `OS64.v64` ROM from the latest [action run - assets] and place it in the `/ED64` folder.
#### ED64 (X series)
The aim is to reach feature parity with [OS](https://krikzz.com/pub/support/everdrive-64/x-series/OS/) for now.
Download the `OS64.v64` ROM from the latest [action run - assets] and place it in the `/ED64` folder.
#### ED64 (P clone)
Download the `OS64P.v64` ROM from the latest [action run - assets] and place it in the `/ED64P` folder.
The aim is to reach feature parity with [Altra64](https://github.com/networkfusion/altra64)
## To deploy:
### SC64
* Download the deployer [here](https://github.com/Polprzewodnikowy/SummerCart64/releases/download/v2.16.0/sc64-deployer-windows-v2.16.0.zip)
* Extract and place `sc64deployer.exe` in the `tools/sc64` directory.
Make sure that your firmware is compatible (currently v2.16.0+)
See: https://github.com/Polprzewodnikowy/SummerCart64/blob/v2.16.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`.
# Update Libdragon submodule
This repo currently uses the `unstable` 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.
# License
This project is released under the [GNU AFFERO GENERAL PUBLIC LICENSE](LICENSE.md) as compatible with all other dependent project licenses.
Other license options may be available upon request with permissions of the original `N64FlashcartMenu` project authors / maintainers.
* Mateusz Faderewski / Polprzewodnikowy
* Robin Jones / NetworkFusion
# Open source software and licenses used
- [libdragon](https://github.com/DragonMinded/libdragon) (UNLICENSE License)
- [libspng](https://github.com/randy408/libspng) (BSD 2-Clause License)
- [mini.c](https://github.com/univrsal/mini.c) (BSD 2-Clause License)
- [minimp3](https://github.com/lieff/minimp3) (CC0 1.0 Universal)
- [miniz](https://github.com/richgel999/miniz) (MIT License)
## libraries
* [libdragon](https://github.com/DragonMinded/libdragon/tree/preview) (UNLICENSE License)
* [libspng](https://github.com/randy408/libspng) (BSD 2-Clause License)
* [mini.c](https://github.com/univrsal/mini.c) (BSD 2-Clause 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)

View File

@ -1,11 +0,0 @@
.section .data.@sym@, "a", %progbits
.type assets_@sym@, %object
.balign 16
.global assets_@sym@
assets_@sym@:
.incbin "@file@"
.global assets_@sym@_size
.set assets_@sym@_size, . - assets_@sym@

View File

@ -1,16 +0,0 @@
#ifndef ASSETS_H__
#define ASSETS_H__
#include <stdint.h>
#define ASSET(n) \
extern uint8_t assets_##n[] __attribute__((section(".data"))); \
extern int assets_##n##_size[] __attribute__((section(".data")))
ASSET(FiraMonoBold);
#endif

BIN
assets/sounds/back.wav Normal file

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/enter.wav Normal file

Binary file not shown.

BIN
assets/sounds/error.wav Normal file

Binary file not shown.

BIN
assets/sounds/settings.wav Normal file

Binary file not shown.

36
docs/00_index.md Normal file
View File

@ -0,0 +1,36 @@
[Return to README](README.md)
## N64FlashcartMenu User Guide
### General
- [Initial Setup of an SD Card](./10_getting_started_sd.md)
- [Basic Controls](./11_menu_controls.md)
- [ROM Configuration](./12_rom_configuration.md)
- [Cheats (Gameshark, etc.)](./13_datel_cheats.md)
- [ROM Patches (Hacks, Fan Translations, etc.)](./14_rom_patches.md)
- [Controller PAKs](./15_controller_paks.md)
- [Background Images](./16_background_images.md)
- [64DD](./17_64dd.md)
- [Emulators](./18_emulators.md)
- [Game Art Images](./19_gamepak_boxart.md)
- [Autoloading N64 ROMs](./22_autoload_roms.md)
### Menus
- [File Browser](./31_file_browser.md)
- [Menu Settings](./32_menu_settings.md)
- [Date-Time (RTC) Settings](./33_rtc_settings.md)
- [Menu Information](./35_menu_information.md)
- [Flashcart Information](./36_flashcart_information.md)
- [N64 Information](./37_n64_information.md)
### Other
- [MP3 Player](./41_mp3_player.md)
- [Advanced Customization](./61_advanced_customization.md)
- [N64 Hardware Modifications Compatibility](./62_hardware_mods.md)
- [FAQ](./81_faq.md)
#### Experimental Features (Subject to change)
- [Experimental Features](./65_experimental.md)
### Developers
- [Developer Guide](./99_developer_guide.md)
- [Contributing](https://github.com/Polprzewodnikowy/N64FlashcartMenu/blob/main/CONTRIBUTING.md)

View File

@ -0,0 +1,87 @@
[Return to the index](./00_index.md)
## Initial Setup of an SD Card
### First steps
Connect the SD card to your PC and ensure it is properly formatted to be compatible with your flashcart.
**WARNING:** Filenames are expected to be written in ASCII, with Western Europe characters fully compatible. Other Unicode characters, such as those from Eastern Europe, Russia, Asia or Middle East regions (to name just a few examples) are not fully supported and may not be displayed.
**Note:** It is advised to use ROM files in the Big Endian (default, also called "non-byteswapped") format. Although the menu auto-converts byteswapped ROM files, the load time will increase.
#### Preparations for SC64
- FAT32 and EXFAT are fully supported.
- An SD formatted with 128 kiB cluster size is recommended.
- 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.
- Place your ROM files on the SD card, **in any folder except `menu`**.
**NOTE:** byteswapped ROM's will increase load times.
#### Preparations for other supported flashcarts
- FAT32 recommended.
- An SD formatted with the default cluster size is recommended.
- Download the latest [menu](https://github.com/Polprzewodnikowy/N64FlashcartMenu/releases/) file specific for your flashcart and place it in the expected location.
### Emulator support
See the [Emulators](./18_emulators.md) page.
### 64DD Disk support
See the [64DD](./17_64dd.md) page.
#### 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
│ ├── smsPlus64.z64
│ └── Press-F.z64
├── (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`) will be read from a `/saves` folder located **in the same directory as the ROM**
and they must share the same file name, but use the `.sav` extension. `.sav` files will be created and modified whenever a ROM file saves to
the "cartridge save memory".
```plaintext
├── (some folder with ROMs)\
├── a_rom.z64
├── b_rom_whatever.n64
└── saves\
├── a_rom.sav
└── b_rom_whatever.sav
```
### Transferring saves from an ED64
If you are transferring a file from a different flashcart, such as the ED64, you must change the file extension to `sav`.
For example, a save file called `Glover (USA).eep` should have its extension changed to `Glover (USA).sav` to work with N64FlashcartMenu.
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 N64SaveConverter](https://github.com/Ninjiteu/N64SaveConverter).
**WARNING:** Saves created with certain emulators, or created for a different ROM version (as in revisions of the same game)
or region (as in between NTSC and PAL versions of a same game) may be incompatible.

28
docs/11_menu_controls.md Normal file
View File

@ -0,0 +1,28 @@
[Return to the index](./00_index.md)
## Basic Controls
<!-- Could use a sample screenshot of the controller and labels here -->
#### Fast scroll
Press either the `C-Up` or `C-Down` buttons to scroll by pages, rather than by elements.
#### N64FlashcartMenu settings
Press the `START` button on the browser screen to open the Settings window.
![Main context menu](./images/main-context-menu.png "Main context menu")
From here you can edit some of the N64FlashcartMenu settings, see information about either the console, the flashcart you are using or N64FlashcartMenu itself, and if your cart has Real-Time Clock (RTC) support, you can also change its date and time.
#### Browser options
Press the `R` button to open the Browser Options window. Here you can see a ROM's properties, delete it from your SD card or establish the default folder
where N64FlashcartMenu's browser will start in future boots.
#### Additional ROM information
<!-- Could use a sample screenshot here -->
Press either the `L` or `Z` button on the ROM information screen to open an additional window that will show additional information about the currently
selected ROM file, such as its endianness, regional variant, set clock rate, and much more.
### 64DD-related
#### Expansion disks
To load an expansion disk (such as F-Zero X), first browse to the N64 ROM and load it (**but not start it!**), then browse to the 64DD expansion file and press either the `L` or `Z` button.
#### Disk swapping
This feature is not currently available in N64FlashcartMenu.

View File

@ -0,0 +1,45 @@
[Return to the index](./00_index.md)
## ROM Configuration
The N64FlashcartMenu allows overriding the ROM's default configuration that is provided from the internal database.
The internal database is contained within `rom_info.c`.
The N64FlashcartMenu expects that you are using a flashcart that has an [UltraCIC](https://n64brew.dev/wiki/Checking_Integrated_Circuit) available.
NOTE: Some old ROM hacks may have adjusted the ROM code to manipulate the expected CIC and save type in order to allow compatibility with more available chips (usually 6102) as was used on flashcarts prior to 2018. If it does, you may need to override the internal database using the override settings.
If you override the defaults and want to go back to the default ones, delete the `<rom name>.ini` file.
### Homebrew Header
The N64FlashcartMenu fully supports the [homebrew header](https://n64brew.dev/wiki/ROM_Header#Advanced_Homebrew_ROM_Header)
### Available Overrides
#### CIC type
The Checking Integrated Circuit [CIC](https://n64brew.dev/wiki/Checking_Integrated_Circuit) was a physical security chip used by retail Nintendo 64 game cartridges that prevented unlicensed and pirated game cartridges from running and used in conjunction with the [PIF](https://n64brew.dev/wiki/PIF-NUS).
WARNING: Changing the CIC type to an unsupported one may result in a blank screen for that particular ROM until you manually delete the override file!
For more detailed information regarding the various CIC chips, please visit [micro-64.com's game CIC database](http://micro-64.com/database/gamecic.shtml).
#### Save type
Games that have been programmed to include an internal save system might use various types of chips, methods and sizes.
WARNING: Using the wrong save type can cause unwanted behaviors on games and/or corrupt existing ones!
For more detailed information regarding the various saving methods, please visit [micro-64.com's game save database](http://micro-64.com/database/gamesave.shtml)
#### TV Region type
<!-- Should we quickly explain regions? -->
All ROMs are generally programmed to work with a single type of television output setting, whether it's NTSC, PAL or MPAL or their multiple variants. Forcing the region will generally make the ROM work on your display, however:
1. Be aware that not every CRT from the 1990s and 2000s is compatible with both NTSC and PAL standards.
2. Be aware that flat TVs from late 2010s-2020s might have other issues, such as when a game changes its internal resolution during gameplay (i.e. Resident Evil 2 with Expansion Pak).
3. Expect potential side effects:
- Speed issues
- Audio/visual desynchronization
- Other unexpected/unwanted behaviors
### Autoload
See the [Autoload N64 ROMs](./22_autoload_roms.md) page.

48
docs/13_datel_cheats.md Normal file
View File

@ -0,0 +1,48 @@
[Return to the index](./00_index.md)
## Cheats (Gameshark, etc.)
The N64FlashcartMenu supports the cheat code types made popular by the peripherals:
- GameShark
- Action Replay
Another product by Blaze, called the Xploder64/Xplorer64 also existed in some regions, but these codes are less likely to work.
**WARNING**: It is not advised to connect a physical cheat cartridge in conjunction with most flashcarts.
The N64FlashcartMenu can only support cheat codes based on Datel carts when also using an Expansion Pak.
Caveats:
- Something about cheats and expansion paks.
The current code types are supported:
- 80 (description here)
- D0 (description here)
- Fx (description here)
- ...
The codes XX are not supported, because...
- e.g. they rely on the button.
```
// Example cheat codes for the game "Majoras Mask USA"
uint32_t cheats[] = {
// Enable code
0xF1096820,
0x2400,
0xFF000220,
0x0000,
// Inventory Editor (assigned to L)
0xD01F9B91,
0x0020,
0x803FDA3F,
0x0002,
// Last 2 entries must be 0
0,
0,
};
```
And pass this array as a boot parameter: `menu->boot_params->cheat_list = cheats;`
Check the [Pull Requests](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls) for work towards GUI editor support.

8
docs/14_rom_patches.md Normal file
View File

@ -0,0 +1,8 @@
[Return to the index](./00_index.md)
## ROM Patches (Hacks, Fan Translations, etc.)
At the time of writing, N64FlashcartMenu does not support patching on-the-fly, although you can use various offline and online utilities to patch your games.
Check the [Pull Requests section](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls) for work towards it.
The aim is to support APS/IPS/BPS/XDELTA patches.

View File

@ -0,0 +1,6 @@
[Return to the index](./00_index.md)
## Controller Paks
<!-- No need for screenshots until the CPak menu is more mature -->
At the time of writing, N64FlashcartMenu can only recognize if a Controller Pak is inserted on a Controller. Controller Pak backups, backup restoration and handling are not yet supported.
Check the [Pull Requests section](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls) for work towards it.

View File

@ -0,0 +1,7 @@
[Return to the index](./00_index.md)
## Background Images
### How to add a background image
First copy an image in .PNG format to anywhere on the SD card. Then when the N64FlashcartMenu is loaded on the N64, browse to the image and then select it to show it on the screen. Press the `A` Button again to open up the confirmation message, which will ask if you want to set a new background image.
<!-- Could use a sample screenshot here -->
Press the `A` Button to confirm and set the image as your new background or press the `B` Button to cancel and return to the image display screen.

12
docs/17_64dd.md Normal file
View File

@ -0,0 +1,12 @@
[Return to the index](./00_index.md)
## 64DD Disk support
Specific flashcarts (such as the [SummerCart64](https://summercart64.dev)) can fully emulate the original 64DD (load, run, save) disks without having a physical device connected to the N64 and don't require conversion ROMs.
To load and run 64DD disk images, 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).
**Note**: It is not yet possible to swap 64DD disks via N64FlashcartMenu.
### Conversion ROMs
The N64Flashcart Menu is able to load and run conversion ROMs (but does not perform saves).

12
docs/18_emulators.md Normal file
View File

@ -0,0 +1,12 @@
[Return to the index](./00_index.md)
## Emulators
N64FlashcartMenu supports multiple emulators that are compatible with the N64. At the time of writing, current emulator support includes NES, SNES, GB, GBC, SMS, GG, and CHF ROMs.
Emulators should be added to the `/menu/emulators` directory on the SD card. N64FlashcartMenu 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)
- **SMS**/**GG**: [smsPlus64](https://github.com/fhoedemakers/smsplus64/releases) by *fhoedmakers* - `smsPlus64.z64`
- **Fairchild Channel F**: [Press-F-Ultra](https://github.com/celerizer/Press-F-Ultra/releases) by *celerizer* - `Press-F.z64`
If you are an emulator developer and are interested in adding your emulator, take a look at this [template pull request](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pull/178).

29
docs/19_gamepak_boxart.md Normal file
View File

@ -0,0 +1,29 @@
[Return to the index](./00_index.md)
## Game Art Images
To use N64 game box art images, place your PNG files within the `sd:/menu/boxart/` folder.
#### Supported images
Files must be in `PNG` format and use the following dimensions:
* American/European N64 box art sprites: 158x112
* Japanese N64 box art sprites: 112x158
* 64DD box art sprites: 129x112
Images will be loaded by directories using each character (case-sensitive) of the full 4-character Game Code (as identified in the menu ROM information):
i.e. for GoldenEye NTSC USA (NGEE), this would be `sd:/menu/boxart/N/G/E/E/boxart_front.png`.
i.e. for GoldenEye PAL (NGEP), this would be `sd:/menu/boxart/N/G/E/P/boxart_front.png`.
To improve compatibility between regions (as a fallback), you may exclude the region ID (last matched directory) for GamePaks to match with 3-character IDs instead:
i.e. for GoldenEye, this would be `sd:/menu/boxart/N/G/E/boxart_front.png`.
**Warning**: Excluding the region ID may show a box art of the wrong region.
**Note**: For future support, box art sprites should also include:
- `boxart_back.png`
- `boxart_top.png`
- `boxart_bottom.png`
- `boxart_left.png`
- `boxart_right.png`
- `gamepak_front.png`
- `gamepak_back.png`
As a starting point, here is a link to a box art pack, that has `boxart_front.png`:
- [Third party box art](https://drive.google.com/file/d/1IpCmFqmGgGwKKmlRBxYObfFR9XywaC6n/view?usp=drive_link)

8
docs/22_autoload_roms.md Normal file
View File

@ -0,0 +1,8 @@
[Return to the index](./00_index.md)
## Autoloading N64 ROMs
You can set up N64FlashcartMenu to load a specific ROM directly instead of booting up the menu's graphical user interface. **NOTE:** byteswapped ROMs will slow down the ROM load process.
If you only want to continously reload a ROM for a single gaming session, you should consider the setting for [Fast ROM reboots](./32_menu_settings.md) instead.
### How to enable autoloading
To use the autoload function, open the `N64 ROM information` screen on any ROM, then press the `R` Button on your Controller and select the `Set ROM to autoload` option. When you restart the console, N64FlashcartMenu will now only load the selected ROM, rather than the menu itself.
**NOTE:** If you want to return to the menu, press and hold the `START` Button on your Controller while turning the console's POWER button to the ON position.

38
docs/31_file_browser.md Normal file
View File

@ -0,0 +1,38 @@
[Return to the index](./00_index.md)
## File Browser
<!-- Could use a beauty screenshot here -->
The File Browser allows you to navigate and manage files on your N64 flashcart. Below are the key features and instructions on how to use the File Browser effectively.
### Features
- **File and folder navigation**: Browse through directories and files on your flashcart.
- **File operations**: Perform operations such as delete and show properties.
- **File information**: View detailed information about each file, including size and date modified.
- **Load files**: Load files from the file system.
- **Switching tabs (Pre-release only)**: Switch between the file browser, favorites and history tabs.
### Usage Instructions
<!-- Maybe all the Control pages could be merged into this section? -->
1. **Navigating Files**:
- Use the directional `Up` and `Down` buttons (`C-Up` and `C-Down` for fast scrolling) to move through the list of files and directories.
- Press the `A` Button to open a directory or load a supported file.
2. **Performing File Operations**:
- Highlight the file or directory you want to operate on.
- Press the `R` Button to open the operations menu.
- Select the desired operation (delete, show properties, set as default) and follow the on-screen prompts.
3. **Viewing Settings menu**:
- Press the `Z` Button to display the menu.
4. **Switching tabs (Pre-release only)**:
- Press the `C-Right` and `C-Left` Buttons to switch between the file browser, favorites and history tabs.
### Tips
- Make sure you regularly back up important files from the SD Card to your computer to avoid accidental loss.
- Familiarize yourself with the button layout to navigate and manage files efficiently.
### Troubleshooting
- If a file operation fails, make sure there is enough space on the SD card and that the file is not write-protected.
- For any issues not covered in this guide, refer to the troubleshooting section in the main manual or contact the support channels on Discord.

21
docs/32_menu_settings.md Normal file
View File

@ -0,0 +1,21 @@
[Return to the index](./00_index.md)
## Menu Settings
N64FlashcartMenu automatically creates a `config.ini` file in `sd:/menu/`, which contains various settings that can be set within the menu's Settings editor.
If required, you can manually adjust the file (required for some advanced settings) on the SD card using your computer.
### Show Hidden Files
Shows any N64FlashcartMenu system-related files. This setting is OFF by default.
### Use Save Folders
Controls whether N64FlashcartMenu should use `/saves` folders to store ROM save data. This setting is ON by default.
ON: ROM saves are saved in separate subfolders (called `\saves`, will create one `\saves` subfolder per folder).
OFF: ROM saves are saved alongside the ROM file.
### Sound Effects
The menu has default sound effects to improve the user experience. This setting is OFF by default.
### Fast ROM reboots
Certain flashcarts support the ability to use the N64 `RESET` button for re-loading the last game, rather than returning to the menu. When enabled (and if supported by your flashcart), the power switch must be toggled to return to the menu.
NOTE: if a USB cable is connected, the last game may continue to be re-loaded.
Fast Rebooting a 64DD disk once will result in a blank screen. Twice will return to menu. This is expected until disk swapping is implemented.
This setting is OFF by default.

9
docs/33_rtc_settings.md Normal file
View File

@ -0,0 +1,9 @@
[Return to the index](./00_index.md)
## Date-Time (RTC) Settings
![RTC information](./images/rtc-information.png "RTC Information")
If your flashcart supports the Real-Time Clock (RTC from herein) feature, N64FlashcartMenu has the ability to read and set it.
Press the `START` Button on the File Browser and select `Time (RTC) settings` to enter the Adjust Real-Time Clock screen. Here you will see a notice regarding other ways to change the RTC. If your flashcart is compatible, press the `A` Button to display the RTC change prompt.
Press Up and/or Down on your Control Stick or Directional Buttons to modify the currently selected value, or Left and/or Right to select another value to change. If you are satisfied with the changes, press the `R` Button to save the current time and date and return to the Adjust Real-Time Clock screen. If you want to cancel your changes, press the `B` Button to return to the Adjust Real-Time Clock screen and keep the old RTC values.

View File

@ -0,0 +1,4 @@
[Return to the index](./00_index.md)
## Menu information
![N64FlashcartMenu menu information](./images/menu-information.png "N64FlashcartMenu menu information")
This screen will show you various information regarding the N64FlashcartMenu you have booted, such as its version, its build date, and the developer credits.

View File

@ -0,0 +1,4 @@
[Return to the index](./00_index.md)
## Flashcart Information
![Screenshot of the Flashcart information screen](./images/sc64-flashcart-information.png "Screenshot of the Flashcart information screen")
This screen shows all the information that N64FlashcartMenu can gather from the flashcart, such as the firmware version and flashcart features that may be supported within the N64FlashcartMenu.

View File

@ -0,0 +1,5 @@
[Return to the index](./00_index.md)
## N64 Information
![Example N64 system information](./images/system-information.png "Example N64 system information")
This screen will show information about your N64 console. At the time of writing, it will show the status of the Expansion Pak, 64DD and all the Controller ports.

8
docs/41_mp3_player.md Normal file
View File

@ -0,0 +1,8 @@
[Return to the index](./00_index.md)
## MP3 Player
<!-- Needs a screenshot of the MP3 player -->
The N64FlashcartMenu includes an MP3 Player that can read MP3 files from the SD Card. MP3 sound files must have a sample rate of 44100 Hz or higher.
Whilst in the menu, select an MP3 file in the File Browser to go to the MP3 Player screen, where the audio playback will begin immediately.
Here you can pause the playback by pressing the `A` Button, and skip the audio forward or backwards by pressing either `Left` or `Right` on the directional buttons or the Control Stick. When you want to stop the playback and return to the File Browser, press the `B` Button.

View File

@ -0,0 +1,3 @@
[Return to the index](./00_index.md)
## Advanced Menu Customization
Advanced customization options are currently [experimental](65_experimental.md).

6
docs/62_hardware_mods.md Normal file
View File

@ -0,0 +1,6 @@
[Return to the index](./00_index.md)
## N64 Hardware Modifications Compatibility
<!-- TBW by people who can actually speak properly about hardware modifications. -->
The following known N64 modifications are supported:
- PixelFX HDMI Game ID (works out the box)

18
docs/65_experimental.md Normal file
View File

@ -0,0 +1,18 @@
[Return to the index](./00_index.md)
## Experimental Features (Subject to change)
### ROM info descriptions (pre-release only)
To show a ROM description in the N64 ROM information screen, add a `.ini` file next to the game ROM file with the same name and the following content:
```ini
[metadata]
description=This is the ROM description that does X Y Z.
```
Text files must use ASCII characters only, Linux `LF` endings (CRLF is not supported) and the descriptions themselves must be limited to 300 characters.
### Customizing the font
The N64FlashcartMenu allows the ability to test new fonts or adding regional characters without recompiling the menu. However the font is explicitly linked to the currently used version of the libdragon SDK.
Add a `font64` file to the `sd:/menu/` directory called `custom.font64`.
<!-- Would be best if we can just link to an actual copy of Mkfont executable file and not expect people to compile libdragon -->
You can build a font64 file with `Mkfont`, one of `libdragon`'s tools. At the time of writing, you will need to obtain `libdragon`'s [preview branch artifacts](https://github.com/DragonMinded/libdragon/actions/workflows/build-tool-windows.yml) to find out a copy of the prebuilt Windows executable. [Read its related Wiki page](https://github.com/DragonMinded/libdragon/wiki/Mkfont) for usage information.

18
docs/81_faq.md Normal file
View File

@ -0,0 +1,18 @@
[Return to the index](./00_index.md)
## Frequently Asked Questions (FAQ)
### ROM hack (insert hack name here) does not work
- Most ROM hacks rely on an Expansion Pak, or might be only compatible with emulators.
- Some (very old) hacks override the CIC or save type that is expected from the internal N64FlashcartMenu database. (for more information on how to change the expected types, [read here](./12_rom_configuration.md)).
### My Roms are all in individual ZIP files and it is hassle to extract them before adding them to the SD card
You can try running a powershell script to extract them before adding them to the SD card:
```
$exts = @("*.n64", "*.z64", "*.v64"); Get-ChildItem -Filter "*.zip" | ForEach-Object { Expand-Archive $_.FullName -DestinationPath "$($_.BaseName)_temp" -Force; Get-ChildItem "$($_.BaseName)_temp\*" -File -Include $exts | Move-Item -Destination .; Remove-Item "$($_.BaseName)_temp" -Recurse -Force }
```
### I am using macOS and want to clean unwanted files before adding them to the SD card
On macOS, if you have extracted ROM's from ZIP or other compressed files, run `dot_clean -m /Volumes/SummerCart` to clear those awful dotfiles.
### I have changed the menu/config.ini file manually and things are not working.
Delete the file. It will be re-created automatically with the default settings.

View File

@ -0,0 +1,77 @@
[Return to the index](./00_index.md)
## Developer Guide
You can use a dev container in VSCode to ease development.
Expected pre-requsites:
* Docker container environment (for dev container support).
* VSCode.
### A quick start video tutorial on how to set up your environment
[![Devcontainer quick start guide](http://img.youtube.com/vi/h05ufOsRgZU/0.jpg)](http://www.youtube.com/watch?v=h05ufOsRgZU "Devcontainer quick start guide").
### How to deploy
#### SC64
* Download the deployer [here](https://github.com/Polprzewodnikowy/SummerCart64/releases/download/v2.20.0/sc64-deployer-windows-v2.20.0.zip).
* Extract and place `sc64deployer.exe` in the `tools/sc64` directory.
Make sure that your firmware is compatible (currently v2.20.0+).
See: [here](https://github.com/Polprzewodnikowy/SummerCart64/blob/v2.20.0/docs/00_quick_startup_guide.md#firmware-backupupdate)
##### From the devcontainer
It is not currently possible to directly communicate with USB devices. BUT, you can use a proxy TCP/IP connection as a workaround.
To 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 generated by running `make all` or running `make sc64`. You can then copy the resulting `sc64menu.n64` file to your SD card.
#### Ares Emulator
For ease of development and debugging, the N64FlashcartMenu 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 submodules
To update to the latest version, use `git submodule update --remote` from the terminal.
### libdragon
This repo currently uses the `preview` branch as a submodule at a specific commit.
* To ensure your local instance is building against it, use `cd ./libdragon && make clobber -j && make libdragon tools -j && make install tools-install -j && cd ..`
## 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
Testing the documentation locally allows you to preview changes and ensure everything renders correctly before submitting your changes.
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
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

@ -1 +1 @@
Subproject commit 352679922bad5403f8ea4b9123dfe0403d030bc2
Subproject commit 47ca3ee8906d01e2cf9aad1c8c3c248d635c6c13

View File

@ -1,8 +1,10 @@
#include <libdragon.h>
#include "boot.h"
#include "boot_io.h"
#include "crc32.h"
#include "boot.h"
#include "cheats.h"
#include "cic.h"
#include "reboot.h"
#define C0_STATUS_FR (1 << 26)
@ -10,85 +12,72 @@
#define C0_STATUS_CU1 (1 << 29)
extern uint32_t reboot_start __attribute__((section(".text")));
extern size_t reboot_size __attribute__((section(".text")));
extern int reboot_entry_offset __attribute__((section(".text")));
typedef struct {
const uint32_t crc32;
const uint8_t seed;
} ipl3_crc32_t;
static const ipl3_crc32_t ipl3_crc32[] = {
{ .crc32 = 0x587BD543, .seed = 0xAC }, // 5101
{ .crc32 = 0x6170A4A1, .seed = 0x3F }, // 6101
{ .crc32 = 0x009E9EA3, .seed = 0x3F }, // 7102
{ .crc32 = 0x90BB6CB5, .seed = 0x3F }, // 6102/7101
{ .crc32 = 0x0B050EE0, .seed = 0x78 }, // x103
{ .crc32 = 0x98BC2C86, .seed = 0x91 }, // x105
{ .crc32 = 0xACC8580A, .seed = 0x85 }, // x106
{ .crc32 = 0x0E018159, .seed = 0xDD }, // 5167
{ .crc32 = 0x10C68B18, .seed = 0xDD }, // NDXJ0
{ .crc32 = 0xBC605D0A, .seed = 0xDD }, // NDDJ0
{ .crc32 = 0x502C4466, .seed = 0xDD }, // NDDJ1
{ .crc32 = 0x0C965795, .seed = 0xDD }, // NDDJ2
{ .crc32 = 0x8FEBA21E, .seed = 0xDE }, // NDDE0
};
static io32_t *boot_get_device_base (boot_params_t *params) {
io32_t *device_base_address = ROM_CART;
if (params->device_type == BOOT_DEVICE_TYPE_DD) {
if (params->device_type == BOOT_DEVICE_TYPE_64DD) {
device_base_address = ROM_DDIPL;
}
return device_base_address;
}
static bool boot_detect_cic_seed (boot_params_t *params) {
static cic_type_t boot_detect_cic (boot_params_t *params) {
io32_t *base = boot_get_device_base(params);
uint32_t ipl3[1008] __attribute__((aligned(8)));
uint8_t ipl3[IPL3_LENGTH] __attribute__((aligned(8)));
data_cache_hit_writeback_invalidate(ipl3, sizeof(ipl3));
dma_read_raw_async(ipl3, (uint32_t) (&base[16]), sizeof(ipl3));
dma_wait();
uint32_t crc32 = crc32_calculate(ipl3, sizeof(ipl3));
for (int i = 0; i < sizeof(ipl3_crc32) / sizeof(ipl3_crc32_t); i++) {
if (ipl3_crc32[i].crc32 == crc32) {
params->cic_seed = ipl3_crc32[i].seed;
return true;
}
}
return false;
return cic_detect(ipl3);
}
bool boot_is_warm (void) {
return (OS_INFO->reset_type == OS_INFO_RESET_TYPE_NMI);
}
void boot (boot_params_t *params) {
if (params->tv_type == BOOT_TV_TYPE_PASSTHROUGH) {
params->tv_type = OS_INFO->tv_type;
}
cic_type_t cic_type = boot_detect_cic(params);
if (params->detect_cic_seed) {
if (!boot_detect_cic_seed(params)) {
params->cic_seed = 0x3F;
}
params->cic_seed = cic_get_seed(cic_type);
}
OS_INFO->mem_size_6105 = OS_INFO->mem_size;
if (params->tv_type == BOOT_TV_TYPE_PASSTHROUGH) {
switch (get_tv_type()) {
case TV_PAL:
params->tv_type = BOOT_TV_TYPE_PAL;
break;
case TV_NTSC:
params->tv_type = BOOT_TV_TYPE_NTSC;
break;
case TV_MPAL:
params->tv_type = BOOT_TV_TYPE_MPAL;
break;
default:
params->tv_type = BOOT_TV_TYPE_NTSC;
break;
}
}
C0_WRITE_STATUS(C0_STATUS_CU1 | C0_STATUS_CU0 | C0_STATUS_FR);
while (!(cpu_io_read(&SP->SR) & SP_SR_HALT));
cpu_io_write(&SP->SR, SP_SR_CLR_INTR | SP_SR_SET_HALT);
cpu_io_write(&SP->SR,
SP_SR_CLR_SIG7 |
SP_SR_CLR_SIG6 |
SP_SR_CLR_SIG5 |
SP_SR_CLR_SIG4 |
SP_SR_CLR_SIG3 |
SP_SR_CLR_SIG2 |
SP_SR_CLR_SIG1 |
SP_SR_CLR_SIG0 |
SP_SR_CLR_INTR_BREAK |
SP_SR_CLR_SSTEP |
SP_SR_CLR_INTR |
SP_SR_CLR_BROKE |
SP_SR_SET_HALT
);
cpu_io_write(&SP->SEMAPHORE, 0);
cpu_io_write(&SP->PC, 0);
while (cpu_io_read(&SP->DMA_BUSY));
@ -106,7 +95,7 @@ void boot (boot_params_t *params) {
io32_t *reboot_dst = SP_MEM->IMEM;
size_t reboot_instructions = (size_t) (&reboot_size) / sizeof(uint32_t);
for (int i = 0; i < reboot_instructions; i++) {
for (unsigned int i = 0; i < reboot_instructions; i++) {
cpu_io_write(&reboot_dst[i], reboot_src[i]);
}
@ -134,15 +123,16 @@ void boot (boot_params_t *params) {
cpu_io_write(&ipl3_dst[i], io_read((uint32_t) (&ipl3_src[i])));
}
register void (*entry_point)(void) asm ("t3");
bool cheats_installed = cheats_install(cic_type, params->cheat_list);
register uint32_t skip_rdram_reset asm ("a0");
register uint32_t boot_device asm ("s3");
register uint32_t tv_type asm ("s4");
register uint32_t reset_type asm ("s5");
register uint32_t cic_seed asm ("s6");
register uint32_t version asm ("s7");
void *stack_pointer;
entry_point = (void (*)(void)) UNCACHED(&SP_MEM->IMEM[(int) (&reboot_entry_offset)]);
skip_rdram_reset = cheats_installed;
boot_device = (params->device_type & 0x01);
tv_type = (params->tv_type & 0x03);
reset_type = BOOT_RESET_TYPE_COLD;
@ -151,18 +141,17 @@ void boot (boot_params_t *params) {
: (params->tv_type == BOOT_TV_TYPE_NTSC) ? 1
: (params->tv_type == BOOT_TV_TYPE_MPAL) ? 4
: 0;
stack_pointer = (void *) UNCACHED(&SP_MEM->IMEM[1020]);
asm volatile (
"move $sp, %[stack_pointer] \n"
"jr %[entry_point] \n" ::
[entry_point] "r" (entry_point),
"la $t3, reboot \n"
"jr $t3 \n" ::
[skip_rdram_reset] "r" (skip_rdram_reset),
[boot_device] "r" (boot_device),
[tv_type] "r" (tv_type),
[reset_type] "r" (reset_type),
[cic_seed] "r" (cic_seed),
[version] "r" (version),
[stack_pointer] "r" (stack_pointer)
[version] "r" (version) :
"t3"
);
while (1);

View File

@ -7,42 +7,43 @@
#ifndef BOOT_H__
#define BOOT_H__
#include <stdbool.h>
#include <stdint.h>
/** @brief Boot device type enumeration */
typedef enum {
BOOT_DEVICE_TYPE_ROM = 0,
BOOT_DEVICE_TYPE_DD = 1,
BOOT_DEVICE_TYPE_ROM = 0, /**< Boot from ROM */
BOOT_DEVICE_TYPE_64DD = 1, /**< Boot from 64DD */
} boot_device_type_t;
/** @brief Reset type enumeration */
typedef enum {
BOOT_RESET_TYPE_COLD = 0,
BOOT_RESET_TYPE_NMI = 1,
BOOT_RESET_TYPE_COLD = 0, /**< Cold reset */
BOOT_RESET_TYPE_NMI = 1, /**< Non-maskable interrupt reset */
} boot_reset_type_t;
/** @brief TV type enumeration */
typedef enum {
BOOT_TV_TYPE_PAL = 0,
BOOT_TV_TYPE_NTSC = 1,
BOOT_TV_TYPE_MPAL = 2,
BOOT_TV_TYPE_PASSTHROUGH = 3,
BOOT_TV_TYPE_PAL = 0, /**< PAL TV type */
BOOT_TV_TYPE_NTSC = 1, /**< NTSC TV type */
BOOT_TV_TYPE_MPAL = 2, /**< MPAL TV type */
BOOT_TV_TYPE_PASSTHROUGH = 3, /**< Passthrough TV type */
} boot_tv_type_t;
/** @brief Boot Parameters Structure */
typedef struct {
boot_device_type_t device_type;
boot_tv_type_t tv_type;
uint8_t cic_seed;
bool detect_cic_seed;
boot_device_type_t device_type; /**< Type of boot device */
boot_tv_type_t tv_type; /**< TV type */
uint8_t cic_seed; /**< CIC seed */
bool detect_cic_seed; /**< Flag to detect CIC seed */
uint32_t *cheat_list; /**< Pointer to the cheat list */
} boot_params_t;
bool boot_is_warm (void);
/**
* @brief Boot the system with the specified parameters.
*
* @param params Pointer to the boot parameters structure.
*/
void boot (boot_params_t *params);
#endif
#endif /* BOOT_H__ */

View File

@ -7,39 +7,79 @@
#ifndef BOOT_IO_H__
#define BOOT_IO_H__
#include <stddef.h>
#include <stdint.h>
/**
* @typedef io8_t
* @brief 8-bit volatile IO type.
*/
typedef volatile uint8_t io8_t;
/**
* @typedef io32_t
* @brief 32-bit volatile IO type.
*/
typedef volatile uint32_t io32_t;
/**
* @brief Convert an address to its uncached equivalent.
*
* This macro takes an address and converts it to its uncached equivalent
* by setting the appropriate bits.
*
* @param address The address to convert.
* @return The uncached equivalent of the address.
*/
#define UNCACHED(address) ((typeof(address)) (((io32_t) (address)) | (0xA0000000UL)))
/**
* @brief Memory Structure.
*
* This structure represents the memory layout for the SP (Signal Processor),
* containing both Data Memory (DMEM) and Instruction Memory (IMEM).
*/
typedef struct {
io32_t DMEM[1024];
io32_t IMEM[1024];
io32_t DMEM[1024]; /**< Data Memory (DMEM) array of 1024 32-bit words. */
io32_t IMEM[1024]; /**< Instruction Memory (IMEM) array of 1024 32-bit words. */
} sp_mem_t;
/**
* @brief Base address for SP memory.
*/
#define SP_MEM_BASE (0x04000000UL)
/**
* @brief Pointer to the SP memory structure.
*/
#define SP_MEM ((sp_mem_t *) SP_MEM_BASE)
/**
* @brief SP Registers Structure.
*
* This structure represents the registers for the SP (Signal Processor).
*/
typedef struct {
io32_t PADDR;
io32_t MADDR;
io32_t RD_LEN;
io32_t WR_LEN;
io32_t SR;
io32_t DMA_FULL;
io32_t DMA_BUSY;
io32_t SEMAPHORE;
io32_t PADDR; /**< Physical Address Register. */
io32_t MADDR; /**< Memory Address Register. */
io32_t RD_LEN; /**< Read Length Register. */
io32_t WR_LEN; /**< Write Length Register. */
io32_t SR; /**< Status Register. */
io32_t DMA_FULL; /**< DMA Full Register. */
io32_t DMA_BUSY; /**< DMA Busy Register. */
io32_t SEMAPHORE; /**< Semaphore Register. */
io32_t __reserved[0xFFF8];
io32_t PC; /**< Program Counter Register. */
} sp_regs_t;
/**
* @brief Base address for SP registers.
*/
#define SP_BASE (0x04040000UL)
/**
* @brief Pointer to the SP registers structure.
*/
#define SP ((sp_regs_t *) SP_BASE)
#define SP_SR_HALT (1 << 0)
@ -83,17 +123,20 @@ typedef struct {
#define SP_SR_CLR_SIG7 (1 << 23)
#define SP_SR_SET_SIG7 (1 << 24)
/** @brief DPC Registers Structure */
/**
* @brief DPC Registers Structure.
*
* This structure represents the registers for the DPC (Display Processor).
*/
typedef struct {
io32_t START;
io32_t END;
io32_t CURRENT;
io32_t SR;
io32_t CLOCK;
io32_t BUF_BUSY;
io32_t PIPE_BUSY;
io32_t TMEM;
io32_t START; /**< Start Register. */
io32_t END; /**< End Register. */
io32_t CURRENT; /**< Current Register. */
io32_t SR; /**< Status Register. */
io32_t CLOCK; /**< Clock Register. */
io32_t BUF_BUSY; /**< Buffer Busy Register. */
io32_t PIPE_BUSY; /**< Pipe Busy Register. */
io32_t TMEM; /**< TMEM Register. */
} dpc_regs_t;
#define DPC_BASE (0x04100000UL)
@ -121,23 +164,26 @@ typedef struct {
#define DPC_SR_CLR_CMD_CTR (1 << 8)
#define DPC_SR_CLR_CLOCK_CTR (1 << 9)
/** @brief Video Interface Registers Structure */
/**
* @brief Video Interface Registers Structure.
*
* This structure represents the registers for the Video Interface (VI).
*/
typedef struct {
io32_t CR;
io32_t MADDR;
io32_t H_WIDTH;
io32_t V_INTR;
io32_t CURR_LINE;
io32_t TIMING;
io32_t V_SYNC;
io32_t H_SYNC;
io32_t H_SYNC_LEAP;
io32_t H_LIMITS;
io32_t V_LIMITS;
io32_t COLOR_BURST;
io32_t H_SCALE;
io32_t V_SCALE;
io32_t CR; /**< Control Register. */
io32_t MADDR; /**< Memory Address. */
io32_t H_WIDTH; /**< Horizontal Width. */
io32_t V_INTR; /**< Vertical Interrupt. */
io32_t CURR_LINE; /**< Current Line. */
io32_t TIMING; /**< Timings. */
io32_t V_SYNC; /**< Vertical Sync. */
io32_t H_SYNC; /**< Horizontal Sync. */
io32_t H_SYNC_LEAP; /**< Horizontal Sync Leap. */
io32_t H_LIMITS; /**< Horizontal Limits. */
io32_t V_LIMITS; /**< Vertical Limits. */
io32_t COLOR_BURST; /**< Color Burst. */
io32_t H_SCALE; /**< Horizontal Scale. */
io32_t V_SCALE; /**< Vertical Scale. */
} vi_regs_t;
#define VI_BASE (0x04400000UL)
@ -159,14 +205,18 @@ typedef struct {
#define VI_CURR_LINE_FIELD (1 << 0)
/**
* @brief Audio Interface Registers Structure.
*
* This structure represents the registers for the Audio Interface (AI).
*/
typedef struct {
io32_t MADDR;
io32_t LEN;
io32_t CR;
io32_t SR;
io32_t DACRATE;
io32_t BITRATE;
io32_t MADDR; /**< Memory Address. */
io32_t LEN; /**< Length of bytes. */
io32_t CR; /**< Control Register. */
io32_t SR; /**< Status Register. */
io32_t DACRATE; /**< DAC rate. */
io32_t BITRATE; /**< Bit rate. */
} ai_regs_t;
#define AI_BASE (0x04500000UL)
@ -176,20 +226,23 @@ typedef struct {
#define AI_SR_FIFO_FULL (1 << 31)
#define AI_CR_DMA_ON (1 << 0)
/** @brief Parallel Interface Register Structure */
/**
* @brief Peripheral Interface Register Structure.
*
* This structure represents the registers for the Peripheral Interface (PI).
*/
typedef struct {
io32_t MADDR;
io32_t PADDR;
io32_t RDMA;
io32_t WDMA;
io32_t SR;
io32_t MADDR; /**< Memory Address. */
io32_t PADDR; /**< Cart Address. */
io32_t RDMA; /**< Read Length. */
io32_t WDMA; /**< Write Length. */
io32_t SR; /**< Status Register. */
struct {
io32_t LAT;
io32_t PWD;
io32_t PGS;
io32_t RLS;
} DOM[2];
io32_t LAT; /**< Latch Value. */
io32_t PWD; /**< Pulse Width Value. */
io32_t PGS; /**< Page Size Value. */
io32_t RLS; /**< Release Value. */
} DOM[2]; /**< Domain 2 Registers. */
} pi_regs_t;
#define PI_BASE (0x04600000UL)
@ -201,45 +254,33 @@ typedef struct {
#define PI_SR_RESET (1 << 0)
#define PI_SR_CLR_INTR (1 << 1)
#define ROM_DDIPL_BASE (0x06000000UL)
#define ROM_DDIPL ((io32_t *) ROM_DDIPL_BASE)
#define ROM_CART_BASE (0x10000000UL)
#define ROM_CART ((io32_t *) ROM_CART_BASE)
typedef struct {
uint32_t tv_type;
uint32_t device_type;
uint32_t device_base;
uint32_t reset_type;
uint32_t cic_id;
uint32_t version;
uint32_t mem_size;
uint8_t app_nmi_buffer[64];
uint32_t __reserved_1[37];
uint32_t mem_size_6105;
} os_info_t;
#define OS_INFO_BASE (0x80000300UL)
#define OS_INFO ((os_info_t *) OS_INFO_BASE)
#define OS_INFO_RESET_TYPE_COLD (0)
#define OS_INFO_RESET_TYPE_NMI (1)
/**
* @brief Read a value from a CPU IO address.
*
* @param address The address to read from.
* @return uint32_t The value read from the address.
*/
static inline uint32_t cpu_io_read (io32_t *address) {
io32_t *uncached = UNCACHED(address);
uint32_t value = *uncached;
return value;
}
/**
* @brief Write a value to a CPU IO address.
*
* @param address The address to write to.
* @param value The value to write.
*/
static inline void cpu_io_write (io32_t *address, uint32_t value) {
io32_t *uncached = UNCACHED(address);
*uncached = value;
}
#endif
#endif /* BOOT_IO_H__ */

376
src/boot/cheats.c Normal file
View File

@ -0,0 +1,376 @@
/**
* @file cheats.c
* @brief Cheat Engine Implementation
* @ingroup boot
*/
#include <libdragon.h>
#include "boot_io.h"
#include "cheats.h"
#include "vr4300_asm.h"
#define HIT_INVALIDATE_I ((4 << 2) | 0)
#define HIT_WRITE_BACK_D ((6 << 2) | 1)
#define D_CACHE_LINE_SIZE (16)
#define CAUSE_IRQ_PRE_NMI (1 << 12)
#define CAUSE_EXC_CODE_MASK (0x7C)
#define CAUSE_EXC_CODE_WATCH (0x5C)
#define WATCHLO_W (1 << 0)
#define RELOCATED_EXCEPTION_HANDLER_ADDRESS (0x80000120)
#define EXCEPTION_HANDLER_ADDRESS (0x80000180)
#define PATCHER_ADDRESS (0x80700000)
#define ENGINE_TEMPORARY_ADDRESS (PATCHER_ADDRESS + 0x10000)
#define DEFAULT_ENGINE_ADDRESS (0x807C5C00)
/** @brief Cheat structure */
typedef struct {
uint8_t type; /**< Cheat type */
uint32_t address; /**< Cheat address */
uint16_t value; /**< Cheat value */
} cheat_t;
/** @brief Cheat entry structure */
typedef struct {
cheat_t main; /**< Main cheat */
cheat_t sub; /**< Sub cheat */
} cheat_entry_t;
/** @brief Special cheat types enumeration */
typedef enum {
SPECIAL_DISABLE_EXPANSION_PAK = 0xEE, /**< Disable Expansion Pak */
SPECIAL_WRITE_BYTE_ON_BOOT = 0xF0, /**< Write byte on boot */
SPECIAL_WRITE_SHORT_ON_BOOT = 0xF1, /**< Write short on boot */
SPECIAL_SET_STORE_LOCATION = 0xFF, /**< Set store location */
} cheat_type_special_t;
#define IS_WIDTH_16(t) ((t) & (1 << 0))
#define IS_CONDITION_NOT_EQUAL(t) ((t) & (1 << 1))
#define IS_CONDITION_GS_BUTTON(t) ((t) & (1 << 3))
#define IS_TYPE_REPEATER(t) ((t) == 0x50)
#define IS_TYPE_WRITE(t) ((((t)&0xF0) == 0x80) || (((t)&0xF0) == 0xA0))
#define IS_TYPE_CONDITIONAL(t) (((t)&0xF0) == 0xD0)
#define IS_DOUBLE_ENTRY(t) (IS_TYPE_CONDITIONAL(t) || IS_TYPE_REPEATER(t))
/**
* @brief Patch the IPL3 with the cheat engine.
*
* @param cic_type The CIC type.
* @param target The target address.
* @return true if successful, false otherwise.
*/
static bool cheats_patch_ipl3 (cic_type_t cic_type, io32_t *target) {
uint32_t patch_offset = 0;
uint32_t j_instruction = I_J((uint32_t)(target));
io32_t *ipl3 = SP_MEM->DMEM;
switch (cic_type) {
case CIC_5101: patch_offset = 476; break;
case CIC_6101:
case CIC_7102: patch_offset = 476; break;
case CIC_x102: patch_offset = 475; break;
case CIC_x103: patch_offset = 472; break;
case CIC_x105: patch_offset = 499; break;
case CIC_x106: patch_offset = 488; break;
default: return true;
}
// NOTE: Check for "jr $t1" instruction
// Libdragon IPL3 could be brute-force signed with any retail
// CIC seed and checksum, and we support only retail libultra IPL3
if (cpu_io_read(&ipl3[patch_offset]) != I_JR(REG_T1)) {
return false;
}
switch (cic_type) {
case CIC_x105:
// NOTE: This disables game code checksum verification
cpu_io_write(&ipl3[486], I_NOP());
break;
case CIC_x106:
// NOTE: CIC x106 IPL3 is partially scrambled
j_instruction ^= 0x8188764A;
break;
default: break;
}
cpu_io_write(&ipl3[patch_offset], j_instruction);
return false;
}
/**
* @brief Get the next cheat entry from the cheat list.
*
* @param cheat_list Pointer to the cheat list.
* @param cheat Pointer to the cheat entry structure.
* @return true if successful, false otherwise.
*/
static bool cheats_get_next (uint32_t **cheat_list, cheat_entry_t *cheat) {
cheat_t *c = &cheat->main;
cheat->sub.type = 0;
for (int i = 0; i < 2; i++) {
uint32_t raw[2] = {(*cheat_list)[0], (*cheat_list)[1]};
(*cheat_list) += 2;
if ((raw[0] == 0) && (raw[1] == 0)) {
return false;
}
c->type = ((raw[0] >> 24) & 0xFF);
c->address = (raw[0] & 0xA07FFFFF);
c->value = (raw[1] & 0xFFFF);
if (!IS_DOUBLE_ENTRY(c->type)) {
break;
}
c = &cheat->sub;
}
return true;
}
/**
* @brief Get the engine address from the cheat list.
*
* @param cheat_list Pointer to the cheat list.
* @return io32_t* The engine address.
*/
static io32_t *cheats_get_engine_address (uint32_t *cheat_list) {
cheat_entry_t cheat;
while (cheats_get_next(&cheat_list, &cheat)) {
if (cheat.main.type == SPECIAL_SET_STORE_LOCATION) {
return (io32_t *)(cheat.main.address & 0x807FFFFF);
}
}
return (io32_t *)(DEFAULT_ENGINE_ADDRESS);
}
/**
* @brief Update the cache for the specified memory range.
*
* @param start The start address.
* @param end The end address.
*/
static void cheats_update_cache (volatile void *start, volatile void *end) {
data_cache_hit_writeback(start, (end - start));
inst_cache_hit_invalidate(start, (end - start));
}
/**
* @brief Install the cheat engine.
*
* @param cic_type The CIC type.
* @param cheat_list Pointer to the cheat list.
* @return true if successful, false otherwise.
*/
bool cheats_install (cic_type_t cic_type, uint32_t *cheat_list) {
if (!cheat_list) {
return false;
}
io32_t *engine_start = (io32_t *)(ENGINE_TEMPORARY_ADDRESS);
io32_t *engine_p = engine_start;
io32_t *patcher_start = (io32_t *)(PATCHER_ADDRESS);
io32_t *patcher_p = patcher_start;
if (cheats_patch_ipl3(cic_type, patcher_start)) {
return false;
}
io32_t *final_engine_address = cheats_get_engine_address(cheat_list);
// Original watch exception handler code written by Jay Oster 'Parasyte'
// https://github.com/parasyte/alt64/blob/master/utils.c#L1024-L1054
uint32_t ori_placeholder_instruction = I_ORI(REG_ZERO, REG_K0, A_OFFSET(RELOCATED_EXCEPTION_HANDLER_ADDRESS));
uint32_t ori_placeholder_address = (uint32_t)(final_engine_address + 20);
// Load cause register
*engine_p++ = I_MFC0(REG_K0, C0_REG_CAUSE);
// Disable watch exception when reset button is pressed
*engine_p++ = I_ANDI(REG_K1, REG_K0, CAUSE_IRQ_PRE_NMI);
*engine_p++ = I_BNEL(REG_K1, REG_ZERO, 1);
*engine_p++ = I_MTC0(REG_ZERO, C0_REG_WATCH_LO);
// Check if watch exception occurred, if yes then proceed to relocate the game exception handler
*engine_p++ = I_ANDI(REG_K0, REG_K0, CAUSE_EXC_CODE_MASK);
*engine_p++ = I_ORI(REG_K1, REG_ZERO, CAUSE_EXC_CODE_WATCH);
*engine_p++ = I_BNE(REG_K0, REG_K1, 15); // Skips to after the 'eret' instruction
// Extract base register number from the store instruction
*engine_p++ = I_MFC0(REG_K1, C0_REG_EPC);
*engine_p++ = I_LW(REG_K1, 0, REG_K1);
*engine_p++ = I_LUI(REG_K0, 0x03E0);
*engine_p++ = I_AND(REG_K1, REG_K0, REG_K1);
*engine_p++ = I_SRL(REG_K1, REG_K1, 5);
// Update create final instruction and update its target register number
*engine_p++ = I_LUI(REG_K0, ori_placeholder_instruction >> 16);
*engine_p++ = I_ORI(REG_K0, REG_K0, ori_placeholder_instruction);
*engine_p++ = I_OR(REG_K0, REG_K0, REG_K1);
// Write created instruction into placeholder
*engine_p++ = I_LUI(REG_K1, A_BASE(ori_placeholder_address));
*engine_p++ = I_SW(REG_K0, A_OFFSET(ori_placeholder_address), REG_K1);
// Force write and instruction cache invalidation
*engine_p++ = I_CACHE(HIT_WRITE_BACK_D, A_OFFSET(ori_placeholder_address), REG_K1);
*engine_p++ = I_CACHE(HIT_INVALIDATE_I, A_OFFSET(ori_placeholder_address), REG_K1);
// Load address base and execute created instruction
*engine_p++ = I_LUI(REG_K0, A_BASE(RELOCATED_EXCEPTION_HANDLER_ADDRESS));
*engine_p++ = I_NOP();
// Return from the exception
*engine_p++ = I_ERET();
cheat_entry_t cheat;
while (cheats_get_next(&cheat_list, &cheat)) {
cheat_t *c = &cheat.main;
if (IS_TYPE_REPEATER(c->type)) {
if ((!IS_TYPE_WRITE(cheat.sub.type)) || IS_CONDITION_GS_BUTTON(cheat.sub.type)) {
continue;
}
int count = ((c->address >> 8) & 0xFF);
int step = (c->address & 0xFF);
int16_t increment = (int16_t)(c->value);
c = &cheat.sub;
for (int i = 0; i < count; i++) {
*engine_p++ = I_LUI(REG_K0, A_BASE(c->address));
*engine_p++ = I_ORI(REG_K1, REG_ZERO, c->value);
*engine_p++ = IS_WIDTH_16(c->type) ? I_SH(REG_K1, A_OFFSET(c->address), REG_K0)
: I_SB(REG_K1, A_OFFSET(c->address), REG_K0);
c->address += step;
c->value += increment;
}
continue;
}
if (IS_TYPE_CONDITIONAL(c->type)) {
if ((!IS_TYPE_WRITE(cheat.sub.type)) || IS_CONDITION_GS_BUTTON(cheat.sub.type)) {
continue;
}
*engine_p++ = I_LUI(REG_K0, A_BASE(c->address));
*engine_p++ = IS_WIDTH_16(c->type) ? I_LHU(REG_K0, A_OFFSET(c->address), REG_K0)
: I_LBU(REG_K0, A_OFFSET(c->address), REG_K0);
*engine_p++ = I_ORI(REG_K1, REG_ZERO, c->value & (IS_WIDTH_16(c->type) ? 0xFFFF : 0xFF));
*engine_p++ = IS_CONDITION_NOT_EQUAL(c->type) ? I_BEQ(REG_K0, REG_K1, 3) : I_BNE(REG_K0, REG_K1, 3);
c = &cheat.sub;
}
if (IS_TYPE_WRITE(c->type)) {
if (IS_CONDITION_GS_BUTTON(c->type)) {
continue;
}
*engine_p++ = I_LUI(REG_K0, A_BASE(c->address));
*engine_p++ = I_ORI(REG_K1, REG_ZERO, c->value);
*engine_p++ = IS_WIDTH_16(c->type) ? I_SH(REG_K1, A_OFFSET(c->address), REG_K0)
: I_SB(REG_K1, A_OFFSET(c->address), REG_K0);
continue;
}
switch (c->type) {
case SPECIAL_WRITE_BYTE_ON_BOOT:
case SPECIAL_WRITE_SHORT_ON_BOOT: {
*patcher_p++ = I_LUI(REG_K0, A_BASE(c->address));
*patcher_p++ = I_ORI(REG_K1, REG_ZERO, c->value);
*patcher_p++ = IS_WIDTH_16(c->type) ? I_SH(REG_K1, A_OFFSET(c->address), REG_K0)
: I_SB(REG_K1, A_OFFSET(c->address), REG_K0);
break;
}
case SPECIAL_DISABLE_EXPANSION_PAK: {
*patcher_p++ = I_LUI(REG_K0, 0xA000);
*patcher_p++ = I_LUI(REG_K1, 0x0040);
*patcher_p++ = I_SW(REG_K1, 0x318, REG_K0);
*patcher_p++ = I_SW(REG_K1, 0x3F0, REG_K0);
break;
}
default: break;
}
}
*engine_p++ = I_J(RELOCATED_EXCEPTION_HANDLER_ADDRESS);
*engine_p++ = I_NOP();
uint32_t j_engine_from_handler = I_J((uint32_t)(final_engine_address));
// Copy engine to the final location
*patcher_p++ = I_LUI(REG_T3, A_BASE((uint32_t)(engine_start)));
*patcher_p++ = I_ADDIU(REG_T3, REG_T3, A_OFFSET((uint32_t)(engine_start)));
*patcher_p++ = I_LUI(REG_T4, A_BASE((uint32_t)(engine_p)));
*patcher_p++ = I_ADDIU(REG_T4, REG_T4, A_OFFSET((uint32_t)(engine_p)));
*patcher_p++ = I_LUI(REG_T5, A_BASE((uint32_t)(final_engine_address)));
*patcher_p++ = I_ADDIU(REG_T5, REG_T5, A_OFFSET((uint32_t)(final_engine_address)));
*patcher_p++ = I_ORI(REG_T6, REG_ZERO, 0);
*patcher_p++ = I_LW(REG_K1, 0, REG_T3);
*patcher_p++ = I_SW(REG_K1, 0, REG_T5);
*patcher_p++ = I_ADDIU(REG_T3, REG_T3, 4);
*patcher_p++ = I_ADDIU(REG_T5, REG_T5, 4);
*patcher_p++ = I_BNE(REG_T3, REG_T4, -5);
*patcher_p++ = I_ADDIU(REG_T6, REG_T6, 4);
// Force write and invalidate instruction cache
*patcher_p++ = I_LUI(REG_T5, A_BASE((uint32_t)(final_engine_address)));
*patcher_p++ = I_ADDIU(REG_T5, REG_T5, A_OFFSET((uint32_t)(final_engine_address)));
*patcher_p++ = I_CACHE(HIT_WRITE_BACK_D, 0, REG_T5);
*patcher_p++ = I_CACHE(HIT_INVALIDATE_I, 0, REG_T5);
*patcher_p++ = I_ADDIU(REG_T6, REG_T6, -D_CACHE_LINE_SIZE);
*patcher_p++ = I_BGTZ(REG_T6, -4);
*patcher_p++ = I_ADDIU(REG_T5, REG_T5, D_CACHE_LINE_SIZE);
// Write jump instruction to the exception handler
*patcher_p++ = I_LUI(REG_K0, A_BASE(EXCEPTION_HANDLER_ADDRESS));
*patcher_p++ = I_ADDIU(REG_K0, REG_K0, A_OFFSET(EXCEPTION_HANDLER_ADDRESS));
*patcher_p++ = I_LUI(REG_K1, j_engine_from_handler >> 16);
*patcher_p++ = I_ORI(REG_K1, REG_K1, j_engine_from_handler);
*patcher_p++ = I_SW(REG_K1, 0, REG_K0);
*patcher_p++ = I_SW(REG_ZERO, 4, REG_K0);
*patcher_p++ = I_CACHE(HIT_WRITE_BACK_D, 0, REG_K0);
*patcher_p++ = I_CACHE(HIT_INVALIDATE_I, 0, REG_K0);
// Set watch exception on address 0x80000180
*patcher_p++ = I_ORI(REG_K1, REG_ZERO, EXCEPTION_HANDLER_ADDRESS | WATCHLO_W);
*patcher_p++ = I_MTC0(REG_K1, C0_REG_WATCH_LO);
*patcher_p++ = I_MTC0(REG_ZERO, C0_REG_WATCH_HI);
// Jump back to the game code
*patcher_p++ = I_JR(REG_T1);
*patcher_p++ = I_NOP();
cheats_update_cache(engine_start, engine_p);
cheats_update_cache(patcher_start, patcher_p);
return true;
}

25
src/boot/cheats.h Normal file
View File

@ -0,0 +1,25 @@
/**
* @file cheats.h
* @brief Header file for cheat installation functions.
* @ingroup boot
*/
#ifndef CHEATS_H__
#define CHEATS_H__
#include <stdint.h>
#include "cic.h"
/**
* @brief Installs cheats based on the CIC type.
*
* This function installs the cheats provided in the cheat list based on the
* specified CIC type.
*
* @param cic_type The type of CIC (Copy Protection Chip) used.
* @param cheat_list A pointer to an array of cheats to be installed.
* @return true if the cheats were successfully installed, false otherwise.
*/
bool cheats_install(cic_type_t cic_type, uint32_t *cheat_list);
#endif // CHEATS_H__

220
src/boot/cic.c Normal file
View File

@ -0,0 +1,220 @@
/**
* @file cic.c
* @brief CIC (Copy Protection) functions implementation
* @ingroup boot
*/
#include "cic.h"
/**
* @brief Get a 32-bit value from a byte array at the specified index.
*
* @param p Pointer to the byte array.
* @param index Index to get the value from.
* @return uint32_t The 32-bit value.
*/
static inline uint32_t _get (uint8_t *p, int index) {
int i = index * 4;
return (p[i] << 24 | p[i + 1] << 16 | p[i + 2] << 8 | p[i + 3]);
}
/**
* @brief Add two 32-bit values.
*
* @param a1 First value.
* @param a2 Second value.
* @return uint32_t The result of the addition.
*/
static inline uint32_t _add (uint32_t a1, uint32_t a2) {
return a1 + a2;
}
/**
* @brief Subtract one 32-bit value from another.
*
* @param a1 First value.
* @param a2 Second value.
* @return uint32_t The result of the subtraction.
*/
static inline uint32_t _sub (uint32_t a1, uint32_t a2) {
return a1 - a2;
}
/**
* @brief Multiply two 32-bit values.
*
* @param a1 First value.
* @param a2 Second value.
* @return uint32_t The result of the multiplication.
*/
static inline uint32_t _mul (uint32_t a1, uint32_t a2) {
return a1 * a2;
}
/**
* @brief Rotate a 32-bit value left by a specified number of bits.
*
* @param a The value to rotate.
* @param s The number of bits to rotate.
* @return uint32_t The result of the rotation.
*/
static inline uint32_t _rol (uint32_t a, uint32_t s) {
return ((a) << (s)) | ((a) >> (-(s) & 31));
}
/**
* @brief Rotate a 32-bit value right by a specified number of bits.
*
* @param a The value to rotate.
* @param s The number of bits to rotate.
* @return uint32_t The result of the rotation.
*/
static inline uint32_t _ror (uint32_t a, uint32_t s) {
return ((a) >> (s)) | ((a) << (-(s) & 31));
}
/**
* @brief Calculate the sum of three 32-bit values.
*
* @param a0 First value.
* @param a1 Second value.
* @param a2 Third value.
* @return uint32_t The result of the sum.
*/
static uint32_t _sum (uint32_t a0, uint32_t a1, uint32_t a2) {
uint64_t prod = ((uint64_t) (a0)) * (a1 == 0 ? a2 : a1);
uint32_t hi = (prod >> 32) & 0xFFFFFFFF;
uint32_t lo = prod & 0xFFFFFFFF;
uint32_t diff = hi - lo;
return (diff == 0) ? a0 : diff;
}
/**
* @brief Calculate the IPL3 checksum for the CIC.
*
* @param ipl3 Pointer to the IPL3 data.
* @param seed The seed value.
* @return uint64_t The calculated checksum.
*/
static uint64_t cic_calculate_ipl3_checksum (uint8_t *ipl3, uint8_t seed) {
const uint32_t MAGIC = 0x6C078965;
uint32_t data, prev, next;
data = prev = next = _get(ipl3, 0);
uint32_t init = _add(_mul(MAGIC, seed), 1) ^ data;
uint32_t buf[16];
for (int i = 0; i < 16; i++) {
buf[i] = init;
}
for (int i = 1; i <= 1008; i++) {
prev = data;
data = next;
buf[0] = _add(buf[0], _sum(_sub(1007, i), data, i));
buf[1] = _sum(buf[1], data, i);
buf[2] = buf[2] ^ data;
buf[3] = _add(buf[3], _sum(_add(data, 5), MAGIC, i));
buf[4] = _add(buf[4], _ror(data, prev & 0x1F));
buf[5] = _add(buf[5], _rol(data, prev >> 27));
buf[6] = (data < buf[6]) ? (_add(buf[3], buf[6]) ^ _add(data, i)) : (_add(buf[4], data) ^ buf[6]);
buf[7] = _sum(buf[7], _rol(data, prev & 0x1F), i);
buf[8] = _sum(buf[8], _ror(data, prev >> 27), i);
buf[9] = (prev < data) ? _sum(buf[9], data, i) : _add(buf[9], data);
if (i == 1008) {
break;
}
next = _get(ipl3, i);
buf[10] = _sum(_add(buf[10], data), next, i);
buf[11] = _sum(buf[11] ^ data, next, i);
buf[12] = _add(buf[12], buf[8] ^ data);
buf[13] = _add(buf[13], _add(_ror(data, data & 0x1F), _ror(next, next & 0x1F)));
buf[14] = _sum(_sum(buf[14], _ror(data, prev & 0x1F), i), _ror(next, data & 0x1F), i);
buf[15] = _sum(_sum(buf[15], _rol(data, prev >> 27), i), _rol(next, data >> 27), i);
}
uint32_t final_buf[4];
for (int i = 0; i < 4; i++) {
final_buf[i] = buf[0];
}
for (int i = 0; i < 16; i++) {
uint32_t data = buf[i];
final_buf[0] = _add(final_buf[0], _ror(data, data & 0x1F));
final_buf[1] = (data < final_buf[0]) ? _add(final_buf[1], data) : _sum(final_buf[1], data, i);
final_buf[2] = (((data & 0x02) >> 1) == (data & 0x01)) ? _add(final_buf[2], data) : _sum(final_buf[2], data, i);
final_buf[3] = ((data & 0x01) == 0x01) ? (final_buf[3] ^ data) : _sum(final_buf[3], data, i);
}
uint32_t final_sum = _sum(final_buf[0], final_buf[1], 16);
uint32_t final_xor = final_buf[3] ^ final_buf[2];
uint64_t checksum = (((((uint64_t) (final_sum)) & 0xFFFF)) << 32) | (final_xor);
return checksum;
}
/**
* @brief Detect the CIC type based on the IPL3 data.
*
* @param ipl3 Pointer to the IPL3 data.
* @return cic_type_t The detected CIC type.
*/
cic_type_t cic_detect (uint8_t *ipl3) {
switch (cic_calculate_ipl3_checksum(ipl3, 0x3F)) {
case 0x45CC73EE317AULL: return CIC_6101; // 6101
case 0x44160EC5D9AFULL: return CIC_7102; // 7102
case 0xA536C0F1D859ULL: return CIC_x102; // 6102 / 7101
}
switch (cic_calculate_ipl3_checksum(ipl3, 0x78)) {
case 0x586FD4709867ULL: return CIC_x103; // 6103 / 7103
}
switch (cic_calculate_ipl3_checksum(ipl3, 0x85)) {
case 0x2BBAD4E6EB74ULL: return CIC_x106; // 6106 / 7106
}
switch (cic_calculate_ipl3_checksum(ipl3, 0x91)) {
case 0x8618A45BC2D3ULL: return CIC_x105; // 6105 / 7105
}
switch (cic_calculate_ipl3_checksum(ipl3, 0xDD)) {
case 0x6EE8D9E84970ULL: return CIC_8401; // NDXJ0
case 0x6C216495C8B9ULL: return CIC_8301; // NDDJ0
case 0xE27F43BA93ACULL: return CIC_8302; // NDDJ1
case 0x32B294E2AB90ULL: return CIC_8303; // NDDJ2
case 0x083C6C77E0B1ULL: return CIC_5167; // 64DD Cartridge conversion
}
switch (cic_calculate_ipl3_checksum(ipl3, 0xDE)) {
case 0x05BA2EF0A5F1ULL: return CIC_8501; // NDDE0
}
return CIC_UNKNOWN;
}
/**
* @brief Get the seed value for the specified CIC type.
*
* @param cic_type The CIC type.
* @return uint8_t The seed value.
*/
uint8_t cic_get_seed (cic_type_t cic_type) {
switch (cic_type) {
case CIC_5101: return 0xAC;
case CIC_5167: return 0xDD;
case CIC_6101: return 0x3F;
case CIC_7102: return 0x3F;
case CIC_x102: return 0x3F;
case CIC_x103: return 0x78;
case CIC_x105: return 0x91;
case CIC_x106: return 0x85;
case CIC_8301: return 0xDD;
case CIC_8302: return 0xDD;
case CIC_8303: return 0xDD;
case CIC_8401: return 0xDD;
case CIC_8501: return 0xDE;
default: return 0x3F;
}
}

55
src/boot/cic.h Normal file
View File

@ -0,0 +1,55 @@
/**
* @file cic.h
* @brief Header file for CIC (Copy Protection Chip) related functions and definitions.
* @ingroup boot
*/
#ifndef CIC_H__
#define CIC_H__
#include <stdint.h>
#define IPL3_LENGTH (4032)
/**
* @enum cic_type_t
* @brief Enumeration of different CIC types.
*/
typedef enum {
CIC_5101, /**< CIC type 5101 */
CIC_5167, /**< CIC type 5167 */
CIC_6101, /**< CIC type 6101 */
CIC_7102, /**< CIC type 7102 */
CIC_x102, /**< CIC type x102 */
CIC_x103, /**< CIC type x103 */
CIC_x105, /**< CIC type x105 */
CIC_x106, /**< CIC type x106 */
CIC_8301, /**< CIC type 8301 */
CIC_8302, /**< CIC type 8302 */
CIC_8303, /**< CIC type 8303 */
CIC_8401, /**< CIC type 8401 */
CIC_8501, /**< CIC type 8501 */
CIC_UNKNOWN /**< Unknown CIC type */
} cic_type_t;
/**
* @brief Detects the CIC type based on the provided IPL3 data.
*
* This function analyzes the provided IPL3 data to determine the CIC type.
*
* @param ipl3 A pointer to the IPL3 data.
* @return The detected CIC type.
*/
cic_type_t cic_detect(uint8_t *ipl3);
/**
* @brief Gets the seed value for the specified CIC type.
*
* This function returns the seed value associated with the given CIC type.
*
* @param cic_type The type of CIC.
* @return The seed value for the specified CIC type.
*/
uint8_t cic_get_seed(cic_type_t cic_type);
#endif // CIC_H__

View File

@ -1,50 +0,0 @@
#include "crc32.h"
static const uint32_t crc32_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
};
uint32_t crc32_calculate (void *buffer, size_t length) {
uint32_t crc32 = 0xFFFFFFFFUL;
uint8_t *byte_pointer = (uint8_t *) (buffer);
while (length--) {
crc32 = (crc32 >> 8) ^ crc32_table[(crc32 & 0xFF) ^ (*byte_pointer++)];
}
return ~(crc32);
}

View File

@ -1,18 +0,0 @@
/**
* @file crc32.h
* @brief Flashcart Boot Checksum
* @ingroup boot
*/
#ifndef CRC32_H__
#define CRC32_H__
#include <stddef.h>
#include <stdint.h>
uint32_t crc32_calculate (void *buffer, size_t length);
#endif

View File

@ -1,23 +1,19 @@
#define RI_ADDRESS 0xA4700000
#include "reboot.h"
#define RI_MODE 0x00
#define RI_CONFIG 0x04
#define RI_CURRENT_LOAD 0x08
#define RI_SELECT 0x0C
#define RI_REFRESH 0x10
#define RI_LATENCY 0x14
#define RI_MODE_RESET 0x00000000
#define RI_MODE_STANDBY 0x0000000E
#define RDRAM_RESET_DELAY 1024
#define RDRAM_STANDBY_DELAY 512
#define IPL3_ENTRY 0xA4000040
#define REBOOT_ADDRESS 0xA4001000
#define STACK_ADDRESS 0xA4001FF0
#define RI_ADDRESS 0xA4700000
#define RI_SELECT 0x0C
#define RI_REFRESH 0x10
.set noat
.section .text.reboot, "ax", %progbits
.type reboot, %object
reboot_start:
.global reboot_start
@ -36,36 +32,39 @@ ipl2:
.set reorder
reboot_entry:
.equ reboot_entry_offset, ((. - reboot_start) / 4)
.global reboot_entry_offset
.set reboot, REBOOT_ADDRESS + (. - reboot_start)
.global reboot
bnez $s5, reset_rdram_skip
li $sp, STACK_ADDRESS
bnez $a0, reset_rdram_skip # Skip when cheats are enabled
bnez $s5, reset_rdram_skip # Skip when reset type is set to NMI
reset_rdram:
li $t0, RI_ADDRESS
li $t1, RI_MODE_RESET
sw $t1, RI_MODE($t0)
li $t2, RDRAM_RESET_DELAY
1:
addiu $t2, (-1)
bnez $t2, 1b
sw $zero, RI_CONFIG($t0)
sw $zero, RI_CURRENT_LOAD($t0)
sw $zero, RI_SELECT($t0)
sw $zero, RI_REFRESH($t0)
li $t1, RI_MODE_STANDBY
sw $t1, RI_MODE($t0)
li $t2, RDRAM_STANDBY_DELAY
1:
addiu $t2, (-1)
bnez $t2, 1b
sw $zero, RI_SELECT($t0)
reset_rdram_skip:
detect_console_region:
li $t0, 1
beq $s4, $zero, pal_console
beq $s4, $t0, ntsc_console
b mpal_console
pal_console:
li $ra, 0xA4001554
b prepare_registers
ntsc_console:
li $ra, 0xA4001550
b prepare_registers
mpal_console:
li $ra, 0xA4001554
prepare_registers:
move $at, $zero
move $v0, $zero
move $v1, $zero
@ -75,8 +74,7 @@ reset_rdram_skip:
move $a3, $zero
move $t0, $zero
move $t1, $zero
move $t2, $zero
move $t3, $zero
li $t2, 0x40
move $t4, $zero
move $t5, $zero
move $t6, $zero
@ -89,14 +87,11 @@ reset_rdram_skip:
move $k0, $zero
move $k1, $zero
move $gp, $zero
move $s8, $zero
move $ra, $zero
mtc0 $zero, $9
move $fp, $zero
run_ipl3:
li $t3, IPL3_ENTRY
jr $t3
.equ reboot_size, (. - reboot_start)
.set reboot_size, (. - reboot_start)
.global reboot_size

31
src/boot/reboot.h Normal file
View File

@ -0,0 +1,31 @@
/**
* @file reboot.h
* @brief Header file for reboot-related definitions.
* @ingroup boot
*/
#ifndef REBOOT_H__
#define REBOOT_H__
#ifndef __ASSEMBLER__
#include <stdint.h>
#include <stddef.h>
/**
* @brief Start address of the reboot code.
*
* This variable marks the start address of the reboot code section.
*/
extern uint32_t reboot_start __attribute__((section(".text")));
/**
* @brief Size of the reboot code.
*
* This variable holds the size of the reboot code section.
*/
extern size_t reboot_size __attribute__((section(".text")));
#endif // __ASSEMBLER__
#endif // REBOOT_H__

413
src/boot/vr4300_asm.h Normal file
View File

@ -0,0 +1,413 @@
/**
* @file vr4300_asm.h
* @brief Header file for v4300 CPU-related definitions.
* @ingroup boot
*/
#ifndef VR4300_ASM_H__
#define VR4300_ASM_H__
#include <stdint.h>
/**
* @brief VR4300 Instruction Structure
*
* This structure represents a VR4300 instruction, which can be of different types (R-type, I-type, J-type, etc.).
*/
typedef union {
uint32_t raw; /**< Raw 32-bit instruction */
struct {
uint32_t op : 6; /**< Opcode field */
uint32_t rs : 5; /**< Source register */
uint32_t rt : 5; /**< Target register */
uint32_t imm : 16; /**< Immediate value */
} i_type; /**< I-type instruction format */
struct {
uint32_t op : 6; /**< Opcode field */
uint32_t target : 26; /**< Target Address field */
} j_type; /**< J-type instruction format */
struct {
uint32_t op : 6; /**< Opcode field */
uint32_t rs : 5; /**< Source register */
uint32_t rt : 5; /**< Target register */
uint32_t rd : 5; /**< Destination register */
uint32_t sa : 5; /**< Shift amount */
uint32_t funct : 6; /**< Function field */
} r_type; /**< Alternate R-type instruction format */
struct {
uint32_t op : 6; /**< Opcode field */
uint32_t co : 1; /**< Coprocessor operation bit */
uint32_t funct : 25; /**< Function field */
} c_type; /**< C-type instruction format */
} vr4300_instruction_t;
/**
* @brief VR4300 Opcode Enumeration
*
* Enumeration for different opcodes used in VR4300 instructions.
*/
typedef enum {
OP_SPECIAL,
OP_REGIMM,
OP_J,
OP_JAL,
OP_BEQ,
OP_BNE,
OP_BLEZ,
OP_BGTZ,
OP_ADDI,
OP_ADDIU,
OP_SLTI,
OP_SLTIU,
OP_ANDI,
OP_ORI,
OP_XORI,
OP_LUI,
OP_COP0,
OP_COP1,
OP_COP2,
__OP_RESERVED_19,
OP_BEQL,
OP_BNEL,
OP_BLEZL,
OP_BGTZL,
OP_DADDI,
OP_DADDIU,
OP_LDL,
OP_LDR,
__OP_RESERVED_28,
__OP_RESERVED_29,
__OP_RESERVED_30,
__OP_RESERVED_31,
OP_LB,
OP_LH,
OP_LWL,
OP_LW,
OP_LBU,
OP_LHU,
OP_LWR,
OP_LWU,
OP_SB,
OP_SH,
OP_SWL,
OP_SW,
OP_SDL,
OP_SDR,
OP_SWR,
OP_CACHE,
OP_LL,
OP_LWC1,
OP_LWC2,
__OP_RESERVED_51,
OP_LLD,
OP_LDC1,
OP_LDC2,
OP_LD,
OP_SC,
OP_SWC1,
OP_SWC2,
__OP_RESERVED_59,
OP_SCD,
OP_SDC1,
OP_SDC2,
OP_SD,
} vr4300_op_t;
typedef enum {
FUNCT_SSL,
__FUNCT_RESERVED_1,
FUNCT_SRL,
FUNCT_SRA,
FUNCT_SLLV,
__FUNCT_RESERVED_5,
FUNCT_SRLV,
FUNCT_SRAV,
FUNCT_JR,
FUNCT_JALR,
__FUNCT_RESERVED_10,
__FUNCT_RESERVED_11,
FUNCT_SYSCALL,
FUNCT_BREAK,
__FUNCT_RESERVED_14,
FUNCT_SYNC,
FUNCT_MFHI,
FUNCT_MTHI,
FUNCT_MFLO,
FUNCT_MTLO,
FUNCT_DSLLV,
__FUNCT_RESERVED_21,
FUNCT_DSRLV,
FUNCT_DSRAV,
FUNCT_MULT,
FUNCT_MULTU,
FUNCT_DIV,
FUNCT_DIVU,
FUNCT_DMULT,
FUNCT_DMULTU,
FUNCT_DDIV,
FUNCT_DDIVU,
FUNCT_ADD,
FUNCT_ADDU,
FUNCT_SUB,
FUNCT_SUBU,
FUNCT_AND,
FUNCT_OR,
FUNCT_XOR,
FUNCT_NOR,
__FUNCT_RESERVED_40,
__FUNCT_RESERVED_41,
FUNCT_SLT,
FUNCT_SLTU,
FUNCT_DADD,
FUNCT_DADDU,
FUNCT_DSUB,
FUNCT_DSUBU,
FUNCT_TGE,
FUNCT_TGEU,
FUNCT_TLT,
FUNCT_TLTU,
FUNCT_TEQ,
__FUNCT_RESERVED_53,
FUNCT_TNE,
__FUNCT_RESERVED_55,
FUNCT_DSLL,
__FUNCT_RESERVED_57,
FUNCT_DSRL,
FUNCT_DSRA,
FUNCT_DSLL32,
__FUNCT_RESERVED_61,
FUNCT_DSRL32,
FUNCT_DSRA32,
} vr4300_funct_t;
typedef enum {
REGIMM_BLTZ,
REGIMM_BGEZ,
REGIMM_BLTZL,
REGIMM_BGEZL,
__REGIMM_RESERVED_4,
__REGIMM_RESERVED_5,
__REGIMM_RESERVED_6,
__REGIMM_RESERVED_7,
REGIMM_TGEI,
REGIMM_TGEIU,
REGIMM_TLTI,
REGIMM_TLTIU,
REGIMM_TEQI,
__REGIMM_RESERVED_13,
REGIMM_TNEI,
__REGIMM_RESERVED_15,
REGIMM_BLTZAL,
REGIMM_BGEZAL,
REGIMM_BLTZALL,
REGIMM_BGEZALL,
__REGIMM_RESERVED_20,
__REGIMM_RESERVED_21,
__REGIMM_RESERVED_22,
__REGIMM_RESERVED_23,
__REGIMM_RESERVED_24,
__REGIMM_RESERVED_25,
__REGIMM_RESERVED_26,
__REGIMM_RESERVED_27,
__REGIMM_RESERVED_28,
__REGIMM_RESERVED_29,
__REGIMM_RESERVED_30,
__REGIMM_RESERVED_31,
} vr4300_regimm_t;
typedef enum {
REG_ZERO,
REG_AT,
REG_V0,
REG_V1,
REG_A0,
REG_A1,
REG_A2,
REG_A3,
REG_T0,
REG_T1,
REG_T2,
REG_T3,
REG_T4,
REG_T5,
REG_T6,
REG_T7,
REG_S0,
REG_S1,
REG_S2,
REG_S3,
REG_S4,
REG_S5,
REG_S6,
REG_S7,
REG_T8,
REG_T9,
REG_K0,
REG_K1,
REG_GP,
REG_SP,
REG_FP,
REG_RA,
} vr4300_reg_t;
typedef enum {
C0_REG_INDEX,
C0_REG_RANDOM,
C0_REG_ENTRY_LO_0,
C0_REG_ENTRY_LO_1,
C0_REG_CONTEXT,
C0_REG_PAGE_MASK,
C0_REG_WIRED,
__C0_REG_RESERVED_7,
C0_REG_BAD_V_ADDR,
C0_REG_COUNT,
C0_REG_ENTRY_HI,
C0_REG_COMPARE,
C0_REG_STATUS,
C0_REG_CAUSE,
C0_REG_EPC,
C0_REG_PR_ID,
C0_REG_CONFIG,
C0_REG_LL_ADDR,
C0_REG_WATCH_LO,
C0_REG_WATCH_HI,
C0_REG_X_CONTEXT,
__C0_REG_RESERVED_21,
__C0_REG_RESERVED_22,
__C0_REG_RESERVED_23,
__C0_REG_RESERVED_24,
__C0_REG_RESERVED_25,
C0_REG_PARITY_ERROR,
C0_REG_CACHE_ERROR,
C0_REG_TAG_LO,
C0_REG_TAG_HI,
C0_REG_ERROR_EPC,
__C0_REG_RESERVED_31,
} vr4300_c0_reg_t;
typedef enum {
COPZ_RS_MF,
COPZ_RS_DMF,
COPZ_RS_CF,
__COPZ_RS_RESERVED_3,
COPZ_RS_MT,
COPZ_RS_DMT,
COPZ_RS_CT,
__COPZ_RS_RESERVED_7,
COPZ_RS_BC,
__COPZ_RS_RESERVED_9,
__COPZ_RS_RESERVED_10,
__COPZ_RS_RESERVED_11,
__COPZ_RS_RESERVED_12,
__COPZ_RS_RESERVED_13,
__COPZ_RS_RESERVED_14,
__COPZ_RS_RESERVED_15,
} vr4300_copz_rs_t;
typedef enum {
__C0_FUNCT_RESERVED_0,
C0_FUNCT_TLBR,
C0_FUNCT_TLBWI,
__C0_FUNCT_RESERVED_3,
__C0_FUNCT_RESERVED_4,
__C0_FUNCT_RESERVED_5,
C0_FUNCT_TLBWR,
__C0_FUNCT_RESERVED_7,
C0_FUNCT_TLBP,
__C0_FUNCT_RESERVED_9,
__C0_FUNCT_RESERVED_10,
__C0_FUNCT_RESERVED_11,
__C0_FUNCT_RESERVED_12,
__C0_FUNCT_RESERVED_13,
__C0_FUNCT_RESERVED_14,
__C0_FUNCT_RESERVED_15,
__C0_FUNCT_RESERVED_16,
__C0_FUNCT_RESERVED_17,
__C0_FUNCT_RESERVED_18,
__C0_FUNCT_RESERVED_19,
__C0_FUNCT_RESERVED_20,
__C0_FUNCT_RESERVED_21,
__C0_FUNCT_RESERVED_22,
__C0_FUNCT_RESERVED_23,
C0_FUNCT_ERET,
__C0_FUNCT_RESERVED_25,
__C0_FUNCT_RESERVED_26,
__C0_FUNCT_RESERVED_27,
__C0_FUNCT_RESERVED_28,
__C0_FUNCT_RESERVED_29,
__C0_FUNCT_RESERVED_30,
__C0_FUNCT_RESERVED_31,
__C0_FUNCT_RESERVED_32,
__C0_FUNCT_RESERVED_33,
__C0_FUNCT_RESERVED_34,
__C0_FUNCT_RESERVED_35,
__C0_FUNCT_RESERVED_36,
__C0_FUNCT_RESERVED_37,
__C0_FUNCT_RESERVED_38,
__C0_FUNCT_RESERVED_39,
__C0_FUNCT_RESERVED_40,
__C0_FUNCT_RESERVED_41,
__C0_FUNCT_RESERVED_42,
__C0_FUNCT_RESERVED_43,
__C0_FUNCT_RESERVED_44,
__C0_FUNCT_RESERVED_45,
__C0_FUNCT_RESERVED_46,
__C0_FUNCT_RESERVED_47,
__C0_FUNCT_RESERVED_48,
__C0_FUNCT_RESERVED_49,
__C0_FUNCT_RESERVED_50,
__C0_FUNCT_RESERVED_51,
__C0_FUNCT_RESERVED_52,
__C0_FUNCT_RESERVED_53,
__C0_FUNCT_RESERVED_54,
__C0_FUNCT_RESERVED_55,
__C0_FUNCT_RESERVED_56,
__C0_FUNCT_RESERVED_57,
__C0_FUNCT_RESERVED_58,
__C0_FUNCT_RESERVED_59,
__C0_FUNCT_RESERVED_60,
__C0_FUNCT_RESERVED_61,
__C0_FUNCT_RESERVED_62,
__C0_FUNCT_RESERVED_63,
} vr4300_c0_funct;
#define __ASM_I_INST(o, s, t, i) \
(((vr4300_instruction_t){.i_type = {.op = (o), .rs = (s), .rt = (t), .imm = (i)&0xFFFF}}).raw)
#define __ASM_J_INST(o, t) (((vr4300_instruction_t){.j_type = {.op = (o), .target = (t)&0x3FFFFFF}}).raw)
#define __ASM_R_INST(o, s, t, d, a, f) \
(((vr4300_instruction_t){.r_type = {.op = (o), .rs = (s), .rt = (t), .rd = (d), .sa = (a), .funct = (f)}}).raw)
#define __ASM_C_INST(o, c, f) (((vr4300_instruction_t){.c_type = {.op = (o), .co = (c), .funct = (f)}}).raw)
#define A_OFFSET(a) ((int16_t)((a)&0xFFFF))
#define A_BASE(a) ((uint16_t)((((a) >> 16) & 0xFFFF) + (A_OFFSET(a) < 0 ? 1 : 0)))
#define I_ADDIU(rt, rs, immediate) __ASM_I_INST(OP_ADDIU, rs, rt, immediate)
#define I_AND(rd, rs, rt) __ASM_R_INST(OP_SPECIAL, rs, rt, rd, 0, FUNCT_AND)
#define I_ANDI(rt, rs, immediate) __ASM_I_INST(OP_ANDI, rs, rt, immediate)
#define I_BEQ(rs, rt, offset) __ASM_I_INST(OP_BEQ, rs, rt, offset)
#define I_BGTZ(rs, offset) __ASM_I_INST(OP_BGTZ, rs, 0, offset)
#define I_BNE(rs, rt, offset) __ASM_I_INST(OP_BNE, rs, rt, offset)
#define I_BNEL(rs, rt, offset) __ASM_I_INST(OP_BNEL, rs, rt, offset)
#define I_CACHE(op, offset, base) __ASM_I_INST(OP_CACHE, base, op, offset)
#define I_ERET() __ASM_C_INST(OP_COP0, 1, C0_FUNCT_ERET)
#define I_J(target) __ASM_J_INST(OP_J, (target >> 2))
#define I_JR(rs) __ASM_R_INST(OP_SPECIAL, rs, REG_ZERO, REG_ZERO, 0, FUNCT_JR)
#define I_LBU(rt, offset, base) __ASM_I_INST(OP_LBU, base, rt, offset)
#define I_LHU(rt, offset, base) __ASM_I_INST(OP_LHU, base, rt, offset)
#define I_LUI(rt, immediate) __ASM_I_INST(OP_LUI, 0, rt, immediate)
#define I_LW(rt, offset, base) __ASM_I_INST(OP_LW, base, rt, offset)
#define I_MFC0(rt, rd) __ASM_R_INST(OP_COP0, COPZ_RS_MF, rt, rd, 0, 0)
#define I_MTC0(rt, rd) __ASM_R_INST(OP_COP0, COPZ_RS_MT, rt, rd, 0, 0)
#define I_NOP() __ASM_R_INST(OP_SPECIAL, REG_ZERO, REG_ZERO, REG_ZERO, 0, FUNCT_SSL)
#define I_OR(rd, rs, rt) __ASM_R_INST(OP_SPECIAL, rs, rt, rd, 0, FUNCT_OR)
#define I_ORI(rt, rs, immediate) __ASM_I_INST(OP_ORI, rs, rt, immediate)
#define I_SB(rt, offset, base) __ASM_I_INST(OP_SB, base, rt, offset)
#define I_SH(rt, offset, base) __ASM_I_INST(OP_SH, base, rt, offset)
#define I_SRL(rd, rt, sa) __ASM_R_INST(OP_SPECIAL, 0, rt, rd, sa, FUNCT_SRL)
#define I_SW(rt, offset, base) __ASM_I_INST(OP_SW, base, rt, offset)
#endif /* VR4300_ASM_H__ */

View File

@ -0,0 +1,386 @@
/**
* @file 64drive.c
* @brief 64drive functions implementation
* @ingroup flashcart
*/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <fatfs/ff.h>
#include <libdragon.h>
#include "utils/fs.h"
#include "utils/utils.h"
#include "../flashcart_utils.h"
#include "64drive_ll.h"
#include "64drive.h"
#define ROM_ADDRESS (0x10000000)
#define SAVE_ADDRESS_DEV_A (0x13FE0000)
#define SAVE_ADDRESS_DEV_A_PKST2 (0x11606560)
#define SAVE_ADDRESS_DEV_B (0x1FFC0000)
#define SUPPORTED_FPGA_REVISION (205)
static d64_device_variant_t device_variant = DEVICE_VARIANT_UNKNOWN;
static d64_save_type_t current_save_type = SAVE_TYPE_NONE;
/**
* @brief Initialize the 64drive.
*
* @return flashcart_err_t Error code.
*/
static flashcart_err_t d64_init (void) {
uint16_t fpga_revision;
uint32_t bootloader_version;
if (d64_ll_enable_extended_mode(false)) {
return FLASHCART_ERR_INT;
}
if (d64_ll_get_version(&device_variant, &fpga_revision, &bootloader_version)) {
return FLASHCART_ERR_INT;
}
if (fpga_revision < SUPPORTED_FPGA_REVISION) {
return FLASHCART_ERR_OUTDATED;
}
if (d64_ll_enable_save_writeback(false)) {
return FLASHCART_ERR_INT;
}
if (d64_ll_set_save_type(SAVE_TYPE_NONE)) {
return FLASHCART_ERR_INT;
}
current_save_type = SAVE_TYPE_NONE;
if (d64_ll_enable_cartrom_writes(true)) {
return FLASHCART_ERR_INT;
}
if (d64_ll_set_persistent_variable_storage(false, 0, 0)) {
return FLASHCART_ERR_INT;
}
return FLASHCART_OK;
}
/**
* @brief Deinitialize the 64drive.
*
* @return flashcart_err_t Error code.
*/
static flashcart_err_t d64_deinit (void) {
if (d64_ll_enable_cartrom_writes(false)) {
return FLASHCART_ERR_INT;
}
return FLASHCART_OK;
}
/**
* @brief Check if the 64drive has a specific feature.
*
* @param feature The feature to check.
* @return true if the feature is supported, false otherwise.
*/
static bool d64_has_feature (flashcart_features_t feature) {
switch (feature) {
case FLASHCART_FEATURE_64DD: return false;
case FLASHCART_FEATURE_RTC: return true;
case FLASHCART_FEATURE_USB: return true;
case FLASHCART_FEATURE_AUTO_CIC: return true;
case FLASHCART_FEATURE_AUTO_REGION: return true;
case FLASHCART_FEATURE_SAVE_WRITEBACK: return true;
case FLASHCART_FEATURE_ROM_REBOOT_FAST: return true;
default: return false;
}
}
/**
* @brief Retrieves the firmware version of the 64drive device.
*
* The firmware version is returned as a flashcart_firmware_version_t structure, with each field
* including the major, minor, and revision numbers.
* The major version is set to 1 for 64drive variant A, and 2 for 64drive variant B.
*
* @return A flashcart_firmware_version_t structure containing the firmware version information.
*/
static flashcart_firmware_version_t d64_get_firmware_version (void) {
flashcart_firmware_version_t version_info;
d64_ll_get_version(&device_variant, &version_info.minor, &version_info.revision);
if (device_variant == DEVICE_VARIANT_A) {
version_info.major = 1;
} else if (device_variant == DEVICE_VARIANT_B) {
version_info.major = 2;
} else {
version_info.major = 0;
}
return version_info;
}
/**
* @brief Load a ROM into the 64drive.
*
* @param rom_path Path to the ROM file.
* @param progress Progress callback function.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t d64_load_rom (char *rom_path, flashcart_progress_callback_t *progress) {
FIL fil;
UINT br;
if (f_open(&fil, strip_fs_prefix(rom_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
fatfs_fix_file_size(&fil);
size_t rom_size = f_size(&fil);
if (rom_size > MiB(64)) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
size_t sdram_size = MiB(64);
size_t chunk_size = KiB(128);
for (unsigned int offset = 0; offset < sdram_size; offset += chunk_size) {
size_t block_size = MIN(sdram_size - offset, chunk_size);
if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (progress) {
progress(f_tell(&fil) / (float) (f_size(&fil)));
}
}
if (f_tell(&fil) != rom_size) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
/**
* @brief Load a file into the 64Drive.
*
* @param file_path Path to the file.
* @param rom_offset ROM offset.
* @param file_offset File offset.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t d64_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) {
FIL fil;
UINT br;
if (f_open(&fil, strip_fs_prefix(file_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
fatfs_fix_file_size(&fil);
size_t file_size = f_size(&fil) - file_offset;
if (file_size > (MiB(64) - rom_offset)) {
f_close(&fil);
return FLASHCART_ERR_ARGS;
}
if (f_lseek(&fil, file_offset) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_read(&fil, (void *) (ROM_ADDRESS + rom_offset), file_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (br != file_size) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
/**
* @brief Load a save file into the 64drive.
*
* @param save_path Path to the save file.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t d64_load_save (char *save_path) {
uint8_t eeprom_contents[2048] __attribute__((aligned(8)));
FIL fil;
UINT br;
if (f_open(&fil, strip_fs_prefix(save_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
size_t save_size = f_size(&fil);
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);
if (is_eeprom_save) {
address = eeprom_contents;
} else if (device_variant == DEVICE_VARIANT_A) {
address = (void *) (SAVE_ADDRESS_DEV_A);
if (current_save_type == SAVE_TYPE_FLASHRAM_PKST2) {
address = (void *) (SAVE_ADDRESS_DEV_A_PKST2);
}
}
if (f_read(&fil, address, save_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (is_eeprom_save) {
if (d64_ll_write_eeprom_contents(eeprom_contents)) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
if (br != save_size) {
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
/**
* @brief Set the save type for the 64drive.
*
* @param save_type The save type.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t d64_set_save_type (flashcart_save_type_t save_type) {
d64_save_type_t type;
switch (save_type) {
case FLASHCART_SAVE_TYPE_NONE:
type = SAVE_TYPE_NONE;
break;
case FLASHCART_SAVE_TYPE_EEPROM_4KBIT:
type = SAVE_TYPE_EEPROM_4KBIT;
break;
case FLASHCART_SAVE_TYPE_EEPROM_16KBIT:
type = SAVE_TYPE_EEPROM_16KBIT;
break;
case FLASHCART_SAVE_TYPE_SRAM_256KBIT:
type = SAVE_TYPE_SRAM_256KBIT;
break;
case FLASHCART_SAVE_TYPE_SRAM_BANKED:
type = SAVE_TYPE_SRAM_BANKED;
break;
case FLASHCART_SAVE_TYPE_SRAM_1MBIT:
// NOTE: 64drive doesn't support 128 kiB SRAM save type, fallback to 32 kiB SRAM save type
type = SAVE_TYPE_SRAM_256KBIT;
break;
case FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT:
type = SAVE_TYPE_FLASHRAM_1MBIT;
break;
case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2:
type = (device_variant == DEVICE_VARIANT_A) ? SAVE_TYPE_FLASHRAM_PKST2 : SAVE_TYPE_FLASHRAM_1MBIT;
break;
default:
return FLASHCART_ERR_ARGS;
}
if (d64_ll_enable_save_writeback(false)) {
return FLASHCART_ERR_INT;
}
if (d64_ll_set_save_type(type)) {
return FLASHCART_ERR_INT;
}
current_save_type = type;
return FLASHCART_OK;
}
/**
* @brief Set the save writeback for the 64drive.
*
* @param save_path Path to the save file.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t d64_set_save_writeback (char *save_path) {
uint32_t sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
if (fatfs_get_file_sectors(save_path, sectors, ADDRESS_TYPE_MEM, SAVE_WRITEBACK_MAX_SECTORS)) {
return FLASHCART_ERR_LOAD;
}
if (d64_ll_write_save_writeback_lba_list(sectors)) {
return FLASHCART_ERR_INT;
}
if (d64_ll_enable_save_writeback(true)) {
return FLASHCART_ERR_INT;
}
return FLASHCART_OK;
}
// static flashcart_err_t d64_set_bootmode (flashcart_reboot_mode_t boot_mode) {
// if (d64_ll_set_persistent_variable_storage(true, 0, 0)) {
// return FLASHCART_ERR_INT;
// }
// return FLASHCART_OK;
// }
/** @brief Flashcart structure for 64drive. */
static flashcart_t flashcart_d64 = {
.init = d64_init,
.deinit = d64_deinit,
.has_feature = d64_has_feature,
.get_firmware_version = d64_get_firmware_version,
.load_rom = d64_load_rom,
.load_file = d64_load_file,
.load_save = d64_load_save,
.load_64dd_ipl = NULL,
.load_64dd_disk = NULL,
.set_save_type = d64_set_save_type,
.set_save_writeback = d64_set_save_writeback,
.set_next_boot_mode = NULL, // d64_set_bootmode,
};
/**
* @brief Get the flashcart structure for 64drive.
*
* @return flashcart_t* Pointer to the flashcart structure.
*/
flashcart_t *d64_get_flashcart (void) {
return &flashcart_d64;
}

View File

@ -0,0 +1,24 @@
/**
* @file 64drive.h
* @brief 64drive flashcart support
* @ingroup flashcart
*/
#ifndef FLASHCART_64DRIVE_H__
#define FLASHCART_64DRIVE_H__
#include "../flashcart.h"
/**
* @addtogroup 64drive
* @{
*/
flashcart_t *d64_get_flashcart (void);
/** @} */ /* 64drive */
#endif

View File

@ -0,0 +1,177 @@
/**
* @file 64drive_ll.c
* @brief Low-level functions for 64drive
* @ingroup flashcart
*/
#include <libdragon.h>
#include "../flashcart_utils.h"
#include "64drive_ll.h"
#define CI_STATUS_BUSY (1 << 12)
#define D64_DEVICE_VARIANT_MASK (0xFFFF)
#define D64_FPGA_REVISION_MASK (0xFFFF)
/**
* @brief Command IDs for 64drive.
*/
typedef enum {
CMD_ID_SET_SAVE_TYPE = 0xD0,
CMD_ID_DISABLE_SAVE_WRITEBACK = 0xD1,
CMD_ID_ENABLE_SAVE_WRITEBACK = 0xD2,
CMD_ID_DISABLE_BYTESWAP_ON_LOAD = 0xE0,
CMD_ID_ENABLE_BYTESWAP_ON_LOAD = 0xE1,
CMD_ID_ENABLE_CARTROM_WRITES = 0xF0,
CMD_ID_DISABLE_CARTROM_WRITES = 0xF1,
CMD_ID_ENABLE_EXTENDED_MODE = 0xF8,
CMD_ID_DISABLE_EXTENDED_MODE = 0xF9,
} d64_ci_cmd_id_t;
static d64_regs_t *d64_regs = D64_REGS;
/**
* @brief Wait for the CI (Command Interface) to become idle.
*
* @return true if a timeout occurred, false otherwise.
*/
static bool d64_ll_ci_wait (void) {
int timeout = 0;
do {
if (timeout++ >= 0x10000) {
return true;
}
} while (io_read((uint32_t) (&d64_regs->STATUS)) & CI_STATUS_BUSY);
return false;
}
/**
* @brief Send a command to the CI (Command Interface).
*
* @param id The command ID.
* @return true if a timeout occurred, false otherwise.
*/
static bool d64_ll_ci_cmd (d64_ci_cmd_id_t id) {
io_write((uint32_t) (&d64_regs->COMMAND), id);
return d64_ll_ci_wait();
}
/**
* @brief Get the version information of the 64drive.
*
* @param device_variant Pointer to store the device variant.
* @param fpga_revision Pointer to store the FPGA revision.
* @param bootloader_version Pointer to store the bootloader version.
* @return true if a timeout occurred, false otherwise.
*/
bool d64_ll_get_version (d64_device_variant_t *device_variant, uint16_t *fpga_revision, uint32_t *bootloader_version) {
if (d64_ll_ci_wait()) {
return true;
}
*device_variant = (d64_device_variant_t) (io_read((uint32_t) (&d64_regs->VARIANT)) & D64_DEVICE_VARIANT_MASK);
*fpga_revision = (io_read((uint32_t) (&d64_regs->REVISION)) & D64_FPGA_REVISION_MASK);
*bootloader_version = io_read((uint32_t) (&d64_regs->PERSISTENT));
return d64_ll_ci_wait();
}
/**
* @brief Set the persistent variable storage on the 64drive.
*
* @param quick_reboot Flag indicating whether to enable quick reboot.
* @param force_tv_type The TV type to force.
* @param cic_seed The CIC seed value.
* @return true if a timeout occurred, false otherwise.
*/
bool d64_ll_set_persistent_variable_storage (bool quick_reboot, d64_tv_type_t force_tv_type, uint8_t cic_seed) {
if (d64_ll_ci_wait()) {
return true;
}
io_write((uint32_t) (&d64_regs->PERSISTENT), (quick_reboot << 16) | ((force_tv_type & 0x03) << 8) | (cic_seed & 0xFF));
return d64_ll_ci_wait();
}
/**
* @brief Set the save type on the 64drive.
*
* @param save_type The save type.
* @return true if a timeout occurred, false otherwise.
*/
bool d64_ll_set_save_type (d64_save_type_t save_type) {
if (d64_ll_ci_wait()) {
return true;
}
io_write((uint32_t) (&d64_regs->BUFFER), save_type);
return d64_ll_ci_cmd(CMD_ID_SET_SAVE_TYPE);
}
/**
* @brief Enable or disable save writeback on the 64drive.
*
* @param enabled Flag indicating whether to enable save writeback.
* @return true if a timeout occurred, false otherwise.
*/
bool d64_ll_enable_save_writeback (bool enabled) {
if (d64_ll_ci_wait()) {
return true;
}
return d64_ll_ci_cmd(enabled ? CMD_ID_ENABLE_SAVE_WRITEBACK : CMD_ID_DISABLE_SAVE_WRITEBACK);
}
/**
* @brief Enable or disable cart ROM writes on the 64drive.
*
* @param enabled Flag indicating whether to enable cart ROM writes.
* @return true if a timeout occurred, false otherwise.
*/
bool d64_ll_enable_cartrom_writes (bool enabled) {
if (d64_ll_ci_wait()) {
return true;
}
return d64_ll_ci_cmd(enabled ? CMD_ID_ENABLE_CARTROM_WRITES : CMD_ID_DISABLE_CARTROM_WRITES);
}
/**
* @brief Enable or disable extended mode on the 64drive.
*
* @param enabled Flag indicating whether to enable extended mode.
* @return true if a timeout occurred, false otherwise.
*/
bool d64_ll_enable_extended_mode (bool enabled) {
d64_ll_ci_wait();
if (enabled) {
io_write((uint32_t) (&D64_REGS->COMMAND), CMD_ID_ENABLE_EXTENDED_MODE);
} else {
io_write((uint32_t) (&D64_REGS_EXT->COMMAND), CMD_ID_DISABLE_EXTENDED_MODE);
}
d64_regs = enabled ? D64_REGS_EXT : D64_REGS;
return d64_ll_ci_wait();
}
/**
* @brief Write EEPROM contents to the 64drive.
*
* @param contents Pointer to the EEPROM contents.
* @return true if a timeout occurred, false otherwise.
*/
bool d64_ll_write_eeprom_contents (void *contents) {
if (d64_ll_ci_wait()) {
return true;
}
pi_dma_write_data(contents, d64_regs->EEPROM, 2048);
return d64_ll_ci_wait();
}
/**
* @brief Write the save writeback LBA list to the 64drive.
*
* @param list Pointer to the LBA list.
* @return true if a timeout occurred, false otherwise.
*/
bool d64_ll_write_save_writeback_lba_list (void *list) {
if (d64_ll_ci_wait()) {
return true;
}
pi_dma_write_data(list, d64_regs->WRITEBACK, 1024);
return d64_ll_ci_wait();
}

View File

@ -0,0 +1,158 @@
/**
* @file 64drive_ll.h
* @brief 64drive flashcart low level access
* @ingroup flashcart
*/
#ifndef FLASHCART_64DRIVE_LL_H__
#define FLASHCART_64DRIVE_LL_H__
#include <stdbool.h>
#include <stdint.h>
/**
* @addtogroup 64drive
* @{
*/
/** @brief Registers Structure. */
typedef struct {
uint8_t BUFFER[512]; /**< General buffer */
uint32_t STATUS; /**< Status register */
uint32_t __unused_1; /**< Unused */
uint32_t COMMAND; /**< Command register */
uint32_t __unused_2; /**< Unused */
uint32_t LBA; /**< Logical Block Address register */
uint32_t __unused_3; /**< Unused */
uint32_t LENGTH; /**< Length register */
uint32_t __unused_4; /**< Unused */
uint32_t RESULT; /**< Result register */
uint32_t __unused_5[49]; /**< Unused */
uint32_t SDRAM_SIZE; /**< SDRAM size register */
uint32_t MAGIC; /**< Magic register */
uint32_t VARIANT; /**< Variant register */
uint32_t PERSISTENT; /**< Persistent register */
uint32_t BUTTON_UPGRADE; /**< Button upgrade register */
uint32_t REVISION; /**< Revision register */
uint32_t __unused_6[64]; /**< Unused */
uint32_t USB_COMMAND_STATUS; /**< USB command status register */
uint32_t USB_PARAM_RESULT[2]; /**< USB parameter result registers */
uint32_t __unused_7[5]; /**< Unused */
uint32_t WIFI_COMMAND_STATUS; /**< WiFi command status register */
uint32_t WIFI_PARAM_RESULT[2]; /**< WiFi parameter result registers */
uint32_t __unused_8[757]; /**< Unused */
uint8_t EEPROM[2048]; /**< EEPROM buffer */
uint32_t WRITEBACK[256]; /**< Writeback buffer */
} d64_regs_t;
/** @brief Registers Base Address. */
#define D64_REGS_BASE (0x18000000UL)
#define D64_REGS_BASE_EXT (0x1F800000UL)
#define D64_REGS ((d64_regs_t *) D64_REGS_BASE)
#define D64_REGS_EXT ((d64_regs_t *) D64_REGS_BASE_EXT)
/** @brief Device Variant Enumeration. */
typedef enum {
DEVICE_VARIANT_UNKNOWN = 0x0000, /**< Unknown device variant */
DEVICE_VARIANT_A = 0x4100, /**< Device variant A */
DEVICE_VARIANT_B = 0x4200, /**< Device variant B */
} d64_device_variant_t;
/** @brief TV Type Enumeration. */
typedef enum {
TV_TYPE_PAL = 0, /**< PAL TV type */
TV_TYPE_NTSC = 1, /**< NTSC TV type */
TV_TYPE_MPAL = 2, /**< MPAL TV type */
TV_TYPE_UNKNOWN = 3, /**< Unknown TV type */
} d64_tv_type_t;
/** @brief Save Type Enumeration. */
typedef enum {
SAVE_TYPE_NONE, /**< No save type */
SAVE_TYPE_EEPROM_4KBIT, /**< EEPROM 4Kbit */
SAVE_TYPE_EEPROM_16KBIT, /**< EEPROM 16Kbit */
SAVE_TYPE_SRAM_256KBIT, /**< SRAM 256Kbit */
SAVE_TYPE_FLASHRAM_1MBIT, /**< FlashRAM 1Mbit */
SAVE_TYPE_SRAM_BANKED, /**< SRAM Banked */
SAVE_TYPE_FLASHRAM_PKST2, /**< FlashRAM PKST2 */
} d64_save_type_t;
/**
* @brief Get the 64drive version.
*
* @param device_variant Pointer to store the device variant.
* @param fpga_revision Pointer to store the FPGA revision.
* @param bootloader_version Pointer to store the bootloader version.
* @return true if successful, false otherwise.
*/
bool d64_ll_get_version(d64_device_variant_t *device_variant, uint16_t *fpga_revision, uint32_t *bootloader_version);
/**
* @brief Set the persistent variable storage.
*
* @param quick_reboot Enable or disable quick reboot.
* @param force_tv_type TV type to force.
* @param cic_seed CIC seed value.
* @return true if successful, false otherwise.
*/
bool d64_ll_set_persistent_variable_storage(bool quick_reboot, d64_tv_type_t force_tv_type, uint8_t cic_seed);
/**
* @brief Set the save type.
*
* @param save_type The save type to set.
* @return true if successful, false otherwise.
*/
bool d64_ll_set_save_type(d64_save_type_t save_type);
/**
* @brief Enable or disable save writeback.
*
* @param enabled True to enable, false to disable.
* @return true if successful, false otherwise.
*/
bool d64_ll_enable_save_writeback(bool enabled);
/**
* @brief Enable or disable cart ROM writes.
*
* @param enabled True to enable, false to disable.
* @return true if successful, false otherwise.
*/
bool d64_ll_enable_cartrom_writes(bool enabled);
/**
* @brief Enable or disable extended mode.
*
* @param enabled True to enable, false to disable.
* @return true if successful, false otherwise.
*/
bool d64_ll_enable_extended_mode(bool enabled);
/**
* @brief Write EEPROM contents.
*
* @param contents Pointer to the EEPROM contents.
* @return true if successful, false otherwise.
*/
bool d64_ll_write_eeprom_contents(void *contents);
/**
* @brief Write save writeback LBA list.
*
* @param list Pointer to the LBA list.
* @return true if successful, false otherwise.
*/
bool d64_ll_write_save_writeback_lba_list(void *list);
/** @} */ /* 64drive */
#endif /* FLASHCART_64DRIVE_LL_H__ */

View File

@ -0,0 +1,49 @@
## 64drive developer notes
### Official documentation
https://web.archive.org/web/20220611032320/http://64drive.retroactive.be/64drive_hardware_spec.pdf
### Save location offset in SDRAM
| Type | HW1 | HW2 |
| ---------------------------- | ------------ | ------------ |
| SRAM | `0x03FE0000` | `0x0FFC0000` |
| FlashRAM | `0x03FE0000` | `0x0FFC0000` |
| SRAM banked | `0x03FE0000` | `0x0FFC0000` |
| FlashRAM (Pokemon Stadium 2) | `0x01606560` | `0x0FFC0000` |
EEPROM save types are stored in separate memory inside FPGA, rest of the save types are stored inside SDRAM memory.
EEPROM save types need manual load as this memory space can't be written with "Read multiple sectors to SDRAM" command.
### "Persistent variable storage" register
| Bits | Meaning |
| --------- | ---------------------------------------------------------- |
| `[31:17]` | _Unused_ |
| `[16]` | Reset behavior: `0` - boot to menu / `1` - quick boot game |
| `[15:10]` | _Unused_ |
| `[9:8]` | Force TV type |
| `[7:0]` | CIC seed |
It's used by the bootloader to quickly load game without running menu again.
Should contain bootloader version but it's zeroed if ROM wasn't ran through bootloader (e.g. ROM was loaded via USB).
### "Enable/disable save writeback" command
Does not work when USB cable is connected - writeback is forced to be in disabled state.
Curiously, official 64drive menu never calls this command, save writeback might be enabled by default.
### "Enable/disable byteswap on load" command
Annoyingly, this command affects both loading single sector into the buffer and loading multiple sectors to the SDRAM.
### "Enable/disable extended address mode" command
As of latest available firmware version 2.05 this command is not implemented.
Documentation specifies it's supported on firmwares 2.06+ but this version (or anything newer) was never published.

View File

@ -0,0 +1,170 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <fatfs/ff.h>
#include <libdragon.h>
#include "utils/fs.h"
#include "utils/utils.h"
#include "../flashcart_utils.h"
#include "ed64_vseries_ll.h"
#include "ed64_vseries.h"
typedef enum {
ED64_V1_0 = 110,
ED64_V2_0 = 320,
ED64_V2_5 = 325,
ED64_V3_0 = 330,
} ed64_vseries_device_variant_t;
/* ED64 save location base address */
#define SRAM_ADDRESS (0xA8000000)
/* ED64 ROM location base address */
#define ROM_ADDRESS (0xB0000000)
static flashcart_firmware_version_t ed64_vseries_get_firmware_version (void) {
flashcart_firmware_version_t version_info;
// FIXME: get version from ll
version_info.major = 1;
version_info.minor = 1;
version_info.revision = 0;
//ed64_ll_get_version(&version_info.major, &version_info.minor, &version_info.revision);
return version_info;
}
static flashcart_err_t ed64_vseries_init (void) {
return FLASHCART_OK;
}
static flashcart_err_t ed64_vseries_deinit (void) {
return FLASHCART_OK;
}
static ed64_vseries_device_variant_t get_cart_model() {
ed64_vseries_device_variant_t variant = ED64_V1_0; // FIXME: check cart model from ll for better feature handling.
return variant;
}
static bool ed64_vseries_has_feature (flashcart_features_t feature) {
bool is_model_v3 = (get_cart_model() == ED64_V3_0);
switch (feature) {
case FLASHCART_FEATURE_RTC: return is_model_v3 ? true : false;
case FLASHCART_FEATURE_USB: return is_model_v3 ? true : false;
case FLASHCART_FEATURE_AUTO_CIC: return is_model_v3 ? true : false;
default: return false;
}
}
static flashcart_err_t ed64_vseries_load_rom (char *rom_path, flashcart_progress_callback_t *progress) {
FIL fil;
UINT br;
if (f_open(&fil, strip_fs_prefix(rom_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
fatfs_fix_file_size(&fil);
size_t rom_size = f_size(&fil);
if (rom_size > MiB(64)) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
size_t sdram_size = MiB(64);
size_t chunk_size = KiB(128);
for (int offset = 0; offset < sdram_size; offset += chunk_size) {
size_t block_size = MIN(sdram_size - offset, chunk_size);
if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (progress) {
progress(f_tell(&fil) / (float) (f_size(&fil)));
}
}
if (f_tell(&fil) != rom_size) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
static flashcart_err_t ed64_vseries_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) {
FIL fil;
UINT br;
if (f_open(&fil, strip_fs_prefix(file_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
fatfs_fix_file_size(&fil);
size_t file_size = f_size(&fil) - file_offset;
if (file_size > (MiB(64) - rom_offset)) {
f_close(&fil);
return FLASHCART_ERR_ARGS;
}
if (f_lseek(&fil, file_offset) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_read(&fil, (void *) (ROM_ADDRESS + rom_offset), file_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (br != file_size) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
static flashcart_err_t ed64_vseries_load_save (char *save_path) {
// FIXME: the savetype will be none.
return FLASHCART_OK;
}
static flashcart_err_t ed64_vseries_set_save_type (flashcart_save_type_t save_type) {
// FIXME: the savetype will be none.
return FLASHCART_OK;
}
static flashcart_t flashcart_ed64_vseries = {
.init = ed64_vseries_init,
.deinit = ed64_vseries_deinit,
.has_feature = ed64_vseries_has_feature,
.get_firmware_version = ed64_vseries_get_firmware_version,
.load_rom = ed64_vseries_load_rom,
.load_file = ed64_vseries_load_file,
.load_save = ed64_vseries_load_save,
.load_64dd_ipl = NULL,
.load_64dd_disk = NULL,
.set_save_type = ed64_vseries_set_save_type,
.set_save_writeback = NULL,
.set_next_boot_mode = NULL,
};
flashcart_t *ed64_vseries_get_flashcart (void) {
return &flashcart_ed64_vseries;
}

View File

@ -0,0 +1,24 @@
/**
* @file ed64_vseries.h
* @brief ED64 Vseries flashcart support
* @ingroup flashcart
*/
#ifndef FLASHCART_ED64_VSERIES_H__
#define FLASHCART_ED64_VSERIES_H__
#include "../flashcart.h"
/**
* @addtogroup ED64_Vseries
* @{
*/
flashcart_t *ed64_vseries_get_flashcart (void);
/** @} */ /* ED64_Vseries */
#endif

View File

@ -0,0 +1,14 @@
/**
* @file ed64_vseries_ll.h
* @brief ed64v flashcart low level access
* @ingroup flashcart
*/
#ifndef FLASHCART_ED64_VSERIES_LL_H__
#define FLASHCART_ED64_VSERIES_LL_H__
/** @} */ /* ed64_vseries_ll */
#endif

View File

@ -0,0 +1,174 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <fatfs/ff.h>
#include <libdragon.h>
#include "utils/fs.h"
#include "utils/utils.h"
#include "../flashcart_utils.h"
#include "ed64_xseries_ll.h"
#include "ed64_xseries.h"
typedef enum {
// potentially handle if the firmware supports it...
ED64_X5_0 = 550,
ED64_X7_0 = 570,
ED64_UKNOWN = 0,
} ed64_xseries_device_variant_t;
/* ED64 save location base address */
#define SRAM_ADDRESS (0xA8000000)
/* ED64 ROM location base address */
#define ROM_ADDRESS (0xB0000000)
static flashcart_firmware_version_t ed64_xseries_get_firmware_version (void) {
flashcart_firmware_version_t version_info;
// FIXME: get version from ll
version_info.major = 1;
version_info.minor = 1;
version_info.revision = 0;
//ed64_ll_get_version(&version_info.major, &version_info.minor, &version_info.revision);
return version_info;
}
static flashcart_err_t ed64_xseries_init (void) {
return FLASHCART_OK;
}
static flashcart_err_t ed64_xseries_deinit (void) {
return FLASHCART_OK;
}
static ed64_xseries_device_variant_t get_cart_model() {
ed64_xseries_device_variant_t variant = ED64_X7_0; // FIXME: check cart model from ll for better feature handling.
return variant;
}
static bool ed64_xseries_has_feature (flashcart_features_t feature) {
bool is_model_x7 = (get_cart_model() == ED64_X7_0);
switch (feature) {
case FLASHCART_FEATURE_RTC: return is_model_x7 ? true : false;
case FLASHCART_FEATURE_USB: return is_model_x7 ? true : false;
case FLASHCART_FEATURE_64DD: return false;
case FLASHCART_FEATURE_AUTO_CIC: return true;
case FLASHCART_FEATURE_AUTO_REGION: return true;
default: return false;
}
}
static flashcart_err_t ed64_xseries_load_rom (char *rom_path, flashcart_progress_callback_t *progress) {
FIL fil;
UINT br;
if (f_open(&fil, strip_fs_prefix(rom_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
fatfs_fix_file_size(&fil);
size_t rom_size = f_size(&fil);
if (rom_size > MiB(64)) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
size_t sdram_size = MiB(64);
size_t chunk_size = KiB(128);
for (int offset = 0; offset < sdram_size; offset += chunk_size) {
size_t block_size = MIN(sdram_size - offset, chunk_size);
if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (progress) {
progress(f_tell(&fil) / (float) (f_size(&fil)));
}
}
if (f_tell(&fil) != rom_size) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
static flashcart_err_t ed64_xseries_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) {
FIL fil;
UINT br;
if (f_open(&fil, strip_fs_prefix(file_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
fatfs_fix_file_size(&fil);
size_t file_size = f_size(&fil) - file_offset;
if (file_size > (MiB(64) - rom_offset)) {
f_close(&fil);
return FLASHCART_ERR_ARGS;
}
if (f_lseek(&fil, file_offset) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_read(&fil, (void *) (ROM_ADDRESS + rom_offset), file_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (br != file_size) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
static flashcart_err_t ed64_xseries_load_save (char *save_path) {
// FIXME: the savetype will be none.
return FLASHCART_OK;
}
static flashcart_err_t ed64_xseries_set_save_type (flashcart_save_type_t save_type) {
// FIXME: the savetype will be none.
return FLASHCART_OK;
}
static flashcart_t flashcart_ed64_xseries = {
.init = ed64_xseries_init,
.deinit = ed64_xseries_deinit,
.has_feature = ed64_xseries_has_feature,
.get_firmware_version = ed64_xseries_get_firmware_version,
.load_rom = ed64_xseries_load_rom,
.load_file = ed64_xseries_load_file,
.load_save = ed64_xseries_load_save,
.load_64dd_ipl = NULL,
.load_64dd_disk = NULL,
.set_save_type = ed64_xseries_set_save_type,
.set_save_writeback = NULL,
.set_next_boot_mode = NULL,
};
flashcart_t *ed64_xseries_get_flashcart (void) {
return &flashcart_ed64_xseries;
}

View File

@ -0,0 +1,24 @@
/**
* @file ed64xseries.h
* @brief ED64 Xseries flashcart support
* @ingroup flashcart
*/
#ifndef FLASHCART_ED64XSERIES_H__
#define FLASHCART_ED64XSERIES_H__
#include "../flashcart.h"
/**
* @addtogroup ED64_Xseries
* @{
*/
flashcart_t *ed64_xseries_get_flashcart (void);
/** @} */ /* ED64_Xseries */
#endif

View File

@ -0,0 +1,14 @@
/**
* @file ed64_xseries_ll.h
* @brief ed64x flashcart low level access
* @ingroup flashcart
*/
#ifndef FLASHCART_ED64_XSERIES_LL_H__
#define FLASHCART_ED64_XSERIES_LL_H__
/** @} */ /* ed64_xseries_ll */
#endif

View File

@ -1,19 +1,24 @@
#include <stddef.h>
/**
* @file flashcart.c
* @brief Flashcart functions implementation
* @ingroup flashcart
*/
#include <stddef.h>
#include <libcart/cart.h>
#include <libdragon.h>
#include <usb.h>
#include "utils/fs.h"
#include "utils/utils.h"
#include "flashcart.h"
#include "flashcart_utils.h"
#include "ed64/ed64_vseries.h"
#include "ed64/ed64_xseries.h"
#include "64drive/64drive.h"
#include "sc64/sc64.h"
#define SAVE_WRITEBACK_MAX_SECTORS (256)
/** @brief Save sizes for different flashcart save types. */
static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = {
0,
512,
@ -22,23 +27,87 @@ static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = {
KiB(96),
KiB(128),
KiB(128),
KiB(128),
};
static uint32_t save_writeback_sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
static flashcart_error_t dummy_init (void) {
/**
* @brief Dummy initialization function for flashcart.
*
* @return flashcart_err_t Error code.
*/
static flashcart_err_t dummy_init (void) {
return FLASHCART_OK;
}
/**
* @brief Dummy function to check if a feature is supported by the flashcart.
*
* @param feature The feature to check.
* @return true if the feature is supported, false otherwise.
*/
static bool dummy_has_feature (flashcart_features_t feature) {
switch (feature) {
default:
return false;
}
}
/**
* @brief Dummy function to load a ROM into the flashcart.
*
* @param rom_path Path to the ROM file.
* @param progress Progress callback function.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t dummy_load_rom (char *rom_path, flashcart_progress_callback_t *progress) {
return FLASHCART_OK;
}
/**
* @brief Dummy function to load a file into the flashcart.
*
* @param file_path Path to the file.
* @param rom_offset ROM offset.
* @param file_offset File offset.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t dummy_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) {
return FLASHCART_OK;
}
/**
* @brief Dummy function to load a save file into the flashcart.
*
* @param save_path Path to the save file.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t dummy_load_save (char *save_path) {
return FLASHCART_OK;
}
/**
* @brief Dummy function to set the save type for the flashcart.
*
* @param save_type The save type.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t dummy_set_save_type (flashcart_save_type_t save_type) {
return FLASHCART_OK;
}
/** @brief Flashcart structure with dummy functions. */
static flashcart_t *flashcart = &((flashcart_t) {
.init = dummy_init,
.deinit = NULL,
.load_rom = NULL,
.load_file = NULL,
.load_save = NULL,
.set_save_type = NULL,
.has_feature = dummy_has_feature,
.load_rom = dummy_load_rom,
.load_file = dummy_load_file,
.load_save = dummy_load_save,
.load_64dd_ipl = NULL,
.load_64dd_disk = NULL,
.set_save_type = dummy_set_save_type,
.set_save_writeback = NULL,
.set_next_boot_mode = NULL,
});
#ifdef NDEBUG
@ -48,34 +117,68 @@ static flashcart_t *flashcart = &((flashcart_t) {
bool debug_init_sdfs (const char *prefix, int npart);
#endif
/**
* @brief Convert a flashcart error code to a human-readable message.
*
* @param err The flashcart error code.
* @return char* The error message.
*/
char *flashcart_convert_error_message (flashcart_err_t err) {
switch (err) {
case FLASHCART_OK: return "No error";
case FLASHCART_ERR_OUTDATED: return "Outdated flashcart firmware";
case FLASHCART_ERR_SD_CARD: return "Error during SD card initialization";
case FLASHCART_ERR_BBFS: return "Error during iQue NAND initialization";
case FLASHCART_ERR_ARGS: return "Invalid argument passed to flashcart function";
case FLASHCART_ERR_LOAD: return "Error during loading data into flashcart";
case FLASHCART_ERR_INT: return "Internal flashcart error";
case FLASHCART_ERR_FUNCTION_NOT_SUPPORTED: return "Flashcart doesn't support this function";
default: return "Unknown flashcart error";
}
}
flashcart_error_t flashcart_init (void) {
flashcart_error_t error;
/**
* @brief Initialize the flashcart.
*
* @param storage_prefix Pointer to the storage prefix.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_init (const char **storage_prefix) {
flashcart_err_t err;
// NOTE: Explicitly support only these flashcarts in this specific initialization order.
struct {
int type;
int (* libcart_init) (void);
flashcart_t *(* get) (void);
} flashcarts[CART_MAX] = {
{ CART_CI, ci_init, NULL }, // 64drive
{ CART_SC, sc_init, sc64_get_flashcart }, // SC64
{ CART_EDX, edx_init, NULL }, // Series X EverDrive-64
{ CART_ED, ed_init, NULL }, // Original EverDrive-64
};
for (int i = 0; i < CART_MAX; i++) {
if (flashcarts[i].libcart_init() >= 0) {
cart_type = flashcarts[i].type;
if (flashcarts[i].get) {
flashcart = flashcarts[i].get();
}
break;
if (sys_bbplayer()) {
// TODO: Add iQue callbacks
*storage_prefix = "bbfs:/";
if (bbfs_init()) {
return FLASHCART_ERR_BBFS;
}
return FLASHCART_OK;
}
if (cart_type == CART_NULL) {
return FLASHCART_ERROR_NOT_DETECTED;
*storage_prefix = "sd:/";
bool sd_card_initialized = debug_init_sdfs(*storage_prefix, -1);
switch (cart_type) {
case CART_CI: // 64drive
flashcart = d64_get_flashcart();
break;
case CART_EDX: // Official EverDrive 64 Series X
flashcart = ed64_xseries_get_flashcart();
break;
case CART_ED: // Series V EverDrive-64 or clone
flashcart = ed64_vseries_get_flashcart();
break;
case CART_SC: // SummerCart64
flashcart = sc64_get_flashcart();
break;
default: // Probably emulator
*storage_prefix = "rom:/";
debug_init_isviewer();
break;
}
#ifndef NDEBUG
@ -83,18 +186,23 @@ flashcart_error_t flashcart_init (void) {
debug_init_usblog();
#endif
if ((error = flashcart->init()) != FLASHCART_OK) {
return error;
if ((err = flashcart->init()) != FLASHCART_OK) {
return err;
}
if (!debug_init_sdfs("sd:/", -1)) {
return FLASHCART_ERROR_SD_CARD;
if ((cart_type != CART_NULL) && (!sd_card_initialized)) {
return FLASHCART_ERR_SD_CARD;
}
return FLASHCART_OK;
}
flashcart_error_t flashcart_deinit (void) {
/**
* @brief Deinitialize the flashcart.
*
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_deinit (void) {
if (flashcart->deinit) {
return flashcart->deinit();
}
@ -102,54 +210,79 @@ flashcart_error_t flashcart_deinit (void) {
return FLASHCART_OK;
}
flashcart_error_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress) {
flashcart_error_t error;
/**
* @brief Check if the flashcart has a specific feature.
*
* @param feature The feature to check.
* @return true if the feature is supported, false otherwise.
*/
bool flashcart_has_feature (flashcart_features_t feature) {
return flashcart->has_feature(feature);
}
if ((rom_path == NULL) || (!file_exists(rom_path)) || (file_get_size(rom_path) < KiB(4))) {
return FLASHCART_ERROR_ARGS;
/**
* @brief Get the firmware version of the flashcart.
*
* @return flashcart_firmware_version_t The firmware version.
*/
flashcart_firmware_version_t flashcart_get_firmware_version (void) {
return flashcart->get_firmware_version();
}
/**
* @brief Load a ROM into the flashcart.
*
* @param rom_path Path to the ROM file.
* @param byte_swap Flag indicating whether to byte swap the ROM.
* @param progress Progress callback function.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress) {
flashcart_err_t err;
if (rom_path == NULL) {
return FLASHCART_ERR_ARGS;
}
cart_card_byteswap = byte_swap;
error = flashcart->load_rom(rom_path, progress);
err = flashcart->load_rom(rom_path, progress);
cart_card_byteswap = false;
return error;
return err;
}
flashcart_error_t flashcart_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) {
if ((file_path == NULL) || (!file_exists(file_path))) {
return FLASHCART_ERROR_ARGS;
}
if ((file_offset % FS_SECTOR_SIZE) != 0) {
return FLASHCART_ERROR_ARGS;
/**
* @brief Load a file into the flashcart.
*
* @param file_path Path to the file.
* @param rom_offset ROM offset.
* @param file_offset File offset.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) {
if ((file_path == NULL) || ((file_offset % FS_SECTOR_SIZE) != 0)) {
return FLASHCART_ERR_ARGS;
}
return flashcart->load_file(file_path, rom_offset, file_offset);
}
static void save_writeback_sectors_callback (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size) {
for (uint32_t i = 0; i < cluster_size; i++) {
uint32_t offset = file_sector + i;
uint32_t sector = cluster_sector + i;
if ((offset > SAVE_WRITEBACK_MAX_SECTORS) || (offset > sector_count)) {
return;
}
save_writeback_sectors[offset] = sector;
}
}
flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type) {
flashcart_error_t error;
/**
* @brief Load a save file into the flashcart.
*
* @param save_path Path to the save file.
* @param save_type The save type.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type) {
flashcart_err_t err;
if (save_type >= __FLASHCART_SAVE_TYPE_END) {
return FLASHCART_ERROR_ARGS;
return FLASHCART_ERR_ARGS;
}
if ((error = flashcart->set_save_type(save_type)) != FLASHCART_OK) {
return error;
if ((err = flashcart->set_save_type(save_type)) != FLASHCART_OK) {
return err;
}
if ((save_path == NULL) || (save_type == FLASHCART_SAVE_TYPE_NONE)) {
@ -158,32 +291,70 @@ flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t sa
if (!file_exists(save_path)) {
if (file_allocate(save_path, SAVE_SIZE[save_type])) {
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if (file_fill(save_path, 0xFF)) {
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
}
if (file_get_size(save_path) != SAVE_SIZE[save_type]) {
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if ((error = flashcart->load_save(save_path)) != FLASHCART_OK) {
return error;
if ((err = flashcart->load_save(save_path)) != FLASHCART_OK) {
return err;
}
if (flashcart->set_save_writeback) {
for (int i = 0; i < SAVE_WRITEBACK_MAX_SECTORS; i++) {
save_writeback_sectors[i] = 0;
}
if (file_get_sectors(save_path, save_writeback_sectors_callback)) {
return FLASHCART_ERROR_LOAD;
}
if ((error = flashcart->set_save_writeback(save_writeback_sectors)) != FLASHCART_OK) {
return error;
}
if (!flashcart->set_save_writeback) {
return FLASHCART_OK;
}
return FLASHCART_OK;
return flashcart->set_save_writeback(save_path);
}
/**
* @brief Load the 64DD IPL into the flashcart.
*
* @param ipl_path Path to the IPL file.
* @param progress Progress callback function.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_64dd_ipl (char *ipl_path, flashcart_progress_callback_t *progress) {
if (!flashcart->load_64dd_ipl) {
return FLASHCART_ERR_FUNCTION_NOT_SUPPORTED;
}
if (ipl_path == NULL) {
return FLASHCART_ERR_ARGS;
}
return flashcart->load_64dd_ipl(ipl_path, progress);
}
/**
* @brief Load a 64DD disk into the flashcart.
*
* @param disk_path Path to the disk file.
* @param disk_parameters Pointer to the disk parameters.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_64dd_disk (char *disk_path, flashcart_disk_parameters_t *disk_parameters) {
if (!flashcart->load_64dd_disk) {
return FLASHCART_ERR_FUNCTION_NOT_SUPPORTED;
}
if ((disk_path == NULL) || (disk_parameters == NULL)) {
return FLASHCART_ERR_ARGS;
}
return flashcart->load_64dd_disk(disk_path, disk_parameters);
}
flashcart_err_t flashcart_set_next_boot_mode (flashcart_reboot_mode_t boot_mode) {
if (!flashcart->set_next_boot_mode) {
return FLASHCART_ERR_FUNCTION_NOT_SUPPORTED;
}
return flashcart->set_next_boot_mode(boot_mode);
}

View File

@ -7,53 +7,185 @@
#ifndef FLASHCART_H__
#define FLASHCART_H__
#include <stdbool.h>
#include <stdint.h>
/** @brief Flashcart error enumeration */
typedef enum {
FLASHCART_OK,
FLASHCART_ERROR_NOT_DETECTED,
FLASHCART_ERROR_OUTDATED,
FLASHCART_ERROR_SD_CARD,
FLASHCART_ERROR_ARGS,
FLASHCART_ERROR_LOAD,
FLASHCART_ERROR_INT,
} flashcart_error_t;
FLASHCART_OK, /**< No error */
FLASHCART_ERR_OUTDATED, /**< Outdated firmware error */
FLASHCART_ERR_SD_CARD, /**< SD card error */
FLASHCART_ERR_BBFS, /**< BBFS error */
FLASHCART_ERR_ARGS, /**< Argument error */
FLASHCART_ERR_LOAD, /**< Load error */
FLASHCART_ERR_INT, /**< Internal error */
FLASHCART_ERR_FUNCTION_NOT_SUPPORTED, /**< Function not supported error */
} flashcart_err_t;
/** @brief List of optional supported flashcart features */
typedef enum {
FLASHCART_FEATURE_64DD, /**< 64DD support */
FLASHCART_FEATURE_RTC, /**< Real-time clock support */
FLASHCART_FEATURE_USB, /**< USB support */
FLASHCART_FEATURE_AUTO_CIC, /**< Automatic CIC detection */
FLASHCART_FEATURE_AUTO_REGION, /**< Automatic region detection */
FLASHCART_FEATURE_DIAGNOSTIC_DATA, /**< Diagnostic data support */
FLASHCART_FEATURE_BIOS_UPDATE_FROM_MENU, /**< BIOS update from menu support */
FLASHCART_FEATURE_SAVE_WRITEBACK, /**< Save writeback support */
FLASHCART_FEATURE_ROM_REBOOT_FAST /**< Fast ROM reboot support */
} flashcart_features_t;
/** @brief Flashcart save type enumeration */
typedef enum {
FLASHCART_SAVE_TYPE_NONE,
FLASHCART_SAVE_TYPE_EEPROM_4K,
FLASHCART_SAVE_TYPE_EEPROM_16K,
FLASHCART_SAVE_TYPE_SRAM,
FLASHCART_SAVE_TYPE_SRAM_BANKED,
FLASHCART_SAVE_TYPE_SRAM_128K,
FLASHCART_SAVE_TYPE_FLASHRAM,
__FLASHCART_SAVE_TYPE_END
FLASHCART_SAVE_TYPE_NONE, /**< No save type */
FLASHCART_SAVE_TYPE_EEPROM_4KBIT, /**< EEPROM 4Kbit */
FLASHCART_SAVE_TYPE_EEPROM_16KBIT, /**< EEPROM 16Kbit */
FLASHCART_SAVE_TYPE_SRAM_256KBIT, /**< SRAM 256Kbit */
FLASHCART_SAVE_TYPE_SRAM_BANKED, /**< SRAM Banked */
FLASHCART_SAVE_TYPE_SRAM_1MBIT, /**< SRAM 1Mbit */
FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT, /**< FlashRAM 1Mbit */
FLASHCART_SAVE_TYPE_FLASHRAM_PKST2, /**< FlashRAM PKST2 */
__FLASHCART_SAVE_TYPE_END /**< End of save types */
} flashcart_save_type_t;
/** @brief Flashcart save type enumeration */
typedef enum {
/** @brief The flashcart will reboot into the menu on soft reboot (using the RESET button) */
FLASHCART_REBOOT_MODE_MENU,
/** @brief The flashcart will reboot into the previous ROM on soft reboot (using the RESET button) */
FLASHCART_REBOOT_MODE_ROM,
} flashcart_reboot_mode_t;
/** @brief Flashcart Disk Parameter Structure. */
typedef struct {
bool development_drive; /**< Development drive flag */
uint8_t disk_type; /**< Disk type */
bool bad_system_area_lbas[24]; /**< Bad system area LBAs */
uint8_t defect_tracks[16][12]; /**< Defect tracks */
} flashcart_disk_parameters_t;
/** @brief Flashcart Firmware version Structure. */
typedef struct {
uint16_t major; /**< Major version */
uint16_t minor; /**< Minor version */
uint32_t revision; /**< Revision */
} flashcart_firmware_version_t;
/** @brief Flashcart progress callback type */
typedef void flashcart_progress_callback_t (float progress);
/** @brief Flashcart Structure */
typedef struct {
flashcart_error_t (*init) (void);
flashcart_error_t (*deinit) (void);
flashcart_error_t (*load_rom) (char *rom_path, flashcart_progress_callback_t *progress);
flashcart_error_t (*load_file) (char *file_path, uint32_t rom_offset, uint32_t file_offset);
flashcart_error_t (*load_save) (char *save_path);
flashcart_error_t (*set_save_type) (flashcart_save_type_t save_type);
flashcart_error_t (*set_save_writeback) (uint32_t *sectors);
/** @brief The flashcart initialization function */
flashcart_err_t (*init) (void);
/** @brief The flashcart de-initialization function */
flashcart_err_t (*deinit) (void);
/** @brief The flashcart feature function */
bool (*has_feature) (flashcart_features_t feature);
/** @brief The flashcart firmware version function */
flashcart_firmware_version_t (*get_firmware_version) (void);
/** @brief The flashcart ROM load function */
flashcart_err_t (*load_rom) (char *rom_path, flashcart_progress_callback_t *progress);
/** @brief The flashcart file load function */
flashcart_err_t (*load_file) (char *file_path, uint32_t rom_offset, uint32_t file_offset);
/** @brief The flashcart save file load function */
flashcart_err_t (*load_save) (char *save_path);
/** @brief The flashcart disk bios load function */
flashcart_err_t (*load_64dd_ipl) (char *ipl_path, flashcart_progress_callback_t *progress);
/** @brief The flashcart disk load function */
flashcart_err_t (*load_64dd_disk) (char *disk_path, flashcart_disk_parameters_t *disk_parameters);
/** @brief The flashcart set save type function */
flashcart_err_t (*set_save_type) (flashcart_save_type_t save_type);
/** @brief The flashcart set save writeback function */
flashcart_err_t (*set_save_writeback) (char *save_path);
/** @brief The flashcart set boot mode function */
flashcart_err_t (*set_next_boot_mode) (flashcart_reboot_mode_t boot_mode);
} flashcart_t;
/**
* @brief Convert a flashcart error code to a human-readable error message.
*
* @param err The flashcart error code.
* @return char* The human-readable error message.
*/
char *flashcart_convert_error_message (flashcart_err_t err);
flashcart_error_t flashcart_init (void);
flashcart_error_t flashcart_deinit (void);
flashcart_error_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress);
flashcart_error_t flashcart_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset);
flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type);
/**
* @brief Initialize the flashcart.
*
* @param storage_prefix Pointer to the storage prefix.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_init (const char **storage_prefix);
/**
* @brief Deinitialize the flashcart.
*
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_deinit (void);
#endif
/**
* @brief Check if the flashcart has a specific feature.
*
* @param feature The flashcart feature to check.
* @return bool True if the feature is supported, false otherwise.
*/
bool flashcart_has_feature (flashcart_features_t feature);
/**
* @brief Get the flashcart firmware version.
*
* @return flashcart_firmware_version_t The firmware version.
*/
flashcart_firmware_version_t flashcart_get_firmware_version (void);
/**
* @brief Load a ROM onto the flashcart.
*
* @param rom_path The path to the ROM file.
* @param byte_swap Whether to byte swap the ROM.
* @param progress Callback function for progress updates.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress);
/**
* @brief Load a file onto the flashcart.
*
* @param file_path The path to the file.
* @param rom_offset The ROM offset.
* @param file_offset The file offset.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset);
/**
* @brief Load a save file onto the flashcart.
*
* @param save_path The path to the save file.
* @param save_type The type of save.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type);
/**
* @brief Load the 64DD IPL (BIOS) onto the flashcart.
*
* @param ipl_path The path to the IPL file.
* @param progress Callback function for progress updates.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_64dd_ipl (char *ipl_path, flashcart_progress_callback_t *progress);
/**
* @brief Load a 64DD disk onto the flashcart.
*
* @param disk_path The path to the disk file.
* @param disk_parameters Pointer to the disk parameters structure.
* @return flashcart_err_t Error code.
*/
flashcart_err_t flashcart_load_64dd_disk (char *disk_path, flashcart_disk_parameters_t *disk_parameters);
flashcart_err_t flashcart_set_next_boot_mode (flashcart_reboot_mode_t boot_mode);
#endif /* FLASHCART_H__ */

View File

@ -1,23 +1,35 @@
/**
* @file flashcart_utils.c
* @brief Flashcart utility functions implementation
* @ingroup flashcart
*/
#include <libdragon.h>
#include "flashcart_utils.h"
#include "utils/fs.h"
#include "utils/utils.h"
void fix_file_size (FIL *fil) {
// HACK: Align file size to the SD sector size to prevent FatFs from doing partial sector load.
// We are relying on direct transfer from SD to SDRAM without CPU intervention.
// Sending some extra bytes isn't an issue here.
fil->obj.objsize = ALIGN(f_size(fil), FS_SECTOR_SIZE);
}
/**
* @brief Perform a DMA read operation from the PI (Peripheral Interface).
*
* @param src Source address.
* @param dst Destination address.
* @param length Length of data to read.
*/
void pi_dma_read_data (void *src, void *dst, size_t length) {
data_cache_hit_writeback_invalidate(dst, length);
dma_read_async(dst, (uint32_t) (src), length);
dma_wait();
}
/**
* @brief Perform a DMA write operation to the PI (Peripheral Interface).
*
* @param src Source address.
* @param dst Destination address.
* @param length Length of data to write.
*/
void pi_dma_write_data (void *src, void *dst, size_t length) {
assert((((uint32_t) (src)) & 0x07) == 0);
assert((((uint32_t) (dst)) & 0x01) == 0);
@ -27,3 +39,82 @@ void pi_dma_write_data (void *src, void *dst, size_t length) {
dma_write_raw_async(src, (uint32_t) (dst), length);
dma_wait();
}
/**
* @brief Align the file size to the SD sector size to prevent partial sector load.
*
* @param fil Pointer to the file object.
*/
void fatfs_fix_file_size (FIL *fil) {
// HACK: Align file size to the SD sector size to prevent FatFs from doing partial sector load.
// We are relying on direct transfer from SD to SDRAM without CPU intervention.
// Sending some extra bytes isn't an issue here.
fil->obj.objsize = ALIGN(f_size(fil), FS_SECTOR_SIZE);
}
/**
* @brief Get the file sectors in the FAT filesystem.
*
* @param path Path to the file.
* @param address Pointer to store the address of the file sectors.
* @param type The type of address (memory or PI).
* @param max_sectors Maximum number of sectors.
* @return true if an error occurred, false otherwise.
*/
bool fatfs_get_file_sectors (char *path, uint32_t *address, address_type_t type, uint32_t max_sectors) {
FATFS *fs;
FIL fil;
bool error = false;
if (f_open(&fil, strip_fs_prefix(path), FA_READ) != FR_OK) {
return true;
}
fatfs_fix_file_size(&fil);
fs = fil.obj.fs;
uint32_t sector_count = MIN(f_size(&fil) / FS_SECTOR_SIZE, max_sectors);
for (uint32_t file_sector = 0; file_sector < sector_count; file_sector += fs->csize) {
if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) {
error = true;
break;
}
uint32_t cluster = fil.clust;
if ((cluster < 2) || (cluster >= fs->n_fatent)) {
error = true;
break;
}
uint32_t cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2)));
for (uint32_t i = 0; i < fs->csize; i++) {
uint32_t sector = (cluster_sector + i);
if ((file_sector + i) >= sector_count) {
break;
}
switch (type) {
case ADDRESS_TYPE_MEM:
*address = sector;
break;
case ADDRESS_TYPE_PI:
io_write((uint32_t) (address), sector);
break;
}
address++;
}
}
if (f_close(&fil) != FR_OK) {
error = true;
}
return error;
}

View File

@ -7,13 +7,56 @@
#ifndef FLASHCART_UTILS_H__
#define FLASHCART_UTILS_H__
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <fatfs/ff.h>
#define SAVE_WRITEBACK_MAX_SECTORS (256)
void fix_file_size (FIL *fil);
/**
* @brief Address types for DMA operations.
*/
typedef enum {
ADDRESS_TYPE_MEM, /**< Memory address type. */
ADDRESS_TYPE_PI, /**< Peripheral Interface address type. */
} address_type_t;
/**
* @brief Perform a DMA read operation from the PI (Peripheral Interface).
*
* @param src Source address.
* @param dst Destination address.
* @param length Length of data to read.
*/
void pi_dma_read_data (void *src, void *dst, size_t length);
/**
* @brief Perform a DMA write operation to the PI (Peripheral Interface).
*
* @param src Source address.
* @param dst Destination address.
* @param length Length of data to write.
*/
void pi_dma_write_data (void *src, void *dst, size_t length);
/**
* @brief Fix the file size in the FAT filesystem.
*
* @param fil Pointer to the file object.
*/
void fatfs_fix_file_size (FIL *fil);
#endif
/**
* @brief Get the file sectors in the FAT filesystem.
*
* @param path Path to the file.
* @param address Pointer to store the address of the file sectors.
* @param address_type The type of address (memory or PI).
* @param max_sectors Maximum number of sectors.
* @return true if successful, false otherwise.
*/
bool fatfs_get_file_sectors (char *path, uint32_t *address, address_type_t address_type, uint32_t max_sectors);
#endif /* FLASHCART_UTILS_H__ */

View File

@ -0,0 +1,5 @@
## SummerCart64 developer notes
### Official documentation
https://github.com/Polprzewodnikowy/SummerCart64/tree/main/docs

View File

@ -1,3 +1,9 @@
/**
* @file sc64.c
* @brief SummerCart64 functions implementation
* @ingroup flashcart
*/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@ -12,38 +18,98 @@
#include "sc64_ll.h"
#include "sc64.h"
#define SRAM_FLASHRAM_ADDRESS (0x08000000)
#define ROM_ADDRESS (0x10000000)
#define IPL_ADDRESS (0x13BC0000)
#define EXTENDED_ADDRESS (0x14000000)
#define SHADOW_ADDRESS (0x1FFC0000)
#define EEPROM_ADDRESS (0x1FFE2000)
#define SUPPORTED_MAJOR_VERSION (2)
#define SUPPORTED_MINOR_VERSION (16)
#define SUPPORTED_MINOR_VERSION (17)
#define SUPPORTED_REVISION (0)
#define DISK_MAPPING_ROM_OFFSET (0x02000000)
#define DISK_MAX_SECTORS (126820)
static flashcart_error_t load_to_flash (FIL *fil, void *address, size_t size, UINT *br, flashcart_progress_callback_t *progress) {
#define DISK_TRACKS (1175)
#define DISK_HEADS (2)
#define DISK_BLOCKS (2)
#define DISK_SECTORS_PER_BLOCK (85)
#define DISK_ZONES (16)
#define DISK_BAD_TRACKS_PER_ZONE (12)
#define DISK_TYPES (7)
#define DISK_SYSTEM_LBA_COUNT (24)
#define THB_UNMAPPED (0xFFFFFFFF)
#define THB_WRITABLE_FLAG (1 << 31)
static const struct {
uint8_t head;
uint8_t sector_length;
uint8_t tracks;
uint16_t track_offset;
} zone_mapping[DISK_ZONES] = {
{ 0, 232, 158, 0 },
{ 0, 216, 158, 158 },
{ 0, 208, 149, 316 },
{ 0, 192, 149, 465 },
{ 0, 176, 149, 614 },
{ 0, 160, 149, 763 },
{ 0, 144, 149, 912 },
{ 0, 128, 114, 1061 },
{ 1, 216, 158, 0 },
{ 1, 208, 158, 158 },
{ 1, 192, 149, 316 },
{ 1, 176, 149, 465 },
{ 1, 160, 149, 614 },
{ 1, 144, 149, 763 },
{ 1, 128, 149, 912 },
{ 1, 112, 114, 1061 },
};
static const uint8_t vzone_to_pzone[DISK_TYPES][DISK_ZONES] = {
{0, 1, 2, 9, 8, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10},
{0, 1, 2, 3, 10, 9, 8, 4, 5, 6, 7, 15, 14, 13, 12, 11},
{0, 1, 2, 3, 4, 11, 10, 9, 8, 5, 6, 7, 15, 14, 13, 12},
{0, 1, 2, 3, 4, 5, 12, 11, 10, 9, 8, 6, 7, 15, 14, 13},
{0, 1, 2, 3, 4, 5, 6, 13, 12, 11, 10, 9, 8, 7, 15, 14},
{0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8, 15},
{0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8},
};
static const uint8_t rom_zones[DISK_TYPES] = { 5, 7, 9, 11, 13, 15, 16 };
/**
* @brief Load data to flash memory.
*
* @param fil Pointer to the file object.
* @param address Address to load data to.
* @param size Size of the data to load.
* @param br Pointer to store the number of bytes read.
* @param progress Progress callback function.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t load_to_flash (FIL *fil, void *address, size_t size, UINT *br, flashcart_progress_callback_t *progress) {
size_t erase_block_size;
UINT bp;
*br = 0;
if (sc64_ll_flash_get_erase_block_size(&erase_block_size) != SC64_OK) {
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
while (size > 0) {
size_t program_size = MIN(size, erase_block_size);
if (sc64_ll_flash_erase_block(address) != SC64_OK) {
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
if (f_read(fil, address, program_size, &bp) != FR_OK) {
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if (sc64_ll_flash_wait_busy() != SC64_OK) {
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
if (progress) {
progress(f_tell(fil) / (float) (f_size(fil)));
@ -56,27 +122,165 @@ static flashcart_error_t load_to_flash (FIL *fil, void *address, size_t size, UI
return FLASHCART_OK;
}
static flashcart_error_t sc64_init (void) {
/**
* @brief Check if a disk zone track is bad.
*
* @param zone Zone number.
* @param track Track number.
* @param disk_parameters Pointer to the disk parameters.
* @return true if the track is bad, false otherwise.
*/
static bool disk_zone_track_is_bad (uint8_t zone, uint8_t track, flashcart_disk_parameters_t *disk_parameters) {
for (int i = 0; i < DISK_BAD_TRACKS_PER_ZONE; i++) {
if (disk_parameters->defect_tracks[zone][i] == track) {
return true;
}
}
return false;
}
/**
* @brief Check if a system LBA is bad.
*
* @param lba Logical block address.
* @param disk_parameters Pointer to the disk parameters.
* @return true if the LBA is bad, false otherwise.
*/
static bool disk_system_lba_is_bad (uint16_t lba, flashcart_disk_parameters_t *disk_parameters) {
if (lba < DISK_SYSTEM_LBA_COUNT) {
return disk_parameters->bad_system_area_lbas[lba];
}
return false;
}
/**
* @brief Set the THB mapping for a disk.
*
* @param offset Offset for the THB mapping.
* @param track Track number.
* @param head Head number.
* @param block Block number.
* @param valid Valid flag.
* @param writable Writable flag.
* @param file_offset File offset.
*/
static void disk_set_thb_mapping (uint32_t offset, uint16_t track, uint8_t head, uint8_t block, bool valid, bool writable, int file_offset) {
uint32_t index = (track << 2) | (head << 1) | (block);
uint32_t mapping = valid ? ((writable ? THB_WRITABLE_FLAG : 0) | (file_offset & ~(THB_WRITABLE_FLAG))) : THB_UNMAPPED;
io_write(ROM_ADDRESS + offset + (index * sizeof(uint32_t)), mapping);
}
/**
* @brief Load the THB table for a disk.
*
* @param disk_parameters Pointer to the disk parameters.
* @param thb_table_offset Pointer to store the THB table offset.
* @param current_offset Pointer to the current offset.
*/
static void disk_load_thb_table (flashcart_disk_parameters_t *disk_parameters, uint32_t *thb_table_offset, uint32_t *current_offset) {
int file_offset = 0;
uint16_t lba = 0;
uint8_t starting_block = 0;
for (uint8_t vzone = 0; vzone < DISK_ZONES; vzone++) {
uint8_t pzone = vzone_to_pzone[disk_parameters->disk_type][vzone];
uint8_t head = zone_mapping[pzone].head;
uint8_t sector_length = zone_mapping[pzone].sector_length;
uint8_t tracks = zone_mapping[pzone].tracks;
uint16_t track_offset = zone_mapping[pzone].track_offset;
bool reverse = (head != 0);
int zone_track_start = (reverse ? (tracks - 1) : 0);
int zone_track_end = (reverse ? (-1) : tracks);
for (int zone_track = zone_track_start; zone_track != zone_track_end; zone_track += (reverse ? (-1) : 1)) {
uint16_t track = track_offset + zone_track;
if (disk_zone_track_is_bad(pzone, zone_track, disk_parameters)) {
disk_set_thb_mapping(*current_offset, track, head, 0, false, false, 0);
disk_set_thb_mapping(*current_offset, track, head, 1, false, false, 0);
continue;
}
for (uint8_t block = 0; block < DISK_BLOCKS; block += 1) {
bool valid = !(disk_system_lba_is_bad(lba, disk_parameters));
bool writable = (vzone >= rom_zones[disk_parameters->disk_type]);
disk_set_thb_mapping(*current_offset, track, head, (starting_block ^ block), valid, writable, file_offset);
file_offset += (sector_length * DISK_SECTORS_PER_BLOCK);
lba += 1;
}
starting_block ^= 1;
}
}
*thb_table_offset = *current_offset;
*current_offset += (DISK_TRACKS * DISK_HEADS * DISK_BLOCKS * sizeof(uint32_t));
}
/**
* @brief Load the sector table for a disk.
*
* @param path Path to the disk file.
* @param sector_table_offset Pointer to store the sector table offset.
* @param current_offset Pointer to the current offset.
* @return true if an error occurred, false otherwise.
*/
static bool disk_load_sector_table (char *path, uint32_t *sector_table_offset, uint32_t *current_offset) {
if (fatfs_get_file_sectors(path, (uint32_t *) (ROM_ADDRESS + *current_offset), ADDRESS_TYPE_PI, DISK_MAX_SECTORS)) {
return true;
}
*sector_table_offset = *current_offset;
*current_offset += (DISK_MAX_SECTORS * sizeof(uint32_t));
return false;
}
/**
* @brief Get the firmware version of the SummerCart64.
*
* @return flashcart_firmware_version_t The firmware version.
*/
static flashcart_firmware_version_t sc64_get_firmware_version (void) {
flashcart_firmware_version_t version_info;
sc64_ll_get_version(&version_info.major, &version_info.minor, &version_info.revision);
return version_info;
}
/**
* @brief Initialize the SummerCart64.
*
* @return flashcart_err_t Error code.
*/
static flashcart_err_t sc64_init (void) {
uint16_t major;
uint16_t minor;
uint32_t revision;
if (sc64_ll_get_version(&major, &minor, &revision) != SC64_OK) {
return FLASHCART_ERROR_OUTDATED;
return FLASHCART_ERR_OUTDATED;
}
if (major != SUPPORTED_MAJOR_VERSION) {
return FLASHCART_ERROR_OUTDATED;
return FLASHCART_ERR_OUTDATED;
}
if (minor < SUPPORTED_MINOR_VERSION) {
return FLASHCART_ERROR_OUTDATED;
return FLASHCART_ERR_OUTDATED;
} else if (minor == SUPPORTED_MINOR_VERSION && revision < SUPPORTED_REVISION) {
return FLASHCART_ERROR_OUTDATED;
return FLASHCART_ERR_OUTDATED;
}
bool writeback_pending;
do {
if (sc64_ll_writeback_pending(&writeback_pending) != SC64_OK) {
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
} while (writeback_pending);
@ -100,16 +304,21 @@ static flashcart_error_t sc64_init (void) {
{ CFG_ID_ROM_EXTENDED_ENABLE, false },
};
for (int i = 0; i < sizeof(default_config) / sizeof(default_config[0]); i++) {
for (unsigned int i = 0; i < sizeof(default_config) / sizeof(default_config[0]); i++) {
if (sc64_ll_set_config(default_config[i].id, default_config[i].value) != SC64_OK) {
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
}
return FLASHCART_OK;
}
static flashcart_error_t sc64_deinit (void) {
/**
* @brief Deinitialize the SummerCart64.
*
* @return flashcart_err_t Error code.
*/
static flashcart_err_t sc64_deinit (void) {
sc64_ll_set_config(CFG_ID_ROM_WRITE_ENABLE, false);
sc64_ll_lock();
@ -117,21 +326,48 @@ static flashcart_error_t sc64_deinit (void) {
return FLASHCART_OK;
}
static flashcart_error_t sc64_load_rom (char *rom_path, flashcart_progress_callback_t *progress) {
/**
* @brief Check if the SummerCart64 has a specific feature.
*
* @param feature The feature to check.
* @return true if the feature is supported, false otherwise.
*/
static bool sc64_has_feature (flashcart_features_t feature) {
switch (feature) {
case FLASHCART_FEATURE_64DD: return true;
case FLASHCART_FEATURE_RTC: return true;
case FLASHCART_FEATURE_USB: return true;
case FLASHCART_FEATURE_AUTO_CIC: return true;
case FLASHCART_FEATURE_AUTO_REGION: return true;
case FLASHCART_FEATURE_DIAGNOSTIC_DATA: return true;
case FLASHCART_FEATURE_SAVE_WRITEBACK: return true;
case FLASHCART_FEATURE_ROM_REBOOT_FAST: return true;
default: return false;
}
}
/**
* @brief Load a ROM into the SummerCart64.
*
* @param rom_path Path to the ROM file.
* @param progress Progress callback function.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t sc64_load_rom (char *rom_path, flashcart_progress_callback_t *progress) {
FIL fil;
UINT br;
if (f_open(&fil, strip_sd_prefix(rom_path), FA_READ) != FR_OK) {
return FLASHCART_ERROR_LOAD;
if (f_open(&fil, strip_fs_prefix(rom_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
fix_file_size(&fil);
fatfs_fix_file_size(&fil);
size_t rom_size = f_size(&fil);
if (rom_size > MiB(78)) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
bool shadow_enabled = (rom_size > (MiB(64) - KiB(128)));
@ -141,12 +377,12 @@ static flashcart_error_t sc64_load_rom (char *rom_path, flashcart_progress_callb
size_t shadow_size = shadow_enabled ? MIN(rom_size - sdram_size, KiB(128)) : 0;
size_t extended_size = extended_enabled ? rom_size - MiB(64) : 0;
size_t chunk_size = MiB(1);
for (int offset = 0; offset < sdram_size; offset += chunk_size) {
size_t chunk_size = KiB(128);
for (unsigned int offset = 0; offset < sdram_size; offset += chunk_size) {
size_t block_size = MIN(sdram_size - offset, chunk_size);
if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if (progress) {
progress(f_tell(&fil) / (float) (f_size(&fil)));
@ -154,179 +390,296 @@ static flashcart_error_t sc64_load_rom (char *rom_path, flashcart_progress_callb
}
if (f_tell(&fil) != sdram_size) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if (sc64_ll_set_config(CFG_ID_ROM_SHADOW_ENABLE, shadow_enabled) != SC64_OK) {
f_close(&fil);
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
if (shadow_enabled) {
flashcart_error_t error = load_to_flash(&fil, (void *) (SHADOW_ADDRESS), shadow_size, &br, progress);
if (error != FLASHCART_OK) {
flashcart_err_t err = load_to_flash(&fil, (void *) (SHADOW_ADDRESS), shadow_size, &br, progress);
if (err != FLASHCART_OK) {
f_close(&fil);
return error;
return err;
}
if (br != shadow_size) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
}
if (sc64_ll_set_config(CFG_ID_ROM_EXTENDED_ENABLE, extended_enabled) != SC64_OK) {
f_close(&fil);
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
if (extended_enabled) {
flashcart_error_t error = load_to_flash(&fil, (void *) (EXTENDED_ADDRESS), extended_size, &br, progress);
if (error != FLASHCART_OK) {
flashcart_err_t err = load_to_flash(&fil, (void *) (EXTENDED_ADDRESS), extended_size, &br, progress);
if (err != FLASHCART_OK) {
f_close(&fil);
return error;
return err;
}
if (br != extended_size) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
static flashcart_error_t sc64_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) {
/**
* @brief Load a file into the SummerCart64.
*
* @param file_path Path to the file.
* @param rom_offset ROM offset.
* @param file_offset File offset.
* @return flashcart_err_t Error code.
*/
static flashcart_err_t sc64_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) {
FIL fil;
UINT br;
if (f_open(&fil, strip_sd_prefix(file_path), FA_READ) != FR_OK) {
return FLASHCART_ERROR_LOAD;
if (f_open(&fil, strip_fs_prefix(file_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
fix_file_size(&fil);
fatfs_fix_file_size(&fil);
size_t file_size = f_size(&fil) - file_offset;
if (file_size > (MiB(64) - rom_offset)) {
f_close(&fil);
return FLASHCART_ERROR_ARGS;
return FLASHCART_ERR_ARGS;
}
if (f_lseek(&fil, file_offset) != FR_OK) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if (f_read(&fil, (void *) (ROM_ADDRESS + rom_offset), file_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if (br != file_size) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
static flashcart_error_t sc64_load_save (char *save_path) {
static flashcart_err_t sc64_load_save (char *save_path) {
void *address = NULL;
uint32_t value;
if (sc64_ll_get_config(CFG_ID_SAVE_TYPE, &value) != SC64_OK) {
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
sc64_save_type_t type = (sc64_save_type_t) (value);
switch (type) {
case SAVE_TYPE_EEPROM_4K:
case SAVE_TYPE_EEPROM_16K:
case SAVE_TYPE_EEPROM_4KBIT:
case SAVE_TYPE_EEPROM_16KBIT:
address = (void *) (EEPROM_ADDRESS);
break;
case SAVE_TYPE_SRAM:
case SAVE_TYPE_FLASHRAM:
case SAVE_TYPE_SRAM_256KBIT:
case SAVE_TYPE_FLASHRAM_1MBIT:
case SAVE_TYPE_SRAM_BANKED:
address = (void *) (SRAM_FLASHRAM_ADDRESS);
break;
case SAVE_TYPE_NONE:
default:
return FLASHCART_ERROR_ARGS;
return FLASHCART_ERR_ARGS;
}
FIL fil;
UINT br;
if (f_open(&fil, strip_sd_prefix(save_path), FA_READ) != FR_OK) {
return FLASHCART_ERROR_LOAD;
if (f_open(&fil, strip_fs_prefix(save_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
size_t save_size = f_size(&fil);
if (f_read(&fil, address, save_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
if (br != save_size) {
return FLASHCART_ERROR_LOAD;
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
static flashcart_error_t sc64_set_save_type (flashcart_save_type_t save_type) {
static flashcart_err_t sc64_load_64dd_ipl (char *ipl_path, flashcart_progress_callback_t *progress) {
FIL fil;
UINT br;
if (f_open(&fil, strip_fs_prefix(ipl_path), FA_READ) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
fatfs_fix_file_size(&fil);
size_t ipl_size = f_size(&fil);
if (ipl_size > MiB(4)) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
size_t chunk_size = KiB(128);
for (unsigned int offset = 0; offset < ipl_size; offset += chunk_size) {
size_t block_size = MIN(ipl_size - offset, chunk_size);
if (f_read(&fil, (void *) (IPL_ADDRESS + offset), block_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (progress) {
progress(f_tell(&fil) / (float) (f_size(&fil)));
}
}
if (f_tell(&fil) != ipl_size) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERR_LOAD;
}
return FLASHCART_OK;
}
static flashcart_err_t sc64_load_64dd_disk (char *disk_path, flashcart_disk_parameters_t *disk_parameters) {
sc64_disk_mapping_t mapping;
uint32_t mapping_offset = DISK_MAPPING_ROM_OFFSET;
sc64_drive_type_t drive_type = (disk_parameters->development_drive ? DRIVE_TYPE_DEVELOPMENT : DRIVE_TYPE_RETAIL);
// TODO: Support loading multiple disks
for (mapping.count = 0; mapping.count < 1; mapping.count++) {
disk_load_thb_table(disk_parameters++, &mapping.disks[mapping.count].thb_table, &mapping_offset);
if (disk_load_sector_table(disk_path++, &mapping.disks[mapping.count].sector_table, &mapping_offset)) {
return FLASHCART_ERR_LOAD;
}
}
if (mapping.count == 0) {
return FLASHCART_ERR_ARGS;
}
if (sc64_ll_set_disk_mapping(&mapping) != SC64_OK) {
return FLASHCART_ERR_INT;
}
const struct {
sc64_cfg_id_t id;
uint32_t value;
} config[] = {
{ CFG_ID_DD_MODE, DD_MODE_FULL },
{ CFG_ID_DD_SD_ENABLE, true },
{ CFG_ID_DD_DRIVE_TYPE, drive_type },
{ CFG_ID_DD_DISK_STATE, DISK_STATE_INSERTED },
{ CFG_ID_BUTTON_MODE, BUTTON_MODE_DD_DISK_SWAP },
};
for (unsigned int i = 0; i < sizeof(config) / sizeof(config[0]); i++) {
if (sc64_ll_set_config(config[i].id, config[i].value) != SC64_OK) {
return FLASHCART_ERR_INT;
}
}
return FLASHCART_OK;
}
static flashcart_err_t sc64_set_save_type (flashcart_save_type_t save_type) {
sc64_save_type_t type;
switch (save_type) {
case FLASHCART_SAVE_TYPE_NONE:
type = SAVE_TYPE_NONE;
break;
case FLASHCART_SAVE_TYPE_EEPROM_4K:
type = SAVE_TYPE_EEPROM_4K;
case FLASHCART_SAVE_TYPE_EEPROM_4KBIT:
type = SAVE_TYPE_EEPROM_4KBIT;
break;
case FLASHCART_SAVE_TYPE_EEPROM_16K:
type = SAVE_TYPE_EEPROM_16K;
case FLASHCART_SAVE_TYPE_EEPROM_16KBIT:
type = SAVE_TYPE_EEPROM_16KBIT;
break;
case FLASHCART_SAVE_TYPE_SRAM:
type = SAVE_TYPE_SRAM;
case FLASHCART_SAVE_TYPE_SRAM_256KBIT:
type = SAVE_TYPE_SRAM_256KBIT;
break;
case FLASHCART_SAVE_TYPE_SRAM_BANKED:
type = SAVE_TYPE_SRAM_BANKED;
break;
case FLASHCART_SAVE_TYPE_SRAM_128K:
type = SAVE_TYPE_SRAM_128K;
case FLASHCART_SAVE_TYPE_SRAM_1MBIT:
type = SAVE_TYPE_SRAM_1MBIT;
break;
case FLASHCART_SAVE_TYPE_FLASHRAM:
type = SAVE_TYPE_FLASHRAM;
case FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT:
type = SAVE_TYPE_FLASHRAM_1MBIT;
break;
case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2:
type = SAVE_TYPE_FLASHRAM_1MBIT;
break;
default:
return FLASHCART_ERROR_ARGS;
return FLASHCART_ERR_ARGS;
}
if (sc64_ll_set_config(CFG_ID_SAVE_TYPE, type) != SC64_OK) {
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
return FLASHCART_OK;
}
static flashcart_error_t sc64_set_save_writeback (uint32_t *sectors) {
static flashcart_err_t sc64_set_save_writeback (char *save_path) {
uint32_t sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
if (fatfs_get_file_sectors(save_path, sectors, ADDRESS_TYPE_MEM, SAVE_WRITEBACK_MAX_SECTORS)) {
return FLASHCART_ERR_LOAD;
}
pi_dma_write_data(sectors, SC64_BUFFERS->BUFFER, 1024);
if (sc64_ll_writeback_enable(SC64_BUFFERS->BUFFER) != SC64_OK) {
return FLASHCART_ERROR_INT;
return FLASHCART_ERR_INT;
}
return FLASHCART_OK;
}
static flashcart_err_t sc64_set_bootmode (flashcart_reboot_mode_t boot_mode) {
sc64_boot_mode_t type = BOOT_MODE_MENU;
switch (boot_mode) {
case FLASHCART_REBOOT_MODE_ROM:
type = BOOT_MODE_ROM;
break;
default:
type = BOOT_MODE_MENU;
break;
}
if (sc64_ll_set_config(CFG_ID_BOOT_MODE, type) != SC64_OK) {
return FLASHCART_ERR_INT;
}
return FLASHCART_OK;
@ -336,11 +689,16 @@ static flashcart_error_t sc64_set_save_writeback (uint32_t *sectors) {
static flashcart_t flashcart_sc64 = {
.init = sc64_init,
.deinit = sc64_deinit,
.has_feature = sc64_has_feature,
.get_firmware_version = sc64_get_firmware_version,
.load_rom = sc64_load_rom,
.load_file = sc64_load_file,
.load_save = sc64_load_save,
.load_64dd_ipl = sc64_load_64dd_ipl,
.load_64dd_disk = sc64_load_64dd_disk,
.set_save_type = sc64_set_save_type,
.set_save_writeback = sc64_set_save_writeback,
.set_next_boot_mode = sc64_set_bootmode,
};

View File

@ -1,13 +1,20 @@
/**
* @file sc64_ll.c
* @brief Low-level functions for SummerCart64
* @ingroup flashcart
*/
#include <libdragon.h>
#include "../flashcart_utils.h"
#include "sc64_ll.h"
/** @brief SummerCart64 Registers Structure. */
typedef struct {
uint32_t SR_CMD;
uint32_t DATA[2];
uint32_t IDENTIFIER;
uint32_t KEY;
uint32_t SR_CMD; /**< Command Status Register. */
uint32_t DATA[2]; /**< Data Registers. */
uint32_t IDENTIFIER; /**< Identifier Register. */
uint32_t KEY; /**< Key Register. */
} sc64_regs_t;
#define SC64_REGS_BASE (0x1FFF0000UL)
@ -18,24 +25,31 @@ typedef struct {
#define SC64_KEY_LOCK (0xFFFFFFFFUL)
/** @brief SummerCart64 Command IDs. */
typedef enum {
CMD_ID_VERSION_GET = 'V',
CMD_ID_CONFIG_GET = 'c',
CMD_ID_CONFIG_SET = 'C',
CMD_ID_DISK_MAPPING_SET = 'D',
CMD_ID_WRITEBACK_PENDING = 'w',
CMD_ID_WRITEBACK_SD_INFO = 'W',
CMD_ID_FLASH_WAIT_BUSY = 'p',
CMD_ID_FLASH_ERASE_BLOCK = 'P',
} sc64_cmd_id_t;
/** @brief SummerCart64 Commands Structure. */
typedef struct {
sc64_cmd_id_t id;
uint32_t arg[2];
uint32_t rsp[2];
sc64_cmd_id_t id; /**< Command ID. */
uint32_t arg[2]; /**< Command arguments. */
uint32_t rsp[2]; /**< Command response. */
} sc64_cmd_t;
/**
* @brief Execute a command on the SummerCart64.
*
* @param cmd Pointer to the command structure.
* @return sc64_error_t Error code.
*/
static sc64_error_t sc64_ll_execute_cmd (sc64_cmd_t *cmd) {
io_write((uint32_t) (&SC64_REGS->DATA[0]), cmd->arg[0]);
io_write((uint32_t) (&SC64_REGS->DATA[1]), cmd->arg[1]);
@ -57,11 +71,21 @@ static sc64_error_t sc64_ll_execute_cmd (sc64_cmd_t *cmd) {
return SC64_OK;
}
/**
* @brief Lock the SummerCart64.
*/
void sc64_ll_lock (void) {
io_write((uint32_t) (&SC64_REGS->KEY), SC64_KEY_LOCK);
}
/**
* @brief Get the firmware version of the SummerCart64.
*
* @param major Pointer to store the major version.
* @param minor Pointer to store the minor version.
* @param revision Pointer to store the revision number.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_get_version (uint16_t *major, uint16_t *minor, uint32_t *revision) {
sc64_cmd_t cmd = {
.id = CMD_ID_VERSION_GET
@ -73,6 +97,13 @@ sc64_error_t sc64_ll_get_version (uint16_t *major, uint16_t *minor, uint32_t *re
return error;
}
/**
* @brief Get a configuration value from the SummerCart64.
*
* @param id Configuration ID.
* @param value Pointer to store the configuration value.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_get_config (sc64_cfg_id_t id, uint32_t *value) {
sc64_cmd_t cmd = {
.id = CMD_ID_CONFIG_GET,
@ -83,6 +114,13 @@ sc64_error_t sc64_ll_get_config (sc64_cfg_id_t id, uint32_t *value) {
return error;
}
/**
* @brief Set a configuration value on the SummerCart64.
*
* @param id Configuration ID.
* @param value Configuration value.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_set_config (sc64_cfg_id_t id, uint32_t value) {
sc64_cmd_t cmd = {
.id = CMD_ID_CONFIG_SET,
@ -91,6 +129,44 @@ sc64_error_t sc64_ll_set_config (sc64_cfg_id_t id, uint32_t value) {
return sc64_ll_execute_cmd(&cmd);
}
/**
* @brief Set the disk mapping on the SummerCart64.
*
* @param disk_mapping Pointer to the disk mapping structure.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_set_disk_mapping (sc64_disk_mapping_t *disk_mapping) {
int disk_count = disk_mapping->count;
if (disk_count <= 0 || disk_count > 4) {
return SC64_ERROR_BAD_ARGUMENT;
}
uint32_t info[8] __attribute__((aligned(8)));
for (int i = 0; i < disk_count; i++) {
info[i * 2] = disk_mapping->disks[i].thb_table;
info[(i * 2) + 1] = disk_mapping->disks[i].sector_table;
}
uint32_t length = (disk_mapping->count * 2 * sizeof(uint32_t));
pi_dma_write_data(info, SC64_BUFFERS->BUFFER, length);
sc64_cmd_t cmd = {
.id = CMD_ID_DISK_MAPPING_SET,
.arg = { (uint32_t) (SC64_BUFFERS->BUFFER), length }
};
return sc64_ll_execute_cmd(&cmd);
}
/**
* @brief Check if there are pending writebacks on the SummerCart64.
*
* @param pending Pointer to store the pending status.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_writeback_pending (bool *pending) {
sc64_cmd_t cmd = {
.id = CMD_ID_WRITEBACK_PENDING
@ -100,6 +176,12 @@ sc64_error_t sc64_ll_writeback_pending (bool *pending) {
return error;
}
/**
* @brief Enable writeback on the SummerCart64.
*
* @param address Address to enable writeback.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_writeback_enable (void *address) {
sc64_cmd_t cmd = {
.id = CMD_ID_WRITEBACK_SD_INFO,
@ -108,6 +190,11 @@ sc64_error_t sc64_ll_writeback_enable (void *address) {
return sc64_ll_execute_cmd(&cmd);
}
/**
* @brief Wait for the flash to become idle on the SummerCart64.
*
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_flash_wait_busy (void) {
sc64_cmd_t cmd = {
.id = CMD_ID_FLASH_WAIT_BUSY,
@ -116,6 +203,12 @@ sc64_error_t sc64_ll_flash_wait_busy (void) {
return sc64_ll_execute_cmd(&cmd);
}
/**
* @brief Get the flash erase block size on the SummerCart64.
*
* @param erase_block_size Pointer to store the erase block size.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_flash_get_erase_block_size (size_t *erase_block_size) {
sc64_cmd_t cmd = {
.id = CMD_ID_FLASH_WAIT_BUSY,
@ -126,6 +219,12 @@ sc64_error_t sc64_ll_flash_get_erase_block_size (size_t *erase_block_size) {
return error;
}
/**
* @brief Erase a flash block on the SummerCart64.
*
* @param address Address of the block to erase.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_flash_erase_block (void *address) {
sc64_cmd_t cmd = {
.id = CMD_ID_FLASH_ERASE_BLOCK,

View File

@ -7,121 +7,206 @@
#ifndef FLASHCART_SC64_LL_H__
#define FLASHCART_SC64_LL_H__
#include <stddef.h>
#include <stdint.h>
/**
* @addtogroup sc64
* @{
*/
/** @brief The SC64 buffers structure. */
typedef struct {
uint8_t BUFFER[8192];
uint8_t EEPROM[2048];
uint8_t DD_SECTOR[256];
uint8_t FLASHRAM[128];
uint8_t BUFFER[8192]; /**< General buffer */
uint8_t EEPROM[2048]; /**< EEPROM buffer */
uint8_t DD_SECTOR[256]; /**< Disk Drive sector buffer */
uint8_t FLASHRAM[128]; /**< FlashRAM buffer */
} sc64_buffers_t;
#define SC64_BUFFERS_BASE (0x1FFE0000UL)
#define SC64_BUFFERS ((sc64_buffers_t *) SC64_BUFFERS_BASE)
/** @brief The SC64 State Enumeration. */
typedef enum {
SC64_OK,
SC64_ERROR_BAD_ARGUMENT,
SC64_ERROR_BAD_ADDRESS,
SC64_ERROR_BAD_CONFIG_ID,
SC64_ERROR_TIMEOUT,
SC64_ERROR_SD_CARD,
SC64_ERROR_UNKNOWN_CMD = -1
SC64_OK, /**< No error */
SC64_ERROR_BAD_ARGUMENT, /**< Bad argument error */
SC64_ERROR_BAD_ADDRESS, /**< Bad address error */
SC64_ERROR_BAD_CONFIG_ID, /**< Bad config ID error */
SC64_ERROR_TIMEOUT, /**< Timeout error */
SC64_ERROR_SD_CARD, /**< SD card error */
SC64_ERROR_UNKNOWN_CMD = -1 /**< Unknown command error */
} sc64_error_t;
/** @brief The SC64 Configuration ID Enumeration. */
typedef enum {
CFG_ID_BOOTLOADER_SWITCH,
CFG_ID_ROM_WRITE_ENABLE,
CFG_ID_ROM_SHADOW_ENABLE,
CFG_ID_DD_MODE,
CFG_ID_ISV_ADDRESS,
CFG_ID_BOOT_MODE,
CFG_ID_SAVE_TYPE,
CFG_ID_CIC_SEED,
CFG_ID_TV_TYPE,
CFG_ID_DD_SD_ENABLE,
CFG_ID_DD_DRIVE_TYPE,
CFG_ID_DD_DISK_STATE,
CFG_ID_BUTTON_STATE,
CFG_ID_BUTTON_MODE,
CFG_ID_ROM_EXTENDED_ENABLE,
CFG_ID_BOOTLOADER_SWITCH, /**< Bootloader switch config */
CFG_ID_ROM_WRITE_ENABLE, /**< ROM write enable config */
CFG_ID_ROM_SHADOW_ENABLE, /**< ROM shadow enable config */
CFG_ID_DD_MODE, /**< Disk Drive mode config */
CFG_ID_ISV_ADDRESS, /**< ISV address config */
CFG_ID_BOOT_MODE, /**< Boot mode config */
CFG_ID_SAVE_TYPE, /**< Save type config */
CFG_ID_CIC_SEED, /**< CIC seed config */
CFG_ID_TV_TYPE, /**< TV type config */
CFG_ID_DD_SD_ENABLE, /**< Disk Drive SD enable config */
CFG_ID_DD_DRIVE_TYPE, /**< Disk Drive type config */
CFG_ID_DD_DISK_STATE, /**< Disk Drive disk state config */
CFG_ID_BUTTON_STATE, /**< Button state config */
CFG_ID_BUTTON_MODE, /**< Button mode config */
CFG_ID_ROM_EXTENDED_ENABLE /**< ROM extended enable config */
} sc64_cfg_id_t;
/** @brief The SC64 Disk Drive Mode Enumeration. */
typedef enum {
DD_MODE_DISABLED = 0,
DD_MODE_REGS = 1,
DD_MODE_IPL = 2,
DD_MODE_FULL = 3
DD_MODE_DISABLED = 0, /**< Disk Drive disabled */
DD_MODE_REGS = 1, /**< Disk Drive registers mode */
DD_MODE_IPL = 2, /**< Disk Drive IPL mode */
DD_MODE_FULL = 3 /**< Disk Drive full mode */
} sc64_dd_mode_t;
/** @brief The SC64 Boot Mode Enumeration. */
typedef enum {
BOOT_MODE_MENU = 0,
BOOT_MODE_ROM = 1,
BOOT_MODE_DDIPL = 2,
BOOT_MODE_DIRECT_ROM = 3,
BOOT_MODE_DIRECT_DDIPL = 4,
BOOT_MODE_MENU = 0, /**< Boot to menu */
BOOT_MODE_ROM = 1, /**< Boot to ROM */
BOOT_MODE_DDIPL = 2, /**< Boot to Disk Drive IPL */
BOOT_MODE_DIRECT_ROM = 3, /**< Direct boot to ROM */
BOOT_MODE_DIRECT_DDIPL = 4 /**< Direct boot to Disk Drive IPL */
} sc64_boot_mode_t;
/** @brief The SC64 Save Type Enumeration. */
typedef enum {
SAVE_TYPE_NONE,
SAVE_TYPE_EEPROM_4K,
SAVE_TYPE_EEPROM_16K,
SAVE_TYPE_SRAM,
SAVE_TYPE_FLASHRAM,
SAVE_TYPE_SRAM_BANKED,
SAVE_TYPE_SRAM_128K,
SAVE_TYPE_NONE, /**< No save type */
SAVE_TYPE_EEPROM_4KBIT, /**< EEPROM 4Kbit */
SAVE_TYPE_EEPROM_16KBIT, /**< EEPROM 16Kbit */
SAVE_TYPE_SRAM_256KBIT, /**< SRAM 256Kbit */
SAVE_TYPE_FLASHRAM_1MBIT, /**< FlashRAM 1Mbit */
SAVE_TYPE_SRAM_BANKED, /**< SRAM Banked */
SAVE_TYPE_SRAM_1MBIT /**< SRAM 1Mbit */
} sc64_save_type_t;
/** @brief The SC64 CIC Seed Enumeration. */
typedef enum {
CIC_SEED_AUTO = 0xFFFF
CIC_SEED_AUTO = 0xFFFF /**< Automatic CIC seed */
} sc64_cic_seed_t;
/** @brief The SC64 TV Type Enumeration. */
typedef enum {
TV_TYPE_PAL = 0,
TV_TYPE_NTSC = 1,
TV_TYPE_MPAL = 2,
TV_TYPE_PASSTHROUGH = 3
TV_TYPE_PAL = 0, /**< PAL TV type */
TV_TYPE_NTSC = 1, /**< NTSC TV type */
TV_TYPE_MPAL = 2, /**< MPAL TV type */
TV_TYPE_PASSTHROUGH = 3 /**< Passthrough TV type */
} sc64_tv_type_t;
/** @brief The SC64 Drive Type Enumeration. */
typedef enum {
DRIVE_TYPE_RETAIL,
DRIVE_TYPE_DEVELOPMENT,
DRIVE_TYPE_RETAIL, /**< Retail drive type */
DRIVE_TYPE_DEVELOPMENT /**< Development drive type */
} sc64_drive_type_t;
/** @brief The SC64 Disk State Enumeration. */
typedef enum {
DISK_STATE_EJECTED,
DISK_STATE_INSERTED,
DISK_STATE_CHANGED,
DISK_STATE_EJECTED, /**< Disk ejected */
DISK_STATE_INSERTED, /**< Disk inserted */
DISK_STATE_CHANGED /**< Disk state changed */
} sc64_disk_state_t;
/** @brief The SC64 Button Mode Enumeration. */
typedef enum {
BUTTON_MODE_NONE,
BUTTON_MODE_N64_IRQ,
BUTTON_MODE_USB_PACKET,
BUTTON_MODE_DD_DISK_SWAP,
BUTTON_MODE_NONE, /**< No button mode */
BUTTON_MODE_N64_IRQ, /**< N64 IRQ button mode */
BUTTON_MODE_USB_PACKET, /**< USB packet button mode */
BUTTON_MODE_DD_DISK_SWAP /**< Disk Drive disk swap button mode */
} sc64_button_mode_t;
/** @brief The SC64 Disk Mapping Structure. */
typedef struct {
int count; /**< Number of disks */
struct {
uint32_t thb_table; /**< THB table */
uint32_t sector_table; /**< Sector table */
} disks[4]; /**< Array of disks */
} sc64_disk_mapping_t;
void sc64_ll_lock (void);
sc64_error_t sc64_ll_get_version (uint16_t *major, uint16_t *minor, uint32_t *revision);
sc64_error_t sc64_ll_get_config (sc64_cfg_id_t cfg, uint32_t *value);
sc64_error_t sc64_ll_set_config (sc64_cfg_id_t cfg, uint32_t value);
sc64_error_t sc64_ll_writeback_pending (bool *pending);
sc64_error_t sc64_ll_writeback_enable (void *address);
sc64_error_t sc64_ll_flash_wait_busy (void);
sc64_error_t sc64_ll_flash_get_erase_block_size (size_t *erase_block_size);
sc64_error_t sc64_ll_flash_erase_block (void *address);
/**
* @brief Lock the SC64.
*/
void sc64_ll_lock(void);
/**
* @brief Get the SC64 version.
*
* @param major Pointer to store the major version.
* @param minor Pointer to store the minor version.
* @param revision Pointer to store the revision.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_get_version(uint16_t *major, uint16_t *minor, uint32_t *revision);
/**
* @brief Get the SC64 configuration.
*
* @param cfg Configuration ID.
* @param value Pointer to store the configuration value.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_get_config(sc64_cfg_id_t cfg, uint32_t *value);
/**
* @brief Set the SC64 configuration.
*
* @param cfg Configuration ID.
* @param value Configuration value.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_set_config(sc64_cfg_id_t cfg, uint32_t value);
/**
* @brief Set the SC64 disk mapping.
*
* @param disk_mapping Pointer to the disk mapping structure.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_set_disk_mapping(sc64_disk_mapping_t *disk_mapping);
/**
* @brief Check if writeback is pending.
*
* @param pending Pointer to store the pending status.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_writeback_pending(bool *pending);
/**
* @brief Enable writeback.
*
* @param address Address to enable writeback.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_writeback_enable(void *address);
/**
* @brief Wait for flash to be not busy.
*
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_flash_wait_busy(void);
/**
* @brief Get the flash erase block size.
*
* @param erase_block_size Pointer to store the erase block size.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_flash_get_erase_block_size(size_t *erase_block_size);
/**
* @brief Erase a flash block.
*
* @param address Address of the block to erase.
* @return sc64_error_t Error code.
*/
sc64_error_t sc64_ll_flash_erase_block(void *address);
/** @} */ /* sc64 */
#endif
#endif // FLASHCART_SC64_LL_H__

View File

@ -1,61 +0,0 @@
// Implementation based on https://gitlab.com/pixelfx-public/n64-game-id
#include <libdragon.h>
#include "hdmi.h"
#define ROM_ADDRESS (0x10000000)
#define DDIPL_ADDRESS (0x06000000)
#define CONTROLLER_PORT (0)
#define GAME_ID_COMMAND (0x1D)
void hdmi_reset_game_id (void) {
uint8_t joybus_tx[10];
uint8_t joybus_rx[1];
memset(joybus_tx, 0, sizeof(joybus_tx));
execute_raw_command(
CONTROLLER_PORT,
GAME_ID_COMMAND,
sizeof(joybus_tx),
sizeof(joybus_rx),
joybus_tx,
joybus_rx
);
}
void hdmi_broadcast_game_id (boot_params_t *boot_params) {
uint8_t rom_header[0x40] __attribute__((aligned(8)));
uint32_t pi_address = ROM_ADDRESS;
if (boot_params->device_type == BOOT_DEVICE_TYPE_DD) {
pi_address = DDIPL_ADDRESS;
}
dma_read_async(rom_header, pi_address, sizeof(rom_header));
dma_wait();
uint8_t joybus_tx[10];
uint8_t joybus_rx[1];
// Copy CRC
memcpy(joybus_tx, rom_header + 0x10, 8);
// Copy media format
joybus_tx[8] = rom_header[0x3B];
// Copy country code
joybus_tx[9] = rom_header[0x3E];
execute_raw_command(
CONTROLLER_PORT,
GAME_ID_COMMAND,
sizeof(joybus_tx),
sizeof(joybus_rx),
joybus_tx,
joybus_rx
);
}

View File

@ -1,12 +0,0 @@
#ifndef HDMI_H__
#define HDMI_H__
#include "boot/boot.h"
void hdmi_reset_game_id (void);
void hdmi_broadcast_game_id (boot_params_t *boot_params);
#endif

2
src/libs/libspng vendored

@ -1 +1 @@
Subproject commit e5c1fc470fceaca08b8c30dc40768c28b82b9e12
Subproject commit adc94393dbeddf9e027d1b2dfff7c1bab975224e

2
src/libs/mini.c vendored

@ -1 +1 @@
Subproject commit f713e3c98ee2340a90334da6084c34ec2109a7e6
Subproject commit 6229658b9d275fb760bcee119104bdbd7873536e

2
src/libs/miniz vendored

@ -1 +1 @@
Subproject commit 9ae305f6e109f8f1fbd2130458f1ee6197269b3b
Subproject commit 0c30a001bc3c70770a8742ff00899e662f040c75

View File

@ -1,19 +1,14 @@
#include <libdragon.h>
#include "boot/boot.h"
#include "hdmi/hdmi.h"
#include "menu/menu.h"
int main (void) {
boot_params_t boot_params;
hdmi_reset_game_id();
menu_run(&boot_params);
hdmi_broadcast_game_id(&boot_params);
disable_interrupts();
boot(&boot_params);

View File

@ -3,8 +3,11 @@
#include "actions.h"
#define ACTIONS_REPEAT_DELAY 10
#define JOYSTICK_DEADZONE 32
#define ACTIONS_REPEAT_DELAY (8)
static int dir_repeat_delay;
static joypad_8way_t last_dir = JOYPAD_8WAY_NONE;
static void actions_clear (menu_t *menu) {
@ -12,101 +15,120 @@ static void actions_clear (menu_t *menu) {
menu->actions.go_down = false;
menu->actions.go_left = false;
menu->actions.go_right = false;
menu->actions.fast = false;
menu->actions.go_fast = false;
menu->actions.enter = false;
menu->actions.back = false;
menu->actions.options = false;
menu->actions.system_info = false;
menu->actions.settings = false;
menu->actions.lz_context = false;
menu->actions.previous_tab = false;
menu->actions.next_tab = false;
}
static void actions_update_direction (menu_t *menu) {
joypad_8way_t held_dir = JOYPAD_8WAY_NONE;
joypad_8way_t fast_dir = JOYPAD_8WAY_NONE;
JOYPAD_PORT_FOREACH (i) {
held_dir = joypad_get_direction(i, JOYPAD_2D_DPAD | JOYPAD_2D_STICK);
fast_dir = joypad_get_direction(i, JOYPAD_2D_C);
if (held_dir != JOYPAD_8WAY_NONE || fast_dir != JOYPAD_8WAY_NONE) {
break;
}
}
if (fast_dir != JOYPAD_8WAY_NONE) {
held_dir = fast_dir;
menu->actions.go_fast = true;
}
joypad_8way_t final_dir = held_dir;
if ((last_dir != held_dir) && (last_dir == JOYPAD_8WAY_NONE)) {
dir_repeat_delay = ACTIONS_REPEAT_DELAY;
} else if (dir_repeat_delay > 0) {
final_dir = JOYPAD_8WAY_NONE;
}
switch (final_dir) {
case JOYPAD_8WAY_NONE:
break;
case JOYPAD_8WAY_RIGHT:
menu->actions.go_right = true;
break;
case JOYPAD_8WAY_UP_RIGHT:
menu->actions.go_up = true;
menu->actions.go_right = true;
break;
case JOYPAD_8WAY_UP:
menu->actions.go_up = true;
break;
case JOYPAD_8WAY_UP_LEFT:
menu->actions.go_up = true;
menu->actions.go_left = true;
break;
case JOYPAD_8WAY_LEFT:
menu->actions.go_left = true;
break;
case JOYPAD_8WAY_DOWN_LEFT:
menu->actions.go_down = true;
menu->actions.go_left = true;
break;
case JOYPAD_8WAY_DOWN:
menu->actions.go_down = true;
break;
case JOYPAD_8WAY_DOWN_RIGHT:
menu->actions.go_down = true;
menu->actions.go_right = true;
break;
}
if (dir_repeat_delay > 0) {
dir_repeat_delay -= 1;
}
last_dir = held_dir;
}
static void actions_update_buttons (menu_t *menu) {
joypad_buttons_t pressed = {0};
JOYPAD_PORT_FOREACH (i) {
pressed = joypad_get_buttons_pressed(i);
if (pressed.raw) {
break;
}
}
if (pressed.a) {
menu->actions.enter = true;
} else if (pressed.b) {
menu->actions.back = true;
} else if (pressed.r) {
menu->actions.options = true;
} else if (pressed.start) {
menu->actions.settings = true;
} else if (pressed.l || pressed.z) {
menu->actions.lz_context = true;
} else if (pressed.c_left) {
menu->actions.previous_tab = true;
} else if (pressed.c_right) {
menu->actions.next_tab = true;
}
}
void actions_init (void) {
JOYPAD_PORT_FOREACH (port) {
joypad_set_rumble_active(port, false);
}
}
void actions_update (menu_t *menu) {
controller_scan();
struct controller_data down = get_keys_down();
struct controller_data held = get_keys_held();
struct controller_data pressed = get_keys_pressed();
if (down.c[0].err != ERROR_NONE) {
return;
}
joypad_poll();
actions_clear(menu);
if (down.c[0].up || down.c[0].C_up) {
menu->actions.go_up = true;
menu->actions.vertical_held_counter = 0;
if (down.c[0].C_up) {
menu->actions.fast = true;
}
} else if (down.c[0].down || down.c[0].C_down) {
menu->actions.go_down = true;
menu->actions.vertical_held_counter = 0;
if (down.c[0].C_down) {
menu->actions.fast = true;
}
} else if (held.c[0].up || held.c[0].C_up) {
menu->actions.vertical_held_counter += 1;
if (menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY) {
menu->actions.go_up = true;
if (held.c[0].C_up) {
menu->actions.fast = true;
}
}
} else if (held.c[0].down || held.c[0].C_down) {
menu->actions.vertical_held_counter += 1;
if (menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY) {
menu->actions.go_down = true;
if (held.c[0].C_down) {
menu->actions.fast = true;
}
}
} else if (pressed.c[0].y > +JOYSTICK_DEADZONE) { // TODO: requires improvement for responsiveness
menu->actions.vertical_held_counter += 1;
if (menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY / 2) {
menu->actions.go_up = true;
if (pressed.c[0].y < +75) {
menu->actions.vertical_held_counter = 0;
}
}
} else if (pressed.c[0].y < -JOYSTICK_DEADZONE) { // TODO: requires improvement for responsiveness
menu->actions.vertical_held_counter += 1;
if (menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY / 2) {
menu->actions.go_down = true;
if (pressed.c[0].y > -75) {
menu->actions.vertical_held_counter = 0;
}
}
}
if (down.c[0].left) {
menu->actions.go_left = true;
menu->actions.horizontal_held_counter = 0;
} else if (down.c[0].right) {
menu->actions.go_right = true;
menu->actions.horizontal_held_counter = 0;
} else if (held.c[0].left) {
menu->actions.horizontal_held_counter += 1;
if (menu->actions.horizontal_held_counter >= ACTIONS_REPEAT_DELAY) {
menu->actions.go_left = true;
}
} else if (held.c[0].right) {
menu->actions.horizontal_held_counter += 1;
if (menu->actions.horizontal_held_counter >= ACTIONS_REPEAT_DELAY) {
menu->actions.go_right = true;
}
}
if (down.c[0].A) {
menu->actions.enter = true;
} else if (down.c[0].B) {
menu->actions.back = true;
} else if (down.c[0].R) {
menu->actions.options = true;
} else if (down.c[0].L) {
menu->actions.system_info = true;
} else if (down.c[0].start) {
menu->actions.settings = true;
}
actions_update_direction(menu);
actions_update_buttons(menu);
}

View File

@ -7,11 +7,18 @@
#ifndef ACTIONS_H__
#define ACTIONS_H__
#include "menu_state.h"
/**
* @brief Initialize the actions module.
*/
void actions_init (void);
/**
* @brief Update the actions based on the current menu state.
*
* @param menu Pointer to the menu structure.
*/
void actions_update (menu_t *menu);
#endif
#endif /* ACTIONS_H__ */

289
src/menu/bookkeeping.c Normal file
View File

@ -0,0 +1,289 @@
/**
* @file bookkeeping.c
* @brief Bookkeeping functions for history and favorites
* @ingroup menu
*/
#include <libdragon.h>
#include <mini.c/src/mini.h>
#include "bookkeeping.h"
#include "utils/fs.h"
#include "path.h"
static char *history_path = NULL;
static path_t *empty_path = NULL;
static bookkeeping_t init;
/**
* @brief Initialize the bookkeeping system with the specified path.
*
* @param path Path to the history file.
*/
void bookkeeping_init (char *path) {
if (history_path) {
free(history_path);
}
history_path = strdup(path);
empty_path = path_create("");
}
/**
* @brief Load a list of bookkeeping items from an INI file.
*
* @param list Pointer to the list of bookkeeping items.
* @param count Number of items in the list.
* @param ini Pointer to the INI file structure.
* @param group Name of the group in the INI file.
*/
void bookkeeping_ini_load_list(bookkeeping_item_t *list, int count, mini_t *ini, const char *group) {
char buf[64];
for(int i = 0; i < count; i++) {
sprintf(buf, "%d_primary_path", i);
list[i].primary_path = path_create(mini_get_string(ini, group, buf, ""));
sprintf(buf, "%d_secondary_path", i);
list[i].secondary_path = path_create(mini_get_string(ini, group, buf, ""));
sprintf(buf, "%d_type", i);
list[i].bookkeeping_type = mini_get_int(ini, group, buf, BOOKKEEPING_TYPE_EMPTY);
}
}
/**
* @brief Load the bookkeeping history and favorites from the history file.
*
* @param history Pointer to the bookkeeping structure.
*/
void bookkeeping_load (bookkeeping_t *history) {
if (!file_exists(history_path)) {
bookkeeping_save(&init);
}
mini_t *bookkeeping_ini = mini_try_load(history_path);
bookkeeping_ini_load_list(history->history_items, HISTORY_COUNT, bookkeeping_ini, "history");
bookkeeping_ini_load_list(history->favorite_items, FAVORITES_COUNT, bookkeeping_ini, "favorite");
mini_free(bookkeeping_ini);
}
/**
* @brief Save a list of bookkeeping items to an INI file.
*
* @param list Pointer to the list of bookkeeping items.
* @param count Number of items in the list.
* @param ini Pointer to the INI file structure.
* @param group Name of the group in the INI file.
*/
static void bookkeeping_ini_save_list(bookkeeping_item_t *list, int count, mini_t *ini, const char *group) {
char buf[64];
for(int i = 0; i < count; i++) {
sprintf(buf, "%d_primary_path", i);
path_t* path = list[i].primary_path;
mini_set_string(ini, group, buf, path != NULL ? path_get(path) : "");
sprintf(buf, "%d_secondary_path", i);
path = list[i].secondary_path;
mini_set_string(ini, group, buf, path != NULL ? path_get(path) : "");
sprintf(buf, "%d_type", i);
mini_set_int(ini, group, buf, list[i].bookkeeping_type);
}
}
/**
* @brief Save the bookkeeping history and favorites to the history file.
*
* @param history Pointer to the bookkeeping structure.
*/
void bookkeeping_save (bookkeeping_t *history) {
mini_t *bookkeeping_ini = mini_create(history_path);
bookkeeping_ini_save_list(history->history_items, HISTORY_COUNT, bookkeeping_ini, "history");
bookkeeping_ini_save_list(history->favorite_items, FAVORITES_COUNT, bookkeeping_ini, "favorite");
mini_save(bookkeeping_ini, MINI_FLAGS_SKIP_EMPTY_GROUPS);
mini_free(bookkeeping_ini);
}
/**
* @brief Check if two bookkeeping items match.
*
* @param left Pointer to the first bookkeeping item.
* @param right Pointer to the second bookkeeping item.
* @return true if the items match, false otherwise.
*/
static bool bookkeeping_item_match(bookkeeping_item_t *left, bookkeeping_item_t *right) {
if(left != NULL && right != NULL) {
return path_are_match(left->primary_path, right->primary_path) && path_are_match(left->secondary_path, right->secondary_path) && left->bookkeeping_type == right->bookkeeping_type;
}
return false;
}
/**
* @brief Clear a bookkeeping item.
*
* @param item Pointer to the bookkeeping item.
* @param leave_null Flag indicating whether to leave the paths as NULL.
*/
static void bookkeeping_clear_item(bookkeeping_item_t *item, bool leave_null) {
if(item->primary_path != NULL){
path_free(item->primary_path);
if(leave_null) {
item->primary_path = NULL;
} else {
item->primary_path = path_create("");
}
}
if(item->secondary_path != NULL){
path_free(item->secondary_path);
if(leave_null) {
item->secondary_path = NULL;
} else {
item->secondary_path = path_create("");
}
}
item->bookkeeping_type = BOOKKEEPING_TYPE_EMPTY;
}
/**
* @brief Copy a bookkeeping item.
*
* @param source Pointer to the source bookkeeping item.
* @param destination Pointer to the destination bookkeeping item.
*/
static void bookkeeping_copy_item(bookkeeping_item_t *source, bookkeeping_item_t *destination) {
bookkeeping_clear_item(destination, true);
destination->primary_path = source->primary_path != NULL ? path_clone(source->primary_path) : path_create("");
destination->secondary_path = source->secondary_path != NULL ? path_clone(source->secondary_path) : path_create("");
destination->bookkeeping_type = source->bookkeeping_type;
}
/**
* @brief Move bookkeeping items down in the list.
*
* @param list Pointer to the list of bookkeeping items.
* @param start Start index.
* @param end End index.
*/
static void bookkeeping_move_items_down(bookkeeping_item_t *list, int start, int end) {
int current = end;
do {
if(current <= start || current < 0) {
break;
}
bookkeeping_copy_item(&list[current - 1], &list[current]);
current--;
} while(true);
}
/**
* @brief Move bookkeeping items up in the list.
*
* @param list Pointer to the list of bookkeeping items.
* @param start Start index.
* @param end End index.
*/
static void bookkeeping_move_items_up(bookkeeping_item_t *list, int start, int end) {
int current = start;
do {
if(current >= end) {
break;
}
bookkeeping_copy_item(&list[current + 1], &list[current]);
current++;
} while(true);
}
/**
* @brief Insert a bookkeeping item at the top of the list.
*
* @param list Pointer to the list of bookkeeping items.
* @param count Number of items in the list.
* @param new_item Pointer to the new bookkeeping item.
*/
static void bookkeeping_insert_top(bookkeeping_item_t *list, int count, bookkeeping_item_t *new_item) {
// if it matches the top of the list already then nothing to do
if(bookkeeping_item_match(&list[0], new_item)) {
return;
}
// if the top isn't empty then we need to move things around
if(list[0].bookkeeping_type != BOOKKEEPING_TYPE_EMPTY) {
int found_at = -1;
for(int i = 1; i < count; i++) {
if(bookkeeping_item_match(&list[i], new_item)){
found_at = i;
break;
}
}
if(found_at == -1) {
bookkeeping_move_items_down(list, 0, count - 1);
} else {
bookkeeping_move_items_down(list, 0, found_at);
}
}
bookkeeping_copy_item(new_item, &list[0]);
}
/**
* @brief Add a new item to the bookkeeping history.
*
* @param bookkeeping Pointer to the bookkeeping structure.
* @param primary_path Pointer to the primary path.
* @param secondary_path Pointer to the secondary path.
* @param type The type of the bookkeeping item.
*/
void bookkeeping_history_add(bookkeeping_t *bookkeeping, path_t *primary_path, path_t *secondary_path, bookkeeping_item_types_t type) {
bookkeeping_item_t new_item = {
.primary_path = primary_path,
.secondary_path = secondary_path,
.bookkeeping_type = type
};
bookkeeping_insert_top(bookkeeping->history_items, HISTORY_COUNT, &new_item);
bookkeeping_save(bookkeeping);
}
/**
* @brief Add a new item to the bookkeeping favorites.
*
* @param bookkeeping Pointer to the bookkeeping structure.
* @param primary_path Pointer to the primary path.
* @param secondary_path Pointer to the secondary path.
* @param type The type of the bookkeeping item.
*/
void bookkeeping_favorite_add(bookkeeping_t *bookkeeping, path_t *primary_path, path_t *secondary_path, bookkeeping_item_types_t type) {
bookkeeping_item_t new_item = {
.primary_path = primary_path,
.secondary_path = secondary_path,
.bookkeeping_type = type
};
bookkeeping_insert_top(bookkeeping->favorite_items, FAVORITES_COUNT, &new_item);
bookkeeping_save(bookkeeping);
}
/**
* @brief Remove an item from the bookkeeping favorites.
*
* @param bookkeeping Pointer to the bookkeeping structure.
* @param selection Index of the item to remove.
*/
void bookkeeping_favorite_remove(bookkeeping_t *bookkeeping, int selection) {
if(bookkeeping->favorite_items[selection].bookkeeping_type != BOOKKEEPING_TYPE_EMPTY) {
bookkeeping_move_items_up(bookkeeping->favorite_items, selection, FAVORITES_COUNT - 1);
bookkeeping_clear_item(&bookkeeping->favorite_items[FAVORITES_COUNT - 1], false);
bookkeeping_save(bookkeeping);
}
}

84
src/menu/bookkeeping.h Normal file
View File

@ -0,0 +1,84 @@
/**
* @file bookkeeping.h
* @brief Bookkeeping of loaded ROMs.
* @ingroup menu
*/
#ifndef BOOKKEEPING_H__
#define BOOKKEEPING_H__
#include "path.h"
#define FAVORITES_COUNT 8 /**< Maximum number of favorite items */
#define HISTORY_COUNT 8 /**< Maximum number of history items */
/** @brief Bookkeeping item types enumeration */
typedef enum {
BOOKKEEPING_TYPE_EMPTY, /**< Empty item */
BOOKKEEPING_TYPE_ROM, /**< ROM item */
BOOKKEEPING_TYPE_DISK, /**< Disk item */
} bookkeeping_item_types_t;
/** @brief Bookkeeping item structure */
typedef struct {
path_t *primary_path; /**< Primary path */
path_t *secondary_path; /**< Secondary path */
bookkeeping_item_types_t bookkeeping_type; /**< Bookkeeping item type */
} bookkeeping_item_t;
/** @brief ROM bookkeeping structure */
typedef struct {
bookkeeping_item_t history_items[HISTORY_COUNT]; /**< History items */
bookkeeping_item_t favorite_items[FAVORITES_COUNT]; /**< Favorite items */
} bookkeeping_t;
/**
* @brief Initialize ROM bookkeeping path.
*
* @param path The path to initialize.
*/
void bookkeeping_init(char *path);
/**
* @brief Load ROM bookkeeping.
*
* @param history Pointer to the bookkeeping structure to load.
*/
void bookkeeping_load(bookkeeping_t *history);
/**
* @brief Save ROM bookkeeping.
*
* @param history Pointer to the bookkeeping structure to save.
*/
void bookkeeping_save(bookkeeping_t *history);
/**
* @brief Add a ROM to the history.
*
* @param bookkeeping The bookkeeping structure.
* @param primary_path The primary path of the ROM.
* @param secondary_path The secondary path of the ROM.
* @param type The type of the bookkeeping item.
*/
void bookkeeping_history_add(bookkeeping_t *bookkeeping, path_t *primary_path, path_t *secondary_path, bookkeeping_item_types_t type);
/**
* @brief Add a ROM to the favorites.
*
* @param bookkeeping The bookkeeping structure.
* @param primary_path The primary path of the ROM.
* @param secondary_path The secondary path of the ROM.
* @param type The type of the bookkeeping item.
*/
void bookkeeping_favorite_add(bookkeeping_t *bookkeeping, path_t *primary_path, path_t *secondary_path, bookkeeping_item_types_t type);
/**
* @brief Remove a ROM from the favorites.
*
* @param bookkeeping The bookkeeping structure.
* @param selection The index of the favorite item to remove.
*/
void bookkeeping_favorite_remove(bookkeeping_t *bookkeeping, int selection);
#endif /* BOOKKEEPING_H__ */

View File

@ -1,75 +1,140 @@
/**
* @file cart_load.c
* @brief Cart loading functions
* @ingroup menu
*/
#include <string.h>
#include <libdragon.h>
#include "cart_load.h"
#include "path.h"
#include "utils/fs.h"
#include "utils/utils.h"
#ifndef SAVES_SUBDIRECTORY
#define SAVES_SUBDIRECTORY "saves"
#define EMU_LOCATION "/emulators"
#endif
#ifndef DDIPL_LOCATION
#define DDIPL_LOCATION "/menu/64ddipl"
#endif
#ifndef EMU_LOCATION
#define EMU_LOCATION "/menu/emulators"
#endif
/**
* @brief Check if the 64DD is connected.
*
* @return true if the 64DD is connected, false otherwise.
*/
static bool is_64dd_connected (void) {
bool is_64dd_io_present = ((io_read(0x05000540) & 0x0000FFFF) == 0x0000);
bool is_64dd_ipl_present = (io_read(0x06001010) == 0x2129FFF8);
return (is_64dd_io_present || is_64dd_ipl_present);
}
static bool create_saves_subdirectory (menu_t *menu) {
path_t *save_folder_path = path_clone_push(menu->browser.directory, SAVES_SUBDIRECTORY);
/**
* @brief Create the saves subdirectory.
*
* @param path Pointer to the path structure.
* @return true if an error occurred, false otherwise.
*/
static bool create_saves_subdirectory (path_t *path) {
path_t *save_folder_path = path_clone(path);
path_pop(save_folder_path);
path_push(save_folder_path, SAVES_SUBDIRECTORY);
bool error = directory_create(path_get(save_folder_path));
path_free(save_folder_path);
return error;
}
static flashcart_save_type_t convert_save_type (rom_header_t *header) {
switch (rom_db_match_save_type(*header)) {
case DB_SAVE_TYPE_EEPROM_4K:
return FLASHCART_SAVE_TYPE_EEPROM_4K;
case DB_SAVE_TYPE_EEPROM_16K:
return FLASHCART_SAVE_TYPE_EEPROM_16K;
case DB_SAVE_TYPE_SRAM:
return FLASHCART_SAVE_TYPE_SRAM;
case DB_SAVE_TYPE_SRAM_BANKED:
return FLASHCART_SAVE_TYPE_SRAM_BANKED;
case DB_SAVE_TYPE_SRAM_128K:
return FLASHCART_SAVE_TYPE_SRAM_128K;
case DB_SAVE_TYPE_FLASHRAM:
return FLASHCART_SAVE_TYPE_FLASHRAM;
default:
return FLASHCART_SAVE_TYPE_NONE;
/**
* @brief Convert the ROM save type to the flashcart save type.
*
* @param save_type The ROM save type.
* @return flashcart_save_type_t The flashcart save type.
*/
static flashcart_save_type_t convert_save_type (rom_save_type_t save_type) {
switch (save_type) {
case SAVE_TYPE_EEPROM_4KBIT: return FLASHCART_SAVE_TYPE_EEPROM_4KBIT;
case SAVE_TYPE_EEPROM_16KBIT: return FLASHCART_SAVE_TYPE_EEPROM_16KBIT;
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_1MBIT: return FLASHCART_SAVE_TYPE_SRAM_1MBIT;
case SAVE_TYPE_FLASHRAM_1MBIT: return FLASHCART_SAVE_TYPE_FLASHRAM_1MBIT;
case SAVE_TYPE_FLASHRAM_PKST2: return FLASHCART_SAVE_TYPE_FLASHRAM_PKST2;
default: return FLASHCART_SAVE_TYPE_NONE;
}
}
/**
* @brief Convert the cart load error code to a human-readable message.
*
* @param err The cart load error code.
* @return char* The error message.
*/
char *cart_load_convert_error_message (cart_load_err_t err) {
switch (err) {
case CART_LOAD_ERR_SAVES_SUBDIR: return "Could not create saves subdirectory";
case CART_LOAD_ERR_ROM: return "Error during ROM loading";
case CART_LOAD_ERR_SAVE: return "Error during save creation or loading";
case CART_LOAD_ERR_EMU_NOT_FOUND: return "Emulator ROM not found";
case CART_LOAD_ERR_EMU: return "Error during emulator ROM loading";
case CART_LOAD_ERR_EMU_ROM: return "Error during emulated ROM loading";
default: return "Unknown cart load error";
case CART_LOAD_OK: return "Cart load OK";
case CART_LOAD_ERR_ROM_LOAD_FAIL: return "Error occured during ROM loading";
case CART_LOAD_ERR_SAVE_LOAD_FAIL: return "Error occured during save loading";
case CART_LOAD_ERR_BOOT_MODE_FAIL: return "Error occured during boot mode setting";
case CART_LOAD_ERR_64DD_PRESENT: return "64DD accessory is connected to the N64";
case CART_LOAD_ERR_64DD_IPL_NOT_FOUND: return "Required 64DD IPL file was not found";
case CART_LOAD_ERR_64DD_IPL_LOAD_FAIL: return "Error occurred during 64DD IPL loading";
case CART_LOAD_ERR_64DD_DISK_LOAD_FAIL: return "Error occurred during 64DD disk loading";
case CART_LOAD_ERR_EMU_NOT_FOUND: return "Required emulator file was not found";
case CART_LOAD_ERR_EMU_LOAD_FAIL: return "Error occurred during emulator ROM loading";
case CART_LOAD_ERR_EMU_ROM_LOAD_FAIL: return "Error occurred during emulated ROM loading";
case CART_LOAD_ERR_CREATE_SAVES_SUBDIR_FAIL: return "Couldn't create saves subdirectory";
case CART_LOAD_ERR_EXP_PAK_NOT_FOUND: return "Mandatory Expansion Pak accessory was not found";
case CART_LOAD_ERR_FUNCTION_NOT_SUPPORTED: return "Your flashcart doesn't support required functionality";
default: return "Unknown error [CART_LOAD]";
}
}
cart_load_err_t cart_load_n64_rom_and_save (menu_t *menu, rom_header_t *header, flashcart_progress_callback_t progress) {
path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name);
bool byte_swap = (header->config_flags == ROM_MID_BIG_ENDIAN);
flashcart_save_type_t save_type = convert_save_type(header);
/**
* @brief Load an N64 ROM and its save file.
*
* @param menu Pointer to the menu structure.
* @param progress Progress callback function.
* @return cart_load_err_t Error code.
*/
cart_load_err_t cart_load_n64_rom_and_save (menu_t *menu, flashcart_progress_callback_t progress) {
path_t *path = path_clone(menu->load.rom_path);
menu->flashcart_error = flashcart_load_rom(path_get(path), byte_swap, progress);
if (menu->flashcart_error != FLASHCART_OK) {
bool byte_swap = (menu->load.rom_info.endianness == ENDIANNESS_BYTE_SWAP);
flashcart_save_type_t save_type = convert_save_type(rom_info_get_save_type(&menu->load.rom_info));
menu->flashcart_err = flashcart_load_rom(path_get(path), byte_swap, progress);
if (menu->flashcart_err != FLASHCART_OK) {
path_free(path);
return CART_LOAD_ERR_ROM;
return CART_LOAD_ERR_ROM_LOAD_FAIL;
}
path_ext_replace(path, "sav");
if (menu->settings.use_saves_folder) {
path_push_subdir(path, SAVES_SUBDIRECTORY);
if ((save_type != FLASHCART_SAVE_TYPE_NONE) && create_saves_subdirectory(menu)) {
if ((save_type != FLASHCART_SAVE_TYPE_NONE) && create_saves_subdirectory(path)) {
path_free(path);
return CART_LOAD_ERR_SAVES_SUBDIR;
return CART_LOAD_ERR_CREATE_SAVES_SUBDIR_FAIL;
}
path_push_subdir(path, SAVES_SUBDIRECTORY);
}
menu->flashcart_error = flashcart_load_save(path_get(path), save_type);
if (menu->flashcart_error != FLASHCART_OK) {
menu->flashcart_err = flashcart_load_save(path_get(path), save_type);
if (menu->flashcart_err != FLASHCART_OK) {
path_free(path);
return CART_LOAD_ERR_SAVE;
return CART_LOAD_ERR_SAVE_LOAD_FAIL;
}
if (menu->settings.rom_fast_reboot_enabled) {
if (!flashcart_has_feature(FLASHCART_FEATURE_ROM_REBOOT_FAST)) {
return CART_LOAD_ERR_FUNCTION_NOT_SUPPORTED;
}
menu->flashcart_err = flashcart_set_next_boot_mode(FLASHCART_REBOOT_MODE_ROM);
if (menu->flashcart_err != FLASHCART_OK) {
path_free(path);
return CART_LOAD_ERR_BOOT_MODE_FAIL;
}
}
path_free(path);
@ -77,8 +142,78 @@ cart_load_err_t cart_load_n64_rom_and_save (menu_t *menu, rom_header_t *header,
return CART_LOAD_OK;
}
/**
* @brief Load the 64DD IPL and disk.
*
* @param menu Pointer to the menu structure.
* @param progress Progress callback function.
* @return cart_load_err_t Error code.
*/
cart_load_err_t cart_load_64dd_ipl_and_disk (menu_t *menu, flashcart_progress_callback_t progress) {
if (!flashcart_has_feature(FLASHCART_FEATURE_64DD)) {
return CART_LOAD_ERR_FUNCTION_NOT_SUPPORTED;
}
if (is_64dd_connected()) {
return CART_LOAD_ERR_64DD_PRESENT;
}
if (!is_memory_expanded()) {
return CART_LOAD_ERR_EXP_PAK_NOT_FOUND;
}
path_t *path = path_init(menu->storage_prefix, DDIPL_LOCATION);
flashcart_disk_parameters_t disk_parameters;
disk_parameters.development_drive = (menu->load.disk_info.region == DISK_REGION_DEVELOPMENT);
disk_parameters.disk_type = menu->load.disk_info.disk_type;
memcpy(disk_parameters.bad_system_area_lbas, menu->load.disk_info.bad_system_area_lbas, sizeof(disk_parameters.bad_system_area_lbas));
memcpy(disk_parameters.defect_tracks, menu->load.disk_info.defect_tracks, sizeof(disk_parameters.defect_tracks));
switch (menu->load.disk_info.region) {
case DISK_REGION_DEVELOPMENT:
path_push(path, "NDXJ0.n64");
break;
case DISK_REGION_JAPANESE:
path_push(path, "NDDJ2.n64");
break;
case DISK_REGION_USA:
path_push(path, "NDDE0.n64");
break;
}
if (!file_exists(path_get(path))) {
path_free(path);
return CART_LOAD_ERR_64DD_IPL_NOT_FOUND;
}
menu->flashcart_err = flashcart_load_64dd_ipl(path_get(path), progress);
if (menu->flashcart_err != FLASHCART_OK) {
path_free(path);
return CART_LOAD_ERR_64DD_IPL_LOAD_FAIL;
}
path_free(path);
menu->flashcart_err = flashcart_load_64dd_disk(path_get(menu->load.disk_path), &disk_parameters);
if (menu->flashcart_err != FLASHCART_OK) {
return CART_LOAD_ERR_64DD_DISK_LOAD_FAIL;
}
return CART_LOAD_OK;
}
/**
* @brief Load an emulator and its ROM.
*
* @param menu Pointer to the menu structure.
* @param emu_type The type of emulator to load.
* @param progress Progress callback function.
* @return cart_load_err_t Error code.
*/
cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type, flashcart_progress_callback_t progress) {
path_t *path = path_init("sd:/", EMU_LOCATION);
path_t *path = path_init(menu->storage_prefix, EMU_LOCATION);
flashcart_save_type_t save_type = FLASHCART_SAVE_TYPE_NONE;
uint32_t emulated_rom_offset = 0x200000;
uint32_t emulated_file_offset = 0;
@ -90,20 +225,23 @@ cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type,
break;
case CART_LOAD_EMU_TYPE_SNES:
path_push(path, "sodium64.z64");
save_type = FLASHCART_SAVE_TYPE_SRAM;
emulated_rom_offset = 0x104000;
save_type = FLASHCART_SAVE_TYPE_SRAM_256KBIT;
break;
case CART_LOAD_EMU_TYPE_GAMEBOY:
path_push(path, "gb.v64");
save_type = FLASHCART_SAVE_TYPE_FLASHRAM;
save_type = FLASHCART_SAVE_TYPE_SRAM_BANKED; //FLASHCART_SAVE_TYPE_FLASHRAM_NONCOMPLIANT;
break;
case CART_LOAD_EMU_TYPE_GAMEBOY_COLOR:
path_push(path, "gbc.v64");
save_type = FLASHCART_SAVE_TYPE_FLASHRAM;
save_type = FLASHCART_SAVE_TYPE_SRAM_BANKED; //FLASHCART_SAVE_TYPE_FLASHRAM_NONCOMPLIANT;
break;
case CART_LOAD_EMU_TYPE_SEGA_GENERIC_8BIT:
path_push(path, "TotalSMS.z64");
save_type = FLASHCART_SAVE_TYPE_SRAM;
path_push(path, "smsPlus64.z64");
save_type = FLASHCART_SAVE_TYPE_NONE;
break;
case CART_LOAD_EMU_TYPE_FAIRCHILD_CHANNELF:
path_push(path, "Press-F.z64");
save_type = FLASHCART_SAVE_TYPE_NONE;
break;
}
@ -112,10 +250,10 @@ cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type,
return CART_LOAD_ERR_EMU_NOT_FOUND;
}
menu->flashcart_error = flashcart_load_rom(path_get(path), false, progress);
if (menu->flashcart_error != FLASHCART_OK) {
menu->flashcart_err = flashcart_load_rom(path_get(path), false, progress);
if (menu->flashcart_err != FLASHCART_OK) {
path_free(path);
return CART_LOAD_ERR_EMU;
return CART_LOAD_ERR_EMU_LOAD_FAIL;
}
path_free(path);
@ -124,31 +262,32 @@ cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type,
switch (emu_type) {
case CART_LOAD_EMU_TYPE_SNES:
// NOTE: The emulator expects the header to be removed from the ROM being uploaded.
emulated_file_offset = ((file_get_size(path_get(path)) & 0x3FF) == 0x200) ? 0x200 : 0;
break;
default:
break;
}
menu->flashcart_error = flashcart_load_file(path_get(path), emulated_rom_offset, emulated_file_offset);
if (menu->flashcart_error != FLASHCART_OK) {
menu->flashcart_err = flashcart_load_file(path_get(path), emulated_rom_offset, emulated_file_offset);
if (menu->flashcart_err != FLASHCART_OK) {
path_free(path);
return CART_LOAD_ERR_EMU_ROM;
return CART_LOAD_ERR_EMU_ROM_LOAD_FAIL;
}
path_ext_replace(path, "sav");
if (menu->settings.use_saves_folder) {
path_push_subdir(path, SAVES_SUBDIRECTORY);
if ((save_type != FLASHCART_SAVE_TYPE_NONE) && create_saves_subdirectory(menu)) {
if ((save_type != FLASHCART_SAVE_TYPE_NONE) && create_saves_subdirectory(path)) {
path_free(path);
return CART_LOAD_ERR_SAVES_SUBDIR;
return CART_LOAD_ERR_CREATE_SAVES_SUBDIR_FAIL;
}
path_push_subdir(path, SAVES_SUBDIRECTORY);
}
menu->flashcart_error = flashcart_load_save(path_get(path), save_type);
if (menu->flashcart_error != FLASHCART_OK) {
menu->flashcart_err = flashcart_load_save(path_get(path), save_type);
if (menu->flashcart_err != FLASHCART_OK) {
path_free(path);
return CART_LOAD_ERR_SAVE;
return CART_LOAD_ERR_SAVE_LOAD_FAIL;
}
path_free(path);

View File

@ -7,34 +7,93 @@
#ifndef CART_LOAD_H__
#define CART_LOAD_H__
#include "disk_info.h"
#include "flashcart/flashcart.h"
#include "menu_state.h"
#include "rom_database.h"
#include "rom_info.h"
/** @brief Cart load state enumeration. */
typedef enum {
/** @brief Returned no error. */
CART_LOAD_OK,
CART_LOAD_ERR_SAVES_SUBDIR,
CART_LOAD_ERR_ROM,
CART_LOAD_ERR_SAVE,
/** @brief Failed to load the ROM correctly. */
CART_LOAD_ERR_ROM_LOAD_FAIL,
/** @brief Failed to load the save correctly. */
CART_LOAD_ERR_SAVE_LOAD_FAIL,
/** @brief Failed to set the next boot mode. */
CART_LOAD_ERR_BOOT_MODE_FAIL,
/** @brief The 64DD is available for use. */
CART_LOAD_ERR_64DD_PRESENT,
/** @brief Failed to find the 64DD IPL (BIOS) file. */
CART_LOAD_ERR_64DD_IPL_NOT_FOUND,
/** @brief Failed to load the 64DD IPL (BIOS) file. */
CART_LOAD_ERR_64DD_IPL_LOAD_FAIL,
/** @brief Failed to find the 64DD disk. */
CART_LOAD_ERR_64DD_DISK_LOAD_FAIL,
/** @brief Failed to find the emulator required. */
CART_LOAD_ERR_EMU_NOT_FOUND,
CART_LOAD_ERR_EMU,
CART_LOAD_ERR_EMU_ROM,
/** @brief Failed to load the emulator required. */
CART_LOAD_ERR_EMU_LOAD_FAIL,
/** @brief Failed to load the emulator ROM. */
CART_LOAD_ERR_EMU_ROM_LOAD_FAIL,
/** @brief Failed to create the save sub-directory. */
CART_LOAD_ERR_CREATE_SAVES_SUBDIR_FAIL,
/** @brief There was not enough system memory available (expected an Expansion PAK). */
CART_LOAD_ERR_EXP_PAK_NOT_FOUND,
/** @brief An unexpected response. */
CART_LOAD_ERR_FUNCTION_NOT_SUPPORTED,
} cart_load_err_t;
/** @brief Cart load type enumeration */
typedef enum {
/** @brief The ROM is designed for a Nintendo Entertainment System or Famicom. */
CART_LOAD_EMU_TYPE_NES,
/** @brief The ROM is designed for a Super Nintendo Entertainment System or Super Famicom. */
CART_LOAD_EMU_TYPE_SNES,
/** @brief The ROM is designed for a Nintendo Gameboy. */
CART_LOAD_EMU_TYPE_GAMEBOY,
/** @brief The ROM is designed for a Nintendo Gameboy Color. */
CART_LOAD_EMU_TYPE_GAMEBOY_COLOR,
/** @brief The ROM is designed for a Sega 8Bit system (Game Gear or Master System). */
CART_LOAD_EMU_TYPE_SEGA_GENERIC_8BIT,
/** @brief The ROM is designed for a Fairchild Channel F system. */
CART_LOAD_EMU_TYPE_FAIRCHILD_CHANNELF,
} cart_load_emu_type_t;
/**
* @brief Convert a cart load error code to a human-readable error message.
*
* @param err The cart load error code.
* @return char* The human-readable error message.
*/
char *cart_load_convert_error_message(cart_load_err_t err);
char *cart_load_convert_error_message (cart_load_err_t err);
cart_load_err_t cart_load_n64_rom_and_save (menu_t *menu, rom_header_t *header, flashcart_progress_callback_t progress);
cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type, flashcart_progress_callback_t progress);
/**
* @brief Load an N64 ROM and its save data.
*
* @param menu Pointer to the menu structure.
* @param progress Callback function for progress updates.
* @return cart_load_err_t Error code.
*/
cart_load_err_t cart_load_n64_rom_and_save(menu_t *menu, flashcart_progress_callback_t progress);
/**
* @brief Load the 64DD IPL (BIOS) and disk.
*
* @param menu Pointer to the menu structure.
* @param progress Callback function for progress updates.
* @return cart_load_err_t Error code.
*/
cart_load_err_t cart_load_64dd_ipl_and_disk(menu_t *menu, flashcart_progress_callback_t progress);
#endif
/**
* @brief Load an emulator and its ROM.
*
* @param menu Pointer to the menu structure.
* @param emu_type The type of emulator to load.
* @param progress Callback function for progress updates.
* @return cart_load_err_t Error code.
*/
cart_load_err_t cart_load_emulator(menu_t *menu, cart_load_emu_type_t emu_type, flashcart_progress_callback_t progress);
#endif /* CART_LOAD_H__ */

View File

@ -1,69 +0,0 @@
/**
* @file components.h
* @brief Menu Components
* @ingroup menu
*/
#ifndef COMPONENTS_H__
#define COMPONENTS_H__
#include <libdragon.h>
#include "menu_state.h"
/**
* @addtogroup
* @{ menu_components
*/
void component_box_draw (int x0, int y0, int x1, int y1, color_t color);
void component_border_draw (int x0, int y0, int x1, int y1);
void component_layout_draw (void);
void component_progressbar_draw (int x0, int y0, int x1, int y1, float progress);
void component_seekbar_draw (float progress);
void component_loader_draw (float position);
void component_scrollbar_draw (int x, int y, int width, int height, int position, int items, int visible_items);
void component_file_list_scrollbar_draw (int position, int items, int visible_items);
void component_dialog_draw (int width, int height);
void component_messagebox_draw (char *fmt, ...);
void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...);
void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...);
void component_background_init (char *cache_location);
void component_background_free (void);
void component_background_replace_image (surface_t *image);
void component_background_draw (void);
void component_file_list_draw (entry_t *list, int entries, int selected);
typedef struct {
int count;
int selected;
bool hide_pending;
struct {
const char *text;
void (*action) (menu_t *menu);
} list[];
} component_context_menu_t;
#define COMPONENT_CONTEXT_MENU_LIST_END { .text = NULL }
void component_context_menu_init (component_context_menu_t *cm);
void component_context_menu_show (component_context_menu_t *cm);
bool component_context_menu_process (menu_t *menu, component_context_menu_t *cm);
void component_context_menu_draw (component_context_menu_t *cm);
typedef struct {
bool loading;
surface_t *image;
} component_boxart_t;
component_boxart_t *component_boxart_init (uint16_t id);
void component_boxart_free (component_boxart_t *b);
void component_boxart_draw (component_boxart_t *b);
/** @} */ /* menu_components */
#endif

View File

@ -1,70 +0,0 @@
#include <stdlib.h>
#include "../components.h"
#include "../path.h"
#include "../png_decoder.h"
#include "constants.h"
#define BOXART_DIRECTORY "sd:/menu/boxart"
static void png_decoder_callback (png_err_t err, surface_t *decoded_image, void *callback_data) {
component_boxart_t *b = (component_boxart_t *) (callback_data);
b->loading = false;
b->image = decoded_image;
}
component_boxart_t *component_boxart_init (uint16_t id) {
component_boxart_t *b = calloc(1, sizeof(component_boxart_t));
if (b) {
b->loading = true;
char *path = alloca(strlen(BOXART_DIRECTORY) + 1 + 6 + 1);
sprintf(path, "%s/%c%c.png", BOXART_DIRECTORY, ((id >> 8) & 0xFF), (id & 0xFF));
if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) {
free(b);
b = NULL;
}
}
return b;
}
void component_boxart_free (component_boxart_t *b) {
if (b) {
if (b->loading) {
png_decoder_abort();
}
if (b->image) {
surface_free(b->image);
free(b->image);
}
free(b);
}
}
void component_boxart_draw (component_boxart_t *b) {
if (b && b->image && b->image->width == BOXART_WIDTH && b->image->height == BOXART_HEIGHT) {
rdpq_mode_push();
rdpq_set_mode_copy(false);
rdpq_tex_blit(
b->image,
BOXART_X,
BOXART_Y,
NULL
);
rdpq_mode_pop();
} else {
component_box_draw(
BOXART_X,
BOXART_Y,
BOXART_X + BOXART_WIDTH,
BOXART_Y + BOXART_HEIGHT,
BOXART_LOADING_COLOR
);
}
}

View File

@ -1,182 +0,0 @@
#include <stdarg.h>
#include "../components.h"
#include "../fonts.h"
#include "constants.h"
void component_box_draw (int x0, int y0, int x1, int y1, color_t color) {
rdpq_mode_push();
rdpq_set_mode_fill(color);
rdpq_fill_rectangle(x0, y0, x1, y1);
rdpq_mode_pop();
}
void component_border_draw (int x0, int y0, int x1, int y1) {
rdpq_mode_push();
rdpq_set_mode_fill(BORDER_COLOR);
rdpq_fill_rectangle(x0 - BORDER_THICKNESS, y0 - BORDER_THICKNESS, x1 + BORDER_THICKNESS, y0);
rdpq_fill_rectangle(x0 - BORDER_THICKNESS, y1, x1 + BORDER_THICKNESS, y1 + BORDER_THICKNESS);
rdpq_fill_rectangle(x0 - BORDER_THICKNESS, y0, x0, y1);
rdpq_fill_rectangle(x1, y0, x1 + BORDER_THICKNESS, y1);
rdpq_mode_pop();
}
void component_layout_draw (void) {
component_border_draw(
VISIBLE_AREA_X0,
VISIBLE_AREA_Y0,
VISIBLE_AREA_X1,
VISIBLE_AREA_Y1
);
component_box_draw(
VISIBLE_AREA_X0,
LAYOUT_ACTIONS_SEPARATOR_Y,
VISIBLE_AREA_X1,
LAYOUT_ACTIONS_SEPARATOR_Y + BORDER_THICKNESS,
BORDER_COLOR
);
}
void component_progressbar_draw (int x0, int y0, int x1, int y1, float progress) {
float progress_width = progress * (x1 - x0);
component_box_draw(x0, y0, x0 + progress_width, y1, PROGRESSBAR_DONE_COLOR);
component_box_draw(x0 + progress_width, y0, x1, y1, PROGRESSBAR_BG_COLOR);
}
void component_seekbar_draw (float position) {
int x0 = SEEKBAR_X;
int y0 = SEEKBAR_Y;
int x1 = SEEKBAR_X + SEEKBAR_WIDTH;
int y1 = SEEKBAR_Y + SEEKBAR_HEIGHT;
component_border_draw(x0, y0, x1, y1);
component_progressbar_draw(x0, y0, x1, y1, position);
}
void component_loader_draw (float progress) {
int x0 = LOADER_X;
int y0 = LOADER_Y;
int x1 = LOADER_X + LOADER_WIDTH;
int y1 = LOADER_Y + LOADER_HEIGHT;
component_border_draw(x0, y0, x1, y1);
component_progressbar_draw(x0, y0, x1, y1, progress);
}
void component_scrollbar_draw (int x, int y, int width, int height, int position, int items, int visible_items) {
if (items <= 1 || items <= visible_items) {
component_box_draw(x, y, x + width, y + height, SCROLLBAR_INACTIVE_COLOR);
} else {
int scroll_height = (int) ((visible_items / (float) (items)) * height);
float scroll_position = ((position / (float) (items - 1)) * (height - scroll_height));
component_box_draw(x, y, x + width, y + height, SCROLLBAR_BG_COLOR);
component_box_draw(x, y + scroll_position, x + width, y + scroll_position + scroll_height, SCROLLBAR_POSITION_COLOR);
}
}
void component_file_list_scrollbar_draw (int position, int items, int visible_items) {
component_scrollbar_draw(
FILE_LIST_SCROLLBAR_X,
FILE_LIST_SCROLLBAR_Y,
FILE_LIST_SCROLLBAR_WIDTH,
FILE_LIST_SCROLLBAR_HEIGHT,
position,
items,
visible_items
);
}
void component_dialog_draw (int width, int height) {
int x0 = DISPLAY_CENTER_X - (width / 2);
int y0 = DISPLAY_CENTER_Y - (height / 2);
int x1 = DISPLAY_CENTER_X + (width / 2);
int y1 = DISPLAY_CENTER_Y + (height / 2);
component_border_draw(x0, y0, x1, y1);
component_box_draw(x0, y0, x1, y1, DIALOG_BG_COLOR);
}
void component_messagebox_draw (char *fmt, ...) {
char buffer[512];
size_t nbytes = sizeof(buffer);
va_list va;
va_start(va, fmt);
char *formatted = vasnprintf(buffer, &nbytes, fmt, va);
va_end(va);
int paragraph_nbytes = nbytes;
rdpq_paragraph_t *paragraph = rdpq_paragraph_build(&(rdpq_textparms_t) {
.width = MESSAGEBOX_MAX_WIDTH,
.height = VISIBLE_AREA_HEIGHT,
.align = ALIGN_CENTER,
.valign = VALIGN_CENTER,
.wrap = WRAP_WORD
}, FNT_DEFAULT, formatted, &paragraph_nbytes);
component_dialog_draw(
paragraph->bbox[2] - paragraph->bbox[0] + MESSAGEBOX_MARGIN,
paragraph->bbox[3] - paragraph->bbox[1] + MESSAGEBOX_MARGIN
);
rdpq_paragraph_render(paragraph, DISPLAY_CENTER_X - (MESSAGEBOX_MAX_WIDTH / 2), VISIBLE_AREA_Y0);
rdpq_paragraph_free(paragraph);
}
void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...) {
char buffer[1024];
size_t nbytes = sizeof(buffer);
va_list va;
va_start(va, fmt);
char *formatted = vasnprintf(buffer, &nbytes, fmt, va);
va_end(va);
rdpq_text_printn(
&(rdpq_textparms_t) {
.width = VISIBLE_AREA_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2),
.height = LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT - (TEXT_MARGIN_VERTICAL * 2),
.align = align,
.valign = valign,
.wrap = WRAP_ELLIPSES,
},
FNT_DEFAULT,
VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL,
VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL,
formatted,
nbytes
);
}
void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...) {
char buffer[256];
size_t nbytes = sizeof(buffer);
va_list va;
va_start(va, fmt);
char *formatted = vasnprintf(buffer, &nbytes, fmt, va);
va_end(va);
rdpq_text_printn(
&(rdpq_textparms_t) {
.width = VISIBLE_AREA_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2),
.height = VISIBLE_AREA_Y1 - LAYOUT_ACTIONS_SEPARATOR_Y - BORDER_THICKNESS - (TEXT_MARGIN_VERTICAL * 2),
.align = align,
.valign = valign,
.wrap = WRAP_ELLIPSES,
},
FNT_DEFAULT,
VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL,
LAYOUT_ACTIONS_SEPARATOR_Y + BORDER_THICKNESS + TEXT_MARGIN_VERTICAL,
formatted,
nbytes
);
}

Some files were not shown because too many files have changed in this diff Show More