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