aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/tads/tads2/character_map.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/tads/tads2/character_map.cpp')
-rw-r--r--engines/glk/tads/tads2/character_map.cpp339
1 files changed, 339 insertions, 0 deletions
diff --git a/engines/glk/tads/tads2/character_map.cpp b/engines/glk/tads/tads2/character_map.cpp
new file mode 100644
index 0000000000..91daa8668c
--- /dev/null
+++ b/engines/glk/tads/tads2/character_map.cpp
@@ -0,0 +1,339 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/tads/tads2/character_map.h"
+#include "glk/tads/tads2/lib.h"
+#include "glk/tads/tads2/error.h"
+#include "glk/tads/tads2/os.h"
+#include "glk/tads/tads2/text_io.h"
+#include "glk/tads/osfrobtads.h"
+#include "common/algorithm.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+/* ------------------------------------------------------------------------ */
+/*
+ * Global variables for character mapping tables
+ */
+
+unsigned char G_cmap_input[256];
+unsigned char G_cmap_output[256];
+char G_cmap_id[5];
+char G_cmap_ldesc[CMAP_LDESC_MAX_LEN + 1];
+
+
+/*
+ * static variables
+ */
+
+/*
+ * flag: true -> a character set has been explicitly loaded, so we
+ * should ignore any game character set setting
+ */
+static int S_cmap_loaded;
+
+
+/* ------------------------------------------------------------------------ */
+/*
+ * Initialize the default character mappings
+ */
+void cmap_init_default(void)
+{
+ size_t i;
+
+ /* initialize the input table */
+ for (i = 0 ; i < sizeof(G_cmap_input)/sizeof(G_cmap_input[0]) ; ++i)
+ G_cmap_input[i] = (unsigned char)i;
+
+ /* initialize the output table */
+ for (i = 0 ; i < sizeof(G_cmap_output)/sizeof(G_cmap_output[0]) ; ++i)
+ G_cmap_output[i] = (unsigned char)i;
+
+ /* we have a null ID */
+ memset(G_cmap_id, 0, sizeof(G_cmap_id));
+
+ /* indicate that it's the default */
+ strcpy(G_cmap_ldesc, "(native/no mapping)");
+
+ /* note that we have no character set loaded */
+ S_cmap_loaded = FALSE;
+}
+
+/* ------------------------------------------------------------------------ */
+/*
+ * Internal routine to load a character map from a file
+ */
+static int cmap_load_internal(char *filename)
+{
+ osfildef *fp;
+ static char sig1[] = CMAP_SIG_S100;
+ char buf[256];
+ uchar lenbuf[2];
+ size_t len;
+ int sysblk;
+
+ /* if there's no mapping file, use the default mapping */
+ if (filename == 0)
+ {
+ /* initialize with the default mapping */
+ cmap_init_default();
+
+ /* return success */
+ return 0;
+ }
+
+ /* open the file */
+ fp = osfoprb(filename, OSFTCMAP);
+ if (fp == 0)
+ return 1;
+
+ /* check the signature */
+ if (osfrb(fp, buf, sizeof(sig1))
+ || memcmp(buf, sig1, sizeof(sig1)) != 0)
+ {
+ osfcls(fp);
+ return 2;
+ }
+
+ /* load the ID */
+ G_cmap_id[4] = '\0';
+ if (osfrb(fp, G_cmap_id, 4))
+ {
+ osfcls(fp);
+ return 3;
+ }
+
+ /* load the long description */
+ if (osfrb(fp, lenbuf, 2)
+ || (len = osrp2(lenbuf)) > sizeof(G_cmap_ldesc)
+ || osfrb(fp, G_cmap_ldesc, len))
+ {
+ osfcls(fp);
+ return 4;
+ }
+
+ /* load the two tables - input, then output */
+ if (osfrb(fp, G_cmap_input, sizeof(G_cmap_input))
+ || osfrb(fp, G_cmap_output, sizeof(G_cmap_output)))
+ {
+ osfcls(fp);
+ return 5;
+ }
+
+ /* read the next section header */
+ if (osfrb(fp, buf, 4))
+ {
+ osfcls(fp);
+ return 6;
+ }
+
+ /* if it's "SYSI", read the system information string */
+ if (!memcmp(buf, "SYSI", 4))
+ {
+ /* read the length prefix, then the string */
+ if (osfrb(fp, lenbuf, 2)
+ || (len = osrp2(lenbuf)) > sizeof(buf)
+ || osfrb(fp, buf, len))
+ {
+ osfcls(fp);
+ return 7;
+ }
+
+ /* we have a system information block */
+ sysblk = TRUE;
+ }
+ else
+ {
+ /* there's no system information block */
+ sysblk = FALSE;
+ }
+
+ /*
+ * call the OS code, so that it can do any system-dependent
+ * initialization for the new character mapping
+ */
+ os_advise_load_charmap(G_cmap_id, G_cmap_ldesc, sysblk ? buf : "");
+
+ /* read the next section header */
+ if (sysblk && osfrb(fp, buf, 4))
+ {
+ osfcls(fp);
+ return 8;
+ }
+
+ /* see if we have an entity list */
+ if (!memcmp(buf, "ENTY", 4))
+ {
+ /* read the entities */
+ for (;;)
+ {
+ size_t blen;
+ unsigned int cval;
+ char expansion[CMAP_MAX_ENTITY_EXPANSION];
+
+ /* read the next item's length and character value */
+ if (osfrb(fp, buf, 4))
+ {
+ osfcls(fp);
+ return 9;
+ }
+
+ /* decode the values */
+ blen = osrp2(buf);
+ cval = osrp2(buf+2);
+
+ /* if we've reached the zero marker, we're done */
+ if (blen == 0 && cval == 0)
+ break;
+
+ /* read the string */
+ if (blen > CMAP_MAX_ENTITY_EXPANSION
+ || osfrb(fp, expansion, blen))
+ {
+ osfcls(fp);
+ return 10;
+ }
+
+ /* tell the output code about the expansion */
+ tio_set_html_expansion(cval, expansion, blen);
+ }
+ }
+
+ /*
+ * ignore anything else we find - if the file format is updated to
+ * include extra information in the future, and this old code tries
+ * to load an updated file, we'll just ignore the new information,
+ * which should always be placed after the "SYSI" block (if present)
+ * to ensure compatibility with past versions (such as this code)
+ */
+
+ /* no problems - close the file and return success */
+ osfcls(fp);
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/*
+ * Explicitly load a character set from a file. This character set
+ * mapping will override any implicit character set mapping that we read
+ * from a game file. This should be called when the player explicitly
+ * loads a character set (via a command line option or similar action).
+ */
+int cmap_load(char *filename)
+{
+ int err;
+
+ /* try loading the file */
+ if ((err = cmap_load_internal(filename)) != 0)
+ return err;
+
+ /*
+ * note that we've explicitly loaded a character set, if they named
+ * a character set (if not, this simply establishes the default
+ * setting, so we haven't explicitly loaded anything)
+ */
+ if (filename != 0)
+ S_cmap_loaded = TRUE;
+
+ /* success */
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/*
+ * Explicitly override any game character set and use no character set
+ * instead.
+ */
+void cmap_override(void)
+{
+ /* apply the default mapping */
+ cmap_init_default();
+
+ /*
+ * pretend we have a character map loaded, so that we don't try to
+ * load another one if the game specifies a character set
+ */
+ S_cmap_loaded = TRUE;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/*
+ * Set the game's internal character set. This is called when a game is
+ * loaded, and the game specifies a character set.
+ */
+void cmap_set_game_charset(errcxdef *ec,
+ char *internal_id, char *internal_ldesc,
+ char *argv0)
+{
+ char filename[OSFNMAX];
+
+ /*
+ * If a character set is already explicitly loaded, ignore the
+ * game's character set - the player asked us to use a particular
+ * mapping, so ignore what the game wants. (This will probably
+ * result in incorrect display of non-ASCII character values, but
+ * the player is most likely to use this to avoid errors when an
+ * appropriate mapping file for the game is not available. In this
+ * case, the player informs us by setting the option that he or she
+ * knows and accepts that the game will not look exactly right.)
+ */
+ if (S_cmap_loaded)
+ return;
+
+ /*
+ * ask the operating system to name the mapping file -- this routine
+ * will determine, if possible, the current native character set,
+ * and apply a system-specific naming convention to tell us what
+ * mapping file we should open
+ */
+ os_gen_charmap_filename(filename, internal_id, argv0);
+
+ /* try loading the mapping file */
+ if (cmap_load_internal(filename))
+ errsig2(ec, ERR_CHRNOFILE,
+ ERRTSTR, errstr(ec, filename, strlen(filename)),
+ ERRTSTR, errstr(ec, internal_ldesc, strlen(internal_ldesc)));
+
+ /*
+ * We were successful - the game's internal character set is now
+ * mapped to the current native character set. Even though we
+ * loaded an ldesc from the mapping file, forget that and store the
+ * internal ldesc that the game specified. The reason we do this is
+ * that it's possible that the player will dynamically switch native
+ * character sets in the future, at which point we'll need to
+ * re-load the mapping table, which could raise an error if a
+ * mapping file for the new character set isn't available. So, we
+ * may need to provide the same explanation later that we needed to
+ * provide here. Save the game's character set ldesc for that
+ * eventuality, since it describes exactly what the *game* wanted.
+ */
+ strcpy(G_cmap_ldesc, internal_ldesc);
+}
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk