Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions workspace/all/common/defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#define HIDE_BOXART_IFSTATE_PATH SHARED_USERDATA_PATH "/hide-boxart-ifstate"
#define AUTO_RESUME_PATH SHARED_USERDATA_PATH "/.minui/auto_resume.txt"
#define AUTO_RESUME_SLOT 0
#define GAME_SWITCHER_PERSIST_PATH SHARED_USERDATA_PATH "/.minui/game_switcher.txt"
#define TOOLBOXART_CFGFILE SDCARD_PATH "/Tools/" THISPLATFORM "/Convert BoxArt.pak/toolboxart.cfg"
#define GAMEBOXART_CFGFILE SDCARD_PATH "/Tools/" THISPLATFORM "/Convert BoxArt.pak/gameboxart.cfg"
#define AUTO_RESUME_MODIFIER_PATH SHARED_USERDATA_PATH "/.minui/auto_resume_modifier.txt"
Expand Down
17 changes: 16 additions & 1 deletion workspace/all/minarch/minarch.c
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ enum {
SHORTCUT_CYCLE_EFFECT,
SHORTCUT_TOGGLE_FF,
SHORTCUT_HOLD_FF,
SHORTCUT_GAMESWITCHER,
SHORTCUT_COUNT,
};

Expand Down Expand Up @@ -1047,6 +1048,7 @@ static struct Config {
[SHORTCUT_CYCLE_EFFECT] = {"Cycle Effect", -1, BTN_ID_NONE, 0},
[SHORTCUT_TOGGLE_FF] = {"Toggle FF", -1, BTN_ID_NONE, 0},
[SHORTCUT_HOLD_FF] = {"Hold FF", -1, BTN_ID_NONE, 0},
[SHORTCUT_GAMESWITCHER] = {"Game Switcher", -1, BTN_ID_NONE, 0},
{NULL}
},
.controller_map_abxy_to_rstick = 0,
Expand Down Expand Up @@ -1757,6 +1759,14 @@ static void input_poll_callback(void) {
ignore_menu = 1;
}

if (PAD_isPressed(BTN_MENU) && PAD_isPressed(BTN_SELECT)) {
ignore_menu = 1;
Menu_saveState();
putFile(GAME_SWITCHER_PERSIST_PATH, game.path + strlen(SDCARD_PATH));
GFX_clear(screen);
quit = 1;
}

