From 8e8f77e55bec5bc9ce7ca64c0a8de2d5b929fdc6 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Thu, 3 Aug 2023 17:18:55 +0200 Subject: [PATCH] Complete rewrite of menu drawing + code cleanup + ROM cold boot fix Uses new `rdpq_text` API for font drawing --- Makefile | 12 +- libdragon | 2 +- src/boot/boot.c | 17 +- src/boot/boot.h | 3 - src/boot/ipl2.S | 17 -- src/boot/reboot.S | 57 ++++ src/flashcart/flashcart.c | 4 +- src/flashcart/flashcart.h | 6 +- src/flashcart/sc64/sc64.c | 26 +- src/menu/assets.c | 54 ---- src/menu/assets.h | 27 -- src/menu/components.h | 52 ++++ src/menu/components/background.c | 140 +++++----- src/menu/components/boxart.c | 70 +++++ src/menu/components/common.c | 182 ++++++++++++ src/menu/components/components.h | 32 --- src/menu/components/constants.h | 83 ++++++ src/menu/components/file_list.c | 145 ++++++++++ src/menu/fonts.c | 30 ++ src/menu/fonts.h | 27 ++ src/menu/menu.c | 26 +- src/menu/menu_state.h | 7 +- src/menu/mp3_player.c | 2 +- src/menu/mp3_player.h | 2 +- src/menu/png_decoder.c | 68 +++-- src/menu/png_decoder.h | 7 +- src/menu/views/browser.c | 197 ++++--------- src/menu/views/credits.c | 77 +++-- src/menu/views/error.c | 28 +- src/menu/views/fault.c | 42 ++- src/menu/views/file_info.c | 403 +++++---------------------- src/menu/views/fragments/fragments.c | 108 ------- src/menu/views/fragments/fragments.h | 59 ---- src/menu/views/fragments/widgets.c | 55 ---- src/menu/views/image_viewer.c | 84 ++++-- src/menu/views/load.c | 200 +++++++------ src/menu/views/music_player.c | 144 +++++----- src/menu/views/startup.c | 16 +- src/menu/views/system_info.c | 123 ++++---- src/menu/views/views.h | 7 +- src/utils/str_utils.h | 33 --- 41 files changed, 1331 insertions(+), 1343 deletions(-) delete mode 100644 src/boot/ipl2.S create mode 100644 src/boot/reboot.S delete mode 100644 src/menu/assets.c delete mode 100644 src/menu/assets.h create mode 100644 src/menu/components.h create mode 100644 src/menu/components/boxart.c create mode 100644 src/menu/components/common.c delete mode 100644 src/menu/components/components.h create mode 100644 src/menu/components/constants.h create mode 100644 src/menu/components/file_list.c create mode 100644 src/menu/fonts.c create mode 100644 src/menu/fonts.h delete mode 100644 src/menu/views/fragments/fragments.c delete mode 100644 src/menu/views/fragments/fragments.h delete mode 100644 src/menu/views/fragments/widgets.c delete mode 100644 src/utils/str_utils.h diff --git a/Makefile b/Makefile index 28fc2fa7..f2b81c5d 100644 --- a/Makefile +++ b/Makefile @@ -10,13 +10,12 @@ OUTPUT_DIR = output include $(N64_INST)/include/n64.mk N64_CFLAGS += -iquote $(SOURCE_DIR) -I $(SOURCE_DIR)/libs $(FLAGS) -N64_LDFLAGS += --wrap asset_load SRCS = \ main.c \ boot/boot.c \ boot/crc32.c \ - boot/ipl2.S \ + boot/reboot.S \ flashcart/flashcart_utils.c \ flashcart/flashcart.c \ flashcart/sc64/sc64_internal.c \ @@ -28,8 +27,11 @@ SRCS = \ libs/miniz/miniz_zip.c \ libs/miniz/miniz.c \ menu/actions.c \ - menu/assets.c \ menu/components/background.c \ + menu/components/boxart.c \ + menu/components/common.c \ + menu/components/file_list.c \ + menu/fonts.c \ menu/menu.c \ menu/mp3_player.c \ menu/path.c \ @@ -41,8 +43,6 @@ SRCS = \ menu/views/error.c \ menu/views/fault.c \ menu/views/file_info.c \ - menu/views/fragments/fragments.c \ - menu/views/fragments/widgets.c \ menu/views/image_viewer.c \ menu/views/load.c \ menu/views/music_player.c \ @@ -59,7 +59,7 @@ SPNG_OBJS = $(filter $(BUILD_DIR)/libs/libspng/%.o,$(OBJS)) $(MINIZ_OBJS): N64_CFLAGS+=-DMINIZ_NO_TIME -fcompare-debug-second $(SPNG_OBJS): N64_CFLAGS+=-isystem $(SOURCE_DIR)/libs/miniz -DSPNG_USE_MINIZ -fcompare-debug-second -$(BUILD_DIR)/FiraMono-Bold.o: MKFONT_FLAGS+=-c 0 --size 16 -r 20-7F -r 2000-206F +$(BUILD_DIR)/FiraMono-Bold.o: MKFONT_FLAGS+=-c 0 --size 16 -r 20-7F -r 2026-2026 --ellipsis 2026,1 $(BUILD_DIR)/%.o: $(ASSETS_DIR)/%.ttf @echo " [FONT] $@" diff --git a/libdragon b/libdragon index 1c35500e..4409fe77 160000 --- a/libdragon +++ b/libdragon @@ -1 +1 @@ -Subproject commit 1c35500e787b3bd2634e1cd0870b246ddffddfe2 +Subproject commit 4409fe77e208aaef77800b684602d4ea5a74da0b diff --git a/src/boot/boot.c b/src/boot/boot.c index 22016ba8..969bd211 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -10,7 +10,9 @@ #define C0_STATUS_CU1 (1 << 29) -extern uint32_t ipl2 __attribute__((section(".data"))); +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 { @@ -99,11 +101,12 @@ void boot (boot_params_t *params) { while (cpu_io_read(&SP->SR) & SP_SR_DMA_BUSY); - uint32_t *ipl2_src = &ipl2; - io32_t *ipl2_dst = SP_MEM->IMEM; + uint32_t *reboot_src = &reboot_start; + io32_t *reboot_dst = SP_MEM->IMEM; + size_t reboot_instructions = (size_t) (&reboot_size) / sizeof(uint32_t); - for (int i = 0; i < 8; i++) { - cpu_io_write(&ipl2_dst[i], ipl2_src[i]); + for (int i = 0; i < reboot_instructions; i++) { + cpu_io_write(&reboot_dst[i], reboot_src[i]); } cpu_io_write(&PI->DOM[0].LAT, 0xFF); @@ -138,10 +141,10 @@ void boot (boot_params_t *params) { register uint32_t version asm ("s7"); void *stack_pointer; - entry_point = (void (*)(void)) UNCACHED(&SP_MEM->DMEM[16]); + entry_point = (void (*)(void)) UNCACHED(&SP_MEM->IMEM[(int) (&reboot_entry_offset)]); boot_device = (params->device_type & 0x01); tv_type = (params->tv_type & 0x03); - reset_type = (params->reset_type & 0x01); + reset_type = BOOT_RESET_TYPE_COLD; cic_seed = (params->cic_seed & 0xFF); version = (params->tv_type == BOOT_TV_TYPE_PAL) ? 6 : 1; stack_pointer = (void *) UNCACHED(&SP_MEM->IMEM[1020]); diff --git a/src/boot/boot.h b/src/boot/boot.h index fb4deafb..d60b5725 100644 --- a/src/boot/boot.h +++ b/src/boot/boot.h @@ -24,7 +24,6 @@ typedef enum { BOOT_RESET_TYPE_NMI = 1, } boot_reset_type_t; - /** @brief TV type enumeration */ typedef enum { BOOT_TV_TYPE_PAL = 0, @@ -33,11 +32,9 @@ typedef enum { BOOT_TV_TYPE_PASSTHROUGH = 3, } boot_tv_type_t; - /** @brief Boot Parameters Structure */ typedef struct { boot_device_type_t device_type; - boot_reset_type_t reset_type; boot_tv_type_t tv_type; uint8_t cic_seed; bool detect_cic_seed; diff --git a/src/boot/ipl2.S b/src/boot/ipl2.S deleted file mode 100644 index 23f9f6af..00000000 --- a/src/boot/ipl2.S +++ /dev/null @@ -1,17 +0,0 @@ -.set noat -.set noreorder - - -.section .text.ipl2, "ax", %progbits -.type ipl2, %object -ipl2: - .global ipl2 - lui $t5, 0xBFC0 -1: - lw $t0, 0x7FC($t5) - addiu $t5, $t5, 0x7C0 - andi $t0, $t0, 0x80 - bnel $t0, $zero, 1b - lui $t5, 0xBFC0 - lw $t0, 0x24($t5) - lui $t3, 0xB000 diff --git a/src/boot/reboot.S b/src/boot/reboot.S new file mode 100644 index 00000000..fabfa644 --- /dev/null +++ b/src/boot/reboot.S @@ -0,0 +1,57 @@ +#define RI_ADDRESS 0xA4700000 + +#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_RERROR 0x18 +#define RI_WERROR 0x1C + +#define RI_MODE_RESET 0x0000000E + +#define IPL3_ENTRY 0xA4000040 + + +.section .text.reboot, "ax", %progbits +.type reboot, %object +reboot_start: + .global reboot_start + +# NOTE: CIC x105 requirement +ipl2: +.set noat +.set noreorder + lui $t5, 0xBFC0 +1: + lw $t0, 0x7FC($t5) + addiu $t5, $t5, 0x7C0 + andi $t0, $t0, 0x80 + bnel $t0, $zero, 1b + lui $t5, 0xBFC0 + lw $t0, 0x24($t5) + lui $t3, 0xB000 +.set reorder +.set at + +reboot_entry: + .equ reboot_entry_offset, ((. - reboot_start) / 4) + .global reboot_entry_offset + +reset_rdram: + li $t0, RI_ADDRESS + + li $t1, RI_MODE_RESET + sw $t1, RI_MODE($t0) + sw $zero, RI_CONFIG($t0) + sw $zero, RI_CURRENT_LOAD($t0) + sw $zero, RI_SELECT($t0) + sw $zero, RI_REFRESH($t0) + +run_ipl3: + li $t3, IPL3_ENTRY + jr $t3 + + .equ reboot_size, (. - reboot_start) + .global reboot_size diff --git a/src/flashcart/flashcart.c b/src/flashcart/flashcart.c index a5a9cf36..46cb7a4c 100644 --- a/src/flashcart/flashcart.c +++ b/src/flashcart/flashcart.c @@ -91,7 +91,7 @@ flashcart_error_t flashcart_deinit (void) { return FLASHCART_OK; } -flashcart_error_t flashcart_load_rom (char *rom_path, bool byte_swap) { +flashcart_error_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress) { flashcart_error_t error; if ((rom_path == NULL) || (!file_exists(rom_path)) || (file_get_size(rom_path) < KiB(4))) { @@ -102,7 +102,7 @@ flashcart_error_t flashcart_load_rom (char *rom_path, bool byte_swap) { return FLASHCART_ERROR_INT; } - error = flashcart->load_rom(rom_path); + error = flashcart->load_rom(rom_path, progress); if (cart_card_byteswap(false)) { return FLASHCART_ERROR_INT; diff --git a/src/flashcart/flashcart.h b/src/flashcart/flashcart.h index 3833a1c3..917b6075 100644 --- a/src/flashcart/flashcart.h +++ b/src/flashcart/flashcart.h @@ -35,11 +35,13 @@ typedef enum { __FLASHCART_SAVE_TYPE_END } flashcart_save_type_t; +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_error_t (*load_rom) (char *rom_path, flashcart_progress_callback_t *progress); 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); @@ -48,7 +50,7 @@ typedef struct { 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_error_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress); flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type); diff --git a/src/flashcart/sc64/sc64.c b/src/flashcart/sc64/sc64.c index b091f7f8..0f5efc97 100644 --- a/src/flashcart/sc64/sc64.c +++ b/src/flashcart/sc64/sc64.c @@ -23,7 +23,7 @@ #define SUPPORTED_MINOR_VERSION (16) -static flashcart_error_t load_to_flash (FIL *fil, void *address, size_t size, UINT *br) { +static flashcart_error_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; @@ -44,6 +44,9 @@ static flashcart_error_t load_to_flash (FIL *fil, void *address, size_t size, UI if (sc64_flash_wait_busy() != SC64_OK) { return FLASHCART_ERROR_INT; } + if (progress) { + progress(f_tell(fil) / (float) (f_size(fil))); + } address += program_size; size -= program_size; *br += bp; @@ -109,7 +112,7 @@ static flashcart_error_t sc64_deinit (void) { return FLASHCART_OK; } -static flashcart_error_t sc64_load_rom (char *rom_path) { +static flashcart_error_t sc64_load_rom (char *rom_path, flashcart_progress_callback_t *progress) { FIL fil; UINT br; @@ -133,11 +136,18 @@ static flashcart_error_t sc64_load_rom (char *rom_path) { 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; - if (f_read(&fil, (void *) (ROM_ADDRESS), sdram_size, &br) != FR_OK) { - f_close(&fil); - return FLASHCART_ERROR_LOAD; + size_t chunk_size = MiB(1); + 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_ERROR_LOAD; + } + if (progress) { + progress(f_tell(&fil) / (float) (f_size(&fil))); + } } - if (br != sdram_size) { + if (f_tell(&fil) != sdram_size) { f_close(&fil); return FLASHCART_ERROR_LOAD; } @@ -148,7 +158,7 @@ static flashcart_error_t sc64_load_rom (char *rom_path) { } if (shadow_enabled) { - flashcart_error_t error = load_to_flash(&fil, (void *) (SHADOW_ADDRESS), shadow_size, &br); + flashcart_error_t error = load_to_flash(&fil, (void *) (SHADOW_ADDRESS), shadow_size, &br, progress); if (error != FLASHCART_OK) { f_close(&fil); return error; @@ -165,7 +175,7 @@ static flashcart_error_t sc64_load_rom (char *rom_path) { } if (extended_enabled) { - flashcart_error_t error = load_to_flash(&fil, (void *) (EXTENDED_ADDRESS), extended_size, &br); + flashcart_error_t error = load_to_flash(&fil, (void *) (EXTENDED_ADDRESS), extended_size, &br, progress); if (error != FLASHCART_OK) { f_close(&fil); return error; diff --git a/src/menu/assets.c b/src/menu/assets.c deleted file mode 100644 index bb053c50..00000000 --- a/src/menu/assets.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include - -#include - -#include "assets.h" - - -typedef struct { - char *name; - uint8_t *data; - int size; -} asset_t; - - -#define ASSET_IMPORT(a) \ - extern uint8_t *_binary_assets_##a##_start __attribute__((section(".data"))); \ - extern int _binary_assets_##a##_size __attribute__((section(".data"))); -#define ASSET(n, a) { n, (uint8_t *) (&_binary_assets_##a##_start), (int) (&_binary_assets_##a##_size) } - - -ASSET_IMPORT(FiraMono_Bold_font64); - -static asset_t assets_list[] = { - ASSET("assets:/font", FiraMono_Bold_font64), -}; - - -extern void *__real_asset_load (char *fn, int *sz); - -void *__wrap_asset_load (char *fn, int *sz) { - for (int i = 0; i < sizeof(assets_list) / sizeof(assets_list[0]); i++) { - asset_t *asset = &assets_list[i]; - if (strcmp(asset->name, fn) == 0) { - *sz = asset->size; - return asset->data; - } - } - - return __real_asset_load(fn, sz); -} - - -static assets_t assets; - - -void assets_init (void) { - assets.font = rdpq_font_load("assets:/font"); - assets.font_height = 16; -} - -assets_t *assets_get (void) { - return &assets; -} diff --git a/src/menu/assets.h b/src/menu/assets.h deleted file mode 100644 index a4d6dea1..00000000 --- a/src/menu/assets.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file assets.h - * @brief Menu Assets - * @ingroup menu - */ - -#ifndef ASSETS_H__ -#define ASSETS_H__ - - -#include - - -/** @brief Assets Structure */ -typedef struct { - /** @brief RDPQ Font */ - rdpq_font_t *font; - /** @brief Font Height */ - int font_height; -} assets_t; - - -void assets_init (void); -assets_t *assets_get (void); - - -#endif diff --git a/src/menu/components.h b/src/menu/components.h new file mode 100644 index 00000000..ce19af07 --- /dev/null +++ b/src/menu/components.h @@ -0,0 +1,52 @@ +/** + * @file components.h + * @brief Menu Components + * @ingroup menu + */ + +#ifndef COMPONENTS_H__ +#define COMPONENTS_H__ + + +#include +#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 { + 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 diff --git a/src/menu/components/background.c b/src/menu/components/background.c index 9e82e97b..ac838098 100644 --- a/src/menu/components/background.c +++ b/src/menu/components/background.c @@ -1,13 +1,20 @@ #include #include -#include "components.h" +#include "../components.h" +#include "constants.h" #include "utils/fs.h" #define CACHE_METADATA_MAGIC (0x424B4731) +typedef struct { + char *cache_location; + surface_t *image; + rspq_block_t *image_display_list; +} component_background_t; + typedef struct { uint32_t magic; uint32_t width; @@ -16,6 +23,9 @@ typedef struct { } cache_metadata_t; +static component_background_t *background = NULL; + + static void load_from_cache (component_background_t *c) { if (!c->cache_location) { return; @@ -35,10 +45,7 @@ static void load_from_cache (component_background_t *c) { return; } - uint32_t display_width = display_get_width(); - uint32_t display_height = display_get_height(); - - if (cache_metadata.magic != CACHE_METADATA_MAGIC || cache_metadata.width > display_width || cache_metadata.height > display_height) { + if (cache_metadata.magic != CACHE_METADATA_MAGIC || cache_metadata.width > DISPLAY_WIDTH || cache_metadata.height > DISPLAY_HEIGHT) { f_close(&fil); return; } @@ -92,62 +99,61 @@ static void prepare_background (component_background_t *c) { return; } - uint32_t display_width = display_get_width(); - uint32_t display_height = display_get_height(); - - int16_t display_center_x = (display_width / 2); - int16_t display_center_y = (display_height / 2); - - int16_t image_center_x = (c->image->width / 2); - int16_t image_center_y = (c->image->height / 2); + uint16_t image_center_x = (c->image->width / 2); + uint16_t image_center_y = (c->image->height / 2); // Darken the image rdpq_attach(c->image, NULL); rdpq_mode_push(); rdpq_set_mode_standard(); - rdpq_set_prim_color(RGBA32(0x00, 0x00, 0x00, 0xA0)); + rdpq_set_prim_color(BACKGROUND_OVERLAY_COLOR); rdpq_mode_combiner(RDPQ_COMBINER_FLAT); rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY); - rdpq_fill_rectangle(0, 0, c->image->width, c->image->height); + rdpq_fill_rectangle( + 0 - (DISPLAY_CENTER_X - image_center_x), + 0 - (DISPLAY_CENTER_Y - image_center_y), + DISPLAY_WIDTH - (DISPLAY_CENTER_X - image_center_x), + DISPLAY_HEIGHT - (DISPLAY_CENTER_Y - image_center_y) + ); rdpq_mode_pop(); rdpq_detach(); // Prepare display list rspq_block_begin(); rdpq_mode_push(); - if ((c->image->width != display_width) || (c->image->height != display_height)) { - rdpq_set_mode_fill(RGBA32(0x00, 0x00, 0x00, 0xFF)); + if ((c->image->width != DISPLAY_WIDTH) || (c->image->height != DISPLAY_HEIGHT)) { + rdpq_set_mode_fill(BACKGROUND_EMPTY_COLOR); } - if (c->image->width != display_width) { + if (c->image->width != DISPLAY_WIDTH) { rdpq_fill_rectangle( 0, - display_center_y - image_center_y, - display_center_x - image_center_x, - display_center_y + image_center_y + DISPLAY_CENTER_Y - image_center_y, + DISPLAY_CENTER_X - image_center_x, + DISPLAY_CENTER_Y + image_center_y ); rdpq_fill_rectangle( - display_center_x + image_center_x - (c->image->width % 2), - display_center_y - image_center_y, - display_width, - display_center_y + image_center_y + DISPLAY_CENTER_X + image_center_x - (c->image->width % 2), + DISPLAY_CENTER_Y - image_center_y, + DISPLAY_WIDTH, + DISPLAY_CENTER_Y + image_center_y ); } - if (c->image->height != display_height) { + if (c->image->height != DISPLAY_HEIGHT) { rdpq_fill_rectangle( 0, 0, - display_width, - display_center_y - image_center_y + DISPLAY_WIDTH, + DISPLAY_CENTER_Y - image_center_y ); rdpq_fill_rectangle( 0, - display_center_y + image_center_y - (c->image->height % 2), - display_width, - display_height + DISPLAY_CENTER_Y + image_center_y - (c->image->height % 2), + DISPLAY_WIDTH, + DISPLAY_HEIGHT ); } rdpq_set_mode_copy(false); - rdpq_tex_blit(c->image, display_center_x - image_center_x, display_center_y - image_center_y, NULL); + rdpq_tex_blit(c->image, DISPLAY_CENTER_X - image_center_x, DISPLAY_CENTER_Y - image_center_y, NULL); rdpq_mode_pop(); c->image_display_list = rspq_block_end(); } @@ -157,46 +163,56 @@ static void display_list_free (void *arg) { } -component_background_t *component_background_create (char *cache_location) { - component_background_t *c = calloc(1, sizeof(component_background_t)); - - c->cache_location = cache_location; - - load_from_cache(c); - - prepare_background(c); - - return c; +void component_background_init (char *cache_location) { + if (!background) { + background = calloc(1, sizeof(component_background_t)); + background->cache_location = cache_location; + load_from_cache(background); + prepare_background(background); + } } -void component_background_replace_image (component_background_t *c, surface_t *image) { - if (!c) { +void component_background_free (void) { + if (background) { + if (background->image) { + surface_free(background->image); + free(background->image); + background->image = NULL; + } + if (background->image_display_list) { + rdpq_call_deferred(display_list_free, background->image_display_list); + background->image_display_list = NULL; + } + free(background); + background = NULL; + } +} + +void component_background_replace_image (surface_t *image) { + if (!background) { return; } - if (c->image) { - surface_free(c->image); - free(c->image); - c->image = NULL; + if (background->image) { + surface_free(background->image); + free(background->image); + background->image = NULL; } - if (c->image_display_list) { - rdpq_call_deferred(display_list_free, c->image_display_list); - c->image_display_list = NULL; + if (background->image_display_list) { + rdpq_call_deferred(display_list_free, background->image_display_list); + background->image_display_list = NULL; } - c->image = image; - - save_to_cache(c); - - prepare_background(c); + background->image = image; + save_to_cache(background); + prepare_background(background); } -void component_background_draw (component_background_t *c) { - if (!c || !c->image_display_list) { - rdpq_clear(RGBA32(0x00, 0x00, 0x00, 0xFF)); - return; +void component_background_draw (void) { + if (background && background->image_display_list) { + rspq_block_run(background->image_display_list); + } else { + rdpq_clear(BACKGROUND_EMPTY_COLOR); } - - rspq_block_run(c->image_display_list); } diff --git a/src/menu/components/boxart.c b/src/menu/components/boxart.c new file mode 100644 index 00000000..411e8bbb --- /dev/null +++ b/src/menu/components/boxart.c @@ -0,0 +1,70 @@ +#include + +#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 + ); + } +} diff --git a/src/menu/components/common.c b/src/menu/components/common.c new file mode 100644 index 00000000..0001d063 --- /dev/null +++ b/src/menu/components/common.c @@ -0,0 +1,182 @@ +#include + +#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, ¶graph_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 + ); +} diff --git a/src/menu/components/components.h b/src/menu/components/components.h deleted file mode 100644 index f8618827..00000000 --- a/src/menu/components/components.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file components.h - * @brief Menu Components - * @ingroup menu - */ - -#ifndef COMPONENTS_H__ -#define COMPONENTS_H__ - - -#include - - -/** - * @addtogroup - * @{ menu_components - */ - -typedef struct { - char *cache_location; - surface_t *image; - rspq_block_t *image_display_list; -} component_background_t; - -component_background_t *component_background_create (char *cache_location); -void component_background_replace_image (component_background_t *c, surface_t *image); -void component_background_draw (component_background_t *c); - -/** @} */ /* menu_components */ - - -#endif diff --git a/src/menu/components/constants.h b/src/menu/components/constants.h new file mode 100644 index 00000000..a9012068 --- /dev/null +++ b/src/menu/components/constants.h @@ -0,0 +1,83 @@ +/** + * @file constants.h + * @brief Menu components constants + * @ingroup menu + */ + +#ifndef COMPONENTS_CONSTANTS_H__ +#define COMPONENTS_CONSTANTS_H__ + + +#define DISPLAY_WIDTH (640) +#define DISPLAY_HEIGHT (480) + +#define DISPLAY_CENTER_X (DISPLAY_WIDTH / 2) +#define DISPLAY_CENTER_Y (DISPLAY_HEIGHT / 2) + +#define OVERSCAN_WIDTH (32) +#define OVERSCAN_HEIGHT (24) + +#define VISIBLE_AREA_X0 (OVERSCAN_WIDTH) +#define VISIBLE_AREA_Y0 (OVERSCAN_HEIGHT) +#define VISIBLE_AREA_X1 (DISPLAY_WIDTH - OVERSCAN_WIDTH) +#define VISIBLE_AREA_Y1 (DISPLAY_HEIGHT - OVERSCAN_HEIGHT) + +#define VISIBLE_AREA_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0) +#define VISIBLE_AREA_HEIGHT (VISIBLE_AREA_Y1 - VISIBLE_AREA_Y0) + +#define BORDER_THICKNESS (4) + +#define LAYOUT_ACTIONS_SEPARATOR_Y (400) + +#define SEEKBAR_HEIGHT (24) +#define SEEKBAR_WIDTH (524) +#define SEEKBAR_X (DISPLAY_CENTER_X - (SEEKBAR_WIDTH / 2)) +#define SEEKBAR_Y (VISIBLE_AREA_Y1 - SEEKBAR_HEIGHT - 80) + +#define LOADER_WIDTH (320) +#define LOADER_HEIGHT (24) +#define LOADER_X (DISPLAY_CENTER_X - (LOADER_WIDTH / 2)) +#define LOADER_Y (DISPLAY_CENTER_Y - (LOADER_HEIGHT / 2)) + +#define MESSAGEBOX_MAX_WIDTH (360) +#define MESSAGEBOX_MARGIN (32) + +#define TEXT_MARGIN_HORIZONTAL (10) +#define TEXT_MARGIN_VERTICAL (7) + +#define BOXART_WIDTH (158) +#define BOXART_HEIGHT (112) +#define BOXART_X (VISIBLE_AREA_X1 - BOXART_WIDTH - 24) +#define BOXART_Y (LAYOUT_ACTIONS_SEPARATOR_Y - BOXART_HEIGHT - 24) + +#define FILE_LIST_SCROLLBAR_WIDTH (12) +#define FILE_LIST_SCROLLBAR_HEIGHT (LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT) +#define FILE_LIST_SCROLLBAR_X (VISIBLE_AREA_X1 - FILE_LIST_SCROLLBAR_WIDTH) +#define FILE_LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0) + +#define FILE_LIST_ENTRIES (20) +#define FILE_LIST_MAX_WIDTH (480) +#define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - FILE_LIST_SCROLLBAR_WIDTH) +#define FILE_LIST_HIGHLIGHT_X (VISIBLE_AREA_X0) + + +#define BACKGROUND_EMPTY_COLOR RGBA32(0x00, 0x00, 0x00, 0xFF) +#define BACKGROUND_OVERLAY_COLOR RGBA32(0x00, 0x00, 0x00, 0xA0) + +#define BORDER_COLOR RGBA32(0xFF, 0xFF, 0xFF, 0xFF) + +#define PROGRESSBAR_BG_COLOR RGBA32(0x00, 0x00, 0x00, 0xFF) +#define PROGRESSBAR_DONE_COLOR RGBA32(0x3B, 0x7C, 0xF5, 0xFF) + +#define SCROLLBAR_BG_COLOR RGBA32(0x3F, 0x3F, 0x3F, 0xFF) +#define SCROLLBAR_INACTIVE_COLOR RGBA32(0x5F, 0x5F, 0x5F, 0xFF) +#define SCROLLBAR_POSITION_COLOR RGBA32(0x7F, 0x7F, 0x7F, 0xFF) + +#define DIALOG_BG_COLOR RGBA32(0x00, 0x00, 0x00, 0xFF) + +#define BOXART_LOADING_COLOR RGBA32(0x3F, 0x3F, 0x3F, 0xFF) + +#define FILE_LIST_HIGHLIGHT_COLOR RGBA32(0x3F, 0x3F, 0x3F, 0xFF) + + +#endif diff --git a/src/menu/components/file_list.c b/src/menu/components/file_list.c new file mode 100644 index 00000000..fd4a1ea4 --- /dev/null +++ b/src/menu/components/file_list.c @@ -0,0 +1,145 @@ +#include + +#include "../components.h" +#include "../fonts.h" +#include "constants.h" + + +static const char *dir_prefix = "/"; + + +static void format_file_size (char *buffer, int size) { + if (size < 8 * 1024) { + sprintf(buffer, "%d B", size); + } else if (size < 8 * 1024 * 1024) { + sprintf(buffer, "%d kB", size / 1024); + } else if (size < 1 * 1024 * 1024 * 1024) { + sprintf(buffer, "%d MB", size / 1024 / 1024); + } else { + sprintf(buffer, "%d GB", size / 1024 / 1024 / 1024); + } +} + + +void component_file_list_draw (entry_t *list, int entries, int selected) { + int starting_position = 0; + + if (entries > FILE_LIST_ENTRIES && selected >= (FILE_LIST_ENTRIES / 2)) { + starting_position = selected - (FILE_LIST_ENTRIES / 2); + if (starting_position >= entries - FILE_LIST_ENTRIES) { + starting_position = entries - FILE_LIST_ENTRIES; + } + } + + component_file_list_scrollbar_draw(selected, entries, FILE_LIST_ENTRIES); + + if (entries == 0) { + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "^%02X** empty directory **", + STL_UNKNOWN + ); + } else { + const int list_max_characters = (256 + strlen(dir_prefix)) * FILE_LIST_ENTRIES; + const int paragraph_size = sizeof(rdpq_paragraph_t) + sizeof(rdpq_paragraph_char_t) * list_max_characters; + + rdpq_paragraph_t *paragraph = calloc(1, paragraph_size); + paragraph->capacity = list_max_characters; + + rdpq_paragraph_builder_begin( + &(rdpq_textparms_t) { + .width = FILE_LIST_MAX_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2), + .height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2), + .wrap = WRAP_ELLIPSES, + }, + FNT_DEFAULT, + paragraph + ); + + for (int i = starting_position; i < entries; i++) { + if (i == (starting_position + FILE_LIST_ENTRIES)) { + break; + } + + entry_t *entry = &list[i]; + + menu_font_style_t style; + + switch (entry->type) { + case ENTRY_TYPE_DIR: style = STL_DIRECTORY; break; + case ENTRY_TYPE_SAVE: style = STL_SAVE; break; + case ENTRY_TYPE_OTHER: style = STL_UNKNOWN; break; + case ENTRY_TYPE_IMAGE: style = STL_MEDIA; break; + case ENTRY_TYPE_MUSIC: style = STL_MEDIA; break; + default: style = STL_DEFAULT; break; + } + + rdpq_paragraph_builder_style(style); + + if (entry->type == ENTRY_TYPE_DIR) { + rdpq_paragraph_builder_span(dir_prefix, strlen(dir_prefix)); + } + + rdpq_paragraph_builder_span(entry->name, strlen(entry->name)); + + rdpq_paragraph_builder_newline(); + } + + rdpq_paragraph_builder_end(); + + int highlight_height = (paragraph->bbox[3] - paragraph->bbox[1]) / paragraph->nlines; + int highlight_y = VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + ((selected - starting_position) * highlight_height); + + component_box_draw( + FILE_LIST_HIGHLIGHT_X, + highlight_y, + FILE_LIST_HIGHLIGHT_X + FILE_LIST_HIGHLIGHT_WIDTH, + highlight_y + highlight_height, + FILE_LIST_HIGHLIGHT_COLOR + ); + + rdpq_paragraph_render( + paragraph, + VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL, + VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + ); + + memset(paragraph, 0, paragraph_size); + + rdpq_paragraph_builder_begin( + &(rdpq_textparms_t) { + .width = VISIBLE_AREA_WIDTH - FILE_LIST_SCROLLBAR_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2), + .height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2), + .align = ALIGN_RIGHT, + .wrap = WRAP_ELLIPSES, + }, + FNT_DEFAULT, + paragraph + ); + + char file_size[16]; + + for (int i = starting_position; i < entries; i++) { + if (i == (starting_position + FILE_LIST_ENTRIES)) { + break; + } + + entry_t *entry = &list[i]; + + if (entry->type != ENTRY_TYPE_DIR) { + format_file_size(file_size, entry->size); + rdpq_paragraph_builder_span(file_size, strlen(file_size)); + } + + rdpq_paragraph_builder_newline(); + } + + rdpq_paragraph_render( + paragraph, + VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL, + VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + ); + + rdpq_paragraph_free(paragraph); + } +} diff --git a/src/menu/fonts.c b/src/menu/fonts.c new file mode 100644 index 00000000..fc743441 --- /dev/null +++ b/src/menu/fonts.c @@ -0,0 +1,30 @@ +#include + +#include "fonts.h" + + +#define FONT_IMPORT(f) \ + extern void *_binary_assets_##f##_start __attribute__((section(".data"))); \ + extern int _binary_assets_##f##_size __attribute__((section(".data"))); +#define FONT_LOAD(f) rdpq_font_load_buf((void *) (&_binary_assets_##f##_start), (size_t) (&_binary_assets_##f##_size)) + + +FONT_IMPORT(FiraMono_Bold_font64); + + +static void load_default_font (void) { + rdpq_font_t *default_font = FONT_LOAD(FiraMono_Bold_font64); + + rdpq_font_style(default_font, STL_DEFAULT, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF) })); + rdpq_font_style(default_font, STL_DIRECTORY, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0x70, 0xFF) })); + rdpq_font_style(default_font, STL_SAVE, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xFF, 0x70, 0xFF) })); + rdpq_font_style(default_font, STL_MEDIA, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xBC, 0xFF, 0xFF) })); + rdpq_font_style(default_font, STL_UNKNOWN, &((rdpq_fontstyle_t) { .color = RGBA32(0xA0, 0xA0, 0xA0, 0xFF) })); + + rdpq_text_register_font(FNT_DEFAULT, default_font); +} + + +void fonts_init (void) { + load_default_font(); +} diff --git a/src/menu/fonts.h b/src/menu/fonts.h new file mode 100644 index 00000000..1c69a5a5 --- /dev/null +++ b/src/menu/fonts.h @@ -0,0 +1,27 @@ +/** + * @file fonts.h + * @brief Menu fonts + * @ingroup menu + */ + +#ifndef FONTS_H__ +#define FONTS_H__ + + +typedef enum { + FNT_DEFAULT = 1, +} menu_font_type_t; + +typedef enum { + STL_DEFAULT = 0, + STL_DIRECTORY, + STL_SAVE, + STL_MEDIA, + STL_UNKNOWN, +} menu_font_style_t; + + +void fonts_init (void); + + +#endif diff --git a/src/menu/menu.c b/src/menu/menu.c index ed89194d..9c46e9a1 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -3,9 +3,9 @@ #include #include "actions.h" -#include "assets.h" #include "boot/boot.h" #include "flashcart/flashcart.h" +#include "fonts.h" #include "menu_state.h" #include "menu.h" #include "mp3_player.h" @@ -17,6 +17,9 @@ #define TV_TYPE_RAM *((uint32_t *) (0x80000300)) +#define CACHE_DIRECTORY "sd:/menu/cache" +#define BACKGROUND_CACHE "sd:/menu/cache/background.data" + static menu_t *menu; static bool boot_pending; @@ -27,12 +30,12 @@ static void menu_init (boot_params_t *boot_params) { controller_init(); timer_init(); rtc_init(); - audio_init(44100, 2); + audio_init(44100, 3); mixer_init(2); rspq_init(); rdpq_init(); - assets_init(); + fonts_init(); mp3player_mixer_init(); boot_pending = false; @@ -48,8 +51,14 @@ static void menu_init (boot_params_t *boot_params) { menu->next_mode = MENU_MODE_FAULT; } + menu->error_message = NULL; + settings_load(&menu->settings); + directory_create(CACHE_DIRECTORY); + + component_background_init(BACKGROUND_CACHE); + menu->boot_params = boot_params; bool default_directory_exists = directory_exists(menu->settings.default_directory); @@ -67,21 +76,24 @@ static void menu_init (boot_params_t *boot_params) { } static void menu_deinit (menu_t *menu) { + display_close(); + // NOTE: Restore previous TV type so boot procedure wouldn't passthrough wrong value. TV_TYPE_RAM = tv_type; - flashcart_deinit(); - path_free(menu->browser.directory); free(menu); + component_background_free(); + + flashcart_deinit(); + rdpq_close(); rspq_close(); mixer_close(); audio_close(); rtc_close(); timer_close(); - display_close(); } @@ -203,7 +215,7 @@ void menu_run (boot_params_t *boot_params) { audio_write_end(); } - png_poll(); + png_decoder_poll(); } menu_deinit(menu); diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index 2b48baa5..48a4c45d 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -9,7 +9,6 @@ #include "boot/boot.h" -#include "components/components.h" #include "flashcart/flashcart.h" #include "path.h" #include "settings.h" @@ -60,6 +59,8 @@ typedef struct { boot_params_t *boot_params; flashcart_error_t flashcart_error; + char *error_message; + struct { bool go_up; bool go_down; @@ -83,10 +84,6 @@ typedef struct { int entries; int selected; } browser; - - struct { - component_background_t *background; - } components; } menu_t; diff --git a/src/menu/mp3_player.c b/src/menu/mp3_player.c index 1c463bbe..0788abd7 100644 --- a/src/menu/mp3_player.c +++ b/src/menu/mp3_player.c @@ -121,7 +121,7 @@ mp3player_err_t mp3player_init (void) { p = calloc(1, sizeof(mp3player_t)); if (p == NULL) { - return MP3PLAYER_ERR_MALLOC; + return MP3PLAYER_ERR_OUT_OF_MEM; } mp3player_reset_decoder(); diff --git a/src/menu/mp3_player.h b/src/menu/mp3_player.h index 858050ff..72887be2 100644 --- a/src/menu/mp3_player.h +++ b/src/menu/mp3_player.h @@ -14,7 +14,7 @@ /** @brief MP3 file error enumeration */ typedef enum { MP3PLAYER_OK, - MP3PLAYER_ERR_MALLOC, + MP3PLAYER_ERR_OUT_OF_MEM, MP3PLAYER_ERR_IO, MP3PLAYER_ERR_NO_FILE, MP3PLAYER_ERR_INVALID_FILE, diff --git a/src/menu/png_decoder.c b/src/menu/png_decoder.c index ac5ecbe9..43c2ada5 100644 --- a/src/menu/png_decoder.c +++ b/src/menu/png_decoder.c @@ -13,6 +13,7 @@ typedef struct { surface_t *image; uint8_t *row_buffer; + int decoded_rows; png_callback_t *callback; void *callback_data; @@ -55,7 +56,7 @@ static int png_file_read (spng_ctx *ctx, void *user, void *dst_src, size_t lengt } -png_err_t png_decode_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data) { +png_err_t png_decoder_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data) { if (decoder != NULL) { return PNG_ERR_BUSY; } @@ -124,45 +125,58 @@ png_err_t png_decode_start (char *path, int max_width, int max_height, png_callb return PNG_ERR_OUT_OF_MEM; } + decoder->decoded_rows = 0; + decoder->callback = callback; decoder->callback_data = callback_data; return PNG_OK; } -void png_decode_abort (void) { +void png_decoder_abort (void) { png_decoder_deinit(true); } -void png_poll (void) { - if (decoder) { - enum spng_errno err; - struct spng_row_info row_info; +float png_decoder_get_progress (void) { + if (!decoder) { + return 0.0f; + } - if ((err = spng_get_row_info(decoder->ctx, &row_info)) != SPNG_OK) { - decoder->callback(PNG_ERR_BAD_FILE, NULL, decoder->callback_data); - png_decoder_deinit(true); - return; - } + return (float) (decoder->decoded_rows) / (decoder->ihdr.height); +} - err = spng_decode_row(decoder->ctx, decoder->row_buffer, decoder->ihdr.width * 3); +void png_decoder_poll (void) { + if (!decoder) { + return; + } - if (err == SPNG_OK || err == SPNG_EOI) { - uint16_t *image_buffer = decoder->image->buffer + (row_info.row_num * decoder->image->stride); - for (int i = 0; i < decoder->ihdr.width * 3; i += 3) { - uint8_t r = decoder->row_buffer[i + 0] >> 3; - uint8_t g = decoder->row_buffer[i + 1] >> 3; - uint8_t b = decoder->row_buffer[i + 2] >> 3; - *image_buffer++ = (r << 11) | (g << 6) | (b << 1) | 1; - } - } + enum spng_errno err; + struct spng_row_info row_info; - if (err == SPNG_EOI) { - decoder->callback(PNG_OK, decoder->image, decoder->callback_data); - png_decoder_deinit(false); - } else if (err != SPNG_OK) { - decoder->callback(PNG_ERR_BAD_FILE, NULL, decoder->callback_data); - png_decoder_deinit(true); + if ((err = spng_get_row_info(decoder->ctx, &row_info)) != SPNG_OK) { + decoder->callback(PNG_ERR_BAD_FILE, NULL, decoder->callback_data); + png_decoder_deinit(true); + return; + } + + err = spng_decode_row(decoder->ctx, decoder->row_buffer, decoder->ihdr.width * 3); + + if (err == SPNG_OK || err == SPNG_EOI) { + decoder->decoded_rows += 1; + uint16_t *image_buffer = decoder->image->buffer + (row_info.row_num * decoder->image->stride); + for (int i = 0; i < decoder->ihdr.width * 3; i += 3) { + uint8_t r = decoder->row_buffer[i + 0] >> 3; + uint8_t g = decoder->row_buffer[i + 1] >> 3; + uint8_t b = decoder->row_buffer[i + 2] >> 3; + *image_buffer++ = (r << 11) | (g << 6) | (b << 1) | 1; } } + + if (err == SPNG_EOI) { + decoder->callback(PNG_OK, decoder->image, decoder->callback_data); + png_decoder_deinit(false); + } else if (err != SPNG_OK) { + decoder->callback(PNG_ERR_BAD_FILE, NULL, decoder->callback_data); + png_decoder_deinit(true); + } } diff --git a/src/menu/png_decoder.h b/src/menu/png_decoder.h index 13d5ef9e..a769ddd3 100644 --- a/src/menu/png_decoder.h +++ b/src/menu/png_decoder.h @@ -24,9 +24,10 @@ typedef enum { typedef void png_callback_t (png_err_t err, surface_t *decoded_image, void *callback_data); -png_err_t png_decode_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data); -void png_decode_abort (void); -void png_poll (void); +png_err_t png_decoder_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data); +void png_decoder_abort (void); +float png_decoder_get_progress (void); +void png_decoder_poll (void); #endif diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index 818b6ee1..559147ea 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -1,12 +1,10 @@ -#include #include #include +#include #include -#include -#include "../components/components.h" -#include "fragments/fragments.h" +#include "../fonts.h" #include "utils/fs.h" #include "views.h" @@ -159,33 +157,6 @@ static bool pop_directory (menu_t *menu) { return false; } -static void format_size (char *buffer, int size) { - if (size < 8 * 1024) { - sprintf(buffer, "%4d B ", size); - } else if (size < 8 * 1024 * 1024) { - sprintf(buffer, "%4d kB", size / 1024); - } else if (size < 1 * 1024 * 1024 * 1024) { - sprintf(buffer, "%4d MB", size / 1024 / 1024); - } else { - sprintf(buffer, "%4d GB", size / 1024 / 1024 / 1024); - } -} - -static void format_entry (char *buffer, entry_t *entry, bool selected) { - int cutoff_length = (entry->type == ENTRY_TYPE_DIR ? 57 : 49); - int name_length = strlen(entry->name); - strcpy(buffer, ""); - if (entry->type == ENTRY_TYPE_DIR) { - strcat(buffer, "/"); - } - if (name_length > cutoff_length) { - strncat(buffer, entry->name, cutoff_length - 1); - strcat(buffer, "…"); - } else { - strcat(buffer, entry->name); - } -} - static void process (menu_t *menu) { int scroll_speed = menu->actions.fast ? 10 : 1; @@ -204,14 +175,14 @@ static void process (menu_t *menu) { } } - if (menu->actions.enter) { + if (menu->actions.enter && menu->browser.selected >= 0) { entry_t *entry = &menu->browser.list[menu->browser.selected]; switch (entry->type) { case ENTRY_TYPE_DIR: if (push_directory(menu, entry->name)) { menu->browser.valid = false; - menu->next_mode = MENU_MODE_ERROR; + menu_show_error(menu, "Couldn't open next directory"); } break; case ENTRY_TYPE_ROM: @@ -230,12 +201,10 @@ static void process (menu_t *menu) { } else if (menu->actions.back && !path_is_root(menu->browser.directory)) { if (pop_directory(menu)) { menu->browser.valid = false; - menu->next_mode = MENU_MODE_ERROR; - } - } else if (menu->actions.file_info) { - if (menu->browser.selected >= 0) { - menu->next_mode = MENU_MODE_FILE_INFO; + menu_show_error(menu, "Couldn't open last directory"); } + } else if (menu->actions.file_info && menu->browser.selected >= 0) { + menu->next_mode = MENU_MODE_FILE_INFO; } else if (menu->actions.system_info) { menu->next_mode = MENU_MODE_SYSTEM_INFO; } else if (menu->actions.settings) { @@ -243,125 +212,52 @@ static void process (menu_t *menu) { } } + static void draw (menu_t *menu, surface_t *d) { - char buffer[64]; - - layout_t *layout = layout_get(); - - const int text_x = layout->offset_x + layout->offset_text_x; - int text_y = layout->offset_y + layout->offset_text_y; - const int text_file_size_x = text_x + 478; - const int text_other_actions_x = text_x + 450; - const int highlight_offset = 2; - - const color_t highlight_color = RGBA32(0x3F, 0x3F, 0x3F, 0xFF); - const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - const color_t directory_color = RGBA32(0xFF, 0xFF, 0x70, 0xFF); - const color_t save_color = RGBA32(0x70, 0xFF, 0x70, 0xFF); - const color_t music_color = RGBA32(0x70, 0xBC, 0xFF, 0xFF); - const color_t other_color = RGBA32(0xA0, 0xA0, 0xA0, 0xFF); - - int starting_position = 0; - - if (menu->browser.entries > layout->main_lines && menu->browser.selected >= (layout->main_lines / 2)) { - starting_position = menu->browser.selected - (layout->main_lines / 2); - if (starting_position >= menu->browser.entries - layout->main_lines) { - starting_position = menu->browser.entries - layout->main_lines; - } - } - rdpq_attach(d, NULL); - // Background - component_background_draw(menu->components.background); + component_background_draw(); - // Layout - fragment_borders(d); - fragment_scrollbar(d, menu->browser.selected, menu->browser.entries); + component_layout_draw(); - // Highlight - if (menu->browser.entries > 0) { - rdpq_set_mode_fill(highlight_color); - rdpq_fill_rectangle( - layout->offset_x, - text_y + highlight_offset + ((menu->browser.selected - starting_position) * layout->line_height), - d->width - layout->offset_x - layout->scrollbar_width, - text_y + layout->line_height + highlight_offset + ((menu->browser.selected - starting_position) * layout->line_height) + component_file_list_draw(menu->browser.list, menu->browser.entries, menu->browser.selected); + + const char *action = NULL; + + switch (menu->browser.list[menu->browser.selected].type) { + case ENTRY_TYPE_DIR: action = "A: Enter"; break; + case ENTRY_TYPE_ROM: action = "A: Load"; break; + case ENTRY_TYPE_IMAGE: action = "A: Show"; break; + case ENTRY_TYPE_MUSIC: action = "A: Play"; break; + default: action = "A: Info"; break; + } + + component_actions_bar_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "%s\n" + "^%02XB: Back^00", + menu->browser.entries == 0 ? "" : action, + path_is_root(menu->browser.directory) ? STL_UNKNOWN : STL_DEFAULT + ); + + component_actions_bar_text_draw( + ALIGN_RIGHT, VALIGN_TOP, + "%s\n" + "L: Settings", + menu->browser.entries == 0 ? "" : "R: Info" + ); + + time_t current_time = time(NULL); + + if (current_time >= 0) { + component_actions_bar_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "\n" + "%s", + ctime(¤t_time) ); } - // Text start - fragment_text_start(text_color); - - // Main screen - for (int i = starting_position; i < menu->browser.entries; i++) { - if (i == (starting_position + layout->main_lines)) { - break; - } - - entry_t *entry = &menu->browser.list[i]; - - switch (entry->type) { - case ENTRY_TYPE_DIR: fragment_text_set_color(directory_color); break; - case ENTRY_TYPE_SAVE: fragment_text_set_color(save_color); break; - case ENTRY_TYPE_OTHER: fragment_text_set_color(other_color); break; - case ENTRY_TYPE_MUSIC: fragment_text_set_color(music_color); break; - default: fragment_text_set_color(text_color); break; - } - - format_entry(buffer, entry, i == menu->browser.selected); - fragment_textf(text_x, text_y, buffer); - - if (entry->type != ENTRY_TYPE_DIR) { - format_size(buffer, entry->size); - fragment_text_set_color(text_color); - fragment_textf(text_file_size_x, text_y, buffer); - } - - text_y += layout->line_height; - } - - if (menu->browser.entries == 0) { - fragment_text_set_color(other_color); - fragment_textf(text_x, text_y, "** empty directory **"); - } - - // Actions bar - fragment_text_set_color(text_color); - text_y = layout->actions_y + layout->offset_text_y; - if (menu->browser.entries > 0) { - switch (menu->browser.list[menu->browser.selected].type) { - case ENTRY_TYPE_DIR: - fragment_textf(text_x, text_y, "A: Enter"); - break; - case ENTRY_TYPE_ROM: - fragment_textf(text_x, text_y, "A: Load"); - break; - case ENTRY_TYPE_IMAGE: - fragment_textf(text_x, text_y, "A: Show"); - break; - case ENTRY_TYPE_MUSIC: - fragment_textf(text_x, text_y, "A: Play"); - break; - default: - fragment_textf(text_x, text_y, "A: Info"); - break; - } - fragment_textf(text_other_actions_x, text_y, "R: Info"); - } - text_y += layout->line_height; - if (!path_is_root(menu->browser.directory)) { - fragment_textf(text_x, text_y, "B: Back"); - } - fragment_textf(text_other_actions_x, text_y, "L: Settings"); - - time_t current_time = -1; - current_time = time( NULL ); - if( current_time != -1 ) - { - fragment_textf(text_other_actions_x - 288, text_y, ctime( ¤t_time )); - } - rdpq_detach_show(); } @@ -371,7 +267,7 @@ void view_browser_init (menu_t *menu) { if (load_directory(menu)) { path_free(menu->browser.directory); menu->browser.directory = path_init(NULL); - menu->next_mode = MENU_MODE_ERROR; + menu_show_error(menu, "Error while opening initial directory"); } else { menu->browser.valid = true; } @@ -380,5 +276,6 @@ void view_browser_init (menu_t *menu) { void view_browser_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); } diff --git a/src/menu/views/credits.c b/src/menu/views/credits.c index e91df1a5..54f8cca7 100644 --- a/src/menu/views/credits.c +++ b/src/menu/views/credits.c @@ -1,7 +1,3 @@ -#include - -#include "../components/components.h" -#include "fragments/fragments.h" #include "views.h" @@ -17,51 +13,45 @@ static void process (menu_t *menu) { } static void draw (menu_t *menu, surface_t *d) { - layout_t *layout = layout_get(); - - const int text_x = layout->offset_x + layout->offset_text_x; - int text_y = layout->offset_y + layout->offset_text_y; - - const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - rdpq_attach(d, NULL); - // Background - component_background_draw(menu->components.background); + component_background_draw(); - // Layout - fragment_borders(d); + component_layout_draw(); - // Text start - fragment_text_start(text_color); + component_main_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "MENU INFORMATION" + ); - text_y += fragment_textf((d->width / 2) - 76, text_y, "MENU INFORMATION"); + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "\n" + "Menu Revision: V%s\n" + "\n" + "Github:\n" + " https://github.com/Polprzewodnikowy/N64FlashcartMenu\n" + "Authors:\n" + " JonesAlmighty / NetworkFusion\n" + " Mateusz Faderewski / Polprzewodnikowy\n" + "Credits:\n" + " N64Brew / libdragon contributors\n" + "\n" + "OSS software used:\n" + " libdragon (UNLICENSE License)\n" + " libspng (BSD 2-Clause License)\n" + " mini.c (BSD 2-Clause License)\n" + " minimp3 (CC0 1.0 Universal)\n" + " miniz (MIT License)", + MENU_VERSION + ); - text_y += fragment_textf(text_x, text_y, "\n"); - - text_y += fragment_textf(text_x, text_y, "Menu Revision: V%s", MENU_VERSION); - text_y += fragment_textf(text_x, text_y, "\n"); - text_y += fragment_textf(text_x, text_y, "Authors:"); - text_y += fragment_textf(text_x, text_y, " JonesAlmighty / NetworkFusion"); - text_y += fragment_textf(text_x, text_y, " korgeaux / Polprzewodnikowy"); - text_y += fragment_textf(text_x, text_y, "\n"); - text_y += fragment_textf(text_x, text_y, "Credits:"); - text_y += fragment_textf(text_x, text_y, " N64Brew / libdragon contributors."); - text_y += fragment_textf(text_x, text_y, "\n"); - text_y += fragment_textf(text_x, text_y, "Github:"); - text_y += fragment_textf(text_x, text_y, " https://github.com/Polprzewodnikowy/N64FlashcartMenu"); - text_y += fragment_textf(text_x, text_y, "\n"); - text_y += fragment_textf(text_x, text_y, "OSS licenses used:"); - text_y += fragment_textf(text_x, text_y, " UNLICENSE"); /* libdragon license */ - text_y += fragment_textf(text_x, text_y, " MIT"); - text_y += fragment_textf(text_x, text_y, " BSD 2-Clause"); /* libspng license */ - text_y += fragment_textf(text_x, text_y, " CC0 1.0 Universal"); /* minimp3 license */ - // text_y += fragment_textf(text_x, text_y, " Permissive, unspecific"); /* miniz license */ - - - // Actions bar - text_y = layout->actions_y + layout->offset_text_y; - text_y += fragment_textf(text_x, text_y, "B: Exit"); + component_actions_bar_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "B: Exit" + ); rdpq_detach_show(); } @@ -73,5 +63,6 @@ void view_credits_init (menu_t *menu) { void view_credits_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); } diff --git a/src/menu/views/error.c b/src/menu/views/error.c index cf14dd91..711f166e 100644 --- a/src/menu/views/error.c +++ b/src/menu/views/error.c @@ -1,5 +1,3 @@ -#include - #include "views.h" @@ -10,11 +8,21 @@ static void process (menu_t *menu) { } static void draw (menu_t *menu, surface_t *d) { - graphics_fill_screen(d, graphics_make_color(0, 0, 0, 255)); + rdpq_attach(d, NULL); - graphics_draw_text(d, 40, 32, "ERROR!"); + component_background_draw(); - display_show(d); + if (menu->error_message) { + component_messagebox_draw(menu->error_message); + } else { + component_messagebox_draw("Unspecified error"); + } + + rdpq_detach_show(); +} + +static void deinit (menu_t *menu) { + menu->error_message = NULL; } @@ -24,5 +32,15 @@ void view_error_init (menu_t *menu) { void view_error_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); + + if (menu->next_mode != MENU_MODE_ERROR) { + deinit(menu); + } +} + +void menu_show_error (menu_t *menu, char *error_message) { + menu->next_mode = MENU_MODE_ERROR; + menu->error_message = error_message; } diff --git a/src/menu/views/fault.c b/src/menu/views/fault.c index 366a95e3..95bf8ce3 100644 --- a/src/menu/views/fault.c +++ b/src/menu/views/fault.c @@ -1,11 +1,7 @@ -#include - -#include "flashcart/flashcart.h" -#include "fragments/fragments.h" #include "views.h" -static char *format_flashcart_error (flashcart_error_t error) { +static char *convert_error_message (flashcart_error_t error) { switch (error) { case FLASHCART_OK: return "No error"; @@ -28,26 +24,26 @@ static char *format_flashcart_error (flashcart_error_t error) { static void draw (menu_t *menu, surface_t *d) { - layout_t *layout = layout_get(); - - const int text_x = layout->offset_x + layout->offset_text_x; - int text_y = layout->offset_y + layout->offset_text_y; - - const color_t bg_color = RGBA32(0x7F, 0x00, 0x00, 0xFF); - const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - rdpq_attach(d, NULL); - rdpq_clear(bg_color); - fragment_text_start(text_color); - text_y += fragment_textf(text_x, text_y, "Unrecoverable error:"); - text_y += fragment_textf(text_x, text_y, " %s", format_flashcart_error(menu->flashcart_error)); - if (menu->flashcart_error == FLASHCART_ERROR_OUTDATED) { - text_y += fragment_textf(text_x, text_y, " Minimum supported versions:"); - text_y += fragment_textf(text_x, text_y, " EverDrive-64: ?"); - text_y += fragment_textf(text_x, text_y, " 64drive: ?"); - text_y += fragment_textf(text_x, text_y, " SC64: 2.16.0"); - } + rdpq_clear(RGBA32(0x7F, 0x00, 0x00, 0xFF)); + + const char *firmware_message = ( + "Minimum supported versions:\n" + "EverDrive-64: ?\n" + "64drive: ?\n" + "SC64: 2.16.0" + ); + + component_messagebox_draw( + "UNRECOVERABLE ERROR\n" + "\n" + "%s\n" + "\n" + "%s", + convert_error_message(menu->flashcart_error), + menu->flashcart_error == FLASHCART_ERROR_OUTDATED ? firmware_message : "" + ); rdpq_detach_show(); } diff --git a/src/menu/views/file_info.c b/src/menu/views/file_info.c index 654dac32..105751b2 100644 --- a/src/menu/views/file_info.c +++ b/src/menu/views/file_info.c @@ -1,215 +1,51 @@ #include -#include -#include -#include "../components/components.h" -#include "../png_decoder.h" -#include "../rom_database.h" -#include "fragments/fragments.h" #include "utils/fs.h" -#include "utils/str_utils.h" #include "views.h" -#ifndef ROM_BOXART_PATH -#define ROM_BOXART_PATH "/menu/boxart" -#endif +static const char *n64_rom_extensions[] = { "z64", "n64", "v64", NULL }; +static const char *text_extensions[] = { "txt", NULL }; +static const char *config_extensions[] = { "ini", "cfg", "yml", "yaml", "toml", NULL }; +static const char *save_extensions[] = { "sav", "eep", "eeprom", "sra", "srm", "ram", "fla", "flashram", NULL }; +static const char *patch_extensions[] = { "ips", "aps", "pps", "xdelta", NULL }; +static const char *archive_extensions[] = { "zip", "rar", "7z", "tar", "gz", NULL }; +static const char *image_extensions[] = { "png", "jpg", "gif", NULL }; +static const char *music_extensions[] = { "mp3", "wav", "ogg", "wma", "flac", NULL }; +static const char *dexdrive_extensions[] = { "mpk", NULL }; +static const char *emulator_extensions[] = { "emu", NULL }; static FILINFO info; -static const char *n64_rom_extensions[] = { "z64", "n64", "v64", NULL }; -static rom_header_t rom_header; - -static bool boxart_image_loading; -static surface_t *boxart_image; -static char *format_rom_endian (uint32_t endian) { - switch (endian) - { - case ROM_BIG_ENDIAN: - case IPL_BIG_ENDIAN: - return "Big (default)"; // expected - break; - case ROM_LITTLE_ENDIAN: - return "Little"; - break; - case ROM_MID_LITTLE_ENDIAN: - return "Middle Little"; - break; - case ROM_MID_BIG_ENDIAN: - return "Middle Big"; - break; - default: - return "Unknown"; - break; +static char *format_file_type (char *name, bool is_directory) { + if (is_directory) { + return ""; + } if (file_has_extensions(name, n64_rom_extensions)) { + return " Type: N64 ROM\n"; + } else if (file_has_extensions(name, text_extensions)) { + return " Type: Text file\n"; + } else if (file_has_extensions(name, config_extensions)) { + return " Type: Config file\n"; + } else if (file_has_extensions(name, save_extensions)) { + return " Type: N64 save\n"; + } else if (file_has_extensions(name, patch_extensions)) { + return " Type: ROM patch\n"; + } else if (file_has_extensions(name, archive_extensions)) { + return " Type: Archive\n"; + } else if (file_has_extensions(name, image_extensions)) { + return " Type: Image file\n"; + } else if (file_has_extensions(name, music_extensions)) { + return " Type: Music file\n"; + } else if (file_has_extensions(name, dexdrive_extensions)) { + return " Type: DexDrive CPak backup file\n"; + } else if (file_has_extensions(name, emulator_extensions)) { + return " Type: Emulator file\n"; } + return " Type: Unknown file\n"; } -static char *format_rom_media_type (uint8_t type) { - switch (type) - { - case N64_CART: - return "Cartridge"; - break; - case N64_DISK: - return "Disk"; - break; - case N64_CART_EXPANDABLE: - return "Cart Expandable"; - break; - case N64_DISK_EXPANDABLE: - return "Disk Expandable"; - break; - case N64_ALECK64: - return "Aleck64"; - break; - default: - return "Unknown"; - break; - } -} - -static char *format_rom_destination_market (uint8_t market_type) { - // TODO: These are all assumptions and should be corrected if required. - switch (market_type) { - case MARKET_ALL: - return "All"; - case MARKET_BRAZIL: - return "Brazil (MPAL)"; - case MARKET_CHINA: - return "China"; - case MARKET_GERMANY: - return "Germany (PAL)"; - case MARKET_USA: - return "USA (NTSC)"; - case MARKET_FRANCE: - return "France (PAL)"; - case MARKET_NETHERLANDS: - return "Netherlands (PAL)"; - case MARKET_ITALY: - return "Italy (PAL)"; - case MARKET_JAPAN: - return "Japan (NTSC)"; - case MARKET_KOREA: - return "Korea"; - case MARKET_CANADA: - return "Canada"; - case MARKET_SPAIN: - return "Spain (PAL)"; - case MARKET_AUSTRAILA: - return "Austraila (PAL)"; - case MARKET_SCANDINAVAIA: - return "Scandinavaia"; - case MARKET_GATEWAY64_NTSC: - return "Gateway (NTSC)"; - case MARKET_GATEWAY64_PAL: - return "Gateway (PAL)"; - case MARKET_PAL_GENERIC: - return "Generic (PAL)"; - case MARKET_PAL_X: //FIXME: some AUS ROM's use this so not only EUR - case MARKET_PAL_Y: - case MARKET_PAL_Z: - return "Unknown (PAL)"; - - default: - return "Unknown"; - } -} - -static char *format_rom_save_type (uint8_t type) { - switch (type) - { - case DB_SAVE_TYPE_EEPROM_4K: - return "EEPROM 4K"; - break; - case DB_SAVE_TYPE_EEPROM_16K: - return "EEPROM 16K"; - break; - case DB_SAVE_TYPE_SRAM: - return "SRAM"; - break; - case DB_SAVE_TYPE_SRAM_BANKED: - return "SRAM Banked"; - break; - case DB_SAVE_TYPE_SRAM_128K: - return "SRAM 128K [ED64]"; - break; - case DB_SAVE_TYPE_FLASHRAM: - return "FLASH RAM"; - break; - case DB_SAVE_TYPE_CPAK: - return "Controller PAK"; - break; - default: - return "Unknown"; - break; - } -} - -static char *format_rom_memory_type (uint8_t type) { - switch (type) - { - case DB_MEMORY_EXPANSION_REQUIRED: - return "Required"; - break; - case DB_MEMORY_EXPANSION_RECOMMENDED: - return "Recommended"; - break; - case DB_MEMORY_EXPANSION_SUGGESTED: - return "Suggested"; - break; - case DB_MEMORY_EXPANSION_FAULTY: - return "Ensure Patched"; - break; - default: - return "Not Required"; - break; - } -} - - -static char *format_file_type (void) { - // TODO: should be at least a switch statement! - if (str_endswith(info.fname, ".z64", false) || - str_endswith(info.fname, ".n64", false) || - str_endswith(info.fname, ".v64", false) || - str_endswith(info.fname, ".rom", false) - ) { - // TODO: check the necessary bytes in the header to ensure! - return "N64 ROM"; - } - else if (str_endswith(info.fname, ".txt", false)) { - return "Text File"; - } - else if (str_endswith(info.fname, ".ini", false)) { - return "INI File"; - } - else if (str_endswith(info.fname, ".yml", false) || str_endswith(info.fname, ".yaml", false)) { - return "YAML File"; - } - else if (str_endswith(info.fname, ".toml", false)) { - return "TOML File"; - } - else if (str_endswith(info.fname, ".sav", false) || str_endswith(info.fname, ".eep", false) || str_endswith(info.fname, ".sra", false) || str_endswith(info.fname, ".srm", false)|| str_endswith(info.fname, ".fla", false)) { - return "ROM Save File"; - } - else if (str_endswith(info.fname, ".ips", false) || str_endswith(info.fname, ".aps", false) || str_endswith(info.fname, ".pps", false) || str_endswith(info.fname, ".xdelta", false)) { - return "ROM Patch File"; - } - else if (str_endswith(info.fname, ".zip", false)) { - return "ZIP Archive"; - } - else if (str_endswith(info.fname, ".mpk", false)) { - return "DexDrive CPak Backup File"; - } - else if (str_endswith(info.fname, ".emu", false)) { - return "Emulator File"; - } - else { - return "Unknown File"; - } -} static void process (menu_t *menu) { if (menu->actions.back) { @@ -217,152 +53,57 @@ static void process (menu_t *menu) { } } -static void menu_fileinfo_draw_unknown_info(surface_t *d, layout_t *layout) { - const int text_x = layout->offset_x + layout->offset_text_x; - int text_y = layout->offset_y + layout->offset_text_y; - text_y += fragment_textf(text_x, text_y, "File Information:\n\n"); - text_y += fragment_textf(text_x, text_y, " Name:\n\n %s\n", info.fname); - text_y += fragment_textf(text_x, text_y, " Size:\n\n %d Bytes\n", (int)info.fsize); - text_y += fragment_textf(text_x, text_y, " Attributes:\n\n %s%s%s%s%s\n", - ((info.fattrib & AM_DIR) ? " Directory" : "File"), - ((info.fattrib & AM_RDO) ? " | ReadOnly" : ""), - ((info.fattrib & AM_SYS) ? " | System" : ""), - ((info.fattrib & AM_ARC) ? " | Archive" : ""), - ((info.fattrib & AM_HID) ? " | Hidden" : "") - ); - - text_y += fragment_textf(text_x, text_y, " Modified Timestamp:\n\n %u-%02u-%02u, %02u:%02u\n", - (info.fdate >> 9) + 1980, - info.fdate >> 5 & 15, - info.fdate & 31, - info.ftime >> 11, - info.ftime >> 5 & 63 - ); - - text_y += fragment_textf(text_x, text_y, " Type:\n\n %s\n", format_file_type()); -} - -static void menu_fileinfo_draw_n64_rom_info(surface_t *d, layout_t *layout) { - - const int text_x = layout->offset_x + layout->offset_text_x; - int text_y = layout->offset_y + layout->offset_text_y; - text_y += fragment_textf(text_x, text_y, "File Information:\n\n"); - text_y += fragment_textf(text_x, text_y, " Name:\n\n %s\n", info.fname); - text_y += fragment_textf(text_x, text_y, " Size:\n\n %d Bytes\n", (int)info.fsize); - text_y += fragment_textf(text_x, text_y, " Modified Timestamp:\n\n %u-%02u-%02u, %02u:%02u\n", - (info.fdate >> 9) + 1980, - info.fdate >> 5 & 15, - info.fdate & 31, - info.ftime >> 11, - info.ftime >> 5 & 63 - ); - - text_y += fragment_textf(text_x, text_y, "\n"); - text_y += fragment_textf(text_x, text_y, "N64 ROM Information:\n"); - text_y += fragment_textf(text_x, text_y, " Endian: %s\n", format_rom_endian(rom_header.config_flags)); - text_y += fragment_textf(text_x, text_y, " Title: %s\n", rom_header.title); - text_y += fragment_textf(text_x, text_y, " Media Type: %c - %s\n", rom_header.metadata.media_type, format_rom_media_type(rom_header.metadata.media_type)); - text_y += fragment_textf(text_x, text_y, " Unique ID: %.2s\n", (char*)&(rom_header.metadata.unique_identifier)); - text_y += fragment_textf(text_x, text_y, " Destination Market: %c - %s\n", rom_header.metadata.destination_market, format_rom_destination_market(rom_header.metadata.destination_market)); - text_y += fragment_textf(text_x, text_y, " Version: %hhu\n", rom_header.version); - text_y += fragment_textf(text_x, text_y, " Checksum: 0x%016llX\n", rom_header.checksum); - text_y += fragment_textf(text_x, text_y, " Save Type: %s\n", format_rom_save_type(rom_db_match_save_type(rom_header))); - text_y += fragment_textf(text_x, text_y, " Expansion PAK: %s\n", format_rom_memory_type(rom_db_match_expansion_pak(rom_header))); - // TODO: Should Extra Info be optional. - text_y += fragment_textf(text_x, text_y, " Extra Info:"); - if ((rom_header.clock_rate & 0xFFFFFFF0) != 0) { - text_y += fragment_textf(text_x, text_y, " Clock Rate?!: %d.%02dMHz\n", (rom_header.clock_rate & 0xFFFFFFF0) /1000000, (rom_header.clock_rate & 0xFFFFFFF0) % 1000000); - } - text_y += fragment_textf(text_x, text_y, " Boot address: 0x%08lX\n", rom_header.boot_address); - text_y += fragment_textf(text_x, text_y, " SDK Version: %d\n", rom_header.sdk_version); - - if (boxart_image) { - uint16_t x = (640 - 150) - (boxart_image->width / 2); - uint16_t y = (480 - 150) - (boxart_image->height / 2); - rdpq_set_mode_copy(false); - rdpq_tex_blit(boxart_image, x, y, NULL); - } -} - static void draw (menu_t *menu, surface_t *d) { - layout_t *layout = layout_get(); - - const int text_x = layout->offset_x + layout->offset_text_x; - int text_y = layout->offset_y + layout->offset_text_y; - - const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - rdpq_attach(d, NULL); - // Background - component_background_draw(menu->components.background); + component_background_draw(); - // Layout - fragment_borders(d); + component_layout_draw(); - // Text start - fragment_text_start(text_color); + component_main_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "ENTRY INFORMATION\n" + "\n" + "%s", + menu->browser.list[menu->browser.selected].name + ); - if (file_has_extensions(info.fname, n64_rom_extensions)) { - menu_fileinfo_draw_n64_rom_info(d, layout); - } else { - menu_fileinfo_draw_unknown_info(d, layout); - } + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "\n" + "\n" + "\n" + " Size: %d bytes\n" + " Attributes: %s%s%s%s%s\n" + "%s" + " Modified: %u-%02u-%02u %02u:%02u", + info.fsize, + (info.fattrib & AM_DIR) ? "Directory " : "File ", + (info.fattrib & AM_RDO) ? "| Read only " : "", + (info.fattrib & AM_SYS) ? "| System " : "", + (info.fattrib & AM_ARC) ? "| Archive " : "", + (info.fattrib & AM_HID) ? "| Hidden " : "", + format_file_type(info.fname, info.fattrib & AM_DIR), + (info.fdate >> 9) + 1980, info.fdate >> 5 & 0x0F, info.fdate & 0x1F, info.ftime >> 11, info.ftime >> 5 & 0x3F + ); - // Ensure RDP mode and loaded texture dont mess up font drawing. - fragment_text_start(text_color); - - // Actions bar - text_y = layout->actions_y + layout->offset_text_y; - text_y += fragment_textf(text_x, text_y, "B: Exit"); + component_actions_bar_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "B: Exit" + ); rdpq_detach_show(); } -static void boxart_image_callback (png_err_t err, surface_t *decoded_image, void *callback_data) { - boxart_image_loading = false; - boxart_image = decoded_image; -} - -/* loads a PNG image for a given ROM ID from ROM_BOXART_PATH. e.g. sd:/menu/boxart/.png */ -static void boxart_image_load (uint16_t id) { - char sd_boxart_path[32]; - sprintf(sd_boxart_path, "%s/%.2s.png", ROM_BOXART_PATH, (char *) (&id)); - - if (png_decode_start(sd_boxart_path, 158, 112, boxart_image_callback, NULL) != PNG_OK) { - debugf("Error loading boxart image\n"); - } -} - -static void deinit (menu_t *menu) { - if (boxart_image_loading) { - png_decode_abort(); - } - - if (boxart_image) { - surface_free(boxart_image); - free(boxart_image); - } -} - void view_file_info_init (menu_t *menu) { - boxart_image_loading = true; - boxart_image = NULL; - - char *file_name = menu->browser.list[menu->browser.selected].name; - path_t *file = path_clone(menu->browser.directory); - path_push(file, file_name); + path_push(file, menu->browser.list[menu->browser.selected].name); - if (f_stat(path_get(file), &info) != FR_OK) { - menu->next_mode = MENU_MODE_ERROR; - } - - if (file_has_extensions(file_name, n64_rom_extensions)) { - rom_header = file_read_rom_header(path_get(file)); - - boxart_image_load(rom_header.metadata.unique_identifier); + if (f_stat(strip_sd_prefix(path_get(file)), &info) != FR_OK) { + menu_show_error(menu, "Couldn't obtain file information"); } path_free(file); @@ -372,8 +113,4 @@ void view_file_info_display (menu_t *menu, surface_t *display) { process(menu); draw(menu, display); - - if (menu->next_mode != MENU_MODE_FILE_INFO) { - deinit(menu); - } } diff --git a/src/menu/views/fragments/fragments.c b/src/menu/views/fragments/fragments.c deleted file mode 100644 index 13d3f87d..00000000 --- a/src/menu/views/fragments/fragments.c +++ /dev/null @@ -1,108 +0,0 @@ -#include - -#include - -#include "fragments.h" -#include "../../assets.h" - - -// TODO: Prepare layout for PAL display -static layout_t layout = { - .offset_x = 32, - .offset_y = 24, - - .offset_text_x = 10, - .offset_text_y = 7, - - .line_height = 18, - .scrollbar_width = 10, - .progressbar_height = 16, - - .border_thickness = 5, - - .main_lines = 20, - - .actions_x = 20, - .actions_y = 405, - .actions_lines = 2, -}; - - -layout_t *layout_get(void) { - return &layout; -} - -void fragment_borders (surface_t *d) { - widget_border( - layout.offset_x, - layout.offset_y, - d->width - layout.offset_x, - d->height - layout.offset_y, - layout.border_thickness - ); - widget_horizontal_line( - layout.offset_x, - d->width - layout.offset_x, - layout.offset_y + ((layout.main_lines + 1) * layout.line_height), - layout.border_thickness - ); -} - -void fragment_scrollbar (surface_t *d, int position, int items) { - widget_scrollbar( - d->width - layout.offset_x - layout.scrollbar_width, - layout.offset_y, - layout.scrollbar_width, - (layout.main_lines + 1) * layout.line_height, - position, - items, - layout.main_lines - ); -} - -void fragment_progressbar (surface_t *d, float progress) { - widget_progressbar ( - layout.offset_x, - layout.offset_y + ((layout.main_lines + 1) * layout.line_height) - layout.progressbar_height, - d->width - (layout.offset_x * 2), - layout.progressbar_height, - progress - ); -} - -void fragment_text_start (color_t color) { - rdpq_font_begin(color); -} - -void fragment_text_set_color (color_t color) { - rdpq_set_prim_color(color); -} - -int fragment_textf (int x, int y, char *fmt, ...) { - char buffer[64]; - - assets_t *assets = assets_get(); - - rdpq_font_position(x, y + assets->font_height); - - va_list va; - va_start(va, fmt); - int n = vsnprintf(buffer, sizeof(buffer), fmt, va); - va_end(va); - - rdpq_font_printn(assets->font, buffer, n); - - return layout.line_height; -} - -void fragment_loader (surface_t *d) { - const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - - const int offset_x = 248; - const int offset_y = 212; - const int text_offset_x = -39; - - widget_border(offset_x, offset_y, d->width - offset_x, d->height - offset_y, layout.border_thickness); - fragment_text_start(text_color); - fragment_textf((d->width / 2) + text_offset_x, (d->height / 2) - (layout.line_height / 2), "Loading…"); -} diff --git a/src/menu/views/fragments/fragments.h b/src/menu/views/fragments/fragments.h deleted file mode 100644 index 1f9c45d3..00000000 --- a/src/menu/views/fragments/fragments.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file fragments.h - * @brief Menu View Fragments - * @ingroup menu - */ - -#ifndef FRAGMENTS_H__ -#define FRAGMENTS_H__ - - -#include - -/** - * @addtogroup view_fragments - * @{ - */ - - -void widget_horizontal_line (int x1, int x2, int y, int thickness); -void widget_border (int x1, int y1, int x2, int y2, int thickness); -void widget_scrollbar (int x, int y, int width, int height, int position, int items, int visible_items); -void widget_progressbar (int x, int y, int width, int height, float progress); - - -/** @brief Layout Structure */ -typedef struct { - int offset_x; - int offset_y; - - int offset_text_x; - int offset_text_y; - - int line_height; - int scrollbar_width; - int progressbar_height; - - int border_thickness; - - int main_lines; - - int actions_x; - int actions_y; - int actions_lines; -} layout_t; - - -layout_t *layout_get(void); -void fragment_borders (surface_t *d); -void fragment_scrollbar (surface_t *d, int position, int items); -void fragment_progressbar (surface_t *d, float progress); -void fragment_text_start (color_t color); -void fragment_text_set_color (color_t color); -int fragment_textf (int x, int y, char *fmt, ...); -void fragment_loader (surface_t *d); - - -/** @} */ /* view_fragments */ - -#endif diff --git a/src/menu/views/fragments/widgets.c b/src/menu/views/fragments/widgets.c deleted file mode 100644 index 502887cd..00000000 --- a/src/menu/views/fragments/widgets.c +++ /dev/null @@ -1,55 +0,0 @@ -#include - - -void widget_horizontal_line (int x1, int x2, int y, int thickness) { - color_t line_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - - rdpq_set_mode_fill(line_color); - - rdpq_fill_rectangle(x1, y, x2, y + thickness); -} - -void widget_border (int x1, int y1, int x2, int y2, int thickness) { - color_t border_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - - rdpq_set_mode_fill(border_color); - - rdpq_fill_rectangle(x1 - thickness, y1 - thickness, x2 + thickness, y1); - rdpq_fill_rectangle(x1 - thickness, y2, x2 + thickness, y2 + thickness); - - rdpq_fill_rectangle(x1 - thickness, y1, x1, y2); - rdpq_fill_rectangle(x2, y1, x2 + thickness, y2); -} - -void widget_scrollbar (int x, int y, int width, int height, int position, int items, int visible_items) { - color_t bg_color = RGBA32(0x3F, 0x3F, 0x3F, 0xFF); - color_t inactive_color = RGBA32(0x5F, 0x5F, 0x5F, 0xFF); - color_t active_color = RGBA32(0x7F, 0x7F, 0x7F, 0xFF); - - if (items < 2 || items <= visible_items) { - rdpq_set_mode_fill(inactive_color); - rdpq_fill_rectangle(x, y, x + width, y + height); - } else { - int scroll_height = (int) ((visible_items / (float) (items)) * height); - float scroll_position = ((position / (float) (items - 1)) * (height - scroll_height)); - - rdpq_set_mode_fill(bg_color); - rdpq_fill_rectangle(x, y, x + width, y + height); - - rdpq_set_fill_color(active_color); - rdpq_fill_rectangle(x, y + scroll_position, x + width, y + scroll_position + scroll_height); - } -} - -void widget_progressbar (int x, int y, int width, int height, float progress) { - color_t bg_color = RGBA32(0x3F, 0x3F, 0x3F, 0xFF); - color_t fg_color = RGBA32(0x7F, 0x7F, 0x7F, 0xFF); - - float progress_width = progress * width; - - rdpq_set_fill_color(fg_color); - rdpq_fill_rectangle(x, y, x + progress_width, y + height); - - rdpq_set_mode_fill(bg_color); - rdpq_fill_rectangle(x + progress_width, y, x + width, y + height); -} diff --git a/src/menu/views/image_viewer.c b/src/menu/views/image_viewer.c index 932ee563..2c452dff 100644 --- a/src/menu/views/image_viewer.c +++ b/src/menu/views/image_viewer.c @@ -1,24 +1,52 @@ -#include #include -#include "../components/components.h" #include "../png_decoder.h" -#include "fragments/fragments.h" #include "views.h" +static bool show_message; static bool image_loading; static bool image_set_as_background; static surface_t *image; +static char *convert_error_message (png_err_t err) { + switch (err) { + case PNG_ERR_INT: return "Internal PNG decoder error"; + case PNG_ERR_BUSY: return "PNG decode already in process"; + case PNG_ERR_OUT_OF_MEM: return "PNG decode failed due to insufficient memory"; + case PNG_ERR_NO_FILE: return "PNG decoder couldn't open file"; + case PNG_ERR_BAD_FILE: return "Invalid PNG file"; + default: return "Unknown PNG decoder error"; + } +} + +static void image_callback (png_err_t err, surface_t *decoded_image, void *callback_data) { + menu_t *menu = (menu_t *) (callback_data); + + image_loading = false; + image = decoded_image; + + if (err != PNG_OK) { + menu_show_error(menu, convert_error_message(err)); + } +} + + static void process (menu_t *menu) { if (menu->actions.back) { - menu->next_mode = MENU_MODE_BROWSER; - } else if (menu->actions.enter) { - if (image) { + if (show_message) { + show_message = false; + } else { menu->next_mode = MENU_MODE_BROWSER; + } + } else if (menu->actions.enter && image) { + if (show_message) { + show_message = false; image_set_as_background = true; + menu->next_mode = MENU_MODE_BROWSER; + } else { + show_message = true; } } } @@ -27,40 +55,42 @@ static void draw (menu_t *menu, surface_t *d) { if (!image) { rdpq_attach(d, NULL); - component_background_draw(menu->components.background); + component_background_draw(); - fragment_loader(d); + component_loader_draw(png_decoder_get_progress()); } else { rdpq_attach_clear(d, NULL); uint16_t x = (d->width / 2) - (image->width / 2); uint16_t y = (d->height / 2) - (image->height / 2); - rdpq_set_mode_copy(false); - rdpq_tex_blit(image, x, y, NULL); + rdpq_mode_push(); + rdpq_set_mode_copy(false); + rdpq_tex_blit(image, x, y, NULL); + rdpq_mode_pop(); + + if (show_message) { + component_messagebox_draw( + "Set \"%s\" as background image?\n\n" + "A: Yes, B: Back", + menu->browser.list[menu->browser.selected].name + ); + } else if (image_set_as_background) { + component_messagebox_draw("Preparing background…"); + } } rdpq_detach_show(); } -static void image_callback (png_err_t err, surface_t *decoded_image, void *callback_data) { - image_loading = false; - image = decoded_image; - - if (err != PNG_OK) { - menu_t *menu = (menu_t *) (callback_data); - menu->next_mode = MENU_MODE_ERROR; - } -} - static void deinit (menu_t *menu) { if (image_loading) { - png_decode_abort(); + png_decoder_abort(); } if (image) { if (image_set_as_background) { - component_background_replace_image(menu->components.background, image); + component_background_replace_image(image); } else { surface_free(image); free(image); @@ -70,17 +100,17 @@ static void deinit (menu_t *menu) { void view_image_viewer_init (menu_t *menu) { - image_loading = false; + show_message = false; + image_loading = true; image_set_as_background = false; image = NULL; path_t *path = path_clone(menu->browser.directory); path_push(path, menu->browser.list[menu->browser.selected].name); - if (png_decode_start(path_get(path), 640, 480, image_callback, menu) == PNG_OK) { - image_loading = true; - } else { - menu->next_mode = MENU_MODE_ERROR; + png_err_t err = png_decoder_start(path_get(path), 640, 480, image_callback, menu); + if (err != PNG_OK) { + menu_show_error(menu, convert_error_message(err)); } path_free(path); diff --git a/src/menu/views/load.c b/src/menu/views/load.c index 369ccf4a..56d9158b 100644 --- a/src/menu/views/load.c +++ b/src/menu/views/load.c @@ -1,22 +1,19 @@ -#include - -#include "../components/components.h" #include "../rom_database.h" #include "boot/boot.h" #include "flashcart/flashcart.h" -#include "fragments/fragments.h" #include "views.h" static bool load_pending; static rom_header_t rom_header; +static component_boxart_t *boxart; -static char *format_rom_endian (uint32_t endian) { +static char *format_rom_endian (rom_endian_type_t endian) { switch (endian) { case ROM_BIG_ENDIAN: case IPL_BIG_ENDIAN: - return "Big (native)"; + return "Big (default)"; case ROM_LITTLE_ENDIAN: return "Little (unsupported)"; case ROM_MID_BIG_ENDIAN: @@ -26,7 +23,7 @@ static char *format_rom_endian (uint32_t endian) { } } -static char *format_rom_media_type (uint8_t media_type) { +static char *format_rom_media_type (rom_media_type_t media_type) { switch (media_type) { case N64_CART: return "Cartridge"; @@ -43,7 +40,7 @@ static char *format_rom_media_type (uint8_t media_type) { } } -static char *format_rom_destination_market (uint8_t market_type) { +static char *format_rom_destination_market (rom_destination_market_t market_type) { // TODO: These are all assumptions and should be corrected if required. switch (market_type) { case MARKET_ALL: @@ -80,17 +77,16 @@ static char *format_rom_destination_market (uint8_t market_type) { return "Gateway (PAL)"; case MARKET_PAL_GENERIC: return "Generic (PAL)"; - case MARKET_PAL_X: //FIXME: some AUS ROM's use this so not only EUR + case MARKET_PAL_X: // FIXME: some AUS ROM's use this so not only EUR case MARKET_PAL_Y: case MARKET_PAL_Z: return "Unknown (PAL)"; - default: return "Unknown"; } } -static char *format_rom_save_type (uint8_t save_type) { +static char *format_rom_save_type (db_savetype_t save_type) { switch (save_type) { case DB_SAVE_TYPE_NONE: return "None"; @@ -107,34 +103,113 @@ static char *format_rom_save_type (uint8_t save_type) { case DB_SAVE_TYPE_FLASHRAM: return "FlashRAM"; case DB_SAVE_TYPE_CPAK: - return "Controller PAK"; + return "Controller Pak"; default: return "Unknown"; } } -static char *format_rom_memory_type (uint8_t memory_type) { - switch (memory_type) - { - case DB_MEMORY_EXPANSION_REQUIRED: - return "Required"; - break; - case DB_MEMORY_EXPANSION_RECOMMENDED: - return "Recommended"; - break; - case DB_MEMORY_EXPANSION_SUGGESTED: - return "Suggested"; - break; - case DB_MEMORY_EXPANSION_FAULTY: - return "May require ROM patch"; - break; - default: - return "Not required"; - break; +static char *format_rom_expansion_pak_info (rom_memorytype_t expansion_pak_info) { + switch (expansion_pak_info) { + case DB_MEMORY_EXPANSION_REQUIRED: + return "Required"; + case DB_MEMORY_EXPANSION_RECOMMENDED: + return "Recommended"; + case DB_MEMORY_EXPANSION_SUGGESTED: + return "Suggested"; + case DB_MEMORY_EXPANSION_FAULTY: + return "May require ROM patch"; + default: + return "Not required"; } } -static flashcart_save_type_t convert_save_type (uint8_t save_type) { + +static void process (menu_t *menu) { + if (menu->actions.enter) { + load_pending = true; + } else if (menu->actions.back) { + menu->next_mode = MENU_MODE_BROWSER; + } +} + +static void draw (menu_t *menu, surface_t *d) { + rdpq_attach(d, NULL); + + component_background_draw(); + + if (load_pending) { + component_loader_draw(0.0f); + } else { + component_layout_draw(); + + component_main_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "N64 ROM information\n" + "\n" + "%s", + menu->browser.list[menu->browser.selected].name + ); + + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "\n" + "\n" + "\n" + " Endian: %s\n" + " Title: %.20s\n" + " Media type: %c - %s\n" + " Unique ID: %.2s\n" + " Destination market: %c - %s\n" + " Version: %hhu\n" + " Checksum: 0x%016llX\n" + " Save type: %s\n" + " Expansion PAK: %s\n" + "\n" + " Extra info:\n" + " Boot address: 0x%08lX\n" + " SDK version: %.1f%c", + format_rom_endian(rom_header.config_flags), + rom_header.title, + rom_header.metadata.media_type, format_rom_media_type(rom_header.metadata.media_type), + (char *) (&rom_header.metadata.unique_identifier), + rom_header.metadata.destination_market, format_rom_destination_market(rom_header.metadata.destination_market), + rom_header.version, + rom_header.checksum, + format_rom_save_type(rom_db_match_save_type(rom_header)), + format_rom_expansion_pak_info(rom_db_match_expansion_pak(rom_header)), + rom_header.boot_address, + (float) ((rom_header.sdk_version >> 8) & 0xFF) / 10.0f, (char) (rom_header.sdk_version & 0xFF) + ); + + component_actions_bar_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "A: Load and run ROM\n" + "B: Exit" + ); + + component_boxart_draw(boxart); + } + + rdpq_detach_show(); +} + +static void draw_progress (float progress) { + surface_t *d = display_try_get(); + + if (d) { + rdpq_attach(d, NULL); + + component_background_draw(); + + component_loader_draw(progress); + + rdpq_detach_show(); + } +} + +static flashcart_save_type_t convert_save_type (db_savetype_t save_type) { switch (save_type) { case DB_SAVE_TYPE_EEPROM_4K: return FLASHCART_SAVE_TYPE_EEPROM_4K; @@ -153,7 +228,6 @@ static flashcart_save_type_t convert_save_type (uint8_t save_type) { } } - static void load (menu_t *menu) { menu->next_mode = MENU_MODE_BOOT; @@ -161,7 +235,7 @@ static void load (menu_t *menu) { path_push(path, menu->browser.list[menu->browser.selected].name); bool byte_swap = (rom_header.config_flags == ROM_MID_BIG_ENDIAN); - menu->flashcart_error = flashcart_load_rom(path_get(path), byte_swap); + menu->flashcart_error = flashcart_load_rom(path_get(path), byte_swap, draw_progress); if (menu->flashcart_error != FLASHCART_OK) { menu->next_mode = MENU_MODE_FAULT; path_free(path); @@ -181,60 +255,12 @@ static void load (menu_t *menu) { path_free(path); menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; - menu->boot_params->reset_type = BOOT_RESET_TYPE_NMI; menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; menu->boot_params->detect_cic_seed = true; } - -static void process (menu_t *menu) { - if (menu->actions.enter) { - load_pending = true; - } else if (menu->actions.back) { - menu->next_mode = MENU_MODE_BROWSER; - } -} - -static void draw (menu_t *menu, surface_t *d) { - layout_t *layout = layout_get(); - - const int text_x = layout->offset_x + layout->offset_text_x; - int text_y = layout->offset_y + layout->offset_text_y; - - const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - - rdpq_attach(d, NULL); - - // Background - component_background_draw(menu->components.background); - - if (load_pending) { - fragment_loader(d); - } else { - // Layout - fragment_borders(d); - - // Text start - fragment_text_start(text_color); - - // Main screen - text_y += fragment_textf(text_x, text_y, "Title: %.20s", rom_header.title); - text_y += fragment_textf(text_x, text_y, "Save type: %s", format_rom_save_type(rom_db_match_save_type(rom_header))); - text_y += fragment_textf(text_x, text_y, "Media type: %c - %s", rom_header.metadata.media_type, format_rom_media_type(rom_header.metadata.media_type)); - text_y += fragment_textf(text_x, text_y, "Unique ID: %.2s", (char*)&(rom_header.metadata.unique_identifier)); - text_y += fragment_textf(text_x, text_y, "Destination market: %c - %s", rom_header.metadata.destination_market, format_rom_destination_market(rom_header.metadata.destination_market)); - text_y += fragment_textf(text_x, text_y, "Version: %hhu", rom_header.version); - text_y += fragment_textf(text_x, text_y, "Checksum: 0x%016llX", rom_header.checksum); - text_y += fragment_textf(text_x, text_y, "ROM endian: %s", format_rom_endian(rom_header.config_flags)); - text_y += fragment_textf(text_x, text_y, "Expansion PAK: %s", format_rom_memory_type(rom_db_match_expansion_pak(rom_header))); - - // Actions bar - text_y = layout->actions_y + layout->offset_text_y; - text_y += fragment_textf(text_x, text_y, "A: Load and run ROM"); - text_y += fragment_textf(text_x, text_y, "B: Exit"); - } - - rdpq_detach_show(); +static void deinit (void) { + component_boxart_free(boxart); } @@ -246,14 +272,22 @@ void view_load_init (menu_t *menu) { rom_header = file_read_rom_header(path_get(path)); + boxart = component_boxart_init(rom_header.metadata.unique_identifier); + path_free(path); } void view_load_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); + if (load_pending) { load_pending = false; load(menu); } + + if (menu->next_mode != MENU_MODE_LOAD) { + deinit(); + } } diff --git a/src/menu/views/music_player.c b/src/menu/views/music_player.c index 114731a4..768e1304 100644 --- a/src/menu/views/music_player.c +++ b/src/menu/views/music_player.c @@ -1,8 +1,4 @@ -#include - -#include "../components/components.h" #include "../mp3_player.h" -#include "fragments/fragments.h" #include "views.h" @@ -13,20 +9,18 @@ static int unmute_counter; -static void format_name (char *buffer, char *name) { - int cutoff_length = 57; - int name_length = strlen(name); - strcpy(buffer, " "); - if (name_length > cutoff_length) { - strncat(buffer, name, cutoff_length - 1); - strcat(buffer, "…"); - } else { - strcat(buffer, name); +static char *convert_error_message (mp3player_err_t err) { + switch (err) { + case MP3PLAYER_ERR_OUT_OF_MEM: return "MP3 player failed due to insufficient memory"; + case MP3PLAYER_ERR_IO: return "I/O error during MP3 playback"; + case MP3PLAYER_ERR_NO_FILE: return "No MP3 file is loaded"; + case MP3PLAYER_ERR_INVALID_FILE: return "Invalid MP3 file"; + default: return "Unknown MP3 player error"; } } static void format_elapsed_duration (char *buffer, float elapsed, float duration) { - strcpy(buffer, " "); + strcpy(buffer, ""); if (duration >= 3600) { sprintf(buffer + strlen(buffer), "%02d:", (int) (elapsed) / 3600); @@ -43,6 +37,8 @@ static void format_elapsed_duration (char *buffer, float elapsed, float duration static void process (menu_t *menu) { + mp3player_err_t err; + if (unmute_counter > 0) { unmute_counter -= 1; if (unmute_counter == 0) { @@ -50,90 +46,94 @@ static void process (menu_t *menu) { } } - if (mp3player_process() != MP3PLAYER_OK) { - menu->next_mode = MENU_MODE_ERROR; + err = mp3player_process(); + if (err != MP3PLAYER_OK) { + menu_show_error(menu, convert_error_message(err)); } else if (menu->actions.back) { menu->next_mode = MENU_MODE_BROWSER; } else if (menu->actions.enter) { - if (mp3player_toggle() != MP3PLAYER_OK) { - menu->next_mode = MENU_MODE_ERROR; + err = mp3player_toggle(); + if (err != MP3PLAYER_OK) { + menu_show_error(menu, convert_error_message(err)); } } else if (menu->actions.go_left || menu->actions.go_right) { mp3player_mute(true); unmute_counter = SEEK_UNMUTE_TIMEOUT; int seconds = (menu->actions.go_left ? -SEEK_SECONDS : SEEK_SECONDS); - if (mp3player_seek(seconds) != MP3PLAYER_OK) { - menu->next_mode = MENU_MODE_ERROR; + err = mp3player_seek(seconds); + if (err != MP3PLAYER_OK) { + menu_show_error(menu, convert_error_message(err)); } } } static void draw (menu_t *menu, surface_t *d) { - char buffer[64]; - - layout_t *layout = layout_get(); - - const int text_x = layout->offset_x + layout->offset_text_x; - int text_y = layout->offset_y + layout->offset_text_y; - - const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - rdpq_attach(d, NULL); - // Background - component_background_draw(menu->components.background); + component_background_draw(); - // Layout - fragment_borders(d); + component_layout_draw(); - // Progressbar - fragment_progressbar(d, mp3player_get_progress()); + component_seekbar_draw(mp3player_get_progress()); - // Text start - fragment_text_start(text_color); + component_main_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "MUSIC PLAYER\n" + "\n" + "%s", + menu->browser.list[menu->browser.selected].name + ); - // Main screen - text_y += fragment_textf(text_x, text_y, "Now playing:"); - format_name(buffer, menu->browser.list[menu->browser.selected].name); - text_y += fragment_textf(text_x, text_y, buffer); + char formatted_track_elapsed_length[64]; - text_y += layout->line_height; - text_y += fragment_textf(text_x, text_y, "Track elapsed / length:"); - format_elapsed_duration(buffer, mp3player_get_duration() * mp3player_get_progress(), mp3player_get_duration()); - text_y += fragment_textf(text_x, text_y, buffer); + format_elapsed_duration( + formatted_track_elapsed_length, + mp3player_get_duration() * mp3player_get_progress(), + mp3player_get_duration() + ); - text_y += layout->line_height; - text_y += fragment_textf(text_x, text_y, "Average bitrate:"); - text_y += fragment_textf(text_x, text_y, " %.0f kbps", mp3player_get_bitrate() / 1000); + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "\n" + "\n" + "\n" + " Track elapsed / length:\n" + " %s\n" + "\n" + " Average bitrate:\n" + " %.0f kbps\n" + "\n" + " Samplerate:\n" + " %d Hz", + formatted_track_elapsed_length, + mp3player_get_bitrate() / 1000, + mp3player_get_samplerate() + ); - text_y += layout->line_height; - text_y += fragment_textf(text_x, text_y, "Samplerate:"); - text_y += fragment_textf(text_x, text_y, " %d Hz", mp3player_get_samplerate()); - - // Actions bar - text_y = layout->actions_y + layout->offset_text_y; - if (mp3player_is_playing()) { - fragment_textf(text_x, text_y, "A: Pause"); - } else if (mp3player_is_finished()) { - fragment_textf(text_x, text_y, "A: Play again"); - } else { - fragment_textf(text_x, text_y, "A: Play"); - } - text_y += layout->line_height; - fragment_textf(text_x, text_y, "B: Exit | Left / Right: Rewind / Fast forward"); + component_actions_bar_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "A: %s\n" + "B: Exit | Left / Right: Rewind / Fast forward", + mp3player_is_playing() ? "Pause" : mp3player_is_finished() ? "Play again" : "Play" + ); rdpq_detach_show(); } +static void deinit (void) { + mp3player_deinit(); +} + void view_music_player_init (menu_t *menu) { - mp3player_err_t error; + mp3player_err_t err; unmute_counter = 0; - error = mp3player_init(); - if (error != MP3PLAYER_OK) { - menu->next_mode = MENU_MODE_ERROR; + err = mp3player_init(); + if (err != MP3PLAYER_OK) { + menu_show_error(menu, convert_error_message(err)); mp3player_deinit(); return; } @@ -141,9 +141,9 @@ void view_music_player_init (menu_t *menu) { path_t *path = path_clone(menu->browser.directory); path_push(path, menu->browser.list[menu->browser.selected].name); - error = mp3player_load(path_get(path)); - if (error != MP3PLAYER_OK) { - menu->next_mode = MENU_MODE_ERROR; + err = mp3player_load(path_get(path)); + if (err != MP3PLAYER_OK) { + menu_show_error(menu, convert_error_message(err)); mp3player_deinit(); } else { mp3player_mute(false); @@ -155,8 +155,10 @@ void view_music_player_init (menu_t *menu) { void view_music_player_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); + if (menu->next_mode != MENU_MODE_MUSIC_PLAYER) { - mp3player_deinit(); + deinit(); } } diff --git a/src/menu/views/startup.c b/src/menu/views/startup.c index 0cddc40b..7fb3837e 100644 --- a/src/menu/views/startup.c +++ b/src/menu/views/startup.c @@ -1,15 +1,7 @@ -#include - -#include "../components/components.h" #include "utils/fs.h" #include "views.h" -#define CACHE_DIRECTORY "sd:/menu/cache" - -#define BACKGROUND_CACHE "sd:/menu/cache/background.data" - - static void process (menu_t *menu) { menu->next_mode = MENU_MODE_BROWSER; } @@ -19,19 +11,13 @@ static void draw (menu_t *menu, surface_t *d) { rdpq_detach_show(); } -static void load (menu_t *menu) { - menu->components.background = component_background_create(BACKGROUND_CACHE); -} - void view_startup_init (menu_t *menu) { - directory_create(CACHE_DIRECTORY); + // Nothing to initialize (yet) } void view_startup_display (menu_t *menu, surface_t *display) { process(menu); draw(menu, display); - - load(menu); } diff --git a/src/menu/views/system_info.c b/src/menu/views/system_info.c index 4bbcae91..d371fdca 100644 --- a/src/menu/views/system_info.c +++ b/src/menu/views/system_info.c @@ -1,28 +1,30 @@ #include -#include -#include "../components/components.h" -#include "fragments/fragments.h" #include "views.h" -char *accessory_type_s( int accessory ) -{ - switch( accessory ) - { +static int controllers; +static int accessory[4]; + + +static char *format_accessory (int controller) { + switch (accessory[controller]) { case ACCESSORY_RUMBLEPAK: - return "[RumblePak]"; + return "[Rumble Pak is inserted]"; case ACCESSORY_MEMPAK: - return "[ControllerPak]"; + return "[Controller Pak is inserted]"; case ACCESSORY_VRU: - return "[VRU]"; + return "[VRU is inserted]"; case ACCESSORY_TRANSFERPAK: - return "[TransferPak]"; + return "[Transfer Pak is inserted]"; + case ACCESSORY_NONE: + return ""; default: - return "Unknown"; + return "[unknown accessory inserted]"; } } + static void process (menu_t *menu) { if (menu->actions.back) { menu->next_mode = MENU_MODE_BROWSER; @@ -30,79 +32,58 @@ static void process (menu_t *menu) { } static void draw (menu_t *menu, surface_t *d) { - - layout_t *layout = layout_get(); - - const int text_x = layout->offset_x + layout->offset_text_x; - int text_y = layout->offset_y + layout->offset_text_y; - - const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); - rdpq_attach(d, NULL); - // Background - component_background_draw(menu->components.background); + component_background_draw(); - // Layout - fragment_borders(d); + component_layout_draw(); - // Text start - fragment_text_start(text_color); + component_main_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "N64 SYSTEM INFORMATION" + ); - text_y += fragment_textf((d->width / 2) - 108, text_y, "N64 SYSTEM INFORMATION\n\n"); + time_t current_time = time(NULL); - text_y += fragment_textf(text_x, text_y, "\n"); + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "\n" + "Current date & time: %s" + "\n" + "Expansion PAK is %sinserted\n" + "\n" + "JoyPad 1 is %sconnected %s\n" + "JoyPad 2 is %sconnected %s\n" + "JoyPad 3 is %sconnected %s\n" + "JoyPad 4 is %sconnected %s\n", + current_time >= 0 ? ctime(¤t_time) : "Unknown\n", + is_memory_expanded() ? "" : "not ", + (controllers & CONTROLLER_1_INSERTED) ? "" : "not ", format_accessory(0), + (controllers & CONTROLLER_2_INSERTED) ? "" : "not ", format_accessory(1), + (controllers & CONTROLLER_3_INSERTED) ? "" : "not ", format_accessory(2), + (controllers & CONTROLLER_4_INSERTED) ? "" : "not ", format_accessory(3) + ); - - time_t current_time = -1; - current_time = time( NULL ); - if( current_time != -1 ) - { - text_y += fragment_textf(text_x, text_y, "Current date & time: %s\n\n", ctime( ¤t_time )); - text_y += fragment_textf(text_x, text_y, "To change the time, use USB App..."); - } - - text_y += fragment_textf(text_x, text_y, "\n"); - - text_y += fragment_textf(text_x, text_y, "Expansion PAK is %sinserted\n", is_memory_expanded() ? "" : "not " ); - - text_y += fragment_textf(text_x, text_y, "\n"); - - int controllers = get_controllers_present(); - - text_y += fragment_textf(text_x, text_y, "JoyPad 1 is %sconnected\n", (controllers & CONTROLLER_1_INSERTED) ? "" : "not " ); - text_y += fragment_textf(text_x, text_y, "JoyPad 2 is %sconnected\n", (controllers & CONTROLLER_2_INSERTED) ? "" : "not " ); - text_y += fragment_textf(text_x, text_y, "JoyPad 3 is %sconnected\n", (controllers & CONTROLLER_3_INSERTED) ? "" : "not " ); - text_y += fragment_textf(text_x, text_y, "JoyPad 4 is %sconnected\n", (controllers & CONTROLLER_4_INSERTED) ? "" : "not " ); - - text_y += fragment_textf(text_x, text_y, "\n"); - - struct controller_data output; - int accessories = get_accessories_present( &output ); - - text_y += fragment_textf(text_x, text_y, "JoyPad 1 Accessory Pak is %sinserted %s\n", (accessories & CONTROLLER_1_INSERTED) ? "" : "not ", - (accessories & CONTROLLER_1_INSERTED) ? accessory_type_s( identify_accessory( 0 ) ) : "" ); - text_y += fragment_textf(text_x, text_y, "JoyPad 2 Accessory Pak is %sinserted %s\n", (accessories & CONTROLLER_2_INSERTED) ? "" : "not ", - (accessories & CONTROLLER_2_INSERTED) ? accessory_type_s( identify_accessory( 1 ) ) : "" ); - text_y += fragment_textf(text_x, text_y, "JoyPad 3 Accessory Pak is %sinserted %s\n", (accessories & CONTROLLER_3_INSERTED) ? "" : "not ", - (accessories & CONTROLLER_3_INSERTED) ? accessory_type_s( identify_accessory( 2 ) ) : "" ); - text_y += fragment_textf(text_x, text_y, "JoyPad 4 Accessory Pak is %sinserted %s\n", (accessories & CONTROLLER_4_INSERTED) ? "" : "not ", - (accessories & CONTROLLER_4_INSERTED) ? accessory_type_s( identify_accessory( 3 ) ) : "" ); - - - // Actions bar - text_y = layout->actions_y + layout->offset_text_y; - text_y += fragment_textf(text_x, text_y, "B: Exit"); + component_actions_bar_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "B: Exit" + ); rdpq_detach_show(); } -void view_system_info_init (menu_t *menu) { - // Nothing to initialize (yet) +void view_system_info_init (menu_t *menu) { + controllers = get_controllers_present(); + + for (int i = 0; i < 4; i++) { + accessory[i] = identify_accessory(i); + } } void view_system_info_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); -} \ No newline at end of file +} diff --git a/src/menu/views/views.h b/src/menu/views/views.h index 991a03d8..1e2c29bd 100644 --- a/src/menu/views/views.h +++ b/src/menu/views/views.h @@ -8,10 +8,10 @@ #define VIEWS_H__ -#include - +#include "../components.h" #include "../menu_state.h" + /** * @addtogroup view * @{ @@ -47,6 +47,9 @@ void view_error_display (menu_t *menu, surface_t *display); void view_fault_init (menu_t *menu); void view_fault_display (menu_t *menu, surface_t *display); +void menu_show_error (menu_t *menu, char *error_message); + /** @} */ /* view */ + #endif diff --git a/src/utils/str_utils.h b/src/utils/str_utils.h deleted file mode 100644 index 9f580361..00000000 --- a/src/utils/str_utils.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef STR_UTILS_H__ -#define STR_UTILS_H__ - - -#include -#include -#include -#include - -// e.g. if (str_endswith(cur_rom, ".z64", false) || str_endswith(cur_rom, ".n64", false)) -static bool str_endswith(const char *str, const char *suffix, bool case_sensitive) { - size_t str_len = strlen(str); - size_t suffix_len = strlen(suffix); - - if (str_len < suffix_len) - return false; - - if (!case_sensitive) { - for (size_t i = 0; i < suffix_len; i++) { - if (tolower(str[str_len - suffix_len + i]) != tolower(suffix[i])) - return false; - } - } else { - for (size_t i = 0; i < suffix_len; i++) { - if (str[str_len - suffix_len + i] != suffix[i]) - return false; - } - } - - return true; -} - -#endif \ No newline at end of file