From 1fb931fbd950324754536ee0b33ed0b91f68c9a2 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Tue, 13 Nov 2018 19:47:07 -0800 Subject: GLK: Changing gargoyle folder to glk --- engines/glk/blorb.cpp | 543 ++ engines/glk/blorb.h | 146 + engines/glk/conf.cpp | 254 + engines/glk/conf.h | 136 + engines/glk/configure.engine | 3 + engines/glk/detection.cpp | 323 + engines/glk/detection_tables.h | 29 + engines/glk/events.cpp | 402 + engines/glk/events.h | 291 + engines/glk/fonts.cpp | 157 + engines/glk/fonts.h | 118 + engines/glk/frotz/detection.cpp | 80 + engines/glk/frotz/detection.h | 43 + engines/glk/frotz/detection_tables.cpp | 85 + engines/glk/frotz/detection_tables.h | 85 + engines/glk/frotz/frotz.cpp | 98 + engines/glk/frotz/frotz.h | 72 + engines/glk/frotz/frotz_types.h | 297 + engines/glk/frotz/glk_interface.cpp | 498 ++ engines/glk/frotz/glk_interface.h | 195 + engines/glk/frotz/mem.cpp | 419 + engines/glk/frotz/mem.h | 280 + engines/glk/frotz/processor.cpp | 664 ++ engines/glk/frotz/processor.h | 1584 ++++ engines/glk/frotz/processor_buffer.cpp | 192 + engines/glk/frotz/processor_input.cpp | 223 + engines/glk/frotz/processor_maths.cpp | 105 + engines/glk/frotz/processor_mem.cpp | 218 + engines/glk/frotz/processor_objects.cpp | 732 ++ engines/glk/frotz/processor_screen.cpp | 528 ++ engines/glk/frotz/processor_streams.cpp | 786 ++ engines/glk/frotz/processor_table.cpp | 120 + engines/glk/frotz/processor_text.cpp | 886 +++ engines/glk/frotz/processor_variables.cpp | 199 + engines/glk/frotz/quetzal.cpp | 485 ++ engines/glk/frotz/quetzal.h | 97 + engines/glk/gargoyle.cpp | 120 + engines/glk/gargoyle.h | 193 + engines/glk/glk.cpp | 1174 +++ engines/glk/glk.h | 292 + engines/glk/glk_types.h | 208 + engines/glk/module.mk | 50 + engines/glk/picture.cpp | 63 + engines/glk/picture.h | 73 + engines/glk/scott/detection.cpp | 109 + engines/glk/scott/detection.h | 43 + engines/glk/scott/scott.cpp | 1263 +++ engines/glk/scott/scott.h | 191 + engines/glk/screen.cpp | 72 + engines/glk/screen.h | 65 + engines/glk/selection.cpp | 321 + engines/glk/selection.h | 120 + engines/glk/speech.h | 49 + engines/glk/streams.cpp | 1592 ++++ engines/glk/streams.h | 639 ++ engines/glk/time.cpp | 120 + engines/glk/time.h | 93 + engines/glk/unicode.cpp | 151 + engines/glk/unicode.h | 51 + engines/glk/unicode_gen.cpp | 11826 ++++++++++++++++++++++++++++ engines/glk/unicode_gen.h | 563 ++ engines/glk/utils.cpp | 44 + engines/glk/utils.h | 51 + engines/glk/window_graphics.cpp | 272 + engines/glk/window_graphics.h | 113 + engines/glk/window_pair.cpp | 236 + engines/glk/window_pair.h | 73 + engines/glk/window_text_buffer.cpp | 1636 ++++ engines/glk/window_text_buffer.h | 245 + engines/glk/window_text_grid.cpp | 654 ++ engines/glk/window_text_grid.h | 195 + engines/glk/windows.cpp | 773 ++ engines/glk/windows.h | 541 ++ 73 files changed, 35647 insertions(+) create mode 100644 engines/glk/blorb.cpp create mode 100644 engines/glk/blorb.h create mode 100644 engines/glk/conf.cpp create mode 100644 engines/glk/conf.h create mode 100644 engines/glk/configure.engine create mode 100644 engines/glk/detection.cpp create mode 100644 engines/glk/detection_tables.h create mode 100644 engines/glk/events.cpp create mode 100644 engines/glk/events.h create mode 100644 engines/glk/fonts.cpp create mode 100644 engines/glk/fonts.h create mode 100644 engines/glk/frotz/detection.cpp create mode 100644 engines/glk/frotz/detection.h create mode 100644 engines/glk/frotz/detection_tables.cpp create mode 100644 engines/glk/frotz/detection_tables.h create mode 100644 engines/glk/frotz/frotz.cpp create mode 100644 engines/glk/frotz/frotz.h create mode 100644 engines/glk/frotz/frotz_types.h create mode 100644 engines/glk/frotz/glk_interface.cpp create mode 100644 engines/glk/frotz/glk_interface.h create mode 100644 engines/glk/frotz/mem.cpp create mode 100644 engines/glk/frotz/mem.h create mode 100644 engines/glk/frotz/processor.cpp create mode 100644 engines/glk/frotz/processor.h create mode 100644 engines/glk/frotz/processor_buffer.cpp create mode 100644 engines/glk/frotz/processor_input.cpp create mode 100644 engines/glk/frotz/processor_maths.cpp create mode 100644 engines/glk/frotz/processor_mem.cpp create mode 100644 engines/glk/frotz/processor_objects.cpp create mode 100644 engines/glk/frotz/processor_screen.cpp create mode 100644 engines/glk/frotz/processor_streams.cpp create mode 100644 engines/glk/frotz/processor_table.cpp create mode 100644 engines/glk/frotz/processor_text.cpp create mode 100644 engines/glk/frotz/processor_variables.cpp create mode 100644 engines/glk/frotz/quetzal.cpp create mode 100644 engines/glk/frotz/quetzal.h create mode 100644 engines/glk/gargoyle.cpp create mode 100644 engines/glk/gargoyle.h create mode 100644 engines/glk/glk.cpp create mode 100644 engines/glk/glk.h create mode 100644 engines/glk/glk_types.h create mode 100644 engines/glk/module.mk create mode 100644 engines/glk/picture.cpp create mode 100644 engines/glk/picture.h create mode 100644 engines/glk/scott/detection.cpp create mode 100644 engines/glk/scott/detection.h create mode 100644 engines/glk/scott/scott.cpp create mode 100644 engines/glk/scott/scott.h create mode 100644 engines/glk/screen.cpp create mode 100644 engines/glk/screen.h create mode 100644 engines/glk/selection.cpp create mode 100644 engines/glk/selection.h create mode 100644 engines/glk/speech.h create mode 100644 engines/glk/streams.cpp create mode 100644 engines/glk/streams.h create mode 100644 engines/glk/time.cpp create mode 100644 engines/glk/time.h create mode 100644 engines/glk/unicode.cpp create mode 100644 engines/glk/unicode.h create mode 100644 engines/glk/unicode_gen.cpp create mode 100644 engines/glk/unicode_gen.h create mode 100644 engines/glk/utils.cpp create mode 100644 engines/glk/utils.h create mode 100644 engines/glk/window_graphics.cpp create mode 100644 engines/glk/window_graphics.h create mode 100644 engines/glk/window_pair.cpp create mode 100644 engines/glk/window_pair.h create mode 100644 engines/glk/window_text_buffer.cpp create mode 100644 engines/glk/window_text_buffer.h create mode 100644 engines/glk/window_text_grid.cpp create mode 100644 engines/glk/window_text_grid.h create mode 100644 engines/glk/windows.cpp create mode 100644 engines/glk/windows.h (limited to 'engines/glk') 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; ixnumchunks; 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 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; ixnumchunks; 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; ixnumresources; 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 { +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; +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; ipos(); + 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; jwrite(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 formats = g_system->getSupportedFormats(); + Graphics::PixelFormat format = formats.front(); + + for (Common::List::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(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(win); + GraphicsWindow *gfxWin = dynamic_cast(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(win); + GraphicsWindow *gfxWin = dynamic_cast(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, <, &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 > 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(_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(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 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 _chars; + Common::Array _attrs; + bool dirty; + + /** + * Constructor + */ + TextGridRow() : dirty(false) {} + + /** + * Resize the row + */ + void resize(size_t newSize); + }; + typedef Common::Array 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(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(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(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(win); + TextBufferWindow *bufWindow = dynamic_cast(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(win); + if (pairWin) { + if (!pairWin->_backward) + return pairWin->_child1; + else + return pairWin->_child2; + } else { + while (win->_parent) { + pairWin = dynamic_cast(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(wx); + + if (pairWin && pairWin->_key == this) { + pairWin->_key = nullptr; + pairWin->_keyDamage = true; + } + } + + PairWindow *pairWin = dynamic_cast(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(this) || dynamic_cast(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(this); + GraphicsWindow *graWin = dynamic_cast(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 -- cgit v1.2.3