From 13e28c0d0dad6727050e282eb475ab0052d892a6 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Tue, 20 Nov 2018 21:39:05 -0800 Subject: GLK: Finish refactoring Blorb class --- engines/glk/blorb.cpp | 579 +++++++++++--------------------------------------- engines/glk/blorb.h | 112 +--------- engines/glk/glk.cpp | 2 +- 3 files changed, 134 insertions(+), 559 deletions(-) diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp index 2a8ef45a85..ace0e91813 100644 --- a/engines/glk/blorb.cpp +++ b/engines/glk/blorb.cpp @@ -24,497 +24,162 @@ namespace Glk { -#define giblorb_Inited_Magic 0xB7012BEDU - -/** - * Describes one chunk of the Blorb file. - */ -struct giblorb_chunkdesc_struct { - glui32 type; - glui32 len; - glui32 startpos; ///< start of chunk header - glui32 datpos; ///< start of data (either startpos or startpos+8) - - byte *ptr; ///< pointer to malloc'd data, if loaded - int auxdatnum; ///< entry in the auxsound/auxpict array; -1 if none. This only applies to chunks that represent resources; -}; -typedef giblorb_chunkdesc_struct giblorb_chunkdesc_t; - -/** - * Describes one resource in the Blorb file. - */ -struct giblorb_resdesc_struct { - glui32 usage; - glui32 resnum; - glui32 chunknum; -}; - -/** - * Holds the complete description of an open Blorb file. - */ -struct giblorb_map_struct { - glui32 inited; ///< holds giblorb_Inited_Magic if the map structure is valid - - uint numchunks; - giblorb_chunkdesc_t *chunks; ///< list of chunk descriptors - - int numresources; - giblorb_resdesc_t *resources; ///< list of resource descriptors - giblorb_resdesc_t **ressorted; ///< list of pointers to descriptors in map->resources -- sorted by usage and resource number. +enum { + ID_FORM = MKTAG('F', 'O', 'R', 'M'), + ID_IFRS = MKTAG('I', 'F', 'R', 'S'), + ID_RIdx = MKTAG('R', 'I', 'd', 'x'), + + ID_Snd = MKTAG('S', 'n', 'd', ' '), + ID_Exec = MKTAG('E', 'x', 'e', 'c'), + ID_Pict = MKTAG('P', 'i', 'c', 't'), + ID_Data = MKTAG('D', 'a', 't', 'a'), + + ID_Copyright = MKTAG('(', 'c', ')', ' '), + ID_AUTH = MKTAG('A', 'U', 'T', 'H'), + ID_ANNO = MKTAG('A', 'N', 'N', 'O'), + + ID_JPEG = MKTAG('J', 'P', 'E', 'G'), + ID_PNG = MKTAG('P', 'N', 'G', ' '), + + ID_MIDI = MKTAG('M', 'I', 'D', 'I'), + ID_MP3 = MKTAG('M', 'P', '3', ' '), + ID_WAVE = MKTAG('W', 'A', 'V', 'E'), + ID_AIFF = MKTAG('A', 'I', 'F', 'F'), + ID_OGG = MKTAG('O', 'G', 'G', ' '), + ID_MOD = MKTAG('M', 'O', 'D', ' '), }; /*--------------------------------------------------------------------------*/ Blorb::Blorb(const Common::String &filename, InterpreterType interpType) : - Common::Archive(), _interpType(interpType), _map(nullptr) { - if (!_file.open(filename)) - error("Could not open blorb file"); - - if (create_map() != giblorb_err_None) + Common::Archive(), _filename(filename), _interpType(interpType) { + if (load() != Common::kNoError) error("Could not parse blorb file"); } -Blorb::~Blorb() { - for (uint ix = 0; ix < _map->numchunks; ix++) { - giblorb_chunkdesc_t *chu = &(_map->chunks[ix]); - if (chu->ptr) { - delete chu->ptr; - chu->ptr = nullptr; - } - } - - if (_map->chunks) { - delete[] _map->chunks; - _map->chunks = nullptr; - } - - _map->numchunks = 0; - - if (_map->resources) { - delete[] _map->resources; - _map->resources = nullptr; - } - - if (_map->ressorted) { - delete[] _map->ressorted; - _map->ressorted = nullptr; +bool Blorb::hasFile(const Common::String &name) const { + for (uint idx = 0; idx < _chunks.size(); ++idx) { + if (_chunks[idx]._filename.equalsIgnoreCase(name)) + return true; } - _map->numresources = 0; - _map->inited = 0; - - delete _map; -} - -bool Blorb::hasFile(const Common::String &name) const { return false; } -int Blorb::listMatchingMembers(Common::ArchiveMemberList &list, const Common::String &pattern) const { - return 0; -} - int Blorb::listMembers(Common::ArchiveMemberList &list) const { - return 0; -} - -const Common::ArchiveMemberPtr Blorb::getMember(const Common::String &name) const { - return Common::ArchiveMemberPtr(); -} - -Common::SeekableReadStream *Blorb::createReadStreamForMember(const Common::String &name) const { - return nullptr; -} - -giblorb_err_t Blorb::create_map() { - giblorb_err_t err; - giblorb_map_t *map; - glui32 readlen; - glui32 nextpos, totallength; - giblorb_chunkdesc_t *chunks; - int chunks_size, numchunks; - char buffer[16]; - - // First, chew through the file and index the chunks - _file.seek(0); - - readlen = _file.read(buffer, 12); - if (readlen != 12) - return giblorb_err_Format; - - if (READ_BE_INT32(buffer + 0) != giblorb_ID_FORM) - return giblorb_err_Format; - if (READ_BE_INT32(buffer + 8) != giblorb_ID_IFRS) - return giblorb_err_Format; - - totallength = READ_BE_INT32(buffer + 4) + 8; - nextpos = 12; - - chunks_size = 8; - numchunks = 0; - chunks = new giblorb_chunkdesc_t[chunks_size]; - - while (nextpos < totallength) { - glui32 type, len; - int chunum; - giblorb_chunkdesc_t *chu; - - _file.seek(nextpos); - - readlen = _file.read(buffer, 8); - if (readlen != 8) { - delete[] chunks; - return giblorb_err_Read; - } - - type = READ_BE_INT32(buffer + 0); - len = READ_BE_INT32(buffer + 4); - - if (numchunks >= chunks_size) { - chunks_size *= 2; - chunks = new giblorb_chunkdesc_t[chunks_size]; - } - - chunum = numchunks; - chu = &(chunks[chunum]); - numchunks++; - - chu->type = type; - chu->startpos = nextpos; - if (type == giblorb_ID_FORM) { - chu->datpos = nextpos; - chu->len = len + 8; - } else { - chu->datpos = nextpos + 8; - chu->len = len; - } - chu->ptr = nullptr; - chu->auxdatnum = -1; - - nextpos = nextpos + len + 8; - if (nextpos & 1) - nextpos++; - - if (nextpos > totallength) { - delete[] chunks; - return giblorb_err_Format; - } - } - - // The basic IFF structure seems to be ok, and we have a list of chunks. - // Now we allocate the map structure itself. - _map = new giblorb_map_t(); - if (!_map) { - delete[] chunks; - return giblorb_err_Alloc; + for (uint idx = 0; idx < _chunks.size(); ++idx) { + list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(_chunks[idx]._filename, this))); } - _map->inited = giblorb_Inited_Magic; - _map->chunks = chunks; - _map->numchunks = numchunks; - _map->resources = nullptr; - _map->ressorted = nullptr; - _map->numresources = 0; - - // Now we do everything else involved in loading the Blorb file, - // such as building resource lists. - err = initialize_map(); - if (err) - return err; - - return giblorb_err_None; -} - -giblorb_err_t Blorb::initialize_map() { - // It is important that the map structure be kept valid during this function. - // If this returns an error, giblorb_destroy_map() will be called. - uint ix, jx; - giblorb_result_t chunkres; - giblorb_err_t err; - char *ptr; - glui32 len; - glui32 numres; - int gotindex = false; - - for (ix = 0; ix < _map->numchunks; ix++) { - giblorb_chunkdesc_t *chu = &_map->chunks[ix]; - - switch (chu->type) { - case giblorb_ID_RIdx: - // Resource index chunk: build the resource list and sort it. - - if (gotindex) - return giblorb_err_Format; // duplicate index chunk - err = load_chunk_by_number(giblorb_method_Memory, &chunkres, ix); - if (err) - return err; - - ptr = (char *)chunkres.data.ptr; - len = chunkres.length; - numres = READ_BE_INT32(ptr + 0); - - if (numres) { - uint ix2; - giblorb_resdesc_t *resources; - giblorb_resdesc_t **ressorted; - - if (len != numres * 12 + 4) - return giblorb_err_Format; // bad length field - - resources = new giblorb_resdesc_t[numres]; - ressorted = new giblorb_resdesc_t *[numres]; - if (!ressorted || !resources) { - delete[] resources; - delete[] ressorted; - return giblorb_err_Alloc; - } - - ix2 = 0; - for (jx = 0; jx < numres; jx++) { - giblorb_resdesc_t *res = &(resources[jx]); - glui32 respos; - - res->usage = READ_BE_INT32(ptr + jx * 12 + 4); - res->resnum = READ_BE_INT32(ptr + jx * 12 + 8); - respos = READ_BE_INT32(ptr + jx * 12 + 12); - - while (ix2 < _map->numchunks - && _map->chunks[ix2].startpos < respos) - ix2++; - - if (ix2 >= _map->numchunks - || _map->chunks[ix2].startpos != respos) { - delete[] resources; - delete[] ressorted; - return giblorb_err_Format; // start pos does not match a real chunk - } - - res->chunknum = ix2; - - ressorted[jx] = res; - } - - // Sort a resource list (actually a list of pointers to structures in _map->resources.) - // This makes it easy to find resources by usage and resource number. - qsort(ressorted, numres); - - _map->numresources = numres; - _map->resources = resources; - _map->ressorted = ressorted; - } - - unload_chunk(ix); - gotindex = true; - break; - } - } - - return giblorb_err_None; + return (int)_chunks.size(); } -void Blorb::qsort(giblorb_resdesc_t **list, size_t len) { - int ix, jx, res; - giblorb_resdesc_t *tmpptr, *pivot; - - if (len < 6) { - // The list is short enough for a bubble-sort. - for (jx = len - 1; jx>0; jx--) { - for (ix = 0; ix 0) { - tmpptr = list[ix]; - list[ix] = list[ix + 1]; - list[ix + 1] = tmpptr; - } - } - } - } else { - // Split the list. - pivot = list[len / 2]; - ix = 0; - jx = len; - for (;;) { - while (ix < jx - 1 && sortsplot(list[ix], pivot) < 0) - ix++; - while (ix < jx - 1 && sortsplot(list[jx - 1], pivot) > 0) - jx--; - if (ix >= jx - 1) - break; - tmpptr = list[ix]; - list[ix] = list[jx - 1]; - list[jx - 1] = tmpptr; - } - ix++; - // Sort the halves. - qsort(list + 0, ix); - qsort(list + ix, len - ix); - } -} - -giblorb_resdesc_t *Blorb::bsearch(giblorb_resdesc_t *sample, - giblorb_resdesc_t **list, int len) { - int top, bot, val, res; - - bot = 0; - top = len; - - while (bot < top) { - val = (top + bot) / 2; - res = sortsplot(list[val], sample); - if (res == 0) - return list[val]; - if (res < 0) { - bot = val + 1; - } else { - top = val; - } - } - - return nullptr; -} - -int Blorb::sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2) { - if (v1->usage < v2->usage) - return -1; - if (v1->usage > v2->usage) - return 1; - if (v1->resnum < v2->resnum) - return -1; - if (v1->resnum > v2->resnum) - return 1; - return 0; -} - -giblorb_err_t Blorb::load_chunk_by_type(glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count) { - uint ix; - - for (ix = 0; ix < _map->numchunks; ix++) { - if (_map->chunks[ix].type == chunktype) { - if (count == 0) - break; - count--; - } - } - - if (ix >= _map->numchunks) { - return giblorb_err_NotFound; - } +const Common::ArchiveMemberPtr Blorb::getMember(const Common::String &name) const { + if (!hasFile(name)) + return Common::ArchiveMemberPtr(); - return load_chunk_by_number(method, res, ix); + return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); } -giblorb_err_t Blorb::load_chunk_by_number(glui32 method, giblorb_result_t *res, glui32 chunknum) { - giblorb_chunkdesc_t *chu; - - if (chunknum >= _map->numchunks) - return giblorb_err_NotFound; - - chu = &(_map->chunks[chunknum]); - - switch (method) { - case giblorb_method_DontLoad: - // do nothing - break; - - case giblorb_method_FilePos: - res->data.startpos = chu->datpos; - break; - - case giblorb_method_Memory: - if (!chu->ptr) { - glui32 readlen; - byte *dat = new byte[chu->len]; - - if (!dat) - return giblorb_err_Alloc; - - _file.seek(chu->datpos); +Common::SeekableReadStream *Blorb::createReadStreamForMember(const Common::String &name) const { + for (uint idx = 0; idx < _chunks.size(); ++idx) { + if (_chunks[idx]._filename.equalsIgnoreCase(name)) { + Common::File f; + if (!f.open(_filename)) + error("Reading failed"); - readlen = _file.read(dat, chu->len); - if (readlen != chu->len) - return giblorb_err_Read; + f.seek(_chunks[idx]._offset); + Common::SeekableReadStream *result = f.readStream(_chunks[idx]._size); + f.close(); - chu->ptr = dat; + return result; } - - res->data.ptr = chu->ptr; - break; } - res->chunknum = chunknum; - res->length = chu->len; - res->chunktype = chu->type; - - return giblorb_err_None; + return nullptr; } -giblorb_err_t Blorb::unload_chunk(glui32 chunknum) { - giblorb_chunkdesc_t *chu; - - if (chunknum >= _map->numchunks) - return giblorb_err_NotFound; - - chu = &(_map->chunks[chunknum]); - - if (chu->ptr) { - delete chu->ptr; - chu->ptr = nullptr; +Common::ErrorCode Blorb::load() { + // First, chew through the file and index the chunks + Common::File f; + if (!f.open(_filename) || f.size() < 12) + return Common::kReadingFailed; + + if (f.readUint32BE() != ID_FORM) + return Common::kReadingFailed; + f.readUint32BE(); + if (f.readUint32BE() != ID_IFRS) + return Common::kReadingFailed; + if (f.readUint32BE() != ID_RIdx) + return Common::kReadingFailed; + + f.readUint32BE(); + uint count = f.readUint32BE(); + + // First read in the resource index + for (uint idx = 0; idx < count; ++idx) { + ChunkEntry ce; + ce._type = f.readUint32BE(); + ce._number = f.readUint32BE(); + ce._offset = f.readUint32BE(); + + _chunks.push_back(ce); } - return giblorb_err_None; -} - -giblorb_err_t Blorb::load_resource(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 = bsearch(&sample, _map->ressorted, _map->numresources); - - if (!found) - return giblorb_err_NotFound; - - return load_chunk_by_number(method, res, found->chunknum); -} - -giblorb_err_t Blorb::count_resources(glui32 usage, glui32 *num, glui32 *min, glui32 *max) { - int ix; - int count; - glui32 val; - glui32 minval, maxval; - - count = 0; - minval = 0; - maxval = 0; - - for (ix = 0; ix<_map->numresources; ix++) { - if (_map->resources[ix].usage == usage) { - val = _map->resources[ix].resnum; - if (count == 0) { - count++; - minval = val; - maxval = val; - } - else { - count++; - if (val < minval) - minval = val; - if (val > maxval) - maxval = val; + // Further iterate through the resources + for (uint idx = 0; idx < _chunks.size(); ++idx) { + ChunkEntry &ce = _chunks[idx]; + f.seek(ce._offset); + ce._offset += 8; + + ce._id = f.readUint32BE(); + ce._size = f.readUint32BE(); + + if (ce._type == ID_Pict) { + ce._filename = Common::String::format("pic%u", ce._number); + if (ce._id == ID_JPEG) + ce._filename += ".jpeg"; + else if (ce._id == ID_PNG) + ce._filename += ".png"; + + } else if (ce._type == ID_Snd) { + ce._filename = Common::String::format("snd%u", ce._number); + if (ce._id == ID_MIDI) + ce._filename += ".midi"; + else if (ce._id == ID_MP3) + ce._filename += ".mp3"; + else if (ce._id == ID_WAVE) + ce._filename += ".wav"; + else if (ce._id == ID_AIFF) + ce._filename += ".aiff"; + else if (ce._id == ID_OGG) + ce._filename += ".ogg"; + else if (ce._id == ID_MOD) + ce._filename += ".mod"; + + } else if (ce._type == ID_Data) { + ce._filename = Common::String::format("data%u", ce._number); + + } else if (ce._type == ID_Exec) { + char buffer[5]; + WRITE_BE_UINT32(buffer, ce._id); + buffer[4] = '\0'; + Common::String type(buffer); + + if ( + (_interpType == INTERPRETER_FROTZ && type == "ZCOD") || + (_interpType == INTERPRETER_TADS && (type == "TAD2" || type == "TAD3")) || + (_interpType == INTERPRETER_HUGO && type == "HUGO") + ) { + // Game executable + ce._filename = "game"; + } else { + ce._filename = type; } } } - if (num) - *num = count; - if (min) - *min = minval; - if (max) - *max = maxval; - - return giblorb_err_None; + return Common::kNoError; } } // End of namespace Glk diff --git a/engines/glk/blorb.h b/engines/glk/blorb.h index 97fd520fcc..afd52af6d9 100644 --- a/engines/glk/blorb.h +++ b/engines/glk/blorb.h @@ -31,106 +31,36 @@ namespace Glk { /** - * Error type + * Describes one chunk of the Blorb file. */ -typedef glui32 giblorb_err_t; - -/** - * Error codes - */ -enum giblorbError { - giblorb_err_None = 0, - giblorb_err_CompileTime = 1, - giblorb_err_Alloc = 2, - giblorb_err_Read = 3, - giblorb_err_NotAMap = 4, - giblorb_err_Format = 5, - giblorb_err_NotFound = 6 -}; - -/** - * Methods for loading a chunk - */ -enum giblorbMethod { - giblorb_method_DontLoad = 0, - giblorb_method_Memory = 1, - giblorb_method_FilePos = 2 -}; - -enum { - giblorb_ID_Snd = MKTAG('S', 'n', 'd', ' '), - giblorb_ID_Exec = MKTAG('E', 'x', 'e', 'c'), - giblorb_ID_Pict = MKTAG('P', 'i', 'c', 't'), - giblorb_ID_Copyright = MKTAG('(', 'c', ')', ' '), - giblorb_ID_AUTH = MKTAG('A', 'U', 'T', 'H'), - giblorb_ID_ANNO = MKTAG('A', 'N', 'N', 'O') -}; - - -enum { - giblorb_ID_MOD = MKTAG('M', 'O', 'D', ' '), - giblorb_ID_FORM = MKTAG('F', 'O', 'R', 'M'), - giblorb_ID_IFRS = MKTAG('I', 'F', 'R', 'S'), - giblorb_ID_RIdx = MKTAG('R', 'I', 'd', 'x'), - giblorb_ID_OGG = MKTAG('O', 'G', 'G', 'V'), - - // non-standard types - giblorb_ID_MIDI = MKTAG('M', 'I', 'D', 'I'), - giblorb_ID_MP3 = MKTAG('M', 'P', '3', ' '), - giblorb_ID_WAVE = MKTAG('W', 'A', 'V', 'E') +struct ChunkEntry { + uint _type; + uint _number; + uint _id; + size_t _offset; + size_t _size; + Common::String _filename; }; -/** - * Holds the complete description of an open Blorb file. - * This type is opaque for normal interpreter use. - */ -typedef struct giblorb_map_struct giblorb_map_t; - -/** - * giblorb_result_t: Result when you try to load a chunk. - */ -typedef struct giblorb_result_struct { - glui32 chunknum; // The chunk number (for use in giblorb_unload_chunk(), etc.) - union { - void *ptr; ///< A pointer to the data (if you used giblorb_method_Memory) - glui32 startpos; ///< The position in the file (if you used giblorb_method_FilePos) - } data; - - glui32 length; ///< The length of the data - glui32 chunktype; ///< The type of the chunk. -} giblorb_result_t; - -typedef struct giblorb_resdesc_struct giblorb_resdesc_t; - /** * Blorb file manager */ class Blorb : public Common::Archive { private: - Common::File _file; + Common::String _filename; InterpreterType _interpType; - giblorb_map_t *_map; + Common::Array _chunks; ///< list of chunk descriptors private: /** * Parses the Blorb file index to load in a list of the chunks */ - giblorb_err_t create_map(); - - giblorb_err_t initialize_map(); - void qsort(giblorb_resdesc_t **list, size_t len); - giblorb_resdesc_t *bsearch(giblorb_resdesc_t *sample, giblorb_resdesc_t **list, int len); - int sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2); + Common::ErrorCode load(); public: /** * Constructor */ Blorb(const Common::String &filename, InterpreterType interpType); - /** - * Destructor - */ - ~Blorb(); - /** * Check if a member with the given name is present in the Archive. * Patterns are not allowed, as this is meant to be a quick File::exists() @@ -138,14 +68,6 @@ public: */ virtual bool hasFile(const Common::String &name) const override; - /** - * Add all members of the Archive matching the specified pattern to list. - * Must only append to list, and not remove elements from it. - * - * @return the number of members added to list - */ - virtual int listMatchingMembers(Common::ArchiveMemberList &list, const Common::String &pattern) const override; - /** * Add all members of the Archive to list. * Must only append to list, and not remove elements from it. @@ -165,18 +87,6 @@ public: * @return the newly created input stream */ virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const override; -public: - /** - * Get a pointer to the Blorb's resource map - */ - giblorb_map_t *get_resource_map() const { return _map; } - - giblorb_err_t load_chunk_by_type(glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count); - giblorb_err_t load_chunk_by_number(glui32 method, giblorb_result_t *res, glui32 chunknum); - giblorb_err_t unload_chunk(glui32 chunknum); - - giblorb_err_t load_resource(glui32 method, giblorb_result_t *res, glui32 usage, glui32 resnum); - giblorb_err_t count_resources(glui32 usage, glui32 *num, glui32 *min, glui32 *max); }; } // End of namespace Glk diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp index c2a8264750..87b736cb25 100644 --- a/engines/glk/glk.cpp +++ b/engines/glk/glk.cpp @@ -111,7 +111,7 @@ Common::Error GlkEngine::run() { _blorb = new Blorb(filename, getInterpreterType()); SearchMan.add("blorb", _blorb, 99, false); - if (!f.open("EXEC", *_blorb)) + if (!f.open("game", *_blorb)) return Common::kNoGameDataFoundError; } else { if (!f.open(filename)) -- cgit v1.2.3