Compare commits
2 commits
718357867b
...
141b388b92
| Author | SHA1 | Date | |
|---|---|---|---|
| 141b388b92 | |||
| ec10eb3fe9 |
38 changed files with 786 additions and 99 deletions
|
|
@ -7,6 +7,14 @@ SINGE 3.00
|
|||
API Changes
|
||||
-----------
|
||||
|
||||
- The name now expands to "SINGE Is Not a Game Emulator", in the banner,
|
||||
the manual, INSTALL, and the Windows file description.
|
||||
|
||||
- lfs.dir, lfs.attributes and lfs.symlinkattributes see inside a packed
|
||||
game (the union of its loose directory, its data overlay and the
|
||||
database), and lfs.mkdir and lfs.rmdir act on its data overlay, so
|
||||
frameworks that list or create their own directories work packed.
|
||||
|
||||
- A game can be one file: singe --pack DIRECTORY GAME.game writes the game's
|
||||
scripts, art, sounds, fonts, and video into an SQLite database, and
|
||||
singe GAME.game (or the menu, which lists every .game file beside the game
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
# Singe 2
|
||||
# Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
# Singe 3
|
||||
# Copyright (C) 2006-2026 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
|
||||
|
|
@ -108,15 +108,34 @@ function(singeEmbed input output prefix)
|
|||
set(EMBEDDED_HEADERS ${EMBEDDED_HEADERS} ${output} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Renders a layered image to a flattened PNG and embeds it.
|
||||
function(singeEmbedImage name)
|
||||
# The canvas size of a GIMP image, from the XCF header (ImageMagick only sees the layers, and its
|
||||
# flatten crops to the first one).
|
||||
function(singeXcfCanvas xcf widthVar heightVar)
|
||||
file(READ ${xcf} header LIMIT 22 HEX)
|
||||
string(SUBSTRING ${header} 28 8 widthHex)
|
||||
string(SUBSTRING ${header} 36 8 heightHex)
|
||||
math(EXPR width "0x${widthHex}")
|
||||
math(EXPR height "0x${heightHex}")
|
||||
set(${widthVar} ${width} PARENT_SCOPE)
|
||||
set(${heightVar} ${height} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Renders assets/<source>.xcf as <name>.png the way GIMP would: every layer at its offset onto the
|
||||
# image's own canvas, anything outside it clipped.
|
||||
function(singeRenderImage name source)
|
||||
singeXcfCanvas(${CMAKE_SOURCE_DIR}/assets/${source}.xcf width height)
|
||||
add_custom_command(
|
||||
OUTPUT ${GENERATED_DIR}/${name}.png
|
||||
COMMAND ${IMAGEMAGICK} ${CMAKE_SOURCE_DIR}/assets/${name}.xcf -background "rgba(0,0,0,0)" -flatten ${GENERATED_DIR}/${name}.png
|
||||
DEPENDS assets/${name}.xcf
|
||||
COMMENT "Rendering ${name}.png"
|
||||
COMMAND ${IMAGEMAGICK} -background "rgba(0,0,0,0)" -size ${width}x${height} xc:none ${CMAKE_SOURCE_DIR}/assets/${source}.xcf -flatten ${GENERATED_DIR}/${name}.png
|
||||
DEPENDS assets/${source}.xcf
|
||||
COMMENT "Rendering ${name}.png (${width}x${height})"
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# Renders a layered image and embeds the PNG.
|
||||
function(singeEmbedImage name)
|
||||
singeRenderImage(${name} ${name})
|
||||
singeEmbed(${GENERATED_DIR}/${name}.png ${GENERATED_DIR}/${name}.h "")
|
||||
set(EMBEDDED_HEADERS ${EMBEDDED_HEADERS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
|
@ -133,10 +152,11 @@ singeEmbedImage(font)
|
|||
# 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)
|
||||
singeRenderImage(iconFull icon)
|
||||
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
|
||||
COMMAND ${IMAGEMAGICK} ${GENERATED_DIR}/iconFull.png -background "rgba(0,0,0,0)" -resize "${ICON_SIZE}x${ICON_SIZE}>" -gravity center -extent ${ICON_SIZE}x${ICON_SIZE} ${GENERATED_DIR}/iconSquare.png
|
||||
DEPENDS ${GENERATED_DIR}/iconFull.png
|
||||
COMMENT "Rendering iconSquare.png"
|
||||
VERBATIM
|
||||
)
|
||||
|
|
@ -385,8 +405,23 @@ add_executable(${CMAKE_PROJECT_NAME}
|
|||
${MANYMOUSE_SOURCE}
|
||||
)
|
||||
if(WIN32)
|
||||
enable_language(RC)
|
||||
target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${GENERATED_DIR}/singe.rc ${GENERATED_DIR}/icon.ico)
|
||||
if(SINGE_ZIG_TARGET)
|
||||
# CMake archives every object into objects.a for MinGW links, and zig's linker cannot take a
|
||||
# compiled resource from inside an archive; compiled on its own it links as a plain input.
|
||||
add_custom_command(
|
||||
OUTPUT ${GENERATED_DIR}/singe.res
|
||||
COMMAND ${CMAKE_RC_COMPILER} /fo ${GENERATED_DIR}/singe.res ${GENERATED_DIR}/singe.rc
|
||||
DEPENDS ${GENERATED_DIR}/singe.rc ${GENERATED_DIR}/icon.ico
|
||||
COMMENT "Compiling resources"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(singeResources DEPENDS ${GENERATED_DIR}/singe.res)
|
||||
add_dependencies(${CMAKE_PROJECT_NAME} singeResources)
|
||||
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE ${GENERATED_DIR}/singe.res)
|
||||
else()
|
||||
enable_language(RC)
|
||||
target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${GENERATED_DIR}/singe.rc ${GENERATED_DIR}/icon.ico)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Output name matches the release artifact: Singe-v3.00-Linux-x86_64
|
||||
|
|
@ -397,6 +432,8 @@ set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME "Singe-v${PRO
|
|||
|
||||
# Warnings apply to our code only; the vendored libraries are not ours to fix.
|
||||
set_source_files_properties(${SINGE_SOURCE} PROPERTIES COMPILE_OPTIONS "-Wall;-Wextra")
|
||||
# librs232 stamps its build date into the module; clang under zig makes that an error by default.
|
||||
set_source_files_properties(thirdparty/librs232/bindings/lua/luars232.c PROPERTIES COMPILE_OPTIONS "-Wno-date-time")
|
||||
|
||||
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
RS232_STATIC
|
||||
|
|
@ -464,8 +501,14 @@ elseif(KANGAROO_OS STREQUAL "macos")
|
|||
message(FATAL_ERROR "OSXCROSS_TARGET_DIR must be set for macOS builds.")
|
||||
endif()
|
||||
elseif(KANGAROO_OS STREQUAL "windows")
|
||||
# A GUI-subsystem executable, so no console window opens; zig's linker wants the flag spelled out.
|
||||
if(SINGE_ZIG_TARGET)
|
||||
set(windowsSubsystem -Wl,--subsystem,windows)
|
||||
else()
|
||||
set(windowsSubsystem -mwindows)
|
||||
endif()
|
||||
set(SYSTEM_LIBS
|
||||
-mwindows
|
||||
${windowsSubsystem}
|
||||
-static
|
||||
-lmingw32
|
||||
-latomic
|
||||
|
|
@ -484,9 +527,12 @@ elseif(KANGAROO_OS STREQUAL "windows")
|
|||
-ldinput8
|
||||
-lws2_32
|
||||
-lbcrypt
|
||||
-lssp
|
||||
-lcrypt32
|
||||
)
|
||||
if(NOT SINGE_ZIG_TARGET)
|
||||
# MinGW's stack protector runtime; zig's compiler-rt carries it itself.
|
||||
list(APPEND SYSTEM_LIBS -lssp)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown KANGAROO_OS: ${KANGAROO_OS}")
|
||||
endif()
|
||||
|
|
@ -542,7 +588,7 @@ set(STATIC_LIBS
|
|||
if(NOT KANGAROO_OS STREQUAL "macos")
|
||||
list(APPEND STATIC_LIBS
|
||||
${BUILD_DIR}/lib/libjpeg.a
|
||||
${BUILD_DIR}/lib/libpng.a
|
||||
${BUILD_DIR}/lib/libpng16.a
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
"displayName": "Linux x86_64",
|
||||
"inherits": "base",
|
||||
"binaryDir": "${sourceDir}/.builddir/linux/x86_64/superbuild",
|
||||
"toolchainFile": "${sourceDir}/../toolchains/cmake/x86_64-linux-gnu.cmake",
|
||||
"toolchainFile": "${sourceDir}/cmake/linux-x86_64.cmake",
|
||||
"cacheVariables": {
|
||||
"SINGE_TRIPLE": "x86_64-linux-gnu",
|
||||
"SINGE_CROSS_OS": "linux",
|
||||
|
|
@ -53,10 +53,10 @@
|
|||
},
|
||||
{
|
||||
"name": "windows-x86_64",
|
||||
"displayName": "Windows x86_64",
|
||||
"displayName": "Windows x86_64 (zig)",
|
||||
"inherits": "base",
|
||||
"binaryDir": "${sourceDir}/.builddir/windows/x86_64/superbuild",
|
||||
"toolchainFile": "${sourceDir}/../toolchains/cmake/x86_64-w64-mingw32.cmake",
|
||||
"toolchainFile": "${sourceDir}/cmake/zig/x86_64-windows-gnu.cmake",
|
||||
"cacheVariables": {
|
||||
"SINGE_TRIPLE": "x86_64-w64-mingw32",
|
||||
"SINGE_CROSS_OS": "mingw32",
|
||||
|
|
|
|||
45
INSTALL
45
INSTALL
|
|
@ -4,7 +4,7 @@ SINGE 3.00
|
|||
(For the latest version of this document, visit https://kangaroopunch.com!
|
||||
The full manual is extracted to Singe/Manual.pdf on first run.)
|
||||
|
||||
Welcome to Singe! The Somewhat Interactive Nostalgic Game Engine!
|
||||
Welcome to Singe! SINGE Is Not a Game Emulator!
|
||||
|
||||
|
||||
INSTALLATION & UPGRADE
|
||||
|
|
@ -48,17 +48,40 @@ 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:
|
||||
On a Debian based system a checkout builds with one command:
|
||||
|
||||
./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
|
||||
|
||||
It installs the host packages it needs with apt (asks for your sudo
|
||||
password once), then runs the CMake superbuild, which builds every vendored
|
||||
library (zlib, zstd, SDL3 and its satellites, OpenSSL, FFmpeg) into
|
||||
.builddir/<os>/<arch> and then Singe against them. The finished binary is
|
||||
copied to .builddir/Singe-v<version>-<Os>-<arch>.
|
||||
|
||||
Host packages (what build-all.sh installs): build-essential cmake
|
||||
pkg-config perl nasm imagemagick lua5.4 ffmpeg asciidoctor
|
||||
ruby-asciidoctor-pdf autoconf automake libtool libasound-dev libxi-dev
|
||||
libvdpau-dev libva-dev libdrm-dev libgl-dev libx11-dev.
|
||||
|
||||
Cross builds need nothing else installed:
|
||||
|
||||
./build-all.sh windows x86_64
|
||||
|
||||
fetches a pinned zig release (version and SHA-256 in cmake/zig/zig.cmake)
|
||||
into .builddir/toolchains on first use and uses it as the cross compiler;
|
||||
zig carries the Windows headers and import libraries itself. The Pi and
|
||||
macOS presets are not yet self-contained; they still expect the toolchains
|
||||
repository beside this one (see PLAN.md section 17 for the plan to move
|
||||
them to zig too).
|
||||
|
||||
Useful forms (anything after the platform goes to cmake --build):
|
||||
|
||||
./build-all.sh linux x86_64 --target singe Singe alone
|
||||
./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
|
||||
Without the wrapper:
|
||||
|
||||
cmake --preset linux-x86_64 && cmake --build --preset linux-x86_64
|
||||
|
||||
Presets: linux-x86_64, windows-x86_64, pi-aarch64, macos-aarch64
|
||||
(CMakePresets.json).
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
--[[
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
--[[
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
--[[
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
--[[
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
41
build-all.sh
41
build-all.sh
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Singe 2
|
||||
# Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
# Singe 3
|
||||
# Copyright (C) 2006-2026 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
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
# 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:
|
||||
# CMakePresets.json); this script installs the host packages and calls it:
|
||||
#
|
||||
# ./build-all.sh every supported platform
|
||||
# ./build-all.sh linux x86_64 one platform
|
||||
|
|
@ -39,12 +39,18 @@ function buildAll() {
|
|||
local ARCH=$2
|
||||
shift 2
|
||||
|
||||
# Activate the toolchain for this platform (PATH, compilers, sysroot, osxcross variables).
|
||||
source <(../toolchains/toolchains.sh use ${ARCH} ${OS})
|
||||
|
||||
# The Pi sysroot needs the development packages the build links against.
|
||||
if [[ "${OS}" == "pi" ]]; then
|
||||
sudo chroot ${SYSROOT} apt-get -y install libasound-dev libxi-dev libvdpau-dev libdrm-dev
|
||||
# Linux builds with the host compiler and Windows with the zig the build fetches itself. The Pi
|
||||
# and macOS presets still lean on the toolchains repository beside this one until they move to
|
||||
# zig as well (PLAN.md section 17).
|
||||
if [[ "${OS}" == "pi" || "${OS}" == "macos" ]]; then
|
||||
if [[ ! -x ../toolchains/toolchains.sh ]]; then
|
||||
echo "error: ${OS} still needs ../toolchains/toolchains.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
source <(../toolchains/toolchains.sh use ${ARCH} ${OS})
|
||||
if [[ "${OS}" == "pi" ]]; then
|
||||
sudo chroot ${SYSROOT} apt-get -y install libasound-dev libxi-dev libvdpau-dev libdrm-dev
|
||||
fi
|
||||
fi
|
||||
|
||||
cmake --preset ${OS}-${ARCH}
|
||||
|
|
@ -57,17 +63,16 @@ function buildAll() {
|
|||
# -o pipefail = stop pipeline if any step fails
|
||||
set -euo pipefail
|
||||
|
||||
if [[ ! -x ../toolchains/toolchains.sh ]]; then
|
||||
echo "error: ../toolchains/toolchains.sh not found (see INSTALL)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
mkdir -p ${G_BUILDDIR}
|
||||
|
||||
|
||||
# These are required for the build.
|
||||
# Host packages the build needs on a Debian based system (the same list is in INSTALL).
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
pkg-config \
|
||||
perl \
|
||||
nasm \
|
||||
imagemagick \
|
||||
lua5.4 \
|
||||
ffmpeg \
|
||||
|
|
@ -80,7 +85,9 @@ sudo apt-get install -y \
|
|||
libxi-dev \
|
||||
libvdpau-dev \
|
||||
libva-dev \
|
||||
libdrm-dev
|
||||
libdrm-dev \
|
||||
libgl-dev \
|
||||
libx11-dev
|
||||
|
||||
|
||||
if [[ $# -ge 2 ]]; then
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
# Singe 2
|
||||
# Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
# Singe 3
|
||||
# Copyright (C) 2006-2026 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
|
||||
|
|
@ -53,7 +53,9 @@ 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})
|
||||
if(CMAKE_CXX_COMPILER)
|
||||
set(SB_CXX "${CMAKE_CXX_COMPILER}")
|
||||
elseif(DEFINED ENV{CXX})
|
||||
set(SB_CXX "$ENV{CXX}")
|
||||
else()
|
||||
string(REGEX REPLACE "gcc$" "g++" SB_CXX "${SB_CC}")
|
||||
|
|
@ -144,13 +146,13 @@ function(singeRebuildTarget name)
|
|||
endfunction()
|
||||
|
||||
|
||||
# ===== zlib (configure script; its own include directory, not the prefix, as before) =====
|
||||
# ===== zlib (configure script; the generated zconf.h lives in the build directory) =====
|
||||
|
||||
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
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env "CC=${SB_CC}" "AR=${CMAKE_AR}" "RANLIB=${SB_RANLIB}" "CFLAGS=-I${zlibBinary} ${SB_CFLAGS_BASE}" ${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
|
||||
|
|
@ -180,6 +182,12 @@ singeCmakeProject(SDL3_mixer ${SB_THIRDPARTY}/SDL3_mixer "SDL3"
|
|||
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}")
|
||||
# SDL3_ttf builds its vendored FreeType but does not install it, and Singe links it directly.
|
||||
ExternalProject_Add_Step(SDL3_ttf freetype
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${SB_PREFIX}/build/SDL3_ttf/external/freetype/libfreetype.a ${SB_PREFIX}/lib/libfreetype.a
|
||||
DEPENDEES install
|
||||
COMMENT "Installing libfreetype.a"
|
||||
)
|
||||
|
||||
|
||||
# ===== OpenSSL (its own Configure; the target name follows the platform) =====
|
||||
|
|
@ -205,6 +213,13 @@ elseif(KANGAROO_OS STREQUAL "macos")
|
|||
else()
|
||||
set(opensslTarget "")
|
||||
endif()
|
||||
# OpenSSL's generated assembly uses GNU as directives clang's assembler rejects; under zig the
|
||||
# portable C paths serve, which is all LuaSec's traffic needs.
|
||||
if(SINGE_ZIG_TARGET)
|
||||
set(opensslAsm no-asm)
|
||||
else()
|
||||
set(opensslAsm "")
|
||||
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)
|
||||
|
|
@ -212,7 +227,7 @@ 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
|
||||
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 ${opensslAsm} 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
|
||||
|
|
@ -235,11 +250,18 @@ elseif(KANGAROO_OS STREQUAL "windows")
|
|||
else()
|
||||
set(hwaccel "")
|
||||
endif()
|
||||
# With a classic cross toolchain every tool is <triple>-<tool>; with zig each is named outright and
|
||||
# the host's nm reads the target's objects (binutils understands PE and Mach-O).
|
||||
if(SINGE_ZIG_TARGET)
|
||||
set(ffmpegTools --enable-cross-compile --cc=${SB_CC} --cxx=${SB_CXX} --ar=${CMAKE_AR} --ranlib=${SB_RANLIB} --nm=nm --windres=${CMAKE_RC_COMPILER})
|
||||
else()
|
||||
set(ffmpegTools --cross-prefix=${SINGE_TRIPLE}- --cc=${SB_CC} --cxx=${SB_CXX} --ranlib=${SB_RANLIB})
|
||||
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}
|
||||
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} ${ffmpegTools}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,23 @@
|
|||
|
||||
#
|
||||
# Singe 3
|
||||
# Copyright (C) 2006-2026 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.
|
||||
#
|
||||
# Embeds a file as a C array. Invoked in script mode by CMakeLists.txt:
|
||||
# cmake -DINPUT=<file> -DOUTPUT=<header> -DGUARD=<macro> -DSYMBOL=<name> -P embed.cmake
|
||||
# The header defines the array when EMBED_HERE is defined and declares it extern otherwise.
|
||||
|
|
@ -14,7 +34,7 @@ string(REGEX REPLACE ",\n\t$" "" bytes "${bytes}")
|
|||
|
||||
file(WRITE "${OUTPUT}" "/*
|
||||
*
|
||||
* Singe 2
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-${COPYRIGHT_END_YEAR} Scott Duensing <scott@kangaroopunch.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
|
|
|||
26
cmake/linux-x86_64.cmake
Normal file
26
cmake/linux-x86_64.cmake
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# Singe 3
|
||||
# Copyright (C) 2006-2026 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.
|
||||
#
|
||||
|
||||
# Linux x86_64 built on a Linux x86_64 host with its own gcc and development packages.
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
set(KANGAROO_OS linux)
|
||||
set(KANGAROO_ARCH x86_64)
|
||||
|
|
@ -1,3 +1,23 @@
|
|||
|
||||
#
|
||||
# Singe 3
|
||||
# Copyright (C) 2006-2026 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.
|
||||
#
|
||||
# Runs a command and captures its standard output in a file. Invoked in script mode:
|
||||
# cmake -DOUTPUT=<file> -DCOMMAND=<program> -DARGS=<semicolon list> -P runToFile.cmake
|
||||
|
||||
|
|
|
|||
34
cmake/zig/x86_64-windows-gnu.cmake
Normal file
34
cmake/zig/x86_64-windows-gnu.cmake
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#
|
||||
# Singe 3
|
||||
# Copyright (C) 2006-2026 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.
|
||||
#
|
||||
|
||||
# Windows x86_64 with zig as the cross compiler: zig ships the mingw-w64 headers and import
|
||||
# libraries, so nothing else has to be installed.
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/zig.cmake)
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
singeZigToolchain(x86_64-windows-gnu)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
set(KANGAROO_OS windows)
|
||||
set(KANGAROO_ARCH x86_64)
|
||||
69
cmake/zig/zig.cmake
Normal file
69
cmake/zig/zig.cmake
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#
|
||||
# Singe 3
|
||||
# Copyright (C) 2006-2026 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.
|
||||
#
|
||||
|
||||
# The one place the zig version lives. Toolchain files include this and name a target; the
|
||||
# compiler, archiver and resource compiler are small wrapper scripts so every tool that wants a
|
||||
# single program name (FFmpeg's configure, OpenSSL's, CMake itself) gets one.
|
||||
|
||||
# Pinned release, fetched into .builddir/toolchains on first use and checked against its hash.
|
||||
set(ZIG_VERSION 0.15.2)
|
||||
set(ZIG_SHA256 02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239)
|
||||
set(ZIG_NAME zig-x86_64-linux-${ZIG_VERSION})
|
||||
set(ZIG_URL https://ziglang.org/download/${ZIG_VERSION}/${ZIG_NAME}.tar.xz)
|
||||
get_filename_component(ZIG_TOOLCHAINS ${CMAKE_CURRENT_LIST_DIR}/../../.builddir/toolchains ABSOLUTE)
|
||||
set(ZIG_ROOT ${ZIG_TOOLCHAINS}/${ZIG_NAME})
|
||||
set(ZIG_EXECUTABLE ${ZIG_ROOT}/zig)
|
||||
if(NOT EXISTS ${ZIG_EXECUTABLE})
|
||||
message(STATUS "Downloading zig ${ZIG_VERSION} from ${ZIG_URL}")
|
||||
file(MAKE_DIRECTORY ${ZIG_TOOLCHAINS})
|
||||
file(DOWNLOAD ${ZIG_URL} ${ZIG_TOOLCHAINS}/${ZIG_NAME}.tar.xz EXPECTED_HASH SHA256=${ZIG_SHA256} STATUS status)
|
||||
list(GET status 0 code)
|
||||
if(NOT code EQUAL 0)
|
||||
message(FATAL_ERROR "zig download failed: ${status}")
|
||||
endif()
|
||||
file(ARCHIVE_EXTRACT INPUT ${ZIG_TOOLCHAINS}/${ZIG_NAME}.tar.xz DESTINATION ${ZIG_TOOLCHAINS})
|
||||
file(REMOVE ${ZIG_TOOLCHAINS}/${ZIG_NAME}.tar.xz)
|
||||
if(NOT EXISTS ${ZIG_EXECUTABLE})
|
||||
message(FATAL_ERROR "zig ${ZIG_VERSION} did not unpack to ${ZIG_ROOT}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Writes the wrapper scripts for a target into the build tree and points CMake at them.
|
||||
function(singeZigToolchain target)
|
||||
set(dir ${CMAKE_BINARY_DIR}/zig-${target})
|
||||
file(MAKE_DIRECTORY ${dir})
|
||||
# The compiler wrappers drop one flag: FFmpeg's configure adds binutils' --pic-executable ASLR
|
||||
# workaround to every test link on Windows, and zig's linker driver refuses it. They also add
|
||||
set(filter "for a in \"$@\"; do [[ $a == -Wl,--pic-executable* ]] || args+=(\"$a\"); done")
|
||||
# -fno-sanitize=undefined: zig cc instruments for UBSan by default and expects its runtime at
|
||||
# link time; a release build wants neither.
|
||||
foreach(tool "cc;-target ${target} -fno-sanitize=undefined" "c++;-target ${target} -fno-sanitize=undefined" "ar;" "ranlib;" "rc;")
|
||||
list(GET tool 0 name)
|
||||
list(GET tool 1 args)
|
||||
file(WRITE ${dir}/zig-${name} "#!/bin/bash\nargs=()\n${filter}\nexec \"${ZIG_EXECUTABLE}\" ${name} ${args} \"$\{args[@]}\"\n")
|
||||
file(CHMOD ${dir}/zig-${name} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
endforeach()
|
||||
set(CMAKE_C_COMPILER ${dir}/zig-cc CACHE FILEPATH "C compiler" FORCE)
|
||||
set(CMAKE_CXX_COMPILER ${dir}/zig-c++ CACHE FILEPATH "C++ compiler" FORCE)
|
||||
set(CMAKE_AR ${dir}/zig-ar CACHE FILEPATH "archiver" FORCE)
|
||||
set(CMAKE_RANLIB ${dir}/zig-ranlib CACHE FILEPATH "ranlib" FORCE)
|
||||
set(CMAKE_RC_COMPILER ${dir}/zig-rc CACHE FILEPATH "resource compiler" FORCE)
|
||||
set(SINGE_ZIG_TARGET ${target} CACHE STRING "zig target triple" FORCE)
|
||||
endfunction()
|
||||
|
|
@ -14,7 +14,7 @@ Scott Duensing <scott@kangaroopunch.com>
|
|||
[preface]
|
||||
== About Singe
|
||||
|
||||
Singe, the Somewhat Interactive Nostalgic Game Engine (named after the dragon in
|
||||
Singe (SINGE Is Not a Game Emulator, and named after the dragon in
|
||||
Dragon's Lair) is a Lua-based scripting system that allows for rapid prototyping
|
||||
of new laserdisc games, or the creation of entirely new games. The language is
|
||||
easy to learn, very powerful, and fast. All the features needed to develop your
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
@ -880,7 +880,7 @@ static void _showHeader(void) {
|
|||
// 00000000011111111112222222222333333333344444444445555555555666666666677777777778
|
||||
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
utilSay(" ___ ___ _ _ ___ ___");
|
||||
utilSay("/ __|_ _| \\| |/ __| __| Somewhat Interactive Nostalgic Game Engine %s", VERSION_STRING);
|
||||
utilSay("/ __|_ _| \\| |/ __| __| SINGE Is Not a Game Emulator %s", VERSION_STRING);
|
||||
utilSay("\\__ \\| || .` | (_ | _| Copyright (c) 2006-%s Scott C. Duensing", COPYRIGHT_END_YEAR);
|
||||
utilSay("|___/___|_|\\_|\\___|___| https://KangarooPunch.com https://SingeEngine.com");
|
||||
utilNewline();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
22
src/pack.c
22
src/pack.c
|
|
@ -1,3 +1,25 @@
|
|||
/*
|
||||
*
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Packer, unpacker, and patcher for single-file games. The database layout is described in vfs.c.
|
||||
*/
|
||||
|
|
|
|||
22
src/pack.h
22
src/pack.h
|
|
@ -1,3 +1,25 @@
|
|||
/*
|
||||
*
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Packing games into, and out of, single-file SQLite databases.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
175
src/singe.c
175
src/singe.c
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
@ -49,6 +49,8 @@ LUASOCKET_API int luaopen_socket_serial(lua_State *L);
|
|||
int luaopen_luars232(lua_State *L);
|
||||
// Nor for lsqlite3.
|
||||
int luaopen_lsqlite3(lua_State *L);
|
||||
// The module table below needs this before the prototypes.
|
||||
static int32_t _luaopenLfs(lua_State *L);
|
||||
// There is no header for ssl.config binding. Make our own.
|
||||
LSEC_API int luaopen_ssl_config(lua_State *L);
|
||||
|
||||
|
|
@ -347,7 +349,7 @@ static MIX_Track *_effectTracks[EFFECT_TRACKS];
|
|||
// Lua Modules
|
||||
static const LuaModuleT _luaModules[] = {
|
||||
// LuaFileSystem
|
||||
MODC("lfs", luaopen_lfs),
|
||||
MODC("lfs", _luaopenLfs),
|
||||
// SQLite for script data
|
||||
MODC("sqlite3", luaopen_lsqlite3),
|
||||
// LuaSocket
|
||||
|
|
@ -455,6 +457,11 @@ 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);
|
||||
static void _installFileHooks(lua_State *L);
|
||||
static int32_t _lfsAttributes(lua_State *L);
|
||||
static int32_t _lfsDir(lua_State *L);
|
||||
static int32_t _lfsDirIterator(lua_State *L);
|
||||
static int32_t _lfsMkdir(lua_State *L);
|
||||
static int32_t _lfsRmdir(lua_State *L);
|
||||
static int32_t _loadAudioCalibration(void);
|
||||
static void _loadControlsFile(const char *path);
|
||||
static SDL_Surface *_loadEmbeddedPng(const unsigned char *data, unsigned int length);
|
||||
|
|
@ -466,6 +473,7 @@ static char *_luaFormat(lua_State *L, const char *method, const char *fmt
|
|||
static int32_t _luaIoHook(lua_State *L);
|
||||
static int32_t _luaLoadfile(lua_State *L);
|
||||
static int32_t _luaLoadFile(lua_State *L, const char *name, const char *mode);
|
||||
static int32_t _luaopenLfs(lua_State *L);
|
||||
static int32_t _luaPanic(lua_State *L);
|
||||
static int32_t _luaSearcher(lua_State *L);
|
||||
static void _luaTrace(lua_State *L, const char *method, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
|
||||
|
|
@ -1323,6 +1331,144 @@ static void _installFileHooks(lua_State *L) {
|
|||
}
|
||||
|
||||
|
||||
// lfs.attributes through the vfs: a packed entry answers with mode, size and modification; a
|
||||
// plain filesystem path goes to the original (upvalue 1).
|
||||
static int32_t _lfsAttributes(lua_State *L) {
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
const char *attribute = luaL_optstring(L, 2, NULL);
|
||||
int64_t size = 0;
|
||||
int64_t modified = 0;
|
||||
bool directory = false;
|
||||
|
||||
if (vfsIsFilesystem(name)) {
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
|
||||
return lua_gettop(L);
|
||||
}
|
||||
directory = vfsIsDirectory(name);
|
||||
if (!directory && !vfsStat(name, &size, &modified)) {
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "cannot obtain information from file '%s'", name);
|
||||
return 2;
|
||||
}
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, directory ? "directory" : "file");
|
||||
lua_setfield(L, -2, "mode");
|
||||
lua_pushinteger(L, (lua_Integer)size);
|
||||
lua_setfield(L, -2, "size");
|
||||
lua_pushinteger(L, (lua_Integer)modified);
|
||||
lua_setfield(L, -2, "modification");
|
||||
lua_pushinteger(L, (lua_Integer)modified);
|
||||
lua_setfield(L, -2, "change");
|
||||
lua_pushinteger(L, (lua_Integer)modified);
|
||||
lua_setfield(L, -2, "access");
|
||||
if (attribute != NULL) {
|
||||
lua_getfield(L, -1, attribute);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// lfs.dir through the vfs: a packed directory iterates the union of loose, overlay and packed
|
||||
// entries; a plain filesystem path goes to the original (upvalue 1).
|
||||
static int32_t _lfsDir(lua_State *L) {
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
char **list = NULL;
|
||||
int32_t count = 0;
|
||||
int32_t i = 0;
|
||||
|
||||
if (vfsIsFilesystem(name)) {
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
|
||||
return lua_gettop(L);
|
||||
}
|
||||
if (!vfsIsDirectory(name)) {
|
||||
return luaL_error(L, "cannot open %s: No such file or directory", name);
|
||||
}
|
||||
list = vfsList(name, &count);
|
||||
lua_createtable(L, count, 0);
|
||||
for (i = 0; i < count; i++) {
|
||||
lua_pushstring(L, list[i]);
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
vfsListFree(list, count);
|
||||
lua_pushinteger(L, 0);
|
||||
lua_pushcclosure(L, _lfsDirIterator, 2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// The iterator lfs.dir returns for a packed directory: upvalues are the entry table and the index.
|
||||
static int32_t _lfsDirIterator(lua_State *L) {
|
||||
lua_Integer index = lua_tointeger(L, lua_upvalueindex(2)) + 1;
|
||||
|
||||
lua_pushinteger(L, index);
|
||||
lua_copy(L, -1, lua_upvalueindex(2));
|
||||
lua_pop(L, 1);
|
||||
lua_rawgeti(L, lua_upvalueindex(1), index);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// lfs.mkdir through the vfs: inside a packed game the directory is made where its files will be
|
||||
// written, the data overlay; a plain filesystem path goes to the original (upvalue 1).
|
||||
static int32_t _lfsMkdir(lua_State *L) {
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
char *path = NULL;
|
||||
bool ok = false;
|
||||
|
||||
if (vfsIsFilesystem(name)) {
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
|
||||
return lua_gettop(L);
|
||||
}
|
||||
path = vfsFilePath(name, true);
|
||||
ok = utilMkDirP(path, 0755);
|
||||
free(path);
|
||||
if (!ok) {
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "cannot create %s", name);
|
||||
return 2;
|
||||
}
|
||||
lua_pushboolean(L, true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// lfs.rmdir through the vfs: removes the overlay directory of a packed name; the packed copy
|
||||
// itself is read only and stays.
|
||||
static int32_t _lfsRmdir(lua_State *L) {
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
char *path = NULL;
|
||||
bool ok = false;
|
||||
|
||||
if (vfsIsFilesystem(name)) {
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
|
||||
return lua_gettop(L);
|
||||
}
|
||||
path = vfsFilePath(name, true);
|
||||
ok = (rmdir(path) == 0);
|
||||
free(path);
|
||||
if (!ok) {
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "cannot remove %s", name);
|
||||
return 2;
|
||||
}
|
||||
lua_pushboolean(L, true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// The per-machine audio delay lives beside the data directories, since it is not a property of any game.
|
||||
static int32_t _loadAudioCalibration(void) {
|
||||
char *path = utilCreateString("%s%s", _global.conf->dataDirBase, AUDIO_CALIBRATION_FILE);
|
||||
|
|
@ -1561,6 +1707,29 @@ static int32_t _luaLoadFile(lua_State *L, const char *name, const char *mode) {
|
|||
|
||||
return status;
|
||||
}
|
||||
// require("lfs") with dir and attributes routed through the vfs, so a packed game can list itself.
|
||||
static int32_t _luaopenLfs(lua_State *L) {
|
||||
luaopen_lfs(L);
|
||||
lua_getfield(L, -1, "dir");
|
||||
lua_pushcclosure(L, _lfsDir, 1);
|
||||
lua_setfield(L, -2, "dir");
|
||||
lua_getfield(L, -1, "attributes");
|
||||
lua_pushcclosure(L, _lfsAttributes, 1);
|
||||
lua_setfield(L, -2, "attributes");
|
||||
lua_getfield(L, -1, "symlinkattributes");
|
||||
lua_pushcclosure(L, _lfsAttributes, 1);
|
||||
lua_setfield(L, -2, "symlinkattributes");
|
||||
lua_getfield(L, -1, "mkdir");
|
||||
lua_pushcclosure(L, _lfsMkdir, 1);
|
||||
lua_setfield(L, -2, "mkdir");
|
||||
lua_getfield(L, -1, "rmdir");
|
||||
lua_pushcclosure(L, _lfsRmdir, 1);
|
||||
lua_setfield(L, -2, "rmdir");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int32_t _luaPanic(lua_State *L) {
|
||||
lua_Debug ar;
|
||||
int32_t level = 0;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ BEGIN
|
|||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Kangaroo Punch Studios"
|
||||
VALUE "FileDescription", "Somewhat Interactive Nostalgic Game Engine"
|
||||
VALUE "FileDescription", "SINGE Is Not a Game Emulator"
|
||||
VALUE "FileVersion", "@PROJECT_VERSION@"
|
||||
VALUE "InternalName", "Singe"
|
||||
VALUE "LegalCopyright", "Copyright 2006-@SINGE_COPYRIGHT_END_YEAR@ Scott C. Duensing"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-@SINGE_COPYRIGHT_END_YEAR@ Scott Duensing <scott@kangaroopunch.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
|
|
|||
175
src/vfs.c
175
src/vfs.c
|
|
@ -1,3 +1,25 @@
|
|||
/*
|
||||
*
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Singe virtual filesystem. See vfs.h for the lookup rules.
|
||||
*
|
||||
|
|
@ -11,6 +33,7 @@
|
|||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -49,6 +72,7 @@ typedef struct DatabaseS {
|
|||
sqlite3 *db;
|
||||
sqlite3_stmt *assetStmt; // size, data by path
|
||||
sqlite3_stmt *chunkStmt; // data by path and chunk number
|
||||
sqlite3_stmt *listStmt; // paths under a prefix, for directory listings
|
||||
struct DatabaseS *next;
|
||||
} DatabaseT;
|
||||
|
||||
|
|
@ -75,10 +99,13 @@ struct VfsStreamS {
|
|||
|
||||
|
||||
static bool _assetExists(DatabaseT *db, const char *key);
|
||||
static bool _assetDirectory(DatabaseT *db, const char *key);
|
||||
static bool _assetSize(DatabaseT *db, const char *key, int64_t *size);
|
||||
static DatabaseT *_databaseOpen(const char *path);
|
||||
static bool _databaseReadMeta(DatabaseT *db, const char *key, char **value);
|
||||
static bool _fileModified(const char *path, int64_t *size, int64_t *modified);
|
||||
static void _listAdd(char ***list, int32_t *count, const char *name);
|
||||
static void _listDirectory(const char *path, char ***list, int32_t *count);
|
||||
static bool _isAbsolute(const char *name);
|
||||
static bool _isEngineName(const char *name);
|
||||
static char *_normalise(const char *name);
|
||||
|
|
@ -103,6 +130,24 @@ static bool _assetExists(DatabaseT *db, const char *key) {
|
|||
}
|
||||
|
||||
|
||||
// True when any asset lives below the key, which is what a directory is in a database.
|
||||
static bool _assetDirectory(DatabaseT *db, const char *key) {
|
||||
char *from = utilCreateString("%s/", key);
|
||||
char *to = utilCreateString("%s0", key);
|
||||
bool found = false;
|
||||
|
||||
sqlite3_reset(db->listStmt);
|
||||
sqlite3_bind_text(db->listStmt, 1, from, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(db->listStmt, 2, to, -1, SQLITE_TRANSIENT);
|
||||
found = (sqlite3_step(db->listStmt) == SQLITE_ROW);
|
||||
sqlite3_reset(db->listStmt);
|
||||
free(from);
|
||||
free(to);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
static bool _assetSize(DatabaseT *db, const char *key, int64_t *size) {
|
||||
bool found = false;
|
||||
|
||||
|
|
@ -174,6 +219,10 @@ static DatabaseT *_databaseOpen(const char *path) {
|
|||
if (sqlite3_prepare_v2(db->db, "SELECT data FROM chunks WHERE path = ? AND chunk = ?", -1, &db->chunkStmt, NULL) != SQLITE_OK) {
|
||||
utilDie("%s has no chunks table: %s", path, sqlite3_errmsg(db->db));
|
||||
}
|
||||
// Everything below a directory key sorts between "key/" and "key0" ('0' follows '/').
|
||||
if (sqlite3_prepare_v2(db->db, "SELECT path FROM assets WHERE path >= ? AND path < ? ORDER BY path", -1, &db->listStmt, NULL) != SQLITE_OK) {
|
||||
utilDie("%s: %s", path, sqlite3_errmsg(db->db));
|
||||
}
|
||||
db->chunkBytes = VFS_CHUNK_BYTES;
|
||||
if (_databaseReadMeta(db, META_CHUNK, &value)) {
|
||||
db->chunkBytes = strtoll(value, NULL, 10);
|
||||
|
|
@ -239,9 +288,15 @@ static bool _isEngineName(const char *name) {
|
|||
if ((utilStricmp(name, ENGINE_DIRECTORY) == 0) || ((strncmp(name, ENGINE_DIRECTORY, length) == 0) && (name[length] == '/'))) {
|
||||
return true;
|
||||
}
|
||||
// The data directory base, when it is a real prefix: "data/" or an absolute path. With no
|
||||
// --datadir the base is "./", which normalises to nothing and must not match everything.
|
||||
if (_dataDirBase != NULL) {
|
||||
base = _normalise(_dataDirBase);
|
||||
engine = (strncmp(name, base, strlen(base)) == 0);
|
||||
length = strlen(base);
|
||||
while ((length > 0) && (base[length - 1] == '/')) {
|
||||
length--;
|
||||
}
|
||||
engine = (length > 0) && (strncmp(name, base, length) == 0) && ((name[length] == '/') || (name[length] == 0));
|
||||
free(base);
|
||||
}
|
||||
|
||||
|
|
@ -249,6 +304,38 @@ static bool _isEngineName(const char *name) {
|
|||
}
|
||||
|
||||
|
||||
// Appends a name to a listing unless it is already there (case-insensitively, like the keys).
|
||||
static void _listAdd(char ***list, int32_t *count, const char *name) {
|
||||
int32_t i = 0;
|
||||
|
||||
for (i = 0; i < *count; i++) {
|
||||
if (utilStricmp((*list)[i], name) == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
*list = (char **)realloc(*list, (size_t)(*count + 1) * sizeof(char *));
|
||||
(*list)[*count] = strdup(name);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
|
||||
// Adds the entries of a filesystem directory to a listing, if it exists.
|
||||
static void _listDirectory(const char *path, char ***list, int32_t *count) {
|
||||
DIR *dir = opendir(path);
|
||||
struct dirent *de = NULL;
|
||||
|
||||
if (dir == NULL) {
|
||||
return;
|
||||
}
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
if ((strcmp(de->d_name, ".") != 0) && (strcmp(de->d_name, "..") != 0)) {
|
||||
_listAdd(list, count, de->d_name);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
|
||||
// Forward slashes, no leading "./", no doubled slashes. Case is untouched.
|
||||
static char *_normalise(const char *name) {
|
||||
char *out = strdup(name);
|
||||
|
|
@ -551,6 +638,91 @@ bool vfsIsDatabase(const char *path) {
|
|||
}
|
||||
|
||||
|
||||
// A directory, loose or packed: a packed one exists when any asset sits below the key.
|
||||
bool vfsIsDirectory(const char *name) {
|
||||
TargetT target;
|
||||
struct stat info;
|
||||
bool found = false;
|
||||
|
||||
if (!_resolve(name, &target)) {
|
||||
return false;
|
||||
}
|
||||
found = (stat(target.path, &info) == 0) && S_ISDIR(info.st_mode);
|
||||
if (!found && (target.db != NULL)) {
|
||||
found = ((stat(target.overlay, &info) == 0) && S_ISDIR(info.st_mode)) || _assetDirectory(target.db, target.key);
|
||||
}
|
||||
_targetFree(&target);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
// True when the name is a plain filesystem path, so a caller may use the filesystem directly.
|
||||
bool vfsIsFilesystem(const char *name) {
|
||||
TargetT target;
|
||||
bool plain = true;
|
||||
|
||||
if (_resolve(name, &target)) {
|
||||
plain = (target.db == NULL);
|
||||
_targetFree(&target);
|
||||
}
|
||||
|
||||
return plain;
|
||||
}
|
||||
|
||||
|
||||
// The entries directly under a directory name, from the loose directory, the overlay and the
|
||||
// database together, each name once. Free with vfsListFree.
|
||||
char **vfsList(const char *name, int32_t *count) {
|
||||
TargetT target;
|
||||
char **list = NULL;
|
||||
char *from = NULL;
|
||||
char *to = NULL;
|
||||
char *entry = NULL;
|
||||
const char *path = NULL;
|
||||
const char *slash = NULL;
|
||||
size_t prefix = 0;
|
||||
|
||||
*count = 0;
|
||||
if (!_resolve(name, &target)) {
|
||||
return NULL;
|
||||
}
|
||||
_listDirectory(target.path, &list, count);
|
||||
if (target.db != NULL) {
|
||||
_listDirectory(target.overlay, &list, count);
|
||||
prefix = strlen(target.key);
|
||||
from = (prefix == 0) ? strdup("") : utilCreateString("%s/", target.key);
|
||||
to = (prefix == 0) ? strdup("\x7f") : utilCreateString("%s0", target.key);
|
||||
sqlite3_reset(target.db->listStmt);
|
||||
sqlite3_bind_text(target.db->listStmt, 1, from, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(target.db->listStmt, 2, to, -1, SQLITE_TRANSIENT);
|
||||
while (sqlite3_step(target.db->listStmt) == SQLITE_ROW) {
|
||||
path = (const char *)sqlite3_column_text(target.db->listStmt, 0) + strlen(from);
|
||||
slash = strchr(path, '/');
|
||||
entry = slash ? utilStrndup(path, (size_t)(slash - path)) : strdup(path);
|
||||
_listAdd(&list, count, entry);
|
||||
free(entry);
|
||||
}
|
||||
sqlite3_reset(target.db->listStmt);
|
||||
free(from);
|
||||
free(to);
|
||||
}
|
||||
_targetFree(&target);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
void vfsListFree(char **list, int32_t count) {
|
||||
int32_t i = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
free(list[i]);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
|
||||
|
||||
// Read-only stream for the SDL loaders. Packed assets are served from memory.
|
||||
SDL_IOStream *vfsOpenIO(const char *name) {
|
||||
TargetT target;
|
||||
|
|
@ -592,6 +764,7 @@ void vfsQuit(void) {
|
|||
next = db->next;
|
||||
sqlite3_finalize(db->assetStmt);
|
||||
sqlite3_finalize(db->chunkStmt);
|
||||
sqlite3_finalize(db->listStmt);
|
||||
sqlite3_close(db->db);
|
||||
free(db->path);
|
||||
free(db->loose);
|
||||
|
|
|
|||
26
src/vfs.h
26
src/vfs.h
|
|
@ -1,3 +1,25 @@
|
|||
/*
|
||||
*
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Singe virtual filesystem: one lookup for every file a game names.
|
||||
*
|
||||
|
|
@ -26,6 +48,10 @@ typedef struct VfsStreamS VfsStreamT;
|
|||
|
||||
|
||||
bool vfsExists(const char *name);
|
||||
bool vfsIsDirectory(const char *name);
|
||||
bool vfsIsFilesystem(const char *name);
|
||||
char **vfsList(const char *name, int32_t *count);
|
||||
void vfsListFree(char **list, int32_t count);
|
||||
char *vfsFilePath(const char *name, bool forWriting);
|
||||
void vfsInit(const char *container, const char *dataDirBase, const char *dataDir);
|
||||
bool vfsIsDatabase(const char *path);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
--[[
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
--[[
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
||||
* Singe 3
|
||||
* Copyright (C) 2006-2026 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 License
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue