Skip to content

Commit

Permalink
[develop] Add ROM favorites and history tabs to file browser (#168)
Browse files Browse the repository at this point in the history
## Description
Contains the code required for showing history and favourites.

There are now 3 tabs at the top of the browser display: Files, History,
Favorites.

When you launch a game, either rom or disk, the file path for those will
be written out to an ini file called `history.ini`,
which can then be reloaded quickly from the History tab

Roms and Disks can be added as favorites.
Select the rom or Disk as normal, then bring up the options menu (R).
an option to "add to favorites" will be on the new list.
Those can then be selected from the favorites tab.

Currently there are limits to the number of history and favorites and
that is set to 8.

## Motivation and Context
Issue request for last game history and favorites from the menu GUI.

## How Has This Been Tested?
Tested locally on Supercart 64 & SummerCart64,
Tested last loading just a rom, just a disk and a disk and rom
Tested History, loading rom, disk and rom and disk.
Tested favorites using F-Zero X and expansion.

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Documentation Improvement
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>

---------

Co-authored-by: Robin Jones <[email protected]>
  • Loading branch information
thegouldfish and networkfusion authored Jan 4, 2025
1 parent 0affd82 commit 0375789
Show file tree
Hide file tree
Showing 18 changed files with 839 additions and 34 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 \
Expand Down
6 changes: 6 additions & 0 deletions src/menu/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
}

Expand Down
204 changes: 204 additions & 0 deletions src/menu/bookkeeping.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#include <libdragon.h>
#include <mini.c/src/mini.h>

#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; i<count; i++) {
sprintf(buf,"%d_primary_path", i);
list[i].primary_path = path_create(mini_get_string(ini, group, buf, ""));

sprintf(buf,"%d_secondary_path", i);
list[i].secondary_path = path_create(mini_get_string(ini, group, buf, ""));

sprintf(buf,"%d_type", i);
list[i].bookkeeping_type = mini_get_int(ini, group, buf, BOOKKEEPING_TYPE_EMPTY);
}
}

/** @brief The history to load */
void bookkeeping_load (bookkeeping_t *history) {
if (!file_exists(history_path)) {
bookkeeping_save(&init);
}

mini_t *bookkeeping_ini = mini_try_load(history_path);
bookkeeping_ini_load_list(history->history_items, HISTORY_COUNT, bookkeeping_ini, "history");
bookkeeping_ini_load_list(history->favorite_items, HISTORY_COUNT, bookkeeping_ini, "favorite");


mini_free(bookkeeping_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; i<count; i++) {
sprintf(buf, "%d_primary_path", i);
path_t* path = list[i].primary_path;
mini_set_string(ini, group, buf, path != NULL ? path_get(path) : "");

sprintf(buf, "%d_secondary_path", i);
path = list[i].secondary_path;
mini_set_string(ini, group, buf, path != NULL ? path_get(path) : "");

sprintf(buf,"%d_type", i);
mini_set_int(ini, group, buf, list[i].bookkeeping_type);
}
}

/** @brief The history to save */
void bookkeeping_save (bookkeeping_t *history)
{
mini_t *bookkeeping_ini = mini_create(history_path);

bookkeeping_ini_save_list(history->history_items, HISTORY_COUNT, bookkeeping_ini, "history");
bookkeeping_ini_save_list(history->favorite_items, FAVORITES_COUNT, bookkeeping_ini, "favorite");

mini_save(bookkeeping_ini, MINI_FLAGS_SKIP_EMPTY_GROUPS);
mini_free(bookkeeping_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);
}
}
75 changes: 75 additions & 0 deletions src/menu/bookkeeping.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @file bookkeeping.h
* @brief Bookkeeping of loadded ROM's.
* @ingroup menu
*/

#ifndef BOOKKEEPING_H__
#define BOOKKEEPING_H__

#include "path.h"


#define FAVORITES_COUNT 8
#define HISTORY_COUNT 8

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 ROM bookkeeping Structure */
typedef struct {
bookkeeping_item_t history_items[HISTORY_COUNT];

bookkeeping_item_t favorite_items[HISTORY_COUNT];
} bookkeeping_t;


/** @brief Init ROM bookkeeping path */
void bookkeeping_init (char *path);

/** @brief The ROM bookkeeping to load */
void bookkeeping_load (bookkeeping_t *history);

/** @brief The ROM bookkeeping to save */
void bookkeeping_save (bookkeeping_t *history);

/**
* @brief Add a ROM to the history.
*
* @param bookkeeping The bookkeeping structure.
* @param primary_path The primary path of the ROM.
* @param secondary_path The secondary path of the ROM.
* @param type The type of the bookkeeping item.
*/
void bookkeeping_history_add(bookkeeping_t *bookkeeping, path_t *primary_path, path_t *secondary_path, bookkeeping_item_types_t type );

/**
* @brief Add a ROM to the favorites.
*
* @param bookkeeping The bookkeeping structure.
* @param primary_path The primary path of the ROM.
* @param secondary_path The secondary path of the ROM.
* @param type The type of the bookkeeping item.
*/
void bookkeeping_favorite_add(bookkeeping_t *bookkeeping, path_t *primary_path, path_t *secondary_path, bookkeeping_item_types_t type );

/**
* @brief Remove a ROM from the favorites.
*
* @param bookkeeping The bookkeeping structure.
* @param selection The index of the favorite item to remove.
*/
void bookkeeping_favorite_remove(bookkeeping_t *bookkeeping, int selection);

#endif
24 changes: 17 additions & 7 deletions src/menu/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@
#include "views/views.h"


#define MENU_DIRECTORY "/menu"
#define MENU_SETTINGS_FILE "config.ini"
#define MENU_CUSTOM_FONT_FILE "custom.font64"
#define MENU_DIRECTORY "/menu"
#define MENU_SETTINGS_FILE "config.ini"
#define MENU_CUSTOM_FONT_FILE "custom.font64"
#define MENU_ROM_LOAD_HISTORY_FILE "history.ini"

#define MENU_CACHE_DIRECTORY "cache"
#define BACKGROUND_CACHE_FILE "background.data"
#define MENU_CACHE_DIRECTORY "cache"
#define BACKGROUND_CACHE_FILE "background.data"

#define INTERLACED (true)
#define FPS_LIMIT (30.0f)
#define INTERLACED (true)
#define FPS_LIMIT (30.0f)


static menu_t *menu;
Expand Down Expand Up @@ -70,6 +71,13 @@ static void menu_init (boot_params_t *boot_params) {
settings_load(&menu->settings);
path_pop(path);

path_push(path, MENU_ROM_LOAD_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,
Expand Down Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit 0375789

Please sign in to comment.