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

fracturehill noreply at scummvm.org
Sat Sep 16 10:15:12 UTC 2023


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

Summary:
74145342ea NANCY: Properly read CVTX chunks
a862dcef29 NANCY: Add support for terse Conversation records
722ee8c3e9 NANCY: Correctly read nancy6 FONT chunks
a45544cb0f DEVTOOLS: Add nancy6 data to nancy.dat
d3864e106d NANCY: Add support for nancy6 static data
555de022cb NANCY: Support nancy6 cursors
11f6f9ec51 NANCY: Implement nancy4 SafeDialPuzzle
c3e7b418b9 NANCY: Fix nancy4 elevator death scene
abc4168433 NANCY: Properly pause all scene sounds


Commit: 74145342eabf50d5cf1d9e942767bb8c8140f48b
    https://github.com/scummvm/scummvm/commit/74145342eabf50d5cf1d9e942767bb8c8140f48b
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T13:14:52+03:00

Commit Message:
NANCY: Properly read CVTX chunks

Fixed a typo that caused most of CVTX chunks to not
actually be read.

Changed paths:
    engines/nancy/enginedata.cpp


diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index fdf7d022936..306024c333a 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -690,11 +690,11 @@ CVTX::CVTX(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 			delete buf;
 			buf = new char[stringSize * 2];
 			bufSize = stringSize * 2;
-
-			chunkStream->read(buf, stringSize);
-			buf[stringSize] = '\0';
-			texts.setVal(keyName, buf);
 		}
+
+		chunkStream->read(buf, stringSize);
+		buf[stringSize] = '\0';
+		texts.setVal(keyName, buf);
 	}
 
 	delete buf;


Commit: a862dcef29463163e665d64fad380d1d7239eee3
    https://github.com/scummvm/scummvm/commit/a862dcef29463163e665d64fad380d1d7239eee3
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T13:14:52+03:00

Commit Message:
NANCY: Add support for terse Conversation records

Added support for ConversationCelT and
ConversationSoundT, which are identical to their sister types,
except instead of containing their caption texts inside the
record data, they contain a key pointing inside the Convo
CVTX struct. First introduced in nancy6.

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/conversation.cpp
    engines/nancy/action/conversation.h


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index cd41dc81b2e..bd1907cf026 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -120,8 +120,15 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
 		return new ConversationCel();
 	case 58:
 		return new ConversationSound();
+	case 59:
+		return new ConversationCelT();
 	case 60:
-		return new MapCall();
+		if (g_nancy->getGameType() <= kGameTypeNancy5) {
+			// Only used in tvd and nancy1
+			return new MapCall();
+		} else {
+			return new ConversationSoundT();
+		}
 	case 61:
 		return new MapCallHot1Fr();
 	case 62:
diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 59fba80957d..e23fcecad64 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -61,17 +61,14 @@ void ConversationSound::readData(Common::SeekableReadStream &stream) {
 		_sound.readNormal(stream);
 	}
 
-	char *rawText = new char[1500];
-	ser.syncBytes((byte *)rawText, 1500);
-	UI::Textbox::assembleTextLine(rawText, _text, 1500);
-	delete[] rawText;
+	readCaptionText(stream);
 
 	if (ser.getVersion() <= kGameTypeNancy1) {
 		_sound.readNormal(stream);
 	}
 
 	_responseGenericSound.readNormal(stream);
-	ser.skip(1);
+	ser.skip(1); // RESPONSE_STARTUP_CLEAR_ALL, RESPONSE_STARTUP_KEEP_OLD; never used (tested up to nancy5)
 	ser.syncAsByte(_conditionalResponseCharacterID);
 	ser.syncAsByte(_goodbyeResponseCharacterID);
 	ser.syncAsByte(_defaultNextScene);
@@ -82,16 +79,14 @@ void ConversationSound::readData(Common::SeekableReadStream &stream) {
 
 	uint16 numResponses = 0;
 	ser.syncAsUint16LE(numResponses);
-	rawText = new char[400];
 
 	_responses.resize(numResponses);
 	for (uint i = 0; i < numResponses; ++i) {
 		ResponseStruct &response = _responses[i];
 		response.conditionFlags.read(stream);
-		ser.syncBytes((byte*)rawText, 400);
-		UI::Textbox::assembleTextLine(rawText, response.text, 400);
+		readResponseText(stream, response);
 		readFilename(stream, response.soundName);
-		ser.skip(1);
+		ser.skip(1); // RESPONSE_ADD_IF_NOT_FOUND, RESPONSE_REMOVE_AND_ADD_TO_END, or RESPONSE_REMOVE
 		response.sceneChange.readData(stream, ser.getVersion() == kGameTypeVampire);
 		ser.syncAsSint16LE(response.flagDesc.label);
 		ser.syncAsByte(response.flagDesc.flag);
@@ -99,8 +94,6 @@ void ConversationSound::readData(Common::SeekableReadStream &stream) {
 		ser.skip(2, kGameTypeNancy2);
 	}
 
-	delete[] rawText;
-
 	uint16 numSceneBranchStructs = stream.readUint16LE();
 	_sceneBranchStructs.resize(numSceneBranchStructs);
 	for (uint i = 0; i < numSceneBranchStructs; ++i) {
@@ -291,6 +284,20 @@ void ConversationSound::execute() {
 	}
 }
 
+void ConversationSound::readCaptionText(Common::SeekableReadStream &stream) {
+	char *rawText = new char[1500];
+	stream.read(rawText, 1500);
+	UI::Textbox::assembleTextLine(rawText, _text, 1500);
+	delete[] rawText;
+}
+
+void ConversationSound::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
+	char *rawText = new char[400];
+	stream.read(rawText, 400);
+	UI::Textbox::assembleTextLine(rawText, response.text, 400);
+	delete[] rawText;
+}
+
 void ConversationSound::addConditionalDialogue() {
 	for (const auto &res : g_nancy->getStaticData().conditionalDialogue[_conditionalResponseCharacterID]) {
 		bool isSatisfied = true;
@@ -708,5 +715,45 @@ ConversationCel::Cel &ConversationCel::loadCel(const Common::String &name, const
 	return _celCache[name];
 }
 
+void ConversationSoundT::readCaptionText(Common::SeekableReadStream &stream) {
+	Common::String key;
+	readFilename(stream, key);
+
+	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
+	assert(convo);
+
+	_text = convo->texts[key];
+}
+
+void ConversationSoundT::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
+	Common::String key;
+	readFilename(stream, key);
+
+	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
+	assert(convo);
+
+	response.text = convo->texts[key];
+}
+
+void ConversationCelT::readCaptionText(Common::SeekableReadStream &stream) {
+	Common::String key;
+	readFilename(stream, key);
+
+	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
+	assert(convo);
+
+	_text = convo->texts[key];
+}
+
+void ConversationCelT::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
+	Common::String key;
+	readFilename(stream, key);
+
+	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
+	assert(convo);
+
+	response.text = convo->texts[key];
+}
+
 } // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/conversation.h b/engines/nancy/action/conversation.h
index 45294a20cdd..bf8ec111603 100644
--- a/engines/nancy/action/conversation.h
+++ b/engines/nancy/action/conversation.h
@@ -87,6 +87,10 @@ protected:
 	Common::String getRecordTypeName() const override { return "ConversationSound"; }
 	bool isViewportRelative() const override { return true; }
 
+	// Functions for reading captions are virtual to allow easier support for the terse Conversation variants
+	virtual void readCaptionText(Common::SeekableReadStream &stream);
+	virtual void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response);
+
 	// Functions for handling the built-in dialogue responses found in the executable
 	void addConditionalDialogue();
 	void addGoodbye();
@@ -195,6 +199,22 @@ protected:
 	Common::SharedPtr<ConversationCelLoader> _loaderPtr;
 };
 
+class ConversationSoundT : public ConversationSound {
+protected:
+	Common::String getRecordTypeName() const override { return "ConversationSoundT"; }
+
+	void readCaptionText(Common::SeekableReadStream &stream) override;
+	void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override;
+};
+
+class ConversationCelT : public ConversationCel {
+protected:
+	Common::String getRecordTypeName() const override { return "ConversationCelT"; }
+
+	void readCaptionText(Common::SeekableReadStream &stream) override;
+	void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override;
+};
+
 } // End of namespace Action
 } // End of namespace Nancy
 


Commit: 722ee8c3e9d6f0ce5f96837e1603fc915f7a3edd
    https://github.com/scummvm/scummvm/commit/722ee8c3e9d6f0ce5f96837e1603fc915f7a3edd
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T13:14:52+03:00

Commit Message:
NANCY: Correctly read nancy6 FONT chunks

Added support for nancy6's font data, which now includes
some extended ASCII characters.

Changed paths:
    engines/nancy/font.cpp
    engines/nancy/font.h


diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index 2ed3a9212cb..1b110e1741c 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -48,30 +48,60 @@ void Font::read(Common::SeekableReadStream &stream) {
 	stream.skip(2);
 	_spaceWidth = stream.readUint16LE();
 	stream.skip(2);
-	_uppercaseOffset = stream.readUint16LE();
-	_lowercaseOffset = stream.readUint16LE();
-	_digitOffset = stream.readUint16LE();
-	_periodOffset = stream.readUint16LE();
-	_commaOffset = stream.readUint16LE();
-	_equalitySignOffset = stream.readUint16LE();
-	_colonOffset = stream.readUint16LE();
-	_dashOffset = stream.readUint16LE();
-	_questionMarkOffset = stream.readUint16LE();
-	_exclamationMarkOffset = stream.readUint16LE();
-	_percentOffset = stream.readUint16LE();
-	_ampersandOffset = stream.readUint16LE();
-	_asteriskOffset = stream.readUint16LE();
-	_leftBracketOffset = stream.readUint16LE();
-	_rightBracketOffset = stream.readUint16LE();
-	_plusOffset = stream.readUint16LE();
-	_apostropheOffset = stream.readUint16LE();
-	_semicolonOffset = stream.readUint16LE();
-	_slashOffset = stream.readUint16LE();
-
-	_symbolRects.reserve(78);
-	for (uint i = 0; i < 78; ++i) {
-		_symbolRects.push_back(Common::Rect());
-		Common::Rect &cur = _symbolRects[i];
+	_uppercaseOffset					= stream.readUint16LE();
+	_lowercaseOffset					= stream.readUint16LE();
+	_digitOffset						= stream.readUint16LE();
+	_periodOffset						= stream.readUint16LE();
+	_commaOffset						= stream.readUint16LE();
+	_equalitySignOffset					= stream.readUint16LE();
+	_colonOffset						= stream.readUint16LE();
+	_dashOffset							= stream.readUint16LE();
+	_questionMarkOffset					= stream.readUint16LE();
+	_exclamationMarkOffset				= stream.readUint16LE();
+	_percentOffset						= stream.readUint16LE();
+	_ampersandOffset					= stream.readUint16LE();
+	_asteriskOffset						= stream.readUint16LE();
+	_leftBracketOffset					= stream.readUint16LE();
+	_rightBracketOffset					= stream.readUint16LE();
+	_plusOffset							= stream.readUint16LE();
+	_apostropheOffset					= stream.readUint16LE();
+	_semicolonOffset					= stream.readUint16LE();
+	_slashOffset						= stream.readUint16LE();
+
+	if (g_nancy->getGameType() >= kGameTypeNancy6) {
+		// Nancy6 added more characters to its fonts
+		_aWithGraveOffset 				= stream.readUint16LE();
+		_cWithCedillaOffset				= stream.readUint16LE();
+		_eWithGraveOffset 				= stream.readUint16LE();
+		_eWithAcuteOffset 				= stream.readUint16LE();
+		_eWithCircumflexOffset			= stream.readUint16LE();
+		_eWithDiaeresisOffset			= stream.readUint16LE();
+		_oWithCircumflexOffset			= stream.readUint16LE();
+		_uppercaseAWithGraveOffset		= stream.readUint16LE();
+		_aWithCircumflexOffset			= stream.readUint16LE();
+		_iWithCircumflexOffset			= stream.readUint16LE();
+		_uWithGraveOffset 				= stream.readUint16LE();
+		_uppercaseAWithDiaeresisOffset	= stream.readUint16LE();
+		_aWithDiaeresisOffset			= stream.readUint16LE();
+		_uppercaseOWithDiaeresisOffset	= stream.readUint16LE();
+		_oWithDiaeresisOffset			= stream.readUint16LE();
+		_uppercaseUWithDiaeresisOffset	= stream.readUint16LE();
+		_uWithDiaeresisOffset			= stream.readUint16LE();
+		_invertedExclamationMarkOffset	= stream.readUint16LE();
+		_invertedQuestionMarkOffset		= stream.readUint16LE();
+		_uppercaseNWithTildeOffset		= stream.readUint16LE();
+		_nWithTildeOffset				= stream.readUint16LE();
+		_uppercaseEWithAcuteOffset		= stream.readUint16LE();
+		_aWithAcuteOffset				= stream.readUint16LE();
+		_iWithAcuteOffset				= stream.readUint16LE();
+		_oWithAcuteOffset				= stream.readUint16LE();
+		_uWithAcuteOffset				= stream.readUint16LE();
+		_eszettOffset					= stream.readUint16LE();
+	}
+
+	_characterRects.resize(g_nancy->getGameType() >= kGameTypeNancy6 ? 105 : 78);
+	for (uint i = 0; i < _characterRects.size(); ++i) {
+		Common::Rect &cur = _characterRects[i];
 		readRect(stream, cur);
 
 		if (g_nancy->getGameType() == kGameTypeVampire) {
@@ -160,18 +190,109 @@ void Font::wordWrap(const Common::String &str, int maxWidth, Common::Array<Commo
 }
 
 Common::Rect Font::getCharacterSourceRect(char chr) const {
+	// Map a character to its source rect inside the font data.
+	// The original engine devs had some _interesting_ ideas on how to store font data,
+	// which makes the ridiculous switch statements below a necessity
 	using namespace Common;
-	uint offset = 0;
+	int offset = -1;
 	Common::Rect ret;
 
-	if (isUpper(chr)) {
+	if (chr < 0) {
+		// Nancy6 introduced extended ASCII characters
+		switch (chr) {
+		case '\xe0':
+			offset = _aWithGraveOffset;
+			break;
+		case '\xe7':
+			offset = _cWithCedillaOffset;
+			break;
+		case '\xe8':
+			offset = _eWithGraveOffset;
+			break;
+		case '\xe9':
+			offset = _eWithAcuteOffset;
+			break;
+		case '\xea':
+			offset = _eWithCircumflexOffset;
+			break;
+		case '\xeb':
+			offset = _eWithDiaeresisOffset;
+			break;
+		case '\xf4':
+			offset = _oWithCircumflexOffset;
+			break;
+		case '\xc0':
+			offset = _uppercaseAWithGraveOffset;
+			break;
+		case '\xe2':
+			offset = _aWithCircumflexOffset;
+			break;
+		case '\xee':
+			offset = _iWithCircumflexOffset;
+			break;
+		case '\xf9':
+			offset = _uWithGraveOffset;
+			break;
+		case '\xc4':
+			offset = _uppercaseAWithDiaeresisOffset;
+			break;
+		case '\xe4':
+			offset = _aWithDiaeresisOffset;
+			break;
+		case '\xd6':
+			offset = _uppercaseOWithDiaeresisOffset;
+			break;
+		case '\xf6':
+			offset = _oWithDiaeresisOffset;
+			break;
+		case '\xdc':
+			offset = _uppercaseUWithDiaeresisOffset;
+			break;
+		case '\xfc':
+			offset = _uWithDiaeresisOffset;
+			break;
+		case '\xa1':
+			offset = _invertedExclamationMarkOffset;
+			break;
+		case '\xbf':
+			offset = _invertedQuestionMarkOffset;
+			break;
+		case '\xd1':
+			offset = _uppercaseNWithTildeOffset;
+			break;
+		case '\xf1':
+			offset = _nWithTildeOffset;
+			break;
+		case '\xc9':
+			offset = _uppercaseEWithAcuteOffset;
+			break;
+		case '\xe1':
+			offset = _aWithAcuteOffset;
+			break;
+		case '\xed':
+			offset = _iWithAcuteOffset;
+			break;
+		case '\xf3':
+			offset = _oWithAcuteOffset;
+			break;
+		case '\xfa':
+			offset = _uWithAcuteOffset;
+			break;
+		case '\xdf':
+			offset = _eszettOffset;
+			break;
+		default:
+			offset = -1;
+			break;
+		}
+	} else if (isUpper(chr)) {
 		offset = chr + _uppercaseOffset - 0x41;
 	} else if (isLower(chr)) {
 		offset = chr + _lowercaseOffset - 0x61;
 	} else if (isDigit(chr)) {
 		offset = chr + _digitOffset - 0x30;
 	} else if (isSpace(chr)) {
-		ret.setWidth(_spaceWidth - 1); // Not sure why we sutract 1
+		ret.setWidth(_spaceWidth - 1);
 		return ret;
 	} else if (isPunct(chr)) {
 		switch (chr) {
@@ -224,14 +345,22 @@ Common::Rect Font::getCharacterSourceRect(char chr) const {
 			offset = _slashOffset;
 			break;
 		default:
-			// Replace unknown characters with question marks. This shouldn't happen normally,
-			// but we need it to support displaying saves that were originally made in the GMM
-			// inside the in-game load/save menu.
-			offset = _questionMarkOffset;
+			offset = -1;
 			break;
 		}
 	}
-	ret = _symbolRects[offset];
+
+	if (offset == -1) {
+		// There is at least one malformed string in nancy4 that will reach this.
+		// Also, this helps support displaying saves that were originally made in the GMM
+		// inside the in-game load/save menu, since those _may_ contain characters not present in the font
+
+		// When reaching an invalid char, the original engine repeated the last printed character,
+		// but we print a question mark instead.
+		offset = _questionMarkOffset;
+	}
+
+	ret = _characterRects[offset];
 	return ret;
 }
 
diff --git a/engines/nancy/font.h b/engines/nancy/font.h
index 39ba8fa89a1..af7dc3ef9d2 100644
--- a/engines/nancy/font.h
+++ b/engines/nancy/font.h
@@ -56,29 +56,61 @@ public:
 private:
 	Common::Rect getCharacterSourceRect(char chr) const;
 
-	Common::String _description;				// 0xA
-	Common::Point _colorCoordsOffset;			// 0x32
-	uint16 _spaceWidth;							// 0x38
-	uint16 _uppercaseOffset;					// 0x3C
-	uint16 _lowercaseOffset;					// 0x3E
-	uint16 _digitOffset;						// 0x40
-	uint16 _periodOffset;						// 0x42
-	uint16 _commaOffset;						// 0x44
-	uint16 _equalitySignOffset;					// 0x46
-	uint16 _colonOffset;						// 0x48
-	uint16 _dashOffset;							// 0x4A
-	uint16 _questionMarkOffset;					// 0x4C
-	uint16 _exclamationMarkOffset;				// 0x4E
-	uint16 _percentOffset;						// 0x50
-	uint16 _ampersandOffset;					// 0x52
-	uint16 _asteriskOffset;						// 0x54
-	uint16 _leftBracketOffset;					// 0x56
-	uint16 _rightBracketOffset;					// 0x58
-	uint16 _plusOffset;							// 0x5A
-	uint16 _apostropheOffset;					// 0x5C
-	uint16 _semicolonOffset;					// 0x5E
-	uint16 _slashOffset;						// 0x60
-	Common::Array<Common::Rect> _symbolRects;	// 0x62
+	Common::String _description;
+	Common::Point _colorCoordsOffset; // Added to source rects when colored text is requested
+	uint16 _spaceWidth;
+
+	// Specific offsets into the _characterRects array
+	uint16 _uppercaseOffset			= 0;
+	uint16 _lowercaseOffset			= 0;
+	uint16 _digitOffset				= 0;
+	uint16 _periodOffset			= 0;
+	uint16 _commaOffset				= 0;
+	uint16 _equalitySignOffset		= 0;
+	uint16 _colonOffset				= 0;
+	uint16 _dashOffset				= 0;
+	uint16 _questionMarkOffset		= 0;
+	uint16 _exclamationMarkOffset	= 0;
+	uint16 _percentOffset			= 0;
+	uint16 _ampersandOffset			= 0;
+	uint16 _asteriskOffset			= 0;
+	uint16 _leftBracketOffset		= 0;
+	uint16 _rightBracketOffset		= 0;
+	uint16 _plusOffset				= 0;
+	uint16 _apostropheOffset		= 0;
+	uint16 _semicolonOffset			= 0;
+	uint16 _slashOffset				= 0;
+
+	// More specific offsets for extended ASCII characters. Introduced in nancy6
+	int16 _aWithGraveOffset					= -1;
+	int16 _cWithCedillaOffset				= -1;
+	int16 _eWithGraveOffset					= -1;
+	int16 _eWithAcuteOffset					= -1;
+	int16 _eWithCircumflexOffset			= -1;
+	int16 _eWithDiaeresisOffset				= -1;
+	int16 _oWithCircumflexOffset			= -1;
+	int16 _uppercaseAWithGraveOffset		= -1;
+	int16 _aWithCircumflexOffset			= -1;
+	int16 _iWithCircumflexOffset			= -1;
+	int16 _uWithGraveOffset 				= -1;
+	int16 _uppercaseAWithDiaeresisOffset	= -1;
+	int16 _aWithDiaeresisOffset				= -1;
+	int16 _uppercaseOWithDiaeresisOffset	= -1;
+	int16 _oWithDiaeresisOffset				= -1;
+	int16 _uppercaseUWithDiaeresisOffset	= -1;
+	int16 _uWithDiaeresisOffset				= -1;
+	int16 _invertedExclamationMarkOffset	= -1;
+	int16 _invertedQuestionMarkOffset		= -1;
+	int16 _uppercaseNWithTildeOffset		= -1;
+	int16 _nWithTildeOffset					= -1;
+	int16 _uppercaseEWithAcuteOffset		= -1;
+	int16 _aWithAcuteOffset					= -1;
+	int16 _iWithAcuteOffset					= -1;
+	int16 _oWithAcuteOffset					= -1;
+	int16 _uWithAcuteOffset					= -1;
+	int16 _eszettOffset						= -1;
+
+	Common::Array<Common::Rect> _characterRects;
 
 	Graphics::ManagedSurface _image;
 


Commit: a45544cb0fc4b240c095fbcf9677f29bc6539796
    https://github.com/scummvm/scummvm/commit/a45544cb0fc4b240c095fbcf9677f29bc6539796
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T13:14:52+03:00

Commit Message:
DEVTOOLS: Add nancy6 data to nancy.dat

Changed paths:
  A devtools/create_nancy/nancy6_data.h
    devtools/create_nancy/create_nancy.cpp
    devtools/create_nancy/nancy3_data.h
    devtools/create_nancy/types.h


diff --git a/devtools/create_nancy/create_nancy.cpp b/devtools/create_nancy/create_nancy.cpp
index cafa856c508..e426f1dcf87 100644
--- a/devtools/create_nancy/create_nancy.cpp
+++ b/devtools/create_nancy/create_nancy.cpp
@@ -26,11 +26,12 @@
 #include "nancy3_data.h"
 #include "nancy4_data.h"
 #include "nancy5_data.h"
+#include "nancy6_data.h"
 
 #define NANCYDAT_MAJOR_VERSION 1
 #define NANCYDAT_MINOR_VERSION 0
 
-#define NANCYDAT_NUM_GAMES 6
+#define NANCYDAT_NUM_GAMES 7
 
 /**
  * Format specifications for nancy.dat:
@@ -64,6 +65,7 @@
  * 		Nancy Drew: Message in a Haunted Mansion
  * 		Nancy Drew: Treasure in the Royal Tower
  * 		Nancy Drew: The Final Scene
+ * 		Nancy Drew: Secret of the Scarlet Hand
 */
 
 // Add the offset to the next tagged section before the section itself for easier navigation
@@ -111,12 +113,24 @@ void writeConditionalDialogue(File &output, const Common::Array<Common::Array<Co
 	writeMultilangArray(output, dialogueTexts);
 }
 
+// Version without text array, used in nancy6 and up
+void writeConditionalDialogue(File &output, const Common::Array<Common::Array<ConditionalDialogue>> &conditionalDialogue) {
+	output.writeUint32(MKTAG('C', 'D', 'L', '2'));
+	writeToFile(output, conditionalDialogue);
+}
+
 void writeGoodbyes(File &output, const Common::Array<Goodbye> &goodbyes, const Common::Array<Common::Array<const char *>> &goodbyeTexts) {
 	output.writeUint32(MKTAG('G', 'D', 'B', 'Y'));
 	writeToFile(output, goodbyes);
 	writeMultilangArray(output, goodbyeTexts);
 }
 
+// Version without text array, used in nancy6 and up
+void writeGoodbyes(File &output, const Common::Array<Goodbye> &goodbyes) {
+	output.writeUint32(MKTAG('G', 'D', 'B', '2'));
+	writeToFile(output, goodbyes);
+}
+
 void writeHints(File &output, const Common::Array<Common::Array<Hint>> &hints, const SceneChangeDescription &hintSceneChange, const Common::Array<Common::Array<const char *>> &hintTexts) {
 	output.writeUint32(MKTAG('H', 'I', 'N', 'T'));
 	writeToFile(output, hintSceneChange);
@@ -197,7 +211,7 @@ int main(int argc, char *argv[]) {
 	// Nancy Drew: Message in a Haunted Mansion data
 	gameOffsets.push_back(output.pos());
 	WRAPWITHOFFSET(writeConstants(output, _nancy3Constants))
-	WRAPWITHOFFSET(writeSoundChannels(output, _nancy3to5SoundChannelInfo))
+	WRAPWITHOFFSET(writeSoundChannels(output, _nancy3to6SoundChannelInfo))
 	WRAPWITHOFFSET(writeLanguages(output, _nancy3LanguagesOrder))
 	WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy3ConditionalDialogue, _nancy3ConditionalDialogueTexts))
 	WRAPWITHOFFSET(writeGoodbyes(output, _nancy3Goodbyes, _nancy3GoodbyeTexts))
@@ -208,7 +222,7 @@ int main(int argc, char *argv[]) {
 	// Nancy Drew: Treasure in the Royal Tower data
 	gameOffsets.push_back(output.pos());
 	WRAPWITHOFFSET(writeConstants(output, _nancy4Constants))
-	WRAPWITHOFFSET(writeSoundChannels(output, _nancy3to5SoundChannelInfo))
+	WRAPWITHOFFSET(writeSoundChannels(output, _nancy3to6SoundChannelInfo))
 	WRAPWITHOFFSET(writeLanguages(output, _nancy4LanguagesOrder))
 	WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy4ConditionalDialogue, _nancy4ConditionalDialogueTexts))
 	WRAPWITHOFFSET(writeGoodbyes(output, _nancy4Goodbyes, _nancy4GoodbyeTexts))
@@ -219,7 +233,7 @@ int main(int argc, char *argv[]) {
 	// Nancy Drew: The Final Scene data
 	gameOffsets.push_back(output.pos());
 	WRAPWITHOFFSET(writeConstants(output, _nancy5Constants))
-	WRAPWITHOFFSET(writeSoundChannels(output, _nancy3to5SoundChannelInfo))
+	WRAPWITHOFFSET(writeSoundChannels(output, _nancy3to6SoundChannelInfo))
 	WRAPWITHOFFSET(writeLanguages(output, _nancy5LanguagesOrder))
 	WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy5ConditionalDialogue, _nancy5ConditionalDialogueTexts))
 	WRAPWITHOFFSET(writeGoodbyes(output, _nancy5Goodbyes, _nancy5GoodbyeTexts))
@@ -227,6 +241,17 @@ int main(int argc, char *argv[]) {
 	WRAPWITHOFFSET(writeEmptySaveTexts(output, _nancy5EmptySaveStrings))
 	WRAPWITHOFFSET(writeEventFlagNames(output, _nancy5EventFlagNames))
 
+	// Nancy Drew: Secret of the Scarlet Hand
+	gameOffsets.push_back(output.pos());
+	WRAPWITHOFFSET(writeConstants(output, _nancy6Constants))
+	WRAPWITHOFFSET(writeSoundChannels(output, _nancy3to6SoundChannelInfo))
+	WRAPWITHOFFSET(writeLanguages(output, _nancy6LanguagesOrder))
+	WRAPWITHOFFSET(writeConditionalDialogue(output, _nancy6ConditionalDialogue))
+	WRAPWITHOFFSET(writeGoodbyes(output, _nancy6Goodbyes))
+	WRAPWITHOFFSET(writeRingingTexts(output, _nancy6TelephoneRinging))
+	WRAPWITHOFFSET(writeEmptySaveTexts(output, _nancy6EmptySaveStrings))
+	WRAPWITHOFFSET(writeEventFlagNames(output, _nancy6EventFlagNames))
+
 	// Write the offsets for each game in the header
 	output.seek(offsetsOffset);
 	for (uint i = 0; i < gameOffsets.size(); ++i) {
diff --git a/devtools/create_nancy/nancy3_data.h b/devtools/create_nancy/nancy3_data.h
index 8a6666fbbae..baf58ef5d29 100644
--- a/devtools/create_nancy/nancy3_data.h
+++ b/devtools/create_nancy/nancy3_data.h
@@ -36,7 +36,7 @@ const GameConstants _nancy3Constants = {
 	4000
 };
 
-const SoundChannelInfo _nancy3to5SoundChannelInfo = {
+const SoundChannelInfo _nancy3to6SoundChannelInfo = {
 	32, 14,
 	{ 12, 13, 30 },
 	{ 0, 1, 2, 3, 19, 26, 27, 29 },
diff --git a/devtools/create_nancy/nancy6_data.h b/devtools/create_nancy/nancy6_data.h
new file mode 100644
index 00000000000..af1a43b095f
--- /dev/null
+++ b/devtools/create_nancy/nancy6_data.h
@@ -0,0 +1,712 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef NANCY6DATA_H
+#define NANCY6DATA_H
+
+#include "types.h"
+
+const GameConstants _nancy6Constants ={
+	36,
+	456,
+	{ }, // No Map state
+	{	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+		11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+		21, 22, 23, 24, 25, 26, 27, 28, 29, 30 },
+	48,
+	7,
+	4000
+};
+
+const Common::Array<Common::Language> _nancy6LanguagesOrder = {
+	Common::Language::EN_ANY,
+	Common::Language::RU_RUS
+};
+
+const Common::Array<Common::Array<ConditionalDialogue>> _nancy6ConditionalDialogue = {
+{	// Joanna, 17 responses
+	{	0, 1050, "NJR50",
+		{ { kEv, 304, true }, { kEv, 364, false }, { kEv, 202, false } } },
+	{	0, 1051, "NJR51",
+		{ { kEv, 304, true }, { kEv, 371, false }, { kEv, 184, false } } },
+	{	0, 1052, "NJR52",
+		{ { kEv, 304, true }, { kEv, 361, false }, { kEv, 198, false } } },
+	{	0, 1053, "NJR53",
+		{ { kEv, 304, true }, { kEv, 398, true }, { kEv, 347, false }, { kEv, 194, false } } },
+	{	0, 1054, "NJR54",
+		{ { kEv, 241, true }, { kEv, 231, true }, { kEv, 188, false } } },
+	{	0, 1055, "NJR55",
+		{ { kEv, 391, true }, { kEv, 189, false }, { kEv, 239, false } } },
+	{	0, 1057, "NJR57",
+		{ { kEv, 239, true }, { kEv, 201, false }, { kEv, 415, false } } },
+	{	0, 1058, "NJR58",
+		{ { kEv, 321, true }, { kEv, 186, false } } },
+	{	0, 1060, "NJR60",
+		{ { kEv, 305, true }, { kEv, 209, false }, { kEv, 344, false } } },
+	{	0, 1063, "NJR63",
+		{ { kEv, 212, true }, { kEv, 148, false }, { kEv, 200, false } } },
+	{	0, 1064, "NJR64",
+		{ { kEv, 391, true }, { kEv, 191, false }, { kEv, 239, false } } },
+	{	0, 1065, "NJR65",
+		{ { kEv, 241, true }, { kEv, 240, true }, { kEv, 190, false } } },
+	{	0, 1068, "NJR68",
+		{ { kEv, 310, true }, { kEv, 39, false }, { kEv, 197, false } } },
+	{	0, 1069, "NJR69",
+		{ { kEv, 420, true }, { kEv, 118, false }, { kEv, 203, false } } },
+	{	0, 1070, "NJR70",
+		{ { kEv, 231, true }, { kEv, 247, true }, { kEv, 205, false } } },
+	{	0, 1071, "NJR71",
+		{ { kEv, 247, true }, { kEv, 204, false } } },
+	{	0, 1072, "NTS60",
+		{ { kEv, 397, true }, { kEv, 154, false }, { kEv, 187, false } } }
+},
+{	// Taylor, 10 responses
+	{	0, 1250, "NTA50",
+		{ { kEv, 45, true }, { kEv, 416, false } } },
+	{	0, 1253, "NTA53",
+		{ { kEv, 231, true }, { kEv, 407, false } } },
+	{	0, 1254, "NTA54",
+		{ { kEv, 231, true }, { kEv, 409, false } } },
+	{	0, 1256, "NTA56",
+		{ { kEv, 243, true }, { kEv, 411, false }, { kIn, 2, true } } },
+	{	0, 1257, "NTA57",
+		{ { kEv, 239, true }, { kEv, 415, false } } },
+	{	0, 1258, "NTA58",
+		{ { kEv, 148, true }, { kEv, 417, false } } },
+	{	0, 1260, "NTS60",
+		{ { kEv, 397, true }, { kEv, 154, false }, { kEv, 408, false } } },
+	{	0, 1262, "NTS62",
+		{ { kEv, 212, true }, { kEv, 424, false } } },
+	{	0, 1263, "NTS63",
+		{ { kEv, 310, true }, { kEv, 39, false }, { kEv, 413, false } } },
+	{	0, 1264, "NTA64",
+		{ { kEv, 243, true }, { kEv, 375, false }, { kEv, 410, false }, { kIn, 2, false } } }
+},
+{	// Alejandro, 9 responses
+	{	0, 1350, "NAR50",
+		{ { kEv, 241, true }, { kEv, 231, true }, { kEv, 40, false } } },
+	{	0, 1351, "NAR51",
+		{ { kEv, 391, true }, { kEv, 42, false } } },
+	{	0, 1353, "NAR53",
+		{ { kEv, 255, true }, { kEv, 36, false } } },
+	{	0, 1361, "NAR61",
+		{ { kEv, 288, true }, { kEv, 384, false }, { kEv, 47, false } } },
+	{	0, 1362, "NAR62",
+		{ { kEv, 310, true }, { kEv, 41, false } } },
+	{	0, 1370, "NAR70",
+		{ { kEv, 148, true }, { kEv, 44, false } } },
+	{	0, 1371, "NAR71",
+		{ { kEv, 43, true }, { kEv, 37, false } } },
+	{	0, 1372, "NAR72",
+		{ { kEv, 186, true }, { kEv, 242, true }, { kEv, 38, false } } },
+	{	0, 1308, "NAR79",
+		{ { kEv, 231, true }, { kEv, 45, false } } }
+},
+{	// Henrik (in lab), 13 responses + 1 repeat
+	{	0, 1450, "NHL50",
+		{ { kEv, 406, true }, { kEv, 185, true }, { kEv, 151, false } } },
+	{	0, 1452, "NHL52",
+		{ { kEv, 401, true }, { kEv, 364, false }, { kEv, 167, false } } },
+	{	0, 1453, "NJR51",
+		{ { kEv, 304, true }, { kEv, 371, false }, { kEv, 149, false } } },
+	{	0, 1454, "NHL54",
+		{ { kEv, 304, true }, { kEv, 361, false }, { kEv, 163, false } } },
+	{	0, 1455, "NHL55",
+		{ { kEv, 175, true }, { kEv, 398, true }, { kEv, 161, false } } },
+	{	0, 1456, "NHL56",
+		{ { kEv, 208, true }, { kEv, 175, false } } },
+	{	0, 1456, "NHL56",
+		{ { kEv, 300, true }, { kEv, 175, false } } },
+	{	0, 1457, "NHL57",
+		{ { kEv, 245, true }, { kEv, 172, false } } },
+	{	0, 1459, "NHL59",
+		{ { kEv, 302, false }, { kEv, 166, false }, { kIn, 5, true } } },
+	{	0, 1460, "NHL60",
+		{ { kEv, 241, true }, { kEv, 171, false } } },
+	{	0, 1462, "NHL62",
+		{ { kEv, 241, true }, { kEv, 156, false } } },
+	{	0, 1464, "NHL64",
+		{ { kEv, 285, true }, { kEv, 157, false } } },
+	{	0, 1465, "NHL65",
+		{ { kEv, 303, true }, { kEv, 176, false } } },
+	{	0, 1466, "NHL66",
+		{ { kEv, 241, true }, { kEv, 159, false } } }
+},
+{	// Henrik (in hospital), 14 responses
+	{	0, 1550, "NHH50",
+		{ { kEv, 307, true }, { kEv, 243, true }, { kEv, 177, false } } },
+	{	0, 1551, "NHH51",
+		{ { kEv, 309, true }, { kEv, 158, false } } },
+	{	0, 1552, "NHH52",
+		{ { kEv, 284, true }, { kEv, 150, false } } },
+	{	0, 1553, "NHH53",
+		{ { kEv, 397, true }, { kEv, 345, false }, { kEv, 169, false } } },
+	{	0, 1554, "NHH54",
+		{ { kEv, 147, true }, { kEv, 374, false } } },
+	{	0, 1555, "NHH55",
+		{ { kEv, 147, true }, { kEv, 374, true }, { kEv, 165, false } } },
+	{	0, 1556, "NHH56",
+		{ { kEv, 311, true }, { kEv, 173, false } } },
+	{	0, 1558, "NHH58",
+		{ { kEv, 420, true }, { kEv, 244, false }, { kEv, 168, false } } },
+	{	0, 1562, "NHH62",
+		{ { kEv, 93, true }, { kEv, 152, false } } },
+	{	0, 1563, "NHH63",
+		{ { kEv, 420, true }, { kEv, 147, true }, { kEv, 170, false } } },
+	{	0, 1565, "NHH65",
+		{ { kEv, 242, true }, { kEv, 153, false } } },
+	{	0, 1567, "NHH67",
+		{ { kEv, 310, true }, { kEv, 39, false }, { kEv, 162, false } } },
+	{	0, 1568, "NHH68",
+		{ { kEv, 307, true }, { kEv, 174, false } } },
+	{	0, 1569, "NSH188",
+		{ { kEv, 403, true }, { kEv, 335, false } } }
+},
+{	// Bess & George, 14 responses + 2 repeats
+	{	0, 1620, "NBG20a",
+		{ { kEv, 232, true }, { kEv, 231, true }, { kEv, 63, false } } },
+	{	0, 1621, "NBG21a",
+		{ { kEv, 246, true }, { kEv, 255, false }, { kEv, 69, false }, { kEv, 67, false } } },
+	{	0, 1623, "NBG23",
+		{ { kEv, 69, true }, { kEv, 247, true }, { kEv, 255, false }, { kEv, 67, false } } },
+	{	0, 1624, "NBG24",
+		{ { kEv, 232, true }, { kEv, 246, true }, { kEv, 71, false }, { kEv, 148, false } } },
+	{	0, 1626, "NBG26",
+		{ { kEv, 75, true }, { kEv, 79, false }, { kEv, 148, false } } },
+	{	0, 1627, "NBG27",
+		{ { kEv, 405, true }, { kEv, 36, true }, { kEv, 147, false }, { kEv, 78, false } } },
+	{	0, 1628, "NBG28",
+		{ { kEv, 40, true }, { kEv, 147, false }, { kEv, 68, false } } },
+	{	0, 1628, "NBG28",
+		{ { kEv, 47, true }, { kEv, 147, false }, { kEv, 68, false } } },
+	{	0, 1628, "NBG28",
+		{ { kEv, 245, true }, { kEv, 147, false }, { kEv, 68, false } } },
+	{	0, 1629, "NBG29",
+		{ { kEv, 41, true }, { kEv, 288, true }, { kEv, 115, false }, { kEv, 388, false }, { kEv, 64, false } } },
+	{	0, 1630, "NBG30",
+		{ { kEv, 420, true }, { kEv, 80, false } } },
+	{	0, 1631, "NBG31",
+		{ { kEv, 120, true }, { kEv, 348, false }, { kEv, 81, false } } },
+	{	0, 1632, "NBG32",
+		{ { kEv, 243, true }, { kEv, 375, false }, { kEv, 76, false } } },
+	{	0, 1633, "NBG33",
+		{ { kEv, 284, true }, { kEv, 77, false } } },
+	{	0, 1670, "NBG70",
+		{ { kEv, 232, true }, { kEv, 73, false }, { kDi, 2, true } } },
+	{	0, 1671, "NBG70",
+		{ { kEv, 232, true }, { kDi, 0, true } } }
+},
+{	// Frank & Jo, 6 responses
+	{	0, 1720, "NFJ20",
+		{ { kEv, 239, true }, { kEv, 165, false }, { kEv, 108, false } } },
+	{	0, 1723, "NFJ23",
+		{ { kEv, 112, true }, { kIn, 4, true } } },
+	{	0, 1724, "NFJ24",
+		{ { kEv, 306, true }, { kEv, 311, true }, { kEv, 340, false }, { kEv, 111, false } } },
+	{	0, 1725, "NFJ25",
+		{ { kEv, 310, true }, { kEv, 334, false }, { kEv, 114, false } } },
+	{	0, 1770, "NFJ70",
+		{ { kEv, 237, true }, { kEv, 109, false } } },
+	{	0, 1771, "NFJ70",
+		{ { kEv, 237, true }, { kEv, 109, true } } }
+}
+};
+
+const Common::Array<Goodbye> _nancy6Goodbyes = {
+	{ "NJR90", { { { 1090, 1092, 1093, 1094, 1095 }, {}, NOFLAG } } },	// Joanna
+	{ "NTA90", { { { 1290, 1291, 1292, 1293 }, {}, NOFLAG } } },		// Taylor
+	{ "NAR90", { { { 1390, 1391, 1393 }, {}, NOFLAG } } },				// Alejandro
+	{ "NHL90", { { { 1490, 1491, 1492, 1493 }, {}, NOFLAG } } },		// Henrik (lab)
+	{ "NHH90", { { { 1590, 1591 }, {}, NOFLAG } } },					// Henrik (hospital)
+	{ "NBG90", { { { 1690, 1691, 1692, 1693, 1694 }, {}, NOFLAG } } },	// Bess & George
+	{ "NFJ90", { { { 1790, 1791, 1792, 1793 }, {}, NOFLAG } } }			// Frank & Jo
+};
+
+const Common::Array<const char *> _nancy6TelephoneRinging = {
+	"ringing...<n><e>", // English
+	"\x46\xf6\xe9\xe2\xf4...  <n><e>" // Russian
+};
+
+const Common::Array<const char *> _nancy6EmptySaveStrings = {
+	"Nothing Saved Here",	// English
+	"- - - - -         "	// Russian
+};
+
+const Common::Array<const char *> _nancy6EventFlagNames = {
+	"EV_Generic0",
+	"EV_Generic1",
+	"EV_Generic2",
+	"EV_Generic3",
+	"EV_Generic4",
+	"EV_Generic5",
+	"EV_Generic6",
+	"EV_Generic7",
+	"EV_Generic8",
+	"EV_Generic9",
+	"EV_Generic10",
+	"EV_Generic11",
+	"EV_Generic12",
+	"EV_Generic13",
+	"EV_Generic14",
+	"EV_Generic15",
+	"EV_Generic16",
+	"EV_Generic17",
+	"EV_Generic18",
+	"EV_Generic19",
+	"EV_Generic20",
+	"EV_Generic21",
+	"EV_Generic22",
+	"EV_Generic23",
+	"EV_Generic24",
+	"EV_Generic25",
+	"EV_Generic26",
+	"EV_Generic27",
+	"EV_Generic28",
+	"EV_Generic29",
+	"EV_Generic30",
+	"EV_TimeForEndgame",
+	"EV_PlayerWonGame",
+	"EV_StopPlayerScrolling",
+	"EV_Easter_Eggs",
+	"EV_AR_Available",
+	"EV_AR_Said_Alibi",
+	"EV_AR_Said_Baseball",
+	"EV_AR_Said_Cinnabar",
+	"EV_AR_Said_Coatl",
+	"EV_AR_Said_Expensive",
+	"EV_AR_Said_Favor",
+	"EV_AR_Said_Nahuatl",
+	"EV_AR_Said_Pillowhead",
+	"EV_AR_Said_Poor_Henrik",
+	"EV_AR_Said_Provenance",
+	"EV_AR_Said_Theft",
+	"EV_AR_Said_Tip",
+	"EV_Assembled_Chaco",
+	"EV_Assembled_Copan",
+	"EV_Assembled_Cortega",
+	"EV_Assembled_Landa",
+	"EV_Assembled_Pacal",
+	"EV_Assembled_Rutherford",
+	"EV_BBall_GameWon",
+	"EV_BBall_High",
+	"EV_BBall_Hoop",
+	"EV_BBall_Low",
+	"EV_BBall_Position1",
+	"EV_BBall_Position2",
+	"EV_BBall_Position3",
+	"EV_BBall_Position4",
+	"EV_BBall_VeryLow",
+	"EV_BG_Said_AR",
+	"EV_BG_Said_Bargain",
+	"EV_BG_Said_Champion",
+	"EV_BG_Said_Difficult",
+	"EV_BG_Said_Eye_Out",
+	"EV_BG_Said_Financial",
+	"EV_BG_Said_Foil",
+	"EV_BG_Said_Hardys",
+	"EV_BG_Said_Monolith",
+	"EV_BG_Said_Mope",
+	"EV_BG_Said_Negative",
+	"EV_BG_Said_Pacal",
+	"EV_BG_Said_Pacal_Theft",
+	"EV_BG_Said_Poppy",
+	"EV_BG_Said_Roadster",
+	"EV_BG_Said_Stranger",
+	"EV_BG_Said_Third_Degree",
+	"EV_BG_Said_Trademark",
+	"EV_BG_Said_Vindicate",
+	"EV_Blue_Said_Message",
+	"EV_Blue_Said_Visiting_Hours",
+	"EV_Calendar_Comment01",
+	"EV_Calendar_Comment02",
+	"EV_Calendar_Comment03",
+	"EV_CalendarInProgress",
+	"EV_Called_Mack",
+	"EV_Called_Silvio",
+	"EV_Card_In_Reader",
+	"EV_Chaco_Package_Available",
+	"EV_Chaco_Said_Foam_Core",
+	"EV_Chaco_Said_Glyph",
+	"EV_Chaco_Said_Henrik_Left",
+	"EV_Chaco_Said_Henrik_Terms",
+	"EV_Chaco_Said_Jade_Carving",
+	"EV_Chaco_Said_North",
+	"EV_Chaco_Said_Photo",
+	"EV_Chaco_Said_Red_Hand",
+	"EV_Chaco_Said_Replica",
+	"EV_Chaco_Said_TS",
+	"EV_Checked_Contract",
+	"EV_Checked_Logograph_Numbers",
+	"EV_Checked_Narrations",
+	"EV_Checked_Pottery",
+	"EV_Disk_In_Drive",
+	"EV_FJ_Said_Ace",
+	"EV_FJ_Said_Amnesia",
+	"EV_FJ_Said_Code",
+	"EV_FJ_Said_Collaborate",
+	"EV_FJ_Said_First_King",
+	"EV_FJ_Said_Glolite",
+	"EV_FJ_Said_Myst",
+	"EV_FJ_Said_Smugglers",
+	"EV_FR_Said_Case_Closed",
+	"EV_FR_Said_Heat_Seeker",
+	"EV_FR_Said_Joanna_Back",
+	"EV_FR_Said_Prudence_Number",
+	"EV_FR_Said_Redeem",
+	"EV_FR_Said_Take_Charge",
+	"EV_FR_Said_Theft_Message",
+	"EV_GB_Said_Trigger",
+	"EV_Ham_Dead",
+	"EV_Ham_Fixed",
+	"EV_Ham_On",
+	"EV_Heard_BL_Message",
+	"EV_Heard_Noise01",
+	"EV_Heard_Noise02",
+	"EV_Heard_Noise03",
+	"EV_Heard_Noise04",
+	"EV_Heard_Noise05",
+	"EV_Heard_Noise06",
+	"EV_Heard_Noise07",
+	"EV_Heard_Noise08",
+	"EV_Heard_Noise09",
+	"EV_Heard_Noise10",
+	"EV_Heard_Noise11",
+	"EV_Heard_Noise12",
+	"EV_Heard_Noise13",
+	"EV_Heard_Noise14",
+	"EV_Heard_Noise15",
+	"EV_Heard_Noise16",
+	"EV_Heard_Noise17",
+	"EV_Heard_Noise18",
+	"EV_Heard_Noise19",
+	"EV_Heard_Noise20",
+	"EV_HH_Confessed_Theft",
+	"EV_HH_Konk",
+	"EV_HH_Said_Addenda",
+	"EV_HH_Said_Afterlife",
+	"EV_HH_Said_Chaco1",
+	"EV_HH_Said_Chaco2",
+	"EV_HH_Said_Cinnabar",
+	"EV_HH_Said_Coatimundi",
+	"EV_HH_Said_Copan",
+	"EV_HH_Said_Guesswork",
+	"EV_HH_Said_Ham1",
+	"EV_HH_Said_Ham2",
+	"EV_HH_Said_Homebase",
+	"EV_HH_Said_Hurricane",
+	"EV_HH_Said_Logograph_Numbers",
+	"EV_HH_Said_Nahuatl",
+	"EV_HH_Said_Narrations",
+	"EV_HH_Said_Oozing_Hives",
+	"EV_HH_Said_Pacal_Tomb",
+	"EV_HH_Said_Password",
+	"EV_HH_Said_Pot",
+	"EV_HH_Said_Prudence",
+	"EV_HH_Said_Quiz",
+	"EV_HH_Said_Red_Hand",
+	"EV_HH_Said_Riddle",
+	"EV_HH_Said_Silvio",
+	"EV_HH_Said_Six_Keys",
+	"EV_HH_Said_Smuggling",
+	"EV_HH_Said_Sonny",
+	"EV_HH_Said_SpectroX",
+	"EV_HH_Said_TS",
+	"EV_HH_Saw_Pot",
+	"EV_HHH_Available",
+	"EV_HHL_Available",
+	"EV_Insulted_PR",
+	"EV_JR_Available",
+	"EV_JR_Returned",
+	"EV_JR_Said_Addenda",
+	"EV_JR_Said_Chaco",
+	"EV_JR_Said_Cinnabar",
+	"EV_JR_Said_Coatimundi",
+	"EV_JR_Said_Curator",
+	"EV_JR_Said_Fish_Food",
+	"EV_JR_Said_Giddyup",
+	"EV_JR_Said_Handprint",
+	"EV_JR_Said_Investigate",
+	"EV_JR_Said_Landa_Code",
+	"EV_JR_Said_Logograph_Numbers",
+	"EV_JR_Said_Magician",
+	"EV_JR_Said_Monolith",
+	"EV_JR_Said_Nahuatl",
+	"EV_JR_Said_Narrations",
+	"EV_JR_Said_Nightmare",
+	"EV_JR_Said_No_Henrik",
+	"EV_JR_Said_No_Photo",
+	"EV_JR_Said_Pottery",
+	"EV_JR_Said_Prudence",
+	"EV_JR_Said_Security",
+	"EV_JR_Said_Stealing",
+	"EV_JR_Said_Suspended_Message",
+	"EV_JR_Said_Tasklist",
+	"EV_JR_Said_Tempest",
+	"EV_JR_Said_Temple",
+	"EV_JR_Said_Unpack",
+	"EV_JR_Said_What_Next",
+	"EV_JR_Said_Yellow_Death",
+	"EV_JR_Suspended",
+	"EV_KeyPuz_Tried_InHole",
+	"EV_KeyPuz_Tried_Turn",
+	"EV_KeyPuzCurrTryE",
+	"EV_KeyPuzCurrTryN",
+	"EV_KeyPuzCurrTryS",
+	"EV_KeyPuzCurrTryW",
+	"EV_KeyPuzSideE",
+	"EV_KeyPuzSideN",
+	"EV_KeyPuzSideS",
+	"EV_KeyPuzSideW",
+	"EV_Mack_Said_At_Lunch",
+	"EV_Mack_Said_Distributor",
+	"EV_Mack_Said_Joanna",
+	"EV_Mack_Said_Last_Week",
+	"EV_Mack_Said_No_Prob",
+	"EV_Mack_Said_Pick_Up",
+	"EV_Mack_Said_Sulfide",
+	"EV_Met_AR",
+	"EV_Met_BG",
+	"EV_Met_BL",
+	"EV_Met_Blue",
+	"EV_Met_Chaco",
+	"EV_Met_Daddle",
+	"EV_Met_FJ",
+	"EV_Met_FR",
+	"EV_Met_HH_Hospital",
+	"EV_Met_HH_Lab",
+	"EV_Met_JR",
+	"EV_Met_Mack",
+	"EV_Met_Poppy",
+	"EV_Met_Prudence",
+	"EV_Met_Silvio",
+	"EV_Met_TS_Monolith",
+	"EV_Met_TS_Office",
+	"EV_Mold_Set",
+	"EV_Mold_Setting",
+	"EV_ND_Said_Pwd",
+	"EV_Opened_Chaco_Package",
+	"EV_Opened_Drawer",
+	"EV_Opened_Rutherford_Package",
+	"EV_Opened_Smuggler_Package",
+	"EV_Pacal_Theft",
+	"EV_PD_Said_Art",
+	"EV_PD_Said_Carving",
+	"EV_PD_Said_Cookie",
+	"EV_Placed_Calendar",
+	"EV_Placed_Knob",
+	"EV_Placed_LogoA",
+	"EV_Placed_LogoB",
+	"EV_Placed_Pacal_Photo",
+	"EV_Placed_PotteryA",
+	"EV_Placed_PotteryB",
+	"EV_Placed_Riddle",
+	"EV_Placed_Threat",
+	"EV_PR_Said_Babble",
+	"EV_PR_Said_Red_Hand",
+	"EV_PR_Said_Send_Copy",
+	"EV_Prudence_Ready",
+	"EV_Rutherford_Package_Available",
+	"EV_Said_Air01",
+	"EV_Said_Air02",
+	"EV_Said_Air03",
+	"EV_Said_Art",
+	"EV_Said_Light01",
+	"EV_Said_Light02",
+	"EV_Said_Logo1",
+	"EV_Saw_CCCC_Number",
+	"EV_Saw_Contacts",
+	"EV_Saw_Donor_Plaque",
+	"EV_Saw_Exhibit",
+	"EV_Saw_Exhibit_Burial_Customs",
+	"EV_Saw_Ham",
+	"EV_Saw_HH_Note",
+	"EV_Saw_JR_Alarm_Cine",
+	"EV_Saw_JR_Gone_Note",
+	"EV_Saw_Koko",
+	"EV_Saw_Landa_List",
+	"EV_Saw_Level3_Quiz",
+	"EV_Saw_Locked_Tube",
+	"EV_Saw_Pacal_Theft",
+	"EV_Saw_Periodic_Table",
+	"EV_Saw_Provenance_Docs",
+	"EV_Saw_Red_Hand",
+	"EV_Saw_Riddle",
+	"EV_Saw_Rutherford_Article",
+	"EV_Saw_Smuggler_Codes",
+	"EV_Saw_Sonny",
+	"EV_Saw_Sonny_Logo_Notes",
+	"EV_Saw_Sonny_Notes",
+	"EV_Saw_SpectroX",
+	"EV_Saw_Tasklist",
+	"EV_Saw_Temple",
+	"EV_Saw_Translation",
+	"EV_Saw_Travel_Notes",
+	"EV_Saw_Zap",
+	"EV_Saw_Zap_Contacts",
+	"EV_Saw_Zap_Honduras",
+	"EV_Saw_Zap_Key_Notes",
+	"EV_Saw_Zap_North",
+	"EV_Saw_Zap_South",
+	"EV_Saw_Zap_West",
+	"EV_Silvio_Said_Beat_It",
+	"EV_Smuggler_Package_Available",
+	"EV_Solved_Basketball",
+	"EV_Solved_Bul",
+	"EV_Solved_Calendar",
+	"EV_Solved_Cinnabar",
+	"EV_Solved_Elements",
+	"EV_Solved_Eye",
+	"EV_Solved_God_Match",
+	"EV_Solved_God_MatchA",
+	"EV_Solved_God_MatchB",
+	"EV_Solved_God_MatchC",
+	"EV_Solved_God_MatchD",
+	"EV_Solved_God_MatchE",
+	"EV_Solved_God_MatchF",
+	"EV_Solved_God_MatchG",
+	"EV_Solved_God_MatchH",
+	"EV_Solved_God_MatchI",
+	"EV_Solved_God_MatchJ",
+	"EV_Solved_Ham",
+	"EV_Solved_HH_Pwd",
+	"EV_Solved_KeyPuzE",
+	"EV_Solved_KeyPuzN",
+	"EV_Solved_KeyPuzS",
+	"EV_Solved_KeyPuzW",
+	"EV_Solved_Lamat",
+	"EV_Solved_Landa_Code",
+	"EV_Solved_Leche",
+	"EV_Solved_Level1",
+	"EV_Solved_Level2",
+	"EV_Solved_Level3",
+	"EV_Solved_Logograph_Match",
+	"EV_Solved_Logograph_Numbers",
+	"EV_Solved_LogographA",
+	"EV_Solved_LogographB",
+	"EV_Solved_LogographC",
+	"EV_Solved_LogographD",
+	"EV_Solved_LogographE",
+	"EV_Solved_LogographF",
+	"EV_Solved_LogographG",
+	"EV_Solved_LogographH",
+	"EV_Solved_LogographI",
+	"EV_Solved_LogographJ",
+	"EV_Solved_Maze",
+	"EV_Solved_Mercury",
+	"EV_Solved_Mold",
+	"EV_Solved_Narrations",
+	"EV_Solved_Nightmare",
+	"EV_Solved_Number_Channel",
+	"EV_Solved_Pottery_Task",
+	"EV_Solved_Smuggler_Station",
+	"EV_Solved_Sonny_Pwd",
+	"EV_Solved_SpectroX",
+	"EV_Solved_Sulphur",
+	"EV_Spectro_On",
+	"EV_Tasks_Done",
+	"EV_Took_Addenda",
+	"EV_Took_Calendar",
+	"EV_Took_Cookie",
+	"EV_Took_Copan",
+	"EV_Took_Cortega",
+	"EV_Took_Diskette",
+	"EV_Took_Jade",
+	"EV_Took_Key_Rutherford",
+	"EV_Took_Knob",
+	"EV_Took_Landa",
+	"EV_Took_LogoA",
+	"EV_Took_LogoB",
+	"EV_Took_Mold",
+	"EV_Took_Pacal_Carving",
+	"EV_Took_Passport",
+	"EV_Took_PotteryA",
+	"EV_Took_PotteryB",
+	"EV_Took_Provenance",
+	"EV_Took_Riddle",
+	"EV_Took_Rutherford_Key",
+	"EV_Took_Threat",
+	"EV_Took_Tube",
+	"EV_Took_Zap",
+	"EV_Tried_Cabinet",
+	"EV_Tried_God_Match",
+	"EV_Tried_HH_Pwd",
+	"EV_Tried_Level3",
+	"EV_Tried_Logograph_Numbers",
+	"EV_Tried_Maze",
+	"EV_Tried_Narrations",
+	"EV_Tried_Pottery_Task",
+	"EV_Tried_Sonny_Pwd",
+	"EV_Tried_Zap",
+	"EV_TS_Available",
+	"EV_TS_Said_Alibi",
+	"EV_TS_Said_Appraised",
+	"EV_TS_Said_Bully",
+	"EV_TS_Said_Coatimundi",
+	"EV_TS_Said_Commission",
+	"EV_TS_Said_Cookie",
+	"EV_TS_Said_Creative",
+	"EV_TS_Said_Functions",
+	"EV_TS_Said_Nahuatl",
+	"EV_TS_Said_Office",
+	"EV_TS_Said_Photo",
+	"EV_TS_Said_Pig",
+	"EV_TS_Said_Poor_Henrik",
+	"EV_TS_Said_Poppy",
+	"EV_TS_Said_Prudence",
+	"EV_TS_Said_Red_Hand",
+	"EV_TS_Said_Scoundrel",
+	"EV_TS_Said_Sick",
+	"EV_TS_Said_Socks",
+	"EV_TS_Said_Yellow_Death",
+	"EV_Zap_In_Drive",
+	"EV_Empty1426",
+	"EV_Empty1427",
+	"EV_Empty1428",
+	"EV_Empty1429",
+	"EV_Empty1430",
+	"EV_Empty1431",
+	"EV_Empty1432",
+	"EV_Empty1433",
+	"EV_Empty1434",
+	"EV_Empty1435",
+	"EV_Empty1436",
+	"EV_Empty1437",
+	"EV_Empty1438",
+	"EV_Empty1439",
+	"EV_Empty1440",
+	"EV_Empty1441",
+	"EV_Empty1442",
+	"EV_Empty1443",
+	"EV_Empty1444",
+	"EV_Empty1445",
+	"EV_Empty1446",
+	"EV_Empty1447",
+	"EV_Empty1448",
+	"EV_Empty1449",
+	"EV_Empty1450",
+	"EV_Empty1451",
+	"EV_Empty1452",
+	"EV_Empty1453",
+	"EV_Empty1454",
+	"EV_TBD"
+};
+
+#endif // NANCY6DATA_H
diff --git a/devtools/create_nancy/types.h b/devtools/create_nancy/types.h
index 97e9d06c048..7f87451531c 100644
--- a/devtools/create_nancy/types.h
+++ b/devtools/create_nancy/types.h
@@ -54,6 +54,9 @@ struct SceneChangeDescription {
 	bool doNotStartSound;
 };
 
+// Note: in nancy6 and above, the textID field is ignored since all dialogue strings are bundled
+// inside the CONVO file's CVTX chunk (thus nancy.dat doesn't include any).
+// Instead, the soundID doubles as the key for the HashMap containing the CONVO data.
 struct ConditionalDialogue {
 	byte textID;
 	uint16 sceneID;


Commit: d3864e106dbc42b649ddffd5e6c77f5aa842244f
    https://github.com/scummvm/scummvm/commit/d3864e106dbc42b649ddffd5e6c77f5aa842244f
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T13:14:52+03:00

Commit Message:
NANCY: Add support for nancy6 static data

Nancy6 data inside nancy.dat doesn't contain any strings,
since those are now found inside the CONVO file.

Changed paths:
    engines/nancy/action/conversation.cpp
    engines/nancy/commontypes.cpp


diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index e23fcecad64..993230bc09f 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -334,7 +334,18 @@ void ConversationSound::addConditionalDialogue() {
 			_responses.push_back(ResponseStruct());
 			ResponseStruct &newResponse = _responses.back();
 			newResponse.soundName = res.soundID;
-			newResponse.text = g_nancy->getStaticData().conditionalDialogueTexts[res.textID];
+
+			if (g_nancy->getGameType() <= kGameTypeNancy5) {
+				// String is also inside nancy.dat
+				newResponse.text = g_nancy->getStaticData().conditionalDialogueTexts[res.textID];
+			} else {
+				// String is inside the CVTX chunk in the CONVO file. Sound ID doubles as string key
+				const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
+				assert(convo);
+
+				newResponse.text = convo->texts[res.soundID];
+			}
+			
 			newResponse.sceneChange.sceneID = res.sceneID;
 			newResponse.sceneChange.continueSceneSound = kContinueSceneSound;
 		}
@@ -346,7 +357,17 @@ void ConversationSound::addGoodbye() {
 	_responses.push_back(ResponseStruct());
 	ResponseStruct &newResponse = _responses.back();
 	newResponse.soundName = res.soundID;
-	newResponse.text = g_nancy->getStaticData().goodbyeTexts[_goodbyeResponseCharacterID];
+
+	if (g_nancy->getGameType() <= kGameTypeNancy5) {
+		// String is also inside nancy.dat
+		newResponse.text = g_nancy->getStaticData().goodbyeTexts[_goodbyeResponseCharacterID];
+	} else {
+		// String is inside the CVTX chunk in the CONVO file. Sound ID doubles as string key
+		const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
+		assert(convo);
+
+		newResponse.text = convo->texts[res.soundID];
+	}
 
 	// Evaluate conditions to pick from the collection of replies
 	uint sceneChangeID = 0;
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index 02388c97996..a3a685d6e8a 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -388,6 +388,19 @@ void StaticData::readData(Common::SeekableReadStream &stream, Common::Language l
 				stream.seek(endOffset);
 			}
 
+			break;
+		case MKTAG('C', 'D', 'L', '2') :
+			// Conditional dialogue, no strings (nancy6 and up)
+			num = stream.readUint16LE();
+			conditionalDialogue.resize(num);
+			for (uint16 i = 0; i < num; ++i) {
+				uint16 num2 = stream.readUint16LE();
+				conditionalDialogue[i].resize(num2);
+				for (uint j = 0; j < num2; ++j) {
+					conditionalDialogue[i][j].readData(stream);
+				}
+			}
+
 			break;
 		case MKTAG('G', 'D', 'B', 'Y') :
 			// Goodbyes
@@ -411,6 +424,15 @@ void StaticData::readData(Common::SeekableReadStream &stream, Common::Language l
 				stream.seek(endOffset);
 			}
 
+			break;
+		case MKTAG('G', 'D', 'B', '2') :
+			// Goodbyes, no strings (nancy6 and up)
+			num = stream.readUint16LE();
+			goodbyes.resize(num);
+			for (uint16 i = 0; i < num; ++i) {
+				goodbyes[i].readData(stream);
+			}
+
 			break;
 		case MKTAG('H', 'I', 'N', 'T') : {
 			// Hints (nancy1 only)


Commit: 555de022cb792e7a6c00b2ea9c9026abe38624c8
    https://github.com/scummvm/scummvm/commit/555de022cb792e7a6c00b2ea9c9026abe38624c8
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T13:14:52+03:00

Commit Message:
NANCY: Support nancy6 cursors

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/navigationrecords.h
    engines/nancy/action/puzzle/collisionpuzzle.cpp
    engines/nancy/cursor.cpp
    engines/nancy/cursor.h
    engines/nancy/ui/viewport.cpp


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index bd1907cf026..1e870fb7ea7 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -90,9 +90,9 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
 			return new HotMultiframeSceneChange(CursorManager::kMoveDown);
 		}
 	case 22:
-		return new Hot1FrSceneChange(CursorManager::kTurnLeft);
+		return new Hot1FrSceneChange(CursorManager::kMoveLeft);
 	case 23:
-		return new Hot1FrSceneChange(CursorManager::kTurnRight);
+		return new Hot1FrSceneChange(CursorManager::kMoveRight);
 	case 24:
 		return new HotMultiframeMultisceneCursorTypeSceneChange();
 	case 40:
diff --git a/engines/nancy/action/navigationrecords.h b/engines/nancy/action/navigationrecords.h
index d02f9b1435a..1d1b33df51b 100644
--- a/engines/nancy/action/navigationrecords.h
+++ b/engines/nancy/action/navigationrecords.h
@@ -92,9 +92,9 @@ protected:
 			return "Hot1FrUpSceneChange";
 		case CursorManager::kMoveDown:
 			return "Hot1FrDownSceneChange";
-		case CursorManager::kTurnLeft:
+		case CursorManager::kMoveLeft:
 			return "Hot1FrLeftSceneChange";
-		case CursorManager::kTurnRight:
+		case CursorManager::kMoveRight:
 			return "Hot1FrUpSceneChange";
 		default:
 			return "Hot1FrSceneChange";
diff --git a/engines/nancy/action/puzzle/collisionpuzzle.cpp b/engines/nancy/action/puzzle/collisionpuzzle.cpp
index 5955eaadf39..50a484ffdcc 100644
--- a/engines/nancy/action/puzzle/collisionpuzzle.cpp
+++ b/engines/nancy/action/puzzle/collisionpuzzle.cpp
@@ -566,7 +566,7 @@ void CollisionPuzzle::handleInput(NancyInput &input) {
 			if (left.contains(input.mousePos)) {
 				checkPos = movePiece(i, kWallLeft);
 				if (checkPos != _pieces[i]._gridPos) {
-					g_nancy->_cursorManager->setCursorType(CursorManager::kTurnLeft);
+					g_nancy->_cursorManager->setCursorType(CursorManager::kMoveLeft);
 
 					if (input.input & NancyInput::kLeftMouseButtonUp) {
 						_lastPosition = _pieces[i]._gridPos;
@@ -584,7 +584,7 @@ void CollisionPuzzle::handleInput(NancyInput &input) {
 			if (right.contains(input.mousePos)) {
 				checkPos = movePiece(i, kWallRight);
 				if (checkPos != _pieces[i]._gridPos) {
-					g_nancy->_cursorManager->setCursorType(CursorManager::kTurnRight);
+					g_nancy->_cursorManager->setCursorType(CursorManager::kMoveRight);
 
 					if (input.input & NancyInput::kLeftMouseButtonUp) {
 						_lastPosition = _pieces[i]._gridPos;
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index dabab50cd40..d977d3689a0 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -120,8 +120,10 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
 			_curCursorID = 5;
 		} else if (gameType ==  kGameTypeNancy3) {
 			_curCursorID = 8;
-		} else {
+		} else if (gameType <= kGameTypeNancy5) {
 			_curCursorID = 12;
+		} else {
+			_curCursorID = 16;
 		}
 
 		return;
@@ -130,27 +132,45 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
 			_curCursorID = 5;
 		} else if (gameType == kGameTypeNancy2) {
 			_curCursorID = 6;
-		} else if (gameType ==  kGameTypeNancy3) {
+		} else if (gameType == kGameTypeNancy3) {
 			_curCursorID = 9;
-		} else {
+		} else if (gameType <= kGameTypeNancy5) {
 			_curCursorID = 13;
+		} else {
+			_curCursorID = 17;
 		}
 
 		return;
-	case kTurnLeft:
+	case kRotateLeft:
+		// Only valid for nancy6 and up
+		if (gameType >= kGameTypeNancy6) {
+			_curCursorID = kRotateLeft;
+			return;
+		}
+
+		// fall through
+	case kMoveLeft:
 		// Only valid for nancy3 and up
 		if (gameType >= kGameTypeNancy3) {
-			_curCursorID = kTurnLeft;
+			_curCursorID = kMoveLeft;
 			return;
 		} else {
 			type = kMove;
 		}
 
 		break;
-	case kTurnRight:
+	case kRotateRight:
+		// Only valid for nancy6 and up
+		if (gameType >= kGameTypeNancy6) {
+			_curCursorID = kRotateRight;
+			return;
+		}
+
+		// fall through
+	case kMoveRight:
 		// Only valid for nancy3 and up
 		if (gameType >= kGameTypeNancy3) {
-			_curCursorID = kTurnRight;
+			_curCursorID = kMoveRight;
 			return;
 		} else {
 			type = kMove;
@@ -185,6 +205,14 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
 		}
 
 		break;
+	case kSwivelLeft:
+		// Only valid for nancy6 and up, but we don't need a check for now
+		_curCursorID = kSwivelLeft;
+		return;
+	case kSwivelRight:
+		// Only valid for nancy6 and up, but we don't need a check for now
+		_curCursorID = kSwivelRight;
+		return;
 	default:
 		break;
 	}
@@ -224,26 +252,14 @@ void CursorManager::applyCursor() {
 		surf = &g_nancy->_graphicsManager->_object0;
 	}
 
-	// Create a temporary surface to hold the cursor since giving replaceCursor() a pointer
-	// to the original surface results in garbage. This also makes it so we don't have to deal
-	// with TVD's palettes
-	Graphics::ManagedSurface temp;
-	temp.create(bounds.width(), bounds.height(), g_nancy->_graphicsManager->getScreenPixelFormat());
-	temp.blitFrom(*surf, bounds, Common::Point());
+	Graphics::ManagedSurface temp(*surf, bounds);
 
-	// Convert the trans color from the original format to the screen format
-	uint transColor;
+	CursorMan.replaceCursor(temp, hotspot.x, hotspot.y, g_nancy->_graphicsManager->getTransColor(), false);
 	if (g_nancy->getGameType() == kGameTypeVampire) {
-		uint8 palette[1 * 3];
-		surf->grabPalette(palette, 1, 1);
-		transColor = temp.format.RGBToColor(palette[0], palette[1], palette[2]);
-	} else {
-		uint8 r, g, b;
-		surf->format.colorToRGB(g_nancy->_graphicsManager->getTransColor(), r, g, b);
-		transColor = temp.format.RGBToColor(r, g, b);
+		byte palette[3 * 256];
+		surf->grabPalette(palette, 0, 256);
+		CursorMan.replaceCursorPalette(palette, 0, 256);
 	}
-
-	CursorMan.replaceCursor(temp, hotspot.x, hotspot.y, transColor, false);
 }
 
 void CursorManager::showCursor(bool shouldShow) {
diff --git a/engines/nancy/cursor.h b/engines/nancy/cursor.h
index 334b9bb667d..03558d12135 100644
--- a/engines/nancy/cursor.h
+++ b/engines/nancy/cursor.h
@@ -35,18 +35,22 @@ class CursorManager {
 
 public:
 	enum CursorType {
-		kNormal 		= 0,
-		kHotspot 		= 1,
-		kMove 			= 2,
-		kExit 			= 3,
-		kRotateCW 		= 4,
-		kRotateCCW 		= 5,
-		kTurnLeft 		= 6,
-		kTurnRight 		= 7,
-		kMoveForward	= 8,
-		kMoveBackward	= 9,
-		kMoveUp			= 10,
-		kMoveDown		= 11,
+		kNormal 		= 0, // Eyeglass (except in TVD), non-highlighted
+		kHotspot 		= 1, // Eyeglass (except in TVD), highlighted
+		kMove 			= 2, // Used for movement in early games
+		kExit 			= 3, // Used for movement, some games use it for exiting puzzles
+		kRotateCW 		= 4, // Used in puzzles only
+		kRotateCCW 		= 5, // Used in puzzles only
+		kMoveLeft 		= 6, // Used for movement, some games used it for turning in 360 scenes
+		kMoveRight 		= 7, // Used for movement, some games used it for turning in 360 scenes
+		kMoveForward	= 8, // Used for movement
+		kMoveBackward	= 9, // Used for movement, some games use it for exiting puzzles
+		kMoveUp			= 10, // Used for movement
+		kMoveDown		= 11, // Used for movement
+		kRotateLeft		= 12, // Used in 360 scenes in nancy6 and up
+		kRotateRight	= 13, // Used in 360 scenes in nancy6 and up
+		kSwivelLeft		= 14, // Not sure where this is used, subject to renaming
+		kSwivelRight	= 15, // Not sure where this is used, subject to renaming
 		kNormalArrow,
 		kHotspotArrow
 	};
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 31922edc9aa..c25570328fd 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -121,9 +121,9 @@ void Viewport::handleInput(NancyInput &input) {
 
 	if (direction) {
 		if (direction & kLeft) {
-			g_nancy->_cursorManager->setCursorType(CursorManager::kTurnLeft);
+			g_nancy->_cursorManager->setCursorType(CursorManager::kRotateLeft);
 		} else if (direction & kRight) {
-			g_nancy->_cursorManager->setCursorType(CursorManager::kTurnRight);
+			g_nancy->_cursorManager->setCursorType(CursorManager::kRotateRight);
 		} else if (direction & kUp) {
 			g_nancy->_cursorManager->setCursorType(CursorManager::kMoveUp);
 		} else if (direction & kDown) {


Commit: 11f6f9ec51d525a799ef5e3bd8cdde45011f5528
    https://github.com/scummvm/scummvm/commit/11f6f9ec51d525a799ef5e3bd8cdde45011f5528
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T13:14:52+03:00

Commit Message:
NANCY: Implement nancy4 SafeDialPuzzle

Added support for nancy4's sextant puzzle, which uses
a modified SafeDialPuzzle action record. Also renamed the
record, since it was erroneously named SafeLockPuzzle
instead of its actual name.

Changed paths:
  A engines/nancy/action/puzzle/safedialpuzzle.cpp
  A engines/nancy/action/puzzle/safedialpuzzle.h
  R engines/nancy/action/puzzle/safelockpuzzle.cpp
  R engines/nancy/action/puzzle/safelockpuzzle.h
    engines/nancy/action/arfactory.cpp
    engines/nancy/module.mk


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 1e870fb7ea7..fdb1972dc36 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -39,7 +39,7 @@
 #include "engines/nancy/action/puzzle/riddlepuzzle.h"
 #include "engines/nancy/action/puzzle/rippedletterpuzzle.h"
 #include "engines/nancy/action/puzzle/rotatinglockpuzzle.h"
-#include "engines/nancy/action/puzzle/safelockpuzzle.h"
+#include "engines/nancy/action/puzzle/safedialpuzzle.h"
 #include "engines/nancy/action/puzzle/setplayerclock.h"
 #include "engines/nancy/action/puzzle/sliderpuzzle.h"
 #include "engines/nancy/action/puzzle/soundequalizerpuzzle.h"
@@ -222,7 +222,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
 	case 209:
 		return new TurningPuzzle();
 	case 210:
-		return new SafeLockPuzzle();
+		return new SafeDialPuzzle();
 	case 211:
 		return new CollisionPuzzle(CollisionPuzzle::PuzzleType::kCollision);
 	case 212:
diff --git a/engines/nancy/action/puzzle/safelockpuzzle.cpp b/engines/nancy/action/puzzle/safedialpuzzle.cpp
similarity index 78%
rename from engines/nancy/action/puzzle/safelockpuzzle.cpp
rename to engines/nancy/action/puzzle/safedialpuzzle.cpp
index 4927bb95266..f117b97d761 100644
--- a/engines/nancy/action/puzzle/safelockpuzzle.cpp
+++ b/engines/nancy/action/puzzle/safedialpuzzle.cpp
@@ -28,14 +28,14 @@
 #include "engines/nancy/input.h"
 #include "engines/nancy/util.h"
 
-#include "engines/nancy/action/puzzle/safelockpuzzle.h"
+#include "engines/nancy/action/puzzle/safedialpuzzle.h"
 
 #include "engines/nancy/state/scene.h"
 
 namespace Nancy {
 namespace Action {
 
-void SafeLockPuzzle::init() {
+void SafeDialPuzzle::init() {
 	g_nancy->_resource->loadImage(_imageName1, _image1);
 	g_nancy->_resource->loadImage(_imageName2, _image2);
 	g_nancy->_resource->loadImage(_resetImageName, _resetImage);
@@ -50,7 +50,7 @@ void SafeLockPuzzle::init() {
 	registerGraphics();
 }
 
-void SafeLockPuzzle::updateGraphics() {
+void SafeDialPuzzle::updateGraphics() {
 	if (_animState == kSelect && (_state == kActionTrigger ? _nextAnim - 500 : _nextAnim) < g_nancy->getTotalPlayTime()) {
 		_drawSurface.fillRect(_arrowDest, _drawSurface.getTransparentColor());
 		_animState = kNone;
@@ -58,12 +58,19 @@ void SafeLockPuzzle::updateGraphics() {
 	}
 
 	if (_animState == kSpin && _nextAnim < g_nancy->getTotalPlayTime()) {
-		drawDialFrame(_current * 2);
+		drawDialFrame(_current * (1 + _numInbetweens));
 		_animState = kNone;
 	}
 
 	if (_animState == kReset && _nextAnim < g_nancy->getTotalPlayTime()) {
-		_animState = kResetAnim;
+		if (_resetImageName.size()) {
+			_animState = kResetAnim;
+		} else {
+			_animState = kNone;
+			_current = 0;
+			drawDialFrame(_current);
+		}
+
 		g_nancy->_sound->playSound(_resetSound);
 	}
 
@@ -74,19 +81,25 @@ void SafeLockPuzzle::updateGraphics() {
 		if (_current >= _resetDialSrcs.size() * _resetTurns) {
 			_animState = kNone;
 			_current = 0;
-			drawDialFrame(_current * 2);
+			drawDialFrame(_current);
 		}
 		_needsRedraw = true;
 	}
 }
 
-void SafeLockPuzzle::readData(Common::SeekableReadStream &stream) {
+void SafeDialPuzzle::readData(Common::SeekableReadStream &stream) {
 	readFilename(stream, _imageName1);
 	readFilename(stream, _imageName2);
 	readFilename(stream, _resetImageName);
 
+	uint16 num = 20;
+	if (g_nancy->getGameType() >= kGameTypeNancy4) {
+		num = stream.readUint16LE();
+		_enableWraparound = stream.readByte();
+	}
+
 	readRect(stream, _dialDest);
-	readRectArray(stream, _dialSrcs, 20);
+	readRectArray(stream, _dialSrcs, num, 20);
 
 	readRect(stream, _resetDest);
 	readRect(stream, _resetSrc);
@@ -104,8 +117,15 @@ void SafeLockPuzzle::readData(Common::SeekableReadStream &stream) {
 	}
 	stream.skip((10 - solveSize) * 2);
 
-	readRect(stream, _ccwHotspot);
-	readRect(stream, _cwHotspot);
+	if (g_nancy->getGameType() >= kGameTypeNancy4) {
+		readRect(stream, _cwHotspot); // swapped order
+		readRect(stream, _ccwHotspot);
+		_useMoveArrows = stream.readByte();
+		_numInbetweens = 0;
+	} else {
+		readRect(stream, _ccwHotspot);
+		readRect(stream, _cwHotspot);
+	}
 
 	_spinSound.readNormal(stream);
 	_selectSound.readNormal(stream);
@@ -119,13 +139,15 @@ void SafeLockPuzzle::readData(Common::SeekableReadStream &stream) {
 	readRect(stream, _exitHotspot);
 }
 
-void SafeLockPuzzle::execute() {
+void SafeDialPuzzle::execute() {
 	switch (_state) {
 	case kBegin :
 		init();
 		g_nancy->_sound->loadSound(_spinSound);
 		g_nancy->_sound->loadSound(_selectSound);
 		g_nancy->_sound->loadSound(_resetSound);
+		_current = 0;
+		drawDialFrame(_current);
 		_state = kRun;
 		// fall through
 	case kRun :
@@ -169,7 +191,7 @@ void SafeLockPuzzle::execute() {
 	}
 }
 
-void SafeLockPuzzle::handleInput(NancyInput &input) {
+void SafeDialPuzzle::handleInput(NancyInput &input) {
 	if (_state != kRun || _playerSequence == _correctSequence) {
 		return;
 	}
@@ -183,17 +205,21 @@ void SafeLockPuzzle::handleInput(NancyInput &input) {
 
 		return;
 	} else if (NancySceneState.getViewport().convertViewportToScreen(_ccwHotspot).contains(input.mousePos)) {
-		g_nancy->_cursorManager->setCursorType(CursorManager::kRotateCCW);
+		if (!_enableWraparound && _current == 0) {
+			return;
+		}
+
+		g_nancy->_cursorManager->setCursorType(_useMoveArrows ? CursorManager::kMoveLeft : CursorManager::kRotateCCW);
 
 		if (input.input & NancyInput::kLeftMouseButtonUp && _nextAnim < g_nancy->getTotalPlayTime() &&
 				_animState != kReset && _animState != kResetAnim) {
 			if (_current == 0) {
-				_current = _dialSrcs.size() / 2 - 1;
+				_current = _dialSrcs.size() / (1 + _numInbetweens) - 1;
 			} else {
 				--_current;
 			}
 
-			drawDialFrame(_current * 2 + 1);
+			drawDialFrame(_current * (1 + _numInbetweens) + (_numInbetweens ? 1 : 0));
 			_nextAnim = g_nancy->getTotalPlayTime() + 250; // hardcoded
 
 			g_nancy->_sound->playSound(_spinSound);
@@ -202,14 +228,18 @@ void SafeLockPuzzle::handleInput(NancyInput &input) {
 
 		return;
 	} else if (NancySceneState.getViewport().convertViewportToScreen(_cwHotspot).contains(input.mousePos)) {
-		g_nancy->_cursorManager->setCursorType(CursorManager::kRotateCW);
+		if (!_enableWraparound && _current == (_dialSrcs.size() / (1 + _numInbetweens) - 1)) {
+			return;
+		}
+
+		g_nancy->_cursorManager->setCursorType(_useMoveArrows ? CursorManager::kMoveRight : CursorManager::kRotateCW);
 
 		if (input.input & NancyInput::kLeftMouseButtonUp && _nextAnim < g_nancy->getTotalPlayTime() &&
 				_animState != kReset && _animState != kResetAnim) {
-			drawDialFrame(_current * 2 + 1);
+			drawDialFrame(_current * (1 + _numInbetweens) + 1);
 			_nextAnim = g_nancy->getTotalPlayTime() + 250; // hardcoded
 
-			if (_current == (_dialSrcs.size() / 2) - 1) {
+			if (_current == (_dialSrcs.size() / (1 + _numInbetweens)) - 1) {
 				_current = 0;
 			} else {
 				++_current;
@@ -256,9 +286,9 @@ void SafeLockPuzzle::handleInput(NancyInput &input) {
 	}	
 }
 
-void SafeLockPuzzle::drawDialFrame(uint frame) {
+void SafeDialPuzzle::drawDialFrame(uint frame) {
 	debug("%u", frame);
-	if (frame >= _dialSrcs.size() / 2) {
+	if (frame >= _dialSrcs.size() / 2 && _imageName2.size()) {
 		_drawSurface.blitFrom(_image2, _dialSrcs[frame], _dialDest);
 	} else {
 		_drawSurface.blitFrom(_image1, _dialSrcs[frame], _dialDest);
@@ -267,10 +297,10 @@ void SafeLockPuzzle::drawDialFrame(uint frame) {
 	_needsRedraw = true;
 }
 
-void SafeLockPuzzle::pushSequence(uint id) {
-	if (id != 0) {
-		// The ids in the correct sequence are in reverse order
-		id = (_dialSrcs.size() / 2) - id;
+void SafeDialPuzzle::pushSequence(uint id) {
+	if (g_nancy->getGameType() <= kGameTypeNancy3 && id != 0) {
+		// In nancy3, the ids in the correct sequence are in reverse order
+		id = (_dialSrcs.size() / (1 + _numInbetweens)) - id;
 	}
 
 	_playerSequence.push_back(id);
diff --git a/engines/nancy/action/puzzle/safelockpuzzle.h b/engines/nancy/action/puzzle/safedialpuzzle.h
similarity index 83%
rename from engines/nancy/action/puzzle/safelockpuzzle.h
rename to engines/nancy/action/puzzle/safedialpuzzle.h
index 9696e3bd969..7296191360e 100644
--- a/engines/nancy/action/puzzle/safelockpuzzle.h
+++ b/engines/nancy/action/puzzle/safedialpuzzle.h
@@ -19,21 +19,20 @@
  *
  */
 
-#ifndef NANCY_ACTION_SAFELOCKPUZZLE_H
-#define NANCY_ACTION_SAFELOCKPUZZLE_H
+#ifndef NANCY_ACTION_SAFEDIALPUZZLE_H
+#define NANCY_ACTION_SAFEDIALPUZZLE_H
 
 #include "engines/nancy/action/actionrecord.h"
 
 namespace Nancy {
 namespace Action {
 
-// Handles a specific type of puzzle where clicking an object rotates it,
-// as well as several other objects linked to it. Examples are the sun/moon
-// and staircase spindle puzzles in nancy3
-class SafeLockPuzzle : public RenderActionRecord {
+// Handles the nancy3 safe puzzle with Chinese characters on the dial,
+// as well as nancy4's sextant puzzle
+class SafeDialPuzzle : public RenderActionRecord {
 public:
-	SafeLockPuzzle() : RenderActionRecord(7) {}
-	virtual ~SafeLockPuzzle() {}
+	SafeDialPuzzle() : RenderActionRecord(7) {}
+	virtual ~SafeDialPuzzle() {}
 
 	void init() override;
 	void updateGraphics() override;
@@ -44,7 +43,7 @@ public:
 
 protected:
 	enum AnimState { kNone, kSpin, kSelect, kReset, kResetAnim };
-	Common::String getRecordTypeName() const override { return "SafeLockPuzzle"; }
+	Common::String getRecordTypeName() const override { return "SafeDialPuzzle"; }
 	bool isViewportRelative() const override { return true; }
 
 	void drawDialFrame(uint frame);
@@ -54,6 +53,8 @@ protected:
 	Common::String _imageName2;
 	Common::String _resetImageName;
 
+	bool _enableWraparound = true;
+
 	Common::Rect _dialDest;
 
 	Common::Array<Common::Rect> _dialSrcs;
@@ -72,6 +73,8 @@ protected:
 	Common::Rect _ccwHotspot;
 	Common::Rect _cwHotspot;
 
+	bool _useMoveArrows = false;
+
 	SoundDescription _spinSound;
 	SoundDescription _selectSound;
 	SoundDescription _resetSound;
@@ -85,6 +88,8 @@ protected:
 
 	Graphics::ManagedSurface _image1, _image2, _resetImage;
 
+	uint _numInbetweens = 1;
+
 	Common::Array<uint16> _playerSequence;
 	bool _solved = false;
 	AnimState _animState = kNone;
@@ -95,4 +100,4 @@ protected:
 } // End of namespace Action
 } // End of namespace Nancy
 
-#endif // NANCY_ACTION_SAFELOCKPUZZLE_H
+#endif // NANCY_ACTION_SAFEDIALPUZZLE_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index ebf8eea339f..92490c618c6 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -22,7 +22,7 @@ MODULE_OBJS = \
   action/puzzle/riddlepuzzle.o \
   action/puzzle/rippedletterpuzzle.o \
   action/puzzle/rotatinglockpuzzle.o \
-  action/puzzle/safelockpuzzle.o \
+  action/puzzle/safedialpuzzle.o \
   action/puzzle/setplayerclock.o \
   action/puzzle/sliderpuzzle.o \
   action/puzzle/soundequalizerpuzzle.o \


Commit: c3e7b418b940283e8013276b5a0c105de4ec9052
    https://github.com/scummvm/scummvm/commit/c3e7b418b940283e8013276b5a0c105de4ec9052
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T13:14:52+03:00

Commit Message:
NANCY: Fix nancy4 elevator death scene

Fixed an edge case in nancy4 where rather than ending
the game, a movie teleports the player to an incorrect
location.

Changed paths:
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 4f914855bea..1b2b97e38ad 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -45,18 +45,15 @@ void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 	Common::Serializer ser(&stream, nullptr);
 	ser.setVersion(g_nancy->getGameType());
 
-	readFilename(stream, _videoName);
-	readFilename(stream, _paletteName);
-	ser.skip(10, kGameTypeVampire, kGameTypeVampire); // skip _bitmapOverlayName for now
+	readFilename(ser, _videoName);
+	readFilename(ser, _paletteName, kGameTypeVampire, kGameTypeVampire);
+	readFilename(ser, _bitmapOverlayName);
 
-	ser.skip(0x2); // videoPlaySource
-
-	ser.skip(2, kGameTypeVampire, kGameTypeVampire); // smallSize
+	ser.skip(2); // videoPlaySource
+	ser.skip(2); // smallSize
 	ser.skip(4, kGameTypeVampire, kGameTypeVampire); // paletteStart, paletteSize
-	ser.skip(2, kGameTypeVampire, kGameTypeVampire); // hasBitmapOverlaySurface
-	ser.skip(2, kGameTypeVampire, kGameTypeVampire); // unknown, probably related to playing a sfx
-
-	ser.skip(6, kGameTypeNancy1);
+	ser.skip(2); // hasBitmapOverlaySurface
+	ser.skip(2); // VIDEO_STOP_RENDERING, VIDEO_CONTINUE_RENDERING
 
 	ser.syncAsUint16LE(_videoSceneChange);
 	ser.syncAsUint16LE(_playerCursorAllowed);
@@ -113,62 +110,6 @@ void PlaySecondaryMovie::init() {
 	RenderObject::init();
 }
 
-void PlaySecondaryMovie::updateGraphics() {
-	if (!_decoder.isVideoLoaded()) {
-		return;
-	}
-
-	if (!_decoder.isPlaying() && _isVisible && !_isFinished) {
-		_decoder.start();
-
-		if (_playDirection == kPlayMovieReverse) {
-			_decoder.setRate(-_decoder.getRate());
-			_decoder.seekToFrame(_lastFrame);
-		} else {
-			_decoder.seekToFrame(_firstFrame);
-		}
-	}
-
-	if (_decoder.needsUpdate()) {
-		uint descID = 0;
-
-		for (uint i = 0; i < _videoDescs.size(); ++i) {
-			if (_videoDescs[i].frameID == _curViewportFrame) {
-				descID = i;
-			}
-		}
-
-		GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _fullFrame, _paletteName.size() > 0);
-		_drawSurface.create(_fullFrame, _fullFrame.getBounds());
-		moveTo(_videoDescs[descID].destRect);
-
-		_needsRedraw = true;
-
-		for (auto &f : _frameFlags) {
-			if (_decoder.getCurFrame() == f.frameID) {
-				NancySceneState.setEventFlag(f.flagDesc);
-			}
-		}
-	}
-
-	if ((_decoder.getCurFrame() == _lastFrame && _playDirection == kPlayMovieForward) ||
-		(_decoder.getCurFrame() == _firstFrame && _playDirection == kPlayMovieReverse) ||
-		_decoder.atEnd()) {
-
-		// Stop the video and block it from starting again, but also wait for
-		// sound to end before changing state
-		_decoder.pauseVideo(true);
-		_isFinished = true;
-
-		if (!g_nancy->_sound->isSoundPlaying(_sound)) {
-			g_nancy->_sound->stopSound(_sound);
-			_state = kActionTrigger;
-		}
-	}
-
-	RenderObject::updateGraphics();
-}
-
 void PlaySecondaryMovie::onPause(bool pause) {
 	_decoder.pauseVideo(pause);
 	RenderActionRecord::onPause(pause);
@@ -209,6 +150,59 @@ void PlaySecondaryMovie::execute() {
 			}
 		}
 
+		// We update the decoder here instead of in updateGraphics() to avoid an
+		// edge case in nancy4 (scene 3180) where the very last frame has a frameFlag that should trigger
+		// another action record, but doesn't do so, because updateGraphics() gets called after all
+		// action record execution. Instead, the movie's own scene change (which is inexplicably enabled)
+		// gets triggered, and teleports the player to the wrong place instead of making them lose the game
+		if (!_decoder.isPlaying() && _isVisible && !_isFinished) {
+			_decoder.start();
+
+			if (_playDirection == kPlayMovieReverse) {
+				_decoder.setRate(-_decoder.getRate());
+				_decoder.seekToFrame(_lastFrame);
+			} else {
+				_decoder.seekToFrame(_firstFrame);
+			}
+		}
+
+		if (_decoder.needsUpdate()) {
+			uint descID = 0;
+
+			for (uint i = 0; i < _videoDescs.size(); ++i) {
+				if (_videoDescs[i].frameID == _curViewportFrame) {
+					descID = i;
+				}
+			}
+
+			GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _fullFrame, _paletteName.size() > 0);
+			_drawSurface.create(_fullFrame, _fullFrame.getBounds());
+			moveTo(_videoDescs[descID].destRect);
+
+			_needsRedraw = true;
+
+			for (auto &f : _frameFlags) {
+				if (_decoder.getCurFrame() == f.frameID) {
+					NancySceneState.setEventFlag(f.flagDesc);
+				}
+			}
+		}
+
+		if ((_decoder.getCurFrame() == _lastFrame && _playDirection == kPlayMovieForward) ||
+			(_decoder.getCurFrame() == _firstFrame && _playDirection == kPlayMovieReverse) ||
+			_decoder.atEnd()) {
+
+			// Stop the video and block it from starting again, but also wait for
+			// sound to end before changing state
+			_decoder.pauseVideo(true);
+			_isFinished = true;
+
+			if (!g_nancy->_sound->isSoundPlaying(_sound)) {
+				g_nancy->_sound->stopSound(_sound);
+				_state = kActionTrigger;
+			}
+		}
+
 		break;
 	}
 	case kActionTrigger:
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index e3011d6ee01..c412e6134a9 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -48,7 +48,6 @@ public:
 	virtual ~PlaySecondaryMovie();
 
 	void init() override;
-	void updateGraphics() override;
 	void onPause(bool pause) override;
 
 	void readData(Common::SeekableReadStream &stream) override;
@@ -56,6 +55,7 @@ public:
 
 	Common::String _videoName;
 	Common::String _paletteName;
+	Common::String _bitmapOverlayName;
 
 	uint16 _videoSceneChange = kMovieNoSceneChange;
 	byte _playerCursorAllowed = kPlayerCursorAllowed;


Commit: abc4168433d10511a8845a9198213cc63ebd1489
    https://github.com/scummvm/scummvm/commit/abc4168433d10511a8845a9198213cc63ebd1489
Author: Kaloyan Chehlarski (strahy at outlook.com)
Date: 2023-09-16T13:14:52+03:00

Commit Message:
NANCY: Properly pause all scene sounds

Fixed an issue where certain sound channels wouldn't get
paused when changing state. Collapsed the pause/unpause
functions inside Scene into a single function, and placed it
inside SoundManager.

Changed paths:
    engines/nancy/action/miscrecords.cpp
    engines/nancy/sound.cpp
    engines/nancy/sound.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index e0870ce2bdb..54f35363c2e 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -231,7 +231,7 @@ void LoseGame::readData(Common::SeekableReadStream &stream) {
 }
 
 void LoseGame::execute() {
-	g_nancy->_sound->stopAndUnloadSpecificSounds();
+	g_nancy->_sound->stopAndUnloadSceneSpecificSounds();
 	NancySceneState.setDestroyOnExit();
 
 	if (!ConfMan.hasKey("original_menus") || ConfMan.getBool("original_menus")) {
@@ -268,7 +268,7 @@ void WinGame::readData(Common::SeekableReadStream &stream) {
 }
 
 void WinGame::execute() {
-	g_nancy->_sound->stopAndUnloadSpecificSounds();
+	g_nancy->_sound->stopAndUnloadSceneSpecificSounds();
 	NancySceneState.setDestroyOnExit();
 	g_nancy->setState(NancyState::kCredits, NancyState::kMainMenu);
 
diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index b49209e514f..6888ab8ffbb 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -583,7 +583,7 @@ void SoundManager::recalculateSoundEffects() {
 	}
 }
 
-void SoundManager::stopAndUnloadSpecificSounds() {
+void SoundManager::stopAndUnloadSceneSpecificSounds() {
 	byte numSSChans = g_nancy->getStaticData().soundChannelInfo.numSceneSpecificChannels;
 
 	if (g_nancy->getGameType() == kGameTypeVampire && Nancy::State::Map::hasInstance()) {
@@ -601,6 +601,23 @@ void SoundManager::stopAndUnloadSpecificSounds() {
 	stopSound("MSND");
 }
 
+void SoundManager::pauseSceneSpecificSounds(bool pause) {
+	byte numSSChans = g_nancy->getStaticData().soundChannelInfo.numSceneSpecificChannels;
+	if (g_nancy->getGameType() == kGameTypeVampire && Nancy::State::Map::hasInstance()) {
+		if (!pause || g_nancy->getState() != NancyState::kMap) {
+			// Stop the map sound in certain scenes
+			uint currentScene = NancySceneState.getSceneInfo().sceneID;
+			if (currentScene == 0 || (currentScene >= 15 && currentScene <= 27)) {
+				g_nancy->_sound->pauseSound(NancyMapState.getSound(), pause);
+			}
+		}
+	}
+
+	for (uint i = 0; i < numSSChans; ++i) {
+		g_nancy->_sound->pauseSound(i, pause);
+	}
+}
+
 void SoundManager::initSoundChannels() {
 	const SoundChannelInfo &channelInfo = g_nancy->getStaticData().soundChannelInfo;
 
diff --git a/engines/nancy/sound.h b/engines/nancy/sound.h
index 9a05398a486..490a85a022b 100644
--- a/engines/nancy/sound.h
+++ b/engines/nancy/sound.h
@@ -117,7 +117,8 @@ public:
 	void recalculateSoundEffects();
 
 	// Used when changing scenes
-	void stopAndUnloadSpecificSounds();
+	void stopAndUnloadSceneSpecificSounds();
+	void pauseSceneSpecificSounds(bool pause);
 
 	static Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 overrideSamplesPerSec = 0);
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index be548732fb9..13c03c1788a 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -149,7 +149,7 @@ void Scene::process() {
 	case kStartSound:
 		_state = kRun;
 		if (_sceneState.currentScene.continueSceneSound == kLoadSceneSound) {
-			g_nancy->_sound->stopAndUnloadSpecificSounds();
+			g_nancy->_sound->stopAndUnloadSceneSpecificSounds();
 			g_nancy->_sound->loadSound(_sceneState.summary.sound);
 			g_nancy->_sound->playSound(_sceneState.summary.sound);
 		}
@@ -180,7 +180,7 @@ void Scene::onStateEnter(const NancyState::NancyState prevState) {
 		if (prevState == NancyState::kPause) {
 			g_nancy->_sound->pauseAllSounds(false);
 		} else {
-			unpauseSceneSpecificSounds();
+			g_nancy->_sound->pauseSceneSpecificSounds(false);
 		}
 
 		g_nancy->_sound->stopSound("MSND");
@@ -201,7 +201,7 @@ bool Scene::onStateExit(const NancyState::NancyState nextState) {
 	if (nextState == NancyState::kPause) {
 		g_nancy->_sound->pauseAllSounds(true);
 	} else {
-		pauseSceneSpecificSounds();
+		g_nancy->_sound->pauseSceneSpecificSounds(true);
 	}
 
 	_gameStateRequested = NancyState::kNone;
@@ -234,32 +234,6 @@ void Scene::popScene() {
 	_sceneState.isScenePushed = false;
 }
 
-void Scene::pauseSceneSpecificSounds() {
-	if (g_nancy->getGameType() == kGameTypeVampire && Nancy::State::Map::hasInstance() && g_nancy->getState() != NancyState::kMap) {
-		uint currentScene = _sceneState.currentScene.sceneID;
-		if (currentScene == 0 || (currentScene >= 15 && currentScene <= 27)) {
-			g_nancy->_sound->pauseSound(NancyMapState.getSound(), true);
-		}
-	}
-
-	for (uint i = 0; i < 10; ++i) {
-		g_nancy->_sound->pauseSound(i, true);
-	}
-}
-
-void Scene::unpauseSceneSpecificSounds() {
-	if (g_nancy->getGameType() == kGameTypeVampire && Nancy::State::Map::hasInstance()) {
-		uint currentScene = _sceneState.currentScene.sceneID;
-		if (currentScene == 0 || (currentScene >= 15 && currentScene <= 27)) {
-			g_nancy->_sound->pauseSound(NancyMapState.getSound(), false);
-		}
-	}
-
-	for (uint i = 0; i < 10; ++i) {
-		g_nancy->_sound->pauseSound(i, false);
-	}
-}
-
 void Scene::setPlayerTime(Time time, byte relative) {
 	if (relative == kRelativeClockBump) {
 		// Relative, add the specified time to current playerTime
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 354b5b16d71..68f8cb9d789 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -125,10 +125,7 @@ public:
 	void changeScene(const SceneChangeDescription &sceneDescription);
 	void pushScene();
 	void popScene();
-
-	void pauseSceneSpecificSounds();
-	void unpauseSceneSpecificSounds();
-
+	
 	void setPlayerTime(Time time, byte relative);
 	Time getPlayerTime() const { return _timers.playerTime; }
 	Time getTimerTime() const { return _timers.timerIsActive ? _timers.timerTime : 0; }




More information about the Scummvm-git-logs mailing list