#include "audio.h" #include "controller.h" #include "emulation.h" #include "external/tinyfiledialogs.h" #include #include #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[4] = { "File", "Emulation", "Video", NULL }; static const char *ROM_EXTENSIONS[2] = { "*.vb", NULL }; typedef enum status_t { status_paused, status_running } status_t; int uiRun(UIContext *ui, bool running) { static uint8_t leftEye[384*224] = {0}; static uint8_t rightEye[384*224] = {0}; status_t status = running ? status_running : status_paused; windowUpdate(&ui->win, leftEye, rightEye); while (1) { struct nk_context *ctx; SDL_Event event; void *samples; uint32_t bytes; ctx = ui->win.nk; if (status == status_running) { emuTick(&ui->emu); } 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(windowScaleX(&ui->win, 100), windowGetScreenHeight(&ui->win)))) { nk_layout_row_dynamic(ctx, windowGetMenuHeight(&ui->win), 1); if (nk_menu_item_label(ctx, "Open ROM", NK_TEXT_ALIGN_LEFT)) { char *file = tinyfd_openFileDialog("Pick a ROM", NULL, 1, ROM_EXTENSIONS, "Virtual Boy ROM files", false); if (file) { uiLoadGame(ui, file); status = status_running; } } 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(windowScaleX(&ui->win, 100), windowGetScreenHeight(&ui->win)))) { const char *label = status == status_paused ? "Resume" : "Pause"; nk_layout_row_dynamic(ctx, windowGetMenuHeight(&ui->win), 1); if (nk_menu_item_label(ctx, label, NK_TEXT_ALIGN_LEFT)) { if (status == status_paused) status = status_running; else status = status_paused; } if (nk_menu_item_label(ctx, "Reset", NK_TEXT_ALIGN_LEFT)) { emuReset(&ui->emu); status = emuIsGameLoaded(&ui->emu) ? status_running : status_paused; } nk_menu_end(ctx); } if (nk_menu_begin_label(ctx, "Video", NK_TEXT_ALIGN_CENTERED, nk_vec2(windowScaleX(&ui->win, 100), windowGetScreenHeight(&ui->win)))) { float multiplier = windowGetScreenSizeMultiplier(&ui->win); nk_layout_row_dynamic(ctx, windowGetMenuHeight(&ui->win), 1); if (nk_menu_item_label(ctx, multiplier == 1.0f ? "x1 *" : "x1", NK_TEXT_ALIGN_LEFT)) { windowSetScreenSizeMultiplier(&ui->win, 1.0f); } if (nk_menu_item_label(ctx, multiplier == 2.0f ? "x2 *" : "x2", NK_TEXT_ALIGN_LEFT)) { windowSetScreenSizeMultiplier(&ui->win, 2.0f); } if (nk_menu_item_label(ctx, multiplier == 3.0f ? "x3 *" : "x3", NK_TEXT_ALIGN_LEFT)) { windowSetScreenSizeMultiplier(&ui->win, 3.0f); } if (nk_menu_item_label(ctx, multiplier == 4.0f ? "x4 *" : "x4", NK_TEXT_ALIGN_LEFT)) { windowSetScreenSizeMultiplier(&ui->win, 4.0f); } 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)); } }