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, const char *log_file,
126                  const char *cmd_log_file,
127                  int load_from_exe, int show_banner, const char *charset,
128                  const char *saved_state, const char *res_dir);
129 
130 /*
131  *   Execute an image file using argc/argv conventions.  We'll parse the
132  *   command line and invoke the program.
133  *
134  *   The 'executable_name' is the name of the host program; this is used to
135  *   prepare "usage" messages.
136  *
137  *   If 'defext' is true, we'll try adding a default extension ("t3",
138  *   formerly "t3x") to the name of the image file we find if the given
139  *   filename doesn't exist.  We'll always check to see if the file exists
140  *   with the exact given name before we do this, so that we don't add an
141  *   extension where none is needed.  If the caller doesn't want us to try
142  *   adding an extension at all, pass in 'defext' as false.
143  *
144  *   If 'test_mode' is true, we'll make some small changes to the program
145  *   invocation protocol appropriate to running system tests.  In
146  *   particular, we'll build the program argument list with only the root
147  *   name of the image file, not the full path - this allows the program to
148  *   display the argument list without any dependencies on local path name
149  *   conventions or the local directory structure, allowing for more easily
150  *   portable test scripts.
151  *
152  *   If 'hostifc' is null, we'll provide our own default interface.  The
153  *   caller can provide a custom host interface by passing in a non-null
154  *   'hostifc' value.
155  */
156 int vm_run_image_main(class CVmMainClientIfc *clientifc,
157                       const char *executable_name,
158                       int argc, char **argv, int defext, int test_mode,
159                       class CVmHostIfc *hostifc);
160 
161 /*
162  *   VM Main client services interface.  Callers of the vm_run_image
163  *   functions must provide an implementation of this interface.
164  */
165 class CVmMainClientIfc
166 {
167 public:
168     /*
169      *   Set "plain" mode.  This should set the console to plain ASCII output
170      *   mode, if appropriate.  Note that this can be called before
171      *   client_init(), and no globals are generally present at this point.
172      *
173      *   In most cases, this can make a call to os_plain() to set the
174      *   OS-level console to plain mode.  Non-console applications generally
175      *   need not do anything here at all.
176      */
177     virtual void set_plain_mode() = 0;
178 
179     /*
180      *   Create the main system console, if desired.  This is called during
181      *   VM initialization, so it is called prior to client_init().  Returns
182      *   the main console object, if desired.  If no main console is desired
183      *   for this application, return null.
184      */
185     virtual class CVmConsoleMain *create_console(
186         struct vm_globals *globals) = 0;
187 
188     /*
189      *   Delete the console, if we created one.  This is called during VM
190      *   termination, so it's called after client_terminate().  If
191      *   create_console() doesn't create a console, this routine need do
192      *   nothing.
193      */
194     virtual void delete_console(struct vm_globals *globals,
195                                 class CVmConsoleMain *console) = 0;
196 
197     /*
198      *   Initialization - we'll invoke this immediately after initializing
199      *   the VM (via vm_initialize), so the client can perform any global
200      *   initialization desired.  The globals are valid at this point because
201      *   we have completed VM initialization.
202      *
203      *   If script_file is non-null, it gives the name of a file to use as
204      *   the source of console input.  The client implementation should set
205      *   up accordingly; if the standard console (G_console) is being used,
206      *   the client can simply use G_console->open_script_file() to set up
207      *   scripting.
208      *
209      *   If log_file is non-null, it gives the name of a file to use to log
210      *   console output.  The client shoudl set up logging; if the standard
211      *   console if being used, G_console->open_log_file() will do the trick.
212      *
213      *   If cmd_log_file is non-null, it gives the name of a file to use to
214      *   log commands read from the input (i.e., only command input should be
215      *   logged, not other console output).  If the standard console is being
216      *   used, G_console->open_command_log() will set things up properly.
217      *
218      *   If banner_str is non-null, it gives a VM banner string that should
219      *   be displayed to the user.  If the standard console is being used,
220      *   this can be displayed using G_console->format_text().
221      *
222      *   The parameters (script_file, log_file, cmd_log_file, and the
223      *   presence or absence of banner_str) are taken from the startup
224      *   parameters.  For a command-line version, for example, these come
225      *   from command line options.  So, these are necessarily passed down in
226      *   some form from the client to begin with; so a client that never
227      *   passes these to vm_run_image() or vm_run_image_main() doesn't need
228      *   to handle these parameters at all here.
229      */
230     virtual void client_init(struct vm_globals *globals,
231                              const char *script_file,
232                              const char *log_file,
233                              const char *cmd_log_file,
234                              const char *banner_str) = 0;
235 
236     /*
237      *   Termination - we'll invoke this immediately before terminating the
238      *   VM (via vm_terminate).  Globals are still valid at this point, but
239      *   will be destroyed after this returns.
240      */
241     virtual void client_terminate(struct vm_globals *globals) = 0;
242 
243     /*
244      *   pre-execution notification - we'll invoke this function just before
245      *   starting execution in the loaded image
246      */
247     virtual void pre_exec(struct vm_globals *globals) = 0;
248 
249     /*
250      *   terminate - we'll invoke this just after execution in the loaded
251      *   image terminates
252      */
253     virtual void post_exec(struct vm_globals *globals) = 0;
254 
255     /*
256      *   Terminate with error - we'll invoke this upon catching an
257      *   exception that the image file doesn't handle and which thus
258      *   terminates execution.  Note that if this is called, post_exec()
259      *   will not be called; however, if post_exec() itself throws an
260      *   exception, we'll invoke this routine.
261      */
262     virtual void post_exec_err(struct vm_globals *globals) = 0;
263 
264     /*
265      *   Display an error message.  We'll call this with a complete error
266      *   message to display.  Note that we won't add a newline at the end of
267      *   the message, so if the message is to be displayed on a stdio-style
268      *   terminal, this routine should display a newline after the message.
269      *
270      *   If the implementation normally writes the text to the main output
271      *   console (G_console), it must take into the account the possibility
272      *   that we have not opened a system console at all (i.e., G_console
273      *   could be null), or have not allocated any globals at all (i.e.,
274      *   'globals' could be null).
275      *
276      *   If 'add_blank_line' is true, the implementation should add a blank
277      *   line after the error, if appropriate for the display device.  If
278      *   we're displaying the message in an alert box on a GUI, for example,
279      *   this can be ignored.
280      */
281     virtual void display_error(struct vm_globals *globals,
282                                const char *msg, int add_blank_line) = 0;
283 };
284 
285 #endif /* VMMAIN_H */
286 
287