| | 1 | /* $Header$ */ |
| | 2 | |
| | 3 | /* |
| | 4 | * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. |
| | 5 | * |
| | 6 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 7 | * on using and copying this software. |
| | 8 | */ |
| | 9 | /* |
| | 10 | Name |
| | 11 | vmmain.h - main entrypoint to run a T3 image file |
| | 12 | Function |
| | 13 | |
| | 14 | Notes |
| | 15 | |
| | 16 | Modified |
| | 17 | 10/07/99 MJRoberts - Creation |
| | 18 | */ |
| | 19 | |
| | 20 | #ifndef VMMAIN_H |
| | 21 | #define VMMAIN_H |
| | 22 | |
| | 23 | #include "vmglob.h" |
| | 24 | |
| | 25 | /* |
| | 26 | * Parse a command line to determine the name of the game file specified by |
| | 27 | * the arguments. If we can find a game file specification, we'll fill in |
| | 28 | * 'buf' with the filename and return true; if there's no file name |
| | 29 | * specified, we'll return false. |
| | 30 | * |
| | 31 | * Note that our parsing will work for TADS 2 or TADS 3 interpreter command |
| | 32 | * lines, so this routine can be used to extract the filename from an |
| | 33 | * ambiguous command line in order to check the file for its type and |
| | 34 | * thereby resolve which interpreter to use. |
| | 35 | * |
| | 36 | * Note that the filename might not come directly from the command |
| | 37 | * arguments, since it might be implied. If there's no game file directly |
| | 38 | * specified, but there is an explicit "-r" option to restore a saved game, |
| | 39 | * we'll pull the game filename out of the saved game file if possible. |
| | 40 | * Saved game files in both TADS 2 and TADS 3 can store the original game |
| | 41 | * file name that was being executed at the time the game was saved. |
| | 42 | */ |
| | 43 | int vm_get_game_arg(int argc, const char *const *argv, |
| | 44 | char *buf, size_t buflen); |
| | 45 | |
| | 46 | /* |
| | 47 | * Given a game file argument, determine which engine (TADS 2 or TADS 3) |
| | 48 | * should be used to run the game. |
| | 49 | * |
| | 50 | * We'll first check to see if the given file exists. If it does, we'll |
| | 51 | * read header information from the file to try to identify the game. If |
| | 52 | * the file does not exist, we will proceed to check default suffixes, if |
| | 53 | * the suffix arguments are non-null. |
| | 54 | * |
| | 55 | * If defexts is not null, it gives an array of default filename suffix |
| | 56 | * strings, suitable for use with os_defext(). When the name as given |
| | 57 | * doesn't refer to an existing file, we'll try looking for files with |
| | 58 | * these suffixes, one at a time. |
| | 59 | * |
| | 60 | * Returns one of the VM_GGT_xxx codes. |
| | 61 | * |
| | 62 | * If the return value is 2 or 3, we'll fill in the actual_filename buffer |
| | 63 | * with the full name of the file; if we added a default suffix, the |
| | 64 | * suffix will be included in this result. |
| | 65 | */ |
| | 66 | int vm_get_game_type(const char *filename, |
| | 67 | char *actual_filename, |
| | 68 | size_t actual_filename_buffer_length, |
| | 69 | const char *const *defexts, size_t defext_count); |
| | 70 | |
| | 71 | /* |
| | 72 | * Returns codes for vm_get_game_type() |
| | 73 | */ |
| | 74 | |
| | 75 | /* game type is TADS 2 */ |
| | 76 | #define VM_GGT_TADS2 2 |
| | 77 | |
| | 78 | /* game type is TADS 3 */ |
| | 79 | #define VM_GGT_TADS3 3 |
| | 80 | |
| | 81 | /* game file not found (even after trying default extensions) */ |
| | 82 | #define VM_GGT_NOT_FOUND (-1) |
| | 83 | |
| | 84 | /* game file exists but isn't a valid tads 2 or tads 3 game */ |
| | 85 | #define VM_GGT_INVALID (-2) |
| | 86 | |
| | 87 | /* |
| | 88 | * ambiguous filename - the exact filename doesn't exist, and more than |
| | 89 | * one default suffix version exists |
| | 90 | */ |
| | 91 | #define VM_GGT_AMBIG (-3) |
| | 92 | |
| | 93 | /* |
| | 94 | * determine if a VM_GGT_xxx code refers to a valid engine version: |
| | 95 | * returns true if the code is an engine version, false if the code is an |
| | 96 | * error indication |
| | 97 | */ |
| | 98 | #define vm_ggt_is_valid(code) ((code) > 0) |
| | 99 | |
| | 100 | |
| | 101 | /* |
| | 102 | * Execute an image file. We'll return zero on success, or a VM error code |
| | 103 | * on failure. If an error occurs, we'll fill in 'errbuf' with the text of |
| | 104 | * a message describing the problem. |
| | 105 | * |
| | 106 | * If 'load_from_exe' is true, the image filename given is actually the |
| | 107 | * name of the native executable file that we're running, and we should |
| | 108 | * load the image file that's attached to the native executable file via |
| | 109 | * the system-specific os_exeseek() mechanism. |
| | 110 | * |
| | 111 | * If 'script_file' is not null, we'll read console input from the given |
| | 112 | * file. If 'log_file' is not null, we'll log console output to the given |
| | 113 | * file. If 'cmd_log_file' is not null, we'll log each line we read from |
| | 114 | * the console to the given command logging file. |
| | 115 | * |
| | 116 | * 'charset' optionally selects a character set to use for text displayed |
| | 117 | * to or read from the user interface. If this is null, we'll use the |
| | 118 | * current system character set as indicated by the osifc layer. 'charset' |
| | 119 | * should usually be null unless explicitly specified by the user. |
| | 120 | */ |
| | 121 | int vm_run_image(class CVmMainClientIfc *clientifc, |
| | 122 | const char *image_file_name, |
| | 123 | class CVmHostIfc *hostifc, |
| | 124 | const char *const *prog_argv, int prog_argc, |
| | 125 | const char *script_file, int script_quiet, |
| | 126 | const char *log_file, const char *cmd_log_file, |
| | 127 | int load_from_exe, int show_banner, |
| | 128 | const char *charset, const char *log_charset, |
| | 129 | const char *saved_state, const char *res_dir); |
| | 130 | |
| | 131 | /* |
| | 132 | * Execute an image file using argc/argv conventions. We'll parse the |
| | 133 | * command line and invoke the program. |
| | 134 | * |
| | 135 | * The 'executable_name' is the name of the host program; this is used to |
| | 136 | * prepare "usage" messages. |
| | 137 | * |
| | 138 | * If 'defext' is true, we'll try adding a default extension ("t3", |
| | 139 | * formerly "t3x") to the name of the image file we find if the given |
| | 140 | * filename doesn't exist. We'll always check to see if the file exists |
| | 141 | * with the exact given name before we do this, so that we don't add an |
| | 142 | * extension where none is needed. If the caller doesn't want us to try |
| | 143 | * adding an extension at all, pass in 'defext' as false. |
| | 144 | * |
| | 145 | * If 'test_mode' is true, we'll make some small changes to the program |
| | 146 | * invocation protocol appropriate to running system tests. In |
| | 147 | * particular, we'll build the program argument list with only the root |
| | 148 | * name of the image file, not the full path - this allows the program to |
| | 149 | * display the argument list without any dependencies on local path name |
| | 150 | * conventions or the local directory structure, allowing for more easily |
| | 151 | * portable test scripts. |
| | 152 | * |
| | 153 | * If 'hostifc' is null, we'll provide our own default interface. The |
| | 154 | * caller can provide a custom host interface by passing in a non-null |
| | 155 | * 'hostifc' value. |
| | 156 | */ |
| | 157 | int vm_run_image_main(class CVmMainClientIfc *clientifc, |
| | 158 | const char *executable_name, |
| | 159 | int argc, char **argv, int defext, int test_mode, |
| | 160 | class CVmHostIfc *hostifc); |
| | 161 | |
| | 162 | /* |
| | 163 | * VM Main client services interface. Callers of the vm_run_image |
| | 164 | * functions must provide an implementation of this interface. |
| | 165 | */ |
| | 166 | class CVmMainClientIfc |
| | 167 | { |
| | 168 | public: |
| | 169 | /* |
| | 170 | * Set "plain" mode. This should set the console to plain ASCII output |
| | 171 | * mode, if appropriate. Note that this can be called before |
| | 172 | * client_init(), and no globals are generally present at this point. |
| | 173 | * |
| | 174 | * In most cases, this can make a call to os_plain() to set the |
| | 175 | * OS-level console to plain mode. Non-console applications generally |
| | 176 | * need not do anything here at all. |
| | 177 | */ |
| | 178 | virtual void set_plain_mode() = 0; |
| | 179 | |
| | 180 | /* |
| | 181 | * Create the main system console, if desired. This is called during |
| | 182 | * VM initialization, so it is called prior to client_init(). Returns |
| | 183 | * the main console object, if desired. If no main console is desired |
| | 184 | * for this application, return null. |
| | 185 | */ |
| | 186 | virtual class CVmConsoleMain *create_console( |
| | 187 | struct vm_globals *globals) = 0; |
| | 188 | |
| | 189 | /* |
| | 190 | * Delete the console, if we created one. This is called during VM |
| | 191 | * termination, so it's called after client_terminate(). If |
| | 192 | * create_console() doesn't create a console, this routine need do |
| | 193 | * nothing. |
| | 194 | */ |
| | 195 | virtual void delete_console(struct vm_globals *globals, |
| | 196 | class CVmConsoleMain *console) = 0; |
| | 197 | |
| | 198 | /* |
| | 199 | * Initialization - we'll invoke this immediately after initializing |
| | 200 | * the VM (via vm_initialize), so the client can perform any global |
| | 201 | * initialization desired. The globals are valid at this point because |
| | 202 | * we have completed VM initialization. |
| | 203 | * |
| | 204 | * If script_file is non-null, it gives the name of a file to use as |
| | 205 | * the source of console input. The client implementation should set |
| | 206 | * up accordingly; if the standard console (G_console) is being used, |
| | 207 | * the client can simply use G_console->open_script_file() to set up |
| | 208 | * scripting. |
| | 209 | * |
| | 210 | * If log_file is non-null, it gives the name of a file to use to log |
| | 211 | * console output. The client shoudl set up logging; if the standard |
| | 212 | * console if being used, G_console->open_log_file() will do the trick. |
| | 213 | * |
| | 214 | * If cmd_log_file is non-null, it gives the name of a file to use to |
| | 215 | * log commands read from the input (i.e., only command input should be |
| | 216 | * logged, not other console output). If the standard console is being |
| | 217 | * used, G_console->open_command_log() will set things up properly. |
| | 218 | * |
| | 219 | * If banner_str is non-null, it gives a VM banner string that should |
| | 220 | * be displayed to the user. If the standard console is being used, |
| | 221 | * this can be displayed using G_console->format_text(). |
| | 222 | * |
| | 223 | * The parameters (script_file, log_file, cmd_log_file, and the |
| | 224 | * presence or absence of banner_str) are taken from the startup |
| | 225 | * parameters. For a command-line version, for example, these come |
| | 226 | * from command line options. So, these are necessarily passed down in |
| | 227 | * some form from the client to begin with; so a client that never |
| | 228 | * passes these to vm_run_image() or vm_run_image_main() doesn't need |
| | 229 | * to handle these parameters at all here. |
| | 230 | */ |
| | 231 | virtual void client_init(struct vm_globals *globals, |
| | 232 | const char *script_file, int script_quiet, |
| | 233 | const char *log_file, |
| | 234 | const char *cmd_log_file, |
| | 235 | const char *banner_str) = 0; |
| | 236 | |
| | 237 | /* |
| | 238 | * Termination - we'll invoke this immediately before terminating the |
| | 239 | * VM (via vm_terminate). Globals are still valid at this point, but |
| | 240 | * will be destroyed after this returns. |
| | 241 | */ |
| | 242 | virtual void client_terminate(struct vm_globals *globals) = 0; |
| | 243 | |
| | 244 | /* |
| | 245 | * pre-execution notification - we'll invoke this function just before |
| | 246 | * starting execution in the loaded image |
| | 247 | */ |
| | 248 | virtual void pre_exec(struct vm_globals *globals) = 0; |
| | 249 | |
| | 250 | /* |
| | 251 | * terminate - we'll invoke this just after execution in the loaded |
| | 252 | * image terminates |
| | 253 | */ |
| | 254 | virtual void post_exec(struct vm_globals *globals) = 0; |
| | 255 | |
| | 256 | /* |
| | 257 | * Terminate with error - we'll invoke this upon catching an |
| | 258 | * exception that the image file doesn't handle and which thus |
| | 259 | * terminates execution. Note that if this is called, post_exec() |
| | 260 | * will not be called; however, if post_exec() itself throws an |
| | 261 | * exception, we'll invoke this routine. |
| | 262 | */ |
| | 263 | virtual void post_exec_err(struct vm_globals *globals) = 0; |
| | 264 | |
| | 265 | /* |
| | 266 | * Display an error message. We'll call this with a complete error |
| | 267 | * message to display. Note that we won't add a newline at the end of |
| | 268 | * the message, so if the message is to be displayed on a stdio-style |
| | 269 | * terminal, this routine should display a newline after the message. |
| | 270 | * |
| | 271 | * If the implementation normally writes the text to the main output |
| | 272 | * console (G_console), it must take into the account the possibility |
| | 273 | * that we have not opened a system console at all (i.e., G_console |
| | 274 | * could be null), or have not allocated any globals at all (i.e., |
| | 275 | * 'globals' could be null). |
| | 276 | * |
| | 277 | * If 'add_blank_line' is true, the implementation should add a blank |
| | 278 | * line after the error, if appropriate for the display device. If |
| | 279 | * we're displaying the message in an alert box on a GUI, for example, |
| | 280 | * this can be ignored. |
| | 281 | */ |
| | 282 | virtual void display_error(struct vm_globals *globals, |
| | 283 | const char *msg, int add_blank_line) = 0; |
| | 284 | }; |
| | 285 | |
| | 286 | #endif /* VMMAIN_H */ |
| | 287 | |