singe/docs/Manual.adoc

978 lines
61 KiB
Text

= Singe Manual
Scott Duensing <scott@kangaroopunch.com>
:revnumber: 3.00
:revdate: 2026
:doctype: book
:toc: left
:toclevels: 3
:sectnums:
:sectnumlevels: 3
:title-logo-image: image:images/singeLogo.png[Singe, pdfwidth=2.6in, align=center]
:source-highlighter: rouge
:icons: font
:experimental:
== About Singe
Singe (SINGE Is Not a Game Emulator, and named after the dragon in
Dragon's Lair) is a Lua-based scripting system that allows for rapid prototyping
of new laserdisc games, or the creation of entirely new games. The language is
easy to learn, very powerful, and fast. All the features needed to develop your
own game are made available through a simple application programming interface
(API).
Singe provides numerous features to the game developer. Some of the more
interesting ones are:
* Object-oriented programming language
* Animated sprites
* TrueType font support
* 32-bit color space with transparency
* Multi-channel, overlapping, stereo sound
* Analog and digital input device support
* Wide support for video and audio formats
For players, Singe allows many unemulated and new games to be enjoyed using any
desired front end or via the included menu system.
Since the original release of Singe back in 2006, several revisions have been
released, both officially and unofficially. In 2020, Singe 2.00 was released.
This was a total rewrite from the ground up, adding numerous features while
staying compatible with existing 1.xx games. As of the writing of this manual,
the latest release is {revnumber}.
== Installation and Upgrading
=== Installing Singe
To install Singe, simply download the appropriate binary for your system. Place
it in a new folder by itself and run it. On the first run, Singe will create
all the necessary directories and support files needed. You are now ready to
install some games.
=== Upgrading Singe
To upgrade Singe to a newer version, place the new binary in the installation
folder beside the old one and run it once with `--refresh`. The support folder
that is there is set aside as `old-Singe-<version>`, named for the version that
wrote it, and a fresh `Singe/` folder is written in its place. Nothing is
deleted: the folder that was set aside is yours to keep or remove, and a second
`--refresh` sets its own aside as `old-Singe-<version>-1` rather than writing
over the first.
The old binary can stay where it is. `Menu.sh` (or `Menu.bat`) runs the newest
Singe it finds beside itself, and a beta counts as older than the release of the
same number, so `Singe-v3.00b2` gives way to `Singe-v3.00` the moment it
arrives. The menu's Engine Update tool does all of this for you; see
<<servicetools,The Service Tools>>.
WARNING: Do not run a new Singe binary against an out-of-date set of support
files. Always refresh the `Singe/` directory when upgrading.
=== Things to Know
Do not touch the `Singe/` folder. Files in this folder belong to Singe and may
be updated or deleted at any time.
== Usage
=== Installing Games
A game is either a single `.game` file or a directory. Copy it into the same
folder where you placed the Singe binary. The included menu system will
automatically detect it and add it to the menu.
=== Game Directory Layout
A Singe installation is a game directory containing the `Singe` support
directory the engine creates on first run, one directory per game, any
packed `.game` files, and a `data` directory for everything the games and
the engine write. The executable usually sits there too, but need not:
`--gamedir` names the game directory from anywhere, and the `Menu.sh` (or
`Menu.bat`) the engine writes there then runs the executable by its full
path. Otherwise the launcher searches beside itself for the files whose
names begin with `Singe` and runs the best of them, in the spirit of the
launchers `BatMaker` writes, so an upgrade that changes the executable's
version in its name does not leave a launcher pointing at a file that is no
longer there.
Best means this machine's architecture first and the version after it. The
platform in the name is compared against what the system reports, because a
newer build for another architecture cannot run at all, and one game directory
on a share can be read by a Pi and a PC alike. The launcher works this out when
it runs rather than being told once, so the same directory is right for every
machine that opens it. On a Mac a universal build counts as native. Among the
builds this machine can run, the newest wins: `Singe-v3.10` beats
`Singe-v3.09`, and a beta loses to the release it leads up to, so
`Singe-v3.00b2` loses to `Singe-v3.00`. A build for another architecture is
still run when it is the only one there, since a real complaint from the engine
beats being told nothing was found, and so is a name the launcher cannot read
at all.
----
Singe/ Support files extracted by the engine
Framework.singe Loaded by every game (dofile it)
Menu.singe The bundled game menu
Tools.singe The menu's service tools (see The Service Tools)
MenuDocument.singe The menu drawn as a GUI document
MenuOverlay.singe The menu drawn into the overlay, for a machine with no GPU
Backdrop.singe The menu's intro and the grid behind it, drawn live
menuBackground.mkv The same backdrop recorded, for a machine with no GPU
menuIntro.flac Its sound, which fades out as the menu takes the screen
Menu.rml The document renderer's RmlUi document
menu.rcss Its style sheet, on top of gui.rcss
gui.rcss The shipped GUI theme
controls.cfg.example Template for input mappings
click.wav Used by the menu's audio delay and sound tests
Manual.pdf This manual
COPYING The GNU General Public License, version 3
LICENSES Third-party notices and asset credits
ActionMax/ One game
games.dat Menu entries for the games in this directory
38AmbushAlley.singe A script
frame_38AmbushAlley.txt Its framefile (or a video with the same base name)
sprite_*.png, sound_*.wav, font_*.ttf
DLe.game A game packed into one file (see Single-File Games)
data/
machine.cfg This machine's audio delay and picture geometry
ActionMax/ Indexes, trace.txt, screenshots, saves for that game
----
Where Singe looks for a script's files, and where a script may write, is a
matter for whoever writes the script; the Singe Reference covers both under
Packaging Your Game.
[#bundledmenu]
=== The Bundled Menu
`Menu.sh` (or `Menu.bat`) starts `Singe/Menu.singe`, the bundled menu: an
RmlUi document, `Singe/Menu.rml` styled by `Singe/menu.rcss`, drawn over the
engine's own disc (see xref:Reference.adoc#gui[GUI]). It reads the `games.dat` of every game
directory and `.game` file beside it and lists every game it finds, sorted by
title, down the left of the screen. Beside the list are the selected game's
cabinet art, its marquee and its attract clip, with the year, genre,
platform, developer, publisher, port credits and description underneath and
a line of key hints along the bottom.
Up and down move the selection one game at a time and left and right move it
a page at a time; both wrap from either end of the list to the other. Start
or any action button launches the selected game, and the key mapped to
`INPUT_SERVICE` (the `9` key by default) opens the service tools (see
<<servicetools,The Service Tools>>). With a mouse, a
click on a row selects that game and a second click on the same row, or a
click on the Start button in the footer, launches it; the mouse wheel
scrolls a description too long for its panel. The menu keeps its selection
in `menu.dat` in its data directory, so it comes back to the same game when
one quits or when Singe is next started. That file is a Lua fragment the menu
reads at startup and rewrites at shutdown: `GAME_SELECTED` is the game it was
showing, `SHOW_INTRO` is false when it is returning from a game so the intro
is not sat through twice, and `MENU_OVERLAY` chooses a renderer (see
<<menurenderers,The two renderers>>).
[#menurenderers]
==== The Two Renderers
A GUI document is drawn through the GPU device the 3D scene uses, so on a
machine without one there is no document to build. The menu handles that
itself: `Singe/Menu.singe` asks xref:Reference.adoc#singehasgpu[singeHasGpu] at startup and
loads one of two renderers.
`Singe/MenuDocument.singe`::
The RmlUi document described above, for a machine with a GPU device. It also
draws the intro and the grid behind the menu as one continuous 3D scene, so
the disc is not used for either.
`Singe/MenuOverlay.singe`::
Sprites and `fontPrint` straight into the overlay, the look Singe 2 had. It
needs nothing beyond the overlay every game already draws into.
Both draw the same menu, take the same keys and run the same service tools;
`Singe/Menu.singe` holds everything that is not drawing -- the game list, the
selection, launching a game -- so there is one menu rather than two. The
overlay renderer shows one game at a time instead of a list, since it has no
panel to scroll, and it clips a description too long for its column rather
than scrolling it.
To see the overlay renderer on a machine that does have a GPU, put
`MENU_OVERLAY = true` in `menu.dat` in the menu's data directory; the menu
writes the line back out, so it survives. That is how the overlay path is
tested, and how a cabinet builder who prefers the old look gets it.
[#onlineservice]
==== Online: Accounts, Games, and Scores
The bundled menu can sign the cabinet in to a master service (by default
`master.singeengine.com`), which does three things: it holds the account, it
offers a catalogue of games to download and update, and it keeps high score
boards.
Two service tools cover it. **Online Account** signs in, creates an account,
recovers a password, and sets the server address -- it is where an operator
running their own service points the cabinet. A new account is not usable until
the emailed link is opened. **Get Games** lists everything the
service offers, marks what is installed here, and downloads, updates or removes
it. The menu itself says when a newer version of an installed game exists: the
game wears an `update` mark in the list, the line under its details names the
version, and button 3 on that page fetches it, so a cabinet need not open a
service tool to stay current. Nothing about this machine is kept anywhere: the
menu reads the version out of each game's own `games.dat` and asks about its own
list, so a game copied in by hand is known as well as one downloaded here. A list longer than the screen scrolls with the selection rather than running
off the bottom, with `...` showing there is more either way. A game that stops being offered keeps working and is shown as "installed,
no longer offered": all that stops is being given an update, because a player
who has a game has it. A download is checked against the digest the catalogue published before it
replaces anything, so a transfer that arrives wrong fails rather than installing
a game that will not run.
The account belongs to the *machine*, not to a game: the token is kept in
`master.dat` in the data root, so a game posting a score uses the same sign-in
the menu made. See xref:Reference.adoc#onlinescores[Online high scores] for the game's side, and
`Singe/Net.singe` for the asynchronous HTTP client underneath -- nothing here
blocks a frame, because Lua in Singe is cooperative and a large download would
otherwise look like a hang.
[#servicetools]
==== The Service Tools
The key mapped to `INPUT_SERVICE` opens a list of tools over the game list.
Up and down choose one and button 1 opens it; button 2 backs out of a tool to
the list, and the service key again leaves the tools altogether. Everything
lives in `Singe/Tools.singe`, which the engine extracts beside `Menu.singe`.
Nothing there draws: a tool builds a list of rows and hands it to the menu,
which is why the same ten tools run on both renderers (see
<<menurenderers,The two renderers>>). Nothing here needs a game to be running,
and nothing changes a game's own files.
Audio Delay::
The downstream audio delay, measured against a click and a flash. See
xref:Reference.adoc#menucalibration[Calibrating from the menu] for what it is doing and why it
works.
Input Test::
Every controller, mouse and gun the engine can see, live: the last switch the
game would have received and how many have arrived, each pad's name with its
six axes and the buttons held down, and each mouse's name and position. This
is the screen that answers "the button does nothing" -- either the switch
arrives and the game ignores it, or it never arrives at all.
System Information::
The engine's version, the operating system, the processor and memory, the
renderer and the 3D device, which video decoders are in play, the audio
formats built in, the SoundFont, the MIDI state, the window, canvas and
overlay sizes, and the data directory. A photograph of this page answers most
of a bug report.
Light Gun::
Targets in the centre and at each corner of the overlay, over whatever the
disc is showing. Button 1 marks where the gun actually pointed, keeping the
last twelve shots; start clears them. A gun that is square on the picture puts
its crosses on the targets, and one that is not shows which way it is out.
Sound Test::
Button 1 plays the click through the left speaker, then the right, then both,
which is the whole of a crossed-wires check. It also lists the disc's own
audio tracks, and left and right switch between them.
Display::
The picture's size, position and rotation, changed live with the arrows,
buttons 3 and 4, and coin 1, over a test pattern: a 16 by 12 grid to square
the picture up with, an eight step greyscale wedge to set brightness by, and
two frames one pixel apart at the very edge -- a monitor that overscans eats
the outer one first. Button 1 keeps the values for this machine, in the same
`machine.cfg` the audio delay uses, so they apply to every game rather than
being set once per game. Start puts everything back.
Disc Test::
Twelve seeks spread over the whole disc, each timed. A rip whose keyframes are
far apart seeks slowly, and the only symptom in a game is that everything
feels heavy; this measures it directly and reports the worst of the twelve.
A seek that has not landed in three seconds is counted as that.
Saved Data::
What each game has kept, found by looking through the data root rather than by
being told, with the size of each. Button 3 -- not button 1, which is "choose"
everywhere else -- throws away the selected game's save. Clearing a high score
table without deleting the game is the usual reason.
Engine Update::
A newer Singe, when the service has one for this machine. A beta is versioned
`3.00b2`, the second beta of 3.00, and comes before 3.00 itself, so a cabinet
running a beta is offered the release the day it is published. Shows what is running
and what is offered, and button 1 fetches it, checks it against the digest the
service published, puts it beside the running Singe under the release's own
name, sets the old one aside as `old-<name>`, and restarts into it. Setting
the old one aside can be refused, by a folder this user cannot write or by a
scanner holding the file; the tool says so and carries on, since the launcher
runs the newest Singe in the folder either way. The new
Singe starts with `--refresh`, so the support folder is rewritten by the
version that now owns it and the old one is kept as `old-Singe-<version>`; a
file an older Singe shipped and this one does not cannot linger to confuse a
game. Nothing happens until that button. Button 3 asks the service again. The menu looks
once a day on its own and says so in a line under the game's details;
`no_update_check = true` in `settings.cfg` stops the daily look, and the tool
still works by hand.
Online Account::
Signing this cabinet in to the master service, creating an account, recovering a
password, and setting the server address. See
<<onlineservice,Online: accounts, games and scores>>.
Get Games::
The catalogue: what the service offers, what is installed here, and downloading,
updating or removing it.
MIDI Ports::
The input and output ports this machine has, which of them Singe has opened,
and the last few messages that arrived. Button 1 sends a note, button 3 looks
for ports again after something has been plugged in. Nothing is opened until
this tool asks, so a cabinet that never touches MIDI never pays for it.
Frame Statistics::
Turns the developer's overlay on and off. It stays on into the game, in the
top left corner. See xref:Reference.adoc#statsenable[statsEnable].
[[controls]]
=== Customizing the Controls
By default, Singe maps controllers as if they were Xbox gamepads. If your
controller is not recognized or you wish to change the default mappings as they
appear to Singe, you can use the free SDL2 Gamepad Tool from
http://generalarcade.com/gamepadtool/.
[[devices]]
==== Every Device Is a Gamepad
Singe has one family of controller, `GAMEPAD_0` through `GAMEPAD_3`, and every
device it opens is in it. SDL calls a device a gamepad only when it has a
mapping for it, and an arcade encoder board, a spinner, a wheel or a flight
stick often has none. Rather than carry a second kind of device with its own
names, Singe writes a mapping for such a device itself and then opens it as an
ordinary gamepad, so it reaches `controls.cfg`, `controllerGetAxis`,
`controllerGetButton` and every `INPUT_*` switch exactly as a recognised pad
does.
The mapping Singe writes is a straight-through one: the device's axes in order
become the left stick, the right stick and then the two triggers; its buttons
in order become `BUTTON_A`, `BUTTON_B`, `BUTTON_X`, `BUTTON_Y`, `BUTTON_BACK`,
`BUTTON_GUIDE`, `BUTTON_START`, `BUTTON_LEFT_STICK`, `BUTTON_RIGHT_STICK`,
`BUTTON_LEFT_BUMPER` and `BUTTON_RIGHT_BUMPER`; and its first hat becomes the
D-pad. A device with no hat spends its next four buttons on the D-pad instead.
**A device with more controls than the gamepad model can name loses the
extras** -- more than six axes, more than fifteen buttons, or a second hat --
and a device whose buttons are not wired in that order will have them in the
wrong places. That is the price of one family instead of two, and it is
straightforward to correct: `--program` traces the mapping it wrote, in SDL's
own format, so you can paste it into a `gamecontrollerdb.txt`, rearrange it and
have it used instead.
`--mapjoysticks=false` turns the writing off, in which case a device SDL does
not recognise is not opened at all, exactly as in earlier releases.
[[gamecontrollerdb]]
==== gamecontrollerdb.txt
SDL ships with a large database of controller mappings, and the community keeps
a larger one -- `gamecontrollerdb.txt`, from
https://github.com/mdqinc/SDL_GameControllerDB. Singe looks for that file in
the same four places it looks for `controls.cfg`:
. the directory Singe was started in;
. the directory above the game's data directory;
. the game's data directory;
. the game's main script directory.
Every file found is loaded, in that order, so one beside a game wins over a
machine-wide one, and both win over SDL's own database. Only a device that
none of them names gets the mapping Singe writes for it, so the order of
precedence is: SDL's built-in database, then your `gamecontrollerdb.txt`, then
the synthetic mapping. `--program` reports how many mappings each file added.
A line of that file looks like this, and is exactly what the trace prints for a
device Singe mapped itself:
----
ff00d87a475300004900000000007603,Arcade Encoder,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b4,guide:b5,start:b6,leftx:a0,lefty:a1,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
----
In addition to configuring your controller, you can remap all the input options
for Singe as a whole or for any individual game by using `controls.cfg` files.
When determining how inputs are mapped, Singe reads configuration settings in
the following order:
. The defaults from inside Singe.
. `controls.cfg` from the directory Singe was started in (the `Singe` directory holds a `controls.cfg.example` template you can copy).
. `controls.cfg` from the directory above the game's data directory.
. `controls.cfg` from the game's data directory.
. `controls.cfg` from the game's main script directory.
You do not have to specify every option in a custom `controls.cfg` file. Feel
free to just provide the entries you want changed.
After installing Singe, you can find a sample input configuration file at
`<InstallDir>/Singe/controls.cfg.example`. To see the available configuration
options available to be used in this file, read through
`<InstallDir>/Singe/Framework.singe`.
`--keymapfile` names one file to read instead of making that search, which is
useful to a front end that wants no doubt about which mappings a game got.
[[deadzones]]
==== Dead Zones and Triggers
`DEAD_ZONE` in `controls.cfg` is how far an axis must move before it counts as
a pressed switch, in the raw units SDL reports: `32767` at full deflection, so
the default of `15000` is a little under half way. It covers every axis of
every pad, the two analogue triggers included, and a script reads it as
`SINGE_DEAD_ZONE`.
Two settings narrow that down.
`DEAD_ZONES` is a list of `{ axis, dead zone }` pairs for one axis at a time,
in those same raw units, for a stiff stick or a worn trigger that should not
move everything else:
[source,lua]
----
DEAD_ZONES = {
{ GAMEPAD_0.AXIS_LEFT_X, 8000 },
{ GAMEPAD_1.AXIS_LEFT_TRIGGER, 30000 },
}
----
The axis may be written as the axis itself or as either of its two directions
(`GAMEPAD_0.AXIS_LEFT_X`, `AXIS_LEFT_X_L` and `AXIS_LEFT_X_R` all name the same
axis). Anything not listed keeps `DEAD_ZONE`.
`TRIGGER_THRESHOLD` is how far an analogue trigger must travel before it counts
as a button, as a *per cent of full travel*, which is the unit Hypseus's
`-trigger-threshold` uses and whose default there is `99.5`. It applies to both
triggers of all four pads. Singe's default is `0`, meaning the triggers stay on
`DEAD_ZONE`, so nothing changes for anyone who does not ask; a script reads the
result in raw units as `SINGE_TRIGGER_THRESHOLD`. The two units convert
directly: a per cent is `32767 / 100` raw units, so Singe's `DEAD_ZONE` of
`15000` is `45.8` per cent and Hypseus's `99.5` per cent is `32603` raw units.
`--trigger_threshold` sets the same number from the command line and wins over
`controls.cfg`; `DEAD_ZONES` wins over both, being the most specific.
[[joymouse]]
==== Driving the Mouse with a Stick
A light gun game on a cabinet with a stick and no mouse is unplayable unless
the stick can move the cursor. `--joymouse` makes it do so: the **left stick of
gamepad slot 0** moves the pointer the game sees, through the same
`onMouseMoved` a real mouse reaches, and the cursor is held inside the picture.
`--js_range` is the speed, `1` to `20` with `5` the default, measured in video
pixels a frame at full deflection, so `5` crosses a 720 pixel picture in about
two seconds. The axis obeys its own dead zone, so a drifting stick does not
creep.
It is off by default, which is where Singe differs from Hypseus: the shipped
`controls.cfg` already binds that stick to `INPUT_UP`, `INPUT_DOWN`,
`INPUT_LEFT` and `INPUT_RIGHT`, and having it drive the cursor as well without
being asked would surprise every existing game. `--joymouse=false` is
Hypseus's `-nojoymouse`, and a script turns it on and off with `joyMouseEnable`
and reads it with `joyMouseIsEnabled`. A reload puts it back where the command
line left it.
[[mousedevices]]
==== Mice, Guns, and Who Chooses
`mouseSetMode(MOUSE_MANY)` is a game's decision to tell up to four mice apart,
which a two gun cabinet needs and which a game that never makes the call cannot
be given. `--manymouse` is the user's decision and outranks it: the mode is on
from the start and `mouseSetMode(MOUSE_SINGLE)` no longer switches it off.
`--absolutes_only` keeps only the devices that report an absolute position,
which is what a real light gun does and an ordinary mouse does not, so a
cabinet with two guns and a desk mouse plugged in is not confused by the mouse.
Be aware of how it decides: **ManyMouse, the backend Singe uses to tell mice
apart, offers no way to ask a device what kind it is**, so a device counts as
absolute once it has reported an absolute position and not before. A gun does
that with its first movement; until then it is silent, and a relative-only
device stays silent for good. The option is therefore a filter by observed
behaviour rather than by device class, and on a machine where no device ever
reports an absolute position it silences every mouse.
`SWITCH_MOUSE_DISCONNECT` is a switch like any other, except that no key or
button produces it: the engine raises it when a mouse or a light gun is
unplugged, once as a press and once as a release, so a gun game can say so on
screen instead of appearing to have died. It is Hypseus's switch of the same
name. `controls.cfg` leaves it bound to nothing; bind a key to it as well if
you want to see what a game does without pulling a cable.
[[hotkeys]]
==== Hotkeys
`Alt-Enter` switches between full screen and a window while the game runs, as
it does in Hypseus. The game never sees the key. A window that started in
exclusive full screen (`--fullscreen`) returns to it in the display mode it was
given; one that started windowed gets a borderless window covering the desktop.
Nothing else has to change with it: the renderer presents a logical size in
video coordinates and scales that to whatever the window now is, the video
rectangle is built in those same coordinates, and the rotation target is the
size of the picture rather than of the window, so the picture, the overlay, the
GUIs and the mouse mapping all follow by themselves.
The other switches a user can press -- quit, pause, screenshot, the console and
the mouse capture toggle -- are ordinary `controls.cfg` mappings and are
described with the `INPUT_*` names in the sample file.
[[settings]]
=== The Settings File
Everything on the command line can also be written down once, in a
`settings.cfg` file, so a cabinet does not need a launcher script carrying the
same dozen options for every game. It is Lua, like `controls.cfg`, and Singe
looks for it in exactly the same four places, in this order:
. `settings.cfg` from the directory Singe was started in (the `Singe` directory holds a `settings.cfg.example` template you can copy).
. `settings.cfg` from the directory above the game's data directory.
. `settings.cfg` from the game's data directory.
. `settings.cfg` from the game's main script directory.
Each place is read once, and a later file wins key by key, so a machine-wide
file can set the display and the volumes while a file beside one game turns off
its crosshair. You do not have to write every key in a file; write the ones you
want changed.
Every key is the long name of a command line option with the leading dashes
removed, so there is no second vocabulary to learn:
[source,lua]
----
-- The cabinet's own settings.cfg
screen = 2 -- the second monitor
fullscreen = true
volume_vldp = 80
haptic = 0 -- this cabinet has no rumble
idleexit = 900 -- hand the screen back after fifteen quiet minutes
startsilent = true
----
An option that takes no value on the command line takes `true` or `false` here
(`yes`, `no`, `on`, `off`, `1` and `0` are accepted too). Setting one to `false`
is the same as leaving it out. An option that takes a value takes the same value
it would on the command line, as a number or a string.
One option, `--deterministic`, takes a value or leaves it out, and its key does
the same: `deterministic = true` uses the default step, `deterministic = 20`
names one, and `deterministic = false` leaves the option off.
==== What Wins
. The built in default.
. The settings file.
. The game's `games.dat` entry, for the handful of settings it carries (see xref:Reference.adoc#gamesdat[games.dat]).
. `machine.cfg`, for the audio delay and the picture geometry the service tools
save (see <<servicetools,The Service Tools>>).
. The command line, which always wins.
A settings file is a set of defaults, in other words, not something you typed:
if a `games.dat` entry gives a game a resolution or a Sinden border, that entry
still describes the game better than a machine-wide file does. Anything actually
typed on the command line beats both. `machine.cfg` sits just under the command
line because somebody adjusted the picture while looking at this monitor, which
is better evidence than any file written elsewhere; the audio delay is the
exception to the ordering entirely, being added to the game's delay rather than
replacing it.
Options that name the game (`--framefile`, `--disc`, `--entry`), name a
directory (`--gamedir`, `--datadir`), or have to act before the file could be
found (`--help`, `--apiversion`, `--noconsole`, `--pack`, `--patch`,
`--unpack`) cannot be set in a settings file. A key that is not an option, one
of those, or a value the option will not take is an error naming the file and
the key, so a typo is never silently ignored:
----
settings.cfg: volume_vldpp is not an option.
settings.cfg: datadir cannot be set here; it belongs on the command line.
settings.cfg: Display scale must be between 25 and 100 percent.
----
With `--program` or `--trace`, the header at the top of `trace.txt` lists every
setting that was applied, so there is never a question about which file a
running game was given.
[[commandlineoptions]]
=== Command Line Options
----
Singe [OPTIONS] gameName
----
The game name is the only required argument. It may be a `.singe` script, a
directory containing a script of the same name (`ActionMax` finds
`ActionMax/ActionMax.singe`), or a packed game (`DLe.game` runs the first
entry of the `games.dat` inside it, `--entry=N` another). A script or directory
launched this way also runs with its `games.dat` entry when one names it (see
xref:Reference.adoc#gamesdat[games.dat]), so a game plays the same from the command line as from
the menu. For a laserdisc game (`--disc`) with no
`--framefile`, Singe looks for a video next to the script with the same base
name and any extension FFmpeg can demux, then for a `.txt` framefile.
[cols="1,2",options="header"]
|===
| Option | Purpose
| `-A`, `--audiodelay=MS` | Compensate for audio that is heard `MS` milliseconds later than the engine can measure (negative when it is heard early), `-1000` to `1000`. See xref:Reference.adoc#audiosync[Audio Sync].
| `-a`, `--aspect=N:D` | Force the aspect ratio used to pick a window size (`4:3`, `16:9`, `16:10`).
| `-B`, `--bezel=FILENAME` | Draw cabinet artwork around the picture. The file is looked for in the `bezels` folder of the game directory (a packed game may carry its own) and then in the `bezels` folder of the data directory; `--bezeldir` names a different folder. Any format SDL_image reads will do, and transparency is kept. A sidecar beside it says where the picture goes inside it; with none, the picture keeps the whole window and `--shiftx`, `--shifty` and `--scalefactor` place it. Artwork that cannot be found ends the program. See <<bezels,Bezels>>.
| `-b`, `--scalefactor=PERCENT` | Shrink the picture about its centre to `PERCENT` (`25` to `100`) of the window for displays that lose their edges to overscan. Mouse and light gun positions follow the shrunken picture. A script may change it while the game runs with `vldpSetScale`. Combines with `--shiftx`, `--shifty`, `--rotate` and `--sindengun`; see <<videorect,The Video Rectangle>>.
| `-c`, `--showcalculated` | Print the frame ranges of every segment of a framefile, for debugging.
| `-C`, `--canvas=WxH` | World size for a game without a disc, default 720x480. Ignored when there is a disc.
| `-D`, `--disc` | Play a laserdisc video: the one named by `--framefile`, or the video found next to the script. Implied by `--framefile`. Without it a video next to the script is reported and ignored.
| `-d`, `--datadir=PATHNAME` | Directory for everything Singe writes: video indexes, `trace.txt`, screenshots, saves, the menu's `menu.dat`. A subdirectory named for the game's directory is created inside it (for a packed game, one named for the database and then for the script's directory inside it). Defaults to `data` in the game directory. Singe 2.x wrote beside the game instead; game directories may be read only, so nothing is written there any more.
| `-E`, `--entry=N` | Run the Nth entry of the `games.dat` inside a `.game` file (default 1). See xref:Reference.adoc#singlefile[Single-File Games].
| `-e`, `--volume_nonvldp=PERCENT` | Sound effect and extra video volume, `0` to `100`.
| `-F`, `--bezelflip` | Draw the bezel artwork in front of the picture instead of behind it, which is what Hypseus does with its own bezels; the artwork's transparency is then the hole the game shows through. Without it the artwork is behind everything and the picture covers its cutout. A script chooses whether the overlay and the GUIs stay above the artwork with `setOverlayOnTop`. See <<bezels,Bezels>>.
| `-f`, `--fullscreen` | Exclusive full screen at the desktop resolution.
| `-G`, `--gamedir=PATHNAME` | The directory holding the games, the packed `.game` files and the `Singe` support folder, when the executable lives somewhere else. It becomes the working directory, so the game name, a relative `--datadir` and the packer's names all count from it; absolute paths work as they are. Defaults to the current directory.
| `-g`, `--sindengun='PARAMS'` | Draw the border a Sinden light gun tracks and map mouse and light gun positions into the picture inside it: the black ring, when one is given, runs along the edge of the video, the white ring sits inside it, and the game is drawn in the middle. `PARAMS` is one quoted list of numbers: `WW` (white border width in video pixels); `WW WB` (white width, then a black border outside it); `RW GW BW WW` (a custom border color and width); `RW GW BW WW WB` (the same plus a black width); or `RW GW BW WW RB GB BB WB` (custom colors and widths for both). A border that leaves no room for the game (twice the white and black widths together reaching the smaller side of the video), or a negative width, ends the program with a message. The rings are drawn around whatever `--shiftx`, `--shifty` and `--scalefactor` left, so a border stays a border however the picture is placed; see <<videorect,The Video Rectangle>>.
| `-H`, `--softwarevideo` | Decode video in software even when the platform offers a hardware decoder (VA-API or VDPAU on Linux, the V4L2 decoder on the Pi, D3D11VA on Windows, VideoToolbox on macOS). Use it to rule the hardware path in or out when a video misbehaves; the program trace says which decoder is in use.
| `-h`, `--help` | Show the option summary and exit.
| `-I`, `--bezeldir=PATHNAME` | The folder holding `--bezel`'s artwork, in place of `bezels`. Relative to the game directory, or absolute. Only that folder is searched. See <<bezels,Bezels>>.
| `-k`, `--nologos` | Skip the splash screens.
| `-l`, `--volume_vldp=PERCENT` | Laserdisc volume, `0` to `100`.
| `-m`, `--nomouse` | Disable mouse and lightgun input entirely.
| `-n`, `--nocrosshair` | Ask the game not to draw its own crosshair. Exposed to scripts as `singeWantsCrosshairs()`.
| `-o`, `--audio=TRACK` | Default audio track (zero based) for videos with several, such as multi-language releases.
| `-P`, `--pack=DIRECTORY` | Pack the game in `DIRECTORY` into the database named after the options, then exit. See xref:Reference.adoc#singlefile[Single-File Games].
| `-p`, `--program` | Trace engine activity to the console and to `trace.txt` in the data directory. The file opens with a header describing the machine; see xref:Reference.adoc#traceheader[The Trace Header].
| `-S`, `--sindenedge=WHERE` | Where the Sinden border sits: `video` draws it around the picture, `window` at the edge of the screen. Without the option a bezel decides: artwork around the picture puts the border at the window's edge, because the gun's camera sees the whole screen, and with no artwork it goes around the picture as it always has. At the window's edge the two rings are drawn as rings, last of all, so the artwork between them and the picture stays visible. See <<bezels,Bezels>>.
| `-s`, `--nosound` | Mute all audio.
| `-T`, `--patch=GAME.game` | Replace files in the packed game from the directory or patch database named after the options, then exit.
| `-t`, `--trace` | Trace every Lua API call, with the script line that made it, to the console and to `trace.txt`. The file opens with a header describing the machine; see xref:Reference.adoc#traceheader[The Trace Header].
| `-r`, `--rotate=DEGREES` | Turn the whole presentation clockwise by `0`, `90`, `180` or `270` degrees, for a cabinet whose panel is mounted on its side. The video, the overlay, the GUIs, the 3D scene and the particles all turn together, a quarter turn swaps the shape of the picture so a portrait window is filled, and mouse and light gun positions are turned back so a click still lands where it is drawn. Only the four right angles are accepted. A script may change it with `vldpSetRotate`.
| `-R`, `--reload` | Watch the game's loose script files and run the game again from scratch when one changes; `F5` does the same on demand. A script that fails to compile stays watched, so saving the fix reloads it. For working on a game; see Reloading While You Work.
| `-U`, `--unpack=GAME.game` | Write the packed game's files into the directory named after the options, then exit. A database whose stored names would land outside that directory is refused.
| `-u`, `--stretch` | Stretch the video to fill the window instead of keeping its aspect ratio.
| `-v`, `--framefile=FILENAME` | Video file or framefile to use instead of the one found next to the script.
| `-w`, `--fullscreen_window` | Borderless window covering the desktop.
| `-X`, `--shiftx=PERCENT` | Move the picture horizontally inside the room `--scalefactor` left it, `-100` (hard against the left) to `100` (hard against the right), `0` centred. As in Hypseus, the shift is a percentage of that room and nothing more, so at `--scalefactor=100` there is no room and the shift does nothing: reduce the scale first. See <<videorect,The Video Rectangle>>.
| `-x`, `--xresolution=VALUE` | Window width. The height is taken from the matching entry of the built in resolution table when omitted.
| `-Y`, `--shifty=PERCENT` | Move the picture vertically inside the room `--scalefactor` left it, `-100` (hard against the top) to `100` (hard against the bottom), `0` centred. It does nothing at full scale, for the same reason. See <<videorect,The Video Rectangle>>.
| `-y`, `--yresolution=VALUE` | Window height.
| `-z`, `--noconsole` | Print nothing (and open no console window on Windows).
| `--absolutes_only` | Keep only the mice that report an absolute position, which is what a real light gun does and an ordinary mouse does not. ManyMouse cannot be asked what a device is, so a device counts as absolute once it has reported an absolute position and not before; see <<mousedevices,Mice, Guns, and Who Chooses>>. Hypseus writes it `-absolutes-only`; the name here uses an underscore because a settings file key has to be a Lua name. Default: off.
| `--altaudio=SUFFIX` | Play `<base><SUFFIX>.ogg` beside the disc's video instead of the audio inside it, for a release whose other languages ship as separate files: `--altaudio=-es` next to `lair.m2v` plays `lair-es.ogg`. Every segment of a framefile is switched together. A file that is not there leaves the game's own audio playing and prints a warning. The `AUDIO_SUFFIX` key in `games.dat` does the same for one game, and a script changes it while running with `discAudioSuffix`. Default: none.
| `--apiversion` | Print one machine readable line describing this build to standard output and exit, for front ends. See <<apiversion,The Version Line>>. Nothing else is printed.
| `--deinterlace=MODE` | What an interlaced picture gets: `auto` (the default) deinterlaces only the frames the file marks interlaced, `on` deinterlaces every frame for a file whose flags are wrong, and `off` never touches the picture. A laserdisc held interlaced fields and a rip that kept them combs on a progressive display; a rip that was deinterlaced when it was made needs nothing here. One picture comes out for each one that goes in, so frame numbers never move. Default: `auto`.
| `--deterministic[=MS]` | For testing only: ignore real time and run the whole engine on a virtual clock that moves `MS` milliseconds every frame, so the same frame number always means the same moment. The disc follows the virtual clock too, its frames lasting the time they are worth, and the random generators are seeded from the same number, so a run repeats to the pixel. The value is optional and is both the step and the seed, `1` to `1000`. Default when given without one: `15`, the frame time the engine's own rate implies. Audio and pacing are meaningless in this mode; see xref:Reference.adoc#deterministic[Deterministic Test Mode]. Default: off.
| `--fvalue=NUMBER` | One number handed from the launcher to the game, which reads it with `getFValue()`. Singe does nothing with it. `0` to `100000`, kept to three decimals as Hypseus keeps it. Default: `0`.
| `--gamepad_reorder=DIGITS` | Which physical pad fills which gamepad slot, as enumeration positions counting from `0`, one for each slot in turn: `--gamepad_reorder=10` makes the second pad found player one and the first player two. Written as bare digits (`3210`) or separated by commas or spaces, as Hypseus writes it; a repeated position is refused. Positions not named fill the slots that are left, in the order SDL found them, and a position with no pad behind it leaves its slot empty. Default: SDL's own order.
| `--haptic=STEP` | The strongest rumble step a game may use, `0` to `4`. `0` turns rumble off altogether, and a lower number quietens a game that asks for more: `controllerDoRumble` never rumbles harder than this. Default: `4`, which is every step a game asks for.
| `--idleexit=SECONDS` | Quit after `SECONDS` with no input of any kind -- no key, no button, no pad axis, no mouse. For an attract cabinet that should hand the screen back. `0` never quits. `0` to `86400`. Default: `0`.
| `--joymouse=BOOLEAN` | Let the left stick of gamepad slot `0` drive the mouse cursor, for a light gun game on a cabinet with no mouse. `--joymouse=false` is Hypseus's `-nojoymouse`, and a script sets it with `joyMouseEnable()`. See <<joymouse,Driving the Mouse With a Stick>>. Default: `false`, where Hypseus's default is on -- the shipped `controls.cfg` already binds that stick to the four directions.
| `--js_range=SPEED` | How fast that stick drives it, `1` to `20`, in video pixels a frame at full deflection. Default: `5`, as in Hypseus.
| `--keymapfile=FILENAME` | Read the control mappings from this file instead of searching the four places for `controls.cfg`, so a front end can be certain which mappings a game got. The built in defaults still apply underneath it, so a partial file still works, and a file that is not there ends the program. Default: the four place search, described in <<controls,Customizing the Controls>>.
| `--linearscale=BOOLEAN` | Smooth the overlay as the window scales it (`true`) or take the nearest pixel (`false`, which keeps pixel art crisp). Takes `true`, `false`, `yes`, `no`, `on`, `off`, `1` or `0`. A script changes it while running with `setOverlayLinearScale`. Default: `true`, which is what every Singe release has drawn; Hypseus's own default is the opposite.
| `--manymouse` | Tell up to four mice apart whatever the game asks for, which a two gun cabinet needs from a game that never calls `mouseSetMode(MOUSE_MANY)` itself. The user's choice outranks the game's: `mouseSetMode(MOUSE_SINGLE)` no longer switches it off. Default: off, and the game decides.
| `--mapjoysticks=BOOLEAN` | Write a gamepad mapping for a device SDL does not recognise, so an arcade encoder, a spinner, a wheel or a flight stick is opened as `GAMEPAD_0` to `GAMEPAD_3` like anything else. `false` leaves such a device unopened, as earlier releases did. See <<devices,Every Device Is a Gamepad>>. Default: `true`.
| `--monochrome` | Start with the disc picture in grey. The overlay, the GUIs and the 3D scene keep their colour; a script does the same with `vldpSetMonochrome`, and undoes it. Default: off.
| `--nogamepad` | Ignore every gamepad, the counterpart of `--nomouse`. None is opened and no pad event reaches the game, so a stuck arcade encoder cannot press anything. Default: off.
| `--no_update_check` | Never ask the service whether a newer Singe exists. The menu otherwise looks once a day and shows what it found under the game's details; the Engine Update tool still works by hand. Default: off, so the daily look happens.
| `--refresh` | Set the `Singe` support folder aside as `old-Singe-<version>`, named for the version that wrote it, and write a fresh one. For upgrading: a file an older release shipped and this one does not is left in the folder that was set aside rather than sitting in the live one. Nothing is deleted, and a second run sets its own aside as `old-Singe-<version>-1`. The Engine Update tool passes this to the Singe it restarts into.
| `--screen=N` | Open the window on display `N`, counting from `1` as Hypseus counts them. A number larger than the number of displays lists the displays that are there and exits. Default: the primary display.
| `--soundfont=FILE` | The SoundFont (`.sf2`) MIDI files are synthesised with. Singe ships none, because a good one is tens of megabytes for a format almost no game uses. Without this option it looks for `Singe/soundfont.sf2` -- so a packed game can carry its own -- and then in the places a distribution installs one. The `SoundFont:` line of the trace header says which was used, or that none was found. See xref:Reference.adoc#midi[MIDI]. Default: the search.
| `--startsilent` | Start muted and stay muted until the first input of any kind, then play at the configured volumes. For an attract cabinet in a quiet room. `--nosound` outranks it. Default: off.
| `--trigger_threshold=PERCENT` | How far an analogue trigger must travel before it counts as a button, as a per cent of full travel -- Hypseus's unit, whose own default is `99.5`. `0` leaves the triggers on `DEAD_ZONE`, which is what Singe has always given them. Wins over `TRIGGER_THRESHOLD` in `controls.cfg`; `DEAD_ZONES` wins over both. `0` to `100`; see <<deadzones,Dead Zones and Triggers>>. Default: `0`.
| `--xratio=FACTOR` | The horizontal scale a light gun game reads with `ratioGetX()` to stretch its own gun coordinates for a display whose shape does not match the video. Singe does nothing with it; the game does the arithmetic, exactly as in Hypseus. `0` to `100`, kept to two decimals as Hypseus keeps it. Default: `0`, which every game that reads it treats as `1`.
| `--yratio=FACTOR` | The same for `ratioGetY()`. Default: `0`.
|===
Options may be given as `-x 640`, `-x640`, or `--xresolution=640`. Numeric
values are validated; a bad value prints the usage text and exits. The options
at the end of the table have no short letter; the alphabet ran out, and a letter
picked at random would help nobody.
Anything here except the options that name the game, name a directory, or must
act before a file can be found may also be set in a settings file, so a cabinet
does not need a launcher script for every game. See <<settings,The Settings
File>>.
[[apiversion]]
==== The Version Line
`--apiversion` prints one line and exits with status `0`:
----
singe version=3.00 protocol=1
----
This is a contract for front ends and will not change shape:
* The line ends with a newline and is the only thing written to standard
output. Nothing else is printed, whatever else is on the command line.
* The first field is always `singe`.
* Every field after it is `key=value`, separated by one space, with no spaces
inside a field.
* `version` is the release, the same number the banner shows.
* `protocol` is the version of this line's format. It becomes `2` only if the
rules above ever have to change.
* Later releases may add fields. A reader must ignore keys it does not know
rather than treating them as an error.
[[videorect]]
=== The Video Rectangle
Everything a game shows -- the disc, the overlay, the GUIs, the 3D scene and
the particles -- is drawn into one rectangle, and a handful of options decide
where that rectangle is. They apply in this order:
. *The window, or the bezel's cutout.* The rectangle starts as the whole
window. Singe works in the video's own coordinates and lets the window
letterbox them (`--stretch` fills instead), so nothing here depends on the
window's size. When `--bezel` loaded artwork that declares a cutout, the
rectangle starts as that hole instead and everything below places the picture
inside it; see <<bezels,Bezels>>.
. *`--scalefactor`.* The rectangle shrinks to that percentage of what it
started as, about its own centre.
. *`--shiftx` and `--shifty`.* The rectangle then moves inside the room the
scale left, `-100` hard against one side and `100` hard against the other,
which is what Hypseus's own `-shiftx` and `-shifty` mean. Two consequences
follow from that and are worth knowing: the picture can never be pushed off
the screen, and *at `--scalefactor=100` there is no room and a shift does
nothing at all*. To move the picture on a badly mounted panel, reduce the
scale first: `--scalefactor=90 --shiftx=-100` puts the shrunken picture hard
against the left.
. *`--sindengun`.* The border is applied last, to whatever the shift and the
scale left. The black ring takes the rectangle's edge, the white ring sits
inside it, and the game goes inside both, so a border is still a border
however the picture was placed. A scale small enough to leave no room for the
game squeezes the two rings rather than swallowing it. With bezel artwork
loaded the border goes to the window's edge instead and leaves the picture the
size it already was; `--sindenedge` decides it either way.
`--rotate` is not part of that order. The frame is drawn unrotated and the
finished picture is turned clockwise as a whole, so a shift moves the picture
along the video's own axes and turns with it. A quarter turn also swaps the
shape of the presented area, so a portrait window is filled rather than
letterboxed into a strip.
Mouse and light gun positions travel the same path backwards: the rotation
comes off first, and the position then maps into the rectangle, so a click
lands where the player sees it under any combination of these options.
`vldpGetScale`, `vldpSetScale`, `vldpGetRotate` and `vldpSetRotate` let a
script move the scale and the rotation while the game runs; the shift is a
display setting and has no script call. None of them is a `games.dat` key:
like `--scalefactor`, they describe the player's screen, not the game.
[[bezels]]
=== Bezels
A bezel is the artwork around an arcade cabinet's screen. Singe draws one from a
single image file, with the game's picture in a hole cut out of it, and can show
an arcade scoreboard in the space that leaves. Three options control it, and
they follow Hypseus's own convention so a cabinet's command line moves across
unchanged.
`--bezel cabinet.png` names the artwork. It is looked for in this order:
. `bezels/cabinet.png` in the game directory -- inside a packed `.game`, its own
copy, so a game can carry its bezel with it;
. `bezels/cabinet.png` in the data directory, where a user's own artwork can
live without touching a read-only game directory.
`--bezeldir art/cabinets` replaces the `bezels` folder with the one it names,
relative to the game directory or absolute, and then only that folder is
searched. Any format SDL_image reads will do (`.png` is the sensible choice,
because transparency matters). Artwork the option names and Singe cannot find
ends the program rather than running without it.
The artwork is drawn over the whole window, whatever shape either of them is, so
it stretches with the window exactly as the picture does. Give it the aspect
ratio you expect to play at.
==== The Cutout
Beside the image, and named after it, a sidecar file says where the picture
goes: `bezels/cabinet.png` is described by `bezels/cabinet.cfg`. It is a Lua
file, as `controls.cfg` is, and sets one table in the artwork's *own pixels*:
[source,lua]
----
-- The hole in cabinet.png the picture is drawn in, in the artwork's own pixels.
CUTOUT = { x = 200, y = 120, width = 800, height = 500 }
----
All four fields are required and the rectangle must lie inside the artwork; a
sidecar that will not run, or that names a rectangle outside the image, ends the
program with a message. Because the numbers are the artwork's own, they stay
right at any window size: the image and the hole are scaled together.
With a cutout, the picture is drawn in that hole -- stretched to it, so the hole
decides the shape of the picture -- and `--scalefactor`, `--shiftx` and
`--shifty` then work *inside* it, shrinking and nudging the picture in the hole.
With no sidecar the picture keeps the whole window, exactly as it does with no
bezel, and those three options place it as they always have. See
<<videorect,The Video Rectangle>>.
The hole itself should be transparent in the image. Singe draws the artwork
behind the picture by default, so an opaque middle would simply be covered, but
`--bezelflip` puts the artwork in front and then only its transparency lets the
game through.
==== Draw Order
Without `--bezelflip` the artwork is the first thing in the frame: the picture,
the overlay, the GUIs, the 3D scene and the particles all draw over it. With
`--bezelflip` the artwork draws in front of the picture instead, which is the
order Hypseus uses for its own bezels, so a frame with soft or shaped edges
overlaps the picture properly.
A script decides what happens to the overlay in that case:
xref:Reference.adoc#setoverlayontop[`setOverlayOnTop(true)`] draws the overlay and the GUIs above
the artwork, and `false` (the default, and Hypseus's) leaves the artwork on top
of them. With the artwork behind the picture the setting changes nothing,
because everything is already above it.
xref:Reference.adoc#mainbezelloaded[`mainBezelLoaded()`] tells a script whether artwork is loaded
and gives it the artwork's identifier, the same number Hypseus reports, so a
game written for a particular bezel can recognise it.
Screen space GUIs and the score panel always draw above the artwork, since the
point of them is to sit on it.
==== The Sinden Border
A Sinden light gun watches the screen for a bright border. With bezel artwork
loaded that border belongs at the *edge of the window*, not around the picture,
because the gun's camera sees the whole screen, so that is where `--sindengun`
puts it as soon as a bezel is loaded. There it is drawn as two rings, after
everything else, so the artwork between the border and the picture stays visible
and nothing paints over the white ring. With no bezel the border goes around the
picture, as it always has, and shrinks it.
`--sindenedge video` or `--sindenedge window` overrides that choice in either
direction: a gun cabinet whose artwork is only a thin frame may still want the
border around the picture, and a cabinet with no artwork at all may want it at
the screen's edge.
==== The Score Panel
Sixteen of the Hypseus Singe games can drive an arcade scoreboard: credits, and
a score and a life count for each of two players. In Singe that scoreboard is
the RmlUi document `Singe/scoreBezel.rml`, styled by `Singe/scoreBezel.rcss`,
both written beside the other support files and both replaceable -- a theme or a
game may restyle the panel by editing them, exactly as it may restyle
`Singe/gui.rcss`. The shipped style is a seven segment look: every readout shows
its digits dimly lit behind the live value, the way a real display does.
The panel is *off* until a game asks for it with
xref:Reference.adoc#scorebezelenable[`scoreBezelEnable(true)`], which is how Hypseus ships as
well, so a game that never asks behaves exactly as before and draws its own
scoreboard with sprites. xref:Reference.adoc#scorebezelgetstate[`scoreBezelGetState()`] is the
question every such game asks first.
Where it goes is decided for you: the panel takes the deeper of the bands the
picture leaves above and below it -- the room a bezel's cutout usually leaves --
centred on the picture, and lies along the bottom of the picture when neither
band is deep enough to hold it. It is drawn as a screen space GUI, so a game may
put its own GUIs on the artwork beside it with
xref:Reference.adoc#guidrawscreen[`guiDrawScreen`].
Hypseus's `scoreBezelEnable` takes a second argument choosing among its five
scoreboard backends. Singe has one, so the argument is accepted and ignored.
== Frequently Asked Questions
*Why is it named Singe?*
Singe is the name of the dragon in Dragon's Lair. As Singe (the program) began
as an add-on to Daphne (the princess in Dragon's Lair) the Dragon's Lair theme
was kept.
*What is the difference between Daphne, Singe, Hypseus, and Singe 2.xx?*
Daphne is an actual laserdisc game emulator that can run a handful of classic
laser games. Daphne/Singe (Singe 1.xx) is the original version of Singe that
was an add-on for the Daphne emulator -- originally shipped as a game DLL for
Daphne, then later combined into a single binary with the Daphne emulation
features removed. Hypseus-Singe is an enhanced fork of Daphne with Singe 1.xx
and some Singe 2.xx support. Finally, Singe 2.xx is an all-new, built-from-
scratch upgrade of the original Singe. In short, if you want accuracy, use
Daphne or Hypseus if they support emulating the desired game. If it is a Singe
game, use Singe 2.xx, or try Hypseus.
*Why should we not call it an "emulator"?*
Emulators use the real software or ROMs from the original game and pretend to
be the machine they were originally intended to run on. From the game's
perspective, it is business as usual. Singe, on the other hand, requires that
every game be re-implemented (ported) to run on Singe. The end result may be
very similar, but how it works is entirely different.
*Can I run Singe 1.xx games on Singe 2.xx?*
Yes, probably. But you likely will not want to. Almost everything from Singe
1.xx has been converted and enhanced for Singe 2.xx.
*Why does the game I just installed not show in the menu?*
Whoever packaged the game failed to include a proper `games.dat` file.
*Why is HD video slow?*
Due to the way Singe accesses video files to provide frame seeking, it is
unable to offload video decoding to the video card. High definition video
requires a lot of CPU.
*Why does my audio stutter on Windows?*
Users have discovered that most Singe audio and stuttering problems are related
to their installed audio driver -- especially Realtek-based devices. Try
updating your sound drivers or switching to the generic Windows HD Audio
driver. Disable surround sound -- use stereo, not 5.1 or 7.1. If your drivers
do not allow you to use stereo, you can use Virtual Audio Cable to fix this:
. Install VB-CABLE (https://vb-audio.com/Cable/).
. Go to Windows sound settings (right-click sound on the taskbar, then
*Sounds*, then *Playback*).
. Right-click the *Cable Input* device and choose *Set as default device*.
(You will lose your current sound output temporarily.)
. Go to the *Recording* tab.
. Right-click *Cable Output*, then *Properties*, then *Listen*, and finally
*Enable Listen to This Device*. In the drop-down list, choose your actual
listening device.
. You should have sound again. Load up Singe and your audio will be working
and lag-free.
*Does 3D work on a Raspberry Pi?*
On a Raspberry Pi 4 or later, yes. They have both a Vulkan driver and OpenGL
ES 3.1, and Singe uses whichever it finds. The Pi 3 has only OpenGL ES 2.0,
which cannot express the scene's skinning or morph targets, so it runs 2D games
(2D physics included) exactly as before and refuses the first 3D call. The Pi 4
is the minimum for any game that uses the 3D Scenes chapter. The same applies to
the cheap handhelds: a Mali-G31 or G610 has the ES 3.1 the scene needs, whether
or not it has Vulkan.
*Why does my audio stutter on the Raspberry Pi?*
The Raspberry Pi OS now uses Pipewire as the default audio backend. Switching
to PulseAudio seems to fix the issue:
. Run `sudo raspi-config` from a terminal window.
. Select *Advanced Options*.
. Select *Audio Config*.
. Select *PulseAudio*.
. Press kbd:[Tab], select *Ok*, and let it reboot.
== Making Games
This manual is about running Singe: installing it, installing games, setting up
a cabinet, and the menu. Making games is covered in three more books, which
are an optional download from the same place as Singe rather than part of it,
since together they are larger than the engine.
* The *Singe Reference* covers everything a game can do: the programming
model, the game directory, packing a game into one file, and every function
the engine offers, with its arguments, its return values, and an example.
* *Learn to Program with Singe* teaches programming from nothing, in thirty
lessons, to somebody who has never written code. It comes with the scripts
and the small art kit the lessons use.
* *Forge* is the authoring tool that makes a game from a description rather
than from code. Its book has ten tutorials and the vocabulary reference.
The download holds the three books as PDF and HTML, the lesson files, and the
licence. Forge itself is a separate download, since it is a Singe game in its
own right.