4 #include <glib-object.h>
6 #include <glib/gi18n-lib.h>
7 #include "chimara-if.h"
8 #include "chimara-glk.h"
9 #include "chimara-glk-private.h"
10 #include "chimara-marshallers.h"
15 * @short_description: Widget which plays an interactive fiction game
16 * @stability: Unstable
17 * @include: libchimara/chimara-if.h
19 * The #ChimaraIF widget, given an interactive fiction game file to run, selects
20 * an appropriate interpreter plugin and runs it. Interpreter options are set by
21 * setting properties on the widget.
23 * Using it in a GTK program is similar to using #ChimaraGlk (which see).
24 * Threads must be initialized before using #ChimaraIF and the call to
25 * gtk_main() must be bracketed between gdk_threads_enter() and
26 * gdk_threads_leave(). Use chimara_if_run_game() to start playing an
27 * interactive fiction game.
30 static gboolean supported_formats[CHIMARA_IF_NUM_FORMATS][CHIMARA_IF_NUM_INTERPRETERS] = {
31 /* Frotz Nitfol Glulxe Git */
32 { TRUE, TRUE, FALSE, FALSE }, /* Z5 */
33 { TRUE, TRUE, FALSE, FALSE }, /* Z6 */
34 { TRUE, TRUE, FALSE, FALSE }, /* Z8 */
35 { TRUE, TRUE, FALSE, FALSE }, /* Zblorb */
36 { FALSE, FALSE, TRUE, TRUE }, /* Glulx */
37 { FALSE, FALSE, TRUE, TRUE } /* Gblorb */
39 static gchar *format_names[CHIMARA_IF_NUM_FORMATS] = {
40 N_("Z-code version 5"),
41 N_("Z-code version 6"),
42 N_("Z-code version 8"),
47 static gchar *interpreter_names[CHIMARA_IF_NUM_INTERPRETERS] = {
48 N_("Frotz"), N_("Nitfol"), N_("Glulxe"), N_("Git")
50 static gchar *plugin_names[CHIMARA_IF_NUM_INTERPRETERS] = {
51 "frotz", "nitfol", "glulxe", "git"
54 typedef enum _ChimaraIFFlags {
55 CHIMARA_IF_PIRACY_MODE = 1 << 0,
56 CHIMARA_IF_TANDY_BIT = 1 << 1,
57 CHIMARA_IF_EXPAND_ABBREVIATIONS = 1 << 2,
58 CHIMARA_IF_IGNORE_ERRORS = 1 << 3,
59 CHIMARA_IF_TYPO_CORRECTION = 1 << 4
62 typedef struct _ChimaraIFPrivate {
63 ChimaraIFInterpreter preferred_interpreter[CHIMARA_IF_NUM_FORMATS];
64 ChimaraIFFormat format;
65 ChimaraIFInterpreter interpreter;
67 ChimaraIFZmachineVersion interpreter_number;
69 gboolean random_seed_set;
70 /* Holding buffers for input and response */
75 #define CHIMARA_IF_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), CHIMARA_TYPE_IF, ChimaraIFPrivate))
76 #define CHIMARA_IF_USE_PRIVATE(o, n) ChimaraIFPrivate *n = CHIMARA_IF_PRIVATE(o)
82 PROP_EXPAND_ABBREVIATIONS,
85 PROP_INTERPRETER_NUMBER,
95 static guint chimara_if_signals[LAST_SIGNAL] = { 0 };
97 G_DEFINE_TYPE(ChimaraIF, chimara_if, CHIMARA_TYPE_GLK);
100 chimara_if_waiting(ChimaraGlk *glk)
102 CHIMARA_IF_USE_PRIVATE(glk, priv);
104 gchar *response = g_string_free(priv->response, FALSE);
105 priv->response = g_string_new("");
108 g_signal_emit_by_name(glk, "command", priv->input, response);
117 chimara_if_stopped(ChimaraGlk *glk)
119 CHIMARA_IF_USE_PRIVATE(glk, priv);
121 if(priv->input || priv->response->len > 0)
122 chimara_if_waiting(glk); /* Send one last command signal */
124 priv->format = CHIMARA_IF_FORMAT_NONE;
125 priv->interpreter = CHIMARA_IF_INTERPRETER_NONE;
129 chimara_if_char_input(ChimaraGlk *glk, guint32 win_rock, guint keysym)
131 CHIMARA_IF_USE_PRIVATE(glk, priv);
132 g_assert(priv->input == NULL);
135 gint outbuflen = g_unichar_to_utf8(gdk_keyval_to_unicode(keysym), outbuf);
136 priv->input = g_strndup(outbuf, outbuflen);
140 chimara_if_line_input(ChimaraGlk *glk, guint32 win_rock, gchar *input)
142 CHIMARA_IF_USE_PRIVATE(glk, priv);
143 g_assert(priv->input == NULL);
144 priv->input = g_strdup(input);
148 chimara_if_text_buffer_output(ChimaraGlk *glk, guint32 win_rock, gchar *output)
150 CHIMARA_IF_USE_PRIVATE(glk, priv);
151 g_string_append(priv->response, output);
155 chimara_if_init(ChimaraIF *self)
157 CHIMARA_IF_USE_PRIVATE(self, priv);
158 priv->preferred_interpreter[CHIMARA_IF_FORMAT_Z5] = CHIMARA_IF_INTERPRETER_FROTZ;
159 priv->preferred_interpreter[CHIMARA_IF_FORMAT_Z6] = CHIMARA_IF_INTERPRETER_FROTZ;
160 priv->preferred_interpreter[CHIMARA_IF_FORMAT_Z8] = CHIMARA_IF_INTERPRETER_FROTZ;
161 priv->preferred_interpreter[CHIMARA_IF_FORMAT_Z_BLORB] = CHIMARA_IF_INTERPRETER_FROTZ;
162 priv->preferred_interpreter[CHIMARA_IF_FORMAT_GLULX] = CHIMARA_IF_INTERPRETER_GLULXE;
163 priv->preferred_interpreter[CHIMARA_IF_FORMAT_GLULX_BLORB] = CHIMARA_IF_INTERPRETER_GLULXE;
164 priv->format = CHIMARA_IF_FORMAT_NONE;
165 priv->interpreter = CHIMARA_IF_INTERPRETER_NONE;
166 priv->flags = CHIMARA_IF_TYPO_CORRECTION;
167 priv->interpreter_number = CHIMARA_IF_ZMACHINE_DEFAULT;
168 priv->random_seed_set = FALSE;
170 priv->response = g_string_new("");
172 /* Connect to signals of ChimaraGlk parent */
173 g_signal_connect(self, "stopped", G_CALLBACK(chimara_if_stopped), NULL);
174 g_signal_connect(self, "waiting", G_CALLBACK(chimara_if_waiting), NULL);
175 g_signal_connect(self, "char-input", G_CALLBACK(chimara_if_char_input), NULL);
176 g_signal_connect(self, "line-input", G_CALLBACK(chimara_if_line_input), NULL);
177 g_signal_connect(self, "text-buffer-output", G_CALLBACK(chimara_if_text_buffer_output), NULL);
180 #define PROCESS_FLAG(flags, flag, val) (flags) = (val)? (flags) | (flag) : (flags) & ~(flag)
183 chimara_if_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
185 CHIMARA_IF_USE_PRIVATE(object, priv);
188 case PROP_PIRACY_MODE:
189 PROCESS_FLAG(priv->flags, CHIMARA_IF_PIRACY_MODE, g_value_get_boolean(value));
190 g_object_notify(object, "piracy-mode");
193 PROCESS_FLAG(priv->flags, CHIMARA_IF_TANDY_BIT, g_value_get_boolean(value));
194 g_object_notify(object, "tandy-bit");
196 case PROP_EXPAND_ABBREVIATIONS:
197 PROCESS_FLAG(priv->flags, CHIMARA_IF_EXPAND_ABBREVIATIONS, g_value_get_boolean(value));
198 g_object_notify(object, "expand-abbreviations");
200 case PROP_IGNORE_ERRORS:
201 PROCESS_FLAG(priv->flags, CHIMARA_IF_IGNORE_ERRORS, g_value_get_boolean(value));
202 g_object_notify(object, "ignore-errors");
204 case PROP_TYPO_CORRECTION:
205 PROCESS_FLAG(priv->flags, CHIMARA_IF_TYPO_CORRECTION, g_value_get_boolean(value));
206 g_object_notify(object, "typo-correction");
208 case PROP_INTERPRETER_NUMBER:
209 priv->interpreter_number = g_value_get_uint(value);
210 g_object_notify(object, "interpreter-number");
212 case PROP_RANDOM_SEED:
213 priv->random_seed = g_value_get_int(value);
214 g_object_notify(object, "random-seed");
216 case PROP_RANDOM_SEED_SET:
217 priv->random_seed_set = g_value_get_boolean(value);
218 g_object_notify(object, "random-seed-set");
221 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
226 chimara_if_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
228 CHIMARA_IF_USE_PRIVATE(object, priv);
231 case PROP_PIRACY_MODE:
232 g_value_set_boolean(value, priv->flags & CHIMARA_IF_PIRACY_MODE);
235 g_value_set_boolean(value, priv->flags & CHIMARA_IF_TANDY_BIT);
237 case PROP_EXPAND_ABBREVIATIONS:
238 g_value_set_boolean(value, priv->flags & CHIMARA_IF_EXPAND_ABBREVIATIONS);
240 case PROP_IGNORE_ERRORS:
241 g_value_set_boolean(value, priv->flags & CHIMARA_IF_IGNORE_ERRORS);
243 case PROP_TYPO_CORRECTION:
244 g_value_set_boolean(value, priv->flags & CHIMARA_IF_TYPO_CORRECTION);
246 case PROP_INTERPRETER_NUMBER:
247 g_value_set_uint(value, priv->interpreter_number);
249 case PROP_RANDOM_SEED:
250 g_value_set_int(value, priv->random_seed);
252 case PROP_RANDOM_SEED_SET:
253 g_value_set_boolean(value, priv->random_seed_set);
256 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
261 chimara_if_finalize(GObject *object)
263 G_OBJECT_CLASS(chimara_if_parent_class)->finalize(object);
267 chimara_if_command(ChimaraIF *self, gchar *input, gchar *response)
269 /* Default signal handler */
272 /* COMPAT: G_PARAM_STATIC_STRINGS only appeared in GTK 2.13.0 */
273 #ifndef G_PARAM_STATIC_STRINGS
275 /* COMPAT: G_PARAM_STATIC_NAME and friends only appeared in GTK 2.8 */
276 #if GTK_CHECK_VERSION(2,8,0)
277 #define G_PARAM_STATIC_STRINGS (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)
279 #define G_PARAM_STATIC_STRINGS (0)
285 chimara_if_class_init(ChimaraIFClass *klass)
287 /* Override methods of parent classes */
288 GObjectClass *object_class = G_OBJECT_CLASS(klass);
289 object_class->set_property = chimara_if_set_property;
290 object_class->get_property = chimara_if_get_property;
291 object_class->finalize = chimara_if_finalize;
294 klass->command = chimara_if_command;
296 * ChimaraIF::command:
297 * @self: The widget that received the signal
298 * @input: The command typed into the game, or %NULL
299 * @response: The game's response to the command
301 * Emitted once for each input-response cycle of an interactive fiction
302 * game. Note that games with nontraditional input systems (i.e. not all
303 * taking place in the same text buffer window) may confuse this signal.
305 * It may happen that @input is %NULL, in which case @response is not due to
306 * a user command, but contains the text printed at the beginning of the
307 * game, up until the first prompt.
309 chimara_if_signals[COMMAND] = g_signal_new("command",
310 G_OBJECT_CLASS_TYPE(klass), 0,
311 G_STRUCT_OFFSET(ChimaraIFClass, command), NULL, NULL,
312 _chimara_marshal_VOID__STRING_STRING,
313 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
317 * ChimaraIF:piracy-mode:
319 * The Z-machine specification defines a facility for games to ask the
320 * interpreter they are running on whether this copy of the game is pirated.
321 * How the interpreter is supposed to magically determine that it is running
322 * pirate software is unclear, and so the majority of games and interpreters
323 * ignore this feature. Set this property to %TRUE if you want the
324 * interpreter to pretend it has detected a pirated game.
326 * Only affects Z-machine interpreters.
328 g_object_class_install_property(object_class, PROP_PIRACY_MODE,
329 g_param_spec_boolean("piracy-mode", _("Piracy mode"),
330 _("Pretend the game is pirated"), FALSE,
331 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
333 * ChimaraIF:tandy-bit:
335 * Some early Infocom games were sold by the Tandy Corporation. Setting this
336 * property to %TRUE changes the wording of some Version 3 Infocom games
337 * slightly, so as to be less offensive. See <ulink
338 * url="http://www.ifarchive.org/if-archive/infocom/info/tandy_bits.html">
339 * http://www.ifarchive.org/if-archive/infocom/info/tandy_bits.html</ulink>.
341 * Only affects Z-machine interpreters.
343 g_object_class_install_property(object_class, PROP_TANDY_BIT,
344 g_param_spec_boolean("tandy-bit", _("Tandy bit"),
345 _("Censor certain Infocom games"), FALSE,
346 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
348 * ChimaraIF:expand-abbreviations:
350 * Most Z-machine games, in particular ones compiled with the Inform
351 * library, support the following one-letter abbreviations:
353 * <member>D — Down</member>
354 * <member>E — East</member>
355 * <member>G — aGain</member>
356 * <member>I — Inventory</member>
357 * <member>L — Look</member>
358 * <member>N — North</member>
359 * <member>O — Oops</member>
360 * <member>Q — Quit</member>
361 * <member>S — South</member>
362 * <member>U — Up</member>
363 * <member>W — West</member>
364 * <member>X — eXamine</member>
365 * <member>Y — Yes</member>
366 * <member>Z — wait (ZZZZ...)</member>
368 * Some early Infocom games might not recognize these abbreviations. Setting
369 * this property to %TRUE will cause the interpreter to expand the
370 * abbreviations to the full words before passing the commands on to the
371 * game. Frotz only expands G, X, and Z; Nitfol expands all of the above
372 * plus the following nonstandard ones:
374 * <member>C — Close</member>
375 * <member>K — attacK</member>
376 * <member>P — oPen</member>
377 * <member>R — dRop</member>
378 * <member>T — Take</member>
381 * Only affects Z-machine interpreters. Behaves differently on Frotz and
384 g_object_class_install_property(object_class, PROP_EXPAND_ABBREVIATIONS,
385 g_param_spec_boolean("expand-abbreviations", _("Expand abbreviations"),
386 _("Expand abbreviations such as X for EXAMINE"), FALSE,
387 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
389 * ChimaraIF:ignore-errors:
391 * Setting this property to %TRUE will cause the interpreter to ignore
392 * certain Z-machine runtime errors. Frotz will ignore any fatal errors.
393 * Nitfol by default warns about any shady behavior, and this property will
394 * turn those warnings off.
396 * Only affects Z-machine interpreters. Behaves differently on Frotz and
399 g_object_class_install_property(object_class, PROP_IGNORE_ERRORS,
400 g_param_spec_boolean("ignore-errors", _("Ignore errors"),
401 _("Do not warn the user about Z-machine errors"), FALSE,
402 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
404 * ChimaraIF:typo-correction:
406 * Nitfol has an automatic typo-correction facility, where it searches the
407 * game dictionary for words which differ by one letter from any unknown
408 * input words. Set this property to %FALSE to turn this feature off.
410 * Only affects Nitfol.
412 g_object_class_install_property(object_class, PROP_TYPO_CORRECTION,
413 g_param_spec_boolean("typo-correction", _("Typo correction"),
414 _("Try to remedy typos if the interpreter supports it"), TRUE,
415 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
417 * ChimaraIF:interpreter-number:
419 * Infocom gave each port of their interpreter a different number. The Frotz
420 * and Nitfol plugins can emulate any of these platforms. Some games behave
421 * slightly differently depending on what platform they are on. Set this
422 * property to a #ChimaraIFZmachineVersion value to emulate a certain
425 * Note that Nitfol pretends to be an Apple IIe by default.
427 * Only affects Z-machine interpreters.
429 g_object_class_install_property(object_class, PROP_INTERPRETER_NUMBER,
430 g_param_spec_uint("interpreter-number", _("Interpreter number"),
431 _("Platform the Z-machine should pretend it is running on"),
432 CHIMARA_IF_ZMACHINE_DEFAULT, CHIMARA_IF_ZMACHINE_MAXVAL, CHIMARA_IF_ZMACHINE_DEFAULT,
433 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
435 * ChimaraIF:random-seed:
437 * If the #ChimaraIF:random-seed-set property is %TRUE, then the interpreter
438 * will use the value of this property as a seed for the random number
439 * generator. Use this feature to duplicate sequences of random numbers
442 * Note that the value -1 is a valid random number seed for
443 * Nitfol, whereas it will cause Frotz to pick an arbitrary seed even when
444 * #ChimaraIF:random-seed-set is %TRUE.
446 * Only affects Z-machine interpreters. Behaves slightly differently on
449 g_object_class_install_property(object_class, PROP_RANDOM_SEED,
450 g_param_spec_int("random-seed", _("Random seed"),
451 _("Seed for the random number generator"), G_MININT, G_MAXINT, 0,
452 G_PARAM_READWRITE | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
454 * ChimaraIF:random-seed-set:
456 * Whether to use or ignore the #ChimaraIF:random-seed property.
458 * Only affects Z-machine interpreters.
460 g_object_class_install_property(object_class, PROP_RANDOM_SEED_SET,
461 g_param_spec_boolean("random-seed-set", _("Random seed set"),
462 _("Whether the seed for the random number generator should be set manually"), FALSE,
463 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
466 g_type_class_add_private(klass, sizeof(ChimaraIFPrivate));
469 /* PUBLIC FUNCTIONS */
474 * Creates and initializes a new #ChimaraIF widget.
476 * Return value: a #ChimaraIF widget, with a floating reference.
481 /* This is a library entry point; initialize the library */
483 return GTK_WIDGET(g_object_new(CHIMARA_TYPE_IF, NULL));
487 * chimara_if_set_preferred_interpreter:
488 * @self: A #ChimaraIF widget.
489 * @format: The game format to set the preferred interpreter plugin for.
490 * @interpreter: The preferred interpreter plugin for @format.
492 * The function chimara_if_run_game() picks an appropriate interpreter for the
493 * type of game file it is given. This function sets which interpreter is picked
494 * for a certain game file format. Most formats, notably the Z-machine, have
495 * had many different interpreters written for them over the years, all with
496 * slightly different quirks and capabilities, so there is plenty of choice.
499 chimara_if_set_preferred_interpreter(ChimaraIF *self, ChimaraIFFormat format, ChimaraIFInterpreter interpreter)
501 g_return_if_fail(self && CHIMARA_IS_IF(self));
502 g_return_if_fail(format < CHIMARA_IF_NUM_FORMATS);
503 g_return_if_fail(interpreter < CHIMARA_IF_NUM_INTERPRETERS);
505 CHIMARA_IF_USE_PRIVATE(self, priv);
507 if(supported_formats[format][interpreter])
508 priv->preferred_interpreter[format] = interpreter;
510 g_warning("Format '%s' is not supported by interpreter '%s'", format_names[format], interpreter_names[interpreter]);
514 * chimara_if_get_preferred_interpreter:
515 * @self: A #ChimaraIF widget.
516 * @format: The game format to query the preferred interpreter plugin for.
518 * Looks up the preferred interpreter for the game file format @format. See
519 * chimara_if_set_preferred_interpreter().
521 * Returns: a #ChimaraIFInterpreter value representing the preferred interpreter
522 * plugin for @format.
525 chimara_if_get_preferred_interpreter(ChimaraIF *self, ChimaraIFFormat format)
527 g_return_val_if_fail(self && CHIMARA_IS_IF(self), -1);
528 g_return_val_if_fail(format < CHIMARA_IF_NUM_FORMATS, -1);
529 CHIMARA_IF_USE_PRIVATE(self, priv);
530 return priv->preferred_interpreter[format];
534 * chimara_if_run_game:
535 * @self: A #ChimaraIF widget.
536 * @gamefile: Path to an interactive fiction game file.
537 * @error: Return location for an error, or %NULL.
539 * Autodetects the type of a game file and runs it using an appropriate
540 * interpreter plugin. If there is more than one interpreter that supports the
541 * file format, the preferred one will be picked, according to
542 * chimara_if_set_preferred_interpreter().
544 * Returns: %TRUE if the game was started successfully, %FALSE if not, in which
545 * case @error is set.
548 chimara_if_run_game(ChimaraIF *self, gchar *gamefile, GError **error)
550 g_return_val_if_fail(self && CHIMARA_IS_IF(self), FALSE);
551 g_return_val_if_fail(gamefile, FALSE);
553 CHIMARA_IF_USE_PRIVATE(self, priv);
555 /* Find out what format the game is */
556 /* TODO: Look inside the file instead of just looking at the extension */
557 ChimaraIFFormat format = CHIMARA_IF_FORMAT_Z5;
558 if(g_str_has_suffix(gamefile, ".z5"))
559 format = CHIMARA_IF_FORMAT_Z5;
560 else if(g_str_has_suffix(gamefile, ".z6"))
561 format = CHIMARA_IF_FORMAT_Z6;
562 else if(g_str_has_suffix(gamefile, ".z8"))
563 format = CHIMARA_IF_FORMAT_Z8;
564 else if(g_str_has_suffix(gamefile, ".zlb") || g_str_has_suffix(gamefile, ".zblorb"))
565 format = CHIMARA_IF_FORMAT_Z_BLORB;
566 else if(g_str_has_suffix(gamefile, ".ulx"))
567 format = CHIMARA_IF_FORMAT_GLULX;
568 else if(g_str_has_suffix(gamefile, ".blb") || g_str_has_suffix(gamefile, ".blorb") || g_str_has_suffix(gamefile, ".glb") || g_str_has_suffix(gamefile, ".gblorb"))
569 format = CHIMARA_IF_FORMAT_GLULX_BLORB;
571 /* Now decide what interpreter to use */
572 ChimaraIFInterpreter interpreter = priv->preferred_interpreter[format];
573 gchar *pluginfile = g_strconcat(plugin_names[interpreter], "." G_MODULE_SUFFIX, NULL);
578 #define LT_OBJDIR ".libs" /* Pre-2.2 libtool, so take a wild guess */
579 #endif /* LT_OBJDIR */
580 /* If there is a plugin in the source tree, use that */
581 pluginpath = g_build_filename(PLUGINSOURCEDIR, plugin_names[interpreter], LT_OBJDIR, pluginfile, NULL);
582 if( !g_file_test(pluginpath, G_FILE_TEST_EXISTS) )
586 pluginpath = g_build_filename(PLUGINDIR, pluginfile, NULL);
587 if( !g_file_test(pluginpath, G_FILE_TEST_EXISTS) )
591 g_set_error(error, CHIMARA_ERROR, CHIMARA_PLUGIN_NOT_FOUND, _("No appropriate %s interpreter plugin was found"), interpreter_names[interpreter]);
599 /* Decide what arguments to pass to the interpreters; currently only the
600 Z-machine interpreters accept command line arguments other than the game */
602 gchar *terpnumstr = NULL, *randomstr = NULL;
603 args = g_slist_prepend(args, pluginpath);
604 args = g_slist_prepend(args, gamefile);
607 case CHIMARA_IF_INTERPRETER_FROTZ:
608 if(priv->flags & CHIMARA_IF_PIRACY_MODE)
609 args = g_slist_prepend(args, "-P");
610 if(priv->flags & CHIMARA_IF_TANDY_BIT)
611 args = g_slist_prepend(args, "-t");
612 if(priv->flags & CHIMARA_IF_EXPAND_ABBREVIATIONS)
613 args = g_slist_prepend(args, "-x");
614 if(priv->flags & CHIMARA_IF_IGNORE_ERRORS)
615 args = g_slist_prepend(args, "-i");
616 if(priv->interpreter_number != CHIMARA_IF_ZMACHINE_DEFAULT)
618 terpnumstr = g_strdup_printf("-I%u", priv->interpreter_number);
619 args = g_slist_prepend(args, terpnumstr);
621 if(priv->random_seed_set)
623 randomstr = g_strdup_printf("-s%d", priv->random_seed);
624 args = g_slist_prepend(args, randomstr);
627 case CHIMARA_IF_INTERPRETER_NITFOL:
628 if(priv->flags & CHIMARA_IF_PIRACY_MODE)
629 args = g_slist_prepend(args, "-pirate");
630 if(priv->flags & CHIMARA_IF_TANDY_BIT)
631 args = g_slist_prepend(args, "-tandy");
632 if(!(priv->flags & CHIMARA_IF_EXPAND_ABBREVIATIONS))
633 args = g_slist_prepend(args, "-no-expand");
634 if(priv->flags & CHIMARA_IF_IGNORE_ERRORS)
635 args = g_slist_prepend(args, "-ignore");
636 if(!(priv->flags & CHIMARA_IF_TYPO_CORRECTION))
637 args = g_slist_prepend(args, "-no-spell");
638 if(priv->interpreter_number != CHIMARA_IF_ZMACHINE_DEFAULT)
640 terpnumstr = g_strdup_printf("-terpnum%u", priv->interpreter_number);
641 args = g_slist_prepend(args, terpnumstr);
643 if(priv->random_seed_set)
645 randomstr = g_strdup_printf("-random%d", priv->random_seed);
646 args = g_slist_prepend(args, randomstr);
653 /* Allocate argv to hold the arguments */
654 int argc = g_slist_length(args);
655 args = g_slist_prepend(args, NULL);
656 char **argv = g_new0(char *, argc + 1);
659 args = g_slist_reverse(args);
662 for(count = 0, ptr = args; ptr; count++, ptr = g_slist_next(ptr))
663 argv[count] = ptr->data;
665 /* Set the story name */
666 /* We peek into ChimaraGlk's private data here, because GObject has no
667 equivalent to "protected" */
668 CHIMARA_GLK_USE_PRIVATE(self, glk_priv);
669 glk_priv->story_name = g_path_get_basename(gamefile);
670 g_object_notify(G_OBJECT(self), "story-name");
672 gboolean retval = chimara_glk_run(CHIMARA_GLK(self), pluginpath, argc, argv, error);
680 /* Set current format and interpreter if plugin was started successfully */
683 priv->format = format;
684 priv->interpreter = interpreter;
690 * chimara_if_get_format:
691 * @self: A #ChimaraIF widget.
693 * Returns the file format of the currently running game.
695 * Returns: a #ChimaraIFFormat constant.
698 chimara_if_get_format(ChimaraIF *self)
700 g_return_val_if_fail(self && CHIMARA_IS_IF(self), CHIMARA_IF_FORMAT_NONE);
701 CHIMARA_IF_USE_PRIVATE(self, priv);
706 * chimara_if_get_interpreter:
707 * @self: A #ChimaraIF widget.
709 * Returns the interpreter plugin currently running.
711 * Returns: a #ChimaraIFInterpreter constant.
714 chimara_if_get_interpreter(ChimaraIF *self)
716 g_return_val_if_fail(self && CHIMARA_IS_IF(self), CHIMARA_IF_FORMAT_NONE);
717 CHIMARA_IF_USE_PRIVATE(self, priv);
718 return priv->interpreter;