[Scummvm-git-logs] scummvm master -> c34807897ee43161db412b2413d6362799658c0f
dreammaster
dreammaster at scummvm.org
Sat Jul 10 21:35:40 UTC 2021
This automated email contains information about 20 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
f1711431f6 AGS: Unicode-aware line splitting and right-to-left text mode
e3a9f13ae0 AGS: Added needed Allegro unicode methods
656659d488 AGS: Unicode-aware script String API implementation
73cd6b9215 AGS: Structure for passing events UTF8 text around along with the keycode
4cb95e324e AGS: TextBox control supports unicode
5dec9be2e9 AGS: Skeleton for converting UTF-8 translation keys to ASCII
adc208ca4f AGS: text parser's F3 command works with unicode
188951a499 AGS: Supports reading scripts as separate assets
02883d6d89 AGS: Supports reading room scripts as separate assets
ba10eb247e AGS: print original filename for import errors if possible
7a61c3838a AGS: Added GetScriptAPIName() and use it instead of an array
4271f74caf AGS: Added API level for v3.6.0
1b0cbbaafc AGS: implemented System.Log()
8fc03ec83b AGS: Fixed DebugManager::RegisterGroup
04d6a41c42 AGS: Simplified System.Log implementation
a8f4d9dc5b AGS: Safety check for asset library v20, for asset name over limit
7d01f1dc1f AGS: in asset library files treat offsets & sizes as unsigned values
17b7626d11 AGS: don't mark viewport & camera as "changed" when not necessary
4c5d1335a7 AGS: fixed software mode's dirty rects in the legacy letterbox mode
c34807897e AGS: separate dirty rects function for the engine overlay sprites
Commit: f1711431f6452ead3bbeade0bd24c8eb7bad47f9
https://github.com/scummvm/scummvm/commit/f1711431f6452ead3bbeade0bd24c8eb7bad47f9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:35-07:00
Commit Message:
AGS: Unicode-aware line splitting and right-to-left text mode
>From upstream 00fee7e61e5746fe3c8722028f4f497b1949559e
Changed paths:
engines/ags/engine/ac/string.cpp
engines/ags/globals.h
engines/ags/lib/allegro/unicode.cpp
engines/ags/lib/allegro/unicode.h
engines/ags/shared/font/fonts.cpp
engines/ags/shared/util/string.cpp
engines/ags/shared/util/string.h
diff --git a/engines/ags/engine/ac/string.cpp b/engines/ags/engine/ac/string.cpp
index fc2ce987a8..a820b80bb7 100644
--- a/engines/ags/engine/ac/string.cpp
+++ b/engines/ags/engine/ac/string.cpp
@@ -252,7 +252,10 @@ size_t break_up_text_into_lines(const char *todis, SplitLines &lines, int wii, i
// write it as normal
if (_GP(game).options[OPT_RIGHTLEFTWRITE])
for (size_t rr = 0; rr < lines.Count(); rr++) {
- lines[rr].Reverse();
+ if (get_uformat() == U_UTF8)
+ lines[rr].ReverseUTF8();
+ else
+ lines[rr].Reverse();
line_length = wgettextwidth_compensate(lines[rr].GetCStr(), fonnt);
if (line_length > _G(longestline))
_G(longestline) = line_length;
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 281778b58e..0f9d6f6dc7 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -206,6 +206,8 @@ public:
int _trans_blend_green = 0;
int _trans_blend_blue = 0;
BlenderMode __blender_mode = kRgbToRgbBlender;
+ /* current format information and worker routines */
+ int _utype = U_UTF8;
/* default palette structures */
PALETTE _black_palette;
diff --git a/engines/ags/lib/allegro/unicode.cpp b/engines/ags/lib/allegro/unicode.cpp
index 7a31f26528..29508a1d75 100644
--- a/engines/ags/lib/allegro/unicode.cpp
+++ b/engines/ags/lib/allegro/unicode.cpp
@@ -20,13 +20,35 @@
*
*/
-#include "ags/lib/allegro/unicode.h"
#include "common/textconsole.h"
+#include "ags/lib/allegro/unicode.h"
+#include "ags/globals.h"
namespace AGS3 {
+/* ugetc: */
+int (*ugetc)(const char *s) = utf8_getc;
+/* ugetxc: */
+int (*ugetx)(char **s) = utf8_getx;
+/* ugetxc: */
+int (*ugetxc)(const char **s) = (int (*)(const char **)) utf8_getx;
+/* usetc: */
+int (*usetc)(char *s, int c) = utf8_setc;
+/* uwidth: */
+int (*uwidth)(const char *s) = utf8_width;
+/* ucwidth: */
+int (*ucwidth)(int c) = utf8_cwidth;
+/* uisok: */
+int (*uisok)(int c) = utf8_isok;
+
+
void set_uformat(int format) {
// TODO: implementation
+ _G(utype) = format;
+}
+
+int get_uformat(void) {
+ return _G(utype);
}
size_t ustrsize(const char *s) {
diff --git a/engines/ags/lib/allegro/unicode.h b/engines/ags/lib/allegro/unicode.h
index c87f8e0dc9..1dc1070985 100644
--- a/engines/ags/lib/allegro/unicode.h
+++ b/engines/ags/lib/allegro/unicode.h
@@ -33,7 +33,15 @@ namespace AGS3 {
#define U_UTF8 AL_ID('U','T','F','8')
#define U_CURRENT AL_ID('c','u','r','.')
+/* set_uformat:
+ * Selects a new text encoding format.
+ */
extern void set_uformat(int format);
+
+/* get_uformat:
+ * Returns the current text encoding format.
+ */
+extern int get_uformat();
extern size_t ustrsize(const char *s);
/* UTF-8 support functions
@@ -45,6 +53,21 @@ int utf8_width(const char *s);
int utf8_cwidth(int c);
int utf8_isok(int c);
+/* ugetc: */
+extern int (*ugetc)(const char *s);
+/* ugetxc: */
+extern int (*ugetx)(char **s);
+/* ugetxc: */
+extern int (*ugetxc)(const char **s);
+/* usetc: */
+extern int (*usetc)(char *s, int c);
+/* uwidth: */
+extern int (*uwidth)(const char *s);
+/* ucwidth: */
+extern int (*ucwidth)(int c);
+/* uisok: */
+extern int (*uisok)(int c);
+
} // namespace AGS3
#endif
diff --git a/engines/ags/shared/font/fonts.cpp b/engines/ags/shared/font/fonts.cpp
index 70fddd9f60..eec9f88bcd 100644
--- a/engines/ags/shared/font/fonts.cpp
+++ b/engines/ags/shared/font/fonts.cpp
@@ -50,8 +50,8 @@ Font::Font()
, Renderer2(nullptr) {
}
-} // Common
-} // AGS
+} // namespace Shared
+} // namespace AGS
FontInfo::FontInfo()
: Flags(0)
@@ -84,7 +84,7 @@ bool font_first_renderer_loaded() {
}
bool is_font_loaded(size_t fontNumber) {
- return fontNumber < _GP(fonts).size() && _GP(fonts)[fontNumber].Renderer != nullptr;
+ return fontNumber < _GP(fonts).size() && _GP(fonts)[fontNumber].Renderer != nullptr;;
}
IAGSFontRenderer *font_replace_renderer(size_t fontNumber, IAGSFontRenderer *renderer) {
@@ -170,6 +170,15 @@ bool use_default_linespacing(size_t fontNumber) {
return _GP(fonts)[fontNumber].Info.LineSpacing == 0;
}
+// Project-dependent implementation
+extern int wgettextwidth_compensate(const char *tex, int font);
+
+namespace AGS {
+namespace Common {
+SplitLines Lines;
+}
+}
+
// Replaces AGS-specific linebreak tags with common '\n'
void unescape_script_string(const char *cstr, std::vector<char> &out) {
out.clear();
@@ -179,6 +188,8 @@ void unescape_script_string(const char *cstr, std::vector<char> &out) {
cstr++;
}
// Replace all other occurrences as they're found
+ // NOTE: we do not need to decode utf8 here, because
+ // we are only searching for low-code ascii chars.
const char *off;
for (off = cstr; *off; ++off) {
if (*off != '[') continue;
@@ -202,76 +213,86 @@ size_t split_lines(const char *todis, SplitLines &lines, int wii, int fonnt, siz
// It's hard to tell how cruicial it is for the game looks, so research may be needed.
// TODO: IMHO this should rely not on game format, but script API level, because it
// defines necessary adjustments to game scripts. If you want to fix this, find a way to
- // pass this flag here all the way from _GP(game).options[OPT_BASESCRIPTAPI] (or game format).
+ // pass this flag here all the way from game.options[OPT_BASESCRIPTAPI] (or game format).
//
- // if (_GP(game).options[OPT_BASESCRIPTAPI] < $Your current version$)
+ // if (game.options[OPT_BASESCRIPTAPI] < $Your current version$)
wii -= 1;
lines.Reset();
unescape_script_string(todis, lines.LineBuf);
char *theline = &lines.LineBuf.front();
- size_t i = 0;
- size_t splitAt;
- char nextCharWas;
+ char *scan_ptr = theline;
+ char *prev_ptr = theline;
+ char *last_whitespace = nullptr;
while (1) {
- splitAt = (size_t)-1;
+ char *split_at = nullptr;
- if (theline[i] == 0) {
+ if (*scan_ptr == 0) {
// end of the text, add the last line if necessary
- if (i > 0) {
+ if (scan_ptr > theline) {
lines.Add(theline);
}
break;
}
- // temporarily terminate the line here and test its width
- nextCharWas = theline[i + 1];
- theline[i + 1] = 0;
+ if (*scan_ptr == ' ')
+ last_whitespace = scan_ptr;
// force end of line with the \n character
- if (theline[i] == '\n')
- splitAt = i;
- // otherwise, see if we are too wide
- else if (wgettextwidth_compensate(theline, fonnt) > wii) {
- int endline = i;
- while ((theline[endline] != ' ') && (endline > 0))
- endline--;
-
- // single very wide word, display as much as possible
- if (endline == 0)
- endline = i - 1;
-
- splitAt = endline;
- }
+ if (*scan_ptr == '\n') {
+ split_at = scan_ptr;
+ // otherwise, see if we are too wide
+ } else {
+ // temporarily terminate the line in the *next* char and test its width
+ char *next_ptr = scan_ptr;
+ ugetx(&next_ptr);
+ const int next_chwas = ugetc(next_ptr);
+ *next_ptr = 0;
+
+ if (wgettextwidth_compensate(theline, fonnt) > wii) {
+ // line is too wide, order the split
+ if (last_whitespace)
+ // revert to the last whitespace
+ split_at = last_whitespace;
+ else
+ // single very wide word, display as much as possible
+ split_at = prev_ptr;
+ }
- // restore the character that was there before
- theline[i + 1] = nextCharWas;
+ // restore the character that was there before
+ usetc(next_ptr, next_chwas);
+ }
- if (splitAt != (size_t)-1) {
- if (splitAt == 0 && !((theline[0] == ' ') || (theline[0] == '\n'))) {
+ if (split_at == nullptr) {
+ prev_ptr = scan_ptr;
+ ugetx(&scan_ptr);
+ } else {
+ // check if even one char cannot fit...
+ if (split_at == theline && !((*theline == ' ') || (*theline == '\n'))) {
// cannot split with current width restriction
lines.Reset();
break;
}
- // add this line
- nextCharWas = theline[splitAt];
- theline[splitAt] = 0;
+ // add this line; do the temporary terminator trick again
+ const int next_chwas = *split_at;
+ *split_at = 0;
lines.Add(theline);
- theline[splitAt] = nextCharWas;
+ usetc(split_at, next_chwas);
+ // check if too many lines
if (lines.Count() >= max_lines) {
lines[lines.Count() - 1].Append("...");
break;
}
- // the next line starts from here
- theline += splitAt;
+ // the next line starts from the split point
+ theline = split_at;
// skip the space or new line that caused the line break
- if ((theline[0] == ' ') || (theline[0] == '\n'))
+ if ((*theline == ' ') || (*theline == '\n'))
theline++;
- i = (size_t)-1;
+ scan_ptr = theline;
+ prev_ptr = theline;
+ last_whitespace = nullptr;
}
-
- i++;
}
return lines.Count();
}
diff --git a/engines/ags/shared/util/string.cpp b/engines/ags/shared/util/string.cpp
index f4866058ab..fd4c60e1e6 100644
--- a/engines/ags/shared/util/string.cpp
+++ b/engines/ags/shared/util/string.cpp
@@ -681,6 +681,30 @@ void String::Reverse() {
}
}
+void String::ReverseUTF8() {
+ if (_len <= 1)
+ return; // nothing to reverse if 1 char or less
+ // TODO: may this be optimized to not alloc new buffer? or dont care
+ char *newstr = new char[_len + 1];
+ for (char *fw = _cstr, *fw2 = _cstr + 1,
+ *bw = _cstr + _len - 1, *bw2 = _cstr + _len;
+ fw <= bw; // FIXME: <= catches odd middle char, optimize?
+ fw = fw2++, bw2 = bw--) {
+ // find end of next character forwards
+ for (; (fw2 < bw) && ((*fw2 & 0xC0) == 0x80); ++fw2);
+ // find beginning of the prev character backwards
+ for (; (bw > fw) && ((*bw & 0xC0) == 0x80); --bw);
+ // put these in opposite sides on the new buffer
+ char *fw_place = newstr + (_cstr + _len - bw2);
+ char *bw_place = newstr + _len - (fw2 - _cstr);
+ memcpy(fw_place, bw, bw2 - bw);
+ if (fw != bw) // FIXME, optimize?
+ memcpy(bw_place, fw, fw2 - fw);
+ }
+ newstr[_len] = 0;
+ SetString(newstr);
+}
+
void String::SetAt(size_t index, char c) {
if ((index < _len) && c) {
BecomeUnique();
diff --git a/engines/ags/shared/util/string.h b/engines/ags/shared/util/string.h
index e784e5d2e8..8d41d856c4 100644
--- a/engines/ags/shared/util/string.h
+++ b/engines/ags/shared/util/string.h
@@ -342,6 +342,10 @@ public:
}
// Reverses the string
void Reverse();
+ // Reverse the multibyte unicode string
+ // FIXME: name? invent some consistent naming for necessary multibyte funcs,
+ // proper utf8 support where necessary
+ void ReverseUTF8();
// Overwrite the Nth character of the string; does not change string's length
void SetAt(size_t index, char c);
// Makes a new string by copying up to N chars from C-string
Commit: e3a9f13ae0a056067095d401022560243d4f9dc0
https://github.com/scummvm/scummvm/commit/e3a9f13ae0a056067095d401022560243d4f9dc0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:36-07:00
Commit Message:
AGS: Added needed Allegro unicode methods
Changed paths:
engines/ags/lib/allegro/unicode.cpp
engines/ags/lib/allegro/unicode.h
diff --git a/engines/ags/lib/allegro/unicode.cpp b/engines/ags/lib/allegro/unicode.cpp
index 29508a1d75..36626c9cdd 100644
--- a/engines/ags/lib/allegro/unicode.cpp
+++ b/engines/ags/lib/allegro/unicode.cpp
@@ -55,10 +55,7 @@ size_t ustrsize(const char *s) {
return strlen(s);
}
-/* utf8_getc:
- * Reads a character from a UTF - 8 string.
- */
-/*static*/ int utf8_getc(const char *s) {
+int utf8_getc(const char *s) {
int c = *((const unsigned char *)(s++));
int n, t;
@@ -82,12 +79,7 @@ size_t ustrsize(const char *s) {
return c;
}
-
-
-/* utf8_getx:
- * Reads a character from a UTF-8 string, advancing the pointer position.
- */
-/*static*/ int utf8_getx(char **s) {
+int utf8_getx(char **s) {
int c = *((unsigned char *)((*s)++));
int n, t;
@@ -113,12 +105,7 @@ size_t ustrsize(const char *s) {
return c;
}
-
-
-/* utf8_setc:
- * Sets a character in a UTF-8 string.
- */
-/*static*/ int utf8_setc(char *s, int c) {
+int utf8_setc(char *s, int c) {
int size, bits, b, i;
if (c < 128) {
@@ -152,12 +139,7 @@ size_t ustrsize(const char *s) {
return size;
}
-
-
-/* utf8_width:
- * Returns the width of a UTF-8 character.
- */
-/*static*/ int utf8_width(const char *s) {
+int utf8_width(const char *s) {
int c = *((const unsigned char *)s);
int n = 1;
@@ -169,12 +151,7 @@ size_t ustrsize(const char *s) {
return n;
}
-
-
-/* utf8_cwidth:
- * Returns the width of a UTF-8 character.
- */
-/*static*/ int utf8_cwidth(int c) {
+int utf8_cwidth(int c) {
int size, bits, b;
if (c < 128)
@@ -195,11 +172,1044 @@ size_t ustrsize(const char *s) {
return size;
}
-/* utf8_isok:
- * Checks whether this character can be encoded in UTF-8 format.
- */
-/*static*/ int utf8_isok(int c) {
+int utf8_isok(int c) {
return true;
}
+int ustrlen(const char *s) {
+ int c = 0;
+ assert(s);
+
+ while (ugetxc(&s))
+ c++;
+
+ return c;
+}
+
+int utolower(int c) {
+ if ((c >= 65 && c <= 90) ||
+ (c >= 192 && c <= 214) ||
+ (c >= 216 && c <= 222) ||
+ (c >= 913 && c <= 929) ||
+ (c >= 931 && c <= 939) ||
+ (c >= 1040 && c <= 1071))
+ return c + 32;
+ if ((c >= 393 && c <= 394))
+ return c + 205;
+ if ((c >= 433 && c <= 434))
+ return c + 217;
+ if ((c >= 904 && c <= 906))
+ return c + 37;
+ if ((c >= 910 && c <= 911))
+ return c + 63;
+ if ((c >= 1025 && c <= 1036) ||
+ (c >= 1038 && c <= 1039))
+ return c + 80;
+ if ((c >= 1329 && c <= 1366) ||
+ (c >= 4256 && c <= 4293))
+ return c + 48;
+ if ((c >= 7944 && c <= 7951) ||
+ (c >= 7960 && c <= 7965) ||
+ (c >= 7976 && c <= 7983) ||
+ (c >= 7992 && c <= 7999) ||
+ (c >= 8008 && c <= 8013) ||
+ (c >= 8040 && c <= 8047) ||
+ (c >= 8072 && c <= 8079) ||
+ (c >= 8088 && c <= 8095) ||
+ (c >= 8104 && c <= 8111) ||
+ (c >= 8120 && c <= 8121) ||
+ (c >= 8152 && c <= 8153) ||
+ (c >= 8168 && c <= 8169))
+ return c + -8;
+ if ((c >= 8122 && c <= 8123))
+ return c + -74;
+ if ((c >= 8136 && c <= 8139))
+ return c + -86;
+ if ((c >= 8154 && c <= 8155))
+ return c + -100;
+ if ((c >= 8170 && c <= 8171))
+ return c + -112;
+ if ((c >= 8184 && c <= 8185))
+ return c + -128;
+ if ((c >= 8186 && c <= 8187))
+ return c + -126;
+ if ((c >= 8544 && c <= 8559))
+ return c + 16;
+ if ((c >= 9398 && c <= 9423))
+ return c + 26;
+
+ switch (c) {
+ case 256:
+ case 258:
+ case 260:
+ case 262:
+ case 264:
+ case 266:
+ case 268:
+ case 270:
+ case 272:
+ case 274:
+ case 276:
+ case 278:
+ case 280:
+ case 282:
+ case 284:
+ case 286:
+ case 288:
+ case 290:
+ case 292:
+ case 294:
+ case 296:
+ case 298:
+ case 300:
+ case 302:
+ case 306:
+ case 308:
+ case 310:
+ case 313:
+ case 315:
+ case 317:
+ case 319:
+ case 321:
+ case 323:
+ case 325:
+ case 327:
+ case 330:
+ case 332:
+ case 334:
+ case 336:
+ case 338:
+ case 340:
+ case 342:
+ case 344:
+ case 346:
+ case 348:
+ case 350:
+ case 352:
+ case 354:
+ case 356:
+ case 358:
+ case 360:
+ case 362:
+ case 364:
+ case 366:
+ case 368:
+ case 370:
+ case 372:
+ case 374:
+ case 377:
+ case 379:
+ case 381:
+ case 386:
+ case 388:
+ case 391:
+ case 395:
+ case 401:
+ case 408:
+ case 416:
+ case 418:
+ case 420:
+ case 423:
+ case 428:
+ case 431:
+ case 435:
+ case 437:
+ case 440:
+ case 444:
+ case 453:
+ case 456:
+ case 459:
+ case 461:
+ case 463:
+ case 465:
+ case 467:
+ case 469:
+ case 471:
+ case 473:
+ case 475:
+ case 478:
+ case 480:
+ case 482:
+ case 484:
+ case 486:
+ case 488:
+ case 490:
+ case 492:
+ case 494:
+ case 498:
+ case 500:
+ case 506:
+ case 508:
+ case 510:
+ case 512:
+ case 514:
+ case 516:
+ case 518:
+ case 520:
+ case 522:
+ case 524:
+ case 526:
+ case 528:
+ case 530:
+ case 532:
+ case 534:
+ case 994:
+ case 996:
+ case 998:
+ case 1000:
+ case 1002:
+ case 1004:
+ case 1006:
+ case 1120:
+ case 1122:
+ case 1124:
+ case 1126:
+ case 1128:
+ case 1130:
+ case 1132:
+ case 1134:
+ case 1136:
+ case 1138:
+ case 1140:
+ case 1142:
+ case 1144:
+ case 1146:
+ case 1148:
+ case 1150:
+ case 1152:
+ case 1168:
+ case 1170:
+ case 1172:
+ case 1174:
+ case 1176:
+ case 1178:
+ case 1180:
+ case 1182:
+ case 1184:
+ case 1186:
+ case 1188:
+ case 1190:
+ case 1192:
+ case 1194:
+ case 1196:
+ case 1198:
+ case 1200:
+ case 1202:
+ case 1204:
+ case 1206:
+ case 1208:
+ case 1210:
+ case 1212:
+ case 1214:
+ case 1217:
+ case 1219:
+ case 1223:
+ case 1227:
+ case 1232:
+ case 1234:
+ case 1236:
+ case 1238:
+ case 1240:
+ case 1242:
+ case 1244:
+ case 1246:
+ case 1248:
+ case 1250:
+ case 1252:
+ case 1254:
+ case 1256:
+ case 1258:
+ case 1262:
+ case 1264:
+ case 1266:
+ case 1268:
+ case 1272:
+ case 7680:
+ case 7682:
+ case 7684:
+ case 7686:
+ case 7688:
+ case 7690:
+ case 7692:
+ case 7694:
+ case 7696:
+ case 7698:
+ case 7700:
+ case 7702:
+ case 7704:
+ case 7706:
+ case 7708:
+ case 7710:
+ case 7712:
+ case 7714:
+ case 7716:
+ case 7718:
+ case 7720:
+ case 7722:
+ case 7724:
+ case 7726:
+ case 7728:
+ case 7730:
+ case 7732:
+ case 7734:
+ case 7736:
+ case 7738:
+ case 7740:
+ case 7742:
+ case 7744:
+ case 7746:
+ case 7748:
+ case 7750:
+ case 7752:
+ case 7754:
+ case 7756:
+ case 7758:
+ case 7760:
+ case 7762:
+ case 7764:
+ case 7766:
+ case 7768:
+ case 7770:
+ case 7772:
+ case 7774:
+ case 7776:
+ case 7778:
+ case 7780:
+ case 7782:
+ case 7784:
+ case 7786:
+ case 7788:
+ case 7790:
+ case 7792:
+ case 7794:
+ case 7796:
+ case 7798:
+ case 7800:
+ case 7802:
+ case 7804:
+ case 7806:
+ case 7808:
+ case 7810:
+ case 7812:
+ case 7814:
+ case 7816:
+ case 7818:
+ case 7820:
+ case 7822:
+ case 7824:
+ case 7826:
+ case 7828:
+ case 7840:
+ case 7842:
+ case 7844:
+ case 7846:
+ case 7848:
+ case 7850:
+ case 7852:
+ case 7854:
+ case 7856:
+ case 7858:
+ case 7860:
+ case 7862:
+ case 7864:
+ case 7866:
+ case 7868:
+ case 7870:
+ case 7872:
+ case 7874:
+ case 7876:
+ case 7878:
+ case 7880:
+ case 7882:
+ case 7884:
+ case 7886:
+ case 7888:
+ case 7890:
+ case 7892:
+ case 7894:
+ case 7896:
+ case 7898:
+ case 7900:
+ case 7902:
+ case 7904:
+ case 7906:
+ case 7908:
+ case 7910:
+ case 7912:
+ case 7914:
+ case 7916:
+ case 7918:
+ case 7920:
+ case 7922:
+ case 7924:
+ case 7926:
+ case 7928:
+ return c + 1;
+ case 304:
+ return c + -199;
+ case 376:
+ return c + -121;
+ case 385:
+ return c + 210;
+ case 390:
+ return c + 206;
+ case 398:
+ return c + 79;
+ case 399:
+ return c + 202;
+ case 400:
+ return c + 203;
+ case 403:
+ return c + 205;
+ case 404:
+ return c + 207;
+ case 406:
+ case 412:
+ return c + 211;
+ case 407:
+ return c + 209;
+ case 413:
+ return c + 213;
+ case 415:
+ return c + 214;
+ case 422:
+ case 425:
+ case 430:
+ return c + 218;
+ case 439:
+ return c + 219;
+ case 452:
+ case 455:
+ case 458:
+ case 497:
+ return c + 2;
+ case 902:
+ return c + 38;
+ case 908:
+ return c + 64;
+ case 8025:
+ case 8027:
+ case 8029:
+ case 8031:
+ return c + -8;
+ case 8124:
+ case 8140:
+ case 8188:
+ return c + -9;
+ case 8172:
+ return c + -7;
+ default:
+ return c;
+ }
+}
+
+int utoupper(int c) {
+ if ((c >= 97 && c <= 122) ||
+ (c >= 224 && c <= 246) ||
+ (c >= 248 && c <= 254) ||
+ (c >= 945 && c <= 961) ||
+ (c >= 963 && c <= 971) ||
+ (c >= 1072 && c <= 1103))
+ return c + -32;
+ if ((c >= 598 && c <= 599))
+ return c + -205;
+ if ((c >= 650 && c <= 651))
+ return c + -217;
+ if ((c >= 941 && c <= 943))
+ return c + -37;
+ if ((c >= 973 && c <= 974))
+ return c + -63;
+ if ((c >= 1105 && c <= 1116) ||
+ (c >= 1118 && c <= 1119))
+ return c + -80;
+ if ((c >= 1377 && c <= 1414))
+ return c + -48;
+ if ((c >= 7936 && c <= 7943) ||
+ (c >= 7952 && c <= 7957) ||
+ (c >= 7968 && c <= 7975) ||
+ (c >= 7984 && c <= 7991) ||
+ (c >= 8000 && c <= 8005) ||
+ (c >= 8032 && c <= 8039) ||
+ (c >= 8064 && c <= 8071) ||
+ (c >= 8080 && c <= 8087) ||
+ (c >= 8096 && c <= 8103) ||
+ (c >= 8112 && c <= 8113) ||
+ (c >= 8144 && c <= 8145) ||
+ (c >= 8160 && c <= 8161))
+ return c + 8;
+ if ((c >= 8048 && c <= 8049))
+ return c + 74;
+ if ((c >= 8050 && c <= 8053))
+ return c + 86;
+ if ((c >= 8054 && c <= 8055))
+ return c + 100;
+ if ((c >= 8056 && c <= 8057))
+ return c + 128;
+ if ((c >= 8058 && c <= 8059))
+ return c + 112;
+ if ((c >= 8060 && c <= 8061))
+ return c + 126;
+ if ((c >= 8560 && c <= 8575))
+ return c + -16;
+ if ((c >= 9424 && c <= 9449))
+ return c + -26;
+
+ switch (c) {
+ case 255:
+ return c + 121;
+ case 257:
+ case 259:
+ case 261:
+ case 263:
+ case 265:
+ case 267:
+ case 269:
+ case 271:
+ case 273:
+ case 275:
+ case 277:
+ case 279:
+ case 281:
+ case 283:
+ case 285:
+ case 287:
+ case 289:
+ case 291:
+ case 293:
+ case 295:
+ case 297:
+ case 299:
+ case 301:
+ case 303:
+ case 307:
+ case 309:
+ case 311:
+ case 314:
+ case 316:
+ case 318:
+ case 320:
+ case 322:
+ case 324:
+ case 326:
+ case 328:
+ case 331:
+ case 333:
+ case 335:
+ case 337:
+ case 339:
+ case 341:
+ case 343:
+ case 345:
+ case 347:
+ case 349:
+ case 351:
+ case 353:
+ case 355:
+ case 357:
+ case 359:
+ case 361:
+ case 363:
+ case 365:
+ case 367:
+ case 369:
+ case 371:
+ case 373:
+ case 375:
+ case 378:
+ case 380:
+ case 382:
+ case 387:
+ case 389:
+ case 392:
+ case 396:
+ case 402:
+ case 409:
+ case 417:
+ case 419:
+ case 421:
+ case 424:
+ case 429:
+ case 432:
+ case 436:
+ case 438:
+ case 441:
+ case 445:
+ case 453:
+ case 456:
+ case 459:
+ case 462:
+ case 464:
+ case 466:
+ case 468:
+ case 470:
+ case 472:
+ case 474:
+ case 476:
+ case 479:
+ case 481:
+ case 483:
+ case 485:
+ case 487:
+ case 489:
+ case 491:
+ case 493:
+ case 495:
+ case 498:
+ case 501:
+ case 507:
+ case 509:
+ case 511:
+ case 513:
+ case 515:
+ case 517:
+ case 519:
+ case 521:
+ case 523:
+ case 525:
+ case 527:
+ case 529:
+ case 531:
+ case 533:
+ case 535:
+ case 995:
+ case 997:
+ case 999:
+ case 1001:
+ case 1003:
+ case 1005:
+ case 1007:
+ case 1121:
+ case 1123:
+ case 1125:
+ case 1127:
+ case 1129:
+ case 1131:
+ case 1133:
+ case 1135:
+ case 1137:
+ case 1139:
+ case 1141:
+ case 1143:
+ case 1145:
+ case 1147:
+ case 1149:
+ case 1151:
+ case 1153:
+ case 1169:
+ case 1171:
+ case 1173:
+ case 1175:
+ case 1177:
+ case 1179:
+ case 1181:
+ case 1183:
+ case 1185:
+ case 1187:
+ case 1189:
+ case 1191:
+ case 1193:
+ case 1195:
+ case 1197:
+ case 1199:
+ case 1201:
+ case 1203:
+ case 1205:
+ case 1207:
+ case 1209:
+ case 1211:
+ case 1213:
+ case 1215:
+ case 1218:
+ case 1220:
+ case 1224:
+ case 1228:
+ case 1233:
+ case 1235:
+ case 1237:
+ case 1239:
+ case 1241:
+ case 1243:
+ case 1245:
+ case 1247:
+ case 1249:
+ case 1251:
+ case 1253:
+ case 1255:
+ case 1257:
+ case 1259:
+ case 1263:
+ case 1265:
+ case 1267:
+ case 1269:
+ case 1273:
+ case 7681:
+ case 7683:
+ case 7685:
+ case 7687:
+ case 7689:
+ case 7691:
+ case 7693:
+ case 7695:
+ case 7697:
+ case 7699:
+ case 7701:
+ case 7703:
+ case 7705:
+ case 7707:
+ case 7709:
+ case 7711:
+ case 7713:
+ case 7715:
+ case 7717:
+ case 7719:
+ case 7721:
+ case 7723:
+ case 7725:
+ case 7727:
+ case 7729:
+ case 7731:
+ case 7733:
+ case 7735:
+ case 7737:
+ case 7739:
+ case 7741:
+ case 7743:
+ case 7745:
+ case 7747:
+ case 7749:
+ case 7751:
+ case 7753:
+ case 7755:
+ case 7757:
+ case 7759:
+ case 7761:
+ case 7763:
+ case 7765:
+ case 7767:
+ case 7769:
+ case 7771:
+ case 7773:
+ case 7775:
+ case 7777:
+ case 7779:
+ case 7781:
+ case 7783:
+ case 7785:
+ case 7787:
+ case 7789:
+ case 7791:
+ case 7793:
+ case 7795:
+ case 7797:
+ case 7799:
+ case 7801:
+ case 7803:
+ case 7805:
+ case 7807:
+ case 7809:
+ case 7811:
+ case 7813:
+ case 7815:
+ case 7817:
+ case 7819:
+ case 7821:
+ case 7823:
+ case 7825:
+ case 7827:
+ case 7829:
+ case 7841:
+ case 7843:
+ case 7845:
+ case 7847:
+ case 7849:
+ case 7851:
+ case 7853:
+ case 7855:
+ case 7857:
+ case 7859:
+ case 7861:
+ case 7863:
+ case 7865:
+ case 7867:
+ case 7869:
+ case 7871:
+ case 7873:
+ case 7875:
+ case 7877:
+ case 7879:
+ case 7881:
+ case 7883:
+ case 7885:
+ case 7887:
+ case 7889:
+ case 7891:
+ case 7893:
+ case 7895:
+ case 7897:
+ case 7899:
+ case 7901:
+ case 7903:
+ case 7905:
+ case 7907:
+ case 7909:
+ case 7911:
+ case 7913:
+ case 7915:
+ case 7917:
+ case 7919:
+ case 7921:
+ case 7923:
+ case 7925:
+ case 7927:
+ case 7929:
+ return c + -1;
+ case 305:
+ return c + -232;
+ case 383:
+ return c + -300;
+ case 454:
+ case 457:
+ case 460:
+ case 499:
+ return c + -2;
+ case 477:
+ case 1010:
+ return c + -79;
+ case 595:
+ return c + -210;
+ case 596:
+ return c + -206;
+ case 601:
+ return c + -202;
+ case 603:
+ return c + -203;
+ case 608:
+ return c + -205;
+ case 611:
+ return c + -207;
+ case 616:
+ return c + -209;
+ case 617:
+ case 623:
+ return c + -211;
+ case 626:
+ return c + -213;
+ case 629:
+ return c + -214;
+ case 640:
+ case 643:
+ case 648:
+ return c + -218;
+ case 658:
+ return c + -219;
+ case 837:
+ return c + 84;
+ case 940:
+ return c + -38;
+ case 962:
+ return c + -31;
+ case 972:
+ return c + -64;
+ case 976:
+ return c + -62;
+ case 977:
+ return c + -57;
+ case 981:
+ return c + -47;
+ case 982:
+ return c + -54;
+ case 1008:
+ return c + -86;
+ case 1009:
+ return c + -80;
+ case 7835:
+ return c + -59;
+ case 8017:
+ case 8019:
+ case 8021:
+ case 8023:
+ return c + 8;
+ case 8115:
+ case 8131:
+ case 8179:
+ return c + 9;
+ case 8126:
+ return c + -7205;
+ case 8165:
+ return c + 7;
+ default:
+ return c;
+ }
+}
+
+int ustrcmp(const char *s1, const char *s2) {
+ int c1, c2;
+ assert(s1);
+ assert(s2);
+
+ for (;;) {
+ c1 = ugetxc(&s1);
+ c2 = ugetxc(&s2);
+
+ if (c1 != c2)
+ return c1 - c2;
+
+ if (!c1)
+ return 0;
+ }
+}
+
+int ustrncmp(const char *s1, const char *s2, int n) {
+ int c1, c2;
+ assert(s1);
+ assert(s2);
+
+ if (n <= 0)
+ return 0;
+
+ for (;;) {
+ c1 = ugetxc(&s1);
+ c2 = ugetxc(&s2);
+
+ if (c1 != c2)
+ return c1 - c2;
+
+ if ((!c1) || (--n <= 0))
+ return 0;
+ }
+}
+
+int ustricmp(const char *s1, const char *s2) {
+ int c1, c2;
+ assert(s1);
+ assert(s2);
+
+ for (;;) {
+ c1 = utolower(ugetxc(&s1));
+ c2 = utolower(ugetxc(&s2));
+
+ if (c1 != c2)
+ return c1 - c2;
+
+ if (!c1)
+ return 0;
+ }
+}
+
+int ustrnicmp(const char *s1, const char *s2, int n) {
+ int c1, c2;
+ assert(s1);
+ assert(s2);
+
+ if (n <= 0)
+ return 0;
+
+ for (;;) {
+ c1 = utolower(ugetxc(&s1));
+ c2 = utolower(ugetxc(&s2));
+
+ if (c1 != c2)
+ return c1 - c2;
+
+ if ((!c1) || (--n <= 0))
+ return 0;
+ }
+}
+
+int uoffset(const char *s, int index) {
+ const char *orig = s;
+ const char *last;
+ assert(s);
+
+ if (index < 0)
+ index += ustrlen(s);
+
+ while (index-- > 0) {
+ last = s;
+ if (!ugetxc(&s)) {
+ s = last;
+ break;
+ }
+ }
+
+ return (long)s - (long)orig;
+}
+
+char *ustrlwr(char *s) {
+ int pos = 0;
+ int c, lc;
+ assert(s);
+
+ while ((c = ugetc(s + pos)) != 0) {
+ lc = utolower(c);
+
+ if (lc != c)
+ usetat(s + pos, 0, lc);
+
+ pos += uwidth(s + pos);
+ }
+
+ return s;
+}
+
+char *ustrupr(char *s) {
+ int pos = 0;
+ int c, uc;
+ assert(s);
+
+ while ((c = ugetc(s + pos)) != 0) {
+ uc = utoupper(c);
+
+ if (uc != c)
+ usetat(s + pos, 0, uc);
+
+ pos += uwidth(s + pos);
+ }
+
+ return s;
+}
+
+char *ustrstr(const char *s1, const char *s2) {
+ int len;
+ assert(s1);
+ assert(s2);
+
+ len = ustrlen(s2);
+ while (ugetc(s1)) {
+ if (ustrncmp(s1, s2, len) == 0)
+ return (char *)s1;
+
+ s1 += uwidth(s1);
+ }
+
+ return NULL;
+}
+
+int usetat(char *s, int index, int c) {
+ int oldw, neww;
+ assert(s);
+
+ s += uoffset(s, index);
+
+ oldw = uwidth(s);
+ neww = ucwidth(c);
+
+ if (oldw != neww)
+ memmove(s + neww, s + oldw, ustrsizez(s + oldw));
+
+ usetc(s, c);
+
+ return neww - oldw;
+}
+
+int ustrsizez(const char *s) {
+ const char *orig = s;
+ assert(s);
+
+ do {
+ } while (ugetxc(&s) != 0);
+
+ return (long)s - (long)orig;
+}
+
} // namespace AGS3
diff --git a/engines/ags/lib/allegro/unicode.h b/engines/ags/lib/allegro/unicode.h
index 1dc1070985..5ea82c03f9 100644
--- a/engines/ags/lib/allegro/unicode.h
+++ b/engines/ags/lib/allegro/unicode.h
@@ -33,16 +33,6 @@ namespace AGS3 {
#define U_UTF8 AL_ID('U','T','F','8')
#define U_CURRENT AL_ID('c','u','r','.')
-/* set_uformat:
- * Selects a new text encoding format.
- */
-extern void set_uformat(int format);
-
-/* get_uformat:
- * Returns the current text encoding format.
- */
-extern int get_uformat();
-extern size_t ustrsize(const char *s);
/* UTF-8 support functions
*/
@@ -68,6 +58,73 @@ extern int (*ucwidth)(int c);
/* uisok: */
extern int (*uisok)(int c);
+/* set_uformat:
+ * Selects a new text encoding format.
+ */
+extern void set_uformat(int format);
+
+/* get_uformat:
+ * Returns the current text encoding format.
+ */
+extern int get_uformat();
+extern size_t ustrsize(const char *s);
+/* &nicode string length
+ */
+extern int ustrlen(const char *s);
+/* utolower:
+ * Unicode-aware version of the ANSI tolower() function.
+ */
+extern int utolower(int c);
+/* utoupper:
+ * Unicode-aware version of the ANSI toupper() function.
+ */
+extern int utoupper(int c);
+/* Unicode string compare
+ */
+extern int ustrcmp(const char *s1, const char *s2);
+ /* ustricmp:
+ * Unicode-aware version of the DJGPP stricmp() function.
+ */
+extern int ustricmp(const char *s1, const char *s2);
+/* ustrncmp:
+ * Unicode-aware version of the ANSI strncmp() function.
+ */
+extern int ustrncmp(const char *s1, const char *s2, int n);
+/* ustrnicmp:
+ * Unicode-aware version of the DJGPP strnicmp() function.
+ */
+extern int ustrnicmp(const char *s1, const char *s2, int n);
+/* uoffset:
+ * Returns the offset in bytes from the start of the string to the
+ * character at the specified index. If the index is negative, counts
+ * backward from the end of the string (-1 returns an offset to the
+ * last character).
+ */
+extern int uoffset(const char *s, int index);
+/* ustrlwr:
+ * Unicode-aware version of the ANSI strlwr() function.
+ */
+extern char *ustrlwr(char *s);
+/* ustrupr:
+ * Unicode-aware version of the ANSI strupr() function.
+ */
+extern char *ustrupr(char *s);
+/* ustrstr:
+ * Unicode-aware version of the ANSI strstr() function.
+ */
+extern char *ustrstr(const char *s1, const char *s2);
+/* usetat:
+ * Modifies the character at the specified index within the string,
+ * handling adjustments for variable width data. Returns how far the
+ * rest of the string was moved.
+ */
+int usetat(char *s, int index, int c);
+/* ustrsizez:
+ * Returns the size of the specified string in bytes, including the
+ * trailing zero.
+ */
+extern int ustrsizez(const char *s);
+
} // namespace AGS3
#endif
Commit: 656659d488a06a34d3756204ead6ed3c9d9f48de
https://github.com/scummvm/scummvm/commit/656659d488a06a34d3756204ead6ed3c9d9f48de
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:36-07:00
Commit Message:
AGS: Unicode-aware script String API implementation
>From upstream f658f6316dd562b28e8b206f3ef3cedebf3626c0
Changed paths:
engines/ags/engine/ac/string.cpp
diff --git a/engines/ags/engine/ac/string.cpp b/engines/ags/engine/ac/string.cpp
index a820b80bb7..b8f62a3d1b 100644
--- a/engines/ags/engine/ac/string.cpp
+++ b/engines/ags/engine/ac/string.cpp
@@ -20,7 +20,7 @@
*
*/
-//include <cstdio>
+#include "ags/lib/std/algorithm.h"
#include "ags/engine/ac/string.h"
#include "ags/shared/ac/common.h"
#include "ags/engine/ac/display.h"
@@ -66,38 +66,51 @@ const char *String_AppendChar(const char *thisString, char extraOne) {
}
const char *String_ReplaceCharAt(const char *thisString, int index, char newChar) {
- if ((index < 0) || (index >= (int)strlen(thisString)))
+ size_t len = ustrlen(thisString);
+ if ((index < 0) || ((size_t)index >= len))
quit("!String.ReplaceCharAt: index outside range of string");
- char *buffer = (char *)malloc(strlen(thisString) + 1);
- strcpy(buffer, thisString);
- buffer[index] = newChar;
+ size_t off = uoffset(thisString, index);
+ 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;
+ 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);
return CreateNewScriptString(buffer, false);
}
const char *String_Truncate(const char *thisString, int length) {
if (length < 0)
quit("!String.Truncate: invalid length");
-
- if (length >= (int)strlen(thisString)) {
+ size_t strlen = ustrlen(thisString);
+ if ((size_t)length >= strlen)
return thisString;
- }
- char *buffer = (char *)malloc(length + 1);
- strncpy(buffer, thisString, length);
- buffer[length] = 0;
+ size_t sz = uoffset(thisString, length);
+ char *buffer = (char *)malloc(sz + 1);
+ memcpy(buffer, thisString, sz);
+ buffer[sz] = 0;
return CreateNewScriptString(buffer, false);
}
const char *String_Substring(const char *thisString, int index, int length) {
if (length < 0)
quit("!String.Substring: invalid length");
- if ((index < 0) || (index > (int)strlen(thisString)))
+ size_t strlen = ustrlen(thisString);
+ if ((index < 0) || ((size_t)index > strlen))
quit("!String.Substring: invalid index");
-
- char *buffer = (char *)malloc(length + 1);
- strncpy(buffer, &thisString[index], length);
- buffer[length] = 0;
+ size_t sublen = std::min((size_t)length, strlen - index);
+ size_t start = uoffset(thisString, index);
+ size_t end = uoffset(thisString + start, sublen) + start;
+ size_t copysz = end - start;
+
+ char *buffer = (char *)malloc(copysz + 1);
+ memcpy(buffer, thisString + start, copysz);
+ buffer[copysz] = 0;
return CreateNewScriptString(buffer, false);
}
@@ -106,7 +119,7 @@ int String_CompareTo(const char *thisString, const char *otherString, bool caseS
if (caseSensitive) {
return strcmp(thisString, otherString);
} else {
- return ags_stricmp(thisString, otherString);
+ return ustricmp(thisString, otherString);
}
}
@@ -115,63 +128,69 @@ int String_StartsWith(const char *thisString, const char *checkForString, bool c
if (caseSensitive) {
return (strncmp(thisString, checkForString, strlen(checkForString)) == 0) ? 1 : 0;
} else {
- return (ags_strnicmp(thisString, checkForString, strlen(checkForString)) == 0) ? 1 : 0;
+ return (ustrnicmp(thisString, checkForString, ustrlen(checkForString)) == 0) ? 1 : 0;
}
}
int String_EndsWith(const char *thisString, const char *checkForString, bool caseSensitive) {
-
- int checkAtOffset = strlen(thisString) - strlen(checkForString);
-
- if (checkAtOffset < 0) {
+ // NOTE: we need size in bytes here
+ size_t thislen = strlen(thisString), checklen = strlen(checkForString);
+ if (checklen > thislen)
return 0;
- }
if (caseSensitive) {
- return (strcmp(&thisString[checkAtOffset], checkForString) == 0) ? 1 : 0;
+ return (strcmp(thisString + (thislen - checklen), checkForString) == 0) ? 1 : 0;
} else {
- return (ags_stricmp(&thisString[checkAtOffset], checkForString) == 0) ? 1 : 0;
+ return (ustricmp(thisString + (thislen - checklen), checkForString) == 0) ? 1 : 0;
}
}
const char *String_Replace(const char *thisString, const char *lookForText, const char *replaceWithText, bool caseSensitive) {
char resultBuffer[STD_BUFFER_SIZE] = "";
- int thisStringLen = (int)strlen(thisString);
- int outputSize = 0;
- for (int i = 0; i < thisStringLen; i++) {
- bool matchHere = false;
- if (caseSensitive) {
- matchHere = (strncmp(&thisString[i], lookForText, strlen(lookForText)) == 0);
- } else {
- matchHere = (ags_strnicmp(&thisString[i], lookForText, strlen(lookForText)) == 0);
+ size_t outputSize = 0; // length in bytes
+ if (caseSensitive) {
+ size_t lookForLen = strlen(lookForText);
+ size_t replaceLen = strlen(replaceWithText);
+ for (const char *ptr = thisString; *ptr; ++ptr) {
+ if (strncmp(ptr, lookForText, lookForLen) == 0) {
+ memcpy(&resultBuffer[outputSize], replaceWithText, replaceLen);
+ outputSize += replaceLen;
+ ptr += lookForLen - 1;
+ } else {
+ resultBuffer[outputSize] = *ptr;
+ outputSize++;
+ }
}
-
- if (matchHere) {
- strcpy(&resultBuffer[outputSize], replaceWithText);
- outputSize += strlen(replaceWithText);
- i += strlen(lookForText) - 1;
- } else {
- resultBuffer[outputSize] = thisString[i];
- outputSize++;
+ } else {
+ size_t lookForLen = ustrlen(lookForText);
+ size_t lookForSz = strlen(lookForText); // length in bytes
+ size_t replaceSz = strlen(replaceWithText); // length in bytes
+ const char *p_cur = thisString;
+ for (int c = ugetxc(&thisString); *p_cur; p_cur = thisString, c = ugetxc(&thisString)) {
+ if (ustrnicmp(p_cur, lookForText, lookForLen) == 0) {
+ memcpy(&resultBuffer[outputSize], replaceWithText, replaceSz);
+ outputSize += replaceSz;
+ thisString = p_cur + lookForSz;
+ } else {
+ usetc(&resultBuffer[outputSize], c);
+ outputSize += ucwidth(c);
+ }
}
}
- resultBuffer[outputSize] = 0;
-
+ resultBuffer[outputSize] = 0; // terminate
return CreateNewScriptString(resultBuffer, true);
}
const char *String_LowerCase(const char *thisString) {
- char *buffer = (char *)malloc(strlen(thisString) + 1);
- strcpy(buffer, thisString);
- ags_strlwr(buffer);
+ char *buffer = ags_strdup(thisString);
+ ustrlwr(buffer);
return CreateNewScriptString(buffer, false);
}
const char *String_UpperCase(const char *thisString) {
- char *buffer = (char *)malloc(strlen(thisString) + 1);
- strcpy(buffer, thisString);
- ags_strupr(buffer);
+ char *buffer = ags_strdup(thisString);
+ ustrupr(buffer);
return CreateNewScriptString(buffer, false);
}
@@ -188,36 +207,47 @@ int StringToInt(const char *stino) {
int StrContains(const char *s1, const char *s2) {
VALIDATE_STRING(s1);
VALIDATE_STRING(s2);
- char *tempbuf1 = (char *)malloc(strlen(s1) + 1);
- char *tempbuf2 = (char *)malloc(strlen(s2) + 1);
- strcpy(tempbuf1, s1);
- strcpy(tempbuf2, s2);
- ags_strlwr(tempbuf1);
- ags_strlwr(tempbuf2);
-
- char *offs = strstr(tempbuf1, tempbuf2);
- free(tempbuf1);
- free(tempbuf2);
+ char *tempbuf1 = ags_strdup(s1);
+ char *tempbuf2 = ags_strdup(s2);
+ ustrlwr(tempbuf1);
+ ustrlwr(tempbuf2);
- if (offs == nullptr)
+ char *offs = ustrstr(tempbuf1, tempbuf2);
+
+ if (offs == nullptr) {
+ free(tempbuf1);
+ free(tempbuf2);
return -1;
+ }
- return (offs - tempbuf1);
+ *offs = 0;
+ int at = ustrlen(tempbuf1);
+ free(tempbuf1);
+ free(tempbuf2);
+ return at;
}
//=============================================================================
+const char *CreateNewScriptString(const String &fromText) {
+ return (const char *)CreateNewScriptStringObj(fromText.GetCStr(), true).second;
+}
+
const char *CreateNewScriptString(const char *fromText, bool reAllocate) {
return (const char *)CreateNewScriptStringObj(fromText, reAllocate).second;
}
+DynObjectRef CreateNewScriptStringObj(const String &fromText) {
+ return CreateNewScriptStringObj(fromText.GetCStr(), true);
+}
+
DynObjectRef CreateNewScriptStringObj(const char *fromText, bool reAllocate) {
ScriptString *str;
if (reAllocate) {
str = new ScriptString(fromText);
} else {
str = new ScriptString();
- str->text = const_cast<char *>(fromText);
+ str->text = (char *)fromText;
}
void *obj_ptr = str->text;
int32_t handle = ccRegisterManagedObject(obj_ptr, str);
@@ -252,29 +282,27 @@ size_t break_up_text_into_lines(const char *todis, SplitLines &lines, int wii, i
// write it as normal
if (_GP(game).options[OPT_RIGHTLEFTWRITE])
for (size_t rr = 0; rr < lines.Count(); rr++) {
- if (get_uformat() == U_UTF8)
- lines[rr].ReverseUTF8();
- else
+ (get_uformat() == U_UTF8) ?
+ lines[rr].ReverseUTF8() :
lines[rr].Reverse();
line_length = wgettextwidth_compensate(lines[rr].GetCStr(), fonnt);
if (line_length > _G(longestline))
_G(longestline) = line_length;
- }
- else
- for (size_t rr = 0; rr < lines.Count(); rr++) {
- line_length = wgettextwidth_compensate(lines[rr].GetCStr(), fonnt);
- if (line_length > _G(longestline))
- _G(longestline) = line_length;
- }
- return lines.Count();
+ } else
+ for (size_t rr = 0; rr < lines.Count(); rr++) {
+ line_length = wgettextwidth_compensate(lines[rr].GetCStr(), fonnt);
+ if (line_length > _G(longestline))
+ _G(longestline) = line_length;
+ }
+ return lines.Count();
}
int MAXSTRLEN = MAX_MAXSTRLEN;
void check_strlen(char *ptt) {
MAXSTRLEN = MAX_MAXSTRLEN;
- uintptr charstart = (uintptr)&_GP(game).chars[0];
- uintptr charend = charstart + sizeof(CharacterInfo) * _GP(game).numcharacters;
- if (((uintptr)&ptt[0] >= charstart) && ((uintptr)&ptt[0] <= charend))
+ long charstart = (long)&_GP(game).chars[0];
+ long charend = charstart + sizeof(CharacterInfo) * _GP(game).numcharacters;
+ if (((long)&ptt[0] >= charstart) && ((long)&ptt[0] <= charend))
MAXSTRLEN = 30;
}
Commit: 73cd6b9215c3b1199ce4060da7893c257b1491cf
https://github.com/scummvm/scummvm/commit/73cd6b9215c3b1199ce4060da7893c257b1491cf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:36-07:00
Commit Message:
AGS: Structure for passing events UTF8 text around along with the keycode
>From upstream 9d65a68c61d0503e05e2cbe80447af0d619161e4
Note that ScummVM doesn't have a TEXT_INPUT event, so this change and following
ones will be mostly to keep in sync with the standalone codebase even if they're
not directly used
Changed paths:
engines/ags/engine/ac/dialog.cpp
engines/ags/engine/ac/display.cpp
engines/ags/engine/ac/inv_window.cpp
engines/ags/engine/ac/sys_events.cpp
engines/ags/engine/ac/sys_events.h
engines/ags/engine/gui/csci_dialog.cpp
engines/ags/engine/main/game_run.cpp
engines/ags/engine/main/game_run.h
engines/ags/engine/media/video/video.cpp
engines/ags/events.cpp
engines/ags/events.h
engines/ags/plugins/ags_plugin.cpp
engines/ags/shared/ac/keycode.h
engines/ags/shared/gui/gui_object.h
engines/ags/shared/gui/gui_textbox.cpp
engines/ags/shared/gui/gui_textbox.h
diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index c3b5a4b3c6..d612c56560 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -816,21 +816,26 @@ bool DialogOptions::Run() {
run_function_on_non_blocking_thread(&_GP(runDialogOptionRepExecFunc));
}
- int gkey;
- if (run_service_key_controls(gkey) && !_GP(play).IsIgnoringInput()) {
+ KeyInput ki;
+ if (run_service_key_controls(ki) && !_GP(play).IsIgnoringInput()) {
+ eAGSKeyCode gkey = ki.Key;
if (parserInput) {
wantRefresh = true;
- // type into the parser
+ // type into the parser
+ // TODO: find out what are these key commands, and are these documented?
if ((gkey == eAGSKeyCodeF3) || ((gkey == eAGSKeyCodeSpace) && (parserInput->Text.GetLength() == 0))) {
// write previous contents into textbox (F3 or Space when box is empty)
- for (unsigned int i = parserInput->Text.GetLength(); i < strlen(_GP(play).lastParserEntry); i++) {
- parserInput->OnKeyPress(_GP(play).lastParserEntry[i]);
+ size_t last_len = strlen(_GP(play).lastParserEntry);
+
+ for (unsigned int i = parserInput->Text.GetLength(); i < last_len; i++) {
+ ki.Key = (eAGSKeyCode)_GP(play).lastParserEntry[i];
+ parserInput->OnKeyPress(ki);
}
//ags_domouse(DOMOUSE_DISABLE);
Redraw();
return true; // continue running loop
} else if ((gkey >= eAGSKeyCodeSpace) || (gkey == eAGSKeyCodeReturn) || (gkey == eAGSKeyCodeBackspace)) {
- parserInput->OnKeyPress(gkey);
+ parserInput->OnKeyPress(ki);
if (!parserInput->IsActivated) {
//ags_domouse(DOMOUSE_DISABLE);
Redraw();
@@ -844,10 +849,10 @@ bool DialogOptions::Run() {
}
// Allow selection of options by keyboard shortcuts
else if (_GP(game).options[OPT_DIALOGNUMBERED] >= kDlgOptKeysOnly &&
- gkey >= '1' && gkey <= '9') {
- gkey -= '1';
- if (gkey < numdisp) {
- chose = disporder[gkey];
+ gkey >= '1' && gkey <= '9') {
+ int numkey = gkey - '1';
+ if (numkey < numdisp) {
+ chose = disporder[numkey];
return false; // end dialog options running loop
}
}
@@ -857,8 +862,8 @@ bool DialogOptions::Run() {
if (new_custom_render); // do not automatically detect option under mouse
else if (usingCustomRendering) {
if ((_G(mousex) >= dirtyx) && (_G(mousey) >= dirtyy) &&
- (_G(mousex) < dirtyx + tempScrn->GetWidth()) &&
- (_G(mousey) < dirtyy + tempScrn->GetHeight())) {
+ (_G(mousex) < dirtyx + tempScrn->GetWidth()) &&
+ (_G(mousey) < dirtyy + tempScrn->GetHeight())) {
_GP(getDialogOptionUnderCursorFunc).params[0].SetDynamicObject(&_GP(ccDialogOptionsRendering), &_GP(ccDialogOptionsRendering));
run_function_on_non_blocking_thread(&_GP(getDialogOptionUnderCursorFunc));
@@ -870,24 +875,23 @@ bool DialogOptions::Run() {
_GP(ccDialogOptionsRendering).activeOptionID = -1;
}
} else if (_G(mousex) >= dialog_abs_x && _G(mousex) < (dialog_abs_x + areawid) &&
- _G(mousey) >= dlgyp && _G(mousey) < curyp) {
+ _G(mousey) >= dlgyp && _G(mousey) < curyp) {
mouseison = numdisp - 1;
for (int i = 0; i < numdisp; ++i) {
if (_G(mousey) < dispyp[i]) {
- mouseison = i - 1;
- break;
+ mouseison = i - 1; break;
}
}
if ((mouseison < 0) | (mouseison >= numdisp)) mouseison = -1;
}
if (parserInput != nullptr) {
- int relativeMousey = _G(mousey);
+ int relative_mousey = _G(mousey);
if (usingCustomRendering)
- relativeMousey -= dirtyy;
+ relative_mousey -= dirtyy;
- if ((relativeMousey > parserInput->Y) &&
- (relativeMousey < parserInput->Y + parserInput->Height))
+ if ((relative_mousey > parserInput->Y) &&
+ (relative_mousey < parserInput->Y + parserInput->Height))
mouseison = DLG_OPTION_PARSER;
if (parserInput->IsActivated)
@@ -897,7 +901,7 @@ bool DialogOptions::Run() {
int mouseButtonPressed = MouseNone;
int mouseWheelTurn = 0;
if (run_service_mb_controls(mouseButtonPressed, mouseWheelTurn) && mouseButtonPressed >= 0 &&
- !_GP(play).IsIgnoringInput()) {
+ !_GP(play).IsIgnoringInput()) {
if (mouseison < 0 && !new_custom_render) {
if (usingCustomRendering) {
_GP(runDialogOptionMouseClickHandlerFunc).params[0].SetDynamicObject(&_GP(ccDialogOptionsRendering), &_GP(ccDialogOptionsRendering));
@@ -970,8 +974,7 @@ bool DialogOptions::Run() {
update_polled_stuff_if_runtime();
- if (!runGameLoopsInBackground && (_GP(play).fast_forward == 0)) {
- // note if runGameLoopsInBackground then it's called inside UpdateGameOnce
+ if (!runGameLoopsInBackground && (_GP(play).fast_forward == 0)) { // note if runGameLoopsInBackground then it's called inside UpdateGameOnce
WaitForNextFrame();
}
diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 19928d4fcb..59671a472c 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -289,9 +289,9 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
if (skip_setting & SKIP_MOUSECLICK && !_GP(play).IsIgnoringInput())
break;
}
- int kp;
+ KeyInput kp;
if (run_service_key_controls(kp)) {
- check_skip_cutscene_keypress(kp);
+ check_skip_cutscene_keypress(kp.Key);
if (_GP(play).fast_forward)
break;
if ((skip_setting & SKIP_KEYPRESS) && !_GP(play).IsIgnoringInput())
diff --git a/engines/ags/engine/ac/inv_window.cpp b/engines/ags/engine/ac/inv_window.cpp
index 47282bd40b..8794f8ab49 100644
--- a/engines/ags/engine/ac/inv_window.cpp
+++ b/engines/ags/engine/ac/inv_window.cpp
@@ -347,7 +347,7 @@ bool InventoryScreen::Run() {
// Run() can be called in a loop, so keep events going.
sys_evt_process_pending();
- int kgn;
+ KeyInput kgn;
if (run_service_key_controls(kgn) && !_GP(play).IsIgnoringInput()) {
return false; // end inventory screen loop
}
diff --git a/engines/ags/engine/ac/sys_events.cpp b/engines/ags/engine/ac/sys_events.cpp
index 342067aff9..6e6347b68d 100644
--- a/engines/ags/engine/ac/sys_events.cpp
+++ b/engines/ags/engine/ac/sys_events.cpp
@@ -64,6 +64,14 @@ static void(*_on_switchout_callback)(void) = nullptr;
// KEYBOARD INPUT
// ----------------------------------------------------------------------------
+KeyInput ags_keycode_from_scummvm(const Common::Event &event) {
+ KeyInput ki;
+
+ ki.Key = ::AGS::g_events->scummvm_key_to_ags_key(event);
+
+ return ki;
+}
+
bool ags_keyevent_ready() {
return ::AGS::g_events->keyEventPending();
}
diff --git a/engines/ags/engine/ac/sys_events.h b/engines/ags/engine/ac/sys_events.h
index e11ae305ad..bd60202726 100644
--- a/engines/ags/engine/ac/sys_events.h
+++ b/engines/ags/engine/ac/sys_events.h
@@ -66,8 +66,7 @@ inline int make_merged_mod(int mod) {
return m_mod;
}
-extern eAGSKeyCode ags_keycode_from_ScummVM(const Common::Event &event);
-extern bool ags_key_to_scummvm_keycode(eAGSKeyCode key, Common::KeyCode(&scan)[3]);
+extern KeyInput ags_keycode_from_scummvm(const Common::Event &event);
// Tells if there are any buffered key events
extern bool ags_keyevent_ready();
diff --git a/engines/ags/engine/gui/csci_dialog.cpp b/engines/ags/engine/gui/csci_dialog.cpp
index 4d8b7dcf0a..055353b907 100644
--- a/engines/ags/engine/gui/csci_dialog.cpp
+++ b/engines/ags/engine/gui/csci_dialog.cpp
@@ -135,8 +135,9 @@ int CSCIWaitMessage(CSCIMessage *cscim) {
cscim->id = -1;
cscim->code = 0;
_G(smcode) = 0;
- int keywas;
- if (run_service_key_controls(keywas) && !_GP(play).IsIgnoringInput()) {
+ KeyInput ki;
+ if (run_service_key_controls(ki) && !_GP(play).IsIgnoringInput()) {
+ int keywas = ki.Key;
if (keywas == eAGSKeyCodeReturn) {
cscim->id = finddefaultcontrol(CNF_DEFAULT);
cscim->code = CM_COMMAND;
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index 0563de19f5..c0af5bd89b 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -245,7 +245,7 @@ int old_key_mod = 0; // for saving previous key mods
// Runs service key controls, returns false if service key combinations were handled
// and no more processing required, otherwise returns true and provides current keycode and key shifts.
-bool run_service_key_controls(int &out_key) {
+bool run_service_key_controls(KeyInput &out_key) {
bool handled = false;
const bool key_valid = ags_keyevent_ready();
const Common::Event key_evt = key_valid ? ags_get_next_keyevent() : Common::Event();
@@ -305,7 +305,8 @@ bool run_service_key_controls(int &out_key) {
return false; // rest of engine currently does not use pressed mod keys
// change this when it's no longer true (but be mindful about key-skipping!)
- int agskey = ::AGS::EventsManager::ags_keycode_from_scummvm(key_evt);
+ KeyInput ki = ags_keycode_from_scummvm(key_evt);
+ eAGSKeyCode agskey = ki.Key;
if (agskey == eAGSKeyCodeNone)
return false; // should skip this key event
@@ -390,7 +391,7 @@ bool run_service_key_controls(int &out_key) {
}
// No service operation triggered? return active keypress and mods to caller
- out_key = agskey;
+ out_key = ki;
return true;
}
@@ -408,10 +409,11 @@ bool run_service_mb_controls(int &mbut, int &mwheelz) {
// Runs default keyboard handling
static void check_keyboard_controls() {
// First check for service engine's combinations (mouse lock, display mode switch, and so forth)
- int kgn;
- if (!run_service_key_controls(kgn)) {
+ KeyInput ki;
+ if (!run_service_key_controls(ki)) {
return;
}
+ eAGSKeyCode kgn = ki.Key;
// Then, check cutscene skip
check_skip_cutscene_keypress(kgn);
if (_GP(play).fast_forward) {
@@ -487,7 +489,7 @@ static void check_keyboard_controls() {
keywasprocessed = 1;
- guitex->OnKeyPress(kgn);
+ guitex->OnKeyPress(ki);
if (guitex->IsActivated) {
guitex->IsActivated = false;
@@ -498,9 +500,9 @@ static void check_keyboard_controls() {
}
if (!keywasprocessed) {
- kgn = AGSKeyToScriptKey(kgn);
- debug_script_log("Running on_key_press keycode %d", kgn);
- setevent(EV_TEXTSCRIPT, TS_KEYPRESS, kgn);
+ int sckey = AGSKeyToScriptKey(kgn);
+ debug_script_log("Running on_key_press keycode %d", sckey);
+ setevent(EV_TEXTSCRIPT, TS_KEYPRESS, sckey);
}
// RunTextScriptIParam(_G(gameinst),"on_key_press",kgn);
diff --git a/engines/ags/engine/main/game_run.h b/engines/ags/engine/main/game_run.h
index 87527e173c..762492e2a5 100644
--- a/engines/ags/engine/main/game_run.h
+++ b/engines/ags/engine/main/game_run.h
@@ -51,7 +51,8 @@ void UpdateGameOnce(bool checkControls = false, IDriverDependantBitmap *extraBit
float get_current_fps();
// Runs service key controls, returns false if no key was pressed or key input was claimed by the engine,
// otherwise returns true and provides a keycode.
-bool run_service_key_controls(int &kgn);
+struct KeyInput;
+bool run_service_key_controls(KeyInput &kgn);
// Runs service mouse controls, returns false if mouse input was claimed by the engine,
// otherwise returns true and provides mouse button code.
bool run_service_mb_controls(int &mbut, int &mwheelz);
diff --git a/engines/ags/engine/media/video/video.cpp b/engines/ags/engine/media/video/video.cpp
index a17c18c0e4..bdf4096d9b 100644
--- a/engines/ags/engine/media/video/video.cpp
+++ b/engines/ags/engine/media/video/video.cpp
@@ -112,9 +112,10 @@ static bool play_video(Video::VideoDecoder *decoder, const char *name, int skip,
if (canAbort) {
// Check for whether user aborted video
- int key, mbut, mwheelz;
+ KeyInput key;
+ int mbut, mwheelz;
if (run_service_key_controls(key)) {
- if (key == 27 && canAbort)
+ if (key.Key == 27 && canAbort)
return true;
if (canAbort >= 2)
return true; // skip on any key
diff --git a/engines/ags/events.cpp b/engines/ags/events.cpp
index f6ec16326f..4928556ba0 100644
--- a/engines/ags/events.cpp
+++ b/engines/ags/events.cpp
@@ -311,7 +311,7 @@ bool EventsManager::ags_key_to_scancode(AGS3::eAGSKeyCode key, Common::KeyCode(&
return false;
}
-AGS3::eAGSKeyCode EventsManager::ags_keycode_from_scummvm(const Common::Event &event) {
+AGS3::eAGSKeyCode EventsManager::scummvm_key_to_ags_key(const Common::Event &event) {
if (event.type != Common::EVENT_KEYDOWN)
return AGS3::eAGSKeyCodeNone;
diff --git a/engines/ags/events.h b/engines/ags/events.h
index 5a69c66896..46db89ed85 100644
--- a/engines/ags/events.h
+++ b/engines/ags/events.h
@@ -53,7 +53,7 @@ public:
/*
* Converts a ScummVM event to the ags keycode
*/
- static AGS3::eAGSKeyCode ags_keycode_from_scummvm(const Common::Event &event);
+ static AGS3::eAGSKeyCode scummvm_key_to_ags_key(const Common::Event &event);
public:
EventsManager();
diff --git a/engines/ags/plugins/ags_plugin.cpp b/engines/ags/plugins/ags_plugin.cpp
index d10e472bbc..818a4308b9 100644
--- a/engines/ags/plugins/ags_plugin.cpp
+++ b/engines/ags/plugins/ags_plugin.cpp
@@ -369,18 +369,17 @@ void IAGSEngine::BlitSpriteRotated(int32 x, int32 y, BITMAP *bmp, int32 angle) {
extern void domouse(int);
void IAGSEngine::PollSystem() {
-
domouse(DOMOUSE_NOCURSOR);
update_polled_stuff_if_runtime();
int mbut, mwheelz;
if (run_service_mb_controls(mbut, mwheelz) && mbut >= 0 && !_GP(play).IsIgnoringInput())
pl_run_plugin_hooks(AGSE_MOUSECLICK, mbut);
- int kp;
+ KeyInput kp;
if (run_service_key_controls(kp) && !_GP(play).IsIgnoringInput()) {
- pl_run_plugin_hooks(AGSE_KEYPRESS, kp);
+ pl_run_plugin_hooks(AGSE_KEYPRESS, kp.Key);
}
-
}
+
AGSCharacter *IAGSEngine::GetCharacter(int32 charnum) {
if (charnum >= _GP(game).numcharacters)
quit("!AGSEngine::GetCharacter: invalid character request");
diff --git a/engines/ags/shared/ac/keycode.h b/engines/ags/shared/ac/keycode.h
index 9903749d48..b541aad747 100644
--- a/engines/ags/shared/ac/keycode.h
+++ b/engines/ags/shared/ac/keycode.h
@@ -24,6 +24,7 @@
#define AGS_SHARED_AC_KEYCODE_H
#include "ags/shared/core/platform.h"
+#include "ags/shared/core/types.h"
namespace AGS3 {
@@ -251,6 +252,15 @@ enum eAGSKeyCode {
*/
};
+// Combined key code and a textual representation in UTF-8
+struct KeyInput {
+ const static size_t UTF8_ARR_SIZE = 5;
+
+ eAGSKeyCode Key = eAGSKeyCodeNone;
+ char Text[UTF8_ARR_SIZE] = { 0 };
+
+ KeyInput() = default;
+};
// Converts eAGSKeyCode to script API code, for "on_key_press" and similar callbacks
int AGSKeyToScriptKey(int keycode);
diff --git a/engines/ags/shared/gui/gui_object.h b/engines/ags/shared/gui/gui_object.h
index 083bd566b0..db40ccb08c 100644
--- a/engines/ags/shared/gui/gui_object.h
+++ b/engines/ags/shared/gui/gui_object.h
@@ -36,6 +36,8 @@ namespace AGS3 {
#define GUIDIS_UNCHANGED 4
#define GUIDIS_GUIOFF 0x80
+struct KeyInput;
+
namespace AGS {
namespace Shared {
@@ -73,8 +75,7 @@ public:
// Events
// Key pressed for control
- virtual void OnKeyPress(int keycode) {
- }
+ virtual void OnKeyPress(const KeyInput &ki) {}
// Mouse button down - return 'True' to lock focus
virtual bool OnMouseDown() {
return false;
diff --git a/engines/ags/shared/gui/gui_textbox.cpp b/engines/ags/shared/gui/gui_textbox.cpp
index b2e606d81d..c487f69b85 100644
--- a/engines/ags/shared/gui/gui_textbox.cpp
+++ b/engines/ags/shared/gui/gui_textbox.cpp
@@ -61,7 +61,9 @@ void GUITextBox::Draw(Bitmap *ds) {
DrawTextBoxContents(ds, text_color);
}
-void GUITextBox::OnKeyPress(int keycode) {
+void GUITextBox::OnKeyPress(const KeyInput &ki) {
+ eAGSKeyCode keycode = ki.Key;
+
// other key, continue
if ((keycode >= 128) && (!font_supports_extended_characters(Font)))
return;
diff --git a/engines/ags/shared/gui/gui_textbox.h b/engines/ags/shared/gui/gui_textbox.h
index ebd3c457a9..d3275ed6aa 100644
--- a/engines/ags/shared/gui/gui_textbox.h
+++ b/engines/ags/shared/gui/gui_textbox.h
@@ -42,7 +42,7 @@ public:
void SetShowBorder(bool on);
// Events
- void OnKeyPress(int keycode) override;
+ void OnKeyPress(const KeyInput &ki) override;
// Serialization
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
Commit: 4cb95e324e3bf416ba928078ce3f41db36356529
https://github.com/scummvm/scummvm/commit/4cb95e324e3bf416ba928078ce3f41db36356529
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:36-07:00
Commit Message:
AGS: TextBox control supports unicode
>From upstream 960e6f54a5b255f21fa9366167b3a8dec75230db
Changed paths:
engines/ags/shared/gui/gui_textbox.cpp
diff --git a/engines/ags/shared/gui/gui_textbox.cpp b/engines/ags/shared/gui/gui_textbox.cpp
index c487f69b85..242e7b95e4 100644
--- a/engines/ags/shared/gui/gui_textbox.cpp
+++ b/engines/ags/shared/gui/gui_textbox.cpp
@@ -61,6 +61,18 @@ void GUITextBox::Draw(Bitmap *ds) {
DrawTextBoxContents(ds, text_color);
}
+// TODO: a shared utility function
+static void Backspace(String &text) {
+ if (get_uformat() == U_UTF8) {// Find where the last utf8 char begins
+ const char *ptr_end = text.GetCStr() + text.GetLength();
+ const char *ptr = ptr_end - 1;
+ for (; ptr > text.GetCStr() && ((*ptr & 0xC0) == 0x80); --ptr);
+ text.ClipRight(ptr_end - ptr);
+ } else {
+ text.ClipRight(1);
+ }
+}
+
void GUITextBox::OnKeyPress(const KeyInput &ki) {
eAGSKeyCode keycode = ki.Key;
@@ -71,7 +83,7 @@ void GUITextBox::OnKeyPress(const KeyInput &ki) {
NotifyParentChanged();
// backspace, remove character
if (keycode == eAGSKeyCodeBackspace) {
- Text.ClipRight(1);
+ Backspace(Text);
return;
}
// return/enter
@@ -83,7 +95,7 @@ void GUITextBox::OnKeyPress(const KeyInput &ki) {
Text.AppendChar(keycode);
// if the new string is too long, remove the new character
if (wgettextwidth(Text.GetCStr(), Font) > (Width - (6 + get_fixed_pixel_size(5))))
- Text.ClipRight(1);
+ Backspace(Text);
}
void GUITextBox::SetShowBorder(bool on) {
Commit: 5dec9be2e9864d70f1a137bdaf5ba90f6708cd4e
https://github.com/scummvm/scummvm/commit/5dec9be2e9864d70f1a137bdaf5ba90f6708cd4e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:36-07:00
Commit Message:
AGS: Skeleton for converting UTF-8 translation keys to ASCII
>From upstream e04f86a91b248f598fccf04369e0874393a9a45d
Upstream uses locale, but since that isn't available to a
ScummVM engine, for now it's just a skeleton implementation
Changed paths:
engines/ags/engine/ac/translation.cpp
engines/ags/globals.h
diff --git a/engines/ags/engine/ac/translation.cpp b/engines/ags/engine/ac/translation.cpp
index e91e250d07..7cd27a2bc8 100644
--- a/engines/ags/engine/ac/translation.cpp
+++ b/engines/ags/engine/ac/translation.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "common/language.h"
#include "ags/engine/ac/asset_helper.h"
#include "ags/shared/ac/common.h"
#include "ags/engine/ac/game_setup.h"
@@ -40,6 +41,13 @@ 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();
@@ -111,6 +119,22 @@ bool init_translation(const String &lang, const String &fallback_lang, bool quit
else
set_uformat(U_ASCII);
+ // Mixed encoding support:
+ // original text unfortunately may contain extended ASCII chars (> 127);
+ // if translation is UTF-8 but game is extended ASCII, then the translation
+ // dictionary keys won't match.
+ // With that assumption we must convert dictionary keys into ASCII using
+ // provided locale hint.
+ String key_enc = _GP(trans).StrOptions["gameencoding"];
+ if (!key_enc.IsEmpty()) {
+ StringMap conv_map;
+ 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));
+ }
+ _GP(trans).Dict = conv_map;
+ }
+
Debug::Printf("Translation initialized: %s", _G(trans_filename).GetCStr());
return true;
}
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 0f9d6f6dc7..12e07cd071 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -1319,6 +1319,8 @@ public:
String _trans_name, _trans_filename;
long _lang_offs_start = 0;
char _transFileName[MAX_PATH] = { 0 };
+ std::vector<uint16> _wcsbuf; // widechar buffer
+ std::vector<char> _mbbuf; // utf8 buffer
/**@}*/
Commit: adc208ca4f8c79ffec9bea1971d6e452533e0f49
https://github.com/scummvm/scummvm/commit/adc208ca4f8c79ffec9bea1971d6e452533e0f49
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:37-07:00
Commit Message:
AGS: text parser's F3 command works with unicode
>From upstream d47ddd66dee768fd76fea74f888929bb9c748323
Changed paths:
engines/ags/engine/ac/dialog.cpp
diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index d612c56560..9673eb633d 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -825,12 +825,17 @@ bool DialogOptions::Run() {
// TODO: find out what are these key commands, and are these documented?
if ((gkey == eAGSKeyCodeF3) || ((gkey == eAGSKeyCodeSpace) && (parserInput->Text.GetLength() == 0))) {
// write previous contents into textbox (F3 or Space when box is empty)
- size_t last_len = strlen(_GP(play).lastParserEntry);
-
- for (unsigned int i = parserInput->Text.GetLength(); i < last_len; i++) {
- ki.Key = (eAGSKeyCode)_GP(play).lastParserEntry[i];
- parserInput->OnKeyPress(ki);
+ size_t last_len = ustrlen(_GP(play).lastParserEntry);
+ size_t cur_len = ustrlen(parserInput->Text.GetCStr());
+ // [ikm] CHECKME: tbh I don't quite get the logic here (it was like this in original code);
+ // but what we do is copying only the last part of the previous string
+ if (cur_len < last_len) {
+ const char *entry = _GP(play).lastParserEntry;
+ // TODO: utility function for advancing N utf-8 chars
+ for (size_t i = 0; i < cur_len; ++i) ugetxc(&entry);
+ parserInput->Text.Append(entry);
}
+
//ags_domouse(DOMOUSE_DISABLE);
Redraw();
return true; // continue running loop
Commit: 188951a4998d18a95d1f4f835669449151d16bd7
https://github.com/scummvm/scummvm/commit/188951a4998d18a95d1f4f835669449151d16bd7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:38-07:00
Commit Message:
AGS: Supports reading scripts as separate assets
>From upstream 4de348c79b14dc9f6801b497ae5d7ea4fa1077cd
Changed paths:
engines/ags/engine/game/game_init.cpp
engines/ags/engine/game/game_init.h
engines/ags/engine/main/game_file.cpp
engines/ags/shared/game/main_game_file.cpp
engines/ags/shared/game/main_game_file.h
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 84f348f269..ca09ed8869 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -75,6 +75,8 @@ String GetGameInitErrorText(GameInitErrorType err) {
return "Too many plugins for this engine to handle.";
case kGameInitErr_PluginNameInvalid:
return "Plugin name is invalid.";
+ case kGameInitErr_NoGlobalScript:
+ return "No global script in game.";
case kGameInitErr_ScriptLinkFailed:
return "Script link failed.";
}
@@ -366,6 +368,8 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
// NOTE: we must do this after plugins, because some plugins may export
// script symbols too.
//
+ if (!ents.GlobalScript)
+ return new GameInitError(kGameInitErr_NoGlobalScript);
_GP(gamescript) = ents.GlobalScript;
_GP(dialogScriptsScript) = ents.DialogScript;
_G(numScriptModules) = ents.ScriptModules.size();
diff --git a/engines/ags/engine/game/game_init.h b/engines/ags/engine/game/game_init.h
index fb9550aa94..31b7d6fc03 100644
--- a/engines/ags/engine/game/game_init.h
+++ b/engines/ags/engine/game/game_init.h
@@ -48,6 +48,7 @@ enum GameInitErrorType {
kGameInitErr_EntityInitFail,
kGameInitErr_TooManyPlugins,
kGameInitErr_PluginNameInvalid,
+ kGameInitErr_NoGlobalScript,
kGameInitErr_ScriptLinkFailed
};
diff --git a/engines/ags/engine/main/game_file.cpp b/engines/ags/engine/main/game_file.cpp
index 373c8b13d1..3ad68abbba 100644
--- a/engines/ags/engine/main/game_file.cpp
+++ b/engines/ags/engine/main/game_file.cpp
@@ -47,6 +47,7 @@
#include "ags/engine/gfx/blender.h"
#include "ags/shared/core/asset_manager.h"
#include "ags/shared/util/aligned_stream.h"
+#include "ags/shared/util/text_stream_reader.h"
#include "ags/engine/ac/game_setup.h"
#include "ags/shared/game/main_game_file.h"
#include "ags/engine/game/game_init.h"
@@ -117,13 +118,66 @@ HError preload_game_data() {
return HError::None();
}
+static inline HError MakeScriptLoadError(const char *name) {
+ return new Error(String::FromFormat(
+ "Failed to load a script module: %s", name),
+ _G(ccErrorString));
+}
+
+// Looks up for the game scripts available as separate assets.
+// These are optional, so no error is raised if some of these are not found.
+// For those that do exist, reads them and replaces any scripts of same kind
+// in the already loaded game data.
+HError LoadGameScripts(LoadedGameEntities &ents, GameDataVersion data_ver) {
+ // Global script
+ std::unique_ptr<Stream> in(_GP(AssetMgr)->OpenAsset("GlobalScript.o"));
+ if (in) {
+ PScript script(ccScript::CreateFromStream(in.get()));
+ if (!script)
+ return MakeScriptLoadError("GlobalScript.o");
+ ents.GlobalScript = script;
+ }
+ // Dialog script
+ in.reset(_GP(AssetMgr)->OpenAsset("DialogScript.o"));
+ if (in) {
+ PScript script(ccScript::CreateFromStream(in.get()));
+ if (!script)
+ return MakeScriptLoadError("DialogScript.o");
+ ents.DialogScript = script;
+ }
+ // Script modules
+ // First load a modules list
+ std::vector<String> modules;
+ in.reset(_GP(AssetMgr)->OpenAsset("ScriptModules.lst"));
+ if (in) {
+ TextStreamReader reader(in.get());
+ in.release(); // TextStreamReader got it
+ while (!reader.EOS())
+ modules.push_back(reader.ReadLine());
+ }
+ if (modules.size() > ents.ScriptModules.size())
+ ents.ScriptModules.resize(modules.size());
+ // Now run by the list and try loading everything
+ for (size_t i = 0; i < modules.size(); ++i) {
+ in.reset(_GP(AssetMgr)->OpenAsset(modules[i]));
+ if (in) {
+ PScript script(ccScript::CreateFromStream(in.get()));
+ if (!script)
+ return MakeScriptLoadError(modules[i].GetCStr());
+ ents.ScriptModules[i] = script;
+ }
+ }
+ return HError::None();
+}
+
HError load_game_file() {
MainGameSource src;
LoadedGameEntities ents(_GP(game), _G(dialog), _G(views));
- HGameFileError load_err = OpenMainGameFileFromDefaultAsset(src);
- if (load_err) {
- load_err = ReadGameData(ents, src.InputStream.get(), src.DataVersion);
- if (load_err) {
+ HError err = (HError)OpenMainGameFileFromDefaultAsset(src);
+ if (err) {
+ err = (HError)ReadGameData(ents, src.InputStream.get(), src.DataVersion);
+ src.InputStream.reset();
+ if (err) {
// Upscale mode -- for old games that supported it.
// NOTE: this must be done before UpdateGameData, or resolution-dependant
// adjustments won't be applied correctly.
@@ -134,14 +188,18 @@ HError load_game_file() {
_GP(game).SetGameResolution(kGameResolution_640x480);
}
- load_err = UpdateGameData(ents, src.DataVersion);
+ err = (HError)UpdateGameData(ents, src.DataVersion);
}
}
- if (!load_err)
- return (HError)load_err;
- HGameInitError init_err = InitGameState(ents, src.DataVersion);
- if (!init_err)
- return (HError)init_err;
+
+ if (!err)
+ return err;
+ err = LoadGameScripts(ents, src.DataVersion);
+ if (!err)
+ return err;
+ err = (HError)InitGameState(ents, src.DataVersion);
+ if (!err)
+ return err;
return HError::None();
}
diff --git a/engines/ags/shared/game/main_game_file.cpp b/engines/ags/shared/game/main_game_file.cpp
index ab7db262bd..0b8b153faf 100644
--- a/engines/ags/shared/game/main_game_file.cpp
+++ b/engines/ags/shared/game/main_game_file.cpp
@@ -76,8 +76,6 @@ String GetMainGameFileErrorText(MainGameFileErrorType err) {
return "Failed to deserialize custom properties schema.";
case kMGFErr_InvalidPropertyValues:
return "Errors encountered when reading custom properties.";
- case kMGFErr_NoGlobalScript:
- return "No global script in _GP(game).";
case kMGFErr_CreateGlobalScriptFailed:
return "Failed to load global script.";
case kMGFErr_CreateDialogScriptFailed:
@@ -732,17 +730,17 @@ HGameFileError ReadGameData(LoadedGameEntities &ents, Stream *in, GameDataVersio
_GP(game).read_interaction_scripts(in, data_ver);
_GP(game).read_words_dictionary(in);
- if (!_GP(game).load_compiled_script)
- return new MainGameFileError(kMGFErr_NoGlobalScript);
- ents.GlobalScript.reset(ccScript::CreateFromStream(in));
- if (!ents.GlobalScript)
- return new MainGameFileError(kMGFErr_CreateGlobalScriptFailed, _G(ccErrorString));
- err = ReadDialogScript(ents.DialogScript, in, data_ver);
- if (!err)
- return err;
- err = ReadScriptModules(ents.ScriptModules, in, data_ver);
- if (!err)
- return err;
+ if (game.load_compiled_script) {
+ ents.GlobalScript.reset(ccScript::CreateFromStream(in));
+ if (!ents.GlobalScript)
+ return new MainGameFileError(kMGFErr_CreateGlobalScriptFailed, _G(ccErrorString));
+ err = ReadDialogScript(ents.DialogScript, in, data_ver);
+ if (!err)
+ return err;
+ err = ReadScriptModules(ents.ScriptModules, in, data_ver);
+ if (!err)
+ return err;
+ }
ReadViews(game, ents.Views, in, data_ver);
diff --git a/engines/ags/shared/game/main_game_file.h b/engines/ags/shared/game/main_game_file.h
index 4e92aadb7b..fed671ed9a 100644
--- a/engines/ags/shared/game/main_game_file.h
+++ b/engines/ags/shared/game/main_game_file.h
@@ -67,7 +67,6 @@ enum MainGameFileErrorType {
kMGFErr_TooManyCursors,
kMGFErr_InvalidPropertySchema,
kMGFErr_InvalidPropertyValues,
- kMGFErr_NoGlobalScript,
kMGFErr_CreateGlobalScriptFailed,
kMGFErr_CreateDialogScriptFailed,
kMGFErr_CreateScriptModuleFailed,
Commit: 02883d6d8958317db0065bd5710381f863845a63
https://github.com/scummvm/scummvm/commit/02883d6d8958317db0065bd5710381f863845a63
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:38-07:00
Commit Message:
AGS: Supports reading room scripts as separate assets
>From upstream ec3f214e1c857d1c9b76f70c2446e9f229968afb
Changed paths:
engines/ags/engine/ac/room.cpp
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 67ab6a5a1a..d5121e718f 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -396,7 +396,25 @@ static void update_all_viewcams_with_newroom() {
}
}
-// forchar = _G(playerchar) on NewRoom, or NULL if restore saved game
+// Looks up for the room script available as a separate asset.
+// This is optional, so no error is raised if one is not found.
+// If found however, it will replace room script if one had been loaded
+// from the room file itself.
+HError LoadRoomScript(RoomStruct *room, int newnum) {
+ String filename = String::FromFormat("room%d.o", newnum);
+ std::unique_ptr<Stream> in(_GP(AssetMgr)->OpenAsset(filename));
+ if (in) {
+ PScript script(ccScript::CreateFromStream(in.get()));
+ if (!script)
+ return new Error(String::FromFormat(
+ "Failed to load a script module: %s", filename.GetCStr()),
+ _G(ccErrorString));
+ room->CompiledScript = script;
+ }
+ return HError::None();
+}
+
+// forchar = playerchar on NewRoom, or NULL if restore saved game
void load_new_room(int newnum, CharacterInfo *forchar) {
debug_script_log("Loading room %d", newnum);
@@ -434,6 +452,11 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
quitprintf("!Unable to load '%s'. This room file is assigned to a different _GP(game).", room_filename.GetCStr());
}
+ HError err = LoadRoomScript(&_GP(thisroom), newnum);
+ if (!err)
+ quitprintf("!Unable to load '%s'. Error: %s", room_filename.GetCStr(),
+ err->FullMessage().GetCStr());
+
convert_room_coordinates_to_data_res(&_GP(thisroom));
update_polled_stuff_if_runtime();
Commit: ba10eb247e546a8fe49f57f40402e7b6cfeb18ea
https://github.com/scummvm/scummvm/commit/ba10eb247e546a8fe49f57f40402e7b6cfeb18ea
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:38-07:00
Commit Message:
AGS: print original filename for import errors if possible
>From upstream 94fc737b34e84a5753479910f8da97a28744f918
Changed paths:
engines/ags/engine/script/cc_instance.cpp
diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index 96fe62be8d..fe71d254cf 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -1500,7 +1500,7 @@ bool ccInstance::ResolveScriptImports(PScript scri) {
resolved_imports[i] = _GP(simp).get_index_of(scri->imports[i]);
if (resolved_imports[i] < 0) {
- cc_error("unresolved import '%s'", scri->imports[i]);
+ cc_error("unresolved import '%s' in %s", scri->imports[i], scri->numSections > 0 ? scri->sectionNames[0] : "<unknown>");
return false;
}
}
Commit: 7a61c3838a2bf3db66883ead75ba9f4cb7248d7d
https://github.com/scummvm/scummvm/commit/7a61c3838a2bf3db66883ead75ba9f4cb7248d7d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:39-07:00
Commit Message:
AGS: Added GetScriptAPIName() and use it instead of an array
>From upstream 20d8861d742a8ce61a9f5f72547d5f17025c2f0e
Changed paths:
engines/ags/engine/game/game_init.cpp
engines/ags/shared/ac/game_setup_struct_base.cpp
engines/ags/shared/ac/game_struct_defines.h
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index ca09ed8869..f853381390 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -281,12 +281,11 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
const ScriptAPIVersion base_api = (ScriptAPIVersion)_GP(game).options[OPT_BASESCRIPTAPI];
const ScriptAPIVersion compat_api = (ScriptAPIVersion)_GP(game).options[OPT_SCRIPTCOMPATLEV];
if (data_ver >= kGameVersion_341) {
- // TODO: find a way to either automate this list of strings or make it more visible (shared & easier to find in engine code)
- // TODO: stack-allocated strings, here and in other similar places
- const String scapi_names[kScriptAPI_Current + 1] = { "v3.2.1", "v3.3.0", "v3.3.4", "v3.3.5", "v3.4.0", "v3.4.1", "v3.5.0", "v3.5.0.7" };
+ const char *base_api_name = GetScriptAPIName(base_api);
+ const char *compat_api_name = GetScriptAPIName(compat_api);
Debug::Printf(kDbgMsg_Info, "Requested script API: %s (%d), compat level: %s (%d)",
- base_api >= 0 && base_api <= kScriptAPI_Current ? scapi_names[base_api].GetCStr() : "unknown", base_api,
- compat_api >= 0 && compat_api <= kScriptAPI_Current ? scapi_names[compat_api].GetCStr() : "unknown", compat_api);
+ base_api >= 0 && base_api <= kScriptAPI_Current ? base_api_name : "unknown", base_api,
+ compat_api >= 0 && compat_api <= kScriptAPI_Current ? compat_api_name : "unknown", compat_api);
}
// If the game was compiled using unsupported version of the script API,
// we warn about potential incompatibilities but proceed further.
diff --git a/engines/ags/shared/ac/game_setup_struct_base.cpp b/engines/ags/shared/ac/game_setup_struct_base.cpp
index 2ea471c01a..3cf1db24dd 100644
--- a/engines/ags/shared/ac/game_setup_struct_base.cpp
+++ b/engines/ags/shared/ac/game_setup_struct_base.cpp
@@ -255,4 +255,19 @@ Size ResolutionTypeToSize(GameResolutionType resolution, bool letterbox) {
return Size();
}
+const char *GetScriptAPIName(ScriptAPIVersion v) {
+ switch (v) {
+ case kScriptAPI_v321: return "v3.2.1";
+ case kScriptAPI_v330: return "v3.3.0";
+ case kScriptAPI_v334: return "v3.3.4";
+ case kScriptAPI_v335: return "v3.3.5";
+ case kScriptAPI_v340: return "v3.4.0";
+ case kScriptAPI_v341: return "v3.4.1";
+ case kScriptAPI_v350: return "v3.5.0-alpha";
+ case kScriptAPI_v3507: return "v3.5.0-final";
+ case kScriptAPI_v351: return "v3.5.1";
+ }
+ return "unknown";
+}
+
} // namespace AGS3
diff --git a/engines/ags/shared/ac/game_struct_defines.h b/engines/ags/shared/ac/game_struct_defines.h
index d7259ca908..cb7d802663 100644
--- a/engines/ags/shared/ac/game_struct_defines.h
+++ b/engines/ags/shared/ac/game_struct_defines.h
@@ -169,6 +169,8 @@ enum ScriptAPIVersion {
kScriptAPI_Current = kScriptAPI_v351
};
+extern const char *GetScriptAPIName(ScriptAPIVersion v);
+
// Determines whether the graphics renderer should scale sprites at the final
// screen resolution, as opposed to native resolution
enum RenderAtScreenRes {
Commit: 4271f74cafcd52fc505c0462b20047f78e333b24
https://github.com/scummvm/scummvm/commit/4271f74cafcd52fc505c0462b20047f78e333b24
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:39-07:00
Commit Message:
AGS: Added API level for v3.6.0
>From upstream 1ff5f4586373f140ebad65809d38aa084357eb4a
Changed paths:
engines/ags/shared/ac/game_struct_defines.h
diff --git a/engines/ags/shared/ac/game_struct_defines.h b/engines/ags/shared/ac/game_struct_defines.h
index cb7d802663..5fd7f8d2b2 100644
--- a/engines/ags/shared/ac/game_struct_defines.h
+++ b/engines/ags/shared/ac/game_struct_defines.h
@@ -166,7 +166,8 @@ enum ScriptAPIVersion {
kScriptAPI_v350 = 6,
kScriptAPI_v3507 = 7,
kScriptAPI_v351 = 8,
- kScriptAPI_Current = kScriptAPI_v351
+ kScriptAPI_v360 = 3060000,
+ kScriptAPI_Current = kScriptAPI_v360
};
extern const char *GetScriptAPIName(ScriptAPIVersion v);
Commit: 1b0cbbaafcfa3f5d039f62944987eb63ae689625
https://github.com/scummvm/scummvm/commit/1b0cbbaafcfa3f5d039f62944987eb63ae689625
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:39-07:00
Commit Message:
AGS: implemented System.Log()
>From upstream 0c226592c679106e15f4ec3847ee0d57c2afae19
Changed paths:
engines/ags/engine/ac/system.cpp
engines/ags/shared/debugging/debug_manager.cpp
diff --git a/engines/ags/engine/ac/system.cpp b/engines/ags/engine/ac/system.cpp
index 8c7667b3dd..ac77d137bc 100644
--- a/engines/ags/engine/ac/system.cpp
+++ b/engines/ags/engine/ac/system.cpp
@@ -27,11 +27,13 @@
#include "ags/shared/ac/game_setup_struct.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/global_debug.h"
+#include "ags/engine/ac/global_translation.h"
#include "ags/engine/ac/mouse.h"
#include "ags/engine/ac/string.h"
#include "ags/engine/ac/system.h"
#include "ags/engine/ac/dynobj/script_system.h"
#include "ags/engine/debugging/debug_log.h"
+#include "ags/shared/debugging/out.h"
#include "ags/engine/gfx/graphics_driver.h"
#include "ags/engine/main/config.h"
#include "ags/engine/main/graphics_mode.h"
@@ -48,6 +50,8 @@
namespace AGS3 {
+using namespace AGS::Shared;
+
bool System_HasInputFocus() {
return !_G(switched_away);
}
@@ -330,7 +334,11 @@ RuntimeScriptValue Sc_System_SaveConfigToFile(const RuntimeScriptValue *params,
API_SCALL_VOID(save_config_file);
}
-
+RuntimeScriptValue Sc_System_Log(const RuntimeScriptValue *params, int32_t param_count) {
+ API_SCALL_SCRIPT_SPRINTF(Sc_System_Log, 2);
+ Debug::Printf(kDbgGroup_Script, (MessageType)params[0].IValue, "%s", scsf_buffer);
+ return RuntimeScriptValue((int32_t)0);
+}
void RegisterSystemAPI() {
@@ -363,6 +371,7 @@ void RegisterSystemAPI() {
ccAddExternalStaticFunction("System::set_Windowed", Sc_System_SetWindowed);
ccAddExternalStaticFunction("System::SaveConfigToFile", Sc_System_SaveConfigToFile);
+ ccAddExternalStaticFunction("System::Log^102", Sc_System_Log);
/* ----------------------- Registering unsafe exports for plugins -----------------------*/
diff --git a/engines/ags/shared/debugging/debug_manager.cpp b/engines/ags/shared/debugging/debug_manager.cpp
index 374e07c2e9..083321d783 100644
--- a/engines/ags/shared/debugging/debug_manager.cpp
+++ b/engines/ags/shared/debugging/debug_manager.cpp
@@ -100,6 +100,7 @@ DebugManager::DebugManager() {
// Add hardcoded groups
RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_Main, "main"), ""));
RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_Game, "game"), "Game"));
+ RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_Script, "script"), "Script"));
RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_SprCache, "sprcache"), "Sprite cache"));
RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_ManObj, "manobj"), "Managed obj"));
_firstFreeGroupID = _groups.size();
Commit: 8fc03ec83b213a8ace0ac638fc75532b75aa2216
https://github.com/scummvm/scummvm/commit/8fc03ec83b213a8ace0ac638fc75532b75aa2216
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:39-07:00
Commit Message:
AGS: Fixed DebugManager::RegisterGroup
>From upstream fa31d1bbe2d5e7af95887590d38f4c2233c5ba25
Changed paths:
engines/ags/shared/debugging/debug_manager.cpp
diff --git a/engines/ags/shared/debugging/debug_manager.cpp b/engines/ags/shared/debugging/debug_manager.cpp
index 083321d783..a4b0b33fd4 100644
--- a/engines/ags/shared/debugging/debug_manager.cpp
+++ b/engines/ags/shared/debugging/debug_manager.cpp
@@ -138,8 +138,9 @@ DebugGroup DebugManager::RegisterGroup(const String &id, const String &out_name)
}
void DebugManager::RegisterGroup(const DebugGroup &group) {
- _groups.push_back(group);
- _groupByStrLookup[group.UID.SID] = group.UID;
+ if (_groups.size() <= group.UID.ID)
+ _groups.resize(group.UID.ID + 1);
+ _groups[group.UID.ID] = group; _groupByStrLookup[group.UID.SID] = group.UID;
}
PDebugOutput DebugManager::RegisterOutput(const String &id, IOutputHandler *handler, MessageType def_verbosity, bool enabled) {
Commit: 04d6a41c428d120ba3af7cd1b7babae93e7f7eff
https://github.com/scummvm/scummvm/commit/04d6a41c428d120ba3af7cd1b7babae93e7f7eff
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:40-07:00
Commit Message:
AGS: Simplified System.Log implementation
>From upstream 3d13f9056cd26b0d4da7b8e3207ad330e20a4799
Changed paths:
engines/ags/engine/ac/system.cpp
engines/ags/engine/script/script_api.h
diff --git a/engines/ags/engine/ac/system.cpp b/engines/ags/engine/ac/system.cpp
index ac77d137bc..161db5e3d1 100644
--- a/engines/ags/engine/ac/system.cpp
+++ b/engines/ags/engine/ac/system.cpp
@@ -335,8 +335,8 @@ RuntimeScriptValue Sc_System_SaveConfigToFile(const RuntimeScriptValue *params,
}
RuntimeScriptValue Sc_System_Log(const RuntimeScriptValue *params, int32_t param_count) {
- API_SCALL_SCRIPT_SPRINTF(Sc_System_Log, 2);
- Debug::Printf(kDbgGroup_Script, (MessageType)params[0].IValue, "%s", scsf_buffer);
+ API_SCALL_SCRIPT_SPRINTF_PURE(Sc_System_Log, 2);
+ Debug::Printf(kDbgGroup_Script, (MessageType)params[0].IValue, String::Wrapper(scsf_buffer));
return RuntimeScriptValue((int32_t)0);
}
diff --git a/engines/ags/engine/script/script_api.h b/engines/ags/engine/script/script_api.h
index d7b30e5853..0ffbe8a705 100644
--- a/engines/ags/engine/script/script_api.h
+++ b/engines/ags/engine/script/script_api.h
@@ -82,7 +82,7 @@ inline const char *ScriptVSprintf(char *buffer, size_t buf_length, const char *f
return RuntimeScriptValue()
//-----------------------------------------------------------------------------
-// Calls to ScriptSprintf
+// Calls to ScriptSprintf with automatic translation
#define API_SCALL_SCRIPT_SPRINTF(FUNCTION, PARAM_COUNT) \
ASSERT_PARAM_COUNT(FUNCTION, PARAM_COUNT); \
@@ -94,6 +94,15 @@ inline const char *ScriptVSprintf(char *buffer, size_t buf_length, const char *f
char ScSfBuffer[STD_BUFFER_SIZE]; \
const char *scsf_buffer = ScriptSprintf(ScSfBuffer, STD_BUFFER_SIZE, get_translation(params[PARAM_COUNT - 1].Ptr), params + PARAM_COUNT, param_count - PARAM_COUNT)
+//-----------------------------------------------------------------------------
+// Calls to ScriptSprintf without translation
+
+#define API_SCALL_SCRIPT_SPRINTF_PURE(FUNCTION, PARAM_COUNT) \
+ ASSERT_PARAM_COUNT(FUNCTION, PARAM_COUNT); \
+ char ScSfBuffer[STD_BUFFER_SIZE]; \
+ const char *scsf_buffer = ScriptSprintf(ScSfBuffer, STD_BUFFER_SIZE, params[PARAM_COUNT - 1].Ptr, params + PARAM_COUNT, param_count - PARAM_COUNT)
+
+
//-----------------------------------------------------------------------------
// Calls to ScriptSprintfV (unsafe plugin variant)
Commit: a8f4d9dc5bcf98de0a6f915649106629e2206783
https://github.com/scummvm/scummvm/commit/a8f4d9dc5bcf98de0a6f915649106629e2206783
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:40-07:00
Commit Message:
AGS: Safety check for asset library v20, for asset name over limit
>From upstream 9a2aaae2b3a0799722f1567f97761701afbef584
Changed paths:
engines/ags/shared/util/multi_file_lib.cpp
engines/ags/shared/util/multi_file_lib.h
diff --git a/engines/ags/shared/util/multi_file_lib.cpp b/engines/ags/shared/util/multi_file_lib.cpp
index 9e6d83cb03..f574519fc2 100644
--- a/engines/ags/shared/util/multi_file_lib.cpp
+++ b/engines/ags/shared/util/multi_file_lib.cpp
@@ -258,6 +258,8 @@ MFLUtil::MFLError MFLUtil::ReadV20(AssetLibInfo &lib, Stream *in) {
for (size_t i = 0; i < asset_count; ++i) {
short len = in->ReadInt16();
len /= 5; // CHECKME: why 5?
+ if (len > MaxAssetFileLen)
+ return kMFLErrAssetNameLong;
in->Read(fn_buf, len);
// decrypt filenames
DecryptText(fn_buf);
diff --git a/engines/ags/shared/util/multi_file_lib.h b/engines/ags/shared/util/multi_file_lib.h
index 4e88394a0b..226d0bfd63 100644
--- a/engines/ags/shared/util/multi_file_lib.h
+++ b/engines/ags/shared/util/multi_file_lib.h
@@ -51,6 +51,7 @@ enum MFLError {
kMFLErrLibVersion = -2, // library version unsupported
kMFLErrNoLibBase = -3, // file is not library base (head)
kMFLErrLibAssetCount = -4, // too many assets in library
+ kMFLErrAssetNameLong = -5 // asset name is too long (old formats only)
};
enum MFLVersion {
Commit: 7d01f1dc1f681106da668a293102c4c4810c80b4
https://github.com/scummvm/scummvm/commit/7d01f1dc1f681106da668a293102c4c4810c80b4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:40-07:00
Commit Message:
AGS: in asset library files treat offsets & sizes as unsigned values
>From upstream 603c5c27165e46afc52b06eed2a9f798ae2ecf4e
Changed paths:
engines/ags/shared/util/multi_file_lib.cpp
diff --git a/engines/ags/shared/util/multi_file_lib.cpp b/engines/ags/shared/util/multi_file_lib.cpp
index f574519fc2..5cb35d265b 100644
--- a/engines/ags/shared/util/multi_file_lib.cpp
+++ b/engines/ags/shared/util/multi_file_lib.cpp
@@ -165,7 +165,7 @@ MFLUtil::MFLError MFLUtil::ReadSingleFileLib(AssetLibInfo &lib, Stream *in, MFLV
int passwmodifier = in->ReadByte();
in->ReadInt8(); // unused byte
lib.LibFileNames.resize(1); // only one library part
- size_t asset_count = in->ReadInt16();
+ size_t asset_count = (uint16)in->ReadInt16();
lib.AssetInfos.resize(asset_count);
in->Seek(SingleFilePswLen, kSeekCurrent); // skip password dooberry
@@ -180,7 +180,7 @@ MFLUtil::MFLError MFLUtil::ReadSingleFileLib(AssetLibInfo &lib, Stream *in, MFLV
lib.AssetInfos[i].LibUid = 0;
}
for (size_t i = 0; i < asset_count; ++i) {
- lib.AssetInfos[i].Size = in->ReadInt32();
+ lib.AssetInfos[i].Size = (uint32)in->ReadInt32();
}
in->Seek(2 * asset_count, kSeekCurrent); // skip flags & ratio
lib.AssetInfos[0].Offset = in->GetPosition();
@@ -213,7 +213,7 @@ MFLUtil::MFLError MFLUtil::ReadMultiFileLib(AssetLibInfo &lib, Stream *in, MFLVe
MFLUtil::MFLError MFLUtil::ReadV10(AssetLibInfo &lib, Stream *in, MFLVersion lib_version) {
// number of clib parts
- size_t mf_count = in->ReadInt32();
+ size_t mf_count = (uint32)in->ReadInt32();
lib.LibFileNames.resize(mf_count);
// filenames for all clib parts; filenames are only 20 chars long in this format version
for (size_t i = 0; i < mf_count; ++i) {
@@ -221,7 +221,7 @@ MFLUtil::MFLError MFLUtil::ReadV10(AssetLibInfo &lib, Stream *in, MFLVersion lib
}
// number of files in clib
- size_t asset_count = in->ReadInt32();
+ size_t asset_count = (uint32)in->ReadInt32();
// read information on clib contents
lib.AssetInfos.resize(asset_count);
// filename array is only 25 chars long in this format version
@@ -233,17 +233,17 @@ MFLUtil::MFLError MFLUtil::ReadV10(AssetLibInfo &lib, Stream *in, MFLVersion lib
lib.AssetInfos[i].FileName = fn_buf;
}
for (size_t i = 0; i < asset_count; ++i)
- lib.AssetInfos[i].Offset = in->ReadInt32();
+ lib.AssetInfos[i].Offset = (uint32)in->ReadInt32();
for (size_t i = 0; i < asset_count; ++i)
- lib.AssetInfos[i].Size = in->ReadInt32();
+ lib.AssetInfos[i].Size = (uint32)in->ReadInt32();
for (size_t i = 0; i < asset_count; ++i)
- lib.AssetInfos[i].LibUid = in->ReadInt8();
+ lib.AssetInfos[i].LibUid = (uint32)in->ReadInt8();
return kMFLNoError;
}
MFLUtil::MFLError MFLUtil::ReadV20(AssetLibInfo &lib, Stream *in) {
// number of clib parts
- size_t mf_count = in->ReadInt32();
+ size_t mf_count = (uint32)in->ReadInt32();
lib.LibFileNames.resize(mf_count);
// filenames for all clib parts
for (size_t i = 0; i < mf_count; ++i) {
@@ -251,12 +251,12 @@ MFLUtil::MFLError MFLUtil::ReadV20(AssetLibInfo &lib, Stream *in) {
}
// number of files in clib
- size_t asset_count = in->ReadInt32();
+ size_t asset_count = (uint32)in->ReadInt32();
// read information on clib contents
lib.AssetInfos.resize(asset_count);
char fn_buf[MaxAssetFileLen];
for (size_t i = 0; i < asset_count; ++i) {
- short len = in->ReadInt16();
+ size_t len = in->ReadInt16();
len /= 5; // CHECKME: why 5?
if (len > MaxAssetFileLen)
return kMFLErrAssetNameLong;
@@ -266,11 +266,11 @@ MFLUtil::MFLError MFLUtil::ReadV20(AssetLibInfo &lib, Stream *in) {
lib.AssetInfos[i].FileName = fn_buf;
}
for (size_t i = 0; i < asset_count; ++i)
- lib.AssetInfos[i].Offset = in->ReadInt32();
+ lib.AssetInfos[i].Offset = (uint32)in->ReadInt32();
for (size_t i = 0; i < asset_count; ++i)
- lib.AssetInfos[i].Size = in->ReadInt32();
+ lib.AssetInfos[i].Size = (uint32)in->ReadInt32();
for (size_t i = 0; i < asset_count; ++i)
- lib.AssetInfos[i].LibUid = in->ReadInt8();
+ lib.AssetInfos[i].LibUid = (uint32)in->ReadInt8();
return kMFLNoError;
}
@@ -278,7 +278,7 @@ MFLUtil::MFLError MFLUtil::ReadV21(AssetLibInfo &lib, Stream *in) {
// init randomizer
int rand_val = in->ReadInt32() + EncryptionRandSeed;
// number of clib parts
- size_t mf_count = ReadEncInt32(in, rand_val);
+ size_t mf_count = (uint32)ReadEncInt32(in, rand_val);
lib.LibFileNames.resize(mf_count);
// filenames for all clib parts
char fn_buf[MaxDataFileLen > MaxAssetFileLen ? MaxDataFileLen : MaxAssetFileLen];
@@ -288,7 +288,7 @@ MFLUtil::MFLError MFLUtil::ReadV21(AssetLibInfo &lib, Stream *in) {
}
// number of files in clib
- size_t asset_count = ReadEncInt32(in, rand_val);
+ size_t asset_count = (uint32)ReadEncInt32(in, rand_val);
// read information on clib contents
lib.AssetInfos.resize(asset_count);
for (size_t i = 0; i < asset_count; ++i) {
@@ -296,11 +296,11 @@ MFLUtil::MFLError MFLUtil::ReadV21(AssetLibInfo &lib, Stream *in) {
lib.AssetInfos[i].FileName = fn_buf;
}
for (size_t i = 0; i < asset_count; ++i)
- lib.AssetInfos[i].Offset = ReadEncInt32(in, rand_val);
+ lib.AssetInfos[i].Offset = (uint32)ReadEncInt32(in, rand_val);
for (size_t i = 0; i < asset_count; ++i)
- lib.AssetInfos[i].Size = ReadEncInt32(in, rand_val);
+ lib.AssetInfos[i].Size = (uint32)ReadEncInt32(in, rand_val);
for (size_t i = 0; i < asset_count; ++i)
- lib.AssetInfos[i].LibUid = ReadEncInt8(in, rand_val);
+ lib.AssetInfos[i].LibUid = (uint32)ReadEncInt8(in, rand_val);
return kMFLNoError;
}
@@ -310,19 +310,19 @@ MFLUtil::MFLError MFLUtil::ReadV30(AssetLibInfo &lib, Stream *in, MFLVersion /*
// as one of the options here.
/* int flags = */ in->ReadInt32(); // reserved options
// number of clib parts
- size_t mf_count = in->ReadInt32();
+ size_t mf_count = (uint32)in->ReadInt32();
lib.LibFileNames.resize(mf_count);
// filenames for all clib parts
for (size_t i = 0; i < mf_count; ++i)
lib.LibFileNames[i] = String::FromStream(in);
// number of files in clib
- size_t asset_count = in->ReadInt32();
+ size_t asset_count = (uint32)in->ReadInt32();
// read information on clib contents
lib.AssetInfos.resize(asset_count);
for (auto &asset : lib.AssetInfos) {
asset.FileName = String::FromStream(in);
- asset.LibUid = in->ReadInt8();
+ asset.LibUid = (uint8)in->ReadInt8();
asset.Offset = in->ReadInt64();
asset.Size = in->ReadInt64();
}
Commit: 17b7626d110720c1f39a6b57c3400a62202219ca
https://github.com/scummvm/scummvm/commit/17b7626d110720c1f39a6b57c3400a62202219ca
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:40-07:00
Commit Message:
AGS: don't mark viewport & camera as "changed" when not necessary
>From upstream ecce694c7d93206cae7aac51b302b58aa513e3dc
Changed paths:
engines/ags/engine/game/viewport.cpp
engines/ags/shared/util/geometry.h
diff --git a/engines/ags/engine/game/viewport.cpp b/engines/ags/engine/game/viewport.cpp
index 84c0cb72f7..f01e433875 100644
--- a/engines/ags/engine/game/viewport.cpp
+++ b/engines/ags/engine/game/viewport.cpp
@@ -46,6 +46,8 @@ void Camera::SetSize(const Size cam_size) {
// (or rather - looking outside of the room background); look into this later
const Size real_room_sz = Size(data_to_game_coord(_GP(thisroom).Width), data_to_game_coord(_GP(thisroom).Height));
Size real_size = Size::Clamp(cam_size, Size(1, 1), real_room_sz);
+ if (_position.GetWidth() == real_size.Width && _position.GetHeight() == real_size.Height)
+ return;
_position.SetWidth(real_size.Width);
_position.SetHeight(real_size.Height);
@@ -66,6 +68,8 @@ void Camera::SetAt(int x, int y) {
int room_height = data_to_game_coord(_GP(thisroom).Height);
x = Math::Clamp(x, 0, room_width - cw);
y = Math::Clamp(y, 0, room_height - ch);
+ if (_position.Left == x && _position.Top == y)
+ return;
_position.MoveTo(Point(x, y));
_hasChangedPosition = true;
}
@@ -129,7 +133,10 @@ void Viewport::SetID(int id) {
void Viewport::SetRect(const Rect &rc) {
// TODO: consider allowing size 0,0, in which case viewport is considered not visible
Size fix_size = rc.GetSize().IsNull() ? Size(1, 1) : rc.GetSize();
- _position = RectWH(rc.Left, rc.Top, fix_size.Width, fix_size.Height);
+ Rect new_pos = RectWH(rc.Left, rc.Top, fix_size.Width, fix_size.Height);
+ if (new_pos == _position)
+ return;
+ _position = new_pos;
AdjustTransformation();
_hasChangedPosition = true;
_hasChangedSize = true;
@@ -138,12 +145,16 @@ void Viewport::SetRect(const Rect &rc) {
void Viewport::SetSize(const Size sz) {
// TODO: consider allowing size 0,0, in which case viewport is considered not visible
Size fix_size = sz.IsNull() ? Size(1, 1) : sz;
+ if (_position.GetWidth() == fix_size.Width && _position.GetHeight() == fix_size.Height)
+ return;
_position = RectWH(_position.Left, _position.Top, fix_size.Width, fix_size.Height);
AdjustTransformation();
_hasChangedSize = true;
}
void Viewport::SetAt(int x, int y) {
+ if (_position.Left == x && _position.Top == y)
+ return;
_position.MoveTo(Point(x, y));
AdjustTransformation();
_hasChangedPosition = true;
diff --git a/engines/ags/shared/util/geometry.h b/engines/ags/shared/util/geometry.h
index 16ac4c5d8a..974292c4b7 100644
--- a/engines/ags/shared/util/geometry.h
+++ b/engines/ags/shared/util/geometry.h
@@ -283,6 +283,11 @@ struct Rect {
inline static Rect MoveBy(const Rect &r, int x, int y) {
return Rect(r.Left + x, r.Top + y, r.Right + x, r.Bottom + y);
}
+
+ inline bool operator ==(const Rect &r) const {
+ return Left == r.Left && Top == r.Top &&
+ Right == r.Right && Bottom == r.Bottom;
+ }
};
// Helper factory function
Commit: 4c5d1335a798214ee03fb0dc20e2563517ea1ff6
https://github.com/scummvm/scummvm/commit/4c5d1335a798214ee03fb0dc20e2563517ea1ff6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:41-07:00
Commit Message:
AGS: fixed software mode's dirty rects in the legacy letterbox mode
>From upstream b7fdf44a70d838a0848cd5c94ffe82821323c798
Changed paths:
engines/ags/engine/ac/draw.cpp
engines/ags/engine/ac/draw_software.cpp
engines/ags/engine/ac/draw_software.h
engines/ags/engine/ac/room.cpp
engines/ags/globals.cpp
engines/ags/globals.h
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 95dda4b642..96b0b55088 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -405,8 +405,11 @@ void dispose_room_drawdata() {
void on_mainviewport_changed() {
if (!_G(gfxDriver)->RequiresFullRedrawEachFrame()) {
- init_invalid_regions(-1, _GP(play).GetMainViewport().GetSize(), RectWH(_GP(play).GetMainViewport().GetSize()));
- if (_GP(game).GetGameRes().ExceedsByAny(_GP(play).GetMainViewport().GetSize()))
+ const auto &view = _GP(play).GetMainViewport();
+ set_invalidrects_globaloffs(view.Left, view.Top);
+ // the black background region covers whole game screen
+ init_invalid_regions(-1, _GP(game).GetGameRes(), RectWH(_GP(game).GetGameRes()));
+ if (_GP(game).GetGameRes().ExceedsByAny(view.GetSize()))
clear_letterbox_borders();
}
}
@@ -447,7 +450,10 @@ void prepare_roomview_frame(Viewport *view) {
void sync_roomview(Viewport *view) {
if (view->GetCamera() == nullptr)
return;
- init_invalid_regions(view->GetID(), view->GetCamera()->GetRect().GetSize(), _GP(play).GetRoomViewportAbs(view->GetID()));
+ // Note the dirty regions' viewport is found using absolute offset on game screen
+ init_invalid_regions(view->GetID(),
+ view->GetCamera()->GetRect().GetSize(),
+ _GP(play).GetRoomViewportAbs(view->GetID()));
prepare_roomview_frame(view);
}
@@ -550,12 +556,10 @@ void invalidate_camera_frame(int index) {
}
void invalidate_rect(int x1, int y1, int x2, int y2, bool in_room) {
- //if (!in_room)
invalidate_rect_ds(x1, y1, x2, y2, in_room);
}
void invalidate_sprite(int x1, int y1, IDriverDependantBitmap *pic, bool in_room) {
- //if (!in_room)
invalidate_rect_ds(x1, y1, x1 + pic->GetWidth(), y1 + pic->GetHeight(), in_room);
}
diff --git a/engines/ags/engine/ac/draw_software.cpp b/engines/ags/engine/ac/draw_software.cpp
index d7aa4dfd66..da3bdac1f6 100644
--- a/engines/ags/engine/ac/draw_software.cpp
+++ b/engines/ags/engine/ac/draw_software.cpp
@@ -122,6 +122,10 @@ void dispose_invalid_regions(bool /* room_only */) {
_GP(RoomCamPositions).clear();
}
+void set_invalidrects_globaloffs(int x, int y) {
+ _GP(GlobalOffs) = Point(x, y);
+}
+
void init_invalid_regions(int view_index, const Size &surf_size, const Rect &viewport) {
if (view_index < 0) {
_GP(BlackRects).Init(surf_size, viewport);
@@ -273,6 +277,7 @@ void invalidate_rect_ds(DirtyRects &rects, int x1, int y1, int x2, int y2, bool
y1 = rects.Screen2DirtySurf.Y.ScalePt(y1);
y2 = rects.Screen2DirtySurf.Y.ScalePt(y2);
} else {
+ // Transform only from camera pos to room background
x1 -= rects.Room2Screen.X.GetSrcOffset();
y1 -= rects.Room2Screen.Y.GetSrcOffset();
x2 -= rects.Room2Screen.X.GetSrcOffset();
@@ -283,6 +288,13 @@ void invalidate_rect_ds(DirtyRects &rects, int x1, int y1, int x2, int y2, bool
}
void invalidate_rect_ds(int x1, int y1, int x2, int y2, bool in_room) {
+ if (!in_room) { // convert from game viewport to global screen coords
+ x1 += _GP(GlobalOffs).X;
+ x2 += _GP(GlobalOffs).X;
+ y1 += _GP(GlobalOffs).Y;
+ y2 += _GP(GlobalOffs).Y;
+ }
+
for (auto &rects : _GP(RoomCamRects))
invalidate_rect_ds(rects, x1, y1, x2, y2, in_room);
}
diff --git a/engines/ags/engine/ac/draw_software.h b/engines/ags/engine/ac/draw_software.h
index 02b2100c21..d5a425e03b 100644
--- a/engines/ags/engine/ac/draw_software.h
+++ b/engines/ags/engine/ac/draw_software.h
@@ -87,6 +87,8 @@ struct DirtyRects {
void Reset();
};
+// Sets global viewport offset (used for legacy letterbox)
+void set_invalidrects_globaloffs(int x, int y);
// Inits dirty rects array for the given room camera/viewport pair
// View_index indicates the room viewport (>= 0) or the main viewport (-1)
void init_invalid_regions(int view_index, const Size &surf_size, const Rect &viewport);
@@ -100,6 +102,7 @@ void set_invalidrects_cameraoffs(int view_index, int x, int y);
void invalidate_all_rects();
// Mark the whole camera surface dirty
void invalidate_all_camera_rects(int view_index);
+// Mark certain rectangle dirty; in_room tells if coordinates are room viewport or screen coords
void invalidate_rect_ds(int x1, int y1, int x2, int y2, bool in_room);
// Paints the black screen background in the regions marked as dirty
void update_black_invreg_and_reset(AGS::Shared::Bitmap *ds);
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index d5121e718f..1bc27c6ce5 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -368,6 +368,7 @@ void update_letterbox_mode() {
_GP(play).SetMainViewport(CenterInRect(game_frame, new_main_view));
_GP(play).SetUIViewport(new_main_view);
+ on_mainviewport_changed();
}
// Automatically reset primary room viewport and camera to match the new room size
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index 1c2e766950..aa2fddc7a0 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -176,6 +176,7 @@ Globals::Globals() {
// draw_software.cpp globals
_BlackRects = new DirtyRects();
+ _GlobalOffs = new Point();
_RoomCamRects = new std::vector<DirtyRects>();
_RoomCamPositions = new std::vector<std::pair<int, int> >();
@@ -406,6 +407,7 @@ Globals::~Globals() {
// draw_software.cpp globals
delete _BlackRects;
+ delete _GlobalOffs;
delete _RoomCamRects;
delete _RoomCamPositions;
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 12e07cd071..71a94dffe4 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -138,6 +138,7 @@ struct NonBlockingScriptFunction;
struct ObjectCache;
struct OnScreenWindow;
struct PluginObjectReader;
+struct Point;
struct ResourcePaths;
struct RGB_MAP;
struct RoomCameraDrawData;
@@ -589,10 +590,11 @@ public:
* @{
*/
- // Dirty rects for the main viewport background (black screen);
+ // Dirty rects for the game screen background (black screen);
// these are used when the room viewport does not cover whole screen,
// so that we know when to paint black after mouse cursor and gui.
DirtyRects *_BlackRects;
+ Point *_GlobalOffs;
// Dirty rects object for the single room camera
std::vector<DirtyRects> *_RoomCamRects;
// Saved room camera offsets to know if we must invalidate whole surface.
Commit: c34807897ee43161db412b2413d6362799658c0f
https://github.com/scummvm/scummvm/commit/c34807897ee43161db412b2413d6362799658c0f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T14:33:41-07:00
Commit Message:
AGS: separate dirty rects function for the engine overlay sprites
>From upstream ef78805ffc0e3702dd329401c19116a7d4737470
Changed paths:
engines/ags/engine/ac/draw.cpp
engines/ags/engine/ac/draw_software.cpp
engines/ags/engine/ac/draw_software.h
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 96b0b55088..6fb675d8aa 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -563,6 +563,10 @@ void invalidate_sprite(int x1, int y1, IDriverDependantBitmap *pic, bool in_room
invalidate_rect_ds(x1, y1, x1 + pic->GetWidth(), y1 + pic->GetHeight(), in_room);
}
+void invalidate_sprite_glob(int x1, int y1, IDriverDependantBitmap *pic) {
+ invalidate_rect_global(x1, y1, x1 + pic->GetWidth(), y1 + pic->GetHeight());
+}
+
void mark_current_background_dirty() {
_G(current_background_is_dirty) = true;
}
@@ -1863,7 +1867,7 @@ void draw_fps(const Rect &viewport) {
ddb = _G(gfxDriver)->CreateDDBFromBitmap(fpsDisplay, false);
int yp = viewport.GetHeight() - fpsDisplay->GetHeight();
_G(gfxDriver)->DrawSprite(1, yp, ddb);
- invalidate_sprite(1, yp, ddb, false);
+ invalidate_sprite_glob(1, yp, ddb);
}
// Draw GUI and overlays of all kinds, anything outside the room space
@@ -2204,7 +2208,7 @@ void construct_engine_overlay() {
_G(gfxDriver)->UpdateDDBFromBitmap(_G(debugConsole), _G(debugConsoleBuffer), false);
_G(gfxDriver)->DrawSprite(0, 0, _G(debugConsole));
- invalidate_sprite(0, 0, _G(debugConsole), false);
+ invalidate_sprite_glob(0, 0, _G(debugConsole));
}
if (_G(display_fps) != kFPS_Hide)
diff --git a/engines/ags/engine/ac/draw_software.cpp b/engines/ags/engine/ac/draw_software.cpp
index da3bdac1f6..1cde520996 100644
--- a/engines/ags/engine/ac/draw_software.cpp
+++ b/engines/ags/engine/ac/draw_software.cpp
@@ -299,6 +299,11 @@ void invalidate_rect_ds(int x1, int y1, int x2, int y2, bool in_room) {
invalidate_rect_ds(rects, x1, y1, x2, y2, in_room);
}
+void invalidate_rect_global(int x1, int y1, int x2, int y2) {
+ for (auto &rects : _GP(RoomCamRects))
+ invalidate_rect_ds(rects, x1, y1, x2, y2, false);
+}
+
// Note that this function is denied to perform any kind of scaling or other transformation
// other than blitting with offset. This is mainly because destination could be a 32-bit virtual screen
// while room background was 16-bit and Allegro lib does not support stretching between colour depths.
diff --git a/engines/ags/engine/ac/draw_software.h b/engines/ags/engine/ac/draw_software.h
index d5a425e03b..cc88302c0a 100644
--- a/engines/ags/engine/ac/draw_software.h
+++ b/engines/ags/engine/ac/draw_software.h
@@ -104,6 +104,8 @@ void invalidate_all_rects();
void invalidate_all_camera_rects(int view_index);
// Mark certain rectangle dirty; in_room tells if coordinates are room viewport or screen coords
void invalidate_rect_ds(int x1, int y1, int x2, int y2, bool in_room);
+// Mark rectangle dirty, treat pos as global screen coords (not offset by legacy letterbox mode)
+void invalidate_rect_global(int x1, int y1, int x2, int y2);
// Paints the black screen background in the regions marked as dirty
void update_black_invreg_and_reset(AGS::Shared::Bitmap *ds);
// Copies the room regions marked as dirty from source (src) to destination (ds) with the given offset (x, y)
More information about the Scummvm-git-logs
mailing list