More vfs fixes.

This commit is contained in:
Scott Duensing 2026-09-04 19:33:02 -05:00
parent 141b388b92
commit 0455657ed5
6 changed files with 24 additions and 8 deletions

6
.gitignore vendored
View file

@ -16,3 +16,9 @@ docs/Manual.html
docs/Manual.pdf docs/Manual.pdf
docs/.asciidoctor/ docs/.asciidoctor/
.claude/settings.local.json .claude/settings.local.json
# Extracted by the binary when it is run from here; never source.
/Singe/
/Menu.sh
/Menu.bat
/data/

View file

@ -10,6 +10,9 @@ API Changes
- The name now expands to "SINGE Is Not a Game Emulator", in the banner, - The name now expands to "SINGE Is Not a Game Emulator", in the banner,
the manual, INSTALL, and the Windows file description. the manual, INSTALL, and the Windows file description.
- Framework.singe applies the legacy sprite argument order once even when
a game loads it more than once.
- lfs.dir, lfs.attributes and lfs.symlinkattributes see inside a packed - lfs.dir, lfs.attributes and lfs.symlinkattributes see inside a packed
game (the union of its loose directory, its data overlay and the game (the union of its loose directory, its data overlay and the
database), and lfs.mkdir and lfs.rmdir act on its data overlay, so database), and lfs.mkdir and lfs.rmdir act on its data overlay, so

View file

@ -422,7 +422,9 @@ end
-- Singe 3.00 moved the sprite handle to the first argument. Games written for -- Singe 3.00 moved the sprite handle to the first argument. Games written for
-- 2.10 can set SINGE_LEGACY_SPRITE_ARGS = true (or LEGACY_SPRITE_ARGS = true in -- 2.10 can set SINGE_LEGACY_SPRITE_ARGS = true (or LEGACY_SPRITE_ARGS = true in
-- games.dat) to keep calling the old way. -- games.dat) to keep calling the old way.
if SINGE_LEGACY_SPRITE_ARGS and spriteDraw ~= nil then if SINGE_LEGACY_SPRITE_ARGS and spriteDraw ~= nil and not SINGE_LEGACY_SPRITE_WRAPPED then
-- Once only: a game may load this file more than once, and a second wrap would undo the first.
SINGE_LEGACY_SPRITE_WRAPPED = true
local newSpriteDraw = spriteDraw local newSpriteDraw = spriteDraw
spriteDraw = function(...) spriteDraw = function(...)
local args = { ... } local args = { ... }

View file

@ -102,10 +102,10 @@ options available to be used in this file, read through
=== Command Line Options === Command Line Options
---- ----
Singe [OPTIONS] scriptName{.singe} Singe [OPTIONS] gameName
---- ----
The script name is the only required argument. It may be a `.singe` file, a 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 directory containing a script of the same name (`ActionMax` finds
`ActionMax/ActionMax.singe`), or a packed game (`DLe.game` runs the first `ActionMax/ActionMax.singe`), or a packed game (`DLe.game` runs the first
entry of the `games.dat` inside it, `--entry=N` another). For a laserdisc game (`--disc`) with no entry of the `games.dat` inside it, `--entry=N` another). For a laserdisc game (`--disc`) with no

View file

@ -527,7 +527,7 @@ static ConfigT *_parseArguments(const char *exeName, int32_t argc, char *argv[])
// Non-option: the script file. // Non-option: the script file.
case 0: case 0:
if (conf->scriptFile) { if (conf->scriptFile) {
_showUsage(exeName, "Only one script file may be specified."); _showUsage(exeName, "Only one game may be specified.");
} }
conf->scriptFile = strdup(arg); conf->scriptFile = strdup(arg);
break; break;
@ -819,7 +819,7 @@ static void _resolveFiles(const char *exeName, ConfigT *conf) {
conf->scriptFile = temp; conf->scriptFile = temp;
} }
if (!conf->scriptFile) { if (!conf->scriptFile) {
_showUsage(exeName, "Unable to locate script."); _showUsage(exeName, "Unable to locate the game.");
} }
// Do we need to generate a video name? // Do we need to generate a video name?
@ -918,7 +918,8 @@ static void _showUsage(const char *name, const char *message) {
_showHeader(); _showHeader();
utilSay("Usage: %s [OPTIONS] scriptName{.singe}", utilGetLastPathComponent(name)); utilSay("Usage: %s [OPTIONS] gameName", utilGetLastPathComponent(name));
utilSay(" gameName: a .singe script, its directory, or a packed .game file");
utilNewline(); utilNewline();
for (x = 0; x < (int32_t)OPTION_COUNT; x++) { for (x = 0; x < (int32_t)OPTION_COUNT; x++) {
if (_options[x].hidden) { if (_options[x].hidden) {
@ -1236,7 +1237,7 @@ int main(int argc, char *argv[]) {
// Nothing to run? Installing was the whole job. // Nothing to run? Installing was the whole job.
if (!conf->scriptFile) { if (!conf->scriptFile) {
_showUsage(exeName, "No script file specified."); _showUsage(exeName, "No game specified.");
} }
// A game database on its own runs its first games.dat entry; a patch is not a game. // A game database on its own runs its first games.dat entry; a patch is not a game.

View file

@ -336,7 +336,7 @@ static void _listDirectory(const char *path, char ***list, int32_t *count) {
} }
// Forward slashes, no leading "./", no doubled slashes. Case is untouched. // Forward slashes, no leading "./", no doubled or trailing slashes. Case is untouched.
static char *_normalise(const char *name) { static char *_normalise(const char *name) {
char *out = strdup(name); char *out = strdup(name);
size_t i = 0; size_t i = 0;
@ -354,6 +354,10 @@ static char *_normalise(const char *name) {
} }
out[o++] = c; out[o++] = c;
} }
// A trailing separator names the same thing ("Overlay/" is "Overlay"), except for the root.
while ((o > 1) && (out[o - 1] == '/')) {
o--;
}
out[o] = 0; out[o] = 0;
return out; return out;