[Scummvm-git-logs] scummvm master -> 34a1e5aac4478854f98bab7567e77e76ffc4170e

sluicebox noreply at scummvm.org
Tue Jan 16 17:12:32 UTC 2024


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
a5656b43fe AGI: Detect V3 volume format in V2 games
34a1e5aac4 AGI: Add error check when loading sounds


Commit: a5656b43fe9a9d037a4603e7c4b022b0cfe58036
    https://github.com/scummvm/scummvm/commit/a5656b43fe9a9d037a4603e7c4b022b0cfe58036
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2024-01-16T10:11:13-07:00

Commit Message:
AGI: Detect V3 volume format in V2 games

The CoCo3 version of Xmas Card 86 has a volume file with a V3 format.

The V2 loader now detects this and ignores the extra header bytes.

Fixes bug #14699

Changed paths:
    engines/agi/agi.h
    engines/agi/loader_v2.cpp
    engines/agi/loader_v3.cpp


diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index 499bcf7ae17..2dae7fad816 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -639,14 +639,17 @@ public:
 class AgiLoader_v2 : public AgiLoader {
 private:
 	AgiEngine *_vm;
+	bool _hasV3VolumeFormat;
 
 	int loadDir(AgiDir *agid, const char *fname);
 	uint8 *loadVolRes(AgiDir *agid);
+	bool detectV3VolumeFormat();
 
 public:
 
 	AgiLoader_v2(AgiEngine *vm) {
 		_vm = vm;
+		_hasV3VolumeFormat = false;
 	}
 
 	int init() override;
diff --git a/engines/agi/loader_v2.cpp b/engines/agi/loader_v2.cpp
index 6af7a657958..e2b9c5dfad2 100644
--- a/engines/agi/loader_v2.cpp
+++ b/engines/agi/loader_v2.cpp
@@ -83,6 +83,51 @@ int AgiLoader_v2::loadDir(AgiDir *agid, const char *fname) {
 	return errOK;
 }
 
+/**
+ * Detects if the volume format is really V3.
+ *
+ * The volume format for a V2 game should have 5 byte headers.
+ * The CoCo3 version of Xmas Card 86 has 7 byte headers.
+ * The resource length repeats as if it were a V3 volume with no compression.
+ *
+ * This function detects if a volume has this unusual structure so that
+ * loadVolRes() can ignore the two extra header bytes.
+ */
+bool AgiLoader_v2::detectV3VolumeFormat() {
+	uint8 volume = _vm->_game.dirLogic[0].volume;
+	Common::Path path(Common::String::format("vol.%i", volume));
+	Common::File volumeFile;
+	if (!volumeFile.open(path)) {
+		return false;
+	}
+
+	// read the first few entries and see if they match the 7 byte header
+	uint8 volumeHeader[7];
+	for (int i = 0; i < 5; i++) {
+		if (volumeFile.read(&volumeHeader, 7) != 7) {
+			return false;
+		}
+		// signature
+		if (READ_BE_UINT16(volumeHeader) != 0x1234) {
+			return false;
+		}
+		// volume number
+		if (volumeHeader[2] != volume) {
+			return false;
+		}
+		// duplicate resource lengths
+		uint16 resourceLength1 = READ_LE_UINT16(volumeHeader + 3);
+		uint16 resourceLength2 = READ_LE_UINT16(volumeHeader + 5);
+		if (resourceLength1 != resourceLength2) {
+			return false;
+		}
+		if (!volumeFile.seek(resourceLength1, SEEK_CUR)) {
+			return false;
+		}
+	}
+	return true;
+}
+
 int AgiLoader_v2::init() {
 	int ec = errOK;
 
@@ -94,6 +139,8 @@ int AgiLoader_v2::init() {
 		ec = loadDir(_vm->_game.dirView, VIEWDIR);
 	if (ec == errOK)
 		ec = loadDir(_vm->_game.dirSound, SNDDIR);
+	if (ec == errOK)
+		_hasV3VolumeFormat = detectV3VolumeFormat();
 
 	return ec;
 }
@@ -138,11 +185,11 @@ int AgiLoader_v2::unloadResource(int16 resourceType, int16 resourceNr) {
 /**
  * This function loads a raw resource into memory,
  * if further decoding is required, it must be done by another
- * routine. NULL is returned if unsucsessfull.
+ * routine. NULL is returned if unsuccessful.
  */
 uint8 *AgiLoader_v2::loadVolRes(struct AgiDir *agid) {
 	uint8 *data = nullptr;
-	char x[6];
+	uint8 volumeHeader[7];
 	Common::File fp;
 	unsigned int sig;
 	Common::Path path(Common::String::format("vol.%i", agid->volume));
@@ -152,9 +199,9 @@ uint8 *AgiLoader_v2::loadVolRes(struct AgiDir *agid) {
 	if (agid->offset != _EMPTY && fp.open(path)) {
 		debugC(3, kDebugLevelResources, "loading resource at offset %d", agid->offset);
 		fp.seek(agid->offset, SEEK_SET);
-		fp.read(&x, 5);
-		if ((sig = READ_BE_UINT16((uint8 *) x)) == 0x1234) {
-			agid->len = READ_LE_UINT16((uint8 *) x + 3);
+		fp.read(&volumeHeader, _hasV3VolumeFormat ? 7 : 5);
+		if ((sig = READ_BE_UINT16(volumeHeader)) == 0x1234) {
+			agid->len = READ_LE_UINT16(volumeHeader + 3);
 			data = (uint8 *)calloc(1, agid->len + 32);
 			if (data != nullptr) {
 				fp.read(data, agid->len);
diff --git a/engines/agi/loader_v3.cpp b/engines/agi/loader_v3.cpp
index 8f82ee02018..246c85dfa6c 100644
--- a/engines/agi/loader_v3.cpp
+++ b/engines/agi/loader_v3.cpp
@@ -194,7 +194,7 @@ int AgiLoader_v3::unloadResource(int16 resourceType, int16 resourceNr) {
  * If further decoding is required, it must be done by another
  * routine.
  *
- * NULL is returned if unsucsessful.
+ * NULL is returned if unsuccessful.
  */
 uint8 *AgiLoader_v3::loadVolRes(AgiDir *agid) {
 	char x[8];


Commit: 34a1e5aac4478854f98bab7567e77e76ffc4170e
    https://github.com/scummvm/scummvm/commit/34a1e5aac4478854f98bab7567e77e76ffc4170e
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2024-01-16T10:11:13-07:00

Commit Message:
AGI: Add error check when loading sounds

Fixes the CoCo3 version of Xmas Card 86 crashing on startup.

The sound format isn't supported yet but the return value from
AgiSound::createFromRawResource wasn't tested.

Changed paths:
    engines/agi/loader_v2.cpp


diff --git a/engines/agi/loader_v2.cpp b/engines/agi/loader_v2.cpp
index e2b9c5dfad2..65c7cc9bb3c 100644
--- a/engines/agi/loader_v2.cpp
+++ b/engines/agi/loader_v2.cpp
@@ -281,11 +281,12 @@ int AgiLoader_v2::loadResource(int16 resourceType, int16 resourceNr) {
 
 		data = loadVolRes(&_vm->_game.dirSound[resourceNr]);
 
-		if (data != nullptr) {
-			// Freeing of the raw resource from memory is delegated to the createFromRawResource-function
-			_vm->_game.sounds[resourceNr] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[resourceNr].len, resourceNr, _vm->_soundemu);
+		// "data" is freed by objects created by createFromRawResource on success
+		_vm->_game.sounds[resourceNr] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[resourceNr].len, resourceNr, _vm->_soundemu);
+		if (_vm->_game.sounds[resourceNr] != nullptr) {
 			_vm->_game.dirSound[resourceNr].flags |= RES_LOADED;
 		} else {
+			free(data);
 			ec = errBadResource;
 		}
 		break;




More information about the Scummvm-git-logs mailing list