[Scummvm-git-logs] scummvm master -> 74b5ea9f44c6e5df441cecfe701cc2ea40bc722a

sev- noreply at scummvm.org
Sat Apr 29 11:12:12 UTC 2023


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

Summary:
74f58fd118 AGS: Engine: small fix in SDLRendererGraphicsDriver::RenderToBackBuffer()
a2e62d8391 AGS: Engine: simplified main viewport storage, from Viewport struct to Rect
8ad4d50e07 AGS: Engine: base sprite batches use full global offset (inc main viewport)
976c757020 AGS: Engine: simplify sprite batches a little
3fec7f3d4a AGS: Engine: Software renderer supports parent-child sprite batches
6dcabde06a AGS: Engine: for software drawing, also split room viewport / camera batches
a099b5c976 AGS: Engine: define no batch parent as -1, and don't pre-init any batches
30214dfb04 AGS: D3D/OGL: safety hotfix which deals with occasional "stray" sprites
374ce9bffc AGS: Common: width of empty text should be zero
d025390cfc AGS: Engine: safety hotfix for sprites met before 1st sprite batch
e402e69539 AGS: Engine: safety hotfix for render pass without sprite batches
9a801a02fd AGS: Engine: fixed preload image does not have a sprite batch
b554ab3bad AGS: Engine: fixed non-blocking speech disabling interface
0a7fba00a5 AGS: Properly implement sprite batches for legacy modes
c387b37714 AGS: Updated build version (3.6.0.42)
17cbbe1252 AGS: Engine: fixed idle anim speed always set in backwards compat mode
a8df307602 AGS: Common: add Close Notify to FileStream
6a463caba0 AGS: Add _gamma and RenderTarget variables (not used yet)
6565ca8530 AGS: Engine: allow VSync in windowed mode
b10d63b9a8 AGS: Updated build version (3.6.0.43)
4258a430b4 AGS: Engine: add plugin stub for agsappopenurl
c23db8fa2c AGS: Engine: removed redundant code from mgetgraphpos()
0cc9cb6d22 AGS: Engine: always rely on absolute mouse coordinates in mgetgraphpos()
2775d0e3d0 AGS: Engine: a fix for coincidental dynamic sprite replacement in old games
7676b9c23b AGS: Engine: hotfix master volume is not applied after restoring a game
3b37a13af6 AGS: Common: in ConvertUtf8ToAscii() fallback to strcopy if setlocale failed
a6335f0a8b AGS: Engine: bit more logging when loading a translation
993db0fbf3 AGS: Updated build version (3.6.0.44)
7133cbfcdb AGS: Engine: fixed room objects with ID >= 100 fail to Animate
0c3fdb6945 AGS: Common: enhanced arg order in CreateClearBitmap() for consistency
8a5a290ea5 AGS: Common: added more comments to Bitmap functions
7d7f9a3b42 AGS: Common: fix font parameters were used and checked for null later
7b3ca8febf AGS: Engine: simplify PlayMP3File asset loading
7d7a6c6b4e AGS: Engine: improvements to ValidateWindowSize()
a80928712d AGS: Engine: restored --gfxfilter support for explicit scaling multipliers
430758510e AGS: Engine: center the SDL window ourselves on desktop platforms
92f0189a0e AGS: Engine: for renderers, picked out SetVsync into base, add SetVsyncImpl
334e34407b AGS: Engine: try to remember when vsync is unsupported / failed
863a3c485a AGS: Engine: push SDL_KEYUP when simulating a keypress
2db380f1d1 AGS: Engine: save vsync property when the result is known
84187b3bea AGS: Updated build version (3.6.0.45)
c537416279 AGS: Engine: renamed WalkArea.Light to PlayerView, matching its real purpose
dedc6e009d AGS: Engine: a fix for the queued sounds (PlayQueue) starting with a gap
7808fb52e6 AGS: Engine: tidied few remaining old functions in Mouse namespace
72888a7a8c AGS: Engine: move cursor and cursor-over-gui updates to the game update fn
f9de999bbe AGS: Engine: Update cursor and dependent "logic" in special game states
a89fe3cd9e AGS: Updated build version (3.6.0.46)
53a472650d AGS: Common: moved iagsstream to util dir (api dir proved to be excessive)
c3fc687b51 AGS: Common: replaced long type in aastr/allegro/alfont
1b3a417e75 AGS: Common: in IniFile replaced string indexes from int with size_t
9b233e9fde AGS: Common: replaced long type in allegro lib sources
37f97cc8b6 AGS: Engine: add bitness and endianess to the printed engine version
ea65074e6b AGS: Engine: expanded a compat comment in find_free_audio_channel()
7ca114ea49 AGS: Engine: made FadeIn and FadeOut functions look consistent
2deb622aa9 AGS: Engine: fixed FadeIn/Out and ShakeScreen prevent audio from starting
74b5ea9f44 AGS: Updated build version (3.6.0.47), marking "final" 3.6.0


Commit: 74f58fd118dc53ca34a9fe57d6aa6389f6827850
    https://github.com/scummvm/scummvm/commit/74f58fd118dc53ca34a9fe57d6aa6389f6827850
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: small fix in SDLRendererGraphicsDriver::RenderToBackBuffer()

>From upstream dc22dfcd09f5e29aae8fefbbfe3c6cb0aca97937

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.cpp


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index 738d1cb2161..b91e2cea51a 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -373,6 +373,7 @@ void ScummVMRendererGraphicsDriver::RenderToBackBuffer() {
 				virtualScreen->StretchBlt(surface, RectWH(view_offx, view_offy, viewport.GetWidth(), viewport.GetHeight()),
 					batch.Opaque ? kBitmap_Copy : kBitmap_Transparency);
 		} else {
+			_stageVirtualScreen = virtualScreen;
 			cur_spr = RenderSpriteBatch(batch, cur_spr, virtualScreen, view_offx + transform.X, view_offy + transform.Y);
 		}
 	}


Commit: a2e62d839148c5152988efe59cf8f3b5049a5880
    https://github.com/scummvm/scummvm/commit/a2e62d839148c5152988efe59cf8f3b5049a5880
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: simplified main viewport storage, from Viewport struct to Rect

Can't remember why did I make it so, the Rect is enough for now.
>From upstream f7a408d6cea65a5279f3b5b4e980c87bc1e381ca

Changed paths:
    engines/ags/engine/ac/game_state.cpp
    engines/ags/engine/ac/game_state.h


