|
|
|
@@ -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 |
|
|
|
|