1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 
15 #include "ac/string.h"
16 #include "ac/common.h"
17 #include "ac/display.h"
18 #include "ac/gamesetupstruct.h"
19 #include "ac/gamestate.h"
20 #include "ac/global_translation.h"
21 #include "ac/runtime_defines.h"
22 #include "ac/dynobj/scriptstring.h"
23 #include "debug/debug_log.h"
24 #include "util/string_utils.h"
25 #include "script/runtimescriptvalue.h"
26 
27 extern char lines[MAXLINE][200];
28 extern int  numlines;
29 extern GameSetupStruct game;
30 extern GameState play;
31 extern int longestline;
32 extern ScriptString myScriptStringImpl;
33 
String_IsNullOrEmpty(const char * thisString)34 int String_IsNullOrEmpty(const char *thisString)
35 {
36     if ((thisString == NULL) || (thisString[0] == 0))
37         return 1;
38 
39     return 0;
40 }
41 
String_Copy(const char * srcString)42 const char* String_Copy(const char *srcString) {
43     return CreateNewScriptString(srcString);
44 }
45 
String_Append(const char * thisString,const char * extrabit)46 const char* String_Append(const char *thisString, const char *extrabit) {
47     char *buffer = (char*)malloc(strlen(thisString) + strlen(extrabit) + 1);
48     strcpy(buffer, thisString);
49     strcat(buffer, extrabit);
50     return CreateNewScriptString(buffer, false);
51 }
52 
String_AppendChar(const char * thisString,char extraOne)53 const char* String_AppendChar(const char *thisString, char extraOne) {
54     char *buffer = (char*)malloc(strlen(thisString) + 2);
55     sprintf(buffer, "%s%c", thisString, extraOne);
56     return CreateNewScriptString(buffer, false);
57 }
58 
String_ReplaceCharAt(const char * thisString,int index,char newChar)59 const char* String_ReplaceCharAt(const char *thisString, int index, char newChar) {
60     if ((index < 0) || (index >= (int)strlen(thisString)))
61         quit("!String.ReplaceCharAt: index outside range of string");
62 
63     char *buffer = (char*)malloc(strlen(thisString) + 1);
64     strcpy(buffer, thisString);
65     buffer[index] = newChar;
66     return CreateNewScriptString(buffer, false);
67 }
68 
String_Truncate(const char * thisString,int length)69 const char* String_Truncate(const char *thisString, int length) {
70     if (length < 0)
71         quit("!String.Truncate: invalid length");
72 
73     if (length >= (int)strlen(thisString))
74     {
75         return thisString;
76     }
77 
78     char *buffer = (char*)malloc(length + 1);
79     strncpy(buffer, thisString, length);
80     buffer[length] = 0;
81     return CreateNewScriptString(buffer, false);
82 }
83 
String_Substring(const char * thisString,int index,int length)84 const char* String_Substring(const char *thisString, int index, int length) {
85     if (length < 0)
86         quit("!String.Substring: invalid length");
87     if ((index < 0) || (index > (int)strlen(thisString)))
88         quit("!String.Substring: invalid index");
89 
90     char *buffer = (char*)malloc(length + 1);
91     strncpy(buffer, &thisString[index], length);
92     buffer[length] = 0;
93     return CreateNewScriptString(buffer, false);
94 }
95 
String_CompareTo(const char * thisString,const char * otherString,bool caseSensitive)96 int String_CompareTo(const char *thisString, const char *otherString, bool caseSensitive) {
97 
98     if (caseSensitive) {
99         return strcmp(thisString, otherString);
100     }
101     else {
102         return stricmp(thisString, otherString);
103     }
104 }
105 
String_StartsWith(const char * thisString,const char * checkForString,bool caseSensitive)106 int String_StartsWith(const char *thisString, const char *checkForString, bool caseSensitive) {
107 
108     if (caseSensitive) {
109         return (strncmp(thisString, checkForString, strlen(checkForString)) == 0) ? 1 : 0;
110     }
111     else {
112         return (strnicmp(thisString, checkForString, strlen(checkForString)) == 0) ? 1 : 0;
113     }
114 }
115 
String_EndsWith(const char * thisString,const char * checkForString,bool caseSensitive)116 int String_EndsWith(const char *thisString, const char *checkForString, bool caseSensitive) {
117 
118     int checkAtOffset = strlen(thisString) - strlen(checkForString);
119 
120     if (checkAtOffset < 0)
121     {
122         return 0;
123     }
124 
125     if (caseSensitive)
126     {
127         return (strcmp(&thisString[checkAtOffset], checkForString) == 0) ? 1 : 0;
128     }
129     else
130     {
131         return (stricmp(&thisString[checkAtOffset], checkForString) == 0) ? 1 : 0;
132     }
133 }
134 
String_Replace(const char * thisString,const char * lookForText,const char * replaceWithText,bool caseSensitive)135 const char* String_Replace(const char *thisString, const char *lookForText, const char *replaceWithText, bool caseSensitive)
136 {
137     char resultBuffer[STD_BUFFER_SIZE] = "";
138     int thisStringLen = (int)strlen(thisString);
139     int outputSize = 0;
140     for (int i = 0; i < thisStringLen; i++)
141     {
142         bool matchHere = false;
143         if (caseSensitive)
144         {
145             matchHere = (strncmp(&thisString[i], lookForText, strlen(lookForText)) == 0);
146         }
147         else
148         {
149             matchHere = (strnicmp(&thisString[i], lookForText, strlen(lookForText)) == 0);
150         }
151 
152         if (matchHere)
153         {
154             strcpy(&resultBuffer[outputSize], replaceWithText);
155             outputSize += strlen(replaceWithText);
156             i += strlen(lookForText) - 1;
157         }
158         else
159         {
160             resultBuffer[outputSize] = thisString[i];
161             outputSize++;
162         }
163     }
164 
165     resultBuffer[outputSize] = 0;
166 
167     return CreateNewScriptString(resultBuffer, true);
168 }
169 
String_LowerCase(const char * thisString)170 const char* String_LowerCase(const char *thisString) {
171     char *buffer = (char*)malloc(strlen(thisString) + 1);
172     strcpy(buffer, thisString);
173     strlwr(buffer);
174     return CreateNewScriptString(buffer, false);
175 }
176 
String_UpperCase(const char * thisString)177 const char* String_UpperCase(const char *thisString) {
178     char *buffer = (char*)malloc(strlen(thisString) + 1);
179     strcpy(buffer, thisString);
180     strupr(buffer);
181     return CreateNewScriptString(buffer, false);
182 }
183 
String_GetChars(const char * texx,int index)184 int String_GetChars(const char *texx, int index) {
185     if ((index < 0) || (index >= (int)strlen(texx)))
186         return 0;
187     return texx[index];
188 }
189 
StringToInt(const char * stino)190 int StringToInt(const char*stino) {
191     return atoi(stino);
192 }
193 
StrContains(const char * s1,const char * s2)194 int StrContains (const char *s1, const char *s2) {
195     VALIDATE_STRING (s1);
196     VALIDATE_STRING (s2);
197     char *tempbuf1 = (char*)malloc(strlen(s1) + 1);
198     char *tempbuf2 = (char*)malloc(strlen(s2) + 1);
199     strcpy(tempbuf1, s1);
200     strcpy(tempbuf2, s2);
201     strlwr(tempbuf1);
202     strlwr(tempbuf2);
203 
204     char *offs = strstr (tempbuf1, tempbuf2);
205     free(tempbuf1);
206     free(tempbuf2);
207 
208     if (offs == NULL)
209         return -1;
210 
211     return (offs - tempbuf1);
212 }
213 
214 //=============================================================================
215 
CreateNewScriptString(const char * fromText,bool reAllocate)216 const char *CreateNewScriptString(const char *fromText, bool reAllocate) {
217     ScriptString *str;
218     if (reAllocate) {
219         str = new ScriptString(fromText);
220     }
221     else {
222         str = new ScriptString();
223         str->text = (char*)fromText;
224     }
225 
226     ccRegisterManagedObject(str->text, str);
227 
228     return str->text;
229 }
230 
reverse_text(char * text)231 void reverse_text(char *text) {
232     int length = strlen(text);
233     int index = length / 2;
234     int xedni;
235     char swap;
236     while(index > 0) {
237         xedni = length - index;
238         index--;
239         swap = text[xedni];
240         text[xedni] = text[index];
241         text[index] = swap;
242     }
243 }
244 
break_up_text_into_lines(int wii,int fonnt,const char * todis)245 void break_up_text_into_lines(int wii,int fonnt, const char*todis) {
246     if (fonnt == -1)
247         fonnt = play.normal_font;
248 
249     //  char sofar[100];
250     if (todis[0]=='&') {
251         while ((todis[0]!=' ') & (todis[0]!=0)) todis++;
252         if (todis[0]==' ') todis++;
253     }
254     numlines=0;
255     longestline=0;
256 
257     // Don't attempt to display anything if the width is tiny
258     if (wii < 3)
259         return;
260 
261     int rr;
262     int line_length;
263 
264     split_lines(todis, wii, fonnt);
265 
266     // Right-to-left just means reverse the text then
267     // write it as normal
268     if (game.options[OPT_RIGHTLEFTWRITE])
269         for (rr = 0; rr < numlines; rr++) {
270             reverse_text(lines[rr]);
271             line_length = wgettextwidth_compensate(lines[rr], fonnt);
272             if (line_length > longestline)
273                 longestline = line_length;
274         }
275     else
276         for (rr = 0; rr < numlines; rr++) {
277             line_length = wgettextwidth_compensate(lines[rr], fonnt);
278             if (line_length > longestline)
279                 longestline = line_length;
280         }
281 }
282 
283 int MAXSTRLEN = MAX_MAXSTRLEN;
check_strlen(char * ptt)284 void check_strlen(char*ptt) {
285     MAXSTRLEN = MAX_MAXSTRLEN;
286     long charstart = (long)&game.chars[0];
287     long charend = charstart + sizeof(CharacterInfo)*game.numcharacters;
288     if (((long)&ptt[0] >= charstart) && ((long)&ptt[0] <= charend))
289         MAXSTRLEN=30;
290 }
291 
292 /*void GetLanguageString(int indxx,char*buffr) {
293 VALIDATE_STRING(buffr);
294 char*bptr=get_language_text(indxx);
295 if (bptr==NULL) strcpy(buffr,"[language string error]");
296 else strncpy(buffr,bptr,199);
297 buffr[199]=0;
298 }*/
299 
my_strncpy(char * dest,const char * src,int len)300 void my_strncpy(char *dest, const char *src, int len) {
301     // the normal strncpy pads out the string with zeros up to the
302     // max length -- we don't want that
303     if (strlen(src) >= (unsigned)len) {
304         strncpy(dest, src, len);
305         dest[len] = 0;
306     }
307     else
308         strcpy(dest, src);
309 }
310 
311 //=============================================================================
312 //
313 // Script API Functions
314 //
315 //=============================================================================
316 
317 #include "debug/out.h"
318 #include "script/script_api.h"
319 #include "script/script_runtime.h"
320 #include "ac/math.h"
321 
322 // int (const char *thisString)
Sc_String_IsNullOrEmpty(const RuntimeScriptValue * params,int32_t param_count)323 RuntimeScriptValue Sc_String_IsNullOrEmpty(const RuntimeScriptValue *params, int32_t param_count)
324 {
325     API_SCALL_INT_POBJ(String_IsNullOrEmpty, const char);
326 }
327 
328 // const char* (const char *thisString, const char *extrabit)
Sc_String_Append(void * self,const RuntimeScriptValue * params,int32_t param_count)329 RuntimeScriptValue Sc_String_Append(void *self, const RuntimeScriptValue *params, int32_t param_count)
330 {
331     API_OBJCALL_OBJ_POBJ(const char, const char, myScriptStringImpl, String_Append, const char);
332 }
333 
334 // const char* (const char *thisString, char extraOne)
Sc_String_AppendChar(void * self,const RuntimeScriptValue * params,int32_t param_count)335 RuntimeScriptValue Sc_String_AppendChar(void *self, const RuntimeScriptValue *params, int32_t param_count)
336 {
337     API_OBJCALL_OBJ_PINT(const char, const char, myScriptStringImpl, String_AppendChar);
338 }
339 
340 // int (const char *thisString, const char *otherString, bool caseSensitive)
Sc_String_CompareTo(void * self,const RuntimeScriptValue * params,int32_t param_count)341 RuntimeScriptValue Sc_String_CompareTo(void *self, const RuntimeScriptValue *params, int32_t param_count)
342 {
343     API_OBJCALL_INT_POBJ_PBOOL(const char, String_CompareTo, const char);
344 }
345 
346 // int  (const char *s1, const char *s2)
Sc_StrContains(void * self,const RuntimeScriptValue * params,int32_t param_count)347 RuntimeScriptValue Sc_StrContains(void *self, const RuntimeScriptValue *params, int32_t param_count)
348 {
349     API_OBJCALL_INT_POBJ(const char, StrContains, const char);
350 }
351 
352 // const char* (const char *srcString)
Sc_String_Copy(void * self,const RuntimeScriptValue * params,int32_t param_count)353 RuntimeScriptValue Sc_String_Copy(void *self, const RuntimeScriptValue *params, int32_t param_count)
354 {
355     API_OBJCALL_OBJ(const char, const char, myScriptStringImpl, String_Copy);
356 }
357 
358 // int (const char *thisString, const char *checkForString, bool caseSensitive)
Sc_String_EndsWith(void * self,const RuntimeScriptValue * params,int32_t param_count)359 RuntimeScriptValue Sc_String_EndsWith(void *self, const RuntimeScriptValue *params, int32_t param_count)
360 {
361     API_OBJCALL_INT_POBJ_PBOOL(const char, String_EndsWith, const char);
362 }
363 
364 // const char* (const char *texx, ...)
Sc_String_Format(const RuntimeScriptValue * params,int32_t param_count)365 RuntimeScriptValue Sc_String_Format(const RuntimeScriptValue *params, int32_t param_count)
366 {
367     API_SCALL_SCRIPT_SPRINTF(String_Format, 1);
368     return RuntimeScriptValue().SetDynamicObject((void*)CreateNewScriptString(scsf_buffer), &myScriptStringImpl);
369 }
370 
371 // const char* (const char *thisString)
Sc_String_LowerCase(void * self,const RuntimeScriptValue * params,int32_t param_count)372 RuntimeScriptValue Sc_String_LowerCase(void *self, const RuntimeScriptValue *params, int32_t param_count)
373 {
374     API_OBJCALL_OBJ(const char, const char, myScriptStringImpl, String_LowerCase);
375 }
376 
377 // const char* (const char *thisString, const char *lookForText, const char *replaceWithText, bool caseSensitive)
Sc_String_Replace(void * self,const RuntimeScriptValue * params,int32_t param_count)378 RuntimeScriptValue Sc_String_Replace(void *self, const RuntimeScriptValue *params, int32_t param_count)
379 {
380     API_OBJCALL_OBJ_POBJ2_PBOOL(const char, const char, myScriptStringImpl, String_Replace, const char, const char);
381 }
382 
383 // const char* (const char *thisString, int index, char newChar)
Sc_String_ReplaceCharAt(void * self,const RuntimeScriptValue * params,int32_t param_count)384 RuntimeScriptValue Sc_String_ReplaceCharAt(void *self, const RuntimeScriptValue *params, int32_t param_count)
385 {
386     API_OBJCALL_OBJ_PINT2(const char, const char, myScriptStringImpl, String_ReplaceCharAt);
387 }
388 
389 // int (const char *thisString, const char *checkForString, bool caseSensitive)
Sc_String_StartsWith(void * self,const RuntimeScriptValue * params,int32_t param_count)390 RuntimeScriptValue Sc_String_StartsWith(void *self, const RuntimeScriptValue *params, int32_t param_count)
391 {
392     API_OBJCALL_INT_POBJ_PBOOL(const char, String_StartsWith, const char);
393 }
394 
395 // const char* (const char *thisString, int index, int length)
Sc_String_Substring(void * self,const RuntimeScriptValue * params,int32_t param_count)396 RuntimeScriptValue Sc_String_Substring(void *self, const RuntimeScriptValue *params, int32_t param_count)
397 {
398     API_OBJCALL_OBJ_PINT2(const char, const char, myScriptStringImpl, String_Substring);
399 }
400 
401 // const char* (const char *thisString, int length)
Sc_String_Truncate(void * self,const RuntimeScriptValue * params,int32_t param_count)402 RuntimeScriptValue Sc_String_Truncate(void *self, const RuntimeScriptValue *params, int32_t param_count)
403 {
404     API_OBJCALL_OBJ_PINT(const char, const char, myScriptStringImpl, String_Truncate);
405 }
406 
407 // const char* (const char *thisString)
Sc_String_UpperCase(void * self,const RuntimeScriptValue * params,int32_t param_count)408 RuntimeScriptValue Sc_String_UpperCase(void *self, const RuntimeScriptValue *params, int32_t param_count)
409 {
410     API_OBJCALL_OBJ(const char, const char, myScriptStringImpl, String_UpperCase);
411 }
412 
413 // FLOAT_RETURN_TYPE (const char *theString);
Sc_StringToFloat(void * self,const RuntimeScriptValue * params,int32_t param_count)414 RuntimeScriptValue Sc_StringToFloat(void *self, const RuntimeScriptValue *params, int32_t param_count)
415 {
416     API_OBJCALL_INT(const char, StringToFloat);
417 }
418 
419 // int (char*stino)
Sc_StringToInt(void * self,const RuntimeScriptValue * params,int32_t param_count)420 RuntimeScriptValue Sc_StringToInt(void *self, const RuntimeScriptValue *params, int32_t param_count)
421 {
422     API_OBJCALL_INT(const char, StringToInt);
423 }
424 
425 // int (const char *texx, int index)
Sc_String_GetChars(void * self,const RuntimeScriptValue * params,int32_t param_count)426 RuntimeScriptValue Sc_String_GetChars(void *self, const RuntimeScriptValue *params, int32_t param_count)
427 {
428     API_OBJCALL_INT_PINT(const char, String_GetChars);
429 }
430 
Sc_strlen(void * self,const RuntimeScriptValue * params,int32_t param_count)431 RuntimeScriptValue Sc_strlen(void *self, const RuntimeScriptValue *params, int32_t param_count)
432 {
433     ASSERT_SELF(strlen)
434     return RuntimeScriptValue().SetInt32(strlen((const char*)self));
435 }
436 
437 //=============================================================================
438 //
439 // Exclusive API for Plugins
440 //
441 //=============================================================================
442 
443 // const char* (const char *texx, ...)
ScPl_String_Format(const char * texx,...)444 const char *ScPl_String_Format(const char *texx, ...)
445 {
446     API_PLUGIN_SCRIPT_SPRINTF(texx);
447     return CreateNewScriptString(scsf_buffer);
448 }
449 
450 
RegisterStringAPI()451 void RegisterStringAPI()
452 {
453     ccAddExternalStaticFunction("String::IsNullOrEmpty^1",  Sc_String_IsNullOrEmpty);
454     ccAddExternalObjectFunction("String::Append^1",         Sc_String_Append);
455     ccAddExternalObjectFunction("String::AppendChar^1",     Sc_String_AppendChar);
456     ccAddExternalObjectFunction("String::CompareTo^2",      Sc_String_CompareTo);
457     ccAddExternalObjectFunction("String::Contains^1",       Sc_StrContains);
458     ccAddExternalObjectFunction("String::Copy^0",           Sc_String_Copy);
459     ccAddExternalObjectFunction("String::EndsWith^2",       Sc_String_EndsWith);
460     ccAddExternalStaticFunction("String::Format^101",       Sc_String_Format);
461     ccAddExternalObjectFunction("String::IndexOf^1",        Sc_StrContains);
462     ccAddExternalObjectFunction("String::LowerCase^0",      Sc_String_LowerCase);
463     ccAddExternalObjectFunction("String::Replace^3",        Sc_String_Replace);
464     ccAddExternalObjectFunction("String::ReplaceCharAt^2",  Sc_String_ReplaceCharAt);
465     ccAddExternalObjectFunction("String::StartsWith^2",     Sc_String_StartsWith);
466     ccAddExternalObjectFunction("String::Substring^2",      Sc_String_Substring);
467     ccAddExternalObjectFunction("String::Truncate^1",       Sc_String_Truncate);
468     ccAddExternalObjectFunction("String::UpperCase^0",      Sc_String_UpperCase);
469     ccAddExternalObjectFunction("String::get_AsFloat",      Sc_StringToFloat);
470     ccAddExternalObjectFunction("String::get_AsInt",        Sc_StringToInt);
471     ccAddExternalObjectFunction("String::geti_Chars",       Sc_String_GetChars);
472     ccAddExternalObjectFunction("String::get_Length",       Sc_strlen);
473 
474     /* ----------------------- Registering unsafe exports for plugins -----------------------*/
475 
476     ccAddExternalFunctionForPlugin("String::IsNullOrEmpty^1",  (void*)String_IsNullOrEmpty);
477     ccAddExternalFunctionForPlugin("String::Append^1",         (void*)String_Append);
478     ccAddExternalFunctionForPlugin("String::AppendChar^1",     (void*)String_AppendChar);
479     ccAddExternalFunctionForPlugin("String::CompareTo^2",      (void*)String_CompareTo);
480     ccAddExternalFunctionForPlugin("String::Contains^1",       (void*)StrContains);
481     ccAddExternalFunctionForPlugin("String::Copy^0",           (void*)String_Copy);
482     ccAddExternalFunctionForPlugin("String::EndsWith^2",       (void*)String_EndsWith);
483     ccAddExternalFunctionForPlugin("String::Format^101",       (void*)ScPl_String_Format);
484     ccAddExternalFunctionForPlugin("String::IndexOf^1",        (void*)StrContains);
485     ccAddExternalFunctionForPlugin("String::LowerCase^0",      (void*)String_LowerCase);
486     ccAddExternalFunctionForPlugin("String::Replace^3",        (void*)String_Replace);
487     ccAddExternalFunctionForPlugin("String::ReplaceCharAt^2",  (void*)String_ReplaceCharAt);
488     ccAddExternalFunctionForPlugin("String::StartsWith^2",     (void*)String_StartsWith);
489     ccAddExternalFunctionForPlugin("String::Substring^2",      (void*)String_Substring);
490     ccAddExternalFunctionForPlugin("String::Truncate^1",       (void*)String_Truncate);
491     ccAddExternalFunctionForPlugin("String::UpperCase^0",      (void*)String_UpperCase);
492     ccAddExternalFunctionForPlugin("String::get_AsFloat",      (void*)StringToFloat);
493     ccAddExternalFunctionForPlugin("String::get_AsInt",        (void*)StringToInt);
494     ccAddExternalFunctionForPlugin("String::geti_Chars",       (void*)String_GetChars);
495     ccAddExternalFunctionForPlugin("String::get_Length",       (void*)strlen);
496 }
497