[Scummvm-git-logs] scummvm master -> e358d02685fe8bb820e6de49b3752c6b3bbc9ea2

sev- noreply at scummvm.org
Sat Aug 9 19:03:48 UTC 2025


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

Summary:
7f51b61906 TEENAGENT: Unify string resources into one segment
4768231b0e TEENAGENT: Add support for Polish voice acting
2faa0d7f03 TEENAGENT: Remove unused variables in Resources class
e358d02685 TEENAGENT: Fix description being not shown for inventory items


Commit: 7f51b61906715fe4adfc70837c5224682a63df37
    https://github.com/scummvm/scummvm/commit/7f51b61906715fe4adfc70837c5224682a63df37
Author: Alikhan Balpykov (luxrage1990 at gmail.com)
Date: 2025-08-09T21:03:44+02:00

Commit Message:
TEENAGENT: Unify string resources into one segment

Store every string resource (message, dialog, credits, etc.) into one
big segment - eseg. This change is a building block for adding
voiceovers in the next commits. In addition in certain methods "addr"
parameter is now 32-bit unsigned integer instead 16-bit. This is needed
as 16-bit address space is not enough store polish and czech strings.

Changed paths:
    devtools/create_teenagent/create_teenagent.cpp
    devtools/create_teenagent/create_teenagent.h
    dists/engine-data/teenagent.dat
    engines/teenagent/callbacks.cpp
    engines/teenagent/dialog.cpp
    engines/teenagent/dialog.h
    engines/teenagent/inventory.cpp
    engines/teenagent/resources.cpp
    engines/teenagent/resources.h
    engines/teenagent/scene.cpp
    engines/teenagent/teenagent.cpp
    engines/teenagent/teenagent.h


diff --git a/devtools/create_teenagent/create_teenagent.cpp b/devtools/create_teenagent/create_teenagent.cpp
index c9d1fd54292..e71a8fedb50 100644
--- a/devtools/create_teenagent/create_teenagent.cpp
+++ b/devtools/create_teenagent/create_teenagent.cpp
@@ -311,11 +311,8 @@ void writeSceneObjects(FILE *fd, Common::Language language) {
 	fseek(fd, pos, SEEK_SET);
 }
 
