Procházet zdrojové kódy

Add menu support + ROM selection

master
Nathaniel Walizer před 11 měsíci
rodič
revize
d713cdb56c
10 změnil soubory, kde provedl 427 přidání a 36 odebrání
  1. +1
    -0
      Makefile
  2. +28
    -0
      src/cart.c
  3. +2
    -0
      src/cart.h
  4. +27
    -0
      src/menu.h
  5. +41
    -27
      src/nese.c
  6. +25
    -0
      src/render.h
  7. +226
    -0
      src/sdl_menu.c
  8. +37
    -9
      src/sdl_overlay.c
  9. +8
    -0
      src/sdl_overlay.h
  10. +32
    -0
      src/sdl_render.c

+ 1
- 0
Makefile Zobrazit soubor

@@ -47,6 +47,7 @@ SRC_SRCS_1 += apu.c audio.c
SRC_SRCS_1 += file.c save.c
SRC_SRCS_1 += sdl_render.c sdl_input.c sdl_audio.c sdl_timer.c
SRC_SRCS_1 += overlay.c sdl_overlay.c
SRC_SRCS_1 += sdl_menu.c

PLAT_SRCS_1 = filemap.c



+ 28
- 0
src/cart.c Zobrazit soubor

@@ -2,6 +2,7 @@
#include "filemap.h"
#include "ines.h"
#include "mapper.h"
#include "save.h"


int nes_cart_init_mem(nes_cart* cart, void* mem, int len) {
@@ -107,3 +108,30 @@ int nes_cart_init_file(nes_cart* cart, FILE* file) {

return status;
}


FILE* nes_load_cart(nes_cart* cart, const char* cart_filename) {
int status = 0;

FILE* cart_file = fopen(cart_filename, "rb");
if (NULL == cart_file) {
status = -1;
fprintf(stderr, "Could not open %s\n", cart_filename);
}

if (status == 0) {
status = nes_cart_init_file(cart, cart_file);
}

if (status == 0) {
// Failure might mean there's nothing to load
load_sram(cart, cart_filename);
}

if (status != 0 && NULL != cart_file) {
fclose(cart_file);
cart_file = NULL;
}

return cart_file;
}

+ 2
- 0
src/cart.h Zobrazit soubor

@@ -28,5 +28,7 @@ int nes_cart_init_file(nes_cart*, FILE* file);
int nes_cart_init_mem(nes_cart*, void*, int len);
void nes_cart_done(nes_cart*);

FILE* nes_load_cart(nes_cart* cart, const char* cart_filename);


#endif // NES_CART_H_

+ 27
- 0
src/menu.h Zobrazit soubor

@@ -0,0 +1,27 @@
#ifndef NESE_MENU_H_
#define NESE_MENU_H_

#include "nes.h"
#include "render.h"
#include "input.h"


typedef struct {
int cursor;
int top;
} menu_state;


// Returns filename of selected ROM
char* run_main_menu(menu_state*, nes_Renderer*,
nes_Input_Reader*, nes*);

// TODO: What does this return?
int run_game_menu(menu_state*, nes_Renderer*,
nes_Input_Reader*, nes*);

int modal_popup(const char* message,
nes_Renderer*, nes_Input_Reader*, nes*);


#endif // NESE_MENU_H_

+ 41
- 27
src/nese.c Zobrazit soubor

@@ -1,6 +1,6 @@
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "nes.h"
#include "timer.h"
@@ -9,6 +9,7 @@
#include "audio.h"
#include "mapper.h"
#include "save.h"
#include "menu.h"


#define audio_freq (44100U)
@@ -87,29 +88,6 @@ int loadsave_tick(loadsave_state* loadsave) {
int main(int argc, char* argv[]) {
int status = 0;

FILE* cart_file = NULL;
const char* cart_filename = NULL;
if (argc > 1) {
cart_filename = argv[1];
cart_file = fopen(argv[1], "rb");
if (NULL == cart_file) {
status = -1;
fprintf(stderr, "Could not open %s\n", argv[1]);
}
} else {
status = -1;
fprintf(stderr, "Missing cartridge file\n");
}

if (status == 0) {
status = nes_cart_init_file(&sys.cart, cart_file);
}

if (status == 0) {
// Failure might mean there's nothing to load
load_sram(&sys.cart, cart_filename);
}

nes_Renderer* rend = &sdl_renderer;
if (status == 0) {
status = nes_render_init(rend);
@@ -125,6 +103,43 @@ int main(int argc, char* argv[]) {
status = nes_audio_init(audio, audio_freq);
}

char* cart_filename = NULL;
FILE* cart_file = NULL;
if (0 == status && argc > 1) {
cart_filename = strdup(argv[1]);
cart_file = nes_load_cart(&sys.cart, argv[1]);
}

menu_state menu = {0};
while (0 == status && NULL == cart_file) {
// Display a load failure message?
if (NULL != cart_filename) {
char message[1024];
snprintf(message, sizeof(message) - 1,
"Could not load\n%s", cart_filename);
int button = modal_popup(message, rend, input, &sys);
if (input_Result_Quit == (button >> 8)) {
// Program closed inside modal
status = -1;
}
}

if (0 == status) {
// If we didn't launch with a file, run the loader
cart_filename = run_main_menu(&menu, rend,
input, &sys);

if (NULL == cart_filename) {
// This means that we dumped out of the loader
status = -1;

} else {
cart_file = nes_load_cart(&sys.cart,
cart_filename);
}
}
}

if (status == 0) {
status = nes_init(&sys, audio_freq);
}
@@ -227,9 +242,8 @@ int main(int argc, char* argv[]) {
save_sram(&sys.cart, cart_filename);
}

if (cart_file != NULL) {
fclose(cart_file);
}
if (NULL != cart_file) fclose(cart_file);
free(cart_filename);

return status;
}

+ 25
- 0
src/render.h Zobrazit soubor

@@ -9,6 +9,12 @@ typedef struct nes_Renderer_t {
int (*init)(struct nes_Renderer_t*);
void (*done)(struct nes_Renderer_t*);
int (*render)(struct nes_Renderer_t*, nes_ppu*);

void (*draw_last_frame)(struct nes_Renderer_t*);
void (*draw_text)(struct nes_Renderer_t*, const char*, int x, int y);
void (*text_size)(struct nes_Renderer_t*, const char*, int* w, int* h);
void (*draw_done)(struct nes_Renderer_t*);

Overlay overlay;
void* data;
} nes_Renderer;
@@ -25,5 +31,24 @@ static inline int nes_render(nes_Renderer* rend, nes_ppu* ppu) {
return rend->render(rend, ppu);
}

static inline void nes_draw_last_frame(nes_Renderer* rend) {
rend->draw_last_frame(rend);
}

static inline void nes_text_size(nes_Renderer* rend,
const char* str,
int* w, int* h) {
rend->text_size(rend, str, w, h);
}

static inline void nes_draw_text(nes_Renderer* rend,
const char* str, int x, int y) {
rend->draw_text(rend, str, x, y);
}

static inline void nes_draw_done(nes_Renderer* rend) {
rend->draw_done(rend);
}


#endif // NES_RENDER_H_

+ 226
- 0
src/sdl_menu.c Zobrazit soubor

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

#include <dirent.h>

#include "menu.h"
#include "file.h"
#include "timer.h"


static int get_input(nes_Input_Reader* reader,
nes_input* input, int *last) {
int status = nes_input_update(reader, input);
int new_buttons = input->controllers[0].buttons;
if (0 == status) {
status = (~*last & new_buttons);
} else {
status = (status << 8);
}
*last = new_buttons;
return status;
}

static int wait_for_input(nes_Input_Reader* reader,
nes_input* input) {
int buttons = input->controllers[0].buttons;
int status = 0;
for ( ;
0 == status;
status = get_input(reader, input, &buttons) ) {
time_sleep(US_PER_S / 60);
}
return status;
}


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

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

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 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 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 - 30) - 1) / 11) - 1;
}