if (PAD_justPressed(BTN_POWER)) {
if (thread_video) {

Expand Down Expand Up @@ -1812,7 +1822,12 @@ static void input_poll_callback(void) {
Menu_saveState();
quit = 1;
break;
case SHORTCUT_CYCLE_SCALE:
case SHORTCUT_GAMESWITCHER:
Menu_saveState();
putFile(GAME_SWITCHER_PERSIST_PATH, game.path + strlen(SDCARD_PATH));
quit = 1;
break;
case SHORTCUT_CYCLE_SCALE:
screen_scaling += 1;
if (screen_scaling>=SCALE_COUNT) screen_scaling -= SCALE_COUNT;
Config_syncFrontend(config.frontend.options[FE_OPT_SCALING].key, screen_scaling);
Expand Down
239 changes: 224 additions & 15 deletions workspace/all/minui/minui.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ static void* Array_pop(Array* self) {
if (self->count==0) return NULL;
return self->items[--self->count];
}
static void Array_remove(Array* self, void* item) {
if (self->count==0 || item == NULL)
return;
int i = 0;
while (self->items[i] != item) i++;
for (int j = i; j < self->count-1; j++)
self->items[j] = self->items[j+1];
self->count--;
}
static void Array_reverse(Array* self) {
int end = self->count-1;
int mid = self->count/2;
Expand Down Expand Up @@ -534,10 +543,14 @@ static Array* hiddens; // HiddenArray
static int quit = 0;
static int can_resume = 0;
static int should_resume = 0; // set to 1 on BTN_RESUME but only if can_resume==1
static int has_preview = 0;
static int last_selected_slot = 0;
static int simple_mode = 0;
static int show_switcher = 0;
static int switcher_selected = 0;
static char slot_path[256];
static char slot_path_rom[256];
static char preview_path[256];

static int restore_depth = -1;
static int restore_relative = -1;
Expand Down Expand Up @@ -1026,21 +1039,29 @@ static Array* getRoot(void) {
if (hasHiddens() && exists(SHOW_HIDDEN_FOLDER_PATH)) Array_push(root, Entry_new(FAUX_HIDDEN_PATH, ENTRY_DIR));
return root;
}

static Entry* entryFromRecent(Recent* recent)
{
if(!recent || !recent->available)
return NULL;

char sd_path[256];
sprintf(sd_path, "%s%s", SDCARD_PATH, recent->path);
int type = suffixMatch(".pak", sd_path) ? ENTRY_PAK : ENTRY_ROM; // ???
Entry* entry = Entry_new(sd_path, type);
if (recent->alias) {
free(entry->name);
entry->name = strdup(recent->alias);
}
return entry;
}
static Array* getRecents(void) {
Array* entries = Array_new();
for (int i=0; i<recents->count; i++) {
Recent* recent = recents->items[i];
if (!recent->available) continue;

char sd_path[256];
sprintf(sd_path, "%s%s", SDCARD_PATH, recent->path);
int type = suffixMatch(".pak", sd_path) ? ENTRY_PAK : ENTRY_ROM; // ???
Entry* entry = Entry_new(sd_path, type);
if (recent->alias) {
free(entry->name);
entry->name = strdup(recent->alias);
}
Array_push(entries, entry);
Entry* entry = entryFromRecent(recent);
if (entry)
Array_push(entries, entry);
}
return entries;
}
Expand Down Expand Up @@ -1299,6 +1320,7 @@ static char* escapeSingleQuotes(char* str) {
static void readyResumePath(char* rom_path, int type) {
char* tmp;
can_resume = 0;
has_preview = 0;
char path[256];
strcpy(path, rom_path);

Expand Down Expand Up @@ -1332,17 +1354,33 @@ static void readyResumePath(char* rom_path, int type) {
sprintf(slot_path, "%s/.minui/%s/%s.txt", SHARED_USERDATA_PATH, emu_name, rom_file); // /.userdata/shared/.minui/<EMU>/<romname>.txt
//sprintf(slot_path_rom, "%s/%s/%s", MYSAVESTATE_PATH, emu_name, rom_file); // /.userdata/shared/.minui/<EMU>/<romname>.ext
//sprintf(slot_path, "%s.txt", slot_path_rom); // /.userdata/.minui/<EMU>/<romname>.ext.txt

// Build the state path for preview
char state_path[256];
getStatePath(path, state_path);

int last_know_slot = 0;
//can_resume = exists(slot_path);
if (exists(slot_path)) {
char slot[16];
getFile(slot_path, slot, 16);
if (slot[0]!='\0') {
if (slot[0] != '\0') {
last_know_slot = atoi(slot);
}
}
}

last_selected_slot = canResume(path, last_know_slot);
if (last_selected_slot) can_resume = 1;
if (last_selected_slot) {
can_resume = 1;

// Build preview_path based on the slot
if (last_selected_slot > 0) {
sprintf(preview_path, "%s/%s.state%d.png", state_path, rom_file, last_selected_slot);
} else {
sprintf(preview_path, "%s/%s.state.png", state_path, rom_file);
}

has_preview = exists(preview_path);
}
}

static void readyResume(Entry* entry) {
Expand Down Expand Up @@ -1704,6 +1742,33 @@ static void Menu_quit(void) {

///////////////////////////////////////

static SDL_Rect GFX_scaled_rect(SDL_Rect preview_rect, SDL_Rect image_rect) {
SDL_Rect scaled_rect;

// Calculate the aspect ratios
float image_aspect = (float)image_rect.w / (float)image_rect.h;
float preview_aspect = (float)preview_rect.w / (float)preview_rect.h;

// Determine scaling factor
if (image_aspect > preview_aspect) {
// Image is wider than the preview area
scaled_rect.w = preview_rect.w;
scaled_rect.h = (int)(preview_rect.w / image_aspect);
} else {
// Image is taller than or equal to the preview area
scaled_rect.h = preview_rect.h;
scaled_rect.w = (int)(preview_rect.h * image_aspect);
}

// Center the scaled rectangle within preview_rect
scaled_rect.x = preview_rect.x + (preview_rect.w - scaled_rect.w) / 2;
scaled_rect.y = preview_rect.y + (preview_rect.h - scaled_rect.h) / 2;

return scaled_rect;
}

///////////////////////////////////////

int main (int argc, char *argv[]) {
selected_modifier = 0;
if (autoResume()) return 0; // nothing to do
Expand Down Expand Up @@ -1756,9 +1821,17 @@ int main (int argc, char *argv[]) {
if (!HAS_POWER_BUTTON && !simple_mode) PWR_disableSleep();

SDL_Surface* version = NULL;
SDL_Surface* preview = NULL;

Menu_init();

show_switcher = exists(GAME_SWITCHER_PERSIST_PATH);
if (show_switcher) {
// consider this "consumed", dont bring up the switcher next time we regularly exit a game
unlink(GAME_SWITCHER_PERSIST_PATH);
// todo: map recent slot to last used game
}

// now that (most of) the heavy lifting is done, take a load off
PWR_setCPUSpeed(CPU_SPEED_MENU);
GFX_setVsync(VSYNC_STRICT);
Expand Down Expand Up @@ -1818,12 +1891,61 @@ int main (int argc, char *argv[]) {
dirty = 1;
}
}
else if(show_switcher) {
if (PAD_justPressed(BTN_B) || PAD_justReleased(BTN_SELECT)) {
show_switcher = 0;
switcher_selected = 0;
dirty = 1;
}
else if (recents->count > 0 && PAD_justReleased(BTN_A)) {
// TODO: This is crappy af - putting this here since it works, but
// super inefficient. Why are Recents not decorated with type, and need
// to be remade into Entries via getRecents()? - need to understand the
// architecture more...
Entry *selectedEntry = entryFromRecent(recents->items[switcher_selected]);
should_resume = can_resume;
Entry_open(selectedEntry);
dirty = 1;
Entry_free(selectedEntry);
}
else if (recents->count > 0 && PAD_justReleased(BTN_Y)) {
// remove
Recent* recentEntry = recents->items[switcher_selected--];
Array_remove(recents, recentEntry);
Recent_free(recentEntry);
saveRecents();
if(switcher_selected < 0)
switcher_selected = recents->count - 1; // wrap
dirty = 1;
}
else if (PAD_justPressed(BTN_RIGHT)) {
switcher_selected++;
if(switcher_selected == recents->count)
switcher_selected = 0; // wrap
dirty = 1;
}
else if (PAD_justPressed(BTN_LEFT)) {
switcher_selected--;
if(switcher_selected < 0)
switcher_selected = recents->count - 1; // wrap


dirty = 1;
}
}
else {
if (PAD_tappedMenu(now)) {
show_version = 1;
show_switcher = 0;
dirty = 1;
if (!HAS_POWER_BUTTON && !simple_mode) PWR_enableSleep();
}
else if (PAD_justReleased(BTN_SELECT)) {
show_switcher = 1;
switcher_selected = 0;
show_version = 0; // just to be sure
dirty = 1;
}
else if (total>0) {
if (PAD_justRepeated(BTN_UP)) {
itemnotchanged = 0;
Expand Down Expand Up @@ -2143,6 +2265,92 @@ int main (int argc, char *argv[]) {

GFX_blitButtonGroup((char*[]){ "UP/DOWN", "MODE", "B","BACK", NULL }, 0, screen, 1, fancy_mode);
}
else if(show_switcher) {
// For all recents with resumable state (i.e. has savegame), show game switcher carousel

#define WINDOW_RADIUS 0 // TODO: this logic belongs in blitRect?
#define PAGINATION_HEIGHT 0
// unscaled
int hw = screen->w;
int hh = screen->h;
int pw = hw + SCALE1(WINDOW_RADIUS*2);
int ph = hh + SCALE1(WINDOW_RADIUS*2 + PAGINATION_HEIGHT + WINDOW_RADIUS);
ox = 0; // screen->w - pw - SCALE1(PADDING);
oy = 0; // (screen->h - ph) / 2;

// window
GFX_blitRect(ASSET_STATE_BG, screen, &(SDL_Rect){ox,oy,pw,ph});

if(recents->count > 0) {
Entry *selectedEntry = entryFromRecent(recents->items[switcher_selected]);
readyResume(selectedEntry);

if(has_preview) {
// lotta memory churn here
SDL_Surface* bmp = IMG_Load(preview_path);
SDL_Surface* raw_preview = SDL_ConvertSurface(bmp, screen->format, SDL_SWSURFACE);
SDL_Rect image_rect = {0, 0, raw_preview->w, raw_preview->h};
SDL_Rect preview_rect = {ox, oy, hw, hh};
SDL_Rect scaled_rect = GFX_scaled_rect(preview_rect, image_rect);
SDL_FillRect(screen, NULL, 0);
SDL_BlitScaled(raw_preview, NULL, screen, &scaled_rect);
SDL_FreeSurface(raw_preview);
SDL_FreeSurface(bmp);
}
else {
SDL_Rect preview_rect = {ox,oy,hw,hh};
SDL_FillRect(screen, &preview_rect, 0);
GFX_blitMessage(font.large, "No Preview", screen, &preview_rect);
}

// title pill
{
int ow = GFX_blitHardwareGroup(screen, show_setting, fancy_mode);
int max_width = screen->w - SCALE1(PADDING * 2) - ow;

char display_name[256];
int text_width = GFX_truncateText(font.large, selectedEntry->name, display_name, max_width, SCALE1(BUTTON_PADDING*2));
max_width = MIN(max_width, text_width);

SDL_Surface* text;
text = TTF_RenderUTF8_Blended(font.large, display_name, COLOR_WHITE);
GFX_blitPill(ASSET_BLACK_PILL, screen, &(SDL_Rect){
SCALE1(PADDING),
SCALE1(PADDING),
max_width,
SCALE1(PILL_SIZE)
});
SDL_BlitSurface(text, &(SDL_Rect){
0,
0,
max_width-SCALE1(BUTTON_PADDING*2),
text->h
}, screen, &(SDL_Rect){
SCALE1(PADDING+BUTTON_PADDING),
SCALE1(PADDING+4)
});
SDL_FreeSurface(text);
}

// pagination
{

}

if(can_resume) GFX_blitButtonGroup((char*[]){ "X","RESUME", NULL }, 0, screen, 0, fancy_mode);
else GFX_blitButtonGroup((char*[]){ BTN_SLEEP==BTN_POWER?"POWER":"MENU","SLEEP", NULL }, 0, screen, 0, fancy_mode);

GFX_blitButtonGroup((char*[]){ "B","BACK", "A", "OPEN", NULL }, 1, screen, 1, fancy_mode);

Entry_free(selectedEntry);
}
else {
SDL_Rect preview_rect = {ox,oy,hw,hh};
SDL_FillRect(screen, &preview_rect, 0);
GFX_blitMessage(font.large, "No Recents", screen, &preview_rect);
GFX_blitButtonGroup((char*[]){ "B","BACK", NULL }, 1, screen, 1, fancy_mode);
}
}
else {
// list
if (total>0) {
Expand Down Expand Up @@ -2405,6 +2613,7 @@ int main (int argc, char *argv[]) {
}

if (version) SDL_FreeSurface(version);
if (preview) SDL_FreeSurface(preview);

Menu_quit();
PWR_quit();
Expand Down