SINGE 3.00 ========== Unreleased API Changes ----------- - The name now expands to "SINGE Is Not a Game Emulator", in the banner, the manual, INSTALL, and the Windows file description. - Framework.singe applies the legacy sprite argument order once even when a game loads it more than once. - lfs.dir, lfs.attributes and lfs.symlinkattributes see inside a packed game (the union of its loose directory, its data overlay and the database), and lfs.mkdir and lfs.rmdir act on its data overlay, so frameworks that list or create their own directories work packed. - Physics. Any scene node can carry a rigid body on Jolt Physics: static, kinematic (follows its node and pushes) or dynamic (drives its node), shaped as a box, sphere, capsule, cylinder, a convex hull of the node's mesh or the mesh's triangles; mass, friction, bounce, velocities, forces and impulses; triggers; hinge, ball and slider joints with limits; a raycast; onCollision and onTrigger callbacks. The world steps at a fixed 60 Hz between animation and rendering and pauses with the game. physicsSet2D keeps bodies in a plane for 2D games, which need no GPU (nodes now exist on every machine, so a Raspberry Pi 3 gets 2D physics without 3D). New calls: body*, joint*, physics*, and the BODY_*, SHAPE_* and JOINT_* constants; see the Physics chapter. Needs SSE4.1 on x86 (2008 and later); the engine's first C++ (one wrapper file and the library), statically linked, no runtime to install. - Soft bodies. softNew makes cloth or a pressure body from a node's mesh (particles from its welded vertices, Jolt's stretch, shear and bend constraints, pressure for balloons) or a rope from the node to a point with a tube mesh the engine makes; they collide with everything rigid and the mesh is rewritten every frame. softPin holds particles in place or to nodes; stiffness, mass, damping and pressure are tunable. meshPlane takes subdivisions; nodeSetMaterial changes a material without the mesh. New calls: soft*, and the SOFT_* constants; see Soft bodies in the Physics chapter. - Ragdolls. ragdollNew derives capsules and swing-twist joints from a skinned model's skeleton; ragdollActivate hands the skeleton to physics from its current pose and the mesh follows the bones as it falls; motors (ragdollSetStrength) pull it back toward that pose; ragdollApplyImpulse shoves a bone; ragdollDeactivate returns it to animation. New calls: ragdoll*; see Ragdolls in the Physics chapter. - Water. bodySetWater fills a static body with water: dynamic bodies inside float, sink and drift by Jolt's buoyancy against its surface, with drag and a current (bodySetCurrent, bodySetBuoyancy per body); players swim in it (playerIsSwimming, playerSetSwim, three-axis playerMove); a buoyant hull with vehicleNew(node, VEHICLE_BOAT) gets a propeller and a rudder (vehicleSetThrust, vehicleSetRudder). - Vehicles. A dynamic body becomes a car, a motorcycle or a tank on Jolt's vehicle constraint: wheels are nodes the engine poses (spin, steer, suspension), the script drives with throttle, steering, brake and hand brake, and engine, gears, suspension, steering, brakes and anti-roll bars are tunable. Motorcycles lean and balance with a spring scaled to the chassis; tanks pivot on the spot. New calls: vehicle*, and the VEHICLE_* constants; see Vehicles in the Physics chapter. - Point lights inside geometry. A bulb in a closed room, under a shade or inside a cabinet now shadows correctly: cube faces render depth two-sided so closed meshes cast from inside, every shadow tap picks its own cube face so the seams along the cube edges are gone, the depth range fits the casters round the light instead of the scene centre, the bias grows with distance and grazing angle, and each face draws only what it can see. nodeSetShadow excuses a mesh from casting. - Character controller. playerNew puts Jolt's CharacterVirtual on a node, as a capsule or any convex bodyNew shape: it walks where playerMove says, climbs steps, slides along walls, refuses steep slopes, falls, jumps from the ground, rides kinematic platforms and shoves light dynamic bodies; onCollision and onTrigger report it like a body. Works in 2D worlds as a platformer character, with tolerances scaled to the shape. New calls: player*; see Players in the Physics chapter. - Linear lighting. The scene is lit in linear light on a 16-bit float target and brought to the screen by a post pass with an exposure control (sceneSetExposure, in stops) and a tone curve (sceneSetTonemap: TONEMAP_NEUTRAL, TONEMAP_ACES, TONEMAP_NONE). Colours given as 0-255 are sRGB and decoded on the way in; textures are sampled as sRGB; bright light compresses instead of clipping. Specular highlights are GGX with Fresnel, so roughness and metallic behave as they do in glTF viewers. Existing scenes look a little different: the same numbers now mean what they mean everywhere else. - Physics debug drawing. physicsSetDebug(DEBUG_*) draws the bodies in wireframe (coloured by motion state), joints and their limits, contact points and velocities as lines over the scene each frame, static shapes too with DEBUG_STATIC; lineDraw draws a script's own world-space lines for a frame, navDraw the baked navigation mesh. - Compressed textures. KTX2 files (Basis Universal, from util/compressTextures.py) stay compressed on the GPU in the block format the machine has (BC7, ASTC, BC3, or plain RGBA where nothing else exists), a quarter of the memory of PNGs or less. A model whose file uses KHR_texture_basisu loads them on its own; materialSetTexture and the map calls take a .ktx2 name in place of a sprite; sceneGetStats reports the texture memory in use. The Sponza test scene went from 393 MB of textures to 99 MB. - Hot reload. singe --reload (-R) watches the game's loose script files and runs the game again from its script when one changes; F5 does the same, and singeReload() from a script. The engine (window, GPU, disc, controllers) stays; everything the script made is remade by the script. - Terrain. meshHeightmap makes rolling ground from a greyscale image, materialSetTiling repeats a texture across it, a SHAPE_MESH body on it becomes a Jolt height field, and terrainGetHeight reads the ground height at a point. - Sprites and text in the scene. nodeSetSprite shows a loaded sprite on a node as a lit quad (nodeSetSpriteFrame picks a frame), nodeSetText a line of text in the selected font, and nodeSetBillboard turns any node to face the camera squarely or about its own up axis (BILLBOARD_*). - Navigation. navNew, navAddNode and navBuild bake a walkable mesh from the scene's geometry on Recast; navPath, navNearest, navRaycast and navRandomPoint query it on Detour; agents (navAgentNew, navAgentMoveTo, navAgentStop, navAgentGetVelocity, navAgentIsArrived, navAgentSetPlayer) walk it on DetourCrowd without walking through each other, moving their nodes or driving a player controller, with onNavArrived when they get there; navSave and navLoad keep a baked mesh. Recast Navigation is vendored under thirdparty/recastnavigation (zlib licence). - Bloom and views. sceneSetBloom makes everything brighter than a threshold glow, blurred over a half-size chain before the tone curve. viewNew renders a second camera to a texture every frame (viewSetCamera points it) that materialSetView shows on any surface: monitors, mirrors, portals. Each view turns billboards to its own camera and draws transparency back to front from it. - Particle extras. Trails (emitterSetTrail: a fading ribbon behind each particle, 2D and 3D), collisions with a floor or, by ray cast, the physics world (emitterSetCollide, COLLIDE_*), lit particles shaded by the lights and sky (emitterSetLit) and soft particles that fade where they meet geometry (emitterSetSoftness). - Culling and instancing. Meshes outside the camera's view are skipped, and copies of one mesh with one material draw as a single instanced call (their matrices in a per-frame buffer the vertex shaders read), in the shadow passes too. sceneGetStats reports the draws collected, the draws in view and the draw calls made. - Cascaded shadows. A directional light's shadow is split into up to four maps along the view (sceneSetShadowCascades, three by default), each fitted to a slice of the camera's view out to sceneSetShadowDistance and snapped to its texels, so shadows in a large level are sharp up close and steady as the camera moves. - Sky, environment light and fog. sceneSetSky wraps the scene in an equirectangular panorama (Radiance .hdr, PNG or JPEG) drawn behind everything and lighting it: its diffuse light replaces the ambient and its reflections show on metals and glossy surfaces, blurred by roughness (sceneSetEnvironment switches that off, sceneSetSkyIntensity scales it). sceneSetFog fades meshes and particles toward a colour between two distances. - Positional sound. A playing channel can follow a scene node (soundSetNode) or sit at a point (soundSetPosition) and is then panned and faded by distance (soundSetRange) as heard from the camera or a chosen node (soundSetListener); soundGetPosition reports where it lies; soundSetPan pans a channel for 2D games; soundPlay takes a loop count. - Animation blending. animationPlay takes a fade in seconds and crossfades from the clip that was playing, or from the pose the nodes are in when nothing was (so a ragdoll eases back up); up to four clips play on layers above the base (animationPlayLayer), each with a weight (animationSetLayerWeight) and an optional mask limiting it to part of the skeleton (animationSetLayerMask); animationStop takes a layer. - Material maps. A material can carry a normal map, an occlusion map, a metallic-roughness map (glTF's packing) and an emissive map beside its base texture, and a loaded model brings its own. Every mesh has tangents: a model's when the file carries them, computed from the UVs otherwise. New calls: materialSetNormalMap, materialSetOcclusionMap, materialSetMetallicRoughnessMap, materialSetEmissiveMap. - Mipmaps. Every scene texture carries a full mipmap chain generated on the GPU at load and is sampled with anisotropic filtering, so textures seen small or at a grazing angle no longer sparkle or moire. materialSetFilter(material, FILTER_NEAREST) keeps pixel art crisp. - Particles. Emitters spawn, move and age particles from a recipe (rate, life, speed, direction and spread, gravity, drag, size, colour and spin over life, a birth radius, a pool cap) in the overlay and in the 3D scene alike. A 2D emitter draws on the GPU above or beneath the overlay when the script asks (emitterDraw); a 3D emitter sits on a node and draws camera-facing billboards after every mesh, depth tested, in world units. Any loaded sprite is the picture (a random frame per particle from an animated one); without one, a soft disc. Normal or additive blending. New calls: emitter*, and the PARTICLE_* constants; see the Particles chapter. - 3D scenes. A game can draw a 3D scene between the disc video and the overlay: primitive meshes and script-built geometry, materials with colour, textures from sprites, the disc or a loaded video, metallic and roughness, up to eight lights each able to cast shadows (cube maps for point lights), any node as the camera, and glTF 2.0 models (.glb, self-contained) with node animation, skinning and morph targets, placed any number of times. Everything is a node in one tree. New calls: scene*, node*, mesh*, material*, light*, camera*, model*, animation*, and the LIGHT_* constants; see the 3D Scenes chapter of the manual. Needs a GPU with Vulkan, Direct3D 12 or Metal; the Raspberry Pi 4 is the minimum Pi (the Pi 3 has no Vulkan driver). 2D games run as before without one. The renderer now runs on SDL's GPU device where there is one. - A game can be one file: singe --pack DIRECTORY GAME.game writes the game's scripts, art, sounds, fonts, and video into an SQLite database, and singe GAME.game (or the menu, which lists every .game file beside the game directories) runs it. Nothing in a game has to change to be packed: every name a script uses (dofile, require, io.open and friends, spriteLoad, soundLoad, fontLoad, videoLoad, framefiles, controls.cfg) goes through one lookup that tries a loose directory named like the database, then the game's data directory, then the database, so packed games can be modded by dropping files beside them and files a game writes land under data//files. Inside a database names are matched without regard to case and a leading own-directory prefix ("DLe/Cfg/x") is ignored. singe --unpack GAME.game DIRECTORY restores the files and singe --patch GAME.game SOURCE replaces files from a directory or a patch database in one transaction. --entry=N picks which games.dat entry a .game launch runs. Loose games are untouched by all of this. Scripts get SQLite for their own data with require("sqlite3"). The archive installer (.game, .tool, and .patch archives dropped beside the binary) is gone along with libarchive; a game is a .game file or a directory, copied into place. - Video is decoded by libavcodec directly; FFMS2 is gone. Seeking is still frame exact, indexing is faster, and the index files in the data directory use a new format (old ones are rebuilt automatically). Multi-channel audio is downmixed to stereo instead of refused. Indexing is a few seconds at most, so the indexing screen and its artwork are gone. Video is decoded by the platform's hardware decoder when there is one (VA-API or VDPAU on Linux, D3D11VA on Windows, VideoToolbox on macOS) and falls back to software otherwise; --softwarevideo forces software. - Singe is built on SDL3 (SDL 3.4, SDL3_image, SDL3_mixer 3.2, SDL3_ttf) instead of SDL2. Nothing changes for scripts: the same functions, constants, scancodes, and controls.cfg names. SDL2_gfx is gone; sprite and video rotation and scaling are done by the engine itself. - 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. - Singe is no longer laserdisc only. A games.dat entry with a VIDEO is a laserdisc game; one with the line missing or blank runs without a disc, on a canvas sized by CANVAS_X/CANVAS_Y (default 720x480), the disc functions become harmless, and discGetState() returns the new DISC_EJECTED. On the command line --disc (implied by --framefile) declares the disc, since a video found beside the script no longer does; --canvas=WxH sizes the canvas. Existing games.dat files need no change. Scripts can test SINGE_DISC. - 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. - Video decoding runs on its own thread per video, so a discSearch() far from a keyframe no longer stalls the game loop; the previous frame stays on screen until the new one is ready. The laserdisc and framefile videos are handed to the GPU as YUV and converted there instead of on the CPU, and each video reports its keyframe spacing to the program trace when loaded, with a warning there when seeks may be slow. - 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. - Audio and video are kept in sync from the audio clock. The engine measures the audio device's queue at startup instead of guessing, uses small mixer buffers, and honours a container whose first video frame is timestamped after its first audio sample. For delay the engine cannot see (receivers, Bluetooth, DACs) there is singeSetAudioDelay(ms) / singeGetAudioDelay(), the games.dat key AUDIO_DELAY, and --audiodelay=MS; positive values mean the audio is heard late. - The menu has an audio delay calibration screen for the machine's own delay (DAC, receiver, Bluetooth, display). Press the service key on the game list; a click plays once a second and the screen flashes. Adjust with the stick until the flash and the click coincide, then press button 1 to save. The value is kept in audio.cfg in the data root and applied to every game on the machine, from the menu or the command line, on top of any per-game AUDIO_DELAY. Scripts can read and set it with singeGetAudioCalibration() / singeSetAudioCalibration(), read the measured device queue with singeGetAudioLatency(), and now have singeGetTicks() for a wall clock in milliseconds (os.clock() is processor time and drifts). - Inside a packed game a relative name with a ".." component resolves nowhere: io.open returns nil and "NAME reaches outside the game", io.lines, io.input and io.output raise it, lfs.mkdir and lfs.rmdir return nil with it, and asset loads report the file as missing. The packer already refused such names; the lookup now enforces the rule at run time. lfs.dir on a packed directory lists its entries sorted without regard to case. - One rule for the data directory on every launch (command line, games.dat, scriptPush and scriptExecute): with --datadir, or for a packed game, a directory named for the game under the base; otherwise the game's own directory. scriptPush of a loose game without --datadir used to write under the current directory. - Color components handed to lightSetColor, materialSetColor (alpha too), materialSetEmissive, lineDraw and navDraw are clamped to 0..255, as colorForeground and colorBackground always were; they used to wrap. - materialDelete on the private material of a sprite or text node is an error ("belongs to a sprite node; clear the sprite instead") instead of freeing a slot the node still used. - lightSetCone raises the outer angle to at least the inner angle. Fixes ----- - The manual's 3D Scenes, Physics and Particles chapters are rewritten as sections in a learning order with worked examples, every reference entry now shows each form the call accepts with a realistic example, and prose spelling is American throughout. Manual.pdf and Manual.html are rendered into .builddir/ by the build and by build-docs.sh alike, and the binary embeds the PDF from there. - 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. - Support files in the Singe directory are refreshed when the installed copy differs from the running build. Upgrading the binary used to keep the old Framework.singe and Menu.singe forever. - The menu background video is encoded with a keyframe every second, so the menu's jump to the game list is instant. - Framefiles whose first line is a relative directory produced garbage video paths (the directory string was freed before use); a lone "." now means the framefile's own directory. - A crash prints a backtrace to the console (Linux and macOS) so a report can say where it happened; run with --program and send trace.txt too. - Mouse positions are delivered in the video's coordinates again (SDL3 stopped scaling them for us), so crosshairs follow the pointer. - os.clock() in scripts now returns wall time rather than processor time. Nearly every existing game times its input debounces and prompts with it; once the GPU decodes the video the engine mostly sleeps, and those timers ran seconds slow. A game that wanted processor time is not known to exist. This diverges from stock Lua and from other Singe runtimes; new code that must be portable should use singeGetTicks(). - A key or button still held when a game exits no longer counts as a press in the script that follows. Confirming Exit in a game with button 1 used to relaunch it from the menu. - Audio ran late by the device queue depth on most Linux audio servers: the clock assumed one mixer buffer of latency where PulseAudio and PipeWire hold several. See the audio sync entry above. - 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. - One command builds Singe from a clean checkout on a Debian based system: ./build-all.sh installs the host packages, then CMake fetches zig (the compiler for every target), the Pi's platform packages and the macOS SDK into .builddir/toolchains and builds every library and Singe itself. The results are a Linux binary that runs on glibc 2.28 or newer, a Windows 10 or newer binary, a 64-bit Raspberry Pi OS (bullseye or newer) binary and a macOS 13 or newer universal (Apple silicon and Intel) binary. The toolchains repository is no longer needed. See INSTALL. - On a plain video disc, discSearch, discSkipToFrame, discSkipForward, discSkipBackward, discStepForward and discStepBackward clamp at the first and last frame instead of wrapping around the disc. discStepForward and discStepBackward are ignored while the disc is stopped, like the skips; they used to move the frame behind the blue screen. On a framefile the skips apply to the frame number discGetFrame reports, so a skip in any segment after the first lands where it should. - Every collision and trigger event of a frame reaches onCollision and onTrigger; only the first 64 used to, the rest were dropped. onTrigger fires once per body and trigger pair even when a mesh or compound shape touches in several places. playerJump is granted whenever the player is on the ground. - playerSetEnabled(node, false) removes the player's body from the world and reports it leaving every trigger it stood in; enabling puts it back. navPath ends a partial path at the last reachable point. navLoad rejects data that is not a Detour mesh. The world holds at most 4096 bodies; bodyNew raises an error when it is full. - modelLoad refuses a skin with more than 128 joints or a skinned mesh naming a joint its skin lacks (the shader used to read past the matrices). A texture that cannot be decoded, or a missing KTX2 image, is traced and the load goes on without it instead of leaving modelLastError set. KHR_materials_emissive_strength is applied. Sparse accessors load, so Blender shape keys are no longer silently zero. Alpha MASK materials, textures on TEXCOORD_1 and up, and KHR_texture_transform are traced as unsupported. - Video index files are named -.index, the hash from the video's full name, so two videos with one base name in a game stop rebuilding each other's index. Old .index files are ignored and can be deleted. - The packer reports every forbidden file in a directory rather than stopping at the first. --unpack refuses a database whose stored names would land outside the target directory. Launching a .game database from the command line no longer crashes. - --stretch hands the script mouse positions in the game's coordinates at any window size. --scalefactor with --sindengun shrinks the picture inside the Sinden border instead of being cancelled by it. A negative --audio track number is ignored. With no --datadir the controls.cfg beside the script is read once, not twice. - A reload (F5, --reload, singeReload) ignores keys and buttons held across it until they are released, resets the effect channels and their pending onSoundCompleted queue, the held switches and the 2D particle textures, and no longer calls into the closed Lua state when it happens while paused, so the new script starts exactly as a fresh launch does. - Error messages: an invalid view, navigation mesh or navigation agent handle reports "No N." like every other handle; navAgentNew, vehicleSetGears and viewNew state their limits from the engine's constants; fontLoad and soundLoad of a missing file say "Unable to open NAME" instead of the library's "invalid parameter"; navDraw reports its argument count, and running out of memory in it is an error rather than a silent blank; scriptExecute and scriptPush that cannot create the new data directory abort the script with a message instead of exiting. materialNew, sceneGetSize and sceneGetStats no longer reject extra arguments. SINGE 2.10 ========== Released March 26th, 2024 New Features ------------ - spriteDraw() now has two more forms. In addition to being able to draw 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. - Sprite anti-aliasing, scaling and rotation! Optional separate X & Y scaling. - Video anti-aliasing, scaling and rotation! Optional separate X & Y scaling. - Multiple audio track support for videos. New command line options to select which default audio track you want (-o or --audio). For games with multiple languages, you can specify AUDIO_TRACK in the games.dat as well. - New keyboard methods to make handling input easier, especially multiple keypresses for diagonal movement. - controllerGetButton() added. - SINGE_DEAD_ZONE global variable now available based on the DEAD_ZONE controller configuration option. - SINGE_FRAMEWORK_VERSION variable added. - DIR variable added providing directory to the running script. - All new Lua library handling. Previously Singe "injected" libraries 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 Auxiliary Library - LuaFileSystem - LuaSocket - LuaSec - LuaRS232 - timerwheel - binaryheap - Copas - json - New command line option: -p (or --program). This is similar to the existing -t (or --trace) option. Where "trace" displays and logs script execution, "program" displays and logs Singe internals. This should make troubleshooting difficult setups and odd script crashes easier. - Auto-installer for the new GAME, TOOL, and PATCH archive formats to make managing games easier. - ZeroBrane Studio (https://studio.zerobrane.com) integration for live debugging of running Singe scripts! A full IDE for developing games! Fixes ----- - USB controllers now work in games launched from the included menu. - Default controller DEAD_ZONE increased. - Framework.singe was missing keyboard mode constants. MODE_NORMAL and MODE_FULL are now included. - SINGE 2.00 added two new input events but never had them in the framework. SWITCH_BUTTON4 and SWITCH_TILT are now available. - Taking screenshots could sometimes crash Singe. - PNG files no longer generate warnings on the console. - Non-VLDP videos now honor command line volume settings. - Menu system now performs "natural sorting" of game titles instead of brain dead "computer sorting". It should make a lot more sense paging through games now. - Building Singe depended on a lot of undocumented software cobbled together from other projects. This has been vastly improved and should be usable by actual humans now. Humans running Linux anyway. ------------------------------------------------------------------------------ SINGE 2.00 ========== Released October 25th, 2020 New Features ------------ - Complete rewrite! No more dependency on Daphne. - Support for almost every (non-patent encumbered) video and audio format that can be played by FFMPEG. - High definition video. - 32 bit color. - Additional videos can be played on top the laserdisc video. - Scaled sprites. - Graphics overlay resolution no longer dependent on video resolution. - Analog controllers. - LuaSocket and LuaFileSystem added to runtime. - Ability to chain scripts together for menus or launchers. - Specify if you want gun games to display on-screen crosshairs or not. - Menu for launching games included. - Many updates to Framework.singe to support the new APIs. - New APIs: - controllerGetAxis - discGetHeight - discGetWidth - fontUnload - mouseGetPosition - mouseSetCaptured - overlayBox - overlayCircle - overlayEllipse - overlayLine - overlayPlot - overlaySetResolution - scriptExecute - scriptPush - singeWantsCrosshairs - soundUnload - spriteGetHeight - spriteGetWidth - spriteUnload - videoDraw - videoGetFrame - videoGetFrameCount - videoGetHeight - videoGetVolume - videoGetWidth - videoIsPlaying - videoLoad - videoPause - videoPlay - videoSeek - videoSetVolume - videoUnload ------------------------------------------------------------------------------ SINGE 1.14 - 1.19 ================= Released ? Notes ----- - Unofficial fan builds. - Stand-alone releases. New Features ------------ - New API functions: - discPauseAtFrame - mouseEnable - mouseDisable - mouseHowMany - mouseSetMode - singeDisablePauseKey - singeEnablePauseKey - singeGetScriptPath - singeSetGameName - soundFullStop - soundGetVolume - soundIsPlaying - soundPause - soundResume ------------------------------------------------------------------------------ SINGE 1.10 - 1.14 ================= Released ? Notes ----- - Unofficial fan builds. - DLL releases for Daphne. New Features ------------ - New API functions: - discGetState - keyboardGetMode - keyboardSetMode - singeGetPauseFlag - singeSetPauseFlag - singeQuit - singeVersion - soundStop ------------------------------------------------------------------------------ SINGE 1.00 ========== Released 2006 Notes ----- - Original add-on for Daphne. - Shipped with the ActionMax "emulator" and games.