소스 검색

Add ROM selection menu

v2
부모
커밋
63a5aa317d
3개의 변경된 파일254개의 추가작업 그리고 25개의 파일을 삭제
  1. +129
    -20
      src/linux/port.c
  2. +118
    -1
      src/menu.c
  3. +7
    -4
      src/menu.h

+ 129
- 20
src/linux/port.c 파일 보기

@@ -3,6 +3,7 @@

#include <time.h>
#include <unistd.h>
#include <dirent.h>

#include <sys/mman.h>
#include <sys/stat.h>
@@ -97,6 +98,62 @@ typedef struct {
} platform_data;


/* Directories */

static int cmp_files(const void* _a, const void* _b) {
const char* a = *(const char**)_a;
const char* b = *(const char**)_b;

int diff = 0;
for (char ca = 1, cb = 1; ca && cb && 0 == diff; ++a, ++b) {
// Cut extensions; replace underscore with space
ca = (*a == '_' ? ' ' : (*a == '.' ? '\0' : *a));
cb = (*b == '_' ? ' ' : (*b == '.' ? '\0' : *b));
diff = (ca - cb);
}

return diff;
}

static int count_files(DIR* dir) {
int count = 0;
struct dirent* de = NULL;
while (NULL != (de = readdir(dir))) {
if ('.' != de->d_name[0]) ++count;
}
rewinddir(dir);
return count;
}

static void make_file_list(DIR* dir, File_List* files) {
files->count = count_files(dir);
files->files = calloc(files->count, sizeof(char*));
struct dirent* de = NULL;
int i_file = 0;
while (NULL != (de = readdir(dir))) {
if ('.' != de->d_name[0]) {
files->files[i_file] = strdup(de->d_name);
++i_file;
}
}
qsort(files->files, files->count, sizeof(char*), cmp_files);
}

static void free_file_list(File_List* files) {
for (int i = 0; i < files->count; ++i) {
free(files->files[i]);
}
free(files->files);
}

static int find_file(File_List* files, const char* filename) {
int i = (files->count - 1);
for ( ; i >= 0 &&
(0 != strcmp(files->files[i], filename)); --i);
return i;
}


/* Input */

static const int sdl_action_keycodes[Action_Max] = {
@@ -175,13 +232,6 @@ static nese_Action process_events(nes* sys) {
return action;
}

nese_Action nese_update_input(void* plat_data, nes_Input* input) {
platform_data* plat = (platform_data*)plat_data;
nese_Action action = process_events(plat->sys);
if (NULL != input) *input = plat->sys->core.memory.input;
return action;
}


/*
* Time / Video - Should be maximally reusable across platforms
@@ -385,6 +435,59 @@ int nese_get_audio_frequency(void*) {
// TODO: Audio functions


/* Menus */

nese_Action nese_update_input(void* plat_data, nes_Input* input) {
time_sleep_until(time_now() + (NS_PER_S / 60));
platform_data* plat = (platform_data*)plat_data;
nese_Action action = process_events(plat->sys);
if (NULL != input) *input = plat->sys->core.memory.input;
return action;
}

static nese_Action run_rom_menu(platform_data* plat, Menu_State* state,
char* filename, int sz_filename) {
nese_Action action = Action_Cancel;

DIR* dir = opendir("rom");

if (NULL == dir) {
modal_popup(plat, "No ROMS found!\nPress any key to exit",
color_error);

} else {
File_List files = {0};
make_file_list(dir, &files);
closedir(dir);

Menu_State menu = {0};
if (NULL != state) menu = *state;

// Add 4 to skip past "rom/"
int current = find_file(&files, filename + 4);
if (current >= 0) menu.cursor = current;

action = run_menu(plat, &menu, &files, 20);

if (Action_OK == action) {
if ( menu.cursor >= 0 &&
menu.cursor < files.count) {
snprintf(filename, sz_filename - 1,
"%s/%s", "rom", files.files[menu.cursor]);
} else {
action = Action_Cancel;
}
}

free_file_list(&files);

if (NULL != state) *state = menu;
}

return action;
}


/* Platform Data */

static int plat_init(platform_data* plat) {
@@ -414,6 +517,7 @@ static nes sys = {0};

int main(int argc, char* argv[]) {
int status = 0;
char filename[1024] = {0};

// This should be tiny enough to keep on the stack.
platform_data plat = {
@@ -425,22 +529,27 @@ int main(int argc, char* argv[]) {
}

if (0 == status) {
if (argc <= 1) {
modal_popup(&plat, "No ROM file provided.", color_error);
status = -1;
if (1 < argc) {
strncpy(filename, argv[1], sizeof(filename) - 1);
filename[sizeof(filename) - 1] = '\0';
}

} else {
status = load_cart(&plat.cart, argv[1], &sys);
if (0 != status) {
char message[128];
snprintf(message, sizeof(message), "Failed to load %s", argv[1]);
modal_popup(&plat, message, color_error);
while (NULL == sys.cart_header && 0 == status) {
if (filename[0]) {
status = load_cart(&plat.cart, filename, &sys);
if (0 != status) {
char message[1039];
snprintf(message, sizeof(message),
"Failed to load %s", filename);
modal_popup(&plat, message, color_error);
}
}
}

if (NULL == sys.cart_header) {
// TODO: ROM selection menu
status = -1;
if (NULL == sys.cart_header) {
nese_Action action = run_rom_menu(&plat, NULL,
filename, sizeof(filename));
status = (Action_OK == action) ? 0 : -1;
}
}

// This shall invoke menus as needed


+ 118
- 1
src/menu.c 파일 보기

@@ -2,6 +2,7 @@
#include "nes.h"
#include "action.h"
#include "port.h"
#include "draw.h"


static nese_Action wait_for_input(void* plat, uint8_t* new_buttons) {
@@ -22,7 +23,7 @@ static nese_Action wait_for_input(void* plat, uint8_t* new_buttons) {
}


int modal_popup(void* plat, const char* message, uint32_t color) {
nese_Action modal_popup(void* plat, const char* message, uint32_t color) {
int w = 0;
int h = 0;

@@ -45,3 +46,119 @@ int modal_popup(void* plat, const char* message, uint32_t color) {

return action;
}

static void fix_filename(char* dst, int n, const char* src) {
for ( int i = 0;
i < n && *src && '.' != *src;
++i, ++dst, ++src) {
*dst = ('_' == *src ? ' ' : *src);
}
*dst = '\0';
}

static inline int n_visible(void) {
return ((nes_ppu_render_h - 20) / 11);
}

static const uint32_t menu_colors[6] = {
color_red, color_orange, color_yellow,
color_green, color_blue, color_purple,
};

static void show_menu(void* plat, const Menu_State* menu,
const File_List* files, int x) {
nese_draw_begin(plat);

int bottom = menu->top + n_visible() - 1;

int max = n_visible();
if (max > files->count) max = files->count;
int y = (nes_ppu_render_h - (max * 11)) / 2;

for ( int n = menu->top;
n < files->count && n <= bottom;
++n, y += 11 ) {
char filename[100];
fix_filename(filename, sizeof(filename) - 1,
files->files[n]);
nese_draw_text(
plat,
( (menu->top == n && 0 != menu->top) ||
(bottom == n && files->count - 1 > bottom) ) ?
"..." : filename,
x, y,
(menu->cursor == n) ?
color_white : menu_colors[n % 6]
);
if (menu->cursor == n) {
nese_draw_text(plat, ">", x - 10, y, color_white);
}
}

nese_draw_finish(plat);
}

int run_menu(void* plat, Menu_State* state, const File_List* files, int x) {
Menu_State menu = {0};
if (NULL != state) {
menu = *state;
if (menu.cursor < 0) {
menu.cursor = 0;
} else if (menu.cursor >= files->count) {
menu.cursor = files->count - 1;
}
}

nese_Action action = Action_OK;
while (Action_OK == action) {
// Scrolling (do this first to ensure menu is valid)
const int visible = n_visible() - 1;
menu.top = menu.cursor - (visible / 2);
if (menu.top <= 0) {
// We use <= so we don't readjust the top from 0
menu.top = 0;
} else if (menu.top + visible >= files->count) {
menu.top = (files->count - 1) - visible;
}

show_menu(plat, &menu, files, x);

uint8_t buttons = 0;
action = wait_for_input(plat, &buttons);

if (Action_Menu == action) {
action = Action_Cancel;

} else if (Action_OK == action) {
if (buttons & (1 << Button_B)) {
action = Action_Cancel;
break;

} else if (buttons & ( (1 << Button_A) |
(1 << Button_Start) )) {
// Select
break;

} else if (buttons & (1 << Button_Up)) {
if (menu.cursor > 0) --menu.cursor;

} else if (buttons & ( (1 << Button_Down) |
(1 << Button_Select) )) {
if (menu.cursor < (files->count - 1)) {
++menu.cursor;
} else if (buttons & (1 << Button_Select)) {
// Wrap around on Select
menu.cursor = 0;
}
}

} else if (Action_Quit != action && Action_Cancel != action) {
// Ignore anything that isn't "Menu", "Cancel" or "Quit"
action = Action_OK;
}
}

if (NULL != state) *state = menu;

return action;
}

+ 7
- 4
src/menu.h 파일 보기

@@ -2,6 +2,7 @@
#define NESE_MENU_H_

#include "cartinfo.h"
#include "action.h"


typedef struct {
@@ -9,12 +10,14 @@ typedef struct {
int top;
} Menu_State;

typedef struct {
int count;
char** files;
} File_List;

int run_rom_menu(void*, Menu_State*, const Cart_Info*, char* file, int sz_file);

int run_game_menu(void*, Menu_State*);
nese_Action run_menu(void*, Menu_State* state, const File_List* files, int x);

int modal_popup(void*, const char* message, uint32_t color);
nese_Action modal_popup(void*, const char* message, uint32_t color);


#endif // NESE_MENU_H_

불러오는 중...
취소
저장