[Scummvm-git-logs] scummvm master -> 07f36ce19b12d5cc8356b7514c7390280140200e

dreammaster noreply at scummvm.org
Sat Mar 19 04:51:30 UTC 2022


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

Summary:
59a93fa277 AGS: Implemented AudioChannel.Pause, Resume and IsPaused
ca434dba3e AGS: Updated build version (3.6.0.9)
b09a7d3bde AGS: Fixed engine crash in case of too many reserved channels
a3dda03cf1 AGS: Store Views in std::vector
c25b08448d AGS: Removed AudioChannelsLock as it's no longer needed
07f36ce19b AGS: Store clip's ID in SOUNDCLIP instead of a pointer to unknown type


Commit: 59a93fa2778bf8b6c92310d0675a4dd9e2d61e48
    https://github.com/scummvm/scummvm/commit/59a93fa2778bf8b6c92310d0675a4dd9e2d61e48
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-18T21:29:55-07:00

Commit Message:
AGS: Implemented AudioChannel.Pause, Resume and IsPaused

>From upstream 3fa95f18f732ced40738a4a79b51552361d2a865

Changed paths:
    engines/ags/engine/ac/audio_channel.cpp
    engines/ags/engine/media/audio/clip_my_midi.cpp
    engines/ags/engine/media/audio/clip_my_midi.h
    engines/ags/engine/media/audio/sound_clip.cpp
    engines/ags/engine/media/audio/sound_clip.h


diff --git a/engines/ags/engine/ac/audio_channel.cpp b/engines/ags/engine/ac/audio_channel.cpp
index 2c13f75a769..57cc2a7b672 100644
--- a/engines/ags/engine/ac/audio_channel.cpp
+++ b/engines/ags/engine/ac/audio_channel.cpp
@@ -49,6 +49,13 @@ int AudioChannel_GetIsPlaying(ScriptAudioChannel *channel) {
 	return channel_is_playing(channel->id) ? 1 : 0;
 }
 
+bool AudioChannel_GetIsPaused(ScriptAudioChannel *channel) {
+	AudioChannelsLock lock;
+	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	if (ch) return ch->is_paused();
+	return false;
+}
+
 int AudioChannel_GetPanning(ScriptAudioChannel *channel) {
 	AudioChannelsLock lock;
 	auto *ch = lock.GetChannelIfPlaying(channel->id);
@@ -167,6 +174,18 @@ void AudioChannel_Stop(ScriptAudioChannel *channel) {
 		stop_or_fade_out_channel(channel->id, -1, nullptr);
 }
 
+void AudioChannel_Pause(ScriptAudioChannel *channel) {
+	AudioChannelsLock lock;
+	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	if (ch) ch->pause();
+}
+
+void AudioChannel_Resume(ScriptAudioChannel *channel) {
+	AudioChannelsLock lock;
+	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	if (ch) ch->resume();
+}
+
 void AudioChannel_Seek(ScriptAudioChannel *channel, int newPosition) {
 	if (newPosition < 0)
 		quitprintf("!AudioChannel.Seek: invalid seek position %d", newPosition);
@@ -275,11 +294,26 @@ RuntimeScriptValue Sc_AudioChannel_SetSpeed(void *self, const RuntimeScriptValue
 	API_OBJCALL_VOID_PINT(ScriptAudioChannel, AudioChannel_SetSpeed);
 }
 
+RuntimeScriptValue Sc_AudioChannel_Pause(void *self, const RuntimeScriptValue *params, int32_t param_count) {
+	API_OBJCALL_VOID(ScriptAudioChannel, AudioChannel_Pause);
+}
+
+RuntimeScriptValue Sc_AudioChannel_Resume(void *self, const RuntimeScriptValue *params, int32_t param_count) {
+	API_OBJCALL_VOID(ScriptAudioChannel, AudioChannel_Resume);
+}
+
+RuntimeScriptValue Sc_AudioChannel_GetIsPaused(void *self, const RuntimeScriptValue *params, int32_t param_count) {
+	API_OBJCALL_BOOL(ScriptAudioChannel, AudioChannel_GetIsPaused);
+}
+
 void RegisterAudioChannelAPI() {
+	ccAddExternalObjectFunction("AudioChannel::Pause^0", Sc_AudioChannel_Pause);
+	ccAddExternalObjectFunction("AudioChannel::Resume^0", Sc_AudioChannel_Resume);
 	ccAddExternalObjectFunction("AudioChannel::Seek^1", Sc_AudioChannel_Seek);
 	ccAddExternalObjectFunction("AudioChannel::SetRoomLocation^2", Sc_AudioChannel_SetRoomLocation);
 	ccAddExternalObjectFunction("AudioChannel::Stop^0", Sc_AudioChannel_Stop);
 	ccAddExternalObjectFunction("AudioChannel::get_ID", Sc_AudioChannel_GetID);
+	ccAddExternalObjectFunction("AudioChannel::get_IsPaused", Sc_AudioChannel_GetIsPaused);
 	ccAddExternalObjectFunction("AudioChannel::get_IsPlaying", Sc_AudioChannel_GetIsPlaying);
 	ccAddExternalObjectFunction("AudioChannel::get_LengthMs", Sc_AudioChannel_GetLengthMs);
 	ccAddExternalObjectFunction("AudioChannel::get_Panning", Sc_AudioChannel_GetPanning);
diff --git a/engines/ags/engine/media/audio/clip_my_midi.cpp b/engines/ags/engine/media/audio/clip_my_midi.cpp
index 14969e2fd19..36ba2357c56 100644
--- a/engines/ags/engine/media/audio/clip_my_midi.cpp
+++ b/engines/ags/engine/media/audio/clip_my_midi.cpp
@@ -94,10 +94,14 @@ int MYMIDI::play_from(int position) {
 	}
 }
 
-bool MYMIDI::is_playing() const {
+bool MYMIDI::is_playing() {
 	return ::AGS::g_music->isPlaying();
 }
 
+bool MYMIDI::is_paused() {
+	return false;
+}
+
 void MYMIDI::set_volume(int volume) {
 	_vol = volume;
 	_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
diff --git a/engines/ags/engine/media/audio/clip_my_midi.h b/engines/ags/engine/media/audio/clip_my_midi.h
index f036a70539a..80a3a5ae556 100644
--- a/engines/ags/engine/media/audio/clip_my_midi.h
+++ b/engines/ags/engine/media/audio/clip_my_midi.h
@@ -61,7 +61,8 @@ struct MYMIDI : public SOUNDCLIP {
 
 	int play() override;
 	int play_from(int position) override;
-	bool is_playing() const override;
+	bool is_playing() override;
+	bool is_paused() override;
 	void set_volume(int volume) override;
 	void set_panning(int newPanning) override;
 	void set_speed(int new_speed) override;
diff --git a/engines/ags/engine/media/audio/sound_clip.cpp b/engines/ags/engine/media/audio/sound_clip.cpp
index 23d2acdad14..e1fe16ce0e3 100644
--- a/engines/ags/engine/media/audio/sound_clip.cpp
+++ b/engines/ags/engine/media/audio/sound_clip.cpp
@@ -100,8 +100,12 @@ void SoundClipWaveBase::resume() {
 	poll();
 }
 
-bool SoundClipWaveBase::is_playing() const {
-	return _mixer->isSoundHandleActive(_soundHandle);
+bool SoundClipWaveBase::is_playing() {
+	return _mixer->isSoundHandleActive(_soundHandle) || is_paused();
+}
+
+bool SoundClipWaveBase::is_paused() {
+	return _state == SoundClipPaused;
 }
 
 void SoundClipWaveBase::seek(int offset) {
diff --git a/engines/ags/engine/media/audio/sound_clip.h b/engines/ags/engine/media/audio/sound_clip.h
index 8dc469a65c3..191e19bb4e7 100644
--- a/engines/ags/engine/media/audio/sound_clip.h
+++ b/engines/ags/engine/media/audio/sound_clip.h
@@ -78,7 +78,8 @@ struct SOUNDCLIP {
 	virtual void pause() = 0;
 	virtual void resume() = 0;
 
-	virtual bool is_playing() const = 0; // true if playing or paused. false if never played or stopped.
+	virtual bool is_playing() = 0; // true if playing or paused. false if never played or stopped.
+	virtual bool is_paused() = 0; // true if paused
 
 	inline int get_speed() const {
 		return _speed;
@@ -184,7 +185,8 @@ public:
 	int play_from(int position) override;
 	void pause() override;
 	void resume() override;
-	bool is_playing() const override;
+	bool is_playing() override;
+	bool is_paused() override;
 	void seek(int offset) override;
 	int get_pos() override;
 	int get_pos_ms() override;


Commit: ca434dba3e21f2aa3162f5282141849fccd92ab9
    https://github.com/scummvm/scummvm/commit/ca434dba3e21f2aa3162f5282141849fccd92ab9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-18T21:29:55-07:00

Commit Message:
AGS: Updated build version (3.6.0.9)

>From upstream bc79bb9fe1f4421f2cde15f33172a388bbe9a733

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 3f75c72c1d3..ad531799595 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.8"
+#define ACI_VERSION_STR      "3.6.0.9"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3,6,0,8
+#define ACI_VERSION_MSRC_DEF  3,6,0,9
 #endif
 
 #define SPECIAL_VERSION ""


Commit: b09a7d3bde8e5f175284b58dfa68f78d421926c4
    https://github.com/scummvm/scummvm/commit/b09a7d3bde8e5f175284b58dfa68f78d421926c4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-18T21:29:55-07:00

Commit Message:
AGS: Fixed engine crash in case of too many reserved channels

>From upstream 398783561b0079720950b117ba1db110100706f6

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


diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index 435cec56042..5a15e0a06e8 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -184,7 +184,8 @@ static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool int
 		for (int i = 0; i < clip->type; i++) {
 			startAtChannel += _GP(game).audioClipTypes[i].reservedChannels;
 		}
-		endBeforeChannel = startAtChannel + _GP(game).audioClipTypes[clip->type].reservedChannels;
+		endBeforeChannel = MIN(_GP(game).numGameChannels,
+			startAtChannel + _GP(game).audioClipTypes[clip->type].reservedChannels);
 	}
 
 	for (int i = startAtChannel; i < endBeforeChannel; i++) {


Commit: a3dda03cf1e37a3bc52b0a5cea1014249a45770a
    https://github.com/scummvm/scummvm/commit/a3dda03cf1e37a3bc52b0a5cea1014249a45770a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-18T21:29:56-07:00

Commit Message:
AGS: Store Views in std::vector

>From upstream 095db88ae0f1acceb298f793a2e1b81dbe06fcff

Changed paths:
    engines/ags/engine/ac/button.cpp
    engines/ags/engine/ac/character.cpp
    engines/ags/engine/ac/character_info_engine.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_game.cpp
    engines/ags/engine/ac/global_object.cpp
    engines/ags/engine/ac/global_view_frame.cpp
    engines/ags/engine/ac/object.cpp
    engines/ags/engine/ac/overlay.cpp
    engines/ags/engine/ac/room_object.cpp
    engines/ags/engine/ac/view_frame.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/engine.cpp
    engines/ags/engine/main/game_file.cpp
    engines/ags/engine/main/update.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h
    engines/ags/plugins/ags_plugin.cpp
    engines/ags/shared/ac/view.cpp
    engines/ags/shared/ac/view.h
    engines/ags/shared/game/main_game_file.cpp
    engines/ags/shared/game/main_game_file.h


diff --git a/engines/ags/engine/ac/button.cpp b/engines/ags/engine/ac/button.cpp
index 903a9d40b80..e63f7c8ed4c 100644
--- a/engines/ags/engine/ac/button.cpp
+++ b/engines/ags/engine/ac/button.cpp
@@ -50,7 +50,7 @@ void Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat)
 		quit("!AnimateButton: invalid view specified");
 	view--;
 
-	if ((loop < 0) || (loop >= _G(views)[view].numLoops))
+	if ((loop < 0) || (loop >= _GP(views)[view].numLoops))
 		quit("!AnimateButton: invalid loop specified for view");
 
 	// if it's already animating, stop it
@@ -200,7 +200,7 @@ int UpdateAnimatingButton(int bu) {
 		_G(animbuts)[bu].wait--;
 		return 0;
 	}
-	ViewStruct *tview = &_G(views)[_G(animbuts)[bu].view];
+	ViewStruct *tview = &_GP(views)[_G(animbuts)[bu].view];
 
 	_G(animbuts)[bu].frame++;
 
diff --git a/engines/ags/engine/ac/character.cpp b/engines/ags/engine/ac/character.cpp
index 17247d1e723..e236a59730d 100644
--- a/engines/ags/engine/ac/character.cpp
+++ b/engines/ags/engine/ac/character.cpp
@@ -287,7 +287,7 @@ enum DirectionalLoop {
 DirectionalLoop GetDirectionalLoop(CharacterInfo *chinfo, int x_diff, int y_diff) {
 	DirectionalLoop next_loop = kDirLoop_Left; // NOTE: default loop was Left for some reason
 
-	const ViewStruct &chview  = _G(views)[chinfo->view];
+	const ViewStruct &chview  = _GP(views)[chinfo->view];
 	const bool new_version    = _G(loaded_game_file_version) > kGameVersion_272;
 	const bool has_down_loop  = ((chview.numLoops > kDirLoop_Down)  && (chview.loops[kDirLoop_Down].numFrames > 0));
 	const bool has_up_loop    = ((chview.numLoops > kDirLoop_Up)    && (chview.loops[kDirLoop_Up].numFrames > 0));
@@ -573,17 +573,17 @@ void Character_LockViewAlignedEx(CharacterInfo *chap, int vii, int loop, int ali
 	if (chap->view < 0)
 		quit("!SetCharacterLoop: character has invalid old view number");
 
-	int sppic = _G(views)[chap->view].loops[chap->loop].frames[chap->frame].pic;
+	int sppic = _GP(views)[chap->view].loops[chap->loop].frames[chap->frame].pic;
 	int leftSide = data_to_game_coord(chap->x) - _GP(game).SpriteInfos[sppic].Width / 2;
 
 	Character_LockViewEx(chap, vii, stopMoving);
 
-	if ((loop < 0) || (loop >= _G(views)[chap->view].numLoops))
+	if ((loop < 0) || (loop >= _GP(views)[chap->view].numLoops))
 		quit("!SetCharacterViewEx: invalid loop specified");
 
 	chap->loop = loop;
 	chap->frame = 0;
-	int newpic = _G(views)[chap->view].loops[chap->loop].frames[chap->frame].pic;
+	int newpic = _GP(views)[chap->view].loops[chap->loop].frames[chap->frame].pic;
 	int newLeft = data_to_game_coord(chap->x) - _GP(game).SpriteInfos[newpic].Width / 2;
 	int xdiff = 0;
 
@@ -609,9 +609,9 @@ void Character_LockViewFrameEx(CharacterInfo *chaa, int view, int loop, int fram
 	Character_LockViewEx(chaa, view, stopMoving);
 
 	view--;
-	if ((loop < 0) || (loop >= _G(views)[view].numLoops))
+	if ((loop < 0) || (loop >= _GP(views)[view].numLoops))
 		quit("!SetCharacterFrame: invalid loop specified");
-	if ((frame < 0) || (frame >= _G(views)[view].loops[loop].numFrames))
+	if ((frame < 0) || (frame >= _GP(views)[view].loops[loop].numFrames))
 		quit("!SetCharacterFrame: invalid frame specified");
 
 	chaa->loop = loop;
@@ -931,7 +931,7 @@ void Character_UnlockViewEx(CharacterInfo *chaa, int stopMoving) {
 		Character_StopMoving(chaa);
 	}
 	if (chaa->view >= 0) {
-		int maxloop = _G(views)[chaa->view].numLoops;
+		int maxloop = _GP(views)[chaa->view].numLoops;
 		if (((chaa->flags & CHF_NODIAGONAL) != 0) && (maxloop > 4))
 			maxloop = 4;
 		FindReasonableLoopForCharacter(chaa);
@@ -1286,12 +1286,12 @@ int Character_GetLoop(CharacterInfo *chaa) {
 }
 
 void Character_SetLoop(CharacterInfo *chaa, int newval) {
-	if ((newval < 0) || (newval >= _G(views)[chaa->view].numLoops))
+	if ((newval < 0) || (newval >= _GP(views)[chaa->view].numLoops))
 		quit("!Character.Loop: invalid loop number for this view");
 
 	chaa->loop = newval;
 
-	if (chaa->frame >= _G(views)[chaa->view].loops[chaa->loop].numFrames)
+	if (chaa->frame >= _GP(views)[chaa->view].loops[chaa->loop].numFrames)
 		chaa->frame = 0;
 }
 
@@ -1651,11 +1651,11 @@ int find_looporder_index(int curloop) {
 
 // returns 0 to use diagonal, 1 to not
 int useDiagonal(CharacterInfo *char1) {
-	if ((_G(views)[char1->view].numLoops < 8) || ((char1->flags & CHF_NODIAGONAL) != 0))
+	if ((_GP(views)[char1->view].numLoops < 8) || ((char1->flags & CHF_NODIAGONAL) != 0))
 		return 1;
 	// If they have just provided standing frames for loops 4-7, to
 	// provide smoother turning
-	if (_G(views)[char1->view].loops[4].numFrames < 2)
+	if (_GP(views)[char1->view].loops[4].numFrames < 2)
 		return 2;
 	return 0;
 }
@@ -1664,9 +1664,9 @@ int useDiagonal(CharacterInfo *char1) {
 int hasUpDownLoops(CharacterInfo *char1) {
 	// if no loops in the Down animation
 	// or no loops in the Up animation
-	if ((_G(views)[char1->view].loops[0].numFrames < 1) ||
-	        (_G(views)[char1->view].numLoops < 4) ||
-	        (_G(views)[char1->view].loops[3].numFrames < 1)) {
+	if ((_GP(views)[char1->view].loops[0].numFrames < 1) ||
+	        (_GP(views)[char1->view].numLoops < 4) ||
+	        (_GP(views)[char1->view].loops[3].numFrames < 1)) {
 		return 0;
 	}
 
@@ -1704,9 +1704,9 @@ void start_character_turning(CharacterInfo *chinf, int useloop, int no_diagonal)
 			break;
 		if ((turnlooporder[ii] >= 4) && (no_diagonal > 0))
 			continue;
-		if (_G(views)[chinf->view].loops[turnlooporder[ii]].numFrames < 1)
+		if (_GP(views)[chinf->view].loops[turnlooporder[ii]].numFrames < 1)
 			continue;
-		if (turnlooporder[ii] < _G(views)[chinf->view].numLoops)
+		if (turnlooporder[ii] < _GP(views)[chinf->view].numLoops)
 			chinf->walking += TURNING_AROUND;
 	}
 
@@ -1732,8 +1732,8 @@ void fix_player_sprite(MoveList *cmls, CharacterInfo *chinf) {
 		chinf->loop = useloop;
 		return;
 	}
-	if ((chinf->loop >= _G(views)[chinf->view].numLoops) ||
-	        (_G(views)[chinf->view].loops[chinf->loop].numFrames < 1) ||
+	if ((chinf->loop >= _GP(views)[chinf->view].numLoops) ||
+	        (_GP(views)[chinf->view].loops[chinf->loop].numFrames < 1) ||
 	        (hasUpDownLoops(chinf) == 0)) {
 		// Character is not currently on a valid loop, so don't try to rotate
 		// eg. left/right only view, but current loop 0
@@ -1885,15 +1885,15 @@ void find_nearest_walkable_area(int *xx, int *yy) {
 
 void FindReasonableLoopForCharacter(CharacterInfo *chap) {
 
-	if (chap->loop >= _G(views)[chap->view].numLoops)
+	if (chap->loop >= _GP(views)[chap->view].numLoops)
 		chap->loop = kDirLoop_Default;
-	if (_G(views)[chap->view].numLoops < 1)
+	if (_GP(views)[chap->view].numLoops < 1)
 		quitprintf("!View %d does not have any loops", chap->view + 1);
 
 	// if the current loop has no frames, find one that does
-	if (_G(views)[chap->view].loops[chap->loop].numFrames < 1) {
-		for (int i = 0; i < _G(views)[chap->view].numLoops; i++) {
-			if (_G(views)[chap->view].loops[i].numFrames > 0) {
+	if (_GP(views)[chap->view].loops[chap->loop].numFrames < 1) {
+		for (int i = 0; i < _GP(views)[chap->view].numLoops; i++) {
+			if (_GP(views)[chap->view].loops[i].numFrames > 0) {
 				chap->loop = i;
 				break;
 			}
@@ -2022,9 +2022,9 @@ void animate_character(CharacterInfo *chap, int loopn, int sppd, int rept, int n
 		Character_UnlockView(chap);
 		chap->idleleft = chap->idletime;
 	}
-	if ((loopn < 0) || (loopn >= _G(views)[chap->view].numLoops))
+	if ((loopn < 0) || (loopn >= _GP(views)[chap->view].numLoops))
 		quit("!AnimateCharacter: invalid loop number specified");
-	if ((sframe < 0) || (sframe >= _G(views)[chap->view].loops[loopn].numFrames))
+	if ((sframe < 0) || (sframe >= _GP(views)[chap->view].loops[loopn].numFrames))
 		quit("!AnimateCharacter: invalid starting frame number specified");
 	Character_StopMoving(chap);
 	chap->animating = 1;
@@ -2037,11 +2037,11 @@ void animate_character(CharacterInfo *chap, int loopn, int sppd, int rept, int n
 	if (direction) {
 		sframe--;
 		if (sframe < 0)
-			sframe = _G(views)[chap->view].loops[loopn].numFrames - (-sframe);
+			sframe = _GP(views)[chap->view].loops[loopn].numFrames - (-sframe);
 	}
 	chap->frame = sframe;
 
-	chap->wait = sppd + _G(views)[chap->view].loops[loopn].frames[chap->frame].speed;
+	chap->wait = sppd + _GP(views)[chap->view].loops[loopn].frames[chap->frame].speed;
 	CheckViewFrameForCharacter(chap);
 }
 
@@ -2077,7 +2077,7 @@ Bitmap *GetCharacterImage(int charid, int *isFlipped) {
 		}
 	}
 	CharacterInfo *chin = &_GP(game).chars[charid];
-	int sppic = _G(views)[chin->view].loops[chin->loop].frames[chin->frame].pic;
+	int sppic = _GP(views)[chin->view].loops[chin->loop].frames[chin->frame].pic;
 	return _GP(spriteset)[sppic];
 }
 
@@ -2105,12 +2105,12 @@ int is_pos_on_character(int xx, int yy) {
 		CharacterInfo *chin = &_GP(game).chars[cc];
 
 		if ((chin->view < 0) ||
-		        (chin->loop >= _G(views)[chin->view].numLoops) ||
-		        (chin->frame >= _G(views)[chin->view].loops[chin->loop].numFrames)) {
+		        (chin->loop >= _GP(views)[chin->view].numLoops) ||
+		        (chin->frame >= _GP(views)[chin->view].loops[chin->loop].numFrames)) {
 			continue;
 		}
 
-		sppic = _G(views)[chin->view].loops[chin->loop].frames[chin->frame].pic;
+		sppic = _GP(views)[chin->view].loops[chin->loop].frames[chin->frame].pic;
 		int usewid = _G(charextra)[cc].width;
 		int usehit = _G(charextra)[cc].height;
 		if (usewid == 0) usewid = _GP(game).SpriteInfos[sppic].Width;
@@ -2118,7 +2118,7 @@ int is_pos_on_character(int xx, int yy) {
 		int xxx = chin->x - game_to_data_coord(usewid) / 2;
 		int yyy = chin->get_effective_y() - game_to_data_coord(usehit);
 
-		int mirrored = _G(views)[chin->view].loops[chin->loop].frames[chin->frame].flags & VFLG_FLIPSPRITE;
+		int mirrored = _GP(views)[chin->view].loops[chin->loop].frames[chin->frame].flags & VFLG_FLIPSPRITE;
 		Bitmap *theImage = GetCharacterImage(cc, &mirrored);
 
 		if (is_pos_in_sprite(xx, yy, xxx, yyy, theImage,
@@ -2381,12 +2381,12 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 			charFrameWas = speakingChar->frame;
 
 		// If speech view is missing a loop or the loop does not have frames - use loop 0
-		if (speakingChar->loop >= _G(views)[speakingChar->view].numLoops ||
-				_G(views)[speakingChar->view].loops[speakingChar->loop].numFrames < 1) {
+		if (speakingChar->loop >= _GP(views)[speakingChar->view].numLoops ||
+				_GP(views)[speakingChar->view].loops[speakingChar->loop].numFrames < 1) {
 			String err = String::FromFormat("Character %s speech view %d does not have necessary loop %d or it has no frames",
 				speakingChar->scrname, speakingChar->view + 1, speakingChar->loop);
 			// is there even a fallback loop?
-			if (_G(views)[speakingChar->view].numLoops == 0 || _G(views)[speakingChar->view].loops[0].numFrames == 0)
+			if (_GP(views)[speakingChar->view].numLoops == 0 || _GP(views)[speakingChar->view].loops[0].numFrames == 0)
 				quitprintf("!%s; and there's no valid loop to fall back.", err.GetCStr());
 			debug_script_warn("WARNING: %s; switching to loop 0.", err.GetCStr());
 			speakingChar->loop = 0;
@@ -2403,7 +2403,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 		tdxp = -tdxp;  // tell it to centre it ([ikm] not sure what's going on here... wrong comment?)
 
 		if (tdyp < 0) {
-			int sppic = _G(views)[speakingChar->view].loops[speakingChar->loop].frames[0].pic;
+			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;
 			tdyp = view->RoomToScreen(0, data_to_game_coord(_GP(game).chars[aschar].get_effective_y()) - height).first.Y
 			       - get_fixed_pixel_size(5);
@@ -2485,7 +2485,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 
 
 			int bigx = 0, bigy = 0, kk;
-			ViewStruct *viptr = &_G(views)[useview];
+			ViewStruct *viptr = &_GP(views)[useview];
 			for (kk = 0; kk < viptr->loops[0].numFrames; kk++) {
 				int tw = _GP(game).SpriteInfos[viptr->loops[0].frames[kk].pic].Width;
 				if (tw > bigx) bigx = tw;
@@ -2621,12 +2621,12 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 			speakingChar->flags |= CHF_FIXVIEW;
 
 			// If speech view is missing a loop or the loop does not have frames - use loop 0
-			if (speakingChar->loop >= _G(views)[speakingChar->view].numLoops ||
-				_G(views)[speakingChar->view].loops[speakingChar->loop].numFrames < 1) {
+			if (speakingChar->loop >= _GP(views)[speakingChar->view].numLoops ||
+				_GP(views)[speakingChar->view].loops[speakingChar->loop].numFrames < 1) {
 				String err = String::FromFormat("Character %s speech view %d does not have necessary loop %d or it has no frames",
 					speakingChar->scrname, speakingChar->view + 1, speakingChar->loop);
 				// is there even a fallback loop?
-				if (_G(views)[speakingChar->view].numLoops == 0 || _G(views)[speakingChar->view].loops[0].numFrames == 0)
+				if (_GP(views)[speakingChar->view].numLoops == 0 || _GP(views)[speakingChar->view].loops[0].numFrames == 0)
 					quitprintf("!%s; and there's no valid loop to fall back.", err.GetCStr());
 				debug_script_warn("WARNING: %s; switching to loop 0.", err.GetCStr());
 				speakingChar->loop = 0;
@@ -2636,7 +2636,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 
 			// set up the speed of the first frame
 			speakingChar->wait = GetCharacterSpeechAnimationDelay(speakingChar) +
-			                     _G(views)[speakingChar->view].loops[speakingChar->loop].frames[0].speed;
+			                     _GP(views)[speakingChar->view].loops[speakingChar->loop].frames[0].speed;
 
 			if (widd < 0) {
 				bwidth = ui_view.GetWidth() / 2 + ui_view.GetWidth() / 6;
@@ -2764,11 +2764,11 @@ int update_lip_sync(int talkview, int talkloop, int *talkframeptr) {
 		talkframe = 0;
 	else {
 		talkframe = GetLipSyncFrame(nowsaying, &_G(text_lips_offset));
-		if (talkframe >= _G(views)[talkview].loops[talkloop].numFrames)
+		if (talkframe >= _GP(views)[talkview].loops[talkloop].numFrames)
 			talkframe = 0;
 	}
 
-	talkwait = _G(loops_per_character) + _G(views)[talkview].loops[talkloop].frames[talkframe].speed;
+	talkwait = _G(loops_per_character) + _GP(views)[talkview].loops[talkloop].frames[talkframe].speed;
 
 	talkframeptr[0] = talkframe;
 	return talkwait;
@@ -2779,7 +2779,7 @@ Rect GetCharacterRoomBBox(int charid, bool use_frame_0) {
 	const CharacterExtras &chex = _G(charextra)[charid];
 	const CharacterInfo &chin = _GP(game).chars[charid];
 	int frame = use_frame_0 ? 0 : chin.frame;
-	int pic = _G(views)[chin.view].loops[chin.loop].frames[frame].pic;
+	int pic = _GP(views)[chin.view].loops[chin.loop].frames[frame].pic;
 	scale_sprite_size(pic, chex.zoom, &width, &height);
 	return RectWH(chin.x - width / 2, chin.y - height, width, height);
 }
diff --git a/engines/ags/engine/ac/character_info_engine.cpp b/engines/ags/engine/ac/character_info_engine.cpp
index 05a7edc370d..a9e95978116 100644
--- a/engines/ags/engine/ac/character_info_engine.cpp
+++ b/engines/ags/engine/ac/character_info_engine.cpp
@@ -77,11 +77,11 @@ void CharacterInfo::UpdateMoveAndAnim(int &char_index, CharacterExtras *chex, in
 
 	// Fixup character's view when possible
 	if (view >= 0 &&
-		(loop >= _G(views)[view].numLoops || frame >= _G(views)[view].loops[loop].numFrames)) {
+		(loop >= _GP(views)[view].numLoops || frame >= _GP(views)[view].loops[loop].numFrames)) {
 		for (loop = 0;
-			(loop < _G(views)[view].numLoops) && (_G(views)[view].loops[loop].numFrames == 0); ++loop) {
+			(loop < _GP(views)[view].numLoops) && (_GP(views)[view].loops[loop].numFrames == 0); ++loop) {
 		}
-		if (loop == _G(views)[view].numLoops) {
+		if (loop == _GP(views)[view].numLoops) {
 			// view has no frames?!
 			// amazingly enough there are old games that allow this to happen...
 			if (_G(loaded_game_file_version) >= kGameVersion_300)
@@ -139,8 +139,8 @@ int CharacterInfo::update_character_walking(CharacterExtras *chex) {
 					wantloop = 0;
 				if (wantloop < 0)
 					wantloop = 7;
-				if ((turnlooporder[wantloop] >= _G(views)[view].numLoops) ||
-				        (_G(views)[view].loops[turnlooporder[wantloop]].numFrames < 1) ||
+				if ((turnlooporder[wantloop] >= _GP(views)[view].numLoops) ||
+				        (_GP(views)[view].loops[turnlooporder[wantloop]].numFrames < 1) ||
 				        ((turnlooporder[wantloop] >= 4) && ((flags & CHF_NODIAGONAL) != 0))) {
 					if (walking >= TURNING_BACKWARDS)
 						wantloop--;
@@ -204,11 +204,11 @@ void CharacterInfo::update_character_moving(int &char_index, CharacterExtras *ch
 				walkwaitcounter++;
 		}
 
-		if (loop >= _G(views)[view].numLoops)
+		if (loop >= _GP(views)[view].numLoops)
 			quitprintf("Unable to render character %d (%s) because loop %d does not exist in view %d", index_id, name, loop, view + 1);
 
 		// check don't overflow loop
-		int framesInLoop = _G(views)[view].loops[loop].numFrames;
+		int framesInLoop = _GP(views)[view].loops[loop].numFrames;
 		if (frame > framesInLoop) {
 			frame = 1;
 
@@ -235,15 +235,15 @@ void CharacterInfo::update_character_moving(int &char_index, CharacterExtras *ch
 
 			if ((flags & CHF_MOVENOTWALK) == 0) {
 				frame++;
-				if (frame >= _G(views)[view].loops[loop].numFrames) {
+				if (frame >= _GP(views)[view].loops[loop].numFrames) {
 					// end of loop, so loop back round skipping the standing frame
 					frame = 1;
 
-					if (_G(views)[view].loops[loop].numFrames < 2)
+					if (_GP(views)[view].loops[loop].numFrames < 2)
 						frame = 0;
 				}
 
-				chex->animwait = _G(views)[view].loops[loop].frames[frame].speed + animspeed;
+				chex->animwait = _GP(views)[view].loops[loop].frames[frame].speed + animspeed;
 
 				if (flags & CHF_ANTIGLIDE)
 					walkwait = chex->animwait;
@@ -293,17 +293,17 @@ int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) {
 				if (frame < 0) {
 					// if the previous loop is a Run Next Loop one, go back to it
 					if ((loop > 0) &&
-					        (_G(views)[view].loops[loop - 1].RunNextLoop())) {
+					        (_GP(views)[view].loops[loop - 1].RunNextLoop())) {
 
 						loop--;
-						frame = _G(views)[view].loops[loop].numFrames - 1;
+						frame = _GP(views)[view].loops[loop].numFrames - 1;
 					} else if (animating & CHANIM_REPEAT) {
 
-						frame = _G(views)[view].loops[loop].numFrames - 1;
+						frame = _GP(views)[view].loops[loop].numFrames - 1;
 
-						while (_G(views)[view].loops[loop].RunNextLoop()) {
+						while (_GP(views)[view].loops[loop].RunNextLoop()) {
 							loop++;
-							frame = _G(views)[view].loops[loop].numFrames - 1;
+							frame = _GP(views)[view].loops[loop].numFrames - 1;
 						}
 					} else {
 						frame++;
@@ -323,10 +323,10 @@ int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) {
 				frame = 0;
 			}
 
-			if (frame >= _G(views)[view].loops[loop].numFrames) {
+			if (frame >= _GP(views)[view].loops[loop].numFrames) {
 
-				if (_G(views)[view].loops[loop].RunNextLoop()) {
-					if (loop + 1 >= _G(views)[view].numLoops)
+				if (_GP(views)[view].loops[loop].RunNextLoop()) {
+					if (loop + 1 >= _GP(views)[view].numLoops)
 						quit("!Animating character tried to overrun last loop in view");
 					loop++;
 					frame = 0;
@@ -349,12 +349,12 @@ int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) {
 					// if it's a multi-loop animation, go back to start
 					if (_GP(play).no_multiloop_repeat == 0) {
 						while ((loop > 0) &&
-						        (_G(views)[view].loops[loop - 1].RunNextLoop()))
+						        (_GP(views)[view].loops[loop - 1].RunNextLoop()))
 							loop--;
 					}
 				}
 			}
-			wait = _G(views)[view].loops[loop].frames[frame].speed;
+			wait = _GP(views)[view].loops[loop].frames[frame].speed;
 			// idle anim doesn't have speed stored cos animating==0
 			if (idleleft < 0)
 				wait += animspeed + 5;
@@ -464,7 +464,7 @@ void CharacterInfo::update_character_idle(CharacterExtras *chex, int &doing_noth
 			Character_LockView(this, idleview + 1);
 			// SetCharView resets it to 0
 			idleleft = -2;
-			int maxLoops = _G(views)[idleview].numLoops;
+			int maxLoops = _GP(views)[idleview].numLoops;
 			// if the char is set to "no diagonal loops", don't try
 			// to use diagonal idle loops either
 			if ((maxLoops > 4) && (useDiagonal(this)))
@@ -475,7 +475,7 @@ void CharacterInfo::update_character_idle(CharacterExtras *chex, int &doing_noth
 				do {
 					useloop = ::AGS::g_vm->getRandomNumber(maxLoops - 1);
 					// don't select a loop which is a continuation of a previous one
-				} while ((useloop > 0) && (_G(views)[idleview].loops[useloop - 1].RunNextLoop()));
+				} while ((useloop > 0) && (_GP(views)[idleview].loops[useloop - 1].RunNextLoop()));
 			}
 			// Normal idle anim - just reset to loop 0 if not enough to
 			// use the current one
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index b5fe5223faa..bfebe2f6e21 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -1338,8 +1338,8 @@ int construct_object_gfx(int aa, int *drawnWidth, int *drawnHeight, bool alwaysU
 	// check whether the image should be flipped
 	int isMirrored = 0;
 	if ((_G(objs)[aa].view != (uint16_t)-1) &&
-	        (_G(views)[_G(objs)[aa].view].loops[_G(objs)[aa].loop].frames[_G(objs)[aa].frame].pic == _G(objs)[aa].num) &&
-	        ((_G(views)[_G(objs)[aa].view].loops[_G(objs)[aa].loop].frames[_G(objs)[aa].frame].flags & VFLG_FLIPSPRITE) != 0)) {
+	        (_GP(views)[_G(objs)[aa].view].loops[_G(objs)[aa].loop].frames[_G(objs)[aa].frame].pic == _G(objs)[aa].num) &&
+	        ((_GP(views)[_G(objs)[aa].view].loops[_G(objs)[aa].loop].frames[_G(objs)[aa].frame].flags & VFLG_FLIPSPRITE) != 0)) {
 		isMirrored = 1;
 	}
 
@@ -1580,17 +1580,17 @@ void prepare_characters_for_drawing() {
 			           chin->name, _G(displayed_room));
 		}
 
-		if (chin->frame >= _G(views)[chin->view].loops[chin->loop].numFrames)
+		if (chin->frame >= _GP(views)[chin->view].loops[chin->loop].numFrames)
 			chin->frame = 0;
 
-		if ((chin->loop >= _G(views)[chin->view].numLoops) ||
-		        (_G(views)[chin->view].loops[chin->loop].numFrames < 1)) {
+		if ((chin->loop >= _GP(views)[chin->view].numLoops) ||
+		        (_GP(views)[chin->view].loops[chin->loop].numFrames < 1)) {
 			warning("The character '%s' could not be displayed because there were no frames in loop %d of view %d.",
 			           chin->name, chin->loop, chin->view + 1);
 			continue;
 		}
 
-		sppic = _G(views)[chin->view].loops[chin->loop].frames[chin->frame].pic;
+		sppic = _GP(views)[chin->view].loops[chin->loop].frames[chin->frame].pic;
 		if (sppic < 0)
 			sppic = 0;  // in case it's screwed up somehow
 		_G(our_eip) = 331;
@@ -1637,7 +1637,7 @@ void prepare_characters_for_drawing() {
 
 		// adjust the sppic if mirrored, so it doesn't accidentally
 		// cache the mirrored frame as the real one
-		if (_G(views)[chin->view].loops[chin->loop].frames[chin->frame].flags & VFLG_FLIPSPRITE) {
+		if (_GP(views)[chin->view].loops[chin->loop].frames[chin->frame].flags & VFLG_FLIPSPRITE) {
 			isMirrored = 1;
 			specialpic = -sppic;
 		}
@@ -2197,16 +2197,16 @@ void construct_game_screen_overlay(bool draw_mouse) {
 		else {
 			int viewnum = _GP(game).mcurs[_G(cur_cursor)].view;
 			int loopnum = 0;
-			if (loopnum >= _G(views)[viewnum].numLoops)
+			if (loopnum >= _GP(views)[viewnum].numLoops)
 				quitprintf("An animating mouse cursor is using view %d which has no loops", viewnum + 1);
-			if (_G(views)[viewnum].loops[loopnum].numFrames < 1)
+			if (_GP(views)[viewnum].loops[loopnum].numFrames < 1)
 				quitprintf("An animating mouse cursor is using view %d which has no frames in loop %d", viewnum + 1, loopnum);
 
 			_G(mouse_frame)++;
-			if (_G(mouse_frame) >= _G(views)[viewnum].loops[loopnum].numFrames)
+			if (_G(mouse_frame) >= _GP(views)[viewnum].loops[loopnum].numFrames)
 				_G(mouse_frame) = 0;
-			set_new_cursor_graphic(_G(views)[viewnum].loops[loopnum].frames[_G(mouse_frame)].pic);
-			_G(mouse_delay) = _G(views)[viewnum].loops[loopnum].frames[_G(mouse_frame)].speed + 5;
+			set_new_cursor_graphic(_GP(views)[viewnum].loops[loopnum].frames[_G(mouse_frame)].pic);
+			_G(mouse_delay) = _GP(views)[viewnum].loops[loopnum].frames[_G(mouse_frame)].speed + 5;
 			CheckViewFrame(viewnum, loopnum, _G(mouse_frame));
 		}
 		_G(lastmx) = _G(mousex);
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 37dd33d9be4..d0087059cb3 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -441,8 +441,7 @@ void unload_game_file() {
 	_GP(runDialogOptionRepExecFunc).moduleHasFunction.resize(0);
 	_G(numScriptModules) = 0;
 
-	free(_G(views));
-	_G(views) = nullptr;
+	_GP(views).clear();
 
 	free(_G(charcache));
 	_G(charcache) = nullptr;
@@ -555,33 +554,33 @@ int Game_GetLoopCountForView(int viewNumber) {
 	if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
 		quit("!GetGameParameter: invalid view specified");
 
-	return _G(views)[viewNumber - 1].numLoops;
+	return _GP(views)[viewNumber - 1].numLoops;
 }
 
 int Game_GetRunNextSettingForLoop(int viewNumber, int loopNumber) {
 	if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
 		quit("!GetGameParameter: invalid view specified");
-	if ((loopNumber < 0) || (loopNumber >= _G(views)[viewNumber - 1].numLoops))
+	if ((loopNumber < 0) || (loopNumber >= _GP(views)[viewNumber - 1].numLoops))
 		quit("!GetGameParameter: invalid loop specified");
 
-	return (_G(views)[viewNumber - 1].loops[loopNumber].RunNextLoop()) ? 1 : 0;
+	return (_GP(views)[viewNumber - 1].loops[loopNumber].RunNextLoop()) ? 1 : 0;
 }
 
 int Game_GetFrameCountForLoop(int viewNumber, int loopNumber) {
 	if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
 		quit("!GetGameParameter: invalid view specified");
-	if ((loopNumber < 0) || (loopNumber >= _G(views)[viewNumber - 1].numLoops))
+	if ((loopNumber < 0) || (loopNumber >= _GP(views)[viewNumber - 1].numLoops))
 		quit("!GetGameParameter: invalid loop specified");
 
-	return _G(views)[viewNumber - 1].loops[loopNumber].numFrames;
+	return _GP(views)[viewNumber - 1].loops[loopNumber].numFrames;
 }
 
 ScriptViewFrame *Game_GetViewFrame(int viewNumber, int loopNumber, int frame) {
 	if ((viewNumber < 1) || (viewNumber > _GP(game).numviews))
 		quit("!GetGameParameter: invalid view specified");
-	if ((loopNumber < 0) || (loopNumber >= _G(views)[viewNumber - 1].numLoops))
+	if ((loopNumber < 0) || (loopNumber >= _GP(views)[viewNumber - 1].numLoops))
 		quit("!GetGameParameter: invalid loop specified");
-	if ((frame < 0) || (frame >= _G(views)[viewNumber - 1].loops[loopNumber].numFrames))
+	if ((frame < 0) || (frame >= _GP(views)[viewNumber - 1].loops[loopNumber].numFrames))
 		quit("!GetGameParameter: invalid frame specified");
 
 	ScriptViewFrame *sdt = new ScriptViewFrame(viewNumber - 1, loopNumber, frame);
@@ -1441,10 +1440,10 @@ void game_sprite_deleted(int sprnum) {
 	}
 	// views
 	for (size_t v = 0; v < (size_t)_GP(game).numviews; ++v) {
-		for (size_t l = 0; l < (size_t)_G(views)[v].numLoops; ++l) {
-			for (size_t f = 0; f < (size_t)_G(views)[v].loops[l].numFrames; ++f) {
-				if (_G(views)[v].loops[l].frames[f].pic == sprnum)
-					_G(views)[v].loops[l].frames[f].pic = 0;
+		for (size_t l = 0; l < (size_t)_GP(views)[v].numLoops; ++l) {
+			for (size_t f = 0; f < (size_t)_GP(views)[v].loops[l].numFrames; ++f) {
+				if (_GP(views)[v].loops[l].frames[f].pic == sprnum)
+					_GP(views)[v].loops[l].frames[f].pic = 0;
 			}
 		}
 	}
diff --git a/engines/ags/engine/ac/global_character.cpp b/engines/ags/engine/ac/global_character.cpp
index 5f3e26687b0..fa0ec42e34c 100644
--- a/engines/ags/engine/ac/global_character.cpp
+++ b/engines/ags/engine/ac/global_character.cpp
@@ -115,13 +115,13 @@ int GetCharacterWidth(int ww) {
 
 	if (_G(charextra)[ww].width < 1) {
 		if ((char1->view < 0) ||
-		        (char1->loop >= _G(views)[char1->view].numLoops) ||
-		        (char1->frame >= _G(views)[char1->view].loops[char1->loop].numFrames)) {
+		        (char1->loop >= _GP(views)[char1->view].numLoops) ||
+		        (char1->frame >= _GP(views)[char1->view].loops[char1->loop].numFrames)) {
 			debug_script_warn("GetCharacterWidth: Character %s has invalid frame: view %d, loop %d, frame %d", char1->scrname, char1->view + 1, char1->loop, char1->frame);
 			return data_to_game_coord(4);
 		}
 
-		return _GP(game).SpriteInfos[_G(views)[char1->view].loops[char1->loop].frames[char1->frame].pic].Width;
+		return _GP(game).SpriteInfos[_GP(views)[char1->view].loops[char1->loop].frames[char1->frame].pic].Width;
 	} else
 		return _G(charextra)[ww].width;
 }
@@ -131,13 +131,13 @@ int GetCharacterHeight(int charid) {
 
 	if (_G(charextra)[charid].height < 1) {
 		if ((char1->view < 0) ||
-		        (char1->loop >= _G(views)[char1->view].numLoops) ||
-		        (char1->frame >= _G(views)[char1->view].loops[char1->loop].numFrames)) {
+		        (char1->loop >= _GP(views)[char1->view].numLoops) ||
+		        (char1->frame >= _GP(views)[char1->view].loops[char1->loop].numFrames)) {
 			debug_script_warn("GetCharacterHeight: Character %s has invalid frame: view %d, loop %d, frame %d", char1->scrname, char1->view + 1, char1->loop, char1->frame);
 			return data_to_game_coord(2);
 		}
 
-		return _GP(game).SpriteInfos[_G(views)[char1->view].loops[char1->loop].frames[char1->frame].pic].Height;
+		return _GP(game).SpriteInfos[_GP(views)[char1->view].loops[char1->loop].frames[char1->frame].pic].Height;
 	} else
 		return _G(charextra)[charid].height;
 }
diff --git a/engines/ags/engine/ac/global_debug.cpp b/engines/ags/engine/ac/global_debug.cpp
index 598456cd31f..67e3e34b18f 100644
--- a/engines/ags/engine/ac/global_debug.cpp
+++ b/engines/ags/engine/ac/global_debug.cpp
@@ -91,7 +91,7 @@ void script_debug(int cmdd, int dataa) {
 		Display(toDisplay.GetCStr());
 		//    Display("shftR: %d  shftG: %d  shftB: %d", _G(_rgb_r_shift_16), _G(_rgb_g_shift_16), _G(_rgb_b_shift_16));
 		//    Display("Remaining memory: %d kb",_go32_dpmi_remaining_virtual_memory()/1024);
-		//Display("Play char bcd: %d",->GetColorDepth(_GP(spriteset)[_G(views)[_G(playerchar)->view].frames[_G(playerchar)->loop][_G(playerchar)->frame].pic]));
+		//Display("Play char bcd: %d",->GetColorDepth(_GP(spriteset)[_GP(views)[_G(playerchar)->view].frames[_G(playerchar)->loop][_G(playerchar)->frame].pic]));
 	} else if (cmdd == 2) {  // show walkable areas from here
 		// TODO: support multiple viewports?!
 		const int viewport_index = 0;
diff --git a/engines/ags/engine/ac/global_game.cpp b/engines/ags/engine/ac/global_game.cpp
index a6dc4757357..afeb1c0e604 100644
--- a/engines/ags/engine/ac/global_game.cpp
+++ b/engines/ags/engine/ac/global_game.cpp
@@ -325,14 +325,14 @@ int GetGameParameter(int parm, int data1, int data2, int data3) {
 		if ((data1 < 1) || (data1 > _GP(game).numviews)) {
 			quitprintf("!GetGameParameter: invalid view specified (v: %d, l: %d, f: %d)", data1, data2, data3);
 		}
-		if ((data2 < 0) || (data2 >= _G(views)[data1 - 1].numLoops)) {
+		if ((data2 < 0) || (data2 >= _GP(views)[data1 - 1].numLoops)) {
 			quitprintf("!GetGameParameter: invalid loop specified (v: %d, l: %d, f: %d)", data1, data2, data3);
 		}
-		if ((data3 < 0) || (data3 >= _G(views)[data1 - 1].loops[data2].numFrames)) {
+		if ((data3 < 0) || (data3 >= _GP(views)[data1 - 1].loops[data2].numFrames)) {
 			quitprintf("!GetGameParameter: invalid frame specified (v: %d, l: %d, f: %d)", data1, data2, data3);
 		}
 
-		ViewFrame *pvf = &_G(views)[data1 - 1].loops[data2].frames[data3];
+		ViewFrame *pvf = &_GP(views)[data1 - 1].loops[data2].frames[data3];
 
 		if (parm == GP_FRAMESPEED)
 			return pvf->speed;
diff --git a/engines/ags/engine/ac/global_object.cpp b/engines/ags/engine/ac/global_object.cpp
index 9d227077700..3b02edffb0b 100644
--- a/engines/ags/engine/ac/global_object.cpp
+++ b/engines/ags/engine/ac/global_object.cpp
@@ -72,7 +72,7 @@ int GetObjectIDAtRoom(int roomx, int roomy) {
 		int spWidth = game_to_data_coord(_G(objs)[aa].get_width());
 		int spHeight = game_to_data_coord(_G(objs)[aa].get_height());
 		if (_G(objs)[aa].view != (uint16_t)-1)
-			isflipped = _G(views)[_G(objs)[aa].view].loops[_G(objs)[aa].loop].frames[_G(objs)[aa].frame].flags & VFLG_FLIPSPRITE;
+			isflipped = _GP(views)[_G(objs)[aa].view].loops[_G(objs)[aa].loop].frames[_G(objs)[aa].frame].flags & VFLG_FLIPSPRITE;
 
 		Bitmap *theImage = GetObjectImage(aa, &isflipped);
 
@@ -140,10 +140,10 @@ void SetObjectView(int obn, int vii) {
 
 	_G(objs)[obn].view = (uint16_t)vii;
 	_G(objs)[obn].frame = 0;
-	if (_G(objs)[obn].loop >= _G(views)[vii].numLoops)
+	if (_G(objs)[obn].loop >= _GP(views)[vii].numLoops)
 		_G(objs)[obn].loop = 0;
 	_G(objs)[obn].cycling = 0;
-	int pic = _G(views)[vii].loops[0].frames[0].pic;
+	int pic = _GP(views)[vii].loops[0].frames[0].pic;
 	_G(objs)[obn].num = Math::InRangeOrDef<uint16_t>(pic, 0);
 	if (pic > UINT16_MAX)
 		debug_script_warn("Warning: object's (id %d) sprite %d is outside of internal range (%d), reset to 0", obn, pic, UINT16_MAX);
@@ -153,13 +153,13 @@ void SetObjectFrame(int obn, int viw, int lop, int fra) {
 	if (!is_valid_object(obn)) quit("!SetObjectFrame: invalid object number specified");
 	viw--;
 	if (viw < 0 || viw >= _GP(game).numviews) quitprintf("!SetObjectFrame: invalid view number used (%d, range is 0 - %d)", viw, _GP(game).numviews - 1);
-	if (lop < 0 || lop >= _G(views)[viw].numLoops) quitprintf("!SetObjectFrame: invalid loop number used (%d, range is 0 - %d)", lop, _G(views)[viw].numLoops - 1);
+	if (lop < 0 || lop >= _GP(views)[viw].numLoops) quitprintf("!SetObjectFrame: invalid loop number used (%d, range is 0 - %d)", lop, _GP(views)[viw].numLoops - 1);
 	// historically AGS let user to pass literally any positive invalid frame value by silently reassigning it to zero...
-	if (fra < 0 || fra >= _G(views)[viw].loops[lop].numFrames) {
-		if (_G(views)[viw].loops[lop].numFrames == 0) // NOTE: we have a dummy frame allocated for this case
+	if (fra < 0 || fra >= _GP(views)[viw].loops[lop].numFrames) {
+		if (_GP(views)[viw].loops[lop].numFrames == 0) // NOTE: we have a dummy frame allocated for this case
 			debug_script_warn("SetObjectFrame: specified loop %d has no frames, will fallback to dummy frame", lop);
 		else
-			debug_script_warn("SetObjectFrame: frame index out of range (%d, must be 0 - %d), set to 0", fra, _G(views)[viw].loops[lop].numFrames - 1);
+			debug_script_warn("SetObjectFrame: frame index out of range (%d, must be 0 - %d), set to 0", fra, _GP(views)[viw].loops[lop].numFrames - 1);
 		fra = 0;
 	}
 	if (viw > UINT16_MAX || lop > UINT16_MAX || fra > UINT16_MAX) {
@@ -179,7 +179,7 @@ void SetObjectFrame(int obn, int viw, int lop, int fra) {
 	_G(objs)[obn].loop = lop;
 	_G(objs)[obn].frame = fra;
 	_G(objs)[obn].cycling = 0;
-	int pic = _G(views)[viw].loops[lop].frames[fra].pic;
+	int pic = _GP(views)[viw].loops[lop].frames[fra].pic;
 	_G(objs)[obn].num = Math::InRangeOrDef<uint16_t>(pic, 0);
 	if (pic > UINT16_MAX)
 		debug_script_warn("Warning: object's (id %d) sprite %d is outside of internal range (%d), reset to 0", obn, pic, UINT16_MAX);
@@ -223,22 +223,22 @@ void AnimateObjectImpl(int obn, int loopn, int spdd, int rept, int direction, in
 		quit("!AnimateObject: invalid object number specified");
 	if (_G(objs)[obn].view == (uint16_t)-1)
 		quit("!AnimateObject: object has not been assigned a view");
-	if (loopn < 0 || loopn >= _G(views)[_G(objs)[obn].view].numLoops)
+	if (loopn < 0 || loopn >= _GP(views)[_G(objs)[obn].view].numLoops)
 		quit("!AnimateObject: invalid loop number specified");
-	if (sframe < 0 || sframe >= _G(views)[_G(objs)[obn].view].loops[loopn].numFrames)
+	if (sframe < 0 || sframe >= _GP(views)[_G(objs)[obn].view].loops[loopn].numFrames)
 		quit("!AnimateObject: invalid starting frame number specified");
 	if ((direction < 0) || (direction > 1))
 		quit("!AnimateObjectEx: invalid direction");
 	if ((rept < 0) || (rept > 2))
 		quit("!AnimateObjectEx: invalid repeat value");
-	if (_G(views)[_G(objs)[obn].view].loops[loopn].numFrames < 1)
+	if (_GP(views)[_G(objs)[obn].view].loops[loopn].numFrames < 1)
 		quit("!AnimateObject: no frames in the specified view loop");
 
 	// reverse animation starts at the *previous frame*
 	if (direction) {
 		sframe--;
 		if (sframe < 0)
-			sframe = _G(views)[_G(objs)[obn].view].loops[loopn].numFrames - (-sframe);
+			sframe = _GP(views)[_G(objs)[obn].view].loops[loopn].numFrames - (-sframe);
 	}
 
 	if (loopn > UINT16_MAX || sframe > UINT16_MAX) {
@@ -253,8 +253,8 @@ void AnimateObjectImpl(int obn, int loopn, int spdd, int rept, int direction, in
 	_G(objs)[obn].loop = (uint16_t)loopn;
 	_G(objs)[obn].frame = (uint16_t)sframe;
 	_G(objs)[obn].overall_speed = spdd;
-	_G(objs)[obn].wait = spdd + _G(views)[_G(objs)[obn].view].loops[loopn].frames[_G(objs)[obn].frame].speed;
-	int pic = _G(views)[_G(objs)[obn].view].loops[loopn].frames[_G(objs)[obn].frame].pic;
+	_G(objs)[obn].wait = spdd + _GP(views)[_G(objs)[obn].view].loops[loopn].frames[_G(objs)[obn].frame].speed;
+	int pic = _GP(views)[_G(objs)[obn].view].loops[loopn].frames[_G(objs)[obn].frame].pic;
 	_G(objs)[obn].num = Math::InRangeOrDef<uint16_t>(pic, 0);
 	if (pic > UINT16_MAX)
 		debug_script_warn("Warning: object's (id %d) sprite %d is outside of internal range (%d), reset to 0", obn, pic, UINT16_MAX);
diff --git a/engines/ags/engine/ac/global_view_frame.cpp b/engines/ags/engine/ac/global_view_frame.cpp
index 6c735d74530..bd5c56810f7 100644
--- a/engines/ags/engine/ac/global_view_frame.cpp
+++ b/engines/ags/engine/ac/global_view_frame.cpp
@@ -34,22 +34,22 @@ void SetFrameSound(int vii, int loop, int frame, int sound) {
 		quit("!SetFrameSound: invalid view number");
 	vii--;
 
-	if (loop >= _G(views)[vii].numLoops)
+	if (loop >= _GP(views)[vii].numLoops)
 		quit("!SetFrameSound: invalid loop number");
 
-	if (frame >= _G(views)[vii].loops[loop].numFrames)
+	if (frame >= _GP(views)[vii].loops[loop].numFrames)
 		quit("!SetFrameSound: invalid frame number");
 
 	if (sound < 1) {
-		_G(views)[vii].loops[loop].frames[frame].sound = -1;
+		_GP(views)[vii].loops[loop].frames[frame].sound = -1;
 	} else {
 		ScriptAudioClip *clip = GetAudioClipForOldStyleNumber(_GP(game), false, sound);
 		if (clip == nullptr)
 			quitprintf("!SetFrameSound: audio clip aSound%d not found", sound);
 
-		_G(views)[vii].loops[loop].frames[frame].sound =
+		_GP(views)[vii].loops[loop].frames[frame].sound =
 			_GP(game).IsLegacyAudioSystem() ? sound : clip->id;
-		_G(views)[vii].loops[loop].frames[frame].audioclip = clip->id;
+		_GP(views)[vii].loops[loop].frames[frame].audioclip = clip->id;
 	}
 }
 
diff --git a/engines/ags/engine/ac/object.cpp b/engines/ags/engine/ac/object.cpp
index e5b77567385..1b3e35b4d02 100644
--- a/engines/ags/engine/ac/object.cpp
+++ b/engines/ags/engine/ac/object.cpp
@@ -93,8 +93,8 @@ void Object_SetView(ScriptObject *objj, int view, int loop, int frame) {
 		if (frame < 0) frame = obj.frame;
 		const int vidx = view - 1;
 		if (vidx < 0 || vidx >= _GP(game).numviews) quit("!Object_SetView: invalid view number used");
-		loop = Math::Clamp(loop, 0, (int)_G(views)[vidx].numLoops - 1);
-		frame = Math::Clamp(frame, 0, (int)_G(views)[vidx].loops[loop].numFrames - 1);
+		loop = Math::Clamp(loop, 0, (int)_GP(views)[vidx].numLoops - 1);
+		frame = Math::Clamp(frame, 0, (int)_GP(views)[vidx].loops[loop].numFrames - 1);
 	}
 
 	SetObjectFrame(objj->id, view, loop, frame);
diff --git a/engines/ags/engine/ac/overlay.cpp b/engines/ags/engine/ac/overlay.cpp
index 75538220ebc..034a79cb32d 100644
--- a/engines/ags/engine/ac/overlay.cpp
+++ b/engines/ags/engine/ac/overlay.cpp
@@ -321,7 +321,7 @@ void get_overlay_position(const ScreenOverlay &over, int *x, int *y) {
 		int charid = over.y;
 
 		auto view = FindNearestViewport(charid);
-		const int charpic = _G(views)[_GP(game).chars[charid].view].loops[_GP(game).chars[charid].loop].frames[0].pic;
+		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;
 		Point screenpt = view->RoomToScreen(
 		                     data_to_game_coord(_GP(game).chars[charid].x),
diff --git a/engines/ags/engine/ac/room_object.cpp b/engines/ags/engine/ac/room_object.cpp
index 4153950ec4a..da0cc6ab5ea 100644
--- a/engines/ags/engine/ac/room_object.cpp
+++ b/engines/ags/engine/ac/room_object.cpp
@@ -92,7 +92,7 @@ void RoomObject::UpdateCyclingView(int ref_id) {
 
 	}  // end if forwards
 
-	ViewFrame *vfptr = &_G(views)[view].loops[loop].frames[frame];
+	ViewFrame *vfptr = &_GP(views)[view].loops[loop].frames[frame];
 	if (vfptr->pic > UINT16_MAX)
 		debug_script_warn("Warning: object's (id %d) sprite %d is outside of internal range (%d), reset to 0",
 		                  ref_id, vfptr->pic, UINT16_MAX);
@@ -108,10 +108,10 @@ void RoomObject::UpdateCyclingView(int ref_id) {
 
 void RoomObject::update_cycle_view_forwards() {
 	frame++;
-	if (frame >= _G(views)[view].loops[loop].numFrames) {
+	if (frame >= _GP(views)[view].loops[loop].numFrames) {
 		// go to next loop thing
-		if (_G(views)[view].loops[loop].RunNextLoop()) {
-			if (loop + 1 >= _G(views)[view].numLoops)
+		if (_GP(views)[view].loops[loop].RunNextLoop()) {
+			if (loop + 1 >= _GP(views)[view].numLoops)
 				quit("!Last loop in a view requested to move to next loop");
 			loop++;
 			frame = 0;
@@ -123,7 +123,7 @@ void RoomObject::update_cycle_view_forwards() {
 			if (_GP(play).no_multiloop_repeat == 0) {
 				// multi-loop anims, go back to start of it
 				while ((loop > 0) &&
-				        (_G(views)[view].loops[loop - 1].RunNextLoop()))
+				        (_GP(views)[view].loops[loop - 1].RunNextLoop()))
 					loop--;
 			}
 			if (cycling % ANIM_BACKWARDS == ANIM_ONCERESET)
@@ -139,16 +139,16 @@ void RoomObject::update_cycle_view_backwards() {
 		frame--;
 	} else {
 		if ((loop > 0) &&
-		        (_G(views)[view].loops[loop - 1].RunNextLoop())) {
+		        (_GP(views)[view].loops[loop - 1].RunNextLoop())) {
 			// If it's a Go-to-next-loop on the previous one, then go back
 			loop--;
-			frame = _G(views)[view].loops[loop].numFrames - 1;
+			frame = _GP(views)[view].loops[loop].numFrames - 1;
 		} else if (cycling % ANIM_BACKWARDS == ANIM_ONCE) {
 			// leave it on the first frame
 			cycling = 0;
 			frame = 0;
 		} else { // repeating animation
-			frame = _G(views)[view].loops[loop].numFrames - 1;
+			frame = _GP(views)[view].loops[loop].numFrames - 1;
 		}
 	}
 }
diff --git a/engines/ags/engine/ac/view_frame.cpp b/engines/ags/engine/ac/view_frame.cpp
index 092831565d1..ba403698212 100644
--- a/engines/ags/engine/ac/view_frame.cpp
+++ b/engines/ags/engine/ac/view_frame.cpp
@@ -46,21 +46,21 @@ using AGS::Shared::Graphics;
 
 
 int ViewFrame_GetFlipped(ScriptViewFrame *svf) {
-	if (_G(views)[svf->view].loops[svf->loop].frames[svf->frame].flags & VFLG_FLIPSPRITE)
+	if (_GP(views)[svf->view].loops[svf->loop].frames[svf->frame].flags & VFLG_FLIPSPRITE)
 		return 1;
 	return 0;
 }
 
 int ViewFrame_GetGraphic(ScriptViewFrame *svf) {
-	return _G(views)[svf->view].loops[svf->loop].frames[svf->frame].pic;
+	return _GP(views)[svf->view].loops[svf->loop].frames[svf->frame].pic;
 }
 
 void ViewFrame_SetGraphic(ScriptViewFrame *svf, int newPic) {
-	_G(views)[svf->view].loops[svf->loop].frames[svf->frame].pic = newPic;
+	_GP(views)[svf->view].loops[svf->loop].frames[svf->frame].pic = newPic;
 }
 
 ScriptAudioClip *ViewFrame_GetLinkedAudio(ScriptViewFrame *svf) {
-	int soundIndex = _G(views)[svf->view].loops[svf->loop].frames[svf->frame].sound;
+	int soundIndex = _GP(views)[svf->view].loops[svf->loop].frames[svf->frame].sound;
 	if (soundIndex < 0)
 		return nullptr;
 
@@ -72,31 +72,31 @@ void ViewFrame_SetLinkedAudio(ScriptViewFrame *svf, ScriptAudioClip *clip) {
 	if (clip != nullptr)
 		newSoundIndex = clip->id;
 
-	_G(views)[svf->view].loops[svf->loop].frames[svf->frame].sound = newSoundIndex;
+	_GP(views)[svf->view].loops[svf->loop].frames[svf->frame].sound = newSoundIndex;
 }
 
 int ViewFrame_GetSound(ScriptViewFrame *svf) {
 	// convert audio clip to old-style sound number
-	return get_old_style_number_for_sound(_G(views)[svf->view].loops[svf->loop].frames[svf->frame].sound);
+	return get_old_style_number_for_sound(_GP(views)[svf->view].loops[svf->loop].frames[svf->frame].sound);
 }
 
 void ViewFrame_SetSound(ScriptViewFrame *svf, int newSound) {
 	if (newSound < 1) {
-		_G(views)[svf->view].loops[svf->loop].frames[svf->frame].audioclip = -1;
+		_GP(views)[svf->view].loops[svf->loop].frames[svf->frame].audioclip = -1;
 	} else {
 		// convert sound number to audio clip
 		ScriptAudioClip *clip = GetAudioClipForOldStyleNumber(_GP(game), false, newSound);
 		if (clip == nullptr)
 			quitprintf("!SetFrameSound: audio clip aSound%d not found", newSound);
 
-		_G(views)[svf->view].loops[svf->loop].frames[svf->frame].sound =
+		_GP(views)[svf->view].loops[svf->loop].frames[svf->frame].sound =
 			_GP(game).IsLegacyAudioSystem() ? newSound : clip->id;
-		_G(views)[svf->view].loops[svf->loop].frames[svf->frame].audioclip = clip->id;
+		_GP(views)[svf->view].loops[svf->loop].frames[svf->frame].audioclip = clip->id;
 	}
 }
 
 int ViewFrame_GetSpeed(ScriptViewFrame *svf) {
-	return _G(views)[svf->view].loops[svf->loop].frames[svf->frame].speed;
+	return _GP(views)[svf->view].loops[svf->loop].frames[svf->frame].speed;
 }
 
 int ViewFrame_GetView(ScriptViewFrame *svf) {
@@ -117,9 +117,9 @@ void precache_view(int view) {
 	if (view < 0)
 		return;
 
-	for (int i = 0; i < _G(views)[view].numLoops; i++) {
-		for (int j = 0; j < _G(views)[view].loops[i].numFrames; j++)
-			_GP(spriteset).Precache(_G(views)[view].loops[i].frames[j].pic);
+	for (int i = 0; i < _GP(views)[view].numLoops; i++) {
+		for (int j = 0; j < _GP(views)[view].loops[i].numFrames; j++)
+			_GP(spriteset).Precache(_GP(views)[view].loops[i].frames[j].pic);
 	}
 }
 
@@ -129,8 +129,8 @@ void CheckViewFrame(int view, int loop, int frame, int sound_volume) {
 	ScriptAudioChannel *channel = nullptr;
 	if (_GP(game).IsLegacyAudioSystem()) {
 		// sound field contains legacy sound num, so we also need an actual clip index
-		const int sound = _G(views)[view].loops[loop].frames[frame].sound;
-		int &clip_id = _G(views)[view].loops[loop].frames[frame].audioclip;
+		const int sound = _GP(views)[view].loops[loop].frames[frame].sound;
+		int &clip_id = _GP(views)[view].loops[loop].frames[frame].audioclip;
 		if (sound > 0) {
 			if (clip_id < 0) {
 				ScriptAudioClip *clip = GetAudioClipForOldStyleNumber(_GP(game), false, sound);
@@ -141,9 +141,9 @@ void CheckViewFrame(int view, int loop, int frame, int sound_volume) {
 			channel = play_audio_clip_by_index(clip_id);
 		}
 	} else {
-		if (_G(views)[view].loops[loop].frames[frame].sound >= 0) {
+		if (_GP(views)[view].loops[loop].frames[frame].sound >= 0) {
 			// play this sound (eg. footstep)
-			channel = play_audio_clip_by_index(_G(views)[view].loops[loop].frames[frame].sound);
+			channel = play_audio_clip_by_index(_GP(views)[view].loops[loop].frames[frame].sound);
 		}
 	}
 	if (sound_volume != SCR_NO_VALUE && channel != nullptr) {
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 6cf7a960e6f..f9db2548181 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -36,6 +36,7 @@
 #include "ags/engine/ac/dynobj/all_script_classes.h"
 #include "ags/engine/ac/statobj/ags_static_object.h"
 #include "ags/engine/ac/statobj/static_array.h"
+#include "ags/shared/ac/view.h"
 #include "ags/shared/core/asset_manager.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/shared/debugging/out.h"
@@ -374,6 +375,7 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
 	_G(charcache) = (CharacterCache *)calloc(1, sizeof(CharacterCache) * _GP(game).numcharacters + 5);
 	_G(mls) = (MoveList *)calloc(_GP(game).numcharacters + MAX_ROOM_OBJECTS + 1, sizeof(MoveList));
 	init_game_drawdata();
+	_GP(views) = std::move(ents.Views);
 
 	_GP(play).charProps.resize(_GP(game).numcharacters);
 	_G(old_dialog_scripts) = ents.OldDialogScripts;
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index 76b5f3121d6..89979712db1 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -448,7 +448,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	}
 
 	// Remap old sound nums in case we restored a save having a different list of audio clips
-	RemapLegacySoundNums(_GP(game), _G(views), _G(loaded_game_file_version));
+	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());
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index 94bf11bd1ef..412fa963dcc 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -685,12 +685,12 @@ HSaveError ReadMouseCursors(Stream *in, int32_t cmp_ver, const PreservedParams &
 HSaveError WriteViews(Stream *out) {
 	out->WriteInt32(_GP(game).numviews);
 	for (int view = 0; view < _GP(game).numviews; ++view) {
-		out->WriteInt32(_G(views)[view].numLoops);
-		for (int loop = 0; loop < _G(views)[view].numLoops; ++loop) {
-			out->WriteInt32(_G(views)[view].loops[loop].numFrames);
-			for (int frame = 0; frame < _G(views)[view].loops[loop].numFrames; ++frame) {
-				out->WriteInt32(_G(views)[view].loops[loop].frames[frame].sound);
-				out->WriteInt32(_G(views)[view].loops[loop].frames[frame].pic);
+		out->WriteInt32(_GP(views)[view].numLoops);
+		for (int loop = 0; loop < _GP(views)[view].numLoops; ++loop) {
+			out->WriteInt32(_GP(views)[view].loops[loop].numFrames);
+			for (int frame = 0; frame < _GP(views)[view].loops[loop].numFrames; ++frame) {
+				out->WriteInt32(_GP(views)[view].loops[loop].frames[frame].sound);
+				out->WriteInt32(_GP(views)[view].loops[loop].frames[frame].pic);
 			}
 		}
 	}
@@ -702,16 +702,16 @@ HSaveError ReadViews(Stream *in, int32_t cmp_ver, const PreservedParams &pp, Res
 	if (!AssertGameContent(err, in->ReadInt32(), _GP(game).numviews, "Views"))
 		return err;
 	for (int view = 0; view < _GP(game).numviews; ++view) {
-		if (!AssertGameObjectContent(err, in->ReadInt32(), _G(views)[view].numLoops,
+		if (!AssertGameObjectContent(err, in->ReadInt32(), _GP(views)[view].numLoops,
 		                             "Loops", "View", view))
 			return err;
-		for (int loop = 0; loop < _G(views)[view].numLoops; ++loop) {
-			if (!AssertGameObjectContent2(err, in->ReadInt32(), _G(views)[view].loops[loop].numFrames,
+		for (int loop = 0; loop < _GP(views)[view].numLoops; ++loop) {
+			if (!AssertGameObjectContent2(err, in->ReadInt32(), _GP(views)[view].loops[loop].numFrames,
 			                              "Frame", "View", view, "Loop", loop))
 				return err;
-			for (int frame = 0; frame < _G(views)[view].loops[loop].numFrames; ++frame) {
-				_G(views)[view].loops[loop].frames[frame].sound = in->ReadInt32();
-				_G(views)[view].loops[loop].frames[frame].pic = in->ReadInt32();
+			for (int frame = 0; frame < _GP(views)[view].loops[loop].numFrames; ++frame) {
+				_GP(views)[view].loops[loop].frames[frame].sound = in->ReadInt32();
+				_GP(views)[view].loops[loop].frames[frame].pic = in->ReadInt32();
 			}
 		}
 	}
diff --git a/engines/ags/engine/game/savegame_v321.cpp b/engines/ags/engine/game/savegame_v321.cpp
index 61fd8d37276..004cec8a678 100644
--- a/engines/ags/engine/game/savegame_v321.cpp
+++ b/engines/ags/engine/game/savegame_v321.cpp
@@ -344,10 +344,10 @@ static HSaveError restore_game_views(Stream *in) {
 	}
 
 	for (int bb = 0; bb < _GP(game).numviews; bb++) {
-		for (int cc = 0; cc < _G(views)[bb].numLoops; cc++) {
-			for (int dd = 0; dd < _G(views)[bb].loops[cc].numFrames; dd++) {
-				_G(views)[bb].loops[cc].frames[dd].sound = in->ReadInt32();
-				_G(views)[bb].loops[cc].frames[dd].pic = in->ReadInt32();
+		for (int cc = 0; cc < _GP(views)[bb].numLoops; cc++) {
+			for (int dd = 0; dd < _GP(views)[bb].loops[cc].numFrames; dd++) {
+				_GP(views)[bb].loops[cc].frames[dd].sound = in->ReadInt32();
+				_GP(views)[bb].loops[cc].frames[dd].pic = in->ReadInt32();
 			}
 		}
 	}
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 7b8005e592d..2a09c070afa 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -640,7 +640,7 @@ void engine_init_game_settings() {
 			// set initial loop to 0
 			_GP(game).chars[ee].loop = 0;
 			// or to 1 if they don't have up/down frames
-			if (_G(views)[_GP(game).chars[ee].view].loops[0].numFrames < 1)
+			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;
diff --git a/engines/ags/engine/main/game_file.cpp b/engines/ags/engine/main/game_file.cpp
index 7ea34c89140..25a9ad47e3c 100644
--- a/engines/ags/engine/main/game_file.cpp
+++ b/engines/ags/engine/main/game_file.cpp
@@ -170,7 +170,7 @@ HError LoadGameScripts(LoadedGameEntities &ents, GameDataVersion data_ver) {
 
 HError load_game_file() {
 	MainGameSource src;
-	LoadedGameEntities ents(_GP(game), _G(dialog), _G(views));
+	LoadedGameEntities ents(_GP(game), _G(dialog));
 	HError err = (HError)OpenMainGameFileFromDefaultAsset(src);
 	if (err) {
 		err = (HError)ReadGameData(ents, src.InputStream.get(), src.DataVersion);
diff --git a/engines/ags/engine/main/update.cpp b/engines/ags/engine/main/update.cpp
index a927dbb9eb0..4e4959a6da4 100644
--- a/engines/ags/engine/main/update.cpp
+++ b/engines/ags/engine/main/update.cpp
@@ -294,12 +294,12 @@ void update_sierra_speech() {
 				}
 			} else if (_G(facetalkchar)->blinktimer < 0) {
 				// currently playing blink anim
-				if (_G(facetalkchar)->blinktimer < ((0 - 6) - _G(views)[_G(facetalkchar)->blinkview].loops[_G(facetalkBlinkLoop)].frames[_G(facetalkchar)->blinkframe].speed)) {
+				if (_G(facetalkchar)->blinktimer < ((0 - 6) - _GP(views)[_G(facetalkchar)->blinkview].loops[_G(facetalkBlinkLoop)].frames[_G(facetalkchar)->blinkframe].speed)) {
 					// time to advance to next frame
 					_G(facetalkchar)->blinktimer = -1;
 					_G(facetalkchar)->blinkframe++;
 					updatedFrame = 2;
-					if (_G(facetalkchar)->blinkframe >= _G(views)[_G(facetalkchar)->blinkview].loops[_G(facetalkBlinkLoop)].numFrames) {
+					if (_G(facetalkchar)->blinkframe >= _GP(views)[_G(facetalkchar)->blinkview].loops[_G(facetalkBlinkLoop)].numFrames) {
 						_G(facetalkchar)->blinkframe = 0;
 						_G(facetalkchar)->blinktimer = _G(facetalkchar)->blinkinterval;
 					}
@@ -322,7 +322,7 @@ void update_sierra_speech() {
 					else
 						_G(facetalkframe) = _G(splipsync)[_G(curLipLine)].frame[_G(curLipLinePhoneme)];
 
-					if (_G(facetalkframe) >= _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].numFrames)
+					if (_G(facetalkframe) >= _GP(views)[_G(facetalkview)].loops[_G(facetalkloop)].numFrames)
 						_G(facetalkframe) = 0;
 
 					updatedFrame |= 1;
@@ -354,11 +354,11 @@ void update_sierra_speech() {
 			} else {
 				// normal non-lip-sync
 				_G(facetalkframe)++;
-				if ((_G(facetalkframe) >= _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].numFrames) ||
+				if ((_G(facetalkframe) >= _GP(views)[_G(facetalkview)].loops[_G(facetalkloop)].numFrames) ||
 				        (!_GP(play).speech_has_voice && (_GP(play).messagetime < 1) && (_GP(play).close_mouth_speech_time > 0))) {
 
-					if ((_G(facetalkframe) >= _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].numFrames) &&
-					        (_G(views)[_G(facetalkview)].loops[_G(facetalkloop)].RunNextLoop())) {
+					if ((_G(facetalkframe) >= _GP(views)[_G(facetalkview)].loops[_G(facetalkloop)].numFrames) &&
+					        (_GP(views)[_G(facetalkview)].loops[_G(facetalkloop)].RunNextLoop())) {
 						_G(facetalkloop)++;
 					} else {
 						_G(facetalkloop) = 0;
@@ -368,7 +368,7 @@ void update_sierra_speech() {
 						_G(facetalkwait) = 999999;
 				}
 				if ((_G(facetalkframe) != 0) || (_G(facetalkrepeat) == 1))
-					_G(facetalkwait) = _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].frames[_G(facetalkframe)].speed + GetCharacterSpeechAnimationDelay(_G(facetalkchar));
+					_G(facetalkwait) = _GP(views)[_G(facetalkview)].loops[_G(facetalkloop)].frames[_G(facetalkframe)].speed + GetCharacterSpeechAnimationDelay(_G(facetalkchar));
 			}
 			updatedFrame |= 1;
 		}
@@ -381,7 +381,7 @@ void update_sierra_speech() {
 			if (updatedFrame & 2)
 				CheckViewFrame(_G(facetalkchar)->blinkview, _G(facetalkBlinkLoop), _G(facetalkchar)->blinkframe);
 
-			int thisPic = _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].frames[_G(facetalkframe)].pic;
+			int thisPic = _GP(views)[_G(facetalkview)].loops[_G(facetalkloop)].frames[_G(facetalkframe)].pic;
 			int view_frame_x = 0;
 			int view_frame_y = 0;
 
@@ -401,12 +401,12 @@ void update_sierra_speech() {
 			}
 
 			Bitmap *frame_pic = _GP(screenover)[_G(face_talking)].pic;
-			const ViewFrame *face_vf = &_G(views)[_G(facetalkview)].loops[_G(facetalkloop)].frames[_G(facetalkframe)];
+			const ViewFrame *face_vf = &_GP(views)[_G(facetalkview)].loops[_G(facetalkloop)].frames[_G(facetalkframe)];
 			bool face_has_alpha = (_GP(game).SpriteInfos[face_vf->pic].Flags & SPF_ALPHACHANNEL) != 0;
 			DrawViewFrame(frame_pic, face_vf, view_frame_x, view_frame_y);
 
 			if ((_G(facetalkchar)->blinkview > 0) && (_G(facetalkchar)->blinktimer < 0)) {
-				ViewFrame *blink_vf = &_G(views)[_G(facetalkchar)->blinkview].loops[_G(facetalkBlinkLoop)].frames[_G(facetalkchar)->blinkframe];
+				ViewFrame *blink_vf = &_GP(views)[_G(facetalkchar)->blinkview].loops[_G(facetalkBlinkLoop)].frames[_G(facetalkchar)->blinkframe];
 				face_has_alpha |= (_GP(game).SpriteInfos[blink_vf->pic].Flags & SPF_ALPHACHANNEL) != 0;
 				// draw the blinking sprite on top
 				DrawViewFrame(frame_pic, blink_vf, view_frame_x, view_frame_y, face_has_alpha);
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index a8de8218d09..03f9fea61be 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -228,6 +228,7 @@ Globals::Globals() {
 	_scrRegion = new ScriptRegion[MAX_ROOM_REGIONS];
 	_scrInv = new ScriptInvItem[MAX_INV];
 	_objcache = new ObjectCache[MAX_ROOM_OBJECTS];
+	_views = new std::vector<ViewStruct>();
 	_saveGameDirectory = AGS::Shared::SAVE_FOLDER_PREFIX;
 
 	// game_init.cpp globals
@@ -474,6 +475,7 @@ Globals::~Globals() {
 	delete[] _scrRegion;
 	delete[] _scrInv;
 	delete[] _objcache;
+	delete _views;
 
 	// game_init.cpp globals
 	delete _StaticCharacterArray;
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index f15e523ff27..3020e6d9de4 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -737,7 +737,7 @@ public:
 	ScriptRegion *_scrRegion;
 	ScriptInvItem *_scrInv;
 	ScriptDialog *_scrDialog = nullptr;
-	ViewStruct *_views = nullptr;
+	std::vector<ViewStruct> *_views;
 	CharacterCache *_charcache = nullptr;
 	ObjectCache *_objcache;
 	MoveList *_mls = nullptr;
diff --git a/engines/ags/plugins/ags_plugin.cpp b/engines/ags/plugins/ags_plugin.cpp
index 7621823a65d..108c12b17d6 100644
--- a/engines/ags/plugins/ags_plugin.cpp
+++ b/engines/ags/plugins/ags_plugin.cpp
@@ -433,12 +433,12 @@ AGSViewFrame *IAGSEngine::GetViewFrame(int32 view, int32 loop, int32 frame) {
 	view--;
 	if ((view < 0) || (view >= _GP(game).numviews))
 		quit("!IAGSEngine::GetViewFrame: invalid view");
-	if ((loop < 0) || (loop >= _G(views)[view].numLoops))
+	if ((loop < 0) || (loop >= _GP(views)[view].numLoops))
 		quit("!IAGSEngine::GetViewFrame: invalid loop");
-	if ((frame < 0) || (frame >= _G(views)[view].loops[loop].numFrames))
+	if ((frame < 0) || (frame >= _GP(views)[view].loops[loop].numFrames))
 		return nullptr;
 
-	return (AGSViewFrame *)&_G(views)[view].loops[loop].frames[frame];
+	return (AGSViewFrame *)&_GP(views)[view].loops[loop].frames[frame];
 }
 
 int IAGSEngine::GetRawPixelColor(int32 color) {
diff --git a/engines/ags/shared/ac/view.cpp b/engines/ags/shared/ac/view.cpp
index 74ea0030cda..1db120a3228 100644
--- a/engines/ags/shared/ac/view.cpp
+++ b/engines/ags/shared/ac/view.cpp
@@ -63,8 +63,7 @@ void ViewFrame::WriteToFile(Stream *out) {
 
 ViewLoopNew::ViewLoopNew()
 	: numFrames(0)
-	, flags(0)
-	, frames(nullptr) {
+	, flags(0) {
 }
 
 bool ViewLoopNew::RunNextLoop() {
@@ -74,15 +73,13 @@ bool ViewLoopNew::RunNextLoop() {
 void ViewLoopNew::Initialize(int frameCount) {
 	numFrames = frameCount;
 	flags = 0;
-	frames = (ViewFrame *)calloc(numFrames + 1, sizeof(ViewFrame));
+	// an extra frame is allocated o prevent crashes with empty loops
+	frames.resize(numFrames > 0 ? numFrames : 1);
 }
 
 void ViewLoopNew::Dispose() {
-	if (frames != nullptr) {
-		free(frames);
-		frames = nullptr;
-		numFrames = 0;
-	}
+	frames.clear();
+	numFrames = 0;
 }
 
 void ViewLoopNew::WriteToFile_v321(Stream *out) {
@@ -103,10 +100,6 @@ void ViewLoopNew::ReadFromFile_v321(Stream *in) {
 	Initialize(in->ReadInt16());
 	flags = in->ReadInt32();
 	ReadFrames_Aligned(in);
-
-	// an extra frame is allocated in memory to prevent
-	// crashes with empty loops -- set its picture to teh BLUE CUP!!
-	frames[numFrames].pic = 0;
 }
 
 void ViewLoopNew::ReadFrames_Aligned(Stream *in) {
@@ -118,22 +111,17 @@ void ViewLoopNew::ReadFrames_Aligned(Stream *in) {
 }
 
 ViewStruct::ViewStruct()
-	: numLoops(0)
-	, loops(nullptr) {
+	: numLoops(0) {
 }
 
 void ViewStruct::Initialize(int loopCount) {
 	numLoops = loopCount;
-	if (numLoops > 0) {
-		loops = (ViewLoopNew *)calloc(numLoops, sizeof(ViewLoopNew));
-	}
+	loops.resize(numLoops);
 }
 
 void ViewStruct::Dispose() {
-	if (numLoops > 0) {
-		free(loops);
-		numLoops = 0;
-	}
+	loops.clear();
+	numLoops = 0;
 }
 
 void ViewStruct::WriteToFile(Stream *out) {
@@ -170,7 +158,7 @@ void ViewStruct272::ReadFromFile(Stream *in) {
 	}
 }
 
-void Convert272ViewsToNew(const std::vector<ViewStruct272> &oldv, ViewStruct *newv) {
+void Convert272ViewsToNew(const std::vector<ViewStruct272> &oldv, std::vector<ViewStruct> &newv) {
 	for (size_t a = 0; a < oldv.size(); a++) {
 		newv[a].Initialize(oldv[a].numloops);
 
diff --git a/engines/ags/shared/ac/view.h b/engines/ags/shared/ac/view.h
index 181e95eb252..906c9da00a0 100644
--- a/engines/ags/shared/ac/view.h
+++ b/engines/ags/shared/ac/view.h
@@ -58,7 +58,10 @@ struct ViewFrame {
 struct ViewLoopNew {
 	short numFrames;
 	int   flags;
-	ViewFrame *frames;
+	std::vector<ViewFrame> frames;
+	// NOTE: we still need numFrames for backward compatibility:
+	// some older versions could allocate extra frame(s) for safety,
+	// but have to report "logical" number of frames for the engine API.
 
 	ViewLoopNew();
 	void Initialize(int frameCount);
@@ -72,7 +75,7 @@ struct ViewLoopNew {
 
 struct ViewStruct {
 	short numLoops;
-	ViewLoopNew *loops;
+	std::vector<ViewLoopNew> loops;
 
 	ViewStruct();
 	void Initialize(int loopCount);
@@ -91,7 +94,7 @@ struct ViewStruct272 {
 	void ReadFromFile(Shared::Stream *in);
 };
 
-void Convert272ViewsToNew(const std::vector<ViewStruct272> &oldv, ViewStruct *newv);
+extern void Convert272ViewsToNew(const std::vector<ViewStruct272> &oldv, std::vector<ViewStruct> &newv);
 
 } // namespace AGS3
 
diff --git a/engines/ags/shared/game/main_game_file.cpp b/engines/ags/shared/game/main_game_file.cpp
index dcbc0a1d33e..f2b3bf75039 100644
--- a/engines/ags/shared/game/main_game_file.cpp
+++ b/engines/ags/shared/game/main_game_file.cpp
@@ -98,10 +98,9 @@ String GetMainGameFileErrorText(MainGameFileErrorType err) {
 	return "Unknown error.";
 }
 
-LoadedGameEntities::LoadedGameEntities(GameSetupStruct &game, DialogTopic *&dialogs, ViewStruct *&views)
+LoadedGameEntities::LoadedGameEntities(GameSetupStruct &game, DialogTopic *&dialogs)
 	: Game(game)
 	, Dialogs(dialogs)
-	, Views(views)
 	, SpriteCount(0) {
 }
 
@@ -254,16 +253,17 @@ void ReadViewStruct272_Aligned(std::vector<ViewStruct272> &oldv, Stream *in, siz
 	}
 }
 
-void ReadViews(GameSetupStruct &game, ViewStruct *&views, Stream *in, GameDataVersion data_ver) {
-	int count = _GP(game).numviews;
-	views = (ViewStruct *)calloc(sizeof(ViewStruct) * count, 1);
-	if (data_ver > kGameVersion_272) { // 3.x views
-		for (int i = 0; i < _GP(game).numviews; ++i) {
-			_G(views)[i].ReadFromFile(in);
+void ReadViews(GameSetupStruct &game, std::vector<ViewStruct> &views, Stream *in, GameDataVersion data_ver) {
+	views.resize(game.numviews);
+	if (data_ver > kGameVersion_272) // 3.x views
+	{
+		for (int i = 0; i < game.numviews; ++i) {
+			views[i].ReadFromFile(in);
 		}
-	} else { // 2.x views
+	} else // 2.x views
+	{
 		std::vector<ViewStruct272> oldv;
-		ReadViewStruct272_Aligned(oldv, in, count);
+		ReadViewStruct272_Aligned(oldv, in, game.numviews);
 		Convert272ViewsToNew(oldv, views);
 	}
 }
@@ -608,7 +608,7 @@ void UpgradeMouseCursors(GameSetupStruct &game, GameDataVersion data_ver) {
 }
 
 // Adjusts score clip id, depending on game data version
-void RemapLegacySoundNums(GameSetupStruct &game, ViewStruct *&views, GameDataVersion data_ver) {
+void RemapLegacySoundNums(GameSetupStruct &game, std::vector<ViewStruct> &views, GameDataVersion data_ver) {
 	if (data_ver >= kGameVersion_320)
 		return;
 
diff --git a/engines/ags/shared/game/main_game_file.h b/engines/ags/shared/game/main_game_file.h
index 991067ebb29..4e9a1e9c078 100644
--- a/engines/ags/shared/game/main_game_file.h
+++ b/engines/ags/shared/game/main_game_file.h
@@ -36,6 +36,7 @@
 #include "ags/lib/std/vector.h"
 #include "ags/shared/core/platform.h"
 #include "ags/shared/ac/game_version.h"
+#include "ags/shared/ac/view.h"
 #include "ags/shared/game/plugin_info.h"
 #include "ags/shared/script/cc_script.h"
 #include "ags/shared/util/error.h"
@@ -47,7 +48,6 @@ namespace AGS3 {
 
 struct GameSetupStruct;
 struct DialogTopic;
-struct ViewStruct;
 
 namespace AGS {
 namespace Shared {
@@ -117,7 +117,7 @@ struct MainGameSource {
 struct LoadedGameEntities {
 	GameSetupStruct &Game;
 	DialogTopic *&Dialogs;
-	ViewStruct *&Views;
+	std::vector<ViewStruct> Views;
 	PScript                 GlobalScript;
 	PScript                 DialogScript;
 	std::vector<PScript>    ScriptModules;
@@ -136,7 +136,7 @@ struct LoadedGameEntities {
 	// speech texts displayed during dialog
 	std::vector<String>     OldSpeechLines;
 
-	LoadedGameEntities(GameSetupStruct &game, DialogTopic *&dialogs, ViewStruct *&views);
+	LoadedGameEntities(GameSetupStruct &game, DialogTopic *&dialogs);
 	~LoadedGameEntities();
 };
 
@@ -159,7 +159,7 @@ HGameFileError     UpdateGameData(LoadedGameEntities &ents, GameDataVersion data
 // Ensures that the game saves directory path is valid
 void               FixupSaveDirectory(GameSetupStruct &game);
 // Maps legacy sound numbers to real audio clips
-void               RemapLegacySoundNums(GameSetupStruct &game, ViewStruct *&views, GameDataVersion data_ver);
+void               RemapLegacySoundNums(GameSetupStruct &game, std::vector<ViewStruct> &views, GameDataVersion data_ver);
 
 } // namespace Shared
 } // namespace AGS


Commit: c25b08448d6f0b3103b8cf3a0a619bdd42919f11
    https://github.com/scummvm/scummvm/commit/c25b08448d6f0b3103b8cf3a0a619bdd42919f11
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-18T21:29:56-07:00

Commit Message:
AGS: Removed AudioChannelsLock as it's no longer needed

>From upstream 43f9320f66bb881920e6f812d1c642e793b134b6

Changed paths:
    engines/ags/engine/ac/audio_channel.cpp
    engines/ags/engine/ac/audio_clip.cpp
    engines/ags/engine/ac/display.cpp
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/global_audio.cpp
    engines/ags/engine/ac/view_frame.cpp
    engines/ags/engine/game/savegame.cpp
    engines/ags/engine/game/savegame_components.cpp
    engines/ags/engine/main/update.cpp
    engines/ags/engine/media/audio/ambient_sound.cpp
    engines/ags/engine/media/audio/audio.cpp
    engines/ags/engine/media/audio/audio.h
    engines/ags/plugins/ags_plugin.cpp


diff --git a/engines/ags/engine/ac/audio_channel.cpp b/engines/ags/engine/ac/audio_channel.cpp
index 57cc2a7b672..ca09077576a 100644
--- a/engines/ags/engine/ac/audio_channel.cpp
+++ b/engines/ags/engine/ac/audio_channel.cpp
@@ -46,19 +46,18 @@ int AudioChannel_GetIsPlaying(ScriptAudioChannel *channel) {
 		return 0;
 	}
 
-	return channel_is_playing(channel->id) ? 1 : 0;
+	return AudioChans::ChannelIsPlaying(channel->id) ? 1 : 0;
 }
 
 bool AudioChannel_GetIsPaused(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
+
 	if (ch) return ch->is_paused();
 	return false;
 }
 
 int AudioChannel_GetPanning(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		return ch->_panningAsPercentage;
@@ -70,8 +69,7 @@ void AudioChannel_SetPanning(ScriptAudioChannel *channel, int newPanning) {
 	if ((newPanning < -100) || (newPanning > 100))
 		quitprintf("!AudioChannel.Panning: panning value must be between -100 and 100 (passed=%d)", newPanning);
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		ch->set_panning(((newPanning + 100) * 255) / 200);
@@ -80,8 +78,7 @@ void AudioChannel_SetPanning(ScriptAudioChannel *channel, int newPanning) {
 }
 
 ScriptAudioClip *AudioChannel_GetPlayingClip(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		return (ScriptAudioClip *)ch->_sourceClip;
@@ -90,8 +87,7 @@ ScriptAudioClip *AudioChannel_GetPlayingClip(ScriptAudioChannel *channel) {
 }
 
 int AudioChannel_GetPosition(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		if (_GP(play).fast_forward)
@@ -103,8 +99,7 @@ int AudioChannel_GetPosition(ScriptAudioChannel *channel) {
 }
 
 int AudioChannel_GetPositionMs(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		if (_GP(play).fast_forward)
@@ -116,8 +111,7 @@ int AudioChannel_GetPositionMs(ScriptAudioChannel *channel) {
 }
 
 int AudioChannel_GetLengthMs(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		return ch->get_length_ms();
@@ -126,8 +120,7 @@ int AudioChannel_GetLengthMs(ScriptAudioChannel *channel) {
 }
 
 int AudioChannel_GetVolume(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		return ch->get_volume();
@@ -139,8 +132,7 @@ int AudioChannel_SetVolume(ScriptAudioChannel *channel, int newVolume) {
 	if ((newVolume < 0) || (newVolume > 100))
 		quitprintf("!AudioChannel.Volume: new value out of range (supplied: %d, range: 0..100)", newVolume);
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		ch->set_volume_percent(newVolume);
@@ -149,8 +141,7 @@ int AudioChannel_SetVolume(ScriptAudioChannel *channel, int newVolume) {
 }
 
 int AudioChannel_GetSpeed(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		return ch->get_speed();
@@ -159,8 +150,7 @@ int AudioChannel_GetSpeed(ScriptAudioChannel *channel) {
 }
 
 void AudioChannel_SetSpeed(ScriptAudioChannel *channel, int new_speed) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		ch->set_speed(new_speed);
@@ -175,14 +165,12 @@ void AudioChannel_Stop(ScriptAudioChannel *channel) {
 }
 
 void AudioChannel_Pause(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 	if (ch) ch->pause();
 }
 
 void AudioChannel_Resume(ScriptAudioChannel *channel) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 	if (ch) ch->resume();
 }
 
@@ -190,8 +178,7 @@ void AudioChannel_Seek(ScriptAudioChannel *channel, int newPosition) {
 	if (newPosition < 0)
 		quitprintf("!AudioChannel.Seek: invalid seek position %d", newPosition);
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		ch->seek(newPosition);
@@ -199,8 +186,7 @@ void AudioChannel_Seek(ScriptAudioChannel *channel, int newPosition) {
 }
 
 void AudioChannel_SetRoomLocation(ScriptAudioChannel *channel, int xPos, int yPos) {
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(channel->id);
+	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
 	if (ch) {
 		int maxDist = ((xPos > _GP(thisroom).Width / 2) ? xPos : (_GP(thisroom).Width - xPos)) - AMBIENCE_FULL_DIST;
diff --git a/engines/ags/engine/ac/audio_clip.cpp b/engines/ags/engine/ac/audio_clip.cpp
index b7ef9b78a4a..5d2e67bb001 100644
--- a/engines/ags/engine/ac/audio_clip.cpp
+++ b/engines/ags/engine/ac/audio_clip.cpp
@@ -47,9 +47,8 @@ int AudioClip_GetIsAvailable(ScriptAudioClip *clip) {
 }
 
 void AudioClip_Stop(ScriptAudioClip *clip) {
-	AudioChannelsLock lock;
 	for (int i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; i++) {
-		auto *ch = lock.GetChannelIfPlaying(i);
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
 		if ((ch != nullptr) && (ch->_sourceClip == clip)) {
 			AudioChannel_Stop(&_G(scrAudioChannel)[i]);
 		}
diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 62a7d458bec..0a76d29caac 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -318,7 +318,7 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 			// Special behavior when coupled with a voice-over
 			if (_GP(play).speech_has_voice) {
 				// extend life of text if the voice hasn't finished yet
-				if (channel_is_playing(SCHAN_SPEECH) && (_GP(play).fast_forward == 0)) {
+				if (AudioChans::ChannelIsPlaying(SCHAN_SPEECH) && (_GP(play).fast_forward == 0)) {
 					if (countdown <= 1)
 						countdown = 1;
 				} else  // if the voice has finished, remove the speech
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index d0087059cb3..3adcc50b5d8 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -145,11 +145,10 @@ void Game_SetAudioTypeVolume(int audioType, int volume, int changeType) {
 	Debug::Printf("Game.SetAudioTypeVolume: type: %d, volume: %d, change: %d", audioType, volume, changeType);
 	if ((changeType == VOL_CHANGEEXISTING) ||
 	        (changeType == VOL_BOTH)) {
-		AudioChannelsLock lock;
 		for (int aa = 0; aa < _GP(game).numGameChannels; aa++) {
 			ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
 			if ((clip != nullptr) && (clip->type == audioType)) {
-				auto *ch = lock.GetChannel(aa);
+				auto *ch = AudioChans::GetChannel(aa);
 				if (ch)
 					ch->set_volume_percent(volume);
 			}
@@ -169,8 +168,7 @@ void Game_SetAudioTypeVolume(int audioType, int volume, int changeType) {
 int Game_GetMODPattern() {
 	if (_G(current_music_type) != MUS_MOD)
 		return -1;
-	AudioChannelsLock lock;
-	auto *music_ch = lock.GetChannelIfPlaying(SCHAN_MUSIC);
+	auto *music_ch = AudioChans::GetChannelIfPlaying(SCHAN_MUSIC);
 	return music_ch ? music_ch->get_pos() : -1;
 }
 
@@ -1127,17 +1125,13 @@ void stop_fast_forwarding() {
 	if (_GP(play).end_cutscene_music >= 0)
 		newmusic(_GP(play).end_cutscene_music);
 
-	{
-		AudioChannelsLock lock;
-
-		// Restore actual volume of sounds
-		for (int aa = 0; aa < TOTAL_AUDIO_CHANNELS; aa++) {
-			auto *ch = lock.GetChannelIfPlaying(aa);
-			if (ch) {
-				ch->set_mute(false);
-			}
+	// Restore actual volume of sounds
+	for (int aa = 0; aa < TOTAL_AUDIO_CHANNELS; aa++) {
+		auto *ch = AudioChans::GetChannelIfPlaying(aa);
+		if (ch) {
+			ch->set_mute(false);
 		}
-	} // -- AudioChannelsLock
+	}
 
 	update_music_volume();
 }
@@ -1237,16 +1231,13 @@ void display_switch_out_suspend() {
 
 	// TODO: find out if anything has to be done here for SDL backend
 
-	{
-		// stop the sound stuttering
-		AudioChannelsLock lock;
-		for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
-			auto *ch = lock.GetChannelIfPlaying(i);
-			if (ch) {
-				ch->pause();
-			}
+	// stop the sound stuttering
+	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
+		if (ch) {
+			ch->pause();
 		}
-	} // -- AudioChannelsLock
+	}
 
 	// restore the callbacks
 	SetMultitasking(0);
@@ -1267,15 +1258,12 @@ void display_switch_in() {
 void display_switch_in_resume() {
 	display_switch_in();
 
-	{
-		AudioChannelsLock lock;
-		for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
-			auto *ch = lock.GetChannelIfPlaying(i);
-			if (ch) {
-				ch->resume();
-			}
+	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
+		if (ch) {
+			ch->resume();
 		}
-	} // -- AudioChannelsLock
+	}
 
 	// clear the screen if necessary
 	if (_G(gfxDriver) && _G(gfxDriver)->UsesMemoryBackBuffer())
diff --git a/engines/ags/engine/ac/global_audio.cpp b/engines/ags/engine/ac/global_audio.cpp
index d9fcbabb1f6..06e1d5128be 100644
--- a/engines/ags/engine/ac/global_audio.cpp
+++ b/engines/ags/engine/ac/global_audio.cpp
@@ -63,8 +63,8 @@ void PlayAmbientSound(int channel, int sndnum, int vol, int x, int y) {
 		return;
 
 	// only play the sound if it's not already playing
-	if ((_GP(ambient)[channel].channel < 1) || (!channel_is_playing(_GP(ambient)[channel].channel)) ||
-	        (_GP(ambient)[channel].num != sndnum)) {
+	if ((_GP(ambient)[channel].channel < 1) || (!AudioChans::ChannelIsPlaying(_GP(ambient)[channel].channel)) ||
+			(_GP(ambient)[channel].num != sndnum)) {
 
 		StopAmbientSound(channel);
 		// in case a normal non-ambient sound was playing, stop it too
@@ -80,7 +80,7 @@ void PlayAmbientSound(int channel, int sndnum, int vol, int x, int y) {
 		debug_script_log("Playing ambient sound %d on channel %d", sndnum, channel);
 		_GP(ambient)[channel].channel = channel;
 		asound->_priority = 15;  // ambient sound higher priority than normal sfx
-		set_clip_to_channel(channel, asound);
+		AudioChans::SetChannel(channel, asound);
 	}
 	// calculate the maximum distance away the player can be, using X
 	// only (since X centred is still more-or-less total Y)
@@ -99,7 +99,7 @@ int IsChannelPlaying(int chan) {
 	if ((chan < 0) || (chan >= _GP(game).numGameChannels))
 		quit("!IsChannelPlaying: invalid sound channel");
 
-	if (channel_is_playing(chan))
+	if (AudioChans::ChannelIsPlaying(chan))
 		return 1;
 
 	return 0;
@@ -110,9 +110,8 @@ int IsSoundPlaying() {
 		return 0;
 
 	// find if there's a sound playing
-	AudioChannelsLock lock;
 	for (int i = SCHAN_NORMAL; i < _GP(game).numGameChannels; i++) {
-		if (lock.GetChannelIfPlaying(i))
+		if (AudioChans::GetChannelIfPlaying(i))
 			return 1;
 	}
 
@@ -156,7 +155,7 @@ int PlaySoundEx(int val1, int channel) {
 
 	soundfx->_priority = 10;
 	soundfx->set_volume(_GP(play).sound_volume);
-	set_clip_to_channel(channel, soundfx);
+	AudioChans::SetChannel(channel, soundfx);
 	return channel;
 }
 
@@ -177,8 +176,7 @@ void SeekMIDIPosition(int position) {
 	if (_GP(play).silent_midi == 0 && _G(current_music_type) != MUS_MIDI)
 		return;
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannel(SCHAN_MUSIC);
+	auto *ch = AudioChans::GetChannel(SCHAN_MUSIC);
 	ch->seek(position);
 	debug_script_log("Seek MIDI position to %d", position);
 }
@@ -189,8 +187,7 @@ int GetMIDIPosition() {
 	if (_GP(play).silent_midi == 0 && _G(current_music_type) != MUS_MIDI)
 		return -1; // returns -1 on failure according to old manuals
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(SCHAN_MUSIC);
+	auto *ch = AudioChans::GetChannelIfPlaying(SCHAN_MUSIC);
 	if (ch) {
 		return ch->get_pos();
 	}
@@ -207,14 +204,13 @@ int IsMusicPlaying() {
 	if (_G(current_music_type) == 0)
 		return 0;
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannel(SCHAN_MUSIC);
+	auto *ch = AudioChans::GetChannel(SCHAN_MUSIC);
 	if (ch == nullptr) { // This was probably a hacky fix in case it was not reset by game update; TODO: find out if needed
 		_G(current_music_type) = 0;
 		return 0;
 	}
 
-	bool result = (ch->is_playing()) || (_G(crossFading) > 0 && (lock.GetChannelIfPlaying(_G(crossFading)) != nullptr));
+	bool result = (ch->is_playing()) || (_G(crossFading) > 0 && (AudioChans::GetChannelIfPlaying(_G(crossFading)) != nullptr));
 	return result ? 1 : 0;
 }
 
@@ -269,8 +265,7 @@ void SeekMODPattern(int patnum) {
 	if (_G(current_music_type) != MUS_MOD)
 		return;
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(SCHAN_MUSIC);
+	auto *ch = AudioChans::GetChannelIfPlaying(SCHAN_MUSIC);
 	if (ch) {
 		ch->seek(patnum);
 		debug_script_log("Seek MOD/XM to pattern %d", patnum);
@@ -281,9 +276,8 @@ void SeekMP3PosMillis(int posn) {
 	if (_G(current_music_type) != MUS_MP3 && _G(current_music_type) != MUS_OGG)
 		return;
 
-	AudioChannelsLock lock;
-	auto *mus_ch = lock.GetChannel(SCHAN_MUSIC);
-	auto *cf_ch = (_G(crossFading) > 0) ? lock.GetChannel(_G(crossFading)) : nullptr;
+	auto *mus_ch = AudioChans::GetChannel(SCHAN_MUSIC);
+	auto *cf_ch = (_G(crossFading) > 0) ? AudioChans::GetChannel(_G(crossFading)) : nullptr;
 	if (cf_ch)
 		cf_ch->seek(posn);
 	else if (mus_ch)
@@ -297,8 +291,7 @@ int GetMP3PosMillis() {
 	if (_G(current_music_type) != MUS_MP3 && _G(current_music_type) != MUS_OGG)
 		return 0;  // returns 0 on failure according to old manuals
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(SCHAN_MUSIC);
+	auto *ch = AudioChans::GetChannelIfPlaying(SCHAN_MUSIC);
 	if (ch) {
 		int result = ch->get_pos_ms();
 		if (result >= 0)
@@ -341,8 +334,7 @@ void SetChannelVolume(int chan, int newvol) {
 	if ((chan < 0) || (chan >= _GP(game).numGameChannels))
 		quit("!SetChannelVolume: invalid channel id");
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannelIfPlaying(chan);
+	auto *ch = AudioChans::GetChannelIfPlaying(chan);
 
 	if (ch) {
 		if (chan == _GP(ambient)[chan].channel) {
@@ -388,7 +380,7 @@ void PlayMP3File(const char *filename) {
 		clip = my_load_static_ogg(asset_name, 150, doLoop);
 		if (clip) {
 			if (clip->play()) {
-				set_clip_to_channel(useChan, clip);
+				AudioChans::SetChannel(useChan, clip);
 				_G(current_music_type) = MUS_OGG;
 				_GP(play).cur_music_number = 1000;
 				// save the filename (if it's not what we were supplied with)
@@ -406,7 +398,7 @@ void PlayMP3File(const char *filename) {
 		clip = my_load_static_mp3(asset_name, 150, doLoop);
 		if (clip) {
 			if (clip->play()) {
-				set_clip_to_channel(useChan, clip);
+				AudioChans::SetChannel(useChan, clip);
 				_G(current_music_type) = MUS_MP3;
 				_GP(play).cur_music_number = 1000;
 				// save the filename (if it's not what we were supplied with)
@@ -421,7 +413,7 @@ void PlayMP3File(const char *filename) {
 	}
 
 	if (!clip) {
-		set_clip_to_channel(useChan, nullptr);
+		AudioChans::SetChannel(useChan, nullptr);
 		debug_script_warn("PlayMP3File: file '%s' not found or cannot play", filename);
 	}
 
@@ -446,8 +438,8 @@ void PlaySilentMIDI(int mnum) {
 	if (clip == nullptr) {
 		quitprintf("!PlaySilentMIDI: failed to load aMusic%d", mnum);
 	}
-	AudioChannelsLock lock;
-	lock.SetChannel(_GP(play).silent_midi_channel, clip);
+	AudioChans::SetChannel(_GP(play).silent_midi_channel, clip);
+
 	if (!clip->play()) {
 		clip->destroy();
 		delete clip;
@@ -461,8 +453,7 @@ void SetSpeechVolume(int newvol) {
 	if ((newvol < 0) | (newvol > 255))
 		quit("!SetSpeechVolume: invalid volume - must be from 0-255");
 
-	AudioChannelsLock lock;
-	auto *ch = lock.GetChannel(SCHAN_SPEECH);
+	auto *ch = AudioChans::GetChannel(SCHAN_SPEECH);
 	if (ch)
 		ch->set_volume(newvol);
 	_GP(play).speech_volume = newvol;
@@ -550,7 +541,7 @@ static bool play_voice_clip_on_channel(const String &voice_name) {
 		speechmp3 = nullptr;
 	}
 
-	set_clip_to_channel(SCHAN_SPEECH, speechmp3);
+	AudioChans::SetChannel(SCHAN_SPEECH, speechmp3);
 	return true;
 }
 
diff --git a/engines/ags/engine/ac/view_frame.cpp b/engines/ags/engine/ac/view_frame.cpp
index ba403698212..5be2f43f377 100644
--- a/engines/ags/engine/ac/view_frame.cpp
+++ b/engines/ags/engine/ac/view_frame.cpp
@@ -147,8 +147,8 @@ void CheckViewFrame(int view, int loop, int frame, int sound_volume) {
 		}
 	}
 	if (sound_volume != SCR_NO_VALUE && channel != nullptr) {
-		AudioChannelsLock lock;
-		auto *ch = lock.GetChannel(channel->id);
+		auto *ch = AudioChans::GetChannel(channel->id);
+
 		if (ch)
 			ch->set_volume_percent(ch->get_volume() * sound_volume / 100);
 	}
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index 89979712db1..310575c45d2 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -552,47 +552,44 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	_GP(play).crossfading_in_channel = 0;
 	_GP(play).crossfading_out_channel = 0;
 
-	{
-		AudioChannelsLock lock;
-		// NOTE: channels are array of MAX_SOUND_CHANNELS+1 size
-		for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
-			const RestoredData::ChannelInfo &chan_info = r_data.AudioChans[i];
-			if (chan_info.ClipID < 0)
-				continue;
-			if ((size_t)chan_info.ClipID >= _GP(game).audioClips.size()) {
-				return new SavegameError(kSvgErr_GameObjectInitFailed,
-					String::FromFormat("Invalid audio clip index: %d (clip count: %zu).", chan_info.ClipID, _GP(game).audioClips.size()));
-			}
-			play_audio_clip_on_channel(i, &_GP(game).audioClips[chan_info.ClipID],
-			                           chan_info.Priority, chan_info.Repeat, chan_info.Pos);
-
-			auto *ch = lock.GetChannel(i);
-			if (ch != nullptr) {
-				ch->set_volume_direct(chan_info.VolAsPercent, chan_info.Vol);
-				ch->set_speed(chan_info.Speed);
-				ch->set_panning(chan_info.Pan);
-				ch->_panningAsPercentage = chan_info.PanAsPercent;
-				ch->_xSource = chan_info.XSource;
-				ch->_ySource = chan_info.YSource;
-				ch->_maximumPossibleDistanceAway = chan_info.MaxDist;
-			}
+	// NOTE: channels are array of MAX_SOUND_CHANNELS+1 size
+	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
+		const RestoredData::ChannelInfo &chan_info = r_data.AudioChans[i];
+		if (chan_info.ClipID < 0)
+			continue;
+		if ((size_t)chan_info.ClipID >= _GP(game).audioClips.size()) {
+			return new SavegameError(kSvgErr_GameObjectInitFailed,
+				String::FromFormat("Invalid audio clip index: %d (clip count: %zu).", chan_info.ClipID, _GP(game).audioClips.size()));
 		}
-		if ((cf_in_chan > 0) && (lock.GetChannel(cf_in_chan) != nullptr))
-			_GP(play).crossfading_in_channel = cf_in_chan;
-		if ((cf_out_chan > 0) && (lock.GetChannel(cf_out_chan) != nullptr))
-			_GP(play).crossfading_out_channel = cf_out_chan;
-
-		// If there were synced audio tracks, the time taken to load in the
-		// different channels will have thrown them out of sync, so re-time it
-		// NOTE: channels are array of MAX_SOUND_CHANNELS+1 size
-		for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
-			auto *ch = lock.GetChannelIfPlaying(i);
-			int pos = r_data.AudioChans[i].Pos;
-			if ((pos > 0) && (ch != nullptr)) {
-				ch->seek(pos);
-			}
+		play_audio_clip_on_channel(i, &_GP(game).audioClips[chan_info.ClipID],
+			                        chan_info.Priority, chan_info.Repeat, chan_info.Pos);
+
+		auto *ch = AudioChans::GetChannel(i);
+		if (ch != nullptr) {
+			ch->set_volume_direct(chan_info.VolAsPercent, chan_info.Vol);
+			ch->set_speed(chan_info.Speed);
+			ch->set_panning(chan_info.Pan);
+			ch->_panningAsPercentage = chan_info.PanAsPercent;
+			ch->_xSource = chan_info.XSource;
+			ch->_ySource = chan_info.YSource;
+			ch->_maximumPossibleDistanceAway = chan_info.MaxDist;
+		}
+	}
+	if ((cf_in_chan > 0) && (AudioChans::GetChannel(cf_in_chan) != nullptr))
+		_GP(play).crossfading_in_channel = cf_in_chan;
+	if ((cf_out_chan > 0) && (AudioChans::GetChannel(cf_out_chan) != nullptr))
+		_GP(play).crossfading_out_channel = cf_out_chan;
+
+	// If there were synced audio tracks, the time taken to load in the
+	// different channels will have thrown them out of sync, so re-time it
+	// NOTE: channels are array of MAX_SOUND_CHANNELS+1 size
+	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
+		int pos = r_data.AudioChans[i].Pos;
+		if ((pos > 0) && (ch != nullptr)) {
+			ch->seek(pos);
 		}
-	} // -- AudioChannelsLock
+	}
 
 	for (int i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; ++i) {
 		if (r_data.DoAmbient[i])
@@ -625,10 +622,8 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 
 	// Test if the old-style audio had playing music and it was properly loaded
 	if (_G(current_music_type) > 0) {
-		AudioChannelsLock lock;
-
-		if ((_G(crossFading) > 0 && !lock.GetChannelIfPlaying(_G(crossFading))) ||
-		        (_G(crossFading) <= 0 && !lock.GetChannelIfPlaying(SCHAN_MUSIC))) {
+		if ((_G(crossFading) > 0 && !AudioChans::GetChannelIfPlaying(_G(crossFading))) ||
+				(_G(crossFading) <= 0 && !AudioChans::GetChannelIfPlaying(SCHAN_MUSIC))) {
 			_G(current_music_type) = 0; // playback failed, reset flag
 		}
 	}
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index 412fa963dcc..a2c6c803fe8 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -332,8 +332,6 @@ HSaveError ReadGameState(Stream *in, int32_t cmp_ver, const PreservedParams &pp,
 }
 
 HSaveError WriteAudio(Stream *out) {
-	AudioChannelsLock lock;
-
 	// Game content assertion
 	out->WriteInt32(_GP(game).audioClipTypes.size());
 	out->WriteInt8(TOTAL_AUDIO_CHANNELS);
@@ -348,7 +346,7 @@ HSaveError WriteAudio(Stream *out) {
 
 	// Audio clips and crossfade
 	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
-		auto *ch = lock.GetChannelIfPlaying(i);
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
 		if ((ch != nullptr) && (ch->_sourceClip != nullptr)) {
 			out->WriteInt32(((ScriptAudioClip *)ch->_sourceClip)->id);
 			out->WriteInt32(ch->get_pos());
diff --git a/engines/ags/engine/main/update.cpp b/engines/ags/engine/main/update.cpp
index 4e4959a6da4..506cf5eecfc 100644
--- a/engines/ags/engine/main/update.cpp
+++ b/engines/ags/engine/main/update.cpp
@@ -235,8 +235,7 @@ void update_overlay_timers() {
 void update_speech_and_messages() {
 	bool is_voice_playing = false;
 	if (_GP(play).speech_has_voice) {
-		AudioChannelsLock lock;
-		auto *ch = lock.GetChannel(SCHAN_SPEECH);
+		auto *ch = AudioChans::GetChannel(SCHAN_SPEECH);
 		is_voice_playing = ch && ch->is_playing();
 	}
 	// determine if speech text should be removed
@@ -276,8 +275,7 @@ void update_speech_and_messages() {
 void update_sierra_speech() {
 	int voice_pos_ms = -1;
 	if (_GP(play).speech_has_voice) {
-		AudioChannelsLock lock;
-		auto *ch = lock.GetChannel(SCHAN_SPEECH);
+		auto *ch = AudioChans::GetChannel(SCHAN_SPEECH);
 		voice_pos_ms = ch ? ch->get_pos_ms() : -1;
 	}
 	if ((_G(face_talking) >= 0) && (_GP(play).fast_forward == 0)) {
diff --git a/engines/ags/engine/media/audio/ambient_sound.cpp b/engines/ags/engine/media/audio/ambient_sound.cpp
index 289d954081f..647ab432ab2 100644
--- a/engines/ags/engine/media/audio/ambient_sound.cpp
+++ b/engines/ags/engine/media/audio/ambient_sound.cpp
@@ -31,7 +31,7 @@ using AGS::Shared::Stream;
 bool AmbientSound::IsPlaying() {
 	if (channel <= 0)
 		return false;
-	return channel_is_playing(channel);
+	return AudioChans::ChannelIsPlaying(channel);
 }
 
 void AmbientSound::ReadFromFile(Stream *in) {
diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index 5a15e0a06e8..437d348dbc5 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -52,19 +52,16 @@ using namespace AGS::Engine;
 //-----------------------
 //sound channel management; all access goes through here, which can't be done without a lock
 
-AudioChannelsLock::AudioChannelsLock() : MutexLock(::AGS::g_vm->_sMutex) {
-}
-
-SOUNDCLIP *AudioChannelsLock::GetChannel(int index) {
+SOUNDCLIP *AudioChans::GetChannel(int index) {
 	return _GP(audioChannels)[index];
 }
 
-SOUNDCLIP *AudioChannelsLock::GetChannelIfPlaying(int index) {
+SOUNDCLIP *AudioChans::GetChannelIfPlaying(int index) {
 	auto *ch = _GP(audioChannels)[index];
 	return (ch != nullptr && ch->is_playing()) ? ch : nullptr;
 }
 
-SOUNDCLIP *AudioChannelsLock::SetChannel(int index, SOUNDCLIP *ch) {
+SOUNDCLIP *AudioChans::SetChannel(int index, SOUNDCLIP *ch) {
 	SoundClipWaveBase *wavClip = dynamic_cast<SoundClipWaveBase *>(ch);
 	if (wavClip) {
 		switch (index) {
@@ -89,31 +86,12 @@ SOUNDCLIP *AudioChannelsLock::SetChannel(int index, SOUNDCLIP *ch) {
 	return ch;
 }
 
-SOUNDCLIP *AudioChannelsLock::MoveChannel(int to, int from) {
+SOUNDCLIP *AudioChans::MoveChannel(int to, int from) {
 	auto from_ch = _GP(audioChannels)[from];
 	_GP(audioChannels)[from] = nullptr;
 	return SetChannel(to, from_ch);
 }
 
-//-----------------------
-// Channel helpers
-
-bool channel_has_clip(int chanid) {
-	AudioChannelsLock lock;
-	return lock.GetChannel(chanid) != nullptr;
-}
-
-bool channel_is_playing(int chanid) {
-	AudioChannelsLock lock;
-	return lock.GetChannelIfPlaying(chanid) != nullptr;
-}
-
-void set_clip_to_channel(int chanid, SOUNDCLIP *clip) {
-	AudioChannelsLock lock;
-	lock.SetChannel(chanid, clip);
-}
-//-----------------------
-
 void calculate_reserved_channel_count() {
 	int reservedChannels = 0;
 	for (size_t i = 0; i < _GP(game).audioClipTypes.size(); i++) {
@@ -139,9 +117,8 @@ void start_fading_in_new_track_if_applicable(int fadeInChannel, ScriptAudioClip
 }
 
 static void move_track_to_crossfade_channel(int currentChannel, int crossfadeSpeed, int fadeInChannel, ScriptAudioClip *newSound) {
-	AudioChannelsLock lock;
 	stop_and_destroy_channel(SPECIAL_CROSSFADE_CHANNEL);
-	auto *cfade_clip = lock.MoveChannel(SPECIAL_CROSSFADE_CHANNEL, currentChannel);
+	auto *cfade_clip = AudioChans::MoveChannel(SPECIAL_CROSSFADE_CHANNEL, currentChannel);
 	if (!cfade_clip)
 		return;
 
@@ -167,8 +144,6 @@ void stop_or_fade_out_channel(int fadeOutChannel, int fadeInChannel, ScriptAudio
 }
 
 static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool interruptEqualPriority) {
-	AudioChannelsLock lock;
-
 	int lowestPrioritySoFar = 9999999;
 	int lowestPriorityID = -1;
 	int channelToUse = -1;
@@ -189,7 +164,7 @@ static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool int
 	}
 
 	for (int i = startAtChannel; i < endBeforeChannel; i++) {
-		auto *ch = lock.GetChannelIfPlaying(i);
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
 		if (ch == nullptr) {
 			channelToUse = i;
 			stop_and_destroy_channel(i);
@@ -258,13 +233,11 @@ static void audio_update_polled_stuff() {
 	// Do crossfade
 	_GP(play).crossfade_step++;
 
-	AudioChannelsLock lock;
-
-	if (_GP(play).crossfading_out_channel > 0 && !lock.GetChannelIfPlaying(_GP(play).crossfading_out_channel))
+	if (_GP(play).crossfading_out_channel > 0 && !AudioChans::GetChannelIfPlaying(_GP(play).crossfading_out_channel))
 		_GP(play).crossfading_out_channel = 0;
 
 	if (_GP(play).crossfading_out_channel > 0) {
-		SOUNDCLIP *ch = lock.GetChannel(_GP(play).crossfading_out_channel);
+		SOUNDCLIP *ch = AudioChans::GetChannel(_GP(play).crossfading_out_channel);
 		int newVolume = ch ? ch->get_volume() - _GP(play).crossfade_out_volume_per_step : 0;
 		if (newVolume > 0) {
 			ch->set_volume_percent(newVolume);
@@ -274,11 +247,11 @@ static void audio_update_polled_stuff() {
 		}
 	}
 
-	if (_GP(play).crossfading_in_channel > 0 && !lock.GetChannelIfPlaying(_GP(play).crossfading_in_channel))
+	if (_GP(play).crossfading_in_channel > 0 && !AudioChans::GetChannelIfPlaying(_GP(play).crossfading_in_channel))
 		_GP(play).crossfading_in_channel = 0;
 
 	if (_GP(play).crossfading_in_channel > 0) {
-		SOUNDCLIP *ch = lock.GetChannel(_GP(play).crossfading_in_channel);
+		SOUNDCLIP *ch = AudioChans::GetChannel(_GP(play).crossfading_in_channel);
 		int newVolume = ch ? ch->get_volume() + _GP(play).crossfade_in_volume_per_step : 0;
 		if (newVolume > _GP(play).crossfade_final_volume_in) {
 			newVolume = _GP(play).crossfade_final_volume_in;
@@ -317,7 +290,7 @@ static void audio_update_polled_stuff() {
 	// or non-blocking at any given time. If it's changed, we'd need to keep
 	// record of every channel, or keep a count of active channels.
 	if (_GP(play).IsNonBlockingVoiceSpeech()) {
-		if (!channel_is_playing(SCHAN_SPEECH)) {
+		if (!AudioChans::ChannelIsPlaying(SCHAN_SPEECH)) {
 			stop_voice_nonblocking();
 		}
 	}
@@ -393,7 +366,7 @@ ScriptAudioChannel *play_audio_clip_on_channel(int channel, ScriptAudioClip *cli
 	if (!_GP(play).fast_forward && _GP(play).speech_has_voice)
 		apply_volume_drop_to_clip(soundfx);
 
-	set_clip_to_channel(channel, soundfx);
+	AudioChans::SetChannel(channel, soundfx);
 	return &_G(scrAudioChannel)[channel];
 }
 
@@ -455,13 +428,12 @@ void stop_and_destroy_channel_ex(int chid, bool resetLegacyMusicSettings) {
 	if ((chid < 0) || (chid >= TOTAL_AUDIO_CHANNELS))
 		quit("!StopChannel: invalid channel ID");
 
-	AudioChannelsLock lock;
-	SOUNDCLIP *ch = lock.GetChannel(chid);
+	SOUNDCLIP *ch = AudioChans::GetChannel(chid);
 
 	if (ch != nullptr) {
 		ch->destroy();
 		delete ch;
-		lock.SetChannel(chid, nullptr);
+		AudioChans::SetChannel(chid, nullptr);
 		ch = nullptr;
 	}
 
@@ -539,10 +511,8 @@ int get_volume_adjusted_for_distance(int volume, int sndX, int sndY, int sndMaxD
 }
 
 void update_directional_sound_vol() {
-	AudioChannelsLock lock;
-
 	for (int chnum = NUM_SPEECH_CHANS; chnum < _GP(game).numGameChannels; chnum++) {
-		auto *ch = lock.GetChannelIfPlaying(chnum);
+		auto *ch = AudioChans::GetChannelIfPlaying(chnum);
 		if ((ch != nullptr) && (ch->_xSource >= 0)) {
 			ch->apply_directional_modifier(
 			    get_volume_adjusted_for_distance(ch->_vol,
@@ -555,8 +525,6 @@ void update_directional_sound_vol() {
 }
 
 void update_ambient_sound_vol() {
-	AudioChannelsLock lock;
-
 	for (int chan = NUM_SPEECH_CHANS; chan < _GP(game).numGameChannels; chan++) {
 		AmbientSound *thisSound = &_GP(ambient)[chan];
 
@@ -589,7 +557,7 @@ void update_ambient_sound_vol() {
 			wantvol = get_volume_adjusted_for_distance(ambientvol, thisSound->x, thisSound->y, thisSound->maxdist);
 		}
 
-		auto *ch = lock.GetChannelIfPlaying(thisSound->channel);
+		auto *ch = AudioChans::GetChannelIfPlaying(thisSound->channel);
 		if (ch)
 			ch->set_volume(wantvol);
 	}
@@ -633,11 +601,9 @@ void shutdown_sound() {
 static int play_sound_priority(int val1, int priority) {
 	int lowest_pri = 9999, lowest_pri_id = -1;
 
-	AudioChannelsLock lock;
-
 	// find a free channel to play it on
 	for (int i = SCHAN_NORMAL; i < _GP(game).numGameChannels; i++) {
-		auto *ch = lock.GetChannelIfPlaying(i);
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
 		if (val1 < 0) {
 			// Playing sound -1 means iterate through and stop all sound
 			if (ch)
@@ -648,7 +614,7 @@ static int play_sound_priority(int val1, int priority) {
 			if (usechan >= 0) {
 				// channel will hold a different clip here
 				assert(usechan == i);
-				auto *chan = lock.GetChannel(usechan);
+				auto *chan = AudioChans::GetChannel(usechan);
 				if (chan)
 					chan->_priority = priority;
 			}
@@ -668,7 +634,7 @@ static int play_sound_priority(int val1, int priority) {
 		const int usechan = PlaySoundEx(val1, lowest_pri_id);
 		if (usechan >= 0) {
 			assert(usechan == lowest_pri_id);
-			auto *ch = lock.GetChannel(usechan);
+			auto *ch = AudioChans::GetChannel(usechan);
 			if (ch)
 				ch->_priority = priority;
 			return usechan;
@@ -774,10 +740,8 @@ int calculate_max_volume() {
 
 // add/remove the volume drop to the audio channels while speech is playing
 void apply_volume_drop_modifier(bool applyModifier) {
-	AudioChannelsLock lock;
-
 	for (int i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; i++) {
-		auto *ch = lock.GetChannelIfPlaying(i);
+		auto *ch = AudioChans::GetChannelIfPlaying(i);
 		if (ch && ch->_sourceClip != nullptr) {
 			if (applyModifier)
 				apply_volume_drop_to_clip(ch);
@@ -797,8 +761,6 @@ void update_volume_drop_if_voiceover() {
 void update_audio_system_on_game_loop() {
 	update_polled_stuff_if_runtime();
 
-	AudioChannelsLock lock;
-
 	process_scheduled_music_update();
 
 	_G(audio_doing_crossfade) = true;
@@ -819,7 +781,7 @@ void update_audio_system_on_game_loop() {
 		} else if ((_GP(game).options[OPT_CROSSFADEMUSIC] > 0) &&
 		           (_GP(play).music_queue_size > 0) && (!_G(crossFading))) {
 			// want to crossfade, and new tune in the queue
-			auto *ch = lock.GetChannel(SCHAN_MUSIC);
+			auto *ch = AudioChans::GetChannel(SCHAN_MUSIC);
 			if (ch) {
 				int curpos = ch->get_pos_ms();
 				int muslen = ch->get_length_ms();
@@ -840,8 +802,6 @@ void update_audio_system_on_game_loop() {
 }
 
 void stopmusic() {
-	AudioChannelsLock lock;
-
 	if (_G(crossFading) > 0) {
 		// stop in the middle of a new track fading in
 		// Abort the new track, and let the old one finish fading out
@@ -857,7 +817,7 @@ void stopmusic() {
 			update_music_volume();
 		}
 	} else if ((_GP(game).options[OPT_CROSSFADEMUSIC] > 0)
-	           && (lock.GetChannelIfPlaying(SCHAN_MUSIC) != nullptr)
+	           && (AudioChans::GetChannelIfPlaying(SCHAN_MUSIC) != nullptr)
 	           && (_G(current_music_type) != 0)
 	           && (_G(current_music_type) != MUS_MIDI)
 	           && (_G(current_music_type) != MUS_MOD)) {
@@ -874,8 +834,6 @@ void stopmusic() {
 }
 
 void update_music_volume() {
-	AudioChannelsLock lock;
-
 	if ((_G(current_music_type)) || (_G(crossFading) < 0)) {
 		// targetVol is the maximum volume we're fading in to
 		// newvol is the starting volume that we faded out from
@@ -898,12 +856,12 @@ void update_music_volume() {
 				newvol = targetVol;
 				stop_and_destroy_channel_ex(SCHAN_MUSIC, false);
 				if (_G(crossFading) > 0) {
-					lock.MoveChannel(SCHAN_MUSIC, _G(crossFading));
+					AudioChans::MoveChannel(SCHAN_MUSIC, _G(crossFading));
 				}
 				_G(crossFading) = 0;
 			} else {
 				if (_G(crossFading) > 0) {
-					auto *ch = lock.GetChannel(_G(crossFading));
+					auto *ch = AudioChans::GetChannel(_G(crossFading));
 					if (ch)
 						ch->set_volume((curvol > targetVol) ? targetVol : curvol);
 				}
@@ -913,7 +871,7 @@ void update_music_volume() {
 					newvol = 0;
 			}
 		}
-		auto *ch = lock.GetChannel(SCHAN_MUSIC);
+		auto *ch = AudioChans::GetChannel(SCHAN_MUSIC);
 		if (ch)
 			ch->set_volume(newvol);
 	}
@@ -922,30 +880,27 @@ void update_music_volume() {
 // Ensures crossfader is stable after loading (or failing to load)
 // new music
 void post_new_music_check(int newchannel) {
-	AudioChannelsLock lock;
-	if ((_G(crossFading) > 0) && (lock.GetChannel(_G(crossFading)) == nullptr)) {
+	if ((_G(crossFading) > 0) && (AudioChans::GetChannel(_G(crossFading)) == nullptr)) {
 		_G(crossFading) = 0;
 		// Was fading out but then they played invalid music, continue to fade out
-		if (lock.GetChannel(SCHAN_MUSIC) != nullptr)
+		if (AudioChans::GetChannel(SCHAN_MUSIC) != nullptr)
 			_G(crossFading) = -1;
 	}
 
 }
 
 int prepare_for_new_music() {
-	AudioChannelsLock lock;
-
 	int useChannel = SCHAN_MUSIC;
 
 	if ((_GP(game).options[OPT_CROSSFADEMUSIC] > 0)
-	        && (lock.GetChannelIfPlaying(SCHAN_MUSIC) != nullptr)
+	        && (AudioChans::GetChannelIfPlaying(SCHAN_MUSIC) != nullptr)
 	        && (_G(current_music_type) != MUS_MIDI)
 	        && (_G(current_music_type) != MUS_MOD)) {
 
 		if (_G(crossFading) > 0) {
 			// It's still crossfading to the previous track
 			stop_and_destroy_channel_ex(SCHAN_MUSIC, false);
-			lock.MoveChannel(SCHAN_MUSIC, _G(crossFading));
+			AudioChans::MoveChannel(SCHAN_MUSIC, _G(crossFading));
 			_G(crossFading) = 0;
 			update_music_volume();
 		} else if (_G(crossFading) < 0) {
@@ -968,7 +923,7 @@ int prepare_for_new_music() {
 	}
 
 	// Just make sure, because it will be overwritten in a sec
-	if (lock.GetChannel(useChannel) != nullptr)
+	if (AudioChans::GetChannel(useChannel) != nullptr)
 		stop_and_destroy_channel(useChannel);
 
 	return useChannel;
@@ -981,7 +936,6 @@ ScriptAudioClip *get_audio_clip_for_music(int mnum) {
 }
 
 SOUNDCLIP *load_music_from_disk(int mnum, bool doRepeat) {
-
 	if (mnum >= QUEUED_MUSIC_REPEAT) {
 		mnum -= QUEUED_MUSIC_REPEAT;
 		doRepeat = true;
@@ -1037,15 +991,14 @@ static void play_new_music(int mnum, SOUNDCLIP *music) {
 	else
 		new_clip = load_music_from_disk(mnum, (_GP(play).music_repeat > 0));
 
-	AudioChannelsLock lock;
-	auto *ch = lock.SetChannel(useChannel, new_clip);
+	auto *ch = AudioChans::SetChannel(useChannel, new_clip);
 	if (ch != nullptr) {
 		if (!ch->play()) {
 			// previous behavior was to set channel[] to null on error, so continue to do that here.
 			ch->destroy();
 			delete ch;
 			ch = nullptr;
-			lock.SetChannel(useChannel, nullptr);
+			AudioChans::SetChannel(useChannel, nullptr);
 		} else
 			_G(current_music_type) = ch->get_sound_type();
 	}
diff --git a/engines/ags/engine/media/audio/audio.h b/engines/ags/engine/media/audio/audio.h
index 7ab75b80593..8ea7a902c69 100644
--- a/engines/ags/engine/media/audio/audio.h
+++ b/engines/ags/engine/media/audio/audio.h
@@ -35,39 +35,30 @@ namespace AGS3 {
 
 struct SOUNDCLIP;
 
-//controls access to the channels, since that's the main point of synchronization between the streaming thread and the user code
-//this is going to be dependent on the underlying mutexes being recursive
-//yes, we will have more recursive traffic on mutexes than we need
-//however this should mostly be happening only when playing sounds, and possibly when sounds numbering only several
-//the load should not be high
-class AudioChannelsLock : public AGS::Engine::MutexLock {
-private:
-	AudioChannelsLock(AudioChannelsLock const &); // non-copyable
-	AudioChannelsLock &operator=(AudioChannelsLock const &); // not copy-assignable
-
+class AudioChans {
 public:
-	AudioChannelsLock();
-
 	// Gets a clip from the channel
-	SOUNDCLIP *GetChannel(int index);
+	static SOUNDCLIP *GetChannel(int index);
 	// Gets a clip from the channel but only if it's in playback state
-	SOUNDCLIP *GetChannelIfPlaying(int index);
+	static SOUNDCLIP *GetChannelIfPlaying(int index);
 	// Assign new clip to the channel
-	SOUNDCLIP *SetChannel(int index, SOUNDCLIP *clip);
+	static SOUNDCLIP *SetChannel(int index, SOUNDCLIP *clip);
 	// Move clip from one channel to another, clearing the first channel
-	SOUNDCLIP *MoveChannel(int to, int from);
-};
+	static SOUNDCLIP *MoveChannel(int to, int from);
 
-//
-// Channel helpers, autolock and perform a simple action on a channel.
-//
-// Tells if channel has got a clip; does not care about its state
-bool channel_has_clip(int chanid);
-// Tells if channel has got a clip and clip is in playback state
-bool channel_is_playing(int chanid);
-// Sets new clip to the channel
-void set_clip_to_channel(int chanid, SOUNDCLIP *clip);
+	// Tells if channel has got a clip; does not care about its state
+	static inline bool ChannelHasClip(int index) {
+		return GetChannel(index) != nullptr;
+	}
+	// Tells if channel has got a clip and clip is in playback state
+	static inline bool ChannelIsPlaying(int index) {
+		return GetChannelIfPlaying(index) != nullptr;
+	}
 
+private:
+	AudioChans() = delete;
+	~AudioChans() = delete;
+};
 
 void        calculate_reserved_channel_count();
 void        update_clip_default_volume(ScriptAudioClip *audioClip);
diff --git a/engines/ags/plugins/ags_plugin.cpp b/engines/ags/plugins/ags_plugin.cpp
index 108c12b17d6..31faaa66ec7 100644
--- a/engines/ags/plugins/ags_plugin.cpp
+++ b/engines/ags/plugins/ags_plugin.cpp
@@ -529,7 +529,7 @@ void IAGSEngine::PlaySoundChannel(int32 channel, int32 soundType, int32 volume,
 	} else
 		quit("!IAGSEngine::PlaySoundChannel: unknown sound type");
 
-	set_clip_to_channel(channel, newcha);
+	AudioChans::SetChannel(channel, newcha);
 }
 // Engine interface 12 and above are below
 void IAGSEngine::MarkRegionDirty(int32 left, int32 top, int32 right, int32 bottom) {


Commit: 07f36ce19b12d5cc8356b7514c7390280140200e
    https://github.com/scummvm/scummvm/commit/07f36ce19b12d5cc8356b7514c7390280140200e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-18T21:29:56-07:00

Commit Message:
AGS: Store clip's ID in SOUNDCLIP instead of a pointer to unknown type

>From upstream 954e9bb27e7aea873931ba20ad89397d4dc004bd

Changed paths:
    engines/ags/engine/ac/audio_channel.cpp
    engines/ags/engine/ac/audio_clip.cpp
    engines/ags/engine/game/savegame_components.cpp
    engines/ags/engine/media/audio/audio.cpp
    engines/ags/engine/media/audio/sound_clip.cpp
    engines/ags/engine/media/audio/sound_clip.h


diff --git a/engines/ags/engine/ac/audio_channel.cpp b/engines/ags/engine/ac/audio_channel.cpp
index ca09077576a..ea838204eee 100644
--- a/engines/ags/engine/ac/audio_channel.cpp
+++ b/engines/ags/engine/ac/audio_channel.cpp
@@ -27,7 +27,7 @@
 #include "ags/shared/game/room_struct.h"
 #include "ags/engine/script/runtime_script_value.h"
 #include "ags/engine/media/audio/audio_system.h"
-
+#include "ags/shared/ac/game_setup_struct.h"
 #include "ags/shared/debugging/out.h"
 #include "ags/engine/script/script_api.h"
 #include "ags/engine/script/script_runtime.h"
@@ -80,8 +80,8 @@ void AudioChannel_SetPanning(ScriptAudioChannel *channel, int newPanning) {
 ScriptAudioClip *AudioChannel_GetPlayingClip(ScriptAudioChannel *channel) {
 	auto *ch = AudioChans::GetChannelIfPlaying(channel->id);
 
-	if (ch) {
-		return (ScriptAudioClip *)ch->_sourceClip;
+	if (ch && ch->_sourceClipID >= 0) {
+		return &_GP(game).audioClips[ch->_sourceClipID];
 	}
 	return nullptr;
 }
diff --git a/engines/ags/engine/ac/audio_clip.cpp b/engines/ags/engine/ac/audio_clip.cpp
index 5d2e67bb001..a961521c729 100644
--- a/engines/ags/engine/ac/audio_clip.cpp
+++ b/engines/ags/engine/ac/audio_clip.cpp
@@ -49,7 +49,7 @@ int AudioClip_GetIsAvailable(ScriptAudioClip *clip) {
 void AudioClip_Stop(ScriptAudioClip *clip) {
 	for (int i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; i++) {
 		auto *ch = AudioChans::GetChannelIfPlaying(i);
-		if ((ch != nullptr) && (ch->_sourceClip == clip)) {
+		if ((ch != nullptr) && (ch->_sourceClipID == clip->id)) {
 			AudioChannel_Stop(&_G(scrAudioChannel)[i]);
 		}
 	}
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index a2c6c803fe8..05dcd890d7d 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -347,8 +347,8 @@ HSaveError WriteAudio(Stream *out) {
 	// Audio clips and crossfade
 	for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
 		auto *ch = AudioChans::GetChannelIfPlaying(i);
-		if ((ch != nullptr) && (ch->_sourceClip != nullptr)) {
-			out->WriteInt32(((ScriptAudioClip *)ch->_sourceClip)->id);
+		if ((ch != nullptr) && (ch->_sourceClipID >= 0)) {
+			out->WriteInt32(ch->_sourceClipID);
 			out->WriteInt32(ch->get_pos());
 			out->WriteInt32(ch->_priority);
 			out->WriteInt32(ch->_repeat ? 1 : 0);
diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index 437d348dbc5..0f231120d5a 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -222,7 +222,7 @@ SOUNDCLIP *load_sound_clip(ScriptAudioClip *audioClip, bool repeat) {
 	}
 	if (soundClip != nullptr) {
 		soundClip->set_volume_percent(audioClip->defaultVolume);
-		soundClip->_sourceClip = audioClip;
+		soundClip->_sourceClipID = audioClip->id;
 		soundClip->_sourceClipType = audioClip->type;
 	}
 	return soundClip;
@@ -742,7 +742,7 @@ int calculate_max_volume() {
 void apply_volume_drop_modifier(bool applyModifier) {
 	for (int i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; i++) {
 		auto *ch = AudioChans::GetChannelIfPlaying(i);
-		if (ch && ch->_sourceClip != nullptr) {
+		if (ch && ch->_sourceClipID >= 0) {
 			if (applyModifier)
 				apply_volume_drop_to_clip(ch);
 			else
diff --git a/engines/ags/engine/media/audio/sound_clip.cpp b/engines/ags/engine/media/audio/sound_clip.cpp
index e1fe16ce0e3..e91351bdf98 100644
--- a/engines/ags/engine/media/audio/sound_clip.cpp
+++ b/engines/ags/engine/media/audio/sound_clip.cpp
@@ -25,7 +25,7 @@
 namespace AGS3 {
 
 SOUNDCLIP::SOUNDCLIP() : _panning(12. / 8), _panningAsPercentage(0),
-	_sourceClip(nullptr), _sourceClipType(0), _speed(1000), _priority(50),
+	_sourceClipID(-1), _sourceClipType(0), _speed(1000), _priority(50),
 	_xSource(-1), _ySource(-1), _maximumPossibleDistanceAway(0), _muted(false),
 	_volAsPercentage(0), _vol(0), _volModifier(0), _repeat(false), _directionalVolModifier(0) {
 }
diff --git a/engines/ags/engine/media/audio/sound_clip.h b/engines/ags/engine/media/audio/sound_clip.h
index 191e19bb4e7..5438790f9dc 100644
--- a/engines/ags/engine/media/audio/sound_clip.h
+++ b/engines/ags/engine/media/audio/sound_clip.h
@@ -57,7 +57,7 @@ struct SOUNDCLIP {
 	int _maximumPossibleDistanceAway;
 	int _directionalVolModifier;
 	bool _repeat;
-	void *_sourceClip;      // Pointer to source object that spawned the clip
+	int _sourceClipID;
 
 	virtual void poll() = 0;
 	virtual void destroy() = 0;




More information about the Scummvm-git-logs mailing list