2024-10-30 01:53:17 +00:00
|
|
|
#include "audio.h"
|
|
|
|
#include "controller.h"
|
|
|
|
#include "emulation.h"
|
2024-10-30 03:25:26 +00:00
|
|
|
#include "external/tinyfiledialogs.h"
|
2024-10-30 01:53:17 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "ui.h"
|
|
|
|
#include "window.h"
|
|
|
|
|
|
|
|
struct UIContext {
|
|
|
|
EmulationContext emu;
|
|
|
|
WindowContext win;
|
|
|
|
AudioContext aud;
|
|
|
|
ControllerState ctrl;
|
|
|
|
};
|
|
|
|
|
|
|
|
UIContext *uiInit() {
|
|
|
|
UIContext *ui = malloc(sizeof(UIContext));
|
|
|
|
if (emuInit(&ui->emu)) {
|
|
|
|
goto destroy_ui;
|
|
|
|
}
|
|
|
|
if (windowInit(&ui->win, "Shrooms VB")) {
|
|
|
|
goto destroy_emu;
|
|
|
|
}
|
|
|
|
if (audioInit(&ui->aud)) {
|
|
|
|
goto destroy_window;
|
|
|
|
}
|
|
|
|
ctrlInit(&ui->ctrl);
|
|
|
|
return ui;
|
|
|
|
|
|
|
|
destroy_window:
|
|
|
|
windowDestroy(&ui->win);
|
|
|
|
destroy_emu:
|
|
|
|
emuDestroy(&ui->emu);
|
|
|
|
destroy_ui:
|
|
|
|
free(ui);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiDestroy(UIContext *ui) {
|
|
|
|
audioDestroy(&ui->aud);
|
|
|
|
windowDestroy(&ui->win);
|
|
|
|
emuDestroy(&ui->emu);
|
|
|
|
free(ui);
|
|
|
|
}
|
|
|
|
|
|
|
|
int uiLoadGame(UIContext *ui, const char *path) {
|
|
|
|
FILE *file = fopen(path, "rb");
|
|
|
|
uint8_t *rom;
|
|
|
|
long fileSize;
|
|
|
|
uint32_t romSize;
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
perror("could not open file");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fseek(file, 0, SEEK_END)) {
|
|
|
|
perror("could not seek file end");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fileSize = ftell(file);
|
|
|
|
if (fileSize == -1) {
|
|
|
|
perror("could not read file size");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (fseek(file, 0, SEEK_SET)) {
|
|
|
|
perror("could not seek file start");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
romSize = (uint32_t) fileSize;
|
|
|
|
|
|
|
|
rom = malloc(romSize);
|
|
|
|
if (!rom) {
|
|
|
|
perror("could not allocate ROM");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fread(rom, 1, romSize, file);
|
|
|
|
if (ferror(file)) {
|
|
|
|
perror("could not read file");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (fclose(file)) {
|
|
|
|
perror("could not close file");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
emuLoadGame(&ui->emu, rom, romSize);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *MENU_ITEMS[3] = {
|
|
|
|
"File",
|
|
|
|
"Emulation",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2024-10-30 03:25:26 +00:00
|
|
|
static const char *ROM_EXTENSIONS[2] = {
|
|
|
|
"*.vb",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2024-10-30 02:33:48 +00:00
|
|
|
typedef enum status_t {
|
|
|
|
status_paused,
|
|
|
|
status_running
|
|
|
|
} status_t;
|
|
|
|
|
2024-10-30 01:53:17 +00:00
|
|
|
int uiRun(UIContext *ui) {
|
|
|
|
static uint8_t leftEye[384*224];
|
|
|
|
static uint8_t rightEye[384*224];
|
2024-10-30 02:33:48 +00:00
|
|
|
status_t status = status_running;
|
2024-10-30 01:53:17 +00:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
struct nk_context *ctx;
|
|
|
|
SDL_Event event;
|
|
|
|
void *samples;
|
|
|
|
uint32_t bytes;
|
|
|
|
ctx = ui->win.nk;
|
|
|
|
|
2024-10-30 02:33:48 +00:00
|
|
|
if (status == status_running) {
|
|
|
|
emuTick(&ui->emu);
|
|
|
|
}
|
2024-10-30 01:53:17 +00:00
|
|
|
|
|
|
|
if (emuReadPixels(&ui->emu, leftEye, rightEye)) {
|
|
|
|
windowUpdate(&ui->win, leftEye, rightEye);
|
|
|
|
}
|
|
|
|
emuReadSamples(&ui->emu, &samples, &bytes);
|
|
|
|
if (bytes) {
|
|
|
|
audioUpdate(&ui->aud, samples, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
windowDisplayBegin(&ui->win);
|
|
|
|
/* GUI */
|
|
|
|
if (windowGuiBegin(&ui->win, "Shrooms VB")) {
|
|
|
|
windowMenubarBegin(&ui->win, MENU_ITEMS);
|
|
|
|
|
|
|
|
if (nk_menu_begin_label(ctx, "File", NK_TEXT_ALIGN_CENTERED, nk_vec2(120, windowGetScreenHeight(&ui->win)))) {
|
|
|
|
nk_layout_row_dynamic(ctx, MENU_HEIGHT + 2, 1);
|
|
|
|
if (nk_menu_item_label(ctx, "Open ROM", NK_TEXT_ALIGN_LEFT)) {
|
2024-10-30 03:25:26 +00:00
|
|
|
char *file = tinyfd_openFileDialog("Pick a ROM", NULL, 1, ROM_EXTENSIONS, "Virtual Boy ROM files", false);
|
|
|
|
if (file) {
|
|
|
|
uiLoadGame(ui, file);
|
|
|
|
status = status_running;
|
|
|
|
}
|
2024-10-30 01:53:17 +00:00
|
|
|
}
|
|
|
|
if (nk_menu_item_label(ctx, "Quit", NK_TEXT_ALIGN_LEFT)) {
|
|
|
|
SDL_Event QuitEvent;
|
|
|
|
QuitEvent.type = SDL_QUIT;
|
|
|
|
QuitEvent.quit.timestamp = SDL_GetTicks();
|
|
|
|
SDL_PushEvent(&QuitEvent);
|
|
|
|
}
|
|
|
|
nk_menu_end(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nk_menu_begin_label(ctx, "Emulation", NK_TEXT_ALIGN_CENTERED, nk_vec2(120, windowGetScreenHeight(&ui->win)))) {
|
2024-10-30 02:33:48 +00:00
|
|
|
const char *label = status == status_paused ? "Resume" : "Pause";
|
2024-10-30 01:53:17 +00:00
|
|
|
nk_layout_row_dynamic(ctx, MENU_HEIGHT + 2, 1);
|
2024-10-30 02:33:48 +00:00
|
|
|
if (nk_menu_item_label(ctx, label, NK_TEXT_ALIGN_LEFT)) {
|
|
|
|
if (status == status_paused)
|
|
|
|
status = status_running;
|
|
|
|
else
|
|
|
|
status = status_paused;
|
2024-10-30 01:53:17 +00:00
|
|
|
}
|
|
|
|
if (nk_menu_item_label(ctx, "Reset", NK_TEXT_ALIGN_LEFT)) {
|
2024-10-30 02:33:48 +00:00
|
|
|
emuReset(&ui->emu);
|
|
|
|
status = status_running;
|
2024-10-30 01:53:17 +00:00
|
|
|
}
|
|
|
|
nk_menu_end(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
windowMenubarEnd(&ui->win);
|
|
|
|
}
|
|
|
|
windowGuiEnd(&ui->win);
|
|
|
|
windowDisplayEnd(&ui->win);
|
|
|
|
|
|
|
|
nk_input_begin(ctx);
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
|
|
nk_sdl_handle_event(&event);
|
|
|
|
if (event.type == SDL_QUIT) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (event.type == SDL_KEYDOWN) {
|
|
|
|
ctrlKeyDown(&ui->ctrl, event.key.keysym.sym);
|
|
|
|
}
|
|
|
|
if (event.type == SDL_KEYUP) {
|
|
|
|
ctrlKeyUp(&ui->ctrl, event.key.keysym.sym);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nk_input_end(ctx);
|
|
|
|
emuSetKeys(&ui->emu, ctrlKeys(&ui->ctrl));
|
|
|
|
}
|
|
|
|
}
|