mirror of
https://github.com/ganelson/inform.git
synced 2024-07-18 06:54:26 +03:00
823 lines
23 KiB
C
823 lines
23 KiB
C
/* 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"
|
|
|
|
#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
|
|
|
|
#ifndef bb_native4
|
|
"You must define either BLORB_BIG_ENDIAN or BLORB_LITTLE_ENDIAN in blorb.h \
|
|
in order to compile this library.";
|
|
#endif
|
|
|
|
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(bb_resdesc_t **p1, bb_resdesc_t **p2);
|
|
|
|
/* Do some one-time startup tests. */
|
|
static bb_err_t bb_initialize()
|
|
{
|
|
union {
|
|
uint32 val;
|
|
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 *),
|
|
(int (*)())&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(bb_resdesc_t **p1, bb_resdesc_t **p2)
|
|
{
|
|
bb_resdesc_t *v1 = *p1;
|
|
bb_resdesc_t *v2 = *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 *),
|
|
(int (*)())&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);
|
|
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;
|
|
}
|
|
|