static void show_menu(const menu_state* menu,
nes_Renderer* rend, file_list* files) {
nes_draw_last_frame(rend);

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

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

nes_draw_done(rend);
}


char* run_main_menu(menu_state* state, nes_Renderer* rend,
nes_Input_Reader* input, nes* sys) {
char* cart_filename = NULL;

DIR* dir = opendir("rom");

if (NULL == dir) {
nes_draw_last_frame(rend);
nes_draw_text(rend, "No ROMS found!", 10, 10);
nes_draw_text(rend, "Press any key to exit", 10, 21);
nes_draw_done(rend);
wait_for_input(input, &sys->input);

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

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

while (1) {
// Scrolling (do this first to ensure menu is valid)
const int visible = n_visible();
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(&menu, rend, &files);

int buttons = wait_for_input(input, &sys->input);
int special = (buttons >> 8);

if ( input_Result_Quit == special ||
(buttons & (1 << Button_B))) {
// Cancel
menu.cursor = -1;
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;
}
}
}

// Selection has been made (or cancelled)
if (menu.cursor >= 0 && menu.cursor < files.count) {
char filename[1024];
snprintf(filename, sizeof(filename) - 1,
"%s/%s", "rom", files.files[menu.cursor]);
cart_filename = strdup(filename);
}

free_file_list(&files);

if (NULL != state) {
*state = menu;
if (menu.cursor < 0) state->cursor = 0;
}
}

return cart_filename;
}

int modal_popup(const char* message, nes_Renderer* rend,
nes_Input_Reader* input, nes* sys) {
int w = 0;
int h = 0;

nes_text_size(rend, message, &w, &h);

int x = (nes_ppu_render_w - w) / 2;
int y = (nes_ppu_render_h - h) / 2;

nes_draw_last_frame(rend);
nes_draw_text(rend, message, x, y);
nes_draw_done(rend);

return wait_for_input(input, &sys->input);
}

+ 37
- 9
src/sdl_overlay.c Zobrazit soubor

@@ -86,6 +86,8 @@ void sdl_overlay_font_done(sdl_overlay_font* font) {
#define overlay_start_x (char_w / 2)
#define overlay_start_y (char_h / 2)

#define space_width ((char_w / 2) - 1)

static inline int char_index(char c) {
if (c >= 'a' && c <= 'z') c += ('A' - 'a');
if (c > ' ' && c < 'a') return (c - ' ');
@@ -93,28 +95,54 @@ static inline int char_index(char c) {
return 0;
}

static void render_string(SDL_Renderer* rend, int ox, int oy,
int sx, int sy,
sdl_overlay_font* font,
const char* string) {
void measure_string(sdl_overlay_font* font, const char* string,
int* w, int* h) {
*h = char_h;
int max_w = 1;
int cur_w = 1;
for (const char* c = string; *c; ++c) {
if (*c == '\n') {
if (cur_w > max_w) max_w = cur_w;
cur_w = 1;
*h += char_h;
} else if (*c == ' ') {
cur_w += space_width;
} else {
int index = char_index(*c);
int cw = font->charbits[index * charbit_size];
cur_w += (cw - 1);
}
}
if (cur_w > max_w) max_w = cur_w;
*w = max_w;
}

void render_string(SDL_Renderer* rend,
int ox, int oy, int sx, int sy,
sdl_overlay_font* font, const char* string) {
int x = ox;
int y = oy;
for (const char* c = string; *c; ++c) {
if (*c == ' ') {
ox += ((char_w / 2) - 1);
if (*c == '\n') {
x = ox;
y += (char_h + 1);
} else if (*c == ' ') {
x += space_width;
} else {
int index = char_index(*c);
int cw = font->charbits[index * charbit_size];
SDL_Texture* texture = font->textures[index];

SDL_Rect rect = {
.x = ox * sx,
.y = oy * sy,
.x = x * sx,
.y = y * sy,
.w = cw * sx,
.h = char_h * sy,
};

SDL_RenderCopy(rend, texture, NULL, &rect);

ox += (cw - 1);
x += (cw - 1);
}
}
}


+ 8
- 0
src/sdl_overlay.h Zobrazit soubor

@@ -24,4 +24,12 @@ int sdl_overlay_frame(Overlay*, sdl_overlay_font*, SDL_Renderer*,
int sx, int sy);


void measure_string(sdl_overlay_font* font, const char* string,
int* w, int* h);

void render_string(SDL_Renderer* rend,
int ox, int oy, int sx, int sy,
sdl_overlay_font* font, const char* string);


#endif // NESE_SDL_OVERLAY_

+ 32
- 0
src/sdl_render.c Zobrazit soubor

@@ -148,6 +148,7 @@ static int sdl_render_init(nes_Renderer* rend) {
} else {
SDL_LockTextureToSurface(data->texture, NULL,
&data->target);
SDL_FillRect(data->target, NULL, 0x556677FF);
}
}

@@ -559,8 +560,39 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) {
}


static void sdl_redraw_frame(nes_Renderer* rend) {
sdl_render_data* data = (sdl_render_data*)rend->data;
SDL_UnlockTexture(data->texture);
SDL_RenderCopy(data->renderer, data->texture, NULL, NULL);
SDL_LockTextureToSurface(data->texture, NULL, &data->target);
}

static void sdl_draw_present(nes_Renderer* rend) {
sdl_render_data* data = (sdl_render_data*)rend->data;
SDL_RenderPresent(data->renderer);
}

static void sdl_draw_text(nes_Renderer* rend,
const char* str, int x, int y) {
sdl_render_data* data = (sdl_render_data*)rend->data;
render_string(data->renderer, x, y,
data->win_w / nes_ppu_render_w,
data->win_h / nes_ppu_render_h,
&data->font, str);
}

static void sdl_text_size(nes_Renderer* rend,
const char* str, int* w, int* h) {
sdl_render_data* data = (sdl_render_data*)rend->data;
measure_string(&data->font, str, w, h);
}

nes_Renderer sdl_renderer = {
.init = sdl_render_init,
.done = sdl_render_done,
.render = sdl_render,
.draw_last_frame = sdl_redraw_frame,
.draw_text = sdl_draw_text,
.text_size = sdl_text_size,
.draw_done = sdl_draw_present,
};

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