Working on 2.20. Or maybe it'll be 3.0.
This commit is contained in:
parent
f62bd1ed30
commit
f6d63b6eb0
37 changed files with 11583 additions and 11123 deletions
3
.gitattributes
vendored
3
.gitattributes
vendored
|
|
@ -25,6 +25,9 @@
|
|||
*.exe filter=lfs diff=lfs merge=lfs -text
|
||||
*.dll filter=lfs diff=lfs merge=lfs -text
|
||||
*.dat filter=lfs diff=lfs merge=lfs -text
|
||||
# Singe game and menu descriptors are Lua text, not binary.
|
||||
games.dat !filter !diff !merge text
|
||||
menu.dat !filter !diff !merge text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
#
|
||||
|
|
|
|||
11
.gitignore
vendored
11
.gitignore
vendored
|
|
@ -1,9 +1,18 @@
|
|||
*~
|
||||
**/.builddir/
|
||||
**/.git-hidden/
|
||||
**/.github/
|
||||
thirdparty/**/.github/
|
||||
cmake-build-debug/
|
||||
*.log
|
||||
*.user
|
||||
temp/
|
||||
*#
|
||||
build/
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
*.a
|
||||
*.o
|
||||
docs/Manual.html
|
||||
docs/Manual.pdf
|
||||
docs/.asciidoctor/
|
||||
.claude/settings.local.json
|
||||
|
|
|
|||
124
CHANGELOG
124
CHANGELOG
|
|
@ -1,3 +1,121 @@
|
|||
SINGE 2.20
|
||||
==========
|
||||
|
||||
Unreleased
|
||||
|
||||
|
||||
API Changes
|
||||
-----------
|
||||
|
||||
- The sprite handle is now the FIRST argument of spriteDraw(), spriteLoop(),
|
||||
spriteQuality(), spriteRotate(), spriteRotateAndScale(), spriteScale(), and
|
||||
spriteSetFrame(), matching the video*() functions. Move the last argument
|
||||
of each call to the front. Games that cannot be edited can set
|
||||
SINGE_LEGACY_SPRITE_ARGS = true before loading the framework, or add
|
||||
LEGACY_SPRITE_ARGS = true to their games.dat entry. See "Migrating from
|
||||
Singe 2.10" in the manual.
|
||||
|
||||
- mouseSetEnabled(bool) and singeSetPauseKeyEnabled(bool) added. The old
|
||||
mouseEnable/mouseDisable and singeEnablePauseKey/singeDisablePauseKey are
|
||||
now aliases defined in Framework.singe.
|
||||
|
||||
- Handles, counts, and frame numbers are returned as Lua integers.
|
||||
videoIsPlaying() returns a boolean. mouseSetMode() returns nothing.
|
||||
|
||||
- onControllerMoved() receives the controller index (0 to 3), not SDL's
|
||||
instance ID. onMouseMoved() in MANY_MOUSE mode receives real relative
|
||||
motion in its third and fourth arguments.
|
||||
|
||||
- Runtime errors inside callbacks now end the game with a traceback, the way
|
||||
argument errors always have.
|
||||
|
||||
- Held keys no longer auto-repeat in MODE_NORMAL.
|
||||
|
||||
- The engine parks the disc on frame 1 before the script runs; the framework
|
||||
no longer seeks when it is loaded, so threaded games keep their own
|
||||
startup positioning.
|
||||
|
||||
- The pause key now pauses the whole game. The engine freezes the script
|
||||
(no callbacks, no singeMain), holds sound completions, releases and later
|
||||
re-presses held inputs, and draws a PAUSED indicator. It acts on the key
|
||||
press, and SWITCH_PAUSE reaches the script only when the key is disabled
|
||||
with singeSetPauseKeyEnabled(false). singeSetPauseFlag() still pauses
|
||||
media without freezing the script. Gamepad and mouse buttons mapped to
|
||||
pause, quit, screenshot, or grab now work in MODE_FULL too; keyboard
|
||||
mappings stay raw there.
|
||||
|
||||
- Constants that used to be duplicated in Framework.singe (SWITCH_*,
|
||||
FONT_QUALITY_*, MODE_*, MOUSE_*, OVERLAY_*, RENDER_*, SOUND_ERROR_*) are
|
||||
now defined by the engine. New: DISC_STOPPED/DISC_PLAYING/DISC_PAUSED
|
||||
for discGetState(), SINGE_VERSION_MAJOR/MINOR/STRING, and the SINGE_*
|
||||
input code layout that Framework.singe builds GAMEPAD_N and MOUSE_N from.
|
||||
|
||||
|
||||
Fixes
|
||||
-----
|
||||
|
||||
- controllerGetButton() never worked; it rejected every framework button
|
||||
code.
|
||||
|
||||
- The five argument form of spriteDraw() always failed.
|
||||
|
||||
- spriteSetFrame() on a still image crashed.
|
||||
|
||||
- Unloading the selected font left later fontPrint() calls using freed
|
||||
memory.
|
||||
|
||||
- overlayCircle() drew around the origin instead of the requested center.
|
||||
|
||||
- overlaySetResolution() broke mouse coordinates and vldpGetPixel() because
|
||||
the overlay scale was computed with integer division.
|
||||
|
||||
- mouseGetPosition() always returned 0,0 and controllerGetAxis() read the
|
||||
wrong controller because the axis cache was indexed two different ways.
|
||||
|
||||
- Analog stick release events fired with a stale input code. Controllers
|
||||
that were unplugged and replugged shifted their codes.
|
||||
|
||||
- The -o/--audio option was lost when the configuration was copied.
|
||||
|
||||
- --option=value forms of numeric options, and options after the script
|
||||
name, were not accepted.
|
||||
|
||||
- Running "singe game.singe" from the game's own directory wrote its data
|
||||
to the filesystem root.
|
||||
|
||||
- Framefile playback never advanced past the first segment, and switching
|
||||
segments dropped the selected audio track.
|
||||
|
||||
- Video timing accumulated main loop latency, drifting behind the audio.
|
||||
Audio is now the master clock: the picture follows the samples the sound
|
||||
device has actually consumed, so the two cannot drift apart and the
|
||||
mixer's buffering latency is accounted for. Silent videos follow the wall
|
||||
clock. The first frame after a seek was shown one update late.
|
||||
|
||||
- Video without an audio track crashed on the first update.
|
||||
|
||||
- The video audio stream was shared with the mixer thread without a lock.
|
||||
|
||||
- onSoundCompleted was called from the audio thread. It is now delivered
|
||||
from the game loop.
|
||||
|
||||
- Screenshots read the frame buffer through the window surface, which SDL
|
||||
forbids alongside a renderer, and could corrupt memory on shutdown.
|
||||
|
||||
- .patch archives crashed the installer. Archives without an explicit top
|
||||
level directory entry were rejected. Extraction now refuses paths that
|
||||
escape the installation directory.
|
||||
|
||||
- The Sinden gun arguments could overflow the configuration structure.
|
||||
|
||||
- Windows builds now carry the icon and version resource, with the version
|
||||
number encoded correctly.
|
||||
|
||||
- The build no longer depends on lyx, upx, or xxd; embedded resources, the
|
||||
version header, and the manual are generated by CMake. The manual is
|
||||
embedded again and extracted as Singe/Manual.pdf.
|
||||
|
||||
|
||||
SINGE 2.10
|
||||
==========
|
||||
|
||||
|
|
@ -8,8 +126,8 @@ New Features
|
|||
------------
|
||||
|
||||
- spriteDraw() now has two more forms. In addition to being able to draw
|
||||
regular sprites and streteched sprites it can now draw both using the
|
||||
sprite's center as the anchor instead of the upper right. This is highly
|
||||
regular sprites and stretched sprites it can now draw both using the
|
||||
sprite's center as the anchor instead of the upper left. This is highly
|
||||
useful when dealing with rotated sprites.
|
||||
|
||||
- Animated sprites! Both animated GIF and WEBP images are supported.
|
||||
|
|
@ -38,7 +156,7 @@ New Features
|
|||
directly into the script context. Now there is a proper Lua module search
|
||||
handler. Scripts can properly require() modules from the following:
|
||||
- Lua Standard Library
|
||||
- Lua Auxillary Library
|
||||
- Lua Auxiliary Library
|
||||
- LuaFileSystem
|
||||
- LuaSocket
|
||||
- LuaSec
|
||||
|
|
|
|||
521
CMakeLists.txt
521
CMakeLists.txt
|
|
@ -20,8 +20,13 @@
|
|||
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
project(singe2)
|
||||
project(singe2 VERSION 2.20 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
||||
# The copyright end year tracks the build date so nothing has to be hand edited.
|
||||
string(TIMESTAMP SINGE_COPYRIGHT_END_YEAR "%Y")
|
||||
|
||||
|
||||
if(NOT DEFINED KANGAROO_OS)
|
||||
|
|
@ -31,201 +36,283 @@ if(NOT DEFINED KANGAROO_ARCH)
|
|||
set(KANGAROO_ARCH x86_64)
|
||||
endif()
|
||||
|
||||
# Where build-all.sh installed the third party libraries for this platform.
|
||||
set(BUILD_DIR ${CMAKE_SOURCE_DIR}/.builddir/${KANGAROO_OS}/${KANGAROO_ARCH})
|
||||
set(GENERATED_DIR ${CMAKE_BINARY_DIR}/generated)
|
||||
file(MAKE_DIRECTORY ${GENERATED_DIR})
|
||||
|
||||
|
||||
# ===== Host tools needed to generate embedded resources =====
|
||||
|
||||
find_program(IMAGEMAGICK NAMES magick convert)
|
||||
if(NOT IMAGEMAGICK)
|
||||
message(FATAL_ERROR "ImageMagick (magick or convert) is required to render the embedded images.")
|
||||
endif()
|
||||
find_program(FFMPEG_TOOL NAMES ffmpeg)
|
||||
if(NOT FFMPEG_TOOL)
|
||||
message(FATAL_ERROR "ffmpeg is required to build the menu background video.")
|
||||
endif()
|
||||
find_program(LUA_TOOL NAMES lua5.4 lua)
|
||||
if(NOT LUA_TOOL)
|
||||
message(FATAL_ERROR "A Lua interpreter is required to generate the LuaSec option table.")
|
||||
endif()
|
||||
find_program(ASCIIDOCTOR_PDF NAMES asciidoctor-pdf)
|
||||
if(NOT ASCIIDOCTOR_PDF)
|
||||
message(FATAL_ERROR "asciidoctor-pdf is required to build the embedded manual (gem install asciidoctor-pdf rouge).")
|
||||
endif()
|
||||
find_program(ASCIIDOCTOR NAMES asciidoctor)
|
||||
|
||||
|
||||
# ===== Generated sources =====
|
||||
|
||||
# Version header and Windows resources come from the project() line above.
|
||||
configure_file(src/version.h.in ${GENERATED_DIR}/version.h @ONLY)
|
||||
configure_file(src/singe.rc.in ${GENERATED_DIR}/singe.rc @ONLY)
|
||||
|
||||
# LuaSec needs its headers reachable as <luasocket/...>.
|
||||
file(GLOB LUASOCKET_HEADERS thirdparty/luasocket/src/*.h)
|
||||
file(COPY ${LUASOCKET_HEADERS} DESTINATION ${GENERATED_DIR}/include/luasocket)
|
||||
|
||||
# LuaSec option table generated from the OpenSSL headers that were built.
|
||||
add_custom_command(
|
||||
OUTPUT ${GENERATED_DIR}/luasec_options.c
|
||||
COMMAND ${CMAKE_COMMAND} -DOUTPUT=${GENERATED_DIR}/luasec_options.c -DCOMMAND=${LUA_TOOL} "-DARGS=${CMAKE_SOURCE_DIR}/thirdparty/luasec/src/options.lua;-g;${BUILD_DIR}/include/openssl/ssl.h" -P ${CMAKE_SOURCE_DIR}/cmake/runToFile.cmake
|
||||
DEPENDS thirdparty/luasec/src/options.lua ${BUILD_DIR}/include/openssl/ssl.h cmake/runToFile.cmake
|
||||
COMMENT "Generating LuaSec options"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# Embeds a file as a C array. The symbol name is derived from the file name, as xxd does.
|
||||
set(EMBEDDED_HEADERS)
|
||||
function(singeEmbed input output prefix)
|
||||
get_filename_component(name ${input} NAME)
|
||||
string(MAKE_C_IDENTIFIER "${prefix}${name}" symbol)
|
||||
get_filename_component(guardName ${output} NAME)
|
||||
string(MAKE_C_IDENTIFIER "${guardName}" guard)
|
||||
string(TOUPPER "${guard}" guard)
|
||||
add_custom_command(
|
||||
OUTPUT ${output}
|
||||
COMMAND ${CMAKE_COMMAND} -DINPUT=${input} -DOUTPUT=${output} -DGUARD=${guard} -DSYMBOL=${symbol} -DCOPYRIGHT_END_YEAR=${SINGE_COPYRIGHT_END_YEAR} -P ${CMAKE_SOURCE_DIR}/cmake/embed.cmake
|
||||
DEPENDS ${input} cmake/embed.cmake
|
||||
COMMENT "Embedding ${name}"
|
||||
VERBATIM
|
||||
)
|
||||
set(EMBEDDED_HEADERS ${EMBEDDED_HEADERS} ${output} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Renders a layered image to a flattened PNG and embeds it.
|
||||
function(singeEmbedImage name)
|
||||
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"
|
||||
VERBATIM
|
||||
)
|
||||
singeEmbed(${GENERATED_DIR}/${name}.png ${GENERATED_DIR}/${name}.h "")
|
||||
set(EMBEDDED_HEADERS ${EMBEDDED_HEADERS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Embeds a Lua module from the third party tree.
|
||||
function(singeEmbedLua path prefix)
|
||||
get_filename_component(name ${path} NAME_WE)
|
||||
singeEmbed(${CMAKE_SOURCE_DIR}/${path} ${GENERATED_DIR}/${prefix}${name}_lua.h "${prefix}")
|
||||
set(EMBEDDED_HEADERS ${EMBEDDED_HEADERS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Images
|
||||
singeEmbedImage(font)
|
||||
singeEmbedImage(icon)
|
||||
singeEmbedImage(kangarooPunchLogo)
|
||||
singeEmbedImage(singeLogo)
|
||||
singeEmbedImage(laserDisc)
|
||||
singeEmbedImage(magnifyingGlass)
|
||||
singeEmbedImage(indexing)
|
||||
|
||||
# Windows icon for the resource file.
|
||||
add_custom_command(
|
||||
OUTPUT ${GENERATED_DIR}/icon.ico
|
||||
COMMAND ${IMAGEMAGICK} ${GENERATED_DIR}/icon.png -define icon:auto-resize=64,48,32,16 ${GENERATED_DIR}/icon.ico
|
||||
DEPENDS ${GENERATED_DIR}/icon.png
|
||||
COMMENT "Rendering icon.ico"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# Support files extracted at first run.
|
||||
singeEmbed(${CMAKE_SOURCE_DIR}/assets/Framework.singe ${GENERATED_DIR}/Framework_singe.h "")
|
||||
singeEmbed(${CMAKE_SOURCE_DIR}/assets/controls.cfg ${GENERATED_DIR}/controls_cfg.h "")
|
||||
singeEmbed(${CMAKE_SOURCE_DIR}/assets/Menu.singe ${GENERATED_DIR}/Menu_singe.h "")
|
||||
singeEmbed(${CMAKE_SOURCE_DIR}/assets/FreeSansBold.ttf ${GENERATED_DIR}/FreeSansBold_ttf.h "")
|
||||
|
||||
# Menu background video: two clips cropped to 4:3, scaled to 720x480, and joined.
|
||||
file(WRITE ${GENERATED_DIR}/menuBackground.txt "file ${GENERATED_DIR}/menuBackground1.mkv\nfile ${GENERATED_DIR}/menuBackground2.mkv\n")
|
||||
add_custom_command(
|
||||
OUTPUT ${GENERATED_DIR}/menuBackground.mkv
|
||||
COMMAND ${FFMPEG_TOOL} -y -loglevel error -i "${CMAKE_SOURCE_DIR}/assets/Singe Engine Intro.mpg" -filter:v "crop=ih/3*4:ih,scale=720:480" -c:v libx264 -c:a aac -f matroska ${GENERATED_DIR}/menuBackground1.mkv
|
||||
COMMAND ${FFMPEG_TOOL} -y -loglevel error -i ${CMAKE_SOURCE_DIR}/assets/180503_01_PurpleGrid.mp4 -filter:v "crop=ih/3*4:ih,scale=720:480" -c:v libx264 -c:a aac -f matroska ${GENERATED_DIR}/menuBackground2.mkv
|
||||
COMMAND ${FFMPEG_TOOL} -y -loglevel error -f concat -safe 0 -i ${GENERATED_DIR}/menuBackground.txt -c copy ${GENERATED_DIR}/menuBackground.mkv
|
||||
DEPENDS "assets/Singe Engine Intro.mpg" assets/180503_01_PurpleGrid.mp4
|
||||
COMMENT "Building menuBackground.mkv"
|
||||
VERBATIM
|
||||
)
|
||||
singeEmbed(${GENERATED_DIR}/menuBackground.mkv ${GENERATED_DIR}/menuBackground_mkv.h "")
|
||||
|
||||
# Manual, rendered from the AsciiDoc source and shipped inside the binary.
|
||||
add_custom_command(
|
||||
OUTPUT ${GENERATED_DIR}/Manual.pdf
|
||||
COMMAND ${ASCIIDOCTOR_PDF} -a revnumber=${PROJECT_VERSION} ${CMAKE_SOURCE_DIR}/docs/Manual.adoc -o ${GENERATED_DIR}/Manual.pdf
|
||||
DEPENDS docs/Manual.adoc
|
||||
COMMENT "Rendering Manual.pdf"
|
||||
VERBATIM
|
||||
)
|
||||
singeEmbed(${GENERATED_DIR}/Manual.pdf ${GENERATED_DIR}/Manual_pdf.h "")
|
||||
|
||||
# Lua libraries
|
||||
singeEmbedLua(thirdparty/luasocket/src/ftp.lua "")
|
||||
singeEmbedLua(thirdparty/luasocket/src/headers.lua "")
|
||||
singeEmbedLua(thirdparty/luasocket/src/http.lua "")
|
||||
singeEmbedLua(thirdparty/luasocket/src/ltn12.lua "")
|
||||
singeEmbedLua(thirdparty/luasocket/src/mbox.lua "")
|
||||
singeEmbedLua(thirdparty/luasocket/src/mime.lua "")
|
||||
singeEmbedLua(thirdparty/luasocket/src/smtp.lua "")
|
||||
singeEmbedLua(thirdparty/luasocket/src/socket.lua "")
|
||||
singeEmbedLua(thirdparty/luasocket/src/tp.lua "")
|
||||
singeEmbedLua(thirdparty/luasocket/src/url.lua "")
|
||||
singeEmbedLua(thirdparty/luasec/src/https.lua "")
|
||||
singeEmbedLua(thirdparty/luasec/src/ssl.lua "")
|
||||
singeEmbedLua(thirdparty/librs232/bindings/lua/rs232.lua "")
|
||||
singeEmbedLua(thirdparty/copas/src/copas.lua "")
|
||||
singeEmbedLua(thirdparty/copas/src/copas/ftp.lua "copas_")
|
||||
singeEmbedLua(thirdparty/copas/src/copas/http.lua "copas_")
|
||||
singeEmbedLua(thirdparty/copas/src/copas/smtp.lua "copas_")
|
||||
singeEmbedLua(thirdparty/copas/src/copas/lock.lua "copas_")
|
||||
singeEmbedLua(thirdparty/copas/src/copas/queue.lua "copas_")
|
||||
singeEmbedLua(thirdparty/copas/src/copas/semaphore.lua "copas_")
|
||||
singeEmbedLua(thirdparty/copas/src/copas/timer.lua "copas_")
|
||||
singeEmbedLua(thirdparty/binaryheap.lua/src/binaryheap.lua "")
|
||||
singeEmbedLua(thirdparty/timerwheel.lua/src/timerwheel/timerwheel.lua "")
|
||||
singeEmbedLua(thirdparty/json.lua/json.lua "")
|
||||
|
||||
# Optional HTML manual for browsing: cmake --build . --target docs
|
||||
if(ASCIIDOCTOR)
|
||||
add_custom_target(docs
|
||||
COMMAND ${ASCIIDOCTOR} -a revnumber=${PROJECT_VERSION} ${CMAKE_SOURCE_DIR}/docs/Manual.adoc -o ${CMAKE_BINARY_DIR}/Manual.html
|
||||
DEPENDS ${GENERATED_DIR}/Manual.pdf
|
||||
COMMENT "Rendering Manual.html"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
# ===== Sources =====
|
||||
|
||||
set(SINGE_SOURCE
|
||||
src/common.h
|
||||
src/embedded.h
|
||||
src/frameFile.c
|
||||
src/frameFile.h
|
||||
src/main.c
|
||||
src/main.h
|
||||
src/singe.c
|
||||
src/singe.h
|
||||
src/util.h
|
||||
src/embedded.h
|
||||
src/frameFile.h
|
||||
src/main.c
|
||||
src/common.h
|
||||
src/videoPlayer.c
|
||||
src/stddclmr.h
|
||||
src/frameFile.c
|
||||
src/videoPlayer.h
|
||||
src/util.c
|
||||
src/util.h
|
||||
src/videoPlayer.c
|
||||
src/videoPlayer.h
|
||||
)
|
||||
|
||||
|
||||
set(ARG_PARSER_SOURCE
|
||||
thirdparty/arg_parser/carg_parser.c
|
||||
thirdparty/arg_parser/carg_parser.h
|
||||
)
|
||||
|
||||
|
||||
set(JBIG_SOURCE
|
||||
thirdparty/jbigkit/libjbig/jbig.c
|
||||
thirdparty/jbigkit/libjbig/jbig.h
|
||||
thirdparty/jbigkit/libjbig/jbig_ar.c
|
||||
thirdparty/jbigkit/libjbig/jbig_ar.h
|
||||
)
|
||||
|
||||
|
||||
set(LUA_SOURCE
|
||||
thirdparty/lua/src/lmem.h
|
||||
thirdparty/lua/src/ltm.h
|
||||
thirdparty/lua/src/lutf8lib.c
|
||||
thirdparty/lua/src/ldo.h
|
||||
thirdparty/lua/src/ldump.c
|
||||
thirdparty/lua/src/lstring.h
|
||||
thirdparty/lua/src/lparser.h
|
||||
thirdparty/lua/src/lcorolib.c
|
||||
thirdparty/lua/src/loslib.c
|
||||
thirdparty/lua/src/lparser.c
|
||||
thirdparty/lua/src/ltablib.c
|
||||
thirdparty/lua/src/ltable.c
|
||||
thirdparty/lua/src/ljumptab.h
|
||||
thirdparty/lua/src/luaconf.h
|
||||
thirdparty/lua/src/lstate.c
|
||||
thirdparty/lua/src/lobject.h
|
||||
thirdparty/lua/src/lstate.h
|
||||
thirdparty/lua/src/ldo.c
|
||||
thirdparty/lua/src/lfunc.c
|
||||
thirdparty/lua/src/lmathlib.c
|
||||
thirdparty/lua/src/lua.h
|
||||
thirdparty/lua/src/lauxlib.h
|
||||
thirdparty/lua/src/ltm.c
|
||||
thirdparty/lua/src/lstrlib.c
|
||||
thirdparty/lua/src/lapi.h
|
||||
thirdparty/lua/src/lopcodes.c
|
||||
thirdparty/lua/src/lvm.h
|
||||
thirdparty/lua/src/linit.c
|
||||
thirdparty/lua/src/lobject.c
|
||||
thirdparty/lua/src/ldebug.c
|
||||
thirdparty/lua/src/lctype.c
|
||||
thirdparty/lua/src/lgc.c
|
||||
thirdparty/lua/src/lzio.h
|
||||
thirdparty/lua/src/lgc.h
|
||||
thirdparty/lua/src/lctype.h
|
||||
thirdparty/lua/src/lopcodes.h
|
||||
thirdparty/lua/src/llimits.h
|
||||
thirdparty/lua/src/lprefix.h
|
||||
thirdparty/lua/src/llex.h
|
||||
thirdparty/lua/src/lundump.c
|
||||
thirdparty/lua/src/lbaselib.c
|
||||
thirdparty/lua/src/loadlib.c
|
||||
thirdparty/lua/src/ldblib.c
|
||||
thirdparty/lua/src/ldebug.h
|
||||
thirdparty/lua/src/lundump.h
|
||||
thirdparty/lua/src/lopnames.h
|
||||
thirdparty/lua/src/ltable.h
|
||||
thirdparty/lua/src/lmem.c
|
||||
thirdparty/lua/src/lcode.h
|
||||
thirdparty/lua/src/lua.hpp
|
||||
thirdparty/lua/src/lauxlib.c
|
||||
thirdparty/lua/src/liolib.c
|
||||
thirdparty/lua/src/lapi.c
|
||||
thirdparty/lua/src/lcode.c
|
||||
thirdparty/lua/src/llex.c
|
||||
thirdparty/lua/src/lstring.c
|
||||
thirdparty/lua/src/lvm.c
|
||||
thirdparty/lua/src/lualib.h
|
||||
thirdparty/lua/src/lfunc.h
|
||||
thirdparty/lua/src/lzio.c
|
||||
)
|
||||
|
||||
file(GLOB LUA_SOURCE thirdparty/lua/src/*.c thirdparty/lua/src/*.h)
|
||||
list(FILTER LUA_SOURCE EXCLUDE REGEX "/(lua|luac|onelua)\\.c$")
|
||||
|
||||
set(LUA_FILESYSTEM_SOURCE
|
||||
thirdparty/luafilesystem/src/lfs.c
|
||||
thirdparty/luafilesystem/src/lfs.h
|
||||
)
|
||||
|
||||
|
||||
set(LUA_SOCKET_SOURCE
|
||||
thirdparty/luasocket/src/luasocket.h
|
||||
thirdparty/luasocket/src/luasocket.c
|
||||
thirdparty/luasocket/src/timeout.h
|
||||
thirdparty/luasocket/src/timeout.c
|
||||
thirdparty/luasocket/src/buffer.h
|
||||
thirdparty/luasocket/src/buffer.c
|
||||
thirdparty/luasocket/src/io.h
|
||||
thirdparty/luasocket/src/io.c
|
||||
thirdparty/luasocket/src/auxiliar.h
|
||||
thirdparty/luasocket/src/auxiliar.c
|
||||
thirdparty/luasocket/src/compat.h
|
||||
thirdparty/luasocket/src/auxiliar.h
|
||||
thirdparty/luasocket/src/buffer.c
|
||||
thirdparty/luasocket/src/buffer.h
|
||||
thirdparty/luasocket/src/compat.c
|
||||
thirdparty/luasocket/src/options.h
|
||||
thirdparty/luasocket/src/options.c
|
||||
thirdparty/luasocket/src/inet.h
|
||||
thirdparty/luasocket/src/inet.c
|
||||
thirdparty/luasocket/src/except.h
|
||||
thirdparty/luasocket/src/compat.h
|
||||
thirdparty/luasocket/src/except.c
|
||||
thirdparty/luasocket/src/select.h
|
||||
thirdparty/luasocket/src/select.c
|
||||
thirdparty/luasocket/src/tcp.h
|
||||
thirdparty/luasocket/src/tcp.c
|
||||
thirdparty/luasocket/src/udp.h
|
||||
thirdparty/luasocket/src/udp.c
|
||||
thirdparty/luasocket/src/mime.h
|
||||
thirdparty/luasocket/src/except.h
|
||||
thirdparty/luasocket/src/inet.c
|
||||
thirdparty/luasocket/src/inet.h
|
||||
thirdparty/luasocket/src/io.c
|
||||
thirdparty/luasocket/src/io.h
|
||||
thirdparty/luasocket/src/luasocket.c
|
||||
thirdparty/luasocket/src/luasocket.h
|
||||
thirdparty/luasocket/src/mime.c
|
||||
thirdparty/luasocket/src/mime.h
|
||||
thirdparty/luasocket/src/options.c
|
||||
thirdparty/luasocket/src/options.h
|
||||
thirdparty/luasocket/src/select.c
|
||||
thirdparty/luasocket/src/select.h
|
||||
thirdparty/luasocket/src/socket.h
|
||||
thirdparty/luasocket/src/pierror.h
|
||||
thirdparty/luasocket/src/tcp.c
|
||||
thirdparty/luasocket/src/tcp.h
|
||||
thirdparty/luasocket/src/timeout.c
|
||||
thirdparty/luasocket/src/timeout.h
|
||||
thirdparty/luasocket/src/udp.c
|
||||
thirdparty/luasocket/src/udp.h
|
||||
)
|
||||
if(WIN32)
|
||||
set(LUA_SOCKET_SOURCE
|
||||
${LUA_SOCKET_SOURCE}
|
||||
thirdparty/luasocket/src/wsocket.h
|
||||
list(APPEND LUA_SOCKET_SOURCE
|
||||
thirdparty/luasocket/src/wsocket.c
|
||||
thirdparty/luasocket/src/wsocket.h
|
||||
)
|
||||
ELSE()
|
||||
set(LUA_SOCKET_SOURCE
|
||||
${LUA_SOCKET_SOURCE}
|
||||
thirdparty/luasocket/src/usocket.h
|
||||
thirdparty/luasocket/src/usocket.c
|
||||
thirdparty/luasocket/src/unix.h
|
||||
else()
|
||||
list(APPEND LUA_SOCKET_SOURCE
|
||||
thirdparty/luasocket/src/serial.c
|
||||
thirdparty/luasocket/src/unix.c
|
||||
thirdparty/luasocket/src/unixstream.h
|
||||
thirdparty/luasocket/src/unixstream.c
|
||||
thirdparty/luasocket/src/unix.h
|
||||
thirdparty/luasocket/src/unixdgram.c
|
||||
thirdparty/luasocket/src/unixdgram.h
|
||||
thirdparty/luasocket/src/serial.c
|
||||
thirdparty/luasocket/src/unixstream.c
|
||||
thirdparty/luasocket/src/unixstream.h
|
||||
thirdparty/luasocket/src/usocket.c
|
||||
thirdparty/luasocket/src/usocket.h
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
endif()
|
||||
|
||||
set(LUASEC_SOURCE
|
||||
thirdparty/luasec/src/compat.h
|
||||
thirdparty/luasec/src/config.c
|
||||
${BUILD_DIR}/generated/luasec_options.c
|
||||
thirdparty/luasec/src/context.c
|
||||
thirdparty/luasec/src/context.h
|
||||
thirdparty/luasec/src/ec.c
|
||||
thirdparty/luasec/src/ec.h
|
||||
thirdparty/luasec/src/options.h
|
||||
thirdparty/luasec/src/ssl.c
|
||||
thirdparty/luasec/src/ssl.h
|
||||
thirdparty/luasec/src/x509.c
|
||||
thirdparty/luasec/src/x509.h
|
||||
${GENERATED_DIR}/luasec_options.c
|
||||
thirdparty/luasec/src/compat.h
|
||||
thirdparty/luasec/src/config.c
|
||||
thirdparty/luasec/src/context.c
|
||||
thirdparty/luasec/src/context.h
|
||||
thirdparty/luasec/src/ec.c
|
||||
thirdparty/luasec/src/ec.h
|
||||
thirdparty/luasec/src/options.h
|
||||
thirdparty/luasec/src/ssl.c
|
||||
thirdparty/luasec/src/ssl.h
|
||||
thirdparty/luasec/src/x509.c
|
||||
thirdparty/luasec/src/x509.h
|
||||
)
|
||||
|
||||
|
||||
set(LUA_RS232_SOURCE
|
||||
thirdparty/librs232/bindings/lua/luars232.c
|
||||
thirdparty/librs232/src/rs232.c
|
||||
)
|
||||
if(WIN32)
|
||||
set(LUA_RS232_SOURCE
|
||||
${LUA_RS232_SOURCE}
|
||||
thirdparty/librs232/src/rs232_windows.c
|
||||
)
|
||||
ELSE()
|
||||
set(LUA_RS232_SOURCE
|
||||
${LUA_RS232_SOURCE}
|
||||
thirdparty/librs232/src/rs232_posix.c
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
list(APPEND LUA_RS232_SOURCE thirdparty/librs232/src/rs232_windows.c)
|
||||
else()
|
||||
list(APPEND LUA_RS232_SOURCE thirdparty/librs232/src/rs232_posix.c)
|
||||
endif()
|
||||
|
||||
set(UTHASH_SOURCE
|
||||
thirdparty/uthash/src/uthash.h
|
||||
thirdparty/uthash/src/utlist.h
|
||||
)
|
||||
|
||||
|
||||
set(MANYMOUSE_SOURCE
|
||||
thirdparty/manymouse/linux_evdev.c
|
||||
thirdparty/manymouse/macosx_hidmanager.c
|
||||
|
|
@ -236,17 +323,19 @@ set(MANYMOUSE_SOURCE
|
|||
thirdparty/manymouse/x11_xinput2.c
|
||||
)
|
||||
|
||||
|
||||
set(SDL2_GFX_SOURCE
|
||||
thirdparty/SDL2_gfx/SDL2_rotozoom.h
|
||||
thirdparty/SDL2_gfx/SDL2_rotozoom.c
|
||||
)
|
||||
|
||||
|
||||
# ===== Target =====
|
||||
|
||||
add_executable(${CMAKE_PROJECT_NAME}
|
||||
${SINGE_SOURCE}
|
||||
${EMBEDDED_HEADERS}
|
||||
${GENERATED_DIR}/version.h
|
||||
${ARG_PARSER_SOURCE}
|
||||
${JBIG_SOURCE}
|
||||
${LUA_SOURCE}
|
||||
${LUA_FILESYSTEM_SOURCE}
|
||||
${LUA_SOCKET_SOURCE}
|
||||
|
|
@ -256,36 +345,40 @@ add_executable(${CMAKE_PROJECT_NAME}
|
|||
${MANYMOUSE_SOURCE}
|
||||
${SDL2_GFX_SOURCE}
|
||||
)
|
||||
|
||||
|
||||
# Perform pre-build operations.
|
||||
#add_custom_target(BUILD_PREREQS
|
||||
# COMMAND ${BUILD_DIR}-deps.sh "${CMAKE_SOURCE_DIR}"
|
||||
# BYPRODUCTS
|
||||
# ${CMAKE_SOURCE_DIR}/thirdparty-installed/lib/libarchive.a
|
||||
#)
|
||||
#add_dependencies(${CMAKE_PROJECT_NAME} BUILD_PREREQS)
|
||||
|
||||
|
||||
if(WIN32)
|
||||
# Note: _WIN32_WINNT=0x0600 sets the minimum compatible version of
|
||||
# Windows to Vista. The function inet_pton() does not exist before then.
|
||||
set(DEFINE_LIST
|
||||
-Dmain=SDL_main
|
||||
-D_WIN32_WINNT=0x0600
|
||||
)
|
||||
else()
|
||||
set(DEFINE_LIST)
|
||||
enable_language(RC)
|
||||
target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${GENERATED_DIR}/singe.rc ${GENERATED_DIR}/icon.ico)
|
||||
endif()
|
||||
target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC
|
||||
${DEFINE_LIST}
|
||||
-DRS232_STATIC
|
||||
-DFFMS_STATIC
|
||||
|
||||
# Output name matches the release artifact: Singe-v2.20-Linux-x86_64
|
||||
string(SUBSTRING ${KANGAROO_OS} 0 1 osInitial)
|
||||
string(SUBSTRING ${KANGAROO_OS} 1 -1 osRest)
|
||||
string(TOUPPER ${osInitial} osInitial)
|
||||
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME "Singe-v${PROJECT_VERSION}-${osInitial}${osRest}-${KANGAROO_ARCH}")
|
||||
|
||||
# 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")
|
||||
|
||||
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
RS232_STATIC
|
||||
FFMS_STATIC
|
||||
)
|
||||
if(WIN32)
|
||||
# _WIN32_WINNT=0x0600 sets the minimum compatible version of Windows to Vista.
|
||||
# The function inet_pton() does not exist before then.
|
||||
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
main=SDL_main
|
||||
_WIN32_WINNT=0x0600
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
-Wno-deprecated-declarations
|
||||
)
|
||||
|
||||
|
||||
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC
|
||||
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
${CMAKE_BINARY_DIR}
|
||||
${GENERATED_DIR}/include
|
||||
${BUILD_DIR}
|
||||
${BUILD_DIR}/include
|
||||
${BUILD_DIR}/include/SDL2
|
||||
|
|
@ -294,30 +387,21 @@ target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC
|
|||
thirdparty/librs232/include
|
||||
)
|
||||
|
||||
|
||||
target_link_directories(${CMAKE_PROJECT_NAME} PUBLIC
|
||||
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
${BUILD_DIR}/lib
|
||||
)
|
||||
|
||||
|
||||
if(${KANGAROO_OS} STREQUAL "linux")
|
||||
set(LIB_LIST
|
||||
-lX11
|
||||
-lvdpau
|
||||
${BUILD_DIR}/lib/libjpeg.a
|
||||
${BUILD_DIR}/lib/libpng.a
|
||||
-lstdc++
|
||||
)
|
||||
endif()
|
||||
if(${KANGAROO_OS} STREQUAL "pi")
|
||||
set(LIB_LIST
|
||||
${BUILD_DIR}/lib/libjpeg.a
|
||||
${BUILD_DIR}/lib/libpng.a
|
||||
-lstdc++
|
||||
)
|
||||
endif()
|
||||
if(${KANGAROO_OS} STREQUAL "macos")
|
||||
set(LIB_LIST
|
||||
# ===== Libraries =====
|
||||
|
||||
# Platform specific system libraries.
|
||||
if(KANGAROO_OS STREQUAL "linux")
|
||||
# ffmpeg's vdpau hardware context is always compiled in on X11 hosts.
|
||||
set(SYSTEM_LIBS -lX11 -lvdpau)
|
||||
elseif(KANGAROO_OS STREQUAL "pi")
|
||||
set(SYSTEM_LIBS)
|
||||
elseif(KANGAROO_OS STREQUAL "macos")
|
||||
set(SYSTEM_LIBS
|
||||
-Wl,-framework,CoreVideo
|
||||
-Wl,-framework,Cocoa
|
||||
-Wl,-framework,IOKit
|
||||
|
|
@ -335,9 +419,11 @@ if(${KANGAROO_OS} STREQUAL "macos")
|
|||
-lc++
|
||||
${OSXCROSS_TARGET_DIR}/darwin/libclang_rt.osx.a
|
||||
)
|
||||
endif()
|
||||
if(${KANGAROO_OS} STREQUAL "windows")
|
||||
set(LIB_LIST
|
||||
if(NOT DEFINED OSXCROSS_TARGET_DIR)
|
||||
message(FATAL_ERROR "OSXCROSS_TARGET_DIR must be set for macOS builds.")
|
||||
endif()
|
||||
elseif(KANGAROO_OS STREQUAL "windows")
|
||||
set(SYSTEM_LIBS
|
||||
-mwindows
|
||||
-static
|
||||
-lmingw32
|
||||
|
|
@ -359,29 +445,22 @@ if(${KANGAROO_OS} STREQUAL "windows")
|
|||
-lbcrypt
|
||||
-lssp
|
||||
-lcrypt32
|
||||
${BUILD_DIR}/lib/libjpeg.a
|
||||
${BUILD_DIR}/lib/libpng.a
|
||||
-lstdc++
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown KANGAROO_OS: ${KANGAROO_OS}")
|
||||
endif()
|
||||
set(LIB_LIST
|
||||
${LIB_LIST}
|
||||
|
||||
# Static third party libraries built by build-all.sh.
|
||||
set(STATIC_LIBS
|
||||
${BUILD_DIR}/lib/libarchive.a
|
||||
${BUILD_DIR}/lib/libavcodec.a
|
||||
${BUILD_DIR}/lib/libavdevice.a
|
||||
${BUILD_DIR}/lib/libavfilter.a
|
||||
${BUILD_DIR}/lib/libavformat.a
|
||||
# ${BUILD_DIR}/lib/libavif.a
|
||||
${BUILD_DIR}/lib/libavutil.a
|
||||
# ${BUILD_DIR}/lib/libbrotlicommon-static.a
|
||||
# ${BUILD_DIR}/lib/libbrotlidec-static.a
|
||||
${BUILD_DIR}/lib/libbz2_static.a
|
||||
# ${BUILD_DIR}/lib/libdav1d.a
|
||||
${BUILD_DIR}/lib/libffms2.a
|
||||
${BUILD_DIR}/lib/libfreetype.a
|
||||
# ${BUILD_DIR}/lib/libharfbuzz.a
|
||||
# ${BUILD_DIR}/lib/libhwy.a
|
||||
# ${BUILD_DIR}/lib/libjxl_dec.a
|
||||
${BUILD_DIR}/lib/liblzma.a
|
||||
${BUILD_DIR}/lib/libopus.a
|
||||
${BUILD_DIR}/lib/libopusfile.a
|
||||
|
|
@ -391,11 +470,9 @@ set(LIB_LIST
|
|||
${BUILD_DIR}/lib/libSDL2_image.a
|
||||
${BUILD_DIR}/lib/libSDL2main.a
|
||||
${BUILD_DIR}/lib/libSDL2_mixer.a
|
||||
${BUILD_DIR}/lib/libSDL2_test.a
|
||||
${BUILD_DIR}/lib/libSDL2_ttf.a
|
||||
${BUILD_DIR}/lib/libswresample.a
|
||||
${BUILD_DIR}/lib/libswscale.a
|
||||
# ${BUILD_DIR}/lib/libtiff.a
|
||||
${BUILD_DIR}/lib/libwavpack.a
|
||||
${BUILD_DIR}/lib/libwebp.a
|
||||
${BUILD_DIR}/lib/libwebpdemux.a
|
||||
|
|
@ -405,15 +482,21 @@ set(LIB_LIST
|
|||
${BUILD_DIR}/lib/libcrypto.a
|
||||
${BUILD_DIR}/lib/libssl.a
|
||||
)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
# -Wl,--start-group
|
||||
${LIB_LIST}
|
||||
${LIB_LIST}
|
||||
${LIB_LIST}
|
||||
# -Wl,--end-group
|
||||
-pthread
|
||||
-lm
|
||||
)
|
||||
if(NOT KANGAROO_OS STREQUAL "macos")
|
||||
list(APPEND STATIC_LIBS
|
||||
${BUILD_DIR}/lib/libjpeg.a
|
||||
${BUILD_DIR}/lib/libpng.a
|
||||
-lstdc++
|
||||
)
|
||||
endif()
|
||||
|
||||
# System libraries follow the static ones so their symbols resolve for everything above.
|
||||
# Apple's linker has no --start-group, so the list is repeated there instead.
|
||||
if(KANGAROO_OS STREQUAL "macos")
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ${STATIC_LIBS} ${STATIC_LIBS} ${STATIC_LIBS} ${SYSTEM_LIBS} -pthread -lm)
|
||||
else()
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE -Wl,--start-group ${STATIC_LIBS} -Wl,--end-group ${SYSTEM_LIBS} -pthread -lm)
|
||||
endif()
|
||||
|
||||
|
||||
install(TARGETS ${CMAKE_PROJECT_NAME})
|
||||
|
|
|
|||
34
INSTALL
34
INSTALL
|
|
@ -1,7 +1,8 @@
|
|||
SINGE 2.10
|
||||
SINGE 2.20
|
||||
==========
|
||||
|
||||
(For the latest version of this document, visit https://kangaroopunch.com!)
|
||||
(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!
|
||||
|
||||
|
|
@ -38,30 +39,7 @@ the new files.
|
|||
|
||||
COMMAND LINE
|
||||
============
|
||||
___ ___ _ _ ___ ___
|
||||
/ __|_ _| \| |/ __| __| Somewhat Interactive Nostalgic Game Engine v2.10
|
||||
\__ \| || .` | (_ | _| Copyright (c) 2006-2024 Scott C. Duensing
|
||||
|___/___|_|\_|\___|___| https://KangarooPunch.com https://SingeEngine.com
|
||||
|
||||
Usage: Singe-v2.10-Windows-x86_64.exe [OPTIONS] scriptName{.singe}
|
||||
|
||||
-a, --aspect=N:D force aspect ratio
|
||||
-c, --showcalculated show calculated framefile values for debugging
|
||||
-d, --datadir=PATHNAME alternate location for written files
|
||||
-e, --volume_nonvldp=PERCENT specify sound effects volume in percent
|
||||
-f, --fullscreen run in full screen mode
|
||||
-h, --help this display
|
||||
-k, --nologos kill the splash screens
|
||||
-l, --volume_vldp=PERCENT specify laserdisc volume in percent
|
||||
-m, --nomouse disable mouse
|
||||
-n, --nocrosshair request game not display gun crosshairs
|
||||
-o, --audio=TRACK select default track for audio output
|
||||
-p, --program trace Singe execution to screen and file
|
||||
-s, --nosound, --mutesound mutes all sound
|
||||
-t, --trace trace script execution to screen and file
|
||||
-u, --stretch use ugly stretched video
|
||||
-v, --framefile=FILENAME use an alternate video file
|
||||
-w, --fullscreen_window run in windowed full screen mode
|
||||
-x, --xresolution=VALUE specify horizontal resolution
|
||||
-y, --yresolution=VALUE specify vertical resolution
|
||||
-z, --noconsole zero console output
|
||||
Run the binary with -h (or --help) for the option summary. Every option is
|
||||
described in the "Command Line Options" chapter of Singe/Manual.pdf, which
|
||||
is extracted on first run.
|
||||
|
|
|
|||
16
LICENSES
16
LICENSES
|
|
@ -9,16 +9,23 @@ bzip2 bzip2-1.0.6 https://sourceware.org/bzip2
|
|||
copas MIT https://lunarmodules.github.io/copas
|
||||
ffmpeg LGPL-2.1 https://ffmpeg.org
|
||||
ffms2 GPL-3.0-only https://github.com/FFMS/ffms2
|
||||
jbigkit GPL-2.0 https://www.cl.cam.ac.uk/~mgk25/jbigkit
|
||||
freetype FTL https://freetype.org
|
||||
json.lua MIT https://github.com/rxi/json.lua
|
||||
libarchive BSD-2-Clause https://libarchive.org
|
||||
libjpeg-turbo IJG https://libjpeg-turbo.org
|
||||
libogg BSD-3-Clause https://xiph.org/ogg
|
||||
libpng libpng-2.0 http://www.libpng.org
|
||||
librs232 MIT https://github.com/srdgame/librs232
|
||||
libwebp BSD-3-Clause https://developers.google.com/speed/webp
|
||||
libxmp MIT https://github.com/libxmp/libxmp
|
||||
lua MIT https://www.lua.org
|
||||
luafilesystem MIT https://lunarmodules.github.io/luafilesystem
|
||||
luasec MIT https://github.com/lunarmodules/luasec
|
||||
luasocket MIT https://lunarmodules.github.io/luasocket
|
||||
manymouse Zlib https://icculus.org/manymouse
|
||||
openssl Apache-2.0 https://www.openssl.org
|
||||
opus BSD-3-Clause https://opus-codec.org
|
||||
opusfile BSD-3-Clause https://opus-codec.org
|
||||
SDL2 Zlib https://www.libsdl.org
|
||||
SDL2_gfx LGPL-2.1 https://github.com/ferzkopp/SDL_gfx
|
||||
SDL2_image Zlib https://www.libsdl.org
|
||||
|
|
@ -27,7 +34,14 @@ SDL2_ttf Zlib https://www.libsdl.org
|
|||
timerwheel.lua MIT https://tieske.github.io/timerwheel.lua
|
||||
uthash BSD-1-Clause https://troydhanson.github.io/uthash
|
||||
vlc GPL-2.0 https://www.videolan.org/vlc
|
||||
wavpack BSD-3-Clause https://www.wavpack.com
|
||||
xz Public-Domain https://github.com/tukaani-project/xz
|
||||
zlib Zlib https://zlib.net
|
||||
zstd BSD-3-Clause https://facebook.github.io/zstd
|
||||
|
||||
|
||||
Fonts
|
||||
-----
|
||||
|
||||
FreeSansBold GPL-3.0 with font exception https://www.gnu.org/software/freefont
|
||||
BreatheFire (license not recorded - used only by the manual's example)
|
||||
|
|
|
|||
|
|
@ -25,24 +25,26 @@
|
|||
-- Singe 2.xx Features -------------------------------------------------------
|
||||
|
||||
|
||||
SINGE_FRAMEWORK_VERSION = 2.10
|
||||
-- SINGE_FRAMEWORK_VERSION, SWITCH_*, FONT_QUALITY_*, MODE_*, MOUSE_*, OVERLAY_*,
|
||||
-- RENDER_*, DISC_*, SOUND_ERROR_* and the SINGE_* input code layout are all
|
||||
-- pushed by the engine before this file runs, so they are defined in exactly
|
||||
-- one place: singe.c.
|
||||
|
||||
if singeGetScriptPath ~= nil then
|
||||
DIR = singeGetScriptPath():match("(.*[/\\])") or "./"
|
||||
end
|
||||
|
||||
|
||||
function utilDeepCopy(orig)
|
||||
local orig_type = type(orig)
|
||||
function utilDeepCopy(original)
|
||||
local copy
|
||||
if orig_type == 'table' then
|
||||
if type(original) == 'table' then
|
||||
copy = {}
|
||||
for orig_key, orig_value in next, orig, nil do
|
||||
copy[utilDeepCopy(orig_key)] = utilDeepCopy(orig_value)
|
||||
for key, value in next, original, nil do
|
||||
copy[utilDeepCopy(key)] = utilDeepCopy(value)
|
||||
end
|
||||
setmetatable(copy, utilDeepCopy(getmetatable(orig)))
|
||||
setmetatable(copy, utilDeepCopy(getmetatable(original)))
|
||||
else -- number, string, boolean, etc
|
||||
copy = orig
|
||||
copy = original
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
|
@ -65,16 +67,26 @@ end
|
|||
|
||||
|
||||
function utilGetTableSize(t)
|
||||
local count = 0
|
||||
for _, __ in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
local count = 0
|
||||
for _ in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
|
||||
-- Returns a copy of a { NAME = { name = NAME, value = N } } table with every value shifted.
|
||||
function utilOffsetTable(source, offset)
|
||||
local copy = utilDeepCopy(source)
|
||||
for _, entry in pairs(copy) do
|
||||
entry.value = entry.value + offset
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
|
||||
function utilTrim(s)
|
||||
return (s:gsub("^%s*(.-)%s*$", "%1"))
|
||||
return (s:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -323,9 +335,6 @@ SCANCODE = {
|
|||
AUDIOFASTFORWARD = { name = "AUDIOFASTFORWARD", value = 286 }
|
||||
}
|
||||
|
||||
SCANCODE_MIN = 4 -- Lowest value
|
||||
SCANCODE_MAX = 286 -- Highest value, not the number of items in the table.
|
||||
|
||||
MODIFIER = {
|
||||
NONE = { name = "NONE", value = 0x0000 },
|
||||
LSHIFT = { name = "LSHIFT", value = 0x0001 },
|
||||
|
|
@ -347,63 +356,41 @@ MODIFIER = {
|
|||
GUI = { name = "GUI", value = 0x0400 + 0x0800 }
|
||||
}
|
||||
|
||||
GAMEPAD_0 = {
|
||||
AXIS_LEFT_X = { name = "AXIS_LEFT_X", value = 500 },
|
||||
AXIS_LEFT_X_L = { name = "AXIS_LEFT_X_L", value = 501 },
|
||||
AXIS_LEFT_X_R = { name = "AXIS_LEFT_X_R", value = 502 },
|
||||
AXIS_LEFT_Y = { name = "AXIS_LEFT_Y", value = 503 },
|
||||
AXIS_LEFT_Y_U = { name = "AXIS_LEFT_Y_U", value = 504 },
|
||||
AXIS_LEFT_Y_D = { name = "AXIS_LEFT_Y_D", value = 505 },
|
||||
AXIS_RIGHT_X = { name = "AXIS_RIGHT_X", value = 506 },
|
||||
AXIS_RIGHT_X_L = { name = "AXIS_RIGHT_X_L", value = 507 },
|
||||
AXIS_RIGHT_X_R = { name = "AXIS_RIGHT_X_R", value = 508 },
|
||||
AXIS_RIGHT_Y = { name = "AXIS_RIGHT_Y", value = 509 },
|
||||
AXIS_RIGHT_Y_U = { name = "AXIS_RIGHT_Y_U", value = 510 },
|
||||
AXIS_RIGHT_Y_D = { name = "AXIS_RIGHT_Y_D", value = 511 },
|
||||
AXIS_LEFT_TRIGGER = { name = "AXIS_LEFT_TRIGGER", value = 512 },
|
||||
AXIS_LEFT_TRIGGER_N = { name = "AXIS_LEFT_TRIGGER_N", value = 513 },
|
||||
AXIS_LEFT_TRIGGER_P = { name = "AXIS_LEFT_TRIGGER_P", value = 514 },
|
||||
AXIS_RIGHT_TRIGGER = { name = "AXIS_RIGHT_TRIGGER", value = 515 },
|
||||
AXIS_RIGHT_TRIGGER_N = { name = "AXIS_RIGHT_TRIGGER_N", value = 516 },
|
||||
AXIS_RIGHT_TRIGGER_P = { name = "AXIS_RIGHT_TRIGGER_P", value = 517 },
|
||||
BUTTON_A = { name = "BUTTON_A", value = 518 },
|
||||
BUTTON_B = { name = "BUTTON_B", value = 519 },
|
||||
BUTTON_X = { name = "BUTTON_X", value = 520 },
|
||||
BUTTON_Y = { name = "BUTTON_Y", value = 521 },
|
||||
BUTTON_BACK = { name = "BUTTON_BACK", value = 522 },
|
||||
BUTTON_GUIDE = { name = "BUTTON_GUIDE", value = 523 },
|
||||
BUTTON_START = { name = "BUTTON_START", value = 524 },
|
||||
BUTTON_LEFT_STICK = { name = "BUTTON_LEFT_STICK", value = 525 },
|
||||
BUTTON_RIGHT_STICK = { name = "BUTTON_RIGHT_STICK", value = 526 },
|
||||
BUTTON_LEFT_BUMPER = { name = "BUTTON_LEFT_BUMPER", value = 527 },
|
||||
BUTTON_RIGHT_BUMPER = { name = "BUTTON_RIGHT_BUMPER", value = 528 },
|
||||
DPAD_UP = { name = "DPAD_UP", value = 529 },
|
||||
DPAD_DOWN = { name = "DPAD_DOWN", value = 530 },
|
||||
DPAD_LEFT = { name = "DPAD_LEFT", value = 531 },
|
||||
DPAD_RIGHT = { name = "DPAD_RIGHT", value = 532 }
|
||||
}
|
||||
|
||||
GAMEPAD_0_MIN = 500 -- Lowest value
|
||||
GAMEPAD_0_MAX = 532 -- Highest value, not the number of items in the table.
|
||||
|
||||
GAMEPAD_1 = utilDeepCopy(GAMEPAD_0)
|
||||
GAMEPAD_2 = utilDeepCopy(GAMEPAD_0)
|
||||
GAMEPAD_3 = utilDeepCopy(GAMEPAD_0)
|
||||
|
||||
for key, value in pairs(GAMEPAD_0) do
|
||||
GAMEPAD_1[key].value = GAMEPAD_1[key].value + 100
|
||||
GAMEPAD_2[key].value = GAMEPAD_2[key].value + 200
|
||||
GAMEPAD_3[key].value = GAMEPAD_3[key].value + 300
|
||||
-- Controller codes are built from the engine's layout: SINGE_GAMEPAD_BASE plus
|
||||
-- SINGE_GAMEPAD_STRIDE per controller; each axis takes SINGE_AXIS_STRIDE codes
|
||||
-- (axis, negative direction, positive direction) and buttons follow at
|
||||
-- SINGE_GAMEPAD_BUTTON_OFFSET in SDL's button order.
|
||||
local function gamepadAxisCodes(axis, name, negative, positive)
|
||||
local base = SINGE_GAMEPAD_BASE + axis * SINGE_AXIS_STRIDE
|
||||
return {
|
||||
{ name, base }, { name .. "_" .. negative, base + 1 }, { name .. "_" .. positive, base + 2 }
|
||||
}
|
||||
end
|
||||
|
||||
GAMEPAD_1_MIN = GAMEPAD_0_MAX + 100
|
||||
GAMEPAD_1_MAX = GAMEPAD_0_MAX + 100
|
||||
local gamepadCodes = {}
|
||||
for _, entry in ipairs(gamepadAxisCodes(0, "AXIS_LEFT_X", "L", "R")) do gamepadCodes[#gamepadCodes + 1] = entry end
|
||||
for _, entry in ipairs(gamepadAxisCodes(1, "AXIS_LEFT_Y", "U", "D")) do gamepadCodes[#gamepadCodes + 1] = entry end
|
||||
for _, entry in ipairs(gamepadAxisCodes(2, "AXIS_RIGHT_X", "L", "R")) do gamepadCodes[#gamepadCodes + 1] = entry end
|
||||
for _, entry in ipairs(gamepadAxisCodes(3, "AXIS_RIGHT_Y", "U", "D")) do gamepadCodes[#gamepadCodes + 1] = entry end
|
||||
for _, entry in ipairs(gamepadAxisCodes(4, "AXIS_LEFT_TRIGGER", "N", "P")) do gamepadCodes[#gamepadCodes + 1] = entry end
|
||||
for _, entry in ipairs(gamepadAxisCodes(5, "AXIS_RIGHT_TRIGGER", "N", "P")) do gamepadCodes[#gamepadCodes + 1] = entry end
|
||||
|
||||
GAMEPAD_2_MIN = GAMEPAD_0_MAX + 200
|
||||
GAMEPAD_2_MAX = GAMEPAD_0_MAX + 200
|
||||
local gamepadButtons = {
|
||||
"BUTTON_A", "BUTTON_B", "BUTTON_X", "BUTTON_Y", "BUTTON_BACK", "BUTTON_GUIDE", "BUTTON_START",
|
||||
"BUTTON_LEFT_STICK", "BUTTON_RIGHT_STICK", "BUTTON_LEFT_BUMPER", "BUTTON_RIGHT_BUMPER",
|
||||
"DPAD_UP", "DPAD_DOWN", "DPAD_LEFT", "DPAD_RIGHT"
|
||||
}
|
||||
for index, name in ipairs(gamepadButtons) do
|
||||
gamepadCodes[#gamepadCodes + 1] = { name, SINGE_GAMEPAD_BASE + SINGE_GAMEPAD_BUTTON_OFFSET + index - 1 }
|
||||
end
|
||||
|
||||
GAMEPAD_3_MIN = GAMEPAD_0_MAX + 300
|
||||
GAMEPAD_3_MAX = GAMEPAD_0_MAX + 300
|
||||
GAMEPAD_0 = {}
|
||||
for _, entry in ipairs(gamepadCodes) do
|
||||
GAMEPAD_0[entry[1]] = { name = entry[1], value = entry[2] }
|
||||
end
|
||||
GAMEPAD_1 = utilOffsetTable(GAMEPAD_0, SINGE_GAMEPAD_STRIDE)
|
||||
GAMEPAD_2 = utilOffsetTable(GAMEPAD_0, SINGE_GAMEPAD_STRIDE * 2)
|
||||
GAMEPAD_3 = utilOffsetTable(GAMEPAD_0, SINGE_GAMEPAD_STRIDE * 3)
|
||||
|
||||
GAMEPAD_AXIS_LEFT_X = 0
|
||||
GAMEPAD_AXIS_LEFT_Y = 1
|
||||
|
|
@ -412,47 +399,57 @@ GAMEPAD_AXIS_RIGHT_Y = 3
|
|||
GAMEPAD_AXIS_LEFT_TRIGGER = 4
|
||||
GAMEPAD_AXIS_RIGHT_TRIGGER = 5
|
||||
|
||||
MOUSE_0 = {
|
||||
BUTTON_LEFT = { name = "BUTTON_LEFT", value = 1000 },
|
||||
BUTTON_RIGHT = { name = "BUTTON_RIGHT", value = 1001 },
|
||||
BUTTON_MIDDLE = { name = "BUTTON_MIDDLE", value = 1002 },
|
||||
BUTTON_X1 = { name = "BUTTON_X1", value = 1003 },
|
||||
BUTTON_X2 = { name = "BUTTON_X2", value = 1004 },
|
||||
WHEEL_UP = { name = "WHEEL_UP", value = 1005 },
|
||||
WHEEL_DOWN = { name = "WHEEL_DOWN", value = 1006 }
|
||||
}
|
||||
-- Mouse codes: SINGE_MOUSE_BASE plus SINGE_MOUSE_STRIDE per mouse; buttons then wheel.
|
||||
local mouseNames = { "BUTTON_LEFT", "BUTTON_RIGHT", "BUTTON_MIDDLE", "BUTTON_X1", "BUTTON_X2", "WHEEL_UP", "WHEEL_DOWN" }
|
||||
MOUSE_0 = {}
|
||||
for index, name in ipairs(mouseNames) do
|
||||
MOUSE_0[name] = { name = name, value = SINGE_MOUSE_BASE + index - 1 }
|
||||
end
|
||||
MOUSE_1 = utilOffsetTable(MOUSE_0, SINGE_MOUSE_STRIDE)
|
||||
MOUSE_2 = utilOffsetTable(MOUSE_0, SINGE_MOUSE_STRIDE * 2)
|
||||
MOUSE_3 = utilOffsetTable(MOUSE_0, SINGE_MOUSE_STRIDE * 3)
|
||||
|
||||
MOUSE_0_MIN = 1000
|
||||
MOUSE_0_MAX = 1006
|
||||
|
||||
MOUSE_1 = utilDeepCopy(MOUSE_0)
|
||||
MOUSE_2 = utilDeepCopy(MOUSE_0)
|
||||
MOUSE_3 = utilDeepCopy(MOUSE_0)
|
||||
|
||||
for key, value in pairs(MOUSE_0) do
|
||||
MOUSE_1[key].value = MOUSE_1[key].value + 100
|
||||
MOUSE_2[key].value = MOUSE_2[key].value + 200
|
||||
MOUSE_3[key].value = MOUSE_3[key].value + 300
|
||||
-- Older names for engine functions that gained a setter.
|
||||
if mouseSetEnabled ~= nil then
|
||||
mouseEnable = function() mouseSetEnabled(true) end
|
||||
mouseDisable = function() mouseSetEnabled(false) end
|
||||
end
|
||||
if singeSetPauseKeyEnabled ~= nil then
|
||||
singeEnablePauseKey = function() singeSetPauseKeyEnabled(true) end
|
||||
singeDisablePauseKey = function() singeSetPauseKeyEnabled(false) end
|
||||
end
|
||||
|
||||
MOUSE_1_MIN = MOUSE_0_MAX + 100
|
||||
MOUSE_1_MAX = MOUSE_0_MAX + 100
|
||||
|
||||
MOUSE_2_MIN = MOUSE_0_MAX + 200
|
||||
MOUSE_2_MAX = MOUSE_0_MAX + 200
|
||||
|
||||
MOUSE_3_MIN = MOUSE_0_MAX + 300
|
||||
MOUSE_3_MAX = MOUSE_0_MAX + 300
|
||||
|
||||
SWITCH_BUTTON4 = 21
|
||||
SWITCH_TILT = 22
|
||||
SWITCH_GRAB = 23
|
||||
|
||||
MOUSE_SINGLE = 100
|
||||
MOUSE_MANY = 200
|
||||
|
||||
RENDER_SMOOTH = 1
|
||||
RENDER_PIXELATED = 0
|
||||
-- Singe 2.20 moved the sprite handle to the first argument. Games written for
|
||||
-- 2.10 can set SINGE_LEGACY_SPRITE_ARGS = true (or LEGACY_SPRITE_ARGS = true in
|
||||
-- games.dat) to keep calling the old way.
|
||||
if SINGE_LEGACY_SPRITE_ARGS and spriteDraw ~= nil then
|
||||
local newSpriteDraw = spriteDraw
|
||||
spriteDraw = function(...)
|
||||
local args = { ... }
|
||||
local id = table.remove(args)
|
||||
return newSpriteDraw(id, table.unpack(args))
|
||||
end
|
||||
local newSpriteLoop = spriteLoop
|
||||
spriteLoop = function(loop, id) return newSpriteLoop(id, loop) end
|
||||
local newSpriteQuality = spriteQuality
|
||||
spriteQuality = function(smooth, id) return newSpriteQuality(id, smooth) end
|
||||
local newSpriteRotate = spriteRotate
|
||||
spriteRotate = function(angle, id) return newSpriteRotate(id, angle) end
|
||||
local newSpriteRotateAndScale = spriteRotateAndScale
|
||||
spriteRotateAndScale = function(...)
|
||||
local args = { ... }
|
||||
local id = table.remove(args)
|
||||
return newSpriteRotateAndScale(id, table.unpack(args))
|
||||
end
|
||||
local newSpriteScale = spriteScale
|
||||
spriteScale = function(...)
|
||||
local args = { ... }
|
||||
local id = table.remove(args)
|
||||
return newSpriteScale(id, table.unpack(args))
|
||||
end
|
||||
local newSpriteSetFrame = spriteSetFrame
|
||||
spriteSetFrame = function(frame, id) return newSpriteSetFrame(id, frame) end
|
||||
end
|
||||
|
||||
if videoGetLanguageDescription ~= nil then
|
||||
discGetLanguageDescription = videoGetLanguageDescription
|
||||
|
|
@ -462,52 +459,6 @@ end
|
|||
-- Singe 1.xx Features -------------------------------------------------------
|
||||
|
||||
|
||||
if discSetFPS ~= nil then
|
||||
discSetFPS(29.97)
|
||||
discSearch(1)
|
||||
end
|
||||
|
||||
SWITCH_UP = 0
|
||||
SWITCH_LEFT = 1
|
||||
SWITCH_DOWN = 2
|
||||
SWITCH_RIGHT = 3
|
||||
SWITCH_START1 = 4
|
||||
SWITCH_START2 = 5
|
||||
SWITCH_BUTTON1 = 6
|
||||
SWITCH_BUTTON2 = 7
|
||||
SWITCH_BUTTON3 = 8
|
||||
SWITCH_COIN1 = 9
|
||||
SWITCH_COIN2 = 10
|
||||
SWITCH_SKILL1 = 11
|
||||
SWITCH_SKILL2 = 12
|
||||
SWITCH_SKILL3 = 13
|
||||
SWITCH_SERVICE = 14
|
||||
SWITCH_TEST = 15
|
||||
SWITCH_RESET = 16
|
||||
SWITCH_SCREENSHOT = 17
|
||||
SWITCH_QUIT = 18
|
||||
SWITCH_PAUSE = 19
|
||||
SWITCH_CONSOLE = 20
|
||||
SWITCH_BUTTON4 = 21 -- Added in Singe 2.00
|
||||
SWITCH_TILT = 22 -- Added in Singe 2.00
|
||||
|
||||
FONT_QUALITY_SOLID = 1
|
||||
FONT_QUALITY_SHADED = 2
|
||||
FONT_QUALITY_BLENDED = 3
|
||||
|
||||
SOUND_ERROR_INVALID = -1
|
||||
SOUND_REMOVE_HANDLE = -1
|
||||
SOUND_ERROR_FULL = -2
|
||||
|
||||
OVERLAY_NOT_UPDATED = 0
|
||||
OVERLAY_UPDATED = 1
|
||||
|
||||
SINGLE_MOUSE = 100
|
||||
MANY_MOUSE = 200
|
||||
|
||||
MODE_NORMAL = 0
|
||||
MODE_FULL = 1
|
||||
|
||||
-- Make old random number calls still work
|
||||
random = {}
|
||||
random.new = math.random
|
||||
|
|
@ -524,12 +475,18 @@ end
|
|||
|
||||
|
||||
if singeMain ~= nil then
|
||||
local singeThread = coroutine.create(singeMain)
|
||||
onOverlayUpdate = function()
|
||||
coroutine.resume(SINGE_SELF)
|
||||
return(OVERLAY_UPDATED)
|
||||
local ok, err = coroutine.resume(singeThread)
|
||||
if not ok then
|
||||
error(err, 0)
|
||||
end
|
||||
if coroutine.status(singeThread) == "dead" then
|
||||
singeQuit()
|
||||
end
|
||||
return OVERLAY_UPDATED
|
||||
end
|
||||
singeYield = coroutine.yield
|
||||
SINGE_SELF = coroutine.create(singeMain)
|
||||
end
|
||||
|
||||
|
||||
|
|
|
|||
2467
assets/Manual.lyx
2467
assets/Manual.lyx
File diff suppressed because it is too large
Load diff
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
dofile("Singe/Framework.singe")
|
||||
|
||||
lfs = require("lfs")
|
||||
local lfs = require("lfs")
|
||||
|
||||
|
||||
function cleanTitle(a)
|
||||
|
|
@ -133,12 +133,12 @@ function onOverlayUpdate()
|
|||
-- Cabinet image
|
||||
x = CABINET_X + (CABINET_W - spriteGetWidth(SPRITE_CABINET)) * 0.5
|
||||
y = CABINET_Y + (CABINET_H - spriteGetHeight(SPRITE_CABINET)) * 0.5
|
||||
spriteDraw(x, y, SPRITE_CABINET)
|
||||
spriteDraw(SPRITE_CABINET, x, y)
|
||||
|
||||
-- Marquee Image
|
||||
x = MARQUEE_X + (MARQUEE_W - spriteGetWidth(SPRITE_MARQUEE)) * 0.5
|
||||
y = MARQUEE_Y + (MARQUEE_H - spriteGetHeight(SPRITE_MARQUEE)) * 0.5
|
||||
spriteDraw(x, y, SPRITE_MARQUEE)
|
||||
spriteDraw(SPRITE_MARQUEE, x, y)
|
||||
|
||||
-- Attract Mode Video
|
||||
videoDraw(VIDEO_ATTRACT, VIDEO_X, VIDEO_Y, VIDEO_X + VIDEO_W, VIDEO_Y + VIDEO_H)
|
||||
|
|
@ -163,7 +163,7 @@ function onOverlayUpdate()
|
|||
if (t >= TEXT_LINE_TOP) then
|
||||
if (c < TEXT_LINE_LIMIT) then
|
||||
if (handle >= 0) then
|
||||
spriteDraw(TEXT_X, y, handle)
|
||||
spriteDraw(handle, TEXT_X, y)
|
||||
end
|
||||
y = y + TEXT_LINE_HEIGHT + 1
|
||||
c = c + 1
|
||||
|
|
@ -186,6 +186,9 @@ end
|
|||
function onShutdown()
|
||||
unloadGameAssets()
|
||||
saveConfig(not SHUTDOWN_FROM_PUSH)
|
||||
if freeSans18 then
|
||||
fontUnload(freeSans18)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -193,18 +196,32 @@ function saveConfig(showIntro)
|
|||
if GAME_COUNT > 0 then
|
||||
-- Save what game we're currently viewing
|
||||
local cfg = io.open(CONFIG_FILE, "w")
|
||||
if cfg then
|
||||
cfg:write("GAME_SELECTED = " .. GAME_SELECTED .. "\n")
|
||||
cfg:write("SHOW_INTRO = " .. tostring(showIntro) .. "\n")
|
||||
cfg:close()
|
||||
cfg:close()
|
||||
else
|
||||
debugPrint("Unable to write " .. CONFIG_FILE)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function unloadGameAssets()
|
||||
spriteUnload(SPRITE_CABINET)
|
||||
spriteUnload(SPRITE_MARQUEE)
|
||||
videoUnload(VIDEO_ATTRACT)
|
||||
for _, handle in ipairs(TEXT_SPRITE_LIST) do
|
||||
-- Nothing is loaded when there are no games.
|
||||
if SPRITE_CABINET then
|
||||
spriteUnload(SPRITE_CABINET)
|
||||
SPRITE_CABINET = nil
|
||||
end
|
||||
if SPRITE_MARQUEE then
|
||||
spriteUnload(SPRITE_MARQUEE)
|
||||
SPRITE_MARQUEE = nil
|
||||
end
|
||||
if VIDEO_ATTRACT then
|
||||
videoUnload(VIDEO_ATTRACT)
|
||||
VIDEO_ATTRACT = nil
|
||||
end
|
||||
for _, handle in ipairs(TEXT_SPRITE_LIST or {}) do
|
||||
if handle >= 0 then
|
||||
spriteUnload(handle)
|
||||
end
|
||||
|
|
@ -301,7 +318,7 @@ if GAME_COUNT == 0 then
|
|||
debugPrint("No games found! Exiting.")
|
||||
singeQuit()
|
||||
else
|
||||
overlaySetResolution(vldpGetWidth(), vldpGetHeight())
|
||||
overlaySetResolution(discGetWidth(), discGetHeight())
|
||||
|
||||
freeSans18 = fontLoad("Singe/FreeSansBold.ttf", 18)
|
||||
fontQuality(FONT_QUALITY_BLENDED)
|
||||
|
|
@ -363,6 +380,8 @@ else
|
|||
local confattr = lfs.attributes(CONFIG_FILE)
|
||||
if confattr then
|
||||
dofile(CONFIG_FILE)
|
||||
-- A damaged menu.dat must not take the menu down with it.
|
||||
GAME_SELECTED = tonumber(GAME_SELECTED) or 1
|
||||
if GAME_SELECTED > GAME_COUNT then
|
||||
GAME_SELECTED = GAME_COUNT
|
||||
end
|
||||
|
|
|
|||
|
|
@ -69,10 +69,10 @@ function onOverlayUpdate()
|
|||
overlayPrint(1, 3, dldGameList.result)
|
||||
end
|
||||
|
||||
colorForeground(255, 0, 0)
|
||||
colorForeground(255, 0, 0, 255)
|
||||
overlayBox(0, 0, overlayGetWidth() - 1, overlayGetHeight() - 1)
|
||||
|
||||
spriteDraw((overlayGetWidth() - spriteGetWidth(sprServiceMenu)) / 2, 25, sprServiceMenu)
|
||||
spriteDraw(sprServiceMenu, (overlayGetWidth() - spriteGetWidth(sprServiceMenu)) / 2, 25)
|
||||
|
||||
return(OVERLAY_UPDATED)
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ copas.running = true
|
|||
|
||||
dldGameList = get("https://kangaroopunch.com/api/singeSoftware")
|
||||
|
||||
overlaySetResolution(vldpGetWidth(), vldpGetHeight())
|
||||
overlaySetResolution(discGetWidth(), discGetHeight())
|
||||
|
||||
DISC_SINGE_LOGO = 140
|
||||
|
||||
|
|
|
|||
|
|
@ -26,27 +26,29 @@
|
|||
|
||||
DEAD_ZONE = 15000
|
||||
|
||||
-- One table per switch, in the order the engine numbers them (SWITCH_UP = 0 ...).
|
||||
-- Each entry is a { name, value } pair from the SCANCODE, GAMEPAD_N, or MOUSE_N tables.
|
||||
INPUT_UP = { SCANCODE.UP, SCANCODE.KP_8, GAMEPAD_0.AXIS_LEFT_Y_U, GAMEPAD_0.AXIS_RIGHT_Y_U, GAMEPAD_0.DPAD_UP }
|
||||
INPUT_LEFT = { SCANCODE.LEFT, SCANCODE.KP_4, GAMEPAD_0.AXIS_LEFT_X_L, GAMEPAD_0.AXIS_RIGHT_X_L, GAMEPAD_0.DPAD_LEFT }
|
||||
INPUT_DOWN = { SCANCODE.DOWN, SCANCODE.KP_2, GAMEPAD_0.AXIS_LEFT_Y_D, GAMEPAD_0.AXIS_RIGHT_Y_D, GAMEPAD_0.DPAD_DOWN }
|
||||
INPUT_RIGHT = { SCANCODE.RIGHT, SCANCODE.KP_6, GAMEPAD_0.AXIS_LEFT_X_R, GAMEPAD_0.AXIS_RIGHT_X_R, GAMEPAD_0.DPAD_RIGHT }
|
||||
INPUT_1P_COIN = { SCANCODE.MAIN_5, SCANCODE.C, GAMEPAD_0.BUTTON_LEFT_BUMPER }
|
||||
INPUT_2P_COIN = { SCANCODE.MAIN_6 }
|
||||
INPUT_1P_START = { SCANCODE.MAIN_1, GAMEPAD_0.BUTTON_RIGHT_BUMPER }
|
||||
INPUT_2P_START = { SCANCODE.MAIN_2 }
|
||||
INPUT_ACTION_1 = { SCANCODE.SPACE, SCANCODE.LCTRL, GAMEPAD_0.BUTTON_A, MOUSE_0.BUTTON_RIGHT }
|
||||
INPUT_ACTION_2 = { SCANCODE.LALT, GAMEPAD_0.BUTTON_B, MOUSE_0.BUTTON_MIDDLE }
|
||||
INPUT_ACTION_3 = { SCANCODE.LSHIFT, GAMEPAD_0.BUTTON_X, MOUSE_0.BUTTON_LEFT }
|
||||
INPUT_ACTION_4 = { SCANCODE.RSHIFT, GAMEPAD_0.BUTTON_Y, MOUSE_0.BUTTON_X1 }
|
||||
INPUT_1P_COIN = { SCANCODE.MAIN_5, SCANCODE.C, GAMEPAD_0.BUTTON_LEFT_BUMPER }
|
||||
INPUT_2P_COIN = { SCANCODE.MAIN_6 }
|
||||
INPUT_SKILL_EASY = { SCANCODE.KP_DIVIDE }
|
||||
INPUT_SKILL_MEDIUM = { SCANCODE.KP_MULTIPLY }
|
||||
INPUT_SKILL_HARD = { SCANCODE.KP_MINUS }
|
||||
INPUT_SERVICE = { SCANCODE.MAIN_9 }
|
||||
INPUT_TEST_MODE = { SCANCODE.F2 }
|
||||
INPUT_RESET_CPU = { SCANCODE.F3 }
|
||||
INPUT_SCREENSHOT = { SCANCODE.F12, SCANCODE.F11, GAMEPAD_0.BUTTON_BACK }
|
||||
INPUT_SCREENSHOT = { SCANCODE.F12, SCANCODE.F11, GAMEPAD_0.BUTTON_BACK } -- F12 is taken by some desktops and debuggers, so F11 works too.
|
||||
INPUT_QUIT = { SCANCODE.ESCAPE, SCANCODE.Q }
|
||||
INPUT_PAUSE = { SCANCODE.P, GAMEPAD_0.BUTTON_START }
|
||||
INPUT_CONSOLE = { SCANCODE.GRAVE }
|
||||
INPUT_ACTION_4 = { SCANCODE.RSHIFT, GAMEPAD_0.BUTTON_Y, MOUSE_0.BUTTON_X1 }
|
||||
INPUT_TILT = { SCANCODE.T }
|
||||
INPUT_GRAB = { SCANCODE.G }
|
||||
|
|
|
|||
277
build-all.sh
277
build-all.sh
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash -x
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Singe 2
|
||||
|
|
@ -40,9 +40,9 @@ function buildAll() {
|
|||
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 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"
|
||||
export PKG_CONFIG_LIBDIR="${G_TARGET}/lib/pkgconfig"
|
||||
|
||||
|
|
@ -55,7 +55,6 @@ function buildAll() {
|
|||
sudo chroot ${SYSROOT} apt-get -y install libasound-dev libxi-dev libvdpau-dev
|
||||
fi
|
||||
|
||||
if [[ 1 == 1 ]]; then
|
||||
pushd thirdparty/bzip2
|
||||
clearAndEnterBuild
|
||||
cmake ${COMMON} \
|
||||
|
|
@ -92,7 +91,6 @@ if [[ 1 == 1 ]]; then
|
|||
../build/cmake
|
||||
make install
|
||||
popd
|
||||
fi
|
||||
|
||||
pushd thirdparty/SDL2
|
||||
clearAndEnterBuild
|
||||
|
|
@ -129,7 +127,7 @@ fi
|
|||
-DWEBP_BUILD_WEBPMUX=off \
|
||||
-DWEBP_BUILD_EXTRAS=off \
|
||||
..
|
||||
# 'make install' failes on zlib, which we don't want anyway.
|
||||
# 'make install' fails on zlib, which we don't want anyway.
|
||||
make
|
||||
cp -f ../include/SDL_image.h "${G_TARGET}/include/SDL2/."
|
||||
cp -f libSDL2_image.a "${G_TARGET}/lib/."
|
||||
|
|
@ -244,15 +242,10 @@ fi
|
|||
no-tests \
|
||||
zlib \
|
||||
enable-zstd
|
||||
make install
|
||||
make build_sw
|
||||
make install_sw
|
||||
popd
|
||||
|
||||
lua thirdparty/luasec/src/options.lua -g "${G_TARGET}/include/openssl/ssl.h" > "${G_TARGET}/generated/luasec_options.c"
|
||||
mkdir -p "${G_TARGET}/include/luasocket"
|
||||
cp -f \
|
||||
thirdparty/luasocket/src/*.h \
|
||||
"${G_TARGET}/include/luasocket/."
|
||||
|
||||
if [[ "${OS}" == "pi" ]]; then
|
||||
# Hack to make ffmpeg compile.
|
||||
mkdir -p "${G_TARGET}/include/sys"
|
||||
|
|
@ -287,14 +280,8 @@ fi
|
|||
popd
|
||||
|
||||
pushd thirdparty/ffms2
|
||||
#libtoolize --force
|
||||
#aclocal
|
||||
#autoheader
|
||||
#automake --force-missing --add-missing
|
||||
#autoupdate
|
||||
#autoconf
|
||||
#./autogen.sh
|
||||
#make distclean || true
|
||||
# The configure script is not checked in; generate it once.
|
||||
[[ -x configure ]] || NOCONFIGURE=1 ./autogen.sh
|
||||
clearAndEnterBuild
|
||||
../configure \
|
||||
--prefix=${G_TARGET} \
|
||||
|
|
@ -307,97 +294,14 @@ fi
|
|||
make install-libLTLIBRARIES # This weird target prevents building the command line tools.
|
||||
popd
|
||||
|
||||
# === Known FFMPEG Types ===
|
||||
createExtensionHeader ffmpeg > ${G_GENERATED}/extensions.h
|
||||
|
||||
# === Overlay Font ===
|
||||
createEmbeddedImage font
|
||||
|
||||
# === Window Icon ===
|
||||
createEmbeddedImage icon
|
||||
|
||||
# === Kangaroo Punch Logo ===
|
||||
createEmbeddedImage kangarooPunchLogo
|
||||
|
||||
# === Singe Logo ===
|
||||
createEmbeddedImage singeLogo
|
||||
|
||||
# === Laser Disc ===
|
||||
createEmbeddedImage laserDisc
|
||||
|
||||
# === Magnifying Glass ===
|
||||
createEmbeddedImage magnifyingGlass
|
||||
|
||||
# === "Indexing" Text ===
|
||||
createEmbeddedImage indexing
|
||||
|
||||
# === Singe Menu Font ===
|
||||
createEmbeddedBinary assets/FreeSansBold.ttf ${G_GENERATED}/FreeSansBold_ttf.h FREESANSBOLD_TTF_H
|
||||
|
||||
# === Singe Menu Background Video ===
|
||||
ffmpeg -i "assets/Singe Engine Intro.mpg" -filter:v 'crop=ih/3*4:ih' -vf scale=720:480 -c:v libx264 -c:a aac -f matroska ${G_TARGET}/temp1.mkv
|
||||
ffmpeg -i assets/180503_01_PurpleGrid.mp4 -filter:v 'crop=ih/3*4:ih' -vf scale=720:480 -c:v libx264 -c:a aac -f matroska ${G_TARGET}/temp2.mkv
|
||||
ffmpeg -f concat -safe 0 -i <(echo -e "file ${G_TARGET}/temp1.mkv\nfile ${G_TARGET}/temp2.mkv\n") -c copy ${G_TARGET}/menuBackground.mkv
|
||||
createEmbeddedBinary ${G_TARGET}/menuBackground.mkv ${G_GENERATED}/menuBackground_mkv.h MENUBACKGROUND_MKV_H
|
||||
rm ${G_TARGET}/temp1.mkv ${G_TARGET}/temp2.mkv ${G_TARGET}/menuBackground.mkv
|
||||
|
||||
# === LuaSocket ===
|
||||
createEmbeddedBinary thirdparty/luasocket/src/ftp.lua ${G_GENERATED}/ftp_lua.h FTP_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasocket/src/headers.lua ${G_GENERATED}/headers_lua.h HEADERS_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasocket/src/http.lua ${G_GENERATED}/http_lua.h HTTP_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasocket/src/ltn12.lua ${G_GENERATED}/ltn12_lua.h LTN12_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasocket/src/mbox.lua ${G_GENERATED}/mbox_lua.h MBOX_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasocket/src/mime.lua ${G_GENERATED}/mime_lua.h MIME_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasocket/src/smtp.lua ${G_GENERATED}/smtp_lua.h SMTP_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasocket/src/socket.lua ${G_GENERATED}/socket_lua.h SOCKET_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasocket/src/tp.lua ${G_GENERATED}/tp_lua.h TP_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasocket/src/url.lua ${G_GENERATED}/url_lua.h URL_LUA_H
|
||||
|
||||
# === LuaSec ===
|
||||
createEmbeddedBinary thirdparty/luasec/src/https.lua ${G_GENERATED}/https_lua.h HTTPS_LUA_H
|
||||
createEmbeddedBinary thirdparty/luasec/src/ssl.lua ${G_GENERATED}/ssl_lua.h SSL_LUA_H
|
||||
|
||||
# === LuaRS232 ===
|
||||
createEmbeddedBinary thirdparty/librs232/bindings/lua/rs232.lua ${G_GENERATED}/rs232_lua.h RS232_LUA_H
|
||||
|
||||
# === Copas ===
|
||||
createEmbeddedBinary thirdparty/copas/src/copas.lua ${G_GENERATED}/copas_lua.h COPAS_LUA_H
|
||||
createEmbeddedBinary thirdparty/copas/src/copas/ftp.lua ${G_GENERATED}/copas_ftp_lua.h COPAS_FTP_LUA_H copas
|
||||
createEmbeddedBinary thirdparty/copas/src/copas/http.lua ${G_GENERATED}/copas_http_lua.h COPAS_HTTP_LUA_H copas
|
||||
createEmbeddedBinary thirdparty/copas/src/copas/smtp.lua ${G_GENERATED}/copas_smtp_lua.h COPAS_SMTP_LUA_H copas
|
||||
createEmbeddedBinary thirdparty/copas/src/copas/lock.lua ${G_GENERATED}/copas_lock_lua.h COPAS_LOCK_LUA_H copas
|
||||
createEmbeddedBinary thirdparty/copas/src/copas/queue.lua ${G_GENERATED}/copas_queue_lua.h COPAS_QUEUE_LUA_H copas
|
||||
createEmbeddedBinary thirdparty/copas/src/copas/semaphore.lua ${G_GENERATED}/copas_semaphore_lua.h COPAS_SEMAPHORE_LUA_H copas
|
||||
createEmbeddedBinary thirdparty/copas/src/copas/timer.lua ${G_GENERATED}/copas_timer_lua.h COPAS_TIMER_LUA_H copas
|
||||
|
||||
# === binaryheap.lua ===
|
||||
createEmbeddedBinary thirdparty/binaryheap.lua/src/binaryheap.lua ${G_GENERATED}/binaryheap_lua.h BINARYHEAP_LUA_H
|
||||
|
||||
# === timerwheel.lua ===
|
||||
createEmbeddedBinary thirdparty/timerwheel.lua/src/timerwheel/timerwheel.lua ${G_GENERATED}/timerwheel_lua.h TIMERWHEEL_LUA_H
|
||||
|
||||
# === json.lua ===
|
||||
createEmbeddedBinary thirdparty/json.lua/json.lua ${G_GENERATED}/json_lua.h JSON_LUA_H
|
||||
|
||||
# === Singe Framework ===
|
||||
createEmbeddedBinary assets/Framework.singe ${G_GENERATED}/Framework_singe.h FRAMEWORK_SINGE_H
|
||||
|
||||
# === Default Config ===
|
||||
createEmbeddedBinary assets/controls.cfg ${G_GENERATED}/controls_cfg.h CONTROLS_CFG_H
|
||||
|
||||
# === Singe Menu App ===
|
||||
createEmbeddedBinary assets/Menu.singe ${G_GENERATED}/Menu_singe.h MENU_SINGE_H
|
||||
|
||||
# === Singe Manual ===
|
||||
#lyx -batch -f all -E pdf ${G_GENERATED}/Manual.pdf assets/Manual.lyx
|
||||
#createEmbeddedBinary ${G_GENERATED}/Manual.pdf ${G_GENERATED}/Manual_pdf.h MANUAL_H
|
||||
# Embedded resources, the version header, and the manual are generated by CMake.
|
||||
|
||||
pushd ${G_TARGET}
|
||||
clearAndEnterBuild
|
||||
cmake ${COMMON} ${G_BUILDROOT}
|
||||
cmake ${COMMON} -DKANGAROO_OS=${OS} -DKANGAROO_ARCH=${ARCH} ${G_BUILDROOT}
|
||||
make
|
||||
#upx -9 --force singe2${SUFFIX}
|
||||
mv -f singe2${SUFFIX} ${G_BUILDROOT}/${G_BUILDDIR}/Singe-v2.10-${OS^}-${ARCH}${SUFFIX}
|
||||
# CMake names the binary Singe-v<version>-<Os>-<arch>.
|
||||
mv -f Singe-v*${SUFFIX} ${G_BUILDROOT}/${G_BUILDDIR}/.
|
||||
popd
|
||||
}
|
||||
|
||||
|
|
@ -409,134 +313,16 @@ function clearAndEnterBuild() {
|
|||
}
|
||||
|
||||
|
||||
function createEmbeddedBinary() {
|
||||
local BINFILE=$1
|
||||
local SOURCEFILE=$2 # This is assumed to be an absolute path
|
||||
local BLOCKER=$3
|
||||
local PREFIX=$4
|
||||
local FILENAME=$(basename ${BINFILE})
|
||||
local DIRNAME=$(dirname ${BINFILE})
|
||||
|
||||
outputLicense > ${SOURCEFILE}
|
||||
outputHeader ${BLOCKER} >> ${SOURCEFILE}
|
||||
printf "\n#ifdef EMBED_HERE\n\n" >> ${SOURCEFILE}
|
||||
pushd ${DIRNAME}
|
||||
xxd -i ${FILENAME} >> ${SOURCEFILE}
|
||||
popd
|
||||
if [[ ! -z ${PREFIX} ]]; then
|
||||
PREFIX=${PREFIX}_
|
||||
sed -i "s/unsigned char /unsigned char ${PREFIX}/" ${SOURCEFILE}
|
||||
sed -i "s/unsigned int /unsigned int ${PREFIX}/" ${SOURCEFILE}
|
||||
fi
|
||||
printf "\n#else // EMBED_HERE\n\n" >> ${SOURCEFILE}
|
||||
printf "extern unsigned char ${PREFIX}${FILENAME/\./_}[];\n" >> ${SOURCEFILE}
|
||||
printf "extern unsigned int ${PREFIX}${FILENAME/\./_}_len;\n" >> ${SOURCEFILE}
|
||||
printf "\n#endif // EMBED_HERE\n\n" >> ${SOURCEFILE}
|
||||
outputFooter ${BLOCKER} >> ${SOURCEFILE}
|
||||
}
|
||||
|
||||
|
||||
function createEmbeddedImage() {
|
||||
local BASENAME=$1
|
||||
|
||||
pushd assets
|
||||
convert -flatten -background rgba\(0,0,0,0\) ${BASENAME}.xcf ${G_GENERATED}/${BASENAME}.png
|
||||
createEmbeddedBinary ${G_GENERATED}/${BASENAME}.png ${G_GENERATED}/${BASENAME}.h ${BASENAME^^}_H
|
||||
rm ${G_GENERATED}/${BASENAME}.png
|
||||
popd
|
||||
}
|
||||
|
||||
|
||||
function createExtensionHeader() {
|
||||
local FFMPEG=$1
|
||||
local a=
|
||||
local c=0
|
||||
|
||||
outputLicense
|
||||
outputHeader FFMPEG_EXTENSIONS_H
|
||||
printf "static char *ffmpegExtensions[] = {\n"
|
||||
printf "\t"
|
||||
getExtensions "${FFMPEG}" | sort | uniq -u | while read a; do
|
||||
printf "\"${a}\", "
|
||||
c=$((c + 1))
|
||||
if [[ ${c} -ge 10 ]]; then
|
||||
printf "\n\t"
|
||||
c=0
|
||||
fi
|
||||
done
|
||||
printf "0\n};"
|
||||
outputFooter FFMPEG_EXTENSIONS_H
|
||||
}
|
||||
|
||||
|
||||
function getExtensions() {
|
||||
local FFMPEG=$1
|
||||
local a=
|
||||
local b=
|
||||
local c=
|
||||
local d=
|
||||
local e=
|
||||
local f=
|
||||
local g=
|
||||
|
||||
"${FFMPEG}" -demuxers 2> /dev/null | while read a b c; do
|
||||
if [[ "${a}x" == "Dx" ]]; then
|
||||
"${FFMPEG}" -h demuxer=${b} 2> /dev/null | grep "Common extensions" | while read d e f; do
|
||||
g=${f/./}
|
||||
echo -e "${g//,/\\n}"
|
||||
done
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
function outputFooter() {
|
||||
local BLOCKER=$1
|
||||
|
||||
printf "\n#pragma GCC diagnostic pop\n\n\n#endif // ${BLOCKER}\n"
|
||||
}
|
||||
|
||||
|
||||
function outputHeader() {
|
||||
local BLOCKER=$1
|
||||
|
||||
printf "\n\n#ifndef ${BLOCKER}\n#define ${BLOCKER}\n\n\n"
|
||||
printf "// ===== THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT =====\n\n\n"
|
||||
printf "#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-variable\"\n"
|
||||
}
|
||||
|
||||
|
||||
function outputLicense() {
|
||||
cat <<- LICENSE
|
||||
/*
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
*/
|
||||
LICENSE
|
||||
}
|
||||
|
||||
|
||||
# -e = stop script on errors
|
||||
# -u = stop script on undefined variable
|
||||
# -o pipefail = stop pipeline if any step fails
|
||||
set -e
|
||||
set -euo pipefail
|
||||
export MAKEFLAGS="${MAKEFLAGS:--j$(nproc)}"
|
||||
|
||||
if [[ ! -x ../toolchains/toolchains.sh ]]; then
|
||||
echo "error: ../toolchains/toolchains.sh not found (see INSTALL)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
mkdir -p ${G_BUILDDIR}
|
||||
|
|
@ -544,21 +330,28 @@ mkdir -p ${G_BUILDDIR}
|
|||
|
||||
# These are required for the build.
|
||||
sudo apt-get install -y \
|
||||
xxd \
|
||||
imagemagick \
|
||||
lua5.4 \
|
||||
lyx \
|
||||
ffmpeg \
|
||||
asciidoctor \
|
||||
ruby-asciidoctor-pdf \
|
||||
autoconf \
|
||||
automake \
|
||||
libtool \
|
||||
libasound-dev \
|
||||
libxi-dev \
|
||||
libvdpau-dev \
|
||||
upx-ucl
|
||||
libvdpau-dev
|
||||
|
||||
|
||||
buildAll linux x86_64 2>&1 | tee ${G_BUILDDIR}/linux-x86_64.log
|
||||
buildAll macos aarch64 2>&1 | tee ${G_BUILDDIR}/macos-aarch64.log
|
||||
buildAll pi aarch64 2>&1 | tee ${G_BUILDDIR}/pi-aarch64.log
|
||||
buildAll windows x86_64 2>&1 | tee ${G_BUILDDIR}/windows-x86_64.log
|
||||
# Usage: build-all.sh [os arch] (default: every supported platform)
|
||||
if [[ $# -eq 2 ]]; then
|
||||
buildAll "$1" "$2" 2>&1 | tee ${G_BUILDDIR}/$1-$2.log
|
||||
else
|
||||
buildAll linux x86_64 2>&1 | tee ${G_BUILDDIR}/linux-x86_64.log
|
||||
buildAll macos aarch64 2>&1 | tee ${G_BUILDDIR}/macos-aarch64.log
|
||||
buildAll pi aarch64 2>&1 | tee ${G_BUILDDIR}/pi-aarch64.log
|
||||
buildAll windows x86_64 2>&1 | tee ${G_BUILDDIR}/windows-x86_64.log
|
||||
fi
|
||||
|
||||
|
||||
# === UNSUPPORTED ===
|
||||
|
|
|
|||
26
build-docs.sh
Executable file
26
build-docs.sh
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build the Singe manual from docs/Manual.adoc.
|
||||
# Produces docs/Manual.html and docs/Manual.pdf. The version comes from CMakeLists.txt.
|
||||
set -euo pipefail
|
||||
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
src=$here/docs/Manual.adoc
|
||||
|
||||
if [[ ! -f $src ]]; then
|
||||
echo "error: $src not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
for tool in asciidoctor asciidoctor-pdf; do
|
||||
if ! command -v "$tool" > /dev/null; then
|
||||
echo "error: $tool not found (gem install asciidoctor asciidoctor-pdf rouge)" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
version=$(sed -n 's/^project(singe2 VERSION \([0-9.]*\).*/\1/p' "$here/CMakeLists.txt")
|
||||
|
||||
asciidoctor -a revnumber="$version" "$src" -o "$here/docs/Manual.html"
|
||||
asciidoctor-pdf -a revnumber="$version" "$src" -o "$here/docs/Manual.pdf"
|
||||
|
||||
echo "built:"
|
||||
ls -la "$here/docs/Manual.html" "$here/docs/Manual.pdf"
|
||||
61
cmake/embed.cmake
Normal file
61
cmake/embed.cmake
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# 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.
|
||||
|
||||
file(READ "${INPUT}" content HEX)
|
||||
string(LENGTH "${content}" hexLength)
|
||||
math(EXPR byteLength "${hexLength} / 2")
|
||||
|
||||
# 0x00, 0x01, ... twelve per line.
|
||||
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " bytes "${content}")
|
||||
string(REGEX REPLACE "((0x[0-9a-f][0-9a-f], ){12})" "\\1\n\t" bytes "${bytes}")
|
||||
string(REGEX REPLACE ", $" "" bytes "${bytes}")
|
||||
string(REGEX REPLACE ",\n\t$" "" bytes "${bytes}")
|
||||
|
||||
file(WRITE "${OUTPUT}" "/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-${COPYRIGHT_END_YEAR} 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ${GUARD}
|
||||
#define ${GUARD}
|
||||
|
||||
|
||||
// ===== THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT =====
|
||||
|
||||
|
||||
#ifdef EMBED_HERE
|
||||
|
||||
const unsigned char ${SYMBOL}[] = {
|
||||
${bytes}
|
||||
};
|
||||
const unsigned int ${SYMBOL}_len = ${byteLength};
|
||||
|
||||
#else // EMBED_HERE
|
||||
|
||||
extern const unsigned char ${SYMBOL}[];
|
||||
extern const unsigned int ${SYMBOL}_len;
|
||||
|
||||
#endif // EMBED_HERE
|
||||
|
||||
|
||||
#endif // ${GUARD}
|
||||
")
|
||||
8
cmake/runToFile.cmake
Normal file
8
cmake/runToFile.cmake
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# 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
|
||||
|
||||
execute_process(COMMAND ${COMMAND} ${ARGS} OUTPUT_FILE "${OUTPUT}" RESULT_VARIABLE result)
|
||||
if(NOT result EQUAL 0)
|
||||
file(REMOVE "${OUTPUT}")
|
||||
message(FATAL_ERROR "${COMMAND} failed with status ${result}")
|
||||
endif()
|
||||
3454
docs/Manual.adoc
Normal file
3454
docs/Manual.adoc
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -15,20 +15,20 @@ pixelLow = 0
|
|||
pixelHigh = 1
|
||||
pixelUnknown = 2
|
||||
|
||||
sprLightOn = spriteLoad("ActionMax/sprite_LightOn.png")
|
||||
sprLightOff = spriteLoad("ActionMax/sprite_LightOff.png")
|
||||
sprActionMax = spriteLoad("ActionMax/sprite_ActionMax.png")
|
||||
sprCrosshair = spriteLoad("ActionMax/sprite_Crosshair.png")
|
||||
sprBullet = spriteLoad("ActionMax/sprite_Bullet.png")
|
||||
sprBoxArt = spriteLoad("ActionMax/sprite_" .. gameID .. ".png")
|
||||
sprLightOn = spriteLoad(DIR .. "sprite_LightOn.png")
|
||||
sprLightOff = spriteLoad(DIR .. "sprite_LightOff.png")
|
||||
sprActionMax = spriteLoad(DIR .. "sprite_ActionMax.png")
|
||||
sprCrosshair = spriteLoad(DIR .. "sprite_Crosshair.png")
|
||||
sprBullet = spriteLoad(DIR .. "sprite_Bullet.png")
|
||||
sprBoxArt = spriteLoad(DIR .. "sprite_" .. gameID .. ".png")
|
||||
|
||||
sndActionMax = soundLoad("ActionMax/sound_ActionMax.wav")
|
||||
sndSteadyAim = soundLoad("ActionMax/sound_ASteadyAimIsCritical.wav")
|
||||
sndGetReady = soundLoad("ActionMax/sound_GetReadyForAction.wav")
|
||||
sndGunShot = soundLoad("ActionMax/sound_Gunshot.wav")
|
||||
sndGoodHit = soundLoad("ActionMax/sound_GoodHit.wav")
|
||||
sndBadHit = soundLoad("ActionMax/sound_BadHit.wav")
|
||||
sndGameOver = soundLoad("ActionMax/sound_GameOver.wav")
|
||||
sndActionMax = soundLoad(DIR .. "sound_ActionMax.wav")
|
||||
sndSteadyAim = soundLoad(DIR .. "sound_ASteadyAimIsCritical.wav")
|
||||
sndGetReady = soundLoad(DIR .. "sound_GetReadyForAction.wav")
|
||||
sndGunShot = soundLoad(DIR .. "sound_Gunshot.wav")
|
||||
sndGoodHit = soundLoad(DIR .. "sound_GoodHit.wav")
|
||||
sndBadHit = soundLoad(DIR .. "sound_BadHit.wav")
|
||||
sndGameOver = soundLoad(DIR .. "sound_GameOver.wav")
|
||||
|
||||
mouseX = 0
|
||||
mouseY = 0
|
||||
|
|
@ -63,11 +63,11 @@ thisSeconds = 0
|
|||
lastSeconds = 0
|
||||
heartbeat = false
|
||||
|
||||
fntBlueStone20 = fontLoad("ActionMax/font_BlueStone.ttf", 20)
|
||||
fntChemRea16 = fontLoad("ActionMax/font_chemrea.ttf", 16)
|
||||
fntChemRea32 = fontLoad("ActionMax/font_chemrea.ttf", 32)
|
||||
fntChemRea48 = fontLoad("ActionMax/font_chemrea.ttf", 48)
|
||||
fntLEDReal32 = fontLoad("ActionMax/font_LED_Real.ttf", 32)
|
||||
fntBlueStone20 = fontLoad(DIR .. "font_BlueStone.ttf", 20)
|
||||
fntChemRea16 = fontLoad(DIR .. "font_chemrea.ttf", 16)
|
||||
fntChemRea32 = fontLoad(DIR .. "font_chemrea.ttf", 32)
|
||||
fntChemRea48 = fontLoad(DIR .. "font_chemrea.ttf", 48)
|
||||
fntLEDReal32 = fontLoad(DIR .. "font_LED_Real.ttf", 32)
|
||||
|
||||
colorBackground(0, 0, 0, 0)
|
||||
fontQuality(FONT_QUALITY_BLENDED)
|
||||
|
|
@ -170,51 +170,6 @@ function onInputPressed(intWhat)
|
|||
end
|
||||
|
||||
|
||||
--dbgPaused = 0
|
||||
--dbgSpeed = 1
|
||||
|
||||
function onInputReleased(intWhat)
|
||||
|
||||
--[[ We don't use this. All this is debug stuff.
|
||||
|
||||
if (intWhat == SWITCH_BUTTON1) then
|
||||
if (currentState == statePlaying) then
|
||||
discSearch(lengthIntro + lengthGame - 60)
|
||||
discPlay()
|
||||
else
|
||||
if (dbgPaused == 1) then
|
||||
discPlay()
|
||||
dbgPaused = 0
|
||||
else
|
||||
discPause()
|
||||
dbgPaused = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if (intWhat == SWITCH_RIGHT) then
|
||||
if (dbgSpeed < 4) then
|
||||
dbgSpeed = dbgSpeed + 1
|
||||
end
|
||||
discChangeSpeed(dbgSpeed, 1)
|
||||
end
|
||||
if (intWhat == SWITCH_LEFT) then
|
||||
if (dbgSpeed > 1) then
|
||||
dbgSpeed = dbgSpeed - 1
|
||||
end
|
||||
discChangeSpeed(dbgSpeed, 1)
|
||||
end
|
||||
if (intWhat == SWITCH_UP) then
|
||||
discStepBackward()
|
||||
end
|
||||
if (intWhat == SWITCH_DOWN) then
|
||||
discStepForward()
|
||||
end
|
||||
|
||||
--]]
|
||||
|
||||
end
|
||||
|
||||
|
||||
function onMouseMoved(intX, intY, intXrel, intYrel)
|
||||
|
||||
-- Remember the mouse location for use later.
|
||||
|
|
@ -307,9 +262,9 @@ function onOverlayUpdate()
|
|||
|
||||
elseif (currentState == stateTitle) then
|
||||
|
||||
spriteDraw(logoLeft, logoTop, sprActionMax)
|
||||
spriteDraw(boxLeft, boxTop, sprBoxArt)
|
||||
spriteDraw(lastGameLeft, lastGameTop, sprLastGame)
|
||||
spriteDraw(sprActionMax, logoLeft, logoTop)
|
||||
spriteDraw(sprBoxArt, boxLeft, boxTop)
|
||||
spriteDraw(sprLastGame, lastGameLeft, lastGameTop)
|
||||
y = scoreTop
|
||||
fontPrint(scoreLeft, y, " Shots Fired: " .. shotsFired)
|
||||
y = y + scoreHeight
|
||||
|
|
@ -326,16 +281,16 @@ function onOverlayUpdate()
|
|||
end
|
||||
fontPrint(scoreLeft, y, " Game Score: " .. scorePercent .. "%")
|
||||
if (heartbeat) then
|
||||
spriteDraw(pullToStartLeft, pullToStartTop, sprPullToStart)
|
||||
spriteDraw(sprPullToStart, pullToStartLeft, pullToStartTop)
|
||||
end
|
||||
|
||||
elseif (currentState == stateMenu) then
|
||||
|
||||
spriteDraw(selectGameTypeLeft, selectGameTypeTop, sprSelectGameType)
|
||||
spriteDraw(standard1Left, standardLimited1Top, sprStandard1)
|
||||
spriteDraw(standard2Left, standardLimited2Top, sprStandard2)
|
||||
spriteDraw(limited1Left, standardLimited1Top, sprLimited1)
|
||||
spriteDraw(limited2Left, standardLimited2Top, sprLimited2)
|
||||
spriteDraw(sprSelectGameType, selectGameTypeLeft, selectGameTypeTop)
|
||||
spriteDraw(sprStandard1, standard1Left, standardLimited1Top)
|
||||
spriteDraw(sprStandard2, standard2Left, standardLimited2Top)
|
||||
spriteDraw(sprLimited1, limited1Left, standardLimited1Top)
|
||||
spriteDraw(sprLimited2, limited2Left, standardLimited2Top)
|
||||
|
||||
if (currentFrame >= lengthIntro + lengthGame + lengthMenu) then
|
||||
discSearch(lengthIntro + lengthGame + 2)
|
||||
|
|
@ -345,7 +300,7 @@ function onOverlayUpdate()
|
|||
elseif (currentState == stateIntro) then
|
||||
|
||||
if (heartbeat) then
|
||||
spriteDraw(getReadyLeft, getReadyTop, sprGetReady)
|
||||
spriteDraw(sprGetReady, getReadyLeft, getReadyTop)
|
||||
end
|
||||
|
||||
-- Skip into game video when the intro is over.
|
||||
|
|
@ -423,9 +378,9 @@ function onOverlayUpdate()
|
|||
|
||||
-- Do we need to light the light?
|
||||
if (lightDisplay > 0) then
|
||||
spriteDraw(lightLeft, lightTop, sprLightOn)
|
||||
spriteDraw(sprLightOn, lightLeft, lightTop)
|
||||
else
|
||||
spriteDraw(lightLeft, lightTop, sprLightOff)
|
||||
spriteDraw(sprLightOff, lightLeft, lightTop)
|
||||
end
|
||||
|
||||
-- Do we need to draw the ammo display?
|
||||
|
|
@ -440,7 +395,7 @@ function onOverlayUpdate()
|
|||
if (ammoLeft > 0) then
|
||||
bulletStart = overlayGetWidth() - bulletWidth - 5
|
||||
for i=1,ammoLeft do
|
||||
spriteDraw(bulletStart, 0, sprBullet)
|
||||
spriteDraw(sprBullet, bulletStart, 0)
|
||||
bulletStart = bulletStart - bulletWidth
|
||||
end
|
||||
end
|
||||
|
|
@ -448,7 +403,7 @@ function onOverlayUpdate()
|
|||
|
||||
elseif (currentState == stateGameOver) then
|
||||
|
||||
spriteDraw(gameOverLeft, gameOverTop, sprGameOver)
|
||||
spriteDraw(sprGameOver, gameOverLeft, gameOverTop)
|
||||
|
||||
if (currentFrame >= lengthIntro + lengthGame + lengthMenu) then
|
||||
discPause()
|
||||
|
|
@ -462,7 +417,7 @@ function onOverlayUpdate()
|
|||
|
||||
-- Draw gun crosshair (This must be the last thing we draw so it's on top.)
|
||||
if (singeWantsCrosshairs()) then
|
||||
spriteDraw(mouseX - crosshairCenterX, mouseY - crosshairCenterY, sprCrosshair)
|
||||
spriteDraw(sprCrosshair, mouseX - crosshairCenterX, mouseY - crosshairCenterY)
|
||||
end
|
||||
|
||||
return(OVERLAY_UPDATED)
|
||||
|
|
|
|||
18
patches/README
Normal file
18
patches/README
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Game Patches
|
||||
============
|
||||
|
||||
These are corrected copies of scripts from third party Singe games. They
|
||||
are not part of the engine build; each one replaces the same named file
|
||||
inside an installed game. Package one as a ".patch" archive (see
|
||||
"Packaging Your Game" in the manual) or copy the file over the original.
|
||||
|
||||
ActionMax/Emulator.singe
|
||||
Shared emulator script used by every ActionMax title. Fixes sprite
|
||||
leaks in the original release and uses the Singe 2.20 sprite argument
|
||||
order. The per-game wrapper script must define gameID, backgroundFrame,
|
||||
lengthIntro, lengthGame, lengthMenu, highThreshold, lowThreshold,
|
||||
sensorX, sensorY, sensorLeft, and sensorTop before loading it.
|
||||
|
||||
daitarn_3_singe/Script/toolbox.singe
|
||||
Helper library from "Daitarn 3" (Karis, 2020). The calling script must
|
||||
define OVLW, OVLH, and bPause.
|
||||
|
|
@ -61,7 +61,9 @@ YELLOW = 2
|
|||
GREEN = 3
|
||||
ORANGE = 4
|
||||
WHITE = 5
|
||||
GREY = 6; GRAY = 6
|
||||
GREY = 6
|
||||
GRAY = 6
|
||||
PINK = 7
|
||||
LIGHTBLUE = 7
|
||||
BLACK = 8
|
||||
|
||||
|
|
@ -126,7 +128,7 @@ function clockRnd()
|
|||
q, w = math.modf(j)
|
||||
|
||||
s2 = tostring(w)
|
||||
r = string.find(s2,".")
|
||||
r = string.find(s2, ".", 1, true)
|
||||
|
||||
if (r == nil) then
|
||||
|
||||
|
|
@ -318,12 +320,8 @@ end
|
|||
|
||||
function getMiddle(thisPhrase)
|
||||
|
||||
local x = 0
|
||||
local y = 0
|
||||
local sprite = fontToSprite(thisPhrase)
|
||||
|
||||
x = OVLW/2 - spriteGetWidth(sprite) * 0.5
|
||||
y = OVLH/2 - spriteGetHeight(sprite) * 0.5
|
||||
local x = OVLW/2 - spriteGetWidth(sprite) * 0.5
|
||||
|
||||
spriteUnload(sprite)
|
||||
|
||||
|
|
|
|||
|
|
@ -26,13 +26,8 @@
|
|||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define byte unsigned char
|
||||
|
||||
#define bool unsigned char
|
||||
#define true 1
|
||||
#define false 0
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
#endif // COMMON_H
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
#include "generated/Menu_singe.h"
|
||||
#include "generated/FreeSansBold_ttf.h"
|
||||
#include "generated/menuBackground_mkv.h"
|
||||
//#include "generated/Manual_pdf.h"
|
||||
#include "generated/Manual_pdf.h"
|
||||
|
||||
// LuaSocket
|
||||
#include "generated/ftp_lua.h"
|
||||
|
|
|
|||
404
src/frameFile.c
404
src/frameFile.c
|
|
@ -21,6 +21,9 @@
|
|||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../thirdparty/uthash/src/uthash.h"
|
||||
|
||||
#include "util.h"
|
||||
|
|
@ -28,64 +31,107 @@
|
|||
#include "frameFile.h"
|
||||
|
||||
|
||||
extern bool _confShowCalculated;
|
||||
#define REPORT_COLUMN_WIDTH 8
|
||||
|
||||
|
||||
typedef struct FrameLineS {
|
||||
int32_t videoHandle;
|
||||
int64_t lastFramePlayed;
|
||||
int64_t frame;
|
||||
int64_t frame; // First laserdisc frame number in this segment
|
||||
char *filename;
|
||||
} FrameLineT;
|
||||
|
||||
|
||||
typedef struct FrameFileS {
|
||||
int32_t id;
|
||||
int32_t count;
|
||||
char *videoPath;
|
||||
int32_t currentIndex; // Segment currently playing
|
||||
int64_t lastObservedFrame; // Frame of that segment at the last update
|
||||
FrameLineT *files;
|
||||
UT_hash_handle hh;
|
||||
} FrameFileT;
|
||||
|
||||
|
||||
static FrameFileT *_frameFileHash = NULL;
|
||||
static int32_t _nextId = 0;
|
||||
static FrameFileT *_frameFileHash = NULL;
|
||||
static int32_t _nextId = 0;
|
||||
|
||||
|
||||
void _transferProperties(int32_t oldHandle, int32_t newHandle);
|
||||
static FrameFileT *_getFrameFile(int32_t frameFileHandle, const char *caller);
|
||||
static void _selectSegment(FrameFileT *f, int32_t index, int64_t frame, int32_t *videoHandle);
|
||||
static void _showCalculated(const FrameFileT *f);
|
||||
|
||||
|
||||
void _transferProperties(int32_t oldHandle, int32_t newHandle) {
|
||||
int32_t l = 0;
|
||||
int32_t r = 0;
|
||||
static FrameFileT *_getFrameFile(int32_t frameFileHandle, const char *caller) {
|
||||
FrameFileT *f = NULL;
|
||||
|
||||
if ((oldHandle < 0) || (newHandle < 0)) {
|
||||
// Invalid handle somewhere
|
||||
return;
|
||||
HASH_FIND_INT(_frameFileHash, &frameFileHandle, f);
|
||||
if (!f) {
|
||||
utilDie("No framefile at index %d in %s.", frameFileHandle, caller);
|
||||
}
|
||||
|
||||
// Transfer previous video's properties to this one
|
||||
videoGetVolume(oldHandle, &l, &r);
|
||||
videoSetVolume(newHandle, l, r);
|
||||
if (videoIsPlaying(oldHandle)) {
|
||||
videoPlay(newHandle);
|
||||
} else {
|
||||
videoPause(newHandle);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
// Make segment "index" the active video at "frame", carrying the previous video's state over.
|
||||
static void _selectSegment(FrameFileT *f, int32_t index, int64_t frame, int32_t *videoHandle) {
|
||||
int32_t oldHandle = *videoHandle;
|
||||
int32_t newHandle = f->files[index].videoHandle;
|
||||
int32_t left = 0;
|
||||
int32_t right = 0;
|
||||
int32_t track = 0;
|
||||
|
||||
videoSeek(newHandle, frame);
|
||||
if ((oldHandle >= 0) && (oldHandle != newHandle)) {
|
||||
// Transfer previous video's properties to this one
|
||||
videoGetVolume(oldHandle, &left, &right);
|
||||
videoSetVolume(newHandle, left, right);
|
||||
track = videoGetAudioTrack(oldHandle);
|
||||
if ((track >= 0) && (track < videoGetAudioTracks(newHandle))) {
|
||||
videoSetAudioTrack(newHandle, track);
|
||||
}
|
||||
if (videoIsPlaying(oldHandle)) {
|
||||
videoPlay(newHandle);
|
||||
} else {
|
||||
videoPause(newHandle);
|
||||
}
|
||||
videoPause(oldHandle);
|
||||
}
|
||||
videoPause(oldHandle);
|
||||
f->currentIndex = index;
|
||||
f->lastObservedFrame = frame;
|
||||
*videoHandle = newHandle;
|
||||
}
|
||||
|
||||
|
||||
static void _showCalculated(const FrameFileT *f) {
|
||||
int32_t x = 0;
|
||||
int64_t count = 0;
|
||||
int64_t next = 0;
|
||||
|
||||
// 00000000011111111112222222222333333333344444444445555555555666666666677777777778
|
||||
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
utilSay("Existing Framefile:\n");
|
||||
utilSay(" Start Length End File");
|
||||
utilSay("-------- -------- -------- -------------------------------------------------");
|
||||
for (x = 0; x < f->count; x++) {
|
||||
count = videoGetFrameCount(f->files[x].videoHandle);
|
||||
utilSay("%*" PRId64 " %*" PRId64 " %*" PRId64 " %s", REPORT_COLUMN_WIDTH, f->files[x].frame, REPORT_COLUMN_WIDTH, count, REPORT_COLUMN_WIDTH, f->files[x].frame + count - 1, f->files[x].filename);
|
||||
}
|
||||
utilSay("\nIdeal Framefile:\n");
|
||||
utilSay(" Start File");
|
||||
utilSay("-------- ---------------------------------------------------------------------");
|
||||
for (x = 0; x < f->count; x++) {
|
||||
utilSay("%*" PRId64 " %s", REPORT_COLUMN_WIDTH, next, f->files[x].filename);
|
||||
next += videoGetFrameCount(f->files[x].videoHandle);
|
||||
}
|
||||
utilNewline();
|
||||
}
|
||||
|
||||
|
||||
int64_t frameFileGetFrame(int32_t frameFileHandle, int32_t videoHandle) {
|
||||
FrameFileT *f = _getFrameFile(frameFileHandle, "frameFileGetFrame");
|
||||
int32_t i = 0;
|
||||
FrameFileT *f = NULL;
|
||||
|
||||
// Get our framefile structure
|
||||
HASH_FIND_INT(_frameFileHash, &frameFileHandle, f);
|
||||
if (!f) utilDie("No framefile at index %d in frameFileSeek.", frameFileHandle);
|
||||
|
||||
// Search through loaded video segments
|
||||
for (i=0; i<f->count; i++) {
|
||||
for (i = 0; i < f->count; i++) {
|
||||
if (f->files[i].videoHandle == videoHandle) {
|
||||
// Frame is in this video
|
||||
return videoGetFrame(videoHandle) + f->files[i].frame;
|
||||
|
|
@ -97,193 +143,114 @@ int64_t frameFileGetFrame(int32_t frameFileHandle, int32_t videoHandle) {
|
|||
}
|
||||
|
||||
|
||||
int32_t frameFileInit(void) {
|
||||
return 0; // Nothing to do
|
||||
}
|
||||
|
||||
|
||||
int32_t frameFileLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer, bool showCalculated) {
|
||||
int32_t result = 0;
|
||||
int32_t count = 0;
|
||||
int64_t frame = 0;
|
||||
int64_t next = 0;
|
||||
size_t bytes = 0;
|
||||
char *audio = NULL;
|
||||
char *data = NULL;
|
||||
char *path = NULL;
|
||||
char *offset = NULL;
|
||||
char *frameLine = NULL;
|
||||
char *space = NULL;
|
||||
char *endptr = NULL;
|
||||
char *temp = NULL;
|
||||
FrameLineT *files = NULL;
|
||||
FrameLineT *newFiles = NULL;
|
||||
FrameFileT *frameFile = NULL;
|
||||
int32_t frameFileLoad(const char *filename, const char *indexPath, SDL_Renderer *renderer, bool showCalculated) {
|
||||
int32_t count = 0;
|
||||
int64_t frame = 0;
|
||||
size_t bytes = 0;
|
||||
size_t x = 0;
|
||||
char *audio = NULL;
|
||||
char *data = NULL;
|
||||
char *path = NULL;
|
||||
char *temp = NULL;
|
||||
char *frameLine = NULL;
|
||||
char *space = NULL;
|
||||
char *endptr = NULL;
|
||||
const char *name = NULL;
|
||||
const char *offset = NULL;
|
||||
FrameLineT *files = NULL;
|
||||
FrameLineT *newFiles = NULL;
|
||||
FrameFileT *frameFile = NULL;
|
||||
|
||||
data = utilReadFile(filename, &bytes);
|
||||
if (!data) utilDie("Unable to open framefile: %s", filename);
|
||||
if (!data) {
|
||||
utilDie("Unable to open framefile: %s", filename);
|
||||
}
|
||||
|
||||
// Get path where video files live
|
||||
path = utilReadLine(data, bytes, &offset);
|
||||
if (!path) utilDie("Cannot read video path from framefile!");
|
||||
if (!path) {
|
||||
utilDie("Cannot read video path from framefile!");
|
||||
}
|
||||
utilFixPathSeparators(&path, true);
|
||||
|
||||
// If it's not an absolute path, pre-pend the path to the framefile
|
||||
if ((path[0] != utilGetPathSeparator()) && (path[1] != ':')) {
|
||||
temp = path;
|
||||
count = strlen(filename) - strlen(utilGetLastPathComponent(filename));
|
||||
path = malloc(sizeof(char) * (count + strlen(temp) + 1));
|
||||
memcpy(path, filename, count);
|
||||
memcpy(&path[count], temp, strlen(temp));
|
||||
path[count + strlen(temp)] = 0;
|
||||
temp = utilGetUpToLastPathComponent(filename);
|
||||
free(path);
|
||||
path = utilCreateString("%s%s", temp, path);
|
||||
free(temp);
|
||||
utilFixPathSeparators(&path, true);
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// Read frame offsets and filenames - silently ignore bad lines
|
||||
while ((frameLine = utilReadLine(data, bytes, &offset)) != NULL) {
|
||||
// Covert tabs to spaces
|
||||
for (next=0; next<(int64_t)strlen(frameLine); next++) {
|
||||
if (frameLine[next] == 9) frameLine[next] = 32;
|
||||
// Convert tabs to spaces
|
||||
for (x = 0; frameLine[x] != 0; x++) {
|
||||
if (frameLine[x] == '\t') {
|
||||
frameLine[x] = ' ';
|
||||
}
|
||||
}
|
||||
// Find first space in this file.
|
||||
space = strstr(frameLine, " ");
|
||||
if (space) {
|
||||
// Write a zero in there to make it two fields.
|
||||
*space = 0;
|
||||
// Is the first part an integer?
|
||||
endptr = NULL;
|
||||
frame = strtol(frameLine, &endptr, 10);
|
||||
*space = 32;
|
||||
if (endptr == space) {
|
||||
// Got an integer. Point at filename.
|
||||
space++;
|
||||
while (space[0] == 32) {
|
||||
space++;
|
||||
}
|
||||
// Copy frame number and filename into array
|
||||
newFiles = realloc(files, sizeof(FrameLineT) * (count + 1));
|
||||
if (!newFiles) utilDie("Unable to allocate new framefile entry!");
|
||||
files = newFiles;
|
||||
files[count].lastFramePlayed = 0;
|
||||
files[count].frame = frame;
|
||||
files[count].filename = utilCreateString("%s%s", path, space);
|
||||
// Is this an old m2v/ogg pair?
|
||||
if (strncmp(utilGetFileExtension(files[count].filename), "m2v", 3) == 0) {
|
||||
// Open split video and audio files
|
||||
audio = strdup(files[count].filename);
|
||||
audio[strlen(audio) - 3] = 'o';
|
||||
audio[strlen(audio) - 2] = 'g';
|
||||
audio[strlen(audio) - 1] = 'g';
|
||||
if (utilFileExists(audio)) {
|
||||
files[count].videoHandle = videoLoadWithAudio(files[count].filename, audio, indexPath, stretchVideo, renderer);
|
||||
} else {
|
||||
files[count].videoHandle = videoLoadWithAudio(files[count].filename, NULL, indexPath, stretchVideo, renderer);
|
||||
}
|
||||
// Is the first field an integer followed by a space?
|
||||
frame = strtoll(frameLine, &endptr, 10);
|
||||
space = strchr(frameLine, ' ');
|
||||
if ((space != NULL) && (endptr == space)) {
|
||||
// Got an integer. Point at filename.
|
||||
name = space;
|
||||
while (*name == ' ') {
|
||||
name++;
|
||||
}
|
||||
// Copy frame number and filename into array
|
||||
newFiles = realloc(files, sizeof(FrameLineT) * (size_t)(count + 1));
|
||||
if (!newFiles) {
|
||||
utilDie("Unable to allocate new framefile entry!");
|
||||
}
|
||||
files = newFiles;
|
||||
files[count].frame = frame;
|
||||
files[count].filename = utilCreateString("%s%s", path, name);
|
||||
// Is this an old m2v/ogg pair?
|
||||
audio = NULL;
|
||||
if (utilStricmp(utilGetFileExtension(files[count].filename), "m2v") == 0) {
|
||||
audio = utilCreateString("%.*s.ogg", (int32_t)(strlen(files[count].filename) - strlen("m2v") - 1), files[count].filename);
|
||||
if (!utilFileExists(audio)) {
|
||||
free(audio);
|
||||
audio = NULL;
|
||||
} else {
|
||||
// Open combined video/audio file
|
||||
files[count].videoHandle = videoLoad(files[count].filename, indexPath, stretchVideo, renderer);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
files[count].videoHandle = videoLoad(files[count].filename, audio, indexPath, renderer);
|
||||
free(audio);
|
||||
count++;
|
||||
}
|
||||
free(frameLine);
|
||||
}
|
||||
|
||||
free(data);
|
||||
free(path);
|
||||
|
||||
if (count == 0) {
|
||||
utilDie("Framefile has no video entries: %s", filename);
|
||||
}
|
||||
|
||||
// Allocate new framefile
|
||||
frameFile = malloc(sizeof(FrameFileT));
|
||||
frameFile->id = _nextId;
|
||||
frameFile->count = count;
|
||||
frameFile->videoPath = path;
|
||||
frameFile->files = files;
|
||||
frameFile = calloc(1, sizeof(FrameFileT));
|
||||
if (!frameFile) {
|
||||
utilDie("Unable to allocate framefile.");
|
||||
}
|
||||
frameFile->id = _nextId++;
|
||||
frameFile->count = count;
|
||||
frameFile->currentIndex = -1;
|
||||
frameFile->files = files;
|
||||
HASH_ADD_INT(_frameFileHash, id, frameFile);
|
||||
|
||||
result = _nextId++;
|
||||
|
||||
// Show debug output?
|
||||
if (showCalculated) {
|
||||
// 00000000011111111112222222222333333333344444444445555555555666666666677777777778
|
||||
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
utilSay("Existing Framefile:\n");
|
||||
utilSay(" Start Length End File");
|
||||
utilSay("-------- -------- -------- -------------------------------------------------");
|
||||
for (count=0; count<frameFile->count; count++) {
|
||||
frame = videoGetFrameCount(frameFile->files[count].videoHandle);
|
||||
utilSay("%8ld %8ld %8ld %s", frameFile->files[count].frame, frame, frameFile->files[count].frame + frame, frameFile->files[count].filename);
|
||||
}
|
||||
next = 0;
|
||||
utilSay("\nIdeal Framefile:\n");
|
||||
utilSay(" Start File");
|
||||
utilSay("-------- ---------------------------------------------------------------------");
|
||||
for (count=0; count<frameFile->count; count++) {
|
||||
frame = videoGetFrameCount(frameFile->files[count].videoHandle);
|
||||
utilSay("%8ld %s", next, frameFile->files[count].filename);
|
||||
next += frame + 1;
|
||||
}
|
||||
utilSay("");
|
||||
_showCalculated(frameFile);
|
||||
}
|
||||
|
||||
return result;
|
||||
return frameFile->id;
|
||||
}
|
||||
|
||||
|
||||
int32_t frameFileSeek(int32_t frameFileHandle, int64_t seekFrame, int32_t *videoHandle, int64_t *actualFrame) {
|
||||
int32_t i = 0;
|
||||
int32_t found = -1;
|
||||
FrameFileT *f = NULL;
|
||||
|
||||
// Get our framefile structure
|
||||
HASH_FIND_INT(_frameFileHash, &frameFileHandle, f);
|
||||
if (!f) utilDie("No framefile at index %d in frameFileSeek.", frameFileHandle);
|
||||
|
||||
// Search through loaded video segments
|
||||
|
||||
// Daphne-like framefile searching
|
||||
found = 0;
|
||||
for (i=0; i<f->count; i++) {
|
||||
if (seekFrame >= f->files[i].frame) {
|
||||
found = i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Strict framefile searching
|
||||
for (i=0; i<f->count; i++) {
|
||||
if ((seekFrame >= f->files[i].frame) && (seekFrame <= (f->files[i].frame + videoGetFrameCount(f->files[i].videoHandle)))) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (found >= 0) {
|
||||
// Frame is in this video
|
||||
*actualFrame = seekFrame - f->files[found].frame;
|
||||
//utilSay("Frame %ld found in file %d - %s at %ld", seekFrame, found, f->files[found].filename, *actualFrame);
|
||||
f->files[found].lastFramePlayed = *actualFrame;
|
||||
videoSeek(f->files[found].videoHandle, *actualFrame);
|
||||
// Is this a different video from the previous one?
|
||||
if (*videoHandle != f->files[found].videoHandle) {
|
||||
// Yes
|
||||
_transferProperties(*videoHandle, f->files[found].videoHandle);
|
||||
*videoHandle = f->files[found].videoHandle;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Didn't find it
|
||||
*videoHandle = -1;
|
||||
*actualFrame = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int32_t frameFileQuit(void) {
|
||||
void frameFileQuit(void) {
|
||||
FrameFileT *f = NULL;
|
||||
FrameFileT *t = NULL;
|
||||
|
||||
|
|
@ -291,63 +258,68 @@ int32_t frameFileQuit(void) {
|
|||
HASH_ITER(hh, _frameFileHash, f, t) {
|
||||
frameFileUnload(f->id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t frameFileUnload(int32_t frameFileHandle) {
|
||||
int32_t i = 0;
|
||||
FrameFileT *f = NULL;
|
||||
// Daphne-like framefile searching: the segment is the last one starting at or before the frame.
|
||||
bool frameFileSeek(int32_t frameFileHandle, int64_t seekFrame, int32_t *videoHandle, int64_t *actualFrame) {
|
||||
FrameFileT *f = _getFrameFile(frameFileHandle, "frameFileSeek");
|
||||
int32_t i = 0;
|
||||
int32_t found = 0;
|
||||
int64_t last = 0;
|
||||
|
||||
// Get our framefile structure
|
||||
HASH_FIND_INT(_frameFileHash, &frameFileHandle, f);
|
||||
if (!f) utilDie("No framefile at index %d in frameFileUnload.", frameFileHandle);
|
||||
for (i = 0; i < f->count; i++) {
|
||||
if (seekFrame >= f->files[i].frame) {
|
||||
found = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp inside the segment instead of letting the player wrap it.
|
||||
*actualFrame = seekFrame - f->files[found].frame;
|
||||
last = videoGetFrameCount(f->files[found].videoHandle) - 1;
|
||||
if (*actualFrame < 0) {
|
||||
*actualFrame = 0;
|
||||
}
|
||||
if (*actualFrame > last) {
|
||||
*actualFrame = last;
|
||||
}
|
||||
_selectSegment(f, found, *actualFrame, videoHandle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void frameFileUnload(int32_t frameFileHandle) {
|
||||
FrameFileT *f = _getFrameFile(frameFileHandle, "frameFileUnload");
|
||||
int32_t i = 0;
|
||||
|
||||
// Unload videos
|
||||
for (i=0; i<f->count; i++) {
|
||||
for (i = 0; i < f->count; i++) {
|
||||
free(f->files[i].filename);
|
||||
videoUnload(f->files[i].videoHandle);
|
||||
}
|
||||
|
||||
// Free memory
|
||||
free(f->files);
|
||||
free(f->videoPath);
|
||||
|
||||
// Remove from hash
|
||||
HASH_DEL(_frameFileHash, f);
|
||||
free(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t frameFileUpdate(int32_t frameFileHandle, int32_t *videoHandle) {
|
||||
int32_t i = 0;
|
||||
// Did the current video loop back to the start? If so, move to the next segment.
|
||||
void frameFileUpdate(int32_t frameFileHandle, int32_t *videoHandle) {
|
||||
FrameFileT *f = _getFrameFile(frameFileHandle, "frameFileUpdate");
|
||||
int64_t frame = 0;
|
||||
FrameFileT *f = NULL;
|
||||
|
||||
// Get our framefile structure
|
||||
HASH_FIND_INT(_frameFileHash, &frameFileHandle, f);
|
||||
if (!f) utilDie("No framefile at index %d in frameFileUpdate.", frameFileHandle);
|
||||
|
||||
// Did the current video loop? If so, move to next video.
|
||||
for (i=0; i<f->count; i++) {
|
||||
if (f->files[i].videoHandle == *videoHandle) {
|
||||
frame = videoGetFrame(*videoHandle);
|
||||
if ((f->files[i].lastFramePlayed > frame) && (frame == 0)) {
|
||||
// Switch video files
|
||||
if (i == (f->count - 1)) {
|
||||
i = 0;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
_transferProperties(*videoHandle, f->files[i].videoHandle);
|
||||
*videoHandle = f->files[i].videoHandle;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ((f->currentIndex < 0) || (f->files[f->currentIndex].videoHandle != *videoHandle)) {
|
||||
return;
|
||||
}
|
||||
frame = videoGetFrame(*videoHandle);
|
||||
if (frame < f->lastObservedFrame) {
|
||||
_selectSegment(f, (f->currentIndex + 1) % f->count, 0, videoHandle);
|
||||
} else {
|
||||
f->lastObservedFrame = frame;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,12 +31,11 @@
|
|||
|
||||
|
||||
int64_t frameFileGetFrame(int32_t frameFileHandle, int32_t videoHandle);
|
||||
int32_t frameFileInit(void);
|
||||
int32_t frameFileLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer, bool showCalculated);
|
||||
int32_t frameFileSeek(int32_t frameFileHandle, int64_t seekFrame, int32_t *videoHandle, int64_t *actualFrame);
|
||||
int32_t frameFileQuit(void);
|
||||
int32_t frameFileUnload(int32_t frameFileHandle);
|
||||
int32_t frameFileUpdate(int32_t frameFileHandle, int32_t *videoHandle);
|
||||
int32_t frameFileLoad(const char *filename, const char *indexPath, SDL_Renderer *renderer, bool showCalculated);
|
||||
void frameFileQuit(void);
|
||||
bool frameFileSeek(int32_t frameFileHandle, int64_t seekFrame, int32_t *videoHandle, int64_t *actualFrame);
|
||||
void frameFileUnload(int32_t frameFileHandle);
|
||||
void frameFileUpdate(int32_t frameFileHandle, int32_t *videoHandle);
|
||||
|
||||
|
||||
#endif // FRAMEFILE_H
|
||||
|
|
|
|||
1724
src/main.c
1724
src/main.c
File diff suppressed because it is too large
Load diff
17
src/main.h
17
src/main.h
|
|
@ -25,15 +25,22 @@
|
|||
#define MAIN_H
|
||||
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "singe.h"
|
||||
|
||||
|
||||
ConfigT *cloneConf(ConfigT *conf);
|
||||
#define VOLUME_MIN 0
|
||||
#define VOLUME_MAX 100
|
||||
#define SCALE_FACTOR_MIN 50
|
||||
#define SCALE_FACTOR_MAX 100
|
||||
#define DIRECTORY_MODE 0777
|
||||
|
||||
|
||||
ConfigT *cloneConf(const ConfigT *conf);
|
||||
char *createDataDir(const char *dataDirBase, const char *filename);
|
||||
void destroyConf(ConfigT **confPointer);
|
||||
bool parseSindenString(char **sindenStringPointer, ConfigT *conf);
|
||||
void queueScript(ConfigT *conf);
|
||||
bool isFrameFileName(const char *filename);
|
||||
bool parseSindenString(const char *sindenString, ConfigT *conf);
|
||||
void queueScript(const ConfigT *conf);
|
||||
|
||||
|
||||
#endif // MAIN_H
|
||||
|
|
|
|||
9593
src/singe.c
9593
src/singe.c
File diff suppressed because it is too large
Load diff
17
src/singe.h
17
src/singe.h
|
|
@ -28,22 +28,20 @@
|
|||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "generated/version.h"
|
||||
|
||||
|
||||
// Don't forget to update singe.rc!
|
||||
#define SINGE_VERSION 2.10
|
||||
#define VERSION_STRING "v2.10"
|
||||
#define COPYRIGHT_END_YEAR "2024"
|
||||
#define SINDEN_ARG_MAX 8
|
||||
|
||||
|
||||
enum {
|
||||
// Number of --sindengun arguments selects the border style.
|
||||
typedef enum SindenModeE {
|
||||
SINDEN_WHITE = 1,
|
||||
SINDEN_WHITE_BLACK = 2,
|
||||
SINDEN_CUSTOM_WHITE = 4,
|
||||
SINDEN_CUSTOM_WHITE_BLACK = 5,
|
||||
SINDEN_CUSTOM_WHITE_CUSTOM_BLACK = 8,
|
||||
SINDEN_OPTION_COUNT = 8
|
||||
};
|
||||
SINDEN_CUSTOM_WHITE_CUSTOM_BLACK = 8
|
||||
} SindenModeE;
|
||||
|
||||
|
||||
typedef struct ConfigS {
|
||||
|
|
@ -64,6 +62,7 @@ typedef struct ConfigS {
|
|||
bool noLogos;
|
||||
bool programTracing;
|
||||
bool scriptTracing;
|
||||
bool legacySpriteArgs;
|
||||
int32_t bestRatioIndex;
|
||||
int32_t volumeVldp;
|
||||
int32_t volumeNonVldp;
|
||||
|
|
@ -71,7 +70,7 @@ typedef struct ConfigS {
|
|||
int32_t xResolution;
|
||||
int32_t yResolution;
|
||||
int32_t sindenArgc;
|
||||
int32_t sindenArgv[SINDEN_OPTION_COUNT];
|
||||
int32_t sindenArgv[SINDEN_ARG_MAX];
|
||||
int32_t audioOutputTrack;
|
||||
} ConfigT;
|
||||
|
||||
|
|
|
|||
24
src/singe.rc
24
src/singe.rc
|
|
@ -1,24 +0,0 @@
|
|||
101 ICON "/tmp/icon.ico"
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 2,1,0,0
|
||||
PRODUCTVERSION 2,1,0,0
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Kangaroo Punch Studios"
|
||||
VALUE "FileDescription", "Somewhat Interactive Nostalgic Game Engine"
|
||||
VALUE "FileVersion", "2.10"
|
||||
VALUE "InternalName", "Singe"
|
||||
VALUE "LegalCopyright", "Copyright 2006-2024 Scott C. Duensing"
|
||||
VALUE "OriginalFilename", "singe.exe"
|
||||
VALUE "ProductName", "Singe"
|
||||
VALUE "ProductVersion", "2.10"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
24
src/singe.rc.in
Normal file
24
src/singe.rc.in
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
101 ICON "@GENERATED_DIR@/icon.ico"
|
||||
1 VERSIONINFO
|
||||
FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,0,0
|
||||
PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,0,0
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Kangaroo Punch Studios"
|
||||
VALUE "FileDescription", "Somewhat Interactive Nostalgic Game Engine"
|
||||
VALUE "FileVersion", "@PROJECT_VERSION@"
|
||||
VALUE "InternalName", "Singe"
|
||||
VALUE "LegalCopyright", "Copyright 2006-@SINGE_COPYRIGHT_END_YEAR@ Scott C. Duensing"
|
||||
VALUE "OriginalFilename", "Singe.exe"
|
||||
VALUE "ProductName", "Singe"
|
||||
VALUE "ProductVersion", "@PROJECT_VERSION@"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
454
src/util.c
454
src/util.c
|
|
@ -29,14 +29,13 @@
|
|||
|
||||
#define ourMkdir(p,m) mkdir(p)
|
||||
|
||||
static const int CONSOLE_LINES = 1000;
|
||||
#define CONSOLE_LINES 1000
|
||||
#else
|
||||
#define ourMkdir mkdir
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
|
@ -48,6 +47,29 @@ static bool _outputHappened = false;
|
|||
static FILE *_utilTraceFile = NULL;
|
||||
|
||||
|
||||
static bool _ensureDirectory(const char *path, const mode_t mode);
|
||||
static void _printLine(FILE *stream, const char *fmt, va_list args) __attribute__((format(printf, 2, 0)));
|
||||
|
||||
|
||||
static bool _ensureDirectory(const char *path, const mode_t mode) {
|
||||
struct stat sb;
|
||||
|
||||
if (stat(path, &sb) != 0) {
|
||||
// Does not exist - create it.
|
||||
return (ourMkdir(path, mode) >= 0);
|
||||
}
|
||||
|
||||
return S_ISDIR(sb.st_mode);
|
||||
}
|
||||
|
||||
|
||||
static void _printLine(FILE *stream, const char *fmt, va_list args) {
|
||||
vfprintf(stream, fmt, args);
|
||||
fputc('\n', stream);
|
||||
fflush(stream);
|
||||
}
|
||||
|
||||
|
||||
bool utilChMod(const char *path, const mode_t mode) {
|
||||
bool result = true;
|
||||
|
||||
|
|
@ -62,7 +84,7 @@ bool utilChMod(const char *path, const mode_t mode) {
|
|||
}
|
||||
|
||||
|
||||
char *utilCreateString(char *format, ...) {
|
||||
char *utilCreateString(const char *format, ...) {
|
||||
va_list args;
|
||||
char *string;
|
||||
|
||||
|
|
@ -74,15 +96,18 @@ char *utilCreateString(char *format, ...) {
|
|||
}
|
||||
|
||||
|
||||
__attribute__((__format__(__printf__, 1, 0)))
|
||||
char *utilCreateStringVArgs(char *format, va_list args) {
|
||||
char *utilCreateStringVArgs(const char *format, va_list args) {
|
||||
va_list argsCopy;
|
||||
int32_t size = 0;
|
||||
char *buffer = NULL;
|
||||
int32_t size = 0;
|
||||
char *buffer = NULL;
|
||||
|
||||
va_copy(argsCopy, args);
|
||||
size = vsnprintf(NULL, 0, format, argsCopy) + 1;
|
||||
size = vsnprintf(NULL, 0, format, argsCopy);
|
||||
va_end(argsCopy);
|
||||
if (size < 0) {
|
||||
return NULL;
|
||||
}
|
||||
size++;
|
||||
buffer = calloc(1, (size_t)size);
|
||||
if (buffer) {
|
||||
vsnprintf(buffer, (size_t)size, format, args);
|
||||
|
|
@ -92,58 +117,48 @@ char *utilCreateStringVArgs(char *format, va_list args) {
|
|||
}
|
||||
|
||||
|
||||
__attribute__((__format__(__printf__, 1, 0)))
|
||||
__attribute__((noreturn))
|
||||
void utilDie(char *fmt, ...) {
|
||||
void utilDie(const char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
if (_consoleEnabled) {
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
_printLine(stderr, fmt, args);
|
||||
va_end(args);
|
||||
printf("\n");
|
||||
fflush(stderr);
|
||||
#ifdef _WIN32
|
||||
getchar();
|
||||
#endif
|
||||
utilWaitForKeyOnWindows();
|
||||
}
|
||||
exit(1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
void utilEnableConsole(bool enable) {
|
||||
void utilEnableConsole(bool enable) {
|
||||
_consoleEnabled = enable;
|
||||
}
|
||||
|
||||
|
||||
bool utilFileExists(char *filename) {
|
||||
FILE *file;
|
||||
if ((file = fopen(filename, "r+"))) {
|
||||
fclose(file);
|
||||
return true;
|
||||
bool utilFileExists(const char *filename) {
|
||||
struct stat sb;
|
||||
|
||||
if (stat(filename, &sb) != 0) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
return S_ISREG(sb.st_mode);
|
||||
}
|
||||
|
||||
|
||||
void utilFixPathSeparators(char **path, bool slash) {
|
||||
|
||||
int32_t i = 0;
|
||||
int32_t j = 0;
|
||||
char *work = *path;
|
||||
char *temp = NULL;
|
||||
const char separator = utilGetPathSeparator();
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
char *work = *path;
|
||||
char *grown = NULL;
|
||||
|
||||
// Flip path separators to whatever our OS wants & remove repeated separators.
|
||||
while (work[i] != 0) {
|
||||
// Correct separator
|
||||
if (work[i] == '\\' || work[i] == '/') {
|
||||
// Was the prior character a seprator?
|
||||
if (j == 0) {
|
||||
work[j++] = utilGetPathSeparator();
|
||||
} else {
|
||||
if (work[j - 1] != utilGetPathSeparator()) {
|
||||
// No, accept it.
|
||||
work[j++] = utilGetPathSeparator();
|
||||
}
|
||||
// Only accept a separator if the prior character was not one.
|
||||
if ((j == 0) || (work[j - 1] != separator)) {
|
||||
work[j++] = separator;
|
||||
}
|
||||
} else {
|
||||
work[j++] = work[i];
|
||||
|
|
@ -152,18 +167,15 @@ void utilFixPathSeparators(char **path, bool slash) {
|
|||
}
|
||||
work[j] = 0;
|
||||
|
||||
if (slash) {
|
||||
// Does this string end with a path separator?
|
||||
if (work[strlen(work) - 1] != utilGetPathSeparator()) {
|
||||
// No - append one.
|
||||
temp = strdup(work);
|
||||
free(work);
|
||||
work = malloc(sizeof(char) * (strlen(temp) + 2));
|
||||
strcpy(work, temp);
|
||||
work[strlen(temp)] = utilGetPathSeparator();
|
||||
work[strlen(temp) + 1] = 0;
|
||||
free(temp);
|
||||
// Does this string need a trailing separator?
|
||||
if (slash && ((j == 0) || (work[j - 1] != separator))) {
|
||||
grown = realloc(work, j + 2);
|
||||
if (!grown) {
|
||||
utilDie("Unable to allocate memory for path.");
|
||||
}
|
||||
work = grown;
|
||||
work[j] = separator;
|
||||
work[j + 1] = 0;
|
||||
}
|
||||
|
||||
*path = work;
|
||||
|
|
@ -175,39 +187,33 @@ bool utilGetConsoleEnabled(void) {
|
|||
}
|
||||
|
||||
|
||||
char *utilGetFileExtension(char *filename) {
|
||||
char *start = filename + strlen(filename);
|
||||
int32_t x;
|
||||
char *utilGetFileExtension(const char *filename) {
|
||||
const char *end = filename + strlen(filename);
|
||||
const char *last = utilGetLastPathComponent(filename);
|
||||
const char *dot = strrchr(last, '.');
|
||||
|
||||
// Scan through name and find the last '.'
|
||||
for (x=0; x<(int32_t)strlen(filename); x++) {
|
||||
if (filename[x] == '.') {
|
||||
start = &filename[x + 1];
|
||||
}
|
||||
// Reset if we find a path separator
|
||||
if (filename[x] == '\\' || filename[x] == '/') {
|
||||
start = filename + strlen(filename);
|
||||
}
|
||||
// No '.' in the last path component means no extension.
|
||||
if (dot == NULL) {
|
||||
return (char *)end;
|
||||
}
|
||||
|
||||
return start;
|
||||
return (char *)(dot + 1);
|
||||
}
|
||||
|
||||
|
||||
char *utilGetLastPathComponent(char *pathname) {
|
||||
static char *start;
|
||||
int32_t x;
|
||||
char *utilGetLastPathComponent(const char *pathname) {
|
||||
const char *start = pathname;
|
||||
const char *slash = strrchr(pathname, '/');
|
||||
const char *backslash = strrchr(pathname, '\\');
|
||||
|
||||
start = pathname;
|
||||
|
||||
// Scan through name and find the last path separator
|
||||
for (x=0; x<(int32_t)strlen(pathname); x++) {
|
||||
if (pathname[x] == '\\' || pathname[x] == '/') {
|
||||
start = &pathname[x + 1];
|
||||
}
|
||||
if (slash != NULL) {
|
||||
start = slash + 1;
|
||||
}
|
||||
if ((backslash != NULL) && (backslash + 1 > start)) {
|
||||
start = backslash + 1;
|
||||
}
|
||||
|
||||
return start;
|
||||
return (char *)start;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -220,21 +226,17 @@ char utilGetPathSeparator(void) {
|
|||
}
|
||||
|
||||
|
||||
char *utilGetUpToLastPathComponent(char *pathname) {
|
||||
static char *copy = NULL;
|
||||
bool dumb = false; // Using (copy == NULL) below didn't work after optimizations, so enter the dummy.
|
||||
int32_t x;
|
||||
// Returns a new string the caller must free. Always ends in a path separator.
|
||||
char *utilGetUpToLastPathComponent(const char *pathname) {
|
||||
size_t length = strlen(pathname) - strlen(utilGetLastPathComponent(pathname));
|
||||
char *copy = NULL;
|
||||
|
||||
x = (int32_t)(strlen(pathname) - strlen(utilGetLastPathComponent(pathname))) - 1;
|
||||
if (x < 0) x = 0;
|
||||
if (dumb) {
|
||||
free(copy);
|
||||
copy = NULL;
|
||||
if (length == 0) {
|
||||
// No directory part - use the current directory.
|
||||
copy = strdup(".");
|
||||
} else {
|
||||
dumb = true;
|
||||
copy = utilStrndup(pathname, length);
|
||||
}
|
||||
copy = strdup(pathname);
|
||||
copy[x] = 0;
|
||||
utilFixPathSeparators(©, true);
|
||||
|
||||
return copy;
|
||||
|
|
@ -242,91 +244,74 @@ char *utilGetUpToLastPathComponent(char *pathname) {
|
|||
|
||||
|
||||
bool utilMkDirP(const char *dir, const mode_t mode) {
|
||||
char tmp[UTIL_PATH_MAX];
|
||||
char *p = NULL;
|
||||
struct stat sb;
|
||||
size_t len;
|
||||
const char separator = utilGetPathSeparator();
|
||||
char tmp[UTIL_PATH_MAX];
|
||||
char *p = NULL;
|
||||
size_t len;
|
||||
|
||||
// Make copy of dir.
|
||||
len = strnlen(dir, UTIL_PATH_MAX);
|
||||
if (len == 0 || len == UTIL_PATH_MAX) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
memcpy(tmp, dir, len);
|
||||
tmp[len] = '\0';
|
||||
tmp[len] = 0;
|
||||
|
||||
// Remove trailing slash.
|
||||
if (tmp[len - 1] == utilGetPathSeparator()) {
|
||||
tmp[len - 1] = '\0';
|
||||
}
|
||||
|
||||
// Does it already exist?
|
||||
if (stat(tmp, &sb) == 0) {
|
||||
if (S_ISDIR (sb.st_mode)) {
|
||||
return true;
|
||||
}
|
||||
if (tmp[len - 1] == separator) {
|
||||
tmp[len - 1] = 0;
|
||||
}
|
||||
|
||||
// Recursive mkdir.
|
||||
for (p = tmp + 1; *p; p++) {
|
||||
if (*p == utilGetPathSeparator()) {
|
||||
if (*p == separator) {
|
||||
*p = 0;
|
||||
if (stat(tmp, &sb) != 0) {
|
||||
// Does not exist - create it.
|
||||
if (ourMkdir(tmp, mode) < 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!S_ISDIR(sb.st_mode)) {
|
||||
// Not a directory
|
||||
return false;
|
||||
}
|
||||
if (!_ensureDirectory(tmp, mode)) {
|
||||
return false;
|
||||
}
|
||||
*p = utilGetPathSeparator();
|
||||
}
|
||||
}
|
||||
// Check path
|
||||
if (stat(tmp, &sb) != 0) {
|
||||
// Does not exist - create it.
|
||||
if (ourMkdir(tmp, mode) < 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!S_ISDIR(sb.st_mode)) {
|
||||
// Not a directory
|
||||
return false;
|
||||
*p = separator;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return _ensureDirectory(tmp, mode);
|
||||
}
|
||||
|
||||
|
||||
bool utilPathExists(char *pathname) {
|
||||
DIR *dir = opendir(pathname);
|
||||
if (dir) {
|
||||
closedir(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
void utilNewline(void) {
|
||||
utilSay("%s", "");
|
||||
}
|
||||
|
||||
|
||||
char *utilReadFile(char *filename, size_t *bytes) {
|
||||
char *data = NULL;
|
||||
FILE *in = fopen(filename, "rb");
|
||||
size_t read = 0;
|
||||
bool utilPathExists(const char *pathname) {
|
||||
struct stat sb;
|
||||
|
||||
(void)read;
|
||||
if (stat(pathname, &sb) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return S_ISDIR(sb.st_mode);
|
||||
}
|
||||
|
||||
|
||||
// Returns a new NUL terminated buffer the caller must free, or NULL.
|
||||
char *utilReadFile(const char *filename, size_t *bytes) {
|
||||
char *data = NULL;
|
||||
FILE *in = fopen(filename, "rb");
|
||||
long size = 0;
|
||||
|
||||
*bytes = 0;
|
||||
|
||||
if (in) {
|
||||
fseek(in, 0, SEEK_END);
|
||||
*bytes = ftell(in);
|
||||
size = ftell(in);
|
||||
fseek(in, 0, SEEK_SET);
|
||||
data = malloc(sizeof(char) * (*bytes));
|
||||
read = fread(data, sizeof(char), *bytes, in);
|
||||
if (size >= 0) {
|
||||
data = malloc((size_t)size + 1);
|
||||
if (data) {
|
||||
*bytes = fread(data, 1, (size_t)size, in);
|
||||
data[*bytes] = 0;
|
||||
}
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
|
|
@ -334,123 +319,91 @@ char *utilReadFile(char *filename, size_t *bytes) {
|
|||
}
|
||||
|
||||
|
||||
char *utilReadLine(char *haystack, size_t length, char **offset) {
|
||||
size_t bytes = 0;
|
||||
char *temp = *offset;
|
||||
char *tail = temp;
|
||||
char *result = NULL;
|
||||
// Returns a new string the caller must free, or NULL when no data remains.
|
||||
char *utilReadLine(const char *haystack, size_t length, const char **offset) {
|
||||
const char *end = haystack + length;
|
||||
const char *start = *offset;
|
||||
const char *tail = NULL;
|
||||
char *result = NULL;
|
||||
|
||||
// They didn't know where to start
|
||||
if (temp == NULL) {
|
||||
temp = haystack;
|
||||
tail = temp;
|
||||
// They didn't know where to start.
|
||||
if (start == NULL) {
|
||||
start = haystack;
|
||||
}
|
||||
tail = start;
|
||||
|
||||
// Is there still data to read?
|
||||
while ((size_t)(tail - haystack) < length) {
|
||||
// Is this the end of a line?
|
||||
if ((tail[0] == 10) || (tail[0] == 13)) {
|
||||
// Yep!
|
||||
bytes = tail - temp + 1;
|
||||
result = malloc(sizeof(char) * bytes);
|
||||
memcpy(result, temp, bytes - 1);
|
||||
result[bytes - 1] = 0;
|
||||
// Read past any additional CR/LFs
|
||||
while ((tail[0] == 10) || (tail[0] == 13)) {
|
||||
tail++;
|
||||
}
|
||||
// Return where we left off
|
||||
temp = tail;
|
||||
*offset = temp;
|
||||
return result;
|
||||
}
|
||||
// Next character
|
||||
// Find the end of the line or the end of the data.
|
||||
while ((tail < end) && (*tail != '\n') && (*tail != '\r')) {
|
||||
tail++;
|
||||
}
|
||||
|
||||
// Was there data at the end of the block with no CR/LF?
|
||||
if (tail > temp) {
|
||||
// Yep. Treat it as a line.
|
||||
bytes = tail - temp + 1;
|
||||
result = malloc(sizeof(char) * bytes);
|
||||
memcpy(result, temp, bytes - 1);
|
||||
result[bytes - 1] = 0;
|
||||
temp = tail;
|
||||
*offset = temp;
|
||||
// Was there anything on this line?
|
||||
if (tail > start) {
|
||||
result = utilStrndup(start, (size_t)(tail - start));
|
||||
}
|
||||
|
||||
// Didn't find anything
|
||||
// Read past any CR/LFs.
|
||||
while ((tail < end) && ((*tail == '\n') || (*tail == '\r'))) {
|
||||
tail++;
|
||||
}
|
||||
|
||||
// Return where we left off.
|
||||
*offset = tail;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void utilRedirectConsole(void) {
|
||||
#ifdef _WIN32
|
||||
// http://dslweb.nwnexus.com/~ast/dload/guicon.htm
|
||||
int hConHandle; // Not changing this int.
|
||||
intptr_t lStdHandle;
|
||||
CONSOLE_SCREEN_BUFFER_INFO coninfo;
|
||||
FILE *fp;
|
||||
static bool consoleOpen = false;
|
||||
|
||||
if (!consoleOpen) {
|
||||
if (_consoleEnabled && !consoleOpen) {
|
||||
consoleOpen = true;
|
||||
|
||||
// allocate a console for this app
|
||||
// Allocate a console for this app.
|
||||
AllocConsole();
|
||||
|
||||
// set the screen buffer to be big enough to let us scroll text
|
||||
// Set the screen buffer to be big enough to let us scroll text.
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
|
||||
coninfo.dwSize.Y = CONSOLE_LINES;
|
||||
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
|
||||
|
||||
// redirect unbuffered STDOUT to the console
|
||||
lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen(hConHandle, "w");
|
||||
*stdout = *fp;
|
||||
// Redirect unbuffered STDOUT, STDIN, and STDERR to the console.
|
||||
freopen("CONOUT$", "w", stdout);
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
// redirect unbuffered STDIN to the console
|
||||
lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen(hConHandle, "r");
|
||||
*stdin = *fp;
|
||||
freopen("CONIN$", "r", stdin);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
|
||||
// redirect unbuffered STDERR to the console
|
||||
lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen(hConHandle, "w");
|
||||
*stderr = *fp;
|
||||
freopen("CONOUT$", "w", stderr);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
__attribute__((__format__(__printf__, 1, 0)))
|
||||
void utilSay(char *fmt, ...) {
|
||||
void utilSay(const char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
if (_consoleEnabled) {
|
||||
va_start(args, fmt);
|
||||
vfprintf(stdout, fmt, args);
|
||||
_printLine(stdout, fmt, args);
|
||||
va_end(args);
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
_outputHappened = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool utilStartsWith(char *string, char *start) {
|
||||
bool utilStartsWith(const char *string, const char *start) {
|
||||
return strncmp(start, string, strlen(start)) == 0;
|
||||
}
|
||||
|
||||
|
||||
int utilStricmp(char *a, char *b) {
|
||||
int32_t utilStricmp(const char *a, const char *b) {
|
||||
int32_t d = 0;
|
||||
|
||||
for (;; a++, b++) {
|
||||
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
|
||||
d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
|
||||
if (d != 0 || !*a) {
|
||||
return d;
|
||||
}
|
||||
|
|
@ -459,16 +412,23 @@ int utilStricmp(char *a, char *b) {
|
|||
|
||||
|
||||
// Windows does not have strndup() so we implement our own.
|
||||
char *utilStrndup( const char *s1, size_t n) {
|
||||
char *copy = (char *)malloc(n + 1);
|
||||
memcpy(copy, s1, n);
|
||||
copy[n] = 0;
|
||||
char *utilStrndup(const char *s1, size_t n) {
|
||||
size_t length = strnlen(s1, n);
|
||||
char *copy = malloc(length + 1);
|
||||
|
||||
if (!copy) {
|
||||
utilDie("Unable to allocate memory for string.");
|
||||
}
|
||||
memcpy(copy, s1, length);
|
||||
copy[length] = 0;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
void utilTrace(char *fmt, ...) {
|
||||
void utilTrace(const char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
if (_utilTraceFile) {
|
||||
va_start(args, fmt);
|
||||
utilTraceVArgs(fmt, args);
|
||||
|
|
@ -478,36 +438,38 @@ void utilTrace(char *fmt, ...) {
|
|||
|
||||
|
||||
void utilTraceEnd(void) {
|
||||
if (_utilTraceFile) fclose(_utilTraceFile);
|
||||
}
|
||||
|
||||
|
||||
FILE *utilTraceGetFile(void) {
|
||||
return _utilTraceFile;
|
||||
}
|
||||
|
||||
|
||||
void utilTraceStart(char *filename) {
|
||||
_utilTraceFile = fopen(filename, "wt");
|
||||
if (!_utilTraceFile) utilDie("Unable to create trace file: %s", filename);
|
||||
}
|
||||
|
||||
|
||||
__attribute__((__format__(__printf__, 1, 0)))
|
||||
void utilTraceVArgs(char *fmt, va_list args) {
|
||||
#if defined(va_copy)
|
||||
va_list args2;
|
||||
#endif
|
||||
|
||||
if (_utilTraceFile) {
|
||||
#if defined(va_copy)
|
||||
va_copy(args2, args);
|
||||
vprintf(fmt, args2);
|
||||
printf("\n");
|
||||
va_end(args2);
|
||||
#endif
|
||||
vfprintf(_utilTraceFile, fmt, args);
|
||||
fprintf(_utilTraceFile, "\n");
|
||||
fflush(_utilTraceFile);
|
||||
fclose(_utilTraceFile);
|
||||
_utilTraceFile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void utilTraceStart(const char *filename) {
|
||||
utilTraceEnd();
|
||||
_utilTraceFile = fopen(filename, "w");
|
||||
if (!_utilTraceFile) {
|
||||
utilDie("Unable to create trace file: %s", filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void utilTraceVArgs(const char *fmt, va_list args) {
|
||||
va_list argsCopy;
|
||||
|
||||
if (_utilTraceFile) {
|
||||
if (_consoleEnabled) {
|
||||
va_copy(argsCopy, args);
|
||||
_printLine(stdout, fmt, argsCopy);
|
||||
va_end(argsCopy);
|
||||
}
|
||||
_printLine(_utilTraceFile, fmt, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void utilWaitForKeyOnWindows(void) {
|
||||
#ifdef _WIN32
|
||||
getchar();
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
54
src/util.h
54
src/util.h
|
|
@ -36,31 +36,33 @@
|
|||
#define UTIL_PATH_MAX 1024
|
||||
|
||||
|
||||
bool utilChMod(const char *path, const mode_t mode);
|
||||
char *utilCreateString(char *format, ...);
|
||||
char *utilCreateStringVArgs(char *format, va_list args);
|
||||
void utilDie(char *fmt, ...);
|
||||
void utilEnableConsole(bool enable);
|
||||
bool utilFileExists(char *filename);
|
||||
void utilFixPathSeparators(char **path, bool slash);
|
||||
bool utilGetConsoleEnabled(void);
|
||||
char *utilGetFileExtension(char *filename);
|
||||
char *utilGetLastPathComponent(char *pathname);
|
||||
char utilGetPathSeparator(void);
|
||||
char *utilGetUpToLastPathComponent(char *pathname);
|
||||
bool utilMkDirP(const char *dir, const mode_t mode);
|
||||
bool utilPathExists(char *pathname);
|
||||
char *utilReadFile(char *filename, size_t *bytes);
|
||||
char *utilReadLine(char *haystack, size_t length, char **offset);
|
||||
void utilRedirectConsole(void);
|
||||
void utilSay(char *fmt, ...);
|
||||
bool utilStartsWith(char *string, char *start);
|
||||
int utilStricmp(char *a, char *b);
|
||||
char *utilStrndup( const char *s1, size_t n);
|
||||
void utilTrace(char *fmt, ...);
|
||||
void utilTraceEnd(void);
|
||||
FILE *utilTraceGetFile(void);
|
||||
void utilTraceStart(char *filename);
|
||||
void utilTraceVArgs(char *fmt, va_list args);
|
||||
bool utilChMod(const char *path, const mode_t mode);
|
||||
char *utilCreateString(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
char *utilCreateStringVArgs(const char *format, va_list args) __attribute__((format(printf, 1, 0)));
|
||||
void utilDie(const char *fmt, ...) __attribute__((format(printf, 1, 2))) __attribute__((noreturn));
|
||||
void utilEnableConsole(bool enable);
|
||||
bool utilFileExists(const char *filename);
|
||||
void utilFixPathSeparators(char **path, bool slash);
|
||||
bool utilGetConsoleEnabled(void);
|
||||
char *utilGetFileExtension(const char *filename);
|
||||
char *utilGetLastPathComponent(const char *pathname);
|
||||
char utilGetPathSeparator(void);
|
||||
char *utilGetUpToLastPathComponent(const char *pathname);
|
||||
bool utilMkDirP(const char *dir, const mode_t mode);
|
||||
void utilNewline(void);
|
||||
bool utilPathExists(const char *pathname);
|
||||
char *utilReadFile(const char *filename, size_t *bytes);
|
||||
char *utilReadLine(const char *haystack, size_t length, const char **offset);
|
||||
void utilRedirectConsole(void);
|
||||
void utilSay(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
bool utilStartsWith(const char *string, const char *start);
|
||||
int32_t utilStricmp(const char *a, const char *b);
|
||||
char *utilStrndup(const char *s1, size_t n);
|
||||
void utilTrace(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void utilTraceEnd(void);
|
||||
void utilTraceStart(const char *filename);
|
||||
void utilTraceVArgs(const char *fmt, va_list args) __attribute__((format(printf, 1, 0)));
|
||||
void utilWaitForKeyOnWindows(void);
|
||||
|
||||
|
||||
#endif // UTIL_H
|
||||
|
|
|
|||
38
src/version.h.in
Normal file
38
src/version.h.in
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
*
|
||||
* Singe 2
|
||||
* Copyright (C) 2006-@SINGE_COPYRIGHT_END_YEAR@ 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef VERSION_H
|
||||
#define VERSION_H
|
||||
|
||||
|
||||
// ===== THIS FILE IS AUTOMATICALLY GENERATED FROM version.h.in - DO NOT EDIT =====
|
||||
// The version number lives in the project() line of CMakeLists.txt.
|
||||
|
||||
#define SINGE_VERSION @PROJECT_VERSION@
|
||||
#define SINGE_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||
#define SINGE_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define VERSION_STRING "v@PROJECT_VERSION@"
|
||||
#define COPYRIGHT_END_YEAR "@SINGE_COPYRIGHT_END_YEAR@"
|
||||
|
||||
|
||||
#endif // VERSION_H
|
||||
1249
src/videoPlayer.c
1249
src/videoPlayer.c
File diff suppressed because it is too large
Load diff
|
|
@ -30,31 +30,35 @@
|
|||
#include "common.h"
|
||||
|
||||
|
||||
typedef void (*videoIndexingCallback)(int32_t);
|
||||
#define VIDEO_VOLUME_MAX 100
|
||||
|
||||
|
||||
int32_t videoInit(void);
|
||||
int32_t videoIsPlaying(int32_t playerHandle);
|
||||
int32_t videoGetAudioTrack(int32_t playerHandle);
|
||||
int32_t videoGetAudioTracks(int32_t playerHandle);
|
||||
int64_t videoGetFrame(int32_t playerHandle);
|
||||
int64_t videoGetFrameCount(int32_t playerHandle);
|
||||
int32_t videoGetHeight(int32_t playerHandle);
|
||||
char *videoGetLanguage(int32_t playerHandle, int32_t audioTrack);
|
||||
char *videoGetLanguageDescription(char *languageCode);
|
||||
int32_t videoGetWidth(int32_t playerHandle);
|
||||
int32_t videoGetVolume(int32_t playerHandle, int32_t *leftPercent, int32_t *rightPercent);
|
||||
int32_t videoLoad(char *filename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer);
|
||||
int32_t videoLoadWithAudio(char *vFilename, char *aFilename, char *indexPath, bool stretchVideo, SDL_Renderer *renderer);
|
||||
int32_t videoPause(int32_t playerHandle);
|
||||
int32_t videoPlay(int32_t playerHandle);
|
||||
int32_t videoQuit(void);
|
||||
int32_t videoSeek(int32_t playerHandle, int64_t seekFrame);
|
||||
int32_t videoSetAudioTrack(int32_t playerHandle, int32_t track);
|
||||
int32_t videoSetIndexCallback(videoIndexingCallback callback);
|
||||
int32_t videoSetVolume(int32_t playerHandle, int32_t leftPercent, int32_t rightPercent);
|
||||
int32_t videoUnload(int32_t playerHandle);
|
||||
int32_t videoUpdate(int32_t playerHandle, SDL_Texture **texture);
|
||||
typedef void (*VideoIndexingCallbackT)(int32_t percent);
|
||||
|
||||
|
||||
int32_t videoGetAudioTrack(int32_t playerHandle);
|
||||
int32_t videoGetAudioTracks(int32_t playerHandle);
|
||||
int64_t videoGetFrame(int32_t playerHandle);
|
||||
int64_t videoGetFrameCount(int32_t playerHandle);
|
||||
int32_t videoGetHeight(int32_t playerHandle);
|
||||
const char *videoGetLanguage(int32_t playerHandle, int32_t audioTrack);
|
||||
const char *videoGetLanguageDescription(const char *languageCode);
|
||||
bool videoGetPixel(int32_t playerHandle, int32_t x, int32_t y, uint8_t *r, uint8_t *g, uint8_t *b);
|
||||
bool videoGetPixels(int32_t playerHandle, const uint8_t **pixels, int32_t *pitch);
|
||||
void videoGetVolume(int32_t playerHandle, int32_t *leftPercent, int32_t *rightPercent);
|
||||
int32_t videoGetWidth(int32_t playerHandle);
|
||||
void videoInit(int32_t mixerChunkFrames);
|
||||
bool videoIsPlaying(int32_t playerHandle);
|
||||
int32_t videoLoad(const char *videoFilename, const char *audioFilename, const char *indexPath, SDL_Renderer *renderer);
|
||||
void videoPause(int32_t playerHandle);
|
||||
void videoPlay(int32_t playerHandle);
|
||||
void videoQuit(void);
|
||||
void videoSeek(int32_t playerHandle, int64_t seekFrame);
|
||||
void videoSetAudioTrack(int32_t playerHandle, int32_t track);
|
||||
void videoSetIndexCallback(VideoIndexingCallbackT callback);
|
||||
void videoSetVolume(int32_t playerHandle, int32_t leftPercent, int32_t rightPercent);
|
||||
void videoUnload(int32_t playerHandle);
|
||||
int64_t videoUpdate(int32_t playerHandle, SDL_Texture **texture);
|
||||
|
||||
|
||||
#endif // VIDEOPLAYER_H
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* 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 2
|
||||
* 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,
|
||||
|
|
@ -15,7 +15,8 @@
|
|||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
--]]
|
||||
|
|
@ -28,25 +29,20 @@ local settings
|
|||
local line = "-------------------------------------------------------------------------------"
|
||||
|
||||
|
||||
local function utilDump(o)
|
||||
if type(o) == 'table' then
|
||||
local s = '{ '
|
||||
for k,v in pairs(o) do
|
||||
if type(k) ~= 'number' then
|
||||
k = '"'..k..'"'
|
||||
end
|
||||
s = s .. '['..k..'] = ' .. utilDump(v) .. ','
|
||||
end
|
||||
return s .. '} '
|
||||
else
|
||||
return tostring(o)
|
||||
end
|
||||
-- Escapes a string for use in string.gsub patterns.
|
||||
local function escapePattern(text)
|
||||
return (text:gsub("%W", "%%%0"))
|
||||
end
|
||||
|
||||
|
||||
-- Wraps a path in quotes so spaces survive the shell.
|
||||
local function quote(path)
|
||||
return '"' .. path .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function configureSinge(self)
|
||||
|
||||
UI = {}
|
||||
local UI = {}
|
||||
|
||||
UI.dlgSinge = wx.wxDialog (wx.NULL, wx.wxID_ANY, "Singe Configuration", wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxDEFAULT_DIALOG_STYLE )
|
||||
UI.dlgSinge:SetSizeHints( wx.wxSize( 500,-1 ), wx.wxDefaultSize )
|
||||
|
|
@ -116,12 +112,11 @@ local function configureSinge(self)
|
|||
UI.filePickScript:GetTextCtrl():Connect( wx.wxEVT_KEY_DOWN, function(event) end )
|
||||
UI.filePickVideo:GetTextCtrl():Connect( wx.wxEVT_KEY_DOWN, function(event) end )
|
||||
|
||||
if settings then
|
||||
if settings.singe then UI.filePickSinge:GetTextCtrl():SetValue(settings.singe) end
|
||||
if settings.script then UI.filePickScript:GetTextCtrl():SetValue(settings.script) end
|
||||
if settings.video then UI.filePickVideo:GetTextCtrl():SetValue(settings.video) end
|
||||
if settings.options then UI.txtOptions:SetValue(settings.options) end
|
||||
end
|
||||
settings = settings or {}
|
||||
if settings.singe then UI.filePickSinge:GetTextCtrl():SetValue(settings.singe) end
|
||||
if settings.script then UI.filePickScript:GetTextCtrl():SetValue(settings.script) end
|
||||
if settings.video then UI.filePickVideo:GetTextCtrl():SetValue(settings.video) end
|
||||
if settings.options then UI.txtOptions:SetValue(settings.options) end
|
||||
|
||||
UI.m_sdbSizer1Cancel:Connect( wx.wxEVT_COMMAND_BUTTON_CLICKED, function(event)
|
||||
event:Skip()
|
||||
|
|
@ -161,10 +156,11 @@ local function startSinge(self)
|
|||
local env
|
||||
local ok
|
||||
|
||||
-- File pickers leave empty strings behind, so test for those as well as nil.
|
||||
if settings then
|
||||
if not settings.singe then message = "You must specify the Singe executable in the Singe configuration." end
|
||||
if not settings.script then message = "You must specify the script to run in the Singe configuration." end
|
||||
if not settings.video then message = "You must specify the video to use in the Singe configuration." end
|
||||
if not settings.singe or settings.singe == "" then message = "You must specify the Singe executable in the Singe configuration." end
|
||||
if not settings.script or settings.script == "" then message = "You must specify the script to run in the Singe configuration." end
|
||||
if not settings.video or settings.video == "" then message = "You must specify the video to use in the Singe configuration." end
|
||||
else
|
||||
message = "Please configure Singe first."
|
||||
end
|
||||
|
|
@ -174,17 +170,21 @@ local function startSinge(self)
|
|||
return
|
||||
end
|
||||
|
||||
-- Run from the Singe directory with paths made relative to it.
|
||||
wdir = settings.singe:match("(.*[/\\])") or "./"
|
||||
launch = settings.singe .. ' ' .. settings.options .. ' -v ' .. settings.video .. ' ' .. settings.script
|
||||
launch = launch:gsub(wdir, "./", 1):gsub(wdir, "")
|
||||
launch = quote(settings.singe) .. ' ' .. (settings.options or "") .. ' -v ' .. quote(settings.video) .. ' ' .. quote(settings.script)
|
||||
launch = launch:gsub(escapePattern(wdir), "./", 1):gsub(escapePattern(wdir), "")
|
||||
wdir = wx.wxFileName.DirName(wdir):GetFullPath()
|
||||
ide:Print(line)
|
||||
ide:Print(launch)
|
||||
|
||||
-- ZeroBrane's own LUA_CPATH would leak into Singe's embedded Lua.
|
||||
ok, env = wx.wxGetEnv('LUA_CPATH')
|
||||
wx.wxUnsetEnv('LUA_CPATH')
|
||||
ide:ExecuteCommand(launch, wdir, singeOutput, singeEnd)
|
||||
wx.wxSetEnv('LUA_CPATH', env)
|
||||
if ok then
|
||||
wx.wxSetEnv('LUA_CPATH', env)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ return {
|
|||
name = "Singe Integration",
|
||||
description = "Adds menu items and a toolbar button to assist with debugging Singe games.",
|
||||
author = "Scott Duensing",
|
||||
version = 1.00,
|
||||
version = 1.01,
|
||||
dependencies = "1.0",
|
||||
|
||||
onRegister = function(self)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,26 @@
|
|||
--[[
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
*
|
||||
--]]
|
||||
|
||||
editor.specmap.singe = 'lua'
|
||||
|
||||
editor.tabwidth = 4
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue