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;