mirror of
https://github.com/Polprzewodnikowy/N64FlashcartMenu.git
synced 2024-11-25 03:56:54 +01:00
Complete rewrite of menu drawing + code cleanup + ROM cold boot fix
Uses new `rdpq_text` API for font drawing
This commit is contained in:
parent
eb9668c721
commit
8e8f77e55b
12
Makefile
12
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] $@"
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 1c35500e787b3bd2634e1cd0870b246ddffddfe2
|
||||
Subproject commit 4409fe77e208aaef77800b684602d4ea5a74da0b
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
@ -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
|
57
src/boot/reboot.S
Normal file
57
src/boot/reboot.S
Normal file
@ -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
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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) {
|
||||
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 (br != sdram_size) {
|
||||
if (progress) {
|
||||
progress(f_tell(&fil) / (float) (f_size(&fil)));
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
@ -1,54 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libdragon.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @file assets.h
|
||||
* @brief Menu Assets
|
||||
* @ingroup menu
|
||||
*/
|
||||
|
||||
#ifndef ASSETS_H__
|
||||
#define ASSETS_H__
|
||||
|
||||
|
||||
#include <rdpq_font.h>
|
||||
|
||||
|
||||
/** @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
|
52
src/menu/components.h
Normal file
52
src/menu/components.h
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file components.h
|
||||
* @brief Menu Components
|
||||
* @ingroup menu
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_H__
|
||||
#define COMPONENTS_H__
|
||||
|
||||
|
||||
#include <libdragon.h>
|
||||
#include "menu_state.h"
|
||||
|
||||
|
||||
/**
|
||||
* @addtogroup
|
||||
* @{ menu_components
|
||||
*/
|
||||
|
||||
void component_box_draw (int x0, int y0, int x1, int y1, color_t color);
|
||||
void component_border_draw (int x0, int y0, int x1, int y1);
|
||||
void component_layout_draw (void);
|
||||
void component_progressbar_draw (int x0, int y0, int x1, int y1, float progress);
|
||||
void component_seekbar_draw (float progress);
|
||||
void component_loader_draw (float position);
|
||||
void component_scrollbar_draw (int x, int y, int width, int height, int position, int items, int visible_items);
|
||||
void component_file_list_scrollbar_draw (int position, int items, int visible_items);
|
||||
void component_dialog_draw (int width, int height);
|
||||
void component_messagebox_draw (char *fmt, ...);
|
||||
void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...);
|
||||
void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...);
|
||||
|
||||
void component_background_init (char *cache_location);
|
||||
void component_background_free (void);
|
||||
void component_background_replace_image (surface_t *image);
|
||||
void component_background_draw (void);
|
||||
|
||||
void component_file_list_draw (entry_t *list, int entries, int selected);
|
||||
|
||||
typedef struct {
|
||||
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
|
@ -1,13 +1,20 @@
|
||||
#include <fatfs/ff.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
70
src/menu/components/boxart.c
Normal file
70
src/menu/components/boxart.c
Normal file
@ -0,0 +1,70 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../components.h"
|
||||
#include "../path.h"
|
||||
#include "../png_decoder.h"
|
||||
#include "constants.h"
|
||||
|
||||
|
||||
#define BOXART_DIRECTORY "sd:/menu/boxart"
|
||||
|
||||
|
||||
static void png_decoder_callback (png_err_t err, surface_t *decoded_image, void *callback_data) {
|
||||
component_boxart_t *b = (component_boxart_t *) (callback_data);
|
||||
b->loading = false;
|
||||
b->image = decoded_image;
|
||||
}
|
||||
|
||||
|
||||
component_boxart_t *component_boxart_init (uint16_t id) {
|
||||
component_boxart_t *b = calloc(1, sizeof(component_boxart_t));
|
||||
|
||||
if (b) {
|
||||
b->loading = true;
|
||||
|
||||
char *path = alloca(strlen(BOXART_DIRECTORY) + 1 + 6 + 1);
|
||||
sprintf(path, "%s/%c%c.png", BOXART_DIRECTORY, ((id >> 8) & 0xFF), (id & 0xFF));
|
||||
|
||||
if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) {
|
||||
free(b);
|
||||
b = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void component_boxart_free (component_boxart_t *b) {
|
||||
if (b) {
|
||||
if (b->loading) {
|
||||
png_decoder_abort();
|
||||
}
|
||||
if (b->image) {
|
||||
surface_free(b->image);
|
||||
free(b->image);
|
||||
}
|
||||
free(b);
|
||||
}
|
||||
}
|
||||
|
||||
void component_boxart_draw (component_boxart_t *b) {
|
||||
if (b && b->image && b->image->width == BOXART_WIDTH && b->image->height == BOXART_HEIGHT) {
|
||||
rdpq_mode_push();
|
||||
rdpq_set_mode_copy(false);
|
||||
rdpq_tex_blit(
|
||||
b->image,
|
||||
BOXART_X,
|
||||
BOXART_Y,
|
||||
NULL
|
||||
);
|
||||
rdpq_mode_pop();
|
||||
} else {
|
||||
component_box_draw(
|
||||
BOXART_X,
|
||||
BOXART_Y,
|
||||
BOXART_X + BOXART_WIDTH,
|
||||
BOXART_Y + BOXART_HEIGHT,
|
||||
BOXART_LOADING_COLOR
|
||||
);
|
||||
}
|
||||
}
|
182
src/menu/components/common.c
Normal file
182
src/menu/components/common.c
Normal file
@ -0,0 +1,182 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "../components.h"
|
||||
#include "../fonts.h"
|
||||
#include "constants.h"
|
||||
|
||||
|
||||
void component_box_draw (int x0, int y0, int x1, int y1, color_t color) {
|
||||
rdpq_mode_push();
|
||||
rdpq_set_mode_fill(color);
|
||||
|
||||
rdpq_fill_rectangle(x0, y0, x1, y1);
|
||||
rdpq_mode_pop();
|
||||
}
|
||||
|
||||
void component_border_draw (int x0, int y0, int x1, int y1) {
|
||||
rdpq_mode_push();
|
||||
rdpq_set_mode_fill(BORDER_COLOR);
|
||||
|
||||
rdpq_fill_rectangle(x0 - BORDER_THICKNESS, y0 - BORDER_THICKNESS, x1 + BORDER_THICKNESS, y0);
|
||||
rdpq_fill_rectangle(x0 - BORDER_THICKNESS, y1, x1 + BORDER_THICKNESS, y1 + BORDER_THICKNESS);
|
||||
|
||||
rdpq_fill_rectangle(x0 - BORDER_THICKNESS, y0, x0, y1);
|
||||
rdpq_fill_rectangle(x1, y0, x1 + BORDER_THICKNESS, y1);
|
||||
rdpq_mode_pop();
|
||||
}
|
||||
|
||||
void component_layout_draw (void) {
|
||||
component_border_draw(
|
||||
VISIBLE_AREA_X0,
|
||||
VISIBLE_AREA_Y0,
|
||||
VISIBLE_AREA_X1,
|
||||
VISIBLE_AREA_Y1
|
||||
);
|
||||
component_box_draw(
|
||||
VISIBLE_AREA_X0,
|
||||
LAYOUT_ACTIONS_SEPARATOR_Y,
|
||||
VISIBLE_AREA_X1,
|
||||
LAYOUT_ACTIONS_SEPARATOR_Y + BORDER_THICKNESS,
|
||||
BORDER_COLOR
|
||||
);
|
||||
}
|
||||
|
||||
void component_progressbar_draw (int x0, int y0, int x1, int y1, float progress) {
|
||||
float progress_width = progress * (x1 - x0);
|
||||
|
||||
component_box_draw(x0, y0, x0 + progress_width, y1, PROGRESSBAR_DONE_COLOR);
|
||||
component_box_draw(x0 + progress_width, y0, x1, y1, PROGRESSBAR_BG_COLOR);
|
||||
}
|
||||
|
||||
void component_seekbar_draw (float position) {
|
||||
int x0 = SEEKBAR_X;
|
||||
int y0 = SEEKBAR_Y;
|
||||
int x1 = SEEKBAR_X + SEEKBAR_WIDTH;
|
||||
int y1 = SEEKBAR_Y + SEEKBAR_HEIGHT;
|
||||
|
||||
component_border_draw(x0, y0, x1, y1);
|
||||
component_progressbar_draw(x0, y0, x1, y1, position);
|
||||
}
|
||||
|
||||
void component_loader_draw (float progress) {
|
||||
int x0 = LOADER_X;
|
||||
int y0 = LOADER_Y;
|
||||
int x1 = LOADER_X + LOADER_WIDTH;
|
||||
int y1 = LOADER_Y + LOADER_HEIGHT;
|
||||
|
||||
component_border_draw(x0, y0, x1, y1);
|
||||
component_progressbar_draw(x0, y0, x1, y1, progress);
|
||||
}
|
||||
|
||||
void component_scrollbar_draw (int x, int y, int width, int height, int position, int items, int visible_items) {
|
||||
if (items <= 1 || items <= visible_items) {
|
||||
component_box_draw(x, y, x + width, y + height, SCROLLBAR_INACTIVE_COLOR);
|
||||
} else {
|
||||
int scroll_height = (int) ((visible_items / (float) (items)) * height);
|
||||
float scroll_position = ((position / (float) (items - 1)) * (height - scroll_height));
|
||||
|
||||
component_box_draw(x, y, x + width, y + height, SCROLLBAR_BG_COLOR);
|
||||
component_box_draw(x, y + scroll_position, x + width, y + scroll_position + scroll_height, SCROLLBAR_POSITION_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void component_file_list_scrollbar_draw (int position, int items, int visible_items) {
|
||||
component_scrollbar_draw(
|
||||
FILE_LIST_SCROLLBAR_X,
|
||||
FILE_LIST_SCROLLBAR_Y,
|
||||
FILE_LIST_SCROLLBAR_WIDTH,
|
||||
FILE_LIST_SCROLLBAR_HEIGHT,
|
||||
position,
|
||||
items,
|
||||
visible_items
|
||||
);
|
||||
}
|
||||
|
||||
void component_dialog_draw (int width, int height) {
|
||||
int x0 = DISPLAY_CENTER_X - (width / 2);
|
||||
int y0 = DISPLAY_CENTER_Y - (height / 2);
|
||||
int x1 = DISPLAY_CENTER_X + (width / 2);
|
||||
int y1 = DISPLAY_CENTER_Y + (height / 2);
|
||||
|
||||
component_border_draw(x0, y0, x1, y1);
|
||||
component_box_draw(x0, y0, x1, y1, DIALOG_BG_COLOR);
|
||||
}
|
||||
|
||||
void component_messagebox_draw (char *fmt, ...) {
|
||||
char buffer[512];
|
||||
size_t nbytes = sizeof(buffer);
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
char *formatted = vasnprintf(buffer, &nbytes, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
int paragraph_nbytes = nbytes;
|
||||
|
||||
rdpq_paragraph_t *paragraph = rdpq_paragraph_build(&(rdpq_textparms_t) {
|
||||
.width = MESSAGEBOX_MAX_WIDTH,
|
||||
.height = VISIBLE_AREA_HEIGHT,
|
||||
.align = ALIGN_CENTER,
|
||||
.valign = VALIGN_CENTER,
|
||||
.wrap = WRAP_WORD
|
||||
}, FNT_DEFAULT, formatted, ¶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
|
||||
);
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/**
|
||||
* @file components.h
|
||||
* @brief Menu Components
|
||||
* @ingroup menu
|
||||
*/
|
||||
|
||||
#ifndef COMPONENTS_H__
|
||||
#define COMPONENTS_H__
|
||||
|
||||
|
||||
#include <libdragon.h>
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
83
src/menu/components/constants.h
Normal file
83
src/menu/components/constants.h
Normal file
@ -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
|
145
src/menu/components/file_list.c
Normal file
145
src/menu/components/file_list.c
Normal file
@ -0,0 +1,145 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
30
src/menu/fonts.c
Normal file
30
src/menu/fonts.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#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();
|
||||
}
|
27
src/menu/fonts.h
Normal file
27
src/menu/fonts.h
Normal file
@ -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
|
@ -3,9 +3,9 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#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);
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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,18 +125,31 @@ 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) {
|
||||
float png_decoder_get_progress (void) {
|
||||
if (!decoder) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return (float) (decoder->decoded_rows) / (decoder->ihdr.height);
|
||||
}
|
||||
|
||||
void png_decoder_poll (void) {
|
||||
if (!decoder) {
|
||||
return;
|
||||
}
|
||||
|
||||
enum spng_errno err;
|
||||
struct spng_row_info row_info;
|
||||
|
||||
@ -148,6 +162,7 @@ void png_poll (void) {
|
||||
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;
|
||||
@ -164,5 +179,4 @@ void png_poll (void) {
|
||||
decoder->callback(PNG_ERR_BAD_FILE, NULL, decoder->callback_data);
|
||||
png_decoder_deinit(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,12 +1,10 @@
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <fatfs/ff.h>
|
||||
#include <libdragon.h>
|
||||
|
||||
#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;
|
||||
menu_show_error(menu, "Couldn't open last directory");
|
||||
}
|
||||
} else if (menu->actions.file_info) {
|
||||
if (menu->browser.selected >= 0) {
|
||||
} 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,123 +212,50 @@ 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);
|
||||
|
||||
// Text start
|
||||
fragment_text_start(text_color);
|
||||
const char *action = NULL;
|
||||
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
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 ));
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -1,7 +1,3 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
@ -1,215 +1,51 @@
|
||||
#include <fatfs/ff.h>
|
||||
#include <libdragon.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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/<id>.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);
|
||||
}
|
||||
}
|
||||
|
@ -1,108 +0,0 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <libdragon.h>
|
||||
|
||||
#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…");
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/**
|
||||
* @file fragments.h
|
||||
* @brief Menu View Fragments
|
||||
* @ingroup menu
|
||||
*/
|
||||
|
||||
#ifndef FRAGMENTS_H__
|
||||
#define FRAGMENTS_H__
|
||||
|
||||
|
||||
#include <surface.h>
|
||||
|
||||
/**
|
||||
* @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
|
@ -1,55 +0,0 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
|
||||
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);
|
||||
}
|
@ -1,24 +1,52 @@
|
||||
#include <libdragon.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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) {
|
||||
if (show_message) {
|
||||
show_message = false;
|
||||
} else {
|
||||
menu->next_mode = MENU_MODE_BROWSER;
|
||||
} else if (menu->actions.enter) {
|
||||
if (image) {
|
||||
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_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);
|
||||
|
@ -1,22 +1,19 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#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)
|
||||
{
|
||||
static char *format_rom_expansion_pak_info (rom_memorytype_t expansion_pak_info) {
|
||||
switch (expansion_pak_info) {
|
||||
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 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();
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,4 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,7 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
@ -1,28 +1,30 @@
|
||||
#include <time.h>
|
||||
#include <libdragon.h>
|
||||
|
||||
#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)
|
||||
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);
|
||||
}
|
@ -8,10 +8,10 @@
|
||||
#define VIEWS_H__
|
||||
|
||||
|
||||
#include <surface.h>
|
||||
|
||||
#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
|
||||
|
@ -1,33 +0,0 @@
|
||||
#ifndef STR_UTILS_H__
|
||||
#define STR_UTILS_H__
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
// 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
|
Loading…
Reference in New Issue
Block a user