1016 lines
26 KiB
C++
1016 lines
26 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "glk/adrift/adrift.h"
|
|
#include "glk/adrift/scprotos.h"
|
|
#include "glk/adrift/scgamest.h"
|
|
#include "common/file.h"
|
|
#include "common/system.h"
|
|
#include "common/savefile.h"
|
|
|
|
namespace Glk {
|
|
namespace Adrift {
|
|
|
|
/* Assorted definitions and constants. */
|
|
static const sc_char NEWLINE = '\n';
|
|
static const sc_char CARRIAGE_RETURN = '\r';
|
|
static const sc_char NUL = '\0';
|
|
|
|
/* Global tracing flags. */
|
|
static sc_uint if_trace_flags = 0;
|
|
|
|
|
|
/*
|
|
* if_initialize()
|
|
*
|
|
* First-time runtime checks for the overall interpreter. This function
|
|
* tries to ensure correct compile options.
|
|
*/
|
|
static void if_initialize(void) {
|
|
static sc_bool initialized = FALSE;
|
|
|
|
/* Only do checks on the first call. */
|
|
if (!initialized) {
|
|
/* Make a few quick checks on types and type sizes. */
|
|
if (sizeof(sc_byte) != 1 || sizeof(sc_char) != 1) {
|
|
sc_error("if_initialize: sizeof sc_byte or sc_char"
|
|
" is not 1, check compile options\n");
|
|
} else if (sizeof(sc_uint) < 4 || sizeof(sc_int) < 4) {
|
|
sc_error("if_initialize: sizeof sc_uint or sc_int"
|
|
" is not at least 4, check compile options\n");
|
|
} else if (sizeof(sc_uint) > 8 || sizeof(sc_int) > 8) {
|
|
sc_error("if_initialize: sizeof sc_uint or sc_int"
|
|
" is more than 8, check compile options\n");
|
|
} else if (!((sc_uint) - 1 > 0)) {
|
|
sc_error("if_initialize: sc_uint appears not to be unsigned,"
|
|
" check compile options\n");
|
|
}
|
|
|
|
initialized = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* if_bool()
|
|
* sc_set_trace_flags()
|
|
* if_get_trace_flag()
|
|
*
|
|
* Set and retrieve tracing flags. Setting new values propagates the new
|
|
* tracing setting to all modules that support it.
|
|
*/
|
|
static sc_bool if_bool(sc_uint flag) {
|
|
return flag ? TRUE : FALSE;
|
|
}
|
|
|
|
void sc_set_trace_flags(sc_uint trace_flags) {
|
|
if_initialize();
|
|
|
|
/* Save the value for queries. */
|
|
if_trace_flags = trace_flags;
|
|
|
|
/* Propagate tracing to modules that support it. */
|
|
parse_debug_trace(if_bool(trace_flags & SC_TRACE_PARSE));
|
|
prop_debug_trace(if_bool(trace_flags & SC_TRACE_PROPERTIES));
|
|
var_debug_trace(if_bool(trace_flags & SC_TRACE_VARIABLES));
|
|
uip_debug_trace(if_bool(trace_flags & SC_TRACE_PARSER));
|
|
lib_debug_trace(if_bool(trace_flags & SC_TRACE_LIBRARY));
|
|
evt_debug_trace(if_bool(trace_flags & SC_TRACE_EVENTS));
|
|
npc_debug_trace(if_bool(trace_flags & SC_TRACE_NPCS));
|
|
obj_debug_trace(if_bool(trace_flags & SC_TRACE_OBJECTS));
|
|
task_debug_trace(if_bool(trace_flags & SC_TRACE_TASKS));
|
|
restr_debug_trace(if_bool(trace_flags & SC_TRACE_TASKS));
|
|
pf_debug_trace(if_bool(trace_flags & SC_TRACE_PRINTFILTER));
|
|
}
|
|
|
|
sc_bool if_get_trace_flag(sc_uint bitmask) {
|
|
return if_bool(if_trace_flags & bitmask);
|
|
}
|
|
|
|
|
|
/*
|
|
* if_print_string_common()
|
|
* if_print_string()
|
|
* if_print_debug()
|
|
* if_print_character_common()
|
|
* if_print_character()
|
|
* if_print_debug_character()
|
|
* if_print_tag()
|
|
*
|
|
* Call OS-specific print function for the given arguments.
|
|
*/
|
|
static void if_print_string_common(const sc_char *string, void (*print_string_function)(const sc_char *)) {
|
|
assert(string);
|
|
|
|
if (string[0] != NUL)
|
|
print_string_function(string);
|
|
}
|
|
|
|
void if_print_string(const sc_char *string) {
|
|
if_print_string_common(string, os_print_string);
|
|
}
|
|
|
|
void if_print_debug(const sc_char *string) {
|
|
if_print_string_common(string, os_print_string_debug);
|
|
}
|
|
|
|
static void if_print_character_common(sc_char character, void (*print_string_function)(const sc_char *)) {
|
|
if (character != NUL) {
|
|
sc_char buffer[2];
|
|
|
|
buffer[0] = character;
|
|
buffer[1] = NUL;
|
|
print_string_function(buffer);
|
|
}
|
|
}
|
|
|
|
void if_print_character(sc_char character) {
|
|
if_print_character_common(character, os_print_string);
|
|
}
|
|
|
|
void if_print_debug_character(sc_char character) {
|
|
if_print_character_common(character, os_print_string_debug);
|
|
}
|
|
|
|
void if_print_tag(sc_int tag, const sc_char *arg) {
|
|
assert(arg);
|
|
|
|
os_print_tag(tag, arg);
|
|
}
|
|
|
|
|
|
/*
|
|
* if_read_line_common()
|
|
* if_read_line()
|
|
* if_read_debug()
|
|
*
|
|
* Call OS-specific line read function. Clean up any read data a little
|
|
* before returning it to the caller.
|
|
*/
|
|
static void if_read_line_common(sc_char *buffer, sc_int length,
|
|
sc_bool(*read_line_function)(sc_char *, sc_int)) {
|
|
sc_bool is_line_available;
|
|
sc_int last;
|
|
assert(buffer && length > 0);
|
|
|
|
/* Loop until valid player input is available. */
|
|
do {
|
|
/* Space first with a blank line, and clear the buffer. */
|
|
if_print_character('\n');
|
|
memset(buffer, NUL, length);
|
|
|
|
is_line_available = read_line_function(buffer, length);
|
|
if (g_vm->shouldQuit())
|
|
return;
|
|
} while (!is_line_available);
|
|
|
|
/* Drop any trailing newline/return. */
|
|
last = strlen(buffer) - 1;
|
|
while (last >= 0
|
|
&& (buffer[last] == CARRIAGE_RETURN || buffer[last] == NEWLINE))
|
|
buffer[last--] = NUL;
|
|
}
|
|
|
|
void if_read_line(sc_char *buffer, sc_int length) {
|
|
if_read_line_common(buffer, length, os_read_line);
|
|
}
|
|
|
|
void if_read_debug(sc_char *buffer, sc_int length) {
|
|
if_read_line_common(buffer, length, os_read_line_debug);
|
|
}
|
|
|
|
|
|
/*
|
|
* if_confirm()
|
|
*
|
|
* Call OS-specific confirm function.
|
|
*/
|
|
sc_bool if_confirm(sc_int type) {
|
|
return os_confirm(type);
|
|
}
|
|
|
|
|
|
/*
|
|
* if_open_saved_game()
|
|
* if_write_saved_game()
|
|
* if_read_saved_game()
|
|
* if_close_saved_game()
|
|
*
|
|
* Call OS-specific functions for saving and restoring games.
|
|
*/
|
|
void *if_open_saved_game(sc_bool is_save) {
|
|
return os_open_file(is_save);
|
|
}
|
|
|
|
void if_write_saved_game(void *opaque, const sc_byte *buffer, sc_int length) {
|
|
assert(buffer);
|
|
|
|
os_write_file(opaque, buffer, length);
|
|
}
|
|
|
|
sc_int if_read_saved_game(void *opaque, sc_byte *buffer, sc_int length) {
|
|
assert(buffer);
|
|
|
|
return os_read_file(opaque, buffer, length);
|
|
}
|
|
|
|
void if_close_saved_game(void *opaque) {
|
|
os_close_file(opaque);
|
|
}
|
|
|
|
|
|
/*
|
|
* if_display_hints()
|
|
*
|
|
* Call OS-specific hint display function.
|
|
*/
|
|
void if_display_hints(sc_gameref_t game) {
|
|
assert(gs_is_game_valid(game));
|
|
|
|
os_display_hints((sc_game) game);
|
|
}
|
|
|
|
|
|
/*
|
|
* if_update_sound()
|
|
* if_update_graphic()
|
|
*
|
|
* Call OS-specific sound and graphic handler functions.
|
|
*/
|
|
void if_update_sound(const sc_char *filename, sc_int sound_offset, sc_int sound_length,
|
|
sc_bool is_looping) {
|
|
if (strlen(filename) > 0)
|
|
os_play_sound(filename, sound_offset, sound_length, is_looping);
|
|
else
|
|
os_stop_sound();
|
|
}
|
|
|
|
void if_update_graphic(const sc_char *filename,
|
|
sc_int graphic_offset, sc_int graphic_length) {
|
|
os_show_graphic(filename, graphic_offset, graphic_length);
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_scare_version()
|
|
* sc_scare_emulation()
|
|
*
|
|
* Return a version string and Adrift emulation level.
|
|
*/
|
|
const sc_char *sc_scare_version(void) {
|
|
if_initialize();
|
|
return "SCARE " SCARE_VERSION SCARE_PATCH_LEVEL;
|
|
}
|
|
|
|
sc_int sc_scare_emulation(void) {
|
|
if_initialize();
|
|
return SCARE_EMULATION;
|
|
}
|
|
|
|
|
|
/*
|
|
* if_file_read_callback()
|
|
* if_file_write_callback()
|
|
*
|
|
* Standard FILE* reader and writer callback for constructing callback-style
|
|
* calls from filename and stream variants.
|
|
*/
|
|
static sc_int if_file_read_callback(void *opaque, sc_byte *buffer, sc_int length) {
|
|
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)opaque;
|
|
sc_int bytes;
|
|
|
|
bytes = stream->read(buffer, length);
|
|
if (stream->err())
|
|
sc_error("if_file_read_callback: warning: read error\n");
|
|
|
|
return bytes;
|
|
}
|
|
|
|
static void if_file_write_callback(void *opaque, const sc_byte *buffer, sc_int length) {
|
|
Common::WriteStream *stream = (Common::WriteStream *) opaque;
|
|
|
|
stream->write(buffer, length);
|
|
if (stream->err())
|
|
sc_error("if_file_write_callback: warning: write error\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_game_from_filename()
|
|
* sc_game_from_stream()
|
|
* sc_game_from_callback()
|
|
*
|
|
* Called by the OS-specific layer to create a run context. The _filename()
|
|
* and _stream() variants are adapters for run_create().
|
|
*/
|
|
sc_game sc_game_from_filename(const sc_char *filename) {
|
|
Common::File *stream;
|
|
sc_game game;
|
|
|
|
if_initialize();
|
|
if (!filename) {
|
|
sc_error("sc_game_from_filename: nullptr filename\n");
|
|
return nullptr;
|
|
}
|
|
|
|
stream = new Common::File();
|
|
if (!stream->open(filename)) {
|
|
delete stream;
|
|
sc_error("sc_game_from_filename: fopen error\n");
|
|
return nullptr;
|
|
}
|
|
|
|
game = run_create(if_file_read_callback, stream);
|
|
delete stream;
|
|
|
|
return game;
|
|
}
|
|
|
|
sc_game sc_game_from_stream(Common::SeekableReadStream *stream) {
|
|
if_initialize();
|
|
if (!stream) {
|
|
sc_error("sc_game_from_stream: nullptr stream\n");
|
|
return nullptr;
|
|
}
|
|
|
|
return run_create(if_file_read_callback, stream);
|
|
}
|
|
|
|
sc_game sc_game_from_callback(sc_int(*callback)(void *, sc_byte *, sc_int), void *opaque) {
|
|
if_initialize();
|
|
if (!callback) {
|
|
sc_error("sc_game_from_callback: nullptr callback\n");
|
|
return nullptr;
|
|
}
|
|
|
|
return run_create(callback, opaque);
|
|
}
|
|
|
|
|
|
/*
|
|
* if_game_error()
|
|
*
|
|
* Common function to verify that the game passed in to functions below
|
|
* is a valid game. Returns TRUE on game error, FALSE if okay.
|
|
*/
|
|
static sc_bool if_game_error(const sc_gameref_t game, const sc_char *function_name) {
|
|
/* Check for invalid game -- null pointer or bad magic. */
|
|
if (!gs_is_game_valid(game)) {
|
|
if (game)
|
|
sc_error("%s: invalid game\n", function_name);
|
|
else
|
|
sc_error("%s: nullptr game\n", function_name);
|
|
return TRUE;
|
|
}
|
|
|
|
/* No game error. */
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_interpret_game()
|
|
* sc_restart_game()
|
|
* sc_save_game()
|
|
* sc_load_game()
|
|
* sc_undo_game_turn()
|
|
* sc_quit_game()
|
|
*
|
|
* Called by the OS-specific layer to run a game loaded into a run context,
|
|
* and to quit the interpreter on demand, if required. sc_quit_game()
|
|
* is implemented as a longjmp(), so never returns to the caller --
|
|
* instead, the program behaves as if sc_interpret_game() had returned.
|
|
* sc_load_game() will longjmp() if the restore is successful (thus
|
|
* behaving like sc_restart_game()), but will return if the game could not
|
|
* be restored. sc_undo_game_turn() behaves like sc_load_game().
|
|
*/
|
|
void sc_interpret_game(CONTEXT, sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_interpret_game"))
|
|
return;
|
|
|
|
run_interpret(context, game_);
|
|
}
|
|
|
|
void sc_restart_game(CONTEXT, sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_restart_game"))
|
|
return;
|
|
|
|
CALL1(run_restart, game_);
|
|
}
|
|
|
|
sc_bool sc_save_game(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_save_game"))
|
|
return FALSE;
|
|
|
|
return run_save_prompted(game_);
|
|
}
|
|
|
|
sc_bool sc_load_game(CONTEXT, sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_load_game"))
|
|
return FALSE;
|
|
|
|
return run_restore_prompted(context, game_);
|
|
}
|
|
|
|
sc_bool sc_undo_game_turn(CONTEXT, sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_undo_game_turn"))
|
|
return FALSE;
|
|
|
|
return run_undo(context, game_);
|
|
}
|
|
|
|
void sc_quit_game(CONTEXT, sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_quit_game"))
|
|
return;
|
|
|
|
run_quit(context, game_);
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_save_game_to_filename()
|
|
* sc_save_game_to_stream()
|
|
* sc_save_game_to_callback()
|
|
* sc_load_game_from_filename()
|
|
* sc_load_game_from_stream()
|
|
* sc_load_game_from_callback()
|
|
*
|
|
* Low level game saving and loading functions. The normal sc_save_game()
|
|
* and sc_load_game() functions act exactly as the "save" and "restore"
|
|
* game commands, in that they prompt the user for a stream to write or read.
|
|
* These alternative forms allow the caller to directly specify the data
|
|
* streams.
|
|
*/
|
|
sc_bool sc_save_game_to_filename(sc_game game, const sc_char *filename) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
Common::OutSaveFile *sf;
|
|
|
|
if (if_game_error(game_, "sc_save_game_to_filename"))
|
|
return FALSE;
|
|
|
|
if (!filename) {
|
|
sc_error("sc_save_game_to_filename: nullptr filename\n");
|
|
return FALSE;
|
|
}
|
|
|
|
sf = g_system->getSavefileManager()->openForSaving(filename);
|
|
if (!sf) {
|
|
sc_error("sc_save_game_to_filename: fopen error\n");
|
|
return FALSE;
|
|
}
|
|
|
|
run_save(game_, if_file_write_callback, sf);
|
|
sf->finalize();
|
|
delete sf;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void sc_save_game_to_stream(sc_game game, Common::SeekableReadStream *stream) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_save_game_to_stream"))
|
|
return;
|
|
|
|
if (!stream) {
|
|
sc_error("sc_save_game_to_stream: nullptr stream\n");
|
|
return;
|
|
}
|
|
|
|
run_save(game_, if_file_write_callback, stream);
|
|
}
|
|
|
|
void sc_save_game_to_callback(sc_game game,
|
|
void (*callback)(void *, const sc_byte *, sc_int), void *opaque) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_save_game_to_callback"))
|
|
return;
|
|
|
|
if (!callback) {
|
|
sc_error("sc_save_game_to_callback: nullptr callback\n");
|
|
return;
|
|
}
|
|
|
|
run_save(game_, callback, opaque);
|
|
}
|
|
|
|
sc_bool sc_load_game_from_filename(CONTEXT, sc_game game, const sc_char *filename) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
Common::InSaveFile *sf;
|
|
sc_bool status;
|
|
|
|
if (if_game_error(game_, "sc_load_game_from_filename"))
|
|
return FALSE;
|
|
|
|
if (!filename) {
|
|
sc_error("sc_load_game_from_filename: nullptr filename\n");
|
|
return FALSE;
|
|
}
|
|
|
|
sf = g_system->getSavefileManager()->openForLoading(filename);
|
|
if (!sf) {
|
|
sc_error("sc_load_game_from_filename: fopen error\n");
|
|
return FALSE;
|
|
}
|
|
|
|
status = run_restore(context, game_, if_file_read_callback, sf);
|
|
delete sf;
|
|
|
|
return status;
|
|
}
|
|
|
|
sc_bool sc_load_game_from_stream(CONTEXT, sc_game game, Common::SeekableReadStream *stream) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_load_game_from_stream"))
|
|
return FALSE;
|
|
|
|
if (!stream) {
|
|
sc_error("sc_load_game_from_stream: nullptr stream\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return run_restore(context, game_, if_file_read_callback, stream);
|
|
}
|
|
|
|
sc_bool sc_load_game_from_callback(CONTEXT, sc_game game,
|
|
sc_int(*callback)(void *, sc_byte *, sc_int), void *opaque) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_load_game_from_callback"))
|
|
return FALSE;
|
|
|
|
if (!callback) {
|
|
sc_error("sc_load_game_from_callback: nullptr callback\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return run_restore(context, game_, callback, opaque);
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_free_game()
|
|
*
|
|
* Called by the OS-specific layer to free run context memory.
|
|
*/
|
|
void sc_free_game(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_free_game"))
|
|
return;
|
|
|
|
run_destroy(game_);
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_is_game_running()
|
|
* sc_get_game_name()
|
|
* sc_get_game_author()
|
|
* sc_get_game_compile_date()
|
|
* sc_get_game_turns()
|
|
* sc_get_game_score()
|
|
* sc_get_game_max_score()
|
|
* sc_get_game_room ()
|
|
* sc_get_game_status_line ()
|
|
* sc_get_game_preferred_font ()
|
|
* sc_get_game_bold_room_names()
|
|
* sc_get_game_verbose()
|
|
* sc_get_game_notify_score_change()
|
|
* sc_has_game_completed()
|
|
* sc_is_game_undo_available()
|
|
*
|
|
* Return a few attributes of a game.
|
|
*/
|
|
sc_bool sc_is_game_running(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_is_game_running"))
|
|
return FALSE;
|
|
|
|
return run_is_running(game_);
|
|
}
|
|
|
|
const sc_char *sc_get_game_name(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_char *retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_name"))
|
|
return "[invalid game]";
|
|
|
|
run_get_attributes(game_, &retval,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
const sc_char *
|
|
sc_get_game_author(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_char *retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_author"))
|
|
return "[invalid game]";
|
|
|
|
run_get_attributes(game_, nullptr, &retval,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr);
|
|
return retval;
|
|
}
|
|
|
|
const sc_char *sc_get_game_compile_date(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_char *retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_compile_date"))
|
|
return "[invalid game]";
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, &retval,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
sc_int sc_get_game_turns(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
sc_int retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_turns"))
|
|
return 0;
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, &retval,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
sc_int sc_get_game_score(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
sc_int retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_score"))
|
|
return 0;
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, &retval,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
sc_int sc_get_game_max_score(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
sc_int retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_max_score"))
|
|
return 0;
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, &retval,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
const sc_char *sc_get_game_room(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_char *retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_room"))
|
|
return "[invalid game]";
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &retval,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
const sc_char *sc_get_game_status_line(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_char *retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_status_line"))
|
|
return "[invalid game]";
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
&retval, nullptr, nullptr, nullptr, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
const sc_char *sc_get_game_preferred_font(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_char *retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_preferred_font"))
|
|
return "[invalid game]";
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, &retval, nullptr, nullptr, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
sc_bool sc_get_game_bold_room_names(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
sc_bool retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_bold_room_names"))
|
|
return FALSE;
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, &retval, nullptr, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
sc_bool sc_get_game_verbose(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
sc_bool retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_verbose"))
|
|
return FALSE;
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, &retval, nullptr);
|
|
return retval;
|
|
}
|
|
|
|
sc_bool sc_get_game_notify_score_change(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
sc_bool retval;
|
|
|
|
if (if_game_error(game_, "sc_get_game_notify_score_change"))
|
|
return FALSE;
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, &retval);
|
|
return retval;
|
|
}
|
|
|
|
sc_bool sc_has_game_completed(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_has_game_completed"))
|
|
return FALSE;
|
|
|
|
return run_has_completed(game_);
|
|
}
|
|
|
|
sc_bool sc_is_game_undo_available(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_is_game_undo_available"))
|
|
return FALSE;
|
|
|
|
return run_is_undo_available(game_);
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_set_game_bold_room_names()
|
|
* sc_set_game_verbose()
|
|
* sc_set_game_notify_score_change()
|
|
*
|
|
* Set a few attributes of a game.
|
|
*/
|
|
void sc_set_game_bold_room_names(sc_game game, sc_bool flag) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
sc_bool bold, verbose, notify;
|
|
|
|
if (if_game_error(game_, "sc_set_game_bold_room_names"))
|
|
return;
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, &bold, &verbose, ¬ify);
|
|
run_set_attributes(game_, flag, verbose, notify);
|
|
}
|
|
|
|
void sc_set_game_verbose(sc_game game, sc_bool flag) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
sc_bool bold, verbose, notify;
|
|
|
|
if (if_game_error(game_, "sc_set_game_verbose"))
|
|
return;
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, &bold, &verbose, ¬ify);
|
|
run_set_attributes(game_, bold, flag, notify);
|
|
}
|
|
|
|
void sc_set_game_notify_score_change(sc_game game, sc_bool flag) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
sc_bool bold, verbose, notify;
|
|
|
|
if (if_game_error(game_, "sc_set_game_notify_score_change"))
|
|
return;
|
|
|
|
run_get_attributes(game_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, &bold, &verbose, ¬ify);
|
|
run_set_attributes(game_, bold, verbose, flag);
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_does_game_use_sounds()
|
|
* sc_does_game_use_graphics()
|
|
*
|
|
* Indicate the game's use of resources.
|
|
*/
|
|
sc_bool sc_does_game_use_sounds(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_does_game_use_sounds"))
|
|
return FALSE;
|
|
|
|
return res_has_sound(game_);
|
|
}
|
|
|
|
sc_bool sc_does_game_use_graphics(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_does_game_use_graphics"))
|
|
return FALSE;
|
|
|
|
return res_has_graphics(game_);
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_get_first_game_hint()
|
|
* sc_get_next_game_hint()
|
|
* sc_get_game_hint_question()
|
|
* sc_get_game_subtle_hint()
|
|
* sc_get_game_sledgehammer_hint()
|
|
*
|
|
* Iterate currently available hints, and return strings for a hint.
|
|
*/
|
|
sc_game_hint sc_get_first_game_hint(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_get_first_game_hint"))
|
|
return nullptr;
|
|
|
|
return run_hint_iterate(game_, nullptr);
|
|
}
|
|
|
|
sc_game_hint sc_get_next_game_hint(sc_game game, sc_game_hint hint) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_hintref_t hint_ = (sc_hintref_t)hint;
|
|
|
|
if (if_game_error(game_, "sc_get_next_game_hint"))
|
|
return nullptr;
|
|
if (!hint_) {
|
|
sc_error("sc_get_next_game_hint: nullptr hint\n");
|
|
return nullptr;
|
|
}
|
|
|
|
return run_hint_iterate(game_, hint_);
|
|
}
|
|
|
|
const sc_char *sc_get_game_hint_question(sc_game game, sc_game_hint hint) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_hintref_t hint_ = (sc_hintref_t)hint;
|
|
|
|
if (if_game_error(game_, "sc_get_game_hint_question"))
|
|
return nullptr;
|
|
if (!hint_) {
|
|
sc_error("sc_get_game_hint_question: nullptr hint\n");
|
|
return nullptr;
|
|
}
|
|
|
|
return run_get_hint_question(game_, hint_);
|
|
}
|
|
|
|
const sc_char *sc_get_game_subtle_hint(sc_game game, sc_game_hint hint) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_hintref_t hint_ = (sc_hintref_t)hint;
|
|
|
|
if (if_game_error(game_, "sc_get_game_subtle_hint"))
|
|
return nullptr;
|
|
if (!hint_) {
|
|
sc_error("sc_get_game_subtle_hint: nullptr hint\n");
|
|
return nullptr;
|
|
}
|
|
|
|
return run_get_subtle_hint(game_, hint_);
|
|
}
|
|
|
|
const sc_char *sc_get_game_unsubtle_hint(sc_game game, sc_game_hint hint) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
const sc_hintref_t hint_ = (sc_hintref_t)hint;
|
|
|
|
if (if_game_error(game_, "sc_get_game_unsubtle_hint"))
|
|
return nullptr;
|
|
if (!hint_) {
|
|
sc_error("sc_get_game_unsubtle_hint: nullptr hint\n");
|
|
return nullptr;
|
|
}
|
|
|
|
return run_get_unsubtle_hint(game_, hint_);
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_set_game_debugger_enabled()
|
|
* sc_is_game_debugger_enabled()
|
|
* sc_run_game_debugger_command()
|
|
*
|
|
* Enable, disable, and query game debugging, and run a single debug command.
|
|
*/
|
|
void sc_set_game_debugger_enabled(sc_game game, sc_bool flag) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_set_game_debugger_enabled"))
|
|
return;
|
|
|
|
debug_set_enabled(game_, flag);
|
|
}
|
|
|
|
sc_bool sc_get_game_debugger_enabled(sc_game game) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_get_game_debugger_enabled"))
|
|
return FALSE;
|
|
|
|
return debug_get_enabled(game_);
|
|
}
|
|
|
|
sc_bool sc_run_game_debugger_command(sc_game game, const sc_char *debug_command) {
|
|
const sc_gameref_t game_ = (sc_gameref_t)game;
|
|
|
|
if (if_game_error(game_, "sc_run_game_debugger_command"))
|
|
return FALSE;
|
|
|
|
return debug_run_command(game_, debug_command);
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_set_locale()
|
|
* sc_get_locale()
|
|
*
|
|
* Set the interpreter locale, and get the currently set locale.
|
|
*/
|
|
sc_bool sc_set_locale(const sc_char *name) {
|
|
if (!name) {
|
|
sc_error("sc_set_locale: nullptr name\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return loc_set_locale(name);
|
|
}
|
|
|
|
const sc_char *sc_get_locale(void) {
|
|
return loc_get_locale();
|
|
}
|
|
|
|
|
|
/*
|
|
* sc_set_portable_random()
|
|
* sc_reseed_random_sequence()
|
|
*
|
|
* Turn portable random number generation on and off, and supply a new seed
|
|
* for random number generators.
|
|
*/
|
|
void sc_set_portable_random(sc_bool flag) {
|
|
if (flag)
|
|
sc_set_congruential_random();
|
|
else
|
|
sc_set_platform_random();
|
|
}
|
|
|
|
void sc_reseed_random_sequence(sc_uint new_seed) {
|
|
if (new_seed == 0) {
|
|
sc_error("sc_reseed_random_sequence: new_seed may not be 0\n");
|
|
return;
|
|
}
|
|
|
|
sc_seed_random(new_seed);
|
|
}
|
|
|
|
} // End of namespace Adrift
|
|
} // End of namespace Glk
|