-void writeResource(FILE *fd, ResourceType resType, Common::Language language) {
-	uint currentFilePos = ftell(fd);
-	uint prevFilePos = currentFilePos;
-	uint32 resourceSize = 0;
-	writeUint32LE(fd, resourceSize);
+uint32 writeResource(FILE *fd, ResourceType resType, Common::Language language) {
+	uint prevFilePos = ftell(fd);
 
 	switch (resType) {
 	case kResCredits: {
@@ -358,11 +355,9 @@ void writeResource(FILE *fd, ResourceType resType, Common::Language language) {
 		break;
 	};
 
-	currentFilePos = ftell(fd);
-	resourceSize = currentFilePos - prevFilePos - sizeof(uint32);
-	fseek(fd, prevFilePos, SEEK_SET);
-	writeUint32LE(fd, resourceSize);
-	fseek(fd, currentFilePos, SEEK_SET);
+	uint currentFilePos = ftell(fd);
+	uint32 resourceSize = currentFilePos - prevFilePos;
+	return resourceSize;
 }
 
 int main(int argc, char *argv[]) {
@@ -416,9 +411,32 @@ int main(int argc, char *argv[]) {
 
 		fseek(fout, dataOffset, SEEK_SET);
 
+		ResourceInfo resourceInfos[NUM_RESOURCES];
+		uint32 resInfoPos = ftell(fout);
+		fseek(fout, (2 * sizeof(uint32) + sizeof(byte)) * NUM_RESOURCES, SEEK_CUR);
+
+		for (uint i = 0; i < NUM_RESOURCES; i++) {
+			resourceInfos[i]._id = i;
+			resourceInfos[i]._offset = ftell(fout);
+
+			uint32 size = writeResource(fout, ResourceType(i), supportedLanguages[lang]);
+			resourceInfos[i]._size = size;
+		}
+
+		fseek(fout, resInfoPos, SEEK_SET);
 		for (uint i = 0; i < NUM_RESOURCES; i++) {
-			writeResource(fout, ResourceType(i), supportedLanguages[lang]);
+			writeByte(fout, resourceInfos[i]._id);
+			if (resourceInfos[i]._id != 0) {
+				// Offsets are stored relative to first resource's offset
+				// NOTE: First resource is kResDialogs(1), not kResDialogStacks(0)
+				// because kResDialogStacks is not stored with the rest of resources.
+				writeUint32LE(fout, resourceInfos[i]._offset - resourceInfos[1]._offset);
+			} else
+				writeUint32LE(fout, resourceInfos[i]._offset);
+			writeUint32LE(fout, resourceInfos[i]._size);
 		}
+		// Go back to current file pos
+		fseek(fout, resourceInfos[NUM_RESOURCES - 1]._offset + resourceInfos[NUM_RESOURCES - 1]._size, SEEK_SET);
 	}
 
 	fclose(fout);
diff --git a/devtools/create_teenagent/create_teenagent.h b/devtools/create_teenagent/create_teenagent.h
index 97274201f50..3118a4af413 100644
--- a/devtools/create_teenagent/create_teenagent.h
+++ b/devtools/create_teenagent/create_teenagent.h
@@ -25,11 +25,11 @@
 #include "common/language.h"
 #include "util.h"
 
-#define TEENAGENT_DAT_VERSION 5
+#define TEENAGENT_DAT_VERSION 6
 
 enum ResourceType {
-	kResDialogs = 0,
-	kResDialogStacks,
+	kResDialogStacks = 0,
+	kResDialogs,
 	kResItems,
 	kResCredits,
 	kResSceneObjects,
@@ -37,6 +37,12 @@ enum ResourceType {
 	kResCombinations,
 };
 
+struct ResourceInfo {
+	byte _id;
+	uint32 _offset;
+	uint32 _size;
+};
+
 #define NUM_RESOURCES 7
 #define NUM_LANGS 4
 
diff --git a/dists/engine-data/teenagent.dat b/dists/engine-data/teenagent.dat
index 41be7c9a348..8926c93863e 100644
Binary files a/dists/engine-data/teenagent.dat and b/dists/engine-data/teenagent.dat differ
diff --git a/engines/teenagent/callbacks.cpp b/engines/teenagent/callbacks.cpp
index b2a104a461b..b7c5045a1f4 100644
--- a/engines/teenagent/callbacks.cpp
+++ b/engines/teenagent/callbacks.cpp
@@ -484,7 +484,7 @@ void TeenAgentEngine::fnGiveAnotherFlowerToAnne() {
 
 void TeenAgentEngine::bookColorMessage() {
 	uint bookIndex = GET_FLAG(dsAddr_drawerPuzzleBookValue) - 1;
-	uint16 addr = res->getMessageAddr(MessageType((int)kBookColorMsg0 + bookIndex));
+	uint32 addr = res->getMessageAddr(MessageType((int)kBookColorMsg0 + bookIndex));
 
 	displayMessage(addr);
 }
diff --git a/engines/teenagent/dialog.cpp b/engines/teenagent/dialog.cpp
index d6e53cf7e16..2e43d6b53c9 100644
--- a/engines/teenagent/dialog.cpp
+++ b/engines/teenagent/dialog.cpp
@@ -27,7 +27,7 @@
 namespace TeenAgent {
 
 void Dialog::show(uint16 dialogNum, Scene *scene, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2) {
-	uint16 addr = _vm->res->getDialogAddr(dialogNum);
+	uint32 addr = _vm->res->getDialogAddr(dialogNum);
 	// WORKAROUND: For Dialog 163, The usage of this in the engine overlaps the previous dialog i.e. the
 	// starting offset used is two bytes early, thus implicitly changing the first command of this dialog
 	// from NEW_LINE to CHANGE_CHARACTER.
@@ -39,7 +39,7 @@ void Dialog::show(uint16 dialogNum, Scene *scene, uint16 animation1, uint16 anim
 	show(scene, addr, animation1, animation2, character1ID, character2ID, slot1, slot2);
 }
 
-void Dialog::show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2) {
+void Dialog::show(Scene *scene, uint32 addr, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2) {
 	debugC(0, kDebugDialog, "Dialog::show(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2);
 	int n = 0;
 	Common::String message;
@@ -151,7 +151,11 @@ uint16 Dialog::pop(Scene *scene, uint16 addr, uint16 animation1, uint16 animatio
 	uint16 next2 = _vm->res->dseg.get_word(addr);
 	if (next2 != 0xffff)
 		_vm->res->dseg.set_word(addr - 2, 0);
-	show(scene, next, animation1, animation2, character1ID, character2ID, slot1, slot2);
+
+	// Dialog addresses popped from stack are relative
+	// to dialog start offset. So we add that offset first
+	uint32 dialogAddr = _vm->res->getDialogStartPos() + next;
+	show(scene, dialogAddr, animation1, animation2, character1ID, character2ID, slot1, slot2);
 	return next;
 }
 
diff --git a/engines/teenagent/dialog.h b/engines/teenagent/dialog.h
index eef6f542cd3..ebdbd448732 100644
--- a/engines/teenagent/dialog.h
+++ b/engines/teenagent/dialog.h
@@ -135,7 +135,7 @@ public:
 private:
 	TeenAgentEngine *_vm;
 
-	void show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2);
+	void show(Scene *scene, uint32 addr, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2);
 };
 
 } // End of namespace TeenAgent
diff --git a/engines/teenagent/inventory.cpp b/engines/teenagent/inventory.cpp
index 56fd486c9cf..bfab6558697 100644
--- a/engines/teenagent/inventory.cpp
+++ b/engines/teenagent/inventory.cpp
@@ -62,8 +62,8 @@ Inventory::Inventory(TeenAgentEngine *vm) : _vm(vm) {
 	_objects.push_back(ioBlank);
 	for (byte i = 0; i < kNumInventoryItems; ++i) {
 		InventoryObject io;
-		uint16 objAddr = vm->res->getItemAddr(i);
-		io.load(vm->res->itemsSeg.ptr(objAddr));
+		uint32 objAddr = vm->res->getItemAddr(i);
+		io.load(vm->res->eseg.ptr(objAddr));
 		_objects.push_back(io);
 	}
 
@@ -213,7 +213,7 @@ bool Inventory::processEvent(const Common::Event &event) {
 
 		debugC(0, kDebugInventory, "combine(%u, %u)!", id1, id2);
 		for (uint i = 0; i < kNumCombinations; i++) {
-			byte *table = _vm->res->combinationsSeg.ptr(_vm->res->getCombinationAddr(i));
+			byte *table = _vm->res->eseg.ptr(_vm->res->getCombinationAddr(i));
 
 			if ((id1 == table[0] && id2 == table[1]) || (id2 == table[0] && id1 == table[1])) {
 				byte newObj = table[2];
diff --git a/engines/teenagent/resources.cpp b/engines/teenagent/resources.cpp
index 7c24cef5de1..836093a38aa 100644
--- a/engines/teenagent/resources.cpp
+++ b/engines/teenagent/resources.cpp
@@ -31,6 +31,13 @@
 namespace TeenAgent {
 
 Resources::Resources() {
+	_combinationsStartOffset = 0;
+	_creditsStartOffset = 0;
+	_dialogsStartOffset = 0;
+	_messagesStartOffset = 0;
+	_sceneObjectsStartOffset = 0;
+	_sceneObjectsBlockSize = 0;
+	_itemsStartOffset = 0;
 }
 
 Resources::~Resources() {
@@ -64,12 +71,12 @@ quick note on varia resources:
 #define DSEG_SIZE 59280 // 0xe790
 #define ESEG_SIZE 35810 // 0x8be2
 
-void Resources::precomputeResourceOffsets(Segment &seg, Common::Array<uint16> &offsets, uint numTerminators) {
-	offsets.push_back(0);
+void Resources::precomputeResourceOffsets(const ResourceInfo &resInfo, Common::Array<uint32> &offsets, uint numTerminators) {
+	offsets.push_back(resInfo._offset);
 	uint n = 0;
 	uint8 current, last = 0xff;
-	for (uint i = 0; i < seg.size(); i++) {
-		current = seg.get_byte(i);
+	for (uint32 i = resInfo._offset; i < resInfo._offset + resInfo._size; i++) {
+		current = eseg.get_byte(i);
 
 		if (n == numTerminators) {
 			offsets.push_back(i);
@@ -86,36 +93,36 @@ void Resources::precomputeResourceOffsets(Segment &seg, Common::Array<uint16> &o
 	}
 }
 
-void Resources::precomputeDialogOffsets() {
-	precomputeResourceOffsets(eseg, dialogOffsets, 4);
+void Resources::precomputeDialogOffsets(const ResourceInfo &resInfo) {
+	precomputeResourceOffsets(resInfo, dialogOffsets, 4);
 
 	debug(1, "Resources::precomputeDialogOffsets() - Found %d dialogs", dialogOffsets.size());
 	for (uint i = 0; i < dialogOffsets.size(); i++)
 		debug(1, "\tDialog #%d: Offset 0x%04x", i, dialogOffsets[i]);
 }
 
-void Resources::precomputeCreditsOffsets() {
-	precomputeResourceOffsets(creditsSeg, creditsOffsets);
+void Resources::precomputeCreditsOffsets(const ResourceInfo &resInfo) {
+	precomputeResourceOffsets(resInfo, creditsOffsets);
 
 	debug(1, "Resources::precomputeCreditsOffsets() - Found %d credits", creditsOffsets.size());
 	for (uint i = 0; i < creditsOffsets.size(); i++)
 		debug(1, "\tCredit #%d: Offset 0x%04x", i, creditsOffsets[i]);
 }
 
-void Resources::precomputeItemOffsets() {
-	precomputeResourceOffsets(itemsSeg, itemOffsets);
+void Resources::precomputeItemOffsets(const ResourceInfo &resInfo) {
+	precomputeResourceOffsets(resInfo, itemOffsets);
 
 	debug(1, "Resources::precomputeItemOffsets() - Found %d items", itemOffsets.size());
 	for (uint i = 0; i < itemOffsets.size(); i++)
 		debug(1, "\tItem #%d: Offset 0x%04x", i, itemOffsets[i]);
 }
 
-void Resources::precomputeMessageOffsets() {
-	precomputeResourceOffsets(messagesSeg, messageOffsets);
+void Resources::precomputeMessageOffsets(const ResourceInfo &resInfo) {
+	precomputeResourceOffsets(resInfo, messageOffsets);
 }
 
-void Resources::precomputeCombinationOffsets() {
-	precomputeResourceOffsets(combinationsSeg, combinationOffsets);
+void Resources::precomputeCombinationOffsets(const ResourceInfo &resInfo) {
+	precomputeResourceOffsets(resInfo, combinationOffsets);
 
 	debug(1, "Resources::precomputeCombinationOffsets() - Found %d combination items", combinationOffsets.size());
 	for (uint i = 0; i < combinationOffsets.size(); i++)
@@ -137,6 +144,41 @@ void Resources::readDialogStacks(byte *src) {
 	}
 }
 
+void Resources::precomputeAllOffsets(const Common::Array<ResourceInfo> &resourceInfos) {
+	for (const auto &resInfo : resourceInfos) {
+		switch ((ResourceType)resInfo._id) {
+		case kResCombinations:
+			_combinationsStartOffset = resInfo._offset;
+			precomputeCombinationOffsets(resInfo);
+			break;
+		case kResCredits:
+			_creditsStartOffset = resInfo._offset;
+			precomputeCreditsOffsets(resInfo);
+			break;
+		case kResDialogs:
+			_dialogsStartOffset = resInfo._offset;
+			precomputeDialogOffsets(resInfo);
+			break;
+		case kResItems:
+			_itemsStartOffset = resInfo._offset;
+			precomputeItemOffsets(resInfo);
+			break;
+		case kResMessages:
+			_messagesStartOffset = resInfo._offset;
+			precomputeMessageOffsets(resInfo);
+			break;
+		case kResSceneObjects:
+			_sceneObjectsStartOffset = resInfo._offset;
+			_sceneObjectsBlockSize = resInfo._size;
+			break;
+		case kResDialogStacks:
+		// fall through
+		default:
+			break;
+		}
+	}
+}
+
 bool Resources::loadArchives(const ADGameDescription *gd) {
 	Common::File *dat_file = new Common::File();
 	Common::String filename = "teenagent.dat";
@@ -195,36 +237,30 @@ bool Resources::loadArchives(const ADGameDescription *gd) {
 		}
 	}
 
-	uint resourceSize = dat->readUint32LE();
-	eseg.read(dat, resourceSize);
+	Common::Array<ResourceInfo> resourceInfos(kNumResources);
+	uint32 allResourcesSize = 0;
 
-	// Dialog stack data
-	resourceSize = dat->readUint32LE();
-	dat->read(tempBuffer, resourceSize);
-	readDialogStacks((byte *)tempBuffer);
+	for (auto &resInfo : resourceInfos) {
+		resInfo._id = dat->readByte();
+		resInfo._offset = dat->readUint32LE();
+		resInfo._size = dat->readUint32LE();
 
-	resourceSize = dat->readUint32LE();
-	itemsSeg.read(dat, resourceSize);
-
-	resourceSize = dat->readUint32LE();
-	creditsSeg.read(dat, resourceSize);
-
-	resourceSize = dat->readUint32LE();
-	sceneObjectsSeg.read(dat, resourceSize);
+		// Don't count Dialog stack's size
+		// since it will be stored in dseg, not eseg
+		if ((ResourceType)resInfo._id != kResDialogStacks)
+			allResourcesSize += resInfo._size;
+	}
 
-	resourceSize = dat->readUint32LE();
-	messagesSeg.read(dat, resourceSize);
+	// Dialog stack data
+	dat->read(tempBuffer, resourceInfos[(uint)kResDialogStacks]._size);
+	readDialogStacks((byte *)tempBuffer);
 
-	resourceSize = dat->readUint32LE();
-	combinationsSeg.read(dat, resourceSize);
+	// Store rest of the resources to eseg
+	eseg.read(dat, allResourcesSize);
 
 	delete dat;
 
-	precomputeDialogOffsets();
-	precomputeItemOffsets();
-	precomputeCreditsOffsets();
-	precomputeMessageOffsets();
-	precomputeCombinationOffsets();
+	precomputeAllOffsets(resourceInfos);
 
 	FilePack varia;
 	varia.open("varia.res");
diff --git a/engines/teenagent/resources.h b/engines/teenagent/resources.h
index a096ab04f72..daeac2653d9 100644
--- a/engines/teenagent/resources.h
+++ b/engines/teenagent/resources.h
@@ -1500,6 +1500,25 @@ enum MessageType{
 	kObjCombineErrorMsg,
 };
 
+// Number of resources in teenagent.dat file
+const byte kNumResources = 7;
+
+enum ResourceType {
+	kResDialogStacks = 0,
+	kResDialogs,
+	kResItems,
+	kResCredits,
+	kResSceneObjects,
+	kResMessages,
+	kResCombinations,
+};
+
+struct ResourceInfo {
+	byte _id;
+	uint32 _offset;
+	uint32 _size;
+};
+
 class Resources {
 public:
 	Resources();
@@ -1526,33 +1545,47 @@ public:
 	Font font7, font8;
 
 	//const byte *getDialog(uint16 dialogNum) { return eseg.ptr(dialogOffsets[dialogNum]); }
-	uint16 getDialogAddr(uint16 dialogNum) { return dialogOffsets[dialogNum]; }
-	uint16 getCreditAddr(uint16 creditNum) { return creditsOffsets[creditNum]; }
-	uint16 getItemAddr(uint16 itemNum) { return itemOffsets[itemNum]; }
-	uint16 getMessageAddr(MessageType msgType) { return messageOffsets[msgType]; }
-	uint16 getCombinationAddr(uint16 msgNum) { return combinationOffsets[msgNum]; }
+	uint32 getDialogStartPos() { return _dialogsStartOffset; }
+	uint32 getDialogAddr(uint16 dialogNum) { return dialogOffsets[dialogNum]; }
+	uint32 getCreditAddr(uint16 creditNum) { return creditsOffsets[creditNum]; }
+	uint32 getItemAddr(uint16 itemNum) { return itemOffsets[itemNum]; }
+	uint32 getMessageAddr(MessageType msgType) { return messageOffsets[msgType]; }
+	uint32 getCombinationAddr(uint16 msgNum) { return combinationOffsets[msgNum]; }
 
-	// Artificial segments added to support multiple languages
+	uint16 sceneObjectsBlockSize() { return _sceneObjectsBlockSize; }
+	uint32 getSceneObjectsStartPos() { return _sceneObjectsStartOffset; }
+
+	// Artificial segment that contains various
+	// string items (messages, dialogs, item names, etc.)
+	// Used to support multiple languages
 	Segment eseg;
-	Segment creditsSeg, itemsSeg, sceneObjectsSeg;
-	Segment messagesSeg;
-	Segment combinationsSeg;
 
 private:
-	void precomputeResourceOffsets(Segment &seg, Common::Array<uint16> &offsets, uint numTerminators = 2);
+	void precomputeAllOffsets(const Common::Array<ResourceInfo> &resourceInfos);
+	void precomputeResourceOffsets(const ResourceInfo &resInfo, Common::Array<uint32> &offsets, uint numTerminators = 2);
 
-	void precomputeDialogOffsets();
-	void precomputeCreditsOffsets();
-	void precomputeItemOffsets();
-	void precomputeMessageOffsets();
-	void precomputeCombinationOffsets();
+	void precomputeDialogOffsets(const ResourceInfo &resInfo);
+	void precomputeCreditsOffsets(const ResourceInfo &resInfo);
+	void precomputeItemOffsets(const ResourceInfo &resInfo);
+	void precomputeMessageOffsets(const ResourceInfo &resInfo);
+	void precomputeCombinationOffsets(const ResourceInfo &resInfo);
 
 	void readDialogStacks(byte *src);
 
-	Common::Array<uint16> dialogOffsets;
-	Common::Array<uint16> creditsOffsets, itemOffsets;
-	Common::Array<uint16> messageOffsets;
-	Common::Array<uint16> combinationOffsets;
+	Common::Array<uint32> dialogOffsets;
+	Common::Array<uint32> creditsOffsets, itemOffsets;
+	Common::Array<uint32> messageOffsets;
+	Common::Array<uint32> combinationOffsets;
+
+	uint32 _messagesStartOffset;
+
+	uint32 _sceneObjectsStartOffset;
+	uint32 _sceneObjectsBlockSize; // Needed to know how much to write to savefile
+
+	uint32 _combinationsStartOffset;
+	uint32 _itemsStartOffset;
+	uint32 _creditsStartOffset;
+	uint32 _dialogsStartOffset;
 };
 
 } // End of namespace TeenAgent
diff --git a/engines/teenagent/scene.cpp b/engines/teenagent/scene.cpp
index 171655214c3..b2a55bb57ba 100644
--- a/engines/teenagent/scene.cpp
+++ b/engines/teenagent/scene.cpp
@@ -247,11 +247,12 @@ void Scene::loadObjectData() {
 		Common::Array<Object> &sceneObjects = objects[i];
 		sceneObjects.clear();
 
-		uint16 sceneTable = _vm->res->sceneObjectsSeg.get_word(i * 2);
-		uint16 objectAddr;
-		while ((objectAddr = _vm->res->sceneObjectsSeg.get_word(sceneTable)) != 0) {
+		uint32 sceneObjectStartAddr = _vm->res->getSceneObjectsStartPos();
+		uint32 sceneTable = _vm->res->eseg.get_word(sceneObjectStartAddr + i * 2);
+		uint32 objectAddr;
+		while ((objectAddr = _vm->res->eseg.get_word(sceneObjectStartAddr + sceneTable)) != 0) {
 			Object obj;
-			obj.load(_vm->res->sceneObjectsSeg.ptr(objectAddr), i + 1);
+			obj.load(_vm->res->eseg.ptr(sceneObjectStartAddr + objectAddr), i + 1);
 			//obj.dump();
 			sceneObjects.push_back(obj);
 			sceneTable += 2;
diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp
index bbd062fd0cb..2831064a47f 100644
--- a/engines/teenagent/teenagent.cpp
+++ b/engines/teenagent/teenagent.cpp
@@ -242,20 +242,21 @@ Common::Error TeenAgentEngine::loadGameState(int slot) {
 	uint32 tag = in->readUint32BE();
 	if (tag == MKTAG('T', 'H', 'M', 'B')) { // Old save (before TEENAGENT_SAVEGAME_VERSION was added)
 		uint16 baseAddr = dsAddr_sceneObjectTablePtr;
+		uint32 sceneObjectStartAddr = res->getSceneObjectsStartPos();
 		// Copy scene object data in the dseg to sceneObjectsSeg
-		Common::copy(res->dseg.ptr(baseAddr), res->dseg.ptr(0xb4f3), res->sceneObjectsSeg.ptr(0));
+		Common::copy(res->dseg.ptr(baseAddr), res->dseg.ptr(0xb4f3), res->eseg.ptr(sceneObjectStartAddr));
 
 		// Set correct addresses, i.e., make them relative to dsAddr_sceneObjectTablePtr
 		for (byte i = 0; i < 42; i++) {
 			uint16 sceneTable = res->dseg.get_word(baseAddr + (i * 2));
-			res->sceneObjectsSeg.set_word(i * 2, sceneTable - baseAddr);
+			res->eseg.set_word(sceneObjectStartAddr + i * 2, sceneTable - baseAddr);
 
 			uint16 objectAddr;
 			while ((objectAddr = res->dseg.get_word(sceneTable)) != 0) {
-				res->sceneObjectsSeg.set_word(sceneTable - baseAddr, objectAddr - baseAddr);
+				res->eseg.set_word(sceneObjectStartAddr + sceneTable - baseAddr, objectAddr - baseAddr);
 				sceneTable += 2;
 			}
-			res->sceneObjectsSeg.set_word(sceneTable - baseAddr, 0);
+			res->eseg.set_word(sceneObjectStartAddr + sceneTable - baseAddr, 0);
 		}
 	} else {
 		if (tag != MKTAG('T', 'N', 'G', 'T')) {
@@ -270,7 +271,7 @@ Common::Error TeenAgentEngine::loadGameState(int slot) {
 		}
 
 		uint32 resourceSize = in->readUint32LE();
-		if (in->read(res->sceneObjectsSeg.ptr(0), resourceSize) != resourceSize) {
+		if (in->read(res->eseg.ptr(res->getSceneObjectsStartPos()), resourceSize) != resourceSize) {
 			warning("loadGameState(): corrupted data");
 			return Common::kReadingFailed;
 		}
@@ -318,8 +319,8 @@ Common::Error TeenAgentEngine::saveGameState(int slot, const Common::String &des
 	out->writeByte(TEENAGENT_SAVEGAME_VERSION);
 
 	// Write scene object data
-	out->writeUint32LE(res->sceneObjectsSeg.size());
-	out->write(res->sceneObjectsSeg.ptr(0), res->sceneObjectsSeg.size());
+	out->writeUint32LE(res->sceneObjectsBlockSize());
+	out->write(res->eseg.ptr(res->getSceneObjectsStartPos()), res->sceneObjectsBlockSize());
 
 	if (!Graphics::saveThumbnail(*out))
 		warning("saveThumbnail failed");
@@ -767,10 +768,10 @@ Common::Error TeenAgentEngine::run() {
 	return Common::kNoError;
 }
 
-Common::String TeenAgentEngine::parseMessage(uint16 addr) {
+Common::String TeenAgentEngine::parseMessage(uint32 addr) {
 	Common::String message;
 	for (
-	    const char *str = (const char *)res->messagesSeg.ptr(addr);
+	    const char *str = (const char *)res->eseg.ptr(addr);
 	    str[0] != 0 || str[1] != 0;
 	    ++str) {
 		char c = str[0];
@@ -813,11 +814,11 @@ void TeenAgentEngine::displayMessage(const Common::String &str, CharacterID char
 	}
 }
 
-void TeenAgentEngine::displayMessage(uint16 addr, CharacterID characterID, uint16 x, uint16 y) {
+void TeenAgentEngine::displayMessage(uint32 addr, CharacterID characterID, uint16 x, uint16 y) {
 	displayMessage(parseMessage(addr), characterID, x, y);
 }
 
-void TeenAgentEngine::displayAsyncMessage(uint16 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, CharacterID characterID) {
+void TeenAgentEngine::displayAsyncMessage(uint32 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, CharacterID characterID) {
 	SceneEvent event(SceneEvent::kMessage);
 	event.message = parseMessage(addr);
 	event.slot = 0;
@@ -831,7 +832,7 @@ void TeenAgentEngine::displayAsyncMessage(uint16 addr, uint16 x, uint16 y, uint1
 	scene->push(event);
 }
 
-void TeenAgentEngine::displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color) {
+void TeenAgentEngine::displayAsyncMessageInSlot(uint32 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color) {
 	SceneEvent event(SceneEvent::kMessage);
 	event.message = parseMessage(addr);
 	event.slot = slot + 1;
@@ -842,10 +843,10 @@ void TeenAgentEngine::displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 f
 	scene->push(event);
 }
 
-void TeenAgentEngine::displayCredits(uint16 addr, uint16 timer) {
+void TeenAgentEngine::displayCredits(uint32 addr, uint16 timer) {
 	SceneEvent event(SceneEvent::kCreditsMessage);
 
-	const byte *src = res->creditsSeg.ptr(addr);
+	const byte *src = res->eseg.ptr(addr);
 	event.orientation = *src++;
 	event.color = *src++;
 	event.lan = 8;
@@ -883,7 +884,7 @@ void TeenAgentEngine::displayCredits() {
 	scene->push(event);
 }
 
-void TeenAgentEngine::displayCutsceneMessage(uint16 addr, uint16 x, uint16 y) {
+void TeenAgentEngine::displayCutsceneMessage(uint32 addr, uint16 x, uint16 y) {
 	SceneEvent event(SceneEvent::kCreditsMessage);
 
 	event.message = parseMessage(addr);
diff --git a/engines/teenagent/teenagent.h b/engines/teenagent/teenagent.h
index 03adbcfb4b2..18d45d14ca8 100644
--- a/engines/teenagent/teenagent.h
+++ b/engines/teenagent/teenagent.h
@@ -62,7 +62,7 @@ class Resources;
 class Inventory;
 class Pack;
 
-#define TEENAGENT_DAT_VERSION 5
+#define TEENAGENT_DAT_VERSION 6
 #define TEENAGENT_SAVEGAME_VERSION 1
 
 // Engine Debug Flags
@@ -120,15 +120,15 @@ public:
 	bool showMetropolis();
 	int skipEvents() const;
 
-	Common::String parseMessage(uint16 addr);
+	Common::String parseMessage(uint32 addr);
 
 	//event driven:
-	void displayMessage(uint16 addr, CharacterID characterID = kMark, uint16 x = 0, uint16 y = 0);
+	void displayMessage(uint32 addr, CharacterID characterID = kMark, uint16 x = 0, uint16 y = 0);
 	void displayMessage(const Common::String &str, CharacterID characterID = kMark, uint16 x = 0, uint16 y = 0);
-	void displayAsyncMessage(uint16 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, CharacterID characterID = kMark);
-	void displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color = textColorMark);
-	void displayCredits(uint16 addr, uint16 timer = 0);
-	void displayCutsceneMessage(uint16 addr, uint16 x, uint16 y);
+	void displayAsyncMessage(uint32 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, CharacterID characterID = kMark);
+	void displayAsyncMessageInSlot(uint32 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color = textColorMark);
+	void displayCredits(uint32 addr, uint16 timer = 0);
+	void displayCutsceneMessage(uint32 addr, uint16 x, uint16 y);
 	void moveTo(const Common::Point &dst, byte o, bool warp = false);
 	void moveTo(uint16 x, uint16 y, byte o, bool warp = false);
 	void moveTo(Object *obj);


Commit: 4768231b0e8c95443175386b1fcf2ce4be1482a6
    https://github.com/scummvm/scummvm/commit/4768231b0e8c95443175386b1fcf2ce4be1482a6
Author: Alikhan Balpykov (luxrage1990 at gmail.com)
Date: 2025-08-09T21:03:44+02:00

Commit Message:
TEENAGENT: Add support for Polish voice acting

Changed paths:
    engines/teenagent/dialog.cpp
    engines/teenagent/inventory.cpp
    engines/teenagent/objects.cpp
    engines/teenagent/objects.h
    engines/teenagent/resources.cpp
    engines/teenagent/resources.h
    engines/teenagent/scene.cpp
    engines/teenagent/scene.h
    engines/teenagent/teenagent.cpp
    engines/teenagent/teenagent.h


diff --git a/engines/teenagent/dialog.cpp b/engines/teenagent/dialog.cpp
index 2e43d6b53c9..00e122cbe1b 100644
--- a/engines/teenagent/dialog.cpp
+++ b/engines/teenagent/dialog.cpp
@@ -42,6 +42,7 @@ void Dialog::show(uint16 dialogNum, Scene *scene, uint16 animation1, uint16 anim
 void Dialog::show(Scene *scene, uint32 addr, uint16 animation1, uint16 animation2, CharacterID character1ID, CharacterID character2ID, byte slot1, byte slot2) {
 	debugC(0, kDebugDialog, "Dialog::show(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2);
 	int n = 0;
+	uint16 voiceId = 0;
 	Common::String message;
 	byte color = characterDialogData[character1ID].textColor;
 	byte color1 = color;
@@ -61,6 +62,10 @@ void Dialog::show(Scene *scene, uint32 addr, uint16 animation1, uint16 animation
 		scene->push(e2);
 	}
 
+	// Number of ANIM_WAIT (0xff) bytes.
+	// Used to correctly find voice index.
+	uint numOfAnimWaits = 0;
+
 	while (n < 4) {
 		byte c = _vm->res->eseg.get_byte(addr++);
 		debugC(1, kDebugDialog, "%02x: %c", c, c > 0x20? c: '.');
@@ -99,6 +104,7 @@ void Dialog::show(Scene *scene, uint32 addr, uint16 animation1, uint16 animation
 				}
 
 				message.trim();
+				voiceId = _vm->res->getVoiceIndex(addr - message.size() - numOfAnimWaits - 2); // -2 for '\n'
 				if (!message.empty()) {
 					SceneEvent em(SceneEvent::kMessage);
 					em.message = message;
@@ -111,8 +117,10 @@ void Dialog::show(Scene *scene, uint32 addr, uint16 animation1, uint16 animation
 						em.slot = slot2;
 						em.characterID = character2ID;
 					}
+					em.voiceId = voiceId;
 					scene->push(em);
 					message.clear();
+					numOfAnimWaits = 0;
 				}
 				break;
 
@@ -127,6 +135,7 @@ void Dialog::show(Scene *scene, uint32 addr, uint16 animation1, uint16 animation
 			break;
 
 		case 0xff:
+			numOfAnimWaits++;
 			//FIXME : wait for the next cycle of the animation
 			break;
 
diff --git a/engines/teenagent/inventory.cpp b/engines/teenagent/inventory.cpp
index bfab6558697..69ff688cb5a 100644
--- a/engines/teenagent/inventory.cpp
+++ b/engines/teenagent/inventory.cpp
@@ -202,7 +202,8 @@ bool Inventory::processEvent(const Common::Event &event) {
 				return true;
 			//activate(false);
 			int w = _vm->res->font7.render(NULL, 0, 0, _hoveredObj->description, textColorMark);
-			_vm->scene->displayMessage(_hoveredObj->description, textColorMark, Common::Point((kScreenWidth - w) / 2, 162));
+			uint16 voiceIndex = _vm->res->getVoiceIndex(_vm->res->getItemAddr(_hoveredObj->id - 1));
+			_vm->scene->displayMessage(_hoveredObj->description, voiceIndex, textColorMark, Common::Point((kScreenWidth - w) / 2, 162));
 			return true;
 		}
 
@@ -213,7 +214,8 @@ bool Inventory::processEvent(const Common::Event &event) {
 
 		debugC(0, kDebugInventory, "combine(%u, %u)!", id1, id2);
 		for (uint i = 0; i < kNumCombinations; i++) {
-			byte *table = _vm->res->eseg.ptr(_vm->res->getCombinationAddr(i));
+			uint32 addr = _vm->res->getCombinationAddr(i);
+			byte *table = _vm->res->eseg.ptr(addr);
 
 			if ((id1 == table[0] && id2 == table[1]) || (id2 == table[0] && id1 == table[1])) {
 				byte newObj = table[2];
@@ -225,7 +227,7 @@ bool Inventory::processEvent(const Common::Event &event) {
 					_vm->playSoundNow(&_vm->res->sam_sam, 69);
 				}
 				Common::String msg = Object::parseDescription((const char *)(table + 3));
-				_vm->displayMessage(msg);
+				_vm->displayMessage(msg, _vm->res->getVoiceIndex(addr));
 				activate(false);
 				resetSelectedObject();
 				return true;
diff --git a/engines/teenagent/objects.cpp b/engines/teenagent/objects.cpp
index e8120bec7e6..45d61b90f8d 100644
--- a/engines/teenagent/objects.cpp
+++ b/engines/teenagent/objects.cpp
@@ -55,6 +55,7 @@ void Rect::render(Graphics::Surface *surface, uint8 color) const {
 
 void Object::load(byte *src, byte sceneId) {
 	_base = src;
+	_addr = src - g_engine->res->eseg.ptr(0);
 
 	id = *src++;
 
@@ -79,6 +80,9 @@ void Object::load(byte *src, byte sceneId) {
 			src++;
 	}
 
+	if (*src == 1)
+		_hasDefaultDescription = true;
+
 	description = parseDescription((const char *)src);
 
 	if (hasRealName) {
diff --git a/engines/teenagent/objects.h b/engines/teenagent/objects.h
index 957a1b0dfc8..941ef6562d8 100644
--- a/engines/teenagent/objects.h
+++ b/engines/teenagent/objects.h
@@ -164,12 +164,15 @@ struct Object {
 	//19
 	Common::String name, description;
 
-	Object(): _base(NULL), _nameSize(0) { id = 0; actorOrientation = 0; enabled = 0;  }
+	Object(): _base(NULL), _nameSize(0) { id = 0; actorOrientation = 0; enabled = 0; _hasDefaultDescription = false;  }
 	void dump(int level = 0) const;
 	void setRealName();
 	void load(byte *addr, byte sceneId = 0);
 	void save() const;
 
+	bool hasDefaultDescription() { return _hasDefaultDescription; }
+	uint32 getAddr() { return _addr; };
+
 	static Common::String parseDescription(const char *desc);
 
 protected:
@@ -178,6 +181,9 @@ protected:
 
 	// New name that will be set when certain event is triggered
 	Common::String _realName;
+
+	bool _hasDefaultDescription;
+	uint32 _addr = 0; // Address inside eseg
 };
 
 struct InventoryObject {
diff --git a/engines/teenagent/resources.cpp b/engines/teenagent/resources.cpp
index 836093a38aa..63f8c12253a 100644
--- a/engines/teenagent/resources.cpp
+++ b/engines/teenagent/resources.cpp
@@ -179,6 +179,138 @@ void Resources::precomputeAllOffsets(const Common::Array<ResourceInfo> &resource
 	}
 }
 
+bool Resources::isVoiceIndexEmpty(uint16 index) {
+	uint size = voices.getSize(index);
+	if (size == 4 || size == 5)
+		return true;
+	return false;
+}
+
+void Resources::precomputeVoiceIndices(const Common::Array<ResourceInfo>& resourceInfos) {
+	byte numTerminators = 0;
+	uint16 voiceIndex = 0;
+
+	for (auto &resInfo : resourceInfos) {
+		switch ((ResourceType)resInfo._id) {
+		case kResMessages:
+			voiceIndex = 1;
+			numTerminators = 2;
+			break;
+		case kResCombinations:
+			voiceIndex = 567;
+			numTerminators = 2;
+			break;
+		case kResItems:
+			voiceIndex = 592;
+			numTerminators = 2;
+			break;
+		case kResDialogs:
+			voiceIndex = 902;
+			numTerminators = 4;
+			break;
+		case kResCredits:
+		case kResDialogStacks:
+		case kResSceneObjects:
+			// There are no voiceovers for credits and dialog stacks.
+			// For scene objects, voice indices calculated separately
+			// in Scene::loadObjectData()
+			continue;
+		default:
+			break;
+		}
+
+		_addrToVoiceIndx[resInfo._offset] = voiceIndex++;
+
+		uint16 currentNum = 1;
+		uint n = 0; // number of consecutive zero bytes
+		byte current, last = 0xff;
+
+		bool setNoIMessage = false;
+
+		for (uint32 i = resInfo._offset; i < resInfo._offset + resInfo._size; i++) {
+			current = eseg.get_byte(i);
+
+			if (n == numTerminators) {
+				currentNum++;
+				n = 0;
+
+				if ((ResourceType)resInfo._id == kResCombinations) {
+					uint16 nthCombination = currentNum - 1;
+					// For dublicate combination messages don't increment voice index
+					if (nthCombination == 3 || nthCombination == 5 ||
+						nthCombination == 15 || nthCombination == 16 || nthCombination == 17 ||
+						nthCombination == 18 || nthCombination == 22 || nthCombination == 26) {
+						_addrToVoiceIndx[i] = voiceIndex - 1;
+					} else if (nthCombination == 28) {
+						_addrToVoiceIndx[i] = voiceIndex - 2;
+					} else {
+						_addrToVoiceIndx[i] = voiceIndex++;
+					}
+				} else if ((ResourceType)resInfo._id == kResDialogs) {
+					if (voiceIndex == 1416) {
+						// "Dzie= dobry, panie robocie." starts at 1418
+						voiceIndex += 2;
+						_addrToVoiceIndx[i] = voiceIndex++;
+					} else if (voiceIndex == 1864) {
+						// "Jak ju< powiedzia%em, nasza organizacja" starts at 1867
+						voiceIndex += 3;
+						_addrToVoiceIndx[i] = voiceIndex++;
+					} else if (isVoiceIndexEmpty(voiceIndex)) {
+						voiceIndex += 1;
+						if (current != 0x00)
+							_addrToVoiceIndx[i] = voiceIndex++;
+					} else if (voiceIndex == 1801) {
+						_addrToVoiceIndx[i] = 2041; // "]adna pogoda."
+					} else if (voiceIndex == 1809) {
+						_addrToVoiceIndx[i] = 2042; // "Sir, mamy sygna%y, <e..."
+					} else {
+						if (current != 0x00)
+							_addrToVoiceIndx[i] = voiceIndex++;
+					}
+				} else if ((ResourceType)resInfo._id == kResMessages) {
+					if (currentNum == 334) { // Combination error message
+						// HACK: Use most good sounding (sigh) version
+						// TODO: Find the correct voice index used in the original
+						_addrToVoiceIndx[i] = 1304;
+					} else
+						_addrToVoiceIndx[i] = voiceIndex++;
+				} else {
+					_addrToVoiceIndx[i] = voiceIndex++;
+				}
+			}
+
+			if (current != 0x00 && last == 0x00) {
+				if ((ResourceType)resInfo._id == kResDialogs) {
+					if (n == 2 || n == 3) {
+						// "...to czemu nie u<y^ dziwnych" at 1886
+						// "Sze$^ miesi#cy temu z%oto i got*wka" at 1921
+						if (voiceIndex == 1885 || voiceIndex == 1920 || isVoiceIndexEmpty(voiceIndex)) {
+							voiceIndex += 1;
+							_addrToVoiceIndx[i] = voiceIndex++;
+						} else if (voiceIndex == 1923 && !setNoIMessage) {
+							_addrToVoiceIndx[i] = 1885; // "No i?..."
+							setNoIMessage = true;
+						} else {
+							_addrToVoiceIndx[i] = voiceIndex++;
+						}
+					} else if (n == 1 && (voiceIndex == 1720 || voiceIndex == 1852)) {
+						// Because of the rare case with
+						// NEW_LINE at the beginning of dialogs 163, 190
+						// we have to assign voiceIndex here
+						_addrToVoiceIndx[i] = voiceIndex++;
+					}
+				}
+				n = 0;
+			}
+
+			if (current == 0x00)
+				n++;
+
+			last = current;
+		}
+	}
+}
+
 bool Resources::loadArchives(const ADGameDescription *gd) {
 	Common::File *dat_file = new Common::File();
 	Common::String filename = "teenagent.dat";
@@ -278,6 +410,9 @@ bool Resources::loadArchives(const ADGameDescription *gd) {
 	sam_sam.open("sam_sam.res");
 	voices.open("voices.res");
 
+	if (gd->language == Common::PL_POL)
+		precomputeVoiceIndices(resourceInfos);
+
 	return true;
 }
 
diff --git a/engines/teenagent/resources.h b/engines/teenagent/resources.h
index daeac2653d9..621a6abbc59 100644
--- a/engines/teenagent/resources.h
+++ b/engines/teenagent/resources.h
@@ -1555,6 +1555,13 @@ public:
 	uint16 sceneObjectsBlockSize() { return _sceneObjectsBlockSize; }
 	uint32 getSceneObjectsStartPos() { return _sceneObjectsStartOffset; }
 
+	uint16 getVoiceIndex(uint32 addr) {
+		if (_addrToVoiceIndx.contains(addr))
+			return _addrToVoiceIndx[addr];
+		return 0;
+	}
+	void setVoiceIndex(uint32 addr, uint16 index) { _addrToVoiceIndx[addr] = index; }
+
 	// Artificial segment that contains various
 	// string items (messages, dialogs, item names, etc.)
 	// Used to support multiple languages
@@ -1570,6 +1577,9 @@ private:
 	void precomputeMessageOffsets(const ResourceInfo &resInfo);
 	void precomputeCombinationOffsets(const ResourceInfo &resInfo);
 
+	void precomputeVoiceIndices(const Common::Array<ResourceInfo> &resourceInfos);
+	bool isVoiceIndexEmpty(uint16 index);
+
 	void readDialogStacks(byte *src);
 
 	Common::Array<uint32> dialogOffsets;
@@ -1586,6 +1596,8 @@ private:
 	uint32 _itemsStartOffset;
 	uint32 _creditsStartOffset;
 	uint32 _dialogsStartOffset;
+
+	Common::HashMap<uint32, uint16> _addrToVoiceIndx;
 };
 
 } // End of namespace TeenAgent
diff --git a/engines/teenagent/scene.cpp b/engines/teenagent/scene.cpp
index b2a55bb57ba..5c87a81df75 100644
--- a/engines/teenagent/scene.cpp
+++ b/engines/teenagent/scene.cpp
@@ -73,6 +73,7 @@ Scene::Scene(TeenAgentEngine *vm) : _vm(vm), intro(false), _id(0), ons(0),
 
 	_onsCount = 0;
 	_messageColor = 0;
+	_voiceId = 0;
 }
 
 Scene::~Scene() {
@@ -243,6 +244,8 @@ void Scene::loadObjectData() {
 	walkboxes.resize(42);
 	fades.resize(42);
 
+	uint16 voiceStartIndx = 334;
+
 	for (byte i = 0; i < 42; ++i) {
 		Common::Array<Object> &sceneObjects = objects[i];
 		sceneObjects.clear();
@@ -254,6 +257,14 @@ void Scene::loadObjectData() {
 			Object obj;
 			obj.load(_vm->res->eseg.ptr(sceneObjectStartAddr + objectAddr), i + 1);
 			//obj.dump();
+			if (obj.hasDefaultDescription()) {
+				uint32 coolMsgAddr = _vm->res->getMessageAddr(kCoolMsg);
+				_vm->res->setVoiceIndex(sceneObjectStartAddr + objectAddr, _vm->res->getVoiceIndex(coolMsgAddr));
+			} else {
+				_vm->res->setVoiceIndex(sceneObjectStartAddr + objectAddr, voiceStartIndx);
+				voiceStartIndx++;
+			}
+
 			sceneObjects.push_back(obj);
 			sceneTable += 2;
 		}
@@ -497,6 +508,7 @@ bool Scene::processEvent(const Common::Event &event) {
 				_vm->playMusic(4);
 				_vm->loadScene(10, Common::Point(136, 153));
 				_vm->stopTextToSpeech();
+				_vm->stopVoice();
 				_vm->setTTSVoice(kMark);
 				return true;
 			}
@@ -504,6 +516,7 @@ bool Scene::processEvent(const Common::Event &event) {
 		case kActionSkipDialog:
 			if (!message.empty() && messageFirstFrame == 0) {
 				_vm->stopTextToSpeech();
+				_vm->stopVoice();
 				clearMessage();
 				nextEvent();
 				return true;
@@ -848,6 +861,7 @@ bool Scene::render(bool tickGame, bool tickMark, uint32 messageDelta) {
 					}
 				}
 				_vm->sayText(ttsMessage);
+				_vm->playVoiceNow(&_vm->res->voices, _voiceId);
 			}
 		}
 
@@ -993,6 +1007,7 @@ bool Scene::processEventQueue() {
 		case SceneEvent::kMessage: {
 			_vm->setTTSVoice((CharacterID)currentEvent.characterID);
 			message = currentEvent.message;
+			_voiceId = currentEvent.voiceId;
 			messageAnimation = NULL;
 			if (currentEvent.firstFrame) {
 				messageTimer = 0;
@@ -1253,13 +1268,14 @@ uint Scene::messageDuration(const Common::String &str) {
 	return delay * 10;
 }
 
-void Scene::displayMessage(const Common::String &str, byte color, const Common::Point &pos) {
+void Scene::displayMessage(const Common::String &str, uint16 voiceIndex, byte color, const Common::Point &pos) {
 	//assert(!str.empty());
 	debugC(0, kDebugScene, "displayMessage: %s", str.c_str());
 	message = str;
 	messagePos = (pos.x | pos.y) ? pos : messagePosition(str, position);
 	_messageColor = color;
 	messageTimer = messageDuration(message);
+	_voiceId = voiceIndex;
 }
 
 void Scene::clear() {
@@ -1277,6 +1293,7 @@ void Scene::clear() {
 void Scene::clearMessage() {
 	message.clear();
 	messageTimer = 0;
+	_voiceId = 0;
 
 	// Reset TTS voice to Mark's voice so that objects and items are always narrated
 	// with his voice
diff --git a/engines/teenagent/scene.h b/engines/teenagent/scene.h
index 7eaf415beb8..7b4db526858 100644
--- a/engines/teenagent/scene.h
+++ b/engines/teenagent/scene.h
@@ -69,6 +69,7 @@ struct SceneEvent {
 	} type;
 
 	Common::String message;
+	uint16 voiceId;
 	byte color;
 	byte slot;
 	union {
@@ -99,6 +100,7 @@ struct SceneEvent {
 	void clear() {
 		type = kNone;
 		message.clear();
+		voiceId = 0;
 		color = textColorMark;
 		slot = 0;
 		orientation = 0;
@@ -140,7 +142,7 @@ public:
 	void moveTo(const Common::Point &point, byte orientation = 0, bool validate = false);
 	Common::Point getPosition() const { return position; }
 
-	void displayMessage(const Common::String &str, byte color = textColorMark, const Common::Point &pos = Common::Point());
+	void displayMessage(const Common::String &str, uint16 voiceIndex, byte color = textColorMark, const Common::Point &pos = Common::Point());
 	void setOrientation(uint8 o) { orientation = o; }
 	void push(const SceneEvent &event);
 	byte peekFlagEvent(uint16 addr) const;
@@ -220,6 +222,8 @@ private:
 	byte messageLastFrame;
 	Animation *messageAnimation;
 
+	uint16 _voiceId;
+
 	typedef Common::List<SceneEvent> EventList;
 	EventList events;
 	SceneEvent currentEvent;
diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp
index 2831064a47f..3934bc35637 100644
--- a/engines/teenagent/teenagent.cpp
+++ b/engines/teenagent/teenagent.cpp
@@ -138,7 +138,7 @@ void TeenAgentEngine::processObject() {
 		dcall += 2 * _dstObject->id - 2;
 		uint16 callback = READ_LE_UINT16(dcall);
 		if (callback == 0 || !processCallback(callback))
-			displayMessage(_dstObject->description);
+			displayMessage(_dstObject->description, res->getVoiceIndex(_dstObject->getAddr()));
 	}
 	break;
 	case kActionUse: {
@@ -150,7 +150,7 @@ void TeenAgentEngine::processObject() {
 		dcall += 2 * _dstObject->id - 2;
 		uint16 callback = READ_LE_UINT16(dcall);
 		if (!processCallback(callback))
-			displayMessage(_dstObject->description);
+			displayMessage(_dstObject->description, 0);
 	}
 	break;
 
@@ -783,7 +783,7 @@ Common::String TeenAgentEngine::parseMessage(uint32 addr) {
 	return message;
 }
 
-void TeenAgentEngine::displayMessage(const Common::String &str, CharacterID characterID, uint16 x, uint16 y) {
+void TeenAgentEngine::displayMessage(const Common::String &str, uint16 voiceIndex, CharacterID characterID, uint16 x, uint16 y) {
 	if (str.empty()) {
 		return;
 	}
@@ -803,6 +803,7 @@ void TeenAgentEngine::displayMessage(const Common::String &str, CharacterID char
 		event.dst.x = x;
 		event.dst.y = y;
 		event.characterID = characterID;
+		event.voiceId = voiceIndex;
 		scene->push(event);
 	}
 
@@ -815,12 +816,13 @@ void TeenAgentEngine::displayMessage(const Common::String &str, CharacterID char
 }
 
 void TeenAgentEngine::displayMessage(uint32 addr, CharacterID characterID, uint16 x, uint16 y) {
-	displayMessage(parseMessage(addr), characterID, x, y);
+	displayMessage(parseMessage(addr), res->getVoiceIndex(addr), characterID, x, y);
 }
 
 void TeenAgentEngine::displayAsyncMessage(uint32 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, CharacterID characterID) {
 	SceneEvent event(SceneEvent::kMessage);
 	event.message = parseMessage(addr);
+	event.voiceId = res->getVoiceIndex(addr);
 	event.slot = 0;
 	event.color = characterDialogData[characterID].textColor;
 	event.dst.x = x;
@@ -835,6 +837,7 @@ void TeenAgentEngine::displayAsyncMessage(uint32 addr, uint16 x, uint16 y, uint1
 void TeenAgentEngine::displayAsyncMessageInSlot(uint32 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color) {
 	SceneEvent event(SceneEvent::kMessage);
 	event.message = parseMessage(addr);
+	event.voiceId = res->getVoiceIndex(addr);
 	event.slot = slot + 1;
 	event.color = color;
 	event.firstFrame = firstFrame;
@@ -892,6 +895,7 @@ void TeenAgentEngine::displayCutsceneMessage(uint32 addr, uint16 x, uint16 y) {
 	event.dst.y = y;
 	event.lan = 7;
 	event.characterID = kMark;
+	event.voiceId = res->getVoiceIndex(addr);
 
 	scene->push(event);
 }
@@ -1106,6 +1110,29 @@ void TeenAgentEngine::playSoundNow(Pack *pack, uint32 id) {
 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, stream); // dispose is YES by default
 }
 
+void TeenAgentEngine::playVoiceNow(Pack *pack, uint32 id) {
+	uint size = pack->getSize(id);
+	if (size == 0) {
+		warning("skipping invalid sound %u", id);
+		return;
+	}
+
+	if (!_mixer->isSoundHandleActive(_voiceHandle) && id != _previousVoiceId) {
+		byte *data = (byte *)malloc(size);
+		pack->read(id, data, size);
+		debug(3, "playing %u samples...", size);
+
+		Audio::AudioStream *stream = Audio::makeRawStream(data, size, 11025, 0);
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_voiceHandle, stream);
+		_previousVoiceId = id;
+	}
+}
+
+void TeenAgentEngine::stopVoice() {
+	_mixer->stopHandle(_voiceHandle);
+	_previousVoiceId = 0;
+}
+
 void TeenAgentEngine::setMusic(byte id) {
 	debugC(0, kDebugMusic, "starting music %u", id);
 
diff --git a/engines/teenagent/teenagent.h b/engines/teenagent/teenagent.h
index 18d45d14ca8..07394d7be7e 100644
--- a/engines/teenagent/teenagent.h
+++ b/engines/teenagent/teenagent.h
@@ -124,7 +124,7 @@ public:
 
 	//event driven:
 	void displayMessage(uint32 addr, CharacterID characterID = kMark, uint16 x = 0, uint16 y = 0);
-	void displayMessage(const Common::String &str, CharacterID characterID = kMark, uint16 x = 0, uint16 y = 0);
+	void displayMessage(const Common::String &str, uint16 voiceIndex, CharacterID characterID = kMark, uint16 x = 0, uint16 y = 0);
 	void displayAsyncMessage(uint32 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, CharacterID characterID = kMark);
 	void displayAsyncMessageInSlot(uint32 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color = textColorMark);
 	void displayCredits(uint32 addr, uint16 timer = 0);
@@ -149,6 +149,8 @@ public:
 	void playMusic(byte id); //schedules play
 	void playSound(byte id, byte skipFrames);
 	void playSoundNow(Pack *pack, uint32 id);
+	void playVoiceNow(Pack *pack, uint32 id);
+	void stopVoice();
 	void enableObject(byte id, byte sceneId = 0);
 	void disableObject(byte id, byte sceneId = 0);
 	void hideActor();
@@ -178,7 +180,8 @@ public:
 	Common::U32String convertCyrillic(const Common::String &text) const;
 
 	Common::String _previousSaid;
-	
+	uint16 _previousVoiceId;
+
 private:
 	void processObject();
 	bool trySelectedObject();
@@ -188,7 +191,7 @@ private:
 	Object *_dstObject;
 
 	Audio::AudioStream *_musicStream;
-	Audio::SoundHandle _musicHandle, _soundHandle;
+	Audio::SoundHandle _musicHandle, _soundHandle, _voiceHandle;
 	const ADGameDescription *_gameDescription;
 
 	uint _markDelay, _gameDelay;


Commit: 2faa0d7f034a9204f6a9268be7904083a9f61e53
    https://github.com/scummvm/scummvm/commit/2faa0d7f034a9204f6a9268be7904083a9f61e53
Author: Alikhan Balpykov (luxrage1990 at gmail.com)
Date: 2025-08-09T21:03:44+02:00

Commit Message:
TEENAGENT: Remove unused variables in Resources class

Changed paths:
    engines/teenagent/resources.cpp
    engines/teenagent/resources.h


diff --git a/engines/teenagent/resources.cpp b/engines/teenagent/resources.cpp
index 63f8c12253a..fca70d1457e 100644
--- a/engines/teenagent/resources.cpp
+++ b/engines/teenagent/resources.cpp
@@ -31,13 +31,9 @@
 namespace TeenAgent {
 
 Resources::Resources() {
-	_combinationsStartOffset = 0;
-	_creditsStartOffset = 0;
 	_dialogsStartOffset = 0;
-	_messagesStartOffset = 0;
 	_sceneObjectsStartOffset = 0;
 	_sceneObjectsBlockSize = 0;
-	_itemsStartOffset = 0;
 }
 
 Resources::~Resources() {
@@ -148,11 +144,9 @@ void Resources::precomputeAllOffsets(const Common::Array<ResourceInfo> &resource
 	for (const auto &resInfo : resourceInfos) {
 		switch ((ResourceType)resInfo._id) {
 		case kResCombinations:
-			_combinationsStartOffset = resInfo._offset;
 			precomputeCombinationOffsets(resInfo);
 			break;
 		case kResCredits:
-			_creditsStartOffset = resInfo._offset;
 			precomputeCreditsOffsets(resInfo);
 			break;
 		case kResDialogs:
@@ -160,11 +154,9 @@ void Resources::precomputeAllOffsets(const Common::Array<ResourceInfo> &resource
 			precomputeDialogOffsets(resInfo);
 			break;
 		case kResItems:
-			_itemsStartOffset = resInfo._offset;
 			precomputeItemOffsets(resInfo);
 			break;
 		case kResMessages:
-			_messagesStartOffset = resInfo._offset;
 			precomputeMessageOffsets(resInfo);
 			break;
 		case kResSceneObjects:
diff --git a/engines/teenagent/resources.h b/engines/teenagent/resources.h
index 621a6abbc59..ceb04fa5cec 100644
--- a/engines/teenagent/resources.h
+++ b/engines/teenagent/resources.h
@@ -1587,14 +1587,9 @@ private:
 	Common::Array<uint32> messageOffsets;
 	Common::Array<uint32> combinationOffsets;
 
-	uint32 _messagesStartOffset;
-
 	uint32 _sceneObjectsStartOffset;
 	uint32 _sceneObjectsBlockSize; // Needed to know how much to write to savefile
 
-	uint32 _combinationsStartOffset;
-	uint32 _itemsStartOffset;
-	uint32 _creditsStartOffset;
 	uint32 _dialogsStartOffset;
 
 	Common::HashMap<uint32, uint16> _addrToVoiceIndx;


Commit: e358d02685fe8bb820e6de49b3752c6b3bbc9ea2
    https://github.com/scummvm/scummvm/commit/e358d02685fe8bb820e6de49b3752c6b3bbc9ea2
Author: Alikhan Balpykov (luxrage1990 at gmail.com)
Date: 2025-08-09T21:03:44+02:00

Commit Message:
TEENAGENT: Fix description being not shown for inventory items

Changed paths:
    engines/teenagent/inventory.cpp


diff --git a/engines/teenagent/inventory.cpp b/engines/teenagent/inventory.cpp
index 69ff688cb5a..5944bfc4211 100644
--- a/engines/teenagent/inventory.cpp
+++ b/engines/teenagent/inventory.cpp
@@ -264,6 +264,8 @@ bool Inventory::processEvent(const Common::Event &event) {
 			activate(!_active);
 			return true;
 		}
+		if (event.customType == kActionSkipDialog)
+			return true;
 		return false;
 
 	case Common::EVENT_LBUTTONUP:




More information about the Scummvm-git-logs mailing list