Started rework of build system. New logo!

This commit is contained in:
Scott Duensing 2026-09-04 16:47:56 -05:00
parent d533beb787
commit 718357867b
20 changed files with 476 additions and 271 deletions

1
.gitattributes vendored
View file

@ -3,6 +3,7 @@
# #
*.xcf filter=lfs diff=lfs merge=lfs -text *.xcf filter=lfs diff=lfs merge=lfs -text
*.jpg 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 *.png filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text
*.bmp filter=lfs diff=lfs merge=lfs -text *.bmp filter=lfs diff=lfs merge=lfs -text

View file

@ -20,7 +20,8 @@ API Changes
are matched without regard to case and a leading own-directory prefix are matched without regard to case and a leading own-directory prefix
("DLe/Cfg/x") is ignored. singe --unpack GAME.game DIRECTORY restores ("DLe/Cfg/x") is ignored. singe --unpack GAME.game DIRECTORY restores
the files and singe --patch GAME.game SOURCE replaces files from a 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 untouched by all of this. Scripts get SQLite for their own data with
require("sqlite3"). The archive installer (.game, .tool, and .patch require("sqlite3"). The archive installer (.game, .tool, and .patch
archives dropped beside the binary) is gone along with libarchive; a archives dropped beside the binary) is gone along with libarchive; a

View file

@ -36,11 +36,19 @@ if(NOT DEFINED KANGAROO_ARCH)
set(KANGAROO_ARCH x86_64) set(KANGAROO_ARCH x86_64)
endif() 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(BUILD_DIR ${CMAKE_SOURCE_DIR}/.builddir/${KANGAROO_OS}/${KANGAROO_ARCH})
set(GENERATED_DIR ${CMAKE_BINARY_DIR}/generated) set(GENERATED_DIR ${CMAKE_BINARY_DIR}/generated)
file(MAKE_DIRECTORY ${GENERATED_DIR}) 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 ===== # ===== Host tools needed to generate embedded resources =====
@ -122,15 +130,32 @@ endfunction()
# Images # Images
singeEmbedImage(font) 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(kangarooPunchLogo)
singeEmbedImage(singeLogo) singeEmbedImage(singeLogo)
# Windows icon for the resource file. # Windows icon for the resource file.
add_custom_command( add_custom_command(
OUTPUT ${GENERATED_DIR}/icon.ico OUTPUT ${GENERATED_DIR}/icon.ico
COMMAND ${IMAGEMAGICK} ${GENERATED_DIR}/icon.png -define icon:auto-resize=64,48,32,16 ${GENERATED_DIR}/icon.ico COMMAND ${IMAGEMAGICK} ${GENERATED_DIR}/iconSquare.png -define icon:auto-resize=256,64,48,32,16 ${GENERATED_DIR}/icon.ico
DEPENDS ${GENERATED_DIR}/icon.png DEPENDS ${GENERATED_DIR}/iconSquare.png
COMMENT "Rendering icon.ico" COMMENT "Rendering icon.ico"
VERBATIM VERBATIM
) )
@ -466,7 +491,7 @@ else()
message(FATAL_ERROR "Unknown KANGAROO_OS: ${KANGAROO_OS}") message(FATAL_ERROR "Unknown KANGAROO_OS: ${KANGAROO_OS}")
endif() 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. # 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") if(KANGAROO_OS STREQUAL "linux" OR KANGAROO_OS STREQUAL "pi")
foreach(pc libavutil libavcodec) foreach(pc libavutil libavcodec)

85
CMakePresets.json Normal file
View file

@ -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"
}
]
}

19
INSTALL
View file

@ -43,3 +43,22 @@ COMMAND LINE
Run the binary with -h (or --help) for the option summary. Every option is 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 described in the "Command Line Options" chapter of Singe/Manual.pdf, which
is extracted on first run. 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/<os>/<arch> 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<version>-<Os>-<arch>.
Presets are linux-x86_64, pi-aarch64, macos-aarch64 and windows-x86_64
(CMakePresets.json).

BIN
assets/BreatheFire-65pg.ttf (Stored with Git LFS)

Binary file not shown.

BIN
assets/Dragon.jpeg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/SingeText.jpeg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/icon.xcf (Stored with Git LFS)

Binary file not shown.

BIN
assets/indexing.xcf (Stored with Git LFS)

