From 1b24c97e06425e5f6d4413d91edb97c287f2e7b5 Mon Sep 17 00:00:00 2001
From: Tim Felgentreff <timfelgentreff@gmail.com>
Date: Mon, 24 Jan 2022 08:48:34 +0100
Subject: [PATCH 1/5] fix SetUnitVariable("Color")

---
 src/unit/script_unit.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp
index cd78ae96b..51fc6cda3 100644
--- a/src/unit/script_unit.cpp
+++ b/src/unit/script_unit.cpp
@@ -1315,6 +1315,7 @@ static int CclSetUnitVariable(lua_State *l)
 	} else if (!strcmp(name, "Color")) {
 		value = LuaToNumber(l, 3);
 		unit->Colors = &Players[value].UnitColors;
+		unit->RescuedFrom = &Players[value];
 	} else if (!strcmp(name, "TTL")) {
 		value = LuaToNumber(l, 3);
 		unit->TTL = GameCycle + value;

From 71c3db35201fb8bd38c2c79bda7852b57db44b2f Mon Sep 17 00:00:00 2001
From: Tim Felgentreff <timfelgentreff@gmail.com>
Date: Mon, 24 Jan 2022 09:48:36 +0100
Subject: [PATCH 2/5] add GiveUnitsToPlayer function

---
 src/stratagus/script_player.cpp | 107 ++++++++++++++++++++++++++++++++
 1 file changed, 107 insertions(+)

diff --git a/src/stratagus/script_player.cpp b/src/stratagus/script_player.cpp
index eed448155..119bb6dbb 100644
--- a/src/stratagus/script_player.cpp
+++ b/src/stratagus/script_player.cpp
@@ -367,6 +367,112 @@ static int CclChangeUnitsOwner(lua_State *l)
 	return 0;
 }
 
+/**
+** <b>Description</b>
+**
+**  <strong>GiveUnitsToPlayer(amount, type, fromPlayer, toPlayer)</strong>
+**  <strong>GiveUnitsToPlayer(amount, type, topLeft, bottomRight, fromPlayer, toPlayer)</strong>
+**  Give some units of a specific type from a player to another player. Optionally only inside a rectangle.
+**  Returns number of units actually assigned. This can be smaller than the requested amount if the
+**  <code>fromPlayer</code> did not have enough units.<br/>
+**
+**  Instead of a number you can pass "all" as the first argument, to hand over all units.<br/>
+**  
+**  Instead of a unit type name, you can pass "any", "unit", "building" as the second argument,
+**  to hand over anything, and unit, or any building.
+**
+**  @param l  Lua state.
+**
+** Example:
+**
+** <div class="example"><code>
+**   -- Give 2 peasants from player 4 to player 2
+**   GiveUnitsToPlayer(2, "unit-peasant", 4, 2)
+**   -- Give 4 knights from player 5 to player 1 inside the rectangle 2,2 - 14,14
+**   GiveUnitsToPlayer(2, "unit-peasant", {2,2}, {14,14}, 4, 2)
+**   -- Give any 4 units from player 5 to player 1 inside the rectangle 2,2 - 14,14
+**   GiveUnitsToPlayer(2, "any", 4, 2)
+** </code></div>
+**
+*/
+static int CclGiveUnitsToPlayer(lua_State *l)
+{
+	int args = lua_gettop(l);
+	if (args != 4 && args != 6) {
+		LuaError(l, "incorrect argument count for GiveUnitsToPlayer, need 4 or 6 args");
+	}
+
+	int cnt;
+	if (lua_isnumber(l, 1)) {
+		cnt = LuaToNumber(l, 1);
+	} else {
+		std::string cntStr = std::string(LuaToString(l, 1));
+		if (cntStr != "all") {
+			LuaError(l, "incorrect 1st argument to GiveUnitsToPlayer. Must be number or 'all'");
+		}
+		cnt = std::numeric_limits<int>::max();
+	}
+
+	const int oldp = LuaToNumber(l, args == 4 ? 3 : 5);
+	const int newp = LuaToNumber(l, args == 4 ? 4 : 6);
+
+	std::string typestr = std::string(LuaToString(l, 2));
+	int assignedCnt = 0;
+
+	CUnitType *type = nullptr;
+	bool any = false;
+	bool onlyUnits = false;
+	bool onlyBuildings = false;
+	if (any = (typestr == "any")) {
+	} else if (onlyUnits = (typestr == "unit")) {
+	} else if (onlyBuildings = (typestr == "building")) {
+	} else {
+		type = UnitTypeByIdent(LuaToString(l, 2));
+		if (!type) {
+			LuaError(l, "incorrect 2nd argument to GiveUnitsToPlayer. Must be a unit type or 'any', 'unit', or 'building'");
+		}
+	}
+
+	if (cnt > 0) {
+		std::vector<CUnit *> table;
+		if (args == 6) {
+			Vec2i pos1;
+			Vec2i pos2;
+			CclGetPos(l, &pos1.x, &pos1.y, 3);
+			CclGetPos(l, &pos2.x, &pos2.y, 4);
+			if (any) {
+				Select(pos1, pos2, table, HasSamePlayerAs(Players[oldp]));
+			} else if (onlyUnits) {
+				Select(pos1, pos2, table, AndPredicate(HasSamePlayerAs(Players[oldp]), NotPredicate(IsBuildingType())));
+			} else if (onlyBuildings) {
+				Select(pos1, pos2, table, AndPredicate(HasSamePlayerAs(Players[oldp]), IsBuildingType()));
+			} else {
+				Select(pos1, pos2, table, HasSamePlayerAndTypeAs(Players[oldp], *type));
+			}
+			for (size_t i = 0; i != table.size() && cnt > 0; ++i) {
+				table[i]->ChangeOwner(Players[newp]);
+				assignedCnt++;
+				cnt--;
+			}
+		} else {
+			std::vector<CUnit *> table;
+			for (std::vector<CUnit *>::const_iterator it = Players[oldp].UnitBegin(); it != Players[oldp].UnitEnd() && cnt > 0; ++it) {
+				CUnit *unit = *it;
+				if (any || (onlyUnits && !unit->Type->Building) || (onlyBuildings && unit->Type->Building) || (type == unit->Type)) {
+					table.push_back(unit);
+				}
+			}
+			for (auto unit : table) {
+				unit->ChangeOwner(Players[newp]);
+			}
+			assignedCnt = table.size();
+		}
+	}
+
+	lua_pushnumber(l, assignedCnt);
+	return 1;
+}
+
 /**
 ** <b>Description</b>
 **
@@ -1103,6 +1209,7 @@ void PlayerCclRegister()
 {
 	lua_register(Lua, "Player", CclPlayer);
 	lua_register(Lua, "ChangeUnitsOwner", CclChangeUnitsOwner);
+	lua_register(Lua, "GiveUnitsToPlayer", CclGiveUnitsToPlayer);
 	lua_register(Lua, "GetThisPlayer", CclGetThisPlayer);
 	lua_register(Lua, "SetThisPlayer", CclSetThisPlayer);
 

From 61e2ad6752f91b7ee594a1d736d8b2e01498aea3 Mon Sep 17 00:00:00 2001
From: Tim Felgentreff <timfelgentreff@gmail.com>
Date: Mon, 24 Jan 2022 10:45:34 +0100
Subject: [PATCH 3/5] fix a crash

---
 src/ui/interface.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp
index 546d0b0ed..be740df55 100644
--- a/src/ui/interface.cpp
+++ b/src/ui/interface.cpp
@@ -927,6 +927,9 @@ static void InputKey(int key)
 		case SDLK_UP:
 			removeCursorFromInput();
 			strncpy(InputHistory + (InputHistoryPos * sizeof(Input)), Input, sizeof(Input));
+			if (InputHistorySize == 0) {
+				break;
+			}
 			InputHistoryPos = ((InputHistoryPos - 1) % InputHistorySize + InputHistorySize) % InputHistorySize;
 			strncpy(Input, InputHistory + (InputHistoryPos * sizeof(Input)), sizeof(Input));
 			InputIndex = strlen(Input);
@@ -937,6 +940,9 @@ static void InputKey(int key)
 		case SDLK_DOWN:
 			removeCursorFromInput();
 			strncpy(InputHistory + (InputHistoryPos * sizeof(Input)), Input, sizeof(Input));
+			if (InputHistorySize == 0) {
+				break;
+			}
 			InputHistoryPos = ((InputHistoryPos + 1) % InputHistorySize + InputHistorySize) % InputHistorySize;
 			strncpy(Input, InputHistory + (InputHistoryPos * sizeof(Input)), sizeof(Input));
 			InputIndex = strlen(Input);

From 2854d6afcce023a203e990b4a99d053d3a03c402 Mon Sep 17 00:00:00 2001
From: Tim Felgentreff <timfelgentreff@gmail.com>
Date: Tue, 25 Jan 2022 10:06:04 +0100
Subject: [PATCH 4/5] use lookup tables for cp437 and cp866 font graphics

---
 src/video/font.cpp | 105 ++++++++++++++++++++++++++++++---------------
 1 file changed, 70 insertions(+), 35 deletions(-)

diff --git a/src/video/font.cpp b/src/video/font.cpp
index 026fd7302..bb16a3d47 100644
--- a/src/video/font.cpp
+++ b/src/video/font.cpp
@@ -74,26 +74,6 @@ static CFont *GameFont;   /// Normal font used in game
 
 static int FormatNumber(int number, char *buf);
 
-static const unsigned char extended_ascii[] = {
-	// char translation table to convert UTF8 (index) into Stratagus char (value)
-	// relevant for char > 0x80 (extendend ascii range)
-	// ______ascii value__________________________     __utf8___
-	0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
-	0x00,  '!', 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, /* 0xa0-0xa7 */ // Convert ยก to !
-	0x00, 0x00, 0x00, 0x00, 0xaa,  '-', 0xa9, 0x00, /* 0xa8-0xaf */
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
-	0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0xa8, /* 0xb8-0xbf */
-	0x00, 0xa0, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, /* 0xc0-0xc7 */
-	0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
-	0x00, 0xa5, 0x00, 0x95, 0x00, 0x00, 0x99, 0x00, /* 0xd0-0xd7 */
-	0x9d, 0x97, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
-	0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, /* 0xe0-0xe7 */
-	0x8a, 0x82, 0x88, 0x89, 0x00, 0xa1, 0x8c, 0x8b, /* 0xe8-0xef */
-	0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0x00, /* 0xf0-0xf7 */
-	0x9b, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98, /* 0xf8-0xff */
-};
-
 CFont &GetSmallFont()
 {
 	if (!SmallFont) {
@@ -190,25 +170,80 @@ void GetDefaultTextColors(std::string &normalp, std::string &reversep)
 	reversep = DefaultReverseColorIndex;
 }
 
+// mapping from utf8 chars 0xa0 - 0xff into codepage 437
+static const unsigned char cp437[] = {
+	  0xff,   0xad,   0x9b,   0x9c,   0x00,   0x9d,   0x00,   0x00,   0x00,   0x00,   0xa6,   0xae,   0xaa,   0x00,   0x00,   0x00, // 0xa0
+	  0xf8,   0xf1,   0xfd,   0x00,   0x00,   0xe6,   0x00,   0xfa,   0x00,   0x00,   0xa7,   0xaf,   0xac,   0xab,   0x00,   0xa8, // 0xb0
+	  0x00,   0x00,   0x00,   0x00,   0x8e,   0x8f,   0x92,   0x80,   0x00,   0x90,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00, // 0xc0
+	  0x00,   0xa5,   0x00,   0x00,    0x00,  0x00,   0x99,   0x00,   0x00,   0x00,   0x00,   0x00,   0x9a,   0x00,   0x00,   0xe1, // 0xd0
+	  0x85,   0xa0,   0x83,   0x00,   0x84,   0x86,   0x91,   0x87,   0x8a,   0x82,   0x88,   0x89,   0x8d,   0xa1,   0x8c,   0x8b, // 0xe0
+	  0x00,   0xa4,   0x95,   0xa2,   0x93,   0x00,   0x94,   0xf6,   0x00,   0x97,   0xa3,   0x96,   0x81,   0x00,   0x00,   0x98  // 0xf0
+};
+
+// mapping from utf8 chars 0x400 - 0x450 (cyrillic) into codepage 866
+static const unsigned char cp866[] = {
+	  0x00,   0xf0,   0x00,   0x00,   0xf2,   0x00,   0x00,   0xf4,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0xf6,   0x00, // 0x400
+	  0x80,   0x81,   0x82,   0x83,   0x84,   0x85,   0x86,   0x87,   0x88,   0x89,   0x8a,   0x8b,   0x8c,   0x8d,   0x8e,   0x8f, // 0x410
+	  0x90,   0x91,   0x92,   0x93,   0x94,   0x95,   0x96,   0x97,   0x98,   0x99,   0x9a,   0x9b,   0x9c,   0x9d,   0x9e,   0x9f, // 0x420
+	  0xa0,   0xa1,   0xa2,   0xa3,   0xa4,   0xa5,   0xa6,   0xa7,   0xa8,   0xa9,   0xaa,   0xab,   0xac,   0xad,   0xae,   0xaf, // 0x430
+	  0xe0,   0xe1,   0xe2,   0xe3,   0xe4,   0xe5,   0xe6,   0xe7,   0xe8,   0xe9,   0xea,   0xeb,   0xec,   0xed,   0xee,   0xef, // 0x440
+	  0x00,   0xf1,   0x00,   0x00,   0xf3,   0x00,   0x00,   0xf5,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0xf7,   0x00  // 0x450
+};
+
 /**
 ** Convert a UTF8 char to an ASCII-extended char, based on the
-** convert table to work with Stratagus char.
-** @param utf8 the char to convert (must be in range 0x90..0xFF)
-** @return the ASCII equivalent, or '?' if char is unsuported.
+** convert table to work with Stratagus char. This maps UTF8
+** chars into codepages commonly used for DOS games; currently
+** CP437 and CP866. More can be added. The games need to ensure
+** that the font graphic represents the appropriate codepage.
+**
+** @param utf8 the char to convert
+** @return the codepage equivalent, or '?' if char is unsuported.
 */
-static int utf8_to_ascii(const int utf8)
+static int utf8_to_codepage(int utf8)
 {
-	Assert(utf8 > 0x80);
-	int ascii_idx = (utf8 & 0xFF) - 0x90;
-	int newutf8 = 0x00;
-	if (ascii_idx >= 0 && ascii_idx < sizeof(extended_ascii)) {
-		newutf8 = extended_ascii[(utf8 & 0xFF) - 0x90];
+	Assert(utf8 >= 0x80);
+
+	int cpChar = 0;
+
+	switch (utf8) {
+		case 0xa1: // special case: print inverted exclamation mark as normal one
+			cpChar = '!';
+			break;
+		case 0x192: // index for this letter in cp437 is 0x9f
+			cpChar = 0x9f;
+			break;
+		case 0x2502:
+			cpChar = 0xb3;
+		case 0x2591:
+			cpChar = 0xb0;
+			break;
+		case 0x2592:
+			cpChar = 0xb1;
+			break;
+		case 0x2593:
+			cpChar = 0xb2;
+			break;
+		default:
+			utf8 -= 0xa0;
+			if (utf8 >= 0 && utf8 < sizeof(cp437)) {
+				// western european, index into cp437
+				cpChar = cp437[utf8];
+			} else {
+				utf8 -= (0x400 - 0xa0);
+				if (utf8 >= 0 && utf8 < sizeof(cp866)) {
+					// cyrillic, index into cp866
+					cpChar = cp866[utf8];
+				}
+			}
+			break;
 	}
-	if (newutf8 < 0x32) {
-		fprintf(stderr, "Can't convert UTF8 char to Ascii : '%c' d=%d (0x%04x)\r\n", utf8, utf8, utf8);
-		newutf8 = '?';
+
+	if (cpChar < 0x32) {
+		fprintf(stderr, "Can't convert UTF8 char to codepage: '%c' d=%d (0x%04x)\r\n", utf8, utf8, utf8);
+		cpChar = '?';
 	}
-	return newutf8;
+	return cpChar;
 }
 /**
 **  Get the next utf8 character from a string
@@ -259,7 +294,7 @@ static bool GetUTF8(const std::string &text, size_t &pos, int &utf8)
 		utf8 |= (c & 0x3F);
 	}
 
-	int ascii = utf8_to_ascii(utf8);
+	int ascii = utf8_to_codepage(utf8);
 	utf8 = ascii;
 
 	return true;
@@ -314,7 +349,7 @@ static bool GetUTF8(const char text[], const size_t len, size_t &pos, int &utf8)
 		utf8 |= (c & 0x3F);
 	}
 	
-	int ascii = utf8_to_ascii(utf8);
+	int ascii = utf8_to_codepage(utf8);
 	utf8 = ascii;
 
 	return true;

From 28d2cb66ab242c716ed4c33a168d4eab2eff354e Mon Sep 17 00:00:00 2001
From: Tim Felgentreff <timfelgentreff@gmail.com>
Date: Wed, 26 Jan 2022 20:13:18 +0100
Subject: [PATCH 5/5] add support for mapping between codepages

---
 src/include/font.h   |  1 +
 src/ui/script_ui.cpp | 23 +++++++++++
 src/video/font.cpp   | 98 ++++++++++++++++++++++++++++++++------------
 3 files changed, 96 insertions(+), 26 deletions(-)

diff --git a/src/include/font.h b/src/include/font.h
index 052e5bb0c..5f72e2e5c 100644
--- a/src/include/font.h
+++ b/src/include/font.h
@@ -155,6 +155,7 @@ extern CFont &GetSmallFont();  /// Small font used in stats
 extern CFont &GetGameFont();   /// Normal font used in game
 extern bool IsGameFontReady(); /// true when GameFont is provided
 
+extern int FontCodePage;
 
 /// Set the default text colors for normal and reverse text
 extern void SetDefaultTextColors(const std::string &normal, const std::string &reverse);
diff --git a/src/ui/script_ui.cpp b/src/ui/script_ui.cpp
index 52b52cad4..de4f476a7 100644
--- a/src/ui/script_ui.cpp
+++ b/src/ui/script_ui.cpp
@@ -1281,6 +1281,27 @@ static int CclDefineMapSetup(lua_State *l)
 
 	return 0;
 }
+/**
+** <b>Description</b>
+**
+** Declare which codepage the font files are in. Text is handled internally
+** as UTF-8 everywhere, but the font rendering system uses graphics with 256
+** symbols. Commonly, DOS and early Windows games used codepage 437 or 1252 for
+** western European languages, or 866 for Russian and some other cyrillic
+** writing systems. These are the only ones that are currently supported, but
+** more can easily be added. All text is mapped into the codepage that is set
+** for the font files. If something doesn't map (for example, some accented
+** characters with codepage 866, or cyrillic letters with codepage 437), a simple
+** "visual" mapping is used to at least print something.
+**
+*/
+static int CclSetFontCodePage(lua_State *l)
+{
+	LuaCheckArgs(l, 1);
+	FontCodePage = LuaToNumber(l, 1);
+
+	return 0;
+}
 
 /**
 **  Register CCL features for UI.
@@ -1309,6 +1330,8 @@ void UserInterfaceCclRegister()
 	lua_register(Lua, "SetWindowSize", CclSetWindowSize);
 	lua_register(Lua, "SetVerticalPixelSize", CclSetVerticalPixelSize);
 
+	lua_register(Lua, "SetFontCodePage", CclSetFontCodePage);
+
 	lua_register(Lua, "SetTitleScreens", CclSetTitleScreens);
 	lua_register(Lua, "ShowTitleScreens", CclShowTitleScreens);
 
diff --git a/src/video/font.cpp b/src/video/font.cpp
index bb16a3d47..8bba01e8e 100644
--- a/src/video/font.cpp
+++ b/src/video/font.cpp
@@ -170,32 +170,61 @@ void GetDefaultTextColors(std::string &normalp, std::string &reversep)
 	reversep = DefaultReverseColorIndex;
 }
 
-// mapping from utf8 chars 0xa0 - 0xff into codepage 437
-static const unsigned char cp437[] = {
-	  0xff,   0xad,   0x9b,   0x9c,   0x00,   0x9d,   0x00,   0x00,   0x00,   0x00,   0xa6,   0xae,   0xaa,   0x00,   0x00,   0x00, // 0xa0
-	  0xf8,   0xf1,   0xfd,   0x00,   0x00,   0xe6,   0x00,   0xfa,   0x00,   0x00,   0xa7,   0xaf,   0xac,   0xab,   0x00,   0xa8, // 0xb0
-	  0x00,   0x00,   0x00,   0x00,   0x8e,   0x8f,   0x92,   0x80,   0x00,   0x90,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00, // 0xc0
-	  0x00,   0xa5,   0x00,   0x00,    0x00,  0x00,   0x99,   0x00,   0x00,   0x00,   0x00,   0x00,   0x9a,   0x00,   0x00,   0xe1, // 0xd0
-	  0x85,   0xa0,   0x83,   0x00,   0x84,   0x86,   0x91,   0x87,   0x8a,   0x82,   0x88,   0x89,   0x8d,   0xa1,   0x8c,   0x8b, // 0xe0
-	  0x00,   0xa4,   0x95,   0xa2,   0x93,   0x00,   0x94,   0xf6,   0x00,   0x97,   0xa3,   0x96,   0x81,   0x00,   0x00,   0x98  // 0xf0
+static const unsigned char utf8_europe_to_cp437[] = {
+    0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00, // 0xa0
+    0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8, // 0xb0
+    0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xc0
+    0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1, // 0xd0
+    0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, // 0xe0
+    0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, 0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98, // 0xf0
 };
 
-// mapping from utf8 chars 0x400 - 0x450 (cyrillic) into codepage 866
-static const unsigned char cp866[] = {
-	  0x00,   0xf0,   0x00,   0x00,   0xf2,   0x00,   0x00,   0xf4,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0xf6,   0x00, // 0x400
-	  0x80,   0x81,   0x82,   0x83,   0x84,   0x85,   0x86,   0x87,   0x88,   0x89,   0x8a,   0x8b,   0x8c,   0x8d,   0x8e,   0x8f, // 0x410
-	  0x90,   0x91,   0x92,   0x93,   0x94,   0x95,   0x96,   0x97,   0x98,   0x99,   0x9a,   0x9b,   0x9c,   0x9d,   0x9e,   0x9f, // 0x420
-	  0xa0,   0xa1,   0xa2,   0xa3,   0xa4,   0xa5,   0xa6,   0xa7,   0xa8,   0xa9,   0xaa,   0xab,   0xac,   0xad,   0xae,   0xaf, // 0x430
-	  0xe0,   0xe1,   0xe2,   0xe3,   0xe4,   0xe5,   0xe6,   0xe7,   0xe8,   0xe9,   0xea,   0xeb,   0xec,   0xed,   0xee,   0xef, // 0x440
-	  0x00,   0xf1,   0x00,   0x00,   0xf3,   0x00,   0x00,   0xf5,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0xf7,   0x00  // 0x450
+static const unsigned char utf8_europe_to_cp1252[] = {
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, // 0xa0
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, // 0xb0
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, // 0xc0
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, // 0xd0
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, // 0xe0
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, // 0xf0
 };
 
+static const unsigned char utf8_europe_to_ascii[] = {
+    0x20, 0x21, 0x43, 0x50, 0x24, 0x59, 0x7c, 0x53, 0x22, 0x28, 0x61, 0x3c, 0x21, 0x00, 0x28, 0x2d, // 0xa0
+    0x64, 0x2b, 0x32, 0x33, 0x27, 0x75, 0x50, 0x2a, 0x2c, 0x31, 0x6f, 0x3e, 0x20, 0x20, 0x20, 0x3f, // 0xb0
+    0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x43, 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, // 0xc0
+    0x44, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x78, 0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0x54, 0x73, // 0xd0
+    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x63, 0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69, // 0xe0
+    0x64, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x2f, 0x6f, 0x75, 0x75, 0x75, 0x75, 0x79, 0x74, 0x79, // 0xf0
+};
+
+static const unsigned char utf8_cyrillic_to_ascii[] = {
+    0x49, 0x49, 0x44, 0x47, 0x49, 0x44, 0x49, 0x59, 0x4a, 0x4c, 0x4e, 0x54, 0x4b, 0x49, 0x55, 0x44, // 0x400
+    0x41, 0x42, 0x56, 0x47, 0x44, 0x45, 0x5a, 0x5a, 0x49, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, // 0x410
+    0x52, 0x53, 0x54, 0x55, 0x46, 0x4b, 0x54, 0x43, 0x53, 0x53, 0x27, 0x59, 0x27, 0x45, 0x49, 0x49, // 0x420
+    0x61, 0x62, 0x76, 0x67, 0x64, 0x65, 0x7a, 0x7a, 0x69, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, // 0x430
+    0x72, 0x73, 0x74, 0x75, 0x66, 0x6b, 0x74, 0x63, 0x73, 0x73, 0x27, 0x79, 0x27, 0x65, 0x69, 0x69, // 0x440
+    0x69, 0x69, 0x64, 0x67, 0x69, 0x64, 0x69, 0x79, 0x6a, 0x6c, 0x6e, 0x74, 0x6b, 0x69, 0x75, 0x64, // 0x450
+};
+
+static const unsigned char utf8_cyrillic_to_cp866[] = {
+    0x00, 0xf0, 0x00, 0x00, 0xf2, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x00, // 0x400
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // 0x410
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // 0x420
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, // 0x430
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, // 0x440
+    0x00, 0xf1, 0x00, 0x00, 0xf3, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x00, // 0x450
+};
+
+// default to codepage 437
+int FontCodePage = 437;
+
 /**
 ** Convert a UTF8 char to an ASCII-extended char, based on the
 ** convert table to work with Stratagus char. This maps UTF8
-** chars into codepages commonly used for DOS games; currently
-** CP437 and CP866. More can be added. The games need to ensure
-** that the font graphic represents the appropriate codepage.
+** chars into codepages commonly used for DOS and early Windows
+** games; currently CP437, CP1251, and CP866. More can be added.
+** The games need to ensure that the font graphic represents the
+** appropriate codepage.
 **
 ** @param utf8 the char to convert
 ** @return the codepage equivalent, or '?' if char is unsuported.
@@ -226,21 +255,38 @@ static int utf8_to_codepage(int utf8)
 			break;
 		default:
 			utf8 -= 0xa0;
-			if (utf8 >= 0 && utf8 < sizeof(cp437)) {
-				// western european, index into cp437
-				cpChar = cp437[utf8];
+			if (utf8 >= 0 && utf8 < sizeof(utf8_europe_to_cp437)) {
+				// western european
+				switch (FontCodePage) {
+					case 437:
+						cpChar = utf8_europe_to_cp437[utf8];
+						break;
+					case 1252:
+						cpChar = utf8_europe_to_cp1252[utf8];
+						break;
+					default:
+						cpChar = utf8_europe_to_ascii[utf8];
+						break;
+				}
 			} else {
 				utf8 -= (0x400 - 0xa0);
-				if (utf8 >= 0 && utf8 < sizeof(cp866)) {
-					// cyrillic, index into cp866
-					cpChar = cp866[utf8];
+				if (utf8 >= 0 && utf8 < sizeof(utf8_cyrillic_to_cp866)) {
+					// cyrillic
+					switch (FontCodePage) {
+						case 866:
+							cpChar = utf8_cyrillic_to_cp866[utf8];
+							break;
+						default:
+							cpChar = utf8_cyrillic_to_ascii[utf8];
+							break;
+					}
 				}
 			}
 			break;
 	}
 
 	if (cpChar < 0x32) {
-		fprintf(stderr, "Can't convert UTF8 char to codepage: '%c' d=%d (0x%04x)\r\n", utf8, utf8, utf8);
+		fprintf(stderr, "Can't convert UTF8 char to codepage %d: '%c' d=%d (0x%04x)\r\n", FontCodePage, utf8, utf8, utf8);
 		cpChar = '?';
 	}
 	return cpChar;