]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
od-unix: add platform support layer
authorStefan Reinauer <stefan.reinauer@coreboot.org>
Thu, 4 Jun 2026 14:57:41 +0000 (07:57 -0700)
committerStefan Reinauer <stefan.reinauer@coreboot.org>
Thu, 11 Jun 2026 21:08:30 +0000 (14:08 -0700)
Add the Unix target base layer for paths, threading, time, logging,
character conversion, memory mapping, filesystems, and hardfiles.

These files provide the host services used by the shared emulator core
through the existing target interfaces.

25 files changed:
od-unix/charset.cpp [new file with mode: 0644]
od-unix/config.cpp [new file with mode: 0644]
od-unix/config.h [new file with mode: 0644]
od-unix/filesys_host.cpp [new file with mode: 0644]
od-unix/gui.cpp [new file with mode: 0644]
od-unix/hardfile_host.cpp [new file with mode: 0644]
od-unix/host.h [new file with mode: 0644]
od-unix/logging.cpp [new file with mode: 0644]
od-unix/machdep/m68k.h [new file with mode: 0644]
od-unix/machdep/maccess.h [new file with mode: 0644]
od-unix/machdep/machdep.h [new file with mode: 0644]
od-unix/machdep/rpt.h [new file with mode: 0644]
od-unix/mman.cpp [new file with mode: 0644]
od-unix/path_expand.cpp [new file with mode: 0644]
od-unix/path_expand.h [new file with mode: 0644]
od-unix/path_expand_test.cpp [new file with mode: 0644]
od-unix/stubs.cpp [new file with mode: 0644]
od-unix/support.cpp [new file with mode: 0644]
od-unix/sysconfig.h [new file with mode: 0644]
od-unix/target.h [new file with mode: 0644]
od-unix/tchar.h [new file with mode: 0644]
od-unix/threaddep/thread.h [new file with mode: 0644]
od-unix/threading.cpp [new file with mode: 0644]
od-unix/threading_test.cpp [new file with mode: 0644]
od-unix/time.cpp [new file with mode: 0644]

