To port SCARE to a new input/output scheme, you need to create a set of input/output functions, specific to your platform for the core interpreter. Something like os_.c file is sufficient for simple cases. More complex cases probably call for a platform-specific subdirectory. There are two example interface modules in the SCARE source, os_ansi.c and os_glk.c. The first of these is pretty much the simplest interface that is possible for SCARE (though it could be slightly simpler if it gave up with splitting output lines on spaces). The second is a Glk interface for Linux. Together, these modules should provide a model for porting SCARE to other platforms. Note that the interpreter core is not thread-safe. Your port must implement the following functions void os_print_string (const sc_char *string); void os_print_tag (sc_int tag, const sc_char *arg); void os_print_string_debug (const sc_char *string); These functions print output to the display. The first provides a single text string to print; though often largely ASCII, this string may contain any Microsoft codepage 1252 character. New lines in output strings are always represented by the single ASCII character '\n'. The second indicates that the game output a text formatting tag. Adrift games have HTML-like markup in their text, and use it to create display effects. The following tag values, defined in scare.h, are ones that the interpreter could send: SC_TAG_ITALICS, SC_TAG_ENDITALICS SC_TAG_BOLD, SC_TAG_ENDBOLD, SC_TAG_UNDERLINE, SC_TAG_ENDUNDERLINE, SC_TAG_COLOR, SC_TAG_ENDCOLOR, These request italics, bold, underlined, and secondary color output, and cancel the output style. For all of these, expect arg to be "". SC_TAG_FONT, SC_TAG_ENDFONT, This requests text coloring and font control. The value of arg indicates what color or font size or face is being set. SC_TAG_BGCOLOR, This requests a given display background color. The value of arg indicates the color. SC_TAG_CENTER, SC_TAG_ENDCENTER, SC_TAG_RIGHT, SC_TAG_ENDRIGHT, These set and clear centering and right text justification. SC_TAG_WAIT, This requests a short delay. The delay time is given in the value of arg, in seconds with one decimal place (for example, "7.3"). SC_TAG_WAITKEY, This requests that the interpreter wait until the player presses a key. SC_TAG_CLS, Request to clear the screen. SC_TAG_UNKNOWN SCARE will output this if it has run into an HTML-like tag in an Adrift game that it doesn't recognize. In this case, the raw tag text will appear in arg, stripped of its <...>. Unknown tags are sometimes used deliberately by games; one example is differentiating rooms with otherwise identical names, "Forest<1>" and "Forest<2>". Here, the game relies on the Adrift Runner ignoring the "invalid" tag. Because of this, your interpreter should probably also just ignore unknown tags. The third output function, os_print_string_debug(), is called by the interpreter core when its game debugger is active. Your interpreter might want to use a separate window, or a distinctive text output style, for output from the game debugger. Otherwise, this function should just call, or behave as, os_print_string(). As with os_print_string(), new lines are represented by the single ASCII character '\n'. sc_bool os_read_line (sc_char *buffer, sc_int length); sc_bool os_read_line_debug (sc_char *buffer, sc_int length); Read a line of input from the user into buffer. The maximum allowable length to read is in length. There is no need to terminate buffer with any \n or \r. It does, of course, need to terminate with a NUL (\0). The function should return TRUE when data has been placed in the buffer, telling the interpreter core to use this data as player input. Returning FALSE will cause the interpreter core to discard any buffer contents, and reprompt for input. The debug version of os_read_line() is used by the interpreter core when the game debugger is active. This lets your interpreter use a separate window for debugging a game, if you wish. Otherwise, the function should call, or behave as, os_read_line(), with the possible exception that it's usually a good idea to change the input prompt to indicate that debug input is being requested. sc_bool os_confirm (sc_int type); Called by the interpreter core when it is about to do something which probably needs some form of confirmation. Confirmation types are SC_CONF_QUIT User entered "quit" SC_CONF_RESTART User entered "restart" SC_CONF_SAVE User entered "save" SC_CONF_RESTORE User entered "restore" or "load" SC_CONF_VIEW_HINTS User entered "hints" In practice, confirming save isn't useful since it doesn't destroy the state of the current running game. For the others, however, it's probably useful to get some form of "Are you sure?" confirmation from the user, though if your interpreter can successfully obscure hint strings until selected by the user, you might want to also unconditionally confirm hint display at this point. Return TRUE if confirmed, FALSE if not. In a GUI interpreter, this function would probably pop up a confirm dialog box. void *os_open_file (sc_bool is_save); Called by the core interpreter when it wants to open a file or other data stream for a saved game. The value of is_save is TRUE when opening for write, FALSE when opening for read. Return the data stream from the call; in the case of stdio, this would be the FILE*, for Glk, it's the strid_t. For other input/output schemes, return something that uniquely identifies the channel, so that you know what to read from or write to, and close, later on. If you cannot open the file successfully, or want to cancel the operation, return NULL. void os_write_file (void *opaque, const sc_byte *buffer, sc_int length); sc_int os_read_file (void *opaque, sc_byte *buffer, sc_int length); Write or read saved game data. The value of opaque is whatever your os_open_file() function returned. So for stdio, you could cast opaque to a FILE*, and use it directly with fread()/fwrite(), for example. Buffer is a pointer to data to write or read, and length is the count of bytes to write, or maximum number of bytes to read. os_read_file() should return the number of bytes actually placed into buffer, and zero on end of file. void os_close_file (void *opaque); Close the data stream or channel associated with opaque. The interpreter core calls this when it has finished writing or reading a game save file. void os_play_sound (sc_char *filepath, sc_int offset, sc_int length, sc_bool is_looping); void os_stop_sound (void); void os_show_graphic (sc_char *filepath, sc_int offset, sc_int length); Calls to these functions indicate that the game has requested either a sound to be played or a picture displayed. The values of the filename, offset, and length arguments indicate how to retrieve the resource. There are two ways a game can request a resource -- either as an external file, or from an offset and length in the main TAF game file. The clue to which of these it is using is in the values of the offset and length arguments. If the values of the offset and length arguments are both zero, the game has not built resources into the TAF file, and expects the interpreter to retrieve external file data. In this case, the path to the file is in the filename argument. It's often not terribly useful, relying on files being in the same absolute path on the system playing the game as they occupied on the game's development system, perhaps "C:\\BOB\BUBBLES.MID". Moreover, the file path is highly Windows-specific. In this case, SCARE can supply only the file's path, and your platform-specific interpreter needs to figure out how best to handle it. Sorry, that's Adrift for you. If the values of the offset and length arguments are not zero, the resource is built in to the TAF game file, and so can be fairly easily extracted and used. The offsets are file offsets, in bytes, into the TAF game file, and the lengths are the number of bytes to extract from that TAF game file to produce the resource file. The filepath argument still contains the path to the resource file on the game's development system, but it can (and almost definitely should) be ignored where the offset and length are passed to your interpreter. For sound resources, is_looping shows whether the sound loops, or is played only once. Stop looping sounds on a call to os_stop_sound(). For graphics, the SCARE core makes a special os_show_graphic() call on quitting or restarting a game. This call has a zero-length filepath string (that is, ""), and is an indication that your interface should clear graphics display if it can do so. If it cannot, this call may be ignored. The core interpreter tracks the currently playing sound or displayed graphic, and attempts to send game requests to the interface only when a resource changes; that is, on a change of sound or when sound is turned off, or on a changed of graphic. If you do not intend your interface to handle these calls, the functions should be stub do-nothing functions in the interpreter interface. For Linux users, a simple way to get graphics displayed is to use 'xv' to display it, once extracted. The os_ansi.c and os_glk.c interfaces contain a small and inelegant piece of code to do this; they use 'dd' to extract the graphic data from the TAF file into the file /tmp/scare.jpg, then create a background 'xv' job to display the image. A second background job cleans away the temporary file after a few seconds. To compile this into Linux builds of SCARE, define a value for the C pre- processor macro LINUX_GRAPHICS. void os_display_hints (sc_game game) Display the hints relevant to the current state of the game. See below for information on how to retrieve a list of game hints to display. A GUI interpreter might display these as a list widget; other interfaces will have their own methods of doing this. An interface might also not display hints at all, in which case this function can be a stub. The interpreter core offers these functions, which you call from your platform-specific module to run a game: void sc_set_trace_flags (sc_uint trace_flags); Set tracing in SCARE modules that support it. The argument is bit mask; see scare.h for a list of flag bits and their meanings. sc_game sc_game_from_filename (const sc_char *filename); sc_game sc_game_from_stream (FILE *stream); sc_game sc_game_from_callback (sc_int (*callback) (void *, sc_byte *, sc_int), void *opaque); These functions create a game object from various sources. The simplest to use is sc_game_from_filename(), where filename is the path to an Adrift .taf file. As an alternative, you can call sc_game_from_stream() to create a game from a stdio FILE* already open to the .taf file. Finally, there is sc_game_from_callback(), which is a stdio-less game creation function. You hand this function the name of a callback function, and an opaque pointer. When the interpreter core needs data from the .taf file to construct the game, it will call callback() with the opaque value passed in, and a pointer to a buffer and a maximum length. callback() should read in data using whatever input/output scheme it wants to use, and return the count of bytes buffered, or zero on end of file. Callbacks behave just like os_read_file() above. For an example, see os_glk.c, which steadfastly avoids using stdio for input/output, and relies purely on Glk. void sc_interpret_game (sc_game game); Call this function with a game created using one of the above functions to start running an Adrift game, or to resume one stopped with "quit". void sc_restart_game (sc_game game); Restart the game. The game may be either running or stopped. For cases where the game is running, this function would be called from the event handler for a GUI interpreter's "Restart" button or menu option. It is implemented as a longjump when used on running games, so the function does not return to the caller, in this case, if successful. For cases where the game is stopped, the function resets the game state to its initial conditions, then returns as normal. Use sc_is_game_running() to determine whether the game is running, and sc_has_game_completed() to determine if the game was lost or won, or if the user left the game using "quit". sc_bool sc_save_game (sc_game game); sc_bool sc_save_game_to_filename (sc_game game, const sc_char *filename); void sc_save_game_to_stream (sc_game game, FILE *stream); void sc_save_game_to_callback (sc_game game, void (*callback) (void *, const sc_byte *, sc_int), void *opaque); Save the state of the current running game. The game may be running or stopped. Functions returning a status return TRUE if the save was successful (the user indicated a valid save file), FALSE otherwise. These functions are designed to be called from the event handler of a GUI interpreter's "Save" button or menu option. The first function results in calls to os_open_file(), os_write_file(), and os_close_file() functions, acting exactly as the game "save" command. The others save the game state to either a named file, a file stream, or via callbacks. SCARE uses the same file save format as the official Adrift Runner (at least, as version 4.0.36 of the Runner), so games saved with SCARE should be restoreable by the Adrift Runner itself. sc_bool sc_load_game (sc_game game); sc_bool sc_load_game_from_filename (sc_game game, const sc_char *filename); sc_bool sc_load_game_from_stream (sc_game game, FILE *stream); sc_bool sc_load_game_from_callback (sc_game game, sc_int (*callback) (void *, sc_byte *, sc_int), void *opaque); Restore the state of a previously saved game. The game may be running or stopped. If successful, and the game is running, these functions do not return, since they are implemented as a longjump. If successful and the game is not running, they return TRUE. If restore fails, these functions return FALSE. These functions are designed to be called from the event handler of a GUI interpreter's "Restore" or "Load" button or menu option. The first function results in calls to os_open_file(), os_read_file(), and os_close_file() functions, acting exactly as the game "restore" command. The others load the game state from either a named file, a file stream, or via callbacks. Because the format is the same, SCARE can restore games saved by the Adrift Runner version 4.0.36. sc_bool sc_undo_game_turn (sc_game game); Undo one turn of a running game. The game may be running or stopped. If successful, the function returns TRUE. If undo fails, the function returns FALSE. This function is designed to be called from the event handler for a GUI interpreter's "Undo" button or menu option. SCARE maintains a limited number of undo buffers. The function returns FALSE when no undo buffer is available; that is, at the start of the game, or when repeated undo has used up all buffers. void sc_quit_game (sc_game game); Stop the currently running game. This function is implemented as a longjump, making it appear as if the call made to sc_interpret_game() returned. The game must be running. Because it contains a longjump, this function never returns to its caller. A game stopped this way, or one stopped by the player typing the "quit" command, can be restarted by calling sc_interpret_game() again, with the same game argument. void sc_free_game (sc_game game); After sc_interpret_game() has returned, call this function to free all memory resources in use by the game, and destroy the game object. The game must not be running. If the program is going to exit after running a game (that it, if it returns from main() or glk_main(), or calls exit() or glk_exit()), you may save some time by simply omitting the call to this function. While sc_free_game() will carefully tidy up heap and malloc'ed memory allocated to the game, the time spent doing this could be saved if the next action is to exit the program. sc_bool sc_is_game_running (sc_game game); Selected functions above require that the game is running, or stopped, before they can be called. This function tells you whether the game is being run, or not. A game starts running when sc_interpret_game() is called. It stops when sc_interpret_game() returns, either normally (game end, user entered "quit"), or when a platform-specific module calls sc_quit_game(). sc_bool sc_has_game_completed (sc_game game); If the game has been won or lost, it can't be restarted by another call to os_interpret_game(). The function sc_has_game_completed() can be used to find out if a game is runnable at all. It returns TRUE if the game has ended, FALSE otherwise. sc_bool sc_is_game_undo_available (sc_game game); The function sc_undo_game_turn() requires an undo buffer to be available. This function tells you whether undo is available, or not. It returns TRUE if the game can undo a turn, otherwise FALSE. The function can be called prior to calling sc_undo_game_turn(), and if it returns TRUE, then sc_undo_game_turn() will succeed. const sc_char *sc_get_game_name (sc_game game); const sc_char *sc_get_game_author (sc_game game); const sc_char *sc_get_game_compile_date (sc_game game); sc_int sc_get_game_turns (sc_game game); sc_int sc_get_game_score (sc_game game); sc_int sc_get_game_max_score (sc_game game); These functions return the name, author, compilation date, turns count, current score, and maximum score for the game. The game doesn't need to be running to get this information. const sc_char *sc_get_game_room (sc_game game); const sc_char *sc_get_game_status_line (sc_game game); These functions return the current room name, and any status line that the game outputs. If a game outputs no status line, the return value from sc_get_game_status_line() is always NULL. The return value of the function sc_get_game_room() may also be NULL if the game hasn't yet run any turns. These two values can be used, together with the score, to build an Adrift Runner-like status line. The Adrift Runner displays the current room name in a box in the bottom left of the display. If the game implements a status line, it displays this alongside it, otherwise it displays the score instead. const sc_char *sc_get_game_preferred_font (sc_game game); A game may request that it be displayed in the Adrift Runner using a particular default font. If your interface has fine grained control over font display, and you want to implement this feature, this function returns the preferred default font set by the game's author, and built into the game. The values of the string are the font name and size, separated by a comma, for example "Comic Sans MS, 14", or "Verdana, 12". If the game has no default font preference, this function returns NULL. void sc_set_game_bold_room_names (sc_game game, sc_bool flag); sc_bool sc_get_game_bold_room_names (sc_game game); void sc_set_game_verbose (sc_game game, sc_bool flag); sc_bool sc_get_game_verbose (sc_game game); void sc_set_game_notify_score_change (sc_game game, sc_bool flag); sc_bool sc_get_game_notify_score_change (sc_game game); These functions control a couple of facets of the way the interpreter core behaves. The Adrift Runner allows the user to select bold or normal text for printing room names. Call the appropriate function, from an event handler in the case of a GUI interpreter, to set the option. Verbose mode means that the interpreter prints out full room descriptions on each visit, Brief mode only prints those descriptions when asked for, or when a room is visited for the first time. The mode can be set either by sc_set_game_verbose() or by the player entering the "verbose" or "brief" commands. The Adrift Runner can be set to indicate whether the game score changed during a turn. A particular game will set this according to preference, and it can be change either by sc_set_game_notify_score_change() or by the player entering the "notification on" or "notification off" commands. sc_bool sc_does_game_use_sounds (sc_game); sc_bool sc_does_game_use_graphics (sc_game); These functions return TRUE if the game contains any resources of the type indicated -- sounds or graphics. There are no restrictions on when these functions may be called. sc_game_hint sc_get_first_game_hint (sc_game game); sc_game_hint sc_get_next_game_hint (sc_game game, sc_game_hint hint); These functions provide sequential access to a game's hints. The first function returns the the first valid hint applicable to the current state of the game, or NULL if there are no game hints currently available. The second returns the hint after the one passed in, or NULL when there are no more game hints currently available. See below for how to use returned hints. If you need a less insistently sequential form of access to hints, you'll need to allocate a hints array, and fill it using this function. Also, note that the valid hints for a game vary as the game is run, so any hints array that you do build like this you should consider to be invalid once control has been passed to, or returns into, run_interpret(). This function, and its cousins below, is intended to be called from os_display_hints(), but it is also valid, though perhaps pointless, to call it while the game is not running. const sc_char *sc_get_game_hint_question (sc_game game, sc_game_hint hint); const sc_char *sc_get_game_subtle_hint (sc_game game, sc_game_hint hint); const sc_char *sc_get_game_unsubtle_hint (sc_game game, sc_game_hint hint); These functions return the hint question, and the subtle and unsubtle ("sledgehammer") game hints. The question is akin to a topic in other IF systems, for example "How do I get the bull's attention?". A hint has a maximum of two strings to display, the first is intended to be subtle ("Look carefully at what you're wearing"), and the second much less so ("Wave your red underpants at the bull!"). The second hint may be absent, but the first (usually) won't be. If either is absent, the function that gets it for you will return NULL. A note about these functions; they share the same data area for their return strings, so it's probably not a good idea to keep the addresses they return across other game hint calls. For that case, you need to take copies of the strings, and use those. void sc_set_game_debugger_enabled (sc_game game, sc_bool flag); sc_bool sc_get_game_debugger_enabled (sc_game game); sc_bool sc_run_game_debugger_command (sc_game game, const sc_char *command); The first of these functions enables or disables SCARE's game debugger. When enabled, entering "debug" at the game prompt will bring up the game debugger. SCARE's debugger lets you inspect the current game state, and also set watchpoints that will trigger, and drop into the debugger when one of the game's items (objects, tasks, events, and so on) changes state. The second function queries SCARE's game debugger availability. SCARE's game debugger is disabled by default; this helps to prevent major accidental "spoilers". The third function takes a command string and treats it as if typed in at a game debugging prompt. This function is intended for interpreters that might want to use a GUI to debug the game in a separate window, or otherwise outside the main flow of game play. The debugger needs to be enabled for this function to succeed. It returns FALSE if the debugger is disabled, or if the command was not understood. Please see DEBUGGER for more on how to use SCARE's game debugger. void sc_set_predictable_random (sc_bool flag); void sc_reseed_random_sequence (sc_uint new_seed); By default, SCARE uses the platform's own rand() function, seeded from the system time shown when SCARE starts running. This will normally provide good random numbers, but is not predictable (unless reseeded), and will probably give different number sequences on different platforms. This makes games with randomized sections hard to test predictably. To address this, SCARE contains its own alternative random number generator, seeded with the constant value 1 when SCARE starts running. Although relatively good, this generator produces random numbers that are not as well distributed as those produced by the platform's own generator. It is however portable across all platforms, and so makes randomized game sections predictable; useful for testing purposes. Call sc_set_predictable_random() with flag TRUE to select this alternative random number generator, or FALSE to reinstate the platform's generator. Call sc_reseed_random_sequence() to seed the currently set random number generator with the given seed value. Use this to provide repeatable random number sequences. The new seed value may not be zero. sc_bool sc_set_locale (const sc_char *name); const sc_char *sc_get_locale (void); A game will be written using a particular character set. For Western Europe, this will normally be Windows codepage 1252 (WinLatin1), and for Russia it will be Windows codepage 1251 (WinCyrillic). SCARE needs to know which character set the game uses so that it can convert between lower and uppercase characters correctly, find spaces in strings, and generally handle text correctly. On starting a game, SCARE tries to guess the character set that the game uses (of course, it's not explicitly stored in the .taf file). Call sc_get_locale() to return the name of the currently set locale, and sc_set_locale() to set the locale explicitly to a specific value. The latter will override any value guessed by SCARE, and any prior set value. It returns TRUE if the locale name is valid, FALSE if it is unknown. At minimum, SCARE supports the locales "Latin1" and "Cyrillic". sc_int sc_strncasecmp (const sc_char *s1, const sc_char *s2, sc_int n); sc_int sc_strcasecmp (const sc_char *s1, const sc_char *s2); ANSI/ISO C doesn't offer case-insensitive string comparison functions. Most platforms offer either str[n]casecmp() or str[n]icmp(), but neither is ANSI/ISO standard so SCARE implements its own. If you want to use them, here they are. const sc_char *sc_scare_version (void); sc_int sc_scare_emulation (void); The first function returns a string giving SCARE's version information. The second returns an integer indicating SCARE's Adrift emulation level, for example 4046, representing 4.0.46.