[Scummvm-git-logs] scummvm master -> 007baa9541c8e5831316ec0efabf275f111ee925

dreammaster noreply at scummvm.org
Thu Apr 14 04:55:31 UTC 2022


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

Summary:
aeddd232e0 AGS: Removed animating buttons limit
14209fafed AGS: Removed arbitrary character followers limit
b13b75712f AGS: Updated build version (3.6.0.19)
596ddd0be6 AGS: Added some utf-8 utils and moved ConvertUtf8ToAscii to StrUtil
1ec52936d2 AGS: Added utf8->wcstr conversions to StrUtils
0e0063bb6b AGS: String.AppendChar, ReplaceChar, Chars[] support unicode
9fe89e4f1d AGS: String.Format treats %c as possibly unicode
007baa9541 AGS: Restore proper text format in close_translation()


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

Commit Message:
AGS: Removed animating buttons limit

>From upstream 484f6d3f44c7dbb361ddeebd8bb46862760501bc

Changed paths:
    engines/ags/engine/ac/button.cpp
    engines/ags/engine/ac/button.h
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/runtime_defines.h
    engines/ags/engine/game/savegame_components.cpp
    engines/ags/engine/game/savegame_v321.cpp
    engines/ags/engine/main/game_run.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h
    engines/ags/shared/gui/gui_object.cpp
    engines/ags/shared/gui/gui_object.h


diff --git a/engines/ags/engine/ac/button.cpp b/engines/ags/engine/ac/button.cpp
index e63f7c8ed4c..937138758df 100644
--- a/engines/ags/engine/ac/button.cpp
+++ b/engines/ags/engine/ac/button.cpp
@@ -56,28 +56,27 @@ void Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat)
 	// if it's already animating, stop it
 	FindAndRemoveButtonAnimation(guin, objn);
 
-	if (_G(numAnimButs) >= MAX_ANIMATING_BUTTONS)
-		quit("!AnimateButton: too many animating GUI buttons at once");
-
 	int buttonId = _GP(guis)[guin].GetControlID(objn);
 
 	_GP(guibuts)[buttonId].PushedImage = 0;
 	_GP(guibuts)[buttonId].MouseOverImage = 0;
 
-	_G(animbuts)[_G(numAnimButs)].ongui = guin;
-	_G(animbuts)[_G(numAnimButs)].onguibut = objn;
-	_G(animbuts)[_G(numAnimButs)].buttonid = buttonId;
-	_G(animbuts)[_G(numAnimButs)].view = view;
-	_G(animbuts)[_G(numAnimButs)].loop = loop;
-	_G(animbuts)[_G(numAnimButs)].speed = speed;
-	_G(animbuts)[_G(numAnimButs)].repeat = repeat;
-	_G(animbuts)[_G(numAnimButs)].frame = -1;
-	_G(animbuts)[_G(numAnimButs)].wait = 0;
-	_G(numAnimButs)++;
+	AnimatingGUIButton abtn;
+	abtn.ongui = guin;
+	abtn.onguibut = objn;
+	abtn.buttonid = buttonId;
+	abtn.view = view;
+	abtn.loop = loop;
+	abtn.speed = speed;
+	abtn.repeat = repeat;
+	abtn.frame = -1;
+	abtn.wait = 0;
+	_GP(animbuts).push_back(abtn);
 	// launch into the first frame
-	if (UpdateAnimatingButton(_G(numAnimButs) - 1)) {
-		debug_script_warn("AnimateButton: no frames to animate");
-		StopButtonAnimation(_G(numAnimButs) - 1);
+	if (UpdateAnimatingButton(_GP(animbuts).size() - 1)) {
+		debug_script_warn("AnimateButton: no frames to animate (button: %s, view: %d, loop: %d)",
+			butt->GetScriptName().GetCStr(), view, loop);
+		StopButtonAnimation(_GP(animbuts).size() - 1);
 	}
 }
 
@@ -194,56 +193,72 @@ void Button_SetTextColor(GUIButton *butt, int newcol) {
 
 // ** start animating buttons code
 
+size_t GetAnimatingButtonCount() {
+	return _GP(animbuts).size();
+}
+
+AnimatingGUIButton *GetAnimatingButtonByIndex(int idxn) {
+	return idxn >= 0 && (size_t)idxn < _GP(animbuts).size() ?
+		&_GP(animbuts)[idxn] : nullptr;
+}
+
+void AddButtonAnimation(const AnimatingGUIButton &abtn) {
+	_GP(animbuts).push_back(abtn);
+}
+
 // returns 1 if animation finished
 int UpdateAnimatingButton(int bu) {
-	if (_G(animbuts)[bu].wait > 0) {
-		_G(animbuts)[bu].wait--;
+	AnimatingGUIButton &abtn = _GP(animbuts)[bu];
+
+	if (abtn.wait > 0) {
+		abtn.wait--;
 		return 0;
 	}
-	ViewStruct *tview = &_GP(views)[_G(animbuts)[bu].view];
+	ViewStruct *tview = &_GP(views)[abtn.view];
 
-	_G(animbuts)[bu].frame++;
+	abtn.frame++;
 
-	if (_G(animbuts)[bu].frame >= tview->loops[_G(animbuts)[bu].loop].numFrames) {
-		if (tview->loops[_G(animbuts)[bu].loop].RunNextLoop()) {
+	if (abtn.frame >= tview->loops[abtn.loop].numFrames) {
+		if (tview->loops[abtn.loop].RunNextLoop()) {
 			// go to next loop
-			_G(animbuts)[bu].loop++;
-			_G(animbuts)[bu].frame = 0;
-		} else if (_G(animbuts)[bu].repeat) {
-			_G(animbuts)[bu].frame = 0;
+			abtn.loop++;
+			abtn.frame = 0;
+		} else if (abtn.repeat) {
+			abtn.frame = 0;
 			// multi-loop anim, go back
-			while ((_G(animbuts)[bu].loop > 0) &&
-			        (tview->loops[_G(animbuts)[bu].loop - 1].RunNextLoop()))
-				_G(animbuts)[bu].loop--;
+			while ((abtn.loop > 0) &&
+				(tview->loops[abtn.loop - 1].RunNextLoop()))
+				abtn.loop--;
 		} else
 			return 1;
 	}
 
-	CheckViewFrame(_G(animbuts)[bu].view, _G(animbuts)[bu].loop, _G(animbuts)[bu].frame);
+	CheckViewFrame(abtn.view, abtn.loop, abtn.frame);
 
 	// update the button's image
-	_GP(guibuts)[_G(animbuts)[bu].buttonid].Image = tview->loops[_G(animbuts)[bu].loop].frames[_G(animbuts)[bu].frame].pic;
-	_GP(guibuts)[_G(animbuts)[bu].buttonid].CurrentImage = _GP(guibuts)[_G(animbuts)[bu].buttonid].Image;
-	_GP(guibuts)[_G(animbuts)[bu].buttonid].PushedImage = 0;
-	_GP(guibuts)[_G(animbuts)[bu].buttonid].MouseOverImage = 0;
-	_GP(guibuts)[_G(animbuts)[bu].buttonid].NotifyParentChanged();
+	_GP(guibuts)[abtn.buttonid].Image = tview->loops[abtn.loop].frames[abtn.frame].pic;
+	_GP(guibuts)[abtn.buttonid].CurrentImage = _GP(guibuts)[abtn.buttonid].Image;
+	_GP(guibuts)[abtn.buttonid].PushedImage = 0;
+	_GP(guibuts)[abtn.buttonid].MouseOverImage = 0;
+	_GP(guibuts)[abtn.buttonid].NotifyParentChanged();
 
-	_G(animbuts)[bu].wait = _G(animbuts)[bu].speed + tview->loops[_G(animbuts)[bu].loop].frames[_G(animbuts)[bu].frame].speed;
+	abtn.wait = abtn.speed + tview->loops[abtn.loop].frames[abtn.frame].speed;
 	return 0;
 }
 
 void StopButtonAnimation(int idxn) {
-	_G(numAnimButs)--;
-	for (int aa = idxn; aa < _G(numAnimButs); aa++) {
-		_G(animbuts)[aa] = _G(animbuts)[aa + 1];
-	}
+	_GP(animbuts).erase(_GP(animbuts).begin() + idxn);
+}
+
+void RemoveAllButtonAnimations() {
+	_GP(animbuts).clear();
 }
 
 // Returns the index of the AnimatingGUIButton object corresponding to the
 // given button ID; returns -1 if no such animation exists
 int FindAnimatedButton(int guin, int objn) {
-	for (int i = 0; i < _G(numAnimButs); ++i) {
-		if (_G(animbuts)[i].ongui == guin && _G(animbuts)[i].onguibut == objn)
+	for (size_t i = 0; i < _GP(animbuts).size(); ++i) {
+		if (_GP(animbuts)[i].ongui == guin && _GP(animbuts)[i].onguibut == objn)
 			return i;
 	}
 	return -1;
@@ -254,6 +269,7 @@ void FindAndRemoveButtonAnimation(int guin, int objn) {
 	if (idx >= 0)
 		StopButtonAnimation(idx);
 }
+
 // ** end animating buttons code
 
 void Button_Click(GUIButton *butt, int mbut) {
@@ -269,17 +285,17 @@ bool Button_IsAnimating(GUIButton *butt) {
 // zero-based index and 0 in case of no animation.
 int Button_GetAnimView(GUIButton *butt) {
 	int idx = FindAnimatedButton(butt->ParentId, butt->Id);
-	return idx >= 0 ? _G(animbuts)[idx].view + 1 : 0;
+	return idx >= 0 ? _GP(animbuts)[idx].view + 1 : 0;
 }
 
 int Button_GetAnimLoop(GUIButton *butt) {
 	int idx = FindAnimatedButton(butt->ParentId, butt->Id);
-	return idx >= 0 ? _G(animbuts)[idx].loop : 0;
+	return idx >= 0 ? _GP(animbuts)[idx].loop : 0;
 }
 
 int Button_GetAnimFrame(GUIButton *butt) {
 	int idx = FindAnimatedButton(butt->ParentId, butt->Id);
-	return idx >= 0 ? _G(animbuts)[idx].frame : 0;
+	return idx >= 0 ? _GP(animbuts)[idx].frame : 0;
 }
 
 int Button_GetTextAlignment(GUIButton *butt) {
diff --git a/engines/ags/engine/ac/button.h b/engines/ags/engine/ac/button.h
index 98ddd7a3b7e..b0ecbcc652b 100644
--- a/engines/ags/engine/ac/button.h
+++ b/engines/ags/engine/ac/button.h
@@ -28,6 +28,7 @@
 namespace AGS3 {
 
 using AGS::Shared::GUIButton;
+struct AnimatingGUIButton;
 
 void        Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat);
 const char *Button_GetText_New(GUIButton *butt);
@@ -48,8 +49,12 @@ int         Button_GetTextColor(GUIButton *butt);
 void        Button_SetTextColor(GUIButton *butt, int newcol);
 
 int         UpdateAnimatingButton(int bu);
-void        StopButtonAnimation(int idxn);
-void        FindAndRemoveButtonAnimation(int guin, int objn);
+size_t      GetAnimatingButtonCount();
+AnimatingGUIButton *GetAnimatingButtonByIndex(int idxn);
+void        AddButtonAnimation(const AnimatingGUIButton &abtn);
+void		StopButtonAnimation(int idxn);
+void		FindAndRemoveButtonAnimation(int guin, int objn);
+void        RemoveAllButtonAnimations();
 
 } // namespace AGS3
 
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index e82ba0a3bf8..124331b6e0d 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -24,6 +24,7 @@
 #include "ags/shared/ac/common.h"
 #include "ags/shared/ac/view.h"
 #include "ags/engine/ac/audio_channel.h"
+#include "ags/engine/ac/button.h"
 #include "ags/engine/ac/character.h"
 #include "ags/engine/ac/character_cache.h"
 #include "ags/shared/ac/dialog_topic.h"
diff --git a/engines/ags/engine/ac/runtime_defines.h b/engines/ags/engine/ac/runtime_defines.h
index c88fda4c634..1e27e341e36 100644
--- a/engines/ags/engine/ac/runtime_defines.h
+++ b/engines/ags/engine/ac/runtime_defines.h
@@ -145,7 +145,6 @@ const int LegacyRoomVolumeFactor = 30;
 
 #define MAX_DYNAMIC_SURFACES 20
 
-#define MAX_ANIMATING_BUTTONS 15
 #define RESTART_POINT_SAVE_GAME_NUMBER 999
 
 #define MAX_OPEN_SCRIPT_FILES 10
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index 68782e4cdd1..8111b8fbede 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -21,9 +21,10 @@
 
 #include "ags/lib/std/map.h"
 #include "ags/shared/ac/audio_clip_type.h"
-#include "ags/engine/ac/character.h"
 #include "ags/shared/ac/common.h"
 #include "ags/shared/ac/dialog_topic.h"
+#include "ags/engine/ac/button.h"
+#include "ags/engine/ac/character.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/ac/dynamic_sprite.h"
 #include "ags/engine/ac/game.h"
@@ -567,9 +568,10 @@ HSaveError WriteGUI(Stream *out) {
 
 	// Animated buttons
 	WriteFormatTag(out, "AnimatedButtons");
-	out->WriteInt32(_G(numAnimButs));
-	for (int i = 0; i < _G(numAnimButs); ++i)
-		_G(animbuts)[i].WriteToFile(out);
+	size_t num_abuts = GetAnimatingButtonCount();
+	out->WriteInt32(num_abuts);
+	for (size_t i = 0; i < num_abuts; ++i)
+		GetAnimatingButtonByIndex(i)->WriteToFile(out);
 	return HSaveError::None();
 }
 
@@ -629,13 +631,13 @@ HSaveError ReadGUI(Stream *in, int32_t cmp_ver, const PreservedParams &pp, Resto
 	// Animated buttons
 	if (!AssertFormatTagStrict(err, in, "AnimatedButtons"))
 		return err;
+	RemoveAllButtonAnimations();
 	int anim_count = in->ReadInt32();
-	if (!AssertCompatLimit(err, anim_count, MAX_ANIMATING_BUTTONS, "animated buttons"))
-		return err;
-	_G(numAnimButs) = anim_count;
-	for (int i = 0; i < _G(numAnimButs); ++i)
-		_G(animbuts)[i].ReadFromFile(in);
-	return err;
+	for (int i = 0; i < anim_count; ++i) {
+		AnimatingGUIButton abut;
+		abut.ReadFromFile(in);
+		AddButtonAnimation(abut);
+	}	return err;
 }
 
 HSaveError WriteInventory(Stream *out) {
diff --git a/engines/ags/engine/game/savegame_v321.cpp b/engines/ags/engine/game/savegame_v321.cpp
index 82321432b30..a5e0792cd15 100644
--- a/engines/ags/engine/game/savegame_v321.cpp
+++ b/engines/ags/engine/game/savegame_v321.cpp
@@ -32,6 +32,7 @@
 #include "ags/engine/ac/character_extras.h"
 #include "ags/shared/ac/common.h"
 #include "ags/shared/ac/dialog_topic.h"
+#include "ags/engine/ac/button.h"
 #include "ags/engine/ac/dynamic_sprite.h"
 #include "ags/engine/ac/game.h"
 #include "ags/shared/ac/game_setup_struct.h"
@@ -211,10 +212,12 @@ static void restore_game_more_dynamic_values(Stream *in) {
 	_G(game_paused) = in->ReadInt32();
 }
 
-static void ReadAnimatedButtons_Aligned(Stream *in) {
+void ReadAnimatedButtons_Aligned(Stream *in, int num_abuts) {
 	AlignedStream align_s(in, Shared::kAligned_Read);
-	for (int i = 0; i < _G(numAnimButs); ++i) {
-		_G(animbuts)[i].ReadFromFile(&align_s);
+	for (int i = 0; i < num_abuts; ++i) {
+		AnimatingGUIButton abtn;
+		abtn.ReadFromFile(&align_s);
+		AddButtonAnimation(abtn);
 		align_s.Reset();
 	}
 }
@@ -229,8 +232,9 @@ static HSaveError restore_game_gui(Stream *in, int numGuisWas) {
 		return new SavegameError(kSvgErr_GameContentAssertion, "Mismatching number of GUI.");
 	}
 
-	_G(numAnimButs) = in->ReadInt32();
-	ReadAnimatedButtons_Aligned(in);
+	RemoveAllButtonAnimations();
+	int anim_count = in->ReadInt32();
+	ReadAnimatedButtons_Aligned(in, anim_count);
 	return HSaveError::None();
 }
 
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index 5830f9dc780..6957ad27aef 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -580,10 +580,10 @@ static void game_loop_update_animated_buttons() {
 	// update animating GUI buttons
 	// this bit isn't in update_stuff because it always needs to
 	// happen, even when the game is paused
-	for (int aa = 0; aa < _G(numAnimButs); aa++) {
-		if (UpdateAnimatingButton(aa)) {
-			StopButtonAnimation(aa);
-			aa--;
+	for (size_t i = 0; i < GetAnimatingButtonCount(); ++i) {
+		if (UpdateAnimatingButton(i)) {
+			StopButtonAnimation(i);
+			i--;
 		}
 	}
 }
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index b160bb3aabc..bc226ee9b56 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -126,7 +126,7 @@ Globals::Globals() {
 	_scrAudioChannel = new ScriptAudioChannel[MAX_GAME_CHANNELS];
 
 	// button.cpp globals
-	_animbuts = new AnimatingGUIButton[MAX_ANIMATING_BUTTONS];
+	_animbuts = new std::vector<AnimatingGUIButton>();
 
 	// cc_instance.cpp globals
 	_GlobalReturnValue = new RuntimeScriptValue();
@@ -387,7 +387,7 @@ Globals::~Globals() {
 	delete[] _scrAudioChannel;
 
 	// button.cpp globals
-	delete[] _animbuts;
+	delete _animbuts;
 
 	// cc_instance.cpp globals
 	delete _GlobalReturnValue;
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 7e2e6b46f43..c656b992117 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -106,7 +106,6 @@ class SplitLines;
 class TTFFontRenderer;
 class WFNFontRenderer;
 
-struct ActiveDisplaySetting;
 struct AGSDeSerializer;
 struct AGSPlatformDriver;
 struct AGSStaticObject;
@@ -324,8 +323,7 @@ public:
 	 * @{
 	 */
 
-	AnimatingGUIButton *_animbuts;
-	int _numAnimButs = 0;
+	std::vector<AnimatingGUIButton> *_animbuts;
 
 	/**@}*/
 
diff --git a/engines/ags/shared/gui/gui_object.cpp b/engines/ags/shared/gui/gui_object.cpp
index ba05e51cab4..a52aeabffd9 100644
--- a/engines/ags/shared/gui/gui_object.cpp
+++ b/engines/ags/shared/gui/gui_object.cpp
@@ -41,6 +41,10 @@ GUIObject::GUIObject() {
 	_scEventCount = 0;
 }
 
+String GUIObject::GetScriptName() const {
+	return Name;
+}
+
 int GUIObject::GetEventCount() const {
 	return _scEventCount;
 }
diff --git a/engines/ags/shared/gui/gui_object.h b/engines/ags/shared/gui/gui_object.h
index 4d74e1675e6..346ba7f7238 100644
--- a/engines/ags/shared/gui/gui_object.h
+++ b/engines/ags/shared/gui/gui_object.h
@@ -46,6 +46,8 @@ public:
 	GUIObject();
 	virtual ~GUIObject() {}
 
+	String          GetScriptName() const;
+
 	String          GetEventArgs(int event) const;
 	int             GetEventCount() const;
 	String          GetEventName(int event) const;


Commit: 14209fafed0493cfcf85d53b564ab178046ad184
    https://github.com/scummvm/scummvm/commit/14209fafed0493cfcf85d53b564ab178046ad184
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-13T21:55:19-07:00

Commit Message:
AGS: Removed arbitrary character followers limit

>From upstream c64850badd610b3ce90821a080e7e6b04d65193e

Changed paths:
    engines/ags/engine/ac/character_info_engine.cpp
    engines/ags/engine/main/update.cpp
    engines/ags/engine/main/update.h
    engines/ags/shared/ac/character_info.h


diff --git a/engines/ags/engine/ac/character_info_engine.cpp b/engines/ags/engine/ac/character_info_engine.cpp
index 1e069308e9b..14fdae64f36 100644
--- a/engines/ags/engine/ac/character_info_engine.cpp
+++ b/engines/ags/engine/ac/character_info_engine.cpp
@@ -63,7 +63,7 @@ int CharacterInfo::get_blocking_bottom() {
 	return y + 3;
 }
 
-void CharacterInfo::UpdateMoveAndAnim(int &char_index, CharacterExtras *chex, int &numSheep, int *followingAsSheep) {
+void CharacterInfo::UpdateMoveAndAnim(int &char_index, CharacterExtras *chex, std::vector<int> &followingAsSheep) {
 	int res;
 
 	if (on != 1) return;
@@ -102,7 +102,7 @@ void CharacterInfo::UpdateMoveAndAnim(int &char_index, CharacterExtras *chex, in
 		return;                   //  must be careful not to screw things up
 	}
 
-	update_character_follower(char_index, numSheep, followingAsSheep, doing_nothing);
+	update_character_follower(char_index, followingAsSheep, doing_nothing);
 
 	update_character_idle(chex, doing_nothing);
 
@@ -372,14 +372,12 @@ int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) {
 	return 0;
 }
 
-void CharacterInfo::update_character_follower(int &aa, int &numSheep, int *followingAsSheep, int &doing_nothing) {
+void CharacterInfo::update_character_follower(int &aa, std::vector<int> &followingAsSheep, int &doing_nothing) {
 	if ((following >= 0) && (followinfo == FOLLOW_ALWAYSONTOP)) {
 		// an always-on-top follow
-		if (numSheep >= MAX_SHEEP)
-			quit("too many sheep");
-		followingAsSheep[numSheep] = aa;
-		numSheep++;
+		followingAsSheep.push_back(aa);
 	}
+
 	// not moving, but should be following another character
 	else if ((following >= 0) && (doing_nothing == 1)) {
 		short distaway = (followinfo >> 8) & 0x00ff;
diff --git a/engines/ags/engine/main/update.cpp b/engines/ags/engine/main/update.cpp
index 506cf5eecfc..4847bf06252 100644
--- a/engines/ags/engine/main/update.cpp
+++ b/engines/ags/engine/main/update.cpp
@@ -197,7 +197,7 @@ void update_shadow_areas() {
 	}
 }
 
-void update_character_move_and_anim(int &numSheep, int *followingAsSheep) {
+void update_character_move_and_anim(std::vector<int> &followingAsSheep) {
 	// move & animate characters
 	for (int aa = 0; aa < _GP(game).numcharacters; aa++) {
 		if (_GP(game).chars[aa].on != 1) continue;
@@ -205,14 +205,14 @@ void update_character_move_and_anim(int &numSheep, int *followingAsSheep) {
 		CharacterInfo *chi = &_GP(game).chars[aa];
 		CharacterExtras *chex = &_G(charextra)[aa];
 
-		chi->UpdateMoveAndAnim(aa, chex, numSheep, followingAsSheep);
+		chi->UpdateMoveAndAnim(aa, chex, followingAsSheep);
 	}
 }
 
-void update_following_exactly_characters(int &numSheep, int *followingAsSheep) {
+void update_following_exactly_characters(const std::vector<int> &followingAsSheep) {
 	// update location of all following_exactly characters
-	for (int aa = 0; aa < numSheep; aa++) {
-		CharacterInfo *chi = &_GP(game).chars[followingAsSheep[aa]];
+	for (size_t i = 0; i < followingAsSheep.size(); ++i) {
+		CharacterInfo *chi = &_GP(game).chars[followingAsSheep[i]];
 
 		chi->UpdateFollowingExactlyCharacter();
 	}
@@ -431,12 +431,11 @@ void update_stuff() {
 
 	_G(our_eip) = 22;
 
-	int numSheep = 0;
-	int followingAsSheep[MAX_SHEEP];
+	std::vector<int> followingAsSheep;
 
-	update_character_move_and_anim(numSheep, followingAsSheep);
+	update_character_move_and_anim(followingAsSheep);
 
-	update_following_exactly_characters(numSheep, followingAsSheep);
+	update_following_exactly_characters(followingAsSheep);
 
 	_G(our_eip) = 23;
 
diff --git a/engines/ags/engine/main/update.h b/engines/ags/engine/main/update.h
index 9ae7c7de6f2..af804c571a9 100644
--- a/engines/ags/engine/main/update.h
+++ b/engines/ags/engine/main/update.h
@@ -24,8 +24,6 @@
 
 namespace AGS3 {
 
-#define MAX_SHEEP 30    // sheep == follower
-
 int do_movelist_move(short *mlnum, int *xx, int *yy);
 void update_stuff();
 
diff --git a/engines/ags/shared/ac/character_info.h b/engines/ags/shared/ac/character_info.h
index 1f4ae82b648..9f856544c22 100644
--- a/engines/ags/shared/ac/character_info.h
+++ b/engines/ags/shared/ac/character_info.h
@@ -22,6 +22,7 @@
 #ifndef AGS_SHARED_AC_CHARACTER_INFO_H
 #define AGS_SHARED_AC_CHARACTER_INFO_H
 
+#include "ags/lib/std/vector.h"
 #include "ags/shared/ac/common_defines.h" // constants
 
 namespace AGS3 {
@@ -120,14 +121,14 @@ struct CharacterInfo {
 	//
 	// [IKM] 2016-08-26: these methods should NOT be in CharacterInfo class,
 	// bit in distinct runtime character class!
-	void UpdateMoveAndAnim(int &char_index, CharacterExtras *chex, int &numSheep, int *followingAsSheep);
+	void UpdateMoveAndAnim(int &char_index, CharacterExtras *chex, std::vector<int> &followingAsSheep);
 	void UpdateFollowingExactlyCharacter();
 
 	int  update_character_walking(CharacterExtras *chex);
 	void update_character_moving(int &char_index, CharacterExtras *chex, int &doing_nothing);
 	int  update_character_animating(int &char_index, int &doing_nothing);
 	void update_character_idle(CharacterExtras *chex, int &doing_nothing);
-	void update_character_follower(int &char_index, int &numSheep, int *followingAsSheep, int &doing_nothing);
+	void update_character_follower(int &char_index, std::vector<int> &followingAsSheep, int &doing_nothing);
 
 	void ReadFromFile(Shared::Stream *in);
 	void WriteToFile(Shared::Stream *out);


Commit: b13b75712f47c0317cca0d614ab42fafe9b255dc
    https://github.com/scummvm/scummvm/commit/b13b75712f47c0317cca0d614ab42fafe9b255dc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-13T21:55:19-07:00

Commit Message:
AGS: Updated build version (3.6.0.19)

>From upstream 230795414b676a2c1e84ae72488af18b90f71692

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 9da371f8590..a47916eb3d3 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.18"
+#define ACI_VERSION_STR      "3.6.0.19"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3.6.0.18
+#define ACI_VERSION_MSRC_DEF  3.6.0.19
 #endif
 
 #define SPECIAL_VERSION ""


Commit: 596ddd0be6f65a35a8041502616a29c9168af5ba
    https://github.com/scummvm/scummvm/commit/596ddd0be6f65a35a8041502616a29c9168af5ba
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-13T21:55:19-07:00

Commit Message:
AGS: Added some utf-8 utils and moved ConvertUtf8ToAscii to StrUtil

>From upstream 307d40d4b6808ed906e918dd6dcc0332b1d6baa2

Changed paths:
  A engines/ags/shared/util/utf8.h
    engines/ags/engine/ac/translation.cpp
    engines/ags/shared/util/string_utils.cpp
    engines/ags/shared/util/string_utils.h


diff --git a/engines/ags/engine/ac/translation.cpp b/engines/ags/engine/ac/translation.cpp
index ac6704789d1..a00adef38da 100644
--- a/engines/ags/engine/ac/translation.cpp
+++ b/engines/ags/engine/ac/translation.cpp
@@ -29,9 +29,11 @@
 #include "ags/engine/ac/runtime_defines.h"
 #include "ags/engine/ac/translation.h"
 #include "ags/shared/ac/words_dictionary.h"
+#include "ags/shared/core/asset_manager.h"
 #include "ags/shared/debugging/out.h"
 #include "ags/shared/game/tra_file.h"
 #include "ags/shared/util/stream.h"
+#include "ags/shared/util/string_utils.h"
 #include "ags/shared/core/asset_manager.h"
 #include "ags/globals.h"
 
@@ -39,13 +41,6 @@ namespace AGS3 {
 
 using namespace AGS::Shared;
 
-// TODO: Since ScummVM can't use uconvert, this is a dummy implementation for now
-const char *convert_utf8_to_ascii(const char *mbstr, const char *loc_name) {
-	_G(mbbuf).resize(strlen(mbstr) + 1);
-	strcpy(&_G(mbbuf)[0], mbstr);
-	return &_G(mbbuf)[0];
-}
-
 void close_translation() {
 	_GP(transtree).clear();
 	_GP(trans) = Translation();
@@ -124,9 +119,11 @@ bool init_translation(const String &lang, const String &fallback_lang) {
 			_GP(trans).StrOptions["gameencoding"];
 		if (!key_enc.IsEmpty()) {
 			StringMap conv_map;
+			std::vector<char> ascii; // ascii buffer
 			for (const auto &item : _GP(trans).Dict) {
-				String key = convert_utf8_to_ascii(item._key.GetCStr(), key_enc.GetCStr());
-				conv_map.insert(std::make_pair(key, item._value));
+				ascii.resize(item._key.GetLength()); // ascii len will be <= utf-8 len
+				StrUtil::ConvertUtf8ToAscii(item._key.GetCStr(), key_enc.GetCStr(), &ascii[0], ascii.size());
+				conv_map.insert(std::make_pair(String(&ascii[0]), item._value));
 			}
 			_GP(trans).Dict = conv_map;
 		}
diff --git a/engines/ags/shared/util/string_utils.cpp b/engines/ags/shared/util/string_utils.cpp
index e06dbf8c622..44c61176194 100644
--- a/engines/ags/shared/util/string_utils.cpp
+++ b/engines/ags/shared/util/string_utils.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "ags/shared/util/string_utils.h"
+#include "ags/shared/util/utf8.h"
 #include "ags/shared/core/platform.h"
 #include "ags/lib/std/regex.h"
 #include "ags/shared/util/math.h"
@@ -220,6 +221,25 @@ void StrUtil::WriteStringMap(const StringMap &map, Stream *out) {
 	}
 }
 
+size_t StrUtil::ConvertUtf8ToAscii(const char *mbstr, const char *loc_name, char *out_cstr, size_t out_sz) {
+	// TODO: later consider using C++11 conversion methods
+	// First convert utf-8 string into widestring;
+	std::vector<wchar_t> wcsbuf; // widechar buffer
+	wcsbuf.resize(Utf8::GetLength(mbstr) + 1);
+	// NOTE: we don't use mbstowcs, because unfortunately ".utf-8" locale
+	// is not normally supported on all systems (e.g. Windows 7 and earlier)
+	for (size_t at = 0, chr_sz = 0; *mbstr; mbstr += chr_sz, ++at) {
+		Utf8::Rune r;
+		chr_sz = Utf8::GetChar(mbstr, Utf8::UtfSz, &r);
+		wcsbuf[at] = static_cast<wchar_t>(r);
+	}
+	// Then convert widestring to single-byte string using specified locale
+	setlocale(LC_CTYPE, loc_name);
+	size_t res_sz = wcstombs(out_cstr, &wcsbuf[0], out_sz);
+	setlocale(LC_CTYPE, "");
+	return res_sz;
+}
+
 } // namespace Shared
 } // namespace AGS
 } // namespace AGS3
diff --git a/engines/ags/shared/util/string_utils.h b/engines/ags/shared/util/string_utils.h
index 2cf9255c667..c08268858f7 100644
--- a/engines/ags/shared/util/string_utils.h
+++ b/engines/ags/shared/util/string_utils.h
@@ -83,6 +83,10 @@ void            WriteCStr(const String &s, Stream *out);
 void            ReadStringMap(StringMap &map, Stream *in);
 void            WriteStringMap(const StringMap &map, Stream *out);
 
+// Convert utf-8 string to ascii/ansi representation;
+	// writes into out_cstr buffer limited by out_sz bytes; returns bytes written.
+size_t ConvertUtf8ToAscii(const char *mbstr, const char *loc_name, char *out_cstr, size_t out_sz);
+
 } // namespace StrUtil
 } // namespace Shared
 } // namespace AGS
diff --git a/engines/ags/shared/util/utf8.h b/engines/ags/shared/util/utf8.h
new file mode 100644
index 00000000000..33bad60dbdb
--- /dev/null
+++ b/engines/ags/shared/util/utf8.h
@@ -0,0 +1,115 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ //=============================================================================
+ //
+ // UTF-8 utilities.
+ // Based on utf8 code from https://c9x.me/git/irc.git/tree/irc.c
+ //
+ //=============================================================================
+
+#ifndef AGS_SHARED_UTIL_UTF8_H
+#define AGS_SHARED_UTIL_UTF8_H
+
+#include "ags/lib/std/algorithm.h"
+#include "ags/shared/core/types.h"
+
+namespace AGS3 {
+namespace Utf8 {
+
+typedef int32_t Rune;
+const size_t UtfSz = 4;
+const Rune RuneInvalid = 0xFFFD;
+
+const unsigned char utfbyte[UtfSz + 1] = { 0x80,    0, 0xC0, 0xE0, 0xF0 };
+const unsigned char utfmask[UtfSz + 1] = { 0xC0, 0x80, 0xE0, 0xF0, 0xF8 };
+const Rune utfmin[UtfSz + 1] = { 0,    0,  0x80,  0x800,  0x10000 };
+const Rune utfmax[UtfSz + 1] = { 0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF };
+
+
+inline size_t Validate(Rune *u, size_t i) {
+	if (*u < utfmin[i] || *u > utfmax[i] || (0xD800 <= *u && *u <= 0xDFFF))
+		*u = RuneInvalid;
+	for (i = 1; *u > utfmax[i]; ++i)
+		;
+	return i;
+}
+
+inline Rune DecodeByte(unsigned char c, size_t *i) {
+	for (*i = 0; *i < UtfSz + 1; ++(*i))
+		if ((c & utfmask[*i]) == utfbyte[*i])
+			return c & ~utfmask[*i];
+	return 0;
+}
+
+inline char EncodeByte(Rune u, size_t i) {
+	return utfbyte[i] | (u & ~utfmask[i]);
+}
+
+// Read a single utf8 codepoint from the c-string;
+// returns codepoint's size in bytes (may be used to advance string pos)
+inline size_t GetChar(const char *c, size_t clen, Rune *u) {
+	size_t i, j, len, type;
+	Rune udecoded;
+	*u = RuneInvalid;
+	if (!clen || !*c)
+		return 0;
+	udecoded = DecodeByte(c[0], &len);
+	if (len < 1 || len > UtfSz)
+		return 1;
+	for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
+		udecoded = (udecoded << 6) | DecodeByte(c[i], &type);
+		if (type != 0)
+			return j;
+	}
+	if (j < len)
+		return 0;
+	*u = udecoded;
+	Validate(u, len);
+	return len;
+}
+
+// Convert utf8 codepoint to the string representation and write to the buffer
+inline size_t SetChar(Rune u, char *c, size_t clen) {
+	size_t len, i;
+	len = Validate(&u, 0);
+	if (len > UtfSz || len > clen)
+		return 0;
+	for (i = len - 1; i != 0; --i) {
+		c[i] = EncodeByte(u, 0);
+		u >>= 6;
+	}
+	c[0] = EncodeByte(u, len);
+	return len;
+}
+
+// Calculates utf8 string length in characters
+inline size_t GetLength(const char *c) {
+	size_t len = 0;
+	Rune r;
+	for (size_t chr_sz = 0; (chr_sz = GetChar(c, UtfSz, &r)) > 0; c += chr_sz, ++len);
+	return len;
+}
+
+} // namespace Utf8
+} // namespace AGS3
+
+#endif


Commit: 1ec52936d278e7356bf7529a223c782c1f41ae19
    https://github.com/scummvm/scummvm/commit/1ec52936d278e7356bf7529a223c782c1f41ae19
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-13T21:55:19-07:00

Commit Message:
AGS: Added utf8->wcstr conversions to StrUtils

>From upstream 4c2bb8ba25fa651175a854b7f0117bfb37a76d4e

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


diff --git a/engines/ags/shared/util/string_utils.cpp b/engines/ags/shared/util/string_utils.cpp
index 44c61176194..66bbc07f7bd 100644
--- a/engines/ags/shared/util/string_utils.cpp
+++ b/engines/ags/shared/util/string_utils.cpp
@@ -240,6 +240,28 @@ size_t StrUtil::ConvertUtf8ToAscii(const char *mbstr, const char *loc_name, char
 	return res_sz;
 }
 
+size_t StrUtil::ConvertUtf8ToWstr(const char *mbstr, wchar_t *out_wcstr, size_t out_sz) {
+	size_t len = 0;
+	for (size_t mb_sz = 1; *mbstr && (mb_sz > 0) && (len < out_sz);
+		mbstr += mb_sz, ++out_wcstr, ++len) {
+		Utf8::Rune r;
+		mb_sz = Utf8::GetChar(mbstr, Utf8::UtfSz, &r);
+		*out_wcstr = static_cast<wchar_t>(r);
+	}
+	*out_wcstr = 0;
+	return len;
+}
+
+size_t StrUtil::ConvertWstrToUtf8(const wchar_t *wcstr, char *out_mbstr, size_t out_sz) {
+	size_t len = 0;
+	for (size_t mb_sz = 1; *wcstr && (mb_sz > 0) && (len + mb_sz < out_sz);
+		++wcstr, out_mbstr += mb_sz, len += mb_sz) {
+		mb_sz = Utf8::SetChar(*wcstr, out_mbstr, out_sz - len);
+	}
+	*out_mbstr = 0;
+	return len;
+}
+
 } // namespace Shared
 } // namespace AGS
 } // namespace AGS3
diff --git a/engines/ags/shared/util/string_utils.h b/engines/ags/shared/util/string_utils.h
index c08268858f7..623961a77ed 100644
--- a/engines/ags/shared/util/string_utils.h
+++ b/engines/ags/shared/util/string_utils.h
@@ -86,6 +86,12 @@ void            WriteStringMap(const StringMap &map, Stream *out);
 // Convert utf-8 string to ascii/ansi representation;
 	// writes into out_cstr buffer limited by out_sz bytes; returns bytes written.
 size_t ConvertUtf8ToAscii(const char *mbstr, const char *loc_name, char *out_cstr, size_t out_sz);
+// Convert utf-8 string to wide-string (16-bit char);
+// writes into out_wcstr buffer limited by out_sz *wchars*; returns *wchars* written.
+size_t ConvertUtf8ToWstr(const char *mbstr, wchar_t *out_wcstr, size_t out_sz);
+// Convert wide-string to utf-8 string;
+// writes into out_mbstr buffer limited by out_sz *bytes*; returns *bytes* written.
+size_t ConvertWstrToUtf8(const wchar_t *wcstr, char *out_mbstr, size_t out_sz);
 
 } // namespace StrUtil
 } // namespace Shared


Commit: 0e0063bb6bb13e6ea17938f96251a681b4c1c50d
    https://github.com/scummvm/scummvm/commit/0e0063bb6bb13e6ea17938f96251a681b4c1c50d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-13T21:55:19-07:00

Commit Message:
AGS: String.AppendChar, ReplaceChar, Chars[] support unicode

>From upstream 072ed5716639e2b65a16037dd81e2a332a78611b

Changed paths:
    engines/ags/engine/ac/string.cpp
    engines/ags/engine/ac/string.h
    engines/ags/lib/allegro/unicode.cpp
    engines/ags/lib/allegro/unicode.h


diff --git a/engines/ags/engine/ac/string.cpp b/engines/ags/engine/ac/string.cpp
index 5e32d87e8bd..dc5dda16662 100644
--- a/engines/ags/engine/ac/string.cpp
+++ b/engines/ags/engine/ac/string.cpp
@@ -22,6 +22,7 @@
 #include "ags/lib/std/algorithm.h"
 #include "ags/engine/ac/string.h"
 #include "ags/shared/ac/common.h"
+#include "ags/shared/util/utf8.h"
 #include "ags/engine/ac/display.h"
 #include "ags/shared/ac/game_setup_struct.h"
 #include "ags/engine/ac/game_state.h"
@@ -58,13 +59,19 @@ const char *String_Append(const char *thisString, const char *extrabit) {
 	return CreateNewScriptString(buffer, false);
 }
 
-const char *String_AppendChar(const char *thisString, char extraOne) {
-	char *buffer = (char *)malloc(strlen(thisString) + 2);
-	sprintf(buffer, "%s%c", thisString, extraOne);
+const char *String_AppendChar(const char *thisString, int extraOne) {
+	size_t chw = 1;
+	char chr[Utf8::UtfSz + 1]{};
+	if (get_uformat() == U_UTF8)
+		chw = Utf8::SetChar(extraOne, chr, sizeof(chr));
+	else
+		chr[0] = extraOne;
+	char *buffer = (char *)malloc(strlen(thisString) + chw + 1);
+	sprintf(buffer, "%s%s", thisString, chr);
 	return CreateNewScriptString(buffer, false);
 }
 
-const char *String_ReplaceCharAt(const char *thisString, int index, char newChar) {
+const char *String_ReplaceCharAt(const char *thisString, int index, int newChar) {
 	size_t len = ustrlen(thisString);
 	if ((index < 0) || ((size_t)index >= len))
 		quit("!String.ReplaceCharAt: index outside range of string");
@@ -73,12 +80,17 @@ const char *String_ReplaceCharAt(const char *thisString, int index, char newChar
 	int uchar = ugetc(thisString + off);
 	size_t remain_sz = strlen(thisString + off);
 	size_t old_sz = ucwidth(uchar);
-	size_t new_sz = sizeof(char); // TODO: support unicode char in API
-	size_t total_sz = off + remain_sz + new_sz - old_sz + 1;
+	size_t new_chw = 1;
+	char new_chr[Utf8::UtfSz + 1]{};
+	if (get_uformat() == U_UTF8)
+		new_chw = Utf8::SetChar(newChar, new_chr, sizeof(new_chr));
+	else
+		new_chr[0] = newChar;
+	size_t total_sz = off + remain_sz + new_chw - old_sz + 1;
 	char *buffer = (char *)malloc(total_sz);
 	memcpy(buffer, thisString, off);
-	usetc(buffer + off, newChar);
-	memcpy(buffer + off + new_sz, thisString + off + old_sz, remain_sz - old_sz + 1);
+	memcpy(buffer + off, new_chr, new_chw);
+	memcpy(buffer + off + new_chw, thisString + off + old_sz, remain_sz - old_sz + 1);
 	return CreateNewScriptString(buffer, false);
 }
 
@@ -194,11 +206,10 @@ const char *String_UpperCase(const char *thisString) {
 }
 
 int String_GetChars(const char *texx, int index) {
-	if ((index < 0) || (index >= (int)strlen(texx)))
+	if ((index < 0) || (index >= ustrlen(texx)))
 		return 0;
-	return texx[index];
+	return ugetat(texx, index);
 }
-
 int StringToInt(const char *stino) {
 	return atoi(stino);
 }
diff --git a/engines/ags/engine/ac/string.h b/engines/ags/engine/ac/string.h
index eb13551a75e..abc93a49f22 100644
--- a/engines/ags/engine/ac/string.h
+++ b/engines/ags/engine/ac/string.h
@@ -33,13 +33,13 @@ namespace AGS3 {
 int String_IsNullOrEmpty(const char *thisString);
 const char *String_Copy(const char *srcString);
 const char *String_Append(const char *thisString, const char *extrabit);
-const char *String_AppendChar(const char *thisString, char extraOne);
-const char *String_ReplaceCharAt(const char *thisString, int index, char newChar);
+const char *String_AppendChar(const char *thisString, int extraOne);
+const char *String_ReplaceCharAt(const char *thisString, int index, int newChar);
 const char *String_Truncate(const char *thisString, int length);
 const char *String_Substring(const char *thisString, int index, int length);
 int String_CompareTo(const char *thisString, const char *otherString, bool caseSensitive);
 int String_StartsWith(const char *thisString, const char *checkForString, bool caseSensitive);
-int String_EfndsWith(const char *thisString, const char *checkForString, bool caseSensitive);
+int String_EndsWith(const char *thisString, const char *checkForString, bool caseSensitive);
 const char *String_Replace(const char *thisString, const char *lookForText, const char *replaceWithText, bool caseSensitive);
 const char *String_LowerCase(const char *thisString);
 const char *String_UpperCase(const char *thisString);
diff --git a/engines/ags/lib/allegro/unicode.cpp b/engines/ags/lib/allegro/unicode.cpp
index 1726b7825a3..4fe5b991092 100644
--- a/engines/ags/lib/allegro/unicode.cpp
+++ b/engines/ags/lib/allegro/unicode.cpp
@@ -1206,6 +1206,11 @@ int uoffset(const char *s, int index) {
 	return (intptr_t)s - (intptr_t)orig;
 }
 
+int ugetat(const char *s, int index) {
+	assert(s);
+	return ugetc(s + uoffset(s, index));
+}
+
 char *ustrlwr(char *s) {
 	int pos = 0;
 	int c, lc;
diff --git a/engines/ags/lib/allegro/unicode.h b/engines/ags/lib/allegro/unicode.h
index ef1175d524e..b3310f27b99 100644
--- a/engines/ags/lib/allegro/unicode.h
+++ b/engines/ags/lib/allegro/unicode.h
@@ -113,6 +113,12 @@ extern int ustrnicmp(const char *s1, const char *s2, int n);
  *  last character).
  */
 extern int uoffset(const char *s, int index);
+
+/* ugetat:
+ *  Returns the character from the specified index within the string.
+ */
+extern int ugetat(const char *s, int idx);
+
 /* ustrlwr:
  *  Unicode-aware version of the ANSI strlwr() function.
  */


Commit: 9fe89e4f1dae9768769c13313d4a76a8479fb952
    https://github.com/scummvm/scummvm/commit/9fe89e4f1dae9768769c13313d4a76a8479fb952
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-13T21:55:19-07:00

Commit Message:
AGS: String.Format treats %c as possibly unicode

>From upstream fe4afad4edecac5724711adb1d801dfb973effff

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


diff --git a/engines/ags/engine/script/script_api.cpp b/engines/ags/engine/script/script_api.cpp
index ecef4e14c43..e478e37d82d 100644
--- a/engines/ags/engine/script/script_api.cpp
+++ b/engines/ags/engine/script/script_api.cpp
@@ -24,6 +24,7 @@
 #include "ags/engine/script/runtime_script_value.h"
 #include "ags/engine/script/script_api.h"
 #include "ags/shared/util/math.h"
+#include "ags/shared/util/utf8.h"
 #include "ags/globals.h"
 
 namespace AGS3 {
@@ -36,6 +37,7 @@ enum FormatParseResult {
 	kFormatParseLiteralPercent,
 	kFormatParseArgInteger,
 	kFormatParseArgFloat,
+	kFormatParseArgCharacter,
 	kFormatParseArgString,
 	kFormatParseArgPointer,
 
@@ -131,9 +133,11 @@ const char *ScriptSprintf(char *buffer, size_t buf_length, const char *format,
 				case 'u':
 				case 'x':
 				case 'X':
-				case 'c':
 					fmt_done = kFormatParseArgInteger;
 					break;
+				case 'c':
+					fmt_done = kFormatParseArgCharacter;
+					break;
 				case 'e':
 				case 'E':
 				case 'f':
@@ -176,15 +180,28 @@ const char *ScriptSprintf(char *buffer, size_t buf_length, const char *format,
 				// NOTE: snprintf is called with avail_outbuf + 1 here, because we let it use our reserved
 				// character for null-terminator, in case we are at the end of the buffer
 				*fmt_bufptr = 0; // terminate the format buffer, we are going to use it
-				if (fmt_done == kFormatParseArgInteger)
-					snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, GetArgInt(sc_args, varg_ptr, arg_idx));
-				else if (fmt_done == kFormatParseArgFloat)
-					snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, GetArgFloat(sc_args, varg_ptr, arg_idx));
-				else {
+				switch (fmt_done) {
+				case kFormatParseArgInteger:
+					snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, GetArgInt(sc_args, varg_ptr, arg_idx)); break;
+				case kFormatParseArgFloat:
+					snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, GetArgFloat(sc_args, varg_ptr, arg_idx)); break;
+				case kFormatParseArgCharacter:
+				{
+					int chr = GetArgInt(sc_args, varg_ptr, arg_idx);
+					char cbuf[Utf8::UtfSz + 1]{};
+					if (get_uformat() == U_UTF8)
+						Utf8::SetChar(chr, cbuf, Utf8::UtfSz);
+					else
+						cbuf[0] = chr;
+					snprintf_res = snprintf(out_ptr, avail_outbuf + 1, "%s", cbuf);
+					break;
+				}
+				case kFormatParseArgString:
+				{
 					const char *p = GetArgPtr(sc_args, varg_ptr, arg_idx);
 					// Do extra checks for %s placeholder
 					if (fmt_done == kFormatParseArgString && !p) {
-						if (_G(loaded_game_file_version) < kGameVersion_320) {
+						if (loaded_game_file_version < kGameVersion_320) {
 							// explicitly put "(null)" into the placeholder
 							p = "(null)";
 						} else {
@@ -196,6 +213,11 @@ const char *ScriptSprintf(char *buffer, size_t buf_length, const char *format,
 						return "";
 					}
 					snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, p);
+					break;
+				}
+				case kFormatParseArgPointer:
+					snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, GetArgPtr(sc_args, varg_ptr, arg_idx)); break;
+				default: /* should not happen */ break;
 				}
 
 				arg_idx++;


Commit: 007baa9541c8e5831316ec0efabf275f111ee925
    https://github.com/scummvm/scummvm/commit/007baa9541c8e5831316ec0efabf275f111ee925
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-13T21:55:20-07:00

Commit Message:
AGS: Restore proper text format in close_translation()

>From upstream b83d8567ae76c3840784f55aa8d06ffd9a7fdf91

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


diff --git a/engines/ags/engine/ac/translation.cpp b/engines/ags/engine/ac/translation.cpp
index a00adef38da..e177d09f378 100644
--- a/engines/ags/engine/ac/translation.cpp
+++ b/engines/ags/engine/ac/translation.cpp
@@ -48,7 +48,10 @@ void close_translation() {
 	_G(trans_filename) = "";
 
 	// Return back to default game's encoding
-	set_uformat(U_ASCII);
+	if (_GP(game).options[OPT_GAMETEXTENCODING] == 65001) // utf-8 codepage number
+		set_uformat(U_UTF8);
+	else
+		set_uformat(U_ASCII);
 }
 
 bool init_translation(const String &lang, const String &fallback_lang) {




More information about the Scummvm-git-logs mailing list