Files
ARMSX1/frontend/imgui_layer.cpp
T

881 lines
29 KiB
C++
Raw Normal View History

#include "imgui_layer.h"
#include <string>
#include <cstring>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include "../third_party/imgui/imgui.h"
#include "../third_party/imgui/backends/imgui_impl_sdl2.h"
#include "../third_party/imgui/backends/imgui_impl_sdlrenderer2.h"
#include "../psx/log.h"
static bool g_imgui_initialized = false;
static SDL_Window* g_window = nullptr;
static SDL_Renderer* g_renderer = nullptr;
extern "C" PSXE_API void imgui_layer_init(SDL_Window* window, SDL_Renderer* renderer) {
if (!window || !renderer)
return;
if (g_imgui_initialized) {
if (window == g_window && renderer == g_renderer)
return;
imgui_layer_shutdown();
}
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.IniFilename = nullptr; // avoid disk writes
ImGui::StyleColorsDark();
g_window = window;
g_renderer = renderer;
ImGui_ImplSDL2_InitForSDLRenderer(g_window, g_renderer);
ImGui_ImplSDLRenderer2_Init(g_renderer);
g_imgui_initialized = true;
}
extern "C" PSXE_API void imgui_layer_shutdown(void) {
if (!g_imgui_initialized)
return;
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
g_window = nullptr;
g_renderer = nullptr;
g_imgui_initialized = false;
}
extern "C" PSXE_API void imgui_layer_handle_event(const SDL_Event* event) {
if (!g_imgui_initialized || !event)
return;
ImGui_ImplSDL2_ProcessEvent(event);
}
extern "C" PSXE_API void imgui_layer_new_frame(void) {
if (!g_imgui_initialized)
return;
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
}
extern "C" PSXE_API void imgui_layer_render_overlay(const char* os_name, float fps, float frame_ms, int dropped_frames, int paused, int tex_w, int tex_h, int out_w, int out_h) {
if (!g_imgui_initialized)
return;
const ImGuiWindowFlags flags =
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_NoBackground;
ImGui::SetNextWindowPos(ImVec2(10.0f, 10.0f), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(240.0f, 115.0f), ImGuiCond_Always);
ImGui::Begin("Overlay", nullptr, flags);
ImGui::Text("FPS: %.1f", fps);
ImGui::Text("Frame: %.2f ms", frame_ms);
ImGui::Text("Dropped: %d", dropped_frames);
ImGui::Text("Paused: %s", paused ? "yes" : "no");
ImGui::Text("Tex: %dx%d Out: %dx%d", tex_w, tex_h, out_w, out_h);
ImGui::Text("OS : %s", os_name ? os_name : "unknown");
ImGui::End();
}
extern "C" PSXE_API void imgui_layer_flush(void) {
if (!g_imgui_initialized)
return;
ImGui::Render();
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), g_renderer);
}
#if defined(IMGUI_FRONTEND) && !defined(__DLL_BUILD) && !defined(IOS_TARGET) && !defined(__ANDROID__)
extern "C" {
#include "config.h"
PSXE_API int psxe_run_configured(psxe_config_t* cfg, void* external_window, void* external_renderer);
}
#include <array>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <vector>
#include <climits>
static void sync_buffer(std::array<char, 512>& buf, const std::string& value) {
buf.fill(0);
size_t copy_len = std::min(value.size(), buf.size() - 1);
memcpy(buf.data(), value.c_str(), copy_len);
buf[copy_len] = '\0';
}
static void center_next_window(const ImVec2& size) {
ImGuiIO& io = ImGui::GetIO();
ImVec2 center = ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(size, ImGuiCond_FirstUseEver);
}
static std::string read_readme() {
std::ifstream file("README.md");
if (!file.is_open())
return std::string();
std::ostringstream ss;
ss << file.rdbuf();
return ss.str();
}
static int map_upscale_index(int height) {
switch (height) {
case 720: return 1;
case 1080: return 2;
case 1440: return 3;
case 2160: return 4;
default: return 0;
}
}
static int upscale_value_from_index(int idx) {
switch (idx) {
case 1: return 720;
case 2: return 1080;
case 3: return 1440;
case 4: return 2160;
default: return 480;
}
}
static std::string display_aspect_to_string(int aspect) {
switch (aspect) {
case 1: return "square";
case 2: return "wide16x9";
default: return "classic";
}
}
static int display_aspect_from_string(const std::string& value) {
if (value == "square") return 1;
if (value == "wide16x9") return 2;
return 0;
}
static std::string upscale_label_from_height(int height) {
switch (height) {
case 720: return "720p";
case 1080: return "1080p";
case 1440: return "1440p";
case 2160: return "2160p";
default: return "480p";
}
}
#ifdef __EMSCRIPTEN__
static std::string g_wasm_pending_bios;
static std::string g_wasm_pending_cd;
extern "C" void psxe_wasm_on_file(const char* path, int is_bios) {
if (!path)
return;
if (is_bios) {
g_wasm_pending_bios = path;
} else {
g_wasm_pending_cd = path;
}
}
extern "C" void psxe_wasm_pick_file(int is_bios) {
EM_ASM({
const isBios = $0 === 1;
const accept = isBios ? '.bin' : '.cue,.bin,.iso,.img';
const input = document.createElement('input');
input.type = 'file';
input.accept = accept;
input.style.display = 'none';
document.body.appendChild(input);
input.addEventListener('change', async function() {
const file = input.files[0];
if (!file) {
document.body.removeChild(input);
return;
}
const data = new Uint8Array(await file.arrayBuffer());
const mountPath = isBios ? '/bios.bin' : '/cdrom.bin';
FS.writeFile(mountPath, data);
Module.ccall('psxe_wasm_on_file', null, ['string','number'], [mountPath, isBios ? 1 : 0]);
document.body.removeChild(input);
});
input.click();
}, is_bios);
}
#endif
static bool write_settings_file(
const std::string& path,
const std::string& bios_search,
const std::string& model,
const std::string& bios_override,
const std::string& region,
bool texture_scale_mode,
bool debug_panel,
bool stretch_mode,
int display_aspect,
int upscale_height
) {
std::ofstream out(path, std::ios::trunc);
if (!out.is_open()) {
log_error("Failed to write settings file at %s", path.c_str());
return false;
}
out
<< "# Settings file generated by ARMSX frontend\n\n"
<< "psxe_version = \"" << STR(REP_VERSION) << "\"\n\n"
<< "[bios]\n"
<< " search_path = \"" << bios_search << "\"\n"
<< " preferred_model = \"" << model << "\"\n"
<< " override_file = \"" << bios_override << "\"\n\n"
<< "[console]\n"
<< " region = \"" << region << "\"\n\n"
<< "[video]\n"
<< " texture_scale_mode = " << (texture_scale_mode ? "true" : "false") << "\n"
<< " debug_panel = " << (debug_panel ? "true" : "false") << "\n"
<< " stretch_mode = " << (stretch_mode ? "true" : "false") << "\n"
<< " display_aspect = \"" << display_aspect_to_string(display_aspect) << "\"\n"
<< " wide_upscale = \"" << upscale_label_from_height(upscale_height) << "\"\n";
return true;
}
static std::string get_current_directory() {
char buf[PATH_MAX];
if (getcwd(buf, sizeof(buf)))
return std::string(buf);
return std::string(".");
}
struct FilePickerState {
bool open = false;
std::string cwd;
std::string selection;
};
static std::string get_pref_directory() {
const char* pref = psxe_cfg_get_pref_path();
if (pref && pref[0])
return std::string(pref);
return get_current_directory();
}
static std::string default_settings_path() {
const char* pref = psxe_cfg_get_pref_path();
if (pref && pref[0])
return std::string(pref) + "settings.toml";
return std::string("settings.toml");
}
static bool is_directory(const std::string& path) {
struct stat st;
if (stat(path.c_str(), &st) == 0)
return S_ISDIR(st.st_mode);
return false;
}
static bool list_directory(const std::string& cwd, std::vector<std::pair<std::string, bool>>& entries) {
DIR* dir = opendir(cwd.c_str());
if (!dir)
return false;
struct dirent* dent;
while ((dent = readdir(dir)) != NULL) {
std::string name = dent->d_name;
if (name == ".")
continue;
// include .. for navigation
std::string full = cwd + "/" + name;
bool dir_flag = (dent->d_type == DT_DIR) || ((dent->d_type == DT_UNKNOWN) && is_directory(full));
entries.emplace_back(full, dir_flag);
}
closedir(dir);
std::sort(entries.begin(), entries.end(), [](const auto& a, const auto& b) {
if (a.second != b.second)
return a.second && !b.second;
return a.first < b.first;
});
return true;
}
static bool file_picker_popup(const char* popup_id, FilePickerState& state, std::string& target_path) {
if (state.open)
ImGui::OpenPopup(popup_id);
bool changed = false;
ImGui::SetNextWindowSize(ImVec2(420.0f, 320.0f), ImGuiCond_Appearing);
if (ImGui::BeginPopupModal(popup_id, nullptr, ImGuiWindowFlags_NoResize)) {
if (state.cwd.empty())
state.cwd = get_pref_directory();
ImGui::TextWrapped("Current folder:\n%s", state.cwd.c_str());
if (ImGui::Button("Up")) {
size_t pos = state.cwd.find_last_of("/\\");
if (pos != std::string::npos) {
state.cwd = state.cwd.substr(0, pos ? pos : 1);
state.selection.clear();
} else {
state.cwd = "/";
}
}
ImGui::SameLine();
if (ImGui::Button("Refresh")) {
// simple redraw
}
ImGui::BeginChild("browser", ImVec2(420, 260), true);
std::vector<std::pair<std::string, bool>> entries;
if (!list_directory(state.cwd, entries)) {
log_error("File picker failed to list dir %s", state.cwd.c_str());
} else {
for (const auto& entry : entries) {
const std::string& full = entry.first;
std::string label;
if (full.rfind(state.cwd, 0) == 0 && full.size() >= state.cwd.size()) {
label = full.substr(state.cwd.size());
if (!label.empty() && (label[0] == '/' || label[0] == '\\'))
label = label.substr(1);
}
if (label.empty())
label = full;
bool is_dir = entry.second;
std::string display = is_dir ? "[DIR] " + label : label;
if (ImGui::Selectable(display.c_str(), state.selection == full, ImGuiSelectableFlags_AllowDoubleClick)) {
state.selection = full;
if (ImGui::IsMouseDoubleClicked(0)) {
if (is_dir) {
state.cwd = full;
state.selection.clear();
} else {
target_path = state.selection;
changed = true;
ImGui::CloseCurrentPopup();
}
}
}
}
}
ImGui::EndChild();
if (ImGui::Button("Select")) {
if (!state.selection.empty()) {
if (is_directory(state.selection)) {
state.cwd = state.selection;
state.selection.clear();
} else {
target_path = state.selection;
changed = true;
ImGui::CloseCurrentPopup();
}
}
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
state.open = false;
return changed;
}
extern "C" PSXE_API int imgui_frontend_main(int argc, const char* argv[]) {
psxe_config_t* cfg = psxe_cfg_create();
if (!cfg) {
log_fatal("Failed to allocate frontend config");
return 1;
}
psxe_cfg_init(cfg);
psxe_cfg_load_defaults(cfg);
psxe_cfg_load(cfg, argc, argv);
std::string bios_path = cfg->bios ? cfg->bios : "";
std::string cdrom_path = cfg->cd_path ? cfg->cd_path : "";
std::string exp_path = cfg->exp_path ? cfg->exp_path : "";
std::string exe_path = cfg->exe ? cfg->exe : "";
std::string region = cfg->region ? cfg->region : "ntsc";
std::string model = cfg->model ? cfg->model : "scph1001";
std::string settings_path = cfg->settings_path ? cfg->settings_path : default_settings_path();
std::string bios_search = cfg->bios_search ? cfg->bios_search : "bios";
bool quiet = cfg->quiet != 0;
bool use_args = cfg->use_args != 0;
bool texture_scale_mode = cfg->texture_scale_mode != 0;
bool debug_panel = cfg->debug_panel != 0;
bool stretch_mode = cfg->stretch_mode != 0;
int display_aspect = cfg->display_aspect;
int upscale_height = cfg->upscale_height ? cfg->upscale_height : 480;
int log_level = std::clamp(cfg->log_level, 0, (int)LOG_FATAL);
int scale = cfg->scale ? cfg->scale : 1;
std::array<char, 512> bios_buf{};
std::array<char, 512> cdrom_buf{};
std::array<char, 512> exp_buf{};
std::array<char, 512> exe_buf{};
std::array<char, 512> region_buf{};
std::array<char, 512> model_buf{};
std::array<char, 512> settings_buf{};
std::array<char, 512> bios_search_buf{};
sync_buffer(bios_buf, bios_path);
sync_buffer(cdrom_buf, cdrom_path);
sync_buffer(exp_buf, exp_path);
sync_buffer(exe_buf, exe_path);
sync_buffer(region_buf, region);
sync_buffer(model_buf, model);
sync_buffer(settings_buf, settings_path);
sync_buffer(bios_search_buf, bios_search);
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
log_error("SDL_Init failed for frontend: %s", SDL_GetError());
psxe_cfg_destroy(cfg);
return 1;
}
SDL_Window* window = SDL_CreateWindow(
"ARMSX Frontend",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
960, 640,
SDL_WINDOW_RESIZABLE
);
if (!window) {
log_error("Failed to create SDL window for frontend: %s", SDL_GetError());
psxe_cfg_destroy(cfg);
SDL_Quit();
return 1;
}
SDL_Renderer* renderer = SDL_CreateRenderer(
window,
-1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
);
if (!renderer) {
log_error("Failed to create SDL renderer for frontend: %s", SDL_GetError());
SDL_DestroyWindow(window);
psxe_cfg_destroy(cfg);
SDL_Quit();
return 1;
}
imgui_layer_init(window, renderer);
bool running = true;
bool settings_dirty = false;
bool start_requested = false;
bool show_settings = false;
bool show_about = false;
FilePickerState bios_picker{};
FilePickerState cd_picker{};
const std::string readme_text = read_readme();
while (running) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
imgui_layer_handle_event(&event);
if (event.type == SDL_QUIT) {
running = false;
}
}
imgui_layer_new_frame();
ImGuiIO& io = ImGui::GetIO();
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_Always);
ImGui::SetNextWindowSize(io.DisplaySize, ImGuiCond_Always);
ImGuiWindowFlags root_flags =
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_MenuBar;
ImGui::Begin("FrontendRoot", nullptr, root_flags);
if (ImGui::BeginMenuBar()) {
if (ImGui::MenuItem("Start")) {
if (!cdrom_path.empty() && bios_path.empty()) {
SDL_ShowSimpleMessageBox(
SDL_MESSAGEBOX_ERROR,
"Missing BIOS",
"A BIOS is required before booting a CDROM image.",
window
);
} else {
start_requested = true;
running = false;
}
}
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Load BIOS")) {
#ifdef __EMSCRIPTEN__
psxe_wasm_pick_file(1);
#else
bios_picker.open = true;
bios_picker.selection.clear();
bios_picker.cwd = get_pref_directory();
#endif
}
if (ImGui::MenuItem("Load CDROM")) {
#ifdef __EMSCRIPTEN__
psxe_wasm_pick_file(0);
#else
cd_picker.open = true;
cd_picker.selection.clear();
cd_picker.cwd = get_pref_directory();
#endif
}
ImGui::Separator();
if (ImGui::MenuItem("Quit")) {
running = false;
}
ImGui::EndMenu();
}
if (ImGui::MenuItem("Settings")) {
show_settings = true;
show_about = false;
}
if (ImGui::MenuItem("About")) {
show_about = true;
show_settings = false;
}
ImGui::EndMenuBar();
}
ImGui::End();
ImGui::SetNextWindowSize(ImVec2(420.0f, 210.0f), ImGuiCond_Appearing);
if (file_picker_popup("Browse BIOS File", bios_picker, bios_path)) {
sync_buffer(bios_buf, bios_path);
settings_dirty = true;
}
#ifdef __EMSCRIPTEN__
if (!g_wasm_pending_bios.empty()) {
bios_path = g_wasm_pending_bios;
sync_buffer(bios_buf, bios_path);
settings_dirty = true;
g_wasm_pending_bios.clear();
}
#endif
ImGui::SetNextWindowSize(ImVec2(420.0f, 210.0f), ImGuiCond_Appearing);
if (file_picker_popup("Browse CDROM File", cd_picker, cdrom_path)) {
sync_buffer(cdrom_buf, cdrom_path);
}
#ifdef __EMSCRIPTEN__
if (!g_wasm_pending_cd.empty()) {
cdrom_path = g_wasm_pending_cd;
sync_buffer(cdrom_buf, cdrom_path);
g_wasm_pending_cd.clear();
}
#endif
if (show_settings) {
center_next_window(ImVec2(392.0f, 448.0f));
if (ImGui::Begin("Settings", &show_settings, ImGuiWindowFlags_NoCollapse)) {
ImGui::TextUnformatted("Boot options");
if (ImGui::InputText("BIOS", bios_buf.data(), bios_buf.size())) {
bios_path = bios_buf.data();
settings_dirty = true;
}
if (ImGui::InputText("CDROM", cdrom_buf.data(), cdrom_buf.size()))
cdrom_path = cdrom_buf.data();
if (ImGui::InputText("Expansion ROM", exp_buf.data(), exp_buf.size()))
exp_path = exp_buf.data();
if (ImGui::InputText("PS-X EXE", exe_buf.data(), exe_buf.size()))
exe_path = exe_buf.data();
ImGui::Separator();
ImGui::TextUnformatted("Console");
const char* regions[] = { "auto", "ntsc", "pal" };
int region_idx = 1;
for (int i = 0; i < 3; i++) {
if (region == regions[i]) region_idx = i;
}
if (ImGui::Combo("Region", &region_idx, regions, IM_ARRAYSIZE(regions))) {
region = regions[region_idx];
settings_dirty = true;
}
static const char* models[] = {
"scph1000","scph1001","scph1002","scph3000","scph3500","scph5000",
"scph5500","scph5501","scph5502","scph5552",
"scph7000","scph7001","scph7002","scph7003",
"scph7501","scph7502","scph9002","scph100","scph101","scph102a","scph102b","scph102c"
};
int model_idx = 1;
for (int i = 0; i < (int)(sizeof(models)/sizeof(models[0])); i++) {
if (model == models[i]) { model_idx = i; break; }
}
if (ImGui::Combo("Model", &model_idx, models, IM_ARRAYSIZE(models))) {
model = models[model_idx];
settings_dirty = true;
}
ImGui::Checkbox("Use CLI args only (--use-args)", &use_args);
if (ImGui::InputText("Settings file", settings_buf.data(), settings_buf.size())) {
settings_path = settings_buf.data();
settings_dirty = true;
}
if (ImGui::InputText("BIOS folder", bios_search_buf.data(), bios_search_buf.size())) {
bios_search = bios_search_buf.data();
settings_dirty = true;
}
ImGui::Separator();
ImGui::TextUnformatted("Video");
if (ImGui::SliderInt("Scale", &scale, 1, 6))
settings_dirty = true;
if (ImGui::Checkbox("Texture scale mode", &texture_scale_mode))
settings_dirty = true;
if (ImGui::Checkbox("Debug panel", &debug_panel))
settings_dirty = true;
if (ImGui::Checkbox("Stretch to window", &stretch_mode))
settings_dirty = true;
const char* aspect_labels[] = { "classic", "square", "wide16x9" };
if (display_aspect < 0 || display_aspect > 2)
display_aspect = 0;
if (ImGui::Combo("Display aspect", &display_aspect, aspect_labels, IM_ARRAYSIZE(aspect_labels)))
settings_dirty = true;
const char* upscales[] = { "480p", "720p", "1080p", "1440p", "2160p" };
int upscale_idx = map_upscale_index(upscale_height);
if (ImGui::Combo("Wide upscale", &upscale_idx, upscales, IM_ARRAYSIZE(upscales))) {
upscale_height = upscale_value_from_index(upscale_idx);
settings_dirty = true;
}
ImGui::Separator();
ImGui::TextUnformatted("Logging");
const char* log_levels[] = { "trace", "debug", "info", "warn", "error", "fatal" };
log_level = std::clamp(log_level, 0, (int)LOG_FATAL);
if (ImGui::Combo("Log level", &log_level, log_levels, IM_ARRAYSIZE(log_levels)))
settings_dirty = true;
if (ImGui::Checkbox("Quiet logs", &quiet))
settings_dirty = true;
if (!readme_text.empty()) {
ImGui::Separator();
ImGui::TextUnformatted("CLI reference (README.md)");
ImGui::BeginChild("readme_help", ImVec2(0, 160), true);
ImGui::TextUnformatted(readme_text.c_str());
ImGui::EndChild();
}
}
ImGui::End();
}
if (show_about) {
center_next_window(ImVec2(540.0f, 260.0f));
ImGui::OpenPopup("About ARMSX");
show_about = false;
}
ImGui::SetNextWindowSize(ImVec2(540.0f, 260.0f), ImGuiCond_Appearing);
if (ImGui::BeginPopupModal("About ARMSX", nullptr, ImGuiWindowFlags_NoResize)) {
ImGui::TextWrapped("ARMSX - the PS1 emulator for everything.");
ImGui::Spacing();
ImGui::TextWrapped("Use the menu bar to load a BIOS, choose a CDROM, adjust settings, and press Start to boot.");
ImGui::Spacing();
if (ImGui::Button("Close")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::Render();
if (settings_dirty) {
if (settings_path.empty())
settings_path = default_settings_path();
write_settings_file(
settings_path,
bios_search,
model,
bios_path,
region,
texture_scale_mode,
debug_panel,
stretch_mode,
display_aspect,
upscale_height
);
settings_dirty = false;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer);
SDL_RenderPresent(renderer);
}
imgui_layer_shutdown();
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
if (!start_requested) {
psxe_cfg_destroy(cfg);
return 0;
}
cfg->bios = bios_path.empty() ? NULL : bios_path.c_str();
cfg->cd_path = cdrom_path.empty() ? NULL : cdrom_path.c_str();
cfg->exp_path = exp_path.empty() ? NULL : exp_path.c_str();
cfg->exe = exe_path.empty() ? NULL : exe_path.c_str();
cfg->region = region.empty() ? cfg->region : region.c_str();
cfg->model = model.empty() ? cfg->model : model.c_str();
cfg->bios_search = bios_search.empty() ? cfg->bios_search : bios_search.c_str();
cfg->settings_path = settings_path.empty() ? cfg->settings_path : settings_path.c_str();
cfg->quiet = quiet ? 1 : 0;
cfg->use_args = use_args ? 1 : 0;
cfg->texture_scale_mode = texture_scale_mode ? 1 : 0;
cfg->debug_panel = debug_panel ? 1 : 0;
cfg->stretch_mode = stretch_mode ? 1 : 0;
cfg->display_aspect = display_aspect;
cfg->upscale_height = upscale_height;
cfg->log_level = log_level;
cfg->scale = scale;
return psxe_run_configured(cfg, NULL, NULL);
}
#endif
#if defined(IMGUI_FRONTEND) && !defined(__DLL_BUILD) && !defined(IOS_TARGET) && !defined(__ANDROID__)
static FilePickerState g_ingame_cd_picker{};
static std::string g_ingame_cd_target;
static bool g_ingame_about = false;
extern "C" PSXE_API void imgui_ingame_menu_render(int paused, const char* current_cd, imgui_ingame_actions_t* actions) {
if (!actions)
return;
memset(actions, 0, sizeof(*actions));
actions->new_cd_path[0] = '\0';
ImGuiIO& io = ImGui::GetIO();
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_Always);
ImGui::SetNextWindowSize(io.DisplaySize, ImGuiCond_Always);
ImGuiWindowFlags flags =
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_MenuBar;
if (ImGui::Begin("InGameMenuBar", nullptr, flags)) {
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("Start")) {
if (ImGui::MenuItem(paused ? "Resume" : "Pause")) {
actions->toggle_pause = 1;
}
if (ImGui::MenuItem("Reset")) {
actions->reset = 1;
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Load CDROM...")) {
g_ingame_cd_picker.open = true;
g_ingame_cd_picker.selection.clear();
g_ingame_cd_picker.cwd = get_pref_directory();
}
if (ImGui::MenuItem("Quit")) {
actions->quit = 1;
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("About")) {
if (ImGui::MenuItem("About ARMSX")) {
g_ingame_about = true;
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
//ImGui::TextWrapped("CDROM: %s", current_cd && current_cd[0] ? current_cd : "(none)");
}
ImGui::End();
ImGui::SetNextWindowSize(ImVec2(420.0f, 210.0f), ImGuiCond_Appearing);
if (file_picker_popup("Ingame CD Picker", g_ingame_cd_picker, g_ingame_cd_target)) {
if (g_ingame_cd_target.size() < sizeof(actions->new_cd_path)) {
memcpy(actions->new_cd_path, g_ingame_cd_target.c_str(), g_ingame_cd_target.size() + 1);
} else {
strncpy(actions->new_cd_path, g_ingame_cd_target.c_str(), sizeof(actions->new_cd_path) - 1);
actions->new_cd_path[sizeof(actions->new_cd_path) - 1] = '\0';
}
actions->swap_cd = 1;
}
if (g_ingame_about)
ImGui::OpenPopup("About In-Game");
ImGui::SetNextWindowSize(ImVec2(420.0f, 200.0f), ImGuiCond_Appearing);
if (ImGui::BeginPopupModal("About In-Game", &g_ingame_about, ImGuiWindowFlags_NoResize)) {
ImGui::TextWrapped("ARMSX in-game menu. Press F6 to toggle visibility.");
ImGui::Spacing();
ImGui::TextWrapped("Use Start -> Pause/Resume, Reset for soft reset, File -> Load CDROM... to reboot with a new image.");
ImGui::Spacing();
if (ImGui::Button("Close")) {
g_ingame_about = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
#endif