diff options
Diffstat (limited to 'engines/glk/tads/tads2/character_map.cpp')
-rw-r--r-- | engines/glk/tads/tads2/character_map.cpp | 339 |
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 |