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

dreammaster noreply at scummvm.org
Fri May 6 05:43:18 UTC 2022


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

Summary:
edca3fcdea COMMON: Added move assignment operator to ScopedPtr
830c1b096b AGS: introduced ObjTexture struct which combines bmp and ddb
8e4254b839 AGS: Refactored walk-behinds calculation and texture generation
d161f11e54 AGS: Further simplify walkbehinds_cropout()
cfefb80461 AGS: Moved font renderer shutdown after the game data disposal
da1178d905 AGS: Implemented proper HasAlphaChannel for all gui controls
d767293a32 AGS: Updated build version (3.6.0.24)
480be2134b AGS: Added script instance thread stack, replacing current_instance
a2a5f2575d AGS: Reorganized few cc_ headers
16d0d89787 AGS: Grouped ccError variables in a struct
8e132f7b3f AGS: cc_error returns stack from all script threads
540a92a6c5 AGS: Necessary fixes to build with latest changes to ccError
faa858a55b AGS: Fixed sending non-error messages to debugger
6482e38803 AGS: CharacterExtras and MoveLists are stored in std::vector
a40ea44aa2 AGS: CharacterCache is stored in std::vector, hide in draw.cpp
97eb5271d3 AGS: Added member initialization for CharacterExtras, MoveList
a8ead540c5 AGS: GUIControl::CalcGraphicRect returns relative rect
28a2b6aa9c AGS: Added extra checks for abort_engine being set
63c42d42c3 AGS: Fixed ScriptString could be serialized with wrong length
97c12e8435 AGS: Merged Character and ObjectCache structs, and hid in draw.cpp
84a3925442 AGS: Simplified the use of get_overlay_position()
b1649597d7 AGS: Replaced Math/std Min/Max with ScummVM macros
86c363a8dd AGS: Remove ScriptOverlay.hasInternalRef flag, add actual ref
ec38c1c61c AGS: Refactor core overlay creation funcs to return ScreenOverlay*
13f40609c7 AGS: Reset currentline when exiting script function, for cc_errors
d385968209 AGS: Tidy up mobile config once more and get rid of psp_* variables


Commit: edca3fcdeae70304e46ddea2290aaa4695c3f208
    https://github.com/scummvm/scummvm/commit/edca3fcdeae70304e46ddea2290aaa4695c3f208
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:05-07:00

Commit Message:
COMMON: Added move assignment operator to ScopedPtr

Changed paths:
    common/ptr.h


diff --git a/common/ptr.h b/common/ptr.h
index c52667a82cc..0c509a74f2e 100644
--- a/common/ptr.h
+++ b/common/ptr.h
@@ -582,6 +582,18 @@ public:
 		_pointer = o;
 	}
 
+	/**
+	 * Replaces the ScopedPtr with another scoped ScopedPtr.
+	 */
+	template<class T2>
+	ScopedPtr &operator=(ScopedPtr<T2> &&other) {
+		PointerType oldPointer = _pointer;
+		_pointer = other._pointer;
+		other._pointer = nullptr;
+		DL()(oldPointer);
+		return *this;
+	}
+
 	/**
 	 * Returns the plain pointer value.
 	 *


Commit: 830c1b096b99f6ed5d4834a79e9acad2d5e0fa6a
    https://github.com/scummvm/scummvm/commit/830c1b096b99f6ed5d4834a79e9acad2d5e0fa6a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:05-07:00

Commit Message:
AGS: introduced ObjTexture struct which combines bmp and ddb

>From upstream b628fb4d1b3ca6a85e2b2424a468f4cec94ae9c6
Also includes new implementation of std::vector class to
properly allow for arrays of smart pointers

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/draw.h
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/game_state.h
    engines/ags/engine/ac/global_object.cpp
    engines/ags/engine/game/savegame.h
    engines/ags/globals.cpp
    engines/ags/globals.h
    engines/ags/lib/std/utility.h
    engines/ags/lib/std/vector.h
    engines/ags/shared/game/main_game_file.h


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 61b0be6b489..1cdfef61680 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -79,12 +79,38 @@ namespace AGS3 {
 using namespace AGS::Shared;
 using namespace AGS::Engine;
 
+int _places_r = 3, _places_g = 2, _places_b = 3;
+
+ObjTexture::ObjTexture(ObjTexture &&o) {
+//	*this = std::move(o);
+	error("TODO: ObjTexture");
+}
+
+ObjTexture::~ObjTexture() {
+	Bmp.reset();
+	if (Ddb) {
+		assert(_G(gfxDriver));
+		_G(gfxDriver)->DestroyDDB(Ddb);
+	}
+}
+
+ObjTexture &ObjTexture::operator=(ObjTexture &&o) {
+	if (Ddb) {
+		assert(_G(gfxDriver));
+		_G(gfxDriver)->DestroyDDB(Ddb);
+	}
+	Bmp = std::move(o.Bmp);
+	Ddb = o.Ddb;
+	o.Ddb = nullptr;
+	Pos = o.Pos;
+	return *this;
+}
+
+
 void setpal() {
 	set_palette_range(_G(palette), 0, 255, 0);
 }
 
-int _places_r = 3, _places_g = 2, _places_b = 3;
-
 // PSP: convert 32 bit RGB to BGR.
 Bitmap *convert_32_to_32bgr(Bitmap *tempbl) {
 
@@ -365,9 +391,7 @@ void init_game_drawdata() {
 
 	size_t actsps_num = _GP(game).numcharacters + MAX_ROOM_OBJECTS;
 	_GP(actsps).resize(actsps_num);
-	_GP(actspsbmp).resize(actsps_num);
 	_GP(guibg).resize(_GP(game).numgui);
-	_GP(guibgddb).resize(_GP(game).numgui);
 
 	size_t guio_num = 0;
 	// Prepare GUI cache lists and build the quick reference for controls cache
@@ -377,33 +401,20 @@ void init_game_drawdata() {
 		guio_num += gui.GetControlCount();
 	}
 	_GP(guiobjbg).resize(guio_num);
-	_GP(guiobjddb).resize(guio_num);
-	_GP(guiobjoff).resize(guio_num);
 }
 
 void dispose_game_drawdata() {
 	clear_drawobj_cache();
 
 	_GP(actsps).clear();
-	_GP(actspsbmp).clear();
 	_GP(guibg).clear();
-	_GP(guibgddb).clear();
-
 	_GP(guiobjbg).clear();
-	_GP(guiobjddb).clear();
 	_GP(guiobjddbref).clear();
-	_GP(guiobjoff).clear();
 }
 
 static void dispose_debug_room_drawdata() {
-	_GP(debugRoomMaskBmp).reset();
-	if (_G(debugRoomMaskDDB) != nullptr)
-		_G(gfxDriver)->DestroyDDB(_G(debugRoomMaskDDB));
-	_G(debugRoomMaskDDB) = nullptr;
-	_GP(debugMoveListBmp).reset();
-	if (_G(debugMoveListDDB) != nullptr)
-		_G(gfxDriver)->DestroyDDB(_G(debugMoveListDDB));
-	_G(debugMoveListDDB) = nullptr;
+	_GP(debugRoomMaskObj) = ObjTexture();
+	_GP(debugMoveListObj) = ObjTexture();
 }
 
 void dispose_room_drawdata() {
@@ -419,30 +430,13 @@ void clear_drawobj_cache() {
 	}
 
 	// cleanup Character + Room object textures
-	for (int i = 0; i < MAX_ROOM_OBJECTS + _GP(game).numcharacters; ++i) {
-		delete _GP(actsps)[i];
-		_GP(actsps)[i] = nullptr;
-		if (_GP(actspsbmp)[i] != nullptr)
-			_G(gfxDriver)->DestroyDDB(_GP(actspsbmp)[i]);
-		_GP(actspsbmp)[i] = nullptr;
-	}
-
-	// cleanup GUI backgrounds
-	for (int i = 0; i < _GP(game).numgui; ++i) {
-		delete _GP(guibg)[i];
-		_GP(guibg)[i] = nullptr;
-		if (_GP(guibgddb)[i])
-			_G(gfxDriver)->DestroyDDB(_GP(guibgddb)[i]);
-		_GP(guibgddb)[i] = nullptr;
-	}
-
-	for (size_t i = 0; i < _GP(guiobjbg).size(); ++i) {
-		delete _GP(guiobjbg)[i];
-		_GP(guiobjbg)[i] = nullptr;
-		if (_GP(guiobjddb)[i])
-			_G(gfxDriver)->DestroyDDB(_GP(guiobjddb)[i]);
-		_GP(guiobjddb)[i] = nullptr;
-	}
+	// cleanup Character + Room object textures
+	for (auto &o : _GP(actsps)) o = ObjTexture();
+	// cleanup GUI and controls textures
+	for (auto &o : _GP(guibg)) o = ObjTexture();
+	for (auto &o : _GP(guiobjbg)) o = ObjTexture();
+	// cleanup Overlay intermediate bitmaps
+	_GP(overlaybmp).clear();
 
 	dispose_debug_room_drawdata();
 }
@@ -736,20 +730,22 @@ void draw_sprite_slot_support_alpha(Bitmap *ds, bool ds_has_alpha, int xpos, int
 	                          blend_mode, alpha);
 }
 
-
-IDriverDependantBitmap *recycle_ddb_bitmap(IDriverDependantBitmap *bimp, Bitmap *source, bool hasAlpha, bool opaque) {
-	if (bimp != nullptr) {
+IDriverDependantBitmap *recycle_ddb_bitmap(IDriverDependantBitmap *ddb, Bitmap *source, bool has_alpha, bool opaque) {
+	if (ddb) {
 		// same colour depth, width and height -> reuse
-		if ((bimp->GetColorDepth() == source->GetColorDepth()) &&
-		        (bimp->GetWidth() == source->GetWidth()) && (bimp->GetHeight() == source->GetHeight())) {
-			_G(gfxDriver)->UpdateDDBFromBitmap(bimp, source, hasAlpha);
-			return bimp;
+		if ((ddb->GetColorDepth() == source->GetColorDepth()) &&
+			(ddb->GetWidth() == source->GetWidth()) && (ddb->GetHeight() == source->GetHeight())) {
+			_G(gfxDriver)->UpdateDDBFromBitmap(ddb, source, has_alpha);
+			return ddb;
 		}
 
-		_G(gfxDriver)->DestroyDDB(bimp);
+		_G(gfxDriver)->DestroyDDB(ddb);
 	}
-	bimp = _G(gfxDriver)->CreateDDBFromBitmap(source, hasAlpha, opaque);
-	return bimp;
+	return _G(gfxDriver)->CreateDDBFromBitmap(source, has_alpha, opaque);
+}
+
+void sync_object_texture(ObjTexture &obj, bool has_alpha = false, bool opaque = false) {
+	obj.Ddb = recycle_ddb_bitmap(obj.Ddb, obj.Bmp.get(), has_alpha, opaque);
 }
 
 //------------------------------------------------------------------------
@@ -971,6 +967,10 @@ Bitmap *recycle_bitmap(Bitmap *bimp, int coldep, int wid, int hit, bool make_tra
 	return bimp;
 }
 
+void recycle_bitmap(std::unique_ptr<Shared::Bitmap> &bimp, int coldep, int wid, int hit, bool make_transparent) {
+	bimp.reset(recycle_bitmap(bimp.release(), coldep, wid, hit, make_transparent));
+}
+
 // Get the local tint at the specified X & Y co-ordinates, based on
 // room regions and SetAmbientTint
 // tint_amnt will be set to 0 if there is no tint enabled
@@ -1064,30 +1064,31 @@ void apply_tint_or_light(int actspsindex, int light_level,
                          int tint_blue, int tint_light, int coldept,
                          Bitmap *blitFrom) {
 
-// In a 256-colour game, we cannot do tinting or lightening
-// (but we can do darkening, if light_level < 0)
+	// In a 256-colour game, we cannot do tinting or lightening
+	// (but we can do darkening, if light_level < 0)
 	if (_GP(game).color_depth == 1) {
 		if ((light_level > 0) || (tint_amount != 0))
 			return;
 	}
 
-// we can only do tint/light if the colour depths match
-	if (_GP(game).GetColorDepth() == _GP(actsps)[actspsindex]->GetColorDepth()) {
-		Bitmap *oldwas;
+	auto &actsp = _GP(actsps)[actspsindex];
+	// we can only do tint/light if the colour depths match
+	if (_GP(game).GetColorDepth() == actsp.Bmp->GetColorDepth()) {
+		std::unique_ptr<Bitmap> oldwas;
 		// if the caller supplied a source bitmap, ->Blit from it
 		// (used as a speed optimisation where possible)
 		if (blitFrom)
-			oldwas = blitFrom;
+			oldwas.reset(blitFrom);
 		// otherwise, make a new target bmp
 		else {
-			oldwas = _GP(actsps)[actspsindex];
-			_GP(actsps)[actspsindex] = BitmapHelper::CreateBitmap(oldwas->GetWidth(), oldwas->GetHeight(), coldept);
+			oldwas = std::move(actsp.Bmp);
+			actsp.Bmp.reset(BitmapHelper::CreateBitmap(oldwas->GetWidth(), oldwas->GetHeight(), coldept));
 		}
-		Bitmap *active_spr = _GP(actsps)[actspsindex];
+		Bitmap *active_spr = actsp.Bmp.get();
 
 		if (tint_amount) {
 			// It is an RGB tint
-			tint_image(active_spr, oldwas, tint_red, tint_green, tint_blue, tint_amount, tint_light);
+			tint_image(active_spr, oldwas.get(), tint_red, tint_green, tint_blue, tint_amount, tint_light);
 		} else {
 			// the RGB values passed to set_trans_blender decide whether it will darken
 			// or lighten sprites ( <128=darken, >128=lighten). The parameter passed
@@ -1108,26 +1109,25 @@ void apply_tint_or_light(int actspsindex, int light_level,
 				lit_amnt = abs(light_level) * 2;
 			}
 
-			active_spr->LitBlendBlt(oldwas, 0, 0, lit_amnt);
+			active_spr->LitBlendBlt(oldwas.get(), 0, 0, lit_amnt);
 		}
 
-		if (oldwas != blitFrom)
-			delete oldwas;
+		if (oldwas.get() == blitFrom)
+			oldwas.release();
 
 	} else if (blitFrom) {
 		// sprite colour depth != game colour depth, so don't try and tint
 		// but we do need to do something, so copy the source
-		Bitmap *active_spr = _GP(actsps)[actspsindex];
+		Bitmap *active_spr = actsp.Bmp.get();
 		active_spr->Blit(blitFrom, 0, 0, 0, 0, active_spr->GetWidth(), active_spr->GetHeight());
 	}
-
 }
 
-Bitmap *transform_sprite(Bitmap *src, bool src_has_alpha, Bitmap *&dst, const Size dst_sz, BitmapFlip flip) {
+Bitmap *transform_sprite(Bitmap *src, bool src_has_alpha, std::unique_ptr<Bitmap> &dst, const Size dst_sz, BitmapFlip flip) {
 	if ((src->GetSize() == dst_sz) && (flip == kBitmap_NoFlip))
 		return src; // No transform: return source image
 
-	dst = recycle_bitmap(dst, src->GetColorDepth(), dst_sz.Width, dst_sz.Height, true);
+	recycle_bitmap(dst, src->GetColorDepth(), dst_sz.Width, dst_sz.Height, true);
 	_G(our_eip) = 339;
 
 	// If scaled: first scale then optionally mirror
@@ -1159,7 +1159,7 @@ Bitmap *transform_sprite(Bitmap *src, bool src_has_alpha, Bitmap *&dst, const Si
 		// If not scaled, then simply blit mirrored
 		dst->FlipBlt(src, 0, 0, kBitmap_HFlip);
 	}
-	return dst; // return transformed result
+	return dst.get(); // return transformed result
 }
 
 // Draws the specified 'sppic' sprite onto _GP(actsps)[useindx] at the
@@ -1168,9 +1168,8 @@ Bitmap *transform_sprite(Bitmap *src, bool src_has_alpha, Bitmap *&dst, const Si
 // scaling or stretching was required, in which case nothing was done
 static bool scale_and_flip_sprite(int useindx, int sppic, int newwidth, int newheight, bool hmirror) {
 	Bitmap *src = _GP(spriteset)[sppic];
-	Bitmap *&dst = _GP(actsps)[useindx];
 	Bitmap *result = transform_sprite(src, (_GP(game).SpriteInfos[sppic].Flags & SPF_ALPHACHANNEL) != 0,
-		dst, Size(newwidth, newheight), hmirror ? kBitmap_HFlip : kBitmap_NoFlip);
+		_GP(actsps)[useindx].Bmp, Size(newwidth, newheight), hmirror ? kBitmap_HFlip : kBitmap_NoFlip);
 	return result != src;
 }
 
@@ -1252,11 +1251,12 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 		isMirrored = true;
 	}
 
+	auto &actsp = _GP(actsps)[useindx];
 	if ((hardwareAccelerated) &&
-	        (_G(walkBehindMethod) != DrawOverCharSprite) &&
-	        (_G(objcache)[aa].image != nullptr) &&
-	        (_G(objcache)[aa].sppic == _G(objs)[aa].num) &&
-	        (_GP(actsps)[useindx] != nullptr)) {
+			(_G(walkBehindMethod) != DrawOverCharSprite) &&
+			(_G(objcache)[aa].image != nullptr) &&
+			(_G(objcache)[aa].sppic == _G(objs)[aa].num) &&
+			(actsp.Bmp != nullptr)) {
 		// HW acceleration
 		_G(objcache)[aa].tintamntwas = tint_level;
 		_G(objcache)[aa].tintredwas = tint_red;
@@ -1289,17 +1289,17 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 	        (_G(objcache)[aa].mirroredWas == isMirrored)) {
 		// the image is the same, we can use it cached!
 		if ((_G(walkBehindMethod) != DrawOverCharSprite) &&
-		        (_GP(actsps)[useindx] != nullptr))
+			(actsp.Bmp != nullptr))
 			return 1;
 		// Check if the X & Y co-ords are the same, too -- if so, there
 		// is scope for further optimisations
 		if ((_G(objcache)[aa].xwas == _G(objs)[aa].x) &&
-		        (_G(objcache)[aa].ywas == _G(objs)[aa].y) &&
-		        (_GP(actsps)[useindx] != nullptr) &&
-		        (_G(walk_behind_baselines_changed) == 0))
+			(_G(objcache)[aa].ywas == _G(objs)[aa].y) &&
+			(actsp.Bmp != nullptr) &&
+			(_G(walk_behind_baselines_changed) == 0))
 			return 1;
-		_GP(actsps)[useindx] = recycle_bitmap(_GP(actsps)[useindx], coldept, sprwidth, sprheight);
-		_GP(actsps)[useindx]->Blit(_G(objcache)[aa].image, 0, 0, 0, 0, _G(objcache)[aa].image->GetWidth(), _G(objcache)[aa].image->GetHeight());
+		recycle_bitmap(actsp.Bmp, coldept, sprwidth, sprheight);
+		actsp.Bmp->Blit(_G(objcache)[aa].image, 0, 0, 0, 0, _G(objcache)[aa].image->GetWidth(), _G(objcache)[aa].image->GetHeight());
 		return 0;
 	}
 
@@ -1312,7 +1312,7 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 	}
 	if (!actspsUsed) {
 		// ensure actsps exists // CHECKME: why do we need this in hardware accel mode too?
-		_GP(actsps)[useindx] = recycle_bitmap(_GP(actsps)[useindx], coldept, src_sprwidth, src_sprheight);
+		recycle_bitmap(actsp.Bmp, coldept, src_sprwidth, src_sprheight);
 	}
 
 	// direct read from source bitmap, where possible
@@ -1327,13 +1327,13 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 			tint_green, tint_blue, tint_light, coldept,
 			comeFrom);
 	} else if (!actspsUsed) {
-		_GP(actsps)[useindx]->Blit(_GP(spriteset)[_G(objs)[aa].num], 0, 0);
+		actsp.Bmp->Blit(_GP(spriteset)[_G(objs)[aa].num], 0, 0);
 	}
 
 	// Re-use the bitmap if it's the same size
 	_G(objcache)[aa].image = recycle_bitmap(_G(objcache)[aa].image, coldept, sprwidth, sprheight);
 	// Create the cached image and store it
-	_G(objcache)[aa].image->Blit(_GP(actsps)[useindx], 0, 0, 0, 0, sprwidth, sprheight);
+	_G(objcache)[aa].image->Blit(actsp.Bmp.get(), 0, 0);
 	_G(objcache)[aa].sppic = _G(objs)[aa].num;
 	_G(objcache)[aa].tintamntwas = tint_level;
 	_G(objcache)[aa].tintredwas = tint_red;
@@ -1361,6 +1361,8 @@ void prepare_objects_for_drawing() {
 		int tehHeight;
 		int actspsIntact = construct_object_gfx(aa, nullptr, &tehHeight, false);
 
+		auto &actsp = _GP(actsps)[useindx];
+
 		// update the cache for next time
 		_G(objcache)[aa].xwas = _G(objs)[aa].x;
 		_G(objcache)[aa].ywas = _G(objs)[aa].y;
@@ -1375,37 +1377,33 @@ void prepare_objects_for_drawing() {
 				usebasel += _GP(thisroom).Height;
 			}
 		} else if ((!actspsIntact) && (_G(walkBehindMethod) == DrawOverCharSprite)) {
-			sort_out_walk_behinds(_GP(actsps)[useindx], atxp, atyp, usebasel);
+			sort_out_walk_behinds(actsp.Bmp.get(), atxp, atyp, usebasel);
 		}
 
-		if ((!actspsIntact) || (_GP(actspsbmp)[useindx] == nullptr)) {
-			bool hasAlpha = (_GP(game).SpriteInfos[_G(objs)[aa].num].Flags & SPF_ALPHACHANNEL) != 0;
-
-			if (_GP(actspsbmp)[useindx] != nullptr)
-				_G(gfxDriver)->DestroyDDB(_GP(actspsbmp)[useindx]);
-			_GP(actspsbmp)[useindx] = _G(gfxDriver)->CreateDDBFromBitmap(_GP(actsps)[useindx], hasAlpha);
+		if ((!actspsIntact) || (actsp.Ddb == nullptr)) {
+			sync_object_texture(actsp, (_GP(game).SpriteInfos[_G(objs)[aa].num].Flags & SPF_ALPHACHANNEL) != 0);
 		}
 
 		if (_G(gfxDriver)->HasAcceleratedTransform()) {
-			_GP(actspsbmp)[useindx]->SetFlippedLeftRight(_G(objcache)[aa].mirroredWas != 0);
-			_GP(actspsbmp)[useindx]->SetStretch(_G(objs)[aa].last_width, _G(objs)[aa].last_height);
-			_GP(actspsbmp)[useindx]->SetTint(_G(objcache)[aa].tintredwas, _G(objcache)[aa].tintgrnwas, _G(objcache)[aa].tintbluwas, (_G(objcache)[aa].tintamntwas * 256) / 100);
+			actsp.Ddb->SetFlippedLeftRight(_G(objcache)[aa].mirroredWas != 0);
+			actsp.Ddb->SetStretch(_G(objs)[aa].last_width, _G(objs)[aa].last_height);
+			actsp.Ddb->SetTint(_G(objcache)[aa].tintredwas, _G(objcache)[aa].tintgrnwas, _G(objcache)[aa].tintbluwas, (_G(objcache)[aa].tintamntwas * 256) / 100);
 
 			if (_G(objcache)[aa].tintamntwas > 0) {
 				if (_G(objcache)[aa].tintlightwas == 0)  // luminance of 0 -- pass 1 to enable
-					_GP(actspsbmp)[useindx]->SetLightLevel(1);
+					actsp.Ddb->SetLightLevel(1);
 				else if (_G(objcache)[aa].tintlightwas < 250)
-					_GP(actspsbmp)[useindx]->SetLightLevel(_G(objcache)[aa].tintlightwas);
+					actsp.Ddb->SetLightLevel(_G(objcache)[aa].tintlightwas);
 				else
-					_GP(actspsbmp)[useindx]->SetLightLevel(0);
+					actsp.Ddb->SetLightLevel(0);
 			} else if (_G(objcache)[aa].lightlevwas != 0)
-				_GP(actspsbmp)[useindx]->SetLightLevel((_G(objcache)[aa].lightlevwas * 25) / 10 + 256);
+				actsp.Ddb->SetLightLevel((_G(objcache)[aa].lightlevwas * 25) / 10 + 256);
 			else
-				_GP(actspsbmp)[useindx]->SetLightLevel(0);
+				actsp.Ddb->SetLightLevel(0);
 		}
 
-		_GP(actspsbmp)[useindx]->SetAlpha(GfxDef::LegacyTrans255ToAlpha255(_G(objs)[aa].transparent));
-		add_to_sprite_list(_GP(actspsbmp)[useindx], atxp, atyp, usebasel, false);
+		actsp.Ddb->SetAlpha(GfxDef::LegacyTrans255ToAlpha255(_G(objs)[aa].transparent));
+		add_to_sprite_list(actsp.Ddb, atxp, atyp, usebasel, false);
 	}
 }
 
@@ -1549,6 +1547,8 @@ void prepare_characters_for_drawing() {
 
 		_G(our_eip) = 3331;
 
+		auto &actsp = _GP(actsps)[useindx];
+
 		// if the character was the same sprite and scaling last time,
 		// just use the cached image
 		if ((_G(charcache)[aa].inUse) &&
@@ -1561,8 +1561,8 @@ void prepare_characters_for_drawing() {
 		        (_G(charcache)[aa].tintlightwas == tint_light) &&
 		        (_G(charcache)[aa].lightlevwas == light_level)) {
 			if (_G(walkBehindMethod) == DrawOverCharSprite) {
-				_GP(actsps)[useindx] = recycle_bitmap(_GP(actsps)[useindx], _G(charcache)[aa].image->GetColorDepth(), _G(charcache)[aa].image->GetWidth(), _G(charcache)[aa].image->GetHeight());
-				_GP(actsps)[useindx]->Blit(_G(charcache)[aa].image, 0, 0, 0, 0, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
+				recycle_bitmap(actsp.Bmp, _G(charcache)[aa].image->GetColorDepth(), _G(charcache)[aa].image->GetWidth(), _G(charcache)[aa].image->GetHeight());
+				actsp.Bmp->Blit(_G(charcache)[aa].image, 0, 0);
 			} else {
 				usingCachedImage = true;
 			}
@@ -1571,7 +1571,7 @@ void prepare_characters_for_drawing() {
 		           (_G(gfxDriver)->HasAcceleratedTransform())) {
 			usingCachedImage = true;
 		} else if (_G(charcache)[aa].inUse) {
-			//destroy_bitmap (charcache[aa].image);
+			//destroy_bitmap (_G(charcache)[aa].image);
 			_G(charcache)[aa].inUse = 0;
 		}
 
@@ -1623,7 +1623,7 @@ void prepare_characters_for_drawing() {
 			}
 			if (!actspsUsed) {
 				// ensure actsps exists // CHECKME: why do we need this in hardware accel mode too?
-				_GP(actsps)[useindx] = recycle_bitmap(_GP(actsps)[useindx], coldept, src_sprwidth, src_sprheight);
+				recycle_bitmap(actsp.Bmp, coldept, src_sprwidth, src_sprheight);
 			}
 
 			_G(our_eip) = 335;
@@ -1641,14 +1641,13 @@ void prepare_characters_for_drawing() {
 				                    comeFrom);
 			} else if (!actspsUsed) {
 				// no scaling, flipping or tinting was done, so just blit it normally
-				_GP(actsps)[useindx]->Blit(_GP(spriteset)[sppic], 0, 0);
+				actsp.Bmp->Blit(_GP(spriteset)[sppic], 0, 0);
 			}
 
 			// update the character cache with the new image
 			_G(charcache)[aa].inUse = 1;
-			//_G(charcache)[aa].image = BitmapHelper::CreateBitmap_ (coldept, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
-			_G(charcache)[aa].image = recycle_bitmap(_G(charcache)[aa].image, coldept, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
-			_G(charcache)[aa].image->Blit(_GP(actsps)[useindx], 0, 0, 0, 0, _GP(actsps)[useindx]->GetWidth(), _GP(actsps)[useindx]->GetHeight());
+			_G(charcache)[aa].image = recycle_bitmap(_G(charcache)[aa].image, coldept, actsp.Bmp->GetWidth(), actsp.Bmp->GetHeight());
+			_G(charcache)[aa].image->Blit(actsp.Bmp.get(), 0, 0);
 
 		} // end if !cache.inUse
 
@@ -1665,31 +1664,30 @@ void prepare_characters_for_drawing() {
 				usebasel += _GP(thisroom).Height;
 			}
 		} else if (_G(walkBehindMethod) == DrawOverCharSprite) {
-			sort_out_walk_behinds(_GP(actsps)[useindx], bgX, bgY, usebasel);
+			sort_out_walk_behinds(actsp.Bmp.get(), bgX, bgY, usebasel);
 		}
 
-		if ((!usingCachedImage) || (_GP(actspsbmp)[useindx] == nullptr)) {
-			bool hasAlpha = (_GP(game).SpriteInfos[sppic].Flags & SPF_ALPHACHANNEL) != 0;
-
-			_GP(actspsbmp)[useindx] = recycle_ddb_bitmap(_GP(actspsbmp)[useindx], _GP(actsps)[useindx], hasAlpha);
+		if ((!usingCachedImage) || (actsp.Ddb == nullptr)) {
+			sync_object_texture(actsp, (_GP(game).SpriteInfos[sppic].Flags & SPF_ALPHACHANNEL) != 0);
 		}
 
 		if (_G(gfxDriver)->HasAcceleratedTransform()) {
-			_GP(actspsbmp)[useindx]->SetStretch(newwidth, newheight);
-			_GP(actspsbmp)[useindx]->SetFlippedLeftRight(isMirrored);
-			_GP(actspsbmp)[useindx]->SetTint(tint_red, tint_green, tint_blue, (tint_amount * 256) / 100);
+			actsp.Ddb->SetStretch(newwidth, newheight);
+			actsp.Ddb->SetFlippedLeftRight(isMirrored);
+			actsp.Ddb->SetTint(tint_red, tint_green, tint_blue, (tint_amount * 256) / 100);
 
 			if (tint_amount != 0) {
 				if (tint_light == 0) // tint with 0 luminance, pass as 1 instead
-					_GP(actspsbmp)[useindx]->SetLightLevel(1);
+					actsp.Ddb->SetLightLevel(1);
 				else if (tint_light < 250)
-					_GP(actspsbmp)[useindx]->SetLightLevel(tint_light);
+					actsp.Ddb->SetLightLevel(tint_light);
 				else
-					_GP(actspsbmp)[useindx]->SetLightLevel(0);
+					actsp.Ddb->SetLightLevel(0);
 			} else if (light_level != 0)
-				_GP(actspsbmp)[useindx]->SetLightLevel((light_level * 25) / 10 + 256);
+				actsp.Ddb->SetLightLevel((light_level * 25) / 10 + 256);
 			else
-				_GP(actspsbmp)[useindx]->SetLightLevel(0);
+				actsp.Ddb->SetLightLevel(0);
+
 		}
 
 		_G(our_eip) = 337;
@@ -1697,19 +1695,20 @@ void prepare_characters_for_drawing() {
 		chin->actx = atxp;
 		chin->acty = atyp;
 
-		_GP(actspsbmp)[useindx]->SetAlpha(GfxDef::LegacyTrans255ToAlpha255(chin->transparency));
-		add_to_sprite_list(_GP(actspsbmp)[useindx], bgX, bgY, usebasel, false);
+		actsp.Ddb->SetAlpha(GfxDef::LegacyTrans255ToAlpha255(chin->transparency));
+		add_to_sprite_list(actsp.Ddb, bgX, bgY, usebasel, false);
 	}
 }
 
-Shared::Bitmap *get_cached_character_image(int charid) {
-	return _GP(actsps)[charid + MAX_ROOM_OBJECTS];
+Bitmap *get_cached_character_image(int charid) {
+	return _GP(actsps)[charid + MAX_ROOM_OBJECTS].Bmp.get();
 }
 
-Shared::Bitmap *get_cached_object_image(int objid) {
-	return _GP(actsps)[objid];
+Bitmap *get_cached_object_image(int objid) {
+	return _GP(actsps)[objid].Bmp.get();
 }
 
+
 // Compiles a list of room sprites (characters, objects, background)
 void prepare_room_sprites() {
 	// Background sprite is required for the non-software renderers always,
@@ -1760,10 +1759,10 @@ void prepare_room_sprites() {
 
 	// Debug room overlay
 	update_room_debug();
-	if ((_G(debugRoomMask) != kRoomAreaNone) && _G(debugRoomMaskDDB))
-		add_thing_to_draw(_G(debugRoomMaskDDB), 0, 0);
-	if ((_G(debugMoveListChar) >= 0) && _G(debugMoveListDDB))
-		add_thing_to_draw(_G(debugMoveListDDB), 0, 0);
+	if ((_G(debugRoomMask) != kRoomAreaNone) && _GP(debugRoomMaskObj).Ddb)
+		add_thing_to_draw(_GP(debugRoomMaskObj).Ddb, 0, 0);
+	if ((_G(debugMoveListChar) >= 0) && _GP(debugMoveListObj).Ddb)
+		add_thing_to_draw(_GP(debugMoveListObj).Ddb, 0, 0);
 }
 
 // Draws the black surface behind (or rather between) the room viewports
@@ -1869,15 +1868,13 @@ void draw_gui_controls(GUIMain &gui) {
 		if (!obj->HasChanged())
 			continue;
 
-		auto *&objbg_bmp = _GP(guiobjbg)[draw_index];
-		auto *&objbg_ddb = _GP(guiobjddb)[draw_index];
+		auto &objbg = _GP(guiobjbg)[draw_index];
 		Rect obj_surf = obj->CalcGraphicRect(GUI::Options.ClipControls);
-		objbg_bmp = recycle_bitmap(objbg_bmp, _GP(game).GetColorDepth(), obj_surf.GetWidth(), obj_surf.GetHeight());
-		objbg_bmp->ClearTransparent();
-		obj->Draw(objbg_bmp, obj->X - obj_surf.Left, obj->Y - obj_surf.Top);
+		recycle_bitmap(objbg.Bmp, _GP(game).GetColorDepth(), obj_surf.GetWidth(), obj_surf.GetHeight(), true);
+		obj->Draw(objbg.Bmp.get(), obj->X - obj_surf.Left, obj->Y - obj_surf.Top);
 
-		objbg_ddb = recycle_ddb_bitmap(objbg_ddb, objbg_bmp, obj->HasAlphaChannel());
-		_GP(guiobjoff)[draw_index] = Point(obj_surf.GetLT());
+		sync_object_texture(objbg, obj->HasAlphaChannel());
+		objbg.Pos = Point(obj_surf.GetLT());
 		obj->ClearChanged();
 	}
 }
@@ -1937,29 +1934,27 @@ void draw_gui_and_overlays() {
 				if (!gui.HasChanged() && !gui.HasControlsChanged()) continue; // no changes: no need to update image
 				if (gui.Transparency == 255) continue; // 100% transparent
 
-				auto *&guibg_bmp = _GP(guibg)[index];
-				auto *&guibg_ddb = _GP(guibgddb)[index];
-				guibg_bmp = recycle_bitmap(guibg_bmp, _GP(game).GetColorDepth(), gui.Width, gui.Height);
-
 				_G(eip_guinum) = index;
 				_G(our_eip) = 372;
 				const bool draw_with_controls = !draw_controls_as_textures;
 				if (gui.HasChanged() || (draw_with_controls && gui.HasControlsChanged())) {
-					guibg_bmp->ClearTransparent();
-					if (draw_with_controls)
-						gui.DrawWithControls(guibg_bmp);
-					else
-						gui.DrawSelf(guibg_bmp);
-
-					const bool is_alpha = gui.HasAlphaChannel();
-					if (is_alpha) {
-						if ((_GP(game).options[OPT_NEWGUIALPHA] == kGuiAlphaRender_Legacy) && (gui.BgImage > 0)) {
-							// old-style (pre-3.0.2) GUI alpha rendering
-							repair_alpha_channel(guibg_bmp, _GP(spriteset)[gui.BgImage]);
-						}
-					}
-
-					guibg_ddb = recycle_ddb_bitmap(guibg_ddb, guibg_bmp, is_alpha);
+					auto &gbg = _GP(guibg)[index];
+                    recycle_bitmap(gbg.Bmp, _GP(game).GetColorDepth(), gui.Width, gui.Height, true);
+                    if (draw_with_controls)
+                        gui.DrawWithControls(gbg.Bmp.get());
+                    else
+                        gui.DrawSelf(gbg.Bmp.get());
+
+                    const bool is_alpha = gui.HasAlphaChannel();
+                    if (is_alpha)
+                    {
+                        if ((_GP(game).options[OPT_NEWGUIALPHA] == kGuiAlphaRender_Legacy) && (gui.BgImage > 0))
+                        {
+                            // old-style (pre-3.0.2) GUI alpha rendering
+                            repair_alpha_channel(gbg.Bmp.get(), _GP(spriteset)[gui.BgImage]);
+                        }
+                    }
+                    sync_object_texture(gbg, is_alpha);
 				}
 
 				_G(our_eip) = 373;
@@ -1984,7 +1979,7 @@ void draw_gui_and_overlays() {
 				(gui.PopupStyle != kGUIPopupNoAutoRemove))
 				continue;
 
-			auto *gui_ddb = _GP(guibgddb)[index];
+			auto *gui_ddb = _GP(guibg)[index].Ddb;
 			assert(gui_ddb); // Test for missing texture, might happen if not marked for update
 			if (!gui_ddb) continue;
 			gui_ddb->SetAlpha(GfxDef::LegacyTrans255ToAlpha255(gui.Transparency));
@@ -2031,13 +2026,13 @@ void draw_gui_and_overlays() {
 				(obj->Width <= 0 || obj->Height <= 0) ||
 				(!obj->IsEnabled() && (GUI::Options.DisabledStyle == kGuiDis_Blackout)))
 				continue;
-			auto *obj_ddb = _GP(guiobjddb)[draw_index + obj_id];
+			auto *obj_ddb = _GP(guiobjbg)[draw_index + obj_id].Ddb;
 			assert(obj_ddb); // Test for missing texture, might happen if not marked for update
 			if (!obj_ddb) continue;
 			obj_ddb->SetAlpha(GfxDef::LegacyTrans255ToAlpha255(obj->GetTransparency()));
 			_G(gfxDriver)->DrawSprite(
-				_GP(guiobjoff)[draw_index + obj_id].X,
-				_GP(guiobjoff)[draw_index + obj_id].Y,
+				_GP(guiobjbg)[draw_index + obj_id].Pos.X,
+				_GP(guiobjbg)[draw_index + obj_id].Pos.Y,
 				obj_ddb);
 		}
 		_G(gfxDriver)->EndSpriteBatch();
@@ -2308,15 +2303,15 @@ void debug_draw_room_mask(RoomAreaMask mask) {
 	if (!_G(gfxDriver)->HasAcceleratedTransform() &&
 		(mask != kRoomAreaWalkBehind) &&
 		(bmp->GetSize() != Size(_GP(thisroom).Width, _GP(thisroom).Height))) {
-		_GP(debugRoomMaskBmp).reset(recycle_bitmap(_GP(debugRoomMaskBmp).release(),
-			bmp->GetColorDepth(), _GP(thisroom).Width, _GP(thisroom).Height));
-		_GP(debugRoomMaskBmp)->StretchBlt(bmp, RectWH(0, 0, _GP(thisroom).Width, _GP(thisroom).Height));
-		bmp = _GP(debugRoomMaskBmp).get();
+		recycle_bitmap(_GP(debugRoomMaskObj).Bmp,
+			bmp->GetColorDepth(), _GP(thisroom).Width, _GP(thisroom).Height);
+		_GP(debugRoomMaskObj).Bmp->StretchBlt(bmp, RectWH(0, 0, _GP(thisroom).Width, _GP(thisroom).Height));
+		bmp = _GP(debugRoomMaskObj).Bmp.get();
 	}
 
-	_G(debugRoomMaskDDB) = recycle_ddb_bitmap(_G(debugRoomMaskDDB), bmp, false, true);
-	_G(debugRoomMaskDDB)->SetAlpha(150);
-	_G(debugRoomMaskDDB)->SetStretch(_GP(thisroom).Width, _GP(thisroom).Height);
+	_GP(debugRoomMaskObj).Ddb = recycle_ddb_bitmap(_GP(debugRoomMaskObj).Ddb, bmp, false, true);
+	_GP(debugRoomMaskObj).Ddb->SetAlpha(150);
+	_GP(debugRoomMaskObj).Ddb->SetStretch(_GP(thisroom).Width, _GP(thisroom).Height);
 }
 
 void debug_draw_movelist(int charnum) {
@@ -2328,23 +2323,23 @@ void update_room_debug() {
 		Bitmap *bmp = prepare_walkable_areas(-1);
 		// Software mode scaling
 		if (!_G(gfxDriver)->HasAcceleratedTransform() && (_GP(thisroom).MaskResolution > 1)) {
-			_GP(debugRoomMaskBmp).reset(recycle_bitmap(_GP(debugRoomMaskBmp).release(),
-				bmp->GetColorDepth(), _GP(thisroom).Width, _GP(thisroom).Height));
-			_GP(debugRoomMaskBmp)->StretchBlt(bmp, RectWH(0, 0, _GP(thisroom).Width, _GP(thisroom).Height));
-			bmp = _GP(debugRoomMaskBmp).get();
+			recycle_bitmap(_GP(debugRoomMaskObj).Bmp,
+				bmp->GetColorDepth(), _GP(thisroom).Width, _GP(thisroom).Height);
+			_GP(debugRoomMaskObj).Bmp->StretchBlt(bmp, RectWH(0, 0, _GP(thisroom).Width, _GP(thisroom).Height));
+			bmp = _GP(debugRoomMaskObj).Bmp.get();
 		}
-		_G(debugRoomMaskDDB) = recycle_ddb_bitmap(_G(debugRoomMaskDDB), bmp, false, true);
-		_G(debugRoomMaskDDB)->SetAlpha(150);
-		_G(debugRoomMaskDDB)->SetStretch(_GP(thisroom).Width, _GP(thisroom).Height);
+		_GP(debugRoomMaskObj).Ddb = recycle_ddb_bitmap(_GP(debugRoomMaskObj).Ddb, bmp, false, true);
+		_GP(debugRoomMaskObj).Ddb->SetAlpha(150);
+		_GP(debugRoomMaskObj).Ddb->SetStretch(_GP(thisroom).Width, _GP(thisroom).Height);
 	}
 	if (_G(debugMoveListChar) >= 0) {
 		const int mult = _G(gfxDriver)->HasAcceleratedTransform() ? _GP(thisroom).MaskResolution : 1;
 		if (_G(gfxDriver)->HasAcceleratedTransform())
-			_GP(debugMoveListBmp).reset(recycle_bitmap(_GP(debugMoveListBmp).release(), _GP(game).GetColorDepth(),
-				_GP(thisroom).WalkAreaMask->GetWidth(), _GP(thisroom).WalkAreaMask->GetHeight(), true));
+			recycle_bitmap(_GP(debugMoveListObj).Bmp, _GP(game).GetColorDepth(),
+				_GP(thisroom).WalkAreaMask->GetWidth(), _GP(thisroom).WalkAreaMask->GetHeight(), true);
 		else
-			_GP(debugMoveListBmp).reset(recycle_bitmap(_GP(debugMoveListBmp).release(), _GP(game).GetColorDepth(),
-				_GP(thisroom).Width, _GP(thisroom).Height, true));
+			recycle_bitmap(_GP(debugMoveListObj).Bmp, _GP(game).GetColorDepth(),
+				_GP(thisroom).Width, _GP(thisroom).Height, true);
 
 		if (_GP(game).chars[_G(debugMoveListChar)].walking > 0) {
 			int mlsnum = _GP(game).chars[_G(debugMoveListChar)].walking;
@@ -2356,13 +2351,13 @@ void update_room_debug() {
 				short srcy = short(cmls.pos[i] & 0x00ffff);
 				short targetx = short((cmls.pos[i + 1] >> 16) & 0x00ffff);
 				short targety = short(cmls.pos[i + 1] & 0x00ffff);
-				_GP(debugMoveListBmp)->DrawLine(Line(srcx / mult, srcy / mult, targetx / mult, targety / mult),
+				_GP(debugMoveListObj).Bmp->DrawLine(Line(srcx / mult, srcy / mult, targetx / mult, targety / mult),
 					MakeColor(i + 1));
 			}
 		}
-		_G(debugMoveListDDB) = recycle_ddb_bitmap(_G(debugMoveListDDB), _GP(debugMoveListBmp).get(), false, false);
-		_G(debugRoomMaskDDB)->SetAlpha(150);
-		_G(debugMoveListDDB)->SetStretch(_GP(thisroom).Width, _GP(thisroom).Height);
+		sync_object_texture(_GP(debugMoveListObj));
+		_GP(debugMoveListObj).Ddb->SetAlpha(150);
+		_GP(debugMoveListObj).Ddb->SetStretch(_GP(thisroom).Width, _GP(thisroom).Height);
 	}
 }
 
diff --git a/engines/ags/engine/ac/draw.h b/engines/ags/engine/ac/draw.h
index fb1648a841b..4ec6e5599a7 100644
--- a/engines/ags/engine/ac/draw.h
+++ b/engines/ags/engine/ac/draw.h
@@ -26,6 +26,7 @@
 #include "ags/shared/core/types.h"
 #include "ags/shared/ac/common_defines.h"
 #include "ags/shared/gfx/gfx_def.h"
+#include "ags/shared/gfx/allegro_bitmap.h"
 #include "ags/shared/gfx/bitmap.h"
 #include "ags/shared/game/room_struct.h"
 
@@ -59,6 +60,28 @@ struct RoomCameraDrawData {
 	bool    IsOverlap;   // whether room viewport overlaps any others (marking dirty rects is complicated)
 };
 
+// ObjTexture is a helper struct that pairs a raw bitmap with
+// a renderer's texture and an optional position
+struct ObjTexture {
+	// Raw bitmap
+	std::unique_ptr<Shared::Bitmap> Bmp;
+	// Corresponding texture, created by renderer
+	Engine::IDriverDependantBitmap *Ddb = nullptr;
+	// Sprite's position, may be used in case the texture's pos is different
+	// from the object's logical position (x,y,w,h) for some reason.
+	Point Pos;
+
+	ObjTexture() = default;
+	ObjTexture(Shared::Bitmap *bmp, Engine::IDriverDependantBitmap *ddb, int x, int y)
+		: Bmp(bmp), Ddb(ddb), Pos(x, y) {
+	}
+	ObjTexture(const ObjTexture &) = default;
+	ObjTexture(ObjTexture &&o);
+	~ObjTexture();
+
+	ObjTexture &operator =(ObjTexture &&o);
+};
+
 // Converts AGS color index to the actual bitmap color using game's color depth
 int MakeColor(int color_index);
 
@@ -108,7 +131,8 @@ void mark_current_background_dirty();
 
 // Avoid freeing and reallocating the memory if possible
 Shared::Bitmap *recycle_bitmap(Shared::Bitmap *bimp, int coldep, int wid, int hit, bool make_transparent = false);
-Engine::IDriverDependantBitmap *recycle_ddb_bitmap(Engine::IDriverDependantBitmap *bimp, Shared::Bitmap *source, bool hasAlpha = false, bool opaque = false);
+void recycle_bitmap(std::unique_ptr<Shared::Bitmap> &bimp, int coldep, int wid, int hit, bool make_transparent = false);
+Engine::IDriverDependantBitmap *recycle_ddb_bitmap(Engine::IDriverDependantBitmap *ddb, Shared::Bitmap *source, bool has_alpha = false, bool opaque = false);
 // Draw everything
 void render_graphics(Engine::IDriverDependantBitmap *extraBitmap = nullptr, int extraX = 0, int extraY = 0);
 // Construct game scene, scheduling drawing list for the renderer
@@ -137,7 +161,7 @@ void draw_gui_sprite(Shared::Bitmap *ds, bool use_alpha, int xpos, int ypos,
 // Generates a transformed sprite, using src image and parameters;
 // * if transformation is necessary - writes into dst and returns dst;
 // * if no transformation is necessary - simply returns src;
-Shared::Bitmap *transform_sprite(Shared::Bitmap *src, bool src_has_alpha, Shared::Bitmap *&dst,
+Shared::Bitmap *transform_sprite(Shared::Bitmap *src, bool src_has_alpha, std::unique_ptr<Shared::Bitmap> &dst,
 	const Size dst_sz, Shared::BitmapFlip flip = Shared::kBitmap_NoFlip);
 // Render game on screen
 void render_to_screen();
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 25e1b264d15..11b27cf73f3 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -882,14 +882,11 @@ void save_game(int slotn, const char *descript) {
 
 	VALIDATE_STRING(descript);
 	String nametouse = get_save_game_path(slotn);
-	UBitmap screenShot;
-
-	// WORKAROUND: AGS originally only creates savegames if the game flags
-	// that it supports it. But we want it all the time for ScummVM GMM
-	if (/*_GP(game).options[OPT_SAVESCREENSHOT] != 0*/ true)
+	std::unique_ptr<Bitmap> screenShot;
+	if (_GP(game).options[OPT_SAVESCREENSHOT] != 0)
 		screenShot.reset(create_savegame_screenshot());
 
-	Engine::UStream out(StartSavegame(nametouse, descript, screenShot.get()));
+	std::unique_ptr<Stream> out(StartSavegame(nametouse, descript, screenShot.get()));
 	if (out == nullptr) {
 		Display("ERROR: Unable to open savegame file for writing!");
 		return;
diff --git a/engines/ags/engine/ac/game_state.h b/engines/ags/engine/ac/game_state.h
index 7f31420a9be..f087b92707e 100644
--- a/engines/ags/engine/ac/game_state.h
+++ b/engines/ags/engine/ac/game_state.h
@@ -227,7 +227,7 @@ struct GameState {
 	short temporarily_turned_off_character = 0;  // Hide Player Charactr ticked
 	short inv_backwards_compatibility = 0;
 	std::vector<int> gui_draw_order; // used only for hit detection now
-	std::vector<AGS::Shared::String> do_once_tokens = 0;
+	std::vector<AGS::Shared::String> do_once_tokens;
 	int   text_min_display_time_ms = 0;
 	int   ignore_user_input_after_text_timeout_ms = 0;
 	int32_t default_audio_type_volumes[MAX_AUDIO_TYPES];
diff --git a/engines/ags/engine/ac/global_object.cpp b/engines/ags/engine/ac/global_object.cpp
index 05ec64bc1a5..37091da362f 100644
--- a/engines/ags/engine/ac/global_object.cpp
+++ b/engines/ags/engine/ac/global_object.cpp
@@ -521,12 +521,12 @@ void GetObjectPropertyText(int item, const char *property, char *bufer) {
 
 Bitmap *GetObjectImage(int obj, int *isFlipped) {
 	if (!_G(gfxDriver)->HasAcceleratedTransform()) {
-		if (_GP(actsps)[obj] != nullptr) {
-			// the actsps image is pre-flipped, so no longer register the image as such
+		Bitmap *actsp = get_cached_object_image(obj);
+		if (actsp) {
+			// the cached image is pre-flipped, so no longer register the image as such
 			if (isFlipped)
 				*isFlipped = 0;
-
-			return _GP(actsps)[obj];
+			return actsp;
 		}
 	}
 	return _GP(spriteset)[_G(objs)[obj].num];
diff --git a/engines/ags/engine/game/savegame.h b/engines/ags/engine/game/savegame.h
index 9d6d9587f68..47b76788a31 100644
--- a/engines/ags/engine/game/savegame.h
+++ b/engines/ags/engine/game/savegame.h
@@ -97,13 +97,6 @@ String GetSavegameErrorText(SavegameErrorType err);
 
 typedef TypedCodeError<SavegameErrorType, GetSavegameErrorText> SavegameError;
 typedef ErrorHandle<SavegameError> HSaveError;
-typedef std::unique_ptr<Bitmap> UBitmap;
-#ifdef UNUSED_AGS_PLATFORM_SCUMMVM
-typedef std::shared_ptr<Stream> UStream;
-#else
-typedef std::unique_ptr<Stream> UStream;
-#endif
-
 
 // SavegameSource defines a successfully opened savegame stream
 struct SavegameSource {
@@ -117,7 +110,7 @@ struct SavegameSource {
 	// Savegame format version
 	SavegameVersion     Version;
 	// A ponter to the opened stream
-	UStream             InputStream;
+	std::unique_ptr<Stream> InputStream;
 
 	SavegameSource();
 };
@@ -156,7 +149,7 @@ struct SavegameDescription {
 	int                 ColorDepth;
 
 	String              UserText;
-	UBitmap             UserImage;
+	std::unique_ptr<Bitmap> UserImage;
 
 	SavegameDescription();
 };
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index 4f15ea49e56..fbe3dd24b00 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -173,23 +173,22 @@ Globals::Globals() {
 	Common::fill(_dynamicallyCreatedSurfaces, _dynamicallyCreatedSurfaces +
 	             MAX_DYNAMIC_SURFACES, (AGS::Shared::Bitmap *)nullptr);
 
-	_actsps = new std::vector<Shared::Bitmap *>();
-	_actspsbmp = new std::vector<Engine::IDriverDependantBitmap *>();
-	_guibg = new std::vector<Shared::Bitmap *>();
-	_guibgddb = new std::vector<Engine::IDriverDependantBitmap *>();
-	_debugRoomMaskBmp = new std::unique_ptr<Shared::Bitmap>();
-	_debugMoveListBmp = new std::unique_ptr<Shared::Bitmap>();
+	_actsps = new std::vector<ObjTexture>();
+	_guibg = new std::vector<ObjTexture>();
+	_guiobjbg = new std::vector<ObjTexture>();
+
+	_guiobjddb = new std::vector<Engine::IDriverDependantBitmap *>();
+	_guiobjoff = new std::vector<Point>();
+	_guiobjddbref = new std::vector<int>();
+	_overlaybmp = new std::vector<std::unique_ptr<Shared::Bitmap> >();
+	_debugRoomMaskObj =  new ObjTexture();
+	_debugMoveListObj = new ObjTexture();
 
 	_maincoltable = new COLOR_MAP();
 	_palette = new color[256];
 	for (int i = 0; i < PALETTE_COUNT; ++i)
 		_palette[i].clear();
 
-	_guiobjbg = new std::vector<Shared::Bitmap *>();
-	_guiobjddb = new std::vector<Engine::IDriverDependantBitmap *>();
-	_guiobjoff = new std::vector<Point>();
-	_guiobjddbref = new std::vector<int>();
-	_overlaybmp = new std::vector<Shared::Bitmap *>();
 
 	// draw_software.cpp globals
 	_BlackRects = new DirtyRects();
@@ -429,19 +428,18 @@ Globals::~Globals() {
 	delete _sprlist;
 	delete _thingsToDrawList;
 	delete _actsps;
-	delete _actspsbmp;
 	delete _guibg;
-	delete _guibgddb;
-	delete _debugRoomMaskBmp;
-	delete _debugMoveListBmp;
-	delete[] _dynamicallyCreatedSurfaces;
-	delete[] _palette;
-	delete _maincoltable;
 	delete _guiobjbg;
+	delete _guiobjddbref;
 	delete _guiobjddb;
 	delete _guiobjoff;
-	delete _guiobjddbref;
 	delete _overlaybmp;
+	delete _debugRoomMaskObj;
+	delete _debugMoveListObj;
+
+	delete[] _dynamicallyCreatedSurfaces;
+	delete[] _palette;
+	delete _maincoltable;
 
 	// draw_software.cpp globals
 	delete _BlackRects;
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index a34069c72dd..52aa0442ffb 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -578,19 +578,20 @@ public:
 
 	// actsps is used for temporary storage of the bitamp image
 	// of the latest version of the sprite
-	std::vector<Shared::Bitmap *> *_actsps;
-	std::vector<Engine::IDriverDependantBitmap *> *_actspsbmp;
-	// temporary cache of walk-behind for this actsps image
+	std::vector<ObjTexture> *_actsps;
 	// GUI surfaces
-	std::vector<Shared::Bitmap *> *_guibg;
-	std::vector<Engine::IDriverDependantBitmap *> *_guibgddb;
+	std::vector<ObjTexture> *_guibg;
+	// GUI control surfaces
+	std::vector<ObjTexture> *_guiobjbg;
+	// first control texture index of each GUI
+	std::vector<int> *_guiobjddbref;
+	// Overlay's cached transformed bitmap, for software mode
+	std::vector<std::unique_ptr<Shared::Bitmap> > *_overlaybmp;
 	// For debugging room masks
+	ObjTexture *_debugRoomMaskObj;
+	ObjTexture *_debugMoveListObj;
 	RoomAreaMask _debugRoomMask = kRoomAreaNone;
-	std::unique_ptr<Shared::Bitmap> *_debugRoomMaskBmp;
-	Engine::IDriverDependantBitmap *_debugRoomMaskDDB = nullptr;
 	int _debugMoveListChar = -1;
-	std::unique_ptr<Shared::Bitmap> *_debugMoveListBmp;
-	Engine::IDriverDependantBitmap *_debugMoveListDDB = nullptr;
 
 	bool _current_background_is_dirty = false;
 	// Room background sprite
@@ -604,13 +605,8 @@ public:
 	color *_palette;
 	COLOR_MAP *_maincoltable;
 
-	// GUI control surfaces
-	std::vector<Shared::Bitmap *> *_guiobjbg;
 	std::vector<Engine::IDriverDependantBitmap *> *_guiobjddb;
 	std::vector<Point> *_guiobjoff; // because surface may be larger than logical position
-	std::vector<int> *_guiobjddbref; // first control texture index of each GUI
-	// Overlay's cached transformed bitmap, for software mode
-	std::vector<Shared::Bitmap *> *_overlaybmp;
 
 	/**@}*/
 
diff --git a/engines/ags/lib/std/utility.h b/engines/ags/lib/std/utility.h
index d701a3c3dd5..d50acfa85c6 100644
--- a/engines/ags/lib/std/utility.h
+++ b/engines/ags/lib/std/utility.h
@@ -46,7 +46,17 @@ pair<T1, T2> make_pair(T1 first, T2 second) {
 // STRUCT TEMPLATE remove_reference
 template <class _Ty>
 struct remove_reference {
-	using type = _Ty;
+	typedef _Ty type;
+};
+
+template<class _Ty>
+struct remove_reference<_Ty &> {
+	typedef _Ty type;
+};
+
+template<class _Ty>
+struct remove_reference<_Ty &&> {
+	typedef _Ty type;
 };
 
 template <class _Ty>
diff --git a/engines/ags/lib/std/vector.h b/engines/ags/lib/std/vector.h
index 06e93f800e8..340a7b06138 100644
--- a/engines/ags/lib/std/vector.h
+++ b/engines/ags/lib/std/vector.h
@@ -22,13 +22,40 @@
 #ifndef AGS_STD_VECTOR_H
 #define AGS_STD_VECTOR_H
 
-#include "common/array.h"
+#include "ags/lib/std/utility.h"
+#include "common/scummsys.h"
+#include "common/algorithm.h"
+#include "common/memory.h"
+#include "common/initializer_list.h"
 
 namespace AGS3 {
 namespace std {
 
+template<class In, class Type>
+Type *uninitialized_move(In first, In last, Type *dst) {
+	while (first != last) {
+		Type &t = *new ((void *)dst++) Type();
+		t = std::move(*first++);
+	}
+
+	return dst;
+}
+
 template<class T>
-class vector : public Common::Array<T> {
+class vector {
+public:
+	typedef T *iterator; /*!< vector iterator. */
+	typedef const T *const_iterator; /*!< Const-qualified array iterator. */
+
+	typedef T value_type; /*!< Value type of the array. */
+
+	typedef uint size_type; /*!< Size type of the array. */
+
+protected:
+	size_type _capacity; /*!< Maximum number of elements the array can hold. */
+	size_type _size; /*!< How many elements the array holds. */
+	T *_storage;  /*!< Memory used for element storage. */
+
 public:
 	struct reverse_iterator {
 	private:
@@ -86,28 +113,256 @@ public:
 			return _index > rhs._index;
 		}
 	};
-
-	using iterator = typename Common::Array<T>::iterator;
-	using const_iterator = typename Common::Array<T>::const_iterator;
 public:
-	typedef T reference;
-	typedef const T const_reference;
+	vector() : _capacity(0), _size(0), _storage(nullptr) {
+	}
 
-	vector() : Common::Array<T>() {
+	/**
+	 * Construct an array with @p count default-inserted instances of @p T. No
+	 * copies are made.
+	 */
+	explicit vector(size_type count) : _size(count) {
+		allocCapacity(count);
+		for (size_type i = 0; i < count; ++i)
+			new ((void *)&_storage[i]) T();
 	}
-	vector(size_t newSize) : Common::Array<T>() {
-		Common::Array<T>::resize(newSize);
+
+	/**
+	 * Construct an array with @p count copies of elements with value @p value.
+	 */
+	vector(size_type count, const T &value) : _size(count) {
+		allocCapacity(count);
+		Common::uninitialized_fill_n(_storage, count, value);
 	}
-	vector(size_t newSize, const T elem) {
-		resize(newSize, elem);
+
+	/**
+	 * Construct an array as a copy of the given @p array.
+	 */
+	vector(const vector<T> &array) : _capacity(array._size), _size(array._size), _storage(nullptr) {
+		if (array._storage) {
+			allocCapacity(_size);
+			Common::uninitialized_copy(array._storage, array._storage + _size, _storage);
+		}
 	}
 
+	/**
+	 * Construct an array as a copy of the given array using the C++11 move semantic.
+	 */
+	vector(vector<T> &&old) : _capacity(old._capacity), _size(old._size), _storage(old._storage) {
+		old._storage = nullptr;
+		old._capacity = 0;
+		old._size = 0;
+	}
+
+	/**
+	 * Construct an array using list initialization.
+	 * For example:
+	 * @code
+	 * Common::vector<int> myArray = {1, 7, 42};
+	 * @endcode
+	 * constructs an array with 3 elements whose values are 1, 7, and 42 respectively.
+	 * @note
+	 * This constructor is only available when C++11 support is enabled.
+	 */
+	vector(::std::initializer_list<T> list) : _size(list.size()) {
+		allocCapacity(list.size());
+		if (_storage)
+			Common::uninitialized_copy(list.begin(), list.end(), _storage);
+	}
+
+	/**
+	 * Construct an array by copying data from a regular array.
+	 */
+	template<class T2>
+	vector(const T2 *array, size_type n) {
+		_size = n;
+		allocCapacity(n);
+		Common::uninitialized_copy(array, array + _size, _storage);
+	}
+
+	~vector() {
+		freeStorage(_storage, _size);
+		_storage = nullptr;
+		_capacity = _size = 0;
+	}
+
+	/** Append an element to the end of the array. */
+	void push_back(const T &element) {
+		if (_size + 1 <= _capacity)
+			new ((void *)&_storage[_size++]) T(element);
+		else
+			insert_aux(end(), &element, &element + 1);
+	}
+
+	/** Append an element to the end of the array. */
+	void push_back(const vector<T> &array) {
+		if (_size + array.size() <= _capacity) {
+			Common::uninitialized_copy(array.begin(), array.end(), end());
+			_size += array.size();
+		} else
+			insert_aux(end(), array.begin(), array.end());
+	}
+
+	void insert(const T &element) {
+		this->push_back(element);
+	}
+
+	/**
+	 * Adds a range of items at the specified position in the array
+	 */
+	void insert(iterator position, const_iterator first, const_iterator last) {
+		int destIndex = position - this->begin();
+		for (; first != last; ++first) {
+			insert_at(destIndex++, *first);
+		}
+	}
+
+	/** Remove the last element of the array. */
+	void pop_back() {
+		assert(_size > 0);
+		_size--;
+		// We also need to destroy the last object properly here.
+		_storage[_size].~T();
+	}
+
+	/** Return a pointer to the underlying memory serving as element storage. */
+	const T *data() const {
+		return _storage;
+	}
+
+	/** Return a pointer to the underlying memory serving as element storage. */
+	T *data() {
+		return _storage;
+	}
+
+	/** Return a reference to the first element of the array. */
+	T &front() {
+		assert(_size > 0);
+		return _storage[0];
+	}
+
+	/** Return a reference to the first element of the array. */
+	const T &front() const {
+		assert(_size > 0);
+		return _storage[0];
+	}
+
+	/** Return a reference to the last element of the array. */
+	T &back() {
+		assert(_size > 0);
+		return _storage[_size - 1];
+	}
+
+	/** Return a reference to the last element of the array. */
+	const T &back() const {
+		assert(_size > 0);
+		return _storage[_size - 1];
+	}
+
+	/** Insert an element into the array at the given position. */
+	void insert_at(size_type idx, const T &element) {
+		assert(idx <= _size);
+		insert_aux(_storage + idx, &element, &element + 1);
+	}
+
+	/** Insert copies of all the elements from the given array into this array at the given position. */
+	void insert_at(size_type idx, const vector<T> &array) {
+		assert(idx <= _size);
+		insert_aux(_storage + idx, array.begin(), array.end());
+	}
+
+	/**
+	 * Insert an element before @p pos.
+	 */
+	void insert(iterator pos, const T &element) {
+		insert_aux(pos, &element, &element + 1);
+	}
+
+	/** Remove an element at the given position from the array and return the value of that element. */
+	T remove_at(size_type idx) {
+		assert(idx < _size);
+		T tmp = _storage[idx];
+		Common::copy(_storage + idx + 1, _storage + _size, _storage + idx);
+		_size--;
+		// We also need to destroy the last object properly here.
+		_storage[_size].~T();
+		return tmp;
+	}
+
+	// TODO: insert, remove, ...
+
+	T &at(size_t index) {
+		return (*this)[index];
+	}
+	const T &at(size_t index) const {
+		return (*this)[index];
+	}
+
+	/** Return a reference to the element at the given position in the array. */
+	T &operator[](size_type idx) {
+		assert(idx < _size);
+		return _storage[idx];
+	}
+
+	/** Return a const reference to the element at the given position in the array. */
+	const T &operator[](size_type idx) const {
+		assert(idx < _size);
+		return _storage[idx];
+	}
+
+	/** Assign the given @p array to this array. */
+	vector<T> &operator=(const vector<T> &array) {
+		if (this == &array)
+			return *this;
+
+		freeStorage(_storage, _size);
+		_size = array._size;
+		allocCapacity(_size);
+		Common::uninitialized_copy(array._storage, array._storage + _size, _storage);
+
+		return *this;
+	}
+
+	/** Assign the given array to this array using the C++11 move semantic. */
+	vector &operator=(vector<T> &&old) {
+		if (this == &old)
+			return *this;
+
+		freeStorage(_storage, _size);
+		_capacity = old._capacity;
+		_size = old._size;
+		_storage = old._storage;
+
+		old._storage = nullptr;
+		old._capacity = 0;
+		old._size = 0;
+
+		return *this;
+	}
+
+	/** Return the size of the array. */
+	size_type size() const {
+		return _size;
+	}
+
+	/** Clear the array of all its elements. */
+	void clear() {
+		freeStorage(_storage, _size);
+		_storage = nullptr;
+		_size = 0;
+		_capacity = 0;
+	}
+
+	/** Erase the element at @p pos position and return an iterator pointing to the next element in the array. */
 	iterator erase(iterator pos) {
-		return Common::Array<T>::erase(pos);
+		Common::copy(pos + 1, _storage + _size, pos);
+		_size--;
+		// We also need to destroy the last object properly here.
+		_storage[_size].~T();
+		return pos;
 	}
 
-	iterator erase(iterator first,
-				   iterator last) {
+	iterator erase(iterator first, iterator last) {
 		Common::copy(last, this->_storage + this->_size, first);
 
 		int count = (last - first);
@@ -120,6 +375,62 @@ public:
 		return first;
 	}
 
+	/**
+	 * Remove an element
+	 */
+	void remove(T element) {
+		for (uint i = 0; i < this->size(); ++i) {
+			if (this->operator[](i) == element) {
+				this->remove_at(i);
+				return;
+			}
+		}
+	}
+
+	/** Check whether the array is empty. */
+	bool empty() const {
+		return (_size == 0);
+	}
+
+	/** Check whether two arrays are identical. */
+	bool operator==(const vector<T> &other) const {
+		if (this == &other)
+			return true;
+		if (_size != other._size)
+			return false;
+		for (size_type i = 0; i < _size; ++i) {
+			if (_storage[i] != other._storage[i])
+				return false;
+		}
+		return true;
+	}
+
+	/** Check if two arrays are different. */
+	bool operator!=(const vector<T> &other) const {
+		return !(*this == other);
+	}
+
+	/** Return an iterator pointing to the first element in the array. */
+	iterator       begin() {
+		return _storage;
+	}
+
+	/** Return an iterator pointing past the last element in the array. */
+	iterator       end() {
+		return _storage + _size;
+	}
+
+	/** Return a const iterator pointing to the first element in the array. */
+	const_iterator begin() const {
+		return _storage;
+	}
+
+	/** Return a const iterator pointing past the last element in the array. */
+	const_iterator end() const {
+		return _storage + _size;
+	}
+
+
 	void swap(vector &arr) {
 		SWAP(this->_capacity, arr._capacity);
 		SWAP(this->_size, arr._size);
@@ -131,11 +442,11 @@ public:
 	 * the first item, and the predeceding item becomes the last one
 	 */
 	void rotate(iterator it) {
-		if (it != Common::Array<T>::end()) {
-			size_t count = it - Common::Array<T>::begin();
+		if (it != end()) {
+			size_t count = it - begin();
 			for (size_t ctr = 0; ctr < count; ++ctr) {
-				Common::Array<T>::push_back(Common::Array<T>::front());
-				Common::Array<T>::remove_at(0);
+				push_back(front());
+				remove_at(0);
 			}
 		}
 	}
@@ -147,76 +458,163 @@ public:
 		return this->end();
 	}
 	reverse_iterator rbegin() {
-		return reverse_iterator(this, (int)Common::Array<T>::size() - 1);
+		return reverse_iterator(this, (int)size() - 1);
 	}
 	reverse_iterator rend() {
 		return reverse_iterator(this, -1);
 	}
 	const_reverse_iterator rbegin() const {
-		return const_reverse_iterator(this, (int)Common::Array<T>::size() - 1);
+		return const_reverse_iterator(this, (int)size() - 1);
 	}
 	const_reverse_iterator rend() const {
 		return const_reverse_iterator(this, -1);
 	}
 	const_reverse_iterator crbegin() const {
-		return const_reverse_iterator(this, (int)Common::Array<T>::size() - 1);
+		return const_reverse_iterator(this, (int)size() - 1);
 	}
 	const_reverse_iterator crend() const {
 		return const_reverse_iterator(this, -1);
 	}
 
-	void pop_front() {
-		Common::Array<T>::remove_at(0);
+	/** Reserve enough memory in the array so that it can store at least the given number of elements.
+	 *  The current content of the array is not modified.
+	 */
+	void reserve(size_type newCapacity) {
+		if (newCapacity <= _capacity)
+			return;
+
+		T *oldStorage = _storage;
+		allocCapacity(newCapacity);
+
+		if (oldStorage) {
+			// Copy old data
+			uninitialized_move(oldStorage, oldStorage + _size, _storage);
+			freeStorage(oldStorage, _size);
+		}
 	}
 
-	void resize(size_t newSize) {
-		Common::Array<T>::resize(newSize);
+	/** Change the size of the array. */
+	void resize(size_type newSize) {
+		reserve(newSize);
+		for (size_type i = newSize; i < _size; ++i)
+			_storage[i].~T();
+		for (size_type i = _size; i < newSize; ++i)
+			new ((void *)&_storage[i]) T();
+		_size = newSize;
 	}
+
 	void resize(size_t newSize, const T elem) {
-		size_t oldSize = Common::Array<T>::size();
+		size_t oldSize = size();
 		resize(newSize);
 		for (size_t idx = oldSize; idx < newSize; ++idx)
 			this->operator[](idx) = elem;
 	}
 
-	T at(size_t index) const {
-		return (*this)[index];
-	}
-
-	/**
-	 * Adds an item to the array
+	/** Assign to this array the elements between the given iterators from another array,
+	 *  from @p first included to @p last excluded.
 	 */
-	void insert(const T &element) {
-		Common::Array<T>::push_back(element);
+	void assign(const_iterator first, const_iterator last) {
+		resize(distance(first, last)); // FIXME: ineffective?
+		T *dst = _storage;
+		while (first != last)
+			*dst++ = *first++;
 	}
 
-	/**
-	 * Adds an item to the array at a specified index
-	 */
-	void insert(iterator pos, const T &element) {
-		Common::Array<T>::insert(pos, element);
+protected:
+	/** Round up capacity to the next power of 2.
+	  * A minimal capacity of 8 is used.
+	  */
+	static size_type roundUpCapacity(size_type capacity) {
+		size_type capa = 8;
+		while (capa < capacity)
+			capa <<= 1;
+		return capa;
 	}
 
-	/**
-	 * Adds a range of items at the specified position in the array
-	 */
-	void insert(iterator position, const_iterator first, const_iterator last) {
-		int destIndex = position - this->begin();
-		for (; first != last; ++first) {
-			this->insert_at(destIndex++, *first);
+	/** Allocate a specific capacity for the array. */
+	void allocCapacity(size_type capacity) {
+		_capacity = capacity;
+		if (capacity) {
+			_storage = (T *)malloc(sizeof(T) * capacity);
+			if (!_storage)
+				::error("Common::vector: failure to allocate %u bytes", capacity * (size_type)sizeof(T));
+		} else {
+			_storage = nullptr;
 		}
 	}
 
+	/** Free the storage used by the array. */
+	void freeStorage(T *storage, const size_type elements) {
+		for (size_type i = 0; i < elements; ++i)
+			storage[i].~T();
+		free(storage);
+	}
+
 	/**
-	 * Remove an element
+	 * Insert a range of elements coming from this or another array.
+	 * Unlike std::vector::insert, this method does not accept
+	 * arbitrary iterators, mainly because our iterator system is
+	 * seriously limited and does not distinguish between input iterators,
+	 * output iterators, forward iterators, or random access iterators.
+	 *
+	 * So, we simply restrict to vector iterators. Extending this to arbitrary
+	 * random access iterators would be trivial.
+	 *
+	 * Moreover, this method does not handle all cases of inserting a subrange
+	 * of an array into itself; this is why it is private for now.
 	 */
-	void remove(T element) {
-		for (uint i = 0; i < this->size(); ++i) {
-			if (this->operator[](i) == element) {
-				this->remove_at(i);
-				return;
+	iterator insert_aux(iterator pos, const_iterator first, const_iterator last) {
+		assert(_storage <= pos && pos <= _storage + _size);
+		assert(first <= last);
+		const size_type n = last - first;
+		if (n) {
+			const size_type idx = pos - _storage;
+			if (_size + n > _capacity || (_storage <= first && first <= _storage + _size)) {
+				T *const oldStorage = _storage;
+
+				// If there is not enough space, allocate more.
+				// Likewise, if this is a self-insert, we allocate new
+				// storage to avoid conflicts.
+				allocCapacity(roundUpCapacity(_size + n));
+
+				// Copy the data from the old storage till the position where
+				// we insert new data
+				Common::uninitialized_copy(oldStorage, oldStorage + idx, _storage);
+				// Copy the data we insert
+				Common::uninitialized_copy(first, last, _storage + idx);
+				// Afterwards, copy the old data from the position where we
+				// insert.
+				Common::uninitialized_copy(oldStorage + idx, oldStorage + _size, _storage + idx + n);
+
+				freeStorage(oldStorage, _size);
+			} else if (idx + n <= _size) {
+				// Make room for the new elements by shifting back
+				// existing ones.
+				// 1. Move a part of the data to the uninitialized area
+				Common::uninitialized_copy(_storage + _size - n, _storage + _size, _storage + _size);
+				// 2. Move a part of the data to the initialized area
+				Common::copy_backward(pos, _storage + _size - n, _storage + _size);
+
+				// Insert the new elements.
+				Common::copy(first, last, pos);
+			} else {
+				// Copy the old data from the position till the end to the new
+				// place.
+				Common::uninitialized_copy(pos, _storage + _size, _storage + idx + n);
+
+				// Copy a part of the new data to the position inside the
+				// initialized space.
+				Common::copy(first, first + (_size - idx), pos);
+
+				// Copy a part of the new data to the position inside the
+				// uninitialized space.
+				Common::uninitialized_copy(first + (_size - idx), last, _storage + _size);
 			}
+
+			// Finally, update the internal state
+			_size += n;
 		}
+		return pos;
 	}
 };
 
diff --git a/engines/ags/shared/game/main_game_file.h b/engines/ags/shared/game/main_game_file.h
index 35aeb68ef6a..8eca795ba66 100644
--- a/engines/ags/shared/game/main_game_file.h
+++ b/engines/ags/shared/game/main_game_file.h
@@ -79,11 +79,7 @@ String GetMainGameFileErrorText(MainGameFileErrorType err);
 
 typedef TypedCodeError<MainGameFileErrorType, GetMainGameFileErrorText> MainGameFileError;
 typedef ErrorHandle<MainGameFileError> HGameFileError;
-#ifdef AGS_PLATFORM_SCUMMVM
-typedef std::shared_ptr<Stream> UStream;
-#else
 typedef std::unique_ptr<Stream> UStream;
-#endif
 
 // MainGameSource defines a successfully opened main game file
 struct MainGameSource {


Commit: 8e4254b8391fbbc21c7c4038311ae44de4fd6c61
    https://github.com/scummvm/scummvm/commit/8e4254b8391fbbc21c7c4038311ae44de4fd6c61
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:05-07:00

Commit Message:
AGS: Refactored walk-behinds calculation and texture generation

>From upstream ac0c4cea6a6e41238fe74f0753fa6a458f05d954

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/draw.h
    engines/ags/engine/ac/drawing_surface.cpp
    engines/ags/engine/ac/room.cpp
    engines/ags/engine/ac/walk_behind.cpp
    engines/ags/engine/ac/walk_behind.h
    engines/ags/engine/main/game_run.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 1cdfef61680..c781bfa4619 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -391,6 +391,7 @@ void init_game_drawdata() {
 
 	size_t actsps_num = _GP(game).numcharacters + MAX_ROOM_OBJECTS;
 	_GP(actsps).resize(actsps_num);
+
 	_GP(guibg).resize(_GP(game).numgui);
 
 	size_t guio_num = 0;
@@ -407,6 +408,8 @@ void dispose_game_drawdata() {
 	clear_drawobj_cache();
 
 	_GP(actsps).clear();
+	_GP(walkbehindobj).clear();
+
 	_GP(guibg).clear();
 	_GP(guiobjbg).clear();
 	_GP(guiobjddbref).clear();
@@ -429,9 +432,9 @@ void clear_drawobj_cache() {
 		_G(objcache)[i].image = nullptr;
 	}
 
-	// cleanup Character + Room object textures
 	// cleanup Character + Room object textures
 	for (auto &o : _GP(actsps)) o = ObjTexture();
+	for (auto &o : _GP(walkbehindobj)) o = ObjTexture();
 	// cleanup GUI and controls textures
 	for (auto &o : _GP(guibg)) o = ObjTexture();
 	for (auto &o : _GP(guiobjbg)) o = ObjTexture();
@@ -824,83 +827,6 @@ void put_sprite_list_on_screen(bool in_room);
 //
 //------------------------------------------------------------------------
 
-// sort_out_walk_behinds: modifies the supplied sprite by overwriting parts
-// of it with transparent pixels where there are walk-behind areas
-// Returns whether any pixels were updated
-int sort_out_walk_behinds(Bitmap *sprit, int xx, int yy, int basel, int zoom = 100) {
-	if (_G(noWalkBehindsAtAll))
-		return 0;
-
-	if ((!_GP(thisroom).WalkBehindMask->IsMemoryBitmap()) ||
-	        (!sprit->IsMemoryBitmap()))
-		quit("!sort_out_walk_behinds: wb bitmap not linear");
-
-	int rr, tmm, toheight; //,tcol;
-	// precalculate this to try and shave some time off
-	int maskcol = sprit->GetMaskColor();
-	int spcoldep = sprit->GetColorDepth();
-	int screenhit = _GP(thisroom).WalkBehindMask->GetHeight();
-	short *shptr;
-	int *loptr;
-	int pixelsChanged = 0;
-	int ee = 0;
-	if (xx < 0)
-		ee = 0 - xx;
-
-	for (; ee < sprit->GetWidth(); ee++) {
-		if (ee + xx >= _GP(thisroom).WalkBehindMask->GetWidth())
-			break;
-
-		if ((!_G(walkBehindExists)[ee + xx]) ||
-		        (_G(walkBehindEndY)[ee + xx] <= yy) ||
-		        (_G(walkBehindStartY)[ee + xx] > yy + sprit->GetHeight()))
-			continue;
-
-		toheight = sprit->GetHeight();
-
-		if (_G(walkBehindStartY)[ee + xx] < yy)
-			rr = 0;
-		else
-			rr = (_G(walkBehindStartY)[ee + xx] - yy);
-
-		// Since we will use _getpixel, ensure we only check within the screen
-		if (rr + yy < 0)
-			rr = 0 - yy;
-		if (toheight + yy > screenhit)
-			toheight = screenhit - yy;
-		if (toheight + yy > _G(walkBehindEndY)[ee + xx])
-			toheight = _G(walkBehindEndY)[ee + xx] - yy;
-		if (rr < 0)
-			rr = 0;
-
-		for (; rr < toheight; rr++) {
-			// we're ok with _getpixel because we've checked the screen edges
-			//tmm = _getpixel(_GP(thisroom).WalkBehindMask,ee+xx,rr+yy);
-			// actually, _getpixel is well inefficient, do it ourselves
-			// since we know it's 8-bit bitmap
-			tmm = _GP(thisroom).WalkBehindMask->GetScanLine(rr + yy)[ee + xx];
-			if (tmm < 1) continue;
-			if (_G(croom)->walkbehind_base[tmm] <= basel) continue;
-
-			pixelsChanged = 1;
-			if (spcoldep <= 8)
-				sprit->GetScanLineForWriting(rr)[ee] = maskcol;
-			else if (spcoldep <= 16) {
-				shptr = (short *)&sprit->GetScanLine(rr)[0];
-				shptr[ee] = maskcol;
-			} else if (spcoldep == 24) {
-				char *chptr = (char *)&sprit->GetScanLine(rr)[0];
-				memcpy(&chptr[ee * 3], &maskcol, 3);
-			} else if (spcoldep <= 32) {
-				loptr = (int *)&sprit->GetScanLine(rr)[0];
-				loptr[ee] = maskcol;
-			} else
-				quit("!Sprite colour depth >32 ??");
-		}
-	}
-	return pixelsChanged;
-}
-
 void repair_alpha_channel(Bitmap *dest, Bitmap *bgpic) {
 	// Repair the alpha channel, because sprites may have been drawn
 	// over it by the buttons, etc
@@ -1377,7 +1303,7 @@ void prepare_objects_for_drawing() {
 				usebasel += _GP(thisroom).Height;
 			}
 		} else if ((!actspsIntact) && (_G(walkBehindMethod) == DrawOverCharSprite)) {
-			sort_out_walk_behinds(actsp.Bmp.get(), atxp, atyp, usebasel);
+			walkbehinds_cropout(actsp.Bmp.get(), atxp, atyp, usebasel);
 		}
 
 		if ((!actspsIntact) || (actsp.Ddb == nullptr)) {
@@ -1664,7 +1590,7 @@ void prepare_characters_for_drawing() {
 				usebasel += _GP(thisroom).Height;
 			}
 		} else if (_G(walkBehindMethod) == DrawOverCharSprite) {
-			sort_out_walk_behinds(actsp.Bmp.get(), bgX, bgY, usebasel);
+			walkbehinds_cropout(actsp.Bmp.get(), bgX, bgY, usebasel);
 		}
 
 		if ((!usingCachedImage) || (actsp.Ddb == nullptr)) {
@@ -1708,6 +1634,13 @@ Bitmap *get_cached_object_image(int objid) {
 	return _GP(actsps)[objid].Bmp.get();
 }
 
+void add_walkbehind_image(size_t index, Shared::Bitmap *bmp, int x, int y) {
+	if (_GP(walkbehindobj).size() <= index)
+		_GP(walkbehindobj).resize(index + 1);
+	_GP(walkbehindobj)[index].Bmp.reset(); // don't store bitmap if added this way
+	_GP(walkbehindobj)[index].Ddb = recycle_ddb_bitmap(_GP(walkbehindobj)[index].Ddb, bmp);
+	_GP(walkbehindobj)[index].Pos = Point(x, y);
+}
 
 // Compiles a list of room sprites (characters, objects, background)
 void prepare_room_sprites() {
@@ -1724,7 +1657,7 @@ void prepare_room_sprites() {
 	if (_G(gfxDriver)->RequiresFullRedrawEachFrame()) {
 		if (_G(current_background_is_dirty) || _G(walkBehindsCachedForBgNum) != _GP(play).bg_frame) {
 			if (_G(walkBehindMethod) == DrawAsSeparateSprite) {
-				update_walk_behind_images();
+				walkbehinds_generate_sprites();
 			}
 		}
 		add_thing_to_draw(_G(roomBackgroundBmp), 0, 0);
@@ -1741,10 +1674,12 @@ void prepare_room_sprites() {
 			_G(our_eip) = 34;
 
 			if (_G(walkBehindMethod) == DrawAsSeparateSprite) {
-				for (int ee = 1; ee < MAX_WALK_BEHINDS; ee++) {
-					if (_G(walkBehindBitmap)[ee] != nullptr) {
-						add_to_sprite_list(_G(walkBehindBitmap)[ee], _G(walkBehindLeft)[ee], _G(walkBehindTop)[ee],
-							_G(croom)->walkbehind_base[ee], true);
+				for (int wb = 1 /* 0 is "no area" */;
+					(wb < MAX_WALK_BEHINDS) && (wb < _GP(walkbehindobj).size()); ++wb) {
+					const auto &wbobj = _GP(walkbehindobj)[wb];
+					if (wbobj.Ddb) {
+						add_to_sprite_list(wbobj.Ddb, wbobj.Pos.X, wbobj.Pos.Y,
+							_G(croom)->walkbehind_base[wb], true);
 					}
 				}
 			}
diff --git a/engines/ags/engine/ac/draw.h b/engines/ags/engine/ac/draw.h
index 4ec6e5599a7..5c58d92e5cd 100644
--- a/engines/ags/engine/ac/draw.h
+++ b/engines/ags/engine/ac/draw.h
@@ -178,6 +178,9 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 Shared::Bitmap *get_cached_character_image(int charid);
 // Returns a cached object image prepared for the render
 Shared::Bitmap *get_cached_object_image(int objid);
+// Adds a walk-behind sprite to the list for the given slot
+// (reuses existing texture if possible)
+void add_walkbehind_image(size_t index, Shared::Bitmap *bmp, int x, int y);
 
 void draw_and_invalidate_text(Shared::Bitmap *ds, int x1, int y1, int font, color_t text_color, const char *text);
 
diff --git a/engines/ags/engine/ac/drawing_surface.cpp b/engines/ags/engine/ac/drawing_surface.cpp
index 7aa64c0c2ca..14355bccaaf 100644
--- a/engines/ags/engine/ac/drawing_surface.cpp
+++ b/engines/ags/engine/ac/drawing_surface.cpp
@@ -66,7 +66,7 @@ void DrawingSurface_Release(ScriptDrawingSurface *sds) {
 	}
 	if (sds->roomMaskType > kRoomAreaNone) {
 		if (sds->roomMaskType == kRoomAreaWalkBehind) {
-			recache_walk_behinds();
+			walkbehinds_recalc();
 		}
 		sds->roomMaskType = kRoomAreaNone;
 	}
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 301764865cd..f529de8d7fa 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -521,7 +521,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 	update_polled_stuff_if_runtime();
 	redo_walkable_areas();
 	update_polled_stuff_if_runtime();
-	recache_walk_behinds();
+	walkbehinds_recalc();
 	update_polled_stuff_if_runtime();
 
 	_G(our_eip) = 205;
diff --git a/engines/ags/engine/ac/walk_behind.cpp b/engines/ags/engine/ac/walk_behind.cpp
index 401bc096a23..d335b89a033 100644
--- a/engines/ags/engine/ac/walk_behind.cpp
+++ b/engines/ags/engine/ac/walk_behind.cpp
@@ -19,9 +19,11 @@
  *
  */
 
+#include "ags/lib/std/algorithm.h"
 #include "ags/engine/ac/walk_behind.h"
 #include "ags/shared/ac/common.h"
 #include "ags/shared/ac/common_defines.h"
+#include "ags/engine/ac/room_status.h"
 #include "ags/engine/ac/game_state.h"
 #include "ags/engine/gfx/graphics_driver.h"
 #include "ags/shared/gfx/bitmap.h"
@@ -32,93 +34,163 @@ namespace AGS3 {
 using namespace AGS::Shared;
 using namespace AGS::Engine;
 
-void update_walk_behind_images() {
-	int ee, rr;
-	int bpp = (_GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic->GetColorDepth() + 7) / 8;
-	Bitmap *wbbmp;
-	for (ee = 1; ee < MAX_WALK_BEHINDS; ee++) {
-		update_polled_stuff_if_runtime();
-
-		if (_G(walkBehindRight)[ee] > 0) {
-			wbbmp = BitmapHelper::CreateTransparentBitmap(
-			            (_G(walkBehindRight)[ee] - _G(walkBehindLeft)[ee]) + 1,
-			            (_G(walkBehindBottom)[ee] - _G(walkBehindTop)[ee]) + 1,
-			            _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic->GetColorDepth());
-			int yy, startX = _G(walkBehindLeft)[ee], startY = _G(walkBehindTop)[ee];
-			for (rr = startX; rr <= _G(walkBehindRight)[ee]; rr++) {
-				for (yy = startY; yy <= _G(walkBehindBottom)[ee]; yy++) {
-					if (_GP(thisroom).WalkBehindMask->GetScanLine(yy)[rr] == ee) {
-						for (int ii = 0; ii < bpp; ii++)
-							wbbmp->GetScanLineForWriting(yy - startY)[(rr - startX) * bpp + ii] = _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic->GetScanLine(yy)[rr * bpp + ii];
+
+// An info on vertical column of walk-behind mask, which may contain WB area
+struct WalkBehindColumn {
+	bool Exists = false; // whether any WB area is in this column
+	int Y1 = 0, Y2 = 0; // WB top and bottom Y coords
+};
+
+WalkBehindMethodEnum walkBehindMethod = DrawOverCharSprite;
+std::vector<WalkBehindColumn> walkBehindCols; // precalculated WB positions
+Rect walkBehindAABB[MAX_WALK_BEHINDS]; // WB bounding box
+int walkBehindsCachedForBgNum = 0; // WB textures are for this background
+bool noWalkBehindsAtAll = false; // quick report that no WBs in this room
+bool walk_behind_baselines_changed = false;
+
+
+// Generates walk-behinds as separate sprites
+void walkbehinds_generate_sprites() {
+	const Bitmap *mask = _GP(thisroom).WalkBehindMask.get();
+	const Bitmap *bg = _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic.get();
+
+	const int coldepth = bg->GetColorDepth();
+	Bitmap wbbmp; // temp buffer
+	// Iterate through walk-behinds and generate a texture for each of them
+	for (int wb = 1 /* 0 is "no area" */; wb < MAX_WALK_BEHINDS; ++wb) {
+		const Rect pos = walkBehindAABB[wb];
+		if (pos.Right > 0) {
+			wbbmp.CreateTransparent(pos.GetWidth(), pos.GetHeight(), coldepth);
+			// Copy over all solid pixels belonging to this WB area
+			const int sx = pos.Left, ex = pos.Right, sy = pos.Top, ey = pos.Bottom;
+			for (int y = sy; y <= ey; ++y) {
+				const uint8_t *check_line = mask->GetScanLine(y);
+				const uint8_t *src_line = bg->GetScanLine(y);
+				uint8_t *dst_line = wbbmp.GetScanLineForWriting(y - sy);
+				for (int x = sx; x <= ex; ++x) {
+					if (check_line[x] != wb) continue;
+					switch (coldepth) {
+					case 8:
+						dst_line[(x - sx)] = src_line[x];
+						break;
+					case 16:
+						reinterpret_cast<uint16_t *>(dst_line)[(x - sx)] =
+							reinterpret_cast<const uint16_t *>(src_line)[x];
+						break;
+					case 32:
+						reinterpret_cast<uint32_t *>(dst_line)[(x - sx)] =
+							reinterpret_cast<const uint32_t *>(src_line)[x];
+						break;
+					default: assert(0); break;
 					}
 				}
 			}
-
-			update_polled_stuff_if_runtime();
-
-			if (_G(walkBehindBitmap)[ee] != nullptr) {
-				_G(gfxDriver)->DestroyDDB(_G(walkBehindBitmap)[ee]);
-			}
-			_G(walkBehindBitmap)[ee] = _G(gfxDriver)->CreateDDBFromBitmap(wbbmp, false);
-			delete wbbmp;
+			// Add to walk-behinds image list
+			add_walkbehind_image(wb, &wbbmp, pos.Left, pos.Top);
 		}
 	}
 
-	_G(walkBehindsCachedForBgNum) = _GP(play).bg_frame;
+	walkBehindsCachedForBgNum = _GP(play).bg_frame;
 }
 
-
-void recache_walk_behinds() {
-	if (_G(walkBehindExists)) {
-		free(_G(walkBehindExists));
-		free(_G(walkBehindStartY));
-		free(_G(walkBehindEndY));
-	}
-
-	_G(walkBehindExists) = (char *)malloc(_GP(thisroom).WalkBehindMask->GetWidth());
-	_G(walkBehindStartY) = (int *)malloc(_GP(thisroom).WalkBehindMask->GetWidth() * sizeof(int));
-	_G(walkBehindEndY) = (int *)malloc(_GP(thisroom).WalkBehindMask->GetWidth() * sizeof(int));
-	_G(noWalkBehindsAtAll) = 1;
-
-	int ee, rr, tmm;
-	const int NO_WALK_BEHIND = 100000;
-	for (ee = 0; ee < MAX_WALK_BEHINDS; ee++) {
-		_G(walkBehindLeft)[ee] = NO_WALK_BEHIND;
-		_G(walkBehindTop)[ee] = NO_WALK_BEHIND;
-		_G(walkBehindRight)[ee] = 0;
-		_G(walkBehindBottom)[ee] = 0;
-
-		if (_G(walkBehindBitmap)[ee] != nullptr) {
-			_G(gfxDriver)->DestroyDDB(_G(walkBehindBitmap)[ee]);
-			_G(walkBehindBitmap)[ee] = nullptr;
+// Edits the given game object's sprite, cutting out pixels covered by walk-behinds;
+// returns whether any pixels were updated;
+bool walkbehinds_cropout(Bitmap *sprit, int sprx, int spry, int basel, int zoom) {
+	if (noWalkBehindsAtAll)
+		return false;
+
+	const int maskcol = sprit->GetMaskColor();
+	const int spcoldep = sprit->GetColorDepth();
+	const int screenhit = _GP(thisroom).WalkBehindMask->GetHeight();
+
+	bool pixels_changed = false;
+	int x = 0;
+	if (sprx < 0)
+		x = 0 - sprx;
+	for (; (x < sprit->GetWidth()) && (x + sprx < _GP(thisroom).WalkBehindMask->GetWidth()); ++x) {
+		const auto &wbcol = walkBehindCols[x + sprx];
+		if ((!wbcol.Exists) ||
+			(wbcol.Y2 <= spry) ||
+			(wbcol.Y1 > spry + sprit->GetHeight()))
+			continue;
+
+		int y;
+		if (wbcol.Y1 < spry)
+			y = 0;
+		else
+			y = (wbcol.Y1 - spry);
+
+		// ensure we only check within the mask
+		int height = sprit->GetHeight();
+		if (y + spry < 0)
+			y = 0 - spry;
+		if (height + spry > screenhit)
+			height = screenhit - spry;
+		if (height + spry > wbcol.Y2)
+			height = wbcol.Y2 - spry;
+		if (y < 0)
+			y = 0;
+
+		for (; y < height; ++y) {
+			const int wb = _GP(thisroom).WalkBehindMask->GetScanLine(y + spry)[x + sprx];
+			if (wb < 1) continue; // "no area"
+			if (_G(croom)->walkbehind_base[wb] <= basel) continue;
+
+			pixels_changed = true;
+			uint8_t *dst_line = sprit->GetScanLineForWriting(y);
+			switch (spcoldep) {
+			case 8:
+				dst_line[x] = maskcol;
+				break;
+			case 16:
+				reinterpret_cast<uint16_t *>(dst_line)[x] = maskcol;
+				break;
+			case 32:
+				reinterpret_cast<uint32_t *>(dst_line)[x] = maskcol;
+				break;
+			default:
+				assert(0);
+				break;
+			}
 		}
 	}
+	return pixels_changed;
+}
 
-	update_polled_stuff_if_runtime();
-
-	for (ee = 0; ee < _GP(thisroom).WalkBehindMask->GetWidth(); ee++) {
-		_G(walkBehindExists)[ee] = 0;
-		for (rr = 0; rr < _GP(thisroom).WalkBehindMask->GetHeight(); rr++) {
-			tmm = _GP(thisroom).WalkBehindMask->GetScanLine(rr)[ee];
-			//tmm = _getpixel(_GP(thisroom).WalkBehindMask,ee,rr);
-			if ((tmm >= 1) && (tmm < MAX_WALK_BEHINDS)) {
-				if (!_G(walkBehindExists)[ee]) {
-					_G(walkBehindStartY)[ee] = rr;
-					_G(walkBehindExists)[ee] = tmm;
-					_G(noWalkBehindsAtAll) = 0;
+void walkbehinds_recalc() {
+	// Reset all data
+	walkBehindCols.clear();
+	for (int wb = 0; wb < MAX_WALK_BEHINDS; ++wb) {
+		walkBehindAABB[wb] = Rect(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN);
+	}
+	noWalkBehindsAtAll = true;
+
+	// Recalculate everything; note that mask is always 8-bit
+	const Bitmap *mask = _GP(thisroom).WalkBehindMask.get();
+	walkBehindCols.resize(mask->GetWidth());
+	for (int col = 0; col < mask->GetWidth(); ++col) {
+		auto &wbcol = walkBehindCols[col];
+		for (int y = 0; y < mask->GetHeight(); ++y) {
+			int wb = mask->GetScanLine(y)[col];
+			// Valid areas start with index 1, 0 = no area
+			if ((wb >= 1) && (wb < MAX_WALK_BEHINDS)) {
+				if (!wbcol.Exists) {
+					wbcol.Y1 = y;
+					wbcol.Exists = true;
+					noWalkBehindsAtAll = false;
 				}
-				_G(walkBehindEndY)[ee] = rr + 1;  // +1 to allow bottom line of screen to work
-
-				if (ee < _G(walkBehindLeft)[tmm]) _G(walkBehindLeft)[tmm] = ee;
-				if (rr < _G(walkBehindTop)[tmm]) _G(walkBehindTop)[tmm] = rr;
-				if (ee > _G(walkBehindRight)[tmm]) _G(walkBehindRight)[tmm] = ee;
-				if (rr > _G(walkBehindBottom)[tmm]) _G(walkBehindBottom)[tmm] = rr;
+				wbcol.Y2 = y + 1; // +1 to allow bottom line of screen to work (CHECKME??)
+				// resize the bounding rect
+				walkBehindAABB[wb].Left = std::min(col, walkBehindAABB[wb].Left);
+				walkBehindAABB[wb].Top = std::min(y, walkBehindAABB[wb].Top);
+				walkBehindAABB[wb].Right = std::max(col, walkBehindAABB[wb].Right);
+				walkBehindAABB[wb].Bottom = std::max(y, walkBehindAABB[wb].Bottom);
 			}
 		}
 	}
 
-	if (_G(walkBehindMethod) == DrawAsSeparateSprite) {
-		update_walk_behind_images();
+	if (walkBehindMethod == DrawAsSeparateSprite) {
+		walkbehinds_generate_sprites();
 	}
 }
 
diff --git a/engines/ags/engine/ac/walk_behind.h b/engines/ags/engine/ac/walk_behind.h
index a259a08534d..52ce5894551 100644
--- a/engines/ags/engine/ac/walk_behind.h
+++ b/engines/ags/engine/ac/walk_behind.h
@@ -19,9 +19,13 @@
  *
  */
 
+// Walk-behinds calculation logic.
+
 #ifndef AGS_ENGINE_AC_WALK_BEHIND_H
 #define AGS_ENGINE_AC_WALK_BEHIND_H
 
+#include "ags/shared/util/geometry.h"
+
 namespace AGS3 {
 
 // A method of rendering walkbehinds on screen:
@@ -36,8 +40,16 @@ enum WalkBehindMethodEnum {
 	DrawAsSeparateSprite
 };
 
-void update_walk_behind_images();
-void recache_walk_behinds();
+namespace AGS { namespace Shared { class Bitmap; } }
+using namespace AGS; // FIXME later
+
+// Recalculates walk-behind positions
+void walkbehinds_recalc();
+// Generates walk-behinds as separate sprites
+void walkbehinds_generate_sprites();
+// Edits the given game object's sprite, cutting out pixels covered by walk-behinds;
+// returns whether any pixels were updated
+bool walkbehinds_cropout(Shared::Bitmap *sprit, int sprx, int spry, int basel, int zoom = 100);
 
 } // namespace AGS3
 
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index f2a615f3c98..d0faf8a9bf5 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/walk_behind.h"
 #include "ags/engine/debugging/debugger.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/engine/device/mouse_w32.h"
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index fbe3dd24b00..40c9340d512 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -174,6 +174,7 @@ Globals::Globals() {
 	             MAX_DYNAMIC_SURFACES, (AGS::Shared::Bitmap *)nullptr);
 
 	_actsps = new std::vector<ObjTexture>();
+	_walkbehindobj = new std::vector<ObjTexture>();
 	_guibg = new std::vector<ObjTexture>();
 	_guiobjbg = new std::vector<ObjTexture>();
 
@@ -428,6 +429,7 @@ Globals::~Globals() {
 	delete _sprlist;
 	delete _thingsToDrawList;
 	delete _actsps;
+	delete _walkbehindobj;
 	delete _guibg;
 	delete _guiobjbg;
 	delete _guiobjddbref;
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 52aa0442ffb..dcdd310d885 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -579,6 +579,8 @@ public:
 	// actsps is used for temporary storage of the bitamp image
 	// of the latest version of the sprite
 	std::vector<ObjTexture> *_actsps;
+	// Walk-behind textures (3D renderers only)
+	std::vector<ObjTexture> *_walkbehindobj;
 	// GUI surfaces
 	std::vector<ObjTexture> *_guibg;
 	// GUI control surfaces


Commit: d161f11e549e6c9b525f94168864a39626a587d5
    https://github.com/scummvm/scummvm/commit/d161f11e549e6c9b525f94168864a39626a587d5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:06-07:00

Commit Message:
AGS: Further simplify walkbehinds_cropout()

>From upstream 08a382f17f4c07e615308caba5a16203729e7bc9

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


diff --git a/engines/ags/engine/ac/walk_behind.cpp b/engines/ags/engine/ac/walk_behind.cpp
index d335b89a033..62e5f73cdb3 100644
--- a/engines/ags/engine/ac/walk_behind.cpp
+++ b/engines/ags/engine/ac/walk_behind.cpp
@@ -101,37 +101,23 @@ bool walkbehinds_cropout(Bitmap *sprit, int sprx, int spry, int basel, int zoom)
 
 	const int maskcol = sprit->GetMaskColor();
 	const int spcoldep = sprit->GetColorDepth();
-	const int screenhit = _GP(thisroom).WalkBehindMask->GetHeight();
 
 	bool pixels_changed = false;
-	int x = 0;
-	if (sprx < 0)
-		x = 0 - sprx;
-	for (; (x < sprit->GetWidth()) && (x + sprx < _GP(thisroom).WalkBehindMask->GetWidth()); ++x) {
+	// pass along the sprite's pixels, but skip those that lie outside the mask
+	for (int x = std::max(0, 0 - sprx);
+		(x < sprit->GetWidth()) && (x + sprx < _GP(thisroom).WalkBehindMask->GetWidth()); ++x) {
+		// select the WB column at this x
 		const auto &wbcol = walkBehindCols[x + sprx];
+		// skip if no area, or sprite lies outside of all areas in this column
 		if ((!wbcol.Exists) ||
 			(wbcol.Y2 <= spry) ||
 			(wbcol.Y1 > spry + sprit->GetHeight()))
 			continue;
 
-		int y;
-		if (wbcol.Y1 < spry)
-			y = 0;
-		else
-			y = (wbcol.Y1 - spry);
-
-		// ensure we only check within the mask
-		int height = sprit->GetHeight();
-		if (y + spry < 0)
-			y = 0 - spry;
-		if (height + spry > screenhit)
-			height = screenhit - spry;
-		if (height + spry > wbcol.Y2)
-			height = wbcol.Y2 - spry;
-		if (y < 0)
-			y = 0;
-
-		for (; y < height; ++y) {
+		// ensure we only check within the valid areas (between Y1 and Y2)
+		// we assume that Y1 and Y2 are always within the mask
+		for (int y = std::max(0, wbcol.Y1 - spry);
+				(y < sprit->GetHeight()) && (y + spry < wbcol.Y2); ++y) {
 			const int wb = _GP(thisroom).WalkBehindMask->GetScanLine(y + spry)[x + sprx];
 			if (wb < 1) continue; // "no area"
 			if (_G(croom)->walkbehind_base[wb] <= basel) continue;


Commit: cfefb80461109b07956a4ca005b16edb31635edf
    https://github.com/scummvm/scummvm/commit/cfefb80461109b07956a4ca005b16edb31635edf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:06-07:00

Commit Message:
AGS: Moved font renderer shutdown after the game data disposal

>From upstream 0914030ff28647262e53d611f8de8e9b5f3aca16

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


diff --git a/engines/ags/engine/main/quit.cpp b/engines/ags/engine/main/quit.cpp
index 23de13a9473..ae73e19bd00 100644
--- a/engines/ags/engine/main/quit.cpp
+++ b/engines/ags/engine/main/quit.cpp
@@ -220,9 +220,6 @@ void quit_free() {
 
 	_G(our_eip) = 9901;
 
-	shutdown_font_renderer();
-	_G(our_eip) = 9902;
-
 	_GP(spriteset).Reset();
 
 	_G(our_eip) = 9908;
@@ -240,7 +237,9 @@ void quit_free() {
 	// release backed library
 	// WARNING: no Allegro objects should remain in memory after this,
 	// if their destruction is called later, program will crash!
+	shutdown_font_renderer();
 	allegro_exit();
+	sys_main_shutdown();
 
 	_G(platform)->PostAllegroExit();
 


Commit: da1178d905e35c0a64c816b37fe46eb0f1de291a
    https://github.com/scummvm/scummvm/commit/da1178d905e35c0a64c816b37fe46eb0f1de291a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:06-07:00

Commit Message:
AGS: Implemented proper HasAlphaChannel for all gui controls

>From upstream 83306ee321e127368a41068f32bd4899ae00223e

Changed paths:
    engines/ags/engine/ac/display.cpp
    engines/ags/engine/ac/display.h
    engines/ags/engine/ac/gui_inv.cpp
    engines/ags/engine/gui/gui_engine.cpp
    engines/ags/shared/font/fonts.cpp
    engines/ags/shared/font/fonts.h
    engines/ags/shared/gui/gui_button.cpp
    engines/ags/shared/gui/gui_button.h
    engines/ags/shared/gui/gui_inv.h
    engines/ags/shared/gui/gui_label.cpp
    engines/ags/shared/gui/gui_label.h
    engines/ags/shared/gui/gui_listbox.cpp
    engines/ags/shared/gui/gui_listbox.h
    engines/ags/shared/gui/gui_textbox.cpp
    engines/ags/shared/gui/gui_textbox.h


diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index a0553554bc9..442044ba925 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -444,7 +444,7 @@ int GetTextDisplayTime(const char *text, int canberel) {
 }
 
 bool ShouldAntiAliasText() {
-	return (_GP(game).options[OPT_ANTIALIASFONTS] != 0 || ::AGS::g_vm->_forceTextAA);
+	return (_GP(game).GetColorDepth() >= 24) && (_GP(game).options[OPT_ANTIALIASFONTS] != 0);
 }
 
 void wouttextxy_AutoOutline(Bitmap *ds, size_t font, int32_t color, const char *texx, int &xxp, int &yyp) {
diff --git a/engines/ags/engine/ac/display.h b/engines/ags/engine/ac/display.h
index b5d5151a9ae..1a30cda060d 100644
--- a/engines/ags/engine/ac/display.h
+++ b/engines/ags/engine/ac/display.h
@@ -44,7 +44,6 @@ void _display_at(int xx, int yy, int wii, const char *text, int disp_type, int a
 // and clip was started, or string cleaned from voice-over tags which is safe to display on screen.
 // Returns whether voice-over clip was started successfully.
 bool try_auto_play_speech(const char *text, const char *&replace_text, int charid);
-bool ShouldAntiAliasText();
 // Calculates meaningful length of the displayed text
 int GetTextDisplayLength(const char *text);
 // Calculates number of game loops for displaying a text on screen
diff --git a/engines/ags/engine/ac/gui_inv.cpp b/engines/ags/engine/ac/gui_inv.cpp
index 8c63a31eeb5..813f581adee 100644
--- a/engines/ags/engine/ac/gui_inv.cpp
+++ b/engines/ags/engine/ac/gui_inv.cpp
@@ -32,6 +32,12 @@ namespace AGS3 {
 namespace AGS {
 namespace Shared {
 
+bool GUIInvWindow::HasAlphaChannel() const {
+	// We would have to test every inventory item's graphic to tell precisely,
+	// so just test game color depth instead:
+	return _GP(game).GetColorDepth() == 32;
+}
+
 int GUIInvWindow::GetCharacterId() const {
 	if (CharId < 0)
 		return _GP(game).playercharacter;
diff --git a/engines/ags/engine/gui/gui_engine.cpp b/engines/ags/engine/gui/gui_engine.cpp
index 61a08412193..3363a82079b 100644
--- a/engines/ags/engine/gui/gui_engine.cpp
+++ b/engines/ags/engine/gui/gui_engine.cpp
@@ -30,6 +30,7 @@
 #include "ags/shared/font/fonts.h"
 #include "ags/shared/gui/gui_main.h"
 #include "ags/shared/gui/gui_button.h"
+#include "ags/shared/gui/gui_inv.h"
 #include "ags/shared/gui/gui_label.h"
 #include "ags/shared/gui/gui_listbox.h"
 #include "ags/shared/gui/gui_textbox.h"
diff --git a/engines/ags/shared/font/fonts.cpp b/engines/ags/shared/font/fonts.cpp
index 600aa7c3b22..e4cf1fce898 100644
--- a/engines/ags/shared/font/fonts.cpp
+++ b/engines/ags/shared/font/fonts.cpp
@@ -188,6 +188,12 @@ void set_font_outline(size_t font_number, int outline_type,
 	_GP(fonts)[font_number].Info.AutoOutlineThickness = thickness;
 }
 
+bool is_font_antialiased(size_t font_number) {
+	if (font_number >= _GP(fonts).size())
+		return false;
+	return ShouldAntiAliasText() && !is_bitmap_font(font_number);
+}
+
 int get_font_height(size_t fontNumber) {
 	if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
 		return 0;
diff --git a/engines/ags/shared/font/fonts.h b/engines/ags/shared/font/fonts.h
index d5bbbb2f35c..0643e6eb441 100644
--- a/engines/ags/shared/font/fonts.h
+++ b/engines/ags/shared/font/fonts.h
@@ -116,6 +116,7 @@ int get_text_lines_surf_height(size_t fontNumber, size_t numlines);
 // Set font's outline type
 void set_font_outline(size_t font_number, int outline_type,
 	enum FontInfo::AutoOutlineStyle style = FontInfo::kSquared, int thickness = 1);
+bool is_font_antialiased(size_t font_number);
 // Outputs a single line of text on the defined position on bitmap, using defined font, color and parameters
 void wouttextxy(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t text_color, const char *texx);
 // Assigns FontInfo to the font
@@ -136,6 +137,9 @@ void wfreefont(size_t fontNumber);
 // Free all fonts data
 void free_all_fonts();
 
+// Tells if the text should be antialiased when possible
+bool ShouldAntiAliasText();
+
 // SplitLines class represents a list of lines and is meant to reduce
 // subsequent memory (de)allocations if used often during game loops
 // and drawing. For that reason it is not equivalent to std::vector,
diff --git a/engines/ags/shared/gui/gui_button.cpp b/engines/ags/shared/gui/gui_button.cpp
index 64dd5e3dc7d..63d986c59c0 100644
--- a/engines/ags/shared/gui/gui_button.cpp
+++ b/engines/ags/shared/gui/gui_button.cpp
@@ -21,6 +21,7 @@
 
 #include "ags/shared/ac/sprite_cache.h"
 #include "ags/shared/ac/game_struct_defines.h"
+#include "ags/shared/font/fonts.h"
 #include "ags/shared/gui/gui_button.h"
 #include "ags/shared/gui/gui_main.h" // TODO: extract helper functions
 #include "ags/shared/util/stream.h"
@@ -28,7 +29,6 @@
 #include "ags/globals.h"
 
 namespace AGS3 {
-
 namespace AGS {
 namespace Shared {
 
@@ -82,6 +82,11 @@ GUIButton::GUIButton() {
 	_scEventArgs[0] = "GUIControl *control, MouseButton button";
 }
 
+bool GUIButton::HasAlphaChannel() const {
+	return ((CurrentImage > 0) && is_sprite_alpha(CurrentImage)) ||
+		(!_unnamed && is_font_antialiased(Font));
+}
+
 const String &GUIButton::GetText() const {
 	return _text;
 }
@@ -94,10 +99,6 @@ bool GUIButton::IsClippingImage() const {
 	return (Flags & kGUICtrl_Clip) != 0;
 }
 
-bool GUIButton::HasAlphaChannel() const {
-	return is_sprite_alpha(CurrentImage);
-}
-
 Rect GUIButton::CalcGraphicRect(bool clipped) {
 	if (clipped)
 		return RectWH(X, Y, Width, Height);
diff --git a/engines/ags/shared/gui/gui_button.h b/engines/ags/shared/gui/gui_button.h
index e83f0242a38..2fe0ae77bd3 100644
--- a/engines/ags/shared/gui/gui_button.h
+++ b/engines/ags/shared/gui/gui_button.h
@@ -62,10 +62,10 @@ class GUIButton : public GUIObject {
 public:
 	GUIButton();
 
+	bool HasAlphaChannel() const override;
 	const String &GetText() const;
 	bool IsImageButton() const;
 	bool IsClippingImage() const;
-	bool HasAlphaChannel() const override;
 
 	// Operations
 	Rect CalcGraphicRect(bool clipped) override;
diff --git a/engines/ags/shared/gui/gui_inv.h b/engines/ags/shared/gui/gui_inv.h
index 6d4f4e814e6..bc0e7045c7e 100644
--- a/engines/ags/shared/gui/gui_inv.h
+++ b/engines/ags/shared/gui/gui_inv.h
@@ -33,6 +33,7 @@ class GUIInvWindow : public GUIObject {
 public:
 	GUIInvWindow();
 
+	bool HasAlphaChannel() const override;
 	// This function has distinct implementations in Engine and Editor
 	int GetCharacterId() const;
 
diff --git a/engines/ags/shared/gui/gui_label.cpp b/engines/ags/shared/gui/gui_label.cpp
index 83719a81e59..14957b4d73e 100644
--- a/engines/ags/shared/gui/gui_label.cpp
+++ b/engines/ags/shared/gui/gui_label.cpp
@@ -41,6 +41,10 @@ GUILabel::GUILabel() {
 	_scEventCount = 0;
 }
 
+bool GUILabel::HasAlphaChannel() const {
+	return is_font_antialiased(Font);
+}
+
 String GUILabel::GetText() const {
 	return Text;
 }
diff --git a/engines/ags/shared/gui/gui_label.h b/engines/ags/shared/gui/gui_label.h
index 5a2333c9cc3..e03ce20cfd5 100644
--- a/engines/ags/shared/gui/gui_label.h
+++ b/engines/ags/shared/gui/gui_label.h
@@ -37,6 +37,7 @@ class GUILabel : public GUIObject {
 public:
 	GUILabel();
 
+	bool HasAlphaChannel() const override;
 	// Gets label's text property in original set form (with macros etc)
 	String       GetText() const;
 	// Gets which macro are contained within label's text
diff --git a/engines/ags/shared/gui/gui_listbox.cpp b/engines/ags/shared/gui/gui_listbox.cpp
index 47524f979db..3a66f5d6f0c 100644
--- a/engines/ags/shared/gui/gui_listbox.cpp
+++ b/engines/ags/shared/gui/gui_listbox.cpp
@@ -49,6 +49,10 @@ GUIListBox::GUIListBox() {
 	_scEventArgs[0] = "GUIControl *control";
 }
 
+bool GUIListBox::HasAlphaChannel() const {
+	return is_font_antialiased(Font);
+}
+
 int GUIListBox::GetItemAt(int x, int y) const {
 	if (RowHeight <= 0 || IsInRightMargin(x))
 		return -1;
diff --git a/engines/ags/shared/gui/gui_listbox.h b/engines/ags/shared/gui/gui_listbox.h
index 6fc08cda7ee..d4e8fd1613a 100644
--- a/engines/ags/shared/gui/gui_listbox.h
+++ b/engines/ags/shared/gui/gui_listbox.h
@@ -34,6 +34,7 @@ class GUIListBox : public GUIObject {
 public:
 	GUIListBox();
 
+	bool HasAlphaChannel() const override;
 	bool AreArrowsShown() const;
 	bool IsBorderShown() const;
 	bool IsSvgIndex() const;
diff --git a/engines/ags/shared/gui/gui_textbox.cpp b/engines/ags/shared/gui/gui_textbox.cpp
index 1f7a7ade5e5..137c2261be7 100644
--- a/engines/ags/shared/gui/gui_textbox.cpp
+++ b/engines/ags/shared/gui/gui_textbox.cpp
@@ -43,6 +43,10 @@ GUITextBox::GUITextBox() {
 	_scEventArgs[0] = "GUIControl *control";
 }
 
+bool GUITextBox::HasAlphaChannel() const {
+	return is_font_antialiased(Font);
+}
+
 bool GUITextBox::IsBorderShown() const {
 	return (TextBoxFlags & kTextBox_ShowBorder) != 0;
 }
diff --git a/engines/ags/shared/gui/gui_textbox.h b/engines/ags/shared/gui/gui_textbox.h
index b90bfd59c8c..ed285651847 100644
--- a/engines/ags/shared/gui/gui_textbox.h
+++ b/engines/ags/shared/gui/gui_textbox.h
@@ -34,6 +34,7 @@ class GUITextBox : public GUIObject {
 public:
 	GUITextBox();
 
+	bool HasAlphaChannel() const override;
 	bool IsBorderShown() const;
 
 	// Operations


Commit: d767293a3229de8863428ba4fb22edc278f05e56
    https://github.com/scummvm/scummvm/commit/d767293a3229de8863428ba4fb22edc278f05e56
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:06-07:00

Commit Message:
AGS: Updated build version (3.6.0.24)

>From upstream c0fe840f2ad123bbb1071e42f8449bf8fa80b58a

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 81073776aeb..280071ee0f1 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.23"
+#define ACI_VERSION_STR      "3.6.0.24"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3.6.0.23
+#define ACI_VERSION_MSRC_DEF  3.6.0.24
 #endif
 
 #define SPECIAL_VERSION ""


Commit: 480be2134b3d565964e43819da8da2abb632b8af
    https://github.com/scummvm/scummvm/commit/480be2134b3d565964e43819da8da2abb632b8af
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:06-07:00

Commit Message:
AGS: Added script instance thread stack, replacing current_instance

>From upstream f3443cb7a4ae5858e5d2d28131aaf6cf138bc0ec

Changed paths:
  A engines/ags/lib/std/stack.h
    engines/ags/engine/debugging/debug.cpp
    engines/ags/engine/debugging/debugger.h
    engines/ags/engine/script/cc_instance.cpp
    engines/ags/engine/script/cc_instance.h
    engines/ags/engine/script/executing_script.cpp
    engines/ags/engine/script/script.cpp
    engines/ags/engine/script/script.h
    engines/ags/engine/script/script_runtime.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h


diff --git a/engines/ags/engine/debugging/debug.cpp b/engines/ags/engine/debugging/debug.cpp
index 05f14fd2583..ca2981a969d 100644
--- a/engines/ags/engine/debugging/debug.cpp
+++ b/engines/ags/engine/debugging/debug.cpp
@@ -326,15 +326,6 @@ String get_cur_script(int numberOfLinesOfCallStack) {
 	return callstack;
 }
 
-bool get_script_position(ScriptPosition &script_pos) {
-	ccInstance *cur_instance = ccInstance::GetCurrentInstance();
-	if (cur_instance) {
-		cur_instance->GetScriptPosition(script_pos);
-		return true;
-	}
-	return false;
-}
-
 struct Breakpoint {
 	char scriptName[80];
 	int lineNumber;
diff --git a/engines/ags/engine/debugging/debugger.h b/engines/ags/engine/debugging/debugger.h
index ee393b854af..0929797b366 100644
--- a/engines/ags/engine/debugging/debugger.h
+++ b/engines/ags/engine/debugging/debugger.h
@@ -34,7 +34,6 @@ bool send_message_to_editor(const char *msg);
 bool send_exception_to_editor(const char *qmsg);
 // Returns current script's location and callstack
 AGS::Shared::String get_cur_script(int numberOfLinesOfCallStack);
-bool get_script_position(ScriptPosition &script_pos);
 void check_debug_keys();
 
 #define DBG_NOIFACE       1
diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index 83924c519fc..640b06efae2 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -182,7 +182,7 @@ struct FunctionCallStack {
 
 
 ccInstance *ccInstance::GetCurrentInstance() {
-	return _G(current_instance);
+	return _GP(InstThreads).size() > 0 ? _GP(InstThreads).top() : nullptr;
 }
 
 ccInstance *ccInstance::CreateFromScript(PScript scri) {
@@ -321,50 +321,41 @@ int ccInstance::CallScriptFunction(const char *funcname, int32_t numargs, const
 		return -2;
 	}
 
+	// Prepare instance for run
+	flags &= ~INSTF_ABORTED;
 	// Allow to pass less parameters if script callback has less declared args
 	numargs = std::min(numargs, export_args);
-
-	//numargs++;                    // account for return address
-	flags &= ~INSTF_ABORTED;
-
 	// object pointer needs to start zeroed
 	registers[SREG_OP].SetDynamicObject(nullptr, nullptr);
-
-	ccInstance *currentInstanceWas = _G(current_instance);
 	registers[SREG_SP].SetStackPtr(&stack[0]);
 	stackdata_ptr = stackdata;
 	// NOTE: Pushing parameters to stack in reverse order
 	ASSERT_STACK_SPACE_AVAILABLE(numargs + 1 /* return address */)
-	for (int i = numargs - 1; i >= 0; --i) {
-		PushValueToStack(params[i]);
-	}
+		for (int i = numargs - 1; i >= 0; --i) {
+			PushValueToStack(params[i]);
+		}
 	PushValueToStack(RuntimeScriptValue().SetInt32(0)); // return address on stack
-	if (_G(ccError)) {
-		return -1;
-	}
-	runningInst = this;
 
+	_GP(InstThreads).push(this); // push instance thread
+	runningInst = this;
 	int reterr = Run(startat);
+	// Cleanup before returning, even if error
 	ASSERT_STACK_SIZE(numargs);
 	PopValuesFromStack(numargs);
 	pc = 0;
-	_G(current_instance) = currentInstanceWas;
-
-	if (_G(abort_engine))
-		return -1;
+	_GP(InstThreads).pop(); // pop instance thread
+	if (reterr != 0)
+		return reterr;
 
 	// NOTE that if proper multithreading is added this will need
-	// to be reconsidered, since the GC could be run in the middle
-	// of a RET from a function or something where there is an
+	// to be reconsidered, since the GC could be run in the middle 
+	// of a RET from a function or something where there is an 
 	// object with ref count 0 that is in use
 	_GP(pool).RunGarbageCollectionIfAppropriate();
 
 	if (_G(new_line_hook))
 		_G(new_line_hook)(nullptr, 0);
 
-	if (reterr)
-		return -6;
-
 	if (flags & INSTF_ABORTED) {
 		flags &= ~INSTF_ABORTED;
 
@@ -419,7 +410,6 @@ int ccInstance::Run(int32_t curpc) {
 	int loopIterationCheckDisabled = 0;
 	thisbase[0] = 0;
 	funcstart[0] = pc;
-	_G(current_instance) = this;
 	ccInstance *codeInst = runningInst;
 	bool write_debug_dump = ccGetOption(SCOPT_DEBUGRUN) ||
 		(gDebugLevel > 0 && DebugMan.isDebugChannelEnabled(::AGS::kDebugScript));
@@ -609,7 +599,6 @@ int ccInstance::Run(int32_t curpc) {
 				returnValue = registers[SREG_AX].IValue;
 				return 0;
 			}
-			_G(current_instance) = this;
 			POP_CALL_STACK;
 			continue; // continue so that the PC doesn't get overwritten
 		}
@@ -1038,7 +1027,6 @@ int ccInstance::Run(int32_t curpc) {
 			}
 
 			registers[SREG_AX] = return_value;
-			_G(current_instance) = this;
 			next_call_needs_object = 0;
 			num_args_to_func = -1;
 			break;
diff --git a/engines/ags/engine/script/cc_instance.h b/engines/ags/engine/script/cc_instance.h
index e7f005691e0..8b468f59e34 100644
--- a/engines/ags/engine/script/cc_instance.h
+++ b/engines/ags/engine/script/cc_instance.h
@@ -161,8 +161,6 @@ public:
 
 	// Call an exported function in the script
 	int     CallScriptFunction(const char *funcname, int32_t num_params, const RuntimeScriptValue *params);
-	// Begin executing script starting from the given bytecode index
-	int     Run(int32_t curpc);
 
 	// Get the script's execution position and callstack as human-readable text
 	Shared::String GetCallStack(int maxLines);
@@ -193,6 +191,8 @@ protected:
 	bool    CreateRuntimeCodeFixups(const ccScript *scri);
 	//bool    ReadOperation(ScriptOperation &op, int32_t at_pc);
 
+	// Begin executing script starting from the given bytecode index
+	int     Run(int32_t curpc);
 	// Runtime fixups
 	//bool    FixupArgument(intptr_t code_value, char fixup_type, RuntimeScriptValue &argument);
 
diff --git a/engines/ags/engine/script/executing_script.cpp b/engines/ags/engine/script/executing_script.cpp
index 0222546c91f..6b0983ad47c 100644
--- a/engines/ags/engine/script/executing_script.cpp
+++ b/engines/ags/engine/script/executing_script.cpp
@@ -22,6 +22,7 @@
 #include "ags/engine/script/executing_script.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/engine/debugging/debugger.h"
+#include "ags/engine/script/script.h"
 
 namespace AGS3 {
 
diff --git a/engines/ags/engine/script/script.cpp b/engines/ags/engine/script/script.cpp
index cda695e5312..409fd59405b 100644
--- a/engines/ags/engine/script/script.cpp
+++ b/engines/ags/engine/script/script.cpp
@@ -890,4 +890,13 @@ void run_unhandled_event(int evnt) {
 	}
 }
 
+bool get_script_position(ScriptPosition &script_pos) {
+	ccInstance *cur_instance = ccInstance::GetCurrentInstance();
+	if (cur_instance) {
+		cur_instance->GetScriptPosition(script_pos);
+		return true;
+	}
+	return false;
+}
+
 } // namespace AGS3
diff --git a/engines/ags/engine/script/script.h b/engines/ags/engine/script/script.h
index d7b2f64dd96..3b221bc2f7f 100644
--- a/engines/ags/engine/script/script.h
+++ b/engines/ags/engine/script/script.h
@@ -86,6 +86,9 @@ InteractionVariable *FindGraphicalVariable(const char *varName);
 void    run_unhandled_event(int evnt);
 void    can_run_delayed_command();
 
+// Gets current running script position
+bool    get_script_position(ScriptPosition &script_pos);
+
 } // namespace AGS3
 
 #endif
diff --git a/engines/ags/engine/script/script_runtime.cpp b/engines/ags/engine/script/script_runtime.cpp
index 8fb0541ac75..292cd27d63a 100644
--- a/engines/ags/engine/script/script_runtime.cpp
+++ b/engines/ags/engine/script/script_runtime.cpp
@@ -124,8 +124,9 @@ void ccSetScriptAliveTimer(int numloop) {
 }
 
 void ccNotifyScriptStillAlive() {
-	if (_G(current_instance) != nullptr)
-		_G(current_instance)->flags |= INSTF_RUNNING;
+	ccInstance *cur_inst = ccInstance::GetCurrentInstance();
+	if (cur_inst)
+		cur_inst->flags |= INSTF_RUNNING;
 }
 
 void ccSetDebugHook(new_line_hook_type jibble) {
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index 40c9340d512..d4da8007e60 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -129,6 +129,7 @@ Globals::Globals() {
 	_animbuts = new std::vector<AnimatingGUIButton>();
 
 	// cc_instance.cpp globals
+	_InstThreads = new std::stack<ccInstance *>();
 	_GlobalReturnValue = new RuntimeScriptValue();
 
 	// cc_options.cpp globals
@@ -396,6 +397,7 @@ Globals::~Globals() {
 	delete _animbuts;
 
 	// cc_instance.cpp globals
+	delete _InstThreads;
 	delete _GlobalReturnValue;
 	delete _scriptDumpFile;
 
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index dcdd310d885..f14d33d7fbe 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -25,6 +25,7 @@
 #include "ags/shared/core/platform.h"
 #define AGS_PLATFORM_DEFINES_PSP_VARS (AGS_PLATFORM_OS_IOS || AGS_PLATFORM_OS_ANDROID)
 
+#include "ags/lib/std/stack.h"
 #include "ags/shared/ac/game_version.h"
 #include "ags/shared/util/stdio_compat.h"
 #include "ags/shared/util/string.h"
@@ -369,7 +370,11 @@ public:
 	 * @{
 	 */
 
-	ccInstance *_current_instance = nullptr;
+	// Instance thread stack holds a list of running or suspended script instances;
+	// In AGS currently only one thread is running, others are waiting in the queue.
+	// An example situation is repeatedly_execute_always callback running while
+	// another instance is waiting at the blocking action or Wait().
+	std::stack<ccInstance *> *_InstThreads;
 	// [IKM] 2012-10-21:
 	// NOTE: This is temporary solution (*sigh*, one of many) which allows certain
 	// exported functions return value as a RuntimeScriptValue object;
diff --git a/engines/ags/lib/std/stack.h b/engines/ags/lib/std/stack.h
new file mode 100644
index 00000000000..4a9723dbbbe
--- /dev/null
+++ b/engines/ags/lib/std/stack.h
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef AGS_STD_STACK_H
+#define AGS_STD_STACK_H
+
+#include "common/stack.h"
+
+namespace AGS3 {
+namespace std {
+
+template<class T>
+using stack = Common::Stack<T>;
+
+} // namespace std
+} // namespace AGS3
+
+#endif


Commit: a2a5f2575db5ee36732850242ce15fc8f01aacb0
    https://github.com/scummvm/scummvm/commit/a2a5f2575db5ee36732850242ce15fc8f01aacb0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:07-07:00

Commit Message:
AGS: Reorganized few cc_ headers

>From upstream ea1f8b9ece69fc7c49c4d3aa7b9af51c4ca736e5

Changed paths:
  A engines/ags/shared/script/cc_common.cpp
  A engines/ags/shared/script/cc_common.h
  A engines/ags/shared/script/cc_internal.h
  R engines/ags/shared/script/cc_error.cpp
  R engines/ags/shared/script/cc_error.h
  R engines/ags/shared/script/cc_options.cpp
  R engines/ags/shared/script/cc_options.h
  R engines/ags/shared/script/script_common.h
    engines/ags/ags.cpp
    engines/ags/console.cpp
    engines/ags/engine/ac/dynobj/cc_dynamic_object.cpp
    engines/ags/engine/ac/dynobj/managed_object_pool.cpp
    engines/ags/engine/ac/event.cpp
    engines/ags/engine/ac/global_debug.cpp
    engines/ags/engine/ac/room.cpp
    engines/ags/engine/debugging/debug.cpp
    engines/ags/engine/game/game_init.cpp
    engines/ags/engine/game/savegame.cpp
    engines/ags/engine/game/savegame_components.cpp
    engines/ags/engine/game/savegame_v321.cpp
    engines/ags/engine/main/game_file.cpp
    engines/ags/engine/script/cc_instance.cpp
    engines/ags/engine/script/cc_instance.h
    engines/ags/engine/script/runtime_script_value.cpp
    engines/ags/engine/script/script.cpp
    engines/ags/engine/script/script_api.cpp
    engines/ags/engine/script/script_engine.cpp
    engines/ags/engine/script/script_runtime.cpp
    engines/ags/globals.cpp
    engines/ags/module.mk
    engines/ags/shared/game/main_game_file.cpp
    engines/ags/shared/game/room_file.cpp
    engines/ags/shared/script/cc_script.cpp


diff --git a/engines/ags/ags.cpp b/engines/ags/ags.cpp
index 05fa1c74ba3..413367bc944 100644
--- a/engines/ags/ags.cpp
+++ b/engines/ags/ags.cpp
@@ -56,7 +56,7 @@
 #include "ags/engine/ac/route_finder.h"
 #include "ags/shared/core/asset_manager.h"
 #include "ags/shared/util/directory.h"
-#include "ags/shared/script/cc_options.h"
+#include "ags/shared/script/cc_common.h"
 
 #ifdef ENABLE_AGS_TESTS
 #include "ags/tests/test_all.h"
diff --git a/engines/ags/console.cpp b/engines/ags/console.cpp
index 285e8567ad3..02a274d832f 100644
--- a/engines/ags/console.cpp
+++ b/engines/ags/console.cpp
@@ -24,7 +24,7 @@
 #include "ags/globals.h"
 #include "ags/shared/ac/sprite_cache.h"
 #include "ags/shared/gfx/allegro_bitmap.h"
-#include "ags/shared/script/cc_options.h"
+#include "ags/shared/script/cc_common.h"
 #include "image/png.h"
 
 namespace AGS {
diff --git a/engines/ags/engine/ac/dynobj/cc_dynamic_object.cpp b/engines/ags/engine/ac/dynobj/cc_dynamic_object.cpp
index 981dbfaacfd..fa8e95b3746 100644
--- a/engines/ags/engine/ac/dynobj/cc_dynamic_object.cpp
+++ b/engines/ags/engine/ac/dynobj/cc_dynamic_object.cpp
@@ -33,15 +33,11 @@
 //
 //=============================================================================
 
-//#define DEBUG_MANAGED_OBJECTS
-
-//include <stdlib.h>
-//include <string.h>
 #include "ags/engine/ac/dynobj/cc_dynamic_object.h"
 #include "ags/engine/ac/dynobj/managed_object_pool.h"
 #include "ags/shared/debugging/out.h"
-#include "ags/shared/script/cc_error.h"
-#include "ags/shared/script/script_common.h"
+#include "ags/shared/script/cc_common.h"
+#include "ags/shared/script/cc_internal.h"
 #include "ags/shared/util/stream.h"
 #include "ags/globals.h"
 
diff --git a/engines/ags/engine/ac/dynobj/managed_object_pool.cpp b/engines/ags/engine/ac/dynobj/managed_object_pool.cpp
index 94909fa9523..dd794a7e433 100644
--- a/engines/ags/engine/ac/dynobj/managed_object_pool.cpp
+++ b/engines/ags/engine/ac/dynobj/managed_object_pool.cpp
@@ -24,8 +24,8 @@
 #include "ags/engine/ac/dynobj/cc_dynamic_array.h" // globalDynamicArray, constants
 #include "ags/shared/debugging/out.h"
 #include "ags/shared/util/string_utils.h"               // fputstring, etc
-#include "ags/shared/script/cc_error.h"
-#include "ags/shared/script/script_common.h"
+#include "ags/shared/script/cc_common.h"
+#include "ags/shared/script/cc_internal.h"
 #include "ags/shared/util/stream.h"
 #include "ags/globals.h"
 
diff --git a/engines/ags/engine/ac/event.cpp b/engines/ags/engine/ac/event.cpp
index 924a2cd79a3..fddd41b67e7 100644
--- a/engines/ags/engine/ac/event.cpp
+++ b/engines/ags/engine/ac/event.cpp
@@ -30,7 +30,7 @@
 #include "ags/engine/ac/gui.h"
 #include "ags/engine/ac/room_status.h"
 #include "ags/engine/ac/screen.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/platform/base/ags_platform_driver.h"
 #include "ags/plugins/ags_plugin.h"
 #include "ags/plugins/plugin_engine.h"
diff --git a/engines/ags/engine/ac/global_debug.cpp b/engines/ags/engine/ac/global_debug.cpp
index f36e3b2ec90..20f75b3a816 100644
--- a/engines/ags/engine/ac/global_debug.cpp
+++ b/engines/ags/engine/ac/global_debug.cpp
@@ -37,12 +37,12 @@
 #include "ags/engine/ac/walkable_area.h"
 #include "ags/engine/gfx/gfxfilter.h"
 #include "ags/engine/gui/gui_dialog.h"
-#include "ags/shared/script/cc_options.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/engine/debugging/debugger.h"
 #include "ags/engine/main/main.h"
 #include "ags/shared/ac/sprite_cache.h"
 #include "ags/shared/gfx/bitmap.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/gfx/graphics_driver.h"
 #include "ags/engine/main/graphics_mode.h"
 
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index f529de8d7fa..560b3920525 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -62,7 +62,7 @@
 #include "ags/engine/platform/base/ags_platform_driver.h"
 #include "ags/plugins/ags_plugin.h"
 #include "ags/plugins/plugin_engine.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/script/script.h"
 #include "ags/engine/script/script_runtime.h"
 #include "ags/shared/ac/sprite_cache.h"
diff --git a/engines/ags/engine/debugging/debug.cpp b/engines/ags/engine/debugging/debug.cpp
index ca2981a969d..4a090cb6398 100644
--- a/engines/ags/engine/debugging/debug.cpp
+++ b/engines/ags/engine/debugging/debug.cpp
@@ -48,8 +48,8 @@
 #include "ags/engine/platform/base/sys_main.h"
 #include "ags/plugins/plugin_engine.h"
 #include "ags/engine/script/script.h"
-#include "ags/shared/script/script_common.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_internal.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/path.h"
 #include "ags/shared/util/string_utils.h"
 #include "ags/shared/util/text_stream_writer.h"
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 10d6b506b8a..22e992c495e 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -49,7 +49,7 @@
 #include "ags/engine/media/audio/audio_system.h"
 #include "ags/engine/platform/base/ags_platform_driver.h"
 #include "ags/plugins/plugin_engine.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/script/exports.h"
 #include "ags/engine/script/script.h"
 #include "ags/engine/script/script_runtime.h"
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index 0eb59d36cb7..3f64117102d 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -57,7 +57,7 @@
 #include "ags/plugins/ags_plugin.h"
 #include "ags/plugins/plugin_engine.h"
 #include "ags/engine/script/script.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/aligned_stream.h"
 #include "ags/shared/util/file.h"
 #include "ags/shared/util/stream.h"
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index ee5fe72990b..f110d06823e 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -54,7 +54,7 @@
 #include "ags/shared/gui/gui_textbox.h"
 #include "ags/plugins/ags_plugin.h"
 #include "ags/plugins/plugin_engine.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/script/script.h"
 #include "ags/shared/util/file_stream.h" // TODO: needed only because plugins expect file handle
 #include "ags/engine/media/audio/audio_system.h"
diff --git a/engines/ags/engine/game/savegame_v321.cpp b/engines/ags/engine/game/savegame_v321.cpp
index 3ceab96aaad..c0a45b6d5dc 100644
--- a/engines/ags/engine/game/savegame_v321.cpp
+++ b/engines/ags/engine/game/savegame_v321.cpp
@@ -52,7 +52,7 @@
 #include "ags/plugins/ags_plugin.h"
 #include "ags/plugins/plugin_engine.h"
 #include "ags/engine/script/script.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/aligned_stream.h"
 #include "ags/shared/util/string_utils.h"
 
diff --git a/engines/ags/engine/main/game_file.cpp b/engines/ags/engine/main/game_file.cpp
index ef2e6d12a18..5c9b2ae8dc7 100644
--- a/engines/ags/engine/main/game_file.cpp
+++ b/engines/ags/engine/main/game_file.cpp
@@ -45,7 +45,7 @@
 #include "ags/shared/gui/gui_label.h"
 #include "ags/engine/main/main.h"
 #include "ags/engine/platform/base/ags_platform_driver.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/script/script.h"
 #include "ags/shared/util/aligned_stream.h"
 #include "ags/shared/util/stream.h"
diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index 640b06efae2..d4f8544025c 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -24,11 +24,10 @@
 #include "ags/engine/ac/dynobj/cc_dynamic_array.h"
 #include "ags/engine/ac/dynobj/managed_object_pool.h"
 #include "ags/shared/gui/gui_defines.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/script/cc_instance.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/shared/debugging/out.h"
-#include "ags/shared/script/cc_options.h"
 #include "ags/engine/script/script.h"
 #include "ags/engine/script/script_runtime.h"
 #include "ags/engine/script/system_imports.h"
diff --git a/engines/ags/engine/script/cc_instance.h b/engines/ags/engine/script/cc_instance.h
index 8b468f59e34..423bef9f738 100644
--- a/engines/ags/engine/script/cc_instance.h
+++ b/engines/ags/engine/script/cc_instance.h
@@ -24,7 +24,7 @@
 
 #include "ags/lib/std/memory.h"
 #include "ags/lib/std/map.h"
-#include "ags/shared/script/script_common.h"
+#include "ags/shared/script/cc_internal.h"
 #include "ags/shared/script/cc_script.h"  // ccScript
 #include "ags/engine/script/non_blocking_script_function.h"
 #include "ags/shared/util/string.h"
@@ -48,9 +48,6 @@ using namespace AGS;
 #define INSTANCE_ID_MASK  0x00000000000000ffLL
 #define INSTANCE_ID_REMOVEMASK 0x0000000000ffffffLL
 
-struct ccInstance;
-struct ScriptImport;
-
 struct ScriptInstruction {
 	ScriptInstruction() {
 		Code = 0;
@@ -100,7 +97,6 @@ struct ScriptPosition {
 // Running instance of the script
 struct ccInstance {
 public:
-	// TODO: change to std:: if moved to C++11
 	typedef std::unordered_map<int32_t, ScriptVariable> ScVarMap;
 	typedef std::shared_ptr<ScVarMap>                   PScVarMap;
 public:
diff --git a/engines/ags/engine/script/runtime_script_value.cpp b/engines/ags/engine/script/runtime_script_value.cpp
index dd83770d6be..7554530693a 100644
--- a/engines/ags/engine/script/runtime_script_value.cpp
+++ b/engines/ags/engine/script/runtime_script_value.cpp
@@ -19,14 +19,12 @@
  *
  */
 
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/script/runtime_script_value.h"
 #include "ags/engine/ac/dynobj/cc_dynamic_object.h"
 #include "ags/engine/ac/statobj/static_object.h"
 #include "ags/shared/util/memory.h"
 
-//include <string.h> // for memcpy()
-
 namespace AGS3 {
 
 using namespace AGS::Shared;
diff --git a/engines/ags/engine/script/script.cpp b/engines/ags/engine/script/script.cpp
index 409fd59405b..d243876cb47 100644
--- a/engines/ags/engine/script/script.cpp
+++ b/engines/ags/engine/script/script.cpp
@@ -41,8 +41,7 @@
 #include "ags/engine/ac/mouse.h"
 #include "ags/engine/ac/room.h"
 #include "ags/engine/ac/room_object.h"
-#include "ags/shared/script/cc_error.h"
-#include "ags/shared/script/cc_options.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/debugging/debugger.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/engine/main/game_run.h"
diff --git a/engines/ags/engine/script/script_api.cpp b/engines/ags/engine/script/script_api.cpp
index 2b9e21a7ea3..5943b5f351e 100644
--- a/engines/ags/engine/script/script_api.cpp
+++ b/engines/ags/engine/script/script_api.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "ags/shared/ac/game_version.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/script/runtime_script_value.h"
 #include "ags/engine/script/script_api.h"
 #include "ags/shared/util/math.h"
diff --git a/engines/ags/engine/script/script_engine.cpp b/engines/ags/engine/script/script_engine.cpp
index f7c31a77d54..92478a37643 100644
--- a/engines/ags/engine/script/script_engine.cpp
+++ b/engines/ags/engine/script/script_engine.cpp
@@ -32,7 +32,7 @@
 
 #include "ags/lib/std/utility.h"
 #include "ags/engine/script/cc_instance.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/file.h"
 #include "ags/shared/util/stream.h"
 #include "ags/globals.h"
diff --git a/engines/ags/engine/script/script_runtime.cpp b/engines/ags/engine/script/script_runtime.cpp
index 292cd27d63a..668bc788caf 100644
--- a/engines/ags/engine/script/script_runtime.cpp
+++ b/engines/ags/engine/script/script_runtime.cpp
@@ -33,15 +33,11 @@
 //
 //=============================================================================
 
-#include "ags/engine/script/script_runtime.h"
-#include "ags/shared/script/script_common.h"
-#include "ags/shared/script/cc_error.h"
-#include "ags/shared/script/cc_options.h"
 #include "ags/engine/ac/dynobj/cc_dynamic_array.h"
-#include "ags/engine/script/system_imports.h"
 #include "ags/engine/ac/statobj/static_object.h"
-#include "ags/plugins/ags_plugin.h"
-#include "ags/plugins/plugin_base.h"
+#include "ags/shared/script/cc_common.h"
+#include "ags/engine/script/system_imports.h"
+#include "ags/engine/script/script_runtime.h"
 #include "ags/globals.h"
 
 namespace AGS3 {
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index d4da8007e60..ed1c82d30da 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -36,7 +36,7 @@
 #include "ags/shared/gui/gui_listbox.h"
 #include "ags/shared/gui/gui_slider.h"
 #include "ags/shared/gui/gui_textbox.h"
-#include "ags/shared/script/cc_options.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/directory.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/draw_software.h"
diff --git a/engines/ags/module.mk b/engines/ags/module.mk
index 62404edf8d9..50ba7cc75c1 100644
--- a/engines/ags/module.mk
+++ b/engines/ags/module.mk
@@ -68,8 +68,7 @@ MODULE_OBJS = \
 	shared/gui/gui_object.o \
 	shared/gui/gui_slider.o \
 	shared/gui/gui_textbox.o \
-	shared/script/cc_error.o \
-	shared/script/cc_options.o \
+	shared/script/cc_common.o \
 	shared/script/cc_script.o \
 	shared/util/aligned_stream.o \
 	shared/util/buffered_stream.o \
diff --git a/engines/ags/shared/game/main_game_file.cpp b/engines/ags/shared/game/main_game_file.cpp
index 9604a2c5363..841224736b8 100644
--- a/engines/ags/shared/game/main_game_file.cpp
+++ b/engines/ags/shared/game/main_game_file.cpp
@@ -32,7 +32,7 @@
 #include "ags/shared/game/main_game_file.h"
 #include "ags/shared/font/fonts.h"
 #include "ags/shared/gui/gui_main.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/aligned_stream.h"
 #include "ags/shared/util/data_ext.h"
 #include "ags/shared/util/path.h"
diff --git a/engines/ags/shared/game/room_file.cpp b/engines/ags/shared/game/room_file.cpp
index 5569bd0dfd5..e636d839c11 100644
--- a/engines/ags/shared/game/room_file.cpp
+++ b/engines/ags/shared/game/room_file.cpp
@@ -29,7 +29,7 @@
 #include "ags/shared/game/room_file.h"
 #include "ags/shared/game/room_struct.h"
 #include "ags/shared/gfx/bitmap.h"
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/shared/script/cc_script.h"
 #include "ags/shared/util/compress.h"
 #include "ags/shared/util/data_ext.h"
diff --git a/engines/ags/shared/script/cc_error.cpp b/engines/ags/shared/script/cc_common.cpp
similarity index 88%
rename from engines/ags/shared/script/cc_error.cpp
rename to engines/ags/shared/script/cc_common.cpp
index 83e5ad7e5e2..9216ea1e5a9 100644
--- a/engines/ags/shared/script/cc_error.cpp
+++ b/engines/ags/shared/script/cc_common.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "ags/lib/std/utility.h"
-#include "ags/shared/script/script_common.h"  // current_line
+#include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/string.h"
 #include "ags/globals.h"
 
@@ -28,6 +28,20 @@ namespace AGS3 {
 
 using namespace AGS::Shared;
 
+void ccSetOption(int optbit, int onoroff) {
+	if (onoroff)
+		_G(ccCompOptions) |= optbit;
+	else
+		_G(ccCompOptions) &= ~optbit;
+}
+
+int ccGetOption(int optbit) {
+	if (_G(ccCompOptions) & optbit)
+		return 1;
+
+	return 0;
+}
+
 // Returns full script error message and callstack (if possible)
 extern std::pair<String, String> cc_error_at_line(const char *error_msg);
 // Returns script error message without location or callstack
diff --git a/engines/ags/shared/script/cc_options.h b/engines/ags/shared/script/cc_common.h
similarity index 85%
rename from engines/ags/shared/script/cc_options.h
rename to engines/ags/shared/script/cc_common.h
index 82e728c21d8..c282c5e5a4f 100644
--- a/engines/ags/shared/script/cc_options.h
+++ b/engines/ags/shared/script/cc_common.h
@@ -19,8 +19,12 @@
  *
  */
 
-#ifndef AGS_SHARED_SCRIPT_CC_OPTIONS_H
-#define AGS_SHARED_SCRIPT_CC_OPTIONS_H
+// Script options and error reporting.
+
+#ifndef AGS_SHARED_SCRIPT_CC_COMMON_H
+#define AGS_SHARED_SCRIPT_CC_COMMON_H
+
+#include "ags/shared/util/string.h"
 
 namespace AGS3 {
 
@@ -32,10 +36,15 @@ namespace AGS3 {
 #define SCOPT_NOIMPORTOVERRIDE 0x20 // do not allow an import to be re-declared
 #define SCOPT_LEFTTORIGHT 0x40   // left-to-right operator precedance
 #define SCOPT_OLDSTRINGS  0x80   // allow old-style strings
+#define SCOPT_UTF8        0x100  // UTF-8 text mode
 
 extern void ccSetOption(int, int);
 extern int ccGetOption(int);
 
+// error reporting
+
+extern void cc_error(const char *, ...);
+
 } // namespace AGS3
 
 #endif
diff --git a/engines/ags/shared/script/cc_error.h b/engines/ags/shared/script/cc_error.h
deleted file mode 100644
index 71db6d8956a..00000000000
--- a/engines/ags/shared/script/cc_error.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef AGS_SHARED_SCRIPT_CC_ERROR_H
-#define AGS_SHARED_SCRIPT_CC_ERROR_H
-
-#include "ags/shared/util/string.h"
-
-namespace AGS3 {
-
-extern void cc_error(const char *, ...);
-
-} // namespace AGS3
-
-#endif
diff --git a/engines/ags/shared/script/script_common.h b/engines/ags/shared/script/cc_internal.h
similarity index 98%
rename from engines/ags/shared/script/script_common.h
rename to engines/ags/shared/script/cc_internal.h
index 966c9445e3e..cb5e395b3c0 100644
--- a/engines/ags/shared/script/script_common.h
+++ b/engines/ags/shared/script/cc_internal.h
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef AGS_SHARED_SCRIPT_SCRIPT_COMMON_H
-#define AGS_SHARED_SCRIPT_SCRIPT_COMMON_H
+#ifndef AGS_SHARED_SCRIPT_CC_INTERNAL_H
+#define AGS_SHARED_SCRIPT_CC_INTERNAL_H
 
 namespace AGS3 {
 
diff --git a/engines/ags/shared/script/cc_options.cpp b/engines/ags/shared/script/cc_options.cpp
deleted file mode 100644
index e0005e11062..00000000000
--- a/engines/ags/shared/script/cc_options.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "ags/shared/script/cc_options.h"
-#include "ags/globals.h"
-
-namespace AGS3 {
-
-void ccSetOption(int optbit, int onoroff) {
-	if (onoroff)
-		_G(ccCompOptions) |= optbit;
-	else
-		_G(ccCompOptions) &= ~optbit;
-}
-
-int ccGetOption(int optbit) {
-	if (_G(ccCompOptions) & optbit)
-		return 1;
-
-	return 0;
-}
-
-} // namespace AGS3
diff --git a/engines/ags/shared/script/cc_script.cpp b/engines/ags/shared/script/cc_script.cpp
index 07673348930..c33ce46626b 100644
--- a/engines/ags/shared/script/cc_script.cpp
+++ b/engines/ags/shared/script/cc_script.cpp
@@ -19,9 +19,9 @@
  *
  */
 
-#include "ags/shared/script/cc_error.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/shared/script/cc_script.h"
-#include "ags/shared/script/script_common.h"
+#include "ags/shared/script/cc_internal.h"
 #include "ags/shared/util/stream.h"
 #include "ags/shared/util/string_compat.h"
 #include "ags/globals.h"


Commit: 16d0d897872d4f742809a669d2ca266877e6a532
    https://github.com/scummvm/scummvm/commit/16d0d897872d4f742809a669d2ca266877e6a532
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:07-07:00

Commit Message:
AGS: Grouped ccError variables in a struct

>From upstream 48ef387226e4d420dc62d0a0b204cf4cd89a1139

Changed paths:
    engines/ags/engine/ac/event.cpp
    engines/ags/engine/ac/room.cpp
    engines/ags/engine/debugging/debug.cpp
    engines/ags/engine/game/game_init.cpp
    engines/ags/engine/game/savegame.cpp
    engines/ags/engine/game/savegame_components.cpp
    engines/ags/engine/game/savegame_v321.cpp
    engines/ags/engine/main/game_file.cpp
    engines/ags/engine/script/cc_instance.cpp
    engines/ags/engine/script/script.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h
    engines/ags/shared/game/main_game_file.cpp
    engines/ags/shared/game/room_file.cpp
    engines/ags/shared/script/cc_common.cpp
    engines/ags/shared/script/cc_common.h


diff --git a/engines/ags/engine/ac/event.cpp b/engines/ags/engine/ac/event.cpp
index fddd41b67e7..32049642c0b 100644
--- a/engines/ags/engine/ac/event.cpp
+++ b/engines/ags/engine/ac/event.cpp
@@ -133,7 +133,7 @@ void force_event(int evtyp, int ev1, int ev2, int ev3) {
 void process_event(const EventHappened *evp) {
 	RuntimeScriptValue rval_null;
 	if (evp->type == EV_TEXTSCRIPT) {
-		_G(ccError) = 0;
+		cc_clear_error();
 		RuntimeScriptValue params[2]{ evp->data2, evp->data3 };
 		if (evp->data3 > -1000)
 			QueueScriptFunction(kScInstGame, _G(tsnames)[evp->data1], 2, params);
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 560b3920525..52d39b34c24 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -406,7 +406,7 @@ HError LoadRoomScript(RoomStruct *room, int newnum) {
 		if (!script)
 			return new Error(String::FromFormat(
 				"Failed to load a script module: %s", filename.GetCStr()),
-				_G(ccErrorString));
+				cc_get_error().ErrorString);
 		room->CompiledScript = script;
 	}
 	return HError::None();
@@ -989,22 +989,22 @@ void check_new_room() {
 }
 
 void compile_room_script() {
-	_G(ccError) = 0;
+	cc_clear_error();
 
 	_G(roominst) = ccInstance::CreateFromScript(_GP(thisroom).CompiledScript);
-	if ((_G(ccError) != 0) || (_G(roominst) == nullptr)) {
-		quitprintf("Unable to create local script:\n%s", _G(ccErrorString).GetCStr());
+	if (cc_has_error() || (_G(roominst) == nullptr)) {
+		quitprintf("Unable to create local script:\n%s", cc_get_error().ErrorString.GetCStr());
 	}
 
 	if (!_G(roominst)->ResolveScriptImports(_G(roominst)->instanceof.get()))
-		quitprintf("Unable to resolve imports in room script:\n%s", _G(ccErrorString).GetCStr());
+		quitprintf("Unable to resolve imports in room script:\n%s", cc_get_error().ErrorString.GetCStr());
 
 	if (!_G(roominst)->ResolveImportFixups(_G(roominst)->instanceof.get()))
-		quitprintf("Unable to resolve import fixups in room script:\n%s", _G(ccErrorString).GetCStr());
+		quitprintf("Unable to resolve import fixups in room script:\n%s", cc_get_error().ErrorString.GetCStr());
 
 	_G(roominstFork) = _G(roominst)->Fork();
 	if (_G(roominstFork) == nullptr)
-		quitprintf("Unable to create forked room instance:\n%s", _G(ccErrorString).GetCStr());
+		quitprintf("Unable to create forked room instance:\n%s", cc_get_error().ErrorString.GetCStr());
 
 	_GP(repExecAlways).roomHasFunction = true;
 	_GP(lateRepExecAlways).roomHasFunction = true;
diff --git a/engines/ags/engine/debugging/debug.cpp b/engines/ags/engine/debugging/debug.cpp
index 4a090cb6398..9b9076ac584 100644
--- a/engines/ags/engine/debugging/debug.cpp
+++ b/engines/ags/engine/debugging/debug.cpp
@@ -322,7 +322,7 @@ String get_cur_script(int numberOfLinesOfCallStack) {
 	if (sci)
 		callstack = sci->GetCallStack(numberOfLinesOfCallStack);
 	if (callstack.IsEmpty())
-		callstack = _G(ccErrorCallStack);
+		callstack = cc_get_error().CallStack;
 	return callstack;
 }
 
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 22e992c495e..01aa82f49ae 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -444,7 +444,7 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
 	_GP(scriptModules) = ents.ScriptModules;
 	AllocScriptModules();
 	if (create_global_script())
-		return new GameInitError(kGameInitErr_ScriptLinkFailed, _G(ccErrorString));
+		return new GameInitError(kGameInitErr_ScriptLinkFailed, cc_get_error().ErrorString);
 
 	return HGameInitError::None();
 }
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index 3f64117102d..e05bf43b694 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -457,7 +457,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 
 	if (create_global_script()) {
 		return new SavegameError(kSvgErr_GameObjectInitFailed,
-		                         String::FromFormat("Unable to recreate global script: %s", _G(ccErrorString).GetCStr()));
+		                         String::FromFormat("Unable to recreate global script: %s", cc_get_error().ErrorString.GetCStr()));
 	}
 
 	// read the global data into the newly created script
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index f110d06823e..248511f6079 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -989,7 +989,7 @@ HSaveError WriteManagedPool(Stream *out) {
 HSaveError ReadManagedPool(Stream *in, int32_t cmp_ver, const PreservedParams & /*pp*/, RestoredData & /*r_data*/) {
 	if (ccUnserializeAllObjects(in, &_GP(ccUnserializer))) {
 		return new SavegameError(kSvgErr_GameObjectInitFailed,
-		                         String::FromFormat("Managed pool deserialization failed: %s", _G(ccErrorString).GetCStr()));
+		                         String::FromFormat("Managed pool deserialization failed: %s", cc_get_error().ErrorString.GetCStr()));
 	}
 	return HSaveError::None();
 }
diff --git a/engines/ags/engine/game/savegame_v321.cpp b/engines/ags/engine/game/savegame_v321.cpp
index c0a45b6d5dc..18a0e73622a 100644
--- a/engines/ags/engine/game/savegame_v321.cpp
+++ b/engines/ags/engine/game/savegame_v321.cpp
@@ -493,7 +493,7 @@ HSaveError restore_save_data_v321(Stream *in, const PreservedParams &pp, Restore
 
 	if (ccUnserializeAllObjects(in, &_GP(ccUnserializer))) {
 		return new SavegameError(kSvgErr_GameObjectInitFailed,
-		                         String::FromFormat("Managed pool deserialization failed: %s.", _G(ccErrorString).GetCStr()));
+		                         String::FromFormat("Managed pool deserialization failed: %s.", cc_get_error().ErrorString.GetCStr()));
 	}
 
 	// preserve legacy music type setting
diff --git a/engines/ags/engine/main/game_file.cpp b/engines/ags/engine/main/game_file.cpp
index 5c9b2ae8dc7..a61aea73b05 100644
--- a/engines/ags/engine/main/game_file.cpp
+++ b/engines/ags/engine/main/game_file.cpp
@@ -119,7 +119,7 @@ HError preload_game_data() {
 static inline HError MakeScriptLoadError(const char *name) {
 	return new Error(String::FromFormat(
 		"Failed to load a script module: %s", name),
-		_G(ccErrorString));
+		cc_get_error().ErrorString);
 }
 
 // Looks up for the game scripts available as separate assets.
diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index d4f8544025c..d80377b54e5 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -262,7 +262,7 @@ void ccInstance::AbortAndDestroy() {
 	}
 
 int ccInstance::CallScriptFunction(const char *funcname, int32_t numargs, const RuntimeScriptValue *params) {
-	_G(ccError) = 0;
+	cc_clear_error();
 	_G(currentline) = 0;
 
 	if (numargs > 0 && !params) {
@@ -367,7 +367,7 @@ int ccInstance::CallScriptFunction(const char *funcname, int32_t numargs, const
 		cc_error("stack pointer was not zero at completion of script");
 		return -5;
 	}
-	return _G(ccError);
+	return cc_has_error();
 }
 
 // Macros to maintain the call stack
@@ -532,7 +532,7 @@ int ccInstance::Run(int32_t curpc) {
 					registers[SREG_SP].RValue++;
 				} else {
 					PushDataToStack(arg2.IValue);
-					if (_G(ccError)) {
+					if (cc_has_error()) {
 						return -1;
 					}
 				}
@@ -553,7 +553,7 @@ int ccInstance::Run(int32_t curpc) {
 					// This is practically LOADSPOFFS
 					reg1 = GetStackPtrOffsetRw(arg2.IValue);
 				}
-				if (_G(ccError)) {
+				if (cc_has_error()) {
 					return -1;
 				}
 			} else {
@@ -614,7 +614,7 @@ int ccInstance::Run(int32_t curpc) {
 			break;
 		case SCMD_LOADSPOFFS:
 			registers[SREG_MAR] = GetStackPtrOffsetRw(arg1.IValue);
-			if (_G(ccError)) {
+			if (cc_has_error()) {
 				return -1;
 			}
 			break;
@@ -693,9 +693,6 @@ int ccInstance::Run(int32_t curpc) {
 
 			ASSERT_STACK_SPACE_AVAILABLE(1);
 			PushValueToStack(RuntimeScriptValue().SetInt32(pc + codeOp.ArgCount + 1));
-			if (_G(ccError)) {
-				return -1;
-			}
 
 			if (thisbase[curnest] == 0)
 				pc = reg1.IValue;
@@ -741,9 +738,6 @@ int ccInstance::Run(int32_t curpc) {
 			// Push reg[arg1] value to the stack
 			ASSERT_STACK_SPACE_AVAILABLE(1);
 			PushValueToStack(reg1);
-			if (_G(ccError)) {
-				return -1;
-			}
 			break;
 		case SCMD_POPREG:
 			ASSERT_STACK_SIZE(1);
@@ -796,7 +790,7 @@ int ccInstance::Run(int32_t curpc) {
 		// 64 bit: Handles are always 32 bit values. They are not C pointer.
 
 		case SCMD_MEMREADPTR: {
-			_G(ccError) = 0;
+			cc_clear_error();
 
 			int32_t handle = registers[SREG_MAR].ReadInt32();
 			void *object;
@@ -809,7 +803,7 @@ int ccInstance::Run(int32_t curpc) {
 			}
 
 			// if error occurred, cc_error will have been set
-			if (_G(ccError))
+			if (cc_has_error())
 				return -1;
 			break;
 		}
@@ -925,7 +919,7 @@ int ccInstance::Run(int32_t curpc) {
 			// 0, so that the cc_run_code returns
 			RuntimeScriptValue oldstack = registers[SREG_SP];
 			PushValueToStack(RuntimeScriptValue().SetInt32(0));
-			if (_G(ccError)) {
+			if (cc_has_error()) {
 				return -1;
 			}
 
@@ -1021,7 +1015,7 @@ int ccInstance::Run(int32_t curpc) {
 				cc_error("invalid pointer type for function call: %d", reg1.Type);
 			}
 
-			if (_G(ccError) || _G(abort_engine)) {
+			if (cc_has_error() || _G(abort_engine)) {
 				return -1;
 			}
 
diff --git a/engines/ags/engine/script/script.cpp b/engines/ags/engine/script/script.cpp
index d243876cb47..3a89de31303 100644
--- a/engines/ags/engine/script/script.cpp
+++ b/engines/ags/engine/script/script.cpp
@@ -302,22 +302,21 @@ static bool DoRunScriptFuncCantBlock(ccInstance *sci, NonBlockingScriptFunction
 	}
 
 	// this might be nested, so don't disrupt blocked scripts
-	_G(ccErrorString) = "";
-	_G(ccError) = 0;
+	cc_clear_error();
 	_G(no_blocking_functions)--;
 	return (hasTheFunc);
 }
 
 static int PrepareTextScript(ccInstance *sci, const char **tsname) {
-	_G(ccError) = 0;
+	cc_clear_error();
 	// FIXME: try to make it so this function is not called with NULL sci
 	if (sci == nullptr) return -1;
 	if (sci->GetSymbolAddress(tsname[0]).IsNull()) {
-		_G(ccErrorString) = "no such function in script";
+		cc_error("no such function in script");
 		return -2;
 	}
 	if (sci->IsBeingRun()) {
-		_G(ccErrorString) = "script is already in execution";
+		cc_error("script is already in execution");
 		return -3;
 	}
 	_G(scripts)[_G(num_scripts)].init();
@@ -339,36 +338,21 @@ static int PrepareTextScript(ccInstance *sci, const char **tsname) {
 	tsname[0] = &scfunctionname[0];
 	update_script_mouse_coords();
 	_G(inside_script)++;
-	//  aborted_ip=0;
-	//  abort_executor=0;
 	return 0;
 }
 
 int RunScriptFunction(ccInstance *sci, const char *tsname, size_t numParam, const RuntimeScriptValue *params) {
 	int oldRestoreCount = _G(gameHasBeenRestored);
-	// First, save the current ccError state
-	// This is necessary because we might be attempting
-	// to run Script B, while Script A is still running in the
-	// background.
-	// If CallInstance here has an error, it would otherwise
-	// also abort Script A because ccError is a global variable.
-	int cachedCcError = _G(ccError);
-	_G(ccError) = 0;
 
+	cc_clear_error();
 	int toret = PrepareTextScript(sci, &tsname);
 	if (toret) {
-		_G(ccError) = cachedCcError;
 		return -18;
 	}
 
-	// Clear the error message
-	_G(ccErrorString) = "";
-
+	cc_clear_error();
 	toret = _G(curscript)->inst->CallScriptFunction(tsname, numParam, params);
 
-	if (_G(abort_engine))
-		return -1;
-
 	// 100 is if Aborted (eg. because we are LoadAGSGame'ing)
 	if ((toret != 0) && (toret != -2) && (toret != 100)) {
 		quit_with_script_error(tsname);
@@ -383,9 +367,6 @@ int RunScriptFunction(ccInstance *sci, const char *tsname, size_t numParam, cons
 
 	_G(post_script_cleanup_stack)--;
 
-	// restore cached error state
-	_G(ccError) = cachedCcError;
-
 	// if the game has been restored, ensure that any further scripts are not run
 	if ((oldRestoreCount != _G(gameHasBeenRestored)) && (_G(eventClaimed) == EVENT_INPROGRESS))
 		_G(eventClaimed) = EVENT_CLAIMED;
@@ -408,7 +389,7 @@ int RunScriptFunctionInRoom(const char *tsname, size_t param_count, const Runtim
 	// If it's a obligatory room event, and return code means missing function - error
 	if (strict_room_event && (toret == -18))
 		quitprintf("RunScriptFunction: error %d (%s) trying to run '%s'   (Room %d)",
-			toret, _G(ccErrorString).GetCStr(), tsname, _G(displayed_room));
+			toret, cc_get_error().ErrorString.GetCStr(), tsname, _G(displayed_room));
 	return toret;
 }
 
@@ -487,7 +468,8 @@ char *make_ts_func_name(const char *base, int iii, int subd) {
 
 void post_script_cleanup() {
 	// should do any post-script stuff here, like go to new room
-	if (_G(ccError)) quit(_G(ccErrorString));
+	if (cc_has_error())
+		quit(cc_get_error().ErrorString);
 	ExecutingScript copyof = _G(scripts)[_G(num_scripts) - 1];
 	if (_G(scripts)[_G(num_scripts) - 1].forked)
 		delete _G(scripts)[_G(num_scripts) - 1].inst;
@@ -577,10 +559,12 @@ void quit_with_script_error(const char *functionName) {
 	// TODO: clean up the error reporting logic. Now engine will append call
 	// stack info in quit_check_for_error_state() but only in case of explicit
 	// script error ("!" type), and not in other case.
-	if (_G(ccErrorIsUserError))
-		quitprintf("!Error running function '%s':\n%s", functionName, _G(ccErrorString).GetCStr());
+	const auto &error = cc_get_error();
+	if (error.IsUserError)
+		quitprintf("!Error running function '%s':\n%s", functionName, error.ErrorString.GetCStr());
 	else
-		quitprintf("Error running function '%s':\n%s\n\n%s", functionName, _G(ccErrorString).GetCStr(), get_cur_script(5).GetCStr());
+		quitprintf("Error running function '%s':\n%s\n\n%s", functionName,
+			error.ErrorString.GetCStr(), get_cur_script(5).GetCStr());
 }
 
 int get_nivalue(InteractionCommandList *nic, int idx, int parm) {
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index ed1c82d30da..cee427f6d65 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -141,6 +141,9 @@ Globals::Globals() {
 	// cc_dynamicarray.cpp globals
 	_globalDynamicArray = new CCDynamicArray();
 
+	// cc_common globals
+	_ccError = new ScriptError();
+
 	// csc_dialog.cpp globals
 	_vobjs = new NewControl *[MAXCONTROLS];
 	_oswi = new OnScreenWindow[MAXSCREENWINDOWS];
@@ -407,6 +410,9 @@ Globals::~Globals() {
 	// cc_dynamic_array.cpp globals
 	delete _globalDynamicArray;
 
+	// cc_common.cpp globals
+	delete _ccError;
+
 	// cscdialog.cpp globals
 	delete[] _vobjs;
 	delete[] _oswi;
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index f14d33d7fbe..a5aa0b1ee7a 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -162,6 +162,7 @@ struct ScriptAudioChannel;
 struct ScriptDialog;
 struct ScriptDialogOptionsRendering;
 struct ScriptDrawingSurface;
+struct ScriptError;
 struct ScriptGUI;
 struct ScriptHotspot;
 struct ScriptInvItem;
@@ -350,17 +351,11 @@ public:
 	/**@}*/
 
 	/**
-	 * @defgroup agscc_errorglobals cc_error globals
+	 * @defgroup agscc_commonglobals cc_common globals
 	 * @ingroup agsglobals
 	 * @{
 	 */
-
-	int _ccError = 0;
-	int _ccErrorLine = 0;
-	String _ccErrorString;
-	String _ccErrorCallStack;
-	bool _ccErrorIsUserError = false;
-	const char *_ccCurScriptName = "";
+	ScriptError *_ccError;
 
 	/**@}*/
 
diff --git a/engines/ags/shared/game/main_game_file.cpp b/engines/ags/shared/game/main_game_file.cpp
index 841224736b8..7c4932c4970 100644
--- a/engines/ags/shared/game/main_game_file.cpp
+++ b/engines/ags/shared/game/main_game_file.cpp
@@ -220,7 +220,7 @@ HGameFileError ReadDialogScript(PScript &dialog_script, Stream *in, GameDataVers
 	if (data_ver > kGameVersion_310) { // 3.1.1+ dialog script
 		dialog_script.reset(ccScript::CreateFromStream(in));
 		if (dialog_script == nullptr)
-			return new MainGameFileError(kMGFErr_CreateDialogScriptFailed, _G(ccErrorString));
+			return new MainGameFileError(kMGFErr_CreateDialogScriptFailed, cc_get_error().ErrorString);
 	} else { // 2.x and < 3.1.1 dialog
 		dialog_script.reset();
 	}
@@ -234,7 +234,7 @@ HGameFileError ReadScriptModules(std::vector<PScript> &sc_mods, Stream *in, Game
 		for (int i = 0; i < count; ++i) {
 			sc_mods[i].reset(ccScript::CreateFromStream(in));
 			if (sc_mods[i] == nullptr)
-				return new MainGameFileError(kMGFErr_CreateScriptModuleFailed, _G(ccErrorString));
+				return new MainGameFileError(kMGFErr_CreateScriptModuleFailed, cc_get_error().ErrorString);
 		}
 	} else {
 		sc_mods.resize(0);
@@ -778,7 +778,7 @@ HGameFileError ReadGameData(LoadedGameEntities &ents, Stream *in, GameDataVersio
 	if (game.load_compiled_script) {
 		ents.GlobalScript.reset(ccScript::CreateFromStream(in));
 		if (!ents.GlobalScript)
-			return new MainGameFileError(kMGFErr_CreateGlobalScriptFailed, _G(ccErrorString));
+			return new MainGameFileError(kMGFErr_CreateGlobalScriptFailed, cc_get_error().ErrorString);
 		err = ReadDialogScript(ents.DialogScript, in, data_ver);
 		if (!err)
 			return err;
diff --git a/engines/ags/shared/game/room_file.cpp b/engines/ags/shared/game/room_file.cpp
index e636d839c11..0db9e4bd203 100644
--- a/engines/ags/shared/game/room_file.cpp
+++ b/engines/ags/shared/game/room_file.cpp
@@ -332,7 +332,7 @@ HError ReadScriptBlock(char *&buf, Stream *in, RoomFileVersion /*data_ver*/) {
 HError ReadCompSc3Block(RoomStruct *room, Stream *in, RoomFileVersion /*data_ver*/) {
 	room->CompiledScript.reset(ccScript::CreateFromStream(in));
 	if (room->CompiledScript == nullptr)
-		return new RoomFileError(kRoomFileErr_ScriptLoadFailed, _G(ccErrorString));
+		return new RoomFileError(kRoomFileErr_ScriptLoadFailed, cc_get_error().ErrorString);
 	return HError::None();
 }
 
diff --git a/engines/ags/shared/script/cc_common.cpp b/engines/ags/shared/script/cc_common.cpp
index 9216ea1e5a9..054c8b20a32 100644
--- a/engines/ags/shared/script/cc_common.cpp
+++ b/engines/ags/shared/script/cc_common.cpp
@@ -47,10 +47,22 @@ extern std::pair<String, String> cc_error_at_line(const char *error_msg);
 // Returns script error message without location or callstack
 extern String cc_error_without_line(const char *error_msg);
 
+void cc_clear_error() {
+	_GP(ccError) = ScriptError();
+}
+
+bool cc_has_error() {
+	return _GP(ccError).HasError;
+}
+
+const ScriptError &cc_get_error() {
+	return _GP(ccError);
+}
+
 void cc_error(const char *descr, ...) {
-	_G(ccErrorIsUserError) = false;
+	_GP(ccError).IsUserError = false;
 	if (descr[0] == '!') {
-		_G(ccErrorIsUserError) = true;
+		_GP(ccError).IsUserError = true;
 		descr++;
 	}
 
@@ -62,15 +74,15 @@ void cc_error(const char *descr, ...) {
 	if (_G(currentline) > 0) {
 		// [IKM] Implementation is project-specific
 		std::pair<String, String> errinfo = cc_error_at_line(displbuf.GetCStr());
-		_G(ccErrorString) = errinfo.first;
-		_G(ccErrorCallStack) = errinfo.second;
+		_GP(ccError).ErrorString = errinfo.first;
+		_GP(ccError).CallStack = errinfo.second;
 	} else {
-		_G(ccErrorString) = cc_error_without_line(displbuf.GetCStr());
-		_G(ccErrorCallStack) = "";
+		_GP(ccError).ErrorString = cc_error_without_line(displbuf.GetCStr());
+		_GP(ccError).CallStack = "";
 	}
 
-	_G(ccError) = 1;
-	_G(ccErrorLine) = _G(currentline);
+	_GP(ccError).HasError = 1;
+	_GP(ccError).Line = _G(currentline);
 }
 
 } // namespace AGS3
diff --git a/engines/ags/shared/script/cc_common.h b/engines/ags/shared/script/cc_common.h
index c282c5e5a4f..8c0678aab04 100644
--- a/engines/ags/shared/script/cc_common.h
+++ b/engines/ags/shared/script/cc_common.h
@@ -43,7 +43,18 @@ extern int ccGetOption(int);
 
 // error reporting
 
-extern void cc_error(const char *, ...);
+struct ScriptError {
+	bool HasError = false; // set if error occurs
+	bool IsUserError = false; // marks script use errors
+	AGS::Shared::String ErrorString; // description of the error
+	int Line = 0;  // line number of the error
+	AGS::Shared::String CallStack; // callstack where error happened
+};
+
+void cc_clear_error();
+bool cc_has_error();
+const ScriptError &cc_get_error();
+void cc_error(const char *, ...);
 
 } // namespace AGS3
 


Commit: 8e132f7b3f5d95212d0d7d4a6a63753cbce1ba75
    https://github.com/scummvm/scummvm/commit/8e132f7b3f5d95212d0d7d4a6a63753cbce1ba75
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:07-07:00

Commit Message:
AGS: cc_error returns stack from all script threads

>From upstream 9e76800d668d8d0af7d47d815e6d1b781c7601f6

Changed paths:
  R engines/ags/engine/script/script_engine.cpp
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/debugging/debug.cpp
    engines/ags/engine/debugging/debugger.h
    engines/ags/engine/main/quit.cpp
    engines/ags/engine/script/cc_instance.cpp
    engines/ags/engine/script/cc_instance.h
    engines/ags/engine/script/script.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h
    engines/ags/lib/std/queue.h
    engines/ags/module.mk
    engines/ags/shared/script/cc_common.cpp


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index c781bfa4619..4e7910f7cc9 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -1675,7 +1675,7 @@ void prepare_room_sprites() {
 
 			if (_G(walkBehindMethod) == DrawAsSeparateSprite) {
 				for (int wb = 1 /* 0 is "no area" */;
-					(wb < MAX_WALK_BEHINDS) && (wb < _GP(walkbehindobj).size()); ++wb) {
+					(wb < MAX_WALK_BEHINDS) && (wb < (int)_GP(walkbehindobj).size()); ++wb) {
 					const auto &wbobj = _GP(walkbehindobj)[wb];
 					if (wbobj.Ddb) {
 						add_to_sprite_list(wbobj.Ddb, wbobj.Pos.X, wbobj.Pos.Y,
diff --git a/engines/ags/engine/debugging/debug.cpp b/engines/ags/engine/debugging/debug.cpp
index 9b9076ac584..3b0644d8c32 100644
--- a/engines/ags/engine/debugging/debug.cpp
+++ b/engines/ags/engine/debugging/debug.cpp
@@ -315,24 +315,13 @@ void debug_script_log(const char *msg, ...) {
 	debug_script_print_impl(full_msg, kDbgMsg_Debug);
 }
 
-
-String get_cur_script(int numberOfLinesOfCallStack) {
-	String callstack;
-	ccInstance *sci = ccInstance::GetCurrentInstance();
-	if (sci)
-		callstack = sci->GetCallStack(numberOfLinesOfCallStack);
-	if (callstack.IsEmpty())
-		callstack = cc_get_error().CallStack;
-	return callstack;
-}
-
 struct Breakpoint {
 	char scriptName[80];
 	int lineNumber;
 };
 
 bool send_message_to_editor(const char *msg, const char *errorMsg) {
-	String callStack = get_cur_script(25);
+	String callStack = cc_get_error().CallStack;
 	if (callStack.IsEmpty())
 		return false;
 
diff --git a/engines/ags/engine/debugging/debugger.h b/engines/ags/engine/debugging/debugger.h
index 0929797b366..60ad55026e8 100644
--- a/engines/ags/engine/debugging/debugger.h
+++ b/engines/ags/engine/debugging/debugger.h
@@ -32,8 +32,6 @@ struct ScriptPosition;
 int check_for_messages_from_editor();
 bool send_message_to_editor(const char *msg);
 bool send_exception_to_editor(const char *qmsg);
-// Returns current script's location and callstack
-AGS::Shared::String get_cur_script(int numberOfLinesOfCallStack);
 void check_debug_keys();
 
 #define DBG_NOIFACE       1
diff --git a/engines/ags/engine/main/quit.cpp b/engines/ags/engine/main/quit.cpp
index ae73e19bd00..77a8413ad45 100644
--- a/engines/ags/engine/main/quit.cpp
+++ b/engines/ags/engine/main/quit.cpp
@@ -48,6 +48,7 @@
 #include "ags/engine/platform/base/ags_platform_driver.h"
 #include "ags/engine/platform/base/sys_main.h"
 #include "ags/plugins/plugin_engine.h"
+#include "ags/shared/script/cc_common.h"
 #include "ags/engine/media/audio/audio_system.h"
 #include "ags/globals.h"
 #include "ags/ags.h"
@@ -114,7 +115,7 @@ QuitReason quit_check_for_error_state(const char *&qmsg, String &alertis) {
 			               "(ACI version %s)\n\n", _G(EngineVersion).LongString.GetCStr());
 		}
 
-		alertis.Append(get_cur_script(5));
+		alertis.Append(cc_get_error().CallStack);
 
 		if (qreason != kQuit_UserAbort)
 			alertis.Append("\nError: ");
@@ -125,7 +126,7 @@ QuitReason quit_check_for_error_state(const char *&qmsg, String &alertis) {
 		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", _G(EngineVersion).LongString.GetCStr(), get_cur_script(5).GetCStr());
+		               "(ACI version %s)\n\n%s\n", _G(EngineVersion).LongString.GetCStr(), cc_get_error().CallStack.GetCStr());
 		return kQuit_GameWarning;
 	} else {
 		alertis.Format("An internal error has occurred. Please note down the following information.\n"
diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index d80377b54e5..3a0217bca5e 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -157,6 +157,18 @@ const char *regnames[] = { "null", "sp", "mar", "ax", "bx", "cx", "op", "dx" };
 
 const char *fixupnames[] = { "null", "fix_gldata", "fix_func", "fix_string", "fix_import", "fix_datadata", "fix_stack" };
 
+String cc_get_callstack(int max_lines) {
+	String callstack;
+	for (auto sci = _GP(InstThreads).crbegin(); sci != _GP(InstThreads).crend(); ++sci) {
+		if (callstack.IsEmpty())
+			callstack.Append("in the active script:\n");
+		else
+			callstack.Append("in the waiting script:\n");
+		callstack.Append((*sci)->GetCallStack(max_lines));
+	}
+	return callstack;
+}
+
 // Function call stack is used to temporarily store
 // values before passing them to script function
 #define MAX_FUNC_PARAMS 20
@@ -181,7 +193,7 @@ struct FunctionCallStack {
 
 
 ccInstance *ccInstance::GetCurrentInstance() {
-	return _GP(InstThreads).size() > 0 ? _GP(InstThreads).top() : nullptr;
+	return _GP(InstThreads).size() > 0 ? _GP(InstThreads).back() : nullptr;
 }
 
 ccInstance *ccInstance::CreateFromScript(PScript scri) {
@@ -335,14 +347,14 @@ int ccInstance::CallScriptFunction(const char *funcname, int32_t numargs, const
 		}
 	PushValueToStack(RuntimeScriptValue().SetInt32(0)); // return address on stack
 
-	_GP(InstThreads).push(this); // push instance thread
+	_GP(InstThreads).push_back(this); // push instance thread
 	runningInst = this;
 	int reterr = Run(startat);
 	// Cleanup before returning, even if error
 	ASSERT_STACK_SIZE(numargs);
 	PopValuesFromStack(numargs);
 	pc = 0;
-	_GP(InstThreads).pop(); // pop instance thread
+	_GP(InstThreads).pop_back(); // pop instance thread
 	if (reterr != 0)
 		return reterr;
 
@@ -1198,7 +1210,7 @@ int ccInstance::Run(int32_t curpc) {
 	}
 }
 
-String ccInstance::GetCallStack(int maxLines) {
+String ccInstance::GetCallStack(int maxLines) const {
 	String buffer = String::FromFormat("in \"%s\", line %d\n", runningInst->instanceof->GetSectionName(pc), line_number);
 
 	int linesDone = 0;
@@ -1212,13 +1224,13 @@ String ccInstance::GetCallStack(int maxLines) {
 	return buffer;
 }
 
-void ccInstance::GetScriptPosition(ScriptPosition &script_pos) {
+void ccInstance::GetScriptPosition(ScriptPosition &script_pos) const {
 	script_pos.Section = runningInst->instanceof->GetSectionName(pc);
 	script_pos.Line    = line_number;
 }
 
 // get a pointer to a variable or function exported by the script
-RuntimeScriptValue ccInstance::GetSymbolAddress(const char *symname) {
+RuntimeScriptValue ccInstance::GetSymbolAddress(const char *symname) const {
 	int k;
 	char altName[200];
 	snprintf(altName, sizeof(altName), "%s$", symname);
@@ -1234,7 +1246,7 @@ RuntimeScriptValue ccInstance::GetSymbolAddress(const char *symname) {
 	return rval_null;
 }
 
-void ccInstance::DumpInstruction(const ScriptOperation &op) {
+void ccInstance::DumpInstruction(const ScriptOperation &op) const {
 	// line_num local var should be shared between all the instances
 	static int line_num = 0;
 
diff --git a/engines/ags/engine/script/cc_instance.h b/engines/ags/engine/script/cc_instance.h
index 423bef9f738..3895d68ed14 100644
--- a/engines/ags/engine/script/cc_instance.h
+++ b/engines/ags/engine/script/cc_instance.h
@@ -159,12 +159,12 @@ public:
 	int     CallScriptFunction(const char *funcname, int32_t num_params, const RuntimeScriptValue *params);
 
 	// Get the script's execution position and callstack as human-readable text
-	Shared::String GetCallStack(int maxLines);
+	Shared::String GetCallStack(int max_lines = INT_MAX) const;
 	// Get the script's execution position
-	void    GetScriptPosition(ScriptPosition &script_pos);
+	void    GetScriptPosition(ScriptPosition &script_pos) const;
 	// Get the address of an exported symbol (function or variable) in the script
-	RuntimeScriptValue GetSymbolAddress(const char *symname);
-	void    DumpInstruction(const ScriptOperation &op);
+	RuntimeScriptValue GetSymbolAddress(const char *symname) const;
+	void    DumpInstruction(const ScriptOperation &op) const;
 	// Tells whether this instance is in the process of executing the byte-code
 	bool    IsBeingRun() const;
 
diff --git a/engines/ags/engine/script/script.cpp b/engines/ags/engine/script/script.cpp
index 3a89de31303..19d190e4f8e 100644
--- a/engines/ags/engine/script/script.cpp
+++ b/engines/ags/engine/script/script.cpp
@@ -564,7 +564,7 @@ void quit_with_script_error(const char *functionName) {
 		quitprintf("!Error running function '%s':\n%s", functionName, error.ErrorString.GetCStr());
 	else
 		quitprintf("Error running function '%s':\n%s\n\n%s", functionName,
-			error.ErrorString.GetCStr(), get_cur_script(5).GetCStr());
+			error.ErrorString.GetCStr(), error.CallStack.GetCStr());
 }
 
 int get_nivalue(InteractionCommandList *nic, int idx, int parm) {
diff --git a/engines/ags/engine/script/script_engine.cpp b/engines/ags/engine/script/script_engine.cpp
deleted file mode 100644
index 92478a37643..00000000000
--- a/engines/ags/engine/script/script_engine.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-//=============================================================================
-//
-// Script Editor run-time engine component (c) 1998 Chris Jones
-// script chunk format:
-// 00h  1 dword  version - should be 2
-// 04h  1 dword  sizeof(scriptblock)
-// 08h  1 dword  number of ScriptBlocks
-// 0Ch  n STRUCTs ScriptBlocks
-//
-//=============================================================================
-
-#include "ags/lib/std/utility.h"
-#include "ags/engine/script/cc_instance.h"
-#include "ags/shared/script/cc_common.h"
-#include "ags/shared/util/file.h"
-#include "ags/shared/util/stream.h"
-#include "ags/globals.h"
-
-namespace AGS3 {
-
-namespace AGS {
-namespace Shared {
-class RoomStruct;
-} // namespace Shared
-} // namespace AGS
-
-using namespace AGS::Shared;
-
-std::pair<String, String> cc_error_at_line(const char *error_msg) {
-	ccInstance *sci = ccInstance::GetCurrentInstance();
-	if (!sci) {
-		return std::make_pair(String::FromFormat("Error (line %d): %s", _G(currentline), error_msg), String());
-	} else {
-		return std::make_pair(String::FromFormat("Error: %s\n", error_msg), ccInstance::GetCurrentInstance()->GetCallStack(5));
-	}
-}
-
-String cc_error_without_line(const char *error_msg) {
-	return String::FromFormat("Runtime error: %s", error_msg);
-}
-
-} // namespace AGS3
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index cee427f6d65..8c835dc464f 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -129,7 +129,7 @@ Globals::Globals() {
 	_animbuts = new std::vector<AnimatingGUIButton>();
 
 	// cc_instance.cpp globals
-	_InstThreads = new std::stack<ccInstance *>();
+	_InstThreads = new std::deque<ccInstance *>();
 	_GlobalReturnValue = new RuntimeScriptValue();
 
 	// cc_options.cpp globals
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index a5aa0b1ee7a..a3f5a7082f5 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -25,7 +25,7 @@
 #include "ags/shared/core/platform.h"
 #define AGS_PLATFORM_DEFINES_PSP_VARS (AGS_PLATFORM_OS_IOS || AGS_PLATFORM_OS_ANDROID)
 
-#include "ags/lib/std/stack.h"
+#include "ags/lib/std/queue.h"
 #include "ags/shared/ac/game_version.h"
 #include "ags/shared/util/stdio_compat.h"
 #include "ags/shared/util/string.h"
@@ -369,7 +369,7 @@ public:
 	// In AGS currently only one thread is running, others are waiting in the queue.
 	// An example situation is repeatedly_execute_always callback running while
 	// another instance is waiting at the blocking action or Wait().
-	std::stack<ccInstance *> *_InstThreads;
+	std::deque<ccInstance *> *_InstThreads;
 	// [IKM] 2012-10-21:
 	// NOTE: This is temporary solution (*sigh*, one of many) which allows certain
 	// exported functions return value as a RuntimeScriptValue object;
diff --git a/engines/ags/lib/std/queue.h b/engines/ags/lib/std/queue.h
index ead08567d02..1617c2a5b01 100644
--- a/engines/ags/lib/std/queue.h
+++ b/engines/ags/lib/std/queue.h
@@ -63,6 +63,80 @@ public:
 	}
 };
 
+template<class T>
+class deque {
+private:
+	vector<T> _intern;
+public:
+	deque() = default;
+	typedef typename vector<T>::iterator iterator;
+	typedef typename const vector<T>::const_iterator const_iterator;
+	typedef typename vector<T>::reverse_iterator reverse_iterator;
+	typedef typename const vector<T>::const_reverse_iterator const_reverse_iterator;
+
+	void clear() {
+		_intern.clear();
+	}
+	void insert(const T &item) {
+		_intern.push_back(item);
+	}
+	void push_back(const T &item) {
+		_intern.push_back(item);
+	}
+	void push_front(const T &item) {
+		_intern.push_front(item);
+	}
+	void pop_back() {
+		_intern.pop_back();
+	}
+	void pop_front() {
+		_intern.remove_at(0);
+	}
+	const T &front() const {
+		return _intern.front();
+	}
+	const T &back() const {
+		return _intern.back();
+	}
+
+	void resize(size_t newSize) {
+		_intern.resize(newSize);
+	}
+
+	size_t size() const {
+		return _intern.size();
+	}
+
+	T at(size_t idx) {
+		return _intern[idx];
+	}
+
+	const_iterator cbegin() {
+		return _intern.cbegin();
+	}
+	const_iterator cend() {
+		return _intern.cend();
+	}
+	reverse_iterator rbegin() {
+		return _intern.rbegin();
+	}
+	reverse_iterator rend() {
+		return _intern.rend();
+	}
+	const_reverse_iterator rbegin() const {
+		return _intern.rbegin();
+	}
+	const_reverse_iterator rend() const {
+		return _intern.rend();
+	}
+	const_reverse_iterator crbegin() const {
+		return _intern.crbegin();
+	}
+	const_reverse_iterator crend() const {
+		return _intern.crend();
+	}
+};
+
 } // namespace std
 } // namespace AGS3
 
diff --git a/engines/ags/module.mk b/engines/ags/module.mk
index 50ba7cc75c1..13c73481072 100644
--- a/engines/ags/module.mk
+++ b/engines/ags/module.mk
@@ -279,7 +279,6 @@ MODULE_OBJS = \
 	engine/script/runtime_script_value.o \
 	engine/script/script.o \
 	engine/script/script_api.o \
-	engine/script/script_engine.o \
 	engine/script/script_runtime.o \
 	engine/script/system_imports.o \
 	plugins/ags_plugin.o \
diff --git a/engines/ags/shared/script/cc_common.cpp b/engines/ags/shared/script/cc_common.cpp
index 054c8b20a32..3e7dfa97158 100644
--- a/engines/ags/shared/script/cc_common.cpp
+++ b/engines/ags/shared/script/cc_common.cpp
@@ -42,10 +42,8 @@ int ccGetOption(int optbit) {
 	return 0;
 }
 
-// Returns full script error message and callstack (if possible)
-extern std::pair<String, String> cc_error_at_line(const char *error_msg);
-// Returns script error message without location or callstack
-extern String cc_error_without_line(const char *error_msg);
+// Returns current running script callstack as a human-readable text
+extern String cc_get_callstack(int max_lines = INT_MAX);
 
 void cc_clear_error() {
 	_GP(ccError) = ScriptError();
@@ -71,15 +69,12 @@ void cc_error(const char *descr, ...) {
 	String displbuf = String::FromFormatV(descr, ap);
 	va_end(ap);
 
-	if (_G(currentline) > 0) {
-		// [IKM] Implementation is project-specific
-		std::pair<String, String> errinfo = cc_error_at_line(displbuf.GetCStr());
-		_GP(ccError).ErrorString = errinfo.first;
-		_GP(ccError).CallStack = errinfo.second;
-	} else {
-		_GP(ccError).ErrorString = cc_error_without_line(displbuf.GetCStr());
-		_GP(ccError).CallStack = "";
-	}
+	String callstack = cc_get_callstack();
+	if ((_G(currentline) > 0) && callstack.IsEmpty())
+		_GP(ccError).ErrorString = String::FromFormat("Error (line %d): %s", _G(currentline), displbuf.GetCStr());
+	else
+		_GP(ccError).ErrorString = String::FromFormat("Error: %s", displbuf.GetCStr());
+	_GP(ccError).CallStack = callstack;
 
 	_GP(ccError).HasError = 1;
 	_GP(ccError).Line = _G(currentline);


Commit: 540a92a6c532c5b9449c97b55f19ed22d5540bb8
    https://github.com/scummvm/scummvm/commit/540a92a6c532c5b9449c97b55f19ed22d5540bb8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:07-07:00

Commit Message:
AGS: Necessary fixes to build with latest changes to ccError

>From upstream 238b18e2ea0a34742d50c5d4298a06cc3077dcdd

Changed paths:
    engines/ags/engine/script/script.cpp
    engines/ags/shared/script/cc_common.cpp
    engines/ags/shared/script/cc_common.h


diff --git a/engines/ags/engine/script/script.cpp b/engines/ags/engine/script/script.cpp
index 19d190e4f8e..3a25cb36f26 100644
--- a/engines/ags/engine/script/script.cpp
+++ b/engines/ags/engine/script/script.cpp
@@ -882,4 +882,11 @@ bool get_script_position(ScriptPosition &script_pos) {
 	return false;
 }
 
+String cc_format_error(const String &message) {
+	if (_G(currentline) > 0)
+		return String::FromFormat("Error (line %d): %s", _G(currentline), message.GetCStr());
+	else
+		return String::FromFormat("Error (line unknown): %s", message.GetCStr());
+}
+
 } // namespace AGS3
diff --git a/engines/ags/shared/script/cc_common.cpp b/engines/ags/shared/script/cc_common.cpp
index 3e7dfa97158..67d165376f6 100644
--- a/engines/ags/shared/script/cc_common.cpp
+++ b/engines/ags/shared/script/cc_common.cpp
@@ -69,13 +69,10 @@ void cc_error(const char *descr, ...) {
 	String displbuf = String::FromFormatV(descr, ap);
 	va_end(ap);
 
-	String callstack = cc_get_callstack();
-	if ((_G(currentline) > 0) && callstack.IsEmpty())
-		_GP(ccError).ErrorString = String::FromFormat("Error (line %d): %s", _G(currentline), displbuf.GetCStr());
-	else
-		_GP(ccError).ErrorString = String::FromFormat("Error: %s", displbuf.GetCStr());
-	_GP(ccError).CallStack = callstack;
-
+	// TODO: because this global ccError is a global shared variable,
+	// we have to use project-dependent function to format the final message
+	_GP(ccError).ErrorString = cc_format_error(displbuf);
+	_GP(ccError).CallStack = cc_get_callstack();
 	_GP(ccError).HasError = 1;
 	_GP(ccError).Line = _G(currentline);
 }
diff --git a/engines/ags/shared/script/cc_common.h b/engines/ags/shared/script/cc_common.h
index 8c0678aab04..e2a2b6cc242 100644
--- a/engines/ags/shared/script/cc_common.h
+++ b/engines/ags/shared/script/cc_common.h
@@ -55,6 +55,8 @@ void cc_clear_error();
 bool cc_has_error();
 const ScriptError &cc_get_error();
 void cc_error(const char *, ...);
+// Project-dependent script error formatting
+AGS::Shared::String cc_format_error(const AGS::Shared::String &message);
 
 } // namespace AGS3
 


Commit: faa858a55b2d64cdf3b0f88be01cd843bfc123d9
    https://github.com/scummvm/scummvm/commit/faa858a55b2d64cdf3b0f88be01cd843bfc123d9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:07-07:00

Commit Message:
AGS: Fixed sending non-error messages to debugger

>From upstream 3b43353986ca91ecad9c5270e362c8716effaf37

Changed paths:
    engines/ags/engine/debugging/debug.cpp
    engines/ags/engine/script/script.h
    engines/ags/shared/script/cc_common.cpp


diff --git a/engines/ags/engine/debugging/debug.cpp b/engines/ags/engine/debugging/debug.cpp
index 3b0644d8c32..6c9bdba5bdd 100644
--- a/engines/ags/engine/debugging/debug.cpp
+++ b/engines/ags/engine/debugging/debug.cpp
@@ -22,13 +22,6 @@
 #include "ags/lib/std/memory.h"
 #include "ags/lib/std/limits.h"
 #include "ags/shared/core/platform.h"
-#if AGS_PLATFORM_OS_WINDOWS
-#define NOMINMAX
-#define BITMAP WINDOWS_BITMAP
-//include <windows.h>
-#undef BITMAP
-#endif
-//include <SDL.h>
 #include "ags/lib/std/initializer_list.h"
 #include "ags/shared/ac/common.h"
 #include "ags/shared/ac/game_setup_struct.h"
@@ -321,23 +314,24 @@ struct Breakpoint {
 };
 
 bool send_message_to_editor(const char *msg, const char *errorMsg) {
-	String callStack = cc_get_error().CallStack;
+	// Get either saved callstack from a script error, or current execution point
+	String callStack = (errorMsg && cc_has_error()) ?
+		cc_get_error().CallStack : cc_get_callstack();
 	if (callStack.IsEmpty())
 		return false;
 
-	char messageToSend[STD_BUFFER_SIZE];
-	sprintf(messageToSend, "<?xml version=\"1.0\" encoding=\"Windows-1252\"?><Debugger Command=\"%s\">", msg);
+	String message;
+	message.AppendFmt("<?xml version=\"1.0\" encoding=\"Windows-1252\"?><Debugger Command=\"%s\">", msg);
 #if AGS_PLATFORM_OS_WINDOWS
-	sprintf(&messageToSend[strlen(messageToSend)], "  <EngineWindow>%d</EngineWindow> ", (int)sys_win_get_window());
+	message.AppendFmt("  <EngineWindow>%d</EngineWindow> ", (int)sys_win_get_window());
 #endif
-	sprintf(&messageToSend[strlen(messageToSend)], "  <ScriptState><![CDATA[%s]]></ScriptState> ", callStack.GetCStr());
+	message.AppendFmt("  <ScriptState><![CDATA[%s]]></ScriptState> ", callStack.GetCStr());
 	if (errorMsg != nullptr) {
-		sprintf(&messageToSend[strlen(messageToSend)], "  <ErrorMessage><![CDATA[%s]]></ErrorMessage> ", errorMsg);
+		message.AppendFmt("  <ErrorMessage><![CDATA[%s]]></ErrorMessage> ", errorMsg);
 	}
-	strcat(messageToSend, "</Debugger>");
-
-	_G(editor_debugger)->SendMessageToEditor(messageToSend);
+	message.Append("</Debugger>");
 
+	_G(editor_debugger)->SendMessageToEditor(message.GetCStr());
 	return true;
 }
 
diff --git a/engines/ags/engine/script/script.h b/engines/ags/engine/script/script.h
index 3b221bc2f7f..622741aa4d7 100644
--- a/engines/ags/engine/script/script.h
+++ b/engines/ags/engine/script/script.h
@@ -88,6 +88,7 @@ void    can_run_delayed_command();
 
 // Gets current running script position
 bool    get_script_position(ScriptPosition &script_pos);
+AGS::Shared::String cc_get_callstack(int max_lines = INT_MAX);
 
 } // namespace AGS3
 
diff --git a/engines/ags/shared/script/cc_common.cpp b/engines/ags/shared/script/cc_common.cpp
index 67d165376f6..76bc36a3415 100644
--- a/engines/ags/shared/script/cc_common.cpp
+++ b/engines/ags/shared/script/cc_common.cpp
@@ -42,9 +42,6 @@ int ccGetOption(int optbit) {
 	return 0;
 }
 
-// Returns current running script callstack as a human-readable text
-extern String cc_get_callstack(int max_lines = INT_MAX);
-
 void cc_clear_error() {
 	_GP(ccError) = ScriptError();
 }


Commit: 6482e3880343594e8e388e7f5cfec22a330105af
    https://github.com/scummvm/scummvm/commit/6482e3880343594e8e388e7f5cfec22a330105af
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:08-07:00

Commit Message:
AGS: CharacterExtras and MoveLists are stored in std::vector

>From upstream 0c81cfefb788c0dcfdb2cad914f939f28110edca

Changed paths:
    engines/ags/engine/ac/character.cpp
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/global_character.cpp
    engines/ags/engine/ac/global_debug.cpp
    engines/ags/engine/ac/global_room.cpp
    engines/ags/engine/ac/gui_inv.cpp
    engines/ags/engine/ac/inv_window.cpp
    engines/ags/engine/ac/object.cpp
    engines/ags/engine/ac/overlay.cpp
    engines/ags/engine/ac/room.cpp
    engines/ags/engine/ac/route_finder_impl.cpp
    engines/ags/engine/ac/route_finder_impl_legacy.cpp
    engines/ags/engine/game/game_init.cpp
    engines/ags/engine/game/savegame_components.cpp
    engines/ags/engine/game/savegame_v321.cpp
    engines/ags/engine/main/engine.cpp
    engines/ags/engine/main/game_run.cpp
    engines/ags/engine/main/update.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h
    engines/ags/plugins/ags_plugin.cpp


diff --git a/engines/ags/engine/ac/character.cpp b/engines/ags/engine/ac/character.cpp
index cf854d17eaf..7a6f6937dd1 100644
--- a/engines/ags/engine/ac/character.cpp
+++ b/engines/ags/engine/ac/character.cpp
@@ -96,8 +96,8 @@ void Character_AddInventory(CharacterInfo *chaa, ScriptInvItem *invi, int addInd
 
 	if (_GP(game).options[OPT_DUPLICATEINV] == 0) {
 		// Ensure it is only in the list once
-		for (ee = 0; ee < _G(charextra)[charid].invorder_count; ee++) {
-			if (_G(charextra)[charid].invorder[ee] == inum) {
+		for (ee = 0; ee < _GP(charextra)[charid].invorder_count; ee++) {
+			if (_GP(charextra)[charid].invorder[ee] == inum) {
 				// They already have the item, so don't add it to the list
 				if (chaa == _G(playerchar))
 					run_on_event(GE_ADD_INV, RuntimeScriptValue().SetInt32(inum));
@@ -105,22 +105,22 @@ void Character_AddInventory(CharacterInfo *chaa, ScriptInvItem *invi, int addInd
 			}
 		}
 	}
-	if (_G(charextra)[charid].invorder_count >= MAX_INVORDER)
+	if (_GP(charextra)[charid].invorder_count >= MAX_INVORDER)
 		quit("!Too many inventory items added, max 500 display at one time");
 
 	if ((addIndex == SCR_NO_VALUE) ||
-	        (addIndex >= _G(charextra)[charid].invorder_count) ||
+	        (addIndex >= _GP(charextra)[charid].invorder_count) ||
 	        (addIndex < 0)) {
 		// add new item at end of list
-		_G(charextra)[charid].invorder[_G(charextra)[charid].invorder_count] = inum;
+		_GP(charextra)[charid].invorder[_GP(charextra)[charid].invorder_count] = inum;
 	} else {
 		// insert new item at index
-		for (ee = _G(charextra)[charid].invorder_count - 1; ee >= addIndex; ee--)
-			_G(charextra)[charid].invorder[ee + 1] = _G(charextra)[charid].invorder[ee];
+		for (ee = _GP(charextra)[charid].invorder_count - 1; ee >= addIndex; ee--)
+			_GP(charextra)[charid].invorder[ee + 1] = _GP(charextra)[charid].invorder[ee];
 
-		_G(charextra)[charid].invorder[addIndex] = inum;
+		_GP(charextra)[charid].invorder[addIndex] = inum;
 	}
-	_G(charextra)[charid].invorder_count++;
+	_GP(charextra)[charid].invorder_count++;
 	GUI::MarkInventoryForUpdate(charid, charid == _GP(game).playercharacter);
 	if (chaa == _G(playerchar))
 		run_on_event(GE_ADD_INV, RuntimeScriptValue().SetInt32(inum));
@@ -137,7 +137,7 @@ void Character_AddWaypoint(CharacterInfo *chaa, int x, int y) {
 		return;
 	}
 
-	MoveList *cmls = &_G(mls)[chaa->walking % TURNING_AROUND];
+	MoveList *cmls = &_GP(mls)[chaa->walking % TURNING_AROUND];
 	if (cmls->numstage >= MAXNEEDSTAGES) {
 		debug_script_warn("Character_AddWaypoint: move is too complex, cannot add any further paths");
 		return;
@@ -263,7 +263,7 @@ void Character_ChangeView(CharacterInfo *chap, int vii) {
 	chap->frame = 0;
 	chap->wait = 0;
 	chap->walkwait = 0;
-	_G(charextra)[chap->index_id].animwait = 0;
+	_GP(charextra)[chap->index_id].animwait = 0;
 	FindReasonableLoopForCharacter(chap);
 }
 
@@ -651,11 +651,11 @@ void Character_LoseInventory(CharacterInfo *chap, ScriptInvItem *invi) {
 
 	if ((chap->inv[inum] == 0) || (_GP(game).options[OPT_DUPLICATEINV] > 0)) {
 		int xx, tt;
-		for (xx = 0; xx < _G(charextra)[charid].invorder_count; xx++) {
-			if (_G(charextra)[charid].invorder[xx] == inum) {
-				_G(charextra)[charid].invorder_count--;
-				for (tt = xx; tt < _G(charextra)[charid].invorder_count; tt++)
-					_G(charextra)[charid].invorder[tt] = _G(charextra)[charid].invorder[tt + 1];
+		for (xx = 0; xx < _GP(charextra)[charid].invorder_count; xx++) {
+			if (_GP(charextra)[charid].invorder[xx] == inum) {
+				_GP(charextra)[charid].invorder_count--;
+				for (tt = xx; tt < _GP(charextra)[charid].invorder_count; tt++)
+					_GP(charextra)[charid].invorder[tt] = _GP(charextra)[charid].invorder[tt + 1];
 				break;
 			}
 		}
@@ -786,7 +786,7 @@ void Character_SetIdleView(CharacterInfo *chaa, int iview, int itime) {
 	}
 	// if they switch to a swimming animation, kick it off immediately
 	if (itime == 0)
-		_G(charextra)[chaa->index_id].process_idle_this_time = 1;
+		_GP(charextra)[chaa->index_id].process_idle_this_time = 1;
 
 }
 
@@ -795,35 +795,35 @@ bool Character_GetHasExplicitLight(CharacterInfo *ch) {
 }
 
 int Character_GetLightLevel(CharacterInfo *ch) {
-	return ch->has_explicit_light() ? _G(charextra)[ch->index_id].tint_light : 0;
+	return ch->has_explicit_light() ? _GP(charextra)[ch->index_id].tint_light : 0;
 }
 
 void Character_SetLightLevel(CharacterInfo *chaa, int light_level) {
 	light_level = Math::Clamp(light_level, -100, 100);
 
-	_G(charextra)[chaa->index_id].tint_light = light_level;
+	_GP(charextra)[chaa->index_id].tint_light = light_level;
 	chaa->flags &= ~CHF_HASTINT;
 	chaa->flags |= CHF_HASLIGHT;
 }
 
 int Character_GetTintRed(CharacterInfo *ch) {
-	return ch->has_explicit_tint() ? _G(charextra)[ch->index_id].tint_r : 0;
+	return ch->has_explicit_tint() ? _GP(charextra)[ch->index_id].tint_r : 0;
 }
 
 int Character_GetTintGreen(CharacterInfo *ch) {
-	return ch->has_explicit_tint() ? _G(charextra)[ch->index_id].tint_g : 0;
+	return ch->has_explicit_tint() ? _GP(charextra)[ch->index_id].tint_g : 0;
 }
 
 int Character_GetTintBlue(CharacterInfo *ch) {
-	return ch->has_explicit_tint() ? _G(charextra)[ch->index_id].tint_b : 0;
+	return ch->has_explicit_tint() ? _GP(charextra)[ch->index_id].tint_b : 0;
 }
 
 int Character_GetTintSaturation(CharacterInfo *ch) {
-	return ch->has_explicit_tint() ? _G(charextra)[ch->index_id].tint_level : 0;
+	return ch->has_explicit_tint() ? _GP(charextra)[ch->index_id].tint_level : 0;
 }
 
 int Character_GetTintLuminance(CharacterInfo *ch) {
-	return ch->has_explicit_tint() ? ((_G(charextra)[ch->index_id].tint_light * 10) / 25) : 0;
+	return ch->has_explicit_tint() ? ((_GP(charextra)[ch->index_id].tint_light * 10) / 25) : 0;
 }
 
 void Character_SetOption(CharacterInfo *chaa, int flag, int yesorno) {
@@ -867,21 +867,21 @@ void Character_StopMoving(CharacterInfo *charp) {
 	if (chaa == _GP(play).skip_until_char_stops)
 		EndSkippingUntilCharStops();
 
-	if (_G(charextra)[chaa].xwas != INVALID_X) {
-		charp->x = _G(charextra)[chaa].xwas;
-		charp->y = _G(charextra)[chaa].ywas;
-		_G(charextra)[chaa].xwas = INVALID_X;
+	if (_GP(charextra)[chaa].xwas != INVALID_X) {
+		charp->x = _GP(charextra)[chaa].xwas;
+		charp->y = _GP(charextra)[chaa].ywas;
+		_GP(charextra)[chaa].xwas = INVALID_X;
 	}
 	if ((charp->walking > 0) && (charp->walking < TURNING_AROUND)) {
 		// if it's not a MoveCharDirect, make sure they end up on a walkable area
-		if ((_G(mls)[charp->walking].direct == 0) && (charp->room == _G(displayed_room)))
+		if ((_GP(mls)[charp->walking].direct == 0) && (charp->room == _G(displayed_room)))
 			Character_PlaceOnWalkableArea(charp);
 
 		debug_script_log("%s: stop moving", charp->scrname);
 
 		charp->idleleft = charp->idletime;
 		// restart the idle animation straight away
-		_G(charextra)[chaa].process_idle_this_time = 1;
+		_GP(charextra)[chaa].process_idle_this_time = 1;
 	}
 	if (charp->walking) {
 		// If the character is currently moving, stop them and reset their frame
@@ -900,11 +900,11 @@ void Character_Tint(CharacterInfo *chaa, int red, int green, int blue, int opaci
 
 	debug_script_log("Set %s tint RGB(%d,%d,%d) %d%%", chaa->scrname, red, green, blue, opacity);
 
-	_G(charextra)[chaa->index_id].tint_r = red;
-	_G(charextra)[chaa->index_id].tint_g = green;
-	_G(charextra)[chaa->index_id].tint_b = blue;
-	_G(charextra)[chaa->index_id].tint_level = opacity;
-	_G(charextra)[chaa->index_id].tint_light = (luminance * 25) / 10;
+	_GP(charextra)[chaa->index_id].tint_r = red;
+	_GP(charextra)[chaa->index_id].tint_g = green;
+	_GP(charextra)[chaa->index_id].tint_b = blue;
+	_GP(charextra)[chaa->index_id].tint_level = opacity;
+	_GP(charextra)[chaa->index_id].tint_light = (luminance * 25) / 10;
 	chaa->flags &= ~CHF_HASLIGHT;
 	chaa->flags |= CHF_HASTINT;
 }
@@ -938,7 +938,7 @@ void Character_UnlockViewEx(CharacterInfo *chaa, int stopMoving) {
 	chaa->pic_xoffs = 0;
 	chaa->pic_yoffs = 0;
 	// restart the idle animation straight away
-	_G(charextra)[chaa->index_id].process_idle_this_time = 1;
+	_GP(charextra)[chaa->index_id].process_idle_this_time = 1;
 
 }
 
@@ -1237,7 +1237,7 @@ void Character_SetIgnoreScaling(CharacterInfo *chaa, int yesorno) {
 	if (yesorno) {
 		// when setting IgnoreScaling to 1, should reset zoom level
 		// like it used to in pre-2.71
-		_G(charextra)[chaa->index_id].zoom = 100;
+		_GP(charextra)[chaa->index_id].zoom = 100;
 	}
 	Character_SetManualScaling(chaa, yesorno);
 }
@@ -1300,7 +1300,7 @@ int Character_GetMoving(CharacterInfo *chaa) {
 
 int Character_GetDestinationX(CharacterInfo *chaa) {
 	if (chaa->walking) {
-		MoveList *cmls = &_G(mls)[chaa->walking % TURNING_AROUND];
+		MoveList *cmls = &_GP(mls)[chaa->walking % TURNING_AROUND];
 		return cmls->pos[cmls->numstage - 1] >> 16;
 	} else
 		return chaa->x;
@@ -1308,7 +1308,7 @@ int Character_GetDestinationX(CharacterInfo *chaa) {
 
 int Character_GetDestinationY(CharacterInfo *chaa) {
 	if (chaa->walking) {
-		MoveList *cmls = &_G(mls)[chaa->walking % TURNING_AROUND];
+		MoveList *cmls = &_GP(mls)[chaa->walking % TURNING_AROUND];
 		return cmls->pos[cmls->numstage - 1] & 0xFFFF;
 	} else
 		return chaa->y;
@@ -1372,7 +1372,7 @@ void Character_SetScaleVolume(CharacterInfo *chaa, int yesorno) {
 }
 
 int Character_GetScaling(CharacterInfo *chaa) {
-	return _G(charextra)[chaa->index_id].zoom;
+	return _GP(charextra)[chaa->index_id].zoom;
 }
 
 void Character_SetScaling(CharacterInfo *chaa, int zoomlevel) {
@@ -1386,7 +1386,7 @@ void Character_SetScaling(CharacterInfo *chaa, int zoomlevel) {
 		debug_script_warn("Character.Scaling: scaling level must be between 1 and %d%%, asked for: %d",
 			(int)(INT16_MAX), zoomlevel);
 
-	_G(charextra)[chaa->index_id].zoom = zoom_fixed;
+	_GP(charextra)[chaa->index_id].zoom = zoom_fixed;
 }
 
 int Character_GetSolid(CharacterInfo *chaa) {
@@ -1600,7 +1600,7 @@ void walk_character(int chac, int tox, int toy, int ignwal, bool autoWalkAnims)
 	// if they are currently walking, save the current Wait
 	if (chin->walking) {
 		waitWas = chin->walkwait;
-		animWaitWas = _G(charextra)[chac].animwait;
+		animWaitWas = _GP(charextra)[chac].animwait;
 	}
 
 	StopMoving(chac);
@@ -1625,8 +1625,8 @@ void walk_character(int chac, int tox, int toy, int ignwal, bool autoWalkAnims)
 	set_color_depth(_GP(game).GetColorDepth());
 	if (mslot > 0) {
 		chin->walking = mslot;
-		_G(mls)[mslot].direct = ignwal;
-		convert_move_path_to_room_resolution(&_G(mls)[mslot]);
+		_GP(mls)[mslot].direct = ignwal;
+		convert_move_path_to_room_resolution(&_GP(mls)[mslot]);
 
 		// cancel any pending waits on current animations
 		// or if they were already moving, keep the current wait -
@@ -1634,10 +1634,10 @@ void walk_character(int chac, int tox, int toy, int ignwal, bool autoWalkAnims)
 		// are already moving
 		if (autoWalkAnims) {
 			chin->walkwait = waitWas;
-			_G(charextra)[chac].animwait = animWaitWas;
+			_GP(charextra)[chac].animwait = animWaitWas;
 
-			if (_G(mls)[mslot].pos[0] != _G(mls)[mslot].pos[1]) {
-				fix_player_sprite(&_G(mls)[mslot], chin);
+			if (_GP(mls)[mslot].pos[0] != _GP(mls)[mslot].pos[1]) {
+				fix_player_sprite(&_GP(mls)[mslot], chin);
 			}
 		} else
 			chin->flags |= CHF_MOVENOTWALK;
@@ -1781,7 +1781,7 @@ int doNextCharMoveStep(CharacterInfo *chi, int &char_index, CharacterExtras *che
 
 	if (do_movelist_move(&chi->walking, &chi->x, &chi->y) == 2) {
 		if ((chi->flags & CHF_MOVENOTWALK) == 0)
-			fix_player_sprite(&_G(mls)[chi->walking], chi);
+			fix_player_sprite(&_GP(mls)[chi->walking], chi);
 	}
 
 	ntf = has_hit_another_character(char_index);
@@ -1800,8 +1800,8 @@ int doNextCharMoveStep(CharacterInfo *chi, int &char_index, CharacterExtras *che
 		}
 
 		if ((chi->walking < 1) || (chi->walking >= TURNING_AROUND)) ;
-		else if (_G(mls)[chi->walking].onpart > 0) {
-			_G(mls)[chi->walking].onpart --;
+		else if (_GP(mls)[chi->walking].onpart > 0) {
+			_GP(mls)[chi->walking].onpart --;
 			chi->x = xwas;
 			chi->y = ywas;
 		}
@@ -1813,7 +1813,7 @@ int doNextCharMoveStep(CharacterInfo *chi, int &char_index, CharacterExtras *che
 
 bool is_char_walking_ndirect(CharacterInfo *chi) {
 	return ((chi->walking > 0) && (chi->walking < TURNING_AROUND)) &&
-		(_G(mls)[chi->walking].direct == 0);
+		(_GP(mls)[chi->walking].direct == 0);
 }
 
 int find_nearest_walkable_area_within(int *xx, int *yy, int range, int step) {
@@ -2058,7 +2058,7 @@ void CheckViewFrameForCharacter(CharacterInfo *chi) {
 
 	if (chi->flags & CHF_SCALEVOLUME) {
 		// adjust the sound volume using the character's zoom level
-		int zoom_level = _G(charextra)[chi->index_id].zoom;
+		int zoom_level = _GP(charextra)[chi->index_id].zoom;
 		if (zoom_level == 0)
 			zoom_level = 100;
 
@@ -2118,8 +2118,8 @@ int is_pos_on_character(int xx, int yy) {
 		}
 
 		sppic = _GP(views)[chin->view].loops[chin->loop].frames[chin->frame].pic;
-		int usewid = _G(charextra)[cc].width;
-		int usehit = _G(charextra)[cc].height;
+		int usewid = _GP(charextra)[cc].width;
+		int usehit = _GP(charextra)[cc].height;
 		if (usewid == 0) usewid = _GP(game).SpriteInfos[sppic].Width;
 		if (usehit == 0) usehit = _GP(game).SpriteInfos[sppic].Height;
 		int xxx = chin->x - game_to_data_coord(usewid) / 2;
@@ -2411,7 +2411,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 
 		if (tdyp < 0) {
 			int sppic = _GP(views)[speakingChar->view].loops[speakingChar->loop].frames[0].pic;
-			int height = (_G(charextra)[aschar].height < 1) ? _GP(game).SpriteInfos[sppic].Height : _G(charextra)[aschar].height;
+			int height = (_GP(charextra)[aschar].height < 1) ? _GP(game).SpriteInfos[sppic].Height : _GP(charextra)[aschar].height;
 			tdyp = view->RoomToScreen(0, data_to_game_coord(_GP(game).chars[aschar].get_effective_y()) - height).first.Y
 			       - get_fixed_pixel_size(5);
 			if (isThought) // if it's a thought, lift it a bit further up
@@ -2702,7 +2702,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 		speakingChar->wait = 0;
 		speakingChar->idleleft = speakingChar->idletime;
 		// restart the idle animation straight away
-		_G(charextra)[aschar].process_idle_this_time = 1;
+		_GP(charextra)[aschar].process_idle_this_time = 1;
 	}
 	_G(char_speaking) = -1;
 	_G(char_thinking) = -1;
@@ -2783,7 +2783,7 @@ int update_lip_sync(int talkview, int talkloop, int *talkframeptr) {
 
 Rect GetCharacterRoomBBox(int charid, bool use_frame_0) {
 	int width, height;
-	const CharacterExtras &chex = _G(charextra)[charid];
+	const CharacterExtras &chex = _GP(charextra)[charid];
 	const CharacterInfo &chin = _GP(game).chars[charid];
 	int frame = use_frame_0 ? 0 : chin.frame;
 	int pic = _GP(views)[chin.view].loops[chin.loop].frames[frame].pic;
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 4e7910f7cc9..f1fad499b4c 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -1428,29 +1428,29 @@ void prepare_characters_for_drawing() {
 
 		// calculates the zoom level
 		if (chin->flags & CHF_MANUALSCALING)  // character ignores scaling
-			zoom_level = _G(charextra)[aa].zoom;
+			zoom_level = _GP(charextra)[aa].zoom;
 		else if ((onarea <= 0) && (_GP(thisroom).WalkAreas[0].ScalingFar == 0)) {
-			zoom_level = _G(charextra)[aa].zoom;
+			zoom_level = _GP(charextra)[aa].zoom;
 			// NOTE: room objects don't have this fix
 			if (zoom_level == 0)
 				zoom_level = 100;
 		} else
 			zoom_level = get_area_scaling(onarea, chin->x, chin->y);
 
-		_G(charextra)[aa].zoom = zoom_level;
+		_GP(charextra)[aa].zoom = zoom_level;
 
 		tint_red = tint_green = tint_blue = tint_amount = tint_light = light_level = 0;
 
 		if (chin->flags & CHF_HASTINT) {
 			// object specific tint, use it
-			tint_red = _G(charextra)[aa].tint_r;
-			tint_green = _G(charextra)[aa].tint_g;
-			tint_blue = _G(charextra)[aa].tint_b;
-			tint_amount = _G(charextra)[aa].tint_level;
-			tint_light = _G(charextra)[aa].tint_light;
+			tint_red = _GP(charextra)[aa].tint_r;
+			tint_green = _GP(charextra)[aa].tint_g;
+			tint_blue = _GP(charextra)[aa].tint_b;
+			tint_amount = _GP(charextra)[aa].tint_level;
+			tint_light = _GP(charextra)[aa].tint_light;
 			light_level = 0;
 		} else if (chin->flags & CHF_HASLIGHT) {
-			light_level = _G(charextra)[aa].tint_light;
+			light_level = _GP(charextra)[aa].tint_light;
 		} else {
 			get_local_tint(chin->x, chin->y, chin->flags & CHF_NOLIGHTING,
 			               &tint_amount, &tint_red, &tint_green, &tint_blue,
@@ -1510,13 +1510,13 @@ void prepare_characters_for_drawing() {
 			// it needs to be stretched, so calculate the new dimensions
 
 			scale_sprite_size(sppic, zoom_level, &newwidth, &newheight);
-			_G(charextra)[aa].width = newwidth;
-			_G(charextra)[aa].height = newheight;
+			_GP(charextra)[aa].width = newwidth;
+			_GP(charextra)[aa].height = newheight;
 		} else {
 			// draw at original size, so just use the sprite width and height
 			// TODO: store width and height always, that's much simplier to use for reference!
-			_G(charextra)[aa].width = 0;
-			_G(charextra)[aa].height = 0;
+			_GP(charextra)[aa].width = 0;
+			_GP(charextra)[aa].height = 0;
 			newwidth = src_sprwidth;
 			newheight = src_sprheight;
 		}
@@ -2280,7 +2280,7 @@ void update_room_debug() {
 			int mlsnum = _GP(game).chars[_G(debugMoveListChar)].walking;
 			if (_GP(game).chars[_G(debugMoveListChar)].walking >= TURNING_AROUND)
 				mlsnum %= TURNING_AROUND;
-			const MoveList &cmls = _G(mls)[mlsnum];
+			const MoveList &cmls = _GP(mls)[mlsnum];
 			for (int i = 0; i < cmls.numstage - 1; i++) {
 				short srcx = short((cmls.pos[i] >> 16) & 0x00ffff);
 				short srcy = short(cmls.pos[i] & 0x00ffff);
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 11b27cf73f3..3c1726cd9f6 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -45,6 +45,7 @@
 #include "ags/shared/ac/keycode.h"
 #include "ags/engine/ac/lip_sync.h"
 #include "ags/engine/ac/mouse.h"
+#include "ags/engine/ac/move_list.h"
 #include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/overlay.h"
 #include "ags/engine/ac/path_helper.h"
@@ -363,10 +364,8 @@ void unload_game_file() {
 	_GP(play).FreeViewportsAndCameras();
 
 	_GP(characterScriptObjNames).clear();
-	free(_G(charextra));
-	_G(charextra) = nullptr;
-	free(_G(mls));
-	_G(mls) = nullptr;
+	_GP(charextra).clear();
+	_GP(mls).clear();
 
 	dispose_game_drawdata();
 
diff --git a/engines/ags/engine/ac/global_character.cpp b/engines/ags/engine/ac/global_character.cpp
index 0b415ab50c9..971db404b7b 100644
--- a/engines/ags/engine/ac/global_character.cpp
+++ b/engines/ags/engine/ac/global_character.cpp
@@ -113,7 +113,7 @@ void SetCharacterIdle(int who, int iview, int itime) {
 int GetCharacterWidth(int ww) {
 	CharacterInfo *char1 = &_GP(game).chars[ww];
 
-	if (_G(charextra)[ww].width < 1) {
+	if (_GP(charextra)[ww].width < 1) {
 		if ((char1->view < 0) ||
 		        (char1->loop >= _GP(views)[char1->view].numLoops) ||
 		        (char1->frame >= _GP(views)[char1->view].loops[char1->loop].numFrames)) {
@@ -123,13 +123,13 @@ int GetCharacterWidth(int ww) {
 
 		return _GP(game).SpriteInfos[_GP(views)[char1->view].loops[char1->loop].frames[char1->frame].pic].Width;
 	} else
-		return _G(charextra)[ww].width;
+		return _GP(charextra)[ww].width;
 }
 
 int GetCharacterHeight(int charid) {
 	CharacterInfo *char1 = &_GP(game).chars[charid];
 
-	if (_G(charextra)[charid].height < 1) {
+	if (_GP(charextra)[charid].height < 1) {
 		if ((char1->view < 0) ||
 		        (char1->loop >= _GP(views)[char1->view].numLoops) ||
 		        (char1->frame >= _GP(views)[char1->view].loops[char1->loop].numFrames)) {
@@ -139,7 +139,7 @@ int GetCharacterHeight(int charid) {
 
 		return _GP(game).SpriteInfos[_GP(views)[char1->view].loops[char1->loop].frames[char1->frame].pic].Height;
 	} else
-		return _G(charextra)[charid].height;
+		return _GP(charextra)[charid].height;
 }
 
 
@@ -465,7 +465,7 @@ void SetActiveInventory(int iit) {
 
 void update_invorder() {
 	for (int cc = 0; cc < _GP(game).numcharacters; cc++) {
-		_G(charextra)[cc].invorder_count = 0;
+		_GP(charextra)[cc].invorder_count = 0;
 		int ff, howmany;
 		// Iterate through all inv items, adding them once (or multiple
 		// times if requested) to the list.
@@ -475,16 +475,16 @@ void update_invorder() {
 				howmany = 1;
 
 			for (int ts = 0; ts < howmany; ts++) {
-				if (_G(charextra)[cc].invorder_count >= MAX_INVORDER)
+				if (_GP(charextra)[cc].invorder_count >= MAX_INVORDER)
 					quit("!Too many inventory items to display: 500 max");
 
-				_G(charextra)[cc].invorder[_G(charextra)[cc].invorder_count] = ff;
-				_G(charextra)[cc].invorder_count++;
+				_GP(charextra)[cc].invorder[_GP(charextra)[cc].invorder_count] = ff;
+				_GP(charextra)[cc].invorder_count++;
 			}
 		}
 	}
 	// backwards compatibility
-	_GP(play).obsolete_inv_numorder = _G(charextra)[_GP(game).playercharacter].invorder_count;
+	_GP(play).obsolete_inv_numorder = _GP(charextra)[_GP(game).playercharacter].invorder_count;
 	GUI::MarkInventoryForUpdate(_GP(game).playercharacter, true);
 }
 
@@ -494,7 +494,7 @@ void add_inventory(int inum) {
 
 	Character_AddInventory(_G(playerchar), &_G(scrInv)[inum], SCR_NO_VALUE);
 
-	_GP(play).obsolete_inv_numorder = _G(charextra)[_GP(game).playercharacter].invorder_count;
+	_GP(play).obsolete_inv_numorder = _GP(charextra)[_GP(game).playercharacter].invorder_count;
 }
 
 void lose_inventory(int inum) {
@@ -503,7 +503,7 @@ void lose_inventory(int inum) {
 
 	Character_LoseInventory(_G(playerchar), &_G(scrInv)[inum]);
 
-	_GP(play).obsolete_inv_numorder = _G(charextra)[_GP(game).playercharacter].invorder_count;
+	_GP(play).obsolete_inv_numorder = _GP(charextra)[_GP(game).playercharacter].invorder_count;
 }
 
 void AddInventoryToCharacter(int charid, int inum) {
diff --git a/engines/ags/engine/ac/global_debug.cpp b/engines/ags/engine/ac/global_debug.cpp
index 20f75b3a816..7874a530fb6 100644
--- a/engines/ags/engine/ac/global_debug.cpp
+++ b/engines/ags/engine/ac/global_debug.cpp
@@ -140,7 +140,7 @@ void script_debug(int cmdd, int dataa) {
 		int mlsnum = _GP(game).chars[dataa].walking;
 		if (_GP(game).chars[dataa].walking >= TURNING_AROUND)
 			mlsnum %= TURNING_AROUND;
-		MoveList *cmls = &_G(mls)[mlsnum];
+		MoveList *cmls = &_GP(mls)[mlsnum];
 		for (int i = 0; i < cmls->numstage - 1; i++) {
 			short srcx = short((cmls->pos[i] >> 16) & 0x00ffff);
 			short srcy = short(cmls->pos[i] & 0x00ffff);
diff --git a/engines/ags/engine/ac/global_room.cpp b/engines/ags/engine/ac/global_room.cpp
index 121962c9733..ae78481ef05 100644
--- a/engines/ags/engine/ac/global_room.cpp
+++ b/engines/ags/engine/ac/global_room.cpp
@@ -124,7 +124,7 @@ void NewRoom(int nrnum) {
 		if (is_char_walking_ndirect(_G(playerchar))) {
 			// nasty hack - make sure it doesn't move the character
 			// to a walkable area
-			_G(mls)[_G(playerchar)->walking].direct = 1;
+			_GP(mls)[_G(playerchar)->walking].direct = 1;
 			StopMoving(_GP(game).playercharacter);
 		}
 	} else if (_G(in_graph_script))
diff --git a/engines/ags/engine/ac/gui_inv.cpp b/engines/ags/engine/ac/gui_inv.cpp
index 813f581adee..f424cf636d8 100644
--- a/engines/ags/engine/ac/gui_inv.cpp
+++ b/engines/ags/engine/ac/gui_inv.cpp
@@ -53,7 +53,7 @@ void GUIInvWindow::Draw(Bitmap *ds, int x, int y) {
 	// backwards compatibility
 	_GP(play).inv_numinline = ColCount;
 	_GP(play).inv_numdisp = RowCount * ColCount;
-	_GP(play).obsolete_inv_numorder = _G(charextra)[_GP(game).playercharacter].invorder_count;
+	_GP(play).obsolete_inv_numorder = _GP(charextra)[_GP(game).playercharacter].invorder_count;
 	// if the user changes top_inv_item, switch into backwards
 	// compatibiltiy mode
 	if (_GP(play).inv_top)
@@ -66,12 +66,12 @@ void GUIInvWindow::Draw(Bitmap *ds, int x, int y) {
 	int at_x = x;
 	int at_y = y;
 	int lastItem = TopItem + (ColCount * RowCount);
-	if (lastItem > _G(charextra)[GetCharacterId()].invorder_count)
-		lastItem = _G(charextra)[GetCharacterId()].invorder_count;
+	if (lastItem > _GP(charextra)[GetCharacterId()].invorder_count)
+		lastItem = _GP(charextra)[GetCharacterId()].invorder_count;
 
 	for (int item = TopItem; item < lastItem; ++item) {
 		// draw inv graphic
-		draw_gui_sprite(ds, _GP(game).invinfo[_G(charextra)[GetCharacterId()].invorder[item]].pic, at_x, at_y, true);
+		draw_gui_sprite(ds, _GP(game).invinfo[_GP(charextra)[GetCharacterId()].invorder[item]].pic, at_x, at_y, true);
 		at_x += data_to_game_coord(ItemWidth);
 
 		// go to next row when appropriate
diff --git a/engines/ags/engine/ac/inv_window.cpp b/engines/ags/engine/ac/inv_window.cpp
index 0a4f388465d..c383fc5751e 100644
--- a/engines/ags/engine/ac/inv_window.cpp
+++ b/engines/ags/engine/ac/inv_window.cpp
@@ -108,7 +108,7 @@ int InvWindow_GetItemsPerRow(GUIInvWindow *guii) {
 }
 
 int InvWindow_GetItemCount(GUIInvWindow *guii) {
-	return _G(charextra)[guii->GetCharacterId()].invorder_count;
+	return _GP(charextra)[guii->GetCharacterId()].invorder_count;
 }
 
 int InvWindow_GetRowCount(GUIInvWindow *guii) {
@@ -116,7 +116,7 @@ int InvWindow_GetRowCount(GUIInvWindow *guii) {
 }
 
 void InvWindow_ScrollDown(GUIInvWindow *guii) {
-	if ((_G(charextra)[guii->GetCharacterId()].invorder_count) >
+	if ((_GP(charextra)[guii->GetCharacterId()].invorder_count) >
 	        (guii->TopItem + (guii->ColCount * guii->RowCount))) {
 		guii->TopItem += guii->ColCount;
 		guii->MarkChanged();
@@ -134,9 +134,9 @@ void InvWindow_ScrollUp(GUIInvWindow *guii) {
 }
 
 ScriptInvItem *InvWindow_GetItemAtIndex(GUIInvWindow *guii, int index) {
-	if ((index < 0) || (index >= _G(charextra)[guii->GetCharacterId()].invorder_count))
+	if ((index < 0) || (index >= _GP(charextra)[guii->GetCharacterId()].invorder_count))
 		return nullptr;
-	return &_G(scrInv)[_G(charextra)[guii->GetCharacterId()].invorder[index]];
+	return &_G(scrInv)[_GP(charextra)[guii->GetCharacterId()].invorder[index]];
 }
 
 //=============================================================================
@@ -153,10 +153,10 @@ int offset_over_inv(GUIInvWindow *inv) {
 		return -1;
 
 	mover += inv->TopItem;
-	if ((mover < 0) || (mover >= _G(charextra)[inv->GetCharacterId()].invorder_count))
+	if ((mover < 0) || (mover >= _GP(charextra)[inv->GetCharacterId()].invorder_count))
 		return -1;
 
-	return _G(charextra)[inv->GetCharacterId()].invorder[mover];
+	return _GP(charextra)[inv->GetCharacterId()].invorder[mover];
 }
 
 //
@@ -237,9 +237,9 @@ int InventoryScreen::Redraw() {
 	numitems = 0;
 	widest = 0;
 	highest = 0;
-	if (_G(charextra)[_GP(game).playercharacter].invorder_count < 0)
+	if (_GP(charextra)[_GP(game).playercharacter].invorder_count < 0)
 		update_invorder();
-	if (_G(charextra)[_GP(game).playercharacter].invorder_count == 0) {
+	if (_GP(charextra)[_GP(game).playercharacter].invorder_count == 0) {
 		DisplayMessage(996);
 		_G(in_inv_screen)--;
 		return -1;
@@ -251,17 +251,17 @@ int InventoryScreen::Redraw() {
 		return -1;
 	}
 
-	for (int i = 0; i < _G(charextra)[_GP(game).playercharacter].invorder_count; ++i) {
-		if (_GP(game).invinfo[_G(charextra)[_GP(game).playercharacter].invorder[i]].name[0] != 0) {
-			dii[numitems].num = _G(charextra)[_GP(game).playercharacter].invorder[i];
-			dii[numitems].sprnum = _GP(game).invinfo[_G(charextra)[_GP(game).playercharacter].invorder[i]].pic;
+	for (int i = 0; i < _GP(charextra)[_GP(game).playercharacter].invorder_count; ++i) {
+		if (_GP(game).invinfo[_GP(charextra)[_GP(game).playercharacter].invorder[i]].name[0] != 0) {
+			dii[numitems].num = _GP(charextra)[_GP(game).playercharacter].invorder[i];
+			dii[numitems].sprnum = _GP(game).invinfo[_GP(charextra)[_GP(game).playercharacter].invorder[i]].pic;
 			int snn = dii[numitems].sprnum;
 			if (_GP(game).SpriteInfos[snn].Width > widest) widest = _GP(game).SpriteInfos[snn].Width;
 			if (_GP(game).SpriteInfos[snn].Height > highest) highest = _GP(game).SpriteInfos[snn].Height;
 			numitems++;
 		}
 	}
-	if (numitems != _G(charextra)[_GP(game).playercharacter].invorder_count)
+	if (numitems != _GP(charextra)[_GP(game).playercharacter].invorder_count)
 		quit("inconsistent inventory calculations");
 
 	widest += get_fixed_pixel_size(4);
diff --git a/engines/ags/engine/ac/object.cpp b/engines/ags/engine/ac/object.cpp
index 953de11aea3..dd2a907d261 100644
--- a/engines/ags/engine/ac/object.cpp
+++ b/engines/ags/engine/ac/object.cpp
@@ -439,8 +439,8 @@ void move_object(int objj, int tox, int toy, int spee, int ignwal) {
 	set_color_depth(_GP(game).GetColorDepth());
 	if (mslot > 0) {
 		_G(objs)[objj].moving = mslot;
-		_G(mls)[mslot].direct = ignwal;
-		convert_move_path_to_room_resolution(&_G(mls)[mslot]);
+		_GP(mls)[mslot].direct = ignwal;
+		convert_move_path_to_room_resolution(&_GP(mls)[mslot]);
 	}
 }
 
diff --git a/engines/ags/engine/ac/overlay.cpp b/engines/ags/engine/ac/overlay.cpp
index 2966b1c4daa..5ddb24a4f96 100644
--- a/engines/ags/engine/ac/overlay.cpp
+++ b/engines/ags/engine/ac/overlay.cpp
@@ -357,7 +357,7 @@ void get_overlay_position(const ScreenOverlay &over, int *x, int *y) {
 
 		auto view = FindNearestViewport(charid);
 		const int charpic = _GP(views)[_GP(game).chars[charid].view].loops[_GP(game).chars[charid].loop].frames[0].pic;
-		const int height = (_G(charextra)[charid].height < 1) ? _GP(game).SpriteInfos[charpic].Height : _G(charextra)[charid].height;
+		const int height = (_GP(charextra)[charid].height < 1) ? _GP(game).SpriteInfos[charpic].Height : _GP(charextra)[charid].height;
 		Point screenpt = view->RoomToScreen(
 		                     data_to_game_coord(_GP(game).chars[charid].x),
 		                     data_to_game_coord(_GP(game).chars[charid].get_effective_y()) - height).first;
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 52d39b34c24..3bda6104694 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -279,7 +279,7 @@ void unload_old_room() {
 			_G(charcache)[ff].inUse = 0;
 		}
 		// ensure that any half-moves (eg. with scaled movement) are stopped
-		_G(charextra)[ff].xwas = INVALID_X;
+		_GP(charextra)[ff].xwas = INVALID_X;
 	}
 
 	_GP(play).swap_portrait_lastchar = -1;
diff --git a/engines/ags/engine/ac/route_finder_impl.cpp b/engines/ags/engine/ac/route_finder_impl.cpp
index abc1a92b824..faea6490527 100644
--- a/engines/ags/engine/ac/route_finder_impl.cpp
+++ b/engines/ags/engine/ac/route_finder_impl.cpp
@@ -230,22 +230,22 @@ int find_route(short srcx, short srcy, short xx, short yy, Bitmap *onscreen, int
 #endif
 
 	int mlist = movlst;
-	_G(mls)[mlist].numstage = _G(num_navpoints);
-	memcpy(&_G(mls)[mlist].pos[0], &_G(navpoints)[0], sizeof(int) * _G(num_navpoints));
+	_GP(mls)[mlist].numstage = _G(num_navpoints);
+	memcpy(&_GP(mls)[mlist].pos[0], &_G(navpoints)[0], sizeof(int) * _G(num_navpoints));
 #ifdef DEBUG_PATHFINDER
 	AGS::Shared::Debug::Printf("stages: %d\n", _G(num_navpoints));
 #endif
 
 	for (i = 0; i < _G(num_navpoints) - 1; i++)
-		calculate_move_stage(&_G(mls)[mlist], i);
-
-	_G(mls)[mlist].fromx = srcx;
-	_G(mls)[mlist].fromy = srcy;
-	_G(mls)[mlist].onstage = 0;
-	_G(mls)[mlist].onpart = 0;
-	_G(mls)[mlist].doneflag = 0;
-	_G(mls)[mlist].lastx = -1;
-	_G(mls)[mlist].lasty = -1;
+		calculate_move_stage(&_GP(mls)[mlist], i);
+
+	_GP(mls)[mlist].fromx = srcx;
+	_GP(mls)[mlist].fromy = srcy;
+	_GP(mls)[mlist].onstage = 0;
+	_GP(mls)[mlist].onpart = 0;
+	_GP(mls)[mlist].doneflag = 0;
+	_GP(mls)[mlist].lastx = -1;
+	_GP(mls)[mlist].lasty = -1;
 	return mlist;
 }
 
diff --git a/engines/ags/engine/ac/route_finder_impl_legacy.cpp b/engines/ags/engine/ac/route_finder_impl_legacy.cpp
index 11adc664fc1..12c45378604 100644
--- a/engines/ags/engine/ac/route_finder_impl_legacy.cpp
+++ b/engines/ags/engine/ac/route_finder_impl_legacy.cpp
@@ -733,7 +733,7 @@ void calculate_move_stage(MoveList *mlsp, int aaa) {
 
 int find_route(short srcx, short srcy, short xx, short yy, Bitmap *onscreen, int movlst, int nocross, int ignore_walls) {
 	assert(onscreen != nullptr);
-	assert(_G(mls) != nullptr);
+	assert((int)_GP(mls).size() > movlst);
 	assert(pathbackx != nullptr);
 	assert(pathbacky != nullptr);
 
@@ -843,23 +843,23 @@ stage_again:
 		AGS::Shared::Debug::Printf("Route from %d,%d to %d,%d - %d stage, %d stages", orisrcx, orisrcy, xx, yy, pathbackstage, numstages);
 #endif
 		int mlist = movlst;
-		_G(mls)[mlist].numstage = numstages;
-		memcpy(&_G(mls)[mlist].pos[0], &reallyneed[0], sizeof(int) * numstages);
+		_GP(mls)[mlist].numstage = numstages;
+		memcpy(&_GP(mls)[mlist].pos[0], &reallyneed[0], sizeof(int) * numstages);
 #ifdef DEBUG_PATHFINDER
 		AGS::Shared::Debug::Printf("stages: %d\n", numstages);
 #endif
 
 		for (aaa = 0; aaa < numstages - 1; aaa++) {
-			calculate_move_stage(&_G(mls)[mlist], aaa);
+			calculate_move_stage(&_GP(mls)[mlist], aaa);
 		}
 
-		_G(mls)[mlist].fromx = orisrcx;
-		_G(mls)[mlist].fromy = orisrcy;
-		_G(mls)[mlist].onstage = 0;
-		_G(mls)[mlist].onpart = 0;
-		_G(mls)[mlist].doneflag = 0;
-		_G(mls)[mlist].lastx = -1;
-		_G(mls)[mlist].lasty = -1;
+		_GP(mls)[mlist].fromx = orisrcx;
+		_GP(mls)[mlist].fromy = orisrcy;
+		_GP(mls)[mlist].onstage = 0;
+		_GP(mls)[mlist].onpart = 0;
+		_GP(mls)[mlist].doneflag = 0;
+		_GP(mls)[mlist].lastx = -1;
+		_GP(mls)[mlist].lasty = -1;
 #ifdef DEBUG_PATHFINDER
 		// getch();
 #endif
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 01aa82f49ae..e15cfa294d6 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -363,9 +363,9 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
 	//
 	// 3. Allocate and init game objects
 	//
-	_G(charextra) = (CharacterExtras *)calloc(game.numcharacters, sizeof(CharacterExtras));
 	_G(charcache) = (CharacterCache *)calloc(1, sizeof(CharacterCache) * game.numcharacters + 5);
-	_G(mls) = (MoveList *)calloc(game.numcharacters + MAX_ROOM_OBJECTS + 1, sizeof(MoveList));
+	_GP(charextra).resize(game.numcharacters);
+	_GP(mls).resize(game.numcharacters + MAX_ROOM_OBJECTS + 1);
 	init_game_drawdata();
 	_GP(views) = std::move(ents.Views);
 
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index 248511f6079..366dc3d9d50 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -483,12 +483,12 @@ HSaveError WriteCharacters(Stream *out) {
 	out->WriteInt32(_GP(game).numcharacters);
 	for (int i = 0; i < _GP(game).numcharacters; ++i) {
 		_GP(game).chars[i].WriteToFile(out);
-		_G(charextra)[i].WriteToFile(out);
+		_GP(charextra)[i].WriteToFile(out);
 		Properties::WriteValues(_GP(play).charProps[i], out);
 		if (_G(loaded_game_file_version) <= kGameVersion_272)
 			WriteTimesRun272(*_GP(game).intrChar[i], out);
 		// character movement path cache
-		_G(mls)[CHMLSOFFS + i].WriteToFile(out);
+		_GP(mls)[CHMLSOFFS + i].WriteToFile(out);
 	}
 	return HSaveError::None();
 }
@@ -499,12 +499,12 @@ HSaveError ReadCharacters(Stream *in, int32_t cmp_ver, const PreservedParams & /
 		return err;
 	for (int i = 0; i < _GP(game).numcharacters; ++i) {
 		_GP(game).chars[i].ReadFromFile(in);
-		_G(charextra)[i].ReadFromFile(in);
+		_GP(charextra)[i].ReadFromFile(in);
 		Properties::ReadValues(_GP(play).charProps[i], in);
 		if (_G(loaded_game_file_version) <= kGameVersion_272)
 			ReadTimesRun272(*_GP(game).intrChar[i], in);
 		// character movement path cache
-		err = _G(mls)[CHMLSOFFS + i].ReadFromFile(in, cmp_ver > 0 ? 1 : 0);
+		err = _GP(mls)[CHMLSOFFS + i].ReadFromFile(in, cmp_ver > 0 ? 1 : 0);
 		if (!err)
 			return err;
 	}
@@ -919,7 +919,7 @@ HSaveError WriteThisRoom(Stream *out) {
 	// room object movement paths cache
 	out->WriteInt32(_GP(thisroom).ObjectCount + 1);
 	for (size_t i = 0; i < _GP(thisroom).ObjectCount + 1; ++i) {
-		_G(mls)[i].WriteToFile(out);
+		_GP(mls)[i].WriteToFile(out);
 	}
 
 	// room music volume
@@ -966,7 +966,7 @@ HSaveError ReadThisRoom(Stream *in, int32_t cmp_ver, const PreservedParams & /*p
 	if (!AssertCompatLimit(err, objmls_count, CHMLSOFFS, "room object move lists"))
 		return err;
 	for (int i = 0; i < objmls_count; ++i) {
-		err = _G(mls)[i].ReadFromFile(in, cmp_ver > 0 ? 1 : 0); // FIXME!!
+		err = _GP(mls)[i].ReadFromFile(in, cmp_ver > 0 ? 1 : 0); // FIXME!!
 		if (!err)
 			return err;
 	}
diff --git a/engines/ags/engine/game/savegame_v321.cpp b/engines/ags/engine/game/savegame_v321.cpp
index 18a0e73622a..27a4e9bdc93 100644
--- a/engines/ags/engine/game/savegame_v321.cpp
+++ b/engines/ags/engine/game/savegame_v321.cpp
@@ -173,7 +173,7 @@ static void restore_game_play(Stream *in, RestoredData &r_data) {
 static void ReadMoveList_Aligned(Stream *in) {
 	AlignedStream align_s(in, Shared::kAligned_Read);
 	for (int i = 0; i < _GP(game).numcharacters + MAX_ROOM_OBJECTS + 1; ++i) {
-		_G(mls)[i].ReadFromFile_Legacy(&align_s);
+		_GP(mls)[i].ReadFromFile_Legacy(&align_s);
 
 		align_s.Reset();
 	}
@@ -187,7 +187,7 @@ static void ReadGameSetupStructBase_Aligned(Stream *in) {
 static void ReadCharacterExtras_Aligned(Stream *in) {
 	AlignedStream align_s(in, Shared::kAligned_Read);
 	for (int i = 0; i < _GP(game).numcharacters; ++i) {
-		_G(charextra)[i].ReadFromFile(&align_s);
+		_GP(charextra)[i].ReadFromFile(&align_s);
 		align_s.Reset();
 	}
 }
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 8dc889f890c..2e78e61c113 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -582,8 +582,8 @@ void engine_init_game_settings() {
 		_GP(game).chars[ee].baseline = -1;
 		_GP(game).chars[ee].walkwaitcounter = 0;
 		_GP(game).chars[ee].z = 0;
-		_G(charextra)[ee].xwas = INVALID_X;
-		_G(charextra)[ee].zoom = 100;
+		_GP(charextra)[ee].xwas = INVALID_X;
+		_GP(charextra)[ee].zoom = 100;
 		if (_GP(game).chars[ee].view >= 0) {
 			// set initial loop to 0
 			_GP(game).chars[ee].loop = 0;
@@ -591,10 +591,10 @@ void engine_init_game_settings() {
 			if (_GP(views)[_GP(game).chars[ee].view].loops[0].numFrames < 1)
 				_GP(game).chars[ee].loop = 1;
 		}
-		_G(charextra)[ee].process_idle_this_time = 0;
-		_G(charextra)[ee].invorder_count = 0;
-		_G(charextra)[ee].slow_move_counter = 0;
-		_G(charextra)[ee].animwait = 0;
+		_GP(charextra)[ee].process_idle_this_time = 0;
+		_GP(charextra)[ee].invorder_count = 0;
+		_GP(charextra)[ee].slow_move_counter = 0;
+		_GP(charextra)[ee].animwait = 0;
 	}
 
 	_G(our_eip) = -5;
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index d0faf8a9bf5..5e826bb30d4 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -377,7 +377,7 @@ bool run_service_key_controls(KeyInput &out_key) {
 			        _GP(game).chars[chd].x, _GP(game).chars[chd].y, _GP(game).chars[chd].z,
 			        _GP(game).chars[chd].idleview, _GP(game).chars[chd].idletime, _GP(game).chars[chd].idleleft,
 			        _GP(game).chars[chd].walking, _GP(game).chars[chd].animating, _GP(game).chars[chd].following,
-			        _GP(game).chars[chd].flags, _GP(game).chars[chd].wait, _G(charextra)[chd].zoom);
+			        _GP(game).chars[chd].flags, _GP(game).chars[chd].wait, _GP(charextra)[chd].zoom);
 		}
 		Display(bigbuffer);
 		return false;
diff --git a/engines/ags/engine/main/update.cpp b/engines/ags/engine/main/update.cpp
index 37c4c72cbec..ac2254adf01 100644
--- a/engines/ags/engine/main/update.cpp
+++ b/engines/ags/engine/main/update.cpp
@@ -57,7 +57,7 @@ int do_movelist_move(short *mlnum, int *xx, int *yy) {
 	int need_to_fix_sprite = 0;
 	if (mlnum[0] < 1) quit("movelist_move: attempted to move on a non-exist movelist");
 	MoveList *cmls;
-	cmls = &_G(mls)[mlnum[0]];
+	cmls = &_GP(mls)[mlnum[0]];
 	fixed xpermove = cmls->xpermove[cmls->onstage], ypermove = cmls->ypermove[cmls->onstage];
 
 	short targetx = short((cmls->pos[cmls->onstage + 1] >> 16) & 0x00ffff);
@@ -203,7 +203,7 @@ void update_character_move_and_anim(std::vector<int> &followingAsSheep) {
 		if (_GP(game).chars[aa].on != 1) continue;
 
 		CharacterInfo *chi = &_GP(game).chars[aa];
-		CharacterExtras *chex = &_G(charextra)[aa];
+		CharacterExtras *chex = &_GP(charextra)[aa];
 
 		chi->UpdateMoveAndAnim(aa, chex, followingAsSheep);
 	}
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index 8c835dc464f..9b2482eb80f 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -38,6 +38,7 @@
 #include "ags/shared/gui/gui_textbox.h"
 #include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/directory.h"
+#include "ags/engine/ac/character_extras.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/draw_software.h"
 #include "ags/engine/ac/event.h"
@@ -237,6 +238,8 @@ Globals::Globals() {
 	_scrRegion = new ScriptRegion[MAX_ROOM_REGIONS];
 	_scrInv = new ScriptInvItem[MAX_INV];
 	_objcache = new ObjectCache[MAX_ROOM_OBJECTS];
+	_charextra = new std::vector<CharacterExtras>();
+	_mls = new std::vector<MoveList>();
 	_views = new std::vector<ViewStruct>();
 	_saveGameDirectory = AGS::Shared::SAVE_FOLDER_PREFIX;
 
@@ -493,6 +496,8 @@ Globals::~Globals() {
 	delete[] _scrRegion;
 	delete[] _scrInv;
 	delete[] _objcache;
+	delete _charextra;
+	delete _mls;
 	delete _views;
 
 	// game_init.cpp globals
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index a3f5a7082f5..24664b0f4ea 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -430,7 +430,6 @@ public:
 	 * @{
 	 */
 
-	CharacterExtras *_charextra = nullptr;
 	CharacterInfo *_playerchar = nullptr;
 	int32_t _sc_PlayerCharPtr = 0;
 	int _char_lowest_yp = 0;
@@ -753,7 +752,9 @@ public:
 	std::vector<ViewStruct> *_views;
 	CharacterCache *_charcache = nullptr;
 	ObjectCache *_objcache;
-	MoveList *_mls = nullptr;
+	std::vector<CharacterExtras> *_charextra;
+	std::vector<MoveList> *_mls;
+
 	GameSetup *_usetup;
 	AGS::Shared::String _saveGameDirectory;
 	AGS::Shared::String _saveGameSuffix;
diff --git a/engines/ags/plugins/ags_plugin.cpp b/engines/ags/plugins/ags_plugin.cpp
index 4bc4e1c1526..9f978835def 100644
--- a/engines/ags/plugins/ags_plugin.cpp
+++ b/engines/ags/plugins/ags_plugin.cpp
@@ -713,21 +713,21 @@ void IAGSEngine::SimulateMouseClick(int32 button) {
 }
 
 int IAGSEngine::GetMovementPathWaypointCount(int32 pathId) {
-	return _G(mls)[pathId % TURNING_AROUND].numstage;
+	return _GP(mls)[pathId % TURNING_AROUND].numstage;
 }
 
 int IAGSEngine::GetMovementPathLastWaypoint(int32 pathId) {
-	return _G(mls)[pathId % TURNING_AROUND].onstage;
+	return _GP(mls)[pathId % TURNING_AROUND].onstage;
 }
 
 void IAGSEngine::GetMovementPathWaypointLocation(int32 pathId, int32 waypoint, int32 *x, int32 *y) {
-	*x = (_G(mls)[pathId % TURNING_AROUND].pos[waypoint] >> 16) & 0x0000ffff;
-	*y = (_G(mls)[pathId % TURNING_AROUND].pos[waypoint] & 0x0000ffff);
+	*x = (_GP(mls)[pathId % TURNING_AROUND].pos[waypoint] >> 16) & 0x0000ffff;
+	*y = (_GP(mls)[pathId % TURNING_AROUND].pos[waypoint] & 0x0000ffff);
 }
 
 void IAGSEngine::GetMovementPathWaypointSpeed(int32 pathId, int32 waypoint, int32 *xSpeed, int32 *ySpeed) {
-	*xSpeed = _G(mls)[pathId % TURNING_AROUND].xpermove[waypoint];
-	*ySpeed = _G(mls)[pathId % TURNING_AROUND].ypermove[waypoint];
+	*xSpeed = _GP(mls)[pathId % TURNING_AROUND].xpermove[waypoint];
+	*ySpeed = _GP(mls)[pathId % TURNING_AROUND].ypermove[waypoint];
 }
 
 int IAGSEngine::IsRunningUnderDebugger() {


Commit: a40ea44aa2e99679d7da225fcefdcc1d6f2b0b3d
    https://github.com/scummvm/scummvm/commit/a40ea44aa2e99679d7da225fcefdcc1d6f2b0b3d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:08-07:00

Commit Message:
AGS: CharacterCache is stored in std::vector, hide in draw.cpp

>From upstream 417879fce0dc27d458a73fedc20f311f3305f26f

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/draw.h
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/global_object.cpp
    engines/ags/engine/ac/object.cpp
    engines/ags/engine/ac/room.cpp
    engines/ags/engine/game/game_init.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index f1fad499b4c..4ac8d0dfa82 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -386,6 +386,9 @@ void dispose_draw_method() {
 }
 
 void init_game_drawdata() {
+	// character and object caches
+	_GP(charcache).resize(_GP(game).numcharacters);
+
 	for (int i = 0; i < MAX_ROOM_OBJECTS; ++i)
 		_G(objcache)[i].image = nullptr;
 
@@ -407,6 +410,7 @@ void init_game_drawdata() {
 void dispose_game_drawdata() {
 	clear_drawobj_cache();
 
+	_GP(charcache).clear();
 	_GP(actsps).clear();
 	_GP(walkbehindobj).clear();
 
@@ -426,6 +430,14 @@ void dispose_room_drawdata() {
 }
 
 void clear_drawobj_cache() {
+	// clear the character cache
+	for (auto &cc : _GP(charcache)) {
+		if (cc.inUse)
+			delete cc.image;
+		cc.image = nullptr;
+		cc.inUse = 0;
+	}
+
 	// clear the object cache
 	for (int i = 0; i < MAX_ROOM_OBJECTS; ++i) {
 		delete _G(objcache)[i].image;
@@ -586,6 +598,10 @@ void on_roomcamera_changed(Camera *cam) {
 	invalidate_screen();
 }
 
+void mark_object_changed(int objid) {
+	_G(objcache)[objid].ywas = -9999;
+}
+
 void mark_screen_dirty() {
 	_G(screen_is_dirty) = true;
 }
@@ -1477,28 +1493,27 @@ void prepare_characters_for_drawing() {
 
 		// if the character was the same sprite and scaling last time,
 		// just use the cached image
-		if ((_G(charcache)[aa].inUse) &&
-		        (_G(charcache)[aa].sppic == specialpic) &&
-		        (_G(charcache)[aa].scaling == zoom_level) &&
-		        (_G(charcache)[aa].tintredwas == tint_red) &&
-		        (_G(charcache)[aa].tintgrnwas == tint_green) &&
-		        (_G(charcache)[aa].tintbluwas == tint_blue) &&
-		        (_G(charcache)[aa].tintamntwas == tint_amount) &&
-		        (_G(charcache)[aa].tintlightwas == tint_light) &&
-		        (_G(charcache)[aa].lightlevwas == light_level)) {
+		if ((_GP(charcache)[aa].inUse) &&
+		        (_GP(charcache)[aa].sppic == specialpic) &&
+		        (_GP(charcache)[aa].scaling == zoom_level) &&
+		        (_GP(charcache)[aa].tintredwas == tint_red) &&
+		        (_GP(charcache)[aa].tintgrnwas == tint_green) &&
+		        (_GP(charcache)[aa].tintbluwas == tint_blue) &&
+		        (_GP(charcache)[aa].tintamntwas == tint_amount) &&
+		        (_GP(charcache)[aa].tintlightwas == tint_light) &&
+		        (_GP(charcache)[aa].lightlevwas == light_level)) {
 			if (_G(walkBehindMethod) == DrawOverCharSprite) {
-				recycle_bitmap(actsp.Bmp, _G(charcache)[aa].image->GetColorDepth(), _G(charcache)[aa].image->GetWidth(), _G(charcache)[aa].image->GetHeight());
-				actsp.Bmp->Blit(_G(charcache)[aa].image, 0, 0);
+				recycle_bitmap(actsp.Bmp, _GP(charcache)[aa].image->GetColorDepth(), _GP(charcache)[aa].image->GetWidth(), _GP(charcache)[aa].image->GetHeight());
+				actsp.Bmp->Blit(_GP(charcache)[aa].image, 0, 0);
 			} else {
 				usingCachedImage = true;
 			}
-		} else if ((_G(charcache)[aa].inUse) &&
-		           (_G(charcache)[aa].sppic == specialpic) &&
+		} else if ((_GP(charcache)[aa].inUse) &&
+		           (_GP(charcache)[aa].sppic == specialpic) &&
 		           (_G(gfxDriver)->HasAcceleratedTransform())) {
 			usingCachedImage = true;
-		} else if (_G(charcache)[aa].inUse) {
-			//destroy_bitmap (_G(charcache)[aa].image);
-			_G(charcache)[aa].inUse = 0;
+		} else if (_GP(charcache)[aa].inUse) {
+			_GP(charcache)[aa].inUse = 0;
 		}
 
 		_G(our_eip) = 3332;
@@ -1529,17 +1544,17 @@ void prepare_characters_for_drawing() {
 		                 // adjust the Y positioning for the character's Z co-ord
 		                 - data_to_game_coord(chin->z);
 
-		_G(charcache)[aa].scaling = zoom_level;
-		_G(charcache)[aa].sppic = specialpic;
-		_G(charcache)[aa].tintredwas = tint_red;
-		_G(charcache)[aa].tintgrnwas = tint_green;
-		_G(charcache)[aa].tintbluwas = tint_blue;
-		_G(charcache)[aa].tintamntwas = tint_amount;
-		_G(charcache)[aa].tintlightwas = tint_light;
-		_G(charcache)[aa].lightlevwas = light_level;
+		_GP(charcache)[aa].scaling = zoom_level;
+		_GP(charcache)[aa].sppic = specialpic;
+		_GP(charcache)[aa].tintredwas = tint_red;
+		_GP(charcache)[aa].tintgrnwas = tint_green;
+		_GP(charcache)[aa].tintbluwas = tint_blue;
+		_GP(charcache)[aa].tintamntwas = tint_amount;
+		_GP(charcache)[aa].tintlightwas = tint_light;
+		_GP(charcache)[aa].lightlevwas = light_level;
 
 		// If cache needs to be re-drawn
-		if (!_G(charcache)[aa].inUse) {
+		if (!_GP(charcache)[aa].inUse) {
 
 			// create the base sprite in _GP(actsps)[useindx], which will
 			// be scaled and/or flipped, as appropriate
@@ -1571,9 +1586,9 @@ void prepare_characters_for_drawing() {
 			}
 
 			// update the character cache with the new image
-			_G(charcache)[aa].inUse = 1;
-			_G(charcache)[aa].image = recycle_bitmap(_G(charcache)[aa].image, coldept, actsp.Bmp->GetWidth(), actsp.Bmp->GetHeight());
-			_G(charcache)[aa].image->Blit(actsp.Bmp.get(), 0, 0);
+			_GP(charcache)[aa].inUse = 1;
+			_GP(charcache)[aa].image = recycle_bitmap(_GP(charcache)[aa].image, coldept, actsp.Bmp->GetWidth(), actsp.Bmp->GetHeight());
+			_GP(charcache)[aa].image->Blit(actsp.Bmp.get(), 0, 0);
 
 		} // end if !cache.inUse
 
diff --git a/engines/ags/engine/ac/draw.h b/engines/ags/engine/ac/draw.h
index 5c58d92e5cd..4c61d0da9ea 100644
--- a/engines/ags/engine/ac/draw.h
+++ b/engines/ags/engine/ac/draw.h
@@ -114,6 +114,8 @@ void on_roomviewport_changed(Viewport *view);
 void detect_roomviewport_overlaps(size_t z_index);
 // Updates drawing settings if room camera's size has changed
 void on_roomcamera_changed(Camera *cam);
+// Marks particular object as need too update the texture
+void mark_object_changed(int objid);
 
 // whether there are currently remnants of a DisplaySpeech
 void mark_screen_dirty();
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 3c1726cd9f6..ac08772fc9f 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -407,9 +407,6 @@ void unload_game_file() {
 
 	_GP(views).clear();
 
-	free(_G(charcache));
-	_G(charcache) = nullptr;
-
 	if (_G(splipsync) != nullptr) {
 		for (int i = 0; i < _G(numLipLines); ++i) {
 			free(_G(splipsync)[i].endtimeoffs);
@@ -1355,8 +1352,8 @@ void game_sprite_updated(int sprnum) {
 	}
 	// character cache
 	for (size_t i = 0; i < (size_t)_GP(game).numcharacters; ++i) {
-		if (_G(charcache)[i].sppic == sprnum)
-			_G(charcache)[i].sppic = -1;
+		if (_GP(charcache)[i].sppic == sprnum)
+			_GP(charcache)[i].sppic = -1;
 	}
 	// gui backgrounds
 	for (size_t i = 0; i < (size_t)_GP(game).numgui; ++i) {
@@ -1391,8 +1388,8 @@ void game_sprite_deleted(int sprnum) {
 	}
 	// character cache
 	for (size_t i = 0; i < (size_t)_GP(game).numcharacters; ++i) {
-		if (_G(charcache)[i].sppic == sprnum)
-			_G(charcache)[i].sppic = -1;
+		if (_GP(charcache)[i].sppic == sprnum)
+			_GP(charcache)[i].sppic = -1;
 	}
 	// gui backgrounds
 	for (size_t i = 0; i < (size_t)_GP(game).numgui; ++i) {
diff --git a/engines/ags/engine/ac/global_object.cpp b/engines/ags/engine/ac/global_object.cpp
index 37091da362f..a7067fc827d 100644
--- a/engines/ags/engine/ac/global_object.cpp
+++ b/engines/ags/engine/ac/global_object.cpp
@@ -200,8 +200,8 @@ void SetObjectBaseline(int obn, int basel) {
 	if (!is_valid_object(obn)) quit("!SetObjectBaseline: invalid object number specified");
 	// baseline has changed, invalidate the cache
 	if (_G(objs)[obn].baseline != basel) {
-		_G(objcache)[obn].ywas = -9999;
 		_G(objs)[obn].baseline = basel;
+		mark_object_changed(obn);
 	}
 }
 
@@ -411,8 +411,7 @@ void SetObjectIgnoreWalkbehinds(int cha, int clik) {
 	_G(objs)[cha].flags &= ~OBJF_NOWALKBEHINDS;
 	if (clik)
 		_G(objs)[cha].flags |= OBJF_NOWALKBEHINDS;
-	// clear the cache
-	_G(objcache)[cha].ywas = -9999;
+	mark_object_changed(cha);
 }
 
 void RunObjectInteraction(int aa, int mood) {
diff --git a/engines/ags/engine/ac/object.cpp b/engines/ags/engine/ac/object.cpp
index dd2a907d261..e22ba59d883 100644
--- a/engines/ags/engine/ac/object.cpp
+++ b/engines/ags/engine/ac/object.cpp
@@ -333,7 +333,7 @@ void Object_SetManualScaling(ScriptObject *objj, bool on) {
 	if (on) _G(objs)[objj->id].flags &= ~OBJF_USEROOMSCALING;
 	else _G(objs)[objj->id].flags |= OBJF_USEROOMSCALING;
 	// clear the cache
-	_G(objcache)[objj->id].ywas = -9999;
+	mark_object_changed(objj->id);
 }
 
 void Object_SetIgnoreScaling(ScriptObject *objj, int newval) {
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 3bda6104694..8554a637956 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -271,14 +271,8 @@ void unload_old_room() {
 	for (size_t i = 0; i < _GP(thisroom).LocalVariables.size() && i < MAX_GLOBAL_VARIABLES; ++i)
 		_G(croom)->interactionVariableValues[i] = _GP(thisroom).LocalVariables[i].Value;
 
-	// wipe the character cache when we change rooms
+	// ensure that any half-moves (eg. with scaled movement) are stopped
 	for (ff = 0; ff < _GP(game).numcharacters; ff++) {
-		if (_G(charcache)[ff].inUse) {
-			delete _G(charcache)[ff].image;
-			_G(charcache)[ff].image = nullptr;
-			_G(charcache)[ff].inUse = 0;
-		}
-		// ensure that any half-moves (eg. with scaled movement) are stopped
 		_GP(charextra)[ff].xwas = INVALID_X;
 	}
 
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index e15cfa294d6..83add8905eb 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -363,7 +363,6 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
 	//
 	// 3. Allocate and init game objects
 	//
-	_G(charcache) = (CharacterCache *)calloc(1, sizeof(CharacterCache) * game.numcharacters + 5);
 	_GP(charextra).resize(game.numcharacters);
 	_GP(mls).resize(game.numcharacters + MAX_ROOM_OBJECTS + 1);
 	init_game_drawdata();
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index 9b2482eb80f..0abe5bf48ec 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -38,6 +38,7 @@
 #include "ags/shared/gui/gui_textbox.h"
 #include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/directory.h"
+#include "ags/engine/ac/character_cache.h"
 #include "ags/engine/ac/character_extras.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/draw_software.h"
@@ -237,6 +238,7 @@ Globals::Globals() {
 	_scrHotspot = new ScriptHotspot[MAX_ROOM_HOTSPOTS];
 	_scrRegion = new ScriptRegion[MAX_ROOM_REGIONS];
 	_scrInv = new ScriptInvItem[MAX_INV];
+	_charcache = new std::vector<CharacterCache>();
 	_objcache = new ObjectCache[MAX_ROOM_OBJECTS];
 	_charextra = new std::vector<CharacterExtras>();
 	_mls = new std::vector<MoveList>();
@@ -495,6 +497,7 @@ Globals::~Globals() {
 	delete[] _scrHotspot;
 	delete[] _scrRegion;
 	delete[] _scrInv;
+	delete _charcache;
 	delete[] _objcache;
 	delete _charextra;
 	delete _mls;
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 24664b0f4ea..1919d4c2fee 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -750,7 +750,9 @@ public:
 	ScriptInvItem *_scrInv;
 	ScriptDialog *_scrDialog = nullptr;
 	std::vector<ViewStruct> *_views;
-	CharacterCache *_charcache = nullptr;
+	// Cached character and object states, used to determine
+	// whether these require texture update
+	std::vector<CharacterCache> *_charcache;
 	ObjectCache *_objcache;
 	std::vector<CharacterExtras> *_charextra;
 	std::vector<MoveList> *_mls;


Commit: 97eb5271d3e4f69d31f226ad38f53f63d944ad40
    https://github.com/scummvm/scummvm/commit/97eb5271d3e4f69d31f226ad38f53f63d944ad40
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:08-07:00

Commit Message:
AGS: Added member initialization for CharacterExtras, MoveList

>From upstream 3dd153f7c4cff10ec0abfa42ca608306cc730316

Changed paths:
    engines/ags/engine/ac/character_cache.h
    engines/ags/engine/ac/character_extras.h
    engines/ags/engine/ac/move_list.h


diff --git a/engines/ags/engine/ac/character_cache.h b/engines/ags/engine/ac/character_cache.h
index 7b0130d0e43..925075542b1 100644
--- a/engines/ags/engine/ac/character_cache.h
+++ b/engines/ags/engine/ac/character_cache.h
@@ -34,12 +34,12 @@ using namespace AGS; // FIXME later
 
 // stores cached info about the character
 struct CharacterCache {
-	Shared::Bitmap *image;
-	int sppic;
-	int scaling;
-	int inUse;
-	short tintredwas, tintgrnwas, tintbluwas, tintamntwas;
-	short lightlevwas, tintlightwas;
+	Shared::Bitmap *image = nullptr;
+	int sppic = 0;
+	int scaling = 0;
+	int inUse = 0;
+	short tintredwas = 0, tintgrnwas = 0, tintbluwas = 0, tintamntwas = 0;
+	short lightlevwas = 0, tintlightwas = 0;
 	// no mirroredWas is required, since the code inverts the sprite number
 };
 
diff --git a/engines/ags/engine/ac/character_extras.h b/engines/ags/engine/ac/character_extras.h
index d5c48a886e4..71f71d64a54 100644
--- a/engines/ags/engine/ac/character_extras.h
+++ b/engines/ags/engine/ac/character_extras.h
@@ -37,23 +37,23 @@ using namespace AGS; // FIXME later
 // The CharacterInfo struct size is fixed because it's exposed to script
 // and plugin API, therefore new stuff has to go here
 struct CharacterExtras {
-	short invorder[MAX_INVORDER];
-	short invorder_count;
-	// TODO: implement full AABB and keep updated, so that engine could rely on these cached values all time;
+	short invorder[MAX_INVORDER] = {};
+	short invorder_count = 0;
+	// TODO: implement full AABB and keep updated, so that engine could rely on these cached values all time = 0;
 	// TODO: consider having both fixed AABB and volatile one that changes with animation frame (unless you change how anims work)
-	short width;
-	short height;
-	short zoom;
-	short xwas;
-	short ywas;
-	short tint_r;
-	short tint_g;
-	short tint_b;
-	short tint_level;
-	short tint_light;
-	int8  process_idle_this_time;
-	int8  slow_move_counter;
-	short animwait;
+	short width = 0;
+	short height = 0;
+	short zoom = 0;
+	short xwas = 0;
+	short ywas = 0;
+	short tint_r = 0;
+	short tint_g = 0;
+	short tint_b = 0;
+	short tint_level = 0;
+	short tint_light = 0;
+	int8  process_idle_this_time = 0;
+	int8  slow_move_counter = 0;
+	short animwait = 0;
 
 	void ReadFromFile(Shared::Stream *in);
 	void WriteToFile(Shared::Stream *out);
diff --git a/engines/ags/engine/ac/move_list.h b/engines/ags/engine/ac/move_list.h
index 19fd04978c7..cc957949318 100644
--- a/engines/ags/engine/ac/move_list.h
+++ b/engines/ags/engine/ac/move_list.h
@@ -39,14 +39,14 @@ using namespace AGS; // FIXME later
 #define MAXNEEDSTAGES_LEGACY 40
 
 struct MoveList {
-	int32_t pos[MAXNEEDSTAGES];
-	int   numstage;
-	fixed xpermove[MAXNEEDSTAGES], ypermove[MAXNEEDSTAGES];
-	int   fromx, fromy;
-	int   onstage, onpart;
-	int   lastx, lasty;
-	int8  doneflag;
-	int8  direct;  // MoveCharDirect was used or not
+	int32_t pos[MAXNEEDSTAGES] = {};
+	int   numstage = 0;
+	fixed xpermove[MAXNEEDSTAGES] = {}, ypermove[MAXNEEDSTAGES] = {};
+	int   fromx = 0, fromy = 0;
+	int   onstage = 0, onpart = 0;
+	int   lastx = 0, lasty = 0;
+	int8  doneflag = 0;
+	int8  direct = 0;  // MoveCharDirect was used or not
 
 	void ReadFromFile_Legacy(Shared::Stream *in);
 	AGS::Engine::HSaveError ReadFromFile(Shared::Stream *in, int32_t cmp_ver);


Commit: a8ead540c5c6dd4a7158b2483e8c0a39ce0f18d1
    https://github.com/scummvm/scummvm/commit/a8ead540c5c6dd4a7158b2483e8c0a39ce0f18d1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:08-07:00

Commit Message:
AGS: GUIControl::CalcGraphicRect returns relative rect

>From upstream f827c75396f433542b451ec4caaea4c41c22e5b2

Changed paths:
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/draw.h
    engines/ags/shared/gui/gui_button.cpp
    engines/ags/shared/gui/gui_label.cpp
    engines/ags/shared/gui/gui_listbox.cpp
    engines/ags/shared/gui/gui_main.cpp
    engines/ags/shared/gui/gui_object.h
    engines/ags/shared/gui/gui_slider.cpp


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 4ac8d0dfa82..696d988aa82 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -103,6 +103,7 @@ ObjTexture &ObjTexture::operator=(ObjTexture &&o) {
 	Ddb = o.Ddb;
 	o.Ddb = nullptr;
 	Pos = o.Pos;
+	Off = o.Off;
 	return *this;
 }
 
@@ -1821,10 +1822,10 @@ void draw_gui_controls(GUIMain &gui) {
 		auto &objbg = _GP(guiobjbg)[draw_index];
 		Rect obj_surf = obj->CalcGraphicRect(GUI::Options.ClipControls);
 		recycle_bitmap(objbg.Bmp, _GP(game).GetColorDepth(), obj_surf.GetWidth(), obj_surf.GetHeight(), true);
-		obj->Draw(objbg.Bmp.get(), obj->X - obj_surf.Left, obj->Y - obj_surf.Top);
+		obj->Draw(objbg.Bmp.get(), -obj_surf.Left, -obj_surf.Top);
 
 		sync_object_texture(objbg, obj->HasAlphaChannel());
-		objbg.Pos = Point(obj_surf.GetLT());
+		objbg.Off = Point(obj_surf.GetLT());
 		obj->ClearChanged();
 	}
 }
@@ -1976,14 +1977,12 @@ void draw_gui_and_overlays() {
 				(obj->Width <= 0 || obj->Height <= 0) ||
 				(!obj->IsEnabled() && (GUI::Options.DisabledStyle == kGuiDis_Blackout)))
 				continue;
-			auto *obj_ddb = _GP(guiobjbg)[draw_index + obj_id].Ddb;
+			const auto &obj_tx = _GP(guiobjbg)[draw_index + obj_id];
+			auto *obj_ddb = obj_tx.Ddb;
 			assert(obj_ddb); // Test for missing texture, might happen if not marked for update
 			if (!obj_ddb) continue;
 			obj_ddb->SetAlpha(GfxDef::LegacyTrans255ToAlpha255(obj->GetTransparency()));
-			_G(gfxDriver)->DrawSprite(
-				_GP(guiobjbg)[draw_index + obj_id].Pos.X,
-				_GP(guiobjbg)[draw_index + obj_id].Pos.Y,
-				obj_ddb);
+			_G(gfxDriver)->DrawSprite(obj->X + obj_tx.Off.X, obj->Y + obj_tx.Off.Y, obj_ddb);
 		}
 		_G(gfxDriver)->EndSpriteBatch();
 	}
diff --git a/engines/ags/engine/ac/draw.h b/engines/ags/engine/ac/draw.h
index 4c61d0da9ea..599ea9e4b38 100644
--- a/engines/ags/engine/ac/draw.h
+++ b/engines/ags/engine/ac/draw.h
@@ -67,13 +67,15 @@ struct ObjTexture {
 	std::unique_ptr<Shared::Bitmap> Bmp;
 	// Corresponding texture, created by renderer
 	Engine::IDriverDependantBitmap *Ddb = nullptr;
-	// Sprite's position, may be used in case the texture's pos is different
-	// from the object's logical position (x,y,w,h) for some reason.
+	// Sprite's position
 	Point Pos;
+	// Texture's offset, *relative* to the logical sprite's position;
+	// may be used in case the texture's size is different for any reason
+	Point Off;
 
 	ObjTexture() = default;
-	ObjTexture(Shared::Bitmap *bmp, Engine::IDriverDependantBitmap *ddb, int x, int y)
-		: Bmp(bmp), Ddb(ddb), Pos(x, y) {
+	ObjTexture(Shared::Bitmap *bmp, Engine::IDriverDependantBitmap *ddb, int x, int y, int xoff = 0, int yoff = 0)
+		: Bmp(bmp), Ddb(ddb), Pos(x, y), Off(xoff, yoff) {
 	}
 	ObjTexture(const ObjTexture &) = default;
 	ObjTexture(ObjTexture &&o);
diff --git a/engines/ags/shared/gui/gui_button.cpp b/engines/ags/shared/gui/gui_button.cpp
index 63d986c59c0..7f7c2cea7fb 100644
--- a/engines/ags/shared/gui/gui_button.cpp
+++ b/engines/ags/shared/gui/gui_button.cpp
@@ -101,18 +101,19 @@ bool GUIButton::IsClippingImage() const {
 
 Rect GUIButton::CalcGraphicRect(bool clipped) {
 	if (clipped)
-		return RectWH(X, Y, Width, Height);
+		return RectWH(0, 0, Width, Height);
 	// TODO: need to find a way to cache image and text position, or there'll be some repetition
-	Rect rc = RectWH(X, Y, Width, Height);
+	Rect rc = RectWH(0, 0, Width, Height);
 	if (IsImageButton()) {
 		if (IsClippingImage())
 			return rc;
 		// Main button graphic
 		if (_GP(spriteset)[CurrentImage] != nullptr)
-			rc = SumRects(rc, RectWH(X, Y, get_adjusted_spritewidth(CurrentImage), get_adjusted_spriteheight(CurrentImage)));
+			rc = SumRects(rc, RectWH(0, 0, get_adjusted_spritewidth(CurrentImage), get_adjusted_spriteheight(CurrentImage)));
 		// Optionally merge with the inventory pic
 		if (_placeholder != kButtonPlace_None && _G(gui_inv_pic) >= 0) {
-			Size inv_sz = Size(get_adjusted_spritewidth(_G(gui_inv_pic)), get_adjusted_spriteheight(_G(gui_inv_pic)));
+			Size inv_sz = Size(get_adjusted_spritewidth(_G(gui_inv_pic)),
+				get_adjusted_spriteheight(_G(gui_inv_pic)));
 			GUIButtonPlaceholder place = _placeholder;
 			if (place == kButtonPlace_InvItemAuto) {
 				place = ((inv_sz.Width > Width - 6) || (inv_sz.Height > Height - 6)) ?
@@ -120,9 +121,9 @@ Rect GUIButton::CalcGraphicRect(bool clipped) {
 			}
 
 			Rect inv_rc = (place == kButtonPlace_InvItemStretch) ?
-				RectWH(X + 3, Y + 3, Width - 6, Height - 6) :
-				RectWH(X + Width / 2 - inv_sz.Width / 2,
-					Y + Height / 2 - inv_sz.Height / 2,
+				RectWH(0 + 3, 0 + 3, Width - 6, Height - 6) :
+				RectWH(0 + Width / 2 - inv_sz.Width / 2,
+					0 + Height / 2 - inv_sz.Height / 2,
 					inv_sz.Width, inv_sz.Height);
 			rc = SumRects(rc, inv_rc);
 		}
@@ -130,7 +131,7 @@ Rect GUIButton::CalcGraphicRect(bool clipped) {
 	// Optionally merge with the button text
 	if (!IsImageButton() || ((_placeholder == kButtonPlace_None) && !_unnamed)) {
 		PrepareTextToDraw();
-		Rect frame = RectWH(X + 2, Y + 2, Width - 4, Height - 4);
+		Rect frame = RectWH(0 + 2, 0 + 2, Width - 4, Height - 4);
 		if (IsPushed && IsMouseOver) {
 			frame.Left++;
 			frame.Top++;
diff --git a/engines/ags/shared/gui/gui_label.cpp b/engines/ags/shared/gui/gui_label.cpp
index 14957b4d73e..da049f7b58d 100644
--- a/engines/ags/shared/gui/gui_label.cpp
+++ b/engines/ags/shared/gui/gui_label.cpp
@@ -55,12 +55,12 @@ GUILabelMacro GUILabel::GetTextMacros() const {
 
 Rect GUILabel::CalcGraphicRect(bool clipped) {
 	if (clipped)
-		return RectWH(X, Y, Width, Height);
+		return RectWH(0, 0, Width, Height);
 	// TODO: need to find a way to text position, or there'll be some repetition
 	// have to precache text and size on some events:
 	// - translation change
 	// - macro value change (score, overhotspot etc)
-	Rect rc = RectWH(X, Y, Width, Height);
+	Rect rc = RectWH(0, 0, Width, Height);
 	PrepareTextToDraw();
 	if (SplitLinesForDrawing(_GP(Lines)) == 0)
 		return rc;
@@ -79,7 +79,7 @@ Rect GUILabel::CalcGraphicRect(bool clipped) {
 			(FrameAlignment)TextAlignment);
 		max_line.X2 = std::max(max_line.X2, lpos.X2);
 	}
-	return SumRects(rc, RectWH(X, Y, max_line.X2 - max_line.X1 + 1, at_y - linespacing + get_font_surface_height(Font)));
+	return SumRects(rc, RectWH(0, 0, max_line.X2 - max_line.X1 + 1, at_y - linespacing + get_font_surface_height(Font)));
 }
 
 void GUILabel::Draw(Bitmap *ds, int x, int y) {
diff --git a/engines/ags/shared/gui/gui_listbox.cpp b/engines/ags/shared/gui/gui_listbox.cpp
index 3a66f5d6f0c..3785b717a84 100644
--- a/engines/ags/shared/gui/gui_listbox.cpp
+++ b/engines/ags/shared/gui/gui_listbox.cpp
@@ -83,14 +83,15 @@ bool GUIListBox::IsInRightMargin(int x) const {
 
 Rect GUIListBox::CalcGraphicRect(bool clipped) {
 	if (clipped)
-		return RectWH(X, Y, Width, Height);
+		return RectWH(0, 0, Width, Height);
 	// TODO: need to find a way to text position, or there'll be some repetition
 	// have to precache text and size on some events:
 	// - translation change
 	// - macro value change (score, overhotspot etc)
-	Rect rc = RectWH(X, Y, Width, Height);
+	Rect rc = RectWH(0, 0, Width, Height);
 	UpdateMetrics();
 	const int width = Width - 1;
+	const int height = Height - 1;
 	const int pixel_size = get_fixed_pixel_size(1);
 	int right_hand_edge = width - pixel_size - 1;
 	// calculate the scroll bar's width if necessary
@@ -105,7 +106,7 @@ Rect GUIListBox::CalcGraphicRect(bool clipped) {
 			(FrameAlignment)TextAlignment);
 		max_line.X2 = std::max(max_line.X2, lpos.X2);
 	}
-	return SumRects(rc, RectWH(X, Y, max_line.X2 - max_line.X1 + 1, Height));
+	return SumRects(rc, RectWH(0, 0, max_line.X2 - max_line.X1 + 1, Height));
 }
 
 int GUIListBox::AddItem(const String &text) {
diff --git a/engines/ags/shared/gui/gui_main.cpp b/engines/ags/shared/gui/gui_main.cpp
index f1507ac601a..6a5f9656421 100644
--- a/engines/ags/shared/gui/gui_main.cpp
+++ b/engines/ags/shared/gui/gui_main.cpp
@@ -282,8 +282,9 @@ void GUIMain::DrawWithControls(Bitmap *ds) {
 		} else {
 			const Rect rc = objToDraw->CalcGraphicRect(GUI::Options.ClipControls && objToDraw->IsContentClipped());
 			tempbmp.CreateTransparent(rc.GetWidth(), rc.GetHeight());
-			objToDraw->Draw(&tempbmp, objToDraw->X - rc.Left, objToDraw->Y - rc.Top);
-			draw_gui_sprite(ds, true, objToDraw->X, objToDraw->Y, &tempbmp, objToDraw->HasAlphaChannel(), kBlendMode_Alpha,
+			objToDraw->Draw(&tempbmp, -rc.Left, -rc.Top);
+			draw_gui_sprite(ds, true, objToDraw->X + rc.Left, objToDraw->Y + rc.Top,
+				&tempbmp, objToDraw->HasAlphaChannel(), kBlendMode_Alpha,
 				GfxDef::LegacyTrans255ToAlpha255(objToDraw->GetTransparency()));
 		}
 
diff --git a/engines/ags/shared/gui/gui_object.h b/engines/ags/shared/gui/gui_object.h
index 1e8381e2e5f..196918d7167 100644
--- a/engines/ags/shared/gui/gui_object.h
+++ b/engines/ags/shared/gui/gui_object.h
@@ -68,9 +68,9 @@ public:
 
 	// Operations
 	// Returns the (untransformed!) visual rectangle of this control,
-	// optionally clipped by the logical position
+	// in *relative* coordinates, optionally clipped by the logical size
 	virtual Rect    CalcGraphicRect(bool clipped) {
-		return RectWH(X, Y, Width, Height);
+		return RectWH(0, 0, Width, Height);
 	}
 	virtual void    Draw(Bitmap *ds, int x = 0, int y = 0) {
 		(void)ds; (void)x; (void)y;
diff --git a/engines/ags/shared/gui/gui_slider.cpp b/engines/ags/shared/gui/gui_slider.cpp
index db18d9f77db..1fcf6b491b1 100644
--- a/engines/ags/shared/gui/gui_slider.cpp
+++ b/engines/ags/shared/gui/gui_slider.cpp
@@ -65,9 +65,9 @@ Rect GUISlider::CalcGraphicRect(bool clipped) {
 	// Sliders are never clipped as of 3.6.0
 	// TODO: precalculate everything on width/height/graphic change!!
 	UpdateMetrics();
-	Rect logical = RectWH(X, Y, Width, Height);
-	Rect bar = Rect::MoveBy(_cachedBar, X, Y);
-	Rect handle = Rect::MoveBy(_cachedHandle, X, Y);
+	Rect logical = RectWH(0, 0, Width, Height);
+	Rect bar = _cachedBar;
+	Rect handle = _cachedHandle;
 	return Rect(
 		std::min(std::min(logical.Left, bar.Left), handle.Left),
 		std::min(std::min(logical.Top, bar.Top), handle.Top),


Commit: 28a2b6aa9c1032bc27343eab763c2dd66998fc2a
    https://github.com/scummvm/scummvm/commit/28a2b6aa9c1032bc27343eab763c2dd66998fc2a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:09-07:00

Commit Message:
AGS: Added extra checks for abort_engine being set

This fixes an intermittant error I noticed when
qutting QFG2AGDI via the quit dialog could open
the debugger with an error rather than exitting

Changed paths:
    engines/ags/engine/ac/event.cpp
    engines/ags/engine/script/script.cpp


diff --git a/engines/ags/engine/ac/event.cpp b/engines/ags/engine/ac/event.cpp
index 32049642c0b..4af03f3977a 100644
--- a/engines/ags/engine/ac/event.cpp
+++ b/engines/ags/engine/ac/event.cpp
@@ -325,9 +325,11 @@ void process_event(const EventHappened *evp) {
 			_G(gfxDriver)->DestroyDDB(ddb);
 		}
 
-	} else if (evp->type == EV_IFACECLICK)
+	} else if (evp->type == EV_IFACECLICK) {
 		process_interface_click(evp->data1, evp->data2, evp->data3);
-	else quit("process_event: unknown event to process");
+	} else {
+		quit("process_event: unknown event to process");
+	}
 }
 
 
@@ -358,7 +360,7 @@ void processallevents() {
 
 	_G(inside_processevent)++;
 
-	for (size_t i = 0; i < evtCopy->size(); ++i) {
+	for (size_t i = 0; i < evtCopy->size() && !_G(abort_engine); ++i) {
 		process_event(&(*evtCopy)[i]);
 
 		if (room_was != _GP(play).room_changes)
diff --git a/engines/ags/engine/script/script.cpp b/engines/ags/engine/script/script.cpp
index 3a25cb36f26..75dbe6a1a62 100644
--- a/engines/ags/engine/script/script.cpp
+++ b/engines/ags/engine/script/script.cpp
@@ -354,7 +354,7 @@ int RunScriptFunction(ccInstance *sci, const char *tsname, size_t numParam, cons
 	toret = _G(curscript)->inst->CallScriptFunction(tsname, numParam, params);
 
 	// 100 is if Aborted (eg. because we are LoadAGSGame'ing)
-	if ((toret != 0) && (toret != -2) && (toret != 100)) {
+	if (!_G(abort_engine) && (toret != 0) && (toret != -2) && (toret != 100)) {
 		quit_with_script_error(tsname);
 	}
 


Commit: 63c42d42c377484bb25f908e7d5b8ecda41cec24
    https://github.com/scummvm/scummvm/commit/63c42d42c377484bb25f908e7d5b8ecda41cec24
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:09-07:00

Commit Message:
AGS: Fixed ScriptString could be serialized with wrong length

>From upstream c00be4f9ee9501aebb0abc81ebbe5b4f2ffb0c0c

Changed paths:
    engines/ags/engine/ac/dynobj/script_string.cpp
    engines/ags/engine/ac/dynobj/script_string.h
    engines/ags/engine/ac/string.cpp


diff --git a/engines/ags/engine/ac/dynobj/script_string.cpp b/engines/ags/engine/ac/dynobj/script_string.cpp
index d6c4d4da094..3e6bca65948 100644
--- a/engines/ags/engine/ac/dynobj/script_string.cpp
+++ b/engines/ags/engine/ac/dynobj/script_string.cpp
@@ -34,9 +34,9 @@ DynObjectRef ScriptString::CreateString(const char *fromText) {
 
 int ScriptString::Dispose(const char *address, bool force) {
 	// always dispose
-	if (text) {
-		free(text);
-		text = nullptr;
+	if (_text) {
+		free(_text);
+		_text = nullptr;
 	}
 	delete this;
 	return 1;
@@ -50,29 +50,34 @@ size_t ScriptString::CalcSerializeSize() {
 	return _len + 1 + sizeof(int32_t);
 }
 
-void ScriptString::Serialize(const char *address, Stream *out) {
-	const auto *cstr = text ? text : "";
+void ScriptString::Serialize(const char * /*address*/, Stream *out) {
+	const auto *cstr = _text ? _text : "";
 	out->WriteInt32(_len);
 	out->Write(cstr, _len + 1);
 }
 
-void ScriptString::Unserialize(int index, Stream *in, size_t data_sz) {
+void ScriptString::Unserialize(int index, Stream *in, size_t /*data_sz*/) {
 	_len = in->ReadInt32();
-	text = (char *)malloc(_len + 1);
-	in->Read(text, _len + 1);
-	text[_len] = 0; // for safety
-	ccRegisterUnserializedObject(index, text, this);
+	_text = (char *)malloc(_len + 1);
+	in->Read(_text, _len + 1);
+	_text[_len] = 0; // for safety
+	ccRegisterUnserializedObject(index, _text, this);
 }
 
-ScriptString::ScriptString() {
-	text = nullptr;
-	_len = 0;
+ScriptString::ScriptString(const char *text) {
+	_len = strlen(text);
+	_text = (char *)malloc(_len + 1);
+	memcpy(_text, text, _len + 1);
 }
 
-ScriptString::ScriptString(const char *fromText) {
-	_len = strlen(fromText);
-	text = (char *)malloc(_len + 1);
-	memcpy(text, fromText, _len + 1);
+ScriptString::ScriptString(char *text, bool take_ownership) {
+	_len = strlen(text);
+	if (take_ownership) {
+		_text = text;
+	} else {
+		_text = (char *)malloc(_len + 1);
+		memcpy(_text, text, _len + 1);
+	}
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/dynobj/script_string.h b/engines/ags/engine/ac/dynobj/script_string.h
index ae3406c4b76..4d6863d474a 100644
--- a/engines/ags/engine/ac/dynobj/script_string.h
+++ b/engines/ags/engine/ac/dynobj/script_string.h
@@ -27,18 +27,18 @@
 namespace AGS3 {
 
 struct ScriptString final : AGSCCDynamicObject, ICCStringClass {
-	// TODO: the preallocated text buffer may be assigned externally;
-	// find out if it's possible to refactor while keeping same functionality
-	char *text;
-
 	int Dispose(const char *address, bool force) override;
 	const char *GetType() override;
 	void Unserialize(int index, AGS::Shared::Stream *in, size_t data_sz) override;
 
 	DynObjectRef CreateString(const char *fromText) override;
 
-	ScriptString();
-	ScriptString(const char *fromText);
+	ScriptString() = default;
+	ScriptString(const char *text);
+	ScriptString(char *text, bool take_ownership);
+	char *GetTextPtr() const {
+		return _text;
+	}
 
 protected:
 	// Calculate and return required space for serialization, in bytes
@@ -47,7 +47,10 @@ protected:
 	void Serialize(const char *address, AGS::Shared::Stream *out) override;
 
 private:
-	size_t _len;
+	// TODO: the preallocated text buffer may be assigned externally;
+	// find out if it's possible to refactor while keeping same functionality
+	char *_text = nullptr;
+	size_t _len = 0;
 };
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/string.cpp b/engines/ags/engine/ac/string.cpp
index dc5dda16662..a2b5c7fc6b6 100644
--- a/engines/ags/engine/ac/string.cpp
+++ b/engines/ags/engine/ac/string.cpp
@@ -255,11 +255,10 @@ DynObjectRef CreateNewScriptStringObj(const char *fromText, bool reAllocate) {
 	ScriptString *str;
 	if (reAllocate) {
 		str = new ScriptString(fromText);
-	} else {
-		str = new ScriptString();
-		str->text = const_cast<char *>(fromText);
+	} else { // TODO: refactor to avoid const casts!
+		str = new ScriptString((char *)fromText, true);
 	}
-	void *obj_ptr = str->text;
+	void *obj_ptr = str->GetTextPtr();
 	int32_t handle = ccRegisterManagedObject(obj_ptr, str);
 	if (handle == 0) {
 		delete str;


Commit: 97c12e8435abdc5e51b390f07600b3c4bb0517c0
    https://github.com/scummvm/scummvm/commit/97c12e8435abdc5e51b390f07600b3c4bb0517c0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:09-07:00

Commit Message:
AGS: Merged Character and ObjectCache structs, and hid in draw.cpp

>From upstream 753d47d975b10666702c47a22cbc4e68847787f1

Changed paths:
  R engines/ags/engine/ac/character_cache.h
  R engines/ags/engine/ac/object_cache.h
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/draw.h
    engines/ags/engine/ac/drawing_surface.cpp
    engines/ags/engine/ac/dynamic_sprite.cpp
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/global_object.cpp
    engines/ags/engine/ac/object.cpp
    engines/ags/engine/ac/room.cpp
    engines/ags/engine/game/game_init.cpp
    engines/ags/engine/main/engine.cpp
    engines/ags/engine/main/game_file.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h
    engines/ags/plugins/ags_plugin.cpp


diff --git a/engines/ags/engine/ac/character_cache.h b/engines/ags/engine/ac/character_cache.h
deleted file mode 100644
index 925075542b1..00000000000
--- a/engines/ags/engine/ac/character_cache.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef AGS_ENGINE_AC_CHARACTER_CACHE_H
-#define AGS_ENGINE_AC_CHARACTER_CACHE_H
-
-namespace AGS3 {
-
-namespace AGS {
-namespace Shared {
-class Bitmap;
-} // namespace Shared
-} // namespace AGS
-
-using namespace AGS; // FIXME later
-
-// stores cached info about the character
-struct CharacterCache {
-	Shared::Bitmap *image = nullptr;
-	int sppic = 0;
-	int scaling = 0;
-	int inUse = 0;
-	short tintredwas = 0, tintgrnwas = 0, tintbluwas = 0, tintamntwas = 0;
-	short lightlevwas = 0, tintlightwas = 0;
-	// no mirroredWas is required, since the code inverts the sprite number
-};
-
-} // namespace AGS3
-
-#endif
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 696d988aa82..2416d54973a 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -1,4 +1,4 @@
-/* ScummVM - Graphic Adventure Engine
+	/* 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
@@ -27,7 +27,6 @@
 #include "ags/shared/util/compress.h"
 #include "ags/shared/util/wgt2_allg.h"
 #include "ags/shared/ac/view.h"
-#include "ags/engine/ac/character_cache.h"
 #include "ags/engine/ac/character_extras.h"
 #include "ags/shared/ac/character_info.h"
 #include "ags/engine/ac/display.h"
@@ -42,7 +41,6 @@
 #include "ags/engine/ac/gui.h"
 #include "ags/engine/ac/mouse.h"
 #include "ags/engine/ac/move_list.h"
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/overlay.h"
 #include "ags/engine/ac/sys_events.h"
 #include "ags/engine/ac/room_object.h"
@@ -433,10 +431,10 @@ void dispose_room_drawdata() {
 void clear_drawobj_cache() {
 	// clear the character cache
 	for (auto &cc : _GP(charcache)) {
-		if (cc.inUse)
+		if (cc.in_use)
 			delete cc.image;
 		cc.image = nullptr;
-		cc.inUse = 0;
+		cc.in_use = false;
 	}
 
 	// clear the object cache
@@ -600,7 +598,23 @@ void on_roomcamera_changed(Camera *cam) {
 }
 
 void mark_object_changed(int objid) {
-	_G(objcache)[objid].ywas = -9999;
+	_G(objcache)[objid].y = -9999;
+}
+
+void reset_objcache_for_sprite(int sprnum) {
+	// Check if this sprite is assigned to any game object, and update them if necessary
+	// room objects cache
+	if (_G(croom) != nullptr) {
+		for (size_t i = 0; i < (size_t)_G(croom)->numobj; ++i) {
+			if (_G(objs)[i].num == sprnum)
+				_G(objcache)[i].sppic = -1;
+		}
+	}
+	// character cache
+	for (size_t i = 0; i < (size_t)_GP(game).numcharacters; ++i) {
+		if (_GP(charcache)[i].sppic == sprnum)
+			_GP(charcache)[i].sppic = -1;
+	}
 }
 
 void mark_screen_dirty() {
@@ -1201,14 +1215,14 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 			(_G(objcache)[aa].sppic == _G(objs)[aa].num) &&
 			(actsp.Bmp != nullptr)) {
 		// HW acceleration
-		_G(objcache)[aa].tintamntwas = tint_level;
-		_G(objcache)[aa].tintredwas = tint_red;
-		_G(objcache)[aa].tintgrnwas = tint_green;
-		_G(objcache)[aa].tintbluwas = tint_blue;
-		_G(objcache)[aa].tintlightwas = tint_light;
-		_G(objcache)[aa].lightlevwas = light_level;
-		_G(objcache)[aa].zoomWas = zoom_level;
-		_G(objcache)[aa].mirroredWas = isMirrored;
+		_G(objcache)[aa].tintamnt = tint_level;
+		_G(objcache)[aa].tintr = tint_red;
+		_G(objcache)[aa].tintg = tint_green;
+		_G(objcache)[aa].tintb = tint_blue;
+		_G(objcache)[aa].tintlight = tint_light;
+		_G(objcache)[aa].lightlev = light_level;
+		_G(objcache)[aa].zoom = zoom_level;
+		_G(objcache)[aa].mirrored = isMirrored;
 
 		return 1;
 	}
@@ -1222,24 +1236,24 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 	// If we have the image cached, use it
 	if ((_G(objcache)[aa].image != nullptr) &&
 	        (_G(objcache)[aa].sppic == _G(objs)[aa].num) &&
-	        (_G(objcache)[aa].tintamntwas == tint_level) &&
-	        (_G(objcache)[aa].tintlightwas == tint_light) &&
-	        (_G(objcache)[aa].tintredwas == tint_red) &&
-	        (_G(objcache)[aa].tintgrnwas == tint_green) &&
-	        (_G(objcache)[aa].tintbluwas == tint_blue) &&
-	        (_G(objcache)[aa].lightlevwas == light_level) &&
-	        (_G(objcache)[aa].zoomWas == zoom_level) &&
-	        (_G(objcache)[aa].mirroredWas == isMirrored)) {
+			(_G(objcache)[aa].tintamnt == tint_level) &&
+			(_G(objcache)[aa].tintlight == tint_light) &&
+			(_G(objcache)[aa].tintr == tint_red) &&
+			(_G(objcache)[aa].tintg == tint_green) &&
+			(_G(objcache)[aa].tintb == tint_blue) &&
+			(_G(objcache)[aa].lightlev == light_level) &&
+			(_G(objcache)[aa].zoom == zoom_level) &&
+			(_G(objcache)[aa].mirrored == isMirrored)) {
 		// the image is the same, we can use it cached!
 		if ((_G(walkBehindMethod) != DrawOverCharSprite) &&
 			(actsp.Bmp != nullptr))
 			return 1;
 		// Check if the X & Y co-ords are the same, too -- if so, there
 		// is scope for further optimisations
-		if ((_G(objcache)[aa].xwas == _G(objs)[aa].x) &&
-			(_G(objcache)[aa].ywas == _G(objs)[aa].y) &&
-			(actsp.Bmp != nullptr) &&
-			(_G(walk_behind_baselines_changed) == 0))
+		if ((_G(objcache)[aa].x == _G(objs)[aa].x) &&
+				(_G(objcache)[aa].y == _G(objs)[aa].y) &&
+				(actsp.Bmp != nullptr) &&
+				(_G(walk_behind_baselines_changed) == 0))
 			return 1;
 		recycle_bitmap(actsp.Bmp, coldept, sprwidth, sprheight);
 		actsp.Bmp->Blit(_G(objcache)[aa].image, 0, 0, 0, 0, _G(objcache)[aa].image->GetWidth(), _G(objcache)[aa].image->GetHeight());
@@ -1278,14 +1292,14 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 	// Create the cached image and store it
 	_G(objcache)[aa].image->Blit(actsp.Bmp.get(), 0, 0);
 	_G(objcache)[aa].sppic = _G(objs)[aa].num;
-	_G(objcache)[aa].tintamntwas = tint_level;
-	_G(objcache)[aa].tintredwas = tint_red;
-	_G(objcache)[aa].tintgrnwas = tint_green;
-	_G(objcache)[aa].tintbluwas = tint_blue;
-	_G(objcache)[aa].tintlightwas = tint_light;
-	_G(objcache)[aa].lightlevwas = light_level;
-	_G(objcache)[aa].zoomWas = zoom_level;
-	_G(objcache)[aa].mirroredWas = isMirrored;
+	_G(objcache)[aa].tintamnt = tint_level;
+	_G(objcache)[aa].tintr = tint_red;
+	_G(objcache)[aa].tintg = tint_green;
+	_G(objcache)[aa].tintb = tint_blue;
+	_G(objcache)[aa].tintlight = tint_light;
+	_G(objcache)[aa].lightlev = light_level;
+	_G(objcache)[aa].zoom = zoom_level;
+	_G(objcache)[aa].mirrored = isMirrored;
 	return 0;
 }
 
@@ -1307,8 +1321,8 @@ void prepare_objects_for_drawing() {
 		auto &actsp = _GP(actsps)[useindx];
 
 		// update the cache for next time
-		_G(objcache)[aa].xwas = _G(objs)[aa].x;
-		_G(objcache)[aa].ywas = _G(objs)[aa].y;
+		_G(objcache)[aa].x = _G(objs)[aa].x;
+		_G(objcache)[aa].y = _G(objs)[aa].y;
 		int atxp = data_to_game_coord(_G(objs)[aa].x);
 		int atyp = data_to_game_coord(_G(objs)[aa].y) - tehHeight;
 
@@ -1328,19 +1342,19 @@ void prepare_objects_for_drawing() {
 		}
 
 		if (_G(gfxDriver)->HasAcceleratedTransform()) {
-			actsp.Ddb->SetFlippedLeftRight(_G(objcache)[aa].mirroredWas != 0);
+			actsp.Ddb->SetFlippedLeftRight(_G(objcache)[aa].mirrored);
 			actsp.Ddb->SetStretch(_G(objs)[aa].last_width, _G(objs)[aa].last_height);
-			actsp.Ddb->SetTint(_G(objcache)[aa].tintredwas, _G(objcache)[aa].tintgrnwas, _G(objcache)[aa].tintbluwas, (_G(objcache)[aa].tintamntwas * 256) / 100);
+			actsp.Ddb->SetTint(_G(objcache)[aa].tintr, _G(objcache)[aa].tintg, _G(objcache)[aa].tintb, (_G(objcache)[aa].tintamnt * 256) / 100);
 
-			if (_G(objcache)[aa].tintamntwas > 0) {
-				if (_G(objcache)[aa].tintlightwas == 0)  // luminance of 0 -- pass 1 to enable
+			if (_G(objcache)[aa].tintamnt > 0) {
+				if (_G(objcache)[aa].tintlight == 0)  // luminance of 0 -- pass 1 to enable
 					actsp.Ddb->SetLightLevel(1);
-				else if (_G(objcache)[aa].tintlightwas < 250)
-					actsp.Ddb->SetLightLevel(_G(objcache)[aa].tintlightwas);
+				else if (_G(objcache)[aa].tintlight < 250)
+					actsp.Ddb->SetLightLevel(_G(objcache)[aa].tintlight);
 				else
 					actsp.Ddb->SetLightLevel(0);
-			} else if (_G(objcache)[aa].lightlevwas != 0)
-				actsp.Ddb->SetLightLevel((_G(objcache)[aa].lightlevwas * 25) / 10 + 256);
+			} else if (_G(objcache)[aa].lightlev != 0)
+				actsp.Ddb->SetLightLevel((_G(objcache)[aa].lightlev * 25) / 10 + 256);
 			else
 				actsp.Ddb->SetLightLevel(0);
 		}
@@ -1494,27 +1508,27 @@ void prepare_characters_for_drawing() {
 
 		// if the character was the same sprite and scaling last time,
 		// just use the cached image
-		if ((_GP(charcache)[aa].inUse) &&
-		        (_GP(charcache)[aa].sppic == specialpic) &&
-		        (_GP(charcache)[aa].scaling == zoom_level) &&
-		        (_GP(charcache)[aa].tintredwas == tint_red) &&
-		        (_GP(charcache)[aa].tintgrnwas == tint_green) &&
-		        (_GP(charcache)[aa].tintbluwas == tint_blue) &&
-		        (_GP(charcache)[aa].tintamntwas == tint_amount) &&
-		        (_GP(charcache)[aa].tintlightwas == tint_light) &&
-		        (_GP(charcache)[aa].lightlevwas == light_level)) {
+		if ((_GP(charcache)[aa].in_use) &&
+			(_GP(charcache)[aa].sppic == specialpic) &&
+			(_GP(charcache)[aa].zoom == zoom_level) &&
+			(_GP(charcache)[aa].tintr == tint_red) &&
+			(_GP(charcache)[aa].tintg == tint_green) &&
+			(_GP(charcache)[aa].tintb == tint_blue) &&
+			(_GP(charcache)[aa].tintamnt == tint_amount) &&
+			(_GP(charcache)[aa].tintlight == tint_light) &&
+			(_GP(charcache)[aa].lightlev == light_level)) {
 			if (_G(walkBehindMethod) == DrawOverCharSprite) {
 				recycle_bitmap(actsp.Bmp, _GP(charcache)[aa].image->GetColorDepth(), _GP(charcache)[aa].image->GetWidth(), _GP(charcache)[aa].image->GetHeight());
 				actsp.Bmp->Blit(_GP(charcache)[aa].image, 0, 0);
 			} else {
 				usingCachedImage = true;
 			}
-		} else if ((_GP(charcache)[aa].inUse) &&
-		           (_GP(charcache)[aa].sppic == specialpic) &&
-		           (_G(gfxDriver)->HasAcceleratedTransform())) {
+		} else if ((_GP(charcache)[aa].in_use) &&
+			(_GP(charcache)[aa].sppic == specialpic) &&
+			(_G(gfxDriver)->HasAcceleratedTransform())) {
 			usingCachedImage = true;
-		} else if (_GP(charcache)[aa].inUse) {
-			_GP(charcache)[aa].inUse = 0;
+		} else if (_GP(charcache)[aa].in_use) {
+			_GP(charcache)[aa].in_use = false;
 		}
 
 		_G(our_eip) = 3332;
@@ -1545,17 +1559,17 @@ void prepare_characters_for_drawing() {
 		                 // adjust the Y positioning for the character's Z co-ord
 		                 - data_to_game_coord(chin->z);
 
-		_GP(charcache)[aa].scaling = zoom_level;
+		_GP(charcache)[aa].zoom = zoom_level;
 		_GP(charcache)[aa].sppic = specialpic;
-		_GP(charcache)[aa].tintredwas = tint_red;
-		_GP(charcache)[aa].tintgrnwas = tint_green;
-		_GP(charcache)[aa].tintbluwas = tint_blue;
-		_GP(charcache)[aa].tintamntwas = tint_amount;
-		_GP(charcache)[aa].tintlightwas = tint_light;
-		_GP(charcache)[aa].lightlevwas = light_level;
+		_GP(charcache)[aa].tintr = tint_red;
+		_GP(charcache)[aa].tintg = tint_green;
+		_GP(charcache)[aa].tintb = tint_blue;
+		_GP(charcache)[aa].tintamnt = tint_amount;
+		_GP(charcache)[aa].tintlight = tint_light;
+		_GP(charcache)[aa].lightlev = light_level;
 
 		// If cache needs to be re-drawn
-		if (!_GP(charcache)[aa].inUse) {
+		if (!_GP(charcache)[aa].in_use) {
 
 			// create the base sprite in _GP(actsps)[useindx], which will
 			// be scaled and/or flipped, as appropriate
@@ -1587,7 +1601,7 @@ void prepare_characters_for_drawing() {
 			}
 
 			// update the character cache with the new image
-			_GP(charcache)[aa].inUse = 1;
+			_GP(charcache)[aa].in_use = true;
 			_GP(charcache)[aa].image = recycle_bitmap(_GP(charcache)[aa].image, coldept, actsp.Bmp->GetWidth(), actsp.Bmp->GetHeight());
 			_GP(charcache)[aa].image->Blit(actsp.Bmp.get(), 0, 0);
 
diff --git a/engines/ags/engine/ac/draw.h b/engines/ags/engine/ac/draw.h
index 599ea9e4b38..c0bd6390e9f 100644
--- a/engines/ags/engine/ac/draw.h
+++ b/engines/ags/engine/ac/draw.h
@@ -84,6 +84,18 @@ struct ObjTexture {
 	ObjTexture &operator =(ObjTexture &&o);
 };
 
+// ObjectCache stores cached object data, used to determine
+// if active sprite / texture should be reconstructed
+struct ObjectCache {
+	Shared::Bitmap *image = nullptr;
+	bool  in_use = false;
+	int   sppic = 0;
+	short tintr = 0, tintg = 0, tintb = 0, tintamnt = 0, tintlight = 0;
+	short lightlev = 0, zoom = 0;
+	bool  mirrored = 0;
+	int   x = 0, y = 0;
+};
+
 // Converts AGS color index to the actual bitmap color using game's color depth
 int MakeColor(int color_index);
 
@@ -116,8 +128,10 @@ void on_roomviewport_changed(Viewport *view);
 void detect_roomviewport_overlaps(size_t z_index);
 // Updates drawing settings if room camera's size has changed
 void on_roomcamera_changed(Camera *cam);
-// Marks particular object as need too update the texture
+// Marks particular object as need to update the texture
 void mark_object_changed(int objid);
+// Resets all object caches which reference this sprite
+void reset_objcache_for_sprite(int sprnum);
 
 // whether there are currently remnants of a DisplaySpeech
 void mark_screen_dirty();
diff --git a/engines/ags/engine/ac/drawing_surface.cpp b/engines/ags/engine/ac/drawing_surface.cpp
index 14355bccaaf..0484e87d09e 100644
--- a/engines/ags/engine/ac/drawing_surface.cpp
+++ b/engines/ags/engine/ac/drawing_surface.cpp
@@ -22,13 +22,11 @@
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/drawing_surface.h"
 #include "ags/shared/ac/common.h"
-#include "ags/engine/ac/character_cache.h"
 #include "ags/engine/ac/display.h"
 #include "ags/engine/ac/game.h"
 #include "ags/shared/ac/game_setup_struct.h"
 #include "ags/engine/ac/game_state.h"
 #include "ags/engine/ac/global_translation.h"
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/room_object.h"
 #include "ags/engine/ac/room_status.h"
 #include "ags/engine/ac/string.h"
diff --git a/engines/ags/engine/ac/dynamic_sprite.cpp b/engines/ags/engine/ac/dynamic_sprite.cpp
index de63ee39363..e6b3532bf17 100644
--- a/engines/ags/engine/ac/dynamic_sprite.cpp
+++ b/engines/ags/engine/ac/dynamic_sprite.cpp
@@ -22,7 +22,6 @@
 #include "ags/lib/std/math.h"
 #include "ags/engine/ac/dynamic_sprite.h"
 #include "ags/shared/ac/common.h"
-#include "ags/engine/ac/character_cache.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/game.h"
 #include "ags/shared/ac/game_setup_struct.h"
@@ -30,7 +29,6 @@
 #include "ags/engine/ac/global_dynamic_sprite.h"
 #include "ags/engine/ac/global_game.h"
 #include "ags/engine/ac/math.h"    // M_PI
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/path_helper.h"
 #include "ags/engine/ac/room_object.h"
 #include "ags/engine/ac/room_status.h"
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index ac08772fc9f..3fcb319b723 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -26,7 +26,6 @@
 #include "ags/engine/ac/audio_channel.h"
 #include "ags/engine/ac/button.h"
 #include "ags/engine/ac/character.h"
-#include "ags/engine/ac/character_cache.h"
 #include "ags/shared/ac/dialog_topic.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/dynamic_sprite.h"
@@ -46,7 +45,6 @@
 #include "ags/engine/ac/lip_sync.h"
 #include "ags/engine/ac/mouse.h"
 #include "ags/engine/ac/move_list.h"
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/overlay.h"
 #include "ags/engine/ac/path_helper.h"
 #include "ags/engine/ac/sys_events.h"
@@ -1342,19 +1340,9 @@ bool unserialize_audio_script_object(int index, const char *objectType, Stream *
 }
 
 void game_sprite_updated(int sprnum) {
-	// Check if this sprite is assigned to any game object, and update them if necessary
-	// room objects cache
-	if (_G(croom) != nullptr) {
-		for (size_t i = 0; i < (size_t)_G(croom)->numobj; ++i) {
-			if (_G(objs)[i].num == sprnum)
-				_G(objcache)[i].sppic = -1;
-		}
-	}
-	// character cache
-	for (size_t i = 0; i < (size_t)_GP(game).numcharacters; ++i) {
-		if (_GP(charcache)[i].sppic == sprnum)
-			_GP(charcache)[i].sppic = -1;
-	}
+	// character and object draw caches
+	reset_objcache_for_sprite(sprnum);
+
 	// gui backgrounds
 	for (size_t i = 0; i < (size_t)_GP(game).numgui; ++i) {
 		if (_GP(guis)[i].BgImage == sprnum) {
@@ -1376,21 +1364,15 @@ void game_sprite_updated(int sprnum) {
 }
 
 void game_sprite_deleted(int sprnum) {
-	// Check if this sprite is assigned to any game object, and update them if necessary
-	// room objects and their cache
+	// character and object draw caches
+	reset_objcache_for_sprite(sprnum);
+	// room object graphics
 	if (_G(croom) != nullptr) {
 		for (size_t i = 0; i < (size_t)_G(croom)->numobj; ++i) {
-			if (_G(objs)[i].num == sprnum) {
+			if (_G(objs)[i].num == sprnum)
 				_G(objs)[i].num = 0;
-				_G(objcache)[i].sppic = -1;
-			}
 		}
 	}
-	// character cache
-	for (size_t i = 0; i < (size_t)_GP(game).numcharacters; ++i) {
-		if (_GP(charcache)[i].sppic == sprnum)
-			_GP(charcache)[i].sppic = -1;
-	}
 	// gui backgrounds
 	for (size_t i = 0; i < (size_t)_GP(game).numgui; ++i) {
 		if (_GP(guis)[i].BgImage == sprnum) {
diff --git a/engines/ags/engine/ac/global_object.cpp b/engines/ags/engine/ac/global_object.cpp
index a7067fc827d..9c2eb1caf95 100644
--- a/engines/ags/engine/ac/global_object.cpp
+++ b/engines/ags/engine/ac/global_object.cpp
@@ -31,7 +31,6 @@
 #include "ags/engine/ac/global_character.h"
 #include "ags/engine/ac/global_translation.h"
 #include "ags/engine/ac/object.h"
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/properties.h"
 #include "ags/engine/ac/room_object.h"
 #include "ags/engine/ac/room_status.h"
diff --git a/engines/ags/engine/ac/object.cpp b/engines/ags/engine/ac/object.cpp
index e22ba59d883..cf731e84a10 100644
--- a/engines/ags/engine/ac/object.cpp
+++ b/engines/ags/engine/ac/object.cpp
@@ -27,7 +27,6 @@
 #include "ags/engine/ac/game_state.h"
 #include "ags/engine/ac/global_object.h"
 #include "ags/engine/ac/global_translation.h"
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/properties.h"
 #include "ags/engine/ac/room.h"
 #include "ags/engine/ac/room_status.h"
diff --git a/engines/ags/engine/ac/object_cache.h b/engines/ags/engine/ac/object_cache.h
deleted file mode 100644
index c0c09cc845d..00000000000
--- a/engines/ags/engine/ac/object_cache.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef AGS_ENGINE_AC_OBJECT_CACHE_H
-#define AGS_ENGINE_AC_OBJECT_CACHE_H
-
-namespace AGS3 {
-
-// stores cached object info
-struct ObjectCache {
-	Shared::Bitmap *image = nullptr;
-	int   sppic = 0;
-	short tintredwas = 0, tintgrnwas = 0, tintbluwas = 0;
-	short tintamntwas = 0, tintlightwas = 0;
-	short lightlevwas = 0, zoomWas = 0;
-	bool  mirroredWas = false;
-
-	// The following are used to determine if the character has moved
-	int   xwas = 0, ywas = 0;
-};
-
-} // namespace AGS3
-
-#endif
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 8554a637956..cb4be1cc741 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -23,7 +23,6 @@
 #include "ags/shared/util/string_utils.h" //strlwr()
 #include "ags/shared/ac/common.h"
 #include "ags/engine/ac/character.h"
-#include "ags/engine/ac/character_cache.h"
 #include "ags/engine/ac/character_extras.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/event.h"
@@ -38,7 +37,6 @@
 #include "ags/engine/ac/global_translation.h"
 #include "ags/engine/ac/move_list.h"
 #include "ags/engine/ac/mouse.h"
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/overlay.h"
 #include "ags/engine/ac/properties.h"
 #include "ags/engine/ac/region.h"
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 83add8905eb..7462af2625a 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -20,7 +20,6 @@
  */
 
 #include "ags/engine/ac/character.h"
-#include "ags/engine/ac/character_cache.h"
 #include "ags/engine/ac/dialog.h"
 #include "ags/engine/ac/display.h"
 #include "ags/engine/ac/draw.h"
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 2e78e61c113..20ea7e1420e 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -39,7 +39,6 @@
 #include "ags/engine/ac/global_game.h"
 #include "ags/engine/ac/gui.h"
 #include "ags/engine/ac/lip_sync.h"
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/path_helper.h"
 #include "ags/engine/ac/route_finder.h"
 #include "ags/engine/ac/sys_events.h"
diff --git a/engines/ags/engine/main/game_file.cpp b/engines/ags/engine/main/game_file.cpp
index a61aea73b05..adcd62429e9 100644
--- a/engines/ags/engine/main/game_file.cpp
+++ b/engines/ags/engine/main/game_file.cpp
@@ -25,7 +25,6 @@
 
 #include "ags/shared/ac/common.h"
 #include "ags/engine/ac/character.h"
-#include "ags/engine/ac/character_cache.h"
 #include "ags/shared/ac/dialog_topic.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/game.h"
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index 0abe5bf48ec..23ac78fb66b 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -38,7 +38,6 @@
 #include "ags/shared/gui/gui_textbox.h"
 #include "ags/shared/script/cc_common.h"
 #include "ags/shared/util/directory.h"
-#include "ags/engine/ac/character_cache.h"
 #include "ags/engine/ac/character_extras.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/draw_software.h"
@@ -47,7 +46,6 @@
 #include "ags/engine/ac/game_state.h"
 #include "ags/engine/ac/mouse.h"
 #include "ags/engine/ac/move_list.h"
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/room_status.h"
 #include "ags/engine/ac/route_finder_jps.h"
 #include "ags/engine/ac/screen_overlay.h"
@@ -238,7 +236,7 @@ Globals::Globals() {
 	_scrHotspot = new ScriptHotspot[MAX_ROOM_HOTSPOTS];
 	_scrRegion = new ScriptRegion[MAX_ROOM_REGIONS];
 	_scrInv = new ScriptInvItem[MAX_INV];
-	_charcache = new std::vector<CharacterCache>();
+	_charcache = new std::vector<ObjectCache>();
 	_objcache = new ObjectCache[MAX_ROOM_OBJECTS];
 	_charextra = new std::vector<CharacterExtras>();
 	_mls = new std::vector<MoveList>();
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 1919d4c2fee..0077f314ac8 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -752,7 +752,7 @@ public:
 	std::vector<ViewStruct> *_views;
 	// Cached character and object states, used to determine
 	// whether these require texture update
-	std::vector<CharacterCache> *_charcache;
+	std::vector<ObjectCache> *_charcache;
 	ObjectCache *_objcache;
 	std::vector<CharacterExtras> *_charextra;
 	std::vector<MoveList> *_mls;
diff --git a/engines/ags/plugins/ags_plugin.cpp b/engines/ags/plugins/ags_plugin.cpp
index 9f978835def..29ef42dab91 100644
--- a/engines/ags/plugins/ags_plugin.cpp
+++ b/engines/ags/plugins/ags_plugin.cpp
@@ -26,7 +26,6 @@
 #include "ags/plugins/core/core.h"
 #include "ags/shared/ac/common.h"
 #include "ags/shared/ac/view.h"
-#include "ags/engine/ac/character_cache.h"
 #include "ags/engine/ac/display.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/dynamic_sprite.h"
@@ -39,7 +38,6 @@
 #include "ags/shared/ac/keycode.h"
 #include "ags/engine/ac/mouse.h"
 #include "ags/engine/ac/move_list.h"
-#include "ags/engine/ac/object_cache.h"
 #include "ags/engine/ac/parser.h"
 #include "ags/engine/ac/path_helper.h"
 #include "ags/engine/ac/room_status.h"


Commit: 84a39254429a293bfd959a731d643c5ee407ba27
    https://github.com/scummvm/scummvm/commit/84a39254429a293bfd959a731d643c5ee407ba27
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:09-07:00

Commit Message:
AGS: Simplified the use of get_overlay_position()

>From upstream d673fb3822907fd01bf0e2c6f98bee04a8a5b17b

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


diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 2416d54973a..5e7d2fd5eb1 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -1876,9 +1876,8 @@ void draw_gui_and_overlays() {
 
 		over.ddb->SetStretch(over.scaleWidth, over.scaleHeight);
 		over.ddb->SetAlpha(GfxDef::LegacyTrans255ToAlpha255(over.transparency));
-		int tdxp, tdyp;
-		get_overlay_position(over, &tdxp, &tdyp);
-		add_to_sprite_list(over.ddb, tdxp, tdyp, over.zorder, false, -1);
+		Point pos = get_overlay_position(over);
+		add_to_sprite_list(over.ddb, pos.X, pos.Y, over.zorder, false);
 	}
 
 	// Add GUIs
diff --git a/engines/ags/engine/ac/overlay.cpp b/engines/ags/engine/ac/overlay.cpp
index 5ddb24a4f96..868809b7ba1 100644
--- a/engines/ags/engine/ac/overlay.cpp
+++ b/engines/ags/engine/ac/overlay.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "ags/lib/std/algorithm.h"
 #include "ags/engine/ac/overlay.h"
 #include "ags/shared/ac/common.h"
 #include "ags/shared/ac/view.h"
@@ -70,10 +71,8 @@ int Overlay_GetX(ScriptOverlay *scover) {
 	if (ovri < 0)
 		quit("!invalid overlay ID specified");
 
-	int tdxp, tdyp;
-	get_overlay_position(_GP(screenover)[ovri], &tdxp, &tdyp);
-
-	return game_to_data_coord(tdxp);
+	Point pos = get_overlay_position(_GP(screenover)[ovri]);
+	return game_to_data_coord(pos.X);
 }
 
 void Overlay_SetX(ScriptOverlay *scover, int newx) {
@@ -89,10 +88,8 @@ int Overlay_GetY(ScriptOverlay *scover) {
 	if (ovri < 0)
 		quit("!invalid overlay ID specified");
 
-	int tdxp, tdyp;
-	get_overlay_position(_GP(screenover)[ovri], &tdxp, &tdyp);
-
-	return game_to_data_coord(tdyp);
+	Point pos = get_overlay_position(_GP(screenover)[ovri]);
+	return game_to_data_coord(pos.Y);
 }
 
 void Overlay_SetY(ScriptOverlay *scover, int newy) {
@@ -347,11 +344,9 @@ size_t add_screen_overlay(int x, int y, int type, Shared::Bitmap *piccy, int pic
 	return _GP(screenover).size() - 1;
 }
 
-void get_overlay_position(const ScreenOverlay &over, int *x, int *y) {
-	int tdxp, tdyp;
-	const Rect &ui_view = _GP(play).GetUIViewport();
-
+Point get_overlay_position(const ScreenOverlay &over) {
 	if (over.x == OVR_AUTOPLACE) {
+		const Rect &ui_view = _GP(play).GetUIViewport();
 		// auto place on character
 		int charid = over.y;
 
@@ -359,13 +354,12 @@ void get_overlay_position(const ScreenOverlay &over, int *x, int *y) {
 		const int charpic = _GP(views)[_GP(game).chars[charid].view].loops[_GP(game).chars[charid].loop].frames[0].pic;
 		const int height = (_GP(charextra)[charid].height < 1) ? _GP(game).SpriteInfos[charpic].Height : _GP(charextra)[charid].height;
 		Point screenpt = view->RoomToScreen(
-		                     data_to_game_coord(_GP(game).chars[charid].x),
-		                     data_to_game_coord(_GP(game).chars[charid].get_effective_y()) - height).first;
-		tdxp = screenpt.X - over.pic->GetWidth() / 2;
-		if (tdxp < 0) tdxp = 0;
-		tdyp = screenpt.Y - get_fixed_pixel_size(5);
+			data_to_game_coord(_GP(game).chars[charid].x),
+			data_to_game_coord(_GP(game).chars[charid].get_effective_y()) - height).first;
+		int tdxp = std::max(0, screenpt.X - over.pic->GetWidth() / 2);
+		int tdyp = screenpt.Y - get_fixed_pixel_size(5);
 		tdyp -= over.pic->GetHeight();
-		if (tdyp < 5) tdyp = 5;
+		tdyp = std::max(5, tdyp);
 
 		if ((tdxp + over.pic->GetWidth()) >= ui_view.GetWidth())
 			tdxp = (ui_view.GetWidth() - over.pic->GetWidth()) - 1;
@@ -373,20 +367,16 @@ void get_overlay_position(const ScreenOverlay &over, int *x, int *y) {
 			tdxp = ui_view.GetWidth() / 2 - over.pic->GetWidth() / 2;
 			tdyp = ui_view.GetHeight() / 2 - over.pic->GetHeight() / 2;
 		}
+		return Point(tdxp, tdyp);
 	} else {
 		// Note: the internal offset is only needed when x,y coordinates are specified
 		// and only in the case where the overlay is using a GUI. See issue #1098
-		tdxp = over.x + over.offsetX;
-		tdyp = over.y + over.offsetY;
-
-		if (!over.positionRelativeToScreen) {
-			Point tdxy = _GP(play).RoomToScreen(tdxp, tdyp);
-			tdxp = tdxy.X;
-			tdyp = tdxy.Y;
-		}
+		int tdxp = over.x + over.offsetX;
+		int tdyp = over.y + over.offsetY;
+		if (over.positionRelativeToScreen)
+			return Point(tdxp, tdyp);
+		return _GP(play).RoomToScreen(tdxp, tdyp);
 	}
-	*x = tdxp;
-	*y = tdyp;
 }
 
 void recreate_overlay_ddbs() {
diff --git a/engines/ags/engine/ac/overlay.h b/engines/ags/engine/ac/overlay.h
index bd7f07bb72d..40c640eab09 100644
--- a/engines/ags/engine/ac/overlay.h
+++ b/engines/ags/engine/ac/overlay.h
@@ -22,6 +22,7 @@
 #ifndef AGS_ENGINE_AC_OVERLAY_H
 #define AGS_ENGINE_AC_OVERLAY_H
 
+#include "ags/shared/util/geometry.h"
 #include "ags/engine/ac/screen_overlay.h"
 #include "ags/engine/ac/dynobj/script_overlay.h"
 
@@ -48,7 +49,7 @@ ScriptOverlay *Overlay_CreateTextual(int x, int y, int width, int font, int colo
 int  find_overlay_of_type(int type);
 void remove_screen_overlay(int type);
 // Calculates overlay position in screen coordinates
-void get_overlay_position(const ScreenOverlay &over, int *x, int *y);
+Point get_overlay_position(const ScreenOverlay &over);
 size_t add_screen_overlay(int x, int y, int type, Shared::Bitmap *piccy, bool alphaChannel = false);
 size_t  add_screen_overlay(int x, int y, int type, Shared::Bitmap *piccy, int pic_offx, int pic_offy, bool alphaChannel = false);
 void remove_screen_overlay_index(size_t over_idx);


Commit: b1649597d7bc5243e3b4fcd425551e2e24ed71a4
    https://github.com/scummvm/scummvm/commit/b1649597d7bc5243e3b4fcd425551e2e24ed71a4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:10-07:00

Commit Message:
AGS: Replaced Math/std Min/Max with ScummVM macros

Inspired by 6dee5ee6f271c600e30879d892295ea6069e8cc9

Changed paths:
    engines/ags/engine/ac/display.cpp
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/file.cpp
    engines/ags/engine/ac/inv_window.cpp
    engines/ags/engine/ac/overlay.cpp
    engines/ags/engine/ac/route_finder_impl.cpp
    engines/ags/engine/ac/string.cpp
    engines/ags/engine/ac/walk_behind.cpp
    engines/ags/engine/device/mouse_w32.cpp
    engines/ags/engine/game/savegame.cpp
    engines/ags/engine/gfx/ali_3d_scummvm.cpp
    engines/ags/engine/main/engine_setup.cpp
    engines/ags/engine/main/graphics_mode.cpp
    engines/ags/engine/platform/base/ags_platform_driver.cpp
    engines/ags/engine/script/cc_instance.cpp
    engines/ags/engine/script/script_api.cpp
    engines/ags/shared/ac/game_setup_struct.cpp
    engines/ags/shared/ac/sprite_file.cpp
    engines/ags/shared/ac/words_dictionary.cpp
    engines/ags/shared/font/fonts.cpp
    engines/ags/shared/game/interactions.cpp
    engines/ags/shared/game/main_game_file.cpp
    engines/ags/shared/gui/gui_label.cpp
    engines/ags/shared/gui/gui_listbox.cpp
    engines/ags/shared/gui/gui_slider.cpp
    engines/ags/shared/util/aligned_stream.cpp
    engines/ags/shared/util/buffered_stream.cpp
    engines/ags/shared/util/geometry.cpp
    engines/ags/shared/util/memory.h
    engines/ags/shared/util/memory_stream.cpp
    engines/ags/shared/util/string.cpp
    engines/ags/shared/util/string_utils.cpp
    engines/ags/shared/util/text_stream_reader.cpp


diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 442044ba925..43365732cf6 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -174,8 +174,8 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 	if (disp_type < DISPLAYTEXT_NORMALOVERLAY)
 		remove_screen_overlay(_GP(play).text_overlay_on); // remove any previous blocking texts
 
-	const int bmp_width = std::max(2, wii);
-	const int bmp_height = std::max(2, disp.fulltxtheight + extraHeight);
+	const int bmp_width = MAX(2, wii);
+	const int bmp_height = MAX(2, disp.fulltxtheight + extraHeight);
 	Bitmap *text_window_ds = BitmapHelper::CreateTransparentBitmap(
 		bmp_width, bmp_height, _GP(game).GetColorDepth());
 
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 5e7d2fd5eb1..23af33e5e95 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -299,8 +299,8 @@ AGS_INLINE void ctx_data_to_game_coord(int &x, int &y, bool hires_ctx) {
 
 AGS_INLINE void ctx_data_to_game_size(int &w, int &h, bool hires_ctx) {
 	if (hires_ctx && !_GP(game).IsLegacyHiRes()) {
-		w = Math::Max(1, (w / HIRES_COORD_MULTIPLIER));
-		h = Math::Max(1, (h / HIRES_COORD_MULTIPLIER));
+		w = MAX(1, (w / HIRES_COORD_MULTIPLIER));
+		h = MAX(1, (h / HIRES_COORD_MULTIPLIER));
 	} else if (!hires_ctx && _GP(game).IsLegacyHiRes()) {
 		w *= HIRES_COORD_MULTIPLIER;
 		h *= HIRES_COORD_MULTIPLIER;
@@ -309,7 +309,7 @@ AGS_INLINE void ctx_data_to_game_size(int &w, int &h, bool hires_ctx) {
 
 AGS_INLINE int ctx_data_to_game_size(int size, bool hires_ctx) {
 	if (hires_ctx && !_GP(game).IsLegacyHiRes())
-		return Math::Max(1, (size / HIRES_COORD_MULTIPLIER));
+		return MAX(1, (size / HIRES_COORD_MULTIPLIER));
 	if (!hires_ctx && _GP(game).IsLegacyHiRes())
 		return size * HIRES_COORD_MULTIPLIER;
 	return size;
@@ -319,7 +319,7 @@ AGS_INLINE int game_to_ctx_data_size(int size, bool hires_ctx) {
 	if (hires_ctx && !_GP(game).IsLegacyHiRes())
 		return size * HIRES_COORD_MULTIPLIER;
 	else if (!hires_ctx && _GP(game).IsLegacyHiRes())
-		return Math::Max(1, (size / HIRES_COORD_MULTIPLIER));
+		return MAX(1, (size / HIRES_COORD_MULTIPLIER));
 	return size;
 }
 
diff --git a/engines/ags/engine/ac/file.cpp b/engines/ags/engine/ac/file.cpp
index 3fcf581d0b2..306386f9028 100644
--- a/engines/ags/engine/ac/file.cpp
+++ b/engines/ags/engine/ac/file.cpp
@@ -466,7 +466,7 @@ static int ags_pf_ungetc(int /*c*/, void * /*userdata*/) {
 static long ags_pf_fread(void *p, long n, void *userdata) {
 	AGS_PACKFILE_OBJ *obj = (AGS_PACKFILE_OBJ *)userdata;
 	if (obj->remains > 0) {
-		size_t read = Math::Min(obj->remains, (size_t)n);
+		size_t read = MIN(obj->remains, (size_t)n);
 		obj->remains -= read;
 		return obj->stream->Read(p, read);
 	}
diff --git a/engines/ags/engine/ac/inv_window.cpp b/engines/ags/engine/ac/inv_window.cpp
index c383fc5751e..d7fd0ea1a12 100644
--- a/engines/ags/engine/ac/inv_window.cpp
+++ b/engines/ags/engine/ac/inv_window.cpp
@@ -303,7 +303,7 @@ void InventoryScreen::Draw(Bitmap *ds) {
 		wputblock(ds, barxp + 1 + ((i - top_item) % 4) * widest + widest / 2 - spof->GetWidth() / 2,
 		          bartop + 1 + ((i - top_item) / 4) * highest + highest / 2 - spof->GetHeight() / 2, spof, 1);
 	}
-#define BUTTONWID Math::Max(1, _GP(game).SpriteInfos[btn_select_sprite].Width)
+#define BUTTONWID MAX(1, _GP(game).SpriteInfos[btn_select_sprite].Width)
 	// Draw select, look and OK buttons
 	wputblock(ds, 2, buttonyp + get_fixed_pixel_size(2), _GP(spriteset)[btn_look_sprite], 1);
 	wputblock(ds, 3 + BUTTONWID, buttonyp + get_fixed_pixel_size(2), _GP(spriteset)[btn_select_sprite], 1);
diff --git a/engines/ags/engine/ac/overlay.cpp b/engines/ags/engine/ac/overlay.cpp
index 868809b7ba1..afeb63445fb 100644
--- a/engines/ags/engine/ac/overlay.cpp
+++ b/engines/ags/engine/ac/overlay.cpp
@@ -356,10 +356,10 @@ Point get_overlay_position(const ScreenOverlay &over) {
 		Point screenpt = view->RoomToScreen(
 			data_to_game_coord(_GP(game).chars[charid].x),
 			data_to_game_coord(_GP(game).chars[charid].get_effective_y()) - height).first;
-		int tdxp = std::max(0, screenpt.X - over.pic->GetWidth() / 2);
+		int tdxp = MAX(0, screenpt.X - over.pic->GetWidth() / 2);
 		int tdyp = screenpt.Y - get_fixed_pixel_size(5);
 		tdyp -= over.pic->GetHeight();
-		tdyp = std::max(5, tdyp);
+		tdyp = MAX(5, tdyp);
 
 		if ((tdxp + over.pic->GetWidth()) >= ui_view.GetWidth())
 			tdxp = (ui_view.GetWidth() - over.pic->GetWidth()) - 1;
diff --git a/engines/ags/engine/ac/route_finder_impl.cpp b/engines/ags/engine/ac/route_finder_impl.cpp
index faea6490527..27c29ed0f89 100644
--- a/engines/ags/engine/ac/route_finder_impl.cpp
+++ b/engines/ags/engine/ac/route_finder_impl.cpp
@@ -96,7 +96,7 @@ static int find_route_jps(int fromx, int fromy, int destx, int desty) {
 	_G(num_navpoints) = 0;
 
 	// new behavior: cut path if too complex rather than abort with error message
-	int count = std::min<int>((int)cpath.size(), MAXNAVPOINTS);
+	int count = MIN<int>((int)cpath.size(), MAXNAVPOINTS);
 
 	for (int i = 0; i < count; i++) {
 		int x, y;
diff --git a/engines/ags/engine/ac/string.cpp b/engines/ags/engine/ac/string.cpp
index a2b5c7fc6b6..5dbac47218d 100644
--- a/engines/ags/engine/ac/string.cpp
+++ b/engines/ags/engine/ac/string.cpp
@@ -114,7 +114,7 @@ const char *String_Substring(const char *thisString, int index, int length) {
 	size_t strlen = ustrlen(thisString);
 	if ((index < 0) || ((size_t)index > strlen))
 		quit("!String.Substring: invalid index");
-	size_t sublen = std::min((size_t)length, strlen - index);
+	size_t sublen = MIN((size_t)length, strlen - index);
 	size_t start = uoffset(thisString, index);
 	size_t end = uoffset(thisString + start, sublen) + start;
 	size_t copysz = end - start;
diff --git a/engines/ags/engine/ac/walk_behind.cpp b/engines/ags/engine/ac/walk_behind.cpp
index 62e5f73cdb3..9c64a1466de 100644
--- a/engines/ags/engine/ac/walk_behind.cpp
+++ b/engines/ags/engine/ac/walk_behind.cpp
@@ -104,7 +104,7 @@ bool walkbehinds_cropout(Bitmap *sprit, int sprx, int spry, int basel, int zoom)
 
 	bool pixels_changed = false;
 	// pass along the sprite's pixels, but skip those that lie outside the mask
-	for (int x = std::max(0, 0 - sprx);
+	for (int x = MAX(0, 0 - sprx);
 		(x < sprit->GetWidth()) && (x + sprx < _GP(thisroom).WalkBehindMask->GetWidth()); ++x) {
 		// select the WB column at this x
 		const auto &wbcol = walkBehindCols[x + sprx];
@@ -116,7 +116,7 @@ bool walkbehinds_cropout(Bitmap *sprit, int sprx, int spry, int basel, int zoom)
 
 		// ensure we only check within the valid areas (between Y1 and Y2)
 		// we assume that Y1 and Y2 are always within the mask
-		for (int y = std::max(0, wbcol.Y1 - spry);
+		for (int y = MAX(0, wbcol.Y1 - spry);
 				(y < sprit->GetHeight()) && (y + spry < wbcol.Y2); ++y) {
 			const int wb = _GP(thisroom).WalkBehindMask->GetScanLine(y + spry)[x + sprx];
 			if (wb < 1) continue; // "no area"
@@ -167,10 +167,10 @@ void walkbehinds_recalc() {
 				}
 				wbcol.Y2 = y + 1; // +1 to allow bottom line of screen to work (CHECKME??)
 				// resize the bounding rect
-				walkBehindAABB[wb].Left = std::min(col, walkBehindAABB[wb].Left);
-				walkBehindAABB[wb].Top = std::min(y, walkBehindAABB[wb].Top);
-				walkBehindAABB[wb].Right = std::max(col, walkBehindAABB[wb].Right);
-				walkBehindAABB[wb].Bottom = std::max(y, walkBehindAABB[wb].Bottom);
+				walkBehindAABB[wb].Left = MIN(col, walkBehindAABB[wb].Left);
+				walkBehindAABB[wb].Top = MIN(y, walkBehindAABB[wb].Top);
+				walkBehindAABB[wb].Right = MAX(col, walkBehindAABB[wb].Right);
+				walkBehindAABB[wb].Bottom = MAX(y, walkBehindAABB[wb].Bottom);
 			}
 		}
 	}
diff --git a/engines/ags/engine/device/mouse_w32.cpp b/engines/ags/engine/device/mouse_w32.cpp
index 229d93b0ab2..016a00a2cb3 100644
--- a/engines/ags/engine/device/mouse_w32.cpp
+++ b/engines/ags/engine/device/mouse_w32.cpp
@@ -160,7 +160,7 @@ float Mouse::GetSpeedUnit() {
 }
 
 void Mouse::SetSpeed(float speed) {
-	SpeedVal = Math::Max(0.f, speed);
+	SpeedVal = MAX(0.f, speed);
 	Speed = SpeedUnit * SpeedVal;
 }
 
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index e05bf43b694..b2dfc689ded 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -446,7 +446,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	RemapLegacySoundNums(_GP(game), _GP(views), _G(loaded_game_file_version));
 
 	// restore these to the ones retrieved from the save game
-	const size_t dynsurf_num = Math::Min((uint)MAX_DYNAMIC_SURFACES, r_data.DynamicSurfaces.size());
+	const size_t dynsurf_num = MIN((uint)MAX_DYNAMIC_SURFACES, r_data.DynamicSurfaces.size());
 	for (size_t i = 0; i < dynsurf_num; ++i) {
 		_G(dynamicallyCreatedSurfaces)[i] = r_data.DynamicSurfaces[i];
 	}
@@ -463,13 +463,13 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	// read the global data into the newly created script
 	if (r_data.GlobalScript.Data.get())
 		memcpy(_G(gameinst)->globaldata, r_data.GlobalScript.Data.get(),
-		       Math::Min((size_t)_G(gameinst)->globaldatasize, r_data.GlobalScript.Len));
+		       MIN((size_t)_G(gameinst)->globaldatasize, r_data.GlobalScript.Len));
 
 	// restore the script module data
 	for (size_t i = 0; i < _G(numScriptModules); ++i) {
 		if (r_data.ScriptModules[i].Data.get())
 			memcpy(_GP(moduleInst)[i]->globaldata, r_data.ScriptModules[i].Data.get(),
-			       Math::Min((size_t)_GP(moduleInst)[i]->globaldatasize, r_data.ScriptModules[i].Len));
+			       MIN((size_t)_GP(moduleInst)[i]->globaldatasize, r_data.ScriptModules[i].Len));
 	}
 
 	setup_player_character(_GP(game).playercharacter);
diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index 30bff013b8f..9a4edea27dd 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -199,9 +199,9 @@ void ScummVMRendererGraphicsDriver::SetGamma(int newGamma) {
 	uint16 gamma_blue[256];
 
 	for (int i = 0; i < 256; i++) {
-		gamma_red[i] = std::min(((int)_defaultGammaRed[i] * newGamma) / 100, 0xffff);
-		gamma_green[i] = std::min(((int)_defaultGammaGreen[i] * newGamma) / 100, 0xffff);
-		gamma_blue[i] = std::min(((int)_defaultGammaBlue[i] * newGamma) / 100, 0xffff);
+		gamma_red[i] = MIN(((int)_defaultGammaRed[i] * newGamma) / 100, 0xffff);
+		gamma_green[i] = MIN(((int)_defaultGammaGreen[i] * newGamma) / 100, 0xffff);
+		gamma_blue[i] = MIN(((int)_defaultGammaBlue[i] * newGamma) / 100, 0xffff);
 	}
 
 	SDL_SetWindowGammaRamp(sys_get_window(), gamma_red, gamma_green, gamma_blue);
diff --git a/engines/ags/engine/main/engine_setup.cpp b/engines/ags/engine/main/engine_setup.cpp
index eb65db27329..d458f98e96b 100644
--- a/engines/ags/engine/main/engine_setup.cpp
+++ b/engines/ags/engine/main/engine_setup.cpp
@@ -228,7 +228,7 @@ void engine_post_gfxmode_mouse_setup(const Size &init_desktop) {
 	if (_GP(usetup).mouse_speed_def == kMouseSpeed_CurrentDisplay) {
 		Size cur_desktop;
 		if (sys_get_desktop_resolution(cur_desktop.Width, cur_desktop.Height) == 0)
-			_GP(mouse).SetSpeedUnit(Math::Max((float)cur_desktop.Width / (float)init_desktop.Width,
+			_GP(mouse).SetSpeedUnit(MAX((float)cur_desktop.Width / (float)init_desktop.Width,
 			                                  (float)cur_desktop.Height / (float)init_desktop.Height));
 	}
 
diff --git a/engines/ags/engine/main/graphics_mode.cpp b/engines/ags/engine/main/graphics_mode.cpp
index 31d636f97d0..98adb998923 100644
--- a/engines/ags/engine/main/graphics_mode.cpp
+++ b/engines/ags/engine/main/graphics_mode.cpp
@@ -169,8 +169,8 @@ Size get_game_frame_from_screen_size(const Size &game_size, const Size screen_si
 		if (scale > 0)
 			fp_scale = convert_scaling_to_fp(scale);
 		else
-			fp_scale = Math::Max<int32_t>(kUnit,
-				Math::Min((screen_size.Width / game_size.Width) << kShift,
+			fp_scale = MAX<int32_t>(kUnit,
+				MIN((screen_size.Width / game_size.Width) << kShift,
 				(screen_size.Height / game_size.Height) << kShift));
 		Size frame_size = Size(
 			(game_size.Width * fp_scale) >> kShift,
@@ -222,8 +222,8 @@ bool try_init_compatible_mode(const DisplayMode &dm) {
 	// Windowed mode
 	if (dm.IsWindowed()) {
 		// If windowed mode, make the resolution stay in the generally supported limits
-		dm_compat.Width = Math::Min(dm_compat.Width, device_size.Width);
-		dm_compat.Height = Math::Min(dm_compat.Height, device_size.Height);
+		dm_compat.Width = MIN(dm_compat.Width, device_size.Width);
+		dm_compat.Height = MIN(dm_compat.Height, device_size.Height);
 	}
 	// Fullscreen mode
 	else {
diff --git a/engines/ags/engine/platform/base/ags_platform_driver.cpp b/engines/ags/engine/platform/base/ags_platform_driver.cpp
index 52fbf2312a2..7fc4158252a 100644
--- a/engines/ags/engine/platform/base/ags_platform_driver.cpp
+++ b/engines/ags/engine/platform/base/ags_platform_driver.cpp
@@ -258,7 +258,7 @@ void AGSPlatformDriver::Delay(int millis) {
 			break;
 		}
 
-		auto duration = std::min<std::chrono::milliseconds>(delayUntil - now,
+		auto duration = MIN<std::chrono::milliseconds>(delayUntil - now,
 		                _G(MaximumDelayBetweenPolling));
 		std::this_thread::sleep_for(duration);
 		now = AGS_Clock::now(); // update now
diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index 3a0217bca5e..ac564320227 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -335,7 +335,7 @@ int ccInstance::CallScriptFunction(const char *funcname, int32_t numargs, const
 	// Prepare instance for run
 	flags &= ~INSTF_ABORTED;
 	// Allow to pass less parameters if script callback has less declared args
-	numargs = std::min(numargs, export_args);
+	numargs = MIN(numargs, export_args);
 	// object pointer needs to start zeroed
 	registers[SREG_OP].SetDynamicObject(nullptr, nullptr);
 	registers[SREG_SP].SetStackPtr(&stack[0]);
diff --git a/engines/ags/engine/script/script_api.cpp b/engines/ags/engine/script/script_api.cpp
index 5943b5f351e..b5ba77d715d 100644
--- a/engines/ags/engine/script/script_api.cpp
+++ b/engines/ags/engine/script/script_api.cpp
@@ -223,7 +223,7 @@ const char *ScriptSprintf(char *buffer, size_t buf_length, const char *format,
 				arg_idx++;
 				if (snprintf_res >= 0) {
 					// snprintf returns maximal number of characters, so limit it with buffer size
-					out_ptr += Math::Min<ptrdiff_t>(snprintf_res, avail_outbuf);
+					out_ptr += MIN<ptrdiff_t>(snprintf_res, avail_outbuf);
 					continue;
 				}
 				// -- pass further to invalid format case
@@ -231,7 +231,7 @@ const char *ScriptSprintf(char *buffer, size_t buf_length, const char *format,
 
 			// If format was not valid, or there are no available
 			// parameters, just copy stored format buffer as it is
-			size_t copy_len = Math::Min(Math::Min<ptrdiff_t>(fmt_bufptr - fmtbuf, fmtbuf_size - 1), avail_outbuf);
+			size_t copy_len = MIN(MIN<ptrdiff_t>(fmt_bufptr - fmtbuf, fmtbuf_size - 1), avail_outbuf);
 			memcpy(out_ptr, fmtbuf, copy_len);
 			out_ptr += copy_len;
 		}
diff --git a/engines/ags/shared/ac/game_setup_struct.cpp b/engines/ags/shared/ac/game_setup_struct.cpp
index a34414c47f5..ad490677641 100644
--- a/engines/ags/shared/ac/game_setup_struct.cpp
+++ b/engines/ags/shared/ac/game_setup_struct.cpp
@@ -122,7 +122,7 @@ void GameSetupStruct::read_font_infos(Shared::Stream *in, GameDataVersion data_v
 		for (int i = 0; i < numfonts; ++i) {
 			fonts[i].YOffset = in->ReadInt32();
 			if (data_ver >= kGameVersion_341_2)
-				fonts[i].LineSpacing = Math::Max<int32_t>(0, in->ReadInt32());
+				fonts[i].LineSpacing = MAX<int32_t>(0, in->ReadInt32());
 		}
 	} else {
 		for (int i = 0; i < numfonts; ++i) {
@@ -130,7 +130,7 @@ void GameSetupStruct::read_font_infos(Shared::Stream *in, GameDataVersion data_v
 			fonts[i].Size = in->ReadInt32();
 			fonts[i].Outline = in->ReadInt32();
 			fonts[i].YOffset = in->ReadInt32();
-			fonts[i].LineSpacing = Math::Max<int32_t>(0, in->ReadInt32());
+			fonts[i].LineSpacing = MAX<int32_t>(0, in->ReadInt32());
 			AdjustFontInfoUsingFlags(fonts[i], flags);
 		}
 	}
diff --git a/engines/ags/shared/ac/sprite_file.cpp b/engines/ags/shared/ac/sprite_file.cpp
index 1f59d494148..63160effa38 100644
--- a/engines/ags/shared/ac/sprite_file.cpp
+++ b/engines/ags/shared/ac/sprite_file.cpp
@@ -325,7 +325,7 @@ static inline void ReadSprHeader(SpriteDatHeader &hdr, Stream *in,
 
 HError SpriteFile::RebuildSpriteIndex(Stream *in, sprkey_t topmost,
 		std::vector<Size> &metrics) {
-	topmost = std::min(topmost, (sprkey_t)_spriteData.size() - 1);
+	topmost = MIN(topmost, (sprkey_t)_spriteData.size() - 1);
 	for (sprkey_t i = 0; !in->EOS() && (i <= topmost); ++i) {
 		_spriteData[i].Offset = in->GetPosition();
 		SpriteDatHeader hdr;
diff --git a/engines/ags/shared/ac/words_dictionary.cpp b/engines/ags/shared/ac/words_dictionary.cpp
index 3bc5697c82e..3019bd096d9 100644
--- a/engines/ags/shared/ac/words_dictionary.cpp
+++ b/engines/ags/shared/ac/words_dictionary.cpp
@@ -110,7 +110,7 @@ void decrypt_text(char *toenc, size_t buf_sz) {
 
 void read_string_decrypt(Stream *in, char *buf, size_t buf_sz) {
 	size_t len = in->ReadInt32();
-	size_t slen = std::min(buf_sz - 1, len);
+	size_t slen = MIN(buf_sz - 1, len);
 	in->Read(buf, slen);
 	if (len > slen)
 		in->Seek(len - slen);
diff --git a/engines/ags/shared/font/fonts.cpp b/engines/ags/shared/font/fonts.cpp
index e4cf1fce898..de90e545718 100644
--- a/engines/ags/shared/font/fonts.cpp
+++ b/engines/ags/shared/font/fonts.cpp
@@ -164,7 +164,7 @@ int get_text_width_outlined(const char *text, size_t font_number) {
 		return self_width + 2 * _GP(fonts)[font_number].Info.AutoOutlineThickness;
 	}
 	int outline_width = _GP(fonts)[outline].Renderer->GetTextWidth(text, outline);
-	return std::max(self_width, outline_width);
+	return MAX(self_width, outline_width);
 }
 
 int get_font_outline(size_t font_number) {
@@ -209,7 +209,7 @@ int get_font_height_outlined(size_t fontNumber) {
 		return self_height + 2 * _GP(fonts)[fontNumber].Info.AutoOutlineThickness;
 	}
 	int outline_height = _GP(fonts)[outline].Metrics.CompatHeight;
-	return std::max(self_height, outline_height);
+	return MAX(self_height, outline_height);
 }
 
 int get_font_surface_height(size_t fontNumber) {
@@ -449,8 +449,8 @@ void alloc_font_outline_buffers(size_t font_number,
 		(f.TextStencil.GetWidth() < text_width) || (f.TextStencil.GetHeight() < text_height)) {
 		int sw = f.TextStencil.IsNull() ? 0 : f.TextStencil.GetWidth();
 		int sh = f.TextStencil.IsNull() ? 0 : f.TextStencil.GetHeight();
-		sw = std::max(text_width, sw);
-		sh = std::max(text_height, sh);
+		sw = MAX(text_width, sw);
+		sh = MAX(text_height, sh);
 		f.TextStencil.Create(sw, sh, color_depth);
 		f.OutlineStencil.Create(sw, sh + thick, color_depth);
 		f.TextStencilSub.CreateSubBitmap(&f.TextStencil, RectWH(Size(text_width, text_height)));
diff --git a/engines/ags/shared/game/interactions.cpp b/engines/ags/shared/game/interactions.cpp
index c4307e94567..237c9b44472 100644
--- a/engines/ags/shared/game/interactions.cpp
+++ b/engines/ags/shared/game/interactions.cpp
@@ -227,7 +227,7 @@ Interaction &Interaction::operator =(const Interaction &ni) {
 
 void Interaction::CopyTimesRun(const Interaction &inter) {
 	assert(Events.size() == inter.Events.size());
-	size_t count = Math::Min(Events.size(), inter.Events.size());
+	size_t count = MIN(Events.size(), inter.Events.size());
 	for (size_t i = 0; i < count; ++i) {
 		Events[i].TimesRun = inter.Events[i].TimesRun;
 	}
diff --git a/engines/ags/shared/game/main_game_file.cpp b/engines/ags/shared/game/main_game_file.cpp
index 7c4932c4970..77e11319957 100644
--- a/engines/ags/shared/game/main_game_file.cpp
+++ b/engines/ags/shared/game/main_game_file.cpp
@@ -356,7 +356,7 @@ void ReadDialogs(DialogTopic *&dialog,
 				break;
 			}
 
-			newlen = Math::Min(newlen, sizeof(buffer) - 1);
+			newlen = MIN(newlen, sizeof(buffer) - 1);
 			in->Read(buffer, newlen);
 			decrypt_text(buffer, newlen);
 			buffer[newlen] = 0;
diff --git a/engines/ags/shared/gui/gui_label.cpp b/engines/ags/shared/gui/gui_label.cpp
index da049f7b58d..02fc4801bf9 100644
--- a/engines/ags/shared/gui/gui_label.cpp
+++ b/engines/ags/shared/gui/gui_label.cpp
@@ -77,7 +77,7 @@ Rect GUILabel::CalcGraphicRect(bool clipped) {
 		++i, at_y += linespacing) {
 		Line lpos = GUI::CalcTextPositionHor(_GP(Lines)[i].GetCStr(), Font, 0, 0 + Width - 1, at_y,
 			(FrameAlignment)TextAlignment);
-		max_line.X2 = std::max(max_line.X2, lpos.X2);
+		max_line.X2 = MAX(max_line.X2, lpos.X2);
 	}
 	return SumRects(rc, RectWH(0, 0, max_line.X2 - max_line.X1 + 1, at_y - linespacing + get_font_surface_height(Font)));
 }
diff --git a/engines/ags/shared/gui/gui_listbox.cpp b/engines/ags/shared/gui/gui_listbox.cpp
index 3785b717a84..783afe5bb57 100644
--- a/engines/ags/shared/gui/gui_listbox.cpp
+++ b/engines/ags/shared/gui/gui_listbox.cpp
@@ -104,7 +104,7 @@ Rect GUIListBox::CalcGraphicRect(bool clipped) {
 		PrepareTextToDraw(Items[item_index]);
 		Line lpos = GUI::CalcTextPositionHor(_textToDraw.GetCStr(), Font, 1 + pixel_size, right_hand_edge, at_y + 1,
 			(FrameAlignment)TextAlignment);
-		max_line.X2 = std::max(max_line.X2, lpos.X2);
+		max_line.X2 = MAX(max_line.X2, lpos.X2);
 	}
 	return SumRects(rc, RectWH(0, 0, max_line.X2 - max_line.X1 + 1, Height));
 }
diff --git a/engines/ags/shared/gui/gui_slider.cpp b/engines/ags/shared/gui/gui_slider.cpp
index 1fcf6b491b1..4efe00e743c 100644
--- a/engines/ags/shared/gui/gui_slider.cpp
+++ b/engines/ags/shared/gui/gui_slider.cpp
@@ -69,10 +69,10 @@ Rect GUISlider::CalcGraphicRect(bool clipped) {
 	Rect bar = _cachedBar;
 	Rect handle = _cachedHandle;
 	return Rect(
-		std::min(std::min(logical.Left, bar.Left), handle.Left),
-		std::min(std::min(logical.Top, bar.Top), handle.Top),
-		std::max(std::max(logical.Right, bar.Right), handle.Right),
-		std::max(std::max(logical.Bottom, bar.Bottom), handle.Bottom)
+		MIN(MIN(logical.Left, bar.Left), handle.Left),
+		MIN(MIN(logical.Top, bar.Top), handle.Top),
+		MAX(MAX(logical.Right, bar.Right), handle.Right),
+		MAX(MAX(logical.Bottom, bar.Bottom), handle.Bottom)
 	);
 }
 
@@ -136,7 +136,7 @@ void GUISlider::UpdateMetrics() {
 
 	_cachedBar = bar;
 	_cachedHandle = handle;
-	_handleRange = std::max(1, handle_range);
+	_handleRange = MAX(1, handle_range);
 }
 
 void GUISlider::Draw(Bitmap *ds, int x, int y) {
diff --git a/engines/ags/shared/util/aligned_stream.cpp b/engines/ags/shared/util/aligned_stream.cpp
index 2fb7ef25494..873aba9d149 100644
--- a/engines/ags/shared/util/aligned_stream.cpp
+++ b/engines/ags/shared/util/aligned_stream.cpp
@@ -210,7 +210,7 @@ void AlignedStream::ReadPadding(size_t next_type) {
 			_block += next_type - pad;
 		}
 
-		_maxAlignment = Math::Max(_maxAlignment, next_type);
+		_maxAlignment = MAX(_maxAlignment, next_type);
 		// Data is evenly aligned now
 		if (_block % LargestPossibleType == 0) {
 			_block = 0;
@@ -233,7 +233,7 @@ void AlignedStream::WritePadding(size_t next_type) {
 			_block += next_type - pad;
 		}
 
-		_maxAlignment = Math::Max(_maxAlignment, next_type);
+		_maxAlignment = MAX(_maxAlignment, next_type);
 		// Data is evenly aligned now
 		if (_block % LargestPossibleType == 0) {
 			_block = 0;
diff --git a/engines/ags/shared/util/buffered_stream.cpp b/engines/ags/shared/util/buffered_stream.cpp
index 9470fc7ce80..a3d65a6bc11 100644
--- a/engines/ags/shared/util/buffered_stream.cpp
+++ b/engines/ags/shared/util/buffered_stream.cpp
@@ -85,7 +85,7 @@ size_t BufferedStream::Read(void *toBuffer, size_t toSize) {
 		soff_t bufferOffset = _position - _bufferPosition;
 		assert(bufferOffset >= 0);
 		size_t bytesLeft = _buffer.size() - (size_t)bufferOffset;
-		size_t chunkSize = std::min<size_t>(bytesLeft, toSize);
+		size_t chunkSize = MIN<size_t>(bytesLeft, toSize);
 
 		memcpy(to, _buffer.data() + bufferOffset, chunkSize);
 
@@ -133,7 +133,7 @@ bool BufferedStream::Seek(soff_t offset, StreamSeek origin) {
 	}
 
 	// clamp
-	_position = std::min(std::max(want_pos, (soff_t)_start), _end);
+	_position = MIN(MAX(want_pos, (soff_t)_start), _end);
 	return _position == want_pos;
 }
 
@@ -141,9 +141,9 @@ BufferedSectionStream::BufferedSectionStream(const String &file_name, soff_t sta
 	FileOpenMode open_mode, FileWorkMode work_mode, DataEndianess stream_endianess)
 	: BufferedStream(file_name, open_mode, work_mode, stream_endianess) {
 	assert(start_pos <= end_pos);
-	start_pos = std::min(start_pos, end_pos);
-	_start = std::min(start_pos, _end);
-	_end = std::min(end_pos, _end);
+	start_pos = MIN(start_pos, end_pos);
+	_start = MIN(start_pos, _end);
+	_end = MIN(end_pos, _end);
 	Seek(0, kSeekBegin);
 }
 
diff --git a/engines/ags/shared/util/geometry.cpp b/engines/ags/shared/util/geometry.cpp
index b0a88abee58..1b1b78530ce 100644
--- a/engines/ags/shared/util/geometry.cpp
+++ b/engines/ags/shared/util/geometry.cpp
@@ -38,13 +38,13 @@ bool IsRectInsideRect(const Rect &place, const Rect &item) {
 float DistanceBetween(const Rect &r1, const Rect &r2) {
 	// https://gamedev.stackexchange.com/a/154040
 	Rect rect_outer(
-	    std::min(r1.Left, r2.Left),
-	    std::min(r1.Top, r2.Top),
-	    std::max(r1.Right, r2.Right),
-	    std::max(r1.Bottom, r2.Bottom)
+	    MIN(r1.Left, r2.Left),
+	    MIN(r1.Top, r2.Top),
+	    MAX(r1.Right, r2.Right),
+	    MAX(r1.Bottom, r2.Bottom)
 	);
-	int inner_width = std::max(0, rect_outer.GetWidth() - r1.GetWidth() - r2.GetWidth());
-	int inner_height = std::max(0, rect_outer.GetHeight() - r1.GetHeight() - r2.GetHeight());
+	int inner_width = MAX(0, rect_outer.GetWidth() - r1.GetWidth() - r2.GetWidth());
+	int inner_height = MAX(0, rect_outer.GetHeight() - r1.GetHeight() - r2.GetHeight());
 	return static_cast<float>(std::sqrt((inner_width ^ 2) + (inner_height ^ 2)));
 }
 
@@ -121,13 +121,13 @@ Rect PlaceInRect(const Rect &place, const Rect &item, const RectPlacement &place
 }
 
 Rect SumRects(const Rect &r1, const Rect &r2) { // NOTE: remember that in AGS Y axis is pointed downwards (top < bottom)
-	return Rect(std::min(r1.Left, r2.Left), std::min(r1.Top, r2.Top),
-		std::max(r1.Right, r2.Right), std::max(r1.Bottom, r2.Bottom));
+	return Rect(MIN(r1.Left, r2.Left), MIN(r1.Top, r2.Top),
+		MAX(r1.Right, r2.Right), MAX(r1.Bottom, r2.Bottom));
 }
 
 Rect IntersectRects(const Rect &r1, const Rect &r2) { // NOTE: the result may be empty (negative) rect if there's no intersection
-	return Rect(std::max(r1.Left, r2.Left), std::max(r1.Top, r2.Top),
-		std::min(r1.Right, r2.Right), std::min(r1.Bottom, r2.Bottom));
+	return Rect(MAX(r1.Left, r2.Left), MAX(r1.Top, r2.Top),
+		MIN(r1.Right, r2.Right), MIN(r1.Bottom, r2.Bottom));
 }
 
 } // namespace AGS3
diff --git a/engines/ags/shared/util/memory.h b/engines/ags/shared/util/memory.h
index e36842fda92..6ce13f32c44 100644
--- a/engines/ags/shared/util/memory.h
+++ b/engines/ags/shared/util/memory.h
@@ -222,7 +222,7 @@ inline void BlockCopy(uint8_t *dst, const size_t dst_pitch, const size_t dst_off
                       const uint8_t *src, const size_t src_pitch, const size_t src_offset,
                       const size_t height) {
 	for (size_t y = 0; y < height; ++y, src += src_pitch, dst += dst_pitch)
-		memcpy(dst + dst_offset, src + src_offset, Math::Min(dst_pitch - dst_offset, src_pitch - src_offset));
+		memcpy(dst + dst_offset, src + src_offset, MIN(dst_pitch - dst_offset, src_pitch - src_offset));
 }
 
 } // namespace Memory
diff --git a/engines/ags/shared/util/memory_stream.cpp b/engines/ags/shared/util/memory_stream.cpp
index 7ac04914bc6..69bcd01f1bd 100644
--- a/engines/ags/shared/util/memory_stream.cpp
+++ b/engines/ags/shared/util/memory_stream.cpp
@@ -92,7 +92,7 @@ size_t MemoryStream::Read(void *buffer, size_t size) {
 	}
 	assert(_len > _pos);
 	size_t remain = _len - _pos;
-	size_t read_sz = std::min(remain, size);
+	size_t read_sz = MIN(remain, size);
 	memcpy(buffer, _cbuf + _pos, read_sz);
 	_pos += read_sz;
 	return read_sz;
@@ -117,8 +117,8 @@ bool MemoryStream::Seek(soff_t offset, StreamSeek origin) {
 	default:
 		return false;
 	}
-	_pos = static_cast<size_t>(std::max<soff_t>(0, pos));
-	_pos = static_cast<size_t>(std::min<soff_t>(_len, pos)); // clamp to EOS
+	_pos = static_cast<size_t>(MAX<soff_t>(0, pos));
+	_pos = static_cast<size_t>(MIN<soff_t>(_len, pos)); // clamp to EOS
 	return true;
 }
 
@@ -126,7 +126,7 @@ size_t MemoryStream::Write(const void *buffer, size_t size) {
 	if (_pos >= _buf_sz) {
 		return 0;
 	}
-	size = std::min(size, _buf_sz - _pos);
+	size = MIN(size, _buf_sz - _pos);
 	memcpy(_buf + _pos, buffer, size);
 	_pos += size;
 	_len += size;
diff --git a/engines/ags/shared/util/string.cpp b/engines/ags/shared/util/string.cpp
index e4f8ce64188..d3387806ed8 100644
--- a/engines/ags/shared/util/string.cpp
+++ b/engines/ags/shared/util/string.cpp
@@ -144,7 +144,7 @@ void String::Write(Stream *out) const {
 
 void String::WriteCount(Stream *out, size_t count) const {
 	if (out) {
-		size_t str_out_len = Math::Min(count - 1, _len);
+		size_t str_out_len = MIN(count - 1, _len);
 		if (str_out_len > 0)
 			out->Write(_cstr, str_out_len);
 		size_t null_out_len = count - str_out_len;
@@ -173,27 +173,27 @@ int String::CompareLeftNoCase(const char *cstr, size_t count) const {
 
 int String::CompareMid(const char *cstr, size_t from, size_t count) const {
 	cstr = cstr ? cstr : "";
-	from = Math::Min(from, _len);
+	from = MIN(from, _len);
 	return strncmp(_cstr + from, cstr, count != NoIndex ? count : strlen(cstr));
 }
 
 int String::CompareMidNoCase(const char *cstr, size_t from, size_t count) const {
 	cstr = cstr ? cstr : "";
-	from = Math::Min(from, _len);
+	from = MIN(from, _len);
 	return ags_strnicmp(_cstr + from, cstr, count != NoIndex ? count : strlen(cstr));
 }
 
 int String::CompareRight(const char *cstr, size_t count) const {
 	cstr = cstr ? cstr : "";
 	count = count != NoIndex ? count : strlen(cstr);
-	size_t off = Math::Min(_len, count);
+	size_t off = MIN(_len, count);
 	return strncmp(_cstr + _len - off, cstr, count);
 }
 
 int String::CompareRightNoCase(const char *cstr, size_t count) const {
 	cstr = cstr ? cstr : "";
 	count = count != NoIndex ? count : strlen(cstr);
-	size_t off = Math::Min(_len, count);
+	size_t off = MIN(_len, count);
 	return ags_strnicmp(_cstr + _len - off, cstr, count);
 }
 
@@ -210,7 +210,7 @@ size_t String::FindCharReverse(char c, size_t from) const {
 		return NoIndex;
 	}
 
-	from = Math::Min(from, _len - 1);
+	from = MIN(from, _len - 1);
 	const char *seek_ptr = _cstr + from;
 	while (seek_ptr >= _cstr) {
 		if (*seek_ptr == c) {
@@ -327,7 +327,7 @@ String String::Upper() const {
 }
 
 String String::Left(size_t count) const {
-	count = Math::Min(count, _len);
+	count = MIN(count, _len);
 	return count == _len ? *this : String(_cstr, count);
 }
 
@@ -337,7 +337,7 @@ String String::Mid(size_t from, size_t count) const {
 }
 
 String String::Right(size_t count) const {
-	count = Math::Min(count, _len);
+	count = MIN(count, _len);
 	return count == _len ? *this : String(_cstr + _len - count, count);
 }
 
@@ -400,7 +400,7 @@ void String::Reserve(size_t max_length) {
 		if (max_length > _bufHead->Capacity) {
 			// grow by 50%
 			size_t grow_length = _bufHead->Capacity + (_bufHead->Capacity / 2);
-			Copy(Math::Max(max_length, grow_length));
+			Copy(MAX(max_length, grow_length));
 		}
 	} else {
 		Create(max_length);
@@ -469,7 +469,7 @@ void String::AppendFmtv(const char *fcstr, va_list argptr) {
 
 void String::ClipLeft(size_t count) {
 	if ((_len != 0) && (count > 0)) {
-		count = Math::Min(count, _len);
+		count = MIN(count, _len);
 		BecomeUnique();
 		_len -= count;
 		_cstr += count;
@@ -478,7 +478,7 @@ void String::ClipLeft(size_t count) {
 
 void String::ClipMid(size_t from, size_t count) {
 	if (from < _len) {
-		count = Math::Min(count, _len - from);
+		count = MIN(count, _len - from);
 		if (count > 0) {
 			BecomeUnique();
 			if (!from) {
@@ -498,7 +498,7 @@ void String::ClipMid(size_t from, size_t count) {
 
 void String::ClipRight(size_t count) {
 	if (_len > 0 && count > 0) {
-		count = Math::Min(count, _len);
+		count = MIN(count, _len);
 		BecomeUnique();
 		_len -= count;
 		_cstr[_len] = 0;
@@ -745,7 +745,7 @@ void String::SetAt(size_t index, char c) {
 
 void String::SetString(const char *cstr, size_t length) {
 	if (cstr) {
-		length = Math::Min(length, strlen(cstr));
+		length = MIN(length, strlen(cstr));
 		if (length > 0) {
 			ReserveAndShift(false, Math::Surplus(length, _len));
 			memcpy(_cstr, cstr, length);
@@ -820,7 +820,7 @@ void String::TrimRight(char c) {
 
 void String::TruncateToLeft(size_t count) {
 	if (_len != 0) {
-		count = Math::Min(count, _len);
+		count = MIN(count, _len);
 		if (count < _len) {
 			BecomeUnique();
 			_len = count;
@@ -843,7 +843,7 @@ void String::TruncateToMid(size_t from, size_t count) {
 
 void String::TruncateToRight(size_t count) {
 	if (_len != 0) {
-		count = Math::Min(count, _len);
+		count = MIN(count, _len);
 		if (count < _len) {
 			BecomeUnique();
 			_cstr += _len - count;
@@ -939,7 +939,7 @@ void String::Copy(size_t max_length, size_t offset) {
 	char *new_data = new char[sizeof(String::BufHeader) + max_length + 1];
 	// remember, that _cstr may point to any address in buffer
 	char *cstr_head = new_data + sizeof(String::BufHeader) + offset;
-	size_t copy_length = Math::Min(_len, max_length);
+	size_t copy_length = MIN(_len, max_length);
 	memcpy(cstr_head, _cstr, copy_length);
 	Free();
 	_buf = new_data;
@@ -974,7 +974,7 @@ void String::ReserveAndShift(bool left, size_t more_length) {
 		if (_bufHead->Capacity < total_length) { // not enough capacity - reallocate buffer
 			// grow by 50% or at least to total_size
 			size_t grow_length = _bufHead->Capacity + (_bufHead->Capacity >> 1);
-			Copy(Math::Max(total_length, grow_length), left ? more_length : 0u);
+			Copy(MAX(total_length, grow_length), left ? more_length : 0u);
 		} else if (_bufHead->RefCount > 1) { // is a shared string - clone buffer
 			Copy(total_length, left ? more_length : 0u);
 		} else {
diff --git a/engines/ags/shared/util/string_utils.cpp b/engines/ags/shared/util/string_utils.cpp
index 353e2579f9e..f5bf7773922 100644
--- a/engines/ags/shared/util/string_utils.cpp
+++ b/engines/ags/shared/util/string_utils.cpp
@@ -130,7 +130,7 @@ void StrUtil::ReadString(char *cstr, Stream *in, size_t buf_limit) {
 		return;
 	}
 
-	len = Math::Min(len, buf_limit - 1);
+	len = MIN(len, buf_limit - 1);
 	if (len > 0)
 		in->Read(cstr, len);
 	cstr[len] = 0;
diff --git a/engines/ags/shared/util/text_stream_reader.cpp b/engines/ags/shared/util/text_stream_reader.cpp
index 79d8f34dfe0..f4c6de4fdb0 100644
--- a/engines/ags/shared/util/text_stream_reader.cpp
+++ b/engines/ags/shared/util/text_stream_reader.cpp
@@ -85,10 +85,10 @@ String TextStreamReader::ReadLine() {
 		if (c < chars_read_last && *seek_ptr == '\n') {
 			line_break_position = seek_ptr - char_buffer;
 			if (str_len < max_chars) {
-				append_length = Math::Min(line_break_position, max_chars - str_len);
+				append_length = MIN(line_break_position, max_chars - str_len);
 			}
 		} else {
-			append_length = Math::Min(chars_read_last, max_chars - str_len);
+			append_length = MIN(chars_read_last, max_chars - str_len);
 		}
 
 		if (append_length > 0) {


Commit: 86c363a8dda160c0328a7907e8b2f244d0b5a535
    https://github.com/scummvm/scummvm/commit/86c363a8dda160c0328a7907e8b2f244d0b5a535
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:10-07:00

Commit Message:
AGS: Remove ScriptOverlay.hasInternalRef flag, add actual ref

>From update ef0655b601004983d13dab99092d708b080eef23

Changed paths:
    engines/ags/engine/ac/character.cpp
    engines/ags/engine/ac/dynobj/script_overlay.cpp
    engines/ags/engine/ac/dynobj/script_overlay.h
    engines/ags/engine/ac/overlay.cpp
    engines/ags/engine/ac/overlay.h
    engines/ags/engine/ac/screen_overlay.h


diff --git a/engines/ags/engine/ac/character.cpp b/engines/ags/engine/ac/character.cpp
index 7a6f6937dd1..467fe4baba2 100644
--- a/engines/ags/engine/ac/character.cpp
+++ b/engines/ags/engine/ac/character.cpp
@@ -706,9 +706,8 @@ ScriptOverlay *Character_SayBackground(CharacterInfo *chaa, const char *texx) {
 	if (ovri < 0)
 		quit("!SayBackground internal error: no overlay");
 
-	ScriptOverlay *scOver = create_scriptobj_for_overlay(_GP(screenover)[ovri]);
-	scOver->hasInternalRef = true; // keep at least until internal timeout
-	return scOver;
+	// Create script object with an internal ref, keep at least until internal timeout
+	return create_scriptobj_for_overlay(_GP(screenover)[ovri], true);
 }
 
 void Character_SetAsPlayer(CharacterInfo *chaa) {
diff --git a/engines/ags/engine/ac/dynobj/script_overlay.cpp b/engines/ags/engine/ac/dynobj/script_overlay.cpp
index 879cb35f1ba..184cfca563a 100644
--- a/engines/ags/engine/ac/dynobj/script_overlay.cpp
+++ b/engines/ags/engine/ac/dynobj/script_overlay.cpp
@@ -43,8 +43,8 @@ int ScriptOverlay::Dispose(const char *address, bool force) {
 
 	// if this is being removed voluntarily (ie. pointer out of
 	// scope) then remove the associateed overlay
-	// Otherwise, it's a Restre Game or something so don't
-	if ((!force) && (!hasInternalRef) && (Overlay_GetValid(this))) {
+	// Otherwise, it's a Restore Game or something so don't
+	if ((!force) && (Overlay_GetValid(this))) {
 		Remove();
 	}
 
@@ -64,14 +64,14 @@ void ScriptOverlay::Serialize(const char *address, Stream *out) {
 	out->WriteInt32(overlayId);
 	out->WriteInt32(0); // unused (was text window x padding)
 	out->WriteInt32(0); // unused (was text window y padding)
-	out->WriteInt32(hasInternalRef);
+	out->WriteInt32(0); // unused (was internal ref flag)
 }
 
 void ScriptOverlay::Unserialize(int index, Stream *in, size_t data_sz) {
 	overlayId = in->ReadInt32();
 	in->ReadInt32(); // unused (was text window x padding)
 	in->ReadInt32(); // unused (was text window y padding)
-	hasInternalRef = in->ReadInt32() != 0;
+	in->ReadInt32(); // unused (was internal ref flag)
 	ccRegisterUnserializedObject(index, this, this);
 }
 
diff --git a/engines/ags/engine/ac/dynobj/script_overlay.h b/engines/ags/engine/ac/dynobj/script_overlay.h
index b9af4f29cb6..5b29b3ccdea 100644
--- a/engines/ags/engine/ac/dynobj/script_overlay.h
+++ b/engines/ags/engine/ac/dynobj/script_overlay.h
@@ -28,9 +28,6 @@ namespace AGS3 {
 
 struct ScriptOverlay final : AGSCCDynamicObject {
 	int overlayId = -1;
-	// TODO: this flag is needed to mark an overlay which lifetime is managed
-	// by the engine; this may be solved with engine owning an object ref instead
-	bool hasInternalRef = false;
 
 	int Dispose(const char *address, bool force) override;
 	const char *GetType() override;
diff --git a/engines/ags/engine/ac/overlay.cpp b/engines/ags/engine/ac/overlay.cpp
index afeb63445fb..442acf0ab33 100644
--- a/engines/ags/engine/ac/overlay.cpp
+++ b/engines/ags/engine/ac/overlay.cpp
@@ -222,19 +222,14 @@ void Overlay_SetZOrder(ScriptOverlay *scover, int zorder) {
 //=============================================================================
 
 // Creates and registers a managed script object for existing overlay object
-ScriptOverlay *create_scriptobj_for_overlay(ScreenOverlay &over) {
+// optionally adds an internal engine reference to prevent object's disposal
+ScriptOverlay *create_scriptobj_for_overlay(ScreenOverlay &over, bool internal_ref) {
 	ScriptOverlay *scover = new ScriptOverlay();
 	scover->overlayId = over.type;
 	int handl = ccRegisterManagedObject(scover, scover);
 	over.associatedOverlayHandle = handl;
-	return scover;
-}
-
-// Creates managed script object for overlay and adds internal engine's reference to it,
-// so that it does not get disposed even if there are no user references in script.
-static ScriptOverlay *create_scriptobj_addref(ScreenOverlay &over) {
-	ScriptOverlay *scover = create_scriptobj_for_overlay(over);
-	ccAddObjectReference(over.associatedOverlayHandle);
+	if (internal_ref)
+		ccAddObjectReference(handl);
 	return scover;
 }
 
@@ -270,7 +265,10 @@ void remove_screen_overlay_index(size_t over_idx) {
 		if (_GP(play).speech_face_scover)
 			invalidate_and_subref(over, _GP(play).speech_face_scover);
 		_G(face_talking) = -1;
+	} else if (over.bgSpeechForChar > 0) { // release internal ref for bg speech
+		ccReleaseObjectReference(over.associatedOverlayHandle);
 	}
+
 	dispose_overlay(over);
 	_GP(screenover).erase(_GP(screenover).begin() + over_idx);
 	// if an overlay before the sierra-style speech one is removed, update the index
@@ -334,9 +332,9 @@ size_t add_screen_overlay(int x, int y, int type, Shared::Bitmap *piccy, int pic
 		// only make script object for blocking speech now, because messagebox blocks all script
 		// and therefore cannot be accessed, so no practical reason for that atm
 		if (type == OVER_TEXTSPEECH)
-			_GP(play).speech_text_scover = create_scriptobj_addref(over);
+			_GP(play).speech_text_scover = create_scriptobj_for_overlay(over, true);
 	} else if (type == OVER_PICTURE) {
-		_GP(play).speech_face_scover = create_scriptobj_addref(over);
+		_GP(play).speech_face_scover = create_scriptobj_for_overlay(over, true);
 	}
 
 	over.MarkChanged();
diff --git a/engines/ags/engine/ac/overlay.h b/engines/ags/engine/ac/overlay.h
index 40c640eab09..ee10201c249 100644
--- a/engines/ags/engine/ac/overlay.h
+++ b/engines/ags/engine/ac/overlay.h
@@ -53,8 +53,9 @@ Point get_overlay_position(const ScreenOverlay &over);
 size_t add_screen_overlay(int x, int y, int type, Shared::Bitmap *piccy, bool alphaChannel = false);
 size_t  add_screen_overlay(int x, int y, int type, Shared::Bitmap *piccy, int pic_offx, int pic_offy, bool alphaChannel = false);
 void remove_screen_overlay_index(size_t over_idx);
-// Creates and registers a managed script object for existing overlay object
-ScriptOverlay *create_scriptobj_for_overlay(ScreenOverlay &over);
+// Creates and registers a managed script object for existing overlay object;
+// optionally adds an internal engine reference to prevent object's disposal
+ScriptOverlay *create_scriptobj_for_overlay(ScreenOverlay &over, bool internal_ref = false);
 void recreate_overlay_ddbs();
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/screen_overlay.h b/engines/ags/engine/ac/screen_overlay.h
index 19214bcbe72..52e031832bd 100644
--- a/engines/ags/engine/ac/screen_overlay.h
+++ b/engines/ags/engine/ac/screen_overlay.h
@@ -65,7 +65,7 @@ struct ScreenOverlay {
 	// Width and height to stretch the texture to
 	int scaleWidth = 0, scaleHeight = 0;
 	int bgSpeechForChar = -1;
-	int associatedOverlayHandle = 0;
+	int associatedOverlayHandle = 0; // script obj handle
 	int zorder = INT_MIN;
 	bool positionRelativeToScreen = false;
 	bool hasSerializedBitmap = false;


Commit: ec38c1c61cfa5b7dfc4a75ebcfa7689a9a2a5263
    https://github.com/scummvm/scummvm/commit/ec38c1c61cfa5b7dfc4a75ebcfa7689a9a2a5263
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:10-07:00

Commit Message:
AGS: Refactor core overlay creation funcs to return ScreenOverlay*

>From upstream f89b2e8e59da107fcbb7a75bf7e1aa293a45fb2b

Changed paths:
    engines/ags/engine/ac/display.cpp
    engines/ags/engine/ac/display.h
    engines/ags/engine/ac/global_overlay.cpp
    engines/ags/engine/ac/global_overlay.h
    engines/ags/engine/ac/overlay.cpp
    engines/ags/engine/ac/overlay.h
    engines/ags/shared/ac/sprite_cache.h


diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 43365732cf6..308a955701d 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -70,7 +70,7 @@ struct DisplayVars {
 // Pass yy = -1 to find Y co-ord automatically
 // allowShrink = 0 for none, 1 for leftwards, 2 for rightwards
 // pass blocking=2 to create permanent overlay
-int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int usingfont, int asspch, int isThought, int allowShrink, bool overlayPositionFixed) {
+ScreenOverlay *_display_main(int xx, int yy, int wii, const char *text, int disp_type, int usingfont, int asspch, int isThought, int allowShrink, bool overlayPositionFixed) {
 	const bool use_speech_textwindow = (asspch < 0) && (_GP(game).options[OPT_SPEECHTYPE] >= 2);
 	const bool use_thought_gui = (isThought) && (_GP(game).options[OPT_THOUGHTGUI] > 0);
 
@@ -252,11 +252,11 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 	default: ovrtype = disp_type; break; // must be precreated overlay id
 	}
 
-	int nse = add_screen_overlay(xx, yy, ovrtype, text_window_ds, adjustedXX - xx, adjustedYY - yy, alphaChannel);
+	size_t nse = add_screen_overlay(xx, yy, ovrtype, text_window_ds, adjustedXX - xx, adjustedYY - yy, alphaChannel);
 	// we should not delete text_window_ds here, because it is now owned by Overlay
 
 	if (disp_type >= DISPLAYTEXT_NORMALOVERLAY) {
-		return _GP(screenover)[nse].type;
+		return &_GP(screenover)[nse];
 	}
 
 	//
@@ -268,7 +268,7 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 			remove_screen_overlay(OVER_TEXTMSG);
 			_GP(play).SetWaitSkipResult(SKIP_AUTOTIMER);
 			_GP(play).messagetime = -1;
-			return 0;
+			return nullptr;
 		}
 
 		int countdown = GetTextDisplayTime(todis);
@@ -351,7 +351,7 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 	}
 
 	_GP(play).messagetime = -1;
-	return 0;
+	return nullptr;
 }
 
 void _display_at(int xx, int yy, int wii, const char *text, int disp_type, int asspch, int isThought, int allowShrink, bool overlayPositionFixed) {
diff --git a/engines/ags/engine/ac/display.h b/engines/ags/engine/ac/display.h
index 1a30cda060d..7047868c142 100644
--- a/engines/ags/engine/ac/display.h
+++ b/engines/ags/engine/ac/display.h
@@ -37,7 +37,12 @@ using AGS::Shared::GUIMain;
 #define DISPLAYTEXT_NORMALOVERLAY 2
 // also accepts explicit overlay ID >= OVER_CUSTOM
 
-int  _display_main(int xx, int yy, int wii, const char *text, int disp_type, int usingfont, int asspch, int isThought, int allowShrink, bool overlayPositionFixed);
+struct ScreenOverlay;
+// Creates a textual overlay using the given parameters;
+// Pass yy = -1 to find Y co-ord automatically
+// allowShrink = 0 for none, 1 for leftwards, 2 for rightwards
+// pass blocking=2 to create permanent overlay
+ScreenOverlay *_display_main(int xx, int yy, int wii, const char *text, int disp_type, int usingfont, int asspch, int isThought, int allowShrink, bool overlayPositionFixed);
 void _display_at(int xx, int yy, int wii, const char *text, int disp_type, int asspch, int isThought, int allowShrink, bool overlayPositionFixed);
 // Tests the given string for the voice-over tags and plays cue clip for the given character;
 // will assign replacement string, which will be blank string if game is in "voice-only" mode
diff --git a/engines/ags/engine/ac/global_overlay.cpp b/engines/ags/engine/ac/global_overlay.cpp
index c2c26a2cbbf..0b540623b34 100644
--- a/engines/ags/engine/ac/global_overlay.cpp
+++ b/engines/ags/engine/ac/global_overlay.cpp
@@ -21,19 +21,10 @@
 
 #include "ags/engine/ac/global_overlay.h"
 #include "ags/shared/ac/common.h"
-#include "ags/engine/ac/display.h"
 #include "ags/engine/ac/draw.h"
-#include "ags/shared/ac/game_setup_struct.h"
-#include "ags/engine/ac/game_state.h"
-#include "ags/engine/ac/global_translation.h"
 #include "ags/engine/ac/overlay.h"
 #include "ags/engine/ac/runtime_defines.h"
-#include "ags/engine/ac/screen_overlay.h"
-#include "ags/engine/ac/string.h"
-#include "ags/shared/ac/sprite_cache.h"
-#include "ags/engine/ac/system.h"
-#include "ags/shared/gfx/bitmap.h"
-#include "ags/shared/util/wgt2_allg.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
@@ -45,21 +36,9 @@ void RemoveOverlay(int ovrid) {
 	remove_screen_overlay(ovrid);
 }
 
-int CreateGraphicOverlay(int xx, int yy, int slott, int trans) {
-	data_to_game_coords(&xx, &yy);
-
-	Bitmap *screeno = BitmapHelper::CreateTransparentBitmap(_GP(game).SpriteInfos[slott].Width, _GP(game).SpriteInfos[slott].Height, _GP(game).GetColorDepth());
-	wputblock(screeno, 0, 0, _GP(spriteset)[slott], trans);
-	bool hasAlpha = (_GP(game).SpriteInfos[slott].Flags & SPF_ALPHACHANNEL) != 0;
-	int nse = add_screen_overlay(xx, yy, OVER_CUSTOM, screeno, hasAlpha);
-	return _GP(screenover)[nse].type;
-}
-
-int CreateTextOverlayCore(int xx, int yy, int wii, int fontid, int text_color, const char *text, int disp_type, int allowShrink) {
-	if (wii < 8) wii = _GP(play).GetUIViewport().GetWidth() / 2;
-	if (xx < 0) xx = _GP(play).GetUIViewport().GetWidth() / 2 - wii / 2;
-	if (text_color == 0) text_color = 16;
-	return _display_main(xx, yy, wii, text, disp_type, fontid, -text_color, 0, allowShrink, false);
+int CreateGraphicOverlay(int x, int y, int slot, int trans) {
+	auto *over = Overlay_CreateGraphicCore(x, y, slot, trans != 0);
+	return over ? over->type : 0;
 }
 
 int CreateTextOverlay(int xx, int yy, int wii, int fontid, int text_color, const char *text, int disp_type) {
@@ -71,7 +50,8 @@ int CreateTextOverlay(int xx, int yy, int wii, int fontid, int text_color, const
 	} else  // allow DisplaySpeechBackground to be shrunk
 		allowShrink = 1;
 
-	return CreateTextOverlayCore(xx, yy, wii, fontid, text_color, text, disp_type, allowShrink);
+	auto *over = Overlay_CreateTextCore(xx, yy, wii, fontid, text_color, text, disp_type, allowShrink);
+	return over ? over->type : 0;
 }
 
 void SetTextOverlay(int ovrid, int xx, int yy, int wii, int fontid, int text_color, const char *text) {
diff --git a/engines/ags/engine/ac/global_overlay.h b/engines/ags/engine/ac/global_overlay.h
index f77593b9ef8..5977631e738 100644
--- a/engines/ags/engine/ac/global_overlay.h
+++ b/engines/ags/engine/ac/global_overlay.h
@@ -24,9 +24,10 @@
 
 namespace AGS3 {
 
+struct ScreenOverlay;
+
 void RemoveOverlay(int ovrid);
 int  CreateGraphicOverlay(int xx, int yy, int slott, int trans);
-int  CreateTextOverlayCore(int xx, int yy, int wii, int fontid, int text_color, const char *text, int disp_type, int allowShrink);
 int  CreateTextOverlay(int xx, int yy, int wii, int fontid, int clr, const char *text, int disp_type);
 void SetTextOverlay(int ovrid, int xx, int yy, int wii, int fontid, int text_color, const char *text);
 void MoveOverlay(int ovrid, int newx, int newy);
diff --git a/engines/ags/engine/ac/overlay.cpp b/engines/ags/engine/ac/overlay.cpp
index 442acf0ab33..ad6a82d51b9 100644
--- a/engines/ags/engine/ac/overlay.cpp
+++ b/engines/ags/engine/ac/overlay.cpp
@@ -22,6 +22,7 @@
 #include "ags/lib/std/algorithm.h"
 #include "ags/engine/ac/overlay.h"
 #include "ags/shared/ac/common.h"
+#include "ags/shared/ac/sprite_cache.h"
 #include "ags/shared/ac/view.h"
 #include "ags/engine/ac/character.h"
 #include "ags/engine/ac/character_extras.h"
@@ -167,20 +168,36 @@ int Overlay_GetValid(ScriptOverlay *scover) {
 	return 1;
 }
 
+ScreenOverlay *Overlay_CreateGraphicCore(int x, int y, int slot, bool transparent) {
+	data_to_game_coords(&x, &y);
+	Bitmap *screeno = BitmapHelper::CreateTransparentBitmap(_GP(game).SpriteInfos[slot].Width, _GP(game).SpriteInfos[slot].Height, _GP(game).GetColorDepth());
+	screeno->Blit(_GP(spriteset)[slot], 0, 0, transparent ? kBitmap_Transparency : kBitmap_Copy);
+	size_t nse = add_screen_overlay(x, y, OVER_CUSTOM, screeno, (_GP(game).SpriteInfos[slot].Flags & SPF_ALPHACHANNEL) != 0);
+	return nse < SIZE_MAX ? &_GP(screenover)[nse] : nullptr;
+}
+
+ScreenOverlay *Overlay_CreateTextCore(int x, int y, int width, int font, int text_color,
+	const char *text, int disp_type, int allow_shrink) {
+	if (width < 8) width = _GP(play).GetUIViewport().GetWidth() / 2;
+	if (x < 0) x = _GP(play).GetUIViewport().GetWidth() / 2 - width / 2;
+	if (text_color == 0) text_color = 16;
+	return _display_main(x, y, width, text, disp_type, font, -text_color, 0, allow_shrink, false);
+}
+
 ScriptOverlay *Overlay_CreateGraphical(int x, int y, int slot, int transparent) {
+	auto *over = Overlay_CreateGraphicCore(x, y, slot, transparent != 0);
 	ScriptOverlay *sco = new ScriptOverlay();
-	sco->overlayId = CreateGraphicOverlay(x, y, slot, transparent);
+	sco->overlayId = over->type;
 	ccRegisterManagedObject(sco, sco);
 	return sco;
 }
 
 ScriptOverlay *Overlay_CreateTextual(int x, int y, int width, int font, int colour, const char *text) {
-	ScriptOverlay *sco = new ScriptOverlay();
-
 	data_to_game_coords(&x, &y);
 	width = data_to_game_coord(width);
-
-	sco->overlayId = CreateTextOverlayCore(x, y, width, font, colour, text, DISPLAYTEXT_NORMALOVERLAY, 0);
+	auto *over = Overlay_CreateTextCore(x, y, width, font, colour, text, DISPLAYTEXT_NORMALOVERLAY, 0);
+	ScriptOverlay *sco = new ScriptOverlay();
+	sco->overlayId = over->type;
 	ccRegisterManagedObject(sco, sco);
 	return sco;
 }
diff --git a/engines/ags/engine/ac/overlay.h b/engines/ags/engine/ac/overlay.h
index ee10201c249..9d653ceed68 100644
--- a/engines/ags/engine/ac/overlay.h
+++ b/engines/ags/engine/ac/overlay.h
@@ -45,6 +45,9 @@ void Overlay_SetY(ScriptOverlay *scover, int newy);
 int  Overlay_GetValid(ScriptOverlay *scover);
 ScriptOverlay *Overlay_CreateGraphical(int x, int y, int slot, int transparent);
 ScriptOverlay *Overlay_CreateTextual(int x, int y, int width, int font, int colour, const char *text);
+ScreenOverlay *Overlay_CreateGraphicCore(int x, int y, int slot, bool transparent);
+ScreenOverlay *Overlay_CreateTextCore(int x, int y, int width, int font, int text_color,
+	const char *text, int disp_type, int allow_shrink);
 
 int  find_overlay_of_type(int type);
 void remove_screen_overlay(int type);
diff --git a/engines/ags/shared/ac/sprite_cache.h b/engines/ags/shared/ac/sprite_cache.h
index 411948b2aab..93b63d83773 100644
--- a/engines/ags/shared/ac/sprite_cache.h
+++ b/engines/ags/shared/ac/sprite_cache.h
@@ -143,7 +143,7 @@ public:
 	void        SetMaxCacheSize(size_t size);
 
 	// Loads (if it's not in cache yet) and returns bitmap by the sprite index
-	Shared::Bitmap *operator[] (sprkey_t index);
+	Shared::Bitmap *operator[](sprkey_t index);
 
 private:
 	void        Init();


Commit: 13f40609c7e2f1fad134bbfc16d5345083c7ca61
    https://github.com/scummvm/scummvm/commit/13f40609c7e2f1fad134bbfc16d5345083c7ca61
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:10-07:00

Commit Message:
AGS: Reset currentline when exiting script function, for cc_errors

>From upstream 538e9072b03b0441853ac0022d17f8990065f8de

Changed paths:
    engines/ags/engine/script/cc_instance.cpp


diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index ac564320227..384a61c3065 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -354,6 +354,7 @@ int ccInstance::CallScriptFunction(const char *funcname, int32_t numargs, const
 	ASSERT_STACK_SIZE(numargs);
 	PopValuesFromStack(numargs);
 	pc = 0;
+	_G(currentline) = 0;
 	_GP(InstThreads).pop_back(); // pop instance thread
 	if (reterr != 0)
 		return reterr;


Commit: d38596820996de74746aceef369704706c27d870
    https://github.com/scummvm/scummvm/commit/d38596820996de74746aceef369704706c27d870
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-05-05T22:41:10-07:00

Commit Message:
AGS: Tidy up mobile config once more and get rid of psp_* variables

>From upstream 4ba8fbed2f16786e86f201f57fdaaa91cc887700

Changed paths:
    engines/ags/engine/ac/game_setup.cpp
    engines/ags/engine/ac/game_setup.h
    engines/ags/engine/main/config.cpp
    engines/ags/engine/main/engine.cpp
    engines/ags/engine/main/main.cpp
    engines/ags/engine/platform/base/ags_platform_driver.h
    engines/ags/globals.h


diff --git a/engines/ags/engine/ac/game_setup.cpp b/engines/ags/engine/ac/game_setup.cpp
index 9af8dfbdc20..b6526473bc9 100644
--- a/engines/ags/engine/ac/game_setup.cpp
+++ b/engines/ags/engine/ac/game_setup.cpp
@@ -41,6 +41,7 @@ GameSetup::GameSetup() {
 	RenderAtScreenRes = false;
 	Supersampling = 1;
 	clear_cache_on_room_change = false;
+	load_latest_save = false;
 	rotation = kScreenRotation_Unlocked;
 
 	Screen.Params.RefreshRate = 0;
diff --git a/engines/ags/engine/ac/game_setup.h b/engines/ags/engine/ac/game_setup.h
index ccef879bbad..718e0a13ab7 100644
--- a/engines/ags/engine/ac/game_setup.h
+++ b/engines/ags/engine/ac/game_setup.h
@@ -92,7 +92,8 @@ struct GameSetup {
 	MouseSpeedDef mouse_speed_def;
 	bool  RenderAtScreenRes; // render sprites at screen resolution, as opposed to native one
 	int   Supersampling;
-	bool  clear_cache_on_room_change; // compatibility
+	bool  clear_cache_on_room_change; // for low-end devices: clear resource caches on room change
+	bool  load_latest_save; // load latest saved game on launch
 	ScreenRotation rotation;
 
 	DisplayModeSetup Screen;
diff --git a/engines/ags/engine/main/config.cpp b/engines/ags/engine/main/config.cpp
index 9f136028fb3..2e8aa8e6bb0 100644
--- a/engines/ags/engine/main/config.cpp
+++ b/engines/ags/engine/main/config.cpp
@@ -262,68 +262,8 @@ static void read_legacy_graphics_config(const ConfigTree &cfg) {
 	_GP(usetup).Screen.Params.RefreshRate = CfgReadInt(cfg, "misc", "refresh");
 }
 
-
 void override_config_ext(ConfigTree &cfg) {
-	// Mobile ports always run in fullscreen mode
-#if AGS_PLATFORM_OS_ANDROID || AGS_PLATFORM_OS_IOS
-	CfgWriteInt(cfg, "graphics", "windowed", 0);
-#endif
-
-	// psp_gfx_renderer - rendering mode
-	//    * 0 - software renderer
-	//    * 1 - hardware, render to screen
-	//    * 2 - hardware, render to texture
-	if (_G(psp_gfx_renderer) == 0) {
-		CfgWriteString(cfg, "graphics", "driver", "Software");
-		CfgWriteInt(cfg, "graphics", "render_at_screenres", 1);
-	} else {
-		CfgWriteString(cfg, "graphics", "driver", "OGL");
-		CfgWriteInt(cfg, "graphics", "render_at_screenres", _G(psp_gfx_renderer) == 1);
-	}
-
-	// psp_gfx_scaling - scaling style:
-	//    * 0 - no scaling
-	//    * 1 - stretch and preserve aspect ratio
-	//    * 2 - stretch to whole screen
-	if (_G(psp_gfx_scaling) == 0)
-		CfgWriteString(cfg, "graphics", "game_scale_fs", "1");
-	else if (_G(psp_gfx_scaling) == 1)
-		CfgWriteString(cfg, "graphics", "game_scale_fs", "proportional");
-	else
-		CfgWriteString(cfg, "graphics", "game_scale_fs", "stretch");
-
-	// psp_gfx_smoothing - scaling filter:
-	//    * 0 - nearest-neighbour
-	//    * 1 - linear
-	if (_G(psp_gfx_smoothing) == 0)
-		CfgWriteString(cfg, "graphics", "filter", "StdScale");
-	else
-		CfgWriteString(cfg, "graphics", "filter", "Linear");
-
-	// psp_gfx_super_sampling - enable super sampling
-	//    * 0 - x1
-	//    * 1 - x2
-	if (_G(psp_gfx_renderer) == 2)
-		CfgWriteInt(cfg, "graphics", "supersampling", _G(psp_gfx_super_sampling) + 1);
-	else
-		CfgWriteInt(cfg, "graphics", "supersampling", 0);
-
-	// psp_gfx_rotation - scaling style:
-	//    * 0 - unlocked, let the user rotate as wished.
-	//    * 1 - portrait
-	//    * 2 - landscape
-	CfgWriteInt(cfg, "graphics", "rotation", _G(psp_rotation));
-
-#if AGS_PLATFORM_OS_ANDROID
-	// config_mouse_control_mode - enable relative mouse mode
-	//    * 1 - relative mouse touch controls
-	//    * 0 - direct touch mouse control
-	CfgWriteInt(cfg, "mouse", "control_enabled", config_mouse_control_mode);
-#endif
-
-	CfgWriteInt(cfg, "misc", "antialias", _G(psp_gfx_smooth_sprites) != 0);
-	CfgWriteString(cfg, "language", "translation", _G(psp_translation));
-	CfgWriteInt(cfg, "misc", "clear_cache_on_room_change", _G(psp_clear_cache_on_room_change) != 0);
+	_G(platform)->ReadConfiguration(cfg);
 }
 
 void apply_config(const ConfigTree &cfg) {
@@ -373,6 +313,7 @@ void apply_config(const ConfigTree &cfg) {
 		_GP(usetup).no_speech_pack = !CfgReadBoolInt(cfg, "sound", "usespeech", true);
 
 		_GP(usetup).clear_cache_on_room_change = CfgReadBoolInt(cfg, "misc", "clear_cache_on_room_change", _GP(usetup).clear_cache_on_room_change);
+		_GP(usetup).load_latest_save = CfgReadBoolInt(cfg, "misc", "load_latest_save", _GP(usetup).load_latest_save);
 		_GP(usetup).user_data_dir = CfgReadString(cfg, "misc", "user_data_dir");
 		_GP(usetup).shared_data_dir = CfgReadString(cfg, "misc", "shared_data_dir");
 
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 20ea7e1420e..eebaeef632f 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -792,10 +792,13 @@ void engine_prepare_to_start_game() {
 
 	engine_setup_scsystem_auxiliary();
 
-#if AGS_PLATFORM_OS_ANDROID
-	if (psp_load_latest_savegame)
-		selectLatestSavegame();
+	if (_GP(usetup).load_latest_save) {
+#ifndef AGS_PLATFORM_SCUMMVM
+		int slot = GetLastSaveSlot();
+		if (slot >= 0)
+			loadSaveGameOnStartup = get_save_game_path(slot);
 #endif
+	}
 }
 
 // TODO: move to test unit
@@ -961,11 +964,9 @@ void engine_read_config(ConfigTree &cfg) {
 	        Path::ComparePaths(user_cfg_file, user_global_cfg_file) != 0)
 		IniUtil::Read(user_cfg_file, cfg);
 
-	// Apply overriding options from mobile port settings
+	// Apply overriding options from platform settings
 	// TODO: normally, those should be instead stored in the same config file in a uniform way
-	// NOTE: the variable is historically called "ignore" but we use it in "override" meaning here
-	if (_G(psp_ignore_acsetup_cfg_file))
-		override_config_ext(cfg);
+	override_config_ext(cfg);
 }
 
 // Gathers settings from all available sources into single ConfigTree
diff --git a/engines/ags/engine/main/main.cpp b/engines/ags/engine/main/main.cpp
index 19648954c68..cc71dc641cd 100644
--- a/engines/ags/engine/main/main.cpp
+++ b/engines/ags/engine/main/main.cpp
@@ -43,10 +43,6 @@
 
 namespace AGS3 {
 
-#if AGS_PLATFORM_OS_WINDOWS && !AGS_PLATFORM_DEBUG
-#define USE_CUSTOM_EXCEPTION_HANDLER
-#endif
-
 using namespace AGS::Shared;
 using namespace AGS::Engine;
 
@@ -185,6 +181,7 @@ void main_print_help() {
 }
 
 int main_process_cmdline(ConfigTree &cfg, int argc, const char *argv[]) {
+	int datafile_argv = 0;
 	for (int ee = 1; ee < argc; ++ee) {
 		const char *arg = argv[ee];
 		//
@@ -283,11 +280,12 @@ int main_process_cmdline(ConfigTree &cfg, int argc, const char *argv[]) {
 				cfg["log"][logarg.Left(split_at)] = logarg.Mid(split_at + 1);
 			else
 				cfg["log"][logarg] = "";
-		}
+		} else if (arg[0] != '-') datafile_argv = ee;
 	}
 
-	// assign standard path (defined in their own platform implementation)
-	_G(cmdGameDataPath) = _G(psp_game_file_name);
+	if (datafile_argv > 0) {
+		_G(cmdGameDataPath) = _G(platform)->GetCommandArg(datafile_argv);
+	}
 
 	if (_G(tellInfoKeys).size() > 0)
 		_G(justTellInfo) = true;
diff --git a/engines/ags/engine/platform/base/ags_platform_driver.h b/engines/ags/engine/platform/base/ags_platform_driver.h
index b4a087f9001..bee844e3f9a 100644
--- a/engines/ags/engine/platform/base/ags_platform_driver.h
+++ b/engines/ags/engine/platform/base/ags_platform_driver.h
@@ -86,6 +86,8 @@ struct AGSPlatformDriver
 	virtual void DisplayAlert(const char *, ...) = 0;
 	virtual void AttachToParentConsole();
 	virtual int  GetLastSystemError();
+	// Optionally fill in config tree from the platform-specific config source
+	virtual void ReadConfiguration(Shared::ConfigTree &cfg) {}
 	// Get root directory for storing per-game shared data
 	virtual FSLocation GetAllUsersDataDirectory() {
 		return FSLocation(".");
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 0077f314ac8..9b88b069835 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -1086,17 +1086,8 @@ public:
 	std::set<String> _tellInfoKeys;
 	int _loadSaveGameOnStartup = -1;
 
-#if ! AGS_PLATFORM_DEFINES_PSP_VARS
-	int _psp_video_framedrop = 1;
-	int _psp_ignore_acsetup_cfg_file = 0;
-	int _psp_clear_cache_on_room_change = 0; // clear --sprite cache-- when room is unloaded
-
-#if defined(AGS_PLATFORM_SCUMMVM) && AGS_PLATFORM_SCUMMVM
-	int _psp_audio_cachesize = 10;
-#endif
-	const char *_psp_game_file_name = "";
-	const char *_psp_translation = "default";
-
+#if 0
+	//! AGS_PLATFORM_DEFINES_PSP_VARS
 	int _psp_rotation = 0;
 	int _psp_gfx_renderer = 0;
 	int _psp_gfx_scaling = 1;




More information about the Scummvm-git-logs mailing list