Merge branch 'develop' into ed64-xseries

This commit is contained in:
Robin Jones 2024-11-08 01:05:48 +00:00 committed by GitHub
commit de2cb405f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 1154 additions and 271 deletions

View File

@ -29,6 +29,7 @@ SRCS = \
flashcart/64drive/64drive_ll.c \ flashcart/64drive/64drive_ll.c \
flashcart/64drive/64drive.c \ flashcart/64drive/64drive.c \
flashcart/flashcart_utils.c \ flashcart/flashcart_utils.c \
flashcart/ed64/ed64_vseries.c \
flashcart/flashcart.c \ flashcart/flashcart.c \
flashcart/ed64/ed64x.c \ flashcart/ed64/ed64x.c \
flashcart/sc64/sc64_ll.c \ flashcart/sc64/sc64_ll.c \

View File

@ -26,6 +26,7 @@ An open source menu for N64 flashcarts.
* Real Time Clock support. * Real Time Clock support.
* Music playback (MP3). * Music playback (MP3).
* Menu sound effects. * Menu sound effects.
* N64 ROM autoload.
## Documentation ## Documentation
@ -49,6 +50,9 @@ An open source menu for N64 flashcarts.
## Experimental features ## Experimental features
These features are subject to change: These features are subject to change:
### N64 ROM autoload
To use the autoload function, while on the `N64 ROM information` display, press the `R` button on your joypad and select the `Set ROM to autoload` option. When you restart the console, it will now only load the selected ROM rather than the menu.
NOTE: to return to the menu, hold joypad `start` button whilst powering on the console.
### GamePak sprites ### GamePak sprites
To use N64 `GamePak` sprites, place `PNG` files within the `sd:/menu/boxart/` folder. To use N64 `GamePak` sprites, place `PNG` files within the `sd:/menu/boxart/` folder.
@ -60,7 +64,7 @@ These must be `PNG` files that use the following dimensions:
* Japanese N64 GamePak boxart sprites: 112x158 * Japanese N64 GamePak boxart sprites: 112x158
* 64DD boxart sprites: 129x112 * 64DD boxart sprites: 129x112
They will be loaded by directories using each character of the full 4 character Game Code (as identified in the menus ROM information). They will be loaded by directories using each character (case-sensitive) of the full 4 character Game Code (as identified in the menu ROM information).
i.e. for GoldenEye NTSC USA (NGEE), this would be `sd:/menu/boxart/N/G/E/E/boxart_front.png`. i.e. for GoldenEye NTSC USA (NGEE), this would be `sd:/menu/boxart/N/G/E/E/boxart_front.png`.
i.e. for GoldenEye PAL (NGEP), this would be `sd:/menu/boxart/N/G/E/P/boxart_front.png`. i.e. for GoldenEye PAL (NGEP), this would be `sd:/menu/boxart/N/G/E/P/boxart_front.png`.
@ -70,6 +74,9 @@ i.e. for GoldenEye, this would be `sd:/menu/boxart/N/G/E/boxart_front.png`.
**Note1:** Excluding the region ID may show the wrong boxart. **Note1:** Excluding the region ID may show the wrong boxart.
**Note2:** For future support, boxart sprites should also include: `boxart_back.png`, `boxart_top.png`, `boxart_bottom.png`, `boxart_left.png`, `boxart_right.png`. **Note2:** For future support, boxart sprites should also include: `boxart_back.png`, `boxart_top.png`, `boxart_bottom.png`, `boxart_left.png`, `boxart_right.png`.
As a starting point, here is a link to a boxart pack following the new structure, including `boxart_front.png` and failback images:
* [Link](https://drive.google.com/file/d/1IpCmFqmGgGwKKmlRBxYObfFR9XywaC6n/view?usp=drive_link)
#### Compatibilty mode #### Compatibilty mode
If you cannot yet satisfy the correct boxart layout, The menu still has **deprecated** support for filenames containing the Game ID. If you cannot yet satisfy the correct boxart layout, The menu still has **deprecated** support for filenames containing the Game ID.
@ -107,10 +114,20 @@ If required, you can manually adjust the file on the SD card using your computer
* Download the latest `menu.bin` file from the [releases](https://github.com/Polprzewodnikowy/N64FlashcartMenu/releases/) page, then put it in the root directory of your SD card. * Download the latest `menu.bin` file from the [releases](https://github.com/Polprzewodnikowy/N64FlashcartMenu/releases/) page, then put it in the root directory of your SD card.
### ED64 & ED64P ### ED64 - WIP - UNTESTED AND UNSUPPORTED - USE AT OWN RISK
Currently not supported, but work is in progress (See [PR's](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls)). Currently not supported, but work is in progress (See [PR's](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls)).
NOTE: The menu may be able to load ROM's but not perform saves and may break existing ones..
The aim is to replace [Altra64](https://github.com/networkfusion/altra64) and [ED64-UnofficialOS](https://github.com/n64-tools/ED64-UnofficialOS-binaries). #### ED64 (Vseries)
The aim is to reach feature parity with [ED64-UnofficialOS](https://github.com/n64-tools/ED64-UnofficialOS-binaries) / [ED64-OfficialOS](https://krikzz.com/pub/support/everdrive-64/v2x-v3x/os-bin/).
Download the `OS64.v64` ROM from the latest [action run - assets] and place it in the `/ED64` folder.
#### ED64 (X series)
X Series support is currently awaiting fixes, in the meantime use the official [OS](https://krikzz.com/pub/support/everdrive-64/x-series/OS/) instead.
#### ED64 (P clone)
Download the `OS64P.v64` ROM from the latest [action run - assets] and place it in the `/ED64P` folder.
The aim is to reach feature parity with [Altra64](https://github.com/networkfusion/altra64)
# Open source software and licenses used # Open source software and licenses used

@ -1 +1 @@
Subproject commit 9dd994151ae3f3709f1f80224e6b654aac8be6b4 Subproject commit 5295016230d657cd6c7fce5b6ed4a342538e09f5

View File

@ -7,41 +7,79 @@
#ifndef BOOT_IO_H__ #ifndef BOOT_IO_H__
#define BOOT_IO_H__ #define BOOT_IO_H__
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
/**
* @typedef io8_t
* @brief 8-bit volatile IO type.
*/
typedef volatile uint8_t io8_t; typedef volatile uint8_t io8_t;
/**
* @typedef io32_t
* @brief 32-bit volatile IO type.
*/
typedef volatile uint32_t io32_t; typedef volatile uint32_t io32_t;
/**
* @brief Convert an address to its uncached equivalent.
*
* This macro takes an address and converts it to its uncached equivalent
* by setting the appropriate bits.
*
* @param address The address to convert.
* @return The uncached equivalent of the address.
*/
#define UNCACHED(address) ((typeof(address)) (((io32_t) (address)) | (0xA0000000UL))) #define UNCACHED(address) ((typeof(address)) (((io32_t) (address)) | (0xA0000000UL)))
/** @brief Memory Structure. */ /**
* @brief Memory Structure.
*
* This structure represents the memory layout for the SP (Signal Processor),
* containing both Data Memory (DMEM) and Instruction Memory (IMEM).
*/
typedef struct { typedef struct {
io32_t DMEM[1024]; io32_t DMEM[1024]; /**< Data Memory (DMEM) array of 1024 32-bit words. */
io32_t IMEM[1024]; io32_t IMEM[1024]; /**< Instruction Memory (IMEM) array of 1024 32-bit words. */
} sp_mem_t; } sp_mem_t;
/**
* @brief Base address for SP memory.
*/
#define SP_MEM_BASE (0x04000000UL) #define SP_MEM_BASE (0x04000000UL)
/**
* @brief Pointer to the SP memory structure.
*/
#define SP_MEM ((sp_mem_t *) SP_MEM_BASE) #define SP_MEM ((sp_mem_t *) SP_MEM_BASE)
/** @brief SP Registers Structure. */ /**
* @brief SP Registers Structure.
*
* This structure represents the registers for the SP (Signal Processor).
*/
typedef struct { typedef struct {
io32_t PADDR; io32_t PADDR; /**< Physical Address Register. */
io32_t MADDR; io32_t MADDR; /**< Memory Address Register. */
io32_t RD_LEN; io32_t RD_LEN; /**< Read Length Register. */
io32_t WR_LEN; io32_t WR_LEN; /**< Write Length Register. */
io32_t SR; io32_t SR; /**< Status Register. */
io32_t DMA_FULL; io32_t DMA_FULL; /**< DMA Full Register. */
io32_t DMA_BUSY; io32_t DMA_BUSY; /**< DMA Busy Register. */
io32_t SEMAPHORE; io32_t SEMAPHORE; /**< Semaphore Register. */
io32_t __reserved[0xFFF8]; io32_t __reserved[0xFFF8];
io32_t PC; io32_t PC;
} sp_regs_t; } sp_regs_t;
/**
* @brief Base address for SP registers.
*/
#define SP_BASE (0x04040000UL) #define SP_BASE (0x04040000UL)
/**
* @brief Pointer to the SP registers structure.
*/
#define SP ((sp_regs_t *) SP_BASE) #define SP ((sp_regs_t *) SP_BASE)
#define SP_SR_HALT (1 << 0) #define SP_SR_HALT (1 << 0)
@ -85,7 +123,6 @@ typedef struct {
#define SP_SR_CLR_SIG7 (1 << 23) #define SP_SR_CLR_SIG7 (1 << 23)
#define SP_SR_SET_SIG7 (1 << 24) #define SP_SR_SET_SIG7 (1 << 24)
/** @brief DPC Registers Structure. */ /** @brief DPC Registers Structure. */
typedef struct { typedef struct {
io32_t START; io32_t START;
@ -123,7 +160,6 @@ typedef struct {
#define DPC_SR_CLR_CMD_CTR (1 << 8) #define DPC_SR_CLR_CMD_CTR (1 << 8)
#define DPC_SR_CLR_CLOCK_CTR (1 << 9) #define DPC_SR_CLR_CLOCK_CTR (1 << 9)
/** @brief Video Interface Registers Structure. */ /** @brief Video Interface Registers Structure. */
typedef struct { typedef struct {
/** @brief The Control Register. */ /** @brief The Control Register. */
@ -198,7 +234,6 @@ typedef struct {
#define AI_SR_FIFO_FULL (1 << 31) #define AI_SR_FIFO_FULL (1 << 31)
#define AI_CR_DMA_ON (1 << 0) #define AI_CR_DMA_ON (1 << 0)
/** @brief Peripheral Interface Register Structure. */ /** @brief Peripheral Interface Register Structure. */
typedef struct { typedef struct {
/** @brief The Memory Address. */ /** @brief The Memory Address. */
@ -233,15 +268,12 @@ typedef struct {
#define PI_SR_RESET (1 << 0) #define PI_SR_RESET (1 << 0)
#define PI_SR_CLR_INTR (1 << 1) #define PI_SR_CLR_INTR (1 << 1)
#define ROM_DDIPL_BASE (0x06000000UL) #define ROM_DDIPL_BASE (0x06000000UL)
#define ROM_DDIPL ((io32_t *) ROM_DDIPL_BASE) #define ROM_DDIPL ((io32_t *) ROM_DDIPL_BASE)
#define ROM_CART_BASE (0x10000000UL) #define ROM_CART_BASE (0x10000000UL)
#define ROM_CART ((io32_t *) ROM_CART_BASE) #define ROM_CART ((io32_t *) ROM_CART_BASE)
static inline uint32_t cpu_io_read (io32_t *address) { static inline uint32_t cpu_io_read (io32_t *address) {
io32_t *uncached = UNCACHED(address); io32_t *uncached = UNCACHED(address);
uint32_t value = *uncached; uint32_t value = *uncached;
@ -253,5 +285,4 @@ static inline void cpu_io_write (io32_t *address, uint32_t value) {
*uncached = value; *uncached = value;
} }
#endif /* BOOT_IO_H__ */
#endif

View File

@ -3,37 +3,47 @@
#include <stdint.h> #include <stdint.h>
/**
* @brief VR4300 Instruction Structure
*
* This structure represents a VR4300 instruction, which can be of different types (R-type, I-type, J-type, etc.).
*/
typedef union { typedef union {
uint32_t raw; uint32_t raw; /**< Raw 32-bit instruction */
struct { struct {
uint32_t op : 6; uint32_t op : 6; /**< Opcode field */
uint32_t rs : 5; uint32_t rs : 5; /**< Source register */
uint32_t rt : 5; uint32_t rt : 5; /**< Target register */
uint32_t imm : 16; uint32_t imm : 16; /**< Immediate value */
} i_type; } i_type; /**< I-type instruction format */
struct { struct {
uint32_t op : 6; uint32_t op : 6; /**< Opcode field */
uint32_t target : 26; uint32_t target : 26; /**< Target Address field */
} j_type; } j_type; /**< J-type instruction format */
struct { struct {
uint32_t op : 6; uint32_t op : 6; /**< Opcode field */
uint32_t rs : 5; uint32_t rs : 5; /**< Source register */
uint32_t rt : 5; uint32_t rt : 5; /**< Target register */
uint32_t rd : 5; uint32_t rd : 5; /**< Destination register */
uint32_t sa : 5; uint32_t sa : 5; /**< Shift amount */
uint32_t funct : 6; uint32_t funct : 6; /**< Function field */
} r_type; } r_type; /**< Alternate R-type instruction format */
struct { struct {
uint32_t op : 6; uint32_t op : 6; /**< Opcode field */
uint32_t co : 1; uint32_t co : 1; /**< Coprocessor operation bit */
uint32_t funct : 25; uint32_t funct : 25; /**< Function field */
} c_type; } c_type; /**< C-type instruction format */
} vr4300_instruction_t; } vr4300_instruction_t;
/**
* @brief VR4300 Opcode Enumeration
*
* Enumeration for different opcodes used in VR4300 instructions.
*/
typedef enum { typedef enum {
OP_SPECIAL, OP_SPECIAL,
OP_REGIMM, OP_REGIMM,
@ -394,4 +404,4 @@ typedef enum {
#define I_SRL(rd, rt, sa) __ASM_R_INST(OP_SPECIAL, 0, rt, rd, sa, FUNCT_SRL) #define I_SRL(rd, rt, sa) __ASM_R_INST(OP_SPECIAL, 0, rt, rd, sa, FUNCT_SRL)
#define I_SW(rt, offset, base) __ASM_I_INST(OP_SW, base, rt, offset) #define I_SW(rt, offset, base) __ASM_I_INST(OP_SW, base, rt, offset)
#endif #endif /* VR4300_ASM_H__ */

View File

@ -1,4 +1,4 @@
## 64drive developer notes # 64drive developer notes
### Official documentation ### Official documentation

View File

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

View File

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

View File

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

View File

@ -10,6 +10,7 @@
#include "flashcart.h" #include "flashcart.h"
#include "flashcart_utils.h" #include "flashcart_utils.h"
#include "ed64/ed64_vseries.h"
#include "64drive/64drive.h" #include "64drive/64drive.h"
#include "ed64/ed64x.h" #include "ed64/ed64x.h"
#include "sc64/sc64.h" #include "sc64/sc64.h"
@ -110,10 +111,11 @@ flashcart_err_t flashcart_init (const char **storage_prefix) {
break; break;
case CART_EDX: // Official EverDrive 64 Series X case CART_EDX: // Official EverDrive 64 Series X
flashcart = ed64x_get_flashcart(); flashcart = ed64_xseries_get_flashcart();
break; break;
case CART_ED: // Series V EverDrive-64 or clone case CART_ED: // Series V EverDrive-64 or clone
flashcart = ed64_vseries_get_flashcart();
break; break;
case CART_SC: // SummerCart64 case CART_SC: // SummerCart64

View File

@ -1,4 +1,4 @@
## SummerCart64 developer notes # SummerCart64 developer notes
### Official documentation ### Official documentation

View File

@ -1,109 +1,261 @@
/** /**
* @file components.h * @file components.h
* @brief Menu Components * @brief Menu Graphical User Interface Components
* @ingroup menu * @ingroup menu
*/ */
#ifndef COMPONENTS_H__ #ifndef COMPONENTS_H__
#define COMPONENTS_H__ #define COMPONENTS_H__
#include <libdragon.h> #include <libdragon.h>
#include "menu_state.h" #include "menu_state.h"
/** @brief File image Enumeration. */
typedef enum {
/** @brief Boxart image from the front */
IMAGE_BOXART_FRONT,
/** @brief Boxart image from the back */
IMAGE_BOXART_BACK,
/** @brief Boxart image from the top */
IMAGE_BOXART_TOP,
/** @brief Boxart image from the bottom */
IMAGE_BOXART_BOTTOM,
/** @brief Boxart image from the left side */
IMAGE_BOXART_LEFT,
/** @brief Boxart image from the right side */
IMAGE_BOXART_RIGHT,
/** @brief GamePak image from the front */
IMAGE_GAMEPAK_FRONT,
/** @brief GamePak image from the back */
IMAGE_GAMEPAK_BACK,
/** @brief File image thumbnail */
IMAGE_THUMBNAIL,
/** @brief List end marker */
IMAGE_TYPE_END
} file_image_type_t;
/** /**
* @addtogroup * @addtogroup menu_ui_components
* @{ 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); * @brief File image Enumeration.
void component_layout_draw (void); *
void component_progressbar_draw (int x0, int y0, int x1, int y1, float progress); * Enumeration for different types of file images used in the GUI.
void component_seekbar_draw (float progress); */
void component_loader_draw (float position); typedef enum {
void component_scrollbar_draw (int x, int y, int width, int height, int position, int items, int visible_items); IMAGE_BOXART_FRONT, /**< Boxart image from the front */
void component_list_scrollbar_draw (int position, int items, int visible_items); IMAGE_BOXART_BACK, /**< Boxart image from the back */
void component_dialog_draw (int width, int height); IMAGE_BOXART_TOP, /**< Boxart image from the top */
void component_messagebox_draw (char *fmt, ...); IMAGE_BOXART_BOTTOM, /**< Boxart image from the bottom */
void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...); IMAGE_BOXART_LEFT, /**< Boxart image from the left side */
void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...); IMAGE_BOXART_RIGHT, /**< Boxart image from the right side */
IMAGE_GAMEPAK_FRONT, /**< GamePak image from the front */
IMAGE_GAMEPAK_BACK, /**< GamePak image from the back */
IMAGE_THUMBNAIL, /**< File image thumbnail */
IMAGE_TYPE_END /**< List end marker */
} file_image_type_t;
void component_background_init (char *cache_location); /**
void component_background_free (void); * @brief Draw a box component.
void component_background_replace_image (surface_t *image); *
void component_background_draw (void); * @param x0 Starting x-coordinate.
* @param y0 Starting y-coordinate.
* @param x1 Ending x-coordinate.
* @param y1 Ending y-coordinate.
* @param color Color of the box.
*/
void component_box_draw(int x0, int y0, int x1, int y1, color_t color);
void component_file_list_draw (entry_t *list, int entries, int selected); /**
* @brief Draw a border component.
*
* @param x0 Starting x-coordinate.
* @param y0 Starting y-coordinate.
* @param x1 Ending x-coordinate.
* @param y1 Ending y-coordinate.
*/
void component_border_draw(int x0, int y0, int x1, int y1);
/**
* @brief Draw the layout component.
*/
void component_layout_draw(void);
/**
* @brief Draw a progress bar component.
*
* @param x0 Starting x-coordinate.
* @param y0 Starting y-coordinate.
* @param x1 Ending x-coordinate.
* @param y1 Ending y-coordinate.
* @param progress Progress value (0.0 to 1.0).
*/
void component_progressbar_draw(int x0, int y0, int x1, int y1, float progress);
/**
* @brief Draw a seek bar component.
*
* @param progress Progress value (0.0 to 1.0).
*/
void component_seekbar_draw(float progress);
/**
* @brief Draw a loader component.
*
* @param position Position value (0.0 to 1.0).
*/
void component_loader_draw(float position);
/**
* @brief Draw a scrollbar component.
*
* @param x Starting x-coordinate.
* @param y Starting y-coordinate.
* @param width Width of the scrollbar.
* @param height Height of the scrollbar.
* @param position Current position.
* @param items Total number of items.
* @param visible_items Number of visible items.
*/
void component_scrollbar_draw(int x, int y, int width, int height, int position, int items, int visible_items);
/**
* @brief Draw a list scrollbar component.
*
* @param position Current position.
* @param items Total number of items.
* @param visible_items Number of visible items.
*/
void component_list_scrollbar_draw(int position, int items, int visible_items);
/**
* @brief Draw a dialog component.
*
* @param width Width of the dialog.
* @param height Height of the dialog.
*/
void component_dialog_draw(int width, int height);
/**
* @brief Draw a message box component.
*
* @param fmt Format string for the message.
* @param ... Additional arguments for the format string.
*/
void component_messagebox_draw(char *fmt, ...);
/**
* @brief Draw the main text component.
*
* @param align Horizontal alignment.
* @param valign Vertical alignment.
* @param fmt Format string for the text.
* @param ... Additional arguments for the format string.
*/
void component_main_text_draw(rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...);
/**
* @brief Draw the actions bar text component.
*
* @param align Horizontal alignment.
* @param valign Vertical alignment.
* @param fmt Format string for the text.
* @param ... Additional arguments for the format string.
*/
void component_actions_bar_text_draw(rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...);
/**
* @brief Initialize the background component.
*
* @param cache_location Location of the cache.
*/
void component_background_init(char *cache_location);
/**
* @brief Free the background component resources.
*/
void component_background_free(void);
/**
* @brief Replace the background image.
*
* @param image New background image.
*/
void component_background_replace_image(surface_t *image);
/**
* @brief Draw the background component.
*/
void component_background_draw(void);
/**
* @brief Draw the file list component.
*
* @param list List of entries.
* @param entries Number of entries.
* @param selected Index of the selected entry.
*/
void component_file_list_draw(entry_t *list, int entries, int selected);
/**
* @brief Context menu structure.
*/
typedef struct component_context_menu { typedef struct component_context_menu {
int row_count; int row_count; /**< Number of rows in the context menu */
int row_selected; int row_selected; /**< Index of the selected row */
bool hide_pending; bool hide_pending; /**< Flag to indicate if hiding is pending */
struct component_context_menu *parent; struct component_context_menu *parent; /**< Pointer to the parent context menu */
struct component_context_menu *submenu; struct component_context_menu *submenu; /**< Pointer to the submenu */
struct { struct {
const char *text; const char *text; /**< Text of the menu item */
void (*action) (menu_t *menu, void *arg); void (*action)(menu_t *menu, void *arg); /**< Action function for the menu item */
void *arg; void *arg; /**< Argument for the action function */
struct component_context_menu *submenu; struct component_context_menu *submenu; /**< Pointer to the submenu */
} list[]; } list[]; /**< List of menu items */
} component_context_menu_t; } component_context_menu_t;
#define COMPONENT_CONTEXT_MENU_LIST_END { .text = NULL } #define COMPONENT_CONTEXT_MENU_LIST_END { .text = NULL } /**< End marker for the context menu list */
void component_context_menu_init (component_context_menu_t *cm); /**
void component_context_menu_show (component_context_menu_t *cm); * @brief Initialize the context menu component.
bool component_context_menu_process (menu_t *menu, component_context_menu_t *cm); *
void component_context_menu_draw (component_context_menu_t *cm); * @param cm Pointer to the context menu structure.
*/
void component_context_menu_init(component_context_menu_t *cm);
/** @brief Box Art Structure. */ /**
* @brief Show the context menu component.
*
* @param cm Pointer to the context menu structure.
*/
void component_context_menu_show(component_context_menu_t *cm);
/**
* @brief Process the context menu component.
*
* @param menu Pointer to the menu structure.
* @param cm Pointer to the context menu structure.
* @return True if the context menu was processed, false otherwise.
*/
bool component_context_menu_process(menu_t *menu, component_context_menu_t *cm);
/**
* @brief Draw the context menu component.
*
* @param cm Pointer to the context menu structure.
*/
void component_context_menu_draw(component_context_menu_t *cm);
/**
* @brief Box Art Structure.
*/
typedef struct { typedef struct {
bool loading; bool loading; /**< Flag to indicate if the box art is loading */
surface_t *image; surface_t *image; /**< Pointer to the box art image */
} component_boxart_t; } component_boxart_t;
component_boxart_t *component_boxart_init (const char *storage_prefix, char *game_code, file_image_type_t current_image_view); /**
void component_boxart_free (component_boxart_t *b); * @brief Initialize the box art component.
void component_boxart_draw (component_boxart_t *b); *
* @param storage_prefix Prefix for the storage location.
* @param game_code Game code for the box art.
* @param current_image_view Current image view type.
* @return Pointer to the initialized box art component.
*/
component_boxart_t *component_boxart_init(const char *storage_prefix, char *game_code, file_image_type_t current_image_view);
/** @} */ /* menu_components */ /**
* @brief Free the box art component resources.
*
* @param b Pointer to the box art component.
*/
void component_boxart_free(component_boxart_t *b);
/**
* @brief Draw the box art component.
*
* @param b Pointer to the box art component.
*/
void component_boxart_draw(component_boxart_t *b);
#endif /** @} */ /* menu_ui_components */
#endif /* COMPONENTS_H__ */

View File

@ -151,7 +151,7 @@ void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *f
.height = LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT - (TEXT_MARGIN_VERTICAL * 2), .height = LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT - (TEXT_MARGIN_VERTICAL * 2),
.align = align, .align = align,
.valign = valign, .valign = valign,
.wrap = WRAP_ELLIPSES, .wrap = WRAP_WORD,
.line_spacing = TEXT_LINE_SPACING_ADJUST, .line_spacing = TEXT_LINE_SPACING_ADJUST,
}, },
FNT_DEFAULT, FNT_DEFAULT,

View File

@ -52,7 +52,17 @@ typedef struct {
} disk_info_t; } disk_info_t;
/**
* @brief Loads disk information from the specified path.
*
* This function reads the disk information from the given path and populates
* the provided disk_info structure with the relevant data.
*
* @param path A pointer to a path_t structure that specifies the path to the disk.
* @param disk_info A pointer to a disk_info_t structure where the disk information will be stored.
* @return A disk_err_t value indicating the success or failure of the operation.
*/
disk_err_t disk_info_load (path_t *path, disk_info_t *disk_info); disk_err_t disk_info_load (path_t *path, disk_info_t *disk_info);
#endif #endif /* DISK_INFO_H__ */

View File

@ -7,23 +7,39 @@
#ifndef FONTS_H__ #ifndef FONTS_H__
#define FONTS_H__ #define FONTS_H__
/** @brief Font type enumeration. */ /**
* @brief Font type enumeration.
*
* This enumeration defines the different types of fonts that can be used
* in the menu system.
*/
typedef enum { typedef enum {
FNT_DEFAULT = 1, FNT_DEFAULT = 1, /**< Default font type */
} menu_font_type_t; } menu_font_type_t;
/** @brief Font style enumeration. */ /**
* @brief Font style enumeration.
*
* This enumeration defines the different styles of fonts that can be used
* in the menu system.
*/
typedef enum { typedef enum {
STL_DEFAULT = 0, STL_DEFAULT = 0, /**< Default font style */
STL_GREEN, STL_GREEN, /**< Green font style */
STL_BLUE, STL_BLUE, /**< Blue font style */
STL_YELLOW, STL_YELLOW, /**< Yellow font style */
STL_ORANGE, STL_ORANGE, /**< Orange font style */
STL_GRAY, STL_GRAY, /**< Gray font style */
} menu_font_style_t; } menu_font_style_t;
/**
* @brief Initialize fonts.
*
* This function initializes the fonts used in the menu system. It can load
* custom fonts from the specified path.
*
* @param custom_font_path Path to the custom font file.
*/
void fonts_init(char *custom_font_path);
void fonts_init (char *custom_font_path); #endif /* FONTS_H__ */
#endif

View File

@ -104,6 +104,12 @@ typedef struct {
path_t *disk_path; path_t *disk_path;
disk_info_t disk_info; disk_info_t disk_info;
} load; } load;
struct {
bool rom_file;
bool disk_file;
bool emulator_file;
} boot_pending;
} menu_t; } menu_t;

View File

@ -7,37 +7,166 @@
#ifndef MP3_PLAYER_H__ #ifndef MP3_PLAYER_H__
#define MP3_PLAYER_H__ #define MP3_PLAYER_H__
#include <stdbool.h> #include <stdbool.h>
/**
/** @brief MP3 file error enumeration */ * @brief MP3 file error enumeration.
*
* Enumeration for different types of errors that can occur in the MP3 player.
*/
typedef enum { typedef enum {
MP3PLAYER_OK, MP3PLAYER_OK, /**< No error */
MP3PLAYER_ERR_OUT_OF_MEM, MP3PLAYER_ERR_OUT_OF_MEM, /**< Out of memory error */
MP3PLAYER_ERR_IO, MP3PLAYER_ERR_IO, /**< Input/Output error */
MP3PLAYER_ERR_NO_FILE, MP3PLAYER_ERR_NO_FILE, /**< No file found error */
MP3PLAYER_ERR_INVALID_FILE, MP3PLAYER_ERR_INVALID_FILE, /**< Invalid file error */
} mp3player_err_t; } mp3player_err_t;
/**
* @brief Initialize the MP3 player mixer.
*
* This function initializes the mixer for the MP3 player.
*/
void mp3player_mixer_init(void);
void mp3player_mixer_init (void); /**
mp3player_err_t mp3player_init (void); * @brief Initialize the MP3 player.
void mp3player_deinit (void); *
mp3player_err_t mp3player_load (char *path); * This function initializes the MP3 player and prepares it for playback.
void mp3player_unload (void); *
mp3player_err_t mp3player_process (void); * @return mp3player_err_t Error code indicating the result of the initialization.
bool mp3player_is_playing (void); */
bool mp3player_is_finished (void); mp3player_err_t mp3player_init(void);
mp3player_err_t mp3player_play (void);
void mp3player_stop (void);
mp3player_err_t mp3player_toggle (void);
void mp3player_mute (bool mute);
mp3player_err_t mp3player_seek (int seconds);
float mp3player_get_duration (void);
float mp3player_get_bitrate (void);
int mp3player_get_samplerate (void);
float mp3player_get_progress (void);
/**
* @brief Deinitialize the MP3 player.
*
* This function deinitializes the MP3 player and releases any resources.
*/
void mp3player_deinit(void);
#endif /**
* @brief Load an MP3 file.
*
* This function loads an MP3 file from the specified path.
*
* @param path Path to the MP3 file.
* @return mp3player_err_t Error code indicating the result of the load operation.
*/
mp3player_err_t mp3player_load(char *path);
/**
* @brief Unload the current MP3 file.
*
* This function unloads the currently loaded MP3 file.
*/
void mp3player_unload(void);
/**
* @brief Process the MP3 player.
*
* This function processes the MP3 player, handling playback and other operations.
*
* @return mp3player_err_t Error code indicating the result of the process operation.
*/
mp3player_err_t mp3player_process(void);
/**
* @brief Check if the MP3 player is playing.
*
* This function checks if the MP3 player is currently playing.
*
* @return true if the MP3 player is playing, false otherwise.
*/
bool mp3player_is_playing(void);
/**
* @brief Check if the MP3 player has finished playing.
*
* This function checks if the MP3 player has finished playing the current file.
*
* @return true if the MP3 player has finished playing, false otherwise.
*/
bool mp3player_is_finished(void);
/**
* @brief Start playback of the MP3 file.
*
* This function starts playback of the currently loaded MP3 file.
*
* @return mp3player_err_t Error code indicating the result of the play operation.
*/
mp3player_err_t mp3player_play(void);
/**
* @brief Stop playback of the MP3 file.
*
* This function stops playback of the currently loaded MP3 file.
*/
void mp3player_stop(void);
/**
* @brief Toggle playback of the MP3 file.
*
* This function toggles playback of the currently loaded MP3 file.
*
* @return mp3player_err_t Error code indicating the result of the toggle operation.
*/
mp3player_err_t mp3player_toggle(void);
/**
* @brief Mute or unmute the MP3 player.
*
* This function mutes or unmutes the MP3 player.
*
* @param mute true to mute, false to unmute.
*/
void mp3player_mute(bool mute);
/**
* @brief Seek to a specific position in the MP3 file.
*
* This function seeks to a specific position in the currently loaded MP3 file.
*
* @param seconds Number of seconds to seek.
* @return mp3player_err_t Error code indicating the result of the seek operation.
*/
mp3player_err_t mp3player_seek(int seconds);
/**
* @brief Get the duration of the MP3 file.
*
* This function gets the duration of the currently loaded MP3 file.
*
* @return float Duration of the MP3 file in seconds.
*/
float mp3player_get_duration(void);
/**
* @brief Get the bitrate of the MP3 file.
*
* This function gets the bitrate of the currently loaded MP3 file.
*
* @return float Bitrate of the MP3 file in kbps.
*/
float mp3player_get_bitrate(void);
/**
* @brief Get the sample rate of the MP3 file.
*
* This function gets the sample rate of the currently loaded MP3 file.
*
* @return int Sample rate of the MP3 file in Hz.
*/
int mp3player_get_samplerate(void);
/**
* @brief Get the current playback progress.
*
* This function gets the current playback progress of the MP3 file.
*
* @return float Current playback progress as a percentage (0.0 to 100.0).
*/
float mp3player_get_progress(void);
#endif /* MP3_PLAYER_H__ */

View File

@ -7,27 +7,68 @@
#ifndef PNG_DECODER_H__ #ifndef PNG_DECODER_H__
#define PNG_DECODER_H__ #define PNG_DECODER_H__
#include <surface.h> #include <surface.h>
/**
/** @brief PNG decoder errors */ * @brief PNG decoder errors
*
* Enumeration for different types of errors that can occur in the PNG decoder.
*/
typedef enum { typedef enum {
PNG_OK, PNG_OK, /**< No error */
PNG_ERR_INT, PNG_ERR_INT, /**< Internal error */
PNG_ERR_BUSY, PNG_ERR_BUSY, /**< Decoder is busy */
PNG_ERR_OUT_OF_MEM, PNG_ERR_OUT_OF_MEM, /**< Out of memory error */
PNG_ERR_NO_FILE, PNG_ERR_NO_FILE, /**< No file found error */
PNG_ERR_BAD_FILE, PNG_ERR_BAD_FILE, /**< Bad file error */
} png_err_t; } png_err_t;
/**
* @brief PNG decoder callback type.
*
* This typedef defines the callback function type used by the PNG decoder.
*
* @param err Error code indicating the result of the decoding process.
* @param decoded_image Pointer to the decoded image surface.
* @param callback_data User-defined data passed to the callback function.
*/
typedef void png_callback_t (png_err_t err, surface_t *decoded_image, void *callback_data); typedef void png_callback_t (png_err_t err, surface_t *decoded_image, void *callback_data);
/**
* @brief Start the PNG decoding process.
*
* This function starts the PNG decoding process for the specified file.
*
* @param path Path to the PNG file.
* @param max_width Maximum width of the decoded image.
* @param max_height Maximum height of the decoded image.
* @param callback Callback function to be called when decoding is complete.
* @param callback_data User-defined data to be passed to the callback function.
* @return png_err_t Error code indicating the result of the start operation.
*/
png_err_t png_decoder_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);
/**
* @brief Abort the PNG decoding process.
*
* This function aborts the ongoing PNG decoding process.
*/
void png_decoder_abort (void); void png_decoder_abort (void);
/**
* @brief Get the progress of the PNG decoding process.
*
* This function returns the current progress of the PNG decoding process as a percentage.
*
* @return float Current progress of the decoding process (0.0 to 100.0).
*/
float png_decoder_get_progress (void); float png_decoder_get_progress (void);
/**
* @brief Poll the PNG decoder.
*
* This function polls the PNG decoder to handle any ongoing decoding tasks.
*/
void png_decoder_poll (void); void png_decoder_poll (void);
#endif /* PNG_DECODER_H__ */
#endif

View File

@ -14,6 +14,9 @@ static settings_t init = {
.default_directory = "/", .default_directory = "/",
.use_saves_folder = true, .use_saves_folder = true,
.sound_enabled = true, .sound_enabled = true,
.rom_autoload_enabled = false,
.rom_autoload_path = "",
.rom_autoload_filename = "",
/* Beta feature flags (should always init to off) */ /* Beta feature flags (should always init to off) */
.bgm_enabled = false, .bgm_enabled = false,
@ -41,6 +44,10 @@ void settings_load (settings_t *settings) {
settings->use_saves_folder = mini_get_bool(ini, "menu", "use_saves_folder", init.use_saves_folder); settings->use_saves_folder = mini_get_bool(ini, "menu", "use_saves_folder", init.use_saves_folder);
settings->sound_enabled = mini_get_bool(ini, "menu", "sound_enabled", init.sound_enabled); settings->sound_enabled = mini_get_bool(ini, "menu", "sound_enabled", init.sound_enabled);
settings->rom_autoload_enabled = mini_get_bool(ini, "menu", "autoload_rom_enabled", init.rom_autoload_enabled);
settings->rom_autoload_path = strdup(mini_get_string(ini, "autoload", "rom_path", init.rom_autoload_path));
settings->rom_autoload_filename = strdup(mini_get_string(ini, "autoload", "rom_filename", init.rom_autoload_filename));
/* Beta feature flags, they might not be in the file */ /* Beta feature flags, they might not be in the file */
settings->bgm_enabled = mini_get_bool(ini, "menu_beta_flag", "bgm_enabled", init.bgm_enabled); settings->bgm_enabled = mini_get_bool(ini, "menu_beta_flag", "bgm_enabled", init.bgm_enabled);
settings->rumble_enabled = mini_get_bool(ini, "menu_beta_flag", "rumble_enabled", init.rumble_enabled); settings->rumble_enabled = mini_get_bool(ini, "menu_beta_flag", "rumble_enabled", init.rumble_enabled);
@ -56,6 +63,9 @@ void settings_save (settings_t *settings) {
mini_set_string(ini, "menu", "default_directory", settings->default_directory); mini_set_string(ini, "menu", "default_directory", settings->default_directory);
mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder); mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder);
mini_set_bool(ini, "menu", "sound_enabled", settings->sound_enabled); mini_set_bool(ini, "menu", "sound_enabled", settings->sound_enabled);
mini_set_bool(ini, "menu", "autoload_rom_enabled", settings->rom_autoload_enabled);
mini_set_string(ini, "autoload", "rom_path", settings->rom_autoload_path);
mini_set_string(ini, "autoload", "rom_filename", settings->rom_autoload_filename);
/* Beta feature flags, they should not save until production ready! */ /* Beta feature flags, they should not save until production ready! */
// mini_set_bool(ini, "menu_beta_flag", "bgm_enabled", settings->bgm_enabled); // mini_set_bool(ini, "menu_beta_flag", "bgm_enabled", settings->bgm_enabled);

View File

@ -30,6 +30,16 @@ typedef struct {
/** @brief Enable rumble feedback */ /** @brief Enable rumble feedback */
bool rumble_enabled; bool rumble_enabled;
/** @brief Enable the ability to bypass the menu and instantly load a ROM */
bool rom_autoload_enabled;
/** @brief A path to the autoloaded ROM */
char *rom_autoload_path;
/** @brief A filename of the autoloaded ROM */
char *rom_autoload_filename;
} settings_t; } settings_t;
@ -40,5 +50,4 @@ void settings_load (settings_t *settings);
/** @brief The settings to save */ /** @brief The settings to save */
void settings_save (settings_t *settings); void settings_save (settings_t *settings);
#endif #endif

View File

@ -9,28 +9,46 @@
#include <stdbool.h> #include <stdbool.h>
#define SOUND_MP3_PLAYER_CHANNEL (0) #define SOUND_MP3_PLAYER_CHANNEL (0) /**< Channel for MP3 player sound */
#define SOUND_SFX_CHANNEL (2) #define SOUND_SFX_CHANNEL (2) /**< Channel for sound effects */
/** /**
* @brief Enumeration of available sound effects for menu interactions. * @brief Enumeration of available sound effects for menu interactions.
*
* This enumeration defines the different sound effects that can be used
* for menu interactions.
*/ */
typedef enum { typedef enum {
SFX_CURSOR, SFX_CURSOR, /**< Sound effect for cursor movement */
SFX_ERROR, SFX_ERROR, /**< Sound effect for error */
SFX_ENTER, SFX_ENTER, /**< Sound effect for entering a menu */
SFX_EXIT, SFX_EXIT, /**< Sound effect for exiting a menu */
SFX_SETTING, SFX_SETTING, /**< Sound effect for changing a setting */
} sound_effect_t; } sound_effect_t;
/**
void sound_init_default (void); * @brief Initialize the default sound system.
void sound_init_mp3_playback (void); *
* This function initializes the default sound system, setting up
* necessary resources and configurations.
*/
void sound_init_default(void);
/** /**
* @brief Initialize sound effects system. * @brief Initialize the MP3 playback system.
*
* This function initializes the MP3 playback system, preparing it
* for playing MP3 files.
*/ */
void sound_init_sfx (void); void sound_init_mp3_playback(void);
/**
* @brief Initialize the sound effects system.
*
* This function initializes the sound effects system, setting up
* necessary resources and configurations for playing sound effects.
*/
void sound_init_sfx(void);
/** /**
* @brief Enable or disable sound effects. * @brief Enable or disable sound effects.
@ -46,4 +64,4 @@ void sound_play_effect(sound_effect_t sfx);
void sound_deinit (void); void sound_deinit (void);
void sound_poll (void); void sound_poll (void);
#endif #endif /* SOUND_H__ */

View File

@ -2,20 +2,36 @@
* @file usb_comm.h * @file usb_comm.h
* @brief USB communication subsystem * @brief USB communication subsystem
* @ingroup menu * @ingroup menu
*
* This file contains the declarations for the USB communication subsystem
* used in the menu system.
*/ */
#ifndef USB_COMM_H__ #ifndef USB_COMM_H__
#define USB_COMM_H__ #define USB_COMM_H__
#include "menu_state.h" #include "menu_state.h"
#ifndef NDEBUG #ifndef NDEBUG
void usb_comm_poll (menu_t *menu); /**
* @brief Poll the USB communication subsystem.
*
* This function polls the USB communication subsystem to handle any
* incoming or outgoing data. It is only available in debug builds.
*
* @param menu Pointer to the menu structure.
*/
void usb_comm_poll(menu_t *menu);
#else #else
/**
* @brief Poll the USB communication subsystem (no-op in release builds).
*
* This macro is a no-op in release builds, where USB communication polling
* is disabled.
*
* @param menu Pointer to the menu structure.
*/
#define usb_comm_poll(menu) #define usb_comm_poll(menu)
#endif #endif
#endif /* USB_COMM_H__ */
#endif

View File

@ -5,8 +5,7 @@
#include "views.h" #include "views.h"
static bool load_pending; static bool load_disk_with_rom;
static bool load_rom;
static component_boxart_t *boxart; static component_boxart_t *boxart;
@ -31,11 +30,11 @@ static char *format_disk_region (disk_region_t region) {
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.enter) { if (menu->actions.enter) {
load_pending = true; menu->boot_pending.disk_file = true;
load_rom = false; load_disk_with_rom = false;
} else if (menu->actions.options && menu->load.rom_path) { } else if (menu->actions.options && menu->load.rom_path) {
load_pending = true; menu->boot_pending.disk_file = true;
load_rom = true; load_disk_with_rom = true;
sound_play_effect(SFX_SETTING); sound_play_effect(SFX_SETTING);
} else if (menu->actions.back) { } else if (menu->actions.back) {
sound_play_effect(SFX_EXIT); sound_play_effect(SFX_EXIT);
@ -48,7 +47,7 @@ static void draw (menu_t *menu, surface_t *d) {
component_background_draw(); component_background_draw();
if (load_pending) { if (menu->boot_pending.disk_file) {
component_loader_draw(0.0f); component_loader_draw(0.0f);
} else { } else {
component_layout_draw(); component_layout_draw();
@ -119,7 +118,7 @@ static void draw_progress (float progress) {
static void load (menu_t *menu) { static void load (menu_t *menu) {
cart_load_err_t err; cart_load_err_t err;
if (menu->load.rom_path && load_rom) { if (menu->load.rom_path && load_disk_with_rom) {
err = cart_load_n64_rom_and_save(menu, draw_progress); err = cart_load_n64_rom_and_save(menu, draw_progress);
if (err != CART_LOAD_OK) { if (err != CART_LOAD_OK) {
menu_show_error(menu, cart_load_convert_error_message(err)); menu_show_error(menu, cart_load_convert_error_message(err));
@ -135,7 +134,7 @@ static void load (menu_t *menu) {
menu->next_mode = MENU_MODE_BOOT; menu->next_mode = MENU_MODE_BOOT;
if (load_rom) { if (load_disk_with_rom) {
menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM;
menu->boot_params->detect_cic_seed = rom_info_get_cic_seed(&menu->load.rom_info, &menu->boot_params->cic_seed); menu->boot_params->detect_cic_seed = rom_info_get_cic_seed(&menu->load.rom_info, &menu->boot_params->cic_seed);
switch (rom_info_get_tv_type(&menu->load.rom_info)) { switch (rom_info_get_tv_type(&menu->load.rom_info)) {
@ -163,13 +162,14 @@ void view_load_disk_init (menu_t *menu) {
menu->load.disk_path = NULL; menu->load.disk_path = NULL;
} }
load_pending = false; menu->boot_pending.disk_file = false;
menu->load.disk_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); menu->load.disk_path = path_clone_push(menu->browser.directory, menu->browser.entry->name);
disk_err_t err = disk_info_load(menu->load.disk_path, &menu->load.disk_info); disk_err_t err = disk_info_load(menu->load.disk_path, &menu->load.disk_info);
if (err != DISK_OK) { if (err != DISK_OK) {
menu_show_error(menu, convert_error_message(err)); menu_show_error(menu, convert_error_message(err));
return;
} }
boxart = component_boxart_init(menu->storage_prefix, menu->load.disk_info.id, IMAGE_BOXART_FRONT); boxart = component_boxart_init(menu->storage_prefix, menu->load.disk_info.id, IMAGE_BOXART_FRONT);
@ -180,8 +180,8 @@ void view_load_disk_display (menu_t *menu, surface_t *display) {
draw(menu, display); draw(menu, display);
if (load_pending) { if (menu->boot_pending.disk_file) {
load_pending = false; menu->boot_pending.disk_file = false;
load(menu); load(menu);
} }

View File

@ -11,7 +11,6 @@ static const char *emu_gameboy_rom_extensions[] = { "gb", NULL };
static const char *emu_gameboy_color_rom_extensions[] = { "gbc", NULL }; static const char *emu_gameboy_color_rom_extensions[] = { "gbc", NULL };
static const char *emu_sega_8bit_rom_extensions[] = { "sms", "gg", "sg", NULL }; static const char *emu_sega_8bit_rom_extensions[] = { "sms", "gg", "sg", NULL };
static bool load_pending;
static cart_load_emu_type_t emu_type; static cart_load_emu_type_t emu_type;
static char *format_emulator_name (cart_load_emu_type_t emulator_info) { static char *format_emulator_name (cart_load_emu_type_t emulator_info) {
@ -34,7 +33,7 @@ static char *format_emulator_name (cart_load_emu_type_t emulator_info) {
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.enter) { if (menu->actions.enter) {
load_pending = true; menu->boot_pending.emulator_file = true;
} else if (menu->actions.back) { } else if (menu->actions.back) {
sound_play_effect(SFX_EXIT); sound_play_effect(SFX_EXIT);
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
@ -46,7 +45,7 @@ static void draw (menu_t *menu, surface_t *d) {
component_background_draw(); component_background_draw();
if (load_pending) { if (menu->boot_pending.emulator_file) {
component_loader_draw(0.0f); component_loader_draw(0.0f);
} else { } else {
component_layout_draw(); component_layout_draw();
@ -107,7 +106,7 @@ static void load (menu_t *menu) {
void view_load_emulator_init (menu_t *menu) { void view_load_emulator_init (menu_t *menu) {
load_pending = false; menu->boot_pending.emulator_file = false;
path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name);
@ -133,8 +132,8 @@ void view_load_emulator_display (menu_t *menu, surface_t *display) {
draw(menu, display); draw(menu, display);
if (load_pending) { if (menu->boot_pending.emulator_file) {
load_pending = false; menu->boot_pending.emulator_file = false;
load(menu); load(menu);
} }
} }

View File

@ -3,12 +3,12 @@
#include "boot/boot.h" #include "boot/boot.h"
#include "../sound.h" #include "../sound.h"
#include "views.h" #include "views.h"
#include <string.h>
#include "utils/fs.h"
static bool show_extra_info_message = false; static bool show_extra_info_message = false;
static bool load_pending;
static component_boxart_t *boxart; static component_boxart_t *boxart;
static char *convert_error_message (rom_err_t err) { static char *convert_error_message (rom_err_t err) {
switch (err) { switch (err) {
case ROM_ERR_LOAD_IO: return "I/O error during loading ROM information and/or options"; case ROM_ERR_LOAD_IO: return "I/O error during loading ROM information and/or options";
@ -145,6 +145,17 @@ static void set_tv_type (menu_t *menu, void *arg) {
menu->browser.reload = true; menu->browser.reload = true;
} }
static void set_autoload_type (menu_t *menu, void *arg) {
free(menu->settings.rom_autoload_path);
menu->settings.rom_autoload_path = strdup(strip_fs_prefix(path_get(menu->browser.directory)));
free(menu->settings.rom_autoload_filename);
menu->settings.rom_autoload_filename = strdup(menu->browser.entry->name);
// FIXME: add a confirmation box here! (press start on reboot)
menu->settings.rom_autoload_enabled = true;
settings_save(&menu->settings);
menu->browser.reload = true;
}
static component_context_menu_t set_cic_type_context_menu = { .list = { static component_context_menu_t set_cic_type_context_menu = { .list = {
{.text = "Automatic", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_AUTOMATIC) }, {.text = "Automatic", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_AUTOMATIC) },
{.text = "CIC-6101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_6101) }, {.text = "CIC-6101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_6101) },
@ -187,6 +198,7 @@ static component_context_menu_t options_context_menu = { .list = {
{ .text = "Set CIC Type", .submenu = &set_cic_type_context_menu }, { .text = "Set CIC Type", .submenu = &set_cic_type_context_menu },
{ .text = "Set Save Type", .submenu = &set_save_type_context_menu }, { .text = "Set Save Type", .submenu = &set_save_type_context_menu },
{ .text = "Set TV Type", .submenu = &set_tv_type_context_menu }, { .text = "Set TV Type", .submenu = &set_tv_type_context_menu },
{ .text = "Set ROM to autoload", .action = set_autoload_type },
COMPONENT_CONTEXT_MENU_LIST_END, COMPONENT_CONTEXT_MENU_LIST_END,
}}; }};
@ -196,7 +208,7 @@ static void process (menu_t *menu) {
} }
if (menu->actions.enter) { if (menu->actions.enter) {
load_pending = true; menu->boot_pending.rom_file = true;
} else if (menu->actions.back) { } else if (menu->actions.back) {
sound_play_effect(SFX_EXIT); sound_play_effect(SFX_EXIT);
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
@ -218,7 +230,7 @@ static void draw (menu_t *menu, surface_t *d) {
component_background_draw(); component_background_draw();
if (load_pending) { if (menu->boot_pending.rom_file) {
component_loader_draw(0.0f); component_loader_draw(0.0f);
} else { } else {
component_layout_draw(); component_layout_draw();
@ -342,14 +354,14 @@ static void deinit (void) {
void view_load_rom_init (menu_t *menu) { void view_load_rom_init (menu_t *menu) {
load_pending = false; if (!menu->settings.rom_autoload_enabled) {
if (menu->load.rom_path) {
path_free(menu->load.rom_path);
}
if (menu->load.rom_path) { menu->load.rom_path = path_clone_push(menu->browser.directory, menu->browser.entry->name);
path_free(menu->load.rom_path);
} }
menu->load.rom_path = path_clone_push(menu->browser.directory, menu->browser.entry->name);
rom_err_t err = rom_info_load(menu->load.rom_path, &menu->load.rom_info); rom_err_t err = rom_info_load(menu->load.rom_path, &menu->load.rom_info);
if (err != ROM_OK) { if (err != ROM_OK) {
path_free(menu->load.rom_path); path_free(menu->load.rom_path);
@ -358,9 +370,10 @@ void view_load_rom_init (menu_t *menu) {
return; return;
} }
boxart = component_boxart_init(menu->storage_prefix, menu->load.rom_info.game_code, IMAGE_BOXART_FRONT); if (!menu->settings.rom_autoload_enabled) {
boxart = component_boxart_init(menu->storage_prefix, menu->load.rom_info.game_code, IMAGE_BOXART_FRONT);
component_context_menu_init(&options_context_menu); component_context_menu_init(&options_context_menu);
}
} }
void view_load_rom_display (menu_t *menu, surface_t *display) { void view_load_rom_display (menu_t *menu, surface_t *display) {
@ -368,8 +381,8 @@ void view_load_rom_display (menu_t *menu, surface_t *display) {
draw(menu, display); draw(menu, display);
if (load_pending) { if (menu->boot_pending.rom_file) {
load_pending = false; menu->boot_pending.rom_file = false;
load(menu); load(menu);
} }

View File

@ -1,26 +1,165 @@
#include <time.h> #include <stdbool.h>
#include <stdio.h>
#include <libdragon.h>
#include <sys/time.h>
#include "../sound.h" #include "../sound.h"
#include "views.h" #include "views.h"
// FIXME: add implementation! #define MAX(a,b) ({ typeof(a) _a = a; typeof(b) _b = b; _a > _b ? _a : _b; })
// struct { #define MIN(a,b) ({ typeof(a) _a = a; typeof(b) _b = b; _a < _b ? _a : _b; })
// uint16_t seconds; #define CLAMP(x, min, max) (MIN(MAX((x), (min)), (max)))
// uint16_t minutes;
// uint16_t hours;
// uint16_t day;
// uint16_t month;
// uint16_t year;
// } adjusted_datetime;
// static void save_adjusted_datetime () { #define YEAR_MIN 1996
#define YEAR_MAX 2095
// } typedef enum {
RTC_EDIT_YEAR,
RTC_EDIT_MONTH,
RTC_EDIT_DAY,
RTC_EDIT_HOUR,
RTC_EDIT_MIN,
RTC_EDIT_SEC,
} rtc_field_t;
static const char* const DAYS_OF_WEEK[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static struct tm rtc_tm = {0};
static bool is_editing_mode;
static rtc_field_t editing_field_type;
int wrap( uint16_t val, uint16_t min, uint16_t max ) {
if( val < min ) return max;
if( val > max ) return min;
return val;
}
rtc_time_t rtc_time_from_tm( struct tm *time ) {
return(rtc_time_t){
.year = CLAMP(time->tm_year + 1900, YEAR_MIN, YEAR_MAX),
.month = CLAMP(time->tm_mon, 1, 12),
.day = CLAMP(time->tm_mday, 1, 31),
.hour = CLAMP(time->tm_hour, 0, 23),
.min = CLAMP(time->tm_min, 0, 59),
.sec = CLAMP(time->tm_sec, 0, 59),
.week_day = CLAMP(time->tm_wday, 0, 6),
};
}
void adjust_rtc_time( struct tm *t, int incr ) {
switch(editing_field_type)
{
case RTC_EDIT_YEAR:
t->tm_year = wrap( t->tm_year + incr, YEAR_MIN - 1900, YEAR_MAX - 1900 );
break;
case RTC_EDIT_MONTH:
t->tm_mon = wrap( t->tm_mon + incr, 0, 11 );
break;
case RTC_EDIT_DAY:
t->tm_mday = wrap( t->tm_mday + incr, 1, 31 );
break;
case RTC_EDIT_HOUR:
t->tm_hour = wrap( t->tm_hour + incr, 0, 23 );
break;
case RTC_EDIT_MIN:
t->tm_min = wrap( t->tm_min + incr, 0, 59 );
break;
case RTC_EDIT_SEC:
t->tm_sec = wrap( t->tm_sec + incr, 0, 59 );
break;
}
// Recalculate day-of-week and day-of-year
time_t timestamp = mktime( t );
*t = *gmtime( &timestamp );
}
void component_editdatetime_draw ( struct tm t, rtc_field_t selected_field ) {
// FIXME: move this to components.c once improved.
/* Format RTC date/time as strings */
char full_dt[30];
char current_selection_chars[30];
snprintf( full_dt, sizeof(full_dt), ">%04d|%02d|%02d|%02d|%02d|%02d< %s",
t.tm_year + 1900,
t.tm_mon + 1,
t.tm_mday,
t.tm_hour,
t.tm_min,
t.tm_sec,
DAYS_OF_WEEK[t.tm_wday]
);
switch(selected_field)
{
// Note: for what ever reason, hat chars need to be duplicated to display correctly. This will be solved when there is a decent UI for it.
case RTC_EDIT_YEAR:
snprintf( current_selection_chars, sizeof(current_selection_chars), "*^^^^^^^^********************");
break;
case RTC_EDIT_MONTH:
snprintf( current_selection_chars, sizeof(current_selection_chars), "******^^^^*****************");
break;
case RTC_EDIT_DAY:
snprintf( current_selection_chars, sizeof(current_selection_chars), "*********^^^^**************");
break;
case RTC_EDIT_HOUR:
snprintf( current_selection_chars, sizeof(current_selection_chars), "************^^^^***********");
break;
case RTC_EDIT_MIN:
snprintf( current_selection_chars, sizeof(current_selection_chars), "***************^^^^********");
break;
case RTC_EDIT_SEC:
snprintf( current_selection_chars, sizeof(current_selection_chars), "******************^^^^*****");
break;
}
component_messagebox_draw(
"|YYYY|MM|DD|HH|MM|SS| DOW\n%s\n%s\n", full_dt, current_selection_chars);
}
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back && !is_editing_mode) {
sound_play_effect(SFX_EXIT); sound_play_effect(SFX_EXIT);
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
} }
else if (menu->actions.enter && !is_editing_mode && menu->current_time >= 0) {
rtc_tm = *gmtime(&menu->current_time);
is_editing_mode = true;
}
if (is_editing_mode) {
if (menu->actions.go_left) {
if ( editing_field_type <= RTC_EDIT_YEAR ) { editing_field_type = RTC_EDIT_SEC; }
else { editing_field_type = editing_field_type - 1; }
}
else if (menu->actions.go_right) {
if ( editing_field_type >= RTC_EDIT_SEC ) { editing_field_type = RTC_EDIT_YEAR; }
else { editing_field_type = editing_field_type + 1; }
}
else if (menu->actions.go_up) {
adjust_rtc_time( &rtc_tm, +1 );
}
else if (menu->actions.go_down) {
adjust_rtc_time( &rtc_tm, -1 );
}
else if (menu->actions.options) { // R button = save
if(rtc_is_writable()) {
// FIXME: settimeofday is not available in libdragon yet.
// struct timeval new_time = { .tv_sec = mktime(&rtc_tm) };
// int res = settimeofday(&new_time, NULL);
rtc_time_t rtc_time = rtc_time_from_tm(&rtc_tm);
int res = rtc_set(&rtc_time);
if (res != 1) {
menu_show_error(menu, "Failed to set RTC time");
}
}
else {
menu_show_error(menu, "RTC is not writable");
}
is_editing_mode = false;
}
else if (menu->actions.back) { // cancel
is_editing_mode = false;
}
}
} }
static void draw (menu_t *menu, surface_t *d) { static void draw (menu_t *menu, surface_t *d) {
@ -30,36 +169,74 @@ static void draw (menu_t *menu, surface_t *d) {
component_layout_draw(); component_layout_draw();
component_main_text_draw( if (!is_editing_mode) {
ALIGN_CENTER, VALIGN_TOP, if( menu->current_time >= 0 ) {
"ADJUST REAL TIME CLOCK\n"
"\n"
"\n"
"To set the date and time, please use the PC terminal\n"
"application and set via USB,\n or a N64 game with RTC support.\n\n"
"Current date & time: %s\n",
menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown\n"
);
component_main_text_draw( component_main_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_CENTER, VALIGN_TOP,
"\n" "ADJUST REAL TIME CLOCK\n"
"\n" "\n"
); "\n"
"To set the RTC date and time, Press A.\n"
"You can also use the PC terminal application via USB,\n"
"or even an N64 game with RTC support.\n"
"\n"
"Current date & time: %s\n"
"\n",
menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown"
);
component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP,
"A: Change\n"
"B: Back"
);
}
else {
component_actions_bar_text_draw( component_main_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_CENTER, VALIGN_TOP,
"\n" // "A: Save\n" "ADJUST REAL TIME CLOCK\n"
"B: Back" "\n"
); "\n"
"This cart does not support a real time clock."
"\n"
"Current date & time: %s\n"
"\n",
menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown"
);
component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP,
"\n"
"B: Back"
);
}
}
else {
component_actions_bar_text_draw(
ALIGN_RIGHT, VALIGN_TOP,
"Up/Down: Adjust Field\n"
"Left/Right: Switch Field"
);
component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP,
"R: Save\n"
"B: Back"
);
}
if (is_editing_mode) {
component_editdatetime_draw(rtc_tm, editing_field_type);
}
rdpq_detach_show(); rdpq_detach_show();
} }
void view_rtc_init (menu_t *menu) { void view_rtc_init (menu_t *menu) {
// Nothing to initialize (yet) is_editing_mode = false;
editing_field_type = RTC_EDIT_YEAR;
} }
void view_rtc_display (menu_t *menu, surface_t *display) { void view_rtc_display (menu_t *menu, surface_t *display) {

View File

@ -136,6 +136,7 @@ static void draw (menu_t *menu, surface_t *d) {
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"\n\n" "\n\n"
" Default Directory : %s\n\n" " Default Directory : %s\n\n"
" Autoload ROM : %s\n"
"To change the following menu settings, press 'A':\n" "To change the following menu settings, press 'A':\n"
"* PAL60 Mode : %s\n" "* PAL60 Mode : %s\n"
" Show Hidden Files : %s\n" " Show Hidden Files : %s\n"
@ -149,6 +150,7 @@ static void draw (menu_t *menu, surface_t *d) {
"Note: Certain settings have the following caveats:\n" "Note: Certain settings have the following caveats:\n"
"* Requires rebooting the N64 Console.\n", "* Requires rebooting the N64 Console.\n",
menu->settings.default_directory, menu->settings.default_directory,
format_switch(menu->settings.rom_autoload_enabled),
format_switch(menu->settings.pal60_enabled), format_switch(menu->settings.pal60_enabled),
format_switch(menu->settings.show_protected_entries), format_switch(menu->settings.show_protected_entries),
format_switch(menu->settings.use_saves_folder), format_switch(menu->settings.use_saves_folder),

View File

@ -9,6 +9,27 @@ static void draw (menu_t *menu, surface_t *d) {
void view_startup_init (menu_t *menu) { void view_startup_init (menu_t *menu) {
// FIXME: rather than use a controller button, would it be better to use the cart button?
JOYPAD_PORT_FOREACH (port) {
joypad_poll();
joypad_buttons_t b_held = joypad_get_buttons_held(port);
if (menu->settings.rom_autoload_enabled && b_held.start) {
menu->settings.rom_autoload_enabled = false;
menu->settings.rom_autoload_path = "";
menu->settings.rom_autoload_filename = "";
settings_save(&menu->settings);
}
}
if (menu->settings.rom_autoload_enabled) {
menu->browser.directory = path_init(menu->storage_prefix, menu->settings.rom_autoload_path);
menu->load.rom_path = path_clone_push(menu->browser.directory, menu->settings.rom_autoload_filename);
menu->boot_pending.rom_file = true;
menu->next_mode = MENU_MODE_LOAD_ROM;
return;
}
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
} }

View File

@ -57,7 +57,7 @@ static void draw (menu_t *menu, surface_t *d) {
"Joypad 2 is %sconnected %s\n" "Joypad 2 is %sconnected %s\n"
"Joypad 3 is %sconnected %s\n" "Joypad 3 is %sconnected %s\n"
"Joypad 4 is %sconnected %s\n", "Joypad 4 is %sconnected %s\n",
menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown\n", menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown",
is_memory_expanded() ? "" : "not ", is_memory_expanded() ? "" : "not ",
(joypad[0]) ? "" : "not ", format_accessory(0), (joypad[0]) ? "" : "not ", format_accessory(0),
(joypad[1]) ? "" : "not ", format_accessory(1), (joypad[1]) ? "" : "not ", format_accessory(1),