diff --git a/engines/ags/engine/ac/game_state.cpp b/engines/ags/engine/ac/game_state.cpp
index 58b0fdbac31..5a8a2c36317 100644
--- a/engines/ags/engine/ac/game_state.cpp
+++ b/engines/ags/engine/ac/game_state.cpp
@@ -80,19 +80,19 @@ void GameState::SetAutoRoomViewport(bool on) {
 }
 
 void GameState::SetMainViewport(const Rect &viewport) {
-	_mainViewport.SetRect(viewport);
+	_mainViewport = viewport;
 	_GP(mouse).UpdateGraphicArea();
-	_GP(scsystem).viewport_width = game_to_data_coord(_mainViewport.GetRect().GetWidth());
-	_GP(scsystem).viewport_height = game_to_data_coord(_mainViewport.GetRect().GetHeight());
+	_GP(scsystem).viewport_width = game_to_data_coord(_mainViewport.GetWidth());
+	_GP(scsystem).viewport_height = game_to_data_coord(_mainViewport.GetHeight());
 	_mainViewportHasChanged = true;
 }
 
 const Rect &GameState::GetMainViewport() const {
-	return _mainViewport.GetRect();
+	return _mainViewport;
 }
 
 const Rect &GameState::GetUIViewport() const {
-	return _uiViewport.GetRect();
+	return _uiViewport;
 }
 
 PViewport GameState::GetRoomViewport(int index) const {
@@ -112,15 +112,15 @@ PViewport GameState::GetRoomViewportAt(int x, int y) const {
 }
 
 Rect GameState::GetUIViewportAbs() const {
-	return Rect::MoveBy(_uiViewport.GetRect(), _mainViewport.GetRect().Left, _mainViewport.GetRect().Top);
+	return Rect::MoveBy(_uiViewport, _mainViewport.Left, _mainViewport.Top);
 }
 
 Rect GameState::GetRoomViewportAbs(int index) const {
-	return Rect::MoveBy(_roomViewports[index]->GetRect(), _mainViewport.GetRect().Left, _mainViewport.GetRect().Top);
+	return Rect::MoveBy(_roomViewports[index]->GetRect(), _mainViewport.Left, _mainViewport.Top);
 }
 
 void GameState::SetUIViewport(const Rect &viewport) {
-	_uiViewport.SetRect(viewport);
+	_uiViewport = viewport;
 }
 
 static bool ViewportZOrder(const PViewport e1, const PViewport e2) {
@@ -244,7 +244,7 @@ PViewport GameState::CreateRoomViewport() {
 	int index = (int)_roomViewports.size();
 	PViewport viewport(new Viewport());
 	viewport->SetID(index);
-	viewport->SetRect(_mainViewport.GetRect());
+	viewport->SetRect(_mainViewport);
 	_roomViewports.push_back(viewport);
 	_scViewportHandles.push_back(0);
 	_roomViewportsSorted.push_back(viewport);
@@ -306,7 +306,7 @@ PCamera GameState::CreateRoomCamera() {
 	PCamera camera(new Camera());
 	camera->SetID(index);
 	camera->SetAt(0, 0);
-	camera->SetSize(_mainViewport.GetRect().GetSize());
+	camera->SetSize(_mainViewport.GetSize());
 	_scCameraHandles.push_back(0);
 	_roomCameras.push_back(camera);
 	return camera;
diff --git a/engines/ags/engine/ac/game_state.h b/engines/ags/engine/ac/game_state.h
index 6684f363c28..3721c7c8f09 100644
--- a/engines/ags/engine/ac/game_state.h
+++ b/engines/ags/engine/ac/game_state.h
@@ -275,9 +275,9 @@ struct GameState {
 	//
 	// Tells if the room viewport should be adjusted automatically each time a new room is loaded
 	bool IsAutoRoomViewport() const;
-	// Returns main viewport position on screen, this is the overall game view
+	// Returns main (game's) viewport position on screen, this is the overall game view
 	const Rect &GetMainViewport() const;
-	// Returns UI viewport position on screen, this is the GUI layer
+	// Returns UI viewport position on screen, within the main viewport
 	const Rect &GetUIViewport() const;
 	// Returns Room viewport object by it's main index
 	PViewport  GetRoomViewport(int index) const;
@@ -396,11 +396,12 @@ private:
 
 	// Defines if the room viewport should be adjusted to the room size automatically.
 	bool _isAutoRoomViewport = false;
-	// Main viewport defines the rectangle of the drawn and interactable area
+	// Main viewport defines the rectangle of the drawn and interactable area;
 	// in the most basic case it will be equal to the game size.
-	Viewport _mainViewport;
-	// UI viewport defines the render and interaction rectangle of game's UI.
-	Viewport _uiViewport;
+	Rect _mainViewport;
+	// UI viewport defines the render and interaction rectangle of game's UI,
+	// within the main game viewport.
+	Rect _uiViewport;
 	// Room viewports define place on screen where the room camera's
 	// contents are drawn.
 	std::vector<PViewport> _roomViewports;


Commit: 8ad4d50e07b0d86da749c77cffc58bb2a4b801bf
    https://github.com/scummvm/scummvm/commit/8ad4d50e07b0d86da749c77cffc58bb2a4b801bf
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: base sprite batches use full global offset (inc main viewport)

This fixes legacy letterbox for Direct3D/OpenGL renderers.
Partially from upstream 29b62232b9c595cc05b5c6872e97544c1204f2b0

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/game_state.cpp
    engines/ags/engine/ac/game_state.h


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 9d111c213d0..74f02216395 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -685,9 +685,11 @@ void render_black_borders() {
 }
 
 void render_to_screen() {
+	const bool full_frame_rend = _G(gfxDriver)->RequiresFullRedrawEachFrame();
 	// Stage: final plugin callback (still drawn on game screen
 	if (pl_any_want_hook(AGSE_FINALSCREENDRAW)) {
-		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
+		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
+										SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
 		_G(gfxDriver)->DrawSprite(AGSE_FINALSCREENDRAW, 0, nullptr);
 		_G(gfxDriver)->EndSpriteBatch();
 	}
@@ -701,7 +703,7 @@ void render_to_screen() {
 	while (!succeeded && !_G(want_exit) && !_G(abort_engine)) {
 		//     try
 		//     {
-		if (_G(gfxDriver)->RequiresFullRedrawEachFrame()) {
+		if (full_frame_rend) {
 			_G(gfxDriver)->Render();
 		}
 		else {
@@ -2219,14 +2221,16 @@ void construct_game_scene(bool full_redraw) {
 		_GP(play).UpdateRoomCameras();
 
 	// Begin with the parent scene node, defining global offset and flip
-	_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform(0, _GP(play).shake_screen_yoff),
-								(GraphicFlip)_GP(play).screen_flipped);
+	bool full_frame_rend = _G(gfxDriver)->RequiresFullRedrawEachFrame();
+	_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
+									SpriteTransform(0, _GP(play).shake_screen_yoff),
+									(GraphicFlip)_GP(play).screen_flipped);
 
 	// Stage: room viewports
 	if (_GP(play).screen_is_faded_out == 0 && _GP(play).complete_overlay_on == 0) {
 		if (_G(displayed_room) >= 0) {
 			construct_room_view();
-		} else if (!_G(gfxDriver)->RequiresFullRedrawEachFrame()) {
+		} else if (!full_frame_rend) {
 			// black it out so we don't get cursor trails
 			// TODO: this is possible to do with dirty rects system now too (it can paint black rects outside of room viewport)
 			_G(gfxDriver)->GetMemoryBackBuffer()->Fill(0);
@@ -2278,8 +2282,10 @@ void update_mouse_cursor() {
 }
 
 void construct_game_screen_overlay(bool draw_mouse) {
+	const bool full_frame_rend = _G(gfxDriver)->RequiresFullRedrawEachFrame();
 	if (pl_any_want_hook(AGSE_POSTSCREENDRAW)) {
-		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
+		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
+										SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
 		_G(gfxDriver)->DrawSprite(AGSE_POSTSCREENDRAW, 0, nullptr);
 		_G(gfxDriver)->EndSpriteBatch();
 	}
@@ -2291,7 +2297,8 @@ void construct_game_screen_overlay(bool draw_mouse) {
 	// Add mouse cursor pic, and global screen tint effect
 	if (_GP(play).screen_is_faded_out == 0) {
 		// Stage: mouse cursor
-		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
+		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
+										SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
 		if (draw_mouse && !_GP(play).mouse_cursor_hidden) {
 			_G(gfxDriver)->DrawSprite(_G(mousex) - _G(hotx), _G(mousey) - _G(hoty), _G(mouseCursor));
 			invalidate_sprite(_G(mousex) - _G(hotx), _G(mousey) - _G(hoty), _G(mouseCursor), false);
@@ -2306,7 +2313,7 @@ void construct_game_screen_overlay(bool draw_mouse) {
 	}
 
 	// Add global screen fade effect
-	if (_GP(play).screen_is_faded_out != 0 && _G(gfxDriver)->RequiresFullRedrawEachFrame()) {
+	if (_GP(play).screen_is_faded_out != 0 && full_frame_rend) {
 		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform());
 		_G(gfxDriver)->SetScreenFade(_GP(play).fade_to_red, _GP(play).fade_to_green, _GP(play).fade_to_blue);
 		_G(gfxDriver)->EndSpriteBatch();
@@ -2455,7 +2462,8 @@ void render_graphics(IDriverDependantBitmap *extraBitmap, int extraX, int extraY
 	// TODO: extraBitmap is a hack, used to place an additional gui element
 	// on top of the screen. Normally this should be a part of the game UI stage.
 	if (extraBitmap != nullptr) {
-		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetUIViewportAbs(), SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
+		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform(0, _GP(play).shake_screen_yoff),
+										(GraphicFlip)_GP(play).screen_flipped);
 		invalidate_sprite(extraX, extraY, extraBitmap, false);
 		_G(gfxDriver)->DrawSprite(extraX, extraY, extraBitmap);
 		_G(gfxDriver)->EndSpriteBatch();
diff --git a/engines/ags/engine/ac/game_state.cpp b/engines/ags/engine/ac/game_state.cpp
index 5a8a2c36317..03918bac584 100644
--- a/engines/ags/engine/ac/game_state.cpp
+++ b/engines/ags/engine/ac/game_state.cpp
@@ -95,10 +95,22 @@ const Rect &GameState::GetUIViewport() const {
 	return _uiViewport;
 }
 
+SpriteTransform GameState::GetGlobalTransform(bool full_frame_rend) const {
+	// NOTE: shake_screen is not applied to the sprite batches,
+	// but only as a final render factor (optimization)
+	// TODO: also add global flip to the same transform, instead of passing separately?
+	return SpriteTransform(_mainViewport.Left, _mainViewport.Top +
+												   shake_screen_yoff * static_cast<int>(full_frame_rend));
+}
+
 PViewport GameState::GetRoomViewport(int index) const {
 	return _roomViewports[index];
 }
 
+Rect GameState::GetUIViewportAbs() const {
+	return Rect::MoveBy(_uiViewport, _mainViewport.Left, _mainViewport.Top);
+}
+
 const std::vector<PViewport> &GameState::GetRoomViewportsZOrdered() const {
 	return _roomViewportsSorted;
 }
@@ -111,10 +123,6 @@ PViewport GameState::GetRoomViewportAt(int x, int y) const {
 	return nullptr;
 }
 
-Rect GameState::GetUIViewportAbs() const {
-	return Rect::MoveBy(_uiViewport, _mainViewport.Left, _mainViewport.Top);
-}
-
 Rect GameState::GetRoomViewportAbs(int index) const {
 	return Rect::MoveBy(_roomViewports[index]->GetRect(), _mainViewport.Left, _mainViewport.Top);
 }
diff --git a/engines/ags/engine/ac/game_state.h b/engines/ags/engine/ac/game_state.h
index 3721c7c8f09..19f015671ee 100644
--- a/engines/ags/engine/ac/game_state.h
+++ b/engines/ags/engine/ac/game_state.h
@@ -31,6 +31,7 @@
 #include "ags/engine/ac/timer.h"
 #include "ags/shared/game/room_struct.h"
 #include "ags/engine/game/viewport.h"
+#include "ags/engine/gfx/graphics_driver.h"
 #include "ags/engine/media/audio/queued_audio_item.h"
 #include "ags/shared/util/geometry.h"
 #include "ags/shared/util/string_types.h"
@@ -273,12 +274,14 @@ struct GameState {
 	// Viewports are positioned in game screen coordinates, related to the "game size",
 	// while cameras are positioned in room coordinates.
 	//
-	// Tells if the room viewport should be adjusted automatically each time a new room is loaded
-	bool IsAutoRoomViewport() const;
 	// Returns main (game's) viewport position on screen, this is the overall game view
 	const Rect &GetMainViewport() const;
 	// Returns UI viewport position on screen, within the main viewport
 	const Rect &GetUIViewport() const;
+	// Returns SpriteTransform corresponding to the global screen offsets
+	AGS::Engine::SpriteTransform GetGlobalTransform(bool full_frame_rend) const;
+	// Tells if the room viewport should be adjusted automatically each time a new room is loaded
+	bool IsAutoRoomViewport() const;
 	// Returns Room viewport object by it's main index
 	PViewport  GetRoomViewport(int index) const;
 	// Returns Room viewport object by index in z-order
@@ -286,8 +289,9 @@ struct GameState {
 	// Finds room viewport at the given screen coordinates; returns nullptr if non found
 	PViewport  GetRoomViewportAt(int x, int y) const;
 	// Returns UI viewport position in absolute coordinates (with main viewport offset)
-	Rect       GetUIViewportAbs() const;
-	// Returns Room viewport position in absolute coordinates (with main viewport offset)
+	Rect GetUIViewportAbs() const;
+	// Returns Room viewport position in absolute coordinates (with main viewport offset);
+	// this is a helper function, meant for peculiar cases.
 	Rect       GetRoomViewportAbs(int index) const;
 	// Sets if the room viewport should be adjusted automatically each time a new room is loaded
 	void SetAutoRoomViewport(bool on);


Commit: 976c757020ee9927835748630fd6245c393693b7
    https://github.com/scummvm/scummvm/commit/976c757020ee9927835748630fd6245c393693b7
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: simplify sprite batches a little

>From upstream 362ea919e9ffaf2407299fbf4e7815bc43ceac4c

Changed paths:
    engines/ags/engine/ac/draw.cpp


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 74f02216395..98dbc03bab5 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -662,25 +662,19 @@ void draw_and_invalidate_text(Bitmap *ds, int x1, int y1, int font, color_t text
 
 // Renders black borders for the legacy boxed game mode,
 // where whole game screen changes size between large and small rooms
-void render_black_borders() {
-	if (_G(gfxDriver)->UsesMemoryBackBuffer())
-		return;
-	{
-		_G(gfxDriver)->BeginSpriteBatch(RectWH(_GP(game).GetGameRes()), SpriteTransform());
-		const Rect &viewport = _GP(play).GetMainViewport();
-		if (viewport.Top > 0) {
-			// letterbox borders
-			_G(blankImage)->SetStretch(_GP(game).GetGameRes().Width, viewport.Top, false);
-			_G(gfxDriver)->DrawSprite(0, 0, _G(blankImage));
-			_G(gfxDriver)->DrawSprite(0, viewport.Bottom + 1, _G(blankImage));
-		}
-		if (viewport.Left > 0) {
-			// sidebar borders for widescreen
-			_G(blankSidebarImage)->SetStretch(viewport.Left, viewport.GetHeight(), false);
-			_G(gfxDriver)->DrawSprite(0, 0, _G(blankSidebarImage));
-			_G(gfxDriver)->DrawSprite(viewport.Right + 1, 0, _G(blankSidebarImage));
-		}
-		_G(gfxDriver)->EndSpriteBatch();
+static void render_black_borders() {
+	const Rect &viewport = _GP(play).GetMainViewport();
+	if (viewport.Top > 0) {
+		// letterbox borders
+		_G(blankImage)->SetStretch(_GP(game).GetGameRes().Width, viewport.Top, false);
+		_G(gfxDriver)->DrawSprite(0, 0, _G(blankImage));
+		_G(gfxDriver)->DrawSprite(0, viewport.Bottom + 1, _G(blankImage));
+	}
+	if (viewport.Left > 0) {
+		// sidebar borders for widescreen
+		_G(blankSidebarImage)->SetStretch(viewport.Left, viewport.GetHeight(), false);
+		_G(gfxDriver)->DrawSprite(0, 0, _G(blankSidebarImage));
+		_G(gfxDriver)->DrawSprite(viewport.Right + 1, 0, _G(blankSidebarImage));
 	}
 }
 
@@ -2283,11 +2277,10 @@ void update_mouse_cursor() {
 
 void construct_game_screen_overlay(bool draw_mouse) {
 	const bool full_frame_rend = _G(gfxDriver)->RequiresFullRedrawEachFrame();
+	_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
+									SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
 	if (pl_any_want_hook(AGSE_POSTSCREENDRAW)) {
-		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
-										SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
 		_G(gfxDriver)->DrawSprite(AGSE_POSTSCREENDRAW, 0, nullptr);
-		_G(gfxDriver)->EndSpriteBatch();
 	}
 
 	// TODO: find out if it's okay to move cursor animation and state update
@@ -2297,8 +2290,6 @@ void construct_game_screen_overlay(bool draw_mouse) {
 	// Add mouse cursor pic, and global screen tint effect
 	if (_GP(play).screen_is_faded_out == 0) {
 		// Stage: mouse cursor
-		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
-										SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
 		if (draw_mouse && !_GP(play).mouse_cursor_hidden) {
 			_G(gfxDriver)->DrawSprite(_G(mousex) - _G(hotx), _G(mousey) - _G(hoty), _G(mouseCursor));
 			invalidate_sprite(_G(mousex) - _G(hotx), _G(mousey) - _G(hoty), _G(mouseCursor), false);
@@ -2306,16 +2297,18 @@ void construct_game_screen_overlay(bool draw_mouse) {
 		// Stage: screen fx
 		if (_GP(play).screen_tint >= 1)
 			_G(gfxDriver)->SetScreenTint(_GP(play).screen_tint & 0xff, (_GP(play).screen_tint >> 8) & 0xff, (_GP(play).screen_tint >> 16) & 0xff);
-		_G(gfxDriver)->EndSpriteBatch();
-
-		// Stage: legacy letterbox mode borders (has its own sprite batch)
-		render_black_borders();
 	}
+	_G(gfxDriver)->EndSpriteBatch();
 
-	// Add global screen fade effect
-	if (_GP(play).screen_is_faded_out != 0 && full_frame_rend) {
+	// For hardware-accelerated renderers: legacy letterbox and global screen fade effect
+	if (full_frame_rend) {
 		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform());
-		_G(gfxDriver)->SetScreenFade(_GP(play).fade_to_red, _GP(play).fade_to_green, _GP(play).fade_to_blue);
+		// Stage: legacy letterbox mode borders
+		if (_GP(play).screen_is_faded_out == 0)
+			render_black_borders();
+		// Stage: full screen fade fx
+		if (_GP(play).screen_is_faded_out != 0)
+			_G(gfxDriver)->SetScreenFade(_GP(play).fade_to_red, _GP(play).fade_to_green, _GP(play).fade_to_blue);
 		_G(gfxDriver)->EndSpriteBatch();
 	}
 }


Commit: 3fec7f3d4adbe10889a0473cc762c8ea894379c1
    https://github.com/scummvm/scummvm/commit/3fec7f3d4adbe10889a0473cc762c8ea894379c1
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: Software renderer supports parent-child sprite batches

>From upstream f4f715f26c83be708da9c7dbfdcb0fa3196624b4

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.cpp
    engines/ags/engine/gfx/ali_3d_scummvm.h


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index b91e2cea51a..4dfd5ef4237 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -270,41 +270,69 @@ void ScummVMRendererGraphicsDriver::InitSpriteBatch(size_t index, const SpriteBa
 		_spriteBatches.resize(index + 1);
 	ALSpriteBatch &batch = _spriteBatches[index];
 	batch.ID = index;
-	// TODO: correct offsets to have pre-scale (source) and post-scale (dest) offsets!
-	const int src_w = desc.Viewport.GetWidth() / desc.Transform.ScaleX;
-	const int src_h = desc.Viewport.GetHeight() / desc.Transform.ScaleY;
+
+	// Apply parent batch's settings, if preset;
+	Rect viewport = desc.Viewport;
+	SpriteTransform transform = desc.Transform;
+	Bitmap *parent_surf = virtualScreen;
+	if (desc.Parent > 0u) {
+		const auto &parent = _spriteBatches[desc.Parent];
+		if (parent.Surface)
+			parent_surf = parent.Surface.get();
+		// NOTE: we prioritize parent's surface size as a dest viewport,
+		// because parent may have a scheduled scaled blit.
+		if (viewport.IsEmpty())
+			viewport = parent_surf ? RectWH(parent_surf->GetSize()) : RectWH(parent.Viewport.GetSize());
+	} else if (viewport.IsEmpty()) {
+		viewport = _srcRect;
+	}
+
+	// Calculate expected source surf size, based on dest viewport and scaling
+	const int src_w = viewport.GetWidth() / transform.ScaleX;
+	const int src_h = viewport.GetHeight() / transform.ScaleY;
+
+	// Initialize batch surface, depending on the batch description.
 	// Surface was prepared externally (common for room cameras)
 	if (desc.Surface != nullptr) {
 		batch.Surface = desc.Surface;
 		batch.Opaque = true;
-		batch.IsVirtualScreen = false;
+		batch.IsParentRegion = false;
 	}
 	// In case something was not initialized
 	else if (desc.Viewport.IsEmpty() || !virtualScreen) {
 		batch.Surface.reset();
 		batch.Opaque = false;
-		batch.IsVirtualScreen = false;
+		batch.IsParentRegion = false;
 	}
-	// Drawing directly on a viewport without transformation (other than offset)
-	else if (desc.Transform.ScaleX == 1.f && desc.Transform.ScaleY == 1.f) {
+	// Drawing directly on a viewport without transformation (other than offset):
+	// then make a subbitmap of the parent surface (virtualScreen or else).
+	else if (transform.ScaleX == 1.f && transform.ScaleY == 1.f) {
 		// We need this subbitmap for plugins, which use _stageVirtualScreen and are unaware of possible multiple viewports;
 		// TODO: there could be ways to optimize this further, but best is to update plugin rendering hooks (and upgrade plugins)
-		if (!batch.Surface || !batch.IsVirtualScreen ||
-			(batch.Surface->GetWidth() != src_w) || (batch.Surface->GetHeight() != src_h) ||
-			(!batch.Surface->IsSameBitmap(virtualScreen)) ||
-			(batch.Surface->GetSubOffset() != desc.Viewport.GetLT())) {
-			Rect rc = RectWH(desc.Viewport.Left, desc.Viewport.Top, desc.Viewport.GetWidth(), desc.Viewport.GetHeight());
-			batch.Surface.reset(BitmapHelper::CreateSubBitmap(virtualScreen, rc));
+		if (!batch.Surface || !batch.IsParentRegion ||
+			(!batch.Surface->IsSameBitmap(parent_surf)) ||
+			(batch.Surface->GetSize() != Size(src_w, src_h)) ||
+			(batch.Surface->GetSubOffset() != viewport.GetLT())) {
+			batch.Surface.reset(BitmapHelper::CreateSubBitmap(parent_surf, viewport));
 		}
 		batch.Opaque = true;
-		batch.IsVirtualScreen = true;
-	}
-	// No surface prepared and has transformation other than offset
-	else if (!batch.Surface || batch.IsVirtualScreen || batch.Surface->GetWidth() != src_w || batch.Surface->GetHeight() != src_h) {
-		batch.Surface.reset(new Bitmap(src_w, src_h, _srcColorDepth));
+		batch.IsParentRegion = true;
+		// Because we sub-bitmap to viewport, render offsets should account for that
+		transform.X -= viewport.Left;
+		transform.Y -= viewport.Top;
+	}
+	// No surface prepared and has transformation other than offset:
+	// then create exclusive intermediate bitmap.
+	else {
+		if (!batch.Surface || batch.IsParentRegion || (batch.Surface->GetSize() != Size(src_w, src_h))) {
+			batch.Surface.reset(new Bitmap(src_w, src_h, _srcColorDepth));
+		}
 		batch.Opaque = false;
-		batch.IsVirtualScreen = false;
+		batch.IsParentRegion = false;
 	}
+
+	batch.Viewport = viewport;
+	batch.Transform = transform;
 }
 
 void ScummVMRendererGraphicsDriver::ResetAllBatches() {
@@ -353,30 +381,66 @@ void ScummVMRendererGraphicsDriver::RenderToBackBuffer() {
 	// that here would slow things down significantly, so if we ever go that way sprite caching will
 	// be required (similarily to how AGS caches flipped/scaled object sprites now for).
 	//
-	for (size_t cur_spr = 0; cur_spr < _spriteList.size();) {
-		const auto &batch_desc = _spriteBatchDesc[_spriteList[cur_spr].node];
-		const ALSpriteBatch &batch = _spriteBatches[_spriteList[cur_spr].node];
-		const Rect &viewport = batch_desc.Viewport;
-		const SpriteTransform &transform = batch_desc.Transform;
-
-		_rendSpriteBatch = batch.ID;
-		virtualScreen->SetClip(viewport);
-		Bitmap *surface = batch.Surface.get();
-		const int view_offx = viewport.Left;
-		const int view_offy = viewport.Top;
-		if (surface) {
-			if (!batch.Opaque)
-				surface->ClearTransparent();
-			_stageVirtualScreen = surface;
-			cur_spr = RenderSpriteBatch(batch, cur_spr, surface, transform.X, transform.Y);
-			if (!batch.IsVirtualScreen)
-				virtualScreen->StretchBlt(surface, RectWH(view_offx, view_offy, viewport.GetWidth(), viewport.GetHeight()),
-					batch.Opaque ? kBitmap_Copy : kBitmap_Transparency);
-		} else {
-			_stageVirtualScreen = virtualScreen;
-			cur_spr = RenderSpriteBatch(batch, cur_spr, virtualScreen, view_offx + transform.X, view_offy + transform.Y);
+
+	const size_t last_batch_to_rend = _spriteBatchDesc.size() - 1;
+	for (size_t cur_bat = 0u, last_bat = 0u, cur_spr = 0u; last_bat <= last_batch_to_rend;) {
+		// Test if we are entering this batch (and not continuing after coming back from nested)
+		if (cur_spr == _spriteBatchRange[cur_bat].first) {
+			const auto &batch = _spriteBatches[cur_bat];
+			// Prepare the transparent surface
+			if (batch.Surface && !batch.Opaque)
+				batch.Surface->ClearTransparent();
+		}
+
+		// Render immediate batch sprites, if any, update cur_spr iterator
+		if ((cur_spr < _spriteList.size()) && (cur_bat == _spriteList[cur_spr].node)) {
+			const auto &batch = _spriteBatches[cur_bat];
+			const auto &batch_desc = _spriteBatchDesc[cur_bat];
+			Bitmap *surface = batch.Surface.get();
+			Bitmap *parent_surf = ((batch_desc.Parent > 0u) && _spriteBatches[batch_desc.Parent].Surface) ? _spriteBatches[batch_desc.Parent].Surface.get() : virtualScreen;
+			const Rect &viewport = batch.Viewport;
+			const SpriteTransform &transform = batch.Transform;
+
+			_rendSpriteBatch = batch.ID;
+			parent_surf->SetClip(viewport); // CHECKME: this is not exactly correct?
+			if (surface && !batch.IsParentRegion) {
+				_stageVirtualScreen = surface;
+				cur_spr = RenderSpriteBatch(batch, cur_spr, surface, transform.X, transform.Y);
+			} else {
+				_stageVirtualScreen = surface ? surface : parent_surf;
+				cur_spr = RenderSpriteBatch(batch, cur_spr, _stageVirtualScreen, transform.X, transform.Y);
+			}
+		}
+
+		// Test if we're exiting current batch (and not going into nested ones):
+		// if there's no sprites belonging to this batch (direct, or nested),
+		// and if there's no nested batches (even if empty ones)
+		const uint32_t was_bat = cur_bat;
+		while ((cur_bat != UINT32_MAX) && (cur_spr >= _spriteBatchRange[cur_bat].second) &&
+			   ((last_bat == last_batch_to_rend) || (_spriteBatchDesc[last_bat + 1].Parent != cur_bat))) {
+			const auto &batch = _spriteBatches[cur_bat];
+			const auto &batch_desc = _spriteBatchDesc[cur_bat];
+			Bitmap *surface = batch.Surface.get();
+			Bitmap *parent_surf = ((batch_desc.Parent > 0u) && _spriteBatches[batch_desc.Parent].Surface) ? _spriteBatches[batch_desc.Parent].Surface.get() : virtualScreen;
+			const Rect &viewport = batch.Viewport;
+
+			// If we're not drawing directly to the subregion of a parent surface,
+			// then blit our own surface to the parent's
+			if (surface && !batch.IsParentRegion) {
+				parent_surf->StretchBlt(surface, viewport, batch.Opaque ? kBitmap_Copy : kBitmap_Transparency);
+			}
+
+			// Back to the parent batch
+			cur_bat = batch_desc.Parent;
+		}
+
+		// If we stayed at the same batch, this means that there are still nested batches;
+		// if there's no batches in the stack left, this means we got to move forward anyway.
+		if ((was_bat == cur_bat) || (cur_bat == UINT32_MAX)) {
+			cur_bat = ++last_bat;
 		}
 	}
+
 	_stageVirtualScreen = virtualScreen;
 	_rendSpriteBatch = UINT32_MAX;
 	ClearDrawLists();
@@ -587,7 +651,7 @@ void ScummVMRendererGraphicsDriver::SetMemoryBackBuffer(Bitmap *backBuffer) {
 	if (_rendSpriteBatch != UINT32_MAX)
 		return;
 	for (auto &batch : _spriteBatches) {
-		if (batch.IsVirtualScreen)
+		if (batch.IsParentRegion)
 			batch.Surface.reset();
 	}
 }
diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.h b/engines/ags/engine/gfx/ali_3d_scummvm.h
index bfeb47d53e1..2e907b2f581 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.h
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.h
@@ -139,11 +139,16 @@ private:
 typedef SpriteDrawListEntry<ALSoftwareBitmap> ALDrawListEntry;
 // Software renderer's sprite batch
 struct ALSpriteBatch {
-	uint32_t ID = 0;
+	uint32_t ID = 0u;
+	// Clipping viewport, also used as a destination for blitting optional Surface;
+	// in *relative* coordinates to parent surface.
+	Rect Viewport;
+	// Optional model transformation, to be applied to each sprite
+	SpriteTransform Transform;
 	// Intermediate surface which will be drawn upon and transformed if necessary
 	std::shared_ptr<Bitmap> Surface;
-	// Whether surface is a virtual screen's region
-	bool IsVirtualScreen = false;
+	// Whether surface is a parent surface's region (e.g. virtual screen)
+	bool IsParentRegion = false;
 	// Tells whether the surface is treated as opaque or transparent
 	bool Opaque = false;
 };


Commit: 6dcabde06a5ce877dba3f736aeb819aeeb15ac74
    https://github.com/scummvm/scummvm/commit/6dcabde06a5ce877dba3f736aeb819aeeb15ac74
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: for software drawing, also split room viewport / camera batches

>From upstream 5446f80a4c95d4581991a52ea54781820f28244f

Changed paths:
    engines/ags/engine/ac/draw.cpp


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 98dbc03bab5..de094ca39b7 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -2089,14 +2089,16 @@ static void construct_room_view() {
 		const Rect &cam_rc = camera->GetRect();
 		const float view_sx = (float)view_rc.GetWidth() / (float)cam_rc.GetWidth();
 		const float view_sy = (float)view_rc.GetHeight() / (float)cam_rc.GetHeight();
+		const SpriteTransform view_trans(view_rc.Left, view_rc.Top, view_sx, view_sy);
+		const SpriteTransform cam_trans(-cam_rc.Left, -cam_rc.Top);
 
 		if (_G(gfxDriver)->RequiresFullRedrawEachFrame()) {
 			// For hw renderer we draw everything as a sprite stack;
 			// viewport-camera pair is done as 2 nested scene nodes,
 			// where first defines how camera's image translates into the viewport on screen,
 			// and second - how room's image translates into the camera.
-			_G(gfxDriver)->BeginSpriteBatch(view_rc, SpriteTransform(view_rc.Left, view_rc.Top, view_sx, view_sy));
-			_G(gfxDriver)->BeginSpriteBatch(Rect(), SpriteTransform(-cam_rc.Left, -cam_rc.Top));
+			_G(gfxDriver)->BeginSpriteBatch(view_rc, view_trans);
+			_G(gfxDriver)->BeginSpriteBatch(Rect(), cam_trans);
 			_G(gfxDriver)->SetStageScreen(cam_rc.GetSize(), cam_rc.Left, cam_rc.Top);
 			put_sprite_list_on_screen(true);
 			_G(gfxDriver)->EndSpriteBatch();
@@ -2105,7 +2107,7 @@ static void construct_room_view() {
 			// For software renderer - combine viewport and camera in one batch,
 			// due to how the room drawing is implemented currently in the software mode.
 			// TODO: review this later?
-			SpriteTransform room_trans(-cam_rc.Left, -cam_rc.Top, view_sx, view_sy, 0.f);
+			_G(gfxDriver)->BeginSpriteBatch(view_rc, view_trans);
 
 			if (_GP(CameraDrawData)[viewport->GetID()].Frame == nullptr && _GP(CameraDrawData)[viewport->GetID()].IsOverlap) {
 				// room background is prepended to the sprite stack
@@ -2117,15 +2119,16 @@ static void construct_room_view() {
 				// coordinates (cam1 -> screen -> cam2).
 				// It's not clear whether this is worth the effort, but if it is,
 				// then we'd need to optimise view/cam data first.
-				_G(gfxDriver)->BeginSpriteBatch(view_rc, room_trans);
+				_G(gfxDriver)->BeginSpriteBatch(Rect(), cam_trans);
 				_G(gfxDriver)->DrawSprite(0, 0, _G(roomBackgroundBmp));
 			} else {
 				// room background is drawn by dirty rects system
 				PBitmap bg_surface = draw_room_background(viewport.get());
-				_G(gfxDriver)->BeginSpriteBatch(view_rc, room_trans, kFlip_None, bg_surface);
+				_G(gfxDriver)->BeginSpriteBatch(Rect(), cam_trans, kFlip_None, bg_surface);
 			}
 		put_sprite_list_on_screen(true);
 		_G(gfxDriver)->EndSpriteBatch();
+		_G(gfxDriver)->EndSpriteBatch();
 		}
 	}
 


Commit: a099b5c976a61da0f8d44a63ce43d52e3b6bbdf1
    https://github.com/scummvm/scummvm/commit/a099b5c976a61da0f8d44a63ce43d52e3b6bbdf1
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: define no batch parent as -1, and don't pre-init any batches

Partially from upstream 975e2192bce136d81275c93310d3de200de1bfba

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.cpp
    engines/ags/engine/gfx/gfx_driver_base.cpp
    engines/ags/engine/gfx/gfx_driver_base.h


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index 4dfd5ef4237..77909cd6abd 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -51,9 +51,6 @@ ScummVMRendererGraphicsDriver::ScummVMRendererGraphicsDriver() {
 	_tint_blue = 0;
 	virtualScreen = nullptr;
 	_stageVirtualScreen = nullptr;
-
-	// Initialize default sprite batch, it will be used when no other batch was activated
-	ScummVMRendererGraphicsDriver::InitSpriteBatch(0, _spriteBatchDesc[0]);
 }
 
 ScummVMRendererGraphicsDriver::~ScummVMRendererGraphicsDriver() {
@@ -275,7 +272,7 @@ void ScummVMRendererGraphicsDriver::InitSpriteBatch(size_t index, const SpriteBa
 	Rect viewport = desc.Viewport;
 	SpriteTransform transform = desc.Transform;
 	Bitmap *parent_surf = virtualScreen;
-	if (desc.Parent > 0u) {
+	if (desc.Parent != UINT32_MAX) {
 		const auto &parent = _spriteBatches[desc.Parent];
 		if (parent.Surface)
 			parent_surf = parent.Surface.get();
@@ -366,8 +363,8 @@ void ScummVMRendererGraphicsDriver::SetStageScreen(const Size & /*sz*/, int /*x*
 
 void ScummVMRendererGraphicsDriver::RenderToBackBuffer() {
 	// Close unended batches, and issue a warning
-	assert(_actSpriteBatch == 0);
-	while (_actSpriteBatch > 0)
+	assert(_actSpriteBatch == UINT32_MAX);
+	while (_actSpriteBatch != UINT32_MAX)
 		EndSpriteBatch();
 
 	// Render all the sprite batches with necessary transformations
@@ -397,7 +394,7 @@ void ScummVMRendererGraphicsDriver::RenderToBackBuffer() {
 			const auto &batch = _spriteBatches[cur_bat];
 			const auto &batch_desc = _spriteBatchDesc[cur_bat];
 			Bitmap *surface = batch.Surface.get();
-			Bitmap *parent_surf = ((batch_desc.Parent > 0u) && _spriteBatches[batch_desc.Parent].Surface) ? _spriteBatches[batch_desc.Parent].Surface.get() : virtualScreen;
+			Bitmap *parent_surf = ((batch_desc.Parent != UINT32_MAX) && _spriteBatches[batch_desc.Parent].Surface) ? _spriteBatches[batch_desc.Parent].Surface.get() : virtualScreen;
 			const Rect &viewport = batch.Viewport;
 			const SpriteTransform &transform = batch.Transform;
 
@@ -421,7 +418,7 @@ void ScummVMRendererGraphicsDriver::RenderToBackBuffer() {
 			const auto &batch = _spriteBatches[cur_bat];
 			const auto &batch_desc = _spriteBatchDesc[cur_bat];
 			Bitmap *surface = batch.Surface.get();
-			Bitmap *parent_surf = ((batch_desc.Parent > 0u) && _spriteBatches[batch_desc.Parent].Surface) ? _spriteBatches[batch_desc.Parent].Surface.get() : virtualScreen;
+			Bitmap *parent_surf = ((batch_desc.Parent != UINT32_MAX) && _spriteBatches[batch_desc.Parent].Surface) ? _spriteBatches[batch_desc.Parent].Surface.get() : virtualScreen;
 			const Rect &viewport = batch.Viewport;
 
 			// If we're not drawing directly to the subregion of a parent surface,
diff --git a/engines/ags/engine/gfx/gfx_driver_base.cpp b/engines/ags/engine/gfx/gfx_driver_base.cpp
index d25229bcffd..8d49f822bb9 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.cpp
+++ b/engines/ags/engine/gfx/gfx_driver_base.cpp
@@ -36,10 +36,7 @@ GraphicsDriverBase::GraphicsDriverBase()
 	, _drawScreenCallback(nullptr)
 	, _spriteEvtCallback(nullptr)
 	, _initGfxCallback(nullptr) {
-	// Initialize default sprite batch, it will be used when no other batch was activated
-	_actSpriteBatch = 0;
-	_spriteBatchDesc.push_back(SpriteBatchDesc());
-	_spriteBatchRange.push_back(std::make_pair((size_t) 0, (size_t) 0));
+	_actSpriteBatch = UINT32_MAX;
 	_rendSpriteBatch = UINT32_MAX;
 }
 
@@ -76,17 +73,18 @@ void GraphicsDriverBase::BeginSpriteBatch(const Rect &viewport, const SpriteTran
 }
 
 void GraphicsDriverBase::EndSpriteBatch() {
+	assert(_actSpriteBatch != UINT32_MAX);
+	if (_actSpriteBatch == UINT32_MAX)
+		return;
 	_spriteBatchRange[_actSpriteBatch].second = GetLastDrawEntryIndex();
 	_actSpriteBatch = _spriteBatchDesc[_actSpriteBatch].Parent;
 }
 
 void GraphicsDriverBase::ClearDrawLists() {
 	ResetAllBatches();
-	_actSpriteBatch = 0;
+	_actSpriteBatch = UINT32_MAX;
 	_spriteBatchDesc.clear();
 	_spriteBatchRange.clear();
-	_spriteBatchDesc.push_back(SpriteBatchDesc());
-	_spriteBatchRange.push_back(std::make_pair((size_t) 0, (size_t) 0));
 }
 
 void GraphicsDriverBase::OnInit() {
@@ -117,10 +115,6 @@ void GraphicsDriverBase::OnSetNativeRes(const GraphicResolution &native_res) {
 	_srcRect = RectWH(0, 0, native_res.Width, native_res.Height);
 	_srcColorDepth = native_res.ColorDepth;
 	OnScalingChanged();
-
-	// Adjust default sprite batch making it comply to native size
-	_spriteBatchDesc[0].Viewport = RectWH(native_res);
-	InitSpriteBatch(_actSpriteBatch, _spriteBatchDesc[_actSpriteBatch]);
 }
 
 void GraphicsDriverBase::OnSetRenderFrame(const Rect &dst_rect) {
diff --git a/engines/ags/engine/gfx/gfx_driver_base.h b/engines/ags/engine/gfx/gfx_driver_base.h
index cd00edc563d..f8fa9bd2632 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.h
+++ b/engines/ags/engine/gfx/gfx_driver_base.h
@@ -45,7 +45,7 @@ using Shared::PlaneScaling;
 
 // Sprite batch, defines viewport and an optional model transformation for the list of sprites
 struct SpriteBatchDesc {
-	uint32_t                 Parent = 0;
+	uint32_t                 Parent = UINT32_MAX;
 	// View rectangle for positioning and clipping, in resolution coordinates
 	// (this may be screen or game frame resolution, depending on circumstances)
 	Rect                     Viewport;


Commit: 30214dfb04548f2ff3adb0c60e09f8387c6c6be8
    https://github.com/scummvm/scummvm/commit/30214dfb04548f2ff3adb0c60e09f8387c6c6be8
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: D3D/OGL: safety hotfix which deals with occasional "stray" sprites

Partially from upstream ae559b469fc5dc4b01d3497f5ca13c2961c98c82

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.cpp


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index 77909cd6abd..7cc949e7e95 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -339,6 +339,7 @@ void ScummVMRendererGraphicsDriver::ResetAllBatches() {
 }
 
 void ScummVMRendererGraphicsDriver::DrawSprite(int x, int y, IDriverDependantBitmap *bitmap) {
+	assert(_actSpriteBatch != UINT32_MAX);
 	_spriteList.push_back(ALDrawListEntry((ALSoftwareBitmap *)bitmap, _actSpriteBatch, x, y));
 }
 
@@ -348,6 +349,7 @@ void ScummVMRendererGraphicsDriver::SetScreenFade(int /*red*/, int /*green*/, in
 }
 
 void ScummVMRendererGraphicsDriver::SetScreenTint(int red, int green, int blue) {
+	assert(_actSpriteBatch != UINT32_MAX);
 	_tint_red = red;
 	_tint_green = green;
 	_tint_blue = blue;


Commit: 374ce9bffccf18685ffc6b4dc24af78b722535d8
    https://github.com/scummvm/scummvm/commit/374ce9bffccf18685ffc6b4dc24af78b722535d8
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: width of empty text should be zero

>From upstream 9c9277dae1b56650b7022a824579626cfdb62728

Changed paths:
    engines/ags/shared/font/fonts.cpp


diff --git a/engines/ags/shared/font/fonts.cpp b/engines/ags/shared/font/fonts.cpp
index 8a71caa3be9..8292cb646c1 100644
--- a/engines/ags/shared/font/fonts.cpp
+++ b/engines/ags/shared/font/fonts.cpp
@@ -205,6 +205,8 @@ int get_text_width(const char *texx, size_t fontNumber) {
 int get_text_width_outlined(const char *text, size_t font_number) {
 	if (font_number >= _GP(fonts).size() || !_GP(fonts)[font_number].Renderer)
 		return 0;
+	if (text == nullptr || text[0] == 0) // we ignore outline width since the text is empty
+		return 0;
 	int self_width = _GP(fonts)[font_number].Renderer->GetTextWidth(text, font_number);
 	int outline = _GP(fonts)[font_number].Info.Outline;
 	if (outline < 0 || static_cast<size_t>(outline) > _GP(fonts).size()) { // FONT_OUTLINE_AUTO or FONT_OUTLINE_NONE


Commit: d025390cfc86af9fe6b3a3b7456bb0becf98fccf
    https://github.com/scummvm/scummvm/commit/d025390cfc86af9fe6b3a3b7456bb0becf98fccf
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: safety hotfix for sprites met before 1st sprite batch

Partially from upstream 8e38f612d8b8fe834bdc8a55bb5ef543e6b45cb8

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.cpp


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index 7cc949e7e95..d82030a13e5 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -384,7 +384,7 @@ void ScummVMRendererGraphicsDriver::RenderToBackBuffer() {
 	const size_t last_batch_to_rend = _spriteBatchDesc.size() - 1;
 	for (size_t cur_bat = 0u, last_bat = 0u, cur_spr = 0u; last_bat <= last_batch_to_rend;) {
 		// Test if we are entering this batch (and not continuing after coming back from nested)
-		if (cur_spr == _spriteBatchRange[cur_bat].first) {
+		if (cur_spr <= _spriteBatchRange[cur_bat].first) {
 			const auto &batch = _spriteBatches[cur_bat];
 			// Prepare the transparent surface
 			if (batch.Surface && !batch.Opaque)


Commit: e402e69539ce55f9f6ce14102bb43fbcfb90261c
    https://github.com/scummvm/scummvm/commit/e402e69539ce55f9f6ce14102bb43fbcfb90261c
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: safety hotfix for render pass without sprite batches

Partially from upstream 7ad4f424f473f480c830faf3f7a2aca4388aa8fe

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.cpp


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index d82030a13e5..5ff6f5c7406 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -369,6 +369,11 @@ void ScummVMRendererGraphicsDriver::RenderToBackBuffer() {
 	while (_actSpriteBatch != UINT32_MAX)
 		EndSpriteBatch();
 
+	if (_spriteBatchDesc.size() == 0) {
+		ClearDrawLists();
+		return; // no batches - no render
+	}
+
 	// Render all the sprite batches with necessary transformations
 	//
 	// NOTE: that's not immediately clear whether it would be faster to first draw upon a camera-sized


Commit: 9a801a02fdd95e280d0915e9422479846358cefe
    https://github.com/scummvm/scummvm/commit/9a801a02fdd95e280d0915e9422479846358cefe
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: fixed preload image does not have a sprite batch

>From upstream 84730a4bbfb971855c667241a9a47745f283f12e

Changed paths:
    engines/ags/engine/main/engine.cpp


diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index b8c5a790e4d..eb68f2cae73 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -499,7 +499,9 @@ void show_preload() {
 		IDriverDependantBitmap *ddb = _G(gfxDriver)->CreateDDBFromBitmap(tsc, false, true);
 		ddb->SetStretch(view.GetWidth(), view.GetHeight());
 		_G(gfxDriver)->ClearDrawLists();
+		_G(gfxDriver)->BeginSpriteBatch(view);
 		_G(gfxDriver)->DrawSprite(0, 0, ddb);
+		_G(gfxDriver)->EndSpriteBatch();
 		render_to_screen();
 		_G(gfxDriver)->DestroyDDB(ddb);
 		delete splashsc;


Commit: b554ab3bad4b463d5bf27cfa1bbcbbf9b8ec7a83
    https://github.com/scummvm/scummvm/commit/b554ab3bad4b463d5bf27cfa1bbcbbf9b8ec7a83
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: fixed non-blocking speech disabling interface

Apparently, this is an ancient bug, but it became noticeable
now after some changes to the GUI update logic fixes and
optimizations.

>From upstream e069c3f51c08c163df023157e99e14bbcefd317a

Changed paths:
    engines/ags/engine/ac/display.cpp


diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 9418ec8ecb6..780685fe5ac 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -243,7 +243,12 @@ ScreenOverlay *_display_main(int xx, int yy, int wii, const char *text, int disp
 	// _display_main may be called even for custom textual overlays
 	EndSkippingUntilCharStops();
 
-	if (asspch > 0) {
+	if (_GP(topBar).wantIt) {
+		// the top bar should behave like DisplaySpeech wrt blocking
+		disp_type = DISPLAYTEXT_SPEECH;
+	}
+
+	if ((asspch > 0) && (disp_type < DISPLAYTEXT_NORMALOVERLAY)) {
 		// update the all_buttons_disabled variable in advance
 		// of the adjust_x/y_for_guis calls
 		_GP(play).disabled_user_interface++;
@@ -251,11 +256,6 @@ ScreenOverlay *_display_main(int xx, int yy, int wii, const char *text, int disp
 		_GP(play).disabled_user_interface--;
 	}
 
-	if (_GP(topBar).wantIt) {
-		// the top bar should behave like DisplaySpeech wrt blocking
-		disp_type = DISPLAYTEXT_SPEECH;
-	}
-
 	// remove any previous blocking texts if necessary
 	if (disp_type < DISPLAYTEXT_NORMALOVERLAY)
 		remove_screen_overlay(_GP(play).text_overlay_on);


Commit: 0a7fba00a554c324aade39ae8b3a31a35285eb2a
    https://github.com/scummvm/scummvm/commit/0a7fba00a554c324aade39ae8b3a31a35285eb2a
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Properly implement sprite batches for legacy modes

This completes commit c06dc7dab97ba33d9fcf2edeffe37e8f36e0336a which was only
partial and completely broke the mouse pointer and UI in old games

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/game_state.cpp
    engines/ags/engine/ac/game_state.h


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index de094ca39b7..4b10426e439 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -683,7 +683,7 @@ void render_to_screen() {
 	// Stage: final plugin callback (still drawn on game screen
 	if (pl_any_want_hook(AGSE_FINALSCREENDRAW)) {
 		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
-										SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
+										_GP(play).GetGlobalTransform(full_frame_rend), (GraphicFlip)_GP(play).screen_flipped);
 		_G(gfxDriver)->DrawSprite(AGSE_FINALSCREENDRAW, 0, nullptr);
 		_G(gfxDriver)->EndSpriteBatch();
 	}
@@ -2085,7 +2085,7 @@ static void construct_room_view() {
 		auto camera = viewport->GetCamera();
 		if (!camera)
 			continue;
-		const Rect &view_rc = _GP(play).GetRoomViewportAbs(viewport->GetID());
+		const Rect &view_rc = viewport->GetRect();
 		const Rect &cam_rc = camera->GetRect();
 		const float view_sx = (float)view_rc.GetWidth() / (float)cam_rc.GetWidth();
 		const float view_sy = (float)view_rc.GetHeight() / (float)cam_rc.GetHeight();
@@ -2137,7 +2137,7 @@ static void construct_room_view() {
 
 // Schedule ui rendering
 static void construct_ui_view() {
-	_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetUIViewportAbs());
+	_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetUIViewport());
 	draw_gui_and_overlays();
 	_G(gfxDriver)->EndSpriteBatch();
 	clear_draw_list();
@@ -2220,7 +2220,7 @@ void construct_game_scene(bool full_redraw) {
 	// Begin with the parent scene node, defining global offset and flip
 	bool full_frame_rend = _G(gfxDriver)->RequiresFullRedrawEachFrame();
 	_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
-									SpriteTransform(0, _GP(play).shake_screen_yoff),
+									_GP(play).GetGlobalTransform(full_frame_rend),
 									(GraphicFlip)_GP(play).screen_flipped);
 
 	// Stage: room viewports
@@ -2281,7 +2281,8 @@ void update_mouse_cursor() {
 void construct_game_screen_overlay(bool draw_mouse) {
 	const bool full_frame_rend = _G(gfxDriver)->RequiresFullRedrawEachFrame();
 	_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
-									SpriteTransform(0, _GP(play).shake_screen_yoff), (GraphicFlip)_GP(play).screen_flipped);
+									_GP(play).GetGlobalTransform(full_frame_rend),
+									(GraphicFlip)_GP(play).screen_flipped);
 	if (pl_any_want_hook(AGSE_POSTSCREENDRAW)) {
 		_G(gfxDriver)->DrawSprite(AGSE_POSTSCREENDRAW, 0, nullptr);
 	}
@@ -2458,7 +2459,7 @@ void render_graphics(IDriverDependantBitmap *extraBitmap, int extraX, int extraY
 	// TODO: extraBitmap is a hack, used to place an additional gui element
 	// on top of the screen. Normally this should be a part of the game UI stage.
 	if (extraBitmap != nullptr) {
-		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform(0, _GP(play).shake_screen_yoff),
+		_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(), _GP(play).GetGlobalTransform(_G(gfxDriver)->RequiresFullRedrawEachFrame()),
 										(GraphicFlip)_GP(play).screen_flipped);
 		invalidate_sprite(extraX, extraY, extraBitmap, false);
 		_G(gfxDriver)->DrawSprite(extraX, extraY, extraBitmap);
diff --git a/engines/ags/engine/ac/game_state.cpp b/engines/ags/engine/ac/game_state.cpp
index 03918bac584..641341b7ead 100644
--- a/engines/ags/engine/ac/game_state.cpp
+++ b/engines/ags/engine/ac/game_state.cpp
@@ -107,10 +107,6 @@ PViewport GameState::GetRoomViewport(int index) const {
 	return _roomViewports[index];
 }
 
-Rect GameState::GetUIViewportAbs() const {
-	return Rect::MoveBy(_uiViewport, _mainViewport.Left, _mainViewport.Top);
-}
-
 const std::vector<PViewport> &GameState::GetRoomViewportsZOrdered() const {
 	return _roomViewportsSorted;
 }
diff --git a/engines/ags/engine/ac/game_state.h b/engines/ags/engine/ac/game_state.h
index 19f015671ee..95591f24172 100644
--- a/engines/ags/engine/ac/game_state.h
+++ b/engines/ags/engine/ac/game_state.h
@@ -288,8 +288,6 @@ struct GameState {
 	const std::vector<PViewport> &GetRoomViewportsZOrdered() const;
 	// Finds room viewport at the given screen coordinates; returns nullptr if non found
 	PViewport  GetRoomViewportAt(int x, int y) const;
-	// Returns UI viewport position in absolute coordinates (with main viewport offset)
-	Rect GetUIViewportAbs() const;
 	// Returns Room viewport position in absolute coordinates (with main viewport offset);
 	// this is a helper function, meant for peculiar cases.
 	Rect       GetRoomViewportAbs(int index) const;


Commit: c387b37714b5a2ff7a3b9727f29ac74a6c7ab020
    https://github.com/scummvm/scummvm/commit/c387b37714b5a2ff7a3b9727f29ac74a6c7ab020
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Updated build version (3.6.0.42)

Partially from upstream 0432b26a2b875b7ee36f32fb321342d11901c459

Changed paths:
    engines/ags/shared/core/def_version.h


diff --git a/engines/ags/shared/core/def_version.h b/engines/ags/shared/core/def_version.h
index cc83d48c666..71b6eaa7ca3 100644
--- a/engines/ags/shared/core/def_version.h
+++ b/engines/ags/shared/core/def_version.h
@@ -22,9 +22,9 @@
 #ifndef AGS_SHARED_CORE_DEFVERSION_H
 #define AGS_SHARED_CORE_DEFVERSION_H
 
-#define ACI_VERSION_STR      "3.6.0.41"
+#define ACI_VERSION_STR      "3.6.0.42"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3.6.0.41
+#define ACI_VERSION_MSRC_DEF  3.6.0.42
 #endif
 
 #define SPECIAL_VERSION ""


Commit: 17cbbe12522bd00e89b5e2fb4b55470daba4590e
    https://github.com/scummvm/scummvm/commit/17cbbe12522bd00e89b5e2fb4b55470daba4590e
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: fixed idle anim speed always set in backwards compat mode

Was broken by 0f19fc3
>From upstream 899b6be0ea088737799e075e7906bc3308108699

Changed paths:
    engines/ags/shared/ac/character_info.cpp
    engines/ags/shared/ac/game_setup_struct.cpp


diff --git a/engines/ags/shared/ac/character_info.cpp b/engines/ags/shared/ac/character_info.cpp
index 66675ee2eb8..67dfce86afe 100644
--- a/engines/ags/shared/ac/character_info.cpp
+++ b/engines/ags/shared/ac/character_info.cpp
@@ -78,7 +78,7 @@ void CharacterInfo::ReadFromFile(Stream *in, GameDataVersion data_ver, int save_
 	on = in->ReadInt8();
 
 	if ((data_ver > kGameVersion_Undefined && data_ver < kGameVersion_360_16) ||
-		(save_ver >= 0 && save_ver < 2)) {
+		((data_ver == kGameVersion_Undefined) && save_ver >= 0 && save_ver < 2)) {
 		idle_anim_speed = animspeed + 5;
 	}
 }
diff --git a/engines/ags/shared/ac/game_setup_struct.cpp b/engines/ags/shared/ac/game_setup_struct.cpp
index e429187ecc1..fa56c19947d 100644
--- a/engines/ags/shared/ac/game_setup_struct.cpp
+++ b/engines/ags/shared/ac/game_setup_struct.cpp
@@ -245,8 +245,10 @@ void GameSetupStruct::read_messages(Shared::Stream *in, GameDataVersion data_ver
 
 void GameSetupStruct::ReadCharacters_Aligned(Stream *in, bool is_save) {
 	AlignedStream align_s(in, Shared::kAligned_Read);
+	const GameDataVersion data_ver = is_save ? kGameVersion_Undefined : _G(loaded_game_file_version);
+	const int save_ver = is_save ? 0 : -1;
 	for (int iteratorCount = 0; iteratorCount < numcharacters; ++iteratorCount) {
-		chars[iteratorCount].ReadFromFile(&align_s, is_save ? kGameVersion_Undefined : _G(loaded_game_file_version), 0);
+		chars[iteratorCount].ReadFromFile(&align_s, data_ver, save_ver);
 		align_s.Reset();
 	}
 }


Commit: a8df307602e6dab437e7e6502574d56a17b45a81
    https://github.com/scummvm/scummvm/commit/a8df307602e6dab437e7e6502574d56a17b45a81
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: add Close Notify to FileStream

- with FileStream::FileCloseNotify we can run an event when a file closes
- the arguments are a struct so we can expand them later if needed easily

>From upstream 2c170cacb84f56004d86d8e981710777c9c09554

Changed paths:
    engines/ags/shared/util/file_stream.cpp
    engines/ags/shared/util/file_stream.h


diff --git a/engines/ags/shared/util/file_stream.cpp b/engines/ags/shared/util/file_stream.cpp
index 95af0d96581..4b24c767302 100644
--- a/engines/ags/shared/util/file_stream.cpp
+++ b/engines/ags/shared/util/file_stream.cpp
@@ -48,6 +48,12 @@ bool FileStream::HasErrors() const {
 void FileStream::Close() {
 	delete _file;
 	_file = nullptr;
+	if (FileCloseNotify) {
+		CloseNotifyArgs args;
+		args.Filepath = _fileName;
+		args.WorkMode = _workMode;
+		FileCloseNotify(args);
+	}
 }
 
 bool FileStream::Flush() {
@@ -189,9 +195,12 @@ void FileStream::Open(const String &file_name, FileOpenMode open_mode, FileWorkM
 
 		if (!_file)
 			error("Invalid attempt to create file - %s", file_name.GetCStr());
+
+		_fileName = file_name;
 	}
 }
 
+FileStream::FFileCloseNotify FileStream::FileCloseNotify = nullptr;
 
 String FileStream::getSaveName(const String &filename) {
 	return String(filename.GetCStr() + strlen(SAVE_FOLDER_PREFIX));
diff --git a/engines/ags/shared/util/file_stream.h b/engines/ags/shared/util/file_stream.h
index c2c72ec8993..d2aa6a7d016 100644
--- a/engines/ags/shared/util/file_stream.h
+++ b/engines/ags/shared/util/file_stream.h
@@ -24,6 +24,7 @@
 
 #include "common/savefile.h"
 #include "common/stream.h"
+#include "ags/lib/std/functional.h"
 #include "ags/shared/util/data_stream.h"
 #include "ags/shared/util/file.h" // TODO: extract filestream mode constants
 
@@ -33,6 +34,15 @@ namespace Shared {
 
 class FileStream : public DataStream {
 public:
+	struct CloseNotifyArgs {
+		String Filepath;
+		FileWorkMode WorkMode;
+	};
+
+	// definition of function called when file closes
+	typedef std::function<void(const CloseNotifyArgs &args)> FFileCloseNotify;
+
+	static FFileCloseNotify FileCloseNotify;
 	// Represents an open file object
 	// The constructor may raise std::runtime_error if
 	// - there is an issue opening the file (does not exist, locked, permissions, etc)
@@ -73,6 +83,7 @@ private:
 
 	Common::Stream *_file;
 	const FileWorkMode  _workMode;
+	String _fileName;
 };
 
 } // namespace Shared


Commit: 6a463caba05e271ae6ef321ce27a600f340f8e39
    https://github.com/scummvm/scummvm/commit/6a463caba05e271ae6ef321ce27a600f340f8e39
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Add _gamma and RenderTarget variables (not used yet)

These are the only relevant changes from upstream:
62b2f42b53010f9db17a89365d4db73eeb5056d7 and
33a2417f2d63638e87e9a6199b571a7929664722

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.cpp
    engines/ags/engine/gfx/ali_3d_scummvm.h
    engines/ags/engine/gfx/gfx_driver_base.h


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index 5ff6f5c7406..dc752ddb14a 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -214,6 +214,7 @@ void ScummVMRendererGraphicsDriver::SetGamma(int newGamma) {
 	}
 
 	SDL_SetWindowGammaRamp(sys_get_window(), gamma_red, gamma_green, gamma_blue);
+	_gamma = newGamma;
 #endif
 }
 
diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.h b/engines/ags/engine/gfx/ali_3d_scummvm.h
index 2e907b2f581..e01b1a4bb18 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.h
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.h
@@ -251,6 +251,7 @@ private:
 	uint16 _defaultGammaRed[256] {};
 	uint16 _defaultGammaGreen[256] {};
 	uint16 _defaultGammaBlue[256] {};
+	int _gamma = 100;
 #endif
 
 	/*  SDL_Renderer *_renderer = nullptr;
diff --git a/engines/ags/engine/gfx/gfx_driver_base.h b/engines/ags/engine/gfx/gfx_driver_base.h
index f8fa9bd2632..e7abea45d57 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.h
+++ b/engines/ags/engine/gfx/gfx_driver_base.h
@@ -207,6 +207,7 @@ protected:
 // properties. It may be shared between multiple sprites if necessary.
 struct TextureData {
 	uint32_t ID = UINT32_MAX;
+	bool RenderTarget = false; // replace with flags later
 	virtual ~TextureData() = default;
 protected:
 	TextureData() = default;


Commit: 6565ca85305b5e648b667646133721cbd0b8278d
    https://github.com/scummvm/scummvm/commit/6565ca85305b5e648b667646133721cbd0b8278d
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: allow VSync in windowed mode

>From upstream bcf90697ac93080d0edac3e99727041a4e2b461a

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/system.cpp
    engines/ags/engine/main/engine.cpp


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 4b10426e439..c4ae2b6594c 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -690,8 +690,7 @@ void render_to_screen() {
 	// Stage: engine overlay
 	construct_engine_overlay();
 
-	// only vsync in full screen mode, it makes things worse in a window
-	_G(gfxDriver)->SetVsync((_GP(scsystem).vsync > 0) && (!_GP(scsystem).windowed));
+	_G(gfxDriver)->SetVsync(_GP(scsystem).vsync > 0);
 
 	bool succeeded = false;
 	while (!succeeded && !_G(want_exit) && !_G(abort_engine)) {
diff --git a/engines/ags/engine/ac/system.cpp b/engines/ags/engine/ac/system.cpp
index 1a99e946780..a0be678d3d0 100644
--- a/engines/ags/engine/ac/system.cpp
+++ b/engines/ags/engine/ac/system.cpp
@@ -127,8 +127,10 @@ int System_GetVsync() {
 }
 
 void System_SetVsync(int newValue) {
-	if (_G(gfxDriver)->DoesSupportVsyncToggle())
+	if (_G(gfxDriver)->DoesSupportVsyncToggle()) {
 		_GP(scsystem).vsync = newValue;
+		_GP(usetup).Screen.Params.VSync = newValue != 0;
+	}
 }
 
 int System_GetWindowed() {
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index eb68f2cae73..f16322bc084 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -1226,6 +1226,9 @@ bool engine_try_switch_windowed_gfxmode() {
 	DisplayMode last_opposite_mode = setting.Dm;
 	FrameScaleDef frame = setting.Frame;
 
+	// Apply vsync in case it has been toggled at runtime
+	last_opposite_mode.Vsync = _GP(usetup).Screen.Params.VSync;
+
 	// If there are saved parameters for given mode (fullscreen/windowed)
 	// then use them, if there are not, get default setup for the new mode.
 	bool res;


Commit: b10d63b9a8b0da162ee25136f52def9b1b39f52d
    https://github.com/scummvm/scummvm/commit/b10d63b9a8b0da162ee25136f52def9b1b39f52d
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Updated build version (3.6.0.43)

Partially from upstream 8dce503b05728edabe6205e7fbf3f660ed31e957

Changed paths:
    engines/ags/shared/core/def_version.h


diff --git a/engines/ags/shared/core/def_version.h b/engines/ags/shared/core/def_version.h
index 71b6eaa7ca3..5b45c052ad0 100644
--- a/engines/ags/shared/core/def_version.h
+++ b/engines/ags/shared/core/def_version.h
@@ -22,9 +22,9 @@
 #ifndef AGS_SHARED_CORE_DEFVERSION_H
 #define AGS_SHARED_CORE_DEFVERSION_H
 
-#define ACI_VERSION_STR      "3.6.0.42"
+#define ACI_VERSION_STR      "3.6.0.43"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3.6.0.42
+#define ACI_VERSION_MSRC_DEF  3.6.0.43
 #endif
 
 #define SPECIAL_VERSION ""


Commit: 4258a430b485ea4a81a0492f9763921adafb0dcd
    https://github.com/scummvm/scummvm/commit/4258a430b485ea4a81a0492f9763921adafb0dcd
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: add plugin stub for agsappopenurl

Reimplemented from upstream f7cf17e279ad44e94fadf21b7922b35dd9134fa2

Changed paths:
  A engines/ags/plugins/ags_app_open_url/ags_app_open_url.cpp
  A engines/ags/plugins/ags_app_open_url/ags_app_open_url.h
    engines/ags/module.mk
    engines/ags/plugins/plugin_base.cpp


diff --git a/engines/ags/module.mk b/engines/ags/module.mk
index 13c73481072..354b522c404 100644
--- a/engines/ags/module.mk
+++ b/engines/ags/module.mk
@@ -317,6 +317,7 @@ MODULE_OBJS = \
 	plugins/core/textbox.o \
 	plugins/core/view_frame.o \
 	plugins/ags_agi/ags_agi.o \
+	plugins/ags_app_open_url/ags_app_open_url.o \
 	plugins/ags_blend/ags_blend.o \
 	plugins/ags_clipboard/ags_clipboard.o \
 	plugins/ags_controller/ags_controller.o \
diff --git a/engines/ags/plugins/ags_app_open_url/ags_app_open_url.cpp b/engines/ags/plugins/ags_app_open_url/ags_app_open_url.cpp
new file mode 100644
index 00000000000..78644b5de88
--- /dev/null
+++ b/engines/ags/plugins/ags_app_open_url/ags_app_open_url.cpp
@@ -0,0 +1,44 @@
+/* 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
+ * 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 "ags/plugins/ags_app_open_url/ags_app_open_url.h"
+
+namespace AGS3 {
+namespace Plugins {
+namespace AGSAppOpenURL {
+
+const char *AGSAppOpenURL::AGS_GetPluginName() {
+	return "AGS AppOpenURL";
+}
+
+void AGSAppOpenURL::AGS_EngineStartup(IAGSEngine *engine) {
+	PluginBase::AGS_EngineStartup(engine);
+
+	SCRIPT_METHOD(AppOpenURL, AGSAppOpenURL::AppOpenURL);
+}
+
+void AGSAppOpenURL::AppOpenURL(ScriptMethodParams &params) {
+	warning("AGSAppOpenURL::AppOpenURL() is not implemented");
+}
+
+} // namespace AGSAppOpenURL
+} // namespace Plugins
+} // namespace AGS3
diff --git a/engines/ags/plugins/ags_app_open_url/ags_app_open_url.h b/engines/ags/plugins/ags_app_open_url/ags_app_open_url.h
new file mode 100644
index 00000000000..f31bb514661
--- /dev/null
+++ b/engines/ags/plugins/ags_app_open_url/ags_app_open_url.h
@@ -0,0 +1,54 @@
+/* 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
+ * 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 AGS_PLUGINS_AGS_APP_OPEN_URL_H
+#define AGS_PLUGINS_AGS_APP_OPEN_URL_H
+
+#include "ags/plugins/ags_plugin.h"
+
+namespace AGS3 {
+namespace Plugins {
+namespace AGSAppOpenURL {
+
+class AGSAppOpenURL : public PluginBase {
+	SCRIPT_HASH(AGSAppOpenURL)
+
+private:
+	/**
+	 * Opens a browser window using the specified
+	 * string as URL.
+	 */
+	void AppOpenURL(ScriptMethodParams &params);
+
+public:
+	AGSAppOpenURL() : PluginBase() {}
+	virtual ~AGSAppOpenURL() {}
+
+	const char *AGS_GetPluginName() override;
+	void AGS_EngineStartup(IAGSEngine *engine) override;
+
+};
+
+} // namespace AGSAppOpenURL
+} // namespace Plugins
+} // namespace AGS3
+
+#endif
diff --git a/engines/ags/plugins/plugin_base.cpp b/engines/ags/plugins/plugin_base.cpp
index 4729f8a2325..da2736cc12d 100644
--- a/engines/ags/plugins/plugin_base.cpp
+++ b/engines/ags/plugins/plugin_base.cpp
@@ -22,6 +22,7 @@
 #include "ags/lib/allegro.h"
 #include "ags/plugins/plugin_base.h"
 #include "ags/plugins/ags_agi/ags_agi.h"
+#include "ags/plugins/ags_app_open_url/ags_app_open_url.h"
 #include "ags/plugins/ags_blend/ags_blend.h"
 #include "ags/plugins/ags_clipboard/ags_clipboard.h"
 #include "ags/plugins/ags_controller/ags_controller.h"
@@ -70,6 +71,9 @@ Plugins::PluginBase *pluginOpen(const char *filename) {
 	if (fname.equalsIgnoreCase("AGS_AGI"))
 		return new AGSAgi::AGSAgi();
 
+	if (fname.equalsIgnoreCase("agsappopenurl"))
+		return new AGSAppOpenURL::AGSAppOpenURL();
+
 	if (fname.equalsIgnoreCase("AGSBlend"))
 		return new AGSBlend::AGSBlend();
 


Commit: c23db8fa2cfe04d017301c1d1c8730f989ef7f50
    https://github.com/scummvm/scummvm/commit/c23db8fa2cfe04d017301c1d1c8730f989ef7f50
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: removed redundant code from mgetgraphpos()

>From upstream 07d72ac3e1c89883c9e9430a22596ac19c3c58b1

Changed paths:
    engines/ags/engine/device/mouse_w32.cpp
    engines/ags/globals.h


diff --git a/engines/ags/engine/device/mouse_w32.cpp b/engines/ags/engine/device/mouse_w32.cpp
index 49046b381f6..22b488669d4 100644
--- a/engines/ags/engine/device/mouse_w32.cpp
+++ b/engines/ags/engine/device/mouse_w32.cpp
@@ -42,29 +42,17 @@ enum {
 // static const int MB_ARRAY[3] = { 1, 2, 4 };
 
 void mgetgraphpos() {
-	// TODO: review and possibly rewrite whole thing;
-	// research what disable_mgetgraphpos does, and is this still necessary?
+	// TODO:
 	// disable or update mouse speed control to sdl
 	// (does sdl support mouse cursor speed? is it even necessary anymore?);
 
 	// TODO: [sonneveld] find out where mgetgraphpos is needed, are events polled before that?
 	sys_evt_process_pending();
 
-	if (_G(disable_mgetgraphpos)) {
-		// The cursor coordinates are provided from alternate source;
-		// in this case we completely ignore actual cursor movement.
-		if (!_G(ignore_bounds) &&
-			// When applying script bounds we only do so while cursor is inside game viewport
-			_GP(mouse).ControlRect.IsInside(_G(mousex), _G(mousey)) &&
-			(_G(mousex) < _G(boundx1) || _G(mousey) < _G(boundy1) || _G(mousex) > _G(boundx2) || _G(mousey) > _G(boundy2))) {
-			_G(mousex) = CLIP(_G(mousex), _G(boundx1), _G(boundx2));
-			_G(mousey) = CLIP(_G(mousey), _G(boundy1), _G(boundy2));
-			msetgraphpos(_G(mousex), _G(mousey));
-		}
+	if (_G(switched_away))
 		return;
-	}
 
-	if (!_G(switched_away) && _GP(mouse).ControlEnabled) {
+	if (_GP(mouse).ControlEnabled) {
 		// Use relative mouse movement; speed factor should already be applied by SDL in this mode
 		int rel_x, rel_y;
 		ags_mouse_get_relxy(rel_x, rel_y);
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index c9188dc51bd..abade8ba51b 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -1126,7 +1126,6 @@ public:
 	// real mouse coordinates and bounds
 	int _real_mouse_x = 0, _real_mouse_y = 0;
 	int _boundx1 = 0, _boundx2 = 99999, _boundy1 = 0, _boundy2 = 99999;
-	int _disable_mgetgraphpos = 0;
 	int8 _ignore_bounds = 0;
 	AGS::Shared::Bitmap *_mousecurs[MAXCURSORS];
 


Commit: 0cc9cb6d22f69cf4c75dc27b291a92bbe30dc596
    https://github.com/scummvm/scummvm/commit/0cc9cb6d22f69cf4c75dc27b291a92bbe30dc596
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: always rely on absolute mouse coordinates in mgetgraphpos()

Assume that SDL2 devices, custom devices, and event handling unit have prepared a correct absolute mouse coordinates, in correspondence to the mouse settings.
>From upstream c63e5e93657f2e525df8f3f5390f74a31a21858d

Changed paths:
    engines/ags/engine/ac/sys_events.cpp
    engines/ags/engine/ac/sys_events.h
    engines/ags/engine/device/mouse_w32.cpp
    engines/ags/globals.h


diff --git a/engines/ags/engine/ac/sys_events.cpp b/engines/ags/engine/ac/sys_events.cpp
index 01f0b084d17..8c5a5182b8f 100644
--- a/engines/ags/engine/ac/sys_events.cpp
+++ b/engines/ags/engine/ac/sys_events.cpp
@@ -189,7 +189,7 @@ eAGSMouseButton ags_mgetbutton() {
 	return mgetbutton();;
 }
 
-void ags_mouse_get_relxy(int &x, int &y) {
+void ags_mouse_acquire_relxy(int &x, int &y) {
 	x = _G(mouse_accum_relx);
 	y = _G(mouse_accum_rely);
 	_G(mouse_accum_relx) = 0;
@@ -220,11 +220,10 @@ int ags_check_mouse_wheel() {
 void ags_clear_input_state() {
 	// Clear everything related to the input field
 	::AGS::g_events->clearEvents();
-	_G(mouse_accum_relx) = 0;
-	_G(mouse_accum_rely) = 0;
 	_G(mouse_button_state) = 0;
 	_G(mouse_accum_button_state) = 0;
 	_G(mouse_clear_at_time) = AGS_Clock::now();
+	ags_clear_mouse_movement();
 }
 
 void ags_clear_input_buffer() {
@@ -232,8 +231,7 @@ void ags_clear_input_buffer() {
 	// accumulated state only helps to not miss clicks
 	_G(mouse_accum_button_state) = 0;
 	// forget about recent mouse relative movement too
-	_G(mouse_accum_relx) = 0;
-	_G(mouse_accum_rely) = 0;
+	ags_clear_mouse_movement();
 }
 
 void ags_clear_mouse_movement() {
diff --git a/engines/ags/engine/ac/sys_events.h b/engines/ags/engine/ac/sys_events.h
index 2e16181ef7b..9e023e10c9f 100644
--- a/engines/ags/engine/ac/sys_events.h
+++ b/engines/ags/engine/ac/sys_events.h
@@ -73,8 +73,8 @@ extern void ags_simulate_keypress(eAGSKeyCode ags_key);
 extern bool ags_misbuttondown(eAGSMouseButton but);
 // Returns mouse button code
 extern eAGSMouseButton ags_mgetbutton();
-// Returns recent relative mouse movement
-extern void ags_mouse_get_relxy(int &x, int &y);
+// Returns recent relative mouse movement; resets accumulated values
+extern void ags_mouse_acquire_relxy(int &x, int &y);
 // Updates mouse cursor position in game
 extern void ags_domouse();
 // Returns -1 for wheel down and +1 for wheel up
diff --git a/engines/ags/engine/device/mouse_w32.cpp b/engines/ags/engine/device/mouse_w32.cpp
index 22b488669d4..848b961a800 100644
--- a/engines/ags/engine/device/mouse_w32.cpp
+++ b/engines/ags/engine/device/mouse_w32.cpp
@@ -52,22 +52,14 @@ void mgetgraphpos() {
 	if (_G(switched_away))
 		return;
 
-	if (_GP(mouse).ControlEnabled) {
-		// Use relative mouse movement; speed factor should already be applied by SDL in this mode
-		int rel_x, rel_y;
-		ags_mouse_get_relxy(rel_x, rel_y);
-		_G(real_mouse_x) = CLIP(_G(real_mouse_x) + rel_x, _GP(mouse).ControlRect.Left, _GP(mouse).ControlRect.Right);
-		_G(real_mouse_y) = CLIP(_G(real_mouse_y) + rel_y, _GP(mouse).ControlRect.Top, _GP(mouse).ControlRect.Bottom);
-	} else {
-		// Save real cursor coordinates provided by system
-		_G(real_mouse_x) = CLIP((int)_G(sys_mouse_x), _GP(mouse).ControlRect.Left, _GP(mouse).ControlRect.Right);
-		_G(real_mouse_y) = CLIP((int)_G(sys_mouse_y), _GP(mouse).ControlRect.Top, _GP(mouse).ControlRect.Bottom);
-	}
+	// Save absolute cursor coordinates provided by system
+	// NOTE: relative motion and the speed factor should already be applied by SDL2 or our custom devices.
+	_G(real_mouse_x) = CLIP((int)_G(sys_mouse_x), _GP(mouse).ControlRect.Left, _GP(mouse).ControlRect.Right);
+	_G(real_mouse_y) = CLIP((int)_G(sys_mouse_y), _GP(mouse).ControlRect.Top, _GP(mouse).ControlRect.Bottom);
 
-	// Set new in-game cursor position
+	// Set new in-game cursor position, convert to the in-game logic coordinates
 	_G(mousex) = _G(real_mouse_x);
 	_G(mousey) = _G(real_mouse_y);
-
 	if (!_G(ignore_bounds) &&
 		// When applying script bounds we only do so while cursor is inside game viewport
 		_GP(mouse).ControlRect.IsInside(_G(mousex), _G(mousey)) &&
@@ -76,7 +68,6 @@ void mgetgraphpos() {
 		_G(mousey) = Math::Clamp(_G(mousey), _G(boundy1), _G(boundy2));
 		msetgraphpos(_G(mousex), _G(mousey));
 	}
-
 	// Convert to virtual coordinates
 	_GP(mouse).WindowToGame(_G(mousex), _G(mousey));
 }
@@ -89,6 +80,8 @@ void msetcursorlimit(int x1, int y1, int x2, int y2) {
 }
 
 void msetgraphpos(int xa, int ya) {
+	_G(sys_mouse_x) = xa;
+	_G(sys_mouse_y) = ya;
 	_G(real_mouse_x) = xa;
 	_G(real_mouse_y) = ya;
 	sys_window_set_mouse(_G(real_mouse_x), _G(real_mouse_y));
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index abade8ba51b..ac5f0b5431f 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -234,15 +234,22 @@ public:
 	volatile int _mouse_z = 0;  // Mouse wheel vertical
 	volatile int _mouse_b = 0;  // Mouse buttons bitflags
 	volatile int _mouse_pos = 0;    // X position in upper 16 bits, Y in lower 16
+
+	// Accumulated absolute and relative mouse device motion.
+	// May be retrieved by calling *acquire_absxy and *acquire_relxy functions,
+	// after which these are reset, until next motion event is received.
 	volatile int _sys_mouse_x = 0; // mouse x position
 	volatile int _sys_mouse_y = 0; // mouse y position
 	volatile int _sys_mouse_z = 0; // mouse wheel position
+
 	volatile int _freeze_mouse_flag = 0;
 
+	// Relative x and y deltas
+	int _mouse_accum_relx = 0, _mouse_accum_rely = 0;
+
 	int _mouse_button_state = 0;
 	int _mouse_accum_button_state = 0;
 	uint32 _mouse_clear_at_time = 0;
-	int _mouse_accum_relx = 0, _mouse_accum_rely = 0;
 	eAGSMouseButton _wasbutdown = kMouseNone;
 	int _wasongui = 0;
 


Commit: 2775d0e3d0c9e72be2511d69b044d1bfb428e364
    https://github.com/scummvm/scummvm/commit/2775d0e3d0c9e72be2511d69b044d1bfb428e364
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: a fix for coincidental dynamic sprite replacement in old games

This is a (ugly) backward compatible workaround in game_sprite_deleted().
Apparently there are few games that may rely (either with or without author's intent) on newly
created dynamic sprite being assigned same index as a recently deleted one, which results in
new sprite "secretly" taking place of an old one on the GUI, etc.
One known example is "Kathy Rain" game.

For old games we keep only partial index reset (full cleanup is 3.5.0+).
>From upstream e8409920049b2a8859856d3525a8f112227df4df

Changed paths:
    engines/ags/engine/ac/game.cpp


diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 05a937f143d..fba3ca0aaec 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -1378,6 +1378,15 @@ void game_sprite_deleted(int sprnum) {
 	_G(gfxDriver)->ClearSharedDDB(sprnum);
 	// character and object draw caches
 	reset_objcache_for_sprite(sprnum, true);
+
+	// This is ugly, but apparently there are few games that may rely
+	// (either with or without author's intent) on newly created sprite
+	// being assigned same index as a recently deleted one, which results
+	// in new sprite "secretly" taking place of an old one on the GUI, etc.
+	// So for old games we keep only partial reset (full cleanup is 3.5.0+).
+	const bool reset_sprindex_oldstyle =
+		_G(loaded_game_file_version) < kGameVersion_350;
+
 	// room object graphics
 	if (_G(croom) != nullptr) {
 		for (size_t i = 0; i < (size_t)_G(croom)->numobj; ++i) {
@@ -1385,13 +1394,6 @@ void game_sprite_deleted(int sprnum) {
 				_G(objs)[i].num = 0;
 		}
 	}
-	// gui backgrounds
-	for (size_t i = 0; i < (size_t)_GP(game).numgui; ++i) {
-		if (_GP(guis)[i].BgImage == sprnum) {
-			_GP(guis)[i].BgImage = 0;
-			_GP(guis)[i].MarkChanged();
-		}
-	}
 	// gui buttons
 	for (auto &but : _GP(guibuts)) {
 		if (but.Image == sprnum)
@@ -1406,6 +1408,17 @@ void game_sprite_deleted(int sprnum) {
 			but.MarkChanged();
 		}
 	}
+
+	if (reset_sprindex_oldstyle)
+		return; // stop here for < 3.5.0 games
+
+	// gui backgrounds
+	for (size_t i = 0; i < (size_t)_GP(game).numgui; ++i) {
+		if (_GP(guis)[i].BgImage == sprnum) {
+			_GP(guis)[i].BgImage = 0;
+			_GP(guis)[i].MarkChanged();
+		}
+	}
 	// gui sliders
 	for (auto &slider : _GP(guislider)) {
 		if ((slider.BgImage == sprnum) || (slider.HandleImage == sprnum))


Commit: 7676b9c23bc02100fd30135a724e4a8af52a8458
    https://github.com/scummvm/scummvm/commit/7676b9c23bc02100fd30135a724e4a8af52a8458
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: hotfix master volume is not applied after restoring a game

Also restored a check in System_SetVolume(), where it skipped updating a volume if the value
is the same (this was removed unexpectedly when introducing a SDL2-based audio system).

>From upstream 8d1772b90105ca5b4b150bfb303781af2ceac1a0

Changed paths:
    engines/ags/engine/ac/system.cpp
    engines/ags/engine/game/savegame.cpp


diff --git a/engines/ags/engine/ac/system.cpp b/engines/ags/engine/ac/system.cpp
index a0be678d3d0..4561b52d9d3 100644
--- a/engines/ags/engine/ac/system.cpp
+++ b/engines/ags/engine/ac/system.cpp
@@ -183,6 +183,9 @@ void System_SetVolume(int newvol) {
 	if ((newvol < 0) || (newvol > 100))
 		quit("!System.Volume: invalid volume - must be from 0-100");
 
+	if (newvol == _GP(play).digital_master_volume)
+		return;
+
 	_GP(play).digital_master_volume = newvol;
 
 	Audio::Mixer *mixer = ::AGS::g_vm->_mixer;
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index 009edff8b1d..8d46393bd98 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -543,8 +543,11 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	// restore the queue now that the music is playing
 	_GP(play).music_queue_size = queuedMusicSize;
 
-	if (_GP(play).digital_master_volume >= 0)
-		System_SetVolume(_GP(play).digital_master_volume);
+	if (_GP(play).digital_master_volume >= 0) {
+		int temp_vol = _GP(play).digital_master_volume;
+		_GP(play).digital_master_volume = -1; // reset to invalid state before re-applying
+		System_SetVolume(temp_vol);
+	}
 
 	// Run audio clips on channels
 	// these two crossfading parameters have to be temporarily reset


Commit: 3b37a13af68522d841f7f9078eba2055c7df4d37
    https://github.com/scummvm/scummvm/commit/3b37a13af68522d841f7f9078eba2055c7df4d37
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: in ConvertUtf8ToAscii() fallback to strcopy if setlocale failed

setlocale equivalent is not actually implemented so this does nothing
>From upstream f4bd0604c73aad7ecf05bf6e3f22dbe161434b2c

Changed paths:
    engines/ags/lib/allegro/unicode.cpp
    engines/ags/lib/allegro/unicode.h
    engines/ags/shared/util/string_utils.cpp


diff --git a/engines/ags/lib/allegro/unicode.cpp b/engines/ags/lib/allegro/unicode.cpp
index 4fe5b991092..a1acc6c49de 100644
--- a/engines/ags/lib/allegro/unicode.cpp
+++ b/engines/ags/lib/allegro/unicode.cpp
@@ -1288,8 +1288,10 @@ int ustrsizez(const char *s) {
 	return (intptr_t)s - (intptr_t)orig;
 }
 
-void setlocale(int type, const char *language) {
+const char *setlocale(int type, const char *language) {
+	const char *locale = "C";
 	// TODO: If needed for alfont
+	return locale;
 }
 
 int need_uconvert(const char *s, int type, int newtype) {
diff --git a/engines/ags/lib/allegro/unicode.h b/engines/ags/lib/allegro/unicode.h
index b3310f27b99..b9977c99fad 100644
--- a/engines/ags/lib/allegro/unicode.h
+++ b/engines/ags/lib/allegro/unicode.h
@@ -73,7 +73,7 @@ extern int (*uisok)(int c);
 extern void set_uformat(int type);
 
 enum { LC_CTYPE };
-extern void setlocale(int type, const char *language);
+extern const char *setlocale(int type, const char *language);
 
 /* get_uformat:
  *  Returns the current text encoding format.
diff --git a/engines/ags/shared/util/string_utils.cpp b/engines/ags/shared/util/string_utils.cpp
index 4ab22b15855..e76aee79681 100644
--- a/engines/ags/shared/util/string_utils.cpp
+++ b/engines/ags/shared/util/string_utils.cpp
@@ -246,7 +246,11 @@ void StrUtil::WriteStringMap(const StringMap &map, Stream *out) {
 }
 
 size_t StrUtil::ConvertUtf8ToAscii(const char *mbstr, const char *loc_name, char *out_cstr, size_t out_sz) {
-	// TODO: later consider using C++11 conversion methods
+	// TODO: later consider using alternative conversion methods
+	// (e.g. see C++11 features), as setlocale is unreliable.
+	if (setlocale(LC_CTYPE, loc_name) == nullptr) { // If failed setlocale, then resort to plain copy the mb string
+		return static_cast<size_t>(snprintf(out_cstr, out_sz, "%s", mbstr));
+	}
 	// First convert utf-8 string into widestring;
 	std::vector<wchar_t> wcsbuf; // widechar buffer
 	wcsbuf.resize(Utf8::GetLength(mbstr) + 1);
@@ -258,7 +262,6 @@ size_t StrUtil::ConvertUtf8ToAscii(const char *mbstr, const char *loc_name, char
 		wcsbuf[at] = static_cast<wchar_t>(r);
 	}
 	// Then convert widestring to single-byte string using specified locale
-	setlocale(LC_CTYPE, loc_name);
 	size_t res_sz = wcstombs(out_cstr, &wcsbuf[0], out_sz);
 	setlocale(LC_CTYPE, "");
 	return res_sz;


Commit: a6335f0a8b755d92e3807bfaae8f5deebf9e7fde
    https://github.com/scummvm/scummvm/commit/a6335f0a8b755d92e3807bfaae8f5deebf9e7fde
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: bit more logging when loading a translation

>From upstream 528c1374cee8d8e09654dc5d01698984dd29568a

Changed paths:
    engines/ags/engine/ac/translation.cpp


diff --git a/engines/ags/engine/ac/translation.cpp b/engines/ags/engine/ac/translation.cpp
index 32993d1be9e..5a779fcad73 100644
--- a/engines/ags/engine/ac/translation.cpp
+++ b/engines/ags/engine/ac/translation.cpp
@@ -90,6 +90,7 @@ bool init_translation(const String &lang, const String &fallback_lang) {
 	}
 
 	// Translation read successfully
+	Debug::Printf("Translation loaded: %s", _G(trans_filename).GetCStr());
 	// Configure new game settings
 	if (_GP(trans).NormalFont >= 0)
 		SetNormalFont(_GP(trans).NormalFont);
@@ -109,6 +110,8 @@ bool init_translation(const String &lang, const String &fallback_lang) {
 		set_uformat(U_UTF8);
 	else
 		set_uformat(U_ASCII);
+	String encoding_msg = !encoding.IsEmpty() ? encoding : "presume ASCII";
+	Debug::Printf("Translation's encoding: %s", encoding_msg.GetCStr());
 
 	// Mixed encoding support: 
 	// original text unfortunately may contain extended ASCII chars (> 127);
@@ -120,9 +123,11 @@ bool init_translation(const String &lang, const String &fallback_lang) {
 		String key_enc = (game_codepage > 0) ?
 			String::FromFormat(".%d", game_codepage) :
 			_GP(trans).StrOptions["gameencoding"];
+		Debug::Printf("Game's source encoding hint: own: %d, from TRA: %s", game_codepage, _GP(trans).StrOptions["gameencoding"].GetCStr());
 		if (!key_enc.IsEmpty()) {
 			StringMap conv_map;
 			std::vector<char> ascii; // ascii buffer
+			Debug::Printf("Converting UTF-8 TRA keys to the game's encoding (%s)", key_enc.GetCStr());
 			for (const auto &item : _GP(trans).Dict) {
 				ascii.resize(item._key.GetLength() + 1); // ascii len will be <= utf-8 len
 				StrUtil::ConvertUtf8ToAscii(item._key.GetCStr(), key_enc.GetCStr(), &ascii[0], ascii.size());
@@ -130,9 +135,12 @@ bool init_translation(const String &lang, const String &fallback_lang) {
 			}
 			_GP(trans).Dict = conv_map;
 		}
+		else {
+			Debug::Printf(kDbgMsg_Warn, "WARNING: UTF-8 translation in the ASCII/ANSI game, but no encoding hint for TRA keys conversion");
+		}
 	}
 
-	Debug::Printf("Translation initialized: %s", _G(trans_filename).GetCStr());
+	Debug::Printf(kDbgMsg_Info, "Translation initialized: %s (format: %s)", _G(trans_name).GetCStr(), encoding_msg.GetCStr());
 	return true;
 }
 


Commit: 993db0fbf33146fe3064c9605107c5177c3ffee2
    https://github.com/scummvm/scummvm/commit/993db0fbf33146fe3064c9605107c5177c3ffee2
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Updated build version (3.6.0.44)

Partially from upstream 8cde9d29c1854a7baee489829fa36fdfa8c41a93

Changed paths:
    engines/ags/shared/core/def_version.h


diff --git a/engines/ags/shared/core/def_version.h b/engines/ags/shared/core/def_version.h
index 5b45c052ad0..d61045e4a03 100644
--- a/engines/ags/shared/core/def_version.h
+++ b/engines/ags/shared/core/def_version.h
@@ -22,9 +22,9 @@
 #ifndef AGS_SHARED_CORE_DEFVERSION_H
 #define AGS_SHARED_CORE_DEFVERSION_H
 
-#define ACI_VERSION_STR      "3.6.0.43"
+#define ACI_VERSION_STR      "3.6.0.44"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3.6.0.43
+#define ACI_VERSION_MSRC_DEF  3.6.0.44
 #endif
 
 #define SPECIAL_VERSION ""


Commit: 7133cbfcdb1c7d88d23fb0ca20996eef98a58b17
    https://github.com/scummvm/scummvm/commit/7133cbfcdb1c7d88d23fb0ca20996eef98a58b17
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: fixed room objects with ID >= 100 fail to Animate

This is because of legacy AnimateObject behavior, which treated IDs >= 100
as a command to run AnimateCharacter(ID - 100) instead.

>From upstream 8365802092cd3f704aecffe7d08b4d9f65f1f396

Changed paths:
    engines/ags/engine/ac/global_object.cpp
    engines/ags/engine/ac/runtime_defines.h


diff --git a/engines/ags/engine/ac/global_object.cpp b/engines/ags/engine/ac/global_object.cpp
index df290d831d5..6cc1de8916b 100644
--- a/engines/ags/engine/ac/global_object.cpp
+++ b/engines/ags/engine/ac/global_object.cpp
@@ -216,10 +216,6 @@ int GetObjectBaseline(int obn) {
 }
 
 void AnimateObjectImpl(int obn, int loopn, int spdd, int rept, int direction, int blocking, int sframe, int volume) {
-	if (obn >= MANOBJNUM) {
-		scAnimateCharacter(obn - 100, loopn, spdd, rept);
-		return;
-	}
 	if (!is_valid_object(obn))
 		quit("!AnimateObject: invalid object number specified");
 	if (_G(objs)[obn].view == RoomObject::NoView)
@@ -266,12 +262,23 @@ void AnimateObjectImpl(int obn, int loopn, int spdd, int rept, int direction, in
 		GameLoopUntilValueIsZero(&_G(objs)[obn].cycling);
 }
 
+// A legacy variant of AnimateObject implementation: for pre-2.72 scripts;
+// it has a quirk: for IDs >= 100 this actually calls AnimateCharacter(ID - 100)
+static void LegacyAnimateObjectImpl(int obn, int loopn, int spdd, int rept,
+									int direction = 0, int blocking = 0) {
+	if (obn >= LEGACY_ANIMATE_CHARIDBASE) {
+		scAnimateCharacter(obn - LEGACY_ANIMATE_CHARIDBASE, loopn, spdd, rept);
+	} else {
+		AnimateObjectImpl(obn, loopn, spdd, rept, direction, blocking, 0);
+	}
+}
+
 void AnimateObjectEx(int obn, int loopn, int spdd, int rept, int direction, int blocking) {
-	AnimateObjectImpl(obn, loopn, spdd, rept, direction, blocking, 0);
+	LegacyAnimateObjectImpl(obn, loopn, spdd, rept, direction, blocking);
 }
 
 void AnimateObject(int obn, int loopn, int spdd, int rept) {
-	AnimateObjectImpl(obn, loopn, spdd, rept, 0, 0, 0);
+	LegacyAnimateObjectImpl(obn, loopn, spdd, rept, 0, 0);
 }
 
 void MergeObject(int obn) {
diff --git a/engines/ags/engine/ac/runtime_defines.h b/engines/ags/engine/ac/runtime_defines.h
index c43430952a3..7c9132b3925 100644
--- a/engines/ags/engine/ac/runtime_defines.h
+++ b/engines/ags/engine/ac/runtime_defines.h
@@ -145,7 +145,9 @@ const int LegacyRoomVolumeFactor = 30;
 // Bit mask for packing skip key/button data into result
 #define SKIP_RESULT_DATA_MASK  0x00FFFFFF
 
-#define MANOBJNUM 99
+// The index base for characters, used in legacy AnimateObject script function;
+// if passed ID is eq or gt than this, then a Character is animated instead
+#define LEGACY_ANIMATE_CHARIDBASE 100
 
 #define STD_BUFFER_SIZE 3000
 


Commit: 0c3fdb6945e16d4c645fbc546eb44cf38209c558
    https://github.com/scummvm/scummvm/commit/0c3fdb6945e16d4c645fbc546eb44cf38209c558
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: enhanced arg order in CreateClearBitmap() for consistency

Partially from upstream 5518f00ba54680d9e314d192f4afc433519f2afa

Changed paths:
    engines/ags/shared/gfx/bitmap.cpp
    engines/ags/shared/gfx/bitmap.h


diff --git a/engines/ags/shared/gfx/bitmap.cpp b/engines/ags/shared/gfx/bitmap.cpp
index 8e046b6ebe3..ec57457fdf6 100644
--- a/engines/ags/shared/gfx/bitmap.cpp
+++ b/engines/ags/shared/gfx/bitmap.cpp
@@ -38,7 +38,7 @@ Bitmap *CreateBitmap(int width, int height, int color_depth) {
 	return bitmap;
 }
 
-Bitmap *CreateClearBitmap(int width, int height, int clear_color, int color_depth) {
+Bitmap *CreateClearBitmap(int width, int height, int color_depth, int clear_color) {
 	Bitmap *bitmap = new Bitmap();
 	if (!bitmap->Create(width, height, color_depth)) {
 		delete bitmap;
diff --git a/engines/ags/shared/gfx/bitmap.h b/engines/ags/shared/gfx/bitmap.h
index cae8ac39449..acc6c247f2b 100644
--- a/engines/ags/shared/gfx/bitmap.h
+++ b/engines/ags/shared/gfx/bitmap.h
@@ -62,7 +62,7 @@ namespace BitmapHelper {
 // Helper functions, that delete faulty bitmaps automatically, and return
 // NULL if bitmap could not be created.
 Bitmap *CreateBitmap(int width, int height, int color_depth = 0);
-Bitmap *CreateClearBitmap(int width, int height, int clear_color = 0, int color_depth = 0);
+Bitmap *CreateClearBitmap(int width, int height, int color_depth = 0, int clear_color = 0);
 Bitmap *CreateTransparentBitmap(int width, int height, int color_depth = 0);
 Bitmap *CreateSubBitmap(Bitmap *src, const Rect &rc);
 Bitmap *CreateBitmapCopy(Bitmap *src, int color_depth = 0);


Commit: 8a5a290ea5e9fc4a4ab12f73ecb5a8a839bed279
    https://github.com/scummvm/scummvm/commit/8a5a290ea5e9fc4a4ab12f73ecb5a8a839bed279
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: added more comments to Bitmap functions

Partially from upstream 9a1221e7c11326c6ae469285f62dbe3a0a1e4c32

Changed paths:
    engines/ags/shared/gfx/allegro_bitmap.h
    engines/ags/shared/gfx/bitmap.h


diff --git a/engines/ags/shared/gfx/allegro_bitmap.h b/engines/ags/shared/gfx/allegro_bitmap.h
index 62e184490de..87909d4afa0 100644
--- a/engines/ags/shared/gfx/allegro_bitmap.h
+++ b/engines/ags/shared/gfx/allegro_bitmap.h
@@ -49,19 +49,27 @@ public:
 	Bitmap(BITMAP *al_bmp, bool shared_data);
 	~Bitmap();
 
-	// Allocate new bitmap
-	// CHECKME: color_depth = 0 is used to call Allegro's create_bitmap, which uses
+	// Allocate new bitmap.
+	// NOTE: color_depth is in BITS per pixel (i.e. 8, 16, 24, 32...).
+	// NOTE: in all of these color_depth may be passed as 0 in which case a default
+	// color depth will be used (as previously set for the system).
+	// TODO: color_depth = 0 is used to call Allegro's create_bitmap, which uses
 	// some global color depth setting; not sure if this is OK to use for generic class,
 	// revise this in future
 	bool    Create(int width, int height, int color_depth = 0);
+	// Create Bitmap and clear to transparent color
 	bool    CreateTransparent(int width, int height, int color_depth = 0);
-	// Allow this object to share existing bitmap data
+	// Creates a sub-bitmap of the given bitmap; the sub-bitmap is a reference to
+	// particular region inside a parent.
+	// WARNING: the parent bitmap MUST be kept in memory for as long as sub-bitmap exists!
 	bool    CreateSubBitmap(Bitmap *src, const Rect &rc);
 	// Resizes existing sub-bitmap within the borders of its parent
 	bool    ResizeSubBitmap(int width, int height);
-	// Create a copy of given bitmap
+	// Creates a plain copy of the given bitmap, optionally converting to a different color depth;
+	// pass color depth 0 to keep the original one.
 	bool    CreateCopy(Bitmap *src, int color_depth = 0);
-	// TODO: a temporary solution for plugin support
+	// TODO: this is a temporary solution for plugin support
+	// Wraps a raw allegro BITMAP object, optionally owns it (will delete on disposal)
 	bool    WrapAllegroBitmap(BITMAP *al_bmp, bool shared_data);
 	// Deallocate bitmap
 	void    Destroy();
diff --git a/engines/ags/shared/gfx/bitmap.h b/engines/ags/shared/gfx/bitmap.h
index acc6c247f2b..6f606dce6dc 100644
--- a/engines/ags/shared/gfx/bitmap.h
+++ b/engines/ags/shared/gfx/bitmap.h
@@ -61,11 +61,24 @@ namespace BitmapHelper {
 
 // Helper functions, that delete faulty bitmaps automatically, and return
 // NULL if bitmap could not be created.
+// NOTE: color_depth is in BITS per pixel (i.e. 8, 16, 24, 32...).
+// NOTE: in all of these color_depth may be passed as 0 in which case a default
+// color depth will be used (as previously set for the system).
+// Creates a new bitmap of the given format; the pixel contents are undefined.
 Bitmap *CreateBitmap(int width, int height, int color_depth = 0);
+// Creates a new bitmap and clears it with the given color
 Bitmap *CreateClearBitmap(int width, int height, int color_depth = 0, int clear_color = 0);
+// Creates a new bitmap and clears it with the transparent color
 Bitmap *CreateTransparentBitmap(int width, int height, int color_depth = 0);
+// Creates a sub-bitmap of the given bitmap; the sub-bitmap is a reference to
+// particular region inside a parent.
+// WARNING: the parent bitmap MUST be kept in memory for as long as sub-bitmap exists!
 Bitmap *CreateSubBitmap(Bitmap *src, const Rect &rc);
+// Creates a plain copy of the given bitmap, optionally converting to a different color depth;
+// pass color depth 0 to keep the original one.
 Bitmap *CreateBitmapCopy(Bitmap *src, int color_depth = 0);
+
+// Load a bitmap from file; supported formats currently are: BMP, PCX.
 Bitmap *LoadFromFile(const char *filename);
 inline Bitmap *LoadFromFile(const String &filename) {
 	return LoadFromFile(filename.GetCStr());


Commit: 7d7f9a3b42bb324ed3c2c172cdce232a11f13b93
    https://github.com/scummvm/scummvm/commit/7d7f9a3b42bb324ed3c2c172cdce232a11f13b93
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: fix font parameters were used and checked for null later

>From upstream a746ae013e8874c816cedf24c89452c25fc189a0

Changed paths:
    engines/ags/shared/font/ttf_font_renderer.cpp


diff --git a/engines/ags/shared/font/ttf_font_renderer.cpp b/engines/ags/shared/font/ttf_font_renderer.cpp
index 4ece2d2e0be..a137d4f90ac 100644
--- a/engines/ags/shared/font/ttf_font_renderer.cpp
+++ b/engines/ags/shared/font/ttf_font_renderer.cpp
@@ -115,16 +115,18 @@ bool TTFFontRenderer::LoadFromDiskEx(int fontNumber, int fontSize,
 	String filename = String::FromFormat("agsfnt%d.ttf", fontNumber);
 	if (fontSize <= 0)
 		fontSize = 8; // compatibility fix
-	if (params && params->SizeMultiplier > 1)
-		fontSize *= params->SizeMultiplier;
+	assert(params);
+	FontRenderParams f_params = params ? *params : FontRenderParams();
+	if (f_params.SizeMultiplier > 1)
+		fontSize *= f_params.SizeMultiplier;
 
 	ALFONT_FONT *alfptr = LoadTTF(filename, fontSize,
-		GetAlfontFlags(params->LoadMode));
+		GetAlfontFlags(f_params.LoadMode));
 	if (!alfptr)
 		return false;
 
 	_fontData[fontNumber].AlFont = alfptr;
-	_fontData[fontNumber].Params = params ? *params : FontRenderParams();
+	_fontData[fontNumber].Params = f_params;
 	if (metrics)
 		FillMetrics(alfptr, metrics);
 	return true;


Commit: 7b3ca8febf915bae91218a227375a6586eebca49
    https://github.com/scummvm/scummvm/commit/7b3ca8febf915bae91218a227375a6586eebca49
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: simplify PlayMP3File asset loading

>From upstream 3653e184243e0eb6b559726c6b33b1e55f52d1f4

Changed paths:
    engines/ags/engine/ac/global_audio.cpp


diff --git a/engines/ags/engine/ac/global_audio.cpp b/engines/ags/engine/ac/global_audio.cpp
index ade07b0a3d1..fbdd1cde77f 100644
--- a/engines/ags/engine/ac/global_audio.cpp
+++ b/engines/ags/engine/ac/global_audio.cpp
@@ -374,13 +374,11 @@ void PlayMP3File(const char *filename) {
 	int useChan = prepare_for_new_music();
 	bool doLoop = (_GP(play).music_repeat > 0);
 
-	SOUNDCLIP *clip = nullptr;
+	SOUNDCLIP *clip = my_load_ogg(asset_name, doLoop);
 	int sound_type = 0;
 
-	if (!clip) {
-		clip = my_load_ogg(asset_name, doLoop);
+	if (clip)
 		sound_type = MUS_OGG;
-	}
 
 	if (!clip) {
 		clip = my_load_mp3(asset_name, doLoop);


Commit: 7d7a6c6b4eed3f2e1a70f21369c83bafb04c0d33
    https://github.com/scummvm/scummvm/commit/7d7a6c6b4eed3f2e1a70f21369c83bafb04c0d33
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: improvements to ValidateWindowSize()

Screen limits are hardcoded in ScummVM's implementation, so this function
does not really validate anything
Partially from upstream cbe729f93c9b204db41ddd1b28c455d4a98ff703

Changed paths:
    engines/ags/engine/main/graphics_mode.cpp
    engines/ags/engine/platform/base/ags_platform_driver.cpp
    engines/ags/engine/platform/base/ags_platform_driver.h
    engines/ags/shared/util/geometry.h


diff --git a/engines/ags/engine/main/graphics_mode.cpp b/engines/ags/engine/main/graphics_mode.cpp
index cf3d1a89bd9..d09c5847cfd 100644
--- a/engines/ags/engine/main/graphics_mode.cpp
+++ b/engines/ags/engine/main/graphics_mode.cpp
@@ -58,7 +58,7 @@ Size get_desktop_size() {
 Size get_max_display_size(bool windowed) {
 	Size device_size = get_desktop_size();
 	if (windowed)
-		_G(platform)->ValidateWindowSize(device_size.Width, device_size.Height, false);
+		device_size = _G(platform)->ValidateWindowSize(device_size, false);
 	return device_size;
 }
 
diff --git a/engines/ags/engine/platform/base/ags_platform_driver.cpp b/engines/ags/engine/platform/base/ags_platform_driver.cpp
index 7fc4158252a..76952376905 100644
--- a/engines/ags/engine/platform/base/ags_platform_driver.cpp
+++ b/engines/ags/engine/platform/base/ags_platform_driver.cpp
@@ -92,7 +92,11 @@ void AGSPlatformDriver::AdjustWindowStyleForFullscreen() {
 void AGSPlatformDriver::AdjustWindowStyleForWindowed() {
 }
 
-void AGSPlatformDriver::ValidateWindowSize(int & /*x*/, int & /*y*/, bool /*borderless*/) const {
+Size AGSPlatformDriver::ValidateWindowSize(const Size &sz, bool borderless) const {
+	// TODO: ScummVM screen limits are hardcoded to 9999x9999
+	// in sys_get_desktop_resolution
+	Size w(9999,9999);
+	return w;
 }
 
 void AGSPlatformDriver::PlayVideo(const char *name, int skip, int flags) {
diff --git a/engines/ags/engine/platform/base/ags_platform_driver.h b/engines/ags/engine/platform/base/ags_platform_driver.h
index a86efa7839a..b4b85955aec 100644
--- a/engines/ags/engine/platform/base/ags_platform_driver.h
+++ b/engines/ags/engine/platform/base/ags_platform_driver.h
@@ -32,6 +32,7 @@
 #include "ags/engine/ac/date_time.h"
 #include "ags/engine/ac/path_helper.h"
 #include "ags/shared/debugging/output_handler.h"
+#include "ags/shared/util/geometry.h"
 #include "ags/shared/util/ini_util.h"
 #include "ags/lib/allegro/error.h"
 
@@ -167,8 +168,8 @@ struct AGSPlatformDriver
 	// Adjust application window's parameters to suit windowed mode
 	virtual void AdjustWindowStyleForWindowed();
 	virtual int  ConvertKeycodeToScanCode(int keyCode);
-	// Adjust window size to ensure it is in the supported limits
-	virtual void ValidateWindowSize(int &x, int &y, bool /*borderless*/) const;
+	// Adjust window's * client size * to ensure it is in the supported limits
+	virtual Size ValidateWindowSize(const Size &sz, bool borderless) const;
 
 	virtual int  InitializeCDPlayer() = 0;  // return 0 on success
 	virtual int  CDPlayerCommand(int cmdd, int datt) = 0;
diff --git a/engines/ags/shared/util/geometry.h b/engines/ags/shared/util/geometry.h
index 451c8892193..0f7b27ed13c 100644
--- a/engines/ags/shared/util/geometry.h
+++ b/engines/ags/shared/util/geometry.h
@@ -181,6 +181,14 @@ struct Size {
 		return Width < other.Width || (Width == other.Width && Height < other.Height);
 	}
 
+	inline Size operator+(const Size &size) const {
+		return Size(Width + size.Width, Height + size.Height);
+	}
+
+	inline Size operator-(const Size &size) const {
+		return Size(Width - size.Width, Height - size.Height);
+	}
+
 	inline Size operator *(int x) const {
 		return Size(Width * x, Height * x);
 	}


Commit: a80928712dcd9b2e432c68f9602e31c9e0bcf8b0
    https://github.com/scummvm/scummvm/commit/a80928712dcd9b2e432c68f9602e31c9e0bcf8b0
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: restored --gfxfilter support for explicit scaling multipliers

The graphic mode config has now separate options for
window size, game scaling and filter, but command line is
lagging behind, so all the scaling is still set in `--gfxfilter`.
This is a hotfix for the time being, we shall think this over in
the future, maybe introduce more args.

Partially from upstream a28160b24a2f27b59cbb12a1de358cc9ec9c19ef and
f19043d902ff9a7e7d6478f51093ef3c807dd258

Changed paths:
    engines/ags/engine/main/main.cpp


diff --git a/engines/ags/engine/main/main.cpp b/engines/ags/engine/main/main.cpp
index 42a42f31ab2..ef5c195e3b7 100644
--- a/engines/ags/engine/main/main.cpp
+++ b/engines/ags/engine/main/main.cpp
@@ -39,6 +39,7 @@
 #include "ags/shared/util/directory.h"
 #include "ags/shared/util/path.h"
 #include "ags/shared/util/string_compat.h"
+#include "ags/shared/util/string_utils.h"
 #include "ags/globals.h"
 
 namespace AGS3 {
@@ -113,9 +114,11 @@ void main_print_help() {
 #endif
 	                          "  --gfxfilter FILTER [SCALING]\n"
 	                          "                               Request graphics filter. Available options:\n"
-	                          "                                 hqx, linear, none, stdscale\n"
-	                          "                                 (support differs between graphic drivers);\n"
-	                          "                                 scaling is specified by integer number\n"
+							  "                                 none, linear, stdscale\n"
+							  "                               (support may differ between graphic drivers);\n"
+							  "                               Scaling is specified as:\n"
+							  "                                 proportional, round, stretch,\n"
+							  "                                 or an explicit integer multiplier.\n"
 	                          "  --help                       Print this help message and stop\n"
 	                          "  --loadsavedgame FILEPATH     Load savegame on startup\n"
 	                          "  --localuserconf              Read and write user config in the game's \n"
@@ -252,13 +255,21 @@ int main_process_cmdline(ConfigTree &cfg, int argc, const char *argv[]) {
 		else if ((ags_stricmp(arg, "--gfxdriver") == 0) && (argc > ee + 1)) {
 			cfg["graphics"]["driver"] = argv[++ee];
 		} else if ((ags_stricmp(arg, "--gfxfilter") == 0) && (argc > ee + 1)) {
-			// NOTE: we make an assumption here that if user provides scaling factor,
-			// this factor means to be applied to windowed mode only.
 			cfg["graphics"]["filter"] = argv[++ee];
-			if (argc > ee + 1 && argv[ee + 1][0] != '-')
-				cfg["graphics"]["game_scale_win"] = argv[++ee];
-			else
-				cfg["graphics"]["game_scale_win"] = "max_round";
+			if (argc > ee + 1 && argv[ee + 1][0] != '-') {
+				// NOTE: we make an assumption here that if user provides scaling
+				// multiplier, then it's meant to be applied to windowed mode only;
+				// Otherwise the scaling style is applied to both.
+				String scale_value = argv[++ee];
+				int scale_mul = StrUtil::StringToInt(scale_value);
+				if (scale_mul > 0) {
+					cfg["graphics"]["window"] = String::FromFormat("x%d", scale_mul);
+					cfg["graphics"]["game_scale_win"] = "round";
+				} else {
+					cfg["graphics"]["game_scale_fs"] = scale_value;
+					cfg["graphics"]["game_scale_win"] = scale_value;
+				}
+			}
 		} else if ((ags_stricmp(arg, "--translation") == 0) && (argc > ee + 1)) {
 			cfg["language"]["translation"] = argv[++ee];
 		} else if (ags_stricmp(arg, "--no-translation") == 0) {


Commit: 430758510e0cf7afa93397eae4be2db6b3a793c6
    https://github.com/scummvm/scummvm/commit/430758510e0cf7afa93397eae4be2db6b3a793c6
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: center the SDL window ourselves on desktop platforms

This is not implemented and just add a  stub
Partially from upstream 82b19ca27b682c497422ad8eac532ceb88c67e62

Changed paths:
    engines/ags/engine/platform/base/sys_main.cpp
    engines/ags/engine/platform/base/sys_main.h
    engines/ags/shared/core/platform.h


diff --git a/engines/ags/engine/platform/base/sys_main.cpp b/engines/ags/engine/platform/base/sys_main.cpp
index d19fb314858..e8dd865e316 100644
--- a/engines/ags/engine/platform/base/sys_main.cpp
+++ b/engines/ags/engine/platform/base/sys_main.cpp
@@ -155,6 +155,7 @@ SDL_Window *sys_window_create(const char *window_title, int w, int h, WindowMode
 	}
 	flags |= ex_flags;
 #if (AGS_PLATFORM_MOBILE)
+	// Resizable flag is necessary for fullscreen app rotation
 	flags |= SDL_WINDOW_RESIZABLE;
 #endif
 	window = SDL_CreateWindow(
@@ -165,6 +166,15 @@ SDL_Window *sys_window_create(const char *window_title, int w, int h, WindowMode
 		h,
 		flags
 	);
+#if (AGS_PLATFORM_DESKTOP)
+	// CHECKME: this is done because SDL2 has some bug(s) during
+	// centering. See: https://github.com/libsdl-org/SDL/issues/6875
+	// TODO: SDL2 docs mentioned that on some systems the window border size
+	// may be known only after the window is displayed, which means that
+	// this may have to be called with a short delay (but how to know when?)
+	if (mode == kWnd_Windowed)
+		sys_window_center();
+#endif
 	return window;
 }
 #else
@@ -230,6 +240,10 @@ bool sys_window_set_size(int w, int h, bool center) {
 	return false;
 }
 
+void sys_window_center() {
+	// No implementation in ScummVM
+}
+
 #if AGS_PLATFORM_OS_WINDOWS
 void *sys_win_get_window() {
 	if (!window) return nullptr;
diff --git a/engines/ags/engine/platform/base/sys_main.h b/engines/ags/engine/platform/base/sys_main.h
index 1e771b4ace8..ff50b3dceb0 100644
--- a/engines/ags/engine/platform/base/sys_main.h
+++ b/engines/ags/engine/platform/base/sys_main.h
@@ -72,6 +72,8 @@ SDL_Window *sys_get_window();
 void sys_window_set_style(AGS::Engine::WindowMode mode, int ex_flags = 0);
 // Set new window size; optionally center new window on screen
 bool sys_window_set_size(int w, int h, bool center);
+// Centers the window on screen
+void sys_window_center();
 // Shows or hides system cursor when it's in the game window
 void sys_window_show_cursor(bool on);
 // Locks on unlocks mouse inside the window.
diff --git a/engines/ags/shared/core/platform.h b/engines/ags/shared/core/platform.h
index 9f2b6415e22..f783925f87b 100644
--- a/engines/ags/shared/core/platform.h
+++ b/engines/ags/shared/core/platform.h
@@ -137,16 +137,18 @@ namespace AGS3 {
 #define AGS_PLATFORM_DEBUG  (0)
 #endif
 
+#define AGS_PLATFORM_DESKTOP ((AGS_PLATFORM_OS_WINDOWS) || (AGS_PLATFORM_OS_LINUX) || (AGS_PLATFORM_OS_MACOS))
+
 #define AGS_PLATFORM_MOBILE ((AGS_PLATFORM_OS_ANDROID) || (AGS_PLATFORM_OS_IOS))
 
 #define AGS_HAS_DIRECT3D (AGS_PLATFORM_OS_WINDOWS)
-#define AGS_HAS_OPENGL (AGS_PLATFORM_OS_WINDOWS || AGS_PLATFORM_OS_LINUX || AGS_PLATFORM_MOBILE)
+#define AGS_HAS_OPENGL ((AGS_PLATFORM_OS_WINDOWS) || (AGS_PLATFORM_OS_LINUX) || (AGS_PLATFORM_MOBILE))
 #define AGS_OPENGL_ES2 (AGS_PLATFORM_OS_ANDROID)
 
 // Only allow searching around for game data on desktop systems;
 // otherwise use explicit argument either from program wrapper, command-line
 // or read from default config.
-#define AGS_SEARCH_FOR_GAME_ON_LAUNCH (AGS_PLATFORM_OS_WINDOWS || AGS_PLATFORM_OS_LINUX || AGS_PLATFORM_OS_MACOS)
+#define AGS_SEARCH_FOR_GAME_ON_LAUNCH ((AGS_PLATFORM_OS_WINDOWS) || (AGS_PLATFORM_OS_LINUX) || (AGS_PLATFORM_OS_MACOS))
 
 #if !defined(DEBUG_MANAGED_OBJECTS)
 	#define DEBUG_MANAGED_OBJECTS (0)


Commit: 92f0189a0e1506c6769e8640163c07fdd3ca79b9
    https://github.com/scummvm/scummvm/commit/92f0189a0e1506c6769e8640163c07fdd3ca79b9
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: for renderers, picked out SetVsync into base, add SetVsyncImpl

The base class will now take care of not applying vsync when
 not necessary, saving the new state, and anything else generic.
SetVsyncImpl() virtual method will be overridden by each
renderer to provide actual implementation. This also allows
setting vsync to be forced internally.

Partially from upstream 81aa25ab6af683f52ebf5a5054f59824fa8851fc

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.cpp
    engines/ags/engine/gfx/ali_3d_scummvm.h
    engines/ags/engine/gfx/gfx_driver_base.cpp
    engines/ags/engine/gfx/gfx_driver_base.h


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index dc752ddb14a..cebdf43a0dc 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -222,19 +222,18 @@ bool ScummVMRendererGraphicsDriver::DoesSupportVsyncToggle() {
 	return g_system->hasFeature(OSystem::kFeatureVSync);
 }
 
-bool ScummVMRendererGraphicsDriver::SetVsync(bool enabled) {
-	if (_mode.Vsync == enabled) {
-		return _mode.Vsync;
-	}
-
+bool ScummVMRendererGraphicsDriver::SetVsyncImpl(bool enabled, bool &vsync_res) {
 	if (g_system->hasFeature(OSystem::kFeatureVSync)) {
 		g_system->beginGFXTransaction();
 		g_system->setFeatureState(OSystem::kFeatureVSync, enabled);
 		g_system->endGFXTransaction();
 
-		_mode.Vsync = g_system->getFeatureState(OSystem::kFeatureVSync);
+		vsync_res = g_system->getFeatureState(OSystem::kFeatureVSync);
+		if (!vsync_res)
+			Debug::Printf(kDbgMsg_Error, "Renderer: SetVsync (%d) failed", enabled);
+		return vsync_res;
 	}
-	return _mode.Vsync;
+	return false;
 }
 
 int ScummVMRendererGraphicsDriver::GetCompatibleBitmapFormat(int color_depth) {
diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.h b/engines/ags/engine/gfx/ali_3d_scummvm.h
index e01b1a4bb18..eee01388e85 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.h
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.h
@@ -214,7 +214,6 @@ public:
 	void SetGamma(int newGamma) override;
 	void UseSmoothScaling(bool /*enabled*/) override {}
 	bool DoesSupportVsyncToggle() override;
-	bool SetVsync(bool enabled) override;
 	void RenderSpritesAtScreenResolution(bool /*enabled*/, int /*supersampling*/) override {}
 	bool RequiresFullRedrawEachFrame() override {
 		return false;
@@ -238,6 +237,7 @@ public:
 	void SetGraphicsFilter(PSDLRenderFilter filter);
 
 protected:
+	bool SetVsyncImpl(bool vsync, bool &vsync_res) override;
 	size_t GetLastDrawEntryIndex() override {
 		return _spriteList.size();
 	}
diff --git a/engines/ags/engine/gfx/gfx_driver_base.cpp b/engines/ags/engine/gfx/gfx_driver_base.cpp
index 8d49f822bb9..8ab26bd3b6a 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.cpp
+++ b/engines/ags/engine/gfx/gfx_driver_base.cpp
@@ -64,6 +64,18 @@ Rect GraphicsDriverBase::GetRenderDestination() const {
 	return _dstRect;
 }
 
+bool GraphicsDriverBase::SetVsync(bool enabled) {
+	if (_mode.Vsync == enabled) {
+		return _mode.Vsync;
+	}
+
+	bool new_value;
+	if (SetVsyncImpl(enabled, new_value)) {
+		_mode.Vsync = new_value;
+	}
+	return _mode.Vsync;
+}
+
 void GraphicsDriverBase::BeginSpriteBatch(const Rect &viewport, const SpriteTransform &transform,
 	GraphicFlip flip, PBitmap surface) {
 	_spriteBatchDesc.push_back(SpriteBatchDesc(_actSpriteBatch, viewport, transform, flip, surface));
diff --git a/engines/ags/engine/gfx/gfx_driver_base.h b/engines/ags/engine/gfx/gfx_driver_base.h
index e7abea45d57..cb637b9efb8 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.h
+++ b/engines/ags/engine/gfx/gfx_driver_base.h
@@ -101,6 +101,8 @@ public:
 	Size        GetNativeSize() const override;
 	Rect        GetRenderDestination() const override;
 
+	bool SetVsync(bool enabled) override;
+
 	void        BeginSpriteBatch(const Rect &viewport, const SpriteTransform &transform,
 	                             Shared::GraphicFlip flip = Shared::kFlip_None, PBitmap surface = nullptr) override;
 	void        EndSpriteBatch() override;
@@ -141,6 +143,11 @@ protected:
 	virtual void OnSetRenderFrame(const Rect &dst_rect);
 	// Called when the new filter is set
 	virtual void OnSetFilter();
+
+	// Try changing vsync setting; fills new current mode in vsync_res,
+	// returns whether the new setting was set successfully.
+	virtual bool SetVsyncImpl(bool vsync, bool &vsync_res) { return false; }
+
 	// Initialize sprite batch and allocate necessary resources
 	virtual void InitSpriteBatch(size_t index, const SpriteBatchDesc &desc) = 0;
 	// Gets the index of a last draw entry (sprite)


Commit: 334e34407bff3b4bf24f02e80fc48dedf75877e0
    https://github.com/scummvm/scummvm/commit/334e34407bff3b4bf24f02e80fc48dedf75877e0
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: try to remember when vsync is unsupported / failed

Partially from upstream 5a867c211abf3e288259e8103b2f948eb354aac4

Changed paths:
    engines/ags/engine/gfx/ali_3d_scummvm.cpp
    engines/ags/engine/gfx/gfx_driver_base.cpp
    engines/ags/engine/gfx/gfx_driver_base.h
    engines/ags/engine/main/graphics_mode.cpp


diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index cebdf43a0dc..70740075441 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -117,6 +117,7 @@ bool ScummVMRendererGraphicsDriver::SetDisplayMode(const DisplayMode &mode) {
 	if (!IsModeSupported(mode))
 		return false;
 
+	_capsVsync = true; // reset vsync flag, allow to try setting again
 	const int driver = GFX_SCUMMVM;
 	if (set_gfx_mode(driver, mode.Width, mode.Height, mode.ColorDepth) != 0)
 		return false;
@@ -126,6 +127,10 @@ bool ScummVMRendererGraphicsDriver::SetDisplayMode(const DisplayMode &mode) {
 		g_system->setFeatureState(OSystem::kFeatureVSync, mode.Vsync);
 		g_system->endGFXTransaction();
 	}
+	else {
+		_capsVsync = false;
+		Debug::Printf(kDbgMsg_Warn, "WARNING: Vertical sync is not supported. Setting will be kept at driver default.");
+	}
 
 	OnInit();
 	OnModeSet(mode);
@@ -230,7 +235,7 @@ bool ScummVMRendererGraphicsDriver::SetVsyncImpl(bool enabled, bool &vsync_res)
 
 		vsync_res = g_system->getFeatureState(OSystem::kFeatureVSync);
 		if (!vsync_res)
-			Debug::Printf(kDbgMsg_Error, "Renderer: SetVsync (%d) failed", enabled);
+			Debug::Printf(kDbgMsg_Warn, "Renderer: SetVsync (%d) failed", enabled);
 		return vsync_res;
 	}
 	return false;
diff --git a/engines/ags/engine/gfx/gfx_driver_base.cpp b/engines/ags/engine/gfx/gfx_driver_base.cpp
index 8ab26bd3b6a..2edabb7f443 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.cpp
+++ b/engines/ags/engine/gfx/gfx_driver_base.cpp
@@ -23,6 +23,7 @@
 #include "ags/engine/gfx/gfxfilter.h"
 #include "ags/engine/gfx/gfx_driver_base.h"
 #include "ags/engine/gfx/gfx_util.h"
+#include "ags/shared/debugging/out.h"
 
 namespace AGS3 {
 
@@ -65,14 +66,19 @@ Rect GraphicsDriverBase::GetRenderDestination() const {
 }
 
 bool GraphicsDriverBase::SetVsync(bool enabled) {
-	if (_mode.Vsync == enabled) {
+	if (!_capsVsync || (_mode.Vsync == enabled)) {
 		return _mode.Vsync;
 	}
 
-	bool new_value;
-	if (SetVsyncImpl(enabled, new_value)) {
+	bool new_value = true;
+	if (SetVsyncImpl(enabled, new_value) && new_value == enabled) {
+		Debug::Printf("SetVsync: switched to %d", new_value);
 		_mode.Vsync = new_value;
 	}
+	else {
+		Debug::Printf("SetVsync: failed, stay at %d", new_value);
+		_capsVsync = false; // mark as non-capable (at least in current mode)
+	}
 	return _mode.Vsync;
 }
 
@@ -107,6 +113,8 @@ void GraphicsDriverBase::OnUnInit() {
 
 void GraphicsDriverBase::OnModeSet(const DisplayMode &mode) {
 	_mode = mode;
+	// Adjust some generic parameters as necessary
+	_mode.Vsync &= _capsVsync;
 }
 
 void GraphicsDriverBase::OnModeReleased() {
diff --git a/engines/ags/engine/gfx/gfx_driver_base.h b/engines/ags/engine/gfx/gfx_driver_base.h
index cb637b9efb8..bd5c2dd1ce0 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.h
+++ b/engines/ags/engine/gfx/gfx_driver_base.h
@@ -164,6 +164,9 @@ protected:
 	Rect                _filterRect;    // filter scaling destination rect (before final scaling)
 	PlaneScaling        _scaling;       // native -> render dest coordinate transformation
 
+	// Capability flags
+	bool				_capsVsync = false; // is vsync available
+
 	// Callbacks
 	GFXDRV_CLIENTCALLBACK _pollingCallback;
 	GFXDRV_CLIENTCALLBACK _drawScreenCallback;
diff --git a/engines/ags/engine/main/graphics_mode.cpp b/engines/ags/engine/main/graphics_mode.cpp
index d09c5847cfd..94d03c42ce7 100644
--- a/engines/ags/engine/main/graphics_mode.cpp
+++ b/engines/ags/engine/main/graphics_mode.cpp
@@ -389,7 +389,8 @@ bool graphics_mode_init_any(const GraphicResolution &game_res, const DisplayMode
 		setup.Windowed ? "yes" : "no",
 		ws.Size.Width, ws.Size.Height,
 		scale_option.GetCStr());
-
+	Debug::Printf(kDbgMsg_Info, "Graphic settings: refresh rate (optional): %d, vsync: %d",
+				  setup.Params.RefreshRate, setup.Params.VSync);
 	// Prepare the list of available gfx factories, having the one requested by user at first place
 	// TODO: make factory & driver IDs case-insensitive!
 	StringV ids;
@@ -472,6 +473,7 @@ bool graphics_mode_set_dm(const DisplayMode &dm) {
 	Debug::Printf(kDbgMsg_Info, "Graphics mode set: %d x %d (%d-bit) %s",
 		rdm.Width, rdm.Height, rdm.ColorDepth,
 		rdm.IsWindowed() ? "windowed" : (rdm.IsRealFullscreen() ? "fullscreen" : "fullscreen desktop"));
+	Debug::Printf(kDbgMsg_Info, "Graphics mode set: refresh rate (optional): %d, vsync: %d", rdm.RefreshRate, rdm.Vsync);
 	return true;
 }
 


Commit: 863a3c485a01db07f9746b0b33f2dbfd9c4a9837
    https://github.com/scummvm/scummvm/commit/863a3c485a01db07f9746b0b33f2dbfd9c4a9837
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: push SDL_KEYUP when simulating a keypress

This fixes internal mod key counter not being reset, which
may break service key combos (alt+ctrl, and so forth).
>From upstream 142e1aaa11e7de2995ddfd24c3f1df0ae6c1b072

Changed paths:
    engines/ags/engine/ac/sys_events.cpp


diff --git a/engines/ags/engine/ac/sys_events.cpp b/engines/ags/engine/ac/sys_events.cpp
index 8c5a5182b8f..bf95b401e7b 100644
--- a/engines/ags/engine/ac/sys_events.cpp
+++ b/engines/ags/engine/ac/sys_events.cpp
@@ -99,6 +99,8 @@ void ags_simulate_keypress(eAGSKeyCode ags_key) {
 	e.kbd.ascii = (e.kbd.keycode >= 32 && e.kbd.keycode <= 127) ? e.kbd.keycode : 0;
 
 	::AGS::g_events->pushKeyboardEvent(e);
+	e.type = Common::EVENT_KEYUP;
+	::AGS::g_events->pushKeyboardEvent(e);
 }
 
 // ----------------------------------------------------------------------------


Commit: 2db380f1d1c52ac90986ac6261e46e256a2ee92b
    https://github.com/scummvm/scummvm/commit/2db380f1d1c52ac90986ac6261e46e256a2ee92b
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: save vsync property when the result is known

>From upstream 178a25b4384c3c96998fc6750a3bc0b4ebda3e5d

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/system.cpp
    engines/ags/engine/ac/system.h
    engines/ags/engine/gfx/gfx_driver_base.cpp
    engines/ags/engine/gfx/gfx_driver_base.h
    engines/ags/engine/gfx/graphics_driver.h


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index c4ae2b6594c..719ef51a12d 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -690,7 +690,10 @@ void render_to_screen() {
 	// Stage: engine overlay
 	construct_engine_overlay();
 
-	_G(gfxDriver)->SetVsync(_GP(scsystem).vsync > 0);
+	// Try set new vsync value, and remember the actual result
+	bool new_vsync = _G(gfxDriver)->SetVsync(_GP(scsystem).vsync > 0);
+	if (new_vsync != _GP(scsystem).vsync)
+		System_SetVSyncInternal(new_vsync);
 
 	bool succeeded = false;
 	while (!succeeded && !_G(want_exit) && !_G(abort_engine)) {
diff --git a/engines/ags/engine/ac/system.cpp b/engines/ags/engine/ac/system.cpp
index 4561b52d9d3..ddee4daff7a 100644
--- a/engines/ags/engine/ac/system.cpp
+++ b/engines/ags/engine/ac/system.cpp
@@ -128,11 +128,15 @@ int System_GetVsync() {
 
 void System_SetVsync(int newValue) {
 	if (_G(gfxDriver)->DoesSupportVsyncToggle()) {
-		_GP(scsystem).vsync = newValue;
-		_GP(usetup).Screen.Params.VSync = newValue != 0;
+		System_SetVSyncInternal(newValue != 0);
 	}
 }
 
+void System_SetVSyncInternal(bool vsync) {
+	_GP(scsystem).vsync = vsync;
+	_GP(usetup).Screen.Params.VSync = vsync;
+}
+
 int System_GetWindowed() {
 	return _GP(scsystem).windowed;
 }
diff --git a/engines/ags/engine/ac/system.h b/engines/ags/engine/ac/system.h
index f2cffe4ef8c..4b1f74e6038 100644
--- a/engines/ags/engine/ac/system.h
+++ b/engines/ags/engine/ac/system.h
@@ -39,6 +39,7 @@ int     System_GetCapsLock();
 int     System_GetScrollLock();
 int     System_GetVsync();
 void    System_SetVsync(int newValue);
+void    System_SetVSyncInternal(bool vsync);
 int     System_GetWindowed();
 int     System_GetSupportsGammaControl();
 int     System_GetGamma();
diff --git a/engines/ags/engine/gfx/gfx_driver_base.cpp b/engines/ags/engine/gfx/gfx_driver_base.cpp
index 2edabb7f443..99b55f9c19c 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.cpp
+++ b/engines/ags/engine/gfx/gfx_driver_base.cpp
@@ -82,6 +82,10 @@ bool GraphicsDriverBase::SetVsync(bool enabled) {
 	return _mode.Vsync;
 }
 
+bool GraphicsDriverBase::GetVsync() const {
+	return _mode.Vsync;
+}
+
 void GraphicsDriverBase::BeginSpriteBatch(const Rect &viewport, const SpriteTransform &transform,
 	GraphicFlip flip, PBitmap surface) {
 	_spriteBatchDesc.push_back(SpriteBatchDesc(_actSpriteBatch, viewport, transform, flip, surface));
diff --git a/engines/ags/engine/gfx/gfx_driver_base.h b/engines/ags/engine/gfx/gfx_driver_base.h
index bd5c2dd1ce0..595b74220e9 100644
--- a/engines/ags/engine/gfx/gfx_driver_base.h
+++ b/engines/ags/engine/gfx/gfx_driver_base.h
@@ -101,7 +101,8 @@ public:
 	Size        GetNativeSize() const override;
 	Rect        GetRenderDestination() const override;
 
-	bool SetVsync(bool enabled) override;
+	bool		SetVsync(bool enabled) override;
+	bool		GetVsync() const override;
 
 	void        BeginSpriteBatch(const Rect &viewport, const SpriteTransform &transform,
 	                             Shared::GraphicFlip flip = Shared::kFlip_None, PBitmap surface = nullptr) override;
diff --git a/engines/ags/engine/gfx/graphics_driver.h b/engines/ags/engine/gfx/graphics_driver.h
index 4701e5e94fe..c47f4bb119f 100644
--- a/engines/ags/engine/gfx/graphics_driver.h
+++ b/engines/ags/engine/gfx/graphics_driver.h
@@ -189,8 +189,10 @@ public:
 	virtual bool GetCopyOfScreenIntoBitmap(Shared::Bitmap *destination, bool at_native_res, GraphicResolution *want_fmt = nullptr) = 0;
 	// Tells if the renderer supports toggling vsync after initializing the mode.
 	virtual bool DoesSupportVsyncToggle() = 0;
-	// Toggles vertical sync mode, if renderer supports one; returns the new state.
+	// Toggles vertical sync mode, if renderer supports one; returns the *new state*.
 	virtual bool SetVsync(bool enabled) = 0;
+	// Tells if the renderer currently has vsync enabled.
+	virtual bool GetVsync() const = 0;
 	// Enables or disables rendering mode that draws sprite list directly into
 	// the final resolution, as opposed to drawing to native-resolution buffer
 	// and scaling to final frame. The effect may be that sprites that are


Commit: 84187b3bea81174611b9f55d672f5e1e748c6411
    https://github.com/scummvm/scummvm/commit/84187b3bea81174611b9f55d672f5e1e748c6411
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Updated build version (3.6.0.45)

Partially from upstream cdb0b7fef561aa5badc3522342e2f0b1d20d0b4d

Changed paths:
    engines/ags/shared/core/def_version.h


diff --git a/engines/ags/shared/core/def_version.h b/engines/ags/shared/core/def_version.h
index d61045e4a03..c70c67378e7 100644
--- a/engines/ags/shared/core/def_version.h
+++ b/engines/ags/shared/core/def_version.h
@@ -22,9 +22,9 @@
 #ifndef AGS_SHARED_CORE_DEFVERSION_H
 #define AGS_SHARED_CORE_DEFVERSION_H
 
-#define ACI_VERSION_STR      "3.6.0.44"
+#define ACI_VERSION_STR      "3.6.0.45"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3.6.0.44
+#define ACI_VERSION_MSRC_DEF  3.6.0.45
 #endif
 
 #define SPECIAL_VERSION ""


Commit: c537416279b0ac25768763c9b573545ef988b03f
    https://github.com/scummvm/scummvm/commit/c537416279b0ac25768763c9b573545ef988b03f
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: renamed WalkArea.Light to PlayerView, matching its real purpose

Apparently, this field was used to store light level of the
walkable area in pre-2.55 engines, prior to introduction of the
Regions.
In current engine, when loading games that old, this value is
copied into Regions for backwards compatibility.
While in newer games it actually stores "player character
view" override.

Renamed update_shadow_areas() into update_player_view(),
and tidied the code for clarity.

>From upstream 71e7dc880b4496dd6937cec00a56f0de66137482

Changed paths:
    engines/ags/engine/main/update.cpp
    engines/ags/shared/game/room_file.cpp
    engines/ags/shared/game/room_struct.cpp
    engines/ags/shared/game/room_struct.h


diff --git a/engines/ags/engine/main/update.cpp b/engines/ags/engine/main/update.cpp
index b8c74266053..3db3dfd1390 100644
--- a/engines/ags/engine/main/update.cpp
+++ b/engines/ags/engine/main/update.cpp
@@ -184,17 +184,22 @@ void update_cycling_views() {
 	}
 }
 
-void update_shadow_areas() {
-	// shadow areas
+// Updates the view of the player character
+void update_player_view() {
+	if (_G(playerchar)->flags & CHF_FIXVIEW)
+		return; // view is locked
+
 	int onwalkarea = get_walkable_area_at_character(_GP(game).playercharacter);
-	if (onwalkarea < 0);
-	else if (_G(playerchar)->flags & CHF_FIXVIEW);
-	else {
-		onwalkarea = _GP(thisroom).WalkAreas[onwalkarea].Light;
-		if (onwalkarea > 0) _G(playerchar)->view = onwalkarea - 1;
-		else if (_GP(thisroom).Options.PlayerView == 0) _G(playerchar)->view = _G(playerchar)->defview;
-		else _G(playerchar)->view = _GP(thisroom).Options.PlayerView - 1;
-	}
+	if (onwalkarea < 0)
+		return; // error?
+
+	int areaview = _GP(thisroom).WalkAreas[onwalkarea].PlayerView;
+	if (areaview > 0)
+		_G(playerchar)->view = areaview - 1; // convert to 0-based id
+	else if (_GP(thisroom).Options.PlayerView > 0)
+		_G(playerchar)->view = _GP(thisroom).Options.PlayerView - 1; // convert to 0-based id
+	else
+		_G(playerchar)->view = _G(playerchar)->defview;
 }
 
 void update_character_move_and_anim(std::vector<int> &followingAsSheep) {
@@ -428,7 +433,7 @@ void update_stuff() {
 
 	_G(our_eip) = 21;
 
-	update_shadow_areas();
+	update_player_view();
 
 	_G(our_eip) = 22;
 
diff --git a/engines/ags/shared/game/room_file.cpp b/engines/ags/shared/game/room_file.cpp
index 5036280879a..df6efd03b7a 100644
--- a/engines/ags/shared/game/room_file.cpp
+++ b/engines/ags/shared/game/room_file.cpp
@@ -219,7 +219,7 @@ HError ReadMainBlock(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
 			room->WalkAreas[i].ScalingFar = in->ReadInt16();
 	if (data_ver >= kRoomVersion_214)
 		for (size_t i = 0; i < room->WalkAreaCount; ++i)
-			room->WalkAreas[i].Light = in->ReadInt16();
+			room->WalkAreas[i].PlayerView = in->ReadInt16();
 	if (data_ver >= kRoomVersion_251) {
 		for (size_t i = 0; i < room->WalkAreaCount; ++i)
 			room->WalkAreas[i].ScalingNear = in->ReadInt16();
@@ -282,8 +282,9 @@ HError ReadMainBlock(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
 	}
 
 	if (data_ver >= kRoomVersion_114) {
+		// NOTE: this WA value was written for the second time here, for some weird reason
 		for (size_t i = 0; i < (size_t)MAX_WALK_AREAS + 1; ++i)
-			room->WalkAreas[i].Light = in->ReadInt16();
+			room->WalkAreas[i].PlayerView = in->ReadInt16();
 	}
 	if (data_ver >= kRoomVersion_255b) {
 		for (size_t i = 0; i < room->RegionCount; ++i)
@@ -504,7 +505,8 @@ HRoomFileError UpdateRoomData(RoomStruct *room, RoomFileVersion data_ver, bool g
 			room->RegionMask.reset(BitmapHelper::CreateBitmap(room->WalkAreaMask->GetWidth(), room->WalkAreaMask->GetHeight(), 8));
 		room->RegionMask->Blit(room->WalkAreaMask.get(), 0, 0, 0, 0, room->RegionMask->GetWidth(), room->RegionMask->GetHeight());
 		for (size_t i = 0; i < MAX_ROOM_REGIONS; ++i) {
-			room->Regions[i].Light = room->WalkAreas[i].Light;
+			// sic!! walkable areas were storing Light level in this field pre-2.55
+			room->Regions[i].Light = room->WalkAreas[i].PlayerView;
 			room->Regions[i].Tint = 255;
 		}
 	}
@@ -701,7 +703,7 @@ void WriteMainBlock(const RoomStruct *room, Stream *out) {
 	for (size_t i = 0; i < (size_t)MAX_WALK_AREAS + 1; ++i)
 		out->WriteInt16(room->WalkAreas[i].ScalingFar);
 	for (size_t i = 0; i < (size_t)MAX_WALK_AREAS + 1; ++i)
-		out->WriteInt16(room->WalkAreas[i].Light);
+		out->WriteInt16(room->WalkAreas[i].PlayerView);
 	for (size_t i = 0; i < (size_t)MAX_WALK_AREAS + 1; ++i)
 		out->WriteInt16(room->WalkAreas[i].ScalingNear);
 	for (size_t i = 0; i < (size_t)MAX_WALK_AREAS + 1; ++i)
@@ -728,8 +730,9 @@ void WriteMainBlock(const RoomStruct *room, Stream *out) {
 
 	out->WriteInt16(0); // legacy room animations
 
+	// NOTE: this WA value was written for the second time here, for some weird reason
 	for (size_t i = 0; i < (size_t)MAX_WALK_AREAS + 1; ++i)
-		out->WriteInt16(room->WalkAreas[i].Light);
+		out->WriteInt16(room->WalkAreas[i].PlayerView);
 	for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i)
 		out->WriteInt16(room->Regions[i].Light);
 	for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i)
diff --git a/engines/ags/shared/game/room_struct.cpp b/engines/ags/shared/game/room_struct.cpp
index d8be7209a94..5d6628deeaa 100644
--- a/engines/ags/shared/game/room_struct.cpp
+++ b/engines/ags/shared/game/room_struct.cpp
@@ -75,7 +75,7 @@ WalkArea::WalkArea()
 	: CharacterView(0)
 	, ScalingFar(0)
 	, ScalingNear(NOT_VECTOR_SCALED)
-	, Light(0)
+	, PlayerView(0)
 	, Top(-1)
 	, Bottom(-1) {
 }
diff --git a/engines/ags/shared/game/room_struct.h b/engines/ags/shared/game/room_struct.h
index 2125751a1c0..089652053a0 100644
--- a/engines/ags/shared/game/room_struct.h
+++ b/engines/ags/shared/game/room_struct.h
@@ -224,8 +224,8 @@ struct WalkArea {
 	int32_t     ScalingFar;
 	// Scaling at the nearest point, or NOT_VECTOR_SCALED for uniform scaling
 	int32_t     ScalingNear;
-	// Light level (-100 -> +100)
-	int32_t     Light;
+	// Optional override for player character view
+	int32_t     PlayerView;
 	// Top and bottom Y of the area
 	int32_t     Top;
 	int32_t     Bottom;


Commit: dedc6e009d4854be4be0803b934f6d4ad2d60377
    https://github.com/scummvm/scummvm/commit/dedc6e009d4854be4be0803b934f6d4ad2d60377
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: a fix for the queued sounds (PlayQueue) starting with a gap

This is a slightly "hacky" way to fix the queued sounds
starting with a small gap after the previous sound ends.
Because of the new audio playback subsystem in AGS 3.6.0,
the clip start timing changed a little, and there appear to be
small gap, about 1-2 game frame long, between previous and
next sound in queue. The queue in AGS is not implemented
ideally, because it relies on being updated once per game
frame, rather than on an audio thread etc, but this is
something we cannot change easily right now (maybe will
reimplement whole thing later).

This commit does 2 things:
1. Sync logical channels with the audio subsystem also *prior*
to updating queue and other things (crossfade etc). This fixes
a 1 game frame queue delay.
2. Additionally, force queued clips to start 1 extra game
frame earlier, by testing current playback position of active clips.

>From upstream e87e3a8d862d39cd6eaab7a6245373fba84c4f78

Changed paths:
    engines/ags/engine/media/audio/audio.cpp


diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index ff0b4da5ffa..7f7604971fd 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -145,7 +145,7 @@ void stop_or_fade_out_channel(int fadeOutChannel, int fadeInChannel, ScriptAudio
 	}
 }
 
-static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool interruptEqualPriority) {
+static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool interruptEqualPriority, bool for_queue = true) {
 	int lowestPrioritySoFar = 9999999;
 	int lowestPriorityID = -1;
 	int channelToUse = -1;
@@ -180,6 +180,19 @@ static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool int
 			lowestPrioritySoFar = ch->_priority;
 			lowestPriorityID = i;
 		}
+		// NOTE: This is a "hack" for starting queued clips;
+		// since having a new audio system (3.6.0 onwards), the audio timing
+		// changed a little, and queued sounds have to start bit earlier
+		// if we want them to sound seamless with the previous clips.
+		// TODO: investigate better solutions? may require reimplementation of the sound queue.
+		if (for_queue && (ch->_sourceClipType == clip->type)) {
+			// try to start queued sounds 1 frame earlier
+			const float trigger_pos = (1000.f / _G(frames_per_second)) * 1.f;
+			if (ch->get_pos_ms() >= (ch->get_length_ms() - trigger_pos)) {
+				lowestPrioritySoFar = priority;
+				lowestPriorityID = i;
+			}
+		}
 	}
 
 	if ((channelToUse < 0) && (lowestPriorityID >= 0) &&
@@ -275,7 +288,7 @@ static void audio_update_polled_stuff() {
 	if (_GP(play).new_music_queue_size > 0) {
 		for (int i = 0; i < _GP(play).new_music_queue_size; i++) {
 			ScriptAudioClip *clip = &_GP(game).audioClips[_GP(play).new_music_queue[i].audioClipIndex];
-			int channel = find_free_audio_channel(clip, clip->defaultPriority, false);
+			int channel = find_free_audio_channel(clip, clip->defaultPriority, false, true);
 			if (channel >= 0) {
 				QueuedAudioItem itemToPlay = _GP(play).new_music_queue[i];
 
@@ -409,7 +422,7 @@ ScriptAudioChannel *play_audio_clip(ScriptAudioClip *clip, int priority, int rep
 	if (repeat == SCR_NO_VALUE)
 		repeat = clip->defaultRepeat;
 
-	int channel = find_free_audio_channel(clip, priority, !queueIfNoChannel);
+	int channel = find_free_audio_channel(clip, priority, !queueIfNoChannel, queueIfNoChannel);
 	if (channel < 0) {
 		if (queueIfNoChannel)
 			queue_audio_clip_to_play(clip, priority, repeat);
@@ -772,6 +785,20 @@ void update_volume_drop_if_voiceover() {
 void update_audio_system_on_game_loop() {
 	update_polled_stuff();
 
+	// Sync logical game channels with the audio backend
+	// NOTE: we update twice, first time here - because we need to know
+	// which clips are still playing before updating the sound transitions
+	// and queues, then second time later - because we need to apply any
+	// changes to channels / parameters.
+	// TODO: investigate options for optimizing this.
+	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) { // update the playing channels, and dispose the finished / invalid ones
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
+		if (ch && !ch->update()) {
+			AudioChans::SetChannel(i, nullptr);
+			delete ch;
+		}
+	}
+
 	process_scheduled_music_update();
 
 	audio_update_polled_stuff();
@@ -806,19 +833,19 @@ void update_audio_system_on_game_loop() {
 		}
 	}
 
-	if (_G(loopcounter) % 5 == 0) {
+	if (_G(loopcounter) % 5 == 0) { // TODO: investigate why we do this each 5 frames?
 		update_ambient_sound_vol();
 		update_directional_sound_vol();
 	}
 
-	// Update and sync logical game channels with the audio backend
+	// Sync logical game channels with the audio backend:
+	// startup new assigned clips, apply changed parameters.
 	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
-		auto *ch = AudioChans::GetChannel(i);
-		if (ch) { // update the playing channel, and if it's finished then dispose it
-			if (ch->is_ready() && !ch->update()) {
-				delete ch;
-				AudioChans::SetChannel(i, nullptr);
-			}
+		// update the playing channels, and dispose the finished / invalid ones
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
+		if (ch && !ch->update()) {
+			AudioChans::SetChannel(i, nullptr);
+			delete ch;
 		}
 	}
 }


Commit: 7808fb52e6196291c51bed470b3730696d2783df
    https://github.com/scummvm/scummvm/commit/7808fb52e6196291c51bed470b3730696d2783df
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: tidied few remaining old functions in Mouse namespace

* minstalled -> Mouse::GetButtonCount();
* msetgraphpos -> Mouse::SetSysPosition() (internal);
* mgetgraphpos -> Mouse::Poll();
* msetcursorlimit -> merged with Mouse::SetMoveLimit();
* msethotspot -> Mouse::SetHotspot().

>From upstream 9211b07f36528776e6fabc34a29405b6d63f6c23

Changed paths:
    engines/ags/engine/ac/mouse.cpp
    engines/ags/engine/ac/sys_events.cpp
    engines/ags/engine/ac/sys_events.h
    engines/ags/engine/device/mouse_w32.cpp
    engines/ags/engine/device/mouse_w32.h
    engines/ags/engine/main/engine.cpp
    engines/ags/globals.h


diff --git a/engines/ags/engine/ac/mouse.cpp b/engines/ags/engine/ac/mouse.cpp
index 19b10d2d5b6..cc6b23d6f60 100644
--- a/engines/ags/engine/ac/mouse.cpp
+++ b/engines/ags/engine/ac/mouse.cpp
@@ -99,7 +99,7 @@ void SetMouseBounds(int x1, int y1, int x2, int y2) {
 // set_mouse_cursor: changes visual appearance to specified cursor
 void set_mouse_cursor(int newcurs) {
 	const int hotspotx = _GP(game).mcurs[newcurs].hotx, hotspoty = _GP(game).mcurs[newcurs].hoty;
-	msethotspot(hotspotx, hotspoty);
+	_GP(mouse).SetHotspot(hotspotx, hotspoty);
 
 	// if it's same cursor and there's animation in progress, then don't assign a new pic just yet
 	if (newcurs == _G(cur_cursor) && _GP(game).mcurs[newcurs].view >= 0 &&
diff --git a/engines/ags/engine/ac/sys_events.cpp b/engines/ags/engine/ac/sys_events.cpp
index bf95b401e7b..7a0d46bc9ce 100644
--- a/engines/ags/engine/ac/sys_events.cpp
+++ b/engines/ags/engine/ac/sys_events.cpp
@@ -199,7 +199,7 @@ void ags_mouse_acquire_relxy(int &x, int &y) {
 }
 
 void ags_domouse() {
-	mgetgraphpos();
+	_GP(mouse).Poll();
 }
 
 int ags_check_mouse_wheel() {
diff --git a/engines/ags/engine/ac/sys_events.h b/engines/ags/engine/ac/sys_events.h
index 9e023e10c9f..6b08c30348a 100644
--- a/engines/ags/engine/ac/sys_events.h
+++ b/engines/ags/engine/ac/sys_events.h
@@ -71,7 +71,7 @@ extern void ags_simulate_keypress(eAGSKeyCode ags_key);
 //
 // Tells if the mouse button is currently down
 extern bool ags_misbuttondown(eAGSMouseButton but);
-// Returns mouse button code
+// Returns last "clicked" mouse button
 extern eAGSMouseButton ags_mgetbutton();
 // Returns recent relative mouse movement; resets accumulated values
 extern void ags_mouse_acquire_relxy(int &x, int &y);
diff --git a/engines/ags/engine/device/mouse_w32.cpp b/engines/ags/engine/device/mouse_w32.cpp
index 848b961a800..e681a187bd8 100644
--- a/engines/ags/engine/device/mouse_w32.cpp
+++ b/engines/ags/engine/device/mouse_w32.cpp
@@ -41,7 +41,7 @@ enum {
 
 // static const int MB_ARRAY[3] = { 1, 2, 4 };
 
-void mgetgraphpos() {
+void Mouse::Poll() {
 	// TODO:
 	// disable or update mouse speed control to sdl
 	// (does sdl support mouse cursor speed? is it even necessary anymore?);
@@ -66,35 +66,29 @@ void mgetgraphpos() {
 		(_G(mousex) < _G(boundx1) || _G(mousey) < _G(boundy1) || _G(mousex) > _G(boundx2) || _G(mousey) > _G(boundy2))) {
 		_G(mousex) = Math::Clamp(_G(mousex), _G(boundx1), _G(boundx2));
 		_G(mousey) = Math::Clamp(_G(mousey), _G(boundy1), _G(boundy2));
-		msetgraphpos(_G(mousex), _G(mousey));
+		_GP(mouse).SetSysPosition(_G(mousex), _G(mousey));
 	}
 	// Convert to virtual coordinates
 	_GP(mouse).WindowToGame(_G(mousex), _G(mousey));
 }
 
-void msetcursorlimit(int x1, int y1, int x2, int y2) {
-	_G(boundx1) = x1;
-	_G(boundy1) = y1;
-	_G(boundx2) = x2;
-	_G(boundy2) = y2;
-}
-
-void msetgraphpos(int xa, int ya) {
-	_G(sys_mouse_x) = xa;
-	_G(sys_mouse_y) = ya;
-	_G(real_mouse_x) = xa;
-	_G(real_mouse_y) = ya;
+void Mouse::SetSysPosition(int x, int y) {
+	_G(sys_mouse_x) = x;
+	_G(sys_mouse_y) = y;
+	_G(real_mouse_x) = x;
+	_G(real_mouse_y) = y;
 	sys_window_set_mouse(_G(real_mouse_x), _G(real_mouse_y));
 }
 
-void msethotspot(int xx, int yy) {
-	_G(hotx) = xx;  // _G(mousex) -= _G(hotx); _G(mousey) -= _G(hoty);
-	_G(hoty) = yy;  // _G(mousex) += _G(hotx); _G(mousey) += _G(hoty);
+void Mouse::SetHotspot(int x, int y) {
+	_G(hotx) = x;
+	_G(hoty) = y;
 }
 
-int minstalled() {
-	// Number of buttons supported
-	return 3;
+int Mouse::GetButtonCount() {
+	// TODO: can SDL tell number of available/supported buttons at all, or whether mouse is present?
+	// this is not that critical, but maybe some game devs would like to detect if player has or not a mouse.
+	return 3; // SDL *theoretically* support 3 mouse buttons, but that does not mean they are physically present...
 }
 
 void Mouse::WindowToGame(int &x, int &y) {
@@ -105,11 +99,15 @@ void Mouse::WindowToGame(int &x, int &y) {
 void Mouse::SetMoveLimit(const Rect &r) {
 	Rect src_r = OffsetRect(r, _GP(play).GetMainViewport().GetLT());
 	Rect dst_r = _GP(GameScaling).ScaleRange(src_r);
-	msetcursorlimit(dst_r.Left, dst_r.Top, dst_r.Right, dst_r.Bottom);
+	_G(boundx1) = dst_r.Left;
+	_G(boundy1) = dst_r.Top;
+	_G(boundx2) = dst_r.Right;
+	_G(boundy2) = dst_r.Bottom;
 }
 
-void Mouse::SetPosition(const Point p) {
-	msetgraphpos(_GP(GameScaling).X.ScalePt(p.X + _GP(play).GetMainViewport().Left), _GP(GameScaling).Y.ScalePt(p.Y + _GP(play).GetMainViewport().Top));
+void Mouse::SetPosition(const Point &p) {
+	_GP(mouse).SetSysPosition(_GP(GameScaling).X.ScalePt(p.X + _GP(play).GetMainViewport().Left),
+							  _GP(GameScaling).Y.ScalePt(p.Y + _GP(play).GetMainViewport().Top));
 }
 
 bool Mouse::IsLockedToWindow() {
diff --git a/engines/ags/engine/device/mouse_w32.h b/engines/ags/engine/device/mouse_w32.h
index 3222e856b1a..20fcd23ca43 100644
--- a/engines/ags/engine/device/mouse_w32.h
+++ b/engines/ags/engine/device/mouse_w32.h
@@ -34,14 +34,6 @@ class Bitmap;
 
 using namespace AGS; // FIXME later
 
-void mgetgraphpos();
-// Sets the area of the game frame (zero-based coordinates) where the mouse cursor is allowed to move;
-// this function was meant to be used to achieve gameplay effect
-void msetcursorlimit(int x1, int y1, int x2, int y2);
-void msetgraphpos(int xa, int ya);
-void msethotspot(int xx, int yy);
-int minstalled();
-
 struct Mouse {
 	// Tells whether mouse was locked to the game window
 	bool LockedToWindow = false;
@@ -59,8 +51,13 @@ struct Mouse {
 	// Actual speed factor (cached)
 	float Speed = 1.f;
 
-
+	// Converts real window coordinates to native game coords
 	void WindowToGame(int &x, int &y);
+	// Sets mouse position in system coordinates, syncs with the real mouse cursor
+	void SetSysPosition(int x, int y);
+
+	// Tells the number of supported mouse buttons
+	int GetButtonCount();
 
 	// Get if mouse is locked to the game window
 	bool IsLockedToWindow();
@@ -80,13 +77,20 @@ struct Mouse {
 	// Get speed factor
 	float GetSpeed();
 
+	// Updates limits of the area inside which the standard OS cursor is not shown;
+	// uses game's main viewport (in native coordinates) to calculate real area on screen
+	void UpdateGraphicArea();
 	// Limits the area where the game cursor can move on virtual screen;
 	// parameter must be in native game coordinates
 	void SetMoveLimit(const Rect &r);
-	// Set actual OS cursor position on screen; parameter must be in native game coordinates
-	void SetPosition(const Point p);
 
-	void UpdateGraphicArea();
+	// Polls the cursor position, updates mousex, mousey
+	void Poll();
+	// Set actual OS cursor position on screen; in native game coordinates
+	void SetPosition(const Point &p);
+	// Sets the relative position of the cursor's hotspot, in native pixels
+	void SetHotspot(int x, int y);
+
 	void SetMovementControl(bool flag);
 };
 
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index f16322bc084..fd47326d716 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -258,7 +258,7 @@ void engine_init_fonts() {
 }
 
 void engine_init_mouse() {
-	int res = minstalled();
+	int res = _GP(mouse).GetButtonCount();
 	if (res < 0)
 		Debug::Printf(kDbgMsg_Info, "Initializing mouse: failed");
 	else
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index ac5f0b5431f..096dd37ab6f 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -1130,7 +1130,7 @@ public:
 	int8 _currentcursor = 0;
 	// virtual mouse cursor coordinates
 	int _mousex = 0, _mousey = 0, _numcurso = -1, _hotx = 0, _hoty = 0;
-	// real mouse coordinates and bounds
+	// real mouse coordinates and bounds (in window coords)
 	int _real_mouse_x = 0, _real_mouse_y = 0;
 	int _boundx1 = 0, _boundx2 = 99999, _boundy1 = 0, _boundy2 = 99999;
 	int8 _ignore_bounds = 0;


Commit: 72888a7a8c3e1cdb8d897872e46a2ebc12b49ff1
    https://github.com/scummvm/scummvm/commit/72888a7a8c3e1cdb8d897872e46a2ebc12b49ff1
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: move cursor and cursor-over-gui updates to the game update fn

This is necessary for the touch-to-mouse emulation to work
properly (and perhaps true touch controls in the future).
The order of updates is a bit wrong historically, where cursor
position and gui control focus is updated later than the
handling of button events. This means, for example, that if the
cursor was positioned over button in game frame 1, then the
mouse click will only trigger button press not later than the frame 2.

Changes:
* moved "cursor over gui" poll from draw_gui_and_overlays()
to update_cursor_over_gui(), call it in the main game update fn.
* moved update_mouse_cursor() call from
construct_game_screen_overlay() to the game update fn
(renamed to update_cursor_view().
* picked out "cursor over location" trigger out of the "render"
function into update_cursor_over_location(), call it explicitly in
the main game update fn.

Effect on user script callbacks:
* the cursor position (and gui focus) will now be updated prior
to `late_repeatedly_execute_always()` callback. Other callbacks
will not be affected, as their relative order won't change
compared with these updates.

>From upstream 23493a681d4f66ae0b1088cddb82241b0bc80dbb

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/inv_window.cpp
    engines/ags/engine/gui/my_listbox.cpp
    engines/ags/engine/gui/my_push_button.cpp
    engines/ags/engine/gui/new_control.cpp
    engines/ags/engine/main/game_run.cpp
    engines/ags/plugins/ags_plugin.cpp


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 719ef51a12d..dd12b18dd2e 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -1988,21 +1988,6 @@ void draw_gui_and_overlays() {
 			gui_ddb->SetAlpha(GfxDef::LegacyTrans255ToAlpha255(gui.Transparency));
 			add_to_sprite_list(gui_ddb, gui.X, gui.Y, gui.ZOrder, false, index);
 		}
-
-		// Poll the GUIs
-		// TODO: move this out of the draw routine into game update!!
-		if (IsInterfaceEnabled()) // only poll if the interface is enabled
-		{
-			for (auto &gui : _GP(guis)) {
-				if (!gui.IsDisplayed()) continue; // not on screen
-				// Don't touch GUI if "GUIs Turn Off When Disabled"
-				if ((_GP(game).options[OPT_DISABLEOFF] == kGuiDis_Off) &&
-					(_G(all_buttons_disabled) >= 0) &&
-					(gui.PopupStyle != kGUIPopupNoAutoRemove))
-					continue;
-				gui.Poll(_G(mousex), _G(mousey));
-			}
-		}
 	}
 
 	// If not adding gui controls as textures, simply move the resulting sprlist to render
@@ -2247,39 +2232,6 @@ void construct_game_scene(bool full_redraw) {
 	_G(gfxDriver)->EndSpriteBatch();
 }
 
-void update_mouse_cursor() {
-	// update mouse position (mousex, mousey)
-	ags_domouse();
-	// update animating mouse cursor
-	if (_GP(game).mcurs[_G(cur_cursor)].view >= 0) {
-		// only on mousemove, and it's not moving
-		if (((_GP(game).mcurs[_G(cur_cursor)].flags & MCF_ANIMMOVE) != 0) &&
-		        (_G(mousex) == _G(lastmx)) && (_G(mousey) == _G(lastmy)));
-		// only on hotspot, and it's not on one
-		else if (((_GP(game).mcurs[_G(cur_cursor)].flags & MCF_HOTSPOT) != 0) &&
-		         (GetLocationType(game_to_data_coord(_G(mousex)), game_to_data_coord(_G(mousey))) == 0))
-			set_new_cursor_graphic(_GP(game).mcurs[_G(cur_cursor)].pic);
-		else if (_G(mouse_delay) > 0) _G(mouse_delay)--;
-		else {
-			int viewnum = _GP(game).mcurs[_G(cur_cursor)].view;
-			int loopnum = 0;
-			if (loopnum >= _GP(views)[viewnum].numLoops)
-				quitprintf("An animating mouse cursor is using view %d which has no loops", viewnum + 1);
-			if (_GP(views)[viewnum].loops[loopnum].numFrames < 1)
-				quitprintf("An animating mouse cursor is using view %d which has no frames in loop %d", viewnum + 1, loopnum);
-
-			_G(mouse_frame)++;
-			if (_G(mouse_frame) >= _GP(views)[viewnum].loops[loopnum].numFrames)
-				_G(mouse_frame) = 0;
-			set_new_cursor_graphic(_GP(views)[viewnum].loops[loopnum].frames[_G(mouse_frame)].pic);
-			_G(mouse_delay) = _GP(views)[viewnum].loops[loopnum].frames[_G(mouse_frame)].speed + _GP(game).mcurs[_G(cur_cursor)].animdelay;
-			CheckViewFrame(viewnum, loopnum, _G(mouse_frame));
-		}
-		_G(lastmx) = _G(mousex);
-		_G(lastmy) = _G(mousey);
-	}
-}
-
 void construct_game_screen_overlay(bool draw_mouse) {
 	const bool full_frame_rend = _G(gfxDriver)->RequiresFullRedrawEachFrame();
 	_G(gfxDriver)->BeginSpriteBatch(_GP(play).GetMainViewport(),
@@ -2289,10 +2241,6 @@ void construct_game_screen_overlay(bool draw_mouse) {
 		_G(gfxDriver)->DrawSprite(AGSE_POSTSCREENDRAW, 0, nullptr);
 	}
 
-	// TODO: find out if it's okay to move cursor animation and state update
-	// to the update loop instead of doing it in the drawing routine
-	update_mouse_cursor();
-
 	// Add mouse cursor pic, and global screen tint effect
 	if (_GP(play).screen_is_faded_out == 0) {
 		// Stage: mouse cursor
diff --git a/engines/ags/engine/ac/inv_window.cpp b/engines/ags/engine/ac/inv_window.cpp
index ea818511c08..82e4c6f7992 100644
--- a/engines/ags/engine/ac/inv_window.cpp
+++ b/engines/ags/engine/ac/inv_window.cpp
@@ -285,7 +285,6 @@ int InventoryScreen::Redraw() {
 
 	Bitmap *ds = prepare_gui_screen(windowxp, windowyp, windowwid, windowhit, true);
 	Draw(ds);
-	//ags_domouse(DOMOUSE_ENABLE);
 	set_mouse_cursor(cmode);
 	wasonitem = -1;
 	return 0;
@@ -387,7 +386,6 @@ bool InventoryScreen::Run() {
 			_GP(play).used_inv_on = dii[clickedon].num;
 
 			if (cmode == MODE_LOOK) {
-				//ags_domouse(DOMOUSE_DISABLE);
 				run_event_block_inv(dii[clickedon].num, 0);
 				// in case the script did anything to the screen, redraw it
 				UpdateGameOnce();
@@ -402,7 +400,6 @@ bool InventoryScreen::Run() {
 				int activeinvwas = _G(playerchar)->activeinv;
 				_G(playerchar)->activeinv = toret;
 
-				//ags_domouse(DOMOUSE_DISABLE);
 				run_event_block_inv(dii[clickedon].num, 3);
 
 				// if the script didn't change it, then put it back
@@ -435,14 +432,12 @@ bool InventoryScreen::Run() {
 				if (my < buttonyp + get_fixed_pixel_size(2) + ARROWBUTTONWID) {
 					if (top_item > 0) {
 						top_item -= ICONSPERLINE;
-						//ags_domouse(DOMOUSE_DISABLE);
 
 						break_code = Redraw();
 						return break_code == 0;
 					}
 				} else if ((my < buttonyp + get_fixed_pixel_size(4) + ARROWBUTTONWID * 2) && (top_item + num_visible_items < numitems)) {
 					top_item += ICONSPERLINE;
-					//ags_domouse(DOMOUSE_DISABLE);
 
 					break_code = Redraw();
 					return break_code == 0;
@@ -471,9 +466,7 @@ bool InventoryScreen::Run() {
 		toret = -1;
 		set_mouse_cursor(cmode);
 	} else if (isonitem != wasonitem) {
-		//ags_domouse(DOMOUSE_DISABLE);
 		RedrawOverItem(get_gui_screen(), isonitem);
-		//ags_domouse(DOMOUSE_ENABLE);
 	}
 	wasonitem = isonitem;
 
diff --git a/engines/ags/engine/gui/my_listbox.cpp b/engines/ags/engine/gui/my_listbox.cpp
index ec92eb65d2a..9fbbf700d3d 100644
--- a/engines/ags/engine/gui/my_listbox.cpp
+++ b/engines/ags/engine/gui/my_listbox.cpp
@@ -116,9 +116,7 @@ int MyListBox::pressedon(int mousex, int mousey) {
 
 	}
 
-	//    ags_domouse(DOMOUSE_DISABLE);
 	draw(get_gui_screen());
-	//  ags_domouse(DOMOUSE_ENABLE);
 	_G(smcode) = CM_SELCHANGE;
 	return 0;
 }
diff --git a/engines/ags/engine/gui/my_push_button.cpp b/engines/ags/engine/gui/my_push_button.cpp
index a952011c9d8..715bba56cae 100644
--- a/engines/ags/engine/gui/my_push_button.cpp
+++ b/engines/ags/engine/gui/my_push_button.cpp
@@ -78,22 +78,16 @@ int MyPushButton::pressedon(int mx, int my) {
 		state = mouseisinarea(mx, my);
 		update_polled_stuff();
 		if (wasstat != state) {
-			//        ags_domouse(DOMOUSE_DISABLE);
 			draw(get_gui_screen());
-			//ags_domouse(DOMOUSE_ENABLE);
 		}
 
-		//      ags_domouse(DOMOUSE_UPDATE);
-
 		refresh_gui_screen();
 
 		WaitForNextFrame();
 	}
 	wasstat = state;
 	state = 0;
-	//    ags_domouse(DOMOUSE_DISABLE);
 	draw(get_gui_screen());
-	//  ags_domouse(DOMOUSE_ENABLE);
 	return wasstat;
 }
 
diff --git a/engines/ags/engine/gui/new_control.cpp b/engines/ags/engine/gui/new_control.cpp
index 4aaaf2ee0a0..2fbeee56b42 100644
--- a/engines/ags/engine/gui/new_control.cpp
+++ b/engines/ags/engine/gui/new_control.cpp
@@ -68,9 +68,7 @@ void NewControl::drawifneeded() {
 	}
 }
 void NewControl::drawandmouse() {
-	//    ags_domouse(DOMOUSE_DISABLE);
 	draw(get_gui_screen());
-	//  ags_domouse(DOMOUSE_ENABLE);
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index 28944bc0e0b..8267b90427e 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -49,6 +49,7 @@
 #include "ags/engine/ac/room.h"
 #include "ags/engine/ac/room_object.h"
 #include "ags/engine/ac/room_status.h"
+#include "ags/engine/ac/view_frame.h"
 #include "ags/engine/ac/walk_behind.h"
 #include "ags/engine/debugging/debugger.h"
 #include "ags/engine/debugging/debug_log.h"
@@ -614,42 +615,91 @@ static void game_loop_update_animated_buttons() {
 	}
 }
 
-static void game_loop_do_render_and_check_mouse(IDriverDependantBitmap *extraBitmap, int extraX, int extraY) {
-	if (!_GP(play).fast_forward) {
-		int mwasatx = _G(mousex), mwasaty = _G(mousey);
+// Updates GUI reaction to the cursor position change
+// TODO: possibly may be merged with gui_on_mouse_move()
+static void update_cursor_over_gui() {
+	if (((_G(debug_flags) & DBG_NOIFACE) != 0) || (_G(displayed_room) < 0))
+		return; // GUI is disabled (debug flag) or room is not loaded
+	if (!IsInterfaceEnabled())
+		return; // interface is disabled (by script or blocking action)
+	// Poll guis
+	for (auto &gui : _GP(guis)) {
+		if (!gui.IsDisplayed())
+			continue; // not on screen
+		// Don't touch GUI if "GUIs Turn Off When Disabled"
+		if ((_GP(game).options[OPT_DISABLEOFF] == kGuiDis_Off) &&
+			(_G(all_buttons_disabled) >= 0) &&
+			(gui.PopupStyle != kGUIPopupNoAutoRemove))
+			continue;
+		gui.Poll(_G(mousex), _G(mousey));
+	}
+}
 
-		// Only do this if we are not skipping a cutscene
-		render_graphics(extraBitmap, extraX, extraY);
+static void update_cursor_view() {
+	// update animating mouse cursor
+	if (_GP(game).mcurs[_G(cur_cursor)].view >= 0) {
+		// only on mousemove, and it's not moving
+		if (((_GP(game).mcurs[_G(cur_cursor)].flags & MCF_ANIMMOVE) != 0) &&
+			(_G(mousex) == _G(lastmx)) && (_G(mousey) == _G(lastmy)))
+			;
+		// only on hotspot, and it's not on one
+		else if (((_GP(game).mcurs[_G(cur_cursor)].flags & MCF_HOTSPOT) != 0) &&
+				 (GetLocationType(game_to_data_coord(_G(mousex)), game_to_data_coord(_G(mousey))) == 0))
+			set_new_cursor_graphic(_GP(game).mcurs[_G(cur_cursor)].pic);
+		else if (_G(mouse_delay) > 0)
+			_G(mouse_delay)--;
+		else {
+			int viewnum = _GP(game).mcurs[_G(cur_cursor)].view;
+			int loopnum = 0;
+			if (loopnum >= _GP(views)[viewnum].numLoops)
+				quitprintf("An animating mouse cursor is using view %d which has no loops", viewnum + 1);
+			if (_GP(views)[viewnum].loops[loopnum].numFrames < 1)
+				quitprintf("An animating mouse cursor is using view %d which has no frames in loop %d", viewnum + 1, loopnum);
+
+			_G(mouse_frame)++;
+			if (_G(mouse_frame) >= _GP(views)[viewnum].loops[loopnum].numFrames)
+				_G(mouse_frame) = 0;
+			set_new_cursor_graphic(_GP(views)[viewnum].loops[loopnum].frames[_G(mouse_frame)].pic);
+			_G(mouse_delay) = _GP(views)[viewnum].loops[loopnum].frames[_G(mouse_frame)].speed + _GP(game).mcurs[_G(cur_cursor)].animdelay;
+			CheckViewFrame(viewnum, loopnum, _G(mouse_frame));
+		}
+		_G(lastmx) = _G(mousex);
+		_G(lastmy) = _G(mousey);
+	}
+}
 
-		// Check Mouse Moves Over Hotspot event
-		// TODO: move this out of render related function? find out why we remember mwasatx and mwasaty before render
-		// TODO: do not use static variables!
-		// TODO: if we support rotation then we also need to compare full transform!
-		if (_G(displayed_room) < 0)
-			return;
-		auto view = _GP(play).GetRoomViewportAt(_G(mousex), _G(mousey));
-		auto cam = view ? view->GetCamera() : nullptr;
-		if (cam) {
-			// NOTE: all cameras are in same room right now, so their positions are in same coordinate system;
-			// therefore we may use this as an indication that mouse is over different camera too.
-			static int offsetxWas = -1000, offsetyWas = -1000;
-			int offsetx = cam->GetRect().Left;
-			int offsety = cam->GetRect().Top;
-
-			if (((mwasatx != _G(mousex)) || (mwasaty != _G(mousey)) ||
-			        (offsetxWas != offsetx) || (offsetyWas != offsety))) {
-				// mouse moves over hotspot
-				if (__GetLocationType(game_to_data_coord(_G(mousex)), game_to_data_coord(_G(mousey)), 1) == LOCTYPE_HOTSPOT) {
-					int onhs = _G(getloctype_index);
-
-					setevent(EV_RUNEVBLOCK, EVB_HOTSPOT, onhs, EVHOT_MOUSEOVER);
-				}
-			}
+static void update_cursor_over_location(int mwasatx, int mwasaty) {
+	if (_GP(play).fast_forward)
+		return;
+	if (_G(displayed_room) < 0)
+		return;
+
+	// Check Mouse Moves Over Hotspot event
+	auto view = _GP(play).GetRoomViewportAt(_G(mousex), _G(mousey));
+	auto cam = view ? view->GetCamera() : nullptr;
+	if (!cam)
+		return;
 
-			offsetxWas = offsetx;
-			offsetyWas = offsety;
-		} // camera found under mouse
+	// NOTE: all cameras are in same room right now, so their positions are in same coordinate system;
+	// therefore we may use this as an indication that mouse is over different camera too.
+	// TODO: do not use static variables!
+	// TODO: if we support rotation then we also need to compare full transform!
+	static int offsetxWas = -1000, offsetyWas = -1000;
+	int offsetx = cam->GetRect().Left;
+	int offsety = cam->GetRect().Top;
+
+	if (((mwasatx != _G(mousex)) || (mwasaty != _G(mousey)) ||
+		 (offsetxWas != offsetx) || (offsetyWas != offsety))) {
+		// mouse moves over hotspot
+		if (__GetLocationType(game_to_data_coord(_G(mousex)), game_to_data_coord(_G(mousey)), 1) == LOCTYPE_HOTSPOT) {
+			int onhs = _G(getloctype_index);
+
+			setevent(EV_RUNEVBLOCK, EVB_HOTSPOT, onhs, EVHOT_MOUSEOVER);
+		}
 	}
+
+	offsetxWas = offsetx;
+	offsetyWas = offsety;
 }
 
 static void game_loop_update_events() {
@@ -770,6 +820,16 @@ void UpdateGameOnce(bool checkControls, IDriverDependantBitmap *extraBitmap, int
 
 	check_debug_keys();
 
+	// Handle player's input
+	// remember old mouse pos, needed for update_cursor_over_location() later
+	const int mwasatx = _G(mousex), mwasaty = _G(mousey);
+	// update mouse position (mousex, mousey)
+	ags_domouse();
+	// update gui under mouse; this also updates gui control focus;
+	// atm we must call this before "check_controls", because GUI interaction
+	// relies on remembering which control was focused by the cursor prior
+	update_cursor_over_gui();
+	// handle actual input (keys, mouse, and so forth)
 	game_loop_check_controls(checkControls);
 
 	if (_G(abort_engine))
@@ -777,6 +837,7 @@ void UpdateGameOnce(bool checkControls, IDriverDependantBitmap *extraBitmap, int
 
 	_G(our_eip) = 2;
 
+	// do the overall game state update
 	game_loop_do_update();
 
 	game_loop_update_animated_buttons();
@@ -785,7 +846,12 @@ void UpdateGameOnce(bool checkControls, IDriverDependantBitmap *extraBitmap, int
 
 	update_audio_system_on_game_loop();
 
-	game_loop_do_render_and_check_mouse(extraBitmap, extraX, extraY);
+	update_cursor_over_location(mwasatx, mwasaty);
+	update_cursor_view();
+
+	// Only render if we are not skipping a cutscene
+	if (!_GP(play).fast_forward)
+		render_graphics(extraBitmap, extraX, extraY);
 
 	_G(our_eip) = 6;
 
diff --git a/engines/ags/plugins/ags_plugin.cpp b/engines/ags/plugins/ags_plugin.cpp
index 4e046ce2aa2..8dbb6328a63 100644
--- a/engines/ags/plugins/ags_plugin.cpp
+++ b/engines/ags/plugins/ags_plugin.cpp
@@ -343,8 +343,8 @@ void IAGSEngine::BlitSpriteRotated(int32 x, int32 y, BITMAP *bmp, int32 angle) {
 }
 
 void IAGSEngine::PollSystem() {
-	ags_domouse();
 	update_polled_stuff();
+	ags_domouse();
 	eAGSMouseButton mbut;
 	int mwheelz;
 	if (run_service_mb_controls(mbut, mwheelz) && mbut > kMouseNone && !_GP(play).IsIgnoringInput())


Commit: f9de999bbe75f9f4036df9b47420028a0695313a
    https://github.com/scummvm/scummvm/commit/f9de999bbe75f9f4036df9b47420028a0695313a
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: Update cursor and dependent "logic" in special game states

Because some things were previously updated during the
render, we also need to explicitly run these whenever we do
not update whole game, but need to keep cursor updated:
* Dialog options;
* blocking Display command
* built-in gui dialogs

>From upstream e236f3ee05bb7e1e748fb050c99ba1afa564ec65

Changed paths:
    engines/ags/engine/ac/dialog.cpp
    engines/ags/engine/ac/display.cpp
    engines/ags/engine/gui/gui_dialog.cpp
    engines/ags/engine/main/game_run.cpp
    engines/ags/engine/main/game_run.h


diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index 1223bdc745a..249507b80ad 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -805,6 +805,7 @@ bool DialogOptions::Run() {
 		_GP(play).disabled_user_interface--;
 	} else {
 		update_audio_system_on_game_loop();
+		update_cursor_and_dependent();
 		render_graphics(ddb, dirtyx, dirtyy);
 	}
 
diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 780685fe5ac..82ef565fe5d 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -307,6 +307,7 @@ ScreenOverlay *_display_main(int xx, int yy, int wii, const char *text, int disp
 			sys_evt_process_pending();
 
 			update_audio_system_on_game_loop();
+			update_cursor_and_dependent();
 			render_graphics();
 			eAGSMouseButton mbut;
 			int mwheelz;
diff --git a/engines/ags/engine/gui/gui_dialog.cpp b/engines/ags/engine/gui/gui_dialog.cpp
index 926e6324db1..0b51fd455af 100644
--- a/engines/ags/engine/gui/gui_dialog.cpp
+++ b/engines/ags/engine/gui/gui_dialog.cpp
@@ -31,6 +31,7 @@
 #include "ags/engine/gui/csci_dialog.h"
 #include "ags/shared/gfx/bitmap.h"
 #include "ags/engine/gfx/graphics_driver.h"
+#include "ags/engine/main/game_run.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/shared/util/path.h"
 #include "ags/ags.h"
@@ -80,6 +81,7 @@ void clear_gui_screen() {
 
 void refresh_gui_screen() {
 	_G(gfxDriver)->UpdateDDBFromBitmap(_G(dialogDDB), _G(windowBuffer), false);
+	update_cursor_and_dependent();
 	render_graphics(_G(dialogDDB), _G(windowPosX), _G(windowPosY));
 }
 
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index 8267b90427e..00a32ef3935 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -1106,6 +1106,14 @@ void RunGameUntilAborted() {
 	}
 }
 
+void update_cursor_and_dependent() {
+	const int mwasatx = _G(mousex), mwasaty = _G(mousey);
+	ags_domouse();
+	update_cursor_over_gui();
+	update_cursor_over_location(mwasatx, mwasaty);
+	update_cursor_view();
+}
+
 void update_polled_stuff() {
 	::AGS::g_events->pollEvents();
 
diff --git a/engines/ags/engine/main/game_run.h b/engines/ags/engine/main/game_run.h
index 605b927c819..7cc247849e2 100644
--- a/engines/ags/engine/main/game_run.h
+++ b/engines/ags/engine/main/game_run.h
@@ -46,9 +46,9 @@ void GameLoopUntilButAnimEnd(int guin, int objn);
 
 // Run the actual game until it ends, or aborted by player/error; loops GameTick() internally
 void RunGameUntilAborted();
-// Update everything game related
+// Update everything game related; wait for the next frame
 void UpdateGameOnce(bool checkControls = false, IDriverDependantBitmap *extraBitmap = nullptr, int extraX = 0, int extraY = 0);
-// Update minimal required game state: audio, loop counter, etc
+// Update minimal required game state: audio, loop counter, etc; wait for the next frame
 void UpdateGameAudioOnly();
 // Gets current logical game FPS, this is normally a fixed number set in script;
 // in case of "maxed fps" mode this function returns real measured FPS.
@@ -62,6 +62,9 @@ bool run_service_mb_controls(eAGSMouseButton &mbut, int &mwheelz);
 // Polls few things (exit flag and debugger messages)
 // TODO: refactor this
 void update_polled_stuff();
+// Update cursor position and view, poll anything related to cursor position;
+// this function is useful when you don't want to update whole game, but only the cursor.
+void update_cursor_and_dependent();
 
 } // namespace AGS3
 


Commit: a89fe3cd9e993d6484a15f4e96226f254f24b95e
    https://github.com/scummvm/scummvm/commit/a89fe3cd9e993d6484a15f4e96226f254f24b95e
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Updated build version (3.6.0.46)

Partially from upstream 2b03d989c8ac5de3dac49b483bd216aaeb807354

Changed paths:
    engines/ags/shared/core/def_version.h


diff --git a/engines/ags/shared/core/def_version.h b/engines/ags/shared/core/def_version.h
index c70c67378e7..1f72294fc54 100644
--- a/engines/ags/shared/core/def_version.h
+++ b/engines/ags/shared/core/def_version.h
@@ -22,9 +22,9 @@
 #ifndef AGS_SHARED_CORE_DEFVERSION_H
 #define AGS_SHARED_CORE_DEFVERSION_H
 
-#define ACI_VERSION_STR      "3.6.0.45"
+#define ACI_VERSION_STR      "3.6.0.46"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3.6.0.45
+#define ACI_VERSION_MSRC_DEF  3.6.0.46
 #endif
 
 #define SPECIAL_VERSION ""


Commit: 53a472650debb2b72472deb916907fe0b50dc83f
    https://github.com/scummvm/scummvm/commit/53a472650debb2b72472deb916907fe0b50dc83f
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: moved iagsstream to util dir (api dir proved to be excessive)

>From upstream 2de0701f47cb189b51aa429cd8ef4812b6dae9ef

Changed paths:
  A engines/ags/shared/util/iags_stream.h
  R engines/ags/shared/api/stream_api.h
    engines/ags/plugins/serializer.h
    engines/ags/shared/util/stream.h


diff --git a/engines/ags/plugins/serializer.h b/engines/ags/plugins/serializer.h
index d49529db241..0e05b4b56de 100644
--- a/engines/ags/plugins/serializer.h
+++ b/engines/ags/plugins/serializer.h
@@ -22,7 +22,7 @@
 #ifndef AGS_PLUGINS_SERIALIZER_H
 #define AGS_PLUGINS_SERIALIZER_H
 
-#include "ags/shared/api/stream_api.h"
+#include "ags/shared/util/iags_stream.h"
 #include "ags/plugins/ags_plugin.h"
 #include "common/serializer.h"
 
diff --git a/engines/ags/shared/api/stream_api.h b/engines/ags/shared/util/iags_stream.h
similarity index 100%
rename from engines/ags/shared/api/stream_api.h
rename to engines/ags/shared/util/iags_stream.h
diff --git a/engines/ags/shared/util/stream.h b/engines/ags/shared/util/stream.h
index ed7a2c03e89..dcd979ebd0e 100644
--- a/engines/ags/shared/util/stream.h
+++ b/engines/ags/shared/util/stream.h
@@ -34,7 +34,7 @@
 #ifndef AGS_SHARED_UTIL_STREAM_H
 #define AGS_SHARED_UTIL_STREAM_H
 
-#include "ags/shared/api/stream_api.h"
+#include "ags/shared/util/iags_stream.h"
 #include "ags/lib/allegro/file.h"
 #include "common/stream.h"
 #include "common/types.h"


Commit: c3fc687b510830860af22c62a41db13439a8a91a
    https://github.com/scummvm/scummvm/commit/c3fc687b510830860af22c62a41db13439a8a91a
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: replaced long type in aastr/allegro/alfont

Use uint32_t, because these libs were written for 16/32-bit
systems originally. (these longs are mostly pixel values)

Partially from upstream 8b918708e52797c3c954c1a3df65119c9a1d3aa9 and
861e394bce389cbf769f433194c55d826993d0b4

Changed paths:
    engines/ags/lib/aastr-0.1.1/aarot.cpp
    engines/ags/lib/aastr-0.1.1/aastr.cpp
    engines/ags/lib/aastr-0.1.1/aautil.cpp
    engines/ags/lib/aastr-0.1.1/aautil.h
    engines/ags/lib/alfont/alfont.cpp


diff --git a/engines/ags/lib/aastr-0.1.1/aarot.cpp b/engines/ags/lib/aastr-0.1.1/aarot.cpp
index d8725bbde2a..bb4594e0e4a 100644
--- a/engines/ags/lib/aastr-0.1.1/aarot.cpp
+++ b/engines/ags/lib/aastr-0.1.1/aarot.cpp
@@ -62,8 +62,8 @@ static void _aa_rotate_bitmap(BITMAP *_src, BITMAP *_dst, int _x, int _y, fixed
 	int rscinc, rscdd, rsci1, rsci2;
 	int sxinc, sxdd, sxi1, sxi2;
 	int syinc, sydd, syi1, syi2;
-	unsigned long num;
-	void (*add)(BITMAP * _src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num);
+	uint32_t num;
+	void (*add)(BITMAP * _src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num);
 	void (*put)(byte * _addr, int _x);
 
 	if (_dst->clip) {
diff --git a/engines/ags/lib/aastr-0.1.1/aastr.cpp b/engines/ags/lib/aastr-0.1.1/aastr.cpp
index 334009dbc17..c86ed64839f 100644
--- a/engines/ags/lib/aastr-0.1.1/aastr.cpp
+++ b/engines/ags/lib/aastr-0.1.1/aastr.cpp
@@ -50,8 +50,8 @@ static void _aa_stretch_blit(BITMAP *_src, BITMAP *_dst,
 	int xi1, xi2, xdd, yxdd;
 	int yi1, yi2, ydd;
 	int dxbeg, dxend, dybeg, dyend;
-	unsigned long num;
-	void (*add)(BITMAP * _src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num);
+	uint32_t num;
+	void (*add)(BITMAP * _src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num);
 	void (*put)(byte * _addr, int _x);
 
 	if ((_dw <= 0) || (_dh <= 0) || (_sw <= 0) || (_sh <= 0))
diff --git a/engines/ags/lib/aastr-0.1.1/aautil.cpp b/engines/ags/lib/aastr-0.1.1/aautil.cpp
index 101919c18c0..f0d4d1188cb 100644
--- a/engines/ags/lib/aastr-0.1.1/aautil.cpp
+++ b/engines/ags/lib/aastr-0.1.1/aautil.cpp
@@ -65,7 +65,7 @@ void _aa_prepare_for_24bpp() {
 /*
  * Add r, g, b values of pixels.
  */
-void _aa_add_rgb8(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_add_rgb8(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned char *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
@@ -183,7 +183,7 @@ void _aa_add_rgb8(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned
 }
 
 #ifdef ALLEGRO_COLOR16
-void _aa_add_rgb15(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_add_rgb15(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned short *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
@@ -300,7 +300,7 @@ void _aa_add_rgb15(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigne
 	}
 }
 
-void _aa_add_rgb16(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_add_rgb16(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned short *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
@@ -419,7 +419,7 @@ void _aa_add_rgb16(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigne
 #endif
 
 #ifdef ALLEGRO_COLOR24
-void _aa_add_rgb24(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_add_rgb24(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned char *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
@@ -528,7 +528,7 @@ void _aa_add_rgb24(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigne
 #endif
 
 #ifdef ALLEGRO_COLOR32
-void _aa_add_rgb32(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_add_rgb32(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned int *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
@@ -675,13 +675,13 @@ void _aa_put_rgb32(byte *addr, int _x) {
 /*
  * Add masked r, g, b values of pixels.
  */
-void _aa_masked_add_rgb8(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_masked_add_rgb8(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned char *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
-	unsigned long r1, g1, b1;
-	unsigned long r2, g2, b2, t2;
-	unsigned long scolor;
+	uint32_t r1, g1, b1;
+	uint32_t r2, g2, b2, t2;
+	uint32_t scolor;
 
 	sy1i = _sy1 >> aa_BITS;
 	sy = sy1i;
@@ -833,7 +833,7 @@ void _aa_masked_add_rgb8(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, u
 }
 
 #ifdef ALLEGRO_COLOR16
-void _aa_masked_add_rgb15(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_masked_add_rgb15(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned short *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
@@ -990,7 +990,7 @@ void _aa_masked_add_rgb15(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2,
 		_aa.transparent = 1;
 }
 
-void _aa_masked_add_rgb16(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_masked_add_rgb16(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned short *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
@@ -1149,7 +1149,7 @@ void _aa_masked_add_rgb16(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2,
 #endif
 
 #ifdef ALLEGRO_COLOR24
-void _aa_masked_add_rgb24(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_masked_add_rgb24(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned char *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
@@ -1371,7 +1371,7 @@ void _aa_masked_add_rgb24(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2,
 #endif
 
 #ifdef ALLEGRO_COLOR32
-void _aa_masked_add_rgb32(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num) {
+void _aa_masked_add_rgb32(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num) {
 	unsigned int *sline;
 	int sx, sx1i, sx1f, sx2i, sx2f;
 	int sy, sy1i, sy1f, sy2i, sy2f;
diff --git a/engines/ags/lib/aastr-0.1.1/aautil.h b/engines/ags/lib/aastr-0.1.1/aautil.h
index 5a1e8653054..401403d6794 100644
--- a/engines/ags/lib/aastr-0.1.1/aautil.h
+++ b/engines/ags/lib/aastr-0.1.1/aautil.h
@@ -100,16 +100,16 @@ extern "C" {
 void _aa_prepare_for_24bpp(void);
 
 /* Add r,g,b values from source bitmap.  */
-void _aa_add_rgb8(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num);
+void _aa_add_rgb8(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num);
 #ifdef ALLEGRO_COLOR16
-void _aa_add_rgb15(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num);
-void _aa_add_rgb16(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num);
+void _aa_add_rgb15(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num);
+void _aa_add_rgb16(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num);
 #endif
 #ifdef ALLEGRO_COLOR24
-void _aa_add_rgb24(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num);
+void _aa_add_rgb24(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num);
 #endif
 #ifdef ALLEGRO_COLOR32
-void _aa_add_rgb32(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, unsigned long _num);
+void _aa_add_rgb32(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2, uint32_t _num);
 #endif
 
 /* Put pixel to destination bitmap.  */
@@ -127,20 +127,20 @@ void _aa_put_rgb32(byte *addr, int _x);
 
 /* Add r,g,b and transparency values from source bitmap.  */
 void _aa_masked_add_rgb8(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2,
-                         unsigned long _num);
+                         uint32_t _num);
 #ifdef ALLEGRO_COLOR16
 void _aa_masked_add_rgb15(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2,
-                          unsigned long _num);
+                          uint32_t _num);
 void _aa_masked_add_rgb16(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2,
-                          unsigned long _num);
+                          uint32_t _num);
 #endif
 #ifdef ALLEGRO_COLOR24
 void _aa_masked_add_rgb24(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2,
-                          unsigned long _num);
+                          uint32_t _num);
 #endif
 #ifdef ALLEGRO_COLOR32
 void _aa_masked_add_rgb32(BITMAP *_src, int _sx1, int _sx2, int _sy1, int _sy2,
-                          unsigned long _num);
+                          uint32_t _num);
 #endif
 
 /* Put masked pixel to destination bitmap.  */
diff --git a/engines/ags/lib/alfont/alfont.cpp b/engines/ags/lib/alfont/alfont.cpp
index 0d233170c71..d980da69c51 100644
--- a/engines/ags/lib/alfont/alfont.cpp
+++ b/engines/ags/lib/alfont/alfont.cpp
@@ -118,8 +118,8 @@ const char *alfont_get_name(ALFONT_FONT *f) {
 */
 
 /* original: _blender_trans15 in colblend.c */
-unsigned long __skiptranspixels_blender_trans15(unsigned long x, unsigned long y, unsigned long n) {
-	unsigned long result;
+uint32_t __skiptranspixels_blender_trans15(uint32_t x, uint32_t y, uint32_t n) {
+	uint32_t result;
 
 	if ((y & 0xFFFF) == 0x7C1F)
 		return x;
@@ -136,8 +136,8 @@ unsigned long __skiptranspixels_blender_trans15(unsigned long x, unsigned long y
 }
 
 /* original: _blender_trans16 in colblend.c */
-unsigned long __skiptranspixels_blender_trans16(unsigned long x, unsigned long y, unsigned long n) {
-	unsigned long result;
+uint32_t __skiptranspixels_blender_trans16(uint32_t x, uint32_t y, uint32_t n) {
+	uint32_t result;
 
 	if ((y & 0xFFFF) == 0xF81F)
 		return x;
@@ -154,8 +154,8 @@ unsigned long __skiptranspixels_blender_trans16(unsigned long x, unsigned long y
 }
 
 /* original: _blender_trans24 in colblend.c */
-unsigned long __preservedalpha_blender_trans24(unsigned long x, unsigned long y, unsigned long n) {
-	unsigned long res, g, alpha;
+uint32_t __preservedalpha_blender_trans24(uint32_t x, uint32_t y, uint32_t n) {
+	uint32_t res, g, alpha;
 
 	alpha = (y & 0xFF000000);
 


Commit: 1b3a417e750faf090390e8c92e01cd82f2cf4ba3
    https://github.com/scummvm/scummvm/commit/1b3a417e750faf090390e8c92e01cd82f2cf4ba3
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: in IniFile replaced string indexes from int with size_t

This is long overdue since String class changed int indexes to size_t.

>From upstream b81f9e31e8b8bbbbf46e570427d16deea5102d3a

Changed paths:
    engines/ags/shared/util/ini_file.cpp
    engines/ags/shared/util/ini_file.h
    engines/ags/tests/test_inifile.cpp


diff --git a/engines/ags/shared/util/ini_file.cpp b/engines/ags/shared/util/ini_file.cpp
index 5b222ed5ef6..c660f9a4ec4 100644
--- a/engines/ags/shared/util/ini_file.cpp
+++ b/engines/ags/shared/util/ini_file.cpp
@@ -41,7 +41,7 @@ IniFile::ItemDef::ItemDef(const String &key, const String &value) {
 	Value.second = Value.first + value.GetLength();
 }
 
-IniFile::ItemDef::ItemDef(const String &line, const StrPos &key, const StrPos &value, int sep_at) {
+IniFile::ItemDef::ItemDef(const String &line, const StrPos &key, const StrPos &value, size_t sep_at) {
 	Line = line;
 	Key = key;
 	Value = value;
@@ -53,7 +53,7 @@ void IniFile::ItemDef::SetKey(const String &key) {
 		return;
 
 	if (IsKeyValue()) {
-		int diff = key.GetLength() - (Key.second - Key.first);
+		size_t diff = key.GetLength() - (Key.second - Key.first);
 		ReplaceSubString(Line, Key, key);
 		Key.second += diff;
 		Value.first += diff;
@@ -67,8 +67,8 @@ void IniFile::ItemDef::SetValue(const String &value) {
 	if (!IsKeyValue())
 		return; // no key
 
-	if (SepAt > 0) {   // replacing existing value
-		int diff = static_cast<int>(value.GetLength()) - (Value.second - Value.first);
+	if (SepAt != String::NoIndex) {   // replacing existing value
+		size_t diff = value.GetLength() - (Value.second - Value.first);
 		ReplaceSubString(Line, Value, value);
 		Value.second += diff;
 	} else {   // inserting value behind the key
@@ -98,7 +98,7 @@ void IniFile::SectionDef::SetName(const String &sec_name) {
 	if (sec_name.IsEmpty())
 		return;
 
-	int diff = sec_name.GetLength() - (Name.second - Name.first);
+	size_t diff = sec_name.GetLength() - (Name.second - Name.first);
 	ReplaceSubString(Header, Name, sec_name);
 	Name.second += diff;
 }
@@ -198,7 +198,7 @@ void IniFile::Read(Stream *in) {
 		if ((endl - pstr >= 2 && *pstr == '/' && *(pstr + 1) == '/') ||
 		        (endl - pstr >= 1 && (*pstr == '#' || *pstr == ';'))) {
 			StrPos nullpos(0, 0);
-			cur_section->InsertItem(cur_section->End(), ItemDef(line, nullpos, nullpos, -1));
+			cur_section->InsertItem(cur_section->End(), ItemDef(line, nullpos, nullpos, String::NoIndex));
 			continue;
 		}
 
@@ -231,7 +231,7 @@ void IniFile::Read(Stream *in) {
 			// Create an item and parse value, if any
 			StrPos keypos(str_at - cstr, str_end - cstr);
 			StrPos valpos(0, 0);
-			int sep_at = -1;
+			size_t sep_at = String::NoIndex;
 			if (pstr != endl) {
 				sep_at = pstr - cstr;
 				ParsePaddedString(++pstr, endl, str_at, str_end);
diff --git a/engines/ags/shared/util/ini_file.h b/engines/ags/shared/util/ini_file.h
index 37d3bfa9ce1..9a1e23f5a6e 100644
--- a/engines/ags/shared/util/ini_file.h
+++ b/engines/ags/shared/util/ini_file.h
@@ -44,10 +44,14 @@ class IniFile {
 public:
 	// Position of a string in the line of text:
 	// is defined by a pair of first and next-after-last character indices
-	typedef std::pair<int, int> StrPos;
+	typedef std::pair<size_t, size_t> StrPos;
 	// Location of section in the array of text lines:
 	// is defined by a pair of first and next-after-last line indices
-	typedef std::pair<int, int> SectionPos;
+	typedef std::pair<size_t, size_t> SectionPos;
+
+	inline static bool IsValidStrPos(const StrPos &pos) {
+		return pos.first < pos.second;
+	}
 
 	// Item definition
 	// Valid key indicates a key-value line; no key means unparsed
@@ -55,7 +59,7 @@ public:
 	class ItemDef {
 	public:
 		ItemDef(const String &key, const String &value);
-		ItemDef(const String &line, const StrPos &key, const StrPos &value, int sep_at);
+		ItemDef(const String &line, const StrPos &key, const StrPos &value, size_t sep_at);
 		String GetLine()  const {
 			return Line;
 		}
@@ -65,8 +69,9 @@ public:
 		String GetValue() const {
 			return SubString(Line, Value);
 		}
-		bool   IsKeyValue() const {
-			return Key.second - Key.first > 0;
+		// Tells if this is a valid key/value item, which means that it has a valid key
+		bool IsKeyValue() const {
+			return IsValidStrPos(Key);
 		}
 		void SetKey(const String &key);
 		void SetValue(const String &value);
@@ -74,7 +79,7 @@ public:
 	private:
 		String  Line;  // actual text
 		StrPos  Key;   // position of item key
-		int     SepAt; // position of the separator (assignment) symbol
+		size_t  SepAt; // position of the separator (assignment) symbol
 		StrPos  Value; // position of item value
 	};
 	// Linked list of items
@@ -96,8 +101,9 @@ public:
 		size_t GetItemCount() const {
 			return Items.size();
 		}
-		bool   IsGlobal() const {
-			return Name.second - Name.first <= 0;
+		// Tells if this is a "global" section, which means that it has no name
+		bool IsGlobal() const {
+			return !IsValidStrPos(Name);
 		}
 		ItemIterator Begin() {
 			return Items.begin();
diff --git a/engines/ags/tests/test_inifile.cpp b/engines/ags/tests/test_inifile.cpp
index a7def671fb9..d826e41d005 100644
--- a/engines/ags/tests/test_inifile.cpp
+++ b/engines/ags/tests/test_inifile.cpp
@@ -115,20 +115,20 @@ void Test_IniFile() {
 	delete fs;
 
 	// there are explicit sections and 1 implicit global one
-	const int section_count = 5;
+	const size_t section_count = 5;
 	// Test reading from the custom ini file
 	{
 		assert(ini.GetSectionCount() == section_count);
 		IniFile::ConstSectionIterator sec = ini.CBegin();
 
-		assert(sec->GetItemCount() == 1);
+		assert(sec->GetItemCount() == 1u);
 		IniFile::ConstItemIterator item = sec->CBegin();
 		assert(item->GetKey() == "global_item");
 		assert(item->GetValue() == "global_value");
 
 		++sec;
 		assert(sec->GetName() == "section1");
-		assert(sec->GetItemCount() == 5);
+		assert(sec->GetItemCount() == 5u);
 		item = sec->CBegin();
 		assert(item->GetKey() == "item1");
 		assert(item->GetValue() == "");
@@ -146,7 +146,7 @@ void Test_IniFile() {
 
 		++sec;
 		assert(sec->GetName() == "this_section_should_be_deleted");
-		assert(sec->GetItemCount() == 3);
+		assert(sec->GetItemCount() == 3u);
 		item = sec->CBegin();
 		assert(item->GetKey() == "item1");
 		assert(item->GetValue() == "value1");
@@ -158,7 +158,7 @@ void Test_IniFile() {
 
 		++sec;
 		assert(sec->GetName() == "section3");
-		assert(sec->GetItemCount() == 2);
+		assert(sec->GetItemCount() == 2u);
 		item = sec->CBegin();
 		assert(item->GetKey() == "item_to_be_deleted");
 		assert(item->GetValue() == "value");
@@ -168,7 +168,7 @@ void Test_IniFile() {
 
 		++sec;
 		assert(sec->GetName() == "section4");
-		assert(sec->GetItemCount() == 1);
+		assert(sec->GetItemCount() == 1u);
 		item = sec->CBegin();
 		assert(item->GetKey() == "item1");
 		assert(item->GetValue() == "value");
@@ -234,18 +234,18 @@ void Test_IniFile() {
 		ConfigTree tree;
 		IniUtil::Read("test.ini", tree);
 
-		assert(tree.size() == 5);
+		assert(tree.size() == 5u);
 		assert(tree.find("") != tree.end()); // global section
 		assert(tree.find("section1") != tree.end());
 		assert(tree.find("section3") != tree.end());
 		assert(tree.find("section4") != tree.end());
 		assert(tree.find("section5") != tree.end());
 		StringOrderMap &sub_tree = tree[""];
-		assert(sub_tree.size() == 1);
+		assert(sub_tree.size() == 1u);
 		assert(sub_tree.find("global_item") != sub_tree.end());
 		assert(sub_tree["global_item"] == "global_value");
 		sub_tree = tree["section1"];
-		assert(sub_tree.size() == 4);
+		assert(sub_tree.size() == 4u);
 		assert(sub_tree.find("item1") != sub_tree.end());
 		assert(sub_tree.find("item2") != sub_tree.end());
 		assert(sub_tree.find("item3") != sub_tree.end());
@@ -255,11 +255,11 @@ void Test_IniFile() {
 		assert(sub_tree["item3"] == "value3");
 		assert(sub_tree["new_item"] == "new_value");
 		sub_tree = tree["section3"];
-		assert(sub_tree.size() == 1);
+		assert(sub_tree.size() == 1u);
 		assert(sub_tree.find("item_to_be_kept") != sub_tree.end());
 		assert(sub_tree["item_to_be_kept"] == "another value");
 		sub_tree = tree["section4"];
-		assert(sub_tree.size() == 3);
+		assert(sub_tree.size() == 3u);
 		assert(sub_tree.find("new_item1") != sub_tree.end());
 		assert(sub_tree.find("item1") != sub_tree.end());
 		assert(sub_tree.find("new_item2") != sub_tree.end());
@@ -267,7 +267,7 @@ void Test_IniFile() {
 		assert(sub_tree["item1"] == "value");
 		assert(sub_tree["new_item2"] == "new_value2");
 		sub_tree = tree["section5"];
-		assert(sub_tree.size() == 3);
+		assert(sub_tree.size() == 3u);
 		assert(sub_tree.find("item5_1") != sub_tree.end());
 		assert(sub_tree.find("item5_2") != sub_tree.end());
 		assert(sub_tree.find("item5_3") != sub_tree.end());


Commit: 9b233e9fde7124a2556662c73d689060b1a0e33f
    https://github.com/scummvm/scummvm/commit/9b233e9fde7124a2556662c73d689060b1a0e33f
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Common: replaced long type in allegro lib sources

Use int32_t instead of long.

Partially from upstream 2f1e73e3b830b3db874148a5a6215c6c270c6a94

Changed paths:
    engines/ags/lib/allegro/file.cpp
    engines/ags/lib/allegro/file.h


diff --git a/engines/ags/lib/allegro/file.cpp b/engines/ags/lib/allegro/file.cpp
index 0b7a7cb1830..8ae0c070ba5 100644
--- a/engines/ags/lib/allegro/file.cpp
+++ b/engines/ags/lib/allegro/file.cpp
@@ -39,7 +39,7 @@ int PACKFILE::pack_igetw() {
 	return pack_fread(buf, 2) == 2 ? READ_LE_UINT16(buf) : 0;
 }
 
-long PACKFILE::pack_igetl() {
+int32_t PACKFILE::pack_igetl() {
 	byte buf[4];
 	return pack_fread(buf, 4) == 4 ? READ_LE_UINT32(buf) : 0;
 }
@@ -51,7 +51,7 @@ int PACKFILE::pack_iputw(int w) {
 	return 0;
 }
 
-long PACKFILE::pack_iputl(long l) {
+int32_t PACKFILE::pack_iputl(int32_t l) {
 	byte buf[4];
 	WRITE_LE_UINT32(buf, l);
 	pack_fwrite(buf, 4);
@@ -63,7 +63,7 @@ int PACKFILE::pack_mgetw() {
 	return pack_fread(buf, 2) == 2 ? READ_BE_UINT16(buf) : 0;
 }
 
-long PACKFILE::pack_mgetl() {
+int32_t PACKFILE::pack_mgetl() {
 	byte buf[4];
 	return pack_fread(buf, 4) == 4 ? READ_BE_UINT32(buf) : 0;
 }
@@ -75,7 +75,7 @@ int PACKFILE::pack_mputw(int w) {
 	return 0;
 }
 
-long PACKFILE::pack_mputl(long l) {
+int32_t PACKFILE::pack_mputl(int32_t l) {
 	byte buf[4];
 	WRITE_BE_UINT16(buf, 4);
 	pack_fwrite(buf, 4);
@@ -206,7 +206,7 @@ int pack_igetw(PACKFILE *f) {
 	error("TODO: xxx");
 }
 
-long pack_igetl(PACKFILE *f) {
+int32_t pack_igetl(PACKFILE *f) {
 	return f->pack_igetl();
 }
 
@@ -214,7 +214,7 @@ int pack_iputw(int w, PACKFILE *f) {
 	return f->pack_iputw(w);
 }
 
-long pack_iputl(long l, PACKFILE *f) {
+int32_t pack_iputl(int32_t l, PACKFILE *f) {
 	return f->pack_iputl(l);
 }
 
@@ -222,7 +222,7 @@ int pack_mgetw(PACKFILE *f) {
 	return f->pack_mgetw();
 }
 
-long pack_mgetl(PACKFILE *f) {
+int32_t pack_mgetl(PACKFILE *f) {
 	return f->pack_mgetl();
 }
 
@@ -230,7 +230,7 @@ int pack_mputw(int w, PACKFILE *f) {
 	return f->pack_mputw(w);
 }
 
-long pack_mputl(long l, PACKFILE *f) {
+int32_t pack_mputl(int32_t l, PACKFILE *f) {
 	return f->pack_mputl(l);
 }
 
diff --git a/engines/ags/lib/allegro/file.h b/engines/ags/lib/allegro/file.h
index 82b29f677a1..6421818306a 100644
--- a/engines/ags/lib/allegro/file.h
+++ b/engines/ags/lib/allegro/file.h
@@ -23,6 +23,7 @@
 #define AGS_LIB_ALLEGRO_FILE_H
 
 #include "ags/lib/allegro/alconfig.h"
+#include "ags/shared/core/types.h"
 #include "common/file.h"
 
 namespace AGS3 {
@@ -79,13 +80,13 @@ struct PACKFILE {
 	PACKFILE *pack_fopen_chunk(int pack);
 	PACKFILE *pack_fclose_chunk();
 	int pack_igetw();
-	long pack_igetl();
+	int32_t pack_igetl();
 	int pack_iputw(int w);
-	long pack_iputl(long l);
+	int32_t pack_iputl(int32_t l);
 	int pack_mgetw();
-	long pack_mgetl();
+	int32_t pack_mgetl();
 	int pack_mputw(int w);
-	long pack_mputl(long l);
+	int32_t pack_mputl(int32_t l);
 	char *pack_fgets(char *p, int max);
 	int pack_fputs(AL_CONST char *p);
 	};
@@ -207,13 +208,13 @@ AL_FUNC(int, pack_putc, (int c, PACKFILE *f));
 AL_FUNC(int, pack_feof, (PACKFILE *f));
 AL_FUNC(int, pack_ferror, (PACKFILE *f));
 AL_FUNC(int, pack_igetw, (PACKFILE *f));
-AL_FUNC(long, pack_igetl, (PACKFILE *f));
+AL_FUNC(int32_t, pack_igetl, (PACKFILE *f));
 AL_FUNC(int, pack_iputw, (int w, PACKFILE *f));
-AL_FUNC(long, pack_iputl, (long l, PACKFILE *f));
+AL_FUNC(int32_t, pack_iputl, (int32_t l, PACKFILE *f));
 AL_FUNC(int, pack_mgetw, (PACKFILE *f));
-AL_FUNC(long, pack_mgetl, (PACKFILE *f));
+AL_FUNC(int32_t, pack_mgetl, (PACKFILE *f));
 AL_FUNC(int, pack_mputw, (int w, PACKFILE *f));
-AL_FUNC(long, pack_mputl, (long l, PACKFILE *f));
+AL_FUNC(int32_t, pack_mputl, (int32_t l, PACKFILE *f));
 AL_FUNC(long, pack_fread, (void *p, long n, PACKFILE *f));
 AL_FUNC(long, pack_fwrite, (AL_CONST void *p, long n, PACKFILE *f));
 AL_FUNC(int, pack_ungetc, (int c, PACKFILE *f));


Commit: 37f97cc8b62a6140cf9028aa5745c6fd9c6708d0
    https://github.com/scummvm/scummvm/commit/37f97cc8b62a6140cf9028aa5745c6fd9c6708d0
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: add bitness and endianess to the printed engine version

+ Use term "engine" instead of "ACI" (a legacy "Adventure
Creator Interpreter" title).
>From upstream eaf298ae3f96db6e37bfd9ac99bad1dff48430ae

Changed paths:
    engines/ags/ags.cpp
    engines/ags/engine/ac/global_debug.cpp
    engines/ags/engine/game/savegame.cpp
    engines/ags/engine/main/engine.cpp
    engines/ags/engine/main/engine.h
    engines/ags/engine/main/main.cpp
    engines/ags/engine/main/quit.cpp


diff --git a/engines/ags/ags.cpp b/engines/ags/ags.cpp
index 73d89fff2bd..f54d55195c5 100644
--- a/engines/ags/ags.cpp
+++ b/engines/ags/ags.cpp
@@ -95,7 +95,7 @@ AGSEngine::AGSEngine(OSystem *syst, const AGSGameDescription *gameDesc) : Engine
 AGSEngine::~AGSEngine() {
 	if (_globals && _G(proper_exit) == 0) {
 		_G(platform)->DisplayAlert("Error: the program has exited without requesting it.\n"
-		                           "Program pointer: %+03d  (write this number down), ACI version %s\n"
+		                           "Program pointer: %+03d  (write this number down), engine version %s\n"
 		                           "If you see a list of numbers above, please write them down and contact\n"
 		                           "developers. Otherwise, note down any other information displayed.",
 		                           _G(our_eip), _G(EngineVersion).LongString.GetCStr());
diff --git a/engines/ags/engine/ac/global_debug.cpp b/engines/ags/engine/ac/global_debug.cpp
index ec0ce698653..66e70f01996 100644
--- a/engines/ags/engine/ac/global_debug.cpp
+++ b/engines/ags/engine/ac/global_debug.cpp
@@ -39,6 +39,7 @@
 #include "ags/engine/gui/gui_dialog.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/engine/debugging/debugger.h"
+#include "ags/engine/main/engine.h"
 #include "ags/engine/main/main.h"
 #include "ags/shared/ac/sprite_cache.h"
 #include "ags/shared/gfx/bitmap.h"
@@ -61,11 +62,13 @@ String GetRuntimeInfo() {
 	const size_t max_normspr =  _GP(spriteset).GetMaxCacheSize() - total_lockspr;
 	const unsigned norm_spr_filled = (uint64_t)total_normspr * 100 / max_normspr;
 	String runtimeInfo = String::FromFormat(
-		"Adventure Game Studio run-time engine[ACI version %s"
+		"%s[Engine version %s"
 		"[Game resolution %d x %d (%d-bit)"
 		"[Running %d x %d at %d-bit%s[GFX: %s; %s[Draw frame %d x %d["
 		"Sprite cache KB: %zu, norm: %zu / %zu (%u%%), locked: %zu",
-		_G(EngineVersion).LongString.GetCStr(), _GP(game).GetGameRes().Width, _GP(game).GetGameRes().Height, _GP(game).GetColorDepth(),
+		get_engine_name(),
+		get_engine_version_and_build().GetCStr(),
+		_GP(game).GetGameRes().Width, _GP(game).GetGameRes().Height, _GP(game).GetColorDepth(),
 		mode.Width, mode.Height, mode.ColorDepth,
 		mode.IsWindowed() ? " W" : "",
 		_G(gfxDriver)->GetDriverName(), filter->GetInfo().Name.GetCStr(),
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index 8d46393bd98..ce52665877a 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -666,7 +666,7 @@ void WriteDescription(Stream *out, const String &user_text, const Bitmap *user_i
 	soff_t env_pos = out->GetPosition();
 	out->WriteInt32(0);
 	// Enviroment information
-	StrUtil::WriteString("Adventure Game Studio run-time engine", out);
+	StrUtil::WriteString(get_engine_name(), out);
 	StrUtil::WriteString(_G(EngineVersion).LongString, out);
 	StrUtil::WriteString(_GP(game).guid, out);
 	StrUtil::WriteString(_GP(game).gamename, out);
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index fd47326d716..fae7dedcd91 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -1285,6 +1285,20 @@ const char *get_engine_version() {
 	return _G(EngineVersion).LongString.GetCStr();
 }
 
+String get_engine_version_and_build() {
+	const char *bit = (AGS_PLATFORM_64BIT) ? "64-bit" : "32-bit";
+	const char *end = (AGS_PLATFORM_ENDIAN_LITTLE) ? "LE" : "BE";
+#ifdef BUILD_STR
+	return String::FromFormat("%s (Build: %s), %s %s",
+							  _G(EngineVersion).LongString.GetCStr(), EngineVersion.BuildInfo.GetCStr(),
+							  bit, end);
+#else
+	return String::FromFormat("%s, %s %s",
+							  _G(EngineVersion).LongString.GetCStr(),
+							  bit, end);
+#endif
+}
+
 void engine_set_pre_init_callback(t_engine_pre_init_callback callback) {
 	_G(engine_pre_init_callback) = callback;
 }
diff --git a/engines/ags/engine/main/engine.h b/engines/ags/engine/main/engine.h
index 62bec9fabbf..08f79e61214 100644
--- a/engines/ags/engine/main/engine.h
+++ b/engines/ags/engine/main/engine.h
@@ -26,8 +26,10 @@
 
 namespace AGS3 {
 
-const char *get_engine_name();
-const char *get_engine_version();
+const char			*get_engine_name();
+const char			*get_engine_version();
+AGS::Shared::String  get_engine_version_and_build();
+
 void        show_preload();
 void        engine_init_game_settings();
 int         initialize_engine(const AGS::Shared::ConfigTree &startup_opts);
diff --git a/engines/ags/engine/main/main.cpp b/engines/ags/engine/main/main.cpp
index ef5c195e3b7..0257a266e52 100644
--- a/engines/ags/engine/main/main.cpp
+++ b/engines/ags/engine/main/main.cpp
@@ -83,13 +83,10 @@ void main_init(int argc, const char *argv[]) {
 
 String get_engine_string() {
 	return String::FromFormat("Adventure Game Studio v%s Interpreter\n"
-	                          "Copyright (c) 1999-2011 Chris Jones and " ACI_COPYRIGHT_YEARS " others\n"
-#ifdef BUILD_STR
-	                          "ACI version %s (Build: %s)\n",
-	                          _G(EngineVersion).ShortString.GetCStr(), _G(EngineVersion).LongString.GetCStr(), _G(EngineVersion).BuildInfo.GetCStr());
-#else
-	                          "ACI version %s\n", _G(EngineVersion).ShortString.GetCStr(), _G(EngineVersion).LongString.GetCStr());
-#endif
+							  "Copyright (c) 1999-2011 Chris Jones and " ACI_COPYRIGHT_YEARS " others\n"
+							  "Engine version %s\n",
+							  _G(EngineVersion).ShortString.GetCStr(),
+							  get_engine_version_and_build().GetCStr());
 }
 
 void main_print_help() {
diff --git a/engines/ags/engine/main/quit.cpp b/engines/ags/engine/main/quit.cpp
index 34b060ff315..006b26bcd9c 100644
--- a/engines/ags/engine/main/quit.cpp
+++ b/engines/ags/engine/main/quit.cpp
@@ -116,7 +116,7 @@ QuitReason quit_check_for_error_state(const char *qmsg, String &errmsg, String &
 			qreason = kQuit_GameError;
 			alertis.Format("An error has occurred. Please contact the game author for support, as this "
 			               "is likely to be a scripting error and not a bug in AGS.\n"
-			               "(ACI version %s)\n\n", _G(EngineVersion).LongString.GetCStr());
+			               "(Engine version %s)\n\n", _G(EngineVersion).LongString.GetCStr());
 		}
 
 		alertis.Append(cc_get_error().CallStack);
@@ -130,12 +130,12 @@ QuitReason quit_check_for_error_state(const char *qmsg, String &errmsg, String &
 		qmsg++;
 		alertis.Format("A warning has been generated. This is not normally fatal, but you have selected "
 		               "to treat warnings as errors.\n"
-		               "(ACI version %s)\n\n%s\n%s", _G(EngineVersion).LongString.GetCStr(), cc_get_error().CallStack.GetCStr(), qmsg);
+		               "(Engine version %s)\n\n%s\n%s", _G(EngineVersion).LongString.GetCStr(), cc_get_error().CallStack.GetCStr(), qmsg);
 		errmsg = qmsg;
 		return kQuit_GameWarning;
 	} else {
 		alertis.Format("An internal error has occurred. Please note down the following information.\n"
-		               "(ACI version %s)\n"
+		               "(Engine version %s)\n"
 		               "\nError: %s", _G(EngineVersion).LongString.GetCStr(), qmsg);
 		return kQuit_FatalError;
 	}


Commit: ea65074e6b4a6e4f1e2d24698d34cc89190fcf51
    https://github.com/scummvm/scummvm/commit/ea65074e6b4a6e4f1e2d24698d34cc89190fcf51
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: expanded a compat comment in find_free_audio_channel()

>From upstream 4392ef832f131c8fd1e5ded00b095f78cf4815ca

Changed paths:
    engines/ags/engine/media/audio/audio.cpp
    engines/ags/shared/ac/game_setup_struct.h


diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index 7f7604971fd..5cf0e4304a0 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -153,7 +153,6 @@ static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool int
 	if (!interruptEqualPriority)
 		priority--;
 
-	// NOTE: in backward compat mode we allow to place sound on a crossfade channel
 	int startAtChannel = _G(reserved_channel_count);
 	int endBeforeChannel = _GP(game).numGameChannels;
 
@@ -163,7 +162,8 @@ static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool int
 			startAtChannel += MIN(MAX_SOUND_CHANNELS,
 				_GP(game).audioClipTypes[i].reservedChannels);
 		}
-		// NOTE: we allow to place sound on a crossfade channel for backward compatibility
+		// NOTE: we allow to place sound on a crossfade channel for backward compatibility,
+		// but ONLY for the case of audio type with reserved channels (weird quirk).
 		endBeforeChannel = MIN(_GP(game).numCompatGameChannels,
 			startAtChannel + _GP(game).audioClipTypes[clip->type].reservedChannels);
 	}
diff --git a/engines/ags/shared/ac/game_setup_struct.h b/engines/ags/shared/ac/game_setup_struct.h
index 434dd020e68..8518fa96f9c 100644
--- a/engines/ags/shared/ac/game_setup_struct.h
+++ b/engines/ags/shared/ac/game_setup_struct.h
@@ -87,8 +87,9 @@ struct GameSetupStruct : public GameSetupStructBase {
 	// A clip to play when player gains score in game
 	// TODO: find out why OPT_SCORESOUND option cannot be used to store this in >=3.2 games
 	int               scoreClipID;
-	// number of allowed game audio channels (the ones under direct user control)
+	// number of accessible game audio channels (the ones under direct user control)
 	int               numGameChannels = 0;
+	// backward-compatible channel limit that may be exported to script and reserved by audiotypes
 	int               numCompatGameChannels = 0;
 
 	// TODO: I converted original array of sprite infos to vector here, because


Commit: 7ca114ea49385ea3bd3c6045c1e5f64947823686
    https://github.com/scummvm/scummvm/commit/7ca114ea49385ea3bd3c6045c1e5f64947823686
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: made FadeIn and FadeOut functions look consistent

>From upstream 95c4362271734c5cb724b314d7cc555f752e06a8

Changed paths:
    engines/ags/engine/ac/event.cpp
    engines/ags/engine/ac/global_api.cpp
    engines/ags/engine/ac/global_screen.cpp
    engines/ags/engine/ac/global_screen.h
    engines/ags/engine/ac/screen.cpp
    engines/ags/engine/ac/screen.h
    engines/ags/plugins/core/global_api.cpp
    engines/ags/plugins/core/global_api.h


diff --git a/engines/ags/engine/ac/event.cpp b/engines/ags/engine/ac/event.cpp
index 9cb59fc8589..2f94521e166 100644
--- a/engines/ags/engine/ac/event.cpp
+++ b/engines/ags/engine/ac/event.cpp
@@ -218,7 +218,7 @@ void process_event(const EventHappened *evp) {
 			// transition type was not crossfade/dissolve when the screen faded out,
 			// but it is now when the screen fades in (Eg. a save game was restored
 			// with a different setting). Therefore just fade normally.
-			my_fade_out(5);
+			fadeout_impl(5);
 			theTransition = FADE_NORMAL;
 		}
 
@@ -228,7 +228,7 @@ void process_event(const EventHappened *evp) {
 		if ((theTransition == FADE_INSTANT) || ignore_transition)
 			set_palette_range(_G(palette), 0, 255, 0);
 		else if (theTransition == FADE_NORMAL) {
-			my_fade_in(_G(palette), 5);
+			fadein_impl(_G(palette), 5);
 		} else if (theTransition == FADE_BOXOUT) {
 			if (!_G(gfxDriver)->UsesMemoryBackBuffer()) {
 				_G(gfxDriver)->BoxOutEffect(false, get_fixed_pixel_size(16), 1000 / GetGameSpeed());
diff --git a/engines/ags/engine/ac/global_api.cpp b/engines/ags/engine/ac/global_api.cpp
index a816a872d54..e13e99df1c7 100644
--- a/engines/ags/engine/ac/global_api.cpp
+++ b/engines/ags/engine/ac/global_api.cpp
@@ -338,8 +338,8 @@ RuntimeScriptValue Sc_FadeIn(const RuntimeScriptValue *params, int32_t param_cou
 }
 
 // void (int spdd)
-RuntimeScriptValue Sc_my_fade_out(const RuntimeScriptValue *params, int32_t param_count) {
-	API_SCALL_VOID_PINT(my_fade_out);
+RuntimeScriptValue Sc_FadeOut(const RuntimeScriptValue *params, int32_t param_count) {
+	API_SCALL_VOID_PINT(FadeOut);
 }
 
 // void (int handle)
@@ -1941,7 +1941,7 @@ void RegisterGlobalAPI() {
 	ccAddExternalStaticFunction("FaceCharacter",            Sc_FaceCharacter);
 	ccAddExternalStaticFunction("FaceLocation",             Sc_FaceLocation);
 	ccAddExternalStaticFunction("FadeIn",                   Sc_FadeIn);
-	ccAddExternalStaticFunction("FadeOut",                  Sc_my_fade_out);
+	ccAddExternalStaticFunction("FadeOut",                  Sc_FadeOut);
 	ccAddExternalStaticFunction("FileClose",                Sc_FileClose);
 	ccAddExternalStaticFunction("FileIsEOF",                Sc_FileIsEOF);
 	ccAddExternalStaticFunction("FileIsError",              Sc_FileIsError);
diff --git a/engines/ags/engine/ac/global_screen.cpp b/engines/ags/engine/ac/global_screen.cpp
index 3702e583f4b..9df16a4f026 100644
--- a/engines/ags/engine/ac/global_screen.cpp
+++ b/engines/ags/engine/ac/global_screen.cpp
@@ -133,14 +133,19 @@ void TintScreen(int red, int grn, int blu) {
 	_GP(play).screen_tint = red + (grn << 8) + (blu << 16);
 }
 
-void my_fade_out(int spdd) {
+void FadeOut(int sppd) {
 	EndSkippingUntilCharStops();
 
 	if (_GP(play).fast_forward)
 		return;
 
-	if (_GP(play).screen_is_faded_out == 0)
+	fadeout_impl(sppd);
+}
+
+void fadeout_impl(int spdd) {
+	if (_GP(play).screen_is_faded_out == 0) {
 		_G(gfxDriver)->FadeOut(spdd, _GP(play).fade_to_red, _GP(play).fade_to_green, _GP(play).fade_to_blue);
+	}
 
 	if (_GP(game).color_depth > 1)
 		_GP(play).screen_is_faded_out = 1;
@@ -180,7 +185,7 @@ void FadeIn(int sppd) {
 	if (_GP(play).fast_forward)
 		return;
 
-	my_fade_in(_G(palette), sppd);
+	fadein_impl(_G(palette), sppd);
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/global_screen.h b/engines/ags/engine/ac/global_screen.h
index 6c9e218f7fe..c7e2204ca08 100644
--- a/engines/ags/engine/ac/global_screen.h
+++ b/engines/ags/engine/ac/global_screen.h
@@ -28,11 +28,11 @@ void FlipScreen(int amount);
 void ShakeScreen(int severe);
 void ShakeScreenBackground(int delay, int amount, int length);
 void TintScreen(int red, int grn, int blu);
-void my_fade_out(int spdd);
 void SetScreenTransition(int newtrans);
 void SetNextScreenTransition(int newtrans);
 void SetFadeColor(int red, int green, int blue);
 void FadeIn(int sppd);
+void FadeOut(int sppd);
 
 } // namespace AGS3
 
diff --git a/engines/ags/engine/ac/screen.cpp b/engines/ags/engine/ac/screen.cpp
index 6cd3cda395b..3ed4891ab56 100644
--- a/engines/ags/engine/ac/screen.cpp
+++ b/engines/ags/engine/ac/screen.cpp
@@ -41,7 +41,7 @@ namespace AGS3 {
 using namespace AGS::Shared;
 using namespace AGS::Engine;
 
-void my_fade_in(PALETTE p, int speed) {
+void fadein_impl(PALETTE p, int speed) {
 	if (_GP(game).color_depth > 1) {
 		set_palette(p);
 
@@ -70,7 +70,7 @@ void current_fade_out_effect() {
 		if (!_GP(play).keep_screen_during_instant_transition)
 			set_palette_range(_G(black_palette), 0, 255, 0);
 	} else if (theTransition == FADE_NORMAL) {
-		my_fade_out(5);
+		fadeout_impl(5);
 	} else if (theTransition == FADE_BOXOUT) {
 		_G(gfxDriver)->BoxOutEffect(true, get_fixed_pixel_size(16), 1000 / GetGameSpeed());
 		_GP(play).screen_is_faded_out = 1;
diff --git a/engines/ags/engine/ac/screen.h b/engines/ags/engine/ac/screen.h
index 9efcde8a18b..b8895dc4015 100644
--- a/engines/ags/engine/ac/screen.h
+++ b/engines/ags/engine/ac/screen.h
@@ -46,7 +46,8 @@ class IDriverDependantBitmap;
 } // namespace Engine
 } // namespace AGS
 
-void my_fade_in(PALETTE p, int speed);
+void fadein_impl(PALETTE p, int speed);
+void fadeout_impl(int spdd);
 void current_fade_out_effect();
 AGS::Engine::IDriverDependantBitmap *prepare_screen_for_transition_in();
 
diff --git a/engines/ags/plugins/core/global_api.cpp b/engines/ags/plugins/core/global_api.cpp
index 7a9df9508d7..e2755bebe72 100644
--- a/engines/ags/plugins/core/global_api.cpp
+++ b/engines/ags/plugins/core/global_api.cpp
@@ -126,7 +126,7 @@ void GlobalAPI::AGS_EngineStartup(IAGSEngine *engine) {
 	SCRIPT_METHOD(FaceCharacter, GlobalAPI::FaceCharacter);
 	SCRIPT_METHOD(FaceLocation, GlobalAPI::FaceLocation);
 	SCRIPT_METHOD(FadeIn, GlobalAPI::FadeIn);
-	SCRIPT_METHOD(FadeOut, GlobalAPI::my_fade_out);
+	SCRIPT_METHOD(FadeOut, GlobalAPI::FadeOut);
 	SCRIPT_METHOD(FileClose, GlobalAPI::FileClose);
 	SCRIPT_METHOD(FileIsEOF, GlobalAPI::FileIsEOF);
 	SCRIPT_METHOD(FileIsError, GlobalAPI::FileIsError);
@@ -694,9 +694,9 @@ void GlobalAPI::FadeIn(ScriptMethodParams &params) {
 	AGS3::FadeIn(sppd);
 }
 
-void GlobalAPI::my_fade_out(ScriptMethodParams &params) {
+void GlobalAPI::FadeOut(ScriptMethodParams &params) {
 	PARAMS1(int, spdd);
-	AGS3::my_fade_out(spdd);
+	AGS3::FadeOut(spdd);
 }
 
 void GlobalAPI::FileClose(ScriptMethodParams &params) {
diff --git a/engines/ags/plugins/core/global_api.h b/engines/ags/plugins/core/global_api.h
index 985a309c302..4f894689cef 100644
--- a/engines/ags/plugins/core/global_api.h
+++ b/engines/ags/plugins/core/global_api.h
@@ -83,7 +83,7 @@ public:
 	void FaceCharacter(ScriptMethodParams &params);
 	void FaceLocation(ScriptMethodParams &params);
 	void FadeIn(ScriptMethodParams &params);
-	void my_fade_out(ScriptMethodParams &params);
+	void FadeOut(ScriptMethodParams &params);
 	void FileClose(ScriptMethodParams &params);
 	void FileIsEOF(ScriptMethodParams &params);
 	void FileIsError(ScriptMethodParams &params);


Commit: 2deb622aa96200863a9e2ab5b4eff5466b7028fa
    https://github.com/scummvm/scummvm/commit/2deb622aa96200863a9e2ab5b4eff5466b7028fa
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Engine: fixed FadeIn/Out and ShakeScreen prevent audio from starting

>From upstream 9634b6d3613f2b066998eaaf853945017f59fa01

Changed paths:
    engines/ags/engine/ac/global_screen.cpp
    engines/ags/engine/media/audio/audio.cpp
    engines/ags/engine/media/audio/audio.h


diff --git a/engines/ags/engine/ac/global_screen.cpp b/engines/ags/engine/ac/global_screen.cpp
index 9df16a4f026..2d36a5149a9 100644
--- a/engines/ags/engine/ac/global_screen.cpp
+++ b/engines/ags/engine/ac/global_screen.cpp
@@ -31,6 +31,7 @@
 #include "ags/engine/ac/screen.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/engine/main/game_run.h"
+#include "ags/engine/media/audio/audio.h"
 #include "ags/engine/platform/base/ags_platform_driver.h"
 #include "ags/engine/gfx/graphics_driver.h"
 #include "ags/shared/gfx/bitmap.h"
@@ -41,14 +42,6 @@ using namespace AGS::Shared;
 using namespace AGS::Engine;
 
 
-
-
-
-
-
-
-
-
 void FlipScreen(int amount) {
 	if ((amount < 0) | (amount > 3)) quit("!FlipScreen: invalid argument (0-3)");
 	_GP(play).screen_flipped = amount;
@@ -71,6 +64,9 @@ void ShakeScreen(int severe) {
 	_GP(play).shakesc_amount = severe;
 	_GP(play).mouse_cursor_hidden++;
 
+	// FIXME: we have to sync audio here explicitly, because ShakeScreen
+	// does not call any game update function while it works
+	sync_audio_playback();
 	if (_G(gfxDriver)->RequiresFullRedrawEachFrame()) {
 		for (int hh = 0; hh < 40; hh++) {
 			_G(loopcounter)++;
@@ -94,6 +90,7 @@ void ShakeScreen(int severe) {
 		clear_letterbox_borders();
 		render_to_screen();
 	}
+	sync_audio_playback();
 
 	_GP(play).mouse_cursor_hidden--;
 	_GP(play).shakesc_length = 0;
@@ -139,7 +136,11 @@ void FadeOut(int sppd) {
 	if (_GP(play).fast_forward)
 		return;
 
+	// FIXME: we have to sync audio here explicitly, because FadeOut
+	// does not call any game update function while it works
+	sync_audio_playback();
 	fadeout_impl(sppd);
+	sync_audio_playback();
 }
 
 void fadeout_impl(int spdd) {
@@ -185,7 +186,11 @@ void FadeIn(int sppd) {
 	if (_GP(play).fast_forward)
 		return;
 
+	// FIXME: we have to sync audio here explicitly, because FadeIn
+	// does not call any game update function while it works
+	sync_audio_playback();
 	fadein_impl(_G(palette), sppd);
+	sync_audio_playback();
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index 5cf0e4304a0..212b8aa4a32 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -780,6 +780,19 @@ void update_volume_drop_if_voiceover() {
 	apply_volume_drop_modifier(_GP(play).speech_has_voice);
 }
 
+// Sync logical game channels with the audio backend:
+// startup new assigned clips, apply changed parameters.
+void sync_audio_playback() {
+	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
+		// update the playing channels, and dispose the finished / invalid ones
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
+		if (ch && !ch->update()) {
+			AudioChans::SetChannel(i, nullptr);
+			delete ch;
+		}
+	}
+}
+
 // Update the music, and advance the crossfade on a step
 // (this should only be called once per game loop)
 void update_audio_system_on_game_loop() {
@@ -791,13 +804,7 @@ void update_audio_system_on_game_loop() {
 	// and queues, then second time later - because we need to apply any
 	// changes to channels / parameters.
 	// TODO: investigate options for optimizing this.
-	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) { // update the playing channels, and dispose the finished / invalid ones
-		auto *ch = AudioChans::GetChannelIfPlaying(i);
-		if (ch && !ch->update()) {
-			AudioChans::SetChannel(i, nullptr);
-			delete ch;
-		}
-	}
+	sync_audio_playback();
 
 	process_scheduled_music_update();
 
@@ -838,16 +845,8 @@ void update_audio_system_on_game_loop() {
 		update_directional_sound_vol();
 	}
 
-	// Sync logical game channels with the audio backend:
-	// startup new assigned clips, apply changed parameters.
-	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
-		// update the playing channels, and dispose the finished / invalid ones
-		auto *ch = AudioChans::GetChannelIfPlaying(i);
-		if (ch && !ch->update()) {
-			AudioChans::SetChannel(i, nullptr);
-			delete ch;
-		}
-	}
+	// Sync logical game channels with the audio backend again
+	sync_audio_playback();
 }
 
 void stopmusic() {
diff --git a/engines/ags/engine/media/audio/audio.h b/engines/ags/engine/media/audio/audio.h
index 03394266a8d..bb6b827f5d6 100644
--- a/engines/ags/engine/media/audio/audio.h
+++ b/engines/ags/engine/media/audio/audio.h
@@ -100,6 +100,8 @@ void        play_next_queued();
 int         calculate_max_volume();
 // add/remove the volume drop to the audio channels while speech is playing
 void        apply_volume_drop_modifier(bool applyModifier);
+// syncs logical audio channels with the audio backend state
+void        sync_audio_playback();
 // Update the music, and advance the crossfade on a step
 // (this should only be called once per game loop);
 void        update_audio_system_on_game_loop();


Commit: 74b5ea9f44c6e5df441cecfe701cc2ea40bc722a
    https://github.com/scummvm/scummvm/commit/74b5ea9f44c6e5df441cecfe701cc2ea40bc722a
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2023-04-29T13:11:41+02:00

Commit Message:
AGS: Updated build version (3.6.0.47), marking "final" 3.6.0

Partially from upstream 820ee6c41427e3529a22ef46ac2799297a050876

Changed paths:
    engines/ags/shared/core/def_version.h


diff --git a/engines/ags/shared/core/def_version.h b/engines/ags/shared/core/def_version.h
index 1f72294fc54..c64bbfac3e3 100644
--- a/engines/ags/shared/core/def_version.h
+++ b/engines/ags/shared/core/def_version.h
@@ -22,9 +22,9 @@
 #ifndef AGS_SHARED_CORE_DEFVERSION_H
 #define AGS_SHARED_CORE_DEFVERSION_H
 
-#define ACI_VERSION_STR      "3.6.0.46"
+#define ACI_VERSION_STR      "3.6.0.47"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3.6.0.46
+#define ACI_VERSION_MSRC_DEF  3.6.0.47
 #endif
 
 #define SPECIAL_VERSION ""




More information about the Scummvm-git-logs mailing list