[Scummvm-git-logs] scummvm branch-2-7 -> 33e6c4a45685983b616ed32d5b491710da1f5183

neuromancer noreply at scummvm.org
Mon Jul 3 20:17:20 UTC 2023


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

Summary:
06a0bb0298 FREESCAPE: refactored on screen controller code to acept clicks outside the view area
7fbbe29aeb FREESCAPE: fix for correct implementation of demo mode in driller/dark
e3c10896a5 FREESCAPE: implemented timed condition executions from dark (and other games)
9ca691aec6 FREESCAPE: fixed potentially unitialized variable
62a2dd0125 FREESCAPE: parsed and added ECD for driller dos demo
5e74f0e025 FREESCAPE: better rendering of lines in OpenGL
8793df7b16 FREESCAPE: added ECDs for driller for dos
9967b61eaa FREESCAPE: added walls and simplified area change code for driller for dos
ef9b556cfa FREESCAPE: re-use loadGlobalObjects for different games
578330a20f FREESCAPE: initial implementation of the opengl shader renderer
1c763a01f9 FREESCAPE: renamed shaders filenames and added them into shader.dat
7049d28f3b FREESCAPE: implemented lines in shader renderer
8ceaf7831e FREESCAPE: implemented player shoot in shader renderer
119405cfb8 FREESCAPE: correctly deallocate shaders memory
e6d6946871 FREESCAPE: loading of title and border images in castle for dos
8ac1af2c57 FREESCAPE: show basic UI in castle for dos
ee1d083a4d FREESCAPE: properly initialize _verts in shader renderer
03e42d15d4 GRAPHICS: OPENGL: added another setUniform function to set an uniform value in shaders from an array of ints
f57126e2fc FREESCAPE: initial implementation of stipple patterns in shader renderer
f937d769b0 FREESCAPE: avoid crashing parsing driller conditions for zx
cb3c3e5002 FREESCAPE: improved rendering of stipple in shader renderer
fdce543286 FREESCAPE: fixed incorrect parsing of 8-bit conditions
8260ca8828 FREESCAPE: make triangle shader more compatible with gles2
1e28728f14 FREESCAPE: make triangle shader more compatible with gles2 (again)
7fe579351e FREESCAPE: added detection of some unsupported demos
e5d0ebefd7 FREESCAPE: initial support for Dark Side ZX demo
81ef1ec054 FREESCAPE: started to implemented Dark Side UI in ZX spectrum
0eae3fbde3 FREESCAPE: attempt to fix effect execution when stepping up
d18b4fd29d FREESCAPE: some fixes in the parsing of fcl opcodes
ea41d363b1 FREESCAPE: correctly print the SETVAR opcode
ee159b6a0e FREESCAPE: refactored clear code as drawBackground
c57029b8e8 FREESCAPE: improved image reading for castle in DOS/EGA
73391bffd7 FREESCAPE: improved border palette for castle in DOS/EGA
c8afca45ec FREESCAPE: make sure the floor is available in area 2 of castle
bccfe4eb0b FREESCAPE: better rendering of EGA colors in castle
99f9bb7eba FREESCAPE: allow to reset color pairs, useful in castle EGA
a42d72ad6e FREESCAPE: improved rendering of rectangles and fixes
ef88a9886f FREESCAPE: disallow zero length conditions
6963aad584 FREESCAPE: allow to parse conditions using the ACTIVATED trigger for castle
d342614658 FREESCAPE: basic support for control flow opcodes of castle
3a6fbca79c FREESCAPE: refactored conditional handling to make it work with castle
55d4acc6e7 FREESCAPE: added support for using different languages in castle (DOS)
746f694b63 FREESCAPE: added support for parsing (hardcoded) font for castle (DOS)
764eb59b8e FREESCAPE: refactored castle (DOS) to use drawDOSUI
9e2f4b760e FREESCAPE: initial support for castle demo (EGA)
9dbe3433aa FREESCAPE: expanded floor size
700c62949c FREESCAPE: initial detection of castle demo for amiga
626f6721d1 FREESCAPE: basic implementation of activation (only used in castle/crypt)
acf7e1e940 FREESCAPE: Fix -Wformat warnings
f4555942b0 FREESCAPE: new collision code
a68b228f59 FREESCAPE: remove spaces from area name when displaying info menu in driller
12e0332415 FREESCAPE: make sure end game area is properly renderer in driller
a7a4415acb FREESCAPE: palette fixes for driller cpc
e5747d3916 FREESCAPE: color remap fixes for driller cpc/amiga/atari
54d4dfc2a4 FREESCAPE: info menu fixes for driller zx/amiga/atari
0f7f13813b FREESCAPE: added missing items for driller cga palettes
0d01a9efdc FREESCAPE: improved handling of entrance usage
e96d2199c3 FREESCAPE: improved handling of rise/fall
d0f9025cb9 FREESCAPE: initial parsing and rendering of group objects
cbe4d4fd45 FREESCAPE: improved rendering and scaling of group objects
2ed383c0d2 FREESCAPE: adjust aabb tolerance for castle
c0995c3fa2 FREESCAPE: improved rendering of group animations
2bdef2ef42 FREESCAPE: added start animation opcode
1145730b6e FREESCAPE: improved parsing of castle demo for amiga
c9bd9c62de FREESCAPE: improved parsing of castle demo for amiga
ababd0da8e FREESCAPE: added code to decrypt dark for amiga/atari
8e675393dd FREESCAPE: added code to decrypt some driller releases for atari
82fd2ee5d4 FREESCAPE: added better detection of driller releases for atari
40b73a35a7 FREESCAPE: better detection of driller releases for atari
7593592c45 FREESCAPE: more precise timing of some fcl instructions
7d3fc01cd1 FREESCAPE: better code to transition between areas
4486477a93 FREESCAPE: correctly scale mouse position in opengl renderers
c36b35b28f FREESCAPE: fixes for driller demo
2d154c3b70 FREESCAPE: Fix typo
fd50a5586b FREESCAPE: enable on screen controls in driller for all the scummvm platforms
60945611b9 DRILLER: Remove newline from detection "extra" field
8ef39c1a0c FREESCAPE: Fix typos in detection "extra" field
60818c0e41 FREESCAPE: invert colors when rendering player shots in driller
4c71526352 FREESCAPE: added offsetOrigin function to avoid breaking setOrigin
33e6c4a456 FREESCAPE: initialize r, g and b variables in renderPlayerShoot


Commit: 06a0bb0298bc88abc12840910a4c6e7afd5316c3
    https://github.com/scummvm/scummvm/commit/06a0bb0298bc88abc12840910a4c6e7afd5316c3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: refactored on screen controller code to acept clicks outside the view area

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/freescape.h
    engines/freescape/games/driller/driller.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 4911e723dd5..49381e47078 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -501,10 +501,17 @@ void FreescapeEngine::processInput() {
 		case Common::EVENT_LBUTTONDOWN:
 			if (_hasFallen)
 				break;
-			if (_viewArea.contains(_crossairPosition))
-				shoot();
-			else
-				onScreenControls(_crossairPosition);
+			mousePos = event.mouse;
+			{
+				bool touchedScreenControls = false;
+
+				#if defined(__ANDROID__) || defined(IPHONE)
+				touchedScreenControls = onScreenControls(mousePos);
+				#endif
+
+				if (!touchedScreenControls && _viewArea.contains(_crossairPosition))
+					shoot();
+			}
 			break;
 
 		default:
@@ -513,7 +520,8 @@ void FreescapeEngine::processInput() {
 	}
 }
 
-void FreescapeEngine::onScreenControls(Common::Point mouse) {
+bool FreescapeEngine::onScreenControls(Common::Point mouse) {
+	return false;
 }
 
 void FreescapeEngine::executeMovementConditions() {
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index daf46137bbc..2219aaba3ec 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -221,7 +221,7 @@ public:
 	void resetInput();
 	void generateDemoInput();
 	virtual void pressedKey(const int keycode);
-	virtual void onScreenControls(Common::Point mouse);
+	virtual bool onScreenControls(Common::Point mouse);
 	void move(CameraMovement direction, uint8 scale, float deltaTime);
 	virtual void checkIfStillInArea();
 	void changePlayerHeight(int index);
@@ -485,7 +485,7 @@ private:
 	void drawCPCUI(Graphics::Surface *surface) override;
 	void drawC64UI(Graphics::Surface *surface) override;
 	void drawAmigaAtariSTUI(Graphics::Surface *surface) override;
-	void onScreenControls(Common::Point mouse) override;
+	bool onScreenControls(Common::Point mouse) override;
 	void initAmigaAtari();
 	void initDOS();
 	void initZX();
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 90a0a65a030..4c6da3eefd0 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -819,32 +819,43 @@ bool DrillerEngine::checkIfGameEnded() {
 	return false;
 }
 
-void DrillerEngine::onScreenControls(Common::Point mouse) {
-	if (_moveFowardArea.contains(mouse))
+bool DrillerEngine::onScreenControls(Common::Point mouse) {
+	if (_moveFowardArea.contains(mouse)) {
 		move(kForwardMovement, _scaleVector.x(), 20.0);
-	else if (_moveLeftArea.contains(mouse))
+		return true;
+	} else if (_moveLeftArea.contains(mouse)) {
 		move(kLeftMovement, _scaleVector.y(), 20.0);
-	else if (_moveRightArea.contains(mouse))
+		return true;
+	} else if (_moveRightArea.contains(mouse)) {
 		move(kRightMovement, _scaleVector.y(), 20.0);
-	else if (_moveBackArea.contains(mouse))
+		return true;
+	} else if (_moveBackArea.contains(mouse)) {
 		move(kBackwardMovement, _scaleVector.x(), 20.0);
-	else if (_moveUpArea.contains(mouse))
+		return true;
+	} else if (_moveUpArea.contains(mouse)) {
 		rise();
-	else if (_moveDownArea.contains(mouse))
+		return true;
+	} else if (_moveDownArea.contains(mouse)) {
 		lower();
-	else if (_deployDrillArea.contains(mouse))
+		return true;
+	} else if (_deployDrillArea.contains(mouse)) {
 		pressedKey(Common::KEYCODE_d);
-	else if (_infoScreenArea.contains(mouse))
+		return true;
+	} else if (_infoScreenArea.contains(mouse)) {
 		drawInfoMenu();
-	else if (_saveGameArea.contains(mouse)) {
+		return true;
+	} else if (_saveGameArea.contains(mouse)) {
 		_gfx->setViewport(_fullscreenViewArea);
 		saveGameDialog();
 		_gfx->setViewport(_viewArea);
+		return true;
 	} else if (_loadGameArea.contains(mouse)) {
 		_gfx->setViewport(_fullscreenViewArea);
 		loadGameDialog();
 		_gfx->setViewport(_viewArea);
+		return true;
 	}
+	return false;
 }
 
 


Commit: 7fbbe29aeb33836d69f1dcf1624bd6be365207d4
    https://github.com/scummvm/scummvm/commit/7fbbe29aeb33836d69f1dcf1624bd6be365207d4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: fix for correct implementation of demo mode in driller/dark

Changed paths:
    engines/freescape/assets.cpp
    engines/freescape/games/driller/driller.cpp


diff --git a/engines/freescape/assets.cpp b/engines/freescape/assets.cpp
index 309c30a424a..d9e6e16931e 100644
--- a/engines/freescape/assets.cpp
+++ b/engines/freescape/assets.cpp
@@ -63,9 +63,6 @@ void FreescapeEngine::loadAssetsDemo() {
 		loadAssetsDOSDemo();
 	} else
 		error("Unsupported demo for Driller");
-
-	_demoMode = !_disableDemoMode;
-	_angleRotationIndex = 0;
 }
 
 void FreescapeEngine::loadAssetsAtariDemo() {
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 4c6da3eefd0..490912727d5 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -78,6 +78,11 @@ DrillerEngine::DrillerEngine(OSystem *syst, const ADGameDescription *gd) : Frees
 	Math::Vector3d drillBaseSize = Math::Vector3d(3, 2, 3);
 	_drillBase = new GeometricObject(kCubeType, 0, 0, drillBaseOrigin, drillBaseSize, nullptr, nullptr, FCLInstructionVector(), "");
 	assert(!_drillBase->isDestroyed() && !_drillBase->isInvisible());
+
+	if (isDemo()) {
+		_demoMode = !_disableDemoMode; // All the driller demos are non-interactive
+		_angleRotationIndex = 0;
+	}
 }
 
 DrillerEngine::~DrillerEngine() {


Commit: e3c10896a5b4b45344778aecfe27aaf2f82d18cf
    https://github.com/scummvm/scummvm/commit/e3c10896a5b4b45344778aecfe27aaf2f82d18cf
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: implemented timed condition executions from dark (and other games)

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/freescape.h
    engines/freescape/games/dark/dark.cpp
    engines/freescape/language/8bitDetokeniser.cpp
    engines/freescape/language/instruction.cpp
    engines/freescape/movement.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 49381e47078..661a2db9dff 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -526,7 +526,7 @@ bool FreescapeEngine::onScreenControls(Common::Point mouse) {
 
 void FreescapeEngine::executeMovementConditions() {
 	// Only execute "on collision" room/global conditions
-	executeLocalGlobalConditions(false, true);
+	executeLocalGlobalConditions(false, true, false);
 }
 
 void FreescapeEngine::updateTimeVariables() {
@@ -537,7 +537,7 @@ void FreescapeEngine::updateTimeVariables() {
 		_lastMinute = minutes;
 		_gameStateVars[0x1e] += 1;
 		_gameStateVars[0x1f] += 1;
-		executeLocalGlobalConditions(false, true); // Only execute "on collision" room/global conditions
+		executeLocalGlobalConditions(false, true, false); // Only execute "on collision" room/global conditions
 	}
 }
 
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 2219aaba3ec..ae3a2f8dc0b 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -276,8 +276,8 @@ public:
 	Math::Vector3d _objExecutingCodeSize;
 	virtual void executeMovementConditions();
 	void executeObjectConditions(GeometricObject *obj, bool shot, bool collided);
-	void executeLocalGlobalConditions(bool shot, bool collided);
-	void executeCode(FCLInstructionVector &code, bool shot, bool collided);
+	void executeLocalGlobalConditions(bool shot, bool collided, bool timer);
+	void executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer);
 
 	// Instructions
 	void executeIncrementVariable(FCLInstruction &instruction);
@@ -531,7 +531,6 @@ public:
 
 	int _lastTenSeconds;
 	void updateTimeVariables() override;
-	void executeMovementConditions() override;
 
 	void drawDOSUI(Graphics::Surface *surface) override;
 	void drawFullscreenMessage(Common::String message);
diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index d628c4d4ef5..1165f60243b 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -201,27 +201,20 @@ void DarkEngine::checkIfStillInArea() {
 		FreescapeEngine::checkIfStillInArea();
 }
 
-void DarkEngine::executeMovementConditions() {
-	// Only execute "on collision" room/global conditions
-	if (_currentArea->getAreaFlags() == 1)
-		executeLocalGlobalConditions(false, true);
-}
-
 void DarkEngine::updateTimeVariables() {
 	// This function only executes "on collision" room/global conditions
 	int seconds, minutes, hours;
 	getTimeFromCountdown(seconds, minutes, hours);
 	if (_lastTenSeconds != seconds / 10) {
 		_lastTenSeconds = seconds / 10;
-		if (_currentArea->getAreaFlags() == 0)
-			executeLocalGlobalConditions(false, true);
+		executeLocalGlobalConditions(false, false, true);
 	}
 
 	if (_lastMinute != minutes) {
 		_lastMinute = minutes;
 		_gameStateVars[0x1e] += 1;
 		_gameStateVars[0x1f] += 1;
-		executeLocalGlobalConditions(false, true);
+		executeLocalGlobalConditions(false, true, false);
 	}
 }
 
diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 06fe20c56ab..187c46f1b48 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -39,9 +39,8 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 	Common::Array<uint8>::size_type sizeOfTokenisedContent = tokenisedCondition.size();
 
 	// on the 8bit platforms, all instructions have a conditional flag;
-	// we'll want to convert them into runs of "if shot? then" and "if collided? then",
+	// we'll want to convert them into runs of "if shot? then", "if collided? then" or "if timer? then",
 	// and we'll want to start that from the top
-	uint8 conditionalIsShot = 0x1;
 	FCLInstructionVector *conditionalInstructions = new FCLInstructionVector();
 	FCLInstruction currentInstruction;
 
@@ -55,30 +54,42 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		 0, 0, 0, 0, 0, 0, 2, 2,
 		 1};
 
+	detokenisedStream += Common::String::format("CONDITION FLAG: %x\n", tokenisedCondition[0]);
+	Token::Type newConditional;
+	Token::Type oldConditional;
+
 	while (bytePointer < sizeOfTokenisedContent) {
 		// get the conditional type of the next operation
-		uint8 newConditionalIsShot = tokenisedCondition[bytePointer] & 0x80;
+		uint8 conditionalByte = tokenisedCondition[bytePointer];
+
+		if (conditionalByte & 0x80)
+			newConditional = Token::SHOTQ;
+		else if (conditionalByte & 0x40)
+			newConditional = Token::TIMERQ;
+		else
+			newConditional = Token::COLLIDEDQ;
 
 		// if the conditional type has changed then end the old conditional,
 		// if we were in one, and begin a new one
-		if (newConditionalIsShot != conditionalIsShot) {
+		if (bytePointer == 0 || newConditional != oldConditional) {
+			oldConditional = newConditional;
 			FCLInstruction branch;
-			if (conditionalIsShot)
-				branch = FCLInstruction(Token::SHOTQ);
-			else
-				branch = FCLInstruction(Token::COLLIDEDQ);
+			branch = FCLInstruction(oldConditional);
 
 			branch.setBranches(conditionalInstructions, nullptr);
 			instructions.push_back(branch);
 
-			conditionalIsShot = newConditionalIsShot;
-			if (bytePointer)
+			if (bytePointer > 0)
 				detokenisedStream += "ENDIF\n";
 
-			if (conditionalIsShot)
+			if (oldConditional == Token::SHOTQ)
 				detokenisedStream += "IF SHOT? THEN\n";
-			else
+			else if (oldConditional == Token::TIMERQ)
+				detokenisedStream += "IF TIMER? THEN\n";
+			else if (oldConditional == Token::COLLIDEDQ)
 				detokenisedStream += "IF COLLIDED? THEN\n";
+			else
+				error("Invalid conditional: %x", oldConditional);
 
 			// Allocate the next vector of instructions
 			conditionalInstructions = new FCLInstructionVector();
@@ -415,10 +426,7 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 	// conditionalInstructions->push_back(currentInstruction);
 
 	FCLInstruction branch;
-	if (conditionalIsShot)
-		branch = FCLInstruction(Token::SHOTQ);
-	else
-		branch = FCLInstruction(Token::COLLIDEDQ);
+	branch = FCLInstruction(oldConditional);
 
 	branch.setBranches(conditionalInstructions, nullptr);
 	instructions.push_back(branch);
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index 2f68554d9cb..a528ec48275 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -72,11 +72,11 @@ void FreescapeEngine::executeObjectConditions(GeometricObject *obj, bool shot, b
 		_firstSound = true;
 		_objExecutingCodeSize = obj->getSize();
 		debugC(1, kFreescapeDebugCode, "Executing with collision flag: %s", obj->_conditionSource.c_str());
-		executeCode(obj->_condition, shot, collided);
+		executeCode(obj->_condition, shot, collided, false); // TODO: check this last parameter
 	}
 }
 
-void FreescapeEngine::executeLocalGlobalConditions(bool shot, bool collided) {
+void FreescapeEngine::executeLocalGlobalConditions(bool shot, bool collided, bool timer) {
 	if (isCastle())
 		return;
 	debugC(1, kFreescapeDebugCode, "Executing room conditions");
@@ -85,17 +85,17 @@ void FreescapeEngine::executeLocalGlobalConditions(bool shot, bool collided) {
 
 	for (uint i = 0; i < conditions.size(); i++) {
 		debugC(1, kFreescapeDebugCode, "%s", conditionSources[i].c_str());
-		executeCode(conditions[i], shot, collided);
+		executeCode(conditions[i], shot, collided, timer);
 	}
 
 	debugC(1, kFreescapeDebugCode, "Executing global conditions (%d)", _conditions.size());
 	for (uint i = 0; i < _conditions.size(); i++) {
 		debugC(1, kFreescapeDebugCode, "%s", _conditionSources[i].c_str());
-		executeCode(_conditions[i], shot, collided);
+		executeCode(_conditions[i], shot, collided, timer);
 	}
 }
 
-void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool collided) {
+void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer) {
 	assert(!(shot && collided));
 	int ip = 0;
 	int codeSize = code.size();
@@ -109,13 +109,19 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 			break;
 		case Token::COLLIDEDQ:
 			if (collided)
-				executeCode(*instruction._thenInstructions, shot, collided);
+				executeCode(*instruction._thenInstructions, shot, collided, timer);
 			// else branch is always empty
 			assert(instruction._elseInstructions == nullptr);
 			break;
 		case Token::SHOTQ:
 			if (shot)
-				executeCode(*instruction._thenInstructions, shot, collided);
+				executeCode(*instruction._thenInstructions, shot, collided, timer);
+			// else branch is always empty
+			assert(instruction._elseInstructions == nullptr);
+			break;
+		case Token::TIMERQ:
+			if (timer)
+				executeCode(*instruction._thenInstructions, shot, collided, timer);
 			// else branch is always empty
 			assert(instruction._elseInstructions == nullptr);
 			break;
diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index c9055e80248..f914780359e 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -92,7 +92,7 @@ void FreescapeEngine::shoot() {
 
 		executeObjectConditions(gobj, true, false);
 	}
-	executeLocalGlobalConditions(true, false); // Only execute "on shot" room/global conditions
+	executeLocalGlobalConditions(true, false, false); // Only execute "on shot" room/global conditions
 }
 
 void FreescapeEngine::changePlayerHeight(int index) {


Commit: 9ca691aec601784f5ab0e4781b16b850358bef70
    https://github.com/scummvm/scummvm/commit/9ca691aec601784f5ab0e4781b16b850358bef70
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: fixed potentially unitialized variable

Changed paths:
    engines/freescape/language/8bitDetokeniser.cpp


diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 187c46f1b48..6b00ecc926e 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -55,8 +55,8 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		 1};
 
 	detokenisedStream += Common::String::format("CONDITION FLAG: %x\n", tokenisedCondition[0]);
-	Token::Type newConditional;
-	Token::Type oldConditional;
+	Token::Type newConditional = Token::UNKNOWN;
+	Token::Type oldConditional = Token::UNKNOWN;
 
 	while (bytePointer < sizeOfTokenisedContent) {
 		// get the conditional type of the next operation


Commit: 62a2dd012598aab028e89f9cfaa6a5ca263a8b09
    https://github.com/scummvm/scummvm/commit/62a2dd012598aab028e89f9cfaa6a5ca263a8b09
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: parsed and added ECD for driller dos demo

Changed paths:
    engines/freescape/freescape.h
    engines/freescape/games/dark/dark.cpp
    engines/freescape/games/dark/dos.cpp


diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index ae3a2f8dc0b..d1c96cf705c 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -539,6 +539,8 @@ public:
 
 private:
 	void loadGlobalObjects(Common::SeekableReadStream *file, int offset);
+	void addECDs(Area *area);
+	void addECD(Area *area, const Math::Vector3d position, int index);
 };
 
 class EclipseEngine : public FreescapeEngine {
diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index 1165f60243b..48bee2a33ba 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -23,6 +23,7 @@
 
 #include "freescape/freescape.h"
 #include "freescape/language/8bitDetokeniser.h"
+#include "freescape/objects/global.h"
 #include "freescape/objects/connections.h"
 
 namespace Freescape {
@@ -65,7 +66,7 @@ void DarkEngine::loadGlobalObjects(Common::SeekableReadStream *file, int offset)
 	assert(!_areaMap.contains(255));
 	ObjectMap *globalObjectsByID = new ObjectMap;
 	file->seek(offset);
-	for (int i = 0; i < 22; i++) {
+	for (int i = 0; i < 23; i++) {
 		Object *gobj = load8bitObject(file);
 		assert(gobj);
 		assert(!globalObjectsByID->contains(gobj->getObjectID()));
@@ -76,6 +77,48 @@ void DarkEngine::loadGlobalObjects(Common::SeekableReadStream *file, int offset)
 	_areaMap[255] = new Area(255, 0, globalObjectsByID, nullptr);
 }
 
+void DarkEngine::addECDs(Area *area) {
+	GlobalStructure *rs = (GlobalStructure *)area->entranceWithID(255);
+	debugC(1, kFreescapeDebugParser, "ECD positions:");
+	for (uint i = 0; i < rs->_structure.size(); i = i + 3) {
+		int x = 32 * rs->_structure[i];
+		int y = 32 * rs->_structure[i + 1];
+		int z = 32 * rs->_structure[i + 2];
+
+		debugC(1, kFreescapeDebugParser, "%d %d %d", x, y, z);
+		if (x == 0 && y == 0 && z == 0) {
+			debugC(1, kFreescapeDebugParser, "Skiping ECD zero position");
+			continue;
+		}
+		addECD(area, Math::Vector3d(x, y, z), i / 3);
+	}
+}
+
+void DarkEngine::addECD(Area *area, const Math::Vector3d position, int index) {
+	GeometricObject *obj = nullptr;
+	Math::Vector3d origin = position;
+
+	int16 id = 227 + index * 6;
+	int heightLastObject = 0;
+	for (int i = 0; i < 4; i++) {
+		debugC(1, kFreescapeDebugParser, "Adding object %d to room structure", id);
+		obj = (GeometricObject *)_areaMap[255]->objectWithID(id);
+		assert(obj);
+		// Set position for object
+		origin.setValue(0, origin.x());
+		origin.setValue(1, origin.y() + heightLastObject);
+		origin.setValue(2, origin.z());
+
+		obj = (GeometricObject *)obj->duplicate();
+		obj->setOrigin(origin);
+		obj->makeVisible();
+		area->addObject(obj);
+
+		heightLastObject = obj->getSize().y();
+		id--;
+	}
+}
+
 void DarkEngine::initGameState() {
 	_flyMode = false;
 	_noClipMode = false;
diff --git a/engines/freescape/games/dark/dos.cpp b/engines/freescape/games/dark/dos.cpp
index 24c4396d079..220d6c3cabd 100644
--- a/engines/freescape/games/dark/dos.cpp
+++ b/engines/freescape/games/dark/dos.cpp
@@ -48,6 +48,12 @@ void DarkEngine::loadAssetsDOSDemo() {
 		load8bitBinary(&file, 0xa700, 16);
 		_border = load8bitBinImage(&file, 0x210);
 		_border->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
+
+		for (auto &it : _areaMap) {
+			if (!it._value->entranceWithID(255))
+				continue;
+			addECDs(it._value);
+		}
 	} else if (_renderMode == Common::kRenderCGA) {
 		//loadBundledImages();
 		file.open("DSIDEC.EXE");


Commit: 5e74f0e02535316b003bbb62a9634e66dedf7aed
    https://github.com/scummvm/scummvm/commit/5e74f0e02535316b003bbb62a9634e66dedf7aed
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: better rendering of lines in OpenGL

Changed paths:
    engines/freescape/gfx_opengl.cpp


diff --git a/engines/freescape/gfx_opengl.cpp b/engines/freescape/gfx_opengl.cpp
index 2848f48e9fb..0c09bb0f5ba 100644
--- a/engines/freescape/gfx_opengl.cpp
+++ b/engines/freescape/gfx_opengl.cpp
@@ -243,7 +243,9 @@ void OpenGLRenderer::renderFace(const Common::Array<Math::Vector3d> &vertices) {
 		copyToVertexArray(0, v0);
 		copyToVertexArray(1, v1);
 		glVertexPointer(3, GL_FLOAT, 0, _verts);
+		glLineWidth(MAX(1, g_system->getWidth() / 192));
 		glDrawArrays(GL_LINES, 0, 2);
+		glLineWidth(1);
 		glDisableClientState(GL_VERTEX_ARRAY);
 		return;
 	}


Commit: 8793df7b16f6610ea5d1960622c39c05465c3ced
    https://github.com/scummvm/scummvm/commit/8793df7b16f6610ea5d1960622c39c05465c3ced
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added ECDs for driller for dos

Changed paths:
    engines/freescape/games/dark/dos.cpp


diff --git a/engines/freescape/games/dark/dos.cpp b/engines/freescape/games/dark/dos.cpp
index 220d6c3cabd..55c19ce6c5d 100644
--- a/engines/freescape/games/dark/dos.cpp
+++ b/engines/freescape/games/dark/dos.cpp
@@ -88,11 +88,11 @@ void DarkEngine::loadAssetsDOSFullGame() {
 		_border->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
 
 		// TODO: load objects
-		/*for (auto &it : _areaMap) {
+		for (auto &it : _areaMap) {
 			if (!it._value->entranceWithID(255))
 				continue;
-			it._value->addStructure(_areaMap[255]);
-		}*/
+			addECDs(it._value);
+		}
 	} else if (_renderMode == Common::kRenderCGA) {
 		loadBundledImages();
 		file.open("DSIDEC.EXE");


Commit: 9967b61eaaa0065d9aa42637fff0e183ffacb193
    https://github.com/scummvm/scummvm/commit/9967b61eaaa0065d9aa42637fff0e183ffacb193
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added walls and simplified area change code for driller for dos

Changed paths:
    engines/freescape/freescape.h
    engines/freescape/games/dark/dark.cpp
    engines/freescape/games/dark/dos.cpp
    engines/freescape/language/8bitDetokeniser.cpp
    engines/freescape/language/instruction.cpp
    engines/freescape/language/instruction.h
    engines/freescape/objects/geometricobject.cpp


diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index d1c96cf705c..f8c7c95a376 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -522,7 +522,6 @@ public:
 	void titleScreen() override;
 
 	void gotoArea(uint16 areaID, int entranceID) override;
-	void checkIfStillInArea() override;
 	void pressedKey(const int keycode) override;
 	void executePrint(FCLInstruction &instruction) override;
 
@@ -541,6 +540,7 @@ private:
 	void loadGlobalObjects(Common::SeekableReadStream *file, int offset);
 	void addECDs(Area *area);
 	void addECD(Area *area, const Math::Vector3d position, int index);
+	void addWalls(Area *area);
 };
 
 class EclipseEngine : public FreescapeEngine {
diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index 48bee2a33ba..b0d5a396340 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -78,6 +78,9 @@ void DarkEngine::loadGlobalObjects(Common::SeekableReadStream *file, int offset)
 }
 
 void DarkEngine::addECDs(Area *area) {
+	if (!area->entranceWithID(255))
+		return;
+
 	GlobalStructure *rs = (GlobalStructure *)area->entranceWithID(255);
 	debugC(1, kFreescapeDebugParser, "ECD positions:");
 	for (uint i = 0; i < rs->_structure.size(); i = i + 3) {
@@ -94,6 +97,29 @@ void DarkEngine::addECDs(Area *area) {
 	}
 }
 
+void DarkEngine::addWalls(Area *area) {
+	if (!area->entranceWithID(254))
+		return;
+
+	AreaConnections *cons = (AreaConnections *)area->entranceWithID(254);
+	debugC(1, kFreescapeDebugParser, "Adding walls for area %d:", area->getAreaID());
+	int id = 240;
+	for (uint i = 1; i < cons->_connections.size(); i = i + 2) {
+		int target = cons->_connections[i];
+		debugC(1, kFreescapeDebugParser, "Connection to %d using id: %d", target, id);
+		if (target > 0) {
+			area->addObjectFromArea(id, _areaMap[255]);
+			GeometricObject *gobj = (GeometricObject *)area->objectWithID(id);
+			assert((*(gobj->_condition[1]._thenInstructions))[0].getType() == Token::Type::GOTO);
+			assert((*(gobj->_condition[1]._thenInstructions))[0]._destination == 0);
+			(*(gobj->_condition[1]._thenInstructions))[0].setSource(target);
+		} else
+			area->addObjectFromArea(id + 1, _areaMap[255]);
+
+		id = id + 2;
+	}
+}
+
 void DarkEngine::addECD(Area *area, const Math::Vector3d position, int index) {
 	GeometricObject *obj = nullptr;
 	Math::Vector3d origin = position;
@@ -220,30 +246,6 @@ void DarkEngine::pressedKey(const int keycode) {
 	}
 }
 
-void DarkEngine::checkIfStillInArea() {
-	AreaConnections *cons = (AreaConnections *)_currentArea->entranceWithID(254);
-	if (!cons) {
-		FreescapeEngine::checkIfStillInArea();
-		return;
-	}
-
-	int nextAreaID = 0;
-
-	if (_position.z() >= 4064 - 16)
-		nextAreaID = cons->_connections[1];
-	else if (_position.x() >= 4064 - 16)
-		nextAreaID = cons->_connections[3];
-	else if (_position.z() <= 16)
-		nextAreaID = cons->_connections[5];
-	else if (_position.x() <= 16)
-		nextAreaID = cons->_connections[7];
-
-	if (nextAreaID > 0)
-		gotoArea(nextAreaID, 0);
-	else
-		FreescapeEngine::checkIfStillInArea();
-}
-
 void DarkEngine::updateTimeVariables() {
 	// This function only executes "on collision" room/global conditions
 	int seconds, minutes, hours;
diff --git a/engines/freescape/games/dark/dos.cpp b/engines/freescape/games/dark/dos.cpp
index 55c19ce6c5d..b7b22563665 100644
--- a/engines/freescape/games/dark/dos.cpp
+++ b/engines/freescape/games/dark/dos.cpp
@@ -50,8 +50,7 @@ void DarkEngine::loadAssetsDOSDemo() {
 		_border->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
 
 		for (auto &it : _areaMap) {
-			if (!it._value->entranceWithID(255))
-				continue;
+			addWalls(it._value);
 			addECDs(it._value);
 		}
 	} else if (_renderMode == Common::kRenderCGA) {
@@ -89,8 +88,7 @@ void DarkEngine::loadAssetsDOSFullGame() {
 
 		// TODO: load objects
 		for (auto &it : _areaMap) {
-			if (!it._value->entranceWithID(255))
-				continue;
+			addWalls(it._value);
 			addECDs(it._value);
 		}
 	} else if (_renderMode == Common::kRenderCGA) {
diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 6b00ecc926e..16f57bf2924 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -42,7 +42,7 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 	// we'll want to convert them into runs of "if shot? then", "if collided? then" or "if timer? then",
 	// and we'll want to start that from the top
 	FCLInstructionVector *conditionalInstructions = new FCLInstructionVector();
-	FCLInstruction currentInstruction;
+	FCLInstruction currentInstruction = FCLInstruction(Token::UNKNOWN);
 
 	// this lookup table tells us how many argument bytes to read per opcode
 	uint8 argumentsRequiredByOpcode[49] =
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index a528ec48275..7b845bf28b1 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -27,6 +27,29 @@
 
 namespace Freescape {
 
+FCLInstructionVector *duplicateCondition(FCLInstructionVector *condition) {
+	if (!condition)
+		return nullptr;
+
+	FCLInstructionVector *copy = new FCLInstructionVector();
+	for (uint i = 0; i < condition->size(); i++) {
+		copy->push_back((*condition)[i].duplicate());
+	}
+	return copy;
+}
+
+FCLInstruction FCLInstruction::duplicate() {
+	FCLInstruction copy(_type);
+	copy.setSource(_source);
+	copy.setDestination(_destination);
+	copy.setAdditional(_additional);
+
+	copy._thenInstructions = duplicateCondition(_thenInstructions);
+	copy._elseInstructions = duplicateCondition(_elseInstructions);
+
+	return copy;
+}
+
 FCLInstruction::FCLInstruction(Token::Type type_) {
 	_source = 0;
 	_destination = 0;
diff --git a/engines/freescape/language/instruction.h b/engines/freescape/language/instruction.h
index c045cc7e2ff..e5e01ea2887 100644
--- a/engines/freescape/language/instruction.h
+++ b/engines/freescape/language/instruction.h
@@ -44,6 +44,8 @@ public:
 	Token::Type getType();
 	void setBranches(FCLInstructionVector *thenBranch, FCLInstructionVector *elseBranch);
 
+	FCLInstruction duplicate();
+
 	int32 _source;
 	int32 _additional;
 	int32 _destination;
diff --git a/engines/freescape/objects/geometricobject.cpp b/engines/freescape/objects/geometricobject.cpp
index ed19eeafd08..954f67755fb 100644
--- a/engines/freescape/objects/geometricobject.cpp
+++ b/engines/freescape/objects/geometricobject.cpp
@@ -26,6 +26,8 @@
 
 namespace Freescape {
 
+extern FCLInstructionVector *duplicateCondition(FCLInstructionVector *condition);
+
 int GeometricObject::numberOfColoursForObjectOfType(ObjectType type) {
 	switch (type) {
 	default:
@@ -186,14 +188,18 @@ void GeometricObject::scale(int factor) {
 }
 
 Object *GeometricObject::duplicate() {
-	Common::Array<uint8> *colours_copy = nullptr;
-	Common::Array<uint16> *ordinates_copy = nullptr;
+	Common::Array<uint8> *coloursCopy = nullptr;
+	Common::Array<uint16> *ordinatesCopy = nullptr;
+	FCLInstructionVector *conditionCopy = nullptr;
 
 	if (_colours)
-		colours_copy = new Common::Array<uint8>(*_colours);
+		coloursCopy = new Common::Array<uint8>(*_colours);
 
 	if (_ordinates)
-		ordinates_copy = new Common::Array<uint16>(*_ordinates);
+		ordinatesCopy = new Common::Array<uint16>(*_ordinates);
+
+	conditionCopy = duplicateCondition(&_condition);
+	assert(conditionCopy);
 
 	return new GeometricObject(
 		_type,
@@ -201,9 +207,9 @@ Object *GeometricObject::duplicate() {
 		_flags,
 		_origin,
 		_size,
-		colours_copy,
-		ordinates_copy,
-		_condition,
+		coloursCopy,
+		ordinatesCopy,
+		*conditionCopy,
 		_conditionSource);
 }
 


Commit: ef9b556cfadaf14caa07e07aee0c6fe55c61d4de
    https://github.com/scummvm/scummvm/commit/ef9b556cfadaf14caa07e07aee0c6fe55c61d4de
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: re-use loadGlobalObjects for different games

Changed paths:
    engines/freescape/freescape.h
    engines/freescape/games/dark/dark.cpp
    engines/freescape/games/dark/dos.cpp
    engines/freescape/games/driller/amiga.cpp
    engines/freescape/games/driller/atari.cpp
    engines/freescape/games/driller/c64.cpp
    engines/freescape/games/driller/cpc.cpp
    engines/freescape/games/driller/dos.cpp
    engines/freescape/games/driller/driller.cpp
    engines/freescape/games/driller/zx.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index f8c7c95a376..ccb3af3942a 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -193,6 +193,7 @@ public:
 	void load8bitBinary(Common::SeekableReadStream *file, int offset, int ncolors);
 	Area *load8bitArea(Common::SeekableReadStream *file, uint16 ncolors);
 	Object *load8bitObject(Common::SeekableReadStream *file);
+	void loadGlobalObjects(Common::SeekableReadStream *file, int offset, int size);
 	void renderPixels8bitBinImage(Graphics::ManagedSurface *surface, int &i, int &j, uint8 pixels, int color);
 
 	void renderPixels8bitBinCGAImage(Graphics::ManagedSurface *surface, int &i, int &j, uint8 pixels, int color);
@@ -454,7 +455,6 @@ public:
 	Common::Error loadGameStreamExtended(Common::SeekableReadStream *stream) override;
 
 private:
-	void loadGlobalObjects(Common::SeekableReadStream *file, int offset);
 	bool drillDeployed(Area *area);
 	GeometricObject *_drillBase;
 	Math::Vector3d drillPosition();
@@ -537,7 +537,6 @@ public:
 	Common::Error loadGameStreamExtended(Common::SeekableReadStream *stream) override;
 
 private:
-	void loadGlobalObjects(Common::SeekableReadStream *file, int offset);
 	void addECDs(Area *area);
 	void addECD(Area *area, const Math::Vector3d position, int index);
 	void addWalls(Area *area);
diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index b0d5a396340..1e17560ba1d 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -62,21 +62,6 @@ void DarkEngine::titleScreen() {
 	}
 }
 
-void DarkEngine::loadGlobalObjects(Common::SeekableReadStream *file, int offset) {
-	assert(!_areaMap.contains(255));
-	ObjectMap *globalObjectsByID = new ObjectMap;
-	file->seek(offset);
-	for (int i = 0; i < 23; i++) {
-		Object *gobj = load8bitObject(file);
-		assert(gobj);
-		assert(!globalObjectsByID->contains(gobj->getObjectID()));
-		debugC(1, kFreescapeDebugParser, "Adding global object: %d", gobj->getObjectID());
-		(*globalObjectsByID)[gobj->getObjectID()] = gobj;
-	}
-
-	_areaMap[255] = new Area(255, 0, globalObjectsByID, nullptr);
-}
-
 void DarkEngine::addECDs(Area *area) {
 	if (!area->entranceWithID(255))
 		return;
diff --git a/engines/freescape/games/dark/dos.cpp b/engines/freescape/games/dark/dos.cpp
index b7b22563665..12881e46e46 100644
--- a/engines/freescape/games/dark/dos.cpp
+++ b/engines/freescape/games/dark/dos.cpp
@@ -44,7 +44,7 @@ void DarkEngine::loadAssetsDOSDemo() {
 		loadMessagesFixedSize(&file, 0x4525, 16, 27);
 		loadMessagesFixedSize(&file, 0x9959, 307, 5);
 		loadFonts(&file, 0xa598);
-		loadGlobalObjects(&file, 0x3d04);
+		loadGlobalObjects(&file, 0x3d04, 23);
 		load8bitBinary(&file, 0xa700, 16);
 		_border = load8bitBinImage(&file, 0x210);
 		_border->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
@@ -81,7 +81,7 @@ void DarkEngine::loadAssetsDOSFullGame() {
 
 		loadFonts(&file, 0xa113);
 		loadMessagesFixedSize(&file, 0x4525, 16, 27);
-		loadGlobalObjects(&file, 0x3d04);
+		loadGlobalObjects(&file, 0x3d04, 23);
 		load8bitBinary(&file, 0xa280, 16);
 		_border = load8bitBinImage(&file, 0x210);
 		_border->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
diff --git a/engines/freescape/games/driller/amiga.cpp b/engines/freescape/games/driller/amiga.cpp
index 28c1037ecc2..7402459aa18 100644
--- a/engines/freescape/games/driller/amiga.cpp
+++ b/engines/freescape/games/driller/amiga.cpp
@@ -44,7 +44,7 @@ void DrillerEngine::loadAssetsAmigaFullGame() {
 
 		loadFonts(&file, 0x8940);
 		loadMessagesFixedSize(&file, 0xc66e, 14, 20);
-		loadGlobalObjects(&file, 0xbd62);
+		loadGlobalObjects(&file, 0xbd62, 8);
 		load8bitBinary(&file, 0x29c16, 16);
 		loadPalettes(&file, 0x297d4);
 		loadSoundsFx(&file, 0x30e80, 25);
@@ -69,7 +69,7 @@ void DrillerEngine::loadAssetsAmigaFullGame() {
 
 		loadFonts(&file, 0xa62);
 		loadMessagesFixedSize(&file, 0x499a, 14, 20);
-		loadGlobalObjects(&file, 0x4098);
+		loadGlobalObjects(&file, 0x4098, 8);
 		load8bitBinary(&file, 0x21a3e, 16);
 		loadPalettes(&file, 0x215fc);
 
@@ -120,7 +120,7 @@ void DrillerEngine::loadAssetsAmigaDemo() {
 
 	loadFonts(&file, 0xa30);
 	loadMessagesFixedSize(&file, 0x3960, 14, 20);
-	loadGlobalObjects(&file, 0x3716);
+	loadGlobalObjects(&file, 0x3716, 8);
 
 	file.close();
 	file.open("soundfx");
diff --git a/engines/freescape/games/driller/atari.cpp b/engines/freescape/games/driller/atari.cpp
index 6c2fe74a4ff..143920cb119 100644
--- a/engines/freescape/games/driller/atari.cpp
+++ b/engines/freescape/games/driller/atari.cpp
@@ -43,7 +43,7 @@ void DrillerEngine::loadAssetsAtariFullGame() {
 
 	loadFonts(&file, 0x8a32);
 	loadMessagesFixedSize(&file, 0xc5d8, 14, 20);
-	loadGlobalObjects(&file, 0xbccc);
+	loadGlobalObjects(&file, 0xbccc, 8);
 	load8bitBinary(&file, 0x29b3c, 16);
 	loadPalettes(&file, 0x296fa);
 	loadSoundsFx(&file, 0x30da6, 25);
@@ -87,7 +87,7 @@ void DrillerEngine::loadAssetsAtariDemo() {
 
 	loadFonts(&file, 0x7bc);
 	loadMessagesFixedSize(&file, 0x3b90, 14, 20);
-	loadGlobalObjects(&file, 0x3946);
+	loadGlobalObjects(&file, 0x3946, 8);
 
 	file.close();
 	file.open("soundfx");
diff --git a/engines/freescape/games/driller/c64.cpp b/engines/freescape/games/driller/c64.cpp
index 45c32204c1c..e52decdc5a7 100644
--- a/engines/freescape/games/driller/c64.cpp
+++ b/engines/freescape/games/driller/c64.cpp
@@ -38,13 +38,13 @@ void DrillerEngine::loadAssetsC64FullGame() {
 		loadMessagesFixedSize(&file, 0x167a, 14, 20);
 		//loadFonts(&file, 0xae54);
 		load8bitBinary(&file, 0x8e02, 4);
-		loadGlobalObjects(&file, 0x1855);
+		loadGlobalObjects(&file, 0x1855, 8);
 	} else if (_targetName.hasPrefix("driller")) {
 		file.open("driller.c64.data");
 		loadMessagesFixedSize(&file, 0x167a - 0x400, 14, 20);
 		//loadFonts(&file, 0xae54);
 		load8bitBinary(&file, 0x8e02 - 0x400, 4);
-		loadGlobalObjects(&file, 0x1855 - 0x400);
+		loadGlobalObjects(&file, 0x1855 - 0x400, 8);
 	} else
 		error("Unknown C64 release");
 }
diff --git a/engines/freescape/games/driller/cpc.cpp b/engines/freescape/games/driller/cpc.cpp
index f441d8bc449..f9d95eef943 100644
--- a/engines/freescape/games/driller/cpc.cpp
+++ b/engines/freescape/games/driller/cpc.cpp
@@ -127,7 +127,7 @@ void DrillerEngine::loadAssetsCPCFullGame() {
 
 	loadMessagesFixedSize(&file, 0x214c, 14, 20);
 	loadFonts(&file, 0x5b69);
-	loadGlobalObjects(&file, 0x1d07);
+	loadGlobalObjects(&file, 0x1d07, 8);
 	load8bitBinary(&file, 0x5ccb, 16);
 }
 
diff --git a/engines/freescape/games/driller/dos.cpp b/engines/freescape/games/driller/dos.cpp
index cdd28d974c9..c96235196cf 100644
--- a/engines/freescape/games/driller/dos.cpp
+++ b/engines/freescape/games/driller/dos.cpp
@@ -286,7 +286,7 @@ void DrillerEngine::loadAssetsDOSFullGame() {
 
 		loadMessagesFixedSize(&file, 0x4135, 14, 20);
 		loadFonts(&file, 0x99dd);
-		loadGlobalObjects(&file, 0x3b42);
+		loadGlobalObjects(&file, 0x3b42, 8);
 		load8bitBinary(&file, 0x9b40, 16);
 		_border = load8bitBinImage(&file, 0x210);
 		_border->setPalette((byte*)&kEGADefaultPaletteData, 0, 16);
@@ -311,7 +311,7 @@ void DrillerEngine::loadAssetsDOSFullGame() {
 		loadFonts(&file, 0x07a4a);
 		loadMessagesFixedSize(&file, 0x2585, 14, 20);
 		load8bitBinary(&file, 0x7bb0, 4);
-		loadGlobalObjects(&file, 0x1fa2);
+		loadGlobalObjects(&file, 0x1fa2, 8);
 		_border = load8bitBinImage(&file, 0x210);
 		_border->setPalette((byte*)&kCGAPalettePinkBlueWhiteData, 0, 4);
 	} else
@@ -338,7 +338,7 @@ void DrillerEngine::loadAssetsDOSDemo() {
 	loadFonts(&file, 0x4eb0);
 	loadMessagesFixedSize(&file, 0x636, 14, 20);
 	load8bitBinary(&file, 0x55b0, 4);
-	loadGlobalObjects(&file, 0x8c);
+	loadGlobalObjects(&file, 0x8c, 5);
 	_border = load8bitDemoImage(&file, 0x6220);
 	_border->setPalette((byte*)&kCGAPalettePinkBlueWhiteData, 0, 4);
 
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 490912727d5..5144aa8ba11 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -186,25 +186,6 @@ void DrillerEngine::gotoArea(uint16 areaID, int entranceID) {
 	resetInput();
 }
 
-void DrillerEngine::loadGlobalObjects(Common::SeekableReadStream *file, int offset) {
-	assert(!_areaMap.contains(255));
-	ObjectMap *globalObjectsByID = new ObjectMap;
-	file->seek(offset);
-	for (int i = 0; i < 8; i++) {
-		if (isDOS() && isDemo()) // The DOS demo has a few missing objects
-			if (i == 5)
-				break;
-
-		Object *gobj = load8bitObject(file);
-		assert(gobj);
-		assert(!globalObjectsByID->contains(gobj->getObjectID()));
-		debugC(1, kFreescapeDebugParser, "Adding global object: %d", gobj->getObjectID());
-		(*globalObjectsByID)[gobj->getObjectID()] = gobj;
-	}
-
-	_areaMap[255] = new Area(255, 0, globalObjectsByID, nullptr);
-}
-
 void DrillerEngine::loadAssetsFullGame() {
 	FreescapeEngine::loadAssetsFullGame();
 	/*
diff --git a/engines/freescape/games/driller/zx.cpp b/engines/freescape/games/driller/zx.cpp
index 65e933b81dc..519832b5957 100644
--- a/engines/freescape/games/driller/zx.cpp
+++ b/engines/freescape/games/driller/zx.cpp
@@ -64,9 +64,9 @@ void DrillerEngine::loadAssetsZXFullGame() {
 		loadFonts(&file, 0x63f0);
 
 	if (_variant & GF_ZX_DISC)
-		loadGlobalObjects(&file, 0x1d13);
+		loadGlobalObjects(&file, 0x1d13, 8);
 	else
-		loadGlobalObjects(&file, 0x1c93);
+		loadGlobalObjects(&file, 0x1c93, 8);
 
 	if (_variant & GF_ZX_RETAIL)
 		load8bitBinary(&file, 0x642c, 4);
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index d9e1157be65..64fe016df02 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -801,4 +801,20 @@ void FreescapeEngine::loadMessagesVariableSize(Common::SeekableReadStream *file,
 	}
 }
 
+void FreescapeEngine::loadGlobalObjects(Common::SeekableReadStream *file, int offset, int size) {
+	assert(!_areaMap.contains(255));
+	ObjectMap *globalObjectsByID = new ObjectMap;
+	file->seek(offset);
+	for (int i = 0; i < size; i++) {
+		Object *gobj = load8bitObject(file);
+		assert(gobj);
+		assert(!globalObjectsByID->contains(gobj->getObjectID()));
+		debugC(1, kFreescapeDebugParser, "Adding global object: %d", gobj->getObjectID());
+		(*globalObjectsByID)[gobj->getObjectID()] = gobj;
+	}
+
+	_areaMap[255] = new Area(255, 0, globalObjectsByID, nullptr);
+}
+
+
 } // namespace Freescape


Commit: 578330a20fd58a19ea4a69b6f413a7b795d42505
    https://github.com/scummvm/scummvm/commit/578330a20fd58a19ea4a69b6f413a7b795d42505
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: initial implementation of the opengl shader renderer

Changed paths:
  A engines/freescape/gfx_opengl_shaders.cpp
  A engines/freescape/gfx_opengl_shaders.h
  A engines/freescape/shaders/bitmap.fragment
  A engines/freescape/shaders/bitmap.vertex
  A engines/freescape/shaders/triangle.fragment
  A engines/freescape/shaders/triangle.vertex
    engines/freescape/games/palettes.cpp
    engines/freescape/gfx.cpp
    engines/freescape/module.mk
    graphics/opengl/shader.cpp


diff --git a/engines/freescape/games/palettes.cpp b/engines/freescape/games/palettes.cpp
index 2947a6c6268..e2fe6b8e3c8 100644
--- a/engines/freescape/games/palettes.cpp
+++ b/engines/freescape/games/palettes.cpp
@@ -209,6 +209,9 @@ byte kDrillerCGAPaletteRedGreenData[4][3] = {
 };
 
 void FreescapeEngine::swapPalette(uint16 levelID) {
+	if (!_border)
+		return;
+
 	if (isAmiga() || isAtariST()) {
 		// The following palette was not available in the demo, so we select another one
 		if (isDemo() && levelID == 32)
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index 58068d0a0eb..b8b45a37179 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -932,6 +932,9 @@ Graphics::RendererType determinateRenderType() {
 #if defined(USE_OPENGL_GAME)
 						Graphics::kRendererTypeOpenGL |
 #endif
+#if defined(USE_OPENGL_SHADERS)
+			Graphics::kRendererTypeOpenGLShaders |
+#endif
 #if defined(USE_TINYGL)
 						Graphics::kRendererTypeTinyGL |
 #endif
@@ -947,6 +950,11 @@ Graphics::RendererType determinateRenderType() {
 			return matchingRendererType;
 	#endif
 
+	#if defined(USE_OPENGL_SHADERS)
+		if (matchingRendererType == Graphics::kRendererTypeOpenGLShaders)
+			return matchingRendererType;
+	#endif
+
 	#if defined(USE_TINYGL)
 		return Graphics::kRendererTypeTinyGL;
 	#endif
@@ -972,6 +980,12 @@ Renderer *createRenderer(int screenW, int screenH, Common::RenderMode renderMode
 		}
 	#endif
 
+	#if defined(USE_OPENGL_SHADERS)
+		if (rendererType == Graphics::kRendererTypeOpenGLShaders) {
+			return CreateGfxOpenGLShader(screenW, screenH, renderMode);
+		}
+	#endif
+
 	#if defined(USE_TINYGL)
 	if (rendererType == Graphics::kRendererTypeTinyGL) {
 		return CreateGfxTinyGL(screenW, screenH, renderMode);
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
new file mode 100644
index 00000000000..a30c22935db
--- /dev/null
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -0,0 +1,340 @@
+/* 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/>.
+ *
+ */
+
+// Based on Phantasma code by Thomas Harte (2013),
+// available at https://github.com/TomHarte/Phantasma/ (MIT)
+
+#include "common/config-manager.h"
+#include "common/math.h"
+#include "common/system.h"
+#include "math/glmath.h"
+
+#include "freescape/objects/object.h"
+#include "freescape/gfx_opengl_shaders.h"
+#include "freescape/gfx_opengl_texture.h"
+
+namespace Freescape {
+
+static const GLfloat bitmapVertices[] = {
+	// XS   YT
+	0.0, 0.0,
+	1.0, 0.0,
+	0.0, 1.0,
+	1.0, 1.0,
+};
+
+Renderer *CreateGfxOpenGLShader(int screenW, int screenH, Common::RenderMode renderMode) {
+	return new OpenGLShaderRenderer(screenW, screenH, renderMode);
+}
+
+OpenGLShaderRenderer::OpenGLShaderRenderer(int screenW, int screenH, Common::RenderMode renderMode) : Renderer(screenW, screenH, renderMode) {
+	_triangleShader = nullptr;
+	_triangleVBO = 0;
+
+	_bitmapShader = nullptr;
+	_bitmapVBO = 0;
+
+	_texturePixelFormat = OpenGLTexture::getRGBAPixelFormat();
+	_isAccelerated = true;
+}
+
+OpenGLShaderRenderer::~OpenGLShaderRenderer() {
+	OpenGL::Shader::freeBuffer(_triangleVBO);
+	delete _triangleShader;
+}
+
+Texture *OpenGLShaderRenderer::createTexture(const Graphics::Surface *surface) {
+	return new OpenGLTexture(surface);
+}
+
+void OpenGLShaderRenderer::freeTexture(Texture *texture) {
+	delete texture;
+}
+
+void OpenGLShaderRenderer::init() {
+	computeScreenViewport();
+
+	_verts = (Vertex *)malloc(sizeof(Vertex) * kVertexArraySize);
+
+	static const char *triangleAttributes[] = { "position", nullptr };
+	_triangleShader = OpenGL::Shader::fromFiles("triangle", triangleAttributes);
+	_triangleVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(Vertex) * kVertexArraySize, _verts, GL_DYNAMIC_DRAW);
+	// TODO: Check if 3 * sizeof(float) == sizeof(Vertex)
+	_triangleShader->enableVertexAttribute("position", _triangleVBO, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
+
+	static const char *bitmapAttributes[] = { "position", "texcoord", nullptr };
+	_bitmapShader = OpenGL::Shader::fromFiles("bitmap", bitmapAttributes);
+	_bitmapVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(bitmapVertices), bitmapVertices);
+	_bitmapShader->enableVertexAttribute("position", _bitmapVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
+	_bitmapShader->enableVertexAttribute("texcoord", _bitmapVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
+
+	glDisable(GL_LIGHTING);
+	glDisable(GL_TEXTURE_2D);
+	glEnable(GL_DEPTH_TEST);
+	glEnable(GL_SCISSOR_TEST);
+	setViewport(_viewport);
+}
+
+void OpenGLShaderRenderer::setViewport(const Common::Rect &rect) {
+	_viewport = Common::Rect(
+					_screenViewport.width() * rect.width() / _screenW,
+					_screenViewport.height() * rect.height() / _screenH
+					);
+
+	_viewport.translate(
+					_screenViewport.left + _screenViewport.width() * rect.left / _screenW,
+					_screenViewport.top + _screenViewport.height() * rect.top / _screenH
+					);
+
+	_unscaledViewport = rect;
+	glViewport(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height());
+	glScissor(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height());
+}
+
+void OpenGLShaderRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Common::Rect &textureRect, Texture *texture) {
+	OpenGLTexture *glTexture = static_cast<OpenGLTexture *>(texture);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	glEnable(GL_BLEND);
+
+	_bitmapShader->use();
+	_bitmapShader->setUniform("flipY", glTexture->_upsideDown);
+
+	glDepthMask(GL_FALSE);
+
+	glBindTexture(GL_TEXTURE_2D, glTexture->_id);
+
+	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+	glDisable(GL_BLEND);
+	glDepthMask(GL_TRUE);
+	_bitmapShader->unbind();
+}
+
+void OpenGLShaderRenderer::updateProjectionMatrix(float fov, float nearClipPlane, float farClipPlane) {
+	float aspectRatio = _screenW / (float)_screenH;
+	float xmaxValue = nearClipPlane * tan(Common::deg2rad(fov) / 2);
+	float ymaxValue = xmaxValue / aspectRatio;
+	_projectionMatrix = Math::makeFrustumMatrix(xmaxValue, -xmaxValue, -ymaxValue, ymaxValue, nearClipPlane, farClipPlane);
+}
+
+void OpenGLShaderRenderer::positionCamera(const Math::Vector3d &pos, const Math::Vector3d &interest) {
+	Math::Vector3d up_vec(0, 1, 0);
+
+	Math::Matrix4 lookMatrix = Math::makeLookAtMatrix(pos, interest, up_vec);
+	Math::Matrix4 viewMatrix;
+	viewMatrix.translate(-pos);
+    viewMatrix.transpose();
+
+	_modelViewMatrix = viewMatrix * lookMatrix;
+
+	Math::Matrix4 proj = _projectionMatrix;
+	Math::Matrix4 model = _modelViewMatrix;
+	proj.transpose();
+	model.transpose();
+	_mvpMatrix = proj * model;
+	_mvpMatrix.transpose();
+}
+
+void OpenGLShaderRenderer::renderSensorShoot(byte color, const Math::Vector3d sensor, const Math::Vector3d player, const Common::Rect viewArea) {
+	/*glColor3ub(255, 255, 255);
+	glLineWidth(20);
+	polygonOffset(true);
+	glEnableClientState(GL_VERTEX_ARRAY);
+	copyToVertexArray(0, sensor);
+	copyToVertexArray(1, player);
+	glVertexPointer(3, GL_FLOAT, 0, _verts);
+	glDrawArrays(GL_LINES, 0, 2);
+	glDisableClientState(GL_VERTEX_ARRAY);
+	polygonOffset(false);
+	glLineWidth(1);*/
+}
+
+void OpenGLShaderRenderer::renderPlayerShoot(byte color, const Common::Point position, const Common::Rect viewArea) {
+	/*uint8 a, r, g, b;
+
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0, _screenW, _screenH, 0, 0, 1);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderZX) {
+		r = g = b = 255;
+	} else {
+		uint32 pixel = 0x0;
+		glReadPixels(g_system->getWidth() / 2, g_system->getHeight() / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
+		_texturePixelFormat.colorToARGB(pixel, a, r, g, b);
+		color = indexFromColor(r, g, b);
+		readFromPalette((color + 3) % (_renderMode == Common::kRenderCGA ? 4 : 16), r, g, b);
+	}
+
+	glDisable(GL_DEPTH_TEST);
+	glDepthMask(GL_FALSE);
+
+	glColor3ub(r, g, b);
+
+	glLineWidth(5); // It will not work in every OpenGL implementation since the
+					 // spec doesn't require support for line widths other than 1
+	glEnableClientState(GL_VERTEX_ARRAY);
+	copyToVertexArray(0, Math::Vector3d(viewArea.left, viewArea.height() + viewArea.top, 0));
+	copyToVertexArray(1, Math::Vector3d(position.x, position.y, 0));
+	copyToVertexArray(2, Math::Vector3d(viewArea.left, viewArea.height() + viewArea.top + 3, 0));
+	copyToVertexArray(3, Math::Vector3d(position.x, position.y, 0));
+
+	copyToVertexArray(4, Math::Vector3d(viewArea.right, viewArea.height() + viewArea.top, 0));
+	copyToVertexArray(5, Math::Vector3d(position.x, position.y, 0));
+	copyToVertexArray(6, Math::Vector3d(viewArea.right, viewArea.height() + viewArea.top + 3, 0));
+	copyToVertexArray(7, Math::Vector3d(position.x, position.y, 0));
+
+	glVertexPointer(3, GL_FLOAT, 0, _verts);
+	glDrawArrays(GL_LINES, 0, 8);
+	glDisableClientState(GL_VERTEX_ARRAY);
+	glLineWidth(1);
+
+	glEnable(GL_DEPTH_TEST);
+	glDepthMask(GL_TRUE);*/
+}
+
+void OpenGLShaderRenderer::renderFace(const Common::Array<Math::Vector3d> &vertices) {
+	assert(vertices.size() >= 2);
+	const Math::Vector3d &v0 = vertices[0];
+
+	/*if (vertices.size() == 2) {
+		const Math::Vector3d &v1 = vertices[1];
+		if (v0 == v1)
+			return;
+
+		glEnableClientState(GL_VERTEX_ARRAY);
+		copyToVertexArray(0, v0);
+		copyToVertexArray(1, v1);
+		glVertexPointer(3, GL_FLOAT, 0, _verts);
+		glLineWidth(MAX(1, g_system->getWidth() / 192));
+		glDrawArrays(GL_LINES, 0, 2);
+		glLineWidth(1);
+		glDisableClientState(GL_VERTEX_ARRAY);
+		return;
+	}*/
+
+	//glEnableClientState(GL_VERTEX_ARRAY);
+	_triangleShader->use();
+	_triangleShader->setUniform("mvpMatrix", _mvpMatrix);
+	uint vi = 0;
+	for (uint i = 1; i < vertices.size() - 1; i++) { // no underflow since vertices.size() > 2
+		const Math::Vector3d &v1 = vertices[i];
+		const Math::Vector3d &v2 = vertices[i + 1];
+		vi = 3 * (i - 1); // no underflow since i >= 1
+		copyToVertexArray(vi + 0, v0);
+		copyToVertexArray(vi + 1, v1);
+		copyToVertexArray(vi + 2, v2);
+	}
+	glBindBuffer(GL_ARRAY_BUFFER, _triangleVBO);
+	glBufferData(GL_ARRAY_BUFFER, (vi + 3) * 3 * sizeof(float), _verts, GL_DYNAMIC_DRAW);
+	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
+	//glEnableVertexAttribArray(0);
+
+	glDrawArrays(GL_TRIANGLES, 0, vi + 3);
+	//glDisableVertexAttribArray(0);
+	//glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+void OpenGLShaderRenderer::polygonOffset(bool enabled) {
+	if (enabled) {
+		glEnable(GL_POLYGON_OFFSET_FILL);
+		glPolygonOffset(-10.0f, 1.0f);
+	} else {
+		glPolygonOffset(0, 0);
+		glDisable(GL_POLYGON_OFFSET_FILL);
+	}
+}
+
+void OpenGLShaderRenderer::setStippleData(byte *data) {
+	if (!data)
+		return;
+
+	//_variableStippleArray = data;
+	//for (int i = 0; i < 128; i++)
+	//	_variableStippleArray[i] = data[(i / 16) % 4];
+}
+
+void OpenGLShaderRenderer::useStipple(bool enabled) {
+	/*if (enabled) {
+		glEnable(GL_POLYGON_OFFSET_FILL);
+		glPolygonOffset(0.0f, -1.0f);
+		glEnable(GL_POLYGON_STIPPLE);
+		if (_renderMode == Common::kRenderZX  ||
+			_renderMode == Common::kRenderCPC ||
+			_renderMode == Common::kRenderCGA)
+			glPolygonStipple(_variableStippleArray);
+		else
+			glPolygonStipple(_defaultStippleArray);
+	} else {
+		glPolygonOffset(0, 0);
+		glDisable(GL_POLYGON_OFFSET_FILL);
+		glDisable(GL_POLYGON_STIPPLE);
+	}*/
+}
+
+void OpenGLShaderRenderer::useColor(uint8 r, uint8 g, uint8 b) {
+	Math::Vector3d color(r / 256.0, g / 256.0, b / 256.0);
+	_triangleShader->use();
+	_triangleShader->setUniform("color", color);
+}
+
+void OpenGLShaderRenderer::clear(uint8 color) {
+	uint8 r, g, b;
+
+	if (_colorRemaps && _colorRemaps->contains(color)) {
+		color = (*_colorRemaps)[color];
+	}
+
+	readFromPalette(color, r, g, b);
+	glClearColor(0, 0, 0, 1.0);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+void OpenGLShaderRenderer::drawFloor(uint8 color) {
+	/*uint8 r1, g1, b1, r2, g2, b2;
+	byte *stipple;
+	assert(getRGBAt(color, r1, g1, b1, r2, g2, b2, stipple)); // TODO: move check inside this function
+	glColor3ub(r1, g1, b1);
+
+	glEnableClientState(GL_VERTEX_ARRAY);
+	copyToVertexArray(0, Math::Vector3d(-100000.0, 0.0, -100000.0));
+	copyToVertexArray(1, Math::Vector3d(100000.0, 0.0, -100000.0));
+	copyToVertexArray(2, Math::Vector3d(100000.0, 0.0, 100000.0));
+	copyToVertexArray(3, Math::Vector3d(-100000.0, 0.0, 100000.0));
+	glVertexPointer(3, GL_FLOAT, 0, _verts);
+	glDrawArrays(GL_QUADS, 0, 4);
+	glDisableClientState(GL_VERTEX_ARRAY);*/
+}
+
+void OpenGLShaderRenderer::flipBuffer() {}
+
+Graphics::Surface *OpenGLShaderRenderer::getScreenshot() {
+	Common::Rect screen = viewport();
+	Graphics::Surface *s = new Graphics::Surface();
+	s->create(screen.width(), screen.height(), OpenGLTexture::getRGBAPixelFormat());
+	glReadPixels(screen.left, screen.top, screen.width(), screen.height(), GL_RGBA, GL_UNSIGNED_BYTE, s->getPixels());
+	flipVertical(s);
+	return s;
+	return nullptr;
+}
+
+} // End of namespace Freescape
diff --git a/engines/freescape/gfx_opengl_shaders.h b/engines/freescape/gfx_opengl_shaders.h
new file mode 100644
index 00000000000..1084a8fb1cb
--- /dev/null
+++ b/engines/freescape/gfx_opengl_shaders.h
@@ -0,0 +1,100 @@
+/* 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 FREESCAPE_GFX_OPENGL_SHADERS_H
+#define FREESCAPE_GFX_OPENGL_SHADERS_H
+
+#include "graphics/opengl/shader.h"
+#include "graphics/opengl/system_headers.h"
+#include "math/vector3d.h"
+#include "math/vector2d.h"
+
+#include "freescape/gfx.h"
+
+namespace Freescape {
+
+class OpenGLShaderRenderer : public Renderer {
+public:
+	OpenGLShaderRenderer(int screenW, int screenH, Common::RenderMode renderMode);
+	virtual ~OpenGLShaderRenderer();
+
+	Math::Matrix4 _projectionMatrix;
+	Math::Matrix4 _modelViewMatrix;
+	Math::Matrix4 _mvpMatrix;
+
+	struct Vertex {
+		GLfloat x;
+		GLfloat y;
+		GLfloat z;
+	};
+
+	void copyToVertexArray(uint idx, const Math::Vector3d &src) {
+		assert(idx < kVertexArraySize);
+		_verts[idx].x = src.x(); _verts[idx].y = src.y(); _verts[idx].z = src.z();
+	}
+
+	Vertex *_verts;
+
+	struct Coord {
+		GLfloat x;
+		GLfloat y;
+	};
+
+	/*Coord *_coords;
+
+	void copyToCoordArray(uint idx, const Math::Vector2d &src) {
+		assert(idx < kCoordsArraySize);
+		_coords[idx].x = src.getValue(0); _coords[idx].y = src.getValue(1);
+	}*/
+
+	OpenGL::Shader *_triangleShader;
+	OpenGL::Shader *_bitmapShader;
+	GLuint _triangleVBO;
+	GLuint _bitmapVBO;
+
+	virtual void init() override;
+	virtual void clear(uint8 color) override;
+	virtual void setViewport(const Common::Rect &rect) override;
+	virtual void positionCamera(const Math::Vector3d &pos, const Math::Vector3d &interest) override;
+	virtual void updateProjectionMatrix(float fov, float nearClipPlane, float farClipPlane) override;
+
+	virtual void useColor(uint8 r, uint8 g, uint8 b) override;
+	virtual void polygonOffset(bool enabled) override;
+	virtual void setStippleData(byte *data) override;
+	virtual void useStipple(bool enabled) override;
+
+	Texture *createTexture(const Graphics::Surface *surface) override;
+	void freeTexture(Texture *texture) override;
+	virtual void drawTexturedRect2D(const Common::Rect &screenRect, const Common::Rect &textureRect, Texture *texture) override;
+
+	virtual void renderSensorShoot(byte color, const Math::Vector3d sensor, const Math::Vector3d player, const Common::Rect viewPort) override;
+	virtual void renderPlayerShoot(byte color, const Common::Point position, const Common::Rect viewPort) override;
+	virtual void renderFace(const Common::Array<Math::Vector3d> &vertices) override;
+
+	virtual void flipBuffer() override;
+	virtual void drawFloor(uint8 color) override;
+
+	virtual Graphics::Surface *getScreenshot() override;
+};
+
+} // End of namespace Freescape
+
+#endif // FREESCAPE_GFX_OPENGL_SHADERS_H
diff --git a/engines/freescape/module.mk b/engines/freescape/module.mk
index 0695e5501b3..fe3a2a95cda 100644
--- a/engines/freescape/module.mk
+++ b/engines/freescape/module.mk
@@ -36,6 +36,11 @@ MODULE_OBJS += \
 	gfx_tinygl_texture.o
 endif
 
+ifdef USE_OPENGL_SHADERS
+MODULE_OBJS += \
+	gfx_opengl_shaders.o
+endif
+
 ifdef USE_OPENGL
 MODULE_OBJS += \
 	gfx_opengl.o \
diff --git a/engines/freescape/shaders/bitmap.fragment b/engines/freescape/shaders/bitmap.fragment
new file mode 100644
index 00000000000..ac293f3685d
--- /dev/null
+++ b/engines/freescape/shaders/bitmap.fragment
@@ -0,0 +1,9 @@
+in vec2 Texcoord;
+
+OUTPUT
+
+uniform sampler2D tex;
+
+void main() {
+	outColor = texture(tex, Texcoord);
+}
diff --git a/engines/freescape/shaders/bitmap.vertex b/engines/freescape/shaders/bitmap.vertex
new file mode 100644
index 00000000000..7362dae979c
--- /dev/null
+++ b/engines/freescape/shaders/bitmap.vertex
@@ -0,0 +1,21 @@
+in vec2 position;
+in vec2 texcoord;
+
+uniform UBOOL flipY;
+
+out vec2 Texcoord;
+
+void main() {
+	Texcoord = texcoord;
+
+	vec2 pos = position;
+	// Coordinates are [0.0;1.0], transform to [-1.0; 1.0]
+	pos.x = pos.x * 2.0 - 1.0;
+	pos.y = -1.0 * (pos.y * 2.0 - 1.0);
+
+	if (UBOOL_TEST(flipY)) {
+		pos.y *= -1.0;
+	}
+
+	gl_Position = vec4(pos, 0.0, 1.0);
+}
diff --git a/engines/freescape/shaders/triangle.fragment b/engines/freescape/shaders/triangle.fragment
new file mode 100644
index 00000000000..aaf98049c01
--- /dev/null
+++ b/engines/freescape/shaders/triangle.fragment
@@ -0,0 +1,8 @@
+OUTPUT
+
+varying vec4 var_color;
+
+void main()
+{
+    outColor = var_color;
+}
\ No newline at end of file
diff --git a/engines/freescape/shaders/triangle.vertex b/engines/freescape/shaders/triangle.vertex
new file mode 100644
index 00000000000..6483e9a8adb
--- /dev/null
+++ b/engines/freescape/shaders/triangle.vertex
@@ -0,0 +1,12 @@
+in vec3 position;
+
+uniform mat4 mvpMatrix;
+uniform vec3 color;
+
+varying vec4 var_color;
+
+void main()
+{
+	var_color = vec4(color, 1.0);
+    gl_Position = mvpMatrix * vec4(position, 1.0);
+}
\ No newline at end of file
diff --git a/graphics/opengl/shader.cpp b/graphics/opengl/shader.cpp
index 652e4cac3b3..99f23e3c14a 100644
--- a/graphics/opengl/shader.cpp
+++ b/graphics/opengl/shader.cpp
@@ -92,6 +92,7 @@ static const GLchar *readFile(const Common::String &filename) {
 	SearchMan.addDirectory("STARK_SHADERS", "engines/stark", 0, 2);
 	SearchMan.addDirectory("WINTERMUTE_SHADERS", "engines/wintermute/base/gfx/opengl", 0, 2);
 	SearchMan.addDirectory("PLAYGROUND3D_SHADERS", "engines/playground3d", 0, 2);
+	SearchMan.addDirectory("FREESCAPE_SHADERS", "engines/freescape", 0, 2);
 	SearchMan.addDirectory("HPL1_SHADERS", "engines/hpl1/engine/impl", 0, 2);
 #endif
 
@@ -111,6 +112,7 @@ static const GLchar *readFile(const Common::String &filename) {
 	SearchMan.remove("STARK_SHADERS");
 	SearchMan.remove("WINTERMUTE_SHADERS");
 	SearchMan.remove("PLAYGROUND3D_SHADERS");
+	SearchMan.remove("FREESCAPE_SHADERS");
 	SearchMan.remove("HPL1_SHADERS");
 #endif
 


Commit: 1c763a01f95c4386a788f86f87efba8421392024
    https://github.com/scummvm/scummvm/commit/1c763a01f95c4386a788f86f87efba8421392024
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: renamed shaders filenames and added them into shader.dat

Changed paths:
  A engines/freescape/shaders/freescape_bitmap.fragment
  A engines/freescape/shaders/freescape_bitmap.vertex
  A engines/freescape/shaders/freescape_triangle.fragment
  A engines/freescape/shaders/freescape_triangle.vertex
  R engines/freescape/shaders/bitmap.fragment
  R engines/freescape/shaders/bitmap.vertex
  R engines/freescape/shaders/triangle.fragment
  R engines/freescape/shaders/triangle.vertex
    Makefile.common
    devtools/create_project/xcode.cpp
    dists/scummvm.rc
    engines/freescape/gfx_opengl_shaders.cpp


diff --git a/Makefile.common b/Makefile.common
index d240c8f1a31..bca64b067eb 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -513,6 +513,9 @@ endif
 ifdef ENABLE_HPL1
 DIST_FILES_SHADERS+=$(wildcard $(srcdir)/engines/hpl1/engine/impl/shaders/*)
 endif
+ifdef ENABLE_FREESCAPE
+DIST_FILES_SHADERS+=$(wildcard $(srcdir)/engines/freescape/shaders/*)
+endif
 endif
 
 .PHONY: all clean distclean plugins dist-src clean-toplevel manual
diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp
index 82a99370d1f..63cf09da57e 100644
--- a/devtools/create_project/xcode.cpp
+++ b/devtools/create_project/xcode.cpp
@@ -1105,6 +1105,12 @@ XcodeProvider::ValueList& XcodeProvider::getResourceFiles(const BuildSetup &setu
 			files.push_back("engines/wintermute/base/gfx/opengl/shaders/wme_sprite.fragment");
 			files.push_back("engines/wintermute/base/gfx/opengl/shaders/wme_sprite.vertex");
 		}
+		if (CONTAINS_DEFINE(setup.defines, "ENABLE_FREESCAPE")) {
+			files.push_back("engines/freescape/shaders/freescape_bitmap.fragment");
+			files.push_back("engines/freescape/shaders/freescape_bitmap.vertex");
+			files.push_back("engines/freescape/shaders/freescape_triangle.fragment");
+			files.push_back("engines/freescape/shaders/freescape_triange.vertex");
+		}
 		files.push_back("icons/scummvm.icns");
 		files.push_back("AUTHORS");
 		files.push_back("COPYING");
diff --git a/dists/scummvm.rc b/dists/scummvm.rc
index 9f3f801d0fe..047a37e990d 100644
--- a/dists/scummvm.rc
+++ b/dists/scummvm.rc
@@ -273,6 +273,12 @@ shaders/hpl1_refract_special.fragment                  FILE   "engines/hpl1/engi
 shaders/hpl1_refract_water.fragment                    FILE   "engines/hpl1/engine/impl/shaders/hpl1_refract_water.fragment"
 shaders/hpl1_refract_water.vertex                      FILE   "engines/hpl1/engine/impl/shaders/hpl1_refract_water.vertex"
 #endif
+#if PLUGIN_ENABLED_STATIC(FREESCAPE)
+shaders/freescape_bitmap.fragment    FILE    "engines/freescape/shaders/freescape_bitmap.fragment"
+shaders/freescape_bitmap.vertex      FILE    "engines/freescape/shaders/freescape_bitmap.vertex"
+shaders/freescape_triangle.fragment  FILE    "engines/freescape/shaders/freescape_cube.fragment"
+shaders/freescape_triangle.vertex    FILE    "engines/freescape/shaders/freescape_cube.vertex"
+#endif
 #endif
 #endif
 
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index a30c22935db..b6799bbfcfa 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -75,13 +75,13 @@ void OpenGLShaderRenderer::init() {
 	_verts = (Vertex *)malloc(sizeof(Vertex) * kVertexArraySize);
 
 	static const char *triangleAttributes[] = { "position", nullptr };
-	_triangleShader = OpenGL::Shader::fromFiles("triangle", triangleAttributes);
+	_triangleShader = OpenGL::Shader::fromFiles("freescape_triangle", triangleAttributes);
 	_triangleVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(Vertex) * kVertexArraySize, _verts, GL_DYNAMIC_DRAW);
 	// TODO: Check if 3 * sizeof(float) == sizeof(Vertex)
 	_triangleShader->enableVertexAttribute("position", _triangleVBO, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
 
 	static const char *bitmapAttributes[] = { "position", "texcoord", nullptr };
-	_bitmapShader = OpenGL::Shader::fromFiles("bitmap", bitmapAttributes);
+	_bitmapShader = OpenGL::Shader::fromFiles("freescape_bitmap", bitmapAttributes);
 	_bitmapVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(bitmapVertices), bitmapVertices);
 	_bitmapShader->enableVertexAttribute("position", _bitmapVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
 	_bitmapShader->enableVertexAttribute("texcoord", _bitmapVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
diff --git a/engines/freescape/shaders/bitmap.fragment b/engines/freescape/shaders/freescape_bitmap.fragment
similarity index 100%
rename from engines/freescape/shaders/bitmap.fragment
rename to engines/freescape/shaders/freescape_bitmap.fragment
diff --git a/engines/freescape/shaders/bitmap.vertex b/engines/freescape/shaders/freescape_bitmap.vertex
similarity index 100%
rename from engines/freescape/shaders/bitmap.vertex
rename to engines/freescape/shaders/freescape_bitmap.vertex
diff --git a/engines/freescape/shaders/triangle.fragment b/engines/freescape/shaders/freescape_triangle.fragment
similarity index 100%
rename from engines/freescape/shaders/triangle.fragment
rename to engines/freescape/shaders/freescape_triangle.fragment
diff --git a/engines/freescape/shaders/triangle.vertex b/engines/freescape/shaders/freescape_triangle.vertex
similarity index 100%
rename from engines/freescape/shaders/triangle.vertex
rename to engines/freescape/shaders/freescape_triangle.vertex


Commit: 7049d28f3bcffccd8c9a22215af2cec422a9edeb
    https://github.com/scummvm/scummvm/commit/7049d28f3bcffccd8c9a22215af2cec422a9edeb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: implemented lines in shader renderer

Changed paths:
    engines/freescape/gfx_opengl_shaders.cpp


diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index b6799bbfcfa..e62e57d9e59 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -216,25 +216,28 @@ void OpenGLShaderRenderer::renderFace(const Common::Array<Math::Vector3d> &verti
 	assert(vertices.size() >= 2);
 	const Math::Vector3d &v0 = vertices[0];
 
-	/*if (vertices.size() == 2) {
+	_triangleShader->use();
+	_triangleShader->setUniform("mvpMatrix", _mvpMatrix);
+
+	if (vertices.size() == 2) {
 		const Math::Vector3d &v1 = vertices[1];
 		if (v0 == v1)
 			return;
 
-		glEnableClientState(GL_VERTEX_ARRAY);
 		copyToVertexArray(0, v0);
 		copyToVertexArray(1, v1);
-		glVertexPointer(3, GL_FLOAT, 0, _verts);
+
 		glLineWidth(MAX(1, g_system->getWidth() / 192));
+
+		glBindBuffer(GL_ARRAY_BUFFER, _triangleVBO);
+		glBufferData(GL_ARRAY_BUFFER, 2 * 3 * sizeof(float), _verts, GL_DYNAMIC_DRAW);
+		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
 		glDrawArrays(GL_LINES, 0, 2);
+
 		glLineWidth(1);
-		glDisableClientState(GL_VERTEX_ARRAY);
 		return;
-	}*/
+	}
 
-	//glEnableClientState(GL_VERTEX_ARRAY);
-	_triangleShader->use();
-	_triangleShader->setUniform("mvpMatrix", _mvpMatrix);
 	uint vi = 0;
 	for (uint i = 1; i < vertices.size() - 1; i++) { // no underflow since vertices.size() > 2
 		const Math::Vector3d &v1 = vertices[i];
@@ -247,11 +250,8 @@ void OpenGLShaderRenderer::renderFace(const Common::Array<Math::Vector3d> &verti
 	glBindBuffer(GL_ARRAY_BUFFER, _triangleVBO);
 	glBufferData(GL_ARRAY_BUFFER, (vi + 3) * 3 * sizeof(float), _verts, GL_DYNAMIC_DRAW);
 	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
-	//glEnableVertexAttribArray(0);
 
 	glDrawArrays(GL_TRIANGLES, 0, vi + 3);
-	//glDisableVertexAttribArray(0);
-	//glDisableClientState(GL_VERTEX_ARRAY);
 }
 
 void OpenGLShaderRenderer::polygonOffset(bool enabled) {


Commit: 8ceaf7831e32a13929b4c51e3d5eca66ba5a4780
    https://github.com/scummvm/scummvm/commit/8ceaf7831e32a13929b4c51e3d5eca66ba5a4780
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: implemented player shoot in shader renderer

Changed paths:
    engines/freescape/gfx_opengl_shaders.cpp


diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index e62e57d9e59..1a1d0717074 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -167,14 +167,23 @@ void OpenGLShaderRenderer::renderSensorShoot(byte color, const Math::Vector3d se
 	glLineWidth(1);*/
 }
 
+// TODO: move inside the shader?
+float remap(float f, float s) {
+	return 2. * f / s - 1;
+}
+
 void OpenGLShaderRenderer::renderPlayerShoot(byte color, const Common::Point position, const Common::Rect viewArea) {
-	/*uint8 a, r, g, b;
+	uint8 a, r, g, b;
+
+	Math::Matrix4 identity;
+	identity(0, 0) = 1.0;
+	identity(1, 1) = 1.0;
+	identity(2, 2) = 1.0;
+	identity(3, 3) = 1.0;
+
+	_triangleShader->use();
+	_triangleShader->setUniform("mvpMatrix", identity);
 
-	glMatrixMode(GL_PROJECTION);
-	glLoadIdentity();
-	glOrtho(0, _screenW, _screenH, 0, 0, 1);
-	glMatrixMode(GL_MODELVIEW);
-	glLoadIdentity();
 	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderZX) {
 		r = g = b = 255;
 	} else {
@@ -188,28 +197,31 @@ void OpenGLShaderRenderer::renderPlayerShoot(byte color, const Common::Point pos
 	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
-	glColor3ub(r, g, b);
+	useColor(r, g, b);
 
 	glLineWidth(5); // It will not work in every OpenGL implementation since the
 					 // spec doesn't require support for line widths other than 1
-	glEnableClientState(GL_VERTEX_ARRAY);
-	copyToVertexArray(0, Math::Vector3d(viewArea.left, viewArea.height() + viewArea.top, 0));
-	copyToVertexArray(1, Math::Vector3d(position.x, position.y, 0));
-	copyToVertexArray(2, Math::Vector3d(viewArea.left, viewArea.height() + viewArea.top + 3, 0));
-	copyToVertexArray(3, Math::Vector3d(position.x, position.y, 0));
+	copyToVertexArray(0, Math::Vector3d(remap(viewArea.left, _screenW), remap(viewArea.height() - viewArea.top,  _screenH), 0));
+	copyToVertexArray(1, Math::Vector3d(remap(position.x,  _screenW), remap(_screenH - position.y, _screenH), 0));
 
-	copyToVertexArray(4, Math::Vector3d(viewArea.right, viewArea.height() + viewArea.top, 0));
-	copyToVertexArray(5, Math::Vector3d(position.x, position.y, 0));
-	copyToVertexArray(6, Math::Vector3d(viewArea.right, viewArea.height() + viewArea.top + 3, 0));
-	copyToVertexArray(7, Math::Vector3d(position.x, position.y, 0));
+	copyToVertexArray(2, Math::Vector3d(remap(viewArea.left, _screenW), remap(viewArea.height() - viewArea.top + 3, _screenH), 0));
+	copyToVertexArray(3, Math::Vector3d(remap(position.x, _screenW), remap(_screenH - position.y, _screenH), 0));
 
-	glVertexPointer(3, GL_FLOAT, 0, _verts);
+	copyToVertexArray(4, Math::Vector3d(remap(viewArea.right, _screenW), remap(_screenH - viewArea.bottom, _screenH), 0));
+	copyToVertexArray(5, Math::Vector3d(remap(position.x,  _screenW), remap(_screenH - position.y, _screenH), 0));
+
+	copyToVertexArray(6, Math::Vector3d(remap(viewArea.right, _screenW), remap(_screenH - viewArea.bottom + 3, _screenH), 0));
+	copyToVertexArray(7, Math::Vector3d(remap(position.x,  _screenW), remap(_screenH - position.y, _screenH), 0));
+
+	glBindBuffer(GL_ARRAY_BUFFER, _triangleVBO);
+	glBufferData(GL_ARRAY_BUFFER, 8 * 3 * sizeof(float), _verts, GL_DYNAMIC_DRAW);
+	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
 	glDrawArrays(GL_LINES, 0, 8);
-	glDisableClientState(GL_VERTEX_ARRAY);
+
 	glLineWidth(1);
 
 	glEnable(GL_DEPTH_TEST);
-	glDepthMask(GL_TRUE);*/
+	glDepthMask(GL_TRUE);
 }
 
 void OpenGLShaderRenderer::renderFace(const Common::Array<Math::Vector3d> &vertices) {


Commit: 119405cfb852063ff56aa565a748a3add239786e
    https://github.com/scummvm/scummvm/commit/119405cfb852063ff56aa565a748a3add239786e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: correctly deallocate shaders memory

Changed paths:
    engines/freescape/gfx_opengl_shaders.cpp


diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index 1a1d0717074..9e067cfd426 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -59,6 +59,9 @@ OpenGLShaderRenderer::OpenGLShaderRenderer(int screenW, int screenH, Common::Ren
 OpenGLShaderRenderer::~OpenGLShaderRenderer() {
 	OpenGL::Shader::freeBuffer(_triangleVBO);
 	delete _triangleShader;
+	OpenGL::Shader::freeBuffer(_bitmapVBO);
+	delete _bitmapShader;
+	free(_verts);
 }
 
 Texture *OpenGLShaderRenderer::createTexture(const Graphics::Surface *surface) {


Commit: e6d694687198266ff0fa1696276932c36c0a5f57
    https://github.com/scummvm/scummvm/commit/e6d694687198266ff0fa1696276932c36c0a5f57
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: loading of title and border images in castle for dos

Changed paths:
    engines/freescape/freescape.h
    engines/freescape/games/castle.cpp


diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index ccb3af3942a..25a0c6ff0d1 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -464,20 +464,14 @@ private:
 	void addSkanner(Area *area);
 
 	void loadAssetsFullGame() override;
-
 	void loadAssetsAtariFullGame() override;
 	void loadAssetsAtariDemo() override;
-
 	void loadAssetsAmigaFullGame() override;
 	void loadAssetsAmigaDemo() override;
-
 	void loadAssetsDOSFullGame() override;
 	void loadAssetsDOSDemo() override;
-
 	void loadAssetsZXFullGame() override;
-
 	void loadAssetsCPCFullGame() override;
-
 	void loadAssetsC64FullGame() override;
 
 	void drawDOSUI(Graphics::Surface *surface) override;
@@ -560,6 +554,7 @@ class CastleEngine : public FreescapeEngine {
 public:
 	CastleEngine(OSystem *syst, const ADGameDescription *gd);
 
+	void titleScreen() override;
 	void loadAssetsDOSFullGame() override;
 
 	void gotoArea(uint16 areaID, int entranceID) override;
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index e81afcff921..04c95a7b2cc 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -56,19 +56,36 @@ Common::SeekableReadStream *CastleEngine::decryptFile(const Common::String filen
 	return (new Common::MemoryReadStream(encryptedBuffer, size));
 }
 
+extern byte kEGADefaultPaletteData[16][3];
+
 void CastleEngine::loadAssetsDOSFullGame() {
+	Common::File file;
 	Common::SeekableReadStream *stream = nullptr;
 
-	stream = decryptFile("CMLE");
-	loadMessagesVariableSize(stream, 0x11, 164);
-	delete stream;
+	if (_renderMode == Common::kRenderEGA) {
+		file.open("CMOE.DAT");
+		_title = load8bitBinImage(&file, 0x0);
+		_title->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
+
+		file.close();
+
+		file.open("CME.DAT");
+		_border = load8bitBinImage(&file, 0x0);
+		_border->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
+
+		file.close();
 
-	stream = decryptFile("CMEDF");
-	load8bitBinary(stream, 0, 16);
-	for (auto &it : _areaMap)
-		it._value->addStructure(_areaMap[255]);
-	delete stream;
+		stream = decryptFile("CMLE");
+		loadMessagesVariableSize(stream, 0x11, 164);
+		delete stream;
 
+		stream = decryptFile("CMEDF");
+		load8bitBinary(stream, 0, 16);
+		for (auto &it : _areaMap)
+			it._value->addStructure(_areaMap[255]);
+		delete stream;
+	} else
+		error("Not implemented yet");
 	// CPC
 	// file = gameDir.createReadStreamForMember("cm.bin");
 	// if (file == nullptr)
@@ -76,6 +93,18 @@ void CastleEngine::loadAssetsDOSFullGame() {
 	// load8bitBinary(file, 0x791a, 16);
 }
 
+void CastleEngine::titleScreen() {
+	if (isAmiga() || isAtariST()) // These releases has their own screens
+		return;
+
+	if (_title) {
+		drawTitle();
+		_gfx->flipBuffer();
+		g_system->updateScreen();
+		g_system->delayMillis(3000);
+	}
+}
+
 void CastleEngine::gotoArea(uint16 areaID, int entranceID) {
 	debugC(1, kFreescapeDebugMove, "Jumping to area: %d, entrance: %d", areaID, entranceID);
 	if (!_gameStateBits.contains(areaID))


Commit: 8ac1af2c570dad1e58374b40a90fd3ddba755210
    https://github.com/scummvm/scummvm/commit/8ac1af2c570dad1e58374b40a90fd3ddba755210
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: show basic UI in castle for dos

Changed paths:
    engines/freescape/freescape.h
    engines/freescape/games/castle.cpp


diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 25a0c6ff0d1..ca41cd9b7c9 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -556,6 +556,7 @@ public:
 
 	void titleScreen() override;
 	void loadAssetsDOSFullGame() override;
+	void drawUI() override;
 
 	void gotoArea(uint16 areaID, int entranceID) override;
 	Common::Error saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave = false) override;
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 04c95a7b2cc..a1e6f6555ff 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -63,6 +63,8 @@ void CastleEngine::loadAssetsDOSFullGame() {
 	Common::SeekableReadStream *stream = nullptr;
 
 	if (_renderMode == Common::kRenderEGA) {
+		_viewArea = Common::Rect(39, 31, 278, 150);
+
 		file.open("CMOE.DAT");
 		_title = load8bitBinImage(&file, 0x0);
 		_title->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
@@ -127,6 +129,28 @@ void CastleEngine::gotoArea(uint16 areaID, int entranceID) {
 		_gfx->_keyColor = 255;
 }
 
+void CastleEngine::drawUI() {
+	_gfx->setViewport(_fullscreenViewArea);
+
+	Graphics::Surface *surface = new Graphics::Surface();
+	surface->create(_screenW, _screenH, _gfx->_texturePixelFormat);
+	uint32 gray = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0xA0, 0xA0, 0xA0);
+	surface->fillRect(_fullscreenViewArea, gray);
+	drawCrossair(surface);
+
+	if (!_uiTexture)
+		_uiTexture = _gfx->createTexture(surface);
+	else
+		_uiTexture->update(surface);
+
+	_gfx->drawTexturedRect2D(_fullscreenViewArea, _fullscreenViewArea, _uiTexture);
+
+	surface->free();
+	delete surface;
+
+	_gfx->setViewport(_viewArea);
+}
+
 Common::Error CastleEngine::saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave) {
 	return Common::kNoError;
 }


Commit: ee1d083a4d8c1e0a80c7b8a280a01bdd294a1ac1
    https://github.com/scummvm/scummvm/commit/ee1d083a4d8c1e0a80c7b8a280a01bdd294a1ac1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: properly initialize _verts in shader renderer

Changed paths:
    engines/freescape/gfx_opengl_shaders.cpp


diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index 9e067cfd426..53f263c56c1 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -46,6 +46,7 @@ Renderer *CreateGfxOpenGLShader(int screenW, int screenH, Common::RenderMode ren
 }
 
 OpenGLShaderRenderer::OpenGLShaderRenderer(int screenW, int screenH, Common::RenderMode renderMode) : Renderer(screenW, screenH, renderMode) {
+	_verts = nullptr;
 	_triangleShader = nullptr;
 	_triangleVBO = 0;
 


Commit: 03e42d15d4905564b48c42c5aac1b33c7d515b69
    https://github.com/scummvm/scummvm/commit/03e42d15d4905564b48c42c5aac1b33c7d515b69
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
GRAPHICS: OPENGL: added another setUniform function to set an uniform value in shaders from an array of ints

Changed paths:
    graphics/opengl/shader.h


diff --git a/graphics/opengl/shader.h b/graphics/opengl/shader.h
index 16d908ec63d..185bd4fb063 100644
--- a/graphics/opengl/shader.h
+++ b/graphics/opengl/shader.h
@@ -131,6 +131,17 @@ public:
 		}
 	}
 
+	bool setUniform(const Common::String &uniform, const int size, const int *array) {
+		GLint pos = getUniformLocation(uniform);
+		if (pos != -1) {
+			use();
+			GL_CALL(glUniform1iv(pos, size, array));
+			return true;
+		} else {
+			return false;
+		}
+	}
+
 	// Different name to avoid overload ambiguity
 	bool setUniform1f(const Common::String &uniform, float f) {
 		GLint pos = getUniformLocation(uniform);


Commit: f57126e2fc57cb86cf41b6bf57a37622cad3db47
    https://github.com/scummvm/scummvm/commit/f57126e2fc57cb86cf41b6bf57a37622cad3db47
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: initial implementation of stipple patterns in shader renderer

Changed paths:
    engines/freescape/gfx_opengl_shaders.cpp
    engines/freescape/gfx_opengl_shaders.h
    engines/freescape/shaders/freescape_triangle.fragment


diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index 53f263c56c1..d14ee7dc874 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -186,6 +186,7 @@ void OpenGLShaderRenderer::renderPlayerShoot(byte color, const Common::Point pos
 	identity(3, 3) = 1.0;
 
 	_triangleShader->use();
+	_triangleShader->setUniform("useStipple", false);
 	_triangleShader->setUniform("mvpMatrix", identity);
 
 	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderZX) {
@@ -281,30 +282,32 @@ void OpenGLShaderRenderer::polygonOffset(bool enabled) {
 }
 
 void OpenGLShaderRenderer::setStippleData(byte *data) {
+	_triangleShader->use();
 	if (!data)
 		return;
 
-	//_variableStippleArray = data;
-	//for (int i = 0; i < 128; i++)
-	//	_variableStippleArray[i] = data[(i / 16) % 4];
+	for (int i = 0; i < 8; i++) {
+		byte b = data[i];
+		for (int j = 0; j < 8; j++) {
+			//debug("%d", 8*i + j);
+			_variableStippleArray[8*i + j] = b & 0x1;
+			b = b >> 1;
+		}
+	}
+	_triangleShader->setUniform("stipple", 64, (const int*)&_variableStippleArray);
 }
 
 void OpenGLShaderRenderer::useStipple(bool enabled) {
-	/*if (enabled) {
+	_triangleShader->use();
+	if (enabled) {
 		glEnable(GL_POLYGON_OFFSET_FILL);
 		glPolygonOffset(0.0f, -1.0f);
-		glEnable(GL_POLYGON_STIPPLE);
-		if (_renderMode == Common::kRenderZX  ||
-			_renderMode == Common::kRenderCPC ||
-			_renderMode == Common::kRenderCGA)
-			glPolygonStipple(_variableStippleArray);
-		else
-			glPolygonStipple(_defaultStippleArray);
+		_triangleShader->setUniform("useStipple", true);
 	} else {
 		glPolygonOffset(0, 0);
 		glDisable(GL_POLYGON_OFFSET_FILL);
-		glDisable(GL_POLYGON_STIPPLE);
-	}*/
+		_triangleShader->setUniform("useStipple", false);
+	}
 }
 
 void OpenGLShaderRenderer::useColor(uint8 r, uint8 g, uint8 b) {
@@ -350,7 +353,6 @@ Graphics::Surface *OpenGLShaderRenderer::getScreenshot() {
 	glReadPixels(screen.left, screen.top, screen.width(), screen.height(), GL_RGBA, GL_UNSIGNED_BYTE, s->getPixels());
 	flipVertical(s);
 	return s;
-	return nullptr;
 }
 
 } // End of namespace Freescape
diff --git a/engines/freescape/gfx_opengl_shaders.h b/engines/freescape/gfx_opengl_shaders.h
index 1084a8fb1cb..ae3592a6587 100644
--- a/engines/freescape/gfx_opengl_shaders.h
+++ b/engines/freescape/gfx_opengl_shaders.h
@@ -58,17 +58,11 @@ public:
 		GLfloat y;
 	};
 
-	/*Coord *_coords;
-
-	void copyToCoordArray(uint idx, const Math::Vector2d &src) {
-		assert(idx < kCoordsArraySize);
-		_coords[idx].x = src.getValue(0); _coords[idx].y = src.getValue(1);
-	}*/
-
 	OpenGL::Shader *_triangleShader;
 	OpenGL::Shader *_bitmapShader;
 	GLuint _triangleVBO;
 	GLuint _bitmapVBO;
+	int _variableStippleArray[64];
 
 	virtual void init() override;
 	virtual void clear(uint8 color) override;
diff --git a/engines/freescape/shaders/freescape_triangle.fragment b/engines/freescape/shaders/freescape_triangle.fragment
index aaf98049c01..8ab397df899 100644
--- a/engines/freescape/shaders/freescape_triangle.fragment
+++ b/engines/freescape/shaders/freescape_triangle.fragment
@@ -1,8 +1,17 @@
 OUTPUT
 
+uniform UBOOL useStipple;
+uniform int stipple[64];
+
 varying vec4 var_color;
 
 void main()
 {
-    outColor = var_color;
+	if (UBOOL_TEST(useStipple)) {
+		ivec2 coord = ivec2(gl_FragCoord.xy - 0.5);
+		if (stipple[int(mod(coord.x, 8) + mod(coord.y, 8) * 8)] == 0)
+			discard;
+	}
+
+	outColor = var_color;
 }
\ No newline at end of file


Commit: f937d769b07147cb436bc2e3eab91e0b57698614
    https://github.com/scummvm/scummvm/commit/f937d769b07147cb436bc2e3eab91e0b57698614
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: avoid crashing parsing driller conditions for zx

Changed paths:
    engines/freescape/language/8bitDetokeniser.cpp


diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 16f57bf2924..988182045d5 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -54,7 +54,8 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		 0, 0, 0, 0, 0, 0, 2, 2,
 		 1};
 
-	detokenisedStream += Common::String::format("CONDITION FLAG: %x\n", tokenisedCondition[0]);
+	if (sizeOfTokenisedContent > 0)
+		detokenisedStream += Common::String::format("CONDITION FLAG: %x\n", tokenisedCondition[0]);
 	Token::Type newConditional = Token::UNKNOWN;
 	Token::Type oldConditional = Token::UNKNOWN;
 


Commit: cb3c3e500255feedd91274c572794de74da2ed2f
    https://github.com/scummvm/scummvm/commit/cb3c3e500255feedd91274c572794de74da2ed2f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved rendering of stipple in shader renderer

Changed paths:
    engines/freescape/gfx_opengl_shaders.cpp


diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index d14ee7dc874..d43f8966bbc 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -290,7 +290,7 @@ void OpenGLShaderRenderer::setStippleData(byte *data) {
 		byte b = data[i];
 		for (int j = 0; j < 8; j++) {
 			//debug("%d", 8*i + j);
-			_variableStippleArray[8*i + j] = b & 0x1;
+			_variableStippleArray[i + 8*j] = b & 0x1;
 			b = b >> 1;
 		}
 	}


Commit: fdce543286769434676f1d7990269fef879db062
    https://github.com/scummvm/scummvm/commit/fdce543286769434676f1d7990269fef879db062
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: fixed incorrect parsing of 8-bit conditions

Changed paths:
    engines/freescape/games/dark/dark.cpp
    engines/freescape/language/8bitDetokeniser.cpp
    engines/freescape/language/instruction.cpp


diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index 1e17560ba1d..55484da32d3 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -95,9 +95,9 @@ void DarkEngine::addWalls(Area *area) {
 		if (target > 0) {
 			area->addObjectFromArea(id, _areaMap[255]);
 			GeometricObject *gobj = (GeometricObject *)area->objectWithID(id);
-			assert((*(gobj->_condition[1]._thenInstructions))[0].getType() == Token::Type::GOTO);
-			assert((*(gobj->_condition[1]._thenInstructions))[0]._destination == 0);
-			(*(gobj->_condition[1]._thenInstructions))[0].setSource(target);
+			assert((*(gobj->_condition[0]._thenInstructions))[0].getType() == Token::Type::GOTO);
+			assert((*(gobj->_condition[0]._thenInstructions))[0]._destination == 0);
+			(*(gobj->_condition[0]._thenInstructions))[0].setSource(target);
 		} else
 			area->addObjectFromArea(id + 1, _areaMap[255]);
 
diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 988182045d5..c4ac1c47621 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -80,8 +80,11 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 			branch.setBranches(conditionalInstructions, nullptr);
 			instructions.push_back(branch);
 
-			if (bytePointer > 0)
+			if (bytePointer > 0) {
 				detokenisedStream += "ENDIF\n";
+				// Allocate the next vector of instructions
+				conditionalInstructions = new FCLInstructionVector();
+			}
 
 			if (oldConditional == Token::SHOTQ)
 				detokenisedStream += "IF SHOT? THEN\n";
@@ -91,9 +94,6 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 				detokenisedStream += "IF COLLIDED? THEN\n";
 			else
 				error("Invalid conditional: %x", oldConditional);
-
-			// Allocate the next vector of instructions
-			conditionalInstructions = new FCLInstructionVector();
 		}
 
 		// get the actual operation
@@ -421,17 +421,6 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		detokenisedStream += "\n";
 	}
 
-	// if (!conditionalInstructions)
-	//	conditionalInstructions = new FCLInstructionVector();
-
-	// conditionalInstructions->push_back(currentInstruction);
-
-	FCLInstruction branch;
-	branch = FCLInstruction(oldConditional);
-
-	branch.setBranches(conditionalInstructions, nullptr);
-	instructions.push_back(branch);
-
 	return detokenisedStream;
 }
 
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index 7b845bf28b1..509e060f142 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -94,7 +94,12 @@ void FreescapeEngine::executeObjectConditions(GeometricObject *obj, bool shot, b
 	if (!obj->_conditionSource.empty()) {
 		_firstSound = true;
 		_objExecutingCodeSize = obj->getSize();
-		debugC(1, kFreescapeDebugCode, "Executing with collision flag: %s", obj->_conditionSource.c_str());
+		if (collided)
+			debugC(1, kFreescapeDebugCode, "Executing with collision flag: %s", obj->_conditionSource.c_str());
+		else if (shot)
+			debugC(1, kFreescapeDebugCode, "Executing with shot flag: %s", obj->_conditionSource.c_str());
+		else
+			error("Neither shot or collided flag is set!");
 		executeCode(obj->_condition, shot, collided, false); // TODO: check this last parameter
 	}
 }
@@ -124,7 +129,7 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 	int codeSize = code.size();
 	while (ip <= codeSize - 1) {
 		FCLInstruction &instruction = code[ip];
-		debugC(1, kFreescapeDebugCode, "Executing ip: %d in code with size: %d", ip, codeSize);
+		debugC(1, kFreescapeDebugCode, "Executing ip: %d with type %d in code with size: %d", ip, instruction.getType(), codeSize);
 		switch (instruction.getType()) {
 		default:
 			if (!isCastle())


Commit: 8260ca88281699f4abffde4ae997b8187199c77a
    https://github.com/scummvm/scummvm/commit/8260ca88281699f4abffde4ae997b8187199c77a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: make triangle shader more compatible with gles2

Changed paths:
    engines/freescape/shaders/freescape_triangle.fragment


diff --git a/engines/freescape/shaders/freescape_triangle.fragment b/engines/freescape/shaders/freescape_triangle.fragment
index 8ab397df899..2088bdc4357 100644
--- a/engines/freescape/shaders/freescape_triangle.fragment
+++ b/engines/freescape/shaders/freescape_triangle.fragment
@@ -8,8 +8,8 @@ varying vec4 var_color;
 void main()
 {
 	if (UBOOL_TEST(useStipple)) {
-		ivec2 coord = ivec2(gl_FragCoord.xy - 0.5);
-		if (stipple[int(mod(coord.x, 8) + mod(coord.y, 8) * 8)] == 0)
+		vec2 coord = ivec2(gl_FragCoord.xy - 0.5);
+		if (stipple[int(mod(coord.x, 8.) + mod(coord.y, 8.) * 8)] == 0)
 			discard;
 	}
 


Commit: 1e28728f146b054d0f6538d43e5dab4e1c4fa173
    https://github.com/scummvm/scummvm/commit/1e28728f146b054d0f6538d43e5dab4e1c4fa173
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: make triangle shader more compatible with gles2 (again)

Changed paths:
    engines/freescape/shaders/freescape_triangle.fragment


diff --git a/engines/freescape/shaders/freescape_triangle.fragment b/engines/freescape/shaders/freescape_triangle.fragment
index 2088bdc4357..9ad098b111e 100644
--- a/engines/freescape/shaders/freescape_triangle.fragment
+++ b/engines/freescape/shaders/freescape_triangle.fragment
@@ -8,8 +8,8 @@ varying vec4 var_color;
 void main()
 {
 	if (UBOOL_TEST(useStipple)) {
-		vec2 coord = ivec2(gl_FragCoord.xy - 0.5);
-		if (stipple[int(mod(coord.x, 8.) + mod(coord.y, 8.) * 8)] == 0)
+		ivec2 coord = ivec2(gl_FragCoord.xy - 0.5);
+		if (stipple[int(mod(float(coord.x), 8.) + mod(float(coord.y), 8.) * 8.)] == 0)
 			discard;
 	}
 


Commit: 7fe579351eee2a4f4c0a4024866e086850ea3485
    https://github.com/scummvm/scummvm/commit/7fe579351eee2a4f4c0a4024866e086850ea3485
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added detection of some unsupported demos

Changed paths:
    engines/freescape/detection.cpp


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index cdfcccba49d..8f9d8c06ddf 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -71,6 +71,18 @@ static const ADGameDescription gameDescriptions[] = {
 		GF_ZX_BUDGET,
 		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
+	{
+		"driller",
+		"Not implemented yet",
+		{
+			{"DRILLER.ZX.DATA", 0, "e571795806ed8a30df0fa3109eaa8ffb", 36000},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformZX,
+		ADGF_UNSUPPORTED | ADGF_DEMO,
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
+	},
 	{
 		"driller",
 		"",
@@ -226,6 +238,20 @@ static const ADGameDescription gameDescriptions[] = {
 		ADGF_DEMO,
 		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
+	{
+		"driller",
+		"Packed data",
+		{
+			{"driller.prg", 0, "ae9b03e247def6f0793174b1cb4352b5", 1821},
+			{"data", 0, "0c927fbc6c390afd0d0c15b2d7f8766f", 10893},
+			{"demo.cmd", 0, "9c732dcdad26b36b537e632924cd8f0e", 745},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformAtariST,
+		ADGF_UNSUPPORTED | ADGF_DEMO,
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
+	},
 	{
 		"darkside",
 		"",
@@ -256,6 +282,18 @@ static const ADGameDescription gameDescriptions[] = {
 		ADGF_UNSTABLE | ADGF_DEMO,
 		GUIO3(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA)
 	},
+	{
+		"darkside",
+		"Not implemented yet",
+		{
+			{"DARKSIDE.ZX.DATA", 0, "0e4d9b6e64ff24801272ff0b18a3caab", 29182},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformZX,
+		ADGF_UNSUPPORTED | ADGF_DEMO,
+		GUIO1(GUIO_NOMIDI)
+	},
 	{
 		"totaleclipse",
 		"",


Commit: e5d0ebefd770e7696666b9d51cff32e9609102d0
    https://github.com/scummvm/scummvm/commit/e5d0ebefd770e7696666b9d51cff32e9609102d0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: initial support for Dark Side ZX demo

Changed paths:
  A engines/freescape/games/dark/zx.cpp
    engines/freescape/assets.cpp
    engines/freescape/detection.cpp
    engines/freescape/freescape.h
    engines/freescape/games/dark/dark.cpp
    engines/freescape/games/dark/dos.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp
    engines/freescape/module.mk


diff --git a/engines/freescape/assets.cpp b/engines/freescape/assets.cpp
index d9e6e16931e..03369a113be 100644
--- a/engines/freescape/assets.cpp
+++ b/engines/freescape/assets.cpp
@@ -50,7 +50,7 @@ void FreescapeEngine::loadAssetsFullGame() {
 	} else if (isDOS()) {
 		loadAssetsDOSFullGame();
 	} else
-		error("Invalid or unsupported render mode %s for Driller", Common::getRenderModeDescription(_renderMode));
+		error("Invalid or unsupported render mode %s", Common::getRenderModeDescription(_renderMode));
 }
 
 void FreescapeEngine::loadAssetsDemo() {
@@ -61,8 +61,10 @@ void FreescapeEngine::loadAssetsDemo() {
 		loadAssetsAtariDemo();
 	} else if (isDOS()) {
 		loadAssetsDOSDemo();
+	} else if (isSpectrum()) {
+		loadAssetsZXDemo();
 	} else
-		error("Unsupported demo for Driller");
+		error("Unsupported demo");
 }
 
 void FreescapeEngine::loadAssetsAtariDemo() {
diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 8f9d8c06ddf..032e9bf425d 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -284,14 +284,14 @@ static const ADGameDescription gameDescriptions[] = {
 	},
 	{
 		"darkside",
-		"Not implemented yet",
+		"Demo",
 		{
 			{"DARKSIDE.ZX.DATA", 0, "0e4d9b6e64ff24801272ff0b18a3caab", 29182},
 			AD_LISTEND
 		},
 		Common::EN_ANY,
 		Common::kPlatformZX,
-		ADGF_UNSUPPORTED | ADGF_DEMO,
+		ADGF_UNSTABLE | ADGF_DEMO,
 		GUIO1(GUIO_NOMIDI)
 	},
 	{
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index ca41cd9b7c9..0d81cb0a5d0 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -519,13 +519,20 @@ public:
 	void pressedKey(const int keycode) override;
 	void executePrint(FCLInstruction &instruction) override;
 
+	void initDOS();
+	void initZX();
+
 	void loadAssetsDOSFullGame() override;
 	void loadAssetsDOSDemo() override;
 
+	void loadAssetsZXDemo() override;
+
 	int _lastTenSeconds;
 	void updateTimeVariables() override;
 
 	void drawDOSUI(Graphics::Surface *surface) override;
+	void drawZXUI(Graphics::Surface *surface) override;
+
 	void drawFullscreenMessage(Common::String message);
 	Common::Error saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave = false) override;
 	Common::Error loadGameStreamExtended(Common::SeekableReadStream *stream) override;
diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index 55484da32d3..d0a36c145d0 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -29,7 +29,11 @@
 namespace Freescape {
 
 DarkEngine::DarkEngine(OSystem *syst, const ADGameDescription *gd) : FreescapeEngine(syst, gd) {
-	_viewArea = Common::Rect(40, 24, 279, 124);
+	if (isDOS())
+		initDOS();
+	else if (isSpectrum())
+		initZX();
+
 	_playerHeightNumber = 1;
 	_playerHeights.push_back(16);
 	_playerHeights.push_back(48);
@@ -251,7 +255,7 @@ void DarkEngine::updateTimeVariables() {
 void DarkEngine::borderScreen() {
 	if (_border) {
 		drawBorder();
-		if (isDemo()) {
+		if (isDemo() && isDOS()) {
 			drawFullscreenMessage(_messagesList[27]);
 			drawFullscreenMessage(_messagesList[28]);
 			drawFullscreenMessage(_messagesList[29]);
diff --git a/engines/freescape/games/dark/dos.cpp b/engines/freescape/games/dark/dos.cpp
index 12881e46e46..28b5304478e 100644
--- a/engines/freescape/games/dark/dos.cpp
+++ b/engines/freescape/games/dark/dos.cpp
@@ -28,6 +28,13 @@ namespace Freescape {
 
 extern byte kEGADefaultPaletteData[16][3];
 
+void DarkEngine::initDOS() {
+	if (_renderMode == Common::kRenderEGA)
+		_viewArea = Common::Rect(40, 24, 279, 124);
+	else
+		error("Invalid or unknown render mode");
+}
+
 void DarkEngine::loadAssetsDOSDemo() {
 	Common::File file;
 	if (_renderMode == Common::kRenderEGA) {
diff --git a/engines/freescape/games/dark/zx.cpp b/engines/freescape/games/dark/zx.cpp
new file mode 100644
index 00000000000..e2f5211b146
--- /dev/null
+++ b/engines/freescape/games/dark/zx.cpp
@@ -0,0 +1,131 @@
+/* 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/>.
+ *
+ */
+
+#include "common/file.h"
+
+#include "freescape/freescape.h"
+#include "freescape/language/8bitDetokeniser.h"
+
+namespace Freescape {
+
+void DarkEngine::initZX() {
+	_viewArea = Common::Rect(56, 28, 265, 132);
+}
+
+void DarkEngine::loadAssetsZXDemo() {
+	Common::File file;
+
+	file.open("darkside.zx.title");
+	if (file.isOpen()) {
+		_title = loadAndCenterScrImage(&file);
+	} else
+		error("Unable to find darkside.zx.title");
+
+	file.close();
+	file.open("darkside.zx.border");
+	if (file.isOpen()) {
+		_border = loadAndCenterScrImage(&file);
+	} else
+		error("Unable to find driller.zx.border");
+	file.close();
+
+
+	file.open("darkside.zx.data");
+
+	if (!file.isOpen())
+		error("Failed to open darksize.zx.data");
+
+	loadMessagesFixedSize(&file, 0x56c, 19, 24);
+	loadMessagesFixedSize(&file, 0x5761, 264, 5);
+
+	loadFonts(&file, 0x6164);
+	//loadGlobalObjects(&file, 0x1d13, 8);
+	load8bitBinary(&file, 0x62c6, 4);
+	/*for (auto &it : _areaMap) {
+		addWalls(it._value);
+		addECDs(it._value);
+	}*/
+}
+
+void DarkEngine::drawZXUI(Graphics::Surface *surface) {
+	/*uint32 color = 1;
+	uint8 r, g, b;
+
+	_gfx->readFromPalette(color, r, g, b);
+	uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
+
+	color = _currentArea->_usualBackgroundColor;
+	if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
+		color = (*_gfx->_colorRemaps)[color];
+	}
+
+	_gfx->readFromPalette(color, r, g, b);
+	uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
+
+	int score = _gameStateVars[k8bitVariableScore];
+	drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 199, 137, front, back, surface);
+	drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 199, 145, front, back, surface);
+	drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 199, 153, front, back, surface);
+
+	drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 71, 168, front, back, surface);
+	drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), 71, 177, front, back, surface);
+	drawStringInSurface(Common::String::format("%07d", score), 95, 8, front, back, surface);
+
+	int seconds, minutes, hours;
+	getTimeFromCountdown(seconds, minutes, hours);
+	// TODO: implement binary clock
+
+	Common::String message;
+	int deadline;
+	getLatestMessages(message, deadline);
+	if (deadline <= _countdown) {
+		drawStringInSurface(message, 112, 177, back, front, surface);
+		_temporaryMessages.push_back(message);
+		_temporaryMessageDeadlines.push_back(deadline);
+	} else
+		drawStringInSurface(_currentArea->_name, 112, 177, front, back, surface);
+
+	int energy = _gameStateVars[k8bitVariableEnergy]; // called fuel in this game
+	int shield = _gameStateVars[k8bitVariableShield];
+
+	_gfx->readFromPalette(9, r, g, b);
+	uint32 blue = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
+
+	if (shield >= 0) {
+		Common::Rect shieldBar;
+		shieldBar = Common::Rect(72, 139, 151 - (k8bitMaxShield - shield), 146);
+		surface->fillRect(shieldBar, front);
+
+		shieldBar = Common::Rect(72, 140, 151 - (k8bitMaxShield - shield), 145);
+		surface->fillRect(shieldBar, blue);
+	}
+
+	if (energy >= 0) {
+		Common::Rect energyBar;
+		energyBar = Common::Rect(72, 147, 151 - (k8bitMaxEnergy - energy), 154);
+		surface->fillRect(energyBar, front);
+
+		energyBar = Common::Rect(72, 148, 151 - (k8bitMaxEnergy - energy), 153);
+		surface->fillRect(energyBar, blue);
+	}*/
+}
+
+} // End of namespace Freescape
\ No newline at end of file
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index 64fe016df02..47bce668a63 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -757,7 +757,7 @@ void FreescapeEngine::loadMessagesFixedSize(Common::SeekableReadStream *file, in
 		file->read(buffer, size);
 		Common::String message = (const char *)buffer;
 		_messagesList.push_back(message);
-		debugC(1, kFreescapeDebugParser, "%s", _messagesList[_messagesList.size() - 1].c_str());
+		debugC(1, kFreescapeDebugParser, "'%s'", _messagesList[_messagesList.size() - 1].c_str());
 	}
 	free(buffer);
 }
diff --git a/engines/freescape/module.mk b/engines/freescape/module.mk
index fe3a2a95cda..cf02372b576 100644
--- a/engines/freescape/module.mk
+++ b/engines/freescape/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
 	games/castle.o \
 	games/dark/dark.o \
 	games/dark/dos.o \
+	games/dark/zx.o \
 	games/driller/amiga.o \
 	games/driller/atari.o \
 	games/driller/c64.o \


Commit: 81ef1ec054fce6d347d28119aeaa2dcd60c5fe43
    https://github.com/scummvm/scummvm/commit/81ef1ec054fce6d347d28119aeaa2dcd60c5fe43
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: started to implemented Dark Side UI in ZX spectrum

Changed paths:
    engines/freescape/games/dark/zx.cpp


diff --git a/engines/freescape/games/dark/zx.cpp b/engines/freescape/games/dark/zx.cpp
index e2f5211b146..ce7800a8ae5 100644
--- a/engines/freescape/games/dark/zx.cpp
+++ b/engines/freescape/games/dark/zx.cpp
@@ -66,7 +66,7 @@ void DarkEngine::loadAssetsZXDemo() {
 }
 
 void DarkEngine::drawZXUI(Graphics::Surface *surface) {
-	/*uint32 color = 1;
+	uint32 color = 7;
 	uint8 r, g, b;
 
 	_gfx->readFromPalette(color, r, g, b);
@@ -81,13 +81,13 @@ void DarkEngine::drawZXUI(Graphics::Surface *surface) {
 	uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
 
 	int score = _gameStateVars[k8bitVariableScore];
-	drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 199, 137, front, back, surface);
-	drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 199, 145, front, back, surface);
-	drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 199, 153, front, back, surface);
+	drawStringInSurface(Common::String::format("%04d", int(2 * _position.x())), 191, 141, front, back, surface);
+	drawStringInSurface(Common::String::format("%04d", int(2 * _position.z())), 191, 149, front, back, surface);
+	drawStringInSurface(Common::String::format("%04d", int(2 * _position.y())), 191, 157, front, back, surface);
 
-	drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 71, 168, front, back, surface);
-	drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), 71, 177, front, back, surface);
-	drawStringInSurface(Common::String::format("%07d", score), 95, 8, front, back, surface);
+	drawStringInSurface(Common::String::format("%02d", int(_angleRotations[_angleRotationIndex])), 78, 165, front, back, surface);
+	drawStringInSurface(Common::String::format("%3d", _playerSteps[_playerStepIndex]), 78, 173, front, back, surface);
+	drawStringInSurface(Common::String::format("%07d", score), 94, 13, front, back, surface);
 
 	int seconds, minutes, hours;
 	getTimeFromCountdown(seconds, minutes, hours);
@@ -97,35 +97,32 @@ void DarkEngine::drawZXUI(Graphics::Surface *surface) {
 	int deadline;
 	getLatestMessages(message, deadline);
 	if (deadline <= _countdown) {
-		drawStringInSurface(message, 112, 177, back, front, surface);
+		drawStringInSurface(message, 112, 173, back, front, surface);
 		_temporaryMessages.push_back(message);
 		_temporaryMessageDeadlines.push_back(deadline);
 	} else
-		drawStringInSurface(_currentArea->_name, 112, 177, front, back, surface);
+		drawStringInSurface(_currentArea->_name, 112, 173, front, back, surface);
 
 	int energy = _gameStateVars[k8bitVariableEnergy]; // called fuel in this game
 	int shield = _gameStateVars[k8bitVariableShield];
 
-	_gfx->readFromPalette(9, r, g, b);
-	uint32 blue = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
-
 	if (shield >= 0) {
 		Common::Rect shieldBar;
-		shieldBar = Common::Rect(72, 139, 151 - (k8bitMaxShield - shield), 146);
-		surface->fillRect(shieldBar, front);
+		shieldBar = Common::Rect(80, 141, 151 - (k8bitMaxShield - shield), 147);
+		surface->fillRect(shieldBar, back);
 
-		shieldBar = Common::Rect(72, 140, 151 - (k8bitMaxShield - shield), 145);
-		surface->fillRect(shieldBar, blue);
+		shieldBar = Common::Rect(80, 142, 151 - (k8bitMaxShield - shield), 146);
+		surface->fillRect(shieldBar, front);
 	}
 
 	if (energy >= 0) {
 		Common::Rect energyBar;
 		energyBar = Common::Rect(72, 147, 151 - (k8bitMaxEnergy - energy), 154);
-		surface->fillRect(energyBar, front);
+		surface->fillRect(energyBar, back);
 
 		energyBar = Common::Rect(72, 148, 151 - (k8bitMaxEnergy - energy), 153);
-		surface->fillRect(energyBar, blue);
-	}*/
+		surface->fillRect(energyBar, front);
+	}
 }
 
 } // End of namespace Freescape
\ No newline at end of file


Commit: 0eae3fbde3995a3b987e06d234a99a2fe57ebd54
    https://github.com/scummvm/scummvm/commit/0eae3fbde3995a3b987e06d234a99a2fe57ebd54
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: attempt to fix effect execution when stepping up

Changed paths:
    engines/freescape/movement.cpp


diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index f914780359e..aa432f1e3ee 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -260,6 +260,19 @@ void FreescapeEngine::move(CameraMovement direction, uint8 scale, float deltaTim
 				if (stepUp) {
 					if (!isPlayingSound())
 						playSound(4, false);
+
+					positionY = _position.y();
+					int fallen;
+					for (fallen = 1; fallen < 64; fallen++) {
+						_position.set(_position.x(), positionY - fallen , _position.z());
+						if (tryStepDown(_position))
+							break;
+					}
+					assert(fallen < 64);
+					fallen++;
+					fallen++;
+					_position.set(_position.x(), positionY - fallen, _position.z());
+
 					debugC(1, kFreescapeDebugCode, "Runing effects:");
 					checkCollisions(true); // run the effects (again)
 				} else {
@@ -358,7 +371,7 @@ bool FreescapeEngine::checkCollisions(bool executeCode) {
 		// FIXME: find a better workaround of this
 		if (gobj->getSize().length() > 3000) {
 			if (largeObjectWasBlocking)
-				break;
+				continue;
 			largeObjectWasBlocking = true;
 		}
 


Commit: d18b4fd29d13d2d5f234b0e8638c95a9fcd13481
    https://github.com/scummvm/scummvm/commit/d18b4fd29d13d2d5f234b0e8638c95a9fcd13481
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: some fixes in the parsing of fcl opcodes

Changed paths:
    engines/freescape/language/8bitDetokeniser.cpp
    engines/freescape/language/instruction.cpp


diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index c4ac1c47621..1d28314779c 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -77,15 +77,15 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 			FCLInstruction branch;
 			branch = FCLInstruction(oldConditional);
 
-			branch.setBranches(conditionalInstructions, nullptr);
-			instructions.push_back(branch);
-
 			if (bytePointer > 0) {
 				detokenisedStream += "ENDIF\n";
 				// Allocate the next vector of instructions
 				conditionalInstructions = new FCLInstructionVector();
 			}
 
+			branch.setBranches(conditionalInstructions, nullptr);
+			instructions.push_back(branch);
+
 			if (oldConditional == Token::SHOTQ)
 				detokenisedStream += "IF SHOT? THEN\n";
 			else if (oldConditional == Token::TIMERQ)
@@ -124,6 +124,8 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		case 0:
 			detokenisedStream += "NOP ";
 			currentInstruction = FCLInstruction(Token::NOP);
+			conditionalInstructions->push_back(currentInstruction);
+			currentInstruction = FCLInstruction(Token::UNKNOWN);
 			break; // NOP
 		case 1:    // add three-byte value to score
 		{
@@ -188,6 +190,7 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 
 		case 9:
 			detokenisedStream += "ADDVAR (1, v";
+			detokenisedStream += Common::String::format("%d)", tokenisedCondition[bytePointer]);
 			currentInstruction = FCLInstruction(Token::ADDVAR);
 			currentInstruction.setSource(tokenisedCondition[bytePointer]);
 			currentInstruction.setDestination(1);
@@ -198,6 +201,7 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 			break;
 		case 10:
 			detokenisedStream += "SUBVAR (1, v";
+			detokenisedStream += Common::String::format("%d)", tokenisedCondition[bytePointer]);
 			currentInstruction = FCLInstruction(Token::SUBVAR);
 			currentInstruction.setSource(tokenisedCondition[bytePointer]);
 			currentInstruction.setDestination(1);
@@ -357,8 +361,7 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		case 20:
 			detokenisedStream += "SETVAR ";
 			detokenisedStream += Common::String::format("(v%d, %d)", (int)tokenisedCondition[bytePointer], (int)tokenisedCondition[bytePointer + 1]);
-			bytePointer += 2;
-			numberOfArguments = 0;
+			currentInstruction = FCLInstruction(Token::SETVAR);
 			break;
 
 		case 35:
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index 509e060f142..5fc29ed61df 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -127,6 +127,7 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 	assert(!(shot && collided));
 	int ip = 0;
 	int codeSize = code.size();
+	assert(codeSize > 0);
 	while (ip <= codeSize - 1) {
 		FCLInstruction &instruction = code[ip];
 		debugC(1, kFreescapeDebugCode, "Executing ip: %d with type %d in code with size: %d", ip, instruction.getType(), codeSize);
@@ -135,6 +136,10 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 			if (!isCastle())
 				error("Instruction %x at ip: %d not implemented!", instruction.getType(), ip);
 			break;
+		case Token::NOP:
+			debugC(1, kFreescapeDebugCode, "Executing NOP at ip: %d", ip);
+			break;
+
 		case Token::COLLIDEDQ:
 			if (collided)
 				executeCode(*instruction._thenInstructions, shot, collided, timer);
@@ -199,6 +204,9 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 		case Token::CLEARBIT:
 			executeClearBit(instruction);
 			break;
+		case Token::TOGGLEBIT:
+			executeToggleBit(instruction);
+			break;
 		case Token::PRINT:
 			executePrint(instruction);
 			break;


Commit: ea41d363b13efd8c74785a01183281342baadbd1
    https://github.com/scummvm/scummvm/commit/ea41d363b13efd8c74785a01183281342baadbd1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: correctly print the SETVAR opcode

Changed paths:
    engines/freescape/language/8bitDetokeniser.cpp


diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 1d28314779c..3b76b2fa9e6 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -359,8 +359,7 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 			break;
 
 		case 20:
-			detokenisedStream += "SETVAR ";
-			detokenisedStream += Common::String::format("(v%d, %d)", (int)tokenisedCondition[bytePointer], (int)tokenisedCondition[bytePointer + 1]);
+			detokenisedStream += "SETVAR (v";
 			currentInstruction = FCLInstruction(Token::SETVAR);
 			break;
 


Commit: ee159b6a0e05d6fa7ddb83266f6993f279b47558
    https://github.com/scummvm/scummvm/commit/ee159b6a0e05d6fa7ddb83266f6993f279b47558
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: refactored clear code as drawBackground

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/freescape.cpp
    engines/freescape/games/castle.cpp
    engines/freescape/games/eclipse.cpp
    engines/freescape/gfx.cpp
    engines/freescape/gfx.h
    engines/freescape/gfx_opengl.cpp
    engines/freescape/gfx_opengl.h
    engines/freescape/gfx_opengl_shaders.cpp
    engines/freescape/gfx_opengl_shaders.h
    engines/freescape/gfx_tinygl.cpp
    engines/freescape/gfx_tinygl.h


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 64304fb9b6e..c1aa7f9a0ee 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -223,7 +223,6 @@ void Area::resetArea() {
 
 
 void Area::draw(Freescape::Renderer *gfx) {
-	gfx->clear(_skyColor);
 	assert(_drawableObjects.size() > 0);
 	for (auto &obj : _drawableObjects) {
 		if (!obj->isDestroyed() && !obj->isInvisible()) {
diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 661a2db9dff..da72e733d90 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -267,9 +267,9 @@ void FreescapeEngine::takeDamageFromSensor() {
 
 void FreescapeEngine::drawBackground() {
 	_gfx->setViewport(_fullscreenViewArea);
-	_gfx->clear(_currentArea->_usualBackgroundColor);
+	_gfx->drawBackground(_currentArea->_usualBackgroundColor);
 	_gfx->setViewport(_viewArea);
-	_gfx->clear(_currentArea->_skyColor);
+	_gfx->drawBackground(_currentArea->_skyColor);
 }
 
 void FreescapeEngine::drawFrame() {
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index a1e6f6555ff..1aa53d527f9 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -124,7 +124,6 @@ void CastleEngine::gotoArea(uint16 areaID, int entranceID) {
 
 	if (_currentArea->_skyColor > 0 && _currentArea->_skyColor != 255) {
 		_gfx->_keyColor = 0;
-		_gfx->clear(_currentArea->_skyColor);
 	} else
 		_gfx->_keyColor = 255;
 }
diff --git a/engines/freescape/games/eclipse.cpp b/engines/freescape/games/eclipse.cpp
index 7f970e86209..0eff0b8a1b3 100644
--- a/engines/freescape/games/eclipse.cpp
+++ b/engines/freescape/games/eclipse.cpp
@@ -181,7 +181,6 @@ void EclipseEngine::gotoArea(uint16 areaID, int entranceID) {
 
 	if (_currentArea->_skyColor > 0 && _currentArea->_skyColor != 255) {
 		_gfx->_keyColor = 0;
-		_gfx->clear(_currentArea->_skyColor);
 	} else
 		_gfx->_keyColor = 255;
 }
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index b8b45a37179..ac69bafa823 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -925,6 +925,22 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 	polygonOffset(false);
 }
 
+void Renderer::drawBackground(uint8 color) {
+
+	if (_colorRemaps && _colorRemaps->contains(color)) {
+		color = (*_colorRemaps)[color];
+	}
+
+	byte *stipple = nullptr;
+	uint8 r1, g1, b1, r2, g2, b2;
+	bool render = getRGBAt(color, r1, g1, b1, r2, g2, b2, stipple);
+	if (!render)
+		r1 = g1 = b1 = 0;
+
+	assert(stipple == nullptr); // Unclear if this is ever used
+	clear(r1, g1, b1);
+}
+
 Graphics::RendererType determinateRenderType() {
 	Common::String rendererConfig = ConfMan.get("renderer");
 	Graphics::RendererType desiredRendererType = Graphics::Renderer::parseTypeCode(rendererConfig);
diff --git a/engines/freescape/gfx.h b/engines/freescape/gfx.h
index 6bae31840c9..0d3acc47099 100644
--- a/engines/freescape/gfx.h
+++ b/engines/freescape/gfx.h
@@ -91,8 +91,9 @@ public:
 	virtual void renderFace(const Common::Array<Math::Vector3d> &vertices) = 0;
 
 	void setColorRemaps(ColorReMap *colorRemaps);
-	virtual void clear(uint8 color) = 0;
+	virtual void clear(uint8 r, uint8 g, uint8 b) = 0;
 	virtual void drawFloor(uint8 color) = 0;
+	virtual void drawBackground(uint8 color);
 
 	Common::Rect viewport() const;
 
diff --git a/engines/freescape/gfx_opengl.cpp b/engines/freescape/gfx_opengl.cpp
index 0c09bb0f5ba..75c6408d35f 100644
--- a/engines/freescape/gfx_opengl.cpp
+++ b/engines/freescape/gfx_opengl.cpp
@@ -306,14 +306,7 @@ void OpenGLRenderer::useColor(uint8 r, uint8 g, uint8 b) {
 	glColor3ub(r, g, b);
 }
 
-void OpenGLRenderer::clear(uint8 color) {
-	uint8 r, g, b;
-
-	if (_colorRemaps && _colorRemaps->contains(color)) {
-		color = (*_colorRemaps)[color];
-	}
-
-	readFromPalette(color, r, g, b);
+void OpenGLRenderer::clear(uint8 r, uint8 g, uint8 b) {
 	glClearColor(r / 255., g / 255., b / 255., 1.0);
 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 }
diff --git a/engines/freescape/gfx_opengl.h b/engines/freescape/gfx_opengl.h
index a3925a8d4ba..07536360988 100644
--- a/engines/freescape/gfx_opengl.h
+++ b/engines/freescape/gfx_opengl.h
@@ -82,7 +82,7 @@ public:
 	GLubyte *_variableStippleArray;
 
 	virtual void init() override;
-	virtual void clear(uint8 color) override;
+	virtual void clear(uint8 r, uint8 g, uint8 b) override;
 	virtual void setViewport(const Common::Rect &rect) override;
 	virtual void positionCamera(const Math::Vector3d &pos, const Math::Vector3d &interest) override;
 	virtual void updateProjectionMatrix(float fov, float nearClipPlane, float farClipPlane) override;
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index d43f8966bbc..f46abb766fe 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -316,15 +316,8 @@ void OpenGLShaderRenderer::useColor(uint8 r, uint8 g, uint8 b) {
 	_triangleShader->setUniform("color", color);
 }
 
-void OpenGLShaderRenderer::clear(uint8 color) {
-	uint8 r, g, b;
-
-	if (_colorRemaps && _colorRemaps->contains(color)) {
-		color = (*_colorRemaps)[color];
-	}
-
-	readFromPalette(color, r, g, b);
-	glClearColor(0, 0, 0, 1.0);
+void OpenGLShaderRenderer::clear(uint8 r, uint8 g, uint8 b) {
+	glClearColor(r / 255., g / 255., b / 255., 1.0);
 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 }
 
diff --git a/engines/freescape/gfx_opengl_shaders.h b/engines/freescape/gfx_opengl_shaders.h
index ae3592a6587..ddcfe4a15b5 100644
--- a/engines/freescape/gfx_opengl_shaders.h
+++ b/engines/freescape/gfx_opengl_shaders.h
@@ -65,7 +65,7 @@ public:
 	int _variableStippleArray[64];
 
 	virtual void init() override;
-	virtual void clear(uint8 color) override;
+	virtual void clear(uint8 r, uint8 g, uint8 b) override;
 	virtual void setViewport(const Common::Rect &rect) override;
 	virtual void positionCamera(const Math::Vector3d &pos, const Math::Vector3d &interest) override;
 	virtual void updateProjectionMatrix(float fov, float nearClipPlane, float farClipPlane) override;
diff --git a/engines/freescape/gfx_tinygl.cpp b/engines/freescape/gfx_tinygl.cpp
index ad1c0f62195..92c19f70ebc 100644
--- a/engines/freescape/gfx_tinygl.cpp
+++ b/engines/freescape/gfx_tinygl.cpp
@@ -213,14 +213,7 @@ void TinyGLRenderer::useColor(uint8 r, uint8 g, uint8 b) {
 	tglColor3ub(r, g, b);
 }
 
-void TinyGLRenderer::clear(uint8 color) {
-	uint8 r, g, b;
-
-	if (_colorRemaps && _colorRemaps->contains(color)) {
-		color = (*_colorRemaps)[color];
-	}
-
-	readFromPalette(color, r, g, b);
+void TinyGLRenderer::clear(uint8 r, uint8 g, uint8 b) {
 	tglClearColor(r / 255., g / 255., b / 255., 1.0);
 	tglClear(TGL_COLOR_BUFFER_BIT | TGL_DEPTH_BUFFER_BIT);
 }
diff --git a/engines/freescape/gfx_tinygl.h b/engines/freescape/gfx_tinygl.h
index e5a6f3d480e..ad65a171581 100644
--- a/engines/freescape/gfx_tinygl.h
+++ b/engines/freescape/gfx_tinygl.h
@@ -47,7 +47,7 @@ public:
 	Vertex *_verts;
 
 	virtual void init() override;
-	virtual void clear(uint8 color) override;
+	virtual void clear(uint8 r, uint8 g, uint8 b) override;
 	virtual void setViewport(const Common::Rect &rect) override;
 	virtual void positionCamera(const Math::Vector3d &pos, const Math::Vector3d &interest) override;
 	virtual void updateProjectionMatrix(float fov, float nearClipPlane, float farClipPlane) override;


Commit: c57029b8e8c6df0c18780ec0b5cdef332d1c06cc
    https://github.com/scummvm/scummvm/commit/c57029b8e8c6df0c18780ec0b5cdef332d1c06cc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved image reading for castle in DOS/EGA

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/freescape.h
    engines/freescape/games/castle.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index da72e733d90..a5af1711f29 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -165,7 +165,6 @@ FreescapeEngine::~FreescapeEngine() {
 		delete _border;
 	}
 
-
 	if (_gfx->_isAccelerated) {
 		delete _borderTexture;
 		delete _uiTexture;
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 0d81cb0a5d0..eb1f87275d1 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -560,7 +560,10 @@ public:
 class CastleEngine : public FreescapeEngine {
 public:
 	CastleEngine(OSystem *syst, const ADGameDescription *gd);
+	~CastleEngine();
 
+
+	Graphics::ManagedSurface *_option;
 	void titleScreen() override;
 	void loadAssetsDOSFullGame() override;
 	void drawUI() override;
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 1aa53d527f9..35bbc14f12d 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -36,6 +36,33 @@ CastleEngine::CastleEngine(OSystem *syst, const ADGameDescription *gd) : Freesca
 	_playerDepth = 8;
 }
 
+CastleEngine::~CastleEngine() {
+	if (_option) {
+		_option->free();
+		delete _option;
+	}
+}
+
+byte kCastleTitleDOSPalette[16][3] = {
+	{0x00, 0x00, 0x00}, // correct!
+	{0x00, 0x00, 0xaa}, // correct!
+	{0x00, 0x00, 0x00}, // ????
+	{0x00, 0xaa, 0xaa}, // changed
+	{0x55, 0x55, 0x55}, // changed
+	{0x55, 0x55, 0xff}, // changed
+	{0xaa, 0xaa, 0xaa}, // changed
+	{0x55, 0xff, 0xff}, // changed
+	{0xff, 0x55, 0xff}, // changed
+	{0x00, 0x00, 0x00},
+	{0xff, 0xff, 0xff}, // changed
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00}
+};
+
+
 Common::SeekableReadStream *CastleEngine::decryptFile(const Common::String filename) {
 	Common::File file;
 	file.open(filename);
@@ -65,16 +92,19 @@ void CastleEngine::loadAssetsDOSFullGame() {
 	if (_renderMode == Common::kRenderEGA) {
 		_viewArea = Common::Rect(39, 31, 278, 150);
 
-		file.open("CMOE.DAT");
+		file.open("CMLE.DAT");
 		_title = load8bitBinImage(&file, 0x0);
-		_title->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
+		_title->setPalette((byte *)&kCastleTitleDOSPalette, 0, 16);
+		file.close();
 
+		file.open("CMOE.DAT");
+		_option = load8bitBinImage(&file, 0x0);
+		_option->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
 		file.close();
 
 		file.open("CME.DAT");
 		_border = load8bitBinImage(&file, 0x0);
 		_border->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
-
 		file.close();
 
 		stream = decryptFile("CMLE");


Commit: 73391bffd77f54ad70ff1f6691e6eb41d38abc5e
    https://github.com/scummvm/scummvm/commit/73391bffd77f54ad70ff1f6691e6eb41d38abc5e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved border palette for castle in DOS/EGA

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/games/castle.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index a5af1711f29..007ba3d185f 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -643,7 +643,7 @@ void FreescapeEngine::processBorder() {
 
 		for (int i = 0; i < border->w; i++) {
 			for (int j = 0; j < border->h; j++) {
-				if (border->getPixel(i, j) == black)
+				if (!isCastle() && border->getPixel(i, j) == black)
 					border->setPixel(i, j, transparent);
 			}
 		}
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 35bbc14f12d..c9b07f07587 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -62,6 +62,42 @@ byte kCastleTitleDOSPalette[16][3] = {
 	{0x00, 0x00, 0x00}
 };
 
+byte kCastleOptionDOSPalette[16][3] = {
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0xaa},
+	{0x00, 0xaa, 0x00},
+	{0xaa, 0x00, 0x00},
+	{0x55, 0x55, 0x55},
+	{0xaa, 0x55, 0x00},
+	{0xaa, 0xaa, 0xaa},
+	{0xff, 0x55, 0x55},
+	{0x12, 0x34, 0x56},
+	{0xff, 0xff, 0x55},
+	{0xff, 0xff, 0xff},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00}
+};
+
+byte kCastleBorderDOSPalette[16][3] = {
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0xaa},
+	{0x00, 0xaa, 0x00},
+	{0xaa, 0x00, 0x00},
+	{0x55, 0x55, 0x55},
+	{0xaa, 0x55, 0x00},
+	{0xaa, 0xaa, 0xaa}, // can be also green
+	{0xff, 0x55, 0x55},
+	{0x00, 0x00, 0x00},
+	{0xff, 0xff, 0x55},
+	{0xff, 0xff, 0xff},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00},
+	{0x00, 0x00, 0x00}
+};
 
 Common::SeekableReadStream *CastleEngine::decryptFile(const Common::String filename) {
 	Common::File file;
@@ -90,7 +126,7 @@ void CastleEngine::loadAssetsDOSFullGame() {
 	Common::SeekableReadStream *stream = nullptr;
 
 	if (_renderMode == Common::kRenderEGA) {
-		_viewArea = Common::Rect(39, 31, 278, 150);
+		_viewArea = Common::Rect(40, 33, 280, 152);
 
 		file.open("CMLE.DAT");
 		_title = load8bitBinImage(&file, 0x0);
@@ -99,12 +135,12 @@ void CastleEngine::loadAssetsDOSFullGame() {
 
 		file.open("CMOE.DAT");
 		_option = load8bitBinImage(&file, 0x0);
-		_option->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
+		_option->setPalette((byte *)&kCastleOptionDOSPalette, 0, 16);
 		file.close();
 
 		file.open("CME.DAT");
 		_border = load8bitBinImage(&file, 0x0);
-		_border->setPalette((byte *)&kEGADefaultPaletteData, 0, 16);
+		_border->setPalette((byte *)&kCastleBorderDOSPalette, 0, 16);
 		file.close();
 
 		stream = decryptFile("CMLE");


Commit: c8afca45ec9c8e986843a9b465651a415fe4179e
    https://github.com/scummvm/scummvm/commit/c8afca45ec9c8e986843a9b465651a415fe4179e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: make sure the floor is available in area 2 of castle

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/area.h
    engines/freescape/games/castle.cpp


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index c1aa7f9a0ee..3039bc5c5e1 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -330,25 +330,29 @@ void Area::addObjectFromArea(int16 id, Area *global) {
 	}
 }
 
+void Area::addFloor() {
+	int id = 0;
+	assert(!_objectsByID->contains(id));
+	Common::Array<uint8> *gColors = new Common::Array<uint8>;
+	for (int i = 0; i < 6; i++)
+		gColors->push_back(_groundColor);
+
+	Object *obj = (Object *)new GeometricObject(
+		ObjectType::kCubeType,
+		id,
+		0,                             // flags
+		Math::Vector3d(0, -1, 0),      // Position
+		Math::Vector3d(4128, 1, 4128), // size
+		gColors,
+		nullptr,
+		FCLInstructionVector());
+	(*_objectsByID)[id] = obj;
+	_drawableObjects.insert_at(0, obj);
+}
+
 void Area::addStructure(Area *global) {
-	Object *obj = nullptr;
 	if (!global || !_entrancesByID->contains(255)) {
-		int id = 254;
-		Common::Array<uint8> *gColors = new Common::Array<uint8>;
-		for (int i = 0; i < 6; i++)
-			gColors->push_back(_groundColor);
-
-		obj = (Object *)new GeometricObject(
-			ObjectType::kCubeType,
-			id,
-			0,                             // flags
-			Math::Vector3d(0, -1, 0),      // Position
-			Math::Vector3d(4128, 1, 4128), // size
-			gColors,
-			nullptr,
-			FCLInstructionVector());
-		(*_objectsByID)[id] = obj;
-		_drawableObjects.insert_at(0, obj);
+		addFloor();
 		return;
 	}
 	GlobalStructure *rs = (GlobalStructure *)(*_entrancesByID)[255];
diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index 49cffb6632a..950854432f9 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -57,6 +57,7 @@ public:
 	ObjectArray checkCollisions(const Math::AABB &boundingBox);
 	void addObjectFromArea(int16 id, Area *global);
 	void addObject(Object *obj);
+	void addFloor();
 	void addStructure(Area *global);
 	void removeObject(int16 id);
 	void resetArea();
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index c9b07f07587..a9fcdb495f8 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -151,6 +151,8 @@ void CastleEngine::loadAssetsDOSFullGame() {
 		load8bitBinary(stream, 0, 16);
 		for (auto &it : _areaMap)
 			it._value->addStructure(_areaMap[255]);
+
+		_areaMap[2]->addFloor();
 		delete stream;
 	} else
 		error("Not implemented yet");


Commit: bccfe4eb0b374d157c6011ac25666ac587c66546
    https://github.com/scummvm/scummvm/commit/bccfe4eb0b374d157c6011ac25666ac587c66546
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: better rendering of EGA colors in castle

Changed paths:
    engines/freescape/area.h
    engines/freescape/games/castle.cpp
    engines/freescape/gfx.cpp
    engines/freescape/gfx.h
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index 950854432f9..294b73a5c88 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -80,6 +80,7 @@ public:
 	uint8 _underFireBackgroundColor;
 	uint8 _inkColor;
 	uint8 _paperColor;
+	uint8 _extraColor[4];
 	ColorReMap _colorRemaps;
 
 private:
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index a9fcdb495f8..505c6bfd86a 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -194,6 +194,28 @@ void CastleEngine::gotoArea(uint16 areaID, int entranceID) {
 		_gfx->_keyColor = 0;
 	} else
 		_gfx->_keyColor = 255;
+
+	_lastPosition = _position;
+	_gameStateVars[0x1f] = 0;
+
+	if (areaID == _startArea && entranceID == _startEntrance) {
+		_yaw = 310;
+		_pitch = 0;
+	}
+
+	debugC(1, kFreescapeDebugMove, "starting player position: %f, %f, %f", _position.x(), _position.y(), _position.z());
+	clearTemporalMessages();
+	playSound(5, false);
+	// Ignore sky/ground fields
+	_gfx->_keyColor = 0;
+
+	_gfx->_colorPair[_currentArea->_underFireBackgroundColor] = _currentArea->_extraColor[0];
+	_gfx->_colorPair[_currentArea->_usualBackgroundColor] = _currentArea->_extraColor[1];
+	_gfx->_colorPair[_currentArea->_paperColor] = _currentArea->_extraColor[2];
+	_gfx->_colorPair[_currentArea->_inkColor] = _currentArea->_extraColor[3];
+
+	swapPalette(areaID);
+	resetInput();
 }
 
 void CastleEngine::drawUI() {
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index ac69bafa823..b84ae117b55 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -361,8 +361,7 @@ bool Renderer::getRGBAtCPC(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &
 	return true;
 }
 
-bool Renderer::getRGBAtEGA(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2) {
- 	// assert(index-1 < _colorMap->size());
+uint8 Renderer::mapEGAColor(uint8 index) {
 	byte *entry = (*_colorMap)[index - 1];
 	uint8 color = 0;
 	uint8 acc = 1;
@@ -376,10 +375,23 @@ bool Renderer::getRGBAtEGA(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &
 		entry++;
 	}
 	assert(color < 16);
-	readFromPalette(color, r1, g1, b1);
-	r2 = r1;
-	g2 = g1;
-	b2 = b1;
+	return color;
+}
+
+bool Renderer::getRGBAtEGA(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2) {
+	uint8 color;
+	if (_colorPair[index] > 0) {
+		color = mapEGAColor(_colorPair[index] & 0xf);
+		readFromPalette(color, r1, g1, b1);
+		color = mapEGAColor(_colorPair[index] >> 4);
+		readFromPalette(color, r2, g2, b2);
+	} else {
+		color = mapEGAColor(index);
+		readFromPalette(color, r1, g1, b1);
+		r2 = r1;
+		g2 = g1;
+		b2 = b1;
+	}
 	return true;
 }
 
diff --git a/engines/freescape/gfx.h b/engines/freescape/gfx.h
index 0d3acc47099..047cb82589f 100644
--- a/engines/freescape/gfx.h
+++ b/engines/freescape/gfx.h
@@ -100,6 +100,8 @@ public:
 	// palette
 	void readFromPalette(uint8 index, uint8 &r, uint8 &g, uint8 &b);
 	uint8 indexFromColor(uint8 r, uint8 g, uint8 b);
+	uint8 mapEGAColor(uint8 index);
+
 	bool getRGBAt(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2, byte *&stipple);
 	bool getRGBAtC64(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2);
 	bool getRGBAtCGA(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2, byte *&stipple);
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index 47bce668a63..e5265274b59 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -438,10 +438,12 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 	// debug("Condition Ptr: %x", cPtr);
 	debugC(1, kFreescapeDebugParser, "Pos before first object: %lx", file->pos());
 
+	// Driller specific
 	uint8 gasPocketX = 0;
 	uint8 gasPocketY = 0;
 	uint8 gasPocketRadius = 0;
-
+	// Castle specific
+	uint8 extraColor[4];
 	if (isEclipse()) {
 		byte idx = file->readByte();
 		name = idx < 8 ? eclipseRoomName[idx] : eclipseRoomName[8];
@@ -471,10 +473,10 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 	} else if (isCastle()) {
 		byte idx = file->readByte();
 		name = _messagesList[idx + 41];
-		debug("byte: %d", file->readByte());
-		debug("byte: %d", file->readByte());
-		debug("byte: %d", file->readByte());
-		debug("byte: %d", file->readByte());
+		extraColor[0] = file->readByte();
+		extraColor[1] = file->readByte();
+		extraColor[2] = file->readByte();
+		extraColor[3] = file->readByte();
 	}
 	debugC(1, kFreescapeDebugParser, "Area name: %s", name.c_str());
 
@@ -511,11 +513,17 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 	area->_scale = scale;
 	area->_skyColor = skyColor;
 	area->_groundColor = groundColor;
+
 	area->_inkColor = inkColor;
 	area->_paperColor = paperColor;
 	area->_usualBackgroundColor = usualBackgroundColor;
 	area->_underFireBackgroundColor = underFireBackgroundColor;
 
+	area->_extraColor[0] = extraColor[0];
+	area->_extraColor[1] = extraColor[1];
+	area->_extraColor[2] = extraColor[2];
+	area->_extraColor[3] = extraColor[3];
+
 	// Driller specific
 	area->_gasPocketPosition = Common::Point(32 * gasPocketX, 32 * gasPocketY);
 	area->_gasPocketRadius = 32 * gasPocketRadius;


Commit: 99f9bb7eba960ef85e7df4c876d55cf886ac0531
    https://github.com/scummvm/scummvm/commit/99f9bb7eba960ef85e7df4c876d55cf886ac0531
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: allow to reset color pairs, useful in castle EGA

Changed paths:
    engines/freescape/games/castle.cpp
    engines/freescape/gfx.cpp
    engines/freescape/gfx.h


diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 505c6bfd86a..7943c1a7578 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -208,6 +208,7 @@ void CastleEngine::gotoArea(uint16 areaID, int entranceID) {
 	playSound(5, false);
 	// Ignore sky/ground fields
 	_gfx->_keyColor = 0;
+	_gfx->clearColorPairArray();
 
 	_gfx->_colorPair[_currentArea->_underFireBackgroundColor] = _currentArea->_extraColor[0];
 	_gfx->_colorPair[_currentArea->_usualBackgroundColor] = _currentArea->_extraColor[1];
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index b84ae117b55..88d7304ba09 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -126,6 +126,11 @@ byte getCGAStipple(byte x, int back, int fore) {
 	return st;
 }
 
+void Renderer::clearColorPairArray() {
+	for (int i = 0; i < 15; i++)
+		_colorPair[i] = 0;
+}
+
 void Renderer::fillColorPairArray() {
 	for (int i = 4; i < 15; i++) {
 		byte *entry = (*_colorMap)[i];
diff --git a/engines/freescape/gfx.h b/engines/freescape/gfx.h
index 047cb82589f..7e38d4d7812 100644
--- a/engines/freescape/gfx.h
+++ b/engines/freescape/gfx.h
@@ -119,6 +119,7 @@ public:
 	void setColorMap(ColorMap *colorMap_);
 	ColorMap *_colorMap;
 	ColorReMap *_colorRemaps;
+	void clearColorPairArray();
 	void fillColorPairArray();
 	byte _colorPair[16];
 	int _keyColor;


Commit: a42d72ad6ec12b4eb27110f84e2ef571d3605be4
    https://github.com/scummvm/scummvm/commit/a42d72ad6ec12b4eb27110f84e2ef571d3605be4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved rendering of rectangles and fixes

Changed paths:
    engines/freescape/gfx.cpp


diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index 88d7304ba09..f4dde905eda 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -836,15 +836,6 @@ void Renderer::renderRectangle(const Math::Vector3d &origin, const Math::Vector3
 
 			vertices.push_back(Math::Vector3d(origin.x() + dx, origin.y() + dy, origin.z() + dz));
 			vertices.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
-			renderFace(vertices);
-			if (r1 != r2 || g1 != g2 || b1 != b2) {
-				useStipple(true);
-				useColor(r2, g2, b2);
-				renderFace(vertices);
-				useStipple(false);
-			}
-
-			vertices.clear();
 			vertices.push_back(Math::Vector3d(origin.x(), origin.y(), origin.z()));
 
 			dx = dy = dz = 0.0;
@@ -954,7 +945,7 @@ void Renderer::drawBackground(uint8 color) {
 	if (!render)
 		r1 = g1 = b1 = 0;
 
-	assert(stipple == nullptr); // Unclear if this is ever used
+	//assert(stipple == nullptr); // Unclear if this is ever used
 	clear(r1, g1, b1);
 }
 


Commit: ef88a9886fd2b02ec6048ae06f1b04983a9cb063
    https://github.com/scummvm/scummvm/commit/ef88a9886fd2b02ec6048ae06f1b04983a9cb063
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: disallow zero length conditions

Changed paths:
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index e5265274b59..35f44a3225b 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -534,11 +534,13 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 		uint32 lengthOfCondition = readField(file, 8);
 		debugC(1, kFreescapeDebugParser, "length of condition: %d", lengthOfCondition);
 		// get the condition
-		Common::Array<uint8> conditionArray = readArray(file, lengthOfCondition);
-		Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions);
-		area->_conditions.push_back(instructions);
-		area->_conditionSources.push_back(conditionSource);
-		debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
+		if (lengthOfCondition > 0) {
+			Common::Array<uint8> conditionArray = readArray(file, lengthOfCondition);
+			Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions);
+			area->_conditions.push_back(instructions);
+			area->_conditionSources.push_back(conditionSource);
+			debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
+		}
 	}
 
 	debugC(1, kFreescapeDebugParser, "End of area at %lx", file->pos());
@@ -625,12 +627,13 @@ void FreescapeEngine::load8bitBinary(Common::SeekableReadStream *file, int offse
 		uint32 lengthOfCondition = readField(file, 8);
 		debugC(1, kFreescapeDebugParser, "length of condition: %d at %lx", lengthOfCondition, file->pos());
 		// get the condition
-		Common::Array<uint8> conditionArray = readArray(file, lengthOfCondition);
-		// debug("Global condition %d", numConditions + 1);
-		Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions);
-		_conditions.push_back(instructions);
-		_conditionSources.push_back(conditionSource);
-		debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
+		if (lengthOfCondition > 0) {
+			Common::Array<uint8> conditionArray = readArray(file, lengthOfCondition);
+			Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions);
+			_conditions.push_back(instructions);
+			_conditionSources.push_back(conditionSource);
+			debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
+		}
 	}
 
 	if (isDriller()) {


Commit: 6963aad58476553cccebe0cf6cc5491415d88230
    https://github.com/scummvm/scummvm/commit/6963aad58476553cccebe0cf6cc5491415d88230
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: allow to parse conditions using the ACTIVATED trigger for castle

Changed paths:
    engines/freescape/games/driller/driller.cpp
    engines/freescape/language/8bitDetokeniser.cpp
    engines/freescape/language/8bitDetokeniser.h
    engines/freescape/language/instruction.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 5144aa8ba11..902354fcce9 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -206,7 +206,7 @@ void DrillerEngine::loadAssetsFullGame() {
 	conditionArray.push_back(0x7f);
 	conditionArray.push_back(0x0);
 
-	Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions);
+	Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions, false);
 	debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
 	_areaMap[18]->_conditions.push_back(instructions);
 	_areaMap[18]->_conditionSources.push_back(conditionSource);
diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 3b76b2fa9e6..2e97886037b 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -33,7 +33,7 @@ uint8 k8bitMaxVariable = 64;
 uint8 k8bitMaxShield = 64;
 uint8 k8bitMaxEnergy = 64;
 
-Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition, FCLInstructionVector &instructions) {
+Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition, FCLInstructionVector &instructions, bool enableActivated) {
 	Common::String detokenisedStream;
 	Common::Array<uint8>::size_type bytePointer = 0;
 	Common::Array<uint8>::size_type sizeOfTokenisedContent = tokenisedCondition.size();
@@ -63,7 +63,9 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		// get the conditional type of the next operation
 		uint8 conditionalByte = tokenisedCondition[bytePointer];
 
-		if (conditionalByte & 0x80)
+		if ((conditionalByte & 0xc0) && enableActivated) {
+			newConditional = Token::ACTIVATEDQ;
+		} else if (conditionalByte & 0x80)
 			newConditional = Token::SHOTQ;
 		else if (conditionalByte & 0x40)
 			newConditional = Token::TIMERQ;
@@ -92,6 +94,8 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 				detokenisedStream += "IF TIMER? THEN\n";
 			else if (oldConditional == Token::COLLIDEDQ)
 				detokenisedStream += "IF COLLIDED? THEN\n";
+			else if (oldConditional == Token::ACTIVATEDQ)
+				detokenisedStream += "IF ACTIVATED? THEN\n";
 			else
 				error("Invalid conditional: %x", oldConditional);
 		}
diff --git a/engines/freescape/language/8bitDetokeniser.h b/engines/freescape/language/8bitDetokeniser.h
index b0c1047bb40..08326c2d5c9 100644
--- a/engines/freescape/language/8bitDetokeniser.h
+++ b/engines/freescape/language/8bitDetokeniser.h
@@ -40,7 +40,7 @@ extern uint8 k8bitMaxVariable;
 extern uint8 k8bitMaxShield;
 extern uint8 k8bitMaxEnergy;
 
-Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition, FCLInstructionVector &instructions);
+Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition, FCLInstructionVector &instructions, bool enableActivated);
 
 } // End of namespace Freescape
 
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index 5fc29ed61df..84f021f32a2 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -133,13 +133,20 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 		debugC(1, kFreescapeDebugCode, "Executing ip: %d with type %d in code with size: %d", ip, instruction.getType(), codeSize);
 		switch (instruction.getType()) {
 		default:
-			if (!isCastle())
-				error("Instruction %x at ip: %d not implemented!", instruction.getType(), ip);
+			//if (!isCastle())
+			error("Instruction %x at ip: %d not implemented!", instruction.getType(), ip);
 			break;
 		case Token::NOP:
 			debugC(1, kFreescapeDebugCode, "Executing NOP at ip: %d", ip);
 			break;
 
+		case Token::ACTIVATEDQ:
+			if (collided) // TODO: implement interaction
+				executeCode(*instruction._thenInstructions, shot, collided, timer);
+			// else branch is always empty
+			assert(instruction._elseInstructions == nullptr);
+			break;
+
 		case Token::COLLIDEDQ:
 			if (collided)
 				executeCode(*instruction._thenInstructions, shot, collided, timer);
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index 35f44a3225b..61265e7c4f2 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -187,7 +187,7 @@ Object *FreescapeEngine::load8bitObject(Common::SeekableReadStream *file) {
 		Common::String conditionSource;
 		if (byteSizeOfObject) {
 			Common::Array<uint8> conditionArray = readArray(file, byteSizeOfObject);
-			conditionSource = detokenise8bitCondition(conditionArray, instructions);
+			conditionSource = detokenise8bitCondition(conditionArray, instructions, isCastle());
 			// instructions = getInstructions(conditionSource);
 			debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
 		}
@@ -257,7 +257,7 @@ Object *FreescapeEngine::load8bitObject(Common::SeekableReadStream *file) {
 		// grab the object condition, if there is one
 		if (byteSizeOfObject) {
 			Common::Array<uint8> conditionArray = readArray(file, byteSizeOfObject);
-			conditionSource = detokenise8bitCondition(conditionArray, instructions);
+			conditionSource = detokenise8bitCondition(conditionArray, instructions, isCastle());
 			debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
 		}
 		debugC(1, kFreescapeDebugParser, "End of object at %lx", file->pos());
@@ -536,7 +536,7 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 		// get the condition
 		if (lengthOfCondition > 0) {
 			Common::Array<uint8> conditionArray = readArray(file, lengthOfCondition);
-			Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions);
+			Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions, isCastle());
 			area->_conditions.push_back(instructions);
 			area->_conditionSources.push_back(conditionSource);
 			debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
@@ -629,7 +629,7 @@ void FreescapeEngine::load8bitBinary(Common::SeekableReadStream *file, int offse
 		// get the condition
 		if (lengthOfCondition > 0) {
 			Common::Array<uint8> conditionArray = readArray(file, lengthOfCondition);
-			Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions);
+			Common::String conditionSource = detokenise8bitCondition(conditionArray, instructions, isCastle());
 			_conditions.push_back(instructions);
 			_conditionSources.push_back(conditionSource);
 			debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());


Commit: d342614658b11c7f2b3128b2c4b17b45b7bf5deb
    https://github.com/scummvm/scummvm/commit/d342614658b11c7f2b3128b2c4b17b45b7bf5deb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: basic support for control flow opcodes of castle

Changed paths:
    engines/freescape/freescape.h
    engines/freescape/language/8bitDetokeniser.cpp
    engines/freescape/language/instruction.cpp
    engines/freescape/language/token.h


diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index eb1f87275d1..385745bb4db 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -281,6 +281,7 @@ public:
 	void executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer);
 
 	// Instructions
+	bool checkIfGreaterOrEqual(FCLInstruction &instruction);
 	void executeIncrementVariable(FCLInstruction &instruction);
 	void executeDecrementVariable(FCLInstruction &instruction);
 	void executeSetVariable(FCLInstruction &instruction);
diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 2e97886037b..37b76ab9854 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -374,28 +374,32 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 
 		case 44:
 			detokenisedStream += "ELSE ";
+			currentInstruction = FCLInstruction(Token::ELSE);
+			conditionalInstructions->push_back(currentInstruction);
+			currentInstruction = FCLInstruction(Token::UNKNOWN);
+			numberOfArguments = 0;
 			break;
 
 		case 45:
 			detokenisedStream += "ENDIF ";
+			currentInstruction = FCLInstruction(Token::ENDIF);
+			conditionalInstructions->push_back(currentInstruction);
+			currentInstruction = FCLInstruction(Token::UNKNOWN);
+			numberOfArguments = 0;
 			break;
 
 		case 46:
-			detokenisedStream += "IFGTE ";
-			detokenisedStream += Common::String::format("(v%d, %d)", (int)tokenisedCondition[bytePointer], (int)tokenisedCondition[bytePointer + 1]);
-			bytePointer += 2;
-			numberOfArguments = 0;
+			detokenisedStream += "IFGTE (v";
+			currentInstruction = FCLInstruction(Token::IFGTEQ);
 			break;
 
 		case 47:
-			detokenisedStream += "IFLTE ";
-			detokenisedStream += Common::String::format("(v%d, %d)", (int)tokenisedCondition[bytePointer], (int)tokenisedCondition[bytePointer + 1]);
-			bytePointer += 2;
-			numberOfArguments = 0;
+			detokenisedStream += "IFLTE (v";
+			currentInstruction = FCLInstruction(Token::IFGTEQ);
 			break;
 
 		case 48:
-			detokenisedStream += "EXECUTE ";
+			detokenisedStream += "EXECUTE (";
 			currentInstruction = FCLInstruction(Token::EXECUTE);
 			break;
 		}
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index 84f021f32a2..b27b8bbffc9 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -126,11 +126,19 @@ void FreescapeEngine::executeLocalGlobalConditions(bool shot, bool collided, boo
 void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer) {
 	assert(!(shot && collided));
 	int ip = 0;
+	bool skip = false;
 	int codeSize = code.size();
 	assert(codeSize > 0);
 	while (ip <= codeSize - 1) {
 		FCLInstruction &instruction = code[ip];
 		debugC(1, kFreescapeDebugCode, "Executing ip: %d with type %d in code with size: %d", ip, instruction.getType(), codeSize);
+
+		if (skip && instruction.getType() != Token::ELSE && instruction.getType() != Token::ENDIF) {
+			debugC(1, kFreescapeDebugCode, "Instruction skipped!");
+			ip++;
+			continue;
+		}
+
 		switch (instruction.getType()) {
 		default:
 			//if (!isCastle())
@@ -141,7 +149,7 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 			break;
 
 		case Token::ACTIVATEDQ:
-			if (collided) // TODO: implement interaction
+			if (shot) // TODO: implement interaction
 				executeCode(*instruction._thenInstructions, shot, collided, timer);
 			// else branch is always empty
 			assert(instruction._elseInstructions == nullptr);
@@ -169,6 +177,18 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 			if (executeEndIfNotEqual(instruction))
 				ip = codeSize;
 			break;
+		case Token::IFGTEQ:
+			skip = !checkIfGreaterOrEqual(instruction);
+			break;
+
+		case Token::ELSE:
+			skip = !skip;
+			break;
+
+		case Token::ENDIF:
+			skip = false;
+			break;
+
 		case Token::SWAPJET:
 			executeSwapJet(instruction);
 			break;
@@ -264,7 +284,7 @@ void FreescapeEngine::executeDelay(FCLInstruction &instruction) {
 
 void FreescapeEngine::executePrint(FCLInstruction &instruction) {
 	uint16 index = instruction._source - 1;
-	debugC(1, kFreescapeDebugCode, "Printing message %d", index);
+	debugC(1, kFreescapeDebugCode, "Printing message %d: \"%s\"", index, _messagesList[index].c_str());
 	_currentAreaMessages.clear();
 	_currentAreaMessages.push_back(_messagesList[index]);
 }
@@ -335,6 +355,14 @@ bool FreescapeEngine::executeEndIfVisibilityIsEqual(FCLInstruction &instruction)
 	return (obj->isInvisible() == (value != 0));
 }
 
+bool FreescapeEngine::checkIfGreaterOrEqual(FCLInstruction &instruction) {
+	uint16 variable = instruction._source;
+	uint16 value = instruction._destination;
+	debugC(1, kFreescapeDebugCode, "Check if variable %d is greater than equal to %d!", variable, value);
+	return (_gameStateVars[variable] >= value);
+}
+
+
 bool FreescapeEngine::executeEndIfNotEqual(FCLInstruction &instruction) {
 	uint16 variable = instruction._source;
 	uint16 value = instruction._destination;
diff --git a/engines/freescape/language/token.h b/engines/freescape/language/token.h
index 77ec65b2dde..d108d93d780 100644
--- a/engines/freescape/language/token.h
+++ b/engines/freescape/language/token.h
@@ -80,8 +80,8 @@ public:
 		TRIGANIM,
 		UPDATEI,
 		VAREQ,
-		VARGQ,
-		VARLQ,
+		IFGTEQ,
+		IFGLEQ,
 		VISQ,
 		VIS,
 		WAIT,


Commit: 3a6fbca79c1b65e85ae55a7e495c2517cf505d5c
    https://github.com/scummvm/scummvm/commit/3a6fbca79c1b65e85ae55a7e495c2517cf505d5c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: refactored conditional handling to make it work with castle

Changed paths:
    engines/freescape/freescape.h
    engines/freescape/language/8bitDetokeniser.cpp
    engines/freescape/language/8bitDetokeniser.h
    engines/freescape/language/instruction.cpp
    engines/freescape/language/token.h


diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 385745bb4db..2bf19ab76ef 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -281,6 +281,7 @@ public:
 	void executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer);
 
 	// Instructions
+	bool checkConditional(FCLInstruction &instruction, bool shot, bool collided, bool timer, bool activated);
 	bool checkIfGreaterOrEqual(FCLInstruction &instruction);
 	void executeIncrementVariable(FCLInstruction &instruction);
 	void executeDecrementVariable(FCLInstruction &instruction);
diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 37b76ab9854..775140d972a 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -33,7 +33,7 @@ uint8 k8bitMaxVariable = 64;
 uint8 k8bitMaxShield = 64;
 uint8 k8bitMaxEnergy = 64;
 
-Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition, FCLInstructionVector &instructions, bool enableActivated) {
+Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition, FCLInstructionVector &instructions, bool multipleConditionals) {
 	Common::String detokenisedStream;
 	Common::Array<uint8>::size_type bytePointer = 0;
 	Common::Array<uint8>::size_type sizeOfTokenisedContent = tokenisedCondition.size();
@@ -56,28 +56,30 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 
 	if (sizeOfTokenisedContent > 0)
 		detokenisedStream += Common::String::format("CONDITION FLAG: %x\n", tokenisedCondition[0]);
-	Token::Type newConditional = Token::UNKNOWN;
-	Token::Type oldConditional = Token::UNKNOWN;
+	uint16 newConditional = 0;
+	uint16 oldConditional = 0;
 
 	while (bytePointer < sizeOfTokenisedContent) {
 		// get the conditional type of the next operation
-		uint8 conditionalByte = tokenisedCondition[bytePointer];
-
-		if ((conditionalByte & 0xc0) && enableActivated) {
-			newConditional = Token::ACTIVATEDQ;
-		} else if (conditionalByte & 0x80)
-			newConditional = Token::SHOTQ;
-		else if (conditionalByte & 0x40)
-			newConditional = Token::TIMERQ;
+		uint8 conditionalByte = tokenisedCondition[bytePointer] & 0xc0;
+		//detokenisedStream += Common::String::format("CONDITION FLAG: %x\n", conditionalByte);
+		newConditional = 0;
+
+		if (conditionalByte == 0x40)
+			newConditional = kConditionalTimeout;
+		else if (conditionalByte == 0x80)
+			newConditional = kConditionalShot;
+		else if (conditionalByte == 0xc0)
+			newConditional = kConditionalActivated;
 		else
-			newConditional = Token::COLLIDEDQ;
+			newConditional = kConditionalCollided;
 
 		// if the conditional type has changed then end the old conditional,
 		// if we were in one, and begin a new one
 		if (bytePointer == 0 || newConditional != oldConditional) {
 			oldConditional = newConditional;
 			FCLInstruction branch;
-			branch = FCLInstruction(oldConditional);
+			branch = FCLInstruction(Token::CONDITIONAL);
 
 			if (bytePointer > 0) {
 				detokenisedStream += "ENDIF\n";
@@ -86,18 +88,23 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 			}
 
 			branch.setBranches(conditionalInstructions, nullptr);
+			branch.setSource(oldConditional); // conditional flag
 			instructions.push_back(branch);
 
-			if (oldConditional == Token::SHOTQ)
-				detokenisedStream += "IF SHOT? THEN\n";
-			else if (oldConditional == Token::TIMERQ)
-				detokenisedStream += "IF TIMER? THEN\n";
-			else if (oldConditional == Token::COLLIDEDQ)
-				detokenisedStream += "IF COLLIDED? THEN\n";
-			else if (oldConditional == Token::ACTIVATEDQ)
-				detokenisedStream += "IF ACTIVATED? THEN\n";
+			detokenisedStream += "IF ";
+
+			if (oldConditional & kConditionalShot)
+				detokenisedStream += "SHOT? ";
+			else if (oldConditional & kConditionalTimeout)
+				detokenisedStream += "TIMER? ";
+			else if (oldConditional & kConditionalCollided)
+				detokenisedStream += "COLLIDED? ";
+			else if (oldConditional & kConditionalActivated)
+				detokenisedStream += "ACTIVATED? ";
 			else
 				error("Invalid conditional: %x", oldConditional);
+
+			detokenisedStream += "THEN\n";
 		}
 
 		// get the actual operation
diff --git a/engines/freescape/language/8bitDetokeniser.h b/engines/freescape/language/8bitDetokeniser.h
index 08326c2d5c9..8ba7c11a3f0 100644
--- a/engines/freescape/language/8bitDetokeniser.h
+++ b/engines/freescape/language/8bitDetokeniser.h
@@ -36,6 +36,13 @@ enum {
 	k8bitVariableEnergyDrillerJet = 57
 };
 
+enum {
+	kConditionalShot = 1 << 0,
+	kConditionalTimeout = 1 << 1,
+	kConditionalCollided = 1 << 2,
+	kConditionalActivated = 1 << 3,
+};
+
 extern uint8 k8bitMaxVariable;
 extern uint8 k8bitMaxShield;
 extern uint8 k8bitMaxEnergy;
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index b27b8bbffc9..da9ed9ba5d4 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -148,31 +148,13 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 			debugC(1, kFreescapeDebugCode, "Executing NOP at ip: %d", ip);
 			break;
 
-		case Token::ACTIVATEDQ:
-			if (shot) // TODO: implement interaction
+		case Token::CONDITIONAL:
+			if (checkConditional(instruction, shot, collided, timer, false)) // TODO: implement interaction
 				executeCode(*instruction._thenInstructions, shot, collided, timer);
 			// else branch is always empty
 			assert(instruction._elseInstructions == nullptr);
 			break;
 
-		case Token::COLLIDEDQ:
-			if (collided)
-				executeCode(*instruction._thenInstructions, shot, collided, timer);
-			// else branch is always empty
-			assert(instruction._elseInstructions == nullptr);
-			break;
-		case Token::SHOTQ:
-			if (shot)
-				executeCode(*instruction._thenInstructions, shot, collided, timer);
-			// else branch is always empty
-			assert(instruction._elseInstructions == nullptr);
-			break;
-		case Token::TIMERQ:
-			if (timer)
-				executeCode(*instruction._thenInstructions, shot, collided, timer);
-			// else branch is always empty
-			assert(instruction._elseInstructions == nullptr);
-			break;
 		case Token::VARNOTEQ:
 			if (executeEndIfNotEqual(instruction))
 				ip = codeSize;
@@ -355,6 +337,23 @@ bool FreescapeEngine::executeEndIfVisibilityIsEqual(FCLInstruction &instruction)
 	return (obj->isInvisible() == (value != 0));
 }
 
+bool FreescapeEngine::checkConditional(FCLInstruction &instruction, bool shot, bool collided, bool timer, bool activated) {
+	uint16 conditional = instruction._source;
+	bool result = false;
+
+	if (conditional & kConditionalShot)
+		result |= shot;
+	if (conditional & kConditionalTimeout)
+		result |= timer;
+	if (conditional & kConditionalCollided)
+		result |= collided;
+	if (conditional & kConditionalActivated)
+		result |= activated;
+
+	debugC(1, kFreescapeDebugCode, "Check if conditional %x is true: %d!", conditional, result);
+	return result;
+}
+
 bool FreescapeEngine::checkIfGreaterOrEqual(FCLInstruction &instruction) {
 	uint16 variable = instruction._source;
 	uint16 value = instruction._destination;
diff --git a/engines/freescape/language/token.h b/engines/freescape/language/token.h
index d108d93d780..a99d777b8bd 100644
--- a/engines/freescape/language/token.h
+++ b/engines/freescape/language/token.h
@@ -30,12 +30,11 @@ namespace Freescape {
 struct Token {
 public:
 	enum Type {
-		ACTIVATEDQ,
 		ADDVAR,
 		AGAIN,
 		AND,
 		ANDV,
-		COLLIDEDQ,
+		CONDITIONAL,
 		DELAY,
 		DESTROY,
 		DESTROYEDQ,
@@ -67,7 +66,6 @@ public:
 		SCREEN,
 		SOUND,
 		SETVAR,
-		SHOTQ,
 		START,
 		STARTANIM,
 		STOPANIM,
@@ -75,7 +73,6 @@ public:
 		SUBVAR,
 		SYNCSND,
 		THEN,
-		TIMERQ,
 		TOGVIS,
 		TRIGANIM,
 		UPDATEI,


Commit: 55d4acc6e796a19c3cc6c2c00823601bc76ffa30
    https://github.com/scummvm/scummvm/commit/55d4acc6e796a19c3cc6c2c00823601bc76ffa30
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added support for using different languages in castle (DOS)

Changed paths:
    engines/freescape/detection.cpp
    engines/freescape/freescape.cpp
    engines/freescape/freescape.h
    engines/freescape/games/castle.cpp


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 032e9bf425d..097a5e98d48 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -346,7 +346,7 @@ static const ADGameDescription gameDescriptions[] = {
 			{"CMH.EXE", 0, "1f3b67e649e718e239ebfd7c56e96d47", 63040},
 			AD_LISTEND
 		},
-		Common::EN_ANY,
+		Common::UNK_LANG,
 		Common::kPlatformDOS,
 		ADGF_UNSTABLE,
 		GUIO1(GUIO_NOMIDI)
@@ -619,6 +619,19 @@ public:
 	const DebugChannelDef *getDebugChannels() const override {
 		return debugFlagList;
 	}
+	DetectedGame toDetectedGame(const ADDetectedGame &adGame, ADDetectedGameExtraInfo *extraInfo) const override;
 };
 
+DetectedGame FreescapeMetaEngineDetection::toDetectedGame(const ADDetectedGame &adGame, ADDetectedGameExtraInfo *extraInfo) const {
+	DetectedGame game = AdvancedMetaEngineDetection::toDetectedGame(adGame);
+
+	// The AdvancedDetector model only allows specifying a single supported game language.
+	if (game.gameId == "castlemaster" && game.language == Common::UNK_LANG) {
+		game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
+		game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
+		game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
+	}
+	return game;
+}
+
 REGISTER_PLUGIN_STATIC(FREESCAPE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, FreescapeMetaEngineDetection);
diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 007ba3d185f..351b96692db 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -60,6 +60,8 @@ FreescapeEngine::FreescapeEngine(OSystem *syst, const ADGameDescription *gd)
 
 	_variant = gd->flags;
 
+	_language = Common::parseLanguage(ConfMan.get("language"));
+
 	if (!Common::parseBool(ConfMan.get("prerecorded_sounds"), _usePrerecordedSounds))
 		error("Failed to parse bool from prerecorded_sounds option");
 
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 2bf19ab76ef..66d56c02e82 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -95,6 +95,7 @@ public:
 
 	// Game selection
 	uint32 _variant;
+	Common::Language _language;
 	bool isDriller() { return _targetName.hasPrefix("driller") || _targetName.hasPrefix("spacestationoblivion"); }
 	bool isDark() { return _targetName.hasPrefix("darkside"); }
 	bool isEclipse() { return _targetName.hasPrefix("totaleclipse"); }
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 7943c1a7578..c5747264ea2 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -143,7 +143,23 @@ void CastleEngine::loadAssetsDOSFullGame() {
 		_border->setPalette((byte *)&kCastleBorderDOSPalette, 0, 16);
 		file.close();
 
-		stream = decryptFile("CMLE");
+		switch (_language) {
+			case Common::ES_ESP:
+				stream = decryptFile("CMLS");
+				break;
+			case Common::FR_FRA:
+				stream = decryptFile("CMLF");
+				break;
+			case Common::DE_DEU:
+				stream = decryptFile("CMLG");
+				break;
+			case Common::EN_ANY:
+				stream = decryptFile("CMLE");
+				break;
+			default:
+				error("Invalid or unsupported language: %x", _language);
+		}
+
 		loadMessagesVariableSize(stream, 0x11, 164);
 		delete stream;
 


Commit: 746f694b6362ec31fd32d0042325eaad49a78b1b
    https://github.com/scummvm/scummvm/commit/746f694b6362ec31fd32d0042325eaad49a78b1b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added support for parsing (hardcoded) font for castle (DOS)

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/freescape.h
    engines/freescape/games/castle.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 351b96692db..b0253e24604 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -722,15 +722,20 @@ void FreescapeEngine::drawStringInSurface(const Common::String &str, int x, int
 	Common::String ustr = str;
 	ustr.toUppercase();
 
+	int sizeX = 8;
+	int sizeY = isCastle() ? 8 : 6;
+	int sep = isCastle() ? 9 : 8;
+	int additional = isCastle() ? 0 : 1;
+
 	if (isDOS() || isSpectrum() || isCPC() || isC64()) {
 		for (uint32 c = 0; c < ustr.size(); c++) {
 			assert(ustr[c] >= 32);
-			for (int j = 0; j < 6; j++) {
-				for (int i = 0; i < 8; i++) {
-					if (_font.get(48 * (offset + ustr[c] - 32) + 1 + j * 8 + i))
-						surface->setPixel(x + 8 - i + 8 * c, y + j, fontColor);
+			for (int j = 0; j < sizeY; j++) {
+				for (int i = 0; i < sizeX; i++) {
+					if (_font.get(sizeX * sizeY * (offset + ustr[c] - 32) + additional + j * 8 + i))
+						surface->setPixel(x + 8 - i + sep * c, y + j, fontColor);
 					else
-						surface->setPixel(x + 8 - i + 8 * c, y + j, backColor);
+						surface->setPixel(x + 8 - i + sep * c, y + j, backColor);
 				}
 			}
 		}
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 66d56c02e82..256c1d5338c 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -358,6 +358,7 @@ public:
 	void loadMessagesVariableSize(Common::SeekableReadStream *file, int offset, int number);
 
 	void loadFonts(Common::SeekableReadStream *file, int offset);
+	void loadFonts(byte *font, int charNumber);
 	Common::StringArray _currentAreaMessages;
 	Common::StringArray _currentEphymeralMessages;
 	Common::BitArray _font;
@@ -565,12 +566,13 @@ public:
 	CastleEngine(OSystem *syst, const ADGameDescription *gd);
 	~CastleEngine();
 
-
 	Graphics::ManagedSurface *_option;
+	void initGameState() override;
 	void titleScreen() override;
 	void loadAssetsDOSFullGame() override;
 	void drawUI() override;
 
+	void executePrint(FCLInstruction &instruction) override;
 	void gotoArea(uint16 areaID, int entranceID) override;
 	Common::Error saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave = false) override;
 	Common::Error loadGameStreamExtended(Common::SeekableReadStream *stream) override;
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index c5747264ea2..254db2de65b 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -23,6 +23,7 @@
 #include "common/memstream.h"
 
 #include "freescape/freescape.h"
+#include "freescape/language/8bitDetokeniser.h"
 
 namespace Freescape {
 
@@ -99,6 +100,68 @@ byte kCastleBorderDOSPalette[16][3] = {
 	{0x00, 0x00, 0x00}
 };
 
+byte kFreescapeCastleFont[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x1c, 0x1c, 0x1c, 0x18, 0x18, 0x00, 0x18, 0x18,
+	0x66, 0x66, 0x44, 0x22, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00,
+	0x10, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x10, 0x00,
+	0x3c, 0x42, 0x9d, 0xb1, 0xb1, 0x9d, 0x42, 0x3c,
+	0x78, 0xcc, 0xcc, 0x78, 0xdb, 0xcf, 0xce, 0x7b,
+	0x30, 0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x20, 0x40, 0x40, 0x40, 0x40, 0x20, 0x10,
+	0x10, 0x08, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10,
+	0x10, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x10, 0x00,
+	0x00, 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x08, 0x10,
+	0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
+	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+	0x18, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x18,
+	0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x9e, 0x61, 0x01, 0x7e, 0xe0, 0xc6, 0xe3, 0xfe,
+	0xee, 0x73, 0x03, 0x3e, 0x03, 0x01, 0x7f, 0xe6,
+	0x0e, 0x1c, 0x38, 0x71, 0xfd, 0xe6, 0x0c, 0x0c,
+	0xfd, 0x86, 0x80, 0x7e, 0x07, 0x63, 0xc7, 0x7c,
+	0x3d, 0x66, 0xc0, 0xf0, 0xfc, 0xc6, 0x66, 0x3c,
+	0xb3, 0x4e, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x3c,
+	0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc2, 0xfe, 0x4c,
+	0x3c, 0x4e, 0xc6, 0xc6, 0x4e, 0x36, 0x46, 0x3c,
+	0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00,
+	0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x08, 0x10,
+	0x03, 0x0c, 0x30, 0xc0, 0x30, 0x0c, 0x03, 0x00,
+	0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
+	0xc0, 0x30, 0x0c, 0x03, 0x0c, 0x30, 0xc0, 0x00,
+	0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x30, 0x00, 0x30,
+	0x00, 0x08, 0x0c, 0xfe, 0xff, 0xfe, 0x0c, 0x08,
+	0x1e, 0x1c, 0x1e, 0x66, 0xbe, 0x26, 0x43, 0xe3,
+	0xee, 0x73, 0x23, 0x3e, 0x23, 0x21, 0x7f, 0xe6,
+	0x39, 0x6e, 0xc6, 0xc0, 0xc0, 0xc2, 0x63, 0x3e,
+	0xec, 0x72, 0x23, 0x23, 0x23, 0x23, 0x72, 0xec,
+	0xce, 0x7f, 0x61, 0x6c, 0x78, 0x61, 0x7f, 0xce,
+	0xce, 0x7f, 0x61, 0x6c, 0x78, 0x60, 0x60, 0xf0,
+	0x3d, 0x66, 0xc0, 0xc1, 0xce, 0xc6, 0x66, 0x3c,
+	0xe7, 0x66, 0x66, 0x6e, 0x76, 0x66, 0x66, 0xe7,
+	0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x66,
+	0x33, 0x1e, 0x0c, 0x8c, 0x4c, 0xcc, 0xdc, 0x78,
+	0xf2, 0x67, 0x64, 0x68, 0x7e, 0x66, 0x66, 0xf3,
+	0xd8, 0x70, 0x60, 0x60, 0x66, 0x61, 0xf3, 0x7e,
+	0xc3, 0x66, 0x6e, 0x76, 0x56, 0x46, 0x46, 0xef,
+	0x87, 0x62, 0x72, 0x7a, 0x5e, 0x4e, 0x46, 0xe1,
+	0x18, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x18,
+	0xec, 0x72, 0x63, 0x63, 0x72, 0x6c, 0x60, 0xf0,
+	0x3c, 0x66, 0xc3, 0xc3, 0x66, 0x3c, 0x31, 0x1e,
+	0xec, 0x72, 0x63, 0x63, 0x76, 0x6c, 0x66, 0xf1,
+	0x79, 0x86, 0x80, 0x7e, 0x07, 0x63, 0xc7, 0x7c,
+	0x01, 0x7f, 0xfe, 0x98, 0x58, 0x18, 0x18, 0x3c,
+	0xf7, 0x62, 0x62, 0x62, 0x62, 0x62, 0xf2, 0x3c,
+	0xf3, 0x61, 0x72, 0x72, 0x32, 0x32, 0x1c, 0x3e,
+	0xc3, 0x62, 0x62, 0x6a, 0x6e, 0x76, 0x66, 0xc3,
+	0xf3, 0x72, 0x3c, 0x38, 0x1c, 0x3c, 0x4e, 0xcf,
+	0xe3, 0x72, 0x34, 0x38, 0x18, 0x18, 0x18, 0x3c,
+	0x7f, 0x87, 0x0e, 0x1c, 0x38, 0x71, 0xfd, 0xe6,
+};
+
 Common::SeekableReadStream *CastleEngine::decryptFile(const Common::String filename) {
 	Common::File file;
 	file.open(filename);
@@ -160,6 +223,7 @@ void CastleEngine::loadAssetsDOSFullGame() {
 				error("Invalid or unsupported language: %x", _language);
 		}
 
+		loadFonts(kFreescapeCastleFont, 59);
 		loadMessagesVariableSize(stream, 0x11, 164);
 		delete stream;
 
@@ -238,12 +302,36 @@ void CastleEngine::gotoArea(uint16 areaID, int entranceID) {
 void CastleEngine::drawUI() {
 	_gfx->setViewport(_fullscreenViewArea);
 
+	uint32 color = 10;
+	uint8 r, g, b;
+
+	_gfx->readFromPalette(color, r, g, b);
+	uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
+
+	color = 0;
+
+	_gfx->readFromPalette(color, r, g, b);
+	uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
+
 	Graphics::Surface *surface = new Graphics::Surface();
 	surface->create(_screenW, _screenH, _gfx->_texturePixelFormat);
 	uint32 gray = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0xA0, 0xA0, 0xA0);
 	surface->fillRect(_fullscreenViewArea, gray);
 	drawCrossair(surface);
 
+	Common::Rect backRect(97, 181, 232, 190);
+	surface->fillRect(backRect, back);
+
+	Common::String message;
+	int deadline;
+	getLatestMessages(message, deadline);
+	if (deadline <= _countdown) {
+		drawStringInSurface(message, 97, 182, front, back, surface);
+		_temporaryMessages.push_back(message);
+		_temporaryMessageDeadlines.push_back(deadline);
+	} else
+		drawStringInSurface(_currentArea->_name, 97, 182, front, back, surface);
+
 	if (!_uiTexture)
 		_uiTexture = _gfx->createTexture(surface);
 	else
@@ -257,6 +345,48 @@ void CastleEngine::drawUI() {
 	_gfx->setViewport(_viewArea);
 }
 
+void CastleEngine::initGameState() {
+	_flyMode = false;
+	_noClipMode = false;
+	_shootingFrames = 0;
+	_underFireFrames = 0;
+	_yaw = 0;
+	_pitch = 0;
+
+	for (int i = 0; i < k8bitMaxVariable; i++) // TODO: check maximum variable
+		_gameStateVars[i] = 0;
+
+	for (auto &it : _areaMap) {
+		it._value->resetArea();
+		_gameStateBits[it._key] = 0;
+	}
+
+	//_gameStateVars[k8bitVariableEnergy] = _initialFuel;
+	//_gameStateVars[k8bitVariableShield] = _initialShield;
+
+	_playerHeightNumber = 1;
+	_playerHeight = _playerHeights[_playerHeightNumber];
+	removeTimers();
+	startCountdown(_initialCountdown);
+	_lastMinute = 0;
+	_demoIndex = 0;
+	_demoEvents.clear();
+}
+
+void CastleEngine::executePrint(FCLInstruction &instruction) {
+	uint16 index = instruction._source;
+	_currentAreaMessages.clear();
+	if (index > 127) {
+		index = _messagesList.size() - (index - 254) - 2;
+		// TODO
+		//drawFullscreenMessage(_messagesList[index]);
+		return;
+	}
+	debugC(1, kFreescapeDebugCode, "Printing message %d: \"%s\"", index, _messagesList[index].c_str());
+	insertTemporaryMessage(_messagesList[index], _countdown - 3);
+}
+
+
 Common::Error CastleEngine::saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave) {
 	return Common::kNoError;
 }
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index 61265e7c4f2..bf54bcab306 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -664,7 +664,8 @@ void FreescapeEngine::load8bitBinary(Common::SeekableReadStream *file, int offse
 			_initialCountdown = 359999; // 99:59:59
 	} else if (isDark())
 		_initialCountdown = 2 * 3600; // 02:00:00
-
+	else if (isCastle())
+		_initialCountdown = 1000000000;
 
 	if (isAmiga() || isAtariST())
 		file->seek(offset + 0x190);
@@ -734,6 +735,17 @@ void FreescapeEngine::loadBundledImages() {
 	}*/
 }
 
+void FreescapeEngine::loadFonts(byte *font, int charNumber) {
+	if (isDOS() || isSpectrum() || isCPC() || isC64()) {
+		_font.set_size(64 * charNumber);
+		_font.set_bits(font);
+	} else if (isAmiga() || isAtariST()) {
+		error("Not implemented yet");
+	}
+	_fontLoaded = true;
+}
+
+
 void FreescapeEngine::loadFonts(Common::SeekableReadStream *file, int offset) {
 	file->seek(offset);
 	int charNumber = 60;


Commit: 764eb59b8e37be61c0c1e5f71ca7662763ee0385
    https://github.com/scummvm/scummvm/commit/764eb59b8e37be61c0c1e5f71ca7662763ee0385
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: refactored castle (DOS) to use drawDOSUI

Changed paths:
    engines/freescape/freescape.h
    engines/freescape/games/castle.cpp


diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 256c1d5338c..ff8d87ad6bf 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -570,7 +570,8 @@ public:
 	void initGameState() override;
 	void titleScreen() override;
 	void loadAssetsDOSFullGame() override;
-	void drawUI() override;
+
+	void drawDOSUI(Graphics::Surface *surface) override;
 
 	void executePrint(FCLInstruction &instruction) override;
 	void gotoArea(uint16 areaID, int entranceID) override;
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 254db2de65b..173cb3c9c2a 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -299,9 +299,7 @@ void CastleEngine::gotoArea(uint16 areaID, int entranceID) {
 	resetInput();
 }
 
-void CastleEngine::drawUI() {
-	_gfx->setViewport(_fullscreenViewArea);
-
+void CastleEngine::drawDOSUI(Graphics::Surface *surface) {
 	uint32 color = 10;
 	uint8 r, g, b;
 
@@ -313,12 +311,6 @@ void CastleEngine::drawUI() {
 	_gfx->readFromPalette(color, r, g, b);
 	uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
 
-	Graphics::Surface *surface = new Graphics::Surface();
-	surface->create(_screenW, _screenH, _gfx->_texturePixelFormat);
-	uint32 gray = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0xA0, 0xA0, 0xA0);
-	surface->fillRect(_fullscreenViewArea, gray);
-	drawCrossair(surface);
-
 	Common::Rect backRect(97, 181, 232, 190);
 	surface->fillRect(backRect, back);
 
@@ -331,18 +323,6 @@ void CastleEngine::drawUI() {
 		_temporaryMessageDeadlines.push_back(deadline);
 	} else
 		drawStringInSurface(_currentArea->_name, 97, 182, front, back, surface);
-
-	if (!_uiTexture)
-		_uiTexture = _gfx->createTexture(surface);
-	else
-		_uiTexture->update(surface);
-
-	_gfx->drawTexturedRect2D(_fullscreenViewArea, _fullscreenViewArea, _uiTexture);
-
-	surface->free();
-	delete surface;
-
-	_gfx->setViewport(_viewArea);
 }
 
 void CastleEngine::initGameState() {


Commit: 9e2f4b760e4bb0da993814268009ac6ee0647010
    https://github.com/scummvm/scummvm/commit/9e2f4b760e4bb0da993814268009ac6ee0647010
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: initial support for castle demo (EGA)

Changed paths:
    engines/freescape/detection.cpp
    engines/freescape/freescape.h
    engines/freescape/games/castle.cpp
    engines/freescape/language/8bitDetokeniser.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 097a5e98d48..1ebd60ab679 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -335,6 +335,20 @@ static const ADGameDescription gameDescriptions[] = {
 		ADGF_UNSUPPORTED,
 		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
+	{
+		"castlemaster",
+		"Demo",
+		{
+			{"CASTLE.EXE", 0, "cbcf475b2d993c831a34a5203d2643e1", 2022},
+			{"CMDC.EXE", 0, "278fd1a96c61db71d952af472164ac57", 56526},
+			{"CMDE.EXE", 0, "428555ba83bc64d69bc2f7cb385f04f2", 88590},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformDOS,
+		ADGF_UNSTABLE | ADGF_DEMO,
+		GUIO1(GUIO_NOMIDI)
+	},
 	{
 		"castlemaster",
 		"",
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index ff8d87ad6bf..7f5a2711797 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -570,6 +570,7 @@ public:
 	void initGameState() override;
 	void titleScreen() override;
 	void loadAssetsDOSFullGame() override;
+	void loadAssetsDOSDemo() override;
 
 	void drawDOSUI(Graphics::Surface *surface) override;
 
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 173cb3c9c2a..1e2dc7fef20 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -243,6 +243,46 @@ void CastleEngine::loadAssetsDOSFullGame() {
 	// load8bitBinary(file, 0x791a, 16);
 }
 
+void CastleEngine::loadAssetsDOSDemo() {
+	Common::File file;
+	Common::SeekableReadStream *stream = nullptr;
+
+	if (_renderMode == Common::kRenderEGA) {
+		_viewArea = Common::Rect(40, 33, 280, 152);
+
+		file.open("CMLE.DAT");
+		_title = load8bitBinImage(&file, 0x0);
+		_title->setPalette((byte *)&kCastleTitleDOSPalette, 0, 16);
+		file.close();
+
+		file.open("CMOE.DAT");
+		_option = load8bitBinImage(&file, 0x0);
+		_option->setPalette((byte *)&kCastleOptionDOSPalette, 0, 16);
+		file.close();
+
+		file.open("CME.DAT");
+		_border = load8bitBinImage(&file, 0x0);
+		_border->setPalette((byte *)&kCastleBorderDOSPalette, 0, 16);
+		file.close();
+
+		stream = decryptFile("CMLD"); // Only english
+
+		loadFonts(kFreescapeCastleFont, 59);
+		loadMessagesVariableSize(stream, 0x11, 164);
+		delete stream;
+
+		stream = decryptFile("CDEDF");
+		load8bitBinary(stream, 0, 16);
+		for (auto &it : _areaMap)
+			it._value->addStructure(_areaMap[255]);
+
+		_areaMap[2]->addFloor();
+		delete stream;
+	} else
+		error("Not implemented yet");
+}
+
+
 void CastleEngine::titleScreen() {
 	if (isAmiga() || isAtariST()) // These releases has their own screens
 		return;
diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index 775140d972a..e74d4182fef 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -115,7 +115,7 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		// check we have enough bytes left to read
 		if (opcode > 48) {
 			debugC(1, kFreescapeDebugParser, "%s", detokenisedStream.c_str());
-			if (opcode != 0x3f)
+			if (opcode != 0x3f && opcode != 0x3b)
 				error("ERROR: failed to read opcode: %x", opcode);
 			break;
 		}
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index bf54bcab306..21cb38ed360 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -553,7 +553,7 @@ void FreescapeEngine::load8bitBinary(Common::SeekableReadStream *file, int offse
 	debugC(1, kFreescapeDebugParser, "Number of areas: %d", numberOfAreas);
 
 	if (isDOS() && isCastle()) // Castle Master for DOS has an invalid number of areas
-		numberOfAreas = 104;
+		numberOfAreas = isDemo() ? 31 : 104;
 
 	uint32 dbSize = readField(file, 16);
 	debugC(1, kFreescapeDebugParser, "Database ends at %x", dbSize);


Commit: 9dbe3433aa5b3b59367f445173256cc61169e8de
    https://github.com/scummvm/scummvm/commit/9dbe3433aa5b3b59367f445173256cc61169e8de
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: expanded floor size

Changed paths:
    engines/freescape/area.cpp


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 3039bc5c5e1..ac168712e88 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -341,8 +341,8 @@ void Area::addFloor() {
 		ObjectType::kCubeType,
 		id,
 		0,                             // flags
-		Math::Vector3d(0, -1, 0),      // Position
-		Math::Vector3d(4128, 1, 4128), // size
+		Math::Vector3d(-4128, -1, -4128),      // Position
+		Math::Vector3d(4128 * 4, 1, 4128 * 4), // size
 		gColors,
 		nullptr,
 		FCLInstructionVector());


Commit: 700c62949c959562188bd2907eae92413e98f2bd
    https://github.com/scummvm/scummvm/commit/700c62949c959562188bd2907eae92413e98f2bd
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: initial detection of castle demo for amiga

Changed paths:
    engines/freescape/detection.cpp
    engines/freescape/freescape.h
    engines/freescape/games/castle.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 1ebd60ab679..01b3f1d2412 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -349,6 +349,33 @@ static const ADGameDescription gameDescriptions[] = {
 		ADGF_UNSTABLE | ADGF_DEMO,
 		GUIO1(GUIO_NOMIDI)
 	},
+	{
+		"castlemaster",
+		"Demo",
+		{
+			{"cm", 0, "936b6ca92be53f122bd904a3397137e2", 1552},
+			{"x", 0, "c8c811439da0cf8a193e35feb5b5c6dc", 353388},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformAmiga,
+		ADGF_UNSTABLE | ADGF_DEMO,
+		GUIO1(GUIO_NOMIDI)
+	},
+	// Stampede Amiga, Issue 1, July 1990
+	{
+		"castlemaster",
+		"Demo",
+		{
+			{"cm", 0, "b7e713a0742fa09aa81c9606bbbba4af", 4068},
+			{"x", 0, "c8c811439da0cf8a193e35feb5b5c6dc", 353388},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformAmiga,
+		ADGF_UNSTABLE | ADGF_DEMO,
+		GUIO1(GUIO_NOMIDI)
+	},
 	{
 		"castlemaster",
 		"",
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 7f5a2711797..383586298c2 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -571,6 +571,7 @@ public:
 	void titleScreen() override;
 	void loadAssetsDOSFullGame() override;
 	void loadAssetsDOSDemo() override;
+	void loadAssetsAmigaDemo() override;
 
 	void drawDOSUI(Graphics::Surface *surface) override;
 
diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 1e2dc7fef20..4165ee42b2c 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -282,6 +282,34 @@ void CastleEngine::loadAssetsDOSDemo() {
 		error("Not implemented yet");
 }
 
+void CastleEngine::loadAssetsAmigaDemo() {
+	Common::File file;
+	file.open("x");
+	if (!file.isOpen())
+		error("Failed to open 'x' file");
+
+	loadMessagesVariableSize(&file, 0x8bb2, 164);
+	load8bitBinary(&file, 0x162a6, 16);
+	assert(0);
+	//loadPalettes(&file, 0x0);
+
+	//file.close();
+	//file.open("driller");
+	//if (!file.isOpen())
+	//	error("Failed to open 'driller' file");
+
+	//loadFonts(&file, 0xa30);
+	//loadMessagesFixedSize(&file, 0x3960, 14, 20);
+	//loadGlobalObjects(&file, 0x3716, 8);
+
+	file.close();
+	//file.open("soundfx");
+	//if (!file.isOpen())
+	//	error("Failed to open 'soundfx' executable for Amiga");
+
+	//loadSoundsFx(&file, 0, 25);
+}
+
 
 void CastleEngine::titleScreen() {
 	if (isAmiga() || isAtariST()) // These releases has their own screens
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index 21cb38ed360..3c35b3d3497 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -471,12 +471,12 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 			i++;
 		}
 	} else if (isCastle()) {
-		byte idx = file->readByte();
+		byte idx = readField(file, 8);
 		name = _messagesList[idx + 41];
-		extraColor[0] = file->readByte();
-		extraColor[1] = file->readByte();
-		extraColor[2] = file->readByte();
-		extraColor[3] = file->readByte();
+		extraColor[0] = readField(file, 8);
+		extraColor[1] = readField(file, 8);
+		extraColor[2] = readField(file, 8);
+		extraColor[3] = readField(file, 8);
 	}
 	debugC(1, kFreescapeDebugParser, "Area name: %s", name.c_str());
 


Commit: 626f6721d16f5a5c6b4a6fd1b50ca4c1b055da9d
    https://github.com/scummvm/scummvm/commit/626f6721d16f5a5c6b4a6fd1b50ca4c1b055da9d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: basic implementation of activation (only used in castle/crypt)

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/freescape.h
    engines/freescape/language/instruction.cpp
    engines/freescape/movement.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index b0253e24604..841e33f3c00 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -515,6 +515,12 @@ void FreescapeEngine::processInput() {
 			}
 			break;
 
+		case Common::EVENT_RBUTTONDOWN:
+			if (_hasFallen || !isCastle())
+				break;
+			activate();
+			break;
+
 		default:
 			break;
 		}
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 383586298c2..97b00b2d94e 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -241,6 +241,7 @@ public:
 	float _lastFrame;
 
 	// Interaction
+	void activate();
 	void shoot();
 	void traverseEntrance(uint16 entranceID);
 
@@ -277,9 +278,9 @@ public:
 	bool checkCollisions(bool executeCode);
 	Math::Vector3d _objExecutingCodeSize;
 	virtual void executeMovementConditions();
-	void executeObjectConditions(GeometricObject *obj, bool shot, bool collided);
+	void executeObjectConditions(GeometricObject *obj, bool shot, bool collided, bool activated);
 	void executeLocalGlobalConditions(bool shot, bool collided, bool timer);
-	void executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer);
+	void executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer, bool activated);
 
 	// Instructions
 	bool checkConditional(FCLInstruction &instruction, bool shot, bool collided, bool timer, bool activated);
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index da9ed9ba5d4..beb817e9841 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -89,7 +89,7 @@ Token::Type FCLInstruction::getType() {
 	return _type;
 }
 
-void FreescapeEngine::executeObjectConditions(GeometricObject *obj, bool shot, bool collided) {
+void FreescapeEngine::executeObjectConditions(GeometricObject *obj, bool shot, bool collided, bool activated) {
 	assert(obj != nullptr);
 	if (!obj->_conditionSource.empty()) {
 		_firstSound = true;
@@ -98,9 +98,11 @@ void FreescapeEngine::executeObjectConditions(GeometricObject *obj, bool shot, b
 			debugC(1, kFreescapeDebugCode, "Executing with collision flag: %s", obj->_conditionSource.c_str());
 		else if (shot)
 			debugC(1, kFreescapeDebugCode, "Executing with shot flag: %s", obj->_conditionSource.c_str());
+		else if (activated)
+			debugC(1, kFreescapeDebugCode, "Executing with activated flag: %s", obj->_conditionSource.c_str());
 		else
 			error("Neither shot or collided flag is set!");
-		executeCode(obj->_condition, shot, collided, false); // TODO: check this last parameter
+		executeCode(obj->_condition, shot, collided, false, activated); // TODO: check this last parameter
 	}
 }
 
@@ -113,17 +115,17 @@ void FreescapeEngine::executeLocalGlobalConditions(bool shot, bool collided, boo
 
 	for (uint i = 0; i < conditions.size(); i++) {
 		debugC(1, kFreescapeDebugCode, "%s", conditionSources[i].c_str());
-		executeCode(conditions[i], shot, collided, timer);
+		executeCode(conditions[i], shot, collided, timer, false);
 	}
 
 	debugC(1, kFreescapeDebugCode, "Executing global conditions (%d)", _conditions.size());
 	for (uint i = 0; i < _conditions.size(); i++) {
 		debugC(1, kFreescapeDebugCode, "%s", _conditionSources[i].c_str());
-		executeCode(_conditions[i], shot, collided, timer);
+		executeCode(_conditions[i], shot, collided, timer, false);
 	}
 }
 
-void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer) {
+void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer, bool activated) {
 	assert(!(shot && collided));
 	int ip = 0;
 	bool skip = false;
@@ -149,8 +151,8 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 			break;
 
 		case Token::CONDITIONAL:
-			if (checkConditional(instruction, shot, collided, timer, false)) // TODO: implement interaction
-				executeCode(*instruction._thenInstructions, shot, collided, timer);
+			if (checkConditional(instruction, shot, collided, timer, activated))
+				executeCode(*instruction._thenInstructions, shot, collided, timer, activated);
 			// else branch is always empty
 			assert(instruction._elseInstructions == nullptr);
 			break;
diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index aa432f1e3ee..7d89bafe446 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -69,6 +69,29 @@ void FreescapeEngine::traverseEntrance(uint16 entranceID) {
 	_sensors = _currentArea->getSensors();
 }
 
+void FreescapeEngine::activate() {
+	Common::Point center(_viewArea.left + _viewArea.width() / 2, _viewArea.top + _viewArea.height() / 2);
+	float xoffset = _crossairPosition.x - center.x;
+	float yoffset = _crossairPosition.y - center.y;
+	xoffset = xoffset * 0.33;
+	yoffset = yoffset * 0.50;
+
+	Math::Vector3d direction = directionToVector(_pitch - yoffset, _yaw - xoffset);
+	Math::Ray ray(_position, direction);
+	Object *interacted = _currentArea->shootRay(ray);
+	if (interacted) {
+		GeometricObject *gobj = (GeometricObject *)interacted;
+		debugC(1, kFreescapeDebugMove, "Interact with object %d with flags %x", gobj->getObjectID(), gobj->getObjectFlags());
+
+		if (!gobj->_conditionSource.empty())
+			debugC(1, kFreescapeDebugMove, "Must use interact = true when executing: %s", gobj->_conditionSource.c_str());
+
+		executeObjectConditions(gobj, false, false, true);
+	}
+	//executeLocalGlobalConditions(true, false, false); // Only execute "on shot" room/global conditions
+}
+
+
 void FreescapeEngine::shoot() {
 	playSound(1, false);
 	g_system->delayMillis(2);
@@ -90,7 +113,7 @@ void FreescapeEngine::shoot() {
 		if (!gobj->_conditionSource.empty())
 			debugC(1, kFreescapeDebugMove, "Must use shot = true when executing: %s", gobj->_conditionSource.c_str());
 
-		executeObjectConditions(gobj, true, false);
+		executeObjectConditions(gobj, true, false, false);
 	}
 	executeLocalGlobalConditions(true, false, false); // Only execute "on shot" room/global conditions
 }
@@ -375,7 +398,7 @@ bool FreescapeEngine::checkCollisions(bool executeCode) {
 			largeObjectWasBlocking = true;
 		}
 
-		executeObjectConditions(gobj, false, true);
+		executeObjectConditions(gobj, false, true, false);
 
 		if (areaID != _currentArea->getAreaID())
 			break;


Commit: acf7e1e9409084bdebdf4e5d1ca20fe4161f6c57
    https://github.com/scummvm/scummvm/commit/acf7e1e9409084bdebdf4e5d1ca20fe4161f6c57
Author: Donovan Watteau (contrib at dwatteau.fr)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: Fix -Wformat warnings

Changed paths:
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index 3c35b3d3497..e570d4dbb65 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -191,7 +191,7 @@ Object *FreescapeEngine::load8bitObject(Common::SeekableReadStream *file) {
 			// instructions = getInstructions(conditionSource);
 			debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
 		}
-		debugC(1, kFreescapeDebugParser, "End of object at %lx", file->pos());
+		debugC(1, kFreescapeDebugParser, "End of object at %lx", long(file->pos()));
 
 		if (!GeometricObject::isPolygon(objectType))
 			position = 32 * position;
@@ -219,7 +219,7 @@ Object *FreescapeEngine::load8bitObject(Common::SeekableReadStream *file) {
 			byteSizeOfObject = 0;
 		}
 		assert(byteSizeOfObject == 0);
-		debugC(1, kFreescapeDebugParser, "End of object at %lx", file->pos());
+		debugC(1, kFreescapeDebugParser, "End of object at %lx", long(file->pos()));
 		// create an entrance
 		return new Entrance(
 			objectID,
@@ -260,7 +260,7 @@ Object *FreescapeEngine::load8bitObject(Common::SeekableReadStream *file) {
 			conditionSource = detokenise8bitCondition(conditionArray, instructions, isCastle());
 			debugC(1, kFreescapeDebugParser, "%s", conditionSource.c_str());
 		}
-		debugC(1, kFreescapeDebugParser, "End of object at %lx", file->pos());
+		debugC(1, kFreescapeDebugParser, "End of object at %lx", long(file->pos()));
 		// create an entrance
 		return new Sensor(
 			objectID,
@@ -436,7 +436,7 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 	debugC(1, kFreescapeDebugParser, "Area %d", areaNumber);
 	debugC(1, kFreescapeDebugParser, "Flags: %d Objects: %d", areaFlags, numberOfObjects);
 	// debug("Condition Ptr: %x", cPtr);
-	debugC(1, kFreescapeDebugParser, "Pos before first object: %lx", file->pos());
+	debugC(1, kFreescapeDebugParser, "Pos before first object: %lx", long(file->pos()));
 
 	// Driller specific
 	uint8 gasPocketX = 0;
@@ -543,7 +543,7 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 		}
 	}
 
-	debugC(1, kFreescapeDebugParser, "End of area at %lx", file->pos());
+	debugC(1, kFreescapeDebugParser, "End of area at %lx", long(file->pos()));
 	return area;
 }
 
@@ -617,7 +617,7 @@ void FreescapeEngine::load8bitBinary(Common::SeekableReadStream *file, int offse
 		loadDemoData(file, offset + demoDataTable, 128); // TODO: check this size
 
 	file->seek(offset + globalByteCodeTable);
-	debugC(1, kFreescapeDebugParser, "Position: %lx\n", file->pos());
+	debugC(1, kFreescapeDebugParser, "Position: %lx\n", long(file->pos()));
 
 	uint8 numConditions = readField(file, 8);
 	debugC(1, kFreescapeDebugParser, "%d global conditions", numConditions);
@@ -625,7 +625,7 @@ void FreescapeEngine::load8bitBinary(Common::SeekableReadStream *file, int offse
 		FCLInstructionVector instructions;
 		// get the length
 		uint32 lengthOfCondition = readField(file, 8);
-		debugC(1, kFreescapeDebugParser, "length of condition: %d at %lx", lengthOfCondition, file->pos());
+		debugC(1, kFreescapeDebugParser, "length of condition: %d at %lx", lengthOfCondition, long(file->pos()));
 		// get the condition
 		if (lengthOfCondition > 0) {
 			Common::Array<uint8> conditionArray = readArray(file, lengthOfCondition);
@@ -673,7 +673,7 @@ void FreescapeEngine::load8bitBinary(Common::SeekableReadStream *file, int offse
 		file->seek(offset + 0xc8);
 	// file->seek(offset + 0x4f); //CPC
 
-	debugC(1, kFreescapeDebugParser, "areas index at: %lx", file->pos());
+	debugC(1, kFreescapeDebugParser, "areas index at: %lx", long(file->pos()));
 	uint16 *fileOffsetForArea = new uint16[numberOfAreas];
 	for (uint16 area = 0; area < numberOfAreas; area++) {
 		fileOffsetForArea[area] = readField(file, 16);


Commit: f4555942b023422d40c4114951faafa852d798e2
    https://github.com/scummvm/scummvm/commit/f4555942b023422d40c4114951faafa852d798e2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: new collision code

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/area.h
    engines/freescape/freescape.h
    engines/freescape/movement.cpp


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index ac168712e88..585256345ec 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -258,6 +258,121 @@ ObjectArray Area::checkCollisions(const Math::AABB &boundingBox) {
 	return collided;
 }
 
+float lineToPlane(Math::Vector3d const &p, Math::Vector3d const &u,  Math::Vector3d const &v, Math::Vector3d const &n) {
+	float NdotU = n.dotProduct(u);
+	if (NdotU == 0)
+		return INFINITY;
+
+	return n.dotProduct(v - p) / NdotU;
+}
+
+bool between(float x, float a, float b) {
+	return x >= a && x <= b;
+}
+
+float sweepAABB(Math::AABB const &a, Math::AABB const &b, Math::Vector3d const &direction, Math::Vector3d &normal) {
+	Math::Vector3d m = b.getMin() - a.getMax();
+	Math::Vector3d mh = a.getMax() - a.getMin() + b.getMax() - b.getMin();
+
+	float h = 1.0;
+	float s = 0.0;
+	Math::Vector3d zero;
+
+	// X min
+	s = lineToPlane(zero, direction, m, Math::Vector3d(-1, 0, 0));
+	if (s >= 0 && direction.x() > 0 && s < h && between(s * direction.y(), m.y(), m.y()+mh.y()) && between(s * direction.z(), m.z(), m.z() + mh.z())) {
+		h = s;
+		normal = Math::Vector3d(-1, 0, 0);
+	}
+
+	// X max
+	m.x() = m.x() + mh.x();
+	s = lineToPlane(zero, direction, m, Math::Vector3d(1, 0, 0));
+	if (s >= 0 && direction.x() < 0 && s < h && between(s * direction.y(), m.y(), m.y() + mh.y()) && between(s * direction.z(), m.z(), m.z() + mh.z())) {
+		h = s;
+		normal = Math::Vector3d(1, 0, 0);
+	}
+
+	m.x() = m.x() - mh.x();
+	// Y min
+	s = lineToPlane(zero, direction, m, Math::Vector3d(0, -1, 0));
+	if (s >= 0 && direction.y() > 0 && s < h && between(s * direction.x(), m.x(), m.x() + mh.x()) && between(s * direction.z(), m.z(), m.z() + mh.z())) {
+		h = s;
+		normal = Math::Vector3d(0, -1, 0);
+	}
+
+	// Y max
+	m.y() = m.y() + mh.y();
+	s = lineToPlane(zero, direction, m, Math::Vector3d(0, 1, 0));
+	if (s >= 0 && direction.y() < 0 && s < h && between(s * direction.x(), m.x(), m.x() + mh.x()) && between(s * direction.z(), m.z(), m.z() + mh.z())) {
+		h = s;
+		normal = Math::Vector3d(0, 1, 0);
+	}
+
+	m.y() = m.y() - mh.y();
+
+	// Z min
+	s = lineToPlane(zero, direction, m, Math::Vector3d(0, 0, -1));
+	if (s >= 0 && direction.z() > 0 && s < h && between(s * direction.x(), m.x() , m.x() + mh.x()) && between(s * direction.y(), m.y(), m.y() + mh.y())) {
+		h = s;
+		normal = Math::Vector3d(0, 0, -1);
+	}
+
+	// Z max
+	m.z() = m.z() + mh.z();
+	s = lineToPlane(zero, direction, m, Math::Vector3d(0, 0, 1));
+	if (s >= 0 && direction.z() < 0 && s < h && between(s * direction.x(), m.x(), m.x() + mh.x()) && between(s * direction.y(), m.y(), m.y() + mh.y())) {
+		h = s;
+		normal = Math::Vector3d(0, 0, 1);
+	}
+
+	//debug("%f", h);
+	return h;
+}
+
+Math::AABB createPlayerAABB(Math::Vector3d const position, int playerHeight) {
+	Math::AABB boundingBox(position, position);
+
+	Math::Vector3d v1(position.x() + 1, position.y() + 1, position.z() + 1);
+	Math::Vector3d v2(position.x() - 1, position.y() - playerHeight, position.z() - 1);
+
+	boundingBox.expand(v1);
+	boundingBox.expand(v2);
+	return boundingBox;
+}
+
+Math::Vector3d Area::resolveCollisions(const Math::Vector3d &lastPosition_, const Math::Vector3d &newPosition_, int playerHeight) {
+	Math::Vector3d position = newPosition_;
+	Math::Vector3d lastPosition = lastPosition_;
+
+	float epsilon = 1.5;
+	int i = 0;
+	while (true) {
+		float distance = 1.0;
+		Math::Vector3d normal;
+		Math::Vector3d direction = position - lastPosition;
+
+		Math::AABB boundingBox = createPlayerAABB(lastPosition, playerHeight);
+		for (auto &obj : _drawableObjects) {
+			if (!obj->isDestroyed() && !obj->isInvisible()) {
+				GeometricObject *gobj = (GeometricObject *)obj;
+				Math::Vector3d collidedNormal;
+				float collidedDistance = sweepAABB(boundingBox, gobj->_boundingBox, direction, collidedNormal);
+				if (collidedDistance < distance) {
+					distance = collidedDistance;
+					normal = collidedNormal;
+				}
+			}
+		}
+		position = lastPosition + distance * direction + epsilon * normal;
+		if (distance >= 1.0)
+			break;
+		i++;
+		assert(i <= 5);
+	}
+	return position;
+}
+
 bool Area::checkInSight(const Math::Ray &ray, float maxDistance) {
 	Math::Vector3d direction = ray.getDirection();
 	direction.normalize();
diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index 294b73a5c88..a6fa1e2fdc7 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -55,6 +55,7 @@ public:
 	Object *shootRay(const Math::Ray &ray);
 	bool checkInSight(const Math::Ray &ray, float maxDistance);
 	ObjectArray checkCollisions(const Math::AABB &boundingBox);
+	Math::Vector3d resolveCollisions(Math::Vector3d const &lastPosition, Math::Vector3d const &newPosition, int playerHeight);
 	void addObjectFromArea(int16 id, Area *global);
 	void addObject(Object *obj);
 	void addFloor();
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 97b00b2d94e..6e3d666dec9 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -225,6 +225,7 @@ public:
 	virtual void pressedKey(const int keycode);
 	virtual bool onScreenControls(Common::Point mouse);
 	void move(CameraMovement direction, uint8 scale, float deltaTime);
+	void resolveCollisions(Math::Vector3d newPosition);
 	virtual void checkIfStillInArea();
 	void changePlayerHeight(int index);
 	void increaseStepSize();
@@ -275,7 +276,7 @@ public:
 	Common::Array<Common::String> _conditionSources;
 	Common::Array<FCLInstructionVector> _conditions;
 
-	bool checkCollisions(bool executeCode);
+	void runCollisionConditions(Math::Vector3d const lastPosition, Math::Vector3d const newPosition);
 	Math::Vector3d _objExecutingCodeSize;
 	virtual void executeMovementConditions();
 	void executeObjectConditions(GeometricObject *obj, bool shot, bool collided, bool activated);
diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index 7d89bafe446..e5245723426 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -158,7 +158,7 @@ void FreescapeEngine::rise() {
 		changePlayerHeight(_playerHeightNumber);
 	}
 
-	bool collided = checkCollisions(true) || _position.y() >= 2016;
+	bool collided = /*checkCollisions(true) ||*/ _position.y() >= 2016;
 	if (collided) {
 		if (_currentArea->getAreaID() == previousAreaID) {
 			if (_flyMode)
@@ -181,7 +181,7 @@ void FreescapeEngine::lower() {
 
 	if (_flyMode) {
 		_position.setValue(1, _position.y() - (_playerSteps[_playerStepIndex] * 0.5));
-		bool collided = checkCollisions(true);
+		bool collided = false; //checkCollisions(true);
 		if (collided) {
 			if (_currentArea->getAreaID() == previousAreaID) {
 				_position = _lastPosition;
@@ -222,21 +222,26 @@ void FreescapeEngine::move(CameraMovement direction, uint8 scale, float deltaTim
 	stepFront.z() = floor(stepFront.z()) + 0.5;
 
 	float positionY = _position.y();
+	Math::Vector3d destination;
 	switch (direction) {
 	case kForwardMovement:
-		_position = _position + stepFront;
+		destination = _position + stepFront;
 		break;
 	case kBackwardMovement:
-		_position = _position - stepFront;
+		destination = _position - stepFront;
 		break;
 	case kRightMovement:
-		_position = _position - stepRight;
+		destination = _position - stepRight;
 		break;
 	case kLeftMovement:
-		_position = _position + stepRight;
+		destination = _position + stepRight;
 		break;
 	}
 
+	if (!_flyMode)
+		destination.y() = positionY;
+	resolveCollisions(destination);
+	/*
 	// restore y coordinate
 	if (!_flyMode)
 		_position.set(_position.x(), positionY, _position.z());
@@ -306,6 +311,7 @@ void FreescapeEngine::move(CameraMovement direction, uint8 scale, float deltaTim
 			}
 		}
 	}
+	*/
 
 	_lastPosition = _position;
 	debugC(1, kFreescapeDebugMove, "new player position: %f, %f, %f", _position.x(), _position.y(), _position.z());
@@ -315,66 +321,64 @@ void FreescapeEngine::move(CameraMovement direction, uint8 scale, float deltaTim
 	clearGameBit(31);
 }
 
-bool FreescapeEngine::checkFloor(Math::Vector3d currentPosition) {
-	debugC(1, kFreescapeDebugMove, "Checking floor under the player");
-	bool collided = checkCollisions(false);
-	assert(!collided);
+void FreescapeEngine::resolveCollisions(Math::Vector3d const position) {
+	Math::Vector3d newPosition = position;
+	Math::Vector3d lastPosition = _lastPosition;
 
-	_position.set(_position.x(), _position.y() - 2, _position.z());
-	collided = checkCollisions(false);
-	_position = currentPosition;
-	return collided;
-}
+	int previousAreaID = _currentArea->getAreaID();
+	runCollisionConditions(lastPosition, newPosition);
+	if (_currentArea->getAreaID() != previousAreaID) {
+		return;
+	}
 
-bool FreescapeEngine::tryStepUp(Math::Vector3d currentPosition) {
-	debugC(1, kFreescapeDebugMove, "Try to step up!");
-	_position.set(_position.x(), _position.y() + 64, _position.z());
-	bool collided = checkCollisions(false);
-	if (collided) {
-		_position = currentPosition;
-		return false;
-	} else {
-		// Try to step down
-		return true;
+	newPosition = _currentArea->resolveCollisions(lastPosition, newPosition, _playerHeight);
+
+	if (_flyMode) {
+		_position = newPosition;
+		return;
 	}
-}
 
-bool FreescapeEngine::tryStepDown(Math::Vector3d currentPosition) {
-	debugC(1, kFreescapeDebugMove, "Try to step down!");
-	_position.set(_position.x(), _position.y() - 1, _position.z());
-	if (checkFloor(_position)) {
-		return true;
-	} else {
-		_position = currentPosition;
-		return false;
+	if ((lastPosition - newPosition).length() < 1) { // If the player has not moved
+		// Try to step up
+		newPosition = position;
+		newPosition.y() = newPosition.y() + 64;
+
+		lastPosition = _lastPosition;
+		lastPosition.y() = lastPosition.y() + 64;
+
+		newPosition = _currentArea->resolveCollisions(lastPosition, newPosition, _playerHeight);
 	}
-}
 
-bool FreescapeEngine::checkCollisions(bool executeCode) {
-	if (_noClipMode)
-		return false;
-	Math::AABB boundingBox(_lastPosition, _lastPosition);
+	/*if ((lastPosition - newPosition).length() >= 1) { // Step up
+		playSound(4, false);
+	}*/
 
-	Math::Vector3d v1(_position.x() + 1, _position.y() + 1, _position.z() + 1);
-	Math::Vector3d v2(_position.x() - 1, _position.y() - _playerHeight, _position.z() - 1);
+	lastPosition = newPosition;
+	newPosition.y() = -8192;
+	newPosition = _currentArea->resolveCollisions(lastPosition, newPosition, _playerHeight);
+	int fallen = lastPosition.y() - newPosition.y();
 
-	boundingBox.expand(v1);
-	boundingBox.expand(v2);
-	ObjectArray objs = _currentArea->checkCollisions(boundingBox);
-	bool collided = !objs.empty();
+	if (fallen > 64)
+		_hasFallen = !_disableFalling;
 
-	// If we don't need to execute code, we can finish here
-	if (!executeCode) {
-		return collided;
+	if (!_hasFallen && fallen > 0) {
+		// Position in Y was changed, let's re-run effects
+		runCollisionConditions(lastPosition, newPosition);
 	}
+	_position = newPosition;
+}
+
+void FreescapeEngine::runCollisionConditions(Math::Vector3d const lastPosition, Math::Vector3d const newPosition) {
+	if (_noClipMode)
+		return;
 
-	// If we need to execute code, we need to make sure the bounding box touches the floor
-	// so we will expand it and re-run the collision checking
-	uint tolerance = 1;
-	Math::Vector3d v3(_position.x() - 1, _position.y() - _playerHeight - tolerance, _position.z() - 1);
-	boundingBox.expand(v3);
+	// We need to make sure the bounding box touches the floor so we will expand it and run the collision checking
+	uint tolerance = 3;
+	Math::Vector3d v(newPosition.x() - 1, newPosition.y() - _playerHeight - tolerance, newPosition.z() - 1);
+	Math::AABB boundingBox(lastPosition, lastPosition);
+	boundingBox.expand(v);
 
-	objs = _currentArea->checkCollisions(boundingBox);
+	ObjectArray objs = _currentArea->checkCollisions(boundingBox);
 
 	// sort so the condition from those objects that are larger are executed last
 	struct {
@@ -393,7 +397,7 @@ bool FreescapeEngine::checkCollisions(bool executeCode) {
 		// The following check stops the player from going through big solid objects such as walls
 		// FIXME: find a better workaround of this
 		if (gobj->getSize().length() > 3000) {
-			if (largeObjectWasBlocking)
+			if (largeObjectWasBlocking && !(isDriller() && _currentArea->getAreaID() == 14))
 				continue;
 			largeObjectWasBlocking = true;
 		}
@@ -403,9 +407,6 @@ bool FreescapeEngine::checkCollisions(bool executeCode) {
 		if (areaID != _currentArea->getAreaID())
 			break;
 	}
-	// We still need to return the original result, not the collision using the expanded bounding box
-	// This will avoid detecting the floor constantly
-	return collided;
 }
 
 } // namespace Freescape


Commit: a68b228f5960e2af1a7e265d718365dd4def267b
    https://github.com/scummvm/scummvm/commit/a68b228f5960e2af1a7e265d718365dd4def267b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: remove spaces from area name when displaying info menu in driller

Changed paths:
    engines/freescape/games/driller/driller.cpp


diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 902354fcce9..42708de1e9f 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -245,7 +245,10 @@ void DrillerEngine::drawInfoMenu() {
 	_gfx->readFromPalette(color, r, g, b);
 	uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
 
-	drawStringInSurface(Common::String::format("%10s : %s", "sector", _currentArea->_name.c_str()), 69, 25, front, black, surface);
+	Common::String areaName = _currentArea->_name;
+	areaName.trim();
+
+	drawStringInSurface(Common::String::format("%10s : %s", "sector", areaName.c_str()), 69, 25, front, black, surface);
 	Common::String rigStatus;
 	Common::String gasFound;
 	Common::String perTapped;


Commit: 12e0332415f8e608305b3e8a1a11791ddd1e5a98
    https://github.com/scummvm/scummvm/commit/12e0332415f8e608305b3e8a1a11791ddd1e5a98
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: make sure end game area is properly renderer in driller

Changed paths:
    engines/freescape/games/driller/driller.cpp


diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 42708de1e9f..2721262e168 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -799,9 +799,15 @@ bool DrillerEngine::checkIfGameEnded() {
 			insertTemporaryMessage(_messagesList[19], _countdown - 2);
 			_gameStateVars[32] = 0;  // Avoid repeating the message
 		}
-		drawFrame();
-		_gfx->flipBuffer();
-		g_system->updateScreen();
+
+		// Draw a few frames
+		for (int i = 0; i < 10; i++) {
+			drawFrame();
+			_gfx->flipBuffer();
+			g_system->updateScreen();
+			g_system->delayMillis(10);
+		}
+
 		g_system->delayMillis(5000);
 		return true;
 	}


Commit: a7a4415acb053c06d46c275e930c7ad5030d6720
    https://github.com/scummvm/scummvm/commit/a7a4415acb053c06d46c275e930c7ad5030d6720
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: palette fixes for driller cpc

Changed paths:
    engines/freescape/games/driller/cpc.cpp
    engines/freescape/games/palettes.cpp


diff --git a/engines/freescape/games/driller/cpc.cpp b/engines/freescape/games/driller/cpc.cpp
index f9d95eef943..e105189cd01 100644
--- a/engines/freescape/games/driller/cpc.cpp
+++ b/engines/freescape/games/driller/cpc.cpp
@@ -138,12 +138,12 @@ void DrillerEngine::drawCPCUI(Graphics::Surface *surface) {
 	_gfx->selectColorFromFourColorPalette(color, r, g, b);
 	uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
 
-	color = 0;
+	color = _currentArea->_usualBackgroundColor;
 	if (_gfx->_colorRemaps && _gfx->_colorRemaps->contains(color)) {
 		color = (*_gfx->_colorRemaps)[color];
 	}
 
-	_gfx->readFromPalette(color, r, g, b);
+	_gfx->selectColorFromFourColorPalette(color, r, g, b);
 	uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
 
 	int score = _gameStateVars[k8bitVariableScore];
diff --git a/engines/freescape/games/palettes.cpp b/engines/freescape/games/palettes.cpp
index e2fe6b8e3c8..529f8f43284 100644
--- a/engines/freescape/games/palettes.cpp
+++ b/engines/freescape/games/palettes.cpp
@@ -74,13 +74,13 @@ byte kDrillerZXPalette[9][3] = {
 };
 
 byte kDrillerCPCPalette[32][3] = {
-	{0x00, 0x00, 0x00}, // 0: special case?
+	{0x80, 0x80, 0x80}, // 0: special case?
 	{0x11, 0x22, 0x33},
 	{0x80, 0xff, 0x80}, // 2
 	{0xff, 0xff, 0x80}, // 3
 	{0x11, 0x22, 0x33},
 	{0xff, 0x00, 0x80}, // 5
-	{0x00, 0xff, 0x80}, // 6
+	{0x00, 0x80, 0x00}, // 6
 	{0xff, 0x80, 0x80}, // 7
 	{0x11, 0x22, 0x33},
 	{0x11, 0x22, 0x33},


Commit: e5747d3916b20390dd7413325e7a93d8126f8d37
    https://github.com/scummvm/scummvm/commit/e5747d3916b20390dd7413325e7a93d8126f8d37
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: color remap fixes for driller cpc/amiga/atari

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/games/driller/cpc.cpp
    engines/freescape/games/driller/driller.cpp
    engines/freescape/games/palettes.cpp
    engines/freescape/gfx.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 841e33f3c00..587c9d6e500 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -278,12 +278,13 @@ void FreescapeEngine::drawFrame() {
 	_gfx->positionCamera(_position, _position + _cameraFront);
 
 	if (_underFireFrames > 0) {
-		int underFireColor = isDriller() && isDOS() ? 1
-							: _currentArea->_underFireBackgroundColor;
-		if (underFireColor < 16) {
-			_currentArea->remapColor(_currentArea->_usualBackgroundColor, underFireColor);
-			_currentArea->remapColor(_currentArea->_skyColor, underFireColor);
-		}
+		int underFireColor = _currentArea->_underFireBackgroundColor;
+
+		if (isDriller() && (isDOS() || isAmiga() || isAtariST()))
+			underFireColor = 1;
+
+		_currentArea->remapColor(_currentArea->_usualBackgroundColor, underFireColor);
+		_currentArea->remapColor(_currentArea->_skyColor, underFireColor);
 	}
 
 	drawBackground();
diff --git a/engines/freescape/games/driller/cpc.cpp b/engines/freescape/games/driller/cpc.cpp
index e105189cd01..79016c4c0e7 100644
--- a/engines/freescape/games/driller/cpc.cpp
+++ b/engines/freescape/games/driller/cpc.cpp
@@ -132,10 +132,10 @@ void DrillerEngine::loadAssetsCPCFullGame() {
 }
 
 void DrillerEngine::drawCPCUI(Graphics::Surface *surface) {
-	uint32 color = 1;
+	uint32 color = _currentArea->_underFireBackgroundColor;
 	uint8 r, g, b;
 
-	_gfx->selectColorFromFourColorPalette(color, r, g, b);
+	_gfx->readFromPalette(color, r, g, b);
 	uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
 
 	color = _currentArea->_usualBackgroundColor;
@@ -143,7 +143,7 @@ void DrillerEngine::drawCPCUI(Graphics::Surface *surface) {
 		color = (*_gfx->_colorRemaps)[color];
 	}
 
-	_gfx->selectColorFromFourColorPalette(color, r, g, b);
+	_gfx->readFromPalette(color, r, g, b);
 	uint32 back = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
 
 	int score = _gameStateVars[k8bitVariableScore];
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 2721262e168..4eb1a81523a 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -180,8 +180,13 @@ void DrillerEngine::gotoArea(uint16 areaID, int entranceID) {
 	_gfx->setColorRemaps(&_currentArea->_colorRemaps);
 
 	swapPalette(areaID);
-	_currentArea->_skyColor = 0;
-	_currentArea->_usualBackgroundColor = 0;
+
+	if (isDOS() || isAmiga() || isAtariST()) {
+		_currentArea->_skyColor = 0;
+		_currentArea->_usualBackgroundColor = 0;
+	} else if (isCPC()) {
+		_currentArea->_skyColor = _currentArea->_usualBackgroundColor;
+	}
 
 	resetInput();
 }
diff --git a/engines/freescape/games/palettes.cpp b/engines/freescape/games/palettes.cpp
index 529f8f43284..0950f90e165 100644
--- a/engines/freescape/games/palettes.cpp
+++ b/engines/freescape/games/palettes.cpp
@@ -94,7 +94,7 @@ byte kDrillerCPCPalette[32][3] = {
 	{0x00, 0xff, 0x80}, // 17
 	{0x00, 0xff, 0x00}, // 18
 	{0x80, 0xff, 0xff}, // 19
-	{0x11, 0x22, 0x33},
+	{0x00, 0x00, 0x00}, // 20
 	{0x00, 0x00, 0xff}, // 21
 	{0x00, 0x80, 0x00}, // 22
 	{0x00, 0x80, 0xff}, // 23
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index f4dde905eda..63a18892d60 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -934,19 +934,12 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 }
 
 void Renderer::drawBackground(uint8 color) {
-
 	if (_colorRemaps && _colorRemaps->contains(color)) {
 		color = (*_colorRemaps)[color];
 	}
-
-	byte *stipple = nullptr;
-	uint8 r1, g1, b1, r2, g2, b2;
-	bool render = getRGBAt(color, r1, g1, b1, r2, g2, b2, stipple);
-	if (!render)
-		r1 = g1 = b1 = 0;
-
-	//assert(stipple == nullptr); // Unclear if this is ever used
-	clear(r1, g1, b1);
+	uint8 r, g, b;
+	readFromPalette(color, r, g, b);
+	clear(r, g, b);
 }
 
 Graphics::RendererType determinateRenderType() {


Commit: 54d4dfc2a46ddfc5e7e6d42e134f82f9f9c5fe88
    https://github.com/scummvm/scummvm/commit/54d4dfc2a46ddfc5e7e6d42e134f82f9f9c5fe88
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: info menu fixes for driller zx/amiga/atari

Changed paths:
    engines/freescape/games/driller/driller.cpp


diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 4eb1a81523a..3d4d2075e07 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -248,12 +248,18 @@ void DrillerEngine::drawInfoMenu() {
 	uint8 r, g, b;
 
 	_gfx->readFromPalette(color, r, g, b);
+	if (isAmiga() || isAtariST()) {
+		r = 0xFF;
+		g = 0xFF;
+		b = 0x55;
+	}
+
 	uint32 front = _gfx->_texturePixelFormat.ARGBToColor(0xFF, r, g, b);
 
 	Common::String areaName = _currentArea->_name;
 	areaName.trim();
 
-	drawStringInSurface(Common::String::format("%10s : %s", "sector", areaName.c_str()), 69, 25, front, black, surface);
+	drawStringInSurface(Common::String::format("%10s : %s", "sector", areaName.c_str()), 59, 25, front, black, surface);
 	Common::String rigStatus;
 	Common::String gasFound;
 	Common::String perTapped;
@@ -284,10 +290,10 @@ void DrillerEngine::drawInfoMenu() {
 			break;
 	}
 
-	drawStringInSurface(Common::String::format("%10s : %s", "rig status", rigStatus.c_str()), 69, 33, front, black, surface);
-	drawStringInSurface(Common::String::format("%10s : %s", "gas found", gasFound.c_str()), 69, 41, front, black, surface);
-	drawStringInSurface(Common::String::format("%10s : %s", "% tapped", perTapped.c_str()), 69, 49, front, black, surface);
-	drawStringInSurface(Common::String::format("%10s : %s", "gas tapped", gasTapped.c_str()), 69, 57, front, black, surface);
+	drawStringInSurface(Common::String::format("%10s : %s", "rig status", rigStatus.c_str()), 59, 33, front, black, surface);
+	drawStringInSurface(Common::String::format("%10s : %s", "gas found", gasFound.c_str()), 59, 41, front, black, surface);
+	drawStringInSurface(Common::String::format("%10s : %s", "% tapped", perTapped.c_str()), 59, 49, front, black, surface);
+	drawStringInSurface(Common::String::format("%10s : %s", "gas tapped", gasTapped.c_str()), 59, 57, front, black, surface);
 
 	drawStringInSurface(Common::String::format("%13s : %d", "total sectors", 18), 84, 73, front, black, surface);
 	drawStringInSurface(Common::String::format("%13s : %d", "safe sectors", _gameStateVars[32]), 84, 81, front, black, surface);
@@ -298,7 +304,8 @@ void DrillerEngine::drawInfoMenu() {
 	} else if (isSpectrum()) {
 		drawStringInSurface("l-load s-save 1-abort", 76, 97, front, black, surface);
 		drawStringInSurface("any other key-continue", 76, 105, front, black, surface);
-	}
+	} else if (isAmiga() || isAtariST())
+		drawStringInSurface("press any key to continue", 66, 97, front, black, surface);
 
 	_uiTexture->update(surface);
 	_gfx->setViewport(_fullscreenViewArea);


Commit: 0f7f13813b527a92219e7f61f67027a0cc2c0471
    https://github.com/scummvm/scummvm/commit/0f7f13813b527a92219e7f61f67027a0cc2c0471
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added missing items for driller cga palettes

Changed paths:
    engines/freescape/games/palettes.cpp


diff --git a/engines/freescape/games/palettes.cpp b/engines/freescape/games/palettes.cpp
index 0950f90e165..f66b90ed5af 100644
--- a/engines/freescape/games/palettes.cpp
+++ b/engines/freescape/games/palettes.cpp
@@ -176,19 +176,22 @@ static const struct CGAPalettteEntry {
 	{10, kDrillerCGAPalettePinkBlue},
 	{11, kDrillerCGAPaletteRedGreen},
 	{12, kDrillerCGAPalettePinkBlue},
-
+	{13, kDrillerCGAPaletteRedGreen},
 	{14, kDrillerCGAPalettePinkBlue},
-
+	{15, kDrillerCGAPaletteRedGreen},
 	{16, kDrillerCGAPalettePinkBlue},
-
+	{17, kDrillerCGAPalettePinkBlue},
+	{18, kDrillerCGAPalettePinkBlue},
 	{19, kDrillerCGAPaletteRedGreen},
 	{20, kDrillerCGAPalettePinkBlue},
 	{21, kDrillerCGAPaletteRedGreen},
 	{22, kDrillerCGAPalettePinkBlue},
 	{23, kDrillerCGAPaletteRedGreen},
-
+	{25, kDrillerCGAPalettePinkBlue},
+	{27, kDrillerCGAPaletteRedGreen},
 	{28, kDrillerCGAPalettePinkBlue},
 
+	{31, kDrillerCGAPaletteRedGreen},
 	{32, kDrillerCGAPalettePinkBlue},
 	{127, kDrillerCGAPaletteRedGreen},
 	{0, 0}   // This marks the end


Commit: 0d01a9efdc7b5ebfe60b6a4de4e0f352d3f57296
    https://github.com/scummvm/scummvm/commit/0d01a9efdc7b5ebfe60b6a4de4e0f352d3f57296
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved handling of entrance usage

Changed paths:
    engines/freescape/language/instruction.cpp
    engines/freescape/movement.cpp


diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index beb817e9841..099a7744841 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -93,7 +93,7 @@ void FreescapeEngine::executeObjectConditions(GeometricObject *obj, bool shot, b
 	assert(obj != nullptr);
 	if (!obj->_conditionSource.empty()) {
 		_firstSound = true;
-		_objExecutingCodeSize = obj->getSize();
+		_objExecutingCodeSize = collided ? obj->getSize() : Math::Vector3d();
 		if (collided)
 			debugC(1, kFreescapeDebugCode, "Executing with collision flag: %s", obj->_conditionSource.c_str());
 		else if (shot)
diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index e5245723426..0737675a376 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -46,10 +46,10 @@ void FreescapeEngine::traverseEntrance(uint16 entranceID) {
 	}
 
 	_pitch = rotation.x();
-	if (ABS(_objExecutingCodeSize.x()) <= ABS(_objExecutingCodeSize.z()))
-		_yaw = rotation.y() - 90;
-	else
+	if (_objExecutingCodeSize.length() == 0 || ABS(_objExecutingCodeSize.x()) > ABS(_objExecutingCodeSize.z()))
 		_yaw = rotation.y() + 90;
+	else
+		_yaw = rotation.y() - 90;
 
 	debugC(1, kFreescapeDebugMove, "entrace position: %f %f %f", _position.x(), _position.y(), _position.z());
 


Commit: e96d2199c3338080119416e78b0d58836e6d263f
    https://github.com/scummvm/scummvm/commit/e96d2199c3338080119416e78b0d58836e6d263f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved handling of rise/fall

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/movement.cpp


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 585256345ec..372d02584f6 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -330,20 +330,12 @@ float sweepAABB(Math::AABB const &a, Math::AABB const &b, Math::Vector3d const &
 	return h;
 }
 
-Math::AABB createPlayerAABB(Math::Vector3d const position, int playerHeight) {
-	Math::AABB boundingBox(position, position);
-
-	Math::Vector3d v1(position.x() + 1, position.y() + 1, position.z() + 1);
-	Math::Vector3d v2(position.x() - 1, position.y() - playerHeight, position.z() - 1);
-
-	boundingBox.expand(v1);
-	boundingBox.expand(v2);
-	return boundingBox;
-}
+extern Math::AABB createPlayerAABB(Math::Vector3d const position, int playerHeight);
 
 Math::Vector3d Area::resolveCollisions(const Math::Vector3d &lastPosition_, const Math::Vector3d &newPosition_, int playerHeight) {
 	Math::Vector3d position = newPosition_;
 	Math::Vector3d lastPosition = lastPosition_;
+	Math::AABB boundingBox = createPlayerAABB(lastPosition, playerHeight);
 
 	float epsilon = 1.5;
 	int i = 0;
@@ -352,7 +344,6 @@ Math::Vector3d Area::resolveCollisions(const Math::Vector3d &lastPosition_, cons
 		Math::Vector3d normal;
 		Math::Vector3d direction = position - lastPosition;
 
-		Math::AABB boundingBox = createPlayerAABB(lastPosition, playerHeight);
 		for (auto &obj : _drawableObjects) {
 			if (!obj->isDestroyed() && !obj->isInvisible()) {
 				GeometricObject *gobj = (GeometricObject *)obj;
diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index 0737675a376..c297bdb8d46 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -23,6 +23,17 @@
 
 namespace Freescape {
 
+Math::AABB createPlayerAABB(Math::Vector3d const position, int playerHeight) {
+	Math::AABB boundingBox(position, position);
+
+	Math::Vector3d v1(position.x() + 1, position.y() + 1, position.z() + 1);
+	Math::Vector3d v2(position.x() - 1, position.y() - playerHeight, position.z() - 1);
+
+	boundingBox.expand(v1);
+	boundingBox.expand(v2);
+	return boundingBox;
+}
+
 void FreescapeEngine::gotoArea(uint16 areaID, int entranceID) {
 	error("Function \"%s\" not implemented", __FUNCTION__);
 }
@@ -149,27 +160,26 @@ void FreescapeEngine::rise() {
 	debugC(1, kFreescapeDebugMove, "playerHeightNumber: %d", _playerHeightNumber);
 	int previousAreaID = _currentArea->getAreaID();
 	if (_flyMode) {
-		_position.setValue(1, _position.y() + _playerSteps[_playerStepIndex]);
+		Math::Vector3d destination = _position;
+		destination.y() = destination.y() + _playerSteps[_playerStepIndex];
+		resolveCollisions(destination);
 	} else {
 		if (_playerHeightNumber == int(_playerHeights.size()) - 1)
 			return;
 
 		_playerHeightNumber++;
 		changePlayerHeight(_playerHeightNumber);
-	}
 
-	bool collided = /*checkCollisions(true) ||*/ _position.y() >= 2016;
-	if (collided) {
-		if (_currentArea->getAreaID() == previousAreaID) {
-			if (_flyMode)
-				_position = _lastPosition;
-			else {
+		Math::AABB boundingBox = createPlayerAABB(_position, _playerHeight);
+		ObjectArray objs = _currentArea->checkCollisions(boundingBox);
+		bool collided = objs.size() > 0;
+		if (collided) {
+			if (_currentArea->getAreaID() == previousAreaID) {
 				_playerHeightNumber--;
 				changePlayerHeight(_playerHeightNumber);
 			}
 		}
 	}
-
 	_lastPosition = _position;
 	debugC(1, kFreescapeDebugMove, "new player position: %f, %f, %f", _position.x(), _position.y(), _position.z());
 	executeMovementConditions();
@@ -177,16 +187,10 @@ void FreescapeEngine::rise() {
 
 void FreescapeEngine::lower() {
 	debugC(1, kFreescapeDebugMove, "playerHeightNumber: %d", _playerHeightNumber);
-	int previousAreaID = _currentArea->getAreaID();
-
 	if (_flyMode) {
-		_position.setValue(1, _position.y() - (_playerSteps[_playerStepIndex] * 0.5));
-		bool collided = false; //checkCollisions(true);
-		if (collided) {
-			if (_currentArea->getAreaID() == previousAreaID) {
-				_position = _lastPosition;
-			}
-		}
+		Math::Vector3d destination = _position;
+		destination.y() = destination.y() - _playerSteps[_playerStepIndex];
+		resolveCollisions(destination);
 	} else {
 		if (_playerHeightNumber == 0)
 			return;


Commit: d0f9025cb95930b6e0c9dcab078deef819daf9a1
    https://github.com/scummvm/scummvm/commit/d0f9025cb95930b6e0c9dcab078deef819daf9a1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: initial parsing and rendering of group objects

Changed paths:
  A engines/freescape/objects/group.cpp
    engines/freescape/area.cpp
    engines/freescape/area.h
    engines/freescape/freescape.h
    engines/freescape/language/instruction.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp
    engines/freescape/module.mk
    engines/freescape/objects/geometricobject.cpp
    engines/freescape/objects/group.h


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 372d02584f6..73748a8e012 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -226,11 +226,20 @@ void Area::draw(Freescape::Renderer *gfx) {
 	assert(_drawableObjects.size() > 0);
 	for (auto &obj : _drawableObjects) {
 		if (!obj->isDestroyed() && !obj->isInvisible()) {
-			obj->draw(gfx);
+			if (obj->getType() != ObjectType::kGroupType)
+				obj->draw(gfx);
+			else
+				drawGroup(gfx, (Group *)obj);
 		}
 	}
 }
 
+void Area::drawGroup(Freescape::Renderer *gfx, Group* group) {
+	for (auto &obj : group->_objects) {
+		obj->draw(gfx);
+	}
+}
+
 Object *Area::shootRay(const Math::Ray &ray) {
 	float size = 16.0 * 8192.0; // TODO: check if this is max size
 	Object *collided = nullptr;
diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index a6fa1e2fdc7..7b149696e2e 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -30,6 +30,8 @@
 
 #include "freescape/language/instruction.h"
 #include "freescape/objects/object.h"
+#include "freescape/objects/group.h"
+
 
 namespace Freescape {
 
@@ -50,6 +52,7 @@ public:
 	void remapColor(int index, int color);
 	void unremapColor(int index);
 	void draw(Renderer *gfx);
+	void drawGroup(Renderer *gfx, Group *group);
 	void show();
 
 	Object *shootRay(const Math::Ray &ray);
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 6e3d666dec9..299cebcfbd9 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -286,6 +286,7 @@ public:
 	// Instructions
 	bool checkConditional(FCLInstruction &instruction, bool shot, bool collided, bool timer, bool activated);
 	bool checkIfGreaterOrEqual(FCLInstruction &instruction);
+	void executeExecute(FCLInstruction &instruction);
 	void executeIncrementVariable(FCLInstruction &instruction);
 	void executeDecrementVariable(FCLInstruction &instruction);
 	void executeSetVariable(FCLInstruction &instruction);
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index 099a7744841..e040fe6492c 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -203,6 +203,9 @@ void FreescapeEngine::executeCode(FCLInstructionVector &code, bool shot, bool co
 		case Token::REDRAW:
 			executeRedraw(instruction);
 			break;
+		case Token::EXECUTE:
+			executeExecute(instruction);
+			break;
 		case Token::DELAY:
 			executeDelay(instruction);
 			break;
@@ -250,6 +253,12 @@ void FreescapeEngine::executeRedraw(FCLInstruction &instruction) {
 	waitForSounds();
 }
 
+void FreescapeEngine::executeExecute(FCLInstruction &instruction) {
+	// TODO
+	uint16 objId = instruction._source;
+	debugC(1, kFreescapeDebugCode, "Executing instructions from object %d", objId);
+}
+
 void FreescapeEngine::executeSound(FCLInstruction &instruction) {
 	if (_firstSound)
 		stopAllSounds();
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index e570d4dbb65..e5c15fc76ca 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -277,11 +277,23 @@ Object *FreescapeEngine::load8bitObject(Common::SeekableReadStream *file) {
 
 	case kGroupType:
 		debugC(1, kFreescapeDebugParser, "Object of type 'group'");
-		file->seek(byteSizeOfObject, SEEK_CUR);
+		Common::Array<uint8> groupDataArray;
+		groupDataArray.push_back(uint8(position.x()));
+		groupDataArray.push_back(uint8(position.y()));
+		groupDataArray.push_back(uint8(position.z()));
+
+		groupDataArray.push_back(uint8(v.x()));
+		groupDataArray.push_back(uint8(v.y()));
+		groupDataArray.push_back(uint8(v.z()));
+
+		byteSizeOfObject++;
+		while(--byteSizeOfObject > 0)
+			groupDataArray.push_back(file->readByte());
+
 		return new Group(
 			objectID,
-			position,
-			v);
+			rawFlagsAndType,
+			groupDataArray);
 		break;
 	}
 	// Unreachable
@@ -486,6 +498,12 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 		debugC(1, kFreescapeDebugParser, "Reading object: %d", object);
 		Object *newObject = load8bitObject(file);
 
+		if (newObject->getType() == ObjectType::kGroupType) {
+			Group *group = (Group *)newObject;
+			for (ObjectMap::iterator it = objectsByID->begin(); it != objectsByID->end(); ++it)
+				group->assemble(it->_value);
+		}
+
 		if (newObject) {
 			newObject->scale(scale);
 			if (newObject->getType() == kEntranceType) {
diff --git a/engines/freescape/module.mk b/engines/freescape/module.mk
index cf02372b576..3db7785ab67 100644
--- a/engines/freescape/module.mk
+++ b/engines/freescape/module.mk
@@ -26,6 +26,7 @@ MODULE_OBJS := \
 	movement.o \
 	neo.o \
 	objects/geometricobject.o \
+	objects/group.o \
 	objects/sensor.o \
 	scr.o \
 	sound.o \
diff --git a/engines/freescape/objects/geometricobject.cpp b/engines/freescape/objects/geometricobject.cpp
index 954f67755fb..422a541e804 100644
--- a/engines/freescape/objects/geometricobject.cpp
+++ b/engines/freescape/objects/geometricobject.cpp
@@ -171,6 +171,16 @@ GeometricObject::GeometricObject(
 }
 
 void GeometricObject::setOrigin(Math::Vector3d origin_) {
+	if (isPolygon(_type)) {
+		Math::Vector3d offset = origin_ - _origin;
+		offset = 32 * offset;
+		for (int i = 0; i < int(_ordinates->size()); i = i + 3) {
+			(*_ordinates)[i    ] += uint16(offset.x());
+			(*_ordinates)[i + 1] += uint16(offset.y());
+			(*_ordinates)[i + 2] += uint16(offset.z());
+		}
+	}
+
 	_origin = origin_;
 	computeBoundingBox();
 }
diff --git a/engines/freescape/objects/group.cpp b/engines/freescape/objects/group.cpp
new file mode 100644
index 00000000000..b080641418a
--- /dev/null
+++ b/engines/freescape/objects/group.cpp
@@ -0,0 +1,90 @@
+/* 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/>.
+ *
+ */
+#include "freescape/freescape.h"
+#include "freescape/objects/group.h"
+#include "freescape/objects/geometricobject.h"
+
+namespace Freescape {
+
+Group::Group(uint16 objectID_, uint16 flags_, const Common::Array<byte> data_) {
+	_objectID = objectID_;
+	_flags = flags_;
+
+	int i;
+	for (i = 0; i < 9; i++) {
+		debugC(1, kFreescapeDebugParser, "group data[%d] = %d", i, data_[i]);
+		if (data_[i] > 0)
+			_objectIds.push_back(data_[i]);
+		/*else
+			break;*/
+	}
+	i = 9;
+	while (i < int(data_.size())) {
+		debugC(1, kFreescapeDebugParser, "group data[%d] = %d", i, data_[i]);
+
+		if (data_[i] >= _objectIds.size())
+			break;
+
+		_objects.push_back(nullptr);
+		//assert(data_[i] < _objectIds.size());
+		_objectIndices.push_back(data_[i]);
+
+		debug("data[%d] = %d", i + 1, data_[i + 1]);
+		debug("data[%d] = %d", i + 2, data_[i + 2]);
+		debug("data[%d] = %d", i + 3, data_[i + 3]);
+		Math::Vector3d position(data_[i + 1], data_[i + 2], data_[i + 3]);
+		_objectPositions.push_back(position);
+
+		i = i + 4;
+	}
+
+	if (isDestroyed()) // If the object is destroyed, restore it
+		restore();
+
+	_flags = _flags & ~0x80;
+	assert(!isInitiallyInvisible());
+	makeVisible();
+}
+
+void Group::assemble(Object *obj) {
+	int objectIndex = -1;
+	for (int i = 0; i < int(_objectIds.size()) ; i++) {
+		if (_objectIds[i] == obj->getObjectID()) {
+			objectIndex = i;
+			break;
+		}
+	}
+
+	if (objectIndex == -1)
+		return;
+
+	for (int i = 0; i < int(_objectIndices.size()) ; i++) {
+		int index = _objectIndices[i];
+		if (index == objectIndex) {
+			Object *duplicate = obj->duplicate();
+			Math::Vector3d position = _objectPositions[i];
+			duplicate->setOrigin(position);
+			_objects[i] = duplicate;
+		}
+	}
+}
+
+} // End of namespace Freescape
\ No newline at end of file
diff --git a/engines/freescape/objects/group.h b/engines/freescape/objects/group.h
index 514ca33542d..270485e276b 100644
--- a/engines/freescape/objects/group.h
+++ b/engines/freescape/objects/group.h
@@ -28,15 +28,16 @@ namespace Freescape {
 
 class Group : public Object {
 public:
-	Group(uint16 objectID_,
-		const Math::Vector3d &origin_,
-		const Math::Vector3d &rotation_) {
-		_objectID = objectID_;
-		_origin = origin_;
-		_rotation = rotation_;
-	}
+	Group(uint16 objectID_, uint16 flags_, const Common::Array<byte> data_);
+	void assemble(Object *obj);
+
+	Common::Array<Object *> _objects;
+	Common::Array<Math::Vector3d> _objectPositions;
+	Common::Array<int16> _objectIndices;
+	Common::Array<int16> _objectIds;
 
 	ObjectType getType() override { return ObjectType::kGroupType; };
+	bool isDrawable() override { return true; }
 	void draw(Freescape::Renderer *gfx) override { error("cannot render Group"); };
 	void scale(int factor) override { warning("cannot scale Group"); };
 	Object *duplicate() override { error("cannot duplicate Group"); };


Commit: cbe4d4fd45f571874a7e3be884369ea76158e1d3
    https://github.com/scummvm/scummvm/commit/cbe4d4fd45f571874a7e3be884369ea76158e1d3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved rendering and scaling of group objects

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/area.h
    engines/freescape/freescape.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp
    engines/freescape/movement.cpp
    engines/freescape/objects/group.cpp
    engines/freescape/objects/group.h
    engines/freescape/objects/object.h


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 73748a8e012..4fe400cbed5 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -222,21 +222,24 @@ void Area::resetArea() {
 }
 
 
-void Area::draw(Freescape::Renderer *gfx) {
+void Area::draw(Freescape::Renderer *gfx, uint32 ticks) {
 	assert(_drawableObjects.size() > 0);
 	for (auto &obj : _drawableObjects) {
 		if (!obj->isDestroyed() && !obj->isInvisible()) {
 			if (obj->getType() != ObjectType::kGroupType)
 				obj->draw(gfx);
 			else
-				drawGroup(gfx, (Group *)obj);
+				drawGroup(gfx, (Group *)obj, ticks);
 		}
 	}
 }
 
-void Area::drawGroup(Freescape::Renderer *gfx, Group* group) {
-	for (auto &obj : group->_objects) {
-		obj->draw(gfx);
+void Area::drawGroup(Freescape::Renderer *gfx, Group* group, uint32 ticks) {
+	uint32 groupSize = group->_objects.size();
+	for (uint32 i = 0; i < groupSize ; i++) {
+		if ((ticks / 10) % (groupSize + 1) == i) {
+			group->_objects[i]->draw(gfx);
+		}
 	}
 }
 
diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index 7b149696e2e..d887065f651 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -51,8 +51,8 @@ public:
 	uint8 getScale();
 	void remapColor(int index, int color);
 	void unremapColor(int index);
-	void draw(Renderer *gfx);
-	void drawGroup(Renderer *gfx, Group *group);
+	void draw(Renderer *gfx, uint32 ticks);
+	void drawGroup(Renderer *gfx, Group *group, uint32 ticks);
 	void show();
 
 	Object *shootRay(const Math::Ray &ray);
diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 587c9d6e500..f985adefcf8 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -288,7 +288,7 @@ void FreescapeEngine::drawFrame() {
 	}
 
 	drawBackground();
-	_currentArea->draw(_gfx);
+	_currentArea->draw(_gfx, _ticks);
 
 	if (_underFireFrames > 0) {
 		for (auto &it : _sensors) {
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index e5c15fc76ca..c99ff34eafd 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -498,14 +498,14 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 		debugC(1, kFreescapeDebugParser, "Reading object: %d", object);
 		Object *newObject = load8bitObject(file);
 
-		if (newObject->getType() == ObjectType::kGroupType) {
-			Group *group = (Group *)newObject;
-			for (ObjectMap::iterator it = objectsByID->begin(); it != objectsByID->end(); ++it)
-				group->assemble(it->_value);
-		}
-
 		if (newObject) {
 			newObject->scale(scale);
+			if (newObject->getType() == ObjectType::kGroupType) {
+				Group *group = (Group *)newObject;
+				for (ObjectMap::iterator it = objectsByID->begin(); it != objectsByID->end(); ++it)
+					group->assemble(it->_value);
+			}
+
 			if (newObject->getType() == kEntranceType) {
 				if (entrancesByID->contains(newObject->getObjectID() & 0x7fff))
 					error("WARNING: replacing object id %d (%d)", newObject->getObjectID(), newObject->getObjectID() & 0x7fff);
diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index c297bdb8d46..40086e378a2 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -326,6 +326,11 @@ void FreescapeEngine::move(CameraMovement direction, uint8 scale, float deltaTim
 }
 
 void FreescapeEngine::resolveCollisions(Math::Vector3d const position) {
+	if (_noClipMode) {
+		_position = position;
+		return;
+	}
+
 	Math::Vector3d newPosition = position;
 	Math::Vector3d lastPosition = _lastPosition;
 
@@ -373,9 +378,6 @@ void FreescapeEngine::resolveCollisions(Math::Vector3d const position) {
 }
 
 void FreescapeEngine::runCollisionConditions(Math::Vector3d const lastPosition, Math::Vector3d const newPosition) {
-	if (_noClipMode)
-		return;
-
 	// We need to make sure the bounding box touches the floor so we will expand it and run the collision checking
 	uint tolerance = 3;
 	Math::Vector3d v(newPosition.x() - 1, newPosition.y() - _playerHeight - tolerance, newPosition.z() - 1);
diff --git a/engines/freescape/objects/group.cpp b/engines/freescape/objects/group.cpp
index b080641418a..434a648b0d9 100644
--- a/engines/freescape/objects/group.cpp
+++ b/engines/freescape/objects/group.cpp
@@ -27,14 +27,13 @@ namespace Freescape {
 Group::Group(uint16 objectID_, uint16 flags_, const Common::Array<byte> data_) {
 	_objectID = objectID_;
 	_flags = flags_;
+	_scale = 0;
 
 	int i;
 	for (i = 0; i < 9; i++) {
 		debugC(1, kFreescapeDebugParser, "group data[%d] = %d", i, data_[i]);
 		if (data_[i] > 0)
 			_objectIds.push_back(data_[i]);
-		/*else
-			break;*/
 	}
 	i = 9;
 	while (i < int(data_.size())) {
@@ -47,9 +46,9 @@ Group::Group(uint16 objectID_, uint16 flags_, const Common::Array<byte> data_) {
 		//assert(data_[i] < _objectIds.size());
 		_objectIndices.push_back(data_[i]);
 
-		debug("data[%d] = %d", i + 1, data_[i + 1]);
-		debug("data[%d] = %d", i + 2, data_[i + 2]);
-		debug("data[%d] = %d", i + 3, data_[i + 3]);
+		debugC(1, kFreescapeDebugParser, "group data[%d] = %d", i + 1, data_[i + 1]);
+		debugC(1, kFreescapeDebugParser, "group data[%d] = %d", i + 2, data_[i + 2]);
+		debugC(1, kFreescapeDebugParser, "group data[%d] = %d", i + 3, data_[i + 3]);
 		Math::Vector3d position(data_[i + 1], data_[i + 2], data_[i + 3]);
 		_objectPositions.push_back(position);
 
@@ -59,8 +58,7 @@ Group::Group(uint16 objectID_, uint16 flags_, const Common::Array<byte> data_) {
 	if (isDestroyed()) // If the object is destroyed, restore it
 		restore();
 
-	_flags = _flags & ~0x80;
-	assert(!isInitiallyInvisible());
+	makeInitiallyVisible();
 	makeVisible();
 }
 
@@ -80,11 +78,18 @@ void Group::assemble(Object *obj) {
 		int index = _objectIndices[i];
 		if (index == objectIndex) {
 			Object *duplicate = obj->duplicate();
+			assert(_scale > 0);
 			Math::Vector3d position = _objectPositions[i];
+
+			if (!GeometricObject::isPolygon(obj->getType()))
+				position = 32 * position / _scale;
+
 			duplicate->setOrigin(position);
+			assert(!duplicate->isDestroyed());
 			_objects[i] = duplicate;
 		}
 	}
+	obj->makeInitiallyInvisible();
 }
 
 } // End of namespace Freescape
\ No newline at end of file
diff --git a/engines/freescape/objects/group.h b/engines/freescape/objects/group.h
index 270485e276b..41ca55d527f 100644
--- a/engines/freescape/objects/group.h
+++ b/engines/freescape/objects/group.h
@@ -35,11 +35,12 @@ public:
 	Common::Array<Math::Vector3d> _objectPositions;
 	Common::Array<int16> _objectIndices;
 	Common::Array<int16> _objectIds;
+	int _scale;
 
 	ObjectType getType() override { return ObjectType::kGroupType; };
 	bool isDrawable() override { return true; }
 	void draw(Freescape::Renderer *gfx) override { error("cannot render Group"); };
-	void scale(int factor) override { warning("cannot scale Group"); };
+	void scale(int scale_) override { _scale = scale_; };
 	Object *duplicate() override { error("cannot duplicate Group"); };
 };
 
diff --git a/engines/freescape/objects/object.h b/engines/freescape/objects/object.h
index a21b844b28a..b03de78a649 100644
--- a/engines/freescape/objects/object.h
+++ b/engines/freescape/objects/object.h
@@ -71,6 +71,8 @@ public:
 	void makeInvisible() { _flags = _flags | 0x40; }
 	void makeVisible() { _flags = _flags & ~0x40; }
 	bool isInitiallyInvisible() { return _flags & 0x80; }
+	void makeInitiallyInvisible() { _flags = _flags | 0x80; }
+	void makeInitiallyVisible() { _flags = _flags & ~0x80; }
 	bool isDestroyed() { return _flags & 0x20; }
 	void destroy() { _flags = _flags | 0x20; }
 	void restore() { _flags = _flags & ~0x20; }


Commit: 2ed383c0d2d764b75bad864f61687d4b350ea5d9
    https://github.com/scummvm/scummvm/commit/2ed383c0d2d764b75bad864f61687d4b350ea5d9
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: adjust aabb tolerance for castle

Changed paths:
    engines/freescape/movement.cpp


diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index 40086e378a2..ed0564cb815 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -379,7 +379,7 @@ void FreescapeEngine::resolveCollisions(Math::Vector3d const position) {
 
 void FreescapeEngine::runCollisionConditions(Math::Vector3d const lastPosition, Math::Vector3d const newPosition) {
 	// We need to make sure the bounding box touches the floor so we will expand it and run the collision checking
-	uint tolerance = 3;
+	uint tolerance = isCastle() ? 1 : 3;
 	Math::Vector3d v(newPosition.x() - 1, newPosition.y() - _playerHeight - tolerance, newPosition.z() - 1);
 	Math::AABB boundingBox(lastPosition, lastPosition);
 	boundingBox.expand(v);


Commit: c0995c3fa289b5ae0daefbbb8061212b54851280
    https://github.com/scummvm/scummvm/commit/c0995c3fa289b5ae0daefbbb8061212b54851280
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved rendering of group animations

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp
    engines/freescape/objects/group.cpp
    engines/freescape/objects/group.h


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 4fe400cbed5..0777589a16b 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -236,10 +236,10 @@ void Area::draw(Freescape::Renderer *gfx, uint32 ticks) {
 
 void Area::drawGroup(Freescape::Renderer *gfx, Group* group, uint32 ticks) {
 	uint32 groupSize = group->_objects.size();
+	uint32 frameSize = group->_objectPositions.size();
 	for (uint32 i = 0; i < groupSize ; i++) {
-		if ((ticks / 10) % (groupSize + 1) == i) {
-			group->_objects[i]->draw(gfx);
-		}
+		group->assemble((ticks / 10) % frameSize, i);
+		group->_objects[i]->draw(gfx);
 	}
 }
 
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index c99ff34eafd..a6289e30f35 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -503,7 +503,7 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 			if (newObject->getType() == ObjectType::kGroupType) {
 				Group *group = (Group *)newObject;
 				for (ObjectMap::iterator it = objectsByID->begin(); it != objectsByID->end(); ++it)
-					group->assemble(it->_value);
+					group->linkObject(it->_value);
 			}
 
 			if (newObject->getType() == kEntranceType) {
diff --git a/engines/freescape/objects/group.cpp b/engines/freescape/objects/group.cpp
index 434a648b0d9..a097259e301 100644
--- a/engines/freescape/objects/group.cpp
+++ b/engines/freescape/objects/group.cpp
@@ -36,14 +36,8 @@ Group::Group(uint16 objectID_, uint16 flags_, const Common::Array<byte> data_) {
 			_objectIds.push_back(data_[i]);
 	}
 	i = 9;
-	while (i < int(data_.size())) {
-		debugC(1, kFreescapeDebugParser, "group data[%d] = %d", i, data_[i]);
-
-		if (data_[i] >= _objectIds.size())
-			break;
-
-		_objects.push_back(nullptr);
-		//assert(data_[i] < _objectIds.size());
+	while (i < int(data_.size() - 4)) {
+		debugC(1, kFreescapeDebugParser, "group data[%d] = %d (index)	", i, data_[i]);
 		_objectIndices.push_back(data_[i]);
 
 		debugC(1, kFreescapeDebugParser, "group data[%d] = %d", i + 1, data_[i + 1]);
@@ -62,7 +56,7 @@ Group::Group(uint16 objectID_, uint16 flags_, const Common::Array<byte> data_) {
 	makeVisible();
 }
 
-void Group::assemble(Object *obj) {
+void Group::linkObject(Object *obj) {
 	int objectIndex = -1;
 	for (int i = 0; i < int(_objectIds.size()) ; i++) {
 		if (_objectIds[i] == obj->getObjectID()) {
@@ -74,22 +68,20 @@ void Group::assemble(Object *obj) {
 	if (objectIndex == -1)
 		return;
 
-	for (int i = 0; i < int(_objectIndices.size()) ; i++) {
-		int index = _objectIndices[i];
-		if (index == objectIndex) {
-			Object *duplicate = obj->duplicate();
-			assert(_scale > 0);
-			Math::Vector3d position = _objectPositions[i];
+	obj->makeInitiallyVisible();
+	obj->makeVisible();
+	_objects.push_back(obj);
+}
 
-			if (!GeometricObject::isPolygon(obj->getType()))
-				position = 32 * position / _scale;
+void Group::assemble(int frame, int index) {
+	Object *obj = _objects[index];
+	Math::Vector3d position = _objectPositions[frame];
 
-			duplicate->setOrigin(position);
-			assert(!duplicate->isDestroyed());
-			_objects[i] = duplicate;
-		}
-	}
-	obj->makeInitiallyInvisible();
-}
+	if (!GeometricObject::isPolygon(obj->getType()))
+		position = 32 * position / _scale;
+	else
+		position = position / _scale;
 
+	obj->setOrigin(position);
+}
 } // End of namespace Freescape
\ No newline at end of file
diff --git a/engines/freescape/objects/group.h b/engines/freescape/objects/group.h
index 41ca55d527f..09aad72adbd 100644
--- a/engines/freescape/objects/group.h
+++ b/engines/freescape/objects/group.h
@@ -29,7 +29,8 @@ namespace Freescape {
 class Group : public Object {
 public:
 	Group(uint16 objectID_, uint16 flags_, const Common::Array<byte> data_);
-	void assemble(Object *obj);
+	void linkObject(Object *obj);
+	void assemble(int frame, int index);
 
 	Common::Array<Object *> _objects;
 	Common::Array<Math::Vector3d> _objectPositions;


Commit: 2bdef2ef4249e523ced5ec1b51b188939f1a79db
    https://github.com/scummvm/scummvm/commit/2bdef2ef4249e523ced5ec1b51b188939f1a79db
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added start animation opcode

Changed paths:
    engines/freescape/language/8bitDetokeniser.cpp


diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index e74d4182fef..2fc96f03c9c 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -50,7 +50,7 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		 2, 1, 1, 2, 1, 1, 2, 1,
 		 1, 2, 2, 1, 2, 0, 0, 0,
 		 1, 1, 0, 1, 1, 1, 1, 1,
-		 2, 2, 1, 1, 0, 0, 0, 0,
+		 2, 2, 1, 1, 0, 1, 0, 0,
 		 0, 0, 0, 0, 0, 0, 2, 2,
 		 1};
 
@@ -115,7 +115,7 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 		// check we have enough bytes left to read
 		if (opcode > 48) {
 			debugC(1, kFreescapeDebugParser, "%s", detokenisedStream.c_str());
-			if (opcode != 0x3f && opcode != 0x3b)
+			if (opcode != 0x3f)
 				error("ERROR: failed to read opcode: %x", opcode);
 			break;
 		}
@@ -304,6 +304,11 @@ Common::String detokenise8bitCondition(Common::Array<uint8> &tokenisedCondition,
 			currentInstruction = FCLInstruction(Token::PRINT);
 			break;
 
+		case 37:
+			detokenisedStream += "STARTANIM (";
+			currentInstruction = FCLInstruction(Token::STARTANIM);
+			break;
+
 		case 12:
 			detokenisedStream += "SETBIT (";
 			currentInstruction = FCLInstruction(Token::SETBIT);


Commit: 1145730b6e44983e3980205dc87ef3b8bb0aa908
    https://github.com/scummvm/scummvm/commit/1145730b6e44983e3980205dc87ef3b8bb0aa908
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved parsing of castle demo for amiga

Changed paths:
    engines/freescape/games/castle.cpp
    engines/freescape/games/palettes.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp
    engines/freescape/sound.cpp


diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 4165ee42b2c..07c885f1abd 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -35,6 +35,7 @@ CastleEngine::CastleEngine(OSystem *syst, const ADGameDescription *gd) : Freesca
 
 	_playerWidth = 8;
 	_playerDepth = 8;
+	_option = nullptr;
 }
 
 CastleEngine::~CastleEngine() {
@@ -290,9 +291,7 @@ void CastleEngine::loadAssetsAmigaDemo() {
 
 	loadMessagesVariableSize(&file, 0x8bb2, 164);
 	load8bitBinary(&file, 0x162a6, 16);
-	assert(0);
-	//loadPalettes(&file, 0x0);
-
+	loadPalettes(&file, 0x151a6);
 	//file.close();
 	//file.open("driller");
 	//if (!file.isOpen())
diff --git a/engines/freescape/games/palettes.cpp b/engines/freescape/games/palettes.cpp
index f66b90ed5af..ee93a325dd1 100644
--- a/engines/freescape/games/palettes.cpp
+++ b/engines/freescape/games/palettes.cpp
@@ -136,7 +136,7 @@ void FreescapeEngine::loadPalettes(Common::SeekableReadStream *file, int offset)
 	for (uint i = 0; i < _areaMap.size() + 2; i++) {
 		int label = readField(file, 8);
 		auto palette = new byte[16][3];
-		debugC(1, kFreescapeDebugParser, "Loading palette for area: %d", label);
+		debugC(1, kFreescapeDebugParser, "Loading palette for area: %d at %lx", label, file->pos());
 		for (int c = 0; c < 16; c++) {
 			int v = file->readUint16BE();
 			r = (v & 0xf00) >> 8;
@@ -212,9 +212,6 @@ byte kDrillerCGAPaletteRedGreenData[4][3] = {
 };
 
 void FreescapeEngine::swapPalette(uint16 levelID) {
-	if (!_border)
-		return;
-
 	if (isAmiga() || isAtariST()) {
 		// The following palette was not available in the demo, so we select another one
 		if (isDemo() && levelID == 32)
@@ -226,6 +223,9 @@ void FreescapeEngine::swapPalette(uint16 levelID) {
 		_gfx->_paperColor = _areaMap[levelID]->_paperColor;
 		_gfx->_underFireBackgroundColor = _areaMap[levelID]->_underFireBackgroundColor;
 
+		if (!_border)
+			return;
+
 		byte *palette = (byte *)malloc(sizeof(byte) * 4 * 3);
 		for (int c = 0; c < 4; c++) {
 			byte r, g, b;
@@ -253,9 +253,14 @@ void FreescapeEngine::swapPalette(uint16 levelID) {
 		}
 
 		assert(entry->areaId == levelID);
+		if (!_border)
+			return;
 		_border->setPalette(_gfx->_palette, 0, 4);
 		processBorder();
 	} else if (isDOS() && _renderMode == Common::kRenderEGA) {
+		if (!_border)
+			return;
+
 		_border->setPalette(_gfx->_palette, 0, 4);
 		processBorder();
 	}
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index a6289e30f35..3e8574cad41 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -99,6 +99,13 @@ Object *FreescapeEngine::load8bitObject(Common::SeekableReadStream *file) {
 		error("Not enough bytes %d to read object %d with type %d", byteSizeOfObject, objectID, objectType);
 	}
 
+	if (objectType > ObjectType::kGroupType && isDemo()) {
+		// Castle DOS demo has an invalid object, which should not be parsed.
+		debugC(1, kFreescapeDebugParser, "WARNING: invalid object %d!", objectID);
+		readArray(file, byteSizeOfObject - 9);
+		return nullptr;
+	}
+
 	assert(byteSizeOfObject >= 9);
 	byteSizeOfObject = byteSizeOfObject - 9;
 	if (objectID == 255 && objectType == ObjectType::kEntranceType) {
@@ -114,7 +121,7 @@ Object *FreescapeEngine::load8bitObject(Common::SeekableReadStream *file) {
 
 		byteSizeOfObject++;
 		while(--byteSizeOfObject > 0)
-			structureArray.push_back(file->readByte());
+			structureArray.push_back(readField(file, 8));
 		return new GlobalStructure(structureArray);
 	} else if (objectID == 254 && objectType == ObjectType::kEntranceType) {
 		debugC(1, kFreescapeDebugParser, "Found the area connections (objectID: 254 with size %d)", byteSizeOfObject + 6);
@@ -288,7 +295,7 @@ Object *FreescapeEngine::load8bitObject(Common::SeekableReadStream *file) {
 
 		byteSizeOfObject++;
 		while(--byteSizeOfObject > 0)
-			groupDataArray.push_back(file->readByte());
+			groupDataArray.push_back(readField(file, 8));
 
 		return new Group(
 			objectID,
@@ -516,7 +523,7 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 					error("WARNING: replacing object id %d", newObject->getObjectID());
 				(*objectsByID)[newObject->getObjectID()] = newObject;
 			}
-		} else
+		} else if (!(isDemo() && isCastle()))
 			error("Failed to read an object!");
 	}
 	long int endLastObject = file->pos();
@@ -570,8 +577,13 @@ void FreescapeEngine::load8bitBinary(Common::SeekableReadStream *file, int offse
 	uint8 numberOfAreas = readField(file, 8);
 	debugC(1, kFreescapeDebugParser, "Number of areas: %d", numberOfAreas);
 
-	if (isDOS() && isCastle()) // Castle Master for DOS has an invalid number of areas
-		numberOfAreas = isDemo() ? 31 : 104;
+	// Castle Master seems to have invalid number of areas?
+	if (isCastle()) {
+		if (isDOS())
+			numberOfAreas = isDemo() ? 31 : 104;
+		else if (isAmiga())
+			numberOfAreas = isDemo() ? 86 : 104;
+	}
 
 	uint32 dbSize = readField(file, 16);
 	debugC(1, kFreescapeDebugParser, "Database ends at %x", dbSize);
diff --git a/engines/freescape/sound.cpp b/engines/freescape/sound.cpp
index f7be53a0b29..053d9668f72 100644
--- a/engines/freescape/sound.cpp
+++ b/engines/freescape/sound.cpp
@@ -239,6 +239,11 @@ void FreescapeEngine::playMusic(const Common::String filename) {
 }
 
 void FreescapeEngine::playSoundFx(int index, bool sync) {
+	if (_soundsFx.size() == 0) {
+		debugC(1, kFreescapeDebugMedia, "WARNING: Sounds are not loaded");
+		return;
+	}
+
 	int size = _soundsFx[index]->size;
 	int sampleRate = _soundsFx[index]->sampleRate;
 	byte *data = _soundsFx[index]->data;


Commit: c9bd9c62deebb0d62018ec3e24c0f53eb6e14858
    https://github.com/scummvm/scummvm/commit/c9bd9c62deebb0d62018ec3e24c0f53eb6e14858
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: improved parsing of castle demo for amiga

Changed paths:
    engines/freescape/games/castle.cpp
    engines/freescape/loaders/8bitBinaryLoader.cpp


diff --git a/engines/freescape/games/castle.cpp b/engines/freescape/games/castle.cpp
index 07c885f1abd..d934042c997 100644
--- a/engines/freescape/games/castle.cpp
+++ b/engines/freescape/games/castle.cpp
@@ -292,21 +292,17 @@ void CastleEngine::loadAssetsAmigaDemo() {
 	loadMessagesVariableSize(&file, 0x8bb2, 164);
 	load8bitBinary(&file, 0x162a6, 16);
 	loadPalettes(&file, 0x151a6);
-	//file.close();
-	//file.open("driller");
-	//if (!file.isOpen())
-	//	error("Failed to open 'driller' file");
-
-	//loadFonts(&file, 0xa30);
-	//loadMessagesFixedSize(&file, 0x3960, 14, 20);
-	//loadGlobalObjects(&file, 0x3716, 8);
 
+	file.seek(0x2be96); // Area 255
+	_areaMap[255] = load8bitArea(&file, 16);
 	file.close();
-	//file.open("soundfx");
-	//if (!file.isOpen())
-	//	error("Failed to open 'soundfx' executable for Amiga");
 
-	//loadSoundsFx(&file, 0, 25);
+
+	_areaMap[2]->_groundColor = 1;
+	for (auto &it : _areaMap)
+		it._value->addStructure(_areaMap[255]);
+
+	_areaMap[2]->addFloor();
 }
 
 
diff --git a/engines/freescape/loaders/8bitBinaryLoader.cpp b/engines/freescape/loaders/8bitBinaryLoader.cpp
index 3e8574cad41..b6db29dbcec 100644
--- a/engines/freescape/loaders/8bitBinaryLoader.cpp
+++ b/engines/freescape/loaders/8bitBinaryLoader.cpp
@@ -491,7 +491,10 @@ Area *FreescapeEngine::load8bitArea(Common::SeekableReadStream *file, uint16 nco
 		}
 	} else if (isCastle()) {
 		byte idx = readField(file, 8);
-		name = _messagesList[idx + 41];
+		if (isAmiga())
+			name = _messagesList[idx + 51];
+		else
+			name = _messagesList[idx + 41];
 		extraColor[0] = readField(file, 8);
 		extraColor[1] = readField(file, 8);
 		extraColor[2] = readField(file, 8);
@@ -582,7 +585,7 @@ void FreescapeEngine::load8bitBinary(Common::SeekableReadStream *file, int offse
 		if (isDOS())
 			numberOfAreas = isDemo() ? 31 : 104;
 		else if (isAmiga())
-			numberOfAreas = isDemo() ? 86 : 104;
+			numberOfAreas = isDemo() ? 87 : 104;
 	}
 
 	uint32 dbSize = readField(file, 16);


Commit: ababd0da8e34330d2c6ecd6d923d2a56eb684d1d
    https://github.com/scummvm/scummvm/commit/ababd0da8e34330d2c6ecd6d923d2a56eb684d1d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added code to decrypt dark for amiga/atari

Changed paths:
  A engines/freescape/games/dark/amiga.cpp
    engines/freescape/detection.cpp
    engines/freescape/freescape.h
    engines/freescape/module.mk


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 01b3f1d2412..118996af34e 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -252,6 +252,19 @@ static const ADGameDescription gameDescriptions[] = {
 		ADGF_UNSUPPORTED | ADGF_DEMO,
 		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
+	{
+		"darkside",
+		"",
+		{
+			{"0.DRK", 0, "9e51b8f93a9af886fb88ab92ed43cf01", 81544},
+			{"1.DRK", 0, "50bbaa2b19fc4072ad85efe93116e561", 280704},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformAmiga,
+		ADGF_UNSTABLE,
+		GUIO1(GUIO_NOMIDI)
+	},
 	{
 		"darkside",
 		"",
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 299cebcfbd9..d6816326828 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -531,6 +531,7 @@ public:
 
 	void loadAssetsDOSFullGame() override;
 	void loadAssetsDOSDemo() override;
+	void loadAssetsAmigaFullGame() override;
 
 	void loadAssetsZXDemo() override;
 
@@ -548,6 +549,7 @@ private:
 	void addECDs(Area *area);
 	void addECD(Area *area, const Math::Vector3d position, int index);
 	void addWalls(Area *area);
+	Common::SeekableReadStream *decryptFile(const Common::String filename);
 };
 
 class EclipseEngine : public FreescapeEngine {
diff --git a/engines/freescape/games/dark/amiga.cpp b/engines/freescape/games/dark/amiga.cpp
new file mode 100644
index 00000000000..fcc2b4c8f71
--- /dev/null
+++ b/engines/freescape/games/dark/amiga.cpp
@@ -0,0 +1,78 @@
+/* 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/>.
+ *
+ */
+#include "common/file.h"
+#include "common/memstream.h"
+
+#include "freescape/freescape.h"
+#include "freescape/language/8bitDetokeniser.h"
+
+namespace Freescape {
+
+Common::SeekableReadStream *DarkEngine::decryptFile(const Common::String filename) {
+	Common::File file;
+	file.open(filename);
+	if (!file.isOpen())
+		error("Failed to open %s", filename.c_str());
+
+	int size = file.size();
+	byte *encryptedBuffer = (byte *)malloc(size);
+	file.read(encryptedBuffer, size);
+	file.close();
+
+    uint32 d7 = 0;
+    uint32 d6 = 0;
+	byte *a6 = encryptedBuffer;
+    byte *a5 = encryptedBuffer + size - 1;
+
+	while (a6 <= a5) {
+		uint64 d0 = (a6[0] << 24) | (a6[1] << 16) | (a6[2] << 8) | a6[3];
+		d0 = d0 + d6;
+		d0 = uint32(d0);
+		d0 = ((d0 << 3) & 0xFFFFFFFF) | ((d0 >> 29) & 0xFFFFFFFF);
+		d0 ^= 0x71049763;
+		d0 -= d7;
+		d0 = ((d0 << 16) & 0xFFFF0000) | ((d0 >> 16) & 0xFFFF);
+
+        a6[0] = byte((d0 >> 24) & 0xFF);
+		//debug("%c", a6[0]);
+        a6[1] = byte((d0 >> 16) & 0xFF);
+		//debug("%c", a6[1]);
+        a6[2] = byte((d0 >> 8) & 0xFF);
+		//debug("%c", a6[2]);
+        a6[3] = byte(d0 & 0xFF);
+		//debug("%c", a6[3]);
+
+		d6 += 5;
+		d6 = ((d6 >> 3) & 0xFFFFFFFF) | ((d6 << 29) & 0xFFFFFFFF);
+		d6 ^= 0x04000000;
+		d7 += 4;
+		a6 += 4;
+	}
+
+	return (new Common::MemoryReadStream(encryptedBuffer, size));
+}
+
+void DarkEngine::loadAssetsAmigaFullGame() {
+	Common::SeekableReadStream *stream = decryptFile("1.drk");
+	load8bitBinary(stream, 0x2e96a, 16);
+}
+
+} // End of namespace Freescape
\ No newline at end of file
diff --git a/engines/freescape/module.mk b/engines/freescape/module.mk
index 3db7785ab67..2cd78466e42 100644
--- a/engines/freescape/module.mk
+++ b/engines/freescape/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
 	demo.o \
 	freescape.o \
 	games/castle.o \
+	games/dark/amiga.o \
 	games/dark/dark.o \
 	games/dark/dos.o \
 	games/dark/zx.o \


Commit: 8e675393dd639e25fc87f577778c4bee25e65fd4
    https://github.com/scummvm/scummvm/commit/8e675393dd639e25fc87f577778c4bee25e65fd4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added code to decrypt some driller releases for atari

Changed paths:
    engines/freescape/detection.cpp
    engines/freescape/freescape.h
    engines/freescape/games/driller/atari.cpp


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 118996af34e..6577c2a919f 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -157,7 +157,20 @@ static const ADGameDescription gameDescriptions[] = {
 		ADGF_NO_FLAGS,
 		GUIO4(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
-	{
+	{ // Retail release
+		"driller",
+		"",
+		{
+			{"x.prg", 0, "d002983a8b652f25b18a09425db78c4c", 293159},
+			{"playseq.prg", 0, "535e9f6baf132831aa7fa066a06f242e", 973},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformAtariST,
+		GF_ATARI_RETAIL,
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
+	},
+	{ // Budget release
 		"driller",
 		"",
 		{
@@ -167,7 +180,7 @@ static const ADGameDescription gameDescriptions[] = {
 		},
 		Common::EN_ANY,
 		Common::kPlatformAtariST,
-		ADGF_NO_FLAGS,
+		GF_ATARI_BUDGET,
 		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
 	{
@@ -340,7 +353,7 @@ static const ADGameDescription gameDescriptions[] = {
 		"spacestationoblivion",
 		"",
 		{
-			{"x.prg", 0, "10c556ee637bf03bcc1a051277542102", 293264},
+			{"x.prg", 0, "bf546ee243c38f51d9beb25c203ccb93", 292624},
 			AD_LISTEND
 		},
 		Common::EN_ANY,
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index d6816326828..e011af256f7 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -427,6 +427,8 @@ enum DrillerReleaseFlags {
 		GF_CPC_RETAIL2 = (1 << 6),
 		GF_CPC_BUDGET = (1 << 7),
 		GF_CPC_VIRTUALWORLDS = (1 << 8),
+		GF_ATARI_RETAIL = (1 << 9),
+		GF_ATARI_BUDGET = (1 << 10),
 };
 
 class DrillerEngine : public FreescapeEngine {
@@ -509,6 +511,8 @@ private:
 
 	uint32 getPixel8bitTitleImage(int index);
 	void renderPixels8bitTitleImage(Graphics::ManagedSurface *surface, int &i, int &j, int pixels);
+
+	Common::SeekableReadStream *decryptFileAtari(const Common::String filename);
 };
 
 class DarkEngine : public FreescapeEngine {
diff --git a/engines/freescape/games/driller/atari.cpp b/engines/freescape/games/driller/atari.cpp
index 143920cb119..df588dd62b6 100644
--- a/engines/freescape/games/driller/atari.cpp
+++ b/engines/freescape/games/driller/atari.cpp
@@ -19,34 +19,83 @@
  *
  */
 #include "common/file.h"
+#include "common/memstream.h"
 
 #include "freescape/freescape.h"
 #include "freescape/language/8bitDetokeniser.h"
 
 namespace Freescape {
 
-void DrillerEngine::loadAssetsAtariFullGame() {
+Common::SeekableReadStream *DrillerEngine::decryptFileAtari(const Common::String filename) {
 	Common::File file;
-	file.open("x.prg");
-
+	file.open(filename);
 	if (!file.isOpen())
-		error("Failed to open 'x.prg' executable for AtariST");
-
-	_border = loadAndConvertNeoImage(&file, 0x1371a);
-	byte *palette = (byte *)malloc(16 * 3);
-	for (int i = 0; i < 16; i++) { // gray scale palette
-		palette[i * 3 + 0] = i * (255 / 16);
-		palette[i * 3 + 1] = i * (255 / 16);
-		palette[i * 3 + 2] = i * (255 / 16);
+		error("Failed to open %s", filename.c_str());
+
+	int size = file.size();
+	byte *encryptedBuffer = (byte *)malloc(size);
+	file.read(encryptedBuffer, size);
+	file.close();
+
+	byte *a6 = encryptedBuffer + 0x118;
+	byte *a5 = encryptedBuffer + size - 4;
+	uint64 key = 0xb9f11bce;
+
+	while (a6 <= a5) {
+		uint64 d0 = (a6[0] << 24) | (a6[1] << 16) | (a6[2] << 8) | a6[3];
+		d0 += key;
+		d0 = uint32(d0);
+
+		a6[0] = byte((d0 >> 24) & 0xFF);
+		a6[1] = byte((d0 >> 16) & 0xFF);
+		a6[2] = byte((d0 >> 8) & 0xFF);
+		a6[3] = byte(d0 & 0xFF);
+
+		key += 0x51684624;
+		key = uint32(key);
+		a6 += 4;
+	}
+
+	return (new Common::MemoryReadStream(encryptedBuffer, size));
+}
+
+void DrillerEngine::loadAssetsAtariFullGame() {
+
+	if (_variant & GF_ATARI_RETAIL) {
+		Common::SeekableReadStream *stream = decryptFileAtari("x.prg");
+
+		_border = loadAndConvertNeoImage(stream, 0x14b96);
+		_title = loadAndConvertNeoImage(stream, 0x1c916);
+
+		loadFonts(stream, 0x8a92);
+		loadMessagesFixedSize(stream, 0xda22, 14, 20);
+		loadGlobalObjects(stream, 0xd116, 8);
+		load8bitBinary(stream, 0x2afb8, 16);
+		loadPalettes(stream, 0x2ab76);
+		//loadSoundsFx(&file, 0x30da6, 25);
+	} else if (_variant & GF_ATARI_BUDGET) {
+		Common::File file;
+		file.open("x.prg");
+
+		if (!file.isOpen())
+			error("Failed to open 'x.prg' executable for AtariST");
+
+		_border = loadAndConvertNeoImage(&file, 0x1371a);
+		byte *palette = (byte *)malloc(16 * 3);
+		for (int i = 0; i < 16; i++) { // gray scale palette
+			palette[i * 3 + 0] = i * (255 / 16);
+			palette[i * 3 + 1] = i * (255 / 16);
+			palette[i * 3 + 2] = i * (255 / 16);
+		}
+		_title = loadAndConvertNeoImage(&file, 0x10, palette);
+
+		loadFonts(&file, 0x8a32);
+		loadMessagesFixedSize(&file, 0xc5d8, 14, 20);
+		loadGlobalObjects(&file, 0xbccc, 8);
+		load8bitBinary(&file, 0x29b3c, 16);
+		loadPalettes(&file, 0x296fa);
+		loadSoundsFx(&file, 0x30da6, 25);
 	}
-	_title = loadAndConvertNeoImage(&file, 0x10, palette);
-
-	loadFonts(&file, 0x8a32);
-	loadMessagesFixedSize(&file, 0xc5d8, 14, 20);
-	loadGlobalObjects(&file, 0xbccc, 8);
-	load8bitBinary(&file, 0x29b3c, 16);
-	loadPalettes(&file, 0x296fa);
-	loadSoundsFx(&file, 0x30da6, 25);
 }
 
 void DrillerEngine::loadAssetsAtariDemo() {


Commit: 82fd2ee5d44abdc5963b8c6deb9bd90217dc9f87
    https://github.com/scummvm/scummvm/commit/82fd2ee5d44abdc5963b8c6deb9bd90217dc9f87
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added better detection of driller releases for atari

Changed paths:
    engines/freescape/detection.cpp


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 6577c2a919f..6c70c1e1895 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -162,7 +162,6 @@ static const ADGameDescription gameDescriptions[] = {
 		"",
 		{
 			{"x.prg", 0, "d002983a8b652f25b18a09425db78c4c", 293159},
-			{"playseq.prg", 0, "535e9f6baf132831aa7fa066a06f242e", 973},
 			AD_LISTEND
 		},
 		Common::EN_ANY,
@@ -175,7 +174,6 @@ static const ADGameDescription gameDescriptions[] = {
 		"",
 		{
 			{"x.prg", 0, "1a79e68e6c2c223c96de0ca2d65149ae", 293062},
-			{"playseq.prg", 0, "535e9f6baf132831aa7fa066a06f242e", 973},
 			AD_LISTEND
 		},
 		Common::EN_ANY,
@@ -183,6 +181,19 @@ static const ADGameDescription gameDescriptions[] = {
 		GF_ATARI_BUDGET,
 		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
+	{ // Virtual Worlds release
+		"driller",
+		"This relese requieres unpacking, check the wiki for instructions:\nhttps://wiki.scummvm.org/index.php?title=Driller#AtariST_releases",
+		{
+			{"d.pak", 0, "607b44b9d31e0da5668b653e03d25efe", 706},
+			{"dril.all", 0, "65277222effa1eb4d73b234245001d75", 158158},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformAtariST,
+		ADGF_UNSUPPORTED,
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
+	},
 	{
 		"driller",
 		"",
@@ -361,6 +372,18 @@ static const ADGameDescription gameDescriptions[] = {
 		ADGF_UNSUPPORTED,
 		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
+	{
+		"spacestationoblivion",
+		"",
+		{
+			{"x.prg", 0, "10c556ee637bf03bcc1a051277542102", 293264},
+			AD_LISTEND
+		},
+		Common::EN_ANY,
+		Common::kPlatformAtariST,
+		ADGF_UNSUPPORTED,
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
+	},
 	{
 		"castlemaster",
 		"Demo",


Commit: 40b73a35a785796f53490d5f4e7670a7bb874be3
    https://github.com/scummvm/scummvm/commit/40b73a35a785796f53490d5f4e7670a7bb874be3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: better detection of driller releases for atari

Changed paths:
    engines/freescape/detection.cpp
    engines/freescape/freescape.h
    engines/freescape/games/driller/atari.cpp


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 6c70c1e1895..49e3c5f1c29 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -376,12 +376,12 @@ static const ADGameDescription gameDescriptions[] = {
 		"spacestationoblivion",
 		"",
 		{
-			{"x.prg", 0, "10c556ee637bf03bcc1a051277542102", 293264},
+			{"x.prg", 0, "7fc6ef316be4819d88d8031738571a50", 293264},
 			AD_LISTEND
 		},
 		Common::EN_ANY,
 		Common::kPlatformAtariST,
-		ADGF_UNSUPPORTED,
+		GF_ATARI_BUDGET,
 		GUIO2(GUIO_NOMIDI, GAMEOPTION_AUTOMATIC_DRILLING)
 	},
 	{
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index e011af256f7..90bbdf1f1b3 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -96,6 +96,7 @@ public:
 	// Game selection
 	uint32 _variant;
 	Common::Language _language;
+	bool isSpaceStationOblivion() { return _targetName.hasPrefix("spacestationoblivion"); }
 	bool isDriller() { return _targetName.hasPrefix("driller") || _targetName.hasPrefix("spacestationoblivion"); }
 	bool isDark() { return _targetName.hasPrefix("darkside"); }
 	bool isEclipse() { return _targetName.hasPrefix("totaleclipse"); }
diff --git a/engines/freescape/games/driller/atari.cpp b/engines/freescape/games/driller/atari.cpp
index df588dd62b6..5cf36467991 100644
--- a/engines/freescape/games/driller/atari.cpp
+++ b/engines/freescape/games/driller/atari.cpp
@@ -80,21 +80,39 @@ void DrillerEngine::loadAssetsAtariFullGame() {
 		if (!file.isOpen())
 			error("Failed to open 'x.prg' executable for AtariST");
 
-		_border = loadAndConvertNeoImage(&file, 0x1371a);
-		byte *palette = (byte *)malloc(16 * 3);
-		for (int i = 0; i < 16; i++) { // gray scale palette
-			palette[i * 3 + 0] = i * (255 / 16);
-			palette[i * 3 + 1] = i * (255 / 16);
-			palette[i * 3 + 2] = i * (255 / 16);
+		if (isSpaceStationOblivion()) {
+			_border = loadAndConvertNeoImage(&file, 0x13544);
+			byte *palette = (byte *)malloc(16 * 3);
+			for (int i = 0; i < 16; i++) { // gray scale palette
+				palette[i * 3 + 0] = i * (255 / 16);
+				palette[i * 3 + 1] = i * (255 / 16);
+				palette[i * 3 + 2] = i * (255 / 16);
+			}
+			_title = loadAndConvertNeoImage(&file, 0x10, palette);
+
+			loadFonts(&file, 0x8a32 - 0x1d6);
+			loadMessagesFixedSize(&file, 0xc5d8 - 0x1da, 14, 20);
+			loadGlobalObjects(&file, 0xbccc - 0x1da, 8);
+			load8bitBinary(&file, 0x29b3c - 0x1d6, 16);
+			loadPalettes(&file, 0x296fa - 0x1d6);
+			loadSoundsFx(&file, 0x30da6 - 0x1d6, 25);
+		} else {
+			_border = loadAndConvertNeoImage(&file, 0x1371a);
+			byte *palette = (byte *)malloc(16 * 3);
+			for (int i = 0; i < 16; i++) { // gray scale palette
+				palette[i * 3 + 0] = i * (255 / 16);
+				palette[i * 3 + 1] = i * (255 / 16);
+				palette[i * 3 + 2] = i * (255 / 16);
+			}
+			_title = loadAndConvertNeoImage(&file, 0x10, palette);
+
+			loadFonts(&file, 0x8a32);
+			loadMessagesFixedSize(&file, 0xc5d8, 14, 20);
+			loadGlobalObjects(&file, 0xbccc, 8);
+			load8bitBinary(&file, 0x29b3c, 16);
+			loadPalettes(&file, 0x296fa);
+			loadSoundsFx(&file, 0x30da6, 25);
 		}
-		_title = loadAndConvertNeoImage(&file, 0x10, palette);
-
-		loadFonts(&file, 0x8a32);
-		loadMessagesFixedSize(&file, 0xc5d8, 14, 20);
-		loadGlobalObjects(&file, 0xbccc, 8);
-		load8bitBinary(&file, 0x29b3c, 16);
-		loadPalettes(&file, 0x296fa);
-		loadSoundsFx(&file, 0x30da6, 25);
 	}
 }
 


Commit: 7593592c455173b20f1d3c828f8dcc45428cf8f2
    https://github.com/scummvm/scummvm/commit/7593592c455173b20f1d3c828f8dcc45428cf8f2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: more precise timing of some fcl instructions

Changed paths:
    engines/freescape/games/driller/driller.cpp
    engines/freescape/language/instruction.cpp
    engines/freescape/sound.cpp


diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index 3d4d2075e07..d66a3a71b25 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -433,6 +433,7 @@ void DrillerEngine::pressedKey(const int keycode) {
 			_gameStateVars[32]++;
 		} else
 			_drillStatusByArea[_currentArea->getAreaID()] = kDrillerRigOutOfPlace;
+		executeMovementConditions();
 	} else if (keycode == Common::KEYCODE_c) {
 		if (isDOS() && isDemo()) // No support for drilling here yet
 			return;
@@ -473,6 +474,7 @@ void DrillerEngine::pressedKey(const int keycode) {
 		uint32 scoreToRemove = uint32(maxScore * success) / 100;
 		assert(scoreToRemove <= uint32(_gameStateVars[k8bitVariableScore]));
 		_gameStateVars[k8bitVariableScore] -= scoreToRemove;
+		executeMovementConditions();
 	}
 }
 
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index e040fe6492c..17ebb83f044 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -93,6 +93,7 @@ void FreescapeEngine::executeObjectConditions(GeometricObject *obj, bool shot, b
 	assert(obj != nullptr);
 	if (!obj->_conditionSource.empty()) {
 		_firstSound = true;
+		_syncSound = false;
 		_objExecutingCodeSize = collided ? obj->getSize() : Math::Vector3d();
 		if (collided)
 			debugC(1, kFreescapeDebugCode, "Executing with collision flag: %s", obj->_conditionSource.c_str());
@@ -250,7 +251,15 @@ void FreescapeEngine::executeRedraw(FCLInstruction &instruction) {
 	_gfx->flipBuffer();
 	g_system->updateScreen();
 	g_system->delayMillis(10);
-	waitForSounds();
+
+	drawFrame();
+	_gfx->flipBuffer();
+	g_system->updateScreen();
+	g_system->delayMillis(isCPC() ? 100 : 10);
+
+	if (_syncSound) {
+		waitForSounds();
+	}
 }
 
 void FreescapeEngine::executeExecute(FCLInstruction &instruction) {
diff --git a/engines/freescape/sound.cpp b/engines/freescape/sound.cpp
index 053d9668f72..016be2cb3fc 100644
--- a/engines/freescape/sound.cpp
+++ b/engines/freescape/sound.cpp
@@ -38,7 +38,8 @@ void FreescapeEngine::playSound(int index, bool sync) {
 		_syncSound = sync;
 		return;
 	}
-	waitForSounds();
+	if (_syncSound)
+		waitForSounds();
 	switch (index) {
 	case 1:
 		if (_usePrerecordedSounds) {
@@ -267,8 +268,13 @@ void FreescapeEngine::stopAllSounds() {
 }
 
 void FreescapeEngine::waitForSounds() {
-	while (!_speaker->endOfStream())
-		g_system->delayMillis(10);
+	if (_usePrerecordedSounds || isAmiga() || isAtariST())
+		while (_mixer->isSoundIDActive(-1))
+			g_system->delayMillis(10);
+	else {
+		while (!_speaker->endOfStream())
+			g_system->delayMillis(10);
+	}
 }
 
 bool FreescapeEngine::isPlayingSound() {


Commit: 7d3fc01cd167401d7f0dfd8ceaf00daacbd82f91
    https://github.com/scummvm/scummvm/commit/7d3fc01cd167401d7f0dfd8ceaf00daacbd82f91
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: better code to transition between areas

Changed paths:
    engines/freescape/movement.cpp


diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index ed0564cb815..3202e203824 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -57,7 +57,9 @@ void FreescapeEngine::traverseEntrance(uint16 entranceID) {
 	}
 
 	_pitch = rotation.x();
-	if (_objExecutingCodeSize.length() == 0 || ABS(_objExecutingCodeSize.x()) > ABS(_objExecutingCodeSize.z()))
+	if (rotation.y() > 0 && rotation.y() <= 45)
+		_yaw = rotation.y();
+	else if (rotation.y() <= 0 || (rotation.y() >= 180 && rotation.y() < 270))
 		_yaw = rotation.y() + 90;
 	else
 		_yaw = rotation.y() - 90;


Commit: 4486477a93cc751eaa7f025c428d383853217da9
    https://github.com/scummvm/scummvm/commit/4486477a93cc751eaa7f025c428d383853217da9
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: correctly scale mouse position in opengl renderers

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/gfx.h
    engines/freescape/gfx_opengl.cpp
    engines/freescape/gfx_opengl.h
    engines/freescape/gfx_opengl_shaders.cpp
    engines/freescape/gfx_opengl_shaders.h


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index f985adefcf8..73af6eb844b 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -468,22 +468,25 @@ void FreescapeEngine::processInput() {
 			if (_shootMode) {
 				{
 					bool shouldWarp = false;
-					_crossairPosition = mousePos;
-					if (mousePos.x < _viewArea.left) {
+					Common::Point resolution = _gfx->nativeResolution();
+					_crossairPosition.x = _screenW * mousePos.x / resolution.x;
+					_crossairPosition.y = _screenH * mousePos.y / resolution.y;
+
+					if (_crossairPosition.x < _viewArea.left) {
 						_crossairPosition.x = _viewArea.left + 1;
 						shouldWarp = true;
 					}
 
-					if  (mousePos.x > _viewArea.right) {
+					if  (_crossairPosition.x > _viewArea.right) {
 						_crossairPosition.x = _viewArea.right - 1;
 						shouldWarp = true;
 					}
-					if (mousePos.y < _viewArea.top) {
+					if (_crossairPosition.y < _viewArea.top) {
 						_crossairPosition.y =  _viewArea.top + 1;
 						shouldWarp = true;
 					}
 
-					if  (mousePos.y > _viewArea.bottom) {
+					if  (_crossairPosition.y > _viewArea.bottom) {
 						_crossairPosition.y = _viewArea.bottom - 1;
 						shouldWarp = true;
 					}
@@ -508,6 +511,9 @@ void FreescapeEngine::processInput() {
 				bool touchedScreenControls = false;
 
 				#if defined(__ANDROID__) || defined(IPHONE)
+				Common::Point resolution = _gfx->nativeResolution();
+				mousPos.x = _screenW * mousePos.x / resolution.x;
+				mousPos.y = _screenH * mousePos.y / resolution.y;
 				touchedScreenControls = onScreenControls(mousePos);
 				#endif
 
diff --git a/engines/freescape/gfx.h b/engines/freescape/gfx.h
index 7e38d4d7812..e269e45f486 100644
--- a/engines/freescape/gfx.h
+++ b/engines/freescape/gfx.h
@@ -96,6 +96,7 @@ public:
 	virtual void drawBackground(uint8 color);
 
 	Common::Rect viewport() const;
+	virtual Common::Point nativeResolution() { return Common::Point(_screenW, _screenH); }
 
 	// palette
 	void readFromPalette(uint8 index, uint8 &r, uint8 &g, uint8 &b);
diff --git a/engines/freescape/gfx_opengl.cpp b/engines/freescape/gfx_opengl.cpp
index 75c6408d35f..f00435c79fb 100644
--- a/engines/freescape/gfx_opengl.cpp
+++ b/engines/freescape/gfx_opengl.cpp
@@ -58,6 +58,12 @@ void OpenGLRenderer::freeTexture(Texture *texture) {
 	delete texture;
 }
 
+Common::Point OpenGLRenderer::nativeResolution() {
+	GLint vect[4];
+	glGetIntegerv(GL_VIEWPORT, vect);
+	return Common::Point(vect[2], vect[3]);
+}
+
 void OpenGLRenderer::init() {
 
 	computeScreenViewport();
diff --git a/engines/freescape/gfx_opengl.h b/engines/freescape/gfx_opengl.h
index 07536360988..1606bece443 100644
--- a/engines/freescape/gfx_opengl.h
+++ b/engines/freescape/gfx_opengl.h
@@ -84,6 +84,7 @@ public:
 	virtual void init() override;
 	virtual void clear(uint8 r, uint8 g, uint8 b) override;
 	virtual void setViewport(const Common::Rect &rect) override;
+	virtual Common::Point nativeResolution() override;
 	virtual void positionCamera(const Math::Vector3d &pos, const Math::Vector3d &interest) override;
 	virtual void updateProjectionMatrix(float fov, float nearClipPlane, float farClipPlane) override;
 
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index f46abb766fe..1055d4d2a96 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -73,6 +73,12 @@ void OpenGLShaderRenderer::freeTexture(Texture *texture) {
 	delete texture;
 }
 
+Common::Point OpenGLShaderRenderer::nativeResolution() {
+	GLint vect[4];
+	glGetIntegerv(GL_VIEWPORT, vect);
+	return Common::Point(vect[2], vect[3]);
+}
+
 void OpenGLShaderRenderer::init() {
 	computeScreenViewport();
 
diff --git a/engines/freescape/gfx_opengl_shaders.h b/engines/freescape/gfx_opengl_shaders.h
index ddcfe4a15b5..2ad2824b74e 100644
--- a/engines/freescape/gfx_opengl_shaders.h
+++ b/engines/freescape/gfx_opengl_shaders.h
@@ -67,6 +67,7 @@ public:
 	virtual void init() override;
 	virtual void clear(uint8 r, uint8 g, uint8 b) override;
 	virtual void setViewport(const Common::Rect &rect) override;
+	virtual Common::Point nativeResolution() override;
 	virtual void positionCamera(const Math::Vector3d &pos, const Math::Vector3d &interest) override;
 	virtual void updateProjectionMatrix(float fov, float nearClipPlane, float farClipPlane) override;
 


Commit: c36b35b28fad40f58c7ca03443fab026338bf7ca
    https://github.com/scummvm/scummvm/commit/c36b35b28fad40f58c7ca03443fab026338bf7ca
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: fixes for driller demo

Changed paths:
    engines/freescape/demo.cpp
    engines/freescape/games/driller/driller.cpp


diff --git a/engines/freescape/demo.cpp b/engines/freescape/demo.cpp
index fc6cb1845eb..aa4227b1c59 100644
--- a/engines/freescape/demo.cpp
+++ b/engines/freescape/demo.cpp
@@ -40,12 +40,19 @@ void FreescapeEngine::generateDemoInput() {
 
 		if (_currentDemoInputCode >= 0x16 && _currentDemoInputCode <= 0x1a) {
 			event = decodeDOSMouseEvent(_currentDemoInputCode, _currentDemoInputRepetition);
+
+			Common::Point resolution = _gfx->nativeResolution();
+			event.mouse.x = resolution.x * event.mouse.x / _screenW;
+			event.mouse.y = resolution.y * event.mouse.y / _screenH ;
+
 			_demoEvents.push_back(event);
 			g_system->delayMillis(10);
 			_currentDemoInputRepetition = 0;
 		} else if (_currentDemoInputCode == 0x7f) {
 			// NOP
 			_currentDemoInputRepetition--;
+		} else if (_currentDemoInputCode == 0x0) {
+			_forceEndGame = true;
 		} else {
 			event = Common::Event();
 			event.type = Common::EVENT_KEYDOWN;
diff --git a/engines/freescape/games/driller/driller.cpp b/engines/freescape/games/driller/driller.cpp
index d66a3a71b25..4dfc8da6336 100644
--- a/engines/freescape/games/driller/driller.cpp
+++ b/engines/freescape/games/driller/driller.cpp
@@ -756,7 +756,8 @@ void DrillerEngine::initGameState() {
 
 bool DrillerEngine::checkIfGameEnded() {
 	if (isDemo() && _demoMode)
-		return (_demoData[_demoIndex + 1] == 0x5f);
+		if (_demoData[_demoIndex + 1] == 0x5f)
+			return true;
 
 	if (_countdown <= 0) {
 		insertTemporaryMessage(_messagesList[14], _countdown - 2);
@@ -800,12 +801,16 @@ bool DrillerEngine::checkIfGameEnded() {
 
 	if (_forceEndGame) {
 		_forceEndGame = false;
-		insertTemporaryMessage(_messagesList[18], _countdown - 2);
-		drawFrame();
-		_gfx->flipBuffer();
-		g_system->updateScreen();
-		g_system->delayMillis(2000);
-		gotoArea(127, 0);
+		if (isDemo())
+			return true;
+		else {
+			insertTemporaryMessage(_messagesList[18], _countdown - 2);
+			drawFrame();
+			_gfx->flipBuffer();
+			g_system->updateScreen();
+			g_system->delayMillis(2000);
+			gotoArea(127, 0);
+		}
 	}
 
 	if (_currentArea->getAreaID() == 127) {


Commit: 2d154c3b708cf90b7f7de68cf162e22e2f2617b1
    https://github.com/scummvm/scummvm/commit/2d154c3b708cf90b7f7de68cf162e22e2f2617b1
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: Fix typo

Changed paths:
    engines/freescape/freescape.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 73af6eb844b..ba2c019525c 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -512,8 +512,8 @@ void FreescapeEngine::processInput() {
 
 				#if defined(__ANDROID__) || defined(IPHONE)
 				Common::Point resolution = _gfx->nativeResolution();
-				mousPos.x = _screenW * mousePos.x / resolution.x;
-				mousPos.y = _screenH * mousePos.y / resolution.y;
+				mousePos.x = _screenW * mousePos.x / resolution.x;
+				mousePos.y = _screenH * mousePos.y / resolution.y;
 				touchedScreenControls = onScreenControls(mousePos);
 				#endif
 


Commit: fd50a5586bfc58161020f118eb2618eadefbe5c5
    https://github.com/scummvm/scummvm/commit/fd50a5586bfc58161020f118eb2618eadefbe5c5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: enable on screen controls in driller for all the scummvm platforms

Changed paths:
    engines/freescape/freescape.cpp


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index ba2c019525c..d030cf33de7 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -466,37 +466,9 @@ void FreescapeEngine::processInput() {
 				g_system->warpMouse(mousePos.x, mousePos.y);
 
 			if (_shootMode) {
-				{
-					bool shouldWarp = false;
-					Common::Point resolution = _gfx->nativeResolution();
-					_crossairPosition.x = _screenW * mousePos.x / resolution.x;
-					_crossairPosition.y = _screenH * mousePos.y / resolution.y;
-
-					if (_crossairPosition.x < _viewArea.left) {
-						_crossairPosition.x = _viewArea.left + 1;
-						shouldWarp = true;
-					}
-
-					if  (_crossairPosition.x > _viewArea.right) {
-						_crossairPosition.x = _viewArea.right - 1;
-						shouldWarp = true;
-					}
-					if (_crossairPosition.y < _viewArea.top) {
-						_crossairPosition.y =  _viewArea.top + 1;
-						shouldWarp = true;
-					}
-
-					if  (_crossairPosition.y > _viewArea.bottom) {
-						_crossairPosition.y = _viewArea.bottom - 1;
-						shouldWarp = true;
-					}
-
-					if (shouldWarp) {
-						g_system->warpMouse(_crossairPosition.x, _crossairPosition.y);
-						g_system->getEventManager()->purgeMouseEvents();
-						g_system->getEventManager()->purgeKeyboardEvents();
-					}
-				}
+				Common::Point resolution = _gfx->nativeResolution();
+				_crossairPosition.x = _screenW * mousePos.x / resolution.x;
+				_crossairPosition.y = _screenH * mousePos.y / resolution.y;
 				break;
 			}
 
@@ -510,12 +482,10 @@ void FreescapeEngine::processInput() {
 			{
 				bool touchedScreenControls = false;
 
-				#if defined(__ANDROID__) || defined(IPHONE)
 				Common::Point resolution = _gfx->nativeResolution();
 				mousePos.x = _screenW * mousePos.x / resolution.x;
 				mousePos.y = _screenH * mousePos.y / resolution.y;
 				touchedScreenControls = onScreenControls(mousePos);
-				#endif
 
 				if (!touchedScreenControls && _viewArea.contains(_crossairPosition))
 					shoot();


Commit: 60945611b9e507334c2a82cf537139f4cb539bb1
    https://github.com/scummvm/scummvm/commit/60945611b9e507334c2a82cf537139f4cb539bb1
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
DRILLER: Remove newline from detection "extra" field

Changed paths:
    engines/freescape/detection.cpp


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index 49e3c5f1c29..fe497d72499 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -183,7 +183,7 @@ static const ADGameDescription gameDescriptions[] = {
 	},
 	{ // Virtual Worlds release
 		"driller",
-		"This relese requieres unpacking, check the wiki for instructions:\nhttps://wiki.scummvm.org/index.php?title=Driller#AtariST_releases",
+		"This relese requieres unpacking, check the wiki for instructions: https://wiki.scummvm.org/index.php?title=Driller#AtariST_releases",
 		{
 			{"d.pak", 0, "607b44b9d31e0da5668b653e03d25efe", 706},
 			{"dril.all", 0, "65277222effa1eb4d73b234245001d75", 158158},


Commit: 8ef39c1a0cedd3238a7bcfc859ee8a77994425a6
    https://github.com/scummvm/scummvm/commit/8ef39c1a0cedd3238a7bcfc859ee8a77994425a6
Author: Donovan Watteau (contrib at dwatteau.fr)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: Fix typos in detection "extra" field

Changed paths:
    engines/freescape/detection.cpp


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index fe497d72499..ad2a419ff0c 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -183,7 +183,7 @@ static const ADGameDescription gameDescriptions[] = {
 	},
 	{ // Virtual Worlds release
 		"driller",
-		"This relese requieres unpacking, check the wiki for instructions: https://wiki.scummvm.org/index.php?title=Driller#AtariST_releases",
+		"This release requires unpacking, check the wiki for instructions: https://wiki.scummvm.org/index.php?title=Driller#AtariST_releases",
 		{
 			{"d.pak", 0, "607b44b9d31e0da5668b653e03d25efe", 706},
 			{"dril.all", 0, "65277222effa1eb4d73b234245001d75", 158158},


Commit: 60818c0e41d71a71fc5f2a218a78c373ceea1cb8
    https://github.com/scummvm/scummvm/commit/60818c0e41d71a71fc5f2a218a78c373ceea1cb8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: invert colors when rendering player shots in driller

Changed paths:
    engines/freescape/gfx_opengl.cpp
    engines/freescape/gfx_opengl_shaders.cpp


diff --git a/engines/freescape/gfx_opengl.cpp b/engines/freescape/gfx_opengl.cpp
index f00435c79fb..ff692cee8a4 100644
--- a/engines/freescape/gfx_opengl.cpp
+++ b/engines/freescape/gfx_opengl.cpp
@@ -192,7 +192,7 @@ void OpenGLRenderer::renderSensorShoot(byte color, const Math::Vector3d sensor,
 }
 
 void OpenGLRenderer::renderPlayerShoot(byte color, const Common::Point position, const Common::Rect viewArea) {
-	uint8 a, r, g, b;
+	uint8 r, g, b;
 
 	glMatrixMode(GL_PROJECTION);
 	glLoadIdentity();
@@ -202,11 +202,8 @@ void OpenGLRenderer::renderPlayerShoot(byte color, const Common::Point position,
 	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderZX) {
 		r = g = b = 255;
 	} else {
-		uint32 pixel = 0x0;
-		glReadPixels(g_system->getWidth() / 2, g_system->getHeight() / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
-		_texturePixelFormat.colorToARGB(pixel, a, r, g, b);
-		color = indexFromColor(r, g, b);
-		readFromPalette((color + 3) % (_renderMode == Common::kRenderCGA ? 4 : 16), r, g, b);
+		glEnable(GL_COLOR_LOGIC_OP);
+		glLogicOp(GL_INVERT);
 	}
 
 	glDisable(GL_DEPTH_TEST);
@@ -232,6 +229,7 @@ void OpenGLRenderer::renderPlayerShoot(byte color, const Common::Point position,
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glLineWidth(1);
 
+	glDisable(GL_COLOR_LOGIC_OP);
 	glEnable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index 1055d4d2a96..56c203b7955 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -183,7 +183,7 @@ float remap(float f, float s) {
 }
 
 void OpenGLShaderRenderer::renderPlayerShoot(byte color, const Common::Point position, const Common::Rect viewArea) {
-	uint8 a, r, g, b;
+	uint8 r, g, b;
 
 	Math::Matrix4 identity;
 	identity(0, 0) = 1.0;
@@ -198,11 +198,8 @@ void OpenGLShaderRenderer::renderPlayerShoot(byte color, const Common::Point pos
 	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderZX) {
 		r = g = b = 255;
 	} else {
-		uint32 pixel = 0x0;
-		glReadPixels(g_system->getWidth() / 2, g_system->getHeight() / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
-		_texturePixelFormat.colorToARGB(pixel, a, r, g, b);
-		color = indexFromColor(r, g, b);
-		readFromPalette((color + 3) % (_renderMode == Common::kRenderCGA ? 4 : 16), r, g, b);
+		glEnable(GL_COLOR_LOGIC_OP);
+		glLogicOp(GL_INVERT);
 	}
 
 	glDisable(GL_DEPTH_TEST);
@@ -231,6 +228,7 @@ void OpenGLShaderRenderer::renderPlayerShoot(byte color, const Common::Point pos
 
 	glLineWidth(1);
 
+	glDisable(GL_COLOR_LOGIC_OP);
 	glEnable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }


Commit: 4c7152635257cc1859bd4d19fab7e6bc23325694
    https://github.com/scummvm/scummvm/commit/4c7152635257cc1859bd4d19fab7e6bc23325694
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: added offsetOrigin function to avoid breaking setOrigin

Changed paths:
    engines/freescape/objects/geometricobject.cpp
    engines/freescape/objects/geometricobject.h
    engines/freescape/objects/group.cpp
    engines/freescape/objects/group.h


diff --git a/engines/freescape/objects/geometricobject.cpp b/engines/freescape/objects/geometricobject.cpp
index 422a541e804..08812353577 100644
--- a/engines/freescape/objects/geometricobject.cpp
+++ b/engines/freescape/objects/geometricobject.cpp
@@ -171,6 +171,11 @@ GeometricObject::GeometricObject(
 }
 
 void GeometricObject::setOrigin(Math::Vector3d origin_) {
+	_origin = origin_;
+	computeBoundingBox();
+}
+
+void GeometricObject::offsetOrigin(Math::Vector3d origin_) {
 	if (isPolygon(_type)) {
 		Math::Vector3d offset = origin_ - _origin;
 		offset = 32 * offset;
@@ -180,9 +185,7 @@ void GeometricObject::setOrigin(Math::Vector3d origin_) {
 			(*_ordinates)[i + 2] += uint16(offset.z());
 		}
 	}
-
-	_origin = origin_;
-	computeBoundingBox();
+	setOrigin(origin_);
 }
 
 void GeometricObject::scale(int factor) {
diff --git a/engines/freescape/objects/geometricobject.h b/engines/freescape/objects/geometricobject.h
index a7a0ed90f17..2c320241b88 100644
--- a/engines/freescape/objects/geometricobject.h
+++ b/engines/freescape/objects/geometricobject.h
@@ -49,6 +49,7 @@ public:
 		Common::String conditionSource = "");
 	virtual ~GeometricObject();
 	void setOrigin(Math::Vector3d origin) override;
+	void offsetOrigin(Math::Vector3d origin_);
 
 	Object *duplicate() override;
 	void scale(int factor) override;
diff --git a/engines/freescape/objects/group.cpp b/engines/freescape/objects/group.cpp
index a097259e301..7804a062bfa 100644
--- a/engines/freescape/objects/group.cpp
+++ b/engines/freescape/objects/group.cpp
@@ -30,12 +30,12 @@ Group::Group(uint16 objectID_, uint16 flags_, const Common::Array<byte> data_) {
 	_scale = 0;
 
 	int i;
-	for (i = 0; i < 9; i++) {
+	for (i = 0; i < 5; i++) {
 		debugC(1, kFreescapeDebugParser, "group data[%d] = %d", i, data_[i]);
 		if (data_[i] > 0)
 			_objectIds.push_back(data_[i]);
 	}
-	i = 9;
+	i = 5;
 	while (i < int(data_.size() - 4)) {
 		debugC(1, kFreescapeDebugParser, "group data[%d] = %d (index)	", i, data_[i]);
 		_objectIndices.push_back(data_[i]);
@@ -68,20 +68,21 @@ void Group::linkObject(Object *obj) {
 	if (objectIndex == -1)
 		return;
 
+	_origins.push_back(obj->getOrigin());
 	obj->makeInitiallyVisible();
 	obj->makeVisible();
 	_objects.push_back(obj);
 }
 
 void Group::assemble(int frame, int index) {
-	Object *obj = _objects[index];
+	GeometricObject *gobj = (GeometricObject *)_objects[index];
 	Math::Vector3d position = _objectPositions[frame];
 
-	if (!GeometricObject::isPolygon(obj->getType()))
+	if (!GeometricObject::isPolygon(gobj->getType()))
 		position = 32 * position / _scale;
 	else
 		position = position / _scale;
 
-	obj->setOrigin(position);
+	gobj->offsetOrigin(position);
 }
 } // End of namespace Freescape
\ No newline at end of file
diff --git a/engines/freescape/objects/group.h b/engines/freescape/objects/group.h
index 09aad72adbd..04587dbc386 100644
--- a/engines/freescape/objects/group.h
+++ b/engines/freescape/objects/group.h
@@ -33,6 +33,7 @@ public:
 	void assemble(int frame, int index);
 
 	Common::Array<Object *> _objects;
+	Common::Array<Math::Vector3d> _origins;
 	Common::Array<Math::Vector3d> _objectPositions;
 	Common::Array<int16> _objectIndices;
 	Common::Array<int16> _objectIds;


Commit: 33e6c4a45685983b616ed32d5b491710da1f5183
    https://github.com/scummvm/scummvm/commit/33e6c4a45685983b616ed32d5b491710da1f5183
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2023-07-03T22:18:13+02:00

Commit Message:
FREESCAPE: initialize r, g and b variables in renderPlayerShoot

Changed paths:
    engines/freescape/gfx_opengl.cpp
    engines/freescape/gfx_opengl_shaders.cpp


diff --git a/engines/freescape/gfx_opengl.cpp b/engines/freescape/gfx_opengl.cpp
index ff692cee8a4..2ec6dcfc289 100644
--- a/engines/freescape/gfx_opengl.cpp
+++ b/engines/freescape/gfx_opengl.cpp
@@ -202,6 +202,7 @@ void OpenGLRenderer::renderPlayerShoot(byte color, const Common::Point position,
 	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderZX) {
 		r = g = b = 255;
 	} else {
+		r = g = b = 0;
 		glEnable(GL_COLOR_LOGIC_OP);
 		glLogicOp(GL_INVERT);
 	}
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index 56c203b7955..7e75cdd9057 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -198,6 +198,7 @@ void OpenGLShaderRenderer::renderPlayerShoot(byte color, const Common::Point pos
 	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderZX) {
 		r = g = b = 255;
 	} else {
+		r = g = b = 0;
 		glEnable(GL_COLOR_LOGIC_OP);
 		glLogicOp(GL_INVERT);
 	}




More information about the Scummvm-git-logs mailing list