diff --git a/.gitattributes b/.gitattributes index 3145ba20f..7a7c81770 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,7 @@ # *.xcf filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text *.png filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text *.bmp filter=lfs diff=lfs merge=lfs -text diff --git a/CHANGELOG b/CHANGELOG index d6d8aad66..e949ceec6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,7 +20,8 @@ API Changes are matched without regard to case and a leading own-directory prefix ("DLe/Cfg/x") is ignored. singe --unpack GAME.game DIRECTORY restores the files and singe --patch GAME.game SOURCE replaces files from a - directory or a patch database in one transaction. Loose games are + directory or a patch database in one transaction. --entry=N picks + which games.dat entry a .game launch runs. Loose games are untouched by all of this. Scripts get SQLite for their own data with require("sqlite3"). The archive installer (.game, .tool, and .patch archives dropped beside the binary) is gone along with libarchive; a diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ef18c130..ac0acb0cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,11 +36,19 @@ if(NOT DEFINED KANGAROO_ARCH) set(KANGAROO_ARCH x86_64) endif() -# Where build-all.sh installed the third party libraries for this platform. +# Where the superbuild installs the third party libraries for this platform. set(BUILD_DIR ${CMAKE_SOURCE_DIR}/.builddir/${KANGAROO_OS}/${KANGAROO_ARCH}) set(GENERATED_DIR ${CMAKE_BINARY_DIR}/generated) file(MAKE_DIRECTORY ${GENERATED_DIR}) +# With SINGE_SUPERBUILD on (the presets' default) this configure builds the vendored libraries +# first and then Singe as a nested project; see cmake/Superbuild.cmake. +option(SINGE_SUPERBUILD "Build the vendored libraries, then Singe" OFF) +if(SINGE_SUPERBUILD) + include(cmake/Superbuild.cmake) + return() +endif() + # ===== Host tools needed to generate embedded resources ===== @@ -122,15 +130,32 @@ endfunction() # Images singeEmbedImage(font) -singeEmbedImage(icon) +# The icon is drawn large; the window gets a 128 pixel copy and the Windows .ico the usual sizes, +# both cut from a square, transparent-padded render so a non-square source keeps its proportions. +set(ICON_SIZE 1024) +add_custom_command( + OUTPUT ${GENERATED_DIR}/iconSquare.png + COMMAND ${IMAGEMAGICK} ${CMAKE_SOURCE_DIR}/assets/icon.xcf -background "rgba(0,0,0,0)" -flatten -resize "${ICON_SIZE}x${ICON_SIZE}>" -gravity center -extent ${ICON_SIZE}x${ICON_SIZE} ${GENERATED_DIR}/iconSquare.png + DEPENDS assets/icon.xcf + COMMENT "Rendering iconSquare.png" + VERBATIM +) +add_custom_command( + OUTPUT ${GENERATED_DIR}/icon.png + COMMAND ${IMAGEMAGICK} ${GENERATED_DIR}/iconSquare.png -resize 128x128 ${GENERATED_DIR}/icon.png + DEPENDS ${GENERATED_DIR}/iconSquare.png + COMMENT "Rendering icon.png" + VERBATIM +) +singeEmbed(${GENERATED_DIR}/icon.png ${GENERATED_DIR}/icon.h "") singeEmbedImage(kangarooPunchLogo) singeEmbedImage(singeLogo) # Windows icon for the resource file. add_custom_command( OUTPUT ${GENERATED_DIR}/icon.ico - COMMAND ${IMAGEMAGICK} ${GENERATED_DIR}/icon.png -define icon:auto-resize=64,48,32,16 ${GENERATED_DIR}/icon.ico - DEPENDS ${GENERATED_DIR}/icon.png + COMMAND ${IMAGEMAGICK} ${GENERATED_DIR}/iconSquare.png -define icon:auto-resize=256,64,48,32,16 ${GENERATED_DIR}/icon.ico + DEPENDS ${GENERATED_DIR}/iconSquare.png COMMENT "Rendering icon.ico" VERBATIM ) @@ -466,7 +491,7 @@ else() message(FATAL_ERROR "Unknown KANGAROO_OS: ${KANGAROO_OS}") endif() -# FFmpeg's hardware decoding backends (VA-API, VDPAU, DRM, V4L2) are whatever build-all.sh +# FFmpeg's hardware decoding backends (VA-API, VDPAU, DRM, V4L2) are whatever the superbuild # configured; its pkg-config files name the system libraries they need, so read them back. if(KANGAROO_OS STREQUAL "linux" OR KANGAROO_OS STREQUAL "pi") foreach(pc libavutil libavcodec) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 000000000..b2b26bba0 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,85 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 25, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "generator": "Unix Makefiles", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "SINGE_SUPERBUILD": "ON" + } + }, + { + "name": "linux-x86_64", + "displayName": "Linux x86_64", + "inherits": "base", + "binaryDir": "${sourceDir}/.builddir/linux/x86_64/superbuild", + "toolchainFile": "${sourceDir}/../toolchains/cmake/x86_64-linux-gnu.cmake", + "cacheVariables": { + "SINGE_TRIPLE": "x86_64-linux-gnu", + "SINGE_CROSS_OS": "linux", + "SINGE_SUFFIX": "" + } + }, + { + "name": "pi-aarch64", + "displayName": "Raspberry Pi 64-bit", + "inherits": "base", + "binaryDir": "${sourceDir}/.builddir/pi/aarch64/superbuild", + "toolchainFile": "${sourceDir}/../toolchains/cmake/aarch64-rpi3-linux-gnu.cmake", + "cacheVariables": { + "SINGE_TRIPLE": "aarch64-rpi3-linux-gnu", + "SINGE_CROSS_OS": "linux", + "SINGE_SUFFIX": "" + } + }, + { + "name": "macos-aarch64", + "displayName": "macOS Apple silicon", + "inherits": "base", + "binaryDir": "${sourceDir}/.builddir/macos/aarch64/superbuild", + "toolchainFile": "${sourceDir}/../toolchains/cmake/aarch64-apple-darwin22.4.cmake", + "cacheVariables": { + "SINGE_TRIPLE": "aarch64-apple-darwin22.4", + "SINGE_CROSS_OS": "darwin", + "SINGE_SUFFIX": "" + } + }, + { + "name": "windows-x86_64", + "displayName": "Windows x86_64", + "inherits": "base", + "binaryDir": "${sourceDir}/.builddir/windows/x86_64/superbuild", + "toolchainFile": "${sourceDir}/../toolchains/cmake/x86_64-w64-mingw32.cmake", + "cacheVariables": { + "SINGE_TRIPLE": "x86_64-w64-mingw32", + "SINGE_CROSS_OS": "mingw32", + "SINGE_SUFFIX": ".exe" + } + } + ], + "buildPresets": [ + { + "name": "linux-x86_64", + "configurePreset": "linux-x86_64" + }, + { + "name": "pi-aarch64", + "configurePreset": "pi-aarch64" + }, + { + "name": "macos-aarch64", + "configurePreset": "macos-aarch64" + }, + { + "name": "windows-x86_64", + "configurePreset": "windows-x86_64" + } + ] +} diff --git a/INSTALL b/INSTALL index 16513edc0..348aa6891 100644 --- a/INSTALL +++ b/INSTALL @@ -43,3 +43,22 @@ COMMAND LINE Run the binary with -h (or --help) for the option summary. Every option is described in the "Command Line Options" chapter of Singe/Manual.pdf, which is extracted on first run. + + +BUILDING FROM SOURCE +==================== + +The build is a CMake superbuild: one preset per platform builds every +vendored library (zlib, zstd, SDL3 and its satellites, OpenSSL, FFmpeg) +into .builddir// and then Singe against them. The cross +toolchains live in ../toolchains (see toolchains.sh there); build-all.sh +prepares that environment and runs the preset: + + ./build-all.sh every supported platform + ./build-all.sh linux x86_64 one platform + ./build-all.sh linux x86_64 --target singe Singe alone, libraries as built + ./build-all.sh linux x86_64 --target rebuild-ffmpeg redo one library + +The finished binary is copied to .builddir/Singe-v--. +Presets are linux-x86_64, pi-aarch64, macos-aarch64 and windows-x86_64 +(CMakePresets.json). diff --git a/assets/BreatheFire-65pg.ttf b/assets/BreatheFire-65pg.ttf deleted file mode 100644 index 76d1f2c12..000000000 --- a/assets/BreatheFire-65pg.ttf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:766ddc21cc9550bd5fc5a9c4153243c2eb15e968a48fb4e534ad07c2025d157e -size 26264 diff --git a/assets/Dragon.jpeg b/assets/Dragon.jpeg new file mode 100644 index 000000000..f9da477ab --- /dev/null +++ b/assets/Dragon.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:290be56ef259099c8bc7ec8018b1d5bb5749d3c90bdb9c10a9e2fe801c427590 +size 301167 diff --git a/assets/SingeText.jpeg b/assets/SingeText.jpeg new file mode 100644 index 000000000..68e96ac5a --- /dev/null +++ b/assets/SingeText.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13d1a888c955635a3f686fad895bfc29aa67853f7c580522dab47446266d39e4 +size 236970 diff --git a/assets/icon.xcf b/assets/icon.xcf index 14c711ca6..5bb74aecd 100644 --- a/assets/icon.xcf +++ b/assets/icon.xcf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4da9618f19e12da35ecde1cae13f59ada65541ecacfe1ebd6717d1962b33fc06 -size 5928 +oid sha256:11f81600f8fc76d79e4fb7773c2d04ee9fcb2b7719fe4cf74d77fd1029641300 +size 1211284 diff --git a/assets/indexing.xcf b/assets/indexing.xcf deleted file mode 100644 index f65810e8b..000000000 --- a/assets/indexing.xcf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5234e23c4b6e991941d3c344ecbe650780da21ad6379932b485fe1fa7c96a191 -size 53941 diff --git a/assets/laserDisc.xcf b/assets/laserDisc.xcf deleted file mode 100644 index 2d0b85150..000000000 --- a/assets/laserDisc.xcf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e97f93b37f6ce29820c110cc7c25b89110aa0292df5bb28feb982b395537cf21 -size 986516 diff --git a/assets/magnifyingGlass.xcf b/assets/magnifyingGlass.xcf deleted file mode 100644 index 71f63de76..000000000 --- a/assets/magnifyingGlass.xcf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6701bc1050020363850820ac0cd0606ea5dd05ccc3d77baa1ab58891042a4bf7 -size 209461 diff --git a/assets/singeLogo.xcf b/assets/singeLogo.xcf index 2539b8640..1e47297b8 100644 --- a/assets/singeLogo.xcf +++ b/assets/singeLogo.xcf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31446cf7492bfadce0029b511bdb1d556d908adba41e688afc6728f0e13c70b3 -size 911807 +oid sha256:d23b71f836bfd5339c10d96200cfabbb79e69851eaae3eb12f4be5663529d0d2 +size 1010687 diff --git a/build-all.sh b/build-all.sh index 59003d31d..c408c9ddf 100755 --- a/build-all.sh +++ b/build-all.sh @@ -20,245 +20,35 @@ # 02110-1301, USA. # +# Builds Singe and every vendored library for one platform, or all of them. The work is done +# by the CMake superbuild (cmake/Superbuild.cmake, one preset per platform in +# CMakePresets.json); this script only prepares the toolchain environment and calls it: +# +# ./build-all.sh every supported platform +# ./build-all.sh linux x86_64 one platform +# +# Anything cmake --build accepts can follow the platform, so a single library can be redone: +# +# ./build-all.sh linux x86_64 --target rebuild-ffmpeg -G_BUILDROOT=${PWD} G_BUILDDIR=.builddir -G_TARGET= -G_GENERATED= function buildAll() { local OS=$1 local ARCH=$2 - local COMMON= - local OPT1= + shift 2 - # Activate toolchain for this platform. + # Activate the toolchain for this platform (PATH, compilers, sysroot, osxcross variables). source <(../toolchains/toolchains.sh use ${ARCH} ${OS}) - G_TARGET=${G_BUILDROOT}/${G_BUILDDIR}/${OS}/${ARCH} - G_GENERATED=${G_TARGET}/generated - COMMON="-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${G_TARGET} -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}" - - export LDFLAGS="-L${G_TARGET}/lib ${LDFLAGS:-}" - export CFLAGS="-I${G_TARGET}/include ${CFLAGS:-}" - export CXXFLAGS="-I${G_TARGET}/include ${CXXFLAGS:-}" - export LD_LIBRARY_PATH="${G_TARGET}/lib" - # Our own libraries first, then the platform's (FFmpeg's hardware decoders need libva and libdrm). - export PKG_CONFIG_LIBDIR="${G_TARGET}/lib/pkgconfig" - if [[ "${OS}" == "linux" ]]; then - export PKG_CONFIG_LIBDIR="${PKG_CONFIG_LIBDIR}:/usr/lib/${TRIPLE}/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig" - elif [[ "${OS}" == "pi" ]]; then - export PKG_CONFIG_LIBDIR="${PKG_CONFIG_LIBDIR}:${SYSROOT}/usr/lib/${TRIPLE}/pkgconfig:${SYSROOT}/usr/lib/pkgconfig:${SYSROOT}/usr/share/pkgconfig" - export PKG_CONFIG_SYSROOT_DIR="${SYSROOT}" - fi - - mkdir -p ${G_GENERATED} - - # Needed for Pi sysroot. + # The Pi sysroot needs the development packages the build links against. if [[ "${OS}" == "pi" ]]; then - export CFLAGS="--sysroot=${SYSROOT} ${CFLAGS}" - export CXXFLAGS="--sysroot=${SYSROOT} ${CXXFLAGS}" sudo chroot ${SYSROOT} apt-get -y install libasound-dev libxi-dev libvdpau-dev libdrm-dev fi - pushd thirdparty/zlib - clearAndEnterBuild - CFLAGS="-I${PWD}" ../configure \ - --prefix=${G_TARGET} \ - --static - make install - popd - - pushd thirdparty/zstd - clearAndEnterBuild - cmake ${COMMON} \ - -DZSTD_BUILD_SHARED=off \ - -DZSTD_BUILD_STATIC=on \ - -DZSTD_BUILD_PROGRAMS=off \ - ../build/cmake - make install - popd - - pushd thirdparty/SDL3 - clearAndEnterBuild - cmake ${COMMON} \ - -DSDL_SHARED=off \ - -DSDL_STATIC=on \ - -DSDL_TESTS=off \ - -DSDL_EXAMPLES=off \ - .. - make install - popd - - pushd thirdparty/SDL3_image - clearAndEnterBuild - # The vendored libpng must see the vendored zlib, not the one under ${G_TARGET}/include. - CFLAGS="${CFLAGS#-I${G_TARGET}/include}" \ - cmake ${COMMON} \ - -DBUILD_SHARED_LIBS=off \ - -DSDLIMAGE_DEPS_SHARED=off \ - -DSDLIMAGE_SAMPLES=off \ - -DSDLIMAGE_TESTS=off \ - -DSDLIMAGE_VENDORED=on \ - -DSDLIMAGE_BACKEND_STB=off \ - -DSDLIMAGE_AVIF=off \ - -DSDLIMAGE_JXL=off \ - -DSDLIMAGE_TIF=off \ - -DSDLIMAGE_WEBP=on \ - -DSDL3_DIR=${G_TARGET}/lib/cmake/SDL3 \ - -DWEBP_BUILD_ANIM_UTILS=off \ - -DWEBP_BUILD_CWEBP=off \ - -DWEBP_BUILD_DWEBP=off \ - -DWEBP_BUILD_GIF2WEBP=off \ - -DWEBP_BUILD_IMG2WEBP=off \ - -DWEBP_BUILD_VWEBP=off \ - -DWEBP_BUILD_WEBPINFO=off \ - -DWEBP_BUILD_WEBPMUX=off \ - -DWEBP_BUILD_EXTRAS=off \ - .. - make install - popd - - pushd thirdparty/SDL3_mixer - clearAndEnterBuild - cmake ${COMMON} \ - -DBUILD_SHARED_LIBS=off \ - -DSDLMIXER_DEPS_SHARED=off \ - -DSDLMIXER_VENDORED=on \ - -DSDLMIXER_EXAMPLES=off \ - -DSDLMIXER_TESTS=off \ - -DSDLMIXER_FLAC_LIBFLAC=off \ - -DSDLMIXER_MP3_MPG123=off \ - -DSDLMIXER_VORBIS_STB=on \ - -DSDLMIXER_VORBIS_VORBISFILE=off \ - -DSDLMIXER_VORBIS_TREMOR=off \ - -DSDLMIXER_GME=off \ - -DSDLMIXER_MIDI_FLUIDSYNTH=off \ - -DSDL3_DIR=${G_TARGET}/lib/cmake/SDL3 \ - -DWAVPACK_ENABLE_ASM=no \ - .. - make install - popd - - pushd thirdparty/SDL3_ttf - clearAndEnterBuild - cmake ${COMMON} \ - -DBUILD_SHARED_LIBS=off \ - -DSDLTTF_VENDORED=on \ - -DSDLTTF_HARFBUZZ=off \ - -DSDLTTF_PLUTOSVG=off \ - -DSDLTTF_SAMPLES=off \ - -DSDL3_DIR=${G_TARGET}/lib/cmake/SDL3 \ - .. - make install - popd - - pushd thirdparty/openssl - clearAndEnterBuild - if [[ "${OS}" == "windows" ]]; then - if [[ "${ARCH}" == "x86" ]]; then - OPT1="mingw" - else - OPT1="mingw64" - fi - else - OPT1="" - fi - if [[ "${OS}" == "pi" ]]; then - if [[ "${ARCH}" == "aarch64" ]]; then - OPT1="linux-aarch64" - else - OPT1="linux-arm4" - fi - fi - if [[ "${OS}" == "macos" ]]; then - if [[ "${ARCH}" == "aarch64" ]]; then - OPT1="darwin64-arm64" - else - OPT1="darwin64-x86_64" - fi - fi - ../Configure ${OPT1} \ - --prefix="${G_TARGET}" \ - --libdir=lib \ - --with-zlib-include="${G_TARGET}/include" \ - --with-zlib-lib="${G_TARGET}/lib" \ - --with-zstd-include="${G_TARGET}/include" \ - --with-zstd-lib="${G_TARGET}/lib" \ - no-apps \ - no-docs \ - no-dso \ - no-dynamic-engine \ - no-module \ - no-shared \ - no-tests \ - zlib \ - enable-zstd - make build_sw - make install_sw - popd - - if [[ "${OS}" == "pi" ]]; then - # Hack to make ffmpeg compile. - mkdir -p "${G_TARGET}/include/sys" - echo "/* File no longer used */" > "${G_TARGET}/include/sys/sysctl.h" - fi - # Hardware decoding: each platform's native API. The libraries these pull in are read back - # from FFmpeg's pkg-config files by CMakeLists.txt, so the link follows this choice. - case "${OS}" in - linux) HWACCEL="--enable-vaapi --enable-vdpau --enable-libdrm" ;; - pi) HWACCEL="--enable-v4l2-m2m --enable-libdrm" ;; - macos) HWACCEL="--enable-videotoolbox" ;; - windows) HWACCEL="--enable-d3d11va --enable-dxva2" ;; - *) HWACCEL="" ;; - esac - pushd thirdparty/ffmpeg - clearAndEnterBuild - # https://trac.ffmpeg.org/wiki/CompilationGuide/CrossCompilingForWindows - ../configure \ - --enable-static \ - --disable-shared \ - --disable-debug \ - --disable-muxers \ - ${HWACCEL} \ - --disable-encoders \ - --disable-filters \ - --disable-network \ - --disable-devices \ - --disable-bzlib \ - --disable-lzma \ - --disable-doc \ - --disable-programs \ - --enable-gpl \ - --enable-version3 \ - --extra-ldflags="-L${G_TARGET}/lib" \ - --prefix=${G_TARGET} \ - --arch=${ARCH} \ - --target-os=${CROSS_OS} \ - --cross-prefix=${TRIPLE}- \ - --cc="${CC}" \ - --cxx="${CXX}" \ - --ranlib="${RANLIB}" - make install - popd - - # Embedded resources, the version header, and the manual are generated by CMake. - - pushd ${G_TARGET} - clearAndEnterBuild - cmake ${COMMON} -DKANGAROO_OS=${OS} -DKANGAROO_ARCH=${ARCH} ${G_BUILDROOT} - make - # CMake names the binary Singe-v--. - mv -f Singe-v*${SUFFIX} ${G_BUILDROOT}/${G_BUILDDIR}/. - popd -} - - -function clearAndEnterBuild() { - [[ -d ${G_BUILDDIR} ]] && rm -rf ${G_BUILDDIR} - mkdir -p ${G_BUILDDIR} - cd ${G_BUILDDIR} + cmake --preset ${OS}-${ARCH} + cmake --build --preset ${OS}-${ARCH} "$@" } @@ -266,7 +56,6 @@ function clearAndEnterBuild() { # -u = stop script on undefined variable # -o pipefail = stop pipeline if any step fails set -euo pipefail -export MAKEFLAGS="${MAKEFLAGS:--j$(nproc)}" if [[ ! -x ../toolchains/toolchains.sh ]]; then echo "error: ../toolchains/toolchains.sh not found (see INSTALL)" >&2 @@ -294,20 +83,11 @@ sudo apt-get install -y \ libdrm-dev -# Usage: build-all.sh [os arch] (default: every supported platform) -if [[ $# -eq 2 ]]; then - buildAll "$1" "$2" 2>&1 | tee ${G_BUILDDIR}/$1-$2.log +if [[ $# -ge 2 ]]; then + buildAll "$@" 2>&1 | tee ${G_BUILDDIR}/$1-$2.log else buildAll linux x86_64 2>&1 | tee ${G_BUILDDIR}/linux-x86_64.log buildAll macos aarch64 2>&1 | tee ${G_BUILDDIR}/macos-aarch64.log buildAll pi aarch64 2>&1 | tee ${G_BUILDDIR}/pi-aarch64.log buildAll windows x86_64 2>&1 | tee ${G_BUILDDIR}/windows-x86_64.log fi - - -# === UNSUPPORTED === - -#buildAll macos x86_64 2>&1 | tee ${G_BUILDDIR}/macos-x86_64.log -#buildAll linux x86 -#buildAll macos x86 #***TODO*** Needs older SDL2 -#buildAll windows x86 diff --git a/cmake/Superbuild.cmake b/cmake/Superbuild.cmake new file mode 100644 index 000000000..5d2377139 --- /dev/null +++ b/cmake/Superbuild.cmake @@ -0,0 +1,267 @@ +# +# Singe 2 +# Copyright (C) 2006-2024 Scott Duensing +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licens +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +# Superbuild: builds every vendored library into BUILD_DIR, then Singe against them. +# Entered from CMakeLists.txt when SINGE_SUPERBUILD is ON (the presets set it). Each +# library is an ExternalProject with its own configure, build and install steps, so a +# change to one rebuilds only it and what depends on it: +# cmake --build --preset linux-x86_64 --target rebuild-ffmpeg +# The toolchain environment (PATH, SYSROOT, osxcross variables) comes from +# ../toolchains/toolchains.sh; build-all.sh sources it before calling cmake. + +include(ExternalProject) +include(ProcessorCount) + +ProcessorCount(SB_JOBS) +if(SB_JOBS EQUAL 0) + set(SB_JOBS 4) +endif() + +# Target description: the presets pass these; a plain cmake invocation can take them from +# the environment toolchains.sh exports. +foreach(pair "SINGE_TRIPLE;TRIPLE" "SINGE_CROSS_OS;CROSS_OS" "SINGE_SUFFIX;SUFFIX") + list(GET pair 0 var) + list(GET pair 1 env) + if(NOT DEFINED ${var}) + set(${var} "$ENV{${env}}") + endif() +endforeach() +if(NOT SINGE_TRIPLE OR NOT SINGE_CROSS_OS) + message(FATAL_ERROR "SINGE_TRIPLE and SINGE_CROSS_OS must be set (use a preset or source toolchains.sh).") +endif() + +set(SB_PREFIX ${BUILD_DIR}) +set(SB_THIRDPARTY ${CMAKE_SOURCE_DIR}/thirdparty) +file(MAKE_DIRECTORY ${SB_PREFIX}/include ${SB_PREFIX}/lib) + +# Compilers as the toolchain resolved them; the configure based libraries get them by name. +set(SB_CC "${CMAKE_C_COMPILER}") +if(DEFINED ENV{CXX}) + set(SB_CXX "$ENV{CXX}") +else() + string(REGEX REPLACE "gcc$" "g++" SB_CXX "${SB_CC}") + string(REGEX REPLACE "clang$" "clang++" SB_CXX "${SB_CXX}") +endif() +if(CMAKE_RANLIB) + set(SB_RANLIB "${CMAKE_RANLIB}") +elseif(DEFINED ENV{RANLIB}) + set(SB_RANLIB "$ENV{RANLIB}") +else() + set(SB_RANLIB ranlib) +endif() + +# The flags build-all.sh exported for every library: our install tree first, then the +# platform's. The Pi sysroot and the osxcross linker come from the toolchain. +set(SB_CFLAGS_BASE "$ENV{CFLAGS}") +set(SB_LDFLAGS "-L${SB_PREFIX}/lib $ENV{LDFLAGS}") +if(CMAKE_SYSROOT) + set(SB_CFLAGS_BASE "--sysroot=${CMAKE_SYSROOT} ${SB_CFLAGS_BASE}") +endif() +set(SB_CFLAGS "-I${SB_PREFIX}/include ${SB_CFLAGS_BASE}") +set(SB_PKG_CONFIG_LIBDIR "${SB_PREFIX}/lib/pkgconfig") +if(KANGAROO_OS STREQUAL "linux") + set(SB_PKG_CONFIG_LIBDIR "${SB_PKG_CONFIG_LIBDIR}:/usr/lib/${SINGE_TRIPLE}/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig") +elseif(KANGAROO_OS STREQUAL "pi") + set(SB_PKG_CONFIG_LIBDIR "${SB_PKG_CONFIG_LIBDIR}:${CMAKE_SYSROOT}/usr/lib/${SINGE_TRIPLE}/pkgconfig:${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig") +elseif(DEFINED ENV{PKG_CONFIG_LIBDIR}) + set(SB_PKG_CONFIG_LIBDIR "${SB_PKG_CONFIG_LIBDIR}:$ENV{PKG_CONFIG_LIBDIR}") +endif() + +# Environment every step runs under. CFLAGS is a parameter because SDL3_image's vendored +# libpng must not see the zlib headers under the install prefix. +function(singeStepEnv outVar cflags) + set(env ${CMAKE_COMMAND} -E env + "CC=${SB_CC}" + "CXX=${SB_CXX}" + "RANLIB=${SB_RANLIB}" + "CFLAGS=${cflags}" + "CXXFLAGS=${cflags}" + "LDFLAGS=${SB_LDFLAGS}" + "LD_LIBRARY_PATH=${SB_PREFIX}/lib" + "PKG_CONFIG_LIBDIR=${SB_PKG_CONFIG_LIBDIR}" + "MAKEFLAGS=-j${SB_JOBS}" + ) + if(CMAKE_AR) + list(APPEND env "AR=${CMAKE_AR}") + endif() + if(KANGAROO_OS STREQUAL "pi") + list(APPEND env "PKG_CONFIG_SYSROOT_DIR=${CMAKE_SYSROOT}") + endif() + set(${outVar} ${env} PARENT_SCOPE) +endfunction() + +singeStepEnv(SB_ENV "${SB_CFLAGS}") +singeStepEnv(SB_ENV_NO_PREFIX_INCLUDE "${SB_CFLAGS_BASE}") + +set(SB_CMAKE_ARGS + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_INSTALL_PREFIX=${SB_PREFIX} + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} +) + +# A vendored CMake project: name, source directory, its dependencies, extra cache arguments, +# and the step environment to use. +function(singeCmakeProject name source deps args env) + set(binary ${SB_PREFIX}/build/${name}) + ExternalProject_Add(${name} + SOURCE_DIR ${source} + BINARY_DIR ${binary} + DEPENDS ${deps} + CONFIGURE_COMMAND ${env} ${CMAKE_COMMAND} -S ${source} -B ${binary} ${SB_CMAKE_ARGS} ${args} + BUILD_COMMAND ${env} ${CMAKE_COMMAND} --build ${binary} --parallel ${SB_JOBS} + INSTALL_COMMAND ${env} ${CMAKE_COMMAND} --build ${binary} --target install + LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON + ) + singeRebuildTarget(${name}) +endfunction() + +# rebuild- forgets the stamps and builds the library again, then whatever depends on it +# is rebuilt by the normal build. +function(singeRebuildTarget name) + ExternalProject_Get_Property(${name} STAMP_DIR) + add_custom_target(rebuild-${name} + COMMAND ${CMAKE_COMMAND} -E rm -rf ${STAMP_DIR} + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ${name} + COMMENT "Rebuilding ${name}" + ) +endfunction() + + +# ===== zlib (configure script; its own include directory, not the prefix, as before) ===== + +set(zlibBinary ${SB_PREFIX}/build/zlib) +ExternalProject_Add(zlib + SOURCE_DIR ${SB_THIRDPARTY}/zlib + BINARY_DIR ${zlibBinary} + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env "CC=${SB_CC}" "CFLAGS=-I${SB_THIRDPARTY}/zlib" ${SB_THIRDPARTY}/zlib/configure --prefix=${SB_PREFIX} --static + BUILD_COMMAND ${SB_ENV} make + INSTALL_COMMAND ${SB_ENV} make install + LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON +) +singeRebuildTarget(zlib) + + +# ===== zstd ===== + +singeCmakeProject(zstd ${SB_THIRDPARTY}/zstd/build/cmake "" "-DZSTD_BUILD_SHARED=off;-DZSTD_BUILD_STATIC=on;-DZSTD_BUILD_PROGRAMS=off" "${SB_ENV}") + + +# ===== SDL3 and its satellites ===== + +singeCmakeProject(SDL3 ${SB_THIRDPARTY}/SDL3 "" "-DSDL_SHARED=off;-DSDL_STATIC=on;-DSDL_TESTS=off;-DSDL_EXAMPLES=off" "${SB_ENV}") + +set(sdl3Dir -DSDL3_DIR=${SB_PREFIX}/lib/cmake/SDL3) + +singeCmakeProject(SDL3_image ${SB_THIRDPARTY}/SDL3_image "SDL3;zlib" + "-DBUILD_SHARED_LIBS=off;-DSDLIMAGE_DEPS_SHARED=off;-DSDLIMAGE_SAMPLES=off;-DSDLIMAGE_TESTS=off;-DSDLIMAGE_VENDORED=on;-DSDLIMAGE_BACKEND_STB=off;-DSDLIMAGE_AVIF=off;-DSDLIMAGE_JXL=off;-DSDLIMAGE_TIF=off;-DSDLIMAGE_WEBP=on;${sdl3Dir};-DWEBP_BUILD_ANIM_UTILS=off;-DWEBP_BUILD_CWEBP=off;-DWEBP_BUILD_DWEBP=off;-DWEBP_BUILD_GIF2WEBP=off;-DWEBP_BUILD_IMG2WEBP=off;-DWEBP_BUILD_VWEBP=off;-DWEBP_BUILD_WEBPINFO=off;-DWEBP_BUILD_WEBPMUX=off;-DWEBP_BUILD_EXTRAS=off" + "${SB_ENV_NO_PREFIX_INCLUDE}") + +singeCmakeProject(SDL3_mixer ${SB_THIRDPARTY}/SDL3_mixer "SDL3" + "-DBUILD_SHARED_LIBS=off;-DSDLMIXER_DEPS_SHARED=off;-DSDLMIXER_VENDORED=on;-DSDLMIXER_EXAMPLES=off;-DSDLMIXER_TESTS=off;-DSDLMIXER_FLAC_LIBFLAC=off;-DSDLMIXER_MP3_MPG123=off;-DSDLMIXER_VORBIS_STB=on;-DSDLMIXER_VORBIS_VORBISFILE=off;-DSDLMIXER_VORBIS_TREMOR=off;-DSDLMIXER_GME=off;-DSDLMIXER_MIDI_FLUIDSYNTH=off;${sdl3Dir};-DWAVPACK_ENABLE_ASM=no" + "${SB_ENV}") + +singeCmakeProject(SDL3_ttf ${SB_THIRDPARTY}/SDL3_ttf "SDL3" + "-DBUILD_SHARED_LIBS=off;-DSDLTTF_VENDORED=on;-DSDLTTF_HARFBUZZ=off;-DSDLTTF_PLUTOSVG=off;-DSDLTTF_SAMPLES=off;${sdl3Dir}" + "${SB_ENV}") + + +# ===== OpenSSL (its own Configure; the target name follows the platform) ===== + +if(KANGAROO_OS STREQUAL "windows") + if(KANGAROO_ARCH STREQUAL "x86") + set(opensslTarget mingw) + else() + set(opensslTarget mingw64) + endif() +elseif(KANGAROO_OS STREQUAL "pi") + if(KANGAROO_ARCH STREQUAL "aarch64") + set(opensslTarget linux-aarch64) + else() + set(opensslTarget linux-arm4) + endif() +elseif(KANGAROO_OS STREQUAL "macos") + if(KANGAROO_ARCH STREQUAL "aarch64") + set(opensslTarget darwin64-arm64) + else() + set(opensslTarget darwin64-x86_64) + endif() +else() + set(opensslTarget "") +endif() +# OpenSSL's Configure derives the source tree from the build directory's position and only +# copes with a build directory nested inside its own tree, so this one lives there. +set(opensslBinary ${SB_THIRDPARTY}/openssl/.builddir) +ExternalProject_Add(openssl + SOURCE_DIR ${SB_THIRDPARTY}/openssl + BINARY_DIR ${opensslBinary} + DEPENDS zlib zstd + CONFIGURE_COMMAND ${SB_ENV} perl ../Configure ${opensslTarget} --prefix=${SB_PREFIX} --libdir=lib --with-zlib-include=${SB_PREFIX}/include --with-zlib-lib=${SB_PREFIX}/lib --with-zstd-include=${SB_PREFIX}/include --with-zstd-lib=${SB_PREFIX}/lib no-apps no-docs no-dso no-dynamic-engine no-module no-shared no-tests zlib enable-zstd + BUILD_COMMAND ${SB_ENV} make build_sw + INSTALL_COMMAND ${SB_ENV} make install_sw + LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON +) +singeRebuildTarget(openssl) + + +# ===== FFmpeg (hardware decoding per platform; CMakeLists.txt reads its .pc files back) ===== + +if(KANGAROO_OS STREQUAL "linux") + set(hwaccel --enable-vaapi --enable-vdpau --enable-libdrm) +elseif(KANGAROO_OS STREQUAL "pi") + set(hwaccel --enable-v4l2-m2m --enable-libdrm) + # FFmpeg still includes a header the Pi's libc dropped. + file(WRITE ${SB_PREFIX}/include/sys/sysctl.h "/* File no longer used */\n") +elseif(KANGAROO_OS STREQUAL "macos") + set(hwaccel --enable-videotoolbox) +elseif(KANGAROO_OS STREQUAL "windows") + set(hwaccel --enable-d3d11va --enable-dxva2) +else() + set(hwaccel "") +endif() +set(ffmpegBinary ${SB_PREFIX}/build/ffmpeg) +ExternalProject_Add(ffmpeg + SOURCE_DIR ${SB_THIRDPARTY}/ffmpeg + BINARY_DIR ${ffmpegBinary} + CONFIGURE_COMMAND ${SB_ENV} ${SB_THIRDPARTY}/ffmpeg/configure --enable-static --disable-shared --disable-debug --disable-muxers ${hwaccel} --disable-encoders --disable-filters --disable-network --disable-devices --disable-bzlib --disable-lzma --disable-doc --disable-programs --enable-gpl --enable-version3 --extra-ldflags=-L${SB_PREFIX}/lib --prefix=${SB_PREFIX} --arch=${KANGAROO_ARCH} --target-os=${SINGE_CROSS_OS} --cross-prefix=${SINGE_TRIPLE}- --cc=${SB_CC} --cxx=${SB_CXX} --ranlib=${SB_RANLIB} + BUILD_COMMAND ${SB_ENV} make + INSTALL_COMMAND ${SB_ENV} make install + LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON +) +singeRebuildTarget(ffmpeg) + + +# ===== Singe itself, against everything above ===== + +string(SUBSTRING ${KANGAROO_OS} 0 1 osInitial) +string(SUBSTRING ${KANGAROO_OS} 1 -1 osRest) +string(TOUPPER ${osInitial} osInitial) +set(singeBinaryName "Singe-v${PROJECT_VERSION}-${osInitial}${osRest}-${KANGAROO_ARCH}${SINGE_SUFFIX}") +set(singeBinary ${SB_PREFIX}/singe) +ExternalProject_Add(singe + SOURCE_DIR ${CMAKE_SOURCE_DIR} + BINARY_DIR ${singeBinary} + DEPENDS zlib zstd SDL3 SDL3_image SDL3_mixer SDL3_ttf openssl ffmpeg + CONFIGURE_COMMAND ${SB_ENV} ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${singeBinary} ${SB_CMAKE_ARGS} -DSINGE_SUPERBUILD=OFF -DKANGAROO_OS=${KANGAROO_OS} -DKANGAROO_ARCH=${KANGAROO_ARCH} + BUILD_COMMAND ${SB_ENV} ${CMAKE_COMMAND} --build ${singeBinary} --parallel ${SB_JOBS} + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${singeBinary}/${singeBinaryName} ${CMAKE_SOURCE_DIR}/.builddir/${singeBinaryName} + BUILD_ALWAYS ON +) + +message(STATUS "Superbuild for ${KANGAROO_OS}/${KANGAROO_ARCH}: libraries into ${SB_PREFIX}, binary ${singeBinaryName}") diff --git a/docs/Manual.adoc b/docs/Manual.adoc index ea009587d..313cd4a6f 100644 --- a/docs/Manual.adoc +++ b/docs/Manual.adoc @@ -108,7 +108,7 @@ Singe [OPTIONS] scriptName{.singe} The script name is the only required argument. It may be a `.singe` file, a directory containing a script of the same name (`ActionMax` finds `ActionMax/ActionMax.singe`), or a packed game (`DLe.game` runs the first -entry of the `games.dat` inside it). For a laserdisc game (`--disc`) with no +entry of the `games.dat` inside it, `--entry=N` another). For a laserdisc game (`--disc`) with no `--framefile`, Singe looks for a video next to the script with the same base name and any extension FFmpeg can demux, then for a `.txt` framefile. @@ -121,6 +121,7 @@ name and any extension FFmpeg can demux, then for a `.txt` framefile. | `-C`, `--canvas=WxH` | World size for a game without a disc, default 720x480. Ignored when there is a disc. | `-D`, `--disc` | Play a laserdisc video: the one named by `--framefile`, or the video found next to the script. Implied by `--framefile`. Without it a video next to the script is reported and ignored. | `-d`, `--datadir=PATHNAME` | Directory for everything Singe writes: video indexes, `trace.txt`, screenshots, the menu's `menu.dat`. A subdirectory named for the game's directory is created inside it. Defaults to the game's own directory. +| `-E`, `--entry=N` | Run the Nth entry of the `games.dat` inside a `.game` file (default 1). See <>. | `-e`, `--volume_nonvldp=PERCENT` | Sound effect and extra video volume, `0` to `100`. | `-f`, `--fullscreen` | Exclusive full screen at the desktop resolution. | `-H`, `--softwarevideo` | Decode video in software even when the platform offers a hardware decoder (VA-API or VDPAU on Linux, D3D11VA on Windows, VideoToolbox on macOS). Use it to rule the hardware path in or out when a video misbehaves; the program trace says which decoder is in use. @@ -316,6 +317,7 @@ lists every `.game` file it finds beside the game directories. ---- Singe --pack DLe DLe.game Pack the DLe directory into DLe.game Singe DLe.game Run the first games.dat entry in it +Singe --entry=2 DLe.game Run its second entry Singe --unpack DLe.game DLe Write the files back out Singe --patch DLe.game fixes Replace files from a directory (or a patch database) ---- diff --git a/src/main.c b/src/main.c index da3babd1f..1dbc8e014 100644 --- a/src/main.c +++ b/src/main.c @@ -118,6 +118,7 @@ static const OptionT _options[] = { { 'C', "canvas", ap_yes, "WxH", "world size for games without a disc (default 720x480)", false }, { 'D', "disc", ap_no, NULL, "play a laserdisc video (implied by --framefile)", false }, { 'd', "datadir", ap_yes, "PATHNAME", "alternate location for written files", false }, + { 'E', "entry", ap_yes, "N", "run the Nth games.dat entry of a .game file (default 1)", false }, { 'e', "volume_nonvldp", ap_yes, "PERCENT", "specify sound effects volume in percent", false }, { 'f', "fullscreen", ap_no, NULL, "run in full screen mode", false }, { 'g', "sindengun", ap_yes, "'PARAMS'", "enable Sinden Light Gun support", true }, @@ -513,6 +514,7 @@ static ConfigT *_parseArguments(const char *exeName, int32_t argc, char *argv[]) conf->resolutionWasCalculated = true; conf->canvasWidth = CANVAS_DEFAULT_WIDTH; conf->canvasHeight = CANVAS_DEFAULT_HEIGHT; + conf->entry = 1; // Parse command line for (argIndex = 0; argIndex < ap_arguments(&parser); argIndex++) { @@ -585,6 +587,13 @@ static ConfigT *_parseArguments(const char *exeName, int32_t argc, char *argv[]) sindenString = strdup(arg); break; + // Which entry of a game database to run + case 'E': + if (!_parseInteger(arg, &conf->entry) || (conf->entry < 1)) { + _showUsage(exeName, "--entry needs a number from 1."); + } + break; + // Software video decoding case 'H': conf->softwareVideo = true; diff --git a/src/singe.c b/src/singe.c index c93dc2a39..2caae06de 100644 --- a/src/singe.c +++ b/src/singe.c @@ -111,6 +111,9 @@ LSEC_API int luaopen_ssl_config(lua_State *L); #define BLUE_SCREEN_BLUE 255 #define LOGO_FADE_STEPS 256 +#define LOGO_MARGIN 40 // Pixels of breathing room around a logo in the logo space +#define LOGO_SPACE_WIDTH 1920 // Logical size the splash screens are laid out in +#define LOGO_SPACE_HEIGHT 1080 #define LOGO_FADE_STEP_MS 3 #define LOGO_HOLD_MS 750 @@ -447,6 +450,7 @@ static void _drawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, ui static void _drawPauseIndicator(const SDL_FRect *target); static InputE _engineSwitch(int32_t scancode); static void _fireMouseMoved(int32_t device, int32_t x, int32_t y, int32_t xr, int32_t yr); +static void _fitRect(int32_t width, int32_t height, int32_t spaceWidth, int32_t spaceHeight, int32_t margin, SDL_FRect *rect); static void _fontDestroy(FontT *font); static void _freezeGame(bool freeze); static void _heldListUpdate(HeldKeyT *list, int32_t *count, bool down, int32_t keysym, int32_t scancode); @@ -1049,19 +1053,25 @@ static void _doLogos(void) { SDL_Surface *surfSinge = NULL; SDL_Texture *texKangaroo = NULL; SDL_Texture *texSinge = NULL; + SDL_FRect rectKangaroo; + SDL_FRect rectSinge; SDL_RendererLogicalPresentation mode = SDL_LOGICAL_PRESENTATION_DISABLED; SDL_GetRenderLogicalPresentation(_global.renderer, &w, &h, &mode); texKangaroo = _loadEmbeddedTexture(kangarooPunchLogo_png, kangarooPunchLogo_png_len, &surfKangaroo); texSinge = _loadEmbeddedTexture(singeLogo_png, singeLogo_png_len, &surfSinge); + // Both logos are fitted into the same space, so the cross fade neither stretches nor jumps. + SDL_SetRenderLogicalPresentation(_global.renderer, LOGO_SPACE_WIDTH, LOGO_SPACE_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX); + _fitRect(surfKangaroo->w, surfKangaroo->h, LOGO_SPACE_WIDTH, LOGO_SPACE_HEIGHT, LOGO_MARGIN, &rectKangaroo); + _fitRect(surfSinge->w, surfSinge->h, LOGO_SPACE_WIDTH, LOGO_SPACE_HEIGHT, LOGO_MARGIN, &rectSinge); + // Fade in to white with Kangaroo logo - SDL_SetRenderLogicalPresentation(_global.renderer, surfKangaroo->w, surfKangaroo->h, SDL_LOGICAL_PRESENTATION_LETTERBOX); for (i = 0; keepGoing && (i < LOGO_FADE_STEPS); i++) { SDL_SetRenderDrawColor(_global.renderer, (uint8_t)i, (uint8_t)i, (uint8_t)i, SDL_ALPHA_OPAQUE); SDL_RenderClear(_global.renderer); SDL_SetTextureAlphaMod(texKangaroo, (uint8_t)i); - SDL_RenderTexture(_global.renderer, texKangaroo, NULL, NULL); + SDL_RenderTexture(_global.renderer, texKangaroo, NULL, &rectKangaroo); SDL_RenderPresent(_global.renderer); keepGoing = _delayAndPump(LOGO_FADE_STEP_MS); } @@ -1071,21 +1081,20 @@ static void _doLogos(void) { for (i = 0; keepGoing && (i < LOGO_FADE_STEPS); i++) { SDL_RenderClear(_global.renderer); SDL_SetTextureAlphaMod(texKangaroo, (uint8_t)(LOGO_FADE_STEPS - 1 - i)); - SDL_RenderTexture(_global.renderer, texKangaroo, NULL, NULL); + SDL_RenderTexture(_global.renderer, texKangaroo, NULL, &rectKangaroo); SDL_SetTextureAlphaMod(texSinge, (uint8_t)i); - SDL_RenderTexture(_global.renderer, texSinge, NULL, NULL); + SDL_RenderTexture(_global.renderer, texSinge, NULL, &rectSinge); SDL_RenderPresent(_global.renderer); keepGoing = _delayAndPump(LOGO_FADE_STEP_MS); } keepGoing = keepGoing && _delayAndPump(LOGO_HOLD_MS); // Fade to black - SDL_SetRenderLogicalPresentation(_global.renderer, surfSinge->w, surfSinge->h, SDL_LOGICAL_PRESENTATION_LETTERBOX); for (i = LOGO_FADE_STEPS - 1; keepGoing && (i >= 0); i--) { SDL_SetRenderDrawColor(_global.renderer, (uint8_t)i, (uint8_t)i, (uint8_t)i, SDL_ALPHA_OPAQUE); SDL_RenderClear(_global.renderer); SDL_SetTextureAlphaMod(texSinge, (uint8_t)i); - SDL_RenderTexture(_global.renderer, texSinge, NULL, NULL); + SDL_RenderTexture(_global.renderer, texSinge, NULL, &rectSinge); SDL_RenderPresent(_global.renderer); keepGoing = _delayAndPump(LOGO_FADE_STEP_MS); } @@ -1208,6 +1217,17 @@ static void _fireMouseMoved(int32_t device, int32_t x, int32_t y, int32_t xr, in } +// Largest rectangle of the given aspect that fits inside the space with a margin, centred. +static void _fitRect(int32_t width, int32_t height, int32_t spaceWidth, int32_t spaceHeight, int32_t margin, SDL_FRect *rect) { + float scale = SDL_min((float)(spaceWidth - 2 * margin) / (float)width, (float)(spaceHeight - 2 * margin) / (float)height); + + rect->w = (float)width * scale; + rect->h = (float)height * scale; + rect->x = ((float)spaceWidth - rect->w) * 0.5f; + rect->y = ((float)spaceHeight - rect->h) * 0.5f; +} + + static void _fontDestroy(FontT *font) { if (_global.fontCurrent == font) { _global.fontCurrent = NULL; @@ -4422,7 +4442,7 @@ static int32_t apiVldpSetVerbose(lua_State *L) { // ===== Engine entry point ===== -// A game database named on the command line runs the first entry of its games.dat. +// A game database named on the command line runs one entry of its games.dat (--entry, default the first). ConfigT *confFromDatabase(const ConfigT *conf) { lua_State *L = luaL_newstate(); ConfigT *base = cloneConf(conf); @@ -4437,14 +4457,14 @@ ConfigT *confFromDatabase(const ConfigT *conf) { utilDie("%s: %s", base->container, lua_tostring(L, -1)); } lua_getglobal(L, "GAMES"); - if (!lua_istable(L, -1) || (lua_rawgeti(L, -1, 1) != LUA_TTABLE)) { - utilDie("%s: games.dat defines no GAMES entries.", base->container); + if (!lua_istable(L, -1) || (lua_rawgeti(L, -1, conf->entry) != LUA_TTABLE)) { + utilDie("%s: games.dat has no entry %d.", base->container, conf->entry); } lua_replace(L, 1); lua_settop(L, 1); result = _buildConfFromTable(L, base); if (result->scriptFile == NULL) { - utilDie("%s: the first games.dat entry has no SCRIPT.", base->container); + utilDie("%s: games.dat entry %d has no SCRIPT.", base->container, conf->entry); } free(result->container); result->container = strdup(base->container); diff --git a/src/singe.h b/src/singe.h index 411227818..999f361bf 100644 --- a/src/singe.h +++ b/src/singe.h @@ -55,6 +55,7 @@ typedef struct ConfigS { char *videoFile; char *scriptFile; char *container; // Game database holding the script, NULL for a loose game + int32_t entry; // Which games.dat entry a database launch runs, from 1 char *toolSource; // Argument of --pack, --unpack, or --patch ToolModeE toolMode; bool dataDirGiven; // -d was on the command line diff --git a/src/vfs.c b/src/vfs.c index 679207356..157b4e9d6 100644 --- a/src/vfs.c +++ b/src/vfs.c @@ -364,7 +364,8 @@ static bool _resolve(const char *name, TargetT *target) { } // A path that passes through a database file addresses its contents: "Games/DLe.game/Overlay/x.png". - if (!_isAbsolute(norm)) { + // Absolute paths included, so a tool can reach the packed games beside it from inside its own. + { for (i = extLen; norm[i] != 0; i++) { if ((norm[i] == '/') && (strncasecmp(norm + i - extLen, VFS_DATABASE_EXTENSION, extLen) == 0)) { prefix = utilStrndup(norm, i);