aboutsummaryrefslogtreecommitdiff
path: root/engines/glk
diff options
context:
space:
mode:
authorPaul Gilbert2018-11-13 19:47:07 -0800
committerPaul Gilbert2018-12-08 19:05:59 -0800
commit1fb931fbd950324754536ee0b33ed0b91f68c9a2 (patch)
treea00c388b39ece9d327f9ddff4109f8795f8aa3a8 /engines/glk
parent7d670ff157fbc3df45f70f9e7a5b537b3d13152b (diff)
downloadscummvm-rg350-1fb931fbd950324754536ee0b33ed0b91f68c9a2.tar.gz
scummvm-rg350-1fb931fbd950324754536ee0b33ed0b91f68c9a2.tar.bz2
scummvm-rg350-1fb931fbd950324754536ee0b33ed0b91f68c9a2.zip
GLK: Changing gargoyle folder to glk
Diffstat (limited to 'engines/glk')
-rw-r--r--engines/glk/blorb.cpp543
-rw-r--r--engines/glk/blorb.h146
-rw-r--r--engines/glk/conf.cpp254
-rw-r--r--engines/glk/conf.h136
-rw-r--r--engines/glk/configure.engine3
-rw-r--r--engines/glk/detection.cpp323
-rw-r--r--engines/glk/detection_tables.h29
-rw-r--r--engines/glk/events.cpp402
-rw-r--r--engines/glk/events.h291
-rw-r--r--engines/glk/fonts.cpp157
-rw-r--r--engines/glk/fonts.h118
-rw-r--r--engines/glk/frotz/detection.cpp80
-rw-r--r--engines/glk/frotz/detection.h43
-rw-r--r--engines/glk/frotz/detection_tables.cpp85
-rw-r--r--engines/glk/frotz/detection_tables.h85
-rw-r--r--engines/glk/frotz/frotz.cpp98
-rw-r--r--engines/glk/frotz/frotz.h72
-rw-r--r--engines/glk/frotz/frotz_types.h297
-rw-r--r--engines/glk/frotz/glk_interface.cpp498
-rw-r--r--engines/glk/frotz/glk_interface.h195
-rw-r--r--engines/glk/frotz/mem.cpp419
-rw-r--r--engines/glk/frotz/mem.h280
-rw-r--r--engines/glk/frotz/processor.cpp664
-rw-r--r--engines/glk/frotz/processor.h1584
-rw-r--r--engines/glk/frotz/processor_buffer.cpp192
-rw-r--r--engines/glk/frotz/processor_input.cpp223
-rw-r--r--engines/glk/frotz/processor_maths.cpp105
-rw-r--r--engines/glk/frotz/processor_mem.cpp218
-rw-r--r--engines/glk/frotz/processor_objects.cpp732
-rw-r--r--engines/glk/frotz/processor_screen.cpp528
-rw-r--r--engines/glk/frotz/processor_streams.cpp786
-rw-r--r--engines/glk/frotz/processor_table.cpp120
-rw-r--r--engines/glk/frotz/processor_text.cpp886
-rw-r--r--engines/glk/frotz/processor_variables.cpp199
-rw-r--r--engines/glk/frotz/quetzal.cpp485
-rw-r--r--engines/glk/frotz/quetzal.h97
-rw-r--r--engines/glk/gargoyle.cpp120
-rw-r--r--engines/glk/gargoyle.h193
-rw-r--r--engines/glk/glk.cpp1174
-rw-r--r--engines/glk/glk.h292
-rw-r--r--engines/glk/glk_types.h208
-rw-r--r--engines/glk/module.mk50
-rw-r--r--engines/glk/picture.cpp63
-rw-r--r--engines/glk/picture.h73
-rw-r--r--engines/glk/scott/detection.cpp109
-rw-r--r--engines/glk/scott/detection.h43
-rw-r--r--engines/glk/scott/scott.cpp1263
-rw-r--r--engines/glk/scott/scott.h191
-rw-r--r--engines/glk/screen.cpp72
-rw-r--r--engines/glk/screen.h65
-rw-r--r--engines/glk/selection.cpp321
-rw-r--r--engines/glk/selection.h120
-rw-r--r--engines/glk/speech.h49
-rw-r--r--engines/glk/streams.cpp1592
-rw-r--r--engines/glk/streams.h639
-rw-r--r--engines/glk/time.cpp120
-rw-r--r--engines/glk/time.h93
-rw-r--r--engines/glk/unicode.cpp151
-rw-r--r--engines/glk/unicode.h51
-rw-r--r--engines/glk/unicode_gen.cpp11826
-rw-r--r--engines/glk/unicode_gen.h563
-rw-r--r--engines/glk/utils.cpp44
-rw-r--r--engines/glk/utils.h51
-rw-r--r--engines/glk/window_graphics.cpp272
-rw-r--r--engines/glk/window_graphics.h113
-rw-r--r--engines/glk/window_pair.cpp236
-rw-r--r--engines/glk/window_pair.h73
-rw-r--r--engines/glk/window_text_buffer.cpp1636
-rw-r--r--engines/glk/window_text_buffer.h245
-rw-r--r--engines/glk/window_text_grid.cpp654
-rw-r--r--engines/glk/window_text_grid.h195
-rw-r--r--engines/glk/windows.cpp773
-rw-r--r--engines/glk/windows.h541
73 files changed, 35647 insertions, 0 deletions
diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
new file mode 100644
index 0000000000..24340fa2e5
--- /dev/null
+++ b/engines/glk/blorb.cpp
@@ -0,0 +1,543 @@
+/* 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/blorb.h"
+
+namespace Gargoyle {
+
+#define giblorb_Inited_Magic 0xB7012BEDU
+
+/**
+ * Describes one chunk of the Blorb file.
+ */
+struct giblorb_chunkdesc_struct {
+ glui32 type;
+ glui32 len;
+ glui32 startpos; ///< start of chunk header
+ glui32 datpos; ///< start of data (either startpos or startpos+8)
+
+ void *ptr; ///< pointer to malloc'd data, if loaded
+ int auxdatnum; ///< entry in the auxsound/auxpict array; -1 if none. This only applies to chunks that represent resources;
+};
+typedef giblorb_chunkdesc_struct giblorb_chunkdesc_t;
+
+/**
+ * Describes one resource in the Blorb file.
+ */
+struct giblorb_resdesc_struct {
+ glui32 usage;
+ glui32 resnum;
+ glui32 chunknum;
+};
+
+/**
+ * Holds the complete description of an open Blorb file.
+ */
+struct giblorb_map_struct {
+ glui32 inited; ///< holds giblorb_Inited_Magic if the map structure is valid
+ Common::SeekableReadStream *file;
+
+ uint numchunks;
+ giblorb_chunkdesc_t *chunks; ///< list of chunk descriptors
+
+ int numresources;
+ giblorb_resdesc_t *resources; ///< list of resource descriptors
+ giblorb_resdesc_t **ressorted; ///< list of pointers to descriptors in map->resources -- sorted by usage and resource number.
+};
+
+/*--------------------------------------------------------------------------*/
+
+giblorb_err_t Blorb::giblorb_initialize() {
+ _file = nullptr;
+ _map = nullptr;
+ return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_create_map(Common::SeekableReadStream *file, giblorb_map_t **newmap) {
+ giblorb_err_t err;
+ giblorb_map_t *map;
+ glui32 readlen;
+ glui32 nextpos, totallength;
+ giblorb_chunkdesc_t *chunks;
+ int chunks_size, numchunks;
+ char buffer[16];
+
+ *newmap = nullptr;
+
+ if (!_libInited) {
+ err = giblorb_initialize();
+ if (err)
+ return err;
+ _libInited = true;
+ }
+
+ /* First, chew through the file and index the chunks. */
+ file->seek(0);
+
+ readlen = file->read(buffer, 12);
+ if (readlen != 12)
+ return giblorb_err_Read;
+
+ if (READ_BE_INT32(buffer + 0) != giblorb_ID_FORM)
+ return giblorb_err_Format;
+ if (READ_BE_INT32(buffer + 8) != giblorb_ID_IFRS)
+ return giblorb_err_Format;
+
+ totallength = READ_BE_INT32(buffer + 4) + 8;
+ nextpos = 12;
+
+ chunks_size = 8;
+ numchunks = 0;
+ chunks = new giblorb_chunkdesc_t[chunks_size];
+
+ while (nextpos < totallength) {
+ glui32 type, len;
+ int chunum;
+ giblorb_chunkdesc_t *chu;
+
+ file->seek(nextpos);
+
+ readlen = file->read(buffer, 8);
+ if (readlen != 8) {
+ delete[] chunks;
+ return giblorb_err_Read;
+ }
+
+ type = READ_BE_INT32(buffer + 0);
+ len = READ_BE_INT32(buffer + 4);
+
+ if (numchunks >= chunks_size) {
+ chunks_size *= 2;
+ chunks = new giblorb_chunkdesc_t[chunks_size];
+ }
+
+ chunum = numchunks;
+ chu = &(chunks[chunum]);
+ numchunks++;
+
+ chu->type = type;
+ chu->startpos = nextpos;
+ if (type == giblorb_ID_FORM) {
+ chu->datpos = nextpos;
+ chu->len = len + 8;
+ } else {
+ chu->datpos = nextpos + 8;
+ chu->len = len;
+ }
+ chu->ptr = nullptr;
+ chu->auxdatnum = -1;
+
+ nextpos = nextpos + len + 8;
+ if (nextpos & 1)
+ nextpos++;
+
+ if (nextpos > totallength) {
+ delete[] chunks;
+ return giblorb_err_Format;
+ }
+ }
+
+ // The basic IFF structure seems to be ok, and we have a list of chunks.
+ // Now we allocate the map structure itself.
+ map = new giblorb_map_t();
+ if (!map) {
+ delete[] chunks;
+ return giblorb_err_Alloc;
+ }
+
+ map->inited = giblorb_Inited_Magic;
+ map->file = file;
+ map->chunks = chunks;
+ map->numchunks = numchunks;
+ map->resources = nullptr;
+ map->ressorted = nullptr;
+ map->numresources = 0;
+
+ // Now we do everything else involved in loading the Blorb file,
+ // such as building resource lists.
+ err = giblorb_initialize_map(map);
+ if (err) {
+ giblorb_destroy_map(map);
+ return err;
+ }
+
+ *newmap = map;
+ return giblorb_err_None;
+}
+
+
+giblorb_err_t Blorb::giblorb_initialize_map(giblorb_map_t *map) {
+ // It is important that the map structure be kept valid during this function.
+ // If this returns an error, giblorb_destroy_map() will be called.
+ uint ix, jx;
+ giblorb_result_t chunkres;
+ giblorb_err_t err;
+ char *ptr;
+ glui32 len;
+ glui32 numres;
+ int gotindex = false;
+
+ for (ix = 0; ix<map->numchunks; ix++) {
+ giblorb_chunkdesc_t *chu = &map->chunks[ix];
+
+ switch (chu->type) {
+ case giblorb_ID_RIdx:
+ // Resource index chunk: build the resource list and sort it.
+
+ if (gotindex)
+ return giblorb_err_Format; // duplicate index chunk
+ err = giblorb_load_chunk_by_number(map, giblorb_method_Memory, &chunkres, ix);
+ if (err)
+ return err;
+
+ ptr = (char *)chunkres.data.ptr;
+ len = chunkres.length;
+ numres = READ_BE_INT32(ptr + 0);
+
+ if (numres) {
+ uint ix2;
+ giblorb_resdesc_t *resources;
+ giblorb_resdesc_t **ressorted;
+
+ if (len != numres * 12 + 4)
+ return giblorb_err_Format; // bad length field
+
+ resources = new giblorb_resdesc_t[numres];
+ ressorted = new giblorb_resdesc_t *[numres];
+ if (!ressorted || !resources) {
+ delete[] resources;
+ delete[] ressorted;
+ return giblorb_err_Alloc;
+ }
+
+ ix2 = 0;
+ for (jx = 0; jx < numres; jx++) {
+ giblorb_resdesc_t *res = &(resources[jx]);
+ glui32 respos;
+
+ res->usage = READ_BE_INT32(ptr + jx * 12 + 4);
+ res->resnum = READ_BE_INT32(ptr + jx * 12 + 8);
+ respos = READ_BE_INT32(ptr + jx * 12 + 12);
+
+ while (ix2 < map->numchunks
+ && map->chunks[ix2].startpos < respos)
+ ix2++;
+
+ if (ix2 >= map->numchunks
+ || map->chunks[ix2].startpos != respos) {
+ delete[] resources;
+ delete[] ressorted;
+ return giblorb_err_Format; // start pos does not match a real chunk
+ }
+
+ res->chunknum = ix2;
+
+ ressorted[jx] = res;
+ }
+
+ // Sort a resource list (actually a list of pointers to structures in map->resources.)
+ // This makes it easy to find resources by usage and resource number.
+ giblorb_qsort(ressorted, numres);
+
+ map->numresources = numres;
+ map->resources = resources;
+ map->ressorted = ressorted;
+ }
+
+ giblorb_unload_chunk(map, ix);
+ gotindex = true;
+ break;
+ }
+ }
+
+ return giblorb_err_None;
+}
+
+void Blorb::giblorb_qsort(giblorb_resdesc_t **list, size_t len) {
+ int ix, jx, res;
+ giblorb_resdesc_t *tmpptr, *pivot;
+
+ if (len < 6) {
+ // The list is short enough for a bubble-sort.
+ for (jx = len - 1; jx>0; jx--) {
+ for (ix = 0; ix<jx; ix++) {
+ res = sortsplot(list[ix], list[ix + 1]);
+ if (res > 0) {
+ tmpptr = list[ix];
+ list[ix] = list[ix + 1];
+ list[ix + 1] = tmpptr;
+ }
+ }
+ }
+ } else {
+ // Split the list.
+ pivot = list[len / 2];
+ ix = 0;
+ jx = len;
+ for (;;) {
+ while (ix < jx - 1 && sortsplot(list[ix], pivot) < 0)
+ ix++;
+ while (ix < jx - 1 && sortsplot(list[jx - 1], pivot) > 0)
+ jx--;
+ if (ix >= jx - 1)
+ break;
+ tmpptr = list[ix];
+ list[ix] = list[jx - 1];
+ list[jx - 1] = tmpptr;
+ }
+ ix++;
+ // Sort the halves.
+ giblorb_qsort(list + 0, ix);
+ giblorb_qsort(list + ix, len - ix);
+ }
+}
+
+giblorb_resdesc_t *Blorb::giblorb_bsearch(giblorb_resdesc_t *sample,
+ giblorb_resdesc_t **list, int len) {
+ int top, bot, val, res;
+
+ bot = 0;
+ top = len;
+
+ while (bot < top) {
+ val = (top + bot) / 2;
+ res = sortsplot(list[val], sample);
+ if (res == 0)
+ return list[val];
+ if (res < 0) {
+ bot = val + 1;
+ } else {
+ top = val;
+ }
+ }
+
+ return nullptr;
+}
+
+int Blorb::sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2) {
+ if (v1->usage < v2->usage)
+ return -1;
+ if (v1->usage > v2->usage)
+ return 1;
+ if (v1->resnum < v2->resnum)
+ return -1;
+ if (v1->resnum > v2->resnum)
+ return 1;
+ return 0;
+}
+
+giblorb_err_t Blorb::giblorb_destroy_map(giblorb_map_t *map) {
+ if (!map || !map->chunks || map->inited != giblorb_Inited_Magic)
+ return giblorb_err_NotAMap;
+
+ for (uint ix = 0; ix<map->numchunks; ix++) {
+ giblorb_chunkdesc_t *chu = &(map->chunks[ix]);
+ if (chu->ptr) {
+ delete chu->ptr;
+ chu->ptr = nullptr;
+ }
+ }
+
+ if (map->chunks) {
+ delete[] map->chunks;
+ map->chunks = nullptr;
+ }
+
+ map->numchunks = 0;
+
+ if (map->resources) {
+ delete[] map->resources;
+ map->resources = nullptr;
+ }
+
+ if (map->ressorted) {
+ delete[] map->ressorted;
+ map->ressorted = nullptr;
+ }
+
+ map->numresources = 0;
+ map->file = nullptr;
+ map->inited = 0;
+
+ delete map;
+
+ return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_load_chunk_by_type(giblorb_map_t *map,
+ glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count) {
+ uint ix;
+
+ for (ix = 0; ix < map->numchunks; ix++) {
+ if (map->chunks[ix].type == chunktype) {
+ if (count == 0)
+ break;
+ count--;
+ }
+ }
+
+ if (ix >= map->numchunks) {
+ return giblorb_err_NotFound;
+ }
+
+ return giblorb_load_chunk_by_number(map, method, res, ix);
+}
+
+giblorb_err_t Blorb::giblorb_load_chunk_by_number(giblorb_map_t *map,
+ glui32 method, giblorb_result_t *res, glui32 chunknum) {
+ giblorb_chunkdesc_t *chu;
+
+ if (chunknum < 0 || chunknum >= map->numchunks)
+ return giblorb_err_NotFound;
+
+ chu = &(map->chunks[chunknum]);
+
+ switch (method) {
+ case giblorb_method_DontLoad:
+ // do nothing
+ break;
+
+ case giblorb_method_FilePos:
+ res->data.startpos = chu->datpos;
+ break;
+
+ case giblorb_method_Memory:
+ if (!chu->ptr) {
+ glui32 readlen;
+ byte *dat = new byte[chu->len];
+
+ if (!dat)
+ return giblorb_err_Alloc;
+
+ map->file->seek(chu->datpos);
+
+ readlen = map->file->read(dat, chu->len);
+ if (readlen != chu->len)
+ return giblorb_err_Read;
+
+ chu->ptr = dat;
+ }
+
+ res->data.ptr = chu->ptr;
+ break;
+ }
+
+ res->chunknum = chunknum;
+ res->length = chu->len;
+ res->chunktype = chu->type;
+
+ return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum) {
+ giblorb_chunkdesc_t *chu;
+
+ if (chunknum < 0 || chunknum >= map->numchunks)
+ return giblorb_err_NotFound;
+
+ chu = &(map->chunks[chunknum]);
+
+ if (chu->ptr) {
+ delete chu->ptr;
+ chu->ptr = nullptr;
+ }
+
+ return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_load_resource(giblorb_map_t *map, glui32 method,
+ giblorb_result_t *res, glui32 usage, glui32 resnum) {
+ giblorb_resdesc_t sample;
+ giblorb_resdesc_t *found;
+
+ sample.usage = usage;
+ sample.resnum = resnum;
+
+ found = giblorb_bsearch(&sample, map->ressorted, map->numresources);
+
+ if (!found)
+ return giblorb_err_NotFound;
+
+ return giblorb_load_chunk_by_number(map, method, res, found->chunknum);
+}
+
+giblorb_err_t Blorb::giblorb_count_resources(giblorb_map_t *map,
+ glui32 usage, glui32 *num, glui32 *min, glui32 *max) {
+ int ix;
+ int count;
+ glui32 val;
+ glui32 minval, maxval;
+
+ count = 0;
+ minval = 0;
+ maxval = 0;
+
+ for (ix = 0; ix<map->numresources; ix++) {
+ if (map->resources[ix].usage == usage) {
+ val = map->resources[ix].resnum;
+ if (count == 0) {
+ count++;
+ minval = val;
+ maxval = val;
+ }
+ else {
+ count++;
+ if (val < minval)
+ minval = val;
+ if (val > maxval)
+ maxval = val;
+ }
+ }
+ }
+
+ if (num)
+ *num = count;
+ if (min)
+ *min = minval;
+ if (max)
+ *max = maxval;
+
+ return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_set_resource_map(Common::SeekableReadStream *file) {
+ giblorb_err_t err;
+
+ err = giblorb_create_map(file, &_map);
+ if (err) {
+ _map = nullptr;
+ return err;
+ }
+
+ _file = file;
+ return giblorb_err_None;
+}
+
+giblorb_map_t *Blorb::giblorb_get_resource_map(void) {
+ return _map;
+}
+
+bool Blorb::giblorb_is_resource_map(void) const {
+ return _map != nullptr;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/blorb.h b/engines/glk/blorb.h
new file mode 100644
index 0000000000..75f811ccfd
--- /dev/null
+++ b/engines/glk/blorb.h
@@ -0,0 +1,146 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_BLORB_H
+#define GLK_BLORB_H
+
+#include "glk/glk_types.h"
+#include "glk/streams.h"
+
+namespace Gargoyle {
+
+
+/**
+ * Error type
+ */
+typedef glui32 giblorb_err_t;
+
+/**
+ * Error codes
+ */
+enum giblorbError {
+ giblorb_err_None = 0,
+ giblorb_err_CompileTime = 1,
+ giblorb_err_Alloc = 2,
+ giblorb_err_Read = 3,
+ giblorb_err_NotAMap = 4,
+ giblorb_err_Format = 5,
+ giblorb_err_NotFound = 6
+};
+
+/**
+ * Methods for loading a chunk
+ */
+enum giblorbMethod {
+ giblorb_method_DontLoad = 0,
+ giblorb_method_Memory = 1,
+ giblorb_method_FilePos = 2
+};
+
+enum {
+ giblorb_ID_Snd = MKTAG('S', 'n', 'd', ' '),
+ giblorb_ID_Exec = MKTAG('E', 'x', 'e', 'c'),
+ giblorb_ID_Pict = MKTAG('P', 'i', 'c', 't'),
+ giblorb_ID_Copyright = MKTAG('(', 'c', ')', ' '),
+ giblorb_ID_AUTH = MKTAG('A', 'U', 'T', 'H'),
+ giblorb_ID_ANNO = MKTAG('A', 'N', 'N', 'O')
+};
+
+
+enum {
+ giblorb_ID_MOD = MKTAG('M', 'O', 'D', ' '),
+ giblorb_ID_FORM = MKTAG('F', 'O', 'R', 'M'),
+ giblorb_ID_IFRS = MKTAG('I', 'F', 'R', 'S'),
+ giblorb_ID_RIdx = MKTAG('R', 'I', 'd', 'x'),
+ giblorb_ID_OGG = MKTAG('O', 'G', 'G', 'V'),
+
+ // non-standard types
+ giblorb_ID_MIDI = MKTAG('M', 'I', 'D', 'I'),
+ giblorb_ID_MP3 = MKTAG('M', 'P', '3', ' '),
+ giblorb_ID_WAVE = MKTAG('W', 'A', 'V', 'E')
+};
+
+/**
+ * Holds the complete description of an open Blorb file.
+ * This type is opaque for normal interpreter use.
+ */
+typedef struct giblorb_map_struct giblorb_map_t;
+
+/* giblorb_result_t: Result when you try to load a chunk. */
+typedef struct giblorb_result_struct {
+ glui32 chunknum; // The chunk number (for use in giblorb_unload_chunk(), etc.)
+ union {
+ void *ptr; ///< A pointer to the data (if you used giblorb_method_Memory)
+ glui32 startpos; ///< The position in the file (if you used giblorb_method_FilePos)
+ } data;
+
+ glui32 length; ///< The length of the data
+ glui32 chunktype; ///< The type of the chunk.
+} giblorb_result_t;
+
+typedef struct giblorb_resdesc_struct giblorb_resdesc_t;
+
+class Blorb {
+private:
+ bool _libInited;
+ Common::SeekableReadStream *_file;
+ giblorb_map_t *_map;
+private:
+ /**
+ * Initializes Blorb
+ */
+ giblorb_err_t giblorb_initialize();
+
+ giblorb_err_t giblorb_initialize_map(giblorb_map_t *map);
+ void giblorb_qsort(giblorb_resdesc_t **list, size_t len);
+ giblorb_resdesc_t *giblorb_bsearch(giblorb_resdesc_t *sample,
+ giblorb_resdesc_t **list, int len);
+ int sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2);
+public:
+ /**
+ * Constructor
+ */
+ Blorb() : _libInited(false), _file(nullptr), _map(nullptr) {}
+
+ giblorb_err_t giblorb_set_resource_map(Common::SeekableReadStream *file);
+ giblorb_map_t *giblorb_get_resource_map(void);
+ bool giblorb_is_resource_map(void) const;
+
+
+ giblorb_err_t giblorb_create_map(Common::SeekableReadStream *file, giblorb_map_t **newmap);
+ giblorb_err_t giblorb_destroy_map(giblorb_map_t *map);
+
+ giblorb_err_t giblorb_load_chunk_by_type(giblorb_map_t *map,
+ glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count);
+ giblorb_err_t giblorb_load_chunk_by_number(giblorb_map_t *map,
+ glui32 method, giblorb_result_t *res, glui32 chunknum);
+ giblorb_err_t giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum);
+
+ giblorb_err_t giblorb_load_resource(giblorb_map_t *map, glui32 method,
+ giblorb_result_t *res, glui32 usage, glui32 resnum);
+ giblorb_err_t giblorb_count_resources(giblorb_map_t *map,
+ glui32 usage, glui32 *num, glui32 *min, glui32 *max);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/conf.cpp b/engines/glk/conf.cpp
new file mode 100644
index 0000000000..45d6168a8f
--- /dev/null
+++ b/engines/glk/conf.cpp
@@ -0,0 +1,254 @@
+/* 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/conf.h"
+#include "glk/fonts.h"
+#include "glk/utils.h"
+#include "glk/windows.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+
+namespace Gargoyle {
+
+const byte WHITE[3] = { 0xff, 0xff, 0xff };
+const byte BLUE[3] = { 0x00, 0x00, 0x60 };
+const byte SCROLL_BG[3] = { 0xb0, 0xb0, 0xb0 };
+const byte SCROLL_FG[3] = { 0x80, 0x80, 0x80 };
+
+WindowStyle T_STYLES[style_NUMSTYLES] = {
+ { PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Normal
+ { PROPI, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Emphasized
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Preformatted
+ { PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Header
+ { PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Subheader
+ { PROPZ, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Alert
+ { PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Note
+ { PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< BlockQuote
+ { PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x60, 0x00 }, 0 }, ///< Input
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< User1
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< User2
+};
+
+WindowStyle G_STYLES[style_NUMSTYLES] = {
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Normal
+ { MONOI, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Emphasized
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Preformatted
+ { MONOB, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Header
+ { MONOB, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Subheader
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Alert
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Note
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< BlockQuote
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Input
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< User1
+ { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< User2
+};
+
+Conf *g_conf;
+
+Conf::Conf() {
+ g_conf = this;
+ _imageW = g_system->getWidth();
+ _imageH = g_system->getHeight();
+ _cellW = _cellH = 8;
+ _leading = 0;
+ _baseLine = 0;
+
+ get("moreprompt", _morePrompt, "\207 more \207");
+ get("morecolor", _moreColor);
+ get("morecolor", _moreSave);
+ get("morefont", _moreFont, PROPB);
+ get("morealign", _moreAlign);
+ get("monoaspect", _monoAspect, 1.0);
+ get("propaspect", _propAspect, 1.0);
+ get("monosize", _monoSize, 11);
+ get("monor", _monoR);
+ get("monob", _monoR);
+ get("monoi", _monoI);
+ get("monoz", _monoZ);
+ get("monofont", _monoFont, "Liberation Mono");
+ get("propsize", _propSize, 12);
+ get("propr", _propR);
+ get("propb", _propR);
+ get("propi", _propI);
+ get("propz", _propZ);
+ get("propfont", _propFont, "Linux Libertine O");
+ get("rows", _rows, 25);
+ get("cols", _cols, 60);
+
+ if (ConfMan.hasKey("leading"))
+ _leading = atof(ConfMan.get("leading").c_str()) + 0.5;
+ if (ConfMan.hasKey("baseline"))
+ _baseLine = atof(ConfMan.get("baseline").c_str()) + 0.5;
+
+ if (ConfMan.hasKey("minrows"))
+ _rows = MAX(_rows, strToInt(ConfMan.get("minrows").c_str()));
+ if (ConfMan.hasKey("maxrows"))
+ _rows = MIN(_rows, strToInt(ConfMan.get("maxrows").c_str()));
+ if (ConfMan.hasKey("mincols"))
+ _cols = MAX(_cols, strToInt(ConfMan.get("mincols").c_str()));
+ if (ConfMan.hasKey("maxcols"))
+ _cols = MIN(_cols, strToInt(ConfMan.get("maxcols").c_str()));
+
+ get("lockrows", _lockRows);
+ get("lockcols", _lockCols);
+ get("wmarginx", _wMarginX, 15);
+ get("wmarginy", _wMarginY, 15);
+ _wMarginSaveX = _wMarginX;
+ _wMarginSaveY = _wMarginY;
+
+ get("wpaddingx", _wPaddingX);
+ get("wpaddingy", _wPaddingY);
+ get("wborderx", _wBorderX);
+ get("wbordery", _wBorderY);
+ get("tmarginx", _tMarginX, 7);
+ get("tmarginy", _tMarginY, 7);
+ get("gamma", _gamma, 1.0);
+
+ get("caretcolor", _caretColor);
+ get("caretcolor", _caretSave);
+ get("linkcolor", _linkColor, BLUE);
+ get("linkcolor", _linkSave, BLUE);
+ get("bordercolor", _borderColor);
+ get("bordercolor", _borderSave);
+ get("windowcolor", _windowColor, WHITE);
+ get("windowcolor", _windowSave, WHITE);
+ get("lcd", _lcd, 1);
+ get("caretshape", _caretShape, 2);
+
+ _linkStyle = ConfMan.hasKey("linkstyle") && !strToInt(ConfMan.get("linkstyle").c_str()) ? 0 : 1;
+
+ get("scrollwidth", _scrollWidth);
+ get("scrollbg", _scrollBg, SCROLL_BG);
+ get("scrollfg", _scrollFg, SCROLL_FG);
+ get("justify", _justify);
+ get("quotes", _quotes, 1);
+ get("dashes", _dashes, 1);
+ get("spaces", _spaces);
+ get("caps", _caps);
+ get("graphics", _graphics, true);
+ get("sound", _sound, true);
+ get("speak", _speak);
+ get("speak_input", _speakInput);
+ get("speak_language", _speakLanguage);
+ get("stylehint", _styleHint, 1);
+ get("safeclicks", _safeClicks);
+
+ Common::copy(T_STYLES, T_STYLES + style_NUMSTYLES, _tStyles);
+ Common::copy(G_STYLES, G_STYLES + style_NUMSTYLES, _gStyles);
+
+ char buffer[255];
+ const char *const TG_COLOR[2] = { "tcolor_%d", "gcolor_%d" };
+ for (int tg = 0; tg < 2; ++tg) {
+ for (int style = 0; style <= 10; ++style) {
+ Common::String key = Common::String::format(TG_COLOR[tg], style);
+ if (!ConfMan.hasKey(key))
+ continue;
+
+ strncpy(buffer, ConfMan.get(key).c_str(), 254);
+ buffer[255] = '\0';
+ char *fg = strtok(buffer, "\r\n\t ");
+ char *bg = strtok(nullptr, "\r\n\t ");
+
+ if (tg == 0) {
+ parseColor(fg, _tStyles[style].fg);
+ parseColor(bg, _tStyles[style].bg);
+ } else {
+ parseColor(fg, _gStyles[style].fg);
+ parseColor(bg, _gStyles[style].bg);
+ }
+ }
+ }
+
+ const char *const TG_FONT[2] = { "tfont_%d", "gfont_%d" };
+ for (int tg = 0; tg < 2; ++tg) {
+ for (int style = 0; style <= 10; ++style) {
+ Common::String key = Common::String::format(TG_FONT[tg], style);
+ if (!ConfMan.hasKey(key))
+ continue;
+
+ strncpy(buffer, ConfMan.get(key).c_str(), 254);
+ buffer[255] = '\0';
+ char *font = strtok(buffer, "\r\n\t ");
+
+ if (tg == 0)
+ _tStyles[style].font = Fonts::getId(font);
+ else
+ _gStyles[style].font = Fonts::getId(font);
+ }
+ }
+
+ Common::copy(_tStyles, _tStyles + style_NUMSTYLES, _tStylesDefault);
+ Common::copy(_gStyles, _gStyles + style_NUMSTYLES, _gStylesDefault);
+}
+
+void Conf::get(const Common::String &key, Common::String &field, const char *defaultVal) {
+ field = ConfMan.hasKey(key) ? ConfMan.get(key) : defaultVal;
+ field.trim();
+}
+
+void Conf::get(const Common::String &key, byte *color, const byte *defaultColor) {
+ if (ConfMan.hasKey(key)) {
+ parseColor(ConfMan.get(key), color);
+ } else if (defaultColor) {
+ Common::copy(defaultColor, defaultColor + 3, color);
+ } else {
+ Common::fill(color, color + 3, 0);
+ }
+}
+
+void Conf::get(const Common::String &key, int &field, int defaultVal) {
+ field = ConfMan.hasKey(key) ? strToInt(ConfMan.get(key).c_str()) : defaultVal;
+}
+
+void Conf::get(const Common::String &key, bool &field, bool defaultVal) {
+ field = ConfMan.hasKey(key) ? strToInt(ConfMan.get(key).c_str()) != 0 : defaultVal;
+}
+
+void Conf::get(const Common::String &key, FACES &field, FACES defaultFont) {
+ field = ConfMan.hasKey(key) ? Fonts::getId(ConfMan.get(key)) : defaultFont;
+}
+
+void Conf::get(const Common::String &key, double &field, double defaultVal) {
+ field = ConfMan.hasKey(key) ? atof(ConfMan.get(key).c_str()) : defaultVal;
+}
+
+void Conf::parseColor(const Common::String &str, byte *color) {
+ char r[3], g[3], b[3];
+
+ if (str.size() == 6) {
+ r[0] = str[0];
+ r[1] = str[1];
+ r[2] = 0;
+ g[0] = str[2];
+ g[1] = str[3];
+ g[2] = 0;
+ b[0] = str[4];
+ b[1] = str[5];
+ b[2] = 0;
+
+ color[0] = strtol(r, nullptr, 16);
+ color[1] = strtol(g, nullptr, 16);
+ color[2] = strtol(b, nullptr, 16);
+ }
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/conf.h b/engines/glk/conf.h
new file mode 100644
index 0000000000..6c9dbf9f56
--- /dev/null
+++ b/engines/glk/conf.h
@@ -0,0 +1,136 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_CONF_H
+#define GLK_CONF_H
+
+#include "glk/glk_types.h"
+#include "glk/fonts.h"
+#include "glk/windows.h"
+
+namespace Gargoyle {
+
+class Conf {
+private:
+ /**
+ * Get a string
+ */
+ void get(const Common::String &key, Common::String &field, const char *defaultVal = nullptr);
+
+ /**
+ * Get a color
+ */
+ void get(const Common::String &key, byte *color, const byte *defaultColor = nullptr);
+
+ /**
+ * Get a font name into a font Id
+ */
+ void get(const Common::String &key, FACES &field, FACES defaultFont);
+
+ /**
+ * Get a numeric value
+ */
+ void get(const Common::String &key, int &field, int defaultVal = 0);
+
+ /**
+ * Get a numeric value
+ */
+ void get(const Common::String &key, bool &field, bool defaultVal = false);
+
+ /**
+ * Get a double
+ */
+ void get(const Common::String &key, double &field, double defaultVal = 0.0);
+
+ /**
+ * Parse a color
+ */
+ void parseColor(const Common::String &str, byte *color);
+public:
+ Common::String _morePrompt;
+ byte _moreColor[3], _moreSave[3];
+ FACES _moreFont;
+ int _moreAlign;
+ double _monoAspect;
+ double _propAspect;
+ double _monoSize;
+ Common::String _monoR;
+ Common::String _monoB;
+ Common::String _monoI;
+ Common::String _monoZ;
+ Common::String _monoFont;
+ double _propSize;
+ Common::String _propR;
+ Common::String _propB;
+ Common::String _propI;
+ Common::String _propZ;
+ Common::String _propFont;
+ int _leading;
+ int _baseLine;
+ int _cols, _rows;
+ int _lockCols, _lockRows;
+ int _wMarginX, _wMarginY;
+ int _wMarginSaveX, _wMarginSaveY;
+ int _wPaddingX, _wPaddingY;
+ int _wBorderX, _wBorderY;
+ int _tMarginX, _tMarginY;
+ double _gamma;
+ byte _caretColor[3], _caretSave[3];
+ byte _linkColor[3], _linkSave[3];
+ byte _borderColor[3], _borderSave[3];
+ byte _windowColor[3], _windowSave[3];
+ int _lcd;
+ int _caretShape;
+ int _linkStyle;
+ int _scrollWidth;
+ byte _scrollBg[3], _scrollFg[3];
+ int _justify;
+ int _quotes;
+ int _dashes;
+ int _spaces;
+ int _caps;
+ bool _graphics;
+ bool _sound;
+ bool _speak;
+ bool _speakInput;
+ Common::String _speakLanguage;
+ int _styleHint;
+ bool _safeClicks;
+ WindowStyle _tStyles[style_NUMSTYLES];
+ WindowStyle _gStyles[style_NUMSTYLES];
+ WindowStyle _tStylesDefault[style_NUMSTYLES];
+ WindowStyle _gStylesDefault[style_NUMSTYLES];
+
+ int _imageW, _imageH;
+ int _cellW, _cellH;
+public:
+ /**
+ * Constructor
+ */
+ Conf();
+};
+
+extern Conf *g_conf;
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/configure.engine b/engines/glk/configure.engine
new file mode 100644
index 0000000000..7f4efd6ca6
--- /dev/null
+++ b/engines/glk/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine glk "ScummGlk Interactive Fiction games" no "" "" "freetype2"
diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
new file mode 100644
index 0000000000..9266825fa2
--- /dev/null
+++ b/engines/glk/detection.cpp
@@ -0,0 +1,323 @@
+/* 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/gargoyle.h"
+
+#include "base/plugins.h"
+#include "common/md5.h"
+#include "common/memstream.h"
+#include "common/savefile.h"
+#include "common/str-array.h"
+#include "common/system.h"
+#include "engines/advancedDetector.h"
+#include "graphics/colormasks.h"
+#include "graphics/surface.h"
+
+#define MAX_SAVES 99
+
+namespace Gargoyle {
+
+struct GargoyleGameDescription {
+ ADGameDescription _desc;
+ Common::String _filename;
+ InterpreterType _interpType;
+ Common::String _md5;
+};
+
+const Common::String &GargoyleEngine::getFilename() const {
+ return _gameDescription->_filename;
+}
+uint32 GargoyleEngine::getFeatures() const {
+ return _gameDescription->_desc.flags;
+}
+
+bool GargoyleEngine::isDemo() const {
+ return (bool)(_gameDescription->_desc.flags & ADGF_DEMO);
+}
+
+Common::Language GargoyleEngine::getLanguage() const {
+ return _gameDescription->_desc.language;
+}
+
+InterpreterType GargoyleEngine::getInterpreterType() const {
+ return _gameDescription->_interpType;
+}
+
+const Common::String &GargoyleEngine::getGameMD5() const {
+ return _gameDescription->_md5;
+}
+
+} // End of namespace Gargoyle
+
+#include "glk/frotz/detection_tables.h"
+#define ZCODE(ID, NAME) { ID, Gargoyle::Frotz::NAME##_DESC }
+
+static const PlainGameDescriptor gargoyleGames[] = {
+ {"zcode", "Zcode Games" },
+ {"scottadams", "Scott Adams Games"},
+
+ // Infocom/Z-code games
+ ZCODE("amfv", AMFV),
+ ZCODE("arthur", ARTHUR),
+ ZCODE("ballyhoo", BALLYHOO),
+ ZCODE("beyondzork", BEYONDZORK),
+ ZCODE("borderzone", BORDERZONE),
+ ZCODE("bureaucracy", BUREAUCRACY),
+ ZCODE("cutthroats", CUTTHROATS),
+ ZCODE("deadline", DEADLINE),
+ ZCODE("enchanter", ENCHANTER),
+ ZCODE("hhgttg", HHGTTG),
+ ZCODE("hijinx", HIJINX),
+ ZCODE("infidel", INFIDEL),
+ ZCODE("journey", JOURNEY),
+ ZCODE("lgop", LGOP),
+ ZCODE("lgop2", LGOP2),
+ ZCODE("lurking", LURKING),
+ ZCODE("minizork1", MINIZORK1),
+ ZCODE("moonmist", MOONMIST),
+ ZCODE("nordbert", NORDBERT),
+ ZCODE("planetfall", PLANETFALL),
+ ZCODE("plundered", PLUNDERED),
+ ZCODE("sampler1", SAMPLER1),
+ ZCODE("sampler2", SAMPLER2),
+ ZCODE("seastalker", SEASTALKER),
+ ZCODE("sherlockriddle", SHERLOCKRIDDLE),
+ ZCODE("shogun", SHOGUN),
+ ZCODE("sorcerer", SORCERER),
+ ZCODE("spellbreaker", SPELLBREAKER),
+ ZCODE("starcross", STARCROSS),
+ ZCODE("stationfall", STATIONFALL),
+ ZCODE("suspect", SUSPECT),
+ ZCODE("suspended", SUSPENDED),
+ ZCODE("trinity", TRINITY),
+ ZCODE("wishbringer", WISHBRINGER),
+ ZCODE("witness", WITNESS),
+ ZCODE("zork0", ZORK0),
+ ZCODE("zork1", ZORK1),
+ ZCODE("zork2", ZORK2),
+ ZCODE("zork3", ZORK3),
+ ZCODE("ztuu", ZTUU),
+
+ // Scott Adams games
+ { "adventureland", "Adventureland" },
+ { "pirateadventure", "Pirate Adventure" },
+ { "missionimpossible", "Mission Impossible" },
+ { "voodoocastle", "Voodoo Castle" },
+ { "thecount", "The Count" },
+ { "strangeodyssey", "Strange Odyssey" },
+ { "mysteryfunhouse", "Mystery Fun House" },
+ { "pyramidofdoom", "Pyramid Of Doom" },
+ { "ghosttown", "Ghost Town" },
+ { "savageisland1", "Savage Island, Part 1" },
+ { "savageisland2", "Savage Island, Part 2" },
+ { "goldenvoyage", "The Golden Voyage" },
+ { "adventure13", "Adventure 13" },
+ { "adventure14", "Adventure 14" },
+ { "buckaroobonzai", "Buckaroo Banzai" },
+ { "marveladventure", "Marvel Adventure #1" },
+ { "scottsampler", "Adventure International's Mini-Adventure Sampler" },
+ {0, 0}
+};
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "glk/detection_tables.h"
+#include "glk/frotz/detection.h"
+#include "glk/frotz/frotz.h"
+#include "glk/scott/detection.h"
+#include "glk/scott/scott.h"
+
+class GargoyleMetaEngine : public AdvancedMetaEngine {
+public:
+ GargoyleMetaEngine() : AdvancedMetaEngine(Gargoyle::gameDescriptions, sizeof(Gargoyle::GargoyleGameDescription), gargoyleGames) {
+ _maxScanDepth = 3;
+ }
+
+ virtual const char *getName() const {
+ return "ScummGlk Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "ScummGlk Engine (c) 2018";
+ }
+
+ virtual bool hasFeature(MetaEngineFeature f) const override;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
+ virtual SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual void removeSaveState(const char *target, int slot) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+
+ virtual DetectedGames detectGames(const Common::FSList &fslist) const override;
+
+ virtual ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const override;
+};
+
+bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportCreationDate) ||
+ (f == kSavesSupportPlayTime) ||
+ (f == kSimpleSavesNames);
+}
+
+bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const Gargoyle::GargoyleGameDescription *gd = (const Gargoyle::GargoyleGameDescription *)desc;
+
+ switch (gd->_interpType) {
+ case Gargoyle::INTERPRETER_FROTZ:
+ *engine = new Gargoyle::Frotz::Frotz(syst, gd);
+ break;
+ case Gargoyle::INTERPRETER_SCOTT:
+ *engine = new Gargoyle::Scott::Scott(syst, gd);
+ break;
+ default:
+ error("Unknown interpreter");
+ }
+
+ return gd != 0;
+}
+
+SaveStateList GargoyleMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String saveDesc;
+ Common::String pattern = Common::String::format("%s.0##", target);
+ Gargoyle::SavegameHeader header;
+
+ filenames = saveFileMan->listSavefiles(pattern);
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ const char *ext = strrchr(file->c_str(), '.');
+ int slot = ext ? atoi(ext + 1) : -1;
+
+ if (slot >= 0 && slot <= MAX_SAVES) {
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file);
+
+ if (in) {
+ if (Gargoyle::FileStream::readSavegameHeader(in, header))
+ saveList.push_back(SaveStateDescriptor(slot, header._saveName));
+
+ delete in;
+ }
+ }
+ }
+
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+ return saveList;
+}
+
+int GargoyleMetaEngine::getMaximumSaveSlot() const {
+ return MAX_SAVES;
+}
+
+void GargoyleMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String filename = Common::String::format("%s.%03d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(filename);
+}
+
+SaveStateDescriptor GargoyleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String filename = Common::String::format("%s.%03d", target, slot);
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
+
+ if (in) {
+ Gargoyle::SavegameHeader header;
+ if (Gargoyle::FileStream::readSavegameHeader(in, header)) {
+ // Create the return descriptor
+ SaveStateDescriptor desc(slot, header._saveName);
+ desc.setSaveDate(header._year, header._month, header._day);
+ desc.setSaveTime(header._hour, header._minute);
+ desc.setPlayTime(header._totalFrames * GAME_FRAME_TIME);
+
+ delete in;
+ return desc;
+ }
+ }
+
+ return SaveStateDescriptor();
+}
+
+DetectedGames GargoyleMetaEngine::detectGames(const Common::FSList &fslist) const {
+ DetectedGames detectedGames;
+ Gargoyle::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
+ Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
+
+ return detectedGames;
+}
+
+static Gargoyle::GargoyleGameDescription gameDescription;
+
+ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
+ static char gameId[100];
+ strcpy(gameId, ConfMan.get("gameid").c_str());
+ Common::String filename = ConfMan.get("filename");
+
+ Common::FSList fslist;
+ DetectedGames detectedGames;
+ fslist.push_back(parent.getChild(filename));
+ ADDetectedGames results;
+ Common::File f;
+
+ // Check each sub-engine for any detected games
+ if (Gargoyle::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames))
+ gameDescription._interpType = Gargoyle::INTERPRETER_FROTZ;
+ else if (Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames))
+ gameDescription._interpType = Gargoyle::INTERPRETER_SCOTT;
+ else
+ // No match found, so return no results
+ return results;
+
+ // Set up the game description and return it
+ if (f.open(parent.getChild(filename))) {
+ DetectedGame gd = detectedGames.front();
+
+ gameDescription._desc.gameId = gameId;
+ gameDescription._desc.language = gd.language;
+ gameDescription._desc.platform = gd.platform;
+ gameDescription._desc.guiOptions = GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES);
+ gameDescription._filename = filename;
+ gameDescription._md5 = Common::computeStreamMD5AsString(f, 5000);
+
+ ADDetectedGame dg((ADGameDescription *)&gameDescription);
+ results.push_back(dg);
+ }
+
+ return results;
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(GLK)
+REGISTER_PLUGIN_DYNAMIC(GLK, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(GLK, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
+#endif
diff --git a/engines/glk/detection_tables.h b/engines/glk/detection_tables.h
new file mode 100644
index 0000000000..30491f6d9b
--- /dev/null
+++ b/engines/glk/detection_tables.h
@@ -0,0 +1,29 @@
+/* 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.
+ *
+ */
+
+namespace Gargoyle {
+
+static const GargoyleGameDescription gameDescriptions[] = {
+ { AD_TABLE_END_MARKER, "", (InterpreterType)0, "" }
+};
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/events.cpp b/engines/glk/events.cpp
new file mode 100644
index 0000000000..74a108e074
--- /dev/null
+++ b/engines/glk/events.cpp
@@ -0,0 +1,402 @@
+/* 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/events.h"
+#include "glk/conf.h"
+#include "glk/gargoyle.h"
+#include "glk/screen.h"
+#include "glk/selection.h"
+#include "glk/windows.h"
+#include "graphics/cursorman.h"
+
+namespace Gargoyle {
+
+const byte ARROW[] = {
+ // byte 1: number of skipped pixels
+ // byte 2: number of plotted pixels
+ // then, pixels
+ 0, 1, 5,
+ 0, 2, 5, 5,
+ 0, 3, 5, 0xF7, 5,
+ 0, 3, 5, 0xF7, 5,
+ 0, 4, 5, 0xF7, 0xF7, 5,
+ 0, 4, 5, 0xF7, 0xF7, 5,
+ 0, 5, 5, 0xF7, 0xF7, 0xF7, 5,
+ 0, 5, 5, 0xF7, 0xF7, 0xF7, 5,
+ 0, 6, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+ 0, 6, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+ 0, 7, 5, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+ 0, 6, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+ 0, 5, 5, 0xF7, 0xF7, 0xF7, 5,
+ 2, 3, 5, 0xF7, 5,
+ 3, 3, 5, 0xF7, 5,
+ 3, 3, 5, 0xF7, 5,
+ 4, 2, 5, 5
+};
+
+Events::Events() : _forceClick(false), _currentEvent(nullptr), _cursorId(CURSOR_NONE),
+ _timerMilli(0), _timerTimeExpiry(0), _priorFrameTime(0), _frameCounter(0) {
+ initializeCursors();
+}
+
+Events::~Events() {
+ for (int idx = 1; idx < 3; ++idx)
+ _cursors[idx].free();
+}
+
+void Events::initializeCursors() {
+ const Graphics::PixelFormat format = g_system->getScreenFormat();
+ const int WHITE = format.RGBToColor(0xff, 0xff, 0xff);
+ const int BLACK = 0;
+ const int TRANSPARENT = format.RGBToColor(0x80, 0x80, 0x80);
+
+ // Setup arrow cursor
+ Surface &arr = _cursors[CURSOR_ARROW];
+ arr.create(8, 16, g_system->getScreenFormat());
+ arr.fillRect(Common::Rect(0, 0, 8, 16), TRANSPARENT);
+
+ const byte *p = ARROW;
+ for (int y = 0; y < 16; ++y) {
+ int offset = *p++;
+ int len = *p++;
+
+ for (int x = offset; x < (offset + len); ++x, ++p) {
+ arr.hLine(x, y, x, (*p == 0xf7) ? WHITE : BLACK);
+ }
+ }
+
+ // Setup selection cusor sized to the vertical line size
+ Surface &sel = _cursors[CURSOR_IBEAM];
+ sel.create(5, g_conf->_leading, g_system->getScreenFormat());
+ sel.fillRect(Common::Rect(0, 0, sel.w, sel.h), TRANSPARENT);
+ sel.hLine(0, 0, 4, 0);
+ sel.hLine(0, sel.h - 1, 4, 0);
+ sel.vLine(2, 1, sel.h - 1, 0);
+ sel._hotspot = Common::Point(2, sel.h - 1);
+
+ // TODO: Hyperlink hand cursor
+}
+
+void Events::checkForNextFrameCounter() {
+ // Check for next game frame
+ uint32 milli = g_system->getMillis();
+ if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) {
+ ++_frameCounter;
+ _priorFrameTime = milli;
+
+ if (_redraw)
+ g_vm->_windows->redraw();
+ _redraw = false;
+ g_vm->_screen->update();
+ return;
+ }
+}
+
+void Events::getEvent(event_t *event, bool polled) {
+ _currentEvent = event;
+ event->clear();
+
+ dispatchEvent(*_currentEvent, polled);
+
+ if (!polled) {
+ while (!g_vm->shouldQuit() && _currentEvent->type == evtype_None && !isTimerExpired()) {
+ pollEvents();
+ g_system->delayMillis(10);
+
+ dispatchEvent(*_currentEvent, polled);
+ }
+
+ if (g_vm->shouldQuit())
+ _currentEvent->type = evtype_Quit;
+ }
+
+ if (_currentEvent->type == evtype_None && isTimerExpired()) {
+ store(evtype_Timer, nullptr, 0, 0);
+ dispatchEvent(*_currentEvent, polled);
+
+ _timerTimeExpiry = g_system->getMillis() + _timerMilli;
+ }
+
+ _currentEvent = nullptr;
+}
+
+void Events::store(EvType type, Window *win, uint32 val1, uint32 val2) {
+ Event ev(type, win, val1, val2);
+
+ switch (type) {
+ case evtype_Arrange:
+ case evtype_Redraw:
+ case evtype_SoundNotify:
+ case evtype_Timer:
+ _eventsPolled.push(ev);
+ break;
+
+ default:
+ _eventsLogged.push(ev);
+ break;
+ }
+}
+
+void Events::dispatchEvent(Event &ev, bool polled) {
+ Event dispatch;
+
+ if (!polled) {
+ dispatch = _eventsLogged.retrieve();
+ if (!dispatch)
+ dispatch = _eventsPolled.retrieve();
+ } else {
+ dispatch = _eventsPolled.retrieve();
+ }
+
+ if (dispatch)
+ ev = dispatch;
+}
+
+void Events::pollEvents() {
+ Common::Event event;
+
+ do {
+ checkForNextFrameCounter();
+ g_system->getEventManager()->pollEvent(event);
+
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ setCursor(CURSOR_NONE);
+ handleKeyDown(event.kbd);
+ return;
+
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_RBUTTONDOWN:
+ handleButtonDown(event.type == Common::EVENT_LBUTTONDOWN, event.mouse);
+ return;
+
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP:
+ handleButtonUp(event.type == Common::EVENT_LBUTTONUP, event.mouse);
+ return;
+
+ case Common::EVENT_WHEELUP:
+ case Common::EVENT_WHEELDOWN:
+ setCursor(CURSOR_NONE);
+ handleScroll(event.type == Common::EVENT_WHEELUP);
+ return;
+
+ case Common::EVENT_MOUSEMOVE:
+ handleMouseMove(event.mouse);
+ break;
+
+ default:
+ break;
+ }
+ } while (event.type == Common::EVENT_MOUSEMOVE);
+}
+
+void Events::handleKeyDown(const Common::KeyState &ks) {
+ Clipboard &clipboard = *g_vm->_clipboard;
+ Windows &windows = *g_vm->_windows;
+
+ if (ks.flags & Common::KBD_CTRL) {
+ if (ks.keycode == Common::KEYCODE_a)
+ windows.inputHandleKey(keycode_Home);
+ else if (ks.keycode == Common::KEYCODE_c)
+ clipboard.clipboardSend(CLIPBOARD);
+ else if (ks.keycode == Common::KEYCODE_e)
+ windows.inputHandleKey(keycode_End);
+ else if (ks.keycode == Common::KEYCODE_u)
+ windows.inputHandleKey(keycode_Escape);
+ else if (ks.keycode == Common::KEYCODE_v)
+ clipboard.clipboardReceive(CLIPBOARD);
+ else if (ks.keycode == Common::KEYCODE_x)
+ clipboard.clipboardSend(CLIPBOARD);
+ else if (ks.keycode == Common::KEYCODE_LEFT || ks.keycode == Common::KEYCODE_KP4)
+ windows.inputHandleKey(keycode_SkipWordLeft);
+ else if (ks.keycode == Common::KEYCODE_RIGHT || ks.keycode == Common::KEYCODE_KP6)
+ windows.inputHandleKey(keycode_SkipWordRight);
+
+ return;
+ }
+
+ if (ks.flags & Common::KBD_ALT)
+ return;
+
+ switch (ks.keycode) {
+ case Common::KEYCODE_RETURN:
+ windows.inputHandleKey(keycode_Return);
+ break;
+ case Common::KEYCODE_BACKSPACE:
+ windows.inputHandleKey(keycode_Delete);
+ break;
+ case Common::KEYCODE_DELETE:
+ windows.inputHandleKey(keycode_Erase);
+ break;
+ case Common::KEYCODE_TAB:
+ windows.inputHandleKey(keycode_Tab);
+ break;
+ case Common::KEYCODE_PAGEUP:
+ windows.inputHandleKey(keycode_PageUp);
+ break;
+ case Common::KEYCODE_PAGEDOWN:
+ windows.inputHandleKey(keycode_PageDown);
+ break;
+ case Common::KEYCODE_HOME:
+ windows.inputHandleKey(keycode_Home);
+ break;
+ case Common::KEYCODE_END:
+ windows.inputHandleKey(keycode_End);
+ break;
+ case Common::KEYCODE_LEFT:
+ windows.inputHandleKey(keycode_Left);
+ break;
+ case Common::KEYCODE_RIGHT:
+ windows.inputHandleKey(keycode_Right);
+ break;
+ case Common::KEYCODE_UP:
+ windows.inputHandleKey(keycode_Up);
+ break;
+ case Common::KEYCODE_DOWN:
+ windows.inputHandleKey(keycode_Down);
+ break;
+ case Common::KEYCODE_ESCAPE:
+ windows.inputHandleKey(keycode_Escape);
+ break;
+ case Common::KEYCODE_F1:
+ windows.inputHandleKey(keycode_Func1);
+ break;
+ case Common::KEYCODE_F2:
+ windows.inputHandleKey(keycode_Func2);
+ break;
+ case Common::KEYCODE_F3:
+ windows.inputHandleKey(keycode_Func3);
+ break;
+ case Common::KEYCODE_F4:
+ windows.inputHandleKey(keycode_Func4);
+ break;
+ case Common::KEYCODE_F5:
+ windows.inputHandleKey(keycode_Func5);
+ break;
+ case Common::KEYCODE_F6:
+ windows.inputHandleKey(keycode_Func6);
+ break;
+ case Common::KEYCODE_F7:
+ windows.inputHandleKey(keycode_Func7);
+ break;
+ case Common::KEYCODE_F8:
+ windows.inputHandleKey(keycode_Func8);
+ break;
+ case Common::KEYCODE_F9:
+ windows.inputHandleKey(keycode_Func9);
+ break;
+ case Common::KEYCODE_F10:
+ windows.inputHandleKey(keycode_Func10);
+ break;
+ case Common::KEYCODE_F11:
+ windows.inputHandleKey(keycode_Func11);
+ break;
+ case Common::KEYCODE_F12:
+ windows.inputHandleKey(keycode_Func12);
+ break;
+ default:
+ windows.inputHandleKey(ks.ascii);
+ break;
+ break;
+ }
+}
+
+void Events::handleScroll(bool wheelUp) {
+ g_vm->_windows->inputHandleKey(wheelUp ? keycode_MouseWheelUp : keycode_MouseWheelDown);
+}
+
+void Events::handleMouseMove(const Point &pos) {
+ if (_cursorId == CURSOR_NONE)
+ setCursor(CURSOR_ARROW);
+
+ // hyperlinks and selection
+ // TODO: Properly handle commented out lines
+ if (g_vm->_copySelect) {
+ //gdk_window_set_cursor((GTK_WIDGET(widget)->window), gdk_ibeam);
+ g_vm->_selection->moveSelection(pos);
+ } else {
+ if (g_vm->_selection->getHyperlink(pos)) {
+ //gdk_window_set_cursor((GTK_WIDGET(widget)->window), gdk_hand);
+ } else {
+ //gdk_window_set_cursor((GTK_WIDGET(widget)->window), nullptr);
+ }
+ }
+}
+
+void Events::handleButtonDown(bool isLeft, const Point &pos) {
+ if (isLeft) {
+ setCursor(CURSOR_IBEAM);
+ g_vm->_windows->inputHandleClick(pos);
+ } else {
+ g_vm->_clipboard->clipboardReceive(PRIMARY);
+ }
+}
+
+void Events::handleButtonUp(bool isLeft, const Point &pos) {
+ if (isLeft) {
+ setCursor(CURSOR_ARROW);
+ g_vm->_copySelect = false;
+ g_vm->_clipboard->clipboardSend(PRIMARY);
+ }
+}
+
+void Events::waitForPress() {
+ Common::Event e;
+
+ do {
+ g_system->getEventManager()->pollEvent(e);
+ g_system->delayMillis(10);
+ checkForNextFrameCounter();
+ } while (!g_vm->shouldQuit() && e.type != Common::EVENT_KEYDOWN &&
+ e.type != Common::EVENT_LBUTTONDOWN && e.type != Common::EVENT_RBUTTONDOWN &&
+ e.type != Common::EVENT_MBUTTONDOWN);
+}
+
+void Events::setCursor(CursorId cursorId) {
+ if (cursorId != _cursorId) {
+ if (cursorId == CURSOR_NONE) {
+ CursorMan.showMouse(false);
+ } else {
+ if (!CursorMan.isVisible())
+ CursorMan.showMouse(true);
+
+ const Surface &s = _cursors[cursorId];
+ const int TRANSPARENT = s.format.RGBToColor(0x80, 0x80, 0x80);
+
+ CursorMan.replaceCursor(s.getPixels(), s.w, s.h, s._hotspot.x, s._hotspot.y, TRANSPARENT, true, &s.format);
+ }
+
+ _cursorId = cursorId;
+ }
+}
+
+void Events::setTimerInterval(uint milli) {
+ _timerMilli = milli;
+ _timerTimeExpiry = g_system->getMillis() + milli;
+}
+
+bool Events::isTimerExpired() const {
+ return _timerMilli && g_system->getMillis() >= _timerTimeExpiry;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/events.h b/engines/glk/events.h
new file mode 100644
index 0000000000..537813f314
--- /dev/null
+++ b/engines/glk/events.h
@@ -0,0 +1,291 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_EVENTS_H
+#define GLK_EVENTS_H
+
+#include "common/events.h"
+#include "graphics/surface.h"
+#include "glk/utils.h"
+
+namespace Gargoyle {
+
+#define GAME_FRAME_RATE 100
+#define GAME_FRAME_TIME (1000 / GAME_FRAME_RATE)
+
+class Window;
+
+/**
+ * Event types
+ */
+enum EvType {
+ evtype_None = 0,
+ evtype_Timer = 1,
+ evtype_CharInput = 2,
+ evtype_LineInput = 3,
+ evtype_MouseInput = 4,
+ evtype_Arrange = 5,
+ evtype_Redraw = 6,
+ evtype_SoundNotify = 7,
+ evtype_Hyperlink = 8,
+ evtype_VolumeNotify = 9,
+
+ // ScummVM custom events
+ evtype_Quit = 99
+};
+
+/**
+ * Keycodes
+ */
+enum Keycode {
+ keycode_Unknown = 0xffffffffU,
+ keycode_Left = 0xfffffffeU,
+ keycode_Right = 0xfffffffdU,
+ keycode_Up = 0xfffffffcU,
+ keycode_Down = 0xfffffffbU,
+ keycode_Return = 0xfffffffaU,
+ keycode_Delete = 0xfffffff9U,
+ keycode_Escape = 0xfffffff8U,
+ keycode_Tab = 0xfffffff7U,
+ keycode_PageUp = 0xfffffff6U,
+ keycode_PageDown = 0xfffffff5U,
+ keycode_Home = 0xfffffff4U,
+ keycode_End = 0xfffffff3U,
+ keycode_Func1 = 0xffffffefU,
+ keycode_Func2 = 0xffffffeeU,
+ keycode_Func3 = 0xffffffedU,
+ keycode_Func4 = 0xffffffecU,
+ keycode_Func5 = 0xffffffebU,
+ keycode_Func6 = 0xffffffeaU,
+ keycode_Func7 = 0xffffffe9U,
+ keycode_Func8 = 0xffffffe8U,
+ keycode_Func9 = 0xffffffe7U,
+ keycode_Func10 = 0xffffffe6U,
+ keycode_Func11 = 0xffffffe5U,
+ keycode_Func12 = 0xffffffe4U,
+
+ // non standard keycodes
+ keycode_Erase = 0xffffef7fU,
+ keycode_MouseWheelUp = 0xffffeffeU,
+ keycode_MouseWheelDown = 0xffffefffU,
+ keycode_SkipWordLeft = 0xfffff000U,
+ keycode_SkipWordRight = 0xfffff001U,
+
+ // The last keycode is always = 0x100000000 - keycode_MAXVAL)
+ keycode_MAXVAL = 28U
+};
+
+/**
+ * List of cursors
+ */
+enum CursorId {
+ CURSOR_NONE = 0,
+ CURSOR_ARROW = 1,
+ CURSOR_IBEAM = 2,
+ CURSOR_HAND = 3
+};
+
+/**
+ * Event structure
+ */
+struct Event {
+ EvType type;
+ Window *window;
+ uint32 val1, val2;
+
+ /**
+ * Constructor
+ */
+ Event() {
+ clear();
+ }
+
+ /**
+ * Constructor
+ */
+ Event(EvType evType, Window *evWindow, uint32 evVal1, uint32 evVal2) {
+ type = evType;
+ window = evWindow;
+ val1 = evVal1;
+ val2 = evVal2;
+ }
+
+ /**
+ * Clear
+ */
+ void clear() {
+ type = evtype_None;
+ window = nullptr;
+ val1 = val2 = 0;
+ }
+
+ /**
+ * Boolean cast to allow checking whether event is filled out
+ */
+ operator bool() const {
+ return type != evtype_None;
+ }
+};
+typedef Event event_t;
+
+class EventQueue : public Common::Queue<Event> {
+public:
+ /**
+ * Retrieve a pending event, if any
+ */
+ Event retrieve() {
+ return empty() ? Event() : pop();
+ }
+};
+
+/**
+ * Events manager
+ */
+class Events {
+ struct Surface : public Graphics::Surface {
+ Common::Point _hotspot;
+ };
+private:
+ EventQueue _eventsPolled; ///< User generated events
+ EventQueue _eventsLogged; ///< Custom events generated by game code
+ Event *_currentEvent; ///< Event pointer passed during event retrieval
+ uint32 _priorFrameTime; ///< Time of prior game frame
+ uint32 _frameCounter; ///< Frame counter
+ bool _redraw; ///< Screen needed redrawing
+ CursorId _cursorId; ///< Current cursor Id
+ Surface _cursors[4]; ///< Cursor pixel data
+ uint _timerMilli; ///< Time in milliseconds between timer events
+ uint _timerTimeExpiry; ///< When to trigger next timer event
+private:
+ /**
+ * Initialize the cursor graphics
+ */
+ void initializeCursors();
+
+ /**
+ * Checks for whether it's time for the next game frame
+ */
+ void checkForNextFrameCounter();
+
+ /**
+ * Dispatches an event
+ */
+ void dispatchEvent(Event &ev, bool polled);
+
+ /**
+ * Poll for user events
+ */
+ void pollEvents();
+
+ /**
+ * Handle a key down event
+ */
+ void handleKeyDown(const Common::KeyState &ks);
+
+ /**
+ * Handle scroll events
+ */
+ void handleScroll(bool wheelUp);
+
+ /**
+ * Handle mouse move events
+ */
+ void handleMouseMove(const Point &pos);
+
+ /**
+ * Handle mouse down events
+ */
+ void handleButtonDown(bool isLeft, const Point &pos);
+
+ /**
+ * Handle mouse up events
+ */
+ void handleButtonUp(bool isLeft, const Point &pos);
+public:
+ bool _forceClick;
+public:
+ /**
+ * Constructor
+ */
+ Events();
+
+ /**
+ * Destructor
+ */
+ ~Events();
+
+ /**
+ * Get any pending event
+ */
+ void getEvent(event_t *event, bool polled);
+
+ /**
+ * Store an event for retrieval
+ */
+ void store(EvType type, Window *win, uint32 val1 = 0, uint32 val2 = 0);
+
+ /**
+ * Wait for a keyboard or mouse press
+ */
+ void waitForPress();
+
+ /**
+ * Get the total number of frames played
+ */
+ uint32 getTotalPlayTicks() const {
+ return _frameCounter;
+ }
+
+ /**
+ * Set the total number of frames played
+ */
+ void setTotalPlayTicks(uint frames) {
+ _frameCounter = frames;
+ }
+
+ /**
+ * Flags the screen for redrawing
+ */
+ void redraw() {
+ _redraw = true;
+ }
+
+ /**
+ * Sets the current cursor
+ */
+ void setCursor(CursorId cursorId);
+
+ /**
+ * Set a timer interval
+ * @param milli Time in millieseconds for intervals, or 0 for off
+ */
+ void setTimerInterval(uint milli);
+
+ /**
+ * Returns true if it's time for a timer event
+ */
+ bool isTimerExpired() const;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/fonts.cpp b/engines/glk/fonts.cpp
new file mode 100644
index 0000000000..ee958b0e0c
--- /dev/null
+++ b/engines/glk/fonts.cpp
@@ -0,0 +1,157 @@
+/* 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/fonts.h"
+#include "glk/glk_types.h"
+#include "glk/conf.h"
+#include "glk/gargoyle.h"
+#include "common/memstream.h"
+#include "common/unzip.h"
+#include "graphics/fonts/ttf.h"
+#include "graphics/fontman.h"
+
+namespace Gargoyle {
+
+#define FONTS_VERSION 1.0
+#define FONTS_FILENAME "fonts.dat"
+
+Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface), _fontsMissing(false) {
+ if (!loadFonts())
+ error("Could not load data file");
+
+ // TODO: See if there's any better way for getting the leading and baseline
+ Common::Rect r1 = _fontTable[7]->getBoundingBox('o');
+ Common::Rect r2 = _fontTable[7]->getBoundingBox('y');
+ double baseLine = (double)r1.bottom;
+ double leading = (double)r2.bottom + 2;
+
+ g_conf->_leading = MAX((double)g_conf->_leading, leading);
+ g_conf->_baseLine = MAX((double)g_conf->_baseLine, baseLine);
+ g_conf->_cellW = _fontTable[0]->getStringWidth("0");
+ g_conf->_cellH = g_conf->_leading;
+}
+
+Fonts::~Fonts() {
+ for (int idx = 0; idx < FONTS_TOTAL; ++idx)
+ delete _fontTable[idx];
+}
+
+bool Fonts::loadFonts() {
+ Common::Archive *archive = nullptr;
+
+ if (!Common::File::exists(FONTS_FILENAME) || (archive = Common::makeZipArchive(FONTS_FILENAME)) == nullptr)
+ return false;
+
+ // Open the version.txt file within it to validate the version
+ Common::File f;
+ if (!f.open("version.txt", *archive)) {
+ delete archive;
+ return false;
+ }
+
+ // Validate the version
+ char buffer[4];
+ f.read(buffer, 3);
+ buffer[3] = '\0';
+
+ if (Common::String(buffer) != "1.0") {
+ delete archive;
+ return false;
+ }
+
+ // R ead in the fonts
+ double monoAspect = g_conf->_monoAspect;
+ double propAspect = g_conf->_propAspect;
+ double monoSize = g_conf->_monoSize;
+ double propSize = g_conf->_propSize;
+
+ _fontTable[0] = loadFont(MONOR, archive, monoSize, monoAspect, FONTR);
+ _fontTable[1] = loadFont(MONOB, archive, monoSize, monoAspect, FONTB);
+ _fontTable[2] = loadFont(MONOI, archive, monoSize, monoAspect, FONTI);
+ _fontTable[3] = loadFont(MONOZ, archive, monoSize, monoAspect, FONTZ);
+
+ _fontTable[4] = loadFont(PROPR, archive, propSize, propAspect, FONTR);
+ _fontTable[5] = loadFont(PROPB, archive, propSize, propAspect, FONTB);
+ _fontTable[6] = loadFont(PROPI, archive, propSize, propAspect, FONTI);
+ _fontTable[7] = loadFont(PROPZ, archive, propSize, propAspect, FONTZ);
+
+ delete archive;
+ return true;
+}
+
+const Graphics::Font *Fonts::loadFont(FACES face, Common::Archive *archive, double size, double aspect, int
+ style) {
+ const char *const FILENAMES[8] = {
+ "GoMono-Regular.ttf", "GoMono-Bold.ttf", "GoMono-Italic.ttf", "GoMono-Bold-Italic.ttf",
+ "NotoSerif-Regular.ttf", "NotoSerif-Bold.ttf", "NotoSerif-Italic.ttf", "NotoSerif-Bold-Italic.ttf"
+ };
+
+ Common::File f;
+ if (!f.open(FILENAMES[face], *archive))
+ error("Could not load font");
+
+ return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
+}
+
+FACES Fonts::getId(const Common::String &name) {
+ if (name == "monor") return MONOR;
+ if (name == "monob") return MONOB;
+ if (name == "monoi") return MONOI;
+ if (name == "monoz") return MONOZ;
+ if (name == "propr") return PROPR;
+ if (name == "propb") return PROPB;
+ if (name == "propi") return PROPI;
+ if (name == "propz") return PROPZ;
+ return MONOR;
+}
+
+int Fonts::drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw) {
+ Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
+ const Graphics::Font *font = _fontTable[fontIdx];
+ const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+ font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
+
+ pt.x += font->getStringWidth(text);
+ return MIN((int)pt.x, (int)_surface->w) * GLI_SUBPIX;
+}
+
+int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
+ Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
+ const Graphics::Font *font = _fontTable[fontIdx];
+ const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+ font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
+
+ pt.x += font->getStringWidth(text);
+ return MIN((int)pt.x, (int)_surface->w) * GLI_SUBPIX;
+}
+
+size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {
+ const Graphics::Font *font = _fontTable[fontIdx];
+ return font->getStringWidth(text) * GLI_SUBPIX;
+}
+
+size_t Fonts::stringWidthUni(int fontIdx, const Common::U32String &text, int spw) {
+ const Graphics::Font *font = _fontTable[fontIdx];
+ return font->getStringWidth(text) * GLI_SUBPIX;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/fonts.h b/engines/glk/fonts.h
new file mode 100644
index 0000000000..db547aabc4
--- /dev/null
+++ b/engines/glk/fonts.h
@@ -0,0 +1,118 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_FONTS_H
+#define GLK_FONTS_H
+
+#include "glk/glk_types.h"
+#include "glk/utils.h"
+#include "common/archive.h"
+#include "common/array.h"
+#include "common/file.h"
+#include "common/str.h"
+#include "common/ustr.h"
+#include "graphics/font.h"
+
+namespace Gargoyle {
+
+#define FONTS_TOTAL 8
+
+enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
+enum TYPES { MONOF, PROPF };
+enum STYLES { FONTR, FONTB, FONTI, FONTZ };
+
+/**
+ * Fonts manager
+ */
+class Fonts {
+private:
+ Graphics::ManagedSurface *_surface;
+ const Graphics::Font *_fontTable[FONTS_TOTAL];
+ bool _fontsMissing;
+private:
+ /**
+ * Load all the fonts
+ */
+ bool loadFonts();
+
+ /**
+ * Load a single font
+ */
+ const Graphics::Font *loadFont(FACES face, Common::Archive *archive, double size, double aspect, int style);
+public:
+ /**
+ * Get the index/id of a font by name
+ */
+ static FACES getId(const Common::String &name);
+public:
+ /**
+ * Constructor
+ */
+ Fonts(Graphics::ManagedSurface *surface);
+
+ /**
+ * Destructor
+ */
+ virtual ~Fonts();
+
+ /**
+ * Draws a string using the specified font at the given co-ordinates
+ * @param pos Position for the bottom-left corner the text will be drawn with
+ * @param fontIdx Which font to use
+ * @param rgb RGB tuplet specifying the text color
+ * @param text The text to draw
+ * @param spw ??
+ */
+ int drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw = 0);
+
+ /**
+ * Draws a unicode string using the specified font at the given co-ordinates
+ * @param pos Position for the bottom-left corner the text will be drawn with
+ * @param fontIdx Which font to use
+ * @param rgb RGB tuplet specifying the text color
+ * @param text The text to draw
+ * @param spw ??
+ */
+ int drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw = 0);
+
+ /**
+ * Get the width in pixels of a string
+ * @param fontIdx Which font to use
+ * @param text Text to get the width of
+ * @param spw Delta X
+ * @returns Width of string multiplied by GLI_SUBPIX
+ */
+ size_t stringWidth(int fontIdx, const Common::String &text, int spw = 0);
+
+ /**
+ * Get the width in pixels of a unicode string
+ * @param fontIdx Which font to use
+ * @param text Text to get the width of
+ * @param spw Delta X
+ * @returns Width of string multiplied by GLI_SUBPIX
+ */
+ size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = 0);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
new file mode 100644
index 0000000000..57965c8efb
--- /dev/null
+++ b/engines/glk/frotz/detection.cpp
@@ -0,0 +1,80 @@
+/* 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/frotz/detection.h"
+#include "common/file.h"
+#include "common/md5.h"
+
+#include "glk/frotz/detection_tables.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+ const char *const EXTENSIONS[9] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".zblorb" };
+
+ // Loop through the files of the folder
+ for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ // Check for a recognised filename
+ if (file->isDirectory())
+ continue;
+ Common::String filename = file->getName();
+ bool hasExt = false;
+ for (int idx = 0; idx < 9 && !hasExt; ++idx)
+ hasExt = filename.hasSuffixIgnoreCase(EXTENSIONS[idx]);
+ if (!hasExt)
+ continue;
+
+ // Open up the file and calculate the md5
+ Common::File gameFile;
+ if (!gameFile.open(*file))
+ continue;
+ Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+ size_t filesize = gameFile.size();
+ gameFile.close();
+
+ // Check for known game
+ const FrotzGameDescription *p = FROTZ_GAMES;
+ while (p->_gameId && p->_md5 && (md5 != p->_md5 || filesize != p->_filesize))
+ ++p;
+
+ DetectedGame gd;
+ if (!p->_gameId) {
+ // Generic .dat files don't get reported as matches unless they have a known md5
+ if (filename.hasSuffixIgnoreCase(".dat"))
+ continue;
+
+ warning("Uknown zcode game %s - %s %d", filename.c_str(), md5.c_str(), filesize);
+ gd = DetectedGame("zcode", "Unrecognised zcode game", Common::UNK_LANG, Common::kPlatformUnknown);
+ } else {
+ gd = DetectedGame(p->_gameId, p->_description, p->_language, Common::kPlatformUnknown, p->_extra);
+ }
+
+ gd.addExtraEntry("filename", filename);
+ gameList.push_back(gd);
+ }
+
+ return !gameList.empty();
+}
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/detection.h b/engines/glk/frotz/detection.h
new file mode 100644
index 0000000000..7630cb3f0f
--- /dev/null
+++ b/engines/glk/frotz/detection.h
@@ -0,0 +1,43 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_FROTZ_DETECTION
+#define GLK_FROTZ_DETECTION
+
+#include "common/fs.h"
+#include "engines/game.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+class FrotzMetaEngine {
+public:
+ /**
+ * Detect supported games
+ */
+ static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/frotz/detection_tables.cpp b/engines/glk/frotz/detection_tables.cpp
new file mode 100644
index 0000000000..3f619a5e4f
--- /dev/null
+++ b/engines/glk/frotz/detection_tables.cpp
@@ -0,0 +1,85 @@
+/* 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/frotz/detection_tables.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+const char *const AMFV_DESC = "A Mind Forever Voyaging";
+const char *const ARTHUR_DESC = "Arthur: The Quest for Excalibur";
+const char *const BALLYHOO_DESC = "Ballyhoo";
+const char *const BEYONDZORK_DESC = "Beyond Zork";
+const char *const BORDERZONE_DESC = "Border Zone";
+const char *const BUREAUCRACY_DESC = "Bureaucracy";
+const char *const CUTTHROATS_DESC = "Cutthroats";
+const char *const DEADLINE_DESC = "Deadline";
+const char *const ENCHANTER_DESC = "Enchanter";
+const char *const HHGTTG_DESC = "The Hitchhiker's Guide to the Galaxy";
+const char *const HIJINX_DESC = "Hollywood Hijinx";
+const char *const INFIDEL_DESC = "Infidel";
+const char *const JOURNEY_DESC = "Journey";
+const char *const LGOP_DESC = "Leather Goddesses of Phobos";
+const char *const LGOP2_DESC = "Leather Goddesses of Phobos 2";
+const char *const LURKING_DESC = "The Lurking Horror";
+const char *const MINIZORK1_DESC = "Mini Zork I: The Great Underground Empire";
+const char *const MOONMIST_DESC = "Moonmist";
+const char *const NORDBERT_DESC = "Nord and Bert Couldn't Make Head or Tail of It";
+const char *const PLANETFALL_DESC = "Planetfall";
+const char *const PLUNDERED_DESC = "Plundered Hearts";
+const char *const SAMPLER1_DESC = "Infocom Sampler 1";
+const char *const SAMPLER2_DESC = "Infocom Sampler 2";
+const char *const SEASTALKER_DESC = "Seastalker";
+const char *const SHERLOCKRIDDLE_DESC = "Sherlock: The Riddle of the Crown Jewels";
+const char *const SHOGUN_DESC = "James Clavell's Shogun";
+const char *const SORCERER_DESC = "Sorcerer";
+const char *const SPELLBREAKER_DESC = "Spellbreaker";
+const char *const STARCROSS_DESC = "Starcross";
+const char *const STATIONFALL_DESC = "Stationfall";
+const char *const SUSPECT_DESC = "Suspect";
+const char *const SUSPENDED_DESC = "Suspended";
+const char *const TRINITY_DESC = "Trinity";
+const char *const WISHBRINGER_DESC = "Wishbringer";
+const char *const WITNESS_DESC = "The Witness";
+const char *const ZORK0_DESC = "Zork Zero: The Revenge of Megaboz";
+const char *const ZORK1_DESC = "Zork I: The Great Underground Empire";
+const char *const ZORK2_DESC = "Zork II: The Wizard of Frobozz";
+const char *const ZORK3_DESC = "Zork III: The Dungeon Master";
+const char *const ZTUU_DESC = "Zork: The Undiscovered Underground";
+
+#define NONE GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
+#define ENTRY0(ID, DESCRIPTION, VERSION, MD5, FILESIZE) { ID, DESCRIPTION##_DESC, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
+#define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
+
+const FrotzGameDescription FROTZ_GAMES[] = {
+ ENTRY0("hhgttg", HHGTTG, "v31 Solid Gold", "379022bcd4ec74b90274c6100c33f579", 158412),
+ ENTRY0("hhgttg", HHGTTG, "v47", "fdda8f4239819402c62db866bb61a648", 112622),
+ ENTRY0("hhgttg", HHGTTG, "v56", "a214fcb42bc9f554d07d983a12f6a062", 113444),
+ ENTRY0("hhgttg", HHGTTG, "v58", "e867d49ad1fb9406ff4e0678a4ee2ac9", 113332),
+ ENTRY0("hhgttg", HHGTTG, "v59", "34f6abc1f2a42be127ef434fc475f0ee", 113334),
+
+ FROTZ_TABLE_END_MARKER
+};
+
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/detection_tables.h b/engines/glk/frotz/detection_tables.h
new file mode 100644
index 0000000000..48e0eb0a5a
--- /dev/null
+++ b/engines/glk/frotz/detection_tables.h
@@ -0,0 +1,85 @@
+/* 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 "engines/advancedDetector.h"
+#include "common/language.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+/**
+ * Game descriptor for ZCode games
+ */
+struct FrotzGameDescription {
+ const char *const _gameId;
+ const char *const _description;
+ const char *const _extra;
+ const char *const _md5;
+ size_t _filesize;
+ Common::Language _language;
+ const char *const _guiOptions;
+};
+
+extern const FrotzGameDescription FROTZ_GAMES[];
+extern const char *const AMFV_DESC;
+extern const char *const ARTHUR_DESC;
+extern const char *const BALLYHOO_DESC;
+extern const char *const BEYONDZORK_DESC;
+extern const char *const BORDERZONE_DESC;
+extern const char *const BUREAUCRACY_DESC;
+extern const char *const CUTTHROATS_DESC;
+extern const char *const DEADLINE_DESC;
+extern const char *const ENCHANTER_DESC;
+extern const char *const HHGTTG_DESC;
+extern const char *const HIJINX_DESC;
+extern const char *const INFIDEL_DESC;
+extern const char *const JOURNEY_DESC;
+extern const char *const LGOP_DESC;
+extern const char *const LGOP2_DESC;
+extern const char *const LURKING_DESC;
+extern const char *const MINIZORK1_DESC;
+extern const char *const MOONMIST_DESC;
+extern const char *const NORDBERT_DESC;
+extern const char *const PLANETFALL_DESC;
+extern const char *const PLUNDERED_DESC;
+extern const char *const SAMPLER1_DESC;
+extern const char *const SAMPLER2_DESC;
+extern const char *const SEASTALKER_DESC;
+extern const char *const SHERLOCKRIDDLE_DESC;
+extern const char *const SHOGUN_DESC;
+extern const char *const SORCERER_DESC;
+extern const char *const SPELLBREAKER_DESC;
+extern const char *const STARCROSS_DESC;
+extern const char *const STATIONFALL_DESC;
+extern const char *const SUSPECT_DESC;
+extern const char *const SUSPENDED_DESC;
+extern const char *const TRINITY_DESC;
+extern const char *const WISHBRINGER_DESC;
+extern const char *const WITNESS_DESC;
+extern const char *const ZORK0_DESC;
+extern const char *const ZORK1_DESC;
+extern const char *const ZORK2_DESC;
+extern const char *const ZORK3_DESC;
+extern const char *const ZTUU_DESC;
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
new file mode 100644
index 0000000000..0e516f0496
--- /dev/null
+++ b/engines/glk/frotz/frotz.cpp
@@ -0,0 +1,98 @@
+/* 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/frotz/frotz.h"
+#include "glk/frotz/frotz_types.h"
+#include "common/config-manager.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+Frotz *g_vm;
+
+Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+ Processor(syst, gameDesc) {
+ g_vm = this;
+}
+
+Frotz::~Frotz() {
+ reset_memory();
+}
+
+void Frotz::runGame(Common::SeekableReadStream *gameFile) {
+ story_fp = gameFile;
+ initialize();
+
+ // Game loop
+ interpret();
+}
+
+void Frotz::initialize() {
+ if (ConfMan.hasKey("attribute_assignment") && ConfMan.getBool("attribute_assignment"))
+ _attribute_assignment = true;
+ if (ConfMan.hasKey("attribute_testing") && ConfMan.getBool("attribute_testing"))
+ _attribute_testing = true;
+ if (ConfMan.hasKey("ignore_errors") && ConfMan.getBool("ignore_errors"))
+ _ignore_errors = true;
+ if (ConfMan.hasKey("object_movement") && ConfMan.getBool("object_movement"))
+ _object_movement = true;
+ if (ConfMan.hasKey("object_locating") && ConfMan.getBool("object_locating"))
+ _object_locating = true;
+ if (ConfMan.hasKey("piracy") && ConfMan.getBool("piracy"))
+ _piracy = true;
+ if (ConfMan.hasKey("save_quetzal") && ConfMan.getBool("save_quetzal"))
+ _save_quetzal = true;
+ if (ConfMan.hasKey("random_seed"))
+ _random.setSeed(ConfMan.getInt("random_seed"));
+ if (ConfMan.hasKey("script_cols"))
+ _script_cols = ConfMan.getInt("script_cols");
+ if (ConfMan.hasKey("tandy_bit") && ConfMan.getBool("tandy_bit"))
+ _user_tandy_bit = true;
+ if (ConfMan.hasKey("undo_slots"))
+ _undo_slots = ConfMan.getInt("undo_slots");
+ if (ConfMan.hasKey("expand_abbreviations") && ConfMan.getBool("expand_abbreviations"))
+ _expand_abbreviations = true;
+ if (ConfMan.hasKey("err_report_mode")) {
+ _err_report_mode = ConfMan.getInt("err_report_mode");
+ if ((_err_report_mode < ERR_REPORT_NEVER) || (_err_report_mode > ERR_REPORT_FATAL))
+ _err_report_mode = ERR_DEFAULT_REPORT_MODE;
+ }
+
+ // Call process initialization
+ Processor::initialize();
+
+ // Restart the game
+ z_restart();
+}
+
+Common::Error Frotz::loadGameState(int slot) {
+ // TODO
+ return Common::kNoError;
+}
+
+Common::Error Frotz::saveGameState(int slot, const Common::String &desc) {
+ // TODO
+ return Common::kNoError;
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/frotz.h b/engines/glk/frotz/frotz.h
new file mode 100644
index 0000000000..c1fe5dcab6
--- /dev/null
+++ b/engines/glk/frotz/frotz.h
@@ -0,0 +1,72 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_FROTZ_FROTZ
+#define GLK_FROTZ_FROTZ
+
+#include "glk/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+/**
+ * Frotz interpreter for Z-code games
+ */
+class Frotz : public Processor {
+public:
+ /**
+ * Constructor
+ */
+ Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+ /**
+ * Destructor
+ */
+ virtual ~Frotz();
+
+ /**
+ * Initialization
+ */
+ void initialize();
+
+ /**
+ * Execute the game
+ */
+ virtual void runGame(Common::SeekableReadStream *gameFile) override;
+
+ /**
+ * Load a savegame
+ */
+ virtual Common::Error loadGameState(int slot) override;
+
+ /**
+ * Save the game
+ */
+ virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
+};
+
+extern Frotz *g_vm;
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/frotz/frotz_types.h b/engines/glk/frotz/frotz_types.h
new file mode 100644
index 0000000000..5aae3d07c3
--- /dev/null
+++ b/engines/glk/frotz/frotz_types.h
@@ -0,0 +1,297 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_FROTZ_FROTZ_TYPES
+#define GLK_FROTZ_FROTZ_TYPES
+
+#include "glk/glk_types.h"
+#include "common/algorithm.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define MAX_UNDO_SLOTS 500
+#define STACK_SIZE 32768
+
+#define lo(v) (v & 0xff)
+#define hi(v) (v >> 8)
+
+/* There are four error reporting modes: never report errors;
+ * report only the first time a given error type occurs;
+ * report every time an error occurs;
+ * or treat all errors as fatal errors, killing the interpreter.
+ * I strongly recommend "report once" as the default. But you can compile in a
+ * different default by changing the definition of ERR_DEFAULT_REPORT_MODE.
+ */
+enum ErrorReport {
+ ERR_REPORT_NEVER = 0,
+ ERR_REPORT_ONCE = 1,
+ ERR_REPORT_ALWAYS = 2,
+ ERR_REPORT_FATAL = 3,
+
+ ERR_DEFAULT_REPORT_MODE = ERR_REPORT_NEVER
+};
+
+/**
+ * Character codes
+ */
+enum ZCode {
+ ZC_TIME_OUT = 0x00,
+ ZC_NEW_STYLE = 0x01,
+ ZC_NEW_FONT = 0x02,
+ ZC_BACKSPACE = 0x08,
+ ZC_INDENT = 0x09,
+ ZC_GAP = 0x0b,
+ ZC_RETURN = 0x0d,
+ ZC_HKEY_MIN = 0x0e,
+ ZC_HKEY_RECORD = 0x0e,
+ ZC_HKEY_PLAYBACK = 0x0f,
+ ZC_HKEY_SEED = 0x10,
+ ZC_HKEY_UNDO = 0x11,
+ ZC_HKEY_RESTART = 0x12,
+ ZC_HKEY_QUIT = 0x13,
+ ZC_HKEY_DEBUG = 0x14,
+ ZC_HKEY_HELP = 0x15,
+ ZC_HKEY_MAX = 0x15,
+ ZC_ESCAPE = 0x1b,
+ ZC_ASCII_MIN = 0x20,
+ ZC_ASCII_MAX = 0x7e,
+ ZC_BAD = 0x7f,
+ ZC_ARROW_MIN = 0x81,
+ ZC_ARROW_UP = 0x81,
+ ZC_ARROW_DOWN = 0x82,
+ ZC_ARROW_LEFT = 0x83,
+ ZC_ARROW_RIGHT = 0x84,
+ ZC_ARROW_MAX = 0x84,
+ ZC_FKEY_MIN = 0x85,
+ ZC_FKEY_MAX = 0x90,
+ ZC_NUMPAD_MIN = 0x91,
+ ZC_NUMPAD_MAX = 0x9a,
+ ZC_SINGLE_CLICK = 0x9b,
+ ZC_DOUBLE_CLICK = 0x9c,
+ ZC_MENU_CLICK = 0x9d,
+ ZC_LATIN1_MIN = 0xa0,
+ ZC_LATIN1_MAX = 0xff
+};
+
+enum Story {
+ BEYOND_ZORK,
+ SHERLOCK,
+ ZORK_ZERO,
+ SHOGUN,
+ ARTHUR,
+ JOURNEY,
+ LURKING_HORROR,
+ UNKNOWN
+};
+
+enum Version {
+ V1 = 1,
+ V2 = 2,
+ V3 = 3,
+ V4 = 4,
+ V5 = 5,
+ V6 = 6,
+ V7 = 7,
+ V8 = 8,
+ V9 = 9
+};
+
+enum ConfigFlag {
+ CONFIG_BYTE_SWAPPED = 0x01, ///< Story file is byte swapped - V3
+ CONFIG_TIME = 0x02, ///< Status line displays time - V3
+ CONFIG_TWODISKS = 0x04, ///< Story file occupied two disks - V3
+ CONFIG_TANDY = 0x08, ///< Tandy licensed game - V3
+ CONFIG_NOSTATUSLINE = 0x10, ///< Interpr can't support status lines - V3
+ CONFIG_SPLITSCREEN = 0x20, ///< Interpr supports split screen mode - V3
+ CONFIG_PROPORTIONAL = 0x40, ///< Interpr uses proportional font - V3
+
+ CONFIG_COLOUR = 0x01, ///< Interpr supports colour - V5+
+ CONFIG_PICTURES = 0x02, ///< Interpr supports pictures - V6
+ CONFIG_BOLDFACE = 0x04, ///< Interpr supports boldface style - V4+
+ CONFIG_EMPHASIS = 0x08, ///< Interpr supports emphasis style - V4+
+ CONFIG_FIXED = 0x10, ///< Interpr supports fixed width style - V4+
+ CONFIG_SOUND = 0x20, ///< Interpr supports sound - V6
+ CONFIG_TIMEDINPUT = 0x80, ///< Interpr supports timed input - V4+
+
+ SCRIPTING_FLAG = 0x0001, ///< Outputting to transscription file - V1+
+ FIXED_FONT_FLAG = 0x0002, ///< Use fixed width font - V3+
+ REFRESH_FLAG = 0x0004, ///< Refresh the screen - V6
+ GRAPHICS_FLAG = 0x0008, ///< Game wants to use graphics - V5+
+ OLD_SOUND_FLAG = 0x0010, ///< Game wants to use sound effects - V3
+ UNDO_FLAG = 0x0010, ///< Game wants to use UNDO feature - V5+
+ MOUSE_FLAG = 0x0020, ///< Game wants to use a mouse - V5+
+ COLOUR_FLAG = 0x0040, ///< Game wants to use colours - V5+
+ SOUND_FLAG = 0x0080, ///< Game wants to use sound effects - V5+
+ MENU_FLAG = 0x0100 ///< Game wants to use menus - V6
+};
+
+enum {
+ TRANSPARENT_FLAG = 0x0001 ///< Game wants to use transparency - V6
+};
+
+enum ErrorCode {
+ ERR_TEXT_BUF_OVF = 1, ///< Text buffer overflow
+ ERR_STORE_RANGE = 2, ///< Store out of dynamic memory
+ ERR_DIV_ZERO = 3, ///< Division by zero
+ ERR_ILL_OBJ = 4, ///< Illegal object
+ ERR_ILL_ATTR = 5, ///< Illegal attribute
+ ERR_NO_PROP = 6, ///< No such property
+ ERR_STK_OVF = 7, ///< Stack overflow
+ ERR_ILL_CALL_ADDR = 8, ///< Call to illegal address
+ ERR_CALL_NON_RTN = 9, ///< Call to non-routine
+ ERR_STK_UNDF = 10, ///< Stack underflow
+ ERR_ILL_OPCODE = 11, ///< Illegal opcode
+ ERR_BAD_FRAME = 12, ///< Bad stack frame
+ ERR_ILL_JUMP_ADDR = 13, ///< Jump to illegal address
+ ERR_SAVE_IN_INTER = 14, ///< Can't save while in interrupt
+ ERR_STR3_NESTING = 15, ///< Nesting stream #3 too deep
+ ERR_ILL_WIN = 16, ///< Illegal window
+ ERR_ILL_WIN_PROP = 17, ///< Illegal window property
+ ERR_ILL_PRINT_ADDR = 18, ///< Print at illegal address
+ ERR_DICT_LEN = 19, ///< Illegal dictionary word length
+ ERR_MAX_FATAL = 19,
+
+ // Less serious errors
+ ERR_JIN_0 = 20, ///< @jin called with object 0
+ ERR_GET_CHILD_0 = 21, ///< @get_child called with object 0
+ ERR_GET_PARENT_0 = 22, ///< @get_parent called with object 0
+ ERR_GET_SIBLING_0 = 23, ///< @get_sibling called with object 0
+ ERR_GET_PROP_ADDR_0 = 24, ///< @get_prop_addr called with object 0
+ ERR_GET_PROP_0 = 25, ///< @get_prop called with object 0
+ ERR_PUT_PROP_0 = 26, ///< @put_prop called with object 0
+ ERR_CLEAR_ATTR_0 = 27, ///< @clear_attr called with object 0
+ ERR_SET_ATTR_0 = 28, ///< @set_attr called with object 0
+ ERR_TEST_ATTR_0 = 29, ///< @test_attr called with object 0
+ ERR_MOVE_OBJECT_0 = 30, ///< @move_object called moving object 0
+ ERR_MOVE_OBJECT_TO_0 = 31, ///< @move_object called moving into object 0
+ ERR_REMOVE_OBJECT_0 = 32, ///< @remove_object called with object 0
+ ERR_GET_NEXT_PROP_0 = 33, ///< @get_next_prop called with object 0
+ ERR_NUM_ERRORS = 33
+};
+
+enum FrotzInterp {
+ INTERP_DEFAULT = 0,
+ INTERP_DEC_20 = 1,
+ INTERP_APPLE_IIE = 2,
+ INTERP_MACINTOSH = 3,
+ INTERP_AMIGA = 4,
+ INTERP_ATARI_ST = 5,
+ INTERP_MSDOS = 6,
+ INTERP_CBM_128 = 7,
+ INTERP_CBM_64 = 8,
+ INTERP_APPLE_IIC = 9,
+ INTERP_APPLE_IIGS = 10,
+ INTERP_TANDY = 11
+};
+
+enum Colour {
+ BLACK_COLOUR = 2,
+ RED_COLOUR = 3,
+ GREEN_COLOUR = 4,
+ YELLOW_COLOUR = 5,
+ BLUE_COLOUR = 6,
+ MAGENTA_COLOUR = 7,
+ CYAN_COLOUR = 8,
+ WHITE_COLOUR = 9,
+ GREY_COLOUR = 10, ///< INTERP_MSDOS only
+ LIGHTGREY_COLOUR = 10, ///< INTERP_AMIGA only
+ MEDIUMGREY_COLOUR = 11, ///< INTERP_AMIGA only
+ DARKGREY_COLOUR = 12, ///< INTERP_AMIGA only
+ TRANSPARENT_COLOUR = 15 ///< ZSpec 1.1
+};
+
+enum Style {
+ REVERSE_STYLE = 1,
+ BOLDFACE_STYLE = 2,
+ EMPHASIS_STYLE = 4,
+ FIXED_WIDTH_STYLE = 8
+};
+
+enum FontStyle {
+ TEXT_FONT = 1,
+ PICTURE_FONT = 2,
+ GRAPHICS_FONT = 3,
+ FIXED_WIDTH_FONT = 4
+};
+
+/*** Constants for os_beep */
+
+#define BEEP_HIGH 1
+#define BEEP_LOW 2
+
+/*** Constants for os_menu */
+
+#define MENU_NEW 0
+#define MENU_ADD 1
+#define MENU_REMOVE 2
+
+typedef byte zbyte;
+typedef uint zchar;
+typedef uint16 zword;
+
+/**
+ * User options
+ */
+struct UserOptions {
+ int _attribute_assignment;
+ int _attribute_testing;
+ int _context_lines;
+ int _object_locating;
+ int _object_movement;
+ int _left_margin;
+ int _right_margin;
+ bool _ignore_errors;
+ bool _piracy;
+ int _undo_slots;
+ int _expand_abbreviations;
+ int _script_cols;
+ bool _save_quetzal;
+ int _err_report_mode;
+ bool _sound;
+ bool _user_tandy_bit;
+
+ UserOptions() : _attribute_assignment(0), _attribute_testing(0),
+ _context_lines(0), _object_locating(0), _object_movement(0),
+ _left_margin(0), _right_margin(0), _ignore_errors(false), _piracy(false),
+ _undo_slots(MAX_UNDO_SLOTS), _expand_abbreviations(0), _script_cols(80),
+ _save_quetzal(true), _err_report_mode(ERR_DEFAULT_REPORT_MODE), _sound(true),
+ _user_tandy_bit(false) {
+ }
+};
+
+#define MAX_NESTING 16
+struct Redirect {
+ zword _xSize;
+ zword _table;
+ zword _width;
+ zword _total;
+
+ Redirect() : _xSize(0), _table(0), _width(0), _total(0) {}
+ Redirect(zword xSize, zword table, zword width = 0, zword total = 0) :
+ _xSize(xSize), _table(table), _width(width), _total(total) {}
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
new file mode 100644
index 0000000000..b2534385e4
--- /dev/null
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -0,0 +1,498 @@
+/* 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/frotz/glk_interface.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+GlkInterface::GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+ Glk(syst, gameDesc),
+ oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
+ curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0),
+ curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr),
+ gos_lower(nullptr), gos_curwin(nullptr), gos_linepending(0), gos_linebuf(nullptr),
+ gos_linewin(nullptr), gos_channel(nullptr), cwin(0), mwin(0), mouse_x(0), mouse_y(0),
+ menu_selected(0), ostream_screen(false), ostream_script(false), ostream_memory(false),
+ ostream_record(false), istream_replay(false), message(false),
+ enable_wrapping(false), enable_scripting(false), enable_scrolling(false),
+ enable_buffering(false), next_sample(0), next_volume(0),
+ _soundLocked(false), _soundPlaying(false) {
+ Common::fill(&statusline[0], &statusline[256], '\0');
+}
+
+void GlkInterface::initialize() {
+ uint width, height;
+
+ /*
+ * Init glk stuff
+ */
+
+ // monor
+ glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Proportional, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Weight, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Oblique, 0);
+
+ // monob
+ glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Proportional, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Weight, 1);
+ glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Oblique, 0);
+
+ // monoi
+ glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Proportional, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Weight, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Oblique, 1);
+
+ // monoz
+ glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Proportional, 0);
+ glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Weight, 1);
+ glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Oblique, 1);
+
+ // propr
+ glk_stylehint_set(wintype_TextBuffer, style_Normal, stylehint_Proportional, 1);
+ glk_stylehint_set(wintype_TextGrid, style_Normal, stylehint_Proportional, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Weight, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Oblique, 0);
+
+ // propb
+ glk_stylehint_set(wintype_TextBuffer, style_Header, stylehint_Proportional, 1);
+ glk_stylehint_set(wintype_TextGrid, style_Header, stylehint_Proportional, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Weight, 1);
+ glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Oblique, 0);
+
+ // propi
+ glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Proportional, 1);
+ glk_stylehint_set(wintype_TextGrid, style_Emphasized, stylehint_Proportional, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Weight, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Oblique, 1);
+
+ // propi
+ glk_stylehint_set(wintype_TextBuffer, style_Note, stylehint_Proportional, 1);
+ glk_stylehint_set(wintype_TextGrid, style_Note, stylehint_Proportional, 0);
+ glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Weight, 1);
+ glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Oblique, 1);
+
+ gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
+ if (!gos_lower)
+ gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+ glk_window_get_size(gos_lower, &width, &height);
+ glk_window_close(gos_lower, NULL);
+
+ gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+ gos_upper = glk_window_open(gos_lower,
+ winmethod_Above | winmethod_Fixed,
+ 0,
+ wintype_TextGrid, 0);
+
+ gos_channel = NULL;
+
+ glk_set_window(gos_lower);
+ gos_curwin = gos_lower;
+
+ /*
+ * Icky magic bit setting
+ */
+
+ if (h_version == V3 && _user_tandy_bit)
+ h_config |= CONFIG_TANDY;
+
+ if (h_version == V3 && gos_upper)
+ h_config |= CONFIG_SPLITSCREEN;
+
+ if (h_version == V3 && !gos_upper)
+ h_config |= CONFIG_NOSTATUSLINE;
+
+ if (h_version >= V4)
+ h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
+ CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR;
+
+ if (h_version >= V5)
+ h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
+
+ if ((h_version >= 5) && (h_flags & SOUND_FLAG))
+ h_flags |= SOUND_FLAG;
+
+ if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
+ h_flags |= OLD_SOUND_FLAG;
+
+ if ((h_version == 6) && (_sound != 0))
+ h_config |= CONFIG_SOUND;
+
+ if (h_version >= V5 && (h_flags & UNDO_FLAG))
+ if (_undo_slots == 0)
+ h_flags &= ~UNDO_FLAG;
+
+ h_screen_cols = width;
+ h_screen_rows = height;
+
+ h_screen_height = h_screen_rows;
+ h_screen_width = h_screen_cols;
+
+ h_font_width = 1;
+ h_font_height = 1;
+
+ /* Must be after screen dimensions are computed. */
+ if (h_version == V6) {
+ h_flags &= ~GRAPHICS_FLAG;
+ }
+
+ // Use the ms-dos interpreter number for v6, because that's the
+ // kind of graphics files we understand. Otherwise, use DEC.
+ h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
+ h_interpreter_version = 'F';
+
+ {
+ // Set these per spec 8.3.2.
+ h_default_foreground = WHITE_COLOUR;
+ h_default_background = BLACK_COLOUR;
+ if (h_flags & COLOUR_FLAG)
+ h_flags &= ~COLOUR_FLAG;
+ }
+}
+
+int GlkInterface::os_char_width(zchar z) {
+ return 1;
+}
+
+int GlkInterface::os_string_width(const zchar *s) {
+ int width = 0;
+ zchar c;
+ while ((c = *s++) != 0)
+ if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
+ s++;
+ else
+ width += os_char_width(c);
+ return width;
+}
+
+int GlkInterface::os_string_length(zchar *s) {
+ int length = 0;
+ while (*s++) length++;
+ return length;
+}
+
+void GlkInterface::os_prepare_sample(int a) {
+ glk_sound_load_hint(a, 1);
+}
+
+void GlkInterface::os_finish_with_sample(int a) {
+ glk_sound_load_hint(a, 0);
+}
+
+void GlkInterface::os_start_sample(int number, int volume, int repeats, zword eos) {
+ int vol;
+
+ if (!gos_channel) {
+ gos_channel = glk_schannel_create(0);
+ if (!gos_channel)
+ return;
+ }
+
+ switch (volume) {
+ case 1: vol = 0x02000; break;
+ case 2: vol = 0x04000; break;
+ case 3: vol = 0x06000; break;
+ case 4: vol = 0x08000; break;
+ case 5: vol = 0x0a000; break;
+ case 6: vol = 0x0c000; break;
+ case 7: vol = 0x0e000; break;
+ case 8: vol = 0x10000; break;
+ default: vol = 0x20000; break;
+ }
+
+ // we dont do repeating or eos-callback for now...
+ glk_schannel_play_ext(gos_channel, number, 1, 0);
+ glk_schannel_set_volume(gos_channel, vol);
+}
+
+void GlkInterface::os_stop_sample(int a) {
+ if (!gos_channel)
+ return;
+ glk_schannel_stop(gos_channel);
+}
+
+void GlkInterface::os_beep(int volume) {
+}
+
+void GlkInterface::start_sample(int number, int volume, int repeats, zword eos) {
+ // TODO
+}
+
+void GlkInterface::start_next_sample() {
+ // TODO
+}
+
+void GlkInterface::gos_update_width() {
+ glui32 width;
+ if (gos_upper) {
+ glk_window_get_size(gos_upper, &width, nullptr);
+ h_screen_cols = width;
+ SET_BYTE(H_SCREEN_COLS, width);
+ if ((uint)curx > width) {
+ glk_window_move_cursor(gos_upper, 0, cury - 1);
+ curx = 1;
+ }
+ }
+}
+
+void GlkInterface::gos_update_height() {
+ glui32 height_upper;
+ glui32 height_lower;
+ if (gos_curwin) {
+ glk_window_get_size(gos_upper, nullptr, &height_upper);
+ glk_window_get_size(gos_lower, nullptr, &height_lower);
+ h_screen_rows = height_upper + height_lower + 1;
+ SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
+ }
+}
+
+void GlkInterface::reset_status_ht() {
+ glui32 height;
+ if (gos_upper) {
+ glk_window_get_size(gos_upper, nullptr, &height);
+ if ((uint)mach_status_ht != height) {
+ glk_window_set_arrangement(
+ glk_window_get_parent(gos_upper),
+ winmethod_Above | winmethod_Fixed,
+ mach_status_ht, nullptr);
+ }
+ }
+}
+
+void GlkInterface::erase_window(zword w) {
+ if (w == 0)
+ glk_window_clear(gos_lower);
+ else if (gos_upper) {
+#ifdef GARGLK
+ garglk_set_reversevideo_stream(
+ glk_window_get_stream(gos_upper),
+ true);
+#endif /* GARGLK */
+
+ memset(statusline, ' ', sizeof statusline);
+ glk_window_clear(gos_upper);
+ reset_status_ht();
+ curr_status_ht = 0;
+ }
+}
+
+void GlkInterface::split_window(zword lines) {
+ if (!gos_upper)
+ return;
+
+ // The top line is always set for V1 to V3 games
+ if (h_version < V4)
+ lines++;
+
+ if (!lines || lines > curr_status_ht) {
+ glui32 height;
+
+ glk_window_get_size(gos_upper, nullptr, &height);
+ if (lines != height)
+ glk_window_set_arrangement(
+ glk_window_get_parent(gos_upper),
+ winmethod_Above | winmethod_Fixed,
+ lines, nullptr);
+ curr_status_ht = lines;
+ }
+ mach_status_ht = lines;
+ if (cury > lines)
+ {
+ glk_window_move_cursor(gos_upper, 0, 0);
+ curx = cury = 1;
+ }
+ gos_update_width();
+
+ if (h_version == V3)
+ glk_window_clear(gos_upper);
+}
+
+void GlkInterface::restart_screen() {
+ erase_window(0);
+ erase_window(1);
+ split_window(0);
+}
+
+void GlkInterface::packspaces(zchar *src, zchar *dst) {
+ int killing = 0;
+ while (*src) {
+ if (*src == 0x20202020)
+ *src = ' ';
+ if (*src == ' ')
+ killing++;
+ else
+ killing = 0;
+ if (killing > 2)
+ src++;
+ else
+ *dst++ = *src++;
+ }
+
+ *dst = 0;
+}
+
+void GlkInterface::smartstatusline() {
+ zchar packed[256];
+ zchar buf[256];
+ zchar *a, *b, *c, *d;
+ int roomlen, scorelen, scoreofs;
+ int len, tmp;
+
+ packspaces(statusline, packed);
+ len = os_string_length(packed);
+
+ a = packed;
+ while (a[0] == ' ')
+ a++;
+
+ b = a;
+ while (b[0] != 0 && !(b[0] == ' ' && b[1] == ' '))
+ b++;
+
+ c = b;
+ while (c[0] == ' ')
+ c++;
+
+ d = packed + len - 1;
+ while (d[0] == ' ' && d > c)
+ d--;
+ if (d[0] != ' ' && d[0] != 0)
+ d++;
+ if (d < c)
+ d = c;
+
+ roomlen = b - a;
+ scorelen = d - c;
+ scoreofs = h_screen_cols - scorelen - 2;
+ if (scoreofs <= roomlen)
+ scoreofs = roomlen + 2;
+
+ for (tmp = 0; tmp < h_screen_cols; tmp++)
+ buf[tmp] = ' ';
+
+ memcpy(buf + 1 + scoreofs, c, scorelen * sizeof(zchar));
+ memcpy(buf + 1, a, roomlen * sizeof(zchar));
+
+ glk_window_move_cursor(gos_upper, 0, 0);
+ glk_put_buffer_uni(buf, h_screen_cols);
+ glk_window_move_cursor(gos_upper, cury - 1, curx - 1);
+}
+
+void GlkInterface::gos_cancel_pending_line() {
+ event_t ev;
+ glk_cancel_line_event(gos_linewin, &ev);
+ gos_linebuf[ev.val1] = '\0';
+ gos_linepending = 0;
+}
+
+zchar GlkInterface::os_read_key(int timeout, bool show_cursor) {
+ event_t ev;
+ winid_t win = gos_curwin ? gos_curwin : gos_lower;
+
+ if (gos_linepending)
+ gos_cancel_pending_line();
+
+ glk_request_char_event_uni(win);
+ if (timeout != 0)
+ glk_request_timer_events(timeout * 100);
+
+ while (!shouldQuit()) {
+ glk_select(&ev);
+ if (ev.type == evtype_Arrange) {
+ gos_update_height();
+ gos_update_width();
+ } else if (ev.type == evtype_Timer) {
+ glk_cancel_char_event(win);
+ glk_request_timer_events(0);
+ return ZC_TIME_OUT;
+ } else if (ev.type == evtype_CharInput)
+ break;
+ }
+ if (shouldQuit())
+ return 0;
+
+ glk_request_timer_events(0);
+
+ if (gos_upper && mach_status_ht < curr_status_ht)
+ reset_status_ht();
+ curr_status_ht = 0;
+
+ switch (ev.val1) {
+ case keycode_Escape: return ZC_ESCAPE;
+ case keycode_PageUp: return ZC_ARROW_MIN;
+ case keycode_PageDown: return ZC_ARROW_MAX;
+ case keycode_Left: return ZC_ARROW_LEFT;
+ case keycode_Right: return ZC_ARROW_RIGHT;
+ case keycode_Up: return ZC_ARROW_UP;
+ case keycode_Down: return ZC_ARROW_DOWN;
+ case keycode_Return: return ZC_RETURN;
+ case keycode_Delete: return ZC_BACKSPACE;
+ case keycode_Tab: return ZC_INDENT;
+ default:
+ return ev.val1;
+ }
+}
+
+zchar GlkInterface::os_read_line(int max, zchar *buf, int timeout, int width, int continued) {
+ event_t ev;
+ winid_t win = gos_curwin ? gos_curwin : gos_lower;
+
+ if (!continued && gos_linepending)
+ gos_cancel_pending_line();
+
+ if (!continued || !gos_linepending) {
+ glk_request_line_event_uni(win, buf, max, os_string_length(buf));
+ if (timeout != 0)
+ glk_request_timer_events(timeout * 100);
+ }
+
+ gos_linepending = 0;
+
+ while (!shouldQuit()) {
+ glk_select(&ev);
+ if (ev.type == evtype_Arrange) {
+ gos_update_height();
+ gos_update_width();
+ } else if (ev.type == evtype_Timer) {
+ gos_linewin = win;
+ gos_linepending = 1;
+ gos_linebuf = buf;
+ return ZC_TIME_OUT;
+ } else if (ev.type == evtype_LineInput) {
+ break;
+ }
+ }
+ if (shouldQuit())
+ return 0;
+
+ glk_request_timer_events(0);
+ buf[ev.val1] = '\0';
+
+ if (gos_upper && mach_status_ht < curr_status_ht)
+ reset_status_ht();
+ curr_status_ht = 0;
+
+ return ZC_RETURN;
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
new file mode 100644
index 0000000000..e3fa2c9081
--- /dev/null
+++ b/engines/glk/frotz/glk_interface.h
@@ -0,0 +1,195 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_FROTZ_GLK_INTERFACE
+#define GLK_FROTZ_GLK_INTERFACE
+
+#include "glk/glk.h"
+#include "glk/frotz/mem.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+enum SoundEffect {
+ EFFECT_PREPARE = 1,
+ EFFECT_PLAY = 2,
+ EFFECT_STOP = 3,
+ EFFECT_FINISH_WITH = 4
+};
+
+enum RestartAction {
+ RESTART_BEGIN = 0,
+ RESTART_WPROP_SET = 1,
+ RESTART_END = 2
+};
+
+
+/**
+ * Implements an intermediate interface on top of the GLK layer, providing screen
+ * and sound effect handling
+ */
+class GlkInterface : public Glk, public virtual UserOptions, public virtual Mem {
+public:
+ zchar statusline[256];
+ int oldstyle;
+ int curstyle;
+ int cury;
+ int curx;
+ int fixforced;
+
+ int curr_fg;
+ int curr_bg;
+ int curr_font;
+ int prev_font;
+ int temp_font;
+
+ int curr_status_ht;
+ int mach_status_ht;
+
+ winid_t gos_status;
+ winid_t gos_upper;
+ winid_t gos_lower;
+ winid_t gos_curwin;
+ int gos_linepending;
+ zchar *gos_linebuf;
+ winid_t gos_linewin;
+ schanid_t gos_channel;
+
+ // Current window and mouse data
+ int cwin;
+ int mwin;
+ int mouse_y;
+ int mouse_x;
+ int menu_selected;
+
+ // IO streams
+ bool ostream_screen;
+ bool ostream_script;
+ bool ostream_memory;
+ bool ostream_record;
+ bool istream_replay;
+ bool message;
+
+ // Window attributes
+ bool enable_wrapping;
+ bool enable_scripting;
+ bool enable_scrolling;
+ bool enable_buffering;
+
+ // Sound fields
+ int next_sample;
+ int next_volume;
+
+ bool _soundLocked;
+ bool _soundPlaying;
+protected:
+ int os_char_width(zchar z);
+ int os_string_width(const zchar *s);
+ int os_string_length(zchar *s);
+ void os_prepare_sample(int a);
+ void os_finish_with_sample(int a);
+
+ /**
+ * Play the given sample at the given volume (ranging from 1 to 8 and
+ * 255 meaning a default volume). The sound is played once or several
+ * times in the background (255 meaning forever). In Z-code 3 the
+ * repeats value is always 0 and the number of repeats is taken from
+ * the sound file itself. The end_of_sound function is called as soon
+ * as the sound finishes.
+ */
+ void os_start_sample(int number, int volume, int repeats, zword eos);
+
+ void os_stop_sample(int a);
+ void os_beep(int volume);
+
+ /**
+ * Call the IO interface to play a sample.
+ */
+ void start_sample(int number, int volume, int repeats, zword eos);
+
+ void start_next_sample();
+ void gos_update_width();
+ void gos_update_height();
+ void reset_status_ht();
+ void erase_window(zword w);
+ void split_window(zword lines);
+ void restart_screen();
+
+ /**
+ * statusline overflowed the window size ... bad game!
+ * so ... split status text into regions, reformat and print anew.
+ */
+ void packspaces(zchar *src, zchar *dst);
+
+ void smartstatusline();
+
+ /**
+ * Cancels any pending line
+ */
+ void gos_cancel_pending_line();
+
+ /**
+ * Called during game restarts
+ */
+ void os_restart_game(RestartAction) {}
+
+ /**
+ * Reads the mouse buttons
+ */
+ zword os_read_mouse() {
+ // Not implemented
+ return 0;
+ }
+
+ void os_scrollback_char(zchar z) {
+ // Not implemented
+ }
+
+ void os_scrollback_erase(int amount) {
+ // Not implemented
+ }
+
+ /**
+ * Waits for a keypress
+ */
+ zchar os_read_key(int timeout, bool show_cursor);
+
+ /**
+ * Waits for the user to type an input line
+ */
+ zchar os_read_line(int max, zchar *buf, int timeout, int width, int continued);
+public:
+ /**
+ * Constructor
+ */
+ GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+ /**
+ * Initialization
+ */
+ void initialize();
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
new file mode 100644
index 0000000000..a7747f5698
--- /dev/null
+++ b/engines/glk/frotz/mem.cpp
@@ -0,0 +1,419 @@
+/* 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/frotz/mem.h"
+#include "glk/frotz/frotz.h"
+#include "common/memstream.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+const Header::StoryEntry Header::RECORDS[25] = {
+ { SHERLOCK, 21, "871214" },
+ { SHERLOCK, 26, "880127" },
+ { BEYOND_ZORK, 47, "870915" },
+ { BEYOND_ZORK, 49, "870917" },
+ { BEYOND_ZORK, 51, "870923" },
+ { BEYOND_ZORK, 57, "871221" },
+ { ZORK_ZERO, 296, "881019" },
+ { ZORK_ZERO, 366, "890323" },
+ { ZORK_ZERO, 383, "890602" },
+ { ZORK_ZERO, 393, "890714" },
+ { SHOGUN, 292, "890314" },
+ { SHOGUN, 295, "890321" },
+ { SHOGUN, 311, "890510" },
+ { SHOGUN, 322, "890706" },
+ { ARTHUR, 54, "890606" },
+ { ARTHUR, 63, "890622" },
+ { ARTHUR, 74, "890714" },
+ { JOURNEY, 26, "890316" },
+ { JOURNEY, 30, "890322" },
+ { JOURNEY, 77, "890616" },
+ { JOURNEY, 83, "890706" },
+ { LURKING_HORROR, 203, "870506" },
+ { LURKING_HORROR, 219, "870912" },
+ { LURKING_HORROR, 221, "870918" },
+ { UNKNOWN, 0, "------" }
+};
+
+void Header::loadHeader(Common::SeekableReadStream &f) {
+ h_version = f.readByte();
+ h_config = f.readByte();
+
+ if (h_version < V1 || h_version > V8)
+ error("Unknown Z-code version");
+
+ if (h_version == V6)
+ error("Cannot play Z-code version 6");
+
+ if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
+ error("Byte swapped story file");
+
+ h_release = f.readUint16BE();
+ h_resident_size = f.readUint16BE();
+ h_start_pc = f.readUint16BE();
+ h_dictionary = f.readUint16BE();
+ h_objects = f.readUint16BE();
+ h_globals = f.readUint16BE();
+ h_dynamic_size = f.readUint16BE();
+ h_flags = f.readUint16BE();
+ f.read(h_serial, 6);
+
+ /* Auto-detect buggy story files that need special fixes */
+ _storyId = UNKNOWN;
+
+ for (int i = 0; RECORDS[i]._storyId != UNKNOWN; ++i) {
+ if (h_release == RECORDS[i]._release) {
+ if (!strncmp((const char *)h_serial, RECORDS[i]._serial, 6)) {
+ _storyId = RECORDS[i]._storyId;
+ break;
+ }
+ }
+ }
+
+ h_abbreviations = f.readUint16BE();
+ h_file_size = f.readUint16BE();
+ h_checksum = f.readUint16BE();
+
+ f.seek(H_FUNCTIONS_OFFSET);
+ h_functions_offset = f.readUint16BE();
+ h_strings_offset = f.readUint16BE();
+ f.seek(H_TERMINATING_KEYS);
+ h_terminating_keys = f.readUint16BE();
+ f.seek(H_ALPHABET);
+ h_alphabet = f.readUint16BE();
+ h_extension_table = f.readUint16BE();
+
+
+ // Zork Zero Macintosh doesn't have the graphics flag set
+ if (_storyId == ZORK_ZERO && h_release == 296)
+ h_flags |= GRAPHICS_FLAG;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0), story_size(0),
+ first_undo(nullptr), last_undo(nullptr), curr_undo(nullptr),
+ undo_mem(nullptr), prev_zmp(nullptr), undo_diff(nullptr),
+ undo_count(0), reserve_mem(0) {
+}
+
+void Mem::initialize() {
+ initializeStoryFile();
+ initializeUndo();
+ loadGameHeader();
+
+ // Allocate memory for story data
+ if ((zmp = (zbyte *)realloc(zmp, story_size)) == nullptr)
+ error("Out of memory");
+
+ // Load story file in chunks of 32KB
+ uint n = 0x8000;
+ for (uint size = 64; size < story_size; size += n) {
+ if (story_size - size < 0x8000)
+ n = story_size - size;
+
+ if (story_fp->read(zmp + size, n) != n)
+ error("Story file read error");
+ }
+
+ // Read header extension table
+ hx_table_size = get_header_extension(HX_TABLE_SIZE);
+ hx_unicode_table = get_header_extension(HX_UNICODE_TABLE);
+ hx_flags = get_header_extension(HX_FLAGS);
+}
+
+void Mem::initializeStoryFile() {
+ Common::SeekableReadStream *f = story_fp;
+ giblorb_map_t *map;
+ giblorb_result_t res;
+ uint32 magic;
+
+ magic = f->readUint32BE();
+
+ if (magic == MKTAG('F', 'O', 'R', 'M')) {
+ if (g_vm->giblorb_set_resource_map(f))
+ error("This Blorb file seems to be invalid.");
+
+ map = g_vm->giblorb_get_resource_map();
+
+ if (g_vm->giblorb_load_resource(map, giblorb_method_FilePos, &res, giblorb_ID_Exec, 0))
+ error("This Blorb file does not contain an executable chunk.");
+ if (res.chunktype != MKTAG('Z', 'C', 'O', 'D'))
+ error("This Blorb file contains an executable chunk, but it is not a Z-code file.");
+
+ blorb_ofs = res.data.startpos;
+ blorb_len = res.length;
+ } else {
+ blorb_ofs = 0;
+ blorb_len = f->size();
+ }
+
+ if (blorb_len < 64)
+ error("This file is too small to be a Z-code file.");
+}
+
+void Mem::initializeUndo() {
+ void *reserved = nullptr;
+
+ if (reserve_mem != 0) {
+ if ((reserved = malloc(reserve_mem)) == NULL)
+ return;
+ }
+
+ // Allocate h_dynamic_size bytes for previous dynamic zmp state
+ // + 1.5 h_dynamic_size for Quetzal diff + 2.
+ undo_mem = new zbyte[(h_dynamic_size * 5) / 2 + 2];
+ if (undo_mem != nullptr) {
+ prev_zmp = undo_mem;
+ undo_diff = undo_mem + h_dynamic_size;
+ memcpy(prev_zmp, zmp, h_dynamic_size);
+ } else {
+ _undo_slots = 0;
+ }
+
+ if (reserve_mem != 0)
+ delete reserved;
+}
+
+void Mem::loadGameHeader() {
+ // Load header
+ zmp = new byte[64];
+ story_fp->seek(blorb_ofs);
+ story_fp->read(zmp, 64);
+
+ Common::MemoryReadStream h(zmp, 64);
+ loadHeader(h);
+
+ // Calculate story file size in bytes
+ if (h_file_size != 0) {
+ story_size = (long)2 * h_file_size;
+
+ if (h_version >= V4)
+ story_size *= 2;
+ if (h_version >= V6)
+ story_size *= 2;
+ } else {
+ // Some old games lack the file size entry
+ story_size = blorb_len;
+ }
+}
+
+zword Mem::get_header_extension(int entry) {
+ zword addr;
+ zword val;
+
+ if (h_extension_table == 0 || entry > hx_table_size)
+ return 0;
+
+ addr = h_extension_table + 2 * entry;
+ LOW_WORD(addr, val);
+
+ return val;
+}
+
+void Mem::set_header_extension(int entry, zword val) {
+ zword addr;
+
+ if (h_extension_table == 0 || entry > hx_table_size)
+ return;
+
+ addr = h_extension_table + 2 * entry;
+ SET_WORD(addr, val);
+}
+
+void Mem::restart_header(void) {
+ zword screen_x_size;
+ zword screen_y_size;
+ zbyte font_x_size;
+ zbyte font_y_size;
+
+ int i;
+
+ SET_BYTE(H_CONFIG, h_config);
+ SET_WORD(H_FLAGS, h_flags);
+
+ if (h_version >= V4) {
+ SET_BYTE(H_INTERPRETER_NUMBER, h_interpreter_number);
+ SET_BYTE(H_INTERPRETER_VERSION, h_interpreter_version);
+ SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
+ SET_BYTE(H_SCREEN_COLS, h_screen_cols);
+ }
+
+ /* It's less trouble to use font size 1x1 for V5 games, especially
+ because of a bug in the unreleased German version of "Zork 1" */
+
+ if (h_version != V6) {
+ screen_x_size = (zword)h_screen_cols;
+ screen_y_size = (zword)h_screen_rows;
+ font_x_size = 1;
+ font_y_size = 1;
+ } else {
+ screen_x_size = h_screen_width;
+ screen_y_size = h_screen_height;
+ font_x_size = h_font_width;
+ font_y_size = h_font_height;
+ }
+
+ if (h_version >= V5) {
+ SET_WORD(H_SCREEN_WIDTH, screen_x_size);
+ SET_WORD(H_SCREEN_HEIGHT, screen_y_size);
+ SET_BYTE(H_FONT_HEIGHT, font_y_size);
+ SET_BYTE(H_FONT_WIDTH, font_x_size);
+ SET_BYTE(H_DEFAULT_BACKGROUND, h_default_background);
+ SET_BYTE(H_DEFAULT_FOREGROUND, h_default_foreground);
+ }
+
+ if (h_version == V6)
+ for (i = 0; i < 8; i++)
+ storeb((zword)(H_USER_NAME + i), h_user_name[i]);
+
+ SET_BYTE(H_STANDARD_HIGH, h_standard_high);
+ SET_BYTE(H_STANDARD_LOW, h_standard_low);
+
+ set_header_extension(HX_FLAGS, hx_flags);
+ set_header_extension(HX_FORE_COLOUR, hx_fore_colour);
+ set_header_extension(HX_BACK_COLOUR, hx_back_colour);
+}
+
+void Mem::storeb(zword addr, zbyte value) {
+ if (addr >= h_dynamic_size)
+ runtimeError(ERR_STORE_RANGE);
+
+ if (addr == H_FLAGS + 1) {
+ // flags register is modified
+
+ h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
+ h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
+
+ flagsChanged(value);
+ }
+
+ SET_BYTE(addr, value);
+}
+
+void Mem::storew(zword addr, zword value) {
+ storeb((zword)(addr + 0), hi(value));
+ storeb((zword)(addr + 1), lo(value));
+}
+
+void Mem::free_undo(int count) {
+ undo_t *p;
+
+ if (count > undo_count)
+ count = undo_count;
+ while (count--) {
+ p = first_undo;
+ if (curr_undo == first_undo)
+ curr_undo = curr_undo->next;
+ first_undo = first_undo->next;
+ free(p);
+ undo_count--;
+ }
+ if (first_undo)
+ first_undo->prev = NULL;
+ else
+ last_undo = NULL;
+}
+
+void Mem::reset_memory() {
+ story_fp = nullptr;
+ blorb_ofs = 0;
+ blorb_len = 0;
+
+ if (undo_mem) {
+ free_undo(undo_count);
+ delete undo_mem;
+ }
+
+ undo_mem = nullptr;
+ undo_count = 0;
+ delete[] zmp;
+ zmp = nullptr;
+}
+
+long Mem::mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff) {
+ unsigned size = mem_size;
+ zbyte *p = diff;
+ unsigned j;
+ zbyte c = 0;
+
+ for (;;) {
+ for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++)
+ size--;
+ if (size == 0) break;
+ size--;
+ if (j > 0x8000) {
+ *p++ = 0;
+ *p++ = 0xff;
+ *p++ = 0xff;
+ j -= 0x8000;
+ }
+ if (j > 0) {
+ *p++ = 0;
+ j--;
+ if (j <= 0x7f) {
+ *p++ = j;
+ } else {
+ *p++ = (j & 0x7f) | 0x80;
+ *p++ = (j & 0x7f80) >> 7;
+ }
+ }
+
+ *p++ = c;
+ *(b - 1) ^= c;
+ }
+
+ return p - diff;
+}
+
+void Mem::mem_undiff(zbyte *diff, long diff_length, zbyte *dest) {
+ zbyte c;
+
+ while (diff_length) {
+ c = *diff++;
+ diff_length--;
+ if (c == 0) {
+ unsigned runlen;
+
+ if (!diff_length)
+ return; // Incomplete run
+ runlen = *diff++;
+ diff_length--;
+ if (runlen & 0x80) {
+ if (!diff_length)
+ return; // Incomplete extended run
+ c = *diff++;
+ diff_length--;
+ runlen = (runlen & 0x7f) | (((unsigned)c) << 7);
+ }
+
+ dest += runlen + 1;
+ } else {
+ *dest++ ^= c;
+ }
+ }
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/mem.h b/engines/glk/frotz/mem.h
new file mode 100644
index 0000000000..de22006a33
--- /dev/null
+++ b/engines/glk/frotz/mem.h
@@ -0,0 +1,280 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_FROTZ_MEM
+#define GLK_FROTZ_MEM
+
+#include "glk/frotz/frotz_types.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define SET_WORD(addr,v) zmp[addr] = hi(v); zmp[addr+1] = lo(v)
+#define LOW_WORD(addr,v) v = READ_BE_UINT16(&zmp[addr])
+#define HIGH_WORD(addr,v) v = READ_BE_UINT16(&zmp[addr])
+#define HIGH_LONG(addr,v) v = READ_BE_UINT32(&zmp[addr])
+#define SET_BYTE(addr,v) zmp[addr] = v
+#define LOW_BYTE(addr,v) v = zmp[addr]
+
+enum HeaderByte {
+ H_VERSION = 0,
+ H_CONFIG = 1,
+ H_RELEASE = 2,
+ H_RESIDENT_SIZE = 4,
+ H_START_PC = 6,
+ H_DICTIONARY = 8,
+ H_OBJECTS = 10,
+ H_GLOBALS = 12,
+ H_DYNAMIC_SIZE = 14,
+ H_FLAGS = 16,
+ H_SERIAL = 18,
+ H_ABBREVIATIONS = 24,
+ H_FILE_SIZE = 26,
+ H_CHECKSUM = 28,
+ H_INTERPRETER_NUMBER = 30,
+ H_INTERPRETER_VERSION = 31,
+ H_SCREEN_ROWS = 32,
+ H_SCREEN_COLS = 33,
+ H_SCREEN_WIDTH = 34,
+ H_SCREEN_HEIGHT = 36,
+ H_FONT_HEIGHT = 38, ///< this is the font width in V5
+ H_FONT_WIDTH = 39, ///< this is the font height in V5
+ H_FUNCTIONS_OFFSET = 40,
+ H_STRINGS_OFFSET = 42,
+ H_DEFAULT_BACKGROUND = 44,
+ H_DEFAULT_FOREGROUND = 45,
+ H_TERMINATING_KEYS = 46,
+ H_LINE_WIDTH = 48,
+ H_STANDARD_HIGH = 50,
+ H_STANDARD_LOW = 51,
+ H_ALPHABET = 52,
+ H_EXTENSION_TABLE = 54,
+ H_USER_NAME = 56
+};
+
+enum {
+ HX_TABLE_SIZE = 0,
+ HX_MOUSE_X = 1,
+ HX_MOUSE_Y = 2,
+ HX_UNICODE_TABLE = 3,
+ HX_FLAGS = 4,
+ HX_FORE_COLOUR = 5,
+ HX_BACK_COLOUR = 6
+};
+
+/**
+ * Stores undo information
+ */
+struct undo_struct {
+ undo_struct *next;
+ undo_struct *prev;
+ long pc;
+ long diff_size;
+ zword frame_count;
+ zword stack_size;
+ zword frame_offset;
+ // undo diff and stack data follow
+};
+typedef undo_struct undo_t;
+
+/**
+ * Story file header data
+ */
+struct Header {
+private:
+ struct StoryEntry {
+ Story _storyId;
+ zword _release;
+ char _serial[7];
+ };
+ static const StoryEntry RECORDS[25];
+public:
+ zbyte h_version;
+ zbyte h_config;
+ zword h_release;
+ zword h_resident_size;
+ zword h_start_pc;
+ zword h_dictionary;
+ zword h_objects;
+ zword h_globals;
+ zword h_dynamic_size;
+ zword h_flags;
+ zbyte h_serial[6];
+ zword h_abbreviations;
+ zword h_file_size;
+ zword h_checksum;
+ zbyte h_interpreter_number;
+ zbyte h_interpreter_version;
+ zbyte h_screen_rows;
+ zbyte h_screen_cols;
+ zword h_screen_width;
+ zword h_screen_height;
+ zbyte h_font_height = 1;
+ zbyte h_font_width = 1;
+ zword h_functions_offset;
+ zword h_strings_offset;
+ zbyte h_default_background;
+ zbyte h_default_foreground;
+ zword h_terminating_keys;
+ zword h_line_width;
+ zbyte h_standard_high;
+ zbyte h_standard_low;
+ zword h_alphabet;
+ zword h_extension_table;
+ zbyte h_user_name[8];
+
+ zword hx_table_size;
+ zword hx_mouse_x;
+ zword hx_mouse_y;
+ zword hx_unicode_table;
+ zword hx_flags;
+ zword hx_fore_colour;
+ zword hx_back_colour;
+
+ Story _storyId;
+
+ /**
+ * Constructor
+ */
+ Header() : h_version(0), h_config(0), h_release(0), h_resident_size(0), h_start_pc(0),
+ h_dictionary(0), h_objects(0), h_globals(0), h_dynamic_size(0), h_flags(0),
+ h_abbreviations(0), h_file_size(0), h_checksum(0), h_interpreter_number(0),
+ h_interpreter_version(0), h_screen_rows(0), h_screen_cols(0), h_screen_width(0),
+ h_screen_height(0), h_font_height(1), h_font_width(1), h_functions_offset(0),
+ h_strings_offset(0), h_default_background(0), h_default_foreground(0),
+ h_terminating_keys(0), h_line_width(0), h_standard_high(1), h_standard_low(1),
+ h_alphabet(0), h_extension_table(0),
+ hx_table_size(0), hx_mouse_x(0), hx_mouse_y(0), hx_unicode_table(0),
+ hx_flags(0), hx_fore_colour(0), hx_back_colour(0), _storyId(UNKNOWN) {
+ Common::fill(&h_serial[0], &h_serial[6], '\0');
+ Common::fill(&h_user_name[0], &h_user_name[8], '\0');
+ }
+
+ /**
+ * Load the header
+ */
+ void loadHeader(Common::SeekableReadStream &f);
+};
+
+class Mem : public Header, public virtual UserOptions {
+protected:
+ Common::SeekableReadStream *story_fp;
+ uint blorb_ofs, blorb_len;
+ uint story_size;
+ byte *pcp;
+ byte *zmp;
+
+ undo_t *first_undo, *last_undo, *curr_undo;
+ zbyte *undo_mem, *prev_zmp, *undo_diff;
+ int undo_count;
+ int reserve_mem;
+private:
+ /**
+ * Handles setting the story file, parsing it if it's a Blorb file
+ */
+ void initializeStoryFile();
+
+ /**
+ * Setup undo data
+ */
+ void initializeUndo();
+
+ /**
+ * Handles loading the game header
+ */
+ void loadGameHeader();
+protected:
+ /**
+ * Read a value from the header extension (former mouse table).
+ */
+ zword get_header_extension(int entry);
+
+ /**
+ * Set an entry in the header extension (former mouse table).
+ */
+ void set_header_extension(int entry, zword val);
+
+ /**
+ * Set all header fields which hold information about the interpreter.
+ */
+ void restart_header();
+
+ /**
+ * Write a byte value to the dynamic Z-machine memory.
+ */
+ void storeb(zword addr, zbyte value);
+
+ /**
+ * Write a word value to the dynamic Z-machine memory.
+ */
+ void storew(zword addr, zword value);
+
+ /**
+ * Free count undo blocks from the beginning of the undo list
+ */
+ void free_undo(int count);
+
+ /**
+ * Generates a runtime error
+ */
+ virtual void runtimeError(ErrorCode errNum) = 0;
+
+ /**
+ * Called when the flags are changed
+ */
+ virtual void flagsChanged(zbyte value) = 0;
+
+ /**
+ * Close the story file and deallocate memory.
+ */
+ void reset_memory();
+
+ /**
+ * Set diff to a Quetzal-like difference between a and b,
+ * copying a to b as we go. It is assumed that diff points to a
+ * buffer which is large enough to hold the diff.
+ * mem_size is the number of bytes to compare.
+ * Returns the number of bytes copied to diff.
+ *
+ */
+ long mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff);
+
+ /**
+ * Applies a quetzal-like diff to dest
+ */
+ void mem_undiff(zbyte *diff, long diff_length, zbyte *dest);
+public:
+ /**
+ * Constructor
+ */
+ Mem();
+
+ /**
+ * Initialize
+ */
+ void initialize();
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/frotz/processor.cpp b/engines/glk/frotz/processor.cpp
new file mode 100644
index 0000000000..1cdc67a589
--- /dev/null
+++ b/engines/glk/frotz/processor.cpp
@@ -0,0 +1,664 @@
+/* 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/frotz/processor.h"
+#include "glk/frotz/frotz.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Stubs to replace with actual code
+zword save_undo() { return 0; }
+zword restore_undo() { return 0; }
+
+
+Opcode Processor::var_opcodes[64] = {
+ &Processor::__illegal__,
+ &Processor::z_je,
+ &Processor::z_jl,
+ &Processor::z_jg,
+ &Processor::z_dec_chk,
+ &Processor::z_inc_chk,
+ &Processor::z_jin,
+ &Processor::z_test,
+ &Processor::z_or,
+ &Processor::z_and,
+ &Processor::z_test_attr,
+ &Processor::z_set_attr,
+ &Processor::z_clear_attr,
+ &Processor::z_store,
+ &Processor::z_insert_obj,
+ &Processor::z_loadw,
+ &Processor::z_loadb,
+ &Processor::z_get_prop,
+ &Processor::z_get_prop_addr,
+ &Processor::z_get_next_prop,
+ &Processor::z_add,
+ &Processor::z_sub,
+ &Processor::z_mul,
+ &Processor::z_div,
+ &Processor::z_mod,
+ &Processor::z_call_s,
+ &Processor::z_call_n,
+ &Processor::z_set_colour,
+ &Processor::z_throw,
+ &Processor::__illegal__,
+ &Processor::__illegal__,
+ &Processor::__illegal__,
+ &Processor::z_call_s,
+ &Processor::z_storew,
+ &Processor::z_storeb,
+ &Processor::z_put_prop,
+ &Processor::z_read,
+ &Processor::z_print_char,
+ &Processor::z_print_num,
+ &Processor::z_random,
+ &Processor::z_push,
+ &Processor::z_pull,
+ &Processor::z_split_window,
+ &Processor::z_set_window,
+ &Processor::z_call_s,
+ &Processor::z_erase_window,
+ &Processor::z_erase_line,
+ &Processor::z_set_cursor,
+ &Processor::z_get_cursor,
+ &Processor::z_set_text_style,
+ &Processor::z_buffer_mode,
+ &Processor::z_output_stream,
+ &Processor::z_input_stream,
+ &Processor::z_sound_effect,
+ &Processor::z_read_char,
+ &Processor::z_scan_table,
+ &Processor::z_not,
+ &Processor::z_call_n,
+ &Processor::z_call_n,
+ &Processor::z_tokenise,
+ &Processor::z_encode_text,
+ &Processor::z_copy_table,
+ &Processor::z_print_table,
+ &Processor::z_check_arg_count
+};
+
+Opcode Processor::ext_opcodes[64] = {
+ &Processor::z_save,
+ &Processor::z_restore,
+ &Processor::z_log_shift,
+ &Processor::z_art_shift,
+ &Processor::z_set_font,
+ &Processor::__illegal__, // glkify - Processor::z_draw_picture,
+ &Processor::__illegal__, // glkify - Processor::z_picture_data,
+ &Processor::__illegal__, // glkify - Processor::z_erase_picture,
+ &Processor::__illegal__, // glkify - Processor::z_set_margins,
+ &Processor::z_save_undo,
+ &Processor::z_restore_undo,
+ &Processor::z_print_unicode,
+ &Processor::z_check_unicode,
+ &Processor::z_set_true_colour, // spec 1.1
+ &Processor::__illegal__,
+ &Processor::__illegal__,
+ &Processor::__illegal__, // glkify - Processor::z_move_window,
+ &Processor::__illegal__, // glkify - Processor::z_window_size,
+ &Processor::__illegal__, // glkify - Processor::z_window_style,
+ &Processor::__illegal__, // glkify - Processor::z_get_wind_prop,
+ &Processor::__illegal__, // glkify - Processor::z_scroll_window,
+ &Processor::z_pop_stack,
+ &Processor::__illegal__, // glkify - Processor::z_read_mouse,
+ &Processor::__illegal__, // glkify - Processor::z_mouse_window,
+ &Processor::z_push_stack,
+ &Processor::__illegal__, // glkify - Processor::z_put_wind_prop,
+ &Processor::z_print_form,
+ &Processor::z_make_menu,
+ &Processor::__illegal__, // glkify - Processor::z_picture_table
+ &Processor::z_buffer_screen, // spec 1.1
+};
+
+Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+ GlkInterface(syst, gameDesc),
+ _finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
+ zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
+ _randomInterval(0), _randomCtr(0), first_restart(true), script_valid(false),
+ _bufPos(0), _locked(false), _prevC('\0'), script_width(0),
+ sfp(nullptr), rfp(nullptr), pfp(nullptr) {
+ static const Opcode OP0_OPCODES[16] = {
+ &Processor::z_rtrue,
+ &Processor::z_rfalse,
+ &Processor::z_print,
+ &Processor::z_print_ret,
+ &Processor::z_nop,
+ &Processor::z_save,
+ &Processor::z_restore,
+ &Processor::z_restart,
+ &Processor::z_ret_popped,
+ &Processor::z_catch,
+ &Processor::z_quit,
+ &Processor::z_new_line,
+ &Processor::z_show_status,
+ &Processor::z_verify,
+ &Processor::__extended__,
+ &Processor::z_piracy
+ };
+ static const Opcode OP1_OPCODES[16] = {
+ &Processor::z_jz,
+ &Processor::z_get_sibling,
+ &Processor::z_get_child,
+ &Processor::z_get_parent,
+ &Processor::z_get_prop_len,
+ &Processor::z_inc,
+ &Processor::z_dec,
+ &Processor::z_print_addr,
+ &Processor::z_call_s,
+ &Processor::z_remove_obj,
+ &Processor::z_print_obj,
+ &Processor::z_ret,
+ &Processor::z_jump,
+ &Processor::z_print_paddr,
+ &Processor::z_load,
+ &Processor::z_call_n
+ };
+
+ Common::copy(&OP0_OPCODES[0], &OP0_OPCODES[16], op0_opcodes);
+ Common::copy(&OP1_OPCODES[0], &OP1_OPCODES[16], op1_opcodes);
+ Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
+ Common::fill(&zargs[0], &zargs[8], 0);
+ Common::fill(&_buffer[0], &_buffer[TEXT_BUFFER_SIZE], '\0');
+ Common::fill(&_errorCount[0], &_errorCount[ERR_NUM_ERRORS], 0);
+}
+
+void Processor::initialize() {
+ Mem::initialize();
+ GlkInterface::initialize();
+
+ if (h_version <= V4) {
+ op0_opcodes[9] = &Processor::z_pop;
+ op1_opcodes[15] = &Processor::z_not;
+ } else {
+ op0_opcodes[9] = &Processor::z_catch;
+ op1_opcodes[15] = &Processor::z_call_n;
+ }
+}
+
+void Processor::load_operand(zbyte type) {
+ zword value;
+
+ if (type & 2) {
+ // variable
+ zbyte variable;
+
+ CODE_BYTE(variable);
+
+ if (variable == 0)
+ value = *_sp++;
+ else if (variable < 16)
+ value = *(_fp - variable);
+ else {
+ zword addr = h_globals + 2 * (variable - 16);
+ LOW_WORD(addr, value);
+ }
+ } else if (type & 1) {
+ // small constant
+ zbyte bvalue;
+
+ CODE_BYTE(bvalue);
+ value = bvalue;
+
+ } else {
+ // large constant
+ CODE_WORD(value);
+ }
+
+ zargs[zargc++] = value;
+}
+
+void Processor::load_all_operands(zbyte specifier) {
+ for (int i = 6; i >= 0; i -= 2) {
+ zbyte type = (specifier >> i) & 0x03;
+
+ if (type == 3)
+ break;
+
+ load_operand(type);
+ }
+}
+
+void Processor::interpret() {
+ do {
+ zbyte opcode;
+ CODE_BYTE(opcode);
+ zargc = 0;
+
+ if (opcode < 0x80) {
+ // 2OP opcodes
+ load_operand((zbyte)(opcode & 0x40) ? 2 : 1);
+ load_operand((zbyte)(opcode & 0x20) ? 2 : 1);
+
+ (*this.*var_opcodes[opcode & 0x1f])();
+
+ } else if (opcode < 0xb0) {
+ // 1OP opcodes
+ load_operand((zbyte)(opcode >> 4));
+
+ (*this.*op1_opcodes[opcode & 0x0f])();
+
+ } else if (opcode < 0xc0) {
+ // 0OP opcodes
+ (*this.*op0_opcodes[opcode - 0xb0])();
+
+ } else {
+ // VAR opcodes
+ zbyte specifier1;
+ zbyte specifier2;
+
+ if (opcode == 0xec || opcode == 0xfa) { // opcodes 0xec
+ CODE_BYTE(specifier1); // and 0xfa are
+ CODE_BYTE(specifier2); // call opcodes
+ load_all_operands(specifier1); // with up to 8
+ load_all_operands(specifier2); // arguments
+ } else {
+ CODE_BYTE(specifier1);
+ load_all_operands(specifier1);
+ }
+
+ (*this.*var_opcodes[opcode - 0xc0])();
+ }
+
+#if defined(DJGPP) && defined(SOUND_SUPPORT)
+ if (end_of_sound_flag)
+ end_of_sound();
+#endif
+ } while (!_finished);
+
+ _finished--;
+}
+
+void Processor::call(zword routine, int argc, zword *args, int ct) {
+ long pc;
+ zword value;
+ zbyte count;
+ int i;
+
+ if (_sp - _stack < 4)
+ runtimeError(ERR_STK_OVF);
+
+ GET_PC(pc);
+
+ *--_sp = (zword)(pc >> 9);
+ *--_sp = (zword)(pc & 0x1ff);
+ *--_sp = (zword)(_fp - _stack - 1);
+ *--_sp = (zword)(argc | (ct << (_save_quetzal ? 12 : 8)));
+
+ _fp = _sp;
+ _frameCount++;
+
+ // Calculate byte address of routine
+ if (h_version <= V3)
+ pc = (long)routine << 1;
+ else if (h_version <= V5)
+ pc = (long)routine << 2;
+ else if (h_version <= V7)
+ pc = ((long)routine << 2) + ((long)h_functions_offset << 3);
+ else if (h_version <= V8)
+ pc = (long)routine << 3;
+ else {
+ // h_version == V9
+ long indirect = (long)routine << 2;
+ HIGH_LONG(indirect, pc);
+ }
+
+ if ((uint)pc >= story_size)
+ runtimeError(ERR_ILL_CALL_ADDR);
+
+ SET_PC(pc);
+
+ // Initialise local variables
+ CODE_BYTE(count);
+
+ if (count > 15)
+ runtimeError(ERR_CALL_NON_RTN);
+ if (_sp - _stack < count)
+ runtimeError(ERR_STK_OVF);
+
+ if (_save_quetzal)
+ _fp[0] |= (zword)count << 8; // Save local var count for Quetzal.
+
+ value = 0;
+
+ for (i = 0; i < count; i++) {
+ if (h_version <= V4) // V1 to V4 games provide default
+ CODE_WORD(value); // values for all local variables
+
+ *--_sp = (zword)((argc-- > 0) ? args[i] : value);
+ }
+
+ // Start main loop for direct calls
+ if (ct == 2)
+ interpret();
+}
+
+void Processor::ret(zword value) {
+ long pc;
+ int ct;
+
+ if (_sp > _fp)
+ runtimeError(ERR_STK_UNDF);
+
+ _sp = _fp;
+
+ ct = *_sp++ >> (_save_quetzal ? 12 : 8);
+ _frameCount--;
+ _fp = _stack + 1 + *_sp++;
+ pc = *_sp++;
+ pc = ((long)*_sp++ << 9) | pc;
+
+ SET_PC(pc);
+
+ // Handle resulting value
+ if (ct == 0)
+ store(value);
+ if (ct == 2)
+ *--_sp = value;
+
+ // Stop main loop for direct calls
+ if (ct == 2)
+ _finished++;
+}
+
+void Processor::branch(bool flag) {
+ long pc;
+ zword offset;
+ zbyte specifier;
+ zbyte off1;
+ zbyte off2;
+
+ CODE_BYTE(specifier);
+ off1 = specifier & 0x3f;
+
+ if (!flag)
+ specifier ^= 0x80;
+
+ if (!(specifier & 0x40)) {
+ // it's a long branch
+ if (off1 & 0x20) // propagate sign bit
+ off1 |= 0xc0;
+
+ CODE_BYTE(off2);
+ offset = (off1 << 8) | off2;
+ } else {
+ // It's a short branch
+ offset = off1;
+ }
+
+ if (specifier & 0x80) {
+ if (offset > 1) {
+ // normal branch
+ GET_PC(pc);
+ pc += (short)offset - 2;
+ SET_PC(pc);
+ } else {
+ // special case, return 0 or 1
+ ret(offset);
+ }
+ }
+}
+
+void Processor::store(zword value) {
+ zbyte variable;
+
+ CODE_BYTE(variable);
+
+ if (variable == 0)
+ *--_sp = value;
+ else if (variable < 16)
+ *(_fp - variable) = value;
+ else {
+ zword addr = h_globals + 2 * (variable - 16);
+ SET_WORD(addr, value);
+ }
+}
+
+int Processor::direct_call(zword addr) {
+ zword saved_zargs[8];
+ int saved_zargc;
+ int i;
+
+ // Calls to address 0 return false
+ if (addr == 0)
+ return 0;
+
+ // Save operands and operand count
+ for (i = 0; i < 8; i++)
+ saved_zargs[i] = zargs[i];
+
+ saved_zargc = zargc;
+
+ // Call routine directly
+ call(addr, 0, 0, 2);
+
+ // Restore operands and operand count
+ for (i = 0; i < 8; i++)
+ zargs[i] = saved_zargs[i];
+
+ zargc = saved_zargc;
+
+ // Resulting value lies on top of the stack
+ return (short)*_sp++;
+}
+
+void Processor::seed_random(int value) {
+ if (value == 0) {
+ // Now using random values
+ _randomInterval = 0;
+ } else if (value < 1000) {
+ // special seed value
+ _randomCtr = 0;
+ _randomInterval = value;
+ } else {
+ // standard seed value
+ _random.setSeed(value);
+ _randomInterval = 0;
+ }
+}
+
+void Processor::__extended__() {
+ zbyte opcode;
+ zbyte specifier;
+
+ CODE_BYTE(opcode);
+ CODE_BYTE(specifier);
+
+ load_all_operands(specifier);
+
+ if (opcode < 0x1e) // extended opcodes from 0x1e on
+ (*this.*ext_opcodes[opcode])(); // are reserved for future spec'
+}
+
+void Processor::__illegal__() {
+ runtimeError(ERR_ILL_OPCODE);
+}
+
+void Processor::z_catch() {
+ store(_save_quetzal ? _frameCount : (zword)(_fp - _stack));
+}
+
+void Processor::z_throw() {
+ if (_save_quetzal) {
+ if (zargs[1] > _frameCount)
+ runtimeError(ERR_BAD_FRAME);
+
+ // Unwind the stack a frame at a time.
+ for (; _frameCount > zargs[1]; --_frameCount)
+ _fp = _stack + 1 + _fp[1];
+ } else {
+ if (zargs[1] > STACK_SIZE)
+ runtimeError(ERR_BAD_FRAME);
+
+ _fp = _stack + zargs[1];
+ }
+
+ ret(zargs[0]);
+}
+
+void Processor::z_call_n() {
+ if (zargs[0] != 0)
+ call(zargs[0], zargc - 1, zargs + 1, 1);
+}
+
+void Processor::z_call_s() {
+ if (zargs[0] != 0)
+ call(zargs[0], zargc - 1, zargs + 1, 0);
+ else
+ store(0);
+}
+
+void Processor::z_check_arg_count() {
+ if (_fp == _stack + STACK_SIZE)
+ branch(zargs[0] == 0);
+ else
+ branch(zargs[0] <= (*_fp & 0xff));
+}
+
+void Processor::z_jump() {
+ long pc;
+ GET_PC(pc);
+
+ pc += (short)zargs[0] - 2;
+
+ if ((uint)pc >= story_size)
+ runtimeError(ERR_ILL_JUMP_ADDR);
+
+ SET_PC(pc);
+}
+
+void Processor::z_nop() {
+ // Do nothing
+}
+
+void Processor::z_quit() {
+ _finished = 9999;
+}
+
+void Processor::z_ret() {
+ ret(zargs[0]);
+}
+
+void Processor::z_ret_popped() {
+ ret(*_sp++);
+}
+
+void Processor::z_rfalse() {
+ ret(0);
+}
+
+void Processor::z_rtrue() {
+ ret(1);
+}
+
+void Processor::z_random() {
+ if ((short) zargs[0] <= 0) {
+ // set random seed
+ seed_random(- (short) zargs[0]);
+ store(0);
+
+ } else {
+ // generate random number
+ zword result;
+ if (_randomInterval != 0) {
+ // ...in special mode
+ result = _randomCtr++;
+ if (_randomCtr == _randomInterval)
+ _randomCtr = 0;
+ } else {
+ // ...in standard mode
+ result = _random.getRandomNumber(0xffff);
+ }
+
+ store((zword)(result % zargs[0] + 1));
+ }
+}
+
+void Processor::z_sound_effect() {
+ zword number = zargs[0];
+ zword effect = zargs[1];
+ zword volume = zargs[2];
+
+ if (zargc < 1)
+ number = 0;
+ if (zargc < 2)
+ effect = EFFECT_PLAY;
+ if (zargc < 3)
+ volume = 8;
+
+ if (number >= 3 || number == 0) {
+ _soundLocked = true;
+
+ if (_storyId == LURKING_HORROR && (number == 9 || number == 16)) {
+ if (effect == EFFECT_PLAY) {
+ next_sample = number;
+ next_volume = volume;
+
+ _soundLocked = false;
+
+ if (!_soundPlaying)
+ start_next_sample();
+ } else {
+ _soundLocked = false;
+ }
+ return;
+ }
+
+ _soundPlaying = false;
+
+ switch (effect) {
+
+ case EFFECT_PREPARE:
+ os_prepare_sample (number);
+ break;
+ case EFFECT_PLAY:
+ start_sample(number, lo (volume), hi (volume), (zargc == 4) ? zargs[3] : 0);
+ break;
+ case EFFECT_STOP:
+ os_stop_sample (number);
+ break;
+ case EFFECT_FINISH_WITH:
+ os_finish_with_sample (number);
+ break;
+ }
+
+ _soundLocked = false;
+ } else {
+ os_beep(number);
+ }
+}
+
+void Processor::z_piracy() {
+ branch(!_piracy);
+}
+
+void Processor::z_save_undo(void) {
+ store((zword)save_undo());
+}
+
+void Processor::z_restore_undo(void) {
+ store((zword)restore_undo());
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
new file mode 100644
index 0000000000..b5c113def0
--- /dev/null
+++ b/engines/glk/frotz/processor.h
@@ -0,0 +1,1584 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_FROTZ_PROCESSOR
+#define GLK_FROTZ_PROCESSOR
+
+#include "glk/frotz/mem.h"
+#include "glk/frotz/glk_interface.h"
+#include "glk/frotz/frotz_types.h"
+#include "common/stack.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define TEXT_BUFFER_SIZE 200
+
+#define CODE_BYTE(v) v = codeByte()
+#define CODE_WORD(v) v = codeWord()
+#define CODE_IDX_WORD(v,i) v = codeWordIdx(i)
+#define GET_PC(v) v = getPC()
+#define SET_PC(v) setPC(v)
+
+enum string_type {
+ LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY
+};
+
+class Processor;
+class Quetzal;
+typedef void (Processor::*Opcode)();
+
+/**
+ * Zcode processor
+ */
+class Processor : public GlkInterface, public virtual Mem {
+ friend class Quetzal;
+private:
+ Opcode op0_opcodes[16];
+ Opcode op1_opcodes[16];
+ static const char *const ERR_MESSAGES[ERR_NUM_ERRORS];
+ static Opcode var_opcodes[64];
+ static Opcode ext_opcodes[64];
+
+ int _finished;
+ zword zargs[8];
+ int zargc;
+ uint _randomInterval;
+ uint _randomCtr;
+ bool first_restart;
+ bool script_valid;
+
+ // Stack data
+ zword _stack[STACK_SIZE];
+ zword *_sp;
+ zword *_fp;
+ zword _frameCount;
+
+ // Text related fields
+ static zchar ZSCII_TO_LATIN1[];
+ zchar *_decoded, *_encoded;
+ int _resolution;
+ int _errorCount[ERR_NUM_ERRORS];
+
+ // Buffer related fields
+ bool _locked;
+ zchar _prevC;
+ zchar _buffer[TEXT_BUFFER_SIZE];
+ size_t _bufPos;
+
+ // Stream related fields
+ int script_width;
+ strid_t sfp, rfp, pfp;
+ Common::FixedStack<Redirect, MAX_NESTING> _redirect;
+private:
+ /**
+ * \defgroup General support methods
+ * @{
+ */
+
+ /**
+ * Load an operand, either a variable or a constant.
+ */
+ void load_operand(zbyte type);
+
+ /**
+ * Given the operand specifier byte, load all (up to four) operands
+ * for a VAR or EXT opcode.
+ */
+ void load_all_operands(zbyte specifier);
+
+ /**
+ * Call a subroutine. Save PC and FP then load new PC and initialise
+ * new stack frame. Note that the caller may legally provide less or
+ * more arguments than the function actually has. The call type "ct"
+ * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
+ */
+ void call(zword routine, int argc, zword *args, int ct);
+
+ /**
+ * Return from the current subroutine and restore the previous _stack
+ * frame. The result may be stored (0), thrown away (1) or pushed on
+ * the stack (2). In the latter case a direct call has been finished
+ * and we must exit the interpreter loop.
+ */
+ void ret(zword value);
+
+ /**
+ * Take a jump after an instruction based on the flag, either true or
+ * false. The branch can be short or long; it is encoded in one or two
+ * bytes respectively. When bit 7 of the first byte is set, the jump
+ * takes place if the flag is true; otherwise it is taken if the flag
+ * is false. When bit 6 of the first byte is set, the branch is short;
+ * otherwise it is long. The offset occupies the bottom 6 bits of the
+ * first byte plus all the bits in the second byte for long branches.
+ * Uniquely, an offset of 0 means return false, and an offset of 1 is
+ * return true.
+ */
+ void branch(bool flag);
+
+ /**
+ * Store an operand, either as a variable or pushed on the stack.
+ */
+ void store(zword value);
+
+ /*
+ * Call the interpreter loop directly. This is necessary when
+ *
+ * - a sound effect has been finished
+ * - a read instruction has timed out
+ * - a newline countdown has hit zero
+ *
+ * The interpreter returns the result value on the stack.
+ */
+ int direct_call(zword addr);
+
+ /**
+ * Set the seed value for the random number generator.
+ */
+ void seed_random(int value);
+
+ /**@}*/
+
+ /**
+ * \defgroup Input support methods
+ * @{
+ */
+
+ /**
+ * Copy the contents of the text buffer to the output streams.
+ */
+ void flush_buffer();
+
+ /**
+ * High level output function.
+ */
+ void print_char(zchar c);
+
+ /**
+ * Print a string of ASCII characters.
+ */
+ void print_string(const char *s);
+
+ /**
+ * Print an unsigned 32bit number in decimal or hex.
+ */
+ void print_long(uint value, int base);
+
+ /**
+ * High level newline function.
+ */
+ void new_line();
+
+ /**
+ * Returns true if the buffer is empty
+ */
+ bool bufferEmpty() const { return !_bufPos; }
+
+ /**
+ * An error has occurred. Ignore it, pass it to os_fatal or report
+ * it according to err_report_mode.
+ * @param errNum Numeric code for error (1 to ERR_NUM_ERRORS)
+ */
+ void runtimeError(ErrorCode errNum);
+
+ /**@}*/
+
+ /**
+ * \defgroup Input support methods
+ * @{
+ */
+
+ /**
+ * Check if the given key is an input terminator.
+ */
+ bool is_terminator(zchar key);
+
+ /**
+ * Ask the user a question; return true if the answer is yes.
+ */
+ bool read_yes_or_no(const char *s);
+
+ /**
+ * Read a string from the current input stream.
+ */
+ void read_string(int max, zchar *buffer);
+
+ /**
+ * Ask the user to type in a number and return it.
+ */
+ int read_number();
+
+ /**@}*/
+
+ /**
+ * \defgroup Memory support methods
+ * @{
+ */
+
+ /**
+ * Called when the H_FLAGS field of the header has changed
+ */
+ virtual void flagsChanged(zbyte value) override;
+
+ /**
+ * This function does the dirty work for z_save_undo.
+ */
+ int save_undo();
+
+ /**
+ * This function does the dirty work for z_restore_undo.
+ */
+ int restore_undo();
+
+ /**
+ * Begin output redirection to the memory of the Z-machine.
+ */
+ void memory_open(zword table, zword xsize, bool buffering);
+
+ /**
+ * End of output redirection.
+ */
+ void memory_close();
+
+ /**
+ * Redirect a newline to the memory of the Z-machine.
+ */
+ void memory_new_line();
+
+ /**
+ * Redirect a string of characters to the memory of the Z-machine.
+ */
+ void memory_word(const zchar *s);
+
+ /**@}*/
+
+ /**
+ * \defgroup Object support methods
+ * @{
+ */
+
+ /**
+ * Calculate the address of an object.
+ */
+ zword object_address(zword obj);
+
+ /**
+ * Return the address of the given object's name.
+ */
+ zword object_name(zword object);
+
+ /**
+ * Calculate the start address of the property list associated with an object.
+ */
+ zword first_property(zword obj);
+
+ /**
+ * Calculate the address of the next property in a property list.
+ */
+ zword next_property(zword prop_addr);
+
+ /**
+ * Unlink an object from its parent and siblings.
+ */
+ void unlink_object(zword object);
+
+ /**@}*/
+
+ /**
+ * \defgroup Screen support methods
+ * @{
+ */
+
+ void screen_char(zchar c);
+ void screen_new_line();
+ void screen_word(const zchar *s);
+ void screen_mssg_on();
+ void screen_mssg_off();
+
+ /**@}*/
+
+ /**
+ * \defgroup Stream support methods
+ * @{
+ */
+
+ /**
+ * Waits for the user to type an input line
+ */
+ zchar console_read_input(int max, zchar *buf, zword timeout, bool continued);
+
+ /**
+ * Waits for a keypress
+ */
+ zchar console_read_key(zword timeout);
+
+ /**
+ * Write a single character to the scrollback buffer.
+ *
+ */
+ void scrollback_char(zchar c);
+
+ /**
+ * Write a string to the scrollback buffer.
+ */
+ void scrollback_word(const zchar *s);
+
+ /**
+ * Send an input line to the scrollback buffer.
+ */
+ void scrollback_write_input(const zchar *buf, zchar key);
+
+ /**
+ * Remove an input line from the scrollback buffer.
+ */
+ void scrollback_erase_input(const zchar *buf);
+
+ /**
+ * Start printing a "debugging" message.
+ */
+ void stream_mssg_on();
+
+ /**
+ * Stop printing a "debugging" message.
+ */
+ void stream_mssg_off();
+
+ /**
+ * Send a single character to the output stream.
+ */
+ void stream_char(zchar c);
+
+ /**
+ * Send a string of characters to the output streams.
+ */
+ void stream_word(const zchar *s);
+
+ /**
+ * Send a newline to the output streams.
+ */
+ void stream_new_line();
+
+ /**
+ * Read a single keystroke from the current input stream.
+ */
+ zchar stream_read_key(zword timeout, zword routine, bool hot_keys);
+
+ /**
+ * Read a line of input from the current input stream.
+ */
+ zchar stream_read_input(int max, zchar *buf, zword timeout, zword routine,
+ bool hot_keys, bool no_scripting);
+
+ /*
+ * script_open
+ *
+ * Open the transscript file. 'AMFV' makes this more complicated as it
+ * turns transscription on/off several times to exclude some text from
+ * the transscription file. This wasn't a problem for the original V4
+ * interpreters which always sent transscription to the printer, but it
+ * means a problem to modern interpreters that offer to open a new file
+ * every time transscription is turned on. Our solution is to append to
+ * the old transscription file in V1 to V4, and to ask for a new file
+ * name in V5+.
+ *
+ */
+ void script_open();
+
+ /*
+ * Stop transscription.
+ */
+ void script_close();
+
+ /**
+ * Write a newline to the transscript file.
+ */
+ void script_new_line();
+
+ /**
+ * Write a single character to the transscript file.
+ */
+ void script_char(zchar c);
+
+ /**
+ * Write a string to the transscript file.
+ */
+ void script_word(const zchar *s);
+
+ /**
+ * Send an input line to the transscript file.
+ */
+ void script_write_input(const zchar *buf, zchar key);
+
+ /**
+ * Remove an input line from the transscript file.
+ */
+ void script_erase_input(const zchar *buf);
+
+ /**
+ * Start sending a "debugging" message to the transscript file.
+ */
+ void script_mssg_on();
+
+ /**
+ * Stop writing a "debugging" message.
+ */
+ void script_mssg_off();
+
+ /**
+ * Open a file to record the player's input.
+ */
+ void record_open();
+
+ /**
+ * Stop recording the player's input.
+ */
+ void record_close();
+
+ /**
+ * Helper function for record_char.
+ */
+ void record_code(int c, bool force_encoding);
+
+ /**
+ * Write a character to the command file.
+ */
+ void record_char(zchar c);
+
+ /**
+ * Copy a keystroke to the command file.
+ */
+ void record_write_key(zchar key);
+
+ /**
+ * Copy a line of input to a command file.
+ */
+ void record_write_input(const zchar *buf, zchar key);
+
+ /**
+ * Open a file of commands for playback.
+ */
+ void replay_open();
+
+ /**
+ * Stop playback of commands.
+ */
+ void replay_close();
+
+ /*
+ * Helper function for replay_key and replay_line.
+ */
+ int replay_code();
+
+ /**
+ * Read a character from the command file.
+ */
+ zchar replay_char();
+
+ /**
+ * Read a keystroke from a command file.
+ */
+ zchar replay_read_key();
+
+ /*
+ * Read a line of input from a command file.
+ */
+ zchar replay_read_input(zchar *buf);
+
+ /**@}*/
+
+ /**
+ * \defgroup Text support methods
+ * @{
+ */
+
+ /**
+ * Map a ZSCII character into Unicode.
+ */
+ zchar translate_from_zscii(zbyte c);
+
+ /**
+ * Convert a Unicode character to ZSCII, returning 0 on failure.
+ */
+ zbyte unicode_to_zscii(zchar c);
+
+ /**
+ * Map a Unicode character onto the ZSCII alphabet.
+ *
+ */
+ zbyte translate_to_zscii(zchar c);
+
+ /**
+ * Return a character from one of the three character sets.
+ */
+ zchar alphabet(int set, int index);
+
+ /**
+ * Find the number of bytes used for dictionary resolution.
+ */
+ void find_resolution();
+
+ /**
+ * Copy a ZSCII string from the memory to the global "decoded" string.
+ */
+ void load_string(zword addr, zword length);
+
+ /**
+ * Encode the Unicode text in the global "decoded" string then write
+ * the result to the global "encoded" array. (This is used to look up
+ * words in the dictionary.) Up to V3 the vocabulary resolution is
+ * two, from V4 it is three, and from V9 it is any number of words.
+ * Because each word contains three Z-characters, that makes six or
+ * nine Z-characters respectively. Longer words are chopped to the
+ * proper size, shorter words are are padded out with 5's. For word
+ * completion we pad with 0s and 31s, the minimum and maximum
+ * Z-characters.
+ */
+ void encode_text(int padding);
+
+ /**
+ * Convert _encoded text to Unicode. The _encoded text consists of 16bit
+ * words. Every word holds 3 Z-characters (5 bits each) plus a spare
+ * bit to mark the last word. The Z-characters translate to ZSCII by
+ * looking at the current current character set. Some select another
+ * character set, others refer to abbreviations.
+ *
+ * There are several different string types:
+ *
+ * LOW_STRING - from the lower 64KB (byte address)
+ * ABBREVIATION - from the abbreviations table (word address)
+ * HIGH_STRING - from the end of the memory map (packed address)
+ * EMBEDDED_STRING - from the instruction stream (at PC)
+ * VOCABULARY - from the dictionary (byte address)
+ *
+ * The last type is only used for word completion.
+ */
+ void decode_text(string_type st, zword addr);
+
+ /**
+ * Print a signed 16bit number.
+ */
+ void print_num(zword value);
+
+ /**
+ * print_object
+ *
+ * Print an object description.
+ *
+ */
+ void print_object(zword object);
+
+ /**
+ * Scan a dictionary searching for the given word. The first argument
+ * can be
+ *
+ * 0x00 - find the first word which is >= the given one
+ * 0x05 - find the word which exactly matches the given one
+ * 0x1f - find the last word which is <= the given one
+ *
+ * The return value is 0 if the search fails.
+ */
+ zword lookup_text(int padding, zword dct);
+
+ /**
+ * tokenise_text
+ *
+ * Translate a single word to a token and append it to the token
+ * buffer. Every token consists of the address of the dictionary
+ * entry, the length of the word and the offset of the word from
+ * the start of the text buffer. Unknown words cause empty slots
+ * if the flag is set (such that the text can be scanned several
+ * times with different dictionaries); otherwise they are zero.
+ *
+ */
+ void tokenise_text(zword text, zword length, zword from, zword parse, zword dct, bool flag);
+
+ /**
+ * Split an input line into words and translate the words to tokens.
+ */
+ void tokenise_line(zword text, zword token, zword dct, bool flag);
+
+ /**
+ * Scan the vocabulary to complete the last word on the input line
+ * (similar to "tcsh" under Unix). The return value is
+ *
+ * 2 ==> completion is impossible
+ * 1 ==> completion is ambiguous
+ * 0 ==> completion is successful
+ *
+ * The function also returns a string in its second argument. In case
+ * of 2, the string is empty; in case of 1, the string is the longest
+ * extension of the last word on the input line that is common to all
+ * possible completions (for instance, if the last word on the input
+ * is "fo" and its only possible completions are "follow" and "folly"
+ * then the string is "ll"); in case of 0, the string is an extension
+ * to the last word that results in the only possible completion.
+ */
+ int completion(const zchar *buffer, zchar *result);
+
+ /**
+ * Convert a Unicode character to lowercase.
+ * Taken from Zip2000 by Kevin Bracey.
+ */
+ zchar unicode_tolower(zchar c);
+
+ /**@}*/
+protected:
+ /**
+ * \defgroup General Opcode methods
+ * @{
+ */
+
+ /*
+ * Load and execute an extended opcode.
+ */
+ void __extended__();
+
+ /*
+ * Exit game because an unknown opcode has been hit.
+ */
+ void __illegal__();
+
+ /*
+ * Store the current _stack frame for later use with z_throw.
+ *
+ * no zargs used
+ */
+ void z_catch();
+
+ /**
+ * Go back to the given _stack frame and return the given value.
+ *
+ * zargs[0] = value to return
+ * zargs[1] = _stack frame
+ */
+ void z_throw();
+
+ /*
+ * Call a subroutine and discard its result.
+ *
+ * zargs[0] = packed address of subroutine
+ * zargs[1] = first argument (optional)
+ * ...
+ * zargs[7] = seventh argument (optional)
+ */
+ void z_call_n();
+
+ /**
+ * Call a subroutine and store its result.
+ *
+ * zargs[0] = packed address of subroutine
+ * zargs[1] = first argument (optional)
+ * ...
+ * zargs[7] = seventh argument (optional)
+ */
+ void z_call_s();
+
+ /**
+ * Branch if subroutine was called with >= n arg's.
+ *
+ * zargs[0] = number of arguments
+ */
+ void z_check_arg_count();
+
+ /**
+ * Jump unconditionally to the given address.
+ *
+ * zargs[0] = PC relative address
+ */
+ void z_jump();
+
+ /*
+ * No operation.
+ *
+ * no zargs used
+ */
+ void z_nop();
+
+ /*
+ * Stop game and exit interpreter.
+ *
+ * no zargs used
+ */
+ void z_quit();
+
+ /*
+ * Return from a subroutine with the given value.
+ *
+ * zargs[0] = value to return
+ */
+ void z_ret();
+
+ /*
+ * Return from a subroutine with a value popped off the stack.
+ *
+ * no zargs used
+ */
+ void z_ret_popped();
+
+ /*
+ * Return from a subroutine with false (0).
+ *
+ * no zargs used
+ */
+ void z_rfalse();
+
+ /*
+ * Return from a subroutine with true (1).
+ *
+ * no zargs used
+ */
+ void z_rtrue();
+
+ /**
+ * Store a random number or set the random number seed.
+ *
+ * zargs[0] = range (positive) or seed value (negative)
+ */
+ void z_random();
+
+ /**
+ * Load / play / stop / discard a sound effect.
+ *
+ * zargs[0] = number of bleep (1 or 2) or sample
+ * zargs[1] = operation to perform (samples only)
+ * zargs[2] = repeats and volume (play sample only)
+ * zargs[3] = end-of-sound routine (play sample only, optional)
+ *
+ * Note: Volumes range from 1 to 8, volume 255 is the default volume.
+ * Repeats are stored in the high byte, 255 is infinite loop.
+ *
+ */
+ void z_sound_effect();
+
+ /**
+ * Branch if the story file is a legal copy
+ */
+ void z_piracy();
+
+ /**
+ * Save the current Z-machine state for a future undo.
+ *
+ * no zargs used
+ */
+ void z_save_undo();
+
+ /**
+ * Restore a Z-machine state from memory.
+ *
+ * no zargs used
+ */
+ void z_restore_undo();
+
+ /**@}*/
+
+ /**
+ * \defgroup Input Opcode methods
+ * @{
+ */
+
+ /**
+ * Add or remove a menu and branch if successful.
+ *
+ * zargs[0] = number of menu
+ * zargs[1] = table of menu entries or 0 to remove menu
+ */
+ void z_make_menu();
+
+ /**
+ * Read a line of input and (in V5+) store the terminating key.
+ *
+ * zargs[0] = address of text buffer
+ * zargs[1] = address of token buffer
+ * zargs[2] = timeout in tenths of a second (optional)
+ * zargs[3] = packed address of routine to be called on timeout
+ */
+ void z_read();
+
+ /**
+ * Read and store a key.
+ *
+ * zargs[0] = input device (must be 1)
+ * zargs[1] = timeout in tenths of a second (optional)
+ * zargs[2] = packed address of routine to be called on timeout
+ */
+ void z_read_char();
+
+ /**
+ * z_read_mouse, write the current mouse status into a table.
+ *
+ * zargs[0] = address of table
+ */
+ void z_read_mouse();
+
+ /**@}*/
+
+ /**
+ * \defgroup Math Opcode methods
+ * @{
+ */
+
+ /**
+ * 16 bit addition.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_add();
+
+ /**
+ * Bitwise AND operation.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_and();
+
+ /**
+ * Arithmetic SHIFT operation.
+ *
+ * zargs[0] = value
+ * zargs[1] = #positions to shift left (positive) or right
+ */
+ void z_art_shift();
+
+ /**
+ * Signed 16bit division.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_div();
+
+ /**
+ * B ranch if the first value equals any of the following.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value (optional)
+ * ...
+ * zargs[3] = fourth value (optional)
+ */
+ void z_je();
+
+ /**
+ * Branch if the first value is greater than the second.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_jg();
+
+ /**
+ * Branch if the first value is less than the second.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_jl();
+
+ /**
+ * Branch if value is zero.
+ *
+ * zargs[0] = value
+ */
+ void z_jz();
+
+ /**
+ * Logical SHIFT operation.
+ *
+ * zargs[0] = value
+ * zargs[1] = #positions to shift left (positive) or right (negative)
+ */
+ void z_log_shift();
+
+ /*
+ * Remainder after signed 16bit division.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_mod();
+
+ /**
+ * 16 bit multiplication.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_mul();
+
+ /**
+ * Bitwise NOT operation.
+ *
+ * zargs[0] = value
+ */
+ void z_not();
+
+ /**
+ * Bitwise OR operation.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_or();
+
+ /**
+ * 16 bit substraction.
+ *
+ * zargs[0] = first value
+ * zargs[1] = second value
+ */
+ void z_sub();
+
+ /**
+ * Branch if all the flags of a bit mask are set in a value.
+ *
+ * zargs[0] = value to be examined
+ * zargs[1] = bit mask
+ */
+ void z_test();
+
+ /**@}*/
+
+ /**
+ * \defgroup Object Opcode methods
+ * @{
+ */
+
+ /**
+ * Branch if the first object is inside the second.
+ *
+ * zargs[0] = first object
+ * zargs[1] = second object
+ */
+ void z_jin();
+
+ /**
+ * Store the child of an object.
+ *
+ * zargs[0] = object
+ */
+ void z_get_child();
+
+ /**
+ * Store the number of the first or next property.
+ *
+ * zargs[0] = object
+ * zargs[1] = address of current property (0 gets the first property)
+ */
+ void z_get_next_prop();
+
+ /**
+ * Store the parent of an object.
+ *
+ * zargs[0] = object
+ */
+ void z_get_parent();
+
+ /**
+ * Store the value of an object property.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of property to be examined
+ */
+ void z_get_prop();
+
+ /**
+ * Store the address of an object property.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of property to be examined
+ */
+ void z_get_prop_addr();
+
+ /**
+ * Store the length of an object property.
+ *
+ * zargs[0] = address of property to be examined
+ */
+ void z_get_prop_len();
+
+ /**
+ * Store the sibling of an object.
+ *
+ * zargs[0] = object
+ */
+ void z_get_sibling();
+
+ /**
+ * Make an object the first child of another object.
+ *
+ * zargs[0] = object to be moved
+ * zargs[1] = destination object
+ */
+ void z_insert_obj();
+
+ /**
+ * Set the value of an object property.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of property to set
+ * zargs[2] = value to set property to
+ */
+ void z_put_prop();
+
+ /**
+ * Unlink an object from its parent and siblings.
+ *
+ * zargs[0] = object
+ */
+ void z_remove_obj();
+
+ /**
+ * Set an object attribute.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of attribute to set
+ */
+ void z_set_attr();
+
+ /**
+ * Branch if an object attribute is set.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of attribute to test
+ */
+ void z_test_attr();
+
+ /**
+ * Clear an object attribute.
+ *
+ * zargs[0] = object
+ * zargs[1] = number of attribute to be cleared
+ */
+ void z_clear_attr();
+
+ /**@}*/
+
+ /**
+ * \defgroup Screen Opcode methods
+ * @{
+ */
+
+ /**
+ * Turn text buffering on/off.
+ *
+ * zargs[0] = new text buffering flag (0 or 1)
+ */
+ void z_buffer_mode();
+
+ /**
+ * Set the screen buffering mode.
+ *
+ * zargs[0] = mode
+ */
+ void z_buffer_screen();
+
+ /**
+ * Erase the line starting at the cursor position.
+ *
+ * zargs[0] = 1 + #units to erase (1 clears to the end of the line)
+ */
+ void z_erase_line();
+
+ /**
+ * Erase a window or the screen to background colour.
+ *
+ * zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
+ */
+ void z_erase_window();
+
+ /**
+ * Write the cursor coordinates into a table.
+ *
+ * zargs[0] = address to write information to
+ */
+ void z_get_cursor();
+
+ /**
+ * Print ASCII text in a rectangular area.
+ *
+ * zargs[0] = address of text to be printed
+ * zargs[1] = width of rectangular area
+ * zargs[2] = height of rectangular area (optional)
+ * zargs[3] = number of char's to skip between lines (optional)
+ */
+ void z_print_table();
+
+ /**
+ * Set the foreground and background colours
+ * to specific RGB colour values.
+ *
+ * zargs[0] = foreground colour
+ * zargs[1] = background colour
+ * zargs[2] = window (-3 is the current one, optional)
+ */
+ void z_set_true_colour();
+
+ /**
+ * Set the foreground and background colours.
+ *
+ * zargs[0] = foreground colour
+ * zargs[1] = background colour
+ * zargs[2] = window (-3 is the current one, optional)
+ */
+ void z_set_colour();
+
+ /**
+ * Set the font for text output and store the previous font.
+ *
+ * zargs[0] = number of font or 0 to keep current font
+ */
+ void z_set_font();
+
+ /**
+ * Set the cursor position or turn the cursor on/off.
+ *
+ * zargs[0] = y-coordinate or -2/-1 for cursor on/off
+ * zargs[1] = x-coordinate
+ * zargs[2] = window (-3 is the current one, optional)
+ */
+ void z_set_cursor();
+
+ /**
+ * z_set_text_style, set the style for text output.
+ *
+ * zargs[0] = style flags to set or 0 to reset text style
+ */
+ void z_set_text_style();
+
+ /**
+ * Select the current window.
+ *
+ * zargs[0] = window to be selected (-3 is the current one)
+ */
+ void z_set_window();
+
+ /**
+ * Display the status line for V1 to V3 games.
+ *
+ * no zargs used
+ */
+ void pad_status_line(int column);
+
+ /**
+ * Display the status line for V1 to V3 games.
+ *
+ * no zargs used
+ */
+ void z_show_status();
+
+ /**
+ * Split the screen into an upper (1) and lower (0) window.
+ *
+ * zargs[0] = height of upper window in screen units (V6) or #lines
+ */
+ void z_split_window();
+
+ /**@}*/
+
+ /**
+ * \defgroup Stream Opcode methods
+ * @{
+ */
+
+ /**
+ * Select an input stream.
+ *
+ * zargs[0] = input stream to be selected
+ */
+ void z_input_stream();
+
+ /**
+ * Open or close an output stream.
+ *
+ * zargs[0] = stream to open (positive) or close (negative)
+ * zargs[1] = address to redirect output to (stream 3 only)
+ * zargs[2] = width of redirected output (stream 3 only, optional)
+ */
+ void z_output_stream();
+
+ /**
+ * Re-load dynamic area, clear the stack and set the PC.
+ *
+ * no zargs used
+ */
+ void z_restart();
+
+ /**
+ * Save [a part of] the Z-machine state to disk.
+ *
+ * zargs[0] = address of memory area to save (optional)
+ * zargs[1] = number of bytes to save
+ * zargs[2] = address of suggested file name
+ */
+ void z_save();
+
+ /**
+ * Restore [a part of] a Z-machine state from disk
+ *
+ * zargs[0] = address of area to restore (optional)
+ * zargs[1] = number of bytes to restore
+ * zargs[2] = address of suggested file name
+ */
+ void z_restore();
+
+ /**
+ * Check the story file integrity.
+ *
+ * no zargs used
+ */
+ void z_verify();
+
+ /**@}*/
+
+ /**
+ * \defgroup Table Opcode methods
+ * @{
+ */
+
+ /**
+ * Copy a table or fill it with zeroes.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = destination address or 0 for fill
+ * zargs[2] = size of table
+ *
+ * Note: Copying is safe even when source and destination overlap; but
+ * if zargs[1] is negative the table _must_ be copied forwards.
+ */
+ void z_copy_table();
+
+ /**
+ * Store a value from a table of bytes.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry to store
+ */
+ void z_loadb();
+
+ /**
+ * Store a value from a table of words.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry to store
+ */
+ void z_loadw();
+
+ /**
+ * Find and store the address of a target within a table.
+ *
+ * zargs[0] = target value to be searched for
+ * zargs[1] = address of table
+ * zargs[2] = number of table entries to check value against
+ * zargs[3] = type of table (optional, defaults to 0x82)
+ *
+ * Note: The table is a word array if bit 7 of zargs[3] is set; otherwise
+ * it's a byte array. The lower bits hold the address step.
+ */
+ void z_scan_table();
+
+ /**
+ * Write a byte into a table of bytes.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry
+ * zargs[2] = value to be written
+ */
+ void z_storeb();
+
+ /**
+ * Write a word into a table of words.
+ *
+ * zargs[0] = address of table
+ * zargs[1] = index of table entry
+ * zargs[2] = value to be written
+ */
+ void z_storew();
+
+ /**@}*/
+
+ /**
+ * \defgroup Text Opcode methods
+ * @{
+ */
+
+ /**
+ * Test if a unicode character can be printed (bit 0) and read (bit 1).
+ *
+ * zargs[0] = Unicode
+ */
+ void z_check_unicode();
+
+ /**
+ * Encode a ZSCII string for use in a dictionary.
+ *
+ * zargs[0] = address of text buffer
+ * zargs[1] = length of ASCII string
+ * zargs[2] = offset of ASCII string within the text buffer
+ * zargs[3] = address to store encoded text in
+ *
+ * This is a V5+ opcode and therefore the dictionary resolution must be
+ * three 16bit words.
+ */
+ void z_encode_text();
+
+ /**
+ * Print a new line.
+ *
+ * no zargs used
+ *
+ */
+ void z_new_line();
+
+ /**
+ * Print a string embedded in the instruction stream.
+ *
+ * no zargs used
+ */
+ void z_print();
+
+ /**
+ * Print a string from the lower 64KB.
+ *
+ * zargs[0] = address of string to print
+ */
+ void z_print_addr();
+
+ /**
+ * Print a single ZSCII character.
+ *
+ * zargs[0] = ZSCII character to be printed
+ */
+ void z_print_char();
+
+ /**
+ * Print a formatted table.
+ *
+ * zargs[0] = address of formatted table to be printed
+ */
+ void z_print_form();
+
+ /**
+ * Print a signed number.
+ *
+ * zargs[0] = number to print
+ */
+ void z_print_num();
+
+ /**
+ * Print an object description.
+ *
+ * zargs[0] = number of object to be printed
+ */
+ void z_print_obj();
+
+ /**
+ * Print the string at the given packed address.
+ *
+ * zargs[0] = packed address of string to be printed
+ */
+ void z_print_paddr();
+
+ /*
+ * Print the string at PC, print newline then return true.
+ *
+ * no zargs used
+ */
+ void z_print_ret();
+
+ /**
+ * Print unicode character
+ *
+ * zargs[0] = Unicode
+ */
+ void z_print_unicode();
+
+ /**
+ * Make a lexical analysis of a ZSCII string.
+ *
+ * zargs[0] = address of string to analyze
+ * zargs[1] = address of token buffer
+ * zargs[2] = address of dictionary (optional)
+ * zargs[3] = set when unknown words cause empty slots (optional)
+ */
+ void z_tokenise();
+
+ /**@}*/
+
+ /**
+ * \defgroup Variable Opcode methods
+ * @{
+ */
+
+ /**
+ * Decrement a variable.
+ *
+ * zargs[0] = variable to decrement
+ */
+ void z_dec();
+
+ /**
+ * Decrement a variable and branch if now less than value.
+ *
+ * zargs[0] = variable to decrement
+ * zargs[1] = value to check variable against
+ */
+ void z_dec_chk();
+
+ /**
+ * Increment a variable.
+ *
+ * zargs[0] = variable to increment
+ */
+ void z_inc();
+
+ /**
+ * Increment a variable and branch if now greater than value.
+ *
+ * zargs[0] = variable to increment
+ * zargs[1] = value to check variable against
+ */
+ void z_inc_chk();
+
+ /**
+ * Store the value of a variable.
+ *
+ * zargs[0] = variable to store
+ */
+ void z_load();
+
+ /**
+ * Pop a value off the game stack and discard it.
+ *
+ * no zargs used
+ */
+ void z_pop();
+
+ /**
+ * Pop n values off the game or user stack and discard them.
+ *
+ * zargs[0] = number of values to discard
+ * zargs[1] = address of user stack (optional)
+ */
+ void z_pop_stack();
+
+ /**
+ * Pop a value off...
+ *
+ * a) ...the game or a user stack and store it (V6)
+ *
+ * zargs[0] = address of user stack (optional)
+ *
+ * b) ...the game stack and write it to a variable (other than V6)
+ *
+ * zargs[0] = variable to write value to
+ */
+ void z_pull();
+
+ /**
+ * Push a value onto the game stack.
+ *
+ * zargs[0] = value to push onto the stack
+ */
+ void z_push();
+
+ /**
+ * Push a value onto a user stack then branch if successful.
+ *
+ * zargs[0] = value to push onto the stack
+ * zargs[1] = address of user stack
+ */
+ void z_push_stack();
+
+ /**
+ * Write a value to a variable.
+ *
+ * zargs[0] = variable to be written to
+ * zargs[1] = value to write
+ */
+ void z_store();
+
+ /**@}*/
+public:
+ /**
+ * Constructor
+ */
+ Processor(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+ /**
+ * Initialization
+ */
+ void initialize();
+
+ /**
+ * Z-code interpreter main loop
+ */
+ void interpret();
+
+ /**
+ * \defgroup Memory access methods
+ * @{
+ */
+
+ /**
+ * Square brackets operator
+ */
+ zbyte &operator[](uint addr) { return zmp[addr]; }
+
+ /**
+ * Read a code byte
+ */
+ zbyte codeByte() { return *pcp++; }
+
+ /**
+ * Read a code word
+ */
+ zword codeWord() {
+ zword v = READ_BE_UINT16(pcp);
+ pcp += 2;
+ return v;
+ }
+
+ /**
+ * Return a code word at a given address
+ */
+ zword codeWordIdx(uint addr) const {
+ return READ_BE_UINT16(pcp + addr);
+ }
+
+ /**
+ * Return the current program execution offset
+ */
+ uint getPC() const { return pcp - zmp; }
+
+ /**
+ * Set the program execution offset
+ */
+ void setPC(uint addr) { pcp = zmp + addr; }
+
+ /**@}*/
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/frotz/processor_buffer.cpp b/engines/glk/frotz/processor_buffer.cpp
new file mode 100644
index 0000000000..faec6ef33d
--- /dev/null
+++ b/engines/glk/frotz/processor_buffer.cpp
@@ -0,0 +1,192 @@
+/* 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/frotz/processor.h"
+#include "common/algorithm.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+const char *const Processor::ERR_MESSAGES[ERR_NUM_ERRORS] = {
+ "Text buffer overflow",
+ "Store out of dynamic memory",
+ "Division by zero",
+ "Illegal object",
+ "Illegal attribute",
+ "No such property",
+ "Stack overflow",
+ "Call to illegal address",
+ "Call to non-routine",
+ "Stack underflow",
+ "Illegal opcode",
+ "Bad stack frame",
+ "Jump to illegal address",
+ "Can't save while in interrupt",
+ "Nesting stream #3 too deep",
+ "Illegal window",
+ "Illegal window property",
+ "Print at illegal address",
+ "Illegal dictionary word length",
+ "@jin called with object 0",
+ "@get_child called with object 0",
+ "@get_parent called with object 0",
+ "@get_sibling called with object 0",
+ "@get_prop_addr called with object 0",
+ "@get_prop called with object 0",
+ "@put_prop called with object 0",
+ "@clear_attr called with object 0",
+ "@set_attr called with object 0",
+ "@test_attr called with object 0",
+ "@move_object called moving object 0",
+ "@move_object called moving into object 0",
+ "@remove_object called with object 0",
+ "@get_next_prop called with object 0"
+};
+
+void Processor::flush_buffer() {
+ /* Make sure we stop when flush_buffer is called from flush_buffer.
+ * Note that this is difficult to avoid as we might print a newline
+ * during flush_buffer, which might cause a newline interrupt, that
+ * might execute any arbitrary opcode, which might flush the buffer.
+ */
+ if (_locked || bufferEmpty())
+ return;
+
+ // Send the buffer to the output streams
+ _buffer[_bufPos] = '\0';
+
+ _locked = true;
+ stream_word(_buffer);
+ _locked = false;
+
+ // Reset the buffer
+ _bufPos = 0;
+ _prevC = '\0';
+}
+
+void Processor::print_char(zchar c) {
+ static bool flag = false;
+
+ if (message || ostream_memory || enable_buffering) {
+ if (!flag) {
+ // Characters 0 and ZC_RETURN are special cases
+ if (c == ZC_RETURN) {
+ new_line();
+ return;
+ }
+ if (c == 0)
+ return;
+
+ // Flush the buffer before a whitespace or after a hyphen
+ if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (_prevC == '-' && c != '-'))
+ flush_buffer();
+
+ // Set the flag if this is part one of a style or font change
+ if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
+ flag = true;
+
+ // Remember the current character code
+ _prevC = c;
+ } else {
+ flag = false;
+ }
+
+ // Insert the character into the buffer
+ _buffer[_bufPos++] = c;
+
+ if (_bufPos == TEXT_BUFFER_SIZE)
+ error("Text buffer overflow");
+ } else {
+ stream_char(c);
+ }
+}
+
+void Processor::print_string(const char *s) {
+ char c;
+
+ while ((c = *s++) != 0) {
+ if (c == '\n')
+ new_line();
+ else
+ print_char(c);
+ }
+}
+
+void Processor::print_long(uint value, int base) {
+ unsigned long i;
+ char c;
+
+ for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base) {
+ if (value >= i || i == 1) {
+ c = (value / i) % base;
+ print_char(c + (c <= 9 ? '0' : 'a' - 10));
+ }
+ }
+}
+
+void Processor::new_line() {
+ flush_buffer();
+ stream_new_line();
+}
+
+void Processor::runtimeError(ErrorCode errNum) {
+ int wasfirst;
+
+ if (errNum <= 0 || errNum > ERR_NUM_ERRORS)
+ return;
+
+ if (_err_report_mode == ERR_REPORT_FATAL
+ || (!_ignore_errors && errNum <= ERR_MAX_FATAL)) {
+ flush_buffer();
+ error(ERR_MESSAGES[errNum - 1]);
+ return;
+ }
+
+ wasfirst = (_errorCount[errNum - 1] == 0);
+ _errorCount[errNum - 1]++;
+
+ if ((_err_report_mode == ERR_REPORT_ALWAYS)
+ || (_err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
+ long pc;
+ GET_PC(pc);
+ print_string("Warning: ");
+ print_string(ERR_MESSAGES[errNum - 1]);
+ print_string(" (PC = ");
+ print_long(pc, 16);
+ print_char(')');
+
+ if (_err_report_mode == ERR_REPORT_ONCE) {
+ print_string(" (will ignore further occurrences)");
+ }
+ else {
+ print_string(" (occurence ");
+ print_long(_errorCount[errNum - 1], 10);
+ print_char(')');
+ }
+
+ new_line();
+ }
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor_input.cpp b/engines/glk/frotz/processor_input.cpp
new file mode 100644
index 0000000000..6dda853fa0
--- /dev/null
+++ b/engines/glk/frotz/processor_input.cpp
@@ -0,0 +1,223 @@
+/* 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/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define INPUT_BUFFER_SIZE 200
+
+bool Processor::read_yes_or_no(const char *s) {
+ zchar key;
+
+ print_string(s);
+ print_string("? (y/n) >");
+
+ key = stream_read_key(0, 0, false);
+
+ if (key == 'y' || key == 'Y') {
+ print_string("y\n");
+ return true;
+ } else {
+ print_string("n\n");
+ return false;
+ }
+}
+
+void Processor::read_string(int max, zchar *buffer) {
+ zchar key;
+
+ buffer[0] = 0;
+
+ do {
+ key = stream_read_input(max, buffer, 0, 0, false, false);
+ } while (key != ZC_RETURN);
+}
+
+bool Processor::is_terminator(zchar key) {
+ if (key == ZC_TIME_OUT)
+ return true;
+ if (key == ZC_RETURN)
+ return true;
+ if (key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX)
+ return true;
+
+ if (h_terminating_keys != 0) {
+ if (key >= ZC_ARROW_MIN && key <= ZC_MENU_CLICK) {
+
+ zword addr = h_terminating_keys;
+ zbyte c;
+
+ do {
+ LOW_BYTE(addr, c);
+ if (c == 255 || key == translate_from_zscii(c))
+ return true;
+ addr++;
+ } while (c != 0);
+ }
+ }
+
+ return false;
+}
+
+void Processor::z_make_menu() {
+ // This opcode was only used for the Macintosh version of Journey.
+ // It controls menus with numbers greater than 2 (menus 0, 1 and 2
+ // are system menus).
+ branch(false);
+}
+
+int Processor::read_number() {
+ zchar buffer[6];
+ int value = 0;
+ int i;
+
+ read_string(5, buffer);
+
+ for (i = 0; buffer[i] != 0; i++)
+ if (buffer[i] >= '0' && buffer[i] <= '9')
+ value = 10 * value + buffer[i] - '0';
+
+ return value;
+}
+
+void Processor::z_read() {
+ zchar buffer[INPUT_BUFFER_SIZE];
+ zword addr;
+ zchar key;
+ zbyte max, size;
+ zbyte c;
+ int i;
+
+ // Supply default arguments
+ if (zargc < 3)
+ zargs[2] = 0;
+
+ // Get maximum input size
+ addr = zargs[0];
+
+ LOW_BYTE(addr, max);
+
+ if (h_version <= V4)
+ max--;
+
+ if (max >= INPUT_BUFFER_SIZE)
+ max = INPUT_BUFFER_SIZE - 1;
+
+ // Get initial input size
+ if (h_version >= V5) {
+ addr++;
+ LOW_BYTE(addr, size);
+ } else {
+ size = 0;
+ }
+
+ // Copy initial input to local buffer
+ for (i = 0; i < size; i++) {
+ addr++;
+ LOW_BYTE(addr, c);
+ buffer[i] = translate_from_zscii(c);
+ }
+ buffer[i] = 0;
+
+ // Draw status line for V1 to V3 games
+ if (h_version <= V3)
+ z_show_status();
+
+ // Read input from current input stream
+ key = stream_read_input(
+ max, buffer, // buffer and size
+ zargs[2], // timeout value
+ zargs[3], // timeout routine
+ false, // enable hot keys
+ h_version == V6 // no script in V6
+ );
+
+ if (key == ZC_BAD)
+ return;
+
+ // Perform save_undo for V1 to V4 games
+ if (h_version <= V4)
+ save_undo();
+
+ // Copy local buffer back to dynamic memory
+ for (i = 0; buffer[i] != 0; i++) {
+ if (key == ZC_RETURN) {
+ buffer[i] = unicode_tolower (buffer[i]);
+ }
+
+ storeb((zword) (zargs[0] + ((h_version <= V4) ? 1 : 2) + i), translate_to_zscii (buffer[i]));
+ }
+
+ // Add null character (V1-V4) or write input length into 2nd byte
+ if (h_version <= V4)
+ storeb((zword) (zargs[0] + 1 + i), 0);
+ else
+ storeb((zword) (zargs[0] + 1), i);
+
+ // Tokenise line if a token buffer is present
+ if (key == ZC_RETURN && zargs[1] != 0)
+ tokenise_line (zargs[0], zargs[1], 0, false);
+
+ // Store key
+ if (h_version >= V5)
+ store(translate_to_zscii(key));
+}
+
+void Processor::z_read_char() {
+ zchar key;
+
+ // Supply default arguments
+ if (zargc < 2)
+ zargs[1] = 0;
+
+ // Read input from the current input stream
+ key = stream_read_key(
+ zargs[1], // timeout value
+ zargs[2], // timeout routine
+ false // enable hot keys
+ );
+
+ if (key == ZC_BAD)
+ return;
+
+ // Store key
+ store (translate_to_zscii (key));
+}
+
+void Processor::z_read_mouse(){
+ zword btn;
+
+ // Read the mouse position, the last menu click and which buttons are down
+ btn = os_read_mouse();
+ hx_mouse_y = mouse_y;
+ hx_mouse_x = mouse_x;
+
+ storew((zword) (zargs[0] + 0), hx_mouse_y);
+ storew((zword) (zargs[0] + 2), hx_mouse_x);
+ storew((zword) (zargs[0] + 4), btn); // mouse button bits
+ storew((zword) (zargs[0] + 6), menu_selected); // menu selection
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor_maths.cpp b/engines/glk/frotz/processor_maths.cpp
new file mode 100644
index 0000000000..90e09ca8b9
--- /dev/null
+++ b/engines/glk/frotz/processor_maths.cpp
@@ -0,0 +1,105 @@
+/* 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/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+void Processor::z_add() {
+ store((zword)((short)zargs[0] + (short)zargs[1]));
+}
+
+void Processor::z_and() {
+ store((zword)(zargs[0] & zargs[1]));
+}
+
+void Processor::z_art_shift() {
+ if ((short)zargs[1] > 0)
+ store((zword)((short)zargs[0] << (short)zargs[1]));
+ else
+ store((zword)((short)zargs[0] >> - (short)zargs[1]));
+}
+
+void Processor::z_div() {
+ if (zargs[1] == 0)
+ runtimeError(ERR_DIV_ZERO);
+
+ store((zword)((short)zargs[0] / (short)zargs[1]));
+}
+
+void Processor::z_je() {
+ branch(
+ zargc > 1 && (zargs[0] == zargs[1] || (
+ zargc > 2 && (zargs[0] == zargs[2] || (
+ zargc > 3 && (zargs[0] == zargs[3])))))
+ );
+}
+
+void Processor::z_jg() {
+ branch((short)zargs[0] > (short)zargs[1]);
+}
+
+void Processor::z_jl() {
+ branch((short)zargs[0] < (short)zargs[1]);
+}
+
+void Processor::z_jz() {
+ branch((short)zargs[0] == 0);
+}
+
+void Processor::z_log_shift() {
+ if ((short)zargs[1] > 0)
+ store((zword)(zargs[0] << (short)zargs[1]));
+ else
+ store((zword)(zargs[0] >> - (short)zargs[1]));
+}
+
+void Processor::z_mod() {
+ if (zargs[1] == 0)
+ runtimeError(ERR_DIV_ZERO);
+
+ store((zword)((short)zargs[0] % (short)zargs[1]));
+}
+
+void Processor::z_mul() {
+ store((zword)((short)zargs[0] * (short)zargs[1]));
+}
+
+void Processor::z_not() {
+ store((zword)~zargs[0]);
+}
+
+void Processor::z_or() {
+ store((zword)(zargs[0] | zargs[1]));
+}
+
+void Processor::z_sub() {
+ store((zword)((short)zargs[0] - (short)zargs[1]));
+}
+
+void Processor::z_test() {
+ branch((zargs[0] & zargs[1]) == zargs[1]);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor_mem.cpp b/engines/glk/frotz/processor_mem.cpp
new file mode 100644
index 0000000000..7c7af0d2f5
--- /dev/null
+++ b/engines/glk/frotz/processor_mem.cpp
@@ -0,0 +1,218 @@
+/* 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/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+void Processor::flagsChanged(zbyte value) {
+ if (value & SCRIPTING_FLAG) {
+ if (!ostream_script)
+ script_open();
+ } else {
+ if (ostream_script)
+ script_close();
+ }
+}
+
+int Processor::save_undo() {
+ long diff_size;
+ zword stack_size;
+ undo_t *p;
+
+ if (_undo_slots == 0)
+ // undo feature unavailable
+ return -1;
+
+ // save undo possible
+ while (last_undo != curr_undo) {
+ p = last_undo;
+ last_undo = last_undo->prev;
+ delete p;
+ undo_count--;
+ }
+ if (last_undo)
+ last_undo->next = nullptr;
+ else
+ first_undo = nullptr;
+
+ if (undo_count == _undo_slots)
+ free_undo(1);
+
+ diff_size = mem_diff(zmp, prev_zmp, h_dynamic_size, undo_diff);
+ stack_size = _stack + STACK_SIZE - _sp;
+ do {
+ p = (undo_t *) malloc(sizeof(undo_t) + diff_size + stack_size * sizeof(*_sp));
+ if (p == nullptr)
+ free_undo(1);
+ } while (!p && undo_count);
+ if (p == nullptr)
+ return -1;
+
+ GET_PC(p->pc);
+ p->frame_count = _frameCount;
+ p->diff_size = diff_size;
+ p->stack_size = stack_size;
+ p->frame_offset = _fp - _stack;
+ memcpy(p + 1, undo_diff, diff_size);
+ memcpy((zbyte *)(p + 1) + diff_size, _sp, stack_size * sizeof(*_sp));
+
+ if (!first_undo) {
+ p->prev = nullptr;
+ first_undo = p;
+ } else {
+ last_undo->next = p;
+ p->prev = last_undo;
+ }
+
+ p->next = nullptr;
+ curr_undo = last_undo = p;
+ undo_count++;
+
+ return 1;
+}
+
+int Processor::restore_undo(void) {
+ if (_undo_slots == 0)
+ // undo feature unavailable
+ return -1;
+
+ if (curr_undo == nullptr)
+ // no saved game state
+ return 0;
+
+ // undo possible
+ memcpy(zmp, prev_zmp, h_dynamic_size);
+ SET_PC(curr_undo->pc);
+ _sp = _stack + STACK_SIZE - curr_undo->stack_size;
+ _fp = _stack + curr_undo->frame_offset;
+ _frameCount = curr_undo->frame_count;
+ mem_undiff((zbyte *)(curr_undo + 1), curr_undo->diff_size, prev_zmp);
+ memcpy(_sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size,
+ curr_undo->stack_size * sizeof(*_sp));
+
+ curr_undo = curr_undo->prev;
+
+ restart_header();
+
+ return 2;
+}
+
+/**
+ * TOR: glkify -- this is for V6 only
+ */
+static zword get_max_width(zword win) { return 80; }
+
+void Processor::memory_open(zword table, zword xsize, bool buffering) {
+ if (_redirect.size() < MAX_NESTING) {
+ if (!buffering)
+ xsize = 0xffff;
+ if (buffering && (short)xsize <= 0)
+ xsize = get_max_width((zword)(-(short)xsize));
+
+ storew(table, 0);
+
+ _redirect.push(Redirect(xsize, table));
+ ostream_memory = true;
+ } else {
+ runtimeError(ERR_STR3_NESTING);
+ }
+}
+
+void Processor::memory_new_line() {
+ zword size;
+ zword addr;
+
+ Redirect &r = _redirect.top();
+ r._total += r._width;
+ r._width = 0;
+
+ addr = r._table;
+
+ LOW_WORD(addr, size);
+ addr += 2;
+
+ if (r._xSize != 0xffff) {
+ r._table = addr + size;
+ size = 0;
+ } else {
+ storeb((zword)(addr + (size++)), 13);
+ }
+
+ storew(r._table, size);
+}
+
+void Processor::memory_word(const zchar *s) {
+ zword size;
+ zword addr;
+ zchar c;
+
+ Redirect &r = _redirect.top();
+ if (h_version == V6) {
+ int width = os_string_width(s);
+
+ if (r._xSize != 0xffff) {
+ if (r._width + width > r._xSize) {
+
+ if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
+ width = os_string_width(++s);
+
+ memory_new_line();
+ }
+ }
+
+ r._width += width;
+ }
+
+ addr = r._table;
+
+ LOW_WORD(addr, size);
+ addr += 2;
+
+ while ((c = *s++) != 0)
+ storeb((zword)(addr + (size++)), translate_to_zscii(c));
+
+ storew(r._table, size);
+}
+
+void Processor::memory_close(void) {
+ if (!_redirect.empty()) {
+ Redirect &r = _redirect.top();
+
+ if (r._xSize != 0xffff)
+ memory_new_line();
+
+ if (h_version == V6) {
+ h_line_width = (r._xSize != 0xffff) ? r._total : r._width;
+
+ SET_WORD(H_LINE_WIDTH, h_line_width);
+ }
+
+ _redirect.pop();
+ if (_redirect.empty())
+ ostream_memory = false;
+ }
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor_objects.cpp b/engines/glk/frotz/processor_objects.cpp
new file mode 100644
index 0000000000..7e1fe6de0c
--- /dev/null
+++ b/engines/glk/frotz/processor_objects.cpp
@@ -0,0 +1,732 @@
+/* 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/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define MAX_OBJECT 2000
+
+enum O1 {
+ O1_PARENT = 4,
+ O1_SIBLING = 5,
+ O1_CHILD = 6,
+ O1_PROPERTY_OFFSET = 7,
+ O1_SIZE = 9
+};
+
+enum O4 {
+ O4_PARENT = 6,
+ O4_SIBLING = 8,
+ O4_CHILD = 10,
+ O4_PROPERTY_OFFSET = 12,
+ O4_SIZE = 14
+};
+
+zword Processor::object_address(zword obj) {
+ // Check object number
+ if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
+ print_string("@Attempt to address illegal object ");
+ print_num(obj);
+ print_string(". This is normally fatal.");
+ new_line();
+ runtimeError(ERR_ILL_OBJ);
+ }
+
+ // Return object address
+ if (h_version <= V3)
+ return h_objects + ((obj - 1) * O1_SIZE + 62);
+ else
+ return h_objects + ((obj - 1) * O4_SIZE + 126);
+}
+
+zword Processor::object_name(zword object) {
+ zword obj_addr;
+ zword name_addr;
+
+ obj_addr = object_address(object);
+
+ // The object name address is found at the start of the properties
+ if (h_version <= V3)
+ obj_addr += O1_PROPERTY_OFFSET;
+ else
+ obj_addr += O4_PROPERTY_OFFSET;
+
+ LOW_WORD(obj_addr, name_addr);
+
+ return name_addr;
+}
+
+zword Processor::first_property(zword obj) {
+ zword prop_addr;
+ zbyte size;
+
+ // Fetch address of object name
+ prop_addr = object_name (obj);
+
+ // Get length of object name
+ LOW_BYTE(prop_addr, size);
+
+ // Add name length to pointer
+ return prop_addr + 1 + 2 * size;
+}
+
+zword Processor::next_property(zword prop_addr) {
+ zbyte value;
+
+ // Load the current property id
+ LOW_BYTE(prop_addr, value);
+ prop_addr++;
+
+ // Calculate the length of this property
+ if (h_version <= V3)
+ value >>= 5;
+ else if (!(value & 0x80))
+ value >>= 6;
+ else {
+ LOW_BYTE(prop_addr, value);
+ value &= 0x3f;
+
+ if (value == 0)
+ // demanded by Spec 1.0
+ value = 64;
+ }
+
+ // Add property length to current property pointer
+ return prop_addr + value + 1;
+}
+
+void Processor::unlink_object(zword object) {
+ zword obj_addr;
+ zword parent_addr;
+ zword sibling_addr;
+
+ if (object == 0) {
+ runtimeError(ERR_REMOVE_OBJECT_0);
+ return;
+ }
+
+ obj_addr = object_address(object);
+
+ if (h_version <= V3) {
+
+ zbyte parent;
+ zbyte younger_sibling;
+ zbyte older_sibling;
+ zbyte zero = 0;
+
+ // Get parent of object, and return if no parent
+ obj_addr += O1_PARENT;
+ LOW_BYTE(obj_addr, parent);
+ if (!parent)
+ return;
+
+ // Get (older) sibling of object and set both parent and sibling pointers to 0
+ SET_BYTE(obj_addr, zero);
+ obj_addr += O1_SIBLING - O1_PARENT;
+ LOW_BYTE(obj_addr, older_sibling);
+ SET_BYTE(obj_addr, zero);
+
+ // Get first child of parent (the youngest sibling of the object)
+ parent_addr = object_address(parent) + O1_CHILD;
+ LOW_BYTE(parent_addr, younger_sibling);
+
+ // Remove object from the list of siblings
+ if (younger_sibling == object)
+ SET_BYTE(parent_addr, older_sibling);
+ else {
+ do {
+ sibling_addr = object_address(younger_sibling) + O1_SIBLING;
+ LOW_BYTE(sibling_addr, younger_sibling);
+ } while (younger_sibling != object);
+ SET_BYTE(sibling_addr, older_sibling);
+ }
+ } else {
+ zword parent;
+ zword younger_sibling;
+ zword older_sibling;
+ zword zero = 0;
+
+ // Get parent of object, and return if no parent
+ obj_addr += O4_PARENT;
+ LOW_WORD(obj_addr, parent);
+ if (!parent)
+ return;
+
+ // Get (older) sibling of object and set both parent and sibling pointers to 0
+ SET_WORD(obj_addr, zero);
+ obj_addr += O4_SIBLING - O4_PARENT;
+ LOW_WORD(obj_addr, older_sibling);
+ SET_WORD(obj_addr, zero);
+
+ // Get first child of parent (the youngest sibling of the object)
+ parent_addr = object_address(parent) + O4_CHILD;
+ LOW_WORD(parent_addr, younger_sibling);
+
+ // Remove object from the list of siblings
+ if (younger_sibling == object) {
+ SET_WORD(parent_addr, older_sibling);
+ } else {
+ do {
+ sibling_addr = object_address(younger_sibling) + O4_SIBLING;
+ LOW_WORD(sibling_addr, younger_sibling);
+ } while (younger_sibling != object);
+ SET_WORD(sibling_addr, older_sibling);
+ }
+ }
+}
+
+void Processor::z_clear_attr() {
+ zword obj_addr;
+ zbyte value;
+
+ if (_storyId == SHERLOCK)
+ if (zargs[1] == 48)
+ return;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtimeError(ERR_ILL_ATTR);
+
+ // If we are monitoring attribute assignment display a short note
+ if (_attribute_assignment) {
+ stream_mssg_on();
+ print_string("@clear_attr ");
+ print_object(zargs[0]);
+ print_string(" ");
+ print_num(zargs[1]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_CLEAR_ATTR_0);
+ return;
+ }
+
+ // Get attribute address
+ obj_addr = object_address(zargs[0]) + zargs[1] / 8;
+
+ // Clear attribute bit
+ LOW_BYTE(obj_addr, value);
+ value &= ~(0x80 >> (zargs[1] & 7));
+ SET_BYTE(obj_addr, value);
+}
+
+void Processor::z_jin() {
+ zword obj_addr;
+
+ // If we are monitoring object locating display a short note
+ if (_object_locating) {
+ stream_mssg_on();
+ print_string("@jin ");
+ print_object(zargs[0]);
+ print_string(" ");
+ print_object(zargs[1]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_JIN_0);
+ branch(0 == zargs[1]);
+ return;
+ }
+
+ obj_addr = object_address(zargs[0]);
+
+ if (h_version <= V3) {
+ zbyte parent;
+
+ // Get parent id from object
+ obj_addr += O1_PARENT;
+ LOW_BYTE(obj_addr, parent);
+
+ // Branch if the parent is obj2
+ branch(parent == zargs[1]);
+
+ } else {
+ zword parent;
+
+ // Get parent id from object
+ obj_addr += O4_PARENT;
+ LOW_WORD(obj_addr, parent);
+
+ // Branch if the parent is obj2
+ branch(parent == zargs[1]);
+ }
+}
+
+void Processor::z_get_child() {
+ zword obj_addr;
+
+ // If we are monitoring object locating display a short note
+ if (_object_locating) {
+ stream_mssg_on();
+ print_string("@get_child ");
+ print_object(zargs[0]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_CHILD_0);
+ store(0);
+ branch(false);
+ return;
+ }
+
+ obj_addr = object_address(zargs[0]);
+
+ if (h_version <= V3) {
+ zbyte child;
+
+ // Get child id from object
+ obj_addr += O1_CHILD;
+ LOW_BYTE(obj_addr, child);
+
+ // Store child id and branch
+ store(child);
+ branch(child);
+ } else {
+ zword child;
+
+ // Get child id from object
+ obj_addr += O4_CHILD;
+ LOW_WORD(obj_addr, child);
+
+ // Store child id and branch
+ store(child);
+ branch(child);
+ }
+}
+
+void Processor::z_get_next_prop() {
+ zword prop_addr;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_NEXT_PROP_0);
+ store(0);
+ return;
+ }
+
+ // Property id is in bottom five (six) bits
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ // Load address of first property
+ prop_addr = first_property(zargs[0]);
+
+ if (zargs[1] != 0) {
+ // Scan down the property list
+ do {
+ LOW_BYTE(prop_addr, value);
+ prop_addr = next_property(prop_addr);
+ } while ((value & mask) > zargs[1]);
+
+ // Exit if the property does not exist
+ if ((value & mask) != zargs[1])
+ runtimeError(ERR_NO_PROP);
+ }
+
+ // Return the property id
+ LOW_BYTE(prop_addr, value);
+ store((zword) (value & mask));
+}
+
+void Processor::z_get_parent() {
+ zword obj_addr;
+
+ // If we are monitoring object locating display a short note
+ if (_object_locating) {
+ stream_mssg_on();
+ print_string("@get_parent ");
+ print_object(zargs[0]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_PARENT_0);
+ store(0);
+ return;
+ }
+
+ obj_addr = object_address(zargs[0]);
+
+ if (h_version <= V3) {
+ zbyte parent;
+
+ // Get parent id from object
+ obj_addr += O1_PARENT;
+ LOW_BYTE(obj_addr, parent);
+
+ // Store parent
+ store(parent);
+
+ } else {
+ zword parent;
+
+ // Get parent id from object
+ obj_addr += O4_PARENT;
+ LOW_WORD(obj_addr, parent);
+
+ // Store parent
+ store(parent);
+ }
+}
+
+void Processor::z_get_prop() {
+ zword prop_addr;
+ zword wprop_val;
+ zbyte bprop_val;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_PROP_0);
+ store(0);
+ return;
+ }
+
+ // Property id is in bottom five (six) bits
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ // Load address of first property
+ prop_addr = first_property(zargs[0]);
+
+ // Scan down the property list
+ for (;;) {
+ LOW_BYTE(prop_addr, value);
+ if ((value & mask) <= zargs[1])
+ break;
+ prop_addr = next_property(prop_addr);
+ }
+
+ if ((value & mask) == zargs[1]) {
+ // property found
+
+ // Load property(byte or word sized)
+ prop_addr++;
+
+ if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
+ LOW_BYTE(prop_addr, bprop_val);
+ wprop_val = bprop_val;
+ } else {
+ LOW_WORD(prop_addr, wprop_val);
+ }
+ } else {
+ // property not found
+
+ // Load default value
+ prop_addr = h_objects + 2 * (zargs[1] - 1);
+ LOW_WORD(prop_addr, wprop_val);
+ }
+
+ // Store the property value
+ store(wprop_val);
+}
+
+void Processor::z_get_prop_addr() {
+ zword prop_addr;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_PROP_ADDR_0);
+ store(0);
+ return;
+ }
+
+ if (_storyId == BEYOND_ZORK)
+ if (zargs[0] > MAX_OBJECT)
+ { store(0); return; }
+
+ // Property id is in bottom five (six) bits
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ // Load address of first property
+ prop_addr = first_property(zargs[0]);
+
+ // Scan down the property list
+ for (;;) {
+ LOW_BYTE(prop_addr, value);
+ if ((value & mask) <= zargs[1])
+ break;
+ prop_addr = next_property(prop_addr);
+ }
+
+ // Calculate the property address or return zero
+ if ((value & mask) == zargs[1]) {
+
+ if (h_version >= V4 && (value & 0x80))
+ prop_addr++;
+ store((zword) (prop_addr + 1));
+
+ } else {
+ store(0);
+ }
+}
+
+void Processor::z_get_prop_len() {
+ zword addr;
+ zbyte value;
+
+ // Back up the property pointer to the property id
+ addr = zargs[0] - 1;
+ LOW_BYTE(addr, value);
+
+ // Calculate length of property
+ if (h_version <= V3)
+ value = (value >> 5) + 1;
+ else if (!(value & 0x80))
+ value = (value >> 6) + 1;
+ else {
+ value &= 0x3f;
+
+ if (value == 0)
+ value = 64; // demanded by Spec 1.0
+ }
+
+ // Store length of property
+ store(value);
+}
+
+void Processor::z_get_sibling() {
+ zword obj_addr;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_SIBLING_0);
+ store(0);
+ branch(false);
+ return;
+ }
+
+ obj_addr = object_address(zargs[0]);
+
+ if (h_version <= V3) {
+ zbyte sibling;
+
+ // Get sibling id from object
+ obj_addr += O1_SIBLING;
+ LOW_BYTE(obj_addr, sibling);
+
+ // Store sibling and branch
+ store(sibling);
+ branch(sibling);
+
+ } else {
+ zword sibling;
+
+ // Get sibling id from object
+ obj_addr += O4_SIBLING;
+ LOW_WORD(obj_addr, sibling);
+
+ // Store sibling and branch
+ store(sibling);
+ branch(sibling);
+ }
+}
+
+void Processor::z_insert_obj() {
+ zword obj1 = zargs[0];
+ zword obj2 = zargs[1];
+ zword obj1_addr;
+ zword obj2_addr;
+
+ // If we are monitoring object movements display a short note
+ if (_object_movement) {
+ stream_mssg_on();
+ print_string("@move_obj ");
+ print_object(obj1);
+ print_string(" ");
+ print_object(obj2);
+ stream_mssg_off();
+ }
+
+ if (obj1 == 0) {
+ runtimeError(ERR_MOVE_OBJECT_0);
+ return;
+ }
+
+ if (obj2 == 0) {
+ runtimeError(ERR_MOVE_OBJECT_TO_0);
+ return;
+ }
+
+ // Get addresses of both objects
+ obj1_addr = object_address(obj1);
+ obj2_addr = object_address(obj2);
+
+ // Remove object 1 from current parent
+ unlink_object(obj1);
+
+ // Make object 1 first child of object 2
+ if (h_version <= V3) {
+ zbyte child;
+
+ obj1_addr += O1_PARENT;
+ SET_BYTE(obj1_addr, obj2);
+ obj2_addr += O1_CHILD;
+ LOW_BYTE(obj2_addr, child);
+ SET_BYTE(obj2_addr, obj1);
+ obj1_addr += O1_SIBLING - O1_PARENT;
+ SET_BYTE(obj1_addr, child);
+
+ } else {
+ zword child;
+
+ obj1_addr += O4_PARENT;
+ SET_WORD(obj1_addr, obj2);
+ obj2_addr += O4_CHILD;
+ LOW_WORD(obj2_addr, child);
+ SET_WORD(obj2_addr, obj1);
+ obj1_addr += O4_SIBLING - O4_PARENT;
+ SET_WORD(obj1_addr, child);
+ }
+}
+
+void Processor::z_put_prop() {
+ zword prop_addr;
+ zword value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_PUT_PROP_0);
+ return;
+ }
+
+ // Property id is in bottom five or six bits
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ // Load address of first property
+ prop_addr = first_property(zargs[0]);
+
+ // Scan down the property list
+ for (;;) {
+ LOW_BYTE(prop_addr, value);
+ if ((value & mask) <= zargs[1])
+ break;
+
+ prop_addr = next_property(prop_addr);
+ }
+
+ // Exit if the property does not exist
+ if ((value & mask) != zargs[1])
+ runtimeError(ERR_NO_PROP);
+
+ // Store the new property value (byte or word sized)
+ prop_addr++;
+
+ if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
+ zbyte v = zargs[2];
+ SET_BYTE(prop_addr, v);
+ } else {
+ zword v = zargs[2];
+ SET_WORD(prop_addr, v);
+ }
+}
+
+void Processor::z_remove_obj() {
+ // If we are monitoring object movements display a short note
+ if (_object_movement) {
+ stream_mssg_on();
+ print_string("@remove_obj ");
+ print_object(zargs[0]);
+ stream_mssg_off();
+ }
+
+ // Call unlink_object to do the job
+ unlink_object(zargs[0]);
+}
+
+void Processor::z_set_attr() {
+ zword obj_addr;
+ zbyte value;
+
+ if (_storyId == SHERLOCK)
+ if (zargs[1] == 48)
+ return;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtimeError(ERR_ILL_ATTR);
+
+ // If we are monitoring attribute assignment display a short note
+ if (_attribute_assignment) {
+ stream_mssg_on();
+ print_string("@set_attr ");
+ print_object(zargs[0]);
+ print_string(" ");
+ print_num(zargs[1]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_SET_ATTR_0);
+ return;
+ }
+
+ // Get attribute address
+ obj_addr = object_address(zargs[0]) + zargs[1] / 8;
+
+ // Load attribute byte
+ LOW_BYTE(obj_addr, value);
+
+ // Set attribute bit
+ value |= 0x80 >> (zargs[1] & 7);
+
+ // Store attribute byte
+ SET_BYTE(obj_addr, value);
+}
+
+void Processor::z_test_attr() {
+ zword obj_addr;
+ zbyte value;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtimeError(ERR_ILL_ATTR);
+
+ // If we are monitoring attribute testing display a short note
+ if (_attribute_testing) {
+ stream_mssg_on();
+ print_string("@test_attr ");
+ print_object(zargs[0]);
+ print_string(" ");
+ print_num(zargs[1]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_TEST_ATTR_0);
+ branch(false);
+ return;
+ }
+
+ // Get attribute address
+ obj_addr = object_address(zargs[0]) + zargs[1] / 8;
+
+ // Load attribute byte
+ LOW_BYTE(obj_addr, value);
+
+ // Test attribute
+ branch(value & (0x80 >> (zargs[1] & 7)));
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
new file mode 100644
index 0000000000..87c190f3c5
--- /dev/null
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -0,0 +1,528 @@
+/* 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/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+void Processor::screen_mssg_on() {
+ if (gos_curwin == gos_lower) {
+ oldstyle = curstyle;
+ glk_set_style(style_Preformatted);
+ glk_put_string("\n ");
+ }
+}
+
+void Processor::screen_mssg_off() {
+ if (gos_curwin == gos_lower) {
+ glk_put_char('\n');
+ zargs[0] = 0;
+ z_set_text_style();
+ zargs[0] = oldstyle;
+ z_set_text_style();
+ }
+}
+
+void Processor::screen_char(zchar c) {
+ if (gos_linepending && (gos_curwin == gos_linewin)) {
+ gos_cancel_pending_line();
+ if (gos_curwin == gos_upper) {
+ curx = 1;
+ cury ++;
+ }
+ if (c == '\n')
+ return;
+ }
+
+ // check fixed flag in header, game can change it at whim
+ int forcefix = ((h_flags & FIXED_FONT_FLAG) != 0);
+ int curfix = ((curstyle & FIXED_WIDTH_STYLE) != 0);
+ if (forcefix && !curfix) {
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+ fixforced = true;
+ } else if (!forcefix && fixforced) {
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+ fixforced = false;
+ }
+
+ if (gos_upper && gos_curwin == gos_upper) {
+ if (c == '\n' || c == ZC_RETURN) {
+ glk_put_char('\n');
+ curx = 1;
+ cury ++;
+ } else {
+ if (cury == 1) {
+ if (curx <= ((sizeof statusline / sizeof(zchar)) - 1)) {
+ statusline[curx - 1] = c;
+ statusline[curx] = 0;
+ }
+ if (curx < h_screen_cols) {
+ glk_put_char_uni(c);
+ } else if (curx == h_screen_cols) {
+ glk_put_char_uni(c);
+ glk_window_move_cursor(gos_curwin, curx-1, cury-1);
+ } else {
+ smartstatusline();
+ }
+
+ curx++;
+ } else {
+ if (curx < h_screen_cols) {
+ glk_put_char_uni(c);
+ } else if (curx == (h_screen_cols)) {
+ glk_put_char_uni(c);
+ glk_window_move_cursor(gos_curwin, curx-1, cury-1);
+ }
+
+ curx++;
+ }
+ }
+ } else if (gos_curwin == gos_lower) {
+ if (c == ZC_RETURN)
+ glk_put_char('\n');
+ else glk_put_char_uni(c);
+ }
+}
+
+void Processor::screen_new_line() {
+ screen_char('\n');
+}
+
+void Processor::screen_word(const zchar *s) {
+ zchar c;
+ while ((c = *s++) != 0) {
+ if (c == ZC_NEW_FONT)
+ s++;
+ else if (c == ZC_NEW_STYLE)
+ s++;
+ else
+ screen_char(c);
+ }
+}
+
+
+void Processor::z_buffer_mode() {
+ // No implementation
+}
+
+void Processor::z_buffer_screen() {
+ store(0);
+}
+
+void Processor::z_erase_line() {
+ int i;
+
+ if (gos_upper && gos_curwin == gos_upper) {
+ for (i = 0; i < h_screen_cols + 1 - curx; i++)
+ glk_put_char(' ');
+ glk_window_move_cursor(gos_curwin, curx - 1, cury - 1);
+ }
+}
+
+void Processor::z_erase_window() {
+ short w = zargs[0];
+ if (w == -2)
+ {
+ if (gos_upper) {
+ glk_set_window(gos_upper);
+#ifdef GARGLK
+ garglk_set_zcolors(curr_fg, curr_bg);
+#endif /* GARGLK */
+ glk_window_clear(gos_upper);
+ glk_set_window(gos_curwin);
+ }
+ glk_window_clear(gos_lower);
+ }
+ if (w == -1)
+ {
+ if (gos_upper) {
+ glk_set_window(gos_upper);
+#ifdef GARGLK
+ garglk_set_zcolors(curr_fg, curr_bg);
+#endif /* GARGLK */
+ glk_window_clear(gos_upper);
+ }
+ glk_window_clear(gos_lower);
+ split_window(0);
+ glk_set_window(gos_lower);
+ gos_curwin = gos_lower;
+ }
+ if (w == 0)
+ glk_window_clear(gos_lower);
+ if (w == 1 && gos_upper)
+ glk_window_clear(gos_upper);
+}
+
+void Processor::z_get_cursor() {
+ storew((zword) (zargs[0] + 0), cury);
+ storew((zword) (zargs[0] + 2), curx);
+}
+
+void Processor::z_print_table() {
+ zword addr = zargs[0];
+ zword x;
+ int i, j;
+
+ // Supply default arguments
+ if (zargc < 3)
+ zargs[2] = 1;
+ if (zargc < 4)
+ zargs[3] = 0;
+
+ // Write text in width x height rectangle
+ x = curx;
+
+ for (i = 0; i < zargs[2]; i++) {
+ if (i != 0) {
+ cury += 1;
+ curx = x;
+ }
+
+ for (j = 0; j < zargs[1]; j++) {
+
+ zbyte c;
+
+ LOW_BYTE(addr, c);
+ addr++;
+
+ print_char(c);
+ }
+
+ addr += zargs[3];
+ }
+}
+
+#define zB(i) ((((i >> 10) & 0x1F) << 3) | (((i >> 10) & 0x1F) >> 2))
+#define zG(i) ((((i >> 5) & 0x1F) << 3) | (((i >> 5) & 0x1F) >> 2))
+#define zR(i) ((((i ) & 0x1F) << 3) | (((i ) & 0x1F) >> 2))
+
+#define zRGB(i) (zR(i) << 16 | zG(i) << 8 | zB(i))
+
+void Processor::z_set_true_colour() {
+ int zfore = zargs[0];
+ int zback = zargs[1];
+
+ if (!(zfore < 0))
+ zfore = zRGB(zargs[0]);
+
+ if (!(zback < 0))
+ zback = zRGB(zargs[1]);
+
+#ifdef GARGLK
+ garglk_set_zcolors(zfore, zback);
+#endif /* GARGLK */
+
+ curr_fg = zfore;
+ curr_bg = zback;
+}
+
+static const int zcolor_map[] = {
+ -2, ///< 0 = current
+ -1, ///< 1 = default
+ 0x0000, ///< 2 = black
+ 0x001D, ///< 3 = red
+ 0x0340, ///< 4 = green
+ 0x03BD, ///< 5 = yellow
+ 0x59A0, ///< 6 = blue
+ 0x7C1F, ///< 7 = magenta
+ 0x77A0, ///< 8 = cyan
+ 0x7FFF, ///< 9 = white
+ 0x5AD6, ///< 10 = light grey
+ 0x4631, ///< 11 = medium grey
+ 0x2D6B, ///< 12 = dark grey
+};
+
+#define zcolor_NUMCOLORS (13)
+
+void Processor::z_set_colour() {
+ int zfore = zargs[0];
+ int zback = zargs[1];
+
+ switch (zfore) {
+ case -1:
+ zfore = -3;
+ break;
+
+ case 0:
+ case 1:
+ zfore = zcolor_map[zfore];
+ break;
+
+ default:
+ if (zfore < zcolor_NUMCOLORS)
+ zfore = zRGB(zcolor_map[zfore]);
+ break;
+ }
+
+ switch (zback) {
+ case -1:
+ zback = -3;
+
+ case 0:
+ case 1:
+ zback = zcolor_map[zback];
+ break;
+
+ default:
+ if (zback < zcolor_NUMCOLORS)
+ zback = zRGB(zcolor_map[zback]);
+ break;
+ }
+
+#ifdef GARGLK
+ garglk_set_zcolors(zfore, zback);
+#endif /* GARGLK */
+
+ curr_fg = zfore;
+ curr_bg = zback;
+}
+
+void Processor::z_set_font() {
+ zword font = zargs[0];
+
+ switch (font) {
+ case 0:
+ // previous font
+ temp_font = curr_font;
+ curr_font = prev_font;
+ prev_font = temp_font;
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+ store (curr_font);
+ break;
+
+ case 1: /* normal font */
+ prev_font = curr_font;
+ curr_font = 1;
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+ store (prev_font);
+ break;
+
+ case 4: /* fixed-pitch font*/
+ prev_font = curr_font;
+ curr_font = 4;
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+ store (prev_font);
+ break;
+
+ case 2: // picture font, undefined per 1.1
+ case 3: // character graphics font
+ default: // unavailable
+ store (0);
+ break;
+ }
+}
+
+void Processor::z_set_cursor() {
+ cury = zargs[0];
+ curx = zargs[1];
+
+ if (gos_upper) {
+ if (cury > mach_status_ht) {
+ mach_status_ht = cury;
+ reset_status_ht();
+ }
+
+ glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
+ }
+}
+
+void Processor::z_set_text_style() {
+ int style;
+
+ if (zargs[0] == 0)
+ curstyle = 0;
+ else if (zargs[0] != 0xf000) /* not tickle time */
+ curstyle |= zargs[0];
+
+ if (h_flags & FIXED_FONT_FLAG || curr_font == 4)
+ style = curstyle | FIXED_WIDTH_STYLE;
+ else
+ style = curstyle;
+
+ if (gos_linepending && gos_curwin == gos_linewin)
+ return;
+
+ if (style & REVERSE_STYLE) {
+#ifdef GARGLK
+ garglk_set_reversevideo(true);
+#endif /* GARGLK */
+ }
+
+ if (style & FIXED_WIDTH_STYLE) {
+ if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
+ glk_set_style(style_BlockQuote); // monoz
+ else if (style & EMPHASIS_STYLE)
+ glk_set_style(style_Alert); // monoi
+ else if (style & BOLDFACE_STYLE)
+ glk_set_style(style_Subheader); // monob
+ else
+ glk_set_style(style_Preformatted); // monor
+ } else {
+ if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
+ glk_set_style(style_Note); // propz
+ else if (style & EMPHASIS_STYLE)
+ glk_set_style(style_Emphasized); // propi
+ else if (style & BOLDFACE_STYLE)
+ glk_set_style(style_Header); // propb
+ else
+ glk_set_style(style_Normal); // propr
+ }
+
+ if (curstyle == 0) {
+#ifdef GARGLK
+ garglk_set_reversevideo(false);
+#endif /* GARGLK */
+ }
+}
+
+void Processor::z_set_window() {
+ int win = zargs[0];
+
+ if (win == 0) {
+ glk_set_window(gos_lower);
+ gos_curwin = gos_lower;
+ } else {
+ if (gos_upper)
+ glk_set_window(gos_upper);
+ gos_curwin = gos_upper;
+ }
+
+ if (win == 0)
+ enable_scripting = true;
+ else
+ enable_scripting = false;
+
+ zargs[0] = 0xf000; // tickle tickle!
+ z_set_text_style();
+}
+
+void Processor::pad_status_line(int column) {
+ int spaces;
+ spaces = (h_screen_cols + 1 - curx) - column;
+ while (spaces-- > 0)
+ print_char(' ');
+}
+
+void Processor::z_show_status() {
+ zword global0;
+ zword global1;
+ zword global2;
+ zword addr;
+
+ bool brief = false;
+
+ if (!gos_upper)
+ return;
+
+ // One V5 game (Wishbringer Solid Gold) contains this opcode by accident,
+ // so just return if the version number does not fit
+ if (h_version >= V4)
+ return;
+
+ // Read all relevant global variables from the memory of the Z-machine
+ // into local variables
+
+ addr = h_globals;
+ LOW_WORD(addr, global0);
+ addr += 2;
+ LOW_WORD(addr, global1);
+ addr += 2;
+ LOW_WORD(addr, global2);
+
+ // Move to top of the status window, and print in reverse style.
+ glk_set_window(gos_upper);
+ gos_curwin = gos_upper;
+
+#ifdef GARGLK
+ garglk_set_reversevideo(true);
+#endif /* GARGLK */
+
+ curx = cury = 1;
+ glk_window_move_cursor(gos_upper, 0, 0);
+
+ // If the screen width is below 55 characters then we have to use
+ // the brief status line format
+ if (h_screen_cols < 55)
+ brief = true;
+
+ // Print the object description for the global variable 0
+ print_char (' ');
+ print_object (global0);
+
+ // A header flag tells us whether we have to display the current
+ // time or the score/moves information
+ if (h_config & CONFIG_TIME) {
+ // print hours and minutes
+ zword hours = (global1 + 11) % 12 + 1;
+
+ pad_status_line (brief ? 15 : 20);
+
+ print_string ("Time: ");
+
+ if (hours < 10)
+ print_char (' ');
+ print_num (hours);
+
+ print_char (':');
+
+ if (global2 < 10)
+ print_char ('0');
+ print_num (global2);
+
+ print_char (' ');
+
+ print_char ((global1 >= 12) ? 'p' : 'a');
+ print_char ('m');
+
+ } else {
+ // print score and moves
+ pad_status_line (brief ? 15 : 30);
+
+ print_string (brief ? "S: " : "Score: ");
+ print_num (global1);
+
+ pad_status_line (brief ? 8 : 14);
+
+ print_string (brief ? "M: " : "Moves: ");
+ print_num (global2);
+ }
+
+ // Pad the end of the status line with spaces
+ pad_status_line (0);
+
+ // Return to the lower window
+ glk_set_window(gos_lower);
+ gos_curwin = gos_lower;
+}
+
+void Processor::z_split_window() {
+ split_window(zargs[0]);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor_streams.cpp b/engines/glk/frotz/processor_streams.cpp
new file mode 100644
index 0000000000..b7b23b2db9
--- /dev/null
+++ b/engines/glk/frotz/processor_streams.cpp
@@ -0,0 +1,786 @@
+/* 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/frotz/processor.h"
+#include "glk/frotz/quetzal.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+zchar Processor::console_read_input(int max, zchar *buf, zword timeout, bool continued) {
+ return os_read_line(max, buf, timeout, max, continued);
+}
+
+zchar Processor::console_read_key(zword timeout) {
+ return os_read_key(timeout, 0);
+}
+
+void Processor::scrollback_char(zchar c) {
+ if (c == ZC_INDENT)
+ { scrollback_char (' '); scrollback_char (' '); scrollback_char (' '); return; }
+ if (c == ZC_GAP)
+ { scrollback_char (' '); scrollback_char (' '); return; }
+
+ os_scrollback_char(c);
+}
+
+void Processor::scrollback_word(const zchar *s) {
+ int i;
+
+ for (i = 0; s[i] != 0; i++) {
+ if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
+ i++;
+ else
+ scrollback_char(s[i]);
+ }
+}
+
+void Processor::scrollback_write_input(const zchar *buf, zchar key) {
+ int i;
+
+ for (i = 0; buf[i] != 0; i++)
+ scrollback_char (buf[i]);
+
+ if (key == ZC_RETURN)
+ scrollback_char ('\n');
+}
+
+void Processor::scrollback_erase_input(const zchar *buf) {
+ int width;
+ int i;
+
+ for (i = 0, width = 0; buf[i] != 0; i++)
+ width++;
+
+ os_scrollback_erase(width);
+
+}
+
+void Processor::stream_mssg_on() {
+ flush_buffer();
+
+ if (ostream_screen)
+ screen_mssg_on();
+ if (ostream_script && enable_scripting)
+ script_mssg_on();
+
+ message = true;
+}
+
+void Processor::stream_mssg_off() {
+ flush_buffer();
+
+ if (ostream_screen)
+ screen_mssg_off();
+ if (ostream_script && enable_scripting)
+ script_mssg_off();
+
+ message = false;
+}
+
+void Processor::stream_char(zchar c) {
+ if (ostream_screen)
+ screen_char(c);
+ if (ostream_script && enable_scripting)
+ script_char(c);
+ if (enable_scripting)
+ scrollback_char(c);
+}
+
+void Processor::stream_word(const zchar *s) {
+ if (ostream_memory && !message)
+ memory_word(s);
+ else {
+ if (ostream_screen)
+ screen_word(s);
+ if (ostream_script && enable_scripting)
+ script_word(s);
+ if (enable_scripting)
+ scrollback_word(s);
+ }
+}
+
+void Processor::stream_new_line() {
+ if (ostream_memory && !message)
+ memory_new_line();
+ else {
+ if (ostream_screen)
+ screen_new_line();
+ if (ostream_script && enable_scripting)
+ script_new_line();
+ if (enable_scripting)
+ os_scrollback_char ('\n');
+ }
+}
+
+zchar Processor::stream_read_key(zword timeout, zword routine, bool hot_keys) {
+ zchar key = ZC_BAD;
+
+ flush_buffer();
+
+ // Read key from current input stream
+continue_input:
+
+ do {
+ if (istream_replay)
+ key = replay_read_key();
+ else
+ key = console_read_key(timeout);
+ } while (key == ZC_BAD);
+
+ // Copy key to the command file
+ if (ostream_record && !istream_replay)
+ record_write_key(key);
+
+ // Handle timeouts
+ if (key == ZC_TIME_OUT)
+ if (direct_call (routine) == 0)
+ goto continue_input;
+
+ // Return key
+ return key;
+}
+
+zchar Processor::stream_read_input(int max, zchar *buf, zword timeout, zword routine,
+ bool hot_keys, bool no_scripting) {
+ zchar key = ZC_BAD;
+ bool no_scrollback = no_scripting;
+
+ if (h_version == V6 && _storyId == UNKNOWN && !ostream_script)
+ no_scrollback = false;
+
+ flush_buffer();
+
+ // Remove initial input from the transscript file or from the screen
+ if (ostream_script && enable_scripting && !no_scripting)
+ script_erase_input(buf);
+
+ // Read input line from current input stream
+continue_input:
+
+ do {
+ if (istream_replay)
+ key = replay_read_input(buf);
+ else
+ key = console_read_input(max, buf, timeout, key != ZC_BAD);
+ } while (key == ZC_BAD);
+
+ // Copy input line to the command file
+ if (ostream_record && !istream_replay)
+ record_write_input(buf, key);
+
+ // Handle timeouts
+ if (key == ZC_TIME_OUT)
+ if (direct_call(routine) == 0)
+ goto continue_input;
+
+ // Copy input line to transscript file or to the screen
+ if (ostream_script && enable_scripting && !no_scripting)
+ script_write_input(buf, key);
+
+ // Return terminating key
+ return key;
+}
+
+void Processor::script_open() {
+ h_flags &= ~SCRIPTING_FLAG;
+
+ frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript,
+ filemode_WriteAppend);
+ sfp = glk_stream_open_file(fref, filemode_WriteAppend);
+
+ if (sfp != nullptr) {
+ sfp->setPosition(0, seekmode_End);
+
+ h_flags |= SCRIPTING_FLAG;
+
+ script_valid = true;
+ ostream_script = true;
+
+ script_width = 0;
+ } else {
+ print_string("Cannot open file\n");
+ }
+
+ SET_WORD(H_FLAGS, h_flags);
+}
+
+void Processor::script_close() {
+ h_flags &= ~SCRIPTING_FLAG;
+ SET_WORD(H_FLAGS, h_flags);
+
+ glk_stream_close(sfp);
+ ostream_script = false;
+}
+
+void Processor::script_new_line() {
+ script_char('\n');
+ script_width = 0;
+}
+
+void Processor::script_char(zchar c) {
+ if (c == ZC_INDENT && script_width != 0)
+ c = ' ';
+
+ if (c == ZC_INDENT) {
+ script_char(' ');
+ script_char(' ');
+ script_char(' ');
+ return;
+ }
+ if (c == ZC_GAP) {
+ script_char(' ');
+ script_char(' ');
+ return;
+ }
+
+ sfp->putCharUni(c);
+ script_width++;
+}
+
+void Processor::script_word(const zchar *s) {
+ int width;
+ int i;
+
+ if (*s == ZC_INDENT && script_width != 0)
+ script_char(*s++);
+
+ for (i = 0, width = 0; s[i] != 0; i++) {
+ if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT)
+ i++;
+ else if (s[i] == ZC_GAP)
+ width += 3;
+ else if (s[i] == ZC_INDENT)
+ width += 2;
+ else
+ width += 1;
+ }
+
+ if (_script_cols != 0 && script_width + width > _script_cols) {
+ if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
+ s++;
+
+ script_new_line();
+ }
+
+ for (i = 0; s[i] != 0; i++) {
+ if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
+ i++;
+ else
+ script_char(s[i]);
+ }
+}
+
+void Processor::script_write_input(const zchar *buf, zchar key) {
+ int width;
+ int i;
+
+ for (i = 0, width = 0; buf[i] != 0; i++)
+ width++;
+
+ if (_script_cols != 0 && script_width + width > _script_cols)
+ script_new_line();
+
+ for (i = 0; buf[i] != 0; i++)
+ script_char(buf[i]);
+
+ if (key == ZC_RETURN)
+ script_new_line();
+}
+
+void Processor::script_erase_input(const zchar *buf) {
+ int width;
+ int i;
+
+ for (i = 0, width = 0; buf[i] != 0; i++)
+ width++;
+
+ sfp->setPosition(-width, seekmode_Current);
+ script_width -= width;
+}
+
+void Processor::script_mssg_on() {
+ if (script_width != 0)
+ script_new_line();
+
+ script_char(ZC_INDENT);
+}
+
+void Processor::script_mssg_off() {
+ script_new_line();
+}
+
+void Processor::record_open() {
+ frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Write);
+ if ((rfp = glk_stream_open_file(fref, filemode_Write)) != nullptr)
+ ostream_record = true;
+ else
+ print_string("Cannot open file\n");
+}
+
+void Processor::record_close() {
+ glk_stream_close(rfp);
+ ostream_record = false;
+}
+
+void Processor::record_code(int c, bool force_encoding) {
+ if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) {
+ int i;
+
+ rfp->putChar('[');
+
+ for (i = 10000; i != 0; i /= 10)
+ if (c >= i || i == 1)
+ rfp->putChar('0' + (c / i) % 10);
+
+ rfp->putChar(']');
+ } else {
+ rfp->putChar(c);
+ }
+}
+
+void Processor::record_char(zchar c) {
+ if (c != ZC_RETURN) {
+ if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) {
+ record_code(translate_to_zscii(c), false);
+ if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
+ record_code(mouse_x, true);
+ record_code(mouse_y, true);
+ }
+ } else {
+ record_code(1000 + c - ZC_HKEY_MIN, true);
+ }
+ }
+}
+
+void Processor::record_write_key(zchar key) {
+ record_char(key);
+ rfp->putChar('\n');
+}
+
+void Processor::record_write_input(const zchar *buf, zchar key) {
+ zchar c;
+
+ while ((c = *buf++) != 0)
+ record_char(c);
+
+ record_write_key(key);
+}
+
+void Processor::replay_open() {
+ frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Read);
+ if ((pfp = glk_stream_open_file(fref, filemode_Read)) != nullptr)
+ istream_replay = true;
+ else
+ print_string("Cannot open file\n");
+}
+
+void Processor::replay_close() {
+ glk_stream_close(pfp);
+ istream_replay = false;
+}
+
+int Processor::replay_code() {
+ int c;
+
+ if ((c = pfp->getChar()) == '[') {
+ int c2;
+
+ c = 0;
+
+ while ((c2 = pfp->getChar()) != EOF && c2 >= '0' && c2 <= '9')
+ c = 10 * c + c2 - '0';
+
+ return (c2 == ']') ? c : EOF;
+ } else {
+ return c;
+ }
+}
+
+zchar Processor::replay_char() {
+ int c;
+
+ if ((c = replay_code()) != EOF) {
+ if (c != '\n') {
+ if (c < 1000) {
+
+ c = translate_from_zscii(c);
+
+ if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
+ mouse_x = replay_code();
+ mouse_y = replay_code();
+ }
+
+ return c;
+ } else {
+ return ZC_HKEY_MIN + c - 1000;
+ }
+ }
+
+ pfp->unputBuffer("\n", 1);
+ return ZC_RETURN;
+
+ } else {
+ return ZC_BAD;
+ }
+}
+
+zchar Processor::replay_read_key() {
+ zchar key = replay_char();
+
+ if (pfp->getChar() != '\n') {
+ replay_close();
+ return ZC_BAD;
+ } else {
+ return key;
+ }
+}
+
+zchar Processor::replay_read_input(zchar *buf) {
+ zchar c;
+
+ for (;;) {
+ c = replay_char();
+
+ if (c == ZC_BAD || is_terminator(c))
+ break;
+
+ *buf++ = c;
+ }
+
+ *buf = 0;
+
+ if (pfp->getChar() != '\n') {
+ replay_close();
+ return ZC_BAD;
+ } else {
+ return c;
+ }
+}
+
+
+void Processor::z_input_stream() {
+ flush_buffer();
+
+ if (zargs[0] == 0 && istream_replay)
+ replay_close();
+ if (zargs[0] == 1 && !istream_replay)
+ replay_open();
+}
+
+void Processor::z_output_stream() {
+ flush_buffer();
+
+ switch ((short) zargs[0]) {
+ case 1: ostream_screen = true;
+ break;
+ case -1: ostream_screen = false;
+ break;
+ case 2: if (!ostream_script) script_open();
+ break;
+ case -2: if (ostream_script) script_close();
+ break;
+ case 3: memory_open(zargs[1], zargs[2], zargc >= 3);
+ break;
+ case -3: memory_close();
+ break;
+ case 4: if (!ostream_record) record_open();
+ break;
+ case -4: if (ostream_record) record_close();
+ break;
+ default:
+ break;
+ }
+}
+
+void Processor::z_restart() {
+ flush_buffer();
+
+ os_restart_game(RESTART_BEGIN);
+
+ seed_random(0);
+
+ if (!first_restart) {
+ story_fp->seek(blorb_ofs);
+
+ if (story_fp->read(zmp, h_dynamic_size) != h_dynamic_size)
+ error("Story file read error");
+
+ } else {
+ first_restart = false;
+ }
+
+ restart_header();
+ restart_screen();
+
+ _sp = _fp = _stack + STACK_SIZE;
+ _frameCount = 0;
+
+ if (h_version != V6 && h_version != V9) {
+ long pc = (long)h_start_pc;
+ SET_PC(pc);
+ } else {
+ call(h_start_pc, 0, nullptr, 0);
+ }
+
+ os_restart_game(RESTART_END);
+}
+
+void Processor::z_save() {
+#ifdef TODO
+ bool success = false;
+
+ if (zargc != 0) {
+ // Open auxilary file
+ frefid_t ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode,
+ filemode_Write, 0);
+ if (ref == nullptr)
+ goto finished;
+
+ // Write data
+ strid_t f = glk_stream_open_file(ref, filemode_Write);
+
+ glk_put_buffer_stream(f, (const char *)zmp + zargs[0], zargs[1]);
+
+ glk_stream_close(f);
+
+ } else {
+ long pc;
+ zword addr;
+ zword nsp, nfp;
+ int skip;
+ int i;
+
+ /* Open game file */
+
+ if ((gfp = frotzopenprompt (FILE_SAVE)) == nullptr)
+ goto finished;
+
+ if (_save_quetzal) {
+ success = save_quetzal (gfp, story_fp, blorb_ofs);
+ } else {
+ /* Write game file */
+
+ fputc ((int) hi (h_release), gfp);
+ fputc ((int) lo (h_release), gfp);
+ fputc ((int) hi (h_checksum), gfp);
+ fputc ((int) lo (h_checksum), gfp);
+
+ GET_PC (pc)
+
+ fputc ((int) (pc >> 16) & 0xff, gfp);
+ fputc ((int) (pc >> 8) & 0xff, gfp);
+ fputc ((int) (pc) & 0xff, gfp);
+
+ nsp = (int) (_sp - _stack);
+ nfp = (int) (_fp - _stack);
+
+ fputc ((int) hi (nsp), gfp);
+ fputc ((int) lo (nsp), gfp);
+ fputc ((int) hi (nfp), gfp);
+ fputc ((int) lo (nfp), gfp);
+
+ for (i = nsp; i < STACK_SIZE; i++) {
+ fputc ((int) hi (_stack[i]), gfp);
+ fputc ((int) lo (_stack[i]), gfp);
+ }
+
+ fseek (story_fp, blorb_ofs, SEEK_SET);
+
+ for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
+ if (zmp[addr] != fgetc (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
+ fputc (skip, gfp);
+ fputc (zmp[addr], gfp);
+ skip = 0;
+ } else skip++;
+ }
+
+ /* Close game file and check for errors */
+
+ if (fclose (gfp) == EOF || ferror (story_fp)) {
+ print_string ("Error writing save file\n");
+ goto finished;
+ }
+
+ /* Success */
+
+ success = 1;
+
+ }
+
+finished:
+
+ if (h_version <= V3)
+ branch (success);
+ else
+ store (success);
+#endif
+}
+
+void Processor::z_restore() {
+#ifdef TODO
+ FILE *gfp;
+
+ zword success = 0;
+
+ if (zargc != 0) {
+
+ /* Get the file name */
+
+ /* Open auxilary file */
+
+ if ((gfp = frotzopenprompt(FILE_LOAD_AUX)) == nullptr)
+ goto finished;
+
+ /* Load auxilary file */
+
+ success = fread (zmp + zargs[0], 1, zargs[1], gfp);
+
+ /* Close auxilary file */
+
+ fclose (gfp);
+
+ } else {
+
+ long pc;
+ zword release;
+ zword addr;
+ int i;
+
+ /* Open game file */
+
+ if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
+ goto finished;
+
+ if (_save_quetzal) {
+ success = restore_quetzal (gfp, story_fp, blorb_ofs);
+
+ } else {
+ /* Load game file */
+
+ release = (unsigned) fgetc (gfp) << 8;
+ release |= fgetc (gfp);
+
+ () fgetc (gfp);
+ () fgetc (gfp);
+
+ /* Check the release number */
+
+ if (release == h_release) {
+
+ pc = (long) fgetc (gfp) << 16;
+ pc |= (unsigned) fgetc (gfp) << 8;
+ pc |= fgetc (gfp);
+
+ SET_PC (pc);
+
+ _sp = _stack + (fgetc (gfp) << 8);
+ _sp += fgetc (gfp);
+ _fp = _stack + (fgetc (gfp) << 8);
+ _fp += fgetc (gfp);
+
+ for (i = (int) (_sp - _stack); i < STACK_SIZE; i++) {
+ _stack[i] = (unsigned) fgetc (gfp) << 8;
+ _stack[i] |= fgetc (gfp);
+ }
+
+ fseek (story_fp, blorb_ofs, SEEK_SET);
+
+ for (addr = 0; addr < h_dynamic_size; addr++) {
+ int skip = fgetc (gfp);
+ for (i = 0; i < skip; i++)
+ zmp[addr++] = fgetc (story_fp);
+ zmp[addr] = fgetc (gfp);
+ () fgetc (story_fp);
+ }
+
+ /* Check for errors */
+
+ if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
+ success = -1;
+ else
+
+ /* Success */
+
+ success = 2;
+
+ } else print_string ("Invalid save file\n");
+ }
+
+ if ((short) success >= 0) {
+
+ /* Close game file */
+
+ fclose (gfp);
+
+ if ((short) success > 0) {
+ zbyte old_screen_rows;
+ zbyte old_screen_cols;
+
+ /* In V3, reset the upper window. */
+ if (h_version == V3)
+ split_window (0);
+
+ LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
+ LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
+
+ /* Reload cached header fields. */
+ restart_header ();
+
+ /*
+ * Since QUETZAL files may be saved on many different machines,
+ * the screen sizes may vary a lot. Erasing the status window
+ * seems to cover up most of the resulting badness.
+ */
+ if (h_version > V3 && h_version != V6
+ && (h_screen_rows != old_screen_rows
+ || h_screen_cols != old_screen_cols))
+ erase_window (1);
+ }
+ } else
+ os_fatal ("Error reading save file");
+ }
+
+finished:
+
+ if (h_version <= V3)
+ branch (success);
+ else
+ store (success);
+#endif
+}
+
+void Processor::z_verify() {
+ zword checksum = 0;
+
+ // Sum all bytes in story file except header bytes
+ story_fp->seek(blorb_ofs + 64);
+
+ for (uint i = 64; i < story_size; i++)
+ checksum += story_fp->readByte();
+
+ // Branch if the checksums are equal
+ branch(checksum == h_checksum);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor_table.cpp b/engines/glk/frotz/processor_table.cpp
new file mode 100644
index 0000000000..bb163899af
--- /dev/null
+++ b/engines/glk/frotz/processor_table.cpp
@@ -0,0 +1,120 @@
+/* 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/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+void Processor::z_copy_table() {
+ zword addr;
+ zword size = zargs[2];
+ zbyte value;
+ int i;
+
+ if (zargs[1] == 0) /* zero table */
+
+ for (i = 0; i < size; i++)
+ storeb((zword) (zargs[0] + i), 0);
+
+ else if ((short) size < 0 || zargs[0] > zargs[1]) /* copy forwards */
+
+ for (i = 0; i < (((short) size < 0) ? - (short) size : size); i++) {
+ addr = zargs[0] + i;
+ LOW_BYTE(addr, value);
+ storeb((zword) (zargs[1] + i), value);
+ } else {
+ // copy backwards
+ for (i = size - 1; i >= 0; i--) {
+ addr = zargs[0] + i;
+ LOW_BYTE(addr, value);
+ storeb((zword) (zargs[1] + i), value);
+ }
+ }
+}
+
+void Processor::z_loadb() {
+ zword addr = zargs[0] + zargs[1];
+ zbyte value;
+
+ LOW_BYTE(addr, value);
+
+ store(value);
+}
+
+void Processor::z_loadw() {
+ zword addr = zargs[0] + 2 * zargs[1];
+ zword value;
+
+ LOW_WORD(addr, value);
+
+ store(value);
+}
+
+void Processor::z_scan_table() {
+ zword addr = zargs[1];
+ int i;
+
+ // Supply default arguments
+ if (zargc < 4)
+ zargs[3] = 0x82;
+
+ // Scan byte or word array
+ for (i = 0; i < zargs[2]; i++) {
+ if (zargs[3] & 0x80) {
+ // scan word array
+ zword wvalue;
+
+ LOW_WORD(addr, wvalue);
+
+ if (wvalue == zargs[0])
+ goto finished;
+ } else {
+ // scan byte array
+ zbyte bvalue;
+
+ LOW_BYTE(addr, bvalue);
+
+ if (bvalue == zargs[0])
+ goto finished;
+ }
+
+ addr += zargs[3] & 0x7f;
+ }
+
+ addr = 0;
+
+finished:
+ store(addr);
+ branch(addr);
+}
+
+void Processor::z_storeb() {
+ storeb((zword) (zargs[0] + zargs[1]), zargs[2]);
+}
+
+void Processor::z_storew() {
+ storew((zword)(zargs[0] + 2 * zargs[1]), zargs[2]);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor_text.cpp b/engines/glk/frotz/processor_text.cpp
new file mode 100644
index 0000000000..e05879398e
--- /dev/null
+++ b/engines/glk/frotz/processor_text.cpp
@@ -0,0 +1,886 @@
+/* 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/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+zchar Processor::ZSCII_TO_LATIN1[] = {
+ 0x0e4, 0x0f6, 0x0fc, 0x0c4, 0x0d6, 0x0dc, 0x0df, 0x0bb,
+ 0x0ab, 0x0eb, 0x0ef, 0x0ff, 0x0cb, 0x0cf, 0x0e1, 0x0e9,
+ 0x0ed, 0x0f3, 0x0fa, 0x0fd, 0x0c1, 0x0c9, 0x0cd, 0x0d3,
+ 0x0da, 0x0dd, 0x0e0, 0x0e8, 0x0ec, 0x0f2, 0x0f9, 0x0c0,
+ 0x0c8, 0x0cc, 0x0d2, 0x0d9, 0x0e2, 0x0ea, 0x0ee, 0x0f4,
+ 0x0fb, 0x0c2, 0x0ca, 0x0ce, 0x0d4, 0x0db, 0x0e5, 0x0c5,
+ 0x0f8, 0x0d8, 0x0e3, 0x0f1, 0x0f5, 0x0c3, 0x0d1, 0x0d5,
+ 0x0e6, 0x0c6, 0x0e7, 0x0c7, 0x0fe, 0x0f0, 0x0de, 0x0d0,
+ 0x0a3, 0x153, 0x152, 0x0a1, 0x0bf
+};
+
+zchar Processor::translate_from_zscii(zbyte c) {
+ if (c == 0xfc)
+ return ZC_MENU_CLICK;
+ if (c == 0xfd)
+ return ZC_DOUBLE_CLICK;
+ if (c == 0xfe)
+ return ZC_SINGLE_CLICK;
+
+ if (c >= 0x9b && _storyId != BEYOND_ZORK) {
+ if (hx_unicode_table != 0) {
+ // game has its own Unicode table
+ zbyte N;
+ LOW_BYTE(hx_unicode_table, N);
+
+ if (c - 0x9b < N) {
+ zword addr = hx_unicode_table + 1 + 2 * (c - 0x9b);
+ zword unicode;
+
+ LOW_WORD(addr, unicode);
+
+ if (unicode < 0x20)
+ return '?';
+
+ return unicode;
+ } else {
+ return '?';
+ }
+ } else {
+ // game uses standard set
+ if (c <= 0xdf) {
+ return ZSCII_TO_LATIN1[c - 0x9b];
+ } else {
+ return '?';
+ }
+ }
+ }
+
+ return (zchar)c;
+}
+
+zbyte Processor::unicode_to_zscii(zchar c) {
+ int i;
+
+ if (c >= ZC_LATIN1_MIN) {
+ if (hx_unicode_table != 0) {
+ // game has its own Unicode table
+ zbyte N;
+ LOW_BYTE(hx_unicode_table, N);
+
+ for (i = 0x9b; i < 0x9b + N; i++) {
+ zword addr = hx_unicode_table + 1 + 2 * (i - 0x9b);
+ zword unicode;
+
+ LOW_WORD(addr, unicode);
+
+ if (c == unicode)
+ return (zbyte) i;
+ }
+
+ return 0;
+ } else {
+ // game uses standard set
+ for (i = 0x9b; i <= 0xdf; i++)
+ if (c == ZSCII_TO_LATIN1[i - 0x9b])
+ return (zbyte) i;
+
+ return 0;
+ }
+ }
+
+ return (zbyte)c;
+}
+
+zbyte Processor::translate_to_zscii(zchar c) {
+ if (c == ZC_SINGLE_CLICK)
+ return 0xfe;
+ if (c == ZC_DOUBLE_CLICK)
+ return 0xfd;
+ if (c == ZC_MENU_CLICK)
+ return 0xfc;
+ if (c == 0)
+ return 0;
+
+ c = unicode_to_zscii (c);
+ if (c == 0)
+ c = '?';
+
+ return (zbyte)c;
+}
+
+zchar Processor::alphabet(int set, int index) {
+ if (h_version > V1 && set == 2 && index == 1)
+ // always newline
+ return '\r';
+
+ if (h_alphabet != 0) {
+ // game uses its own alphabet
+ zbyte c;
+
+ zword addr = h_alphabet + 26 * set + index;
+ LOW_BYTE(addr, c);
+
+ return translate_from_zscii(c);
+ } else {
+ // game uses default alphabet
+ if (set == 0)
+ return 'a' + index;
+ else if (set == 1)
+ return 'A' + index;
+ else if (h_version == V1)
+ return " 0123456789.,!?_#'\"/\\<-:()"[index];
+ else
+ return " ^0123456789.,!?_#'\"/\\-:()"[index];
+ }
+}
+
+void Processor::find_resolution() {
+ zword dct = h_dictionary;
+ zword entry_count;
+ zbyte sep_count;
+ zbyte entry_len;
+
+ LOW_BYTE(dct, sep_count);
+ dct += 1 + sep_count; // skip word separators
+ LOW_BYTE(dct, entry_len);
+ dct += 1; // skip entry length
+ LOW_WORD(dct, entry_count);
+ dct += 2; // get number of entries
+
+ if (h_version < V9) {
+ _resolution = (h_version <= V3) ? 2 : 3;
+ } else {
+ zword addr = dct;
+ zword code;
+
+ if (entry_count == 0)
+ runtimeError(ERR_DICT_LEN);
+
+ // check the first word in the dictionary
+ do {
+ LOW_WORD(addr, code);
+ addr += 2;
+ } while (!(code & 0x8000) && (addr - dct < entry_len + 1));
+
+ _resolution = (addr - dct) / 2;
+ }
+
+ if (2 * _resolution > entry_len) {
+ runtimeError(ERR_DICT_LEN);
+ }
+
+ _decoded = (zchar *)malloc (sizeof (zchar) * (3 * _resolution) + 1);
+ _encoded = (zchar *)malloc (sizeof (zchar) * _resolution);
+}
+
+void Processor::load_string (zword addr, zword length) {
+ int i = 0;
+
+ if (_resolution == 0)
+ find_resolution();
+
+ while (i < 3 * _resolution) {
+ if (i < length) {
+ zbyte c;
+ LOW_BYTE(addr, c);
+ addr++;
+
+ _decoded[i++] = translate_from_zscii(c);
+ } else {
+ _decoded[i++] = 0;
+ }
+ }
+}
+
+void Processor::encode_text(int padding) {
+ static const zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0, 0, 0, 0 };
+ static const zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0, 0 };
+ static const zchar wait[] = { 'w', 'a', 'i', 't', 0, 0, 0, 0, 0 };
+
+ zbyte *zchars;
+ const zchar *ptr;
+ zchar c;
+ int i = 0;
+
+ if (_resolution == 0) find_resolution();
+
+ zchars = new byte[3 * (_resolution + 1)];
+ ptr = _decoded;
+
+ // Expand abbreviations that some old Infocom games lack
+ if (_expand_abbreviations && (h_version <= V8)) {
+ if (padding == 0x05 && _decoded[1] == 0) {
+ switch (_decoded[0]) {
+ case 'g': ptr = again; break;
+ case 'x': ptr = examine; break;
+ case 'z': ptr = wait; break;
+ default: break;
+ }
+ }
+ }
+
+ // Translate string to a sequence of Z-characters
+ while (i < 3 * _resolution) {
+ if ((c = *ptr++) != 0) {
+ int index, set;
+ zbyte c2;
+
+ if (c == ' ') {
+ zchars[i++] = 0;
+ continue;
+ }
+
+ // Search character in the alphabet
+ for (set = 0; set < 3; set++)
+ for (index = 0; index < 26; index++)
+ if (c == alphabet (set, index))
+ goto letter_found;
+
+ // Character not found, store its ZSCII value
+ c2 = translate_to_zscii (c);
+
+ zchars[i++] = 5;
+ zchars[i++] = 6;
+ zchars[i++] = c2 >> 5;
+ zchars[i++] = c2 & 0x1f;
+ continue;
+
+ letter_found:
+ // Character found, store its index
+ if (set != 0)
+ zchars[i++] = ((h_version <= V2) ? 1 : 3) + set;
+
+ zchars[i++] = index + 6;
+ } else {
+ zchars[i++] = padding;
+ }
+ }
+
+ // Three Z-characters make a 16bit word
+ for (i = 0; i < _resolution; i++)
+ _encoded[i] =
+ (zchars[3 * i + 0] << 10) |
+ (zchars[3 * i + 1] << 5) |
+ (zchars[3 * i + 2]);
+
+ _encoded[_resolution - 1] |= 0x8000;
+ delete[] zchars;
+}
+
+#define outchar(c) if (st == VOCABULARY) *ptr++=c; else print_char(c)
+
+void Processor::decode_text(enum string_type st, zword addr) {
+ zchar *ptr = nullptr;
+ long byte_addr = 0;
+ zchar c2;
+ zword code;
+ zbyte c, prev_c = 0;
+ int shift_state = 0;
+ int shift_lock = 0;
+ int status = 0;
+
+ if (_resolution == 0)
+ find_resolution();
+
+ // Calculate the byte address if necessary
+ if (st == ABBREVIATION)
+ byte_addr = (long)addr << 1;
+
+ else if (st == HIGH_STRING) {
+ if (h_version <= V3)
+ byte_addr = (long)addr << 1;
+ else if (h_version <= V5)
+ byte_addr = (long)addr << 2;
+ else if (h_version <= V7)
+ byte_addr = ((long)addr << 2) + ((long)h_strings_offset << 3);
+ else if (h_version <= V8)
+ byte_addr = (long)addr << 3;
+ else {
+ // h_version == V9
+ long indirect = (long)addr << 2;
+ HIGH_LONG(indirect, byte_addr);
+ }
+
+ if ((uint)byte_addr >= story_size)
+ runtimeError(ERR_ILL_PRINT_ADDR);
+ }
+
+ // Loop until a 16bit word has the highest bit set
+ if (st == VOCABULARY)
+ ptr = _decoded;
+
+ do {
+ int i;
+
+ // Fetch the next 16bit word
+ if (st == LOW_STRING || st == VOCABULARY) {
+ LOW_WORD(addr, code);
+ addr += 2;
+ } else if (st == HIGH_STRING || st == ABBREVIATION) {
+ HIGH_WORD(byte_addr, code);
+ byte_addr += 2;
+ } else {
+ CODE_WORD(code);
+ }
+
+ // Read its three Z-characters
+ for (i = 10; i >= 0; i -= 5) {
+ zword abbr_addr;
+ zword ptr_addr;
+ zchar zc;
+
+ c = (code >> i) & 0x1f;
+
+ switch (status) {
+ case 0:
+ // normal operation
+ if (shift_state == 2 && c == 6)
+ status = 2;
+
+ else if (h_version == V1 && c == 1)
+ new_line();
+
+ else if (h_version >= V2 && shift_state == 2 && c == 7)
+ new_line();
+
+ else if (c >= 6)
+ outchar(alphabet(shift_state, c - 6));
+
+ else if (c == 0)
+ outchar(' ');
+
+ else if (h_version >= V2 && c == 1)
+ status = 1;
+
+ else if (h_version >= V3 && c <= 3)
+ status = 1;
+
+ else {
+ shift_state = (shift_lock + (c & 1) + 1) % 3;
+
+ if (h_version <= V2 && c >= 4)
+ shift_lock = shift_state;
+
+ break;
+ }
+
+ shift_state = shift_lock;
+ break;
+
+ case 1:
+ // abbreviation
+ ptr_addr = h_abbreviations + 64 * (prev_c - 1) + 2 * c;
+
+ LOW_WORD(ptr_addr, abbr_addr);
+ decode_text(ABBREVIATION, abbr_addr);
+
+ status = 0;
+ break;
+
+ case 2:
+ // ZSCII character - first part
+ status = 3;
+ break;
+
+ case 3:
+ // ZSCII character - second part
+ zc = (prev_c << 5) | c;
+
+ if (zc > 767) {
+ // Unicode escape
+ while (zc-- > 767) {
+ if (st == LOW_STRING || st == VOCABULARY) {
+ LOW_WORD(addr, c2);
+ addr += 2;
+ } else if (st == HIGH_STRING || st == ABBREVIATION) {
+ HIGH_WORD(byte_addr, c2);
+ byte_addr += 2;
+ } else
+ CODE_WORD(c2);
+
+ outchar(c2 ^ 0xFFFF);
+ }
+ } else {
+ c2 = translate_from_zscii(zc);
+ outchar(c2);
+ }
+
+ status = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ prev_c = c;
+ }
+ } while (!(code & 0x8000));
+
+ if (st == VOCABULARY)
+ *ptr = 0;
+}
+
+#undef outchar
+
+void Processor::print_num(zword value) {
+ int i;
+
+ /* Print sign */
+
+ if ((short)value < 0) {
+ print_char('-');
+ value = -(short)value;
+ }
+
+ /* Print absolute value */
+
+ for (i = 10000; i != 0; i /= 10)
+ if (value >= i || i == 1)
+ print_char('0' + (value / i) % 10);
+
+}
+
+void Processor::print_object(zword object) {
+ zword addr = object_name(object);
+ zword code = 0x94a5;
+ zbyte length;
+
+ LOW_BYTE(addr, length);
+ addr++;
+
+ if (length != 0)
+ LOW_WORD(addr, code);
+
+ if (code == 0x94a5) {
+ // _encoded text 0x94a5 == empty string
+ print_string("object#"); // supply a generic name
+ print_num(object); // for anonymous objects
+ } else {
+ decode_text(LOW_STRING, addr);
+ }
+}
+
+zword Processor::lookup_text(int padding, zword dct) {
+ zword entry_addr;
+ zword entry_count;
+ zword entry;
+ zword addr;
+ zbyte entry_len;
+ zbyte sep_count;
+ int entry_number;
+ int lower, upper;
+ int i;
+ bool sorted;
+
+ if (_resolution == 0)
+ find_resolution();
+
+ encode_text(padding);
+
+ LOW_BYTE(dct, sep_count); // skip word separators
+ dct += 1 + sep_count;
+ LOW_BYTE(dct, entry_len); // get length of entries
+ dct += 1;
+ LOW_WORD(dct, entry_count); // get number of entries
+ dct += 2;
+
+ if ((short)entry_count < 0) {
+ // bad luck, entries aren't sorted
+ entry_count = -(short)entry_count;
+ sorted = false;
+
+ } else {
+ sorted = true; // entries are sorted
+ }
+
+ lower = 0;
+ upper = entry_count - 1;
+
+ while (lower <= upper) {
+ if (sorted)
+ // binary search
+ entry_number = (lower + upper) / 2;
+ else
+ // linear search
+ entry_number = lower;
+
+ entry_addr = dct + entry_number * entry_len;
+
+ // Compare word to dictionary entry
+ addr = entry_addr;
+
+ for (i = 0; i < _resolution; i++) {
+ LOW_WORD(addr, entry);
+ if (_encoded[i] != entry)
+ goto continuing;
+ addr += 2;
+ }
+
+ return entry_addr; // exact match found, return now
+
+ continuing:
+ if (sorted) {
+ // binary search
+ if (_encoded[i] > entry)
+ lower = entry_number + 1;
+ else
+ upper = entry_number - 1;
+ } else {
+ // linear search
+ lower++;
+ }
+ }
+
+ // No exact match has been found
+ if (padding == 0x05)
+ return 0;
+
+ entry_number = (padding == 0x00) ? lower : upper;
+
+ if (entry_number == -1 || entry_number == entry_count)
+ return 0;
+
+ return dct + entry_number * entry_len;
+}
+
+void Processor::tokenise_text(zword text, zword length, zword from, zword parse, zword dct, bool flag) {
+ zword addr;
+ zbyte token_max, token_count;
+
+ LOW_BYTE(parse, token_max);
+ parse++;
+ LOW_BYTE(parse, token_count);
+
+ if (token_count < token_max) {
+ // sufficient space left for token?
+ storeb(parse++, token_count + 1);
+
+ load_string((zword)(text + from), length);
+
+ addr = lookup_text(0x05, dct);
+
+ if (addr != 0 || !flag) {
+
+ parse += 4 * token_count;
+
+ storew((zword)(parse + 0), addr);
+ storeb((zword)(parse + 2), length);
+ storeb((zword)(parse + 3), from);
+ }
+ }
+}
+
+void Processor::tokenise_line(zword text, zword token, zword dct, bool flag) {
+ zword addr1;
+ zword addr2;
+ zbyte length = 0;
+ zbyte c;
+
+ // Use standard dictionary if the given dictionary is zero
+ if (dct == 0)
+ dct = h_dictionary;
+
+ // Remove all tokens before inserting new ones
+ storeb((zword)(token + 1), 0);
+
+ // Move the first pointer across the text buffer searching for the beginning
+ // of a word. If this succeeds, store the position in a second pointer.
+ // Move the first pointer searching for the end of the word. When it is found,
+ // "tokenise" the word. Continue until the end of the buffer is reached.
+ addr1 = text;
+ addr2 = 0;
+
+ if (h_version >= V5) {
+ addr1++;
+ LOW_BYTE(addr1, length);
+ }
+
+ do {
+ zword sep_addr;
+ zbyte sep_count;
+ zbyte separator;
+
+ // Fetch next ZSCII character
+ addr1++;
+
+ if (h_version >= V5 && addr1 == text + 2 + length)
+ c = 0;
+ else
+ LOW_BYTE(addr1, c);
+
+ // Check for separator
+ sep_addr = dct;
+
+ LOW_BYTE(sep_addr, sep_count);
+ sep_addr++;
+
+ do {
+ LOW_BYTE(sep_addr, separator);
+ sep_addr++;
+ } while (c != separator && --sep_count != 0);
+
+ // This could be the start or the end of a word
+ if (sep_count == 0 && c != ' ' && c != 0) {
+ if (addr2 == 0)
+ addr2 = addr1;
+ } else if (addr2 != 0) {
+ tokenise_text(text, (zword)(addr1 - addr2), (zword)(addr2 - text),
+ token, dct, flag);
+
+ addr2 = 0;
+ }
+
+ // Translate separator (which is a word in its own right)
+ if (sep_count != 0)
+ tokenise_text(text, (zword)(1), (zword)(addr1 - text), token, dct, flag);
+
+ } while (c != 0);
+}
+
+int Processor::completion(const zchar *buffer, zchar *result) {
+ zword minaddr;
+ zword maxaddr;
+ zchar *ptr;
+ zchar c;
+ int len;
+ int i;
+
+ *result = 0;
+
+ if (_resolution == 0)
+ find_resolution();
+
+ // Copy last word to "_decoded" string
+ len = 0;
+
+ while ((c = *buffer++) != 0)
+ if (c != ' ') {
+ if (len < 3 * _resolution)
+ _decoded[len++] = c;
+ } else {
+ len = 0;
+ }
+
+ _decoded[len] = 0;
+
+ // Search the dictionary for first and last possible extensions
+ minaddr = lookup_text(0x00, h_dictionary);
+ maxaddr = lookup_text(0x1f, h_dictionary);
+
+ if (minaddr == 0 || maxaddr == 0 || minaddr > maxaddr)
+ return 2;
+
+ // Copy first extension to "result" string
+ decode_text(VOCABULARY, minaddr);
+
+ ptr = result;
+
+ for (i = len; (c = _decoded[i]) != 0; i++)
+ *ptr++ = c;
+ *ptr = 0;
+
+ // Merge second extension with "result" string
+ decode_text(VOCABULARY, maxaddr);
+
+ for (i = len, ptr = result; (c = _decoded[i]) != 0; i++, ptr++) {
+ if (*ptr != c)
+ break;
+ }
+ *ptr = 0;
+
+ // Search was ambiguous or successful
+ return (minaddr == maxaddr) ? 0 : 1;
+}
+
+zchar Processor::unicode_tolower(zchar c) {
+ static const byte tolower_basic_latin[0x100] = {
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+ 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+ 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+ 0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,
+ 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+ 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xD7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xDF,
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+ };
+ static const byte tolower_latin_extended_a[0x80] = {
+ 0x01,0x01,0x03,0x03,0x05,0x05,0x07,0x07,0x09,0x09,0x0B,0x0B,0x0D,0x0D,0x0F,0x0F,
+ 0x11,0x11,0x13,0x13,0x15,0x15,0x17,0x17,0x19,0x19,0x1B,0x1B,0x1D,0x1D,0x1F,0x1F,
+ 0x21,0x21,0x23,0x23,0x25,0x25,0x27,0x27,0x29,0x29,0x2B,0x2B,0x2D,0x2D,0x2F,0x2F,
+ 0x00,0x31,0x33,0x33,0x35,0x35,0x37,0x37,0x38,0x3A,0x3A,0x3C,0x3C,0x3E,0x3E,0x40,
+ 0x40,0x42,0x42,0x44,0x44,0x46,0x46,0x48,0x48,0x49,0x4B,0x4B,0x4D,0x4D,0x4F,0x4F,
+ 0x51,0x51,0x53,0x53,0x55,0x55,0x57,0x57,0x59,0x59,0x5B,0x5B,0x5D,0x5D,0x5F,0x5F,
+ 0x61,0x61,0x63,0x63,0x65,0x65,0x67,0x67,0x69,0x69,0x6B,0x6B,0x6D,0x6D,0x6F,0x6F,
+ 0x71,0x71,0x73,0x73,0x75,0x75,0x77,0x77,0x00,0x7A,0x7A,0x7C,0x7C,0x7E,0x7E,0x7F
+ };
+ static const byte tolower_greek[0x50] = {
+ 0x80,0x81,0x82,0x83,0x84,0x85,0xAC,0x87,0xAD,0xAE,0xAF,0x8B,0xCC,0x8D,0xCD,0xCE,
+ 0x90,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+ 0xC0,0xC1,0xA2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xAC,0xAD,0xAE,0xAF,
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF
+ };
+ static const byte tolower_cyrillic[0x60] = {
+ 0x00,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+ 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F
+ };
+
+ if (c < 0x0100)
+ c = tolower_basic_latin[c];
+ else if (c == 0x0130)
+ c = 0x0069; // Capital I with dot -> lower case i
+ else if (c == 0x0178)
+ c = 0x00FF; // Capital Y diaeresis -> lower case y diaeresis
+ else if (c < 0x0180)
+ c = tolower_latin_extended_a[c - 0x100] + 0x100;
+ else if (c >= 0x380 && c < 0x3D0)
+ c = tolower_greek[c - 0x380] + 0x300;
+ else if (c >= 0x400 && c < 0x460)
+ c = tolower_cyrillic[c - 0x400] + 0x400;
+
+ return c;
+}
+
+
+void Processor::z_check_unicode() {
+ zword c = zargs[0];
+ zword result = 0;
+
+ if (c <= 0x1f) {
+ if ((c == 0x08) || (c == 0x0d) || (c == 0x1b))
+ result = 2;
+ } else if (c <= 0x7e) {
+ result = 3;
+ } else {
+ // we support unicode
+ result = 1;
+ }
+
+ store (result);
+}
+
+void Processor::z_encode_text() {
+ int i;
+
+ load_string((zword) (zargs[0] + zargs[2]), zargs[1]);
+
+ encode_text(0x05);
+
+ for (i = 0; i < _resolution; i++)
+ storew((zword) (zargs[3] + 2 * i), _encoded[i]);
+
+}
+
+void Processor::z_new_line() {
+ new_line ();
+}
+
+void Processor::z_print () {
+ decode_text(EMBEDDED_STRING, 0);
+}
+
+void Processor::z_print_addr() {
+ decode_text(LOW_STRING, zargs[0]);
+}
+
+void Processor::z_print_char() {
+ print_char (translate_from_zscii(zargs[0]));
+}
+
+void Processor::z_print_form() {
+ zword count;
+ zword addr = zargs[0];
+ bool first = true;
+
+ for (;;) {
+ LOW_WORD(addr, count);
+ addr += 2;
+
+ if (count == 0)
+ break;
+
+ if (!first)
+ new_line ();
+
+ while (count--) {
+ zbyte c;
+
+ LOW_BYTE(addr, c);
+ addr++;
+
+ print_char(translate_from_zscii (c));
+ }
+
+ first = false;
+ }
+}
+
+void Processor::z_print_num() {
+ print_num (zargs[0]);
+}
+
+void Processor::z_print_obj() {
+ print_object(zargs[0]);
+}
+
+void Processor::z_print_paddr() {
+ decode_text (HIGH_STRING, zargs[0]);
+}
+
+void Processor::z_print_ret() {
+ decode_text(EMBEDDED_STRING, 0);
+ new_line();
+ ret(1);
+}
+
+void Processor::z_print_unicode() {
+ if (zargs[0] < 0x20)
+ print_char('?');
+ else
+ print_char(zargs[0]);
+}
+
+void Processor::z_tokenise() {
+ // Supply default arguments
+ if (zargc < 3)
+ zargs[2] = 0;
+ if (zargc < 4)
+ zargs[3] = 0;
+
+ // Call tokenise_line to do the real work
+ tokenise_line(zargs[0], zargs[1], zargs[2], zargs[3] != 0);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/processor_variables.cpp b/engines/glk/frotz/processor_variables.cpp
new file mode 100644
index 0000000000..0162f84d17
--- /dev/null
+++ b/engines/glk/frotz/processor_variables.cpp
@@ -0,0 +1,199 @@
+/* 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/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+void Processor::z_dec() {
+ zword value;
+
+ if (zargs[0] == 0)
+ (*_sp)--;
+ else if (zargs[0] < 16)
+ (*(_fp - zargs[0]))--;
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD(addr, value);
+ value--;
+ SET_WORD(addr, value);
+ }
+}
+
+void Processor::z_dec_chk() {
+ zword value;
+
+ if (zargs[0] == 0)
+ value = --(*_sp);
+ else if (zargs[0] < 16)
+ value = --(*(_fp - zargs[0]));
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD(addr, value);
+ value--;
+ SET_WORD(addr, value);
+ }
+
+ branch((short)value < (short)zargs[1]);
+}
+
+void Processor::z_inc() {
+ zword value;
+
+ if (zargs[0] == 0)
+ (*_sp)++;
+ else if (zargs[0] < 16)
+ (*(_fp - zargs[0]))++;
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD(addr, value);
+ value++;
+ SET_WORD(addr, value);
+ }
+}
+
+void Processor::z_inc_chk() {
+ zword value;
+
+ if (zargs[0] == 0)
+ value = ++(*_sp);
+ else if (zargs[0] < 16)
+ value = ++(*(_fp - zargs[0]));
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD(addr, value);
+ value++;
+ SET_WORD(addr, value);
+ }
+
+ branch((short)value > (short)zargs[1]);
+}
+
+void Processor::z_load() {
+ zword value;
+
+ if (zargs[0] == 0)
+ value = *_sp;
+ else if (zargs[0] < 16)
+ value = *(_fp - zargs[0]);
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ LOW_WORD(addr, value);
+ }
+
+ store(value);
+}
+
+void Processor::z_pop() {
+ _sp++;
+}
+
+void Processor::z_pop_stack() {
+ if (zargc == 2) {
+ // it's a user stack
+ zword size;
+ zword addr = zargs[1];
+
+ LOW_WORD(addr, size);
+
+ size += zargs[0];
+ storew(addr, size);
+ } else {
+ // it's the game stack
+ _sp += zargs[0];
+ }
+}
+
+void Processor::z_pull() {
+ zword value;
+
+ if (h_version != V6) {
+ // not a V6 game, pop stack and write
+ value = *_sp++;
+
+ if (zargs[0] == 0)
+ *_sp = value;
+ else if (zargs[0] < 16)
+ *(_fp - zargs[0]) = value;
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ SET_WORD(addr, value);
+ }
+ } else {
+ // it's V6, but is there a user stack?
+ if (zargc == 1) {
+ // it's a user stack
+ zword size;
+ zword addr = zargs[0];
+
+ LOW_WORD(addr, size);
+
+ size++;
+ storew(addr, size);
+
+ addr += 2 * size;
+ LOW_WORD(addr, value);
+ } else {
+ // it's the game stack
+ value = *_sp++;
+ }
+
+ store(value);
+ }
+}
+
+void Processor::z_push() {
+ *--_sp = zargs[0];
+}
+
+void Processor::z_push_stack() {
+ zword size;
+ zword addr = zargs[1];
+
+ LOW_WORD(addr, size);
+
+ if (size != 0) {
+ storew((zword)(addr + 2 * size), zargs[0]);
+
+ size--;
+ storew(addr, size);
+ }
+
+ branch(size);
+}
+
+void Processor::z_store() {
+ zword value = zargs[1];
+
+ if (zargs[0] == 0)
+ *_sp = value;
+ else if (zargs[0] < 16)
+ *(_fp - zargs[0]) = value;
+ else {
+ zword addr = h_globals + 2 * (zargs[0] - 16);
+ SET_WORD(addr, value);
+ }
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/quetzal.cpp b/engines/glk/frotz/quetzal.cpp
new file mode 100644
index 0000000000..a7b48fc699
--- /dev/null
+++ b/engines/glk/frotz/quetzal.cpp
@@ -0,0 +1,485 @@
+/* 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/frotz/quetzal.h"
+#include "glk/frotz/processor.h"
+#include "common/memstream.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+/**
+ * Various parsing states within restoration.
+ */
+enum ParseState {
+ GOT_HEADER = 0x01,
+ GOT_STACK = 0x02,
+ GOT_MEMORY = 0x04,
+ GOT_NONE = 0x00,
+ GOT_ALL = 0x07,
+ GOT_ERROR = 0x80
+};
+
+
+bool Quetzal::read_word(Common::ReadStream *f, zword *result) {
+ *result = f->readUint16BE();
+ return true;
+}
+
+bool Quetzal::read_long(Common::ReadStream *f, uint *result) {
+ *result = f->readUint32BE();
+ return true;
+}
+
+bool Quetzal::save(Common::WriteStream *svf, Processor *proc) {
+ Processor &p = *proc;
+ uint ifzslen = 0, cmemlen = 0, stkslen = 0;
+ uint pc;
+ zword i, j, n;
+ zword nvars, nargs, nstk;
+ zbyte var;
+ long cmempos, stkspos;
+ int c;
+
+ // Set a temporary memory stream for writing out the data. This is needed, since we need to
+ // do some seeking within it at the end to fill out totals before properly writing it all out
+ Common::MemoryWriteStreamDynamic saveData(DisposeAfterUse::YES);
+ _out = &saveData;
+
+ // Write `IFZS' header.
+ write_chnk(ID_FORM, 0);
+ write_long(ID_IFZS);
+
+ // Write `IFhd' chunk
+ pc = p.getPC();
+ write_chnk(ID_IFhd, 13);
+ write_word(p.h_release);
+ for (i = H_SERIAL; i<H_SERIAL + 6; ++i)
+ write_byte(p[i]);
+
+ write_word(p.h_checksum);
+ write_long(pc << 8); // Includes pad
+
+ // Write `CMem' chunk.
+ cmempos = svf->pos();
+ write_chnk(ID_CMem, 0);
+ _storyFile->seek(_blorbOffset);
+
+ // j holds current run length.
+ for (i = 0, j = 0, cmemlen = 0; i < p.h_dynamic_size; ++i) {
+ c = _storyFile->readByte();
+ c ^= p[i];
+
+ if (c == 0) {
+ // It's a run of equal bytes
+ ++j;
+ } else {
+ // Write out any run there may be.
+ if (j > 0) {
+ for (; j > 0x100; j -= 0x100) {
+ write_run(0xFF);
+ cmemlen += 2;
+ }
+ write_run(j - 1);
+ cmemlen += 2;
+ j = 0;
+ }
+
+ // Any runs are now written. Write this (nonzero) byte
+ write_byte((zbyte)c);
+ ++cmemlen;
+ }
+ }
+
+ // Reached end of dynamic memory. We ignore any unwritten run there may be at this point.
+ if (cmemlen & 1)
+ // Chunk length must be even.
+ write_byte(0);
+
+ // Write `Stks' chunk. You are not expected to understand this. ;)
+ stkspos = _storyFile->pos();
+ write_chnk(ID_Stks, 0);
+
+ // We construct a list of frame indices, most recent first, in `frames'.
+ // These indices are the offsets into the `stack' array of the word before
+ // the first word pushed in each frame.
+ frames[0] = p._sp - p._stack; // The frame we'd get by doing a call now.
+ for (i = p._fp - p._stack + 4, n = 0; i < STACK_SIZE + 4; i = p._stack[i - 3] + 5)
+ frames[++n] = i;
+
+ // All versions other than V6 can use evaluation stack outside a function
+ // context. We write a faked stack frame (most fields zero) to cater for this.
+ if (p.h_version != V6) {
+ for (i = 0; i < 6; ++i)
+ write_byte(0);
+ nstk = STACK_SIZE - frames[n];
+ write_word(nstk);
+ for (j = STACK_SIZE - 1; j >= frames[n]; --j)
+ write_word(p._stack[j]);
+ stkslen = 8 + 2 * nstk;
+ }
+
+ // Write out the rest of the stack frames.
+ for (i = n; i > 0; --i) {
+ zword *pf = p._stack + frames[i] - 4; // Points to call frame
+ nvars = (pf[0] & 0x0F00) >> 8;
+ nargs = pf[0] & 0x00FF;
+ nstk = frames[i] - frames[i - 1] - nvars - 4;
+ pc = ((uint)pf[3] << 9) | pf[2];
+
+ // Check type of call
+ switch (pf[0] & 0xF000) {
+ case 0x0000:
+ // Function
+ var = p[pc];
+ pc = ((pc + 1) << 8) | nvars;
+ break;
+
+ case 0x1000:
+ // Procedure
+ var = 0;
+ pc = (pc << 8) | 0x10 | nvars; // Set procedure flag
+ break;
+
+ default:
+ p.runtimeError(ERR_SAVE_IN_INTER);
+ return 0;
+ }
+ if (nargs != 0)
+ nargs = (1 << nargs) - 1; // Make args into bitmap
+
+ // Write the main part of the frame...
+ write_long(pc);
+ write_byte(var);
+ write_byte(nargs);
+ write_word(nstk);
+
+ // Write the variables and eval stack
+ for (j = 0, --pf; j<nvars + nstk; ++j, --pf)
+ write_word(*pf);
+
+ // Calculate length written thus far
+ stkslen += 8 + 2 * (nvars + nstk);
+ }
+
+ // Fill in variable chunk lengths
+ ifzslen = 3 * 8 + 4 + 14 + cmemlen + stkslen;
+ if (cmemlen & 1)
+ ++ifzslen;
+
+ saveData.seek(4);
+ saveData.writeUint32BE(ifzslen);
+ saveData.seek(cmempos + 4);
+ saveData.writeUint32BE(cmemlen);
+ saveData.seek(stkspos + 4);
+ saveData.writeUint32BE(stkslen);
+
+ // Write the save data out
+ svf->write(saveData.getData(), saveData.size());
+
+ // After all that, still nothing went wrong!
+ return true;
+}
+
+
+int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
+ Processor &p = *proc;
+ uint ifzslen, currlen, tmpl;
+ uint pc;
+ zword i, tmpw;
+ int fatal = 0; // Set to -1 when errors must be fatal.
+ zbyte skip, progress = GOT_NONE;
+ int x, y;
+
+ // Check it's really an `IFZS' file.
+ tmpl = svf->readUint32BE();
+ ifzslen = svf->readUint32BE();
+ currlen = svf->readUint32BE();
+ if (tmpl != ID_FORM || currlen != ID_IFZS) {
+ p.print_string("This is not a saved game file!\n");
+ return 0;
+ }
+ if ((ifzslen & 1) || ifzslen<4)
+ // Sanity checks
+ return 0;
+ ifzslen -= 4;
+
+ // Read each chunk and process it
+ while (ifzslen > 0) {
+ // Read chunk header
+ if (ifzslen < 8)
+ // Couldn't contain a chunk
+ return 0;
+
+ tmpl = svf->readUint32BE();
+ currlen = svf->readUint32BE();
+ ifzslen -= 8; // Reduce remaining by size of header
+
+ // Handle chunk body
+ if (ifzslen < currlen)
+ // Chunk goes past EOF?!
+ return 0;
+ skip = currlen & 1;
+ ifzslen -= currlen + (uint)skip;
+
+ switch (tmpl) {
+ // `IFhd' header chunk; must be first in file
+ case ID_IFhd:
+ if (progress & GOT_HEADER) {
+ p.print_string("Save file has two IFZS chunks!\n");
+ return fatal;
+ }
+ progress |= GOT_HEADER;
+ if (currlen < 13)
+ return fatal;
+
+ tmpw = svf->readUint16BE();
+ if (tmpw != p.h_release)
+ progress = GOT_ERROR;
+
+ for (i = H_SERIAL; i < H_SERIAL + 6; ++i) {
+ x = svf->readByte();
+ if (x != p[i])
+ progress = GOT_ERROR;
+ }
+
+ tmpw = svf->readUint16BE();
+ if (tmpw != p.h_checksum)
+ progress = GOT_ERROR;
+
+ if (progress & GOT_ERROR) {
+ p.print_string("File was not saved from this story!\n");
+ return fatal;
+ }
+
+ x = svf->readByte();
+ pc = (uint)x << 16;
+ x = svf->readByte();
+ pc |= (uint)x << 8;
+ x = svf->readByte();
+ pc |= (uint)x;
+
+ fatal = -1; // Setting PC means errors must be fatal
+ p.setPC(pc);
+
+ svf->skip(13); // Skip rest of chunk
+ break;
+
+ // `Stks' stacks chunk; restoring this is quite complex. ;)
+ case ID_Stks:
+ if (progress & GOT_STACK) {
+ p.print_string("File contains two stack chunks!\n");
+ break;
+ }
+ progress |= GOT_STACK;
+
+ fatal = -1; // Setting SP means errors must be fatal
+ p._sp = p._stack + STACK_SIZE;
+
+ // All versions other than V6 may use evaluation stack outside any function context.
+ // As a result a faked function context will be present in the file here. We skip
+ // this context, but load the associated stack onto the stack proper...
+ if (p.h_version != V6) {
+ if (currlen < 8)
+ return fatal;
+
+ svf->skip(6);
+ tmpw = svf->readUint16BE();
+
+ if (tmpw > STACK_SIZE) {
+ p.print_string("Save-file has too much stack (and I can't cope).\n");
+ return fatal;
+ }
+
+ currlen -= 8;
+ if (currlen < (uint)tmpw * 2)
+ return fatal;
+ for (i = 0; i < tmpw; ++i)
+ *--p._sp = svf->readUint16BE();
+ currlen -= tmpw * 2;
+ }
+
+ // We now proceed to load the main block of stack frames
+ for (p._fp = p._stack + STACK_SIZE, p._frameCount = 0;
+ currlen > 0; currlen -= 8, ++p._frameCount) {
+ if (currlen < 8) return fatal;
+ if (p._sp - p._stack < 4) {
+ // No space for frame
+ p.print_string("Save-file has too much stack (and I can't cope).\n");
+ return fatal;
+ }
+
+ // Read PC, procedure flag and formal param count
+ tmpl = svf->readUint32BE();
+ y = (int)(tmpl & 0x0F); // Number of formals
+ tmpw = y << 8;
+
+ // Read result variable
+ x = svf->readByte();
+
+ // Check the procedure flag...
+ if (tmpl & 0x10) {
+ tmpw |= 0x1000; // It's a procedure
+ tmpl >>= 8; // Shift to get PC value
+ } else {
+ // Functions have type 0, so no need to or anything
+ tmpl >>= 8; // Shift to get PC value
+ --tmpl; // Point at result byte. */
+
+ // Sanity check on result variable...
+ if (p[tmpl] != (zbyte)x) {
+ p.print_string("Save-file has wrong variable number on stack (possibly wrong game version?)\n");
+ return fatal;
+ }
+ }
+
+ *--p._sp = (zword)(tmpl >> 9); // High part of PC
+ *--p._sp = (zword)(tmpl & 0x1FF); // Low part of PC
+ *--p._sp = (zword)(p._fp - p._stack - 1); // FP
+
+ // Read and process argument mask
+ x = svf->readByte();
+ ++x; // Should now be a power of 2
+ for (i = 0; i<8; ++i)
+ if (x & (1 << i))
+ break;
+ if (x ^ (1 << i)) {
+ // Not a power of 2
+ p.print_string("Save-file uses incomplete argument lists (which I can't handle)\n");
+ return fatal;
+ }
+
+ *--p._sp = tmpw | i;
+ p._fp = p._sp; // FP for next frame
+
+ // Read amount of eval stack used
+ tmpw = svf->readUint16BE();
+
+ tmpw += y; // Amount of stack + number of locals
+ if (p._sp - p._stack <= tmpw) {
+ p.print_string("Save-file has too much stack (and I can't cope).\n");
+ return fatal;
+ }
+ if (currlen < (uint)tmpw * 2)
+ return fatal;
+
+ for (i = 0; i < tmpw; ++i)
+ --*p._sp = svf->readUint16BE();
+ currlen -= tmpw * 2;
+ }
+
+ // End of `Stks' processing...
+ break;
+
+ // Any more special chunk types must go in HERE or ABOVE
+ // `CMem' compressed memory chunk; uncompress it
+ case ID_CMem:
+ if (!(progress & GOT_MEMORY)) {
+ // Don't complain if two
+ _storyFile->seek(_blorbOffset);
+
+ i = 0; // Bytes written to data area
+ for (; currlen > 0; --currlen) {
+ x = svf->readByte();
+ if (x == 0) {
+ // Start of run
+ // Check for bogus run
+ if (currlen < 2) {
+ p.print_string("File contains bogus `CMem' chunk.\n");
+ svf->skip(currlen);
+
+ currlen = 1;
+ i = 0xFFFF;
+ break; // Keep going; may be a `UMem' too
+ }
+
+ // Copy story file to memory during the run
+ --currlen;
+ x = svf->readByte();
+ for (; x >= 0 && i < p.h_dynamic_size; --x, ++i)
+ p[i] = svf->readByte();
+ } else {
+ // Not a run
+ y = svf->readByte();
+ p[i] = (zbyte)(x ^ y);
+ ++i;
+ }
+
+ // Make sure we don't load too much
+ if (i > p.h_dynamic_size) {
+ p.print_string("warning: `CMem' chunk too long!\n");
+ svf->skip(currlen);
+ break; // Keep going; there may be a `UMem' too
+ }
+ }
+
+ // If chunk is short, assume a run
+ for (; i < p.h_dynamic_size; ++i)
+ p[i] = svf->readByte();
+
+ if (currlen == 0)
+ progress |= GOT_MEMORY; // Only if succeeded
+ break;
+ }
+
+ // Intentional fall-through
+
+ case ID_UMem:
+ if (!(progress & GOT_MEMORY)) {
+ // Must be exactly the right size
+ if (currlen == p.h_dynamic_size) {
+ if (svf->read(p.zmp, currlen) == currlen) {
+ progress |= GOT_MEMORY; // Only on success
+ break;
+ }
+ } else {
+ p.print_string("`UMem' chunk wrong size!\n");
+ }
+
+ // Fall into default action (skip chunk) on errors
+ }
+
+ // Intentional fall-through
+
+ default:
+ svf->seek(currlen, SEEK_CUR); // Skip chunk
+ break;
+ }
+
+ if (skip)
+ svf->skip(1); // Skip pad byte
+ }
+
+ // We've reached the end of the file. For the restoration to have been a
+ // success, we must have had one of each of the required chunks.
+ if (!(progress & GOT_HEADER))
+ p.print_string("error: no valid header (`IFhd') chunk in file.\n");
+ if (!(progress & GOT_STACK))
+ p.print_string("error: no valid stack (`Stks') chunk in file.\n");
+ if (!(progress & GOT_MEMORY))
+ p.print_string("error: no valid memory (`CMem' or `UMem') chunk in file.\n");
+
+ return (progress == GOT_ALL ? 2 : fatal);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/frotz/quetzal.h b/engines/glk/frotz/quetzal.h
new file mode 100644
index 0000000000..bbd4496a45
--- /dev/null
+++ b/engines/glk/frotz/quetzal.h
@@ -0,0 +1,97 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_FROTZ_QUETZAL
+#define GLK_FROTZ_QUETZAL
+
+#include "glk/glk_types.h"
+#include "glk/frotz/frotz_types.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+enum QueztalTag {
+ ID_FORM = MKTAG('F', 'O', 'R', 'M'),
+ ID_IFZS = MKTAG('I', 'F', 'Z', 'S'),
+ ID_IFhd = MKTAG('I', 'F', 'h', 'd'),
+ ID_UMem = MKTAG('U', 'M', 'e', 'm'),
+ ID_CMem = MKTAG('C', 'M', 'e', 'm'),
+ ID_Stks = MKTAG('S', 't', 'k', 's'),
+ ID_ANNO = MKTAG('A', 'N', 'N', 'O')
+};
+
+class Processor;
+
+class Quetzal {
+private:
+ Common::SeekableReadStream *_storyFile;
+ Common::WriteStream *_out;
+ size_t _blorbOffset;
+ int _slot;
+ zword frames[STACK_SIZE / 4 + 1];
+private:
+ /**
+ * Read a 16-bit value from the file
+ */
+ bool read_word(Common::ReadStream *f, zword *result);
+
+ /**
+ * Read 32-bit value from the file
+ */
+ bool read_long(Common::ReadStream *f, uint *result);
+
+ void write_byte(zbyte b) { _out->writeByte(b); }
+ void write_bytx(zword b) { _out->writeByte(b & 0xFF); }
+ void write_word(zword w) { _out->writeUint16BE(w); }
+ void write_long(uint l) { _out->writeUint32BE(l); }
+ void write_run(zword run) { _out->writeUint16LE(run); }
+ void write_chnk(QueztalTag id, zword len) {
+ _out->writeUint32BE(id);
+ _out->writeUint32BE(len);
+ }
+public:
+ /**
+ * Constructor
+ */
+ Quetzal(Common::SeekableReadStream *storyFile, size_t blorbOffset, int slot) :
+ _storyFile(storyFile), _blorbOffset(blorbOffset), _slot(slot) {}
+
+ /*
+ * Save a game using Quetzal format.
+ * @param svf Savegame file
+ * @returns Returns true if OK, false if failed
+ */
+ bool save(Common::WriteStream *svf, Processor *proc);
+
+ /**
+ * Restore a saved game using Quetzal format
+ * @param svf Savegame file
+ * @returns Return 2 if OK, 0 if an error occurred before any damage was done,
+ * -1 on a fatal error
+ */
+ int restore(Common::SeekableReadStream *svf, Processor *proc);
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/gargoyle.cpp b/engines/glk/gargoyle.cpp
new file mode 100644
index 0000000000..702efe7443
--- /dev/null
+++ b/engines/glk/gargoyle.cpp
@@ -0,0 +1,120 @@
+/* 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 "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "engines/util.h"
+#include "graphics/scaler.h"
+#include "graphics/thumbnail.h"
+#include "glk/gargoyle.h"
+#include "glk/conf.h"
+#include "glk/events.h"
+#include "glk/picture.h"
+#include "glk/screen.h"
+#include "glk/selection.h"
+#include "glk/streams.h"
+#include "glk/windows.h"
+
+namespace Gargoyle {
+
+GargoyleEngine *g_vm;
+
+GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+ _gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _clipboard(nullptr),
+ _conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr),
+ _selection(nullptr), _windows(nullptr), _copySelect(false), _terminated(false),
+ gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
+ g_vm = this;
+}
+
+GargoyleEngine::~GargoyleEngine() {
+ delete _clipboard;
+ delete _conf;
+ delete _events;
+ delete _picList;
+ delete _screen;
+ delete _selection;
+ delete _streams;
+ delete _windows;
+}
+
+void GargoyleEngine::initialize() {
+ // Set up debug channels
+ DebugMan.addDebugChannel(kDebugCore, "core", "Core engine debug level");
+ DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
+ DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling");
+ DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
+
+ initGraphicsMode();
+ _conf = new Conf();
+ _screen = new Screen();
+
+ _clipboard = new Clipboard();
+ _events = new Events();
+ _picList = new PicList();
+ _selection = new Selection();
+ _streams = new Streams();
+ _windows = new Windows(_screen);
+}
+
+void GargoyleEngine::initGraphicsMode() {
+ uint width = ConfMan.hasKey("width") ? ConfMan.getInt("width") : 640;
+ uint height = ConfMan.hasKey("height") ? ConfMan.getInt("height") : 480;
+ Common::List<Graphics::PixelFormat> formats = g_system->getSupportedFormats();
+ Graphics::PixelFormat format = formats.front();
+
+ for (Common::List<Graphics::PixelFormat>::iterator i = formats.begin(); i != formats.end(); ++i) {
+ if ((*i).bytesPerPixel > 1) {
+ format = *i;
+ break;
+ }
+ }
+
+ initGraphics(width, height, &format);
+}
+
+Common::Error GargoyleEngine::run() {
+ initialize();
+
+ Common::File f;
+ if (f.open(getFilename()))
+ runGame(&f);
+
+ return Common::kNoError;
+}
+
+void GargoyleEngine::GUIError(const char *msg, ...) {
+ char buffer[STRINGBUFLEN];
+ va_list va;
+
+ // Generate the full error message
+ va_start(va, msg);
+ vsnprintf(buffer, STRINGBUFLEN, msg, va);
+ va_end(va);
+
+ GUIErrorMessage(buffer);
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/gargoyle.h b/engines/glk/gargoyle.h
new file mode 100644
index 0000000000..d3019ca039
--- /dev/null
+++ b/engines/glk/gargoyle.h
@@ -0,0 +1,193 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_GARGOLE_H
+#define GLK_GARGOLE_H
+
+#include "common/scummsys.h"
+#include "common/random.h"
+#include "common/system.h"
+#include "common/serializer.h"
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
+#include "glk/glk_types.h"
+
+namespace Gargoyle {
+
+class Clipboard;
+class Conf;
+class Events;
+class PicList;
+class Screen;
+class Selection;
+class Streams;
+class Windows;
+
+enum InterpreterType {
+ INTERPRETER_ADVSYS = 0,
+ INTERPRETER_AGILITY = 1,
+ INTERPRETER_ALAN2 = 2,
+ INTERPRETER_ALAN3 = 3,
+ INTERPRETER_BOCFEL = 4,
+ INTERPRETER_FROTZ = 5,
+ INTERPRETER_GEAS = 6,
+ INTERPRETER_HUGO = 7,
+ INTERPRETER_JACL = 8,
+ INTERPRETER_LEVEL9 = 9,
+ INTERPRETER_MAGNETIC = 10,
+ INTERPRETER_NITFOL = 11,
+ INTERPRETER_SCARE = 12,
+ INTERPRETER_SCOTT = 13,
+ INTERPRETER_TADS = 14
+};
+
+enum GargoyleDebugChannels {
+ kDebugCore = 1 << 0,
+ kDebugScripts = 1 << 1,
+ kDebugGraphics = 1 << 2,
+ kDebugSound = 1 << 3
+};
+
+
+#define GLK_SAVEGAME_VERSION 1
+
+struct GargoyleGameDescription;
+
+/**
+ * Base class for the different interpreters
+ */
+class GargoyleEngine : public Engine {
+private:
+ /**
+ * Handles basic initialization
+ */
+ void initialize();
+
+ /**
+ * Setup the video mode
+ */
+ void initGraphicsMode();
+protected:
+ const GargoyleGameDescription *_gameDescription;
+ Common::RandomSource _random;
+ int _loadSaveSlot;
+
+ // Engine APIs
+ virtual Common::Error run();
+
+ /**
+ * Returns true whether a given feature is supported by the engine
+ */
+ virtual bool hasFeature(EngineFeature f) const;
+
+ /**
+ * Main game loop for the individual interpreters
+ */
+ virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
+public:
+ Clipboard *_clipboard;
+ Conf *_conf;
+ Events *_events;
+ PicList *_picList;
+ Screen *_screen;
+ Selection *_selection;
+ Streams *_streams;
+ Windows *_windows;
+ bool _copySelect;
+ bool _terminated;
+ void (*gli_unregister_obj)(void *obj, glui32 objclass, gidispatch_rock_t objrock);
+ gidispatch_rock_t (*gli_register_arr)(void *array, glui32 len, const char *typecode);
+ void (*gli_unregister_arr)(void *array, glui32 len, const char *typecode, gidispatch_rock_t objrock);
+
+public:
+ GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc);
+ virtual ~GargoyleEngine();
+
+ /**
+ * Returns true if a savegame can be loaded
+ */
+ virtual bool canLoadGameStateCurrently() override {
+ return true;
+ }
+
+ /**
+ * Returns true if the game can be saved
+ */
+ virtual bool canSaveGameStateCurrently() override {
+ return true;
+ }
+
+ /**
+ * Returns the bitset of game features
+ */
+ uint32 getFeatures() const;
+
+ /**
+ * Returns whether the game is a demo
+ */
+ bool isDemo() const;
+
+ /**
+ * Returns the language
+ */
+ Common::Language getLanguage() const;
+
+ /**
+ * Returns the running interpreter type
+ */
+ InterpreterType getInterpreterType() const;
+
+ /**
+ * Returns the game's md5
+ */
+ const Common::String &getGameMD5() const;
+
+ /**
+ * Returns the primary filename for the game
+ */
+ const Common::String &getFilename() const;
+
+ /**
+ * Return the game engine's target name
+ */
+ const Common::String &getTargetName() const {
+ return _targetName;
+ }
+
+ /**
+ * Return the filename for a given save slot
+ */
+ Common::String getSaveName(uint slot) const {
+ return Common::String::format("%s.%.3u", getTargetName().c_str(), slot);
+ }
+
+ /**
+ * Display a message in a GUI dialog
+ */
+ void GUIError(const char *msg, ...);
+};
+
+extern GargoyleEngine *g_vm;
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
new file mode 100644
index 0000000000..77fb242b8b
--- /dev/null
+++ b/engines/glk/glk.cpp
@@ -0,0 +1,1174 @@
+/* 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/glk.h"
+#include "glk/conf.h"
+#include "glk/events.h"
+#include "glk/picture.h"
+#include "glk/streams.h"
+#include "glk/unicode.h"
+#include "glk/windows.h"
+#include "glk/window_graphics.h"
+#include "glk/window_text_buffer.h"
+#include "glk/window_pair.h"
+
+
+namespace Gargoyle {
+
+Glk::Glk(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+ GargoyleEngine(syst, gameDesc), _gliFirstEvent(false) {
+ // Set uppercase/lowercase tables
+ int ix, res;
+ for (ix = 0; ix < 256; ix++) {
+ _charToupperTable[ix] = ix;
+ _charTolowerTable[ix] = ix;
+ }
+
+ for (ix = 0; ix < 256; ix++) {
+ if (ix >= 'A' && ix <= 'Z')
+ res = ix + ('a' - 'A');
+ else if (ix >= 0xC0 && ix <= 0xDE && ix != 0xD7)
+ res = ix + 0x20;
+ else
+ res = 0;
+
+ if (res) {
+ _charTolowerTable[ix] = res;
+ _charToupperTable[res] = ix;
+ }
+ }
+}
+
+void Glk::glk_exit(void) {
+ glk_put_string("[ press any key to exit ]");
+ _events->waitForPress();
+
+ quitGame();
+}
+
+void Glk::glk_set_interrupt_handler(void(*func)(void)) {
+ // This library doesn't handle interrupts.
+}
+
+void Glk::glk_tick(void) {
+ // Nothing needed
+}
+
+glui32 Glk::glk_gestalt(glui32 id, glui32 val) {
+ return glk_gestalt_ext(id, val, nullptr, 0);
+}
+
+glui32 Glk::glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen) {
+ switch (id) {
+ case gestalt_Version:
+ return 0x00000703;
+
+ case gestalt_LineInput:
+ if (val >= 32 && val < 0x10ffff)
+ return true;
+ else
+ return false;
+
+ case gestalt_CharInput:
+ if (val >= 32 && val < 0x10ffff)
+ return true;
+ else if (val == keycode_Return)
+ return true;
+ else
+ return false;
+
+ case gestalt_CharOutput:
+ if (val >= 32 && val < 0x10ffff) {
+ if (arr && arrlen >= 1)
+ arr[0] = 1;
+ return gestalt_CharOutput_ExactPrint;
+ } else {
+ // cheaply, we don't do any translation of printed characters,
+ // so the output is always one character even if it's wrong.
+ if (arr && arrlen >= 1)
+ arr[0] = 1;
+ return gestalt_CharOutput_CannotPrint;
+ }
+
+ case gestalt_MouseInput:
+ if (val == wintype_TextGrid)
+ return true;
+ if (val == wintype_Graphics)
+ return true;
+ return false;
+
+ case gestalt_Graphics:
+ case gestalt_GraphicsTransparency:
+ return g_conf->_graphics;
+
+ case gestalt_DrawImage:
+ if (val == wintype_TextBuffer)
+ return g_conf->_graphics;
+ if (val == wintype_Graphics)
+ return g_conf->_graphics;
+ return false;
+
+ case gestalt_Sound:
+ case gestalt_SoundVolume:
+ case gestalt_SoundMusic:
+ case gestalt_SoundNotify:
+ return g_conf->_sound;
+
+ case gestalt_LineTerminatorKey:
+ return Window::checkTerminator(val);
+
+ case gestalt_Timer:
+ case gestalt_Unicode:
+ case gestalt_UnicodeNorm:
+ case gestalt_Hyperlinks:
+ case gestalt_HyperlinkInput:
+ case gestalt_LineInputEcho:
+ case gestalt_LineTerminators:
+ case gestalt_DateTime:
+ case gestalt_GarglkText:
+ return true;
+
+ case gestalt_Sound2:
+ default:
+ return false;
+ }
+}
+
+unsigned char Glk::glk_char_to_lower(unsigned char ch) {
+ return _charTolowerTable[ch];
+}
+
+unsigned char Glk::glk_char_to_upper(unsigned char ch) {
+ return _charToupperTable[ch];
+}
+
+winid_t Glk::glk_window_get_root(void) const {
+ return _windows->getRoot();
+}
+
+winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, glui32 rock) const {
+ return _windows->windowOpen(split, method, size, wintype, rock);
+}
+
+void Glk::glk_window_close(winid_t win, stream_result_t *result) {
+ if (win) {
+ _windows->windowClose(win, result);
+ } else {
+ warning("glk_window_close: invalid ref");
+ }
+}
+
+void Glk::glk_window_get_size(winid_t win, glui32 *width, glui32 *height) {
+ if (win) {
+ win->getSize(width, height);
+ } else {
+ warning("window_get_size: invalid ref");
+ }
+}
+
+void Glk::glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin) {
+ if (win) {
+ win->setArrangement(method, size, keywin);
+ } else {
+ warning("window_set_arrangement: invalid ref");
+ }
+}
+
+void Glk::glk_window_get_arrangement(winid_t win, glui32 *method,
+ glui32 *size, winid_t *keyWin) {
+ if (win) {
+ win->getArrangement(method, size, keyWin);
+ } else {
+ warning("window_get_arrangement: invalid ref");
+ }
+}
+
+winid_t Glk::glk_window_iterate(winid_t win, glui32 *rock) {
+ win = win ? win->_next : _windows->getRoot();
+
+ if (win) {
+ if (rock)
+ *rock = win->_rock;
+ return win;
+ }
+
+ if (rock)
+ *rock = 0;
+
+ return nullptr;
+}
+
+glui32 Glk::glk_window_get_rock(winid_t win) {
+ if (win) {
+ return win->_rock;
+ } else {
+ warning("window_get_rock: invalid ref.");
+ return 0;
+ }
+}
+
+glui32 Glk::glk_window_get_type(winid_t win) {
+ if (win) {
+ return win->_type;
+ } else {
+ warning("window_get_parent: invalid ref");
+ return 0;
+ }
+}
+
+winid_t Glk::glk_window_get_parent(winid_t win) {
+ if (!win) {
+ warning("window_get_parent: invalid ref");
+ return 0;
+ }
+
+ return win->_parent;
+}
+
+winid_t Glk::glk_window_get_sibling(winid_t win) {
+ if (!win) {
+ warning("window_get_sibling: invalid ref");
+ return nullptr;
+ }
+
+ PairWindow *parentWin = dynamic_cast<PairWindow *>(win->_parent);
+ if (!parentWin)
+ return nullptr;
+
+ if (parentWin->_child1 == win)
+ return parentWin->_child2;
+ else if (parentWin->_child2 == win)
+ return parentWin->_child1;
+
+ return nullptr;
+}
+
+void Glk::glk_window_clear(winid_t win) {
+ if (!win) {
+ warning("window_clear: invalid ref");
+ } else {
+ if (win->_lineRequest || win->_lineRequestUni) {
+ if (g_conf->_safeClicks && _events->_forceClick) {
+ glk_cancel_line_event(win, nullptr);
+ _events->_forceClick = false;
+
+ win->clear();
+ } else {
+ warning("window_clear: window has pending line request");
+ return;
+ }
+ }
+
+ // Clear the window
+ win->clear();
+ }
+}
+
+void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
+ if (win) {
+ win->moveCursor(Point(xpos, ypos));
+ } else {
+ warning("window_move_cursor: invalid ref");
+ }
+}
+
+strid_t Glk::glk_window_get_stream(winid_t win) {
+ if (win) {
+ return win->_stream;
+ } else {
+ warning("window_get_stream: invalid ref");
+ return nullptr;
+ }
+}
+
+void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
+ if (win) {
+ win->_echoStream = str;
+ } else {
+ warning("window_set_echo_stream: invalid window id");
+ }
+}
+
+strid_t Glk::glk_window_get_echo_stream(winid_t win) {
+ if (!win) {
+ warning("window_get_echo_stream: invalid ref");
+ return nullptr;
+ }
+
+ return win->_echoStream;
+}
+
+void Glk::glk_set_window(winid_t win) {
+ _streams->setCurrent(win ? win->_stream : nullptr);
+}
+
+strid_t Glk::glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock) {
+ return _streams->openFileStream(fileref, fmode, rock, false);
+}
+
+strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock) {
+ return _streams->openMemoryStream(buf, buflen, fmode, rock, false);
+}
+
+void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
+ str->close(result);
+}
+
+strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) const {
+ return str ? str->getNext(rockptr) : _streams->getFirst(rockptr);
+}
+
+glui32 Glk::glk_stream_get_rock(strid_t str) const {
+ if (!str) {
+ warning("stream_get_rock: invalid ref");
+ return 0;
+ }
+
+ return str->getRock();
+}
+
+void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode) {
+ if (str) {
+ str->setPosition(pos, seekMode);
+ } else {
+ warning("stream_set_position: invalid ref");
+ }
+}
+
+glui32 Glk::glk_stream_get_position(strid_t str) const {
+ if (str) {
+ return str->getPosition();
+ } else {
+ warning("stream_get_position: invalid ref");
+ return 0;
+ }
+}
+
+void Glk::glk_stream_set_current(strid_t str) {
+ _streams->setCurrent(str);
+}
+
+strid_t Glk::glk_stream_get_current(void) {
+ return _streams->getCurrent();
+}
+
+void Glk::glk_put_char(unsigned char ch) {
+ _streams->getCurrent()->putChar(ch);
+}
+
+void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
+ if (str) {
+ str->putChar(ch);
+ } else {
+ warning("put_char_stream: invalid ref");
+ }
+}
+
+void Glk::glk_put_string(const char *s) {
+ _streams->getCurrent()->putBuffer(s, strlen(s));
+}
+
+void Glk::glk_put_string_stream(strid_t str, const char *s) {
+ str->putBuffer(s, strlen(s));
+}
+
+void Glk::glk_put_buffer(char *buf, glui32 len) {
+ _streams->getCurrent()->putBuffer(buf, len);
+}
+
+void Glk::glk_put_buffer_stream(strid_t str, const char *buf, glui32 len) {
+ str->putBuffer(buf, len);
+}
+
+void Glk::glk_set_style(glui32 styl) {
+ _streams->getCurrent()->setStyle(styl);
+}
+
+void Glk::glk_set_style_stream(strid_t str, glui32 styl) {
+ if (str) {
+ str->setStyle(styl);
+ } else {
+ warning("set_style_stream: invalid ref");
+ }
+}
+
+glsi32 Glk::glk_get_char_stream(strid_t str) {
+ if (str) {
+ return str->getChar();
+ } else {
+ warning("get_char_stream: invalid ref");
+ return -1;
+ }
+}
+
+glui32 Glk::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
+ if (str) {
+ return str->getLine(buf, len);
+ } else {
+ warning("get_line_stream: invalid ref");
+ return 0;
+ }
+}
+
+glui32 Glk::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
+ if (str) {
+ return str->getBuffer(buf, len);
+ } else {
+ warning("get_line_stream: invalid ref");
+ return 0;
+ }
+}
+
+void Glk::glk_stylehint_set(glui32 wintype, glui32 style, glui32 hint, glsi32 val) {
+ WindowStyle *styles;
+ bool p, b, i;
+
+ if (wintype == wintype_AllTypes) {
+ glk_stylehint_set(wintype_TextGrid, style, hint, val);
+ glk_stylehint_set(wintype_TextBuffer, style, hint, val);
+ return;
+ }
+
+ if (wintype == wintype_TextGrid)
+ styles = g_conf->_gStyles;
+ else if (wintype == wintype_TextBuffer)
+ styles = g_conf->_tStyles;
+ else
+ return;
+
+ if (!g_conf->_styleHint)
+ return;
+
+ switch (hint) {
+ case stylehint_TextColor:
+ styles[style].fg[0] = (val >> 16) & 0xff;
+ styles[style].fg[1] = (val >> 8) & 0xff;
+ styles[style].fg[2] = (val) & 0xff;
+ break;
+
+ case stylehint_BackColor:
+ styles[style].bg[0] = (val >> 16) & 0xff;
+ styles[style].bg[1] = (val >> 8) & 0xff;
+ styles[style].bg[2] = (val) & 0xff;
+ break;
+
+ case stylehint_ReverseColor:
+ styles[style].reverse = (val != 0);
+ break;
+
+ case stylehint_Proportional:
+ if (wintype == wintype_TextBuffer) {
+ p = val > 0;
+ b = styles[style].isBold();
+ i = styles[style].isItalic();
+ styles[style].font = WindowStyle::makeFont(p, b, i);
+ }
+ break;
+
+ case stylehint_Weight:
+ p = styles[style].isProp();
+ b = val > 0;
+ i = styles[style].isItalic();
+ styles[style].font = WindowStyle::makeFont(p, b, i);
+ break;
+
+ case stylehint_Oblique:
+ p = styles[style].isProp();
+ b = styles[style].isBold();
+ i = val > 0;
+ styles[style].font = WindowStyle::makeFont(p, b, i);
+ break;
+ }
+
+ if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_BackColor) {
+ memcpy(g_conf->_windowColor, styles[style].bg, 3);
+ }
+
+ if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_TextColor) {
+ memcpy(g_conf->_moreColor, styles[style].fg, 3);
+ memcpy(g_conf->_caretColor, styles[style].fg, 3);
+ }
+}
+
+void Glk::glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint) {
+ WindowStyle *styles;
+ const WindowStyle *defaults;
+
+ if (wintype == wintype_AllTypes) {
+ glk_stylehint_clear(wintype_TextGrid, style, hint);
+ glk_stylehint_clear(wintype_TextBuffer, style, hint);
+ return;
+ }
+
+ if (wintype == wintype_TextGrid) {
+ styles = g_conf->_gStyles;
+ defaults = g_conf->_gStylesDefault;
+ } else if (wintype == wintype_TextBuffer) {
+ styles = g_conf->_tStyles;
+ defaults = g_conf->_tStylesDefault;
+ } else {
+ return;
+ }
+
+ if (!g_conf->_styleHint)
+ return;
+
+ switch (hint) {
+ case stylehint_TextColor:
+ styles[style].fg[0] = defaults[style].fg[0];
+ styles[style].fg[1] = defaults[style].fg[1];
+ styles[style].fg[2] = defaults[style].fg[2];
+ break;
+
+ case stylehint_BackColor:
+ styles[style].bg[0] = defaults[style].bg[0];
+ styles[style].bg[1] = defaults[style].bg[1];
+ styles[style].bg[2] = defaults[style].bg[2];
+ break;
+
+ case stylehint_ReverseColor:
+ styles[style].reverse = defaults[style].reverse;
+ break;
+
+ case stylehint_Proportional:
+ case stylehint_Weight:
+ case stylehint_Oblique:
+ styles[style].font = defaults[style].font;
+ break;
+ }
+}
+
+glui32 Glk::glk_style_distinguish(winid_t win, glui32 style1, glui32 style2) {
+ const WindowStyle *styles = win->getStyles();
+ if (!styles)
+ return false;
+
+ return styles[style1] == styles[style2] ? 0 : 1;
+}
+
+bool Glk::glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result) {
+ const WindowStyle *styles = win->getStyles();
+ if (!styles)
+ return false;
+
+ switch (hint) {
+ case stylehint_Indentation:
+ case stylehint_ParaIndentation:
+ *result = 0;
+ break;
+
+ case stylehint_Justification:
+ *result = stylehint_just_LeftFlush;
+ break;
+
+ case stylehint_Size:
+ *result = 1;
+ break;
+
+ case stylehint_Weight:
+ *result =
+ (styles[style].font == PROPB || styles[style].font == PROPZ ||
+ styles[style].font == MONOB || styles[style].font == MONOZ);
+ break;
+
+ case stylehint_Oblique:
+ *result =
+ (styles[style].font == PROPI || styles[style].font == PROPZ ||
+ styles[style].font == MONOI || styles[style].font == MONOZ);
+ break;
+
+ case stylehint_Proportional:
+ *result =
+ (styles[style].font == PROPR || styles[style].font == PROPI ||
+ styles[style].font == PROPB || styles[style].font == PROPZ);
+ break;
+
+ case stylehint_TextColor:
+ *result =
+ (styles[style].fg[0] << 16) | (styles[style].fg[1] << 8) | (styles[style].fg[2]);
+ break;
+
+ case stylehint_BackColor:
+ *result =
+ (styles[style].bg[0] << 16) | (styles[style].bg[1] << 8) | (styles[style].bg[2]);
+ break;
+
+ case stylehint_ReverseColor:
+ *result = styles[style].reverse;
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
+ return _streams->createTemp(usage, rock);
+}
+
+frefid_t Glk::glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock) {
+ // Take out all dangerous characters
+ Common::String tempName(name);
+ for (uint idx = 0; idx < tempName.size(); ++idx) {
+ if (tempName[idx] == '/' || tempName[idx] == '\\' || tempName[idx] == ':')
+ tempName.setChar(idx, '-');
+ }
+
+ return _streams->createRef(tempName, usage, rock);
+}
+
+frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock) {
+ return _streams->createByPrompt(usage, fmode, rock);
+}
+
+frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
+ if (!fref) {
+ warning("fileref_create_from_fileref: invalid ref");
+ return nullptr;
+ } else {
+ return _streams->createFromRef(fref, usage, rock);
+ }
+}
+
+void Glk::glk_fileref_destroy(frefid_t fref) {
+ _streams->deleteRef(fref);
+}
+
+frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
+ return _streams->iterate(fref, rockptr);
+}
+
+glui32 Glk::glk_fileref_get_rock(frefid_t fref) {
+ if (!fref) {
+ warning("fileref_get_rock: invalid ref.");
+ return 0;
+ } else {
+ return fref->_rock;
+ }
+}
+
+void Glk::glk_fileref_delete_file(frefid_t fref) {
+ fref->deleteFile();
+}
+
+glui32 Glk::glk_fileref_does_file_exist(frefid_t fref) {
+ return fref->exists();
+}
+
+void Glk::glk_select(event_t *event) {
+ if (!_gliFirstEvent) {
+ _windows->inputGuessFocus();
+ _gliFirstEvent = true;
+ }
+
+ _events->getEvent(event, false);
+}
+
+void Glk::glk_select_poll(event_t *event) {
+ if (!_gliFirstEvent) {
+ _windows->inputGuessFocus();
+ _gliFirstEvent = true;
+ }
+
+ _events->getEvent(event, true);
+}
+
+void Glk::glk_request_timer_events(glui32 millisecs) {
+ _events->setTimerInterval(millisecs);
+}
+
+void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {
+ if (!win) {
+ warning("request_line_event: invalid ref");
+ } else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+ || win->_lineRequestUni) {
+ warning("request_line_event: window already has keyboard request");
+ } else {
+ win->requestLineEvent(buf, maxlen, initlen);
+ }
+}
+
+void Glk::glk_request_char_event(winid_t win) {
+ if (!win) {
+ warning("request_char_event: invalid ref");
+ } else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+ || win->_lineRequestUni) {
+ warning("request_char_event: window already has keyboard request");
+ } else {
+ win->requestCharEvent();
+ }
+}
+
+void Glk::glk_request_mouse_event(winid_t win) {
+ if (!win) {
+ warning("request_mouse_event: invalid ref");
+ } else {
+ win->requestMouseEvent();
+ }
+}
+
+void Glk::glk_cancel_line_event(winid_t win, event_t *event) {
+ if (!win) {
+ warning("cancel_line_event: invalid ref");
+ } else {
+ win->cancelLineEvent(event);
+ }
+}
+
+void Glk::glk_cancel_char_event(winid_t win) {
+ if (!win) {
+ warning("glk_cancel_char_event: invalid ref");
+ } else {
+ win->cancelCharEvent();
+ }
+}
+
+void Glk::glk_cancel_mouse_event(winid_t win) {
+ if (!win) {
+ warning("cancel_mouse_event: invalid ref");
+ } else {
+ win->cancelMouseEvent();
+ }
+}
+
+void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
+ if (!win) {
+ warning("set_echo_line_event: invalid ref");
+ } else {
+ win->setEchoLineEvent(val);
+ }
+}
+
+void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
+ if (!win) {
+ warning("set_terminators_line_event: invalid ref");
+ } else {
+ win->setTerminatorsLineEvent(keycodes, count);
+ }
+}
+
+glui32 Glk::glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+ return bufferChangeCase(buf, len, numchars, CASE_LOWER, COND_ALL, true);
+}
+
+glui32 Glk::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+ return bufferChangeCase(buf, len, numchars, CASE_UPPER, COND_ALL, true);
+}
+
+glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+ glui32 numchars, glui32 lowerrest) {
+ return bufferChangeCase(buf, len, numchars, CASE_TITLE, COND_LINESTART, lowerrest);
+}
+
+void Glk::glk_put_char_uni(glui32 ch) {
+ _streams->getCurrent()->putCharUni(ch);
+}
+
+void Glk::glk_put_string_uni(glui32 *s) {
+ _streams->getCurrent()->putBufferUni(s, strlen_uni(s));
+}
+
+void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
+ _streams->getCurrent()->putBufferUni(buf, len);
+}
+
+void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
+ if (str) {
+ str->putCharUni(ch);
+ } else {
+ warning("put_char_stream_uni: invalid ref");
+ }
+}
+
+void Glk::glk_put_string_stream_uni(strid_t str, const glui32 *s) {
+ if (str) {
+ str->putBufferUni(s, strlen_uni(s));
+ } else {
+ warning("put_string_stream_uni: invalid ref");
+ }
+}
+
+void Glk::glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len) {
+ if (str) {
+ str->putBufferUni(buf, len);
+ } else {
+ warning("put_buffer_stream_uni: invalid ref");
+ }
+}
+
+glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
+ if (str) {
+ return str->getCharUni();
+ } else {
+ warning("get_char_stream_uni: invalid ref");
+ return -1;
+ }
+}
+
+glui32 Glk::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+ if (str) {
+ return str->getBufferUni(buf, len);
+ } else {
+ warning("get_buffer_stream_uni: invalid ref");
+ return 0;
+ }
+}
+
+glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+ if (str) {
+ return str->getLineUni(buf, len);
+ } else {
+ warning("get_line_stream_uni: invalid ref");
+ return (glui32) - 1;
+ }
+}
+
+strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock) {
+ return _streams->openFileStream(fileref, fmode, rock, true);
+}
+
+strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock) {
+ return _streams->openMemoryStream(buf, buflen, fmode, rock, true);
+}
+
+void Glk::glk_request_char_event_uni(winid_t win) {
+ if (!win) {
+ warning("request_char_event_uni: invalid ref");
+ } else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+ || win->_lineRequestUni) {
+ warning("request_char_event_uni: window already has keyboard request");
+ } else {
+ win->requestCharEvent();
+ }
+}
+
+void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen) {
+ if (!win) {
+ warning("request_line_event_uni: invalid ref");
+ } else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+ || win->_lineRequestUni) {
+ warning("request_line_event_uni: window already has keyboard request");
+ } else {
+ win->requestLineEventUni(buf, maxlen, initlen);
+ }
+}
+
+glui32 Glk::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+ glui32 numchars) {
+ // TODO
+ return 0;
+}
+
+glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numchars) {
+ return 0;
+}
+
+glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
+ if (!win) {
+ warning("image_draw: invalid ref");
+ } else if (g_conf->_graphics) {
+ TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
+ GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
+
+ if (textWin)
+ textWin->drawPicture(image, val1, false, 0, 0);
+ else if (gfxWin)
+ gfxWin->drawPicture(image, val1, val2, false, 0, 0);
+ }
+
+ return false;
+}
+
+glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2,
+ glui32 width, glui32 height) {
+ if (!win) {
+ warning("image_draw_scaled: invalid ref");
+ } else if (g_conf->_graphics) {
+ TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
+ GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
+
+ if (textWin)
+ textWin->drawPicture(image, val1, true, width, height);
+ else if (gfxWin)
+ gfxWin->drawPicture(image, val1, val2, true, width, height);
+ }
+
+ return false;
+}
+
+glui32 Glk::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
+ if (!g_conf->_graphics)
+ return false;
+
+ Picture *pic = Picture::load(image);
+ if (!pic)
+ return false;
+
+ if (width)
+ *width = pic->w;
+ if (height)
+ *height = pic->h;
+
+ return true;
+}
+
+void Glk::glk_window_flow_break(winid_t win) {
+ if (!win) {
+ warning("window_erase_rect: invalid ref");
+ } else {
+ win->flowBreak();
+ }
+}
+
+void Glk::glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height) {
+ if (!win) {
+ warning("window_erase_rect: invalid ref");
+ } else {
+ win->eraseRect(false, Rect(left, top, left + width, top + height));
+ }
+}
+
+void Glk::glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top,
+ glui32 width, glui32 height) {
+ if (!win) {
+ warning("window_fill_rect: invalid ref");
+ } else {
+ win->eraseRect(color, Rect(left, top, left + width, top + height));
+ }
+}
+
+void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
+ if (!win) {
+ warning("window_set_background_color: invalid ref");
+ } else {
+ win->setBackgroundColor(color);
+ }
+}
+
+schanid_t Glk::glk_schannel_create(glui32 rock) {
+ // TODO
+ return nullptr;
+}
+
+void Glk::glk_schannel_destroy(schanid_t chan) {
+ // TODO
+}
+
+schanid_t Glk::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
+ // TODO
+ return nullptr;
+}
+
+glui32 Glk::glk_schannel_get_rock(schanid_t chan) {
+ // TODO
+ return 0;
+}
+
+glui32 Glk::glk_schannel_play(schanid_t chan, glui32 snd) {
+ // TODO
+ return 0;
+}
+
+glui32 Glk::glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify) {
+ // TODO
+ return 0;
+}
+
+void Glk::glk_schannel_stop(schanid_t chan) {
+ // TODO
+}
+
+void Glk::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
+ // TODO
+}
+
+void Glk::glk_sound_load_hint(glui32 snd, glui32 flag) {
+ // TODO
+}
+
+schanid_t Glk::glk_schannel_create_ext(glui32 rock, glui32 volume) {
+ // TODO
+ return nullptr;
+}
+
+glui32 Glk::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+ glui32 *sndarray, glui32 soundcount, glui32 notify) {
+ // TODO
+ return 0;
+}
+
+void Glk::glk_schannel_pause(schanid_t chan) {
+ // TODO
+}
+
+void Glk::glk_schannel_unpause(schanid_t chan) {
+ // TODO
+}
+
+void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+ glui32 duration, glui32 notify) {
+ // TODO
+}
+
+void Glk::glk_set_hyperlink(glui32 linkval) {
+ _streams->getCurrent()->setHyperlink(linkval);
+}
+
+void Glk::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
+ if (str)
+ str->setHyperlink(linkval);
+}
+
+void Glk::glk_request_hyperlink_event(winid_t win) {
+ if (!win) {
+ warning("request_hyperlink_event: invalid ref");
+ } else {
+ win->requestHyperlinkEvent();
+ }
+}
+
+void Glk::glk_cancel_hyperlink_event(winid_t win) {
+ if (win) {
+ win->cancelHyperlinkEvent();
+ } else {
+ warning("cancel_hyperlink_event: invalid ref");
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Glk::glk_current_time(glktimeval_t *time) {
+ TimeAndDate td;
+ *time = td;
+}
+
+glsi32 Glk::glk_current_simple_time(glui32 factor) {
+ assert(factor);
+ TimeAndDate td;
+
+ return td / factor;
+}
+
+void Glk::glk_time_to_date_utc(const glktimeval_t *time, glkdate_t *date) {
+ // TODO: timezones aren't currently supported
+ *date = TimeAndDate(*time);
+}
+
+void Glk::glk_time_to_date_local(const glktimeval_t *time, glkdate_t *date) {
+ *date = TimeAndDate(*time);
+}
+
+void Glk::glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) {
+ TimeSeconds secs = (int64)time * factor;
+ *date = TimeAndDate(secs);
+}
+
+void Glk::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) {
+ TimeSeconds secs = (int64)time * factor;
+ *date = TimeAndDate(secs);
+}
+
+void Glk::glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time) {
+ // WORKAROUND: timezones aren't currently supported
+ *time = TimeAndDate(*date);
+}
+
+void Glk::glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time) {
+ *time = TimeAndDate(*date);
+}
+
+glsi32 Glk::glk_date_to_simple_time_utc(const glkdate_t *date, glui32 factor) {
+ // WORKAROUND: timezones aren't currently supported
+ assert(factor);
+ TimeSeconds ts = TimeAndDate(*date);
+ return ts / factor;
+}
+
+glsi32 Glk::glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor) {
+ assert(factor);
+ TimeSeconds ts = TimeAndDate(*date);
+ return ts / factor;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* XXX non-official Glk functions */
+
+const char *Glk::garglk_fileref_get_name(frefid_t fref) const {
+ return fref->_filename.c_str();
+}
+
+void Glk::garglk_set_program_name(const char *name) {
+ // Program name isn't displayed
+}
+
+void Glk::garglk_set_program_info(const char *info) {
+ // Program info isn't displayed
+}
+
+void Glk::garglk_set_story_name(const char *name) {
+ // Story name isn't displayed
+}
+
+void Glk::garglk_set_story_title(const char *title) {
+ // Story title isn't displayed
+}
+
+void Glk::garglk_set_config(const char *name) {
+ // No implementation
+}
+
+void Glk::garglk_unput_string(const char *str) {
+ _streams->getCurrent()->unputBuffer(str, strlen(str));
+}
+
+void Glk::garglk_unput_string_uni(const glui32 *str) {
+ _streams->getCurrent()->unputBufferUni(str, strlen_uni(str));
+}
+
+void Glk::garglk_set_zcolors(glui32 fg, glui32 bg) {
+ _streams->getCurrent()->setZColors(fg, bg);
+}
+
+void Glk::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
+ if (str) {
+ str->setZColors(fg, bg);
+ } else {
+ warning("set_style_stream: Invalid ref");
+ }
+}
+
+void Glk::garglk_set_reversevideo(glui32 reverse) {
+ _streams->getCurrent()->setReverseVideo(reverse != 0);
+}
+
+void Glk::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
+ if (str) {
+ str->setReverseVideo(reverse != 0);
+ } else {
+ warning("set_reversevideo: Invalid ref");
+ }
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
new file mode 100644
index 0000000000..f71ad764ac
--- /dev/null
+++ b/engines/glk/glk.h
@@ -0,0 +1,292 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_GLK_H
+#define GLK_GLK_H
+
+#include "glk/gargoyle.h"
+#include "glk/glk_types.h"
+#include "glk/blorb.h"
+#include "glk/time.h"
+#include "glk/windows.h"
+
+namespace Gargoyle {
+
+/**
+ * Implements the GLK interface
+ */
+class Glk : public GargoyleEngine, public Blorb {
+private:
+ bool _gliFirstEvent;
+ unsigned char _charTolowerTable[256];
+ unsigned char _charToupperTable[256];
+public:
+ /**
+ * Constructor
+ */
+ Glk(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+ void glk_exit(void);
+ void glk_set_interrupt_handler(void(*func)(void));
+ void glk_tick(void);
+
+ glui32 glk_gestalt(glui32 id, glui32 val);
+ glui32 glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen);
+
+ unsigned char glk_char_to_lower(unsigned char ch);
+ unsigned char glk_char_to_upper(unsigned char ch);
+
+ /**
+ * Get the root window of the window hierarchy
+ */
+ winid_t glk_window_get_root(void) const;
+
+ /**
+ * Open a new window
+ */
+ winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
+ glui32 wintype, glui32 rock = 0) const;
+
+ void glk_window_close(winid_t win, stream_result_t *result = nullptr);
+ void glk_window_get_size(winid_t win, glui32 *width, glui32 *height);
+ void glk_window_set_arrangement(winid_t win, glui32 method,
+ glui32 size, winid_t keyWin);
+ void glk_window_get_arrangement(winid_t win, glui32 *method,
+ glui32 *size, winid_t *keyWin);
+ winid_t glk_window_iterate(winid_t win, glui32 *rock = 0);
+ glui32 glk_window_get_rock(winid_t win);
+ glui32 glk_window_get_type(winid_t win);
+ winid_t glk_window_get_parent(winid_t win);
+ winid_t glk_window_get_sibling(winid_t win);
+ void glk_window_clear(winid_t win);
+ void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos);
+
+ strid_t glk_window_get_stream(winid_t win);
+ void glk_window_set_echo_stream(winid_t win, strid_t str);
+ strid_t glk_window_get_echo_stream(winid_t win);
+ void glk_set_window(winid_t win);
+
+ strid_t glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock = 0);
+ strid_t glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
+ void glk_stream_close(strid_t str, stream_result_t *result = nullptr);
+ strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) const;
+ glui32 glk_stream_get_rock(strid_t str) const;
+ void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode);
+ glui32 glk_stream_get_position(strid_t str) const;
+ void glk_stream_set_current(strid_t str);
+ strid_t glk_stream_get_current(void);
+
+ void glk_put_char(unsigned char ch);
+ void glk_put_char_stream(strid_t str, unsigned char ch);
+ void glk_put_string(const char *s);
+ void glk_put_string_stream(strid_t str, const char *s);
+ void glk_put_buffer(char *buf, glui32 len);
+ void glk_put_buffer_stream(strid_t str, const char *buf, glui32 len);
+ void glk_set_style(glui32 styl);
+ void glk_set_style_stream(strid_t str, glui32 styl);
+
+ glsi32 glk_get_char_stream(strid_t str);
+ glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len);
+ glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);
+
+ void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
+ glsi32 val);
+ void glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint);
+ glui32 glk_style_distinguish(winid_t win, glui32 style1, glui32 style2);
+ bool glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result);
+
+ frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock = 0);
+ frefid_t glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock = 0);
+ frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock = 0);
+ frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock = 0);
+ void glk_fileref_destroy(frefid_t fref);
+ frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
+ glui32 glk_fileref_get_rock(frefid_t fref);
+ void glk_fileref_delete_file(frefid_t fref);
+ glui32 glk_fileref_does_file_exist(frefid_t fref);
+
+ void glk_select(event_t *event);
+ void glk_select_poll(event_t *event);
+
+ void glk_request_timer_events(glui32 millisecs);
+
+ void glk_request_line_event(winid_t win, char *buf, glui32 maxlen,
+ glui32 initlen);
+ void glk_request_char_event(winid_t win);
+ void glk_request_mouse_event(winid_t win);
+
+ void glk_cancel_line_event(winid_t win, event_t *event);
+ void glk_cancel_char_event(winid_t win);
+ void glk_cancel_mouse_event(winid_t win);
+
+#ifdef GLK_MODULE_LINE_ECHO
+ void glk_set_echo_line_event(winid_t win, glui32 val);
+#endif /* GLK_MODULE_LINE_ECHO */
+
+#ifdef GLK_MODULE_LINE_TERMINATORS
+ void glk_set_terminators_line_event(winid_t win, glui32 *keycodes,
+ glui32 count);
+#endif /* GLK_MODULE_LINE_TERMINATORS */
+
+ /** \addtogroup Unicode
+ * @{
+ */
+
+ glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
+ glui32 numchars);
+ glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len,
+ glui32 numchars);
+ glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+ glui32 numchars, glui32 lowerrest);
+
+ void glk_put_char_uni(glui32 ch);
+ void glk_put_string_uni(glui32 *s);
+ void glk_put_buffer_uni(glui32 *buf, glui32 len);
+ void glk_put_char_stream_uni(strid_t str, glui32 ch);
+ void glk_put_string_stream_uni(strid_t str, const glui32 *s);
+ void glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len);
+
+ glsi32 glk_get_char_stream_uni(strid_t str);
+ glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
+ glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
+
+ strid_t glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock = 0);
+ strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
+
+ void glk_request_char_event_uni(winid_t win);
+ void glk_request_line_event_uni(winid_t win, glui32 *buf,
+ glui32 maxlen, glui32 initlen);
+
+ /** @}*/
+
+#ifdef GLK_MODULE_UNICODE_NORM
+
+ glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+ glui32 numchars);
+ glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len,
+ glui32 numchars);
+
+#endif /* GLK_MODULE_UNICODE_NORM */
+
+#ifdef GLK_MODULE_IMAGE
+
+ glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
+ glui32 glk_image_draw_scaled(winid_t win, glui32 image,
+ glsi32 val1, glsi32 val2, glui32 width, glui32 height);
+ glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
+
+ void glk_window_flow_break(winid_t win);
+
+ void glk_window_erase_rect(winid_t win,
+ glsi32 left, glsi32 top, glui32 width, glui32 height);
+ void glk_window_fill_rect(winid_t win, glui32 color,
+ glsi32 left, glsi32 top, glui32 width, glui32 height);
+ void glk_window_set_background_color(winid_t win, glui32 color);
+
+#endif /* GLK_MODULE_IMAGE */
+
+#ifdef GLK_MODULE_SOUND
+
+ schanid_t glk_schannel_create(glui32 rock = 0);
+ void glk_schannel_destroy(schanid_t chan);
+ schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
+ glui32 glk_schannel_get_rock(schanid_t chan);
+
+ glui32 glk_schannel_play(schanid_t chan, glui32 snd);
+ glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
+ glui32 notify);
+ void glk_schannel_stop(schanid_t chan);
+ void glk_schannel_set_volume(schanid_t chan, glui32 vol);
+
+ void glk_sound_load_hint(glui32 snd, glui32 flag);
+
+#ifdef GLK_MODULE_SOUND2
+ /* Note that this section is nested inside the #ifdef GLK_MODULE_SOUND.
+ GLK_MODULE_SOUND must be defined if GLK_MODULE_SOUND2 is. */
+
+ schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume);
+ glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+ glui32 *sndarray, glui32 soundcount, glui32 notify);
+ void glk_schannel_pause(schanid_t chan);
+ void glk_schannel_unpause(schanid_t chan);
+ void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+ glui32 duration, glui32 notify);
+
+#endif /* GLK_MODULE_SOUND2 */
+#endif /* GLK_MODULE_SOUND */
+
+#ifdef GLK_MODULE_HYPERLINKS
+
+ void glk_set_hyperlink(glui32 linkval);
+ void glk_set_hyperlink_stream(strid_t str, glui32 linkval);
+ void glk_request_hyperlink_event(winid_t win);
+ void glk_cancel_hyperlink_event(winid_t win);
+
+#endif /* GLK_MODULE_HYPERLINKS */
+
+#ifdef GLK_MODULE_DATETIME
+
+ void glk_current_time(glktimeval_t *time);
+ glsi32 glk_current_simple_time(glui32 factor);
+ void glk_time_to_date_utc(const glktimeval_t *time, glkdate_t *date);
+ void glk_time_to_date_local(const glktimeval_t *time, glkdate_t *date);
+ void glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date);
+ void glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date);
+ void glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time);
+ void glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time);
+ glsi32 glk_date_to_simple_time_utc(const glkdate_t *date, glui32 factor);
+ glsi32 glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor);
+
+#endif /* GLK_MODULE_DATETIME */
+
+ /* XXX non-official Glk functions that may or may not exist */
+#define GARGLK 1
+
+ const char *garglk_fileref_get_name(frefid_t fref) const;
+
+ void garglk_set_program_name(const char *name);
+ void garglk_set_program_info(const char *info);
+ void garglk_set_story_name(const char *name);
+ void garglk_set_story_title(const char *title);
+ void garglk_set_config(const char *name);
+
+ /**
+ * Removes the specified string from the end of the output buffer, if
+ * indeed it is there.
+ */
+ void garglk_unput_string(const char *str);
+
+ /**
+ * Removes the specified string from the end of the output buffer, if
+ * indeed it is there.
+ */
+ void garglk_unput_string_uni(const glui32 *str);
+
+ void garglk_set_zcolors(glui32 fg, glui32 bg);
+ void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg);
+ void garglk_set_reversevideo(glui32 reverse);
+ void garglk_set_reversevideo_stream(strid_t str, glui32 reverse);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
new file mode 100644
index 0000000000..32b0968761
--- /dev/null
+++ b/engines/glk/glk_types.h
@@ -0,0 +1,208 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_GLK_TYPES_H
+#define GLK_GLK_TYPES_H
+
+#include "common/scummsys.h"
+#include "common/stream.h"
+
+namespace Gargoyle {
+
+typedef uint32 glui32;
+typedef int32 glsi32;
+class Window;
+
+/**
+ * These are the compile-time conditionals that reveal various Glk optional modules.
+ */
+#define GLK_MODULE_LINE_ECHO
+#define GLK_MODULE_LINE_TERMINATORS
+#define GLK_MODULE_UNICODE
+#define GLK_MODULE_UNICODE_NORM
+#define GLK_MODULE_IMAGE
+#define GLK_MODULE_SOUND
+#define GLK_MODULE_SOUND2
+#define GLK_MODULE_HYPERLINKS
+#define GLK_MODULE_DATETIME
+#define GLK_MODULE_GARGLKTEXT
+
+typedef struct glk_schannel_struct *schanid_t;
+
+/**
+ * Usurp C1 space for ligatures and smart typography glyphs
+ */
+enum Enc {
+ ENC_LIG_FI = 128,
+ ENC_LIG_FL = 129,
+ ENC_LSQUO = 130,
+ ENC_RSQUO = 131,
+ ENC_LDQUO = 132,
+ ENC_RDQUO = 133,
+ ENC_NDASH = 134,
+ ENC_MDASH = 135,
+ ENC_FLOWBREAK = 136
+};
+
+/**
+ * These are the Unicode versions
+ */
+enum UniChars {
+ UNI_LIG_FI = 0xFB01,
+ UNI_LIG_FL = 0xFB02,
+ UNI_LSQUO = 0x2018,
+ UNI_RSQUO = 0x2019,
+ UNI_LDQUO = 0x201c,
+ UNI_RDQUO = 0x201d,
+ UNI_NDASH = 0x2013,
+ UNI_MDASH = 0x2014
+};
+
+enum Gestalt {
+ gestalt_Version = 0,
+ gestalt_CharInput = 1,
+ gestalt_LineInput = 2,
+ gestalt_CharOutput = 3,
+ gestalt_CharOutput_CannotPrint = 0,
+ gestalt_CharOutput_ApproxPrint = 1,
+ gestalt_CharOutput_ExactPrint = 2,
+ gestalt_MouseInput = 4,
+ gestalt_Timer = 5,
+ gestalt_Graphics = 6,
+ gestalt_DrawImage = 7,
+ gestalt_Sound = 8,
+ gestalt_SoundVolume = 9,
+ gestalt_SoundNotify = 10,
+ gestalt_Hyperlinks = 11,
+ gestalt_HyperlinkInput = 12,
+ gestalt_SoundMusic = 13,
+ gestalt_GraphicsTransparency = 14,
+ gestalt_Unicode = 15,
+ gestalt_UnicodeNorm = 16,
+ gestalt_LineInputEcho = 17,
+ gestalt_LineTerminators = 18,
+ gestalt_LineTerminatorKey = 19,
+ gestalt_DateTime = 20,
+ gestalt_Sound2 = 21,
+ gestalt_GarglkText = 0x1100
+};
+
+enum Style {
+ style_Normal = 0,
+ style_Emphasized = 1,
+ style_Preformatted = 2,
+ style_Header = 3,
+ style_Subheader = 4,
+ style_Alert = 5,
+ style_Note = 6,
+ style_BlockQuote = 7,
+ style_Input = 8,
+ style_User1 = 9,
+ style_User2 = 10,
+ style_NUMSTYLES = 11
+};
+
+enum WinType {
+ wintype_AllTypes = 0,
+ wintype_Pair = 1,
+ wintype_Blank = 2,
+ wintype_TextBuffer = 3,
+ wintype_TextGrid = 4,
+ wintype_Graphics = 5
+};
+
+enum WinMethod {
+ winmethod_Left = 0x00,
+ winmethod_Right = 0x01,
+ winmethod_Above = 0x02,
+ winmethod_Below = 0x03,
+ winmethod_DirMask = 0x0f,
+
+ winmethod_Fixed = 0x10,
+ winmethod_Proportional = 0x20,
+ winmethod_DivisionMask = 0xf0,
+
+ winmethod_Border = 0x000,
+ winmethod_NoBorder = 0x100,
+ winmethod_BorderMask = 0x100
+};
+
+enum StyleHint {
+ stylehint_Indentation = 0,
+ stylehint_ParaIndentation = 1,
+ stylehint_Justification = 2,
+ stylehint_Size = 3,
+ stylehint_Weight = 4,
+ stylehint_Oblique = 5,
+ stylehint_Proportional = 6,
+ stylehint_TextColor = 7,
+ stylehint_BackColor = 8,
+ stylehint_ReverseColor = 9,
+ stylehint_NUMHINTS = 10,
+
+ stylehint_just_LeftFlush = 0,
+ stylehint_just_LeftRight = 1,
+ stylehint_just_Centered = 2,
+ stylehint_just_RightFlush = 3
+};
+
+/**
+ * These constants define the classes of opaque objects. It's a bit ugly to put
+ * them in this header file, since more classes may be added in the future.
+ * But if you find yourself stuck with an obsolete version of this file,
+ * adding new class definitions will be easy enough -- they will be numbered
+ * sequentially, and the numeric constants can be found in the Glk specification.
+ */
+enum giDisp {
+ gidisp_Class_Window = 0,
+ gidisp_Class_Stream = 1,
+ gidisp_Class_Fileref = 2,
+ gidisp_Class_Schannel = 3
+};
+
+enum zcolor {
+ zcolor_Transparent = (uint32) - 4,
+ zcolor_Cursor = (uint32) - 3,
+ zcolor_Current = (uint32) - 2,
+ zcolor_Default = (uint32) - 1
+};
+
+#ifdef GLK_MODULE_IMAGE
+
+enum ImageAlign {
+ imagealign_InlineUp = 1,
+ imagealign_InlineDown = 2,
+ imagealign_InlineCenter = 3,
+ imagealign_MarginLeft = 4,
+ imagealign_MarginRight = 5
+};
+
+#endif /* GLK_MODULE_IMAGE */
+
+union gidispatch_rock_t {
+ glui32 num;
+ void *ptr;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
new file mode 100644
index 0000000000..e3fa26b684
--- /dev/null
+++ b/engines/glk/module.mk
@@ -0,0 +1,50 @@
+MODULE := engines/glk
+
+MODULE_OBJS := \
+ blorb.o \
+ conf.o \
+ detection.o \
+ events.o \
+ fonts.o \
+ gargoyle.o \
+ glk.o \
+ picture.o \
+ screen.o \
+ selection.o \
+ streams.o \
+ time.o \
+ unicode.o \
+ unicode_gen.o \
+ utils.o \
+ windows.o \
+ window_graphics.o \
+ window_pair.o \
+ window_text_buffer.o \
+ window_text_grid.o \
+ frotz/detection.o \
+ frotz/detection_tables.o \
+ frotz/frotz.o \
+ frotz/glk_interface.o \
+ frotz/mem.o \
+ frotz/processor.o \
+ frotz/processor_buffer.o \
+ frotz/processor_input.o \
+ frotz/processor_maths.o \
+ frotz/processor_mem.o \
+ frotz/processor_objects.o \
+ frotz/processor_screen.o \
+ frotz/processor_streams.o \
+ frotz/processor_table.o \
+ frotz/processor_text.o \
+ frotz/processor_variables.o \
+ frotz/quetzal.o \
+ scott/detection.o \
+ scott/scott.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_GLK), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
new file mode 100644
index 0000000000..444589bde8
--- /dev/null
+++ b/engines/glk/picture.cpp
@@ -0,0 +1,63 @@
+/* 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/picture.h"
+
+namespace Gargoyle {
+
+void PicList::increment() {
+ // TODO
+}
+
+void PicList::decrement() {
+ // TODO
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Picture::increment() {
+ ++_refCount;
+}
+
+void Picture::decrement() {
+ if (_refCount > 0 && --_refCount == 0) {
+ free();
+ delete this;
+ }
+}
+
+Picture *Picture::load(uint32 id) {
+ // TODO: gli_picture_load
+ return nullptr;
+}
+
+Picture *Picture::scale(int sx, int sy) {
+ // TODO: gli_picture_scale
+ return nullptr;
+}
+
+void Picture::drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1) {
+ // TODO: drawPicture
+}
+
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/picture.h b/engines/glk/picture.h
new file mode 100644
index 0000000000..e29d420bdf
--- /dev/null
+++ b/engines/glk/picture.h
@@ -0,0 +1,73 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_PICTURE_H
+#define GLK_PICTURE_H
+
+#include "graphics/surface.h"
+
+namespace Gargoyle {
+
+class PicList {
+public:
+ void increment();
+
+ void decrement();
+};
+
+struct Picture : Graphics::Surface {
+public:
+ static Picture *load(uint32 id);
+public:
+ int _refCount;
+ uint32 _id;
+ bool _scaled;
+
+ /**
+ * Constructor
+ */
+ Picture() : Graphics::Surface(), _refCount(0), _id(0), _scaled(0) {}
+
+ /**
+ * Increment reference counter
+ */
+ void increment();
+
+ /**
+ * Decrement reference counter
+ */
+ void decrement();
+
+ /**
+ * Rescale the picture to a new picture of a given size
+ */
+ Picture *scale(int sx, int sy);
+
+ /**
+ * Draw the picture
+ */
+ void drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/scott/detection.cpp b/engines/glk/scott/detection.cpp
new file mode 100644
index 0000000000..900f6507fc
--- /dev/null
+++ b/engines/glk/scott/detection.cpp
@@ -0,0 +1,109 @@
+/* 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/scott/detection.h"
+#include "common/file.h"
+#include "common/md5.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+struct ScottGame {
+ const char *_md5;
+ const char *_gameId;
+ int32 _filesize;
+ const char *_desc;
+};
+
+const ScottGame SCOTT_GAMES[] = {
+ // PC game versions
+ { "7c6f495d757a54e73d259efc718d8024", "adventureland", 15896, "Adventureland" },
+ { "ea535fa7684508410151b4561de1f323", "pirateadventure", 16325, "Pirate Adventure" },
+ { "379c77a9a483886366b3b5c425e56410", "missionimpossible", 15275, "Mission Impossible" },
+ { "a530a6857d1092eaa177eee575c94c71", "voodoocastle", 15852, "Voodoo Castle" },
+ { "5ebb4ade985670bb2eac54f8fa202214", "thecount", 17476, "The Count" },
+ { "c57bb6df04dc77a2b232bc5bcab6e417", "strangeodyssey", 17489, "Strange Odyssey" },
+ { "ce2931ac3d5cbc270a5cb7be9e614f6e", "mysteryfunhouse", 17165, "Mystery Fun House" },
+ { "4e6127fad6b5d75eccd3f3b101f8c9c8", "pyramidofdoom", 17673, "Pyramid Of Doom" },
+ { "2c08327ab06d5490bd9e367ddaeca627", "ghosttown", 17831, "Ghost Town" },
+ { "8feb77f11d32e9567ce2fc7d435eaf44", "savageisland1", 19533, "Savage Island, Part 1" },
+ { "20c40a349f7a214ac515fb1d63c30a87", "savageisland2", 18367, "Savage Island, Part 2" },
+ { "e2a8f956ab215012d1495550c4c11ee8", "goldenvoyage", 18513, "The Golden Voyage" },
+ { "f986d7e1ee074f65b6c1d00461c9b3c3", "adventure13", 19232, "Adventure 13" },
+ { "6d98f422cc986d959a3c74351785aea3", "adventure14", 19013, "Adventure 14" },
+ { "aadcc04e6b37eb9d30a58b5bc775842e", "marveladventure", 18876, "Marvel Adventure #1" },
+ { "d569a769f304dc02b3062d97458ddd01", "scottsampler", 13854, "Adventure International's Mini-Adventure Sampler" },
+
+ // PDA game versions
+ { "ae541fc1085da2f7d561b72ed20a6bc1", "adventureland", 18003, "Adventureland" },
+ { "cbd47ab4fcfe00231ffd71d52378d410", "pirateadventure", 18482, "Pirate Adventure" },
+ { "9251ab2c64e63559d8a6e9e6246760a5", "missionimpossible", 17227, "Mission Impossible" },
+ { "be849c5747c7fc3b201984afb4403b8e", "voodoocastle", 18140, "Voodoo Castle" },
+ { "85b75b6079b5ee572b5259b29a0e5d21", "thecount", 19999, "The Count" },
+ { "c423cae841ac1927b5b2e503607b21bc", "strangeodyssey", 20115, "Strange Odyssey" },
+ { "326b98b991d401605074e64d474ce566", "mysteryfunhouse", 19700, "Mystery Fun House" },
+ { "8ef9010399f055da9adb15ce7745a11c", "pyramidofdoom", 20320, "Pyramid Of Doom" },
+ { "fcdcca8b2acf76ba2d0006cefa3630a1", "ghosttown", 20687, "Ghost Town" },
+ { "c8aaa80f07c40fa8e4b17432644919dc", "savageisland1", 22669, "Savage Island, Part 1" },
+ { "2add0f28d9b236c866890cdf8d86ee60", "savageisland2", 21169, "Savage Island, Part 2" },
+ { "675126bd0477e8ed9230ad3db5afc45f", "goldenvoyage", 21401, "The Golden Voyage" },
+ { "0ef0def798d895ed766041fa99dd28a0", "adventure13", 22346, "Adventure 13" },
+ { "0bf1bcc649422798332a38c88588fdff", "adventure14", 22087, "Adventure 14" },
+ { "a0a5423967287dae9cbeb9abe8324479", "buckaroobonzai", 21038, "Buckaroo Banzai" },
+ { nullptr, nullptr, 0, nullptr }
+};
+
+bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+ Common::File gameFile;
+ Common::String md5;
+
+ // Loop through the files of the folder
+ for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (file->isDirectory() || !(file->getName().hasSuffixIgnoreCase(".saga")
+ || file->getName().hasSuffixIgnoreCase(".dat")))
+ continue;
+
+ if (gameFile.open(*file)) {
+ md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+
+ // Scan through the Scott game list for a match
+ const ScottGame *p = SCOTT_GAMES;
+ while (p->_md5 && p->_filesize != gameFile.size() && md5 != p->_md5)
+ ++p;
+
+ if (p->_filesize) {
+ // Found a match
+ DetectedGame gd(p->_gameId, p->_desc, Common::EN_ANY, Common::kPlatformUnknown);
+ gd.addExtraEntry("filename", file->getName());
+
+ gameList.push_back(gd);
+ }
+
+ gameFile.close();
+ }
+ }
+
+ return !gameList.empty();
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/scott/detection.h b/engines/glk/scott/detection.h
new file mode 100644
index 0000000000..cd487bdfdd
--- /dev/null
+++ b/engines/glk/scott/detection.h
@@ -0,0 +1,43 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_SCOTT_DETECTION
+#define GLK_SCOTT_DETECTION
+
+#include "common/fs.h"
+#include "engines/game.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+class ScottMetaEngine {
+public:
+ /**
+ * Detect supported games
+ */
+ static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+};
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
new file mode 100644
index 0000000000..a5e866796a
--- /dev/null
+++ b/engines/glk/scott/scott.cpp
@@ -0,0 +1,1263 @@
+/* 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/scott/scott.h"
+#include "common/config-manager.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+Scott::Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
+ Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
+ Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
+ split_screen(true), Bottom(0), Top(0), BitFlags(0), _saveSlot(-1) {
+ Common::fill(&NounText[0], &NounText[16], '\0');
+ Common::fill(&Counters[0], &Counters[16], 0);
+ Common::fill(&RoomSaved[0], &RoomSaved[16], 0);
+}
+
+void Scott::runGame(Common::SeekableReadStream *gameFile) {
+ int vb, no;
+ initialize();
+
+ Bottom = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
+ if (Bottom == nullptr)
+ glk_exit();
+ glk_set_window(Bottom);
+
+ if (Options & TRS80_STYLE) {
+ Width = 64;
+ TopHeight = 11;
+ } else {
+ Width = 80;
+ TopHeight = 10;
+ }
+
+ if (split_screen) {
+ Top = glk_window_open(Bottom, winmethod_Above | winmethod_Fixed, TopHeight, wintype_TextGrid, 0);
+ if (Top == nullptr) {
+ split_screen = 0;
+ Top = Bottom;
+ }
+ } else {
+ Top = Bottom;
+ }
+
+ output("ScummVM support adapted from Scott Free, A Scott Adams game driver in C.\n\n");
+
+ // Check for savegame
+ _saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
+
+ // Load the game
+ loadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
+
+ // Main game loop
+ while (!shouldQuit()) {
+ glk_tick();
+
+ performActions(0, 0);
+
+ if (_saveSlot >= 0) {
+ // Load any savegame during startup
+ loadGameState(_saveSlot);
+ _saveSlot = -1;
+ }
+
+ look();
+
+ if (getInput(&vb, &no) == -1)
+ continue;
+ if (g_vm->shouldQuit())
+ break;
+
+ switch (performActions(vb, no)) {
+ case -1:
+ output("I don't understand your command. ");
+ break;
+ case -2:
+ output("I can't do that yet. ");
+ break;
+ default:
+ break;
+ }
+
+ // Brian Howarth games seem to use -1 for forever
+ if (Items[LIGHT_SOURCE].Location/*==-1*/ != DESTROYED && GameHeader.LightTime != -1) {
+ GameHeader.LightTime--;
+ if (GameHeader.LightTime < 1) {
+ BitFlags |= (1 << LIGHTOUTBIT);
+ if (Items[LIGHT_SOURCE].Location == CARRIED ||
+ Items[LIGHT_SOURCE].Location == MyLoc) {
+ if (Options & SCOTTLIGHT)
+ output("Light has run out! ");
+ else
+ output("Your light has run out. ");
+ }
+ if (Options & PREHISTORIC_LAMP)
+ Items[LIGHT_SOURCE].Location = DESTROYED;
+ } else if (GameHeader.LightTime < 25) {
+ if (Items[LIGHT_SOURCE].Location == CARRIED ||
+ Items[LIGHT_SOURCE].Location == MyLoc) {
+
+ if (Options & SCOTTLIGHT) {
+ output("Light runs out in ");
+ outputNumber(GameHeader.LightTime);
+ output(" turns. ");
+ } else {
+ if (GameHeader.LightTime % 5 == 0)
+ output("Your light is growing dim. ");
+ }
+ }
+ }
+ }
+ }
+}
+
+void Scott::initialize() {
+ if (ConfMan.hasKey("YOUARE")) {
+ if (ConfMan.getBool("YOUARE"))
+ Options |= YOUARE;
+ else
+ Options &= ~YOUARE;
+ }
+ if (gDebugLevel > 0)
+ Options |= DEBUGGING;
+ if (ConfMan.hasKey("SCOTTLIGHT") && ConfMan.getBool("SCOTTLIGHT"))
+ Options |= SCOTTLIGHT;
+ if (ConfMan.hasKey("TRS80_STYLE") && ConfMan.getBool("TRS80_STYLE"))
+ Options |= TRS80_STYLE;
+ if (ConfMan.hasKey("PREHISTORIC_LAMP") && ConfMan.getBool("PREHISTORIC_LAMP"))
+ Options |= PREHISTORIC_LAMP;
+}
+
+void Scott::display(winid_t w, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ Common::String msg = Common::String::vformat(fmt, ap);
+ va_end(ap);
+
+ glk_put_string_stream(glk_window_get_stream(w), msg.c_str());
+}
+
+void Scott::delay(int seconds) {
+ event_t ev;
+
+ if (!glk_gestalt(gestalt_Timer, 0))
+ return;
+
+ glk_request_timer_events(1000 * seconds);
+
+ do {
+ glk_select(&ev);
+ } while (ev.type != evtype_Timer && ev.type != evtype_Quit);
+
+ glk_request_timer_events(0);
+}
+
+void Scott::fatal(const char *x) {
+ error("%s", x);
+}
+
+void Scott::clearScreen(void) {
+ glk_window_clear(Bottom);
+}
+
+void *Scott::memAlloc(int size) {
+ void *t = (void *)malloc(size);
+ if (t == nullptr)
+ fatal("Out of memory");
+ return t;
+}
+
+bool Scott::randomPercent(uint n) {
+ return _random.getRandomNumber(99) < n;
+}
+
+int Scott::countCarried(void) {
+ int ct = 0;
+ int n = 0;
+ while (ct <= GameHeader.NumItems) {
+ if (Items[ct].Location == CARRIED)
+ n++;
+ ct++;
+ }
+ return n;
+}
+
+const char *Scott::mapSynonym(const char *word) {
+ int n = 1;
+ const char *tp;
+ static char lastword[16]; // Last non synonym
+ while (n <= GameHeader.NumWords) {
+ tp = Nouns[n];
+ if (*tp == '*')
+ tp++;
+ else
+ strcpy(lastword, tp);
+ if (xstrncasecmp(word, tp, GameHeader.WordLength) == 0)
+ return lastword;
+ n++;
+ }
+ return nullptr;
+}
+
+int Scott::matchUpItem(const char *text, int loc) {
+ const char *word = mapSynonym(text);
+ int ct = 0;
+
+ if (word == nullptr)
+ word = text;
+
+ while (ct <= GameHeader.NumItems) {
+ if (Items[ct].AutoGet && Items[ct].Location == loc &&
+ xstrncasecmp(Items[ct].AutoGet, word, GameHeader.WordLength) == 0)
+ return ct;
+ ct++;
+ }
+
+ return -1;
+}
+
+char *Scott::readString(Common::SeekableReadStream *f) {
+ char tmp[1024];
+ char *t;
+ int c, nc;
+ int ct = 0;
+ do {
+ c = f->readByte();
+ } while (f->pos() < f->size() && Common::isSpace(c));
+ if (c != '"') {
+ fatal("Initial quote expected");
+ }
+
+ for (;;) {
+ if (f->pos() >= f->size())
+ fatal("EOF in string");
+
+ c = f->readByte();
+ if (c == '"') {
+ nc = f->readByte();
+ if (nc != '"') {
+ f->seek(-1, SEEK_CUR);
+ break;
+ }
+ }
+ if (c == '`')
+ c = '"'; // pdd
+
+ // Ensure a valid Glk newline is sent.
+ if (c == '\n')
+ tmp[ct++] = 10;
+ // Special case: assume CR is part of CRLF in a DOS-formatted file, and ignore it.
+ else if (c == 13)
+ ;
+ // Pass only ASCII to Glk; the other reasonable option would be to pass Latin-1,
+ // but it's probably safe to assume that Scott Adams games are ASCII only.
+ else if ((c >= 32 && c <= 126))
+ tmp[ct++] = c;
+ else
+ tmp[ct++] = '?';
+ }
+
+ tmp[ct] = 0;
+ t = (char *)memAlloc(ct + 1);
+ memcpy(t, tmp, ct + 1);
+ return t;
+}
+
+void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
+ int unused, ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
+ int ct;
+ int lo;
+ Action *ap;
+ Room *rp;
+ Item *ip;
+
+ // Load the header
+ readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
+
+ GameHeader.NumItems = ni;
+ Items = (Item *)memAlloc(sizeof(Item) * (ni + 1));
+ GameHeader.NumActions = na;
+ Actions = (Action *)memAlloc(sizeof(Action) * (na + 1));
+ GameHeader.NumWords = nw;
+ GameHeader.WordLength = wl;
+ Verbs = (const char **)memAlloc(sizeof(char *) * (nw + 1));
+ Nouns = (const char **)memAlloc(sizeof(char *) * (nw + 1));
+ GameHeader.NumRooms = nr;
+ Rooms = (Room *)memAlloc(sizeof(Room) * (nr + 1));
+ GameHeader.MaxCarry = mc;
+ GameHeader.PlayerRoom = pr;
+ GameHeader.Treasures = tr;
+ GameHeader.LightTime = lt;
+ LightRefill = lt;
+ GameHeader.NumMessages = mn;
+ Messages = (const char **)memAlloc(sizeof(char *) * (mn + 1));
+ GameHeader.TreasureRoom = trm;
+
+ // Load the actions
+ ct = 0;
+ ap = Actions;
+ if (loud)
+ debug("Reading %d actions.", na);
+ while (ct < na + 1) {
+ readInts(f, 8,
+ &ap->Vocab,
+ &ap->Condition[0],
+ &ap->Condition[1],
+ &ap->Condition[2],
+ &ap->Condition[3],
+ &ap->Condition[4],
+ &ap->action[0],
+ &ap->action[1]);
+ ap++;
+ ct++;
+ }
+
+ ct = 0;
+ if (loud)
+ debug("Reading %d word pairs.", nw);
+ while (ct < nw + 1) {
+ Verbs[ct] = readString(f);
+ Nouns[ct] = readString(f);
+ ct++;
+ }
+ ct = 0;
+ rp = Rooms;
+ if (loud)
+ debug("Reading %d rooms.", nr);
+ while (ct < nr + 1) {
+ readInts(f, 6,
+ &rp->Exits[0], &rp->Exits[1], &rp->Exits[2],
+ &rp->Exits[3], &rp->Exits[4], &rp->Exits[5]);
+
+ rp->Text = readString(f);
+ ct++;
+ rp++;
+ }
+
+ ct = 0;
+ if (loud)
+ debug("Reading %d messages.", mn);
+ while (ct < mn + 1) {
+ Messages[ct] = readString(f);
+ ct++;
+ }
+
+ ct = 0;
+ if (loud)
+ debug("Reading %d items.", ni);
+ ip = Items;
+ while (ct < ni + 1) {
+ ip->Text = readString(f);
+ ip->AutoGet = strchr(ip->Text, '/');
+ // Some games use // to mean no auto get/drop word!
+ if (ip->AutoGet && strcmp(ip->AutoGet, "//") && strcmp(ip->AutoGet, "/*")) {
+ char *t;
+ *ip->AutoGet++ = 0;
+ t = strchr(ip->AutoGet, '/');
+ if (t != nullptr)
+ *t = 0;
+ }
+
+ readInts(f, 1, &lo);
+ ip->Location = (unsigned char)lo;
+ ip->InitialLoc = ip->Location;
+ ip++;
+ ct++;
+ }
+ ct = 0;
+ // Discard Comment Strings
+ while (ct < na + 1) {
+ free(readString(f));
+ ct++;
+ }
+
+ readInts(f, 1, &ct);
+ if (loud)
+ debug("Version %d.%02d of Adventure ", ct / 100, ct % 100);
+ readInts(f, 1, &ct);
+
+ if (loud)
+ debug("%d.\nLoad Complete.\n", ct);
+}
+
+void Scott::output(const char *a) {
+ if (_saveSlot == -1)
+ display(Bottom, "%s", a);
+}
+
+void Scott::outputNumber(int a) {
+ display(Bottom, "%d", a);
+}
+
+void Scott::look(void) {
+ const char *const ExitNames[6] = { "North", "South", "East", "West", "Up", "Down" };
+ Room *r;
+ int ct, f;
+ int pos;
+
+ if (split_screen)
+ glk_window_clear(Top);
+
+ if ((BitFlags & (1 << DARKBIT)) && Items[LIGHT_SOURCE].Location != CARRIED
+ && Items[LIGHT_SOURCE].Location != MyLoc) {
+ if (Options & YOUARE)
+ display(Top, "You can't see. It is too dark!\n");
+ else
+ display(Top, "I can't see. It is too dark!\n");
+ if (Options & TRS80_STYLE)
+ display(Top, TRS80_LINE);
+ return;
+ }
+ r = &Rooms[MyLoc];
+ if (*r->Text == '*')
+ display(Top, "%s\n", r->Text + 1);
+ else {
+ if (Options & YOUARE)
+ display(Top, "You are in a %s\n", r->Text);
+ else
+ display(Top, "I'm in a %s\n", r->Text);
+ }
+
+ ct = 0;
+ f = 0;
+ display(Top, "\nObvious exits: ");
+ while (ct < 6) {
+ if (r->Exits[ct] != 0) {
+ if (f == 0)
+ f = 1;
+ else
+ display(Top, ", ");
+ display(Top, "%s", ExitNames[ct]);
+ }
+ ct++;
+ }
+
+ if (f == 0)
+ display(Top, "none");
+ display(Top, ".\n");
+ ct = 0;
+ f = 0;
+ pos = 0;
+ while (ct <= GameHeader.NumItems) {
+ if (Items[ct].Location == MyLoc) {
+ if (f == 0) {
+ if (Options & YOUARE) {
+ display(Top, "\nYou can also see: ");
+ pos = 18;
+ } else {
+ display(Top, "\nI can also see: ");
+ pos = 16;
+ }
+ f++;
+ } else if (!(Options & TRS80_STYLE)) {
+ display(Top, " - ");
+ pos += 3;
+ }
+ if (pos + (int)strlen(Items[ct].Text) > (Width - 10)) {
+ pos = 0;
+ display(Top, "\n");
+ }
+ display(Top, "%s", Items[ct].Text);
+ pos += strlen(Items[ct].Text);
+ if (Options & TRS80_STYLE) {
+ display(Top, ". ");
+ pos += 2;
+ }
+ }
+ ct++;
+ }
+
+ display(Top, "\n");
+ if (Options & TRS80_STYLE)
+ display(Top, TRS80_LINE);
+}
+
+int Scott::whichWord(const char *word, const char **list) {
+ int n = 1;
+ int ne = 1;
+ const char *tp;
+ while (ne <= GameHeader.NumWords) {
+ tp = list[ne];
+ if (*tp == '*')
+ tp++;
+ else
+ n = ne;
+ if (xstrncasecmp(word, tp, GameHeader.WordLength) == 0)
+ return n;
+ ne++;
+ }
+ return -1;
+}
+
+void Scott::lineInput(char *buf, size_t n) {
+ event_t ev;
+
+ glk_request_line_event(Bottom, buf, n - 1, 0);
+
+ do {
+ glk_select(&ev);
+ if (ev.type == evtype_Quit)
+ return;
+ else if (ev.type == evtype_LineInput)
+ break;
+ else if (ev.type == evtype_Arrange && split_screen)
+ look();
+ } while (ev.type != evtype_Quit);
+
+ buf[ev.val1] = 0;
+}
+
+void Scott::saveGame(void) {
+ frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
+ filemode_Write, 0);
+ if (ref == nullptr)
+ return;
+
+ int slot = ref->_slotNumber;
+ Common::String desc = ref->_description;
+ glk_fileref_destroy(ref);
+
+ saveGameState(slot, desc);
+}
+
+Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
+ Common::String msg;
+ FileReference ref(slot, desc, fileusage_TextMode | fileusage_SavedGame);
+
+ strid_t file = glk_stream_open_file(&ref, filemode_Write, 0);
+ if (file == nullptr)
+ return Common::kWritingFailed;
+
+ for (int ct = 0; ct < 16; ct++) {
+ msg = Common::String::format("%d %d\n", Counters[ct], RoomSaved[ct]);
+ glk_put_string_stream(file, msg.c_str());
+ }
+
+ msg = Common::String::format("%u %d %hd %d %d %hd\n",
+ BitFlags, (BitFlags & (1 << DARKBIT)) ? 1 : 0,
+ MyLoc, CurrentCounter, SavedRoom, GameHeader.LightTime);
+ glk_put_string_stream(file, msg.c_str());
+
+ for (int ct = 0; ct <= GameHeader.NumItems; ct++) {
+ msg = Common::String::format("%hd\n", (short)Items[ct].Location);
+ glk_put_string_stream(file, msg.c_str());
+ }
+
+ glk_stream_close(file, nullptr);
+ output("Saved.\n");
+
+ return Common::kNoError;
+}
+
+void Scott::loadGame(void) {
+ frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
+ filemode_Read, 0);
+ if (ref == nullptr)
+ return;
+
+ int slotNumber = ref->_slotNumber;
+ glk_fileref_destroy(ref);
+
+ loadGameState(slotNumber);
+}
+
+Common::Error Scott::loadGameState(int slot) {
+ strid_t file;
+ char buf[128];
+ int ct = 0;
+ short lo;
+ short darkFlag;
+
+ FileReference ref(slot, "", fileusage_SavedGame | fileusage_TextMode);
+
+ file = glk_stream_open_file(&ref, filemode_Read, 0);
+ if (file == nullptr)
+ return Common::kReadingFailed;
+
+ for (ct = 0; ct < 16; ct++) {
+ glk_get_line_stream(file, buf, sizeof buf);
+ sscanf(buf, "%d %d", &Counters[ct], &RoomSaved[ct]);
+ }
+
+ glk_get_line_stream(file, buf, sizeof buf);
+ sscanf(buf, "%u %hd %d %d %d %d\n",
+ &BitFlags, &darkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
+ &GameHeader.LightTime);
+
+ // Backward compatibility
+ if (darkFlag)
+ BitFlags |= (1 << 15);
+ for (ct = 0; ct <= GameHeader.NumItems; ct++) {
+ glk_get_line_stream(file, buf, sizeof buf);
+ sscanf(buf, "%hd\n", &lo);
+ Items[ct].Location = (unsigned char)lo;
+ }
+
+ return Common::kNoError;
+}
+
+int Scott::getInput(int *vb, int *no) {
+ char buf[256];
+ char verb[10], noun[10];
+ int vc, nc;
+ int num;
+
+ do {
+ do {
+ output("\nTell me what to do ? ");
+ lineInput(buf, sizeof buf);
+ if (g_vm->shouldQuit())
+ return 0;
+
+ num = sscanf(buf, "%9s %9s", verb, noun);
+ } while (num == 0 || *buf == '\n');
+
+ if (xstrcasecmp(verb, "restore") == 0) {
+ loadGame();
+ return -1;
+ }
+ if (num == 1)
+ *noun = 0;
+ if (*noun == 0 && strlen(verb) == 1) {
+ switch (Common::isUpper((unsigned char)*verb) ? tolower((unsigned char)*verb) : *verb) {
+ case 'n':
+ strcpy(verb, "NORTH");
+ break;
+ case 'e':
+ strcpy(verb, "EAST");
+ break;
+ case 's':
+ strcpy(verb, "SOUTH");
+ break;
+ case 'w':
+ strcpy(verb, "WEST");
+ break;
+ case 'u':
+ strcpy(verb, "UP");
+ break;
+ case 'd':
+ strcpy(verb, "DOWN");
+ break;
+ // Brian Howarth interpreter also supports this
+ case 'i':
+ strcpy(verb, "INVENTORY");
+ break;
+ }
+ }
+ nc = whichWord(verb, Nouns);
+ // The Scott Adams system has a hack to avoid typing 'go'
+ if (nc >= 1 && nc <= 6) {
+ vc = 1;
+ } else {
+ vc = whichWord(verb, Verbs);
+ nc = whichWord(noun, Nouns);
+ }
+ *vb = vc;
+ *no = nc;
+ if (vc == -1) {
+ output("You use word(s) I don't know! ");
+ }
+ } while (vc == -1);
+
+ strcpy(NounText, noun); // Needed by GET/DROP hack
+ return 0;
+}
+
+int Scott::performLine(int ct) {
+ int continuation = 0;
+ int param[5], pptr = 0;
+ int act[4];
+ int cc = 0;
+
+ while (cc < 5) {
+ int cv, dv;
+ cv = Actions[ct].Condition[cc];
+ dv = cv / 20;
+ cv %= 20;
+ switch (cv) {
+ case 0:
+ param[pptr++] = dv;
+ break;
+ case 1:
+ if (Items[dv].Location != CARRIED)
+ return 0;
+ break;
+ case 2:
+ if (Items[dv].Location != MyLoc)
+ return 0;
+ break;
+ case 3:
+ if (Items[dv].Location != CARRIED &&
+ Items[dv].Location != MyLoc)
+ return 0;
+ break;
+ case 4:
+ if (MyLoc != dv)
+ return 0;
+ break;
+ case 5:
+ if (Items[dv].Location == MyLoc)
+ return 0;
+ break;
+ case 6:
+ if (Items[dv].Location == CARRIED)
+ return 0;
+ break;
+ case 7:
+ if (MyLoc == dv)
+ return 0;
+ break;
+ case 8:
+ if ((BitFlags & (1 << dv)) == 0)
+ return 0;
+ break;
+ case 9:
+ if (BitFlags & (1 << dv))
+ return 0;
+ break;
+ case 10:
+ if (countCarried() == 0)
+ return 0;
+ break;
+ case 11:
+ if (countCarried())
+ return 0;
+ break;
+ case 12:
+ if (Items[dv].Location == CARRIED || Items[dv].Location == MyLoc)
+ return 0;
+ break;
+ case 13:
+ if (Items[dv].Location == 0)
+ return 0;
+ break;
+ case 14:
+ if (Items[dv].Location)
+ return 0;
+ break;
+ case 15:
+ if (CurrentCounter > dv)
+ return 0;
+ break;
+ case 16:
+ if (CurrentCounter <= dv)
+ return 0;
+ break;
+ case 17:
+ if (Items[dv].Location != Items[dv].InitialLoc)
+ return 0;
+ break;
+ case 18:
+ if (Items[dv].Location == Items[dv].InitialLoc)
+ return 0;
+ break;
+ case 19:
+ // Only seen in Brian Howarth games so far
+ if (CurrentCounter != dv)
+ return 0;
+ break;
+ }
+ cc++;
+ }
+
+ // Actions
+ act[0] = Actions[ct].action[0];
+ act[2] = Actions[ct].action[1];
+ act[1] = act[0] % 150;
+ act[3] = act[2] % 150;
+ act[0] /= 150;
+ act[2] /= 150;
+ cc = 0;
+ pptr = 0;
+ while (cc < 4) {
+ if (act[cc] >= 1 && act[cc] < 52) {
+ output(Messages[act[cc]]);
+ output("\n");
+ } else if (act[cc] > 101) {
+ output(Messages[act[cc] - 50]);
+ output("\n");
+ } else {
+ switch (act[cc]) {
+ case 0:// NOP
+ break;
+ case 52:
+ if (countCarried() == GameHeader.MaxCarry) {
+ if (Options & YOUARE)
+ output("You are carrying too much. ");
+ else
+ output("I've too much to carry! ");
+ break;
+ }
+ Items[param[pptr++]].Location = CARRIED;
+ break;
+ case 53:
+ Items[param[pptr++]].Location = MyLoc;
+ break;
+ case 54:
+ MyLoc = param[pptr++];
+ break;
+ case 55:
+ Items[param[pptr++]].Location = 0;
+ break;
+ case 56:
+ BitFlags |= 1 << DARKBIT;
+ break;
+ case 57:
+ BitFlags &= ~(1 << DARKBIT);
+ break;
+ case 58:
+ BitFlags |= (1 << param[pptr++]);
+ break;
+ case 59:
+ Items[param[pptr++]].Location = 0;
+ break;
+ case 60:
+ BitFlags &= ~(1 << param[pptr++]);
+ break;
+ case 61:
+ if (Options & YOUARE)
+ output("You are dead.\n");
+ else
+ output("I am dead.\n");
+ BitFlags &= ~(1 << DARKBIT);
+ MyLoc = GameHeader.NumRooms;// It seems to be what the code says!
+ break;
+ case 62: {
+ // Bug fix for some systems - before it could get parameters wrong */
+ int i = param[pptr++];
+ Items[i].Location = param[pptr++];
+ break;
+ }
+ case 63:
+doneit:
+ output("The game is now over.\n");
+ glk_exit();
+ break;
+ case 64:
+ break;
+ case 65: {
+ int i = 0;
+ int n = 0;
+ while (i <= GameHeader.NumItems) {
+ if (Items[i].Location == GameHeader.TreasureRoom &&
+ *Items[i].Text == '*')
+ n++;
+ i++;
+ }
+ if (Options & YOUARE)
+ output("You have stored ");
+ else
+ output("I've stored ");
+ outputNumber(n);
+ output(" treasures. On a scale of 0 to 100, that rates ");
+ outputNumber((n * 100) / GameHeader.Treasures);
+ output(".\n");
+ if (n == GameHeader.Treasures) {
+ output("Well done.\n");
+ goto doneit;
+ }
+ break;
+ }
+ case 66: {
+ int i = 0;
+ int f = 0;
+ if (Options & YOUARE)
+ output("You are carrying:\n");
+ else
+ output("I'm carrying:\n");
+ while (i <= GameHeader.NumItems) {
+ if (Items[i].Location == CARRIED) {
+ if (f == 1) {
+ if (Options & TRS80_STYLE)
+ output(". ");
+ else
+ output(" - ");
+ }
+ f = 1;
+ output(Items[i].Text);
+ }
+ i++;
+ }
+ if (f == 0)
+ output("Nothing");
+ output(".\n");
+ break;
+ }
+ case 67:
+ BitFlags |= (1 << 0);
+ break;
+ case 68:
+ BitFlags &= ~(1 << 0);
+ break;
+ case 69:
+ GameHeader.LightTime = LightRefill;
+ Items[LIGHT_SOURCE].Location = CARRIED;
+ BitFlags &= ~(1 << LIGHTOUTBIT);
+ break;
+ case 70:
+ clearScreen(); // pdd.
+ break;
+ case 71:
+ saveGame();
+ break;
+ case 72: {
+ int i1 = param[pptr++];
+ int i2 = param[pptr++];
+ int t = Items[i1].Location;
+ Items[i1].Location = Items[i2].Location;
+ Items[i2].Location = t;
+ break;
+ }
+ case 73:
+ continuation = 1;
+ break;
+ case 74:
+ Items[param[pptr++]].Location = CARRIED;
+ break;
+ case 75: {
+ int i1, i2;
+ i1 = param[pptr++];
+ i2 = param[pptr++];
+ Items[i1].Location = Items[i2].Location;
+ break;
+ }
+ case 76:
+ // Looking at adventure ..
+ break;
+ case 77:
+ if (CurrentCounter >= 0)
+ CurrentCounter--;
+ break;
+ case 78:
+ outputNumber(CurrentCounter);
+ break;
+ case 79:
+ CurrentCounter = param[pptr++];
+ break;
+ case 80: {
+ int t = MyLoc;
+ MyLoc = SavedRoom;
+ SavedRoom = t;
+ break;
+ }
+ case 81: {
+ // This is somewhat guessed. Claymorgue always seems to do
+ // select counter n, thing, select counter n, but uses one value that always
+ // seems to exist. Trying a few options I found this gave sane results on ageing
+ int t = param[pptr++];
+ int c1 = CurrentCounter;
+ CurrentCounter = Counters[t];
+ Counters[t] = c1;
+ break;
+ }
+ case 82:
+ CurrentCounter += param[pptr++];
+ break;
+ case 83:
+ CurrentCounter -= param[pptr++];
+ if (CurrentCounter < -1)
+ CurrentCounter = -1;
+ // Note: This seems to be needed. I don't yet know if there
+ // is a maximum value to limit too
+ break;
+ case 84:
+ output(NounText);
+ break;
+ case 85:
+ output(NounText);
+ output("\n");
+ break;
+ case 86:
+ output("\n");
+ break;
+ case 87: {
+ // Changed this to swap location<->roomflag[x] not roomflag 0 and x
+ int p = param[pptr++];
+ int sr = MyLoc;
+ MyLoc = RoomSaved[p];
+ RoomSaved[p] = sr;
+ break;
+ }
+ case 88:
+ delay(2);
+ break;
+ case 89:
+ pptr++;
+ // SAGA draw picture n
+ // Spectrum Seas of Blood - start combat ?
+ // Poking this into older spectrum games causes a crash
+ break;
+ default:
+ error("Unknown action %d [Param begins %d %d]\n",
+ act[cc], param[pptr], param[pptr + 1]);
+ break;
+ }
+ }
+
+ cc++;
+ }
+
+ return 1 + continuation;
+}
+
+int Scott::performActions(int vb, int no) {
+ static int disable_sysfunc = 0; // Recursion lock
+ int d = BitFlags & (1 << DARKBIT);
+
+ int ct = 0;
+ int fl;
+ int doagain = 0;
+ if (vb == 1 && no == -1) {
+ output("Give me a direction too.");
+ return 0;
+ }
+ if (vb == 1 && no >= 1 && no <= 6) {
+ int nl;
+ if (Items[LIGHT_SOURCE].Location == MyLoc ||
+ Items[LIGHT_SOURCE].Location == CARRIED)
+ d = 0;
+ if (d)
+ output("Dangerous to move in the dark! ");
+ nl = Rooms[MyLoc].Exits[no - 1];
+ if (nl != 0) {
+ MyLoc = nl;
+ return 0;
+ }
+ if (d) {
+ if (Options & YOUARE)
+ output("You fell down and broke your neck. ");
+ else
+ output("I fell down and broke my neck. ");
+ glk_exit();
+ }
+ if (Options & YOUARE)
+ output("You can't go in that direction. ");
+ else
+ output("I can't go in that direction. ");
+ return 0;
+ }
+
+ fl = -1;
+ while (ct <= GameHeader.NumActions) {
+ int vv, nv;
+ vv = Actions[ct].Vocab;
+ // Think this is now right. If a line we run has an action73
+ // run all following lines with vocab of 0,0
+ if (vb != 0 && (doagain && vv != 0))
+ break;
+ // Oops.. added this minor cockup fix 1.11
+ if (vb != 0 && !doagain && fl == 0)
+ break;
+ nv = vv % 150;
+ vv /= 150;
+ if ((vv == vb) || (doagain && Actions[ct].Vocab == 0)) {
+ if ((vv == 0 && randomPercent(nv)) || doagain ||
+ (vv != 0 && (nv == no || nv == 0))) {
+ int f2;
+ if (fl == -1)
+ fl = -2;
+ if ((f2 = performLine(ct)) > 0) {
+ // ahah finally figured it out !
+ fl = 0;
+ if (f2 == 2)
+ doagain = 1;
+ if (vb != 0 && doagain == 0)
+ return 0;
+ }
+ }
+ }
+ ct++;
+
+ // Previously this did not check ct against GameHeader.NumActions and would read
+ // past the end of Actions. I don't know what should happen on the last action,
+ // but doing nothing is better than reading one past the end.
+ // --Chris
+ if (ct <= GameHeader.NumActions && Actions[ct].Vocab != 0)
+ doagain = 0;
+ }
+ if (fl != 0 && disable_sysfunc == 0) {
+ int item;
+ if (Items[LIGHT_SOURCE].Location == MyLoc ||
+ Items[LIGHT_SOURCE].Location == CARRIED)
+ d = 0;
+ if (vb == 10 || vb == 18) {
+ // Yes they really _are_ hardcoded values
+ if (vb == 10) {
+ if (xstrcasecmp(NounText, "ALL") == 0) {
+ int i = 0;
+ int f = 0;
+
+ if (d) {
+ output("It is dark.\n");
+ return 0;
+ }
+ while (i <= GameHeader.NumItems) {
+ if (Items[i].Location == MyLoc && Items[i].AutoGet != nullptr && Items[i].AutoGet[0] != '*') {
+ no = whichWord(Items[i].AutoGet, Nouns);
+ disable_sysfunc = 1; // Don't recurse into auto get !
+ performActions(vb, no); // Recursively check each items table code
+ disable_sysfunc = 0;
+ if (countCarried() == GameHeader.MaxCarry) {
+ if (Options & YOUARE)
+ output("You are carrying too much. ");
+ else
+ output("I've too much to carry. ");
+ return 0;
+ }
+ Items[i].Location = CARRIED;
+ output(Items[i].Text);
+ output(": O.K.\n");
+ f = 1;
+ }
+ i++;
+ }
+ if (f == 0)
+ output("Nothing taken.");
+ return 0;
+ }
+ if (no == -1) {
+ output("What ? ");
+ return 0;
+ }
+ if (countCarried() == GameHeader.MaxCarry) {
+ if (Options & YOUARE)
+ output("You are carrying too much. ");
+ else
+ output("I've too much to carry. ");
+ return 0;
+ }
+ item = matchUpItem(NounText, MyLoc);
+ if (item == -1) {
+ if (Options & YOUARE)
+ output("It is beyond your power to do that. ");
+ else
+ output("It's beyond my power to do that. ");
+ return 0;
+ }
+ Items[item].Location = CARRIED;
+ output("O.K. ");
+ return 0;
+ }
+ if (vb == 18) {
+ if (xstrcasecmp(NounText, "ALL") == 0) {
+ int i = 0;
+ int f = 0;
+ while (i <= GameHeader.NumItems) {
+ if (Items[i].Location == CARRIED && Items[i].AutoGet && Items[i].AutoGet[0] != '*') {
+ no = whichWord(Items[i].AutoGet, Nouns);
+ disable_sysfunc = 1;
+ performActions(vb, no);
+ disable_sysfunc = 0;
+ Items[i].Location = MyLoc;
+ output(Items[i].Text);
+ output(": O.K.\n");
+ f = 1;
+ }
+ i++;
+ }
+ if (f == 0)
+ output("Nothing dropped.\n");
+ return 0;
+ }
+ if (no == -1) {
+ output("What ? ");
+ return 0;
+ }
+ item = matchUpItem(NounText, CARRIED);
+ if (item == -1) {
+ if (Options & YOUARE)
+ output("It's beyond your power to do that.\n");
+ else
+ output("It's beyond my power to do that.\n");
+ return 0;
+ }
+ Items[item].Location = MyLoc;
+ output("O.K. ");
+ return 0;
+ }
+ }
+ }
+
+ return fl;
+}
+
+int Scott::xstrcasecmp(const char *s1, const char *s2) {
+ const unsigned char
+ *us1 = (const unsigned char *)s1,
+ *us2 = (const unsigned char *)s2;
+
+ while (tolower(*us1) == tolower(*us2++))
+ if (*us1++ == '\0')
+ return (0);
+ return (tolower(*us1) - tolower(*--us2));
+}
+
+int Scott::xstrncasecmp(const char *s1, const char *s2, size_t n) {
+ if (n != 0) {
+ const unsigned char
+ *us1 = (const unsigned char *)s1,
+ *us2 = (const unsigned char *)s2;
+
+ do {
+ if (tolower(*us1) != tolower(*us2++))
+ return (tolower(*us1) - tolower(*--us2));
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+
+ return 0;
+}
+
+void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
+ va_list va;
+ va_start(va, count);
+ unsigned char c = f->readByte();
+
+ for (size_t idx = 0; idx < count; ++idx) {
+ while (f->pos() < f->size() && Common::isSpace(c))
+ c = f->readByte();
+
+ // Get the next value
+ int *val = va_arg(va, int *);
+ *val = 0;
+
+ int factor = c == '-' ? -1 : 1;
+ if (factor == -1)
+ c = f->readByte();
+
+ while (Common::isDigit(c)) {
+ *val = (*val * 10) + (c - '0');
+ c = f->readByte();
+ }
+
+ *val *= factor; // Handle negatives
+ }
+
+ va_end(va);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
new file mode 100644
index 0000000000..ec6a8601ba
--- /dev/null
+++ b/engines/glk/scott/scott.h
@@ -0,0 +1,191 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_SCOTT
+#define GLK_SCOTT
+
+/*
+ * Controlling block
+ */
+
+#include "common/scummsys.h"
+#include "glk/glk.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+#define LIGHT_SOURCE 9 // Always 9 how odd
+#define CARRIED 255 // Carried
+#define DESTROYED 0 // Destroyed
+#define DARKBIT 15
+#define LIGHTOUTBIT 16 // Light gone out
+
+enum GameOption {
+ YOUARE = 1, ///< You are not I am
+ SCOTTLIGHT = 2, ///< Authentic Scott Adams light messages
+ DEBUGGING = 4, ///< Info from database load
+ TRS80_STYLE = 8, ///< Display in style used on TRS-80
+ PREHISTORIC_LAMP = 16 ///< Destroy the lamp (very old databases)
+};
+
+#define TRS80_LINE "\n<------------------------------------------------------------>\n"
+#define MyLoc (GameHeader.PlayerRoom)
+
+struct Header {
+ int Unknown;
+ int NumItems;
+ int NumActions;
+ int NumWords; ///< Smaller of verb/noun is padded to same size
+ int NumRooms;
+ int MaxCarry;
+ int PlayerRoom;
+ int Treasures;
+ int WordLength;
+ int LightTime;
+ int NumMessages;
+ int TreasureRoom;
+
+ Header() : Unknown(0), NumItems(0), NumActions(0), NumWords(0), NumRooms(0),
+ MaxCarry(0), PlayerRoom(0), Treasures(0), WordLength(0), LightTime(0),
+ NumMessages(0), TreasureRoom(0) {}
+};
+
+struct Action {
+ uint Vocab;
+ uint Condition[5];
+ uint action[2];
+
+ Action() : Vocab(0) {
+ Common::fill(&Condition[0], &Condition[5], 0);
+ Common::fill(&action[0], &action[2], 0);
+ }
+};
+
+struct Room {
+ char *Text;
+ short Exits[6];
+
+ Room() : Text(0) {
+ Common::fill(&Exits[0], &Exits[6], 0);
+ }
+};
+
+struct Item {
+ char *Text; // PORTABILITY WARNING: THESE TWO MUST BE 8 BIT VALUES.
+ byte Location;
+ byte InitialLoc;
+ char *AutoGet;
+
+ Item() : Text(nullptr), Location(0), InitialLoc(0), AutoGet(nullptr) {}
+};
+
+struct Tail {
+ int Version;
+ int AdventureNumber;
+ int Unknown;
+
+ Tail() : Version(0), AdventureNumber(0), Unknown(0) {}
+};
+
+/**
+ * Scott Adams game interpreter
+ */
+class Scott : public Glk {
+private:
+ Header GameHeader;
+ Item *Items;
+ Room *Rooms;
+ const char **Verbs;
+ const char **Nouns;
+ const char **Messages;
+ Action *Actions;
+ int LightRefill;
+ char NounText[16];
+ int Counters[16]; ///< Range unknown
+ int CurrentCounter;
+ int SavedRoom;
+ int RoomSaved[16]; ///< Range unknown
+ int Options; ///< Option flags set
+ int Width; ///< Terminal width
+ int TopHeight; ///< Height of top window
+
+ bool split_screen;
+ winid_t Bottom, Top;
+ uint32 BitFlags; ///< Might be >32 flags - I haven't seen >32 yet
+ int _saveSlot; ///< Save slot when loading savegame from launcher
+private:
+ /**
+ * Initialization code
+ */
+ void initialize();
+
+ void display(winid_t w, const char *fmt, ...);
+ void delay(int seconds);
+ void fatal(const char *x);
+ void clearScreen(void);
+ void *memAlloc(int size);
+ bool randomPercent(uint n);
+ int countCarried(void);
+ const char *mapSynonym(const char *word);
+ int matchUpItem(const char *text, int loc);
+ char *readString(Common::SeekableReadStream *f);
+ void loadDatabase(Common::SeekableReadStream *f, bool loud);
+ void output(const char *a);
+ void outputNumber(int a);
+ void look(void);
+ int whichWord(const char *word, const char **list);
+ void lineInput(char *buf, size_t n);
+ void saveGame(void);
+ void loadGame(void);
+ int getInput(int *vb, int *no);
+ int performLine(int ct);
+ int performActions(int vb, int no);
+
+ int xstrcasecmp(const char *, const char *);
+ int xstrncasecmp(const char *, const char *, size_t);
+ void readInts(Common::SeekableReadStream *f, size_t count, ...);
+public:
+ /**
+ * Constructor
+ */
+ Scott(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+ /**
+ * Execute the game
+ */
+ virtual void runGame(Common::SeekableReadStream *gameFile) override;
+
+ /**
+ * Load a savegame
+ */
+ virtual Common::Error loadGameState(int slot) override;
+
+ /**
+ * Save the game
+ */
+ virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
+};
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/screen.cpp b/engines/glk/screen.cpp
new file mode 100644
index 0000000000..ff46f1b57b
--- /dev/null
+++ b/engines/glk/screen.cpp
@@ -0,0 +1,72 @@
+/* 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/screen.h"
+#include "glk/conf.h"
+
+namespace Gargoyle {
+
+void Screen::fill(const byte *rgb) {
+ uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+ clear(color);
+}
+
+void Screen::fillRect(const Rect &box, const byte *rgb) {
+ uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+ Graphics::Screen::fillRect(box, color);
+}
+
+void Screen::drawCaret(const Point &pos) {
+ const byte *rgb = g_conf->_caretColor;
+ uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+ int x = pos.x / GLI_SUBPIX, y = pos.y;
+
+ switch (g_conf->_caretShape) {
+ case SMALL_DOT:
+ hLine(x + 0, y + 1, x + 0, color);
+ hLine(x - 1, y + 2, x + 1, color);
+ hLine(x - 2, y + 3, x + 2, color);
+ break;
+
+ case FAT_DOT:
+ hLine(x + 0, y + 1, x + 0, color);
+ hLine(x - 1, y + 2, x + 1, color);
+ hLine(x - 2, y + 3, x + 2, color);
+ hLine(x - 3, y + 4, x + 3, color);
+ break;
+
+ case THIN_LINE:
+ vLine(x, y - g_conf->_baseLine + 1, y - 1, color);
+ break;
+
+ case FAT_LINE:
+ Graphics::Screen::fillRect(Rect(x, y - g_conf->_baseLine + 1, x + 1, y - 1), color);
+ break;
+
+ default:
+ // BLOCK
+ Graphics::Screen::fillRect(Rect(x, y - g_conf->_baseLine + 1, x + g_conf->_cellW, y - 1), color);
+ break;
+ }
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/screen.h b/engines/glk/screen.h
new file mode 100644
index 0000000000..e81cf9faa4
--- /dev/null
+++ b/engines/glk/screen.h
@@ -0,0 +1,65 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_DRAW_H
+#define GLK_DRAW_H
+
+#include "graphics/screen.h"
+#include "glk/fonts.h"
+
+namespace Gargoyle {
+
+enum CaretShape {
+ SMALL_DOT = 0, FAT_DOT = 1, THIN_LINE = 2, FAT_LINE = 3, BLOCK = 4
+};
+
+/**
+ * Screen surface class
+ */
+class Screen : public Graphics::Screen, public Fonts {
+public:
+ /**
+ * Constructor
+ */
+ Screen() : Graphics::Screen(), Fonts(this) {}
+
+ /**
+ * Fills the screen with a given rgb color
+ */
+ void fill(const byte *rgb);
+
+ /**
+ * Fill a given area of the screen with an rgb color
+ */
+ void fillRect(const Rect &box, const byte *rgb);
+
+ /**
+ * Draws the text input caret at the given position
+ * @remarks The position specifies the caret's bottom-left corner,
+ * and the X position is in multiples of GLI_SUBPIX
+ */
+ void drawCaret(const Point &pos);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/selection.cpp b/engines/glk/selection.cpp
new file mode 100644
index 0000000000..f1c5bb86dc
--- /dev/null
+++ b/engines/glk/selection.cpp
@@ -0,0 +1,321 @@
+/* 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/selection.h"
+#include "glk/conf.h"
+#include "glk/gargoyle.h"
+#include "glk/windows.h"
+#include "common/system.h"
+
+namespace Gargoyle {
+
+void Clipboard::clipboardStore(const Common::U32String &text) {
+ _text = text;
+}
+
+void Clipboard::clipboardSend(ClipSource source) {
+ if (g_system->hasFeature(OSystem::kFeatureClipboardSupport)) {
+ // Convert unicode string to standard string, since that's all ScummVM supports
+ Common::String text;
+ for (uint idx = 0; idx < _text.size(); ++idx)
+ text += (_text[idx] <= 0x7f) ? (char)_text[idx] : '?';
+
+ g_system->setTextInClipboard(text);
+ }
+}
+
+void Clipboard::clipboardReceive(ClipSource source) {
+ if (g_system->hasFeature(OSystem::kFeatureClipboardSupport)) {
+ Windows &windows = *g_vm->_windows;
+
+ if (g_system->hasTextInClipboard()) {
+ Common::String text = g_system->getTextFromClipboard();
+ for (uint idx = 0; idx < text.size(); ++idx) {
+ uint c = text[idx];
+ if (c != '\r' && c != '\n' && c != '\b' && c != '\t')
+ windows.inputHandleKey(c);
+ }
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+WindowMask::WindowMask() : _hor(0), _ver(0), _links(nullptr) {
+ _last.x = _last.y = 0;
+ resize(g_system->getWidth(), g_system->getHeight());
+}
+
+void WindowMask::resize(size_t x, size_t y) {
+ // Deallocate old storage
+ for (size_t i = 0; i < _hor; i++) {
+ if (_links[i])
+ delete _links[i];
+ }
+
+ delete _links;
+
+ _hor = x + 1;
+ _ver = y + 1;
+
+ // allocate new storage
+ _links = new glui32 *[_hor];
+ if (!_links) {
+ warning("resize_mask: out of memory");
+ _hor = _ver = 0;
+ return;
+ }
+
+ for (size_t i = 0; i < _hor; i++) {
+ _links[i] = new glui32[_ver];
+ if (!_links[i]) {
+ warning("resize_mask: could not allocate new memory");
+ return;
+ }
+ }
+
+ _select.left = 0;
+ _select.top = 0;
+ _select.right = 0;
+ _select.bottom = 0;
+}
+
+void WindowMask::putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1) {
+ uint i, k;
+ size_t tx0 = x0 < x1 ? x0 : x1;
+ size_t tx1 = x0 < x1 ? x1 : x0;
+ size_t ty0 = y0 < y1 ? y0 : y1;
+ size_t ty1 = y0 < y1 ? y1 : y0;
+
+ if (!_hor || !_ver) {
+ warning("putHyperlink: struct not initialized");
+ return;
+ }
+
+ if (tx0 >= _hor
+ || tx1 >= _hor
+ || ty0 >= _ver || ty1 >= _ver
+ || !_links[tx0] || !_links[tx1]) {
+ warning("putHyperlink: invalid range given");
+ return;
+ }
+
+ for (i = tx0; i < tx1; i++) {
+ for (k = ty0; k < ty1; k++)
+ _links[i][k] = linkval;
+ }
+}
+
+glui32 WindowMask::getHyperlink(const Point &pos) const {
+ if (!_hor || !_ver) {
+ warning("getHyperlink: struct not initialized");
+ return 0;
+ }
+
+ if (pos.x >= (int16)_hor || pos.y >= (int16)_ver || !_links[pos.x]) {
+ warning("getHyperlink: invalid range given");
+ return 0;
+ }
+
+ return _links[pos.x][pos.y];
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Selection::startSelection(const Point &pos) {
+ int tx, ty;
+
+ if (!_hor || !_ver) {
+ warning("startSelection: mask not initialized");
+ return;
+ }
+
+ tx = MIN(pos.x, (int16)_hor);
+ ty = MIN(pos.y, (int16)_ver);
+
+ _select.left = _last.x = tx;
+ _select.top = _last.y = ty;
+ _select.right = 0;
+ _select.bottom = 0;
+
+ g_vm->_windows->selectionChanged();
+}
+
+void Selection::moveSelection(const Point &pos) {
+ int tx, ty;
+
+ if (ABS(pos.x - _last.x) < 5 && ABS(pos.y - _last.y) < 5)
+ return;
+
+ if (!_hor || !_ver) {
+ warning("moveSelection: mask not initialized");
+ return;
+ }
+
+ tx = MIN(pos.x, (int16)_hor);
+ ty = MIN(pos.y, (int16)_ver);
+
+ _select.right = _last.x = tx;
+ _select.bottom = _last.y = ty;
+
+ g_vm->_windows->selectionChanged();
+}
+
+void Selection::clearSelection() {
+ if (!_select.isEmpty())
+ Windows::_forceRedraw = true;
+
+ _select = Rect();
+ g_vm->_windows->clearClaimSelect();
+}
+
+bool Selection::checkSelection(const Rect &r) const {
+ Rect select(MIN(_select.left, _select.right), MAX(_select.left, _select.right),
+ MIN(_select.top, _select.bottom), MAX(_select.top, _select.bottom));
+ if (select.isEmpty())
+ return false;
+
+ return select.intersects(r);
+}
+
+bool Selection::getSelection(const Rect &r, int *rx0, int *rx1) const {
+ uint row, upper, lower, above, below;
+ bool row_selected, found_left, found_right;
+ int from_right, from_below, is_above, is_below;
+ uint cx0, cx1, cy0, cy1;
+ uint x0 = r.left, y0 = r.top, x1 = r.right, y1 = r.bottom;
+
+ row = (y0 + y1) / 2;
+ upper = row - (row - y0) / 2;
+ lower = row + (y1 - row) / 2;
+ above = upper - (g_conf->_leading) / 2;
+ below = lower + (g_conf->_leading) / 2;
+
+ cx0 = MIN(_select.left, _select.right);
+ cx1 = MAX(_select.left, _select.right);
+ cy0 = MIN(_select.top, _select.bottom);
+ cy1 = MAX(_select.top, _select.bottom);
+
+ row_selected = false;
+
+ if ((cy0 >= upper && cy0 <= lower)
+ || (cy1 >= upper && cy1 <= lower))
+ row_selected = true;
+
+ if (row >= cy0 && row <= cy1)
+ row_selected = true;
+
+ if (!row_selected)
+ return false;
+
+ from_right = (_select.left != (int16)cx0);
+ from_below = (_select.top != (int16)cy0);
+ is_above = (above >= cy0 && above <= cy1);
+ is_below = (below >= cy0 && below <= cy1);
+
+ *rx0 = 0;
+ *rx1 = 0;
+
+ found_left = false;
+ found_right = false;
+
+ if (is_above && is_below) {
+ *rx0 = x0;
+ *rx1 = x1;
+ found_left = true;
+ found_right = true;
+ } else if (!is_above && is_below) {
+ if (from_below) {
+ if (from_right) {
+ *rx0 = cx0;
+ *rx1 = x1;
+ found_left = true;
+ found_right = true;
+ } else {
+ *rx0 = cx1;
+ *rx1 = x1;
+ found_left = true;
+ found_right = true;
+ }
+ } else {
+ if (from_right) {
+ *rx0 = cx1;
+ *rx1 = x1;
+ found_left = true;
+ found_right = true;
+ } else {
+ *rx1 = x1;
+ found_right = true;
+ }
+ }
+ } else if (is_above && !is_below) {
+ if (from_below) {
+ if (from_right) {
+ *rx0 = x0;
+ *rx1 = cx1;
+ found_left = true;
+ found_right = true;
+ } else {
+ *rx0 = x0;
+ *rx1 = cx0;
+ found_left = true;
+ found_right = true;
+ }
+ } else {
+ if (from_right) {
+ if (x0 > cx0)
+ return false;
+ *rx0 = x0;
+ *rx1 = cx0;
+ found_left = true;
+ found_right = true;
+ } else {
+ *rx0 = x0;
+ found_left = true;
+ }
+ }
+ }
+
+ if (found_left && found_right)
+ return true;
+
+ for (uint i = x0; i <= x1; i++) {
+ if (i >= cx0 && i <= cx1) {
+ if (!found_left) {
+ *rx0 = i;
+ found_left = true;
+ if (found_right)
+ return true;
+ } else {
+ if (!found_right)
+ *rx1 = i;
+ }
+ }
+ }
+
+ if (rx0 && !rx1)
+ *rx1 = x1;
+
+ return (rx0 && rx1);
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/selection.h b/engines/glk/selection.h
new file mode 100644
index 0000000000..aa44a1d115
--- /dev/null
+++ b/engines/glk/selection.h
@@ -0,0 +1,120 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_SELECTION_H
+#define GLK_SELECTION_H
+
+#include "glk/glk_types.h"
+#include "glk/utils.h"
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/ustr.h"
+
+namespace Gargoyle {
+
+enum ClipSource { PRIMARY = 0, CLIPBOARD = 1 };
+
+class Window;
+
+/**
+ * Acts as interface to and from the system's clipboard storage
+ */
+class Clipboard {
+private:
+ Common::U32String _text;
+public:
+ /**
+ * Makes a copy of selected text in preparation for the user copying it
+ * to the clpboard
+ */
+ void clipboardStore(const Common::U32String &text);
+
+ /**
+ * Send previously designated text to the clipboard
+ */
+ void clipboardSend(ClipSource source);
+
+ /**
+ * Receive text from the clipboard, and paste it into the current window
+ */
+ void clipboardReceive(ClipSource source);
+};
+
+/**
+ * Manages hyperlinks for the screen
+ */
+class WindowMask {
+public:
+ size_t _hor, _ver;
+ glui32 **_links;
+ Rect _select;
+ Point _last;
+public:
+ /**
+ * Constructor
+ */
+ WindowMask();
+
+ /**
+ * Resize the links array
+ */
+ void resize(size_t x, size_t y);
+
+ void putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1);
+
+ glui32 getHyperlink(const Point &pos) const;
+};
+
+/**
+ * Overall manager for selecting areas on the screen, copying to/from the clipboard,
+ * and managing hyperlinks
+ */
+class Selection : public Clipboard, public WindowMask {
+public:
+ /**
+ * Start selecting an area of the screen
+ * @param pos Position to start selection area at
+ */
+ void startSelection(const Point &pos);
+
+ /**
+ * Move the end point of the selection area
+ * @param pos Position to end selection area at
+ */
+ void moveSelection(const Point &pos);
+
+ /**
+ * Remove any previously selected area
+ */
+ void clearSelection();
+
+ /**
+ * Checks whether the passed area intersects the selection area
+ */
+ bool checkSelection(const Rect &r) const;
+
+ bool getSelection(const Rect &r, int *rx0, int *rx1) const;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/speech.h b/engines/glk/speech.h
new file mode 100644
index 0000000000..5f9255565a
--- /dev/null
+++ b/engines/glk/speech.h
@@ -0,0 +1,49 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_SPEECH_H
+#define GLK_SPEECH_H
+
+#include "common/events.h"
+#include "glk/glk_types.h"
+
+namespace Gargoyle {
+
+/**
+ * Currently not implemented
+ */
+class Speech {
+protected:
+ void gli_initialize_tts(void) {}
+
+ void gli_tts_flush(void) {}
+
+ void gli_tts_purge(void) {}
+
+ void gli_tts_speak(const glui32 *buf, size_t len) {}
+
+ void gli_free_tts(void) {}
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp
new file mode 100644
index 0000000000..d53f5d72a1
--- /dev/null
+++ b/engines/glk/streams.cpp
@@ -0,0 +1,1592 @@
+/* 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/streams.h"
+#include "glk/conf.h"
+#include "glk/events.h"
+#include "glk/gargoyle.h"
+#include "glk/windows.h"
+#include "gui/saveload.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/translation.h"
+
+namespace Gargoyle {
+
+Stream::Stream(Streams *streams, bool readable, bool writable, uint32 rock, bool unicode) :
+ _streams(streams), _readable(readable), _writable(writable), _readCount(0),
+ _writeCount(0), _prev(nullptr), _next(nullptr), _rock(0) {
+}
+
+Stream::~Stream() {
+ _streams->removeStream(this);
+}
+
+Stream *Stream::getNext(uint32 *rock) const {
+ Stream *stream = _next;
+ if (rock)
+ *rock = stream ? stream->_rock : 0;
+ return stream;
+}
+
+void Stream::fillResult(StreamResult *result) {
+ if (result) {
+ result->_readCount = _readCount;
+ result->_writeCount = _writeCount;
+ }
+}
+
+void Stream::close(StreamResult *result) {
+ // Get the read/write totals
+ fillResult(result);
+
+ // Remove the stream
+ delete this;
+}
+
+void Stream::setZColors(glui32 fg, glui32 bg) {
+ if (_writable && g_conf->_styleHint)
+ Windows::_forceRedraw = true;
+}
+
+void Stream::setReverseVideo(bool reverse) {
+ if (_writable && g_conf->_styleHint)
+ Windows::_forceRedraw = true;
+}
+
+/*--------------------------------------------------------------------------*/
+
+void WindowStream::close(StreamResult *result) {
+ warning("cannot close window stream");
+}
+
+void WindowStream::putChar(unsigned char ch) {
+ if (!_writable)
+ return;
+ ++_writeCount;
+
+ if (_window->_lineRequest || _window->_lineRequestUni) {
+ if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+ _window->cancelLineEvent(nullptr);
+ g_vm->_events->_forceClick = false;
+ } else {
+ warning("putChar: window has pending line request");
+ }
+ }
+
+ _window->putCharUni(ch);
+ if (_window->_echoStream)
+ _window->_echoStream->putChar(ch);
+}
+
+void WindowStream::putCharUni(uint32 ch) {
+ if (!_writable)
+ return;
+ ++_writeCount;
+
+ if (_window->_lineRequest || _window->_lineRequestUni) {
+ if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+ _window->cancelLineEvent(nullptr);
+ g_vm->_events->_forceClick = false;
+ } else {
+ warning("putCharUni: window has pending line request");
+ }
+ }
+
+ _window->putCharUni(ch);
+ if (_window->_echoStream)
+ _window->_echoStream->putCharUni(ch);
+}
+
+void WindowStream::putBuffer(const char *buf, size_t len) {
+ if (!_writable)
+ return;
+ _writeCount += len;
+
+ if (_window->_lineRequest || _window->_lineRequestUni) {
+ if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+ _window->cancelLineEvent(nullptr);
+ g_vm->_events->_forceClick = false;
+ } else {
+ warning("putBuffer: window has pending line request");
+ }
+ }
+
+ for (size_t lx = 0; lx < len; lx++, buf++)
+ _window->putCharUni(*buf);
+ if (_window->_echoStream)
+ _window->_echoStream->putBuffer(buf, len);
+}
+
+void WindowStream::putBufferUni(const uint32 *buf, size_t len) {
+ if (!_writable)
+ return;
+ _writeCount += len;
+
+ if (_window->_lineRequest || _window->_lineRequestUni) {
+ if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+ _window->cancelLineEvent(nullptr);
+ g_vm->_events->_forceClick = false;
+ } else {
+ warning("putBuffer: window has pending line request");
+ }
+ }
+
+ for (size_t lx = 0; lx < len; lx++, buf++)
+ _window->putCharUni(*buf);
+ if (_window->_echoStream)
+ _window->_echoStream->putBufferUni(buf, len);
+}
+
+void WindowStream::unputBuffer(const char *buf, size_t len) {
+ glui32 lx;
+ const char *cx;
+
+ if (!_writable)
+ return;
+
+ if (_window->_lineRequest || _window->_lineRequestUni) {
+ if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+ _window->cancelLineEvent(nullptr);
+ g_vm->_events->_forceClick = false;
+ } else {
+ warning("unput_buffer: window has pending line request");
+ return;
+ }
+ }
+
+ for (lx = 0, cx = buf + len - 1; lx < len; lx++, cx--) {
+ if (!_window->unputCharUni(*cx))
+ break;
+ _writeCount--;
+ }
+ if (_window->_echoStream)
+ _window->_echoStream->unputBuffer(buf, len);
+}
+
+void WindowStream::unputBufferUni(const glui32 *buf, size_t len) {
+ glui32 lx;
+ const glui32 *cx;
+
+ if (!_writable)
+ return;
+
+ if (_window->_lineRequest || _window->_lineRequestUni) {
+ if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+ _window->cancelLineEvent(nullptr);
+ g_vm->_events->_forceClick = false;
+ } else {
+ warning("unput_buffer: window has pending line request");
+ return;
+ }
+ }
+
+ for (lx = 0, cx = buf + len - 1; lx < len; lx++, cx--) {
+ if (!_window->unputCharUni(*cx))
+ break;
+ _writeCount--;
+ }
+
+ if (_window->_echoStream)
+ _window->_echoStream->unputBufferUni(buf, len);
+}
+
+void WindowStream::setStyle(glui32 val) {
+ if (!_writable)
+ return;
+
+ if (val >= style_NUMSTYLES)
+ val = 0;
+
+ _window->_attr.style = val;
+ if (_window->_echoStream)
+ _window->_echoStream->setStyle(val);
+}
+
+void WindowStream::setHyperlink(glui32 linkVal) {
+ if (_writable)
+ _window->_attr.hyper = linkVal;
+}
+
+void WindowStream::setZColors(glui32 fg, glui32 bg) {
+ if (!_writable || !g_conf->_styleHint)
+ return;
+
+ byte fore[3], back[3];
+ fore[0] = (fg >> 16) & 0xff;
+ fore[1] = (fg >> 8) & 0xff;
+ fore[2] = (fg) & 0xff;
+ back[0] = (bg >> 16) & 0xff;
+ back[1] = (bg >> 8) & 0xff;
+ back[2] = (bg) & 0xff;
+
+ if (fg != zcolor_Transparent && fg != zcolor_Cursor) {
+ if (fg == zcolor_Default) {
+ _window->_attr.fgset = 0;
+ _window->_attr.fgcolor = 0;
+ Windows::_overrideFgSet = false;
+ Windows::_overrideFgVal = 0;
+
+ Common::copy(g_conf->_moreSave, g_conf->_moreSave + 3, g_conf->_moreColor);
+ Common::copy(g_conf->_caretSave, g_conf->_caretSave + 3, g_conf->_caretColor);
+ Common::copy(g_conf->_linkSave, g_conf->_linkSave + 3, g_conf->_linkColor);
+ } else if (fg != zcolor_Current) {
+ _window->_attr.fgset = 1;
+ _window->_attr.fgcolor = fg;
+ Windows::_overrideFgSet = true;
+ Windows::_overrideFgVal = fg;
+
+ Common::copy(fore, fore + 3, g_conf->_moreColor);
+ Common::copy(fore, fore + 3, g_conf->_caretColor);
+ Common::copy(fore, fore + 3, g_conf->_linkColor);
+ }
+ }
+
+ if (bg != zcolor_Transparent && bg != zcolor_Cursor) {
+ if (bg == zcolor_Default) {
+ _window->_attr.bgset = 0;
+ _window->_attr.bgcolor = 0;
+ Windows::_overrideBgSet = false;
+ Windows::_overrideBgVal = 0;
+
+ Common::copy(g_conf->_windowSave, g_conf->_windowSave + 3, g_conf->_windowColor);
+ Common::copy(g_conf->_borderSave, g_conf->_borderSave + 3, g_conf->_borderColor);
+ } else if (bg != zcolor_Current) {
+ _window->_attr.bgset = 1;
+ _window->_attr.bgcolor = bg;
+ Windows::_overrideBgSet = true;
+ Windows::_overrideBgVal = bg;
+
+ Common::copy(back, back + 3, g_conf->_windowColor);
+ Common::copy(back, back + 3, g_conf->_borderColor);
+ }
+ }
+
+ Windows::_overrideReverse = !(fg == zcolor_Default && bg == zcolor_Default);
+ Windows::_forceRedraw = true;
+
+ if (_window->_echoStream)
+ _window->_echoStream->setZColors(fg, bg);
+}
+
+void WindowStream::setReverseVideo(bool reverse) {
+ if (!_writable || !g_conf->_styleHint)
+ return;
+
+ _window->_attr.reverse = reverse;
+ if (_window->_echoStream)
+ _window->_echoStream->setReverseVideo(reverse);
+
+ Windows::_forceRedraw = true;
+}
+
+/*--------------------------------------------------------------------------*/
+
+MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
+ Stream(streams, mode != filemode_Write, mode != filemode_Read, rock, unicode),
+ _buf(buf), _bufLen(buflen), _bufPtr(buf) {
+ assert(_buf && _bufLen);
+ assert(mode == filemode_Read || mode == filemode_Write || mode == filemode_ReadWrite);
+
+ if (unicode)
+ _bufEnd = (uint32 *)buf + buflen;
+ else
+ _bufEnd = (byte *)buf + buflen;
+ _bufEof = mode == filemode_Write ? _buf : _bufEnd;
+}
+
+void MemoryStream::putChar(unsigned char ch) {
+ if (!_writable)
+ return;
+ ++_writeCount;
+
+ if (_bufPtr < _bufEnd) {
+ if (_unicode) {
+ *((glui32 *)_bufPtr) = ch;
+ _bufPtr = ((glui32 *)_bufPtr) + 1;
+ } else {
+ *((unsigned char *)_bufPtr) = ch;
+ _bufPtr = ((unsigned char *)_bufPtr) + 1;
+ }
+
+ if (_bufPtr > _bufEof)
+ _bufEof = _bufPtr;
+ }
+}
+
+void MemoryStream::putCharUni(uint32 ch) {
+ if (!_writable)
+ return;
+ ++_writeCount;
+
+ if (_bufPtr < _bufEnd) {
+ if (_unicode) {
+ *((glui32 *)_bufPtr) = ch;
+ _bufPtr = ((glui32 *)_bufPtr) + 1;
+ } else {
+ *((unsigned char *)_bufPtr) = (unsigned char)ch;
+ _bufPtr = ((unsigned char *)_bufPtr) + 1;
+ }
+ if (_bufPtr > _bufEof)
+ _bufEof = _bufPtr;
+ }
+}
+
+void MemoryStream::putBuffer(const char *buf, size_t len) {
+ size_t lx;
+
+ if (!_writable)
+ return;
+ _writeCount += len;
+
+ if (_bufPtr >= _bufEnd) {
+ len = 0;
+ } else {
+ if (!_unicode) {
+ unsigned char *bp = (unsigned char *)_bufPtr;
+ if (bp + len > (unsigned char *)_bufEnd) {
+ lx = (bp + len) - (unsigned char *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ if (len) {
+ memmove(bp, buf, len);
+ bp += len;
+ if (bp > (unsigned char *)_bufEof)
+ _bufEof = bp;
+ }
+ _bufPtr = bp;
+ } else {
+ glui32 *bp = (glui32 *)_bufPtr;
+ if (bp + len > (glui32 *)_bufEnd) {
+ lx = (bp + len) - (glui32 *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ if (len) {
+ glui32 i;
+ for (i = 0; i < len; i++)
+ bp[i] = buf[i];
+ bp += len;
+ if (bp > (glui32 *)_bufEof)
+ _bufEof = bp;
+ }
+ _bufPtr = bp;
+ }
+ }
+}
+
+void MemoryStream::putBufferUni(const uint32 *buf, size_t len) {
+ size_t lx;
+
+ if (!_writable)
+ return;
+ _writeCount += len;
+
+ if (_bufPtr >= _bufEnd) {
+ len = 0;
+ } else {
+ if (!_unicode) {
+ unsigned char *bp = (unsigned char *)_bufPtr;
+ if (bp + len > (unsigned char *)_bufEnd) {
+ lx = (bp + len) - (unsigned char *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ if (len) {
+ glui32 i;
+ for (i = 0; i < len; i++) {
+ glui32 ch = buf[i];
+ if (ch > 0xff)
+ ch = '?';
+ bp[i] = (unsigned char)ch;
+ }
+ bp += len;
+ if (bp > (unsigned char *)_bufEof)
+ _bufEof = bp;
+ }
+ _bufPtr = bp;
+ } else {
+ glui32 *bp = (glui32 *)_bufPtr;
+ if (bp + len > (glui32 *)_bufEnd) {
+ lx = (bp + len) - (glui32 *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ if (len) {
+ memmove(bp, buf, len * 4);
+ bp += len;
+ if (bp > (glui32 *)_bufEof)
+ _bufEof = bp;
+ }
+ _bufPtr = bp;
+ }
+ }
+}
+
+glui32 MemoryStream::getPosition() const {
+ if (_unicode)
+ return ((glui32 *)_bufPtr - (glui32 *)_buf);
+ else
+ return ((unsigned char *)_bufPtr - (unsigned char *)_buf);
+}
+
+void MemoryStream::setPosition(glsi32 pos, glui32 seekMode) {
+ if (!_unicode) {
+ if (seekMode == seekmode_Current)
+ pos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + pos;
+ else if (seekMode == seekmode_End)
+ pos = ((unsigned char *)_bufEof - (unsigned char *)_buf) + pos;
+ else
+ /* pos = pos */;
+ if (pos < 0)
+ pos = 0;
+ if (pos > ((unsigned char *)_bufEof - (unsigned char *)_buf))
+ pos = ((unsigned char *)_bufEof - (unsigned char *)_buf);
+ _bufPtr = (unsigned char *)_buf + pos;
+ } else {
+ if (seekMode == seekmode_Current)
+ pos = ((glui32 *)_bufPtr - (glui32 *)_buf) + pos;
+ else if (seekMode == seekmode_End)
+ pos = ((glui32 *)_bufEof - (glui32 *)_buf) + pos;
+
+ if (pos < 0)
+ pos = 0;
+ if (pos > ((glui32 *)_bufEof - (glui32 *)_buf))
+ pos = ((glui32 *)_bufEof - (glui32 *)_buf);
+ _bufPtr = (glui32 *)_buf + pos;
+ }
+}
+
+glsi32 MemoryStream::getChar() {
+ if (!_readable)
+ return -1;
+
+ if (_bufPtr < _bufEnd) {
+ if (!_unicode) {
+ unsigned char ch;
+ ch = *((unsigned char *)_bufPtr);
+ _bufPtr = ((unsigned char *)_bufPtr) + 1;
+ _readCount++;
+ return ch;
+ } else {
+ glui32 ch;
+ ch = *((glui32 *)_bufPtr);
+ _bufPtr = ((glui32 *)_bufPtr) + 1;
+ _readCount++;
+ if (ch > 0xff)
+ ch = '?';
+ return ch;
+ }
+ } else {
+ return -1;
+ }
+}
+
+glsi32 MemoryStream::getCharUni() {
+ if (!_readable)
+ return -1;
+
+ if (_bufPtr < _bufEnd) {
+ if (!_unicode) {
+ unsigned char ch;
+ ch = *((unsigned char *)_bufPtr);
+ _bufPtr = ((unsigned char *)_bufPtr) + 1;
+ _readCount++;
+ return ch;
+ } else {
+ glui32 ch;
+ ch = *((glui32 *)_bufPtr);
+ _bufPtr = ((glui32 *)_bufPtr) + 1;
+ _readCount++;
+ return ch;
+ }
+ } else {
+ return -1;
+ }
+}
+
+glui32 MemoryStream::getBuffer(char *buf, glui32 len) {
+ if (!_readable)
+ return 0;
+
+ if (_bufPtr >= _bufEnd) {
+ len = 0;
+ } else {
+ if (!_unicode) {
+ unsigned char *bp = (unsigned char *)_bufPtr;
+ if (bp + len > (unsigned char *)_bufEnd) {
+ glui32 lx;
+ lx = (bp + len) - (unsigned char *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+
+ if (len) {
+ memcpy(buf, bp, len);
+ bp += len;
+ if (bp > (unsigned char *)_bufEof)
+ _bufEof = bp;
+ }
+
+ _readCount += len;
+ _bufPtr = bp;
+ } else {
+ glui32 *bp = (glui32 *)_bufPtr;
+ if (bp + len > (glui32 *)_bufEnd) {
+ glui32 lx;
+ lx = (bp + len) - (glui32 *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ if (len) {
+ glui32 i;
+ for (i = 0; i < len; i++) {
+ glui32 ch = *bp++;
+ if (ch > 0xff)
+ ch = '?';
+ *buf++ = (char)ch;
+ }
+ if (bp > (glui32 *)_bufEof)
+ _bufEof = bp;
+ }
+
+ _readCount += len;
+ _bufPtr = bp;
+ }
+ }
+
+ return len;
+}
+
+glui32 MemoryStream::getBufferUni(glui32 *buf, glui32 len) {
+ if (!_readable)
+ return 0;
+
+ if (_bufPtr >= _bufEnd) {
+ len = 0;
+ } else {
+ if (!_unicode) {
+ unsigned char *bp = (unsigned char *)_bufPtr;
+ if (bp + len > (unsigned char *)_bufEnd) {
+ glui32 lx;
+ lx = (bp + len) - (unsigned char *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ if (len) {
+ glui32 i;
+ for (i = 0; i < len; i++)
+ buf[i] = bp[i];
+ bp += len;
+ if (bp > (unsigned char *)_bufEof)
+ _bufEof = bp;
+ }
+ _readCount += len;
+ _bufPtr = bp;
+ } else {
+ glui32 *bp = (glui32 *)_bufPtr;
+ if (bp + len > (glui32 *)_bufEnd) {
+ glui32 lx;
+ lx = (bp + len) - (glui32 *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ if (len) {
+ memcpy(buf, bp, len * 4);
+ bp += len;
+ if (bp > (glui32 *)_bufEof)
+ _bufEof = bp;
+ }
+ _readCount += len;
+ _bufPtr = bp;
+ }
+ }
+
+ return len;
+}
+
+glui32 MemoryStream::getLine(char *buf, glui32 len) {
+ glui32 lx;
+ bool gotNewline;
+
+ if (len == 0)
+ return 0;
+
+ len -= 1; /* for the terminal null */
+ if (!_unicode) {
+ if (_bufPtr >= _bufEnd) {
+ len = 0;
+ } else {
+ if ((char *)_bufPtr + len > (char *)_bufEnd) {
+ lx = ((char *)_bufPtr + len) - (char *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ }
+
+ gotNewline = false;
+ for (lx = 0; lx < len && !gotNewline; lx++) {
+ buf[lx] = ((char *)_bufPtr)[lx];
+ gotNewline = (buf[lx] == '\n');
+ }
+
+ buf[lx] = '\0';
+ _bufPtr = ((char *)_bufPtr) + lx;
+ } else {
+ if (_bufPtr >= _bufEnd) {
+ len = 0;
+ } else {
+ if ((char *)_bufPtr + len > (char *)_bufEnd) {
+ lx = ((char *)_bufPtr + len) - (char *)_bufEnd;
+ if (lx < len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ }
+
+ gotNewline = false;
+ for (lx = 0; lx < len && !gotNewline; lx++) {
+ glui32 ch;
+ ch = ((glui32 *)_bufPtr)[lx];
+ if (ch >= 0x100)
+ ch = '?';
+ buf[lx] = (char)ch;
+ gotNewline = (ch == '\n');
+ }
+
+ buf[lx] = '\0';
+ _bufPtr = ((glui32 *)_bufPtr) + lx;
+ }
+
+ _readCount += lx;
+ return lx;
+}
+
+glui32 MemoryStream::getLineUni(glui32 *ubuf, glui32 len) {
+ bool gotNewline;
+ int lx;
+
+ if (!_readable || len == 0)
+ return 0;
+
+ len -= 1; // for the terminal null
+ if (!_unicode) {
+ if (_bufPtr >= _bufEnd) {
+ len = 0;
+ } else {
+ if ((char *)_bufPtr + len > (char *)_bufEnd) {
+ lx = ((char *)_bufPtr + len) - (char *)_bufEnd;
+ if (lx < (int)len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ }
+ gotNewline = false;
+ for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+ ubuf[lx] = ((unsigned char *)_bufPtr)[lx];
+ gotNewline = (ubuf[lx] == '\n');
+ }
+ ubuf[lx] = '\0';
+ _bufPtr = ((unsigned char *)_bufPtr) + lx;
+ } else {
+ if (_bufPtr >= _bufEnd) {
+ len = 0;
+ } else {
+ if ((glui32 *)_bufPtr + len > (glui32 *)_bufEnd) {
+ lx = ((glui32 *)_bufPtr + len) - (glui32 *)_bufEnd;
+ if (lx < (int)len)
+ len -= lx;
+ else
+ len = 0;
+ }
+ }
+ gotNewline = false;
+ for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+ glui32 ch;
+ ch = ((glui32 *)_bufPtr)[lx];
+ ubuf[lx] = ch;
+ gotNewline = (ch == '\n');
+ }
+ ubuf[lx] = '\0';
+ _bufPtr = ((glui32 *)_bufPtr) + lx;
+ }
+
+ _readCount += lx;
+ return lx;
+}
+
+/*--------------------------------------------------------------------------*/
+
+FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 rock, bool unicode) :
+ Stream(streams, fmode == filemode_Read, fmode != filemode_Read, rock, unicode), _lastOp(0),
+ _textFile(fref->_textMode), _inFile(nullptr), _outFile(nullptr), _inStream(nullptr) {
+ Common::String fname = fref->_slotNumber == -1 ? fref->_filename : fref->getSaveName();
+
+ if (fmode == filemode_Write || fmode == filemode_ReadWrite || fmode == filemode_WriteAppend) {
+ _outFile = g_system->getSavefileManager()->openForSaving(fname, fref->_slotNumber != -1);
+ if (!_outFile)
+ error("Could open file for writing - %s", fname.c_str());
+
+ if (fref->_slotNumber != -1)
+ writeSavegameHeader(_outFile, fref->_description);
+ } else if (fmode == filemode_Read) {
+ if (_file.open(fname)) {
+ _inStream = &_file;
+ } else {
+ _inFile = g_system->getSavefileManager()->openForLoading(fname);
+ _inStream = _inFile;
+ }
+
+ if (!_inStream)
+ error("Could not open for reading - %s", fname.c_str());
+
+ if (_inFile) {
+ // It's a save file, so skip over the header
+ SavegameHeader header;
+ if (!readSavegameHeader(_inStream, header))
+ error("Invalid savegame");
+ if (header._interpType != g_vm->getInterpreterType() || header._language != g_vm->getLanguage()
+ || header._md5 != g_vm->getGameMD5())
+ error("Savegame is for a different game");
+
+ g_vm->_events->setTotalPlayTicks(header._totalFrames);
+ }
+ }
+}
+
+FileStream::~FileStream() {
+ _file.close();
+ delete _inFile;
+ if (_outFile) {
+ _outFile->finalize();
+ delete _outFile;
+ }
+}
+
+void FileStream::ensureOp(FileMode mode) {
+ // No implementation
+}
+
+void FileStream::putChar(unsigned char ch) {
+ if (!_writable)
+ return;
+ ++_writeCount;
+
+ ensureOp(filemode_Write);
+ if (!_unicode) {
+ _outFile->writeByte(ch);
+ } else if (_textFile) {
+ putCharUtf8((glui32)ch);
+ } else {
+ _outFile->writeUint32BE(ch);
+ }
+
+ _outFile->flush();
+}
+
+void FileStream::putCharUni(uint32 ch) {
+ if (!_writable)
+ return;
+ ++_writeCount;
+
+ ensureOp(filemode_Write);
+ if (!_unicode) {
+ if (ch >= 0x100)
+ ch = '?';
+ _outFile->writeByte(ch);
+ } else if (_textFile) {
+ putCharUtf8(ch);
+ } else {
+ _outFile->writeUint32BE(ch);
+ }
+
+ _outFile->flush();
+}
+
+void FileStream::putBuffer(const char *buf, size_t len) {
+ if (!_writable)
+ return;
+ _writeCount += len;
+
+ ensureOp(filemode_Write);
+ for (size_t lx = 0; lx < len; lx++) {
+ unsigned char ch = ((const unsigned char *)buf)[lx];
+ if (!_unicode) {
+ _outFile->writeByte(ch);
+ } else if (_textFile) {
+ putCharUtf8((glui32)ch);
+ } else {
+ _outFile->writeUint32BE(ch);
+ }
+ }
+
+ _outFile->flush();
+}
+
+void FileStream::putBufferUni(const uint32 *buf, size_t len) {
+ if (!_writable)
+ return;
+ _writeCount += len;
+
+
+ ensureOp(filemode_Write);
+ for (size_t lx = 0; lx < len; lx++) {
+ glui32 ch = buf[lx];
+ if (!_unicode) {
+ if (ch >= 0x100)
+ ch = '?';
+ _outFile->writeByte(ch);
+ } else if (_textFile) {
+ putCharUtf8(ch);
+ } else {
+ _outFile->writeUint32BE(ch);
+ }
+ }
+
+ _outFile->flush();
+}
+
+void FileStream::putCharUtf8(glui32 val) {
+ if (val < 0x80) {
+ _outFile->writeByte(val);
+ } else if (val < 0x800) {
+ _outFile->writeByte((0xC0 | ((val & 0x7C0) >> 6)));
+ _outFile->writeByte((0x80 | (val & 0x03F)));
+ } else if (val < 0x10000) {
+ _outFile->writeByte((0xE0 | ((val & 0xF000) >> 12)));
+ _outFile->writeByte((0x80 | ((val & 0x0FC0) >> 6)));
+ _outFile->writeByte((0x80 | (val & 0x003F)));
+ } else if (val < 0x200000) {
+ _outFile->writeByte((0xF0 | ((val & 0x1C0000) >> 18)));
+ _outFile->writeByte((0x80 | ((val & 0x03F000) >> 12)));
+ _outFile->writeByte((0x80 | ((val & 0x000FC0) >> 6)));
+ _outFile->writeByte((0x80 | (val & 0x00003F)));
+ } else {
+ _outFile->writeByte('?');
+ }
+}
+
+glsi32 FileStream::getCharUtf8() {
+ glui32 res;
+ glui32 val0, val1, val2, val3;
+
+ if (_inStream->eos())
+ return -1;
+ val0 = _inStream->readByte();
+ if (val0 < 0x80) {
+ res = val0;
+ return res;
+ }
+
+ if ((val0 & 0xe0) == 0xc0) {
+ if (_inStream->eos()) {
+ warning("incomplete two-byte character");
+ return -1;
+ }
+
+ val1 = _inStream->readByte();
+ if ((val1 & 0xc0) != 0x80) {
+ warning("malformed two-byte character");
+ return '?';
+ }
+
+ res = (val0 & 0x1f) << 6;
+ res |= (val1 & 0x3f);
+ return res;
+ }
+
+ if ((val0 & 0xf0) == 0xe0) {
+ val1 = _inStream->readByte();
+ val2 = _inStream->readByte();
+ if (_inStream->eos()) {
+ warning("incomplete three-byte character");
+ return -1;
+ }
+ if ((val1 & 0xc0) != 0x80) {
+ warning("malformed three-byte character");
+ return '?';
+ }
+ if ((val2 & 0xc0) != 0x80) {
+ warning("malformed three-byte character");
+ return '?';
+ }
+
+ res = (((val0 & 0xf) << 12) & 0x0000f000);
+ res |= (((val1 & 0x3f) << 6) & 0x00000fc0);
+ res |= (((val2 & 0x3f)) & 0x0000003f);
+ return res;
+ }
+
+ if ((val0 & 0xf0) == 0xf0) {
+ if ((val0 & 0xf8) != 0xf0) {
+ warning("malformed four-byte character");
+ return '?';
+ }
+
+ val1 = _inStream->readByte();
+ val2 = _inStream->readByte();
+ val3 = _inStream->readByte();
+ if (_inStream->eos()) {
+ warning("incomplete four-byte character");
+ return -1;
+ }
+ if ((val1 & 0xc0) != 0x80) {
+ warning("malformed four-byte character");
+ return '?';
+ }
+ if ((val2 & 0xc0) != 0x80) {
+ warning("malformed four-byte character");
+ return '?';
+ }
+ if ((val3 & 0xc0) != 0x80) {
+ warning("malformed four-byte character");
+ return '?';
+ }
+
+ res = (((val0 & 0x7) << 18) & 0x1c0000);
+ res |= (((val1 & 0x3f) << 12) & 0x03f000);
+ res |= (((val2 & 0x3f) << 6) & 0x000fc0);
+ res |= (((val3 & 0x3f)) & 0x00003f);
+ return res;
+ }
+
+ warning("malformed character");
+ return '?';
+}
+
+glui32 FileStream::getPosition() const {
+ return _outFile ? _outFile->pos() : _inStream->pos();
+}
+
+void FileStream::setPosition(glsi32 pos, glui32 seekMode) {
+ _lastOp = 0;
+ if (_unicode)
+ pos *= 4;
+
+ if (_inStream) {
+ _inStream->seek(pos, SEEK_SET);
+ } else {
+ error("seek not supported for writing files");
+ }
+}
+
+glsi32 FileStream::getChar() {
+ if (!_readable)
+ return -1;
+
+ ensureOp(filemode_Read);
+ int res;
+ if (!_unicode) {
+ res = _inStream->readByte();
+ } else if (_textFile) {
+ res = getCharUtf8();
+ } else {
+ glui32 ch;
+ res = _inStream->readByte();
+ if (_inStream->eos())
+ return -1;
+ ch = (res & 0xFF);
+ res = _inStream->readByte();
+ if (_inStream->eos())
+ return -1;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (_inStream->eos())
+ return -1;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (_inStream->eos())
+ return -1;
+ ch = (ch << 8) | (res & 0xFF);
+ res = ch;
+ }
+ if (res != -1) {
+ _readCount++;
+ if (res >= 0x100)
+ return '?';
+ return (glsi32)res;
+ } else {
+ return -1;
+ }
+}
+
+glsi32 FileStream::getCharUni() {
+ if (!_readable)
+ return -1;
+
+ ensureOp(filemode_Read);
+ int res;
+ if (!_unicode) {
+ res = _inStream->readByte();
+ } else if (_textFile) {
+ res = getCharUtf8();
+ } else {
+ glui32 ch;
+ res = _inStream->readByte();
+ if (res == -1)
+ return -1;
+ ch = (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ return -1;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ return -1;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ return -1;
+ ch = (ch << 8) | (res & 0xFF);
+ res = ch;
+ }
+ if (res != -1) {
+ _readCount++;
+ return (glsi32)res;
+ } else {
+ return -1;
+ }
+}
+
+glui32 FileStream::getBuffer(char *buf, glui32 len) {
+ ensureOp(filemode_Read);
+ if (!_unicode) {
+ glui32 res;
+ res = _inStream->read(buf, len);
+ _readCount += res;
+ return res;
+ } else if (_textFile) {
+ glui32 lx;
+ for (lx = 0; lx < len; lx++) {
+ glui32 ch;
+ ch = getCharUtf8();
+ if (ch == (glui32)-1)
+ break;
+ _readCount++;
+ if (ch >= 0x100)
+ ch = '?';
+ buf[lx] = (char)ch;
+ }
+ return lx;
+ } else {
+ glui32 lx;
+ for (lx = 0; lx < len; lx++) {
+ int res;
+ glui32 ch;
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ _readCount++;
+ if (ch >= 0x100)
+ ch = '?';
+ buf[lx] = (char)ch;
+ }
+ return lx;
+ }
+}
+
+glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
+ if (!_readable)
+ return 0;
+
+ ensureOp(filemode_Read);
+ if (!_unicode) {
+ glui32 lx;
+ for (lx = 0; lx < len; lx++) {
+ int res;
+ glui32 ch;
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (res & 0xFF);
+ _readCount++;
+ buf[lx] = ch;
+ }
+ return lx;
+ } else if (_textFile) {
+ glui32 lx;
+ for (lx = 0; lx < len; lx++) {
+ glui32 ch;
+ ch = getCharUtf8();
+ if (ch == (glui32)-1)
+ break;
+ _readCount++;
+ buf[lx] = ch;
+ }
+ return lx;
+ } else {
+ glui32 lx;
+ for (lx = 0; lx < len; lx++) {
+ int res;
+ glui32 ch;
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ _readCount++;
+ buf[lx] = ch;
+ }
+ return lx;
+ }
+}
+
+glui32 FileStream::getLine(char *buf, glui32 len) {
+ glui32 lx;
+ bool gotNewline;
+
+ if (len == 0)
+ return 0;
+
+ ensureOp(filemode_Read);
+ if (!_unicode) {
+ char *res = buf;
+ for (; len > 0; ++res, --len) {
+ *res = _inStream->readByte();
+ if (*res == '\n')
+ break;
+ }
+ *res = '\0';
+
+ lx = strlen(buf);
+ _readCount += lx;
+ return lx;
+ } else if (_textFile) {
+ len -= 1; // for the terminal null
+ gotNewline = false;
+ for (lx = 0; lx < len && !gotNewline; lx++) {
+ glui32 ch;
+ ch = getCharUtf8();
+ if (ch == (glui32)-1)
+ break;
+ _readCount++;
+ if (ch >= 0x100)
+ ch = '?';
+ buf[lx] = (char)ch;
+ gotNewline = (ch == '\n');
+ }
+ buf[lx] = '\0';
+ return lx;
+ } else {
+ len -= 1; // for the terminal null
+ gotNewline = false;
+ for (lx = 0; lx < len && !gotNewline; lx++) {
+ int res;
+ glui32 ch;
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ _readCount++;
+ if (ch >= 0x100)
+ ch = '?';
+ buf[lx] = (char)ch;
+ gotNewline = (ch == '\n');
+ }
+
+ buf[lx] = '\0';
+ return lx;
+ }
+}
+
+glui32 FileStream::getLineUni(glui32 *ubuf, glui32 len) {
+ bool gotNewline;
+ int lx;
+
+ if (!_readable || len == 0)
+ return 0;
+
+ ensureOp(filemode_Read);
+ if (!_unicode) {
+ len -= 1; // for the terminal null
+ gotNewline = false;
+ for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+ int res;
+ glui32 ch;
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (res & 0xFF);
+ _readCount++;
+ ubuf[lx] = ch;
+ gotNewline = (ch == '\n');
+ }
+ ubuf[lx] = '\0';
+ return lx;
+ } else if (_textFile) {
+ len -= 1; /* for the terminal null */
+ gotNewline = false;
+ for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+ glui32 ch;
+ ch = getCharUtf8();
+ if (ch == (glui32)-1)
+ break;
+ _readCount++;
+ ubuf[lx] = ch;
+ gotNewline = (ch == '\n');
+ }
+ ubuf[lx] = '\0';
+ return lx;
+ } else {
+ len -= 1; // for the terminal null
+ gotNewline = false;
+ for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+ int res;
+ glui32 ch;
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ res = _inStream->readByte();
+ if (res == -1)
+ break;
+ ch = (ch << 8) | (res & 0xFF);
+ _readCount++;
+ ubuf[lx] = ch;
+ gotNewline = (ch == '\n');
+ }
+ ubuf[lx] = '\0';
+ return lx;
+ }
+}
+
+static Common::String readString(Common::ReadStream *src) {
+ char c;
+ Common::String result;
+ while ((c = src->readByte()) != 0)
+ result += c;
+
+ return result;
+}
+
+bool FileStream::readSavegameHeader(Common::SeekableReadStream *stream, SavegameHeader &header) {
+ header._totalFrames = 0;
+
+ // Validate the header Id
+ if (stream->readUint32BE() != MKTAG('G', 'A', 'R', 'G'))
+ return false;
+
+ // Check the savegame version
+ header._version = stream->readByte();
+ if (header._version > SAVEGAME_VERSION)
+ error("Savegame is too recent");
+
+ // Read the interpreter, language, and game Id
+ header._interpType = stream->readByte();
+ header._language = stream->readByte();
+ header._md5 = readString(stream);
+
+ // Read in name
+ header._saveName = readString(stream);
+
+ // Read in save date/time
+ header._year = stream->readUint16LE();
+ header._month = stream->readUint16LE();
+ header._day = stream->readUint16LE();
+ header._hour = stream->readUint16LE();
+ header._minute = stream->readUint16LE();
+ header._totalFrames = stream->readUint32LE();
+
+ return true;
+}
+
+void FileStream::writeSavegameHeader(Common::WriteStream *stream, const Common::String &saveName) {
+ // Write out a savegame header
+ stream->writeUint32BE(MKTAG('G', 'A', 'R', 'G'));
+ stream->writeByte(SAVEGAME_VERSION);
+
+ // Write out intrepreter type, language, and game Id
+ stream->writeByte(g_vm->getInterpreterType());
+ stream->writeByte(g_vm->getLanguage());
+ Common::String md5 = g_vm->getGameMD5();
+ stream->write(md5.c_str(), md5.size());
+ stream->writeByte('\0');
+
+ // Write savegame name
+ stream->write(saveName.c_str(), saveName.size());
+ stream->writeByte('\0');
+
+ // Write out the save date/time
+ TimeDate td;
+ g_system->getTimeAndDate(td);
+ stream->writeUint16LE(td.tm_year + 1900);
+ stream->writeUint16LE(td.tm_mon + 1);
+ stream->writeUint16LE(td.tm_mday);
+ stream->writeUint16LE(td.tm_hour);
+ stream->writeUint16LE(td.tm_min);
+ stream->writeUint32LE(g_vm->_events->getTotalPlayTicks());
+}
+
+/*--------------------------------------------------------------------------*/
+
+Streams::Streams() : _streamList(nullptr), _currentStream(nullptr) {
+}
+
+Streams::~Streams() {
+ while (_streamList)
+ delete _streamList;
+}
+
+FileStream *Streams::openFileStream(frefid_t fref, glui32 fmode, glui32 rock, bool unicode) {
+ FileStream *stream = new FileStream(this, fref, fmode, rock, unicode);
+ addStream(stream);
+ return stream;
+}
+
+WindowStream *Streams::openWindowStream(Window *window) {
+ WindowStream *stream = new WindowStream(this, window);
+ addStream(stream);
+ return stream;
+}
+
+MemoryStream *Streams::openMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) {
+ MemoryStream *stream = new MemoryStream(this, buf, buflen, mode, rock, unicode);
+ addStream(stream);
+ return stream;
+}
+
+void Streams::addStream(Stream *stream) {
+ stream->_next = _streamList;
+ _streamList = stream;
+ if (stream->_next)
+ stream->_next->_prev = stream;
+}
+
+void Streams::removeStream(Stream *stream) {
+ Stream *prev = stream->_prev;
+ Stream *next = stream->_next;
+
+ if (prev)
+ prev->_next = next;
+ else
+ _streamList = next;
+ if (next)
+ next->_prev = prev;
+
+ // Remove the stream as the echo stream of any window
+ for (Windows::iterator i = g_vm->_windows->begin(); i != g_vm->_windows->end(); ++i) {
+ if ((*i)->_echoStream == stream)
+ (*i)->_echoStream = nullptr;
+ }
+}
+
+Stream *Streams::getFirst(uint32 *rock) {
+ if (rock)
+ *rock = _streamList ? _streamList->_rock : 0;
+ return _streamList;
+}
+
+
+frefid_t Streams::createByPrompt(glui32 usage, FileMode fmode, glui32 rock) {
+ switch (usage & fileusage_TypeMask) {
+ case fileusage_SavedGame: {
+ if (fmode == filemode_Write) {
+ // Select a savegame slot
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+
+ int slot = dialog->runModalWithCurrentTarget();
+ if (slot >= 0) {
+ Common::String desc = dialog->getResultString();
+ return createRef(slot, desc, usage, rock);
+ }
+ } else if (fmode == filemode_Read) {
+ // Load a savegame slot
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+
+ int slot = dialog->runModalWithCurrentTarget();
+ if (slot >= 0) {
+ return createRef(slot, "", usage, rock);
+ }
+ } else {
+ error("Unsupport file mode");
+ }
+ break;
+ }
+
+ case fileusage_Transcript:
+ return createRef("transcript.txt", fmode, rock);
+
+ default:
+ error("Unsupport file mode");
+ break;
+ }
+
+ return nullptr;
+}
+
+frefid_t Streams::createRef(int slot, const Common::String &desc, glui32 usage, glui32 rock) {
+ frefid_t fref = new FileReference();
+ fref->_slotNumber = slot;
+ fref->_description = desc;
+ fref->_textMode = ((usage & fileusage_TextMode) != 0);
+ fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
+
+ _fileReferences.push_back(FileRefArray::value_type(fref));
+ return fref;
+}
+
+frefid_t Streams::createRef(const Common::String &filename, glui32 usage, glui32 rock) {
+ frefid_t fref = new FileReference();
+ fref->_filename = filename;
+ fref->_textMode = ((usage & fileusage_TextMode) != 0);
+ fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
+
+ _fileReferences.push_back(FileRefArray::value_type(fref));
+ return fref;
+}
+
+frefid_t Streams::createTemp(glui32 usage, glui32 rock) {
+ return createRef(Common::String::format("%s.tmp", g_vm->getTargetName().c_str()),
+ usage, rock);
+}
+
+frefid_t Streams::createFromRef(frefid_t fref, glui32 usage, glui32 rock) {
+ return createRef(fref->_filename, usage, rock);
+}
+
+void Streams::deleteRef(frefid_t fref) {
+ for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
+ if (_fileReferences[idx].get() == fref) {
+ _fileReferences.remove_at(idx);
+ return;
+ }
+ }
+}
+
+frefid_t Streams::iterate(frefid_t fref, glui32 *rock) {
+ // Find reference following the specified one
+ int index = -1;
+ for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
+ if (fref == nullptr || _fileReferences[idx].get() == fref) {
+ if (idx < (_fileReferences.size() - 1))
+ index = idx + 1;
+ break;
+ }
+ }
+
+ if (index != -1) {
+ if (rock)
+ *rock = _fileReferences[index].get()->_rock;
+ return _fileReferences[index].get();
+ }
+
+ if (rock)
+ *rock = 0;
+ return nullptr;
+}
+
+/*--------------------------------------------------------------------------*/
+
+const Common::String FileReference::getSaveName() const {
+ assert(_slotNumber != -1);
+ return g_vm->getSaveName(_slotNumber);
+}
+
+bool FileReference::exists() const {
+ Common::String filename;
+
+ if (_slotNumber == -1) {
+ if (Common::File::exists(_filename))
+ return true;
+ filename = _filename;
+ } else {
+ filename = getSaveName();
+ }
+
+ // Check for a savegame (or other file in the save folder) with that name
+ Common::InSaveFile *inSave = g_system->getSavefileManager()->openForLoading(filename);
+ bool result = inSave != nullptr;
+ delete inSave;
+ return result;
+}
+
+void FileReference::deleteFile() {
+ Common::String filename = (_slotNumber == -1) ? _filename : getSaveName();
+ g_system->getSavefileManager()->removeSavefile(filename);
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/streams.h b/engines/glk/streams.h
new file mode 100644
index 0000000000..24ecc68eb5
--- /dev/null
+++ b/engines/glk/streams.h
@@ -0,0 +1,639 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_STREAMS_H
+#define GLK_STREAMS_H
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/str.h"
+#include "glk/glk_types.h"
+
+namespace Gargoyle {
+
+#define SAVEGAME_VERSION 1
+
+class Window;
+class Streams;
+
+enum FileUsage {
+ fileusage_Data = 0x00,
+ fileusage_SavedGame = 0x01,
+ fileusage_Transcript = 0x02,
+ fileusage_InputRecord = 0x03,
+ fileusage_TypeMask = 0x0f,
+
+ fileusage_TextMode = 0x100,
+ fileusage_BinaryMode = 0x000
+};
+
+enum FileMode {
+ filemode_Write = 0x01,
+ filemode_Read = 0x02,
+ filemode_ReadWrite = 0x03,
+ filemode_WriteAppend = 0x05
+};
+
+enum SeekMode {
+ seekmode_Start = 0,
+ seekmode_Current = 1,
+ seekmode_End = 2
+};
+
+struct StreamResult {
+ uint32 _readCount;
+ uint32 _writeCount;
+};
+typedef StreamResult stream_result_t;
+
+struct SavegameHeader {
+ uint8 _version;
+ byte _interpType;
+ byte _language;
+ Common::String _md5;
+ Common::String _saveName;
+ int _year, _month, _day;
+ int _hour, _minute;
+ int _totalFrames;
+
+ /**
+ * Constructor
+ */
+ SavegameHeader() : _version(0), _interpType(0), _language(0), _year(0), _month(0), _day(0),
+ _hour(0), _minute(0), _totalFrames(0) {}
+};
+
+/**
+ * File details
+ */
+struct FileReference {
+ glui32 _rock;
+ int _slotNumber;
+ Common::String _description;
+ Common::String _filename;
+ FileUsage _fileType;
+ bool _textMode;
+ gidispatch_rock_t _dispRock;
+
+ /**
+ * Constructor
+ */
+ FileReference() : _rock(0), _slotNumber(-1), _fileType(fileusage_Data), _textMode(false) {}
+
+ /**
+ * Constructor
+ */
+ FileReference(int slot, const Common::String &desc, glui32 usage, glui32 rock = 0) :
+ _rock(rock), _slotNumber(slot), _description(desc),
+ _fileType((FileUsage)(usage & fileusage_TypeMask)), _textMode(usage & fileusage_TextMode) {}
+
+ /**
+ * Get savegame filename
+ */
+ const Common::String getSaveName() const;
+
+ /**
+ * Returns true if the given file exists
+ */
+ bool exists() const;
+
+ /**
+ * Delete the given file
+ */
+ void deleteFile();
+};
+
+typedef FileReference *frefid_t;
+typedef Common::Array< Common::SharedPtr<FileReference> > FileRefArray;
+
+
+/**
+ * Base class for streams
+ */
+class Stream {
+public:
+ Streams *_streams;
+ Stream *_prev;
+ Stream *_next;
+ uint32 _rock;
+ bool _unicode;
+ uint32 _readCount;
+ uint32 _writeCount;
+ bool _readable, _writable;
+public:
+ /**
+ * Constructor
+ */
+ Stream(Streams *streams, bool readable, bool writable, uint32 rock, bool unicode);
+
+ /**
+ * Destructor
+ */
+ virtual ~Stream();
+
+ /**
+ * Get the next stream
+ */
+ Stream *getNext(uint32 *rock) const;
+
+ /**
+ * Get the rock value for the stream
+ */
+ uint32 getRock() const {
+ return _rock;
+ }
+
+ /**
+ * Fill out the total amount read and/or written
+ */
+ void fillResult(StreamResult *result);
+
+ /**
+ * Close and delete the stream
+ */
+ void close(StreamResult *result = nullptr);
+
+ /**
+ * Write a character
+ */
+ virtual void putChar(unsigned char ch) = 0;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putCharUni(uint32 ch) = 0;
+
+ /**
+ * Write a buffer
+ */
+ virtual void putBuffer(const char *buf, size_t len) = 0;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putBufferUni(const uint32 *buf, size_t len) = 0;
+
+ /**
+ * Remove a string from the end of the stream, if indeed it is at the end
+ */
+ virtual void unputBuffer(const char *buf, size_t len) {}
+
+ /**
+ * Remove a string from the end of the stream, if indeed it is at the end
+ */
+ virtual void unputBufferUni(const glui32 *buf, size_t len) {}
+
+ /**
+ * Send a line to the stream with a trailing newline
+ */
+ void echoLine(char *buf, glui32 len) {
+ putBuffer(buf, len);
+ putChar('\n');
+ };
+
+ /**
+ * Send a line to the stream with a trailing newline
+ */
+ void echoLineUni(glui32 *buf, glui32 len) {
+ putBufferUni(buf, len);
+ putCharUni('\n');
+ }
+
+ virtual glui32 getPosition() const {
+ return 0;
+ }
+
+ virtual void setPosition(glsi32 pos, glui32 seekMode) {}
+
+ virtual void setStyle(glui32 val) {}
+
+ /**
+ * Get a character from the stream
+ */
+ virtual glsi32 getChar() {
+ return -1;
+ }
+
+ /**
+ * Get a unicode character from the stream
+ */
+ virtual glsi32 getCharUni() {
+ return -1;
+ }
+
+ /**
+ * Get a buffer
+ */
+ virtual glui32 getBuffer(char *buf, glui32 len) {
+ return 0;
+ }
+
+ /**
+ * Get a unicode buffer
+ */
+ virtual glui32 getBufferUni(glui32 *buf, glui32 len) {
+ return 0;
+ }
+
+ /**
+ * Get a line
+ */
+ virtual glui32 getLine(char *buf, glui32 len) {
+ return 0;
+ }
+
+ /**
+ * Get a unicode line
+ */
+ virtual glui32 getLineUni(glui32 *ubuf, glui32 len) {
+ return 0;
+ }
+
+ /**
+ * Set a hyperlink
+ */
+ virtual void setHyperlink(glui32 linkVal) {}
+
+ /**
+ * Set the style colors
+ */
+ virtual void setZColors(glui32 fg, glui32 bg);
+
+ /**
+ * Set the reverse video style
+ */
+ virtual void setReverseVideo(bool reverse);
+};
+typedef Stream *strid_t;
+
+/**
+ * Implements the stream for writing text to a window
+ */
+class WindowStream : public Stream {
+private:
+ Window *_window;
+public:
+ /**
+ * Constructor
+ */
+ WindowStream(Streams *streams, Window *window, uint32 rock = 0, bool unicode = true) :
+ Stream(streams, false, true, rock, unicode), _window(window) {}
+
+ /**
+ * Close the stream
+ */
+ virtual void close(StreamResult *result = nullptr);
+
+ /**
+ * Write a character
+ */
+ virtual void putChar(unsigned char ch) override;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putCharUni(uint32 ch) override;
+
+ /**
+ * Write a buffer
+ */
+ virtual void putBuffer(const char *buf, size_t len) override;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+ /**
+ * Remove a string from the end of the stream, if indeed it is at the end
+ */
+ virtual void unputBuffer(const char *buf, size_t len) override;
+
+ /**
+ * Remove a string from the end of the stream, if indeed it is at the end
+ */
+ virtual void unputBufferUni(const glui32 *buf, size_t len) override;
+
+ virtual void setStyle(glui32 val) override;
+
+ /**
+ * Set a hyperlink
+ */
+ virtual void setHyperlink(glui32 linkVal) override;
+
+ /**
+ * Set the style colors
+ */
+ virtual void setZColors(glui32 fg, glui32 bg) override;
+
+ /**
+ * Set the reverse video style
+ */
+ virtual void setReverseVideo(bool reverse) override;
+};
+
+/**
+ * Implements an in-memory stream
+ */
+class MemoryStream : public Stream {
+private:
+ void *_buf; ///< unsigned char* for latin1, glui32* for unicode
+ void *_bufPtr;
+ void *_bufEnd;
+ void *_bufEof;
+ size_t _bufLen; ///< # of bytes for latin1, # of 4-byte words for unicode
+public:
+ /**
+ * Constructor
+ */
+ MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
+
+ /**
+ * Write a character
+ */
+ virtual void putChar(unsigned char ch) override;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putCharUni(uint32 ch) override;
+
+ /**
+ * Write a buffer
+ */
+ virtual void putBuffer(const char *buf, size_t len) override;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+ virtual glui32 getPosition() const override;
+
+ virtual void setPosition(glsi32 pos, glui32 seekMode) override;
+
+ /**
+ * Get a character from the stream
+ */
+ virtual glsi32 getChar() override;
+
+ /**
+ * Get a unicode character from the stream
+ */
+ virtual glsi32 getCharUni() override;
+
+ /**
+ * Get a buffer
+ */
+ virtual glui32 getBuffer(char *buf, glui32 len) override;
+
+ /**
+ * Get a unicode buffer
+ */
+ virtual glui32 getBufferUni(glui32 *buf, glui32 len) override;
+
+ /**
+ * Get a line
+ */
+ virtual glui32 getLine(char *buf, glui32 len) override;
+
+ /**
+ * Get a unicode line
+ */
+ virtual glui32 getLineUni(glui32 *ubuf, glui32 len) override;
+};
+
+/**
+ * Implements a file stream
+ */
+class FileStream : public Stream {
+private:
+ Common::File _file;
+ Common::OutSaveFile *_outFile;
+ Common::InSaveFile *_inFile;
+ Common::SeekableReadStream *_inStream;
+ uint32 _lastOp; ///< 0, filemode_Write, or filemode_Read
+ bool _textFile;
+private:
+ /**
+ * Ensure the stream is ready for the given operation
+ */
+ void ensureOp(FileMode mode);
+
+ /**
+ * Put a UTF8 character
+ */
+ void putCharUtf8(glui32 val);
+
+ /**
+ * Get a UTF8 character
+ */
+ glsi32 getCharUtf8();
+public:
+ /**
+ * Read a savegame header from a stream
+ */
+ static bool readSavegameHeader(Common::SeekableReadStream *stream, SavegameHeader &header);
+
+ /**
+ * Write out a savegame header
+ */
+ static void writeSavegameHeader(Common::WriteStream *stream, const Common::String &saveName);
+public:
+ /**
+ * Constructor
+ */
+ FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 rock, bool unicode);
+
+ /**
+ * Destructor
+ */
+ virtual ~FileStream();
+
+ /**
+ * Write a character
+ */
+ virtual void putChar(unsigned char ch) override;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putCharUni(uint32 ch) override;
+
+ /**
+ * Write a buffer
+ */
+ virtual void putBuffer(const char *buf, size_t len) override;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+ virtual glui32 getPosition() const override;
+
+ virtual void setPosition(glsi32 pos, glui32 seekMode) override;
+
+ /**
+ * Get a character from the stream
+ */
+ virtual glsi32 getChar() override;
+
+ /**
+ * Get a unicode character from the stream
+ */
+ virtual glsi32 getCharUni() override;
+
+ /**
+ * Get a buffer
+ */
+ virtual glui32 getBuffer(char *buf, glui32 len) override;
+
+ /**
+ * Get a unicode buffer
+ */
+ virtual glui32 getBufferUni(glui32 *buf, glui32 len) override;
+
+ /**
+ * Get a line
+ */
+ virtual glui32 getLine(char *buf, glui32 len) override;
+
+ /**
+ * Get a unicode line
+ */
+ virtual glui32 getLineUni(glui32 *ubuf, glui32 len) override;
+};
+
+/**
+ * Streams manager
+ */
+class Streams {
+ friend class Stream;
+private:
+ Stream *_streamList;
+ Stream *_currentStream;
+ FileRefArray _fileReferences;
+private:
+ /**
+ * Adds a created stream to the list
+ */
+ void addStream(Stream *stream);
+
+ /**
+ * Remove a stream
+ */
+ void removeStream(Stream *stream);
+public:
+ /**
+ * Constructor
+ */
+ Streams();
+
+ /**
+ * Destructor
+ */
+ ~Streams();
+
+ /**
+ * Open a file stream
+ */
+ FileStream *openFileStream(frefid_t fref, glui32 fmode, glui32 rock, bool unicode);
+
+ /**
+ * Open a window stream
+ */
+ WindowStream *openWindowStream(Window *window);
+
+ /**
+ * Open a memory stream
+ */
+ MemoryStream *openMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
+
+ /**
+ * Delete a stream
+ */
+ void deleteStream(Stream *stream) {
+ delete stream;
+ }
+
+ /**
+ * Start an Iteration through streams
+ */
+ Stream *getFirst(uint32 *rock);
+
+ /**
+ * Set the current output stream
+ */
+ void setCurrent(Stream *stream) {
+ assert(stream->_writable);
+ _currentStream = stream;
+ }
+
+ /**
+ * Gets the current output stream
+ */
+ Stream *getCurrent() const {
+ return _currentStream;
+ }
+
+ /**
+ * Prompt for a savegame to load or save, and populate a file reference from the result
+ */
+ frefid_t createByPrompt(glui32 usage, FileMode fmode, glui32 rock);
+
+ /**
+ * Create a new file reference
+ */
+ frefid_t createRef(int slot, const Common::String &desc, glui32 usage, glui32 rock);
+
+ /**
+ * Create a new file reference
+ */
+ frefid_t createRef(const Common::String &filename, glui32 usage, glui32 rock);
+
+ /**
+ * Create a new temporary file reference
+ */
+ frefid_t createTemp(glui32 usage, glui32 rock);
+
+ /**
+ * Create a new file reference from an old one
+ */
+ frefid_t createFromRef(frefid_t fref, glui32 usage, glui32 rock);
+
+ /**
+ * Delete a file reference
+ */
+ void deleteRef(frefid_t fref);
+
+ /**
+ * Iterates to the next file reference following the specified one,
+ * or the first if null is passed
+ */
+ frefid_t iterate(frefid_t fref, glui32 *rock);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/time.cpp b/engines/glk/time.cpp
new file mode 100644
index 0000000000..9442102779
--- /dev/null
+++ b/engines/glk/time.cpp
@@ -0,0 +1,120 @@
+/* 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/time.h"
+#include "common/system.h"
+
+namespace Gargoyle {
+
+TimeAndDate::TimeAndDate() {
+ ::TimeDate t;
+ g_system->getTimeAndDate(t);
+
+ year = t.tm_year;
+ month = t.tm_mon;
+ day = t.tm_mday;
+ weekday = t.tm_wday;
+ hour = t.tm_hour;
+ minute = t.tm_min;
+ second = t.tm_sec;
+ microsec = 0;
+}
+
+TimeAndDate::TimeAndDate(const TimeSeconds &ts) {
+ setTime(ts);
+}
+
+TimeAndDate::TimeAndDate(const Timestamp &t) {
+ setTime(((int64)t.high_sec << 32) | t.low_sec);
+}
+
+TimeAndDate::operator TimeSeconds() const {
+ return getTime();
+}
+
+TimeAndDate::operator Timestamp() const {
+ TimeSeconds secs = getTime();
+ Timestamp ts;
+ ts.high_sec = secs >> 32;
+ ts.low_sec = secs & 0xffffffff;
+ ts.microsec = 0;
+
+ return ts;
+}
+
+void TimeAndDate::setTime(const TimeSeconds &ts) {
+ TimeSeconds total = ts;
+ int daysInYear = 0, secsInYear = 0;
+
+ // Figure out the year
+ this->year = 1969;
+ do {
+ ++this->year;
+ total -= secsInYear;
+
+ daysInYear = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 366 : 365;
+ secsInYear = daysInYear * 24 * 60 * 60;
+ } while (total >= secsInYear);
+
+ // Figure out month and day
+ int dayInYear = total / (24 * 60 * 60);
+ total %= 24 * 60 * 60;
+
+ int MONTH_DAYS[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ this->month = 1;
+ while (dayInYear >= MONTH_DAYS[this->month - 1]) {
+ dayInYear -= MONTH_DAYS[this->month - 1];
+ this->month++;
+ }
+
+ this->day = dayInYear + 1;
+
+ // set the time within the day
+ this->hour = total / (60 * 60);
+ total %= (60 * 60);
+ this->minute = total / 60;
+ this->second = total % 60;
+ this->microsec = 0;
+}
+
+TimeSeconds TimeAndDate::getTime() const {
+ uint32 days = day - 1;
+ for (int i = 1970; i < year; ++i)
+ if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0))
+ days += 366;
+ else
+ days += 365;
+
+ int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ for (int i = 1; i < month; ++i) {
+ days += mdays[i - 1];
+ if (i == 2)
+ if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
+ days += 1;
+ }
+
+ int64 totalHours = days * 24 + hour;
+ int64 totalMinutes = totalHours * 60 + minute;
+ return totalMinutes * 60 + second;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/time.h b/engines/glk/time.h
new file mode 100644
index 0000000000..119fab76e5
--- /dev/null
+++ b/engines/glk/time.h
@@ -0,0 +1,93 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_TIME_H
+#define GLK_TIME_H
+
+#include "glk/glk_types.h"
+
+namespace Gargoyle {
+
+typedef int64 TimeSeconds;
+
+struct Timestamp {
+ glsi32 high_sec;
+ glui32 low_sec;
+ glsi32 microsec;
+};
+typedef Timestamp glktimeval_t;
+
+struct TimeAndDate {
+ glsi32 year; ///< full (four-digit) year
+ glsi32 month; ///< 1-12, 1 is January
+ glsi32 day; ///< 1-31
+ glsi32 weekday; ///< 0-6, 0 is Sunday
+ glsi32 hour; ///< 0-23
+ glsi32 minute; ///< 0-59
+ glsi32 second; ///< 0-59, maybe 60 during a leap second
+ glsi32 microsec; ///< 0-999999
+private:
+ /**
+ * Get the number of seconds since the start of 1970
+ */
+ int64 getSecondsSince1970() const;
+
+ /**
+ * Convert a time in seconds to the structure
+ */
+ void setTime(const TimeSeconds &ts);
+
+ /**
+ * Get time in seconds from the structure
+ */
+ TimeSeconds getTime() const;
+public:
+ /**
+ * Constructor
+ */
+ TimeAndDate();
+
+ /**
+ * Constructor
+ */
+ TimeAndDate(const TimeSeconds &ts);
+
+ /**
+ * Constructor
+ */
+ TimeAndDate(const Timestamp &t);
+
+ /**
+ * Convert to seconds
+ */
+ operator TimeSeconds() const;
+
+ /**
+ * Convert to time stamp
+ */
+ operator Timestamp() const;
+};
+typedef TimeAndDate glkdate_t;
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/unicode.cpp b/engines/glk/unicode.cpp
new file mode 100644
index 0000000000..dd88c9571a
--- /dev/null
+++ b/engines/glk/unicode.cpp
@@ -0,0 +1,151 @@
+/* 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/unicode.h"
+#include "glk/unicode_gen.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+
+size_t strlen_uni(const uint32 *s) {
+ size_t len = 0;
+ while (*s++)
+ ++len;
+ return len;
+}
+
+glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCase destcase,
+ BufferChangeCond cond, int changerest) {
+ glui32 ix, jx;
+ glui32 *outbuf;
+ glui32 *newoutbuf;
+ glui32 outcount;
+ int dest_block_rest = 0, dest_block_first = 0;
+ int dest_spec_rest = 0, dest_spec_first = 0;
+
+ switch (cond) {
+ case COND_ALL:
+ dest_spec_rest = destcase;
+ dest_spec_first = destcase;
+ break;
+ case COND_LINESTART:
+ if (changerest)
+ dest_spec_rest = CASE_LOWER;
+ else
+ dest_spec_rest = CASE_IDENT;
+ dest_spec_first = destcase;
+ break;
+ }
+
+ dest_block_rest = dest_spec_rest;
+ if (dest_block_rest == CASE_TITLE)
+ dest_block_rest = CASE_UPPER;
+ dest_block_first = dest_spec_first;
+ if (dest_block_first == CASE_TITLE)
+ dest_block_first = CASE_UPPER;
+
+ newoutbuf = nullptr;
+ outcount = 0;
+ outbuf = buf;
+
+ for (ix = 0; ix < numchars; ix++) {
+ int target;
+ int isfirst;
+ glui32 res;
+ glui32 *special;
+ glui32 *ptr;
+ glui32 speccount;
+ glui32 ch = buf[ix];
+
+ isfirst = (ix == 0);
+
+ target = (isfirst ? dest_block_first : dest_block_rest);
+
+ if (target == CASE_IDENT) {
+ res = ch;
+ } else {
+ gli_case_block_t *block;
+
+ GET_CASE_BLOCK(ch, &block);
+ if (!block)
+ res = ch;
+ else
+ res = block[ch & 0xFF][target];
+ }
+
+ if (res != 0xFFFFFFFF || res == ch) {
+ /* simple case */
+ if (outcount < len)
+ outbuf[outcount] = res;
+ outcount++;
+ continue;
+ }
+
+ target = (isfirst ? dest_spec_first : dest_spec_rest);
+
+ /* complicated cases */
+ GET_CASE_SPECIAL(ch, &special);
+ if (!special) {
+ warning("inconsistency in cgunigen.c");
+ continue;
+ }
+ ptr = &unigen_special_array[special[target]];
+ speccount = *(ptr++);
+
+ if (speccount == 1) {
+ /* simple after all */
+ if (outcount < len)
+ outbuf[outcount] = ptr[0];
+ outcount++;
+ continue;
+ }
+
+ // Now we have to allocate a new buffer, if we haven't already.
+ if (!newoutbuf) {
+ newoutbuf = new glui32[len + 1];
+ if (!newoutbuf)
+ return 0;
+ if (outcount)
+ memcpy(newoutbuf, buf, outcount * sizeof(glui32));
+ outbuf = newoutbuf;
+ }
+
+ for (jx = 0; jx < speccount; jx++) {
+ if (outcount < len)
+ outbuf[outcount] = ptr[jx];
+ outcount++;
+ }
+ }
+
+ if (newoutbuf) {
+ glui32 finallen = outcount;
+ if (finallen > len)
+ finallen = len;
+ if (finallen)
+ memcpy(buf, newoutbuf, finallen * sizeof(glui32));
+ free(newoutbuf);
+ }
+
+ return outcount;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/unicode.h b/engines/glk/unicode.h
new file mode 100644
index 0000000000..249ab5dade
--- /dev/null
+++ b/engines/glk/unicode.h
@@ -0,0 +1,51 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_UNICODE_H
+#define GLK_UNICODE_H
+
+#include "glk/glk_types.h"
+
+namespace Gargoyle {
+
+typedef glui32 gli_case_block_t[2]; /* upper, lower */
+enum BufferChangeCase { CASE_UPPER = 0, CASE_LOWER = 1, CASE_TITLE = 2, CASE_IDENT = 3 };
+enum BufferChangeCond { COND_ALL = 0, COND_LINESTART = 1 };
+
+/*
+ * Get the length of a unicode string
+ */
+size_t strlen_uni(const uint32 *s);
+
+/**
+ * Apply a case change to the buffer. The len is the length of the buffer
+ * array; numchars is the number of characters originally in it. (This
+ * may be less than len.) The result will be clipped to fit len, but
+ * the return value will be the full number of characters that the
+ *converted string should have contained.
+ */
+extern glui32 bufferChangeCase(glui32 *buf, glui32 len,
+ glui32 numchars, BufferChangeCase destcase, BufferChangeCond cond, int changerest);
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/unicode_gen.cpp b/engines/glk/unicode_gen.cpp
new file mode 100644
index 0000000000..ac8a149f7e
--- /dev/null
+++ b/engines/glk/unicode_gen.cpp
@@ -0,0 +1,11826 @@
+/* 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.
+ *
+ */
+
+/**
+ * This file is based on one that was generated by casemap.py.
+ * Derived from Unicode data files, Unicode version 4.0.1.
+ */
+
+#include "glk/unicode_gen.h"
+
+namespace Gargoyle {
+
+gli_case_block_t unigen_case_block_0x0[256] = {
+ { 0x0, 0x0 },
+ { 0x1, 0x1 },
+ { 0x2, 0x2 },
+ { 0x3, 0x3 },
+ { 0x4, 0x4 },
+ { 0x5, 0x5 },
+ { 0x6, 0x6 },
+ { 0x7, 0x7 },
+ { 0x8, 0x8 },
+ { 0x9, 0x9 },
+ { 0xa, 0xa },
+ { 0xb, 0xb },
+ { 0xc, 0xc },
+ { 0xd, 0xd },
+ { 0xe, 0xe },
+ { 0xf, 0xf },
+ { 0x10, 0x10 },
+ { 0x11, 0x11 },
+ { 0x12, 0x12 },
+ { 0x13, 0x13 },
+ { 0x14, 0x14 },
+ { 0x15, 0x15 },
+ { 0x16, 0x16 },
+ { 0x17, 0x17 },
+ { 0x18, 0x18 },
+ { 0x19, 0x19 },
+ { 0x1a, 0x1a },
+ { 0x1b, 0x1b },
+ { 0x1c, 0x1c },
+ { 0x1d, 0x1d },
+ { 0x1e, 0x1e },
+ { 0x1f, 0x1f },
+ { 0x20, 0x20 },
+ { 0x21, 0x21 },
+ { 0x22, 0x22 },
+ { 0x23, 0x23 },
+ { 0x24, 0x24 },
+ { 0x25, 0x25 },
+ { 0x26, 0x26 },
+ { 0x27, 0x27 },
+ { 0x28, 0x28 },
+ { 0x29, 0x29 },
+ { 0x2a, 0x2a },
+ { 0x2b, 0x2b },
+ { 0x2c, 0x2c },
+ { 0x2d, 0x2d },
+ { 0x2e, 0x2e },
+ { 0x2f, 0x2f },
+ { 0x30, 0x30 },
+ { 0x31, 0x31 },
+ { 0x32, 0x32 },
+ { 0x33, 0x33 },
+ { 0x34, 0x34 },
+ { 0x35, 0x35 },
+ { 0x36, 0x36 },
+ { 0x37, 0x37 },
+ { 0x38, 0x38 },
+ { 0x39, 0x39 },
+ { 0x3a, 0x3a },
+ { 0x3b, 0x3b },
+ { 0x3c, 0x3c },
+ { 0x3d, 0x3d },
+ { 0x3e, 0x3e },
+ { 0x3f, 0x3f },
+ { 0x40, 0x40 },
+ { 0x41, 0x61 }, /* upper */
+ { 0x42, 0x62 }, /* upper */
+ { 0x43, 0x63 }, /* upper */
+ { 0x44, 0x64 }, /* upper */
+ { 0x45, 0x65 }, /* upper */
+ { 0x46, 0x66 }, /* upper */
+ { 0x47, 0x67 }, /* upper */
+ { 0x48, 0x68 }, /* upper */
+ { 0x49, 0x69 }, /* upper */
+ { 0x4a, 0x6a }, /* upper */
+ { 0x4b, 0x6b }, /* upper */
+ { 0x4c, 0x6c }, /* upper */
+ { 0x4d, 0x6d }, /* upper */
+ { 0x4e, 0x6e }, /* upper */
+ { 0x4f, 0x6f }, /* upper */
+ { 0x50, 0x70 }, /* upper */
+ { 0x51, 0x71 }, /* upper */
+ { 0x52, 0x72 }, /* upper */
+ { 0x53, 0x73 }, /* upper */
+ { 0x54, 0x74 }, /* upper */
+ { 0x55, 0x75 }, /* upper */
+ { 0x56, 0x76 }, /* upper */
+ { 0x57, 0x77 }, /* upper */
+ { 0x58, 0x78 }, /* upper */
+ { 0x59, 0x79 }, /* upper */
+ { 0x5a, 0x7a }, /* upper */
+ { 0x5b, 0x5b },
+ { 0x5c, 0x5c },
+ { 0x5d, 0x5d },
+ { 0x5e, 0x5e },
+ { 0x5f, 0x5f },
+ { 0x60, 0x60 },
+ { 0x41, 0x61 }, /* lower */
+ { 0x42, 0x62 }, /* lower */
+ { 0x43, 0x63 }, /* lower */
+ { 0x44, 0x64 }, /* lower */
+ { 0x45, 0x65 }, /* lower */
+ { 0x46, 0x66 }, /* lower */
+ { 0x47, 0x67 }, /* lower */
+ { 0x48, 0x68 }, /* lower */
+ { 0x49, 0x69 }, /* lower */
+ { 0x4a, 0x6a }, /* lower */
+ { 0x4b, 0x6b }, /* lower */
+ { 0x4c, 0x6c }, /* lower */
+ { 0x4d, 0x6d }, /* lower */
+ { 0x4e, 0x6e }, /* lower */
+ { 0x4f, 0x6f }, /* lower */
+ { 0x50, 0x70 }, /* lower */
+ { 0x51, 0x71 }, /* lower */
+ { 0x52, 0x72 }, /* lower */
+ { 0x53, 0x73 }, /* lower */
+ { 0x54, 0x74 }, /* lower */
+ { 0x55, 0x75 }, /* lower */
+ { 0x56, 0x76 }, /* lower */
+ { 0x57, 0x77 }, /* lower */
+ { 0x58, 0x78 }, /* lower */
+ { 0x59, 0x79 }, /* lower */
+ { 0x5a, 0x7a }, /* lower */
+ { 0x7b, 0x7b },
+ { 0x7c, 0x7c },
+ { 0x7d, 0x7d },
+ { 0x7e, 0x7e },
+ { 0x7f, 0x7f },
+ { 0x80, 0x80 },
+ { 0x81, 0x81 },
+ { 0x82, 0x82 },
+ { 0x83, 0x83 },
+ { 0x84, 0x84 },
+ { 0x85, 0x85 },
+ { 0x86, 0x86 },
+ { 0x87, 0x87 },
+ { 0x88, 0x88 },
+ { 0x89, 0x89 },
+ { 0x8a, 0x8a },
+ { 0x8b, 0x8b },
+ { 0x8c, 0x8c },
+ { 0x8d, 0x8d },
+ { 0x8e, 0x8e },
+ { 0x8f, 0x8f },
+ { 0x90, 0x90 },
+ { 0x91, 0x91 },
+ { 0x92, 0x92 },
+ { 0x93, 0x93 },
+ { 0x94, 0x94 },
+ { 0x95, 0x95 },
+ { 0x96, 0x96 },
+ { 0x97, 0x97 },
+ { 0x98, 0x98 },
+ { 0x99, 0x99 },
+ { 0x9a, 0x9a },
+ { 0x9b, 0x9b },
+ { 0x9c, 0x9c },
+ { 0x9d, 0x9d },
+ { 0x9e, 0x9e },
+ { 0x9f, 0x9f },
+ { 0xa0, 0xa0 },
+ { 0xa1, 0xa1 },
+ { 0xa2, 0xa2 },
+ { 0xa3, 0xa3 },
+ { 0xa4, 0xa4 },
+ { 0xa5, 0xa5 },
+ { 0xa6, 0xa6 },
+ { 0xa7, 0xa7 },
+ { 0xa8, 0xa8 },
+ { 0xa9, 0xa9 },
+ { 0xaa, 0xaa },
+ { 0xab, 0xab },
+ { 0xac, 0xac },
+ { 0xad, 0xad },
+ { 0xae, 0xae },
+ { 0xaf, 0xaf },
+ { 0xb0, 0xb0 },
+ { 0xb1, 0xb1 },
+ { 0xb2, 0xb2 },
+ { 0xb3, 0xb3 },
+ { 0xb4, 0xb4 },
+ { 0x39c, 0xb5 }, /* lower */
+ { 0xb6, 0xb6 },
+ { 0xb7, 0xb7 },
+ { 0xb8, 0xb8 },
+ { 0xb9, 0xb9 },
+ { 0xba, 0xba },
+ { 0xbb, 0xbb },
+ { 0xbc, 0xbc },
+ { 0xbd, 0xbd },
+ { 0xbe, 0xbe },
+ { 0xbf, 0xbf },
+ { 0xc0, 0xe0 }, /* upper */
+ { 0xc1, 0xe1 }, /* upper */
+ { 0xc2, 0xe2 }, /* upper */
+ { 0xc3, 0xe3 }, /* upper */
+ { 0xc4, 0xe4 }, /* upper */
+ { 0xc5, 0xe5 }, /* upper */
+ { 0xc6, 0xe6 }, /* upper */
+ { 0xc7, 0xe7 }, /* upper */
+ { 0xc8, 0xe8 }, /* upper */
+ { 0xc9, 0xe9 }, /* upper */
+ { 0xca, 0xea }, /* upper */
+ { 0xcb, 0xeb }, /* upper */
+ { 0xcc, 0xec }, /* upper */
+ { 0xcd, 0xed }, /* upper */
+ { 0xce, 0xee }, /* upper */
+ { 0xcf, 0xef }, /* upper */
+ { 0xd0, 0xf0 }, /* upper */
+ { 0xd1, 0xf1 }, /* upper */
+ { 0xd2, 0xf2 }, /* upper */
+ { 0xd3, 0xf3 }, /* upper */
+ { 0xd4, 0xf4 }, /* upper */
+ { 0xd5, 0xf5 }, /* upper */
+ { 0xd6, 0xf6 }, /* upper */
+ { 0xd7, 0xd7 },
+ { 0xd8, 0xf8 }, /* upper */
+ { 0xd9, 0xf9 }, /* upper */
+ { 0xda, 0xfa }, /* upper */
+ { 0xdb, 0xfb }, /* upper */
+ { 0xdc, 0xfc }, /* upper */
+ { 0xdd, 0xfd }, /* upper */
+ { 0xde, 0xfe }, /* upper */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xc0, 0xe0 }, /* lower */
+ { 0xc1, 0xe1 }, /* lower */
+ { 0xc2, 0xe2 }, /* lower */
+ { 0xc3, 0xe3 }, /* lower */
+ { 0xc4, 0xe4 }, /* lower */
+ { 0xc5, 0xe5 }, /* lower */
+ { 0xc6, 0xe6 }, /* lower */
+ { 0xc7, 0xe7 }, /* lower */
+ { 0xc8, 0xe8 }, /* lower */
+ { 0xc9, 0xe9 }, /* lower */
+ { 0xca, 0xea }, /* lower */
+ { 0xcb, 0xeb }, /* lower */
+ { 0xcc, 0xec }, /* lower */
+ { 0xcd, 0xed }, /* lower */
+ { 0xce, 0xee }, /* lower */
+ { 0xcf, 0xef }, /* lower */
+ { 0xd0, 0xf0 }, /* lower */
+ { 0xd1, 0xf1 }, /* lower */
+ { 0xd2, 0xf2 }, /* lower */
+ { 0xd3, 0xf3 }, /* lower */
+ { 0xd4, 0xf4 }, /* lower */
+ { 0xd5, 0xf5 }, /* lower */
+ { 0xd6, 0xf6 }, /* lower */
+ { 0xf7, 0xf7 },
+ { 0xd8, 0xf8 }, /* lower */
+ { 0xd9, 0xf9 }, /* lower */
+ { 0xda, 0xfa }, /* lower */
+ { 0xdb, 0xfb }, /* lower */
+ { 0xdc, 0xfc }, /* lower */
+ { 0xdd, 0xfd }, /* lower */
+ { 0xde, 0xfe }, /* lower */
+ { 0x178, 0xff }, /* lower */
+};
+
+gli_case_block_t unigen_case_block_0x1[256] = {
+ { 0x100, 0x101 }, /* upper */
+ { 0x100, 0x101 }, /* lower */
+ { 0x102, 0x103 }, /* upper */
+ { 0x102, 0x103 }, /* lower */
+ { 0x104, 0x105 }, /* upper */
+ { 0x104, 0x105 }, /* lower */
+ { 0x106, 0x107 }, /* upper */
+ { 0x106, 0x107 }, /* lower */
+ { 0x108, 0x109 }, /* upper */
+ { 0x108, 0x109 }, /* lower */
+ { 0x10a, 0x10b }, /* upper */
+ { 0x10a, 0x10b }, /* lower */
+ { 0x10c, 0x10d }, /* upper */
+ { 0x10c, 0x10d }, /* lower */
+ { 0x10e, 0x10f }, /* upper */
+ { 0x10e, 0x10f }, /* lower */
+ { 0x110, 0x111 }, /* upper */
+ { 0x110, 0x111 }, /* lower */
+ { 0x112, 0x113 }, /* upper */
+ { 0x112, 0x113 }, /* lower */
+ { 0x114, 0x115 }, /* upper */
+ { 0x114, 0x115 }, /* lower */
+ { 0x116, 0x117 }, /* upper */
+ { 0x116, 0x117 }, /* lower */
+ { 0x118, 0x119 }, /* upper */
+ { 0x118, 0x119 }, /* lower */
+ { 0x11a, 0x11b }, /* upper */
+ { 0x11a, 0x11b }, /* lower */
+ { 0x11c, 0x11d }, /* upper */
+ { 0x11c, 0x11d }, /* lower */
+ { 0x11e, 0x11f }, /* upper */
+ { 0x11e, 0x11f }, /* lower */
+ { 0x120, 0x121 }, /* upper */
+ { 0x120, 0x121 }, /* lower */
+ { 0x122, 0x123 }, /* upper */
+ { 0x122, 0x123 }, /* lower */
+ { 0x124, 0x125 }, /* upper */
+ { 0x124, 0x125 }, /* lower */
+ { 0x126, 0x127 }, /* upper */
+ { 0x126, 0x127 }, /* lower */
+ { 0x128, 0x129 }, /* upper */
+ { 0x128, 0x129 }, /* lower */
+ { 0x12a, 0x12b }, /* upper */
+ { 0x12a, 0x12b }, /* lower */
+ { 0x12c, 0x12d }, /* upper */
+ { 0x12c, 0x12d }, /* lower */
+ { 0x12e, 0x12f }, /* upper */
+ { 0x12e, 0x12f }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x49, 0x131 }, /* lower */
+ { 0x132, 0x133 }, /* upper */
+ { 0x132, 0x133 }, /* lower */
+ { 0x134, 0x135 }, /* upper */
+ { 0x134, 0x135 }, /* lower */
+ { 0x136, 0x137 }, /* upper */
+ { 0x136, 0x137 }, /* lower */
+ { 0x138, 0x138 },
+ { 0x139, 0x13a }, /* upper */
+ { 0x139, 0x13a }, /* lower */
+ { 0x13b, 0x13c }, /* upper */
+ { 0x13b, 0x13c }, /* lower */
+ { 0x13d, 0x13e }, /* upper */
+ { 0x13d, 0x13e }, /* lower */
+ { 0x13f, 0x140 }, /* upper */
+ { 0x13f, 0x140 }, /* lower */
+ { 0x141, 0x142 }, /* upper */
+ { 0x141, 0x142 }, /* lower */
+ { 0x143, 0x144 }, /* upper */
+ { 0x143, 0x144 }, /* lower */
+ { 0x145, 0x146 }, /* upper */
+ { 0x145, 0x146 }, /* lower */
+ { 0x147, 0x148 }, /* upper */
+ { 0x147, 0x148 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x14a, 0x14b }, /* upper */
+ { 0x14a, 0x14b }, /* lower */
+ { 0x14c, 0x14d }, /* upper */
+ { 0x14c, 0x14d }, /* lower */
+ { 0x14e, 0x14f }, /* upper */
+ { 0x14e, 0x14f }, /* lower */
+ { 0x150, 0x151 }, /* upper */
+ { 0x150, 0x151 }, /* lower */
+ { 0x152, 0x153 }, /* upper */
+ { 0x152, 0x153 }, /* lower */
+ { 0x154, 0x155 }, /* upper */
+ { 0x154, 0x155 }, /* lower */
+ { 0x156, 0x157 }, /* upper */
+ { 0x156, 0x157 }, /* lower */
+ { 0x158, 0x159 }, /* upper */
+ { 0x158, 0x159 }, /* lower */
+ { 0x15a, 0x15b }, /* upper */
+ { 0x15a, 0x15b }, /* lower */
+ { 0x15c, 0x15d }, /* upper */
+ { 0x15c, 0x15d }, /* lower */
+ { 0x15e, 0x15f }, /* upper */
+ { 0x15e, 0x15f }, /* lower */
+ { 0x160, 0x161 }, /* upper */
+ { 0x160, 0x161 }, /* lower */
+ { 0x162, 0x163 }, /* upper */
+ { 0x162, 0x163 }, /* lower */
+ { 0x164, 0x165 }, /* upper */
+ { 0x164, 0x165 }, /* lower */
+ { 0x166, 0x167 }, /* upper */
+ { 0x166, 0x167 }, /* lower */
+ { 0x168, 0x169 }, /* upper */
+ { 0x168, 0x169 }, /* lower */
+ { 0x16a, 0x16b }, /* upper */
+ { 0x16a, 0x16b }, /* lower */
+ { 0x16c, 0x16d }, /* upper */
+ { 0x16c, 0x16d }, /* lower */
+ { 0x16e, 0x16f }, /* upper */
+ { 0x16e, 0x16f }, /* lower */
+ { 0x170, 0x171 }, /* upper */
+ { 0x170, 0x171 }, /* lower */
+ { 0x172, 0x173 }, /* upper */
+ { 0x172, 0x173 }, /* lower */
+ { 0x174, 0x175 }, /* upper */
+ { 0x174, 0x175 }, /* lower */
+ { 0x176, 0x177 }, /* upper */
+ { 0x176, 0x177 }, /* lower */
+ { 0x178, 0xff }, /* upper */
+ { 0x179, 0x17a }, /* upper */
+ { 0x179, 0x17a }, /* lower */
+ { 0x17b, 0x17c }, /* upper */
+ { 0x17b, 0x17c }, /* lower */
+ { 0x17d, 0x17e }, /* upper */
+ { 0x17d, 0x17e }, /* lower */
+ { 0x53, 0x17f }, /* lower */
+ { 0x180, 0x180 },
+ { 0x181, 0x253 }, /* upper */
+ { 0x182, 0x183 }, /* upper */
+ { 0x182, 0x183 }, /* lower */
+ { 0x184, 0x185 }, /* upper */
+ { 0x184, 0x185 }, /* lower */
+ { 0x186, 0x254 }, /* upper */
+ { 0x187, 0x188 }, /* upper */
+ { 0x187, 0x188 }, /* lower */
+ { 0x189, 0x256 }, /* upper */
+ { 0x18a, 0x257 }, /* upper */
+ { 0x18b, 0x18c }, /* upper */
+ { 0x18b, 0x18c }, /* lower */
+ { 0x18d, 0x18d },
+ { 0x18e, 0x1dd }, /* upper */
+ { 0x18f, 0x259 }, /* upper */
+ { 0x190, 0x25b }, /* upper */
+ { 0x191, 0x192 }, /* upper */
+ { 0x191, 0x192 }, /* lower */
+ { 0x193, 0x260 }, /* upper */
+ { 0x194, 0x263 }, /* upper */
+ { 0x1f6, 0x195 }, /* lower */
+ { 0x196, 0x269 }, /* upper */
+ { 0x197, 0x268 }, /* upper */
+ { 0x198, 0x199 }, /* upper */
+ { 0x198, 0x199 }, /* lower */
+ { 0x19a, 0x19a },
+ { 0x19b, 0x19b },
+ { 0x19c, 0x26f }, /* upper */
+ { 0x19d, 0x272 }, /* upper */
+ { 0x220, 0x19e }, /* lower */
+ { 0x19f, 0x275 }, /* upper */
+ { 0x1a0, 0x1a1 }, /* upper */
+ { 0x1a0, 0x1a1 }, /* lower */
+ { 0x1a2, 0x1a3 }, /* upper */
+ { 0x1a2, 0x1a3 }, /* lower */
+ { 0x1a4, 0x1a5 }, /* upper */
+ { 0x1a4, 0x1a5 }, /* lower */
+ { 0x1a6, 0x280 }, /* upper */
+ { 0x1a7, 0x1a8 }, /* upper */
+ { 0x1a7, 0x1a8 }, /* lower */
+ { 0x1a9, 0x283 }, /* upper */
+ { 0x1aa, 0x1aa },
+ { 0x1ab, 0x1ab },
+ { 0x1ac, 0x1ad }, /* upper */
+ { 0x1ac, 0x1ad }, /* lower */
+ { 0x1ae, 0x288 }, /* upper */
+ { 0x1af, 0x1b0 }, /* upper */
+ { 0x1af, 0x1b0 }, /* lower */
+ { 0x1b1, 0x28a }, /* upper */
+ { 0x1b2, 0x28b }, /* upper */
+ { 0x1b3, 0x1b4 }, /* upper */
+ { 0x1b3, 0x1b4 }, /* lower */
+ { 0x1b5, 0x1b6 }, /* upper */
+ { 0x1b5, 0x1b6 }, /* lower */
+ { 0x1b7, 0x292 }, /* upper */
+ { 0x1b8, 0x1b9 }, /* upper */
+ { 0x1b8, 0x1b9 }, /* lower */
+ { 0x1ba, 0x1ba },
+ { 0x1bb, 0x1bb },
+ { 0x1bc, 0x1bd }, /* upper */
+ { 0x1bc, 0x1bd }, /* lower */
+ { 0x1be, 0x1be },
+ { 0x1f7, 0x1bf }, /* lower */
+ { 0x1c0, 0x1c0 },
+ { 0x1c1, 0x1c1 },
+ { 0x1c2, 0x1c2 },
+ { 0x1c3, 0x1c3 },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1cd, 0x1ce }, /* upper */
+ { 0x1cd, 0x1ce }, /* lower */
+ { 0x1cf, 0x1d0 }, /* upper */
+ { 0x1cf, 0x1d0 }, /* lower */
+ { 0x1d1, 0x1d2 }, /* upper */
+ { 0x1d1, 0x1d2 }, /* lower */
+ { 0x1d3, 0x1d4 }, /* upper */
+ { 0x1d3, 0x1d4 }, /* lower */
+ { 0x1d5, 0x1d6 }, /* upper */
+ { 0x1d5, 0x1d6 }, /* lower */
+ { 0x1d7, 0x1d8 }, /* upper */
+ { 0x1d7, 0x1d8 }, /* lower */
+ { 0x1d9, 0x1da }, /* upper */
+ { 0x1d9, 0x1da }, /* lower */
+ { 0x1db, 0x1dc }, /* upper */
+ { 0x1db, 0x1dc }, /* lower */
+ { 0x18e, 0x1dd }, /* lower */
+ { 0x1de, 0x1df }, /* upper */
+ { 0x1de, 0x1df }, /* lower */
+ { 0x1e0, 0x1e1 }, /* upper */
+ { 0x1e0, 0x1e1 }, /* lower */
+ { 0x1e2, 0x1e3 }, /* upper */
+ { 0x1e2, 0x1e3 }, /* lower */
+ { 0x1e4, 0x1e5 }, /* upper */
+ { 0x1e4, 0x1e5 }, /* lower */
+ { 0x1e6, 0x1e7 }, /* upper */
+ { 0x1e6, 0x1e7 }, /* lower */
+ { 0x1e8, 0x1e9 }, /* upper */
+ { 0x1e8, 0x1e9 }, /* lower */
+ { 0x1ea, 0x1eb }, /* upper */
+ { 0x1ea, 0x1eb }, /* lower */
+ { 0x1ec, 0x1ed }, /* upper */
+ { 0x1ec, 0x1ed }, /* lower */
+ { 0x1ee, 0x1ef }, /* upper */
+ { 0x1ee, 0x1ef }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1f4, 0x1f5 }, /* upper */
+ { 0x1f4, 0x1f5 }, /* lower */
+ { 0x1f6, 0x195 }, /* upper */
+ { 0x1f7, 0x1bf }, /* upper */
+ { 0x1f8, 0x1f9 }, /* upper */
+ { 0x1f8, 0x1f9 }, /* lower */
+ { 0x1fa, 0x1fb }, /* upper */
+ { 0x1fa, 0x1fb }, /* lower */
+ { 0x1fc, 0x1fd }, /* upper */
+ { 0x1fc, 0x1fd }, /* lower */
+ { 0x1fe, 0x1ff }, /* upper */
+ { 0x1fe, 0x1ff }, /* lower */
+};
+
+gli_case_block_t unigen_case_block_0x2[256] = {
+ { 0x200, 0x201 }, /* upper */
+ { 0x200, 0x201 }, /* lower */
+ { 0x202, 0x203 }, /* upper */
+ { 0x202, 0x203 }, /* lower */
+ { 0x204, 0x205 }, /* upper */
+ { 0x204, 0x205 }, /* lower */
+ { 0x206, 0x207 }, /* upper */
+ { 0x206, 0x207 }, /* lower */
+ { 0x208, 0x209 }, /* upper */
+ { 0x208, 0x209 }, /* lower */
+ { 0x20a, 0x20b }, /* upper */
+ { 0x20a, 0x20b }, /* lower */
+ { 0x20c, 0x20d }, /* upper */
+ { 0x20c, 0x20d }, /* lower */
+ { 0x20e, 0x20f }, /* upper */
+ { 0x20e, 0x20f }, /* lower */
+ { 0x210, 0x211 }, /* upper */
+ { 0x210, 0x211 }, /* lower */
+ { 0x212, 0x213 }, /* upper */
+ { 0x212, 0x213 }, /* lower */
+ { 0x214, 0x215 }, /* upper */
+ { 0x214, 0x215 }, /* lower */
+ { 0x216, 0x217 }, /* upper */
+ { 0x216, 0x217 }, /* lower */
+ { 0x218, 0x219 }, /* upper */
+ { 0x218, 0x219 }, /* lower */
+ { 0x21a, 0x21b }, /* upper */
+ { 0x21a, 0x21b }, /* lower */
+ { 0x21c, 0x21d }, /* upper */
+ { 0x21c, 0x21d }, /* lower */
+ { 0x21e, 0x21f }, /* upper */
+ { 0x21e, 0x21f }, /* lower */
+ { 0x220, 0x19e }, /* upper */
+ { 0x221, 0x221 },
+ { 0x222, 0x223 }, /* upper */
+ { 0x222, 0x223 }, /* lower */
+ { 0x224, 0x225 }, /* upper */
+ { 0x224, 0x225 }, /* lower */
+ { 0x226, 0x227 }, /* upper */
+ { 0x226, 0x227 }, /* lower */
+ { 0x228, 0x229 }, /* upper */
+ { 0x228, 0x229 }, /* lower */
+ { 0x22a, 0x22b }, /* upper */
+ { 0x22a, 0x22b }, /* lower */
+ { 0x22c, 0x22d }, /* upper */
+ { 0x22c, 0x22d }, /* lower */
+ { 0x22e, 0x22f }, /* upper */
+ { 0x22e, 0x22f }, /* lower */
+ { 0x230, 0x231 }, /* upper */
+ { 0x230, 0x231 }, /* lower */
+ { 0x232, 0x233 }, /* upper */
+ { 0x232, 0x233 }, /* lower */
+ { 0x234, 0x234 },
+ { 0x235, 0x235 },
+ { 0x236, 0x236 },
+ { 0x237, 0x237 },
+ { 0x238, 0x238 },
+ { 0x239, 0x239 },
+ { 0x23a, 0x23a },
+ { 0x23b, 0x23b },
+ { 0x23c, 0x23c },
+ { 0x23d, 0x23d },
+ { 0x23e, 0x23e },
+ { 0x23f, 0x23f },
+ { 0x240, 0x240 },
+ { 0x241, 0x241 },
+ { 0x242, 0x242 },
+ { 0x243, 0x243 },
+ { 0x244, 0x244 },
+ { 0x245, 0x245 },
+ { 0x246, 0x246 },
+ { 0x247, 0x247 },
+ { 0x248, 0x248 },
+ { 0x249, 0x249 },
+ { 0x24a, 0x24a },
+ { 0x24b, 0x24b },
+ { 0x24c, 0x24c },
+ { 0x24d, 0x24d },
+ { 0x24e, 0x24e },
+ { 0x24f, 0x24f },
+ { 0x250, 0x250 },
+ { 0x251, 0x251 },
+ { 0x252, 0x252 },
+ { 0x181, 0x253 }, /* lower */
+ { 0x186, 0x254 }, /* lower */
+ { 0x255, 0x255 },
+ { 0x189, 0x256 }, /* lower */
+ { 0x18a, 0x257 }, /* lower */
+ { 0x258, 0x258 },
+ { 0x18f, 0x259 }, /* lower */
+ { 0x25a, 0x25a },
+ { 0x190, 0x25b }, /* lower */
+ { 0x25c, 0x25c },
+ { 0x25d, 0x25d },
+ { 0x25e, 0x25e },
+ { 0x25f, 0x25f },
+ { 0x193, 0x260 }, /* lower */
+ { 0x261, 0x261 },
+ { 0x262, 0x262 },
+ { 0x194, 0x263 }, /* lower */
+ { 0x264, 0x264 },
+ { 0x265, 0x265 },
+ { 0x266, 0x266 },
+ { 0x267, 0x267 },
+ { 0x197, 0x268 }, /* lower */
+ { 0x196, 0x269 }, /* lower */
+ { 0x26a, 0x26a },
+ { 0x26b, 0x26b },
+ { 0x26c, 0x26c },
+ { 0x26d, 0x26d },
+ { 0x26e, 0x26e },
+ { 0x19c, 0x26f }, /* lower */
+ { 0x270, 0x270 },
+ { 0x271, 0x271 },
+ { 0x19d, 0x272 }, /* lower */
+ { 0x273, 0x273 },
+ { 0x274, 0x274 },
+ { 0x19f, 0x275 }, /* lower */
+ { 0x276, 0x276 },
+ { 0x277, 0x277 },
+ { 0x278, 0x278 },
+ { 0x279, 0x279 },
+ { 0x27a, 0x27a },
+ { 0x27b, 0x27b },
+ { 0x27c, 0x27c },
+ { 0x27d, 0x27d },
+ { 0x27e, 0x27e },
+ { 0x27f, 0x27f },
+ { 0x1a6, 0x280 }, /* lower */
+ { 0x281, 0x281 },
+ { 0x282, 0x282 },
+ { 0x1a9, 0x283 }, /* lower */
+ { 0x284, 0x284 },
+ { 0x285, 0x285 },
+ { 0x286, 0x286 },
+ { 0x287, 0x287 },
+ { 0x1ae, 0x288 }, /* lower */
+ { 0x289, 0x289 },
+ { 0x1b1, 0x28a }, /* lower */
+ { 0x1b2, 0x28b }, /* lower */
+ { 0x28c, 0x28c },
+ { 0x28d, 0x28d },
+ { 0x28e, 0x28e },
+ { 0x28f, 0x28f },
+ { 0x290, 0x290 },
+ { 0x291, 0x291 },
+ { 0x1b7, 0x292 }, /* lower */
+ { 0x293, 0x293 },
+ { 0x294, 0x294 },
+ { 0x295, 0x295 },
+ { 0x296, 0x296 },
+ { 0x297, 0x297 },
+ { 0x298, 0x298 },
+ { 0x299, 0x299 },
+ { 0x29a, 0x29a },
+ { 0x29b, 0x29b },
+ { 0x29c, 0x29c },
+ { 0x29d, 0x29d },
+ { 0x29e, 0x29e },
+ { 0x29f, 0x29f },
+ { 0x2a0, 0x2a0 },
+ { 0x2a1, 0x2a1 },
+ { 0x2a2, 0x2a2 },
+ { 0x2a3, 0x2a3 },
+ { 0x2a4, 0x2a4 },
+ { 0x2a5, 0x2a5 },
+ { 0x2a6, 0x2a6 },
+ { 0x2a7, 0x2a7 },
+ { 0x2a8, 0x2a8 },
+ { 0x2a9, 0x2a9 },
+ { 0x2aa, 0x2aa },
+ { 0x2ab, 0x2ab },
+ { 0x2ac, 0x2ac },
+ { 0x2ad, 0x2ad },
+ { 0x2ae, 0x2ae },
+ { 0x2af, 0x2af },
+ { 0x2b0, 0x2b0 },
+ { 0x2b1, 0x2b1 },
+ { 0x2b2, 0x2b2 },
+ { 0x2b3, 0x2b3 },
+ { 0x2b4, 0x2b4 },
+ { 0x2b5, 0x2b5 },
+ { 0x2b6, 0x2b6 },
+ { 0x2b7, 0x2b7 },
+ { 0x2b8, 0x2b8 },
+ { 0x2b9, 0x2b9 },
+ { 0x2ba, 0x2ba },
+ { 0x2bb, 0x2bb },
+ { 0x2bc, 0x2bc },
+ { 0x2bd, 0x2bd },
+ { 0x2be, 0x2be },
+ { 0x2bf, 0x2bf },
+ { 0x2c0, 0x2c0 },
+ { 0x2c1, 0x2c1 },
+ { 0x2c2, 0x2c2 },
+ { 0x2c3, 0x2c3 },
+ { 0x2c4, 0x2c4 },
+ { 0x2c5, 0x2c5 },
+ { 0x2c6, 0x2c6 },
+ { 0x2c7, 0x2c7 },
+ { 0x2c8, 0x2c8 },
+ { 0x2c9, 0x2c9 },
+ { 0x2ca, 0x2ca },
+ { 0x2cb, 0x2cb },
+ { 0x2cc, 0x2cc },
+ { 0x2cd, 0x2cd },
+ { 0x2ce, 0x2ce },
+ { 0x2cf, 0x2cf },
+ { 0x2d0, 0x2d0 },
+ { 0x2d1, 0x2d1 },
+ { 0x2d2, 0x2d2 },
+ { 0x2d3, 0x2d3 },
+ { 0x2d4, 0x2d4 },
+ { 0x2d5, 0x2d5 },
+ { 0x2d6, 0x2d6 },
+ { 0x2d7, 0x2d7 },
+ { 0x2d8, 0x2d8 },
+ { 0x2d9, 0x2d9 },
+ { 0x2da, 0x2da },
+ { 0x2db, 0x2db },
+ { 0x2dc, 0x2dc },
+ { 0x2dd, 0x2dd },
+ { 0x2de, 0x2de },
+ { 0x2df, 0x2df },
+ { 0x2e0, 0x2e0 },
+ { 0x2e1, 0x2e1 },
+ { 0x2e2, 0x2e2 },
+ { 0x2e3, 0x2e3 },
+ { 0x2e4, 0x2e4 },
+ { 0x2e5, 0x2e5 },
+ { 0x2e6, 0x2e6 },
+ { 0x2e7, 0x2e7 },
+ { 0x2e8, 0x2e8 },
+ { 0x2e9, 0x2e9 },
+ { 0x2ea, 0x2ea },
+ { 0x2eb, 0x2eb },
+ { 0x2ec, 0x2ec },
+ { 0x2ed, 0x2ed },
+ { 0x2ee, 0x2ee },
+ { 0x2ef, 0x2ef },
+ { 0x2f0, 0x2f0 },
+ { 0x2f1, 0x2f1 },
+ { 0x2f2, 0x2f2 },
+ { 0x2f3, 0x2f3 },
+ { 0x2f4, 0x2f4 },
+ { 0x2f5, 0x2f5 },
+ { 0x2f6, 0x2f6 },
+ { 0x2f7, 0x2f7 },
+ { 0x2f8, 0x2f8 },
+ { 0x2f9, 0x2f9 },
+ { 0x2fa, 0x2fa },
+ { 0x2fb, 0x2fb },
+ { 0x2fc, 0x2fc },
+ { 0x2fd, 0x2fd },
+ { 0x2fe, 0x2fe },
+ { 0x2ff, 0x2ff },
+};
+
+gli_case_block_t unigen_case_block_0x3[256] = {
+ { 0x300, 0x300 },
+ { 0x301, 0x301 },
+ { 0x302, 0x302 },
+ { 0x303, 0x303 },
+ { 0x304, 0x304 },
+ { 0x305, 0x305 },
+ { 0x306, 0x306 },
+ { 0x307, 0x307 },
+ { 0x308, 0x308 },
+ { 0x309, 0x309 },
+ { 0x30a, 0x30a },
+ { 0x30b, 0x30b },
+ { 0x30c, 0x30c },
+ { 0x30d, 0x30d },
+ { 0x30e, 0x30e },
+ { 0x30f, 0x30f },
+ { 0x310, 0x310 },
+ { 0x311, 0x311 },
+ { 0x312, 0x312 },
+ { 0x313, 0x313 },
+ { 0x314, 0x314 },
+ { 0x315, 0x315 },
+ { 0x316, 0x316 },
+ { 0x317, 0x317 },
+ { 0x318, 0x318 },
+ { 0x319, 0x319 },
+ { 0x31a, 0x31a },
+ { 0x31b, 0x31b },
+ { 0x31c, 0x31c },
+ { 0x31d, 0x31d },
+ { 0x31e, 0x31e },
+ { 0x31f, 0x31f },
+ { 0x320, 0x320 },
+ { 0x321, 0x321 },
+ { 0x322, 0x322 },
+ { 0x323, 0x323 },
+ { 0x324, 0x324 },
+ { 0x325, 0x325 },
+ { 0x326, 0x326 },
+ { 0x327, 0x327 },
+ { 0x328, 0x328 },
+ { 0x329, 0x329 },
+ { 0x32a, 0x32a },
+ { 0x32b, 0x32b },
+ { 0x32c, 0x32c },
+ { 0x32d, 0x32d },
+ { 0x32e, 0x32e },
+ { 0x32f, 0x32f },
+ { 0x330, 0x330 },
+ { 0x331, 0x331 },
+ { 0x332, 0x332 },
+ { 0x333, 0x333 },
+ { 0x334, 0x334 },
+ { 0x335, 0x335 },
+ { 0x336, 0x336 },
+ { 0x337, 0x337 },
+ { 0x338, 0x338 },
+ { 0x339, 0x339 },
+ { 0x33a, 0x33a },
+ { 0x33b, 0x33b },
+ { 0x33c, 0x33c },
+ { 0x33d, 0x33d },
+ { 0x33e, 0x33e },
+ { 0x33f, 0x33f },
+ { 0x340, 0x340 },
+ { 0x341, 0x341 },
+ { 0x342, 0x342 },
+ { 0x343, 0x343 },
+ { 0x344, 0x344 },
+ { 0x399, 0x345 }, /* lower */
+ { 0x346, 0x346 },
+ { 0x347, 0x347 },
+ { 0x348, 0x348 },
+ { 0x349, 0x349 },
+ { 0x34a, 0x34a },
+ { 0x34b, 0x34b },
+ { 0x34c, 0x34c },
+ { 0x34d, 0x34d },
+ { 0x34e, 0x34e },
+ { 0x34f, 0x34f },
+ { 0x350, 0x350 },
+ { 0x351, 0x351 },
+ { 0x352, 0x352 },
+ { 0x353, 0x353 },
+ { 0x354, 0x354 },
+ { 0x355, 0x355 },
+ { 0x356, 0x356 },
+ { 0x357, 0x357 },
+ { 0x358, 0x358 },
+ { 0x359, 0x359 },
+ { 0x35a, 0x35a },
+ { 0x35b, 0x35b },
+ { 0x35c, 0x35c },
+ { 0x35d, 0x35d },
+ { 0x35e, 0x35e },
+ { 0x35f, 0x35f },
+ { 0x360, 0x360 },
+ { 0x361, 0x361 },
+ { 0x362, 0x362 },
+ { 0x363, 0x363 },
+ { 0x364, 0x364 },
+ { 0x365, 0x365 },
+ { 0x366, 0x366 },
+ { 0x367, 0x367 },
+ { 0x368, 0x368 },
+ { 0x369, 0x369 },
+ { 0x36a, 0x36a },
+ { 0x36b, 0x36b },
+ { 0x36c, 0x36c },
+ { 0x36d, 0x36d },
+ { 0x36e, 0x36e },
+ { 0x36f, 0x36f },
+ { 0x370, 0x370 },
+ { 0x371, 0x371 },
+ { 0x372, 0x372 },
+ { 0x373, 0x373 },
+ { 0x374, 0x374 },
+ { 0x375, 0x375 },
+ { 0x376, 0x376 },
+ { 0x377, 0x377 },
+ { 0x378, 0x378 },
+ { 0x379, 0x379 },
+ { 0x37a, 0x37a },
+ { 0x37b, 0x37b },
+ { 0x37c, 0x37c },
+ { 0x37d, 0x37d },
+ { 0x37e, 0x37e },
+ { 0x37f, 0x37f },
+ { 0x380, 0x380 },
+ { 0x381, 0x381 },
+ { 0x382, 0x382 },
+ { 0x383, 0x383 },
+ { 0x384, 0x384 },
+ { 0x385, 0x385 },
+ { 0x386, 0x3ac }, /* upper */
+ { 0x387, 0x387 },
+ { 0x388, 0x3ad }, /* upper */
+ { 0x389, 0x3ae }, /* upper */
+ { 0x38a, 0x3af }, /* upper */
+ { 0x38b, 0x38b },
+ { 0x38c, 0x3cc }, /* upper */
+ { 0x38d, 0x38d },
+ { 0x38e, 0x3cd }, /* upper */
+ { 0x38f, 0x3ce }, /* upper */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x391, 0x3b1 }, /* upper */
+ { 0x392, 0x3b2 }, /* upper */
+ { 0x393, 0x3b3 }, /* upper */
+ { 0x394, 0x3b4 }, /* upper */
+ { 0x395, 0x3b5 }, /* upper */
+ { 0x396, 0x3b6 }, /* upper */
+ { 0x397, 0x3b7 }, /* upper */
+ { 0x398, 0x3b8 }, /* upper */
+ { 0x399, 0x3b9 }, /* upper */
+ { 0x39a, 0x3ba }, /* upper */
+ { 0x39b, 0x3bb }, /* upper */
+ { 0x39c, 0x3bc }, /* upper */
+ { 0x39d, 0x3bd }, /* upper */
+ { 0x39e, 0x3be }, /* upper */
+ { 0x39f, 0x3bf }, /* upper */
+ { 0x3a0, 0x3c0 }, /* upper */
+ { 0x3a1, 0x3c1 }, /* upper */
+ { 0x3a2, 0x3a2 },
+ { 0x3a3, 0x3c3 }, /* upper */
+ { 0x3a4, 0x3c4 }, /* upper */
+ { 0x3a5, 0x3c5 }, /* upper */
+ { 0x3a6, 0x3c6 }, /* upper */
+ { 0x3a7, 0x3c7 }, /* upper */
+ { 0x3a8, 0x3c8 }, /* upper */
+ { 0x3a9, 0x3c9 }, /* upper */
+ { 0x3aa, 0x3ca }, /* upper */
+ { 0x3ab, 0x3cb }, /* upper */
+ { 0x386, 0x3ac }, /* lower */
+ { 0x388, 0x3ad }, /* lower */
+ { 0x389, 0x3ae }, /* lower */
+ { 0x38a, 0x3af }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x391, 0x3b1 }, /* lower */
+ { 0x392, 0x3b2 }, /* lower */
+ { 0x393, 0x3b3 }, /* lower */
+ { 0x394, 0x3b4 }, /* lower */
+ { 0x395, 0x3b5 }, /* lower */
+ { 0x396, 0x3b6 }, /* lower */
+ { 0x397, 0x3b7 }, /* lower */
+ { 0x398, 0x3b8 }, /* lower */
+ { 0x399, 0x3b9 }, /* lower */
+ { 0x39a, 0x3ba }, /* lower */
+ { 0x39b, 0x3bb }, /* lower */
+ { 0x39c, 0x3bc }, /* lower */
+ { 0x39d, 0x3bd }, /* lower */
+ { 0x39e, 0x3be }, /* lower */
+ { 0x39f, 0x3bf }, /* lower */
+ { 0x3a0, 0x3c0 }, /* lower */
+ { 0x3a1, 0x3c1 }, /* lower */
+ { 0x3a3, 0x3c2 }, /* lower */
+ { 0x3a3, 0x3c3 }, /* lower */
+ { 0x3a4, 0x3c4 }, /* lower */
+ { 0x3a5, 0x3c5 }, /* lower */
+ { 0x3a6, 0x3c6 }, /* lower */
+ { 0x3a7, 0x3c7 }, /* lower */
+ { 0x3a8, 0x3c8 }, /* lower */
+ { 0x3a9, 0x3c9 }, /* lower */
+ { 0x3aa, 0x3ca }, /* lower */
+ { 0x3ab, 0x3cb }, /* lower */
+ { 0x38c, 0x3cc }, /* lower */
+ { 0x38e, 0x3cd }, /* lower */
+ { 0x38f, 0x3ce }, /* lower */
+ { 0x3cf, 0x3cf },
+ { 0x392, 0x3d0 }, /* lower */
+ { 0x398, 0x3d1 }, /* lower */
+ { 0x3d2, 0x3d2 },
+ { 0x3d3, 0x3d3 },
+ { 0x3d4, 0x3d4 },
+ { 0x3a6, 0x3d5 }, /* lower */
+ { 0x3a0, 0x3d6 }, /* lower */
+ { 0x3d7, 0x3d7 },
+ { 0x3d8, 0x3d9 }, /* upper */
+ { 0x3d8, 0x3d9 }, /* lower */
+ { 0x3da, 0x3db }, /* upper */
+ { 0x3da, 0x3db }, /* lower */
+ { 0x3dc, 0x3dd }, /* upper */
+ { 0x3dc, 0x3dd }, /* lower */
+ { 0x3de, 0x3df }, /* upper */
+ { 0x3de, 0x3df }, /* lower */
+ { 0x3e0, 0x3e1 }, /* upper */
+ { 0x3e0, 0x3e1 }, /* lower */
+ { 0x3e2, 0x3e3 }, /* upper */
+ { 0x3e2, 0x3e3 }, /* lower */
+ { 0x3e4, 0x3e5 }, /* upper */
+ { 0x3e4, 0x3e5 }, /* lower */
+ { 0x3e6, 0x3e7 }, /* upper */
+ { 0x3e6, 0x3e7 }, /* lower */
+ { 0x3e8, 0x3e9 }, /* upper */
+ { 0x3e8, 0x3e9 }, /* lower */
+ { 0x3ea, 0x3eb }, /* upper */
+ { 0x3ea, 0x3eb }, /* lower */
+ { 0x3ec, 0x3ed }, /* upper */
+ { 0x3ec, 0x3ed }, /* lower */
+ { 0x3ee, 0x3ef }, /* upper */
+ { 0x3ee, 0x3ef }, /* lower */
+ { 0x39a, 0x3f0 }, /* lower */
+ { 0x3a1, 0x3f1 }, /* lower */
+ { 0x3f9, 0x3f2 }, /* lower */
+ { 0x3f3, 0x3f3 },
+ { 0x3f4, 0x3b8 }, /* upper */
+ { 0x395, 0x3f5 }, /* lower */
+ { 0x3f6, 0x3f6 },
+ { 0x3f7, 0x3f8 }, /* upper */
+ { 0x3f7, 0x3f8 }, /* lower */
+ { 0x3f9, 0x3f2 }, /* upper */
+ { 0x3fa, 0x3fb }, /* upper */
+ { 0x3fa, 0x3fb }, /* lower */
+ { 0x3fc, 0x3fc },
+ { 0x3fd, 0x3fd },
+ { 0x3fe, 0x3fe },
+ { 0x3ff, 0x3ff },
+};
+
+gli_case_block_t unigen_case_block_0x4[256] = {
+ { 0x400, 0x450 }, /* upper */
+ { 0x401, 0x451 }, /* upper */
+ { 0x402, 0x452 }, /* upper */
+ { 0x403, 0x453 }, /* upper */
+ { 0x404, 0x454 }, /* upper */
+ { 0x405, 0x455 }, /* upper */
+ { 0x406, 0x456 }, /* upper */
+ { 0x407, 0x457 }, /* upper */
+ { 0x408, 0x458 }, /* upper */
+ { 0x409, 0x459 }, /* upper */
+ { 0x40a, 0x45a }, /* upper */
+ { 0x40b, 0x45b }, /* upper */
+ { 0x40c, 0x45c }, /* upper */
+ { 0x40d, 0x45d }, /* upper */
+ { 0x40e, 0x45e }, /* upper */
+ { 0x40f, 0x45f }, /* upper */
+ { 0x410, 0x430 }, /* upper */
+ { 0x411, 0x431 }, /* upper */
+ { 0x412, 0x432 }, /* upper */
+ { 0x413, 0x433 }, /* upper */
+ { 0x414, 0x434 }, /* upper */
+ { 0x415, 0x435 }, /* upper */
+ { 0x416, 0x436 }, /* upper */
+ { 0x417, 0x437 }, /* upper */
+ { 0x418, 0x438 }, /* upper */
+ { 0x419, 0x439 }, /* upper */
+ { 0x41a, 0x43a }, /* upper */
+ { 0x41b, 0x43b }, /* upper */
+ { 0x41c, 0x43c }, /* upper */
+ { 0x41d, 0x43d }, /* upper */
+ { 0x41e, 0x43e }, /* upper */
+ { 0x41f, 0x43f }, /* upper */
+ { 0x420, 0x440 }, /* upper */
+ { 0x421, 0x441 }, /* upper */
+ { 0x422, 0x442 }, /* upper */
+ { 0x423, 0x443 }, /* upper */
+ { 0x424, 0x444 }, /* upper */
+ { 0x425, 0x445 }, /* upper */
+ { 0x426, 0x446 }, /* upper */
+ { 0x427, 0x447 }, /* upper */
+ { 0x428, 0x448 }, /* upper */
+ { 0x429, 0x449 }, /* upper */
+ { 0x42a, 0x44a }, /* upper */
+ { 0x42b, 0x44b }, /* upper */
+ { 0x42c, 0x44c }, /* upper */
+ { 0x42d, 0x44d }, /* upper */
+ { 0x42e, 0x44e }, /* upper */
+ { 0x42f, 0x44f }, /* upper */
+ { 0x410, 0x430 }, /* lower */
+ { 0x411, 0x431 }, /* lower */
+ { 0x412, 0x432 }, /* lower */
+ { 0x413, 0x433 }, /* lower */
+ { 0x414, 0x434 }, /* lower */
+ { 0x415, 0x435 }, /* lower */
+ { 0x416, 0x436 }, /* lower */
+ { 0x417, 0x437 }, /* lower */
+ { 0x418, 0x438 }, /* lower */
+ { 0x419, 0x439 }, /* lower */
+ { 0x41a, 0x43a }, /* lower */
+ { 0x41b, 0x43b }, /* lower */
+ { 0x41c, 0x43c }, /* lower */
+ { 0x41d, 0x43d }, /* lower */
+ { 0x41e, 0x43e }, /* lower */
+ { 0x41f, 0x43f }, /* lower */
+ { 0x420, 0x440 }, /* lower */
+ { 0x421, 0x441 }, /* lower */
+ { 0x422, 0x442 }, /* lower */
+ { 0x423, 0x443 }, /* lower */
+ { 0x424, 0x444 }, /* lower */
+ { 0x425, 0x445 }, /* lower */
+ { 0x426, 0x446 }, /* lower */
+ { 0x427, 0x447 }, /* lower */
+ { 0x428, 0x448 }, /* lower */
+ { 0x429, 0x449 }, /* lower */
+ { 0x42a, 0x44a }, /* lower */
+ { 0x42b, 0x44b }, /* lower */
+ { 0x42c, 0x44c }, /* lower */
+ { 0x42d, 0x44d }, /* lower */
+ { 0x42e, 0x44e }, /* lower */
+ { 0x42f, 0x44f }, /* lower */
+ { 0x400, 0x450 }, /* lower */
+ { 0x401, 0x451 }, /* lower */
+ { 0x402, 0x452 }, /* lower */
+ { 0x403, 0x453 }, /* lower */
+ { 0x404, 0x454 }, /* lower */
+ { 0x405, 0x455 }, /* lower */
+ { 0x406, 0x456 }, /* lower */
+ { 0x407, 0x457 }, /* lower */
+ { 0x408, 0x458 }, /* lower */
+ { 0x409, 0x459 }, /* lower */
+ { 0x40a, 0x45a }, /* lower */
+ { 0x40b, 0x45b }, /* lower */
+ { 0x40c, 0x45c }, /* lower */
+ { 0x40d, 0x45d }, /* lower */
+ { 0x40e, 0x45e }, /* lower */
+ { 0x40f, 0x45f }, /* lower */
+ { 0x460, 0x461 }, /* upper */
+ { 0x460, 0x461 }, /* lower */
+ { 0x462, 0x463 }, /* upper */
+ { 0x462, 0x463 }, /* lower */
+ { 0x464, 0x465 }, /* upper */
+ { 0x464, 0x465 }, /* lower */
+ { 0x466, 0x467 }, /* upper */
+ { 0x466, 0x467 }, /* lower */
+ { 0x468, 0x469 }, /* upper */
+ { 0x468, 0x469 }, /* lower */
+ { 0x46a, 0x46b }, /* upper */
+ { 0x46a, 0x46b }, /* lower */
+ { 0x46c, 0x46d }, /* upper */
+ { 0x46c, 0x46d }, /* lower */
+ { 0x46e, 0x46f }, /* upper */
+ { 0x46e, 0x46f }, /* lower */
+ { 0x470, 0x471 }, /* upper */
+ { 0x470, 0x471 }, /* lower */
+ { 0x472, 0x473 }, /* upper */
+ { 0x472, 0x473 }, /* lower */
+ { 0x474, 0x475 }, /* upper */
+ { 0x474, 0x475 }, /* lower */
+ { 0x476, 0x477 }, /* upper */
+ { 0x476, 0x477 }, /* lower */
+ { 0x478, 0x479 }, /* upper */
+ { 0x478, 0x479 }, /* lower */
+ { 0x47a, 0x47b }, /* upper */
+ { 0x47a, 0x47b }, /* lower */
+ { 0x47c, 0x47d }, /* upper */
+ { 0x47c, 0x47d }, /* lower */
+ { 0x47e, 0x47f }, /* upper */
+ { 0x47e, 0x47f }, /* lower */
+ { 0x480, 0x481 }, /* upper */
+ { 0x480, 0x481 }, /* lower */
+ { 0x482, 0x482 },
+ { 0x483, 0x483 },
+ { 0x484, 0x484 },
+ { 0x485, 0x485 },
+ { 0x486, 0x486 },
+ { 0x487, 0x487 },
+ { 0x488, 0x488 },
+ { 0x489, 0x489 },
+ { 0x48a, 0x48b }, /* upper */
+ { 0x48a, 0x48b }, /* lower */
+ { 0x48c, 0x48d }, /* upper */
+ { 0x48c, 0x48d }, /* lower */
+ { 0x48e, 0x48f }, /* upper */
+ { 0x48e, 0x48f }, /* lower */
+ { 0x490, 0x491 }, /* upper */
+ { 0x490, 0x491 }, /* lower */
+ { 0x492, 0x493 }, /* upper */
+ { 0x492, 0x493 }, /* lower */
+ { 0x494, 0x495 }, /* upper */
+ { 0x494, 0x495 }, /* lower */
+ { 0x496, 0x497 }, /* upper */
+ { 0x496, 0x497 }, /* lower */
+ { 0x498, 0x499 }, /* upper */
+ { 0x498, 0x499 }, /* lower */
+ { 0x49a, 0x49b }, /* upper */
+ { 0x49a, 0x49b }, /* lower */
+ { 0x49c, 0x49d }, /* upper */
+ { 0x49c, 0x49d }, /* lower */
+ { 0x49e, 0x49f }, /* upper */
+ { 0x49e, 0x49f }, /* lower */
+ { 0x4a0, 0x4a1 }, /* upper */
+ { 0x4a0, 0x4a1 }, /* lower */
+ { 0x4a2, 0x4a3 }, /* upper */
+ { 0x4a2, 0x4a3 }, /* lower */
+ { 0x4a4, 0x4a5 }, /* upper */
+ { 0x4a4, 0x4a5 }, /* lower */
+ { 0x4a6, 0x4a7 }, /* upper */
+ { 0x4a6, 0x4a7 }, /* lower */
+ { 0x4a8, 0x4a9 }, /* upper */
+ { 0x4a8, 0x4a9 }, /* lower */
+ { 0x4aa, 0x4ab }, /* upper */
+ { 0x4aa, 0x4ab }, /* lower */
+ { 0x4ac, 0x4ad }, /* upper */
+ { 0x4ac, 0x4ad }, /* lower */
+ { 0x4ae, 0x4af }, /* upper */
+ { 0x4ae, 0x4af }, /* lower */
+ { 0x4b0, 0x4b1 }, /* upper */
+ { 0x4b0, 0x4b1 }, /* lower */
+ { 0x4b2, 0x4b3 }, /* upper */
+ { 0x4b2, 0x4b3 }, /* lower */
+ { 0x4b4, 0x4b5 }, /* upper */
+ { 0x4b4, 0x4b5 }, /* lower */
+ { 0x4b6, 0x4b7 }, /* upper */
+ { 0x4b6, 0x4b7 }, /* lower */
+ { 0x4b8, 0x4b9 }, /* upper */
+ { 0x4b8, 0x4b9 }, /* lower */
+ { 0x4ba, 0x4bb }, /* upper */
+ { 0x4ba, 0x4bb }, /* lower */
+ { 0x4bc, 0x4bd }, /* upper */
+ { 0x4bc, 0x4bd }, /* lower */
+ { 0x4be, 0x4bf }, /* upper */
+ { 0x4be, 0x4bf }, /* lower */
+ { 0x4c0, 0x4c0 },
+ { 0x4c1, 0x4c2 }, /* upper */
+ { 0x4c1, 0x4c2 }, /* lower */
+ { 0x4c3, 0x4c4 }, /* upper */
+ { 0x4c3, 0x4c4 }, /* lower */
+ { 0x4c5, 0x4c6 }, /* upper */
+ { 0x4c5, 0x4c6 }, /* lower */
+ { 0x4c7, 0x4c8 }, /* upper */
+ { 0x4c7, 0x4c8 }, /* lower */
+ { 0x4c9, 0x4ca }, /* upper */
+ { 0x4c9, 0x4ca }, /* lower */
+ { 0x4cb, 0x4cc }, /* upper */
+ { 0x4cb, 0x4cc }, /* lower */
+ { 0x4cd, 0x4ce }, /* upper */
+ { 0x4cd, 0x4ce }, /* lower */
+ { 0x4cf, 0x4cf },
+ { 0x4d0, 0x4d1 }, /* upper */
+ { 0x4d0, 0x4d1 }, /* lower */
+ { 0x4d2, 0x4d3 }, /* upper */
+ { 0x4d2, 0x4d3 }, /* lower */
+ { 0x4d4, 0x4d5 }, /* upper */
+ { 0x4d4, 0x4d5 }, /* lower */
+ { 0x4d6, 0x4d7 }, /* upper */
+ { 0x4d6, 0x4d7 }, /* lower */
+ { 0x4d8, 0x4d9 }, /* upper */
+ { 0x4d8, 0x4d9 }, /* lower */
+ { 0x4da, 0x4db }, /* upper */
+ { 0x4da, 0x4db }, /* lower */
+ { 0x4dc, 0x4dd }, /* upper */
+ { 0x4dc, 0x4dd }, /* lower */
+ { 0x4de, 0x4df }, /* upper */
+ { 0x4de, 0x4df }, /* lower */
+ { 0x4e0, 0x4e1 }, /* upper */
+ { 0x4e0, 0x4e1 }, /* lower */
+ { 0x4e2, 0x4e3 }, /* upper */
+ { 0x4e2, 0x4e3 }, /* lower */
+ { 0x4e4, 0x4e5 }, /* upper */
+ { 0x4e4, 0x4e5 }, /* lower */
+ { 0x4e6, 0x4e7 }, /* upper */
+ { 0x4e6, 0x4e7 }, /* lower */
+ { 0x4e8, 0x4e9 }, /* upper */
+ { 0x4e8, 0x4e9 }, /* lower */
+ { 0x4ea, 0x4eb }, /* upper */
+ { 0x4ea, 0x4eb }, /* lower */
+ { 0x4ec, 0x4ed }, /* upper */
+ { 0x4ec, 0x4ed }, /* lower */
+ { 0x4ee, 0x4ef }, /* upper */
+ { 0x4ee, 0x4ef }, /* lower */
+ { 0x4f0, 0x4f1 }, /* upper */
+ { 0x4f0, 0x4f1 }, /* lower */
+ { 0x4f2, 0x4f3 }, /* upper */
+ { 0x4f2, 0x4f3 }, /* lower */
+ { 0x4f4, 0x4f5 }, /* upper */
+ { 0x4f4, 0x4f5 }, /* lower */
+ { 0x4f6, 0x4f6 },
+ { 0x4f7, 0x4f7 },
+ { 0x4f8, 0x4f9 }, /* upper */
+ { 0x4f8, 0x4f9 }, /* lower */
+ { 0x4fa, 0x4fa },
+ { 0x4fb, 0x4fb },
+ { 0x4fc, 0x4fc },
+ { 0x4fd, 0x4fd },
+ { 0x4fe, 0x4fe },
+ { 0x4ff, 0x4ff },
+};
+
+gli_case_block_t unigen_case_block_0x5[256] = {
+ { 0x500, 0x501 }, /* upper */
+ { 0x500, 0x501 }, /* lower */
+ { 0x502, 0x503 }, /* upper */
+ { 0x502, 0x503 }, /* lower */
+ { 0x504, 0x505 }, /* upper */
+ { 0x504, 0x505 }, /* lower */
+ { 0x506, 0x507 }, /* upper */
+ { 0x506, 0x507 }, /* lower */
+ { 0x508, 0x509 }, /* upper */
+ { 0x508, 0x509 }, /* lower */
+ { 0x50a, 0x50b }, /* upper */
+ { 0x50a, 0x50b }, /* lower */
+ { 0x50c, 0x50d }, /* upper */
+ { 0x50c, 0x50d }, /* lower */
+ { 0x50e, 0x50f }, /* upper */
+ { 0x50e, 0x50f }, /* lower */
+ { 0x510, 0x510 },
+ { 0x511, 0x511 },
+ { 0x512, 0x512 },
+ { 0x513, 0x513 },
+ { 0x514, 0x514 },
+ { 0x515, 0x515 },
+ { 0x516, 0x516 },
+ { 0x517, 0x517 },
+ { 0x518, 0x518 },
+ { 0x519, 0x519 },
+ { 0x51a, 0x51a },
+ { 0x51b, 0x51b },
+ { 0x51c, 0x51c },
+ { 0x51d, 0x51d },
+ { 0x51e, 0x51e },
+ { 0x51f, 0x51f },
+ { 0x520, 0x520 },
+ { 0x521, 0x521 },
+ { 0x522, 0x522 },
+ { 0x523, 0x523 },
+ { 0x524, 0x524 },
+ { 0x525, 0x525 },
+ { 0x526, 0x526 },
+ { 0x527, 0x527 },
+ { 0x528, 0x528 },
+ { 0x529, 0x529 },
+ { 0x52a, 0x52a },
+ { 0x52b, 0x52b },
+ { 0x52c, 0x52c },
+ { 0x52d, 0x52d },
+ { 0x52e, 0x52e },
+ { 0x52f, 0x52f },
+ { 0x530, 0x530 },
+ { 0x531, 0x561 }, /* upper */
+ { 0x532, 0x562 }, /* upper */
+ { 0x533, 0x563 }, /* upper */
+ { 0x534, 0x564 }, /* upper */
+ { 0x535, 0x565 }, /* upper */
+ { 0x536, 0x566 }, /* upper */
+ { 0x537, 0x567 }, /* upper */
+ { 0x538, 0x568 }, /* upper */
+ { 0x539, 0x569 }, /* upper */
+ { 0x53a, 0x56a }, /* upper */
+ { 0x53b, 0x56b }, /* upper */
+ { 0x53c, 0x56c }, /* upper */
+ { 0x53d, 0x56d }, /* upper */
+ { 0x53e, 0x56e }, /* upper */
+ { 0x53f, 0x56f }, /* upper */
+ { 0x540, 0x570 }, /* upper */
+ { 0x541, 0x571 }, /* upper */
+ { 0x542, 0x572 }, /* upper */
+ { 0x543, 0x573 }, /* upper */
+ { 0x544, 0x574 }, /* upper */
+ { 0x545, 0x575 }, /* upper */
+ { 0x546, 0x576 }, /* upper */
+ { 0x547, 0x577 }, /* upper */
+ { 0x548, 0x578 }, /* upper */
+ { 0x549, 0x579 }, /* upper */
+ { 0x54a, 0x57a }, /* upper */
+ { 0x54b, 0x57b }, /* upper */
+ { 0x54c, 0x57c }, /* upper */
+ { 0x54d, 0x57d }, /* upper */
+ { 0x54e, 0x57e }, /* upper */
+ { 0x54f, 0x57f }, /* upper */
+ { 0x550, 0x580 }, /* upper */
+ { 0x551, 0x581 }, /* upper */
+ { 0x552, 0x582 }, /* upper */
+ { 0x553, 0x583 }, /* upper */
+ { 0x554, 0x584 }, /* upper */
+ { 0x555, 0x585 }, /* upper */
+ { 0x556, 0x586 }, /* upper */
+ { 0x557, 0x557 },
+ { 0x558, 0x558 },
+ { 0x559, 0x559 },
+ { 0x55a, 0x55a },
+ { 0x55b, 0x55b },
+ { 0x55c, 0x55c },
+ { 0x55d, 0x55d },
+ { 0x55e, 0x55e },
+ { 0x55f, 0x55f },
+ { 0x560, 0x560 },
+ { 0x531, 0x561 }, /* lower */
+ { 0x532, 0x562 }, /* lower */
+ { 0x533, 0x563 }, /* lower */
+ { 0x534, 0x564 }, /* lower */
+ { 0x535, 0x565 }, /* lower */
+ { 0x536, 0x566 }, /* lower */
+ { 0x537, 0x567 }, /* lower */
+ { 0x538, 0x568 }, /* lower */
+ { 0x539, 0x569 }, /* lower */
+ { 0x53a, 0x56a }, /* lower */
+ { 0x53b, 0x56b }, /* lower */
+ { 0x53c, 0x56c }, /* lower */
+ { 0x53d, 0x56d }, /* lower */
+ { 0x53e, 0x56e }, /* lower */
+ { 0x53f, 0x56f }, /* lower */
+ { 0x540, 0x570 }, /* lower */
+ { 0x541, 0x571 }, /* lower */
+ { 0x542, 0x572 }, /* lower */
+ { 0x543, 0x573 }, /* lower */
+ { 0x544, 0x574 }, /* lower */
+ { 0x545, 0x575 }, /* lower */
+ { 0x546, 0x576 }, /* lower */
+ { 0x547, 0x577 }, /* lower */
+ { 0x548, 0x578 }, /* lower */
+ { 0x549, 0x579 }, /* lower */
+ { 0x54a, 0x57a }, /* lower */
+ { 0x54b, 0x57b }, /* lower */
+ { 0x54c, 0x57c }, /* lower */
+ { 0x54d, 0x57d }, /* lower */
+ { 0x54e, 0x57e }, /* lower */
+ { 0x54f, 0x57f }, /* lower */
+ { 0x550, 0x580 }, /* lower */
+ { 0x551, 0x581 }, /* lower */
+ { 0x552, 0x582 }, /* lower */
+ { 0x553, 0x583 }, /* lower */
+ { 0x554, 0x584 }, /* lower */
+ { 0x555, 0x585 }, /* lower */
+ { 0x556, 0x586 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x588, 0x588 },
+ { 0x589, 0x589 },
+ { 0x58a, 0x58a },
+ { 0x58b, 0x58b },
+ { 0x58c, 0x58c },
+ { 0x58d, 0x58d },
+ { 0x58e, 0x58e },
+ { 0x58f, 0x58f },
+ { 0x590, 0x590 },
+ { 0x591, 0x591 },
+ { 0x592, 0x592 },
+ { 0x593, 0x593 },
+ { 0x594, 0x594 },
+ { 0x595, 0x595 },
+ { 0x596, 0x596 },
+ { 0x597, 0x597 },
+ { 0x598, 0x598 },
+ { 0x599, 0x599 },
+ { 0x59a, 0x59a },
+ { 0x59b, 0x59b },
+ { 0x59c, 0x59c },
+ { 0x59d, 0x59d },
+ { 0x59e, 0x59e },
+ { 0x59f, 0x59f },
+ { 0x5a0, 0x5a0 },
+ { 0x5a1, 0x5a1 },
+ { 0x5a2, 0x5a2 },
+ { 0x5a3, 0x5a3 },
+ { 0x5a4, 0x5a4 },
+ { 0x5a5, 0x5a5 },
+ { 0x5a6, 0x5a6 },
+ { 0x5a7, 0x5a7 },
+ { 0x5a8, 0x5a8 },
+ { 0x5a9, 0x5a9 },
+ { 0x5aa, 0x5aa },
+ { 0x5ab, 0x5ab },
+ { 0x5ac, 0x5ac },
+ { 0x5ad, 0x5ad },
+ { 0x5ae, 0x5ae },
+ { 0x5af, 0x5af },
+ { 0x5b0, 0x5b0 },
+ { 0x5b1, 0x5b1 },
+ { 0x5b2, 0x5b2 },
+ { 0x5b3, 0x5b3 },
+ { 0x5b4, 0x5b4 },
+ { 0x5b5, 0x5b5 },
+ { 0x5b6, 0x5b6 },
+ { 0x5b7, 0x5b7 },
+ { 0x5b8, 0x5b8 },
+ { 0x5b9, 0x5b9 },
+ { 0x5ba, 0x5ba },
+ { 0x5bb, 0x5bb },
+ { 0x5bc, 0x5bc },
+ { 0x5bd, 0x5bd },
+ { 0x5be, 0x5be },
+ { 0x5bf, 0x5bf },
+ { 0x5c0, 0x5c0 },
+ { 0x5c1, 0x5c1 },
+ { 0x5c2, 0x5c2 },
+ { 0x5c3, 0x5c3 },
+ { 0x5c4, 0x5c4 },
+ { 0x5c5, 0x5c5 },
+ { 0x5c6, 0x5c6 },
+ { 0x5c7, 0x5c7 },
+ { 0x5c8, 0x5c8 },
+ { 0x5c9, 0x5c9 },
+ { 0x5ca, 0x5ca },
+ { 0x5cb, 0x5cb },
+ { 0x5cc, 0x5cc },
+ { 0x5cd, 0x5cd },
+ { 0x5ce, 0x5ce },
+ { 0x5cf, 0x5cf },
+ { 0x5d0, 0x5d0 },
+ { 0x5d1, 0x5d1 },
+ { 0x5d2, 0x5d2 },
+ { 0x5d3, 0x5d3 },
+ { 0x5d4, 0x5d4 },
+ { 0x5d5, 0x5d5 },
+ { 0x5d6, 0x5d6 },
+ { 0x5d7, 0x5d7 },
+ { 0x5d8, 0x5d8 },
+ { 0x5d9, 0x5d9 },
+ { 0x5da, 0x5da },
+ { 0x5db, 0x5db },
+ { 0x5dc, 0x5dc },
+ { 0x5dd, 0x5dd },
+ { 0x5de, 0x5de },
+ { 0x5df, 0x5df },
+ { 0x5e0, 0x5e0 },
+ { 0x5e1, 0x5e1 },
+ { 0x5e2, 0x5e2 },
+ { 0x5e3, 0x5e3 },
+ { 0x5e4, 0x5e4 },
+ { 0x5e5, 0x5e5 },
+ { 0x5e6, 0x5e6 },
+ { 0x5e7, 0x5e7 },
+ { 0x5e8, 0x5e8 },
+ { 0x5e9, 0x5e9 },
+ { 0x5ea, 0x5ea },
+ { 0x5eb, 0x5eb },
+ { 0x5ec, 0x5ec },
+ { 0x5ed, 0x5ed },
+ { 0x5ee, 0x5ee },
+ { 0x5ef, 0x5ef },
+ { 0x5f0, 0x5f0 },
+ { 0x5f1, 0x5f1 },
+ { 0x5f2, 0x5f2 },
+ { 0x5f3, 0x5f3 },
+ { 0x5f4, 0x5f4 },
+ { 0x5f5, 0x5f5 },
+ { 0x5f6, 0x5f6 },
+ { 0x5f7, 0x5f7 },
+ { 0x5f8, 0x5f8 },
+ { 0x5f9, 0x5f9 },
+ { 0x5fa, 0x5fa },
+ { 0x5fb, 0x5fb },
+ { 0x5fc, 0x5fc },
+ { 0x5fd, 0x5fd },
+ { 0x5fe, 0x5fe },
+ { 0x5ff, 0x5ff },
+};
+
+gli_case_block_t unigen_case_block_0x1e[256] = {
+ { 0x1e00, 0x1e01 }, /* upper */
+ { 0x1e00, 0x1e01 }, /* lower */
+ { 0x1e02, 0x1e03 }, /* upper */
+ { 0x1e02, 0x1e03 }, /* lower */
+ { 0x1e04, 0x1e05 }, /* upper */
+ { 0x1e04, 0x1e05 }, /* lower */
+ { 0x1e06, 0x1e07 }, /* upper */
+ { 0x1e06, 0x1e07 }, /* lower */
+ { 0x1e08, 0x1e09 }, /* upper */
+ { 0x1e08, 0x1e09 }, /* lower */
+ { 0x1e0a, 0x1e0b }, /* upper */
+ { 0x1e0a, 0x1e0b }, /* lower */
+ { 0x1e0c, 0x1e0d }, /* upper */
+ { 0x1e0c, 0x1e0d }, /* lower */
+ { 0x1e0e, 0x1e0f }, /* upper */
+ { 0x1e0e, 0x1e0f }, /* lower */
+ { 0x1e10, 0x1e11 }, /* upper */
+ { 0x1e10, 0x1e11 }, /* lower */
+ { 0x1e12, 0x1e13 }, /* upper */
+ { 0x1e12, 0x1e13 }, /* lower */
+ { 0x1e14, 0x1e15 }, /* upper */
+ { 0x1e14, 0x1e15 }, /* lower */
+ { 0x1e16, 0x1e17 }, /* upper */
+ { 0x1e16, 0x1e17 }, /* lower */
+ { 0x1e18, 0x1e19 }, /* upper */
+ { 0x1e18, 0x1e19 }, /* lower */
+ { 0x1e1a, 0x1e1b }, /* upper */
+ { 0x1e1a, 0x1e1b }, /* lower */
+ { 0x1e1c, 0x1e1d }, /* upper */
+ { 0x1e1c, 0x1e1d }, /* lower */
+ { 0x1e1e, 0x1e1f }, /* upper */
+ { 0x1e1e, 0x1e1f }, /* lower */
+ { 0x1e20, 0x1e21 }, /* upper */
+ { 0x1e20, 0x1e21 }, /* lower */
+ { 0x1e22, 0x1e23 }, /* upper */
+ { 0x1e22, 0x1e23 }, /* lower */
+ { 0x1e24, 0x1e25 }, /* upper */
+ { 0x1e24, 0x1e25 }, /* lower */
+ { 0x1e26, 0x1e27 }, /* upper */
+ { 0x1e26, 0x1e27 }, /* lower */
+ { 0x1e28, 0x1e29 }, /* upper */
+ { 0x1e28, 0x1e29 }, /* lower */
+ { 0x1e2a, 0x1e2b }, /* upper */
+ { 0x1e2a, 0x1e2b }, /* lower */
+ { 0x1e2c, 0x1e2d }, /* upper */
+ { 0x1e2c, 0x1e2d }, /* lower */
+ { 0x1e2e, 0x1e2f }, /* upper */
+ { 0x1e2e, 0x1e2f }, /* lower */
+ { 0x1e30, 0x1e31 }, /* upper */
+ { 0x1e30, 0x1e31 }, /* lower */
+ { 0x1e32, 0x1e33 }, /* upper */
+ { 0x1e32, 0x1e33 }, /* lower */
+ { 0x1e34, 0x1e35 }, /* upper */
+ { 0x1e34, 0x1e35 }, /* lower */
+ { 0x1e36, 0x1e37 }, /* upper */
+ { 0x1e36, 0x1e37 }, /* lower */
+ { 0x1e38, 0x1e39 }, /* upper */
+ { 0x1e38, 0x1e39 }, /* lower */
+ { 0x1e3a, 0x1e3b }, /* upper */
+ { 0x1e3a, 0x1e3b }, /* lower */
+ { 0x1e3c, 0x1e3d }, /* upper */
+ { 0x1e3c, 0x1e3d }, /* lower */
+ { 0x1e3e, 0x1e3f }, /* upper */
+ { 0x1e3e, 0x1e3f }, /* lower */
+ { 0x1e40, 0x1e41 }, /* upper */
+ { 0x1e40, 0x1e41 }, /* lower */
+ { 0x1e42, 0x1e43 }, /* upper */
+ { 0x1e42, 0x1e43 }, /* lower */
+ { 0x1e44, 0x1e45 }, /* upper */
+ { 0x1e44, 0x1e45 }, /* lower */
+ { 0x1e46, 0x1e47 }, /* upper */
+ { 0x1e46, 0x1e47 }, /* lower */
+ { 0x1e48, 0x1e49 }, /* upper */
+ { 0x1e48, 0x1e49 }, /* lower */
+ { 0x1e4a, 0x1e4b }, /* upper */
+ { 0x1e4a, 0x1e4b }, /* lower */
+ { 0x1e4c, 0x1e4d }, /* upper */
+ { 0x1e4c, 0x1e4d }, /* lower */
+ { 0x1e4e, 0x1e4f }, /* upper */
+ { 0x1e4e, 0x1e4f }, /* lower */
+ { 0x1e50, 0x1e51 }, /* upper */
+ { 0x1e50, 0x1e51 }, /* lower */
+ { 0x1e52, 0x1e53 }, /* upper */
+ { 0x1e52, 0x1e53 }, /* lower */
+ { 0x1e54, 0x1e55 }, /* upper */
+ { 0x1e54, 0x1e55 }, /* lower */
+ { 0x1e56, 0x1e57 }, /* upper */
+ { 0x1e56, 0x1e57 }, /* lower */
+ { 0x1e58, 0x1e59 }, /* upper */
+ { 0x1e58, 0x1e59 }, /* lower */
+ { 0x1e5a, 0x1e5b }, /* upper */
+ { 0x1e5a, 0x1e5b }, /* lower */
+ { 0x1e5c, 0x1e5d }, /* upper */
+ { 0x1e5c, 0x1e5d }, /* lower */
+ { 0x1e5e, 0x1e5f }, /* upper */
+ { 0x1e5e, 0x1e5f }, /* lower */
+ { 0x1e60, 0x1e61 }, /* upper */
+ { 0x1e60, 0x1e61 }, /* lower */
+ { 0x1e62, 0x1e63 }, /* upper */
+ { 0x1e62, 0x1e63 }, /* lower */
+ { 0x1e64, 0x1e65 }, /* upper */
+ { 0x1e64, 0x1e65 }, /* lower */
+ { 0x1e66, 0x1e67 }, /* upper */
+ { 0x1e66, 0x1e67 }, /* lower */
+ { 0x1e68, 0x1e69 }, /* upper */
+ { 0x1e68, 0x1e69 }, /* lower */
+ { 0x1e6a, 0x1e6b }, /* upper */
+ { 0x1e6a, 0x1e6b }, /* lower */
+ { 0x1e6c, 0x1e6d }, /* upper */
+ { 0x1e6c, 0x1e6d }, /* lower */
+ { 0x1e6e, 0x1e6f }, /* upper */
+ { 0x1e6e, 0x1e6f }, /* lower */
+ { 0x1e70, 0x1e71 }, /* upper */
+ { 0x1e70, 0x1e71 }, /* lower */
+ { 0x1e72, 0x1e73 }, /* upper */
+ { 0x1e72, 0x1e73 }, /* lower */
+ { 0x1e74, 0x1e75 }, /* upper */
+ { 0x1e74, 0x1e75 }, /* lower */
+ { 0x1e76, 0x1e77 }, /* upper */
+ { 0x1e76, 0x1e77 }, /* lower */
+ { 0x1e78, 0x1e79 }, /* upper */
+ { 0x1e78, 0x1e79 }, /* lower */
+ { 0x1e7a, 0x1e7b }, /* upper */
+ { 0x1e7a, 0x1e7b }, /* lower */
+ { 0x1e7c, 0x1e7d }, /* upper */
+ { 0x1e7c, 0x1e7d }, /* lower */
+ { 0x1e7e, 0x1e7f }, /* upper */
+ { 0x1e7e, 0x1e7f }, /* lower */
+ { 0x1e80, 0x1e81 }, /* upper */
+ { 0x1e80, 0x1e81 }, /* lower */
+ { 0x1e82, 0x1e83 }, /* upper */
+ { 0x1e82, 0x1e83 }, /* lower */
+ { 0x1e84, 0x1e85 }, /* upper */
+ { 0x1e84, 0x1e85 }, /* lower */
+ { 0x1e86, 0x1e87 }, /* upper */
+ { 0x1e86, 0x1e87 }, /* lower */
+ { 0x1e88, 0x1e89 }, /* upper */
+ { 0x1e88, 0x1e89 }, /* lower */
+ { 0x1e8a, 0x1e8b }, /* upper */
+ { 0x1e8a, 0x1e8b }, /* lower */
+ { 0x1e8c, 0x1e8d }, /* upper */
+ { 0x1e8c, 0x1e8d }, /* lower */
+ { 0x1e8e, 0x1e8f }, /* upper */
+ { 0x1e8e, 0x1e8f }, /* lower */
+ { 0x1e90, 0x1e91 }, /* upper */
+ { 0x1e90, 0x1e91 }, /* lower */
+ { 0x1e92, 0x1e93 }, /* upper */
+ { 0x1e92, 0x1e93 }, /* lower */
+ { 0x1e94, 0x1e95 }, /* upper */
+ { 0x1e94, 0x1e95 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1e60, 0x1e9b }, /* lower */
+ { 0x1e9c, 0x1e9c },
+ { 0x1e9d, 0x1e9d },
+ { 0x1e9e, 0x1e9e },
+ { 0x1e9f, 0x1e9f },
+ { 0x1ea0, 0x1ea1 }, /* upper */
+ { 0x1ea0, 0x1ea1 }, /* lower */
+ { 0x1ea2, 0x1ea3 }, /* upper */
+ { 0x1ea2, 0x1ea3 }, /* lower */
+ { 0x1ea4, 0x1ea5 }, /* upper */
+ { 0x1ea4, 0x1ea5 }, /* lower */
+ { 0x1ea6, 0x1ea7 }, /* upper */
+ { 0x1ea6, 0x1ea7 }, /* lower */
+ { 0x1ea8, 0x1ea9 }, /* upper */
+ { 0x1ea8, 0x1ea9 }, /* lower */
+ { 0x1eaa, 0x1eab }, /* upper */
+ { 0x1eaa, 0x1eab }, /* lower */
+ { 0x1eac, 0x1ead }, /* upper */
+ { 0x1eac, 0x1ead }, /* lower */
+ { 0x1eae, 0x1eaf }, /* upper */
+ { 0x1eae, 0x1eaf }, /* lower */
+ { 0x1eb0, 0x1eb1 }, /* upper */
+ { 0x1eb0, 0x1eb1 }, /* lower */
+ { 0x1eb2, 0x1eb3 }, /* upper */
+ { 0x1eb2, 0x1eb3 }, /* lower */
+ { 0x1eb4, 0x1eb5 }, /* upper */
+ { 0x1eb4, 0x1eb5 }, /* lower */
+ { 0x1eb6, 0x1eb7 }, /* upper */
+ { 0x1eb6, 0x1eb7 }, /* lower */
+ { 0x1eb8, 0x1eb9 }, /* upper */
+ { 0x1eb8, 0x1eb9 }, /* lower */
+ { 0x1eba, 0x1ebb }, /* upper */
+ { 0x1eba, 0x1ebb }, /* lower */
+ { 0x1ebc, 0x1ebd }, /* upper */
+ { 0x1ebc, 0x1ebd }, /* lower */
+ { 0x1ebe, 0x1ebf }, /* upper */
+ { 0x1ebe, 0x1ebf }, /* lower */
+ { 0x1ec0, 0x1ec1 }, /* upper */
+ { 0x1ec0, 0x1ec1 }, /* lower */
+ { 0x1ec2, 0x1ec3 }, /* upper */
+ { 0x1ec2, 0x1ec3 }, /* lower */
+ { 0x1ec4, 0x1ec5 }, /* upper */
+ { 0x1ec4, 0x1ec5 }, /* lower */
+ { 0x1ec6, 0x1ec7 }, /* upper */
+ { 0x1ec6, 0x1ec7 }, /* lower */
+ { 0x1ec8, 0x1ec9 }, /* upper */
+ { 0x1ec8, 0x1ec9 }, /* lower */
+ { 0x1eca, 0x1ecb }, /* upper */
+ { 0x1eca, 0x1ecb }, /* lower */
+ { 0x1ecc, 0x1ecd }, /* upper */
+ { 0x1ecc, 0x1ecd }, /* lower */
+ { 0x1ece, 0x1ecf }, /* upper */
+ { 0x1ece, 0x1ecf }, /* lower */
+ { 0x1ed0, 0x1ed1 }, /* upper */
+ { 0x1ed0, 0x1ed1 }, /* lower */
+ { 0x1ed2, 0x1ed3 }, /* upper */
+ { 0x1ed2, 0x1ed3 }, /* lower */
+ { 0x1ed4, 0x1ed5 }, /* upper */
+ { 0x1ed4, 0x1ed5 }, /* lower */
+ { 0x1ed6, 0x1ed7 }, /* upper */
+ { 0x1ed6, 0x1ed7 }, /* lower */
+ { 0x1ed8, 0x1ed9 }, /* upper */
+ { 0x1ed8, 0x1ed9 }, /* lower */
+ { 0x1eda, 0x1edb }, /* upper */
+ { 0x1eda, 0x1edb }, /* lower */
+ { 0x1edc, 0x1edd }, /* upper */
+ { 0x1edc, 0x1edd }, /* lower */
+ { 0x1ede, 0x1edf }, /* upper */
+ { 0x1ede, 0x1edf }, /* lower */
+ { 0x1ee0, 0x1ee1 }, /* upper */
+ { 0x1ee0, 0x1ee1 }, /* lower */
+ { 0x1ee2, 0x1ee3 }, /* upper */
+ { 0x1ee2, 0x1ee3 }, /* lower */
+ { 0x1ee4, 0x1ee5 }, /* upper */
+ { 0x1ee4, 0x1ee5 }, /* lower */
+ { 0x1ee6, 0x1ee7 }, /* upper */
+ { 0x1ee6, 0x1ee7 }, /* lower */
+ { 0x1ee8, 0x1ee9 }, /* upper */
+ { 0x1ee8, 0x1ee9 }, /* lower */
+ { 0x1eea, 0x1eeb }, /* upper */
+ { 0x1eea, 0x1eeb }, /* lower */
+ { 0x1eec, 0x1eed }, /* upper */
+ { 0x1eec, 0x1eed }, /* lower */
+ { 0x1eee, 0x1eef }, /* upper */
+ { 0x1eee, 0x1eef }, /* lower */
+ { 0x1ef0, 0x1ef1 }, /* upper */
+ { 0x1ef0, 0x1ef1 }, /* lower */
+ { 0x1ef2, 0x1ef3 }, /* upper */
+ { 0x1ef2, 0x1ef3 }, /* lower */
+ { 0x1ef4, 0x1ef5 }, /* upper */
+ { 0x1ef4, 0x1ef5 }, /* lower */
+ { 0x1ef6, 0x1ef7 }, /* upper */
+ { 0x1ef6, 0x1ef7 }, /* lower */
+ { 0x1ef8, 0x1ef9 }, /* upper */
+ { 0x1ef8, 0x1ef9 }, /* lower */
+ { 0x1efa, 0x1efa },
+ { 0x1efb, 0x1efb },
+ { 0x1efc, 0x1efc },
+ { 0x1efd, 0x1efd },
+ { 0x1efe, 0x1efe },
+ { 0x1eff, 0x1eff },
+};
+
+gli_case_block_t unigen_case_block_0x1f[256] = {
+ { 0x1f08, 0x1f00 }, /* lower */
+ { 0x1f09, 0x1f01 }, /* lower */
+ { 0x1f0a, 0x1f02 }, /* lower */
+ { 0x1f0b, 0x1f03 }, /* lower */
+ { 0x1f0c, 0x1f04 }, /* lower */
+ { 0x1f0d, 0x1f05 }, /* lower */
+ { 0x1f0e, 0x1f06 }, /* lower */
+ { 0x1f0f, 0x1f07 }, /* lower */
+ { 0x1f08, 0x1f00 }, /* upper */
+ { 0x1f09, 0x1f01 }, /* upper */
+ { 0x1f0a, 0x1f02 }, /* upper */
+ { 0x1f0b, 0x1f03 }, /* upper */
+ { 0x1f0c, 0x1f04 }, /* upper */
+ { 0x1f0d, 0x1f05 }, /* upper */
+ { 0x1f0e, 0x1f06 }, /* upper */
+ { 0x1f0f, 0x1f07 }, /* upper */
+ { 0x1f18, 0x1f10 }, /* lower */
+ { 0x1f19, 0x1f11 }, /* lower */
+ { 0x1f1a, 0x1f12 }, /* lower */
+ { 0x1f1b, 0x1f13 }, /* lower */
+ { 0x1f1c, 0x1f14 }, /* lower */
+ { 0x1f1d, 0x1f15 }, /* lower */
+ { 0x1f16, 0x1f16 },
+ { 0x1f17, 0x1f17 },
+ { 0x1f18, 0x1f10 }, /* upper */
+ { 0x1f19, 0x1f11 }, /* upper */
+ { 0x1f1a, 0x1f12 }, /* upper */
+ { 0x1f1b, 0x1f13 }, /* upper */
+ { 0x1f1c, 0x1f14 }, /* upper */
+ { 0x1f1d, 0x1f15 }, /* upper */
+ { 0x1f1e, 0x1f1e },
+ { 0x1f1f, 0x1f1f },
+ { 0x1f28, 0x1f20 }, /* lower */
+ { 0x1f29, 0x1f21 }, /* lower */
+ { 0x1f2a, 0x1f22 }, /* lower */
+ { 0x1f2b, 0x1f23 }, /* lower */
+ { 0x1f2c, 0x1f24 }, /* lower */
+ { 0x1f2d, 0x1f25 }, /* lower */
+ { 0x1f2e, 0x1f26 }, /* lower */
+ { 0x1f2f, 0x1f27 }, /* lower */
+ { 0x1f28, 0x1f20 }, /* upper */
+ { 0x1f29, 0x1f21 }, /* upper */
+ { 0x1f2a, 0x1f22 }, /* upper */
+ { 0x1f2b, 0x1f23 }, /* upper */
+ { 0x1f2c, 0x1f24 }, /* upper */
+ { 0x1f2d, 0x1f25 }, /* upper */
+ { 0x1f2e, 0x1f26 }, /* upper */
+ { 0x1f2f, 0x1f27 }, /* upper */
+ { 0x1f38, 0x1f30 }, /* lower */
+ { 0x1f39, 0x1f31 }, /* lower */
+ { 0x1f3a, 0x1f32 }, /* lower */
+ { 0x1f3b, 0x1f33 }, /* lower */
+ { 0x1f3c, 0x1f34 }, /* lower */
+ { 0x1f3d, 0x1f35 }, /* lower */
+ { 0x1f3e, 0x1f36 }, /* lower */
+ { 0x1f3f, 0x1f37 }, /* lower */
+ { 0x1f38, 0x1f30 }, /* upper */
+ { 0x1f39, 0x1f31 }, /* upper */
+ { 0x1f3a, 0x1f32 }, /* upper */
+ { 0x1f3b, 0x1f33 }, /* upper */
+ { 0x1f3c, 0x1f34 }, /* upper */
+ { 0x1f3d, 0x1f35 }, /* upper */
+ { 0x1f3e, 0x1f36 }, /* upper */
+ { 0x1f3f, 0x1f37 }, /* upper */
+ { 0x1f48, 0x1f40 }, /* lower */
+ { 0x1f49, 0x1f41 }, /* lower */
+ { 0x1f4a, 0x1f42 }, /* lower */
+ { 0x1f4b, 0x1f43 }, /* lower */
+ { 0x1f4c, 0x1f44 }, /* lower */
+ { 0x1f4d, 0x1f45 }, /* lower */
+ { 0x1f46, 0x1f46 },
+ { 0x1f47, 0x1f47 },
+ { 0x1f48, 0x1f40 }, /* upper */
+ { 0x1f49, 0x1f41 }, /* upper */
+ { 0x1f4a, 0x1f42 }, /* upper */
+ { 0x1f4b, 0x1f43 }, /* upper */
+ { 0x1f4c, 0x1f44 }, /* upper */
+ { 0x1f4d, 0x1f45 }, /* upper */
+ { 0x1f4e, 0x1f4e },
+ { 0x1f4f, 0x1f4f },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1f59, 0x1f51 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1f5b, 0x1f53 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1f5d, 0x1f55 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1f5f, 0x1f57 }, /* lower */
+ { 0x1f58, 0x1f58 },
+ { 0x1f59, 0x1f51 }, /* upper */
+ { 0x1f5a, 0x1f5a },
+ { 0x1f5b, 0x1f53 }, /* upper */
+ { 0x1f5c, 0x1f5c },
+ { 0x1f5d, 0x1f55 }, /* upper */
+ { 0x1f5e, 0x1f5e },
+ { 0x1f5f, 0x1f57 }, /* upper */
+ { 0x1f68, 0x1f60 }, /* lower */
+ { 0x1f69, 0x1f61 }, /* lower */
+ { 0x1f6a, 0x1f62 }, /* lower */
+ { 0x1f6b, 0x1f63 }, /* lower */
+ { 0x1f6c, 0x1f64 }, /* lower */
+ { 0x1f6d, 0x1f65 }, /* lower */
+ { 0x1f6e, 0x1f66 }, /* lower */
+ { 0x1f6f, 0x1f67 }, /* lower */
+ { 0x1f68, 0x1f60 }, /* upper */
+ { 0x1f69, 0x1f61 }, /* upper */
+ { 0x1f6a, 0x1f62 }, /* upper */
+ { 0x1f6b, 0x1f63 }, /* upper */
+ { 0x1f6c, 0x1f64 }, /* upper */
+ { 0x1f6d, 0x1f65 }, /* upper */
+ { 0x1f6e, 0x1f66 }, /* upper */
+ { 0x1f6f, 0x1f67 }, /* upper */
+ { 0x1fba, 0x1f70 }, /* lower */
+ { 0x1fbb, 0x1f71 }, /* lower */
+ { 0x1fc8, 0x1f72 }, /* lower */
+ { 0x1fc9, 0x1f73 }, /* lower */
+ { 0x1fca, 0x1f74 }, /* lower */
+ { 0x1fcb, 0x1f75 }, /* lower */
+ { 0x1fda, 0x1f76 }, /* lower */
+ { 0x1fdb, 0x1f77 }, /* lower */
+ { 0x1ff8, 0x1f78 }, /* lower */
+ { 0x1ff9, 0x1f79 }, /* lower */
+ { 0x1fea, 0x1f7a }, /* lower */
+ { 0x1feb, 0x1f7b }, /* lower */
+ { 0x1ffa, 0x1f7c }, /* lower */
+ { 0x1ffb, 0x1f7d }, /* lower */
+ { 0x1f7e, 0x1f7e },
+ { 0x1f7f, 0x1f7f },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fb8, 0x1fb0 }, /* lower */
+ { 0x1fb9, 0x1fb1 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fb5, 0x1fb5 },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fb8, 0x1fb0 }, /* upper */
+ { 0x1fb9, 0x1fb1 }, /* upper */
+ { 0x1fba, 0x1f70 }, /* upper */
+ { 0x1fbb, 0x1f71 }, /* upper */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fbd, 0x1fbd },
+ { 0x399, 0x1fbe }, /* lower */
+ { 0x1fbf, 0x1fbf },
+ { 0x1fc0, 0x1fc0 },
+ { 0x1fc1, 0x1fc1 },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fc5, 0x1fc5 },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fc8, 0x1f72 }, /* upper */
+ { 0x1fc9, 0x1f73 }, /* upper */
+ { 0x1fca, 0x1f74 }, /* upper */
+ { 0x1fcb, 0x1f75 }, /* upper */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fcd, 0x1fcd },
+ { 0x1fce, 0x1fce },
+ { 0x1fcf, 0x1fcf },
+ { 0x1fd8, 0x1fd0 }, /* lower */
+ { 0x1fd9, 0x1fd1 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fd4, 0x1fd4 },
+ { 0x1fd5, 0x1fd5 },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fd8, 0x1fd0 }, /* upper */
+ { 0x1fd9, 0x1fd1 }, /* upper */
+ { 0x1fda, 0x1f76 }, /* upper */
+ { 0x1fdb, 0x1f77 }, /* upper */
+ { 0x1fdc, 0x1fdc },
+ { 0x1fdd, 0x1fdd },
+ { 0x1fde, 0x1fde },
+ { 0x1fdf, 0x1fdf },
+ { 0x1fe8, 0x1fe0 }, /* lower */
+ { 0x1fe9, 0x1fe1 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fec, 0x1fe5 }, /* lower */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1fe8, 0x1fe0 }, /* upper */
+ { 0x1fe9, 0x1fe1 }, /* upper */
+ { 0x1fea, 0x1f7a }, /* upper */
+ { 0x1feb, 0x1f7b }, /* upper */
+ { 0x1fec, 0x1fe5 }, /* upper */
+ { 0x1fed, 0x1fed },
+ { 0x1fee, 0x1fee },
+ { 0x1fef, 0x1fef },
+ { 0x1ff0, 0x1ff0 },
+ { 0x1ff1, 0x1ff1 },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1ff5, 0x1ff5 },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1ff8, 0x1f78 }, /* upper */
+ { 0x1ff9, 0x1f79 }, /* upper */
+ { 0x1ffa, 0x1f7c }, /* upper */
+ { 0x1ffb, 0x1f7d }, /* upper */
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0x1ffd, 0x1ffd },
+ { 0x1ffe, 0x1ffe },
+ { 0x1fff, 0x1fff },
+};
+
+gli_case_block_t unigen_case_block_0x21[256] = {
+ { 0x2100, 0x2100 },
+ { 0x2101, 0x2101 },
+ { 0x2102, 0x2102 },
+ { 0x2103, 0x2103 },
+ { 0x2104, 0x2104 },
+ { 0x2105, 0x2105 },
+ { 0x2106, 0x2106 },
+ { 0x2107, 0x2107 },
+ { 0x2108, 0x2108 },
+ { 0x2109, 0x2109 },
+ { 0x210a, 0x210a },
+ { 0x210b, 0x210b },
+ { 0x210c, 0x210c },
+ { 0x210d, 0x210d },
+ { 0x210e, 0x210e },
+ { 0x210f, 0x210f },
+ { 0x2110, 0x2110 },
+ { 0x2111, 0x2111 },
+ { 0x2112, 0x2112 },
+ { 0x2113, 0x2113 },
+ { 0x2114, 0x2114 },
+ { 0x2115, 0x2115 },
+ { 0x2116, 0x2116 },
+ { 0x2117, 0x2117 },
+ { 0x2118, 0x2118 },
+ { 0x2119, 0x2119 },
+ { 0x211a, 0x211a },
+ { 0x211b, 0x211b },
+ { 0x211c, 0x211c },
+ { 0x211d, 0x211d },
+ { 0x211e, 0x211e },
+ { 0x211f, 0x211f },
+ { 0x2120, 0x2120 },
+ { 0x2121, 0x2121 },
+ { 0x2122, 0x2122 },
+ { 0x2123, 0x2123 },
+ { 0x2124, 0x2124 },
+ { 0x2125, 0x2125 },
+ { 0x2126, 0x3c9 }, /* upper */
+ { 0x2127, 0x2127 },
+ { 0x2128, 0x2128 },
+ { 0x2129, 0x2129 },
+ { 0x212a, 0x6b }, /* upper */
+ { 0x212b, 0xe5 }, /* upper */
+ { 0x212c, 0x212c },
+ { 0x212d, 0x212d },
+ { 0x212e, 0x212e },
+ { 0x212f, 0x212f },
+ { 0x2130, 0x2130 },
+ { 0x2131, 0x2131 },
+ { 0x2132, 0x2132 },
+ { 0x2133, 0x2133 },
+ { 0x2134, 0x2134 },
+ { 0x2135, 0x2135 },
+ { 0x2136, 0x2136 },
+ { 0x2137, 0x2137 },
+ { 0x2138, 0x2138 },
+ { 0x2139, 0x2139 },
+ { 0x213a, 0x213a },
+ { 0x213b, 0x213b },
+ { 0x213c, 0x213c },
+ { 0x213d, 0x213d },
+ { 0x213e, 0x213e },
+ { 0x213f, 0x213f },
+ { 0x2140, 0x2140 },
+ { 0x2141, 0x2141 },
+ { 0x2142, 0x2142 },
+ { 0x2143, 0x2143 },
+ { 0x2144, 0x2144 },
+ { 0x2145, 0x2145 },
+ { 0x2146, 0x2146 },
+ { 0x2147, 0x2147 },
+ { 0x2148, 0x2148 },
+ { 0x2149, 0x2149 },
+ { 0x214a, 0x214a },
+ { 0x214b, 0x214b },
+ { 0x214c, 0x214c },
+ { 0x214d, 0x214d },
+ { 0x214e, 0x214e },
+ { 0x214f, 0x214f },
+ { 0x2150, 0x2150 },
+ { 0x2151, 0x2151 },
+ { 0x2152, 0x2152 },
+ { 0x2153, 0x2153 },
+ { 0x2154, 0x2154 },
+ { 0x2155, 0x2155 },
+ { 0x2156, 0x2156 },
+ { 0x2157, 0x2157 },
+ { 0x2158, 0x2158 },
+ { 0x2159, 0x2159 },
+ { 0x215a, 0x215a },
+ { 0x215b, 0x215b },
+ { 0x215c, 0x215c },
+ { 0x215d, 0x215d },
+ { 0x215e, 0x215e },
+ { 0x215f, 0x215f },
+ { 0x2160, 0x2170 }, /* upper */
+ { 0x2161, 0x2171 }, /* upper */
+ { 0x2162, 0x2172 }, /* upper */
+ { 0x2163, 0x2173 }, /* upper */
+ { 0x2164, 0x2174 }, /* upper */
+ { 0x2165, 0x2175 }, /* upper */
+ { 0x2166, 0x2176 }, /* upper */
+ { 0x2167, 0x2177 }, /* upper */
+ { 0x2168, 0x2178 }, /* upper */
+ { 0x2169, 0x2179 }, /* upper */
+ { 0x216a, 0x217a }, /* upper */
+ { 0x216b, 0x217b }, /* upper */
+ { 0x216c, 0x217c }, /* upper */
+ { 0x216d, 0x217d }, /* upper */
+ { 0x216e, 0x217e }, /* upper */
+ { 0x216f, 0x217f }, /* upper */
+ { 0x2160, 0x2170 }, /* lower */
+ { 0x2161, 0x2171 }, /* lower */
+ { 0x2162, 0x2172 }, /* lower */
+ { 0x2163, 0x2173 }, /* lower */
+ { 0x2164, 0x2174 }, /* lower */
+ { 0x2165, 0x2175 }, /* lower */
+ { 0x2166, 0x2176 }, /* lower */
+ { 0x2167, 0x2177 }, /* lower */
+ { 0x2168, 0x2178 }, /* lower */
+ { 0x2169, 0x2179 }, /* lower */
+ { 0x216a, 0x217a }, /* lower */
+ { 0x216b, 0x217b }, /* lower */
+ { 0x216c, 0x217c }, /* lower */
+ { 0x216d, 0x217d }, /* lower */
+ { 0x216e, 0x217e }, /* lower */
+ { 0x216f, 0x217f }, /* lower */
+ { 0x2180, 0x2180 },
+ { 0x2181, 0x2181 },
+ { 0x2182, 0x2182 },
+ { 0x2183, 0x2183 },
+ { 0x2184, 0x2184 },
+ { 0x2185, 0x2185 },
+ { 0x2186, 0x2186 },
+ { 0x2187, 0x2187 },
+ { 0x2188, 0x2188 },
+ { 0x2189, 0x2189 },
+ { 0x218a, 0x218a },
+ { 0x218b, 0x218b },
+ { 0x218c, 0x218c },
+ { 0x218d, 0x218d },
+ { 0x218e, 0x218e },
+ { 0x218f, 0x218f },
+ { 0x2190, 0x2190 },
+ { 0x2191, 0x2191 },
+ { 0x2192, 0x2192 },
+ { 0x2193, 0x2193 },
+ { 0x2194, 0x2194 },
+ { 0x2195, 0x2195 },
+ { 0x2196, 0x2196 },
+ { 0x2197, 0x2197 },
+ { 0x2198, 0x2198 },
+ { 0x2199, 0x2199 },
+ { 0x219a, 0x219a },
+ { 0x219b, 0x219b },
+ { 0x219c, 0x219c },
+ { 0x219d, 0x219d },
+ { 0x219e, 0x219e },
+ { 0x219f, 0x219f },
+ { 0x21a0, 0x21a0 },
+ { 0x21a1, 0x21a1 },
+ { 0x21a2, 0x21a2 },
+ { 0x21a3, 0x21a3 },
+ { 0x21a4, 0x21a4 },
+ { 0x21a5, 0x21a5 },
+ { 0x21a6, 0x21a6 },
+ { 0x21a7, 0x21a7 },
+ { 0x21a8, 0x21a8 },
+ { 0x21a9, 0x21a9 },
+ { 0x21aa, 0x21aa },
+ { 0x21ab, 0x21ab },
+ { 0x21ac, 0x21ac },
+ { 0x21ad, 0x21ad },
+ { 0x21ae, 0x21ae },
+ { 0x21af, 0x21af },
+ { 0x21b0, 0x21b0 },
+ { 0x21b1, 0x21b1 },
+ { 0x21b2, 0x21b2 },
+ { 0x21b3, 0x21b3 },
+ { 0x21b4, 0x21b4 },
+ { 0x21b5, 0x21b5 },
+ { 0x21b6, 0x21b6 },
+ { 0x21b7, 0x21b7 },
+ { 0x21b8, 0x21b8 },
+ { 0x21b9, 0x21b9 },
+ { 0x21ba, 0x21ba },
+ { 0x21bb, 0x21bb },
+ { 0x21bc, 0x21bc },
+ { 0x21bd, 0x21bd },
+ { 0x21be, 0x21be },
+ { 0x21bf, 0x21bf },
+ { 0x21c0, 0x21c0 },
+ { 0x21c1, 0x21c1 },
+ { 0x21c2, 0x21c2 },
+ { 0x21c3, 0x21c3 },
+ { 0x21c4, 0x21c4 },
+ { 0x21c5, 0x21c5 },
+ { 0x21c6, 0x21c6 },
+ { 0x21c7, 0x21c7 },
+ { 0x21c8, 0x21c8 },
+ { 0x21c9, 0x21c9 },
+ { 0x21ca, 0x21ca },
+ { 0x21cb, 0x21cb },
+ { 0x21cc, 0x21cc },
+ { 0x21cd, 0x21cd },
+ { 0x21ce, 0x21ce },
+ { 0x21cf, 0x21cf },
+ { 0x21d0, 0x21d0 },
+ { 0x21d1, 0x21d1 },
+ { 0x21d2, 0x21d2 },
+ { 0x21d3, 0x21d3 },
+ { 0x21d4, 0x21d4 },
+ { 0x21d5, 0x21d5 },
+ { 0x21d6, 0x21d6 },
+ { 0x21d7, 0x21d7 },
+ { 0x21d8, 0x21d8 },
+ { 0x21d9, 0x21d9 },
+ { 0x21da, 0x21da },
+ { 0x21db, 0x21db },
+ { 0x21dc, 0x21dc },
+ { 0x21dd, 0x21dd },
+ { 0x21de, 0x21de },
+ { 0x21df, 0x21df },
+ { 0x21e0, 0x21e0 },
+ { 0x21e1, 0x21e1 },
+ { 0x21e2, 0x21e2 },
+ { 0x21e3, 0x21e3 },
+ { 0x21e4, 0x21e4 },
+ { 0x21e5, 0x21e5 },
+ { 0x21e6, 0x21e6 },
+ { 0x21e7, 0x21e7 },
+ { 0x21e8, 0x21e8 },
+ { 0x21e9, 0x21e9 },
+ { 0x21ea, 0x21ea },
+ { 0x21eb, 0x21eb },
+ { 0x21ec, 0x21ec },
+ { 0x21ed, 0x21ed },
+ { 0x21ee, 0x21ee },
+ { 0x21ef, 0x21ef },
+ { 0x21f0, 0x21f0 },
+ { 0x21f1, 0x21f1 },
+ { 0x21f2, 0x21f2 },
+ { 0x21f3, 0x21f3 },
+ { 0x21f4, 0x21f4 },
+ { 0x21f5, 0x21f5 },
+ { 0x21f6, 0x21f6 },
+ { 0x21f7, 0x21f7 },
+ { 0x21f8, 0x21f8 },
+ { 0x21f9, 0x21f9 },
+ { 0x21fa, 0x21fa },
+ { 0x21fb, 0x21fb },
+ { 0x21fc, 0x21fc },
+ { 0x21fd, 0x21fd },
+ { 0x21fe, 0x21fe },
+ { 0x21ff, 0x21ff },
+};
+
+gli_case_block_t unigen_case_block_0x24[256] = {
+ { 0x2400, 0x2400 },
+ { 0x2401, 0x2401 },
+ { 0x2402, 0x2402 },
+ { 0x2403, 0x2403 },
+ { 0x2404, 0x2404 },
+ { 0x2405, 0x2405 },
+ { 0x2406, 0x2406 },
+ { 0x2407, 0x2407 },
+ { 0x2408, 0x2408 },
+ { 0x2409, 0x2409 },
+ { 0x240a, 0x240a },
+ { 0x240b, 0x240b },
+ { 0x240c, 0x240c },
+ { 0x240d, 0x240d },
+ { 0x240e, 0x240e },
+ { 0x240f, 0x240f },
+ { 0x2410, 0x2410 },
+ { 0x2411, 0x2411 },
+ { 0x2412, 0x2412 },
+ { 0x2413, 0x2413 },
+ { 0x2414, 0x2414 },
+ { 0x2415, 0x2415 },
+ { 0x2416, 0x2416 },
+ { 0x2417, 0x2417 },
+ { 0x2418, 0x2418 },
+ { 0x2419, 0x2419 },
+ { 0x241a, 0x241a },
+ { 0x241b, 0x241b },
+ { 0x241c, 0x241c },
+ { 0x241d, 0x241d },
+ { 0x241e, 0x241e },
+ { 0x241f, 0x241f },
+ { 0x2420, 0x2420 },
+ { 0x2421, 0x2421 },
+ { 0x2422, 0x2422 },
+ { 0x2423, 0x2423 },
+ { 0x2424, 0x2424 },
+ { 0x2425, 0x2425 },
+ { 0x2426, 0x2426 },
+ { 0x2427, 0x2427 },
+ { 0x2428, 0x2428 },
+ { 0x2429, 0x2429 },
+ { 0x242a, 0x242a },
+ { 0x242b, 0x242b },
+ { 0x242c, 0x242c },
+ { 0x242d, 0x242d },
+ { 0x242e, 0x242e },
+ { 0x242f, 0x242f },
+ { 0x2430, 0x2430 },
+ { 0x2431, 0x2431 },
+ { 0x2432, 0x2432 },
+ { 0x2433, 0x2433 },
+ { 0x2434, 0x2434 },
+ { 0x2435, 0x2435 },
+ { 0x2436, 0x2436 },
+ { 0x2437, 0x2437 },
+ { 0x2438, 0x2438 },
+ { 0x2439, 0x2439 },
+ { 0x243a, 0x243a },
+ { 0x243b, 0x243b },
+ { 0x243c, 0x243c },
+ { 0x243d, 0x243d },
+ { 0x243e, 0x243e },
+ { 0x243f, 0x243f },
+ { 0x2440, 0x2440 },
+ { 0x2441, 0x2441 },
+ { 0x2442, 0x2442 },
+ { 0x2443, 0x2443 },
+ { 0x2444, 0x2444 },
+ { 0x2445, 0x2445 },
+ { 0x2446, 0x2446 },
+ { 0x2447, 0x2447 },
+ { 0x2448, 0x2448 },
+ { 0x2449, 0x2449 },
+ { 0x244a, 0x244a },
+ { 0x244b, 0x244b },
+ { 0x244c, 0x244c },
+ { 0x244d, 0x244d },
+ { 0x244e, 0x244e },
+ { 0x244f, 0x244f },
+ { 0x2450, 0x2450 },
+ { 0x2451, 0x2451 },
+ { 0x2452, 0x2452 },
+ { 0x2453, 0x2453 },
+ { 0x2454, 0x2454 },
+ { 0x2455, 0x2455 },
+ { 0x2456, 0x2456 },
+ { 0x2457, 0x2457 },
+ { 0x2458, 0x2458 },
+ { 0x2459, 0x2459 },
+ { 0x245a, 0x245a },
+ { 0x245b, 0x245b },
+ { 0x245c, 0x245c },
+ { 0x245d, 0x245d },
+ { 0x245e, 0x245e },
+ { 0x245f, 0x245f },
+ { 0x2460, 0x2460 },
+ { 0x2461, 0x2461 },
+ { 0x2462, 0x2462 },
+ { 0x2463, 0x2463 },
+ { 0x2464, 0x2464 },
+ { 0x2465, 0x2465 },
+ { 0x2466, 0x2466 },
+ { 0x2467, 0x2467 },
+ { 0x2468, 0x2468 },
+ { 0x2469, 0x2469 },
+ { 0x246a, 0x246a },
+ { 0x246b, 0x246b },
+ { 0x246c, 0x246c },
+ { 0x246d, 0x246d },
+ { 0x246e, 0x246e },
+ { 0x246f, 0x246f },
+ { 0x2470, 0x2470 },
+ { 0x2471, 0x2471 },
+ { 0x2472, 0x2472 },
+ { 0x2473, 0x2473 },
+ { 0x2474, 0x2474 },
+ { 0x2475, 0x2475 },
+ { 0x2476, 0x2476 },
+ { 0x2477, 0x2477 },
+ { 0x2478, 0x2478 },
+ { 0x2479, 0x2479 },
+ { 0x247a, 0x247a },
+ { 0x247b, 0x247b },
+ { 0x247c, 0x247c },
+ { 0x247d, 0x247d },
+ { 0x247e, 0x247e },
+ { 0x247f, 0x247f },
+ { 0x2480, 0x2480 },
+ { 0x2481, 0x2481 },
+ { 0x2482, 0x2482 },
+ { 0x2483, 0x2483 },
+ { 0x2484, 0x2484 },
+ { 0x2485, 0x2485 },
+ { 0x2486, 0x2486 },
+ { 0x2487, 0x2487 },
+ { 0x2488, 0x2488 },
+ { 0x2489, 0x2489 },
+ { 0x248a, 0x248a },
+ { 0x248b, 0x248b },
+ { 0x248c, 0x248c },
+ { 0x248d, 0x248d },
+ { 0x248e, 0x248e },
+ { 0x248f, 0x248f },
+ { 0x2490, 0x2490 },
+ { 0x2491, 0x2491 },
+ { 0x2492, 0x2492 },
+ { 0x2493, 0x2493 },
+ { 0x2494, 0x2494 },
+ { 0x2495, 0x2495 },
+ { 0x2496, 0x2496 },
+ { 0x2497, 0x2497 },
+ { 0x2498, 0x2498 },
+ { 0x2499, 0x2499 },
+ { 0x249a, 0x249a },
+ { 0x249b, 0x249b },
+ { 0x249c, 0x249c },
+ { 0x249d, 0x249d },
+ { 0x249e, 0x249e },
+ { 0x249f, 0x249f },
+ { 0x24a0, 0x24a0 },
+ { 0x24a1, 0x24a1 },
+ { 0x24a2, 0x24a2 },
+ { 0x24a3, 0x24a3 },
+ { 0x24a4, 0x24a4 },
+ { 0x24a5, 0x24a5 },
+ { 0x24a6, 0x24a6 },
+ { 0x24a7, 0x24a7 },
+ { 0x24a8, 0x24a8 },
+ { 0x24a9, 0x24a9 },
+ { 0x24aa, 0x24aa },
+ { 0x24ab, 0x24ab },
+ { 0x24ac, 0x24ac },
+ { 0x24ad, 0x24ad },
+ { 0x24ae, 0x24ae },
+ { 0x24af, 0x24af },
+ { 0x24b0, 0x24b0 },
+ { 0x24b1, 0x24b1 },
+ { 0x24b2, 0x24b2 },
+ { 0x24b3, 0x24b3 },
+ { 0x24b4, 0x24b4 },
+ { 0x24b5, 0x24b5 },
+ { 0x24b6, 0x24d0 }, /* upper */
+ { 0x24b7, 0x24d1 }, /* upper */
+ { 0x24b8, 0x24d2 }, /* upper */
+ { 0x24b9, 0x24d3 }, /* upper */
+ { 0x24ba, 0x24d4 }, /* upper */
+ { 0x24bb, 0x24d5 }, /* upper */
+ { 0x24bc, 0x24d6 }, /* upper */
+ { 0x24bd, 0x24d7 }, /* upper */
+ { 0x24be, 0x24d8 }, /* upper */
+ { 0x24bf, 0x24d9 }, /* upper */
+ { 0x24c0, 0x24da }, /* upper */
+ { 0x24c1, 0x24db }, /* upper */
+ { 0x24c2, 0x24dc }, /* upper */
+ { 0x24c3, 0x24dd }, /* upper */
+ { 0x24c4, 0x24de }, /* upper */
+ { 0x24c5, 0x24df }, /* upper */
+ { 0x24c6, 0x24e0 }, /* upper */
+ { 0x24c7, 0x24e1 }, /* upper */
+ { 0x24c8, 0x24e2 }, /* upper */
+ { 0x24c9, 0x24e3 }, /* upper */
+ { 0x24ca, 0x24e4 }, /* upper */
+ { 0x24cb, 0x24e5 }, /* upper */
+ { 0x24cc, 0x24e6 }, /* upper */
+ { 0x24cd, 0x24e7 }, /* upper */
+ { 0x24ce, 0x24e8 }, /* upper */
+ { 0x24cf, 0x24e9 }, /* upper */
+ { 0x24b6, 0x24d0 }, /* lower */
+ { 0x24b7, 0x24d1 }, /* lower */
+ { 0x24b8, 0x24d2 }, /* lower */
+ { 0x24b9, 0x24d3 }, /* lower */
+ { 0x24ba, 0x24d4 }, /* lower */
+ { 0x24bb, 0x24d5 }, /* lower */
+ { 0x24bc, 0x24d6 }, /* lower */
+ { 0x24bd, 0x24d7 }, /* lower */
+ { 0x24be, 0x24d8 }, /* lower */
+ { 0x24bf, 0x24d9 }, /* lower */
+ { 0x24c0, 0x24da }, /* lower */
+ { 0x24c1, 0x24db }, /* lower */
+ { 0x24c2, 0x24dc }, /* lower */
+ { 0x24c3, 0x24dd }, /* lower */
+ { 0x24c4, 0x24de }, /* lower */
+ { 0x24c5, 0x24df }, /* lower */
+ { 0x24c6, 0x24e0 }, /* lower */
+ { 0x24c7, 0x24e1 }, /* lower */
+ { 0x24c8, 0x24e2 }, /* lower */
+ { 0x24c9, 0x24e3 }, /* lower */
+ { 0x24ca, 0x24e4 }, /* lower */
+ { 0x24cb, 0x24e5 }, /* lower */
+ { 0x24cc, 0x24e6 }, /* lower */
+ { 0x24cd, 0x24e7 }, /* lower */
+ { 0x24ce, 0x24e8 }, /* lower */
+ { 0x24cf, 0x24e9 }, /* lower */
+ { 0x24ea, 0x24ea },
+ { 0x24eb, 0x24eb },
+ { 0x24ec, 0x24ec },
+ { 0x24ed, 0x24ed },
+ { 0x24ee, 0x24ee },
+ { 0x24ef, 0x24ef },
+ { 0x24f0, 0x24f0 },
+ { 0x24f1, 0x24f1 },
+ { 0x24f2, 0x24f2 },
+ { 0x24f3, 0x24f3 },
+ { 0x24f4, 0x24f4 },
+ { 0x24f5, 0x24f5 },
+ { 0x24f6, 0x24f6 },
+ { 0x24f7, 0x24f7 },
+ { 0x24f8, 0x24f8 },
+ { 0x24f9, 0x24f9 },
+ { 0x24fa, 0x24fa },
+ { 0x24fb, 0x24fb },
+ { 0x24fc, 0x24fc },
+ { 0x24fd, 0x24fd },
+ { 0x24fe, 0x24fe },
+ { 0x24ff, 0x24ff },
+};
+
+gli_case_block_t unigen_case_block_0xfb[256] = {
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xfb07, 0xfb07 },
+ { 0xfb08, 0xfb08 },
+ { 0xfb09, 0xfb09 },
+ { 0xfb0a, 0xfb0a },
+ { 0xfb0b, 0xfb0b },
+ { 0xfb0c, 0xfb0c },
+ { 0xfb0d, 0xfb0d },
+ { 0xfb0e, 0xfb0e },
+ { 0xfb0f, 0xfb0f },
+ { 0xfb10, 0xfb10 },
+ { 0xfb11, 0xfb11 },
+ { 0xfb12, 0xfb12 },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0xfb18, 0xfb18 },
+ { 0xfb19, 0xfb19 },
+ { 0xfb1a, 0xfb1a },
+ { 0xfb1b, 0xfb1b },
+ { 0xfb1c, 0xfb1c },
+ { 0xfb1d, 0xfb1d },
+ { 0xfb1e, 0xfb1e },
+ { 0xfb1f, 0xfb1f },
+ { 0xfb20, 0xfb20 },
+ { 0xfb21, 0xfb21 },
+ { 0xfb22, 0xfb22 },
+ { 0xfb23, 0xfb23 },
+ { 0xfb24, 0xfb24 },
+ { 0xfb25, 0xfb25 },
+ { 0xfb26, 0xfb26 },
+ { 0xfb27, 0xfb27 },
+ { 0xfb28, 0xfb28 },
+ { 0xfb29, 0xfb29 },
+ { 0xfb2a, 0xfb2a },
+ { 0xfb2b, 0xfb2b },
+ { 0xfb2c, 0xfb2c },
+ { 0xfb2d, 0xfb2d },
+ { 0xfb2e, 0xfb2e },
+ { 0xfb2f, 0xfb2f },
+ { 0xfb30, 0xfb30 },
+ { 0xfb31, 0xfb31 },
+ { 0xfb32, 0xfb32 },
+ { 0xfb33, 0xfb33 },
+ { 0xfb34, 0xfb34 },
+ { 0xfb35, 0xfb35 },
+ { 0xfb36, 0xfb36 },
+ { 0xfb37, 0xfb37 },
+ { 0xfb38, 0xfb38 },
+ { 0xfb39, 0xfb39 },
+ { 0xfb3a, 0xfb3a },
+ { 0xfb3b, 0xfb3b },
+ { 0xfb3c, 0xfb3c },
+ { 0xfb3d, 0xfb3d },
+ { 0xfb3e, 0xfb3e },
+ { 0xfb3f, 0xfb3f },
+ { 0xfb40, 0xfb40 },
+ { 0xfb41, 0xfb41 },
+ { 0xfb42, 0xfb42 },
+ { 0xfb43, 0xfb43 },
+ { 0xfb44, 0xfb44 },
+ { 0xfb45, 0xfb45 },
+ { 0xfb46, 0xfb46 },
+ { 0xfb47, 0xfb47 },
+ { 0xfb48, 0xfb48 },
+ { 0xfb49, 0xfb49 },
+ { 0xfb4a, 0xfb4a },
+ { 0xfb4b, 0xfb4b },
+ { 0xfb4c, 0xfb4c },
+ { 0xfb4d, 0xfb4d },
+ { 0xfb4e, 0xfb4e },
+ { 0xfb4f, 0xfb4f },
+ { 0xfb50, 0xfb50 },
+ { 0xfb51, 0xfb51 },
+ { 0xfb52, 0xfb52 },
+ { 0xfb53, 0xfb53 },
+ { 0xfb54, 0xfb54 },
+ { 0xfb55, 0xfb55 },
+ { 0xfb56, 0xfb56 },
+ { 0xfb57, 0xfb57 },
+ { 0xfb58, 0xfb58 },
+ { 0xfb59, 0xfb59 },
+ { 0xfb5a, 0xfb5a },
+ { 0xfb5b, 0xfb5b },
+ { 0xfb5c, 0xfb5c },
+ { 0xfb5d, 0xfb5d },
+ { 0xfb5e, 0xfb5e },
+ { 0xfb5f, 0xfb5f },
+ { 0xfb60, 0xfb60 },
+ { 0xfb61, 0xfb61 },
+ { 0xfb62, 0xfb62 },
+ { 0xfb63, 0xfb63 },
+ { 0xfb64, 0xfb64 },
+ { 0xfb65, 0xfb65 },
+ { 0xfb66, 0xfb66 },
+ { 0xfb67, 0xfb67 },
+ { 0xfb68, 0xfb68 },
+ { 0xfb69, 0xfb69 },
+ { 0xfb6a, 0xfb6a },
+ { 0xfb6b, 0xfb6b },
+ { 0xfb6c, 0xfb6c },
+ { 0xfb6d, 0xfb6d },
+ { 0xfb6e, 0xfb6e },
+ { 0xfb6f, 0xfb6f },
+ { 0xfb70, 0xfb70 },
+ { 0xfb71, 0xfb71 },
+ { 0xfb72, 0xfb72 },
+ { 0xfb73, 0xfb73 },
+ { 0xfb74, 0xfb74 },
+ { 0xfb75, 0xfb75 },
+ { 0xfb76, 0xfb76 },
+ { 0xfb77, 0xfb77 },
+ { 0xfb78, 0xfb78 },
+ { 0xfb79, 0xfb79 },
+ { 0xfb7a, 0xfb7a },
+ { 0xfb7b, 0xfb7b },
+ { 0xfb7c, 0xfb7c },
+ { 0xfb7d, 0xfb7d },
+ { 0xfb7e, 0xfb7e },
+ { 0xfb7f, 0xfb7f },
+ { 0xfb80, 0xfb80 },
+ { 0xfb81, 0xfb81 },
+ { 0xfb82, 0xfb82 },
+ { 0xfb83, 0xfb83 },
+ { 0xfb84, 0xfb84 },
+ { 0xfb85, 0xfb85 },
+ { 0xfb86, 0xfb86 },
+ { 0xfb87, 0xfb87 },
+ { 0xfb88, 0xfb88 },
+ { 0xfb89, 0xfb89 },
+ { 0xfb8a, 0xfb8a },
+ { 0xfb8b, 0xfb8b },
+ { 0xfb8c, 0xfb8c },
+ { 0xfb8d, 0xfb8d },
+ { 0xfb8e, 0xfb8e },
+ { 0xfb8f, 0xfb8f },
+ { 0xfb90, 0xfb90 },
+ { 0xfb91, 0xfb91 },
+ { 0xfb92, 0xfb92 },
+ { 0xfb93, 0xfb93 },
+ { 0xfb94, 0xfb94 },
+ { 0xfb95, 0xfb95 },
+ { 0xfb96, 0xfb96 },
+ { 0xfb97, 0xfb97 },
+ { 0xfb98, 0xfb98 },
+ { 0xfb99, 0xfb99 },
+ { 0xfb9a, 0xfb9a },
+ { 0xfb9b, 0xfb9b },
+ { 0xfb9c, 0xfb9c },
+ { 0xfb9d, 0xfb9d },
+ { 0xfb9e, 0xfb9e },
+ { 0xfb9f, 0xfb9f },
+ { 0xfba0, 0xfba0 },
+ { 0xfba1, 0xfba1 },
+ { 0xfba2, 0xfba2 },
+ { 0xfba3, 0xfba3 },
+ { 0xfba4, 0xfba4 },
+ { 0xfba5, 0xfba5 },
+ { 0xfba6, 0xfba6 },
+ { 0xfba7, 0xfba7 },
+ { 0xfba8, 0xfba8 },
+ { 0xfba9, 0xfba9 },
+ { 0xfbaa, 0xfbaa },
+ { 0xfbab, 0xfbab },
+ { 0xfbac, 0xfbac },
+ { 0xfbad, 0xfbad },
+ { 0xfbae, 0xfbae },
+ { 0xfbaf, 0xfbaf },
+ { 0xfbb0, 0xfbb0 },
+ { 0xfbb1, 0xfbb1 },
+ { 0xfbb2, 0xfbb2 },
+ { 0xfbb3, 0xfbb3 },
+ { 0xfbb4, 0xfbb4 },
+ { 0xfbb5, 0xfbb5 },
+ { 0xfbb6, 0xfbb6 },
+ { 0xfbb7, 0xfbb7 },
+ { 0xfbb8, 0xfbb8 },
+ { 0xfbb9, 0xfbb9 },
+ { 0xfbba, 0xfbba },
+ { 0xfbbb, 0xfbbb },
+ { 0xfbbc, 0xfbbc },
+ { 0xfbbd, 0xfbbd },
+ { 0xfbbe, 0xfbbe },
+ { 0xfbbf, 0xfbbf },
+ { 0xfbc0, 0xfbc0 },
+ { 0xfbc1, 0xfbc1 },
+ { 0xfbc2, 0xfbc2 },
+ { 0xfbc3, 0xfbc3 },
+ { 0xfbc4, 0xfbc4 },
+ { 0xfbc5, 0xfbc5 },
+ { 0xfbc6, 0xfbc6 },
+ { 0xfbc7, 0xfbc7 },
+ { 0xfbc8, 0xfbc8 },
+ { 0xfbc9, 0xfbc9 },
+ { 0xfbca, 0xfbca },
+ { 0xfbcb, 0xfbcb },
+ { 0xfbcc, 0xfbcc },
+ { 0xfbcd, 0xfbcd },
+ { 0xfbce, 0xfbce },
+ { 0xfbcf, 0xfbcf },
+ { 0xfbd0, 0xfbd0 },
+ { 0xfbd1, 0xfbd1 },
+ { 0xfbd2, 0xfbd2 },
+ { 0xfbd3, 0xfbd3 },
+ { 0xfbd4, 0xfbd4 },
+ { 0xfbd5, 0xfbd5 },
+ { 0xfbd6, 0xfbd6 },
+ { 0xfbd7, 0xfbd7 },
+ { 0xfbd8, 0xfbd8 },
+ { 0xfbd9, 0xfbd9 },
+ { 0xfbda, 0xfbda },
+ { 0xfbdb, 0xfbdb },
+ { 0xfbdc, 0xfbdc },
+ { 0xfbdd, 0xfbdd },
+ { 0xfbde, 0xfbde },
+ { 0xfbdf, 0xfbdf },
+ { 0xfbe0, 0xfbe0 },
+ { 0xfbe1, 0xfbe1 },
+ { 0xfbe2, 0xfbe2 },
+ { 0xfbe3, 0xfbe3 },
+ { 0xfbe4, 0xfbe4 },
+ { 0xfbe5, 0xfbe5 },
+ { 0xfbe6, 0xfbe6 },
+ { 0xfbe7, 0xfbe7 },
+ { 0xfbe8, 0xfbe8 },
+ { 0xfbe9, 0xfbe9 },
+ { 0xfbea, 0xfbea },
+ { 0xfbeb, 0xfbeb },
+ { 0xfbec, 0xfbec },
+ { 0xfbed, 0xfbed },
+ { 0xfbee, 0xfbee },
+ { 0xfbef, 0xfbef },
+ { 0xfbf0, 0xfbf0 },
+ { 0xfbf1, 0xfbf1 },
+ { 0xfbf2, 0xfbf2 },
+ { 0xfbf3, 0xfbf3 },
+ { 0xfbf4, 0xfbf4 },
+ { 0xfbf5, 0xfbf5 },
+ { 0xfbf6, 0xfbf6 },
+ { 0xfbf7, 0xfbf7 },
+ { 0xfbf8, 0xfbf8 },
+ { 0xfbf9, 0xfbf9 },
+ { 0xfbfa, 0xfbfa },
+ { 0xfbfb, 0xfbfb },
+ { 0xfbfc, 0xfbfc },
+ { 0xfbfd, 0xfbfd },
+ { 0xfbfe, 0xfbfe },
+ { 0xfbff, 0xfbff },
+};
+
+gli_case_block_t unigen_case_block_0xff[256] = {
+ { 0xff00, 0xff00 },
+ { 0xff01, 0xff01 },
+ { 0xff02, 0xff02 },
+ { 0xff03, 0xff03 },
+ { 0xff04, 0xff04 },
+ { 0xff05, 0xff05 },
+ { 0xff06, 0xff06 },
+ { 0xff07, 0xff07 },
+ { 0xff08, 0xff08 },
+ { 0xff09, 0xff09 },
+ { 0xff0a, 0xff0a },
+ { 0xff0b, 0xff0b },
+ { 0xff0c, 0xff0c },
+ { 0xff0d, 0xff0d },
+ { 0xff0e, 0xff0e },
+ { 0xff0f, 0xff0f },
+ { 0xff10, 0xff10 },
+ { 0xff11, 0xff11 },
+ { 0xff12, 0xff12 },
+ { 0xff13, 0xff13 },
+ { 0xff14, 0xff14 },
+ { 0xff15, 0xff15 },
+ { 0xff16, 0xff16 },
+ { 0xff17, 0xff17 },
+ { 0xff18, 0xff18 },
+ { 0xff19, 0xff19 },
+ { 0xff1a, 0xff1a },
+ { 0xff1b, 0xff1b },
+ { 0xff1c, 0xff1c },
+ { 0xff1d, 0xff1d },
+ { 0xff1e, 0xff1e },
+ { 0xff1f, 0xff1f },
+ { 0xff20, 0xff20 },
+ { 0xff21, 0xff41 }, /* upper */
+ { 0xff22, 0xff42 }, /* upper */
+ { 0xff23, 0xff43 }, /* upper */
+ { 0xff24, 0xff44 }, /* upper */
+ { 0xff25, 0xff45 }, /* upper */
+ { 0xff26, 0xff46 }, /* upper */
+ { 0xff27, 0xff47 }, /* upper */
+ { 0xff28, 0xff48 }, /* upper */
+ { 0xff29, 0xff49 }, /* upper */
+ { 0xff2a, 0xff4a }, /* upper */
+ { 0xff2b, 0xff4b }, /* upper */
+ { 0xff2c, 0xff4c }, /* upper */
+ { 0xff2d, 0xff4d }, /* upper */
+ { 0xff2e, 0xff4e }, /* upper */
+ { 0xff2f, 0xff4f }, /* upper */
+ { 0xff30, 0xff50 }, /* upper */
+ { 0xff31, 0xff51 }, /* upper */
+ { 0xff32, 0xff52 }, /* upper */
+ { 0xff33, 0xff53 }, /* upper */
+ { 0xff34, 0xff54 }, /* upper */
+ { 0xff35, 0xff55 }, /* upper */
+ { 0xff36, 0xff56 }, /* upper */
+ { 0xff37, 0xff57 }, /* upper */
+ { 0xff38, 0xff58 }, /* upper */
+ { 0xff39, 0xff59 }, /* upper */
+ { 0xff3a, 0xff5a }, /* upper */
+ { 0xff3b, 0xff3b },
+ { 0xff3c, 0xff3c },
+ { 0xff3d, 0xff3d },
+ { 0xff3e, 0xff3e },
+ { 0xff3f, 0xff3f },
+ { 0xff40, 0xff40 },
+ { 0xff21, 0xff41 }, /* lower */
+ { 0xff22, 0xff42 }, /* lower */
+ { 0xff23, 0xff43 }, /* lower */
+ { 0xff24, 0xff44 }, /* lower */
+ { 0xff25, 0xff45 }, /* lower */
+ { 0xff26, 0xff46 }, /* lower */
+ { 0xff27, 0xff47 }, /* lower */
+ { 0xff28, 0xff48 }, /* lower */
+ { 0xff29, 0xff49 }, /* lower */
+ { 0xff2a, 0xff4a }, /* lower */
+ { 0xff2b, 0xff4b }, /* lower */
+ { 0xff2c, 0xff4c }, /* lower */
+ { 0xff2d, 0xff4d }, /* lower */
+ { 0xff2e, 0xff4e }, /* lower */
+ { 0xff2f, 0xff4f }, /* lower */
+ { 0xff30, 0xff50 }, /* lower */
+ { 0xff31, 0xff51 }, /* lower */
+ { 0xff32, 0xff52 }, /* lower */
+ { 0xff33, 0xff53 }, /* lower */
+ { 0xff34, 0xff54 }, /* lower */
+ { 0xff35, 0xff55 }, /* lower */
+ { 0xff36, 0xff56 }, /* lower */
+ { 0xff37, 0xff57 }, /* lower */
+ { 0xff38, 0xff58 }, /* lower */
+ { 0xff39, 0xff59 }, /* lower */
+ { 0xff3a, 0xff5a }, /* lower */
+ { 0xff5b, 0xff5b },
+ { 0xff5c, 0xff5c },
+ { 0xff5d, 0xff5d },
+ { 0xff5e, 0xff5e },
+ { 0xff5f, 0xff5f },
+ { 0xff60, 0xff60 },
+ { 0xff61, 0xff61 },
+ { 0xff62, 0xff62 },
+ { 0xff63, 0xff63 },
+ { 0xff64, 0xff64 },
+ { 0xff65, 0xff65 },
+ { 0xff66, 0xff66 },
+ { 0xff67, 0xff67 },
+ { 0xff68, 0xff68 },
+ { 0xff69, 0xff69 },
+ { 0xff6a, 0xff6a },
+ { 0xff6b, 0xff6b },
+ { 0xff6c, 0xff6c },
+ { 0xff6d, 0xff6d },
+ { 0xff6e, 0xff6e },
+ { 0xff6f, 0xff6f },
+ { 0xff70, 0xff70 },
+ { 0xff71, 0xff71 },
+ { 0xff72, 0xff72 },
+ { 0xff73, 0xff73 },
+ { 0xff74, 0xff74 },
+ { 0xff75, 0xff75 },
+ { 0xff76, 0xff76 },
+ { 0xff77, 0xff77 },
+ { 0xff78, 0xff78 },
+ { 0xff79, 0xff79 },
+ { 0xff7a, 0xff7a },
+ { 0xff7b, 0xff7b },
+ { 0xff7c, 0xff7c },
+ { 0xff7d, 0xff7d },
+ { 0xff7e, 0xff7e },
+ { 0xff7f, 0xff7f },
+ { 0xff80, 0xff80 },
+ { 0xff81, 0xff81 },
+ { 0xff82, 0xff82 },
+ { 0xff83, 0xff83 },
+ { 0xff84, 0xff84 },
+ { 0xff85, 0xff85 },
+ { 0xff86, 0xff86 },
+ { 0xff87, 0xff87 },
+ { 0xff88, 0xff88 },
+ { 0xff89, 0xff89 },
+ { 0xff8a, 0xff8a },
+ { 0xff8b, 0xff8b },
+ { 0xff8c, 0xff8c },
+ { 0xff8d, 0xff8d },
+ { 0xff8e, 0xff8e },
+ { 0xff8f, 0xff8f },
+ { 0xff90, 0xff90 },
+ { 0xff91, 0xff91 },
+ { 0xff92, 0xff92 },
+ { 0xff93, 0xff93 },
+ { 0xff94, 0xff94 },
+ { 0xff95, 0xff95 },
+ { 0xff96, 0xff96 },
+ { 0xff97, 0xff97 },
+ { 0xff98, 0xff98 },
+ { 0xff99, 0xff99 },
+ { 0xff9a, 0xff9a },
+ { 0xff9b, 0xff9b },
+ { 0xff9c, 0xff9c },
+ { 0xff9d, 0xff9d },
+ { 0xff9e, 0xff9e },
+ { 0xff9f, 0xff9f },
+ { 0xffa0, 0xffa0 },
+ { 0xffa1, 0xffa1 },
+ { 0xffa2, 0xffa2 },
+ { 0xffa3, 0xffa3 },
+ { 0xffa4, 0xffa4 },
+ { 0xffa5, 0xffa5 },
+ { 0xffa6, 0xffa6 },
+ { 0xffa7, 0xffa7 },
+ { 0xffa8, 0xffa8 },
+ { 0xffa9, 0xffa9 },
+ { 0xffaa, 0xffaa },
+ { 0xffab, 0xffab },
+ { 0xffac, 0xffac },
+ { 0xffad, 0xffad },
+ { 0xffae, 0xffae },
+ { 0xffaf, 0xffaf },
+ { 0xffb0, 0xffb0 },
+ { 0xffb1, 0xffb1 },
+ { 0xffb2, 0xffb2 },
+ { 0xffb3, 0xffb3 },
+ { 0xffb4, 0xffb4 },
+ { 0xffb5, 0xffb5 },
+ { 0xffb6, 0xffb6 },
+ { 0xffb7, 0xffb7 },
+ { 0xffb8, 0xffb8 },
+ { 0xffb9, 0xffb9 },
+ { 0xffba, 0xffba },
+ { 0xffbb, 0xffbb },
+ { 0xffbc, 0xffbc },
+ { 0xffbd, 0xffbd },
+ { 0xffbe, 0xffbe },
+ { 0xffbf, 0xffbf },
+ { 0xffc0, 0xffc0 },
+ { 0xffc1, 0xffc1 },
+ { 0xffc2, 0xffc2 },
+ { 0xffc3, 0xffc3 },
+ { 0xffc4, 0xffc4 },
+ { 0xffc5, 0xffc5 },
+ { 0xffc6, 0xffc6 },
+ { 0xffc7, 0xffc7 },
+ { 0xffc8, 0xffc8 },
+ { 0xffc9, 0xffc9 },
+ { 0xffca, 0xffca },
+ { 0xffcb, 0xffcb },
+ { 0xffcc, 0xffcc },
+ { 0xffcd, 0xffcd },
+ { 0xffce, 0xffce },
+ { 0xffcf, 0xffcf },
+ { 0xffd0, 0xffd0 },
+ { 0xffd1, 0xffd1 },
+ { 0xffd2, 0xffd2 },
+ { 0xffd3, 0xffd3 },
+ { 0xffd4, 0xffd4 },
+ { 0xffd5, 0xffd5 },
+ { 0xffd6, 0xffd6 },
+ { 0xffd7, 0xffd7 },
+ { 0xffd8, 0xffd8 },
+ { 0xffd9, 0xffd9 },
+ { 0xffda, 0xffda },
+ { 0xffdb, 0xffdb },
+ { 0xffdc, 0xffdc },
+ { 0xffdd, 0xffdd },
+ { 0xffde, 0xffde },
+ { 0xffdf, 0xffdf },
+ { 0xffe0, 0xffe0 },
+ { 0xffe1, 0xffe1 },
+ { 0xffe2, 0xffe2 },
+ { 0xffe3, 0xffe3 },
+ { 0xffe4, 0xffe4 },
+ { 0xffe5, 0xffe5 },
+ { 0xffe6, 0xffe6 },
+ { 0xffe7, 0xffe7 },
+ { 0xffe8, 0xffe8 },
+ { 0xffe9, 0xffe9 },
+ { 0xffea, 0xffea },
+ { 0xffeb, 0xffeb },
+ { 0xffec, 0xffec },
+ { 0xffed, 0xffed },
+ { 0xffee, 0xffee },
+ { 0xffef, 0xffef },
+ { 0xfff0, 0xfff0 },
+ { 0xfff1, 0xfff1 },
+ { 0xfff2, 0xfff2 },
+ { 0xfff3, 0xfff3 },
+ { 0xfff4, 0xfff4 },
+ { 0xfff5, 0xfff5 },
+ { 0xfff6, 0xfff6 },
+ { 0xfff7, 0xfff7 },
+ { 0xfff8, 0xfff8 },
+ { 0xfff9, 0xfff9 },
+ { 0xfffa, 0xfffa },
+ { 0xfffb, 0xfffb },
+ { 0xfffc, 0xfffc },
+ { 0xfffd, 0xfffd },
+ { 0xfffe, 0xfffe },
+ { 0xffff, 0xffff },
+};
+
+gli_case_block_t unigen_case_block_0x104[256] = {
+ { 0x10400, 0x10428 }, /* upper */
+ { 0x10401, 0x10429 }, /* upper */
+ { 0x10402, 0x1042a }, /* upper */
+ { 0x10403, 0x1042b }, /* upper */
+ { 0x10404, 0x1042c }, /* upper */
+ { 0x10405, 0x1042d }, /* upper */
+ { 0x10406, 0x1042e }, /* upper */
+ { 0x10407, 0x1042f }, /* upper */
+ { 0x10408, 0x10430 }, /* upper */
+ { 0x10409, 0x10431 }, /* upper */
+ { 0x1040a, 0x10432 }, /* upper */
+ { 0x1040b, 0x10433 }, /* upper */
+ { 0x1040c, 0x10434 }, /* upper */
+ { 0x1040d, 0x10435 }, /* upper */
+ { 0x1040e, 0x10436 }, /* upper */
+ { 0x1040f, 0x10437 }, /* upper */
+ { 0x10410, 0x10438 }, /* upper */
+ { 0x10411, 0x10439 }, /* upper */
+ { 0x10412, 0x1043a }, /* upper */
+ { 0x10413, 0x1043b }, /* upper */
+ { 0x10414, 0x1043c }, /* upper */
+ { 0x10415, 0x1043d }, /* upper */
+ { 0x10416, 0x1043e }, /* upper */
+ { 0x10417, 0x1043f }, /* upper */
+ { 0x10418, 0x10440 }, /* upper */
+ { 0x10419, 0x10441 }, /* upper */
+ { 0x1041a, 0x10442 }, /* upper */
+ { 0x1041b, 0x10443 }, /* upper */
+ { 0x1041c, 0x10444 }, /* upper */
+ { 0x1041d, 0x10445 }, /* upper */
+ { 0x1041e, 0x10446 }, /* upper */
+ { 0x1041f, 0x10447 }, /* upper */
+ { 0x10420, 0x10448 }, /* upper */
+ { 0x10421, 0x10449 }, /* upper */
+ { 0x10422, 0x1044a }, /* upper */
+ { 0x10423, 0x1044b }, /* upper */
+ { 0x10424, 0x1044c }, /* upper */
+ { 0x10425, 0x1044d }, /* upper */
+ { 0x10426, 0x1044e }, /* upper */
+ { 0x10427, 0x1044f }, /* upper */
+ { 0x10400, 0x10428 }, /* lower */
+ { 0x10401, 0x10429 }, /* lower */
+ { 0x10402, 0x1042a }, /* lower */
+ { 0x10403, 0x1042b }, /* lower */
+ { 0x10404, 0x1042c }, /* lower */
+ { 0x10405, 0x1042d }, /* lower */
+ { 0x10406, 0x1042e }, /* lower */
+ { 0x10407, 0x1042f }, /* lower */
+ { 0x10408, 0x10430 }, /* lower */
+ { 0x10409, 0x10431 }, /* lower */
+ { 0x1040a, 0x10432 }, /* lower */
+ { 0x1040b, 0x10433 }, /* lower */
+ { 0x1040c, 0x10434 }, /* lower */
+ { 0x1040d, 0x10435 }, /* lower */
+ { 0x1040e, 0x10436 }, /* lower */
+ { 0x1040f, 0x10437 }, /* lower */
+ { 0x10410, 0x10438 }, /* lower */
+ { 0x10411, 0x10439 }, /* lower */
+ { 0x10412, 0x1043a }, /* lower */
+ { 0x10413, 0x1043b }, /* lower */
+ { 0x10414, 0x1043c }, /* lower */
+ { 0x10415, 0x1043d }, /* lower */
+ { 0x10416, 0x1043e }, /* lower */
+ { 0x10417, 0x1043f }, /* lower */
+ { 0x10418, 0x10440 }, /* lower */
+ { 0x10419, 0x10441 }, /* lower */
+ { 0x1041a, 0x10442 }, /* lower */
+ { 0x1041b, 0x10443 }, /* lower */
+ { 0x1041c, 0x10444 }, /* lower */
+ { 0x1041d, 0x10445 }, /* lower */
+ { 0x1041e, 0x10446 }, /* lower */
+ { 0x1041f, 0x10447 }, /* lower */
+ { 0x10420, 0x10448 }, /* lower */
+ { 0x10421, 0x10449 }, /* lower */
+ { 0x10422, 0x1044a }, /* lower */
+ { 0x10423, 0x1044b }, /* lower */
+ { 0x10424, 0x1044c }, /* lower */
+ { 0x10425, 0x1044d }, /* lower */
+ { 0x10426, 0x1044e }, /* lower */
+ { 0x10427, 0x1044f }, /* lower */
+ { 0x10450, 0x10450 },
+ { 0x10451, 0x10451 },
+ { 0x10452, 0x10452 },
+ { 0x10453, 0x10453 },
+ { 0x10454, 0x10454 },
+ { 0x10455, 0x10455 },
+ { 0x10456, 0x10456 },
+ { 0x10457, 0x10457 },
+ { 0x10458, 0x10458 },
+ { 0x10459, 0x10459 },
+ { 0x1045a, 0x1045a },
+ { 0x1045b, 0x1045b },
+ { 0x1045c, 0x1045c },
+ { 0x1045d, 0x1045d },
+ { 0x1045e, 0x1045e },
+ { 0x1045f, 0x1045f },
+ { 0x10460, 0x10460 },
+ { 0x10461, 0x10461 },
+ { 0x10462, 0x10462 },
+ { 0x10463, 0x10463 },
+ { 0x10464, 0x10464 },
+ { 0x10465, 0x10465 },
+ { 0x10466, 0x10466 },
+ { 0x10467, 0x10467 },
+ { 0x10468, 0x10468 },
+ { 0x10469, 0x10469 },
+ { 0x1046a, 0x1046a },
+ { 0x1046b, 0x1046b },
+ { 0x1046c, 0x1046c },
+ { 0x1046d, 0x1046d },
+ { 0x1046e, 0x1046e },
+ { 0x1046f, 0x1046f },
+ { 0x10470, 0x10470 },
+ { 0x10471, 0x10471 },
+ { 0x10472, 0x10472 },
+ { 0x10473, 0x10473 },
+ { 0x10474, 0x10474 },
+ { 0x10475, 0x10475 },
+ { 0x10476, 0x10476 },
+ { 0x10477, 0x10477 },
+ { 0x10478, 0x10478 },
+ { 0x10479, 0x10479 },
+ { 0x1047a, 0x1047a },
+ { 0x1047b, 0x1047b },
+ { 0x1047c, 0x1047c },
+ { 0x1047d, 0x1047d },
+ { 0x1047e, 0x1047e },
+ { 0x1047f, 0x1047f },
+ { 0x10480, 0x10480 },
+ { 0x10481, 0x10481 },
+ { 0x10482, 0x10482 },
+ { 0x10483, 0x10483 },
+ { 0x10484, 0x10484 },
+ { 0x10485, 0x10485 },
+ { 0x10486, 0x10486 },
+ { 0x10487, 0x10487 },
+ { 0x10488, 0x10488 },
+ { 0x10489, 0x10489 },
+ { 0x1048a, 0x1048a },
+ { 0x1048b, 0x1048b },
+ { 0x1048c, 0x1048c },
+ { 0x1048d, 0x1048d },
+ { 0x1048e, 0x1048e },
+ { 0x1048f, 0x1048f },
+ { 0x10490, 0x10490 },
+ { 0x10491, 0x10491 },
+ { 0x10492, 0x10492 },
+ { 0x10493, 0x10493 },
+ { 0x10494, 0x10494 },
+ { 0x10495, 0x10495 },
+ { 0x10496, 0x10496 },
+ { 0x10497, 0x10497 },
+ { 0x10498, 0x10498 },
+ { 0x10499, 0x10499 },
+ { 0x1049a, 0x1049a },
+ { 0x1049b, 0x1049b },
+ { 0x1049c, 0x1049c },
+ { 0x1049d, 0x1049d },
+ { 0x1049e, 0x1049e },
+ { 0x1049f, 0x1049f },
+ { 0x104a0, 0x104a0 },
+ { 0x104a1, 0x104a1 },
+ { 0x104a2, 0x104a2 },
+ { 0x104a3, 0x104a3 },
+ { 0x104a4, 0x104a4 },
+ { 0x104a5, 0x104a5 },
+ { 0x104a6, 0x104a6 },
+ { 0x104a7, 0x104a7 },
+ { 0x104a8, 0x104a8 },
+ { 0x104a9, 0x104a9 },
+ { 0x104aa, 0x104aa },
+ { 0x104ab, 0x104ab },
+ { 0x104ac, 0x104ac },
+ { 0x104ad, 0x104ad },
+ { 0x104ae, 0x104ae },
+ { 0x104af, 0x104af },
+ { 0x104b0, 0x104b0 },
+ { 0x104b1, 0x104b1 },
+ { 0x104b2, 0x104b2 },
+ { 0x104b3, 0x104b3 },
+ { 0x104b4, 0x104b4 },
+ { 0x104b5, 0x104b5 },
+ { 0x104b6, 0x104b6 },
+ { 0x104b7, 0x104b7 },
+ { 0x104b8, 0x104b8 },
+ { 0x104b9, 0x104b9 },
+ { 0x104ba, 0x104ba },
+ { 0x104bb, 0x104bb },
+ { 0x104bc, 0x104bc },
+ { 0x104bd, 0x104bd },
+ { 0x104be, 0x104be },
+ { 0x104bf, 0x104bf },
+ { 0x104c0, 0x104c0 },
+ { 0x104c1, 0x104c1 },
+ { 0x104c2, 0x104c2 },
+ { 0x104c3, 0x104c3 },
+ { 0x104c4, 0x104c4 },
+ { 0x104c5, 0x104c5 },
+ { 0x104c6, 0x104c6 },
+ { 0x104c7, 0x104c7 },
+ { 0x104c8, 0x104c8 },
+ { 0x104c9, 0x104c9 },
+ { 0x104ca, 0x104ca },
+ { 0x104cb, 0x104cb },
+ { 0x104cc, 0x104cc },
+ { 0x104cd, 0x104cd },
+ { 0x104ce, 0x104ce },
+ { 0x104cf, 0x104cf },
+ { 0x104d0, 0x104d0 },
+ { 0x104d1, 0x104d1 },
+ { 0x104d2, 0x104d2 },
+ { 0x104d3, 0x104d3 },
+ { 0x104d4, 0x104d4 },
+ { 0x104d5, 0x104d5 },
+ { 0x104d6, 0x104d6 },
+ { 0x104d7, 0x104d7 },
+ { 0x104d8, 0x104d8 },
+ { 0x104d9, 0x104d9 },
+ { 0x104da, 0x104da },
+ { 0x104db, 0x104db },
+ { 0x104dc, 0x104dc },
+ { 0x104dd, 0x104dd },
+ { 0x104de, 0x104de },
+ { 0x104df, 0x104df },
+ { 0x104e0, 0x104e0 },
+ { 0x104e1, 0x104e1 },
+ { 0x104e2, 0x104e2 },
+ { 0x104e3, 0x104e3 },
+ { 0x104e4, 0x104e4 },
+ { 0x104e5, 0x104e5 },
+ { 0x104e6, 0x104e6 },
+ { 0x104e7, 0x104e7 },
+ { 0x104e8, 0x104e8 },
+ { 0x104e9, 0x104e9 },
+ { 0x104ea, 0x104ea },
+ { 0x104eb, 0x104eb },
+ { 0x104ec, 0x104ec },
+ { 0x104ed, 0x104ed },
+ { 0x104ee, 0x104ee },
+ { 0x104ef, 0x104ef },
+ { 0x104f0, 0x104f0 },
+ { 0x104f1, 0x104f1 },
+ { 0x104f2, 0x104f2 },
+ { 0x104f3, 0x104f3 },
+ { 0x104f4, 0x104f4 },
+ { 0x104f5, 0x104f5 },
+ { 0x104f6, 0x104f6 },
+ { 0x104f7, 0x104f7 },
+ { 0x104f8, 0x104f8 },
+ { 0x104f9, 0x104f9 },
+ { 0x104fa, 0x104fa },
+ { 0x104fb, 0x104fb },
+ { 0x104fc, 0x104fc },
+ { 0x104fd, 0x104fd },
+ { 0x104fe, 0x104fe },
+ { 0x104ff, 0x104ff },
+};
+
+glui32 unigen_special_array[] = {
+ 2, 0x53, 0x53, /* 0xdf upcase */
+ 1, 0xdf, /* 0xdf downcase */
+ 2, 0x53, 0x73, /* 0xdf titlecase */
+ 1, 0x130, /* 0x130 upcase */
+ 2, 0x69, 0x307, /* 0x130 downcase */
+ 1, 0x130, /* 0x130 titlecase */
+ 2, 0x2bc, 0x4e, /* 0x149 upcase */
+ 1, 0x149, /* 0x149 downcase */
+ 2, 0x2bc, 0x4e, /* 0x149 titlecase */
+ 1, 0x1c4, /* 0x1c4 upcase */
+ 1, 0x1c6, /* 0x1c4 downcase */
+ 1, 0x1c5, /* 0x1c4 titlecase */
+ 1, 0x1c4, /* 0x1c5 upcase */
+ 1, 0x1c6, /* 0x1c5 downcase */
+ 1, 0x1c5, /* 0x1c5 titlecase */
+ 1, 0x1c4, /* 0x1c6 upcase */
+ 1, 0x1c6, /* 0x1c6 downcase */
+ 1, 0x1c5, /* 0x1c6 titlecase */
+ 1, 0x1c7, /* 0x1c7 upcase */
+ 1, 0x1c9, /* 0x1c7 downcase */
+ 1, 0x1c8, /* 0x1c7 titlecase */
+ 1, 0x1c7, /* 0x1c8 upcase */
+ 1, 0x1c9, /* 0x1c8 downcase */
+ 1, 0x1c8, /* 0x1c8 titlecase */
+ 1, 0x1c7, /* 0x1c9 upcase */
+ 1, 0x1c9, /* 0x1c9 downcase */
+ 1, 0x1c8, /* 0x1c9 titlecase */
+ 1, 0x1ca, /* 0x1ca upcase */
+ 1, 0x1cc, /* 0x1ca downcase */
+ 1, 0x1cb, /* 0x1ca titlecase */
+ 1, 0x1ca, /* 0x1cb upcase */
+ 1, 0x1cc, /* 0x1cb downcase */
+ 1, 0x1cb, /* 0x1cb titlecase */
+ 1, 0x1ca, /* 0x1cc upcase */
+ 1, 0x1cc, /* 0x1cc downcase */
+ 1, 0x1cb, /* 0x1cc titlecase */
+ 2, 0x4a, 0x30c, /* 0x1f0 upcase */
+ 1, 0x1f0, /* 0x1f0 downcase */
+ 2, 0x4a, 0x30c, /* 0x1f0 titlecase */
+ 1, 0x1f1, /* 0x1f1 upcase */
+ 1, 0x1f3, /* 0x1f1 downcase */
+ 1, 0x1f2, /* 0x1f1 titlecase */
+ 1, 0x1f1, /* 0x1f2 upcase */
+ 1, 0x1f3, /* 0x1f2 downcase */
+ 1, 0x1f2, /* 0x1f2 titlecase */
+ 1, 0x1f1, /* 0x1f3 upcase */
+ 1, 0x1f3, /* 0x1f3 downcase */
+ 1, 0x1f2, /* 0x1f3 titlecase */
+ 3, 0x399, 0x308, 0x301, /* 0x390 upcase */
+ 1, 0x390, /* 0x390 downcase */
+ 3, 0x399, 0x308, 0x301, /* 0x390 titlecase */
+ 3, 0x3a5, 0x308, 0x301, /* 0x3b0 upcase */
+ 1, 0x3b0, /* 0x3b0 downcase */
+ 3, 0x3a5, 0x308, 0x301, /* 0x3b0 titlecase */
+ 2, 0x535, 0x552, /* 0x587 upcase */
+ 1, 0x587, /* 0x587 downcase */
+ 2, 0x535, 0x582, /* 0x587 titlecase */
+ 2, 0x48, 0x331, /* 0x1e96 upcase */
+ 1, 0x1e96, /* 0x1e96 downcase */
+ 2, 0x48, 0x331, /* 0x1e96 titlecase */
+ 2, 0x54, 0x308, /* 0x1e97 upcase */
+ 1, 0x1e97, /* 0x1e97 downcase */
+ 2, 0x54, 0x308, /* 0x1e97 titlecase */
+ 2, 0x57, 0x30a, /* 0x1e98 upcase */
+ 1, 0x1e98, /* 0x1e98 downcase */
+ 2, 0x57, 0x30a, /* 0x1e98 titlecase */
+ 2, 0x59, 0x30a, /* 0x1e99 upcase */
+ 1, 0x1e99, /* 0x1e99 downcase */
+ 2, 0x59, 0x30a, /* 0x1e99 titlecase */
+ 2, 0x41, 0x2be, /* 0x1e9a upcase */
+ 1, 0x1e9a, /* 0x1e9a downcase */
+ 2, 0x41, 0x2be, /* 0x1e9a titlecase */
+ 2, 0x3a5, 0x313, /* 0x1f50 upcase */
+ 1, 0x1f50, /* 0x1f50 downcase */
+ 2, 0x3a5, 0x313, /* 0x1f50 titlecase */
+ 3, 0x3a5, 0x313, 0x300, /* 0x1f52 upcase */
+ 1, 0x1f52, /* 0x1f52 downcase */
+ 3, 0x3a5, 0x313, 0x300, /* 0x1f52 titlecase */
+ 3, 0x3a5, 0x313, 0x301, /* 0x1f54 upcase */
+ 1, 0x1f54, /* 0x1f54 downcase */
+ 3, 0x3a5, 0x313, 0x301, /* 0x1f54 titlecase */
+ 3, 0x3a5, 0x313, 0x342, /* 0x1f56 upcase */
+ 1, 0x1f56, /* 0x1f56 downcase */
+ 3, 0x3a5, 0x313, 0x342, /* 0x1f56 titlecase */
+ 2, 0x1f08, 0x399, /* 0x1f80 upcase */
+ 1, 0x1f80, /* 0x1f80 downcase */
+ 1, 0x1f88, /* 0x1f80 titlecase */
+ 2, 0x1f09, 0x399, /* 0x1f81 upcase */
+ 1, 0x1f81, /* 0x1f81 downcase */
+ 1, 0x1f89, /* 0x1f81 titlecase */
+ 2, 0x1f0a, 0x399, /* 0x1f82 upcase */
+ 1, 0x1f82, /* 0x1f82 downcase */
+ 1, 0x1f8a, /* 0x1f82 titlecase */
+ 2, 0x1f0b, 0x399, /* 0x1f83 upcase */
+ 1, 0x1f83, /* 0x1f83 downcase */
+ 1, 0x1f8b, /* 0x1f83 titlecase */
+ 2, 0x1f0c, 0x399, /* 0x1f84 upcase */
+ 1, 0x1f84, /* 0x1f84 downcase */
+ 1, 0x1f8c, /* 0x1f84 titlecase */
+ 2, 0x1f0d, 0x399, /* 0x1f85 upcase */
+ 1, 0x1f85, /* 0x1f85 downcase */
+ 1, 0x1f8d, /* 0x1f85 titlecase */
+ 2, 0x1f0e, 0x399, /* 0x1f86 upcase */
+ 1, 0x1f86, /* 0x1f86 downcase */
+ 1, 0x1f8e, /* 0x1f86 titlecase */
+ 2, 0x1f0f, 0x399, /* 0x1f87 upcase */
+ 1, 0x1f87, /* 0x1f87 downcase */
+ 1, 0x1f8f, /* 0x1f87 titlecase */
+ 2, 0x1f08, 0x399, /* 0x1f88 upcase */
+ 1, 0x1f80, /* 0x1f88 downcase */
+ 1, 0x1f88, /* 0x1f88 titlecase */
+ 2, 0x1f09, 0x399, /* 0x1f89 upcase */
+ 1, 0x1f81, /* 0x1f89 downcase */
+ 1, 0x1f89, /* 0x1f89 titlecase */
+ 2, 0x1f0a, 0x399, /* 0x1f8a upcase */
+ 1, 0x1f82, /* 0x1f8a downcase */
+ 1, 0x1f8a, /* 0x1f8a titlecase */
+ 2, 0x1f0b, 0x399, /* 0x1f8b upcase */
+ 1, 0x1f83, /* 0x1f8b downcase */
+ 1, 0x1f8b, /* 0x1f8b titlecase */
+ 2, 0x1f0c, 0x399, /* 0x1f8c upcase */
+ 1, 0x1f84, /* 0x1f8c downcase */
+ 1, 0x1f8c, /* 0x1f8c titlecase */
+ 2, 0x1f0d, 0x399, /* 0x1f8d upcase */
+ 1, 0x1f85, /* 0x1f8d downcase */
+ 1, 0x1f8d, /* 0x1f8d titlecase */
+ 2, 0x1f0e, 0x399, /* 0x1f8e upcase */
+ 1, 0x1f86, /* 0x1f8e downcase */
+ 1, 0x1f8e, /* 0x1f8e titlecase */
+ 2, 0x1f0f, 0x399, /* 0x1f8f upcase */
+ 1, 0x1f87, /* 0x1f8f downcase */
+ 1, 0x1f8f, /* 0x1f8f titlecase */
+ 2, 0x1f28, 0x399, /* 0x1f90 upcase */
+ 1, 0x1f90, /* 0x1f90 downcase */
+ 1, 0x1f98, /* 0x1f90 titlecase */
+ 2, 0x1f29, 0x399, /* 0x1f91 upcase */
+ 1, 0x1f91, /* 0x1f91 downcase */
+ 1, 0x1f99, /* 0x1f91 titlecase */
+ 2, 0x1f2a, 0x399, /* 0x1f92 upcase */
+ 1, 0x1f92, /* 0x1f92 downcase */
+ 1, 0x1f9a, /* 0x1f92 titlecase */
+ 2, 0x1f2b, 0x399, /* 0x1f93 upcase */
+ 1, 0x1f93, /* 0x1f93 downcase */
+ 1, 0x1f9b, /* 0x1f93 titlecase */
+ 2, 0x1f2c, 0x399, /* 0x1f94 upcase */
+ 1, 0x1f94, /* 0x1f94 downcase */
+ 1, 0x1f9c, /* 0x1f94 titlecase */
+ 2, 0x1f2d, 0x399, /* 0x1f95 upcase */
+ 1, 0x1f95, /* 0x1f95 downcase */
+ 1, 0x1f9d, /* 0x1f95 titlecase */
+ 2, 0x1f2e, 0x399, /* 0x1f96 upcase */
+ 1, 0x1f96, /* 0x1f96 downcase */
+ 1, 0x1f9e, /* 0x1f96 titlecase */
+ 2, 0x1f2f, 0x399, /* 0x1f97 upcase */
+ 1, 0x1f97, /* 0x1f97 downcase */
+ 1, 0x1f9f, /* 0x1f97 titlecase */
+ 2, 0x1f28, 0x399, /* 0x1f98 upcase */
+ 1, 0x1f90, /* 0x1f98 downcase */
+ 1, 0x1f98, /* 0x1f98 titlecase */
+ 2, 0x1f29, 0x399, /* 0x1f99 upcase */
+ 1, 0x1f91, /* 0x1f99 downcase */
+ 1, 0x1f99, /* 0x1f99 titlecase */
+ 2, 0x1f2a, 0x399, /* 0x1f9a upcase */
+ 1, 0x1f92, /* 0x1f9a downcase */
+ 1, 0x1f9a, /* 0x1f9a titlecase */
+ 2, 0x1f2b, 0x399, /* 0x1f9b upcase */
+ 1, 0x1f93, /* 0x1f9b downcase */
+ 1, 0x1f9b, /* 0x1f9b titlecase */
+ 2, 0x1f2c, 0x399, /* 0x1f9c upcase */
+ 1, 0x1f94, /* 0x1f9c downcase */
+ 1, 0x1f9c, /* 0x1f9c titlecase */
+ 2, 0x1f2d, 0x399, /* 0x1f9d upcase */
+ 1, 0x1f95, /* 0x1f9d downcase */
+ 1, 0x1f9d, /* 0x1f9d titlecase */
+ 2, 0x1f2e, 0x399, /* 0x1f9e upcase */
+ 1, 0x1f96, /* 0x1f9e downcase */
+ 1, 0x1f9e, /* 0x1f9e titlecase */
+ 2, 0x1f2f, 0x399, /* 0x1f9f upcase */
+ 1, 0x1f97, /* 0x1f9f downcase */
+ 1, 0x1f9f, /* 0x1f9f titlecase */
+ 2, 0x1f68, 0x399, /* 0x1fa0 upcase */
+ 1, 0x1fa0, /* 0x1fa0 downcase */
+ 1, 0x1fa8, /* 0x1fa0 titlecase */
+ 2, 0x1f69, 0x399, /* 0x1fa1 upcase */
+ 1, 0x1fa1, /* 0x1fa1 downcase */
+ 1, 0x1fa9, /* 0x1fa1 titlecase */
+ 2, 0x1f6a, 0x399, /* 0x1fa2 upcase */
+ 1, 0x1fa2, /* 0x1fa2 downcase */
+ 1, 0x1faa, /* 0x1fa2 titlecase */
+ 2, 0x1f6b, 0x399, /* 0x1fa3 upcase */
+ 1, 0x1fa3, /* 0x1fa3 downcase */
+ 1, 0x1fab, /* 0x1fa3 titlecase */
+ 2, 0x1f6c, 0x399, /* 0x1fa4 upcase */
+ 1, 0x1fa4, /* 0x1fa4 downcase */
+ 1, 0x1fac, /* 0x1fa4 titlecase */
+ 2, 0x1f6d, 0x399, /* 0x1fa5 upcase */
+ 1, 0x1fa5, /* 0x1fa5 downcase */
+ 1, 0x1fad, /* 0x1fa5 titlecase */
+ 2, 0x1f6e, 0x399, /* 0x1fa6 upcase */
+ 1, 0x1fa6, /* 0x1fa6 downcase */
+ 1, 0x1fae, /* 0x1fa6 titlecase */
+ 2, 0x1f6f, 0x399, /* 0x1fa7 upcase */
+ 1, 0x1fa7, /* 0x1fa7 downcase */
+ 1, 0x1faf, /* 0x1fa7 titlecase */
+ 2, 0x1f68, 0x399, /* 0x1fa8 upcase */
+ 1, 0x1fa0, /* 0x1fa8 downcase */
+ 1, 0x1fa8, /* 0x1fa8 titlecase */
+ 2, 0x1f69, 0x399, /* 0x1fa9 upcase */
+ 1, 0x1fa1, /* 0x1fa9 downcase */
+ 1, 0x1fa9, /* 0x1fa9 titlecase */
+ 2, 0x1f6a, 0x399, /* 0x1faa upcase */
+ 1, 0x1fa2, /* 0x1faa downcase */
+ 1, 0x1faa, /* 0x1faa titlecase */
+ 2, 0x1f6b, 0x399, /* 0x1fab upcase */
+ 1, 0x1fa3, /* 0x1fab downcase */
+ 1, 0x1fab, /* 0x1fab titlecase */
+ 2, 0x1f6c, 0x399, /* 0x1fac upcase */
+ 1, 0x1fa4, /* 0x1fac downcase */
+ 1, 0x1fac, /* 0x1fac titlecase */
+ 2, 0x1f6d, 0x399, /* 0x1fad upcase */
+ 1, 0x1fa5, /* 0x1fad downcase */
+ 1, 0x1fad, /* 0x1fad titlecase */
+ 2, 0x1f6e, 0x399, /* 0x1fae upcase */
+ 1, 0x1fa6, /* 0x1fae downcase */
+ 1, 0x1fae, /* 0x1fae titlecase */
+ 2, 0x1f6f, 0x399, /* 0x1faf upcase */
+ 1, 0x1fa7, /* 0x1faf downcase */
+ 1, 0x1faf, /* 0x1faf titlecase */
+ 2, 0x1fba, 0x399, /* 0x1fb2 upcase */
+ 1, 0x1fb2, /* 0x1fb2 downcase */
+ 2, 0x1fba, 0x345, /* 0x1fb2 titlecase */
+ 2, 0x391, 0x399, /* 0x1fb3 upcase */
+ 1, 0x1fb3, /* 0x1fb3 downcase */
+ 1, 0x1fbc, /* 0x1fb3 titlecase */
+ 2, 0x386, 0x399, /* 0x1fb4 upcase */
+ 1, 0x1fb4, /* 0x1fb4 downcase */
+ 2, 0x386, 0x345, /* 0x1fb4 titlecase */
+ 2, 0x391, 0x342, /* 0x1fb6 upcase */
+ 1, 0x1fb6, /* 0x1fb6 downcase */
+ 2, 0x391, 0x342, /* 0x1fb6 titlecase */
+ 3, 0x391, 0x342, 0x399, /* 0x1fb7 upcase */
+ 1, 0x1fb7, /* 0x1fb7 downcase */
+ 3, 0x391, 0x342, 0x345, /* 0x1fb7 titlecase */
+ 2, 0x391, 0x399, /* 0x1fbc upcase */
+ 1, 0x1fb3, /* 0x1fbc downcase */
+ 1, 0x1fbc, /* 0x1fbc titlecase */
+ 2, 0x1fca, 0x399, /* 0x1fc2 upcase */
+ 1, 0x1fc2, /* 0x1fc2 downcase */
+ 2, 0x1fca, 0x345, /* 0x1fc2 titlecase */
+ 2, 0x397, 0x399, /* 0x1fc3 upcase */
+ 1, 0x1fc3, /* 0x1fc3 downcase */
+ 1, 0x1fcc, /* 0x1fc3 titlecase */
+ 2, 0x389, 0x399, /* 0x1fc4 upcase */
+ 1, 0x1fc4, /* 0x1fc4 downcase */
+ 2, 0x389, 0x345, /* 0x1fc4 titlecase */
+ 2, 0x397, 0x342, /* 0x1fc6 upcase */
+ 1, 0x1fc6, /* 0x1fc6 downcase */
+ 2, 0x397, 0x342, /* 0x1fc6 titlecase */
+ 3, 0x397, 0x342, 0x399, /* 0x1fc7 upcase */
+ 1, 0x1fc7, /* 0x1fc7 downcase */
+ 3, 0x397, 0x342, 0x345, /* 0x1fc7 titlecase */
+ 2, 0x397, 0x399, /* 0x1fcc upcase */
+ 1, 0x1fc3, /* 0x1fcc downcase */
+ 1, 0x1fcc, /* 0x1fcc titlecase */
+ 3, 0x399, 0x308, 0x300, /* 0x1fd2 upcase */
+ 1, 0x1fd2, /* 0x1fd2 downcase */
+ 3, 0x399, 0x308, 0x300, /* 0x1fd2 titlecase */
+ 3, 0x399, 0x308, 0x301, /* 0x1fd3 upcase */
+ 1, 0x1fd3, /* 0x1fd3 downcase */
+ 3, 0x399, 0x308, 0x301, /* 0x1fd3 titlecase */
+ 2, 0x399, 0x342, /* 0x1fd6 upcase */
+ 1, 0x1fd6, /* 0x1fd6 downcase */
+ 2, 0x399, 0x342, /* 0x1fd6 titlecase */
+ 3, 0x399, 0x308, 0x342, /* 0x1fd7 upcase */
+ 1, 0x1fd7, /* 0x1fd7 downcase */
+ 3, 0x399, 0x308, 0x342, /* 0x1fd7 titlecase */
+ 3, 0x3a5, 0x308, 0x300, /* 0x1fe2 upcase */
+ 1, 0x1fe2, /* 0x1fe2 downcase */
+ 3, 0x3a5, 0x308, 0x300, /* 0x1fe2 titlecase */
+ 3, 0x3a5, 0x308, 0x301, /* 0x1fe3 upcase */
+ 1, 0x1fe3, /* 0x1fe3 downcase */
+ 3, 0x3a5, 0x308, 0x301, /* 0x1fe3 titlecase */
+ 2, 0x3a1, 0x313, /* 0x1fe4 upcase */
+ 1, 0x1fe4, /* 0x1fe4 downcase */
+ 2, 0x3a1, 0x313, /* 0x1fe4 titlecase */
+ 2, 0x3a5, 0x342, /* 0x1fe6 upcase */
+ 1, 0x1fe6, /* 0x1fe6 downcase */
+ 2, 0x3a5, 0x342, /* 0x1fe6 titlecase */
+ 3, 0x3a5, 0x308, 0x342, /* 0x1fe7 upcase */
+ 1, 0x1fe7, /* 0x1fe7 downcase */
+ 3, 0x3a5, 0x308, 0x342, /* 0x1fe7 titlecase */
+ 2, 0x1ffa, 0x399, /* 0x1ff2 upcase */
+ 1, 0x1ff2, /* 0x1ff2 downcase */
+ 2, 0x1ffa, 0x345, /* 0x1ff2 titlecase */
+ 2, 0x3a9, 0x399, /* 0x1ff3 upcase */
+ 1, 0x1ff3, /* 0x1ff3 downcase */
+ 1, 0x1ffc, /* 0x1ff3 titlecase */
+ 2, 0x38f, 0x399, /* 0x1ff4 upcase */
+ 1, 0x1ff4, /* 0x1ff4 downcase */
+ 2, 0x38f, 0x345, /* 0x1ff4 titlecase */
+ 2, 0x3a9, 0x342, /* 0x1ff6 upcase */
+ 1, 0x1ff6, /* 0x1ff6 downcase */
+ 2, 0x3a9, 0x342, /* 0x1ff6 titlecase */
+ 3, 0x3a9, 0x342, 0x399, /* 0x1ff7 upcase */
+ 1, 0x1ff7, /* 0x1ff7 downcase */
+ 3, 0x3a9, 0x342, 0x345, /* 0x1ff7 titlecase */
+ 2, 0x3a9, 0x399, /* 0x1ffc upcase */
+ 1, 0x1ff3, /* 0x1ffc downcase */
+ 1, 0x1ffc, /* 0x1ffc titlecase */
+ 2, 0x46, 0x46, /* 0xfb00 upcase */
+ 1, 0xfb00, /* 0xfb00 downcase */
+ 2, 0x46, 0x66, /* 0xfb00 titlecase */
+ 2, 0x46, 0x49, /* 0xfb01 upcase */
+ 1, 0xfb01, /* 0xfb01 downcase */
+ 2, 0x46, 0x69, /* 0xfb01 titlecase */
+ 2, 0x46, 0x4c, /* 0xfb02 upcase */
+ 1, 0xfb02, /* 0xfb02 downcase */
+ 2, 0x46, 0x6c, /* 0xfb02 titlecase */
+ 3, 0x46, 0x46, 0x49, /* 0xfb03 upcase */
+ 1, 0xfb03, /* 0xfb03 downcase */
+ 3, 0x46, 0x66, 0x69, /* 0xfb03 titlecase */
+ 3, 0x46, 0x46, 0x4c, /* 0xfb04 upcase */
+ 1, 0xfb04, /* 0xfb04 downcase */
+ 3, 0x46, 0x66, 0x6c, /* 0xfb04 titlecase */
+ 2, 0x53, 0x54, /* 0xfb05 upcase */
+ 1, 0xfb05, /* 0xfb05 downcase */
+ 2, 0x53, 0x74, /* 0xfb05 titlecase */
+ 2, 0x53, 0x54, /* 0xfb06 upcase */
+ 1, 0xfb06, /* 0xfb06 downcase */
+ 2, 0x53, 0x74, /* 0xfb06 titlecase */
+ 2, 0x544, 0x546, /* 0xfb13 upcase */
+ 1, 0xfb13, /* 0xfb13 downcase */
+ 2, 0x544, 0x576, /* 0xfb13 titlecase */
+ 2, 0x544, 0x535, /* 0xfb14 upcase */
+ 1, 0xfb14, /* 0xfb14 downcase */
+ 2, 0x544, 0x565, /* 0xfb14 titlecase */
+ 2, 0x544, 0x53b, /* 0xfb15 upcase */
+ 1, 0xfb15, /* 0xfb15 downcase */
+ 2, 0x544, 0x56b, /* 0xfb15 titlecase */
+ 2, 0x54e, 0x546, /* 0xfb16 upcase */
+ 1, 0xfb16, /* 0xfb16 downcase */
+ 2, 0x54e, 0x576, /* 0xfb16 titlecase */
+ 2, 0x544, 0x53d, /* 0xfb17 upcase */
+ 1, 0xfb17, /* 0xfb17 downcase */
+ 2, 0x544, 0x56d, /* 0xfb17 titlecase */
+};
+
+gli_case_special_t unigen_special_0xdf = { 0, 3, 5 };
+gli_case_special_t unigen_special_0x130 = { 8, 10, 13 };
+gli_case_special_t unigen_special_0x149 = { 15, 18, 20 };
+gli_case_special_t unigen_special_0x1c4 = { 23, 25, 27 };
+gli_case_special_t unigen_special_0x1c5 = { 29, 31, 33 };
+gli_case_special_t unigen_special_0x1c6 = { 35, 37, 39 };
+gli_case_special_t unigen_special_0x1c7 = { 41, 43, 45 };
+gli_case_special_t unigen_special_0x1c8 = { 47, 49, 51 };
+gli_case_special_t unigen_special_0x1c9 = { 53, 55, 57 };
+gli_case_special_t unigen_special_0x1ca = { 59, 61, 63 };
+gli_case_special_t unigen_special_0x1cb = { 65, 67, 69 };
+gli_case_special_t unigen_special_0x1cc = { 71, 73, 75 };
+gli_case_special_t unigen_special_0x1f0 = { 77, 80, 82 };
+gli_case_special_t unigen_special_0x1f1 = { 85, 87, 89 };
+gli_case_special_t unigen_special_0x1f2 = { 91, 93, 95 };
+gli_case_special_t unigen_special_0x1f3 = { 97, 99, 101 };
+gli_case_special_t unigen_special_0x390 = { 103, 107, 109 };
+gli_case_special_t unigen_special_0x3b0 = { 113, 117, 119 };
+gli_case_special_t unigen_special_0x587 = { 123, 126, 128 };
+gli_case_special_t unigen_special_0x1e96 = { 131, 134, 136 };
+gli_case_special_t unigen_special_0x1e97 = { 139, 142, 144 };
+gli_case_special_t unigen_special_0x1e98 = { 147, 150, 152 };
+gli_case_special_t unigen_special_0x1e99 = { 155, 158, 160 };
+gli_case_special_t unigen_special_0x1e9a = { 163, 166, 168 };
+gli_case_special_t unigen_special_0x1f50 = { 171, 174, 176 };
+gli_case_special_t unigen_special_0x1f52 = { 179, 183, 185 };
+gli_case_special_t unigen_special_0x1f54 = { 189, 193, 195 };
+gli_case_special_t unigen_special_0x1f56 = { 199, 203, 205 };
+gli_case_special_t unigen_special_0x1f80 = { 209, 212, 214 };
+gli_case_special_t unigen_special_0x1f81 = { 216, 219, 221 };
+gli_case_special_t unigen_special_0x1f82 = { 223, 226, 228 };
+gli_case_special_t unigen_special_0x1f83 = { 230, 233, 235 };
+gli_case_special_t unigen_special_0x1f84 = { 237, 240, 242 };
+gli_case_special_t unigen_special_0x1f85 = { 244, 247, 249 };
+gli_case_special_t unigen_special_0x1f86 = { 251, 254, 256 };
+gli_case_special_t unigen_special_0x1f87 = { 258, 261, 263 };
+gli_case_special_t unigen_special_0x1f88 = { 265, 268, 270 };
+gli_case_special_t unigen_special_0x1f89 = { 272, 275, 277 };
+gli_case_special_t unigen_special_0x1f8a = { 279, 282, 284 };
+gli_case_special_t unigen_special_0x1f8b = { 286, 289, 291 };
+gli_case_special_t unigen_special_0x1f8c = { 293, 296, 298 };
+gli_case_special_t unigen_special_0x1f8d = { 300, 303, 305 };
+gli_case_special_t unigen_special_0x1f8e = { 307, 310, 312 };
+gli_case_special_t unigen_special_0x1f8f = { 314, 317, 319 };
+gli_case_special_t unigen_special_0x1f90 = { 321, 324, 326 };
+gli_case_special_t unigen_special_0x1f91 = { 328, 331, 333 };
+gli_case_special_t unigen_special_0x1f92 = { 335, 338, 340 };
+gli_case_special_t unigen_special_0x1f93 = { 342, 345, 347 };
+gli_case_special_t unigen_special_0x1f94 = { 349, 352, 354 };
+gli_case_special_t unigen_special_0x1f95 = { 356, 359, 361 };
+gli_case_special_t unigen_special_0x1f96 = { 363, 366, 368 };
+gli_case_special_t unigen_special_0x1f97 = { 370, 373, 375 };
+gli_case_special_t unigen_special_0x1f98 = { 377, 380, 382 };
+gli_case_special_t unigen_special_0x1f99 = { 384, 387, 389 };
+gli_case_special_t unigen_special_0x1f9a = { 391, 394, 396 };
+gli_case_special_t unigen_special_0x1f9b = { 398, 401, 403 };
+gli_case_special_t unigen_special_0x1f9c = { 405, 408, 410 };
+gli_case_special_t unigen_special_0x1f9d = { 412, 415, 417 };
+gli_case_special_t unigen_special_0x1f9e = { 419, 422, 424 };
+gli_case_special_t unigen_special_0x1f9f = { 426, 429, 431 };
+gli_case_special_t unigen_special_0x1fa0 = { 433, 436, 438 };
+gli_case_special_t unigen_special_0x1fa1 = { 440, 443, 445 };
+gli_case_special_t unigen_special_0x1fa2 = { 447, 450, 452 };
+gli_case_special_t unigen_special_0x1fa3 = { 454, 457, 459 };
+gli_case_special_t unigen_special_0x1fa4 = { 461, 464, 466 };
+gli_case_special_t unigen_special_0x1fa5 = { 468, 471, 473 };
+gli_case_special_t unigen_special_0x1fa6 = { 475, 478, 480 };
+gli_case_special_t unigen_special_0x1fa7 = { 482, 485, 487 };
+gli_case_special_t unigen_special_0x1fa8 = { 489, 492, 494 };
+gli_case_special_t unigen_special_0x1fa9 = { 496, 499, 501 };
+gli_case_special_t unigen_special_0x1faa = { 503, 506, 508 };
+gli_case_special_t unigen_special_0x1fab = { 510, 513, 515 };
+gli_case_special_t unigen_special_0x1fac = { 517, 520, 522 };
+gli_case_special_t unigen_special_0x1fad = { 524, 527, 529 };
+gli_case_special_t unigen_special_0x1fae = { 531, 534, 536 };
+gli_case_special_t unigen_special_0x1faf = { 538, 541, 543 };
+gli_case_special_t unigen_special_0x1fb2 = { 545, 548, 550 };
+gli_case_special_t unigen_special_0x1fb3 = { 553, 556, 558 };
+gli_case_special_t unigen_special_0x1fb4 = { 560, 563, 565 };
+gli_case_special_t unigen_special_0x1fb6 = { 568, 571, 573 };
+gli_case_special_t unigen_special_0x1fb7 = { 576, 580, 582 };
+gli_case_special_t unigen_special_0x1fbc = { 586, 589, 591 };
+gli_case_special_t unigen_special_0x1fc2 = { 593, 596, 598 };
+gli_case_special_t unigen_special_0x1fc3 = { 601, 604, 606 };
+gli_case_special_t unigen_special_0x1fc4 = { 608, 611, 613 };
+gli_case_special_t unigen_special_0x1fc6 = { 616, 619, 621 };
+gli_case_special_t unigen_special_0x1fc7 = { 624, 628, 630 };
+gli_case_special_t unigen_special_0x1fcc = { 634, 637, 639 };
+gli_case_special_t unigen_special_0x1fd2 = { 641, 645, 647 };
+gli_case_special_t unigen_special_0x1fd3 = { 651, 655, 657 };
+gli_case_special_t unigen_special_0x1fd6 = { 661, 664, 666 };
+gli_case_special_t unigen_special_0x1fd7 = { 669, 673, 675 };
+gli_case_special_t unigen_special_0x1fe2 = { 679, 683, 685 };
+gli_case_special_t unigen_special_0x1fe3 = { 689, 693, 695 };
+gli_case_special_t unigen_special_0x1fe4 = { 699, 702, 704 };
+gli_case_special_t unigen_special_0x1fe6 = { 707, 710, 712 };
+gli_case_special_t unigen_special_0x1fe7 = { 715, 719, 721 };
+gli_case_special_t unigen_special_0x1ff2 = { 725, 728, 730 };
+gli_case_special_t unigen_special_0x1ff3 = { 733, 736, 738 };
+gli_case_special_t unigen_special_0x1ff4 = { 740, 743, 745 };
+gli_case_special_t unigen_special_0x1ff6 = { 748, 751, 753 };
+gli_case_special_t unigen_special_0x1ff7 = { 756, 760, 762 };
+gli_case_special_t unigen_special_0x1ffc = { 766, 769, 771 };
+gli_case_special_t unigen_special_0xfb00 = { 773, 776, 778 };
+gli_case_special_t unigen_special_0xfb01 = { 781, 784, 786 };
+gli_case_special_t unigen_special_0xfb02 = { 789, 792, 794 };
+gli_case_special_t unigen_special_0xfb03 = { 797, 801, 803 };
+gli_case_special_t unigen_special_0xfb04 = { 807, 811, 813 };
+gli_case_special_t unigen_special_0xfb05 = { 817, 820, 822 };
+gli_case_special_t unigen_special_0xfb06 = { 825, 828, 830 };
+gli_case_special_t unigen_special_0xfb13 = { 833, 836, 838 };
+gli_case_special_t unigen_special_0xfb14 = { 841, 844, 846 };
+gli_case_special_t unigen_special_0xfb15 = { 849, 852, 854 };
+gli_case_special_t unigen_special_0xfb16 = { 857, 860, 862 };
+gli_case_special_t unigen_special_0xfb17 = { 865, 868, 870 };
+
+#define RETURN_COMBINING_CLASS(ch) \
+ switch ((glui32)(ch) >> 8) { \
+ case 3: \
+ switch (ch) { \
+ case 820: \
+ case 821: \
+ case 822: \
+ case 823: \
+ case 824: \
+ return 1; \
+ case 801: \
+ case 802: \
+ case 807: \
+ case 808: \
+ return 202; \
+ case 795: \
+ return 216; \
+ case 790: \
+ case 791: \
+ case 792: \
+ case 793: \
+ case 796: \
+ case 797: \
+ case 798: \
+ case 799: \
+ case 800: \
+ case 803: \
+ case 804: \
+ case 805: \
+ case 806: \
+ case 809: \
+ case 810: \
+ case 811: \
+ case 812: \
+ case 813: \
+ case 814: \
+ case 815: \
+ case 816: \
+ case 817: \
+ case 818: \
+ case 819: \
+ case 825: \
+ case 826: \
+ case 827: \
+ case 828: \
+ case 839: \
+ case 840: \
+ case 841: \
+ case 845: \
+ case 846: \
+ case 851: \
+ case 852: \
+ case 853: \
+ case 854: \
+ return 220; \
+ case 768: \
+ case 769: \
+ case 770: \
+ case 771: \
+ case 772: \
+ case 773: \
+ case 774: \
+ case 775: \
+ case 776: \
+ case 777: \
+ case 778: \
+ case 779: \
+ case 780: \
+ case 781: \
+ case 782: \
+ case 783: \
+ case 784: \
+ case 785: \
+ case 786: \
+ case 787: \
+ case 788: \
+ case 829: \
+ case 830: \
+ case 831: \
+ case 832: \
+ case 833: \
+ case 834: \
+ case 835: \
+ case 836: \
+ case 838: \
+ case 842: \
+ case 843: \
+ case 844: \
+ case 848: \
+ case 849: \
+ case 850: \
+ case 855: \
+ case 867: \
+ case 868: \
+ case 869: \
+ case 870: \
+ case 871: \
+ case 872: \
+ case 873: \
+ case 874: \
+ case 875: \
+ case 876: \
+ case 877: \
+ case 878: \
+ case 879: \
+ return 230; \
+ case 789: \
+ case 794: \
+ return 232; \
+ case 863: \
+ case 866: \
+ return 233; \
+ case 861: \
+ case 862: \
+ case 864: \
+ case 865: \
+ return 234; \
+ case 837: \
+ return 240; \
+ } \
+ return 0; \
+ case 4: \
+ switch (ch) { \
+ case 1155: \
+ case 1156: \
+ case 1157: \
+ case 1158: \
+ return 230; \
+ } \
+ return 0; \
+ case 5: \
+ switch (ch) { \
+ case 1456: \
+ return 10; \
+ case 1457: \
+ return 11; \
+ case 1458: \
+ return 12; \
+ case 1459: \
+ return 13; \
+ case 1460: \
+ return 14; \
+ case 1461: \
+ return 15; \
+ case 1462: \
+ return 16; \
+ case 1463: \
+ return 17; \
+ case 1464: \
+ return 18; \
+ case 1465: \
+ return 19; \
+ case 1467: \
+ return 20; \
+ case 1468: \
+ return 21; \
+ case 1469: \
+ return 22; \
+ case 1471: \
+ return 23; \
+ case 1473: \
+ return 24; \
+ case 1474: \
+ return 25; \
+ case 1425: \
+ case 1430: \
+ case 1435: \
+ case 1443: \
+ case 1444: \
+ case 1445: \
+ case 1446: \
+ case 1447: \
+ case 1450: \
+ return 220; \
+ case 1434: \
+ case 1453: \
+ return 222; \
+ case 1454: \
+ return 228; \
+ case 1426: \
+ case 1427: \
+ case 1428: \
+ case 1429: \
+ case 1431: \
+ case 1432: \
+ case 1433: \
+ case 1436: \
+ case 1437: \
+ case 1438: \
+ case 1439: \
+ case 1440: \
+ case 1441: \
+ case 1448: \
+ case 1449: \
+ case 1451: \
+ case 1452: \
+ case 1455: \
+ case 1476: \
+ return 230; \
+ } \
+ return 0; \
+ case 6: \
+ switch (ch) { \
+ case 1611: \
+ return 27; \
+ case 1612: \
+ return 28; \
+ case 1613: \
+ return 29; \
+ case 1614: \
+ return 30; \
+ case 1615: \
+ return 31; \
+ case 1616: \
+ return 32; \
+ case 1617: \
+ return 33; \
+ case 1618: \
+ return 34; \
+ case 1648: \
+ return 35; \
+ case 1621: \
+ case 1622: \
+ case 1763: \
+ case 1770: \
+ case 1773: \
+ return 220; \
+ case 1552: \
+ case 1553: \
+ case 1554: \
+ case 1555: \
+ case 1556: \
+ case 1557: \
+ case 1619: \
+ case 1620: \
+ case 1623: \
+ case 1624: \
+ case 1750: \
+ case 1751: \
+ case 1752: \
+ case 1753: \
+ case 1754: \
+ case 1755: \
+ case 1756: \
+ case 1759: \
+ case 1760: \
+ case 1761: \
+ case 1762: \
+ case 1764: \
+ case 1767: \
+ case 1768: \
+ case 1771: \
+ case 1772: \
+ return 230; \
+ } \
+ return 0; \
+ case 7: \
+ switch (ch) { \
+ case 1809: \
+ return 36; \
+ case 1841: \
+ case 1844: \
+ case 1847: \
+ case 1848: \
+ case 1849: \
+ case 1851: \
+ case 1852: \
+ case 1854: \
+ case 1858: \
+ case 1860: \
+ case 1862: \
+ case 1864: \
+ return 220; \
+ case 1840: \
+ case 1842: \
+ case 1843: \
+ case 1845: \
+ case 1846: \
+ case 1850: \
+ case 1853: \
+ case 1855: \
+ case 1856: \
+ case 1857: \
+ case 1859: \
+ case 1861: \
+ case 1863: \
+ case 1865: \
+ case 1866: \
+ return 230; \
+ } \
+ return 0; \
+ case 9: \
+ switch (ch) { \
+ case 2364: \
+ case 2492: \
+ return 7; \
+ case 2381: \
+ case 2509: \
+ return 9; \
+ case 2386: \
+ return 220; \
+ case 2385: \
+ case 2387: \
+ case 2388: \
+ return 230; \
+ } \
+ return 0; \
+ case 10: \
+ switch (ch) { \
+ case 2620: \
+ case 2748: \
+ return 7; \
+ case 2637: \
+ case 2765: \
+ return 9; \
+ } \
+ return 0; \
+ case 11: \
+ switch (ch) { \
+ case 2876: \
+ return 7; \
+ case 2893: \
+ case 3021: \
+ return 9; \
+ } \
+ return 0; \
+ case 12: \
+ switch (ch) { \
+ case 3260: \
+ return 7; \
+ case 3149: \
+ case 3277: \
+ return 9; \
+ case 3157: \
+ return 84; \
+ case 3158: \
+ return 91; \
+ } \
+ return 0; \
+ case 13: \
+ switch (ch) { \
+ case 3405: \
+ case 3530: \
+ return 9; \
+ } \
+ return 0; \
+ case 14: \
+ switch (ch) { \
+ case 3642: \
+ return 9; \
+ case 3640: \
+ case 3641: \
+ return 103; \
+ case 3656: \
+ case 3657: \
+ case 3658: \
+ case 3659: \
+ return 107; \
+ case 3768: \
+ case 3769: \
+ return 118; \
+ case 3784: \
+ case 3785: \
+ case 3786: \
+ case 3787: \
+ return 122; \
+ } \
+ return 0; \
+ case 15: \
+ switch (ch) { \
+ case 3972: \
+ return 9; \
+ case 3953: \
+ return 129; \
+ case 3954: \
+ case 3962: \
+ case 3963: \
+ case 3964: \
+ case 3965: \
+ case 3968: \
+ return 130; \
+ case 3956: \
+ return 132; \
+ case 3897: \
+ return 216; \
+ case 3864: \
+ case 3865: \
+ case 3893: \
+ case 3895: \
+ case 4038: \
+ return 220; \
+ case 3970: \
+ case 3971: \
+ case 3974: \
+ case 3975: \
+ return 230; \
+ } \
+ return 0; \
+ case 16: \
+ switch (ch) { \
+ case 4151: \
+ return 7; \
+ case 4153: \
+ return 9; \
+ } \
+ return 0; \
+ case 23: \
+ switch (ch) { \
+ case 5908: \
+ case 5940: \
+ case 6098: \
+ return 9; \
+ case 6109: \
+ return 230; \
+ } \
+ return 0; \
+ case 24: \
+ switch (ch) { \
+ case 6313: \
+ return 228; \
+ } \
+ return 0; \
+ case 25: \
+ switch (ch) { \
+ case 6459: \
+ return 220; \
+ case 6457: \
+ return 222; \
+ case 6458: \
+ return 230; \
+ } \
+ return 0; \
+ case 32: \
+ switch (ch) { \
+ case 8402: \
+ case 8403: \
+ case 8408: \
+ case 8409: \
+ case 8410: \
+ case 8421: \
+ case 8422: \
+ case 8426: \
+ return 1; \
+ case 8424: \
+ return 220; \
+ case 8400: \
+ case 8401: \
+ case 8404: \
+ case 8405: \
+ case 8406: \
+ case 8407: \
+ case 8411: \
+ case 8412: \
+ case 8417: \
+ case 8423: \
+ case 8425: \
+ return 230; \
+ } \
+ return 0; \
+ case 48: \
+ switch (ch) { \
+ case 12441: \
+ case 12442: \
+ return 8; \
+ case 12330: \
+ return 218; \
+ case 12333: \
+ return 222; \
+ case 12334: \
+ case 12335: \
+ return 224; \
+ case 12331: \
+ return 228; \
+ case 12332: \
+ return 232; \
+ } \
+ return 0; \
+ case 251: \
+ switch (ch) { \
+ case 64286: \
+ return 26; \
+ } \
+ return 0; \
+ case 254: \
+ switch (ch) { \
+ case 65056: \
+ case 65057: \
+ case 65058: \
+ case 65059: \
+ return 230; \
+ } \
+ return 0; \
+ case 465: \
+ switch (ch) { \
+ case 119143: \
+ case 119144: \
+ case 119145: \
+ return 1; \
+ case 119141: \
+ case 119142: \
+ case 119150: \
+ case 119151: \
+ case 119152: \
+ case 119153: \
+ case 119154: \
+ return 216; \
+ case 119163: \
+ case 119164: \
+ case 119165: \
+ case 119166: \
+ case 119167: \
+ case 119168: \
+ case 119169: \
+ case 119170: \
+ case 119178: \
+ case 119179: \
+ return 220; \
+ case 119149: \
+ return 226; \
+ case 119173: \
+ case 119174: \
+ case 119175: \
+ case 119176: \
+ case 119177: \
+ case 119210: \
+ case 119211: \
+ case 119212: \
+ case 119213: \
+ return 230; \
+ } \
+ return 0; \
+ } \
+ return 0;
+
+#define RETURN_COMPOSITION(ch1, ch2) \
+ switch ((glui32)(ch1) >> 8) { \
+ case 0: \
+ switch (ch1) { \
+ case 60: \
+ switch (ch2) { \
+ case 824: return 8814; \
+ } \
+ return 0; \
+ case 61: \
+ switch (ch2) { \
+ case 824: return 8800; \
+ } \
+ return 0; \
+ case 62: \
+ switch (ch2) { \
+ case 824: return 8815; \
+ } \
+ return 0; \
+ case 65: \
+ switch (ch2) { \
+ case 768: return 192; \
+ case 769: return 193; \
+ case 770: return 194; \
+ case 771: return 195; \
+ case 772: return 256; \
+ case 774: return 258; \
+ case 775: return 550; \
+ case 776: return 196; \
+ case 777: return 7842; \
+ case 778: return 197; \
+ case 780: return 461; \
+ case 783: return 512; \
+ case 785: return 514; \
+ case 803: return 7840; \
+ case 805: return 7680; \
+ case 808: return 260; \
+ } \
+ return 0; \
+ case 66: \
+ switch (ch2) { \
+ case 775: return 7682; \
+ case 803: return 7684; \
+ case 817: return 7686; \
+ } \
+ return 0; \
+ case 67: \
+ switch (ch2) { \
+ case 769: return 262; \
+ case 770: return 264; \
+ case 775: return 266; \
+ case 780: return 268; \
+ case 807: return 199; \
+ } \
+ return 0; \
+ case 68: \
+ switch (ch2) { \
+ case 775: return 7690; \
+ case 780: return 270; \
+ case 803: return 7692; \
+ case 807: return 7696; \
+ case 813: return 7698; \
+ case 817: return 7694; \
+ } \
+ return 0; \
+ case 69: \
+ switch (ch2) { \
+ case 768: return 200; \
+ case 769: return 201; \
+ case 770: return 202; \
+ case 771: return 7868; \
+ case 772: return 274; \
+ case 774: return 276; \
+ case 775: return 278; \
+ case 776: return 203; \
+ case 777: return 7866; \
+ case 780: return 282; \
+ case 783: return 516; \
+ case 785: return 518; \
+ case 803: return 7864; \
+ case 807: return 552; \
+ case 808: return 280; \
+ case 813: return 7704; \
+ case 816: return 7706; \
+ } \
+ return 0; \
+ case 70: \
+ switch (ch2) { \
+ case 775: return 7710; \
+ } \
+ return 0; \
+ case 71: \
+ switch (ch2) { \
+ case 769: return 500; \
+ case 770: return 284; \
+ case 772: return 7712; \
+ case 774: return 286; \
+ case 775: return 288; \
+ case 780: return 486; \
+ case 807: return 290; \
+ } \
+ return 0; \
+ case 72: \
+ switch (ch2) { \
+ case 770: return 292; \
+ case 775: return 7714; \
+ case 776: return 7718; \
+ case 780: return 542; \
+ case 803: return 7716; \
+ case 807: return 7720; \
+ case 814: return 7722; \
+ } \
+ return 0; \
+ case 73: \
+ switch (ch2) { \
+ case 768: return 204; \
+ case 769: return 205; \
+ case 770: return 206; \
+ case 771: return 296; \
+ case 772: return 298; \
+ case 774: return 300; \
+ case 775: return 304; \
+ case 776: return 207; \
+ case 777: return 7880; \
+ case 780: return 463; \
+ case 783: return 520; \
+ case 785: return 522; \
+ case 803: return 7882; \
+ case 808: return 302; \
+ case 816: return 7724; \
+ } \
+ return 0; \
+ case 74: \
+ switch (ch2) { \
+ case 770: return 308; \
+ } \
+ return 0; \
+ case 75: \
+ switch (ch2) { \
+ case 769: return 7728; \
+ case 780: return 488; \
+ case 803: return 7730; \
+ case 807: return 310; \
+ case 817: return 7732; \
+ } \
+ return 0; \
+ case 76: \
+ switch (ch2) { \
+ case 769: return 313; \
+ case 780: return 317; \
+ case 803: return 7734; \
+ case 807: return 315; \
+ case 813: return 7740; \
+ case 817: return 7738; \
+ } \
+ return 0; \
+ case 77: \
+ switch (ch2) { \
+ case 769: return 7742; \
+ case 775: return 7744; \
+ case 803: return 7746; \
+ } \
+ return 0; \
+ case 78: \
+ switch (ch2) { \
+ case 768: return 504; \
+ case 769: return 323; \
+ case 771: return 209; \
+ case 775: return 7748; \
+ case 780: return 327; \
+ case 803: return 7750; \
+ case 807: return 325; \
+ case 813: return 7754; \
+ case 817: return 7752; \
+ } \
+ return 0; \
+ case 79: \
+ switch (ch2) { \
+ case 768: return 210; \
+ case 769: return 211; \
+ case 770: return 212; \
+ case 771: return 213; \
+ case 772: return 332; \
+ case 774: return 334; \
+ case 775: return 558; \
+ case 776: return 214; \
+ case 777: return 7886; \
+ case 779: return 336; \
+ case 780: return 465; \
+ case 783: return 524; \
+ case 785: return 526; \
+ case 795: return 416; \
+ case 803: return 7884; \
+ case 808: return 490; \
+ } \
+ return 0; \
+ case 80: \
+ switch (ch2) { \
+ case 769: return 7764; \
+ case 775: return 7766; \
+ } \
+ return 0; \
+ case 82: \
+ switch (ch2) { \
+ case 769: return 340; \
+ case 775: return 7768; \
+ case 780: return 344; \
+ case 783: return 528; \
+ case 785: return 530; \
+ case 803: return 7770; \
+ case 807: return 342; \
+ case 817: return 7774; \
+ } \
+ return 0; \
+ case 83: \
+ switch (ch2) { \
+ case 769: return 346; \
+ case 770: return 348; \
+ case 775: return 7776; \
+ case 780: return 352; \
+ case 803: return 7778; \
+ case 806: return 536; \
+ case 807: return 350; \
+ } \
+ return 0; \
+ case 84: \
+ switch (ch2) { \
+ case 775: return 7786; \
+ case 780: return 356; \
+ case 803: return 7788; \
+ case 806: return 538; \
+ case 807: return 354; \
+ case 813: return 7792; \
+ case 817: return 7790; \
+ } \
+ return 0; \
+ case 85: \
+ switch (ch2) { \
+ case 768: return 217; \
+ case 769: return 218; \
+ case 770: return 219; \
+ case 771: return 360; \
+ case 772: return 362; \
+ case 774: return 364; \
+ case 776: return 220; \
+ case 777: return 7910; \
+ case 778: return 366; \
+ case 779: return 368; \
+ case 780: return 467; \
+ case 783: return 532; \
+ case 785: return 534; \
+ case 795: return 431; \
+ case 803: return 7908; \
+ case 804: return 7794; \
+ case 808: return 370; \
+ case 813: return 7798; \
+ case 816: return 7796; \
+ } \
+ return 0; \
+ case 86: \
+ switch (ch2) { \
+ case 771: return 7804; \
+ case 803: return 7806; \
+ } \
+ return 0; \
+ case 87: \
+ switch (ch2) { \
+ case 768: return 7808; \
+ case 769: return 7810; \
+ case 770: return 372; \
+ case 775: return 7814; \
+ case 776: return 7812; \
+ case 803: return 7816; \
+ } \
+ return 0; \
+ case 88: \
+ switch (ch2) { \
+ case 775: return 7818; \
+ case 776: return 7820; \
+ } \
+ return 0; \
+ case 89: \
+ switch (ch2) { \
+ case 768: return 7922; \
+ case 769: return 221; \
+ case 770: return 374; \
+ case 771: return 7928; \
+ case 772: return 562; \
+ case 775: return 7822; \
+ case 776: return 376; \
+ case 777: return 7926; \
+ case 803: return 7924; \
+ } \
+ return 0; \
+ case 90: \
+ switch (ch2) { \
+ case 769: return 377; \
+ case 770: return 7824; \
+ case 775: return 379; \
+ case 780: return 381; \
+ case 803: return 7826; \
+ case 817: return 7828; \
+ } \
+ return 0; \
+ case 97: \
+ switch (ch2) { \
+ case 768: return 224; \
+ case 769: return 225; \
+ case 770: return 226; \
+ case 771: return 227; \
+ case 772: return 257; \
+ case 774: return 259; \
+ case 775: return 551; \
+ case 776: return 228; \
+ case 777: return 7843; \
+ case 778: return 229; \
+ case 780: return 462; \
+ case 783: return 513; \
+ case 785: return 515; \
+ case 803: return 7841; \
+ case 805: return 7681; \
+ case 808: return 261; \
+ } \
+ return 0; \
+ case 98: \
+ switch (ch2) { \
+ case 775: return 7683; \
+ case 803: return 7685; \
+ case 817: return 7687; \
+ } \
+ return 0; \
+ case 99: \
+ switch (ch2) { \
+ case 769: return 263; \
+ case 770: return 265; \
+ case 775: return 267; \
+ case 780: return 269; \
+ case 807: return 231; \
+ } \
+ return 0; \
+ case 100: \
+ switch (ch2) { \
+ case 775: return 7691; \
+ case 780: return 271; \
+ case 803: return 7693; \
+ case 807: return 7697; \
+ case 813: return 7699; \
+ case 817: return 7695; \
+ } \
+ return 0; \
+ case 101: \
+ switch (ch2) { \
+ case 768: return 232; \
+ case 769: return 233; \
+ case 770: return 234; \
+ case 771: return 7869; \
+ case 772: return 275; \
+ case 774: return 277; \
+ case 775: return 279; \
+ case 776: return 235; \
+ case 777: return 7867; \
+ case 780: return 283; \
+ case 783: return 517; \
+ case 785: return 519; \
+ case 803: return 7865; \
+ case 807: return 553; \
+ case 808: return 281; \
+ case 813: return 7705; \
+ case 816: return 7707; \
+ } \
+ return 0; \
+ case 102: \
+ switch (ch2) { \
+ case 775: return 7711; \
+ } \
+ return 0; \
+ case 103: \
+ switch (ch2) { \
+ case 769: return 501; \
+ case 770: return 285; \
+ case 772: return 7713; \
+ case 774: return 287; \
+ case 775: return 289; \
+ case 780: return 487; \
+ case 807: return 291; \
+ } \
+ return 0; \
+ case 104: \
+ switch (ch2) { \
+ case 770: return 293; \
+ case 775: return 7715; \
+ case 776: return 7719; \
+ case 780: return 543; \
+ case 803: return 7717; \
+ case 807: return 7721; \
+ case 814: return 7723; \
+ case 817: return 7830; \
+ } \
+ return 0; \
+ case 105: \
+ switch (ch2) { \
+ case 768: return 236; \
+ case 769: return 237; \
+ case 770: return 238; \
+ case 771: return 297; \
+ case 772: return 299; \
+ case 774: return 301; \
+ case 776: return 239; \
+ case 777: return 7881; \
+ case 780: return 464; \
+ case 783: return 521; \
+ case 785: return 523; \
+ case 803: return 7883; \
+ case 808: return 303; \
+ case 816: return 7725; \
+ } \
+ return 0; \
+ case 106: \
+ switch (ch2) { \
+ case 770: return 309; \
+ case 780: return 496; \
+ } \
+ return 0; \
+ case 107: \
+ switch (ch2) { \
+ case 769: return 7729; \
+ case 780: return 489; \
+ case 803: return 7731; \
+ case 807: return 311; \
+ case 817: return 7733; \
+ } \
+ return 0; \
+ case 108: \
+ switch (ch2) { \
+ case 769: return 314; \
+ case 780: return 318; \
+ case 803: return 7735; \
+ case 807: return 316; \
+ case 813: return 7741; \
+ case 817: return 7739; \
+ } \
+ return 0; \
+ case 109: \
+ switch (ch2) { \
+ case 769: return 7743; \
+ case 775: return 7745; \
+ case 803: return 7747; \
+ } \
+ return 0; \
+ case 110: \
+ switch (ch2) { \
+ case 768: return 505; \
+ case 769: return 324; \
+ case 771: return 241; \
+ case 775: return 7749; \
+ case 780: return 328; \
+ case 803: return 7751; \
+ case 807: return 326; \
+ case 813: return 7755; \
+ case 817: return 7753; \
+ } \
+ return 0; \
+ case 111: \
+ switch (ch2) { \
+ case 768: return 242; \
+ case 769: return 243; \
+ case 770: return 244; \
+ case 771: return 245; \
+ case 772: return 333; \
+ case 774: return 335; \
+ case 775: return 559; \
+ case 776: return 246; \
+ case 777: return 7887; \
+ case 779: return 337; \
+ case 780: return 466; \
+ case 783: return 525; \
+ case 785: return 527; \
+ case 795: return 417; \
+ case 803: return 7885; \
+ case 808: return 491; \
+ } \
+ return 0; \
+ case 112: \
+ switch (ch2) { \
+ case 769: return 7765; \
+ case 775: return 7767; \
+ } \
+ return 0; \
+ case 114: \
+ switch (ch2) { \
+ case 769: return 341; \
+ case 775: return 7769; \
+ case 780: return 345; \
+ case 783: return 529; \
+ case 785: return 531; \
+ case 803: return 7771; \
+ case 807: return 343; \
+ case 817: return 7775; \
+ } \
+ return 0; \
+ case 115: \
+ switch (ch2) { \
+ case 769: return 347; \
+ case 770: return 349; \
+ case 775: return 7777; \
+ case 780: return 353; \
+ case 803: return 7779; \
+ case 806: return 537; \
+ case 807: return 351; \
+ } \
+ return 0; \
+ case 116: \
+ switch (ch2) { \
+ case 775: return 7787; \
+ case 776: return 7831; \
+ case 780: return 357; \
+ case 803: return 7789; \
+ case 806: return 539; \
+ case 807: return 355; \
+ case 813: return 7793; \
+ case 817: return 7791; \
+ } \
+ return 0; \
+ case 117: \
+ switch (ch2) { \
+ case 768: return 249; \
+ case 769: return 250; \
+ case 770: return 251; \
+ case 771: return 361; \
+ case 772: return 363; \
+ case 774: return 365; \
+ case 776: return 252; \
+ case 777: return 7911; \
+ case 778: return 367; \
+ case 779: return 369; \
+ case 780: return 468; \
+ case 783: return 533; \
+ case 785: return 535; \
+ case 795: return 432; \
+ case 803: return 7909; \
+ case 804: return 7795; \
+ case 808: return 371; \
+ case 813: return 7799; \
+ case 816: return 7797; \
+ } \
+ return 0; \
+ case 118: \
+ switch (ch2) { \
+ case 771: return 7805; \
+ case 803: return 7807; \
+ } \
+ return 0; \
+ case 119: \
+ switch (ch2) { \
+ case 768: return 7809; \
+ case 769: return 7811; \
+ case 770: return 373; \
+ case 775: return 7815; \
+ case 776: return 7813; \
+ case 778: return 7832; \
+ case 803: return 7817; \
+ } \
+ return 0; \
+ case 120: \
+ switch (ch2) { \
+ case 775: return 7819; \
+ case 776: return 7821; \
+ } \
+ return 0; \
+ case 121: \
+ switch (ch2) { \
+ case 768: return 7923; \
+ case 769: return 253; \
+ case 770: return 375; \
+ case 771: return 7929; \
+ case 772: return 563; \
+ case 775: return 7823; \
+ case 776: return 255; \
+ case 777: return 7927; \
+ case 778: return 7833; \
+ case 803: return 7925; \
+ } \
+ return 0; \
+ case 122: \
+ switch (ch2) { \
+ case 769: return 378; \
+ case 770: return 7825; \
+ case 775: return 380; \
+ case 780: return 382; \
+ case 803: return 7827; \
+ case 817: return 7829; \
+ } \
+ return 0; \
+ case 168: \
+ switch (ch2) { \
+ case 768: return 8173; \
+ case 769: return 901; \
+ case 834: return 8129; \
+ } \
+ return 0; \
+ case 194: \
+ switch (ch2) { \
+ case 768: return 7846; \
+ case 769: return 7844; \
+ case 771: return 7850; \
+ case 777: return 7848; \
+ } \
+ return 0; \
+ case 196: \
+ switch (ch2) { \
+ case 772: return 478; \
+ } \
+ return 0; \
+ case 197: \
+ switch (ch2) { \
+ case 769: return 506; \
+ } \
+ return 0; \
+ case 198: \
+ switch (ch2) { \
+ case 769: return 508; \
+ case 772: return 482; \
+ } \
+ return 0; \
+ case 199: \
+ switch (ch2) { \
+ case 769: return 7688; \
+ } \
+ return 0; \
+ case 202: \
+ switch (ch2) { \
+ case 768: return 7872; \
+ case 769: return 7870; \
+ case 771: return 7876; \
+ case 777: return 7874; \
+ } \
+ return 0; \
+ case 207: \
+ switch (ch2) { \
+ case 769: return 7726; \
+ } \
+ return 0; \
+ case 212: \
+ switch (ch2) { \
+ case 768: return 7890; \
+ case 769: return 7888; \
+ case 771: return 7894; \
+ case 777: return 7892; \
+ } \
+ return 0; \
+ case 213: \
+ switch (ch2) { \
+ case 769: return 7756; \
+ case 772: return 556; \
+ case 776: return 7758; \
+ } \
+ return 0; \
+ case 214: \
+ switch (ch2) { \
+ case 772: return 554; \
+ } \
+ return 0; \
+ case 216: \
+ switch (ch2) { \
+ case 769: return 510; \
+ } \
+ return 0; \
+ case 220: \
+ switch (ch2) { \
+ case 768: return 475; \
+ case 769: return 471; \
+ case 772: return 469; \
+ case 780: return 473; \
+ } \
+ return 0; \
+ case 226: \
+ switch (ch2) { \
+ case 768: return 7847; \
+ case 769: return 7845; \
+ case 771: return 7851; \
+ case 777: return 7849; \
+ } \
+ return 0; \
+ case 228: \
+ switch (ch2) { \
+ case 772: return 479; \
+ } \
+ return 0; \
+ case 229: \
+ switch (ch2) { \
+ case 769: return 507; \
+ } \
+ return 0; \
+ case 230: \
+ switch (ch2) { \
+ case 769: return 509; \
+ case 772: return 483; \
+ } \
+ return 0; \
+ case 231: \
+ switch (ch2) { \
+ case 769: return 7689; \
+ } \
+ return 0; \
+ case 234: \
+ switch (ch2) { \
+ case 768: return 7873; \
+ case 769: return 7871; \
+ case 771: return 7877; \
+ case 777: return 7875; \
+ } \
+ return 0; \
+ case 239: \
+ switch (ch2) { \
+ case 769: return 7727; \
+ } \
+ return 0; \
+ case 244: \
+ switch (ch2) { \
+ case 768: return 7891; \
+ case 769: return 7889; \
+ case 771: return 7895; \
+ case 777: return 7893; \
+ } \
+ return 0; \
+ case 245: \
+ switch (ch2) { \
+ case 769: return 7757; \
+ case 772: return 557; \
+ case 776: return 7759; \
+ } \
+ return 0; \
+ case 246: \
+ switch (ch2) { \
+ case 772: return 555; \
+ } \
+ return 0; \
+ case 248: \
+ switch (ch2) { \
+ case 769: return 511; \
+ } \
+ return 0; \
+ case 252: \
+ switch (ch2) { \
+ case 768: return 476; \
+ case 769: return 472; \
+ case 772: return 470; \
+ case 780: return 474; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 1: \
+ switch (ch1) { \
+ case 258: \
+ switch (ch2) { \
+ case 768: return 7856; \
+ case 769: return 7854; \
+ case 771: return 7860; \
+ case 777: return 7858; \
+ } \
+ return 0; \
+ case 259: \
+ switch (ch2) { \
+ case 768: return 7857; \
+ case 769: return 7855; \
+ case 771: return 7861; \
+ case 777: return 7859; \
+ } \
+ return 0; \
+ case 274: \
+ switch (ch2) { \
+ case 768: return 7700; \
+ case 769: return 7702; \
+ } \
+ return 0; \
+ case 275: \
+ switch (ch2) { \
+ case 768: return 7701; \
+ case 769: return 7703; \
+ } \
+ return 0; \
+ case 332: \
+ switch (ch2) { \
+ case 768: return 7760; \
+ case 769: return 7762; \
+ } \
+ return 0; \
+ case 333: \
+ switch (ch2) { \
+ case 768: return 7761; \
+ case 769: return 7763; \
+ } \
+ return 0; \
+ case 346: \
+ switch (ch2) { \
+ case 775: return 7780; \
+ } \
+ return 0; \
+ case 347: \
+ switch (ch2) { \
+ case 775: return 7781; \
+ } \
+ return 0; \
+ case 352: \
+ switch (ch2) { \
+ case 775: return 7782; \
+ } \
+ return 0; \
+ case 353: \
+ switch (ch2) { \
+ case 775: return 7783; \
+ } \
+ return 0; \
+ case 360: \
+ switch (ch2) { \
+ case 769: return 7800; \
+ } \
+ return 0; \
+ case 361: \
+ switch (ch2) { \
+ case 769: return 7801; \
+ } \
+ return 0; \
+ case 362: \
+ switch (ch2) { \
+ case 776: return 7802; \
+ } \
+ return 0; \
+ case 363: \
+ switch (ch2) { \
+ case 776: return 7803; \
+ } \
+ return 0; \
+ case 383: \
+ switch (ch2) { \
+ case 775: return 7835; \
+ } \
+ return 0; \
+ case 416: \
+ switch (ch2) { \
+ case 768: return 7900; \
+ case 769: return 7898; \
+ case 771: return 7904; \
+ case 777: return 7902; \
+ case 803: return 7906; \
+ } \
+ return 0; \
+ case 417: \
+ switch (ch2) { \
+ case 768: return 7901; \
+ case 769: return 7899; \
+ case 771: return 7905; \
+ case 777: return 7903; \
+ case 803: return 7907; \
+ } \
+ return 0; \
+ case 431: \
+ switch (ch2) { \
+ case 768: return 7914; \
+ case 769: return 7912; \
+ case 771: return 7918; \
+ case 777: return 7916; \
+ case 803: return 7920; \
+ } \
+ return 0; \
+ case 432: \
+ switch (ch2) { \
+ case 768: return 7915; \
+ case 769: return 7913; \
+ case 771: return 7919; \
+ case 777: return 7917; \
+ case 803: return 7921; \
+ } \
+ return 0; \
+ case 439: \
+ switch (ch2) { \
+ case 780: return 494; \
+ } \
+ return 0; \
+ case 490: \
+ switch (ch2) { \
+ case 772: return 492; \
+ } \
+ return 0; \
+ case 491: \
+ switch (ch2) { \
+ case 772: return 493; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 2: \
+ switch (ch1) { \
+ case 550: \
+ switch (ch2) { \
+ case 772: return 480; \
+ } \
+ return 0; \
+ case 551: \
+ switch (ch2) { \
+ case 772: return 481; \
+ } \
+ return 0; \
+ case 552: \
+ switch (ch2) { \
+ case 774: return 7708; \
+ } \
+ return 0; \
+ case 553: \
+ switch (ch2) { \
+ case 774: return 7709; \
+ } \
+ return 0; \
+ case 558: \
+ switch (ch2) { \
+ case 772: return 560; \
+ } \
+ return 0; \
+ case 559: \
+ switch (ch2) { \
+ case 772: return 561; \
+ } \
+ return 0; \
+ case 658: \
+ switch (ch2) { \
+ case 780: return 495; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 3: \
+ switch (ch1) { \
+ case 776: \
+ switch (ch2) { \
+ case 769: return 836; \
+ } \
+ return 0; \
+ case 913: \
+ switch (ch2) { \
+ case 768: return 8122; \
+ case 769: return 902; \
+ case 772: return 8121; \
+ case 774: return 8120; \
+ case 787: return 7944; \
+ case 788: return 7945; \
+ case 837: return 8124; \
+ } \
+ return 0; \
+ case 917: \
+ switch (ch2) { \
+ case 768: return 8136; \
+ case 769: return 904; \
+ case 787: return 7960; \
+ case 788: return 7961; \
+ } \
+ return 0; \
+ case 919: \
+ switch (ch2) { \
+ case 768: return 8138; \
+ case 769: return 905; \
+ case 787: return 7976; \
+ case 788: return 7977; \
+ case 837: return 8140; \
+ } \
+ return 0; \
+ case 921: \
+ switch (ch2) { \
+ case 768: return 8154; \
+ case 769: return 906; \
+ case 772: return 8153; \
+ case 774: return 8152; \
+ case 776: return 938; \
+ case 787: return 7992; \
+ case 788: return 7993; \
+ } \
+ return 0; \
+ case 927: \
+ switch (ch2) { \
+ case 768: return 8184; \
+ case 769: return 908; \
+ case 787: return 8008; \
+ case 788: return 8009; \
+ } \
+ return 0; \
+ case 929: \
+ switch (ch2) { \
+ case 788: return 8172; \
+ } \
+ return 0; \
+ case 933: \
+ switch (ch2) { \
+ case 768: return 8170; \
+ case 769: return 910; \
+ case 772: return 8169; \
+ case 774: return 8168; \
+ case 776: return 939; \
+ case 788: return 8025; \
+ } \
+ return 0; \
+ case 937: \
+ switch (ch2) { \
+ case 768: return 8186; \
+ case 769: return 911; \
+ case 787: return 8040; \
+ case 788: return 8041; \
+ case 837: return 8188; \
+ } \
+ return 0; \
+ case 940: \
+ switch (ch2) { \
+ case 837: return 8116; \
+ } \
+ return 0; \
+ case 942: \
+ switch (ch2) { \
+ case 837: return 8132; \
+ } \
+ return 0; \
+ case 945: \
+ switch (ch2) { \
+ case 768: return 8048; \
+ case 769: return 940; \
+ case 772: return 8113; \
+ case 774: return 8112; \
+ case 787: return 7936; \
+ case 788: return 7937; \
+ case 834: return 8118; \
+ case 837: return 8115; \
+ } \
+ return 0; \
+ case 949: \
+ switch (ch2) { \
+ case 768: return 8050; \
+ case 769: return 941; \
+ case 787: return 7952; \
+ case 788: return 7953; \
+ } \
+ return 0; \
+ case 951: \
+ switch (ch2) { \
+ case 768: return 8052; \
+ case 769: return 942; \
+ case 787: return 7968; \
+ case 788: return 7969; \
+ case 834: return 8134; \
+ case 837: return 8131; \
+ } \
+ return 0; \
+ case 953: \
+ switch (ch2) { \
+ case 768: return 8054; \
+ case 769: return 943; \
+ case 772: return 8145; \
+ case 774: return 8144; \
+ case 776: return 970; \
+ case 787: return 7984; \
+ case 788: return 7985; \
+ case 834: return 8150; \
+ } \
+ return 0; \
+ case 959: \
+ switch (ch2) { \
+ case 768: return 8056; \
+ case 769: return 972; \
+ case 787: return 8000; \
+ case 788: return 8001; \
+ } \
+ return 0; \
+ case 961: \
+ switch (ch2) { \
+ case 787: return 8164; \
+ case 788: return 8165; \
+ } \
+ return 0; \
+ case 965: \
+ switch (ch2) { \
+ case 768: return 8058; \
+ case 769: return 973; \
+ case 772: return 8161; \
+ case 774: return 8160; \
+ case 776: return 971; \
+ case 787: return 8016; \
+ case 788: return 8017; \
+ case 834: return 8166; \
+ } \
+ return 0; \
+ case 969: \
+ switch (ch2) { \
+ case 768: return 8060; \
+ case 769: return 974; \
+ case 787: return 8032; \
+ case 788: return 8033; \
+ case 834: return 8182; \
+ case 837: return 8179; \
+ } \
+ return 0; \
+ case 970: \
+ switch (ch2) { \
+ case 768: return 8146; \
+ case 769: return 912; \
+ case 834: return 8151; \
+ } \
+ return 0; \
+ case 971: \
+ switch (ch2) { \
+ case 768: return 8162; \
+ case 769: return 944; \
+ case 834: return 8167; \
+ } \
+ return 0; \
+ case 974: \
+ switch (ch2) { \
+ case 837: return 8180; \
+ } \
+ return 0; \
+ case 978: \
+ switch (ch2) { \
+ case 769: return 979; \
+ case 776: return 980; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 4: \
+ switch (ch1) { \
+ case 1030: \
+ switch (ch2) { \
+ case 776: return 1031; \
+ } \
+ return 0; \
+ case 1040: \
+ switch (ch2) { \
+ case 774: return 1232; \
+ case 776: return 1234; \
+ } \
+ return 0; \
+ case 1043: \
+ switch (ch2) { \
+ case 769: return 1027; \
+ } \
+ return 0; \
+ case 1045: \
+ switch (ch2) { \
+ case 768: return 1024; \
+ case 774: return 1238; \
+ case 776: return 1025; \
+ } \
+ return 0; \
+ case 1046: \
+ switch (ch2) { \
+ case 774: return 1217; \
+ case 776: return 1244; \
+ } \
+ return 0; \
+ case 1047: \
+ switch (ch2) { \
+ case 776: return 1246; \
+ } \
+ return 0; \
+ case 1048: \
+ switch (ch2) { \
+ case 768: return 1037; \
+ case 772: return 1250; \
+ case 774: return 1049; \
+ case 776: return 1252; \
+ } \
+ return 0; \
+ case 1050: \
+ switch (ch2) { \
+ case 769: return 1036; \
+ } \
+ return 0; \
+ case 1054: \
+ switch (ch2) { \
+ case 776: return 1254; \
+ } \
+ return 0; \
+ case 1059: \
+ switch (ch2) { \
+ case 772: return 1262; \
+ case 774: return 1038; \
+ case 776: return 1264; \
+ case 779: return 1266; \
+ } \
+ return 0; \
+ case 1063: \
+ switch (ch2) { \
+ case 776: return 1268; \
+ } \
+ return 0; \
+ case 1067: \
+ switch (ch2) { \
+ case 776: return 1272; \
+ } \
+ return 0; \
+ case 1069: \
+ switch (ch2) { \
+ case 776: return 1260; \
+ } \
+ return 0; \
+ case 1072: \
+ switch (ch2) { \
+ case 774: return 1233; \
+ case 776: return 1235; \
+ } \
+ return 0; \
+ case 1075: \
+ switch (ch2) { \
+ case 769: return 1107; \
+ } \
+ return 0; \
+ case 1077: \
+ switch (ch2) { \
+ case 768: return 1104; \
+ case 774: return 1239; \
+ case 776: return 1105; \
+ } \
+ return 0; \
+ case 1078: \
+ switch (ch2) { \
+ case 774: return 1218; \
+ case 776: return 1245; \
+ } \
+ return 0; \
+ case 1079: \
+ switch (ch2) { \
+ case 776: return 1247; \
+ } \
+ return 0; \
+ case 1080: \
+ switch (ch2) { \
+ case 768: return 1117; \
+ case 772: return 1251; \
+ case 774: return 1081; \
+ case 776: return 1253; \
+ } \
+ return 0; \
+ case 1082: \
+ switch (ch2) { \
+ case 769: return 1116; \
+ } \
+ return 0; \
+ case 1086: \
+ switch (ch2) { \
+ case 776: return 1255; \
+ } \
+ return 0; \
+ case 1091: \
+ switch (ch2) { \
+ case 772: return 1263; \
+ case 774: return 1118; \
+ case 776: return 1265; \
+ case 779: return 1267; \
+ } \
+ return 0; \
+ case 1095: \
+ switch (ch2) { \
+ case 776: return 1269; \
+ } \
+ return 0; \
+ case 1099: \
+ switch (ch2) { \
+ case 776: return 1273; \
+ } \
+ return 0; \
+ case 1101: \
+ switch (ch2) { \
+ case 776: return 1261; \
+ } \
+ return 0; \
+ case 1110: \
+ switch (ch2) { \
+ case 776: return 1111; \
+ } \
+ return 0; \
+ case 1140: \
+ switch (ch2) { \
+ case 783: return 1142; \
+ } \
+ return 0; \
+ case 1141: \
+ switch (ch2) { \
+ case 783: return 1143; \
+ } \
+ return 0; \
+ case 1240: \
+ switch (ch2) { \
+ case 776: return 1242; \
+ } \
+ return 0; \
+ case 1241: \
+ switch (ch2) { \
+ case 776: return 1243; \
+ } \
+ return 0; \
+ case 1256: \
+ switch (ch2) { \
+ case 776: return 1258; \
+ } \
+ return 0; \
+ case 1257: \
+ switch (ch2) { \
+ case 776: return 1259; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 5: \
+ switch (ch1) { \
+ case 1488: \
+ switch (ch2) { \
+ case 1463: return 64302; \
+ case 1464: return 64303; \
+ case 1468: return 64304; \
+ } \
+ return 0; \
+ case 1489: \
+ switch (ch2) { \
+ case 1468: return 64305; \
+ case 1471: return 64332; \
+ } \
+ return 0; \
+ case 1490: \
+ switch (ch2) { \
+ case 1468: return 64306; \
+ } \
+ return 0; \
+ case 1491: \
+ switch (ch2) { \
+ case 1468: return 64307; \
+ } \
+ return 0; \
+ case 1492: \
+ switch (ch2) { \
+ case 1468: return 64308; \
+ } \
+ return 0; \
+ case 1493: \
+ switch (ch2) { \
+ case 1465: return 64331; \
+ case 1468: return 64309; \
+ } \
+ return 0; \
+ case 1494: \
+ switch (ch2) { \
+ case 1468: return 64310; \
+ } \
+ return 0; \
+ case 1496: \
+ switch (ch2) { \
+ case 1468: return 64312; \
+ } \
+ return 0; \
+ case 1497: \
+ switch (ch2) { \
+ case 1460: return 64285; \
+ case 1468: return 64313; \
+ } \
+ return 0; \
+ case 1498: \
+ switch (ch2) { \
+ case 1468: return 64314; \
+ } \
+ return 0; \
+ case 1499: \
+ switch (ch2) { \
+ case 1468: return 64315; \
+ case 1471: return 64333; \
+ } \
+ return 0; \
+ case 1500: \
+ switch (ch2) { \
+ case 1468: return 64316; \
+ } \
+ return 0; \
+ case 1502: \
+ switch (ch2) { \
+ case 1468: return 64318; \
+ } \
+ return 0; \
+ case 1504: \
+ switch (ch2) { \
+ case 1468: return 64320; \
+ } \
+ return 0; \
+ case 1505: \
+ switch (ch2) { \
+ case 1468: return 64321; \
+ } \
+ return 0; \
+ case 1507: \
+ switch (ch2) { \
+ case 1468: return 64323; \
+ } \
+ return 0; \
+ case 1508: \
+ switch (ch2) { \
+ case 1468: return 64324; \
+ case 1471: return 64334; \
+ } \
+ return 0; \
+ case 1510: \
+ switch (ch2) { \
+ case 1468: return 64326; \
+ } \
+ return 0; \
+ case 1511: \
+ switch (ch2) { \
+ case 1468: return 64327; \
+ } \
+ return 0; \
+ case 1512: \
+ switch (ch2) { \
+ case 1468: return 64328; \
+ } \
+ return 0; \
+ case 1513: \
+ switch (ch2) { \
+ case 1468: return 64329; \
+ case 1473: return 64298; \
+ case 1474: return 64299; \
+ } \
+ return 0; \
+ case 1514: \
+ switch (ch2) { \
+ case 1468: return 64330; \
+ } \
+ return 0; \
+ case 1522: \
+ switch (ch2) { \
+ case 1463: return 64287; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 6: \
+ switch (ch1) { \
+ case 1575: \
+ switch (ch2) { \
+ case 1619: return 1570; \
+ case 1620: return 1571; \
+ case 1621: return 1573; \
+ } \
+ return 0; \
+ case 1608: \
+ switch (ch2) { \
+ case 1620: return 1572; \
+ } \
+ return 0; \
+ case 1610: \
+ switch (ch2) { \
+ case 1620: return 1574; \
+ } \
+ return 0; \
+ case 1729: \
+ switch (ch2) { \
+ case 1620: return 1730; \
+ } \
+ return 0; \
+ case 1746: \
+ switch (ch2) { \
+ case 1620: return 1747; \
+ } \
+ return 0; \
+ case 1749: \
+ switch (ch2) { \
+ case 1620: return 1728; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 9: \
+ switch (ch1) { \
+ case 2325: \
+ switch (ch2) { \
+ case 2364: return 2392; \
+ } \
+ return 0; \
+ case 2326: \
+ switch (ch2) { \
+ case 2364: return 2393; \
+ } \
+ return 0; \
+ case 2327: \
+ switch (ch2) { \
+ case 2364: return 2394; \
+ } \
+ return 0; \
+ case 2332: \
+ switch (ch2) { \
+ case 2364: return 2395; \
+ } \
+ return 0; \
+ case 2337: \
+ switch (ch2) { \
+ case 2364: return 2396; \
+ } \
+ return 0; \
+ case 2338: \
+ switch (ch2) { \
+ case 2364: return 2397; \
+ } \
+ return 0; \
+ case 2344: \
+ switch (ch2) { \
+ case 2364: return 2345; \
+ } \
+ return 0; \
+ case 2347: \
+ switch (ch2) { \
+ case 2364: return 2398; \
+ } \
+ return 0; \
+ case 2351: \
+ switch (ch2) { \
+ case 2364: return 2399; \
+ } \
+ return 0; \
+ case 2352: \
+ switch (ch2) { \
+ case 2364: return 2353; \
+ } \
+ return 0; \
+ case 2355: \
+ switch (ch2) { \
+ case 2364: return 2356; \
+ } \
+ return 0; \
+ case 2465: \
+ switch (ch2) { \
+ case 2492: return 2524; \
+ } \
+ return 0; \
+ case 2466: \
+ switch (ch2) { \
+ case 2492: return 2525; \
+ } \
+ return 0; \
+ case 2479: \
+ switch (ch2) { \
+ case 2492: return 2527; \
+ } \
+ return 0; \
+ case 2503: \
+ switch (ch2) { \
+ case 2494: return 2507; \
+ case 2519: return 2508; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 10: \
+ switch (ch1) { \
+ case 2582: \
+ switch (ch2) { \
+ case 2620: return 2649; \
+ } \
+ return 0; \
+ case 2583: \
+ switch (ch2) { \
+ case 2620: return 2650; \
+ } \
+ return 0; \
+ case 2588: \
+ switch (ch2) { \
+ case 2620: return 2651; \
+ } \
+ return 0; \
+ case 2603: \
+ switch (ch2) { \
+ case 2620: return 2654; \
+ } \
+ return 0; \
+ case 2610: \
+ switch (ch2) { \
+ case 2620: return 2611; \
+ } \
+ return 0; \
+ case 2616: \
+ switch (ch2) { \
+ case 2620: return 2614; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 11: \
+ switch (ch1) { \
+ case 2849: \
+ switch (ch2) { \
+ case 2876: return 2908; \
+ } \
+ return 0; \
+ case 2850: \
+ switch (ch2) { \
+ case 2876: return 2909; \
+ } \
+ return 0; \
+ case 2887: \
+ switch (ch2) { \
+ case 2878: return 2891; \
+ case 2902: return 2888; \
+ case 2903: return 2892; \
+ } \
+ return 0; \
+ case 2962: \
+ switch (ch2) { \
+ case 3031: return 2964; \
+ } \
+ return 0; \
+ case 3014: \
+ switch (ch2) { \
+ case 3006: return 3018; \
+ case 3031: return 3020; \
+ } \
+ return 0; \
+ case 3015: \
+ switch (ch2) { \
+ case 3006: return 3019; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 12: \
+ switch (ch1) { \
+ case 3142: \
+ switch (ch2) { \
+ case 3158: return 3144; \
+ } \
+ return 0; \
+ case 3263: \
+ switch (ch2) { \
+ case 3285: return 3264; \
+ } \
+ return 0; \
+ case 3270: \
+ switch (ch2) { \
+ case 3266: return 3274; \
+ case 3285: return 3271; \
+ case 3286: return 3272; \
+ } \
+ return 0; \
+ case 3274: \
+ switch (ch2) { \
+ case 3285: return 3275; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 13: \
+ switch (ch1) { \
+ case 3398: \
+ switch (ch2) { \
+ case 3390: return 3402; \
+ case 3415: return 3404; \
+ } \
+ return 0; \
+ case 3399: \
+ switch (ch2) { \
+ case 3390: return 3403; \
+ } \
+ return 0; \
+ case 3545: \
+ switch (ch2) { \
+ case 3530: return 3546; \
+ case 3535: return 3548; \
+ case 3551: return 3550; \
+ } \
+ return 0; \
+ case 3548: \
+ switch (ch2) { \
+ case 3530: return 3549; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 15: \
+ switch (ch1) { \
+ case 3904: \
+ switch (ch2) { \
+ case 4021: return 3945; \
+ } \
+ return 0; \
+ case 3906: \
+ switch (ch2) { \
+ case 4023: return 3907; \
+ } \
+ return 0; \
+ case 3916: \
+ switch (ch2) { \
+ case 4023: return 3917; \
+ } \
+ return 0; \
+ case 3921: \
+ switch (ch2) { \
+ case 4023: return 3922; \
+ } \
+ return 0; \
+ case 3926: \
+ switch (ch2) { \
+ case 4023: return 3927; \
+ } \
+ return 0; \
+ case 3931: \
+ switch (ch2) { \
+ case 4023: return 3932; \
+ } \
+ return 0; \
+ case 3953: \
+ switch (ch2) { \
+ case 3954: return 3955; \
+ case 3956: return 3957; \
+ case 3968: return 3969; \
+ } \
+ return 0; \
+ case 3984: \
+ switch (ch2) { \
+ case 4021: return 4025; \
+ } \
+ return 0; \
+ case 3986: \
+ switch (ch2) { \
+ case 4023: return 3987; \
+ } \
+ return 0; \
+ case 3996: \
+ switch (ch2) { \
+ case 4023: return 3997; \
+ } \
+ return 0; \
+ case 4001: \
+ switch (ch2) { \
+ case 4023: return 4002; \
+ } \
+ return 0; \
+ case 4006: \
+ switch (ch2) { \
+ case 4023: return 4007; \
+ } \
+ return 0; \
+ case 4011: \
+ switch (ch2) { \
+ case 4023: return 4012; \
+ } \
+ return 0; \
+ case 4018: \
+ switch (ch2) { \
+ case 3968: return 3958; \
+ } \
+ return 0; \
+ case 4019: \
+ switch (ch2) { \
+ case 3968: return 3960; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 16: \
+ switch (ch1) { \
+ case 4133: \
+ switch (ch2) { \
+ case 4142: return 4134; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 30: \
+ switch (ch1) { \
+ case 7734: \
+ switch (ch2) { \
+ case 772: return 7736; \
+ } \
+ return 0; \
+ case 7735: \
+ switch (ch2) { \
+ case 772: return 7737; \
+ } \
+ return 0; \
+ case 7770: \
+ switch (ch2) { \
+ case 772: return 7772; \
+ } \
+ return 0; \
+ case 7771: \
+ switch (ch2) { \
+ case 772: return 7773; \
+ } \
+ return 0; \
+ case 7778: \
+ switch (ch2) { \
+ case 775: return 7784; \
+ } \
+ return 0; \
+ case 7779: \
+ switch (ch2) { \
+ case 775: return 7785; \
+ } \
+ return 0; \
+ case 7840: \
+ switch (ch2) { \
+ case 770: return 7852; \
+ case 774: return 7862; \
+ } \
+ return 0; \
+ case 7841: \
+ switch (ch2) { \
+ case 770: return 7853; \
+ case 774: return 7863; \
+ } \
+ return 0; \
+ case 7864: \
+ switch (ch2) { \
+ case 770: return 7878; \
+ } \
+ return 0; \
+ case 7865: \
+ switch (ch2) { \
+ case 770: return 7879; \
+ } \
+ return 0; \
+ case 7884: \
+ switch (ch2) { \
+ case 770: return 7896; \
+ } \
+ return 0; \
+ case 7885: \
+ switch (ch2) { \
+ case 770: return 7897; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 31: \
+ switch (ch1) { \
+ case 7936: \
+ switch (ch2) { \
+ case 768: return 7938; \
+ case 769: return 7940; \
+ case 834: return 7942; \
+ case 837: return 8064; \
+ } \
+ return 0; \
+ case 7937: \
+ switch (ch2) { \
+ case 768: return 7939; \
+ case 769: return 7941; \
+ case 834: return 7943; \
+ case 837: return 8065; \
+ } \
+ return 0; \
+ case 7938: \
+ switch (ch2) { \
+ case 837: return 8066; \
+ } \
+ return 0; \
+ case 7939: \
+ switch (ch2) { \
+ case 837: return 8067; \
+ } \
+ return 0; \
+ case 7940: \
+ switch (ch2) { \
+ case 837: return 8068; \
+ } \
+ return 0; \
+ case 7941: \
+ switch (ch2) { \
+ case 837: return 8069; \
+ } \
+ return 0; \
+ case 7942: \
+ switch (ch2) { \
+ case 837: return 8070; \
+ } \
+ return 0; \
+ case 7943: \
+ switch (ch2) { \
+ case 837: return 8071; \
+ } \
+ return 0; \
+ case 7944: \
+ switch (ch2) { \
+ case 768: return 7946; \
+ case 769: return 7948; \
+ case 834: return 7950; \
+ case 837: return 8072; \
+ } \
+ return 0; \
+ case 7945: \
+ switch (ch2) { \
+ case 768: return 7947; \
+ case 769: return 7949; \
+ case 834: return 7951; \
+ case 837: return 8073; \
+ } \
+ return 0; \
+ case 7946: \
+ switch (ch2) { \
+ case 837: return 8074; \
+ } \
+ return 0; \
+ case 7947: \
+ switch (ch2) { \
+ case 837: return 8075; \
+ } \
+ return 0; \
+ case 7948: \
+ switch (ch2) { \
+ case 837: return 8076; \
+ } \
+ return 0; \
+ case 7949: \
+ switch (ch2) { \
+ case 837: return 8077; \
+ } \
+ return 0; \
+ case 7950: \
+ switch (ch2) { \
+ case 837: return 8078; \
+ } \
+ return 0; \
+ case 7951: \
+ switch (ch2) { \
+ case 837: return 8079; \
+ } \
+ return 0; \
+ case 7952: \
+ switch (ch2) { \
+ case 768: return 7954; \
+ case 769: return 7956; \
+ } \
+ return 0; \
+ case 7953: \
+ switch (ch2) { \
+ case 768: return 7955; \
+ case 769: return 7957; \
+ } \
+ return 0; \
+ case 7960: \
+ switch (ch2) { \
+ case 768: return 7962; \
+ case 769: return 7964; \
+ } \
+ return 0; \
+ case 7961: \
+ switch (ch2) { \
+ case 768: return 7963; \
+ case 769: return 7965; \
+ } \
+ return 0; \
+ case 7968: \
+ switch (ch2) { \
+ case 768: return 7970; \
+ case 769: return 7972; \
+ case 834: return 7974; \
+ case 837: return 8080; \
+ } \
+ return 0; \
+ case 7969: \
+ switch (ch2) { \
+ case 768: return 7971; \
+ case 769: return 7973; \
+ case 834: return 7975; \
+ case 837: return 8081; \
+ } \
+ return 0; \
+ case 7970: \
+ switch (ch2) { \
+ case 837: return 8082; \
+ } \
+ return 0; \
+ case 7971: \
+ switch (ch2) { \
+ case 837: return 8083; \
+ } \
+ return 0; \
+ case 7972: \
+ switch (ch2) { \
+ case 837: return 8084; \
+ } \
+ return 0; \
+ case 7973: \
+ switch (ch2) { \
+ case 837: return 8085; \
+ } \
+ return 0; \
+ case 7974: \
+ switch (ch2) { \
+ case 837: return 8086; \
+ } \
+ return 0; \
+ case 7975: \
+ switch (ch2) { \
+ case 837: return 8087; \
+ } \
+ return 0; \
+ case 7976: \
+ switch (ch2) { \
+ case 768: return 7978; \
+ case 769: return 7980; \
+ case 834: return 7982; \
+ case 837: return 8088; \
+ } \
+ return 0; \
+ case 7977: \
+ switch (ch2) { \
+ case 768: return 7979; \
+ case 769: return 7981; \
+ case 834: return 7983; \
+ case 837: return 8089; \
+ } \
+ return 0; \
+ case 7978: \
+ switch (ch2) { \
+ case 837: return 8090; \
+ } \
+ return 0; \
+ case 7979: \
+ switch (ch2) { \
+ case 837: return 8091; \
+ } \
+ return 0; \
+ case 7980: \
+ switch (ch2) { \
+ case 837: return 8092; \
+ } \
+ return 0; \
+ case 7981: \
+ switch (ch2) { \
+ case 837: return 8093; \
+ } \
+ return 0; \
+ case 7982: \
+ switch (ch2) { \
+ case 837: return 8094; \
+ } \
+ return 0; \
+ case 7983: \
+ switch (ch2) { \
+ case 837: return 8095; \
+ } \
+ return 0; \
+ case 7984: \
+ switch (ch2) { \
+ case 768: return 7986; \
+ case 769: return 7988; \
+ case 834: return 7990; \
+ } \
+ return 0; \
+ case 7985: \
+ switch (ch2) { \
+ case 768: return 7987; \
+ case 769: return 7989; \
+ case 834: return 7991; \
+ } \
+ return 0; \
+ case 7992: \
+ switch (ch2) { \
+ case 768: return 7994; \
+ case 769: return 7996; \
+ case 834: return 7998; \
+ } \
+ return 0; \
+ case 7993: \
+ switch (ch2) { \
+ case 768: return 7995; \
+ case 769: return 7997; \
+ case 834: return 7999; \
+ } \
+ return 0; \
+ case 8000: \
+ switch (ch2) { \
+ case 768: return 8002; \
+ case 769: return 8004; \
+ } \
+ return 0; \
+ case 8001: \
+ switch (ch2) { \
+ case 768: return 8003; \
+ case 769: return 8005; \
+ } \
+ return 0; \
+ case 8008: \
+ switch (ch2) { \
+ case 768: return 8010; \
+ case 769: return 8012; \
+ } \
+ return 0; \
+ case 8009: \
+ switch (ch2) { \
+ case 768: return 8011; \
+ case 769: return 8013; \
+ } \
+ return 0; \
+ case 8016: \
+ switch (ch2) { \
+ case 768: return 8018; \
+ case 769: return 8020; \
+ case 834: return 8022; \
+ } \
+ return 0; \
+ case 8017: \
+ switch (ch2) { \
+ case 768: return 8019; \
+ case 769: return 8021; \
+ case 834: return 8023; \
+ } \
+ return 0; \
+ case 8025: \
+ switch (ch2) { \
+ case 768: return 8027; \
+ case 769: return 8029; \
+ case 834: return 8031; \
+ } \
+ return 0; \
+ case 8032: \
+ switch (ch2) { \
+ case 768: return 8034; \
+ case 769: return 8036; \
+ case 834: return 8038; \
+ case 837: return 8096; \
+ } \
+ return 0; \
+ case 8033: \
+ switch (ch2) { \
+ case 768: return 8035; \
+ case 769: return 8037; \
+ case 834: return 8039; \
+ case 837: return 8097; \
+ } \
+ return 0; \
+ case 8034: \
+ switch (ch2) { \
+ case 837: return 8098; \
+ } \
+ return 0; \
+ case 8035: \
+ switch (ch2) { \
+ case 837: return 8099; \
+ } \
+ return 0; \
+ case 8036: \
+ switch (ch2) { \
+ case 837: return 8100; \
+ } \
+ return 0; \
+ case 8037: \
+ switch (ch2) { \
+ case 837: return 8101; \
+ } \
+ return 0; \
+ case 8038: \
+ switch (ch2) { \
+ case 837: return 8102; \
+ } \
+ return 0; \
+ case 8039: \
+ switch (ch2) { \
+ case 837: return 8103; \
+ } \
+ return 0; \
+ case 8040: \
+ switch (ch2) { \
+ case 768: return 8042; \
+ case 769: return 8044; \
+ case 834: return 8046; \
+ case 837: return 8104; \
+ } \
+ return 0; \
+ case 8041: \
+ switch (ch2) { \
+ case 768: return 8043; \
+ case 769: return 8045; \
+ case 834: return 8047; \
+ case 837: return 8105; \
+ } \
+ return 0; \
+ case 8042: \
+ switch (ch2) { \
+ case 837: return 8106; \
+ } \
+ return 0; \
+ case 8043: \
+ switch (ch2) { \
+ case 837: return 8107; \
+ } \
+ return 0; \
+ case 8044: \
+ switch (ch2) { \
+ case 837: return 8108; \
+ } \
+ return 0; \
+ case 8045: \
+ switch (ch2) { \
+ case 837: return 8109; \
+ } \
+ return 0; \
+ case 8046: \
+ switch (ch2) { \
+ case 837: return 8110; \
+ } \
+ return 0; \
+ case 8047: \
+ switch (ch2) { \
+ case 837: return 8111; \
+ } \
+ return 0; \
+ case 8048: \
+ switch (ch2) { \
+ case 837: return 8114; \
+ } \
+ return 0; \
+ case 8052: \
+ switch (ch2) { \
+ case 837: return 8130; \
+ } \
+ return 0; \
+ case 8060: \
+ switch (ch2) { \
+ case 837: return 8178; \
+ } \
+ return 0; \
+ case 8118: \
+ switch (ch2) { \
+ case 837: return 8119; \
+ } \
+ return 0; \
+ case 8127: \
+ switch (ch2) { \
+ case 768: return 8141; \
+ case 769: return 8142; \
+ case 834: return 8143; \
+ } \
+ return 0; \
+ case 8134: \
+ switch (ch2) { \
+ case 837: return 8135; \
+ } \
+ return 0; \
+ case 8182: \
+ switch (ch2) { \
+ case 837: return 8183; \
+ } \
+ return 0; \
+ case 8190: \
+ switch (ch2) { \
+ case 768: return 8157; \
+ case 769: return 8158; \
+ case 834: return 8159; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 33: \
+ switch (ch1) { \
+ case 8592: \
+ switch (ch2) { \
+ case 824: return 8602; \
+ } \
+ return 0; \
+ case 8594: \
+ switch (ch2) { \
+ case 824: return 8603; \
+ } \
+ return 0; \
+ case 8596: \
+ switch (ch2) { \
+ case 824: return 8622; \
+ } \
+ return 0; \
+ case 8656: \
+ switch (ch2) { \
+ case 824: return 8653; \
+ } \
+ return 0; \
+ case 8658: \
+ switch (ch2) { \
+ case 824: return 8655; \
+ } \
+ return 0; \
+ case 8660: \
+ switch (ch2) { \
+ case 824: return 8654; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 34: \
+ switch (ch1) { \
+ case 8707: \
+ switch (ch2) { \
+ case 824: return 8708; \
+ } \
+ return 0; \
+ case 8712: \
+ switch (ch2) { \
+ case 824: return 8713; \
+ } \
+ return 0; \
+ case 8715: \
+ switch (ch2) { \
+ case 824: return 8716; \
+ } \
+ return 0; \
+ case 8739: \
+ switch (ch2) { \
+ case 824: return 8740; \
+ } \
+ return 0; \
+ case 8741: \
+ switch (ch2) { \
+ case 824: return 8742; \
+ } \
+ return 0; \
+ case 8764: \
+ switch (ch2) { \
+ case 824: return 8769; \
+ } \
+ return 0; \
+ case 8771: \
+ switch (ch2) { \
+ case 824: return 8772; \
+ } \
+ return 0; \
+ case 8773: \
+ switch (ch2) { \
+ case 824: return 8775; \
+ } \
+ return 0; \
+ case 8776: \
+ switch (ch2) { \
+ case 824: return 8777; \
+ } \
+ return 0; \
+ case 8781: \
+ switch (ch2) { \
+ case 824: return 8813; \
+ } \
+ return 0; \
+ case 8801: \
+ switch (ch2) { \
+ case 824: return 8802; \
+ } \
+ return 0; \
+ case 8804: \
+ switch (ch2) { \
+ case 824: return 8816; \
+ } \
+ return 0; \
+ case 8805: \
+ switch (ch2) { \
+ case 824: return 8817; \
+ } \
+ return 0; \
+ case 8818: \
+ switch (ch2) { \
+ case 824: return 8820; \
+ } \
+ return 0; \
+ case 8819: \
+ switch (ch2) { \
+ case 824: return 8821; \
+ } \
+ return 0; \
+ case 8822: \
+ switch (ch2) { \
+ case 824: return 8824; \
+ } \
+ return 0; \
+ case 8823: \
+ switch (ch2) { \
+ case 824: return 8825; \
+ } \
+ return 0; \
+ case 8826: \
+ switch (ch2) { \
+ case 824: return 8832; \
+ } \
+ return 0; \
+ case 8827: \
+ switch (ch2) { \
+ case 824: return 8833; \
+ } \
+ return 0; \
+ case 8828: \
+ switch (ch2) { \
+ case 824: return 8928; \
+ } \
+ return 0; \
+ case 8829: \
+ switch (ch2) { \
+ case 824: return 8929; \
+ } \
+ return 0; \
+ case 8834: \
+ switch (ch2) { \
+ case 824: return 8836; \
+ } \
+ return 0; \
+ case 8835: \
+ switch (ch2) { \
+ case 824: return 8837; \
+ } \
+ return 0; \
+ case 8838: \
+ switch (ch2) { \
+ case 824: return 8840; \
+ } \
+ return 0; \
+ case 8839: \
+ switch (ch2) { \
+ case 824: return 8841; \
+ } \
+ return 0; \
+ case 8849: \
+ switch (ch2) { \
+ case 824: return 8930; \
+ } \
+ return 0; \
+ case 8850: \
+ switch (ch2) { \
+ case 824: return 8931; \
+ } \
+ return 0; \
+ case 8866: \
+ switch (ch2) { \
+ case 824: return 8876; \
+ } \
+ return 0; \
+ case 8872: \
+ switch (ch2) { \
+ case 824: return 8877; \
+ } \
+ return 0; \
+ case 8873: \
+ switch (ch2) { \
+ case 824: return 8878; \
+ } \
+ return 0; \
+ case 8875: \
+ switch (ch2) { \
+ case 824: return 8879; \
+ } \
+ return 0; \
+ case 8882: \
+ switch (ch2) { \
+ case 824: return 8938; \
+ } \
+ return 0; \
+ case 8883: \
+ switch (ch2) { \
+ case 824: return 8939; \
+ } \
+ return 0; \
+ case 8884: \
+ switch (ch2) { \
+ case 824: return 8940; \
+ } \
+ return 0; \
+ case 8885: \
+ switch (ch2) { \
+ case 824: return 8941; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 42: \
+ switch (ch1) { \
+ case 10973: \
+ switch (ch2) { \
+ case 824: return 10972; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 48: \
+ switch (ch1) { \
+ case 12358: \
+ switch (ch2) { \
+ case 12441: return 12436; \
+ } \
+ return 0; \
+ case 12363: \
+ switch (ch2) { \
+ case 12441: return 12364; \
+ } \
+ return 0; \
+ case 12365: \
+ switch (ch2) { \
+ case 12441: return 12366; \
+ } \
+ return 0; \
+ case 12367: \
+ switch (ch2) { \
+ case 12441: return 12368; \
+ } \
+ return 0; \
+ case 12369: \
+ switch (ch2) { \
+ case 12441: return 12370; \
+ } \
+ return 0; \
+ case 12371: \
+ switch (ch2) { \
+ case 12441: return 12372; \
+ } \
+ return 0; \
+ case 12373: \
+ switch (ch2) { \
+ case 12441: return 12374; \
+ } \
+ return 0; \
+ case 12375: \
+ switch (ch2) { \
+ case 12441: return 12376; \
+ } \
+ return 0; \
+ case 12377: \
+ switch (ch2) { \
+ case 12441: return 12378; \
+ } \
+ return 0; \
+ case 12379: \
+ switch (ch2) { \
+ case 12441: return 12380; \
+ } \
+ return 0; \
+ case 12381: \
+ switch (ch2) { \
+ case 12441: return 12382; \
+ } \
+ return 0; \
+ case 12383: \
+ switch (ch2) { \
+ case 12441: return 12384; \
+ } \
+ return 0; \
+ case 12385: \
+ switch (ch2) { \
+ case 12441: return 12386; \
+ } \
+ return 0; \
+ case 12388: \
+ switch (ch2) { \
+ case 12441: return 12389; \
+ } \
+ return 0; \
+ case 12390: \
+ switch (ch2) { \
+ case 12441: return 12391; \
+ } \
+ return 0; \
+ case 12392: \
+ switch (ch2) { \
+ case 12441: return 12393; \
+ } \
+ return 0; \
+ case 12399: \
+ switch (ch2) { \
+ case 12441: return 12400; \
+ case 12442: return 12401; \
+ } \
+ return 0; \
+ case 12402: \
+ switch (ch2) { \
+ case 12441: return 12403; \
+ case 12442: return 12404; \
+ } \
+ return 0; \
+ case 12405: \
+ switch (ch2) { \
+ case 12441: return 12406; \
+ case 12442: return 12407; \
+ } \
+ return 0; \
+ case 12408: \
+ switch (ch2) { \
+ case 12441: return 12409; \
+ case 12442: return 12410; \
+ } \
+ return 0; \
+ case 12411: \
+ switch (ch2) { \
+ case 12441: return 12412; \
+ case 12442: return 12413; \
+ } \
+ return 0; \
+ case 12445: \
+ switch (ch2) { \
+ case 12441: return 12446; \
+ } \
+ return 0; \
+ case 12454: \
+ switch (ch2) { \
+ case 12441: return 12532; \
+ } \
+ return 0; \
+ case 12459: \
+ switch (ch2) { \
+ case 12441: return 12460; \
+ } \
+ return 0; \
+ case 12461: \
+ switch (ch2) { \
+ case 12441: return 12462; \
+ } \
+ return 0; \
+ case 12463: \
+ switch (ch2) { \
+ case 12441: return 12464; \
+ } \
+ return 0; \
+ case 12465: \
+ switch (ch2) { \
+ case 12441: return 12466; \
+ } \
+ return 0; \
+ case 12467: \
+ switch (ch2) { \
+ case 12441: return 12468; \
+ } \
+ return 0; \
+ case 12469: \
+ switch (ch2) { \
+ case 12441: return 12470; \
+ } \
+ return 0; \
+ case 12471: \
+ switch (ch2) { \
+ case 12441: return 12472; \
+ } \
+ return 0; \
+ case 12473: \
+ switch (ch2) { \
+ case 12441: return 12474; \
+ } \
+ return 0; \
+ case 12475: \
+ switch (ch2) { \
+ case 12441: return 12476; \
+ } \
+ return 0; \
+ case 12477: \
+ switch (ch2) { \
+ case 12441: return 12478; \
+ } \
+ return 0; \
+ case 12479: \
+ switch (ch2) { \
+ case 12441: return 12480; \
+ } \
+ return 0; \
+ case 12481: \
+ switch (ch2) { \
+ case 12441: return 12482; \
+ } \
+ return 0; \
+ case 12484: \
+ switch (ch2) { \
+ case 12441: return 12485; \
+ } \
+ return 0; \
+ case 12486: \
+ switch (ch2) { \
+ case 12441: return 12487; \
+ } \
+ return 0; \
+ case 12488: \
+ switch (ch2) { \
+ case 12441: return 12489; \
+ } \
+ return 0; \
+ case 12495: \
+ switch (ch2) { \
+ case 12441: return 12496; \
+ case 12442: return 12497; \
+ } \
+ return 0; \
+ case 12498: \
+ switch (ch2) { \
+ case 12441: return 12499; \
+ case 12442: return 12500; \
+ } \
+ return 0; \
+ case 12501: \
+ switch (ch2) { \
+ case 12441: return 12502; \
+ case 12442: return 12503; \
+ } \
+ return 0; \
+ case 12504: \
+ switch (ch2) { \
+ case 12441: return 12505; \
+ case 12442: return 12506; \
+ } \
+ return 0; \
+ case 12507: \
+ switch (ch2) { \
+ case 12441: return 12508; \
+ case 12442: return 12509; \
+ } \
+ return 0; \
+ case 12527: \
+ switch (ch2) { \
+ case 12441: return 12535; \
+ } \
+ return 0; \
+ case 12528: \
+ switch (ch2) { \
+ case 12441: return 12536; \
+ } \
+ return 0; \
+ case 12529: \
+ switch (ch2) { \
+ case 12441: return 12537; \
+ } \
+ return 0; \
+ case 12530: \
+ switch (ch2) { \
+ case 12441: return 12538; \
+ } \
+ return 0; \
+ case 12541: \
+ switch (ch2) { \
+ case 12441: return 12542; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 251: \
+ switch (ch1) { \
+ case 64329: \
+ switch (ch2) { \
+ case 1473: return 64300; \
+ case 1474: return 64301; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ case 465: \
+ switch (ch1) { \
+ case 119127: \
+ switch (ch2) { \
+ case 119141: return 119134; \
+ } \
+ return 0; \
+ case 119128: \
+ switch (ch2) { \
+ case 119141: return 119135; \
+ } \
+ return 0; \
+ case 119135: \
+ switch (ch2) { \
+ case 119150: return 119136; \
+ case 119151: return 119137; \
+ case 119152: return 119138; \
+ case 119153: return 119139; \
+ case 119154: return 119140; \
+ } \
+ return 0; \
+ case 119225: \
+ switch (ch2) { \
+ case 119141: return 119227; \
+ } \
+ return 0; \
+ case 119226: \
+ switch (ch2) { \
+ case 119141: return 119228; \
+ } \
+ return 0; \
+ case 119227: \
+ switch (ch2) { \
+ case 119150: return 119229; \
+ case 119151: return 119231; \
+ } \
+ return 0; \
+ case 119228: \
+ switch (ch2) { \
+ case 119150: return 119230; \
+ case 119151: return 119232; \
+ } \
+ return 0; \
+ } \
+ return 0; \
+ } \
+ return 0;
+
+glui32 unigen_decomp_data[3247] = {
+ 0x41, 0x300, 0x41, 0x301, 0x41, 0x302, 0x41, 0x303,
+ 0x41, 0x308, 0x41, 0x30a, 0x43, 0x327, 0x45, 0x300,
+ 0x45, 0x301, 0x45, 0x302, 0x45, 0x308, 0x49, 0x300,
+ 0x49, 0x301, 0x49, 0x302, 0x49, 0x308, 0x4e, 0x303,
+ 0x4f, 0x300, 0x4f, 0x301, 0x4f, 0x302, 0x4f, 0x303,
+ 0x4f, 0x308, 0x55, 0x300, 0x55, 0x301, 0x55, 0x302,
+ 0x55, 0x308, 0x59, 0x301, 0x61, 0x300, 0x61, 0x301,
+ 0x61, 0x302, 0x61, 0x303, 0x61, 0x308, 0x61, 0x30a,
+ 0x63, 0x327, 0x65, 0x300, 0x65, 0x301, 0x65, 0x302,
+ 0x65, 0x308, 0x69, 0x300, 0x69, 0x301, 0x69, 0x302,
+ 0x69, 0x308, 0x6e, 0x303, 0x6f, 0x300, 0x6f, 0x301,
+ 0x6f, 0x302, 0x6f, 0x303, 0x6f, 0x308, 0x75, 0x300,
+ 0x75, 0x301, 0x75, 0x302, 0x75, 0x308, 0x79, 0x301,
+ 0x79, 0x308, 0x41, 0x304, 0x61, 0x304, 0x41, 0x306,
+ 0x61, 0x306, 0x41, 0x328, 0x61, 0x328, 0x43, 0x301,
+ 0x63, 0x301, 0x43, 0x302, 0x63, 0x302, 0x43, 0x307,
+ 0x63, 0x307, 0x43, 0x30c, 0x63, 0x30c, 0x44, 0x30c,
+ 0x64, 0x30c, 0x45, 0x304, 0x65, 0x304, 0x45, 0x306,
+ 0x65, 0x306, 0x45, 0x307, 0x65, 0x307, 0x45, 0x328,
+ 0x65, 0x328, 0x45, 0x30c, 0x65, 0x30c, 0x47, 0x302,
+ 0x67, 0x302, 0x47, 0x306, 0x67, 0x306, 0x47, 0x307,
+ 0x67, 0x307, 0x47, 0x327, 0x67, 0x327, 0x48, 0x302,
+ 0x68, 0x302, 0x49, 0x303, 0x69, 0x303, 0x49, 0x304,
+ 0x69, 0x304, 0x49, 0x306, 0x69, 0x306, 0x49, 0x328,
+ 0x69, 0x328, 0x49, 0x307, 0x4a, 0x302, 0x6a, 0x302,
+ 0x4b, 0x327, 0x6b, 0x327, 0x4c, 0x301, 0x6c, 0x301,
+ 0x4c, 0x327, 0x6c, 0x327, 0x4c, 0x30c, 0x6c, 0x30c,
+ 0x4e, 0x301, 0x6e, 0x301, 0x4e, 0x327, 0x6e, 0x327,
+ 0x4e, 0x30c, 0x6e, 0x30c, 0x4f, 0x304, 0x6f, 0x304,
+ 0x4f, 0x306, 0x6f, 0x306, 0x4f, 0x30b, 0x6f, 0x30b,
+ 0x52, 0x301, 0x72, 0x301, 0x52, 0x327, 0x72, 0x327,
+ 0x52, 0x30c, 0x72, 0x30c, 0x53, 0x301, 0x73, 0x301,
+ 0x53, 0x302, 0x73, 0x302, 0x53, 0x327, 0x73, 0x327,
+ 0x53, 0x30c, 0x73, 0x30c, 0x54, 0x327, 0x74, 0x327,
+ 0x54, 0x30c, 0x74, 0x30c, 0x55, 0x303, 0x75, 0x303,
+ 0x55, 0x304, 0x75, 0x304, 0x55, 0x306, 0x75, 0x306,
+ 0x55, 0x30a, 0x75, 0x30a, 0x55, 0x30b, 0x75, 0x30b,
+ 0x55, 0x328, 0x75, 0x328, 0x57, 0x302, 0x77, 0x302,
+ 0x59, 0x302, 0x79, 0x302, 0x59, 0x308, 0x5a, 0x301,
+ 0x7a, 0x301, 0x5a, 0x307, 0x7a, 0x307, 0x5a, 0x30c,
+ 0x7a, 0x30c, 0x4f, 0x31b, 0x6f, 0x31b, 0x55, 0x31b,
+ 0x75, 0x31b, 0x41, 0x30c, 0x61, 0x30c, 0x49, 0x30c,
+ 0x69, 0x30c, 0x4f, 0x30c, 0x6f, 0x30c, 0x55, 0x30c,
+ 0x75, 0x30c, 0x55, 0x308, 0x304, 0x75, 0x308, 0x304,
+ 0x55, 0x308, 0x301, 0x75, 0x308, 0x301, 0x55, 0x308,
+ 0x30c, 0x75, 0x308, 0x30c, 0x55, 0x308, 0x300, 0x75,
+ 0x308, 0x300, 0x41, 0x308, 0x304, 0x61, 0x308, 0x304,
+ 0x41, 0x307, 0x304, 0x61, 0x307, 0x304, 0xc6, 0x304,
+ 0xe6, 0x304, 0x47, 0x30c, 0x67, 0x30c, 0x4b, 0x30c,
+ 0x6b, 0x30c, 0x4f, 0x328, 0x6f, 0x328, 0x4f, 0x328,
+ 0x304, 0x6f, 0x328, 0x304, 0x1b7, 0x30c, 0x292, 0x30c,
+ 0x6a, 0x30c, 0x47, 0x301, 0x67, 0x301, 0x4e, 0x300,
+ 0x6e, 0x300, 0x41, 0x30a, 0x301, 0x61, 0x30a, 0x301,
+ 0xc6, 0x301, 0xe6, 0x301, 0xd8, 0x301, 0xf8, 0x301,
+ 0x41, 0x30f, 0x61, 0x30f, 0x41, 0x311, 0x61, 0x311,
+ 0x45, 0x30f, 0x65, 0x30f, 0x45, 0x311, 0x65, 0x311,
+ 0x49, 0x30f, 0x69, 0x30f, 0x49, 0x311, 0x69, 0x311,
+ 0x4f, 0x30f, 0x6f, 0x30f, 0x4f, 0x311, 0x6f, 0x311,
+ 0x52, 0x30f, 0x72, 0x30f, 0x52, 0x311, 0x72, 0x311,
+ 0x55, 0x30f, 0x75, 0x30f, 0x55, 0x311, 0x75, 0x311,
+ 0x53, 0x326, 0x73, 0x326, 0x54, 0x326, 0x74, 0x326,
+ 0x48, 0x30c, 0x68, 0x30c, 0x41, 0x307, 0x61, 0x307,
+ 0x45, 0x327, 0x65, 0x327, 0x4f, 0x308, 0x304, 0x6f,
+ 0x308, 0x304, 0x4f, 0x303, 0x304, 0x6f, 0x303, 0x304,
+ 0x4f, 0x307, 0x6f, 0x307, 0x4f, 0x307, 0x304, 0x6f,
+ 0x307, 0x304, 0x59, 0x304, 0x79, 0x304, 0x300, 0x301,
+ 0x313, 0x308, 0x301, 0x2b9, 0x3b, 0xa8, 0x301, 0x391,
+ 0x301, 0xb7, 0x395, 0x301, 0x397, 0x301, 0x399, 0x301,
+ 0x39f, 0x301, 0x3a5, 0x301, 0x3a9, 0x301, 0x3b9, 0x308,
+ 0x301, 0x399, 0x308, 0x3a5, 0x308, 0x3b1, 0x301, 0x3b5,
+ 0x301, 0x3b7, 0x301, 0x3b9, 0x301, 0x3c5, 0x308, 0x301,
+ 0x3b9, 0x308, 0x3c5, 0x308, 0x3bf, 0x301, 0x3c5, 0x301,
+ 0x3c9, 0x301, 0x3d2, 0x301, 0x3d2, 0x308, 0x415, 0x300,
+ 0x415, 0x308, 0x413, 0x301, 0x406, 0x308, 0x41a, 0x301,
+ 0x418, 0x300, 0x423, 0x306, 0x418, 0x306, 0x438, 0x306,
+ 0x435, 0x300, 0x435, 0x308, 0x433, 0x301, 0x456, 0x308,
+ 0x43a, 0x301, 0x438, 0x300, 0x443, 0x306, 0x474, 0x30f,
+ 0x475, 0x30f, 0x416, 0x306, 0x436, 0x306, 0x410, 0x306,
+ 0x430, 0x306, 0x410, 0x308, 0x430, 0x308, 0x415, 0x306,
+ 0x435, 0x306, 0x4d8, 0x308, 0x4d9, 0x308, 0x416, 0x308,
+ 0x436, 0x308, 0x417, 0x308, 0x437, 0x308, 0x418, 0x304,
+ 0x438, 0x304, 0x418, 0x308, 0x438, 0x308, 0x41e, 0x308,
+ 0x43e, 0x308, 0x4e8, 0x308, 0x4e9, 0x308, 0x42d, 0x308,
+ 0x44d, 0x308, 0x423, 0x304, 0x443, 0x304, 0x423, 0x308,
+ 0x443, 0x308, 0x423, 0x30b, 0x443, 0x30b, 0x427, 0x308,
+ 0x447, 0x308, 0x42b, 0x308, 0x44b, 0x308, 0x627, 0x653,
+ 0x627, 0x654, 0x648, 0x654, 0x627, 0x655, 0x64a, 0x654,
+ 0x6d5, 0x654, 0x6c1, 0x654, 0x6d2, 0x654, 0x928, 0x93c,
+ 0x930, 0x93c, 0x933, 0x93c, 0x915, 0x93c, 0x916, 0x93c,
+ 0x917, 0x93c, 0x91c, 0x93c, 0x921, 0x93c, 0x922, 0x93c,
+ 0x92b, 0x93c, 0x92f, 0x93c, 0x9c7, 0x9be, 0x9c7, 0x9d7,
+ 0x9a1, 0x9bc, 0x9a2, 0x9bc, 0x9af, 0x9bc, 0xa32, 0xa3c,
+ 0xa38, 0xa3c, 0xa16, 0xa3c, 0xa17, 0xa3c, 0xa1c, 0xa3c,
+ 0xa2b, 0xa3c, 0xb47, 0xb56, 0xb47, 0xb3e, 0xb47, 0xb57,
+ 0xb21, 0xb3c, 0xb22, 0xb3c, 0xb92, 0xbd7, 0xbc6, 0xbbe,
+ 0xbc7, 0xbbe, 0xbc6, 0xbd7, 0xc46, 0xc56, 0xcbf, 0xcd5,
+ 0xcc6, 0xcd5, 0xcc6, 0xcd6, 0xcc6, 0xcc2, 0xcc6, 0xcc2,
+ 0xcd5, 0xd46, 0xd3e, 0xd47, 0xd3e, 0xd46, 0xd57, 0xdd9,
+ 0xdca, 0xdd9, 0xdcf, 0xdd9, 0xdcf, 0xdca, 0xdd9, 0xddf,
+ 0xf42, 0xfb7, 0xf4c, 0xfb7, 0xf51, 0xfb7, 0xf56, 0xfb7,
+ 0xf5b, 0xfb7, 0xf40, 0xfb5, 0xf71, 0xf72, 0xf71, 0xf74,
+ 0xfb2, 0xf80, 0xfb3, 0xf80, 0xf71, 0xf80, 0xf92, 0xfb7,
+ 0xf9c, 0xfb7, 0xfa1, 0xfb7, 0xfa6, 0xfb7, 0xfab, 0xfb7,
+ 0xf90, 0xfb5, 0x1025, 0x102e, 0x41, 0x325, 0x61, 0x325,
+ 0x42, 0x307, 0x62, 0x307, 0x42, 0x323, 0x62, 0x323,
+ 0x42, 0x331, 0x62, 0x331, 0x43, 0x327, 0x301, 0x63,
+ 0x327, 0x301, 0x44, 0x307, 0x64, 0x307, 0x44, 0x323,
+ 0x64, 0x323, 0x44, 0x331, 0x64, 0x331, 0x44, 0x327,
+ 0x64, 0x327, 0x44, 0x32d, 0x64, 0x32d, 0x45, 0x304,
+ 0x300, 0x65, 0x304, 0x300, 0x45, 0x304, 0x301, 0x65,
+ 0x304, 0x301, 0x45, 0x32d, 0x65, 0x32d, 0x45, 0x330,
+ 0x65, 0x330, 0x45, 0x327, 0x306, 0x65, 0x327, 0x306,
+ 0x46, 0x307, 0x66, 0x307, 0x47, 0x304, 0x67, 0x304,
+ 0x48, 0x307, 0x68, 0x307, 0x48, 0x323, 0x68, 0x323,
+ 0x48, 0x308, 0x68, 0x308, 0x48, 0x327, 0x68, 0x327,
+ 0x48, 0x32e, 0x68, 0x32e, 0x49, 0x330, 0x69, 0x330,
+ 0x49, 0x308, 0x301, 0x69, 0x308, 0x301, 0x4b, 0x301,
+ 0x6b, 0x301, 0x4b, 0x323, 0x6b, 0x323, 0x4b, 0x331,
+ 0x6b, 0x331, 0x4c, 0x323, 0x6c, 0x323, 0x4c, 0x323,
+ 0x304, 0x6c, 0x323, 0x304, 0x4c, 0x331, 0x6c, 0x331,
+ 0x4c, 0x32d, 0x6c, 0x32d, 0x4d, 0x301, 0x6d, 0x301,
+ 0x4d, 0x307, 0x6d, 0x307, 0x4d, 0x323, 0x6d, 0x323,
+ 0x4e, 0x307, 0x6e, 0x307, 0x4e, 0x323, 0x6e, 0x323,
+ 0x4e, 0x331, 0x6e, 0x331, 0x4e, 0x32d, 0x6e, 0x32d,
+ 0x4f, 0x303, 0x301, 0x6f, 0x303, 0x301, 0x4f, 0x303,
+ 0x308, 0x6f, 0x303, 0x308, 0x4f, 0x304, 0x300, 0x6f,
+ 0x304, 0x300, 0x4f, 0x304, 0x301, 0x6f, 0x304, 0x301,
+ 0x50, 0x301, 0x70, 0x301, 0x50, 0x307, 0x70, 0x307,
+ 0x52, 0x307, 0x72, 0x307, 0x52, 0x323, 0x72, 0x323,
+ 0x52, 0x323, 0x304, 0x72, 0x323, 0x304, 0x52, 0x331,
+ 0x72, 0x331, 0x53, 0x307, 0x73, 0x307, 0x53, 0x323,
+ 0x73, 0x323, 0x53, 0x301, 0x307, 0x73, 0x301, 0x307,
+ 0x53, 0x30c, 0x307, 0x73, 0x30c, 0x307, 0x53, 0x323,
+ 0x307, 0x73, 0x323, 0x307, 0x54, 0x307, 0x74, 0x307,
+ 0x54, 0x323, 0x74, 0x323, 0x54, 0x331, 0x74, 0x331,
+ 0x54, 0x32d, 0x74, 0x32d, 0x55, 0x324, 0x75, 0x324,
+ 0x55, 0x330, 0x75, 0x330, 0x55, 0x32d, 0x75, 0x32d,
+ 0x55, 0x303, 0x301, 0x75, 0x303, 0x301, 0x55, 0x304,
+ 0x308, 0x75, 0x304, 0x308, 0x56, 0x303, 0x76, 0x303,
+ 0x56, 0x323, 0x76, 0x323, 0x57, 0x300, 0x77, 0x300,
+ 0x57, 0x301, 0x77, 0x301, 0x57, 0x308, 0x77, 0x308,
+ 0x57, 0x307, 0x77, 0x307, 0x57, 0x323, 0x77, 0x323,
+ 0x58, 0x307, 0x78, 0x307, 0x58, 0x308, 0x78, 0x308,
+ 0x59, 0x307, 0x79, 0x307, 0x5a, 0x302, 0x7a, 0x302,
+ 0x5a, 0x323, 0x7a, 0x323, 0x5a, 0x331, 0x7a, 0x331,
+ 0x68, 0x331, 0x74, 0x308, 0x77, 0x30a, 0x79, 0x30a,
+ 0x17f, 0x307, 0x41, 0x323, 0x61, 0x323, 0x41, 0x309,
+ 0x61, 0x309, 0x41, 0x302, 0x301, 0x61, 0x302, 0x301,
+ 0x41, 0x302, 0x300, 0x61, 0x302, 0x300, 0x41, 0x302,
+ 0x309, 0x61, 0x302, 0x309, 0x41, 0x302, 0x303, 0x61,
+ 0x302, 0x303, 0x41, 0x323, 0x302, 0x61, 0x323, 0x302,
+ 0x41, 0x306, 0x301, 0x61, 0x306, 0x301, 0x41, 0x306,
+ 0x300, 0x61, 0x306, 0x300, 0x41, 0x306, 0x309, 0x61,
+ 0x306, 0x309, 0x41, 0x306, 0x303, 0x61, 0x306, 0x303,
+ 0x41, 0x323, 0x306, 0x61, 0x323, 0x306, 0x45, 0x323,
+ 0x65, 0x323, 0x45, 0x309, 0x65, 0x309, 0x45, 0x303,
+ 0x65, 0x303, 0x45, 0x302, 0x301, 0x65, 0x302, 0x301,
+ 0x45, 0x302, 0x300, 0x65, 0x302, 0x300, 0x45, 0x302,
+ 0x309, 0x65, 0x302, 0x309, 0x45, 0x302, 0x303, 0x65,
+ 0x302, 0x303, 0x45, 0x323, 0x302, 0x65, 0x323, 0x302,
+ 0x49, 0x309, 0x69, 0x309, 0x49, 0x323, 0x69, 0x323,
+ 0x4f, 0x323, 0x6f, 0x323, 0x4f, 0x309, 0x6f, 0x309,
+ 0x4f, 0x302, 0x301, 0x6f, 0x302, 0x301, 0x4f, 0x302,
+ 0x300, 0x6f, 0x302, 0x300, 0x4f, 0x302, 0x309, 0x6f,
+ 0x302, 0x309, 0x4f, 0x302, 0x303, 0x6f, 0x302, 0x303,
+ 0x4f, 0x323, 0x302, 0x6f, 0x323, 0x302, 0x4f, 0x31b,
+ 0x301, 0x6f, 0x31b, 0x301, 0x4f, 0x31b, 0x300, 0x6f,
+ 0x31b, 0x300, 0x4f, 0x31b, 0x309, 0x6f, 0x31b, 0x309,
+ 0x4f, 0x31b, 0x303, 0x6f, 0x31b, 0x303, 0x4f, 0x31b,
+ 0x323, 0x6f, 0x31b, 0x323, 0x55, 0x323, 0x75, 0x323,
+ 0x55, 0x309, 0x75, 0x309, 0x55, 0x31b, 0x301, 0x75,
+ 0x31b, 0x301, 0x55, 0x31b, 0x300, 0x75, 0x31b, 0x300,
+ 0x55, 0x31b, 0x309, 0x75, 0x31b, 0x309, 0x55, 0x31b,
+ 0x303, 0x75, 0x31b, 0x303, 0x55, 0x31b, 0x323, 0x75,
+ 0x31b, 0x323, 0x59, 0x300, 0x79, 0x300, 0x59, 0x323,
+ 0x79, 0x323, 0x59, 0x309, 0x79, 0x309, 0x59, 0x303,
+ 0x79, 0x303, 0x3b1, 0x313, 0x3b1, 0x314, 0x3b1, 0x313,
+ 0x300, 0x3b1, 0x314, 0x300, 0x3b1, 0x313, 0x301, 0x3b1,
+ 0x314, 0x301, 0x3b1, 0x313, 0x342, 0x3b1, 0x314, 0x342,
+ 0x391, 0x313, 0x391, 0x314, 0x391, 0x313, 0x300, 0x391,
+ 0x314, 0x300, 0x391, 0x313, 0x301, 0x391, 0x314, 0x301,
+ 0x391, 0x313, 0x342, 0x391, 0x314, 0x342, 0x3b5, 0x313,
+ 0x3b5, 0x314, 0x3b5, 0x313, 0x300, 0x3b5, 0x314, 0x300,
+ 0x3b5, 0x313, 0x301, 0x3b5, 0x314, 0x301, 0x395, 0x313,
+ 0x395, 0x314, 0x395, 0x313, 0x300, 0x395, 0x314, 0x300,
+ 0x395, 0x313, 0x301, 0x395, 0x314, 0x301, 0x3b7, 0x313,
+ 0x3b7, 0x314, 0x3b7, 0x313, 0x300, 0x3b7, 0x314, 0x300,
+ 0x3b7, 0x313, 0x301, 0x3b7, 0x314, 0x301, 0x3b7, 0x313,
+ 0x342, 0x3b7, 0x314, 0x342, 0x397, 0x313, 0x397, 0x314,
+ 0x397, 0x313, 0x300, 0x397, 0x314, 0x300, 0x397, 0x313,
+ 0x301, 0x397, 0x314, 0x301, 0x397, 0x313, 0x342, 0x397,
+ 0x314, 0x342, 0x3b9, 0x313, 0x3b9, 0x314, 0x3b9, 0x313,
+ 0x300, 0x3b9, 0x314, 0x300, 0x3b9, 0x313, 0x301, 0x3b9,
+ 0x314, 0x301, 0x3b9, 0x313, 0x342, 0x3b9, 0x314, 0x342,
+ 0x399, 0x313, 0x399, 0x314, 0x399, 0x313, 0x300, 0x399,
+ 0x314, 0x300, 0x399, 0x313, 0x301, 0x399, 0x314, 0x301,
+ 0x399, 0x313, 0x342, 0x399, 0x314, 0x342, 0x3bf, 0x313,
+ 0x3bf, 0x314, 0x3bf, 0x313, 0x300, 0x3bf, 0x314, 0x300,
+ 0x3bf, 0x313, 0x301, 0x3bf, 0x314, 0x301, 0x39f, 0x313,
+ 0x39f, 0x314, 0x39f, 0x313, 0x300, 0x39f, 0x314, 0x300,
+ 0x39f, 0x313, 0x301, 0x39f, 0x314, 0x301, 0x3c5, 0x313,
+ 0x3c5, 0x314, 0x3c5, 0x313, 0x300, 0x3c5, 0x314, 0x300,
+ 0x3c5, 0x313, 0x301, 0x3c5, 0x314, 0x301, 0x3c5, 0x313,
+ 0x342, 0x3c5, 0x314, 0x342, 0x3a5, 0x314, 0x3a5, 0x314,
+ 0x300, 0x3a5, 0x314, 0x301, 0x3a5, 0x314, 0x342, 0x3c9,
+ 0x313, 0x3c9, 0x314, 0x3c9, 0x313, 0x300, 0x3c9, 0x314,
+ 0x300, 0x3c9, 0x313, 0x301, 0x3c9, 0x314, 0x301, 0x3c9,
+ 0x313, 0x342, 0x3c9, 0x314, 0x342, 0x3a9, 0x313, 0x3a9,
+ 0x314, 0x3a9, 0x313, 0x300, 0x3a9, 0x314, 0x300, 0x3a9,
+ 0x313, 0x301, 0x3a9, 0x314, 0x301, 0x3a9, 0x313, 0x342,
+ 0x3a9, 0x314, 0x342, 0x3b1, 0x300, 0x3b1, 0x301, 0x3b5,
+ 0x300, 0x3b5, 0x301, 0x3b7, 0x300, 0x3b7, 0x301, 0x3b9,
+ 0x300, 0x3b9, 0x301, 0x3bf, 0x300, 0x3bf, 0x301, 0x3c5,
+ 0x300, 0x3c5, 0x301, 0x3c9, 0x300, 0x3c9, 0x301, 0x3b1,
+ 0x313, 0x345, 0x3b1, 0x314, 0x345, 0x3b1, 0x313, 0x300,
+ 0x345, 0x3b1, 0x314, 0x300, 0x345, 0x3b1, 0x313, 0x301,
+ 0x345, 0x3b1, 0x314, 0x301, 0x345, 0x3b1, 0x313, 0x342,
+ 0x345, 0x3b1, 0x314, 0x342, 0x345, 0x391, 0x313, 0x345,
+ 0x391, 0x314, 0x345, 0x391, 0x313, 0x300, 0x345, 0x391,
+ 0x314, 0x300, 0x345, 0x391, 0x313, 0x301, 0x345, 0x391,
+ 0x314, 0x301, 0x345, 0x391, 0x313, 0x342, 0x345, 0x391,
+ 0x314, 0x342, 0x345, 0x3b7, 0x313, 0x345, 0x3b7, 0x314,
+ 0x345, 0x3b7, 0x313, 0x300, 0x345, 0x3b7, 0x314, 0x300,
+ 0x345, 0x3b7, 0x313, 0x301, 0x345, 0x3b7, 0x314, 0x301,
+ 0x345, 0x3b7, 0x313, 0x342, 0x345, 0x3b7, 0x314, 0x342,
+ 0x345, 0x397, 0x313, 0x345, 0x397, 0x314, 0x345, 0x397,
+ 0x313, 0x300, 0x345, 0x397, 0x314, 0x300, 0x345, 0x397,
+ 0x313, 0x301, 0x345, 0x397, 0x314, 0x301, 0x345, 0x397,
+ 0x313, 0x342, 0x345, 0x397, 0x314, 0x342, 0x345, 0x3c9,
+ 0x313, 0x345, 0x3c9, 0x314, 0x345, 0x3c9, 0x313, 0x300,
+ 0x345, 0x3c9, 0x314, 0x300, 0x345, 0x3c9, 0x313, 0x301,
+ 0x345, 0x3c9, 0x314, 0x301, 0x345, 0x3c9, 0x313, 0x342,
+ 0x345, 0x3c9, 0x314, 0x342, 0x345, 0x3a9, 0x313, 0x345,
+ 0x3a9, 0x314, 0x345, 0x3a9, 0x313, 0x300, 0x345, 0x3a9,
+ 0x314, 0x300, 0x345, 0x3a9, 0x313, 0x301, 0x345, 0x3a9,
+ 0x314, 0x301, 0x345, 0x3a9, 0x313, 0x342, 0x345, 0x3a9,
+ 0x314, 0x342, 0x345, 0x3b1, 0x306, 0x3b1, 0x304, 0x3b1,
+ 0x300, 0x345, 0x3b1, 0x345, 0x3b1, 0x301, 0x345, 0x3b1,
+ 0x342, 0x3b1, 0x342, 0x345, 0x391, 0x306, 0x391, 0x304,
+ 0x391, 0x300, 0x391, 0x301, 0x391, 0x345, 0x3b9, 0xa8,
+ 0x342, 0x3b7, 0x300, 0x345, 0x3b7, 0x345, 0x3b7, 0x301,
+ 0x345, 0x3b7, 0x342, 0x3b7, 0x342, 0x345, 0x395, 0x300,
+ 0x395, 0x301, 0x397, 0x300, 0x397, 0x301, 0x397, 0x345,
+ 0x1fbf, 0x300, 0x1fbf, 0x301, 0x1fbf, 0x342, 0x3b9, 0x306,
+ 0x3b9, 0x304, 0x3b9, 0x308, 0x300, 0x3b9, 0x308, 0x301,
+ 0x3b9, 0x342, 0x3b9, 0x308, 0x342, 0x399, 0x306, 0x399,
+ 0x304, 0x399, 0x300, 0x399, 0x301, 0x1ffe, 0x300, 0x1ffe,
+ 0x301, 0x1ffe, 0x342, 0x3c5, 0x306, 0x3c5, 0x304, 0x3c5,
+ 0x308, 0x300, 0x3c5, 0x308, 0x301, 0x3c1, 0x313, 0x3c1,
+ 0x314, 0x3c5, 0x342, 0x3c5, 0x308, 0x342, 0x3a5, 0x306,
+ 0x3a5, 0x304, 0x3a5, 0x300, 0x3a5, 0x301, 0x3a1, 0x314,
+ 0xa8, 0x300, 0xa8, 0x301, 0x60, 0x3c9, 0x300, 0x345,
+ 0x3c9, 0x345, 0x3c9, 0x301, 0x345, 0x3c9, 0x342, 0x3c9,
+ 0x342, 0x345, 0x39f, 0x300, 0x39f, 0x301, 0x3a9, 0x300,
+ 0x3a9, 0x301, 0x3a9, 0x345, 0xb4, 0x2002, 0x2003, 0x3a9,
+ 0x4b, 0x41, 0x30a, 0x2190, 0x338, 0x2192, 0x338, 0x2194,
+ 0x338, 0x21d0, 0x338, 0x21d4, 0x338, 0x21d2, 0x338, 0x2203,
+ 0x338, 0x2208, 0x338, 0x220b, 0x338, 0x2223, 0x338, 0x2225,
+ 0x338, 0x223c, 0x338, 0x2243, 0x338, 0x2245, 0x338, 0x2248,
+ 0x338, 0x3d, 0x338, 0x2261, 0x338, 0x224d, 0x338, 0x3c,
+ 0x338, 0x3e, 0x338, 0x2264, 0x338, 0x2265, 0x338, 0x2272,
+ 0x338, 0x2273, 0x338, 0x2276, 0x338, 0x2277, 0x338, 0x227a,
+ 0x338, 0x227b, 0x338, 0x2282, 0x338, 0x2283, 0x338, 0x2286,
+ 0x338, 0x2287, 0x338, 0x22a2, 0x338, 0x22a8, 0x338, 0x22a9,
+ 0x338, 0x22ab, 0x338, 0x227c, 0x338, 0x227d, 0x338, 0x2291,
+ 0x338, 0x2292, 0x338, 0x22b2, 0x338, 0x22b3, 0x338, 0x22b4,
+ 0x338, 0x22b5, 0x338, 0x3008, 0x3009, 0x2add, 0x338, 0x304b,
+ 0x3099, 0x304d, 0x3099, 0x304f, 0x3099, 0x3051, 0x3099, 0x3053,
+ 0x3099, 0x3055, 0x3099, 0x3057, 0x3099, 0x3059, 0x3099, 0x305b,
+ 0x3099, 0x305d, 0x3099, 0x305f, 0x3099, 0x3061, 0x3099, 0x3064,
+ 0x3099, 0x3066, 0x3099, 0x3068, 0x3099, 0x306f, 0x3099, 0x306f,
+ 0x309a, 0x3072, 0x3099, 0x3072, 0x309a, 0x3075, 0x3099, 0x3075,
+ 0x309a, 0x3078, 0x3099, 0x3078, 0x309a, 0x307b, 0x3099, 0x307b,
+ 0x309a, 0x3046, 0x3099, 0x309d, 0x3099, 0x30ab, 0x3099, 0x30ad,
+ 0x3099, 0x30af, 0x3099, 0x30b1, 0x3099, 0x30b3, 0x3099, 0x30b5,
+ 0x3099, 0x30b7, 0x3099, 0x30b9, 0x3099, 0x30bb, 0x3099, 0x30bd,
+ 0x3099, 0x30bf, 0x3099, 0x30c1, 0x3099, 0x30c4, 0x3099, 0x30c6,
+ 0x3099, 0x30c8, 0x3099, 0x30cf, 0x3099, 0x30cf, 0x309a, 0x30d2,
+ 0x3099, 0x30d2, 0x309a, 0x30d5, 0x3099, 0x30d5, 0x309a, 0x30d8,
+ 0x3099, 0x30d8, 0x309a, 0x30db, 0x3099, 0x30db, 0x309a, 0x30a6,
+ 0x3099, 0x30ef, 0x3099, 0x30f0, 0x3099, 0x30f1, 0x3099, 0x30f2,
+ 0x3099, 0x30fd, 0x3099, 0x8c48, 0x66f4, 0x8eca, 0x8cc8, 0x6ed1,
+ 0x4e32, 0x53e5, 0x9f9c, 0x9f9c, 0x5951, 0x91d1, 0x5587, 0x5948,
+ 0x61f6, 0x7669, 0x7f85, 0x863f, 0x87ba, 0x88f8, 0x908f, 0x6a02,
+ 0x6d1b, 0x70d9, 0x73de, 0x843d, 0x916a, 0x99f1, 0x4e82, 0x5375,
+ 0x6b04, 0x721b, 0x862d, 0x9e1e, 0x5d50, 0x6feb, 0x85cd, 0x8964,
+ 0x62c9, 0x81d8, 0x881f, 0x5eca, 0x6717, 0x6d6a, 0x72fc, 0x90ce,
+ 0x4f86, 0x51b7, 0x52de, 0x64c4, 0x6ad3, 0x7210, 0x76e7, 0x8001,
+ 0x8606, 0x865c, 0x8def, 0x9732, 0x9b6f, 0x9dfa, 0x788c, 0x797f,
+ 0x7da0, 0x83c9, 0x9304, 0x9e7f, 0x8ad6, 0x58df, 0x5f04, 0x7c60,
+ 0x807e, 0x7262, 0x78ca, 0x8cc2, 0x96f7, 0x58d8, 0x5c62, 0x6a13,
+ 0x6dda, 0x6f0f, 0x7d2f, 0x7e37, 0x964b, 0x52d2, 0x808b, 0x51dc,
+ 0x51cc, 0x7a1c, 0x7dbe, 0x83f1, 0x9675, 0x8b80, 0x62cf, 0x6a02,
+ 0x8afe, 0x4e39, 0x5be7, 0x6012, 0x7387, 0x7570, 0x5317, 0x78fb,
+ 0x4fbf, 0x5fa9, 0x4e0d, 0x6ccc, 0x6578, 0x7d22, 0x53c3, 0x585e,
+ 0x7701, 0x8449, 0x8aaa, 0x6bba, 0x8fb0, 0x6c88, 0x62fe, 0x82e5,
+ 0x63a0, 0x7565, 0x4eae, 0x5169, 0x51c9, 0x6881, 0x7ce7, 0x826f,
+ 0x8ad2, 0x91cf, 0x52f5, 0x5442, 0x5973, 0x5eec, 0x65c5, 0x6ffe,
+ 0x792a, 0x95ad, 0x9a6a, 0x9e97, 0x9ece, 0x529b, 0x66c6, 0x6b77,
+ 0x8f62, 0x5e74, 0x6190, 0x6200, 0x649a, 0x6f23, 0x7149, 0x7489,
+ 0x79ca, 0x7df4, 0x806f, 0x8f26, 0x84ee, 0x9023, 0x934a, 0x5217,
+ 0x52a3, 0x54bd, 0x70c8, 0x88c2, 0x8aaa, 0x5ec9, 0x5ff5, 0x637b,
+ 0x6bae, 0x7c3e, 0x7375, 0x4ee4, 0x56f9, 0x5be7, 0x5dba, 0x601c,
+ 0x73b2, 0x7469, 0x7f9a, 0x8046, 0x9234, 0x96f6, 0x9748, 0x9818,
+ 0x4f8b, 0x79ae, 0x91b4, 0x96b8, 0x60e1, 0x4e86, 0x50da, 0x5bee,
+ 0x5c3f, 0x6599, 0x6a02, 0x71ce, 0x7642, 0x84fc, 0x907c, 0x9f8d,
+ 0x6688, 0x962e, 0x5289, 0x677b, 0x67f3, 0x6d41, 0x6e9c, 0x7409,
+ 0x7559, 0x786b, 0x7d10, 0x985e, 0x516d, 0x622e, 0x9678, 0x502b,
+ 0x5d19, 0x6dea, 0x8f2a, 0x5f8b, 0x6144, 0x6817, 0x7387, 0x9686,
+ 0x5229, 0x540f, 0x5c65, 0x6613, 0x674e, 0x68a8, 0x6ce5, 0x7406,
+ 0x75e2, 0x7f79, 0x88cf, 0x88e1, 0x91cc, 0x96e2, 0x533f, 0x6eba,
+ 0x541d, 0x71d0, 0x7498, 0x85fa, 0x96a3, 0x9c57, 0x9e9f, 0x6797,
+ 0x6dcb, 0x81e8, 0x7acb, 0x7b20, 0x7c92, 0x72c0, 0x7099, 0x8b58,
+ 0x4ec0, 0x8336, 0x523a, 0x5207, 0x5ea6, 0x62d3, 0x7cd6, 0x5b85,
+ 0x6d1e, 0x66b4, 0x8f3b, 0x884c, 0x964d, 0x898b, 0x5ed3, 0x5140,
+ 0x55c0, 0x585a, 0x6674, 0x51de, 0x732a, 0x76ca, 0x793c, 0x795e,
+ 0x7965, 0x798f, 0x9756, 0x7cbe, 0x7fbd, 0x8612, 0x8af8, 0x9038,
+ 0x90fd, 0x98ef, 0x98fc, 0x9928, 0x9db4, 0x4fae, 0x50e7, 0x514d,
+ 0x52c9, 0x52e4, 0x5351, 0x559d, 0x5606, 0x5668, 0x5840, 0x58a8,
+ 0x5c64, 0x5c6e, 0x6094, 0x6168, 0x618e, 0x61f2, 0x654f, 0x65e2,
+ 0x6691, 0x6885, 0x6d77, 0x6e1a, 0x6f22, 0x716e, 0x722b, 0x7422,
+ 0x7891, 0x793e, 0x7949, 0x7948, 0x7950, 0x7956, 0x795d, 0x798d,
+ 0x798e, 0x7a40, 0x7a81, 0x7bc0, 0x7df4, 0x7e09, 0x7e41, 0x7f72,
+ 0x8005, 0x81ed, 0x8279, 0x8279, 0x8457, 0x8910, 0x8996, 0x8b01,
+ 0x8b39, 0x8cd3, 0x8d08, 0x8fb6, 0x9038, 0x96e3, 0x97ff, 0x983b,
+ 0x5d9, 0x5b4, 0x5f2, 0x5b7, 0x5e9, 0x5c1, 0x5e9, 0x5c2,
+ 0x5e9, 0x5bc, 0x5c1, 0x5e9, 0x5bc, 0x5c2, 0x5d0, 0x5b7,
+ 0x5d0, 0x5b8, 0x5d0, 0x5bc, 0x5d1, 0x5bc, 0x5d2, 0x5bc,
+ 0x5d3, 0x5bc, 0x5d4, 0x5bc, 0x5d5, 0x5bc, 0x5d6, 0x5bc,
+ 0x5d8, 0x5bc, 0x5d9, 0x5bc, 0x5da, 0x5bc, 0x5db, 0x5bc,
+ 0x5dc, 0x5bc, 0x5de, 0x5bc, 0x5e0, 0x5bc, 0x5e1, 0x5bc,
+ 0x5e3, 0x5bc, 0x5e4, 0x5bc, 0x5e6, 0x5bc, 0x5e7, 0x5bc,
+ 0x5e8, 0x5bc, 0x5e9, 0x5bc, 0x5ea, 0x5bc, 0x5d5, 0x5b9,
+ 0x5d1, 0x5bf, 0x5db, 0x5bf, 0x5e4, 0x5bf, 0x1d157, 0x1d165,
+ 0x1d158, 0x1d165, 0x1d158, 0x1d165, 0x1d16e, 0x1d158, 0x1d165, 0x1d16f,
+ 0x1d158, 0x1d165, 0x1d170, 0x1d158, 0x1d165, 0x1d171, 0x1d158, 0x1d165,
+ 0x1d172, 0x1d1b9, 0x1d165, 0x1d1ba, 0x1d165, 0x1d1b9, 0x1d165, 0x1d16e,
+ 0x1d1ba, 0x1d165, 0x1d16e, 0x1d1b9, 0x1d165, 0x1d16f, 0x1d1ba, 0x1d165,
+ 0x1d16f, 0x4e3d, 0x4e38, 0x4e41, 0x20122, 0x4f60, 0x4fae, 0x4fbb,
+ 0x5002, 0x507a, 0x5099, 0x50e7, 0x50cf, 0x349e, 0x2063a, 0x514d,
+ 0x5154, 0x5164, 0x5177, 0x2051c, 0x34b9, 0x5167, 0x518d, 0x2054b,
+ 0x5197, 0x51a4, 0x4ecc, 0x51ac, 0x51b5, 0x291df, 0x51f5, 0x5203,
+ 0x34df, 0x523b, 0x5246, 0x5272, 0x5277, 0x3515, 0x52c7, 0x52c9,
+ 0x52e4, 0x52fa, 0x5305, 0x5306, 0x5317, 0x5349, 0x5351, 0x535a,
+ 0x5373, 0x537d, 0x537f, 0x537f, 0x537f, 0x20a2c, 0x7070, 0x53ca,
+ 0x53df, 0x20b63, 0x53eb, 0x53f1, 0x5406, 0x549e, 0x5438, 0x5448,
+ 0x5468, 0x54a2, 0x54f6, 0x5510, 0x5553, 0x5563, 0x5584, 0x5584,
+ 0x5599, 0x55ab, 0x55b3, 0x55c2, 0x5716, 0x5606, 0x5717, 0x5651,
+ 0x5674, 0x5207, 0x58ee, 0x57ce, 0x57f4, 0x580d, 0x578b, 0x5832,
+ 0x5831, 0x58ac, 0x214e4, 0x58f2, 0x58f7, 0x5906, 0x591a, 0x5922,
+ 0x5962, 0x216a8, 0x216ea, 0x59ec, 0x5a1b, 0x5a27, 0x59d8, 0x5a66,
+ 0x36ee, 0x36fc, 0x5b08, 0x5b3e, 0x5b3e, 0x219c8, 0x5bc3, 0x5bd8,
+ 0x5be7, 0x5bf3, 0x21b18, 0x5bff, 0x5c06, 0x5f53, 0x5c22, 0x3781,
+ 0x5c60, 0x5c6e, 0x5cc0, 0x5c8d, 0x21de4, 0x5d43, 0x21de6, 0x5d6e,
+ 0x5d6b, 0x5d7c, 0x5de1, 0x5de2, 0x382f, 0x5dfd, 0x5e28, 0x5e3d,
+ 0x5e69, 0x3862, 0x22183, 0x387c, 0x5eb0, 0x5eb3, 0x5eb6, 0x5eca,
+ 0x2a392, 0x5efe, 0x22331, 0x22331, 0x8201, 0x5f22, 0x5f22, 0x38c7,
+ 0x232b8, 0x261da, 0x5f62, 0x5f6b, 0x38e3, 0x5f9a, 0x5fcd, 0x5fd7,
+ 0x5ff9, 0x6081, 0x393a, 0x391c, 0x6094, 0x226d4, 0x60c7, 0x6148,
+ 0x614c, 0x614e, 0x614c, 0x617a, 0x618e, 0x61b2, 0x61a4, 0x61af,
+ 0x61de, 0x61f2, 0x61f6, 0x6210, 0x621b, 0x625d, 0x62b1, 0x62d4,
+ 0x6350, 0x22b0c, 0x633d, 0x62fc, 0x6368, 0x6383, 0x63e4, 0x22bf1,
+ 0x6422, 0x63c5, 0x63a9, 0x3a2e, 0x6469, 0x647e, 0x649d, 0x6477,
+ 0x3a6c, 0x654f, 0x656c, 0x2300a, 0x65e3, 0x66f8, 0x6649, 0x3b19,
+ 0x6691, 0x3b08, 0x3ae4, 0x5192, 0x5195, 0x6700, 0x669c, 0x80ad,
+ 0x43d9, 0x6717, 0x671b, 0x6721, 0x675e, 0x6753, 0x233c3, 0x3b49,
+ 0x67fa, 0x6785, 0x6852, 0x6885, 0x2346d, 0x688e, 0x681f, 0x6914,
+ 0x3b9d, 0x6942, 0x69a3, 0x69ea, 0x6aa8, 0x236a3, 0x6adb, 0x3c18,
+ 0x6b21, 0x238a7, 0x6b54, 0x3c4e, 0x6b72, 0x6b9f, 0x6bba, 0x6bbb,
+ 0x23a8d, 0x21d0b, 0x23afa, 0x6c4e, 0x23cbc, 0x6cbf, 0x6ccd, 0x6c67,
+ 0x6d16, 0x6d3e, 0x6d77, 0x6d41, 0x6d69, 0x6d78, 0x6d85, 0x23d1e,
+ 0x6d34, 0x6e2f, 0x6e6e, 0x3d33, 0x6ecb, 0x6ec7, 0x23ed1, 0x6df9,
+ 0x6f6e, 0x23f5e, 0x23f8e, 0x6fc6, 0x7039, 0x701e, 0x701b, 0x3d96,
+ 0x704a, 0x707d, 0x7077, 0x70ad, 0x20525, 0x7145, 0x24263, 0x719c,
+ 0x243ab, 0x7228, 0x7235, 0x7250, 0x24608, 0x7280, 0x7295, 0x24735,
+ 0x24814, 0x737a, 0x738b, 0x3eac, 0x73a5, 0x3eb8, 0x3eb8, 0x7447,
+ 0x745c, 0x7471, 0x7485, 0x74ca, 0x3f1b, 0x7524, 0x24c36, 0x753e,
+ 0x24c92, 0x7570, 0x2219f, 0x7610, 0x24fa1, 0x24fb8, 0x25044, 0x3ffc,
+ 0x4008, 0x76f4, 0x250f3, 0x250f2, 0x25119, 0x25133, 0x771e, 0x771f,
+ 0x771f, 0x774a, 0x4039, 0x778b, 0x4046, 0x4096, 0x2541d, 0x784e,
+ 0x788c, 0x78cc, 0x40e3, 0x25626, 0x7956, 0x2569a, 0x256c5, 0x798f,
+ 0x79eb, 0x412f, 0x7a40, 0x7a4a, 0x7a4f, 0x2597c, 0x25aa7, 0x25aa7,
+ 0x7aee, 0x4202, 0x25bab, 0x7bc6, 0x7bc9, 0x4227, 0x25c80, 0x7cd2,
+ 0x42a0, 0x7ce8, 0x7ce3, 0x7d00, 0x25f86, 0x7d63, 0x4301, 0x7dc7,
+ 0x7e02, 0x7e45, 0x4334, 0x26228, 0x26247, 0x4359, 0x262d9, 0x7f7a,
+ 0x2633e, 0x7f95, 0x7ffa, 0x8005, 0x264da, 0x26523, 0x8060, 0x265a8,
+ 0x8070, 0x2335f, 0x43d5, 0x80b2, 0x8103, 0x440b, 0x813e, 0x5ab5,
+ 0x267a7, 0x267b5, 0x23393, 0x2339c, 0x8201, 0x8204, 0x8f9e, 0x446b,
+ 0x8291, 0x828b, 0x829d, 0x52b3, 0x82b1, 0x82b3, 0x82bd, 0x82e6,
+ 0x26b3c, 0x82e5, 0x831d, 0x8363, 0x83ad, 0x8323, 0x83bd, 0x83e7,
+ 0x8457, 0x8353, 0x83ca, 0x83cc, 0x83dc, 0x26c36, 0x26d6b, 0x26cd5,
+ 0x452b, 0x84f1, 0x84f3, 0x8516, 0x273ca, 0x8564, 0x26f2c, 0x455d,
+ 0x4561, 0x26fb1, 0x270d2, 0x456b, 0x8650, 0x865c, 0x8667, 0x8669,
+ 0x86a9, 0x8688, 0x870e, 0x86e2, 0x8779, 0x8728, 0x876b, 0x8786,
+ 0x45d7, 0x87e1, 0x8801, 0x45f9, 0x8860, 0x8863, 0x27667, 0x88d7,
+ 0x88de, 0x4635, 0x88fa, 0x34bb, 0x278ae, 0x27966, 0x46be, 0x46c7,
+ 0x8aa0, 0x8aed, 0x8b8a, 0x8c55, 0x27ca8, 0x8cab, 0x8cc1, 0x8d1b,
+ 0x8d77, 0x27f2f, 0x20804, 0x8dcb, 0x8dbc, 0x8df0, 0x208de, 0x8ed4,
+ 0x8f38, 0x285d2, 0x285ed, 0x9094, 0x90f1, 0x9111, 0x2872e, 0x911b,
+ 0x9238, 0x92d7, 0x92d8, 0x927c, 0x93f9, 0x9415, 0x28bfa, 0x958b,
+ 0x4995, 0x95b7, 0x28d77, 0x49e6, 0x96c3, 0x5db2, 0x9723, 0x29145,
+ 0x2921a, 0x4a6e, 0x4a76, 0x97e0, 0x2940a, 0x4ab2, 0x29496, 0x980b,
+ 0x980b, 0x9829, 0x295b6, 0x98e2, 0x4b33, 0x9929, 0x99a7, 0x99c2,
+ 0x99fe, 0x4bce, 0x29b30, 0x9b12, 0x9c40, 0x9cfd, 0x4cce, 0x4ced,
+ 0x9d67, 0x2a0ce, 0x4cf8, 0x2a105, 0x2a20e, 0x2a291, 0x9ebb, 0x4d56,
+ 0x9ef9, 0x9efe, 0x9f05, 0x9f0f, 0x9f16, 0x9f3b, 0x2a600,
+};
+
+gli_decomp_block_t unigen_decomp_block_0x0[256] = {
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 0 },
+ { 2, 2 },
+ { 2, 4 },
+ { 2, 6 },
+ { 2, 8 },
+ { 2, 10 },
+ { 0, 0 },
+ { 2, 12 },
+ { 2, 14 },
+ { 2, 16 },
+ { 2, 18 },
+ { 2, 20 },
+ { 2, 22 },
+ { 2, 24 },
+ { 2, 26 },
+ { 2, 28 },
+ { 0, 0 },
+ { 2, 30 },
+ { 2, 32 },
+ { 2, 34 },
+ { 2, 36 },
+ { 2, 38 },
+ { 2, 40 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 42 },
+ { 2, 44 },
+ { 2, 46 },
+ { 2, 48 },
+ { 2, 50 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 52 },
+ { 2, 54 },
+ { 2, 56 },
+ { 2, 58 },
+ { 2, 60 },
+ { 2, 62 },
+ { 0, 0 },
+ { 2, 64 },
+ { 2, 66 },
+ { 2, 68 },
+ { 2, 70 },
+ { 2, 72 },
+ { 2, 74 },
+ { 2, 76 },
+ { 2, 78 },
+ { 2, 80 },
+ { 0, 0 },
+ { 2, 82 },
+ { 2, 84 },
+ { 2, 86 },
+ { 2, 88 },
+ { 2, 90 },
+ { 2, 92 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 94 },
+ { 2, 96 },
+ { 2, 98 },
+ { 2, 100 },
+ { 2, 102 },
+ { 0, 0 },
+ { 2, 104 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x1[256] = {
+ { 2, 106 },
+ { 2, 108 },
+ { 2, 110 },
+ { 2, 112 },
+ { 2, 114 },
+ { 2, 116 },
+ { 2, 118 },
+ { 2, 120 },
+ { 2, 122 },
+ { 2, 124 },
+ { 2, 126 },
+ { 2, 128 },
+ { 2, 130 },
+ { 2, 132 },
+ { 2, 134 },
+ { 2, 136 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 138 },
+ { 2, 140 },
+ { 2, 142 },
+ { 2, 144 },
+ { 2, 146 },
+ { 2, 148 },
+ { 2, 150 },
+ { 2, 152 },
+ { 2, 154 },
+ { 2, 156 },
+ { 2, 158 },
+ { 2, 160 },
+ { 2, 162 },
+ { 2, 164 },
+ { 2, 166 },
+ { 2, 168 },
+ { 2, 170 },
+ { 2, 172 },
+ { 2, 174 },
+ { 2, 176 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 178 },
+ { 2, 180 },
+ { 2, 182 },
+ { 2, 184 },
+ { 2, 186 },
+ { 2, 188 },
+ { 2, 190 },
+ { 2, 192 },
+ { 2, 194 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 196 },
+ { 2, 198 },
+ { 2, 200 },
+ { 2, 202 },
+ { 0, 0 },
+ { 2, 204 },
+ { 2, 206 },
+ { 2, 208 },
+ { 2, 210 },
+ { 2, 212 },
+ { 2, 214 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 216 },
+ { 2, 218 },
+ { 2, 220 },
+ { 2, 222 },
+ { 2, 224 },
+ { 2, 226 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 228 },
+ { 2, 230 },
+ { 2, 232 },
+ { 2, 234 },
+ { 2, 236 },
+ { 2, 238 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 240 },
+ { 2, 242 },
+ { 2, 244 },
+ { 2, 246 },
+ { 2, 248 },
+ { 2, 250 },
+ { 2, 252 },
+ { 2, 254 },
+ { 2, 256 },
+ { 2, 258 },
+ { 2, 260 },
+ { 2, 262 },
+ { 2, 264 },
+ { 2, 266 },
+ { 2, 268 },
+ { 2, 270 },
+ { 2, 272 },
+ { 2, 274 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 276 },
+ { 2, 278 },
+ { 2, 280 },
+ { 2, 282 },
+ { 2, 284 },
+ { 2, 286 },
+ { 2, 288 },
+ { 2, 290 },
+ { 2, 292 },
+ { 2, 294 },
+ { 2, 296 },
+ { 2, 298 },
+ { 2, 300 },
+ { 2, 302 },
+ { 2, 304 },
+ { 2, 306 },
+ { 2, 308 },
+ { 2, 310 },
+ { 2, 312 },
+ { 2, 314 },
+ { 2, 316 },
+ { 2, 318 },
+ { 2, 320 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 322 },
+ { 2, 324 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 326 },
+ { 2, 328 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 330 },
+ { 2, 332 },
+ { 2, 334 },
+ { 2, 336 },
+ { 2, 338 },
+ { 2, 340 },
+ { 2, 342 },
+ { 2, 344 },
+ { 3, 346 },
+ { 3, 349 },
+ { 3, 352 },
+ { 3, 355 },
+ { 3, 358 },
+ { 3, 361 },
+ { 3, 364 },
+ { 3, 367 },
+ { 0, 0 },
+ { 3, 370 },
+ { 3, 373 },
+ { 3, 376 },
+ { 3, 379 },
+ { 2, 382 },
+ { 2, 384 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 386 },
+ { 2, 388 },
+ { 2, 390 },
+ { 2, 392 },
+ { 2, 394 },
+ { 2, 396 },
+ { 3, 398 },
+ { 3, 401 },
+ { 2, 404 },
+ { 2, 406 },
+ { 2, 408 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 410 },
+ { 2, 412 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 414 },
+ { 2, 416 },
+ { 3, 418 },
+ { 3, 421 },
+ { 2, 424 },
+ { 2, 426 },
+ { 2, 428 },
+ { 2, 430 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x2[256] = {
+ { 2, 432 },
+ { 2, 434 },
+ { 2, 436 },
+ { 2, 438 },
+ { 2, 440 },
+ { 2, 442 },
+ { 2, 444 },
+ { 2, 446 },
+ { 2, 448 },
+ { 2, 450 },
+ { 2, 452 },
+ { 2, 454 },
+ { 2, 456 },
+ { 2, 458 },
+ { 2, 460 },
+ { 2, 462 },
+ { 2, 464 },
+ { 2, 466 },
+ { 2, 468 },
+ { 2, 470 },
+ { 2, 472 },
+ { 2, 474 },
+ { 2, 476 },
+ { 2, 478 },
+ { 2, 480 },
+ { 2, 482 },
+ { 2, 484 },
+ { 2, 486 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 488 },
+ { 2, 490 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 492 },
+ { 2, 494 },
+ { 2, 496 },
+ { 2, 498 },
+ { 3, 500 },
+ { 3, 503 },
+ { 3, 506 },
+ { 3, 509 },
+ { 2, 512 },
+ { 2, 514 },
+ { 3, 516 },
+ { 3, 519 },
+ { 2, 522 },
+ { 2, 524 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x3[256] = {
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 1, 526 },
+ { 1, 527 },
+ { 0, 0 },
+ { 1, 528 },
+ { 2, 529 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 1, 531 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 1, 532 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 533 },
+ { 2, 535 },
+ { 1, 537 },
+ { 2, 538 },
+ { 2, 540 },
+ { 2, 542 },
+ { 0, 0 },
+ { 2, 544 },
+ { 0, 0 },
+ { 2, 546 },
+ { 2, 548 },
+ { 3, 550 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 553 },
+ { 2, 555 },
+ { 2, 557 },
+ { 2, 559 },
+ { 2, 561 },
+ { 2, 563 },
+ { 3, 565 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 568 },
+ { 2, 570 },
+ { 2, 572 },
+ { 2, 574 },
+ { 2, 576 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 578 },
+ { 2, 580 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x4[256] = {
+ { 2, 582 },
+ { 2, 584 },
+ { 0, 0 },
+ { 2, 586 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 588 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 590 },
+ { 2, 592 },
+ { 2, 594 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 596 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 598 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 600 },
+ { 2, 602 },
+ { 0, 0 },
+ { 2, 604 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 606 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 608 },
+ { 2, 610 },
+ { 2, 612 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 614 },
+ { 2, 616 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 618 },
+ { 2, 620 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 622 },
+ { 2, 624 },
+ { 2, 626 },
+ { 2, 628 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 630 },
+ { 2, 632 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 634 },
+ { 2, 636 },
+ { 2, 638 },
+ { 2, 640 },
+ { 2, 642 },
+ { 2, 644 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 646 },
+ { 2, 648 },
+ { 2, 650 },
+ { 2, 652 },
+ { 2, 654 },
+ { 2, 656 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 658 },
+ { 2, 660 },
+ { 2, 662 },
+ { 2, 664 },
+ { 2, 666 },
+ { 2, 668 },
+ { 2, 670 },
+ { 2, 672 },
+ { 2, 674 },
+ { 2, 676 },
+ { 2, 678 },
+ { 2, 680 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 682 },
+ { 2, 684 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x1e[256] = {
+ { 2, 828 },
+ { 2, 830 },
+ { 2, 832 },
+ { 2, 834 },
+ { 2, 836 },
+ { 2, 838 },
+ { 2, 840 },
+ { 2, 842 },
+ { 3, 844 },
+ { 3, 847 },
+ { 2, 850 },
+ { 2, 852 },
+ { 2, 854 },
+ { 2, 856 },
+ { 2, 858 },
+ { 2, 860 },
+ { 2, 862 },
+ { 2, 864 },
+ { 2, 866 },
+ { 2, 868 },
+ { 3, 870 },
+ { 3, 873 },
+ { 3, 876 },
+ { 3, 879 },
+ { 2, 882 },
+ { 2, 884 },
+ { 2, 886 },
+ { 2, 888 },
+ { 3, 890 },
+ { 3, 893 },
+ { 2, 896 },
+ { 2, 898 },
+ { 2, 900 },
+ { 2, 902 },
+ { 2, 904 },
+ { 2, 906 },
+ { 2, 908 },
+ { 2, 910 },
+ { 2, 912 },
+ { 2, 914 },
+ { 2, 916 },
+ { 2, 918 },
+ { 2, 920 },
+ { 2, 922 },
+ { 2, 924 },
+ { 2, 926 },
+ { 3, 928 },
+ { 3, 931 },
+ { 2, 934 },
+ { 2, 936 },
+ { 2, 938 },
+ { 2, 940 },
+ { 2, 942 },
+ { 2, 944 },
+ { 2, 946 },
+ { 2, 948 },
+ { 3, 950 },
+ { 3, 953 },
+ { 2, 956 },
+ { 2, 958 },
+ { 2, 960 },
+ { 2, 962 },
+ { 2, 964 },
+ { 2, 966 },
+ { 2, 968 },
+ { 2, 970 },
+ { 2, 972 },
+ { 2, 974 },
+ { 2, 976 },
+ { 2, 978 },
+ { 2, 980 },
+ { 2, 982 },
+ { 2, 984 },
+ { 2, 986 },
+ { 2, 988 },
+ { 2, 990 },
+ { 3, 992 },
+ { 3, 995 },
+ { 3, 998 },
+ { 3, 1001 },
+ { 3, 1004 },
+ { 3, 1007 },
+ { 3, 1010 },
+ { 3, 1013 },
+ { 2, 1016 },
+ { 2, 1018 },
+ { 2, 1020 },
+ { 2, 1022 },
+ { 2, 1024 },
+ { 2, 1026 },
+ { 2, 1028 },
+ { 2, 1030 },
+ { 3, 1032 },
+ { 3, 1035 },
+ { 2, 1038 },
+ { 2, 1040 },
+ { 2, 1042 },
+ { 2, 1044 },
+ { 2, 1046 },
+ { 2, 1048 },
+ { 3, 1050 },
+ { 3, 1053 },
+ { 3, 1056 },
+ { 3, 1059 },
+ { 3, 1062 },
+ { 3, 1065 },
+ { 2, 1068 },
+ { 2, 1070 },
+ { 2, 1072 },
+ { 2, 1074 },
+ { 2, 1076 },
+ { 2, 1078 },
+ { 2, 1080 },
+ { 2, 1082 },
+ { 2, 1084 },
+ { 2, 1086 },
+ { 2, 1088 },
+ { 2, 1090 },
+ { 2, 1092 },
+ { 2, 1094 },
+ { 3, 1096 },
+ { 3, 1099 },
+ { 3, 1102 },
+ { 3, 1105 },
+ { 2, 1108 },
+ { 2, 1110 },
+ { 2, 1112 },
+ { 2, 1114 },
+ { 2, 1116 },
+ { 2, 1118 },
+ { 2, 1120 },
+ { 2, 1122 },
+ { 2, 1124 },
+ { 2, 1126 },
+ { 2, 1128 },
+ { 2, 1130 },
+ { 2, 1132 },
+ { 2, 1134 },
+ { 2, 1136 },
+ { 2, 1138 },
+ { 2, 1140 },
+ { 2, 1142 },
+ { 2, 1144 },
+ { 2, 1146 },
+ { 2, 1148 },
+ { 2, 1150 },
+ { 2, 1152 },
+ { 2, 1154 },
+ { 2, 1156 },
+ { 2, 1158 },
+ { 2, 1160 },
+ { 2, 1162 },
+ { 2, 1164 },
+ { 2, 1166 },
+ { 0, 0 },
+ { 2, 1168 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 1170 },
+ { 2, 1172 },
+ { 2, 1174 },
+ { 2, 1176 },
+ { 3, 1178 },
+ { 3, 1181 },
+ { 3, 1184 },
+ { 3, 1187 },
+ { 3, 1190 },
+ { 3, 1193 },
+ { 3, 1196 },
+ { 3, 1199 },
+ { 3, 1202 },
+ { 3, 1205 },
+ { 3, 1208 },
+ { 3, 1211 },
+ { 3, 1214 },
+ { 3, 1217 },
+ { 3, 1220 },
+ { 3, 1223 },
+ { 3, 1226 },
+ { 3, 1229 },
+ { 3, 1232 },
+ { 3, 1235 },
+ { 2, 1238 },
+ { 2, 1240 },
+ { 2, 1242 },
+ { 2, 1244 },
+ { 2, 1246 },
+ { 2, 1248 },
+ { 3, 1250 },
+ { 3, 1253 },
+ { 3, 1256 },
+ { 3, 1259 },
+ { 3, 1262 },
+ { 3, 1265 },
+ { 3, 1268 },
+ { 3, 1271 },
+ { 3, 1274 },
+ { 3, 1277 },
+ { 2, 1280 },
+ { 2, 1282 },
+ { 2, 1284 },
+ { 2, 1286 },
+ { 2, 1288 },
+ { 2, 1290 },
+ { 2, 1292 },
+ { 2, 1294 },
+ { 3, 1296 },
+ { 3, 1299 },
+ { 3, 1302 },
+ { 3, 1305 },
+ { 3, 1308 },
+ { 3, 1311 },
+ { 3, 1314 },
+ { 3, 1317 },
+ { 3, 1320 },
+ { 3, 1323 },
+ { 3, 1326 },
+ { 3, 1329 },
+ { 3, 1332 },
+ { 3, 1335 },
+ { 3, 1338 },
+ { 3, 1341 },
+ { 3, 1344 },
+ { 3, 1347 },
+ { 3, 1350 },
+ { 3, 1353 },
+ { 2, 1356 },
+ { 2, 1358 },
+ { 2, 1360 },
+ { 2, 1362 },
+ { 3, 1364 },
+ { 3, 1367 },
+ { 3, 1370 },
+ { 3, 1373 },
+ { 3, 1376 },
+ { 3, 1379 },
+ { 3, 1382 },
+ { 3, 1385 },
+ { 3, 1388 },
+ { 3, 1391 },
+ { 2, 1394 },
+ { 2, 1396 },
+ { 2, 1398 },
+ { 2, 1400 },
+ { 2, 1402 },
+ { 2, 1404 },
+ { 2, 1406 },
+ { 2, 1408 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x1f[256] = {
+ { 2, 1410 },
+ { 2, 1412 },
+ { 3, 1414 },
+ { 3, 1417 },
+ { 3, 1420 },
+ { 3, 1423 },
+ { 3, 1426 },
+ { 3, 1429 },
+ { 2, 1432 },
+ { 2, 1434 },
+ { 3, 1436 },
+ { 3, 1439 },
+ { 3, 1442 },
+ { 3, 1445 },
+ { 3, 1448 },
+ { 3, 1451 },
+ { 2, 1454 },
+ { 2, 1456 },
+ { 3, 1458 },
+ { 3, 1461 },
+ { 3, 1464 },
+ { 3, 1467 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 1470 },
+ { 2, 1472 },
+ { 3, 1474 },
+ { 3, 1477 },
+ { 3, 1480 },
+ { 3, 1483 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 1486 },
+ { 2, 1488 },
+ { 3, 1490 },
+ { 3, 1493 },
+ { 3, 1496 },
+ { 3, 1499 },
+ { 3, 1502 },
+ { 3, 1505 },
+ { 2, 1508 },
+ { 2, 1510 },
+ { 3, 1512 },
+ { 3, 1515 },
+ { 3, 1518 },
+ { 3, 1521 },
+ { 3, 1524 },
+ { 3, 1527 },
+ { 2, 1530 },
+ { 2, 1532 },
+ { 3, 1534 },
+ { 3, 1537 },
+ { 3, 1540 },
+ { 3, 1543 },
+ { 3, 1546 },
+ { 3, 1549 },
+ { 2, 1552 },
+ { 2, 1554 },
+ { 3, 1556 },
+ { 3, 1559 },
+ { 3, 1562 },
+ { 3, 1565 },
+ { 3, 1568 },
+ { 3, 1571 },
+ { 2, 1574 },
+ { 2, 1576 },
+ { 3, 1578 },
+ { 3, 1581 },
+ { 3, 1584 },
+ { 3, 1587 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 1590 },
+ { 2, 1592 },
+ { 3, 1594 },
+ { 3, 1597 },
+ { 3, 1600 },
+ { 3, 1603 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 1606 },
+ { 2, 1608 },
+ { 3, 1610 },
+ { 3, 1613 },
+ { 3, 1616 },
+ { 3, 1619 },
+ { 3, 1622 },
+ { 3, 1625 },
+ { 0, 0 },
+ { 2, 1628 },
+ { 0, 0 },
+ { 3, 1630 },
+ { 0, 0 },
+ { 3, 1633 },
+ { 0, 0 },
+ { 3, 1636 },
+ { 2, 1639 },
+ { 2, 1641 },
+ { 3, 1643 },
+ { 3, 1646 },
+ { 3, 1649 },
+ { 3, 1652 },
+ { 3, 1655 },
+ { 3, 1658 },
+ { 2, 1661 },
+ { 2, 1663 },
+ { 3, 1665 },
+ { 3, 1668 },
+ { 3, 1671 },
+ { 3, 1674 },
+ { 3, 1677 },
+ { 3, 1680 },
+ { 2, 1683 },
+ { 2, 1685 },
+ { 2, 1687 },
+ { 2, 1689 },
+ { 2, 1691 },
+ { 2, 1693 },
+ { 2, 1695 },
+ { 2, 1697 },
+ { 2, 1699 },
+ { 2, 1701 },
+ { 2, 1703 },
+ { 2, 1705 },
+ { 2, 1707 },
+ { 2, 1709 },
+ { 0, 0 },
+ { 0, 0 },
+ { 3, 1711 },
+ { 3, 1714 },
+ { 4, 1717 },
+ { 4, 1721 },
+ { 4, 1725 },
+ { 4, 1729 },
+ { 4, 1733 },
+ { 4, 1737 },
+ { 3, 1741 },
+ { 3, 1744 },
+ { 4, 1747 },
+ { 4, 1751 },
+ { 4, 1755 },
+ { 4, 1759 },
+ { 4, 1763 },
+ { 4, 1767 },
+ { 3, 1771 },
+ { 3, 1774 },
+ { 4, 1777 },
+ { 4, 1781 },
+ { 4, 1785 },
+ { 4, 1789 },
+ { 4, 1793 },
+ { 4, 1797 },
+ { 3, 1801 },
+ { 3, 1804 },
+ { 4, 1807 },
+ { 4, 1811 },
+ { 4, 1815 },
+ { 4, 1819 },
+ { 4, 1823 },
+ { 4, 1827 },
+ { 3, 1831 },
+ { 3, 1834 },
+ { 4, 1837 },
+ { 4, 1841 },
+ { 4, 1845 },
+ { 4, 1849 },
+ { 4, 1853 },
+ { 4, 1857 },
+ { 3, 1861 },
+ { 3, 1864 },
+ { 4, 1867 },
+ { 4, 1871 },
+ { 4, 1875 },
+ { 4, 1879 },
+ { 4, 1883 },
+ { 4, 1887 },
+ { 2, 1891 },
+ { 2, 1893 },
+ { 3, 1895 },
+ { 2, 1898 },
+ { 3, 1900 },
+ { 0, 0 },
+ { 2, 1903 },
+ { 3, 1905 },
+ { 2, 1908 },
+ { 2, 1910 },
+ { 2, 1912 },
+ { 2, 1914 },
+ { 2, 1916 },
+ { 0, 0 },
+ { 1, 1918 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 1919 },
+ { 3, 1921 },
+ { 2, 1924 },
+ { 3, 1926 },
+ { 0, 0 },
+ { 2, 1929 },
+ { 3, 1931 },
+ { 2, 1934 },
+ { 2, 1936 },
+ { 2, 1938 },
+ { 2, 1940 },
+ { 2, 1942 },
+ { 2, 1944 },
+ { 2, 1946 },
+ { 2, 1948 },
+ { 2, 1950 },
+ { 2, 1952 },
+ { 3, 1954 },
+ { 3, 1957 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 1960 },
+ { 3, 1962 },
+ { 2, 1965 },
+ { 2, 1967 },
+ { 2, 1969 },
+ { 2, 1971 },
+ { 0, 0 },
+ { 2, 1973 },
+ { 2, 1975 },
+ { 2, 1977 },
+ { 2, 1979 },
+ { 2, 1981 },
+ { 3, 1983 },
+ { 3, 1986 },
+ { 2, 1989 },
+ { 2, 1991 },
+ { 2, 1993 },
+ { 3, 1995 },
+ { 2, 1998 },
+ { 2, 2000 },
+ { 2, 2002 },
+ { 2, 2004 },
+ { 2, 2006 },
+ { 2, 2008 },
+ { 2, 2010 },
+ { 1, 2012 },
+ { 0, 0 },
+ { 0, 0 },
+ { 3, 2013 },
+ { 2, 2016 },
+ { 3, 2018 },
+ { 0, 0 },
+ { 2, 2021 },
+ { 3, 2023 },
+ { 2, 2026 },
+ { 2, 2028 },
+ { 2, 2030 },
+ { 2, 2032 },
+ { 2, 2034 },
+ { 1, 2036 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x22[256] = {
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2055 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2057 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2059 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2061 },
+ { 0, 0 },
+ { 2, 2063 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2065 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2067 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2069 },
+ { 0, 0 },
+ { 2, 2071 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2073 },
+ { 0, 0 },
+ { 2, 2075 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2077 },
+ { 2, 2079 },
+ { 2, 2081 },
+ { 2, 2083 },
+ { 2, 2085 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2087 },
+ { 2, 2089 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2091 },
+ { 2, 2093 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2095 },
+ { 2, 2097 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2099 },
+ { 2, 2101 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2103 },
+ { 2, 2105 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2107 },
+ { 2, 2109 },
+ { 2, 2111 },
+ { 2, 2113 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2115 },
+ { 2, 2117 },
+ { 2, 2119 },
+ { 2, 2121 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2123 },
+ { 2, 2125 },
+ { 2, 2127 },
+ { 2, 2129 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x30[256] = {
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2135 },
+ { 0, 0 },
+ { 2, 2137 },
+ { 0, 0 },
+ { 2, 2139 },
+ { 0, 0 },
+ { 2, 2141 },
+ { 0, 0 },
+ { 2, 2143 },
+ { 0, 0 },
+ { 2, 2145 },
+ { 0, 0 },
+ { 2, 2147 },
+ { 0, 0 },
+ { 2, 2149 },
+ { 0, 0 },
+ { 2, 2151 },
+ { 0, 0 },
+ { 2, 2153 },
+ { 0, 0 },
+ { 2, 2155 },
+ { 0, 0 },
+ { 2, 2157 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2159 },
+ { 0, 0 },
+ { 2, 2161 },
+ { 0, 0 },
+ { 2, 2163 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2165 },
+ { 2, 2167 },
+ { 0, 0 },
+ { 2, 2169 },
+ { 2, 2171 },
+ { 0, 0 },
+ { 2, 2173 },
+ { 2, 2175 },
+ { 0, 0 },
+ { 2, 2177 },
+ { 2, 2179 },
+ { 0, 0 },
+ { 2, 2181 },
+ { 2, 2183 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2185 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2187 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2189 },
+ { 0, 0 },
+ { 2, 2191 },
+ { 0, 0 },
+ { 2, 2193 },
+ { 0, 0 },
+ { 2, 2195 },
+ { 0, 0 },
+ { 2, 2197 },
+ { 0, 0 },
+ { 2, 2199 },
+ { 0, 0 },
+ { 2, 2201 },
+ { 0, 0 },
+ { 2, 2203 },
+ { 0, 0 },
+ { 2, 2205 },
+ { 0, 0 },
+ { 2, 2207 },
+ { 0, 0 },
+ { 2, 2209 },
+ { 0, 0 },
+ { 2, 2211 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2213 },
+ { 0, 0 },
+ { 2, 2215 },
+ { 0, 0 },
+ { 2, 2217 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2219 },
+ { 2, 2221 },
+ { 0, 0 },
+ { 2, 2223 },
+ { 2, 2225 },
+ { 0, 0 },
+ { 2, 2227 },
+ { 2, 2229 },
+ { 0, 0 },
+ { 2, 2231 },
+ { 2, 2233 },
+ { 0, 0 },
+ { 2, 2235 },
+ { 2, 2237 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2239 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2241 },
+ { 2, 2243 },
+ { 2, 2245 },
+ { 2, 2247 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2249 },
+ { 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0xf9[256] = {
+ { 1, 2251 },
+ { 1, 2252 },
+ { 1, 2253 },
+ { 1, 2254 },
+ { 1, 2255 },
+ { 1, 2256 },
+ { 1, 2257 },
+ { 1, 2258 },
+ { 1, 2259 },
+ { 1, 2260 },
+ { 1, 2261 },
+ { 1, 2262 },
+ { 1, 2263 },
+ { 1, 2264 },
+ { 1, 2265 },
+ { 1, 2266 },
+ { 1, 2267 },
+ { 1, 2268 },
+ { 1, 2269 },
+ { 1, 2270 },
+ { 1, 2271 },
+ { 1, 2272 },
+ { 1, 2273 },
+ { 1, 2274 },
+ { 1, 2275 },
+ { 1, 2276 },
+ { 1, 2277 },
+ { 1, 2278 },
+ { 1, 2279 },
+ { 1, 2280 },
+ { 1, 2281 },
+ { 1, 2282 },
+ { 1, 2283 },
+ { 1, 2284 },
+ { 1, 2285 },
+ { 1, 2286 },
+ { 1, 2287 },
+ { 1, 2288 },
+ { 1, 2289 },
+ { 1, 2290 },
+ { 1, 2291 },
+ { 1, 2292 },
+ { 1, 2293 },
+ { 1, 2294 },
+ { 1, 2295 },
+ { 1, 2296 },
+ { 1, 2297 },
+ { 1, 2298 },
+ { 1, 2299 },
+ { 1, 2300 },
+ { 1, 2301 },
+ { 1, 2302 },
+ { 1, 2303 },
+ { 1, 2304 },
+ { 1, 2305 },
+ { 1, 2306 },
+ { 1, 2307 },
+ { 1, 2308 },
+ { 1, 2309 },
+ { 1, 2310 },
+ { 1, 2311 },
+ { 1, 2312 },
+ { 1, 2313 },
+ { 1, 2314 },
+ { 1, 2315 },
+ { 1, 2316 },
+ { 1, 2317 },
+ { 1, 2318 },
+ { 1, 2319 },
+ { 1, 2320 },
+ { 1, 2321 },
+ { 1, 2322 },
+ { 1, 2323 },
+ { 1, 2324 },
+ { 1, 2325 },
+ { 1, 2326 },
+ { 1, 2327 },
+ { 1, 2328 },
+ { 1, 2329 },
+ { 1, 2330 },
+ { 1, 2331 },
+ { 1, 2332 },
+ { 1, 2333 },
+ { 1, 2334 },
+ { 1, 2335 },
+ { 1, 2336 },
+ { 1, 2337 },
+ { 1, 2338 },
+ { 1, 2339 },
+ { 1, 2340 },
+ { 1, 2341 },
+ { 1, 2342 },
+ { 1, 2343 },
+ { 1, 2344 },
+ { 1, 2345 },
+ { 1, 2346 },
+ { 1, 2347 },
+ { 1, 2348 },
+ { 1, 2349 },
+ { 1, 2350 },
+ { 1, 2351 },
+ { 1, 2352 },
+ { 1, 2353 },
+ { 1, 2354 },
+ { 1, 2355 },
+ { 1, 2356 },
+ { 1, 2357 },
+ { 1, 2358 },
+ { 1, 2359 },
+ { 1, 2360 },
+ { 1, 2361 },
+ { 1, 2362 },
+ { 1, 2363 },
+ { 1, 2364 },
+ { 1, 2365 },
+ { 1, 2366 },
+ { 1, 2367 },
+ { 1, 2368 },
+ { 1, 2369 },
+ { 1, 2370 },
+ { 1, 2371 },
+ { 1, 2372 },
+ { 1, 2373 },
+ { 1, 2374 },
+ { 1, 2375 },
+ { 1, 2376 },
+ { 1, 2377 },
+ { 1, 2378 },
+ { 1, 2379 },
+ { 1, 2380 },
+ { 1, 2381 },
+ { 1, 2382 },
+ { 1, 2383 },
+ { 1, 2384 },
+ { 1, 2385 },
+ { 1, 2386 },
+ { 1, 2387 },
+ { 1, 2388 },
+ { 1, 2389 },
+ { 1, 2390 },
+ { 1, 2391 },
+ { 1, 2392 },
+ { 1, 2393 },
+ { 1, 2394 },
+ { 1, 2395 },
+ { 1, 2396 },
+ { 1, 2397 },
+ { 1, 2398 },
+ { 1, 2399 },
+ { 1, 2400 },
+ { 1, 2401 },
+ { 1, 2402 },
+ { 1, 2403 },
+ { 1, 2404 },
+ { 1, 2405 },
+ { 1, 2406 },
+ { 1, 2407 },
+ { 1, 2408 },
+ { 1, 2409 },
+ { 1, 2410 },
+ { 1, 2411 },
+ { 1, 2412 },
+ { 1, 2413 },
+ { 1, 2414 },
+ { 1, 2415 },
+ { 1, 2416 },
+ { 1, 2417 },
+ { 1, 2418 },
+ { 1, 2419 },
+ { 1, 2420 },
+ { 1, 2421 },
+ { 1, 2422 },
+ { 1, 2423 },
+ { 1, 2424 },
+ { 1, 2425 },
+ { 1, 2426 },
+ { 1, 2427 },
+ { 1, 2428 },
+ { 1, 2429 },
+ { 1, 2430 },
+ { 1, 2431 },
+ { 1, 2432 },
+ { 1, 2433 },
+ { 1, 2434 },
+ { 1, 2435 },
+ { 1, 2436 },
+ { 1, 2437 },
+ { 1, 2438 },
+ { 1, 2439 },
+ { 1, 2440 },
+ { 1, 2441 },
+ { 1, 2442 },
+ { 1, 2443 },
+ { 1, 2444 },
+ { 1, 2445 },
+ { 1, 2446 },
+ { 1, 2447 },
+ { 1, 2448 },
+ { 1, 2449 },
+ { 1, 2450 },
+ { 1, 2451 },
+ { 1, 2452 },
+ { 1, 2453 },
+ { 1, 2454 },
+ { 1, 2455 },
+ { 1, 2456 },
+ { 1, 2457 },
+ { 1, 2458 },
+ { 1, 2459 },
+ { 1, 2460 },
+ { 1, 2461 },
+ { 1, 2462 },
+ { 1, 2463 },
+ { 1, 2464 },
+ { 1, 2465 },
+ { 1, 2466 },
+ { 1, 2467 },
+ { 1, 2468 },
+ { 1, 2469 },
+ { 1, 2470 },
+ { 1, 2471 },
+ { 1, 2472 },
+ { 1, 2473 },
+ { 1, 2474 },
+ { 1, 2475 },
+ { 1, 2476 },
+ { 1, 2477 },
+ { 1, 2478 },
+ { 1, 2479 },
+ { 1, 2480 },
+ { 1, 2481 },
+ { 1, 2482 },
+ { 1, 2483 },
+ { 1, 2484 },
+ { 1, 2485 },
+ { 1, 2486 },
+ { 1, 2487 },
+ { 1, 2488 },
+ { 1, 2489 },
+ { 1, 2490 },
+ { 1, 2491 },
+ { 1, 2492 },
+ { 1, 2493 },
+ { 1, 2494 },
+ { 1, 2495 },
+ { 1, 2496 },
+ { 1, 2497 },
+ { 1, 2498 },
+ { 1, 2499 },
+ { 1, 2500 },
+ { 1, 2501 },
+ { 1, 2502 },
+ { 1, 2503 },
+ { 1, 2504 },
+ { 1, 2505 },
+ { 1, 2506 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0xfa[256] = {
+ { 1, 2507 },
+ { 1, 2508 },
+ { 1, 2509 },
+ { 1, 2510 },
+ { 1, 2511 },
+ { 1, 2512 },
+ { 1, 2513 },
+ { 1, 2514 },
+ { 1, 2515 },
+ { 1, 2516 },
+ { 1, 2517 },
+ { 1, 2518 },
+ { 1, 2519 },
+ { 1, 2520 },
+ { 0, 0 },
+ { 0, 0 },
+ { 1, 2521 },
+ { 0, 0 },
+ { 1, 2522 },
+ { 0, 0 },
+ { 0, 0 },
+ { 1, 2523 },
+ { 1, 2524 },
+ { 1, 2525 },
+ { 1, 2526 },
+ { 1, 2527 },
+ { 1, 2528 },
+ { 1, 2529 },
+ { 1, 2530 },
+ { 1, 2531 },
+ { 1, 2532 },
+ { 0, 0 },
+ { 1, 2533 },
+ { 0, 0 },
+ { 1, 2534 },
+ { 0, 0 },
+ { 0, 0 },
+ { 1, 2535 },
+ { 1, 2536 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 1, 2537 },
+ { 1, 2538 },
+ { 1, 2539 },
+ { 1, 2540 },
+ { 0, 0 },
+ { 0, 0 },
+ { 1, 2541 },
+ { 1, 2542 },
+ { 1, 2543 },
+ { 1, 2544 },
+ { 1, 2545 },
+ { 1, 2546 },
+ { 1, 2547 },
+ { 1, 2548 },
+ { 1, 2549 },
+ { 1, 2550 },
+ { 1, 2551 },
+ { 1, 2552 },
+ { 1, 2553 },
+ { 1, 2554 },
+ { 1, 2555 },
+ { 1, 2556 },
+ { 1, 2557 },
+ { 1, 2558 },
+ { 1, 2559 },
+ { 1, 2560 },
+ { 1, 2561 },
+ { 1, 2562 },
+ { 1, 2563 },
+ { 1, 2564 },
+ { 1, 2565 },
+ { 1, 2566 },
+ { 1, 2567 },
+ { 1, 2568 },
+ { 1, 2569 },
+ { 1, 2570 },
+ { 1, 2571 },
+ { 1, 2572 },
+ { 1, 2573 },
+ { 1, 2574 },
+ { 1, 2575 },
+ { 1, 2576 },
+ { 1, 2577 },
+ { 1, 2578 },
+ { 1, 2579 },
+ { 1, 2580 },
+ { 1, 2581 },
+ { 1, 2582 },
+ { 1, 2583 },
+ { 1, 2584 },
+ { 1, 2585 },
+ { 1, 2586 },
+ { 1, 2587 },
+ { 1, 2588 },
+ { 1, 2589 },
+ { 1, 2590 },
+ { 1, 2591 },
+ { 1, 2592 },
+ { 1, 2593 },
+ { 1, 2594 },
+ { 1, 2595 },
+ { 1, 2596 },
+ { 1, 2597 },
+ { 1, 2598 },
+ { 1, 2599 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0xfb[256] = {
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2600 },
+ { 0, 0 },
+ { 2, 2602 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 2, 2604 },
+ { 2, 2606 },
+ { 3, 2608 },
+ { 3, 2611 },
+ { 2, 2614 },
+ { 2, 2616 },
+ { 2, 2618 },
+ { 2, 2620 },
+ { 2, 2622 },
+ { 2, 2624 },
+ { 2, 2626 },
+ { 2, 2628 },
+ { 2, 2630 },
+ { 0, 0 },
+ { 2, 2632 },
+ { 2, 2634 },
+ { 2, 2636 },
+ { 2, 2638 },
+ { 2, 2640 },
+ { 0, 0 },
+ { 2, 2642 },
+ { 0, 0 },
+ { 2, 2644 },
+ { 2, 2646 },
+ { 0, 0 },
+ { 2, 2648 },
+ { 2, 2650 },
+ { 0, 0 },
+ { 2, 2652 },
+ { 2, 2654 },
+ { 2, 2656 },
+ { 2, 2658 },
+ { 2, 2660 },
+ { 2, 2662 },
+ { 2, 2664 },
+ { 2, 2666 },
+ { 2, 2668 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x2f8[256] = {
+ { 1, 2705 },
+ { 1, 2706 },
+ { 1, 2707 },
+ { 1, 2708 },
+ { 1, 2709 },
+ { 1, 2710 },
+ { 1, 2711 },
+ { 1, 2712 },
+ { 1, 2713 },
+ { 1, 2714 },
+ { 1, 2715 },
+ { 1, 2716 },
+ { 1, 2717 },
+ { 1, 2718 },
+ { 1, 2719 },
+ { 1, 2720 },
+ { 1, 2721 },
+ { 1, 2722 },
+ { 1, 2723 },
+ { 1, 2724 },
+ { 1, 2725 },
+ { 1, 2726 },
+ { 1, 2727 },
+ { 1, 2728 },
+ { 1, 2729 },
+ { 1, 2730 },
+ { 1, 2731 },
+ { 1, 2732 },
+ { 1, 2733 },
+ { 1, 2734 },
+ { 1, 2735 },
+ { 1, 2736 },
+ { 1, 2737 },
+ { 1, 2738 },
+ { 1, 2739 },
+ { 1, 2740 },
+ { 1, 2741 },
+ { 1, 2742 },
+ { 1, 2743 },
+ { 1, 2744 },
+ { 1, 2745 },
+ { 1, 2746 },
+ { 1, 2747 },
+ { 1, 2748 },
+ { 1, 2749 },
+ { 1, 2750 },
+ { 1, 2751 },
+ { 1, 2752 },
+ { 1, 2753 },
+ { 1, 2754 },
+ { 1, 2755 },
+ { 1, 2756 },
+ { 1, 2757 },
+ { 1, 2758 },
+ { 1, 2759 },
+ { 1, 2760 },
+ { 1, 2761 },
+ { 1, 2762 },
+ { 1, 2763 },
+ { 1, 2764 },
+ { 1, 2765 },
+ { 1, 2766 },
+ { 1, 2767 },
+ { 1, 2768 },
+ { 1, 2769 },
+ { 1, 2770 },
+ { 1, 2771 },
+ { 1, 2772 },
+ { 1, 2773 },
+ { 1, 2774 },
+ { 1, 2775 },
+ { 1, 2776 },
+ { 1, 2777 },
+ { 1, 2778 },
+ { 1, 2779 },
+ { 1, 2780 },
+ { 1, 2781 },
+ { 1, 2782 },
+ { 1, 2783 },
+ { 1, 2784 },
+ { 1, 2785 },
+ { 1, 2786 },
+ { 1, 2787 },
+ { 1, 2788 },
+ { 1, 2789 },
+ { 1, 2790 },
+ { 1, 2791 },
+ { 1, 2792 },
+ { 1, 2793 },
+ { 1, 2794 },
+ { 1, 2795 },
+ { 1, 2796 },
+ { 1, 2797 },
+ { 1, 2798 },
+ { 1, 2799 },
+ { 1, 2800 },
+ { 1, 2801 },
+ { 1, 2802 },
+ { 1, 2803 },
+ { 1, 2804 },
+ { 1, 2805 },
+ { 1, 2806 },
+ { 1, 2807 },
+ { 1, 2808 },
+ { 1, 2809 },
+ { 1, 2810 },
+ { 1, 2811 },
+ { 1, 2812 },
+ { 1, 2813 },
+ { 1, 2814 },
+ { 1, 2815 },
+ { 1, 2816 },
+ { 1, 2817 },
+ { 1, 2818 },
+ { 1, 2819 },
+ { 1, 2820 },
+ { 1, 2821 },
+ { 1, 2822 },
+ { 1, 2823 },
+ { 1, 2824 },
+ { 1, 2825 },
+ { 1, 2826 },
+ { 1, 2827 },
+ { 1, 2828 },
+ { 1, 2829 },
+ { 1, 2830 },
+ { 1, 2831 },
+ { 1, 2832 },
+ { 1, 2833 },
+ { 1, 2834 },
+ { 1, 2835 },
+ { 1, 2836 },
+ { 1, 2837 },
+ { 1, 2838 },
+ { 1, 2839 },
+ { 1, 2840 },
+ { 1, 2841 },
+ { 1, 2842 },
+ { 1, 2843 },
+ { 1, 2844 },
+ { 1, 2845 },
+ { 1, 2846 },
+ { 1, 2847 },
+ { 1, 2848 },
+ { 1, 2849 },
+ { 1, 2850 },
+ { 1, 2851 },
+ { 1, 2852 },
+ { 1, 2853 },
+ { 1, 2854 },
+ { 1, 2855 },
+ { 1, 2856 },
+ { 1, 2857 },
+ { 1, 2858 },
+ { 1, 2859 },
+ { 1, 2860 },
+ { 1, 2861 },
+ { 1, 2862 },
+ { 1, 2863 },
+ { 1, 2864 },
+ { 1, 2865 },
+ { 1, 2866 },
+ { 1, 2867 },
+ { 1, 2868 },
+ { 1, 2869 },
+ { 1, 2870 },
+ { 1, 2871 },
+ { 1, 2872 },
+ { 1, 2873 },
+ { 1, 2874 },
+ { 1, 2875 },
+ { 1, 2876 },
+ { 1, 2877 },
+ { 1, 2878 },
+ { 1, 2879 },
+ { 1, 2880 },
+ { 1, 2881 },
+ { 1, 2882 },
+ { 1, 2883 },
+ { 1, 2884 },
+ { 1, 2885 },
+ { 1, 2886 },
+ { 1, 2887 },
+ { 1, 2888 },
+ { 1, 2889 },
+ { 1, 2890 },
+ { 1, 2891 },
+ { 1, 2892 },
+ { 1, 2893 },
+ { 1, 2894 },
+ { 1, 2895 },
+ { 1, 2896 },
+ { 1, 2897 },
+ { 1, 2898 },
+ { 1, 2899 },
+ { 1, 2900 },
+ { 1, 2901 },
+ { 1, 2902 },
+ { 1, 2903 },
+ { 1, 2904 },
+ { 1, 2905 },
+ { 1, 2906 },
+ { 1, 2907 },
+ { 1, 2908 },
+ { 1, 2909 },
+ { 1, 2910 },
+ { 1, 2911 },
+ { 1, 2912 },
+ { 1, 2913 },
+ { 1, 2914 },
+ { 1, 2915 },
+ { 1, 2916 },
+ { 1, 2917 },
+ { 1, 2918 },
+ { 1, 2919 },
+ { 1, 2920 },
+ { 1, 2921 },
+ { 1, 2922 },
+ { 1, 2923 },
+ { 1, 2924 },
+ { 1, 2925 },
+ { 1, 2926 },
+ { 1, 2927 },
+ { 1, 2928 },
+ { 1, 2929 },
+ { 1, 2930 },
+ { 1, 2931 },
+ { 1, 2932 },
+ { 1, 2933 },
+ { 1, 2934 },
+ { 1, 2935 },
+ { 1, 2936 },
+ { 1, 2937 },
+ { 1, 2938 },
+ { 1, 2939 },
+ { 1, 2940 },
+ { 1, 2941 },
+ { 1, 2942 },
+ { 1, 2943 },
+ { 1, 2944 },
+ { 1, 2945 },
+ { 1, 2946 },
+ { 1, 2947 },
+ { 1, 2948 },
+ { 1, 2949 },
+ { 1, 2950 },
+ { 1, 2951 },
+ { 1, 2952 },
+ { 1, 2953 },
+ { 1, 2954 },
+ { 1, 2955 },
+ { 1, 2956 },
+ { 1, 2957 },
+ { 1, 2958 },
+ { 1, 2959 },
+ { 1, 2960 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x2f9[256] = {
+ { 1, 2961 },
+ { 1, 2962 },
+ { 1, 2963 },
+ { 1, 2964 },
+ { 1, 2965 },
+ { 1, 2966 },
+ { 1, 2967 },
+ { 1, 2968 },
+ { 1, 2969 },
+ { 1, 2970 },
+ { 1, 2971 },
+ { 1, 2972 },
+ { 1, 2973 },
+ { 1, 2974 },
+ { 1, 2975 },
+ { 1, 2976 },
+ { 1, 2977 },
+ { 1, 2978 },
+ { 1, 2979 },
+ { 1, 2980 },
+ { 1, 2981 },
+ { 1, 2982 },
+ { 1, 2983 },
+ { 1, 2984 },
+ { 1, 2985 },
+ { 1, 2986 },
+ { 1, 2987 },
+ { 1, 2988 },
+ { 1, 2989 },
+ { 1, 2990 },
+ { 1, 2991 },
+ { 1, 2992 },
+ { 1, 2993 },
+ { 1, 2994 },
+ { 1, 2995 },
+ { 1, 2996 },
+ { 1, 2997 },
+ { 1, 2998 },
+ { 1, 2999 },
+ { 1, 3000 },
+ { 1, 3001 },
+ { 1, 3002 },
+ { 1, 3003 },
+ { 1, 3004 },
+ { 1, 3005 },
+ { 1, 3006 },
+ { 1, 3007 },
+ { 1, 3008 },
+ { 1, 3009 },
+ { 1, 3010 },
+ { 1, 3011 },
+ { 1, 3012 },
+ { 1, 3013 },
+ { 1, 3014 },
+ { 1, 3015 },
+ { 1, 3016 },
+ { 1, 3017 },
+ { 1, 3018 },
+ { 1, 3019 },
+ { 1, 3020 },
+ { 1, 3021 },
+ { 1, 3022 },
+ { 1, 3023 },
+ { 1, 3024 },
+ { 1, 3025 },
+ { 1, 3026 },
+ { 1, 3027 },
+ { 1, 3028 },
+ { 1, 3029 },
+ { 1, 3030 },
+ { 1, 3031 },
+ { 1, 3032 },
+ { 1, 3033 },
+ { 1, 3034 },
+ { 1, 3035 },
+ { 1, 3036 },
+ { 1, 3037 },
+ { 1, 3038 },
+ { 1, 3039 },
+ { 1, 3040 },
+ { 1, 3041 },
+ { 1, 3042 },
+ { 1, 3043 },
+ { 1, 3044 },
+ { 1, 3045 },
+ { 1, 3046 },
+ { 1, 3047 },
+ { 1, 3048 },
+ { 1, 3049 },
+ { 1, 3050 },
+ { 1, 3051 },
+ { 1, 3052 },
+ { 1, 3053 },
+ { 1, 3054 },
+ { 1, 3055 },
+ { 1, 3056 },
+ { 1, 3057 },
+ { 1, 3058 },
+ { 1, 3059 },
+ { 1, 3060 },
+ { 1, 3061 },
+ { 1, 3062 },
+ { 1, 3063 },
+ { 1, 3064 },
+ { 1, 3065 },
+ { 1, 3066 },
+ { 1, 3067 },
+ { 1, 3068 },
+ { 1, 3069 },
+ { 1, 3070 },
+ { 1, 3071 },
+ { 1, 3072 },
+ { 1, 3073 },
+ { 1, 3074 },
+ { 1, 3075 },
+ { 1, 3076 },
+ { 1, 3077 },
+ { 1, 3078 },
+ { 1, 3079 },
+ { 1, 3080 },
+ { 1, 3081 },
+ { 1, 3082 },
+ { 1, 3083 },
+ { 1, 3084 },
+ { 1, 3085 },
+ { 1, 3086 },
+ { 1, 3087 },
+ { 1, 3088 },
+ { 1, 3089 },
+ { 1, 3090 },
+ { 1, 3091 },
+ { 1, 3092 },
+ { 1, 3093 },
+ { 1, 3094 },
+ { 1, 3095 },
+ { 1, 3096 },
+ { 1, 3097 },
+ { 1, 3098 },
+ { 1, 3099 },
+ { 1, 3100 },
+ { 1, 3101 },
+ { 1, 3102 },
+ { 1, 3103 },
+ { 1, 3104 },
+ { 1, 3105 },
+ { 1, 3106 },
+ { 1, 3107 },
+ { 1, 3108 },
+ { 1, 3109 },
+ { 1, 3110 },
+ { 1, 3111 },
+ { 1, 3112 },
+ { 1, 3113 },
+ { 1, 3114 },
+ { 1, 3115 },
+ { 1, 3116 },
+ { 1, 3117 },
+ { 1, 3118 },
+ { 1, 3119 },
+ { 1, 3120 },
+ { 1, 3121 },
+ { 1, 3122 },
+ { 1, 3123 },
+ { 1, 3124 },
+ { 1, 3125 },
+ { 1, 3126 },
+ { 1, 3127 },
+ { 1, 3128 },
+ { 1, 3129 },
+ { 1, 3130 },
+ { 1, 3131 },
+ { 1, 3132 },
+ { 1, 3133 },
+ { 1, 3134 },
+ { 1, 3135 },
+ { 1, 3136 },
+ { 1, 3137 },
+ { 1, 3138 },
+ { 1, 3139 },
+ { 1, 3140 },
+ { 1, 3141 },
+ { 1, 3142 },
+ { 1, 3143 },
+ { 1, 3144 },
+ { 1, 3145 },
+ { 1, 3146 },
+ { 1, 3147 },
+ { 1, 3148 },
+ { 1, 3149 },
+ { 1, 3150 },
+ { 1, 3151 },
+ { 1, 3152 },
+ { 1, 3153 },
+ { 1, 3154 },
+ { 1, 3155 },
+ { 1, 3156 },
+ { 1, 3157 },
+ { 1, 3158 },
+ { 1, 3159 },
+ { 1, 3160 },
+ { 1, 3161 },
+ { 1, 3162 },
+ { 1, 3163 },
+ { 1, 3164 },
+ { 1, 3165 },
+ { 1, 3166 },
+ { 1, 3167 },
+ { 1, 3168 },
+ { 1, 3169 },
+ { 1, 3170 },
+ { 1, 3171 },
+ { 1, 3172 },
+ { 1, 3173 },
+ { 1, 3174 },
+ { 1, 3175 },
+ { 1, 3176 },
+ { 1, 3177 },
+ { 1, 3178 },
+ { 1, 3179 },
+ { 1, 3180 },
+ { 1, 3181 },
+ { 1, 3182 },
+ { 1, 3183 },
+ { 1, 3184 },
+ { 1, 3185 },
+ { 1, 3186 },
+ { 1, 3187 },
+ { 1, 3188 },
+ { 1, 3189 },
+ { 1, 3190 },
+ { 1, 3191 },
+ { 1, 3192 },
+ { 1, 3193 },
+ { 1, 3194 },
+ { 1, 3195 },
+ { 1, 3196 },
+ { 1, 3197 },
+ { 1, 3198 },
+ { 1, 3199 },
+ { 1, 3200 },
+ { 1, 3201 },
+ { 1, 3202 },
+ { 1, 3203 },
+ { 1, 3204 },
+ { 1, 3205 },
+ { 1, 3206 },
+ { 1, 3207 },
+ { 1, 3208 },
+ { 1, 3209 },
+ { 1, 3210 },
+ { 1, 3211 },
+ { 1, 3212 },
+ { 1, 3213 },
+ { 1, 3214 },
+ { 1, 3215 },
+ { 1, 3216 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x2fa[256] = {
+ { 1, 3217 },
+ { 1, 3218 },
+ { 1, 3219 },
+ { 1, 3220 },
+ { 1, 3221 },
+ { 1, 3222 },
+ { 1, 3223 },
+ { 1, 3224 },
+ { 1, 3225 },
+ { 1, 3226 },
+ { 1, 3227 },
+ { 1, 3228 },
+ { 1, 3229 },
+ { 1, 3230 },
+ { 1, 3231 },
+ { 1, 3232 },
+ { 1, 3233 },
+ { 1, 3234 },
+ { 1, 3235 },
+ { 1, 3236 },
+ { 1, 3237 },
+ { 1, 3238 },
+ { 1, 3239 },
+ { 1, 3240 },
+ { 1, 3241 },
+ { 1, 3242 },
+ { 1, 3243 },
+ { 1, 3244 },
+ { 1, 3245 },
+ { 1, 3246 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+};
+
+#define GET_DECOMP_BLOCK(ch, blockptr) \
+ switch ((glui32)(ch) >> 8) { \
+ case 0x0: \
+ *blockptr = unigen_decomp_block_0x0; \
+ break; \
+ case 0x1: \
+ *blockptr = unigen_decomp_block_0x1; \
+ break; \
+ case 0x2: \
+ *blockptr = unigen_decomp_block_0x2; \
+ break; \
+ case 0x3: \
+ *blockptr = unigen_decomp_block_0x3; \
+ break; \
+ case 0x4: \
+ *blockptr = unigen_decomp_block_0x4; \
+ break; \
+ case 0x1e: \
+ *blockptr = unigen_decomp_block_0x1e; \
+ break; \
+ case 0x1f: \
+ *blockptr = unigen_decomp_block_0x1f; \
+ break; \
+ case 0x22: \
+ *blockptr = unigen_decomp_block_0x22; \
+ break; \
+ case 0x30: \
+ *blockptr = unigen_decomp_block_0x30; \
+ break; \
+ case 0xf9: \
+ *blockptr = unigen_decomp_block_0xf9; \
+ break; \
+ case 0xfa: \
+ *blockptr = unigen_decomp_block_0xfa; \
+ break; \
+ case 0xfb: \
+ *blockptr = unigen_decomp_block_0xfb; \
+ break; \
+ case 0x2f8: \
+ *blockptr = unigen_decomp_block_0x2f8; \
+ break; \
+ case 0x2f9: \
+ *blockptr = unigen_decomp_block_0x2f9; \
+ break; \
+ case 0x2fa: \
+ *blockptr = unigen_decomp_block_0x2fa; \
+ break; \
+ default: \
+ *blockptr = nullptr; \
+ }
+
+#define GET_DECOMP_SPECIAL(ch, countptr, posptr) \
+ switch (ch) { \
+ case 0x622: \
+ *countptr = 2; *posptr = 686; \
+ break; \
+ case 0x623: \
+ *countptr = 2; *posptr = 688; \
+ break; \
+ case 0x624: \
+ *countptr = 2; *posptr = 690; \
+ break; \
+ case 0x625: \
+ *countptr = 2; *posptr = 692; \
+ break; \
+ case 0x626: \
+ *countptr = 2; *posptr = 694; \
+ break; \
+ case 0x6c0: \
+ *countptr = 2; *posptr = 696; \
+ break; \
+ case 0x6c2: \
+ *countptr = 2; *posptr = 698; \
+ break; \
+ case 0x6d3: \
+ *countptr = 2; *posptr = 700; \
+ break; \
+ case 0x929: \
+ *countptr = 2; *posptr = 702; \
+ break; \
+ case 0x931: \
+ *countptr = 2; *posptr = 704; \
+ break; \
+ case 0x934: \
+ *countptr = 2; *posptr = 706; \
+ break; \
+ case 0x958: \
+ *countptr = 2; *posptr = 708; \
+ break; \
+ case 0x959: \
+ *countptr = 2; *posptr = 710; \
+ break; \
+ case 0x95a: \
+ *countptr = 2; *posptr = 712; \
+ break; \
+ case 0x95b: \
+ *countptr = 2; *posptr = 714; \
+ break; \
+ case 0x95c: \
+ *countptr = 2; *posptr = 716; \
+ break; \
+ case 0x95d: \
+ *countptr = 2; *posptr = 718; \
+ break; \
+ case 0x95e: \
+ *countptr = 2; *posptr = 720; \
+ break; \
+ case 0x95f: \
+ *countptr = 2; *posptr = 722; \
+ break; \
+ case 0x9cb: \
+ *countptr = 2; *posptr = 724; \
+ break; \
+ case 0x9cc: \
+ *countptr = 2; *posptr = 726; \
+ break; \
+ case 0x9dc: \
+ *countptr = 2; *posptr = 728; \
+ break; \
+ case 0x9dd: \
+ *countptr = 2; *posptr = 730; \
+ break; \
+ case 0x9df: \
+ *countptr = 2; *posptr = 732; \
+ break; \
+ case 0xa33: \
+ *countptr = 2; *posptr = 734; \
+ break; \
+ case 0xa36: \
+ *countptr = 2; *posptr = 736; \
+ break; \
+ case 0xa59: \
+ *countptr = 2; *posptr = 738; \
+ break; \
+ case 0xa5a: \
+ *countptr = 2; *posptr = 740; \
+ break; \
+ case 0xa5b: \
+ *countptr = 2; *posptr = 742; \
+ break; \
+ case 0xa5e: \
+ *countptr = 2; *posptr = 744; \
+ break; \
+ case 0xb48: \
+ *countptr = 2; *posptr = 746; \
+ break; \
+ case 0xb4b: \
+ *countptr = 2; *posptr = 748; \
+ break; \
+ case 0xb4c: \
+ *countptr = 2; *posptr = 750; \
+ break; \
+ case 0xb5c: \
+ *countptr = 2; *posptr = 752; \
+ break; \
+ case 0xb5d: \
+ *countptr = 2; *posptr = 754; \
+ break; \
+ case 0xb94: \
+ *countptr = 2; *posptr = 756; \
+ break; \
+ case 0xbca: \
+ *countptr = 2; *posptr = 758; \
+ break; \
+ case 0xbcb: \
+ *countptr = 2; *posptr = 760; \
+ break; \
+ case 0xbcc: \
+ *countptr = 2; *posptr = 762; \
+ break; \
+ case 0xc48: \
+ *countptr = 2; *posptr = 764; \
+ break; \
+ case 0xcc0: \
+ *countptr = 2; *posptr = 766; \
+ break; \
+ case 0xcc7: \
+ *countptr = 2; *posptr = 768; \
+ break; \
+ case 0xcc8: \
+ *countptr = 2; *posptr = 770; \
+ break; \
+ case 0xcca: \
+ *countptr = 2; *posptr = 772; \
+ break; \
+ case 0xccb: \
+ *countptr = 3; *posptr = 774; \
+ break; \
+ case 0xd4a: \
+ *countptr = 2; *posptr = 777; \
+ break; \
+ case 0xd4b: \
+ *countptr = 2; *posptr = 779; \
+ break; \
+ case 0xd4c: \
+ *countptr = 2; *posptr = 781; \
+ break; \
+ case 0xdda: \
+ *countptr = 2; *posptr = 783; \
+ break; \
+ case 0xddc: \
+ *countptr = 2; *posptr = 785; \
+ break; \
+ case 0xddd: \
+ *countptr = 3; *posptr = 787; \
+ break; \
+ case 0xdde: \
+ *countptr = 2; *posptr = 790; \
+ break; \
+ case 0xf43: \
+ *countptr = 2; *posptr = 792; \
+ break; \
+ case 0xf4d: \
+ *countptr = 2; *posptr = 794; \
+ break; \
+ case 0xf52: \
+ *countptr = 2; *posptr = 796; \
+ break; \
+ case 0xf57: \
+ *countptr = 2; *posptr = 798; \
+ break; \
+ case 0xf5c: \
+ *countptr = 2; *posptr = 800; \
+ break; \
+ case 0xf69: \
+ *countptr = 2; *posptr = 802; \
+ break; \
+ case 0xf73: \
+ *countptr = 2; *posptr = 804; \
+ break; \
+ case 0xf75: \
+ *countptr = 2; *posptr = 806; \
+ break; \
+ case 0xf76: \
+ *countptr = 2; *posptr = 808; \
+ break; \
+ case 0xf78: \
+ *countptr = 2; *posptr = 810; \
+ break; \
+ case 0xf81: \
+ *countptr = 2; *posptr = 812; \
+ break; \
+ case 0xf93: \
+ *countptr = 2; *posptr = 814; \
+ break; \
+ case 0xf9d: \
+ *countptr = 2; *posptr = 816; \
+ break; \
+ case 0xfa2: \
+ *countptr = 2; *posptr = 818; \
+ break; \
+ case 0xfa7: \
+ *countptr = 2; *posptr = 820; \
+ break; \
+ case 0xfac: \
+ *countptr = 2; *posptr = 822; \
+ break; \
+ case 0xfb9: \
+ *countptr = 2; *posptr = 824; \
+ break; \
+ case 0x1026: \
+ *countptr = 2; *posptr = 826; \
+ break; \
+ case 0x2000: \
+ *countptr = 1; *posptr = 2037; \
+ break; \
+ case 0x2001: \
+ *countptr = 1; *posptr = 2038; \
+ break; \
+ case 0x2126: \
+ *countptr = 1; *posptr = 2039; \
+ break; \
+ case 0x212a: \
+ *countptr = 1; *posptr = 2040; \
+ break; \
+ case 0x212b: \
+ *countptr = 2; *posptr = 2041; \
+ break; \
+ case 0x219a: \
+ *countptr = 2; *posptr = 2043; \
+ break; \
+ case 0x219b: \
+ *countptr = 2; *posptr = 2045; \
+ break; \
+ case 0x21ae: \
+ *countptr = 2; *posptr = 2047; \
+ break; \
+ case 0x21cd: \
+ *countptr = 2; *posptr = 2049; \
+ break; \
+ case 0x21ce: \
+ *countptr = 2; *posptr = 2051; \
+ break; \
+ case 0x21cf: \
+ *countptr = 2; *posptr = 2053; \
+ break; \
+ case 0x2329: \
+ *countptr = 1; *posptr = 2131; \
+ break; \
+ case 0x232a: \
+ *countptr = 1; *posptr = 2132; \
+ break; \
+ case 0x2adc: \
+ *countptr = 2; *posptr = 2133; \
+ break; \
+ case 0x1d15e: \
+ *countptr = 2; *posptr = 2670; \
+ break; \
+ case 0x1d15f: \
+ *countptr = 2; *posptr = 2672; \
+ break; \
+ case 0x1d160: \
+ *countptr = 3; *posptr = 2674; \
+ break; \
+ case 0x1d161: \
+ *countptr = 3; *posptr = 2677; \
+ break; \
+ case 0x1d162: \
+ *countptr = 3; *posptr = 2680; \
+ break; \
+ case 0x1d163: \
+ *countptr = 3; *posptr = 2683; \
+ break; \
+ case 0x1d164: \
+ *countptr = 3; *posptr = 2686; \
+ break; \
+ case 0x1d1bb: \
+ *countptr = 2; *posptr = 2689; \
+ break; \
+ case 0x1d1bc: \
+ *countptr = 2; *posptr = 2691; \
+ break; \
+ case 0x1d1bd: \
+ *countptr = 3; *posptr = 2693; \
+ break; \
+ case 0x1d1be: \
+ *countptr = 3; *posptr = 2696; \
+ break; \
+ case 0x1d1bf: \
+ *countptr = 3; *posptr = 2699; \
+ break; \
+ case 0x1d1c0: \
+ *countptr = 3; *posptr = 2702; \
+ break; \
+ default: \
+ *countptr = 0; \
+ }
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/unicode_gen.h b/engines/glk/unicode_gen.h
new file mode 100644
index 0000000000..f601bf6b92
--- /dev/null
+++ b/engines/glk/unicode_gen.h
@@ -0,0 +1,563 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_UNICODE_GEN_H
+#define GLK_UNICODE_GEN_H
+
+#include "glk/glk_types.h"
+
+namespace Gargoyle {
+
+#define GET_CASE_BLOCK(ch, blockptr) \
+ switch ((glui32)(ch) >> 8) { \
+ case 0x0: \
+ *blockptr = unigen_case_block_0x0; \
+ break; \
+ case 0x1: \
+ *blockptr = unigen_case_block_0x1; \
+ break; \
+ case 0x2: \
+ *blockptr = unigen_case_block_0x2; \
+ break; \
+ case 0x3: \
+ *blockptr = unigen_case_block_0x3; \
+ break; \
+ case 0x4: \
+ *blockptr = unigen_case_block_0x4; \
+ break; \
+ case 0x5: \
+ *blockptr = unigen_case_block_0x5; \
+ break; \
+ case 0x1e: \
+ *blockptr = unigen_case_block_0x1e; \
+ break; \
+ case 0x1f: \
+ *blockptr = unigen_case_block_0x1f; \
+ break; \
+ case 0x21: \
+ *blockptr = unigen_case_block_0x21; \
+ break; \
+ case 0x24: \
+ *blockptr = unigen_case_block_0x24; \
+ break; \
+ case 0xfb: \
+ *blockptr = unigen_case_block_0xfb; \
+ break; \
+ case 0xff: \
+ *blockptr = unigen_case_block_0xff; \
+ break; \
+ case 0x104: \
+ *blockptr = unigen_case_block_0x104; \
+ break; \
+ default: \
+ *blockptr = nullptr; \
+ }
+
+#define GET_CASE_SPECIAL(ch, specptr) \
+ switch (ch) { \
+ case 0xdf: \
+ *specptr = unigen_special_0xdf; \
+ break; \
+ case 0x130: \
+ *specptr = unigen_special_0x130; \
+ break; \
+ case 0x149: \
+ *specptr = unigen_special_0x149; \
+ break; \
+ case 0x1c4: \
+ *specptr = unigen_special_0x1c4; \
+ break; \
+ case 0x1c5: \
+ *specptr = unigen_special_0x1c5; \
+ break; \
+ case 0x1c6: \
+ *specptr = unigen_special_0x1c6; \
+ break; \
+ case 0x1c7: \
+ *specptr = unigen_special_0x1c7; \
+ break; \
+ case 0x1c8: \
+ *specptr = unigen_special_0x1c8; \
+ break; \
+ case 0x1c9: \
+ *specptr = unigen_special_0x1c9; \
+ break; \
+ case 0x1ca: \
+ *specptr = unigen_special_0x1ca; \
+ break; \
+ case 0x1cb: \
+ *specptr = unigen_special_0x1cb; \
+ break; \
+ case 0x1cc: \
+ *specptr = unigen_special_0x1cc; \
+ break; \
+ case 0x1f0: \
+ *specptr = unigen_special_0x1f0; \
+ break; \
+ case 0x1f1: \
+ *specptr = unigen_special_0x1f1; \
+ break; \
+ case 0x1f2: \
+ *specptr = unigen_special_0x1f2; \
+ break; \
+ case 0x1f3: \
+ *specptr = unigen_special_0x1f3; \
+ break; \
+ case 0x390: \
+ *specptr = unigen_special_0x390; \
+ break; \
+ case 0x3b0: \
+ *specptr = unigen_special_0x3b0; \
+ break; \
+ case 0x587: \
+ *specptr = unigen_special_0x587; \
+ break; \
+ case 0x1e96: \
+ *specptr = unigen_special_0x1e96; \
+ break; \
+ case 0x1e97: \
+ *specptr = unigen_special_0x1e97; \
+ break; \
+ case 0x1e98: \
+ *specptr = unigen_special_0x1e98; \
+ break; \
+ case 0x1e99: \
+ *specptr = unigen_special_0x1e99; \
+ break; \
+ case 0x1e9a: \
+ *specptr = unigen_special_0x1e9a; \
+ break; \
+ case 0x1f50: \
+ *specptr = unigen_special_0x1f50; \
+ break; \
+ case 0x1f52: \
+ *specptr = unigen_special_0x1f52; \
+ break; \
+ case 0x1f54: \
+ *specptr = unigen_special_0x1f54; \
+ break; \
+ case 0x1f56: \
+ *specptr = unigen_special_0x1f56; \
+ break; \
+ case 0x1f80: \
+ *specptr = unigen_special_0x1f80; \
+ break; \
+ case 0x1f81: \
+ *specptr = unigen_special_0x1f81; \
+ break; \
+ case 0x1f82: \
+ *specptr = unigen_special_0x1f82; \
+ break; \
+ case 0x1f83: \
+ *specptr = unigen_special_0x1f83; \
+ break; \
+ case 0x1f84: \
+ *specptr = unigen_special_0x1f84; \
+ break; \
+ case 0x1f85: \
+ *specptr = unigen_special_0x1f85; \
+ break; \
+ case 0x1f86: \
+ *specptr = unigen_special_0x1f86; \
+ break; \
+ case 0x1f87: \
+ *specptr = unigen_special_0x1f87; \
+ break; \
+ case 0x1f88: \
+ *specptr = unigen_special_0x1f88; \
+ break; \
+ case 0x1f89: \
+ *specptr = unigen_special_0x1f89; \
+ break; \
+ case 0x1f8a: \
+ *specptr = unigen_special_0x1f8a; \
+ break; \
+ case 0x1f8b: \
+ *specptr = unigen_special_0x1f8b; \
+ break; \
+ case 0x1f8c: \
+ *specptr = unigen_special_0x1f8c; \
+ break; \
+ case 0x1f8d: \
+ *specptr = unigen_special_0x1f8d; \
+ break; \
+ case 0x1f8e: \
+ *specptr = unigen_special_0x1f8e; \
+ break; \
+ case 0x1f8f: \
+ *specptr = unigen_special_0x1f8f; \
+ break; \
+ case 0x1f90: \
+ *specptr = unigen_special_0x1f90; \
+ break; \
+ case 0x1f91: \
+ *specptr = unigen_special_0x1f91; \
+ break; \
+ case 0x1f92: \
+ *specptr = unigen_special_0x1f92; \
+ break; \
+ case 0x1f93: \
+ *specptr = unigen_special_0x1f93; \
+ break; \
+ case 0x1f94: \
+ *specptr = unigen_special_0x1f94; \
+ break; \
+ case 0x1f95: \
+ *specptr = unigen_special_0x1f95; \
+ break; \
+ case 0x1f96: \
+ *specptr = unigen_special_0x1f96; \
+ break; \
+ case 0x1f97: \
+ *specptr = unigen_special_0x1f97; \
+ break; \
+ case 0x1f98: \
+ *specptr = unigen_special_0x1f98; \
+ break; \
+ case 0x1f99: \
+ *specptr = unigen_special_0x1f99; \
+ break; \
+ case 0x1f9a: \
+ *specptr = unigen_special_0x1f9a; \
+ break; \
+ case 0x1f9b: \
+ *specptr = unigen_special_0x1f9b; \
+ break; \
+ case 0x1f9c: \
+ *specptr = unigen_special_0x1f9c; \
+ break; \
+ case 0x1f9d: \
+ *specptr = unigen_special_0x1f9d; \
+ break; \
+ case 0x1f9e: \
+ *specptr = unigen_special_0x1f9e; \
+ break; \
+ case 0x1f9f: \
+ *specptr = unigen_special_0x1f9f; \
+ break; \
+ case 0x1fa0: \
+ *specptr = unigen_special_0x1fa0; \
+ break; \
+ case 0x1fa1: \
+ *specptr = unigen_special_0x1fa1; \
+ break; \
+ case 0x1fa2: \
+ *specptr = unigen_special_0x1fa2; \
+ break; \
+ case 0x1fa3: \
+ *specptr = unigen_special_0x1fa3; \
+ break; \
+ case 0x1fa4: \
+ *specptr = unigen_special_0x1fa4; \
+ break; \
+ case 0x1fa5: \
+ *specptr = unigen_special_0x1fa5; \
+ break; \
+ case 0x1fa6: \
+ *specptr = unigen_special_0x1fa6; \
+ break; \
+ case 0x1fa7: \
+ *specptr = unigen_special_0x1fa7; \
+ break; \
+ case 0x1fa8: \
+ *specptr = unigen_special_0x1fa8; \
+ break; \
+ case 0x1fa9: \
+ *specptr = unigen_special_0x1fa9; \
+ break; \
+ case 0x1faa: \
+ *specptr = unigen_special_0x1faa; \
+ break; \
+ case 0x1fab: \
+ *specptr = unigen_special_0x1fab; \
+ break; \
+ case 0x1fac: \
+ *specptr = unigen_special_0x1fac; \
+ break; \
+ case 0x1fad: \
+ *specptr = unigen_special_0x1fad; \
+ break; \
+ case 0x1fae: \
+ *specptr = unigen_special_0x1fae; \
+ break; \
+ case 0x1faf: \
+ *specptr = unigen_special_0x1faf; \
+ break; \
+ case 0x1fb2: \
+ *specptr = unigen_special_0x1fb2; \
+ break; \
+ case 0x1fb3: \
+ *specptr = unigen_special_0x1fb3; \
+ break; \
+ case 0x1fb4: \
+ *specptr = unigen_special_0x1fb4; \
+ break; \
+ case 0x1fb6: \
+ *specptr = unigen_special_0x1fb6; \
+ break; \
+ case 0x1fb7: \
+ *specptr = unigen_special_0x1fb7; \
+ break; \
+ case 0x1fbc: \
+ *specptr = unigen_special_0x1fbc; \
+ break; \
+ case 0x1fc2: \
+ *specptr = unigen_special_0x1fc2; \
+ break; \
+ case 0x1fc3: \
+ *specptr = unigen_special_0x1fc3; \
+ break; \
+ case 0x1fc4: \
+ *specptr = unigen_special_0x1fc4; \
+ break; \
+ case 0x1fc6: \
+ *specptr = unigen_special_0x1fc6; \
+ break; \
+ case 0x1fc7: \
+ *specptr = unigen_special_0x1fc7; \
+ break; \
+ case 0x1fcc: \
+ *specptr = unigen_special_0x1fcc; \
+ break; \
+ case 0x1fd2: \
+ *specptr = unigen_special_0x1fd2; \
+ break; \
+ case 0x1fd3: \
+ *specptr = unigen_special_0x1fd3; \
+ break; \
+ case 0x1fd6: \
+ *specptr = unigen_special_0x1fd6; \
+ break; \
+ case 0x1fd7: \
+ *specptr = unigen_special_0x1fd7; \
+ break; \
+ case 0x1fe2: \
+ *specptr = unigen_special_0x1fe2; \
+ break; \
+ case 0x1fe3: \
+ *specptr = unigen_special_0x1fe3; \
+ break; \
+ case 0x1fe4: \
+ *specptr = unigen_special_0x1fe4; \
+ break; \
+ case 0x1fe6: \
+ *specptr = unigen_special_0x1fe6; \
+ break; \
+ case 0x1fe7: \
+ *specptr = unigen_special_0x1fe7; \
+ break; \
+ case 0x1ff2: \
+ *specptr = unigen_special_0x1ff2; \
+ break; \
+ case 0x1ff3: \
+ *specptr = unigen_special_0x1ff3; \
+ break; \
+ case 0x1ff4: \
+ *specptr = unigen_special_0x1ff4; \
+ break; \
+ case 0x1ff6: \
+ *specptr = unigen_special_0x1ff6; \
+ break; \
+ case 0x1ff7: \
+ *specptr = unigen_special_0x1ff7; \
+ break; \
+ case 0x1ffc: \
+ *specptr = unigen_special_0x1ffc; \
+ break; \
+ case 0xfb00: \
+ *specptr = unigen_special_0xfb00; \
+ break; \
+ case 0xfb01: \
+ *specptr = unigen_special_0xfb01; \
+ break; \
+ case 0xfb02: \
+ *specptr = unigen_special_0xfb02; \
+ break; \
+ case 0xfb03: \
+ *specptr = unigen_special_0xfb03; \
+ break; \
+ case 0xfb04: \
+ *specptr = unigen_special_0xfb04; \
+ break; \
+ case 0xfb05: \
+ *specptr = unigen_special_0xfb05; \
+ break; \
+ case 0xfb06: \
+ *specptr = unigen_special_0xfb06; \
+ break; \
+ case 0xfb13: \
+ *specptr = unigen_special_0xfb13; \
+ break; \
+ case 0xfb14: \
+ *specptr = unigen_special_0xfb14; \
+ break; \
+ case 0xfb15: \
+ *specptr = unigen_special_0xfb15; \
+ break; \
+ case 0xfb16: \
+ *specptr = unigen_special_0xfb16; \
+ break; \
+ case 0xfb17: \
+ *specptr = unigen_special_0xfb17; \
+ break; \
+ default: \
+ *specptr = nullptr; \
+ }
+
+typedef glui32 gli_case_block_t[2]; ///< upper, lower
+typedef glui32 gli_case_special_t[3]; ///< upper, lower, title
+typedef glui32 gli_decomp_block_t[2]; ///< count, position
+
+extern gli_case_block_t unigen_case_block_0x0[256];
+extern gli_case_block_t unigen_case_block_0x1[256];
+extern gli_case_block_t unigen_case_block_0x2[256];
+extern gli_case_block_t unigen_case_block_0x3[256];
+extern gli_case_block_t unigen_case_block_0x4[256];
+extern gli_case_block_t unigen_case_block_0x5[256];
+extern gli_case_block_t unigen_case_block_0x1e[256];
+extern gli_case_block_t unigen_case_block_0x1f[256];
+extern gli_case_block_t unigen_case_block_0x21[256];
+extern gli_case_block_t unigen_case_block_0x24[256];
+extern gli_case_block_t unigen_case_block_0xfb[256];
+extern gli_case_block_t unigen_case_block_0xff[256];
+extern gli_case_block_t unigen_case_block_0x104[256];
+
+extern gli_case_special_t unigen_special_0xdf;
+extern gli_case_special_t unigen_special_0x130;
+extern gli_case_special_t unigen_special_0x149;
+extern gli_case_special_t unigen_special_0x1c4;
+extern gli_case_special_t unigen_special_0x1c5;
+extern gli_case_special_t unigen_special_0x1c6;
+extern gli_case_special_t unigen_special_0x1c7;
+extern gli_case_special_t unigen_special_0x1c8;
+extern gli_case_special_t unigen_special_0x1c9;
+extern gli_case_special_t unigen_special_0x1ca;
+extern gli_case_special_t unigen_special_0x1cb;
+extern gli_case_special_t unigen_special_0x1cc;
+extern gli_case_special_t unigen_special_0x1f0;
+extern gli_case_special_t unigen_special_0x1f1;
+extern gli_case_special_t unigen_special_0x1f2;
+extern gli_case_special_t unigen_special_0x1f3;
+extern gli_case_special_t unigen_special_0x390;
+extern gli_case_special_t unigen_special_0x3b0;
+extern gli_case_special_t unigen_special_0x587;
+extern gli_case_special_t unigen_special_0x1e96;
+extern gli_case_special_t unigen_special_0x1e97;
+extern gli_case_special_t unigen_special_0x1e98;
+extern gli_case_special_t unigen_special_0x1e99;
+extern gli_case_special_t unigen_special_0x1e9a;
+extern gli_case_special_t unigen_special_0x1f50;
+extern gli_case_special_t unigen_special_0x1f52;
+extern gli_case_special_t unigen_special_0x1f54;
+extern gli_case_special_t unigen_special_0x1f56;
+extern gli_case_special_t unigen_special_0x1f80;
+extern gli_case_special_t unigen_special_0x1f81;
+extern gli_case_special_t unigen_special_0x1f82;
+extern gli_case_special_t unigen_special_0x1f83;
+extern gli_case_special_t unigen_special_0x1f84;
+extern gli_case_special_t unigen_special_0x1f85;
+extern gli_case_special_t unigen_special_0x1f86;
+extern gli_case_special_t unigen_special_0x1f87;
+extern gli_case_special_t unigen_special_0x1f88;
+extern gli_case_special_t unigen_special_0x1f89;
+extern gli_case_special_t unigen_special_0x1f8a;
+extern gli_case_special_t unigen_special_0x1f8b;
+extern gli_case_special_t unigen_special_0x1f8c;
+extern gli_case_special_t unigen_special_0x1f8d;
+extern gli_case_special_t unigen_special_0x1f8e;
+extern gli_case_special_t unigen_special_0x1f8f;
+extern gli_case_special_t unigen_special_0x1f90;
+extern gli_case_special_t unigen_special_0x1f91;
+extern gli_case_special_t unigen_special_0x1f92;
+extern gli_case_special_t unigen_special_0x1f93;
+extern gli_case_special_t unigen_special_0x1f94;
+extern gli_case_special_t unigen_special_0x1f95;
+extern gli_case_special_t unigen_special_0x1f96;
+extern gli_case_special_t unigen_special_0x1f97;
+extern gli_case_special_t unigen_special_0x1f98;
+extern gli_case_special_t unigen_special_0x1f99;
+extern gli_case_special_t unigen_special_0x1f9a;
+extern gli_case_special_t unigen_special_0x1f9b;
+extern gli_case_special_t unigen_special_0x1f9c;
+extern gli_case_special_t unigen_special_0x1f9d;
+extern gli_case_special_t unigen_special_0x1f9e;
+extern gli_case_special_t unigen_special_0x1f9f;
+extern gli_case_special_t unigen_special_0x1fa0;
+extern gli_case_special_t unigen_special_0x1fa1;
+extern gli_case_special_t unigen_special_0x1fa2;
+extern gli_case_special_t unigen_special_0x1fa3;
+extern gli_case_special_t unigen_special_0x1fa4;
+extern gli_case_special_t unigen_special_0x1fa5;
+extern gli_case_special_t unigen_special_0x1fa6;
+extern gli_case_special_t unigen_special_0x1fa7;
+extern gli_case_special_t unigen_special_0x1fa8;
+extern gli_case_special_t unigen_special_0x1fa9;
+extern gli_case_special_t unigen_special_0x1faa;
+extern gli_case_special_t unigen_special_0x1fab;
+extern gli_case_special_t unigen_special_0x1fac;
+extern gli_case_special_t unigen_special_0x1fad;
+extern gli_case_special_t unigen_special_0x1fae;
+extern gli_case_special_t unigen_special_0x1faf;
+extern gli_case_special_t unigen_special_0x1fb2;
+extern gli_case_special_t unigen_special_0x1fb3;
+extern gli_case_special_t unigen_special_0x1fb4;
+extern gli_case_special_t unigen_special_0x1fb6;
+extern gli_case_special_t unigen_special_0x1fb7;
+extern gli_case_special_t unigen_special_0x1fbc;
+extern gli_case_special_t unigen_special_0x1fc2;
+extern gli_case_special_t unigen_special_0x1fc3;
+extern gli_case_special_t unigen_special_0x1fc4;
+extern gli_case_special_t unigen_special_0x1fc6;
+extern gli_case_special_t unigen_special_0x1fc7;
+extern gli_case_special_t unigen_special_0x1fcc;
+extern gli_case_special_t unigen_special_0x1fd2;
+extern gli_case_special_t unigen_special_0x1fd3;
+extern gli_case_special_t unigen_special_0x1fd6;
+extern gli_case_special_t unigen_special_0x1fd7;
+extern gli_case_special_t unigen_special_0x1fe2;
+extern gli_case_special_t unigen_special_0x1fe3;
+extern gli_case_special_t unigen_special_0x1fe4;
+extern gli_case_special_t unigen_special_0x1fe6;
+extern gli_case_special_t unigen_special_0x1fe7;
+extern gli_case_special_t unigen_special_0x1ff2;
+extern gli_case_special_t unigen_special_0x1ff3;
+extern gli_case_special_t unigen_special_0x1ff4;
+extern gli_case_special_t unigen_special_0x1ff6;
+extern gli_case_special_t unigen_special_0x1ff7;
+extern gli_case_special_t unigen_special_0x1ffc;
+extern gli_case_special_t unigen_special_0xfb00;
+extern gli_case_special_t unigen_special_0xfb01;
+extern gli_case_special_t unigen_special_0xfb02;
+extern gli_case_special_t unigen_special_0xfb03;
+extern gli_case_special_t unigen_special_0xfb04;
+extern gli_case_special_t unigen_special_0xfb05;
+extern gli_case_special_t unigen_special_0xfb06;
+extern gli_case_special_t unigen_special_0xfb13;
+extern gli_case_special_t unigen_special_0xfb14;
+extern gli_case_special_t unigen_special_0xfb15;
+extern gli_case_special_t unigen_special_0xfb16;
+extern gli_case_special_t unigen_special_0xfb17;
+extern glui32 unigen_special_array[];
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/utils.cpp b/engines/glk/utils.cpp
new file mode 100644
index 0000000000..12f9e304e5
--- /dev/null
+++ b/engines/glk/utils.cpp
@@ -0,0 +1,44 @@
+/* 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/utils.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+
+int strToInt(const char *s) {
+ if (!*s)
+ // No string at all
+ return 0;
+ else if (toupper(s[strlen(s) - 1]) != 'H')
+ // Standard decimal string
+ return atoi(s);
+
+ // Hexadecimal string
+ uint tmp = 0;
+ int read = sscanf(s, "%xh", &tmp);
+ if (read < 1)
+ error("strToInt failed on string \"%s\"", s);
+ return (int)tmp;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/utils.h b/engines/glk/utils.h
new file mode 100644
index 0000000000..361c65fa20
--- /dev/null
+++ b/engines/glk/utils.h
@@ -0,0 +1,51 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_UTILS_H
+#define GLK_UTILS_H
+
+#include "common/rect.h"
+#include "glk/glk_types.h"
+
+namespace Gargoyle {
+
+typedef Common::Point Point;
+
+struct Rect : public Common::Rect {
+public:
+ static Rect fromXYWH(int x, int y, int w, int h) {
+ return Rect(x, y, x + w, y + h);
+ }
+
+ Rect() : Common::Rect() {}
+ Rect(int16 w, int16 h) : Common::Rect(w, h) {}
+ Rect(int16 x1, int16 y1, int16 x2, int16 y2) : Common::Rect(x1, y1, x2, y2) {}
+};
+
+/**
+ * Converts a decimal or hexadecimal string into a number
+ */
+int strToInt(const char *s);
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/window_graphics.cpp b/engines/glk/window_graphics.cpp
new file mode 100644
index 0000000000..4eea5c0316
--- /dev/null
+++ b/engines/glk/window_graphics.cpp
@@ -0,0 +1,272 @@
+/* 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/window_graphics.h"
+#include "glk/conf.h"
+#include "glk/gargoyle.h"
+#include "glk/screen.h"
+
+namespace Gargoyle {
+
+GraphicsWindow::GraphicsWindow(Windows *windows, uint32 rock) : Window(windows, rock),
+ _w(0), _h(0), _dirty(false), _surface(nullptr) {
+ _type = wintype_Graphics;
+ Common::copy(&_bgColor[0], &_bgColor[3], _bgnd);
+}
+
+GraphicsWindow::~GraphicsWindow() {
+ delete _surface;
+}
+
+void GraphicsWindow::rearrange(const Rect &box) {
+ int newwid, newhgt;
+ int bothwid, bothhgt;
+ int oldw, oldh;
+ Graphics::ManagedSurface *newSurface;
+
+ _bbox = box;
+
+ newwid = box.width();
+ newhgt = box.height();
+ oldw = _w;
+ oldh = _h;
+
+ if (newwid <= 0 || newhgt <= 0) {
+ _w = 0;
+ _h = 0;
+ delete _surface;
+ _surface = nullptr;
+ return;
+ }
+
+ bothwid = _w;
+ if (newwid < bothwid)
+ bothwid = newwid;
+ bothhgt = _h;
+ if (newhgt < bothhgt)
+ bothhgt = newhgt;
+
+ newSurface = new Graphics::ManagedSurface(newwid, newhgt,
+ Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0));
+
+ // If the new surface is equal or bigger than the old one, copy it over
+ if (_surface && bothwid && bothhgt)
+ newSurface->blitFrom(*_surface);
+
+ delete _surface;
+ _surface = newSurface;
+ _w = newwid;
+ _h = newhgt;
+
+ touch();
+}
+
+void GraphicsWindow::touch() {
+ _dirty = true;
+ _windows->repaint(_bbox);
+}
+
+void GraphicsWindow::redraw() {
+ Screen &screen = *g_vm->_screen;
+ Window::redraw();
+
+ if (_dirty || Windows::_forceRedraw) {
+ _dirty = 0;
+
+ if (_surface)
+ screen.blitFrom(*_surface, Point(_bbox.left, _bbox.top));
+ }
+}
+
+glui32 GraphicsWindow::drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int scale,
+ glui32 imagewidth, glui32 imageheight) {
+ Picture *pic = Picture::load(image);
+ glui32 hyperlink = _attr.hyper;
+
+ if (!pic)
+ return false;
+
+ if (!_imageLoaded) {
+ g_vm->_picList->increment();
+ _imageLoaded = true;
+ }
+
+ if (!scale) {
+ imagewidth = pic->w;
+ imageheight = pic->h;
+ }
+
+ drawPicture(pic, xpos, ypos, imagewidth, imageheight, hyperlink);
+ touch();
+
+ return true;
+}
+
+void GraphicsWindow::eraseRect(bool whole, const Rect &box) {
+ int x0 = box.left, y0 = box.top, x1 = box.right, y1 = box.bottom;
+ int hx0, hx1, hy0, hy1;
+
+ if (whole) {
+ x0 = 0;
+ y0 = 0;
+ x1 = _w;
+ y1 = _h;
+ }
+
+ if (x0 < 0) x0 = 0;
+ if (y0 < 0) y0 = 0;
+ if (x1 < 0) x1 = 0;
+ if (y1 < 0) y1 = 0;
+ if ((glui32)x0 >= _w) x0 = _w;
+ if ((glui32)y0 >= _h) y0 = _h;
+ if ((glui32)x1 >= _w) x1 = _w;
+ if ((glui32)y1 >= _h) y1 = _h;
+
+ hx0 = _bbox.left + x0;
+ hx1 = _bbox.left + x1;
+ hy0 = _bbox.top + y0;
+ hy1 = _bbox.top + y1;
+
+ /* zero out hyperlinks for these coordinates */
+ g_vm->_selection->putHyperlink(0, hx0, hy0, hx1, hy1);
+
+ _surface->fillRect(Rect(x0, y0, x1, y1), MKTAG(_bgnd[0], _bgnd[1], _bgnd[2], 0));
+ touch();
+}
+
+void GraphicsWindow::fillRect(glui32 color, const Rect &box) {
+ unsigned char col[3];
+ int x0 = box.left, y0 = box.top, x1 = box.right, y1 = box.bottom;
+ int hx0, hx1, hy0, hy1;
+
+ col[0] = (color >> 16) & 0xff;
+ col[1] = (color >> 8) & 0xff;
+ col[2] = (color >> 0) & 0xff;
+
+ if (x0 < 0) x0 = 0;
+ if (y0 < 0) y0 = 0;
+ if (x1 < 0) x1 = 0;
+ if (y1 < 0) y1 = 0;
+ if ((glui32)x0 > _w) x0 = _w;
+ if ((glui32)y0 > _h) y0 = _h;
+ if ((glui32)x1 > _w) x1 = _w;
+ if ((glui32)y1 > _h) y1 = _h;
+
+ hx0 = _bbox.left + x0;
+ hx1 = _bbox.left + x1;
+ hy0 = _bbox.top + y0;
+ hy1 = _bbox.top + y1;
+
+ /* zero out hyperlinks for these coordinates */
+ g_vm->_selection->putHyperlink(0, hx0, hy0, hx1, hy1);
+
+ _surface->fillRect(Rect(x0, y0, x1, y1), MKTAG(col[0], col[1], col[2], 0));
+ touch();
+}
+
+void GraphicsWindow::drawPicture(Picture *src, int x0, int y0, int width, int height, glui32 linkval) {
+ int dx1, dy1, x1, y1, sx0, sy0, sx1, sy1;
+ int hx0, hx1, hy0, hy1;
+ int w, h;
+
+ if (width != src->w || height != src->h) {
+ src = src->scale(width, height);
+ if (!src)
+ return;
+ }
+
+ sx0 = 0;
+ sy0 = 0;
+ sx1 = src->w;
+ sy1 = src->h;
+ dx1 = _w;
+ dy1 = _h;
+
+ x1 = x0 + src->w;
+ y1 = y0 + src->h;
+
+ if (x1 <= 0 || x0 >= dx1) return;
+ if (y1 <= 0 || y0 >= dy1) return;
+ if (x0 < 0) {
+ sx0 -= x0;
+ x0 = 0;
+ }
+ if (y0 < 0) {
+ sy0 -= y0;
+ y0 = 0;
+ }
+ if (x1 > dx1) {
+ sx1 += dx1 - x1;
+ x1 = dx1;
+ }
+ if (y1 > dy1) {
+ sy1 += dy1 - y1;
+ y1 = dy1;
+ }
+
+ hx0 = _bbox.left + x0;
+ hx1 = _bbox.left + x1;
+ hy0 = _bbox.top + y0;
+ hy1 = _bbox.top + y1;
+
+ /* zero out or set hyperlink for these coordinates */
+ g_vm->_selection->putHyperlink(linkval, hx0, hy0, hx1, hy1);
+
+ w = sx1 - sx0;
+ h = sy1 - sy0;
+
+ _surface->blitFrom(*g_vm->_screen, Rect(sx0, sy0, sx0 + w, sy0 + h), Point(0, 0));
+}
+
+void GraphicsWindow::getSize(glui32 *width, glui32 *height) const {
+ *width = _bbox.width();
+ *height = _bbox.height();
+}
+
+void GraphicsWindow::setBackgroundColor(glui32 color) {
+ _bgnd[0] = (color >> 16) & 0xff;
+ _bgnd[1] = (color >> 8) & 0xff;
+ _bgnd[2] = (color >> 0) & 0xff;
+}
+
+void GraphicsWindow::click(const Point &newPos) {
+ Point diff = newPos - Point(_bbox.left, _bbox.top);
+
+ if (_mouseRequest) {
+ g_vm->_events->store(evtype_MouseInput, this, diff.x, diff.y);
+ _mouseRequest = false;
+ if (g_conf->_safeClicks)
+ g_vm->_events->_forceClick = true;
+ }
+
+ if (_hyperRequest) {
+ glui32 linkval = g_vm->_selection->getHyperlink(newPos);
+ if (linkval) {
+ g_vm->_events->store(evtype_Hyperlink, this, linkval, 0);
+ _hyperRequest = false;
+ if (g_conf->_safeClicks)
+ g_vm->_events->_forceClick = 1;
+ }
+ }
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/window_graphics.h b/engines/glk/window_graphics.h
new file mode 100644
index 0000000000..1ddc26c1cc
--- /dev/null
+++ b/engines/glk/window_graphics.h
@@ -0,0 +1,113 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_WINDOW_GRAPHICS_H
+#define GLK_WINDOW_GRAPHICS_H
+
+#include "glk/windows.h"
+#include "glk/picture.h"
+
+namespace Gargoyle {
+
+/**
+ * Graphics window
+ */
+class GraphicsWindow : public Window {
+private:
+ void touch();
+
+ void drawPicture(Picture *src, int x0, int y0, int width, int height, glui32 linkval);
+public:
+ unsigned char _bgnd[3];
+ bool _dirty;
+ glui32 _w, _h;
+ Graphics::ManagedSurface *_surface;
+public:
+ /**
+ * Constructor
+ */
+ GraphicsWindow(Windows *windows, uint32 rock);
+
+ /**
+ * Destructor
+ */
+ virtual ~GraphicsWindow();
+
+ glui32 drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int scale,
+ glui32 imagewidth, glui32 imageheight);
+
+ /**
+ * Rearranges the window
+ */
+ virtual void rearrange(const Rect &box) override;
+
+ /**
+ * Get window split size within parent pair window
+ */
+ virtual glui32 getSplit(glui32 size, bool vertical) const override {
+ return size;
+ }
+
+ /**
+ * Click the window
+ */
+ virtual void click(const Point &newPos) override;
+
+ /**
+ * Cancel a mouse event
+ */
+ virtual void cancelMouseEvent() override {
+ _mouseRequest = false;
+ }
+
+ /**
+ * Cancel a hyperlink event
+ */
+ virtual void cancelHyperlinkEvent() override {
+ _hyperRequest = false;
+ }
+
+ virtual void requestMouseEvent() override {
+ _mouseRequest = true;
+ }
+
+ virtual void requestHyperlinkEvent() override {
+ _hyperRequest = true;
+ }
+
+ /**
+ * Redraw the window
+ */
+ virtual void redraw() override;
+
+ virtual void eraseRect(bool whole, const Rect &box) override;
+
+ virtual void fillRect(glui32 color, const Rect &box) override;
+
+ virtual void getSize(glui32 *width, glui32 *height) const override;
+
+ virtual void setBackgroundColor(glui32 color) override;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/window_pair.cpp b/engines/glk/window_pair.cpp
new file mode 100644
index 0000000000..56e5b453e0
--- /dev/null
+++ b/engines/glk/window_pair.cpp
@@ -0,0 +1,236 @@
+/* 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/window_pair.h"
+#include "glk/conf.h"
+#include "glk/gargoyle.h"
+#include "glk/screen.h"
+
+namespace Gargoyle {
+
+PairWindow::PairWindow(Windows *windows, glui32 method, Window *key, glui32 size) :
+ Window(windows, 0),
+ _dir(method & winmethod_DirMask),
+ _division(method & winmethod_DivisionMask),
+ _wBorder((method & winmethod_BorderMask) == winmethod_Border),
+ _vertical(_dir == winmethod_Left || _dir == winmethod_Right),
+ _backward(_dir == winmethod_Left || _dir == winmethod_Above),
+ _key(key), _size(size), _keyDamage(0), _child1(nullptr), _child2(nullptr) {
+ _type = wintype_Pair;
+}
+
+void PairWindow::rearrange(const Rect &box) {
+ Rect box1, box2;
+ int min, diff, split, splitwid, max;
+ Window *ch1, *ch2;
+
+ _bbox = box;
+
+ if (_vertical) {
+ min = _bbox.left;
+ max = _bbox.right;
+ } else {
+ min = _bbox.top;
+ max = _bbox.bottom;
+ }
+ diff = max - min;
+
+ // We now figure split.
+ if (_vertical)
+ splitwid = g_conf->_wPaddingX; // want border?
+ else
+ splitwid = g_conf->_wPaddingY; // want border?
+
+ switch (_division) {
+ case winmethod_Proportional:
+ split = (diff * _size) / 100;
+ break;
+
+ case winmethod_Fixed:
+ split = !_key ? 0 : _key->getSplit(_size, _vertical);
+ break;
+
+ default:
+ split = diff / 2;
+ break;
+ }
+
+ if (!_backward)
+ split = max - split - splitwid;
+ else
+ split = min + split;
+
+ if (min >= max) {
+ split = min;
+ } else {
+ if (split < min)
+ split = min;
+ else if (split > max - splitwid)
+ split = max - splitwid;
+ }
+
+ if (_vertical) {
+ box1.left = _bbox.left;
+ box1.right = split;
+ box2.left = split + splitwid;
+ box2.right = _bbox.right;
+ box1.top = _bbox.top;
+ box1.bottom = _bbox.bottom;
+ box2.top = _bbox.top;
+ box2.bottom = _bbox.bottom;
+ } else {
+ box1.top = _bbox.top;
+ box1.bottom = split;
+ box2.top = split + splitwid;
+ box2.bottom = _bbox.bottom;
+ box1.left = _bbox.left;
+ box1.right = _bbox.right;
+ box2.left = _bbox.left;
+ box2.right = _bbox.right;
+ }
+
+ if (!_backward) {
+ ch1 = _child1;
+ ch2 = _child2;
+ } else {
+ ch1 = _child2;
+ ch2 = _child1;
+ }
+
+ ch1->rearrange(box1);
+ ch2->rearrange(box2);
+}
+
+void PairWindow::redraw() {
+ Window::redraw();
+
+ _child1->redraw();
+ _child2->redraw();
+
+ Window *child = !_backward ? _child1 : _child2;
+ Rect box(child->_bbox.left, child->_yAdj ? child->_bbox.top - child->_yAdj : child->_bbox.top,
+ child->_bbox.right, child->_bbox.bottom);
+
+ if (_vertical) {
+ int xBord = _wBorder ? g_conf->_wBorderX : 0;
+ int xPad = (g_conf->_wPaddingX - xBord) / 2;
+
+ g_vm->_screen->fillRect(Rect(box.right + xPad, box.top, box.right + xPad + xBord, box.bottom),
+ g_conf->_borderColor);
+ } else {
+ int yBord = _wBorder ? g_conf->_wBorderY : 0;
+ int yPad = (g_conf->_wPaddingY - yBord) / 2;
+ g_vm->_screen->fillRect(Rect(box.left, box.bottom + yPad, box.right, box.bottom + yPad + yBord),
+ g_conf->_borderColor);
+ }
+}
+
+void PairWindow::getArrangement(glui32 *method, glui32 *size, Window **keyWin) {
+ glui32 val = _dir | _division;
+ if (!_wBorder)
+ val |= winmethod_NoBorder;
+
+ if (size)
+ *size = _size;
+ if (keyWin) {
+ if (_key)
+ *keyWin = _key;
+ else
+ *keyWin = nullptr;
+ }
+
+ if (method)
+ *method = val;
+}
+
+void PairWindow::setArrangement(glui32 method, glui32 size, Window *keyWin) {
+ glui32 newDir;
+ bool newVertical, newBackward;
+
+ if (_key) {
+ Window *wx;
+ PairWindow *pairWin = dynamic_cast<PairWindow *>(_key);
+
+ if (pairWin) {
+ warning("setArrangement: keywin cannot be a Pair");
+ return;
+ }
+
+ for (wx = _key; wx; wx = wx->_parent) {
+ if (wx == this)
+ break;
+ }
+ if (wx == nullptr) {
+ warning("setArrangement: keywin must be a descendant");
+ return;
+ }
+ }
+
+ newDir = method & winmethod_DirMask;
+ newVertical = (newDir == winmethod_Left || newDir == winmethod_Right);
+ newBackward = (newDir == winmethod_Left || newDir == winmethod_Above);
+ if (!keyWin)
+ keyWin = _key;
+
+ if ((newVertical && !_vertical) || (!newVertical && _vertical)) {
+ if (!_vertical)
+ warning("setArrangement: split must stay horizontal");
+ else
+ warning("setArrangement: split must stay vertical");
+ return;
+ }
+
+ if (keyWin && dynamic_cast<BlankWindow *>(keyWin)
+ && (method & winmethod_DivisionMask) == winmethod_Fixed) {
+ warning("setArrangement: a Blank window cannot have a fixed size");
+ return;
+ }
+
+ if ((newBackward && !_backward) || (!newBackward && _backward)) {
+ // switch the children
+ Window *tmpWin = _child1;
+ _child1 = _child2;
+ _child2 = tmpWin;
+ }
+
+ // set up everything else
+ _dir = newDir;
+ _division = method & winmethod_DivisionMask;
+ _key = keyWin;
+ _size = size;
+ _wBorder = ((method & winmethod_BorderMask) == winmethod_Border);
+
+ _vertical = (_dir == winmethod_Left || _dir == winmethod_Right);
+ _backward = (_dir == winmethod_Left || _dir == winmethod_Above);
+
+ _windows->rearrange();
+}
+
+void PairWindow::click(const Point &newPos) {
+ if (_child1->_bbox.contains(newPos))
+ _child1->click(newPos);
+
+ if (_child2->_bbox.contains(newPos))
+ _child2->click(newPos);
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/window_pair.h b/engines/glk/window_pair.h
new file mode 100644
index 0000000000..11b4e499ee
--- /dev/null
+++ b/engines/glk/window_pair.h
@@ -0,0 +1,73 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_WINDOW_PAIR_H
+#define GLK_WINDOW_PAIR_H
+
+#include "glk/windows.h"
+
+namespace Gargoyle {
+
+/**
+ * Pair window
+ */
+class PairWindow : public Window {
+public:
+ Window *_child1, *_child2;
+
+ /* split info... */
+ glui32 _dir; ///< winmethod_Left, Right, Above, or Below
+ bool _vertical, _backward; ///< flags
+ glui32 _division; ///< winmethod_Fixed or winmethod_Proportional
+ Window *_key; ///< nullptr or a leaf-descendant (not a Pair)
+ int _keyDamage; ///< used as scratch space in window closing
+ glui32 _size; ///< size value
+ bool _wBorder; ///< If windows are separated by border
+public:
+ /**
+ * Constructor
+ */
+ PairWindow(Windows *windows, glui32 method, Window *key, glui32 size);
+
+ /**
+ * Rearranges the window
+ */
+ virtual void rearrange(const Rect &box) override;
+
+ /**
+ * Redraw the window
+ */
+ virtual void redraw() override;
+
+ virtual void getArrangement(glui32 *method, glui32 *size, Window **keyWin) override;
+
+ virtual void setArrangement(glui32 method, glui32 size, Window *keyWin) override;
+
+ /**
+ * Click the window
+ */
+ virtual void click(const Point &newPos) override;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
new file mode 100644
index 0000000000..1506f4e163
--- /dev/null
+++ b/engines/glk/window_text_buffer.cpp
@@ -0,0 +1,1636 @@
+/* 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/window_text_buffer.h"
+#include "glk/conf.h"
+#include "glk/gargoyle.h"
+#include "glk/screen.h"
+#include "glk/selection.h"
+#include "glk/unicode.h"
+
+namespace Gargoyle {
+
+/**
+ *
+ * How many pixels we add to left/right margins
+ */
+#define SLOP (2 * GLI_SUBPIX)
+
+
+TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windows, rock),
+ _historyPos(0), _historyFirst(0), _historyPresent(0), _lastSeen(0), _scrollPos(0),
+ _scrollMax(0), _scrollBack(SCROLLBACK), _width(-1), _height(-1), _inBuf(nullptr),
+ _lineTerminators(nullptr), _echoLineInput(true), _ladjw(0), _radjw(0), _ladjn(0),
+ _radjn(0), _numChars(0), _chars(nullptr), _attrs(nullptr),
+ _spaced(0), _dashed(0), _copyBuf(0), _copyPos(0) {
+ _type = wintype_TextBuffer;
+ Common::fill(&_history[0], &_history[HISTORYLEN], (glui32 *)nullptr);
+
+ _lines.resize(SCROLLBACK);
+ _chars = _lines[0]._chars;
+ _attrs = _lines[0]._attrs;
+
+ Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], _styles);
+}
+
+TextBufferWindow::~TextBufferWindow() {
+ if (_inBuf) {
+ if (g_vm->gli_unregister_arr)
+ (*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
+ _inBuf = nullptr;
+ }
+
+ delete[] _copyBuf;
+ delete[] _lineTerminators;
+
+ for (int i = 0; i < _scrollBack; i++) {
+ if (_lines[i]._lPic)
+ _lines[i]._lPic->decrement();
+ if (_lines[i]._rPic)
+ _lines[i]._rPic->decrement();
+ }
+}
+
+void TextBufferWindow::rearrange(const Rect &box) {
+ Window::rearrange(box);
+ int newwid, newhgt;
+ int rnd;
+
+ newwid = (box.width() - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) / g_conf->_cellW;
+ newhgt = (box.height() - g_conf->_tMarginY * 2) / g_conf->_cellH;
+
+ // align text with bottom
+ rnd = newhgt * g_conf->_cellH + g_conf->_tMarginY * 2;
+ _yAdj = (box.height() - rnd);
+ _bbox.top += (box.height() - rnd);
+
+ if (newwid != _width) {
+ _width = newwid;
+ reflow();
+ }
+
+ if (newhgt != _height) {
+ // scroll up if we obscure new lines
+ if (_lastSeen >= newhgt - 1)
+ _scrollPos += (_height - newhgt);
+
+ _height = newhgt;
+
+ // keep window within 'valid' lines
+ if (_scrollPos > _scrollMax - _height + 1)
+ _scrollPos = _scrollMax - _height + 1;
+ if (_scrollPos < 0)
+ _scrollPos = 0;
+ touchScroll();
+
+ // allocate copy buffer
+ if (_copyBuf)
+ delete[] _copyBuf;
+ _copyBuf = new glui32[_height * TBLINELEN];
+
+ for (int i = 0; i < (_height * TBLINELEN); i++)
+ _copyBuf[i] = 0;
+
+ _copyPos = 0;
+ }
+}
+
+void TextBufferWindow::reflow() {
+ int inputbyte = -1;
+ Attributes curattr, oldattr;
+ int i, k, p, s;
+ int x;
+
+ if (_height < 4 || _width < 20)
+ return;
+
+ _lines[0]._len = _numChars;
+
+ // allocate temp buffers
+ Attributes *attrbuf = new Attributes[SCROLLBACK * TBLINELEN];
+ glui32 *charbuf = new glui32[SCROLLBACK * TBLINELEN];
+ int *alignbuf = new int[SCROLLBACK];
+ Picture **pictbuf = new Picture *[SCROLLBACK];
+ glui32 *hyperbuf = new glui32[SCROLLBACK];
+ int *offsetbuf = new int[SCROLLBACK];
+
+ if (!attrbuf || !charbuf || !alignbuf || !pictbuf || !hyperbuf || !offsetbuf) {
+ delete[] attrbuf;
+ delete[] charbuf;
+ delete[] alignbuf;
+ delete[] pictbuf;
+ delete[] hyperbuf;
+ delete[] offsetbuf;
+ return;
+ }
+
+ // copy text to temp buffers
+
+ oldattr = _attr;
+ curattr.clear();
+
+ x = 0;
+ p = 0;
+ s = _scrollMax < SCROLLBACK ? _scrollMax : SCROLLBACK - 1;
+
+ for (k = s; k >= 0; k--) {
+ if (k == 0 && _lineRequest)
+ inputbyte = p + _inFence;
+
+ if (_lines[k]._lPic) {
+ offsetbuf[x] = p;
+ alignbuf[x] = imagealign_MarginLeft;
+ pictbuf[x] = _lines[k]._lPic;
+
+ if (pictbuf[x]) pictbuf[x]->increment();
+ hyperbuf[x] = _lines[k]._lHyper;
+ x++;
+ }
+
+ if (_lines[k]._rPic) {
+ offsetbuf[x] = p;
+ alignbuf[x] = imagealign_MarginRight;
+ pictbuf[x] = _lines[k]._rPic;
+ if (pictbuf[x]) pictbuf[x]->increment();
+ hyperbuf[x] = _lines[k]._rHyper;
+ x++;
+ }
+
+ for (i = 0; i < _lines[k]._len; i++) {
+ attrbuf[p] = curattr = _lines[k]._attrs[i];
+ charbuf[p] = _lines[k]._chars[i];
+ p++;
+ }
+
+ if (_lines[k]._newLine) {
+ attrbuf[p] = curattr;
+ charbuf[p] = '\n';
+ p++;
+ }
+ }
+
+ offsetbuf[x] = -1;
+
+ // clear window
+ clear();
+
+ // and dump text back
+ x = 0;
+ for (i = 0; i < p; i++) {
+ if (i == inputbyte)
+ break;
+ _attr = attrbuf[i];
+
+ if (offsetbuf[x] == i) {
+ putPicture(pictbuf[x], alignbuf[x], hyperbuf[x]);
+ x++;
+ }
+
+ putCharUni(charbuf[i]);
+ }
+
+ // terribly sorry about this...
+ _lastSeen = 0;
+ _scrollPos = 0;
+
+ if (inputbyte != -1) {
+ _inFence = _numChars;
+ putTextUni(charbuf + inputbyte, p - inputbyte, _numChars, 0);
+ _inCurs = _numChars;
+ }
+
+ // free temp buffers
+ delete[] attrbuf;
+ delete[] charbuf;
+ delete[] alignbuf;
+ delete[] pictbuf;
+ delete[] hyperbuf;
+ delete[] offsetbuf;
+
+ _attr = oldattr;
+
+ touchScroll();
+}
+
+void TextBufferWindow::touchScroll() {
+ g_vm->_selection->clearSelection();
+ _windows->repaint(_bbox);
+
+ for (int i = 0; i < _scrollMax; i++)
+ _lines[i]._dirty = true;
+}
+
+bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
+ if (align == imagealign_MarginRight) {
+ if (_lines[0]._rPic || _numChars)
+ return false;
+
+ _radjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
+ _radjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
+ _lines[0]._rPic = pic;
+ _lines[0]._rm = _radjw;
+ _lines[0]._rHyper = linkval;
+ } else {
+ if (align != imagealign_MarginLeft && _numChars)
+ putCharUni('\n');
+
+ if (_lines[0]._lPic || _numChars)
+ return false;
+
+ _ladjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
+ _ladjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
+ _lines[0]._lPic = pic;
+ _lines[0]._lm = _ladjw;
+ _lines[0]._lHyper = linkval;
+
+ if (align != imagealign_MarginLeft)
+ flowBreak();
+ }
+
+ return true;
+}
+
+glui32 TextBufferWindow::drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height) {
+ Picture *pic;
+ glui32 hyperlink;
+ int error;
+
+ pic = Picture::load(image);
+
+ if (!pic)
+ return false;
+
+ if (!_imageLoaded) {
+ g_vm->_picList->increment();
+ _imageLoaded = true;
+ }
+
+ if (scaled) {
+ Picture *tmp;
+ tmp = pic->scale(width, height);
+ pic = tmp;
+ }
+
+ hyperlink = _attr.hyper;
+
+ pic->increment();
+ error = putPicture(pic, align, hyperlink);
+
+ return error;
+}
+
+void TextBufferWindow::putText(const char *buf, int len, int pos, int oldlen) {
+ int diff = len - oldlen;
+
+ if (_numChars + diff >= TBLINELEN)
+ return;
+
+ if (diff != 0 && pos + oldlen < _numChars) {
+ memmove(_chars + pos + len,
+ _chars + pos + oldlen,
+ (_numChars - (pos + oldlen)) * 4);
+ memmove(_attrs + pos + len,
+ _attrs + pos + oldlen,
+ (_numChars - (pos + oldlen)) * sizeof(Attributes));
+ }
+ if (len > 0) {
+ for (int i = 0; i < len; i++) {
+ _chars[pos + i] = buf[i];
+ _attrs[pos + i].set(style_Input);
+ }
+ }
+ _numChars += diff;
+
+ if (_inBuf) {
+ if (_inCurs >= pos + oldlen)
+ _inCurs += diff;
+ else if (_inCurs >= pos)
+ _inCurs = pos + len;
+ }
+
+ touch(0);
+}
+
+void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldlen) {
+ int diff = len - oldlen;
+
+ if (_numChars + diff >= TBLINELEN)
+ return;
+
+ if (diff != 0 && pos + oldlen < _numChars) {
+ memmove(_chars + pos + len,
+ _chars + pos + oldlen,
+ (_numChars - (pos + oldlen)) * 4);
+ memmove(_attrs + pos + len,
+ _attrs + pos + oldlen,
+ (_numChars - (pos + oldlen)) * sizeof(Attributes));
+ }
+ if (len > 0) {
+ int i;
+ memmove(_chars + pos, buf, len * 4);
+ for (i = 0; i < len; i++)
+ _attrs[pos + i].set(style_Input);
+ }
+ _numChars += diff;
+
+ if (_inBuf) {
+ if (_inCurs >= pos + oldlen)
+ _inCurs += diff;
+ else if (_inCurs >= pos)
+ _inCurs = pos + len;
+ }
+
+ touch(0);
+}
+
+void TextBufferWindow::touch(int line) {
+ _lines[line]._dirty = true;
+ g_vm->_selection->clearSelection();
+
+ int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
+ _windows->repaint(Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
+}
+
+glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
+ return (vertical) ? size * g_conf->_cellW : size * g_conf->_cellH;
+}
+
+void TextBufferWindow::putCharUni(glui32 ch) {
+ glui32 bchars[TBLINELEN];
+ Attributes battrs[TBLINELEN];
+ int pw;
+ int bpoint;
+ int saved;
+ int i;
+ int linelen;
+ byte *color;
+
+ gli_tts_speak(&ch, 1);
+
+ pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) * GLI_SUBPIX;
+ pw = pw - 2 * SLOP - _radjw - _ladjw;
+
+ color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+
+ // oops ... overflow
+ if (_numChars + 1 >= TBLINELEN)
+ scrollOneLine(0);
+
+ if (ch == '\n') {
+ scrollOneLine(1);
+ return;
+ }
+
+ if (g_conf->_quotes) {
+ // fails for 'tis a wonderful day in the '80s
+ if (g_conf->_quotes > 1 && ch == '\'') {
+ if (_numChars == 0 || leftquote(_chars[_numChars - 1]))
+ ch = UNI_LSQUO;
+ }
+
+ if (ch == '`')
+ ch = UNI_LSQUO;
+
+ if (ch == '\'')
+ ch = UNI_RSQUO;
+
+ if (ch == '"') {
+ if (_numChars == 0 || leftquote(_chars[_numChars - 1]))
+ ch = UNI_LDQUO;
+ else
+ ch = UNI_RDQUO;
+ }
+ }
+
+ if (g_conf->_dashes && _attr.style != style_Preformatted) {
+ if (ch == '-') {
+ _dashed++;
+ if (_dashed == 2) {
+ _numChars--;
+ if (g_conf->_dashes == 2)
+ ch = UNI_NDASH;
+ else
+ ch = UNI_MDASH;
+ }
+ if (_dashed == 3) {
+ _numChars--;
+ ch = UNI_MDASH;
+ _dashed = 0;
+ }
+ } else {
+ _dashed = 0;
+ }
+ }
+
+ if (g_conf->_spaces && _attr.style != style_Preformatted
+ && _styles[_attr.style].bg == color
+ && !_styles[_attr.style].reverse) {
+ // turn (period space space) into (period space)
+ if (g_conf->_spaces == 1) {
+ if (ch == '.')
+ _spaced = 1;
+ else if (ch == ' ' && _spaced == 1)
+ _spaced = 2;
+ else if (ch == ' ' && _spaced == 2) {
+ _spaced = 0;
+ return;
+ } else {
+ _spaced = 0;
+ }
+ }
+
+ // Turn (per sp x) into (per sp sp x)
+ if (g_conf->_spaces == 2) {
+ if (ch == '.')
+ _spaced = 1;
+ else if (ch == ' ' && _spaced == 1)
+ _spaced = 2;
+ else if (ch != ' ' && _spaced == 2) {
+ _spaced = 0;
+ putCharUni(' ');
+ } else {
+ _spaced = 0;
+ }
+ }
+ }
+
+ _chars[_numChars] = ch;
+ _attrs[_numChars] = _attr;
+ _numChars++;
+
+ // kill spaces at the end for line width calculation
+ linelen = _numChars;
+ while (linelen > 1 && _chars[linelen - 1] == ' '
+ && _styles[_attrs[linelen - 1].style].bg == color
+ && !_styles[_attrs[linelen - 1].style].reverse)
+ linelen--;
+
+ if (calcWidth(_chars, _attrs, 0, linelen, -1) >= pw) {
+ bpoint = _numChars;
+
+ for (i = _numChars - 1; i > 0; i--) {
+ if (_chars[i] == ' ') {
+ bpoint = i + 1; // skip space
+ break;
+ }
+ }
+
+ saved = _numChars - bpoint;
+
+ memcpy(bchars, _chars + bpoint, saved * 4);
+ memcpy(battrs, _attrs + bpoint, saved * sizeof(Attributes));
+ _numChars = bpoint;
+
+ scrollOneLine(0);
+
+ memcpy(_chars, bchars, saved * 4);
+ memcpy(_attrs, battrs, saved * sizeof(Attributes));
+ _numChars = saved;
+ }
+
+ touch(0);
+}
+
+bool TextBufferWindow::unputCharUni(uint32 ch) {
+ if (_numChars > 0 && _chars[_numChars - 1] == ch) {
+ _numChars--;
+ touch(0);
+ return true;
+ }
+
+ return false;
+}
+
+void TextBufferWindow::clear() {
+ int i;
+
+ _attr.fgset = Windows::_overrideFgSet;
+ _attr.bgset = Windows::_overrideBgSet;
+ _attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
+ _attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
+ _attr.reverse = false;
+
+ _ladjw = _radjw = 0;
+ _ladjn = _radjn = 0;
+
+ _spaced = 0;
+ _dashed = 0;
+
+ _numChars = 0;
+
+ for (i = 0; i < _scrollBack; i++) {
+ _lines[i]._len = 0;
+
+ if (_lines[i]._lPic) _lines[i]._lPic->decrement();
+ _lines[i]._lPic = nullptr;
+ if (_lines[i]._rPic) _lines[i]._rPic->decrement();
+ _lines[i]._rPic = nullptr;
+
+ _lines[i]._lHyper = 0;
+ _lines[i]._rHyper = 0;
+ _lines[i]._lm = 0;
+ _lines[i]._rm = 0;
+ _lines[i]._newLine = 0;
+ _lines[i]._dirty = true;
+ _lines[i]._repaint = false;
+ }
+
+ _lastSeen = 0;
+ _scrollPos = 0;
+ _scrollMax = 0;
+
+ for (i = 0; i < _height; i++)
+ touch(i);
+}
+
+void TextBufferWindow::click(const Point &newPos) {
+ int gh = false;
+ int gs = false;
+
+ if (_lineRequest || _charRequest
+ || _lineRequestUni || _charRequestUni
+ || _moreRequest || _scrollRequest)
+ _windows->setFocus(this);
+
+ if (_hyperRequest) {
+ glui32 linkval = g_vm->_selection->getHyperlink(newPos);
+ if (linkval) {
+ g_vm->_events->store(evtype_Hyperlink, this, linkval, 0);
+ _hyperRequest = false;
+ if (g_conf->_safeClicks)
+ g_vm->_events->_forceClick = 1;
+ gh = true;
+ }
+ }
+
+ if (newPos.x > _bbox.right - g_conf->_scrollWidth) {
+ if (newPos.y < _bbox.top + g_conf->_tMarginY + g_conf->_scrollWidth)
+ acceptScroll(keycode_Up);
+ else if (newPos.y > _bbox.bottom - g_conf->_tMarginY - g_conf->_scrollWidth)
+ acceptScroll(keycode_Down);
+ else if (newPos.y < (_bbox.top + _bbox.bottom) / 2)
+ acceptScroll(keycode_PageUp);
+ else
+ acceptScroll(keycode_PageDown);
+ gs = true;
+ }
+
+ if (!gh && !gs) {
+ g_vm->_copySelect = true;
+ g_vm->_selection->startSelection(newPos);
+ }
+}
+
+void TextBufferWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
+ if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
+ warning("request_line_event: window already has keyboard request");
+ return;
+ }
+
+ _lineRequest = true;
+ int pw;
+
+ gli_tts_flush();
+
+ // because '>' prompt is ugly without extra space
+ if (_numChars && _chars[_numChars - 1] == '>')
+ putCharUni(' ');
+ if (_numChars && _chars[_numChars - 1] == '?')
+ putCharUni(' ');
+
+ // make sure we have some space left for typing...
+ pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2) * GLI_SUBPIX;
+ pw = pw - 2 * SLOP - _radjw + _ladjw;
+ if (calcWidth(_chars, _attrs, 0, _numChars, -1) >= pw * 3 / 4)
+ putCharUni('\n');
+
+ _inBuf = buf;
+ _inMax = maxlen;
+ _inFence = _numChars;
+ _inCurs = _numChars;
+ _origAttr = _attr;
+ _attr.set(style_Input);
+
+ _historyPos = _historyPresent;
+
+ if (initlen) {
+ touch(0);
+ putText(buf, initlen, _inCurs, 0);
+ }
+
+ // WORKAROUND: Mark bottom line as dirty so caret will be drawn
+ _lines[0]._dirty = true;
+
+ _echoLineInput = _echoLineInputBase;
+
+ if (_lineTerminatorsBase && _termCt) {
+ _lineTerminators = new glui32[_termCt + 1];
+
+ if (_lineTerminators) {
+ memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+ _lineTerminators[_termCt] = 0;
+ }
+ }
+
+ if (g_vm->gli_register_arr)
+ _inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Cn");
+}
+
+void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
+ if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
+ warning("request_line_event_uni: window already has keyboard request");
+ return;
+ }
+
+ int pw;
+
+ gli_tts_flush();
+
+ // because '>' prompt is ugly without extra space
+ if (_numChars && _chars[_numChars - 1] == '>')
+ putCharUni(' ');
+ if (_numChars && _chars[_numChars - 1] == '?')
+ putCharUni(' ');
+
+ // make sure we have some space left for typing...
+ pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2) * GLI_SUBPIX;
+ pw = pw - 2 * SLOP - _radjw + _ladjw;
+ if (calcWidth(_chars, _attrs, 0, _numChars, -1) >= pw * 3 / 4)
+ putCharUni('\n');
+
+ //_lastSeen = 0;
+
+ _inBuf = buf;
+ _inMax = maxlen;
+ _inFence = _numChars;
+ _inCurs = _numChars;
+ _origAttr = _attr;
+ _attr.set(style_Input);
+
+ _historyPos = _historyPresent;
+
+ if (initlen) {
+ touch(0);
+ putTextUni(buf, initlen, _inCurs, 0);
+ }
+
+ // WORKAROUND: Mark bottom line as dirty so caret will be drawn
+ _lines[0]._dirty = true;
+
+ _echoLineInput = _echoLineInputBase;
+
+ if (_lineTerminatorsBase && _termCt) {
+ _lineTerminators = new glui32[_termCt + 1];
+
+ if (_lineTerminators) {
+ memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+ _lineTerminators[_termCt] = 0;
+ }
+ }
+
+ if (g_vm->gli_register_arr)
+ _inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Iu");
+}
+
+void TextBufferWindow::cancelLineEvent(Event *ev) {
+ gidispatch_rock_t inarrayrock;
+ int ix;
+ int len;
+ void *inbuf;
+ int inmax;
+ int unicode = _lineRequestUni;
+ Event dummyEv;
+
+ if (!ev)
+ ev = &dummyEv;
+
+ ev->clear();
+
+ if (!_lineRequest && !_lineRequestUni)
+ return;
+
+ if (!_inBuf)
+ return;
+
+ inbuf = _inBuf;
+ inmax = _inMax;
+ inarrayrock = _inArrayRock;
+
+ len = _numChars - _inFence;
+ if (_echoStream)
+ _echoStream->echoLineUni(_chars + _inFence, len);
+
+ if (len > inmax)
+ len = inmax;
+
+ if (!unicode) {
+ for (ix = 0; ix < len; ix++) {
+ glui32 ch = _chars[_inFence + ix];
+ if (ch > 0xff)
+ ch = '?';
+ ((char *)inbuf)[ix] = (char)ch;
+ }
+ } else {
+ for (ix = 0; ix < len; ix++)
+ ((glui32 *)inbuf)[ix] = _chars[_inFence + ix];
+ }
+
+ _attr = _origAttr;
+
+ ev->type = evtype_LineInput;
+ ev->window = this;
+ ev->val1 = len;
+ ev->val2 = 0;
+
+ _lineRequest = false;
+ _lineRequestUni = false;
+ if (_lineTerminators) {
+ free(_lineTerminators);
+ _lineTerminators = nullptr;
+ }
+ _inBuf = nullptr;
+ _inMax = 0;
+
+ if (_echoLineInput) {
+ putCharUni('\n');
+ } else {
+ _numChars = _inFence;
+ touch(0);
+ }
+
+ if (g_vm->gli_unregister_arr)
+ (*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
+}
+
+void TextBufferWindow::redraw() {
+ TextBufferRow *ln;
+ int linelen;
+ int nsp, spw, pw;
+ int x0, y0, x1, y1;
+ int x, y, w;
+ int a, b;
+ glui32 link;
+ int font;
+ unsigned char *color;
+ int i;
+ int hx0, hx1, hy0, hy1;
+ int selrow, selchar, sx0, sx1, selleft, selright;
+ bool selBuf;
+ int tx, tsc, tsw, lsc, rsc;
+ Screen &screen = *g_vm->_screen;
+
+ Window::redraw();
+
+ _lines[0]._len = _numChars;
+ sx0 = sx1 = selleft = selright = 0;
+
+ ln = new TextBufferRow();
+ if (!ln)
+ return;
+
+ x0 = (_bbox.left + g_conf->_tMarginX) * GLI_SUBPIX;
+ x1 = (_bbox.right - g_conf->_tMarginX - g_conf->_scrollWidth) * GLI_SUBPIX;
+ y0 = _bbox.top + g_conf->_tMarginY;
+ y1 = _bbox.bottom - g_conf->_tMarginY;
+
+ pw = x1 - x0 - 2 * GLI_SUBPIX;
+
+ // check if any part of buffer is selected
+ selBuf = g_vm->_selection->checkSelection(Rect(x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1));
+
+ for (i = _scrollPos + _height - 1; i >= _scrollPos; i--) {
+ // top of line
+ y = y0 + (_height - (i - _scrollPos) - 1) * g_conf->_leading;
+
+ // check if part of line is selected
+ if (selBuf) {
+ selrow = g_vm->_selection->getSelection(Rect(x0 / GLI_SUBPIX, y,
+ x1 / GLI_SUBPIX, y + g_conf->_leading), &sx0, &sx1);
+ selleft = (sx0 == x0 / GLI_SUBPIX);
+ selright = (sx1 == x1 / GLI_SUBPIX);
+ } else {
+ selrow = false;
+ }
+
+ // mark selected line dirty
+ if (selrow)
+ _lines[i]._dirty = true;
+
+ memcpy(ln, &_lines[i], sizeof(TextBufferRow));
+
+ // skip if we can
+ if (!ln->_dirty && !ln->_repaint && !Windows::_forceRedraw && _scrollPos == 0)
+ continue;
+
+ // repaint previously selected lines if needed
+ if (ln->_repaint && !Windows::_forceRedraw)
+ _windows->redrawRect(Rect(x0 / GLI_SUBPIX, y,
+ x1 / GLI_SUBPIX, y + g_conf->_leading));
+
+ // keep selected line dirty and flag for repaint
+ if (!selrow) {
+ _lines[i]._dirty = false;
+ _lines[i]._repaint = false;
+ } else {
+ _lines[i]._repaint = true;
+ }
+
+ // leave bottom line blank for [more] prompt
+ if (i == _scrollPos && i > 0)
+ continue;
+
+ linelen = ln->_len;
+
+ // kill spaces at the end unless they're a different color
+ color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+ while (i > 0 && linelen > 1 && ln->_chars[linelen - 1] == ' '
+ && _styles[ln->_attrs[linelen - 1].style].bg == color
+ && !_styles[ln->_attrs[linelen - 1].style].reverse)
+ linelen --;
+
+ // kill characters that would overwrite the scroll bar
+ while (linelen > 1 && calcWidth(ln->_chars, ln->_attrs, 0, linelen, -1) >= pw)
+ linelen --;
+
+ /*
+ * count spaces and width for justification
+ */
+ if (g_conf->_justify && !ln->_newLine && i > 0) {
+ for (a = 0, nsp = 0; a < linelen; a++)
+ if (ln->_chars[a] == ' ')
+ nsp ++;
+ w = calcWidth(ln->_chars, ln->_attrs, 0, linelen, 0);
+ if (nsp)
+ spw = (x1 - x0 - ln->_lm - ln->_rm - 2 * SLOP - w) / nsp;
+ else
+ spw = 0;
+ } else {
+ spw = -1;
+ }
+
+ // find and highlight selected characters
+ if (selrow && !Windows::_claimSelect) {
+ lsc = 0;
+ rsc = 0;
+ selchar = false;
+ // optimized case for all chars selected
+ if (selleft && selright) {
+ rsc = linelen > 0 ? linelen - 1 : 0;
+ selchar = calcWidth(ln->_chars, ln->_attrs, lsc, rsc, spw) / GLI_SUBPIX;
+ } else {
+ // optimized case for leftmost char selected
+ if (selleft) {
+ tsc = linelen > 0 ? linelen - 1 : 0;
+ selchar = calcWidth(ln->_chars, ln->_attrs, lsc, tsc, spw) / GLI_SUBPIX;
+ } else {
+ // find the substring contained by the selection
+ tx = (x0 + SLOP + ln->_lm) / GLI_SUBPIX;
+ // measure string widths until we find left char
+ for (tsc = 0; tsc < linelen; tsc++) {
+ tsw = calcWidth(ln->_chars, ln->_attrs, 0, tsc, spw) / GLI_SUBPIX;
+ if (tsw + tx >= sx0 ||
+ ((tsw + tx + GLI_SUBPIX) >= sx0 && ln->_chars[tsc] != ' ')) {
+ lsc = tsc;
+ selchar = true;
+ break;
+ }
+ }
+ }
+ if (selchar) {
+ // optimized case for rightmost char selected
+ if (selright) {
+ rsc = linelen > 0 ? linelen - 1 : 0;
+ } else {
+ // measure string widths until we find right char
+ for (tsc = lsc; tsc < linelen; tsc++) {
+ tsw = calcWidth(ln->_chars, ln->_attrs, lsc, tsc, spw) / GLI_SUBPIX;
+ if (tsw + sx0 < sx1)
+ rsc = tsc;
+ }
+ if (lsc && !rsc)
+ rsc = lsc;
+ }
+ }
+ }
+ // reverse colors for selected chars
+ if (selchar) {
+ for (tsc = lsc; tsc <= rsc; tsc++) {
+ ln->_attrs[tsc].reverse = !ln->_attrs[tsc].reverse;
+ _copyBuf[_copyPos] = ln->_chars[tsc];
+ _copyPos++;
+ }
+ }
+ // add newline if we reach the end of the line
+ if (ln->_len == 0 || ln->_len == (rsc + 1)) {
+ _copyBuf[_copyPos] = '\n';
+ _copyPos++;
+ }
+ }
+
+ // clear any stored hyperlink coordinates
+ g_vm->_selection->putHyperlink(0, x0 / GLI_SUBPIX, y,
+ x1 / GLI_SUBPIX, y + g_conf->_leading);
+
+ /*
+ * fill in background colors
+ */
+ color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+ screen.fillRect(Rect::fromXYWH(x0 / GLI_SUBPIX, y, (x1 - x0) / GLI_SUBPIX, g_conf->_leading),
+ color);
+
+ x = x0 + SLOP + ln->_lm;
+ a = 0;
+ for (b = 0; b < linelen; b++) {
+ if (ln->_attrs[a] != ln->_attrs[b]) {
+ link = ln->_attrs[a].hyper;
+ font = ln->_attrs[a].attrFont(_styles);
+ color = ln->_attrs[a].attrBg(_styles);
+ w = screen.stringWidthUni(font, Common::U32String(ln->_chars + a, b - a), spw);
+ screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, w / GLI_SUBPIX, g_conf->_leading),
+ color);
+ if (link) {
+ screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+ w / GLI_SUBPIX + 1, g_conf->_linkStyle), g_conf->_linkColor);
+ g_vm->_selection->putHyperlink(link, x / GLI_SUBPIX, y,
+ x / GLI_SUBPIX + w / GLI_SUBPIX,
+ y + g_conf->_leading);
+ }
+ x += w;
+ a = b;
+ }
+ }
+ link = ln->_attrs[a].hyper;
+ font = ln->_attrs[a].attrFont(_styles);
+ color = ln->_attrs[a].attrBg(_styles);
+ w = screen.stringWidthUni(font, Common::U32String(ln->_chars + a, b - a), spw);
+ screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, w / GLI_SUBPIX, g_conf->_leading), color);
+ if (link) {
+ screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+ w / GLI_SUBPIX + 1, g_conf->_linkStyle), g_conf->_linkColor);
+ g_vm->_selection->putHyperlink(link, x / GLI_SUBPIX, y,
+ x / GLI_SUBPIX + w / GLI_SUBPIX,
+ y + g_conf->_leading);
+ }
+ x += w;
+
+ color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+ screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, x1 / GLI_SUBPIX - x / GLI_SUBPIX, g_conf->_leading), color);
+
+ /*
+ * draw caret
+ */
+
+ if (_windows->getFocusWindow() == this && i == 0 && (_lineRequest || _lineRequestUni)) {
+ w = calcWidth(_chars, _attrs, 0, _inCurs, spw);
+ if (w < pw - g_conf->_caretShape * 2 * GLI_SUBPIX)
+ screen.drawCaret(Point(x0 + SLOP + ln->_lm + w, y + g_conf->_baseLine));
+ }
+
+ /*
+ * draw text
+ */
+
+ x = x0 + SLOP + ln->_lm;
+ a = 0;
+ for (b = 0; b < linelen; b++) {
+ if (ln->_attrs[a] != ln->_attrs[b]) {
+ link = ln->_attrs[a].hyper;
+ font = ln->_attrs[a].attrFont(_styles);
+ color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
+ x = screen.drawStringUni(Point(x, y + g_conf->_baseLine),
+ font, color, Common::U32String(ln->_chars + a, b - a), spw);
+ a = b;
+ }
+ }
+ link = ln->_attrs[a].hyper;
+ font = ln->_attrs[a].attrFont(_styles);
+ color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
+ screen.drawStringUni(Point(x, y + g_conf->_baseLine), font, color, Common::U32String(ln->_chars + a, linelen - a), spw);
+ }
+
+ /*
+ * draw more prompt
+ */
+ if (_scrollPos && _height > 1) {
+ x = x0 + SLOP;
+ y = y0 + (_height - 1) * g_conf->_leading;
+
+ g_vm->_selection->putHyperlink(0, x0 / GLI_SUBPIX, y,
+ x1 / GLI_SUBPIX, y + g_conf->_leading);
+
+ color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+ screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, x1 / GLI_SUBPIX - x / GLI_SUBPIX, g_conf->_leading), color);
+
+ w = screen.stringWidth(g_conf->_moreFont, g_conf->_morePrompt);
+
+ if (g_conf->_moreAlign == 1)
+ // center
+ x = x0 + SLOP + (x1 - x0 - w - SLOP * 2) / 2;
+ if (g_conf->_moreAlign == 2)
+ // right
+ x = x1 - SLOP - w;
+
+ color = Windows::_overrideFgSet ? g_conf->_moreColor : _fgColor;
+ screen.drawString(Point(x, y + g_conf->_baseLine),
+ g_conf->_moreFont, color, g_conf->_morePrompt);
+ y1 = y; // don't want pictures overdrawing "[more]"
+
+ // try to claim the focus
+ _moreRequest = true;
+ Windows::_moreFocus = true;
+ } else {
+ _moreRequest = false;
+ y1 = y0 + _height * g_conf->_leading;
+ }
+
+ /*
+ * draw the images
+ */
+ for (i = 0; i < _scrollBack; i++) {
+ memcpy(ln, &_lines[i], sizeof(TextBufferRow));
+
+ y = y0 + (_height - (i - _scrollPos) - 1) * g_conf->_leading;
+
+ if (ln->_lPic) {
+ if (y < y1 && y + ln->_lPic->h > y0) {
+ ln->_lPic->drawPicture(x0 / GLI_SUBPIX, y, x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1);
+ link = ln->_lHyper;
+ hy0 = y > y0 ? y : y0;
+ hy1 = y + ln->_lPic->h < y1 ? y + ln->_lPic->h : y1;
+ hx0 = x0 / GLI_SUBPIX;
+ hx1 = x0 / GLI_SUBPIX + ln->_lPic->w < x1 / GLI_SUBPIX
+ ? x0 / GLI_SUBPIX + ln->_lPic->w
+ : x1 / GLI_SUBPIX;
+ g_vm->_selection->putHyperlink(link, hx0, hy0, hx1, hy1);
+ }
+ }
+
+ if (ln->_rPic) {
+ if (y < y1 && y + ln->_rPic->h > y0) {
+ ln->_rPic->drawPicture(x1 / GLI_SUBPIX - ln->_rPic->w, y,
+ x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1);
+ link = ln->_rHyper;
+ hy0 = y > y0 ? y : y0;
+ hy1 = y + ln->_rPic->h < y1 ? y + ln->_rPic->h : y1;
+ hx0 = x1 / GLI_SUBPIX - ln->_rPic->w > x0 / GLI_SUBPIX
+ ? x1 / GLI_SUBPIX - ln->_rPic->w
+ : x0 / GLI_SUBPIX;
+ hx1 = x1 / GLI_SUBPIX;
+ g_vm->_selection->putHyperlink(link, hx0, hy0, hx1, hy1);
+ }
+ }
+ }
+
+ /*
+ * Draw the scrollbar
+ */
+
+ // try to claim scroll keys
+ _scrollRequest = _scrollMax > _height;
+
+ if (_scrollRequest && g_conf->_scrollWidth) {
+ int t0, t1;
+ x0 = _bbox.right - g_conf->_scrollWidth;
+ x1 = _bbox.right;
+ y0 = _bbox.top + g_conf->_tMarginY;
+ y1 = _bbox.bottom - g_conf->_tMarginY;
+
+ g_vm->_selection->putHyperlink(0, x0, y0, x1, y1);
+
+ y0 += g_conf->_scrollWidth / 2;
+ y1 -= g_conf->_scrollWidth / 2;
+
+ // pos = thbot, pos - ht = thtop, max = wtop, 0 = wbot
+ t0 = (_scrollMax - _scrollPos) - (_height - 1);
+ t1 = (_scrollMax - _scrollPos);
+ if (_scrollMax > _height) {
+ t0 = t0 * (y1 - y0) / _scrollMax + y0;
+ t1 = t1 * (y1 - y0) / _scrollMax + y0;
+ } else {
+ t0 = t1 = y0;
+ }
+
+ screen.fillRect(Rect::fromXYWH(x0 + 1, y0, x1 - x0 - 2, y1 - y0), g_conf->_scrollBg);
+ screen.fillRect(Rect::fromXYWH(x0 + 1, t0, x1 - x0 - 2, t1 - t0), g_conf->_scrollFg);
+
+ for (i = 0; i < g_conf->_scrollWidth / 2 + 1; i++) {
+ screen.fillRect(Rect::fromXYWH(x0 + g_conf->_scrollWidth / 2 - i,
+ y0 - g_conf->_scrollWidth / 2 + i, i * 2, 1), g_conf->_scrollFg);
+ screen.fillRect(Rect::fromXYWH(x0 + g_conf->_scrollWidth / 2 - i,
+ y1 + g_conf->_scrollWidth / 2 - i, i * 2, 1), g_conf->_scrollFg);
+ }
+ }
+
+ // Keep track of selected text to be ready when user copies it to the clipboard
+ if (selBuf && _copyPos) {
+ Windows::_claimSelect = true;
+
+ g_vm->_clipboard->clipboardStore(Common::U32String(_copyBuf, _copyPos));
+ for (i = 0; i < _copyPos; i++)
+ _copyBuf[i] = 0;
+ _copyPos = 0;
+ }
+
+ // no more prompt means all text has been seen
+ if (!_moreRequest)
+ _lastSeen = 0;
+
+ delete ln;
+}
+
+int TextBufferWindow::acceptScroll(glui32 arg) {
+ int pageht = _height - 2; // 1 for prompt, 1 for overlap
+ int startpos = _scrollPos;
+
+ switch (arg) {
+ case keycode_PageUp:
+ _scrollPos += pageht;
+ break;
+ case keycode_End:
+ _scrollPos = 0;
+ break;
+ case keycode_Up:
+ _scrollPos++;
+ break;
+ case keycode_Down:
+ case keycode_Return:
+ _scrollPos--;
+ break;
+ case keycode_MouseWheelUp:
+ _scrollPos += 3;
+ startpos = true;
+ break;
+ case keycode_MouseWheelDown:
+ _scrollPos -= 3;
+ startpos = true;
+ break;
+ case ' ':
+ case keycode_PageDown:
+ //default:
+ if (pageht)
+ _scrollPos -= pageht;
+ else
+ _scrollPos = 0;
+ break;
+ }
+
+ if (_scrollPos > _scrollMax - _height + 1)
+ _scrollPos = _scrollMax - _height + 1;
+ if (_scrollPos < 0)
+ _scrollPos = 0;
+ touchScroll();
+
+ return (startpos || _scrollPos);
+}
+
+void TextBufferWindow::acceptReadChar(glui32 arg) {
+ glui32 key;
+
+ if (_height < 2)
+ _scrollPos = 0;
+
+ if (_scrollPos
+ || arg == keycode_PageUp
+ || arg == keycode_MouseWheelUp) {
+ acceptScroll(arg);
+ return;
+ }
+
+ switch (arg) {
+ case keycode_Erase:
+ key = keycode_Delete;
+ break;
+ case keycode_MouseWheelUp:
+ case keycode_MouseWheelDown:
+ return;
+ default:
+ key = arg;
+ break;
+ }
+
+ gli_tts_purge();
+
+ if (key > 0xff && key < (0xffffffff - keycode_MAXVAL + 1)) {
+ if (!(_charRequestUni) || key > 0x10ffff)
+ key = keycode_Unknown;
+ }
+
+ _charRequest = false;
+ _charRequestUni = false;
+ g_vm->_events->store(evtype_CharInput, this, key, 0);
+}
+
+void TextBufferWindow::acceptReadLine(glui32 arg) {
+ glui32 *cx;
+ int len;
+
+ if (_height < 2)
+ _scrollPos = 0;
+
+ if (_scrollPos || arg == keycode_PageUp || arg == keycode_MouseWheelUp) {
+ acceptScroll(arg);
+ return;
+ }
+
+ if (!_inBuf)
+ return;
+
+ if (_lineTerminators && checkTerminator(arg)) {
+ for (cx = _lineTerminators; *cx; cx++) {
+ if (*cx == arg) {
+ acceptLine(arg);
+ return;
+ }
+ }
+ }
+
+ switch (arg) {
+ // History keys (up and down)
+ case keycode_Up:
+ if (_historyPos == _historyFirst)
+ return;
+ if (_historyPos == _historyPresent) {
+ len = _numChars - _inFence;
+ if (len > 0) {
+ cx = new glui32[len + 1];
+ memcpy(cx, &(_chars[_inFence]), len * 4);
+ cx[len] = 0;
+ } else {
+ cx = nullptr;
+ }
+ if (_history[_historyPos])
+ free(_history[_historyPos]);
+ _history[_historyPos] = cx;
+ }
+ _historyPos--;
+ if (_historyPos < 0)
+ _historyPos += HISTORYLEN;
+ cx = _history[_historyPos];
+ putTextUni(cx, cx ? strlen_uni(cx) : 0, _inFence,
+ _numChars - _inFence);
+ break;
+
+ case keycode_Down:
+ if (_historyPos == _historyPresent)
+ return;
+ _historyPos++;
+ if (_historyPos >= HISTORYLEN)
+ _historyPos -= HISTORYLEN;
+ cx = _history[_historyPos];
+ putTextUni(cx, cx ? strlen_uni(cx) : 0, _inFence,
+ _numChars - _inFence);
+ break;
+
+ // Cursor movement keys, during line input.
+ case keycode_Left:
+ if (_inCurs <= _inFence)
+ return;
+ _inCurs--;
+ break;
+
+ case keycode_Right:
+ if (_inCurs >= _numChars)
+ return;
+ _inCurs++;
+ break;
+
+ case keycode_Home:
+ if (_inCurs <= _inFence)
+ return;
+ _inCurs = _inFence;
+ break;
+
+ case keycode_End:
+ if (_inCurs >= _numChars)
+ return;
+ _inCurs = _numChars;
+ break;
+
+ case keycode_SkipWordLeft:
+ while (_inCurs > _inFence && _chars[_inCurs - 1] == ' ')
+ _inCurs--;
+ while (_inCurs > _inFence && _chars[_inCurs - 1] != ' ')
+ _inCurs--;
+ break;
+
+ case keycode_SkipWordRight:
+ while (_inCurs < _numChars && _chars[_inCurs] != ' ')
+ _inCurs++;
+ while (_inCurs < _numChars && _chars[_inCurs] == ' ')
+ _inCurs++;
+ break;
+
+ // Delete keys, during line input.
+ case keycode_Delete:
+ if (_inCurs <= _inFence)
+ return;
+ putTextUni(nullptr, 0, _inCurs - 1, 1);
+ break;
+
+ case keycode_Erase:
+ if (_inCurs >= _numChars)
+ return;
+ putTextUni(nullptr, 0, _inCurs, 1);
+ break;
+
+ case keycode_Escape:
+ if (_inFence >= _numChars)
+ return;
+ putTextUni(nullptr, 0, _inFence, _numChars - _inFence);
+ break;
+
+ // Regular keys
+ case keycode_Return:
+ acceptLine(arg);
+ break;
+
+ default:
+ if (arg >= 32 && arg <= 0x10FFFF) {
+ if (g_conf->_caps && (arg > 0x60 && arg < 0x7b))
+ arg -= 0x20;
+ putTextUni(&arg, 1, _inCurs, 0);
+ }
+ break;
+ }
+
+ touch(0);
+}
+
+void TextBufferWindow::acceptLine(glui32 keycode) {
+ int ix;
+ int len, olen;
+ void *inbuf;
+ glui32 *s, *o;
+ int inmax;
+ gidispatch_rock_t inarrayrock;
+ int unicode = _lineRequestUni;
+
+ if (!_inBuf)
+ return;
+
+ inbuf = _inBuf;
+ inmax = _inMax;
+ inarrayrock = _inArrayRock;
+
+ len = _numChars - _inFence;
+ if (_echoStream)
+ _echoStream->echoLineUni(_chars + _inFence, len);
+
+ gli_tts_purge();
+ if (g_conf->_speakInput) {
+ const uint32 NEWLINE = '\n';
+ gli_tts_speak(_chars + _inFence, len);
+ gli_tts_speak((const glui32 *)&NEWLINE, 1);
+ }
+
+ /*
+ * Store in history.
+ * The history is a ring buffer, with historypresent being the index of the most recent
+ * element and historyfirst the index of the oldest element.
+ * A history entry should not repeat the string from the entry before it.
+ */
+ if (len) {
+ s = new glui32[len + 1];
+ memcpy(s, _chars + _inFence, len * sizeof(glui32));
+ s[len] = 0;
+
+ free(_history[_historyPresent]);
+ _history[_historyPresent] = nullptr;
+
+ o = _history[(_historyPresent == 0 ? HISTORYLEN : _historyPresent) - 1];
+ olen = o ? strlen_uni(o) : 0;
+
+ if (len != olen || memcmp(s, o, olen * sizeof(glui32))) {
+ _history[_historyPresent] = s;
+
+ _historyPresent++;
+ if (_historyPresent == HISTORYLEN)
+ _historyPresent = 0;
+
+ if (_historyPresent == _historyFirst) {
+ _historyFirst++;
+ if (_historyFirst == HISTORYLEN)
+ _historyFirst = 0;
+ }
+ } else {
+ free(s);
+ }
+ }
+
+ // Store in event buffer.
+ if (len > inmax)
+ len = inmax;
+
+ if (!unicode) {
+ for (ix = 0; ix < len; ix++) {
+ glui32 ch = _chars[_inFence + ix];
+ if (ch > 0xff)
+ ch = '?';
+ ((char *)inbuf)[ix] = (char)ch;
+ }
+ } else {
+ for (ix = 0; ix < len; ix++)
+ ((glui32 *)inbuf)[ix] = _chars[_inFence + ix];
+ }
+
+ _attr = _origAttr;
+
+ if (_lineTerminators) {
+ glui32 val2 = keycode;
+ if (val2 == keycode_Return)
+ val2 = 0;
+ g_vm->_events->store(evtype_LineInput, this, len, val2);
+ free(_lineTerminators);
+ _lineTerminators = nullptr;
+ } else {
+ g_vm->_events->store(evtype_LineInput, this, len, 0);
+ }
+
+ _lineRequest = false;
+ _lineRequestUni = false;
+ _inBuf = nullptr;
+ _inMax = 0;
+
+ if (_echoLineInput) {
+ putCharUni('\n');
+ } else {
+ _numChars = _inFence;
+ touch(0);
+ }
+
+ if (g_vm->gli_unregister_arr)
+ (*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
+}
+
+bool TextBufferWindow::leftquote(glui32 c) {
+ switch (c) {
+ case '(':
+ case '[':
+
+ // The following are Unicode characters in the "Separator, Space" category.
+ case 0x0020:
+ case 0x00a0:
+ case 0x1680:
+ case 0x2000:
+ case 0x2001:
+ case 0x2002:
+ case 0x2003:
+ case 0x2004:
+ case 0x2005:
+ case 0x2006:
+ case 0x2007:
+ case 0x2008:
+ case 0x2009:
+ case 0x200a:
+ case 0x202f:
+ case 0x205f:
+ case 0x3000:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void TextBufferWindow::scrollOneLine(bool forced) {
+ _lastSeen++;
+ _scrollMax++;
+
+ if (_scrollMax > _scrollBack - 1
+ || _lastSeen > _scrollBack - 1)
+ scrollResize();
+
+ if (_lastSeen >= _height)
+ _scrollPos++;
+
+ if (_scrollPos > _scrollMax - _height + 1)
+ _scrollPos = _scrollMax - _height + 1;
+ if (_scrollPos < 0)
+ _scrollPos = 0;
+
+ if (forced)
+ _dashed = 0;
+ _spaced = 0;
+
+ _lines[0]._len = _numChars;
+ _lines[0]._newLine = forced;
+
+ for (int i = _scrollBack - 1; i > 0; i--) {
+ memcpy(&_lines[i], &_lines[i - 1], sizeof(TextBufferRow));
+ if (i < _height)
+ touch(i);
+ }
+
+ if (_radjn)
+ _radjn--;
+ if (_radjn == 0)
+ _radjw = 0;
+ if (_ladjn)
+ _ladjn--;
+ if (_ladjn == 0)
+ _ladjw = 0;
+
+ touch(0);
+ _lines[0]._len = 0;
+ _lines[0]._newLine = 0;
+ _lines[0]._lm = _ladjw;
+ _lines[0]._rm = _radjw;
+ _lines[0]._lPic = nullptr;
+ _lines[0]._rPic = nullptr;
+ _lines[0]._lHyper = 0;
+ _lines[0]._rHyper = 0;
+ memset(_chars, ' ', TBLINELEN * 4);
+ memset(_attrs, 0, TBLINELEN * sizeof(Attributes));
+
+ _numChars = 0;
+
+ touchScroll();
+
+}
+
+void TextBufferWindow::scrollResize() {
+ int i;
+
+ _lines.clear();
+ _lines.resize(_scrollBack + SCROLLBACK);
+
+ _chars = _lines[0]._chars;
+ _attrs = _lines[0]._attrs;
+
+ for (i = _scrollBack; i < (_scrollBack + SCROLLBACK); i++) {
+ _lines[i]._dirty = false;
+ _lines[i]._repaint = false;
+ _lines[i]._lm = 0;
+ _lines[i]._rm = 0;
+ _lines[i]._lPic = 0;
+ _lines[i]._rPic = 0;
+ _lines[i]._lHyper = 0;
+ _lines[i]._rHyper = 0;
+ _lines[i]._len = 0;
+ _lines[i]._newLine = 0;
+ memset(_lines[i]._chars, ' ', sizeof _lines[i]._chars);
+ memset(_lines[i]._attrs, 0, sizeof _lines[i]._attrs);
+ }
+
+ _scrollBack += SCROLLBACK;
+}
+
+int TextBufferWindow::calcWidth(glui32 *chars, Attributes *attrs, int startchar,
+ int numChars, int spw) {
+ Screen &screen = *g_vm->_screen;
+ int w = 0;
+ int a, b;
+
+ a = startchar;
+ for (b = startchar; b < numChars; b++) {
+ if (attrs[a] != attrs[b]) {
+ w += screen.stringWidthUni(attrs[a].attrFont(_styles),
+ Common::U32String(chars + a, b - a), spw);
+ a = b;
+ }
+ }
+
+ w += screen.stringWidthUni(attrs[a].attrFont(_styles), Common::U32String(chars + a, b - a), spw);
+
+ return w;
+}
+
+void TextBufferWindow::getSize(glui32 *width, glui32 *height) const {
+ if (width)
+ *width = (_bbox.width() - g_conf->_tMarginX * 2) / g_conf->_cellW;
+ if (height)
+ *height = (_bbox.height() - g_conf->_tMarginY * 2) / g_conf->_cellH;
+}
+
+void TextBufferWindow::flowBreak() {
+ while (_ladjn || _radjn)
+ putCharUni('\n');
+}
+
+/*--------------------------------------------------------------------------*/
+
+TextBufferWindow::TextBufferRow::TextBufferRow() : _len(0), _newLine(0), _dirty(false),
+ _repaint(false), _lPic(nullptr), _rPic(nullptr), _lHyper(0), _rHyper(0),
+ _lm(0), _rm(0) {
+ Common::fill(&_chars[0], &_chars[TBLINELEN], 0);
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/window_text_buffer.h b/engines/glk/window_text_buffer.h
new file mode 100644
index 0000000000..a26a95b5bc
--- /dev/null
+++ b/engines/glk/window_text_buffer.h
@@ -0,0 +1,245 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_WINDOW_TEXT_BUFFER_H
+#define GLK_WINDOW_TEXT_BUFFER_H
+
+#include "glk/windows.h"
+#include "glk/picture.h"
+#include "glk/speech.h"
+
+namespace Gargoyle {
+
+/**
+ * Text Buffer window
+ */
+class TextBufferWindow : public Window, Speech {
+ /**
+ * Structure for a row within the window
+ */
+ struct TextBufferRow {
+ glui32 _chars[TBLINELEN];
+ Attributes _attrs[TBLINELEN];
+ int _len, _newLine;
+ bool _dirty, _repaint;
+ Picture *_lPic, *_rPic;
+ glui32 _lHyper, _rHyper;
+ int _lm, _rm;
+
+ /**
+ * Constructor
+ */
+ TextBufferRow();
+ };
+ typedef Common::Array<TextBufferRow> TextBufferRows;
+private:
+ void reflow();
+ void touchScroll();
+ bool putPicture(Picture *pic, glui32 align, glui32 linkval);
+
+ /**
+ * @remarks Only for input text
+ */
+ void putText(const char *buf, int len, int pos, int oldlen);
+
+ /**
+ * @remarks Only for input text
+ */
+ void putTextUni(const glui32 *buf, int len, int pos, int oldlen);
+
+ /**
+ * Return or enter, during line input. Ends line input.
+ */
+ void acceptLine(glui32 keycode);
+
+ /**
+ * Return true if a following quotation mark should be an opening mark,
+ * false if it should be a closing mark. Opening quotation marks will
+ * appear following an open parenthesis, open square bracket, or
+ * whitespace.
+ */
+ bool leftquote(glui32 c);
+
+ /**
+ * Mark a given text row as modified
+ */
+ void touch(int line);
+
+ void scrollOneLine(bool forced);
+ void scrollResize();
+ int calcWidth(glui32 *chars, Attributes *attrs, int startchar, int numchars, int spw);
+public:
+ int _width, _height;
+ int _spaced;
+ int _dashed;
+
+ TextBufferRows _lines;
+ int _scrollBack;
+
+ int _numChars; ///< number of chars in last line: lines[0]
+ glui32 *_chars; ///< alias to lines[0].chars
+ Attributes *_attrs; ///< alias to lines[0].attrs
+
+ ///< adjust margins temporarily for images
+ int _ladjw;
+ int _ladjn;
+ int _radjw;
+ int _radjn;
+
+ /* Command history. */
+ glui32 *_history[HISTORYLEN];
+ int _historyPos;
+ int _historyFirst, _historyPresent;
+
+ /* for paging */
+ int _lastSeen;
+ int _scrollPos;
+ int _scrollMax;
+
+ /* for line input */
+ void *_inBuf; ///< unsigned char* for latin1, glui32* for unicode
+ int _inMax;
+ long _inFence;
+ long _inCurs;
+ Attributes _origAttr;
+ gidispatch_rock_t _inArrayRock;
+
+ glui32 _echoLineInput;
+ glui32 *_lineTerminators;
+
+ /* style hints and settings */
+ WindowStyle _styles[style_NUMSTYLES];
+
+ /* for copy selection */
+ glui32 *_copyBuf;
+ int _copyPos;
+public:
+ /**
+ * Constructor
+ */
+ TextBufferWindow(Windows *windows, uint32 rock);
+
+ /**
+ * Destructor
+ */
+ virtual ~TextBufferWindow();
+
+ int acceptScroll(glui32 arg);
+
+ glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height);
+
+ /**
+ * Rearranges the window
+ */
+ virtual void rearrange(const Rect &box) override;
+
+ /**
+ * Get window split size within parent pair window
+ */
+ virtual glui32 getSplit(glui32 size, bool vertical) const override;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putCharUni(uint32 ch) override;
+
+ /**
+ * Unput a unicode character
+ */
+ virtual bool unputCharUni(uint32 ch) override;
+
+ /**
+ * Clear the window
+ */
+ virtual void clear() override;
+
+ /**
+ * Click the window
+ */
+ virtual void click(const Point &newPos) override;
+
+ /**
+ * Prepare for inputing a line
+ */
+ virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) override;
+
+ /**
+ * Prepare for inputing a line
+ */
+ virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) override;
+
+ /**
+ * Cancel an input line event
+ */
+ virtual void cancelLineEvent(Event *ev) override;
+
+ /**
+ * Cancel a hyperlink event
+ */
+ virtual void cancelHyperlinkEvent() override {
+ _hyperRequest = false;
+ }
+
+ /**
+ * Redraw the window
+ */
+ virtual void redraw() override;
+
+ virtual void acceptReadLine(glui32 arg) override;
+
+ virtual void acceptReadChar(glui32 arg) override;
+
+ virtual void getSize(glui32 *width, glui32 *height) const override;
+
+ virtual void requestCharEvent() override {
+ _charRequest = true;
+ }
+
+ virtual void requestCharEventUni() override {
+ _charRequestUni = true;
+ }
+
+ virtual void setEchoLineEvent(glui32 val) override {
+ _echoLineInput = val != 0;
+ }
+
+ virtual void requestHyperlinkEvent() override {
+ _hyperRequest = true;
+ }
+
+ virtual void cancelCharEvent() override {
+ _charRequest = _charRequestUni = false;
+ }
+
+ virtual void flowBreak() override;
+
+ /**
+ * Returns a pointer to the styles for the window
+ */
+ virtual const WindowStyle *getStyles() const override {
+ return _styles;
+ }
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/window_text_grid.cpp b/engines/glk/window_text_grid.cpp
new file mode 100644
index 0000000000..963083659f
--- /dev/null
+++ b/engines/glk/window_text_grid.cpp
@@ -0,0 +1,654 @@
+/* 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/window_text_grid.h"
+#include "glk/conf.h"
+#include "glk/gargoyle.h"
+#include "glk/selection.h"
+#include "glk/screen.h"
+
+namespace Gargoyle {
+
+TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
+ _type = wintype_TextGrid;
+ _width = _height = 0;
+ _curX = _curY = 0;
+ _inBuf = nullptr;
+ _inOrgX = _inOrgY = 0;
+ _inMax = 0;
+ _inCurs = _inLen = 0;
+ _inArrayRock.num = 0;
+ _lineTerminators = nullptr;
+
+ Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], _styles);
+}
+
+TextGridWindow::~TextGridWindow() {
+ if (_inBuf) {
+ if (g_vm->gli_unregister_arr)
+ (*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
+ _inBuf = nullptr;
+ }
+
+ delete[] _lineTerminators;
+}
+
+void TextGridWindow::rearrange(const Rect &box) {
+ Window::rearrange(box);
+ int newwid, newhgt;
+
+ newwid = box.width() / g_conf->_cellW;
+ newhgt = box.height() / g_conf->_cellH;
+
+ if (newwid == _width && newhgt == _height)
+ return;
+
+ _lines.resize(newhgt);
+ for (int y = 0; y < newhgt; ++y) {
+ _lines[y].resize(newwid);
+ touch(y);
+ }
+
+ _attr.clear();
+ _width = newwid;
+ _height = newhgt;
+}
+
+void TextGridWindow::touch(int line) {
+ int y = _bbox.top + line * g_conf->_leading;
+ _lines[line].dirty = true;
+ _windows->repaint(Rect(_bbox.left, y, _bbox.right, y + g_conf->_leading));
+}
+
+glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
+ return vertical ? size * g_conf->_cellW : size * g_conf->_cellH;
+}
+
+void TextGridWindow::putCharUni(uint32 ch) {
+ TextGridRow *ln;
+
+ // Canonicalize the cursor position. That is, the cursor may have been
+ // left outside the window area; wrap it if necessary.
+ if (_curX < 0) {
+ _curX = 0;
+ } else if (_curX >= _width) {
+ _curX = 0;
+ _curY++;
+ }
+ if (_curY < 0)
+ _curY = 0;
+ else if (_curY >= _height)
+ return; // outside the window
+
+ if (ch == '\n') {
+ // a newline just moves the cursor.
+ _curY++;
+ _curX = 0;
+ return;
+ }
+
+ touch(_curY);
+
+ ln = &(_lines[_curY]);
+ ln->_chars[_curX] = ch;
+ ln->_attrs[_curX] = _attr;
+
+ _curX++;
+ // We can leave the cursor outside the window, since it will be
+ // canonicalized next time a character is printed.
+}
+
+bool TextGridWindow::unputCharUni(uint32 ch) {
+ TextGridRow *ln;
+ int oldx = _curX, oldy = _curY;
+
+ // Move the cursor back.
+ if (_curX >= _width)
+ _curX = _width - 1;
+ else
+ _curX--;
+
+ // Canonicalize the cursor position. That is, the cursor may have been
+ // left outside the window area; wrap it if necessary.
+ if (_curX < 0) {
+ _curX = _width - 1;
+ _curY--;
+ }
+ if (_curY < 0)
+ _curY = 0;
+ else if (_curY >= _height)
+ return false; // outside the window
+
+ if (ch == '\n') {
+ // a newline just moves the cursor.
+ if (_curX == _width - 1)
+ return 1; // deleted a newline
+ _curX = oldx;
+ _curY = oldy;
+ return 0; // it wasn't there
+ }
+
+ ln = &(_lines[_curY]);
+ if (ln->_chars[_curX] == ch) {
+ ln->_chars[_curX] = ' ';
+ ln->_attrs[_curX].clear();
+ touch(_curY);
+ return true; // deleted the char
+ } else {
+ _curX = oldx;
+ _curY = oldy;
+ return false; // it wasn't there
+ }
+}
+
+void TextGridWindow::moveCursor(const Point &pos) {
+ // If the values are negative, they're really huge positive numbers --
+ // remember that they were cast from glui32. So set them huge and
+ // let canonicalization take its course.
+ _curX = (pos.x < 0) ? 32767 : pos.x;
+ _curY = (pos.y < 0) ? 32767 : pos.y;
+}
+
+void TextGridWindow::clear() {
+ _attr.fgset = Windows::_overrideFgSet;
+ _attr.bgset = Windows::_overrideBgSet;
+ _attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
+ _attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
+ _attr.reverse = false;
+
+ for (int k = 0; k < _height; k++) {
+ TextGridRow &ln = _lines[k];
+ touch(k);
+ for (uint j = 0; j < ln._attrs.size(); ++j) {
+ ln._chars[j] = ' ';
+ ln._attrs[j].clear();
+ }
+ }
+
+ _curX = 0;
+ _curY = 0;
+}
+
+void TextGridWindow::click(const Point &newPos) {
+ int x = newPos.x - _bbox.left;
+ int y = newPos.y - _bbox.top;
+
+ if (_lineRequest || _charRequest || _lineRequestUni || _charRequestUni
+ || _moreRequest || _scrollRequest)
+ _windows->setFocus(this);
+
+ if (_mouseRequest) {
+ g_vm->_events->store(evtype_MouseInput, this, x / g_conf->_cellW, y / g_conf->_leading);
+ _mouseRequest = false;
+ if (g_conf->_safeClicks)
+ g_vm->_events->_forceClick = true;
+ }
+
+ if (_hyperRequest) {
+ glui32 linkval = g_vm->_selection->getHyperlink(newPos);
+ if (linkval) {
+ g_vm->_events->store(evtype_Hyperlink, this, linkval, 0);
+ _hyperRequest = false;
+ if (g_conf->_safeClicks)
+ g_vm->_events->_forceClick = true;
+ }
+ }
+}
+
+void TextGridWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
+ if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
+ warning("request_line_event: window already has keyboard request");
+ return;
+ }
+
+ _lineRequest = true;
+
+ if ((int)maxlen > (_width - _curX))
+ maxlen = (_width - _curX);
+
+ _inBuf = buf;
+ _inMax = maxlen;
+ _inLen = 0;
+ _inCurs = 0;
+ _inOrgX = _curX;
+ _inOrgY = _curY;
+ _origAttr = _attr;
+ _attr.set(style_Input);
+
+ if (initlen > maxlen)
+ initlen = maxlen;
+
+ if (initlen) {
+ TextGridRow *ln = &_lines[_inOrgY];
+
+ for (glui32 ix = 0; ix < initlen; ix++) {
+ ln->_attrs[_inOrgX + ix].set(style_Input);
+ ln->_chars[_inOrgX + ix] = buf[ix];
+ }
+
+ _inCurs += initlen;
+ _inLen += initlen;
+ _curX = _inOrgX + _inCurs;
+ _curY = _inOrgY;
+
+ touch(_inOrgY);
+ }
+
+ if (_lineTerminatorsBase && _termCt) {
+ _lineTerminators = new glui32[_termCt + 1];
+
+ if (_lineTerminators) {
+ memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+ _lineTerminators[_termCt] = 0;
+ }
+ }
+
+ if (g_vm->gli_register_arr)
+ _inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Cn");
+}
+
+void TextGridWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
+ if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
+ warning("requestLineEventUni: window already has keyboard request");
+ return;
+ }
+
+ if ((int)maxlen > (_width - _curX))
+ maxlen = (_width - _curX);
+
+ _inBuf = buf;
+ _inMax = maxlen;
+ _inLen = 0;
+ _inCurs = 0;
+ _inOrgX = _curX;
+ _inOrgY = _curY;
+ _origAttr = _attr;
+ _attr.set(style_Input);
+
+ if (initlen > maxlen)
+ initlen = maxlen;
+
+ if (initlen) {
+ TextGridRow *ln = &(_lines[_inOrgY]);
+
+ for (glui32 ix = 0; ix < initlen; ix++) {
+ ln->_attrs[_inOrgX + ix].set(style_Input);
+ ln->_chars[_inOrgX + ix] = buf[ix];
+ }
+
+ _inCurs += initlen;
+ _inLen += initlen;
+ _curX = _inOrgX + _inCurs;
+ _curY = _inOrgY;
+
+ touch(_inOrgY);
+ }
+
+ if (_lineTerminatorsBase && _termCt) {
+ _lineTerminators = new glui32[_termCt + 1];
+
+ if (_lineTerminators) {
+ memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+ _lineTerminators[_termCt] = 0;
+ }
+ }
+
+ if (g_vm->gli_register_arr)
+ _inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Iu");
+}
+
+void TextGridWindow::cancelLineEvent(Event *ev) {
+ int ix;
+ void *inbuf;
+ int inmax;
+ int unicode = _lineRequestUni;
+ gidispatch_rock_t inarrayrock;
+ TextGridRow *ln = &_lines[_inOrgY];
+ Event dummyEv;
+
+ if (!ev)
+ ev = &dummyEv;
+
+ ev->clear();
+
+ if (!_lineRequest && !_lineRequestUni)
+ return;
+
+
+ inbuf = _inBuf;
+ inmax = _inMax;
+ inarrayrock = _inArrayRock;
+
+ if (!unicode) {
+ for (ix = 0; ix < _inLen; ix++) {
+ glui32 ch = ln->_chars[_inOrgX + ix];
+ if (ch > 0xff)
+ ch = '?';
+ ((char *)inbuf)[ix] = (char)ch;
+ }
+ if (_echoStream)
+ _echoStream->echoLine((char *)_inBuf, _inLen);
+ } else {
+ for (ix = 0; ix < _inLen; ix++)
+ ((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix];
+ if (_echoStream)
+ _echoStream->echoLineUni((glui32 *)inbuf, _inLen);
+ }
+
+ _curY = _inOrgY + 1;
+ _curX = 0;
+ _attr = _origAttr;
+
+ ev->type = evtype_LineInput;
+ ev->window = this;
+ ev->val1 = _inLen;
+ ev->val2 = 0;
+
+ _lineRequest = false;
+ _lineRequestUni = false;
+
+ if (_lineTerminators) {
+ free(_lineTerminators);
+ _lineTerminators = nullptr;
+ }
+
+ _inBuf = nullptr;
+ _inMax = 0;
+ _inOrgX = 0;
+ _inOrgY = 0;
+
+ if (g_vm->gli_unregister_arr)
+ (*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
+}
+
+void TextGridWindow::acceptReadChar(glui32 arg) {
+ glui32 key;
+
+ switch (arg) {
+ case keycode_Erase:
+ key = keycode_Delete;
+ break;
+ case keycode_MouseWheelUp:
+ case keycode_MouseWheelDown:
+ return;
+ default:
+ key = arg;
+ }
+
+ if (key > 0xff && key < (0xffffffff - keycode_MAXVAL + 1)) {
+ if (!(_charRequestUni) || key > 0x10ffff)
+ key = keycode_Unknown;
+ }
+
+ _charRequest = false;
+ _charRequestUni = false;
+ g_vm->_events->store(evtype_CharInput, this, key, 0);
+}
+
+void TextGridWindow::acceptLine(glui32 keycode) {
+ int ix;
+ void *inbuf;
+ int inmax;
+ gidispatch_rock_t inarrayrock;
+ TextGridRow *ln = &(_lines[_inOrgY]);
+ int unicode = _lineRequestUni;
+
+ if (!_inBuf)
+ return;
+
+ inbuf = _inBuf;
+ inmax = _inMax;
+ inarrayrock = _inArrayRock;
+
+ if (!unicode) {
+ for (ix = 0; ix < _inLen; ix++)
+ ((char *)inbuf)[ix] = (char)ln->_chars[_inOrgX + ix];
+ if (_echoStream)
+ _echoStream->echoLine((char *)inbuf, _inLen);
+ } else {
+ for (ix = 0; ix < _inLen; ix++)
+ ((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix];
+ if (_echoStream)
+ _echoStream->echoLineUni((glui32 *)inbuf, _inLen);
+ }
+
+ _curY = _inOrgY + 1;
+ _curX = 0;
+ _attr = _origAttr;
+
+ if (_lineTerminators) {
+ glui32 val2 = keycode;
+ if (val2 == keycode_Return)
+ val2 = 0;
+ g_vm->_events->store(evtype_LineInput, this, _inLen, val2);
+ free(_lineTerminators);
+ _lineTerminators = nullptr;
+ } else {
+ g_vm->_events->store(evtype_LineInput, this, _inLen, 0);
+ }
+ _lineRequest = false;
+ _lineRequestUni = false;
+ _inBuf = nullptr;
+ _inMax = 0;
+ _inOrgX = 0;
+ _inOrgY = 0;
+
+ if (g_vm->gli_unregister_arr)
+ (*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
+}
+
+void TextGridWindow::acceptReadLine(glui32 arg) {
+ int ix;
+ TextGridRow *ln = &(_lines[_inOrgY]);
+
+ if (!_inBuf)
+ return;
+
+ if (_lineTerminators && checkTerminator(arg)) {
+ glui32 *cx;
+ for (cx = _lineTerminators; *cx; cx++) {
+ if (*cx == arg) {
+ acceptLine(arg);
+ return;
+ }
+ }
+ }
+
+ switch (arg) {
+
+ // Delete keys, during line input.
+ case keycode_Delete:
+ if (_inLen <= 0)
+ return;
+ if (_inCurs <= 0)
+ return;
+ for (ix = _inCurs; ix < _inLen; ix++)
+ ln->_chars[_inOrgX + ix - 1] = ln->_chars[_inOrgX + ix];
+ ln->_chars[_inOrgX + _inLen - 1] = ' ';
+ _inCurs--;
+ _inLen--;
+ break;
+
+ case keycode_Erase:
+ if (_inLen <= 0)
+ return;
+ if (_inCurs >= _inLen)
+ return;
+ for (ix = _inCurs; ix < _inLen - 1; ix++)
+ ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix + 1];
+ ln->_chars[_inOrgX + _inLen - 1] = ' ';
+ _inLen--;
+ break;
+
+ case keycode_Escape:
+ if (_inLen <= 0)
+ return;
+ for (ix = 0; ix < _inLen; ix++)
+ ln->_chars[_inOrgX + ix] = ' ';
+ _inLen = 0;
+ _inCurs = 0;
+ break;
+
+ // Cursor movement keys, during line input.
+ case keycode_Left:
+ if (_inCurs <= 0)
+ return;
+ _inCurs--;
+ break;
+
+ case keycode_Right:
+ if (_inCurs >= _inLen)
+ return;
+ _inCurs++;
+ break;
+
+ case keycode_Home:
+ if (_inCurs <= 0)
+ return;
+ _inCurs = 0;
+ break;
+
+ case keycode_End:
+ if (_inCurs >= _inLen)
+ return;
+ _inCurs = _inLen;
+ break;
+
+ case keycode_Return:
+ acceptLine(arg);
+ break;
+
+ default:
+ if (_inLen >= _inMax)
+ return;
+
+ if (arg < 32 || arg > 0xff)
+ return;
+
+ if (g_conf->_caps && (arg > 0x60 && arg < 0x7b))
+ arg -= 0x20;
+
+ for (ix = _inLen; ix > _inCurs; ix--)
+ ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix - 1];
+ ln->_attrs[_inOrgX + _inLen].set(style_Input);
+ ln->_chars[_inOrgX + _inCurs] = arg;
+
+ _inCurs++;
+ _inLen++;
+ }
+
+ _curX = _inOrgX + _inCurs;
+ _curY = _inOrgY;
+
+ touch(_inOrgY);
+}
+
+void TextGridWindow::redraw() {
+ TextGridRow *ln;
+ int x0, y0;
+ int x, y, w;
+ int i, a, b, k, o;
+ glui32 link;
+ int font;
+ byte *fgcolor, *bgcolor;
+ Screen &screen = *g_vm->_screen;
+
+ Window::redraw();
+
+ x0 = _bbox.left;
+ y0 = _bbox.top;
+
+ for (i = 0; i < _height; i++) {
+ ln = &_lines[i];
+ if (ln->dirty || Windows::_forceRedraw) {
+ ln->dirty = false;
+
+ x = x0;
+ y = y0 + i * g_conf->_leading;
+
+ // clear any stored hyperlink coordinates
+ g_vm->_selection->putHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading);
+
+ a = 0;
+ for (b = 0; b < _width; b++) {
+ if (ln->_attrs[a] != ln->_attrs[b]) {
+ link = ln->_attrs[a].hyper;
+ font = ln->_attrs[a].attrFont(_styles);
+ fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
+ bgcolor = ln->_attrs[a].attrBg(_styles);
+ w = (b - a) * g_conf->_cellW;
+ screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor);
+ o = x;
+
+ for (k = a, o = x; k < b; k++, o += g_conf->_cellW) {
+ screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine), font,
+ fgcolor, Common::U32String(&ln->_chars[k], 1), -1);
+ }
+ if (link) {
+ screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w,
+ g_conf->_linkStyle), g_conf->_linkColor);
+ g_vm->_selection->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
+ }
+
+ x += w;
+ a = b;
+ }
+ }
+ link = ln->_attrs[a].hyper;
+ font = ln->_attrs[a].attrFont(_styles);
+ fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
+ bgcolor = ln->_attrs[a].attrBg(_styles);
+ w = (b - a) * g_conf->_cellW;
+ w += _bbox.right - (x + w);
+ screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor);
+
+ for (k = a, o = x; k < b; k++, o += g_conf->_cellW) {
+ screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine), font,
+ fgcolor, Common::U32String(&ln->_chars[k], 1));
+ }
+ if (link) {
+ screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w, g_conf->_linkStyle),
+ g_conf->_linkColor);
+ g_vm->_selection->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
+ }
+ }
+ }
+}
+
+void TextGridWindow::getSize(glui32 *width, glui32 *height) const {
+ if (width)
+ *width = _bbox.width() / g_conf->_cellW;
+ if (height)
+ *height = _bbox.height() / g_conf->_cellH;
+}
+
+/*--------------------------------------------------------------------------*/
+
+void TextGridWindow::TextGridRow::resize(size_t newSize) {
+ _chars.clear();
+ _attrs.clear();
+ _chars.resize(newSize);
+ _attrs.resize(newSize);
+ Common::fill(&_chars[0], &_chars[0] + newSize, ' ');
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/window_text_grid.h b/engines/glk/window_text_grid.h
new file mode 100644
index 0000000000..acfd366cb2
--- /dev/null
+++ b/engines/glk/window_text_grid.h
@@ -0,0 +1,195 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_WINDOW_TEXT_GRID_H
+#define GLK_WINDOW_TEXT_GRID_H
+
+#include "glk/windows.h"
+
+namespace Gargoyle {
+
+/**
+ * Text Grid window
+ */
+class TextGridWindow : public Window {
+ /**
+ * Structure for a row within the grid window
+ */
+ struct TextGridRow {
+ Common::Array<uint32> _chars;
+ Common::Array<Attributes> _attrs;
+ bool dirty;
+
+ /**
+ * Constructor
+ */
+ TextGridRow() : dirty(false) {}
+
+ /**
+ * Resize the row
+ */
+ void resize(size_t newSize);
+ };
+ typedef Common::Array<TextGridRow> TextGridRows;
+private:
+ /**
+ * Mark a given text row as modified
+ */
+ void touch(int line);
+
+ /**
+ * Return or enter, during line input. Ends line input.
+ */
+ void acceptLine(glui32 keycode);
+public:
+ int _width, _height;
+ TextGridRows _lines;
+
+ int _curX, _curY; ///< the window cursor position
+
+ ///< for line input
+ void *_inBuf; ///< unsigned char* for latin1, glui32* for unicode
+ int _inOrgX, _inOrgY;
+ int _inMax;
+ int _inCurs, _inLen;
+ Attributes _origAttr;
+ gidispatch_rock_t _inArrayRock;
+ glui32 *_lineTerminators;
+
+ WindowStyle _styles[style_NUMSTYLES]; ///< style hints and settings
+public:
+ /**
+ * Constructor
+ */
+ TextGridWindow(Windows *windows, uint32 rock);
+
+ /**
+ * Destructor
+ */
+ virtual ~TextGridWindow();
+
+ /**
+ * Rearranges the window
+ */
+ virtual void rearrange(const Rect &box) override;
+
+ /**
+ * Get window split size within parent pair window
+ */
+ virtual glui32 getSplit(glui32 size, bool vertical) const override;
+
+ /**
+ * Write a unicode character
+ */
+ virtual void putCharUni(uint32 ch) override;
+
+ /**
+ * Unput a unicode character
+ */
+ virtual bool unputCharUni(uint32 ch) override;
+
+ /**
+ * Move the cursor
+ */
+ virtual void moveCursor(const Point &newPos) override;
+
+ /**
+ * Clear the window
+ */
+ virtual void clear() override;
+
+ /**
+ * Click the window
+ */
+ virtual void click(const Point &newPos) override;
+
+ /**
+ * Cancel a hyperlink event
+ */
+ virtual void cancelHyperlinkEvent() override {
+ _hyperRequest = false;
+ }
+
+ /**
+ * Redraw the window
+ */
+ virtual void redraw() override;
+
+ virtual void acceptReadLine(glui32 arg) override;
+
+ virtual void acceptReadChar(glui32 arg) override;
+
+ virtual void getSize(glui32 *width, glui32 *height) const override;
+
+ virtual void requestCharEvent() override {
+ _charRequest = true;
+ }
+
+ /**
+ * Prepare for inputing a line
+ */
+ virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) override;
+
+ /**
+ * Prepare for inputing a line
+ */
+ virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) override;
+
+ /**
+ * Cancel an input line event
+ */
+ virtual void cancelLineEvent(Event *ev) override;
+
+ /**
+ * Cancel a mouse event
+ */
+ virtual void cancelMouseEvent() override {
+ _mouseRequest = false;
+ }
+
+ virtual void requestCharEventUni() override {
+ _charRequestUni = true;
+ }
+
+ virtual void requestMouseEvent() override {
+ _mouseRequest = true;
+ }
+
+ virtual void requestHyperlinkEvent() override {
+ _hyperRequest = true;
+ }
+
+ virtual void cancelCharEvent() override {
+ _charRequest = _charRequestUni = false;
+ }
+
+ /**
+ * Returns a pointer to the styles for the window
+ */
+ virtual const WindowStyle *getStyles() const override {
+ return _styles;
+ }
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/glk/windows.cpp b/engines/glk/windows.cpp
new file mode 100644
index 0000000000..d982f6ccb5
--- /dev/null
+++ b/engines/glk/windows.cpp
@@ -0,0 +1,773 @@
+/* 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/windows.h"
+#include "glk/window_graphics.h"
+#include "glk/window_pair.h"
+#include "glk/window_text_buffer.h"
+#include "glk/window_text_grid.h"
+#include "glk/conf.h"
+#include "glk/gargoyle.h"
+#include "glk/screen.h"
+#include "glk/streams.h"
+#include "common/algorithm.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+
+bool Windows::_overrideReverse;
+bool Windows::_overrideFgSet;
+bool Windows::_overrideBgSet;
+bool Windows::_forceRedraw;
+bool Windows::_claimSelect;
+bool Windows::_moreFocus;
+int Windows::_overrideFgVal;
+int Windows::_overrideBgVal;
+int Windows::_zcolor_fg;
+int Windows::_zcolor_bg;
+byte Windows::_zcolor_LightGrey[3];
+byte Windows::_zcolor_Foreground[3];
+byte Windows::_zcolor_Background[3];
+byte Windows::_zcolor_Bright[3];
+
+/*--------------------------------------------------------------------------*/
+
+Windows::Windows(Graphics::Screen *screen) : _screen(screen), _windowList(nullptr),
+ _rootWin(nullptr), _focusWin(nullptr) {
+ _overrideReverse = false;
+ _overrideFgSet = false;
+ _overrideBgSet = false;
+ _forceRedraw = true;
+ _claimSelect = false;
+ _moreFocus = false;
+ _overrideFgVal = 0;
+ _overrideBgVal = 0;
+ _zcolor_fg = _zcolor_bg = 0;
+ _drawSelect = false;
+
+ _zcolor_LightGrey[0] = _zcolor_LightGrey[1] = _zcolor_LightGrey[2] = 181;
+ _zcolor_Foreground[0] = _zcolor_Foreground[1] = _zcolor_Foreground[2] = 0;
+ _zcolor_Background[0] = _zcolor_Background[1] = _zcolor_Background[2] = 0;
+ _zcolor_Bright[0] = _zcolor_Bright[1] = _zcolor_Bright[2] = 0;
+}
+
+Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
+ glui32 wintype, glui32 rock) {
+ Window *newwin, *oldparent;
+ PairWindow *pairWin;
+ glui32 val;
+
+ _forceRedraw = true;
+
+ if (!_rootWin) {
+ if (splitwin) {
+ warning("window_open: ref must be nullptr");
+ return nullptr;
+ }
+
+ /* ignore method and size now */
+ oldparent = nullptr;
+ } else {
+ if (!splitwin) {
+ warning("window_open: ref must not be nullptr");
+ return nullptr;
+ }
+
+ val = (method & winmethod_DivisionMask);
+ if (val != winmethod_Fixed && val != winmethod_Proportional) {
+ warning("window_open: invalid method (not fixed or proportional)");
+ return nullptr;
+ }
+
+ val = (method & winmethod_DirMask);
+ if (val != winmethod_Above && val != winmethod_Below
+ && val != winmethod_Left && val != winmethod_Right) {
+ warning("window_open: invalid method (bad direction)");
+ return nullptr;
+ }
+
+ oldparent = splitwin->_parent;
+ if (oldparent && oldparent->_type != wintype_Pair) {
+ warning("window_open: parent window is not Pair");
+ return nullptr;
+ }
+ }
+
+ assert(wintype != wintype_Pair);
+ newwin = newWindow(wintype, rock);
+ if (!newwin) {
+ warning("window_open: unable to create window");
+ return nullptr;
+ }
+
+ if (!splitwin) {
+ _rootWin = newwin;
+ } else {
+ // create pairWin, with newwin as the key
+ pairWin = newPairWindow(method, newwin, size);
+ pairWin->_child1 = splitwin;
+ pairWin->_child2 = newwin;
+
+ splitwin->_parent = pairWin;
+ newwin->_parent = pairWin;
+ pairWin->_parent = oldparent;
+
+ if (oldparent) {
+ PairWindow *parentWin = dynamic_cast<PairWindow *>(oldparent);
+ assert(parentWin);
+ if (parentWin->_child1 == splitwin)
+ parentWin->_child1 = pairWin;
+ else
+ parentWin->_child2 = pairWin;
+ } else {
+ _rootWin = pairWin;
+ }
+ }
+
+ rearrange();
+
+ return newwin;
+}
+
+void Windows::windowClose(Window *win, StreamResult *result) {
+ _forceRedraw = true;
+
+ if (win == _rootWin || win->_parent == nullptr) {
+ // Close the root window, which means all windows.
+ _rootWin = nullptr;
+
+ // Begin (simpler) closation
+ win->_stream->fillResult(result);
+ win->close(true);
+ } else {
+ // Have to jigger parent
+ Window *sibWin;
+ PairWindow *pairWin = dynamic_cast<PairWindow *>(win->_parent);
+ PairWindow *grandparWin;
+
+ if (win == pairWin->_child1) {
+ sibWin = pairWin->_child2;
+ } else if (win == pairWin->_child2) {
+ sibWin = pairWin->_child1;
+ } else {
+ warning("windowClose: window tree is corrupted");
+ return;
+ }
+
+ grandparWin = dynamic_cast<PairWindow *>(pairWin->_parent);
+ if (!grandparWin) {
+ _rootWin = sibWin;
+ sibWin->_parent = nullptr;
+ } else {
+ if (grandparWin->_child1 == pairWin)
+ grandparWin->_child1 = sibWin;
+ else
+ grandparWin->_child2 = sibWin;
+ sibWin->_parent = grandparWin;
+ }
+
+ // Begin closation
+ win->_stream->fillResult(result);
+
+ // Close the child window (and descendants), so that key-deletion can
+ // crawl up the tree to the root window.
+ win->close(true);
+
+ // This probably isn't necessary, but the child *is* gone, so just in case.
+ if (win == pairWin->_child1)
+ pairWin->_child1 = nullptr;
+ else if (win == pairWin->_child2)
+ pairWin->_child2 = nullptr;
+
+ // Now we can delete the parent pair.
+ pairWin->close(false);
+
+ // Sort out the arrangements
+ rearrange();
+ }
+}
+
+Window *Windows::newWindow(glui32 type, glui32 rock) {
+ Window *win;
+
+ switch (type) {
+ case wintype_Blank:
+ win = new BlankWindow(this, rock);
+ break;
+ case wintype_TextGrid:
+ win = new TextGridWindow(this, rock);
+ break;
+ case wintype_TextBuffer:
+ win = new TextBufferWindow(this, rock);
+ break;
+ case wintype_Graphics:
+ win = new GraphicsWindow(this, rock);
+ break;
+ case wintype_Pair:
+ error("Pair windows cannot be created directly");
+ default:
+ error("Unknown window type");
+ }
+
+ win->_next = _windowList;
+ _windowList = win;
+ if (win->_next)
+ win->_next->_prev = win;
+
+ return win;
+}
+
+PairWindow *Windows::newPairWindow(glui32 method, Window *key, glui32 size) {
+ PairWindow *pwin = new PairWindow(this, method, key, size);
+ pwin->_next = _windowList;
+ _windowList = pwin;
+ if (pwin->_next)
+ pwin->_next->_prev = pwin;
+
+ return pwin;
+}
+
+void Windows::rearrange() {
+ if (_rootWin) {
+ Rect box;
+
+ if (g_conf->_lockCols) {
+ int desired_width = g_conf->_wMarginSaveX * 2 + g_conf->_cellW * g_conf->_cols;
+ if (desired_width > g_conf->_imageW)
+ g_conf->_wMarginX = g_conf->_wMarginSaveX;
+ else
+ g_conf->_wMarginX = (g_conf->_imageW - g_conf->_cellW * g_conf->_cols) / 2;
+ }
+
+ if (g_conf->_lockRows) {
+ int desired_height = g_conf->_wMarginSaveY * 2 + g_conf->_cellH * g_conf->_rows;
+ if (desired_height > g_conf->_imageH)
+ g_conf->_wMarginY = g_conf->_wMarginSaveY;
+ else
+ g_conf->_wMarginY = (g_conf->_imageH - g_conf->_cellH * g_conf->_rows) / 2;
+ }
+
+ box.left = g_conf->_wMarginX;
+ box.top = g_conf->_wMarginY;
+ box.right = g_conf->_imageW - g_conf->_wMarginX;
+ box.bottom = g_conf->_imageH - g_conf->_wMarginY;
+
+ _rootWin->rearrange(box);
+ }
+}
+
+void Windows::inputGuessFocus() {
+ Window *altWin = _focusWin;
+
+ do {
+ if (altWin
+ && (altWin->_lineRequest || altWin->_charRequest ||
+ altWin->_lineRequestUni || altWin->_charRequestUni))
+ break;
+ altWin = iterateTreeOrder(altWin);
+ } while (altWin != _focusWin);
+
+ if (_focusWin != altWin) {
+ _focusWin = altWin;
+ _forceRedraw = true;
+ redraw();
+ }
+}
+
+void Windows::inputMoreFocus() {
+ Window *altWin = _focusWin;
+
+ do {
+ if (altWin && altWin->_moreRequest)
+ break;
+ altWin = iterateTreeOrder(altWin);
+ } while (altWin != _focusWin);
+
+ _focusWin = altWin;
+}
+
+void Windows::inputNextFocus() {
+ Window *altWin = _focusWin;
+
+ do {
+ altWin = iterateTreeOrder(altWin);
+ if (altWin
+ && (altWin->_lineRequest || altWin->_charRequest ||
+ altWin->_lineRequestUni || altWin->_charRequestUni))
+ break;
+ } while (altWin != _focusWin);
+
+ if (_focusWin != altWin) {
+ _focusWin = altWin;
+ _forceRedraw = true;
+ redraw();
+ }
+}
+
+void Windows::inputScrollFocus() {
+ Window *altWin = _focusWin;
+
+ do {
+ if (altWin && altWin->_scrollRequest)
+ break;
+ altWin = iterateTreeOrder(altWin);
+ } while (altWin != _focusWin);
+
+ _focusWin = altWin;
+}
+
+void Windows::inputHandleKey(glui32 key) {
+ if (_moreFocus) {
+ inputMoreFocus();
+ } else {
+ switch (key) {
+ case keycode_Tab:
+ inputNextFocus();
+ return;
+ case keycode_PageUp:
+ case keycode_PageDown:
+ case keycode_MouseWheelUp:
+ case keycode_MouseWheelDown:
+ inputScrollFocus();
+ break;
+ default:
+ inputGuessFocus();
+ break;
+ }
+ }
+
+ Window *win = _focusWin;
+ if (!win)
+ return;
+
+ bool deferExit = false;
+
+ TextGridWindow *gridWindow = dynamic_cast<TextGridWindow *>(win);
+ TextBufferWindow *bufWindow = dynamic_cast<TextBufferWindow *>(win);
+
+ if (gridWindow) {
+ if (gridWindow->_charRequest || gridWindow->_charRequestUni)
+ gridWindow->acceptReadChar(key);
+ else if (gridWindow->_lineRequest || gridWindow->_lineRequestUni)
+ gridWindow->acceptReadLine(key);
+ } else if (bufWindow) {
+ if (bufWindow->_charRequest || bufWindow->_charRequestUni)
+ bufWindow->acceptReadChar(key);
+ else if (bufWindow->_lineRequest || bufWindow->_lineRequestUni)
+ bufWindow->acceptReadLine(key);
+ else if (bufWindow->_moreRequest || bufWindow->_scrollRequest)
+ deferExit = bufWindow->acceptScroll(key);
+ }
+
+ if (!deferExit && g_vm->_terminated)
+ g_vm->quitGame();
+}
+
+void Windows::inputHandleClick(const Point &pos) {
+ if (_rootWin)
+ _rootWin->click(pos);
+}
+
+void Windows::selectionChanged() {
+ _claimSelect = false;
+ _forceRedraw = true;
+ redraw();
+}
+
+void Windows::redraw() {
+ _claimSelect = false;
+
+ if (_forceRedraw) {
+ repaint(Rect(0, 0, g_conf->_imageW, g_conf->_imageH));
+ g_vm->_screen->fill(g_conf->_windowColor);
+ }
+
+ if (_rootWin)
+ _rootWin->redraw();
+
+ if (_moreFocus)
+ refocus(_focusWin);
+
+ _forceRedraw = 0;
+}
+
+void Windows::redrawRect(const Rect &r) {
+ _drawSelect = true;
+ repaint(r);
+}
+
+void Windows::repaint(const Rect &box) {
+ g_vm->_events->redraw();
+}
+
+byte *Windows::rgbShift(byte *rgb) {
+ _zcolor_Bright[0] = (rgb[0] + 0x30) < 0xff ? (rgb[0] + 0x30) : 0xff;
+ _zcolor_Bright[1] = (rgb[1] + 0x30) < 0xff ? (rgb[1] + 0x30) : 0xff;
+ _zcolor_Bright[2] = (rgb[2] + 0x30) < 0xff ? (rgb[2] + 0x30) : 0xff;
+
+ return _zcolor_Bright;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Windows::iterator &Windows::iterator::operator++() {
+ _current = _windows->iterateTreeOrder(_current);
+ return *this;
+}
+
+void Windows::refocus(Window *win) {
+ Window *focus = win;
+ do {
+ if (focus && focus->_moreRequest) {
+ _focusWin = focus;
+ return;
+ }
+
+ focus = iterateTreeOrder(focus);
+ } while (focus != win);
+
+ _moreFocus = false;
+}
+
+Window *Windows::iterateTreeOrder(Window *win) {
+ if (!win)
+ return _rootWin;
+
+ PairWindow *pairWin = dynamic_cast<PairWindow *>(win);
+ if (pairWin) {
+ if (!pairWin->_backward)
+ return pairWin->_child1;
+ else
+ return pairWin->_child2;
+ } else {
+ while (win->_parent) {
+ pairWin = dynamic_cast<PairWindow *>(win->_parent);
+ assert(pairWin);
+
+ if (!pairWin->_backward) {
+ if (win == pairWin->_child1)
+ return pairWin->_child2;
+ } else {
+ if (win == pairWin->_child2)
+ return pairWin->_child1;
+ }
+
+ win = pairWin;
+ }
+
+ return nullptr;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Window::Window(Windows *windows, glui32 rock) : _windows(windows), _rock(rock),
+ _type(0), _parent(nullptr), _next(nullptr), _prev(nullptr), _yAdj(0),
+ _lineRequest(0), _lineRequestUni(0), _charRequest(0), _charRequestUni(0),
+ _mouseRequest(0), _hyperRequest(0), _moreRequest(0), _scrollRequest(0), _imageLoaded(0),
+ _echoLineInputBase(true), _lineTerminatorsBase(nullptr), _termCt(0), _echoStream(nullptr) {
+ _attr.fgset = 0;
+ _attr.bgset = 0;
+ _attr.reverse = 0;
+ _attr.style = 0;
+ _attr.fgcolor = 0;
+ _attr.bgcolor = 0;
+ _attr.hyper = 0;
+
+ Common::copy(&g_conf->_windowColor[0], &g_conf->_windowColor[3], &_bgColor[0]);
+ Common::copy(&g_conf->_moreColor[0], &g_conf->_moreColor[3], _fgColor);
+ _dispRock.num = 0;
+
+ Streams &streams = *g_vm->_streams;
+ _stream = streams.openWindowStream(this);
+}
+
+Window::~Window() {
+ if (g_vm->gli_unregister_obj)
+ (*g_vm->gli_unregister_obj)(this, gidisp_Class_Window, _dispRock);
+
+
+ _echoStream = nullptr;
+ delete _stream;
+
+ delete[] _lineTerminatorsBase;
+
+ Window *prev = _prev;
+ Window *next = _next;
+
+ if (prev)
+ prev->_next = next;
+ else
+ _windows->_windowList = next;
+ if (next)
+ next->_prev = prev;
+}
+
+void Window::close(bool recurse) {
+ if (_windows->getFocusWindow() == this)
+ // Focused window is being removed
+ _windows->setFocus(nullptr);
+
+ for (Window *wx = _parent; wx; wx = wx->_parent) {
+ PairWindow *pairWin = dynamic_cast<PairWindow *>(wx);
+
+ if (pairWin && pairWin->_key == this) {
+ pairWin->_key = nullptr;
+ pairWin->_keyDamage = true;
+ }
+ }
+
+ PairWindow *pairWin = dynamic_cast<PairWindow *>(this);
+ if (pairWin) {
+ pairWin->_child1->close(recurse);
+ pairWin->_child2->close(recurse);
+ }
+
+ // Finally, delete the window
+ delete this;
+}
+
+void Window::cancelLineEvent(Event *ev) {
+ Event dummyEv;
+ if (!ev)
+ ev = &dummyEv;
+
+ ev->clear();
+}
+
+void Window::moveCursor(const Point &newPos) {
+ warning("moveCursor: not a TextGrid window");
+}
+
+void Window::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
+ warning("requestLineEvent: window does not support keyboard input");
+}
+
+void Window::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
+ warning("requestLineEventUni: window does not support keyboard input");
+}
+
+void Window::redraw() {
+ if (Windows::_forceRedraw) {
+ unsigned char *color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+ int y0 = _yAdj ? _bbox.top - _yAdj : _bbox.top;
+ g_vm->_screen->fillRect(Rect(_bbox.left, y0, _bbox.right, _bbox.bottom), color);
+ }
+}
+
+void Window::acceptReadLine(glui32 arg) {
+ warning("acceptReadLine:: window does not support keyboard input");
+}
+
+void Window::acceptReadChar(glui32 arg) {
+ warning("acceptReadChar:: window does not support keyboard input");
+}
+
+void Window::getArrangement(glui32 *method, glui32 *size, Window **keyWin) {
+ warning("getArrangement: not a Pair window");
+}
+
+void Window::setArrangement(glui32 method, glui32 size, Window *keyWin) {
+ warning("setArrangement: not a Pair window");
+}
+
+void Window::requestCharEvent() {
+ warning("requestCharEvent: window does not support keyboard input");
+}
+
+void Window::requestCharEventUni() {
+ warning("requestCharEventUni: window does not support keyboard input");
+}
+
+void Window::flowBreak() {
+ warning("flowBreak: not a text buffer window");
+}
+
+void Window::eraseRect(bool whole, const Rect &box) {
+ warning("eraseRect: not a graphics window");
+}
+
+void Window::fillRect(glui32 color, const Rect &box) {
+ warning("fillRect: not a graphics window");
+}
+
+void Window::setBackgroundColor(glui32 color) {
+ warning("setBackgroundColor: not a graphics window");
+}
+
+const WindowStyle *Window::getStyles() const {
+ warning("getStyles: not a text window");
+ return nullptr;
+}
+
+void Window::setTerminatorsLineEvent(glui32 *keycodes, glui32 count) {
+ if (dynamic_cast<TextBufferWindow *>(this) || dynamic_cast<TextGridWindow *>(this)) {
+ delete _lineTerminatorsBase;
+ _lineTerminatorsBase = nullptr;
+
+ if (!keycodes || count == 0) {
+ _termCt = 0;
+ } else {
+ _lineTerminatorsBase = new glui32[count + 1];
+ if (_lineTerminatorsBase) {
+ memcpy(_lineTerminatorsBase, keycodes, count * sizeof(glui32));
+ _lineTerminatorsBase[count] = 0;
+ _termCt = count;
+ }
+ }
+ } else {
+ warning("setTerminatorsLineEvent: window does not support keyboard input");
+ }
+}
+
+bool Window::checkTerminator(glui32 ch) {
+ if (ch == keycode_Escape)
+ return true;
+ else if (ch >= keycode_Func12 && ch <= keycode_Func1)
+ return true;
+ else
+ return false;
+}
+
+bool Window::imageDraw(glui32 image, glui32 align, glsi32 val1, glsi32 val2) {
+ if (!g_conf->_graphics)
+ return false;
+
+ TextBufferWindow *bufWin = dynamic_cast<TextBufferWindow *>(this);
+ GraphicsWindow *graWin = dynamic_cast<GraphicsWindow *>(this);
+
+ if (bufWin)
+ return bufWin->drawPicture(image, val1, false, 0, 0);
+ if (graWin)
+ return graWin->drawPicture(image, val1, val2, false, 0, 0);
+
+ return false;
+}
+
+void Window::getSize(glui32 *width, glui32 *height) const {
+ if (width)
+ *width = 0;
+ if (height)
+ *height = 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+BlankWindow::BlankWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
+ _type = wintype_Blank;
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Attributes::clear() {
+ fgset = 0;
+ bgset = 0;
+ fgcolor = 0;
+ bgcolor = 0;
+ reverse = false;
+ hyper = 0;
+ style = 0;
+}
+
+byte *Attributes::attrBg(WindowStyle *styles) {
+ int revset = reverse || (styles[style].reverse && !Windows::_overrideReverse);
+
+ int zfset = fgset ? fgset : Windows::_overrideFgSet;
+ int zbset = bgset ? bgset : Windows::_overrideBgSet;
+
+ int zfore = fgset ? fgcolor : Windows::_overrideFgVal;
+ int zback = bgset ? bgcolor : Windows::_overrideBgVal;
+
+ if (zfset && zfore != Windows::_zcolor_fg) {
+ Windows::_zcolor_Foreground[0] = (zfore >> 16) & 0xff;
+ Windows::_zcolor_Foreground[1] = (zfore >> 8) & 0xff;
+ Windows::_zcolor_Foreground[2] = (zfore) & 0xff;
+ Windows::_zcolor_fg = zfore;
+ }
+
+ if (zbset && zback != Windows::_zcolor_bg) {
+ Windows::_zcolor_Background[0] = (zback >> 16) & 0xff;
+ Windows::_zcolor_Background[1] = (zback >> 8) & 0xff;
+ Windows::_zcolor_Background[2] = (zback) & 0xff;
+ Windows::_zcolor_bg = zback;
+ }
+
+ if (!revset) {
+ if (zbset)
+ return Windows::_zcolor_Background;
+ else
+ return styles[style].bg;
+ } else {
+ if (zfset)
+ if (zfore == zback)
+ return Windows::rgbShift(Windows::_zcolor_Foreground);
+ else
+ return Windows::_zcolor_Foreground;
+ else if (zbset && !memcmp(styles[style].fg, Windows::_zcolor_Background, 3))
+ return Windows::_zcolor_LightGrey;
+ else
+ return styles[style].fg;
+ }
+}
+
+byte *Attributes::attrFg(WindowStyle *styles) {
+ int revset = reverse || (styles[style].reverse && !Windows::_overrideReverse);
+
+ int zfset = fgset ? fgset : Windows::_overrideFgSet;
+ int zbset = bgset ? bgset : Windows::_overrideBgSet;
+
+ int zfore = fgset ? fgcolor : Windows::_overrideFgVal;
+ int zback = bgset ? bgcolor : Windows::_overrideBgVal;
+
+ if (zfset && zfore != Windows::_zcolor_fg) {
+ Windows::_zcolor_Foreground[0] = (zfore >> 16) & 0xff;
+ Windows::_zcolor_Foreground[1] = (zfore >> 8) & 0xff;
+ Windows::_zcolor_Foreground[2] = (zfore) & 0xff;
+ Windows::_zcolor_fg = zfore;
+ }
+
+ if (zbset && zback != Windows::_zcolor_bg) {
+ Windows::_zcolor_Background[0] = (zback >> 16) & 0xff;
+ Windows::_zcolor_Background[1] = (zback >> 8) & 0xff;
+ Windows::_zcolor_Background[2] = (zback) & 0xff;
+ Windows::_zcolor_bg = zback;
+ }
+
+ if (!revset) {
+ if (zfset)
+ if (zfore == zback)
+ return Windows::rgbShift(Windows::_zcolor_Foreground);
+ else
+ return Windows::_zcolor_Foreground;
+ else if (zbset && !memcmp(styles[style].fg, Windows::_zcolor_Background, 3))
+ return Windows::_zcolor_LightGrey;
+ else
+ return styles[style].fg;
+ } else {
+ if (zbset)
+ return Windows::_zcolor_Background;
+ else
+ return styles[style].bg;
+ }
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/glk/windows.h b/engines/glk/windows.h
new file mode 100644
index 0000000000..5bf5f2aa32
--- /dev/null
+++ b/engines/glk/windows.h
@@ -0,0 +1,541 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_WINDOWS_H
+#define GLK_WINDOWS_H
+
+#include "common/array.h"
+#include "common/list.h"
+#include "common/rect.h"
+#include "graphics/screen.h"
+#include "glk/events.h"
+#include "glk/glk_types.h"
+#include "glk/fonts.h"
+#include "glk/selection.h"
+#include "glk/streams.h"
+
+namespace Gargoyle {
+
+class Window;
+class PairWindow;
+
+#define HISTORYLEN 100
+#define SCROLLBACK 512
+#define TBLINELEN 300
+#define GLI_SUBPIX 8
+
+
+/**
+ * Main windows manager
+ */
+class Windows {
+ friend class Window;
+public:
+ class iterator {
+ private:
+ Windows *_windows;
+ Window *_current;
+ public:
+ /**
+ * Constructor
+ */
+ iterator(Windows *windows, Window *start) : _windows(windows), _current(start) {}
+
+ /**
+ * Dereference
+ */
+ Window *operator*() const {
+ return _current;
+ }
+
+ /**
+ * Move to next
+ */
+ iterator &operator++();
+
+ /**
+ * Equality test
+ */
+ bool operator==(const iterator &i) {
+ return _current == i._current;
+ }
+
+ /**
+ * Inequality test
+ */
+ bool operator!=(const iterator &i) {
+ return _current != i._current;
+ }
+ };
+ friend class iterator;
+private:
+ Graphics::Screen *_screen;
+ Window *_windowList; ///< List of all windows
+ Window *_rootWin; ///< The topmost window
+ Window *_focusWin; ///< The window selected by the player
+ bool _drawSelect;
+private:
+ /**
+ * Create a new window
+ */
+ Window *newWindow(glui32 type, glui32 rock);
+
+ /**
+ * Create a new pair window
+ */
+ PairWindow *newPairWindow(glui32 method, Window *key, glui32 size);
+
+ /**
+ * Set the window focus
+ */
+ void refocus(Window *win);
+
+ /**
+ * Used to loop over windows in tree order
+ */
+ Window *iterateTreeOrder(Window *win);
+
+ /**
+ * Pick first window which has a more request
+ */
+ void inputMoreFocus();
+
+ /**
+ *
+ */
+ void inputNextFocus();
+
+ /**
+ * Pick first window which might want scrolling.
+ * This is called after pressing page keys.
+ */
+ void inputScrollFocus();
+public:
+ static bool _overrideReverse;
+ static bool _overrideFgSet;
+ static bool _overrideBgSet;
+ static bool _forceRedraw;
+ static bool _claimSelect;
+ static bool _moreFocus;
+ static int _overrideFgVal;
+ static int _overrideBgVal;
+ static int _zcolor_fg, _zcolor_bg;
+ static byte _zcolor_LightGrey[3];
+ static byte _zcolor_Foreground[3];
+ static byte _zcolor_Background[3];
+ static byte _zcolor_Bright[3];
+
+ static byte *rgbShift(byte *rgb);
+public:
+ /**
+ * Constructor
+ */
+ Windows(Graphics::Screen *screen);
+
+ /**
+ * Open a new window
+ */
+ Window *windowOpen(Window *splitwin, glui32 method, glui32 size,
+ glui32 wintype, glui32 rock);
+
+ /**
+ * Close an existing window
+ */
+ void windowClose(Window *win, StreamResult *result = nullptr);
+
+ /**
+ * Return the root window
+ */
+ Window *getRoot() const {
+ return _rootWin;
+ }
+
+ /**
+ * Gets the focused window
+ */
+ Window *getFocusWindow() const {
+ return _focusWin;
+ }
+
+ /**
+ * Setst the focused window
+ */
+ void setFocus(Window *win) {
+ _focusWin = win;
+ }
+
+ /**
+ * Pick first window which might want input. This is called after every keystroke.
+ */
+ void inputGuessFocus();
+
+ /**
+ * Handle input keypress
+ */
+ void inputHandleKey(glui32 key);
+
+ /**
+ * Handle mouse clicks
+ */
+ void inputHandleClick(const Point &pos);
+
+ void selectionChanged();
+
+ void clearClaimSelect() {
+ _claimSelect = false;
+ }
+
+ /**
+ * Rearrange windows
+ */
+ void rearrange();
+
+ void redraw();
+
+ void redrawRect(const Rect &r);
+
+ /**
+ * Repaint an area of the windows
+ */
+ void repaint(const Rect &box);
+
+ /**
+ * Get an iterator that will move over the tree
+ */
+ iterator begin() {
+ return iterator(this, _windowList);
+ }
+
+ /**
+ * Returns the end point of window iteration
+ */
+ iterator end() {
+ return iterator(this, nullptr);
+ }
+};
+
+/**
+ * Window styles
+ */
+struct WindowStyle {
+ FACES font;
+ byte bg[3];
+ byte fg[3];
+ bool reverse;
+
+ /**
+ * Equality comparison
+ */
+ bool operator==(const WindowStyle &src) const {
+ return !memcmp(this, &src, sizeof(WindowStyle));
+ }
+
+ /**
+ * Returns true if the font is proportinate
+ */
+ bool isProp() const {
+ return font == PROPR || font == PROPI || font == PROPB || font == PROPZ;
+ }
+
+ /**
+ * Returns true ifont the font is bold
+ */
+ bool isBold() const {
+ return font == PROPB || font == PROPZ || font == MONOB || font == MONOZ;
+ }
+
+ /**
+ * Returns true ifont the font is italic
+ */
+ bool isItalic() const {
+ return font == PROPI || font == PROPZ || font == MONOI || font == MONOZ;
+ }
+
+ /**
+ * Returns a font that has the following combination of proportinate, bold, and italic
+ */
+ static FACES makeFont(bool p, bool b, bool i) {
+ if (p && !b && !i) return PROPR;
+ if (p && !b && i) return PROPI;
+ if (p && b && !i) return PROPB;
+ if (p && b && i) return PROPZ;
+ if (!p && !b && !i) return MONOR;
+ if (!p && !b && i) return MONOI;
+ if (!p && b && !i) return MONOB;
+ if (!p && b && i) return MONOZ;
+ return PROPR;
+ }
+};
+
+/**
+ * Window attributes
+ */
+struct Attributes {
+ unsigned fgset : 1;
+ unsigned bgset : 1;
+ unsigned reverse : 1;
+ unsigned : 1;
+ unsigned style : 4;
+ unsigned fgcolor : 24;
+ unsigned bgcolor : 24;
+ unsigned hyper : 32;
+
+ /**
+ * Constructor
+ */
+ Attributes() {
+ clear();
+ }
+
+ /**
+ * Clear
+ */
+ void clear();
+
+ /**
+ * Set the style
+ */
+ void set(glui32 s) {
+ clear();
+ style = s;
+ }
+
+ /**
+ * Equality comparison
+ */
+ bool operator==(const Attributes &src) {
+ return fgset == src.fgset && bgset == src.bgset && reverse == src.reverse
+ && style == src.style && fgcolor == src.fgcolor && bgcolor == src.bgcolor
+ && hyper == src.hyper;
+ }
+ /**
+ * Inequality comparison
+ */
+ bool operator!=(const Attributes &src) {
+ return fgset != src.fgset || bgset != src.bgset || reverse != src.reverse
+ || style != src.style || fgcolor != src.fgcolor || bgcolor != src.bgcolor
+ || hyper != src.hyper;
+ }
+
+ /**
+ * Return the background color for the current font style
+ */
+ byte *attrBg(WindowStyle *styles);
+
+ /**
+ * Return the foreground color for the current font style
+ */
+ byte *attrFg(WindowStyle *styles);
+
+ /**
+ * Get the font for the current font style
+ */
+ FACES attrFont(WindowStyle *styles) const {
+ return styles[style].font;
+ }
+};
+
+/**
+ * Window definition
+ */
+class Window {
+public:
+ Windows *_windows;
+ glui32 _rock;
+ glui32 _type;
+
+ Window *_parent; ///< pair window which contains this one
+ Window *_next, *_prev; ///< in the big linked list of windows
+ Rect _bbox;
+ int _yAdj;
+
+ Stream *_stream; ///< the window stream.
+ Stream *_echoStream; ///< the window's echo stream, if any.
+
+ bool _lineRequest;
+ bool _lineRequestUni;
+ bool _charRequest;
+ bool _charRequestUni;
+ bool _mouseRequest;
+ bool _hyperRequest;
+ bool _moreRequest;
+ bool _scrollRequest;
+ bool _imageLoaded;
+
+ glui32 _echoLineInputBase;
+ glui32 *_lineTerminatorsBase;
+ glui32 _termCt;
+
+ Attributes _attr;
+ byte _bgColor[3];
+ byte _fgColor[3];
+
+ gidispatch_rock_t _dispRock;
+public:
+ static bool checkTerminator(glui32 ch);
+public:
+ /**
+ * Constructor
+ */
+ Window(Windows *windows, uint32 rock);
+
+ /**
+ * Destructor
+ */
+ virtual ~Window();
+
+ /**
+ * Close and delete the window
+ */
+ void close(bool recurse = true);
+
+ /**
+ * Rearranges the window
+ */
+ virtual void rearrange(const Rect &box) {
+ _bbox = box;
+ }
+
+ /**
+ * Get window split size within parent pair window
+ */
+ virtual glui32 getSplit(glui32 size, bool vertical) const {
+ return 0;
+ }
+
+ /**
+ * Write a character
+ */
+ virtual void putCharUni(uint32 ch) {}
+
+ /**
+ * Unput a unicode character
+ */
+ virtual bool unputCharUni(uint32 ch) {
+ return false;
+ }
+
+ /**
+ * Move the cursor
+ */
+ virtual void moveCursor(const Point &newPos);
+
+ /**
+ * Clear the window
+ */
+ virtual void clear() {}
+
+ /**
+ * Click the window
+ */
+ virtual void click(const Point &newPos) {}
+
+ /**
+ * Prepare for inputing a line
+ */
+ virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen);
+
+ /**
+ * Prepare for inputing a line
+ */
+ virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen);
+
+ /**
+ * Cancel an input line event
+ */
+ virtual void cancelLineEvent(Event *ev);
+
+ /**
+ * Cancel a character event
+ */
+ virtual void cancelCharEvent() {}
+
+ /**
+ * Cancel a mouse event
+ */
+ virtual void cancelMouseEvent() {}
+
+ /**
+ * Cancel a hyperlink event
+ */
+ virtual void cancelHyperlinkEvent() {}
+
+ /**
+ * Redraw the window
+ */
+ virtual void redraw();
+
+ bool imageDraw(glui32 image, glui32 align, glsi32 val1, glsi32 val2);
+
+ int acceptScroll(glui32 arg);
+
+ void setTerminatorsLineEvent(glui32 *keycodes, glui32 count);
+
+ virtual void acceptReadLine(glui32 arg);
+
+ virtual void acceptReadChar(glui32 arg);
+
+ virtual void getArrangement(glui32 *method, glui32 *size, Window **keyWin);
+
+ virtual void setArrangement(glui32 method, glui32 size, Window *keyWin);
+
+ virtual void getSize(glui32 *width, glui32 *height) const;
+
+ virtual void requestCharEvent();
+
+ virtual void requestCharEventUni();
+
+ virtual void setEchoLineEvent(glui32 val) {}
+
+ virtual void requestMouseEvent() {}
+
+ virtual void requestHyperlinkEvent() {}
+
+ virtual void flowBreak();
+
+ virtual void eraseRect(bool whole, const Rect &box);
+
+ virtual void fillRect(glui32 color, const Rect &box);
+
+ virtual void setBackgroundColor(glui32 color);
+
+ /**
+ * Returns a pointer to the styles for the window
+ */
+ virtual const WindowStyle *getStyles() const;
+};
+typedef Window *winid_t;
+
+/**
+ * Blank window
+ */
+class BlankWindow : public Window {
+public:
+ /**
+ * Constructor
+ */
+ BlankWindow(Windows *windows, uint32 rock);
+};
+
+} // End of namespace Gargoyle
+
+#endif