978 lines
61 KiB
Text
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.
|