Procházet zdrojové kódy

Move common menu and popup elements out of platform source

v2
Nathaniel Walizer před 6 měsíci
rodič
revize
bd97746282
8 změnil soubory, kde provedl 286 přidání a 228 odebrání
  1. +2
    -2
      Makefile
  2. +30
    -226
      src/linux/port.c
  3. +59
    -0
      src/list.c
  4. +14
    -0
      src/list.h
  5. +90
    -0
      src/menus.c
  6. +13
    -0
      src/menus.h
  7. +57
    -0
      src/state.c
  8. +21
    -0
      src/state.h

+ 2
- 2
Makefile Zobrazit soubor

@@ -32,8 +32,8 @@ GFXDIR = $(SRCDIR)/$(GFX)
NESE_SRC_SRCS = f6502.c f6502_opcodes.c
NESE_SRC_SRCS += nese.c nes.c cart.c mapper.c
NESE_SRC_SRCS += ppu.c apu.c
NESE_SRC_SRCS += memory.c serdes.c save.c rle.c
NESE_SRC_SRCS += cartinfo.c menu.c
NESE_SRC_SRCS += memory.c serdes.c save.c rle.c cartinfo.c
NESE_SRC_SRCS += menu.c list.c state.c menus.c
NESE_SRC_SRCS += $(OS)/port.c
NESE_GFX_SRCS = $(notdir $(wildcard $(GFXDIR)/*.c))
NESE_MAP_SRCS = $(notdir $(wildcard $(MAPDIR)/*.c))


+ 30
- 226
src/linux/port.c Zobrazit soubor

@@ -16,7 +16,9 @@
#include "save.h"
#include "draw.h"
#include "cartinfo.h"
#include "menu.h"
#include "list.h"
#include "state.h"
#include "menus.h"

#define NESE_DEBUG "Port"
#include "log.h"
@@ -79,71 +81,30 @@ void* nese_alloc(int size) {
}


/*
* Platform-specific features and controls
*/

#define ACTION_DELAY (60U * 1U)
#define ACTION_NOTIFY (60U * 3U)
/* Time */

typedef struct {
int timer;
int msg_id;
nese_Action action;
} Action_State;

static void action_immediate(Action_State* state, nese_Action action) {
state->action = action;
state->timer = 0;
}

static void action_start(Action_State* state, nese_Action action,
Overlay* overlay) {
if (state->action != Action_OK) {
// We can't do two things at once - cancel the current action
overlay_clear_message(overlay, state->msg_id);
state->action = Action_OK;
#define NS_PER_S (1000L * 1000L * 1000L)
#define FRAME_TIME_NS (16639267L)

} else {
state->action = action;

const char* op = NULL;
if (Action_Load == action) op = "Restoring ...";
else if (Action_Save == action) op = "Saving ...";
else if (Action_Reset == action) op = "Resetting ...";
if (NULL != op) {
state->timer = ACTION_DELAY;
state->msg_id = overlay_add_message(overlay, op, state->timer);
} else {
state->timer = 0;
}
}
uint64_t time_now(void) {
struct timespec ts_now = {0};
clock_gettime(CLOCK_REALTIME, &ts_now);
return (ts_now.tv_sec * NS_PER_S) + ts_now.tv_nsec;
}

static nese_Action action_tick(Action_State* state, Overlay* overlay) {
nese_Action action = Action_OK;

if (state->action != Action_OK && --state->timer <= 0) {
action = state->action;
state->action = Action_OK;
overlay_clear_message(overlay, state->msg_id);
}

return action;
int64_t time_sleep_until(int64_t t_target) {
int64_t t_now = time_now();
int64_t t_diff = (t_target - t_now) / 1000;
if (t_diff > 0) {
usleep(t_diff);
}
return t_diff * 1000;
}

static int action_notify(nese_Action action, int status, Overlay* overlay) {
int msg_id = 0;
const char* op = NULL;
if (Action_Load == action) op = (status < 0 ? "Restore failed" : "Restored");
else if (Action_Save == action) op = (status < 0 ? "Save failed" : "Saved");
else if (Action_Reset == action) op = "Reset";
if (NULL != op) {
msg_id = overlay_add_message(overlay, op, ACTION_NOTIFY);
}
return msg_id;
}

/*
* Platform-specific features and controls
*/

#define PLAT_FILENAME_SIZE (1024U)

@@ -164,62 +125,6 @@ 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] = {
@@ -307,6 +212,14 @@ static nese_Action process_events(nes* sys) {
return action;
}

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;
}


/*
* Time / Video - Should be maximally reusable across platforms
@@ -334,24 +247,6 @@ int nese_line_ready(void* plat_data, uint8_t* buffer, int line) {
}


#define NS_PER_S (1000L * 1000L * 1000L)
#define FRAME_TIME_NS (16639267L)

uint64_t time_now(void) {
struct timespec ts_now = {0};
clock_gettime(CLOCK_REALTIME, &ts_now);
return (ts_now.tv_sec * NS_PER_S) + ts_now.tv_nsec;
}

int64_t time_sleep_until(int64_t t_target) {
int64_t t_now = time_now();
int64_t t_diff = (t_target - t_now) / 1000;
if (t_diff > 0) {
usleep(t_diff);
}
return t_diff * 1000;
}

static nese_Action game_menu(platform_data* plat);

int nese_frame_ready(void* plat_data) {
@@ -480,7 +375,8 @@ int nese_frame_ready(void* plat_data) {

char message[32];
snprintf(message, sizeof(message), "%.1f fps", fps);
overlay_update_message(&plat->overlay, plat->fps_msg_id, message);
overlay_update_message(&plat->overlay, plat->fps_msg_id,
message);
}

slept_total = 0;
@@ -526,58 +422,6 @@ int nese_get_audio_frequency(void*) {

/* 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/"
if (strlen(filename) > 4) {
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;
}

static nese_Action nese_load_cart(platform_data* plat, const char* filename) {
nese_Action action = Action_OK;
unload_cart(&plat->cart);
@@ -621,46 +465,6 @@ static nese_Action select_rom(platform_data* plat, char* file, int sz_file) {
return action;
}

static nese_Action run_game_menu(platform_data* plat, Menu_State* state) {
static char* items[] = {
"Resume",
"Save",
"Restore",
"Reset",
"Select ROM",
"Exit",
};
static const nese_Action choices[] = {
Action_Cancel,
Action_Save,
Action_Load,
Action_Reset,
Action_Menu,
Action_Quit,
};
static const File_List options = {
.files = items,
.count = (sizeof(items) / sizeof(*items)),
};

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

nese_Action action = run_menu(plat, &menu, &options, 100);
if (Action_Menu == action) {
action = Action_Cancel;
} else if (Action_OK == action) {
if ( menu.cursor >= 0 &&
menu.cursor < (sizeof(choices) / sizeof(*choices))) {
action = choices[menu.cursor];
}
}

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

return action;
}

static nese_Action game_menu(platform_data* plat) {
nese_Action action = Action_OK;
Menu_State menu = {0};


+ 59
- 0
src/list.c Zobrazit soubor

@@ -0,0 +1,59 @@
#include <stdlib.h>
#include <string.h>

#include "list.h"


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;
}


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);
}

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

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;
}

+ 14
- 0
src/list.h Zobrazit soubor

@@ -0,0 +1,14 @@
#ifndef NESE_LIST_H_
#define NESE_LIST_H_

#include <dirent.h>

#include "menu.h"


void make_file_list(DIR* dir, File_List* files);
void free_file_list(File_List* files);
int find_file(File_List* files, const char* filename);


#endif // NESE_LIST_H_

+ 90
- 0
src/menus.c Zobrazit soubor

@@ -0,0 +1,90 @@
#include <string.h>

#include "draw.h"
#include "list.h"
#include "menus.h"


nese_Action run_rom_menu(void* 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/"
if (strlen(filename) > 4) {
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;
}

nese_Action run_game_menu(void* plat, Menu_State* state) {
static char* items[] = {
"Resume",
"Save",
"Restore",
"Reset",
"Select ROM",
"Exit",
};
static const nese_Action choices[] = {
Action_Cancel,
Action_Save,
Action_Load,
Action_Reset,
Action_Menu,
Action_Quit,
};
static const File_List options = {
.files = items,
.count = (sizeof(items) / sizeof(*items)),
};

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

nese_Action action = run_menu(plat, &menu, &options, 100);
if (Action_Menu == action) {
action = Action_Cancel;
} else if (Action_OK == action) {
if ( menu.cursor >= 0 &&
menu.cursor < (sizeof(choices) / sizeof(*choices))) {
action = choices[menu.cursor];
}
}

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

return action;
}

+ 13
- 0
src/menus.h Zobrazit soubor

@@ -0,0 +1,13 @@
#ifndef NESE_MENUS_H_
#define NESE_MENUS_H_

#include "menu.h"


nese_Action run_rom_menu(void* plat, Menu_State* state,
char* filename, int sz_filename);

nese_Action run_game_menu(void* plat, Menu_State* state);


#endif // NESE_MENUS_H_

+ 57
- 0
src/state.c Zobrazit soubor

@@ -0,0 +1,57 @@
#include "state.h"


#define ACTION_DELAY (60U * 1U)
#define ACTION_NOTIFY (60U * 3U)


void action_immediate(Action_State* state, nese_Action action) {
state->action = action;
state->timer = 0;
}

void action_start(Action_State* state, nese_Action action, Overlay* overlay) {
if (state->action != Action_OK) {
// We can't do two things at once - cancel the current action
overlay_clear_message(overlay, state->msg_id);
state->action = Action_OK;

} else {
state->action = action;

const char* op = NULL;
if (Action_Load == action) op = "Restoring ...";
else if (Action_Save == action) op = "Saving ...";
else if (Action_Reset == action) op = "Resetting ...";
if (NULL != op) {
state->timer = ACTION_DELAY;
state->msg_id = overlay_add_message(overlay, op, state->timer);
} else {
state->timer = 0;
}
}
}

nese_Action action_tick(Action_State* state, Overlay* overlay) {
nese_Action action = Action_OK;

if (state->action != Action_OK && --state->timer <= 0) {
action = state->action;
state->action = Action_OK;
overlay_clear_message(overlay, state->msg_id);
}

return action;
}

int action_notify(nese_Action action, int status, Overlay* overlay) {
int msg_id = 0;
const char* op = NULL;
if (Action_Load == action) op = (status < 0 ? "Restore failed" : "Restored");
else if (Action_Save == action) op = (status < 0 ? "Save failed" : "Saved");
else if (Action_Reset == action) op = "Reset";
if (NULL != op) {
msg_id = overlay_add_message(overlay, op, ACTION_NOTIFY);
}
return msg_id;
}

+ 21
- 0
src/state.h Zobrazit soubor

@@ -0,0 +1,21 @@
#ifndef NESE_STATE_H_
#define NESE_STATE_H_

#include "action.h"
#include "overlay.h"


typedef struct {
int timer;
int msg_id;
nese_Action action;
} Action_State;


void action_immediate(Action_State* state, nese_Action action);
void action_start(Action_State* state, nese_Action action, Overlay* overlay);
nese_Action action_tick(Action_State* state, Overlay* overlay);
int action_notify(nese_Action action, int status, Overlay* overlay);


#endif // NESE_STATE_H_

Načítá se…
Zrušit
Uložit