aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCameron Cawley2019-03-30 23:30:10 +0000
committerPaul Gilbert2019-03-31 16:37:47 -0700
commit7b4c6d6a35f00e46d9021b236393a40aa54e6a6e (patch)
treebc6478a404a3ff9014620fe52a370b6a0cf7a5fc
parent82ccce948aaafed2f8e847359ab87c1a4878c74b (diff)
downloadscummvm-rg350-7b4c6d6a35f00e46d9021b236393a40aa54e6a6e.tar.gz
scummvm-rg350-7b4c6d6a35f00e46d9021b236393a40aa54e6a6e.tar.bz2
scummvm-rg350-7b4c6d6a35f00e46d9021b236393a40aa54e6a6e.zip
GLK: Improved detection of Blorb files
-rw-r--r--engines/glk/blorb.cpp141
-rw-r--r--engines/glk/blorb.h45
-rw-r--r--engines/glk/frotz/detection.cpp21
-rw-r--r--engines/glk/glulxe/detection.cpp15
-rw-r--r--engines/glk/magnetic/detection.cpp3
-rw-r--r--engines/glk/scott/detection.cpp45
-rw-r--r--engines/glk/tads/detection.cpp20
7 files changed, 178 insertions, 112 deletions
diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index ce86dee5f7..ebf5e0150b 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -24,32 +24,6 @@
namespace Glk {
-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_Rect = MKTAG('R', 'e', 'c', 't'),
-
- 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) :
@@ -111,39 +85,18 @@ Common::ErrorCode Blorb::load() {
// First, chew through the file and index the chunks
Common::File f;
if ((!_filename.empty() && !f.open(_filename)) ||
- (_filename.empty() && !f.open(_fileNode)) ||
- f.size() < 12)
+ (_filename.empty() && !f.open(_fileNode)))
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)
+ if (!isBlorb(f))
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);
- }
+ if (!readRIdx(f, _chunks))
+ return Common::kReadingFailed;
// 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);
@@ -173,22 +126,21 @@ Common::ErrorCode Blorb::load() {
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_GLULXE && type == "GLUL") ||
- (_interpType == INTERPRETER_TADS2 && type == "TAD2") ||
- (_interpType == INTERPRETER_TADS3 && type == "TAD3") ||
- (_interpType == INTERPRETER_HUGO && type == "HUGO") ||
- (_interpType == INTERPRETER_SCOTT && type == "SAAI")
+ (_interpType == INTERPRETER_FROTZ && ce._id == ID_ZCOD) ||
+ (_interpType == INTERPRETER_GLULXE && ce._id == ID_GLUL) ||
+ (_interpType == INTERPRETER_TADS2 && ce._id == ID_TAD2) ||
+ (_interpType == INTERPRETER_TADS3 && ce._id == ID_TAD3) ||
+ (_interpType == INTERPRETER_HUGO && ce._id == ID_HUGO) ||
+ (_interpType == INTERPRETER_SCOTT && ce._id == ID_SAAI)
) {
// Game executable
ce._filename = "game";
} else {
+ char buffer[5];
+ WRITE_BE_UINT32(buffer, ce._id);
+ buffer[4] = '\0';
+ Common::String type(buffer);
ce._filename = type;
}
}
@@ -197,9 +149,68 @@ Common::ErrorCode Blorb::load() {
return Common::kNoError;
}
-bool Blorb::isBlorb(const Common::String &filename) {
- return filename.hasSuffixIgnoreCase(".blorb") || filename.hasSuffixIgnoreCase(".zblorb")
- || filename.hasSuffixIgnoreCase(".gblorb") || filename.hasSuffixIgnoreCase(".blb");
+bool Blorb::readRIdx(Common::SeekableReadStream &stream, Common::Array<ChunkEntry> &chunks) {
+ if (stream.readUint32BE() != ID_RIdx)
+ return false;
+
+ stream.readUint32BE();
+ uint count = stream.readUint32BE();
+
+ // First read in the resource index
+ for (uint idx = 0; idx < count; ++idx) {
+ ChunkEntry ce;
+ ce._type = stream.readUint32BE();
+ ce._number = stream.readUint32BE();
+ ce._offset = stream.readUint32BE();
+
+ chunks.push_back(ce);
+ }
+
+ // Further iterate through the resources
+ for (uint idx = 0; idx < chunks.size(); ++idx) {
+ ChunkEntry &ce = chunks[idx];
+ stream.seek(ce._offset);
+ ce._offset += 8;
+
+ ce._id = stream.readUint32BE();
+ ce._size = stream.readUint32BE();
+ }
+
+ return true;
+}
+
+bool Blorb::isBlorb(Common::SeekableReadStream &stream, uint32 type) {
+ if (stream.size() < 12)
+ return false;
+ if (stream.readUint32BE() != ID_FORM)
+ return false;
+ stream.readUint32BE();
+ if (stream.readUint32BE() != ID_IFRS)
+ return false;
+
+ if (type == 0)
+ return true;
+
+ Common::Array<ChunkEntry> chunks;
+ if (!readRIdx(stream, chunks))
+ return false;
+
+ // Further iterate through the resources
+ for (uint idx = 0; idx < chunks.size(); ++idx) {
+ ChunkEntry &ce = chunks[idx];
+ if (ce._type == ID_Exec && ce._id == type)
+ return true;
+ }
+
+ return false;
+}
+
+bool Blorb::isBlorb(const Common::String &filename, uint32 type) {
+ Common::File f;
+ if (!filename.empty() && !f.open(filename))
+ return false;
+
+ return isBlorb(f, type);
}
} // End of namespace Glk
diff --git a/engines/glk/blorb.h b/engines/glk/blorb.h
index 18a84a5f08..e5dde288ac 100644
--- a/engines/glk/blorb.h
+++ b/engines/glk/blorb.h
@@ -42,6 +42,39 @@ struct ChunkEntry {
Common::String _filename;
};
+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_ZCOD = MKTAG('Z', 'C', 'O', 'D'),
+ ID_GLUL = MKTAG('G', 'L', 'U', 'L'),
+ ID_TAD2 = MKTAG('T', 'A', 'D', '2'),
+ ID_TAD3 = MKTAG('T', 'A', 'D', '3'),
+ ID_HUGO = MKTAG('H', 'U', 'G', 'O'),
+ ID_SAAI = MKTAG('S', 'A', 'A', 'I'),
+
+ ID_JPEG = MKTAG('J', 'P', 'E', 'G'),
+ ID_PNG = MKTAG('P', 'N', 'G', ' '),
+ ID_Rect = MKTAG('R', 'e', 'c', 't'),
+
+ 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 file manager
*/
@@ -95,9 +128,19 @@ public:
virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const override;
/**
+ * Read the RIdx section from the stream.
+ */
+ static bool readRIdx(Common::SeekableReadStream &stream, Common::Array<ChunkEntry> &chunks);
+
+ /**
+ * Returns true if a given file is a Blorb file
+ */
+ static bool isBlorb(Common::SeekableReadStream &stream, uint32 type = 0);
+
+ /**
* Returns true if a given filename specifies a Blorb file
*/
- static bool isBlorb(const Common::String &filename);
+ static bool isBlorb(const Common::String &filename, uint32 type = 0);
};
} // End of namespace Glk
diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index 273ca37342..4df4fd2be8 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -57,7 +57,7 @@ GameDescriptor FrotzMetaEngine::findGame(const char *gameId) {
bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
const char *const EXTENSIONS[] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8",
- ".zblorb", ".dat", ".zip", nullptr };
+ ".dat", ".zip", nullptr };
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
@@ -65,11 +65,9 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
if (file->isDirectory())
continue;
Common::String filename = file->getName();
- bool hasExt = false;
+ bool hasExt = false, isBlorb = false;
for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext)
hasExt = filename.hasSuffixIgnoreCase(*ext);
- if (!hasExt)
- continue;
// Open up the file and calculate the md5, and get the serial
Common::File gameFile;
@@ -79,15 +77,19 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
size_t filesize = gameFile.size();
char serial[9] = "";
bool emptyBlorb = false;
+ gameFile.seek(0);
+ isBlorb = Blorb::isBlorb(gameFile, ID_ZCOD);
- if (!filename.hasSuffixIgnoreCase(".zblorb")) {
+ if (!isBlorb) {
+ if (!hasExt) {
+ gameFile.close();
+ continue;
+ }
gameFile.seek(18);
strcpy(&serial[0], "\"");
gameFile.read(&serial[1], 6);
strcpy(&serial[7], "\"");
- }
- gameFile.close();
- if (filename.hasSuffixIgnoreCase(".zblorb")) {
+ } else {
Blorb b(*file, INTERPRETER_FROTZ);
Common::SeekableReadStream *f = b.createReadStreamForMember("game");
emptyBlorb = f == nullptr;
@@ -100,13 +102,14 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
delete f;
}
}
+ gameFile.close();
// Check for known games. Note that there has been some variation in exact filesizes
// for Infocom games due to padding at the end of files. So we match on md5s for the
// first 5Kb, and only worry about filesize for more recent Blorb based Zcode games
const FrotzGameDescription *p = FROTZ_GAMES;
while (p->_gameId && p->_md5 && (md5 != p->_md5 ||
- (filesize != p->_filesize && filename.hasSuffix(".zblorb"))))
+ (filesize != p->_filesize && isBlorb)))
++p;
DetectedGame gd;
diff --git a/engines/glk/glulxe/detection.cpp b/engines/glk/glulxe/detection.cpp
index 06ce40362d..64c6eabb5a 100644
--- a/engines/glk/glulxe/detection.cpp
+++ b/engines/glk/glulxe/detection.cpp
@@ -22,6 +22,7 @@
#include "glk/glulxe/detection.h"
#include "glk/glulxe/detection_tables.h"
+#include "glk/blorb.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/md5.h"
@@ -46,7 +47,7 @@ GameDescriptor GlulxeMetaEngine::findGame(const char *gameId) {
}
bool GlulxeMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
- const char *const EXTENSIONS[] = { ".ulx", ".blb", ".gblorb", nullptr };
+ const char *const EXTENSIONS[] = { ".ulx", nullptr };
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
@@ -54,11 +55,9 @@ bool GlulxeMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &
if (file->isDirectory())
continue;
Common::String filename = file->getName();
- bool hasExt = false;
+ bool hasExt = false, isBlorb = false;
for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext)
hasExt = filename.hasSuffixIgnoreCase(*ext);
- if (!hasExt)
- continue;
// Open up the file and calculate the md5
Common::File gameFile;
@@ -66,8 +65,13 @@ bool GlulxeMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &
continue;
Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
size_t filesize = gameFile.size();
+ gameFile.seek(0);
+ isBlorb = Blorb::isBlorb(gameFile, ID_GLUL);
gameFile.close();
+ if (!hasExt && !isBlorb)
+ continue;
+
// Check for known games
const GlulxeGameDescription *p = GLULXE_GAMES;
while (p->_gameId && (md5 != p->_md5 || filesize != p->_filesize))
@@ -75,9 +79,6 @@ bool GlulxeMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &
DetectedGame gd;
if (!p->_gameId) {
- if (filename.hasSuffixIgnoreCase(".blb"))
- continue;
-
if (gDebugLevel > 0) {
// Print an entry suitable for putting into the detection_tables.h, using the
// name of the parent folder the game is in as the presumed game Id
diff --git a/engines/glk/magnetic/detection.cpp b/engines/glk/magnetic/detection.cpp
index f7021065bc..cfd0102895 100644
--- a/engines/glk/magnetic/detection.cpp
+++ b/engines/glk/magnetic/detection.cpp
@@ -75,9 +75,6 @@ bool MagneticMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames
DetectedGame gd;
if (!p->_gameId) {
- if (filename.hasSuffixIgnoreCase(".blb"))
- continue;
-
if (gDebugLevel > 0) {
// Print an entry suitable for putting into the detection_tables.h, using the
// name of the parent folder the game is in as the presumed game Id
diff --git a/engines/glk/scott/detection.cpp b/engines/glk/scott/detection.cpp
index f1c4b9aa69..0e89b13f86 100644
--- a/engines/glk/scott/detection.cpp
+++ b/engines/glk/scott/detection.cpp
@@ -22,6 +22,7 @@
#include "glk/scott/detection.h"
#include "glk/scott/detection_tables.h"
+#include "glk/blorb.h"
#include "common/file.h"
#include "common/md5.h"
#include "engines/game.h"
@@ -44,41 +45,43 @@ GameDescriptor ScottMetaEngine::findGame(const char *gameId) {
}
bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
- const char *const EXTENSIONS[] = { ".saga", ".dat", ".blb", ".blorb", nullptr };
- Common::File gameFile;
- Common::String md5;
+ const char *const EXTENSIONS[] = { ".saga", ".dat", nullptr };
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
- Common::String name = file->getName();
+ // Check for a recognised filename
if (file->isDirectory())
continue;
Common::String filename = file->getName();
- bool hasExt = false;
+ bool hasExt = false, isBlorb = false;
for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext)
hasExt = filename.hasSuffixIgnoreCase(*ext);
- if (!hasExt)
- continue;
- if (gameFile.open(*file)) {
- md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+ Common::File gameFile;
+ if (!gameFile.open(*file))
+ continue;
+ Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+ int32 filesize = gameFile.size();
+ gameFile.seek(0);
+ isBlorb = Blorb::isBlorb(gameFile, ID_SAAI);
+ gameFile.close();
- // Scan through the Scott game list for a match
- const ScottGame *p = SCOTT_GAMES;
- while (p->_md5 && p->_filesize != gameFile.size() && md5 != p->_md5)
- ++p;
+ if (!hasExt && !isBlorb)
+ continue;
- if (p->_filesize) {
- // Found a match
- PlainGameDescriptor gameDesc = findGame(p->_gameId);
- DetectedGame gd(p->_gameId, gameDesc.description, Common::EN_ANY, Common::kPlatformUnknown);
- gd.addExtraEntry("filename", file->getName());
+ // Scan through the Scott game list for a match
+ const ScottGame *p = SCOTT_GAMES;
+ while (p->_md5 && p->_filesize != filesize && md5 != p->_md5)
+ ++p;
- gameList.push_back(gd);
- }
+ if (p->_filesize) {
+ // Found a match
+ PlainGameDescriptor gameDesc = findGame(p->_gameId);
+ DetectedGame gd(p->_gameId, gameDesc.description, Common::EN_ANY, Common::kPlatformUnknown);
+ gd.addExtraEntry("filename", file->getName());
- gameFile.close();
+ gameList.push_back(gd);
}
}
diff --git a/engines/glk/tads/detection.cpp b/engines/glk/tads/detection.cpp
index 9e1eb0833d..92633dacef 100644
--- a/engines/glk/tads/detection.cpp
+++ b/engines/glk/tads/detection.cpp
@@ -22,6 +22,7 @@
#include "glk/tads/detection.h"
#include "glk/tads/detection_tables.h"
+#include "glk/blorb.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/md5.h"
@@ -58,22 +59,32 @@ GameDescriptor TADSMetaEngine::findGame(const char *gameId) {
}
bool TADSMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+ const char *const EXTENSIONS[] = { ".gam", nullptr };
+
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
// Check for a recognised filename
- Common::String filename = file->getName();
- if (file->isDirectory() || !(filename.hasSuffixIgnoreCase(".gam")
- || filename.hasSuffixIgnoreCase(".blorb")))
+ if (file->isDirectory())
continue;
+ Common::String filename = file->getName();
+ bool hasExt = false, isBlorb = false;
+ for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext)
+ hasExt = filename.hasSuffixIgnoreCase(*ext);
+
// Open up the file and calculate the md5
Common::File gameFile;
if (!gameFile.open(*file))
continue;
Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
size_t filesize = gameFile.size();
+ gameFile.seek(0);
+ isBlorb = Blorb::isBlorb(gameFile, ID_TAD2) || Blorb::isBlorb(gameFile, ID_TAD3);
gameFile.close();
+ if (!hasExt && !isBlorb)
+ continue;
+
// Check for known games
const TADSGameDescription *p = TADS_GAMES;
while (p->_gameId && p->_md5 && (md5 != p->_md5 || filesize != p->_filesize))
@@ -81,9 +92,6 @@ bool TADSMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &ga
DetectedGame gd;
if (!p->_gameId) {
- if (!filename.hasSuffixIgnoreCase(".gam"))
- continue;
-
if (gDebugLevel > 0) {
// Print an entry suitable for putting into the detection_tables.h, using the
Common::String fname = filename;