Optimize boxart image load (#130)

<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Improves boxart image loading by using directory file paths.
It also adds matching by full game ID's (for compatibility), but notes
in the readme that it is slow.

## 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 -->
Loading boxart was slow in certain circumstances.

## 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): -->

## 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**
- Introduced a new image type enumeration for boxart components,
enhancing image management.
- Updated boxart initialization to allow for specific image views,
improving flexibility in image retrieval.
- Enhanced instructions for setting up boxart images with clearer
directory structures and simplified naming conventions.

- **Bug Fixes**
- Improved the logic for loading boxart images, enabling better fallback
options in case of missing files.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Robin Jones 2024-09-28 20:47:53 +01:00 committed by GitHub
parent d87dfb4ab2
commit 269a8ae94d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 151 additions and 32 deletions

View File

@ -49,22 +49,44 @@ An open source menu for N64 flashcarts.
## Experimental features ## Experimental features
These features are subject to change: These features are subject to change:
### ROM Boxart
To use boxart, place PNG files in the `/menu/boxart` folder on the SD card with the following dimensions:
* Standard covers: 158x112
* 64DD covers: 129x112
* Japanese covers: 112x158
Each file must be named according to the 2 letter ROM ID, or 3 letter ROM ID including media type. ### GamePak sprites
i.e. for GoldenEye 2 letters, this would be `GE.png`. To use N64 `GamePak` sprites, place `PNG` files within the `sd:/menu/boxart/` folder.
i.e. for GoldenEye 3 letters, this would be `NGE.png`.
You can download these boxart packs:
[American Boxart](https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w)
[European Boxart](https://mega.nz/file/O7AjDbRJ#VnVU10dq8HQvBUQptppI6PAcQMb8-Zembqav8WtAQ_M) #### Supported sprites
These must be `PNG` files that use the following dimensions:
* Standard N64 GamePak boxart sprites: 158x112
* Japanese N64 GamePak boxart sprites: 112x158
* 64DD boxart sprites: 129x112
[64DD Boxart](https://mega.nz/file/O3JzwD7B#BYl1aV-pbrJ-MxWUbM_K0yGVIRbmSoxJJZqQInRzZyM) They will be loaded by directories using each character of the full 4 character Game Code (as identified in the menus 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 letter IDs instead:
i.e. for GoldenEye, this would be `sd:/menu/boxart/N/G/E/boxart_front.png`.
**Note1:** Excluding the region ID may show the wrong boxart.
**Note2:** For future support, boxart sprites should also include: `boxart_back.png`, `boxart_top.png`, `boxart_bottom.png`, `boxart_left.png`, `boxart_right.png`.
#### Compatibilty mode
If you cannot yet satisfy the correct boxart layout, The menu still has **deprecated** support for filenames containing the Game ID.
**Note:** This will add a noticeable delay for displaying parts of the menu.
Each file must be named according to the 2,3 or 4 letter GamePak ID (matched in this order).
i.e.
* for GoldenEye 4 letters, this would be `sd:/menu/boxart/NGEE.png` and/or `sd:/menu/boxart/NGEP.png`.
* for GoldenEye 3 letters, this would be `sd:/menu/boxart/NGE.png`.
* for GoldenEye 2 letters, this would be `sd:/menu/boxart/GE.png`.
As a starting point, here are some links to boxart packs:
* [American GamePak Boxart](https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w)
* [European GamePak Boxart](https://mega.nz/file/O7AjDbRJ#VnVU10dq8HQvBUQptppI6PAcQMb8-Zembqav8WtAQ_M)
* [64DD Boxart](https://mega.nz/file/O3JzwD7B#BYl1aV-pbrJ-MxWUbM_K0yGVIRbmSoxJJZqQInRzZyM)
### Menu Settings ### Menu Settings

View File

@ -11,6 +11,41 @@
#include <libdragon.h> #include <libdragon.h>
#include "menu_state.h" #include "menu_state.h"
/** @brief File image Enumeration. */
typedef enum {
/** @brief Boxart image from the front */
IMAGE_BOXART_FRONT,
/** @brief Boxart image from the back */
IMAGE_BOXART_BACK,
/** @brief Boxart image from the top */
IMAGE_BOXART_TOP,
/** @brief Boxart image from the bottom */
IMAGE_BOXART_BOTTOM,
/** @brief Boxart image from the left side */
IMAGE_BOXART_LEFT,
/** @brief Boxart image from the right side */
IMAGE_BOXART_RIGHT,
/** @brief GamePak image from the front */
IMAGE_GAMEPAK_FRONT,
/** @brief GamePak image from the back */
IMAGE_GAMEPAK_BACK,
/** @brief File image thumbnail */
IMAGE_THUMBNAIL,
/** @brief List end marker */
IMAGE_TYPE_END
} file_image_type_t;
/** /**
* @addtogroup * @addtogroup
@ -64,7 +99,7 @@ typedef struct {
surface_t *image; surface_t *image;
} component_boxart_t; } component_boxart_t;
component_boxart_t *component_boxart_init (const char *storage_prefix, char *game_code); component_boxart_t *component_boxart_init (const char *storage_prefix, char *game_code, file_image_type_t current_image_view);
void component_boxart_free (component_boxart_t *b); void component_boxart_free (component_boxart_t *b);
void component_boxart_draw (component_boxart_t *b); void component_boxart_draw (component_boxart_t *b);

View File

@ -17,9 +17,9 @@ static void png_decoder_callback (png_err_t err, surface_t *decoded_image, void
} }
component_boxart_t *component_boxart_init (const char *storage_prefix, char *game_code) { component_boxart_t *component_boxart_init (const char *storage_prefix, char *game_code, file_image_type_t current_image_view) {
component_boxart_t *b; component_boxart_t *b;
char file_name[8]; char boxart_id_path[8];
if ((b = calloc(1, sizeof(component_boxart_t))) == NULL) { if ((b = calloc(1, sizeof(component_boxart_t))) == NULL) {
return NULL; return NULL;
@ -29,22 +29,84 @@ component_boxart_t *component_boxart_init (const char *storage_prefix, char *gam
path_t *path = path_init(storage_prefix, BOXART_DIRECTORY); path_t *path = path_init(storage_prefix, BOXART_DIRECTORY);
sprintf(file_name, "%.3s.png", game_code); sprintf(boxart_id_path, "%c/%c/%c/%c", game_code[0], game_code[1], game_code[2], game_code[3]);
path_push(path, file_name); path_push(path, boxart_id_path);
if (png_decoder_start(path_get(path), BOXART_WIDTH_MAX, BOXART_HEIGHT_MAX, png_decoder_callback, b) == PNG_OK) {
path_free(path);
return b;
}
path_pop(path);
// TODO: This is bad, we should only check for 3 letter codes if (!directory_exists(path_get(path))) { // Allow boxart to not specify the region code.
sprintf(file_name, "%.2s.png", game_code + 1); path_pop(path);
path_push(path, file_name);
if (png_decoder_start(path_get(path), BOXART_WIDTH_MAX, BOXART_HEIGHT_MAX, png_decoder_callback, b) == PNG_OK) {
path_free(path);
return b;
} }
if (directory_exists(path_get(path))) {
switch (current_image_view) {
case IMAGE_GAMEPAK_FRONT:
path_push(path, "gamepak_front.png");
case IMAGE_GAMEPAK_BACK:
path_push(path, "gamepak_back.png");
case IMAGE_BOXART_BACK:
path_push(path, "boxart_back.png");
case IMAGE_BOXART_LEFT:
path_push(path, "boxart_left.png");
case IMAGE_BOXART_RIGHT:
path_push(path, "boxart_right.png");
case IMAGE_BOXART_BOTTOM:
path_push(path, "boxart_bottom.png");
case IMAGE_BOXART_TOP:
path_push(path, "boxart_top.png");
default:
path_push(path, "boxart_front.png");
}
if (file_exists(path_get(path))) {
if (png_decoder_start(path_get(path), BOXART_WIDTH_MAX, BOXART_HEIGHT_MAX, png_decoder_callback, b) == PNG_OK) {
path_free(path);
return b;
}
}
}
else { // compatibility mode
char file_name[8];
// reset the directory path used for boxart.
path = path_init(storage_prefix, BOXART_DIRECTORY);
sprintf(file_name, "%c%c%c%c.png", game_code[0], game_code[1], game_code[2], game_code[3]);
path_push(path, file_name);
if (file_exists(path_get(path))) {
if (png_decoder_start(path_get(path), BOXART_WIDTH_MAX, BOXART_HEIGHT_MAX, png_decoder_callback, b) == PNG_OK) {
path_free(path);
return b;
}
}
path_pop(path);
sprintf(file_name, "%c%c%c.png", game_code[0], game_code[1], game_code[2]);
path_push(path, file_name);
if (file_exists(path_get(path))) {
if (file_exists(path_get(path))) {
if (png_decoder_start(path_get(path), BOXART_WIDTH_MAX, BOXART_HEIGHT_MAX, png_decoder_callback, b) == PNG_OK) {
path_free(path);
return b;
}
}
}
else {
path_pop(path);
sprintf(file_name, "%c%c.png", game_code[1], game_code[2]);
path_push(path, file_name);
if (file_exists(path_get(path))) {
if (png_decoder_start(path_get(path), BOXART_WIDTH_MAX, BOXART_HEIGHT_MAX, png_decoder_callback, b) == PNG_OK) {
path_free(path);
return b;
}
}
}
}
// TODO: return default image.
path_free(path); path_free(path);
free(b); free(b);

View File

@ -170,7 +170,7 @@ void view_load_disk_init (menu_t *menu) {
menu_show_error(menu, convert_error_message(err)); menu_show_error(menu, convert_error_message(err));
} }
boxart = component_boxart_init(menu->storage_prefix, menu->load.disk_info.id); boxart = component_boxart_init(menu->storage_prefix, menu->load.disk_info.id, IMAGE_BOXART_FRONT);
} }
void view_load_disk_display (menu_t *menu, surface_t *display) { void view_load_disk_display (menu_t *menu, surface_t *display) {

View File

@ -355,7 +355,7 @@ void view_load_rom_init (menu_t *menu) {
return; return;
} }
boxart = component_boxart_init(menu->storage_prefix, menu->load.rom_info.game_code); boxart = component_boxart_init(menu->storage_prefix, menu->load.rom_info.game_code, IMAGE_BOXART_FRONT);
component_context_menu_init(&options_context_menu); component_context_menu_init(&options_context_menu);
} }