From d0dfc4561aed61b3bfee09dbda5f3175844d0d05 Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Sun, 17 Mar 2024 22:12:37 +0100 Subject: [PATCH] feat: merged WHDLoad panel and other improvements from preview --- CMakeLists.txt | 5 +- Makefile | 6 +- cmake/gcc-flags-x86_64.cmake | 3 + external/libguisan/include/guisan.hpp | 12 +- .../com.blitterstudio.amiberry.metainfo.xml | 2 +- src/cfgfile.cpp | 50 +- src/include/fsdb.h | 10 +- src/include/options.h | 100 ++- src/main.cpp | 2 +- src/osdep/amiberry.cpp | 391 +++++------- src/osdep/amiberry_filesys.cpp | 574 +++++++++++------- src/osdep/amiberry_gui.cpp | 191 ++---- src/osdep/amiberry_input.cpp | 2 +- src/osdep/amiberry_whdbooter.cpp | 508 ++++++++++------ src/osdep/fsdb_host.h | 4 +- src/osdep/gui/Navigation.cpp | 15 +- src/osdep/gui/PanelCustom.cpp | 2 +- src/osdep/gui/PanelDiskSwapper.cpp | 4 +- src/osdep/gui/PanelDisplay.cpp | 11 +- src/osdep/gui/PanelFloppy.cpp | 6 +- src/osdep/gui/PanelHD.cpp | 2 +- src/osdep/gui/PanelQuickstart.cpp | 35 +- src/osdep/gui/PanelWHDLoad.cpp | 455 ++++++++++++++ src/osdep/gui/SelectFile.cpp | 21 +- src/osdep/gui/SelectFolder.cpp | 10 +- src/osdep/gui/SelectorEntry.hpp | 6 +- src/osdep/gui/ShowCustomFields.cpp | 434 +++++++++++++ src/osdep/gui/gui_handling.h | 13 +- src/osdep/gui/main_window.cpp | 153 ++--- src/osdep/target.h | 17 +- 30 files changed, 2080 insertions(+), 964 deletions(-) create mode 100644 cmake/gcc-flags-x86_64.cmake create mode 100644 src/osdep/gui/PanelWHDLoad.cpp create mode 100644 src/osdep/gui/ShowCustomFields.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b23c45a0..016355b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,6 +290,7 @@ add_executable(${PROJECT_NAME} src/threaddep/threading.cpp src/osdep/gui/ControllerMap.cpp src/osdep/gui/SelectorEntry.cpp + src/osdep/gui/ShowCustomFields.cpp src/osdep/gui/ShowHelp.cpp src/osdep/gui/ShowMessage.cpp src/osdep/gui/ShowDiskInfo.cpp @@ -322,6 +323,7 @@ add_executable(${PROJECT_NAME} src/osdep/gui/PanelPrio.cpp src/osdep/gui/PanelSavestate.cpp src/osdep/gui/PanelVirtualKeyboard.cpp + src/osdep/gui/PanelWHDLoad.cpp src/osdep/gui/main_window.cpp src/osdep/gui/Navigation.cpp src/osdep/vkbd/vkbd.cpp @@ -387,7 +389,8 @@ target_link_options(${PROJECT_NAME} PRIVATE "LINKER:-as-needed,-no-pie") if (USE_OPENGL) add_definitions(-DUSE_OPENGL) find_package(OpenGL REQUIRED) - TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE ${TARGET_LINK_LIBRARIES} OpenGL::GL) + find_package(GLEW REQUIRED) + TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE ${TARGET_LINK_LIBRARIES} GLEW OpenGL::GL) endif () add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD diff --git a/Makefile b/Makefile index 1230a31a..a94602b2 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ LDFLAGS += -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -lpthread -lz -lpng -lrt ifdef USE_OPENGL CFLAGS += -DUSE_OPENGL - LDFLAGS += -lGL + LDFLAGS += -lGLEW -lGL endif ifdef USE_LTO @@ -636,6 +636,7 @@ OBJS = \ src/threaddep/threading.o \ src/osdep/gui/ControllerMap.o \ src/osdep/gui/SelectorEntry.o \ + src/osdep/gui/ShowCustomFields.o \ src/osdep/gui/ShowHelp.o \ src/osdep/gui/ShowMessage.o \ src/osdep/gui/ShowDiskInfo.o \ @@ -668,6 +669,7 @@ OBJS = \ src/osdep/gui/PanelPrio.o \ src/osdep/gui/PanelSavestate.o \ src/osdep/gui/PanelVirtualKeyboard.o \ + src/osdep/gui/PanelWHDLoad.o \ src/osdep/gui/main_window.o \ src/osdep/gui/Navigation.o \ src/osdep/vkbd/vkbd.o \ @@ -737,7 +739,7 @@ clean: cleanprofile: $(RM) $(OBJS:%.o=%.gcda) $(MAKE) -C external/libguisan cleanprofile - + guisan: $(MAKE) -C external/libguisan diff --git a/cmake/gcc-flags-x86_64.cmake b/cmake/gcc-flags-x86_64.cmake new file mode 100644 index 00000000..a86e84ac --- /dev/null +++ b/cmake/gcc-flags-x86_64.cmake @@ -0,0 +1,3 @@ +set(CMAKE_C_FLAGS_INIT "-march=x86-64 -msse2 -mfpmath=sse -mstackrealign") +set(CMAKE_CXX_FLAGS_INIT "${CMAKE_C_FLAGS_INIT}") + diff --git a/external/libguisan/include/guisan.hpp b/external/libguisan/include/guisan.hpp index a6f78b7d..facee2fd 100644 --- a/external/libguisan/include/guisan.hpp +++ b/external/libguisan/include/guisan.hpp @@ -117,13 +117,13 @@ class Widget; extern "C" { -/** - * Gets the the version of Guisan. As it is a C function + /** + * Gets the the version of Guisan. As it is a C function * it can be used to check for Guisan with autotools. - * - * @return the version of Guisan. - */ -GCN_CORE_DECLSPEC extern const char* gcnGuisanVersion(); + * + * @return the version of Guisan. + */ + GCN_CORE_DECLSPEC extern const char* gcnGuisanVersion(); } #endif // end GCN_GUISAN_HPP diff --git a/flatpak/com.blitterstudio.amiberry.metainfo.xml b/flatpak/com.blitterstudio.amiberry.metainfo.xml index 984d7fff..5f0a58c8 100644 --- a/flatpak/com.blitterstudio.amiberry.metainfo.xml +++ b/flatpak/com.blitterstudio.amiberry.metainfo.xml @@ -44,7 +44,7 @@ - + diff --git a/src/cfgfile.cpp b/src/cfgfile.cpp index 3d4bb386..29999c1a 100644 --- a/src/cfgfile.cpp +++ b/src/cfgfile.cpp @@ -2968,18 +2968,18 @@ void cfgfile_save_options (struct zfile *f, struct uae_prefs *p, int type) #ifdef AMIBERRY cfg_write(_T("; *** WHDLoad Booter. Options"), f); - cfgfile_write_str(f, _T("whdload_slave"), p->whdbootprefs.slave); - cfgfile_write_bool(f, _T("whdload_showsplash"), p->whdbootprefs.showsplash); - cfgfile_write_bool(f, _T("whdload_buttonwait"), p->whdbootprefs.buttonwait); - cfgfile_dwrite(f, _T("whdload_configdelay"), _T("%d"), p->whdbootprefs.configdelay); - cfgfile_write(f, _T("whdload_custom1"), _T("%d"), p->whdbootprefs.custom1); - cfgfile_write(f, _T("whdload_custom2"), _T("%d"), p->whdbootprefs.custom2); - cfgfile_write(f, _T("whdload_custom3"), _T("%d"), p->whdbootprefs.custom3); - cfgfile_write(f, _T("whdload_custom4"), _T("%d"), p->whdbootprefs.custom4); - cfgfile_write(f, _T("whdload_custom5"), _T("%d"), p->whdbootprefs.custom5); - cfgfile_write_str(f, _T("whdload_custom"), p->whdbootprefs.custom); - cfgfile_write_bool(f, _T("whdload_writecache"), p->whdbootprefs.writecache); - cfgfile_write_bool(f, _T("whdload_quit_on_exit"), p->whdbootprefs.quit_on_exit); + cfgfile_write_str(f, _T("whdload_slave"), whdload_prefs.selected_slave.filename.c_str()); + cfgfile_write_bool(f, _T("whdload_showsplash"), whdload_prefs.show_splash); + cfgfile_write_bool(f, _T("whdload_buttonwait"), whdload_prefs.button_wait); + cfgfile_dwrite(f, _T("whdload_configdelay"), _T("%d"), whdload_prefs.config_delay); + cfgfile_write(f, _T("whdload_custom1"), _T("%d"), whdload_prefs.selected_slave.custom1.value); + cfgfile_write(f, _T("whdload_custom2"), _T("%d"), whdload_prefs.selected_slave.custom2.value); + cfgfile_write(f, _T("whdload_custom3"), _T("%d"), whdload_prefs.selected_slave.custom3.value); + cfgfile_write(f, _T("whdload_custom4"), _T("%d"), whdload_prefs.selected_slave.custom4.value); + cfgfile_write(f, _T("whdload_custom5"), _T("%d"), whdload_prefs.selected_slave.custom5.value); + cfgfile_write_str(f, _T("whdload_custom"), whdload_prefs.custom.c_str()); + cfgfile_write_bool(f, _T("whdload_writecache"), whdload_prefs.write_cache); + cfgfile_write_bool(f, _T("whdload_quit_on_exit"), whdload_prefs.quit_on_exit); #endif } @@ -3518,18 +3518,18 @@ static int cfgfile_parse_host (struct uae_prefs *p, TCHAR *option, TCHAR *value) } /* Read in WHDLoad Options */ - if (cfgfile_string(option, value, _T("whdload_slave"), p->whdbootprefs.slave, sizeof p->whdbootprefs.slave / sizeof(TCHAR)) - || cfgfile_string(option, value, _T("whdload_custom"), p->whdbootprefs.custom, sizeof p->whdbootprefs.custom / sizeof(TCHAR)) - || cfgfile_intval(option, value, _T("whdload_custom1"), &p->whdbootprefs.custom1, 1) - || cfgfile_intval(option, value, _T("whdload_custom2"), &p->whdbootprefs.custom2, 1) - || cfgfile_intval(option, value, _T("whdload_custom3"), &p->whdbootprefs.custom3, 1) - || cfgfile_intval(option, value, _T("whdload_custom4"), &p->whdbootprefs.custom4, 1) - || cfgfile_intval(option, value, _T("whdload_custom5"), &p->whdbootprefs.custom5, 1) - || cfgfile_yesno(option, value, _T("whdload_buttonwait"), &p->whdbootprefs.buttonwait) - || cfgfile_yesno(option, value, _T("whdload_showsplash"), &p->whdbootprefs.showsplash) - || cfgfile_intval(option, value, _T("whdload_configdelay"), &p->whdbootprefs.configdelay, 1) - || cfgfile_yesno(option, value, _T("whdload_writecache"), &p->whdbootprefs.writecache) - || cfgfile_yesno(option, value, _T("whdload_quit_on_exit"), &p->whdbootprefs.quit_on_exit) + if (cfgfile_string(option, value, _T("whdload_slave"), whdload_prefs.selected_slave.filename) + || cfgfile_intval(option, value, _T("whdload_custom1"), &whdload_prefs.selected_slave.custom1.value, 1) + || cfgfile_intval(option, value, _T("whdload_custom2"), &whdload_prefs.selected_slave.custom2.value, 1) + || cfgfile_intval(option, value, _T("whdload_custom3"), &whdload_prefs.selected_slave.custom3.value, 1) + || cfgfile_intval(option, value, _T("whdload_custom4"), &whdload_prefs.selected_slave.custom4.value, 1) + || cfgfile_intval(option, value, _T("whdload_custom5"), &whdload_prefs.selected_slave.custom5.value, 1) + || cfgfile_string(option, value, _T("whdload_custom"), whdload_prefs.custom) + || cfgfile_yesno(option, value, _T("whdload_buttonwait"), &whdload_prefs.button_wait) + || cfgfile_yesno(option, value, _T("whdload_showsplash"), &whdload_prefs.show_splash) + || cfgfile_intval(option, value, _T("whdload_configdelay"), &whdload_prefs.config_delay, 1) + || cfgfile_yesno(option, value, _T("whdload_writecache"), &whdload_prefs.write_cache) + || cfgfile_yesno(option, value, _T("whdload_quit_on_exit"), &whdload_prefs.quit_on_exit) ) { return 1; @@ -8380,7 +8380,7 @@ void default_prefs (struct uae_prefs *p, bool reset, int type) p->sampler_freq = 0; #ifdef AMIBERRY p->sound_volume_cd = 20; - p->whdbootprefs.writecache = false; + whdload_prefs.write_cache = false; #endif p->comptrustbyte = 0; p->comptrustword = 0; diff --git a/src/include/fsdb.h b/src/include/fsdb.h index 70ed916f..5a8b73c4 100644 --- a/src/include/fsdb.h +++ b/src/include/fsdb.h @@ -161,10 +161,10 @@ extern unsigned int my_read (struct my_openfile_s*, void*, unsigned int); extern unsigned int my_write (struct my_openfile_s*, void*, unsigned int); extern int my_truncate (const TCHAR *name, uae_u64 len); extern int dos_errno (void); -extern int my_existslink(const char* name); -extern int my_existsfile (const TCHAR *name); -extern int my_existsfile2(const TCHAR* name); -extern int my_existsdir (const TCHAR *name); +extern bool my_existslink(const char* name); +extern bool my_existsfile (const TCHAR *name); +extern bool my_existsfile2(const TCHAR* name); +extern bool my_existsdir (const TCHAR *name); extern FILE *my_opentext (const TCHAR*); extern bool my_stat (const TCHAR *name, struct mystat *ms); @@ -187,7 +187,7 @@ extern bool my_createshortcut(const TCHAR *source, const TCHAR *target, const TC #define MYVOLUMEINFO_CDFS 16 extern int my_getvolumeinfo (const TCHAR *root); -extern const std::string my_get_sha1_of_file(const char* filepath); +extern std::string my_get_sha1_of_file(const char* filepath); #ifdef AMIBERRY char* fsdb_native_path(const char* root_dir, const char* amiga_path); diff --git a/src/include/options.h b/src/include/options.h index b2084d76..054d9de6 100644 --- a/src/include/options.h +++ b/src/include/options.h @@ -11,6 +11,8 @@ #define UAE_OPTIONS_H #include +#include + #include "uae/types.h" #include "traps.h" @@ -18,7 +20,7 @@ #define UAEMAJOR 5 #define UAEMINOR 6 -#define UAESUBREV 8 +#define UAESUBREV 9 #define MAX_AMIGADISPLAYS 1 @@ -533,20 +535,83 @@ struct monconfig }; #ifdef AMIBERRY -struct whdbooter +enum custom_type { - int custom1 = 0; - int custom2 = 0; - int custom3 = 0; - int custom4 = 0; - int custom5 = 0; - TCHAR custom[256]{}; - bool buttonwait{}; - TCHAR slave[4096]{}; - bool showsplash{}; - int configdelay = 0; - bool writecache{}; - bool quit_on_exit{}; + none, + bool_type, + bit_type, + list_type +}; +struct whdload_custom +{ + /** + * \brief The value of this custom field. This will be sent to WHDLoad + */ + int value; + /** + * \brief The type of the custom field: None, Boolean, Bit or List + */ + custom_type type; + /** + * \brief Caption for this custom field function. Used in the label which describes what this option does + */ + std::string caption; + /** + * \brief The list of labels to show in the dropdown. Used in List type custom fields + */ + std::vector labels; + /** + * \brief When the type is a Bit, it can contain multiple entries for the same custom field. + * Each entry has it's own label and a different value that goes with it. + */ + std::vector> label_bit_pairs; +}; +struct whdload_slave +{ + std::string filename; + std::string data_path; + whdload_custom custom1; + whdload_custom custom2; + whdload_custom custom3; + whdload_custom custom4; + whdload_custom custom5; + + whdload_custom& get_custom(const int index) + { + switch (index) + { + case 1: + return custom1; + case 2: + return custom2; + case 3: + return custom3; + case 4: + return custom4; + case 5: + return custom5; + default: + return custom1; + } + } +}; +struct whdload_options +{ + std::string filename; + std::string game_name; + std::string sub_path; + std::string variant_uuid; + int slave_count; + std::string slave_default; + bool slave_libraries; + std::vector slaves; + whdload_slave selected_slave; + std::string custom; + bool button_wait; + bool show_splash; + int config_delay; + bool write_cache; + bool quit_on_exit; }; #endif @@ -1032,11 +1097,12 @@ struct uae_prefs bool use_retroarch_statebuttons; bool use_retroarch_vkbd; - struct whdbooter whdbootprefs; - + #endif }; +extern whdload_options whdload_prefs; + extern int config_changed, config_changed_flags; extern void config_check_vsync(void); extern void set_config_changed(int flags = 0); @@ -1143,7 +1209,7 @@ extern bool cfgfile_createconfigstore(struct uae_prefs* p); extern void cfgfile_get_shader_config(struct uae_prefs* p, int rtg); #ifdef AMIBERRY -extern void whdload_auto_prefs(struct uae_prefs* prefs, char* filepath); +extern void whdload_auto_prefs(struct uae_prefs* prefs, const char* filepath); extern void cd_auto_prefs(struct uae_prefs* prefs, char* filepath); extern void symlink_roms(struct uae_prefs* prefs); extern void drawbridge_update_profiles(struct uae_prefs* prefs); diff --git a/src/main.cpp b/src/main.cpp index 6056e429..78422ba2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,7 +74,7 @@ #include "fsdb_host.h" #include "keyboard.h" -static const char __ver[40] = "$VER: Amiberry 5.6.9 (2024-3-09)"; +static const char __ver[40] = "$VER: Amiberry 5.6.9 (2024-03-17)"; long int version = 256 * 65536L * UAEMAJOR + 65536L * UAEMINOR + UAESUBREV; struct uae_prefs currprefs, changed_prefs; diff --git a/src/osdep/amiberry.cpp b/src/osdep/amiberry.cpp index 504338ad..29016e0a 100644 --- a/src/osdep/amiberry.cpp +++ b/src/osdep/amiberry.cpp @@ -110,6 +110,7 @@ bool host_poweroff = false; int relativepaths = 0; int saveimageoriginalpath = 0; +struct whdload_options whdload_prefs = {}; struct amiberry_options amiberry_options = {}; struct amiberry_gui_theme gui_theme = {}; amiberry_hotkey enter_gui_key; @@ -1502,6 +1503,9 @@ void process_event(SDL_Event event) case SDL_MOUSEMOTION: { + if (event.window.windowID != SDL_GetWindowID(mon->sdl_window)) + return; + // This will be useful when/if SDL2 supports more than 1 mouse. // Currently, it always returns 0. auto wm = event.motion.which; @@ -2211,11 +2215,11 @@ void target_default_options(struct uae_prefs* p, int type) p->use_retroarch_reset = amiberry_options.default_retroarch_reset; p->use_retroarch_vkbd = amiberry_options.default_retroarch_vkbd; - p->whdbootprefs.buttonwait = amiberry_options.default_whd_buttonwait; - p->whdbootprefs.showsplash = amiberry_options.default_whd_showsplash; - p->whdbootprefs.configdelay = amiberry_options.default_whd_configdelay; - p->whdbootprefs.writecache = amiberry_options.default_whd_writecache; - p->whdbootprefs.quit_on_exit = amiberry_options.default_whd_quit_on_exit; + whdload_prefs.button_wait = amiberry_options.default_whd_buttonwait; + whdload_prefs.show_splash = amiberry_options.default_whd_showsplash; + whdload_prefs.config_delay = amiberry_options.default_whd_configdelay; + whdload_prefs.write_cache = amiberry_options.default_whd_writecache; + whdload_prefs.quit_on_exit = amiberry_options.default_whd_quit_on_exit; if (amiberry_options.default_soundcard > 0) p->soundcard = amiberry_options.default_soundcard; @@ -2975,7 +2979,7 @@ int target_cfgfile_load(struct uae_prefs* p, const char* filename, int type, int p->floppyslots[i].df[0] = 0; disk_insert(i, p->floppyslots[i].df); if (strlen(p->floppyslots[i].df) > 0) - AddFileToDiskList(p->floppyslots[i].df, 1); + add_file_to_mru_list(lstMRUDiskList, std::string(p->floppyslots[i].df)); } if (!isdefault) @@ -3023,6 +3027,12 @@ void extract_filename(const char* str, char* buffer) strncpy(buffer, p, MAX_DPATH - 1); } +std::string extract_filename(const std::string& path) +{ + const std::filesystem::path file_path(path); + return file_path.filename().string(); +} + void extract_path(char* str, char* buffer) { strncpy(buffer, str, MAX_DPATH - 1); @@ -3051,7 +3061,7 @@ void remove_file_extension(char* filename) std::string remove_file_extension(const std::string& filename) { - const size_t last_dot = filename.find_last_of("."); + const size_t last_dot = filename.find_last_of('.'); if (last_dot == std::string::npos) { // No extension found, return the original filename return filename; @@ -3059,50 +3069,43 @@ std::string remove_file_extension(const std::string& filename) return filename.substr(0, last_dot); } -void read_directory(const std::string path, std::vector* dirs, std::vector* files) +void read_directory(const std::string& path, std::vector* dirs, std::vector* files) { - struct dirent* dent; - if (dirs != nullptr) dirs->clear(); if (files != nullptr) files->clear(); - auto* const dir = opendir(path.c_str()); - if (dir != nullptr) + // check if the path exists first + if (!std::filesystem::exists(path)) + return; + for (const auto& entry : std::filesystem::directory_iterator(path)) { - while ((dent = readdir(dir)) != nullptr) + if (entry.is_directory()) { - if (dent->d_type == DT_DIR) - { - if (dirs != nullptr - && (dent->d_name[0] != '.' || (dent->d_name[0] == '.' && dent->d_name[1] == '.'))) - dirs->emplace_back(dent->d_name); - } - else if (dent->d_type == DT_LNK) - { - struct stat stbuf{}; - stat(dent->d_name, &stbuf); - if (S_ISDIR(stbuf.st_mode)) - { - if (dirs != nullptr - && (dent->d_name[0] != '.' || (dent->d_name[0] == '.' && dent->d_name[1] == '.'))) - dirs->emplace_back(dent->d_name); - } - else - { - if (files != nullptr && dent->d_name[0] != '.') - files->emplace_back(dent->d_name); - } - } - else if (files != nullptr && dent->d_name[0] != '.') - files->emplace_back(dent->d_name); + if (dirs != nullptr && (entry.path().filename().string()[0] != '.' || (entry.path().filename().string()[0] == '.' && entry.path().filename().string()[1] == '.'))) + dirs->emplace_back(entry.path().filename().string()); } - if (dirs != nullptr && !dirs->empty() && (*dirs)[0] == ".") - dirs->erase(dirs->begin()); - closedir(dir); + else if (entry.is_symlink()) + { + if (std::filesystem::is_directory(entry)) + { + if (dirs != nullptr && (entry.path().filename().string()[0] != '.' || (entry.path().filename().string()[0] == '.' && entry.path().filename().string()[1] == '.'))) + dirs->emplace_back(entry.path().filename().string()); + } + else + { + if (files != nullptr && entry.path().filename().string()[0] != '.') + files->emplace_back(entry.path().filename().string()); + } + } + else if (files != nullptr && entry.path().filename().string()[0] != '.') + files->emplace_back(entry.path().filename().string()); } + if (dirs != nullptr && !dirs->empty() && (*dirs)[0] == ".") + dirs->erase(dirs->begin()); + if (dirs != nullptr) sort(dirs->begin(), dirs->end()); if (files != nullptr) @@ -3117,300 +3120,222 @@ void save_amiberry_settings(void) char buffer[MAX_DPATH]; + auto write_bool_option = [&](const char* name, bool value) { + snprintf(buffer, MAX_DPATH, "%s=%s\n", name, value ? "yes" : "no"); + fputs(buffer, f); + }; + + auto write_int_option = [&](const char* name, int value) { + snprintf(buffer, MAX_DPATH, "%s=%d\n", name, value); + fputs(buffer, f); + }; + + auto write_string_option = [&](const char* name, const std::string& value) { + snprintf(buffer, MAX_DPATH, "%s=%s\n", name, value.c_str()); + fputs(buffer, f); + }; + // Should the Quickstart Panel be the default when opening the GUI? - snprintf(buffer, MAX_DPATH, "Quickstart=%d\n", amiberry_options.quickstart_start); - fputs(buffer, f); + write_int_option("Quickstart", amiberry_options.quickstart_start); // Open each config file and read the Description field? // This will slow down scanning the config list if it's very large - snprintf(buffer, MAX_DPATH, "read_config_descriptions=%s\n", amiberry_options.read_config_descriptions ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("read_config_descriptions", amiberry_options.read_config_descriptions); // Write to logfile? // If enabled, a file named "amiberry_log.txt" will be generated in the startup folder - snprintf(buffer, MAX_DPATH, "write_logfile=%s\n", amiberry_options.write_logfile ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("write_logfile", amiberry_options.write_logfile); // Scanlines ON by default? // This will only be enabled if the vertical height is enough, as we need Line Doubling set to ON also // Beware this comes with a performance hit, as double the amount of lines need to be drawn on-screen - snprintf(buffer, MAX_DPATH, "default_line_mode=%d\n", amiberry_options.default_line_mode); - fputs(buffer, f); + write_int_option("default_line_mode", amiberry_options.default_line_mode); // Map RCtrl key to RAmiga key? // This helps with keyboards that may not have 2 Win keys and no Menu key either - snprintf(buffer, MAX_DPATH, "rctrl_as_ramiga=%s\n", amiberry_options.rctrl_as_ramiga ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("rctrl_as_ramiga", amiberry_options.rctrl_as_ramiga); // Disable controller in the GUI? // If you want to disable the default behavior for some reason - snprintf(buffer, MAX_DPATH, "gui_joystick_control=%s\n", amiberry_options.gui_joystick_control ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("gui_joystick_control", amiberry_options.gui_joystick_control); // Use a separate thread for drawing native chipset output // This helps with performance, but may cause glitches in some cases - snprintf(buffer, MAX_DPATH, "default_multithreaded_drawing=%s\n", amiberry_options.default_multithreaded_drawing ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_multithreaded_drawing", amiberry_options.default_multithreaded_drawing); // Default mouse input speed - snprintf(buffer, MAX_DPATH, "input_default_mouse_speed=%d\n", amiberry_options.input_default_mouse_speed); - fputs(buffer, f); + write_int_option("input_default_mouse_speed", amiberry_options.input_default_mouse_speed); // When using Keyboard as Joystick, stop any double keypresses - snprintf(buffer, MAX_DPATH, "input_keyboard_as_joystick_stop_keypresses=%s\n", amiberry_options.input_keyboard_as_joystick_stop_keypresses ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("input_keyboard_as_joystick_stop_keypresses", amiberry_options.input_keyboard_as_joystick_stop_keypresses); // Default key for opening the GUI (e.g. "F12") - snprintf(buffer, MAX_DPATH, "default_open_gui_key=%s\n", amiberry_options.default_open_gui_key); - fputs(buffer, f); + write_string_option("default_open_gui_key", amiberry_options.default_open_gui_key); // Default key for Quitting the emulator - snprintf(buffer, MAX_DPATH, "default_quit_key=%s\n", amiberry_options.default_quit_key); - fputs(buffer, f); + write_string_option("default_quit_key", amiberry_options.default_quit_key); // Default key for opening Action Replay - snprintf(buffer, MAX_DPATH, "default_ar_key=%s\n", amiberry_options.default_ar_key); - fputs(buffer, f); + write_string_option("default_ar_key", amiberry_options.default_ar_key); // Default key for Fullscreen Toggle - snprintf(buffer, MAX_DPATH, "default_fullscreen_toggle_key=%s\n", amiberry_options.default_fullscreen_toggle_key); - fputs(buffer, f); + write_string_option("default_fullscreen_toggle_key", amiberry_options.default_fullscreen_toggle_key); // Rotation angle of the output display (useful for screens with portrait orientation, like the Go Advance) - snprintf(buffer, MAX_DPATH, "rotation_angle=%d\n", amiberry_options.rotation_angle); - fputs(buffer, f); - + write_int_option("rotation_angle", amiberry_options.rotation_angle); + // Enable Horizontal Centering by default? - snprintf(buffer, MAX_DPATH, "default_horizontal_centering=%s\n", amiberry_options.default_horizontal_centering ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_horizontal_centering", amiberry_options.default_horizontal_centering); // Enable Vertical Centering by default? - snprintf(buffer, MAX_DPATH, "default_vertical_centering=%s\n", amiberry_options.default_vertical_centering ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_vertical_centering", amiberry_options.default_vertical_centering); // Scaling method to use by default? // Valid options are: -1 Auto, 0 Nearest Neighbor, 1 Linear - snprintf(buffer, MAX_DPATH, "default_scaling_method=%d\n", amiberry_options.default_scaling_method); - fputs(buffer, f); + write_int_option("default_scaling_method", amiberry_options.default_scaling_method); // Enable frameskip by default? - snprintf(buffer, MAX_DPATH, "default_frameskip=%s\n", amiberry_options.default_frameskip ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_frameskip", amiberry_options.default_frameskip); // Correct Aspect Ratio by default? - snprintf(buffer, MAX_DPATH, "default_correct_aspect_ratio=%s\n", amiberry_options.default_correct_aspect_ratio ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_correct_aspect_ratio", amiberry_options.default_correct_aspect_ratio); - // Enable Auto-Height by default? - snprintf(buffer, MAX_DPATH, "default_auto_crop=%s\n", amiberry_options.default_auto_crop ? "yes" : "no"); - fputs(buffer, f); + // Enable Auto-Crop by default? + write_bool_option("default_auto_crop", amiberry_options.default_auto_crop); // Default Screen Width - snprintf(buffer, MAX_DPATH, "default_width=%d\n", amiberry_options.default_width); - fputs(buffer, f); + write_int_option("default_width", amiberry_options.default_width); // Default Screen Height - snprintf(buffer, MAX_DPATH, "default_height=%d\n", amiberry_options.default_height); - fputs(buffer, f); + write_int_option("default_height", amiberry_options.default_height); // Full screen mode (0, 1, 2) - snprintf(buffer, MAX_DPATH, "default_fullscreen_mode=%d\n", amiberry_options.default_fullscreen_mode); - fputs(buffer, f); + write_int_option("default_fullscreen_mode", amiberry_options.default_fullscreen_mode); // Default Stereo Separation - snprintf(buffer, MAX_DPATH, "default_stereo_separation=%d\n", amiberry_options.default_stereo_separation); - fputs(buffer, f); + write_int_option("default_stereo_separation", amiberry_options.default_stereo_separation); // Default Sound buffer size - snprintf(buffer, MAX_DPATH, "default_sound_buffer=%d\n", amiberry_options.default_sound_buffer); - fputs(buffer, f); + write_int_option("default_sound_buffer", amiberry_options.default_sound_buffer); // Default Sound Mode (Pull/Push) - snprintf(buffer, MAX_DPATH, "default_sound_pull=%s\n", amiberry_options.default_sound_pull ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_sound_pull", amiberry_options.default_sound_pull); // Default Joystick Deadzone - snprintf(buffer, MAX_DPATH, "default_joystick_deadzone=%d\n", amiberry_options.default_joystick_deadzone); - fputs(buffer, f); + write_int_option("default_joystick_deadzone", amiberry_options.default_joystick_deadzone); // Enable RetroArch Quit by default? - snprintf(buffer, MAX_DPATH, "default_retroarch_quit=%s\n", amiberry_options.default_retroarch_quit ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_retroarch_quit", amiberry_options.default_retroarch_quit); // Enable RetroArch Menu by default? - snprintf(buffer, MAX_DPATH, "default_retroarch_menu=%s\n", amiberry_options.default_retroarch_menu ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_retroarch_menu", amiberry_options.default_retroarch_menu); // Enable RetroArch Reset by default? - snprintf(buffer, MAX_DPATH, "default_retroarch_reset=%s\n", amiberry_options.default_retroarch_reset ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_retroarch_reset", amiberry_options.default_retroarch_reset); - // Enable RetroArch Reset by default? - snprintf(buffer, MAX_DPATH, "default_retroarch_vkbd=%s\n", amiberry_options.default_retroarch_vkbd ? "yes" : "no"); - fputs(buffer, f); + // Enable RetroArch VKBD by default? + write_bool_option("default_retroarch_vkbd", amiberry_options.default_retroarch_vkbd); // Controller1 - snprintf(buffer, MAX_DPATH, "default_controller1=%s\n", amiberry_options.default_controller1); - fputs(buffer, f); + write_string_option("default_controller1", amiberry_options.default_controller1); // Controller2 - snprintf(buffer, MAX_DPATH, "default_controller2=%s\n", amiberry_options.default_controller2); - fputs(buffer, f); + write_string_option("default_controller2", amiberry_options.default_controller2); // Controller3 - snprintf(buffer, MAX_DPATH, "default_controller3=%s\n", amiberry_options.default_controller3); - fputs(buffer, f); + write_string_option("default_controller3", amiberry_options.default_controller3); // Controller4 - snprintf(buffer, MAX_DPATH, "default_controller4=%s\n", amiberry_options.default_controller4); - fputs(buffer, f); + write_string_option("default_controller4", amiberry_options.default_controller4); // Mouse1 - snprintf(buffer, MAX_DPATH, "default_mouse1=%s\n", amiberry_options.default_mouse1); - fputs(buffer, f); + write_string_option("default_mouse1", amiberry_options.default_mouse1); // Mouse2 - snprintf(buffer, MAX_DPATH, "default_mouse2=%s\n", amiberry_options.default_mouse2); - fputs(buffer, f); + write_string_option("default_mouse2", amiberry_options.default_mouse2); // WHDLoad ButtonWait - snprintf(buffer, MAX_DPATH, "default_whd_buttonwait=%s\n", amiberry_options.default_whd_buttonwait ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_whd_buttonwait", amiberry_options.default_whd_buttonwait); // WHDLoad Show Splash screen - snprintf(buffer, MAX_DPATH, "default_whd_showsplash=%s\n", amiberry_options.default_whd_showsplash ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_whd_showsplash", amiberry_options.default_whd_showsplash); // WHDLoad Config Delay - snprintf(buffer, MAX_DPATH, "default_whd_configdelay=%d\n", amiberry_options.default_whd_configdelay); - fputs(buffer, f); + write_int_option("default_whd_configdelay", amiberry_options.default_whd_configdelay); // WHDLoad WriteCache - snprintf(buffer, MAX_DPATH, "default_whd_writecache=%s\n", amiberry_options.default_whd_writecache ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_whd_writecache", amiberry_options.default_whd_writecache); // WHDLoad Quit emulator after game exits - snprintf(buffer, MAX_DPATH, "default_whd_quit_on_exit=%s\n", amiberry_options.default_whd_quit_on_exit ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_whd_quit_on_exit", amiberry_options.default_whd_quit_on_exit); // Disable Shutdown button in GUI - snprintf(buffer, MAX_DPATH, "disable_shutdown_button=%s\n", amiberry_options.disable_shutdown_button ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("disable_shutdown_button", amiberry_options.disable_shutdown_button); // Allow Display settings to be used from the WHDLoad XML (override amiberry.conf defaults) - snprintf(buffer, MAX_DPATH, "allow_display_settings_from_xml=%s\n", amiberry_options.allow_display_settings_from_xml ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("allow_display_settings_from_xml", amiberry_options.allow_display_settings_from_xml); // Default Sound Card (0=default, first one available in the system) - snprintf(buffer, MAX_DPATH, "default_soundcard=%d\n", amiberry_options.default_soundcard); - fputs(buffer, f); - + write_int_option("default_soundcard", amiberry_options.default_soundcard); + // Enable Virtual Keyboard by default - snprintf(buffer, MAX_DPATH, "default_vkbd_enabled=%s\n", amiberry_options.default_vkbd_enabled ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_vkbd_enabled", amiberry_options.default_vkbd_enabled); // Show the High-res version of the Virtual Keyboard by default - snprintf(buffer, MAX_DPATH, "default_vkbd_hires=%s\n", amiberry_options.default_vkbd_hires ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_vkbd_hires", amiberry_options.default_vkbd_hires); // Enable Quit functionality through Virtual Keyboard by default - snprintf(buffer, MAX_DPATH, "default_vkbd_exit=%s\n", amiberry_options.default_vkbd_exit ? "yes" : "no"); - fputs(buffer, f); + write_bool_option("default_vkbd_exit", amiberry_options.default_vkbd_exit); // Default Language for the Virtual Keyboard - snprintf(buffer, MAX_DPATH, "default_vkbd_language=%s\n", amiberry_options.default_vkbd_language); - fputs(buffer, f); + write_string_option("default_vkbd_language", amiberry_options.default_vkbd_language); // Default Style for the Virtual Keyboard - snprintf(buffer, MAX_DPATH, "default_vkbd_style=%s\n", amiberry_options.default_vkbd_style); - fputs(buffer, f); + write_string_option("default_vkbd_style", amiberry_options.default_vkbd_style); // Default transparency for the Virtual Keyboard - snprintf(buffer, MAX_DPATH, "default_vkbd_transparency=%d\n", amiberry_options.default_vkbd_transparency); - fputs(buffer, f); + write_int_option("default_vkbd_transparency", amiberry_options.default_vkbd_transparency); // Default controller button for toggling the Virtual Keyboard - snprintf(buffer, MAX_DPATH, "default_vkbd_toggle=%s\n", amiberry_options.default_vkbd_toggle); - fputs(buffer, f); + write_string_option("default_vkbd_toggle", amiberry_options.default_vkbd_toggle); // GUI Theme: Font name - snprintf(buffer, MAX_DPATH, "gui_theme_font_name=%s\n", amiberry_options.gui_theme_font_name); - fputs(buffer, f); + write_string_option("gui_theme_font_name", amiberry_options.gui_theme_font_name); // GUI Theme: Font size - snprintf(buffer, MAX_DPATH, "gui_theme_font_size=%d\n", amiberry_options.gui_theme_font_size); - fputs(buffer, f); + write_int_option("gui_theme_font_size", amiberry_options.gui_theme_font_size); // GUI Theme: Base color - snprintf(buffer, MAX_DPATH, "gui_theme_base_color=%s\n", amiberry_options.gui_theme_base_color); - fputs(buffer, f); + write_string_option("gui_theme_base_color", amiberry_options.gui_theme_base_color); // GUI Theme: Selector Inactive color - snprintf(buffer, MAX_DPATH, "gui_theme_selector_inactive=%s\n", amiberry_options.gui_theme_selector_inactive); - fputs(buffer, f); + write_string_option("gui_theme_selector_inactive", amiberry_options.gui_theme_selector_inactive); // GUI Theme: Selector Active color - snprintf(buffer, MAX_DPATH, "gui_theme_selector_active=%s\n", amiberry_options.gui_theme_selector_active); - fputs(buffer, f); + write_string_option("gui_theme_selector_active", amiberry_options.gui_theme_selector_active); // GUI Theme: Textbox Background color - snprintf(buffer, MAX_DPATH, "gui_theme_textbox_background=%s\n", amiberry_options.gui_theme_textbox_background); - fputs(buffer, f); + write_string_option("gui_theme_textbox_background", amiberry_options.gui_theme_textbox_background); // Paths - snprintf(buffer, MAX_DPATH, "path=%s\n", current_dir.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "config_path=%s\n", config_path.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "controllers_path=%s\n", controllers_path.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "retroarch_config=%s\n", retroarch_file.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "whdboot_path=%s\n", whdboot_path.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "whdload_arch_path=%s\n", whdload_arch_path.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "logfile_path=%s\n", logfile_path.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "rom_path=%s\n", rom_path.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "rp9_path=%s\n", rp9_path.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "floppy_sounds_dir=%s\n", floppy_sounds_dir.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "data_dir=%s\n", data_dir.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "saveimage_dir=%s\n", saveimage_dir.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "savestate_dir=%s\n", savestate_dir.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "ripper_dir=%s\n", ripper_path.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "inputrecordings_dir=%s\n", input_dir.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "screenshot_dir=%s\n", screenshot_dir.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "nvram_dir=%s\n", nvram_dir.c_str()); - fputs(buffer, f); - - snprintf(buffer, MAX_DPATH, "video_dir=%s\n", video_dir.c_str()); - fputs(buffer, f); + write_string_option("path", current_dir); + write_string_option("config_path", config_path); + write_string_option("controllers_path", controllers_path); + write_string_option("retroarch_config", retroarch_file); + write_string_option("whdboot_path", whdboot_path); + write_string_option("whdload_arch_path", whdload_arch_path); + write_string_option("logfile_path", logfile_path); + write_string_option("rom_path", rom_path); + write_string_option("rp9_path", rp9_path); + write_string_option("floppy_sounds_dir", floppy_sounds_dir); + write_string_option("data_dir", data_dir); + write_string_option("saveimage_dir", saveimage_dir); + write_string_option("savestate_dir", savestate_dir); + write_string_option("screenshot_dir", screenshot_dir); + write_string_option("ripper_path", ripper_path); + write_string_option("inputrecordings_dir", input_dir); + write_string_option("nvram_dir", nvram_dir); + write_string_option("video_dir", video_dir); // The number of ROMs in the last scan snprintf(buffer, MAX_DPATH, "ROMs=%zu\n", lstAvailableROMs.size()); @@ -3419,12 +3344,9 @@ void save_amiberry_settings(void) // The ROMs found in the last scan for (auto& lstAvailableROM : lstAvailableROMs) { - snprintf(buffer, MAX_DPATH, "ROMName=%s\n", lstAvailableROM->Name); - fputs(buffer, f); - snprintf(buffer, MAX_DPATH, "ROMPath=%s\n", lstAvailableROM->Path); - fputs(buffer, f); - snprintf(buffer, MAX_DPATH, "ROMType=%d\n", lstAvailableROM->ROMType); - fputs(buffer, f); + write_string_option("ROMName", lstAvailableROM->Name); + write_string_option("ROMPath", lstAvailableROM->Path); + write_int_option("ROMType", lstAvailableROM->ROMType); } // Recent disk entries (these are used in the dropdown controls) @@ -3432,8 +3354,7 @@ void save_amiberry_settings(void) fputs(buffer, f); for (auto& i : lstMRUDiskList) { - snprintf(buffer, MAX_DPATH, "Diskfile=%s\n", i.c_str()); - fputs(buffer, f); + write_string_option("Diskfile", i); } // Recent CD entries (these are used in the dropdown controls) @@ -3441,8 +3362,7 @@ void save_amiberry_settings(void) fputs(buffer, f); for (auto& i : lstMRUCDList) { - snprintf(buffer, MAX_DPATH, "CDfile=%s\n", i.c_str()); - fputs(buffer, f); + write_string_option("CDfile", i); } // Recent WHDLoad entries (these are used in the dropdown controls) @@ -3451,8 +3371,7 @@ void save_amiberry_settings(void) fputs(buffer, f); for (auto& i : lstMRUWhdloadList) { - snprintf(buffer, MAX_DPATH, "WHDLoadfile=%s\n", i.c_str()); - fputs(buffer, f); + write_string_option("WHDLoadfile", i); } fclose(f); @@ -3681,7 +3600,7 @@ static int get_env_dir( char * path, const char *path_template, const char *envn return ret; } -void init_macos_amiberry_folders(std::string macos_amiberry_directory) +void init_macos_amiberry_folders(const std::string& macos_amiberry_directory) { if (!my_existsdir(macos_amiberry_directory.c_str())) my_mkdir(macos_amiberry_directory.c_str()); @@ -4088,12 +4007,12 @@ int main(int argc, char* argv[]) sortdisplays(); enumerate_sound_devices(); for (int i = 0; i < MAX_SOUND_DEVICES && sound_devices[i]; i++) { - int type = sound_devices[i]->type; + const int type = sound_devices[i]->type; write_log(_T("%d:%s: %s\n"), i, type == SOUND_DEVICE_SDL2 ? _T("SDL2") : (type == SOUND_DEVICE_DS ? _T("DS") : (type == SOUND_DEVICE_AL ? _T("AL") : (type == SOUND_DEVICE_WASAPI ? _T("WA") : (type == SOUND_DEVICE_WASAPI_EXCLUSIVE ? _T("WX") : _T("PA"))))), sound_devices[i]->name); } write_log(_T("Enumerating recording devices:\n")); for (int i = 0; i < MAX_SOUND_DEVICES && record_devices[i]; i++) { - int type = record_devices[i]->type; + const int type = record_devices[i]->type; write_log(_T("%d:%s: %s\n"), i, type == SOUND_DEVICE_SDL2 ? _T("SDL2") : (type == SOUND_DEVICE_DS ? _T("DS") : (type == SOUND_DEVICE_AL ? _T("AL") : (type == SOUND_DEVICE_WASAPI ? _T("WA") : (type == SOUND_DEVICE_WASAPI_EXCLUSIVE ? _T("WX") : _T("PA"))))), record_devices[i]->name); } write_log(_T("done\n")); @@ -4220,7 +4139,7 @@ bool get_plugin_path(TCHAR* out, int len, const TCHAR* path) void drawbridge_update_profiles(uae_prefs* p) { #ifdef FLOPPYBRIDGE - unsigned int flags = (p->drawbridge_autocache ? 1 : 0) | (p->drawbridge_connected_drive_b & 1) << 1 | (p->drawbridge_serial_auto ? 4 : 0) | (p->drawbridge_smartspeed ? 8 : 0); + const unsigned int flags = (p->drawbridge_autocache ? 1 : 0) | (p->drawbridge_connected_drive_b & 1) << 1 | (p->drawbridge_serial_auto ? 4 : 0) | (p->drawbridge_smartspeed ? 8 : 0); const std::string profile_name_fast = "Fast"; const std::string profile_name_comp = "Compatible"; @@ -4269,3 +4188,17 @@ struct netdriverdata **target_ethernet_enumerate(void) net_enumerated = 1; return ndd; } + +void clear_whdload_prefs() +{ + whdload_prefs.filename.clear(); + whdload_prefs.game_name.clear(); + whdload_prefs.sub_path.clear(); + whdload_prefs.variant_uuid.clear(); + whdload_prefs.slave_count = 0; + whdload_prefs.slave_default.clear(); + whdload_prefs.slave_libraries = false; + whdload_prefs.slaves = {}; + whdload_prefs.selected_slave = {}; + whdload_prefs.custom.clear(); +} diff --git a/src/osdep/amiberry_filesys.cpp b/src/osdep/amiberry_filesys.cpp index 722a1769..a0eb33f4 100644 --- a/src/osdep/amiberry_filesys.cpp +++ b/src/osdep/amiberry_filesys.cpp @@ -18,6 +18,7 @@ #include #endif +#include #include #include "crc32.h" @@ -41,45 +42,44 @@ struct my_openfile_s { void utf8_to_latin1_string(std::string& input, std::string& output) { std::vector in_buf(input.begin(), input.end()); - char* src_ptr = &in_buf[0]; + char* src_ptr = in_buf.data(); size_t src_size = input.size(); std::vector buf(1024); std::string dst; - + auto* iconv_ = iconv_open("ISO-8859-1//TRANSLIT", "UTF-8"); - - while (0 < src_size) { - char* dst_ptr = &buf[0]; + + while (src_size > 0) { + char* dst_ptr = buf.data(); size_t dst_size = buf.size(); size_t res = ::iconv(iconv_, &src_ptr, &src_size, &dst_ptr, &dst_size); if (res == (size_t)-1) { - if (errno == E2BIG) { - // ignore this error - } - else { + if (errno != E2BIG) { // skip character ++src_ptr; --src_size; } } - dst.append(&buf[0], buf.size() - dst_size); + dst.append(buf.data(), buf.size() - dst_size); } - dst.swap(output); + output = std::move(dst); iconv_close(iconv_); } -std::string iso_8859_1_to_utf8(std::string& str) +std::string iso_8859_1_to_utf8(const std::string& str) { - string str_out; - for (auto it = str.begin(); it != str.end(); ++it) + std::string str_out; + str_out.reserve(str.size() * 2); // Reserve space to avoid reallocations + + for (const auto& ch : str) { - uint8_t ch = *it; - if (ch < 0x80) { - str_out.push_back(ch); + uint8_t byte = static_cast(ch); + if (byte < 0x80) { + str_out.push_back(byte); } else { - str_out.push_back(0xc0 | ch >> 6); - str_out.push_back(0x80 | (ch & 0x3f)); + str_out.push_back(0xc0 | byte >> 6); + str_out.push_back(0x80 | (byte & 0x3f)); } } return str_out; @@ -209,54 +209,50 @@ bool my_chmod(const TCHAR* name, uae_u32 mode) { // Note: only used to set or clear write protect on disk file - // get current state - struct stat st{}; - auto input = string(name); - auto output = iso_8859_1_to_utf8(input); + // Convert input to UTF-8 + auto output = iso_8859_1_to_utf8(string(name)); + + // Get current state + struct stat st {}; if (stat(output.c_str(), &st) == -1) { write_log("my_chmod: stat on file %s failed\n", output.c_str()); return false; } + // Prepare new mode + auto newmode = st.st_mode; if (mode & FILEFLAG_WRITE) - // set write permission - st.st_mode |= S_IWUSR; + newmode |= S_IWUSR; // set write permission else - // clear write permission - st.st_mode &= ~(S_IWUSR); - chmod(output.c_str(), st.st_mode); + newmode &= ~S_IWUSR; // clear write permission - stat(output.c_str(), &st); - auto newmode = 0; - if (st.st_mode & S_IRUSR) { - newmode |= FILEFLAG_READ; - } - if (st.st_mode & S_IWUSR) { - newmode |= FILEFLAG_WRITE; + // Change mode + if (chmod(output.c_str(), newmode) == -1) { + write_log("my_chmod: chmod on file %s failed\n", output.c_str()); + return false; } - return (mode == newmode); + return true; } bool my_stat(const TCHAR* name, struct mystat* statbuf) { - struct stat st{}; + if (!name || !statbuf) { + write_log("my_stat: null arguments provided\n"); + return false; + } + + struct stat st {}; + auto output = iso_8859_1_to_utf8(string(name)); - auto input = string(name); - auto output = iso_8859_1_to_utf8(input); if (stat(output.c_str(), &st) == -1) { write_log("my_stat: stat on file %s failed\n", output.c_str()); return false; } statbuf->size = st.st_size; - statbuf->mode = 0; - if (st.st_mode & S_IRUSR) { - statbuf->mode |= FILEFLAG_READ; - } - if (st.st_mode & S_IWUSR) { - statbuf->mode |= FILEFLAG_WRITE; - } + statbuf->mode = ((st.st_mode & S_IRUSR) ? FILEFLAG_READ : 0) | + ((st.st_mode & S_IWUSR) ? FILEFLAG_WRITE : 0); statbuf->mtime.tv_sec = st.st_mtime; statbuf->mtime.tv_usec = 0; @@ -265,29 +261,42 @@ bool my_stat(const TCHAR* name, struct mystat* statbuf) bool compare_nocase(const std::string& first, const std::string& second) { - unsigned int i = 0; - while ((i < first.length()) && (i < second.length())) + const auto len = std::min(first.length(), second.length()); + + for (unsigned int i = 0; i < len; ++i) { - if (tolower(first[i]) < tolower(second[i])) return true; - else if (tolower(first[i]) > tolower(second[i])) return false; - ++i; + const auto first_ch = std::tolower(first[i]); + const auto second_ch = std::tolower(second[i]); + + if (first_ch != second_ch) + return first_ch < second_ch; } - return (first.length() < second.length()); + + return first.length() < second.length(); } struct my_opendir_s* my_opendir(const TCHAR* name, const TCHAR* mask) { - auto* mod = xmalloc(struct my_opendir_s, 1); - if (!mod) - return NULL; - auto input = string(name); - auto output = iso_8859_1_to_utf8(input); - mod->dir = opendir(output.c_str()); - if (!mod->dir) { - write_log("my_opendir %s failed\n", name); - xfree(mod); + if (!name) { + write_log("my_opendir: null directory name provided\n"); return NULL; } + + auto* mod = new my_opendir_s; + if (!mod) { + write_log("my_opendir: memory allocation failed\n"); + return NULL; + } + + auto output = iso_8859_1_to_utf8(string(name)); + mod->dir = opendir(output.c_str()); + + if (!mod->dir) { + write_log("my_opendir: opendir on directory %s failed\n", name); + delete mod; + return NULL; + } + return mod; } @@ -305,99 +314,77 @@ void my_closedir(struct my_opendir_s* mod) int my_readdir(struct my_opendir_s* mod, TCHAR* name) { - if (!mod) + if (!mod || !name) return 0; - + + const set ignoreList = { "_UAEFSDB.___", "Thumbs.db", ".DS_Store", "UAEFS.ini" }; + while (true) { auto* entry = readdir(mod->dir); if (entry == NULL) return 0; - + auto* result = entry->d_name; const int len = strlen(result); - if (strcasecmp(result, "_UAEFSDB.___") == 0) { - continue; - } - if (strcasecmp(result, "Thumbs.db") == 0) { - continue; - } - if (strcasecmp(result, ".DS_Store") == 0) { - continue; - } - if (strcasecmp(result, "UAEFS.ini") == 0) { - continue; - } - if (len > 5 && strncmp(result + len - 5, ".uaem", 5) == 0) { - // ignore metadata / attribute files, obviously + if (ignoreList.find(result) != ignoreList.end() || + (len > 5 && strncmp(result + len - 5, ".uaem", 5) == 0)) { continue; } auto string_input = string(result); string string_output; utf8_to_latin1_string(string_input, string_output); - //write_log("Original: %s - Converted: %s\n", string_input.c_str(), string_output.c_str()); _tcscpy(name, string_output.c_str()); return 1; } } -int my_existslink(const char* name) +bool my_existslink(const char* name) { - struct stat st {}; - if (lstat(name, &st) == -1) - { - return 0; + if (!name) { + write_log("my_existslink: null file name provided\n"); + return false; } - if (S_ISLNK(st.st_mode)) - return 1; - return 0; + + struct stat st {}; + if (lstat(name, &st) == -1) { + write_log("my_existslink: lstat on file %s failed\n", name); + return false; + } + + return S_ISLNK(st.st_mode); } -int my_existsfile2(const char* name) +bool my_existsfile2(const char* name) { struct stat st {}; - if (lstat(name, &st) == -1) - { - return 0; - } - if (!S_ISDIR(st.st_mode)) - return 1; - return 0; + return lstat(name, &st) != -1 && !S_ISDIR(st.st_mode); } -int my_existsfile(const char* name) +bool my_existsfile(const char* name) { struct stat st {}; - auto input = string(name); - auto output = iso_8859_1_to_utf8(input); - if (lstat(output.c_str(), &st) == -1) - { - return 0; - } - if (!S_ISDIR(st.st_mode)) - return 1; - return 0; + auto output = iso_8859_1_to_utf8(string(name)); + return lstat(output.c_str(), &st) != -1 && !S_ISDIR(st.st_mode); } -int my_existsdir(const char* name) +bool my_existsdir(const char* name) { struct stat st {}; - auto input = string(name); - auto output = iso_8859_1_to_utf8(input); - if (lstat(output.c_str(), &st) == -1) - { - return 0; - } - if (S_ISDIR(st.st_mode)) - return 1; - return 0; + auto output = iso_8859_1_to_utf8(string(name)); + return lstat(output.c_str(), &st) != -1 && S_ISDIR(st.st_mode); } uae_s64 my_fsize(struct my_openfile_s* mos) { + if (mos == nullptr) { + write_log("my_fsize: null pointer provided\n"); + return -1; + } + struct stat st {}; if (fstat(mos->fd, &st) == -1) { write_log("my_fsize: fstat on file %s failed\n", mos->path); @@ -408,48 +395,67 @@ uae_s64 my_fsize(struct my_openfile_s* mos) int my_getvolumeinfo(const char* root) { + if (root == nullptr) { + write_log("my_getvolumeinfo: null pointer provided\n"); + return -1; + } + struct stat st {}; auto ret = 0; - if (lstat(root, &st) == -1) + if (lstat(root, &st) == -1) { + write_log("my_getvolumeinfo: lstat on file %s failed\n", root); return -1; - if (!S_ISDIR(st.st_mode)) + } + if (!S_ISDIR(st.st_mode)) { + write_log("my_getvolumeinfo: %s is not a directory\n", root); return -2; + } ret |= MYVOLUMEINFO_STREAMS; - + return ret; } bool fs_path_exists(const std::string& s) { - struct stat buffer{}; - auto input = string(s); - auto output = iso_8859_1_to_utf8(input); + if (s.empty()) { + write_log("fs_path_exists: empty string provided\n"); + return false; + } + + struct stat buffer {}; + auto output = iso_8859_1_to_utf8(s); return stat(output.c_str(), &buffer) == 0; } struct my_openfile_s* my_open(const TCHAR* name, int flags) { - struct my_openfile_s* mos; - - mos = xmalloc(struct my_openfile_s, 1); - if (!mos) + if (name == nullptr) { + write_log("my_open: null pointer provided\n"); return NULL; - auto input = string(name); - auto output = iso_8859_1_to_utf8(input); - if (flags & O_CREAT) - { + } + + auto* mos = xmalloc(struct my_openfile_s, 1); + if (!mos) { + write_log("my_open: memory allocation failed\n"); + return NULL; + } + + auto output = iso_8859_1_to_utf8(string(name)); + if (flags & O_CREAT) { mos->fd = open(output.c_str(), flags, 0660); } - else - { + else { mos->fd = open(output.c_str(), flags); } - if (!mos->fd) { + + if (mos->fd == -1) { + write_log("my_open: open on file %s failed\n", name); xfree(mos); - mos = NULL; + return NULL; } + return mos; } @@ -462,35 +468,63 @@ void my_close(struct my_openfile_s* mos) unsigned int my_read(struct my_openfile_s* mos, void* b, unsigned int size) { - const auto bytes_read = read(mos->fd, b, size); - if (bytes_read == -1) - { - write_log("WARNING: my_read failed (-1)\n"); + if (mos == nullptr) { + write_log("my_read: null file pointer provided\n"); return 0; } + + if (b == nullptr) { + write_log("my_read: null buffer pointer provided\n"); + return 0; + } + + const auto bytes_read = read(mos->fd, b, size); + if (bytes_read == -1) { + write_log("my_read: read on file %s failed with error %s\n", mos->path, strerror(errno)); + return 0; + } + else if (bytes_read < size) { + write_log("my_read: read on file %s returned less bytes than requested\n", mos->path); + } + return static_cast(bytes_read); } unsigned int my_write(struct my_openfile_s* mos, void* b, unsigned int size) { - const auto bytes_written = write(mos->fd, b, size); - if (bytes_written == -1) - { - write_log("WARNING: my_write failed (-1) fd=%d buffer=%p size=%d\n", - mos->fd, b, size); - write_log("errno %d\n", errno); - write_log(" mos %p -> h=%d\n", mos, mos->fd); + if (mos == nullptr) { + write_log("my_write: null file pointer provided\n"); return 0; } + + if (b == nullptr) { + write_log("my_write: null buffer pointer provided\n"); + return 0; + } + + const auto bytes_written = write(mos->fd, b, size); + if (bytes_written == -1) { + write_log("my_write: write on file %s failed with error %s\n", mos->path, strerror(errno)); + return 0; + } + else if (bytes_written < size) { + write_log("my_write: write on file %s wrote less bytes than requested\n", mos->path); + } + return static_cast(bytes_written); } int my_mkdir(const TCHAR* path) { - auto input = string(path); - auto output = iso_8859_1_to_utf8(input); + if (path == nullptr) { + write_log("my_mkdir: null pointer provided\n"); + return -1; + } + + auto output = iso_8859_1_to_utf8(string(path)); int error = mkdir(output.c_str(), 0755); if (error) { + write_log("my_mkdir: mkdir on path %s failed with error %s\n", path, strerror(errno)); return -1; } return 0; @@ -498,87 +532,192 @@ int my_mkdir(const TCHAR* path) int my_truncate(const TCHAR* name, uae_u64 len) { - int int_len = (int)len; - struct my_openfile_s* mos = my_open(name, O_WRONLY); - if (mos == NULL) { - write_log("WARNING: opening file for truncation failed\n"); + if (name == nullptr) { + write_log("my_truncate: null pointer provided\n"); return -1; } + + struct my_openfile_s* mos = my_open(name, O_WRONLY); + if (mos == NULL) { + write_log("my_truncate: opening file %s for truncation failed\n", name); + return -1; +} + + int result; #ifdef WINDOWS - int result = _chsize(mos->fd, int_len); + if (len > INT_MAX) { + write_log("my_truncate: length %llu is too large for _chsize\n", len); + result = -1; + } + else { + result = _chsize(mos->fd, static_cast(len)); + } #else - int result = ftruncate(mos->fd, int_len); + result = ftruncate(mos->fd, len); #endif + + if (result == -1) { + write_log("my_truncate: truncating file %s failed with error %s\n", name, strerror(errno)); + } + my_close(mos); return result; } static void remove_extra_file(const char* path, const char* name) { - auto* p = (TCHAR*)malloc(MAX_DPATH); - _tcscpy(p, path); - fix_trailing(p); - strcat(p, name); - - unlink(p); - xfree(p); + std::string p(path); + p = fix_trailing(p); + p += name; + unlink(p.c_str()); } -bool my_isfilehidden (const TCHAR *path) +bool my_isfilehidden(const char* path) { - return false; + if (path == nullptr) + { + return false; + } + + std::string filename(path); + return filename[0] == '.'; } -void my_setfilehidden (const TCHAR *path, bool hidden) +void my_setfilehidden(const TCHAR* path, bool hidden) { + if (path == nullptr) + { + return; + } + std::string filename(path); + +#ifdef _WIN32 + DWORD attrs = GetFileAttributes(path); + + if (hidden) + { + attrs |= FILE_ATTRIBUTE_HIDDEN; + } + else + { + attrs &= ~FILE_ATTRIBUTE_HIDDEN; + } + + SetFileAttributes(path, attrs); +#else + std::string newname; + + if (hidden) + { + if (filename[0] != '.') + { + newname = "." + filename; + rename(filename.c_str(), newname.c_str()); + } + } + else + { + if (filename[0] == '.') + { + newname = filename.substr(1); + rename(filename.c_str(), newname.c_str()); + } + } +#endif } int my_rmdir(const TCHAR* path) { + if (path == nullptr) { + write_log("my_rmdir: null directory path provided\n"); + return -1; + } + remove_extra_file(path, "Thumbs.db"); remove_extra_file(path, ".DS_Store"); errno = 0; - auto input = string(path); - auto output = iso_8859_1_to_utf8(input); + auto output = iso_8859_1_to_utf8(string(path)); int result = rmdir(output.c_str()); + if (result != 0) { + write_log("my_rmdir: rmdir on directory %s failed with error %d\n", path, errno); + } + return result; } int my_unlink(const TCHAR* path) { + if (path == nullptr) { + write_log("my_unlink: null file path provided\n"); + return -1; + } + errno = 0; - auto input = string(path); - auto output = iso_8859_1_to_utf8(input); + auto output = iso_8859_1_to_utf8(string(path)); int result = unlink(output.c_str()); + if (result != 0) { + write_log("my_unlink: unlink on file %s failed with error %d\n", path, errno); + } + return result; } int my_rename(const TCHAR* oldname, const TCHAR* newname) { - errno = 0; - auto old_input = string(oldname); - auto old_output = iso_8859_1_to_utf8(old_input); - auto new_input = string(newname); - auto new_output = iso_8859_1_to_utf8(new_input); + if (oldname == nullptr || newname == nullptr) { + write_log("my_rename: null file name provided\n"); + return -1; + } - return rename(old_output.c_str(), new_output.c_str()); + errno = 0; + auto old_output = iso_8859_1_to_utf8(string(oldname)); + auto new_output = iso_8859_1_to_utf8(string(newname)); + + int result = rename(old_output.c_str(), new_output.c_str()); + + if (result != 0) { + write_log("my_rename: rename from %s to %s failed with error %d\n", oldname, newname, errno); + } + + return result; } uae_s64 my_lseek(struct my_openfile_s* mos, uae_s64 offset, int whence) { + if (mos == nullptr) { + write_log("my_lseek: null file descriptor provided\n"); + return -1; + } + errno = 0; - return lseek(mos->fd, offset, whence); + auto result = lseek(mos->fd, offset, whence); + + if (result == -1) { + write_log("my_lseek: lseek on file %s failed with error %d\n", mos->path, errno); + } + + return result; } FILE* my_opentext(const TCHAR* name) { - auto input = string(name); - auto output = iso_8859_1_to_utf8(input); - return fopen(output.c_str(), "rb"); + if (name == nullptr) { + write_log("my_opentext: null file name provided\n"); + return nullptr; + } + + auto output = iso_8859_1_to_utf8(string(name)); + FILE* file = fopen(output.c_str(), "rb"); + + if (file == nullptr) { + write_log("my_opentext: fopen on file %s failed\n", name); + } + + return file; } bool my_createshortcut(const char* source, const char* target, const char* description) @@ -648,37 +787,55 @@ int host_errno_to_dos_errno(int err) void my_canonicalize_path(const TCHAR* path, TCHAR* out, int size) { - _tcsncpy(out, path, size); - out[size - 1] = 0; - return; + if (path == nullptr || out == nullptr) { + write_log("my_canonicalize_path: null arguments provided\n"); + return; + } + + _tcsncpy(out, path, size - 1); + out[size - 1] = '\0'; } bool my_issamepath(const TCHAR* path1, const TCHAR* path2) { + if (path1 == nullptr || path2 == nullptr) { + write_log("my_issamepath: null path provided\n"); + return false; + } + return _tcsicmp(path1, path2) == 0; } int my_issamevolume(const TCHAR* path1, const TCHAR* path2, TCHAR* path) { + if (path1 == nullptr || path2 == nullptr || path == nullptr) { + write_log("my_issamevolume: null arguments provided\n"); + return 0; + } + TCHAR p1[MAX_DPATH]; TCHAR p2[MAX_DPATH]; - unsigned int len, cnt; my_canonicalize_path(path1, p1, sizeof p1 / sizeof(TCHAR)); my_canonicalize_path(path2, p2, sizeof p2 / sizeof(TCHAR)); - len = _tcslen(p1); + + unsigned int len = _tcslen(p1); if (len > _tcslen(p2)) len = _tcslen(p2); + if (_tcsnicmp(p1, p2, len)) return 0; + _tcscpy(path, p2 + len); - cnt = 0; + + unsigned int cnt = 0; for (unsigned int i = 0; i < _tcslen(path); i++) { if (path[i] == '\\' || path[i] == '/') { path[i] = '/'; cnt++; } } + write_log(_T("'%s' (%s) matched with '%s' (%s), extra = '%s'\n"), path1, p1, path2, p2, path); return cnt; } @@ -709,7 +866,7 @@ int dos_errno(void) #endif default: - write_log(("Unimplemented error %s\n", strerror(e))); + write_log(_T("Unimplemented error %s\n"), strerror(e)); return ERROR_NOT_IMPLEMENTED; } } @@ -729,13 +886,13 @@ int target_get_volume_name(struct uaedev_mount_info* mtinf, struct uaedev_config // If replace is false, copyfile will fail if file already exists bool copyfile(const char* target, const char* source, const bool replace) { - #ifdef USE_OLDGCC +#ifdef USE_OLDGCC std::experimental::filesystem::copy_options options = {}; options = replace ? experimental::filesystem::copy_options::overwrite_existing : experimental::filesystem::copy_options::none; - #else +#else std::filesystem::copy_options options = {}; options = replace ? filesystem::copy_options::overwrite_existing : filesystem::copy_options::none; - #endif +#endif return copy_file(source, target, options); } @@ -744,32 +901,37 @@ void filesys_addexternals(void) // this would mount system drives on Windows } -const std::string my_get_sha1_of_file(const char* filepath) +std::string my_get_sha1_of_file(const char* filepath) { - void* mem; - struct stat sb; - int fd = -1; - int ret = 0; - int len; - - if ((fd = open((char*)filepath, O_RDONLY)) < 0) ret = (-3); - if (ret == 0 && fstat(fd, &sb) < 0) ret = (-2); - - if (ret == 0) { - if ((mem = mmap((caddr_t)0, (int)sb.st_size, - PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) != MAP_FAILED) { - len = (int)sb.st_size; - } - else ret = (-1); + if (filepath == nullptr) { + write_log("my_get_sha1_of_file: null file path provided\n"); + return ""; } - if (ret > (-3)) close(fd); + int fd = open(filepath, O_RDONLY); + if (fd < 0) { + write_log("my_get_sha1_of_file: open on file %s failed\n", filepath); + return ""; + } - if (ret < 0) return NULL; + struct stat sb; + if (fstat(fd, &sb) < 0) { + write_log("my_get_sha1_of_file: fstat on file %s failed\n", filepath); + close(fd); + return ""; + } - const TCHAR* sha1 = get_sha1_txt(mem, len); + void* mem = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (mem == MAP_FAILED) { + write_log("my_get_sha1_of_file: mmap on file %s failed\n", filepath); + close(fd); + return ""; + } - munmap((caddr_t)mem, (size_t)len); + const TCHAR* sha1 = get_sha1_txt(mem, sb.st_size); + + munmap(mem, sb.st_size); + close(fd); return string(sha1); } diff --git a/src/osdep/amiberry_gui.cpp b/src/osdep/amiberry_gui.cpp index 762364f1..ab687d90 100644 --- a/src/osdep/amiberry_gui.cpp +++ b/src/osdep/amiberry_gui.cpp @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include #include @@ -94,83 +97,25 @@ std::string get_full_path_from_disk_list(std::string element) return full_path; } -void AddFileToDiskList(const char *file, int moveToTop) +void add_file_to_mru_list(std::vector& vec, const std::string& file) { - unsigned int i; - - for (i = 0; i < lstMRUDiskList.size(); ++i) - { - if (!stricmp(lstMRUDiskList[i].c_str(), file)) - { - if (moveToTop) - { - lstMRUDiskList.erase(lstMRUDiskList.begin() + i); - lstMRUDiskList.insert(lstMRUDiskList.begin(), file); - } - break; - } + // Check if the string already exists in the vector + if (std::find(vec.begin(), vec.end(), file) == vec.end()) { + // The string does not exist in the vector, so add it at the first position + vec.insert(vec.begin(), file); } - if (i >= lstMRUDiskList.size()) - lstMRUDiskList.insert(lstMRUDiskList.begin(), file); - while (lstMRUDiskList.size() > MAX_MRU_DISKLIST) - lstMRUDiskList.pop_back(); -} - -void AddFileToCDList(const char *file, int moveToTop) -{ - unsigned int i; - - for (i = 0; i < lstMRUCDList.size(); ++i) - { - if (!stricmp(lstMRUCDList[i].c_str(), file)) - { - if (moveToTop) - { - lstMRUCDList.erase(lstMRUCDList.begin() + i); - lstMRUCDList.insert(lstMRUCDList.begin(), file); - } - break; - } - } - if (i >= lstMRUCDList.size()) - lstMRUCDList.insert(lstMRUCDList.begin(), file); - - while (lstMRUCDList.size() > MAX_MRU_CDLIST) - lstMRUCDList.pop_back(); -} - -void AddFileToWHDLoadList(const char* file, int moveToTop) -{ - unsigned int i; - - for (i = 0; i < lstMRUWhdloadList.size(); ++i) - { - if (!stricmp(lstMRUWhdloadList[i].c_str(), file)) - { - if (moveToTop) - { - lstMRUWhdloadList.erase(lstMRUWhdloadList.begin() + i); - lstMRUWhdloadList.insert(lstMRUWhdloadList.begin(), file); - } - break; - } - } - if (i >= lstMRUWhdloadList.size()) - lstMRUWhdloadList.insert(lstMRUWhdloadList.begin(), file); - - while (lstMRUWhdloadList.size() > MAX_MRU_WHDLOADLIST) - lstMRUWhdloadList.pop_back(); + while (vec.size() > MAX_MRU_LIST) + vec.pop_back(); } void ClearAvailableROMList() { - while (!lstAvailableROMs.empty()) + for (const auto* rom : lstAvailableROMs) { - auto* const tmp = lstAvailableROMs[0]; - lstAvailableROMs.erase(lstAvailableROMs.begin()); - delete tmp; + delete rom; } + lstAvailableROMs.clear(); } static void addrom(struct romdata* rd, const char* path) @@ -321,7 +266,7 @@ void RescanROMs() if (dir != "..") { std::string full_path = std::string(path).append(dir); - read_directory(full_path.c_str(), nullptr, &files); + read_directory(full_path, nullptr, &files); for (auto & file : files) { std::string tmp_path = full_path; @@ -346,12 +291,11 @@ void RescanROMs() static void ClearConfigFileList() { - while (!ConfigFilesList.empty()) + for (const auto* config : ConfigFilesList) { - auto* const tmp = ConfigFilesList[0]; - ConfigFilesList.erase(ConfigFilesList.begin()); - delete tmp; + delete config; } + ConfigFilesList.clear(); } void ReadConfigFileList(void) @@ -404,7 +348,7 @@ void ReadConfigFileList(void) ConfigFileInfo* SearchConfigInList(const char* name) { - for (auto & i : ConfigFilesList) + for (const auto & i : ConfigFilesList) { if (!SDL_strncasecmp(i->Name, name, MAX_DPATH)) return i; @@ -426,7 +370,7 @@ void disk_selection(const int drive, uae_prefs* prefs) { strncpy(prefs->floppyslots[drive].df, tmp.c_str(), MAX_DPATH); disk_insert(drive, tmp.c_str()); - AddFileToDiskList(tmp.c_str(), 1); + add_file_to_mru_list(lstMRUDiskList, tmp); current_dir = extract_path(tmp); } } @@ -543,68 +487,11 @@ int gui_update() remove_file_extension(savestate_fname); remove_file_extension(screenshot_filename); - switch(currentStateNum) - { - case 1: - strncat(savestate_fname,"-1.uss", MAX_DPATH - 1); - screenshot_filename.append("-1.png"); - break; - case 2: - strncat(savestate_fname,"-2.uss", MAX_DPATH - 1); - screenshot_filename.append("-2.png"); - break; - case 3: - strncat(savestate_fname,"-3.uss", MAX_DPATH - 1); - screenshot_filename.append("-3.png"); - break; - case 4: - strncat(savestate_fname, "-4.uss", MAX_DPATH - 1); - screenshot_filename.append("-4.png"); - break; - case 5: - strncat(savestate_fname, "-5.uss", MAX_DPATH - 1); - screenshot_filename.append("-5.png"); - break; - case 6: - strncat(savestate_fname, "-6.uss", MAX_DPATH - 1); - screenshot_filename.append("-6.png"); - break; - case 7: - strncat(savestate_fname, "-7.uss", MAX_DPATH - 1); - screenshot_filename.append("-7.png"); - break; - case 8: - strncat(savestate_fname, "-8.uss", MAX_DPATH - 1); - screenshot_filename.append("-8.png"); - break; - case 9: - strncat(savestate_fname, "-9.uss", MAX_DPATH - 1); - screenshot_filename.append("-9.png"); - break; - case 10: - strncat(savestate_fname, "-10.uss", MAX_DPATH - 1); - screenshot_filename.append("-10.png"); - break; - case 11: - strncat(savestate_fname, "-11.uss", MAX_DPATH - 1); - screenshot_filename.append("-11.png"); - break; - case 12: - strncat(savestate_fname, "-12.uss", MAX_DPATH - 1); - screenshot_filename.append("-12.png"); - break; - case 13: - strncat(savestate_fname, "-13.uss", MAX_DPATH - 1); - screenshot_filename.append("-13.png"); - break; - case 14: - strncat(savestate_fname, "-14.uss", MAX_DPATH - 1); - screenshot_filename.append("-14.png"); - break; - default: - strncat(savestate_fname,".uss", MAX_DPATH - 1); - screenshot_filename.append(".png"); - } + std::string suffix = (currentStateNum >= 1 && currentStateNum <= 14) ? + "-" + std::to_string(currentStateNum) : ""; + strncat(savestate_fname, (suffix + ".uss").c_str(), MAX_DPATH - 1); + screenshot_filename.append(suffix + ".png"); + return 0; } @@ -648,7 +535,7 @@ void gui_display(int shortcut) inputdevice_copyconfig(&changed_prefs, &currprefs); inputdevice_config_change_test(); clearallkeys (); - update_display(&changed_prefs); + if (resumepaused(7)) { inputdevice_acquire(TRUE); setmouseactive(0, 1); @@ -1125,30 +1012,30 @@ void DisplayDiskInfo(int num) snprintf(title, MAX_DPATH - 1, "Info for %s", nameonly); snprintf(linebuffer, sizeof(linebuffer) - 1, "Disk readable: %s", di.unreadable ? _T("No") : _T("Yes")); - infotext.push_back(linebuffer); + infotext.emplace_back(linebuffer); snprintf(linebuffer, sizeof(linebuffer) - 1, "Disk CRC32: %08X", di.imagecrc32); - infotext.push_back(linebuffer); + infotext.emplace_back(linebuffer); snprintf(linebuffer, sizeof(linebuffer) - 1, "Boot block CRC32: %08X", di.bootblockcrc32); - infotext.push_back(linebuffer); + infotext.emplace_back(linebuffer); snprintf(linebuffer, sizeof(linebuffer) - 1, "Boot block checksum valid: %s", di.bb_crc_valid ? _T("Yes") : _T("No")); - infotext.push_back(linebuffer); + infotext.emplace_back(linebuffer); snprintf(linebuffer, sizeof(linebuffer) - 1, "Boot block type: %s", di.bootblocktype == 0 ? _T("Custom") : (di.bootblocktype == 1 ? _T("Standard 1.x") : _T("Standard 2.x+"))); - infotext.push_back(linebuffer); + infotext.emplace_back(linebuffer); if (di.diskname[0]) { snprintf(linebuffer, sizeof(linebuffer) - 1, "Label: '%s'", di.diskname); - infotext.push_back(linebuffer); + infotext.emplace_back(linebuffer); } - infotext.push_back(""); + infotext.emplace_back(""); if (di.bootblockinfo[0]) { - infotext.push_back("Amiga Bootblock Reader database detected:"); + infotext.emplace_back("Amiga Bootblock Reader database detected:"); snprintf(linebuffer, sizeof(linebuffer) - 1, "Name: '%s'", di.bootblockinfo); - infotext.push_back(linebuffer); + infotext.emplace_back(linebuffer); if (di.bootblockclass[0]) { snprintf(linebuffer, sizeof(linebuffer) - 1, "Class: '%s'", di.bootblockclass); - infotext.push_back(linebuffer); + infotext.emplace_back(linebuffer); } - infotext.push_back(""); + infotext.emplace_back(""); } int w = 16; @@ -1163,13 +1050,13 @@ void DisplayDiskInfo(int num) } linebuffer[w * 3] = ' '; linebuffer[w * 3 + 1 + w] = 0; - infotext.push_back(linebuffer); + infotext.emplace_back(linebuffer); } ShowDiskInfo(title, infotext); } -void save_mapping_to_file(std::string mapping) +void save_mapping_to_file(const std::string& mapping) { std::string filename = get_controllers_path(); filename.append("gamecontrollerdb_user.txt"); @@ -1178,7 +1065,7 @@ void save_mapping_to_file(std::string mapping) file_output.open(filename, ios::app); if (file_output.is_open()) { - file_output << std::endl << mapping << std::endl; + file_output << '\n' << mapping << '\n'; file_output.close(); } } diff --git a/src/osdep/amiberry_input.cpp b/src/osdep/amiberry_input.cpp index 56b47d92..0d5047cd 100644 --- a/src/osdep/amiberry_input.cpp +++ b/src/osdep/amiberry_input.cpp @@ -1081,7 +1081,7 @@ static const TCHAR* get_joystick_friendlyname(const int joy) static const TCHAR* get_joystick_uniquename(const int joy) { TCHAR tmp[MAX_DPATH]; - _stprintf(tmp, _T("JOY%d"), joy); + _sntprintf(tmp, MAX_DPATH, _T("JOY%d"), joy); return my_strdup(tmp); } diff --git a/src/osdep/amiberry_whdbooter.cpp b/src/osdep/amiberry_whdbooter.cpp index 8428a651..982843a9 100644 --- a/src/osdep/amiberry_whdbooter.cpp +++ b/src/osdep/amiberry_whdbooter.cpp @@ -31,7 +31,7 @@ enum A1200_CONFIG = 2 // 8MB fast ram }; -struct game_options +struct game_hardware_options { std::string port0 = "nul"; std::string port1 = "nul"; @@ -71,12 +71,6 @@ TCHAR uae_config[255]; TCHAR whd_config[255]; TCHAR whd_startup[255]; -TCHAR game_name[MAX_DPATH]; -TCHAR selected_slave[MAX_DPATH]; -TCHAR sub_path[MAX_DPATH]; -TCHAR data_path[MAX_DPATH]; -TCHAR use_slave_libs = false; - static TCHAR* parse_text(const TCHAR* s) { if (*s == '"' || *s == '\'') @@ -147,11 +141,11 @@ void parse_cfg_line(uae_prefs* prefs, const std::string& line_string) xfree(line); } -void parse_custom_settings(uae_prefs* p, const char* settings) +void parse_custom_settings(uae_prefs* p, const std::string& settings) { const std::string lf = "\n"; const std::string check = "amiberry_custom"; - auto full_line = trim_full_line(string(settings)); + auto full_line = trim_full_line(settings); auto lf_found = full_line.find(lf); while (lf_found != std::string::npos) @@ -173,38 +167,38 @@ void parse_custom_settings(uae_prefs* p, const char* settings) } } -std::string find_whdload_game_option(const TCHAR* find_setting, const char* whd_options) +std::string find_whdload_game_option(const std::string& find_setting, const std::string& whd_options) { - return find_substring(string(find_setting), string(whd_options)); + return find_substring(find_setting, whd_options); } -game_options get_game_settings(const char* HW) +game_hardware_options get_game_hardware_settings(const std::string& hardware) { - game_options output_detail; - output_detail.port0 = find_whdload_game_option("PORT0", HW); - output_detail.port1 = find_whdload_game_option("PORT1", HW); - output_detail.control = find_whdload_game_option("PRIMARY_CONTROL", HW); - output_detail.control2 = find_whdload_game_option("SECONDARY_CONTROL", HW); - output_detail.cpu = find_whdload_game_option("CPU", HW); - output_detail.blitter = find_whdload_game_option("BLITTER", HW); - output_detail.clock = find_whdload_game_option("CLOCK", HW); - output_detail.chipset = find_whdload_game_option("CHIPSET", HW); - output_detail.jit = find_whdload_game_option("JIT", HW); - output_detail.cpu_24bit = find_whdload_game_option("CPU_24BITADDRESSING", HW); - output_detail.cpu_comp = find_whdload_game_option("CPU_COMPATIBLE", HW); - output_detail.sprites = find_whdload_game_option("SPRITES", HW); - output_detail.scr_height = find_whdload_game_option("SCREEN_HEIGHT", HW); - output_detail.scr_width = find_whdload_game_option("SCREEN_WIDTH", HW); - output_detail.scr_autoheight = find_whdload_game_option("SCREEN_AUTOHEIGHT", HW); - output_detail.scr_centerh = find_whdload_game_option("SCREEN_CENTERH", HW); - output_detail.scr_centerv = find_whdload_game_option("SCREEN_CENTERV", HW); - output_detail.scr_offseth = find_whdload_game_option("SCREEN_OFFSETH", HW); - output_detail.scr_offsetv = find_whdload_game_option("SCREEN_OFFSETV", HW); - output_detail.ntsc = find_whdload_game_option("NTSC", HW); - output_detail.fast = find_whdload_game_option("FAST_RAM", HW); - output_detail.z3 = find_whdload_game_option("Z3_RAM", HW); - output_detail.cpu_exact = find_whdload_game_option("CPU_EXACT", HW); - output_detail.fastcopper = find_whdload_game_option("FAST_COPPER", HW); + game_hardware_options output_detail; + output_detail.port0 = find_whdload_game_option("PORT0", hardware); + output_detail.port1 = find_whdload_game_option("PORT1", hardware); + output_detail.control = find_whdload_game_option("PRIMARY_CONTROL", hardware); + output_detail.control2 = find_whdload_game_option("SECONDARY_CONTROL", hardware); + output_detail.cpu = find_whdload_game_option("CPU", hardware); + output_detail.blitter = find_whdload_game_option("BLITTER", hardware); + output_detail.clock = find_whdload_game_option("CLOCK", hardware); + output_detail.chipset = find_whdload_game_option("CHIPSET", hardware); + output_detail.jit = find_whdload_game_option("JIT", hardware); + output_detail.cpu_24bit = find_whdload_game_option("CPU_24BITADDRESSING", hardware); + output_detail.cpu_comp = find_whdload_game_option("CPU_COMPATIBLE", hardware); + output_detail.sprites = find_whdload_game_option("SPRITES", hardware); + output_detail.scr_height = find_whdload_game_option("SCREEN_HEIGHT", hardware); + output_detail.scr_width = find_whdload_game_option("SCREEN_WIDTH", hardware); + output_detail.scr_autoheight = find_whdload_game_option("SCREEN_AUTOHEIGHT", hardware); + output_detail.scr_centerh = find_whdload_game_option("SCREEN_CENTERH", hardware); + output_detail.scr_centerv = find_whdload_game_option("SCREEN_CENTERV", hardware); + output_detail.scr_offseth = find_whdload_game_option("SCREEN_OFFSETH", hardware); + output_detail.scr_offsetv = find_whdload_game_option("SCREEN_OFFSETV", hardware); + output_detail.ntsc = find_whdload_game_option("NTSC", hardware); + output_detail.fast = find_whdload_game_option("FAST_RAM", hardware); + output_detail.z3 = find_whdload_game_option("Z3_RAM", hardware); + output_detail.cpu_exact = find_whdload_game_option("CPU_EXACT", hardware); + output_detail.fastcopper = find_whdload_game_option("FAST_COPPER", hardware); return output_detail; } @@ -289,11 +283,11 @@ void symlink_roms(struct uae_prefs* prefs) } } -void get_game_name(char* filepath) +std::string get_game_filename(const char* filepath) { extract_filename(filepath, last_loaded_config); - extract_filename(filepath, game_name); - remove_file_extension(game_name); + const std::string game_name = extract_filename(filepath); + return remove_file_extension(game_name); } void set_jport_modes(uae_prefs* prefs, const bool is_cd32) @@ -322,10 +316,10 @@ void clear_jports(uae_prefs* prefs) } } -void build_uae_config_filename() +void build_uae_config_filename(const std::string& game_name) { _tcscpy(uae_config, conf_path); - _tcscat(uae_config, game_name); + _tcscat(uae_config, game_name.c_str()); _tcscat(uae_config, ".uae"); } @@ -336,11 +330,11 @@ void cd_auto_prefs(uae_prefs* prefs, char* filepath) write_log("\nCD Autoload: %s \n\n", filepath); get_configuration_path(conf_path, MAX_DPATH); - get_game_name(filepath); + whdload_prefs.filename = get_game_filename(filepath); // LOAD GAME SPECIFICS FOR EXISTING .UAE - USE SHA1 IF AVAILABLE // CONFIG LOAD IF .UAE IS IN CONFIG PATH - build_uae_config_filename(); + build_uae_config_filename(whdload_prefs.filename); if (my_existsfile2(uae_config)) { @@ -374,11 +368,11 @@ void cd_auto_prefs(uae_prefs* prefs, char* filepath) } // enable CD - _stprintf(tmp, "cd32cd=1"); + _sntprintf(tmp, MAX_DPATH, "cd32cd=1"); cfgfile_parse_line(prefs, parse_text(tmp), 0); // mount the image - _stprintf(tmp, "cdimage0=%s,image", filepath); + _sntprintf(tmp, MAX_DPATH, "cdimage0=%s,image", filepath); cfgfile_parse_line(prefs, parse_text(tmp), 0); //APPLY THE SETTINGS FOR MOUSE/JOYSTICK ETC @@ -422,7 +416,7 @@ void cd_auto_prefs(uae_prefs* prefs, char* filepath) } } -void set_input_settings(uae_prefs* prefs, const game_options& game_detail, const bool is_cd32) +void set_input_settings(uae_prefs* prefs, const game_hardware_options& game_detail, const bool is_cd32) { // APPLY SPECIAL CONFIG E.G. MOUSE OR ALT. JOYSTICK SETTINGS clear_jports(prefs); @@ -503,7 +497,7 @@ void set_input_settings(uae_prefs* prefs, const game_options& game_detail, const } } -void parse_gfx_settings(uae_prefs* prefs, const game_options& game_detail) +void set_gfx_settings(uae_prefs* prefs, const game_hardware_options& game_detail) { std::string line_string; // SCREEN AUTO-HEIGHT @@ -680,7 +674,7 @@ void parse_gfx_settings(uae_prefs* prefs, const game_options& game_detail) } } -void set_compatibility_settings(uae_prefs* prefs, const game_options& game_detail, const bool a600_available, const bool use_aga) +void set_compatibility_settings(uae_prefs* prefs, const game_hardware_options& game_detail, const bool a600_available, const bool use_aga) { std::string line_string; // CPU 68020/040 or no A600 ROM available @@ -835,17 +829,170 @@ void set_compatibility_settings(uae_prefs* prefs, const game_options& game_detai // Screen settings, only if allowed to override the defaults from amiberry.conf if (amiberry_options.allow_display_settings_from_xml) { - parse_gfx_settings(prefs, game_detail); + set_gfx_settings(prefs, game_detail); } } -game_options parse_settings_from_xml(uae_prefs* prefs, const char* filepath) +void parse_slave_custom_fields(whdload_slave& slave, const std::string& custom) { - game_options game_detail{}; + std::istringstream stream(custom); + std::string line; + + while (std::getline(stream, line)) { + if (line.find("C1") != std::string::npos || line.find("C2") != std::string::npos || + line.find("C3") != std::string::npos || line.find("C4") != std::string::npos || + line.find("C5") != std::string::npos) { + + std::istringstream lineStream(line); + std::string segment; + std::vector seglist; + + while (std::getline(lineStream, segment, ':')) { + segment.erase(std::remove(segment.begin(), segment.end(), '\t'), segment.end()); + seglist.push_back(segment); + } + + // Process seglist as needed + if (seglist[0] == "C1") + { + if (seglist[1] == "B") + { + slave.custom1.type = bool_type; + slave.custom1.caption = seglist[2]; + slave.custom1.value = 0; + } + else if (seglist[1] == "X") + { + slave.custom1.type = bit_type; + slave.custom1.value = 0; + slave.custom1.label_bit_pairs.insert(slave.custom1.label_bit_pairs.end(), { seglist[2], stoi(seglist[3]) }); + } + else if (seglist[1] == "L") + { + slave.custom1.type = list_type; + slave.custom1.caption = seglist[2]; + slave.custom1.value = 0; + std::string token; + std::istringstream token_stream(seglist[3]); + while (std::getline(token_stream, token, ',')) { + slave.custom1.labels.push_back(token); + } + } + } + else if (seglist[0] == "C2") + { + if (seglist[1] == "B") + { + slave.custom2.type = bool_type; + slave.custom2.caption = seglist[2]; + slave.custom2.value = 0; + } + else if (seglist[1] == "X") + { + slave.custom2.type = bit_type; + slave.custom2.value = 0; + slave.custom2.label_bit_pairs.insert(slave.custom2.label_bit_pairs.end(), { seglist[2], stoi(seglist[3]) }); + } + else if (seglist[1] == "L") + { + slave.custom2.type = list_type; + slave.custom2.caption = seglist[2]; + slave.custom2.value = 0; + std::string token; + std::istringstream token_stream(seglist[3]); + while (std::getline(token_stream, token, ',')) { + slave.custom2.labels.push_back(token); + } + } + } + else if (seglist[0] == "C3") + { + if (seglist[1] == "B") + { + slave.custom3.type = bool_type; + slave.custom3.caption = seglist[2]; + slave.custom3.value = 0; + } + else if (seglist[1] == "X") + { + slave.custom3.type = bit_type; + slave.custom3.value = 0; + slave.custom3.label_bit_pairs.insert(slave.custom3.label_bit_pairs.end(), { seglist[2], stoi(seglist[3]) }); + } + else if (seglist[1] == "L") + { + slave.custom3.type = list_type; + slave.custom3.caption = seglist[2]; + slave.custom3.value = 0; + std::string token; + std::istringstream token_stream(seglist[3]); + while (std::getline(token_stream, token, ',')) { + slave.custom3.labels.push_back(token); + } + } + } + else if (seglist[0] == "C4") + { + if (seglist[1] == "B") + { + slave.custom4.type = bool_type; + slave.custom4.caption = seglist[2]; + slave.custom4.value = 0; + } + else if (seglist[1] == "X") + { + slave.custom4.type = bit_type; + slave.custom4.value = 0; + slave.custom4.label_bit_pairs.insert(slave.custom4.label_bit_pairs.end(), { seglist[2], stoi(seglist[3]) }); + } + else if (seglist[1] == "L") + { + slave.custom4.type = list_type; + slave.custom4.caption = seglist[2]; + slave.custom4.value = 0; + std::string token; + std::istringstream token_stream(seglist[3]); + while (std::getline(token_stream, token, ',')) { + slave.custom4.labels.push_back(token); + } + } + } + else if (seglist[0] == "C5") + { + if (seglist[1] == "B") + { + slave.custom5.type = bool_type; + slave.custom5.caption = seglist[2]; + slave.custom5.value = 0; + } + else if (seglist[1] == "X") + { + slave.custom5.type = bit_type; + slave.custom5.value = 0; + slave.custom5.label_bit_pairs.insert(slave.custom5.label_bit_pairs.end(), { seglist[2], stoi(seglist[3]) }); + } + else if (seglist[1] == "L") + { + slave.custom5.type = list_type; + slave.custom5.caption = seglist[2]; + slave.custom5.value = 0; + std::string token; + std::istringstream token_stream(seglist[3]); + while (std::getline(token_stream, token, ',')) { + slave.custom5.labels.push_back(token); + } + } + } + } + } +} + +game_hardware_options parse_settings_from_xml(uae_prefs* prefs, const char* filepath) +{ + game_hardware_options game_detail{}; tinyxml2::XMLDocument doc; auto error = false; - write_log("WHDBooter - Loading whdload_db.xml\n"); - write_log("WHDBooter - Searching whdload_db.xml for %s\n", game_name); + write_log(_T("WHDBooter - Searching whdload_db.xml for %s\n"), whdload_prefs.filename.c_str()); auto* f = fopen(whd_config, _T("rb")); if (f) @@ -875,85 +1022,117 @@ game_options parse_settings_from_xml(uae_prefs* prefs, const char* filepath) // then fall back to sha1 if a user has renamed the file! // int found = 0; - if (game_node->Attribute("filename", game_name)) - { - found = 1; - } - if (game_node->Attribute("sha1", sha1.c_str())) + if (game_node->Attribute("filename", whdload_prefs.filename.c_str()) || + game_node->Attribute("sha1", sha1.c_str())) { found = 1; } if (found) { - // now get the and items + // Name + auto xml_element = game_node->FirstChildElement("name"); + if (xml_element) + { + whdload_prefs.game_name.assign(xml_element->GetText()); + } + + // Sub Path + xml_element = game_node->FirstChildElement("subpath"); + if (xml_element) + { + whdload_prefs.sub_path.assign(xml_element->GetText()); + } + + // Variant UUID + xml_element = game_node->FirstChildElement("variant_uuid"); + if (xml_element) + { + whdload_prefs.variant_uuid.assign(xml_element->GetText()); + } + + // Slave count + xml_element = game_node->FirstChildElement("slave_count"); + if (xml_element) + { + whdload_prefs.slave_count = xml_element->IntText(0); + } + + // Default slave + xml_element = game_node->FirstChildElement("slave_default"); + if (xml_element) + { + whdload_prefs.slave_default.assign(xml_element->GetText()); + write_log("WHDBooter - Selected Slave: %s \n", whdload_prefs.slave_default.c_str()); + } + + // Slave_libraries + xml_element = game_node->FirstChildElement("slave_libraries"); + if (xml_element->GetText() != nullptr) + { + if (strcmpi(xml_element->GetText(), "true") == 0) + whdload_prefs.slave_libraries = true; + } + + // Get slaves and settings + xml_element = game_node->FirstChildElement("slave"); + whdload_prefs.slaves.clear(); + + for (int i = 0; i < whdload_prefs.slave_count && xml_element; ++i) + { + whdload_slave slave; + const char* slave_text = nullptr; + + slave_text = xml_element->FirstChildElement("filename")->GetText(); + if (slave_text) + slave.filename.assign(slave_text); + + slave_text = xml_element->FirstChildElement("datapath")->GetText(); + if (slave_text) + slave.data_path.assign(slave_text); + + auto customElement = xml_element->FirstChildElement("custom"); + if (customElement && ((slave_text = customElement->GetText()))) + { + auto custom = std::string(slave_text); + parse_slave_custom_fields(slave, custom); + } + + whdload_prefs.slaves.emplace_back(slave); + + // Set the default slave as the selected one + if (slave.filename == whdload_prefs.slave_default) + whdload_prefs.selected_slave = slave; + + xml_element = xml_element->NextSiblingElement("slave"); + } + // get hardware - const auto* temp_node = game_node->FirstChildElement("hardware"); - if (temp_node) + xml_element = game_node->FirstChildElement("hardware"); + if (xml_element) { - const auto* hardware = temp_node->GetText(); - if (hardware) + std::string hardware; + hardware.assign(xml_element->GetText()); + if (!hardware.empty()) { - game_detail = get_game_settings(hardware); - write_log("WHDBooter - Game H/W Settings: \n%s\n", hardware); + game_detail = get_game_hardware_settings(hardware); + write_log("WHDBooter - Game H/W Settings: \n%s\n", hardware.c_str()); } } - + // get custom controls - temp_node = game_node->FirstChildElement("custom_controls"); - if (temp_node) + xml_element = game_node->FirstChildElement("custom_controls"); + if (xml_element) { - const auto* custom_settings = temp_node->GetText(); - if (custom_settings) + std::string custom_settings; + custom_settings.assign(xml_element->GetText()); + if (!custom_settings.empty()) { - write_log("WHDBooter - Game Custom Settings: \n%s\n", custom_settings); parse_custom_settings(prefs, custom_settings); + write_log("WHDBooter - Game Custom Settings: \n%s\n", custom_settings.c_str()); } } - if (strlen(selected_slave) == 0) - { - temp_node = game_node->FirstChildElement("slave_default"); - - // use a selected slave if we have one - if (strlen(prefs->whdbootprefs.slave) != 0) - { - _tcscpy(selected_slave, prefs->whdbootprefs.slave); - write_log("WHDBooter - Config Selected Slave: %s \n", selected_slave); - } - // otherwise use the XML default - else if (temp_node->GetText() != nullptr) - { - _stprintf(selected_slave, "%s", temp_node->GetText()); - write_log("WHDBooter - Default Slave: %s\n", selected_slave); - } - - temp_node = game_node->FirstChildElement("subpath"); - - if (temp_node->GetText() != nullptr) - { - _stprintf(sub_path, "%s", temp_node->GetText()); - write_log("WHDBooter - SubPath: %s\n", sub_path); - } - } - - // get slave_libraries - temp_node = game_node->FirstChildElement("slave_libraries"); - if (temp_node->GetText() != nullptr) - { - if (strcmpi(temp_node->GetText(), "true") == 0) - use_slave_libs = true; - - write_log("WHDBooter - Libraries: %s\n", sub_path); - } - - // get data path - temp_node = game_node->FirstChildElement("slave")->FirstChildElement("datapath"); - if (temp_node && temp_node->GetText() != nullptr) - { - _stprintf(data_path, "%s", temp_node->GetText()); - write_log("WHDBooter - Data Path: %s\n", data_path); - } break; } game_node = game_node->NextSiblingElement(); @@ -962,12 +1141,12 @@ game_options parse_settings_from_xml(uae_prefs* prefs, const char* filepath) return game_detail; } -void create_startup_sequence(uae_prefs* prefs) +void create_startup_sequence() { std::ostringstream whd_bootscript; whd_bootscript << "FAILAT 999\n"; - if (use_slave_libs) + if (whdload_prefs.slave_libraries) { whd_bootscript << "DH3:C/Assign LIBS: DH3:LIBS/ ADD\n"; } @@ -976,74 +1155,57 @@ void create_startup_sequence(uae_prefs* prefs) whd_bootscript << "DH3:C/Assign C: DH3:C/ ADD\n"; whd_bootscript << "ENDIF\n"; - whd_bootscript << "CD \"Games:" << sub_path << "\"\n"; - whd_bootscript << "WHDLoad SLAVE=\"Games:" << sub_path << "/" << selected_slave << "\""; + whd_bootscript << "CD \"Games:" << whdload_prefs.sub_path << "\"\n"; + whd_bootscript << "WHDLoad SLAVE=\"Games:" << whdload_prefs.sub_path << "/" << whdload_prefs.selected_slave.filename << "\""; // Write Cache - if (prefs->whdbootprefs.writecache) + whd_bootscript << " PRELOAD NOREQ"; + if (!whdload_prefs.write_cache) { - whd_bootscript << " PRELOAD NOREQ"; - } - else - { - whd_bootscript << " PRELOAD NOREQ NOWRITECACHE"; + whd_bootscript << " NOWRITECACHE"; } // CUSTOM options - if (prefs->whdbootprefs.custom1 > 0) - { - whd_bootscript << " CUSTOM1=" << prefs->whdbootprefs.custom1; + for (int i = 1; i <= 5; ++i) { + auto& custom = whdload_prefs.selected_slave.get_custom(i); + if (custom.type != none && custom.value != 0) { + whd_bootscript << " CUSTOM" << i << "=" << custom.value; + } } - if (prefs->whdbootprefs.custom2 > 0) + if (!whdload_prefs.custom.empty()) { - whd_bootscript << " CUSTOM2=" << prefs->whdbootprefs.custom2; - } - if (prefs->whdbootprefs.custom3 > 0) - { - whd_bootscript << " CUSTOM3=" << prefs->whdbootprefs.custom3; - } - if (prefs->whdbootprefs.custom4 > 0) - { - whd_bootscript << " CUSTOM4=" << prefs->whdbootprefs.custom4; - } - if (prefs->whdbootprefs.custom5 > 0) - { - whd_bootscript << " CUSTOM5=" << prefs->whdbootprefs.custom5; - } - if (strlen(prefs->whdbootprefs.custom) != 0) - { - whd_bootscript << " CUSTOM=\"" << prefs->whdbootprefs.custom << "\""; + whd_bootscript << " CUSTOM=\"" << whdload_prefs.custom << "\""; } // BUTTONWAIT - if (prefs->whdbootprefs.buttonwait) + if (whdload_prefs.button_wait) { whd_bootscript << " BUTTONWAIT"; } // SPLASH - if (!prefs->whdbootprefs.showsplash) + if (!whdload_prefs.show_splash) { whd_bootscript << " SPLASHDELAY=0"; } // CONFIGDELAY - if (prefs->whdbootprefs.configdelay != 0) + if (whdload_prefs.config_delay != 0) { - whd_bootscript << " CONFIGDELAY=" << prefs->whdbootprefs.configdelay; + whd_bootscript << " CONFIGDELAY=" << whdload_prefs.config_delay; } // SPECIAL SAVE PATH - whd_bootscript << " SAVEPATH=Saves:Savegames/ SAVEDIR=\"" << sub_path << "\""; + whd_bootscript << " SAVEPATH=Saves:Savegames/ SAVEDIR=\"" << whdload_prefs.sub_path << "\""; // DATA PATH - if (data_path[0]) - whd_bootscript << "DATA=\"" << data_path << "\""; + if (!whdload_prefs.selected_slave.data_path.empty()) + whd_bootscript << " DATA=\"" << whdload_prefs.selected_slave.data_path << "\""; whd_bootscript << '\n'; // Launches utility program to quit the emulator (via a UAE trap in RTAREA) - if (prefs->whdbootprefs.quit_on_exit) + if (whdload_prefs.quit_on_exit) { whd_bootscript << "DH0:C/AmiQuit\n"; } @@ -1066,28 +1228,28 @@ bool is_a600_available(uae_prefs* prefs) return rom_test == 1; } -void set_booter_drives(uae_prefs* prefs, char* filepath) +void set_booter_drives(uae_prefs* prefs, const char* filepath) { TCHAR tmp[MAX_DPATH]; - if (strlen(selected_slave) != 0) // new booter solution + if (!whdload_prefs.selected_slave.filename.empty()) // new booter solution { _sntprintf(boot_path, MAX_DPATH, "/tmp/amiberry/"); - _stprintf(tmp, _T("filesystem2=rw,DH0:DH0:%s,10"), boot_path); + _sntprintf(tmp, MAX_DPATH, _T("filesystem2=rw,DH0:DH0:%s,10"), boot_path); cfgfile_parse_line(prefs, parse_text(tmp), 0); - _stprintf(tmp, _T("uaehf0=dir,rw,DH0:DH0::%s,10"), boot_path); + _sntprintf(tmp, MAX_DPATH, _T("uaehf0=dir,rw,DH0:DH0::%s,10"), boot_path); cfgfile_parse_line(prefs, parse_text(tmp), 0); _sntprintf(boot_path, MAX_DPATH, "%sboot-data.zip", whdbooter_path.c_str()); if (!my_existsfile2(boot_path)) _sntprintf(boot_path, MAX_DPATH, "%sboot-data/", whdbooter_path.c_str()); - _stprintf(tmp, _T("filesystem2=rw,DH3:DH3:%s,-10"), boot_path); + _sntprintf(tmp, MAX_DPATH, _T("filesystem2=rw,DH3:DH3:%s,-10"), boot_path); cfgfile_parse_line(prefs, parse_text(tmp), 0); - _stprintf(tmp, _T("uaehf0=dir,rw,DH3:DH3::%s,-10"), boot_path); + _sntprintf(tmp, MAX_DPATH, _T("uaehf0=dir,rw,DH3:DH3::%s,-10"), boot_path); cfgfile_parse_line(prefs, parse_text(tmp), 0); } else // revert to original booter is no slave was set @@ -1096,18 +1258,18 @@ void set_booter_drives(uae_prefs* prefs, char* filepath) if (!my_existsfile2(boot_path)) _sntprintf(boot_path, MAX_DPATH, "%sboot-data/", whdbooter_path.c_str()); - _stprintf(tmp, _T("filesystem2=rw,DH0:DH0:%s,10"), boot_path); + _sntprintf(tmp, MAX_DPATH, _T("filesystem2=rw,DH0:DH0:%s,10"), boot_path); cfgfile_parse_line(prefs, parse_text(tmp), 0); - _stprintf(tmp, _T("uaehf0=dir,rw,DH0:DH0::%s,10"), boot_path); + _sntprintf(tmp, MAX_DPATH, _T("uaehf0=dir,rw,DH0:DH0::%s,10"), boot_path); cfgfile_parse_line(prefs, parse_text(tmp), 0); } //set the Second (game data) drive - _stprintf(tmp, "filesystem2=rw,DH1:Games:\"%s\",0", filepath); + _sntprintf(tmp, MAX_DPATH, "filesystem2=rw,DH1:Games:\"%s\",0", filepath); cfgfile_parse_line(prefs, parse_text(tmp), 0); - _stprintf(tmp, "uaehf1=dir,rw,DH1:Games:\"%s\",0", filepath); + _sntprintf(tmp, MAX_DPATH, "uaehf1=dir,rw,DH1:Games:\"%s\",0", filepath); cfgfile_parse_line(prefs, parse_text(tmp), 0); //set the third (save data) drive @@ -1115,18 +1277,17 @@ void set_booter_drives(uae_prefs* prefs, char* filepath) if (my_existsdir(save_path)) { - _stprintf(tmp, "filesystem2=rw,DH2:Saves:%s,0", save_path); + _sntprintf(tmp, MAX_DPATH, "filesystem2=rw,DH2:Saves:%s,0", save_path); cfgfile_parse_line(prefs, parse_text(tmp), 0); - _stprintf(tmp, "uaehf2=dir,rw,DH2:Saves:%s,0", save_path); + _sntprintf(tmp, MAX_DPATH, "uaehf2=dir,rw,DH2:Saves:%s,0", save_path); cfgfile_parse_line(prefs, parse_text(tmp), 0); } } -void whdload_auto_prefs(uae_prefs* prefs, char* filepath) +void whdload_auto_prefs(uae_prefs* prefs, const char* filepath) { write_log("WHDBooter Launched\n"); - _tcscpy(selected_slave, ""); get_configuration_path(conf_path, MAX_DPATH); whdbooter_path = get_whdbootpath(); @@ -1147,11 +1308,12 @@ void whdload_auto_prefs(uae_prefs* prefs, char* filepath) // REMOVE THE FILE PATH AND EXTENSION const auto* filename = my_getfilepart(filepath); - get_game_name(filepath); + const std::string filename_no_extension = get_game_filename(filepath); + whdload_prefs.filename = filename_no_extension; // LOAD GAME SPECIFICS FOR EXISTING .UAE - USE SHA1 IF AVAILABLE // CONFIG LOAD IF .UAE IS IN CONFIG PATH - build_uae_config_filename(); + build_uae_config_filename(whdload_prefs.filename); // If we have a config file, we will use it. // We will need it for the WHDLoad options too. @@ -1177,7 +1339,7 @@ void whdload_auto_prefs(uae_prefs* prefs, char* filepath) // LOAD GAME SPECIFICS _sntprintf(whd_path, MAX_DPATH, "%sgame-data/", whdbooter_path.c_str()); - game_options game_detail; + game_hardware_options game_detail; _tcscpy(whd_config, whd_path); _tcscat(whd_config, "whdload_db.xml"); @@ -1192,9 +1354,9 @@ void whdload_auto_prefs(uae_prefs* prefs, char* filepath) } // If we have a slave, create a startup-sequence - if (strlen(selected_slave) != 0) + if (!whdload_prefs.selected_slave.filename.empty()) { - create_startup_sequence(prefs); + create_startup_sequence(); } // now we should have a startup-sequence file (if we don't, we are going to use the original booter) diff --git a/src/osdep/fsdb_host.h b/src/osdep/fsdb_host.h index f55db512..85c70ce3 100644 --- a/src/osdep/fsdb_host.h +++ b/src/osdep/fsdb_host.h @@ -29,8 +29,8 @@ struct fs_stat { }; extern bool fs_path_exists(const std::string& s); -extern std::string iso_8859_1_to_utf8(std::string& str); +extern std::string iso_8859_1_to_utf8(const std::string& str); extern void utf8_to_latin1_string(std::string& input, std::string& output); extern std::string prefix_with_application_directory_path(std::string currentpath); extern std::string prefix_with_data_path(std::string filename); -extern std::string prefix_with_whdboot_path(std::string filename); \ No newline at end of file +extern std::string prefix_with_whdboot_path(std::string filename); diff --git a/src/osdep/gui/Navigation.cpp b/src/osdep/gui/Navigation.cpp index 05cf45e1..e252816d 100644 --- a/src/osdep/gui/Navigation.cpp +++ b/src/osdep/gui/Navigation.cpp @@ -40,13 +40,14 @@ static NavigationMap navMap[] = {"Miscellaneous", "chkMouseUntrap", "chkMouseUntrap", "Disk swapper", "Priority"}, {"Priority", "cboInactiveRunAtPrio", "cboActiveRunAtPrio", "Miscellaneous", "Savestates" }, {"Savestates", "State0", "State0", "Priority", "Virtual Keyboard"}, - {"Virtual Keyboard", "chkVkEnabled", "chkVkEnabled", "Savestates", "Quit"}, - {"Shutdown", "Start", "Quit", "Virtual Keyboard", "About"}, - {"Quit", "Shutdown", "Restart", "Virtual Keyboard", "About"}, - {"Restart", "Quit", "Help", "Virtual Keyboard", "About"}, - {"Help", "Restart", "Reset", "Virtual Keyboard", "About"}, - {"Reset", "Help", "Start", "Virtual Keyboard", "About"}, - {"Start", "Reset", "Shutdown", "Virtual Keyboard", "About"}, + {"Virtual Keyboard", "chkVkEnabled", "chkVkEnabled", "Savestates", "WHDLoad"}, + {"WHDLoad", "", "", "Virtual Keyboard", "Quit"}, + {"Shutdown", "Start", "Quit", "WHDLoad", "About"}, + {"Quit", "Shutdown", "Restart", "WHDLoad", "About"}, + {"Restart", "Quit", "Help", "WHDLoad", "About"}, + {"Help", "Restart", "Reset", "WHDLoad", "About"}, + {"Reset", "Help", "Start", "WHDLoad", "About"}, + {"Start", "Reset", "Shutdown", "WHDLoad", "About"}, // PanelPaths {"cmdSystemROMs", "Paths", "Paths", "cmdRescanROMs", "cmdConfigPath"}, diff --git a/src/osdep/gui/PanelCustom.cpp b/src/osdep/gui/PanelCustom.cpp index 9df9084a..5aaab1db 100644 --- a/src/osdep/gui/PanelCustom.cpp +++ b/src/osdep/gui/PanelCustom.cpp @@ -334,7 +334,7 @@ void InitPanelCustom(const config_category& category) posY = txtPortInput->getY() + txtPortInput->getHeight() + DISTANCE_NEXT_Y; // Column 1 - const auto column1 = 5; + constexpr auto column1 = 5; for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX / 2; i++) { category.panel->add(lblCustomButtonAction[i], column1, posY); diff --git a/src/osdep/gui/PanelDiskSwapper.cpp b/src/osdep/gui/PanelDiskSwapper.cpp index 2d805226..618ac231 100644 --- a/src/osdep/gui/PanelDiskSwapper.cpp +++ b/src/osdep/gui/PanelDiskSwapper.cpp @@ -150,7 +150,7 @@ public: if (strncmp(changed_prefs.dfxlist[i], tmp.c_str(), MAX_DPATH) != 0) { strncpy(changed_prefs.dfxlist[i], tmp.c_str(), MAX_DPATH); - AddFileToDiskList(tmp.c_str(), 1); + add_file_to_mru_list(lstMRUDiskList, tmp); } } cmdDiskSwapperListAdd[i]->requestFocus(); @@ -212,7 +212,7 @@ public: { disk_swap(row, 1); - AddFileToDiskList(changed_prefs.dfxlist[row], 1); + add_file_to_mru_list(lstMRUDiskList, std::string(changed_prefs.dfxlist[row])); RefreshPanelDiskSwapper(); RefreshPanelFloppy(); RefreshPanelQuickstart(); diff --git a/src/osdep/gui/PanelDisplay.cpp b/src/osdep/gui/PanelDisplay.cpp index 548278b0..4bc3dce0 100644 --- a/src/osdep/gui/PanelDisplay.cpp +++ b/src/osdep/gui/PanelDisplay.cpp @@ -388,8 +388,8 @@ void InitPanelDisplay(const config_category& category) lblHOffset = new gcn::Label("H. Offset:"); lblHOffset->setAlignment(gcn::Graphics::LEFT); - sldHOffset = new gcn::Slider(-60, 60); - sldHOffset->setSize(135, SLIDER_HEIGHT); + sldHOffset = new gcn::Slider(-80, 80); + sldHOffset->setSize(200, SLIDER_HEIGHT); sldHOffset->setBaseColor(gui_baseCol); sldHOffset->setMarkerLength(20); sldHOffset->setStepLength(1); @@ -400,8 +400,8 @@ void InitPanelDisplay(const config_category& category) lblVOffset = new gcn::Label("V. Offset:"); lblVOffset->setAlignment(gcn::Graphics::LEFT); - sldVOffset = new gcn::Slider(-60, 60); - sldVOffset->setSize(135, SLIDER_HEIGHT); + sldVOffset = new gcn::Slider(-80, 80); + sldVOffset->setSize(200, SLIDER_HEIGHT); sldVOffset->setBaseColor(gui_baseCol); sldVOffset->setMarkerLength(20); sldVOffset->setStepLength(1); @@ -700,9 +700,8 @@ void RefreshPanelDisplay() sldBrightness->setValue(changed_prefs.gfx_luminance); lblBrightnessValue->setCaption(std::to_string(changed_prefs.gfx_luminance)); lblBrightnessValue->adjustSize(); - - int i; + int i; for (i = 0; i < AMIGAWIDTH_COUNT; ++i) { if (changed_prefs.gfx_monitor[0].gfx_size_win.width == amigawidth_values[i]) diff --git a/src/osdep/gui/PanelFloppy.cpp b/src/osdep/gui/PanelFloppy.cpp index ce242674..7afdbad8 100644 --- a/src/osdep/gui/PanelFloppy.cpp +++ b/src/osdep/gui/PanelFloppy.cpp @@ -211,7 +211,7 @@ public: { strncpy(changed_prefs.floppyslots[i].df, tmp.c_str(), MAX_DPATH); disk_insert(i, tmp.c_str()); - AddFileToDiskList(tmp.c_str(), 1); + add_file_to_mru_list(lstMRUDiskList, tmp); RefreshDiskListModel(); current_dir = extract_path(tmp); @@ -334,7 +334,7 @@ public: diskname[31] = '\0'; disk_creatediskfile(&changed_prefs, tmp.c_str(), 0, DRV_35_DD, -1, diskname, false, false, nullptr); DISK_history_add (tmp.c_str(), -1, HISTORY_FLOPPY, 0); - AddFileToDiskList(tmp.c_str(), 1); + add_file_to_mru_list(lstMRUDiskList, tmp); RefreshDiskListModel(); current_dir = extract_path(tmp); } @@ -351,7 +351,7 @@ public: diskname[31] = '\0'; disk_creatediskfile(&changed_prefs, tmp.c_str(), 0, DRV_35_HD, -1, diskname, false, false, nullptr); DISK_history_add (tmp.c_str(), -1, HISTORY_FLOPPY, 0); - AddFileToDiskList(tmp.c_str(), 1); + add_file_to_mru_list(lstMRUDiskList, tmp); RefreshDiskListModel(); current_dir = extract_path(tmp); } diff --git a/src/osdep/gui/PanelHD.cpp b/src/osdep/gui/PanelHD.cpp index 18dc4970..5d6ec2b8 100644 --- a/src/osdep/gui/PanelHD.cpp +++ b/src/osdep/gui/PanelHD.cpp @@ -252,7 +252,7 @@ public: strncpy(changed_prefs.cdslots[0].name, tmp.c_str(), sizeof changed_prefs.cdslots[0].name); changed_prefs.cdslots[0].inuse = true; changed_prefs.cdslots[0].type = SCSI_UNIT_DEFAULT; - AddFileToCDList(tmp.c_str(), 1); + add_file_to_mru_list(lstMRUCDList, tmp); current_dir = extract_path(tmp); RefreshCDListModel(); diff --git a/src/osdep/gui/PanelQuickstart.cpp b/src/osdep/gui/PanelQuickstart.cpp index 9492d23a..c3e9dc32 100644 --- a/src/osdep/gui/PanelQuickstart.cpp +++ b/src/osdep/gui/PanelQuickstart.cpp @@ -136,7 +136,6 @@ static amigamodels amodels[] = { static const int numModels = 10; static int numModelConfigs = 0; static bool bIgnoreListChange = true; -static char whdload_file[MAX_DPATH]; static gcn::StringListModel amigaModelList; static gcn::StringListModel amigaConfigList; @@ -144,6 +143,8 @@ static gcn::StringListModel diskfileList; static gcn::StringListModel cdfileList; static gcn::StringListModel whdloadFileList; +std::string whdload_filename; + static void AdjustDropDownControls(); static void CountModelConfigs() @@ -317,7 +318,7 @@ public: strncpy(changed_prefs.cdslots[0].name, tmp.c_str(), MAX_DPATH); changed_prefs.cdslots[0].inuse = true; changed_prefs.cdslots[0].type = SCSI_UNIT_DEFAULT; - AddFileToCDList(tmp.c_str(), 1); + add_file_to_mru_list(lstMRUCDList, tmp); current_dir = extract_path(tmp); RefreshCDListModel(); @@ -394,21 +395,21 @@ public: if (idx < 0) { - strncpy(whdload_file, "", MAX_DPATH); + whdload_filename = ""; } else { const auto element = get_full_path_from_disk_list(whdloadFileList.getElementAt(idx)); - if (element != whdload_file) + if (element != whdload_filename) { - strncpy(whdload_file, element.c_str(), MAX_DPATH); + whdload_filename.assign(element); lstMRUWhdloadList.erase(lstMRUWhdloadList.begin() + idx); - lstMRUWhdloadList.insert(lstMRUWhdloadList.begin(), whdload_file); + lstMRUWhdloadList.insert(lstMRUWhdloadList.begin(), whdload_filename); bIgnoreListChange = true; cboWhdload->setSelected(0); bIgnoreListChange = false; } - whdload_auto_prefs(&changed_prefs, whdload_file); + whdload_auto_prefs(&changed_prefs, whdload_filename.c_str()); } refresh_all_panels(); } @@ -428,23 +429,22 @@ public: //--------------------------------------- // Eject WHDLoad file //--------------------------------------- - strncpy(whdload_file, "", MAX_DPATH); - AdjustPrefs(); + whdload_filename = ""; } else if (actionEvent.getSource() == cmdWhdloadSelect) { std::string tmp; - if (strlen(whdload_file) > 0) - tmp = std::string(whdload_file); + if (!whdload_filename.empty()) + tmp = whdload_filename; else tmp = get_whdload_arch_path(); tmp = SelectFile("Select WHDLoad LHA file", tmp, whdload_filter); { - strncpy(whdload_file, tmp.c_str(), MAX_DPATH); - AddFileToWHDLoadList(whdload_file, 1); + whdload_filename = tmp; + add_file_to_mru_list(lstMRUWhdloadList, whdload_filename); RefreshWhdListModel(); - whdload_auto_prefs(&changed_prefs, whdload_file); + whdload_auto_prefs(&changed_prefs, whdload_filename.c_str()); AdjustDropDownControls(); } @@ -642,7 +642,7 @@ public: { strncpy(changed_prefs.floppyslots[i].df, tmp.c_str(), MAX_DPATH); disk_insert(i, tmp.c_str()); - AddFileToDiskList(tmp.c_str(), 1); + add_file_to_mru_list(lstMRUDiskList, tmp); current_dir = extract_path(tmp); RefreshDiskListModel(); current_dir = extract_path(tmp); @@ -1027,11 +1027,11 @@ static void AdjustDropDownControls() } cboWhdload->clearSelected(); - if (strlen(whdload_file) > 0) + if (!whdload_filename.empty()) { for (auto i = 0; i < static_cast(lstMRUWhdloadList.size()); ++i) { - if (lstMRUWhdloadList[i].c_str() != whdload_file) + if (lstMRUWhdloadList[i] == whdload_filename) { cboWhdload->setSelected(i); break; @@ -1048,6 +1048,7 @@ void RefreshPanelQuickstart() chkNTSC->setSelected(changed_prefs.ntscmode); + RefreshWhdListModel(); AdjustDropDownControls(); changed_prefs.nr_floppies = 0; diff --git a/src/osdep/gui/PanelWHDLoad.cpp b/src/osdep/gui/PanelWHDLoad.cpp new file mode 100644 index 00000000..9ed11299 --- /dev/null +++ b/src/osdep/gui/PanelWHDLoad.cpp @@ -0,0 +1,455 @@ +#include +#include +#include + +#include +#include + +#include "SelectorEntry.hpp" + +#include "sysdeps.h" +#include "options.h" +#include "gui_handling.h" +#include "StringListModel.h" + +static gcn::Label* lblWhdload; +static gcn::DropDown* cboWhdload; +static gcn::Button* cmdWhdloadEject; +static gcn::Button* cmdWhdloadSelect; + +// WHDLoad game options +static gcn::Label* lblGameName; +static gcn::TextField* txtGameName; + +static gcn::Label* lblVariantUuid; +static gcn::TextField* txtVariantUuid; + +static gcn::Label* lblSlaveDefault; +static gcn::TextField* txtSlaveDefault; + +static gcn::CheckBox* chkSlaveLibraries; + +static gcn::Label* lblSlaves; +static gcn::DropDown* cboSlaves; + +// WHDLoad global options +static gcn::Window* grpWHDLoadGlobal; +static gcn::Label* lblCustomText; +static gcn::TextField* txtCustomText; + +static gcn::CheckBox* chkButtonWait; +static gcn::CheckBox* chkShowSplash; + +static gcn::Label* lblConfigDelay; +static gcn::TextField* txtConfigDelay; + +static gcn::CheckBox* chkWriteCache; +static gcn::CheckBox* chkQuitOnExit; + +// Selected Slave options +static gcn::Label* lblSlaveDataPath; +static gcn::TextField* txtSlaveDataPath; + +// Selected Slave Custom options +static gcn::Button* cmdCustomFields; + +static gcn::StringListModel whdloadFileList; +static gcn::StringListModel slaves_list; + +static bool bIgnoreListChange = true; + +static void RefreshWhdListModel() +{ + whdloadFileList.clear(); + for (const auto& i : lstMRUWhdloadList) + { + const std::string full_path = i; + const std::string filename = full_path.substr(full_path.find_last_of("/\\") + 1); + whdloadFileList.add(std::string(filename).append(" { ").append(full_path).append(" }")); + } +} + +static void AdjustDropDownControls() +{ + bIgnoreListChange = true; + + cboWhdload->clearSelected(); + if (!whdload_filename.empty()) + { + for (auto i = 0; i < static_cast(lstMRUWhdloadList.size()); ++i) + { + if (lstMRUWhdloadList[i] == whdload_filename) + { + cboWhdload->setSelected(i); + break; + } + } + } + + bIgnoreListChange = false; +} + +class WHDLoadActionListener : public gcn::ActionListener +{ +public: + void action(const gcn::ActionEvent& actionEvent) override + { + const auto source = actionEvent.getSource(); + if (source == cboWhdload) + { + //--------------------------------------- + // WHDLoad file from list selected + //--------------------------------------- + if (!bIgnoreListChange) + { + const auto idx = cboWhdload->getSelected(); + + if (idx < 0) + { + whdload_filename = ""; + } + else + { + const auto element = get_full_path_from_disk_list(whdloadFileList.getElementAt(idx)); + if (element != whdload_filename) + { + whdload_filename.assign(element); + lstMRUWhdloadList.erase(lstMRUWhdloadList.begin() + idx); + lstMRUWhdloadList.insert(lstMRUWhdloadList.begin(), whdload_filename); + bIgnoreListChange = true; + cboWhdload->setSelected(0); + bIgnoreListChange = false; + } + whdload_auto_prefs(&changed_prefs, whdload_filename.c_str()); + } + refresh_all_panels(); + } + } + else if (source == cmdWhdloadEject) + { + //--------------------------------------- + // Eject WHDLoad file + //--------------------------------------- + whdload_filename = ""; + refresh_all_panels(); + } + else if (source == cmdWhdloadSelect) + { + std::string tmp; + if (!whdload_filename.empty()) + tmp = whdload_filename; + else + tmp = get_whdload_arch_path(); + + tmp = SelectFile("Select WHDLoad LHA file", tmp, whdload_filter); + { + whdload_filename.assign(tmp); + add_file_to_mru_list(lstMRUWhdloadList, whdload_filename); + RefreshWhdListModel(); + whdload_auto_prefs(&changed_prefs, whdload_filename.c_str()); + + AdjustDropDownControls(); + } + cmdWhdloadSelect->requestFocus(); + refresh_all_panels(); + } + else if (source == cboSlaves) + { + if (cboSlaves->getSelected() >= 0) + { + whdload_prefs.selected_slave = whdload_prefs.slaves[cboSlaves->getSelected()]; + txtSlaveDataPath->setText(whdload_prefs.selected_slave.data_path.empty() ? "" : whdload_prefs.selected_slave.data_path); + create_startup_sequence(); + } + } + else if (source == cmdCustomFields) + { + ShowCustomFields(); + } + else if (source == chkButtonWait) + { + whdload_prefs.button_wait = chkButtonWait->isSelected(); + create_startup_sequence(); + } + else if (source == chkShowSplash) + { + whdload_prefs.show_splash = chkShowSplash->isSelected(); + create_startup_sequence(); + } + else if (source == chkWriteCache) + { + whdload_prefs.write_cache = chkWriteCache->isSelected(); + create_startup_sequence(); + } + else if (source == chkQuitOnExit) + { + whdload_prefs.quit_on_exit = chkQuitOnExit->isSelected(); + create_startup_sequence(); + } + } +}; + +static WHDLoadActionListener* whdloadActionListener; + +void InitPanelWHDLoad(const struct config_category& category) +{ + slaves_list.clear(); + + constexpr int textfield_width = 350; + + whdloadActionListener = new WHDLoadActionListener(); + + lblWhdload = new gcn::Label("WHDLoad auto-config:"); + cboWhdload = new gcn::DropDown(&whdloadFileList); + cboWhdload->setSize(category.panel->getWidth() - 2 * DISTANCE_BORDER, cboWhdload->getHeight()); + cboWhdload->setBaseColor(gui_baseCol); + cboWhdload->setBackgroundColor(colTextboxBackground); + cboWhdload->setId("cboWhdload"); + cboWhdload->addActionListener(whdloadActionListener); + + cmdWhdloadEject = new gcn::Button("Eject"); + cmdWhdloadEject->setSize(SMALL_BUTTON_WIDTH * 2, SMALL_BUTTON_HEIGHT); + cmdWhdloadEject->setBaseColor(gui_baseCol); + cmdWhdloadEject->setId("cmdWhdloadEject"); + cmdWhdloadEject->addActionListener(whdloadActionListener); + + cmdWhdloadSelect = new gcn::Button("Select file"); + cmdWhdloadSelect->setSize(BUTTON_WIDTH + 10, SMALL_BUTTON_HEIGHT); + cmdWhdloadSelect->setBaseColor(gui_baseCol); + cmdWhdloadSelect->setId("cmdWhdloadSelect"); + cmdWhdloadSelect->addActionListener(whdloadActionListener); + + // WHDLoad options + lblGameName = new gcn::Label("Game Name:"); + txtGameName = new gcn::TextField(); + txtGameName->setSize(textfield_width, TEXTFIELD_HEIGHT); + txtGameName->setBackgroundColor(colTextboxBackground); + + lblVariantUuid = new gcn::Label("UUID:"); + txtVariantUuid = new gcn::TextField(); + txtVariantUuid->setSize(textfield_width, TEXTFIELD_HEIGHT); + txtVariantUuid->setBackgroundColor(colTextboxBackground); + + lblSlaveDefault = new gcn::Label("Slave Default:"); + txtSlaveDefault = new gcn::TextField(); + txtSlaveDefault->setSize(textfield_width, TEXTFIELD_HEIGHT); + txtSlaveDefault->setBackgroundColor(colTextboxBackground); + + chkSlaveLibraries = new gcn::CheckBox("Slave Libraries"); + + lblSlaves = new gcn::Label("Slaves:"); + cboSlaves = new gcn::DropDown(&slaves_list); + cboSlaves->setSize(textfield_width, cboSlaves->getHeight()); + cboSlaves->setBaseColor(gui_baseCol); + cboSlaves->setBackgroundColor(colTextboxBackground); + cboSlaves->addActionListener(whdloadActionListener); + + lblSlaveDataPath = new gcn::Label("Slave Data path:"); + txtSlaveDataPath = new gcn::TextField(); + txtSlaveDataPath->setSize(textfield_width, TEXTFIELD_HEIGHT); + txtSlaveDataPath->setBackgroundColor(colTextboxBackground); + + cmdCustomFields = new gcn::Button("Custom Fields"); + cmdCustomFields->setSize(BUTTON_WIDTH * 2, BUTTON_HEIGHT); + cmdCustomFields->setBaseColor(gui_baseCol); + cmdCustomFields->setId("cmdCustomFields"); + cmdCustomFields->addActionListener(whdloadActionListener); + + lblCustomText = new gcn::Label("Custom:"); + txtCustomText = new gcn::TextField(); + txtCustomText->setSize(textfield_width, TEXTFIELD_HEIGHT); + txtCustomText->setBackgroundColor(colTextboxBackground); + + chkButtonWait = new gcn::CheckBox("Button Wait"); + chkButtonWait->addActionListener(whdloadActionListener); + chkShowSplash = new gcn::CheckBox("Show Splash"); + chkShowSplash->addActionListener(whdloadActionListener); + + lblConfigDelay = new gcn::Label("Config Delay:"); + txtConfigDelay = new gcn::TextField(); + txtConfigDelay->setSize(textfield_width, TEXTFIELD_HEIGHT); + txtConfigDelay->setBackgroundColor(colTextboxBackground); + + chkWriteCache = new gcn::CheckBox("Write Cache"); + chkWriteCache->addActionListener(whdloadActionListener); + chkQuitOnExit = new gcn::CheckBox("Quit on Exit"); + chkQuitOnExit->addActionListener(whdloadActionListener); + + constexpr int pos_x1 = DISTANCE_BORDER; + const int pos_x2 = chkSlaveLibraries->getWidth() + 8; + int pos_y = DISTANCE_BORDER; + + category.panel->add(lblWhdload, DISTANCE_BORDER, pos_y); + category.panel->add(cmdWhdloadEject, lblWhdload->getX() + lblWhdload->getWidth() + DISTANCE_NEXT_X * 16, pos_y); + category.panel->add(cmdWhdloadSelect, cmdWhdloadEject->getX() + cmdWhdloadEject->getWidth() + DISTANCE_NEXT_X, pos_y); + pos_y += cmdWhdloadSelect->getHeight() + 8; + + category.panel->add(cboWhdload, DISTANCE_BORDER, pos_y); + pos_y += cboWhdload->getHeight() + DISTANCE_NEXT_Y; + + category.panel->add(lblGameName, pos_x1, pos_y); + category.panel->add(txtGameName, pos_x2, pos_y); + pos_y += lblGameName->getHeight() + 8; + + category.panel->add(lblVariantUuid, pos_x1, pos_y); + category.panel->add(txtVariantUuid, pos_x2, pos_y); + pos_y += lblVariantUuid->getHeight() + 8; + + category.panel->add(lblSlaveDefault, pos_x1, pos_y); + category.panel->add(txtSlaveDefault, pos_x2, pos_y); + pos_y += lblSlaveDefault->getHeight() + 8; + + category.panel->add(chkSlaveLibraries, pos_x1, pos_y); + pos_y += chkSlaveLibraries->getHeight() + 8; + + category.panel->add(lblSlaves, pos_x1, pos_y); + category.panel->add(cboSlaves, pos_x2, pos_y); + pos_y += lblSlaves->getHeight() + 8; + + category.panel->add(lblSlaveDataPath, pos_x1, pos_y); + category.panel->add(txtSlaveDataPath, pos_x2, pos_y); + pos_y += lblSlaveDataPath->getHeight() + 8; + + category.panel->add(lblCustomText, pos_x1, pos_y); + category.panel->add(txtCustomText, pos_x2, pos_y); + pos_y += lblCustomText->getHeight() + DISTANCE_NEXT_Y; + + category.panel->add(cmdCustomFields, pos_x2, pos_y); + + grpWHDLoadGlobal = new gcn::Window("Global options"); + grpWHDLoadGlobal->setMovable(false); + grpWHDLoadGlobal->setTitleBarHeight(TITLEBAR_HEIGHT); + grpWHDLoadGlobal->setBaseColor(gui_baseCol); + + pos_y = 10; + grpWHDLoadGlobal->add(chkButtonWait, pos_x1, pos_y); + pos_y += chkButtonWait->getHeight() + 8; + + grpWHDLoadGlobal->add(chkShowSplash, pos_x1, pos_y); + pos_y += chkShowSplash->getHeight() + 8; + + grpWHDLoadGlobal->add(lblConfigDelay, pos_x1, pos_y); + grpWHDLoadGlobal->add(txtConfigDelay, pos_x2, pos_y); + pos_y += txtConfigDelay->getHeight() + 8; + + grpWHDLoadGlobal->add(chkWriteCache, pos_x1, pos_y); + pos_y += chkWriteCache->getHeight() + 8; + + grpWHDLoadGlobal->add(chkQuitOnExit, pos_x1, pos_y); + grpWHDLoadGlobal->setSize(category.panel->getWidth() - DISTANCE_BORDER * 2, + chkQuitOnExit->getY() + chkQuitOnExit->getHeight() + DISTANCE_NEXT_Y + TITLEBAR_HEIGHT); + grpWHDLoadGlobal->setPosition(pos_x1, category.panel->getHeight() - grpWHDLoadGlobal->getHeight() - DISTANCE_NEXT_Y); + category.panel->add(grpWHDLoadGlobal); + + bIgnoreListChange = false; + + RefreshPanelWHDLoad(); +} + +void ExitPanelWHDLoad() +{ + delete lblWhdload; + delete cboWhdload; + delete cmdWhdloadEject; + delete cmdWhdloadSelect; + + // WHDLoad options + delete lblGameName; + delete txtGameName; + + delete lblVariantUuid; + delete txtVariantUuid; + + delete lblSlaveDefault; + delete txtSlaveDefault; + + delete chkSlaveLibraries; + + delete lblSlaves; + delete cboSlaves; + + delete lblCustomText; + delete txtCustomText; + + delete chkButtonWait; + delete chkShowSplash; + + delete lblConfigDelay; + delete txtConfigDelay; + + delete chkWriteCache; + delete chkQuitOnExit; + + // Selected Slave options + delete lblSlaveDataPath; + delete txtSlaveDataPath; + + // Selected Slave Custom options + delete cmdCustomFields; + delete grpWHDLoadGlobal; + + delete whdloadActionListener; +} + +void update_slaves_list(const std::vector& slaves) +{ + slaves_list.clear(); + for (auto& slave : slaves) + { + slaves_list.add(slave.filename); + } +} + +void update_selected_slave(const whdload_slave& selected_slave) +{ + int selected = 0; + for (int i = 0; i < slaves_list.getNumberOfElements(); i++) + { + if (slaves_list.getElementAt(i) == selected_slave.filename) + { + selected = i; + break; + } + } + cboSlaves->setSelected(selected); +} + +void RefreshPanelWHDLoad() +{ + RefreshWhdListModel(); + AdjustDropDownControls(); + + cmdCustomFields->setEnabled(!whdload_filename.empty()); + + if (whdload_filename.empty()) + { + clear_whdload_prefs(); + slaves_list.clear(); + } + else + { + update_slaves_list(whdload_prefs.slaves); + update_selected_slave(whdload_prefs.selected_slave); + txtSlaveDataPath->setText(whdload_prefs.selected_slave.data_path.empty() ? "" : whdload_prefs.selected_slave.data_path); + } + + txtGameName->setText(whdload_prefs.game_name.empty() ? "" : whdload_prefs.game_name); + txtVariantUuid->setText(whdload_prefs.variant_uuid.empty() ? "" : whdload_prefs.variant_uuid); + txtSlaveDefault->setText(whdload_prefs.slave_default.empty() ? "" : whdload_prefs.slave_default); + chkSlaveLibraries->setSelected(whdload_prefs.slave_libraries); + txtCustomText->setText(whdload_prefs.custom.empty() ? "" : whdload_prefs.custom); + + // These are global + chkButtonWait->setSelected(whdload_prefs.button_wait); + chkShowSplash->setSelected(whdload_prefs.show_splash); + txtConfigDelay->setText(std::to_string(whdload_prefs.config_delay)); + chkWriteCache->setSelected(whdload_prefs.write_cache); + chkQuitOnExit->setSelected(whdload_prefs.quit_on_exit); +} + +bool HelpPanelWHDLoad(std::vector& helptext) +{ + helptext.clear(); + //TODO + return true; +} \ No newline at end of file diff --git a/src/osdep/gui/SelectFile.cpp b/src/osdep/gui/SelectFile.cpp index 845300a9..bd9282e2 100644 --- a/src/osdep/gui/SelectFile.cpp +++ b/src/osdep/gui/SelectFile.cpp @@ -44,40 +44,41 @@ static gcn::TextField* txtFilename; class SelectFileListModel : public gcn::ListModel { - std::vector dirs{}; - std::vector files{}; + std::vector dirs; + std::vector files; public: - explicit SelectFileListModel(const char* path) + explicit SelectFileListModel(const std::string& path) { changeDir(path); } int getNumberOfElements() override { - return static_cast(dirs.size() + files.size()); + return dirs.size() + files.size(); } void add(const std::string& elem) override { - dirs.emplace_back(elem); + dirs.push_back(elem); } void clear() override { dirs.clear(); + files.clear(); } - std::string getElementAt(const int i) override + std::string getElementAt(int i) override { - if (i >= static_cast(dirs.size() + files.size()) || i < 0) + if (i < 0 || i >= getNumberOfElements()) return "---"; - if (i < static_cast(dirs.size())) + if (i < dirs.size()) return dirs[i]; return files[i - dirs.size()]; } - void changeDir(const char* path) + void changeDir(const std::string& path) { read_directory(path, &dirs, &files); if (dirs.empty()) @@ -85,7 +86,7 @@ public: FilterFiles(&files, filefilter); } - [[nodiscard]] bool isDir(const unsigned int i) const + bool isDir(unsigned int i) const { return (i < dirs.size()); } diff --git a/src/osdep/gui/SelectFolder.cpp b/src/osdep/gui/SelectFolder.cpp index 9ef3f594..d596f9d3 100644 --- a/src/osdep/gui/SelectFolder.cpp +++ b/src/osdep/gui/SelectFolder.cpp @@ -49,13 +49,11 @@ public: dialogFinished = true; } }; - static FolderRequesterButtonActionListener* folderButtonActionListener; - class SelectDirListModel : public gcn::ListModel { - std::vector dirs{}; + std::vector dirs; public: SelectDirListModel(const std::string& path) @@ -70,7 +68,7 @@ public: void add(const std::string& elem) override { - dirs.emplace_back(elem); + dirs.push_back(elem); } void clear() override @@ -78,9 +76,9 @@ public: dirs.clear(); } - std::string getElementAt(const int i) override + std::string getElementAt(int i) override { - if (i >= static_cast(dirs.size()) || i < 0) + if (i < 0 || i >= getNumberOfElements()) return "---"; return dirs[i]; } diff --git a/src/osdep/gui/SelectorEntry.hpp b/src/osdep/gui/SelectorEntry.hpp index 8b33aebe..7345f8d2 100644 --- a/src/osdep/gui/SelectorEntry.hpp +++ b/src/osdep/gui/SelectorEntry.hpp @@ -1,5 +1,4 @@ -#ifndef GCN_SELECTORENTRY_HPP -#define GCN_SELECTORENTRY_HPP +#pragma once #include #include @@ -31,7 +30,7 @@ namespace gcn void setInactiveColor(const Color& color); void setActiveColor(const Color& color); void setActive(bool active); - [[nodiscard]] bool getActive() const; + bool getActive() const; void widgetResized(const Event& event) override; @@ -48,4 +47,3 @@ namespace gcn }; } -#endif // end GCN_SELECTORENTRY_HPP diff --git a/src/osdep/gui/ShowCustomFields.cpp b/src/osdep/gui/ShowCustomFields.cpp new file mode 100644 index 00000000..47770d83 --- /dev/null +++ b/src/osdep/gui/ShowCustomFields.cpp @@ -0,0 +1,434 @@ +#include +#include + +#include +#include + +#include "SelectorEntry.hpp" +#include "StringListModel.h" + +#include "sysdeps.h" +#include "config.h" +#include "gui_handling.h" + +#include "options.h" +#include "amiberry_input.h" + +enum +{ + DIALOG_WIDTH = 600, + DIALOG_HEIGHT = 600 +}; + +static bool dialog_finished = false; +static int custom_number[5]; +static gcn::StringListModel custom_list[5]; + +static gcn::Window* wndShowCustomFields; +static gcn::Button* cmdOK; + +struct custom_widget { + std::vector lbl; + std::vector boolean; + std::vector bit; + std::vector list; +}; + +custom_widget customWidget1; +custom_widget customWidget2; +custom_widget customWidget3; +custom_widget customWidget4; +custom_widget customWidget5; + +constexpr std::array customWidgets = { &customWidget1, &customWidget2, &customWidget3, &customWidget4, &customWidget5 }; +constexpr std::array customFields = { &whdload_prefs.selected_slave.custom1, &whdload_prefs.selected_slave.custom2, &whdload_prefs.selected_slave.custom3, &whdload_prefs.selected_slave.custom4, &whdload_prefs.selected_slave.custom5 }; + +static int set_bit(const int value, const int bit_position) { + return value | (1 << bit_position); +} + +static int clear_bit(const int value, const int bit_position) { + return value & ~(1 << bit_position); +} + +static bool is_bit_set(const int num, const int bit) { + return (num & (1 << bit)) != 0; +} + +class ShowCustomFieldsActionListener : public gcn::ActionListener +{ +public: + void action(const gcn::ActionEvent& actionEvent) override + { + const auto source = actionEvent.getSource(); + + if (source == cmdOK) + dialog_finished = true; + else + { + for (int j = 0; j < 5; ++j) + { + for (int i = 0; i < custom_number[j]; i++) + { + if (!customWidgets[j]->list.empty() && customFields[j]->type == list_type) + { + if (source == customWidgets[j]->list[i]) + { + customFields[j]->value = customWidgets[j]->list[i]->getSelected(); + write_log("Custom field %d value updated to: %d\n", i, customFields[j]->value); + } + } + else if (!customWidgets[j]->boolean.empty() && customFields[j]->type == bool_type) + { + if (source == customWidgets[j]->boolean[i]) + { + customFields[j]->value = customWidgets[j]->boolean[i]->isSelected(); + write_log("Custom field %d value updated to: %d\n", i, customFields[j]->value); + } + } + else if (!customWidgets[j]->bit.empty() && customFields[j]->type == bit_type) + { + if (source == customWidgets[j]->bit[i]) + { + customFields[j]->value = customWidgets[j]->bit[i]->isSelected() + ? set_bit(customFields[j]->value, customFields[j]->label_bit_pairs[i].second) + : clear_bit(customFields[j]->value, customFields[j]->label_bit_pairs[i].second); + write_log("Custom field %d value updated to: %d\n", i, customFields[j]->value); + } + } + } + } + } + } +}; + +static ShowCustomFieldsActionListener* showCustomFieldsActionListener; + +void create_custom_field(custom_widget& widget, const int number, const std::string& caption, const whdload_custom& custom_field, int& pos_y, const int custom_list_index) +{ + constexpr int textfield_width = 350; + constexpr int pos_x1 = DISTANCE_BORDER; + constexpr int pos_x2 = 150; + + for (int i = 0; i < number; i++) { + std::string id; + + auto label = new gcn::Label(caption + ":"); + label->setPosition(pos_x1, pos_y); + widget.lbl.emplace_back(label); + wndShowCustomFields->add(label); + + switch (custom_field.type) { + case bit_type: { + auto checkbox = new gcn::CheckBox(custom_field.label_bit_pairs[i].first); + checkbox->setSelected(is_bit_set(custom_field.value, custom_field.label_bit_pairs[i].second)); + checkbox->addActionListener(showCustomFieldsActionListener); + checkbox->setId("chkCustomFieldBit_" + std::to_string(i)); + checkbox->setPosition(pos_x2, pos_y); + widget.bit.emplace_back(checkbox); + wndShowCustomFields->add(checkbox); + pos_y += checkbox->getHeight() + 8; + break; + } + case bool_type: { + auto checkbox = new gcn::CheckBox(custom_field.caption); + checkbox->setSelected(custom_field.value); + checkbox->addActionListener(showCustomFieldsActionListener); + checkbox->setId("chkCustomFieldBool_" + std::to_string(i)); + checkbox->setPosition(pos_x2, pos_y); + widget.boolean.emplace_back(checkbox); + wndShowCustomFields->add(checkbox); + pos_y += checkbox->getHeight() + 8; + break; + } + case list_type: { + label->setCaption(custom_field.caption); + label->adjustSize(); + for (const auto& item : custom_field.labels) + { + custom_list[custom_list_index].add(item); + } + auto dropdown = new gcn::DropDown(&custom_list[custom_list_index]); + dropdown->setId("cboCustomFieldList_" + std::to_string(i)); + dropdown->setSize(textfield_width, dropdown->getHeight()); + dropdown->setBaseColor(gui_baseCol); + dropdown->setBackgroundColor(colTextboxBackground); + dropdown->addActionListener(showCustomFieldsActionListener); + dropdown->setPosition(pos_x2, pos_y); + widget.list.emplace_back(dropdown); + wndShowCustomFields->add(dropdown); + pos_y += dropdown->getHeight() + 8; + break; + } + default: + pos_y += label->getHeight() + 8; + break; + } + } +} + +void delete_custom_field(custom_widget& customField) { + for (const auto& lbl : customField.lbl) { + delete lbl; + } + customField.lbl.clear(); + + for (const auto& chk : customField.boolean) { + delete chk; + } + customField.boolean.clear(); + + for (const auto& chk : customField.bit) { + delete chk; + } + customField.bit.clear(); + + for (const auto& cbo : customField.list) { + delete cbo; + } + customField.list.clear(); +} + +static void InitShowCustomFields() +{ + wndShowCustomFields = new gcn::Window("Custom Fields"); + wndShowCustomFields->setSize(DIALOG_WIDTH, DIALOG_HEIGHT); + wndShowCustomFields->setPosition((GUI_WIDTH - DIALOG_WIDTH) / 2, (GUI_HEIGHT - DIALOG_HEIGHT) / 2); + wndShowCustomFields->setBaseColor(gui_baseCol); + wndShowCustomFields->setTitleBarHeight(TITLEBAR_HEIGHT); + + showCustomFieldsActionListener = new ShowCustomFieldsActionListener(); + int pos_y = DISTANCE_BORDER; + + for (auto& i : custom_list) + { + i.clear(); + } + + for (int i = 0; i < 5; ++i) { + create_custom_field(*customWidgets[i], custom_number[i], "Custom" + std::to_string(i + 1), *customFields[i], pos_y, i); + } + cmdOK = new gcn::Button("Ok"); + cmdOK->setSize(BUTTON_WIDTH, BUTTON_HEIGHT); + cmdOK->setPosition(DIALOG_WIDTH - DISTANCE_BORDER - BUTTON_WIDTH, + DIALOG_HEIGHT - 2 * DISTANCE_BORDER - BUTTON_HEIGHT - 10); + cmdOK->setBaseColor(gui_baseCol); + cmdOK->addActionListener(showCustomFieldsActionListener); + + wndShowCustomFields->add(cmdOK); + + gui_top->add(wndShowCustomFields); + + wndShowCustomFields->requestModalFocus(); + focus_bug_workaround(wndShowCustomFields); + cmdOK->requestFocus(); +} + +static void ExitShowCustomFields() +{ + wndShowCustomFields->releaseModalFocus(); + gui_top->remove(wndShowCustomFields); + + delete cmdOK; + + delete_custom_field(customWidget1); + delete_custom_field(customWidget2); + delete_custom_field(customWidget3); + delete_custom_field(customWidget4); + delete_custom_field(customWidget5); + + delete showCustomFieldsActionListener; + delete wndShowCustomFields; +} + +static void ShowCustomFieldsLoop() +{ + const AmigaMonitor* mon = &AMonitors[0]; + + auto got_event = 0; + SDL_Event event; + SDL_Event touch_event; + didata* did = &di_joystick[0]; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_KEYDOWN: + got_event = 1; + switch (event.key.keysym.sym) + { + case VK_ESCAPE: + dialog_finished = true; + break; + + case VK_Blue: + case VK_Green: + case SDLK_RETURN: + event.key.keysym.sym = SDLK_RETURN; + gui_input->pushInput(event); // Fire key down + event.type = SDL_KEYUP; // and the key up + break; + default: + break; + } + break; + + case SDL_JOYBUTTONDOWN: + if (gui_joystick) + { + got_event = 1; + if (SDL_JoystickGetButton(gui_joystick, did->mapping.button[SDL_CONTROLLER_BUTTON_A]) || + SDL_JoystickGetButton(gui_joystick, did->mapping.button[SDL_CONTROLLER_BUTTON_B])) + { + PushFakeKey(SDLK_RETURN); + break; + } + if (SDL_JoystickGetButton(gui_joystick, did->mapping.button[SDL_CONTROLLER_BUTTON_X]) || + SDL_JoystickGetButton(gui_joystick, did->mapping.button[SDL_CONTROLLER_BUTTON_Y]) || + SDL_JoystickGetButton(gui_joystick, did->mapping.button[SDL_CONTROLLER_BUTTON_START])) + { + dialog_finished = true; + break; + } + } + break; + + case SDL_FINGERDOWN: + got_event = 1; + memcpy(&touch_event, &event, sizeof event); + touch_event.type = SDL_MOUSEBUTTONDOWN; + touch_event.button.which = 0; + touch_event.button.button = SDL_BUTTON_LEFT; + touch_event.button.state = SDL_PRESSED; + + touch_event.button.x = gui_graphics->getTarget()->w * static_cast(event.tfinger.x); + touch_event.button.y = gui_graphics->getTarget()->h * static_cast(event.tfinger.y); + + gui_input->pushInput(touch_event); + break; + + case SDL_FINGERUP: + got_event = 1; + memcpy(&touch_event, &event, sizeof event); + touch_event.type = SDL_MOUSEBUTTONUP; + touch_event.button.which = 0; + touch_event.button.button = SDL_BUTTON_LEFT; + touch_event.button.state = SDL_RELEASED; + + touch_event.button.x = gui_graphics->getTarget()->w * static_cast(event.tfinger.x); + touch_event.button.y = gui_graphics->getTarget()->h * static_cast(event.tfinger.y); + + gui_input->pushInput(touch_event); + break; + + case SDL_FINGERMOTION: + got_event = 1; + memcpy(&touch_event, &event, sizeof event); + touch_event.type = SDL_MOUSEMOTION; + touch_event.motion.which = 0; + touch_event.motion.state = 0; + + touch_event.motion.x = gui_graphics->getTarget()->w * static_cast(event.tfinger.x); + touch_event.motion.y = gui_graphics->getTarget()->h * static_cast(event.tfinger.y); + + gui_input->pushInput(touch_event); + break; + + case SDL_MOUSEWHEEL: + got_event = 1; + if (event.wheel.y > 0) + { + for (auto z = 0; z < event.wheel.y; ++z) + { + PushFakeKey(SDLK_UP); + } + } + else if (event.wheel.y < 0) + { + for (auto z = 0; z > event.wheel.y; --z) + { + PushFakeKey(SDLK_DOWN); + } + } + break; + + case SDL_KEYUP: + case SDL_JOYBUTTONUP: + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEMOTION: + case SDL_RENDER_TARGETS_RESET: + case SDL_RENDER_DEVICE_RESET: + case SDL_WINDOWEVENT: + case SDL_DISPLAYEVENT: + case SDL_SYSWMEVENT: + got_event = 1; + break; + + default: + break; + } + //------------------------------------------------- + // Send event to guisan-controls + //------------------------------------------------- + gui_input->pushInput(event); + } + + if (got_event) + { + // Now we let the Gui object perform its logic. + uae_gui->logic(); + + SDL_RenderClear(mon->sdl_renderer); + + // Now we let the Gui object draw itself. + uae_gui->draw(); + // Finally we update the screen. + update_gui_screen(); + } +} + +void ShowCustomFields() +{ + const AmigaMonitor* mon = &AMonitors[0]; + + dialog_finished = false; + + // Initialize custom_number to 1 + std::fill(std::begin(custom_number), std::end(custom_number), 1); + + // If the custom field is a bit field, set the custom_number to the number of pairs + if (whdload_prefs.selected_slave.custom1.type == bit_type) + custom_number[0] = static_cast(whdload_prefs.selected_slave.custom1.label_bit_pairs.size()); + if (whdload_prefs.selected_slave.custom2.type == bit_type) + custom_number[1] = static_cast(whdload_prefs.selected_slave.custom2.label_bit_pairs.size()); + if (whdload_prefs.selected_slave.custom3.type == bit_type) + custom_number[2] = static_cast(whdload_prefs.selected_slave.custom3.label_bit_pairs.size()); + if (whdload_prefs.selected_slave.custom4.type == bit_type) + custom_number[3] = static_cast(whdload_prefs.selected_slave.custom4.label_bit_pairs.size()); + if (whdload_prefs.selected_slave.custom5.type == bit_type) + custom_number[4] = static_cast(whdload_prefs.selected_slave.custom5.label_bit_pairs.size()); + + InitShowCustomFields(); + + wndShowCustomFields->setCaption("Custom Fields"); + cmdOK->setCaption("Ok"); + + // Prepare the screen once + uae_gui->logic(); + + SDL_RenderClear(mon->sdl_renderer); + + uae_gui->draw(); + update_gui_screen(); + + while (!dialog_finished) + { + const auto start = SDL_GetPerformanceCounter(); + ShowCustomFieldsLoop(); + cap_fps(start); + } + + create_startup_sequence(); + ExitShowCustomFields(); +} \ No newline at end of file diff --git a/src/osdep/gui/gui_handling.h b/src/osdep/gui/gui_handling.h index 4a460aaa..1088a949 100644 --- a/src/osdep/gui/gui_handling.h +++ b/src/osdep/gui/gui_handling.h @@ -322,6 +322,11 @@ void ExitPanelVirtualKeyboard(); void RefreshPanelVirtualKeyboard(); bool HelpPanelVirtualKeyboard(std::vector& helptext); +void InitPanelWHDLoad(const struct config_category& category); +void ExitPanelWHDLoad(); +void RefreshPanelWHDLoad(); +bool HelpPanelWHDLoad(std::vector& helptext); + void refresh_all_panels(); void register_refresh_func(void (*func)()); void focus_bug_workaround(gcn::Window* wnd); @@ -340,9 +345,10 @@ bool EditFilesysHardDrive(int unit_no); bool CreateFilesysHardfile(); void ShowHelp(const char* title, const std::vector& text); void ShowDiskInfo(const char* title, const std::vector& text); +void ShowCustomFields(); std::string show_controller_map(int device, bool map_touchpad); -extern void read_directory(std::string path, vector* dirs, vector* files); +extern void read_directory(const std::string& path, vector* dirs, vector* files); extern void FilterFiles(vector* files, const char* filter[]); enum @@ -371,6 +377,7 @@ STATIC_INLINE bool is_hdf_rdb() return current_hfdlg.ci.sectors == 0 && current_hfdlg.ci.surfaces == 0 && current_hfdlg.ci.reserved == 0; } +extern std::string whdload_filename; extern std::string screenshot_filename; extern int currentStateNum; extern int delay_savestate_frame; @@ -390,6 +397,8 @@ extern int todfxtype(int num, int dfx, int* subtype); extern void DisplayDiskInfo(int num); extern std::string get_full_path_from_disk_list(std::string element); extern amiberry_hotkey get_hotkey_from_config(std::string config_option); -extern void save_mapping_to_file(std::string mapping); +extern void save_mapping_to_file(const std::string& mapping); +extern void clear_whdload_prefs(); +extern void create_startup_sequence(); #endif // GUI_HANDLING_H diff --git a/src/osdep/gui/main_window.cpp b/src/osdep/gui/main_window.cpp index 28348d46..5d90bb5a 100644 --- a/src/osdep/gui/main_window.cpp +++ b/src/osdep/gui/main_window.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -91,6 +92,7 @@ ConfigCategory categories[] = { {"Virtual Keyboard", "keyboard.png", nullptr, nullptr, InitPanelVirtualKeyboard, ExitPanelVirtualKeyboard, RefreshPanelVirtualKeyboard, HelpPanelVirtualKeyboard }, + {"WHDLoad", "drive.ico", nullptr, nullptr, InitPanelWHDLoad, ExitPanelWHDLoad, RefreshPanelWHDLoad, HelpPanelWHDLoad}, {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr} }; @@ -248,28 +250,23 @@ static void show_help_requested() void cap_fps(Uint64 start) { - int refresh_rate; const auto end = SDL_GetPerformanceCounter(); const auto elapsed_ms = static_cast(end - start) / static_cast(SDL_GetPerformanceFrequency()) * 1000.0f; #ifdef USE_DISPMANX refresh_rate = 60; #else - refresh_rate = sdl_mode.refresh_rate; - if (refresh_rate < 50) refresh_rate = 50; - if (refresh_rate > 60) refresh_rate = 60; + const int refresh_rate = std::clamp(sdl_mode.refresh_rate, 50, 60); #endif - float d = 0.0f; - if (refresh_rate == 60) - d = floor(16.666f - elapsed_ms); - else - d = floor(20.000f - elapsed_ms); + const float frame_time = 1000.0f / refresh_rate; + const float delay_time = frame_time - elapsed_ms; - if( d > 0.0f ) SDL_Delay( Uint32(d) ); + if (delay_time > 0.0f) + SDL_Delay(static_cast(delay_time)); } void update_gui_screen() { - AmigaMonitor* mon = &AMonitors[0]; + const AmigaMonitor* mon = &AMonitors[0]; #ifdef USE_DISPMANX vc_dispmanx_resource_write_data(gui_resource, rgb_mode, gui_screen->pitch, gui_screen->pixels, &blit_rect); updateHandle = vc_dispmanx_update_start(0); @@ -557,7 +554,7 @@ void amiberry_gui_halt() void check_input() { - AmigaMonitor* mon = &AMonitors[0]; + const AmigaMonitor* mon = &AMonitors[0]; auto got_event = 0; didata* did = &di_joystick[0]; @@ -928,11 +925,7 @@ void check_input() //------------------------------------------------- // Send event to gui-controls //------------------------------------------------- -#ifdef ANDROID - androidsdl_event(gui_event, gui_input); -#else gui_input->pushInput(gui_event); -#endif } if (got_event) @@ -950,7 +943,7 @@ void check_input() void amiberry_gui_run() { - AmigaMonitor* mon = &AMonitors[0]; + const AmigaMonitor* mon = &AMonitors[0]; if (amiberry_options.gui_joystick_control) { @@ -1010,72 +1003,84 @@ class MainButtonActionListener : public gcn::ActionListener public: void action(const gcn::ActionEvent& actionEvent) override { - if (actionEvent.getSource() == cmdShutdown) + const auto source = actionEvent.getSource(); + if (source == cmdShutdown) { - // ------------------------------------------------ - // Shutdown the host (power off) - // ------------------------------------------------ - uae_quit(); - gui_running = false; - host_poweroff = true; + shutdown_host(); } + else if (source == cmdQuit) + { + quit_program(); + } + else if (source == cmdReset) + { + reset_amiga(); + } + else if (source == cmdRestart) + { + restart_emulator(); + } + else if (source == cmdStart) + { + start_emulation(); + } + else if (source == cmdHelp) + { + show_help(); + } + } +private: + static void shutdown_host() + { + uae_quit(); + gui_running = false; + host_poweroff = true; + } - if (actionEvent.getSource() == cmdQuit) + static void quit_program() + { + uae_quit(); + gui_running = false; + } + + static void reset_amiga() + { + uae_reset(1, 1); + gui_running = false; + } + + static void restart_emulator() + { + char tmp[MAX_DPATH]; + get_configuration_path(tmp, sizeof tmp); + if (strlen(last_loaded_config) > 0) + strncat(tmp, last_loaded_config, MAX_DPATH - 1); + else + { + strncat(tmp, OPTIONSFILENAME, MAX_DPATH - 1); + strncat(tmp, ".uae", MAX_DPATH - 10); + } + uae_restart(&changed_prefs, -1, tmp); + gui_running = false; + } + + static void start_emulation() + { + if (emulating && cmdStart->isEnabled()) { - //------------------------------------------------- - // Quit entire program via click on Quit-button - //------------------------------------------------- - uae_quit(); gui_running = false; } - else if (actionEvent.getSource() == cmdReset) + else { - //------------------------------------------------- - // Reset Amiga via click on Reset-button - //------------------------------------------------- - uae_reset(1, 1); + uae_reset(0, 1); gui_running = false; } - else if (actionEvent.getSource() == cmdRestart) - { - //------------------------------------------------- - // Restart emulator - //------------------------------------------------- - char tmp[MAX_DPATH]; - get_configuration_path(tmp, sizeof tmp); - if (strlen(last_loaded_config) > 0) - strncat(tmp, last_loaded_config, MAX_DPATH - 1); - else - { - strncat(tmp, OPTIONSFILENAME, MAX_DPATH - 1); - strncat(tmp, ".uae", MAX_DPATH - 10); - } - uae_restart(&changed_prefs, -1, tmp); - gui_running = false; - } - else if (actionEvent.getSource() == cmdStart) - { - if (emulating && cmdStart->isEnabled()) - { - //------------------------------------------------ - // Continue emulation - //------------------------------------------------ - gui_running = false; - } - else - { - //------------------------------------------------ - // First start of emulator -> reset Amiga - //------------------------------------------------ - uae_reset(0, 1); - gui_running = false; - } - } - else if (actionEvent.getSource() == cmdHelp) - { - show_help_requested(); - cmdHelp->requestFocus(); - } + } + + static void show_help() + { + show_help_requested(); + cmdHelp->requestFocus(); } }; diff --git a/src/osdep/target.h b/src/osdep/target.h index 8a624b4c..f23aef72 100644 --- a/src/osdep/target.h +++ b/src/osdep/target.h @@ -20,8 +20,8 @@ #define GETBDM(x) (((x) - (((x) / 10000) * 10000)) / 100) #define GETBDD(x) ((x) % 100) -#define AMIBERRYVERSION _T("Amiberry v5.6.9 (2024-09-09)") -#define AMIBERRYDATE MAKEBD(2024, 3, 9) +#define AMIBERRYVERSION _T("Amiberry v5.6.9 (2024-03-17)") +#define AMIBERRYDATE MAKEBD(2024, 3, 17) #define COPYRIGHT _T("Copyright (C) 2016-2024 Dimitris Panokostas") #define IHF_WINDOWHIDDEN 6 @@ -129,6 +129,7 @@ extern void get_rp9_path(char* out, int size); extern std::string get_screenshot_path(); extern void extract_filename(const char* str, char* buffer); +extern std::string extract_filename(const std::string& path); extern void extract_path(char* str, char* buffer); extern std::string extract_path(const std::string& filename); extern void remove_file_extension(char* filename); @@ -158,17 +159,13 @@ typedef struct extern std::vector lstAvailableROMs; -#define MAX_MRU_DISKLIST 40 +#define MAX_MRU_LIST 40 + extern std::vector lstMRUDiskList; -extern void AddFileToDiskList(const char* file, int moveToTop); - -#define MAX_MRU_CDLIST 10 extern std::vector lstMRUCDList; -extern void AddFileToCDList(const char* file, int moveToTop); - -#define MAX_MRU_WHDLOADLIST 10 extern std::vector lstMRUWhdloadList; -extern void AddFileToWHDLoadList(const char* file, int moveToTop); + +extern void add_file_to_mru_list(std::vector& vec, const std::string& file); int count_HDs(struct uae_prefs* p); extern void gui_force_rtarea_hdchange(void);