--- /dev/null
+#ifndef BLORB_H
+#define BLORB_H
+
+/* blorb.h: Header file for Blorb library, version 1.0.2.
+ Designed by Andrew Plotkin <erkyrath@eblong.com>
+ http://www.eblong.com/zarf/blorb/index.html
+
+ This is the header that a Z-machine interpreter should include.
+ It defines everything that the interpreter has to know.
+*/
+
+/* Things you (the porter) have to edit: */
+
+/* As you might expect, uint32 must be a 32-bit unsigned numeric type,
+ and uint16 a 16-bit unsigned numeric type. You should also uncomment
+ exactly one of the two ENDIAN definitions. */
+
+/* #define BLORB_BIG_ENDIAN */
+
+#define BLORB_LITTLE_ENDIAN
+typedef unsigned long uint32;
+typedef unsigned short uint16;
+
+/* End of things you have to edit. */
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* Error type and error codes */
+typedef int bb_err_t;
+
+#define bb_err_None (0)
+#define bb_err_CompileTime (1)
+#define bb_err_Alloc (2)
+#define bb_err_Read (3)
+#define bb_err_NotAMap (4)
+#define bb_err_Format (5)
+#define bb_err_NotFound (6)
+
+/* Methods for loading a chunk */
+#define bb_method_DontLoad (0)
+#define bb_method_Memory (1)
+#define bb_method_FilePos (2)
+
+/* Four-byte constants */
+
+#define bb_make_id(c1, c2, c3, c4) \
+ (((c1) << 24) | ((c2) << 16) | ((c3) << 8) | (c4))
+
+#define bb_ID_Snd (bb_make_id('S', 'n', 'd', ' '))
+#define bb_ID_Exec (bb_make_id('E', 'x', 'e', 'c'))
+#define bb_ID_Pict (bb_make_id('P', 'i', 'c', 't'))
+#define bb_ID_Copyright (bb_make_id('(', 'c', ')', ' '))
+#define bb_ID_AUTH (bb_make_id('A', 'U', 'T', 'H'))
+#define bb_ID_ANNO (bb_make_id('A', 'N', 'N', 'O'))
+
+/* bb_result_t: Result when you try to load a chunk. */
+typedef struct bb_result_struct {
+ int chunknum; /* The chunk number (for use in bb_unload_chunk(), etc.) */
+ union {
+ void *ptr; /* A pointer to the data (if you used bb_method_Memory) */
+ uint32 startpos; /* The position in the file (if you used bb_method_FilePos) */
+ } data;
+ uint32 length; /* The length of the data */
+} bb_result_t;
+
+/* bb_aux_sound_t: Extra data which may be associated with a sound. */
+typedef struct bb_aux_sound_struct {
+ char repeats;
+} bb_aux_sound_t;
+
+/* bb_aux_pict_t: Extra data which may be associated with an image. */
+typedef struct bb_aux_pict_struct {
+ uint32 ratnum, ratden;
+ uint32 minnum, minden;
+ uint32 maxnum, maxden;
+} bb_aux_pict_t;
+
+/* bb_resolution_t: The global resolution data. */
+typedef struct bb_resolution_struct {
+ uint32 px, py;
+ uint32 minx, miny;
+ uint32 maxx, maxy;
+} bb_resolution_t;
+
+/* bb_color_t: Guess what. */
+typedef struct bb_color_struct {
+ unsigned char red, green, blue;
+} bb_color_t;
+
+/* bb_palette_t: The palette data. */
+typedef struct bb_palette_struct {
+ int isdirect;
+ union {
+ int depth; /* The depth (if isdirect is TRUE). Either 16 or 32. */
+ struct {
+ int numcolors;
+ bb_color_t *colors;
+ } table; /* The list of colors (if isdirect is FALSE). */
+ } data;
+} bb_palette_t;
+
+/* bb_zheader_t: Information to identify a Z-code file. */
+typedef struct bb_zheader_struct {
+ uint16 releasenum; /* Bytes $2-3 of header. */
+ char serialnum[6]; /* Bytes $12-17 of header. */
+ uint16 checksum; /* Bytes $1C-1D of header. */
+ /* The initpc field is not used by Blorb. */
+} bb_zheader_t;
+
+/* bb_map_t: Holds the complete description of an open Blorb file.
+ This type is opaque for normal interpreter use. */
+typedef struct bb_map_struct bb_map_t;
+
+/* Function declarations. These functions are of fairly general use;
+ they would apply to any Blorb file. */
+
+extern bb_err_t bb_create_map(FILE *file, bb_map_t **newmap);
+extern bb_err_t bb_destroy_map(bb_map_t *map);
+
+extern char *bb_err_to_string(bb_err_t err);
+
+extern bb_err_t bb_load_chunk_by_type(bb_map_t *map, int method,
+ bb_result_t *res, uint32 chunktype, int count);
+extern bb_err_t bb_load_chunk_by_number(bb_map_t *map, int method,
+ bb_result_t *res, int chunknum);
+extern bb_err_t bb_unload_chunk(bb_map_t *map, int chunknum);
+
+extern bb_err_t bb_load_resource(bb_map_t *map, int method,
+ bb_result_t *res, uint32 usage, int resnum);
+extern bb_err_t bb_count_resources(bb_map_t *map, uint32 usage,
+ int *num, int *min, int *max);
+
+/* More function declarations. These functions are more or less
+ specific to the Z-machine's use of Blorb. */
+
+extern uint16 bb_get_release_num(bb_map_t *map);
+extern bb_zheader_t *bb_get_zheader(bb_map_t *map);
+extern bb_resolution_t *bb_get_resolution(bb_map_t *map);
+extern bb_err_t bb_get_palette(bb_map_t *map, bb_palette_t **res);
+extern bb_err_t bb_load_resource_pict(bb_map_t *map, int method,
+ bb_result_t *res, int resnum, bb_aux_pict_t **auxdata);
+extern bb_err_t bb_load_resource_snd(bb_map_t *map, int method,
+ bb_result_t *res, int resnum, bb_aux_sound_t **auxdata);
+
+#endif /* BLORB_H */
--- /dev/null
+/* blorblib.c: Blorb file reader library, version 1.0.2.
+ Designed by Andrew Plotkin <erkyrath@eblong.com>
+ http://www.eblong.com/zarf/blorb/index.html
+
+ This is portable code to read a Blorb file. Add it to your
+ interpreter, #include "blorb.h", and you're ready to go.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "blorb.h"
+#include "blorblow.h"
+
+/* This endian stuff needs to be fixed with something from
+ * http://www.ibm.com/developerworks/aix/library/au-endianc/index.html
+ */
+
+#ifdef BLORB_BIG_ENDIAN
+static char contentsticker[] = "\nBlorb Library 1.0 (big-endian)\n";
+#define bb_native2(v) (v)
+#define bb_native4(v) (v)
+#endif
+
+#ifdef BLORB_LITTLE_ENDIAN
+static char contentsticker[] = "\nBlorb Library 1.0 (little-endian)\n";
+#define bb_native2(v) \
+ ( (((uint16)(v) >> 8) & 0x00ff) \
+ | (((uint16)(v) << 8) & 0xff00))
+#define bb_native4(v) \
+ ( (((uint32)(v) >> 24) & 0x000000ff) \
+ | (((uint32)(v) >> 8) & 0x0000ff00) \
+ | (((uint32)(v) << 8) & 0x00ff0000) \
+ | (((uint32)(v) << 24) & 0xff000000))
+#endif
+
+#ifdef CRAPOLA
+#ifndef bb_native4
+#error "You must define either BLORB_BIG_ENDIAN"
+#error "or BLORB_LITTLE_ENDIAN in blorb.h in order"
+#error "to compile this library."
+#endif
+#endif /* CRAPOLA */
+
+static int lib_inited = FALSE;
+
+static bb_err_t bb_initialize_map(bb_map_t *map);
+static bb_err_t bb_initialize(void);
+static int sortsplot(const void *p1, const void *p2);
+
+/* Do some one-time startup tests. */
+static bb_err_t bb_initialize()
+{
+ union {
+ uint32 val;
+ unsigned char ch[4];
+ } test;
+ uint32 val;
+
+ if (sizeof(uint32) != 4 || sizeof(uint16) != 2)
+ return bb_err_CompileTime; /* Basic types are the wrong size. */
+
+ test.ch[0] = 0x13;
+ test.ch[1] = 0x57;
+ test.ch[2] = 0x9a;
+ test.ch[3] = 0xce;
+ val = test.val;
+ if (bb_native4(val) != 0x13579ace)
+ return bb_err_CompileTime; /* Wrong endianness. */
+
+ return bb_err_None;
+}
+
+bb_err_t bb_create_map(FILE *file, bb_map_t **newmap)
+{
+ bb_err_t err;
+ bb_map_t *map;
+ size_t readlen;
+ uint32 nextpos, totallength;
+ bb_chunkdesc_t *chunks;
+ int chunks_size, numchunks;
+ uint32 buffer[4];
+
+ *newmap = NULL;
+
+ if (!lib_inited) {
+ err = bb_initialize();
+ if (err)
+ return err;
+ lib_inited = TRUE;
+ }
+
+ /* First, chew through the file and index the chunks. */
+
+ err = fseek(file, 0, 0);
+ if (err)
+ return bb_err_Read;
+
+ readlen = fread(buffer, sizeof(uint32), 3, file);
+ if (readlen != 3)
+ return bb_err_Read;
+
+ if (bb_native4(buffer[0]) != bb_ID_FORM)
+ return bb_err_Format;
+ if (bb_native4(buffer[2]) != bb_ID_IFRS)
+ return bb_err_Format;
+
+ totallength = bb_native4(buffer[1]) + 8;
+ nextpos = 12;
+
+ chunks_size = 8;
+ numchunks = 0;
+ chunks = (bb_chunkdesc_t *)malloc(sizeof(bb_chunkdesc_t) * chunks_size);
+
+ while (nextpos < totallength) {
+ uint32 type, len;
+ int chunum;
+ bb_chunkdesc_t *chu;
+
+ err = fseek(file, nextpos, 0);
+ if (err)
+ return bb_err_Read;
+
+ readlen = fread(buffer, sizeof(uint32), 2, file);
+ if (readlen != 2)
+ return bb_err_Read;
+
+ type = bb_native4(buffer[0]);
+ len = bb_native4(buffer[1]);
+
+ if (numchunks >= chunks_size) {
+ chunks_size *= 2;
+ chunks = (bb_chunkdesc_t *)realloc(chunks,
+ sizeof(bb_chunkdesc_t) * chunks_size);
+ }
+
+ chunum = numchunks;
+ chu = &(chunks[chunum]);
+ numchunks++;
+
+ chu->type = type;
+ chu->startpos = nextpos;
+ if (type == bb_ID_FORM) {
+ chu->datpos = nextpos;
+ chu->len = len+8;
+ }
+ else {
+ chu->datpos = nextpos+8;
+ chu->len = len;
+ }
+ chu->ptr = NULL;
+ chu->auxdatnum = -1;
+
+ nextpos = nextpos + len + 8;
+ if (nextpos & 1)
+ nextpos++;
+
+ if (nextpos > totallength)
+ return bb_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 = (bb_map_t *)malloc(sizeof(bb_map_t));
+ if (!map) {
+ free(chunks);
+ return bb_err_Alloc;
+ }
+
+ map->inited = bb_Inited_Magic;
+ map->file = file;
+ map->chunks = chunks;
+ map->numchunks = numchunks;
+ map->resources = NULL;
+ map->ressorted = NULL;
+ map->numresources = 0;
+ map->releasenum = 0;
+ map->zheader = NULL;
+ map->resolution = NULL;
+ map->palettechunk = -1;
+ map->palette = NULL;
+ map->auxsound = NULL;
+ map->auxpict = NULL;
+
+ /* Now we do everything else involved in loading the Blorb file,
+ such as building resource lists. */
+
+ err = bb_initialize_map(map);
+ if (err) {
+ bb_destroy_map(map);
+ return err;
+ }
+
+ *newmap = map;
+ return bb_err_None;
+}
+
+static bb_err_t bb_initialize_map(bb_map_t *map)
+{
+ /* It is important that the map structure be kept valid during this
+ function. If this returns an error, bb_destroy_map() will be called. */
+
+ int ix, jx;
+ bb_result_t chunkres;
+ bb_err_t err;
+ uint32 *ptr;
+ uint32 len;
+ uint32 val;
+ int numres;
+ int gotindex = FALSE;
+
+ for (ix=0; ix<map->numchunks; ix++) {
+ bb_chunkdesc_t *chu = &map->chunks[ix];
+
+ switch (chu->type) {
+
+ case bb_ID_RIdx:
+ /* Resource index chunk: build the resource list and sort it. */
+
+ if (gotindex)
+ return bb_err_Format; /* duplicate index chunk */
+ err = bb_load_chunk_by_number(map, bb_method_Memory,
+ &chunkres, ix);
+ if (err)
+ return err;
+
+ ptr = chunkres.data.ptr;
+ len = chunkres.length;
+ val = ptr[0];
+ numres = bb_native4(val);
+
+ if (numres) {
+ int ix2;
+ bb_resdesc_t *resources;
+ bb_resdesc_t **ressorted;
+
+ if (len != numres*12+4)
+ return bb_err_Format; /* bad length field */
+
+ resources = (bb_resdesc_t *)malloc(numres * sizeof(bb_resdesc_t));
+ ressorted = (bb_resdesc_t **)malloc(numres * sizeof(bb_resdesc_t *));
+ if (!ressorted || !resources)
+ return bb_err_Alloc;
+
+ ix2 = 0;
+ for (jx=0; jx<numres; jx++) {
+ bb_resdesc_t *res = &(resources[jx]);
+ uint32 respos;
+
+ val = ptr[1+jx*3];
+ res->usage = bb_native4(val);
+ val = ptr[2+jx*3];
+ res->resnum = bb_native4(val);
+ val = ptr[3+jx*3];
+ respos = bb_native4(val);
+
+ while (ix2 < map->numchunks && map->chunks[ix2].startpos < respos)
+ ix2++;
+
+ if (ix2 >= map->numchunks || map->chunks[ix2].startpos != respos)
+ return bb_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. */
+ qsort(ressorted, numres, sizeof(bb_resdesc_t *),
+ &sortsplot);
+
+ map->numresources = numres;
+ map->resources = resources;
+ map->ressorted = ressorted;
+ }
+
+ bb_unload_chunk(map, ix);
+ gotindex = TRUE;
+ break;
+
+ case bb_ID_RelN:
+ /* Release number chunk: Get the release number. */
+
+ err = bb_load_chunk_by_number(map, bb_method_Memory,
+ &chunkres, ix);
+ if (err)
+ return err;
+
+ if (chunkres.length != 2)
+ return bb_err_Format;
+
+ {
+ uint16 val = *((uint16 *)chunkres.data.ptr);
+ map->releasenum = bb_native2(val);
+ }
+
+ bb_unload_chunk(map, ix);
+ break;
+
+ case bb_ID_IFhd:
+ /* Z-header chunk: Get the header info. */
+
+ err = bb_load_chunk_by_number(map, bb_method_Memory,
+ &chunkres, ix);
+ if (err)
+ return err;
+
+ if (chunkres.length < 13)
+ return bb_err_Format;
+
+ {
+ uint16 val;
+ bb_zheader_t *head = (bb_zheader_t *)malloc(sizeof(bb_zheader_t));
+ if (!head)
+ return bb_err_Alloc;
+
+ val = ((uint16 *)(chunkres.data.ptr))[0];
+ head->releasenum = bb_native2(val);
+
+ val = ((uint16 *)(chunkres.data.ptr))[4];
+ head->checksum = bb_native2(val);
+
+ for (jx=0; jx<6; jx++) {
+ head->serialnum[jx] = ((char *)(chunkres.data.ptr))[2+jx];
+ }
+
+ map->zheader = head;
+ }
+
+ bb_unload_chunk(map, ix);
+ break;
+
+ case bb_ID_Reso:
+ /* Resolution chunk: Get the window size data, and resolution
+ ratios for images. */
+
+ err = bb_load_chunk_by_number(map, bb_method_Memory,
+ &chunkres, ix);
+ if (err)
+ return err;
+
+ if (chunkres.length < 24)
+ return bb_err_Format;
+
+ ptr = chunkres.data.ptr;
+ len = chunkres.length;
+
+ {
+ bb_resolution_t *reso = (bb_resolution_t *)malloc(sizeof(bb_resolution_t));
+ if (!reso)
+ return bb_err_Alloc;
+
+ reso->px = bb_native4(ptr[0]);
+ reso->py = bb_native4(ptr[1]);
+ reso->minx = bb_native4(ptr[2]);
+ reso->miny = bb_native4(ptr[3]);
+ reso->maxx = bb_native4(ptr[4]);
+ reso->maxy = bb_native4(ptr[5]);
+
+ map->resolution = reso;
+ }
+
+ ptr += 6;
+ len -= 6*4;
+
+ len = len / 28;
+
+ if (len) {
+ bb_aux_pict_t *aux = (bb_aux_pict_t *)malloc(len * sizeof(bb_aux_pict_t));
+
+ for (jx=0; jx<len; jx++, ptr += 7) {
+ bb_result_t res;
+
+ err = bb_load_resource(map, bb_method_DontLoad, &res,
+ bb_ID_Pict, bb_native4(ptr[0]));
+ if (!err) {
+ bb_chunkdesc_t *chu = &(map->chunks[res.chunknum]);
+ if (chu->auxdatnum != -1)
+ return bb_err_Format; /* two image entries for this resource */
+ chu->auxdatnum = jx;
+ aux[jx].ratnum = bb_native4(ptr[1]);
+ aux[jx].ratden = bb_native4(ptr[2]);
+ aux[jx].minnum = bb_native4(ptr[3]);
+ aux[jx].minden = bb_native4(ptr[4]);
+ aux[jx].maxnum = bb_native4(ptr[5]);
+ aux[jx].maxden = bb_native4(ptr[6]);
+ }
+ }
+
+ map->auxpict = aux;
+ }
+
+ bb_unload_chunk(map, ix);
+ break;
+
+ case bb_ID_Loop:
+ /* Looping chunk: Get looping data for sounds. */
+
+ err = bb_load_chunk_by_number(map, bb_method_Memory,
+ &chunkres, ix);
+ if (err)
+ return err;
+
+ ptr = chunkres.data.ptr;
+ len = chunkres.length;
+
+ len = len / 8;
+
+ if (len) {
+ bb_aux_sound_t *aux = (bb_aux_sound_t *)malloc(len * sizeof(bb_aux_sound_t));
+
+ for (jx=0; jx<len; jx++, ptr += 2) {
+ bb_result_t res;
+
+ err = bb_load_resource(map, bb_method_DontLoad, &res,
+ bb_ID_Snd, bb_native4(ptr[0]));
+ if (!err) {
+ bb_chunkdesc_t *chu = &(map->chunks[res.chunknum]);
+ if (chu->auxdatnum != -1)
+ return bb_err_Format; /* two looping entries for this resource */
+ chu->auxdatnum = jx;
+ aux[jx].repeats = bb_native4(ptr[1]);
+ }
+ }
+
+ map->auxsound = aux;
+ }
+
+ bb_unload_chunk(map, ix);
+ break;
+
+ case bb_ID_Plte:
+ /* Palette chunk: Don't get the palette info now, since it may
+ be large and the interpreter may not care. But remember
+ the chunk number in case the interpreter asks later. */
+
+ map->palettechunk = ix;
+ break;
+ }
+ }
+
+ return bb_err_None;
+}
+
+bb_err_t bb_destroy_map(bb_map_t *map)
+{
+ int ix;
+
+ if (!map || !map->chunks || map->inited != bb_Inited_Magic)
+ return bb_err_NotAMap;
+
+ for (ix=0; ix<map->numchunks; ix++) {
+ bb_chunkdesc_t *chu = &(map->chunks[ix]);
+ if (chu->ptr) {
+ free(chu->ptr);
+ chu->ptr = NULL;
+ }
+ }
+
+ if (map->chunks) {
+ free(map->chunks);
+ map->chunks = NULL;
+ }
+
+ map->numchunks = 0;
+
+ if (map->resources) {
+ free(map->resources);
+ map->resources = NULL;
+ }
+
+ if (map->ressorted) {
+ free(map->ressorted);
+ map->ressorted = NULL;
+ }
+
+ map->numresources = 0;
+
+ if (map->zheader) {
+ free(map->zheader);
+ map->zheader = NULL;
+ }
+
+ if (map->resolution) {
+ free(map->resolution);
+ map->resolution = NULL;
+ }
+
+ if (map->palette) {
+ if (!map->palette->isdirect && map->palette->data.table.colors) {
+ free(map->palette->data.table.colors);
+ map->palette->data.table.colors = NULL;
+ }
+ free(map->palette);
+ map->palette = NULL;
+ }
+
+ if (map->auxsound) {
+ free(map->auxsound);
+ map->auxsound = NULL;
+ }
+
+ if (map->auxpict) {
+ free(map->auxpict);
+ map->auxpict = NULL;
+ }
+
+ map->file = NULL;
+ map->inited = 0;
+
+ free(map);
+
+ return bb_err_None;
+}
+
+/* Turn a four-byte constant into a string. This returns a static buffer,
+ so if you call it twice, the old value gets overwritten. */
+char *bb_id_to_string(uint32 id)
+{
+ static char buf[5];
+ buf[0] = (id >> 24) & 0xff;
+ buf[1] = (id >> 16) & 0xff;
+ buf[2] = (id >> 8) & 0xff;
+ buf[3] = (id) & 0xff;
+ buf[4] = '\0';
+ return buf;
+}
+
+/* Turn an error code into a string describing the error. */
+char *bb_err_to_string(bb_err_t err)
+{
+ switch (err) {
+ case bb_err_None:
+ return "ok";
+ case bb_err_CompileTime:
+ return "library compiled wrong";
+ case bb_err_Alloc:
+ return "cannot allocate memory";
+ case bb_err_Read:
+ return "cannot read from file";
+ case bb_err_NotAMap:
+ return "map structure is bad";
+ case bb_err_Format:
+ return "bad format in Blorb file";
+ case bb_err_NotFound:
+ return "data not found";
+ default:
+ return "unknown error";
+ }
+}
+
+/* This is used for binary searching and quicksorting the resource pointer list. */
+static int sortsplot(const void *p1, const void *p2)
+{
+ bb_resdesc_t *v1 = *(bb_resdesc_t **)p1;
+ bb_resdesc_t *v2 = *(bb_resdesc_t **)p2;
+ if (v1->usage < v2->usage)
+ return -1;
+ if (v1->usage > v2->usage)
+ return 1;
+ return v1->resnum - v2->resnum;
+}
+
+bb_err_t bb_load_chunk_by_type(bb_map_t *map, int method, bb_result_t *res,
+ uint32 type, int count)
+{
+ int ix;
+
+ for (ix=0; ix < map->numchunks; ix++) {
+ if (map->chunks[ix].type == type) {
+ if (count == 0)
+ break;
+ count--;
+ }
+ }
+
+ if (ix >= map->numchunks) {
+ return bb_err_NotFound;
+ }
+
+ return bb_load_chunk_by_number(map, method, res, ix);
+}
+
+bb_err_t bb_load_chunk_by_number(bb_map_t *map, int method, bb_result_t *res,
+ int chunknum)
+{
+ bb_chunkdesc_t *chu;
+
+ if (chunknum < 0 || chunknum >= map->numchunks)
+ return bb_err_NotFound;
+
+ chu = &(map->chunks[chunknum]);
+
+ switch (method) {
+
+ case bb_method_DontLoad:
+ /* do nothing */
+ break;
+
+ case bb_method_FilePos:
+ res->data.startpos = chu->datpos;
+ break;
+
+ case bb_method_Memory:
+ if (!chu->ptr) {
+ bb_err_t err;
+ size_t readlen;
+ void *dat = malloc(chu->len);
+
+ if (!dat)
+ return bb_err_Alloc;
+
+ err = fseek(map->file, chu->datpos, 0);
+ if (err)
+ return bb_err_Read;
+
+ readlen = fread(dat, 1, chu->len, map->file);
+ if (readlen != chu->len)
+ return bb_err_Read;
+
+ chu->ptr = dat;
+ }
+ res->data.ptr = chu->ptr;
+ break;
+ }
+
+ res->chunknum = chunknum;
+ res->length = chu->len;
+
+ return bb_err_None;
+}
+
+bb_err_t bb_load_resource(bb_map_t *map, int method, bb_result_t *res,
+ uint32 usage, int resnum)
+{
+ bb_resdesc_t sample;
+ bb_resdesc_t *ptr;
+ bb_resdesc_t **found;
+
+ sample.usage = usage;
+ sample.resnum = resnum;
+ ptr = &sample;
+
+ found = bsearch(&ptr, map->ressorted, map->numresources, sizeof(bb_resdesc_t *),
+ &sortsplot);
+
+ if (!found)
+ return bb_err_NotFound;
+
+ return bb_load_chunk_by_number(map, method, res, (*found)->chunknum);
+}
+
+bb_err_t bb_unload_chunk(bb_map_t *map, int chunknum)
+{
+ bb_chunkdesc_t *chu;
+
+ if (chunknum < 0 || chunknum >= map->numchunks)
+ return bb_err_NotFound;
+
+ chu = &(map->chunks[chunknum]);
+
+ if (chu->ptr) {
+ free(chu->ptr);
+ chu->ptr = NULL;
+ }
+
+ return bb_err_None;
+}
+
+bb_err_t bb_count_resources(bb_map_t *map, uint32 usage,
+ int *num, int *min, int *max)
+{
+ int ix;
+ int count, minval, maxval, val;
+
+ count = 0;
+ minval = 0;
+ maxval = 0;
+
+ for (ix=0; ix<map->numresources; ix++) {
+ if (map->resources[ix].usage == usage) {
+ val = map->resources[ix].resnum;
+ if (count == 0) {
+ count++;
+ minval = val;
+ maxval = val;
+ }
+ else {
+ count++;
+ if (val < minval)
+ minval = val;
+ if (val > maxval)
+ maxval = val;
+ }
+ }
+ }
+
+ if (num)
+ *num = count;
+ if (min)
+ *min = minval;
+ if (max)
+ *max = maxval;
+
+ return bb_err_None;
+}
+
+uint16 bb_get_release_num(bb_map_t *map)
+{
+ return map->releasenum;
+}
+
+bb_zheader_t *bb_get_zheader(bb_map_t *map)
+{
+ return map->zheader;
+}
+
+bb_resolution_t *bb_get_resolution(bb_map_t *map)
+{
+ return map->resolution;
+}
+
+bb_err_t bb_get_palette(bb_map_t *map, bb_palette_t **res)
+{
+ int ix;
+ bb_err_t err;
+
+ if (res)
+ *res = NULL;
+
+ if (map->palettechunk < 0) {
+ return bb_err_None;
+ }
+
+ if (!map->palette) {
+ bb_result_t chunkres;
+ bb_palette_t *pal;
+ unsigned char *ptr;
+
+ pal = (bb_palette_t *)malloc(sizeof(bb_palette_t));
+ if (!pal)
+ return bb_err_Alloc;
+
+ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres,
+ map->palettechunk);
+ if (err)
+ return err;
+
+ ptr = chunkres.data.ptr;
+
+ if (chunkres.length == 1) {
+ int val = ptr[0];
+ if (val != 16 && val != 32)
+ return bb_err_Format;
+ pal->isdirect = TRUE;
+ pal->data.depth = val;
+ }
+ else {
+ int size = chunkres.length / 3;
+ bb_color_t *colors = (bb_color_t *)malloc(size * sizeof(bb_color_t));
+ if (!colors)
+ return bb_err_Alloc;
+ if (size < 1 || size > 256)
+ return bb_err_Format;
+ for (ix=0; ix<size; ix++) {
+ colors[ix].red = ptr[ix*3+0];
+ colors[ix].green = ptr[ix*3+1];
+ colors[ix].blue = ptr[ix*3+2];
+ }
+ pal->isdirect = FALSE;
+ pal->data.table.numcolors = size;
+ pal->data.table.colors = colors;
+ }
+
+ bb_unload_chunk(map, map->palettechunk);
+ map->palette = pal;
+ }
+
+ if (res)
+ *res = map->palette;
+ return bb_err_None;
+}
+
+bb_err_t bb_load_resource_pict(bb_map_t *map, int method, bb_result_t *res,
+ int resnum, bb_aux_pict_t **auxdata)
+{
+ bb_err_t err;
+
+ if (auxdata)
+ *auxdata = NULL;
+
+ err = bb_load_resource(map, method, res, bb_ID_Pict, resnum);
+ if (err)
+ return err;
+
+ if (auxdata) {
+ bb_chunkdesc_t *chu = &(map->chunks[res->chunknum]);
+ if (chu->auxdatnum >= 0 && map->auxpict) {
+ *auxdata = &(map->auxpict[chu->auxdatnum]);
+ }
+ }
+
+ return bb_err_None;
+}
+
+bb_err_t bb_load_resource_snd(bb_map_t *map, int method, bb_result_t *res,
+ int resnum, bb_aux_sound_t **auxdata)
+{
+ bb_err_t err;
+
+ if (auxdata)
+ *auxdata = NULL;
+
+/* err = bb_load_resource(map, method, res, bb_ID_Pict, resnum); */
+ err = bb_load_resource(map, method, res, bb_ID_Snd, resnum);
+ if (err)
+ return err;
+
+ if (auxdata) {
+ bb_chunkdesc_t *chu = &(map->chunks[res->chunknum]);
+ if (chu->auxdatnum >= 0 && map->auxsound) {
+ *auxdata = &(map->auxsound[chu->auxdatnum]);
+ }
+ }
+
+ return bb_err_None;
+}
+
--- /dev/null
+#ifndef BLORBLOW_H
+#define BLORBLOW_H
+
+/* blorblow.h: Low-level header file for Blorb library, version 1.0.2.
+ Designed by Andrew Plotkin <erkyrath@eblong.com>
+ http://www.eblong.com/zarf/blorb/index.html
+
+ This header is generally of interest only to the Blorb library code
+ itself (blorblib.c); it defines things internal to the library.
+ An interpreter shouldn't have to include this file. The only time you
+ might need to include this is if you're writing a Blorb file analysis
+ tool (such as blorbscan), or a transformation tool, or some such thing.
+*/
+
+/* More four-byte constants. */
+
+#define bb_ID_FORM (bb_make_id('F', 'O', 'R', 'M'))
+#define bb_ID_IFRS (bb_make_id('I', 'F', 'R', 'S'))
+#define bb_ID_RIdx (bb_make_id('R', 'I', 'd', 'x'))
+#define bb_ID_IFhd (bb_make_id('I', 'F', 'h', 'd'))
+#define bb_ID_Reso (bb_make_id('R', 'e', 's', 'o'))
+#define bb_ID_Loop (bb_make_id('L', 'o', 'o', 'p'))
+#define bb_ID_RelN (bb_make_id('R', 'e', 'l', 'N'))
+#define bb_ID_Plte (bb_make_id('P', 'l', 't', 'e'))
+
+/* bb_chunkdesc_t: Describes one chunk of the Blorb file. */
+typedef struct bb_chunkdesc_struct {
+ uint32 type;
+ uint32 len;
+ uint32 startpos; /* start of chunk header */
+ uint32 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; */
+
+} bb_chunkdesc_t;
+
+/* bb_resdesc_t: Describes one resource in the Blorb file. */
+typedef struct bb_resdesc_struct {
+ uint32 usage;
+ int resnum;
+ int chunknum;
+} bb_resdesc_t;
+
+/* bb_map_t: Holds the complete description of an open Blorb file. */
+struct bb_map_struct {
+ uint32 inited; /* holds bb_Inited_Magic if the map structure is valid */
+ FILE *file;
+
+ int numchunks;
+ bb_chunkdesc_t *chunks; /* list of chunk descriptors */
+
+ int numresources;
+ bb_resdesc_t *resources; /* list of resource descriptors */
+ bb_resdesc_t **ressorted; /* list of pointers to descriptors
+ in map->resources -- sorted by usage and resource number. */
+
+ bb_zheader_t *zheader;
+ int releasenum;
+ bb_resolution_t *resolution;
+ int palettechunk; /* chunk number of palette, or -1 if there is none. */
+ bb_palette_t *palette;
+ bb_aux_sound_t *auxsound; /* extra information about sounds */
+ bb_aux_pict_t *auxpict; /* extra information about pictures */
+};
+
+#define bb_Inited_Magic (0xB7012BED)
+
+extern char *bb_id_to_string(uint32 id);
+
+#endif /* BLORBLOW_H */
--- /dev/null
+// sf_aiffwav.c
+//
+// Functions to read an AIFF file and convert (in memory) to WAV format,
+// as libmikmod only reads WAV samples
+//
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include "sf_frotz.h"
+
+#ifndef HUGE_INT32
+#define HUGE_INT32 0x7fffffff
+#endif /* HUGE_VAL */
+
+#ifndef Uint32
+#define Uint32 unsigned int
+#endif
+
+/****************************************************************
+ * Extended precision IEEE floating-point conversion routine.
+ ****************************************************************/
+
+static int IeeeExtendedToLong( unsigned char *bytes){
+ int f = 0, expon; Uint32 hiMant, loMant;
+
+ expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
+ hiMant = ((Uint32) (bytes[2] & 0xFF) << 24)
+ | ((Uint32) (bytes[3] & 0xFF) << 16)
+ | ((Uint32) (bytes[4] & 0xFF) << 8)
+ | ((Uint32) (bytes[5] & 0xFF));
+ loMant = ((Uint32) (bytes[6] & 0xFF) << 24)
+ | ((Uint32) (bytes[7] & 0xFF) << 16)
+ | ((Uint32) (bytes[8] & 0xFF) << 8)
+ | ((Uint32) (bytes[9] & 0xFF));
+
+ if (expon == 0 && hiMant == 0 && loMant == 0) f = 0;
+ else if (expon == 0x7FFF) f = HUGE_INT32;
+ else {
+ expon -= 16382;
+ expon = 32-expon;
+ if (expon < 0) f = HUGE_INT32;
+ else f = hiMant >> expon;
+ }
+
+ if (bytes[0] & 0x80)
+ return -f;
+ else
+ return f;
+ }
+
+static void writeRIFFheader( unsigned char *f, int freq, int numsamples, int numchannels,
+ int bytespersample);
+
+
+// returns 1 if resampling, 0 if not needed, negative if error
+static int initCONV( CONV *conv, double ratio, int ncha, int bytespersam, int maxinp);
+static void finishCONV( CONV *conv);
+
+#define BLOCKSIZE 1024
+
+// hack to leave resampling to mixer in simpler cases
+#define HACKING 0
+static int hack( double ratio)
+ {
+ int m; double diff;
+ if (HACKING == 0) return 0;
+ if (ratio > 0.9)
+ {
+ m = ratio + 0.499; // nint
+ diff = fabs(ratio - (double)m)/ratio;
+ if (diff < 0.01) return m;
+ }
+ else
+ {
+ m = 1.0/ratio + 0.499;
+ diff = ratio*fabs(1.0/ratio - (double)m);
+ if (diff < 0.01) return (-m);
+ }
+ return 0;
+ }
+
+#define MAXCHAN 8
+// size of WAV header
+#define HEADERSIZE 44
+
+#define NPAD 30
+
+#define glong( b) (((int)((b)[0]) << 24) + ((int)((b)[1]) << 16) +\
+ ((int)((b)[2]) << 8) + (int)((b)[3]))
+
+#define gshort( b) (((int)((b)[0]) << 8) + (int)((b)[1]))
+
+int sf_aiffwav( FILE *f, int foffs, void ** wav, int *length){
+
+ unsigned char chk[18], fid[4];
+ int len, bps=0, offs, bksize, size;
+ int samples, frequency, channels, found = 0;
+ int bytespersample, reqsize;
+ int noutsam=0, noutreqsam, bytesneeded, outbytespersample=2, mhack;
+ double ratio, cratio;
+ CONV conv; int needCONV;
+
+ unsigned char *bout = NULL;
+
+ if (!f) return -20;
+ if (!wav) return -21;
+
+ fseek(f,foffs,0);
+
+ samples = 0; frequency = 0;
+ channels = 0;
+
+ if (fread(chk,1,4,f) < 4) return -1; // FORM
+ if (memcmp(chk,"FORM",4)) return -3;
+ if (fread(chk,1,4,f) < 4) return -1; // size
+ size = glong(chk);
+ if (size & 1) size++;
+ if (size < 20) return -99;
+ if (fread(chk,1,4,f) < 4) return -1; // AIFF
+ if (memcmp(chk,"AIFF",4)) return -3;
+ size -= 4;
+ while (size > 8){
+ if (fread(fid,1,4,f) < 4) return -1; // chunck id
+ if (fread(chk,1,4,f) < 4) return -1; // and len
+ size -= 8;
+ len = glong(chk);
+ if (len < 0) return -98;
+ if (len & 1) len++;
+ size -= len;
+ if (size < 0) return -97;
+ if (memcmp(fid,"COMM",4)==0){
+ if (len != 18) return -5;
+ if (fread(chk,1,18,f) < 18) return -1;
+ channels = gshort(chk);
+ if (channels < 1) return -9;
+ if (channels > MAXCHAN) return -9;
+ samples = glong(chk+2);
+ if (samples < 1) return -9;
+ frequency = IeeeExtendedToLong(chk+8);
+ if (frequency <= 0) return -54;
+ bps = gshort(chk+6);
+ if (bps < 1 || bps > 16) return -51;
+// data = malloc(samples*sizeof(short));
+// if (!data) return -55;
+ }
+ else if (memcmp(fid,"SSND",4)==0){
+ if (!channels) return -11;
+ if (fread(chk,1,4,f) < 4) return -1;
+ offs = glong(chk);
+ if (fread(chk,1,4,f) < 4) return -1;
+ bksize = glong(chk);
+ if (bksize) return -77;
+ if (offs) fseek(f,offs,SEEK_CUR);
+ found = 1;
+ break;
+ }
+ else fseek(f,len,SEEK_CUR);
+ }
+ if (!found) return -69;
+ if (!channels) return -66;
+
+ // her should check freq
+ ratio = (double)m_frequency / (double)frequency;
+ mhack = hack(ratio);
+ cratio = ratio;
+ if (mhack)
+ {
+ cratio = 1.0;
+ if (mhack > 0) frequency = m_frequency/mhack;
+ else frequency = m_frequency * (-mhack);
+ }
+
+ noutreqsam = cratio*samples + 0.49;
+ bytesneeded = HEADERSIZE + noutreqsam*channels*outbytespersample;
+ bout = malloc(bytesneeded);
+ if (!bout) return -55;
+
+ *wav = (void *)bout;
+ bout += HEADERSIZE;
+
+ noutsam = 0;
+
+ if ((needCONV = initCONV(&conv,cratio,channels,(bps+7)/8,BLOCKSIZE)) < 0)
+ {
+ free(bout);
+ *wav = NULL;
+ return -997;
+ }
+
+ while (samples)
+ {
+ int nrd = BLOCKSIZE, nwr;
+ if (nrd > samples) nrd = samples;
+ nwr = conv.doCONV( &conv, f, bout, nrd, nrd == samples);
+ samples -= nrd;
+ noutsam += nwr;
+ bout += nwr*channels*outbytespersample;
+ }
+
+ conv.finishCONV(&conv);
+
+//printf("AIFF %d, req %d\n",frequency,m_frequency);
+ // write RIFF header
+ if (needCONV) frequency = m_frequency;
+ writeRIFFheader( *wav, frequency, noutsam, channels, outbytespersample);
+
+ *length = HEADERSIZE + noutsam*channels*outbytespersample;
+
+/*{
+FILE *f;
+f = fopen("_TMP","w"); fwrite(*wav,1,*length,f); fclose(f);
+}*/
+ return 0;
+ }
+
+// returns num of output samples
+static int NOCONV( CONV *conv, FILE *f, void *dest, int nin, int eod)
+ {
+ int nbrd, i, j, v=0;
+ short *sdata = (short *)dest;
+ unsigned char c1, c2; char c;
+
+ nbrd = conv->bytespersam;;
+ switch (conv->bytespersam)
+ {
+ case 1:
+ for (i=0;i<nin*conv->channels;i++)
+ {
+ c = fgetc(f);
+ *sdata++ = ((short)c) << 8;
+ }
+ break;
+ case 2:
+ for (i=0;i<nin*conv->channels;i++)
+ {
+ c1 = fgetc(f);
+ c2 = fgetc(f);
+ *sdata++ = (short)(((unsigned short)c1) << 8 + (unsigned short)c2);
+ }
+ }
+ return nin;
+ }
+
+static void finishCONV( CONV *conv)
+ {
+ if (conv->inbuf) free(conv->inbuf);
+ if (conv->outbuf) free(conv->outbuf);
+ conv->inbuf = conv->outbuf = NULL;
+ }
+
+// needs a pre-initialized CONV
+int (*sfx_exinitconv)( CONV *conv) = NULL;
+
+static int initCONV( CONV *conv, double ratio, int ncha, int bytespersam, int maxinp)
+ {
+ int doconv;
+ conv->ratio = ratio;
+ conv->channels = ncha;
+ conv->bytespersam = bytespersam;
+ conv->maxin = maxinp;
+ conv->maxout = ratio*maxinp+5; // a little margin
+ doconv = ((ratio != 1.0) && (sfx_exinitconv != NULL));
+ conv->inbuf = conv->outbuf = NULL;
+ if (doconv == 0)
+ {
+ conv->doCONV = NOCONV;
+ conv->finishCONV = finishCONV;
+ }
+ else
+ {
+ doconv = sfx_exinitconv(conv);
+ }
+ return doconv;
+ }
+
+#define PCwrite4( f, v) \
+ *f++ = v & 0xff;\
+ *f++ = (v >> 8) & 0xff;\
+ *f++ = (v >> 16) & 0xff;\
+ *f++ = v >> 24;
+
+#define PCwrite2( f, v) \
+ *f++ = v & 0xff;\
+ *f++ = (v >> 8) & 0xff;
+
+static void writeRIFFheader( unsigned char *f, int freq, int numsamples, int numchannels,
+ int bytespersample){
+ // write header
+ // offset 0
+ memcpy(f,"RIFF",4); f += 4;
+ // offset 4
+ PCwrite4(f,(36+numsamples*numchannels*bytespersample));
+ // offset 8
+ memcpy(f,"WAVEfmt ",8); f += 8;
+ // offset 16
+ PCwrite4(f,16);
+ // offset 20
+ PCwrite2(f,1); // PCM
+ // offset 22
+ PCwrite2(f,numchannels); // nchannels
+ // offset 24
+ PCwrite4(f,freq);
+ // offset 28
+ PCwrite4(f,(freq*numchannels*bytespersample));
+ // offset 32
+ PCwrite2(f,(numchannels*bytespersample));
+ // offset 34
+ PCwrite2(f,(8*bytespersample));
+ // offset 36
+ memcpy(f,"data",4); f += 4;
+ // offset 40
+ PCwrite4(f,(numsamples*numchannels*bytespersample));
+ // offset 44
+ }
+
--- /dev/null
+static unsigned char myfont[] = {\r
+ 0x00,0x00,0x00,0x00,0x20,0x00,0x53,0x01,\r
+ 0x20,0x00,0x0e,0x02,0xdc,0x04,0x00,0x00,\r
+ 0xf1,0x04,0x00,0x00,0x06,0x05,0x00,0x00,\r
+ 0x1b,0x05,0x00,0x00,0x30,0x05,0x00,0x00,\r
+ 0x45,0x05,0x00,0x00,0x5a,0x05,0x00,0x00,\r
+ 0x6f,0x05,0x00,0x00,0x84,0x05,0x00,0x00,\r
+ 0x99,0x05,0x00,0x00,0xae,0x05,0x00,0x00,\r
+ 0xc3,0x05,0x00,0x00,0xd8,0x05,0x00,0x00,\r
+ 0xed,0x05,0x00,0x00,0x02,0x06,0x00,0x00,\r
+ 0x17,0x06,0x00,0x00,0x2c,0x06,0x00,0x00,\r
+ 0x41,0x06,0x00,0x00,0x56,0x06,0x00,0x00,\r
+ 0x6b,0x06,0x00,0x00,0x80,0x06,0x00,0x00,\r
+ 0x95,0x06,0x00,0x00,0xaa,0x06,0x00,0x00,\r
+ 0xbf,0x06,0x00,0x00,0xd4,0x06,0x00,0x00,\r
+ 0xe9,0x06,0x00,0x00,0xfe,0x06,0x00,0x00,\r
+ 0x13,0x07,0x00,0x00,0x28,0x07,0x00,0x00,\r
+ 0x3d,0x07,0x00,0x00,0x52,0x07,0x00,0x00,\r
+ 0x67,0x07,0x00,0x00,0x7c,0x07,0x00,0x00,\r
+ 0x91,0x07,0x00,0x00,0xa6,0x07,0x00,0x00,\r
+ 0xbb,0x07,0x00,0x00,0xd0,0x07,0x00,0x00,\r
+ 0xe5,0x07,0x00,0x00,0xfa,0x07,0x00,0x00,\r
+ 0x0f,0x08,0x00,0x00,0x24,0x08,0x00,0x00,\r
+ 0x39,0x08,0x00,0x00,0x4e,0x08,0x00,0x00,\r
+ 0x63,0x08,0x00,0x00,0x78,0x08,0x00,0x00,\r
+ 0x8d,0x08,0x00,0x00,0xa2,0x08,0x00,0x00,\r
+ 0xb7,0x08,0x00,0x00,0xcc,0x08,0x00,0x00,\r
+ 0xe1,0x08,0x00,0x00,0xf6,0x08,0x00,0x00,\r
+ 0x0b,0x09,0x00,0x00,0x20,0x09,0x00,0x00,\r
+ 0x35,0x09,0x00,0x00,0x4a,0x09,0x00,0x00,\r
+ 0x5f,0x09,0x00,0x00,0x74,0x09,0x00,0x00,\r
+ 0x89,0x09,0x00,0x00,0x9e,0x09,0x00,0x00,\r
+ 0xb3,0x09,0x00,0x00,0xc8,0x09,0x00,0x00,\r
+ 0xdd,0x09,0x00,0x00,0xf2,0x09,0x00,0x00,\r
+ 0x07,0x0a,0x00,0x00,0x1c,0x0a,0x00,0x00,\r
+ 0x31,0x0a,0x00,0x00,0x46,0x0a,0x00,0x00,\r
+ 0x5b,0x0a,0x00,0x00,0x70,0x0a,0x00,0x00,\r
+ 0x85,0x0a,0x00,0x00,0x9a,0x0a,0x00,0x00,\r
+ 0xaf,0x0a,0x00,0x00,0xc4,0x0a,0x00,0x00,\r
+ 0xd9,0x0a,0x00,0x00,0xee,0x0a,0x00,0x00,\r
+ 0x03,0x0b,0x00,0x00,0x18,0x0b,0x00,0x00,\r
+ 0x2d,0x0b,0x00,0x00,0x42,0x0b,0x00,0x00,\r
+ 0x57,0x0b,0x00,0x00,0x6c,0x0b,0x00,0x00,\r
+ 0x81,0x0b,0x00,0x00,0x96,0x0b,0x00,0x00,\r
+ 0xab,0x0b,0x00,0x00,0xc0,0x0b,0x00,0x00,\r
+ 0xd5,0x0b,0x00,0x00,0xea,0x0b,0x00,0x00,\r
+ 0xff,0x0b,0x00,0x00,0x14,0x0c,0x00,0x00,\r
+ 0x29,0x0c,0x00,0x00,0x3e,0x0c,0x00,0x00,\r
+ 0x53,0x0c,0x00,0x00,0x68,0x0c,0x00,0x00,\r
+ 0x7d,0x0c,0x00,0x00,0x92,0x0c,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0xa7,0x0c,0x00,0x00,\r
+ 0xbc,0x0c,0x00,0x00,0xd1,0x0c,0x00,0x00,\r
+ 0xe6,0x0c,0x00,0x00,0xfb,0x0c,0x00,0x00,\r
+ 0x10,0x0d,0x00,0x00,0x25,0x0d,0x00,0x00,\r
+ 0x3a,0x0d,0x00,0x00,0x4f,0x0d,0x00,0x00,\r
+ 0x64,0x0d,0x00,0x00,0x79,0x0d,0x00,0x00,\r
+ 0x8e,0x0d,0x00,0x00,0xa3,0x0d,0x00,0x00,\r
+ 0xb8,0x0d,0x00,0x00,0xcd,0x0d,0x00,0x00,\r
+ 0xe2,0x0d,0x00,0x00,0xf7,0x0d,0x00,0x00,\r
+ 0x0c,0x0e,0x00,0x00,0x21,0x0e,0x00,0x00,\r
+ 0x36,0x0e,0x00,0x00,0x4b,0x0e,0x00,0x00,\r
+ 0x60,0x0e,0x00,0x00,0x75,0x0e,0x00,0x00,\r
+ 0x8a,0x0e,0x00,0x00,0x9f,0x0e,0x00,0x00,\r
+ 0xb4,0x0e,0x00,0x00,0xc9,0x0e,0x00,0x00,\r
+ 0xde,0x0e,0x00,0x00,0xf3,0x0e,0x00,0x00,\r
+ 0x08,0x0f,0x00,0x00,0x1d,0x0f,0x00,0x00,\r
+ 0x32,0x0f,0x00,0x00,0x47,0x0f,0x00,0x00,\r
+ 0x5c,0x0f,0x00,0x00,0x71,0x0f,0x00,0x00,\r
+ 0x86,0x0f,0x00,0x00,0x9b,0x0f,0x00,0x00,\r
+ 0xb0,0x0f,0x00,0x00,0xc5,0x0f,0x00,0x00,\r
+ 0xda,0x0f,0x00,0x00,0xef,0x0f,0x00,0x00,\r
+ 0x04,0x10,0x00,0x00,0x19,0x10,0x00,0x00,\r
+ 0x2e,0x10,0x00,0x00,0x43,0x10,0x00,0x00,\r
+ 0x58,0x10,0x00,0x00,0x6d,0x10,0x00,0x00,\r
+ 0x82,0x10,0x00,0x00,0x97,0x10,0x00,0x00,\r
+ 0xac,0x10,0x00,0x00,0xc1,0x10,0x00,0x00,\r
+ 0xd6,0x10,0x00,0x00,0xeb,0x10,0x00,0x00,\r
+ 0x00,0x11,0x00,0x00,0x15,0x11,0x00,0x00,\r
+ 0x2a,0x11,0x00,0x00,0x3f,0x11,0x00,0x00,\r
+ 0x54,0x11,0x00,0x00,0x69,0x11,0x00,0x00,\r
+ 0x7e,0x11,0x00,0x00,0x93,0x11,0x00,0x00,\r
+ 0xa8,0x11,0x00,0x00,0xbd,0x11,0x00,0x00,\r
+ 0xd2,0x11,0x00,0x00,0xe7,0x11,0x00,0x00,\r
+ 0xfc,0x11,0x00,0x00,0x11,0x12,0x00,0x00,\r
+ 0x26,0x12,0x00,0x00,0x3b,0x12,0x00,0x00,\r
+ 0x50,0x12,0x00,0x00,0x65,0x12,0x00,0x00,\r
+ 0x7a,0x12,0x00,0x00,0x8f,0x12,0x00,0x00,\r
+ 0xa4,0x12,0x00,0x00,0xb9,0x12,0x00,0x00,\r
+ 0xce,0x12,0x00,0x00,0xe3,0x12,0x00,0x00,\r
+ 0xf8,0x12,0x00,0x00,0x0d,0x13,0x00,0x00,\r
+ 0x22,0x13,0x00,0x00,0x37,0x13,0x00,0x00,\r
+ 0x4c,0x13,0x00,0x00,0x61,0x13,0x00,0x00,\r
+ 0x76,0x13,0x00,0x00,0x8b,0x13,0x00,0x00,\r
+ 0xa0,0x13,0x00,0x00,0xb5,0x13,0x00,0x00,\r
+ 0xca,0x13,0x00,0x00,0xdf,0x13,0x00,0x00,\r
+ 0xf4,0x13,0x00,0x00,0x09,0x14,0x00,0x00,\r
+ 0x1e,0x14,0x00,0x00,0x33,0x14,0x00,0x00,\r
+ 0x48,0x14,0x00,0x00,0x5d,0x14,0x00,0x00,\r
+ 0x72,0x14,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x87,0x14,0x00,0x00,\r
+ 0x9c,0x14,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,\r
+ 0x08,0x00,0x08,0x08,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x22,0x22,0x22,\r
+ 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x12,0x12,0x12,0x7e,\r
+ 0x24,0x24,0x7e,0x48,0x48,0x48,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x08,0x3e,0x49,0x48,0x38,0x0e,0x09,\r
+ 0x49,0x3e,0x08,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x31,0x4a,\r
+ 0x4a,0x34,0x08,0x08,0x16,0x29,0x29,0x46,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x1c,0x22,0x22,0x22,0x1c,\r
+ 0x39,0x45,0x42,0x46,0x39,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x08,0x08,\r
+ 0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x04,0x08,0x08,0x10,\r
+ 0x10,0x10,0x10,0x10,0x10,0x08,0x08,0x04,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x20,0x10,0x10,0x08,0x08,0x08,0x08,\r
+ 0x08,0x08,0x10,0x10,0x20,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x08,0x49,0x2a,0x1c,0x2a,0x49,0x08,\r
+ 0x00,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,\r
+ 0x08,0x7f,0x08,0x08,0x08,0x00,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x18,0x08,0x08,0x10,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x02,0x02,0x04,0x08,0x08,0x10,0x10,0x20,\r
+ 0x40,0x40,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x18,0x24,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x08,0x18,0x28,0x08,0x08,0x08,\r
+ 0x08,0x08,0x08,0x3e,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x3c,\r
+ 0x42,0x42,0x02,0x0c,0x10,0x20,0x40,0x40,\r
+ 0x7e,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x02,\r
+ 0x1c,0x02,0x02,0x42,0x42,0x3c,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x04,0x0c,0x14,0x24,0x44,0x44,0x7e,\r
+ 0x04,0x04,0x04,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x7e,0x40,\r
+ 0x40,0x40,0x7c,0x02,0x02,0x02,0x42,0x3c,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x1c,0x20,0x40,0x40,0x7c,\r
+ 0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x7e,0x02,0x02,0x04,0x04,0x04,0x08,0x08,\r
+ 0x08,0x08,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x3c,0x42,0x42,\r
+ 0x42,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x3c,0x42,0x42,0x42,0x3e,0x02,\r
+ 0x02,0x02,0x04,0x38,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,\r
+ 0x00,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,\r
+ 0x00,0x00,0x00,0x18,0x08,0x08,0x10,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x10,\r
+ 0x08,0x04,0x02,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x00,0x40,0x20,0x10,0x08,\r
+ 0x04,0x08,0x10,0x20,0x40,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x3c,0x42,0x42,0x02,0x04,0x08,0x08,0x00,\r
+ 0x08,0x08,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x1c,0x22,0x4a,\r
+ 0x56,0x52,0x52,0x52,0x4e,0x20,0x1e,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x18,0x24,0x24,0x42,0x42,0x7e,\r
+ 0x42,0x42,0x42,0x42,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x7c,\r
+ 0x42,0x42,0x42,0x7c,0x42,0x42,0x42,0x42,\r
+ 0x7c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x40,\r
+ 0x40,0x40,0x40,0x42,0x42,0x3c,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x78,0x44,0x42,0x42,0x42,0x42,0x42,\r
+ 0x42,0x44,0x78,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x7e,0x40,\r
+ 0x40,0x40,0x7c,0x40,0x40,0x40,0x40,0x7e,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x7e,0x40,0x40,0x40,0x7c,\r
+ 0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x3c,0x42,0x42,0x40,0x40,0x4e,0x42,0x42,\r
+ 0x46,0x3a,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x42,0x42,0x42,\r
+ 0x42,0x7e,0x42,0x42,0x42,0x42,0x42,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x3e,0x08,0x08,0x08,0x08,0x08,\r
+ 0x08,0x08,0x08,0x3e,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x1f,\r
+ 0x04,0x04,0x04,0x04,0x04,0x04,0x44,0x44,\r
+ 0x38,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x42,0x44,0x48,0x50,\r
+ 0x60,0x60,0x50,0x48,0x44,0x42,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,\r
+ 0x40,0x40,0x7e,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x42,0x42,\r
+ 0x66,0x66,0x5a,0x5a,0x42,0x42,0x42,0x42,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x42,0x62,0x62,0x52,0x52,\r
+ 0x4a,0x4a,0x46,0x46,0x42,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x3c,0x42,0x42,0x42,0x42,0x42,0x42,0x42,\r
+ 0x42,0x3c,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x7c,0x42,0x42,\r
+ 0x42,0x7c,0x40,0x40,0x40,0x40,0x40,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42,\r
+ 0x42,0x5a,0x66,0x3c,0x03,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x7c,\r
+ 0x42,0x42,0x42,0x7c,0x48,0x44,0x44,0x42,\r
+ 0x42,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x40,\r
+ 0x30,0x0c,0x02,0x42,0x42,0x3c,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x7f,0x08,0x08,0x08,0x08,0x08,0x08,\r
+ 0x08,0x08,0x08,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x42,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3c,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x41,0x41,0x41,0x22,0x22,\r
+ 0x22,0x14,0x14,0x08,0x08,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x42,0x42,0x42,0x42,0x5a,0x5a,0x66,0x66,\r
+ 0x42,0x42,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x42,0x42,0x24,\r
+ 0x24,0x18,0x18,0x24,0x24,0x42,0x42,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x41,0x41,0x22,0x22,0x14,0x08,\r
+ 0x08,0x08,0x08,0x08,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x7e,\r
+ 0x02,0x02,0x04,0x08,0x10,0x20,0x40,0x40,\r
+ 0x7e,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x0e,0x08,0x08,0x08,0x08,\r
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x0e,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x40,0x40,0x20,0x10,0x10,0x08,0x08,\r
+ 0x04,0x02,0x02,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x70,0x10,0x10,\r
+ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,\r
+ 0x70,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x18,0x24,0x42,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x7f,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x18,0x10,0x10,0x08,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x3c,0x42,0x02,0x3e,\r
+ 0x42,0x42,0x46,0x3a,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x40,0x40,\r
+ 0x40,0x5c,0x62,0x42,0x42,0x42,0x42,0x62,\r
+ 0x5c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x42,\r
+ 0x40,0x40,0x40,0x40,0x42,0x3c,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x02,0x02,0x02,0x3a,0x46,0x42,0x42,0x42,\r
+ 0x42,0x46,0x3a,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x3c,0x42,0x42,0x7e,0x40,0x40,0x42,0x3c,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x0c,0x10,0x10,0x10,0x7c,0x10,\r
+ 0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x00,0x02,0x3a,0x44,0x44,0x44,0x38,0x20,\r
+ 0x3c,0x42,0x42,0x3c,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x40,0x40,0x40,0x5c,\r
+ 0x62,0x42,0x42,0x42,0x42,0x42,0x42,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x08,0x08,0x00,0x18,0x08,0x08,0x08,\r
+ 0x08,0x08,0x08,0x3e,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x04,0x04,\r
+ 0x00,0x0c,0x04,0x04,0x04,0x04,0x04,0x04,\r
+ 0x04,0x48,0x30,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x40,0x40,0x44,0x48,\r
+ 0x50,0x60,0x50,0x48,0x44,0x42,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x18,0x08,0x08,0x08,0x08,0x08,0x08,\r
+ 0x08,0x08,0x3e,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x76,0x49,0x49,0x49,0x49,0x49,0x49,0x49,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x5c,0x62,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42,\r
+ 0x42,0x3c,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x5c,\r
+ 0x62,0x42,0x42,0x42,0x42,0x62,0x5c,0x40,\r
+ 0x40,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x3a,0x46,0x42,0x42,\r
+ 0x42,0x42,0x46,0x3a,0x02,0x02,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x5c,0x62,0x42,0x40,0x40,0x40,0x40,\r
+ 0x40,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x42,\r
+ 0x40,0x30,0x0c,0x02,0x42,0x3c,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x10,0x10,0x7c,0x10,0x10,0x10,0x10,\r
+ 0x10,0x10,0x0c,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x42,0x42,0x42,0x42,0x42,0x42,0x46,0x3a,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x42,\r
+ 0x24,0x24,0x24,0x18,0x18,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x41,0x49,0x49,0x49,0x49,0x49,\r
+ 0x49,0x36,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x42,\r
+ 0x42,0x24,0x18,0x18,0x24,0x42,0x42,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,\r
+ 0x42,0x26,0x1a,0x02,0x02,0x3c,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x7e,0x02,0x04,0x08,0x10,0x20,0x40,\r
+ 0x7e,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x0c,0x10,0x10,0x08,0x08,\r
+ 0x10,0x10,0x08,0x08,0x10,0x10,0x0c,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x08,\r
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,\r
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x30,0x08,0x08,\r
+ 0x10,0x10,0x08,0x08,0x10,0x10,0x08,0x08,\r
+ 0x30,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x31,0x49,0x46,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x08,0x08,0x00,\r
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x00,0x00,0x08,0x08,0x3e,0x49,0x48,0x48,\r
+ 0x49,0x3e,0x08,0x08,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x0e,\r
+ 0x10,0x10,0x10,0x7c,0x10,0x10,0x10,0x3e,\r
+ 0x61,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x00,0x42,0x24,0x3c,\r
+ 0x24,0x24,0x3c,0x24,0x42,0x00,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x41,0x22,0x14,0x08,0x7f,0x08,0x7f,\r
+ 0x08,0x08,0x08,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x08,0x08,\r
+ 0x08,0x08,0x00,0x00,0x08,0x08,0x08,0x08,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x3c,0x42,0x40,0x3c,0x42,\r
+ 0x42,0x3c,0x02,0x42,0x3c,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x24,0x24,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x3c,0x42,0x99,\r
+ 0xa5,0xa1,0xa1,0xa5,0x99,0x42,0x3c,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x1c,0x02,0x1e,0x22,0x1e,0x00,0x3e,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,\r
+ 0x12,0x12,0x24,0x24,0x48,0x24,0x24,0x12,\r
+ 0x12,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x7e,0x02,0x02,0x02,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x3c,0x42,\r
+ 0xb9,0xa5,0xa5,0xb9,0xa9,0xa5,0x42,0x3c,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x10,0x28,0x28,0x10,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x08,0x08,0x08,\r
+ 0x7f,0x08,0x08,0x08,0x00,0x7f,0x00,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x1c,0x22,0x02,0x1c,0x20,0x20,0x3e,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x1c,0x22,0x02,\r
+ 0x1c,0x02,0x22,0x1c,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x0c,0x30,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x22,0x22,0x22,\r
+ 0x22,0x36,0x2a,0x20,0x20,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x3e,0x7a,0x7a,\r
+ 0x7a,0x7a,0x3a,0x0a,0x0a,0x0a,0x0a,0x0a,\r
+ 0x0e,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,\r
+ 0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x00,0x08,0x30,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x08,0x18,0x28,0x08,0x08,\r
+ 0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x1c,0x22,0x22,0x22,0x1c,0x00,0x3e,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,\r
+ 0x48,0x48,0x24,0x24,0x12,0x24,0x24,0x48,\r
+ 0x48,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x00,0x00,0x22,0x62,0x24,0x28,\r
+ 0x28,0x12,0x16,0x2a,0x4e,0x42,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00,\r
+ 0x00,0x22,0x62,0x24,0x28,0x28,0x14,0x1a,\r
+ 0x22,0x44,0x4e,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x62,0x12,\r
+ 0x24,0x18,0x68,0x12,0x16,0x2a,0x4e,0x42,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x08,0x08,0x00,0x08,0x08,\r
+ 0x30,0x42,0x42,0x42,0x3c,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x30,0x0c,0x00,0x00,\r
+ 0x18,0x24,0x24,0x42,0x42,0x7e,0x42,0x42,\r
+ 0x42,0x42,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x0c,0x30,0x00,0x00,0x18,0x24,0x24,\r
+ 0x42,0x42,0x7e,0x42,0x42,0x42,0x42,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x18,0x24,\r
+ 0x00,0x00,0x18,0x24,0x24,0x42,0x42,0x7e,\r
+ 0x42,0x42,0x42,0x42,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x32,0x4c,0x00,0x00,0x18,\r
+ 0x24,0x24,0x42,0x42,0x7e,0x42,0x42,0x42,\r
+ 0x42,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x24,0x24,0x00,0x00,0x18,0x24,0x24,0x42,\r
+ 0x42,0x7e,0x42,0x42,0x42,0x42,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x18,0x24,0x18,\r
+ 0x00,0x18,0x24,0x24,0x42,0x42,0x7e,0x42,\r
+ 0x42,0x42,0x42,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x1f,0x28,\r
+ 0x48,0x48,0x7f,0x48,0x48,0x48,0x48,0x4f,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x3c,0x42,0x42,0x40,0x40,\r
+ 0x40,0x40,0x42,0x42,0x3c,0x08,0x30,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x30,0x0c,0x00,0x00,\r
+ 0x7e,0x40,0x40,0x40,0x7c,0x40,0x40,0x40,\r
+ 0x40,0x7e,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x0c,0x30,0x00,0x00,0x7e,0x40,0x40,\r
+ 0x40,0x7c,0x40,0x40,0x40,0x40,0x7e,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x18,0x24,\r
+ 0x00,0x00,0x7e,0x40,0x40,0x40,0x7c,0x40,\r
+ 0x40,0x40,0x40,0x7e,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x24,0x24,0x00,0x00,0x7e,\r
+ 0x40,0x40,0x40,0x7c,0x40,0x40,0x40,0x40,\r
+ 0x7e,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x18,0x06,0x00,0x00,0x3e,0x08,0x08,0x08,\r
+ 0x08,0x08,0x08,0x08,0x08,0x3e,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x0c,0x30,0x00,\r
+ 0x00,0x3e,0x08,0x08,0x08,0x08,0x08,0x08,\r
+ 0x08,0x08,0x3e,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x18,0x24,0x00,0x00,0x3e,0x08,\r
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x3e,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x24,\r
+ 0x24,0x00,0x00,0x3e,0x08,0x08,0x08,0x08,\r
+ 0x08,0x08,0x08,0x08,0x3e,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x78,0x44,0x42,0x42,0xf2,0x42,0x42,0x42,\r
+ 0x44,0x78,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x32,0x4c,0x00,0x00,0x42,0x62,0x62,\r
+ 0x52,0x52,0x4a,0x4a,0x46,0x46,0x42,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x30,0x0c,\r
+ 0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42,\r
+ 0x42,0x42,0x42,0x3c,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x0c,0x30,0x00,0x00,0x3c,\r
+ 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,\r
+ 0x3c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x18,0x24,0x00,0x00,0x3c,0x42,0x42,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x32,0x4c,0x00,\r
+ 0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x42,\r
+ 0x42,0x42,0x3c,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x24,0x24,0x00,0x00,0x3c,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3c,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x24,\r
+ 0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x02,\r
+ 0x3a,0x44,0x46,0x4a,0x4a,0x52,0x52,0x62,\r
+ 0x22,0x5c,0x40,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x30,0x0c,0x00,0x00,0x42,0x42,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x0c,0x30,\r
+ 0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x42,\r
+ 0x42,0x42,0x42,0x3c,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x18,0x24,0x00,0x00,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,\r
+ 0x3c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x24,0x24,0x00,0x00,0x42,0x42,0x42,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x0c,0x30,0x00,\r
+ 0x00,0x41,0x41,0x22,0x22,0x14,0x08,0x08,\r
+ 0x08,0x08,0x08,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x40,0x40,0x78,\r
+ 0x44,0x42,0x42,0x44,0x78,0x40,0x40,0x40,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x7c,\r
+ 0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x30,0x0c,\r
+ 0x00,0x00,0x3c,0x42,0x02,0x3e,0x42,0x42,\r
+ 0x46,0x3a,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x0c,0x30,0x00,0x00,0x3c,\r
+ 0x42,0x02,0x3e,0x42,0x42,0x46,0x3a,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x18,0x24,0x00,0x00,0x3c,0x42,0x02,0x3e,\r
+ 0x42,0x42,0x46,0x3a,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x32,0x4c,0x00,\r
+ 0x00,0x3c,0x42,0x02,0x3e,0x42,0x42,0x46,\r
+ 0x3a,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x24,0x24,0x00,0x00,0x3c,0x42,\r
+ 0x02,0x3e,0x42,0x42,0x46,0x3a,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x18,0x24,\r
+ 0x18,0x00,0x00,0x3c,0x42,0x02,0x3e,0x42,\r
+ 0x42,0x46,0x3a,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,\r
+ 0x3e,0x49,0x09,0x3f,0x48,0x48,0x49,0x3e,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x3c,0x42,0x40,\r
+ 0x40,0x40,0x40,0x42,0x3c,0x08,0x30,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x30,0x0c,\r
+ 0x00,0x00,0x3c,0x42,0x42,0x7e,0x40,0x40,\r
+ 0x42,0x3c,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x0c,0x30,0x00,0x00,0x3c,\r
+ 0x42,0x42,0x7e,0x40,0x40,0x42,0x3c,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x18,0x24,0x00,0x00,0x3c,0x42,0x42,0x7e,\r
+ 0x40,0x40,0x42,0x3c,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x24,0x24,0x00,\r
+ 0x00,0x3c,0x42,0x42,0x7e,0x40,0x40,0x42,\r
+ 0x3c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x30,0x0c,0x00,0x00,0x18,0x08,\r
+ 0x08,0x08,0x08,0x08,0x08,0x3e,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x0c,\r
+ 0x30,0x00,0x00,0x18,0x08,0x08,0x08,0x08,\r
+ 0x08,0x08,0x3e,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x18,0x24,0x00,0x00,\r
+ 0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x3e,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x24,0x24,0x00,0x00,0x18,0x08,0x08,\r
+ 0x08,0x08,0x08,0x08,0x3e,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x32,0x0c,\r
+ 0x14,0x22,0x02,0x3e,0x42,0x42,0x42,0x42,\r
+ 0x42,0x3c,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x32,0x4c,0x00,0x00,0x5c,\r
+ 0x62,0x42,0x42,0x42,0x42,0x42,0x42,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x30,0x0c,0x00,0x00,0x3c,0x42,0x42,0x42,\r
+ 0x42,0x42,0x42,0x3c,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x0c,0x30,0x00,\r
+ 0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x42,\r
+ 0x3c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x18,0x24,0x00,0x00,0x3c,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x32,\r
+ 0x4c,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,\r
+ 0x42,0x42,0x3c,0x00,0x00,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x24,0x24,0x00,0x00,\r
+ 0x3c,0x42,0x42,0x42,0x42,0x42,0x42,0x3c,\r
+ 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,\r
+ 0x7e,0x00,0x00,0x18,0x00,0x00,0x00,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x00,0x02,0x3c,0x46,0x4a,0x4a,0x52,0x52,\r
+ 0x62,0x3c,0x40,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x30,0x0c,0x00,0x00,0x42,\r
+ 0x42,0x42,0x42,0x42,0x42,0x46,0x3a,0x00,\r
+ 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00,\r
+ 0x0c,0x30,0x00,0x00,0x42,0x42,0x42,0x42,\r
+ 0x42,0x42,0x46,0x3a,0x00,0x00,0x08,0x08,\r
+ 0x10,0x00,0xfe,0x00,0x00,0x18,0x24,0x00,\r
+ 0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x46,\r
+ 0x3a,0x00,0x00,0x08,0x08,0x10,0x00,0xfe,\r
+ 0x00,0x00,0x24,0x24,0x00,0x00,0x42,0x42,\r
+ 0x42,0x42,0x42,0x42,0x46,0x3a,0x00,0x00,\r
+ 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x0c,\r
+ 0x30,0x00,0x00,0x42,0x42,0x42,0x42,0x42,\r
+ 0x26,0x1a,0x02,0x02,0x3c,0x08,0x08,0x10,\r
+ 0x00,0xfe,0x00,0x00,0x00,0x00,0x20,0x20,\r
+ 0x3c,0x22,0x22,0x22,0x24,0x28,0x30,0x20,\r
+ 0x20,0x20,0x08,0x08,0x10,0x00,0xfe,0x00,\r
+ 0x00,0x24,0x24,0x00,0x00,0x42,0x42,0x42,\r
+ 0x42,0x42,0x26,0x1a,0x02,0x02,0x3c,0x08,\r
+ 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00,\r
+ 0x6e,0x90,0x90,0x90,0x9c,0x90,0x90,0x90,\r
+ 0x90,0x6e,0x00,0x00,0x08,0x08,0x10,0x00,\r
+ 0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x6c,\r
+ 0x92,0x92,0x9e,0x90,0x90,0x92,0x6c,0x00,\r
+ 0x00};\r
+\r
+unsigned char * SF_defaultfont = &(myfont[0]);\r
+int SF_defaultfontsize = sizeof(myfont);\r
+\r
--- /dev/null
+#include "sf_frotz.h"
+
+static byte Zfont3[] = {
+/* 32*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* 33*/ 0x00,0x00,0x20,0x60,0xfe,0x60,0x20,0x00,
+/* 34*/ 0x00,0x00,0x08,0x0c,0xfe,0x0c,0x08,0x00,
+/* 35*/ 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,
+/* 36*/ 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,
+/* 37*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* 38*/ 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,
+/* 39*/ 0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,
+/* 40*/ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
+/* 41*/ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
+/* 42*/ 0x08,0x08,0x08,0xff,0x00,0x00,0x00,0x00,
+/* 43*/ 0x00,0x00,0x00,0x00,0xff,0x08,0x08,0x08,
+/* 44*/ 0x08,0x08,0x08,0x08,0x0f,0x08,0x08,0x08,
+/* 45*/ 0x10,0x10,0x10,0x10,0xf0,0x10,0x10,0x10,
+/* 46*/ 0x10,0x10,0x10,0x10,0x1f,0x00,0x00,0x00,
+/* 47*/ 0x00,0x00,0x00,0x1f,0x10,0x10,0x10,0x10,
+/* 48*/ 0x00,0x00,0x00,0xf8,0x08,0x08,0x08,0x08,
+/* 49*/ 0x08,0x08,0x08,0x08,0xf8,0x00,0x00,0x00,
+/* 50*/ 0x10,0x10,0x10,0x10,0x1f,0x20,0x40,0x80,
+/* 51*/ 0x80,0x40,0x20,0x1f,0x10,0x10,0x10,0x10,
+/* 52*/ 0x01,0x02,0x04,0xf8,0x08,0x08,0x08,0x08,
+/* 53*/ 0x08,0x08,0x08,0x08,0xf8,0x04,0x02,0x01,
+/* 54*/ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+/* 55*/ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,
+/* 56*/ 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,
+/* 57*/ 0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,
+/* 58*/ 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,
+/* 59*/ 0x08,0x08,0x08,0xff,0xff,0xff,0xff,0xff,
+/* 60*/ 0xff,0xff,0xff,0xff,0xff,0x08,0x08,0x08,
+/* 61*/ 0xf8,0xf8,0xf8,0xf8,0xff,0xf8,0xf8,0xf8,
+/* 62*/ 0x1f,0x1f,0x1f,0x1f,0xff,0x1f,0x1f,0x1f,
+/* 63*/ 0x1f,0x1f,0x1f,0x1f,0x1f,0x00,0x00,0x00,
+/* 64*/ 0x00,0x00,0x00,0x1f,0x1f,0x1f,0x1f,0x1f,
+/* 65*/ 0x00,0x00,0x00,0xf8,0xf8,0xf8,0xf8,0xf8,
+/* 66*/ 0xf8,0xf8,0xf8,0xf8,0xf8,0x00,0x00,0x00,
+/* 67*/ 0x1f,0x1f,0x1f,0x1f,0x1f,0x20,0x40,0x80,
+/* 68*/ 0x80,0x40,0x20,0x1f,0x1f,0x1f,0x1f,0x1f,
+/* 69*/ 0x01,0x02,0x04,0xf8,0xf8,0xf8,0xf8,0xf8,
+/* 70*/ 0xf8,0xf8,0xf8,0xf8,0xf8,0x04,0x02,0x01,
+/* 71*/ 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* 72*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
+/* 73*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+/* 74*/ 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* 75*/ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* 76*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,
+/* 77*/ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+/* 78*/ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
+/* 79*/ 0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,
+/* 80*/ 0x00,0xff,0x80,0x80,0x80,0x80,0xff,0x00,
+/* 81*/ 0x00,0xff,0xc0,0xc0,0xc0,0xc0,0xff,0x00,
+/* 82*/ 0x00,0xff,0xe0,0xe0,0xe0,0xe0,0xff,0x00,
+/* 83*/ 0x00,0xff,0xf0,0xf0,0xf0,0xf0,0xff,0x00,
+/* 84*/ 0x00,0xff,0xf8,0xf8,0xf8,0xf8,0xff,0x00,
+/* 85*/ 0x00,0xff,0xfc,0xfc,0xfc,0xfc,0xff,0x00,
+/* 86*/ 0x00,0xff,0xfe,0xfe,0xfe,0xfe,0xff,0x00,
+/* 87*/ 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
+/* 88*/ 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
+/* 89*/ 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
+/* 90*/ 0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81,
+/* 91*/ 0x08,0x08,0x08,0x08,0xff,0x08,0x08,0x08,
+/* 92*/ 0x18,0x3c,0xdb,0x18,0x18,0x18,0x18,0x00,
+/* 93*/ 0x18,0x18,0x18,0x18,0xdb,0x3c,0x18,0x00,
+/* 94*/ 0x18,0x3c,0xdb,0x18,0xdb,0x3c,0x18,0x00,
+/* 95*/ 0xff,0x81,0x81,0x81,0x81,0x81,0x81,0xff,
+/* 96*/ 0x3c,0x66,0x06,0x0c,0x18,0x00,0x18,0x00,
+/* 97*/ 0xc4,0xa8,0x90,0xc0,0xa0,0x90,0x80,0x00,
+/* 98*/ 0x60,0x50,0x48,0x70,0x48,0x50,0x60,0x00,
+/* 99*/ 0x10,0x18,0x14,0x92,0x50,0x30,0x10,0x00,
+/*100*/ 0x82,0xc6,0xaa,0x92,0xaa,0xc6,0x82,0x00,
+/*101*/ 0x82,0xc6,0xaa,0x92,0x82,0x82,0x82,0x00,
+/*102*/ 0x94,0xa8,0xd0,0xa0,0xc0,0x80,0x80,0x00,
+/*103*/ 0x82,0x44,0x28,0x10,0x28,0x44,0x82,0x00,
+/*104*/ 0xc2,0xa2,0xd2,0xaa,0x96,0x8a,0x86,0x00,
+/*105*/ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,
+/*106*/ 0x10,0x38,0x54,0x92,0x54,0x38,0x10,0x00,
+/*107*/ 0x10,0x10,0x10,0x38,0x54,0x92,0x92,0x00,
+/*108*/ 0x10,0x18,0x14,0x12,0x10,0x10,0x10,0x00,
+/*109*/ 0xc6,0xaa,0x92,0xaa,0xc6,0x82,0x82,0x00,
+/*110*/ 0x90,0x50,0x38,0x14,0x12,0x10,0x10,0x00,
+/*111*/ 0xc4,0xac,0xd4,0xa8,0x90,0x80,0x80,0x00,
+/*112*/ 0x80,0x80,0x80,0x90,0xa8,0xc4,0x82,0x00,
+/*113*/ 0x40,0x40,0x40,0x78,0x44,0x44,0x44,0x00,
+/*114*/ 0x60,0x50,0x48,0x50,0x60,0x50,0x48,0x00,
+/*115*/ 0x40,0x44,0x4c,0x54,0x64,0x44,0x04,0x00,
+/*116*/ 0x10,0x38,0x54,0x92,0x10,0x10,0x10,0x00,
+/*117*/ 0x60,0x50,0x48,0x44,0x44,0x44,0x44,0x00,
+/*118*/ 0x10,0xba,0x54,0x10,0x10,0x10,0x10,0x00,
+/*119*/ 0x60,0x50,0x48,0x50,0x60,0x40,0x40,0x00,
+/*120*/ 0x92,0x54,0x38,0x10,0x10,0x10,0x10,0x00,
+/*121*/ 0xe0,0xd0,0xa8,0x94,0x9a,0x96,0x92,0x00,
+/*122*/ 0x10,0x28,0x44,0x28,0x10,0x28,0x44,0x00,
+/*123*/ 0xe7,0xc3,0x24,0xe7,0xe7,0xe7,0xe7,0xff,
+/*124*/ 0xe7,0xe7,0xe7,0xe7,0x24,0xc3,0xe7,0xff,
+/*125*/ 0xe7,0xc3,0x24,0xe7,0x24,0xc3,0xe7,0xff,
+/*126*/ 0xc3,0x99,0xf9,0xf3,0xe7,0xff,0xe7,0xff};
+
+// glyph buffer
+static struct {
+ byte dx;
+ byte w, h;
+ char xof, yof;
+ byte bitmap[16];
+ } myglyph = {8,8,8,0,0};
+
+static void nodestroy(SFONT *s){}
+static int myheight(SFONT *s){ return 8;}
+static int myascent(SFONT *s){ return 6;}
+static int mydescent(SFONT *s){ return 2;}
+static int myminchar(SFONT *s){ return 32;}
+static int mymaxchar(SFONT *s){ return 126;}
+static int myhasglyph(SFONT *s, word c, int allowdef)
+ { if (c >= 32 && c <= 126) return 1; if (allowdef) return 1;}
+static SF_glyph * mygetglyph(SFONT *s, word c, int allowdef)
+ {
+ byte *src;
+ if (c < 32 || c > 126)
+ {
+ if (!allowdef) return NULL;
+ c = 32;
+ }
+ src = Zfont3+8*(c-32);
+ memcpy(&(myglyph.bitmap[0]),src,8);
+ myglyph.h = 8;
+ return (SF_glyph *)&myglyph;
+ }
+
+static SFONT myfont3 = {
+ 0,
+ nodestroy,
+ myheight,
+ myascent,
+ mydescent,
+ myminchar,
+ mymaxchar,
+ myhasglyph,
+ mygetglyph,
+ 0,
+ NULL};
+
+SFONT * SF_font3 = &myfont3;
+
+static int myheight2(SFONT *s){ return 16;}
+static int myascent2(SFONT *s){ return 12;}
+static int mydescent2(SFONT *s){ return 4;}
+static SF_glyph * mygetglyph2(SFONT *s, word c, int allowdef)
+ {
+ byte *src, *dst; int i;
+ if (c < 32 || c > 126)
+ {
+ if (!allowdef) return NULL;
+ c = 32;
+ }
+ src = Zfont3+8*(c-32);
+ dst = &(myglyph.bitmap[0]);
+ for (i=0;i<8;i++)
+ { dst[0] = dst[1] = src[i]; dst += 2;}
+ myglyph.h = 16;
+ return (SF_glyph *)&myglyph;
+ }
+
+static SFONT myfont3dbl = {
+ 0,
+ nodestroy,
+ myheight2,
+ myascent2,
+ mydescent2,
+ myminchar,
+ mymaxchar,
+ myhasglyph,
+ mygetglyph2,
+ 0,
+ NULL};
+
+SFONT * SF_font3double = &myfont3dbl;
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sf_frotz.h"
+
+// font handling
+
+/*
+struct sfontstruct {
+ int refcount;
+ void (*destroy)(SFONT *);
+ int (*height)(SFONT *);
+ int (*ascent)(SFONT *);
+ int (*descent)(SFONT *);
+ int (*minchar)(SFONT *);
+ int (*maxchar)(SFONT *);
+ int (*hasglyph)(SFONT *,int,int);
+ SF_glyph *(*getglyph)(SFONT *,int,int);
+ };
+*/
+
+typedef struct {
+ int refcount;
+ word minchar, maxchar, defchar;
+ byte ascent, descent;
+ int glyphs[0]; // offsets to glyphs from start of rec
+ } SF_bdffont;
+
+char * m_fontfiles[8];
+
+static char s[1026];
+
+static char * starts( char *s, char *id){
+ int len = strlen(id);
+ while (*s == ' ') s++;
+ if (memcmp(s,id,len)==0) return (s+len);
+ return NULL;
+ }
+
+static int hexd( char c){
+ if (c >= '0' && c <= '9') return c-'0';
+ if (c >= 'A' && c <= 'F') return c-'A'+10;
+ if (c >= 'a' && c <= 'f') return c-'a'+10;
+ return 0;
+ }
+
+static void gethex( char *p, byte *dst, int n){
+ while (n--){
+ *dst++ = 16*hexd(p[0])+hexd(p[1]);
+ p += 2;
+ }
+ }
+
+#define ERRET(n) { *err = n; return font;}
+
+static SF_bdffont *sBDXload( FILE *f, int *err, int *size, int MAXCHAR){
+ int totb, i, k, wh[4];
+ byte *po, *pbeg;
+ char *p, *q;
+ char *fontname = "", *copyright = "unknown";
+ int fngot=0,cpgot=0;
+ int hasenc, hasbbx;
+ SF_bdffont *font = NULL;
+ int foffs, minch, maxch, rejected=0;
+ int defchar = -1, descent = -1, ascent = -1, nprop, nchars;
+ // NOTE: file MUST be opened as binary on MSDOS, otherwise
+ // ftell() won't return correct values if the file does not
+ // have CR's (as is usually the case, since BDF files frequently
+ // come from Unix systems)
+
+ // header
+ for (;;){
+ fgets(s,1024,f);
+ if (feof(f)) ERRET(-1) // errorexit(99,"unexpected EOF\n");
+ if ((p = starts(s,"FONT "))){
+ while (*p == ' ') p++;
+ q = p;
+ while (*p >= ' ') p++;
+ *p = 0;
+ fontname = strdup(q);
+ fngot = 1;
+ }
+ if ((p = starts(s,"STARTPROPERTIES "))){
+ nprop = atoi(p);
+ break;
+ }
+ }
+ for (i=0;i<nprop;i++){
+ fgets(s,1024,f);
+ if (feof(f)) ERRET(-51) // errorexit(99,"unexpected EOF\n");
+ if ((p = starts(s,"COPYRIGHT "))){
+ while (*p == ' ') p++;
+ q = p;
+ while (*p >= ' ') p++;
+ *p = 0;
+ copyright = strdup(q);
+ cpgot = 1;
+ }
+ if ((p = starts(s,"DEFAULT_CHAR ")))
+ defchar = atoi(p);
+ if ((p = starts(s,"FONT_ASCENT ")))
+ ascent = atoi(p);
+ if ((p = starts(s,"FONT_DESCENT ")))
+ descent = atoi(p);
+ }
+ for (;;){
+ fgets(s,1024,f);
+ if (feof(f)) ERRET(-61) // errorexit(99,"unexpected EOF\n");
+ if ((p = starts(s,"CHARS "))){
+ nchars = atoi(p);
+ break;
+ }
+ }
+ foffs = ftell(f);
+
+ // first pass
+ totb = 0;
+ minch = 65536; maxch = -1;
+ hasenc = 0;
+ for (i=0;i<nchars;){
+ fgets(s,1024,f);
+ if (feof(f)) ERRET(-71) // errorexit(99,"unexpected EOF\n");
+ if ((p = starts(s,"ENCODING "))){
+ k = atoi(p);
+ k &= 0xffff;
+ if (k <= MAXCHAR) {
+ if (k < minch) minch = k;
+ if (k > maxch) maxch = k;
+ }
+ else rejected++;
+ hasenc = 1;
+ }
+ if ((p = starts(s,"BBX "))){
+ if (!hasenc) ERRET(-10)
+ sscanf(p,"%d %d",wh,wh+1);
+//printf("c%d %d\n",k,((wh[0]+7)/8)*wh[1] + sizeof(SF_glyph));
+ if (k <= MAXCHAR){
+ totb += ((wh[0]+7)/8)*wh[1] + sizeof(SF_glyph);
+ }
+ i++;
+ hasenc = 0;
+ }
+ }
+//printf("nchars=%d minch=%d maxch=%d\n",nchars,minch,maxch);
+//printf("sizeof(SF_glyph)=%d\n",sizeof(SF_glyph));
+// printf("<%s>\n",fontname);
+// printf("<%s>\n",copyright);
+// printf("dc%d a%d d%d %d..%d exp%d read%d totb%d\n",
+// defchar,ascent,descent,minch,maxch,nchars,i,totb);
+ if (ascent < 0 || descent < 0 || nchars != i)
+ ERRET(-2)
+// errorexit(99,"??? dc%d a%d d%d %d..%d exp%d read%d\n",
+// defchar,ascent,descent,minch,maxch,nchars,k);
+ if (defchar < minch || defchar > maxch){
+// printf("WARNING: defchar=%d, set to",defchar);
+ if (defchar < minch) defchar = minch;
+ if (defchar > maxch) defchar = maxch;
+// printf(" %d\n",defchar);
+ }
+// nchars = i;
+ totb += (maxch-minch+1)*sizeof(int)+sizeof(SF_bdffont)+strlen(fontname)+
+ strlen(copyright)+2;
+// printf("totb %d\n",totb);
+ font = calloc(1,totb);
+ if (!font) ERRET(-3) // errorexit(99,"malloc()\n");
+//printf("allocated: %p-%p\n",font,((byte *)font)+totb);
+// memcpy(&(font->magic[0]),"gBDX",4);
+ font->minchar = minch;
+ font->maxchar = maxch;
+ font->defchar = defchar;
+ font->ascent = ascent;
+ font->descent = descent;
+ pbeg = (byte *)font;
+ po = (byte *)(&(font->glyphs[maxch-minch+1]));
+ k = strlen(fontname)+1;
+ memcpy(po,fontname,k); po += k; if (fngot) free(fontname);
+ k = strlen(copyright)+1;
+ memcpy(po,copyright,k); po += k; if (cpgot) free(copyright);
+ for (i=minch;i<=maxch;i++) font->glyphs[i-minch] = 0;
+
+ // second pass
+ fseek(f,foffs,0);
+ for (k=0;k<nchars;k++){
+ SF_glyph *bg;
+ int j;
+ int dwid,w,h;
+
+ bg = (SF_glyph *)po;
+ hasenc = hasbbx = 0;
+ for (;;){
+ fgets(s,1024,f);
+ if (feof(f)) ERRET(-81) // errorexit(99,"unexpected EOF c=%d\n",i);
+ if ((p = starts(s,"ENCODING "))){
+ i = atoi(p);
+ i &= 0xffff;
+ if (i <= MAXCHAR)
+ font->glyphs[i-minch] = po-pbeg;
+ hasenc = 1;
+ }
+ if ((p = starts(s,"DWIDTH "))){
+ if (!hasenc) ERRET(-11)
+ dwid = atoi(p);
+ }
+ if ((p = starts(s,"BBX "))){
+ if (!hasenc) ERRET(-12)
+ sscanf(p,"%d %d %d %d",wh,wh+1,wh+2,wh+3);
+ hasbbx = 1;
+ }
+ if (starts(s,"BITMAP")) break;
+ }
+ if (!hasenc || !hasbbx) ERRET(-13)
+ if (i <= MAXCHAR) {
+ bg->dx = dwid;
+ bg->w = wh[0]; w = (wh[0]+7)/8;
+ bg->h = h = wh[1];
+ bg->xof = wh[2];
+ bg->yof = wh[3];
+ po = (byte *)(&(bg->bitmap[0]));
+//printf("k=%d ch=%d delta=%d\n",k,i,po-(byte *)bg);
+ for (j=0;j<h;j++){
+ fgets(s,1024,f);
+ if (feof(f)) ERRET(-91) // errorexit(99,"unexpected EOF c=%d (B)\n",i);
+ gethex(s,po,w);
+ po += w;
+ }
+ }
+//printf("used %d nextpo %p\n",po-pbeg-font->glyphs[i-minch],po);
+ }
+ *err = 0;
+ *size = totb;
+ return font;
+ }
+
+// destructor for fonts with all dynamic data
+static void bdestroy( SFONT *s)
+ {
+ if (s)
+ {
+ if (s->data) free(s->data);
+ free(s);
+ }
+ }
+
+static int bheight( SFONT *s)
+ {
+ if (s) if (s->data)
+ {
+ SF_bdffont *f = s->data;
+ return ((int)(f->ascent)+(int)(f->descent));
+ }
+ return 0;
+ }
+
+static int bascent( SFONT *s)
+ {
+ if (s) if (s->data) return (int)((SF_bdffont *)(s->data))->ascent;
+ return 0;
+ }
+
+static int bdescent( SFONT *s)
+ {
+ if (s) if (s->data) return (int)((SF_bdffont *)(s->data))->descent;
+ return 0;
+ }
+
+static int bminchar( SFONT *s)
+ {
+ if (s) if (s->data) return (int)((SF_bdffont *)(s->data))->minchar;
+ return 0;
+ }
+
+static int bmaxchar( SFONT *s)
+ {
+ if (s) if (s->data) return (int)((SF_bdffont *)(s->data))->maxchar;
+ return 0;
+ }
+
+static SF_glyph *getglyph( SFONT *fo, word c, int allowdef)
+ {
+ int m; SF_bdffont *b;
+ if (!fo) return NULL;
+ b = fo->data;
+ if (!b) return NULL;
+ if (c < b->minchar || c > b->maxchar)
+ {
+ if (allowdef) c = b->defchar;
+ else return NULL;
+ }
+ m = b->glyphs[c-b->minchar];
+ if (!m)
+ {
+ if (allowdef) m = b->glyphs[0];
+ else return NULL;
+ }
+ return (SF_glyph *)(((byte *)b)+m);
+ }
+
+static int hasglyph( SFONT *fo, word c, int allowdef)
+ {
+ return (getglyph(fo,c,allowdef) != NULL);
+ }
+
+static SFONT * makefont( SF_bdffont *b)
+ {
+ SFONT * res;
+ if (!b) return NULL;
+ res = calloc(1,sizeof(SFONT));
+ if (!res)
+ {
+ free(b);
+ return NULL;
+ }
+ res->destroy = bdestroy;
+ res->height = bheight;
+ res->data = b;
+ res->ascent = bascent;
+ res->descent = bdescent;
+ res->minchar = bminchar;
+ res->maxchar = bmaxchar;
+ res->hasglyph = hasglyph;
+ res->getglyph = getglyph;
+ return res;
+ }
+
+static SFONT * loadfont( char *fname, int *err, int *size){
+ SF_bdffont * font; SFONT *res;
+ FILE *f;
+ *err = 0;
+ if (!fname) { *err = -8; return NULL;}
+ // NOTE: file MUST be opened as binary on MSDOS, otherwise
+ // ftell() won't return correct values if the file does not
+ // have CR's (as is usually the case, since BDF files frequently
+ // come from Unix systems)
+ f = fopen(fname,"rb"); if (!f) {*err = -7; return NULL;}
+ font = sBDXload(f,err,size,65535);
+ fclose(f);
+ if (*err)
+ {
+ if (font) free(font);
+ return NULL;
+ }
+ res = makefont(font);
+ if (!res)
+ {
+ free(font);
+ *err = -9999;
+ return NULL;
+ }
+ return res;
+ }
+
+// these are the 8 fonts needed
+// PROPORTIONAL FONT
+// ROMAN
+// ROMAN BOLD
+// ITALIC
+// ITALIC BOLD
+// FIXED FONT
+// ROMAN
+// ROMAN BOLD
+// ITALIC
+// ITALIC BOLD
+
+#define NUMFONTS 9
+
+static SFONT *myfonts[9] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+
+static int styleidx( int zfont, int style)
+ {
+ int k = 0;
+ if (zfont == GRAPHICS_FONT) return 8;
+ if (zfont != TEXT_FONT) zfont = FIXED_WIDTH_FONT;
+ if (style & FIXED_WIDTH_STYLE) zfont = FIXED_WIDTH_FONT;
+ if (h_flags & FIXED_FONT_FLAG) zfont = FIXED_WIDTH_FONT;
+ if (zfont != TEXT_FONT) k += 4;
+ if (style & EMPHASIS_STYLE) k += 2;
+ if (style & BOLDFACE_STYLE) k++;
+ return k;
+ }
+
+static SF_textsetting current;
+#define MAXSTACK 16
+static SF_textsetting tsstack[MAXSTACK];
+static int tsstackptr = 0;
+
+// does not increase the refcount of f - user must do that
+static void setfontk( int k, SFONT *f)
+ {
+ if (myfonts[k])
+ {
+ myfonts[k]->refcount--;
+ if (myfonts[k]->refcount == 0) myfonts[k]->destroy(myfonts[k]);
+ }
+ myfonts[k] = f;
+ }
+
+static void cleanfonts()
+ {
+ int i;
+ for (i=0;i<8;i++) setfontk(i,NULL);
+/* for (i=0;i<8;i++)
+ if (m_fontfiles[i]){ free(m_fontfiles[i]); m_fontfiles[i] = NULL;}*/
+ }
+
+SF_textsetting * sf_curtextsetting()
+ {
+ return ¤t;
+ }
+
+void sf_pushtextsettings()
+ {
+ if (tsstackptr < MAXSTACK)
+ {
+ tsstack[tsstackptr] = current;
+ tsstackptr++;
+ }
+ }
+
+void sf_poptextsettings()
+ {
+ if (tsstackptr)
+ {
+ tsstackptr--;
+ current = tsstack[tsstackptr];
+ }
+ }
+
+/*
+ * os_check_unicode
+ *
+ * Return with bit 0 set if the Unicode character can be
+ * displayed, and bit 1 if it can be input.
+ *
+ *
+ */
+int os_check_unicode(int font, zword c)
+ {
+ return ((current.font->hasglyph(current.font,c,0) != 0) ? 3 : 2);
+ }
+
+static int charwidth( zword c, int *oh)
+ {
+ SF_glyph *g; int ww = 0;
+ if (c == ZC_INDENT) return (3*charwidth(' ',oh));
+ if (c == ZC_GAP) return (2*charwidth(' ',oh));
+ *oh = 0;
+ g = current.font->getglyph(current.font,c,1);
+ if (g)
+ {
+ int ext = g->w+g->xof;
+ if (ext > g->dx) *oh = ext-g->dx;
+ ww = (g->dx);
+ }
+ return ww;
+ }
+
+/*
+ * os_char_width
+ *
+ * Return the length of the character in screen units.
+ *
+ */
+int os_char_width(zword c)
+ {
+ int w, oh;
+ w = charwidth( c, &oh);
+ return (w+oh);
+ }
+
+
+static void setfont( int zfont){
+ int k = styleidx(zfont,current.style);
+//printf("%d.",k);
+ current.font = myfonts[k];
+// if (k < 4) zfont = TEXT_FONT;
+// else zfont = FIXED_WIDTH_FONT;
+ current.zfontnum = zfont;
+ current.proportional = (k < 4);
+ }
+
+static void setstyle( int style){
+ current.style = style;
+ setfont(current.zfontnum);
+ }
+
+/*
+ * os_font_data
+ *
+ * Return true if the given font is available. The font can be
+ *
+ * TEXT_FONT
+ * PICTURE_FONT
+ * GRAPHICS_FONT
+ * FIXED_WIDTH_FONT
+ *
+ * The font size should be stored in "height" and "width". If
+ * the given font is unavailable then these values must _not_
+ * be changed.
+ *
+ */
+int os_font_data( int font, int *height, int *width)
+ {
+ switch (font)
+ {
+ case TEXT_FONT:
+ case FIXED_WIDTH_FONT:
+ case GRAPHICS_FONT:
+ {
+ sf_pushtextsettings();
+ setfont(font);
+ setstyle(0);
+ *height = current.font->height(current.font);
+ *width = os_char_width((zword)('0'));
+ sf_poptextsettings();
+ return 1;
+ }
+ default: break;
+ }
+ return 0;
+ }
+
+/*
+ * os_set_font
+ *
+ * Set the font for text output. The interpreter takes care not to
+ * choose fonts which aren't supported by the interface.
+ *
+ */
+void os_set_font( int new_font)
+ {
+//printf("os_set_font(%d)\n",new_font);
+ sf_flushtext();
+ setfont(new_font);
+ }
+
+/*
+ * os_set_text_style
+ *
+ * Set the current text style. Following flags can be set:
+ *
+ * REVERSE_STYLE
+ * BOLDFACE_STYLE
+ * EMPHASIS_STYLE (aka underline aka italics)
+ * FIXED_WIDTH_STYLE
+ *
+ */
+void os_set_text_style( int new_style)
+ {
+ sf_flushtext();
+ setstyle(new_style);
+ }
+
+/*
+ * os_string_width
+ *
+ * Calculate the length of a word in screen units. Apart from letters,
+ * the word may contain special codes:
+ *
+ * ZC_NEW_STYLE - next character is a new text style
+ * ZC_NEW_FONT - next character is a new font
+ *
+ */
+int os_string_width(const zword *s)
+ {
+ int width = 0, wacc = 0, oh = 0; zword c;
+
+ setfont(current.zfontnum);
+ // Look for style or font changes, or indents
+ sf_pushtextsettings();
+ while ((c = *s++))
+ {
+ if (c == ZC_NEW_STYLE)
+ {
+ wacc = width+oh; width = 0;
+ os_set_text_style(*s++);
+ }
+ else if (c == ZC_NEW_FONT)
+ {
+ wacc = width+oh; width = 0;
+ os_set_font(*s++);
+ }
+ else
+ width += charwidth(c,&oh);
+ }
+ sf_poptextsettings();
+
+ return (width+oh+wacc);
+ }
+
+/*
+ * os_display_string
+ *
+ * Pass a string of characters to os_display_char.
+ *
+ */
+void os_display_string(const zword *s)
+ {
+ zword c;
+ while ((c = *s++) != 0)
+ {
+ if (c == ZC_NEW_FONT)
+ os_set_font(*s++);
+ else if (c == ZC_NEW_STYLE)
+ os_set_text_style(*s++);
+ else
+ os_display_char(c);
+ }
+ }
+
+/*
+ * os_display_char
+ *
+ * Display a character of the current font using the current colours and
+ * text style. The cursor moves to the next position. Printable codes are
+ * all ASCII values from 32 to 126, ISO Latin-1 characters from 160 to
+ * 255, ZC_GAP (gap between two sentences) and ZC_INDENT (paragraph
+ * indentation), and Unicode characters above 255. The screen should not
+ * be scrolled after printing to the bottom right corner.
+ *
+ */
+void os_display_char(zword c)
+ {
+ if (c == ZC_INDENT)
+ {
+ os_display_char(' ');
+ os_display_char(' ');
+ os_display_char(' ');
+ }
+ else if (c == ZC_GAP)
+ {
+ os_display_char(' ');
+ os_display_char(' ');
+ }
+ else if ((c >= 32 && c <= 126) || (c >= 160))
+ {
+ SF_glyph *g;
+ setfont(current.zfontnum);
+ g = current.font->getglyph(current.font,c,1);
+//printf("{%c}%d.%p/%p",c,current.zfontnum,current.font,g);
+ if (g)
+ {
+//printf("[%c]\n",c); fflush(stdout);
+ sf_writeglyph(g);
+ m_exitPause = true;
+ }
+ }
+}
+
+/*
+ * os_buffer_screen
+ *
+ * Set the screen buffering mode, and return the previous mode.
+ * Possible values for mode are:
+ *
+ * 0 - update the display to reflect changes when possible
+ * 1 - do not update the display
+ * -1 - redraw the screen, do not change the mode
+ *
+ */
+int os_buffer_screen (int mode)
+ {
+ if (mode == -1)
+ sf_flushdisplay();
+ return 0;
+ }
+
+/*
+ * os_wrap_window
+ *
+ * Return non-zero if the window should have text wrapped.
+ *
+ */
+int os_wrap_window (int win)
+ {
+ return 1;
+ }
+
+/*
+ * os_window_height
+ *
+ * Called when the height of a window is changed.
+ *
+ */
+void os_window_height (int win, int height)
+ {
+ }
+
+/*
+ * os_set_cursor
+ *
+ * Place the text cursor at the given coordinates. Top left is (1,1).
+ *
+ */
+void os_set_cursor(int row, int col)
+ {
+ sf_flushtext();
+// theWnd->ResetOverhang();
+ current.cx = col-1;
+ current.cy = row-1;
+ }
+
+extern SF_bdffont * SF_defaultfont;
+extern int SF_defaultfontsize;
+
+static void destroySFonly( SFONT *f)
+ {
+ if (f) free(f);
+ }
+
+extern SFONT *SF_font3, *SF_font3double;
+
+SFONT * (*ttfontloader)( char *fspec, int *err) = NULL;
+void (*ttfontsdone)() = NULL;
+
+static SFONT *tryloadfont( char *fspec)
+ {
+ int err,size;
+ char *p;
+ SFONT *b = NULL;
+ for (;;){
+ p = strchr(fspec,'|');
+ if (p) *p = 0;
+ if (ttfontloader)
+ b = ttfontloader(fspec,&err);
+ if (!b)
+ b = loadfont(fspec,&err,&size);
+ if (b) break;
+ if (p) { *p = '|'; fspec = p+1;}
+ else break;
+ }
+ return b;
+ }
+
+SFONT *sf_VGA_SFONT;
+
+// ensure a font loaded
+void sf_initfonts()
+ {
+ int i, j, size=0;
+ int w,h,nby,m,nocc;
+ byte *cfont, *bmp; SF_glyph *g;
+ SF_bdffont *b, *norm, *emph, *bold, *bemp;
+ SFONT *Norm, *Emph=NULL, *Bold=NULL, *Bemp=NULL;
+
+ norm = SF_defaultfont;
+//dumpfont(norm);
+ sf_VGA_SFONT = Norm = makefont(norm);
+ if (!Norm) os_fatal("malloc() failure in initfonts()");
+ Norm->destroy = destroySFonly;
+
+ // get size of default font
+ size = SF_defaultfontsize;
+
+ // copy norm to emphasized
+ emph = malloc(size);
+ if (!emph) os_fatal("malloc() failure in initfonts()");
+ Emph = makefont(emph);
+ if (!Emph) os_fatal("malloc() failure in initfonts()");
+ memcpy (emph, norm, size);
+ // emphasize (underline)...
+ cfont = (byte *)emph;
+ for (i = norm->minchar;i <= norm->maxchar;i++){
+ m = norm->glyphs[i-norm->minchar];
+ if (!m) continue;
+ g = (SF_glyph *)(cfont + m);
+ w = g->dx;
+ h = g->h; nby = (g->w+7)/8;
+ bmp = (byte *)(&(g->bitmap[0]));
+ bmp[h-2] = 0xff;
+ }
+ // make a copy for bold
+ bold = malloc(size);
+ if (!bold) os_fatal("malloc() failure in initfonts()");
+ Bold = makefont(bold);
+ if (!Bold) os_fatal("malloc() failure in initfonts()");
+ memcpy (bold, norm, size);
+ // boldify...
+ cfont = (byte *)bold;
+ for (i = norm->minchar;i <= norm->maxchar;i++){
+ int c;
+ m = norm->glyphs[i-norm->minchar];
+ if (!m) continue;
+ g = (SF_glyph *)(cfont + m);
+ w = g->dx;
+ h = g->h; nby = (g->w+7)/8;
+ bmp = (byte *)(&(g->bitmap[0]));
+ for (j=0;j<h;j++){
+ c = bmp[j];
+ bmp[j] = (c) | (c >> 1);
+ }
+ }
+ // copy bold to bold, emphasized
+ bemp = malloc(size);
+ if (!bemp) os_fatal("malloc() failure in initfonts()");
+ Bemp = makefont(bemp);
+ if (!Bemp) os_fatal("malloc() failure in initfonts()");
+ memcpy (bemp, bold, size);
+ // emphasize (underline)...
+ cfont = (byte *)bemp;
+ for (i = norm->minchar;i <= norm->maxchar;i++){
+ m = norm->glyphs[i-norm->minchar];
+ if (!m) continue;
+ g = (SF_glyph *)(cfont + m);
+ w = g->dx;
+ h = g->h; nby = (g->w+7)/8;
+ bmp = (byte *)(&(g->bitmap[0]));
+ bmp[h-2] = 0xff;
+ }
+
+ myfonts[0] = myfonts[4] = Norm;
+ norm->refcount = 2;
+ myfonts[1] = myfonts[5] = Bold;
+ bold->refcount = 2;
+ myfonts[2] = myfonts[6] = Emph;
+ emph->refcount = 2;
+ myfonts[3] = myfonts[7] = Bemp;
+ bemp->refcount = 2;
+
+// for (i=0;i<8;i++) myfonts[i] = SF_defaultfont;
+// SF_defaultfont->refcount = 9;
+
+ CLEANREG(cleanfonts);
+
+ if (!m_vga_fonts)
+ {
+ for (i=0;i<8;i++)
+ if (m_fontfiles[i])
+ {
+ SFONT *b = tryloadfont(m_fontfiles[i]);
+ if (!b) fprintf(stderr,"WARNING: could not load font%d [%s]\n",i,m_fontfiles[i]);
+ else
+ {
+ setfontk(i,b);
+ b->refcount = 1;
+ }
+ }
+ }
+
+ if (ttfontsdone) ttfontsdone();
+ // now set the graphics font
+ if (myfonts[4]->height(myfonts[4]) < 16)
+ myfonts[8] = SF_font3;
+ else
+ myfonts[8] = SF_font3double;
+
+//for (i=0;i<8;i++){ SFONT *s = myfonts[i]; printf("%d %p %d %d %d %d %d\n",
+//i,s,s->minchar(s),s->maxchar(s),s->ascent(s),s->descent(s),s->height(s));}
+
+ }
+
--- /dev/null
+#ifndef _SF_FROTZ_H
+#define _SF_FROTZ_H
+
+#include "frotz.h"
+#include "blorb.h"
+
+// version info
+#define SFROTZ_MAJOR 0
+#define SFROTZ_MINOR 2
+
+// typedef unsigned char byte;
+// typedef unsigned short word;
+// typedef unsigned long ulong;
+#include <stdint.h>
+typedef uint8_t byte;
+typedef uint16_t word;
+// typedef uint32_t ulong;
+#define ulong uint32_t
+
+typedef struct {
+ bb_result_t bbres;
+ ulong type;
+ FILE *file;
+ } myresource;
+
+int sf_getresource( int num, int ispic, int method, myresource * res);
+void sf_freeresource( myresource *res);
+
+#ifndef true
+#define true 1
+#endif
+#ifndef false
+#define false 0
+#endif
+
+#define NON_STD_COLS 238
+
+// thiss assumes RGBA with lsb = R
+
+static inline ulong RGB5ToTrue( word w)
+ {
+ int _r = w & 0x001F; int _g = (w & 0x03E0)>>5; int _b = (w & 0x7C00)>>10;
+ _r = (_r<<3) | (_r>>2);
+ _g = (_g<<3) | (_g>>2);
+ _b = (_b<<3) | (_b>>2);
+ return (ulong) ( _r | (_g<<8) | (_b<<16));
+ }
+
+static inline word TrueToRGB5( ulong u)
+ {
+ return (word)(((u >> 3) & 0x001f) | ((u >> 6) & 0x03e0) | ((u >> 9) & 0x7c00));
+ }
+
+void reset_memory(void);
+void replay_close(void);
+void set_header_extension (int entry, zword val);
+int colour_in_use(zword colour);
+
+// various data
+
+extern bool m_tandy;
+extern bool m_quetzal;
+// CRect m_wndSize;
+// CString m_propFontName;
+// CString m_fixedFontName;
+// int m_fontSize;
+extern int m_v6scale;
+extern int m_gfxScale;
+extern ulong m_defaultFore;
+extern ulong m_defaultBack;
+extern ulong m_colours[11];
+extern ulong m_nonStdColours[NON_STD_COLS];
+extern int m_nonStdIndex;
+extern bool m_exitPause;
+extern bool m_lineInput;
+//extern bool m_IsInfocomV6;
+// bool m_fastScroll;
+extern bool m_morePrompts;
+// int m_leftMargin;
+// int m_rightMargin;
+// FILE* m_blorbFile;
+// bb_map_t* m_blorbMap;
+// GameInfo m_gameInfo;
+extern int AcWidth;
+extern int AcHeight;
+extern int m_random_seed;
+extern int m_fullscreen;
+extern int m_reqW, m_reqH;
+extern char * m_fontfiles[8];
+extern bool m_localfiles;
+extern int m_no_sound;
+extern int m_vga_fonts;
+extern int SFdticks;
+extern volatile bool SFticked;
+extern char * m_fontdir;
+extern bool m_aafonts;
+extern char * m_setupfile;
+extern int m_frequency;
+
+extern double m_gamma;
+
+// sf_resource.c
+
+// must be called as soon as possible (i.e. by os_process_arguments())
+int sf_load_resources( char *givenfn);
+
+typedef struct {
+ int number; // 0 means unallocated
+ int width, height;
+ byte *pixels;
+ } sf_picture;
+
+#define DEFAULT_GAMMA 2.2
+
+void sf_setgamma( double gamma);
+
+//int sf_loadpic( int num, sf_picture *gfx);
+
+// get pointer from cache
+sf_picture * sf_getpic( int num);
+
+void sf_flushtext();
+
+// glyph
+typedef struct {
+ byte dx;
+ byte w, h;
+ char xof, yof;
+ byte bitmap[0];
+ } SF_glyph;
+
+typedef struct sfontstruct SFONT;
+
+extern SFONT * (*ttfontloader)( char *fspec, int *err);
+extern void (*ttfontsdone)();
+
+struct sfontstruct {
+ int refcount;
+ void (*destroy)(SFONT *);
+ int (*height)(SFONT *);
+ int (*ascent)(SFONT *);
+ int (*descent)(SFONT *);
+ int (*minchar)(SFONT *);
+ int (*maxchar)(SFONT *);
+ int (*hasglyph)(SFONT *,word,int);
+ SF_glyph *(*getglyph)(SFONT *,word,int);
+ int antialiased;
+ void *data;
+ };
+
+typedef struct {
+ SFONT *font;
+ int proportional;
+ int style, zfontnum;
+ int cx, cy; // cursor position - 0 based
+ int oh; // overhang
+ unsigned long fore, back;
+ bool foreDefault, backDefault, backTransparent;
+ } SF_textsetting;
+
+SF_textsetting * sf_curtextsetting();
+
+void sf_writeglyph( SF_glyph *g);
+
+void sf_fillrect( unsigned long color, int x, int y, int w, int h);
+
+
+int sf_GetProfileInt( const char *sect, const char *id, int def);
+double sf_GetProfileDouble( const char *sect, const char *id, double def);
+char * sf_GetProfileString( const char *sect, const char *id, char * def);
+
+void sf_readsettings();
+
+ulong sf_GetColour( int colour);
+ulong sf_GetDefaultColour(bool fore);
+int sf_GetColourIndex( ulong colour);
+
+void sf_initvideo( int w, int h, int full);
+
+int sf_initsound();
+
+void sf_cleanup_all();
+void sf_regcleanfunc( void *f, const char *nam);
+#define CLEANREG( f) sf_regcleanfunc( (void *)f, #f)
+
+const char * sf_msgstring( int id);
+
+// consts for msg ids
+enum { IDS_BLORB_GLULX, IDS_BLORB_NOEXEC, IDS_MORE, IDS_HIT_KEY_EXIT, IDS_TITLE,
+ IDS_FATAL, IDS_FROTZ, IDS_FAIL_DIRECTSOUND, IDS_FAIL_MODPLUG, IDS_ABOUT_INFO,
+ IDS_SAVE_FILTER, IDS_SAVE_TITLE, IDS_RESTORE_TITLE,
+ IDS_SCRIPT_FILTER, IDS_SCRIPT_TITLE,
+ IDS_RECORD_FILTER, IDS_RECORD_TITLE, IDS_PLAYBACK_TITLE,
+ IDS_AUX_FILTER, IDS_SAVE_AUX_TITLE, IDS_LOAD_AUX_TITLE };
+
+bool sf_IsInfocomV6();
+
+ulong sf_blend( int a, ulong s, ulong d);
+
+void sf_sleep( int millisecs);
+
+unsigned long sf_ticks (void);
+
+void sf_DrawInput(zword * buffer, int pos, int ptx, int pty, int width, bool cursor);
+
+int sf_aiffwav( FILE *f, int foffs, void ** wav, int *size);
+
+int sf_pkread( FILE *f, int foffs, void ** out, int *size);
+
+ulong * sf_savearea( int x, int y, int w, int h);
+void sf_restoreareaandfree( ulong *s);
+#define SF_NOTIMP (-9999)
+
+zword sf_read_key( int timeout, int cursor, int allowed);
+
+int sf_user_fdialog( bool exist, const char *def, const char *filt, const char *title, char **res);
+extern int (*sf_osdialog)( bool ex, const char *def, const char *filt, const char *tit, char **res,
+ ulong *sbuf, int sbp, int ew, int eh, int isfull);
+
+#ifdef WIN32
+#define OS_PATHSEP ';'
+#else
+#define OS_PATHSEP ':'
+#endif
+
+// virtual keys
+#define VK_TAB 0x16
+#define VK_INS 0x17
+
+// for AIFF resampling
+
+typedef struct CONVstruct CONV;
+struct CONVstruct {
+ double ratio;
+ // input
+ int channels;
+ int bytespersam;
+ // returns num of output samples
+ int (* doCONV)( CONV *, FILE *, void *, int, int );
+ void (* finishCONV)( CONV *);
+ int maxin, maxout;
+ float *inbuf, *outbuf;
+ void *aux;
+};
+
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include "sf_frotz.h"
+
+/////////////////////////////////////////////////////////////////
+
+static char * sf_searchfile( char *fn, int fnlen, char *buf, char *paths)
+ {
+ char *p;
+ if (!fn) return NULL;
+ if (!paths) paths = "";
+ if (fnlen < 0) fnlen = strlen(fn);
+ if (!fnlen) return NULL;
+ for (;;)
+ {
+ int plen;
+ p = strchr(paths,OS_PATHSEP);
+ if (p)
+ plen = p-paths;
+ else
+ plen = strlen(paths);
+ if (plen) strncpy(buf,paths,plen);
+ buf[plen] = 0;
+ if ((plen) && (buf[plen-1] != '\\') && (buf[plen-1] != '/'))
+ strcat(buf,"/");
+ plen = strlen(buf);
+ strncpy(buf+plen,fn,fnlen);
+ buf[plen+fnlen] = 0;
+//printf("try[%s]\n",buf);
+ if (access(buf,F_OK)==0) return buf;
+ if (p) paths = p+1;
+ else break;
+ }
+ return NULL;
+ }
+
+/////////////////////////////////////////////////////////////////
+
+typedef struct {
+ SFONT sfont;
+ int ascent, descent, height;
+ int minchar, maxchar, totglyphs;
+ SF_glyph *glyphs[0];
+ } MYFONT;
+
+// destructor
+static void bdestroy( SFONT *s)
+ {
+ if (s)
+ {
+ int i; MYFONT *f = (MYFONT *)s;
+ for (i=0;i<f->totglyphs;i++)
+ if (f->glyphs[i]) free(f->glyphs[i]);
+ free(s);
+ }
+ }
+
+static int bheight( SFONT *s)
+ {
+ if (s) return ((MYFONT *)s)->height;
+ return 0;
+ }
+
+static int bascent( SFONT *s)
+ {
+ if (s) return ((MYFONT *)s)->ascent;
+ return 0;
+ }
+
+static int bdescent( SFONT *s)
+ {
+ if (s) return ((MYFONT *)s)->descent;
+ return 0;
+ }
+
+static int bminchar( SFONT *s)
+ {
+ if (s) return ((MYFONT *)s)->minchar;
+ return 0;
+ }
+
+static int bmaxchar( SFONT *s)
+ {
+ if (s) return ((MYFONT *)s)->maxchar;
+ return 0;
+ }
+
+static SF_glyph *getglyph( SFONT *s, word c, int allowdef)
+ {
+ if (s)
+ {
+ int i; MYFONT *f = (MYFONT *)s;
+ if (c < f->minchar || c > f->maxchar)
+ {
+ if (allowdef) c = 0;
+ else return NULL;
+ }
+ return f->glyphs[c];
+ }
+ return NULL;
+ }
+
+static int hasglyph( SFONT *fo, word c, int allowdef)
+ {
+ return (getglyph(fo,c,allowdef) != NULL);
+ }
+
+static int inited = 0, initerr = 0;
+static FT_Library library;
+
+static void libfinish()
+ {
+ if (!inited) return;
+ FT_Done_FreeType( library );
+ inited = 0;
+ }
+
+static void libinit()
+ {
+ if (initerr) return;
+ if (inited) return;
+ initerr = FT_Init_FreeType( &library ); /* initialize library */
+ /* error handling omitted */
+ if (initerr)
+ printf("FT_Init_FreeType: error %d\n",initerr);
+ else
+ {
+ inited = 1;
+ atexit(libfinish);
+ }
+ }
+
+
+static MYFONT * makefont( int totglyphs)
+ {
+ MYFONT * res;
+ res = calloc(1,sizeof(MYFONT)+totglyphs*sizeof(SF_glyph *));
+ if (!res) return NULL;
+ res->sfont.destroy = bdestroy;
+ res->sfont.height = bheight;
+ res->sfont.ascent = bascent;
+ res->sfont.descent = bdescent;
+ res->sfont.minchar = bminchar;
+ res->sfont.maxchar = bmaxchar;
+ res->sfont.hasglyph = hasglyph;
+ res->sfont.getglyph = getglyph;
+ res->totglyphs = totglyphs;
+ res->maxchar = totglyphs - 1;
+ return res;
+ }
+
+#define MAXUNI 0x153
+
+static void setglyph( MYFONT *f, FT_Face face, int ch)
+ {
+ int err, gid = FT_Get_Char_Index( face, ch);
+ int mode = FT_RENDER_MODE_MONO;
+ SF_glyph *res;
+ FT_GlyphSlot slot = face->glyph;
+ int i,j, nbypr, pitch;
+ unsigned char *s;
+ FT_Bitmap *bitmap;
+
+ if (m_aafonts) mode = FT_RENDER_MODE_NORMAL;
+
+ err = FT_Load_Glyph( face, gid, 0);
+ if (err) return;
+ if (slot->format != FT_GLYPH_FORMAT_BITMAP)
+ {
+ err = FT_Render_Glyph(slot, mode);
+ if (err) return;
+ }
+ bitmap = &slot->bitmap;
+ nbypr = m_aafonts ? bitmap->width : (bitmap->width+7)/8;
+ res = calloc(1,sizeof(SF_glyph) + nbypr*bitmap->rows);
+ if (!res) return;
+ for (i=0;i<bitmap->rows;i++)
+ for (j=0;j<nbypr;j++)
+ res->bitmap[i*nbypr+j] = bitmap->buffer[i*bitmap->pitch+j];
+
+//printf("%c %d %p w%d\n",ch,bitmap->pitch,res,bitmap->width);
+//{
+//int i,j; unsigned char *p = &(res->bitmap[0]);
+//for (i=0;i<bitmap->rows;i++){
+// for (j=0;j<nbypr;j++) printf("%02x",*p++);
+// printf("\n");
+// }
+//}
+ res->w = bitmap->width;
+ res->h = bitmap->rows;
+ res->dx = slot->advance.x/64;
+ res->xof = slot->bitmap_left;
+ res->yof = slot->bitmap_top - bitmap->rows;
+
+ f->glyphs[ch] = res;
+ }
+
+static SFONT * loadftype( char *fname, int size, int *err)
+ {
+ MYFONT *res;
+ FT_Face face;
+ int i;
+
+ *err = 0;
+ if (!fname) { *err = -8; return NULL;}
+ libinit();
+ if (initerr) { *err = -99; return NULL;}
+
+ res = makefont( MAXUNI+1);
+ if (!res) { *err = -3; return NULL;}
+
+ *err = FT_New_Face( library, fname, 0, &face ); /* create face object */
+ if (*err){ res->sfont.destroy(&res->sfont); return NULL; }
+
+ *err = FT_Set_Pixel_Sizes( face, size, size);
+ if (*err){ res->sfont.destroy(&res->sfont); return NULL; }
+
+ res->ascent = face->size->metrics.ascender/64;
+ res->descent = -face->size->metrics.descender/64;
+ res->height = res->ascent+res->descent; //face->size->metrics.height/64;
+
+ res->sfont.antialiased = m_aafonts;
+ res->minchar = 32;
+ setglyph(res,face,0);
+ for (i=32;i<127;i++) setglyph(res,face,i);
+ for (i=0xa0;i<256;i++) setglyph(res,face,i);
+ setglyph(res,face,0x152);
+ setglyph(res,face,0x153);
+
+ FT_Done_Face( face );
+
+ return (SFONT *) res;
+ }
+
+#define DEFSIZE 14
+
+#ifdef WIN32
+#define SYSFONTS "c:/windows/fonts"
+#else
+#define SYSFONTS "/usr/share/fonts/freetype"
+#endif
+
+SFONT * sf_loadftype( char *fspec, int *err)
+ {
+ char buf[FILENAME_MAX], *fn, *at, *fenv;
+ int size = DEFSIZE, fnlen=-1;
+
+ at = strchr(fspec,'@');
+ if (at)
+ {
+ fnlen = at-fspec;
+ size = atoi(at+1);
+ }
+
+ fn = sf_searchfile( fspec, fnlen, buf, "");
+ if (!fn) fn = sf_searchfile( fspec, fnlen, buf, "./");
+ if (!fn)
+ if (m_fontdir)
+ fn = sf_searchfile( fspec, fnlen, buf, m_fontdir);
+ if (!fn) fn = sf_searchfile( fspec, fnlen, buf, SYSFONTS);
+ if (!fn)
+ {
+ fenv = getenv("FONTS");
+ if (fenv) fn = sf_searchfile( fspec, fnlen, buf, fenv);
+ }
+
+ if (!fn) return NULL;
+
+ return loadftype(fn,size,err);
+ }
+
+//////////////////////////////////////////
+
+static void initloader() __attribute__((constructor));
+static void initloader()
+ {
+ ttfontloader = sf_loadftype;
+ ttfontsdone = libfinish;
+ }
+
--- /dev/null
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sf_frotz.h"
+
+#include <math.h>
+
+#include "png.h"
+#include <setjmp.h>
+
+#include "blorblow.h"
+
+// static double m_gamma = DEFAULT_GAMMA;
+static byte toLinear[256];
+static byte fromLinear[256];
+
+ulong sf_blend( int a, ulong s, ulong d){
+ ulong r;
+ r = fromLinear[(toLinear[s & 0xff]*a + toLinear[d & 0xff]*(256-a))>>8];
+ s >>= 8; d >>= 8;
+ r |= (fromLinear[(toLinear[s & 0xff]*a + toLinear[d & 0xff]*(256-a))>>8]) << 8;
+ s >>= 8; d >>= 8;
+ r |= (fromLinear[(toLinear[s & 0xff]*a + toLinear[d & 0xff]*(256-a))>>8]) << 16;
+ return r;
+ }
+
+// Set the screen gamma and build gamma correction tables
+void sf_setgamma(double gamma)
+ {
+ int i;
+
+ m_gamma = gamma;
+ for (i = 0; i < 256; i++)
+ toLinear[i] = (int)((pow(i/255.0,gamma) * 255.0) + 0.5);
+ gamma = 1.0/gamma;
+ for (i = 0; i < 256; i++)
+ fromLinear[i] = (int)((pow(i/255.0,gamma) * 255.0) + 0.5);
+ }
+
+/////////////////////////////////////////////////////////////////////////////
+// Loader for PNG images
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct {
+ byte * gfxData;
+ unsigned long offset;
+ } PNGData;
+
+static void readPNGData(
+ png_structp png_ptr, png_bytep data, png_size_t length)
+ {
+ PNGData* pngData = (PNGData*)png_get_io_ptr(png_ptr);
+ memcpy(data,pngData->gfxData+pngData->offset,length);
+ pngData->offset += length;
+ }
+
+static int loadpng( byte *data, int length, sf_picture *graphic)
+ {
+ png_bytep * rowPointers = NULL;
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ png_infop end_info = NULL;
+ PNGData pngData;
+ png_uint_32 width, height;
+ int i, bit_depth, color_type, size;
+ double gamma;
+
+ graphic->pixels = NULL;
+ graphic->width = graphic->height = 0;
+
+ if (!png_check_sig(data,8))
+ return 0;
+
+ png_ptr = png_create_read_struct
+ (PNG_LIBPNG_VER_STRING,(png_voidp)NULL,NULL,NULL);
+ if (!png_ptr)
+ return 0;
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_read_struct(&png_ptr,
+ (png_infopp)NULL,(png_infopp)NULL);
+ return 0;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info)
+ {
+ png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)NULL);
+ return 0;
+ }
+
+ if (setjmp(png_ptr->jmpbuf))
+ {
+ png_destroy_read_struct(&png_ptr,&info_ptr,&end_info);
+ if (rowPointers) free(rowPointers);
+ if (graphic->pixels)
+ {
+ free(graphic->pixels);
+ graphic->pixels = NULL;
+ }
+ return 0;
+ }
+
+ pngData.gfxData = data;
+ pngData.offset = 8;
+ png_set_read_fn(png_ptr,&pngData,readPNGData);
+
+ png_set_sig_bytes(png_ptr,8);
+ png_read_info(png_ptr,info_ptr);
+
+ width = png_get_image_width(png_ptr,info_ptr);
+ height = png_get_image_height(png_ptr,info_ptr);
+ bit_depth = png_get_bit_depth(png_ptr,info_ptr);
+ color_type = png_get_color_type(png_ptr,info_ptr);
+
+ graphic->width = width;
+ graphic->height = height;
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8)
+ png_set_palette_to_rgb(png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ #ifdef __WIN32__
+ os_fatal("Missing png_set_gray_1_2_4_to_8\n");
+ #else
+ png_set_gray_1_2_4_to_8(png_ptr);
+ #endif
+ if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png_ptr);
+
+ if (png_get_gAMA(png_ptr,info_ptr,&gamma))
+ png_set_gamma(png_ptr,m_gamma,gamma);
+
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+ if (bit_depth < 8)
+ png_set_packing(png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+ // png_set_bgr(png_ptr);
+ png_set_filler(png_ptr,0xFF,PNG_FILLER_AFTER);
+
+// graphic->m_header = new BITMAPINFOHEADER;
+// ::ZeroMemory(graphic->m_header,sizeof(BITMAPINFOHEADER));
+// graphic->m_header->biSize = sizeof(BITMAPINFOHEADER);
+// graphic->m_header->biWidth = width;
+// graphic->m_header->biHeight = height*-1;
+// graphic->m_header->biPlanes = 1;
+// graphic->m_header->biBitCount = 32;
+// graphic->m_header->biCompression = BI_RGB;
+
+ size = width*height*4;
+ graphic->pixels = (byte *)malloc(size);
+
+ rowPointers = (png_bytep *) malloc(height*sizeof(png_bytep));
+ for (i = 0; i < (int)height; i++)
+ rowPointers[i] = graphic->pixels+(width*i*4);
+ png_read_image(png_ptr,rowPointers);
+
+ // Get the palette after reading the image, so that the gamma
+ // correction is applied
+// png_colorp palette;
+// int num_palette;
+// if (png_get_PLTE(png_ptr,info_ptr,&palette,&num_palette))
+// {
+// for (int i = 0; i < num_palette; i++)
+// {
+// DWORD colour =
+// (palette[i].red<<16)|(palette[i].green<<8)|palette[i].blue;
+// graphic->m_palette.Add(colour);
+// graphic->m_invPalette[colour] = i;
+// }
+// }
+
+ png_read_end(png_ptr,end_info);
+ png_destroy_read_struct(&png_ptr,&info_ptr,&end_info);
+ if (rowPointers) free( rowPointers);
+
+ return 1;
+ }
+
+/////////////////////////////////////////////////////////////////////////////
+// Loader for JPEG images
+/////////////////////////////////////////////////////////////////////////////
+
+#include <jpeglib.h>
+
+// Error Handling
+
+struct JPEGErrorInfo {
+ struct jpeg_error_mgr base;
+ jmp_buf errorJump;
+ };
+
+static void errorJPEGExit(j_common_ptr cinfo)
+ {
+ struct JPEGErrorInfo* error = (struct JPEGErrorInfo*)cinfo->err;
+ (*cinfo->err->output_message)(cinfo);
+ longjmp(error->errorJump,1);
+ }
+
+static void outputJPEGMessage(j_common_ptr cinfo)
+ {
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo,buffer);
+// TRACE("JPEG: %s\n",buffer);
+ }
+
+// Memory Data Source
+
+static void memJPEGInit(j_decompress_ptr unused)
+ {
+ }
+
+static int memJPEGFillInput(j_decompress_ptr unused)
+ {
+ return 0;
+ }
+
+static void memJPEGSkipInput(j_decompress_ptr cinfo, long num_bytes)
+ {
+ if (num_bytes > 0)
+ {
+ if (num_bytes > (long)cinfo->src->bytes_in_buffer)
+ num_bytes = (long)cinfo->src->bytes_in_buffer;
+
+ cinfo->src->next_input_byte += num_bytes;
+ cinfo->src->bytes_in_buffer -= num_bytes;
+ }
+ }
+
+static void memJPEGTerm(j_decompress_ptr unused)
+ {
+ }
+
+static int loadjpeg( byte *data, int length, sf_picture *graphic)
+ {
+ struct jpeg_decompress_struct info;
+ struct JPEGErrorInfo error;
+ int width, height, size;
+ JSAMPARRAY buffer;
+
+ graphic->pixels = NULL;
+ graphic->width = graphic->height = 0;
+
+ info.err = jpeg_std_error(&(error.base));
+ error.base.error_exit = errorJPEGExit;
+ error.base.output_message = outputJPEGMessage;
+ if (setjmp(error.errorJump))
+ {
+ jpeg_destroy_decompress(&info);
+ if (graphic->pixels)
+ {
+ free(graphic->pixels);
+ graphic->pixels = NULL;
+ }
+ return 0;
+ }
+
+ jpeg_create_decompress(&info);
+
+ info.src = (struct jpeg_source_mgr *)(info.mem->alloc_small)
+ ((j_common_ptr)(&info),JPOOL_PERMANENT,sizeof(struct jpeg_source_mgr));
+ info.src->init_source = memJPEGInit;
+ info.src->fill_input_buffer = memJPEGFillInput;
+ info.src->skip_input_data = memJPEGSkipInput;
+ info.src->resync_to_restart = jpeg_resync_to_restart;
+ info.src->term_source = memJPEGTerm;
+ info.src->bytes_in_buffer = length;
+ info.src->next_input_byte = data;
+
+ jpeg_read_header(&info,TRUE);
+ jpeg_calc_output_dimensions(&info);
+ width = info.output_width;
+ height = info.output_height;
+
+ graphic->width = width;
+ graphic->height = height;
+ size = width*height*4;
+ graphic->pixels = (byte *)malloc(size);
+
+// graphic->m_header = new BITMAPINFOHEADER;
+// ::ZeroMemory(graphic->m_header,sizeof(BITMAPINFOHEADER));
+// graphic->m_header->biSize = sizeof(BITMAPINFOHEADER);
+// graphic->m_header->biWidth = width;
+// graphic->m_header->biHeight = height*-1;
+// graphic->m_header->biPlanes = 1;
+// graphic->m_header->biBitCount = 32;
+// graphic->m_header->biCompression = BI_RGB;
+// graphic->m_pixels = new BYTE[width*height*4];
+
+ // Force RGB output
+ info.out_color_space = JCS_RGB;
+
+ // Get an output buffer
+ buffer = (*info.mem->alloc_sarray)
+ ((j_common_ptr)&info,JPOOL_IMAGE,width*3,1);
+
+ jpeg_start_decompress(&info);
+ while ((int)info.output_scanline < height)
+ {
+ byte * pixelRow; int i;
+ jpeg_read_scanlines(&info,buffer,1);
+
+ pixelRow = graphic->pixels+
+ (width*(info.output_scanline-1)*4);
+ for (i = 0; i < width; i++)
+ {
+/* pixelRow[(i*4)+0] = (*buffer)[(i*3)+2];
+ pixelRow[(i*4)+1] = (*buffer)[(i*3)+1];
+ pixelRow[(i*4)+2] = (*buffer)[(i*3)+0];*/
+ pixelRow[(i*4)+0] = (*buffer)[(i*3)+0];
+ pixelRow[(i*4)+1] = (*buffer)[(i*3)+1];
+ pixelRow[(i*4)+2] = (*buffer)[(i*3)+2];
+ pixelRow[(i*4)+3] = 0xFF;
+ }
+ }
+ jpeg_finish_decompress(&info);
+ jpeg_destroy_decompress(&info);
+
+ return 1;
+ }
+
+/////////////////////////////////////////////////////////////////////////////
+// Loader for simple rectangles
+/////////////////////////////////////////////////////////////////////////////
+
+static int loadrect( byte *data, int length, sf_picture *graphic)
+ {
+ graphic->width = (data[0]<<24)|(data[1]<<16)|(data[2]<<8)|data[3];
+ graphic->height = (data[4]<<24)|(data[5]<<16)|(data[6]<<8)|data[7];
+ graphic->pixels = NULL;
+ return 1;
+ }
+
+///////////////////////////////
+
+// Get a picture from the Blorb resource map
+static int sf_loadpic( int picture, sf_picture *graphic)
+ {
+ myresource res;
+ int st = 0;
+
+ if (sf_getresource( picture, 1, bb_method_Memory,&res) == bb_err_None)
+ {
+ byte * data = (byte *)res.bbres.data.ptr;
+ int length = res.bbres.length;
+ unsigned int id = res.type;
+
+ // Look for a recognized format
+ if (id == bb_make_id('P','N','G',' '))
+ {
+ st = loadpng( data, length, graphic);
+ if (!st) st = loadjpeg( data, length, graphic);
+ }
+ else if (id == bb_make_id('J','P','E','G'))
+ {
+ st = loadjpeg( data, length, graphic);
+ if (!st) st = loadpng( data, length, graphic);
+ }
+ else if (id == bb_make_id('R','e','c','t'))
+ st = loadrect( data, length, graphic);
+ sf_freeresource(&res);
+ }
+
+ if (st) graphic->number = picture;
+ return st;
+ }
+
+////////////////////
+// CACHE
+
+#define MAXCACHE 1
+
+static sf_picture cached[MAXCACHE];
+static int cacheinited = 0;
+
+static void cacheflush()
+ {
+ int i;
+ if (!cacheinited) return;
+ for (i=0;i<MAXCACHE;i++)
+ {
+ cached[i].number = -1;
+ if (cached[i].pixels) free(cached[i].pixels);
+ cached[i].pixels = NULL;
+ }
+ cacheinited = 0;
+ }
+
+static void cacheinit(){
+ int i;
+ if (cacheinited) return;
+ CLEANREG(cacheflush);
+ for (i=0;i<MAXCACHE;i++)
+ {
+ cached[i].number = -1;
+ cached[i].pixels = NULL;
+ }
+ cacheinited = 1;
+ }
+
+static sf_picture * cachefind( int n){
+ int i;
+ for (i=0;i<MAXCACHE;i++)
+ if (cached[i].number == n) return (cached+i);
+ if (n < 0){
+ cached[0].number = -1;
+ if (cached[0].pixels) free(cached[0].pixels);
+ cached[0].pixels = NULL;
+ return (cached+0);
+ }
+ return NULL;
+ }
+
+sf_picture * sf_getpic( int num){
+ sf_picture *res;
+ cacheinit();
+ res = cachefind(num);
+ if (res) return res;
+ // not found, peek a slot
+ res = cachefind(-1);
+ if (sf_loadpic( num, res)) return res;
+ return NULL;
+ }
+
--- /dev/null
+#include "sf_frotz.h"
+
+const char * sf_msgstring( int id)
+ {
+ const char *p = "";
+ switch (id)
+ {
+ case IDS_BLORB_GLULX:
+ p = "unsupported Glulx code\n\nYou have attempted to load a Blorb file containing a Glulx game.\n"
+ "For this game you need a Glulx interpreter instead.";
+ break;
+ case IDS_BLORB_NOEXEC:
+ p = "no Z code\n\nYou have attempted to load a Blorb file that does not contain any\n"
+ "recognized game data. It may be a Blorb file containing just graphics or\n"
+ "sound data for a game, with the game in a separate file.\nCheck for a "
+ "file with the same name but an extension of .z5, .z6 or .z8\nand try "
+ "loading that into Frotz instead.";
+ break;
+ case IDS_MORE:
+ p = "[More]";
+ break;
+ case IDS_HIT_KEY_EXIT:
+ p = "[Hit any key to exit.]";
+ break;
+ case IDS_TITLE:
+ p = "SDL Frotz";
+ break;
+ case IDS_FATAL:
+ p = "Frotz Fatal Error";
+ break;
+ case IDS_FROTZ:
+ p = "Frotz";
+ break;
+ case IDS_FAIL_DIRECTSOUND:
+ p = "Failed to initialize DirectSound";
+ break;
+ case IDS_FAIL_MODPLUG:
+ p = "Failed to initialize MODPlug";
+ break;
+ case IDS_ABOUT_INFO:
+ p = "{\\rtf1\\ansi{\\b Windows Frotz 1.10, written by David Kinder.\\line Another fine product of the Frobozz Magic Z-code Interpreter Company.}{\\line\\super{ }\\par}Windows Frotz is released under the terms of the GNU General Public License. See the file COPYING that is included with this program for details.{\\line\\super{ }\\par}Windows Frotz copyright David Kinder 2002-2006.\\line Frotz copyright Stefan Jokisch 1995-1997.{\\line\\super{ }\\par}Frotz was written by Stefan Jokisch, with additions by Jim Dunleavy and David Griffith. Windows Frotz uses jpeglib by the Independent JPEG Group; libpng by Guy Eric Schalnat, Andreas Dilger, Glenn Randers-Pehrson, and others; zlib by Jean-loup Gailly and Mark Adler; ModPlug by Olivier Lapicque; and libogg and libvorbis by the Xiph.org Foundation.}";
+ break;
+ case IDS_SAVE_TITLE:
+ p = "Save the current game";
+ break;
+ case IDS_RESTORE_TITLE:
+ p = "Restore a saved game";
+ break;
+ case IDS_LOAD_AUX_TITLE:
+ p = "Load a portion of z-machine memory";
+ break;
+ case IDS_SAVE_AUX_TITLE:
+ p = "Save a portion of z-machine memory";
+ break;
+ case IDS_AUX_FILTER:
+ p = "*|Any file";
+ break;
+ case IDS_SAVE_FILTER:
+ p = "*.sav|Saved games";
+ break;
+ case IDS_RECORD_TITLE:
+ p = "Record input to a file";
+ break;
+ case IDS_PLAYBACK_TITLE:
+ p = "Play back recorded input";
+ break;
+ case IDS_RECORD_FILTER:
+ p = "*.rec|Record Files";
+ break;
+ case IDS_SCRIPT_TITLE:
+ p = "Write out a script";
+ break;
+ case IDS_SCRIPT_FILTER:
+ p = "*.log|Transcript Log Files";
+ break;
+ default:
+ break;
+ }
+
+ return p;
+ }
--- /dev/null
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <unistd.h>\r
+\r
+#define STATIC static\r
+\r
+#include "sf_frotz.h"\r
+\r
+typedef struct {\r
+ void *left, *right;\r
+ char *value;\r
+ } ENTRY;\r
+\r
+\r
+extern SFONT *sf_VGA_SFONT;\r
+\r
+#define FRAMECOLOR 222275\r
+\r
+static char buffer[512];\r
+static char lastdir[FILENAME_MAX] = "";\r
+static char filename[FILENAME_MAX];\r
+static char pattern[64];\r
+static zword pushed = 0;\r
+static int wentry;\r
+\r
+static ulong *sbuffer = NULL;\r
+static int sbpitch; // in longs\r
+static int ewidth, eheight;\r
+static int X,Y,W,H, xdlg,ydlg,wdlg,hdlg;\r
+\r
+#define HTEXT 18\r
+\r
+STATIC void cleanlist( ENTRY *t);\r
+STATIC void drawlist();\r
+STATIC ENTRY * dodir(\r
+ char *dirname, char *pattern, char *resdir, int size, int *ndirs, int *ntot);\r
+static int Numdirs, Numtot, First;\r
+static ENTRY *curdir = NULL, *selected;\r
+\r
+STATIC void updatelist()\r
+ {\r
+ if (curdir) cleanlist(curdir);\r
+ curdir = dodir(lastdir,pattern,lastdir,FILENAME_MAX,&Numdirs,&Numtot);\r
+ First = 0;\r
+ selected = NULL;\r
+ drawlist();\r
+ }\r
+\r
+STATIC void goright();\r
+STATIC void goleft();\r
+// assumes a / at end\r
+STATIC void goup()\r
+ {\r
+ char *p;\r
+ if (strlen(lastdir) < 2) return;\r
+ lastdir[strlen(lastdir)-1] = 0;\r
+ p = strrchr(lastdir,'/');\r
+ if (p){ p[1] = 0; updatelist();}\r
+ else strcat(lastdir,"/");\r
+ }\r
+\r
+typedef struct {\r
+ int x,y,w,h; // internal\r
+ zword (*click)(int,int);\r
+ ulong back;\r
+ int isbutton;\r
+ } BAREA;\r
+\r
+#define MAXBAREA 20\r
+static BAREA bareas[MAXBAREA];\r
+static int nbareas=0;\r
+static SF_textsetting *ts;\r
+\r
+#define BFRAME 2\r
+#define SPC 5\r
+\r
+#define WDLG (63*8)\r
+#define HDLG 208\r
+\r
+#define HCURSOR 8\r
+\r
+#define O_BLACK 0\r
+#define O_GRAY1 0x8a8a8a\r
+#define O_GRAY2 0xd6d6d6\r
+#define O_GRAY3 0xe2e2e2\r
+#define O_WHITE 0xf5f5f5\r
+\r
+STATIC void frame_upframe( int x, int y, int w, int h){\r
+ ulong v = O_WHITE;\r
+ sf_chline(x,y,v,w);\r
+ sf_cvline(x,y,v,--h);\r
+ v = O_BLACK;\r
+ sf_chline(x,y+h,v,w--);\r
+ sf_cvline(x+w--,y,v,h--);\r
+ x++; y++;\r
+ v = O_GRAY3;\r
+ sf_chline(x,y,v,w);\r
+ sf_cvline(x,y,v,--h);\r
+ v = O_GRAY1;\r
+ sf_chline(x,y+h,v,w--);\r
+ sf_cvline(x+w,y,v,h);\r
+ }\r
+\r
+STATIC void frame_downframe( int x, int y, int w, int h){\r
+ ulong v = O_BLACK;\r
+ sf_chline(x,y,v,w);\r
+ sf_cvline(x,y,v,--h);\r
+ v = O_WHITE;\r
+ sf_chline(x,y+h,v,w--);\r
+ sf_cvline(x+w--,y,v,h--);\r
+ x++; y++;\r
+ v = O_GRAY1;\r
+ sf_chline(x,y,v,w);\r
+ sf_cvline(x,y,v,--h);\r
+ v = O_GRAY3;\r
+ sf_chline(x,y+h,v,w--);\r
+ sf_cvline(x+w,y,v,h);\r
+ }\r
+\r
+// internal coords\r
+STATIC int addarea( int x, int y, int w, int h, zword (*click)(int,int))\r
+ {\r
+ BAREA *a = bareas+nbareas;\r
+ a->x = x; a->y = y; a->w = w; a->h = h; a->click = click;\r
+ a->back = O_GRAY2;\r
+ return nbareas++;\r
+ }\r
+\r
+STATIC void clarea( int n)\r
+ {\r
+ BAREA *a = bareas+n;\r
+ sf_fillrect(a->back,a->x,a->y,a->w,a->h);\r
+ }\r
+\r
+STATIC void writetext( ulong color, const char *s, int x, int y, int w, int center)\r
+ {\r
+ int ox,oy,ow,oh;\r
+//printf("W %p [%s]\n",s,s ? s : "??");\r
+ if (!s) return;\r
+ if (!s[0]) return;\r
+ sf_getclip(&ox,&oy,&ow,&oh);\r
+ sf_setclip(x,y,w,HTEXT);\r
+//printf("1\n");\r
+ if (center)\r
+ {\r
+ int wt = 8*strlen(s);\r
+ x += (w-wt)/2;\r
+ }\r
+//printf("2 ts %p\n",ts); fflush(stdout); if (ts < 1000){sf_flushdisplay(); getchar();}\r
+ ts->cx = x;\r
+ ts->cy = y;\r
+ ts->fore = color;\r
+//printf("3\n"); fflush(stdout);\r
+ while (*s) sf_writeglyph(ts->font->getglyph(ts->font,(*s++),1));\r
+//printf("4\n");\r
+ sf_setclip(ox,oy,ow,oh);\r
+//printf("5\n");\r
+ }\r
+\r
+STATIC int addbutton( int x, int y, int w, int h, char *text, zword (*click)(int,int))\r
+ {\r
+ int b = addarea(x,y,w,h,click);\r
+ bareas[b].isbutton = 1;\r
+ frame_upframe(x-2,y-2,w+4,h+4);\r
+ clarea(b);\r
+ if (text) writetext(0,text,x,y,w,1);\r
+ return b;\r
+ }\r
+\r
+static int B_up, B_ok, B_cancel;\r
+static int A_dir, A_filter, A_entry, A_list;\r
+\r
+#define BUTTW 60\r
+\r
+STATIC void showfilename( int pos)\r
+ {\r
+ BAREA *a = bareas+A_entry;\r
+ clarea(A_entry);\r
+ writetext(0,filename,a->x,a->y,a->w,0);\r
+ if (pos >= 0)\r
+ sf_cvline(a->x+8*pos,a->y,O_BLACK,HTEXT);\r
+ }\r
+\r
+STATIC void clicked( BAREA *a)\r
+ {\r
+ frame_downframe(a->x-2,a->y-2,a->w+4,a->h+4);\r
+ sf_flushdisplay();\r
+ sf_sleep(100);\r
+ frame_upframe(a->x-2,a->y-2,a->w+4,a->h+4);\r
+ sf_flushdisplay();\r
+ }\r
+\r
+STATIC zword checkmouse( int i0)\r
+ {\r
+ int x = mouse_x-1, y = mouse_y-1;\r
+ int i;\r
+ for (i=i0;i<nbareas;i++)\r
+ {\r
+ BAREA *a = bareas+i;\r
+ if (x > a->x && x < a->x+a->w && y > a->y && y < a->y+a->h)\r
+ {\r
+ if (a->click)\r
+ {\r
+ if (a->isbutton) clicked(a);\r
+ return a->click(x-a->x,y-a->y);\r
+ }\r
+ else return 0;\r
+ }\r
+ }\r
+ return 0;\r
+ }\r
+\r
+STATIC zword Zup( int x, int y)\r
+ {\r
+ goup();\r
+ return 0;\r
+ }\r
+\r
+STATIC zword Zok( int x, int y)\r
+ {\r
+ return ZC_RETURN;\r
+ }\r
+\r
+STATIC zword Zcanc( int x, int y)\r
+ {\r
+ return ZC_ESCAPE;\r
+ }\r
+\r
+STATIC zword Zselect( int x, int y);\r
+STATIC zword yesnoover( int xc, int yc);\r
+STATIC zword Zentry( int x, int y);\r
+\r
+STATIC zword inputkey()\r
+ {\r
+ zword c = sf_read_key(0,0,1);\r
+ if (c == ZC_SINGLE_CLICK)\r
+ {\r
+ switch (mouse_button)\r
+ {\r
+ case 4: c = ZC_ARROW_LEFT; break;\r
+ case 5: c = ZC_ARROW_RIGHT; break;\r
+ case 1: break;\r
+ default: c = 0; break;\r
+ }\r
+ }\r
+// if (os_read_mouse() != 1) c = 0;\r
+ return c;\r
+ }\r
+\r
+int (*sf_sysdialog)( bool existing, const char *def, const char *filt, const char *tit, char **res) = NULL;\r
+\r
+STATIC int myosdialog( bool existing, const char *def, const char *filt, const char *tit, char **res, ulong *sbuf, int sbp, int ew, int eh, int isfull)\r
+ {\r
+ char *pp; ulong *saved; int y0, y1, y2, x1;\r
+ zword c = 0;\r
+\r
+ // allow system-specific dialog if not fullscreen\r
+ if (isfull == 0) if (sf_sysdialog)\r
+ return sf_sysdialog(existing,def,filt,tit,res);\r
+\r
+ ts = sf_curtextsetting();\r
+ if (!ts) return SF_NOTIMP;\r
+\r
+//printf("0 ts %p (%p)\n",ts,&ts);\r
+\r
+ if (!def) def = "";\r
+ strcpy(filename,def);\r
+ pp = strrchr(filename,'/');\r
+ if (pp)\r
+ {\r
+ *pp = 0;\r
+ strcpy(lastdir,filename);\r
+ strcpy(filename,pp+1);\r
+ }\r
+\r
+ if (!filt) filt = "*|All files";\r
+\r
+ if (!lastdir[0]) strcpy(lastdir,"./");\r
+\r
+ strcpy(buffer,filt);\r
+ pp = strchr(buffer,'|'); if (pp) *pp = 0;\r
+ strcpy(pattern,buffer);\r
+\r
+ ewidth = ew;\r
+ eheight = eh;\r
+ sbuffer = sbuf;\r
+ sbpitch = sbp;\r
+\r
+ wdlg = WDLG;\r
+ hdlg = HDLG;\r
+\r
+ nbareas = 0;\r
+\r
+ W = WDLG+4*BFRAME+2*SPC;\r
+ H = HDLG+4*BFRAME+6*SPC+6*BFRAME+3*(HTEXT+2)+HCURSOR+HTEXT;\r
+\r
+ if (W > ew) return SF_NOTIMP;\r
+ if (H > eh) return SF_NOTIMP;\r
+\r
+ X = (ew-W)/2;\r
+ Y = (eh-H)/2;\r
+\r
+ // internal!!\r
+ xdlg = X+SPC+2*BFRAME;\r
+ ydlg = Y+2*SPC+4*BFRAME+HTEXT+HTEXT;\r
+\r
+ wentry = wdlg - BUTTW - SPC - 2*BFRAME;\r
+\r
+ saved = sf_savearea(X,Y,W,H);\r
+ if (!saved) return SF_NOTIMP;\r
+\r
+//printf("saved: %p %d %d %d %d\n",saved,saved[0],saved[1],saved[2],saved[3]);\r
+ sf_pushtextsettings();\r
+ ts->font = sf_VGA_SFONT;\r
+ ts->style = 0;\r
+ ts->oh = 0;\r
+ ts->fore = 0;\r
+ ts->backTransparent = 1;\r
+\r
+ sf_fillrect(O_GRAY2,X,Y,W,H);\r
+// frame_upframe(X,Y,W,H);\r
+ sf_rect(FRAMECOLOR,X,Y,W,H);\r
+ sf_rect(FRAMECOLOR,X+1,Y+1,W-2,H-2);\r
+ sf_fillrect(FRAMECOLOR,X,Y+2,W,HTEXT);\r
+ if (tit) writetext(O_WHITE,tit,X+2+SPC,Y+2,W-4,0);\r
+ A_list = addarea(xdlg,ydlg,wdlg,hdlg,Zselect);\r
+ bareas[A_list].back = O_WHITE;\r
+ clarea(A_list);\r
+ frame_downframe(xdlg-2,ydlg-2,wdlg+4,hdlg+4);\r
+\r
+ y0 = Y+SPC+2*BFRAME+HTEXT;\r
+ y2 = Y+H-SPC-2*BFRAME-HTEXT;\r
+ y1 = y2-SPC-HTEXT-2*BFRAME;\r
+ x1 = xdlg+wentry+2*BFRAME+SPC;\r
+\r
+ A_dir = addarea(xdlg,y0,wentry,HTEXT,NULL);\r
+ A_entry = addarea(xdlg,y1,wentry,HTEXT,Zentry);\r
+ bareas[A_entry].back = O_WHITE;\r
+ clarea(A_entry);\r
+ frame_downframe(xdlg-2,y1-2,wentry+4,HTEXT+4);\r
+ B_up = addbutton(x1,y0,BUTTW,HTEXT,"^up^",Zup);\r
+ A_filter = addarea(xdlg,y2,wentry,HTEXT,NULL);\r
+ strcpy(buffer,"Filter: ");\r
+ strcat(buffer,filt);\r
+ writetext(0,buffer,xdlg,y2,wentry,0);\r
+ B_cancel = addbutton(x1,y2,BUTTW,HTEXT,"Cancel",Zcanc);\r
+ B_ok = addbutton(x1,y1,BUTTW,HTEXT,"OK",Zok);\r
+\r
+ showfilename(-1);\r
+ updatelist();\r
+\r
+ for (;;)\r
+ {\r
+ if (pushed) { c = pushed; pushed = 0;}\r
+ else c = inputkey();\r
+ if (c == ZC_SINGLE_CLICK) c = checkmouse(0);\r
+ if (c == VK_INS) c = Zentry(0,-1);\r
+ if (c == ZC_ARROW_LEFT) goleft();\r
+ if (c == ZC_ARROW_RIGHT) goright();\r
+ if (c == ZC_ESCAPE) break;\r
+ if (c == ZC_RETURN)\r
+ {\r
+ strcpy(buffer,lastdir);\r
+ strcat(buffer,filename);\r
+ *res = buffer;\r
+ if ((existing==0) && (access(buffer,F_OK)==0))\r
+ c = yesnoover(xdlg+wdlg/2,ydlg+hdlg/2);\r
+ if (c == ZC_RETURN) break;\r
+ }\r
+ }\r
+\r
+ sf_poptextsettings();\r
+\r
+ cleanlist(curdir); curdir = NULL;\r
+\r
+//printf("2saved: %p %d %d %d %d\n",saved,saved[0],saved[1],saved[2],saved[3]);\r
+ sf_restoreareaandfree(saved);\r
+\r
+ if (c == ZC_ESCAPE) return -1;\r
+\r
+ if (c == ZC_RETURN)\r
+ {\r
+ strcpy(buffer,lastdir);\r
+ strcat(buffer,filename);\r
+ *res = buffer;\r
+ return 0;\r
+ }\r
+\r
+ return SF_NOTIMP;\r
+ }\r
+\r
+static void setdialog(void) __attribute__((constructor));\r
+static void setdialog(void)\r
+ {\r
+ sf_osdialog = myosdialog;\r
+ }\r
+\r
+///////////////////////////////////\r
+\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#ifdef WIN32\r
+#define strcasecmp stricmp\r
+#else\r
+#include <strings.h>\r
+#endif\r
+#include <unistd.h>\r
+#include <dirent.h>\r
+//#include <fnmatch.h>\r
+#include <sys/stat.h>\r
+\r
+// simplified fnmatch - only allows a single * at beginning\r
+STATIC int myfnmatch( const char *pattern, const char *p, int dummy)\r
+ {\r
+ int lpat, lp;\r
+ if (!pattern) return -1;\r
+ if (!p) return -1;\r
+ if (pattern[0] != '*') return strcmp(pattern,p);\r
+ lpat = strlen(pattern);\r
+ if (lpat == 1) return 0; // * matches anything\r
+ lpat--; pattern++;\r
+ lp = strlen(p);\r
+ if (lp < lpat) return 1; // too short\r
+ return strcmp(pattern,p+lp-lpat);\r
+ }\r
+\r
+STATIC void cleanlist( ENTRY *t)\r
+ {\r
+ while (t)\r
+ {\r
+ ENTRY *n = t->right;\r
+ if (t->value) free(t->value);\r
+ free(t);\r
+ t = n;\r
+ }\r
+ }\r
+\r
+STATIC ENTRY * newentry( char *s)\r
+ {\r
+ ENTRY *r = calloc(1,sizeof(ENTRY));\r
+\r
+ if (r){\r
+ r->value = strdup(s);\r
+ if (!r->value){ free(r); return NULL;}\r
+ }\r
+ return r;\r
+ }\r
+\r
+STATIC void addentry( char *s, ENTRY **ae)\r
+ {\r
+ ENTRY *t = *ae;\r
+ if (!t)\r
+ {\r
+ *ae = newentry(s);\r
+ return;\r
+ }\r
+ for (;;)\r
+ {\r
+ int k = strcasecmp(s,t->value);\r
+ if (!k) return;\r
+ if (k > 0)\r
+ {\r
+ if (t->right) t = t->right;\r
+ else\r
+ {\r
+ t->right = newentry(s);\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (t->left) t = t->left;\r
+ else\r
+ {\r
+ t->left = newentry(s);\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+STATIC char *resolvedir( char *dir, char *res, int size)\r
+ {\r
+ char cwd[FILENAME_MAX], *p; int i;\r
+ if (!getcwd(cwd,FILENAME_MAX)) return NULL;\r
+ if (chdir(dir)) return NULL;\r
+ p = getcwd(res,size);\r
+ for (i=0;p[i];i++) if (p[i]=='\\') p[i] = '/';\r
+ chdir(cwd);\r
+ if (p)\r
+ {\r
+ int n = strlen(p);\r
+ if (n) if (p[n-1] != '/') { p[n] = '/'; p[n+1] = 0;}\r
+ }\r
+ return p;\r
+ }\r
+\r
+STATIC ENTRY * dodir(\r
+ char *dirname, char *pattern, char *resdir, int size, int *ndirs, int *ntot)\r
+ {\r
+ DIR *dir;\r
+ ENTRY *dirs = NULL;\r
+ ENTRY *files = NULL, *res = NULL;\r
+ struct dirent *d;\r
+ char *p, *resdend;\r
+ struct stat fst;\r
+ int n;\r
+\r
+ void exhaust( ENTRY *e)\r
+ {\r
+ if (!e) return;\r
+ exhaust(e->left);\r
+ e->left = res;\r
+ res = e;\r
+ n++;\r
+ exhaust(e->right);\r
+ }\r
+\r
+//printf("\ndodir\n");\r
+ if (!resolvedir(dirname,resdir,size)) return NULL;\r
+ resdend = resdir+strlen(resdir);\r
+\r
+//printf("[%s]\n",resdir);\r
+ // MinGW opendir() does not like the final slash\r
+#ifdef WIN32\r
+ n = strlen(resdir);\r
+ if (n > 2 && (resdir[n-2] != ':'))\r
+ resdir[n-1] = 0;\r
+ dir = opendir(resdir);\r
+ resdir[n-1] = '/';\r
+#else\r
+ dir = opendir(resdir);\r
+#endif\r
+ if (!dir) return NULL;\r
+\r
+//printf("opened [%s]\n",resdir);\r
+ for (;;)\r
+ {\r
+ d = readdir(dir);\r
+ if (!d) break;\r
+ p = d->d_name;\r
+ if (strcmp(p,".")==0) continue;\r
+ if (strcmp(p,"..")==0) continue;\r
+ strcpy(resdend,p);\r
+//printf("-%s\n",resdir);\r
+ if (stat(resdir,&fst)) continue;\r
+//printf("--mode %x\n",fst.st_mode);\r
+ if (fst.st_mode & S_IFDIR)\r
+ addentry(p,&dirs);\r
+ else\r
+ {\r
+//printf("--fnmatch: %d\n",fnmatch(pattern,p,0));\r
+ if (myfnmatch(pattern,p,0)==0) addentry(p,&files);\r
+ }\r
+ }\r
+\r
+ closedir(dir);\r
+ *resdend = 0;\r
+\r
+ n = 0;\r
+ exhaust(dirs);\r
+ *ndirs = n;\r
+ exhaust(files);\r
+ *ntot = n;\r
+\r
+ if (res)\r
+ while (res->left)\r
+ {\r
+ ((ENTRY *)(res->left))->right = res;\r
+ res = res->left;\r
+ }\r
+\r
+ return res;\r
+ }\r
+\r
+\r
+//////////////////////////////////////////////\r
+// white,black,gray,yellow\r
+static ulong bcolors[4] = {0xfcfcfc,0,0xa0a0a0,0xa0d0e0};\r
+\r
+static unsigned char folderbmp[] = {\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,1,1,1,1,1,1,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,1,3,3,3,3,3,3,\r
+ 1,1,1,1,1,1,0,0,\r
+ 0,1,3,3,3,3,3,3,\r
+ 3,3,3,3,3,3,1,0,\r
+ 0,1,3,3,3,3,3,3,\r
+ 3,1,1,1,1,1,1,0,\r
+ 0,1,3,3,1,1,1,1,\r
+ 1,3,3,3,3,3,1,0,\r
+ 0,1,3,1,3,3,3,3,\r
+ 3,3,3,3,3,3,1,0,\r
+ 0,1,3,1,3,3,3,3,\r
+ 3,3,3,3,3,3,1,0,\r
+ 0,1,3,1,3,3,3,3,\r
+ 3,3,3,3,3,3,1,0,\r
+ 0,1,3,1,3,3,3,3,\r
+ 3,3,3,3,3,3,1,0,\r
+ 0,1,3,1,3,3,3,3,\r
+ 3,3,3,3,3,3,1,0,\r
+ 0,1,3,1,3,3,3,3,\r
+ 3,3,3,3,3,3,1,0,\r
+ 0,0,1,1,1,1,1,1,\r
+ 1,1,1,1,1,1,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0};\r
+\r
+\r
+static unsigned char docbmp[] = {\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,1,1,1,1,1,1,\r
+ 1,1,1,1,1,1,0,0,\r
+ 0,1,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,1,0,\r
+ 0,1,0,2,2,2,2,2,\r
+ 2,2,2,2,2,0,1,0,\r
+ 0,1,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,1,0,\r
+ 0,1,0,2,2,2,2,2,\r
+ 2,2,2,2,2,0,1,0,\r
+ 0,1,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,1,0,\r
+ 0,1,0,2,2,2,2,2,\r
+ 2,2,2,2,2,0,1,0,\r
+ 0,1,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,1,0,\r
+ 0,1,0,2,2,2,2,2,\r
+ 2,2,2,2,2,0,1,0,\r
+ 0,1,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,1,0,\r
+ 0,1,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,1,0,\r
+ 0,0,1,1,1,1,1,1,\r
+ 1,1,1,1,1,1,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0,\r
+ 0,0,0,0,0,0,0,0};\r
+\r
+////////////////////////////////\r
+\r
+STATIC void drawit( int x, int y, ENTRY *e, int w, int issub)\r
+ {\r
+ int i,j,n,sw,dy,color;\r
+ unsigned char *bmp;\r
+ char *s = e->value;\r
+ bmp = (issub ? folderbmp : docbmp);\r
+ for (i=0;i<16;i++) for (j=0;j<16;j++) sf_wpixel(x+j,y+i,bcolors[*bmp++]);\r
+ x += 17;\r
+ w -= 17;\r
+ n = w/8;\r
+ if (n < 1) return;\r
+ if (strlen(s) > n)\r
+ {\r
+ strcpy(buffer,s);\r
+ buffer[n] = 0;\r
+ buffer[n-1] = '>';\r
+ s = buffer;\r
+ }\r
+ if (e == selected)\r
+ {\r
+ color = O_WHITE;\r
+ sf_fillrect(0,x,y,w,16);\r
+ }\r
+ else\r
+ color = O_BLACK;\r
+ writetext(color,s,x,y,w,0);\r
+ }\r
+\r
+static int Nrows, Ncols, Ewid, Fh;\r
+\r
+STATIC void drawnames( int x, int y, int w, int h, ENTRY *files, int first, int nsub, int ntot, int ewid)\r
+ {\r
+ int i;\r
+\r
+ Fh = 16;\r
+ Ewid = ewid;\r
+ Ncols = w/ewid;\r
+ Nrows = h/Fh;\r
+\r
+ sf_fillrect(O_WHITE,x,y,w,h);\r
+ if (!files) return;\r
+ if (first < 0) return;\r
+ if (nsub > ntot) nsub = ntot;\r
+ while (first > 0)\r
+ {\r
+ files = files->right;\r
+ if (!files) return;\r
+ nsub--;\r
+ ntot--;\r
+ first--;\r
+ }\r
+ if (ntot <= 0) return;\r
+ if (Ncols < 1) return;\r
+ if (Nrows < 1) return;\r
+ if (Nrows*Ncols < ntot) ntot = Nrows*Ncols;\r
+ for (i=0;i<ntot;i++)\r
+ {\r
+ drawit(x+ewid*(i/Nrows),y+Fh*(i % Nrows),files,ewid,i < nsub);\r
+ files = files->right;\r
+ }\r
+ }\r
+\r
+STATIC void drawlist()\r
+ {\r
+ BAREA *a = bareas+A_list, *b = bareas+A_dir;\r
+\r
+ clarea(A_dir);\r
+ writetext(0,lastdir,b->x,b->y,b->w,0);\r
+ drawnames(a->x,a->y,a->w,a->h,curdir,First,Numdirs,Numtot,21*8);\r
+\r
+ }\r
+\r
+STATIC void goright()\r
+ {\r
+ if (First+Nrows*Ncols > Numtot) return;\r
+ First += Nrows;\r
+ drawlist();\r
+ }\r
+\r
+STATIC void goleft()\r
+ {\r
+ if (!First) return;\r
+ First -= Nrows;\r
+ drawlist();\r
+ }\r
+\r
+STATIC ENTRY *filesat( int n){\r
+ ENTRY *e = curdir;\r
+ while (n--)\r
+ {\r
+ if (e) e = e->right;\r
+ }\r
+ return e;\r
+ }\r
+\r
+STATIC zword Zselect( int x, int y)\r
+ {\r
+ int n;\r
+ x /= Ewid;\r
+ y /= Fh;\r
+ n = First + y + x*Nrows;\r
+ if (n >= Numtot)\r
+ {\r
+ if (selected)\r
+ {\r
+ selected = NULL;\r
+ drawlist();\r
+ }\r
+ return 0;\r
+ }\r
+ if (n < Numdirs)\r
+ {\r
+ ENTRY *e = filesat(n);\r
+ if (!e) return 0;\r
+ strcat(lastdir,e->value);\r
+ updatelist();\r
+ return 0;\r
+ }\r
+ selected = curdir;\r
+ while (n--) selected = selected->right;\r
+ strcpy(filename,selected->value);\r
+ showfilename(-1);\r
+ drawlist();\r
+ return 0;\r
+ }\r
+\r
+extern void sf_videodata( ulong **sb, int *sp, int *ew, int *eh);\r
+zword sf_yesnooverlay( int xc, int yc, char *t, int saverest)\r
+ {\r
+ zword c = ZC_RETURN;\r
+ int nsav = nbareas;\r
+ ulong *saved = NULL;\r
+ int hx = BUTTW+3*SPC, hy = HTEXT+2*SPC, heff;\r
+\r
+ heff = 8*strlen(t);\r
+ if (heff > 2*hx) hx = (heff+3)/2;\r
+ if (saverest)\r
+ {\r
+ ts = sf_curtextsetting();\r
+ if (!ts) return ZC_ESCAPE;\r
+ saved = sf_savearea(xc-hx-2,yc-hy-2,2*hx+4,2*hy+4);\r
+ if (!saved) return ZC_ESCAPE;\r
+ sf_pushtextsettings();\r
+ ts->font = sf_VGA_SFONT;\r
+ ts->style = 0;\r
+ ts->oh = 0;\r
+ ts->fore = 0;\r
+ ts->backTransparent = 1;\r
+ sf_videodata(&sbuffer, &sbpitch, &ewidth, &eheight);\r
+ }\r
+\r
+ sf_fillrect(FRAMECOLOR,xc-hx-2,yc-hy-2,2*hx+4,2*hy+4);\r
+ sf_fillrect(O_WHITE,xc-hx,yc-hy,2*hx,2*hy);\r
+ writetext(O_BLACK,t,xc-hx,yc-SPC-HTEXT,2*hx,1);\r
+ addbutton(xc-SPC-BUTTW,yc+SPC,BUTTW,HTEXT,"Yes",Zok);\r
+ addbutton(xc+SPC,yc+SPC,BUTTW,HTEXT,"No",Zcanc);\r
+ for (;;)\r
+ {\r
+ c = inputkey();\r
+ if (c == 'n' || c == 'N') c = ZC_ESCAPE;\r
+ if (c == 'y' || c == 'Y') c = ZC_RETURN;\r
+ if (c == ZC_SINGLE_CLICK) c = checkmouse(nsav);\r
+ if (c == ZC_ESCAPE) break;\r
+ if (c == ZC_RETURN) break;\r
+ }\r
+\r
+ if (saved)\r
+ {\r
+ sf_restoreareaandfree(saved);\r
+ sf_poptextsettings();\r
+ }\r
+\r
+ nbareas = nsav;\r
+ return c;\r
+ }\r
+\r
+STATIC zword yesnoover( int xc, int yc)\r
+ {\r
+ zword c;\r
+\r
+ c = sf_yesnooverlay(xc,yc,"Overwrite file?",0);\r
+\r
+ drawlist();\r
+ return c;\r
+ }\r
+\r
+// this is needed for overlapping source and dest in Zentry\r
+// (lib does not guarantee correct behaviour in that case)\r
+static void mystrcpy( char *d, const char *s)\r
+ {\r
+ while (*d++ = *s++);\r
+ }\r
+\r
+STATIC zword Zentry( int x, int y)\r
+ {\r
+ static int pos = 10000;\r
+ int i,n,nmax; zword c;\r
+\r
+ nmax = wentry/8;\r
+ if (nmax >= FILENAME_MAX) nmax = FILENAME_MAX-1;\r
+ n = strlen(filename);\r
+ if (n > nmax) { n = nmax; filename[n] = 0;}\r
+ if (y >= 0)\r
+ {\r
+ pos = x/4-1; if (pos < 0) pos = 0;\r
+ pos /= 2;\r
+ }\r
+ if (pos > n) pos = n;\r
+ showfilename(pos);\r
+ for (;;)\r
+ {\r
+ c = inputkey();\r
+ if (c == ZC_SINGLE_CLICK)\r
+ {\r
+ pushed = c;\r
+ c = 0;\r
+ break;\r
+ }\r
+ if (c == ZC_ESCAPE || c == VK_INS) { c = 0; break; }\r
+ if (c == ZC_RETURN) break;\r
+ if (c == ZC_ARROW_LEFT)\r
+ {\r
+ if (pos){ pos--; showfilename(pos); }\r
+ continue;\r
+ }\r
+ if (c == ZC_ARROW_RIGHT)\r
+ {\r
+ if (pos < n){ pos++; showfilename(pos); }\r
+ continue;\r
+ }\r
+ if (c == ZC_BACKSPACE)\r
+ {\r
+ if (pos)\r
+ {\r
+ // needs mystrcpy() because overlapping src-dst\r
+ if (pos < n) mystrcpy(filename+pos-1,filename+pos);\r
+ n--;\r
+ filename[n] = 0;\r
+ pos--;\r
+ showfilename(pos);\r
+ }\r
+ continue;\r
+ }\r
+ if ((c >= 32 && c < 127) || (c >= 160 && c < 256))\r
+ {\r
+ if (n >= nmax) continue;\r
+ if (n > pos)\r
+ for (i=n;i>pos;i--) filename[i] = filename[i-1];\r
+ filename[pos] = c;\r
+ n++;\r
+ filename[n] = 0;\r
+ pos++;\r
+ showfilename(pos);\r
+ }\r
+ }\r
+ showfilename(-1);\r
+ return c;\r
+ }\r
+\r
+\r
--- /dev/null
+#include <math.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sf_frotz.h"
+
+#include "samplerate.h"
+
+static int myconv( CONV *conv, FILE *f, void *dest, int nin, int eod)
+ {
+ int nbrd, i, j, v=0;
+ float *sdata;
+ unsigned char c1, c2; char c;
+ SRC_DATA src;
+ sdata = src.data_in = conv->inbuf; src.data_out = conv->outbuf;
+ src.input_frames = nin; src.output_frames = conv->maxout;
+ src.src_ratio = conv->ratio;
+ src.end_of_input = eod;
+ // load input data
+ nbrd = conv->bytespersam;
+ switch (conv->bytespersam)
+ {
+ case 1:
+ for (i=0;i<nin*conv->channels;i++)
+ {
+ c = fgetc(f);
+ *sdata++ = (((short)c) << 8)/32768.0;
+ }
+ break;
+ case 2:
+ for (i=0;i<nin*conv->channels;i++)
+ {
+ c1 = fgetc(f);
+ c2 = fgetc(f);
+ *sdata++ = ((short)(((unsigned short)c1) << 8 + (unsigned short)c2))
+ / 32768.0;
+ }
+ }
+ // do conversion
+ src_process ((SRC_STATE *)conv->aux, &src);
+ // save output
+ nbrd = src.output_frames_gen;
+ src_float_to_short_array (conv->outbuf, (short *)dest, nbrd*conv->channels) ;
+
+ return nbrd;
+ }
+
+static void finish( CONV *conv)
+ {
+ if (conv->inbuf) free(conv->inbuf);
+ if (conv->outbuf) free(conv->outbuf);
+ if (conv->aux) src_delete(conv->aux);
+ conv->inbuf = conv->outbuf = NULL;
+ conv->aux = NULL;
+ }
+
+extern int (*sfx_exinitconv)( CONV *conv);
+
+static int my_exinitconv( CONV *conv)
+ {
+ int err;
+ if (!conv) return 0;
+ conv->inbuf = malloc(conv->maxin*conv->channels*sizeof(float));
+ if (!conv->inbuf) return 0;
+ conv->outbuf = malloc(conv->maxout*conv->channels*sizeof(float));
+ if (!conv->outbuf) { free(conv->inbuf); return 0;}
+ conv->aux = src_new(SRC_SINC_FASTEST,conv->channels,&err);
+ if (!conv->aux){ finish(conv); return 0;}
+ conv->finishCONV = finish;
+ conv->doCONV = myconv;
+ return 1;
+ }
+
+static void MyInitFunc(void) __attribute__ ((constructor));
+static void MyInitFunc(void)
+ {
+ sfx_exinitconv = my_exinitconv;
+ }
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// for access()
+#include <unistd.h>
+
+#include "sf_frotz.h"
+
+#include "blorb.h"
+#include "blorblow.h"
+
+// various data
+
+bool m_tandy = 0;
+bool m_quetzal = 1;
+// CRect m_wndSize;
+// CString m_propFontName;
+// CString m_fixedFontName;
+// int m_fontSize;
+int m_v6scale;
+int m_gfxScale = 1;
+ulong m_defaultFore;
+ulong m_defaultBack;
+ulong m_colours[11];
+ulong m_nonStdColours[NON_STD_COLS];
+int m_nonStdIndex;
+bool m_exitPause = 0;
+bool m_lineInput = 0;
+bool m_IsInfocomV6 = false;
+// bool m_fastScroll;
+bool m_morePrompts = 1;
+// int m_leftMargin;
+// int m_rightMargin;
+// FILE* m_blorbFile;
+// bb_map_t* m_blorbMap;
+// GameInfo m_gameInfo;
+bool m_localfiles = false;
+char * m_fontdir = NULL;
+bool m_aafonts = 0;
+char m_names_format = 0;
+char * m_reslist_file = NULL;
+char * m_setupfile = ".sfrotzrc";
+extern int m_frequency;
+
+static int countedpics = 0;
+static int maxlegalpic = 0;
+static int releaseno = 0;
+
+static char *ResDir = "./";
+static char *ResPict = "PIC%d";
+static char *ResSnd = "SND%d";
+
+int AcWidth=640, AcHeight=400;
+int option_scrollback_buffer = 0;
+
+static FILE *bfile = NULL;
+static FILE *zfile = NULL;
+static bb_map_t *bmap = NULL;
+static long zoffset;
+static long zsize;
+static int zcodeinblorb = 0;
+
+static void checkwidths()
+ {
+ bb_resolution_t *reso;
+ reso = bb_get_resolution(bmap);
+ if (reso)
+ {
+//printf("get_resolution: %dx%d\n",reso->px,reso->py);
+ // ignore small resolution hints
+ if ((reso->px) && (reso->px >= AcWidth)) AcWidth = reso->px;
+ if ((reso->py) && (reso->py >= AcHeight)) AcHeight = reso->py;
+ }
+ }
+
+static int tryloadblorb( char *bfn)
+ {
+ bb_err_t err;
+ bfile = fopen(bfn,"rb");
+ if (!bfile) return -1;
+ err = bb_create_map(bfile,&bmap);
+ if (err){
+ bmap = NULL;
+ fclose(bfile);
+ bfile = NULL;
+ return err;
+ }
+ return 0;
+ }
+
+static void sf_cleanup_resources()
+ {
+ if (zfile) fclose(zfile);
+ zfile = NULL;
+ if (bmap) bb_destroy_map(bmap);
+ bmap = NULL;
+ if (bfile) fclose(bfile);
+ bfile = NULL;
+ }
+
+static int checkfile( char *fn, char *ofn)
+ {
+ char *p, *bp, lastch;
+
+ if (access(fn,F_OK)==0){
+ strcpy(ofn,fn);
+ return 0;
+ }
+
+ // if qualified path, don't do anything else
+ if ( (strchr(fn,'\\')) || (strchr(fn,'/')) )
+ return -1;
+
+ if ((p = getenv ("ZCODE_PATH")) == NULL)
+ p = getenv ("INFOCOM_PATH");
+ if (p != NULL) {
+ while (*p) {
+ bp = ofn;
+ while (*p && *p != OS_PATHSEP)
+ lastch = *bp++ = *p++;
+ if (lastch != '\\' && lastch != '/')
+ *bp++ = '\\';
+ strcpy (bp, fn);
+ if (access(ofn,F_OK)==0)
+ return 0;
+ if (*p)
+ p++;
+ }
+ }
+
+ return -1;
+ }
+
+// must be called as soon as possible (i.e. by os_process_arguments())
+static int load_resources( char *givenfn)
+ {
+
+ char buf[FILENAME_MAX + 1], *p;
+ int st;
+ FILE *f;
+ unsigned char hd2[2];
+
+ CLEANREG(sf_cleanup_resources);
+
+ // first check whether file exists
+ st = checkfile(givenfn, buf);
+ if (st) os_fatal( "File not found");
+
+ // check whether it is a story file
+ f = fopen( buf, "rb");
+ if (!f) os_fatal( "File open failed");
+
+ fread(hd2,1,2,f);
+
+ // blorb file ?
+ if (hd2[0] == 'F' && hd2[1] == 'O'){
+ bb_result_t result;
+ fclose(f);
+ if (tryloadblorb(buf))
+ os_fatal("File is neither Zcode nor Blorb");
+ // Look for an executable chunk
+ if (bb_load_resource(bmap,bb_method_FilePos,&result,bb_ID_Exec,0)
+ == bb_err_None)
+ {
+ unsigned int id = bmap->chunks[result.chunknum].type;
+ if (id == bb_make_id('Z','C','O','D'))
+ {
+ // If this is a Z-code game, set the file pointer and return
+ zoffset = result.data.startpos;
+ zsize = result.length;
+ zcodeinblorb = 1;
+ return 0;
+ }
+ else if (id == bb_make_id('G','L','U','L'))
+ {
+ os_fatal(sf_msgstring(IDS_BLORB_GLULX));
+ }
+ }
+ // we are given a blorb file with no executable chunck
+ // Tell the user that there was no game in the Blorb file
+ os_fatal(sf_msgstring(IDS_BLORB_NOEXEC));
+ }
+
+ // check if possibly Zcode
+ if (hd2[0] < 1 || hd2[0] > 8)
+ os_fatal("Not a Zcode file (or wrong version)");
+
+ // OK, assume a bona fide Z code file
+ zfile = f;
+ fseek(f,0,SEEK_END);
+ zsize = ftell(f);
+ zoffset = 0;
+
+ // try loading a corresponding blorb, but without complaining...
+ p = strrchr(buf,'.');
+ if (p){
+ strcpy(p,".blb");
+ tryloadblorb(buf);
+ }
+
+ return 0;
+ }
+
+static void load_local_resources();
+
+// must be called as soon as possible (i.e. by os_process_arguments())
+int sf_load_resources( char *givenfn)
+ {
+ int st;
+
+ st = load_resources(givenfn);
+ if (st) return st;
+
+ if (bmap)
+ {
+ checkwidths();
+ bb_count_resources(bmap,bb_ID_Pict,&countedpics,NULL,&maxlegalpic);
+ releaseno = bb_get_release_num(bmap);
+ }
+
+ if ((m_reslist_file)) load_local_resources();
+
+ return 0;
+ }
+
+// this routine is only used for the Z code, so we can safely
+// ignore its args
+// NOTE that there is an extra argument (as in WindowsFrotz version)
+FILE *os_path_open (const char *name, const char *mode, long *size)
+ {
+
+ FILE *f = NULL;
+
+ *size = zsize;
+
+ if (zcodeinblorb) f = bfile;
+ else f = zfile;
+
+ if (f) fseek(f,zoffset,SEEK_SET);
+
+ return f;
+ }
+
+/*
+ * os_picture_data
+ *
+ * Return true if the given picture is available. If so, store the
+ * picture width and height in the appropriate variables. Picture
+ * number 0 is a special case: Write the highest legal picture number
+ * and the picture file release number into the height and width
+ * variables respectively when this picture number is asked for.
+ *
+ */
+
+int os_picture_data(int picture, int *height, int *width)
+ {
+ if (maxlegalpic)
+ {
+ if (picture == 0)
+ {
+ *height = maxlegalpic;
+ *width = releaseno;
+ return 1;
+ }
+ else
+ {
+ sf_picture *res = sf_getpic(picture);
+ if (res)
+ {
+ *height = m_gfxScale*res->height;
+ *width = m_gfxScale*res->width;
+ return 1;
+ }
+ }
+ }
+
+ *height = 0;
+ *width = 0;
+ return 0;
+ }
+
+/*
+ * os_menu
+ *
+ * Add to or remove a menu item. Action can be:
+ * MENU_NEW - Add a new menu with the given title
+ * MENU_ADD - Add a new menu item with the given text
+ * MENU_REMOVE - Remove the menu at the given index
+ *
+ */
+void os_menu(int action, int menu, const zword * text)
+{
+/* switch (action)
+ {
+ case MENU_NEW:
+ theWnd->AddNewMenu(menu,text);
+ break;
+ case MENU_ADD:
+ theWnd->AddMenuItem(menu,text);
+ break;
+ case MENU_REMOVE:
+ theWnd->RemoveMenu(menu);
+ break;
+ }*/
+}
+
+/*
+ * os_random_seed
+ *
+ * Return an appropriate random seed value in the range from 0 to
+ * 32767, possibly by using the current system time.
+ *
+ */
+// this is a provisional workaround (time granularity is at best 1s)
+#include <time.h>
+int os_random_seed(void)
+ {
+// return ::GetTickCount() & 32767;
+ if (m_random_seed == -1)
+ {
+ return ((int)(time(NULL))) & 32767;
+ }
+ return m_random_seed;
+ }
+
+// The following assumes Unicode
+
+/*
+ * os_scrollback_char
+ *
+ * Write a character to the scrollback buffer.
+ *
+ */
+void os_scrollback_char(zword c)
+ {
+// theApp.ScrollbackChar(c);
+ if (option_scrollback_buffer == 0) return;
+ if (c == 13) c = 10;
+ if (option_scrollback_buffer == 1) // latin-1
+ {
+ if (c > 255) c = '?';
+ putchar(c);
+ }
+ else
+ { // UTF8
+ if (c < 0x80) putchar(c);
+ else
+ {
+ putchar(0xc0+(c>>6));
+ putchar(0x80+(c & 0x3f));
+ }
+ }
+ }
+
+/*
+ * os_scrollback_erase
+ *
+ * Remove characters from the scrollback buffer.
+ *
+ */
+void os_scrollback_erase (int erase)
+ {
+ if (option_scrollback_buffer) while (erase--) putchar(8);
+// theApp.ScrollbackRemove(erase);
+ }
+
+/*
+ * os_restart_game
+ *
+ * This routine allows the interface to interfere with the process of
+ * restarting a game at various stages:
+ *
+ * RESTART_BEGIN - restart has just begun
+ * RESTART_WPROP_SET - window properties have been initialised
+ * RESTART_END - restart is complete
+ *
+ */
+void os_restart_game(int stage)
+ {
+ // Show Beyond Zork's title screen
+ if ((stage == RESTART_BEGIN) && (story_id == BEYOND_ZORK))
+ {
+ int w,h;
+ if (os_picture_data(1,&h,&w))
+ {
+ sf_fillrect(0,0,0,10000,10000);
+ os_draw_picture(1,1,1);
+ os_read_key(0,0);
+ }
+ }
+ }
+
+#define DEFAULT_GAMMA 2.2
+
+double m_gamma = DEFAULT_GAMMA;
+
+void sf_initcolours()
+ {
+ int i;
+
+ sf_setgamma(m_gamma);
+
+ // Standard Z-Machine colours
+ m_colours[0] = RGB5ToTrue(0x0000); // black
+ m_colours[1] = RGB5ToTrue(0x001D); // red
+ m_colours[2] = RGB5ToTrue(0x0340); // green
+ m_colours[3] = RGB5ToTrue(0x03BD); // yellow
+ m_colours[4] = RGB5ToTrue(0x59A0); // blue
+ m_colours[5] = RGB5ToTrue(0x7C1F); // magenta
+ m_colours[6] = RGB5ToTrue(0x77A0); // cyan
+ m_colours[7] = RGB5ToTrue(0x7FFF); // white
+ m_colours[8] = RGB5ToTrue(0x5AD6); // light grey
+ m_colours[9] = RGB5ToTrue(0x4631); // medium grey
+ m_colours[10] = RGB5ToTrue(0x2D6B); // dark grey
+
+ for (i = 0; i < NON_STD_COLS; i++)
+ m_nonStdColours[i] = 0xFFFFFFFF;
+ m_nonStdIndex = 0;
+
+ }
+
+// Read in settings
+void sf_readsettings(void)
+ {
+ char *p;
+
+ sf_InitProfile(m_setupfile);
+
+ m_aafonts = sf_GetProfileInt("Fonts","antialias",0);
+ m_fontdir = sf_GetProfileString("Fonts","fontdir",NULL);
+ m_fontfiles[0] = sf_GetProfileString("Fonts","textroman",NULL);
+ m_fontfiles[1] = sf_GetProfileString("Fonts","textbold",NULL);
+ m_fontfiles[2] = sf_GetProfileString("Fonts","textitalic",NULL);
+ m_fontfiles[3] = sf_GetProfileString("Fonts","textbolditalic",NULL);
+ m_fontfiles[4] = sf_GetProfileString("Fonts","fixedroman",NULL);
+ m_fontfiles[5] = sf_GetProfileString("Fonts","fixedbold",NULL);
+ m_fontfiles[6] = sf_GetProfileString("Fonts","fixeditalic",NULL);
+ m_fontfiles[7] = sf_GetProfileString("Fonts","fixedbolditalic",NULL);
+
+ ResDir = sf_GetProfileString("Resources","Dir",ResDir);
+ ResPict = sf_GetProfileString("Resources","Pict",ResPict);
+ ResSnd = sf_GetProfileString("Resources","Snd",ResSnd);
+
+//printf("sf_readsettings\n");
+ h_interpreter_number = sf_GetProfileInt("Interpreter","Number",INTERP_AMIGA);
+ err_report_mode = sf_GetProfileInt("Interpreter","Error Reporting",ERR_REPORT_ONCE);
+ option_ignore_errors = sf_GetProfileInt("Interpreter","Ignore Errors",0);
+ option_expand_abbreviations = sf_GetProfileInt("Interpreter","Expand Abbreviations",0);
+ m_tandy = sf_GetProfileInt("Interpreter","Tandy Bit",0) ? true : false;
+ m_quetzal = sf_GetProfileInt("Interpreter","Quetzal Format",1) ? true : false;
+ option_script_cols = sf_GetProfileInt("Interpreter","Wrap Script Lines",1) ? 80 : 0;
+
+ if ((p = sf_GetProfileString("Interpreter","SaveNames",NULL)))
+ m_names_format = p[0];
+
+ AcWidth = sf_GetProfileInt("Window","AcWidth",AcWidth);
+ AcHeight = sf_GetProfileInt("Window","AcHeight",AcHeight);
+
+ m_frequency = sf_GetProfileInt("Audio","Frequency",m_frequency);
+
+// m_filename = sf_GetProfileString("Files","Initial Game","");
+// m_register = sf_GetProfileInt("Files","Register File Types",0) ? true : false;
+
+/* m_wndSize.left = sf_GetProfileInt("Window","Left",0);
+ m_wndSize.top = sf_GetProfileInt("Window","Top",0);
+ m_wndSize.right = sf_GetProfileInt("Window","Right",0);
+ m_wndSize.bottom = sf_GetProfileInt("Window","Bottom",0);
+ m_wndState = sf_GetProfileInt("Window","State",SW_SHOWNORMAL);
+
+ m_toolBar = sf_GetProfileInt("Window","Toolbar",1) ? true : false;
+ m_statusBar = sf_GetProfileInt("Window","Status Bar",1) ? true : false;
+ m_notifyFull = sf_GetProfileInt("Window","Notify Full Screen",1) ? true : false;
+
+ m_propFontName = sf_GetProfileString("Display","Proportional Font Name",
+ GetDefaultFont());
+ m_fixedFontName = sf_GetProfileString("Display","Fixed Font Name",
+ "Courier New");
+ m_fontSize = sf_GetProfileInt("Display","Font Size",10);*/
+
+ m_v6scale = sf_GetProfileInt("Display","Infocom V6 Scaling",2);
+ m_gfxScale = 1;
+ m_defaultFore = ( sf_GetProfileInt("Display","Foreground",0xffffff));
+ m_defaultBack = ( sf_GetProfileInt("Display","Background",0x800000));
+//printf("mdff%d mdfb%d\n",m_defaultFore,m_defaultBack);
+// m_fastScroll = sf_GetProfileInt("Display","Fast Scrolling",0) ? true : false;
+ m_morePrompts = sf_GetProfileInt("Display","Show More Prompts",1) ? true : false;
+// m_leftMargin = sf_GetProfileInt("Display","Left Margin",0);
+// m_rightMargin = sf_GetProfileInt("Display","Right Margin",0);
+ m_gamma = sf_GetProfileDouble("Display","Gamma",DEFAULT_GAMMA);
+ sf_initcolours();
+
+ sf_FinishProfile();
+
+ }
+
+// Get a colour
+ulong sf_GetColour(int colour)
+ {
+ // Standard colours
+ if ((colour >= BLACK_COLOUR) && (colour <= DARKGREY_COLOUR))
+ return m_colours[colour-BLACK_COLOUR];
+
+ // Default colours
+ if (colour == 16)
+ return m_defaultFore;
+ if (colour == 17)
+ return m_defaultBack;
+
+ // Non standard colours
+ if ((colour >= 18) && (colour < 256))
+ {
+ if (m_nonStdColours[colour-18] != 0xFFFFFFFF)
+ return m_nonStdColours[colour-18];
+ }
+ return m_colours[0];
+ }
+
+// Get a default colour
+ulong sf_GetDefaultColour(bool fore)
+ {
+ if (m_IsInfocomV6)
+ return sf_GetColour(fore ? WHITE_COLOUR : BLACK_COLOUR);
+ return fore ? m_defaultFore : m_defaultBack;
+ }
+
+
+// Get an index for a non-standard colour
+int sf_GetColourIndex( ulong colour)
+ {
+ int i, index = -1;
+ // Is this a standard colour?
+ for (i = 0; i < 11; i++)
+ {
+ if (m_colours[i] == colour)
+ return i+BLACK_COLOUR;
+ }
+
+ // Is this a default colour?
+ if (m_defaultFore == colour)
+ return 16;
+ if (m_defaultBack == colour)
+ return 17;
+
+ // Is this colour already in the table?
+ for (i = 0; i < NON_STD_COLS; i++)
+ {
+ if (m_nonStdColours[i] == colour)
+ return i+18;
+ }
+
+ // Find a free colour index
+ while (index == -1)
+ {
+ if (colour_in_use(m_nonStdIndex+18) == 0)
+ {
+ m_nonStdColours[m_nonStdIndex] = colour;
+ index = m_nonStdIndex+18;
+ }
+
+ m_nonStdIndex++;
+ if (m_nonStdIndex >= NON_STD_COLS)
+ m_nonStdIndex = 0;
+ }
+ return index;
+ }
+
+
+
+/*
+ * os_set_colour
+ *
+ * Set the foreground and background colours which can be:
+ *
+ * 1
+ * BLACK_COLOUR
+ * RED_COLOUR
+ * GREEN_COLOUR
+ * YELLOW_COLOUR
+ * BLUE_COLOUR
+ * MAGENTA_COLOUR
+ * CYAN_COLOUR
+ * WHITE_COLOUR
+ * TRANSPARENT_COLOUR
+ *
+ * Amiga only:
+ *
+ * LIGHTGREY_COLOUR
+ * MEDIUMGREY_COLOUR
+ * DARKGREY_COLOUR
+ *
+ * There may be more colours in the range from 16 to 255; see the
+ * remarks about os_peek_colour.
+ *
+ */
+void os_set_colour(int new_foreground, int new_background)
+ {
+ SF_textsetting *ts = sf_curtextsetting();
+ sf_flushtext();
+// theWnd->ResetOverhang();
+//printf("os_set_colour %d %d\n",new_foreground,new_background);
+ if (new_foreground == 1)
+ ts->fore = sf_GetDefaultColour(true);
+ else if (new_foreground < 256)
+ ts->fore = sf_GetColour(new_foreground);
+ ts->foreDefault = (new_foreground == 1);
+
+ if (new_background == 1)
+ ts->back = sf_GetDefaultColour(false);
+ else if (new_background < 256)
+ ts->back = sf_GetColour(new_background);
+ ts->backDefault = (new_background == 1);
+ ts->backTransparent = (new_background == 15);
+
+//printf("os_set_colour %d %d %x %x\n",new_foreground,new_background,ts->fore,ts->back);
+// theWnd->ApplyTextSettings();
+}
+
+/*
+ * os_from_true_cursor
+ *
+ * Given a true colour, return an appropriate colour index.
+ *
+ */
+int os_from_true_colour(zword colour)
+ {
+ return sf_GetColourIndex(RGB5ToTrue(colour));
+ }
+
+/*
+ * os_to_true_cursor
+ *
+ * Given a colour index, return the appropriate true colour.
+ *
+ */
+zword os_to_true_colour(int index)
+ {
+ return TrueToRGB5(sf_GetColour(index));
+ }
+
+/*
+ * os_init_screen
+ *
+ * Initialise the IO interface. Prepare screen and other devices
+ * (mouse, sound card). Set various OS depending story file header
+ * entries:
+ *
+ * h_config (aka flags 1)
+ * h_flags (aka flags 2)
+ * h_screen_cols (aka screen width in characters)
+ * h_screen_rows (aka screen height in lines)
+ * h_screen_width
+ * h_screen_height
+ * h_font_height (defaults to 1)
+ * h_font_width (defaults to 1)
+ * h_default_foreground
+ * h_default_background
+ * h_interpreter_number
+ * h_interpreter_version
+ * h_user_name (optional; not used by any game)
+ *
+ * Finally, set reserve_mem to the amount of memory (in bytes) that
+ * should not be used for multiple undo and reserved for later use.
+ *
+ */
+void os_init_screen(void)
+ {
+
+ sf_initvideo(AcWidth,AcHeight,(m_fullscreen != -1));
+
+ // Set the graphics scaling
+ if (sf_IsInfocomV6() || (story_id == BEYOND_ZORK))
+ m_gfxScale = m_v6scale;
+ else
+ m_gfxScale = 1;
+
+ // Set the configuration
+ if (h_version == V3)
+ {
+ h_config |= CONFIG_SPLITSCREEN;
+ h_config |= CONFIG_PROPORTIONAL;
+ if (m_tandy)
+ h_config |= CONFIG_TANDY;
+ else
+ h_config &= ~CONFIG_TANDY;
+ }
+ if (h_version >= V4)
+ {
+ h_config |= CONFIG_BOLDFACE;
+ h_config |= CONFIG_EMPHASIS;
+ h_config |= CONFIG_FIXED;
+ h_config |= CONFIG_TIMEDINPUT;
+ }
+ if (h_version >= V5)
+ h_config |= CONFIG_COLOUR;
+ if (h_version == V6)
+ {
+ if (bmap)
+ {
+ h_config |= CONFIG_PICTURES;
+ h_config |= CONFIG_SOUND;
+ }
+ }
+
+ h_interpreter_version = 'F';
+ if (h_version == V6)
+ {
+ h_default_foreground = sf_GetColourIndex(sf_GetDefaultColour(true));
+ h_default_background = sf_GetColourIndex(sf_GetDefaultColour(false));
+ }
+ else
+ {
+ h_default_foreground = 1;
+ h_default_background = 1;
+ }
+
+ os_set_font(FIXED_WIDTH_FONT);
+ os_set_text_style(0);
+
+/* theWnd->ApplyTextSettings(
+ FrotzWnd::TextSettings(0,FIXED_WIDTH_FONT));*/
+ {
+ int H, W;
+ os_font_data( FIXED_WIDTH_FONT, &H, &W);
+ h_font_width = (zbyte)W;
+ h_font_height = (zbyte)H;
+ }
+
+ h_screen_width = (zword)AcWidth;
+ h_screen_height = (zword)AcHeight;
+ h_screen_cols = (zbyte)(h_screen_width / h_font_width);
+ h_screen_rows = (zbyte)(h_screen_height / h_font_height);
+
+ // Check for sound
+ if ((h_version == V3) && (h_flags & OLD_SOUND_FLAG))
+ {
+ if (((bmap==NULL) && (m_localfiles==0)) || (!sf_initsound()))
+ h_flags &= ~OLD_SOUND_FLAG;
+ }
+ else if ((h_version >= V4) && (h_flags & SOUND_FLAG))
+ {
+ if (((bmap==NULL) && (m_localfiles==0)) || (!sf_initsound()))
+ h_flags &= ~SOUND_FLAG;
+ }
+
+ if (h_version >= V5)
+ {
+ zword mask = 0;
+ if (h_version == V6)
+ mask |= TRANSPARENT_FLAG;
+
+ // Mask out any unsupported bits in the extended flags
+ hx_flags &= mask;
+
+ hx_fore_colour = TrueToRGB5(sf_GetDefaultColour(true));
+ hx_back_colour = TrueToRGB5(sf_GetDefaultColour(false));
+ }
+ }
+
+/*
+ * os_fatal
+ *
+ * Display error message and stop interpreter.
+ *
+ */
+void os_fatal(const char *s, ...)
+ {
+ va_list m;
+// if (theWnd != NULL)
+// theWnd->FlushDisplay();
+
+ sf_cleanup_all();
+ fprintf(stderr,"\n%s: ",sf_msgstring(IDS_FATAL));
+ va_start( m, s);
+ vfprintf( stderr, s, m);
+ va_end(m);
+ fprintf(stderr,"\n\n");
+
+ exit(EXIT_FAILURE);
+
+// ::MessageBox(AfxGetMainWnd()->GetSafeHwnd(),s,CResString(IDS_FATAL),MB_ICONERROR|MB_OK);
+// throw FrotzApp::AbortFrotz();
+ }
+
+// If true, running one of Infocom's V6 games
+bool sf_IsInfocomV6()
+ {
+ switch (story_id)
+ {
+ case ARTHUR:
+ case JOURNEY:
+ case SHOGUN:
+ case ZORK_ZERO:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+#define LOCAL_MEM -1
+#define LOCAL_FILE -2
+
+void sf_freeresource( myresource *res)
+ {
+ int cnu;
+
+ if (!res) return;
+
+ cnu = res->bbres.chunknum;
+
+ if (cnu == LOCAL_MEM)
+ {
+ if (res->bbres.data.ptr) free(res->bbres.data.ptr);
+ return;
+ }
+ if (cnu == LOCAL_FILE)
+ {
+ if (res->file) fclose(res->file);
+ return;
+ }
+
+ if ((bmap) && (cnu >= 0))
+ bb_unload_chunk(bmap,cnu);
+ }
+
+static FILE * findlocal( int ispic, int num, int *size)
+ {
+ FILE *f;
+ char *tpl, buf[MAX_FILE_NAME+1];
+
+ tpl = ispic ? ResPict : ResSnd;
+ strcpy(buf,ResDir);
+ sprintf(buf+strlen(buf),tpl,num);
+ f = fopen(buf,"rb");
+ if (!f) return f;
+ fseek(f,0,SEEK_END);
+ *size = ftell(f);
+ fseek(f,0,SEEK_SET);
+ return f;
+ }
+
+static FILE * findfromlist( int ispic, int num, int *size);
+
+static int loadlocal( int num, int ispic, int method, myresource * res)
+ {
+ FILE *f;
+ int size;
+ byte hd[4];
+
+ f = findlocal( ispic, num, &size);
+ if (!f) f = findfromlist( ispic, num, &size);
+ if (!f) return bb_err_NotFound;
+
+ fread(hd,1,4,f);
+ fseek(f,0,SEEK_SET);
+ res->type = 0;
+ if (ispic)
+ {
+ if (hd[0] == 0xff && hd[1] == 0xd8) res->type = bb_make_id('J','P','E','G');
+ else if (hd[0] == 0x89 && hd[1] == 0x50) res->type = bb_make_id('P','N','G',' ');
+ }
+ else
+ {
+ if (memcmp(hd,"FORM",4) == 0) res->type = bb_make_id('F','O','R','M');
+ else if (memcmp(hd,"OggS",4) == 0) res->type = bb_make_id('O','G','G','V');
+ else res->type = bb_make_id('M','O','D',' ');
+ }
+ if (!res->type) { fclose(f); return bb_err_NotFound;}
+
+ res->bbres.data.startpos = 0;
+ res->file = f;
+ res->bbres.length = size;
+ if (method == bb_method_FilePos) res->bbres.chunknum = LOCAL_FILE;
+ else
+ {
+ void *ptr;
+ res->bbres.chunknum = LOCAL_MEM;
+ ptr = res->bbres.data.ptr = malloc(size);
+ if (ptr) fread(ptr,1,size,f);
+ fclose(f);
+ if (!ptr) return bb_err_NotFound;
+ }
+
+ return bb_err_None;
+ }
+
+int sf_getresource( int num, int ispic, int method, myresource * res)
+ {
+ int st; ulong usage;
+
+ res->bbres.data.ptr = NULL;
+ res->file = NULL;
+
+ if (m_localfiles)
+ if ((st = loadlocal(num,ispic,method,res)) == bb_err_None) return st;
+
+ if (!bmap) return bb_err_NotFound;
+
+ if (ispic) usage = bb_ID_Pict;
+ else usage = bb_ID_Snd;
+ st = bb_load_resource(bmap,method,(bb_result_t *)res,usage,num);
+ if (st == bb_err_None)
+ {
+ res->type = bmap->chunks[res->bbres.chunknum].type;
+ if (method == bb_method_FilePos) res->file = bfile;
+ }
+ return st;
+ }
+
+/////////////////
+
+typedef struct {
+ void *next;
+ int num, ispic;
+ ulong type;
+ char *name;
+ } LLENTRY;
+
+static LLENTRY *Lpics=NULL, *Lsnds = NULL;
+
+static int numlocal = 0, numlocalpic = 0, numlocalsnd = 0;
+static int p_ispic, p_num;
+static ulong p_type;
+static char *p_name;
+
+static void cleanLLENTRY( LLENTRY *e)
+ {
+ while (e){ LLENTRY *n = e->next; if (e->name) free(e->name); free(e); e = n;}
+ }
+
+static void cleanlocallist()
+ {
+ cleanLLENTRY(Lpics); Lpics = NULL;
+ cleanLLENTRY(Lsnds); Lsnds = NULL;
+ }
+
+static int parseline( char *s)
+ {
+ char *p, p3; int n;
+ p = strtok(s," \t\n");
+ if (!p) return 0;
+ if (strcmp(p,"Pict")==0) p_ispic = 1;
+ else if (strcmp(p,"Snd")==0) p_ispic = 0;
+ else return -1;
+ p = strtok(NULL," \t\n"); if (!p) return -1;
+ p_num = atoi(p);
+ p = strtok(NULL," \t\n"); if (!p) return -1;
+ n = strlen(p); if (n < 3) return -1;
+ if (p[3]) p3 = p[3]; else p3 = ' ';
+ p_type = bb_make_id(p[0],p[1],p[2],p3);
+ p = strtok(NULL," \t\n"); if (!p) return -1;
+ p_name = p;
+ return 1;
+ }
+
+static void load_local_resources()
+ {
+ FILE *f; LLENTRY *e;
+ char s[256]; int st;
+ f = fopen(m_reslist_file,"r"); if (!f) return;
+ CLEANREG(cleanlocallist);
+ for (;;)
+ {
+ fgets(s,254,f); if (feof(f)) break;
+ st = parseline(s);
+ if (st < 1) continue;
+ e = calloc(1,sizeof(LLENTRY));
+ if (e)
+ {
+ e->num = p_num; e->ispic = p_ispic; e->type = p_type;
+ e->name = strdup(p_name);
+ if (p_ispic)
+ {
+ e->next = Lpics; Lpics = e; numlocalpic++;
+ if (p_num > maxlegalpic) maxlegalpic = p_num;
+ }
+ else
+ {
+ e->next = Lsnds; Lsnds = e; numlocalsnd++;
+ }
+ }
+ }
+ numlocal = numlocalpic + numlocalsnd;
+ if (numlocal) m_localfiles = 1;
+ fclose(f);
+ }
+
+static FILE * findfromlist( int ispic, int num, int *size)
+ {
+ FILE *f; LLENTRY *l;
+ char buf[MAX_FILE_NAME+1];
+
+ if (ispic) l = Lpics;
+ else l = Lsnds;
+ while (l)
+ {
+ if (l->num == num) break;
+ l = l->next;
+ }
+ if (!l) return NULL;
+
+ strcpy(buf,ResDir);
+ strcat(buf,l->name);
+
+ f = fopen(buf,"rb");
+ if (!f) return f;
+ fseek(f,0,SEEK_END);
+ *size = ftell(f);
+ fseek(f,0,SEEK_SET);
+ return f;
+ }
+
--- /dev/null
+#include <stdio.h>
+#include <signal.h>
+
+#include "sf_frotz.h"
+
+#ifdef WIN32
+
+static void resethandlers()
+ {
+ signal(SIGINT,SIG_DFL);
+ signal(SIGFPE,SIG_DFL);
+ signal(SIGSEGV,SIG_DFL);
+ }
+
+static const char * signame( int sig)
+ {
+ switch (sig)
+ {
+ case SIGINT: return "[SIGINT]";
+ case SIGFPE: return "[SIGFPE]";
+ case SIGSEGV: return "[SIGSEGV]";
+ default: return "";
+ }
+ }
+
+static void myhandler( int s)
+ {
+ resethandlers();
+ os_fatal("Signal %d received %s",s,signame(s));
+ }
+
+void sf_installhandlers()
+ {
+// CLEANREG(resethandlers);
+ signal(SIGINT,myhandler);
+ signal(SIGFPE,myhandler);
+ signal(SIGSEGV,myhandler);
+ }
+
+
+#else
+
+#include <execinfo.h>
+
+/* get REG_EIP from ucontext.h */
+#define __USE_GNU
+#include <ucontext.h>
+
+// REG_EIP does not exist on 64bit CPU
+#if defined(__amd64__) || defined (__x86_64__)
+#define _PROG_COUNTER REG_RIP
+#else
+#define _PROG_COUNTER REG_EIP
+#endif
+
+static struct { int sig; char *name;} NAMES[] = {
+ {SIGSEGV,"SIGSEGV"},
+ {SIGFPE,"SIGFPE"},
+ {SIGILL,"SIGILL"},
+ {0,NULL}};
+
+static char *getsigname( int s){
+ int i = 0;
+ while (NAMES[i].name)
+ {
+ if (NAMES[i].sig == s) return NAMES[i].name;
+ i++;
+ }
+ return NULL;
+ }
+
+static void bt_sighandler(int sig, siginfo_t *info,
+ void *secret) {
+
+ void *trace[16];
+ char **messages = (char **)NULL;
+ char *nam;
+ int i, trace_size = 0;
+ ucontext_t *uc = (ucontext_t *)secret;
+
+ if (sig == SIGINT)
+ os_fatal("Emergency exit!\n\n(Signal SIGINT received)");
+
+ /* Do something useful with siginfo_t */
+
+ printf("\nInterpreter bug!\nSignal %d ", sig);
+ if ((nam = getsigname(sig))) printf("[%s] ",nam);
+ printf("from %p", uc->uc_mcontext.gregs[_PROG_COUNTER]);
+
+ if (sig == SIGSEGV)
+ printf(" [faulty address is %p]",info->si_addr);
+
+ printf("\n");
+
+ trace_size = backtrace(trace, 16);
+ /* overwrite sigaction with caller's address */
+ trace[1] = (void *) uc->uc_mcontext.gregs[_PROG_COUNTER];
+
+ /* skip first stack frame (points here) */
+ printf("Backtrace:\n");
+// messages = backtrace_symbols(trace, trace_size);
+// for (i=1; i<trace_size; ++i)
+// printf("[bt] %s\n", messages[i]);
+
+ for (i=1;i<trace_size;i++)
+ {
+ printf(" "); fflush(stdout);
+ backtrace_symbols_fd(trace+i, 1,fileno(stdout));
+ }
+
+ exit(0);
+ }
+
+void sf_installhandlers()
+ {
+
+ /* Install our signal handler */
+ struct sigaction sa;
+
+ sa.sa_sigaction = (void *)bt_sighandler;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+
+ sigaction(SIGSEGV, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ sigaction(SIGFPE, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGILL, &sa, NULL);
+
+ }
+
+#endif
--- /dev/null
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sf_frotz.h"
+#include "blorblow.h"
+
+#include <SDL.h>
+#include <SDL_thread.h>
+#include <SDL_mixer.h>
+
+/////////////// AUDIO!!!
+
+enum {SFX_TYPE, MOD_TYPE};
+
+typedef struct EFFECT {
+ void (*destroy)(struct EFFECT *);
+ int number;
+ int type;
+ int active;
+ int voice;
+ Mix_Music * mod;
+ Mix_Chunk * sam;
+ int repeats;
+ int volume;
+ int ended;
+ zword eos;
+ ulong endtime;
+ } EFFECT;
+
+// no effects cache
+
+static EFFECT *e_sfx = NULL;
+static EFFECT *e_mod = NULL;
+
+int SFaudiorunning = 0;
+volatile int end_of_sound_flag = 0;
+int m_no_sound = 0;
+int m_frequency = 44100;
+
+static int audio_rate, audio_channels;
+ // set this to any of 512,1024,2048,4096
+ // the higher it is, the more FPS shown and CPU needed
+static int audio_buffers=512;
+static Uint16 audio_format;
+static int volume = SDL_MIX_MAXVOLUME;
+static int bits;
+
+static void finishaudio()
+ {
+ if (!SFaudiorunning) return;
+ os_stop_sample(0);
+ SFaudiorunning = 0;
+
+ // flush cache
+//printf("Flushing sound cache\n");
+ if (e_sfx) e_sfx->destroy(e_sfx);
+ if (e_mod) e_mod->destroy(e_mod);
+
+ e_sfx = e_mod = NULL;
+
+ Mix_CloseAudio();
+ }
+
+static void music_finished(void);
+static void channel_finished(int channel);
+
+int sf_initsound()
+ {
+ if (SFaudiorunning) return 1;
+ if (m_no_sound) return 0;
+ SFaudiorunning = 1;
+
+ // initialize sdl mixer, open up the audio device
+ if (Mix_OpenAudio(m_frequency,MIX_DEFAULT_FORMAT,2,audio_buffers) < 0)
+ {
+ SFaudiorunning = 0;
+ return 0;
+ }
+
+ Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
+ bits = audio_format & 0xFF;
+
+ // hook for end-of-music
+ Mix_HookMusicFinished(music_finished);
+ // hook for end-of-sfx
+ Mix_ChannelFinished(channel_finished);
+
+ Mix_AllocateChannels(1);
+ CLEANREG(finishaudio);
+// sf_initresample();
+ return 1;
+ }
+
+static void baredestroy(EFFECT * r)
+ {
+ if (r)
+ {
+ if (r->mod) Mix_FreeMusic(r->mod);
+ if (r->sam) Mix_FreeChunk(r->sam);
+ free(r);
+ }
+ }
+
+static EFFECT * new_effect( int type, int num)
+ {
+ EFFECT * reader = (EFFECT *)calloc(1,sizeof(EFFECT));
+ if (reader)
+ {
+ reader->type = type;
+ reader->number = num;
+ reader->destroy = baredestroy;
+ }
+ return (EFFECT *)reader;
+ }
+
+// according to specs, this is only called when music ends "naturally",
+// which I take for "not halted programmatically"
+static void music_finished(void)
+ {
+ if (!e_mod) return;
+ if (!e_mod->active) return;
+ e_mod->active = 0;
+ e_mod->ended = 1;
+ }
+
+// this may be called also via a Mix_Haltetc.
+static void channel_finished(int channel)
+ {
+ if (channel != 0) return;
+ if (!e_sfx) return;
+ if (!e_sfx->active) return;
+ e_sfx->active = 0;
+ e_sfx->ended = 1; // stopsample will take care of this...
+ }
+
+static void stopsample()
+ {
+ if (!e_sfx) return;
+ e_sfx->active = 0;
+ Mix_HaltChannel(0);
+ e_sfx->ended = 0;
+ }
+
+static void stopmodule()
+ {
+ if (!e_mod) return;
+ e_mod->active = 0;
+// Player_Mute(MUTE_INCLUSIVE,0,64);
+// Player_SetVolume(0); // bug??
+ Mix_HaltMusic();
+ e_mod->ended = 0;
+ }
+
+static void startsample()
+ {
+ if (!e_sfx) return;
+ Mix_PlayChannel(0,e_sfx->sam,e_sfx->repeats);
+ Mix_Volume(0,e_sfx->volume);
+// Voice_SetPanning(v,PAN_CENTER);
+// while (Voice_Stopped(v)){
+// MikMod_Update(); sf_sleep(10);
+// }
+// e_sfx->voice = v;
+ e_sfx->active = 1;
+ }
+
+//static int samplefinished(){
+// if (!Voice_Stopped(e_sfx->voice)) return 0;
+// return (sf_ticks() >= e_sfx->endtime);
+// }
+
+static void startmodule()
+ {
+ if (!e_mod) return;
+ Mix_PlayMusic(e_mod->mod,e_mod->repeats);
+ Mix_VolumeMusic(e_mod->volume);
+ e_mod->active = 1;
+ }
+
+static EFFECT *getaiff( FILE *f, size_t pos, int len, int num)
+ {
+ EFFECT *res;
+ void * data; int size;
+
+ res = new_effect(SFX_TYPE,num);
+ if (!res) return res;
+
+ if (sf_aiffwav(f,pos,&data,&size)==0)
+ {
+ res->sam = Mix_LoadWAV_RW(SDL_RWFromMem( data, size),0);
+ if (data) free(data);
+ }
+
+ if (!res->sam)
+ {
+//printf("Sample_LoadGeneric failure: %s\n",MikMod_strerror(MikMod_errno));
+ res->destroy(res);
+ return NULL;
+ }
+//printf("AIFF loaded\n");
+ return res;
+ }
+
+static EFFECT *getmodule( FILE *f, size_t pos, int len, int num)
+ {
+ EFFECT *res;
+ byte h[2];
+
+ res = new_effect(MOD_TYPE,num);
+ if (!res)
+ return NULL;
+
+ fseek(f,pos,SEEK_SET);
+ fread(h,1,2,f);
+ fseek(f,pos,SEEK_SET);
+ if (h[0] == 'P' && h[1] == 'K') // zipped module
+ {
+ int size; void *data;
+ int st = sf_pkread(f,pos,&data,&size);
+ if (st)
+ {
+//printf("Error %d from sf_pkread()\n",st);
+ res->destroy(res);
+ return NULL;
+ }
+ res->mod = Mix_LoadMUS_RW(SDL_RWFromMem( data, size));
+ free(data);
+ }
+ else
+ {
+ FILE *f2 = fdopen(dup(fileno(f)),"rb");
+ fseek(f2,pos,SEEK_SET);
+ res->mod = Mix_LoadMUS_RW(SDL_RWFromFP(f2,1));
+ }
+ if (!res->mod)
+ {
+//printf("Player_Load failure: %s\n",MikMod_strerror(MikMod_errno));
+ res->destroy(res);
+ return NULL;
+ }
+//printf("Module loaded\n");
+ return res;
+ }
+
+static EFFECT *geteffect( num)
+ {
+ myresource res;
+ EFFECT *result = NULL;
+ unsigned int id;
+
+ if ((e_sfx) && (e_sfx->number == num)) return e_sfx;
+ if ((e_mod) && (e_mod->number == num)) return e_mod;
+
+ if (sf_getresource(num,0,bb_method_FilePos,&res) != bb_err_None)
+ return NULL;
+
+ // Look for a recognized format
+ id = res.type;
+
+ if (id == bb_make_id('F','O','R','M'))
+ {
+ result = getaiff( res.file, res.bbres.data.startpos, res.bbres.length, num);
+ }
+ else if (id == bb_make_id('M','O','D',' ') ||
+ id == bb_make_id('O','G','G','V'))
+ {
+ result = getmodule( res.file, res.bbres.data.startpos, res.bbres.length, num);
+ }
+ sf_freeresource(&res);
+
+ return result;
+ }
+
+// sound handling
+
+/*
+ * os_beep
+ *
+ * Play a beep sound. Ideally, the sound should be high- (number == 1)
+ * or low-pitched (number == 2).
+ *
+ */
+void os_beep(int number)
+ {
+ if (m_no_sound) return;
+ printf("\a"); fflush(stdout);
+ if (!SFaudiorunning) return;
+/* theWnd->FlushDisplay();
+ ::MessageBeep(MB_ICONEXCLAMATION);*/
+ }
+
+/*
+ * os_finish_with_sample
+ *
+ * Remove the current sample from memory (if any).
+ *
+ */
+void os_finish_with_sample(int number)
+ {
+ if (!SFaudiorunning) return;
+ os_stop_sample(number);
+// FrotzSound::Stop(number);
+ }
+
+/*
+ * os_prepare_sample
+ *
+ * Load the given sample from the disk.
+ *
+ */
+void os_prepare_sample(int number)
+ {
+ if (!SFaudiorunning) return;
+ }
+
+/*
+ * os_start_sample
+ *
+ * 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). The end_of_sound
+ * function is called as soon as the sound finishes, passing in the
+ * eos argument.
+ *
+ */
+void os_start_sample(int number, int volume, int repeats, zword eos)
+ {
+ EFFECT *e;
+
+ if (!SFaudiorunning) return;
+
+//printf("start %d vol %d rep %d\n",number,volume,repeats);
+
+ // NOTE: geteffect may return an already loaded effect
+ e = geteffect(number);
+ if (!e) return;
+ if (e->type == SFX_TYPE) stopsample();
+ else stopmodule();
+ if (repeats < 1) repeats = 1;
+ if (repeats == 255) repeats = -1;
+ if (volume < 0) volume = 0;
+ if (volume > 8) volume = 8;
+ if (e->type == SFX_TYPE && repeats > 0) repeats--;
+ e->repeats = repeats;
+ e->volume = 32*volume;
+ e->eos = eos;
+ e->ended = 0;
+ if (e->type == SFX_TYPE)
+ {
+ if ((e_sfx) && (e_sfx != e)) e_sfx->destroy(e_sfx);
+ e_sfx = e;
+ startsample();
+ }
+ else
+ {
+ if ((e_mod) && (e_mod != e)) e_mod->destroy(e_mod);
+ e_mod = e;
+ startmodule();
+ }
+ }
+
+/*
+ * os_stop_sample
+ *
+ * Turn off the current sample.
+ *
+ */
+void os_stop_sample(int number)
+ {
+ if (!SFaudiorunning) return;
+ if (number == 0)
+ {
+ stopsample();
+ stopmodule();
+ return;
+ }
+ if ((e_sfx) && (e_sfx->number == number)) stopsample();
+ if ((e_mod) && (e_mod->number == number)) stopmodule();
+
+// FrotzSound::Stop(number);
+ }
+
+void sf_checksound()
+ {
+ if ((e_sfx) && (e_sfx->ended))
+ {
+ e_sfx->ended = 0;
+ if (e_sfx->eos)
+ {
+ end_of_sound_flag = 1;
+ end_of_sound(e_sfx->eos);
+ }
+ }
+ if ((e_mod) && (e_mod->ended))
+ {
+ e_mod->ended = 0;
+ if (e_mod->eos)
+ {
+ end_of_sound_flag = 1;
+ end_of_sound(e_mod->eos);
+ }
+ }
+ }
+
+void os_tick()
+ {
+ sf_checksound();
+ if (SFticked)
+ {
+ SFticked = false;
+ sf_flushdisplay();
+ }
+ }
+
+///////////////////////////////////////
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <zlib.h>
+
+#ifdef __WIN32__
+#include <io.h>
+#endif
+
+#include "sf_frotz.h"
+
+
+typedef void (*CLEANFUNC)();
+
+typedef struct cfstruct cfrec;
+
+struct cfstruct {
+ CLEANFUNC func;
+ cfrec *next;
+ const char *name;
+ };
+
+static cfrec *cflist = NULL;
+
+void sf_regcleanfunc( void *f, const char *p){
+ cfrec *n = calloc(1,sizeof(cfrec));
+ if (n)
+ {
+ if (!p) p = "";
+ n->func = (CLEANFUNC) f;
+ n->name = p;
+ n->next = cflist;
+ cflist = n;
+ }
+ }
+
+void sf_cleanup_all()
+ {
+ while (cflist)
+ {
+ cfrec *n = cflist->next;
+//printf("cleanup c%p [%s] n%p\n",cflist,cflist->name,n);
+ if (cflist->func) cflist->func();
+ free(cflist);
+ cflist = n;
+ }
+//printf("Cleanup done.\n");
+ }
+
+/*
+ * os_reset_screen
+ *
+ * Reset the screen before the program ends.
+ *
+ */
+void os_reset_screen(void)
+ {
+ sf_flushdisplay();
+// theWnd->FlushDisplay();
+// theWnd->ResetOverhang();
+
+ if (m_exitPause)
+ {
+ const char *hit = sf_msgstring(IDS_HIT_KEY_EXIT);
+ os_set_font(TEXT_FONT);
+ os_set_text_style(0);
+ screen_new_line();
+
+ while (*hit)
+ os_display_char((*hit++));
+ os_read_key(0,1);
+ }
+
+ sf_cleanup_all();
+ }
+
+
+
+
+int user_background = -1;
+int user_foreground = -1;
+int user_emphasis = -1;
+int user_bold_typing = -1;
+int user_reverse_bg = -1;
+int user_reverse_fg = -1;
+int user_screen_height = -1;
+int user_screen_width = -1;
+int user_tandy_bit = -1;
+//int user_random_seed = -1;
+int user_font = 1;
+int m_random_seed = -1;
+int m_fullscreen = -1;
+int m_reqW = 0, m_reqH = 0;
+int m_vga_fonts = 0;
+extern char * m_setupfile;
+extern char m_names_format;
+static char user_names_format = 0;
+extern char *m_reslist_file;
+extern int option_scrollback_buffer;
+
+static char *info1 =
+ "\n"
+ "SDL Frotz V%d.%02d build %s - interpreter for z-code games.\n"
+ "Complies with Standard 1.0; supports Blorb resources and Quetzal save files.\n"
+ "Based on Frotz 2.40 by Stefan Jokisch and WindowsFrotz2000 by David Kinder.\n"
+ "\n"
+ "Syntax: sfrotz [options] story-file\n\n";
+
+static char *infos[] = {
+ "-a watch attribute setting",
+ "-A watch attribute testing",
+ "-b # background colour",
+ "-c # context lines",
+ "-f # foreground colour",
+ "-F fullscreen mode",
+ "-h # screen height",
+ "-i ignore runtime errors",
+ "-l # left margin",
+ "-L use local resources",
+ "-o watch object movement",
+ "-O watch object locating",
+ "-p alter piracy opcode",
+ "-q quiet (disable sound)",
+ "-r # right margin",
+ "-R save/restore in old Frotz format",
+ "-s # random number seed value",
+ "-S # transcript width",
+ "-t set Tandy bit",
+ "-u # slots for multiple undo",
+ "-w # screen width",
+ "-x expand abbreviations g/x/z",
+ "-V force VGA fonts",
+ "-Z # error checking (see below)",
+ NULL};
+
+static char *info2 =
+ "\nError checking: 0 none, 1 first only (default), 2 all, 3 exit after any error.\n"
+ "For more options and explanations, please read the HTML manual.\n";
+
+static char * getbuilddatetime( int tf);
+
+#define WIDCOL 40
+static void usage()
+ {
+ char **p = infos; int i=0,len=0;
+ printf(info1,SFROTZ_MAJOR,SFROTZ_MINOR,getbuilddatetime(1));
+ while (*p)
+ {
+ if (i)
+ {
+ while (len > 0){ fputc(' ',stdout); len--;}
+ puts(*p);
+ }
+ else
+ {
+ fputs(" ",stdout);
+ fputs(*p,stdout);
+ len = WIDCOL-strlen(*p)-2;
+ }
+ i = 1-i;
+ p++;
+ }
+ if (i) fputc('\n',stdout);
+ puts (info2);
+ }
+
+/*
+ * parse_options
+ *
+ * Parse program options and set global flags accordingly.
+ *
+ */
+
+static const char *progname = NULL;
+
+extern char script_name[];
+extern char command_name[];
+extern char save_name[];
+extern char auxilary_name[];
+
+char stripped_story_name[100];
+
+extern char *optarg;
+extern int optind;
+extern int m_timerinterval;
+
+static char *options = "@:aAb:B:c:D:f:Fh:iI:l:Lm:N:oOpqr:Rs:S:tTu:Vw:xZ:";
+
+static int limit( int v, int m, int M)
+ {
+ if (v < m) return m;
+ if (v > M) return M;
+ return v;
+ }
+
+static void parse_options (int argc, char **argv)
+ {
+ int c;
+
+ do {
+
+ int num = 0, copt = 0;;
+
+ c = getopt (argc, argv, options);
+
+ if (optarg != NULL)
+ {
+ num = atoi (optarg);
+ copt = optarg[0];
+ }
+
+ if (c == 'a')
+ option_attribute_assignment = 1;
+ if (c == 'A')
+ option_attribute_testing = 1;
+ if (c == 'b')
+ user_background = num;
+ if (c == 'B')
+ option_scrollback_buffer = num;
+ if (c == 'c')
+ option_context_lines = num;
+ if (c == 'D')
+ {
+ if (copt == 'k') m_reqW = -1;
+ else sscanf(optarg,"%dx%d",&m_reqW,&m_reqH);
+ m_fullscreen = 1;
+ }
+ if (c == 'm')
+ m_timerinterval = limit(num,10,1000000);
+ if (c == 'N')
+ user_names_format = copt;
+ if (c == '@')
+ m_reslist_file = optarg;
+ if (c == 'I')
+ m_setupfile = optarg;
+ if (c == 'f')
+ user_foreground = num;
+ if (c == 'F')
+ m_fullscreen = 1;
+ if (c == 'h')
+ user_screen_height = num;
+ if (c == 'i')
+ option_ignore_errors = 1;
+ if (c == 'l')
+ option_left_margin = num;
+ if (c == 'L')
+ m_localfiles = true;
+ if (c == 'q')
+ m_no_sound = 1;
+ if (c == 'o')
+ option_object_movement = 1;
+ if (c == 'O')
+ option_object_locating = 1;
+ if (c == 'p')
+ option_piracy = 1;
+ if (c == 'r')
+ option_right_margin = num;
+ if (c == 'R')
+ option_save_quetzal = 0;
+ if (c == 's')
+ m_random_seed = num;
+ if (c == 'S')
+ option_script_cols = num;
+ if (c == 't')
+ user_tandy_bit = 1;
+ if (c == 'T')
+ sf_osdialog = NULL;
+ if (c == 'u')
+ option_undo_slots = num;
+ if (c == 'V')
+ m_vga_fonts = 1;
+ if (c == 'w')
+ user_screen_width = num;
+ if (c == 'x')
+ option_expand_abbreviations = 1;
+ if (c == 'Z')
+ if (num >= ERR_REPORT_NEVER && num <= ERR_REPORT_FATAL)
+ err_report_mode = num;
+ if (c == '?')
+ optind = argc;
+ } while (c != EOF && c != '?');
+
+ }/* parse_options */
+
+
+
+/*
+ * os_process_arguments
+ *
+ * Handle command line switches. Some variables may be set to activate
+ * special features of Frotz:
+ *
+ * option_attribute_assignment
+ * option_attribute_testing
+ * option_context_lines
+ * option_object_locating
+ * option_object_movement
+ * option_left_margin
+ * option_right_margin
+ * option_ignore_errors
+ * option_piracy
+ * option_undo_slots
+ * option_expand_abbreviations
+ * option_script_cols
+ *
+ * The global pointer "story_name" is set to the story file name.
+ *
+ */
+
+void os_process_arguments (int argc, char *argv[])
+ {
+ const char *p;
+ int i;
+
+ // install signal handlers
+
+ sf_installhandlers();
+
+ /* Parse command line options */
+
+ parse_options(argc, argv);
+
+ if (optind != argc - 1)
+ {
+ usage();
+ exit (EXIT_FAILURE);
+ }
+
+ /* Set the story file name */
+
+ story_name = argv[optind];
+
+ // load resources
+ // it's useless to test the retval, as in case of error it does not return
+ sf_load_resources( story_name);
+
+ /* Strip path and extension off the story file name */
+
+ p = story_name;
+
+ for (i = 0; story_name[i] != 0; i++)
+ if (story_name[i] == '\\' || story_name[i] == '/'
+ || story_name[i] == ':')
+ p = story_name + i + 1;
+
+ for (i = 0; p[i] != 0 && p[i] != '.'; i++)
+ stripped_story_name[i] = p[i];
+
+ stripped_story_name[i] = 0;
+
+ /* Create nice default file names */
+
+ strcpy (script_name, stripped_story_name);
+ strcpy (command_name, stripped_story_name);
+ strcpy (save_name, stripped_story_name);
+ strcpy (auxilary_name, stripped_story_name);
+
+ strcat (script_name, ".scr");
+ strcat (command_name, ".rec");
+ strcat (save_name, ".sav");
+ strcat (auxilary_name, ".aux");
+
+ /* Save the executable file name */
+
+ progname = argv[0];
+
+ sf_readsettings();
+
+ if (user_screen_width > 0) AcWidth = user_screen_width;
+ if (user_screen_height > 0) AcHeight = user_screen_height;
+
+ if (user_names_format) m_names_format = user_names_format;
+
+ if (user_background != -1) m_defaultBack = sf_GetColour(user_background);
+ if (user_foreground != -1) m_defaultFore = sf_GetColour(user_foreground);
+ if (user_tandy_bit != -1) m_tandy = user_tandy_bit;
+
+ sf_initfonts();
+
+ }/* os_process_arguments */
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <time.h>
+#include <sys/time.h>
+#endif
+
+#ifdef WIN32
+
+void sf_sleep( int msecs){
+ Sleep(msecs);
+ }
+
+unsigned long sf_ticks( void){
+ return (GetTickCount());
+ }
+
+#else
+
+//#include <unistd.h>
+
+void sf_sleep( int msecs){
+ usleep(msecs/1000);
+ }
+
+unsigned long sf_ticks (void) {
+ struct timeval now;
+ static struct timeval start;
+ static int started = 0;
+ unsigned long ticks;
+ now.tv_sec = now.tv_usec = 0;
+ gettimeofday(&now, NULL);
+ if (!started){
+ started = 1;
+ start = now;
+ }
+ ticks = (now.tv_sec-start.tv_sec)*1000 + (now.tv_usec-start.tv_usec)/1000;
+// ticks = now.tv_sec*1000 + now.tv_usec/1000;
+ return ticks;
+ }
+
+#endif
+
+/*
+ * os_read_file_name
+ *
+ * Return the name of a file. Flag can be one of:
+ *
+ * FILE_SAVE - Save game file
+ * FILE_RESTORE - Restore game file
+ * FILE_SCRIPT - Transscript file
+ * FILE_RECORD - Command file for recording
+ * FILE_PLAYBACK - Command file for playback
+ * FILE_SAVE_AUX - Save auxilary ("preferred settings") file
+ * FILE_LOAD_AUX - Load auxilary ("preferred settings") file
+ *
+ * The length of the file name is limited by MAX_FILE_NAME. Ideally
+ * an interpreter should open a file requester to ask for the file
+ * name. If it is unable to do that then this function should call
+ * print_string and read_string to ask for a file name.
+ *
+ */
+
+extern char stripped_story_name[];
+
+static char *getextension( int flag)
+ {
+ char *ext = ".aux";
+
+ if (flag == FILE_SAVE || flag == FILE_RESTORE)
+ ext = ".sav";
+ else if (flag == FILE_SCRIPT)
+ ext = ".scr";
+ else if (flag == FILE_RECORD || flag == FILE_PLAYBACK)
+ ext = ".rec";
+
+ return ext;
+ }
+
+static bool newfile( int flag)
+ {
+ if (flag == FILE_SAVE || flag == FILE_SAVE_AUX || flag == FILE_RECORD)
+ return true;
+ return false;
+ }
+
+static char buf[FILENAME_MAX];
+
+static const char *getnumbername( const char *def, char *ext)
+ {
+ int len, number = 0;
+ strcpy(buf,stripped_story_name);
+ len = strlen(buf);
+ for (;;){
+ sprintf(buf+len,"%03d%s",number++,ext);
+ if (access(buf,F_OK)) break;
+ }
+ return buf;
+ }
+
+static const char *getdatename( const char *def, char *ext)
+ {
+ char *p;
+
+ time_t t; struct tm *tm;
+ time(&t);
+ tm = localtime(&t);
+
+ strcpy(buf,stripped_story_name);
+ p = buf + 1;
+ if (*p) p++;
+ sprintf(p,"%04d%02d%02d%02d%02d%s",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, ext);
+ return buf;
+ }
+
+// fdialog( existing, defname, filter, title, &resultstr)
+static int ingame_read_file_name (char *file_name, const char *default_name, int flag);
+static int dialog_read_file_name (char *file_name, const char *default_name, int flag);
+
+int os_read_file_name (char *file_name, const char *default_name, int flag)
+ {
+ int st;
+ const char *initname = default_name;
+ char *ext = ".aux";
+
+ if (newfile(flag))
+ {
+ char *ext = getextension(flag);
+ if (m_names_format == 'd') initname = getdatename(initname,ext);
+ else if (m_names_format == 'n') initname = getnumbername(initname,ext);
+ }
+
+ st = dialog_read_file_name( file_name, initname, flag);
+ if (st == SF_NOTIMP) st = ingame_read_file_name( file_name, initname, flag);
+ return st;
+ }
+
+static int ingame_read_file_name (char *file_name, const char *default_name, int flag)
+ {
+ char *extension;
+ FILE *fp;
+ bool terminal;
+ bool result;
+
+ bool saved_replay = istream_replay;
+ bool saved_record = ostream_record;
+
+ /* Turn off playback and recording temporarily */
+
+ istream_replay = FALSE;
+ ostream_record = FALSE;
+
+ /* Select appropriate extension */
+
+ extension = getextension(flag);
+
+ /* Input file name (reserve four bytes for a file name extension) */
+
+ print_string ("Enter file name (\"");
+ print_string (extension);
+ print_string ("\" will be added).\nDefault is \"");
+ print_string (default_name);
+ print_string ("\": ");
+
+ read_string (MAX_FILE_NAME - 4, (byte *) file_name);
+
+ /* Use the default name if nothing was typed */
+
+ if (file_name[0] == 0)
+ strcpy (file_name, default_name);
+ if (strchr (file_name, '.') == NULL)
+ strcat (file_name, extension);
+
+ /* Make sure it is safe to use this file name */
+
+ result = TRUE;
+
+ /* OK if the file is opened for reading */
+
+ if (!newfile(flag))
+ goto finished;
+
+ /* OK if the file does not exist */
+
+ if ((fp = fopen (file_name, "rb")) == NULL)
+ goto finished;
+
+ /* OK if this is a pseudo-file (like PRN, CON, NUL) */
+
+ terminal = isatty(fileno(fp));
+
+ fclose (fp);
+
+ if (terminal)
+ goto finished;
+
+ /* OK if user wants to overwrite */
+
+ result = read_yes_or_no ("Overwrite existing file");
+
+finished:
+
+ /* Restore state of playback and recording */
+
+ istream_replay = saved_replay;
+ ostream_record = saved_record;
+
+ return result;
+
+ }/* os_read_file_name */
+
+static int dialog_read_file_name(char *file_name, const char *default_name, int flag)
+ {
+ int filter = 0;
+ int title = 0, st;
+ char *res;
+
+ sf_flushdisplay();
+// theWnd->ResetOverhang();
+
+ switch (flag)
+ {
+ case FILE_SAVE:
+ filter = IDS_SAVE_FILTER;
+ title = IDS_SAVE_TITLE;
+ break;
+ case FILE_RESTORE:
+ filter = IDS_SAVE_FILTER;
+ title = IDS_RESTORE_TITLE;
+ break;
+ case FILE_SCRIPT:
+ filter = IDS_SCRIPT_FILTER;
+ title = IDS_SCRIPT_TITLE;
+ break;
+ case FILE_RECORD:
+ filter = IDS_RECORD_FILTER;
+ title = IDS_RECORD_TITLE;
+ break;
+ case FILE_PLAYBACK:
+ filter = IDS_RECORD_FILTER;
+ title = IDS_PLAYBACK_TITLE;
+ break;
+ case FILE_SAVE_AUX:
+ filter = IDS_AUX_FILTER;
+ title = IDS_SAVE_AUX_TITLE;
+ break;
+ case FILE_LOAD_AUX:
+ filter = IDS_AUX_FILTER;
+ title = IDS_LOAD_AUX_TITLE;
+ break;
+ default:
+ return 0;
+ }
+
+// fdialog( existing, defname, filter, title, &resultstr)
+// returns 0 if OK
+ st = sf_user_fdialog( !newfile(flag), default_name, sf_msgstring(filter), sf_msgstring(title), &res);
+ if (st == SF_NOTIMP) return st;
+ if (st == 0)
+ {
+ strncpy(file_name,res,MAX_FILE_NAME);
+ file_name[MAX_FILE_NAME-1] = 0;
+ return 1;
+ }
+ return 0;
+ }
+
+typedef struct {
+ void *link;
+ char *str;
+ } Dynstr;
+
+static Dynstr * strings = NULL;
+
+static void freestrings()
+ {
+ while (strings)
+ {
+ Dynstr *r = strings->link;
+ if (strings->str) free(strings->str);
+ free(strings);
+ strings = r;
+ }
+ }
+
+static char *mystrdup( char *p)
+ {
+ Dynstr *r;
+ if (!p) return p;
+ p = strdup(p);
+ if (!p) return p;
+ r = calloc(1,sizeof(Dynstr));
+ if (r)
+ {
+ if (!strings) CLEANREG(freestrings);
+ r->link = strings;
+ r->str = p;
+ strings = r;
+ }
+ return p;
+ }
+
+static char *rc = NULL;
+
+void sf_FinishProfile()
+ {
+//printf("finishprofile\n");
+ if (!rc) return;
+ free(rc);
+ rc = NULL;
+ }
+
+void sf_InitProfile( const char *fn)
+ {
+ FILE *f; int size; char *s, *d;
+
+ if (!fn) return;
+ f = fopen(fn,"rb");
+ if (!f) return;
+ fseek(f,0,SEEK_END);
+ size = ftell(f);
+ if (!size) { fclose(f); return;}
+ rc = malloc(size+1);
+ if (!rc) { fclose(f); return;}
+ fseek(f,0,0);
+ fread(rc,1,size,f);
+ fclose(f);
+ rc[size] = 0;
+
+ s = d = rc;
+
+ while (*s)
+ {
+ if (*s == '#')
+ {
+ while ((*s) && (*s != '\n')) s++;
+ if (!*s) break;
+ }
+ else
+ *d++ = *s++;
+ }
+ *d = 0;
+
+ CLEANREG(sf_FinishProfile);
+ }
+
+
+static char * findsect( const char *sect)
+ {
+ int ns = strlen(sect);
+ char *r = rc;
+ while (r)
+ {
+//printf("{%s}\n",r);
+ r = strchr(r,'[');
+ if (!r) return NULL;
+ r++;
+ if (strncmp(r,sect,ns)) continue;
+ return (r+ns);
+ }
+ }
+
+static char * findid( const char *sect, const char *id)
+ {
+ int nid = strlen(id);
+ char *p, *r, *sav, *rq, *fnd = NULL;
+ r = findsect(sect);
+//printf("findsect(%s) %p\n",sect,r);
+ if (!r) return NULL;
+ sav = strchr(r,'['); if (sav) *sav = 0;
+ while (r)
+ {
+ r = strstr(r,id);
+ if (!r) break;
+ rq = r+nid;
+ if ((*(byte *)(r-1) <= ' ') && ((*rq == ' ') || (*rq == '=')))
+ {
+ while (*rq) if (*rq++ == '=') break;
+ if (*rq) { fnd = rq; break;}
+ }
+ r = rq;
+ }
+ if (sav) *sav = '[';
+ return fnd;
+ }
+
+int sf_GetProfileInt( const char *sect, const char *id, int def)
+ {
+ if (rc)
+ {
+ char *p = findid(sect,id);
+ if (p) def = atoi(p);
+ }
+ return def;
+ }
+
+double sf_GetProfileDouble( const char *sect, const char *id, double def)
+ {
+ if (rc)
+ {
+ char *p = findid(sect,id);
+ if (p) def = atof(p);
+ }
+ return def;
+ }
+
+char * sf_GetProfileString( const char *sect, const char *id, char * def)
+ {
+ char *q=NULL, sav=0;
+ if (rc)
+ {
+ char *p = findid(sect,id);
+//printf("findid(%s,%s) %p\n",sect,id,p);
+ if (p)
+ {
+ int quoted = 0;
+ while (*p)
+ {
+ if (*p == '\"') { quoted = 1; p++; break;}
+ if ((byte)(*p) > ' ') break;
+ }
+ if (*p)
+ {
+ if (quoted) q = strchr(p,'\"');
+ if (!q)
+ {
+ q = p;
+ while (*q > ' ') q++;
+ sav = *q; *q = 0;
+ }
+ }
+ def = p;
+ }
+ }
+ if (def) def = mystrdup(def);
+ if (sav) *q = sav;
+ return def;
+ }
+
+// A. Local file header:
+//
+// local file header signature 0 4 bytes (0x04034b50)
+// version needed to extract 4 2 bytes
+// general purpose bit flag 6 2 bytes
+// compression method 8 2 bytes
+// last mod file time 10 2 bytes
+// last mod file date 12 2 bytes
+// crc-32 14 4 bytes
+// compressed size 18 4 bytes
+// uncompressed size 22 4 bytes
+// file name length 26 2 bytes
+// extra field length 28 2 bytes
+//
+// file name (variable size)
+// extra field (variable size)
+
+#define plong( b) (((int)((b)[3]) << 24) + ((int)((b)[2]) << 16) +\
+ ((int)((b)[1]) << 8) + (int)((b)[0]))
+
+#define pshort( b) (((int)((b)[1]) << 8) + (int)((b)[0]))
+
+static int myunzip( int csize, byte *cdata, byte *udata)
+ {
+ byte window[32768];
+ z_stream z;
+ int st;
+
+ unsigned myin( void *d, byte **b){return 0;}
+ int myout( void *d, byte *b, unsigned n)
+ {
+ memcpy(udata,b,n); udata += n;
+ return 0;
+ }
+
+ memset(&z,0,sizeof(z));
+
+ st = inflateBackInit( &z, 15, window);
+ if (st) return st;
+
+ z.next_in = cdata;
+ z.avail_in = csize;
+
+ for (;;){
+ st = inflateBack( &z, myin, NULL, myout, NULL);
+ if (st == Z_STREAM_END) break;
+ if (st) return st;
+ }
+
+ st = inflateBackEnd(&z);
+ return st;
+ }
+
+int sf_pkread( FILE *f, int foffs, void ** out, int *size)
+ {
+ byte hd[30], *dest;
+ byte *data, *cdata;
+ int csize, usize, cmet, skip, st;
+
+ fseek(f,foffs,SEEK_SET);
+ fread(hd,1,30,f);
+ cmet = pshort(hd+8);
+ if (cmet != 8) return -10;
+ csize = plong(hd+18);
+ usize = plong(hd+22);
+ if (csize <= 0) return -11;
+ if (usize <= 0) return -12;
+ data = malloc(usize);
+ if (!data) return -13;
+ cdata = malloc(csize);
+ if (!cdata){ free(data); return -14;}
+ skip = pshort(hd+26) + pshort(hd+28);
+ fseek(f,foffs+30+skip,SEEK_SET);
+ fread(cdata,1,csize,f);
+//printf("%02x csize %d usize %d skip %d\n",cdata[0],csize,usize,skip);
+
+ st = myunzip(csize,cdata,data);
+
+ free(cdata);
+ if (st)
+ {
+ free(data);
+ return st;
+ }
+ *out = (void *)data;
+ *size = usize;
+ return st;
+ }
+
+//////////////////////////
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+static char * getexepath( char *buf){
+#ifdef WIN32
+ buf[0] = 0;
+ GetModuleFileName(NULL,buf,262);
+#else
+ char baf[80];
+ int n;
+ sprintf(baf,"/proc/%d/exe",getpid());
+ n = readlink(baf,buf,262);
+ if (n < 0) n = 0;
+ buf[n] = 0;
+#endif
+ return buf;
+ }
+
+#ifndef WIN32
+#define _stat stat
+#endif
+
+static char * getbuilddatetime( int tf){
+ time_t t; struct tm *tm;
+ struct _stat sta;
+ static char buf[263];
+
+ getexepath(buf);
+ _stat(buf,&sta);
+ t = sta.st_mtime;
+ tm = localtime(&t);
+ buf[0] = 0;
+ sprintf(buf,"%04d%02d%02d",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
+ if (tf){
+ strcat(buf,".");
+ sprintf(buf+strlen(buf),"%02d%02d%02d",
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ }
+ return buf;
+ }
+
+
--- /dev/null
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+//#include <signal.h>\r
+\r
+//#define STATIC\r
+\r
+#include <SDL.h>\r
+\r
+#include "sf_frotz.h"\r
+\r
+static SDL_Rect blitrect = {0,0,0,0};\r
+static char banner[256];\r
+static int isfullscreen;\r
+static ulong *sbuffer = NULL;\r
+static int sbpitch; // in longs\r
+static int dirty = 0;\r
+static int bitsperpixel = 32;\r
+static int RBswap = 0;\r
+static int ewidth, eheight;\r
+static SDL_Surface *screen, *off = NULL;\r
+static int mustlockscreen = 0;\r
+int m_timerinterval = 100;\r
+\r
+static void sf_quitconf();\r
+\r
+// clipping region\r
+static int xmin,xmax,ymin,ymax;\r
+\r
+void sf_setclip( int x, int y, int w, int h)\r
+ {\r
+ if (x < 0){ w += x; x = 0;}\r
+ if (x+w > ewidth) w = ewidth-x;\r
+ if (y < 0){ h += y; y = 0;}\r
+ if (y+h > eheight) h = eheight-y;\r
+ xmin = x; xmax = x+w;\r
+ ymin = y; ymax = y+h;\r
+ }\r
+\r
+void sf_getclip( int *x, int *y, int *w, int *h)\r
+ {\r
+ *x = xmin; *y = ymin;\r
+ *w = xmax-xmin; *h = ymax-ymin;\r
+ }\r
+\r
+static int mywcslen( zword *b)\r
+ {\r
+ int n=0;\r
+ while (*b++) n++;\r
+ return n;\r
+ }\r
+\r
+static void myGrefresh(){\r
+ if (off) {\r
+ if (mustlockscreen) SDL_LockSurface(screen);\r
+ SDL_BlitSurface(off,NULL,screen,&blitrect);\r
+ if (mustlockscreen) SDL_UnlockSurface(screen);\r
+ }\r
+ SDL_UpdateRect(screen,0,0,0,0);\r
+ }\r
+\r
+void sf_wpixel( int x, int y, ulong c)\r
+ {\r
+ if (x < xmin || x >= xmax || y < ymin || y >= ymax) return;\r
+ sbuffer[x+sbpitch*y] = c;\r
+ dirty = 1;\r
+ }\r
+\r
+ulong sf_rpixel( int x, int y)\r
+ {\r
+ if (x < 0 || x >= ewidth || y < 0 || y >= eheight) return 0;\r
+ return sbuffer[x+sbpitch*y];\r
+ }\r
+\r
+#define MAXCUR 64\r
+static ulong savedcur[MAXCUR];\r
+\r
+static void drawthecursor( int x, int y, int onoff)\r
+ {\r
+ SF_textsetting * ts = sf_curtextsetting();\r
+ int i, h = ts->font->height(ts->font);\r
+ if (h > MAXCUR) h = MAXCUR;\r
+ if (onoff)\r
+ {\r
+ for (i=0;i<h;i++) \r
+ {\r
+ savedcur[i] = sf_rpixel(x,y+i);\r
+ sf_wpixel(x,y+i,ts->fore);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for (i=0;i<h;i++) \r
+ {\r
+ sf_wpixel(x,y+i,savedcur[i]);\r
+ }\r
+ }\r
+ }\r
+\r
+bool sf_IsValidChar(unsigned short c)\r
+ {\r
+ if (c >= ZC_ASCII_MIN && c <= ZC_ASCII_MAX)\r
+ return true;\r
+ if (c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX)\r
+ return true;\r
+ if (c >= 0x100)\r
+ return true;\r
+ return false;\r
+ }\r
+\r
+void sf_drawcursor( bool c)\r
+ {\r
+ SF_textsetting * ts = sf_curtextsetting();\r
+ drawthecursor(ts->cx,ts->cy,c);\r
+ }\r
+\r
+void sf_chline( int x, int y, ulong c, int n)\r
+ {\r
+ ulong *s;\r
+ if (y < ymin || y >= ymax) return;\r
+ if (x < xmin){ n += x-xmin; x = xmin;}\r
+ if (x+n > xmax) n = xmax-x;\r
+ if (n <= 0) return;\r
+ s = sbuffer+x+sbpitch*y;\r
+ while (n--) *s++ = c;\r
+ dirty = 1;\r
+ }\r
+\r
+void sf_cvline( int x, int y, ulong c, int n)\r
+ {\r
+ ulong *s;\r
+ if (x < xmin || x >= xmax) return;\r
+ if (y < xmin){ n += y-ymin; y = ymin;}\r
+ if (y+n > ymax) n = ymax-y;\r
+ if (n <= 0) return;\r
+ s = sbuffer+x+sbpitch*y;\r
+ while (n--) { *s = c; s += sbpitch;}\r
+ dirty = 1;\r
+ }\r
+\r
+\r
+ulong sf_blendlinear( int a, ulong s, ulong d){\r
+ ulong r;\r
+ r = ((s & 0xff)*a + (d & 0xff)*(256-a))>>8;\r
+ s >>= 8; d >>= 8;\r
+ r |= (((s & 0xff)*a + (d & 0xff)*(256-a))>>8)<<8;\r
+ s >>= 8; d >>= 8;\r
+ r |= (((s & 0xff)*a + (d & 0xff)*(256-a))>>8)<<16;\r
+ return r;\r
+ }\r
+\r
+void sf_writeglyph( SF_glyph *g)\r
+ {\r
+ SF_textsetting *ts = sf_curtextsetting();\r
+\r
+ int i,j,m;\r
+\r
+ int w = g->dx;\r
+ int weff = g->xof+g->w;\r
+ byte * bmp = (byte *)(&(g->bitmap[0]));\r
+ int h = g->h;\r
+ int nby = (g->w+7)/8;\r
+ int byw = g->w;\r
+\r
+ int x = ts->cx;\r
+ int y = ts->cy;\r
+\r
+ int dxpre = g->xof;\r
+ int dypre = ts->font->ascent(ts->font)-h-(int)g->yof;\r
+\r
+ int height = ts->font->height(ts->font);\r
+ int width;\r
+\r
+ ulong color, bc;\r
+\r
+ if ((ts->style & REVERSE_STYLE) != 0)\r
+ {\r
+ bc = ts->fore;\r
+ color = ts->back;\r
+ }\r
+ else\r
+ {\r
+ color = ts->fore;\r
+ bc = ts->back;\r
+ }\r
+\r
+ // compute size and position of background rect\r
+\r
+ if (weff < w) weff = w;\r
+ width = weff - ts->oh;\r
+ if ((width > 0) && (ts->backTransparent == 0))\r
+ sf_fillrect(bc,x+ts->oh,y,width,height);\r
+\r
+ x += dxpre;\r
+ y += dypre;\r
+\r
+//printf("\n");\r
+ for (i=0;i<h;i++)\r
+ {\r
+ int xx = 0;\r
+ if (ts->font->antialiased)\r
+ for (m=0;m<byw;m++)\r
+ {\r
+ int t = *bmp++;\r
+ if (xx < byw)\r
+ {\r
+ if (t)\r
+ {\r
+ ulong sval = color;\r
+ if (t < 255)\r
+ sval = sf_blend((int)(t + (t>>7)),sval,sf_rpixel(x+xx,y));\r
+ sf_wpixel( x+xx, y, sval);\r
+ }\r
+ }\r
+ xx++;\r
+ }\r
+ else\r
+ for (m=0;m<nby;m++)\r
+ {\r
+ int t = *bmp++;\r
+ for (j=0;j<8;j++, t *= 2)\r
+ {\r
+ if (xx < byw)\r
+ {\r
+ if (t & 0x80)\r
+ sf_wpixel( x+xx, y, color);\r
+ }\r
+ xx++;\r
+ }\r
+ }\r
+ y++;\r
+ }\r
+\r
+ ts->cx += (w);\r
+ ts->oh = (weff > w) ? weff-w : 0;\r
+ }\r
+\r
+\r
+void sf_fillrect( unsigned long color, int x, int y, int w, int h)\r
+ {\r
+ ulong *dst;\r
+ int i;\r
+//printf("fillrect %x %d %d %d %d\n",color,x,y,w,h);\r
+//printf("dst%p sbpitch%d\n",dst,sbpitch);\r
+ if (x < xmin){ w += x-xmin; x = xmin;}\r
+ if (x+w > xmax) w = xmax-x;\r
+ if (w <= 0) return;\r
+ if (y < ymin){ h += y-ymin; y = ymin;}\r
+ if (y+h > ymax) h = ymax-y;\r
+ if (h <= 0) return;\r
+ dst = sbuffer+x+sbpitch*y;\r
+ while (h--)\r
+ {\r
+ for (i=0;i<w;i++) dst[i] = color;\r
+ dst += sbpitch;\r
+ }\r
+ dirty = 1;\r
+ }\r
+\r
+void sf_rect( unsigned long color, int x, int y, int w, int h)\r
+ {\r
+ sf_chline(x,y,color,w);\r
+ sf_chline(x,y+h-1,color,w);\r
+ sf_cvline(x,y,color,h);\r
+ sf_cvline(x+w-1,y,color,h);\r
+ }\r
+\r
+\r
+void sf_flushtext()\r
+ {\r
+ SF_textsetting *ts = sf_curtextsetting();\r
+ ts->cx += ts->oh;\r
+ ts->oh = 0;\r
+ }\r
+\r
+/*\r
+ * os_erase_area\r
+ *\r
+ * Fill a rectangular area of the screen with the current background\r
+ * colour. Top left coordinates are (1,1). The cursor does not move.\r
+ *\r
+ * The final argument gives the window being changed, -1 if only a\r
+ * portion of a window is being erased, or -2 if the whole screen is\r
+ * being erased.\r
+ *\r
+ */\r
+void os_erase_area(int top, int left, int bottom, int right, int win)\r
+ {\r
+ sf_flushtext();\r
+ sf_fillrect((sf_curtextsetting())->back,left-1,top-1,right-left+1,bottom-top+1);\r
+// theWnd->FillBackground(CRect(left-1,top-1,right,bottom));\r
+ }\r
+\r
+/*\r
+ * os_peek_colour\r
+ *\r
+ * Return the colour of the screen unit below the cursor. (If the\r
+ * interface uses a text mode, it may return the background colour\r
+ * of the character at the cursor position instead.) This is used\r
+ * when text is printed on top of pictures. Note that this coulor\r
+ * need not be in the standard set of Z-machine colours. To handle\r
+ * this situation, Frotz entends the colour scheme: Colours above\r
+ * 15 (and below 256) may be used by the interface to refer to non\r
+ * standard colours. Of course, os_set_colour must be able to deal\r
+ * with these colours.\r
+ *\r
+ */\r
+int os_peek_colour(void)\r
+ {\r
+ SF_textsetting *ts = sf_curtextsetting();\r
+ sf_flushtext();\r
+ return sf_GetColourIndex(sf_rpixel(ts->cx,ts->cy));\r
+ }\r
+\r
+static void scroll( int x, int y, int w, int h, int n)\r
+ {\r
+ ulong *src, *dst;\r
+ int nmove, step;\r
+ if (n > 0)\r
+ {\r
+ dst = sbuffer+x+sbpitch*y;\r
+ src = dst + n*sbpitch;\r
+ nmove = h-n;\r
+ step = sbpitch;\r
+ }\r
+ else if (n < 0)\r
+ {\r
+ n = -n;\r
+ nmove = h-n;\r
+ step = -sbpitch;\r
+ src = sbuffer+x+sbpitch*(y+nmove-1);\r
+ dst = src + n*sbpitch;\r
+ }\r
+ else\r
+ return;\r
+ if (nmove > 0)\r
+ {\r
+ while (nmove--)\r
+ {\r
+ memcpy(dst,src,w*sizeof(ulong));\r
+ dst += step;\r
+ src += step;\r
+ }\r
+ dirty = 1;\r
+ }\r
+ }\r
+\r
+void sf_flushdisplay()\r
+ {\r
+ if (dirty) myGrefresh();\r
+ dirty = 0;\r
+ }\r
+\r
+/*\r
+ * os_scroll_area\r
+ *\r
+ * Scroll a rectangular area of the screen up (units > 0) or down\r
+ * (units < 0) and fill the empty space with the current background\r
+ * colour. Top left coordinates are (1,1). The cursor stays put.\r
+ *\r
+ */\r
+void os_scroll_area(int top, int left, int bottom, int right, int units)\r
+ {\r
+ sf_flushtext();\r
+// theWnd->ResetOverhang();\r
+\r
+ scroll(left-1,top-1,right-left+1,bottom-top+1,units);\r
+ if (units > 0)\r
+ sf_fillrect((sf_curtextsetting())->back,left-1,bottom-units,right-left+1,units);\r
+ else if (units < 0)\r
+ sf_fillrect((sf_curtextsetting())->back,left-1,top-1,right-left+1,units);\r
+\r
+// if (theApp.GetFastScrolling() == false)\r
+// sf_flushdisplay();\r
+// theWnd->FlushDisplay();\r
+ }\r
+\r
+int SFdticks = 200;\r
+volatile bool SFticked = 0;\r
+static SDL_TimerID timerid = 0;\r
+\r
+static Uint32 mytimer( Uint32 inter, void *parm)\r
+ {\r
+ SFticked = true;\r
+ return inter;\r
+ }\r
+\r
+static void cleanvideo()\r
+ {\r
+ if (timerid) SDL_RemoveTimer(timerid);\r
+ SDL_Quit();\r
+ }\r
+\r
+#define RM 0x0000ff\r
+#define GM 0x00ff00\r
+#define BM 0xff0000\r
+\r
+static int check( int R, int G, int B){\r
+ if (R==RM && G==GM && B==BM) return 0;\r
+ if (R==BM && G==GM && B==RM) return -1;\r
+ return 1;\r
+ }\r
+\r
+extern char stripped_story_name[];\r
+\r
+void sf_initvideo( int W, int H, int full)\r
+ {\r
+ int desired_bpp, needoff, reqW, reqH;\r
+ Uint32 video_flags;\r
+ SDL_PixelFormat *format;\r
+ Uint32 initflags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER |\r
+ SDL_INIT_AUDIO;\r
+\r
+ sprintf(banner,"SDL Frotz v%d.%02d - %s (z%d)",SFROTZ_MAJOR,SFROTZ_MINOR,\r
+ stripped_story_name,h_version);\r
+ desired_bpp = 32;\r
+ video_flags = 0;\r
+\r
+ if ( SDL_Init(initflags) < 0 ) {\r
+ os_fatal("Couldn't initialize SDL: %s", SDL_GetError());\r
+ }\r
+\r
+ CLEANREG(cleanvideo);\r
+\r
+ isfullscreen = full;\r
+ if (full) video_flags = SDL_FULLSCREEN;\r
+ reqW = W; reqH = H;\r
+ if (full)\r
+ {\r
+ if (m_reqW == -1)\r
+ {\r
+ const SDL_VideoInfo * v = SDL_GetVideoInfo();\r
+ if (v) { m_reqW = v->current_w; m_reqH = v->current_h;}\r
+ }\r
+ if (m_reqW > reqW) reqW = m_reqW;\r
+ if (m_reqH > reqH) reqH = m_reqH;\r
+ }\r
+ screen = SDL_SetVideoMode( reqW, reqH, desired_bpp, video_flags);\r
+ if ( screen == NULL ) {\r
+ os_fatal("Couldn't set %dx%dx%d video mode: %s",\r
+ reqW, reqH, desired_bpp, SDL_GetError());\r
+ }\r
+ SDL_WM_SetCaption(banner,NULL);\r
+ bitsperpixel = 32;\r
+\r
+ mustlockscreen = SDL_MUSTLOCK(screen);\r
+ format = screen->format;\r
+\r
+ needoff = (mustlockscreen) || (screen->w != W) || (screen->h != H) ||\r
+ (format->BitsPerPixel != 24) ||\r
+ (screen->pitch != 3*W);\r
+\r
+ RBswap = 0;\r
+ if (!needoff) {\r
+ needoff = check(format->Rmask,format->Gmask,format->Bmask);\r
+ if ((needoff == -1)){\r
+ RBswap = 1;\r
+ needoff = 0;\r
+ }\r
+ else\r
+ needoff = 1;\r
+ }\r
+// printf("setvideo: gm %dx%d rq %dx%d(f%d) got %dx%d needoff %d\n", W,H,reqW,reqH,full,screen->w,screen->h,needoff);\r
+\r
+ if (needoff) {\r
+ sbuffer = calloc(W*H,sizeof(ulong));\r
+ if (!sbuffer){\r
+ os_fatal("Could not create gc");\r
+ }\r
+ off = SDL_CreateRGBSurfaceFrom(sbuffer,\r
+ W,H,32,4*W,0xff,0xff00,0xff0000,0);\r
+// off = SDL_CreateRGBSurfaceFrom(sbuffer,\r
+// W,H,32,4*screen->w,0xff,0xff00,0xff0000,0);\r
+ if (!off){\r
+ os_fatal("Could not create offscreen surface");\r
+ }\r
+ }\r
+ else {\r
+ sbuffer = (ulong *)screen->pixels;\r
+ }\r
+\r
+ blitrect.w = W; blitrect.h = H;\r
+ blitrect.x = (screen->w - W)/2;\r
+ blitrect.y = (screen->h - H)/2;\r
+ SDL_EnableUNICODE(1);\r
+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);\r
+\r
+ SDL_AddTimer(SFdticks,mytimer,NULL);\r
+\r
+ xmin = ymin = 0;\r
+ xmax = ewidth = W;\r
+ ymax = eheight = H;\r
+ sbpitch = W;\r
+ dirty = 1;\r
+ }\r
+\r
+/*\r
+ * os_draw_picture\r
+ *\r
+ * Display a picture at the given coordinates.\r
+ *\r
+ */\r
+void os_draw_picture(int picture, int y, int x)\r
+ {\r
+ int ew, eh, xx, yy, ix, iy, d;\r
+ int ox, oy, ow, oh;\r
+ Zwindow * winpars;\r
+ sf_picture * pic = sf_getpic(picture);\r
+ ulong *src, *dst, sval, dval, alpha;\r
+\r
+ sf_flushtext();\r
+\r
+ if (!pic) return;\r
+ if (!pic->pixels) return; // TODO: rect\r
+ src = (ulong *) pic->pixels;\r
+\r
+ x--; y--;\r
+ ew = m_gfxScale*pic->width;\r
+ eh = m_gfxScale*pic->height;\r
+\r
+ // this takes care of the fact taht x, y are really 16 bit values\r
+ if (x & 0x8000) x |= 0xffff0000;\r
+ if (y & 0x8000) y |= 0xffff0000;\r
+\r
+ // get current window rect\r
+ sf_getclip(&ox,&oy,&ow,&oh);\r
+ winpars = curwinrec();\r
+ sf_setclip( winpars->x_pos-1, winpars->y_pos-1, winpars->x_size, winpars->y_size);\r
+\r
+ // clip taking into account possible origin\r
+ // outside the clipping rect\r
+ if (x < xmin) { d = xmin-x; ew -= d; x = xmin; src += d;}\r
+ if (x+ew > xmax) ew = xmax - x;\r
+ ew /= m_gfxScale;\r
+\r
+ if (y < ymin) { d = ymin-y; eh -= d; y = ymin; src += d*pic->width;}\r
+ if (y+eh > ymax) eh = ymax-y;\r
+ eh /= m_gfxScale;\r
+\r
+ sf_setclip(ox,oy,ow,oh);\r
+\r
+ if (ew <= 0) return;\r
+ if (eh <= 0) return;\r
+\r
+ for (yy=0;yy<eh;yy++)\r
+ {\r
+ for (xx=0;xx<ew;xx++)\r
+ {\r
+ dst = sbuffer + x +xx*m_gfxScale + sbpitch*(y + yy*m_gfxScale);\r
+ sval = src[xx];\r
+ alpha = (sval >> 24);\r
+ if (alpha == 255)\r
+ dval = sval & 0xffffff;\r
+ else\r
+ dval = sf_blend((int)(alpha + (alpha>>7)),sval,dst[0]);\r
+ for (iy=0;iy<m_gfxScale;iy++)\r
+ {\r
+ for (ix=0;ix<m_gfxScale;ix++) dst[ix] = dval;\r
+ dst += sbpitch;\r
+ }\r
+ }\r
+ src += pic->width;\r
+ }\r
+\r
+ dirty = 1;\r
+ }\r
+\r
+static ulong mytimeout;\r
+int mouse_button;\r
+static int numAltQ = 0;\r
+\r
+static zword goodzkey( SDL_Event *e, int allowed)\r
+ {\r
+ zword c;\r
+ if (e->type == SDL_QUIT)\r
+ {\r
+ sf_quitconf();\r
+// if (allowed) return ZC_HKEY_QUIT;\r
+ return 0;\r
+ }\r
+ if (e->type == SDL_MOUSEBUTTONDOWN)\r
+ {\r
+//printf("down %d\n",e->button.button);\r
+ if (true) //(e->button.button == SDL_BUTTON_LEFT)\r
+ {\r
+ mouse_button = e->button.button;\r
+ mouse_x = e->button.x+1-blitrect.x;\r
+ mouse_y = e->button.y+1-blitrect.y;\r
+ return ZC_SINGLE_CLICK;\r
+ }\r
+ return 0;\r
+ }\r
+ if (e->type != SDL_KEYDOWN) return 0;\r
+ // emergency exit\r
+ if (((e->key.keysym.mod & 0xfff) == (KMOD_LALT | KMOD_LCTRL)) && (e->key.keysym.sym == 'x'))\r
+ os_fatal("Emergency exit!\n\n(Control-Alt-X pressed)");\r
+ if (((e->key.keysym.mod & KMOD_LALT) == KMOD_LALT) ||\r
+ ((e->key.keysym.mod & KMOD_RALT) == KMOD_RALT))\r
+ {\r
+ if (e->key.keysym.sym == 'q')\r
+ {\r
+ numAltQ++;\r
+ if (numAltQ > 2)\r
+ os_fatal("Emergency exit!\n\n(Alt-Q pressed 3 times in succession)");\r
+ }\r
+ else numAltQ = 0;\r
+ if (!allowed) return 0;\r
+ switch (e->key.keysym.sym)\r
+ {\r
+ case 'x': return ZC_HKEY_QUIT;\r
+ case 'p': return ZC_HKEY_PLAYBACK;\r
+ case 'r': return ZC_HKEY_RECORD;\r
+ case 's': return ZC_HKEY_SEED;\r
+ case 'u': return ZC_HKEY_UNDO;\r
+ case 'n': return ZC_HKEY_RESTART;\r
+ case 'd': return ZC_HKEY_DEBUG;\r
+ case 'h': return ZC_HKEY_HELP;\r
+ default: return 0;\r
+ }\r
+ }\r
+ else numAltQ = 0;\r
+ switch (e->key.keysym.sym)\r
+ {\r
+ case SDLK_INSERT: return (allowed ? VK_INS : 0);\r
+ case SDLK_BACKSPACE: return ZC_BACKSPACE;\r
+ case SDLK_ESCAPE: return ZC_ESCAPE;\r
+ case SDLK_RETURN: return ZC_RETURN;\r
+ case SDLK_UP: return ZC_ARROW_UP;\r
+ case SDLK_DOWN: return ZC_ARROW_DOWN;\r
+ case SDLK_LEFT: return ZC_ARROW_LEFT;\r
+ case SDLK_RIGHT: return ZC_ARROW_RIGHT;\r
+ case SDLK_TAB: return (allowed ? VK_TAB : 0);\r
+ case SDLK_KP0: return ZC_NUMPAD_MIN+0;\r
+ case SDLK_KP1: return ZC_NUMPAD_MIN+1;\r
+ case SDLK_KP2: return ZC_NUMPAD_MIN+2;\r
+ case SDLK_KP3: return ZC_NUMPAD_MIN+3;\r
+ case SDLK_KP4: return ZC_NUMPAD_MIN+4;\r
+ case SDLK_KP5: return ZC_NUMPAD_MIN+5;\r
+ case SDLK_KP6: return ZC_NUMPAD_MIN+6;\r
+ case SDLK_KP7: return ZC_NUMPAD_MIN+7;\r
+ case SDLK_KP8: return ZC_NUMPAD_MIN+8;\r
+ case SDLK_KP9: return ZC_NUMPAD_MIN+9;\r
+ case SDLK_F1: return ZC_FKEY_MIN+0;\r
+ case SDLK_F2: return ZC_FKEY_MIN+1;\r
+ case SDLK_F3: return ZC_FKEY_MIN+2;\r
+ case SDLK_F4: return ZC_FKEY_MIN+3;\r
+ case SDLK_F5: return ZC_FKEY_MIN+4;\r
+ case SDLK_F6: return ZC_FKEY_MIN+5;\r
+ case SDLK_F7: return ZC_FKEY_MIN+6;\r
+ case SDLK_F8: return ZC_FKEY_MIN+7;\r
+ case SDLK_F9: return ZC_FKEY_MIN+8;\r
+ case SDLK_F10: return ZC_FKEY_MIN+9;\r
+ case SDLK_F11: return ZC_FKEY_MIN+10;\r
+ case SDLK_F12: return ZC_FKEY_MIN+11;\r
+ default: break;\r
+ }\r
+ c = e->key.keysym.unicode;\r
+ if ((c >= 32 && c <= 126) || (c >= 160)) return c;\r
+ return 0;\r
+ }\r
+\r
+zword sf_read_key( int timeout, int cursor, int allowed)\r
+ {\r
+ SDL_Event event;\r
+ zword inch = 0;\r
+\r
+ sf_flushtext();\r
+// theWnd->ResetOverhang();\r
+// theWnd->UpdateMenus();\r
+ if (cursor)\r
+ sf_drawcursor(true);\r
+ sf_flushdisplay();\r
+\r
+ if (timeout) mytimeout = sf_ticks() + m_timerinterval*timeout;\r
+// InputTimer timer(timeout);\r
+// FrotzWnd::Input input;\r
+ while (true)\r
+ {\r
+ // Get the next input\r
+ while (SDL_PollEvent(&event)) \r
+ {\r
+//if (event.type == SDL_QUIT) printf("got SDL_QUIT\n");\r
+ if ((inch = goodzkey(&event,allowed))) \r
+ break;\r
+ }\r
+ if (inch) break;\r
+ if ((timeout) && (sf_ticks() >= mytimeout))\r
+ {\r
+ inch = ZC_TIME_OUT;\r
+ break;\r
+ }\r
+ sf_checksound();\r
+ sf_sleep(10);\r
+ }\r
+\r
+ if (cursor)\r
+ sf_drawcursor(false);\r
+\r
+ return inch;\r
+ }\r
+\r
+\r
+/*\r
+ * os_read_key\r
+ *\r
+ * Read a single character from the keyboard (or a mouse click) and\r
+ * return it. Input aborts after timeout/10 seconds.\r
+ *\r
+ */\r
+zword os_read_key(int timeout, int cursor)\r
+ {\r
+ return sf_read_key(timeout,cursor,0);\r
+ }\r
+\r
+#define MAXHISTORY 8192\r
+static zword History[MAXHISTORY] = {0};\r
+static int histptr = 0;\r
+static void addtoHistory( zword *buf)\r
+ {\r
+ int n = mywcslen(buf)+2;\r
+ int avail = MAXHISTORY - histptr;\r
+ if (n <= 2) return;\r
+ while (avail < n)\r
+ {\r
+ int k;\r
+ if (histptr == 0) return;\r
+ k = History[histptr-1]+2;\r
+ histptr -= k;\r
+ }\r
+ if (histptr) memmove(History+n,History,histptr*sizeof(zword));\r
+ histptr += n;\r
+ n -= 2;\r
+ History[0] = History[n+1] = n;\r
+ memcpy(History+1,buf,n*sizeof(zword));\r
+ }\r
+\r
+/*\r
+ * os_read_line\r
+ *\r
+ * Read a line of input from the keyboard into a buffer. The buffer\r
+ * may already be primed with some text. In this case, the "initial"\r
+ * text is already displayed on the screen. After the input action\r
+ * is complete, the function returns with the terminating key value.\r
+ * The length of the input should not exceed "max" characters plus\r
+ * an extra 0 terminator.\r
+ *\r
+ * Terminating keys are the return key (13) and all function keys\r
+ * (see the Specification of the Z-machine) which are accepted by\r
+ * the is_terminator function. Mouse clicks behave like function\r
+ * keys except that the mouse position is stored in global variables\r
+ * "mouse_x" and "mouse_y" (top left coordinates are (1,1)).\r
+ *\r
+ * Furthermore, Frotz introduces some special terminating keys:\r
+ *\r
+ * ZC_HKEY_PLAYBACK (Alt-P)\r
+ * ZC_HKEY_RECORD (Alt-R)\r
+ * ZC_HKEY_SEED (Alt-S)\r
+ * ZC_HKEY_UNDO (Alt-U)\r
+ * ZC_HKEY_RESTART (Alt-N, "new game")\r
+ * ZC_HKEY_QUIT (Alt-X, "exit game")\r
+ * ZC_HKEY_DEBUG (Alt-D)\r
+ * ZC_HKEY_HELP (Alt-H)\r
+ *\r
+ * If the timeout argument is not zero, the input gets interrupted\r
+ * after timeout/10 seconds (and the return value is 0).\r
+ *\r
+ * The complete input line including the cursor must fit in "width"\r
+ * screen units.\r
+ *\r
+ * The function may be called once again to continue after timeouts,\r
+ * misplaced mouse clicks or hot keys. In this case the "continued"\r
+ * flag will be set. This information can be useful if the interface\r
+ * implements input line history.\r
+ *\r
+ * The screen is not scrolled after the return key was pressed. The\r
+ * cursor is at the end of the input line when the function returns.\r
+ *\r
+ * Since Frotz 2.2 the helper function "completion" can be called\r
+ * to implement word completion (similar to tcsh under Unix).\r
+ *\r
+ */\r
+zword os_read_line(int max, zword *buf, int timeout, int width, int continued)\r
+ {\r
+ static int prev_pos = 0;\r
+ static int prev_history = -1;\r
+ int pos = 0;\r
+ int history = -1;\r
+ int ptx,pty;\r
+ SF_textsetting * ts = sf_curtextsetting();\r
+ SDL_Event event;\r
+\r
+//printf("os_read_line mx%d buf[0]%d tm%d w%d c%d\n",max,buf[0],timeout,width,continued);\r
+// LineInput line;\r
+ sf_flushtext();\r
+// theWnd->ResetOverhang();\r
+// theWnd->UpdateMenus();\r
+// theWnd->RecaseInput(buf);\r
+\r
+ // Find the editing position\r
+ if (continued)\r
+ {\r
+ if (prev_pos <= (int)mywcslen(buf))\r
+ pos = prev_pos;\r
+ }\r
+ else\r
+ pos = mywcslen(buf);\r
+\r
+//printf("pos %d\n",pos);\r
+ // Find the input history position\r
+ if (continued)\r
+ history = prev_history;\r
+\r
+ // Draw the input line\r
+ ptx = ts->cx;\r
+ pty = ts->cy;\r
+// CPoint point = theWnd->GetTextPoint();\r
+ ptx -= os_string_width(buf); //theWnd->GetTextWidth(buf,mywcslen(buf));\r
+ sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+\r
+ if (timeout) mytimeout = sf_ticks() + m_timerinterval*timeout;\r
+// InputTimer timer(timeout);\r
+ while (true)\r
+ {\r
+ // Get the next input\r
+ while (SDL_PollEvent(&event)) \r
+ {\r
+ zword c;\r
+ if ((c = goodzkey(&event,1))) \r
+ {\r
+//printf("goodzk %4x\n",c);\r
+ if (c == ZC_BACKSPACE)\r
+ {\r
+ // Delete the character to the left of the cursor\r
+ if (pos > 0)\r
+ {\r
+ memmove(buf+pos-1,buf+pos,sizeof(zword)*(mywcslen(buf)-pos+1));\r
+ pos--;\r
+ sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+ }\r
+ }\r
+ else if (c == VK_TAB)\r
+ {\r
+ if (pos == (int)mywcslen(buf))\r
+ {\r
+ zword extension[10], *s;\r
+ completion(buf,extension);\r
+\r
+ // Add the completion to the input stream\r
+ for (s = extension; *s != 0; s++)\r
+ if (sf_IsValidChar(*s))\r
+ buf[pos++] = (*s);\r
+ buf[pos] = 0;\r
+ sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+ }\r
+ }\r
+ else if ((c == ZC_ARROW_LEFT))\r
+ {\r
+ // Move the cursor left\r
+ if (pos > 0)\r
+ pos--;\r
+ sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+ }\r
+ else if ((c == ZC_ARROW_RIGHT))\r
+ {\r
+ // Move the cursor right\r
+ if (pos < (int)mywcslen(buf))\r
+ pos++;\r
+ sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+ }\r
+ else if ((c == ZC_ARROW_UP))\r
+ {\r
+ int n; zword *p;\r
+ // Move up through the command history\r
+ if (history >= 0)\r
+ {\r
+ n = History[history];\r
+ if (n) history += (n+2);\r
+ }\r
+ else\r
+ history = 0;\r
+ n = History[history];\r
+ p = History + history + 1;\r
+ pos = 0;\r
+ while (n) { buf[pos++] = *p++; n--;}\r
+ buf[pos] = 0;\r
+ sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+ }\r
+ else if ((c == ZC_ARROW_DOWN))\r
+ {\r
+ // Move down through the command history\r
+ pos = 0;\r
+ if (history > 0)\r
+ {\r
+ int n; zword *p;\r
+ n = History[history-1];\r
+ history -= (n+2);\r
+ p = History + history + 1;\r
+ while (n) { buf[pos++] = *p++; n--; }\r
+ }\r
+ else\r
+ history = -1;\r
+ buf[pos] = 0;\r
+ sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+ }\r
+ else if (is_terminator(c))\r
+ {\r
+ // Terminate the current input\r
+ m_exitPause = false;\r
+ sf_DrawInput(buf,pos,ptx,pty,width,false);\r
+\r
+ if ((c == ZC_SINGLE_CLICK) || (c == ZC_DOUBLE_CLICK))\r
+ {\r
+/* mouse_x = input.mousex+1;\r
+ mouse_y = input.mousey+1;*/\r
+ }\r
+ else if (c == ZC_RETURN)\r
+ addtoHistory(buf); //theWnd->AddToInputHistory(buf);\r
+\r
+// theWnd->SetLastInput(buf);\r
+ prev_pos = pos;\r
+ prev_history = history;\r
+ return c;\r
+ }\r
+ else if (sf_IsValidChar(c))\r
+ {\r
+ // Add a valid character to the input line\r
+ if ((int)mywcslen(buf) < max)\r
+ {\r
+ // Get the width of the new input line\r
+ int len = os_string_width(buf);\r
+ len += os_char_width(c);\r
+ len += os_char_width('0');\r
+\r
+//printf("l%d w%d p%d\n",len,width,pos);\r
+ // Only allow if the width limit is not exceeded\r
+ if (len <= width)\r
+ {\r
+ memmove(buf+pos+1,buf+pos,sizeof(zword)*(mywcslen(buf)-pos+1));\r
+ *(buf+pos) = c;\r
+ pos++;\r
+ sf_DrawInput(buf,pos,ptx,pty,width,true);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if ((timeout) && (sf_ticks() >= mytimeout))\r
+ {\r
+ prev_pos = pos;\r
+ prev_history = history;\r
+ return ZC_TIME_OUT;\r
+ }\r
+ sf_checksound();\r
+ sf_sleep(10);\r
+ }\r
+\r
+ return 0;\r
+ }\r
+\r
+// Draw the current input line\r
+void sf_DrawInput(zword * buffer, int pos, int ptx, int pty, int width, bool cursor)\r
+ {\r
+ int height;\r
+ SF_textsetting * ts = sf_curtextsetting();\r
+\r
+//printf("DrawInput (%d)[%d] %d x%d y%d w%d %d\n",mywcslen(buffer),os_string_width(buffer),pos,ptx,pty,width,cursor);\r
+\r
+ height = ts->font->height(ts->font);\r
+\r
+ // Remove any previous input\r
+ sf_fillrect(ts->back,ptx,pty,width,height);\r
+\r
+ // Display the input\r
+// sf_pushtextsettings();\r
+ ts->cx = ptx; ts->cy = pty;\r
+ os_display_string(buffer);\r
+\r
+ if (cursor)\r
+ {\r
+ int wid=0, i=0;\r
+ while (i<pos) wid += os_char_width(buffer[i++]);\r
+ drawthecursor(ptx+wid,pty,1);\r
+ }\r
+\r
+// sf_poptextsettings();\r
+/*\r
+ SetTextPoint(point);\r
+ WriteText(buffer,mywcslen(buffer));\r
+\r
+ if (cursor)\r
+ {\r
+ int x = point.x + GetTextWidth(buffer,pos);\r
+ int cx = GetCharWidth('0');\r
+ if (*(buffer+pos) != 0)\r
+ cx = GetCharWidth(*(buffer+pos));\r
+\r
+ // Invert colours\r
+ COLORREF fore = m_dc.GetTextColor();\r
+ m_dc.SetTextColor(m_dc.GetBkColor());\r
+ m_dc.SetBkColor(fore);\r
+\r
+ // Draw a cursor\r
+ m_dc.MoveTo(x,point.y);\r
+ CRect rect(x,point.y,x+cx,point.y+height);\r
+ if (*(buffer+pos) != 0)\r
+ ::ExtTextOutW(m_dc.GetSafeHdc(),0,0,ETO_OPAQUE,rect,buffer+pos,1,NULL);\r
+ else\r
+ ::ExtTextOutW(m_dc.GetSafeHdc(),0,0,ETO_OPAQUE,rect,NULL,0,NULL);\r
+\r
+ // Put colours back\r
+ m_dc.SetBkColor(m_dc.GetTextColor());\r
+ m_dc.SetTextColor(fore);\r
+ }*/\r
+\r
+ // Update the window\r
+ sf_flushdisplay();\r
+// Invalidate();\r
+ }\r
+\r
+\r
+/*\r
+ * os_read_mouse\r
+ *\r
+ * Store the mouse position in the global variables "mouse_x" and\r
+ * "mouse_y", the code of the last clicked menu in "menu_selected"\r
+ * and return the mouse buttons currently pressed.\r
+ *\r
+ */\r
+zword os_read_mouse(void)\r
+ {\r
+ byte c; int x, y; zword btn = 0;\r
+ // Get the mouse position\r
+ SDL_PumpEvents();\r
+ c = SDL_GetMouseState(&x,&y);\r
+ mouse_x = x+1-blitrect.x;\r
+ mouse_y = y+1-blitrect.y;\r
+ // Get the last selected menu item\r
+// menu_selected = theWnd->GetMenuClick();\r
+//printf("%04x\n",c);\r
+ // Get the mouse buttons\r
+ if (c & SDL_BUTTON_LMASK)\r
+ btn |= 1;\r
+ if (c & SDL_BUTTON_RMASK)\r
+ btn |= 2;\r
+ if (c & SDL_BUTTON_MMASK)\r
+ btn |= 4;\r
+\r
+ return btn;\r
+ }\r
+\r
+/*\r
+ * os_more_prompt\r
+ *\r
+ * Display a MORE prompt, wait for a keypress and remove the MORE\r
+ * prompt from the screen.\r
+ *\r
+ */\r
+void os_more_prompt(void)\r
+ {\r
+ if (m_morePrompts)\r
+ {\r
+ SF_textsetting * ts; int x,y,h;\r
+ const char *p = sf_msgstring(IDS_MORE);\r
+ sf_flushtext();\r
+// theWnd->ResetOverhang();\r
+\r
+ // Save the current text position\r
+ sf_pushtextsettings();\r
+ ts = sf_curtextsetting();\r
+ x = ts->cx; y = ts->cy;\r
+ h = ts->font->height(ts->font);\r
+ // Show a [More] prompt\r
+ while (*p) os_display_char((zword)(*p++));\r
+// theWnd->WriteText(CResString(IDS_MORE));\r
+// sf_drawcursor(true);\r
+// sf_flushdisplay();\r
+\r
+ // Wait for a key press\r
+ os_read_key(0,1);\r
+ // Remove the [More] prompt\r
+ sf_fillrect(ts->back,x,y,ts->cx-x,h);\r
+// sf_drawcursor(false);\r
+\r
+ // Restore the current text position\r
+ sf_poptextsettings();\r
+ }\r
+}\r
+\r
+ulong * sf_savearea( int x, int y, int w, int h)\r
+ {\r
+ ulong *r, *p, *s; int i;\r
+\r
+ if (x < 0){ w += x; x = 0;}\r
+ if (x+w > ewidth) w = ewidth-x;\r
+ if (w <= 0) return NULL;\r
+\r
+ if (y < 0){ h += y; y = 0;}\r
+ if (y+h > eheight) h = eheight-y;\r
+ if (h <= 0) return NULL;\r
+\r
+ r = p = malloc((w*h+4)*sizeof(ulong));\r
+ if (!r) return NULL;\r
+\r
+ *p++ = x;\r
+ *p++ = y;\r
+ *p++ = w;\r
+ *p++ = h;\r
+\r
+ s = sbuffer+x+y*sbpitch;\r
+ for (i=0;i<h;i++)\r
+ {\r
+ memcpy(p,s,w*sizeof(ulong));\r
+ p += w;\r
+ s += sbpitch;\r
+ }\r
+// printf("savearea %d %d %d %d\n",x,y,w,h); fflush(stdout);\r
+ return r;\r
+ }\r
+\r
+void sf_restoreareaandfree( ulong *s)\r
+ {\r
+ ulong *p, *d; int i,x,y,w,h;\r
+ if (!s) return;\r
+\r
+ p = s;\r
+ x = *p++;\r
+ y = *p++;\r
+ w = *p++;\r
+ h = *p++;\r
+// printf("restorearea %d %d %d %d\n",x,y,w,h); fflush(stdout);\r
+\r
+ d = sbuffer+x+y*sbpitch;\r
+ for (i=0;i<h;i++)\r
+ {\r
+ memcpy(d,p,w*sizeof(ulong));\r
+ p += w;\r
+ d += sbpitch;\r
+ }\r
+\r
+ free(s);\r
+ dirty = 1;\r
+ sf_flushdisplay();\r
+ }\r
+\r
+int (*sf_osdialog)( bool ex, const char *def, const char *filt, const char *tit, char **res,\r
+ ulong *sbuf, int sbp, int ew, int eh, int isfull) = NULL;\r
+\r
+int sf_user_fdialog( bool existing, const char *defaultname, const char *filter, const char *title,\r
+ char **result)\r
+ {\r
+ if (sf_osdialog) return sf_osdialog(existing,defaultname,filter,title,result,\r
+ sbuffer, sbpitch, ewidth, eheight, isfullscreen);\r
+ return SF_NOTIMP;\r
+ }\r
+\r
+void sf_videodata( ulong **sb, int *sp, int *ew, int *eh)\r
+ {\r
+ *sb = sbuffer;\r
+ *sp = sbpitch;\r
+ *ew = ewidth;\r
+ *eh = eheight;\r
+ }\r
+\r
+extern zword sf_yesnooverlay( int xc, int yc, char *t, int sr);\r
+static void sf_quitconf()\r
+ {\r
+ if (sf_yesnooverlay(ewidth/2,eheight/2,"Quit: are you sure?",1)==ZC_RETURN)\r
+ {\r
+ printf("\n\nQuitting (close button clicked on main window)\n\n");\r
+ SDL_Quit();\r
+ exit(0);\r
+ }\r
+ }\r
+\r
+void os_check_events(void)\r
+ {\r
+ SDL_Event event;\r
+ SDL_PumpEvents();\r
+ if (SDL_PeepEvents(&event,1,SDL_GETEVENT,SDL_QUITMASK))\r
+ sf_quitconf();\r
+ }\r
+\r
--- /dev/null
+<html>
+<head>
+</head>
+<body>
+Sfrotz manual (sort of...)
+Copyright © 2011 Aldo Cumani<br>
+Comments and bug reports to <sfrotz .at. writeme .dot. com> (apologies for the anti-spam-munging.)<br><br>
+
+This is just a preliminary draft of a manual. It refers to sfrotz v0.02<br>
+This document is relased under the terms of the GNU Free Documentation License.
+
+
+<h1>1. Introduction</h1>
+Sfrotz is a Linux (though potentially cross-platform) port of the well-known z-code interpreter Frotz 2.40. Sfrotz uses SDL for video rendering and either SDL-mixer or MikMod for audio. While in principle Sfrotz should run on any platform supporting SDL, in practice it has only been extensively tested on Linux (Debian 5 lenny x86 and Debian 6 squeeze x64).
+
+<h2>1.1 Installation</h2>
+Sfrotz is distributed in source form. Compiling the sources requires gcc (Mingw32 on Windows also works, more or less) and GNU make. The distribution does not currently include any configuration script, so the Makefile must be adapted by hand to the target system.
+
+
+<h1>2. Features</h1>
+
+
+<h2>2.1 Blorb resources</h2>
+Sfrotz can render graphics (for V6 games) and sound effects packaged in a Blorb file (but see also the next section). As concerns pictures, Sfrotz supports both PNG and JPEG images. Regarding sound, AIFF sound effects, MOD songs and OGG music are currently supported, while SONG songs won't probably be ever supported.
+
+
+<h2>2.2 <a name="nonblorb">Non-Blorb resources</a></h2>
+Besides Blorb-packaged resources, Sfrotz can also render graphics and sound from individual files, provided the latter are either suitably named (e.g a common root with a numeric field specifying the resource number), or listed in a suitably formatted text file.<br>
+
+Note that this feature is not automatic, but must be enabled by the -L or -@ command line options. When the feature is enabled, resources found this way override those in the Blorb file, if present.<br>
+
+This feature can be useful to game designers, as it eases changing and adding resources without having to re-compile the Blorb file each time. The format of the list file used with the -@ option (see the <a href="#appendix2">Appendix</a>) is identical to that of the BLC control file used by L. Ross Raszewski's <a href="http://www.trenchcoatsoft.com/">iblorb</a> package, so one can use the same list for compiling the final Blorb file for distribution.
+
+
+<h2>2.3 Truetype fonts</h2>
+Sfrotz can display text with Truetype or Type1 <a href="#fontdir">fonts</a>,
+using the FreeType2 rendering library.
+The location of the
+necessary font files must be specified in the setup file. Also,
+<a href="#antialias">antialiased</a> rendering of Truetype fonts can be
+specified.
+Please note that this feature in Sfrotz is still <b>experimental</b>,
+so don't expect
+too much (e.g., kerning is not supported).
+
+
+<h2>2.4 Default fonts</h2>
+Sfrotz needs not Truetype fonts for working; in fact it has a default
+monospaced font, based on an 8x16 VGA font, which can be used for both
+the TEXT_FONT and the FIXED_FONT of the z-machine, with suitable
+(though not very nice) modifications for the various styles. Even if
+Truetype fonts are specified in the setup file, Sfrotz can be
+forced to use the VGA font by the <a href="#-V"><tt>-V</tt></a> command
+line switch.
+
+<h1>3. Command line options</h1>
+
+<h2>3.1 Standard Frotz options</h2>
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-a<em></em></tt></td>
+<td valign=top>For game debugging: watch attribute setting.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-A<em></em></tt></td>
+<td valign=top>For game debugging: watch attribute testing.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-b <em>colour</em></tt></td>
+<td valign=top>Set the background colour. The <em>colour</em> argument
+is the number of a z-machine colour, in the range 2..9:</td></tr></tbody></table>
+
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-c <em>lines</em></tt></td>
+<td valign=top>Set the number of context lines.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-f <em>colour</em></tt></td>
+<td valign=top>Set the foreground colour.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-h <em>size</em></tt></td>
+<td valign=top>Set the z-machine screen height (see the <a href="#sNOTE">NOTE</a>).</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-i<em></em></tt></td>
+<td valign=top>Ignore non-fatal runtime errors.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-l <em>margin</em></tt></td>
+<td valign=top>Set the left margin.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-o<em></em></tt></td>
+<td valign=top>For game debugging: watch object movement.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-O<em></em></tt></td>
+<td valign=top>For game debugging: watch object locating.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-p<em></em></tt></td>
+<td valign=top>Alter the piracy opcode.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-r <em>margin</em></tt></td>
+<td valign=top>Set the right margin.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-R<em></em></tt></td>
+<td valign=top>Save/restore in the old Frotz format.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-s <em>seed</em></tt></td>
+<td valign=top>Set the random number seed value.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-S <em>width</em></tt></td>
+<td valign=top>Set the transcript width (in columns, default = 80).</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-t<em></em></tt></td>
+<td valign=top>Set the Tandy bit.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-u <em>number</em></tt></td>
+<td valign=top>Set the number of slots for multiple undo (default: 500).</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-w <em>size</em></tt></td>
+<td valign=top>Set the width of the z-machine screen to <em>size</em> (see the <a href="#sNOTE">NOTE</a>).</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-x<em></em></tt></td>
+<td valign=top>Expand abbreviations (g/x/z).</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-Z <em>level</em></tt></td>
+<td valign=top>Set the z-code error reporting level. Level can be:
+<table><tbody>
+<tr><td width=50 valign=top>0</td><td valign=top>don't report errors</td></tr>
+<tr><td width=50 valign=top>1</td><td valign=top>report only the first error</td></tr>
+<tr><td width=50 valign=top>2</td><td valign=top>report all errors</td></tr>
+<tr><td width=50 valign=top>3</td><td valign=top>exit after any error</td></tr>
+</tbody></table>
+</td></tr></tbody></table>
+
+
+
+<h2>3.2 Extended options</h2>
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-@<em>listfile</em></tt></td>
+<td valign=top>Use <a href="#nonblorb">resource files</a> listed in <i>listfile</i>.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-F<em></em></tt></td>
+<td valign=top>Run in fullscreen mode (see <a href="#sNOTE">NOTE</a>).</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-L<em></em></tt></td>
+<td valign=top>Use local <a href="#nonblorb">resource files</a>.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-m <em>msecs</em></tt></td>
+<td valign=top>Set the timer interrupt cycle to <em>msecs</em> milliseconds,
+instead of the default 100 (1/10 sec).</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-N <em>mode</em></tt></td>
+<td valign=top>Set the mode for creating default file names for save/script etc.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>-T<em></em></tt></td>
+<td valign=top>Use traditional in-game requests for file names, intead of on-screen
+dialogs.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt><a name="-V">-V</a><em></em></tt></td>
+<td valign=top>Force the use of default monospaced VGA font.</td></tr></tbody></table>
+
+
+
+<h1>4. Hot keys</h1>
+Sfrotz supports the same hot keys as standard Frotz, plus the Ctl-Alt-X combination for immediate
+exit, which may be used in case of emergency.<br>
+Note that these hot keys are enabled only when the z-machine is waiting for <i>line input</i> (for z-machine experts: @read opcode), whith the exception of Ctl-Alt-X which also works in <i>single character</i> input mode (@read_char opcode).<br>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>Alt-D<em></em></tt></td>
+<td valign=top>set debugging options</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>Alt-H<em></em></tt></td>
+<td valign=top>help (print the list of hot keys)</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>Alt-N<em></em></tt></td>
+<td valign=top>new game (restart)</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>Alt-P<em></em></tt></td>
+<td valign=top>playback on</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>Alt-R<em></em></tt></td>
+<td valign=top>recording on/off</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>Alt-S<em></em></tt></td>
+<td valign=top>set random number seed</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>Alt-U<em></em></tt></td>
+<td valign=top>undo one turn</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>Alt-X<em></em></tt></td>
+<td valign=top>exit game (after confirmation)</td></tr></tbody></table>
+
+
+<table width=100%><tbody><tr><td width=120 valign=top><tt>Ctl-Alt-X<em></em></tt></td>
+<td valign=top>exit game immediately (no confirmation)</td></tr></tbody></table>
+
+
+
+<h1>5. The setup file</h1>
+
+<h2>5.1 Section <tt>[Interpreter]</tt></h2>
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Number = <em>number</em></tt></td>
+<td valign=top>Set the interpreter number (default is 4, i.e. Amiga Interpreter)</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Error Reporting = <em>level</em></tt></td>
+<td valign=top>Set the error reporting level (same as the -Z option)</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Ignore Errors = <em>0/1</em></tt></td>
+<td valign=top>Ignore (1) or not (0) non-fatal runtime errors.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Expand Abbreviations = <em>0/1</em></tt></td>
+<td valign=top>Set/reset expansion of g/x/z abbreviations. Expansion is useful for old v1
+games which do not understand such abbreviations. Default: 0 (the -x option can set this switch.)</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Tandy Bit = <em>0/1</em></tt></td>
+<td valign=top>Set/reset the Tandy bit. Default: 0 (the -t option can set this switch.)</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Quetzal Format = <em>0/1</em></tt></td>
+<td valign=top>Save/restore in Quetzal format when set, in old Frotz format when reset.
+Default: 1 (the -R option can only reset this switch.)</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Wrap Script Lines = <em>nc</em></tt></td>
+<td valign=top>Set the width (number of columns) of the transcript to <i>nc</i>. Same
+as -S option.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>SaveNames = <em>mode</em></tt></td>
+<td valign=top></td></tr></tbody></table>
+
+
+<h2>5.2 Section <tt>[Window]</tt></h2>
+<a name="sNOTE"></a>
+Sfrotz has a hardwired default screen size of 640x400. The screen size can be changed by the values in this section, by the values found in the Reso chunck of a Blorb file, and finally by the -w and -h command line options (in that order). Note however that Sfrotz shall refuse to set a screen width less than 640 and/or a height less than 400.<br>
+<b>NOTE</b>: for normal (windowed) usage, the screen size should obviously be less than the PC screen resolution (taking into account also window decorations, taskbars etc.) For fullscreen usage, the size should preferably be one of those supported by the PC video driver; otherwise, SDL shall try to use the next higher available resolution, with black borders around the z-machine screen. In fullscreen mode, however, it may happen that for some strange resolutions SDL accepts the request, but the screen goes blank... In such a case, you may shut down the program by pressing Ctrl-Alt-X.<br><br>
+<table width=100%><tbody><tr><td width=250 valign=top><tt>AcWidth = <em>width</em></tt></td>
+<td valign=top>Set the screen width (default: 640)</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>AcHeight = <em>height</em></tt></td>
+<td valign=top>Set the screen height (default: 400)</td></tr></tbody></table>
+
+
+<h2>5.3 Section <tt>[Display]</tt></h2>
+This section is for future developments.
+
+<h2>5.4 Section <tt>[Fonts]</tt></h2>
+<table width=100%><tbody><tr><td width=250 valign=top><tt><a name="antialias">antialias</a> = <em>0/1</em></tt></td>
+<td valign=top>Set antialiased rendering
+of Truetype fonts off (0) or on (nonzero). Note that this option cannot
+be overridden by a command line switch.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt><a name="fontdir">fontdir</a> = <em>folder</em></tt></td>
+<td valign=top>Specify the directory containing the Truetype fonts.</td></tr></tbody></table>
+
+<p>The following eight statements specify the eight font faces used by the z-machine (not counting the
+so-called graphics font used in Beyond Zork, which is hardwired in the program), that is the normal TEXT_FONT and the monospaced FIXED_FONT, each in four styles (roman, bold, italic and bold+italic). A single face is specified by the file name (with its suffix!), optionally followed by an @ sign and a number, indicating the font size in pixels (default is 14). Multiple face files can be specified, separated by pipe (|) characters; Sfrotz shall use the first one it finds (see the example in the Appendix). This feature allows e.g. to use the same setup file on different systems.<br><br>
+<table width=100%><tbody><tr><td width=250 valign=top><tt><a name="font">textroman</a> = <em>fontspec</em></tt></td>
+<td valign=top>Set the font file for TEXT_FONT,
+roman style</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>textbold = <em>fontspec</em></tt></td>
+<td valign=top>Set the font file for TEXT_FONT, bold style</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>textitalic = <em>fontspec</em></tt></td>
+<td valign=top>Set the font file for TEXT_FONT, italic style</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>textbolditalic = <em>fontspec</em></tt></td>
+<td valign=top>Set the font file for TEXT_FONT, bold and italic
+style</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>fixedroman = <em>fontspec</em></tt></td>
+<td valign=top>Set the font file for FIXED_FONT, roman style</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>fixedbold = <em>fontspec</em></tt></td>
+<td valign=top>Set the font file for FIXED_FONT, bold style</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>fixeditalic = <em>fontspec</em></tt></td>
+<td valign=top>Set the font file for FIXED_FONT, italic style</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>fixedbolditalic = <em>fontspec</em></tt></td>
+<td valign=top>Set the font file for FIXED_FONT, bold and
+italic style</td></tr></tbody></table>
+
+
+<h2>5.5 Section <tt>[Resources]</tt></h2>
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Dir = <em>folder</em></tt></td>
+<td valign=top>Specify the folder for individual graphics/sound resource files.</td></tr></tbody></table>
+
+<p>
+The following two statements specify the templates for the names of individual picture/sound files.
+Each template must contain exactly one C-style decimal format specifier (e.g. Pict%d) to be substituted by the resource number.
+<br><br>
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Pict = <em>template</em></tt></td>
+<td valign=top>Template for picture resource files.</td></tr></tbody></table>
+
+<table width=100%><tbody><tr><td width=250 valign=top><tt>Snd = <em>template</em></tt></td>
+<td valign=top>Template for sound resource files.</td></tr></tbody></table>
+
+
+
+<h1>6. Credits</h1>
+The original <a href="http://frotz.sourceforge.net/">Frotz</a> code was designed by Stefan Jokisch.<br>
+This port is heavily based on the <a href="http://www.davidkinder.co.uk/frotz.html">Windows port</a> by David Kinder.<br>
+The <a href="http://www.eblong.com/zarf/blorb/">Blorb</a> resource format and related software are by Andrew Plotkin.<br>
+The <a href="http://www.libsdl.org/">Simple DirectMedia Layer</a> library is the work of Sam Lantinga.<br>
+TrueType font rendering relies on the <a href="http://www.freetype.org/">freetype2</a> library.<br>
+Sound and music are rendered by the <a href="http://mikmod.raphnet.net/">MikMod</a> library.<br>
+PNG format images are decoded using <a href="http://www.libpng.org/">libpng</a> and <a href="http://www.zlib.net/">zlib</a>.<br>
+JPEG format images are decoded using the <a href="http://www.ijg.org/">IJG</a> software, whose authors kindly request us to state that<br>
+<em>"This software is based in part on the work of the Independent JPEG Group."</em>
+
+<br><br>Apologies to all those people and/or organisations who should have been mentioned, and were not :(
+<br>
+
+
+<h1>7. Appendix</h1>
+
+<h2>7.1 <a name="appendix1">Example setup file</a></h2>
+<pre><tt>
+# The # denotes the start of a comment
+# Everything after the # is ignored, up to the end of the line
+
+[Interpreter]
+SaveNames=date
+
+[Window]
+# The following entries are commented out
+# but they are the same as the hardwired defaults, anyway
+#AcWidth = 640
+#AcHeight = 400
+
+[Display]
+
+[Fonts]
+antialias=1
+fontdir=/usr/share/fonts/truetype/freefont
+textroman=arial.ttf@16|FreeSans.ttf@16
+textbold=arialbd.ttf@16|FreeSansBold.ttf@16
+textitalic=ariali.ttf@16|FreeSansOblique.ttf@16
+textbolditalic=arialbi.ttf@16|FreeSansBoldOblique.ttf@16
+fixedroman=cour.ttf@16|FreeMono.ttf@16
+fixedbold=courbd.ttf@16|FreeMonoBold.ttf@16
+fixeditalic=couri.ttf@16|FreeMonoOblique.ttf@16
+fixedbolditalic=courbi.ttf@16|FreeMonoBoldOblique.ttf@16
+
+[Resources]
+Dir=./ # the current dir
+Pict=PIC%d # i.e. PIC1, PIC2, ...
+Snd=SND%d # i.e. SND3, SND4, ...
+
+</tt></pre>
+
+<h2>7.2 <a name="appendix2">Example BLC file</a></h2>
+<pre><tt>
+Exec 0 ZCOD ani.z6
+
+Snd 13 FORM busyalone.au.aiff
+Snd 12 FORM s0020.au.aiff
+Snd 11 FORM s0154.au.aiff
+Snd 10 FORM s1484.au.aiff
+
+Pict 10 PNG edleft.png0.png
+Pict 11 PNG edleft.png1.png
+Pict 12 PNG edleft.png2.png
+Pict 13 PNG edleft.png3.png
+Pict 14 PNG edleft.png4.png
+Pict 15 PNG edleft.png5.png
+
+Pict 16 PNG lauhoh.png0.png
+Pict 17 PNG lauhoh.png1.png
+Pict 18 PNG lauhoh.png2.png
+
+Pict 19 PNG edfront.png0.png
+Pict 20 PNG edfront.png1.png
+Pict 21 PNG edfront.png2.png
+Pict 22 PNG edfront.png3.png
+Pict 23 PNG edfront.png4.png
+Pict 24 PNG edfront.png5.png
+
+Pict 25 PNG hoagie.png0.png
+Pict 26 PNG hoagie.png1.png
+
+Pict 30 PNG dott0.png
+</tt></pre>
+</body>
+</html>