diff --git a/od-unix/charset.cpp b/od-unix/charset.cpp
new file mode 100644 (file)
index 0000000..39eab93
--- /dev/null
@@ -0,0 +1,79 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "uae/string.h"
+
+static TCHAR *dup_tchar(const TCHAR *s)
+{
+    if (!s) {
+        s = _T("");
+    }
+    size_t len = _tcslen(s) + 1;
+    TCHAR *out = xmalloc(TCHAR, len);
+    memcpy(out, s, len * sizeof(TCHAR));
+    return out;
+}
+
+TCHAR *my_strdup_ansi(const char *s)
+{
+    return dup_tchar(s ? s : "");
+}
+
+TCHAR *au(const char *s) { return dup_tchar(s); }
+char *ua(const TCHAR *s) { return dup_tchar(s); }
+TCHAR *aucp(const char *s, unsigned int) { return au(s); }
+char *uacp(const TCHAR *s, unsigned int) { return ua(s); }
+TCHAR *au_fs(const char *s) { return au(s); }
+char *ua_fs(const TCHAR *s, int) { return ua(s); }
+char *uutf8(const TCHAR *s) { return ua(s); }
+TCHAR *utf8u(const char *s) { return au(s); }
+
+char *ua_copy(char *dst, int maxlen, const TCHAR *src)
+{
+    uae_tcslcpy(dst, src ? src : "", maxlen);
+    return dst;
+}
+
+TCHAR *au_copy(TCHAR *dst, int maxlen, const char *src)
+{
+    uae_tcslcpy(dst, src ? src : "", maxlen);
+    return dst;
+}
+
+char *ua_fs_copy(char *dst, int maxlen, const TCHAR *src, int)
+{
+    return ua_copy(dst, maxlen, src);
+}
+
+TCHAR *au_fs_copy(TCHAR *dst, int maxlen, const char *src)
+{
+    return au_copy(dst, maxlen, src);
+}
+
+void unicode_init(void)
+{
+}
+
+void to_lower(TCHAR *s, int len)
+{
+    for (int i = 0; s && s[i] && i < len; i++) {
+        s[i] = (TCHAR)_totlower((unsigned char)s[i]);
+    }
+}
+
+void to_upper(TCHAR *s, int len)
+{
+    for (int i = 0; s && s[i] && i < len; i++) {
+        s[i] = (TCHAR)_totupper((unsigned char)s[i]);
+    }
+}
+
+int uaestrlen(const char *s)
+{
+    return s ? (int)strlen(s) : 0;
+}
+
+int uaetcslen(const TCHAR *s)
+{
+    return s ? (int)_tcslen(s) : 0;
+}
diff --git a/od-unix/config.cpp b/od-unix/config.cpp
new file mode 100644 (file)
index 0000000..9248147
--- /dev/null
@@ -0,0 +1,799 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <ctype.h>
+#include <cstdlib>
+#include <string>
+#include <unistd.h>
+
+#include "options.h"
+#ifdef AVIOUTPUT
+#include "avioutput.h"
+extern int video_recording_active;
+static int unix_avi_audio_codec = AVIAUDIO_AVI;
+#endif
+#include "path_expand.h"
+#include "romscan.h"
+#include "savestate.h"
+#include "sound_unix.h"
+#include "uaeserial_unix.h"
+#ifdef WITH_MIDI
+#include "midi.h"
+#endif
+#include "uae/string.h"
+#include "uae.h"
+#include "zfile.h"
+
+TCHAR start_path_data[MAX_DPATH];
+TCHAR start_path_data_exe[MAX_DPATH];
+TCHAR start_path_plugins[MAX_DPATH];
+int saveimageoriginalpath;
+
+static TCHAR path_configuration[MAX_DPATH];
+static TCHAR path_nvram[MAX_DPATH];
+static TCHAR path_screenshot[MAX_DPATH];
+static TCHAR path_video[MAX_DPATH];
+static TCHAR path_saveimage[MAX_DPATH];
+static TCHAR path_ripper[MAX_DPATH];
+static TCHAR path_data[MAX_DPATH];
+static TCHAR path_rom[MAX_DPATH];
+static TCHAR uaeserial_ports[UNIX_UAESERIAL_MAX_UNITS][256];
+
+static std::string trim_copy(const std::string &s)
+{
+    size_t first = 0;
+    while (first < s.size() && isspace((unsigned char)s[first])) {
+        first++;
+    }
+    size_t last = s.size();
+    while (last > first && isspace((unsigned char)s[last - 1])) {
+        last--;
+    }
+    return s.substr(first, last - first);
+}
+
+static bool parse_path_option(const TCHAR *option, const TCHAR *value, const TCHAR *name, TCHAR *out, int out_size)
+{
+    if (_tcsicmp(option, name)) {
+        return false;
+    }
+    if (!value || !value[0]) {
+        out[0] = 0;
+        return true;
+    }
+    target_expand_environment(value, out, out_size);
+    fixtrailing(out);
+    return true;
+}
+
+static bool parse_rom_path_option(struct uae_prefs *p, const TCHAR *option, const TCHAR *value)
+{
+    if (!parse_path_option(option, value, _T("rom_path"), path_rom, sizeof path_rom / sizeof(TCHAR))) {
+        return false;
+    }
+    if (!p) {
+        return true;
+    }
+    if (!path_rom[0]) {
+        p->path_rom.path[0][0] = 0;
+        return true;
+    }
+    for (int i = 0; i < MAX_PATHS; i++) {
+        if (p->path_rom.path[i][0] == 0 || (i == 0 && (!_tcscmp(p->path_rom.path[i], _T(".\\")) || !_tcscmp(p->path_rom.path[i], _T("./"))))) {
+            uae_tcslcpy(p->path_rom.path[i], path_rom, sizeof p->path_rom.path[i] / sizeof(TCHAR));
+            target_multipath_modified(p);
+            return true;
+        }
+    }
+    uae_tcslcpy(p->path_rom.path[MAX_PATHS - 1], path_rom, sizeof p->path_rom.path[MAX_PATHS - 1] / sizeof(TCHAR));
+    target_multipath_modified(p);
+    return true;
+}
+
+static std::string lowercase_copy(std::string s)
+{
+    for (char &c : s) {
+        c = (char)tolower((unsigned char)c);
+    }
+    return s;
+}
+
+static bool file_exists(const std::string &path)
+{
+    return !path.empty() && access(path.c_str(), F_OK) == 0;
+}
+
+static int parse_bool_value(const std::string &value)
+{
+    std::string v = lowercase_copy(trim_copy(value));
+    return v == "1" || v == "true" || v == "yes" || v == "on";
+}
+
+static bool parse_int_value(const TCHAR *value, int *out)
+{
+    if (!value || !out) {
+        return false;
+    }
+    std::string text = trim_copy(value);
+    if (text.empty()) {
+        return false;
+    }
+    char *end = NULL;
+    long parsed = strtol(text.c_str(), &end, 0);
+    if (!end || *end != 0) {
+        return false;
+    }
+    *out = (int)parsed;
+    return true;
+}
+
+const TCHAR *unix_uaeserial_get_port(int unit)
+{
+    if (unit < 0 || unit >= UNIX_UAESERIAL_MAX_UNITS) {
+        return _T("");
+    }
+    return uaeserial_ports[unit];
+}
+
+void unix_uaeserial_set_port(int unit, const TCHAR *port)
+{
+    if (unit < 0 || unit >= UNIX_UAESERIAL_MAX_UNITS) {
+        return;
+    }
+    uae_tcslcpy(uaeserial_ports[unit], port ? port : _T(""), sizeof uaeserial_ports[unit] / sizeof(TCHAR));
+}
+
+static bool parse_uaeserial_port_option(const TCHAR *option, const TCHAR *value)
+{
+    const TCHAR *prefix = _T("uaeserial_port");
+    int unit = 0;
+
+    if (_tcsicmp(option, prefix)) {
+        size_t prefix_len = _tcslen(prefix);
+        if (_tcsnicmp(option, prefix, prefix_len)) {
+            return false;
+        }
+        const TCHAR *unit_text = option + prefix_len;
+        if (*unit_text == '_') {
+            unit_text++;
+        }
+        if (!*unit_text) {
+            unit = 0;
+        } else if (!parse_int_value(unit_text, &unit)) {
+            return false;
+        }
+    }
+    if (unit < 0 || unit >= UNIX_UAESERIAL_MAX_UNITS) {
+        write_log(_T("UAESER: ignoring unsupported Unix uaeserial unit %d\n"), unit);
+        return true;
+    }
+
+    std::string port = trim_copy(value ? value : "");
+    if (port.size() >= 2 &&
+        ((port[0] == '"' && port[port.size() - 1] == '"') || (port[0] == '\'' && port[port.size() - 1] == '\''))) {
+        port = port.substr(1, port.size() - 2);
+    }
+    if (lowercase_copy(port) == "none") {
+        port.clear();
+    }
+    unix_uaeserial_set_port(unit, port.c_str());
+    return true;
+}
+
+static int activity_priority_index_from_value(int value, int defpri)
+{
+    switch (value) {
+    case 1:
+        return 0;
+    case 0:
+        return 1;
+    case -1:
+        return 2;
+    case -2:
+        return 3;
+    default:
+        return defpri;
+    }
+}
+
+static const TCHAR *configmult[] = { _T("1x"), _T("2x"), _T("3x"), _T("4x"), _T("5x"), _T("6x"), _T("7x"), _T("8x"), NULL };
+static const TCHAR *uaescsimode[] = { _T("SCSIEMU"), _T("SPTI"), _T("SPTI+SCSISCAN"), NULL };
+
+int target_cfgfile_load(struct uae_prefs *p, const TCHAR *filename, int type, int isdefault)
+{
+    if (isdefault && type == CONFIG_TYPE_DEFAULT && !file_exists(filename)) {
+        return 1;
+    }
+    int loaded_type = type;
+    return cfgfile_load(p, filename, &loaded_type, 0, !isdefault);
+}
+
+int target_parse_option(struct uae_prefs *p, const TCHAR *option, const TCHAR *value, int)
+{
+    if (!_tcsicmp(option, _T("ui.recursive_roms"))) {
+        unix_romscan_set_recursive(parse_bool_value(value ? value : ""));
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("middle_mouse"))) {
+        if (parse_bool_value(value ? value : "")) {
+            p->input_mouse_untrap |= MOUSEUNTRAP_MIDDLEBUTTON;
+        } else {
+            p->input_mouse_untrap &= ~MOUSEUNTRAP_MIDDLEBUTTON;
+        }
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("active_not_captured_pause"))) {
+        p->win32_active_nocapture_pause = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("active_not_captured_nosound"))) {
+        p->win32_active_nocapture_nosound = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("inactive_pause"))) {
+        p->win32_inactive_pause = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("inactive_nosound"))) {
+        p->win32_inactive_nosound = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("iconified_pause"))) {
+        p->win32_iconified_pause = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("iconified_nosound"))) {
+        p->win32_iconified_nosound = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("active_input"))
+        || !_tcsicmp(option, _T("inactive_input"))
+        || !_tcsicmp(option, _T("iconified_input"))) {
+        int parsed = 0;
+        if (!parse_int_value(value, &parsed)) {
+            return 0;
+        }
+        if (!_tcsicmp(option, _T("active_input"))) {
+            p->win32_active_input = parsed;
+        } else if (!_tcsicmp(option, _T("inactive_input"))) {
+            p->win32_inactive_input = parsed;
+        } else {
+            p->win32_iconified_input = parsed;
+        }
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("active_priority"))
+        || !_tcsicmp(option, _T("activepriority"))
+        || !_tcsicmp(option, _T("inactive_priority"))
+        || !_tcsicmp(option, _T("iconified_priority"))) {
+        int parsed = 0;
+        if (!parse_int_value(value, &parsed)) {
+            return 0;
+        }
+        if (!_tcsicmp(option, _T("active_priority")) || !_tcsicmp(option, _T("activepriority"))) {
+            p->win32_active_capture_priority = activity_priority_index_from_value(parsed, 1);
+        } else if (!_tcsicmp(option, _T("inactive_priority"))) {
+            p->win32_inactive_priority = activity_priority_index_from_value(parsed, 1);
+        } else {
+            p->win32_iconified_priority = activity_priority_index_from_value(parsed, 2);
+        }
+        return 1;
+    }
+    if (cfgfile_intval(option, value, _T("recording_width"), &p->aviout_width, 1)
+        || cfgfile_intval(option, value, _T("recording_height"), &p->aviout_height, 1)
+        || cfgfile_intval(option, value, _T("recording_x"), &p->aviout_xoffset, 1)
+        || cfgfile_intval(option, value, _T("recording_y"), &p->aviout_yoffset, 1)
+        || cfgfile_intval(option, value, _T("screenshot_width"), &p->screenshot_width, 1)
+        || cfgfile_intval(option, value, _T("screenshot_height"), &p->screenshot_height, 1)
+        || cfgfile_intval(option, value, _T("screenshot_x"), &p->screenshot_xoffset, 1)
+        || cfgfile_intval(option, value, _T("screenshot_y"), &p->screenshot_yoffset, 1)
+        || cfgfile_intval(option, value, _T("screenshot_min_width"), &p->screenshot_min_width, 1)
+        || cfgfile_intval(option, value, _T("screenshot_min_height"), &p->screenshot_min_height, 1)
+        || cfgfile_intval(option, value, _T("screenshot_max_width"), &p->screenshot_max_width, 1)
+        || cfgfile_intval(option, value, _T("screenshot_max_height"), &p->screenshot_max_height, 1)
+        || cfgfile_intval(option, value, _T("screenshot_output_width_limit"), &p->screenshot_output_width, 1)
+        || cfgfile_intval(option, value, _T("screenshot_output_height_limit"), &p->screenshot_output_height, 1)) {
+        return 1;
+    }
+    if (cfgfile_strval(option, value, _T("uaescsimode"), &p->win32_uaescsimode, uaescsimode, 0)) {
+        return 1;
+    }
+    {
+        TCHAR tmpbuf[CONFIG_BLEN];
+        if (cfgfile_string(option, value, _T("rtg_vblank"), tmpbuf, sizeof tmpbuf / sizeof(TCHAR))) {
+            if (!_tcsicmp(tmpbuf, _T("real"))) {
+                p->win32_rtgvblankrate = -1;
+            } else if (!_tcsicmp(tmpbuf, _T("disabled"))) {
+                p->win32_rtgvblankrate = -2;
+            } else if (!_tcsicmp(tmpbuf, _T("chipset"))) {
+                p->win32_rtgvblankrate = 0;
+            } else {
+                p->win32_rtgvblankrate = _tstol(tmpbuf);
+            }
+            return 1;
+        }
+    }
+    if (cfgfile_strval(option, value, _T("screenshot_mult_width"), &p->screenshot_xmult, configmult, 0)
+        || cfgfile_strval(option, value, _T("screenshot_mult_height"), &p->screenshot_ymult, configmult, 0)) {
+        return 1;
+    }
+#ifdef AVIOUTPUT
+    if (!_tcsicmp(option, _T("screenshot_original_size")) || !_tcsicmp(option, _T("ui.screenshot_original_size"))) {
+        screenshot_originalsize = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("screenshot_paletted")) || !_tcsicmp(option, _T("ui.screenshot_paletted"))) {
+        screenshot_paletteindexed = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("screenshot_clip")) || !_tcsicmp(option, _T("ui.screenshot_clip"))) {
+        screenshot_clipmode = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("screenshot_auto")) || !_tcsicmp(option, _T("ui.screenshot_auto"))) {
+        screenshot_multi = parse_bool_value(value ? value : "") ? -1 : 0;
+        if (screenshot_multi) {
+            video_recording_active |= 2;
+        } else {
+            video_recording_active &= ~2;
+        }
+        return 1;
+    }
+    {
+        TCHAR tmpbuf[MAX_DPATH];
+        if (cfgfile_string_escape(option, value, _T("output_file"), tmpbuf, sizeof tmpbuf / sizeof(TCHAR)) ||
+            cfgfile_string_escape(option, value, _T("ui.output_file"), tmpbuf, sizeof tmpbuf / sizeof(TCHAR))) {
+            _tcsncpy(avioutput_filename_gui, tmpbuf, sizeof avioutput_filename_gui / sizeof(TCHAR) - 1);
+            avioutput_filename_gui[sizeof avioutput_filename_gui / sizeof(TCHAR) - 1] = 0;
+            return 1;
+        }
+    }
+    if (!_tcsicmp(option, _T("output_frame_limiter_disabled")) || !_tcsicmp(option, _T("ui.output_frame_limiter_disabled"))) {
+        avioutput_framelimiter = parse_bool_value(value ? value : "") ? 0 : 1;
+        if (!avioutput_framelimiter) {
+            avioutput_nosoundoutput = 1;
+        }
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("output_original_size")) || !_tcsicmp(option, _T("ui.output_original_size"))) {
+        avioutput_originalsize = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("output_no_sound")) || !_tcsicmp(option, _T("ui.output_no_sound"))) {
+        avioutput_nosoundoutput = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("output_no_sound_sync")) || !_tcsicmp(option, _T("ui.output_no_sound_sync"))) {
+        avioutput_nosoundsync = parse_bool_value(value ? value : "");
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("output_audio_codec")) || !_tcsicmp(option, _T("ui.output_audio_codec"))) {
+        if (value && (!_tcsicmp(value, _T("wav")) || !_tcsicmp(value, _T("wave")))) {
+            unix_avi_audio_codec = AVIAUDIO_WAV;
+        } else {
+            unix_avi_audio_codec = AVIAUDIO_AVI;
+        }
+        if (avioutput_audio) {
+            avioutput_audio = unix_avi_audio_codec;
+        }
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("output_video_codec")) || !_tcsicmp(option, _T("ui.output_video_codec"))) {
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("output_audio")) || !_tcsicmp(option, _T("ui.output_audio"))) {
+        avioutput_audio = parse_bool_value(value ? value : "") ? unix_avi_audio_codec : 0;
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("output_video")) || !_tcsicmp(option, _T("ui.output_video"))) {
+        avioutput_video = parse_bool_value(value ? value : "") ? 1 : 0;
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("output_enabled")) || !_tcsicmp(option, _T("ui.output_enabled"))) {
+        if (parse_bool_value(value ? value : "")) {
+            AVIOutput_Begin(false);
+        } else {
+            AVIOutput_End();
+        }
+        return 1;
+    }
+#endif
+    if (!_tcsicmp(option, _T("serial_port"))) {
+        std::string port = trim_copy(value ? value : "");
+        if (port.size() >= 2 &&
+            ((port[0] == '"' && port[port.size() - 1] == '"') || (port[0] == '\'' && port[port.size() - 1] == '\''))) {
+            port = port.substr(1, port.size() - 2);
+        }
+        if (lowercase_copy(port) == "none") {
+            port.clear();
+        }
+        uae_tcslcpy(p->sername, port.c_str(), sizeof p->sername / sizeof(TCHAR));
+        p->use_serial = p->sername[0] != 0;
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("parallel_port"))) {
+        std::string port = trim_copy(value ? value : "");
+        if (port.size() >= 2 &&
+            ((port[0] == '"' && port[port.size() - 1] == '"') || (port[0] == '\'' && port[port.size() - 1] == '\''))) {
+            port = port.substr(1, port.size() - 2);
+        }
+        if (lowercase_copy(port) == "none") {
+            port.clear();
+        } else if (lowercase_copy(port) == "default") {
+            port = DEFPRTNAME;
+        }
+        uae_tcslcpy(p->prtname, port.c_str(), sizeof p->prtname / sizeof(TCHAR));
+        return 1;
+    }
+    if (parse_uaeserial_port_option(option, value)) {
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("soundcard"))) {
+        int parsed = 0;
+        if (!parse_int_value(value, &parsed)) {
+            return 0;
+        }
+        if (parsed < 0 || parsed >= unix_sound_device_count()) {
+            parsed = 0;
+        }
+        p->win32_soundcard = parsed;
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("soundcardname"))) {
+        int index = unix_sound_device_index_from_config_name(value);
+        if (index >= 0) {
+            p->win32_soundcard = index;
+        }
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("samplersoundcard")) || !_tcsicmp(option, _T("sampler_soundcard"))) {
+        int parsed = 0;
+        if (!parse_int_value(value, &parsed)) {
+            return 0;
+        }
+        if (parsed < 0 || parsed >= unix_sampler_device_count()) {
+            parsed = -1;
+        }
+        p->win32_samplersoundcard = parsed;
+        return 1;
+    }
+    if (!_tcsicmp(option, _T("samplersoundcardname")) || !_tcsicmp(option, _T("sampler_soundcardname"))) {
+        int index = unix_sampler_device_index_from_config_name(value);
+        if (index >= 0) {
+            p->win32_samplersoundcard = index;
+        }
+        return 1;
+    }
+    if (cfgfile_intval(option, value, _T("midi_device"), &p->win32_midioutdev, 1)
+        || cfgfile_intval(option, value, _T("midiout_device"), &p->win32_midioutdev, 1)) {
+        return 1;
+    }
+    if (cfgfile_intval(option, value, _T("midiin_device"), &p->win32_midiindev, 1)) {
+#ifndef WITH_MIDI
+        p->win32_midiindev = -1;
+#endif
+        return 1;
+    }
+    if (cfgfile_yesno(option, value, _T("midirouter"), &p->win32_midirouter)) {
+#ifndef WITH_MIDI
+        p->win32_midirouter = false;
+#endif
+        return 1;
+    }
+    TCHAR tmpbuf[256];
+    if (cfgfile_string_escape(option, value, _T("midiout_device_name"), tmpbuf, sizeof tmpbuf / sizeof(TCHAR))) {
+#ifdef WITH_MIDI
+        p->win32_midioutdev = unix_midi_output_device_id_from_config_name(tmpbuf);
+#else
+        p->win32_midioutdev = !_tcsicmp(tmpbuf, _T("default")) ? -1 : -2;
+#endif
+        return 1;
+    }
+    if (cfgfile_string_escape(option, value, _T("midiin_device_name"), tmpbuf, sizeof tmpbuf / sizeof(TCHAR))) {
+#ifdef WITH_MIDI
+        p->win32_midiindev = unix_midi_input_device_id_from_config_name(tmpbuf);
+#else
+        p->win32_midiindev = -1;
+#endif
+        return 1;
+    }
+    if (parse_path_option(option, value, _T("config_path"), path_configuration, sizeof path_configuration / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("ui.config_path"), path_configuration, sizeof path_configuration / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("nvram_path"), path_nvram, sizeof path_nvram / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("ui.nvram_path"), path_nvram, sizeof path_nvram / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("screenshot_path"), path_screenshot, sizeof path_screenshot / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("ui.screenshot_path"), path_screenshot, sizeof path_screenshot / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("video_path"), path_video, sizeof path_video / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("ui.video_path"), path_video, sizeof path_video / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("saveimage_path"), path_saveimage, sizeof path_saveimage / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("ui.saveimage_path"), path_saveimage, sizeof path_saveimage / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("rip_path"), path_ripper, sizeof path_ripper / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("ripper_path"), path_ripper, sizeof path_ripper / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("ui.rip_path"), path_ripper, sizeof path_ripper / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("data_path"), path_data, sizeof path_data / sizeof(TCHAR))
+        || parse_path_option(option, value, _T("ui.data_path"), path_data, sizeof path_data / sizeof(TCHAR))
+        || parse_rom_path_option(p, option, value)) {
+        return 1;
+    }
+    return 0;
+}
+
+void target_save_options(struct zfile *f, struct uae_prefs *p)
+{
+    cfgfile_target_dwrite_str(f, _T("serial_port"), p->sername[0] ? p->sername : _T("none"));
+    cfgfile_target_dwrite_str_escape(f, _T("parallel_port"), p->prtname[0] ? p->prtname : _T("none"));
+    for (int i = 0; i < UNIX_UAESERIAL_MAX_UNITS; i++) {
+        if (uaeserial_ports[i][0]) {
+            TCHAR option[64];
+            _stprintf(option, _T("uaeserial_port%d"), i);
+            cfgfile_target_write_str(f, option, uaeserial_ports[i]);
+        }
+    }
+
+    int index = p->win32_soundcard;
+    if (index < 0 || index >= unix_sound_device_count()) {
+        index = 0;
+    }
+    cfgfile_target_write(f, _T("soundcard"), _T("%d"), index);
+    const TCHAR *name = unix_sound_device_config_name(index);
+    if (name && name[0]) {
+        cfgfile_target_write_str(f, _T("soundcardname"), name);
+    }
+    if (p->win32_samplersoundcard >= 0 && p->win32_samplersoundcard < unix_sampler_device_count()) {
+        cfgfile_target_write(f, _T("samplersoundcard"), _T("%d"), p->win32_samplersoundcard);
+        const TCHAR *sampler_name = unix_sampler_device_config_name(p->win32_samplersoundcard);
+        if (sampler_name && sampler_name[0]) {
+            cfgfile_target_write_str(f, _T("samplersoundcardname"), sampler_name);
+        }
+    }
+    cfgfile_target_dwrite(f, _T("midiout_device"), _T("%d"), p->win32_midioutdev);
+    cfgfile_target_dwrite(f, _T("midiin_device"), _T("%d"), p->win32_midiindev);
+#ifdef WITH_MIDI
+    cfgfile_target_dwrite_str_escape(f, _T("midiout_device_name"), unix_midi_output_device_config_name_for_id(p->win32_midioutdev));
+    cfgfile_target_dwrite_str_escape(f, _T("midiin_device_name"), unix_midi_input_device_config_name_for_id(p->win32_midiindev));
+#else
+    cfgfile_target_dwrite_str_escape(f, _T("midiout_device_name"), p->win32_midioutdev == -1 ? _T("default") : _T("none"));
+    cfgfile_target_dwrite_str_escape(f, _T("midiin_device_name"), _T("none"));
+#endif
+    cfgfile_target_dwrite_bool(f, _T("midirouter"), p->win32_midirouter);
+    int scsimode = p->win32_uaescsimode;
+    if (scsimode < 0 || scsimode > UAESCSI_LAST) {
+        scsimode = UAESCSI_SPTI;
+    }
+    cfgfile_target_dwrite_str(f, _T("uaescsimode"), uaescsimode[scsimode]);
+    if (p->win32_rtgvblankrate <= 0) {
+        cfgfile_target_dwrite_str(f, _T("rtg_vblank"),
+            p->win32_rtgvblankrate == -1 ? _T("real") : (p->win32_rtgvblankrate == -2 ? _T("disabled") : _T("chipset")));
+    } else {
+        cfgfile_target_dwrite(f, _T("rtg_vblank"), _T("%d"), p->win32_rtgvblankrate);
+    }
+    cfgfile_target_dwrite(f, _T("recording_width"), _T("%d"), p->aviout_width);
+    cfgfile_target_dwrite(f, _T("recording_height"), _T("%d"), p->aviout_height);
+    cfgfile_target_dwrite(f, _T("recording_x"), _T("%d"), p->aviout_xoffset);
+    cfgfile_target_dwrite(f, _T("recording_y"), _T("%d"), p->aviout_yoffset);
+    cfgfile_target_dwrite(f, _T("screenshot_width"), _T("%d"), p->screenshot_width);
+    cfgfile_target_dwrite(f, _T("screenshot_height"), _T("%d"), p->screenshot_height);
+    cfgfile_target_dwrite(f, _T("screenshot_x"), _T("%d"), p->screenshot_xoffset);
+    cfgfile_target_dwrite(f, _T("screenshot_y"), _T("%d"), p->screenshot_yoffset);
+    cfgfile_target_dwrite(f, _T("screenshot_min_width"), _T("%d"), p->screenshot_min_width);
+    cfgfile_target_dwrite(f, _T("screenshot_min_height"), _T("%d"), p->screenshot_min_height);
+    cfgfile_target_dwrite(f, _T("screenshot_max_width"), _T("%d"), p->screenshot_max_width);
+    cfgfile_target_dwrite(f, _T("screenshot_max_height"), _T("%d"), p->screenshot_max_height);
+    cfgfile_target_dwrite(f, _T("screenshot_output_width_limit"), _T("%d"), p->screenshot_output_width);
+    cfgfile_target_dwrite(f, _T("screenshot_output_height_limit"), _T("%d"), p->screenshot_output_height);
+    cfgfile_target_dwrite_str(f, _T("screenshot_mult_width"), configmult[p->screenshot_xmult]);
+    cfgfile_target_dwrite_str(f, _T("screenshot_mult_height"), configmult[p->screenshot_ymult]);
+#ifdef AVIOUTPUT
+    cfgfile_target_dwrite_bool(f, _T("screenshot_original_size"), screenshot_originalsize != 0);
+    cfgfile_target_dwrite_bool(f, _T("screenshot_paletted"), screenshot_paletteindexed != 0);
+    cfgfile_target_dwrite_bool(f, _T("screenshot_clip"), screenshot_clipmode != 0);
+    cfgfile_target_dwrite_bool(f, _T("screenshot_auto"), screenshot_multi != 0);
+    if (avioutput_filename_gui[0]) {
+        cfgfile_target_dwrite_str_escape(f, _T("output_file"), avioutput_filename_gui);
+    }
+    cfgfile_target_dwrite_bool(f, _T("output_frame_limiter_disabled"), avioutput_framelimiter == 0);
+    cfgfile_target_dwrite_bool(f, _T("output_original_size"), avioutput_originalsize != 0);
+    cfgfile_target_dwrite_bool(f, _T("output_no_sound"), avioutput_nosoundoutput != 0);
+    cfgfile_target_dwrite_bool(f, _T("output_no_sound_sync"), avioutput_nosoundsync != 0);
+    cfgfile_target_write_str(f, _T("output_audio_codec"),
+        avioutput_audio == AVIAUDIO_WAV ? _T("wav") : _T("pcm"));
+    cfgfile_target_write_str(f, _T("output_video_codec"), _T("dib"));
+    cfgfile_target_dwrite_bool(f, _T("output_audio"), avioutput_audio != 0);
+    cfgfile_target_dwrite_bool(f, _T("output_video"), avioutput_video != 0);
+    cfgfile_target_dwrite_bool(f, _T("output_enabled"), avioutput_requested != 0);
+#endif
+    if (path_configuration[0]) {
+        cfgfile_target_write_str(f, _T("config_path"), path_configuration);
+    }
+    if (path_nvram[0]) {
+        cfgfile_target_write_str(f, _T("nvram_path"), path_nvram);
+    }
+    if (path_screenshot[0]) {
+        cfgfile_target_write_str(f, _T("screenshot_path"), path_screenshot);
+    }
+    if (path_video[0]) {
+        cfgfile_target_write_str(f, _T("video_path"), path_video);
+    }
+    if (path_saveimage[0]) {
+        cfgfile_target_write_str(f, _T("saveimage_path"), path_saveimage);
+    }
+    if (path_ripper[0]) {
+        cfgfile_target_write_str(f, _T("rip_path"), path_ripper);
+    }
+    if (path_data[0]) {
+        cfgfile_target_write_str(f, _T("data_path"), path_data);
+    }
+    if (path_rom[0]) {
+        cfgfile_target_write_str(f, _T("rom_path"), path_rom);
+    }
+}
+
+void target_default_options(struct uae_prefs *p, int)
+{
+    path_configuration[0] = 0;
+    path_nvram[0] = 0;
+    path_screenshot[0] = 0;
+    path_video[0] = 0;
+    path_saveimage[0] = 0;
+    path_ripper[0] = 0;
+    path_data[0] = 0;
+    path_rom[0] = 0;
+    for (int i = 0; i < UNIX_UAESERIAL_MAX_UNITS; i++) {
+        uaeserial_ports[i][0] = 0;
+    }
+    unix_romscan_mark_dirty();
+    p->rtg_dacswitch = true;
+    p->rtg_hardwaresprite = true;
+    p->win32_rtgvblankrate = 0;
+    p->win32_samplersoundcard = -1;
+    p->win32_midioutdev = -2;
+    p->win32_midiindev = -1;
+    p->win32_midirouter = false;
+}
+
+void target_fixup_options(struct uae_prefs *p)
+{
+#ifndef WITH_MIDI
+    p->win32_midiindev = -1;
+    p->win32_midirouter = false;
+#endif
+    if (p->win32_uaescsimode > UAESCSI_LAST) {
+        p->win32_uaescsimode = UAESCSI_SPTI;
+    }
+    unix_romscan_refresh(p, false);
+}
+
+void target_multipath_modified(struct uae_prefs*)
+{
+    unix_romscan_mark_dirty();
+}
+
+bool target_isrelativemode(void)
+{
+    return false;
+}
+
+TCHAR *target_expand_environment(const TCHAR *path, TCHAR *out, int maxlen)
+{
+    if (!path) {
+        return NULL;
+    }
+
+    std::string expanded = unix_expand_path(path);
+    if (out) {
+        uae_tcslcpy(out, expanded.c_str(), maxlen);
+        return out;
+    }
+    return my_strdup(expanded.c_str());
+}
+
+bool get_plugin_path(TCHAR *out, int size, const TCHAR *path)
+{
+    uae_tcslcpy(out, path, size);
+    return true;
+}
+
+void stripslashes(TCHAR *p)
+{
+    while (*p) {
+        if (*p == '\\') {
+            *p = '/';
+        }
+        p++;
+    }
+}
+
+void fixtrailing(TCHAR *p)
+{
+    int len = _tcslen(p);
+    if (len > 0 && p[len - 1] != '/') {
+        _tcscat(p, "/");
+    }
+}
+
+void fullpath(TCHAR *path, int size)
+{
+    fullpath(path, size, false);
+}
+
+void fullpath(TCHAR *path, int size, bool)
+{
+    if (!path || !path[0]) {
+        return;
+    }
+    const std::string absolute = unix_absolute_path(path);
+    uae_tcslcpy(path, absolute.c_str(), size);
+}
+
+void getpathpart(TCHAR *outpath, int size, const TCHAR *inpath)
+{
+    uae_tcslcpy(outpath, inpath, size);
+    TCHAR *slash = _tcsrchr(outpath, '/');
+    if (slash) {
+        slash[1] = 0;
+    } else {
+        outpath[0] = 0;
+    }
+}
+
+void getfilepart(TCHAR *out, int size, const TCHAR *path)
+{
+    const TCHAR *slash = _tcsrchr(path, '/');
+    uae_tcslcpy(out, slash ? slash + 1 : path, size);
+}
+
+bool samepath(const TCHAR *p1, const TCHAR *p2)
+{
+    return _tcscmp(p1, p2) == 0;
+}
+
+static void fetch_home_path(TCHAR *out, int size)
+{
+    const char *home = getenv("HOME");
+    uae_tcslcpy(out, home ? home : ".", size);
+    fixtrailing(out);
+}
+
+static void fetch_user_data_path(TCHAR *out, int size, const char *subdir)
+{
+    if (!out || size <= 0) {
+        return;
+    }
+    const char *home = getenv("HOME");
+    const char *base = home ? home : ".";
+    if (subdir && subdir[0]) {
+        snprintf(out, (size_t)size, "%s/Documents/WinUAE/%s", base, subdir);
+    } else {
+        snprintf(out, (size_t)size, "%s/Documents/WinUAE", base);
+    }
+    fixtrailing(out);
+}
+
+static void fetch_user_data_path_override(TCHAR *out, int size, const TCHAR *override_path, const char *subdir)
+{
+    if (override_path && override_path[0]) {
+        uae_tcslcpy(out, override_path, size);
+        fixtrailing(out);
+    } else {
+        fetch_user_data_path(out, size, subdir);
+    }
+}
+
+void fetch_saveimagepath(TCHAR *out, int size, int) { fetch_user_data_path_override(out, size, path_saveimage, "SaveImages"); }
+void fetch_configurationpath(TCHAR *out, int size) { fetch_user_data_path_override(out, size, path_configuration, "Configuration"); }
+void fetch_nvrampath(TCHAR *out, int size) { fetch_user_data_path_override(out, size, path_nvram, "NVRAMs"); }
+void fetch_luapath(TCHAR *out, int size) { fetch_home_path(out, size); }
+void fetch_screenshotpath(TCHAR *out, int size) { fetch_user_data_path_override(out, size, path_screenshot, "Screenshots"); }
+void fetch_ripperpath(TCHAR *out, int size) { fetch_user_data_path_override(out, size, path_ripper, "Rips"); }
+void fetch_statefilepath(TCHAR *out, int size) { fetch_user_data_path_override(out, size, path_statefile, "Save States"); }
+void fetch_inputfilepath(TCHAR *out, int size) { fetch_home_path(out, size); }
+void fetch_datapath(TCHAR *out, int size) { fetch_user_data_path_override(out, size, path_data, NULL); }
+void fetch_rompath(TCHAR *out, int size) { fetch_user_data_path_override(out, size, path_rom, "Kickstarts"); }
+void fetch_videopath(TCHAR *out, int size) { fetch_user_data_path_override(out, size, path_video, "Videos"); }
+
+void target_getdate(int *y, int *m, int *d)
+{
+    *y = 2026;
+    *m = 5;
+    *d = 10;
+}
diff --git a/od-unix/config.h b/od-unix/config.h
new file mode 100644 (file)
index 0000000..3cd9893
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef WINUAE_OD_UNIX_CONFIG_H
+#define WINUAE_OD_UNIX_CONFIG_H
+
+#include "sysconfig.h"
+
+#endif /* WINUAE_OD_UNIX_CONFIG_H */
diff --git a/od-unix/filesys_host.cpp b/od-unix/filesys_host.cpp
new file mode 100644 (file)
index 0000000..ccfe502
--- /dev/null
@@ -0,0 +1,315 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "fsdb.h"
+#include "fsusage.h"
+#include "uae/io.h"
+#include "uae/string.h"
+#include "zfile.h"
+
+struct my_opendir_s {
+    DIR *dir;
+};
+
+struct my_openfile_s {
+    int fd;
+};
+
+bool my_issamepath(const TCHAR *path1, const TCHAR *path2);
+
+static uae_u32 mode_from_stat(const struct stat &st)
+{
+    uae_u32 mode = 0;
+    if (S_ISDIR(st.st_mode)) {
+        mode |= FILEFLAG_DIR;
+    }
+    if (st.st_mode & S_IRUSR) {
+        mode |= FILEFLAG_READ;
+    }
+    if (st.st_mode & S_IWUSR) {
+        mode |= FILEFLAG_WRITE;
+    }
+    if (st.st_mode & S_IXUSR) {
+        mode |= FILEFLAG_EXECUTE;
+    }
+    return mode;
+}
+
+FILE *uae_tfopen(const TCHAR *path, const TCHAR *mode)
+{
+    return uae_unix_tfopen(path, mode);
+}
+
+int dos_errno(void)
+{
+    switch (errno) {
+    case 0: return 0;
+    case ENOENT: return ERROR_OBJECT_NOT_AROUND;
+    case EACCES:
+    case EPERM: return ERROR_WRITE_PROTECTED;
+    case ENOTDIR: return ERROR_DIR_NOT_FOUND;
+    case EEXIST: return ERROR_OBJECT_EXISTS;
+    case ENOSPC: return ERROR_DISK_IS_FULL;
+    case ENOTEMPTY: return ERROR_DIRECTORY_NOT_EMPTY;
+    default: return ERROR_OBJECT_NOT_AROUND;
+    }
+}
+
+my_opendir_s *my_opendir(const TCHAR *name, const TCHAR *)
+{
+    DIR *dir = opendir(name);
+    if (!dir) {
+        return NULL;
+    }
+    my_opendir_s *out = xcalloc(my_opendir_s, 1);
+    out->dir = dir;
+    return out;
+}
+
+my_opendir_s *my_opendir(const TCHAR *name)
+{
+    return my_opendir(name, _T("*"));
+}
+
+void my_closedir(my_opendir_s *mod)
+{
+    if (!mod) {
+        return;
+    }
+    if (mod->dir) {
+        closedir(mod->dir);
+    }
+    xfree(mod);
+}
+
+int my_readdir(my_opendir_s *mod, TCHAR *name)
+{
+    if (!mod || !mod->dir || !name) {
+        return 0;
+    }
+    for (;;) {
+        struct dirent *de = readdir(mod->dir);
+        if (!de) {
+            return 0;
+        }
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+            continue;
+        }
+        uae_tcslcpy(name, de->d_name, MAX_DPATH);
+        return 1;
+    }
+}
+
+int my_rmdir(const TCHAR *name) { return rmdir(name); }
+int my_mkdir(const TCHAR *name) { return mkdir(name, 0777); }
+int my_unlink(const TCHAR *name, bool) { return unlink(name); }
+int my_rename(const TCHAR *oldname, const TCHAR *newname) { return rename(oldname, newname); }
+int my_truncate(const TCHAR *name, uae_u64 len) { return truncate(name, (off_t)len); }
+
+my_openfile_s *my_open(const TCHAR *name, int flags)
+{
+    int fd = open(name, flags, 0666);
+    if (fd < 0) {
+        return NULL;
+    }
+    my_openfile_s *out = xcalloc(my_openfile_s, 1);
+    out->fd = fd;
+    return out;
+}
+
+void my_close(my_openfile_s *mos)
+{
+    if (!mos) {
+        return;
+    }
+    if (mos->fd >= 0) {
+        close(mos->fd);
+    }
+    xfree(mos);
+}
+
+uae_s64 my_lseek(my_openfile_s *mos, uae_s64 offset, int whence)
+{
+    return mos ? (uae_s64)lseek(mos->fd, (off_t)offset, whence) : -1;
+}
+
+uae_s64 my_fsize(my_openfile_s *mos)
+{
+    struct stat st;
+    if (!mos || fstat(mos->fd, &st) != 0) {
+        return -1;
+    }
+    return (uae_s64)st.st_size;
+}
+
+unsigned int my_read(my_openfile_s *mos, void *b, unsigned int size)
+{
+    ssize_t ret = mos ? read(mos->fd, b, size) : -1;
+    return ret < 0 ? 0 : (unsigned int)ret;
+}
+
+unsigned int my_write(my_openfile_s *mos, void *b, unsigned int size)
+{
+    ssize_t ret = mos ? write(mos->fd, b, size) : -1;
+    return ret < 0 ? 0 : (unsigned int)ret;
+}
+
+int my_existsfile(const TCHAR *name)
+{
+    struct stat st;
+    return name && stat(name, &st) == 0 && S_ISREG(st.st_mode);
+}
+
+int my_existsdir(const TCHAR *name)
+{
+    struct stat st;
+    return name && stat(name, &st) == 0 && S_ISDIR(st.st_mode);
+}
+
+bool my_existsfiledir(const TCHAR *name)
+{
+    struct stat st;
+    return name && stat(name, &st) == 0;
+}
+
+FILE *my_opentext(const TCHAR *name)
+{
+    return uae_tfopen(name, _T("r"));
+}
+
+bool my_stat(const TCHAR *name, struct mystat *ms)
+{
+    struct stat st;
+    if (!name || !ms || stat(name, &st) != 0) {
+        return false;
+    }
+    ms->size = (uae_s64)st.st_size;
+    ms->mode = mode_from_stat(st);
+    ms->mtime.tv_sec = (uae_s64)st.st_mtime;
+    ms->mtime.tv_usec = 0;
+    return true;
+}
+
+bool my_utime(const TCHAR *name, struct mytimeval *tv)
+{
+    if (!name) {
+        return false;
+    }
+    if (!tv) {
+        return utimes(name, NULL) == 0;
+    }
+    struct timeval times[2];
+    times[0].tv_sec = (time_t)tv->tv_sec;
+    times[0].tv_usec = tv->tv_usec;
+    times[1] = times[0];
+    return utimes(name, times) == 0;
+}
+
+bool my_chmod(const TCHAR *name, uae_u32 mode)
+{
+    struct stat st;
+    if (!name || stat(name, &st) != 0) {
+        return false;
+    }
+    mode_t m = st.st_mode;
+    if (mode & FILEFLAG_WRITE) {
+        m |= S_IWUSR;
+    } else {
+        m &= ~S_IWUSR;
+    }
+    return chmod(name, m) == 0;
+}
+
+bool my_resolveshortcut(TCHAR *, int) { return false; }
+bool my_resolvessymboliclink(TCHAR *, int) { return false; }
+bool my_resolvesoftlink(TCHAR *, int, bool) { return false; }
+bool my_isfilehidden(const TCHAR *path) { return path && path[0] == '.'; }
+void my_setfilehidden(const TCHAR *, bool) {}
+int my_readonlyfile(const TCHAR *path)
+{
+    return access(path, W_OK) != 0;
+}
+
+const TCHAR *my_getfilepart(const TCHAR *filename)
+{
+    const TCHAR *slash = filename ? _tcsrchr(filename, FSDB_DIR_SEPARATOR) : NULL;
+    return slash ? slash + 1 : filename;
+}
+
+void my_canonicalize_path(const TCHAR *path, TCHAR *out, int size)
+{
+    if (!path || !out || size <= 0) {
+        return;
+    }
+    char resolved[PATH_MAX];
+    if (realpath(path, resolved)) {
+        uae_tcslcpy(out, resolved, size);
+    } else {
+        uae_tcslcpy(out, path, size);
+    }
+}
+
+int my_setcurrentdir(const TCHAR *curdir, TCHAR *oldcur)
+{
+    if (oldcur) {
+        if (!getcwd(oldcur, MAX_DPATH)) {
+            oldcur[0] = 0;
+        }
+    }
+    return curdir ? chdir(curdir) : -1;
+}
+
+int my_issamevolume(const TCHAR *path1, const TCHAR *path2, TCHAR *path)
+{
+    if (path && path1) {
+        uae_tcslcpy(path, path1, MAX_DPATH);
+    }
+    return my_issamepath(path1, path2);
+}
+
+bool my_issamepath(const TCHAR *path1, const TCHAR *path2)
+{
+    char r1[PATH_MAX], r2[PATH_MAX];
+    const char *p1 = realpath(path1, r1) ? r1 : path1;
+    const char *p2 = realpath(path2, r2) ? r2 : path2;
+    return p1 && p2 && !_tcscmp(p1, p2);
+}
+
+bool my_createsoftlink(const TCHAR *path, const TCHAR *target)
+{
+    return symlink(target, path) == 0;
+}
+
+bool my_createshortcut(const TCHAR *, const TCHAR *, const TCHAR *) { return false; }
+void makesafefilename(TCHAR *s, bool)
+{
+    for (; s && *s; s++) {
+        if (*s == '/' || *s == ':') {
+            *s = '_';
+        }
+    }
+}
+
+int my_getvolumeinfo(const TCHAR *)
+{
+    return 0;
+}
+
+int get_fs_usage(const TCHAR *path, const TCHAR *, struct fs_usage *fsp)
+{
+    struct statvfs svfs;
+    if (!path || !fsp || statvfs(path, &svfs) != 0) {
+        return -1;
+    }
+    uae_u64 bsize = svfs.f_frsize ? svfs.f_frsize : svfs.f_bsize;
+    fsp->total = (uae_u64)svfs.f_blocks * bsize / 512;
+    fsp->avail = (uae_u64)svfs.f_bavail * bsize / 512;
+    return 0;
+}
diff --git a/od-unix/gui.cpp b/od-unix/gui.cpp
new file mode 100644 (file)
index 0000000..d4365b2
--- /dev/null
@@ -0,0 +1,367 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "options.h"
+#include "traps.h"
+#include "custom.h"
+#include "inputdevice.h"
+#include "gui.h"
+#include "target_main.h"
+#include "savestate.h"
+#include "sounddep/sound.h"
+
+#ifdef WINUAE_UNIX_WITH_INTEGRATED_QT_UI
+#include "qt/launcher_bridge.h"
+#endif
+
+unsigned int gui_ledstate;
+
+static int unix_gui_argc;
+static TCHAR **unix_gui_argv;
+
+void target_main_set_args(int argc, TCHAR **argv)
+{
+    unix_gui_argc = argc;
+    unix_gui_argv = argv;
+}
+
+int target_main_handle_early(int argc, TCHAR **argv)
+{
+#ifdef WINUAE_UNIX_WITH_INTEGRATED_QT_UI
+    for (int i = 1; i < argc; i++) {
+        if (!_tcscmp(argv[i], _T("--qt-board-catalog"))) {
+            return runWinUaeQtBoardCatalogDump();
+        }
+    }
+#endif
+    return -1;
+}
+
+int gui_init(void)
+{
+#ifdef WINUAE_UNIX_WITH_INTEGRATED_QT_UI
+    const int action = runWinUaeQtLauncherForPrefs(unix_gui_argc, unix_gui_argv, &changed_prefs, 0);
+    if (action == WINUAE_QT_LAUNCHER_START) {
+        return 1;
+    }
+    if (action == WINUAE_QT_LAUNCHER_ERROR) {
+        return -1;
+    }
+    return -2;
+#else
+    return 0;
+#endif
+}
+int gui_update(void) { return 1; }
+void gui_exit(void) {}
+void gui_led(int led, int on, int brightness)
+{
+    if (on >= 0 && led >= 0 && led < int(sizeof(gui_ledstate) * 8)) {
+        if (on) {
+            gui_ledstate |= 1u << led;
+        } else {
+            gui_ledstate &= ~(1u << led);
+        }
+    }
+    if (led == LED_POWER && brightness >= 0) {
+        gui_data.powerled_brightness = brightness;
+    }
+}
+void gui_filename(int, const TCHAR*) {}
+void gui_fps(int fps, int lines, bool lace, int idle, int color)
+{
+    gui_data.fps = fps;
+    gui_data.lines = lines;
+    gui_data.lace = lace;
+    gui_data.idle = idle;
+    gui_data.fps_color = color;
+    gui_led(LED_FPS, 1, -1);
+    gui_led(LED_LINES, 1, -1);
+    gui_led(LED_CPU, 1, -1);
+    gui_led(LED_SND, (gui_data.sndbuf_status > 1 || gui_data.sndbuf_status < 0) ? 0 : 1, -1);
+}
+void gui_lock(void) {}
+void gui_unlock(void) {}
+
+static void gui_flicker_led_single(int led, int status)
+{
+    static int resetcounter[LED_MAX];
+    uae_s8 *target = nullptr;
+
+    if (led == LED_HD) {
+        target = &gui_data.hd;
+    } else if (led == LED_CD) {
+        target = &gui_data.cd;
+    } else if (led == LED_MD) {
+        target = &gui_data.md;
+    } else if (led == LED_NET) {
+        target = &gui_data.net;
+    }
+    if (!target) {
+        return;
+    }
+
+    const uae_s8 old = *target;
+    if (status < 0) {
+        gui_led(led, old < 0 ? -1 : 0, -1);
+        return;
+    }
+    if (status == 0 && old < 0) {
+        *target = 0;
+        resetcounter[led] = 0;
+        gui_led(led, 0, -1);
+        return;
+    }
+    if (status == 0) {
+        resetcounter[led]--;
+        if (resetcounter[led] > 0) {
+            return;
+        }
+    }
+
+    *target = status;
+    resetcounter[led] = 15;
+    if (old != *target) {
+        gui_led(led, *target, -1);
+    }
+}
+
+void gui_flicker_led(int led, int, int status)
+{
+    if (led < 0) {
+        gui_flicker_led_single(LED_HD, 0);
+        gui_flicker_led_single(LED_CD, 0);
+        if (gui_data.net >= 0) {
+            gui_flicker_led_single(LED_NET, 0);
+        }
+        if (gui_data.md >= 0) {
+            gui_flicker_led_single(LED_MD, 0);
+        }
+    } else {
+        gui_flicker_led_single(led, status);
+    }
+}
+void gui_disk_image_change(int, const TCHAR*, bool) {}
+
+#ifdef WINUAE_UNIX_WITH_INTEGRATED_QT_UI
+static bool write_runtime_config_snapshot(TCHAR *path, size_t path_len)
+{
+    const char *tmpdir = getenv("TMPDIR");
+    if (!tmpdir || !tmpdir[0]) {
+        tmpdir = "/tmp";
+    }
+    const int written = snprintf(path, path_len, "%s/winuae-runtime-%ld.uae", tmpdir, (long)getpid());
+    if (written < 0 || size_t(written) >= path_len) {
+        write_log("Unix Qt runtime UI: temporary config path is too long\n");
+        return false;
+    }
+    if (!cfgfile_save(&changed_prefs, path, 0)) {
+        write_log("Unix Qt runtime UI: failed to write temporary config '%s'\n", path);
+        return false;
+    }
+    return true;
+}
+
+static const TCHAR *runtime_shortcut_initial_path(int shortcut)
+{
+    if (shortcut >= 0 && shortcut < 4) {
+        return changed_prefs.floppyslots[shortcut].df[0]
+            ? changed_prefs.floppyslots[shortcut].df
+            : currprefs.floppyslots[shortcut].df;
+    }
+    if (shortcut == 4 || shortcut == 5) {
+        if (savestate_fname[0]) {
+            return savestate_fname;
+        }
+        if (changed_prefs.statefile[0]) {
+            return changed_prefs.statefile;
+        }
+        return currprefs.statefile;
+    }
+    if (shortcut == 6) {
+        return changed_prefs.cdslots[0].name[0]
+            ? changed_prefs.cdslots[0].name
+            : currprefs.cdslots[0].name;
+    }
+    return "";
+}
+
+static bool apply_runtime_shortcut_selection(int shortcut, const TCHAR *path)
+{
+    if (!path || !path[0]) {
+        return false;
+    }
+
+    if (shortcut >= 0 && shortcut < 4) {
+        _tcsncpy(changed_prefs.floppyslots[shortcut].df, path, MAX_DPATH);
+        changed_prefs.floppyslots[shortcut].df[MAX_DPATH - 1] = 0;
+        set_config_changed();
+        return true;
+    }
+    if (shortcut == 4) {
+        savestate_initsave(path, 1, true, false);
+        savestate_state = STATE_DORESTORE;
+        return true;
+    }
+    if (shortcut == 5) {
+        savestate_initsave(path, 1, true, true);
+        save_state(savestate_fname, STATE_SAVE_DESCRIPTION);
+        return true;
+    }
+    if (shortcut == 6) {
+        _tcsncpy(changed_prefs.cdslots[0].name, path, MAX_DPATH);
+        changed_prefs.cdslots[0].name[MAX_DPATH - 1] = 0;
+        changed_prefs.cdslots[0].inuse = true;
+        set_config_changed();
+        return true;
+    }
+    return false;
+}
+#endif
+
+void gui_display(int shortcut)
+{
+#ifdef WINUAE_UNIX_WITH_INTEGRATED_QT_UI
+    static bool active;
+    if (active) {
+        return;
+    }
+    if (shortcut != -1 && (shortcut < 0 || shortcut > 6)) {
+        write_log("Unix Qt runtime UI: shortcut %d is not implemented yet\n", shortcut);
+        return;
+    }
+
+    active = true;
+
+    const int old_pause = pause_emulation;
+    pause_emulation = 1;
+    setsystime();
+    inputdevice_unacquire();
+    pause_sound();
+
+    if (shortcut == -1) {
+        TCHAR snapshot_path[MAX_DPATH];
+        snapshot_path[0] = 0;
+        const bool have_snapshot = write_runtime_config_snapshot(snapshot_path, sizeof snapshot_path / sizeof snapshot_path[0]);
+
+        int exit_code = 0;
+        const int action = runWinUaeQtLauncherForPrefsWithConfig(
+            unix_gui_argc,
+            unix_gui_argv,
+            &changed_prefs,
+            have_snapshot ? snapshot_path : nullptr,
+            &exit_code);
+
+        if (have_snapshot) {
+            unlink(snapshot_path);
+        }
+
+        if (action == WINUAE_QT_LAUNCHER_START) {
+            fixup_prefs(&changed_prefs, true);
+            reset_sound();
+            inputdevice_copyconfig(&changed_prefs, &currprefs);
+            inputdevice_config_change_test();
+            set_config_changed();
+        } else if (action == WINUAE_QT_LAUNCHER_ERROR) {
+            write_log("Unix Qt runtime UI exited with error code %d\n", exit_code);
+        }
+    } else {
+        TCHAR selected_path[MAX_DPATH];
+        selected_path[0] = 0;
+        int exit_code = 0;
+        const int action = runWinUaeQtRuntimeFileDialog(
+            unix_gui_argc,
+            unix_gui_argv,
+            shortcut,
+            runtime_shortcut_initial_path(shortcut),
+            selected_path,
+            sizeof selected_path / sizeof selected_path[0],
+            &exit_code);
+        if (action == WINUAE_QT_LAUNCHER_START) {
+            apply_runtime_shortcut_selection(shortcut, selected_path);
+        } else if (action == WINUAE_QT_LAUNCHER_ERROR) {
+            write_log("Unix Qt runtime file dialog exited with error code %d\n", exit_code);
+        }
+    }
+
+    pause_emulation = old_pause;
+    setsystime();
+    resume_sound();
+    inputdevice_acquire(TRUE);
+    fpscounter_reset();
+
+    active = false;
+#else
+    write_log("Unix Qt runtime UI is not enabled in this build\n");
+#endif
+}
+void gui_gameport_button_change(int, int, int) {}
+void gui_gameport_axis_change(int, int, int, int) {}
+void notify_user(int msg)
+{
+    switch (msg) {
+        case NUMSG_MODRIP_NOTFOUND:
+            write_log("No music modules or packed data found.\n");
+            break;
+        case NUMSG_MODRIP_FINISHED:
+            write_log("Module ripper scan finished.\n");
+            break;
+        default:
+            write_log("notify_user: %d\n", msg);
+            break;
+    }
+}
+void notify_user_parms(int msg, const TCHAR*, ...) { write_log("notify_user: %d\n", msg); }
+int translate_message(int msg, TCHAR *out)
+{
+    if (!out) {
+        return 0;
+    }
+    switch (msg) {
+        case NUMSG_MODRIP_SAVE:
+            _tcscpy(out, _T("Module/packed data found\n%s\nStart address %08.8X, Size %d bytes\n'%s'\nWould you like to save it?"));
+            return 1;
+        default:
+            out[0] = 0;
+            return 0;
+    }
+}
+void gui_message(const TCHAR *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    vfprintf(stderr, format, ap);
+    fputc('\n', stderr);
+    va_end(ap);
+}
+
+int gui_message_multibutton(int flags, const TCHAR *format, ...)
+{
+    TCHAR msg[2048];
+    va_list ap;
+    va_start(ap, format);
+    _vsntprintf(msg, sizeof msg / sizeof(TCHAR), format, ap);
+    msg[(sizeof msg / sizeof(TCHAR)) - 1] = 0;
+    va_end(ap);
+
+    const size_t msg_len = _tcslen(msg);
+    write_log("%s", msg);
+    if (msg_len == 0 || msg[msg_len - 1] != '\n') {
+        write_log("\n");
+    }
+
+#ifdef WINUAE_UNIX_WITH_INTEGRATED_QT_UI
+    int exit_code = 0;
+    const int ret = runWinUaeQtMessageBox(unix_gui_argc, unix_gui_argv, flags, msg, &exit_code);
+    if (exit_code == 0) {
+        return ret;
+    }
+#endif
+
+    return flags == 0 ? 0 : 1;
+}
diff --git a/od-unix/hardfile_host.cpp b/od-unix/hardfile_host.cpp
new file mode 100644 (file)
index 0000000..19edeee
--- /dev/null
@@ -0,0 +1,397 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#if defined(__APPLE__)
+#include <sys/disk.h>
+#include <sys/ioctl.h>
+#elif defined(__linux__)
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#endif
+
+#include "options.h"
+#include "filesys.h"
+#include "uae.h"
+#include "uae/io.h"
+
+struct hardfilehandle {
+    int fd;
+    bool realdrive;
+};
+
+#define UNIX_MAX_NATIVE_DRIVES MAX_FILESYSTEM_UNITS
+
+struct unix_driveinfo {
+    TCHAR device_name[1024];
+    TCHAR device_path[1024];
+    TCHAR device_full_path[2048];
+    uae_u64 size;
+    int bytespersector;
+    int readonly;
+    int dangerous;
+};
+
+static unix_driveinfo unix_drives[UNIX_MAX_NATIVE_DRIVES];
+static int unix_drive_count;
+
+static bool query_fd_geometry(int fd, uae_u64 *size, int *blocksize)
+{
+    if (size) {
+        *size = 0;
+    }
+    if (blocksize) {
+        *blocksize = 512;
+    }
+#if defined(__APPLE__)
+    uint32_t block_size = 512;
+    uint64_t block_count = 0;
+    if (ioctl(fd, DKIOCGETBLOCKSIZE, &block_size) == 0 && block_size > 0 && blocksize) {
+        *blocksize = (int)block_size;
+    }
+    if (ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count) == 0 && size) {
+        *size = block_count * (uae_u64)(blocksize ? *blocksize : block_size);
+        return *size > 0;
+    }
+#elif defined(__linux__)
+    unsigned long long bytes = 0;
+    int logical_block_size = 512;
+    if (ioctl(fd, BLKSSZGET, &logical_block_size) == 0 && logical_block_size > 0 && blocksize) {
+        *blocksize = logical_block_size;
+    }
+    if (ioctl(fd, BLKGETSIZE64, &bytes) == 0 && size) {
+        *size = (uae_u64)bytes;
+        return *size > 0;
+    }
+#endif
+    struct stat st;
+    if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && size) {
+        *size = (uae_u64)st.st_size;
+        return true;
+    }
+    return false;
+}
+
+static bool query_path_geometry(const char *path, uae_u64 *size, int *blocksize)
+{
+    int fd = open(path, O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        return false;
+    }
+    bool ok = query_fd_geometry(fd, size, blocksize);
+    close(fd);
+    return ok;
+}
+
+static bool unix_drive_path_exists(const char *path)
+{
+    for (int i = 0; i < unix_drive_count; i++) {
+        if (!_tcscmp(unix_drives[i].device_path, path)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static void add_unix_drive(const char *path, const char *display_name)
+{
+#ifdef WINUAE_UNIX_WITH_NATIVE_HARDDRIVES
+    if (!path || !path[0] || unix_drive_count >= UNIX_MAX_NATIVE_DRIVES || unix_drive_path_exists(path)) {
+        return;
+    }
+    uae_u64 size = 0;
+    int blocksize = 512;
+    if (!query_path_geometry(path, &size, &blocksize) || size == 0) {
+        return;
+    }
+    unix_driveinfo *udi = &unix_drives[unix_drive_count++];
+    memset(udi, 0, sizeof *udi);
+    _tcsncpy(udi->device_path, path, sizeof udi->device_path / sizeof(TCHAR) - 1);
+    _sntprintf(udi->device_full_path, sizeof udi->device_full_path / sizeof(TCHAR), _T(":%s"), path);
+    _sntprintf(udi->device_name, sizeof udi->device_name / sizeof(TCHAR), _T(":%s"),
+        display_name && display_name[0] ? display_name : path);
+    udi->size = size;
+    udi->bytespersector = blocksize > 0 ? blocksize : 512;
+    udi->readonly = 1;
+    udi->dangerous = 0;
+#else
+    (void)path;
+    (void)display_name;
+#endif
+}
+
+static void enumerate_unix_drives(void)
+{
+    unix_drive_count = 0;
+#ifdef WINUAE_UNIX_WITH_NATIVE_HARDDRIVES
+#if defined(__APPLE__)
+    DIR *dir = opendir("/dev");
+    if (!dir) {
+        return;
+    }
+    for (struct dirent *entry = readdir(dir); entry; entry = readdir(dir)) {
+        const char *name = entry->d_name;
+        if (strncmp(name, "rdisk", 5) != 0) {
+            continue;
+        }
+        if (strchr(name + 5, 's')) {
+            continue;
+        }
+        char path[PATH_MAX];
+        snprintf(path, sizeof path, "/dev/%s", name);
+        add_unix_drive(path, name);
+    }
+    closedir(dir);
+#elif defined(__linux__)
+    DIR *dir = opendir("/sys/block");
+    if (!dir) {
+        return;
+    }
+    for (struct dirent *entry = readdir(dir); entry; entry = readdir(dir)) {
+        const char *name = entry->d_name;
+        if (name[0] == '.') {
+            continue;
+        }
+        if (!strncmp(name, "loop", 4) || !strncmp(name, "ram", 3) || !strncmp(name, "zram", 4) || !strncmp(name, "dm-", 3)) {
+            continue;
+        }
+        char path[PATH_MAX];
+        snprintf(path, sizeof path, "/dev/%s", name);
+        add_unix_drive(path, name);
+    }
+    closedir(dir);
+#endif
+#endif
+}
+
+static bool is_native_device_name(const TCHAR *name)
+{
+    return name && name[0] == ':' && name[1] == '/';
+}
+
+int hdf_init_target(void)
+{
+    enumerate_unix_drives();
+    return 1;
+}
+
+int hdf_open_target(struct hardfiledata *hfd, const TCHAR *name)
+{
+    if (!hfd || !name) {
+        return 0;
+    }
+    hdf_close_target(hfd);
+    hfd->handle = xcalloc(hardfilehandle, 1);
+    hfd->handle->fd = -1;
+    hfd->cache = xcalloc(uae_u8, 16384);
+    hfd->cache_valid = 0;
+    hfd->flags = 0;
+    if (is_native_device_name(name)) {
+#ifdef WINUAE_UNIX_WITH_NATIVE_HARDDRIVES
+        const TCHAR *path = name + 1;
+        int fd = open(path, O_RDONLY | O_CLOEXEC);
+        if (fd < 0) {
+            hdf_close_target(hfd);
+            return 0;
+        }
+        uae_u64 size = 0;
+        int blocksize = hfd->ci.blocksize > 0 ? hfd->ci.blocksize : 512;
+        if (!query_fd_geometry(fd, &size, &blocksize) || size == 0) {
+            close(fd);
+            hdf_close_target(hfd);
+            return 0;
+        }
+        hfd->handle->fd = fd;
+        hfd->handle->realdrive = true;
+        hfd->flags = HFD_FLAGS_REALDRIVE;
+        hfd->ci.readonly = true;
+        hfd->physsize = hfd->virtsize = size;
+        hfd->offset = 0;
+        hfd->handle_valid = 1;
+        hfd->ci.blocksize = blocksize;
+        _tcsncpy(hfd->vendor_id, _T("Unix"), sizeof hfd->vendor_id / sizeof(TCHAR) - 1);
+        _tcsncpy(hfd->product_id, path, sizeof hfd->product_id / sizeof(TCHAR) - 1);
+        return 1;
+#else
+        hdf_close_target(hfd);
+        return 0;
+#endif
+    }
+    int open_flags = hfd->ci.readonly ? O_RDONLY : O_RDWR;
+    hfd->handle->fd = open(name, open_flags | O_CLOEXEC);
+    if (hfd->handle->fd < 0 && !hfd->ci.readonly) {
+        hfd->ci.readonly = true;
+        hfd->handle->fd = open(name, O_RDONLY | O_CLOEXEC);
+    }
+    if (hfd->handle->fd < 0) {
+        hdf_close_target(hfd);
+        return 0;
+    }
+    uae_u64 size = 0;
+    int blocksize = hfd->ci.blocksize > 0 ? hfd->ci.blocksize : 512;
+    if (!query_fd_geometry(hfd->handle->fd, &size, &blocksize)) {
+        hdf_close_target(hfd);
+        return 0;
+    }
+    hfd->physsize = hfd->virtsize = size;
+    hfd->offset = 0;
+    hfd->handle_valid = 1;
+    if (hfd->ci.blocksize <= 0) {
+        hfd->ci.blocksize = blocksize > 0 ? blocksize : 512;
+    }
+    return 1;
+}
+
+int hdf_dup_target(struct hardfiledata *dhfd, const struct hardfiledata *shfd)
+{
+    if (!dhfd || !shfd) {
+        return 0;
+    }
+    *dhfd = *shfd;
+    dhfd->handle = NULL;
+    dhfd->cache = NULL;
+    return hdf_open_target(dhfd, shfd->ci.rootdir);
+}
+
+void hdf_close_target(struct hardfiledata *hfd)
+{
+    if (!hfd) {
+        return;
+    }
+    if (hfd->handle) {
+        if (hfd->handle->fd >= 0) {
+            close(hfd->handle->fd);
+        }
+        xfree(hfd->handle);
+        hfd->handle = NULL;
+    }
+    xfree(hfd->cache);
+    hfd->cache = NULL;
+    hfd->cache_valid = 0;
+    hfd->handle_valid = 0;
+}
+
+int hdf_read_target(struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len, uae_u32 *error)
+{
+    if (error) {
+        *error = 0;
+    }
+    if (!hfd || !hfd->handle || hfd->handle->fd < 0) {
+        if (error) {
+            *error = errno;
+        }
+        return 0;
+    }
+    ssize_t got = pread(hfd->handle->fd, buffer, len, (off_t)(offset + hfd->offset));
+    if (got < 0) {
+        if (error) {
+            *error = errno;
+        }
+        return 0;
+    }
+    return (int)got;
+}
+
+int hdf_write_target(struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len, uae_u32 *error)
+{
+    if (error) {
+        *error = 0;
+    }
+    if (!hfd || hfd->ci.readonly || !hfd->handle || hfd->handle->fd < 0 || hfd->handle->realdrive) {
+        if (error) {
+            *error = errno ? errno : EACCES;
+        }
+        return 0;
+    }
+    ssize_t done = pwrite(hfd->handle->fd, buffer, len, (off_t)(offset + hfd->offset));
+    if (done < 0) {
+        if (error) {
+            *error = errno;
+        }
+        return 0;
+    }
+    return (int)done;
+}
+
+int hdf_resize_target(struct hardfiledata *hfd, uae_u64 newsize)
+{
+    if (!hfd || !hfd->handle || hfd->handle->fd < 0 || hfd->handle->realdrive) {
+        return 0;
+    }
+    if (ftruncate(hfd->handle->fd, (off_t)newsize) != 0) {
+        return 0;
+    }
+    hfd->physsize = hfd->virtsize = newsize;
+    return 1;
+}
+
+int hdf_getnumharddrives(void)
+{
+    if (unix_drive_count == 0) {
+        enumerate_unix_drives();
+    }
+    return unix_drive_count;
+}
+
+TCHAR *hdf_getnameharddrive(int index, int flags, int *sectorsize, int *dangerousdrive, uae_u32 *outflags)
+{
+    static TCHAR name[2048];
+    if (sectorsize) {
+        *sectorsize = 512;
+    }
+    if (dangerousdrive) {
+        *dangerousdrive = 0;
+    }
+    if (outflags) {
+        *outflags = 0;
+    }
+    if (index < 0 || index >= hdf_getnumharddrives()) {
+        name[0] = 0;
+        return name;
+    }
+    unix_driveinfo *udi = &unix_drives[index];
+    if (sectorsize) {
+        *sectorsize = udi->bytespersector;
+    }
+    if (dangerousdrive) {
+        *dangerousdrive = udi->dangerous ? 0 : 1;
+        if (udi->readonly) {
+            *dangerousdrive |= 2;
+        }
+    }
+    if (flags & 4) {
+        return udi->device_full_path;
+    }
+    if (flags & 2) {
+        return udi->device_path;
+    }
+    if (flags & 1) {
+        TCHAR size_text[32];
+        if (udi->size >= 1024ULL * 1024ULL * 1024ULL) {
+            _sntprintf(size_text, sizeof size_text / sizeof(TCHAR), _T("%.1fG"), (double)udi->size / (1024.0 * 1024.0 * 1024.0));
+        } else if (udi->size >= 1024ULL * 1024ULL) {
+            _sntprintf(size_text, sizeof size_text / sizeof(TCHAR), _T("%.1fM"), (double)udi->size / (1024.0 * 1024.0));
+        } else {
+            _sntprintf(size_text, sizeof size_text / sizeof(TCHAR), _T("%lluK"), (unsigned long long)(udi->size / 1024));
+        }
+        _sntprintf(name, sizeof name / sizeof(TCHAR), _T("%10s [%s,RO] %s"), _T("[OS]"), size_text, udi->device_name + 1);
+        return name;
+    }
+    return udi->device_name;
+}
+
+int get_guid_target(uae_u8 *out)
+{
+    if (!out) {
+        return 0;
+    }
+    for (int i = 0; i < 16; i++) {
+        out[i] = (uae_u8)uaerand();
+    }
+    return 1;
+}
diff --git a/od-unix/host.h b/od-unix/host.h
new file mode 100644 (file)
index 0000000..1a28c5b
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef WINUAE_OD_UNIX_HOST_H
+#define WINUAE_OD_UNIX_HOST_H
+
+bool unix_host_quit_requested(void);
+void unix_host_check_quit(void);
+
+#endif /* WINUAE_OD_UNIX_HOST_H */
diff --git a/od-unix/logging.cpp b/od-unix/logging.cpp
new file mode 100644 (file)
index 0000000..d3ea30f
--- /dev/null
@@ -0,0 +1,238 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "uae.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <mutex>
+#include <string>
+#include <vector>
+
+int console_logging;
+int always_flush_log;
+TCHAR *conlogfile;
+FILE *debugfile;
+
+static constexpr size_t LOG_CAPTURE_LIMIT = 256 * 1024;
+
+static std::mutex log_capture_mutex;
+static std::string log_capture;
+
+static void capture_log_bytes(const char *text, size_t len)
+{
+    if (!text || len == 0) {
+        return;
+    }
+    std::lock_guard<std::mutex> lock(log_capture_mutex);
+    if (len >= LOG_CAPTURE_LIMIT) {
+        log_capture.assign(text + len - LOG_CAPTURE_LIMIT, LOG_CAPTURE_LIMIT);
+        return;
+    }
+    if (log_capture.size() + len > LOG_CAPTURE_LIMIT) {
+        log_capture.erase(0, log_capture.size() + len - LOG_CAPTURE_LIMIT);
+    }
+    log_capture.append(text, len);
+}
+
+static void capture_log_format(const char *format, va_list ap)
+{
+    va_list size_args;
+    va_copy(size_args, ap);
+    const int needed = vsnprintf(NULL, 0, format, size_args);
+    va_end(size_args);
+    if (needed <= 0) {
+        return;
+    }
+
+    std::vector<char> buffer(size_t(needed) + 1);
+    va_list format_args;
+    va_copy(format_args, ap);
+    vsnprintf(buffer.data(), buffer.size(), format, format_args);
+    va_end(format_args);
+    capture_log_bytes(buffer.data(), size_t(needed));
+}
+
+static void vlog_write(const char *format, va_list ap)
+{
+    va_list stderr_args;
+    va_copy(stderr_args, ap);
+    vfprintf(stderr, format, stderr_args);
+    va_end(stderr_args);
+    fflush(stderr);
+
+    if (debugfile) {
+        va_list file_args;
+        va_copy(file_args, ap);
+        vfprintf(debugfile, format, file_args);
+        va_end(file_args);
+        if (always_flush_log) {
+            fflush(debugfile);
+        }
+    }
+
+    capture_log_format(format, ap);
+}
+
+void write_log(const char *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    vlog_write(format, ap);
+    va_end(ap);
+}
+
+void write_logx(const TCHAR *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    vlog_write(format, ap);
+    va_end(ap);
+}
+
+void write_dlog(const TCHAR *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    vlog_write(format, ap);
+    va_end(ap);
+}
+
+int read_log(void)
+{
+    return 0;
+}
+
+void flush_log(void)
+{
+    fflush(stderr);
+    if (debugfile) {
+        fflush(debugfile);
+    }
+}
+
+void logging_init(void)
+{
+}
+
+uae_u8 *save_log(int, size_t *len)
+{
+    if (!len) {
+        return NULL;
+    }
+
+    flush_log();
+
+    std::lock_guard<std::mutex> lock(log_capture_mutex);
+    size_t size = log_capture.size();
+    size_t offset = 0;
+    if (*len > 0 && size > *len) {
+        offset = size - *len;
+        size = *len;
+    }
+    if (size == 0) {
+        *len = 0;
+        return NULL;
+    }
+
+    uae_u8 *dst = xmalloc(uae_u8, size + 1);
+    if (!dst) {
+        *len = 0;
+        return NULL;
+    }
+    memcpy(dst, log_capture.data() + offset, size);
+    dst[size] = 0;
+    *len = size + 1;
+    return dst;
+}
+
+FILE *log_open(const TCHAR *name, int append, int, TCHAR*)
+{
+    return fopen(name, append ? "ab" : "wb");
+}
+
+void log_close(FILE *f)
+{
+    if (f) {
+        if (f == debugfile) {
+            debugfile = NULL;
+        }
+        fclose(f);
+    }
+}
+
+TCHAR *setconsolemode(TCHAR *buffer, int)
+{
+    return buffer;
+}
+
+void close_console(void) {}
+void open_console(void) {}
+bool is_interactive_console(void) { return true; }
+void reopen_console(void) {}
+void activate_console(void) {}
+void deactivate_console(void) {}
+void set_console_input_mode(int) {}
+bool is_console_open(void) { return true; }
+void console_out(const TCHAR *s)
+{
+    fputs(s, stderr);
+    if (debugfile) {
+        fputs(s, debugfile);
+        if (always_flush_log) {
+            fflush(debugfile);
+        }
+    }
+    capture_log_bytes(s, strlen(s));
+}
+void console_out_f(const TCHAR *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    vlog_write(format, ap);
+    va_end(ap);
+}
+void console_flush(void) { flush_log(); }
+int console_get(TCHAR *, int) { return 0; }
+bool console_isch(void) { return false; }
+TCHAR console_getch(void) { return 0; }
+
+void jit_abort(const char *format, ...)
+{
+    char buffer[4096];
+
+    va_list ap;
+    va_start(ap, format);
+    vsnprintf(buffer, sizeof(buffer), format, ap);
+    va_end(ap);
+    buffer[sizeof(buffer) - 1] = 0;
+
+    write_log("JIT: Serious error: %s\n", buffer);
+    uae_reset(1, 0);
+}
+
+void f_out(void *, const TCHAR *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    vlog_write(format, ap);
+    va_end(ap);
+}
+TCHAR* buf_out(TCHAR *buffer, int *bufsize, const TCHAR *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    int n = vsnprintf(buffer, *bufsize, format, ap);
+    va_end(ap);
+    if (n >= 0 && n < *bufsize) {
+        *bufsize -= n;
+        return buffer + n;
+    }
+    return buffer;
+}
+TCHAR *write_log_get_ts(void)
+{
+    static TCHAR ts[1] = { 0 };
+    return ts;
+}
diff --git a/od-unix/machdep/m68k.h b/od-unix/machdep/m68k.h
new file mode 100644 (file)
index 0000000..aee7188
--- /dev/null
@@ -0,0 +1,112 @@
+#ifndef WINUAE_OD_UNIX_MACHDEP_M68K_H
+#define WINUAE_OD_UNIX_MACHDEP_M68K_H
+
+#include <stdlib.h>
+#include "uae/types.h"
+
+struct flag_struct {
+#if defined(CPU_AARCH64)
+    union {
+        uae_u64 cznv;
+        uae_u64 nzcv;
+    };
+    uae_u64 x;
+#else
+    uae_u32 cznv;
+    uae_u32 x;
+#endif
+};
+
+extern struct flag_struct regflags;
+
+#if defined(CPU_AARCH64)
+#define FLAGBIT_N 31
+#define FLAGBIT_Z 30
+#define FLAGBIT_C 29
+#define FLAGBIT_V 28
+#define FLAGBIT_X 0
+
+#define FLAGVAL_N (1UL << FLAGBIT_N)
+#define FLAGVAL_Z (1UL << FLAGBIT_Z)
+#define FLAGVAL_C (1UL << FLAGBIT_C)
+#define FLAGVAL_V (1UL << FLAGBIT_V)
+#define FLAGVAL_X (1UL << FLAGBIT_X)
+
+#define SET_ZFLG(y) (regflags.nzcv = (regflags.nzcv & ~FLAGVAL_Z) | (((y) ? 1UL : 0UL) << FLAGBIT_Z))
+#define SET_CFLG(y) (regflags.nzcv = (regflags.nzcv & ~FLAGVAL_C) | (((y) ? 1UL : 0UL) << FLAGBIT_C))
+#define SET_VFLG(y) (regflags.nzcv = (regflags.nzcv & ~FLAGVAL_V) | (((y) ? 1UL : 0UL) << FLAGBIT_V))
+#define SET_NFLG(y) (regflags.nzcv = (regflags.nzcv & ~FLAGVAL_N) | (((y) ? 1UL : 0UL) << FLAGBIT_N))
+#define SET_XFLG(y) (regflags.x = ((y) ? 1UL : 0UL))
+
+#define GET_ZFLG() ((regflags.nzcv >> FLAGBIT_Z) & 1)
+#define GET_CFLG() ((regflags.nzcv >> FLAGBIT_C) & 1)
+#define GET_VFLG() ((regflags.nzcv >> FLAGBIT_V) & 1)
+#define GET_NFLG() ((regflags.nzcv >> FLAGBIT_N) & 1)
+#define GET_XFLG() (regflags.x & 1)
+
+#define CLEAR_CZNV() (regflags.nzcv = 0)
+#define GET_CZNV() (regflags.nzcv)
+#define IOR_CZNV(X) (regflags.nzcv |= (X))
+#define SET_CZNV(X) (regflags.nzcv = (X))
+#define COPY_CARRY() (regflags.x = (regflags.nzcv >> FLAGBIT_C) & 1)
+#else
+#define FLAGBIT_N 15
+#define FLAGBIT_Z 14
+#define FLAGBIT_C 8
+#define FLAGBIT_V 0
+#define FLAGBIT_X 8
+
+#define FLAGVAL_N (1 << FLAGBIT_N)
+#define FLAGVAL_Z (1 << FLAGBIT_Z)
+#define FLAGVAL_C (1 << FLAGBIT_C)
+#define FLAGVAL_V (1 << FLAGBIT_V)
+#define FLAGVAL_X (1 << FLAGBIT_X)
+
+#define SET_ZFLG(y) (regflags.cznv = (regflags.cznv & ~FLAGVAL_Z) | (((y) ? 1 : 0) << FLAGBIT_Z))
+#define SET_CFLG(y) (regflags.cznv = (regflags.cznv & ~FLAGVAL_C) | (((y) ? 1 : 0) << FLAGBIT_C))
+#define SET_VFLG(y) (regflags.cznv = (regflags.cznv & ~FLAGVAL_V) | (((y) ? 1 : 0) << FLAGBIT_V))
+#define SET_NFLG(y) (regflags.cznv = (regflags.cznv & ~FLAGVAL_N) | (((y) ? 1 : 0) << FLAGBIT_N))
+#define SET_XFLG(y) (regflags.x = ((y) ? 1 : 0) << FLAGBIT_X)
+
+#define GET_ZFLG() ((regflags.cznv >> FLAGBIT_Z) & 1)
+#define GET_CFLG() ((regflags.cznv >> FLAGBIT_C) & 1)
+#define GET_VFLG() ((regflags.cznv >> FLAGBIT_V) & 1)
+#define GET_NFLG() ((regflags.cznv >> FLAGBIT_N) & 1)
+#define GET_XFLG() ((regflags.x >> FLAGBIT_X) & 1)
+
+#define CLEAR_CZNV() (regflags.cznv = 0)
+#define GET_CZNV() (regflags.cznv)
+#define IOR_CZNV(X) (regflags.cznv |= (X))
+#define SET_CZNV(X) (regflags.cznv = (X))
+#define COPY_CARRY() (regflags.x = regflags.cznv)
+#endif
+
+static inline int cctrue(int cc)
+{
+    uae_u64 cznv = GET_CZNV();
+    switch (cc) {
+    case 0: return 1;
+    case 1: return 0;
+    case 2: return (cznv & (FLAGVAL_C | FLAGVAL_Z)) == 0;
+    case 3: return (cznv & (FLAGVAL_C | FLAGVAL_Z)) != 0;
+    case 4: return (cznv & FLAGVAL_C) == 0;
+    case 5: return (cznv & FLAGVAL_C) != 0;
+    case 6: return (cznv & FLAGVAL_Z) == 0;
+    case 7: return (cznv & FLAGVAL_Z) != 0;
+    case 8: return (cznv & FLAGVAL_V) == 0;
+    case 9: return (cznv & FLAGVAL_V) != 0;
+    case 10: return (cznv & FLAGVAL_N) == 0;
+    case 11: return (cznv & FLAGVAL_N) != 0;
+    case 12: return (((cznv << (FLAGBIT_N - FLAGBIT_V)) ^ cznv) & FLAGVAL_N) == 0;
+    case 13: return (((cznv << (FLAGBIT_N - FLAGBIT_V)) ^ cznv) & FLAGVAL_N) != 0;
+    case 14:
+        cznv &= (FLAGVAL_N | FLAGVAL_Z | FLAGVAL_V);
+        return (((cznv << (FLAGBIT_N - FLAGBIT_V)) ^ cznv) & (FLAGVAL_N | FLAGVAL_Z)) == 0;
+    case 15:
+        cznv &= (FLAGVAL_N | FLAGVAL_Z | FLAGVAL_V);
+        return (((cznv << (FLAGBIT_N - FLAGBIT_V)) ^ cznv) & (FLAGVAL_N | FLAGVAL_Z)) != 0;
+    }
+    abort();
+}
+
+#endif /* WINUAE_OD_UNIX_MACHDEP_M68K_H */
diff --git a/od-unix/machdep/maccess.h b/od-unix/machdep/maccess.h
new file mode 100644 (file)
index 0000000..1a96094
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef WINUAE_OD_UNIX_MACHDEP_MACCESS_H
+#define WINUAE_OD_UNIX_MACHDEP_MACCESS_H
+
+#include "uae/types.h"
+
+static inline uae_u16 uae_bswap16(uae_u16 v)
+{
+    return __builtin_bswap16(v);
+}
+
+static inline uae_u32 uae_bswap32(uae_u32 v)
+{
+    return __builtin_bswap32(v);
+}
+
+static inline uae_u64 uae_bswap64(uae_u64 v)
+{
+    return __builtin_bswap64(v);
+}
+
+static inline uae_u8 do_get_mem_byte(uae_u8 *a)
+{
+    return *a;
+}
+
+static inline uae_u16 do_get_mem_word(uae_u16 *a)
+{
+    uae_u16 v;
+    __builtin_memcpy(&v, a, sizeof(v));
+    return uae_bswap16(v);
+}
+
+static inline uae_u32 do_get_mem_long(uae_u32 *a)
+{
+    uae_u32 v;
+    __builtin_memcpy(&v, a, sizeof(v));
+    return uae_bswap32(v);
+}
+
+static inline uae_u64 do_get_mem_quad(uae_u64 *a)
+{
+    uae_u64 v;
+    __builtin_memcpy(&v, a, sizeof(v));
+    return uae_bswap64(v);
+}
+
+static inline void do_put_mem_byte(uae_u8 *a, uae_u8 v)
+{
+    *a = v;
+}
+
+static inline void do_put_mem_word(uae_u16 *a, uae_u16 v)
+{
+    v = uae_bswap16(v);
+    __builtin_memcpy(a, &v, sizeof(v));
+}
+
+static inline void do_put_mem_long(uae_u32 *a, uae_u32 v)
+{
+    v = uae_bswap32(v);
+    __builtin_memcpy(a, &v, sizeof(v));
+}
+
+static inline void do_put_mem_quad(uae_u64 *a, uae_u64 v)
+{
+    v = uae_bswap64(v);
+    __builtin_memcpy(a, &v, sizeof(v));
+}
+
+static inline uae_u16 do_byteswap_16(uae_u16 v) { return uae_bswap16(v); }
+static inline uae_u32 do_byteswap_32(uae_u32 v) { return uae_bswap32(v); }
+static inline uae_u64 do_byteswap_64(uae_u64 v) { return uae_bswap64(v); }
+
+static inline uae_u32 do_get_mem_word_unswapped(uae_u16 *a)
+{
+    return *a;
+}
+
+#define ALIGN_POINTER_TO32(p) ((~(uintptr_t)(p)) & 3)
+#define call_mem_get_func(func, addr) ((*func)(addr))
+#define call_mem_put_func(func, addr, v) ((*func)(addr, v))
+
+#endif /* WINUAE_OD_UNIX_MACHDEP_MACCESS_H */
diff --git a/od-unix/machdep/machdep.h b/od-unix/machdep/machdep.h
new file mode 100644 (file)
index 0000000..bd0b420
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef WINUAE_OD_UNIX_MACHDEP_H
+#define WINUAE_OD_UNIX_MACHDEP_H
+
+#if defined(__x86_64__) || defined(__i386__)
+#define MACHDEP_X86 1
+#define MACHDEP_NAME "x86"
+#elif defined(__aarch64__) || defined(__arm__)
+#define MACHDEP_ARM 1
+#define MACHDEP_NAME "arm"
+#else
+#define MACHDEP_NAME "generic"
+#endif
+
+#define HAVE_MACHDEP_TIMER 1
+
+#endif /* WINUAE_OD_UNIX_MACHDEP_H */
diff --git a/od-unix/machdep/rpt.h b/od-unix/machdep/rpt.h
new file mode 100644 (file)
index 0000000..4adc7ee
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef WINUAE_OD_UNIX_MACHDEP_RPT_H
+#define WINUAE_OD_UNIX_MACHDEP_RPT_H
+
+#include "uae/time.h"
+
+#endif /* WINUAE_OD_UNIX_MACHDEP_RPT_H */
diff --git a/od-unix/mman.cpp b/od-unix/mman.cpp
new file mode 100644 (file)
index 0000000..7461c95
--- /dev/null
@@ -0,0 +1,74 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "options.h"
+#include "memory.h"
+#include "uae/mman.h"
+
+bool jit_direct_compatible_memory;
+size_t max_z3fastmem = 256 * 1024 * 1024;
+size_t max_physmem = 512 * 1024 * 1024;
+
+bool preinit_shm(void)
+{
+    return true;
+}
+
+bool init_shm(void)
+{
+    jit_direct_compatible_memory = false;
+    canbang = false;
+    return true;
+}
+
+void free_shm(void)
+{
+}
+
+bool uae_mman_info(addrbank *, struct uae_mman_data *)
+{
+    return false;
+}
+
+void mapped_free(addrbank *ab)
+{
+    if (!ab) {
+        return;
+    }
+    if (!(ab->flags & ABFLAG_NOALLOC)) {
+        xfree(ab->baseaddr);
+    }
+    ab->flags &= ~(ABFLAG_MAPPED | ABFLAG_DIRECTMAP);
+    ab->allocated_size = 0;
+    ab->baseaddr = NULL;
+    ab->baseaddr_direct_r = NULL;
+    ab->baseaddr_direct_w = NULL;
+}
+
+void mman_set_barriers(bool)
+{
+}
+
+void commit_natmem_gaps(void)
+{
+}
+
+void *uae_shmat(addrbank *, int, void *, int, struct uae_mman_data *)
+{
+    return (void *)-1;
+}
+
+int uae_shmdt(const void *)
+{
+    return 0;
+}
+
+int uae_shmget(uae_key_t, addrbank *, int)
+{
+    return -1;
+}
+
+int uae_shmctl(int, int, struct uae_shmid_ds *)
+{
+    return 0;
+}
diff --git a/od-unix/path_expand.cpp b/od-unix/path_expand.cpp
new file mode 100644 (file)
index 0000000..1f3fcea
--- /dev/null
@@ -0,0 +1,155 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <ctype.h>
+#include <cstdlib>
+#include <limits.h>
+#include <string>
+#include <string.h>
+#include <unistd.h>
+#include <vector>
+
+#include "path_expand.h"
+
+std::string unix_expand_path(const std::string &path)
+{
+    std::string out;
+    const char *home = getenv("HOME");
+
+    if (!path.empty() && path[0] == '~' && (path.size() == 1 || path[1] == '/')) {
+        out = home ? home : "";
+        out += path.substr(1);
+    } else {
+        out = path;
+    }
+
+    if (home) {
+        std::string homestr(home);
+        size_t slash = homestr.find_last_of('/');
+        std::string user = slash == std::string::npos ? homestr : homestr.substr(slash + 1);
+        std::string oldhome = "/home/" + user;
+        if (!user.empty() && out.compare(0, oldhome.size(), oldhome) == 0 &&
+            (out.size() == oldhome.size() || out[oldhome.size()] == '/')) {
+            out = homestr + out.substr(oldhome.size());
+        }
+    }
+
+    for (size_t i = 0; i < out.size(); i++) {
+        if (out[i] != '$') {
+            continue;
+        }
+        size_t start = i + 1;
+        size_t end = start;
+        std::string name;
+        if (start < out.size() && out[start] == '{') {
+            start++;
+            end = out.find('}', start);
+            if (end == std::string::npos) {
+                continue;
+            }
+            name = out.substr(start, end - start);
+            end++;
+        } else {
+            while (end < out.size() && (isalnum((unsigned char)out[end]) || out[end] == '_')) {
+                end++;
+            }
+            name = out.substr(start, end - start);
+        }
+        if (name.empty()) {
+            continue;
+        }
+        const char *value = getenv(name.c_str());
+        if (!value) {
+            continue;
+        }
+        const size_t value_len = strlen(value);
+        out.replace(i, end - i, value);
+        if (value_len > 0) {
+            i += value_len - 1;
+        }
+    }
+
+    return out;
+}
+
+static bool unix_path_is_absolute(const std::string &path)
+{
+    return !path.empty() && path[0] == '/';
+}
+
+static bool unix_path_has_unresolved_prefix(const std::string &path)
+{
+    return path.find('$') != std::string::npos || (!path.empty() && path[0] == '~');
+}
+
+static std::string unix_join_path(const std::string &dir, const std::string &name)
+{
+    if (dir.empty() || dir == ".") {
+        return name;
+    }
+    if (!dir.empty() && dir[dir.size() - 1] == '/') {
+        return dir + name;
+    }
+    return dir + "/" + name;
+}
+
+static std::string unix_current_directory()
+{
+    char cwd[PATH_MAX];
+    if (getcwd(cwd, sizeof cwd)) {
+        return cwd;
+    }
+    return ".";
+}
+
+static std::string unix_lexically_normalize_absolute(const std::string &path)
+{
+    std::vector<std::string> parts;
+    size_t pos = 0;
+    while (pos < path.size()) {
+        while (pos < path.size() && path[pos] == '/') {
+            pos++;
+        }
+        size_t end = pos;
+        while (end < path.size() && path[end] != '/') {
+            end++;
+        }
+        std::string part = path.substr(pos, end - pos);
+        if (part == "..") {
+            if (!parts.empty()) {
+                parts.pop_back();
+            }
+        } else if (!part.empty() && part != ".") {
+            parts.push_back(part);
+        }
+        pos = end;
+    }
+
+    std::string out = "/";
+    for (size_t i = 0; i < parts.size(); i++) {
+        if (i > 0) {
+            out += "/";
+        }
+        out += parts[i];
+    }
+    return out;
+}
+
+std::string unix_absolute_path(const std::string &path, const std::string &base)
+{
+    if (path.empty() || unix_path_has_unresolved_prefix(path)) {
+        return path;
+    }
+
+    std::string base_path = base.empty() ? unix_current_directory() : base;
+    if (!unix_path_is_absolute(base_path)) {
+        base_path = unix_join_path(unix_current_directory(), base_path);
+    }
+
+    const std::string combined = unix_path_is_absolute(path) ? path : unix_join_path(base_path, path);
+    char resolved[PATH_MAX];
+    if (realpath(combined.c_str(), resolved)) {
+        return resolved;
+    }
+    return unix_lexically_normalize_absolute(combined);
+}
diff --git a/od-unix/path_expand.h b/od-unix/path_expand.h
new file mode 100644 (file)
index 0000000..fd6824e
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef WINUAE_OD_UNIX_PATH_EXPAND_H
+#define WINUAE_OD_UNIX_PATH_EXPAND_H
+
+#include <string>
+
+std::string unix_expand_path(const std::string &path);
+std::string unix_absolute_path(const std::string &path, const std::string &base = std::string());
+
+#endif /* WINUAE_OD_UNIX_PATH_EXPAND_H */
diff --git a/od-unix/path_expand_test.cpp b/od-unix/path_expand_test.cpp
new file mode 100644 (file)
index 0000000..c8be38d
--- /dev/null
@@ -0,0 +1,45 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+#include "path_expand.h"
+
+static bool require_equal(const char *label, const std::string &actual, const std::string &expected)
+{
+    if (actual == expected) {
+        return true;
+    }
+    fprintf(stderr, "%s: expected '%s', got '%s'\n", label, expected.c_str(), actual.c_str());
+    return false;
+}
+
+int main()
+{
+    bool ok = true;
+    setenv("HOME", "/tmp/winuae-home", 1);
+    setenv("WINUAE_TEST_PATH", "/tmp/winuae-env", 1);
+    setenv("WINUAE_TEST_SUFFIX", "-suffix", 1);
+    unsetenv("WINUAE_TEST_MISSING");
+
+    ok = require_equal("home directory", unix_expand_path("~"), "/tmp/winuae-home") && ok;
+    ok = require_equal("home subpath", unix_expand_path("~/roms/kick.rom"), "/tmp/winuae-home/roms/kick.rom") && ok;
+    ok = require_equal("plain environment", unix_expand_path("$WINUAE_TEST_PATH/kick.rom"), "/tmp/winuae-env/kick.rom") && ok;
+    ok = require_equal("braced environment", unix_expand_path("${WINUAE_TEST_PATH}/kick.rom"), "/tmp/winuae-env/kick.rom") && ok;
+    ok = require_equal("adjacent environment", unix_expand_path("$WINUAE_TEST_PATH${WINUAE_TEST_SUFFIX}"), "/tmp/winuae-env-suffix") && ok;
+    ok = require_equal("embedded environment", unix_expand_path("/roms/$WINUAE_TEST_PATH"), "/roms//tmp/winuae-env") && ok;
+    ok = require_equal("missing environment", unix_expand_path("$WINUAE_TEST_MISSING/kick.rom"), "$WINUAE_TEST_MISSING/kick.rom") && ok;
+    ok = require_equal("malformed brace", unix_expand_path("${WINUAE_TEST_PATH/kick.rom"), "${WINUAE_TEST_PATH/kick.rom") && ok;
+    ok = require_equal("literal dollar", unix_expand_path("disk$"), "disk$") && ok;
+    ok = require_equal("legacy linux home", unix_expand_path("/home/winuae-home/roms"), "/tmp/winuae-home/roms") && ok;
+    ok = require_equal("named home unsupported", unix_expand_path("~other/roms"), "~other/roms") && ok;
+    ok = require_equal("relative absolute path", unix_absolute_path("roms/kick.rom", "/tmp/winuae-base"), "/tmp/winuae-base/roms/kick.rom") && ok;
+    ok = require_equal("relative parent normalization", unix_absolute_path("../disk.adf", "/tmp/winuae-base/configs"), "/tmp/winuae-base/disk.adf") && ok;
+    ok = require_equal("absolute normalization", unix_absolute_path("/tmp/winuae-base/../disk.adf"), "/tmp/disk.adf") && ok;
+    ok = require_equal("unresolved environment absolute path", unix_absolute_path("$WINUAE_TEST_MISSING/kick.rom", "/tmp/winuae-base"), "$WINUAE_TEST_MISSING/kick.rom") && ok;
+    ok = require_equal("unresolved named home absolute path", unix_absolute_path("~other/roms", "/tmp/winuae-base"), "~other/roms") && ok;
+
+    return ok ? 0 : 1;
+}
diff --git a/od-unix/stubs.cpp b/od-unix/stubs.cpp
new file mode 100644 (file)
index 0000000..f6409ba
--- /dev/null
@@ -0,0 +1,203 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "options.h"
+#include "traps.h"
+#include "memory.h"
+#include "autoconf.h"
+#include "audio.h"
+#include "cpuboard.h"
+#include "debug.h"
+#include "filesys.h"
+#include "fsdb.h"
+#include "gfxboard.h"
+#include "inputdevice.h"
+#include "keyboard.h"
+#include "rommgr.h"
+#include "savestate.h"
+#include "sampler.h"
+#include "sana2.h"
+#include "scsidev.h"
+#include "statusline.h"
+#include "ethernet.h"
+#include "uae/string.h"
+#include "videograb.h"
+#include "zfile.h"
+#include "zarchive.h"
+
+int consoleopen;
+int log_scsi;
+int log_net;
+int log_vsync;
+int debug_vsync_min_delay;
+int debug_vsync_forced_delay;
+int uaelib_debug;
+int pissoff_value = 15000 * CYCLE_UNIT;
+int multithread_enabled = 1;
+int p96syncrate = 312;
+int p96refresh_active;
+int max_uae_width = 8192;
+int max_uae_height = 8192;
+int pissoff_nojit_value = 160 * CYCLE_UNIT;
+#ifndef BSDSOCKET
+volatile int bsd_int_requested;
+#endif
+
+void machdep_free(void) {}
+void protect_roms(bool) {}
+void debugger_change(int) {}
+void pausevideograb(int) {}
+bool getpausevideograb(void) { return false; }
+uae_s64 getsetpositionvideograb(uae_s64) { return -1; }
+#ifndef WINUAE_UNIX_WITH_SAMPLER
+int sampler_init(void) { return 0; }
+void sampler_free(void) {}
+void sampler_vsync(void) {}
+uae_u8 sampler_getsample(int) { return 0; }
+float sampler_evtime;
+int unix_sampler_device_count(void) { return 0; }
+const TCHAR *unix_sampler_device_name(int) { return _T(""); }
+const TCHAR *unix_sampler_device_config_name(int) { return _T(""); }
+int unix_sampler_device_index_from_config_name(const TCHAR *) { return -1; }
+#endif
+int audio_is_pull(void) { return 0; }
+bool audio_is_pull_event(void) { return false; }
+int audio_pull_buffer(void) { return 0; }
+bool audio_finish_pull(void) { return false; }
+void save_log_open(void) {}
+void update_debug_info(void) {}
+void statusline_updated(int) {}
+#ifndef BSDSOCKET
+void bsdsock_fake_int_handler(void) { bsd_int_requested = 0; }
+#endif
+
+#ifndef GFXBOARD
+void gfxboard_vsync_handler(bool, bool) {}
+#endif
+
+void ldp_render(const char *, int, uae_u8 *, struct vidbuffer *, int, int, int, int)
+{
+}
+
+#ifndef WITH_CPUBOARD
+bool cpuboard_autoconfig_init(struct autoconfig_info *) { return false; }
+bool cpuboard_maprom(void) { return false; }
+void cpuboard_map(void) {}
+void cpuboard_reset(int) {}
+void cpuboard_rethink(void) {}
+void cpuboard_cleanup(void) {}
+void cpuboard_init(void) {}
+void cpuboard_clear(void) {}
+int cpuboard_memorytype(struct uae_prefs *) { return 0; }
+int cpuboard_maxmemory(struct uae_prefs *) { return 0; }
+bool cpuboard_32bit(struct uae_prefs *) { return false; }
+bool cpuboard_io_special(int, uae_u32 *, int, bool) { return false; }
+void cpuboard_overlay_override(void) {}
+uaecptr cpuboard_get_reset_pc(uaecptr *) { return 0; }
+void cpuboard_set_flash_unlocked(bool) {}
+bool cpuboard_forced_hardreset(void) { return false; }
+bool cpuboard_fc_check(uaecptr, uae_u32 *, int, bool) { return false; }
+void cpuboard_gvpmaprom(int) {}
+#endif
+void unprotect_maprom(void) {}
+#ifndef WITH_CPUBOARD
+void cyberstorm_scsi_ram_put(uaecptr, uae_u32) {}
+uae_u32 cyberstorm_scsi_ram_get(uaecptr) { return 0; }
+int REGPARAM2 cyberstorm_scsi_ram_check(uaecptr, uae_u32) { return 0; }
+uae_u8 *REGPARAM2 cyberstorm_scsi_ram_xlate(uaecptr) { return NULL; }
+void cyberstorm_mk3_ppc_irq(int, int) {}
+void blizzardppc_irq(int, int) {}
+void cyberstorm_mk3_ppc_irq_setonly(int, int) {}
+#endif
+#ifndef WITH_PCI
+void wildfire_ncr815_irq(int, int) {}
+#endif
+
+struct netdriverdata **target_ethernet_enumerate(void)
+{
+#if defined(WITH_SLIRP) || defined(WITH_UAENET_PCAP)
+    static netdriverdata *drivers[MAX_TOTAL_NET_DEVICES];
+    memset(drivers, 0, sizeof drivers);
+    ethernet_enumerate(drivers, 0);
+    return drivers;
+#else
+    static netdriverdata *none[1] = { NULL };
+    return none;
+#endif
+}
+
+#ifndef WITH_UAENET_PCAP
+void ethernet_pause(int) {}
+void ethernet_reset(void) {}
+#endif
+
+#ifndef WITH_PCI
+bool ariadne2_init(struct autoconfig_info *) { return false; }
+bool hydra_init(struct autoconfig_info *) { return false; }
+bool lanrover_init(struct autoconfig_info *) { return false; }
+bool xsurf_init(struct autoconfig_info *) { return false; }
+bool xsurf100_init(struct autoconfig_info *) { return false; }
+#endif
+
+#ifndef WINUAE_UNIX_WITH_ARCHIVES
+struct zvolume *archive_directory_plain(struct zfile *) { return NULL; }
+struct zvolume *archive_directory_lha(struct zfile *) { return NULL; }
+struct zvolume *archive_directory_zip(struct zfile *) { return NULL; }
+struct zvolume *archive_directory_7z(struct zfile *) { return NULL; }
+struct zfile *archive_access_7z(struct znode *) { return NULL; }
+struct zvolume *archive_directory_rar(struct zfile *) { return NULL; }
+struct zfile *archive_access_rar(struct znode *) { return NULL; }
+struct zvolume *archive_directory_lzx(struct zfile *) { return NULL; }
+struct zfile *archive_access_lzx(struct znode *) { return NULL; }
+struct zvolume *archive_directory_arcacc(struct zfile *, unsigned int) { return NULL; }
+struct zvolume *archive_directory_adf(struct znode *, struct zfile *) { return NULL; }
+struct zvolume *archive_directory_rdb(struct zfile *) { return NULL; }
+struct zvolume *archive_directory_fat(struct zfile *) { return NULL; }
+struct zvolume *archive_directory_tar(struct zfile *) { return NULL; }
+struct zfile *archive_access_select(struct znode *, struct zfile *, unsigned int, int, int *retcode, int)
+{
+    if (retcode) {
+        *retcode = 0;
+    }
+    return NULL;
+}
+void archive_access_scan(struct zfile *, zfile_callback, void *, unsigned int) {}
+void archive_access_close(void *, unsigned int) {}
+struct zfile *archive_unpackzfile(struct zfile *zf) { return zf; }
+struct zfile *archive_access_lha(struct znode *) { return NULL; }
+struct zfile *archive_getzfile(struct znode *, unsigned int, int) { return NULL; }
+int isfat(uae_u8 *) { return 0; }
+#endif
+
+#ifndef SCSIEMU
+int scsi_do_disk_change(int, int, int *pollmode) { if (pollmode) *pollmode = 0; return 0; }
+uae_u32 scsi_get_cd_drive_mask(void) { return 0; }
+uae_u32 scsi_get_cd_drive_media_mask(void) { return 0; }
+int scsi_add_tape(struct uaedev_config_info *) { return -1; }
+uae_u8 *save_scsidev(int, size_t *len, uae_u8 *dst) { if (len) *len = 0; return dst; }
+uae_u8 *restore_scsidev(uae_u8 *src) { return src; }
+#endif
+
+a_inode *custom_fsdb_lookup_aino_aname(a_inode *, const TCHAR *) { return NULL; }
+a_inode *custom_fsdb_lookup_aino_nname(a_inode *, const TCHAR *) { return NULL; }
+int custom_fsdb_used_as_nname(a_inode *, const TCHAR *) { return 0; }
+
+bool gui_ask_disk(int, TCHAR *) { return false; }
+
+void filesys_addexternals(void) {}
+int target_get_volume_name(struct uaedev_mount_info *, struct uaedev_config_info *, bool, bool, int) { return 0; }
+uae_u8 *target_load_keyfile(struct uae_prefs *, const TCHAR *, int *size, TCHAR *)
+{
+    if (size) {
+        *size = 0;
+    }
+    return NULL;
+}
+uae_u32 emulib_target_getcpurate(uae_u32, uae_u32 *low)
+{
+    if (low) {
+        *low = 0;
+    }
+    return 0;
+}
+int is_touch_lightpen(void) { return 0; }
diff --git a/od-unix/support.cpp b/od-unix/support.cpp
new file mode 100644 (file)
index 0000000..07e6a3c
--- /dev/null
@@ -0,0 +1,181 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "options.h"
+#include "uae.h"
+#include "uae/time.h"
+#include "memory.h"
+#include "newcpu.h"
+#include "host.h"
+
+#include <signal.h>
+#include <string.h>
+
+uae_u8 *natmem_offset;
+uae_u8 *natmem_reserved;
+size_t natmem_reserved_size;
+
+static volatile sig_atomic_t unix_signal_quit_requested;
+static volatile sig_atomic_t unix_exit_signal;
+static bool unix_signal_quit_dispatched;
+static bool unix_signals_installed;
+
+static void unix_alarm_handler(int)
+{
+    int sig = unix_exit_signal ? unix_exit_signal : SIGTERM;
+    _exit(128 + sig);
+}
+
+static void unix_signal_handler(int sig)
+{
+    unix_signal_quit_requested = 1;
+    unix_exit_signal = sig;
+    quit_program = -UAE_QUIT;
+    set_special_exter(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE);
+    alarm(2);
+}
+
+bool unix_host_quit_requested(void)
+{
+    return unix_signal_quit_requested != 0;
+}
+
+void unix_host_check_quit(void)
+{
+    if (unix_signal_quit_requested && !unix_signal_quit_dispatched) {
+        unix_signal_quit_dispatched = true;
+        uae_quit();
+        set_special(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE);
+    }
+}
+
+void setup_brkhandler(void)
+{
+    if (unix_signals_installed) {
+        return;
+    }
+
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(sa));
+    sigemptyset(&sa.sa_mask);
+    sa.sa_handler = unix_signal_handler;
+
+    sigaction(SIGINT, &sa, NULL);
+    sigaction(SIGTERM, &sa, NULL);
+    sigaction(SIGHUP, &sa, NULL);
+
+    sa.sa_handler = unix_alarm_handler;
+    sigaction(SIGALRM, &sa, NULL);
+    unix_signals_installed = true;
+}
+
+int machdep_init(void)
+{
+    setup_brkhandler();
+    uae_time_init();
+    return 1;
+}
+
+int sleep_resolution;
+
+int sleep_millis(int ms)
+{
+    if (ms <= 0) {
+        unix_host_check_quit();
+        return 0;
+    }
+    struct timespec ts;
+    ts.tv_sec = ms / 1000;
+    ts.tv_nsec = (ms % 1000) * 1000000;
+    while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {
+        unix_host_check_quit();
+        if (unix_host_quit_requested()) {
+            break;
+        }
+    }
+    unix_host_check_quit();
+    return 0;
+}
+
+int sleep_millis_main(int ms)
+{
+    return sleep_millis(ms);
+}
+
+int sleep_millis_amiga(int ms)
+{
+    return sleep_millis(ms);
+}
+
+void sleep_cpu_wakeup(void)
+{
+}
+
+int target_sleep_nanos(int ns)
+{
+    if (ns <= 0) {
+        unix_host_check_quit();
+        return 0;
+    }
+    struct timespec ts;
+    ts.tv_sec = ns / 1000000000;
+    ts.tv_nsec = ns % 1000000000;
+    while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {
+        unix_host_check_quit();
+        if (unix_host_quit_requested()) {
+            break;
+        }
+    }
+    unix_host_check_quit();
+    return 0;
+}
+
+uae_atomic atomic_and(volatile uae_atomic *p, uae_u32 v)
+{
+    return __sync_and_and_fetch(p, (uae_atomic)v);
+}
+
+uae_atomic atomic_or(volatile uae_atomic *p, uae_u32 v)
+{
+    return __sync_or_and_fetch(p, (uae_atomic)v);
+}
+
+uae_atomic atomic_inc(volatile uae_atomic *p)
+{
+    return __sync_add_and_fetch(p, 1);
+}
+
+uae_atomic atomic_dec(volatile uae_atomic *p)
+{
+    return __sync_sub_and_fetch(p, 1);
+}
+
+uae_u32 atomic_bit_test_and_reset(volatile uae_atomic *p, uae_u32 v)
+{
+    uae_atomic mask = (uae_atomic)1 << v;
+    uae_atomic old = __sync_fetch_and_and(p, ~mask);
+    return (old & mask) != 0;
+}
+
+uae_u32 getlocaltime(void)
+{
+    return (uae_u32)time(NULL);
+}
+
+void target_run(void) {}
+void target_quit(void) {}
+void target_restart(void) {}
+void target_reset(void) {}
+void target_cpu_speed(void) {}
+void target_addtorecent(const TCHAR*, int) {}
+void target_setdefaultstatefilename(const TCHAR*) {}
+bool target_osd_keyboard(int) { return false; }
+void target_osk_control(int, int, int, int) {}
+bool isguiactive(void) { return false; }
+bool is_mainthread(void) { return true; }
+void fpux_save(int*) {}
+void fpux_restore(int*) {}
diff --git a/od-unix/sysconfig.h b/od-unix/sysconfig.h
new file mode 100644 (file)
index 0000000..78c4d1d
--- /dev/null
@@ -0,0 +1,264 @@
+#ifndef WINUAE_OD_UNIX_SYSCONFIG_H
+#define WINUAE_OD_UNIX_SYSCONFIG_H
+
+#define UAE_TARGET_UNIX 1
+
+#if defined(__APPLE__)
+#define UAE_HOST_DARWIN 1
+#define MACOSX 1
+#elif defined(__linux__)
+#define UAE_HOST_LINUX 1
+#endif
+
+#if defined(__linux__) && (defined(__x86_64__) || defined(__i386__))
+#define HAVE_STRUCT_UCONTEXT_UC_MCONTEXT_GREGS 1
+#endif
+
+#if !defined(ARM64) && !defined(_M_ARM64) && !defined(__aarch64__) && \
+    (defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) || \
+     defined(_M_IX86) || defined(i386) || defined(__i386) || \
+     defined(__i386__) || defined(_X86_))
+#define PCEM_VOODOO_CODEGEN 1
+#endif
+
+#define SUPPORT_THREADS 1
+#define MAX_DPATH 1000
+#define MAX_AMIGAMONITORS 4
+#define MAX_AMIGADISPLAYS 4
+#define NATMEM_OFFSET natmem_offset
+#define PACKAGE_STRING "WinUAE Unix"
+#if defined(UAE_HOST_DARWIN)
+#define WINUAE_UNIX_WINDOW_TITLE "WinUAE for macOS"
+#elif defined(UAE_HOST_LINUX)
+#define WINUAE_UNIX_WINDOW_TITLE "WinUAE for Linux"
+#else
+#define WINUAE_UNIX_WINDOW_TITLE "WinUAE for Unix"
+#endif
+
+#define GFXFILTER 1
+#define FILESYS 1
+#define UAE_FILESYS_THREADS 1
+#define AUTOCONFIG 1
+#define DEBUGGER 1
+#define ECS_DENISE 1
+#define AGA 1
+#define CD32 1
+#define CDTV 1
+#define FPUEMU 1
+#define FPU_UAE 1
+#define MMUEMU 1
+#define FULLMMU 1
+#define CPUEMU_0 1
+#define CPUEMU_11 1
+#define CPUEMU_13 1
+#define CPUEMU_20 1
+#define CPUEMU_21 1
+#define CPUEMU_22 1
+#define CPUEMU_23 1
+#define CPUEMU_24 1
+#define CPUEMU_31 1
+#define CPUEMU_32 1
+#define CPUEMU_33 1
+#define CPUEMU_34 1
+#define CPUEMU_35 1
+#define CPUEMU_40 1
+#define CPUEMU_50 1
+#define ACTION_REPLAY 1
+#define SAVESTATE 1
+#define A2091 1
+#define PICASSO96_SUPPORTED 1
+#define PICASSO96 1
+#define SCP 1
+#define FDI2RAW 1
+#define ARCADIA 1
+#define AMAX 1
+#define WITH_SOFTFLOAT 1
+#define DRIVESOUND 1
+#define PARALLEL_PORT 1
+#define SERIAL_PORT 1
+
+#define CAN_PRINTF_LONG_LONG 1
+#define RETSIGTYPE void
+#define TIME_WITH_SYS_TIME 1
+
+#define HAVE_DIRENT_H 1
+#define HAVE_FCNTL_H 1
+#define HAVE_GETCWD 1
+#define HAVE_GETTIMEOFDAY 1
+#define HAVE_INET_ATON 1
+#define HAVE_MKDIR 1
+#define HAVE_RMDIR 1
+#define HAVE_SELECT 1
+#define HAVE_STRDUP 1
+#define HAVE_STRERROR 1
+#define HAVE_STDINT_H 1
+#define HAVE_STRING_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_SYS_MMAN_H 1
+#define HAVE_SYS_PARAM_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_UNISTD_H 1
+#define HAVE_UTIME_H 1
+#define HAVE_VFPRINTF 1
+#define HAVE_VPRINTF 1
+#define HAVE_VSPRINTF 1
+
+#define SIZEOF_CHAR 1
+#define SIZEOF_SHORT 2
+#define SIZEOF_INT 4
+#if defined(__LP64__)
+#define SIZEOF_LONG 8
+#define SIZEOF_VOID_P 8
+#define CPU_64_BIT 1
+#else
+#define SIZEOF_LONG 4
+#define SIZEOF_VOID_P 4
+#endif
+#define SIZEOF_LONG_LONG 8
+#define SIZEOF_FLOAT 4
+#define SIZEOF_DOUBLE 8
+
+#define LT_MODULE_EXT _T(".dylib")
+#if defined(UAE_HOST_LINUX)
+#undef LT_MODULE_EXT
+#define LT_MODULE_EXT _T(".so")
+#endif
+
+#define FSDB_DIR_SEPARATOR '/'
+#define FSDB_DIR_SEPARATOR_S _T("/")
+
+#ifndef PATH_MAX
+#define PATH_MAX MAX_DPATH
+#endif
+#ifndef MAX_PATH
+#define MAX_PATH MAX_DPATH
+#endif
+#ifndef MAXINT
+#define MAXINT INT_MAX
+#endif
+
+#define UAE_RAND_MAX 2147483647
+#define ZEXPORT
+#ifndef __cdecl
+#define __cdecl
+#endif
+
+#include <stdint.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+#include <time.h>
+
+typedef long uae_atomic;
+typedef int boolean;
+
+#ifdef WINUAE_UNIX_WITH_GFXBOARD
+typedef int8_t INT8;
+typedef uint8_t UINT8;
+typedef int16_t INT16;
+typedef uint16_t UINT16;
+typedef int32_t INT32;
+typedef uint32_t UINT32;
+typedef signed long long INT64;
+typedef unsigned long long UINT64;
+#endif
+
+#define BYTE int8_t
+#define WORD int16_t
+#define UBYTE uint8_t
+#define UWORD uint16_t
+#define USHORT uint16_t
+#define ULONG uint32_t
+#define DWORD uint32_t
+#define UINT uint32_t
+#define BOOL int
+#define HANDLE void*
+#define HWND void*
+#define HINSTANCE void*
+#define HMODULE void*
+#define WPARAM uintptr_t
+#define LPARAM intptr_t
+#define LRESULT intptr_t
+#define INT_PTR intptr_t
+#define CALLBACK
+#define WINAPI
+#define PASCAL
+#define _cdecl
+#define _stdcall
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+#define INVALID_HANDLE_VALUE ((HANDLE)-1)
+#define INVALID_SOCKET (-1)
+#define SOCKET_ERROR (-1)
+
+#ifdef WINUAE_UNIX_WITH_PCEM
+#ifndef INFINITE
+#define INFINITE 0xffffffffU
+#endif
+#ifndef WAIT_OBJECT_0
+#define WAIT_OBJECT_0 0
+#endif
+#ifndef WAIT_TIMEOUT
+#define WAIT_TIMEOUT 258
+#endif
+HANDLE CreateSemaphore(void*, int, int, const char*);
+DWORD WaitForSingleObject(HANDLE, DWORD);
+BOOL ReleaseSemaphore(HANDLE, int, void*);
+BOOL CloseHandle(HANDLE);
+#endif
+
+#define Sleep sleep_millis
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+#define _stricmp strcasecmp
+#define _strnicmp strncasecmp
+#define _tzset tzset
+#define _ftime ftime
+#define _timeb timeb
+#define _istalnum isalnum
+#define _daylight daylight
+
+static inline long uae_unix_timezone_offset(void)
+{
+    time_t now = time(NULL);
+    struct tm local_tm;
+    struct tm gm_tm;
+    localtime_r(&now, &local_tm);
+    gmtime_r(&now, &gm_tm);
+    return (long)difftime(mktime(&gm_tm), mktime(&local_tm));
+}
+
+#define _timezone uae_unix_timezone_offset()
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_NDELAY
+#define O_NDELAY O_NONBLOCK
+#endif
+
+#define FILEFLAG_WRITE S_IWUSR
+#define FILEFLAG_READ S_IRUSR
+#define FILEFLAG_EXECUTE S_IXUSR
+#define FILEFLAG_DIR S_IFDIR
+
+#define UAESCSI_CDEMU 0
+#define UAESCSI_SPTI 1
+#define UAESCSI_SPTISCAN 2
+#define UAESCSI_LAST 2
+#define UAESCSI_ASPI_FIRST 3
+#define UAESCSI_ADAPTECASPI 3
+#define UAESCSI_NEROASPI 4
+#define UAESCSI_FROGASPI 5
+
+#define DRIVE_CDROM 5
+
+#endif /* WINUAE_OD_UNIX_SYSCONFIG_H */
diff --git a/od-unix/target.h b/od-unix/target.h
new file mode 100644 (file)
index 0000000..b9d4565
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef WINUAE_OD_UNIX_TARGET_H
+#define WINUAE_OD_UNIX_TARGET_H
+
+#define TARGET_NAME _T("unix")
+#define OPTIONSFILENAME _T("default.uae")
+
+#define TARGET_ROM_PATH _T("~/")
+#define TARGET_FLOPPY_PATH _T("~/")
+#define TARGET_HARDFILE_PATH _T("~/")
+#define TARGET_SAVESTATE_PATH _T("~/")
+
+#define DEFPRTNAME _T("lpr")
+#define DEFSERNAME _T("/dev/ttyS0")
+
+static inline int uae_deterministic_mode(void)
+{
+    return 0;
+}
+
+#endif /* WINUAE_OD_UNIX_TARGET_H */
diff --git a/od-unix/tchar.h b/od-unix/tchar.h
new file mode 100644 (file)
index 0000000..f493c6b
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef WINUAE_OD_UNIX_TCHAR_H
+#define WINUAE_OD_UNIX_TCHAR_H
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+
+typedef char TCHAR;
+
+#ifndef _T
+#define _T(x) x
+#endif
+
+static inline FILE *uae_unix_tfopen(const TCHAR *path, const TCHAR *mode)
+{
+    char unixmode[8];
+    size_t out = 0;
+    for (size_t i = 0; mode[i] != 0 && mode[i] != ',' && out + 1 < sizeof(unixmode); i++) {
+        if (mode[i] == 't' || mode[i] == ' ') {
+            continue;
+        }
+        unixmode[out++] = mode[i];
+    }
+    unixmode[out] = 0;
+    return fopen(path, unixmode[0] ? unixmode : mode);
+}
+
+#define _istdigit isdigit
+#define _istspace isspace
+#define _istupper isupper
+#define _istxdigit isxdigit
+#define _sntprintf snprintf
+#define _stscanf sscanf
+#define _stprintf sprintf
+#define _strtoui64 strtoull
+#define _tcscat strcat
+#define _tcschr strchr
+#define _tcscmp strcmp
+#define _tcscpy strcpy
+#define _tcscspn strcspn
+#define _tcsdup strdup
+#define _tcsftime strftime
+#define _tcsicmp strcasecmp
+#define _tcslen strlen
+#define _tcsncat strncat
+#define _tcsncmp strncmp
+#define _tcsncpy strncpy
+#define _tcsnicmp strncasecmp
+#define _tcsrchr strrchr
+#define _tcsspn strspn
+#define _tcsstr strstr
+#define _tcstod strtod
+#define _tcstok strtok
+#define _tcstol strtol
+#define _tcstoul strtoul
+#define _totlower tolower
+#define _totupper toupper
+#define _tprintf printf
+#define _tstof atof
+#define _tstoi atoi
+#define _tstoi64 atoll
+#define _tstol atol
+#define _tfopen uae_unix_tfopen
+#define _tfopen64 uae_unix_tfopen
+#define _ftelli64 ftello
+#define _fseeki64 fseeko
+#define _tunlink unlink
+#define _vsnprintf vsnprintf
+#define _vsntprintf vsnprintf
+#define fgetws fgets
+#define fputws fputs
+#define _wunlink unlink
+#define swscanf_s sscanf
+
+#endif /* WINUAE_OD_UNIX_TCHAR_H */
diff --git a/od-unix/threaddep/thread.h b/od-unix/threaddep/thread.h
new file mode 100644 (file)
index 0000000..6a9da86
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef WINUAE_OD_UNIX_THREADDEP_THREAD_H
+#define WINUAE_OD_UNIX_THREADDEP_THREAD_H
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <time.h>
+#include <errno.h>
+#include "uae/types.h"
+
+typedef pthread_t uae_thread_id;
+
+struct uae_unix_sem {
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    int signaled;
+    int manual_reset;
+};
+
+typedef struct uae_unix_sem* uae_sem_t;
+
+typedef void (*uae_thread_function)(void *);
+
+void uae_sem_destroy(uae_sem_t*);
+int uae_sem_trywait(uae_sem_t*);
+int uae_sem_trywait_delay(uae_sem_t*, int);
+void uae_sem_post(uae_sem_t*);
+void uae_sem_unpost(uae_sem_t*);
+void uae_sem_wait(uae_sem_t*);
+void uae_sem_init(uae_sem_t*, int manual_reset, int initial_state);
+
+int uae_start_thread(const TCHAR *name, uae_thread_function f, void *arg, uae_thread_id *thread);
+int uae_start_thread_fast(uae_thread_function f, void *arg, uae_thread_id *thread);
+void uae_end_thread(uae_thread_id *thread);
+void uae_set_thread_priority(uae_thread_id *, int);
+uae_thread_id uae_thread_get_id(void);
+
+static inline void uae_wait_thread(uae_thread_id tid)
+{
+    pthread_join(tid, NULL);
+}
+
+#define BAD_THREAD 0
+#define UAE_THREAD_EXIT do { return; } while (0)
+
+#include "commpipe.h"
+
+#endif /* WINUAE_OD_UNIX_THREADDEP_THREAD_H */
diff --git a/od-unix/threading.cpp b/od-unix/threading.cpp
new file mode 100644 (file)
index 0000000..73a3ff4
--- /dev/null
@@ -0,0 +1,188 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "threaddep/thread.h"
+#include "uae.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void unix_sem_timeout_from_now(struct timespec *ts, int ms)
+{
+    clock_gettime(CLOCK_REALTIME, ts);
+    ts->tv_sec += ms / 1000;
+    ts->tv_nsec += (long)(ms % 1000) * 1000000L;
+    if (ts->tv_nsec >= 1000000000L) {
+        ts->tv_sec++;
+        ts->tv_nsec -= 1000000000L;
+    }
+}
+
+void uae_sem_init(uae_sem_t *sem, int manual_reset, int initial_state)
+{
+    if (!sem) {
+        return;
+    }
+    if (*sem) {
+        pthread_mutex_lock(&(*sem)->mutex);
+        (*sem)->manual_reset = manual_reset ? 1 : 0;
+        (*sem)->signaled = initial_state ? 1 : 0;
+        if ((*sem)->signaled) {
+            pthread_cond_broadcast(&(*sem)->cond);
+        }
+        pthread_mutex_unlock(&(*sem)->mutex);
+        return;
+    }
+    *sem = (uae_sem_t)calloc(1, sizeof(**sem));
+    if (!*sem) {
+        abort();
+    }
+    pthread_mutex_init(&(*sem)->mutex, NULL);
+    pthread_cond_init(&(*sem)->cond, NULL);
+    (*sem)->manual_reset = manual_reset ? 1 : 0;
+    (*sem)->signaled = initial_state ? 1 : 0;
+}
+
+void uae_sem_destroy(uae_sem_t *sem)
+{
+    if (!sem || !*sem) {
+        return;
+    }
+    pthread_cond_destroy(&(*sem)->cond);
+    pthread_mutex_destroy(&(*sem)->mutex);
+    free(*sem);
+    *sem = NULL;
+}
+
+void uae_sem_post(uae_sem_t *sem)
+{
+    if (!sem || !*sem) {
+        return;
+    }
+    pthread_mutex_lock(&(*sem)->mutex);
+    (*sem)->signaled = 1;
+    if ((*sem)->manual_reset) {
+        pthread_cond_broadcast(&(*sem)->cond);
+    } else {
+        pthread_cond_signal(&(*sem)->cond);
+    }
+    pthread_mutex_unlock(&(*sem)->mutex);
+}
+
+void uae_sem_unpost(uae_sem_t *sem)
+{
+    if (!sem || !*sem) {
+        return;
+    }
+    pthread_mutex_lock(&(*sem)->mutex);
+    (*sem)->signaled = 0;
+    pthread_mutex_unlock(&(*sem)->mutex);
+}
+
+void uae_sem_wait(uae_sem_t *sem)
+{
+    if (!sem || !*sem) {
+        return;
+    }
+    pthread_mutex_lock(&(*sem)->mutex);
+    while (!(*sem)->signaled) {
+        pthread_cond_wait(&(*sem)->cond, &(*sem)->mutex);
+    }
+    if (!(*sem)->manual_reset) {
+        (*sem)->signaled = 0;
+    }
+    pthread_mutex_unlock(&(*sem)->mutex);
+}
+
+int uae_sem_trywait(uae_sem_t *sem)
+{
+    return uae_sem_trywait_delay(sem, 0);
+}
+
+int uae_sem_trywait_delay(uae_sem_t *sem, int ms)
+{
+    int result = -1;
+    if (!sem || !*sem) {
+        return result;
+    }
+    pthread_mutex_lock(&(*sem)->mutex);
+    if (!(*sem)->signaled && ms != 0) {
+        if (ms < 0) {
+            while (!(*sem)->signaled) {
+                pthread_cond_wait(&(*sem)->cond, &(*sem)->mutex);
+            }
+        } else {
+            struct timespec ts;
+            unix_sem_timeout_from_now(&ts, ms);
+            while (!(*sem)->signaled) {
+                int err = pthread_cond_timedwait(&(*sem)->cond, &(*sem)->mutex, &ts);
+                if (err == ETIMEDOUT) {
+                    break;
+                }
+            }
+        }
+    }
+    if ((*sem)->signaled) {
+        if (!(*sem)->manual_reset) {
+            (*sem)->signaled = 0;
+        }
+        result = 0;
+    }
+    pthread_mutex_unlock(&(*sem)->mutex);
+    return result;
+}
+
+struct thread_start_data {
+    uae_thread_function fn;
+    void *arg;
+};
+
+static void *thread_entry(void *data)
+{
+    thread_start_data *tsd = (thread_start_data*)data;
+    uae_thread_function fn = tsd->fn;
+    void *arg = tsd->arg;
+    free(tsd);
+    fn(arg);
+    return NULL;
+}
+
+int uae_start_thread(const TCHAR *, uae_thread_function f, void *arg, uae_thread_id *thread)
+{
+    thread_start_data *tsd = (thread_start_data*)calloc(1, sizeof(*tsd));
+    tsd->fn = f;
+    tsd->arg = arg;
+    pthread_t tid;
+    if (pthread_create(&tid, NULL, thread_entry, tsd) != 0) {
+        free(tsd);
+        return 0;
+    }
+    if (thread) {
+        *thread = tid;
+    } else {
+        pthread_detach(tid);
+    }
+    return 1;
+}
+
+int uae_start_thread_fast(uae_thread_function f, void *arg, uae_thread_id *thread)
+{
+    return uae_start_thread(NULL, f, arg, thread);
+}
+
+void uae_end_thread(uae_thread_id *thread)
+{
+    if (thread) {
+        *thread = 0;
+    }
+}
+
+void uae_set_thread_priority(uae_thread_id *, int)
+{
+}
+
+uae_thread_id uae_thread_get_id(void)
+{
+    return pthread_self();
+}
diff --git a/od-unix/threading_test.cpp b/od-unix/threading_test.cpp
new file mode 100644 (file)
index 0000000..1d04650
--- /dev/null
@@ -0,0 +1,54 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <stdio.h>
+
+#include "threaddep/thread.h"
+
+static bool require_int(const char *label, int actual, int expected)
+{
+    if (actual == expected) {
+        return true;
+    }
+    fprintf(stderr, "%s: expected %d, got %d\n", label, expected, actual);
+    return false;
+}
+
+int main()
+{
+    bool ok = true;
+
+    uae_sem_t sem = NULL;
+    uae_sem_init(&sem, 0, 0);
+    ok = require_int("auto initial timeout", uae_sem_trywait(&sem), -1) && ok;
+    uae_sem_post(&sem);
+    ok = require_int("auto post wakes once", uae_sem_trywait(&sem), 0) && ok;
+    ok = require_int("auto post consumed", uae_sem_trywait(&sem), -1) && ok;
+    uae_sem_post(&sem);
+    uae_sem_post(&sem);
+    ok = require_int("auto repeated post coalesces", uae_sem_trywait(&sem), 0) && ok;
+    ok = require_int("auto repeated post consumed once", uae_sem_trywait(&sem), -1) && ok;
+    uae_sem_post(&sem);
+    uae_sem_unpost(&sem);
+    ok = require_int("auto unpost clears signal", uae_sem_trywait(&sem), -1) && ok;
+    uae_sem_destroy(&sem);
+    ok = require_int("destroy clears handle", sem == NULL ? 0 : 1, 0) && ok;
+
+    uae_sem_init(&sem, 1, 0);
+    ok = require_int("manual initial timeout", uae_sem_trywait(&sem), -1) && ok;
+    uae_sem_post(&sem);
+    ok = require_int("manual post first wait", uae_sem_trywait(&sem), 0) && ok;
+    ok = require_int("manual remains signaled", uae_sem_trywait(&sem), 0) && ok;
+    uae_sem_unpost(&sem);
+    ok = require_int("manual unpost clears signal", uae_sem_trywait(&sem), -1) && ok;
+    uae_sem_init(&sem, 1, 1);
+    ok = require_int("manual reinit existing sets signal", uae_sem_trywait(&sem), 0) && ok;
+    uae_sem_destroy(&sem);
+
+    uae_sem_post(&sem);
+    uae_sem_unpost(&sem);
+    uae_sem_wait(&sem);
+    ok = require_int("null trywait fails", uae_sem_trywait(&sem), -1) && ok;
+
+    return ok ? 0 : 1;
+}
diff --git a/od-unix/time.cpp b/od-unix/time.cpp
new file mode 100644 (file)
index 0000000..80318ef
--- /dev/null
@@ -0,0 +1,71 @@
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#ifdef __APPLE__
+#include <mach/mach_time.h>
+#else
+#include <time.h>
+#endif
+
+#include "uae/time.h"
+
+static uint64_t epoch_ns;
+#ifdef __APPLE__
+static mach_timebase_info_data_t timebase;
+#endif
+
+static uint64_t monotonic_ns(void)
+{
+#ifdef __APPLE__
+    if (!timebase.denom) {
+        mach_timebase_info(&timebase);
+    }
+    uint64_t t = mach_absolute_time();
+    return t * timebase.numer / timebase.denom;
+#else
+    struct timespec ts;
+    if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
+        return 0;
+    }
+    return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
+#endif
+}
+
+void uae_time_init(void)
+{
+    epoch_ns = monotonic_ns();
+    syncbase = 1000000;
+}
+
+void uae_time_calibrate(void)
+{
+    syncbase = 1000000;
+}
+
+uae_time_t uae_time(void)
+{
+    if (!epoch_ns) {
+        uae_time_init();
+    }
+    return (uae_time_t)((monotonic_ns() - epoch_ns) / 1000);
+}
+
+int64_t uae_time_us(void)
+{
+    if (!epoch_ns) {
+        uae_time_init();
+    }
+    return (int64_t)((monotonic_ns() - epoch_ns) / 1000);
+}
+
+int64_t uae_time_ns(void)
+{
+    if (!epoch_ns) {
+        uae_time_init();
+    }
+    return (int64_t)(monotonic_ns() - epoch_ns);
+}
+
+void uae_time_use_rdtsc(bool)
+{
+}