Merge branch '86Box:master' into nec-v20
This commit is contained in:
13
.ci/Jenkinsfile
vendored
13
.ci/Jenkinsfile
vendored
@@ -25,7 +25,7 @@ def buildBranch = env.JOB_BASE_NAME.contains('-') ? 1 : 0
|
|||||||
def osArchs = [
|
def osArchs = [
|
||||||
'Windows': ['32', '64'],
|
'Windows': ['32', '64'],
|
||||||
'Linux': ['x86', 'x86_64', 'arm32', 'arm64'],
|
'Linux': ['x86', 'x86_64', 'arm32', 'arm64'],
|
||||||
'macOS': ['x86_64+arm64']
|
'macOS': ['x86_64+x86_64h+arm64']
|
||||||
]
|
]
|
||||||
|
|
||||||
def osFlags = [
|
def osFlags = [
|
||||||
@@ -261,12 +261,13 @@ pipeline {
|
|||||||
osArchs.each { os, thisOsArchs ->
|
osArchs.each { os, thisOsArchs ->
|
||||||
def combinations = [:]
|
def combinations = [:]
|
||||||
thisOsArchs.each { arch ->
|
thisOsArchs.each { arch ->
|
||||||
def thisArchDynarecs = dynarecArchs[arch.toLowerCase()]
|
def archSlug = arch.replace('+x86_64h', '') /* all instances of arch except the one passed to -b */
|
||||||
|
def thisArchDynarecs = dynarecArchs[archSlug.toLowerCase()]
|
||||||
if (!thisArchDynarecs)
|
if (!thisArchDynarecs)
|
||||||
thisArchDynarecs = ['NoDR']
|
thisArchDynarecs = ['NoDR']
|
||||||
thisArchDynarecs.each { dynarec ->
|
thisArchDynarecs.each { dynarec ->
|
||||||
presets.each { preset ->
|
presets.each { preset ->
|
||||||
def combination = "$os $arch $dynarec $preset"
|
def combination = "$os $archSlug $dynarec $preset"
|
||||||
combinations[combination] = {
|
combinations[combination] = {
|
||||||
catchError(buildResult: 'FAILURE', stageResult: 'SUCCESS') {
|
catchError(buildResult: 'FAILURE', stageResult: 'SUCCESS') {
|
||||||
retry(10) {
|
retry(10) {
|
||||||
@@ -278,11 +279,11 @@ pipeline {
|
|||||||
/* Switch to output directory. */
|
/* Switch to output directory. */
|
||||||
dir("${env.WORKSPACE_TMP}/output") {
|
dir("${env.WORKSPACE_TMP}/output") {
|
||||||
/* Run build process. */
|
/* Run build process. */
|
||||||
def packageName = "${env.JOB_BASE_NAME}${dynarecSlugs[dynarec]}${presetSlugs[preset]}-$os-$arch$buildSuffix"
|
def packageName = "${env.JOB_BASE_NAME}${dynarecSlugs[dynarec]}${presetSlugs[preset]}-$os-$archSlug$buildSuffix"
|
||||||
def ret = -1
|
def ret = -1
|
||||||
def archName = archNames[arch]
|
def archName = archNames[archSlug]
|
||||||
if (os == 'macOS')
|
if (os == 'macOS')
|
||||||
archName = archNamesMac[arch]
|
archName = archNamesMac[archSlug]
|
||||||
dir("${dynarecNames[dynarec]}/$os - $archName") {
|
dir("${dynarecNames[dynarec]}/$os - $archName") {
|
||||||
ret = runBuild("-b \"$packageName\" \"$arch\" ${presetFlags[preset]} ${dynarecFlags[dynarec]} ${osFlags[os]} $buildFlags")
|
ret = runBuild("-b \"$packageName\" \"$arch\" ${presetFlags[preset]} ${dynarecFlags[dynarec]} ${osFlags[os]} $buildFlags")
|
||||||
}
|
}
|
||||||
|
|||||||
91
.ci/build.sh
91
.ci/build.sh
@@ -37,10 +37,17 @@
|
|||||||
# build_arch x86_64 (or arm64)
|
# build_arch x86_64 (or arm64)
|
||||||
# universal_archs (blank)
|
# universal_archs (blank)
|
||||||
# ui_interactive no
|
# ui_interactive no
|
||||||
# macosx_deployment_target 10.13 (for x86_64, or 11.0 for arm64)
|
# macosx_deployment_target 10.13 (for x86_64, 10.14 for Qt Vulkan, or 11.0 for arm64)
|
||||||
# - For universal building on Apple Silicon hardware, install native MacPorts on the default
|
# - For universal building on Apple Silicon hardware, install native MacPorts on the default
|
||||||
# /opt/local and Intel MacPorts on /opt/intel, then tell build.sh to build for "x86_64+arm64"
|
# /opt/local and Intel MacPorts on /opt/intel, then tell build.sh to build for "x86_64+arm64"
|
||||||
# - port is called through sudo to manage dependencies; make sure it is configured
|
# - Qt Vulkan support through MoltenVK requires 10.14 while we target 10.13. We deal with that
|
||||||
|
# (at least for now) by abusing the x86_64h universal slice to branch Haswell and newer Macs
|
||||||
|
# into a Vulkan-enabled but 10.14+ binary, with older ones opting for a 10.13-compatible,
|
||||||
|
# non-Vulkan binary. With this approach, the only machines that miss out on Vulkan despite
|
||||||
|
# supporting Metal are Ivy Bridge ones as well as GPU-upgraded Mac Pros. For building that
|
||||||
|
# Vulkan binary, install another Intel MacPorts on /opt/x86_64h, then use the "x86_64h"
|
||||||
|
# architecture when invoking build.sh (either standalone or as part of an universal build)
|
||||||
|
# - port and sed are called through sudo to manage dependencies; make sure those are configured
|
||||||
# as NOPASSWD in /etc/sudoers if you're doing unattended builds
|
# as NOPASSWD in /etc/sudoers if you're doing unattended builds
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -401,10 +408,10 @@ then
|
|||||||
args=
|
args=
|
||||||
[ $strip -ne 0 ] && args="-t $args"
|
[ $strip -ne 0 ] && args="-t $args"
|
||||||
case $arch_universal in # workaround: force new dynarec on for ARM
|
case $arch_universal in # workaround: force new dynarec on for ARM
|
||||||
arm32 | arm64) cmake_flags_extra="-D NEW_DYNAREC=ON";;
|
arm*) cmake_flags_extra="-D NEW_DYNAREC=ON";;
|
||||||
*) cmake_flags_extra=;;
|
*) cmake_flags_extra=;;
|
||||||
esac
|
esac
|
||||||
zsh -lc 'exec "'"$0"'" -n -b "universal part" "'"$arch_universal"'" '"$args""$cmake_flags"' '"$cmake_flags_extra"
|
zsh -lc 'exec "'"$0"'" -n -b "universal slice" "'"$arch_universal"'" '"$args""$cmake_flags"' '"$cmake_flags_extra"
|
||||||
status=$?
|
status=$?
|
||||||
|
|
||||||
if [ $status -eq 0 ]
|
if [ $status -eq 0 ]
|
||||||
@@ -538,8 +545,8 @@ then
|
|||||||
|
|
||||||
# Switch into the correct architecture if required.
|
# Switch into the correct architecture if required.
|
||||||
case $arch in
|
case $arch in
|
||||||
x86_64) arch_mac="i386";;
|
x86_64*) arch_mac="i386";;
|
||||||
*) arch_mac="$arch";;
|
*) arch_mac="$arch";;
|
||||||
esac
|
esac
|
||||||
if [ "$(arch)" != "$arch" -a "$(arch)" != "$arch_mac" ]
|
if [ "$(arch)" != "$arch" -a "$(arch)" != "$arch_mac" ]
|
||||||
then
|
then
|
||||||
@@ -560,17 +567,33 @@ then
|
|||||||
[ "$arch" = "x86_64" -a -e "/opt/intel/bin/port" ] && macports="/opt/intel"
|
[ "$arch" = "x86_64" -a -e "/opt/intel/bin/port" ] && macports="/opt/intel"
|
||||||
export PATH="$macports/bin:$macports/sbin:$macports/libexec/qt5/bin:$PATH"
|
export PATH="$macports/bin:$macports/sbin:$macports/libexec/qt5/bin:$PATH"
|
||||||
|
|
||||||
# Install dependencies only if we're in a new build and/or architecture.
|
# Enable MoltenVK on x86_64h and arm64, but not on x86_64.
|
||||||
if check_buildtag "$(arch)"
|
# The rationale behind that is explained on the big comment up top.
|
||||||
|
moltenvk=0
|
||||||
|
if [ "$arch" != "x86_64" ]
|
||||||
|
then
|
||||||
|
moltenvk=1
|
||||||
|
cmake_flags_extra="$cmake_flags_extra -D MOLTENVK=ON -D \"MOLTENVK_INCLUDE_DIR=$macports\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install dependencies only if we're in a new build and/or MacPorts prefix.
|
||||||
|
if check_buildtag "$(basename "$macports")"
|
||||||
then
|
then
|
||||||
# Install dependencies.
|
# Install dependencies.
|
||||||
echo [-] Installing dependencies through MacPorts
|
echo [-] Installing dependencies through MacPorts
|
||||||
sudo "$macports/bin/port" selfupdate
|
sudo "$macports/bin/port" selfupdate
|
||||||
|
if [ $moltenvk -ne 0 ]
|
||||||
|
then
|
||||||
|
# Patch Qt to enable Vulkan support where supported.
|
||||||
|
qt5_portfile="$macports/var/macports/sources/rsync.macports.org/macports/release/tarballs/ports/aqua/qt5/Portfile"
|
||||||
|
sudo sed -i -e 's/-no-feature-vulkan/-feature-vulkan/g' "$qt5_portfile"
|
||||||
|
sudo sed -i -e 's/configure.env-append MAKE=/configure.env-append VULKAN_SDK=${prefix} MAKE=/g' "$qt5_portfile"
|
||||||
|
fi
|
||||||
sudo "$macports/bin/port" install $(cat .ci/dependencies_macports.txt)
|
sudo "$macports/bin/port" install $(cat .ci/dependencies_macports.txt)
|
||||||
|
|
||||||
# Save build tag to skip this later. Doing it here (once everything is
|
# Save build tag to skip this later. Doing it here (once everything is
|
||||||
# in place) is important to avoid potential issues with retried builds.
|
# in place) is important to avoid potential issues with retried builds.
|
||||||
save_buildtag "$(arch)"
|
save_buildtag "$(basename "$macports")"
|
||||||
else
|
else
|
||||||
echo [-] Not installing dependencies again
|
echo [-] Not installing dependencies again
|
||||||
|
|
||||||
@@ -697,7 +720,7 @@ rm -rf build
|
|||||||
# Add ARCH to skip the arch_detect process.
|
# Add ARCH to skip the arch_detect process.
|
||||||
case $arch in
|
case $arch in
|
||||||
32 | x86) cmake_flags_extra="$cmake_flags_extra -D ARCH=i386";;
|
32 | x86) cmake_flags_extra="$cmake_flags_extra -D ARCH=i386";;
|
||||||
64 | x86_64) cmake_flags_extra="$cmake_flags_extra -D ARCH=x86_64";;
|
64 | x86_64*) cmake_flags_extra="$cmake_flags_extra -D ARCH=x86_64";;
|
||||||
ARM32 | arm32) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm";;
|
ARM32 | arm32) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm";;
|
||||||
ARM64 | arm64) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm64";;
|
ARM64 | arm64) cmake_flags_extra="$cmake_flags_extra -D ARCH=arm64";;
|
||||||
*) cmake_flags_extra="$cmake_flags_extra -D \"ARCH=$arch\"";;
|
*) cmake_flags_extra="$cmake_flags_extra -D \"ARCH=$arch\"";;
|
||||||
@@ -778,7 +801,7 @@ fi
|
|||||||
# Determine Discord Game SDK architecture.
|
# Determine Discord Game SDK architecture.
|
||||||
case $arch in
|
case $arch in
|
||||||
32) arch_discord="x86";;
|
32) arch_discord="x86";;
|
||||||
64) arch_discord="x86_64";;
|
64 | x86_64*) arch_discord="x86_64";;
|
||||||
arm64 | ARM64) arch_discord="aarch64";;
|
arm64 | ARM64) arch_discord="aarch64";;
|
||||||
*) arch_discord="$arch";;
|
*) arch_discord="$arch";;
|
||||||
esac
|
esac
|
||||||
@@ -844,6 +867,50 @@ then
|
|||||||
unzip -j "$discord_zip" "lib/$arch_discord/discord_game_sdk.dylib" -d "archive_tmp/"*".app/Contents/Frameworks"
|
unzip -j "$discord_zip" "lib/$arch_discord/discord_game_sdk.dylib" -d "archive_tmp/"*".app/Contents/Frameworks"
|
||||||
[ ! -e "archive_tmp/"*".app/Contents/Frameworks/discord_game_sdk.dylib" ] && echo [!] No Discord Game SDK for architecture [$arch_discord]
|
[ ! -e "archive_tmp/"*".app/Contents/Frameworks/discord_game_sdk.dylib" ] && echo [!] No Discord Game SDK for architecture [$arch_discord]
|
||||||
|
|
||||||
|
# Hack to convert x86_64 binaries to x86_64h when building that architecture.
|
||||||
|
if [ "$arch" = "x86_64h" ]
|
||||||
|
then
|
||||||
|
find archive_tmp -type f | while IFS= read line
|
||||||
|
do
|
||||||
|
# Parse and patch a fat header (0xCAFEBABE, big endian) first.
|
||||||
|
macho_offset=0
|
||||||
|
if [ "$(dd if="$line" bs=1 count=4 status=none)" = "$(printf '\xCA\xFE\xBA\xBE')" ]
|
||||||
|
then
|
||||||
|
# Get the number of fat architectures.
|
||||||
|
fat_archs=$(($(dd if="$line" bs=1 skip=4 count=4 status=none | rev | tr -d '\n' | od -An -vtu4)))
|
||||||
|
|
||||||
|
# Go through fat architectures.
|
||||||
|
fat_offset=8
|
||||||
|
for fat_arch in $(seq 1 $fat_archs)
|
||||||
|
do
|
||||||
|
# Check CPU type.
|
||||||
|
if [ "$(dd if="$line" bs=1 skip=$fat_offset count=4 status=none)" = "$(printf '\x01\x00\x00\x07')" ]
|
||||||
|
then
|
||||||
|
# Change CPU subtype in the fat header from ALL (0x00000003) to H (0x00000008).
|
||||||
|
printf '\x00\x00\x00\x08' | dd of="$line" bs=1 seek=$((fat_offset + 4)) count=4 conv=notrunc status=none
|
||||||
|
|
||||||
|
# Save offset for this architecture's Mach-O header.
|
||||||
|
macho_offset=$(($(dd if="$line" bs=1 skip=$((fat_offset + 8)) count=4 status=none | rev | tr -d '\n' | od -An -vtu4)))
|
||||||
|
|
||||||
|
# Stop looking for the x86_64 slice.
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move on to the next architecture.
|
||||||
|
fat_offset=$((fat_offset + 20))
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now patch a 64-bit Mach-O header (0xFEEDFACF, little endian), either at
|
||||||
|
# the beginning or as a sub-header within a fat binary as parsed above.
|
||||||
|
if [ "$(dd if="$line" bs=1 seek=$macho_offset count=8 status=none)" = "$(printf '\xCF\xFA\xED\xFE\x07\x00\x00\x01')" ]
|
||||||
|
then
|
||||||
|
# Change CPU subtype in the Mach-O header from ALL (0x00000003) to H (0x00000008).
|
||||||
|
printf '\x08\x00\x00\x00' | dd of="$line" bs=1 seek=$((macho_offset + 8)) count=4 conv=notrunc status=none
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# Sign app bundle, unless we're in an universal build.
|
# Sign app bundle, unless we're in an universal build.
|
||||||
[ $skip_archive -eq 0 ] && codesign --force --deep -s - "archive_tmp/"*".app"
|
[ $skip_archive -eq 0 ] && codesign --force --deep -s - "archive_tmp/"*".app"
|
||||||
elif [ "$BUILD_TAG" = "precondition" ]
|
elif [ "$BUILD_TAG" = "precondition" ]
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ ninja
|
|||||||
freetype
|
freetype
|
||||||
libsdl2
|
libsdl2
|
||||||
libpng
|
libpng
|
||||||
|
openal-soft
|
||||||
FAudio
|
FAudio
|
||||||
rtmidi
|
rtmidi
|
||||||
|
vulkan-headers
|
||||||
|
MoltenVK
|
||||||
qt5
|
qt5
|
||||||
wget
|
wget
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ if(MUNT_EXTERNAL)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(86Box
|
project(86Box
|
||||||
VERSION 3.7.1
|
VERSION 3.8
|
||||||
DESCRIPTION "Emulator of x86-based systems"
|
DESCRIPTION "Emulator of x86-based systems"
|
||||||
HOMEPAGE_URL "https://86box.net"
|
HOMEPAGE_URL "https://86box.net"
|
||||||
LANGUAGES C CXX)
|
LANGUAGES C CXX)
|
||||||
@@ -165,6 +165,9 @@ cmake_dependent_option(XL24 "ATI VGA Wonder XL24 (ATI-28800-6)"
|
|||||||
# Ditto but for Qt
|
# Ditto but for Qt
|
||||||
if(QT)
|
if(QT)
|
||||||
option(USE_QT6 "Use Qt6 instead of Qt5" OFF)
|
option(USE_QT6 "Use Qt6 instead of Qt5" OFF)
|
||||||
|
if(APPLE)
|
||||||
|
option(MOLTENVK "Use MoltenVK libraries for Vulkan support on macOS. Requires a Vulkan-enabled QT." OFF)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Determine the build type
|
# Determine the build type
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
# Copyright 2020-2022 David Hrdlička.
|
# Copyright 2020-2022 David Hrdlička.
|
||||||
# Copyright 2021 dob205.
|
# Copyright 2021 dob205.
|
||||||
#
|
#
|
||||||
|
if(APPLE)
|
||||||
|
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(86Box 86box.c config.c log.c random.c timer.c io.c acpi.c apm.c
|
add_executable(86Box 86box.c config.c log.c random.c timer.c io.c acpi.c apm.c
|
||||||
dma.c ddma.c discord.c nmi.c pic.c pit.c pit_fast.c port_6x.c port_92.c ppi.c pci.c
|
dma.c ddma.c discord.c nmi.c pic.c pit.c pit_fast.c port_6x.c port_92.c ppi.c pci.c
|
||||||
@@ -83,11 +86,16 @@ if(APPLE)
|
|||||||
# Force using the newest library if it's installed by homebrew
|
# Force using the newest library if it's installed by homebrew
|
||||||
set(CMAKE_FIND_FRAMEWORK LAST)
|
set(CMAKE_FIND_FRAMEWORK LAST)
|
||||||
|
|
||||||
# setting our compilation target to macOS 10.15 Catalina if targetting Qt6, macOS 10.13 High Sierra otherwise
|
# setting our compilation target to macOS 10.15 Catalina if targeting Qt6,
|
||||||
|
# macOS 10.14 Mojave for vulkan support, 10.13 High Sierra otherwise
|
||||||
if (USE_QT6)
|
if (USE_QT6)
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15")
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15")
|
||||||
else()
|
else()
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
|
if(MOLTENVK)
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
|
||||||
|
else()
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
18
src/config.c
18
src/config.c
@@ -1829,6 +1829,15 @@ load_floppy_and_cdrom_drives(void)
|
|||||||
|
|
||||||
sprintf(temp, "cdrom_%02i_iso_path", c + 1);
|
sprintf(temp, "cdrom_%02i_iso_path", c + 1);
|
||||||
config_delete_var(cat, temp);
|
config_delete_var(cat, temp);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_PREV_IMAGES; i++) {
|
||||||
|
cdrom[c].image_history[i] = (char *) calloc(MAX_IMAGE_PATH_LEN + 1, sizeof(char));
|
||||||
|
sprintf(temp, "cdrom_%02i_image_history_%02i", c + 1, i + 1);
|
||||||
|
p = config_get_string(cat, temp, NULL);
|
||||||
|
if(p) {
|
||||||
|
sprintf(cdrom[c].image_history[i], "%s", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3137,6 +3146,15 @@ save_floppy_and_cdrom_drives(void)
|
|||||||
} else {
|
} else {
|
||||||
config_set_string(cat, temp, cdrom[c].image_path);
|
config_set_string(cat, temp, cdrom[c].image_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_PREV_IMAGES; i++) {
|
||||||
|
sprintf(temp, "cdrom_%02i_image_history_%02i", c + 1, i + 1);
|
||||||
|
if((cdrom[c].image_history[i] == 0) || strlen(cdrom[c].image_history[i]) == 0) {
|
||||||
|
config_delete_var(cat, temp);
|
||||||
|
} else {
|
||||||
|
config_set_string(cat, temp, cdrom[c].image_history[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_section_if_empty(cat);
|
delete_section_if_empty(cat);
|
||||||
|
|||||||
@@ -1391,7 +1391,7 @@ cpu_set(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is386) {
|
if (is386) {
|
||||||
#ifdef USE_DYNAREC
|
#if defined(USE_DYNAREC) && !defined(USE_GDBSTUB)
|
||||||
if (cpu_use_dynarec)
|
if (cpu_use_dynarec)
|
||||||
cpu_exec = exec386_dynarec;
|
cpu_exec = exec386_dynarec;
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -114,7 +114,8 @@ typedef struct _gdbstub_client_ {
|
|||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
char packet[16384], response[16384];
|
char packet[16384], response[16384];
|
||||||
int has_packet, waiting_stop, packet_pos, response_pos;
|
int has_packet: 1, first_packet_received: 1, ida_mode: 1, waiting_stop: 1,
|
||||||
|
packet_pos, response_pos;
|
||||||
|
|
||||||
event_t *processed_event, *response_event;
|
event_t *processed_event, *response_event;
|
||||||
|
|
||||||
@@ -133,7 +134,6 @@ typedef struct _gdbstub_breakpoint_ {
|
|||||||
struct _gdbstub_breakpoint_ *next;
|
struct _gdbstub_breakpoint_ *next;
|
||||||
} gdbstub_breakpoint_t;
|
} gdbstub_breakpoint_t;
|
||||||
|
|
||||||
#define ENABLE_GDBSTUB_LOG 1
|
|
||||||
#ifdef ENABLE_GDBSTUB_LOG
|
#ifdef ENABLE_GDBSTUB_LOG
|
||||||
int gdbstub_do_log = ENABLE_GDBSTUB_LOG;
|
int gdbstub_do_log = ENABLE_GDBSTUB_LOG;
|
||||||
|
|
||||||
@@ -152,15 +152,15 @@ gdbstub_log(const char *fmt, ...)
|
|||||||
# define gdbstub_log(fmt, ...)
|
# define gdbstub_log(fmt, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static x86seg *segment_regs[] = { &cpu_state.seg_cs, &cpu_state.seg_ss, &cpu_state.seg_ds, &cpu_state.seg_es, &cpu_state.seg_fs, &cpu_state.seg_gs };
|
static x86seg *segment_regs[] = { &cpu_state.seg_cs, &cpu_state.seg_ss, &cpu_state.seg_ds, &cpu_state.seg_es, &cpu_state.seg_fs, &cpu_state.seg_gs };
|
||||||
static uint32_t *cr_regs[] = { &cpu_state.CR0.l, &cr2, &cr3, &cr4 };
|
static uint32_t *cr_regs[] = { &cpu_state.CR0.l, &cr2, &cr3, &cr4 };
|
||||||
static void *fpu_regs[] = { &cpu_state.npxc, &cpu_state.npxs, NULL, &x87_pc_seg, &x87_pc_off, &x87_op_seg, &x87_op_off };
|
static void *fpu_regs[] = { &cpu_state.npxc, &cpu_state.npxs, NULL, &x87_pc_seg, &x87_pc_off, &x87_op_seg, &x87_op_off };
|
||||||
static const char target_xml[] = /* QEMU gdb-xml/i386-32bit.xml with modifications (described in comments) */
|
static char target_xml[] = /* QEMU gdb-xml/i386-32bit.xml with modifications (described in comments) */
|
||||||
// clang-format off
|
// clang-format off
|
||||||
"<?xml version=\"1.0\"?>"
|
"<?xml version=\"1.0\"?>"
|
||||||
"<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
|
"<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
|
||||||
"<target>"
|
"<target>"
|
||||||
"<architecture>i8086</architecture>" /* start in 16-bit mode to work around known GDB bug preventing 32->16 switching */
|
"<!-- architecture tag goes here -->" /* <architecture> patched in here (length must be kept) */
|
||||||
""
|
""
|
||||||
"<feature name=\"org.gnu.gdb.i386.core\">"
|
"<feature name=\"org.gnu.gdb.i386.core\">"
|
||||||
"<flags id=\"i386_eflags\" size=\"4\">"
|
"<flags id=\"i386_eflags\" size=\"4\">"
|
||||||
@@ -753,6 +753,15 @@ gdbstub_client_packet(gdbstub_client_t *client)
|
|||||||
client->response_pos = 0;
|
client->response_pos = 0;
|
||||||
client->packet_pos = 1;
|
client->packet_pos = 1;
|
||||||
|
|
||||||
|
/* Handle IDA-specific hacks. */
|
||||||
|
if (!client->first_packet_received) {
|
||||||
|
client->first_packet_received = 1;
|
||||||
|
if (!strcmp(client->packet, "qSupported:xmlRegisters=i386,arm,mips")) {
|
||||||
|
gdbstub_log("GDB Stub: Enabling IDA mode\n");
|
||||||
|
client->ida_mode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Parse command. */
|
/* Parse command. */
|
||||||
switch (client->packet[0]) {
|
switch (client->packet[0]) {
|
||||||
case '?': /* stop reason */
|
case '?': /* stop reason */
|
||||||
@@ -982,10 +991,21 @@ e14:
|
|||||||
if (!strcmp(client->response, "read")) {
|
if (!strcmp(client->response, "read")) {
|
||||||
/* Read the transfer annex. */
|
/* Read the transfer annex. */
|
||||||
client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1;
|
client->packet_pos += gdbstub_client_read_string(client, client->response, sizeof(client->response) - 1, ':') + 1;
|
||||||
if (!strcmp(client->response, "target.xml"))
|
if (!strcmp(client->response, "target.xml")) {
|
||||||
p = (char *) target_xml;
|
/* Patch architecture for IDA. */
|
||||||
else
|
p = strstr(target_xml, "<!-- architecture tag goes here -->");
|
||||||
|
if (p) {
|
||||||
|
if (client->ida_mode)
|
||||||
|
memcpy(p, "<architecture>i386</architecture> ", 35); /* make IDA not complain about i8086 being unknown */
|
||||||
|
else
|
||||||
|
memcpy(p, "<architecture>i8086</architecture> ", 35); /* start in 16-bit mode to work around known GDB bug preventing 32->16 switching */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send target XML. */
|
||||||
|
p = target_xml;
|
||||||
|
} else {
|
||||||
p = NULL;
|
p = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Stop if the file wasn't found. */
|
/* Stop if the file wasn't found. */
|
||||||
if (!p) {
|
if (!p) {
|
||||||
|
|||||||
@@ -31,6 +31,10 @@
|
|||||||
#define NVR_PATH "nvr"
|
#define NVR_PATH "nvr"
|
||||||
#define SCREENSHOT_PATH "screenshots"
|
#define SCREENSHOT_PATH "screenshots"
|
||||||
|
|
||||||
|
/* Recently used images */
|
||||||
|
#define MAX_PREV_IMAGES 4
|
||||||
|
#define MAX_IMAGE_PATH_LEN 256
|
||||||
|
|
||||||
/* Default language 0xFFFF = from system, 0x409 = en-US */
|
/* Default language 0xFFFF = from system, 0x409 = en-US */
|
||||||
#define DEFAULT_LANGUAGE 0x0409
|
#define DEFAULT_LANGUAGE 0x0409
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
#define CD_TOC_SESSION 1
|
#define CD_TOC_SESSION 1
|
||||||
#define CD_TOC_RAW 2
|
#define CD_TOC_RAW 2
|
||||||
|
|
||||||
|
#define CD_IMAGE_HISTORY 4
|
||||||
|
|
||||||
#define BUF_SIZE 32768
|
#define BUF_SIZE 32768
|
||||||
|
|
||||||
#define CDROM_IMAGE 200
|
#define CDROM_IMAGE 200
|
||||||
@@ -110,6 +112,8 @@ typedef struct cdrom {
|
|||||||
char image_path[1024],
|
char image_path[1024],
|
||||||
prev_image_path[1024];
|
prev_image_path[1024];
|
||||||
|
|
||||||
|
char *image_history[CD_IMAGE_HISTORY];
|
||||||
|
|
||||||
uint32_t sound_on, cdrom_capacity,
|
uint32_t sound_on, cdrom_capacity,
|
||||||
pad, seek_pos,
|
pad, seek_pos,
|
||||||
seek_diff, cd_end;
|
seek_diff, cd_end;
|
||||||
|
|||||||
@@ -20,12 +20,12 @@
|
|||||||
#define EMU_NAME "86Box"
|
#define EMU_NAME "86Box"
|
||||||
#define EMU_NAME_W LSTR(EMU_NAME)
|
#define EMU_NAME_W LSTR(EMU_NAME)
|
||||||
|
|
||||||
#define EMU_VERSION "3.7.1"
|
#define EMU_VERSION "3.8"
|
||||||
#define EMU_VERSION_W LSTR(EMU_VERSION)
|
#define EMU_VERSION_W LSTR(EMU_VERSION)
|
||||||
#define EMU_VERSION_EX "3.50" /* frozen due to IDE re-detection behavior on Windows */
|
#define EMU_VERSION_EX "3.50" /* frozen due to IDE re-detection behavior on Windows */
|
||||||
#define EMU_VERSION_MAJ 3
|
#define EMU_VERSION_MAJ 3
|
||||||
#define EMU_VERSION_MIN 7
|
#define EMU_VERSION_MIN 8
|
||||||
#define EMU_VERSION_PATCH 1
|
#define EMU_VERSION_PATCH 0
|
||||||
|
|
||||||
#define EMU_BUILD_NUM 0
|
#define EMU_BUILD_NUM 0
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
#define EMU_ROMS_URL "https://github.com/86Box/roms/releases/latest"
|
#define EMU_ROMS_URL "https://github.com/86Box/roms/releases/latest"
|
||||||
#define EMU_ROMS_URL_W LSTR(EMU_ROMS_URL)
|
#define EMU_ROMS_URL_W LSTR(EMU_ROMS_URL)
|
||||||
#ifdef RELEASE_BUILD
|
#ifdef RELEASE_BUILD
|
||||||
# define EMU_DOCS_URL "https://86box.readthedocs.io/en/v3.7/"
|
# define EMU_DOCS_URL "https://86box.readthedocs.io/en/v3.8/"
|
||||||
#else
|
#else
|
||||||
# define EMU_DOCS_URL "https://86box.readthedocs.io"
|
# define EMU_DOCS_URL "https://86box.readthedocs.io"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -332,8 +332,8 @@ machine_at_5ivg_init(const machine_t *model)
|
|||||||
|
|
||||||
pci_init(PCI_CONFIG_TYPE_1);
|
pci_init(PCI_CONFIG_TYPE_1);
|
||||||
pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0);
|
pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0);
|
||||||
pci_register_slot(0x11, PCI_CARD_NORMAL, 2, 3, 4, 1);
|
pci_register_slot(0x11, PCI_CARD_NORMAL, 1, 2, 3, 4);
|
||||||
pci_register_slot(0x12, PCI_CARD_NORMAL, 1, 2, 3, 4);
|
pci_register_slot(0x12, PCI_CARD_NORMAL, 2, 3, 4, 1);
|
||||||
pci_register_slot(0x13, PCI_CARD_NORMAL, 3, 4, 1, 2);
|
pci_register_slot(0x13, PCI_CARD_NORMAL, 3, 4, 1, 2);
|
||||||
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0);
|
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0);
|
||||||
device_add(&i430vx_device);
|
device_add(&i430vx_device);
|
||||||
|
|||||||
@@ -166,6 +166,9 @@ add_library(ui STATIC
|
|||||||
qt_mcadevicelist.cpp
|
qt_mcadevicelist.cpp
|
||||||
qt_mcadevicelist.ui
|
qt_mcadevicelist.ui
|
||||||
|
|
||||||
|
qt_mediahistorymanager.cpp
|
||||||
|
qt_mediahistorymanager.hpp
|
||||||
|
|
||||||
../qt_resources.qrc
|
../qt_resources.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -206,6 +209,18 @@ endif()
|
|||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
target_sources(ui PRIVATE macos_event_filter.mm)
|
target_sources(ui PRIVATE macos_event_filter.mm)
|
||||||
|
if(MOLTENVK)
|
||||||
|
find_path(MOLTENVK_INCLUDE "vulkan/vulkan.h" PATHS "/opt/homebrew/opt/molten-vk/libexec/include" "/usr/local/opt/molten-vk/libexec/include" ${MOLTENVK_INCLUDE_DIR})
|
||||||
|
if (NOT MOLTENVK_INCLUDE)
|
||||||
|
message(FATAL_ERROR "Could not find vulkan/vulkan.h. If the headers are installed please use -DMOLTENVK_INCLUDE_DIR=/path/to/headers")
|
||||||
|
endif()
|
||||||
|
target_include_directories(ui PRIVATE ${MOLTENVK_INCLUDE})
|
||||||
|
find_library(MOLTENVK_LIB MoltenVK)
|
||||||
|
if (NOT MOLTENVK_LIB)
|
||||||
|
message(FATAL_ERROR "Could not find MoltenVK library")
|
||||||
|
endif()
|
||||||
|
target_link_libraries(ui PRIVATE "${MOLTENVK_LIB}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@@ -282,6 +297,7 @@ if (APPLE AND CMAKE_MACOSX_BUNDLE)
|
|||||||
set(prefix "86Box.app/Contents")
|
set(prefix "86Box.app/Contents")
|
||||||
set(INSTALL_RUNTIME_DIR "${prefix}/MacOS")
|
set(INSTALL_RUNTIME_DIR "${prefix}/MacOS")
|
||||||
set(INSTALL_CMAKE_DIR "${prefix}/Resources")
|
set(INSTALL_CMAKE_DIR "${prefix}/Resources")
|
||||||
|
set(INSTALL_LIB_DIR "${prefix}/Frameworks")
|
||||||
|
|
||||||
# using the install_qt5_plugin to add Qt plugins into the macOS app bundle
|
# using the install_qt5_plugin to add Qt plugins into the macOS app bundle
|
||||||
install_qt5_plugin("Qt${QT_MAJOR}::QCocoaIntegrationPlugin" QT_PLUGINS ${prefix})
|
install_qt5_plugin("Qt${QT_MAJOR}::QCocoaIntegrationPlugin" QT_PLUGINS ${prefix})
|
||||||
@@ -314,6 +330,16 @@ if (APPLE AND CMAKE_MACOSX_BUNDLE)
|
|||||||
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\"
|
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\"
|
||||||
\"\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/${INSTALL_RUNTIME_DIR}/86Box\")
|
\"\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/${INSTALL_RUNTIME_DIR}/86Box\")
|
||||||
")
|
")
|
||||||
|
if(MOLTENVK)
|
||||||
|
install(CODE "
|
||||||
|
execute_process(
|
||||||
|
COMMAND bash -c \"set -e
|
||||||
|
echo \\\"-- Creating vulkan dylib symlink for QT (libVulkan.dylib -> libMoltenVK.dylib)\\\"
|
||||||
|
cd \${CMAKE_INSTALL_PREFIX_ABSOLUTE}/${INSTALL_LIB_DIR}
|
||||||
|
ln -sf libMoltenVK.dylib libVulkan.dylib
|
||||||
|
\")
|
||||||
|
")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE AND NOT HAIKU)
|
if (UNIX AND NOT APPLE AND NOT HAIKU)
|
||||||
|
|||||||
326
src/qt/qt_mediahistorymanager.cpp
Normal file
326
src/qt/qt_mediahistorymanager.cpp
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
/*
|
||||||
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||||
|
* running old operating systems and software designed for IBM
|
||||||
|
* PC systems and compatibles from 1981 through fairly recent
|
||||||
|
* system designs based on the PCI bus.
|
||||||
|
*
|
||||||
|
* This file is part of the 86Box distribution.
|
||||||
|
*
|
||||||
|
* Media history management module
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Authors: cold-brewed
|
||||||
|
*
|
||||||
|
* Copyright 2022 The 86Box development team
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QMetaEnum>
|
||||||
|
#include <QStringBuilder>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "86box/cdrom.h"
|
||||||
|
#include "qt_mediahistorymanager.hpp"
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
MediaHistoryManager::MediaHistoryManager() {
|
||||||
|
initializeImageHistory();
|
||||||
|
deserializeAllImageHistory();
|
||||||
|
initialDeduplication();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaHistoryManager::~MediaHistoryManager()
|
||||||
|
= default;
|
||||||
|
|
||||||
|
master_list_t &
|
||||||
|
MediaHistoryManager::blankImageHistory(master_list_t &initialized_master_list) const
|
||||||
|
{
|
||||||
|
for ( const auto device_type : ui::AllSupportedMediaHistoryTypes ) {
|
||||||
|
device_media_history_t device_media_history;
|
||||||
|
// Loop for all possible media devices
|
||||||
|
for (int device_index = 0 ; device_index < maxDevicesSupported(device_type); device_index++) {
|
||||||
|
device_index_list_t indexing_list;
|
||||||
|
device_media_history[device_index] = indexing_list;
|
||||||
|
// Loop for each history slot
|
||||||
|
for (int slot_index = 0; slot_index < max_images; slot_index++) {
|
||||||
|
device_media_history[device_index].append(QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initialized_master_list.insert(device_type, device_media_history);
|
||||||
|
}
|
||||||
|
return initialized_master_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const device_index_list_t&
|
||||||
|
MediaHistoryManager::getHistoryListForDeviceIndex(int index, ui::MediaType type)
|
||||||
|
{
|
||||||
|
if (master_list.contains(type)) {
|
||||||
|
if ((index >= 0 ) && (index < master_list[type].size())) {
|
||||||
|
return master_list[type][index];
|
||||||
|
} else {
|
||||||
|
qWarning("Media device index %i for device type %s was requested but index %i is out of range (valid range: >= 0 && < %i)",
|
||||||
|
index, mediaTypeToString(type).toUtf8().constData(), index, master_list[type].size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Failure gets an empty list
|
||||||
|
return empty_device_index_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaHistoryManager::setHistoryListForDeviceIndex(int index, ui::MediaType type, device_index_list_t history_list)
|
||||||
|
{
|
||||||
|
master_list[type][index] = std::move(history_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
MediaHistoryManager::getImageForSlot(int index, int slot, ui::MediaType type)
|
||||||
|
{
|
||||||
|
QString image_name;
|
||||||
|
device_index_list_t device_history = getHistoryListForDeviceIndex(index, type);
|
||||||
|
if ((slot >= 0) && (slot < device_history.size())) {
|
||||||
|
image_name = device_history[slot];
|
||||||
|
} else {
|
||||||
|
qWarning("Media history slot %i, index %i for device type %s was requested but slot %i is out of range (valid range: >= 0 && < %i, device_history.size() is %i)",
|
||||||
|
slot, index, mediaTypeToString(type).toUtf8().constData(), slot, maxDevicesSupported(type), device_history.size());
|
||||||
|
}
|
||||||
|
return image_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are hardcoded since we can't include the various
|
||||||
|
// header files where they are defined (e.g., fdd.h, mo.h).
|
||||||
|
// However, all in ui::MediaType support 4 except cassette.
|
||||||
|
int MediaHistoryManager::maxDevicesSupported(ui::MediaType type)
|
||||||
|
{
|
||||||
|
return type == ui::MediaType::Cassette ? 1 : 4;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaHistoryManager::deserializeImageHistoryType(ui::MediaType type)
|
||||||
|
{
|
||||||
|
for (int device = 0; device < maxDevicesSupported(type); device++) {
|
||||||
|
char **device_history_ptr = getEmuHistoryVarForType(type, device);
|
||||||
|
if(device_history_ptr == nullptr) {
|
||||||
|
// Device not supported, return and do not deserialize.
|
||||||
|
// This will leave the image listing at the default initialization state
|
||||||
|
// from the ui side (this class)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for ( int slot = 0; slot < MAX_PREV_IMAGES; slot++) {
|
||||||
|
master_list[type][device][slot] = device_history_ptr[slot];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void MediaHistoryManager::deserializeAllImageHistory()
|
||||||
|
{
|
||||||
|
for ( const auto device_type : ui::AllSupportedMediaHistoryTypes ) {
|
||||||
|
deserializeImageHistoryType(device_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void MediaHistoryManager::serializeImageHistoryType(ui::MediaType type)
|
||||||
|
{
|
||||||
|
for (int device = 0; device < maxDevicesSupported(type); device++) {
|
||||||
|
char **device_history_ptr = getEmuHistoryVarForType(type, device);
|
||||||
|
if(device_history_ptr == nullptr) {
|
||||||
|
// Device not supported, return and do not serialize.
|
||||||
|
// This will leave the image listing at the current state,
|
||||||
|
// and it will not be saved on the emu side
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for ( int slot = 0; slot < MAX_PREV_IMAGES; slot++) {
|
||||||
|
strncpy(device_history_ptr[slot], master_list[type][device][slot].toUtf8().constData(), MAX_IMAGE_PATH_LEN);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaHistoryManager::serializeAllImageHistory()
|
||||||
|
{
|
||||||
|
for ( const auto device_type : ui::AllSupportedMediaHistoryTypes ) {
|
||||||
|
serializeImageHistoryType(device_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaHistoryManager::initialDeduplication()
|
||||||
|
{
|
||||||
|
|
||||||
|
QString current_image;
|
||||||
|
// Perform initial dedup if an image is loaded
|
||||||
|
for ( const auto device_type : ui::AllSupportedMediaHistoryTypes ) {
|
||||||
|
for (int device_index = 0; device_index < maxDevicesSupported(device_type); device_index++) {
|
||||||
|
device_index_list_t device_history = getHistoryListForDeviceIndex(device_index, device_type);
|
||||||
|
switch (device_type) {
|
||||||
|
case ui::MediaType::Optical:
|
||||||
|
current_image = cdrom[device_index].image_path;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
deduplicateList(device_history, QVector<QString> (1, current_image));
|
||||||
|
// Fill in missing, if any
|
||||||
|
int missing = MAX_PREV_IMAGES - device_history.size();
|
||||||
|
if(missing) {
|
||||||
|
for (int i = 0; i < missing; i++) {
|
||||||
|
device_history.push_back(QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setHistoryListForDeviceIndex(device_index, device_type, device_history);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char ** MediaHistoryManager::getEmuHistoryVarForType(ui::MediaType type, int index)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case ui::MediaType::Optical:
|
||||||
|
return &cdrom[index].image_history[0];
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device_index_list_t &
|
||||||
|
MediaHistoryManager::deduplicateList(device_index_list_t &device_history, const QVector<QString>& filenames)
|
||||||
|
{
|
||||||
|
QVector<QString> items_to_delete;
|
||||||
|
for (auto &list_item_path : device_history) {
|
||||||
|
if(list_item_path.isEmpty()) {
|
||||||
|
continue ;
|
||||||
|
}
|
||||||
|
for (const auto& path_to_check : filenames) {
|
||||||
|
if(path_to_check.isEmpty()) {
|
||||||
|
continue ;
|
||||||
|
}
|
||||||
|
QString adjusted_path = pathAdjustSingle(path_to_check);
|
||||||
|
int match = QString::localeAwareCompare(list_item_path, adjusted_path);
|
||||||
|
if (match == 0) {
|
||||||
|
items_to_delete.append(list_item_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove by name rather than index because the index would change
|
||||||
|
// after each removal
|
||||||
|
for (const auto& path: items_to_delete) {
|
||||||
|
device_history.removeAll(path);
|
||||||
|
}
|
||||||
|
return device_history;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaHistoryManager::addImageToHistory(int index, ui::MediaType type, const QString& image_name, const QString& new_image_name)
|
||||||
|
{
|
||||||
|
device_index_list_t device_history = getHistoryListForDeviceIndex(index, type);
|
||||||
|
QVector<QString> files_to_check;
|
||||||
|
|
||||||
|
files_to_check.append(image_name);
|
||||||
|
files_to_check.append(new_image_name);
|
||||||
|
device_history = deduplicateList(device_history, files_to_check);
|
||||||
|
|
||||||
|
|
||||||
|
if (!image_name.isEmpty()) {
|
||||||
|
device_history.push_front(image_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop any extras
|
||||||
|
if ((device_history.size() > MAX_PREV_IMAGES)) {
|
||||||
|
device_history.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in missing, if any
|
||||||
|
int missing = MAX_PREV_IMAGES - device_history.size();
|
||||||
|
if(missing) {
|
||||||
|
for (int i = 0; i < missing; i++) {
|
||||||
|
device_history.push_back(QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device_history = removeMissingImages(device_history);
|
||||||
|
device_history = pathAdjustFull(device_history);
|
||||||
|
|
||||||
|
setHistoryListForDeviceIndex(index, type, device_history);
|
||||||
|
serializeImageHistoryType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MediaHistoryManager::mediaTypeToString(ui::MediaType type)
|
||||||
|
{
|
||||||
|
QMetaEnum qme = QMetaEnum::fromType<ui::MediaType>();
|
||||||
|
return qme.valueToKey(static_cast<int>(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
MediaHistoryManager::pathAdjustSingle(QString checked_path)
|
||||||
|
{
|
||||||
|
QString current_usr_path = getUsrPath();
|
||||||
|
QFileInfo file_info(checked_path);
|
||||||
|
if (file_info.filePath().isEmpty() || current_usr_path.isEmpty() || file_info.isRelative()) {
|
||||||
|
return checked_path;
|
||||||
|
}
|
||||||
|
if (file_info.filePath().startsWith(current_usr_path)) {
|
||||||
|
checked_path = file_info.filePath().remove(current_usr_path);
|
||||||
|
}
|
||||||
|
return checked_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_index_list_t &
|
||||||
|
MediaHistoryManager::pathAdjustFull(device_index_list_t &device_history)
|
||||||
|
{
|
||||||
|
for (auto &checked_path : device_history) {
|
||||||
|
checked_path = pathAdjustSingle(checked_path);
|
||||||
|
}
|
||||||
|
return device_history;
|
||||||
|
}
|
||||||
|
QString MediaHistoryManager::getUsrPath()
|
||||||
|
{
|
||||||
|
QString current_usr_path(usr_path);
|
||||||
|
// Ensure `usr_path` has a trailing slash
|
||||||
|
return current_usr_path.endsWith("/") ? current_usr_path : current_usr_path.append("/");
|
||||||
|
}
|
||||||
|
device_index_list_t &
|
||||||
|
MediaHistoryManager::removeMissingImages(device_index_list_t &device_history)
|
||||||
|
{
|
||||||
|
for (auto &checked_path : device_history) {
|
||||||
|
QFileInfo file_info(checked_path);
|
||||||
|
if (file_info.filePath().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// For this check, explicitly prepend `usr_path` to relative paths to account for $CWD platform variances
|
||||||
|
QFileInfo absolute_path = file_info.isRelative() ? getUsrPath().append(file_info.filePath()) : file_info;
|
||||||
|
if(!absolute_path.exists()) {
|
||||||
|
qWarning("Image file %s does not exist - removing from history", qPrintable(file_info.filePath()));
|
||||||
|
checked_path = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return device_history;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaHistoryManager::initializeImageHistory()
|
||||||
|
{
|
||||||
|
auto initial_master_list = getMasterList();
|
||||||
|
setMasterList(blankImageHistory(initial_master_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
const master_list_t &
|
||||||
|
MediaHistoryManager::getMasterList() const
|
||||||
|
{
|
||||||
|
return master_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MediaHistoryManager::setMasterList(const master_list_t &masterList)
|
||||||
|
{
|
||||||
|
master_list = masterList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MediaHistoryManager::clearImageHistory()
|
||||||
|
{
|
||||||
|
initializeImageHistory();
|
||||||
|
serializeAllImageHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // ui
|
||||||
139
src/qt/qt_mediahistorymanager.hpp
Normal file
139
src/qt/qt_mediahistorymanager.hpp
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||||
|
* running old operating systems and software designed for IBM
|
||||||
|
* PC systems and compatibles from 1981 through fairly recent
|
||||||
|
* system designs based on the PCI bus.
|
||||||
|
*
|
||||||
|
* This file is part of the 86Box distribution.
|
||||||
|
*
|
||||||
|
* Header for the media history management module
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Authors: cold-brewed
|
||||||
|
*
|
||||||
|
* Copyright 2022 The 86Box development team
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QT_MEDIAHISTORYMANAGER_HPP
|
||||||
|
#define QT_MEDIAHISTORYMANAGER_HPP
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <86box/86box.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
// This macro helps give us the required `qHash()` function in order to use the
|
||||||
|
// enum as a hash key
|
||||||
|
#define QHASH_FOR_CLASS_ENUM(T) \
|
||||||
|
inline uint qHash(const T &t, uint seed) { \
|
||||||
|
return ::qHash(static_cast<typename std::underlying_type<T>::type>(t), seed); \
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef QVector<QString> device_index_list_t;
|
||||||
|
typedef QHash<int, QVector<QString>> device_media_history_t;
|
||||||
|
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
Q_NAMESPACE
|
||||||
|
|
||||||
|
enum class MediaType {
|
||||||
|
Floppy,
|
||||||
|
Optical,
|
||||||
|
Zip,
|
||||||
|
Mo,
|
||||||
|
Cassette
|
||||||
|
};
|
||||||
|
// This macro allows us to do a reverse lookup of the enum with `QMetaEnum`
|
||||||
|
Q_ENUM_NS(MediaType)
|
||||||
|
|
||||||
|
QHASH_FOR_CLASS_ENUM(MediaType)
|
||||||
|
|
||||||
|
typedef QHash<ui::MediaType, device_media_history_t> master_list_t;
|
||||||
|
|
||||||
|
// Used to iterate over all supported types when preparing data structures
|
||||||
|
// Also useful to indicate which types support history
|
||||||
|
static const MediaType AllSupportedMediaHistoryTypes[] = {
|
||||||
|
MediaType::Optical
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaHistoryManager {
|
||||||
|
|
||||||
|
public:
|
||||||
|
MediaHistoryManager();
|
||||||
|
virtual ~MediaHistoryManager();
|
||||||
|
|
||||||
|
// Get the image name for a particular slot,
|
||||||
|
// index, and type combination
|
||||||
|
QString getImageForSlot(int index, int slot, ui::MediaType type);
|
||||||
|
|
||||||
|
// Add an image to history
|
||||||
|
void addImageToHistory(int index, ui::MediaType type, const QString& image_name, const QString& new_image_name);
|
||||||
|
|
||||||
|
// Convert the enum value to a string
|
||||||
|
static QString mediaTypeToString(ui::MediaType type);
|
||||||
|
|
||||||
|
// Clear out the image history
|
||||||
|
void clearImageHistory();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
int max_images = MAX_PREV_IMAGES;
|
||||||
|
|
||||||
|
// Main hash of hash of vector of strings
|
||||||
|
master_list_t master_list;
|
||||||
|
const master_list_t &getMasterList() const;
|
||||||
|
void setMasterList(const master_list_t &masterList);
|
||||||
|
|
||||||
|
device_index_list_t index_list, empty_device_index_list;
|
||||||
|
|
||||||
|
// Return a blank, initialized image history list
|
||||||
|
master_list_t &blankImageHistory(master_list_t &initialized_master_list) const;
|
||||||
|
|
||||||
|
// Initialize the image history
|
||||||
|
void initializeImageHistory();
|
||||||
|
|
||||||
|
// Max number of devices supported by media type
|
||||||
|
static int maxDevicesSupported(ui::MediaType type);
|
||||||
|
|
||||||
|
// Serialize the data back into the C array
|
||||||
|
// on the emu side
|
||||||
|
void serializeImageHistoryType(ui::MediaType type);
|
||||||
|
void serializeAllImageHistory();
|
||||||
|
|
||||||
|
// Deserialize the data from C array on the emu side
|
||||||
|
// for the ui side
|
||||||
|
void deserializeImageHistoryType(ui::MediaType type);
|
||||||
|
void deserializeAllImageHistory();
|
||||||
|
|
||||||
|
// Get emu history variable for a device type
|
||||||
|
static char** getEmuHistoryVarForType(ui::MediaType type, int index);
|
||||||
|
|
||||||
|
// Get or set the history for a specific device/index combo
|
||||||
|
const device_index_list_t &getHistoryListForDeviceIndex(int index, ui::MediaType type);
|
||||||
|
void setHistoryListForDeviceIndex(int index, ui::MediaType type, device_index_list_t history_list);
|
||||||
|
|
||||||
|
// Remove missing image files from history list
|
||||||
|
static device_index_list_t &removeMissingImages(device_index_list_t &device_history);
|
||||||
|
|
||||||
|
// If an absolute path is contained within `usr_path`, convert to a relative path
|
||||||
|
static device_index_list_t &pathAdjustFull(device_index_list_t &device_history);
|
||||||
|
static QString pathAdjustSingle(QString checked_path);
|
||||||
|
|
||||||
|
// Deduplicate history entries
|
||||||
|
static device_index_list_t &deduplicateList(device_index_list_t &device_history, const QVector<QString>& filenames);
|
||||||
|
void initialDeduplication();
|
||||||
|
|
||||||
|
// Gets the `usr_path` from the emu side and appends a
|
||||||
|
// trailing slash if necessary
|
||||||
|
static QString getUsrPath();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // ui
|
||||||
|
|
||||||
|
#endif // QT_MEDIAHISTORYMANAGER_HPP
|
||||||
@@ -52,6 +52,7 @@ extern "C" {
|
|||||||
#include "qt_newfloppydialog.hpp"
|
#include "qt_newfloppydialog.hpp"
|
||||||
#include "qt_util.hpp"
|
#include "qt_util.hpp"
|
||||||
#include "qt_deviceconfig.hpp"
|
#include "qt_deviceconfig.hpp"
|
||||||
|
#include "qt_mediahistorymanager.hpp"
|
||||||
|
|
||||||
std::shared_ptr<MediaMenu> MediaMenu::ptr;
|
std::shared_ptr<MediaMenu> MediaMenu::ptr;
|
||||||
|
|
||||||
@@ -120,8 +121,11 @@ void MediaMenu::refresh(QMenu *parentMenu) {
|
|||||||
menu->addAction(tr("&Mute"), [this, i]() { cdromMute(i); })->setCheckable(true);
|
menu->addAction(tr("&Mute"), [this, i]() { cdromMute(i); })->setCheckable(true);
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
menu->addAction(tr("&Image..."), [this, i]() { cdromMount(i); })->setCheckable(false);
|
menu->addAction(tr("&Image..."), [this, i]() { cdromMount(i); })->setCheckable(false);
|
||||||
cdromReloadPos = menu->children().count();
|
menu->addSeparator();
|
||||||
menu->addAction(tr("&Reload previous image"), [this, i]() { cdromReload(i); });
|
for (int slot = 0; slot < MAX_PREV_IMAGES; slot++) {
|
||||||
|
cdromImageHistoryPos[slot] = menu->children().count();
|
||||||
|
menu->addAction(QString::asprintf(tr("Image %i").toUtf8().constData(), slot), [this, i, slot]() { cdromReload(i, slot); })->setCheckable(false);
|
||||||
|
}
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
cdromImagePos = menu->children().count();
|
cdromImagePos = menu->children().count();
|
||||||
menu->addAction(tr("E&ject"), [this, i]() { cdromEject(i); })->setCheckable(false);
|
menu->addAction(tr("E&ject"), [this, i]() { cdromEject(i); })->setCheckable(false);
|
||||||
@@ -170,6 +174,7 @@ void MediaMenu::refresh(QMenu *parentMenu) {
|
|||||||
netMenus[i] = menu;
|
netMenus[i] = menu;
|
||||||
nicUpdateMenu(i);
|
nicUpdateMenu(i);
|
||||||
});
|
});
|
||||||
|
parentMenu->addAction(tr("Clear image history"), [this]() { clearImageHistory(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaMenu::cassetteNewImage() {
|
void MediaMenu::cassetteNewImage() {
|
||||||
@@ -404,14 +409,13 @@ void MediaMenu::cdromMount(int i, const QString &filename)
|
|||||||
} else {
|
} else {
|
||||||
ui_sb_update_icon_state(SB_CDROM | i, 1);
|
ui_sb_update_icon_state(SB_CDROM | i, 1);
|
||||||
}
|
}
|
||||||
|
mhm.addImageToHistory(i, ui::MediaType::Optical, cdrom[i].prev_image_path, cdrom[i].image_path);
|
||||||
cdromUpdateMenu(i);
|
cdromUpdateMenu(i);
|
||||||
ui_sb_update_tip(SB_CDROM | i);
|
ui_sb_update_tip(SB_CDROM | i);
|
||||||
config_save();
|
config_save();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaMenu::cdromMount(int i) {
|
void MediaMenu::cdromMount(int i) {
|
||||||
QString dir;
|
|
||||||
QFileInfo fi(cdrom[i].image_path);
|
|
||||||
|
|
||||||
auto filename = QFileDialog::getOpenFileName(
|
auto filename = QFileDialog::getOpenFileName(
|
||||||
parentWidget,
|
parentWidget,
|
||||||
@@ -430,22 +434,60 @@ void MediaMenu::cdromMount(int i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MediaMenu::cdromEject(int i) {
|
void MediaMenu::cdromEject(int i) {
|
||||||
|
mhm.addImageToHistory(i, ui::MediaType::Optical, cdrom[i].image_path, QString());
|
||||||
cdrom_eject(i);
|
cdrom_eject(i);
|
||||||
cdromUpdateMenu(i);
|
cdromUpdateMenu(i);
|
||||||
ui_sb_update_tip(SB_CDROM | i);
|
ui_sb_update_tip(SB_CDROM | i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaMenu::cdromReload(int i) {
|
void MediaMenu::cdromReload(int index, int slot) {
|
||||||
cdrom_reload(i);
|
QString filename = mhm.getImageForSlot(index, slot, ui::MediaType::Optical);
|
||||||
cdromUpdateMenu(i);
|
cdromMount(index, filename.toUtf8().constData());
|
||||||
ui_sb_update_tip(SB_CDROM | i);
|
cdromUpdateMenu(index);
|
||||||
|
ui_sb_update_tip(SB_CDROM | index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaMenu::updateImageHistory(int index, int slot, ui::MediaType type) {
|
||||||
|
QMenu* menu;
|
||||||
|
QAction* imageHistoryUpdatePos;
|
||||||
|
QString image_path;
|
||||||
|
QObjectList children;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ui::MediaType::Optical:
|
||||||
|
if (!cdromMenus.contains(index))
|
||||||
|
return;
|
||||||
|
menu = cdromMenus[index];
|
||||||
|
children = menu->children();
|
||||||
|
imageHistoryUpdatePos = dynamic_cast<QAction*>(children[cdromImageHistoryPos[slot]]);
|
||||||
|
image_path = mhm.getImageForSlot(index, slot, type);
|
||||||
|
break;
|
||||||
|
case ui::MediaType::Floppy:
|
||||||
|
if (!floppyMenus.contains(index))
|
||||||
|
return;
|
||||||
|
menu = floppyMenus[index];
|
||||||
|
children = menu->children();
|
||||||
|
imageHistoryUpdatePos = dynamic_cast<QAction*>(children[floppyImageHistoryPos[slot]]);
|
||||||
|
image_path = mhm.getImageForSlot(index, slot, type);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pclog("History not yet implemented for media type %s\n", qPrintable(mhm.mediaTypeToString(type)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo fi(image_path);
|
||||||
|
imageHistoryUpdatePos->setText(QString::asprintf(tr("%s").toUtf8().constData(), fi.fileName().isEmpty() ? tr("previous image").toUtf8().constData() : fi.fileName().toUtf8().constData()));
|
||||||
|
imageHistoryUpdatePos->setVisible(!fi.fileName().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaMenu::clearImageHistory() {
|
||||||
|
mhm.clearImageHistory();
|
||||||
|
ui_sb_update_panes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaMenu::cdromUpdateMenu(int i) {
|
void MediaMenu::cdromUpdateMenu(int i) {
|
||||||
QString name = cdrom[i].image_path;
|
QString name = cdrom[i].image_path;
|
||||||
QString prev_name = cdrom[i].prev_image_path;
|
|
||||||
QFileInfo fi(cdrom[i].image_path);
|
QFileInfo fi(cdrom[i].image_path);
|
||||||
QFileInfo fi_prev(cdrom[i].prev_image_path);
|
|
||||||
|
|
||||||
if (!cdromMenus.contains(i))
|
if (!cdromMenus.contains(i))
|
||||||
return;
|
return;
|
||||||
@@ -459,9 +501,8 @@ void MediaMenu::cdromUpdateMenu(int i) {
|
|||||||
imageMenu->setEnabled(!name.isEmpty());
|
imageMenu->setEnabled(!name.isEmpty());
|
||||||
imageMenu->setText(QString::asprintf(tr("Eject %s").toUtf8().constData(), name.isEmpty() ? QString().toUtf8().constData() : fi.fileName().toUtf8().constData()));
|
imageMenu->setText(QString::asprintf(tr("Eject %s").toUtf8().constData(), name.isEmpty() ? QString().toUtf8().constData() : fi.fileName().toUtf8().constData()));
|
||||||
|
|
||||||
auto* prevMenu = dynamic_cast<QAction*>(childs[cdromReloadPos]);
|
for (int slot = 0; slot < MAX_PREV_IMAGES; slot++)
|
||||||
prevMenu->setText(QString::asprintf(tr("Reload %s").toUtf8().constData(), prev_name.isEmpty() ? tr("previous image").toUtf8().constData() : fi_prev.fileName().toUtf8().constData()));
|
updateImageHistory(i, slot, ui::MediaType::Optical);
|
||||||
prevMenu->setVisible(name.isEmpty() && cdrom[i].prev_host_drive != 0);
|
|
||||||
|
|
||||||
QString busName = tr("Unknown Bus");
|
QString busName = tr("Unknown Bus");
|
||||||
switch (cdrom[i].bus_type) {
|
switch (cdrom[i].bus_type) {
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include "qt_mediahistorymanager.hpp"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <86box/86box.h>
|
||||||
|
}
|
||||||
class QMenu;
|
class QMenu;
|
||||||
|
|
||||||
class MediaMenu : QObject
|
class MediaMenu : QObject
|
||||||
@@ -40,7 +44,9 @@ public:
|
|||||||
void cdromMount(int i);
|
void cdromMount(int i);
|
||||||
void cdromMount(int i, const QString& filename);
|
void cdromMount(int i, const QString& filename);
|
||||||
void cdromEject(int i);
|
void cdromEject(int i);
|
||||||
void cdromReload(int i);
|
void cdromReload(int index, int slot);
|
||||||
|
void updateImageHistory(int index, int slot, ui::MediaType type);
|
||||||
|
void clearImageHistory();
|
||||||
void cdromUpdateMenu(int i);
|
void cdromUpdateMenu(int i);
|
||||||
|
|
||||||
void zipNewImage(int i);
|
void zipNewImage(int i);
|
||||||
@@ -72,6 +78,7 @@ private:
|
|||||||
QMap<int, QMenu*> netMenus;
|
QMap<int, QMenu*> netMenus;
|
||||||
|
|
||||||
QString getMediaOpenDirectory();
|
QString getMediaOpenDirectory();
|
||||||
|
ui::MediaHistoryManager mhm;
|
||||||
|
|
||||||
int cassetteRecordPos;
|
int cassetteRecordPos;
|
||||||
int cassettePlayPos;
|
int cassettePlayPos;
|
||||||
@@ -87,6 +94,8 @@ private:
|
|||||||
int cdromMutePos;
|
int cdromMutePos;
|
||||||
int cdromReloadPos;
|
int cdromReloadPos;
|
||||||
int cdromImagePos;
|
int cdromImagePos;
|
||||||
|
int cdromImageHistoryPos[MAX_PREV_IMAGES];
|
||||||
|
int floppyImageHistoryPos[MAX_PREV_IMAGES];
|
||||||
|
|
||||||
int zipEjectPos;
|
int zipEjectPos;
|
||||||
int zipReloadPos;
|
int zipReloadPos;
|
||||||
|
|||||||
@@ -167,7 +167,13 @@ plat_fopen(const char *path, const char *mode)
|
|||||||
FILE *
|
FILE *
|
||||||
plat_fopen64(const char *path, const char *mode)
|
plat_fopen64(const char *path, const char *mode)
|
||||||
{
|
{
|
||||||
|
#if defined(Q_OS_MACOS) or defined(Q_OS_LINUX)
|
||||||
|
QFileInfo fi(path);
|
||||||
|
QString filename = fi.isRelative() ? usr_path + fi.filePath() : fi.filePath();
|
||||||
|
return fopen(filename.toUtf8().constData(), mode);
|
||||||
|
#else
|
||||||
return fopen(QString::fromUtf8(path).toLocal8Bit(), mode);
|
return fopen(QString::fromUtf8(path).toLocal8Bit(), mode);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
%global romver v3.7
|
%global romver v3.7
|
||||||
|
|
||||||
Name: 86Box
|
Name: 86Box
|
||||||
Version: 3.7.1
|
Version: 3.8
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Classic PC emulator
|
Summary: Classic PC emulator
|
||||||
License: GPLv2+
|
License: GPLv2+
|
||||||
@@ -117,5 +117,5 @@ popd
|
|||||||
%{_datadir}/%{name}/roms
|
%{_datadir}/%{name}/roms
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Tue Aug 02 2022 Robert de Rooy <robert.de.rooy[AT]gmail.com> 3.7.1-1
|
* Tue Aug 30 2022 Robert de Rooy <robert.de.rooy[AT]gmail.com> 3.8-1
|
||||||
- Bump release
|
- Bump release
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</categories>
|
</categories>
|
||||||
<launchable type="desktop-id">net.86box.86Box.desktop</launchable>
|
<launchable type="desktop-id">net.86box.86Box.desktop</launchable>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="3.7.1" date="2022-08-02"/>
|
<release version="3.8" date="2022-08-30"/>
|
||||||
</releases>
|
</releases>
|
||||||
<content_rating type="oars-1.1" />
|
<content_rating type="oars-1.1" />
|
||||||
<description>
|
<description>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "86box",
|
"name": "86box",
|
||||||
"version-string": "3.7.1",
|
"version-string": "3.8",
|
||||||
"homepage": "https://86box.net/",
|
"homepage": "https://86box.net/",
|
||||||
"documentation": "http://86box.readthedocs.io/",
|
"documentation": "http://86box.readthedocs.io/",
|
||||||
"license": "GPL-2.0-or-later",
|
"license": "GPL-2.0-or-later",
|
||||||
|
|||||||
Reference in New Issue
Block a user