Game patches supported.
This commit is contained in:
parent
84a2c25183
commit
6859990bec
7 changed files with 194 additions and 68 deletions
16
CHANGELOG
16
CHANGELOG
|
|
@ -49,14 +49,14 @@ API Changes
|
|||
LEGACY_SPRITE_ARGS = true to their games.dat entry. See "Migrating from
|
||||
Singe 2.10" in the manual.
|
||||
|
||||
- Singe is no longer laserdisc only. A game declares a disc with
|
||||
DISC = true in games.dat (or --disc / --framefile on the command line);
|
||||
otherwise it runs without one, on a canvas sized by CANVAS_X/CANVAS_Y or
|
||||
--canvas=WxH (default 720x480), the disc functions become harmless, and
|
||||
discGetState() returns the new DISC_EJECTED.
|
||||
Every existing games.dat entry needs DISC = true added; the menu skips
|
||||
an entry with VIDEO but no DISC = true and says why, and the engine
|
||||
refuses one if launched directly. Scripts can test SINGE_DISC.
|
||||
- Singe is no longer laserdisc only. A games.dat entry with a VIDEO is a
|
||||
laserdisc game; one with the line missing or blank runs without a
|
||||
disc, on a canvas sized by CANVAS_X/CANVAS_Y (default 720x480), the
|
||||
disc functions become harmless, and discGetState() returns the new
|
||||
DISC_EJECTED. On the command line --disc (implied by --framefile)
|
||||
declares the disc, since a video found beside the script no longer
|
||||
does; --canvas=WxH sizes the canvas. Existing games.dat files need no
|
||||
change. Scripts can test SINGE_DISC.
|
||||
|
||||
- mouseSetEnabled(bool) and singeSetPauseKeyEnabled(bool) added. The old
|
||||
mouseEnable/mouseDisable and singeEnablePauseKey/singeDisablePauseKey are
|
||||
|
|
|
|||
|
|
@ -419,14 +419,6 @@ local function loadGamesDat(source, container)
|
|||
return
|
||||
end
|
||||
for _, value in pairs(GAMES or {}) do
|
||||
-- Since 3.00 a laserdisc game must say DISC = true; refuse the ambiguous cases here
|
||||
-- with a message rather than letting the engine stop the menu when it is picked.
|
||||
local title = tostring(value.TITLE or value.SCRIPT or "?")
|
||||
if value.VIDEO and not value.DISC then
|
||||
debugPrint(source .. ": \"" .. title .. "\" names a VIDEO without DISC = true; add DISC = true to the entry. Skipped.")
|
||||
elseif value.DISC and not value.VIDEO then
|
||||
debugPrint(source .. ": \"" .. title .. "\" says DISC = true but has no VIDEO. Skipped.")
|
||||
else
|
||||
if container then
|
||||
-- Packed game: the engine opens its files through the database, and so does the menu.
|
||||
value.CONTAINER = container
|
||||
|
|
@ -439,7 +431,6 @@ local function loadGamesDat(source, container)
|
|||
table.insert(GAME_LIST, value)
|
||||
GAME_COUNT = GAME_COUNT + 1
|
||||
end
|
||||
end
|
||||
GAMES = {}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -353,13 +353,20 @@ rule ambiguous), or whose scripts and data files reach outside the game
|
|||
with `..`; it reports the file and line. Two files whose names differ only
|
||||
by case cannot both be packed. Stale `.index` files are skipped. A game
|
||||
that references another game's directory (a shared framework beside it)
|
||||
must copy that directory inside first.
|
||||
must copy that directory inside first. Before the database is committed,
|
||||
its `games.dat` is checked: every entry needs a `SCRIPT`, and each
|
||||
`SCRIPT`, `VIDEO`, `CABINET`, `MARQUEE`, and `ATTRACT` it names must be a
|
||||
file in the database (or under `Singe/`); a bad path is reported with the
|
||||
entry's title and the pack is refused. `--patch` runs the same check on the
|
||||
patched game, so a patch cannot leave a dangling path behind either.
|
||||
|
||||
`--patch` takes either a directory laid out like the game, whose files
|
||||
replace the packed ones, or a database made by `--pack` from such a
|
||||
directory. Either way the change is one transaction: the game is intact if
|
||||
it is interrupted. A patch database may also carry a `removed(path)` table
|
||||
naming files to delete.
|
||||
replace the packed ones, or a `.patch` file made by `--pack` from such a
|
||||
directory (`Singe --pack fixes DLe-1.1.patch`; a patch needs no
|
||||
`games.dat`). Files the patch does not name are left alone. Either way the
|
||||
change is one transaction: the game is intact if it is interrupted. A patch
|
||||
may also carry a `removed(path)` table naming files to delete. A `.patch`
|
||||
can be unpacked like a game, but it cannot be run and the menu ignores it.
|
||||
|
||||
A packed game keeps its data directory exactly where the loose game would
|
||||
have it (`data/DLe/` for `DLe/DLe.singe`), so saves and settings are shared
|
||||
|
|
@ -615,7 +622,6 @@ GAMES = {
|
|||
{
|
||||
TITLE = ".38 Ambush Alley",
|
||||
SCRIPT = "ActionMax/38AmbushAlley.singe",
|
||||
DISC = true,
|
||||
VIDEO = "ActionMax/frame_38AmbushAlley.txt",
|
||||
DATA = "ActionMax",
|
||||
STRETCH = false,
|
||||
|
|
@ -641,14 +647,13 @@ GAMES = {
|
|||
}
|
||||
----
|
||||
|
||||
The keys `SCRIPT`, `DISC`, `VIDEO`, `CANVAS_X`, `CANVAS_Y`, `STRETCH`,
|
||||
The keys `SCRIPT`, `VIDEO`, `CANVAS_X`, `CANVAS_Y`, `STRETCH`,
|
||||
`NO_MOUSE`, `RESOLUTION_X`, `RESOLUTION_Y`, `SINDEN_GUN`, `AUDIO_TRACK`,
|
||||
`AUDIO_DELAY`, and `LEGACY_SPRITE_ARGS` are read by the engine when the menu (or your own
|
||||
script, through `scriptExecute` / `scriptPush`) launches the entry; they
|
||||
override the command line. A laserdisc game must say `DISC = true` and name
|
||||
its `VIDEO`; an entry with a `VIDEO` but no `DISC = true` is refused with a
|
||||
message, as is `DISC = true` without a `VIDEO`. A game without a disc leaves
|
||||
both out and may set `CANVAS_X` / `CANVAS_Y` (default 720x480); see
|
||||
override the command line. The `VIDEO` line is the disc: an entry that names
|
||||
one is a laserdisc game, and an entry that leaves it out or blank runs
|
||||
without a disc and may set `CANVAS_X` / `CANVAS_Y` (default 720x480); see
|
||||
<<withoutadisc,Games Without a Disc>>. `LEGACY_SPRITE_ARGS = true` runs a game written for Singe 2.10
|
||||
with the old sprite argument order (see <<migrating,Migrating from Singe
|
||||
2.10>>). The remaining keys are read by the menu for display.
|
||||
|
|
@ -657,7 +662,7 @@ with the old sprite argument order (see <<migrating,Migrating from Singe
|
|||
=== Games Without a Disc
|
||||
|
||||
Singe no longer assumes a laserdisc. A game is a laserdisc game only when it
|
||||
says so: `DISC = true` in its `games.dat` entry, or `--disc` (or a
|
||||
names one: a `VIDEO` in its `games.dat` entry, or `--disc` (or a
|
||||
`--framefile`) on the command line. Everything else runs without a disc, and
|
||||
the world is a *canvas* instead of a video frame: `CANVAS_X` / `CANVAS_Y` in
|
||||
`games.dat` or `--canvas=WxH` on the command line, 720x480 by default so a
|
||||
|
|
@ -715,11 +720,11 @@ changed shape, but a few behaviors did:
|
|||
* `mouseEnable` / `mouseDisable` and `singeEnablePauseKey` /
|
||||
`singeDisablePauseKey` still work, as aliases of `mouseSetEnabled` and
|
||||
`singeSetPauseKeyEnabled`.
|
||||
* A laserdisc game must now declare itself: add `DISC = true` next to
|
||||
`VIDEO` in every `games.dat` entry, and pass `--disc` (or `--framefile`)
|
||||
on the command line. Without it the game runs without a disc, and a
|
||||
`games.dat` entry that names a `VIDEO` without `DISC = true` is refused
|
||||
with a message naming the fix. See <<withoutadisc,Games Without a Disc>>.
|
||||
* A laserdisc is no longer assumed. A `games.dat` entry with a `VIDEO` is a
|
||||
laserdisc game, as before; one without runs on a canvas instead. On the
|
||||
command line pass `--disc` (or `--framefile`), since a video found beside
|
||||
the script no longer implies a disc. See <<withoutadisc,Games Without a
|
||||
Disc>>.
|
||||
* The pause key now pauses the whole game, not just the media: the script
|
||||
is frozen until the key is pressed again, and `SWITCH_PAUSE` is delivered
|
||||
to the script only when the key has been disabled. It acts on the key
|
||||
|
|
|
|||
11
src/main.c
11
src/main.c
|
|
@ -127,12 +127,12 @@ static const OptionT _options[] = {
|
|||
{ 'm', "nomouse", ap_no, NULL, "disable mouse", false },
|
||||
{ 'n', "nocrosshair", ap_no, NULL, "request game not display gun crosshairs", false },
|
||||
{ 'o', "audio", ap_yes, "TRACK", "select default track for audio output", false },
|
||||
{ 'P', "pack", ap_yes, "DIRECTORY", "pack a game DIRECTORY into the database named after the options", false },
|
||||
{ 'P', "pack", ap_yes, "DIRECTORY", "pack a game DIRECTORY into the .game (or changed files into the .patch) named after the options", false },
|
||||
{ 'p', "program", ap_no, NULL, "trace Singe execution to screen and file", false },
|
||||
{ 's', "nosound", ap_no, NULL, "mutes all sound", false },
|
||||
{ 'T', "patch", ap_yes, "DATABASE", "patch a game DATABASE from the directory or patch database named after the options", false },
|
||||
{ 'T', "patch", ap_yes, "DATABASE", "patch a game DATABASE from the directory or .patch named after the options", false },
|
||||
{ 't', "trace", ap_no, NULL, "trace script execution to screen and file", false },
|
||||
{ 'U', "unpack", ap_yes, "DATABASE", "unpack a game DATABASE into the directory named after the options", false },
|
||||
{ 'U', "unpack", ap_yes, "DATABASE", "unpack a game or patch DATABASE into the directory named after the options", false },
|
||||
{ 'u', "stretch", ap_no, NULL, "use ugly stretched video", false },
|
||||
{ 'v', "framefile", ap_yes, "FILENAME", "use an alternate video file", false },
|
||||
{ 'w', "fullscreen_window", ap_no, NULL, "run in windowed full screen mode", false },
|
||||
|
|
@ -1203,7 +1203,10 @@ int main(int argc, char *argv[]) {
|
|||
_showUsage(exeName, "No script file specified.");
|
||||
}
|
||||
|
||||
// A game database on its own runs its first games.dat entry.
|
||||
// A game database on its own runs its first games.dat entry; a patch is not a game.
|
||||
if (!vfsIsDatabase(conf->scriptFile) && packIsDatabase(conf->scriptFile)) {
|
||||
_showUsage(exeName, "That is a patch database. Apply it with --patch; it cannot be run.");
|
||||
}
|
||||
if (vfsIsDatabase(conf->scriptFile)) {
|
||||
replacement = confFromDatabase(conf);
|
||||
destroyConf(&conf);
|
||||
|
|
|
|||
148
src/pack.c
148
src/pack.c
|
|
@ -12,13 +12,20 @@
|
|||
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "pack.h"
|
||||
#include "singe.h"
|
||||
#include "util.h"
|
||||
#include "vfs.h"
|
||||
|
||||
|
||||
#define ENGINE_PREFIX "singe/"
|
||||
#define GAMES_DAT "games.dat"
|
||||
#define PAGE_SIZE 4096
|
||||
#define PATCH_EXTENSION "patch"
|
||||
|
||||
|
||||
typedef struct EntryS {
|
||||
|
|
@ -52,8 +59,10 @@ static void _entriesFree(EntryT *list);
|
|||
static bool _exec(sqlite3 *db, const char *sql);
|
||||
static bool _insertFile(WriterT *writer, const EntryT *entry);
|
||||
static bool _isDirectory(const char *path);
|
||||
static bool _isPatchName(const char *database);
|
||||
static char *_keyFor(const char *relative);
|
||||
static bool _scanEscapes(const EntryT *list);
|
||||
static bool _validateGamesDat(WriterT *writer);
|
||||
static bool _writeMeta(sqlite3 *db, const char *key, const char *value);
|
||||
static bool _writerClose(WriterT *writer, bool commit);
|
||||
static bool _writerOpen(WriterT *writer, const char *database, bool create);
|
||||
|
|
@ -103,7 +112,8 @@ static bool _applyDatabase(WriterT *writer, const char *source) {
|
|||
}
|
||||
|
||||
|
||||
// Inserts every file below directory. checkGame enforces the rules a whole game must meet.
|
||||
// Inserts every file below directory. checkGame enforces the rules a whole game must meet; the
|
||||
// forbidden-file list and the escape scan apply to patches too.
|
||||
static bool _applyDirectory(WriterT *writer, const char *directory, bool checkGame) {
|
||||
EntryT *list = NULL;
|
||||
EntryT *entry = NULL;
|
||||
|
|
@ -134,7 +144,7 @@ static bool _applyDirectory(WriterT *writer, const char *directory, bool checkGa
|
|||
}
|
||||
}
|
||||
}
|
||||
if (ok && checkGame && !_scanEscapes(list)) {
|
||||
if (ok && !_scanEscapes(list)) {
|
||||
ok = false;
|
||||
}
|
||||
for (entry = list; ok && (entry != NULL); entry = entry->next) {
|
||||
|
|
@ -296,6 +306,12 @@ static bool _isDirectory(const char *path) {
|
|||
}
|
||||
|
||||
|
||||
// Patch databases are .patch files: packed like a game but without a games.dat, and never run.
|
||||
static bool _isPatchName(const char *database) {
|
||||
return utilStricmp(utilGetFileExtension(database), PATCH_EXTENSION) == 0;
|
||||
}
|
||||
|
||||
|
||||
// The lookup key: lower case, forward slashes.
|
||||
static char *_keyFor(const char *relative) {
|
||||
char *key = strdup(relative);
|
||||
|
|
@ -352,6 +368,101 @@ static bool _scanEscapes(const EntryT *list) {
|
|||
}
|
||||
|
||||
|
||||
// Every file a games.dat entry names must be in the database (or belong to the engine), checked
|
||||
// before the transaction commits so a game or patch never ships with a dangling path.
|
||||
static bool _validateGamesDat(WriterT *writer) {
|
||||
static const char *const keys[] = { "SCRIPT", "VIDEO", "CABINET", "MARQUEE", "ATTRACT", NULL };
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
lua_State *L = NULL;
|
||||
char *gameDir = NULL;
|
||||
char *key = NULL;
|
||||
const char *value = NULL;
|
||||
const char *title = NULL;
|
||||
const char *inner = NULL;
|
||||
size_t length = 0;
|
||||
int32_t x = 0;
|
||||
bool ok = true;
|
||||
|
||||
// The file itself, and the own-directory prefix its paths may carry.
|
||||
if ((sqlite3_prepare_v2(writer->db, "SELECT data FROM assets WHERE path = ?", -1, &stmt, NULL) != SQLITE_OK)) {
|
||||
return false;
|
||||
}
|
||||
sqlite3_bind_text(stmt, 1, GAMES_DAT, -1, SQLITE_STATIC);
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW) {
|
||||
sqlite3_finalize(stmt);
|
||||
utilSay("!!! No %s at the root of the game.", GAMES_DAT);
|
||||
return false;
|
||||
}
|
||||
L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
if ((luaL_loadbuffer(L, sqlite3_column_blob(stmt, 0), (size_t)sqlite3_column_bytes(stmt, 0), GAMES_DAT) != LUA_OK) || (lua_pcall(L, 0, 0, 0) != LUA_OK)) {
|
||||
utilSay("!!! %s", lua_tostring(L, -1));
|
||||
sqlite3_finalize(stmt);
|
||||
lua_close(L);
|
||||
return false;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
if (sqlite3_prepare_v2(writer->db, "SELECT value FROM meta WHERE key = 'gamedir'", -1, &stmt, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
gameDir = _keyFor((const char *)sqlite3_column_text(stmt, 0));
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
if (sqlite3_prepare_v2(writer->db, "SELECT count(*) FROM assets WHERE path = ?", -1, &stmt, NULL) != SQLITE_OK) {
|
||||
free(gameDir);
|
||||
lua_close(L);
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_getglobal(L, "GAMES");
|
||||
if (!lua_istable(L, -1)) {
|
||||
utilSay("!!! %s defines no GAMES table.", GAMES_DAT);
|
||||
ok = false;
|
||||
}
|
||||
lua_pushnil(L);
|
||||
while (ok && lua_next(L, -2)) {
|
||||
if (lua_istable(L, -1)) {
|
||||
lua_getfield(L, -1, "TITLE");
|
||||
title = lua_tostring(L, -1) ? lua_tostring(L, -1) : "(untitled)";
|
||||
for (x = 0; keys[x] != NULL; x++) {
|
||||
lua_getfield(L, -2, keys[x]);
|
||||
value = lua_tostring(L, -1);
|
||||
if ((value != NULL) && (value[0] != 0)) {
|
||||
key = _keyFor(value);
|
||||
inner = key;
|
||||
if (gameDir != NULL) {
|
||||
length = strlen(gameDir);
|
||||
if ((strncmp(inner, gameDir, length) == 0) && (inner[length] == '/')) {
|
||||
inner += length + 1;
|
||||
}
|
||||
}
|
||||
if (strncmp(key, ENGINE_PREFIX, strlen(ENGINE_PREFIX)) != 0) {
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_bind_text(stmt, 1, inner, -1, SQLITE_TRANSIENT);
|
||||
if ((sqlite3_step(stmt) != SQLITE_ROW) || (sqlite3_column_int64(stmt, 0) == 0)) {
|
||||
utilSay("!!! %s: \"%s\" names %s = \"%s\", which is not in the game.", GAMES_DAT, title, keys[x], value);
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
free(key);
|
||||
} else if (strcmp(keys[x], "SCRIPT") == 0) {
|
||||
utilSay("!!! %s: \"%s\" has no SCRIPT.", GAMES_DAT, title);
|
||||
ok = false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
free(gameDir);
|
||||
lua_close(L);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
static bool _writeMeta(sqlite3 *db, const char *key, const char *value) {
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
bool ok = false;
|
||||
|
|
@ -467,13 +578,14 @@ char *packForbiddenReason(const char *path, bool isPatch) {
|
|||
}
|
||||
|
||||
|
||||
// Packs a game directory into a new database.
|
||||
// Packs a game directory into a new database, or a directory of changed files into a .patch.
|
||||
bool packGame(const char *directory, const char *database) {
|
||||
WriterT writer;
|
||||
char *root = strdup(directory);
|
||||
char *gamesDat = NULL;
|
||||
char *chunk = NULL;
|
||||
size_t length = strlen(root);
|
||||
bool isPatch = _isPatchName(database);
|
||||
bool ok = true;
|
||||
|
||||
while ((length > 1) && ((root[length - 1] == '/') || (root[length - 1] == '\\'))) {
|
||||
|
|
@ -485,7 +597,7 @@ bool packGame(const char *directory, const char *database) {
|
|||
return false;
|
||||
}
|
||||
gamesDat = utilCreateString("%s/games.dat", root);
|
||||
if (!utilFileExists(gamesDat)) {
|
||||
if (!isPatch && !utilFileExists(gamesDat)) {
|
||||
utilSay("!!! %s has no games.dat; a game database needs one at its root.", root);
|
||||
free(gamesDat);
|
||||
free(root);
|
||||
|
|
@ -502,7 +614,8 @@ bool packGame(const char *directory, const char *database) {
|
|||
ok = ok && _writeMeta(writer.db, "chunk", chunk);
|
||||
ok = ok && _writeMeta(writer.db, "packer", VERSION_STRING);
|
||||
free(chunk);
|
||||
ok = ok && _applyDirectory(&writer, root, true);
|
||||
ok = ok && _applyDirectory(&writer, root, !isPatch);
|
||||
ok = ok && (isPatch || _validateGamesDat(&writer));
|
||||
if (ok) {
|
||||
utilSay(">>> Packed %lld files (%lld bytes) from %s into %s", (long long)writer.files, (long long)writer.bytes, root, database);
|
||||
}
|
||||
|
|
@ -518,6 +631,26 @@ bool packGame(const char *directory, const char *database) {
|
|||
}
|
||||
|
||||
|
||||
// A database with our meta table, whatever its extension.
|
||||
bool packIsDatabase(const char *database) {
|
||||
sqlite3 *db = NULL;
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
bool found = false;
|
||||
|
||||
if (!utilFileExists(database) || (sqlite3_open_v2(database, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK)) {
|
||||
sqlite3_close(db);
|
||||
return false;
|
||||
}
|
||||
if (sqlite3_prepare_v2(db, "SELECT value FROM meta WHERE key = 'version'", -1, &stmt, NULL) == SQLITE_OK) {
|
||||
found = (sqlite3_step(stmt) == SQLITE_ROW);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
// Applies a patch, either a directory of replacement files or a patch database, in one transaction.
|
||||
bool packPatch(const char *database, const char *source) {
|
||||
WriterT writer;
|
||||
|
|
@ -534,6 +667,7 @@ bool packPatch(const char *database, const char *source) {
|
|||
utilSay("!!! %s does not exist.", source);
|
||||
ok = false;
|
||||
}
|
||||
ok = ok && _validateGamesDat(&writer);
|
||||
if (ok) {
|
||||
utilSay(">>> Patched %s with %lld files (%lld bytes) from %s", database, (long long)writer.files, (long long)writer.bytes, source);
|
||||
}
|
||||
|
|
@ -557,8 +691,8 @@ bool packUnpack(const char *database, const char *directory) {
|
|||
int64_t files = 0;
|
||||
bool ok = true;
|
||||
|
||||
if (!vfsIsDatabase(database)) {
|
||||
utilSay("!!! %s is not a Singe game database.", database);
|
||||
if (!packIsDatabase(database)) {
|
||||
utilSay("!!! %s is not a Singe game or patch database.", database);
|
||||
return false;
|
||||
}
|
||||
if (sqlite3_open_v2(database, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
char *packForbiddenReason(const char *path, bool isPatch);
|
||||
bool packGame(const char *directory, const char *database);
|
||||
bool packIsDatabase(const char *database);
|
||||
bool packPatch(const char *database, const char *source);
|
||||
bool packUnpack(const char *database, const char *directory);
|
||||
|
||||
|
|
|
|||
20
src/singe.c
20
src/singe.c
|
|
@ -744,7 +744,7 @@ static ConfigT *_buildConfFromTable(lua_State *L, const ConfigT *base) {
|
|||
int64_t valueNumber = 0;
|
||||
ConfigT *c = NULL;
|
||||
|
||||
// Start with the given config, but every entry declares its own disc and container.
|
||||
// Start with the given config, but every entry brings its own video and container.
|
||||
c = cloneConf(base);
|
||||
free(c->container);
|
||||
c->container = NULL;
|
||||
|
|
@ -825,8 +825,6 @@ static ConfigT *_buildConfFromTable(lua_State *L, const ConfigT *base) {
|
|||
c->audioDelayMs = (int32_t)valueNumber;
|
||||
} else if (strcmp(confKey, "LEGACY_SPRITE_ARGS") == 0) {
|
||||
c->legacySpriteArgs = valueBoolean;
|
||||
} else if (strcmp(confKey, "DISC") == 0) {
|
||||
c->disc = valueBoolean;
|
||||
} else if (strcmp(confKey, "CANVAS_X") == 0) {
|
||||
c->canvasWidth = (int32_t)valueNumber;
|
||||
} else if (strcmp(confKey, "CANVAS_Y") == 0) {
|
||||
|
|
@ -837,13 +835,12 @@ static ConfigT *_buildConfFromTable(lua_State *L, const ConfigT *base) {
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
// A laserdisc game says DISC = true and names its VIDEO; anything else is a mistake worth stopping for.
|
||||
if (c->disc && (c->videoFile == NULL)) {
|
||||
utilDie("%s: DISC = true but no VIDEO given.", c->scriptFile);
|
||||
}
|
||||
if (!c->disc && (c->videoFile != NULL)) {
|
||||
utilDie("%s: VIDEO given without DISC = true. Add DISC = true to the games.dat entry for a laserdisc game.", c->scriptFile);
|
||||
// The VIDEO line is the disc: present and not blank means a laserdisc game.
|
||||
if ((c->videoFile != NULL) && (c->videoFile[0] == 0)) {
|
||||
free(c->videoFile);
|
||||
c->videoFile = NULL;
|
||||
}
|
||||
c->disc = (c->videoFile != NULL);
|
||||
if ((c->canvasWidth <= 0) || (c->canvasHeight <= 0)) {
|
||||
utilDie("%s: CANVAS_X and CANVAS_Y must be positive.", c->scriptFile);
|
||||
}
|
||||
|
|
@ -4445,11 +4442,6 @@ ConfigT *confFromDatabase(const ConfigT *conf) {
|
|||
}
|
||||
lua_replace(L, 1);
|
||||
lua_settop(L, 1);
|
||||
// --disc on the command line stands in for a DISC = true the entry forgot.
|
||||
if (conf->disc) {
|
||||
lua_pushboolean(L, true);
|
||||
lua_setfield(L, 1, "DISC");
|
||||
}
|
||||
result = _buildConfFromTable(L, base);
|
||||
if (result->scriptFile == NULL) {
|
||||
utilDie("%s: the first games.dat entry has no SCRIPT.", base->container);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue