From c96d10f779bd09b67b2e9f8bb4e1c43e01ebc7b4 Mon Sep 17 00:00:00 2001 From: rodin Date: Fri, 22 May 2009 12:05:41 +0000 Subject: [PATCH] * Added BLORB file support (i.e. copied the gi_blorb.* files to this dir by instructions of the allmighty spec) --- src/Makefile.am | 3 +- src/gi_blorb.c | 603 ++++++++++++++++++++++++++++++++++++++++++++++++ src/gi_blorb.h | 88 +++++++ 3 files changed, 693 insertions(+), 1 deletion(-) create mode 100644 src/gi_blorb.c create mode 100644 src/gi_blorb.h diff --git a/src/Makefile.am b/src/Makefile.am index 66ba275..894db95 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,7 +31,8 @@ libchimara_la_SOURCES = \ strio.c \ style.c \ timer.c timer.h \ - window.c window.h + window.c window.h \ + gi_blorb.c gi_blorb.h libchimara_la_CPPFLAGS = \ -DG_LOG_DOMAIN=\"Chimara\" libchimara_la_CFLAGS = @CHIMARA_CFLAGS@ $(AM_CFLAGS) diff --git a/src/gi_blorb.c b/src/gi_blorb.c new file mode 100644 index 0000000..329280d --- /dev/null +++ b/src/gi_blorb.c @@ -0,0 +1,603 @@ +/* gi_blorb.c: Blorb library layer for Glk API. + gi_blorb version 1.4. + Designed by Andrew Plotkin + http://www.eblong.com/zarf/glk/index.html + + This file is copyright 1998-2000 by Andrew Plotkin. You may copy, + distribute, and incorporate it into your own programs, by any means + and under any conditions, as long as you do not modify it. You may + also modify this file, incorporate it into your own programs, + and distribute the modified version, as long as you retain a notice + in your program or documentation which mentions my name and the URL + shown above. +*/ + +#include "glk.h" +#include "gi_blorb.h" + +#ifndef NULL +#define NULL 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* The magic macro of endian conversion. */ + +#define giblorb_native4(v) \ + ( (((glui32)((v)[3]) ) & 0x000000ff) \ + | (((glui32)((v)[2]) << 8) & 0x0000ff00) \ + | (((glui32)((v)[1]) << 16) & 0x00ff0000) \ + | (((glui32)((v)[0]) << 24) & 0xff000000)) + +/* More four-byte constants. */ + +#define giblorb_ID_FORM (giblorb_make_id('F', 'O', 'R', 'M')) +#define giblorb_ID_IFRS (giblorb_make_id('I', 'F', 'R', 'S')) +#define giblorb_ID_RIdx (giblorb_make_id('R', 'I', 'd', 'x')) + +/* giblorb_chunkdesc_t: Describes one chunk of the Blorb file. */ +typedef struct giblorb_chunkdesc_struct { + glui32 type; + glui32 len; + glui32 startpos; /* start of chunk header */ + glui32 datpos; /* start of data (either startpos or startpos+8) */ + + void *ptr; /* pointer to malloc'd data, if loaded */ + int auxdatnum; /* entry in the auxsound/auxpict array; -1 if none. + This only applies to chunks that represent resources; */ + +} giblorb_chunkdesc_t; + +/* giblorb_resdesc_t: Describes one resource in the Blorb file. */ +typedef struct giblorb_resdesc_struct { + glui32 usage; + glui32 resnum; + glui32 chunknum; +} giblorb_resdesc_t; + +/* giblorb_map_t: Holds the complete description of an open Blorb file. */ +struct giblorb_map_struct { + glui32 inited; /* holds giblorb_Inited_Magic if the map structure is + valid */ + strid_t file; + + int numchunks; + giblorb_chunkdesc_t *chunks; /* list of chunk descriptors */ + + int numresources; + giblorb_resdesc_t *resources; /* list of resource descriptors */ + giblorb_resdesc_t **ressorted; /* list of pointers to descriptors + in map->resources -- sorted by usage and resource number. */ +}; + +#define giblorb_Inited_Magic (0xB7012BED) + +/* Static variables. */ + +static int lib_inited = FALSE; + +static giblorb_err_t giblorb_initialize(void); +static giblorb_err_t giblorb_initialize_map(giblorb_map_t *map); +static void giblorb_qsort(giblorb_resdesc_t **list, int len); +static giblorb_resdesc_t *giblorb_bsearch(giblorb_resdesc_t *sample, + giblorb_resdesc_t **list, int len); +static void *giblorb_malloc(glui32 len); +static void *giblorb_realloc(void *ptr, glui32 len); +static void giblorb_free(void *ptr); + +static giblorb_err_t giblorb_initialize() +{ + return giblorb_err_None; +} + +giblorb_err_t giblorb_create_map(strid_t file, giblorb_map_t **newmap) +{ + giblorb_err_t err; + giblorb_map_t *map; + glui32 readlen; + glui32 nextpos, totallength; + giblorb_chunkdesc_t *chunks; + int chunks_size, numchunks; + char buffer[16]; + + *newmap = NULL; + + if (!lib_inited) { + err = giblorb_initialize(); + if (err) + return err; + lib_inited = TRUE; + } + + /* First, chew through the file and index the chunks. */ + + glk_stream_set_position(file, 0, seekmode_Start); + + readlen = glk_get_buffer_stream(file, buffer, 12); + if (readlen != 12) + return giblorb_err_Read; + + if (giblorb_native4(buffer+0) != giblorb_ID_FORM) + return giblorb_err_Format; + if (giblorb_native4(buffer+8) != giblorb_ID_IFRS) + return giblorb_err_Format; + + totallength = giblorb_native4(buffer+4) + 8; + nextpos = 12; + + chunks_size = 8; + numchunks = 0; + chunks = (giblorb_chunkdesc_t *)giblorb_malloc(sizeof(giblorb_chunkdesc_t) + * chunks_size); + + while (nextpos < totallength) { + glui32 type, len; + int chunum; + giblorb_chunkdesc_t *chu; + + glk_stream_set_position(file, nextpos, seekmode_Start); + + readlen = glk_get_buffer_stream(file, buffer, 8); + if (readlen != 8) + return giblorb_err_Read; + + type = giblorb_native4(buffer+0); + len = giblorb_native4(buffer+4); + + if (numchunks >= chunks_size) { + chunks_size *= 2; + chunks = (giblorb_chunkdesc_t *)giblorb_realloc(chunks, + sizeof(giblorb_chunkdesc_t) * chunks_size); + } + + chunum = numchunks; + chu = &(chunks[chunum]); + numchunks++; + + chu->type = type; + chu->startpos = nextpos; + if (type == giblorb_ID_FORM) { + chu->datpos = nextpos; + chu->len = len+8; + } + else { + chu->datpos = nextpos+8; + chu->len = len; + } + chu->ptr = NULL; + chu->auxdatnum = -1; + + nextpos = nextpos + len + 8; + if (nextpos & 1) + nextpos++; + + if (nextpos > totallength) + return giblorb_err_Format; + } + + /* The basic IFF structure seems to be ok, and we have a list of + chunks. Now we allocate the map structure itself. */ + + map = (giblorb_map_t *)giblorb_malloc(sizeof(giblorb_map_t)); + if (!map) { + giblorb_free(chunks); + return giblorb_err_Alloc; + } + + map->inited = giblorb_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 = giblorb_initialize_map(map); + if (err) { + giblorb_destroy_map(map); + return err; + } + + *newmap = map; + return giblorb_err_None; +} + +static giblorb_err_t giblorb_initialize_map(giblorb_map_t *map) +{ + /* It is important that the map structure be kept valid during this + function. If this returns an error, giblorb_destroy_map() will + be called. */ + + int ix, jx; + giblorb_result_t chunkres; + giblorb_err_t err; + char *ptr; + glui32 len; + glui32 val; + glui32 numres; + int gotindex = FALSE; + + for (ix=0; ixnumchunks; ix++) { + giblorb_chunkdesc_t *chu = &map->chunks[ix]; + + switch (chu->type) { + + case giblorb_ID_RIdx: + /* Resource index chunk: build the resource list and + sort it. */ + + if (gotindex) + return giblorb_err_Format; /* duplicate index chunk */ + err = giblorb_load_chunk_by_number(map, giblorb_method_Memory, + &chunkres, ix); + if (err) + return err; + + ptr = chunkres.data.ptr; + len = chunkres.length; + numres = giblorb_native4(ptr+0); + + if (numres) { + int ix2; + giblorb_resdesc_t *resources; + giblorb_resdesc_t **ressorted; + + if (len != numres*12+4) + return giblorb_err_Format; /* bad length field */ + + resources = (giblorb_resdesc_t *)giblorb_malloc(numres + * sizeof(giblorb_resdesc_t)); + ressorted = (giblorb_resdesc_t **)giblorb_malloc(numres + * sizeof(giblorb_resdesc_t *)); + if (!ressorted || !resources) + return giblorb_err_Alloc; + + ix2 = 0; + for (jx=0; jxusage = giblorb_native4(ptr+jx*12+4); + res->resnum = giblorb_native4(ptr+jx*12+8); + respos = giblorb_native4(ptr+jx*12+12); + + while (ix2 < map->numchunks + && map->chunks[ix2].startpos < respos) + ix2++; + + if (ix2 >= map->numchunks + || map->chunks[ix2].startpos != respos) + return giblorb_err_Format; /* start pos does + not match a real chunk */ + + res->chunknum = ix2; + + ressorted[jx] = res; + } + + /* Sort a resource list (actually a list of pointers to + structures in map->resources.) This makes it easy + to find resources by usage and resource number. */ + giblorb_qsort(ressorted, numres); + + map->numresources = numres; + map->resources = resources; + map->ressorted = ressorted; + } + + giblorb_unload_chunk(map, ix); + gotindex = TRUE; + break; + + } + } + + return giblorb_err_None; +} + +giblorb_err_t giblorb_destroy_map(giblorb_map_t *map) +{ + int ix; + + if (!map || !map->chunks || map->inited != giblorb_Inited_Magic) + return giblorb_err_NotAMap; + + for (ix=0; ixnumchunks; ix++) { + giblorb_chunkdesc_t *chu = &(map->chunks[ix]); + if (chu->ptr) { + giblorb_free(chu->ptr); + chu->ptr = NULL; + } + } + + if (map->chunks) { + giblorb_free(map->chunks); + map->chunks = NULL; + } + + map->numchunks = 0; + + if (map->resources) { + giblorb_free(map->resources); + map->resources = NULL; + } + + if (map->ressorted) { + giblorb_free(map->ressorted); + map->ressorted = NULL; + } + + map->numresources = 0; + + map->file = NULL; + map->inited = 0; + + giblorb_free(map); + + return giblorb_err_None; +} + +/* Chunk-handling functions. */ + +giblorb_err_t giblorb_load_chunk_by_type(giblorb_map_t *map, + glui32 method, giblorb_result_t *res, glui32 type, + glui32 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 giblorb_err_NotFound; + } + + return giblorb_load_chunk_by_number(map, method, res, ix); +} + +giblorb_err_t giblorb_load_chunk_by_number(giblorb_map_t *map, + glui32 method, giblorb_result_t *res, glui32 chunknum) +{ + giblorb_chunkdesc_t *chu; + + if (chunknum < 0 || chunknum >= map->numchunks) + return giblorb_err_NotFound; + + chu = &(map->chunks[chunknum]); + + switch (method) { + + case giblorb_method_DontLoad: + /* do nothing */ + break; + + case giblorb_method_FilePos: + res->data.startpos = chu->datpos; + break; + + case giblorb_method_Memory: + if (!chu->ptr) { + giblorb_err_t err; + glui32 readlen; + void *dat = giblorb_malloc(chu->len); + + if (!dat) + return giblorb_err_Alloc; + + glk_stream_set_position(map->file, chu->datpos, + seekmode_Start); + + readlen = glk_get_buffer_stream(map->file, dat, + chu->len); + if (readlen != chu->len) + return giblorb_err_Read; + + chu->ptr = dat; + } + res->data.ptr = chu->ptr; + break; + } + + res->chunknum = chunknum; + res->length = chu->len; + res->chunktype = chu->type; + + return giblorb_err_None; +} + +giblorb_err_t giblorb_load_resource(giblorb_map_t *map, glui32 method, + giblorb_result_t *res, glui32 usage, glui32 resnum) +{ + giblorb_resdesc_t sample; + giblorb_resdesc_t *found; + + sample.usage = usage; + sample.resnum = resnum; + + found = giblorb_bsearch(&sample, map->ressorted, map->numresources); + + if (!found) + return giblorb_err_NotFound; + + return giblorb_load_chunk_by_number(map, method, res, found->chunknum); +} + +giblorb_err_t giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum) +{ + giblorb_chunkdesc_t *chu; + + if (chunknum < 0 || chunknum >= map->numchunks) + return giblorb_err_NotFound; + + chu = &(map->chunks[chunknum]); + + if (chu->ptr) { + giblorb_free(chu->ptr); + chu->ptr = NULL; + } + + return giblorb_err_None; +} + +giblorb_err_t giblorb_count_resources(giblorb_map_t *map, glui32 usage, + glui32 *num, glui32 *min, glui32 *max) +{ + int ix; + int count; + glui32 val; + glui32 minval, maxval; + + count = 0; + minval = 0; + maxval = 0; + + for (ix=0; ixnumresources; ix++) { + if (map->resources[ix].usage == usage) { + val = map->resources[ix].resnum; + if (count == 0) { + count++; + minval = val; + maxval = val; + } + else { + count++; + if (val < minval) + minval = val; + if (val > maxval) + maxval = val; + } + } + } + + if (num) + *num = count; + if (min) + *min = minval; + if (max) + *max = maxval; + + return giblorb_err_None; +} + +/* Sorting and searching. */ + +static int sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2) +{ + if (v1->usage < v2->usage) + return -1; + if (v1->usage > v2->usage) + return 1; + if (v1->resnum < v2->resnum) + return -1; + if (v1->resnum > v2->resnum) + return 1; + return 0; +} + +static void giblorb_qsort(giblorb_resdesc_t **list, int len) +{ + int ix, jx, res, val; + giblorb_resdesc_t *tmpptr, *pivot; + + if (len < 6) { + /* The list is short enough for a bubble-sort. */ + for (jx=len-1; jx>0; jx--) { + for (ix=0; ix 0) { + tmpptr = list[ix]; + list[ix] = list[ix+1]; + list[ix+1] = tmpptr; + } + } + } + } + else { + /* Split the list. */ + pivot = list[len/2]; + ix=0; + jx=len; + while (1) { + while (ix < jx-1 && sortsplot(list[ix], pivot) < 0) + ix++; + while (ix < jx-1 && sortsplot(list[jx-1], pivot) > 0) + jx--; + if (ix >= jx-1) + break; + tmpptr = list[ix]; + list[ix] = list[jx-1]; + list[jx-1] = tmpptr; + } + ix++; + /* Sort the halves. */ + giblorb_qsort(list+0, ix); + giblorb_qsort(list+ix, len-ix); + } +} + +giblorb_resdesc_t *giblorb_bsearch(giblorb_resdesc_t *sample, + giblorb_resdesc_t **list, int len) +{ + int top, bot, val, res; + + bot = 0; + top = len; + + while (bot < top) { + val = (top+bot) / 2; + res = sortsplot(list[val], sample); + if (res == 0) + return list[val]; + if (res < 0) { + bot = val+1; + } + else { + top = val; + } + } + + return NULL; +} + + +/* Boring utility functions. If your platform doesn't support ANSI + malloc(), feel free to edit these however you like. */ + +#include /* The OS-native header file -- you can edit + this too. */ + +static void *giblorb_malloc(glui32 len) +{ + return malloc(len); +} + +static void *giblorb_realloc(void *ptr, glui32 len) +{ + return realloc(ptr, len); +} + +static void giblorb_free(void *ptr) +{ + free(ptr); +} + + diff --git a/src/gi_blorb.h b/src/gi_blorb.h new file mode 100644 index 0000000..ddec1bd --- /dev/null +++ b/src/gi_blorb.h @@ -0,0 +1,88 @@ +#ifndef _GI_BLORB_H +#define _GI_BLORB_H + +/* gi_blorb.h: Blorb library layer for Glk API. + gi_blorb version 1.4. + Designed by Andrew Plotkin + http://www.eblong.com/zarf/glk/index.html + + This file is copyright 1998-2000 by Andrew Plotkin. You may copy, + distribute, and incorporate it into your own programs, by any means + and under any conditions, as long as you do not modify it. You may + also modify this file, incorporate it into your own programs, + and distribute the modified version, as long as you retain a notice + in your program or documentation which mentions my name and the URL + shown above. +*/ + +/* Error type and error codes */ +typedef glui32 giblorb_err_t; +#define giblorb_err_None (0) +#define giblorb_err_CompileTime (1) +#define giblorb_err_Alloc (2) +#define giblorb_err_Read (3) +#define giblorb_err_NotAMap (4) +#define giblorb_err_Format (5) +#define giblorb_err_NotFound (6) + +/* Methods for loading a chunk */ +#define giblorb_method_DontLoad (0) +#define giblorb_method_Memory (1) +#define giblorb_method_FilePos (2) + +/* Four-byte constants */ + +#define giblorb_make_id(c1, c2, c3, c4) \ + (((c1) << 24) | ((c2) << 16) | ((c3) << 8) | (c4)) + +#define giblorb_ID_Snd (giblorb_make_id('S', 'n', 'd', ' ')) +#define giblorb_ID_Exec (giblorb_make_id('E', 'x', 'e', 'c')) +#define giblorb_ID_Pict (giblorb_make_id('P', 'i', 'c', 't')) +#define giblorb_ID_Copyright (giblorb_make_id('(', 'c', ')', ' ')) +#define giblorb_ID_AUTH (giblorb_make_id('A', 'U', 'T', 'H')) +#define giblorb_ID_ANNO (giblorb_make_id('A', 'N', 'N', 'O')) + +/* giblorb_map_t: Holds the complete description of an open Blorb + file. This type is opaque for normal interpreter use. */ +typedef struct giblorb_map_struct giblorb_map_t; + +/* giblorb_result_t: Result when you try to load a chunk. */ +typedef struct giblorb_result_struct { + glui32 chunknum; /* The chunk number (for use in + giblorb_unload_chunk(), etc.) */ + union { + void *ptr; /* A pointer to the data (if you used + giblorb_method_Memory) */ + glui32 startpos; /* The position in the file (if you + used giblorb_method_FilePos) */ + } data; + glui32 length; /* The length of the data */ + glui32 chunktype; /* The type of the chunk. */ +} giblorb_result_t; + +extern giblorb_err_t giblorb_create_map(strid_t file, + giblorb_map_t **newmap); +extern giblorb_err_t giblorb_destroy_map(giblorb_map_t *map); + +extern giblorb_err_t giblorb_load_chunk_by_type(giblorb_map_t *map, + glui32 method, giblorb_result_t *res, glui32 chunktype, + glui32 count); +extern giblorb_err_t giblorb_load_chunk_by_number(giblorb_map_t *map, + glui32 method, giblorb_result_t *res, glui32 chunknum); +extern giblorb_err_t giblorb_unload_chunk(giblorb_map_t *map, + glui32 chunknum); + +extern giblorb_err_t giblorb_load_resource(giblorb_map_t *map, + glui32 method, giblorb_result_t *res, glui32 usage, + glui32 resnum); +extern giblorb_err_t giblorb_count_resources(giblorb_map_t *map, + glui32 usage, glui32 *num, glui32 *min, glui32 *max); + +/* The following functions are part of the Glk library itself, not + the Blorb layer (whose code is in gi_blorb.c). These functions + are necessarily implemented in platform-dependent code. +*/ +extern giblorb_err_t giblorb_set_resource_map(strid_t file); +extern giblorb_map_t *giblorb_get_resource_map(void); + +#endif /* _GI_BLORB_H */ -- 2.30.2