diff --git a/Makefile b/Makefile index 052b3c44..990c355c 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,7 @@ SRCS = \ libs/miniz/miniz_zip.c \ libs/miniz/miniz.c \ menu/actions.c \ + menu/bookkeeping.c \ menu/cart_load.c \ menu/disk_info.c \ menu/fonts.c \ @@ -56,12 +57,14 @@ SRCS = \ menu/ui_components/common.c \ menu/ui_components/context_menu.c \ menu/ui_components/file_list.c \ + menu/ui_components/tabs.c \ menu/usb_comm.c \ menu/views/browser.c \ menu/views/credits.c \ menu/views/error.c \ menu/views/fault.c \ menu/views/file_info.c \ + menu/views/history_favorites.c \ menu/views/image_viewer.c \ menu/views/text_viewer.c \ menu/views/load_disk.c \ diff --git a/New Text Document.txt b/New Text Document.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/menu/actions.c b/src/menu/actions.c index 4cafbce7..659c134d 100644 --- a/src/menu/actions.c +++ b/src/menu/actions.c @@ -22,6 +22,8 @@ static void actions_clear (menu_t *menu) { menu->actions.options = false; menu->actions.settings = false; menu->actions.lz_context = false; + menu->actions.previous_tab = false; + menu->actions.next_tab = false; } static void actions_update_direction (menu_t *menu) { @@ -109,6 +111,10 @@ static void actions_update_buttons (menu_t *menu) { menu->actions.settings = true; } else if (pressed.l || pressed.z) { menu->actions.lz_context = true; + } else if (pressed.c_left) { + menu->actions.previous_tab = true; + } else if (pressed.c_right) { + menu->actions.next_tab = true; } } diff --git a/src/menu/bookkeeping.c b/src/menu/bookkeeping.c new file mode 100644 index 00000000..7de26f85 --- /dev/null +++ b/src/menu/bookkeeping.c @@ -0,0 +1,204 @@ +#include +#include + +#include "bookkeeping.h" +#include "utils/fs.h" +#include "path.h" + +static char *history_path = NULL; + +static path_t* empty_path = NULL; +static bookkeeping_t init; + +/** @brief Init history path */ +void bookkeeping_init (char *path) { + if (history_path) { + free(history_path); + } + history_path = strdup(path); + empty_path = path_create(""); +} + + +void bookkeeping_ini_load_list(bookkeeping_item_t* list, int count, mini_t* ini, const char* group) +{ + char buf[64]; + for(int i=0;ihistory_items, HISTORY_COUNT, ini, "history"); + bookkeeping_ini_load_list(history->favorite_items, HISTORY_COUNT, ini, "favorite"); + + + mini_free(ini); +} + +static void bookkeeping_ini_save_list(bookkeeping_item_t* list, int count, mini_t* ini, const char* group) +{ + char buf[64]; + for(int i=0;ihistory_items, HISTORY_COUNT, ini, "history"); + bookkeeping_ini_save_list(history->favorite_items, FAVORITES_COUNT, ini, "favorite"); + + mini_save(ini, MINI_FLAGS_SKIP_EMPTY_GROUPS); + mini_free(ini); +} + +static bool bookkeeping_item_match(bookkeeping_item_t* left, bookkeeping_item_t* right) { + if(left != NULL && right != NULL) { + return path_are_match(left->primary_path, right->primary_path) && path_are_match(left->secondary_path, right->secondary_path) && left->bookkeeping_type == right->bookkeeping_type; + } + + return false; +} + +static void bookkeeping_clear_item(bookkeeping_item_t* item, bool leave_null) { + if(item->primary_path != NULL){ + path_free(item->primary_path); + + if(leave_null) { + item->primary_path = NULL; + } else { + item->primary_path = path_create(""); + } + } + if(item->secondary_path != NULL){ + path_free(item->secondary_path); + + if(leave_null) { + item->secondary_path = NULL; + } else { + item->secondary_path = path_create(""); + } + } + item->bookkeeping_type = BOOKKEEPING_TYPE_EMPTY; +} + +static void bookkeeping_copy_item(bookkeeping_item_t* source, bookkeeping_item_t* destination) { + bookkeeping_clear_item(destination, true); + + destination->primary_path = path_clone(source->primary_path); + destination->secondary_path = source->secondary_path != NULL ? path_clone(source->secondary_path) : path_create(""); + destination->bookkeeping_type = source->bookkeeping_type; +} + +static void bookkeeping_move_items_down(bookkeeping_item_t* list, int start, int end) { + int current = end; + + do { + if(current <= start || current < 0) { + break; + } + + bookkeeping_copy_item(&list[current - 1], &list[current]); + current--; + } while(true); +} + + +static void bookkeeping_move_items_up(bookkeeping_item_t* list, int start, int end) { + int current = start; + + do { + if(current > end) { + break; + } + + bookkeeping_copy_item(&list[current + 1], &list[current]); + current++; + } while(true); +} + + +static void bookkeeping_insert_top(bookkeeping_item_t* list, int count, bookkeeping_item_t* new_item) { + // if it matches the top of the list already then nothing to do + if(bookkeeping_item_match(&list[0], new_item)) { + return; + } + + // if the top isn't empty then we need to move things around + if(list[0].bookkeeping_type != BOOKKEEPING_TYPE_EMPTY) { + int found_at = -1; + for(int i=1; i < count; i++) { + if(bookkeeping_item_match(&list[i], new_item)){ + found_at = i; + break; + } + } + + if(found_at == -1) { + bookkeeping_move_items_down(list, 0, count - 1); + } else { + bookkeeping_move_items_down(list, 0, found_at); + } + } + + bookkeeping_copy_item(new_item, &list[0]); +} + +void bookkeeping_history_add(bookkeeping_t *bookkeeping, path_t* primary_path, path_t* secondary_path, bookkeeping_item_types_t type ) { + bookkeeping_item_t new_item = { + .primary_path = primary_path, + .secondary_path = secondary_path, + .bookkeeping_type = type + }; + + bookkeeping_insert_top(bookkeeping->history_items, HISTORY_COUNT, &new_item); + bookkeeping_save(bookkeeping); +} + + +void bookkeeping_favorite_add(bookkeeping_t *bookkeeping, path_t* primary_path, path_t* secondary_path, bookkeeping_item_types_t type ) { + bookkeeping_item_t new_item = { + .primary_path = primary_path, + .secondary_path = secondary_path, + .bookkeeping_type = type + }; + + bookkeeping_insert_top(bookkeeping->favorite_items, FAVORITES_COUNT, &new_item); + bookkeeping_save(bookkeeping); +} + +void bookkeeping_favorite_remove(bookkeeping_t *bookkeeping, int selection) { + if(bookkeeping->favorite_items[selection].bookkeeping_type != BOOKKEEPING_TYPE_EMPTY) { + + bookkeeping_move_items_up(bookkeeping->favorite_items, selection, FAVORITES_COUNT -1); + bookkeeping_clear_item(&bookkeeping->favorite_items[FAVORITES_COUNT -1], false); + + bookkeeping_save(bookkeeping); + } +} \ No newline at end of file diff --git a/src/menu/bookkeeping.h b/src/menu/bookkeeping.h new file mode 100644 index 00000000..b365bf04 --- /dev/null +++ b/src/menu/bookkeeping.h @@ -0,0 +1,52 @@ +/** + * @file rom_history.h + * @brief Menu History + * @ingroup menu + */ + +#ifndef HISTORY_H__ +#define HISTORY_H__ + +#include "path.h" + + +#define FAVORITES_COUNT 5 +#define HISTORY_COUNT 5 + +typedef enum { + BOOKKEEPING_TYPE_EMPTY, + BOOKKEEPING_TYPE_ROM, + BOOKKEEPING_TYPE_DISK, +} bookkeeping_item_types_t; + +typedef struct { + path_t* primary_path; + path_t* secondary_path; + + bookkeeping_item_types_t bookkeeping_type; + +} bookkeeping_item_t; + +/** @brief history Structure */ +typedef struct { + bookkeeping_item_t history_items[HISTORY_COUNT]; + + bookkeeping_item_t favorite_items[HISTORY_COUNT]; +} bookkeeping_t; + + +/** @brief Init history path */ +void bookkeeping_init (char *path); + +/** @brief The history to load */ +void bookkeeping_load (bookkeeping_t *history); + +/** @brief The history to save */ +void bookkeeping_save (bookkeeping_t *history); + +void bookkeeping_history_add(bookkeeping_t *bookkeeping, path_t* primary_path, path_t* secondary_path, bookkeeping_item_types_t type ); + +void bookkeeping_favorite_add(bookkeeping_t *bookkeeping, path_t* primary_path, path_t* secondary_path, bookkeeping_item_types_t type ); +void bookkeeping_favorite_remove(bookkeeping_t *bookkeeping, int selection); + +#endif \ No newline at end of file diff --git a/src/menu/menu.c b/src/menu/menu.c index 91a51a09..c032d36a 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -23,6 +23,7 @@ #define MENU_DIRECTORY "/menu" #define MENU_SETTINGS_FILE "config.ini" #define MENU_CUSTOM_FONT_FILE "custom.font64" +#define MENU_HISTORY_FILE "history.ini" #define MENU_CACHE_DIRECTORY "cache" #define BACKGROUND_CACHE_FILE "background.data" @@ -70,6 +71,13 @@ static void menu_init (boot_params_t *boot_params) { settings_load(&menu->settings); path_pop(path); + path_push(path, MENU_HISTORY_FILE); + bookkeeping_init(path_get(path)); + bookkeeping_load(&menu->bookkeeping); + menu->load.load_history = -1; + menu->load.load_favorite = -1; + path_pop(path); + resolution_t resolution = { .width = 640, .height = 480, @@ -150,6 +158,8 @@ static view_t menu_views[] = { { MENU_MODE_LOAD_EMULATOR, view_load_emulator_init, view_load_emulator_display }, { MENU_MODE_ERROR, view_error_init, view_error_display }, { MENU_MODE_FAULT, view_fault_init, view_fault_display }, + { MENU_MODE_FAVORITE, view_favorite_init, view_favorite_display }, + { MENU_MODE_HISTORY, view_history_init, view_history_display } }; static view_t *menu_get_view (menu_mode_t id) { diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index eaeb83ec..8781e0c4 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -16,6 +16,7 @@ #include "path.h" #include "rom_info.h" #include "settings.h" +#include "bookkeeping.h" /** @brief Menu mode enumeration */ @@ -38,6 +39,8 @@ typedef enum { MENU_MODE_ERROR, MENU_MODE_FAULT, MENU_MODE_BOOT, + MENU_MODE_FAVORITE, + MENU_MODE_HISTORY } menu_mode_t; /** @brief File entry type enumeration */ @@ -67,6 +70,7 @@ typedef struct { const char *storage_prefix; settings_t settings; + bookkeeping_t bookkeeping; boot_params_t *boot_params; char *error_message; @@ -86,6 +90,8 @@ typedef struct { bool options; bool settings; bool lz_context; + bool previous_tab; + bool next_tab; } actions; struct { @@ -103,6 +109,8 @@ typedef struct { rom_info_t rom_info; path_t *disk_path; disk_info_t disk_info; + int load_history; + int load_favorite; } load; struct { diff --git a/src/menu/path.c b/src/menu/path.c index 59f622f2..580aec06 100644 --- a/src/menu/path.c +++ b/src/menu/path.c @@ -19,7 +19,7 @@ static void path_resize (path_t *path, size_t min_length) { assert(path->buffer != NULL); } -static path_t *path_create (const char *string) { +path_t *path_create (const char *string) { if (string == NULL) { string = ""; } @@ -141,3 +141,22 @@ void path_ext_replace (path_t *path, char *ext) { path_append(path, "."); path_append(path, ext); } + +bool path_has_value(path_t *path) { + if(path != NULL) { + if(strlen(path->buffer) > 0) { + return true; + } + } + return false; +} + +bool path_are_match(path_t *left, path_t *right) { + if(!path_has_value(left) && !path_has_value(right)) { + return true; + } else if(path_has_value(left) && path_has_value(right)) { + return (strcmp(path_get(left), path_get(right)) == 0); + } else { + return false; + } +} \ No newline at end of file diff --git a/src/menu/path.h b/src/menu/path.h index df2672d2..0b0f534c 100644 --- a/src/menu/path.h +++ b/src/menu/path.h @@ -19,7 +19,7 @@ typedef struct { size_t capacity; } path_t; - +path_t *path_create (const char *string); path_t *path_init (const char *prefix, char *string); void path_free (path_t *path); path_t *path_clone (path_t *string); @@ -33,6 +33,7 @@ void path_push_subdir (path_t *path, char *string); char *path_ext_get (path_t *path); void path_ext_remove (path_t *path); void path_ext_replace (path_t *path, char *ext); - +bool path_has_value(path_t *path); +bool path_are_match(path_t *left, path_t *right); #endif diff --git a/src/menu/ui_components.h b/src/menu/ui_components.h index f0dc6060..7274b409 100644 --- a/src/menu/ui_components.h +++ b/src/menu/ui_components.h @@ -252,4 +252,19 @@ void ui_components_boxart_free(component_boxart_t *b); */ void ui_components_boxart_draw(component_boxart_t *b); + +/** + * + */ +void ui_components_main_text_draw_location (float x, float y, char *fmt, ...); + +/** + * + */ +void ui_components_tabs_draw(const char** text, int count, int selected, int width ); + +/** + * + */ +void ui_compontents_tabs_common_draw(int selected); #endif /* UI_COMPONENTS_H__ */ diff --git a/src/menu/ui_components/common.c b/src/menu/ui_components/common.c index dec31885..33fdbc98 100644 --- a/src/menu/ui_components/common.c +++ b/src/menu/ui_components/common.c @@ -13,9 +13,10 @@ void ui_components_box_draw (int x0, int y0, int x1, int y1, color_t color) { rdpq_mode_pop(); } -void ui_components_border_draw (int x0, int y0, int x1, int y1) { + +static void ui_components_border_draw_interal (int x0, int y0, int x1, int y1, color_t color) { rdpq_mode_push(); - rdpq_set_mode_fill(BORDER_COLOR); + rdpq_set_mode_fill(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); @@ -25,6 +26,10 @@ void ui_components_border_draw (int x0, int y0, int x1, int y1) { rdpq_mode_pop(); } +void ui_components_border_draw (int x0, int y0, int x1, int y1) { + ui_components_border_draw_interal(x0, y0, x1, y1, BORDER_COLOR); +} + void ui_components_layout_draw (void) { ui_components_border_draw( VISIBLE_AREA_X0, @@ -195,3 +200,134 @@ void ui_components_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t vali free(formatted); } } + +void ui_components_main_text_draw_location (float x, float y, 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 = LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT - (TEXT_MARGIN_VERTICAL * 2), + .align = ALIGN_LEFT, + .valign = VALIGN_TOP, + .wrap = WRAP_ELLIPSES, + .line_spacing = TEXT_LINE_SPACING_ADJUST, + }, + FNT_DEFAULT, + x, + y, + ///VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL + x, + //VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + TEXT_OFFSET_VERTICAL + y, + formatted, + nbytes + ); + + if (formatted != buffer) { + free(formatted); + } +} + + + +static void ui_components_main_text_draw_tab (float x, float y, float width, 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 = width, + .height = LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT - (TEXT_MARGIN_VERTICAL * 2), + .align = ALIGN_CENTER, + .valign = VALIGN_TOP, + .wrap = WRAP_ELLIPSES, + .line_spacing = TEXT_OFFSET_VERTICAL, + }, + FNT_DEFAULT, + x, + y, + formatted, + nbytes + ); + + if (formatted != buffer) { + free(formatted); + } +} + +void ui_components_tabs_draw(const char** text, int count, int selected, int width ) { + + ui_components_box_draw( + VISIBLE_AREA_X0, + LAYOUT_ACTIONS_SEPARATOR_Y, + VISIBLE_AREA_X1, + LAYOUT_ACTIONS_SEPARATOR_Y + BORDER_THICKNESS, + BORDER_COLOR + ); + + int starting_x = VISIBLE_AREA_X0 + 4; + + int x = starting_x; + int y = OVERSCAN_HEIGHT; + int height = TAB_HEIGHT; + + // first draw the tabs that are not selected + for(int i=0;i< count;i++) { + if(i != selected) { + + ui_components_box_draw( + x, + y, + x + width, + y + height, + TAB_INACTIVE_BACKGROUND_COLOR + ); + + ui_components_border_draw_interal( + x, + y, + x + width, + y + height, + TAB_INACTIVE_BORDER_COLOR + ); + + ui_components_main_text_draw_tab(x + 4, y, width, (char*)text[i]); + } + + x += width; + } + // draw the selected tab + + if(selected >= 0 && selected < count) { + x = starting_x + (width * selected); + + ui_components_box_draw( + x, + y, + x + width, + y + height, + TAB_ACTIVE_BACKGROUND_COLOR + ); + + ui_components_border_draw_interal( + x, + y, + x + width, + y + height, + TAB_ACTIVE_BORDER_COLOR + ); + + + ui_components_main_text_draw_tab(x + 4, y, width, (char*)text[selected]); + } +} diff --git a/src/menu/ui_components/constants.h b/src/menu/ui_components/constants.h index c468a327..a352e9e4 100644 --- a/src/menu/ui_components/constants.h +++ b/src/menu/ui_components/constants.h @@ -7,6 +7,10 @@ #ifndef COMPONENTS_CONSTANTS_H__ #define COMPONENTS_CONSTANTS_H__ +#define TAB_HEIGHT (20) +/** @brief The thickness of borders. */ +#define BORDER_THICKNESS (4) + /** @brief The display width. */ #define DISPLAY_WIDTH (640) /** @brief The display height. */ @@ -25,7 +29,7 @@ /** @brief The start position of the visible display on the X axis. */ #define VISIBLE_AREA_X0 (OVERSCAN_WIDTH) /** @brief The start position of the visible display on the Y axis. */ -#define VISIBLE_AREA_Y0 (OVERSCAN_HEIGHT) +#define VISIBLE_AREA_Y0 (OVERSCAN_HEIGHT + TAB_HEIGHT + BORDER_THICKNESS) /** @brief The end position of the visible display on the X axis. */ #define VISIBLE_AREA_X1 (DISPLAY_WIDTH - OVERSCAN_WIDTH) /** @brief The end position of the visible display on the Y axis. */ @@ -36,9 +40,8 @@ /** @brief The height of the visible display. */ #define VISIBLE_AREA_HEIGHT (VISIBLE_AREA_Y1 - VISIBLE_AREA_Y0) -/** @brief The thickness of borders. */ -#define BORDER_THICKNESS (4) +// 400 #define LAYOUT_ACTIONS_SEPARATOR_Y (400) /** @brief The seek bar height. */ @@ -101,14 +104,14 @@ /** @brief The scroll bar width. */ #define LIST_SCROLLBAR_WIDTH (12) /** @brief The scroll bar height. */ -#define LIST_SCROLLBAR_HEIGHT (LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT) +#define LIST_SCROLLBAR_HEIGHT (LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT - TAB_HEIGHT) /** @brief The scroll bar position on the X axis. */ #define LIST_SCROLLBAR_X (VISIBLE_AREA_X1 - LIST_SCROLLBAR_WIDTH) /** @brief The scroll bar position on the Y axis. */ #define LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0) /** @brief The maximum amount of file list entries. */ -#define LIST_ENTRIES (19) +#define LIST_ENTRIES (17) /** @brief The maximum width available for a file list entry. */ #define FILE_LIST_MAX_WIDTH (480) #define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - LIST_SCROLLBAR_WIDTH) @@ -147,4 +150,10 @@ #define CONTEXT_MENU_HIGHLIGHT_COLOR RGBA32(0x3F, 0x3F, 0x3F, 0xFF) +#define TAB_INACTIVE_BORDER_COLOR RGBA32(0x5F, 0x5F, 0x5F, 0xFF) +#define TAB_ACTIVE_BORDER_COLOR RGBA32(0x00, 0x00, 0xFF, 0xFF) + +#define TAB_INACTIVE_BACKGROUND_COLOR RGBA32(0x3F, 0x3F, 0x3F, 0xFF) +#define TAB_ACTIVE_BACKGROUND_COLOR RGBA32(0x6F, 0x6F, 0x6F, 0xFF) + #endif diff --git a/src/menu/ui_components/tabs.c b/src/menu/ui_components/tabs.c new file mode 100644 index 00000000..ac5e3865 --- /dev/null +++ b/src/menu/ui_components/tabs.c @@ -0,0 +1,15 @@ +#include "../ui_components.h" + + + +static const char* tabs[3] = +{ + "Files", + "History", + "Favorites" +}; + +void ui_compontents_tabs_common_draw(int selected) +{ + ui_components_tabs_draw(tabs, 3, selected, 189); +} \ No newline at end of file diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index 53d445f4..3e64e901 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -358,10 +358,13 @@ static void process (menu_t *menu) { } else if (menu->actions.settings) { ui_components_context_menu_show(&settings_context_menu); sound_play_effect(SFX_SETTING); + } else if (menu->actions.next_tab) { + menu->next_mode = MENU_MODE_HISTORY; + } else if (menu->actions.previous_tab) { + menu->next_mode = MENU_MODE_FAVORITE; } } - static void draw (menu_t *menu, surface_t *d) { rdpq_attach(d, NULL); @@ -371,6 +374,8 @@ static void draw (menu_t *menu, surface_t *d) { ui_components_file_list_draw(menu->browser.list, menu->browser.entries, menu->browser.selected); + ui_compontents_tabs_common_draw(0); + const char *action = NULL; if (menu->browser.entry) { @@ -403,12 +408,20 @@ static void draw (menu_t *menu, surface_t *d) { if (menu->current_time >= 0) { ui_components_actions_bar_text_draw( ALIGN_CENTER, VALIGN_TOP, - "\n" - "%s", + "%s" //ctime includes a newline + "", ctime(&menu->current_time) ); + } else { + ui_components_actions_bar_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "\n" + "" + ); } + + ui_components_context_menu_draw(&entry_context_menu); ui_components_context_menu_draw(&settings_context_menu); diff --git a/src/menu/views/history_favorites.c b/src/menu/views/history_favorites.c new file mode 100644 index 00000000..d7629d57 --- /dev/null +++ b/src/menu/views/history_favorites.c @@ -0,0 +1,225 @@ +#include +#include "views.h" +#include "../bookkeeping.h" +#include "../fonts.h" +#include "../ui_components/constants.h" +#include "../sound.h" + + +typedef enum { + BOOKKEEPING_SCREEN_MODE_HISTORY, + BOOKKEEPING_SCREEN_MODE_FAVORITE +} bookkeeping_screen_modes_t; + +static bookkeeping_screen_modes_t screen_mode = -1; +static int selected_item = -1; +static bookkeeping_item_t* item_list; +static int item_max; + +#define IS_FAVORITE (screen_mode == BOOKKEEPING_SCREEN_MODE_FAVORITE) +#define IS_HISTORY (screen_mode == BOOKKEEPING_SCREEN_MODE_HISTORY) + +static void reset_selected(menu_t* menu) { + selected_item = -1; + + for(int i=0;i= item_max) { + selected_item = last; + break; + } else if(item_list[selected_item].bookkeeping_type != BOOKKEEPING_TYPE_EMPTY) { + sound_play_effect(SFX_CURSOR); + break; + } + } while (true); +} + +static void move_back() { + int last = selected_item; + do + { + selected_item--; + + if(selected_item < 0) { + selected_item = last; + break; + } else if(item_list[selected_item].bookkeeping_type != BOOKKEEPING_TYPE_EMPTY) { + sound_play_effect(SFX_CURSOR); + break; + } + } while (true); +} + + + +static void process(menu_t* menu) { + if(menu->actions.go_down) { + move_next(); + } else if(menu->actions.go_up) { + move_back(); + } else if(menu->actions.enter && selected_item != -1) { + + if(IS_FAVORITE) { + menu->load.load_favorite = selected_item; + } else if(IS_HISTORY) { + menu->load.load_history = selected_item; + } + + if(item_list[selected_item].bookkeeping_type == BOOKKEEPING_TYPE_DISK) { + menu->next_mode = MENU_MODE_LOAD_DISK; + sound_play_effect(SFX_ENTER); + } else if(item_list[selected_item].bookkeeping_type == BOOKKEEPING_TYPE_ROM) { + menu->next_mode = MENU_MODE_LOAD_ROM; + sound_play_effect(SFX_ENTER); + } + } else if (menu->actions.previous_tab) { + if(IS_FAVORITE) { + menu->next_mode = MENU_MODE_HISTORY; + } else if(IS_HISTORY) { + menu->next_mode = MENU_MODE_BROWSER; + } + } else if (menu->actions.next_tab) { + if(IS_FAVORITE) { + menu->next_mode = MENU_MODE_BROWSER; + } else if(IS_HISTORY) { + menu->next_mode = MENU_MODE_FAVORITE; + } + }else if(IS_FAVORITE && menu->actions.options && selected_item != -1) { + bookkeeping_favorite_remove(&menu->bookkeeping, selected_item); + reset_selected(menu); + sound_play_effect(SFX_SETTING); + } +} + +static void draw_list(menu_t *menu, surface_t *display) { + if(selected_item != -1) { + float highlight_y = VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + TEXT_OFFSET_VERTICAL + (selected_item * 20 * 2); + + ui_components_box_draw( + VISIBLE_AREA_X0, + highlight_y, + VISIBLE_AREA_X0 + FILE_LIST_HIGHLIGHT_WIDTH, + highlight_y + 40, + FILE_LIST_HIGHLIGHT_COLOR + ); + } + + + char buffer[1024]; + buffer[0] = 0; + + for(int i=0;i < item_max; i++) { + if(path_has_value(item_list[i].primary_path)) { + sprintf(buffer, "%s%d : %s\n",buffer ,(i+1), path_last_get(item_list[i].primary_path)); + } else { + sprintf(buffer, "%s%d : \n",buffer ,(i+1)); + } + + if(path_has_value(item_list[i].secondary_path)) { + sprintf(buffer, "%s %s\n", buffer, path_last_get(item_list[i].secondary_path)); + } else { + sprintf(buffer, "%s\n", buffer); + } + } + + int nbytes = strlen(buffer); + 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_LEFT, + .valign = VALIGN_TOP, + .wrap = WRAP_ELLIPSES, + .line_spacing = TEXT_OFFSET_VERTICAL, + }, + FNT_DEFAULT, + VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL, + VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + TEXT_OFFSET_VERTICAL, + buffer, + nbytes + ); +} + +static void draw(menu_t *menu, surface_t *display) { + rdpq_attach(display, NULL); + + ui_components_background_draw(); + + ui_components_layout_draw(); + + if(IS_FAVORITE) { + ui_compontents_tabs_common_draw(2); + } else if(IS_HISTORY) { + ui_compontents_tabs_common_draw(1); + } + + draw_list(menu, display); + + if(selected_item != -1) { + ui_components_actions_bar_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "A: Load Game" + ); + + if(IS_FAVORITE && selected_item != -1) { + ui_components_actions_bar_text_draw( + ALIGN_RIGHT, VALIGN_TOP, + "R: Remove Favorite" + ); + } + } + + ui_components_actions_bar_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "\n" + "" + ); + + rdpq_detach_show(); +} + + + + +void view_favorite_init (menu_t *menu) { + screen_mode = BOOKKEEPING_SCREEN_MODE_FAVORITE; + item_list = menu->bookkeeping.favorite_items; + item_max = FAVORITES_COUNT; + + reset_selected(menu); +} + +void view_favorite_display (menu_t *menu, surface_t *display) { + process(menu); + draw(menu, display); +} + + + +void view_history_init (menu_t *menu) { + screen_mode = BOOKKEEPING_SCREEN_MODE_HISTORY; + item_list = menu->bookkeeping.history_items; + item_max = HISTORY_COUNT; + + reset_selected(menu); +} + +void view_history_display (menu_t *menu, surface_t *display) { + process(menu); + draw(menu, display); +} \ No newline at end of file diff --git a/src/menu/views/load_disk.c b/src/menu/views/load_disk.c index c1e24a79..ab11fa85 100644 --- a/src/menu/views/load_disk.c +++ b/src/menu/views/load_disk.c @@ -3,11 +3,12 @@ #include "boot/boot.h" #include "../sound.h" #include "views.h" +#include "../bookkeeping.h" static bool load_disk_with_rom; static component_boxart_t *boxart; - +static char* name; static char *convert_error_message (disk_err_t err) { switch (err) { @@ -57,7 +58,7 @@ static void draw (menu_t *menu, surface_t *d) { "64DD disk information\n" "\n" "%s", - menu->browser.entry->name + name ); ui_components_main_text_draw( @@ -86,6 +87,12 @@ static void draw (menu_t *menu, surface_t *d) { "B: Exit" ); + ui_components_actions_bar_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "\n" + "C>: Favorite" + ); + if (menu->load.rom_path) { ui_components_actions_bar_text_draw( ALIGN_RIGHT, VALIGN_TOP, @@ -132,6 +139,7 @@ static void load (menu_t *menu) { return; } + bookkeeping_history_add(&menu->bookkeeping, menu->load.disk_path, menu->load.rom_path, BOOKKEEPING_TYPE_DISK); menu->next_mode = MENU_MODE_BOOT; if (load_disk_with_rom) { @@ -156,6 +164,27 @@ static void deinit (void) { ui_components_boxart_free(boxart); } +static bool load_rom(menu_t* menu, path_t* rom_path) { + if(path_has_value(rom_path)) { + if (menu->load.rom_path) { + path_free(menu->load.rom_path); + menu->load.rom_path = NULL; + } + + menu->load.rom_path = path_clone(rom_path); + + rom_err_t err = rom_info_load(rom_path, &menu->load.rom_info); + if (err != ROM_OK) { + path_free(menu->load.rom_path); + menu->load.rom_path = NULL; + menu_show_error(menu, convert_error_message(err)); + return false; + } + } + + return true; +} + void view_load_disk_init (menu_t *menu) { if (menu->load.disk_path) { path_free(menu->load.disk_path); @@ -164,10 +193,34 @@ void view_load_disk_init (menu_t *menu) { menu->boot_pending.disk_file = false; - menu->load.disk_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); + if(menu->load.load_history != -1 || menu->load.load_favorite != -1) { + int id = -1; + bookkeeping_item_t* items; + + if(menu->load.load_history != -1) { + id = menu->load.load_history; + items = menu->bookkeeping.history_items; + } else if (menu->load.load_favorite != -1) { + id = menu->load.load_favorite; + items = menu->bookkeeping.favorite_items; + } + + menu->load.disk_path = path_clone(items[id].primary_path); + if(!load_rom(menu, items[id].secondary_path)) { + return; + } + + } else { + menu->load.disk_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); + } + + menu->load.load_favorite = -1; + menu->load.load_history = -1; + name = path_last_get(menu->load.disk_path); disk_err_t err = disk_info_load(menu->load.disk_path, &menu->load.disk_info); if (err != DISK_OK) { + //snprintf(error, 256, "path : '%s' error: '%s'", path_get(menu->load.disk_path), convert_error_message(err)); menu_show_error(menu, convert_error_message(err)); return; } diff --git a/src/menu/views/load_rom.c b/src/menu/views/load_rom.c index fb95003f..545e17e4 100644 --- a/src/menu/views/load_rom.c +++ b/src/menu/views/load_rom.c @@ -5,9 +5,11 @@ #include "views.h" #include #include "utils/fs.h" +#include "../bookkeeping.h" static bool show_extra_info_message = false; static component_boxart_t *boxart; +static char* name = NULL; static char *convert_error_message (rom_err_t err) { switch (err) { @@ -156,6 +158,10 @@ static void set_autoload_type (menu_t *menu, void *arg) { menu->browser.reload = true; } +static void add_favorite (menu_t *menu, void *arg) { + bookkeeping_favorite_add(&menu->bookkeeping, menu->load.rom_path, NULL, BOOKKEEPING_TYPE_ROM); +} + static component_context_menu_t set_cic_type_context_menu = { .list = { {.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) }, @@ -199,6 +205,7 @@ static component_context_menu_t options_context_menu = { .list = { { .text = "Set Save Type", .submenu = &set_save_type_context_menu }, { .text = "Set TV Type", .submenu = &set_tv_type_context_menu }, { .text = "Set ROM to autoload", .action = set_autoload_type }, + { .text = "Add to favorite", .action = add_favorite }, COMPONENT_CONTEXT_MENU_LIST_END, }}; @@ -240,7 +247,7 @@ static void draw (menu_t *menu, surface_t *d) { "N64 ROM information\n" "\n" "%s", - menu->browser.entry->name + name ); ui_components_main_text_draw( @@ -249,7 +256,7 @@ static void draw (menu_t *menu, surface_t *d) { "\n" "\n" "\n" - "Description:\n None.\n\n\n\n\n\n\n\n" + "Description:\n None.\n\n\n\n\n\n\n" "Expansion PAK: %s\n" "TV type: %s\n" "CIC: %s\n" @@ -334,6 +341,8 @@ static void load (menu_t *menu) { return; } + bookkeeping_history_add(&menu->bookkeeping, menu->load.rom_path, NULL, BOOKKEEPING_TYPE_ROM); + menu->next_mode = MENU_MODE_BOOT; menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; @@ -359,8 +368,19 @@ void view_load_rom_init (menu_t *menu) { path_free(menu->load.rom_path); } - menu->load.rom_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - } + if(menu->load.load_history != -1) { + menu->load.rom_path = path_clone(menu->bookkeeping.history_items[menu->load.load_history].primary_path); + } else if(menu->load.load_favorite != -1) { + menu->load.rom_path = path_clone(menu->bookkeeping.favorite_items[menu->load.load_history].primary_path); + } else { + menu->load.rom_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); + } + + name = path_last_get(menu->load.rom_path); + } + + menu->load.load_favorite = -1; + menu->load.load_history = -1; rom_err_t err = rom_info_load(menu->load.rom_path, &menu->load.rom_info); if (err != ROM_OK) { diff --git a/src/menu/views/views.h b/src/menu/views/views.h index 8c475280..d088f8b2 100644 --- a/src/menu/views/views.h +++ b/src/menu/views/views.h @@ -65,7 +65,13 @@ 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 view_favorite_init (menu_t *menu); +void view_favorite_display (menu_t *menu, surface_t *display); + +void view_history_init (menu_t *menu); + void menu_show_error (menu_t *menu, char *error_message); +void view_history_display (menu_t *menu, surface_t *display); /** @} */ /* view */