Binary file not shown.

BIN
assets/laserDisc.xcf (Stored with Git LFS)

Binary file not shown.

BIN
assets/magnifyingGlass.xcf (Stored with Git LFS)

Binary file not shown.

BIN
assets/singeLogo.xcf (Stored with Git LFS)

Binary file not shown.

View file

@ -20,245 +20,35 @@
# 02110-1301, USA. # 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_BUILDDIR=.builddir
G_TARGET=
G_GENERATED=
function buildAll() { function buildAll() {
local OS=$1 local OS=$1
local ARCH=$2 local ARCH=$2
local COMMON= shift 2
local OPT1=
# Activate toolchain for this platform. # Activate the toolchain for this platform (PATH, compilers, sysroot, osxcross variables).
source <(../toolchains/toolchains.sh use ${ARCH} ${OS}) source <(../toolchains/toolchains.sh use ${ARCH} ${OS})
G_TARGET=${G_BUILDROOT}/${G_BUILDDIR}/${OS}/${ARCH} # The Pi sysroot needs the development packages the build links against.
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.
if [[ "${OS}" == "pi" ]]; then 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 sudo chroot ${SYSROOT} apt-get -y install libasound-dev libxi-dev libvdpau-dev libdrm-dev
fi fi
pushd thirdparty/zlib cmake --preset ${OS}-${ARCH}
clearAndEnterBuild cmake --build --preset ${OS}-${ARCH} "$@"
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<version>-<Os>-<arch>.
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}
} }
@ -266,7 +56,6 @@ function clearAndEnterBuild() {
# -u = stop script on undefined variable # -u = stop script on undefined variable
# -o pipefail = stop pipeline if any step fails # -o pipefail = stop pipeline if any step fails
set -euo pipefail set -euo pipefail
export MAKEFLAGS="${MAKEFLAGS:--j$(nproc)}"
if [[ ! -x ../toolchains/toolchains.sh ]]; then if [[ ! -x ../toolchains/toolchains.sh ]]; then
echo "error: ../toolchains/toolchains.sh not found (see INSTALL)" >&2 echo "error: ../toolchains/toolchains.sh not found (see INSTALL)" >&2
@ -294,20 +83,11 @@ sudo apt-get install -y \
libdrm-dev libdrm-dev
# Usage: build-all.sh [os arch] (default: every supported platform) if [[ $# -ge 2 ]]; then
if [[ $# -eq 2 ]]; then buildAll "$@" 2>&1 | tee ${G_BUILDDIR}/$1-$2.log
buildAll "$1" "$2" 2>&1 | tee ${G_BUILDDIR}/$1-$2.log
else else
buildAll linux x86_64 2>&1 | tee ${G_BUILDDIR}/linux-x86_64.log 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 macos aarch64 2>&1 | tee ${G_BUILDDIR}/macos-aarch64.log
buildAll pi aarch64 2>&1 | tee ${G_BUILDDIR}/pi-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 buildAll windows x86_64 2>&1 | tee ${G_BUILDDIR}/windows-x86_64.log
fi 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

267
cmake/Superbuild.cmake Normal file
View file

@ -0,0 +1,267 @@
#
# Singe 2
# Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
#
# 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-<name> 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}")

View file

@ -108,7 +108,7 @@ Singe [OPTIONS] scriptName{.singe}
The script name is the only required argument. It may be a `.singe` file, a 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 directory containing a script of the same name (`ActionMax` finds
`ActionMax/ActionMax.singe`), or a packed game (`DLe.game` runs the first `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 `--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. 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. | `-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`, `--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. | `-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 <<singlefile,Single-File Games>>.
| `-e`, `--volume_nonvldp=PERCENT` | Sound effect and extra video volume, `0` to `100`. | `-e`, `--volume_nonvldp=PERCENT` | Sound effect and extra video volume, `0` to `100`.
| `-f`, `--fullscreen` | Exclusive full screen at the desktop resolution. | `-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. | `-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 --pack DLe DLe.game Pack the DLe directory into DLe.game
Singe DLe.game Run the first games.dat entry in it 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 --unpack DLe.game DLe Write the files back out
Singe --patch DLe.game fixes Replace files from a directory (or a patch database) Singe --patch DLe.game fixes Replace files from a directory (or a patch database)
---- ----

View file

@ -118,6 +118,7 @@ static const OptionT _options[] = {
{ 'C', "canvas", ap_yes, "WxH", "world size for games without a disc (default 720x480)", false }, { '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', "disc", ap_no, NULL, "play a laserdisc video (implied by --framefile)", false },
{ 'd', "datadir", ap_yes, "PATHNAME", "alternate location for written files", 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 }, { 'e', "volume_nonvldp", ap_yes, "PERCENT", "specify sound effects volume in percent", false },
{ 'f', "fullscreen", ap_no, NULL, "run in full screen mode", false }, { 'f', "fullscreen", ap_no, NULL, "run in full screen mode", false },
{ 'g', "sindengun", ap_yes, "'PARAMS'", "enable Sinden Light Gun support", true }, { '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->resolutionWasCalculated = true;
conf->canvasWidth = CANVAS_DEFAULT_WIDTH; conf->canvasWidth = CANVAS_DEFAULT_WIDTH;
conf->canvasHeight = CANVAS_DEFAULT_HEIGHT; conf->canvasHeight = CANVAS_DEFAULT_HEIGHT;
conf->entry = 1;
// Parse command line // Parse command line
for (argIndex = 0; argIndex < ap_arguments(&parser); argIndex++) { 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); sindenString = strdup(arg);
break; 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 // Software video decoding
case 'H': case 'H':
conf->softwareVideo = true; conf->softwareVideo = true;

View file

@ -111,6 +111,9 @@ LSEC_API int luaopen_ssl_config(lua_State *L);
#define BLUE_SCREEN_BLUE 255 #define BLUE_SCREEN_BLUE 255
#define LOGO_FADE_STEPS 256 #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_FADE_STEP_MS 3
#define LOGO_HOLD_MS 750 #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 void _drawPauseIndicator(const SDL_FRect *target);
static InputE _engineSwitch(int32_t scancode); 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 _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 _fontDestroy(FontT *font);
static void _freezeGame(bool freeze); static void _freezeGame(bool freeze);
static void _heldListUpdate(HeldKeyT *list, int32_t *count, bool down, int32_t keysym, int32_t scancode); 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_Surface *surfSinge = NULL;
SDL_Texture *texKangaroo = NULL; SDL_Texture *texKangaroo = NULL;
SDL_Texture *texSinge = NULL; SDL_Texture *texSinge = NULL;
SDL_FRect rectKangaroo;
SDL_FRect rectSinge;
SDL_RendererLogicalPresentation mode = SDL_LOGICAL_PRESENTATION_DISABLED; SDL_RendererLogicalPresentation mode = SDL_LOGICAL_PRESENTATION_DISABLED;
SDL_GetRenderLogicalPresentation(_global.renderer, &w, &h, &mode); SDL_GetRenderLogicalPresentation(_global.renderer, &w, &h, &mode);
texKangaroo = _loadEmbeddedTexture(kangarooPunchLogo_png, kangarooPunchLogo_png_len, &surfKangaroo); texKangaroo = _loadEmbeddedTexture(kangarooPunchLogo_png, kangarooPunchLogo_png_len, &surfKangaroo);
texSinge = _loadEmbeddedTexture(singeLogo_png, singeLogo_png_len, &surfSinge); 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 // 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++) { 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_SetRenderDrawColor(_global.renderer, (uint8_t)i, (uint8_t)i, (uint8_t)i, SDL_ALPHA_OPAQUE);
SDL_RenderClear(_global.renderer); SDL_RenderClear(_global.renderer);
SDL_SetTextureAlphaMod(texKangaroo, (uint8_t)i); SDL_SetTextureAlphaMod(texKangaroo, (uint8_t)i);
SDL_RenderTexture(_global.renderer, texKangaroo, NULL, NULL); SDL_RenderTexture(_global.renderer, texKangaroo, NULL, &rectKangaroo);
SDL_RenderPresent(_global.renderer); SDL_RenderPresent(_global.renderer);
keepGoing = _delayAndPump(LOGO_FADE_STEP_MS); keepGoing = _delayAndPump(LOGO_FADE_STEP_MS);
} }
@ -1071,21 +1081,20 @@ static void _doLogos(void) {
for (i = 0; keepGoing && (i < LOGO_FADE_STEPS); i++) { for (i = 0; keepGoing && (i < LOGO_FADE_STEPS); i++) {
SDL_RenderClear(_global.renderer); SDL_RenderClear(_global.renderer);
SDL_SetTextureAlphaMod(texKangaroo, (uint8_t)(LOGO_FADE_STEPS - 1 - i)); 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_SetTextureAlphaMod(texSinge, (uint8_t)i);
SDL_RenderTexture(_global.renderer, texSinge, NULL, NULL); SDL_RenderTexture(_global.renderer, texSinge, NULL, &rectSinge);
SDL_RenderPresent(_global.renderer); SDL_RenderPresent(_global.renderer);
keepGoing = _delayAndPump(LOGO_FADE_STEP_MS); keepGoing = _delayAndPump(LOGO_FADE_STEP_MS);
} }
keepGoing = keepGoing && _delayAndPump(LOGO_HOLD_MS); keepGoing = keepGoing && _delayAndPump(LOGO_HOLD_MS);
// Fade to black // 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--) { 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_SetRenderDrawColor(_global.renderer, (uint8_t)i, (uint8_t)i, (uint8_t)i, SDL_ALPHA_OPAQUE);
SDL_RenderClear(_global.renderer); SDL_RenderClear(_global.renderer);
SDL_SetTextureAlphaMod(texSinge, (uint8_t)i); SDL_SetTextureAlphaMod(texSinge, (uint8_t)i);
SDL_RenderTexture(_global.renderer, texSinge, NULL, NULL); SDL_RenderTexture(_global.renderer, texSinge, NULL, &rectSinge);
SDL_RenderPresent(_global.renderer); SDL_RenderPresent(_global.renderer);
keepGoing = _delayAndPump(LOGO_FADE_STEP_MS); 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) { static void _fontDestroy(FontT *font) {
if (_global.fontCurrent == font) { if (_global.fontCurrent == font) {
_global.fontCurrent = NULL; _global.fontCurrent = NULL;
@ -4422,7 +4442,7 @@ static int32_t apiVldpSetVerbose(lua_State *L) {
// ===== Engine entry point ===== // ===== 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) { ConfigT *confFromDatabase(const ConfigT *conf) {
lua_State *L = luaL_newstate(); lua_State *L = luaL_newstate();
ConfigT *base = cloneConf(conf); ConfigT *base = cloneConf(conf);
@ -4437,14 +4457,14 @@ ConfigT *confFromDatabase(const ConfigT *conf) {
utilDie("%s: %s", base->container, lua_tostring(L, -1)); utilDie("%s: %s", base->container, lua_tostring(L, -1));
} }
lua_getglobal(L, "GAMES"); lua_getglobal(L, "GAMES");
if (!lua_istable(L, -1) || (lua_rawgeti(L, -1, 1) != LUA_TTABLE)) { if (!lua_istable(L, -1) || (lua_rawgeti(L, -1, conf->entry) != LUA_TTABLE)) {
utilDie("%s: games.dat defines no GAMES entries.", base->container); utilDie("%s: games.dat has no entry %d.", base->container, conf->entry);
} }
lua_replace(L, 1); lua_replace(L, 1);
lua_settop(L, 1); lua_settop(L, 1);
result = _buildConfFromTable(L, base); result = _buildConfFromTable(L, base);
if (result->scriptFile == NULL) { 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); free(result->container);
result->container = strdup(base->container); result->container = strdup(base->container);

View file

@ -55,6 +55,7 @@ typedef struct ConfigS {
char *videoFile; char *videoFile;
char *scriptFile; char *scriptFile;
char *container; // Game database holding the script, NULL for a loose game 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 char *toolSource; // Argument of --pack, --unpack, or --patch
ToolModeE toolMode; ToolModeE toolMode;
bool dataDirGiven; // -d was on the command line bool dataDirGiven; // -d was on the command line

View file

@ -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". // 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++) { for (i = extLen; norm[i] != 0; i++) {
if ((norm[i] == '/') && (strncasecmp(norm + i - extLen, VFS_DATABASE_EXTENSION, extLen) == 0)) { if ((norm[i] == '/') && (strncasecmp(norm + i - extLen, VFS_DATABASE_EXTENSION, extLen) == 0)) {
prefix = utilStrndup(norm, i); prefix = utilStrndup(norm, i);