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/common.h"
16 #include "ac/view.h"
17 #include "ac/audiochannel.h"
18 #include "ac/character.h"
19 #include "ac/charactercache.h"
20 #include "ac/characterextras.h"
21 #include "ac/dialogtopic.h"
22 #include "ac/draw.h"
23 #include "ac/dynamicsprite.h"
24 #include "ac/event.h"
25 #include "ac/game.h"
26 #include "ac/gamesetup.h"
27 #include "ac/gamesetupstruct.h"
28 #include "ac/gamestate.h"
29 #include "ac/global_audio.h"
30 #include "ac/global_character.h"
31 #include "ac/global_display.h"
32 #include "ac/global_game.h"
33 #include "ac/global_gui.h"
34 #include "ac/global_object.h"
35 #include "ac/global_translation.h"
36 #include "ac/gui.h"
37 #include "ac/hotspot.h"
38 #include "ac/lipsync.h"
39 #include "ac/mouse.h"
40 #include "ac/movelist.h"
41 #include "ac/objectcache.h"
42 #include "ac/overlay.h"
43 #include "ac/path_helper.h"
44 #include "ac/record.h"
45 #include "ac/region.h"
46 #include "ac/richgamemedia.h"
47 #include "ac/room.h"
48 #include "ac/roomobject.h"
49 #include "ac/roomstatus.h"
50 #include "ac/roomstruct.h"
51 #include "ac/runtime_defines.h"
52 #include "ac/screenoverlay.h"
53 #include "ac/spritecache.h"
54 #include "ac/string.h"
55 #include "ac/system.h"
56 #include "ac/timer.h"
57 #include "ac/translation.h"
58 #include "ac/dynobj/all_dynamicclasses.h"
59 #include "ac/dynobj/all_scriptclasses.h"
60 #include "ac/dynobj/cc_audiochannel.h"
61 #include "ac/dynobj/cc_audioclip.h"
62 #include "debug/debug_log.h"
63 #include "debug/out.h"
64 #include "device/mousew32.h"
65 #include "font/fonts.h"
66 #include "game/savegame.h"
67 #include "game/savegame_internal.h"
68 #include "gui/animatingguibutton.h"
69 #include "gfx/graphicsdriver.h"
70 #include "gfx/gfxfilter.h"
71 #include "gui/guidialog.h"
72 #include "main/graphics_mode.h"
73 #include "main/main.h"
74 #include "media/audio/audio.h"
75 #include "media/audio/soundclip.h"
76 #include "plugin/agsplugin.h"
77 #include "plugin/plugin_engine.h"
78 #include "script/cc_error.h"
79 #include "script/runtimescriptvalue.h"
80 #include "script/script.h"
81 #include "script/script_runtime.h"
82 #include "util/alignedstream.h"
83 #include "util/directory.h"
84 #include "util/filestream.h"
85 #include "util/path.h"
86 #include "util/string_utils.h"
87 
88 using namespace AGS::Common;
89 using namespace AGS::Engine;
90 
91 extern ScriptAudioChannel scrAudioChannel[MAX_SOUND_CHANNELS + 1];
92 extern int time_between_timers;
93 extern Bitmap *virtual_screen;
94 extern int cur_mode,cur_cursor;
95 extern SpeechLipSyncLine *splipsync;
96 extern int numLipLines, curLipLine, curLipLinePhoneme;
97 
98 extern CharacterExtras *charextra;
99 extern DialogTopic *dialog;
100 
101 extern int ifacepopped;  // currently displayed pop-up GUI (-1 if none)
102 extern int mouse_on_iface;   // mouse cursor is over this interface
103 extern int mouse_on_iface_button;
104 extern int mouse_pushed_iface;  // this BUTTON on interface MOUSE_ON_IFACE is pushed
105 extern int mouse_ifacebut_xoffs,mouse_ifacebut_yoffs;
106 
107 extern AnimatingGUIButton animbuts[MAX_ANIMATING_BUTTONS];
108 extern int numAnimButs;
109 
110 extern ScreenOverlay screenover[MAX_SCREEN_OVERLAYS];
111 extern int numscreenover;
112 extern int is_complete_overlay,is_text_overlay;
113 
114 #if defined(IOS_VERSION) || defined(ANDROID_VERSION)
115 extern int psp_gfx_renderer;
116 #endif
117 
118 extern int obj_lowest_yp, char_lowest_yp;
119 
120 extern int actSpsCount;
121 extern Bitmap **actsps;
122 extern IDriverDependantBitmap* *actspsbmp;
123 // temporary cache of walk-behind for this actsps image
124 extern Bitmap **actspswb;
125 extern IDriverDependantBitmap* *actspswbbmp;
126 extern CachedActSpsData* actspswbcache;
127 extern Bitmap **guibg;
128 extern IDriverDependantBitmap **guibgbmp;
129 extern char transFileName[MAX_PATH];
130 extern color palette[256];
131 extern int offsetx,offsety;
132 extern unsigned int loopcounter;
133 extern Bitmap *raw_saved_screen;
134 extern Bitmap *dynamicallyCreatedSurfaces[MAX_DYNAMIC_SURFACES];
135 extern IGraphicsDriver *gfxDriver;
136 
137 //=============================================================================
138 GameState play;
139 GameSetup usetup;
140 GameSetupStruct game;
141 RoomStatus troom;    // used for non-saveable rooms, eg. intro
142 RoomObject*objs;
143 RoomStatus*croom=NULL;
144 roomstruct thisroom;
145 
146 volatile int switching_away_from_game = 0;
147 volatile bool switched_away = false;
148 volatile char want_exit = 0, abort_engine = 0;
149 GameDataVersion loaded_game_file_version = kGameVersion_Undefined;
150 int frames_per_second=40;
151 int displayed_room=-10,starting_room = -1;
152 int in_new_room=0, new_room_was = 0;  // 1 in new room, 2 first time in new room, 3 loading saved game
153 int new_room_pos=0;
154 int new_room_x = SCR_NO_VALUE, new_room_y = SCR_NO_VALUE;
155 int new_room_loop = SCR_NO_VALUE;
156 
157 //Bitmap *spriteset[MAX_SPRITES+1];
158 //SpriteCache spriteset (MAX_SPRITES+1);
159 // initially size 1, this will be increased by the initFile function
160 int spritewidth[MAX_SPRITES],spriteheight[MAX_SPRITES];
161 SpriteCache spriteset(1);
162 int proper_exit=0,our_eip=0;
163 
164 std::vector<GUIMain> guis;
165 
166 CCGUIObject ccDynamicGUIObject;
167 CCCharacter ccDynamicCharacter;
168 CCHotspot   ccDynamicHotspot;
169 CCRegion    ccDynamicRegion;
170 CCInventory ccDynamicInv;
171 CCGUI       ccDynamicGUI;
172 CCObject    ccDynamicObject;
173 CCDialog    ccDynamicDialog;
174 CCAudioClip ccDynamicAudioClip;
175 CCAudioChannel ccDynamicAudio;
176 ScriptString myScriptStringImpl;
177 // TODO: IMPORTANT!!
178 // we cannot simply replace these arrays with vectors, or other C++ containers,
179 // until we implement safe management of such containers in script exports
180 // system. Noteably we would need an alternate to StaticArray class to track
181 // access to their elements.
182 ScriptObject scrObj[MAX_INIT_SPR];
183 ScriptGUI    *scrGui = NULL;
184 ScriptHotspot scrHotspot[MAX_HOTSPOTS];
185 ScriptRegion scrRegion[MAX_REGIONS];
186 ScriptInvItem scrInv[MAX_INV];
187 ScriptDialog *scrDialog;
188 
189 ViewStruct*views=NULL;
190 
191 CharacterCache *charcache = NULL;
192 ObjectCache objcache[MAX_INIT_SPR];
193 
194 MoveList *mls = NULL;
195 
196 //=============================================================================
197 
198 char saveGameDirectory[260] = "./";
199 // Custom save game parent directory
200 String saveGameParent;
201 
202 const char* sgnametemplate = "agssave.%03d";
203 String saveGameSuffix;
204 
205 int game_paused=0;
206 char pexbuf[STD_BUFFER_SIZE];
207 
208 unsigned int load_new_game = 0;
209 int load_new_game_restore = -1;
210 
211 int getloctype_index = 0, getloctype_throughgui = 0;
212 
213 //=============================================================================
214 // Audio
215 //=============================================================================
216 
Game_StopAudio(int audioType)217 void Game_StopAudio(int audioType)
218 {
219     if (((audioType < 0) || (audioType >= game.audioClipTypeCount)) && (audioType != SCR_NO_VALUE))
220         quitprintf("!Game.StopAudio: invalid audio type %d", audioType);
221     int aa;
222 
223     for (aa = 0; aa < MAX_SOUND_CHANNELS; aa++)
224     {
225         if (audioType == SCR_NO_VALUE)
226         {
227             stop_or_fade_out_channel(aa);
228         }
229         else
230         {
231             ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&scrAudioChannel[aa]);
232             if ((clip != NULL) && (clip->type == audioType))
233                 stop_or_fade_out_channel(aa);
234         }
235     }
236 
237     remove_clips_of_type_from_queue(audioType);
238 }
239 
Game_IsAudioPlaying(int audioType)240 int Game_IsAudioPlaying(int audioType)
241 {
242     if (((audioType < 0) || (audioType >= game.audioClipTypeCount)) && (audioType != SCR_NO_VALUE))
243         quitprintf("!Game.IsAudioPlaying: invalid audio type %d", audioType);
244 
245     if (play.fast_forward)
246         return 0;
247 
248     for (int aa = 0; aa < MAX_SOUND_CHANNELS; aa++)
249     {
250         ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&scrAudioChannel[aa]);
251         if (clip != NULL)
252         {
253             if ((clip->type == audioType) || (audioType == SCR_NO_VALUE))
254             {
255                 return 1;
256             }
257         }
258     }
259     return 0;
260 }
261 
Game_SetAudioTypeSpeechVolumeDrop(int audioType,int volumeDrop)262 void Game_SetAudioTypeSpeechVolumeDrop(int audioType, int volumeDrop)
263 {
264     if ((audioType < 0) || (audioType >= game.audioClipTypeCount))
265         quitprintf("!Game.SetAudioTypeVolume: invalid audio type: %d", audioType);
266 
267     Debug::Printf("Game.SetAudioTypeSpeechVolumeDrop: type: %d, drop: %d", audioType, volumeDrop);
268     game.audioClipTypes[audioType].volume_reduction_while_speech_playing = volumeDrop;
269     update_volume_drop_if_voiceover();
270 }
271 
Game_SetAudioTypeVolume(int audioType,int volume,int changeType)272 void Game_SetAudioTypeVolume(int audioType, int volume, int changeType)
273 {
274     if ((volume < 0) || (volume > 100))
275         quitprintf("!Game.SetAudioTypeVolume: volume %d is not between 0..100", volume);
276     if ((audioType < 0) || (audioType >= game.audioClipTypeCount))
277         quitprintf("!Game.SetAudioTypeVolume: invalid audio type: %d", audioType);
278     int aa;
279 
280     Debug::Printf("Game.SetAudioTypeVolume: type: %d, volume: %d, change: %d", audioType, volume, changeType);
281     if ((changeType == VOL_CHANGEEXISTING) ||
282         (changeType == VOL_BOTH))
283     {
284         for (aa = 0; aa < MAX_SOUND_CHANNELS; aa++)
285         {
286             ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&scrAudioChannel[aa]);
287             if ((clip != NULL) && (clip->type == audioType))
288             {
289                 channels[aa]->set_volume_percent(volume);
290             }
291         }
292     }
293 
294     if ((changeType == VOL_SETFUTUREDEFAULT) ||
295         (changeType == VOL_BOTH))
296     {
297         play.default_audio_type_volumes[audioType] = volume;
298 
299         // update queued clip volumes
300         update_queued_clips_volume(audioType, volume);
301     }
302 
303 }
304 
Game_GetMODPattern()305 int Game_GetMODPattern() {
306     if (current_music_type == MUS_MOD && channels[SCHAN_MUSIC]) {
307         return channels[SCHAN_MUSIC]->get_pos();
308     }
309     return -1;
310 }
311 
312 //=============================================================================
313 // ---
314 //=============================================================================
315 
Game_GetDialogCount()316 int Game_GetDialogCount()
317 {
318   return game.numdialog;
319 }
320 
set_debug_mode(bool on)321 void set_debug_mode(bool on)
322 {
323     play.debug_mode = on ? 1 : 0;
324     debug_set_console(on);
325 }
326 
set_game_speed(int fps)327 void set_game_speed(int fps) {
328     frames_per_second = fps;
329     time_between_timers = 1000 / fps;
330     install_int_ex(dj_timer_handler,MSEC_TO_TIMER(time_between_timers));
331 }
332 
333 extern int cbuttfont;
334 extern int acdialog_font;
335 
336 extern char buffer2[60];
337 int oldmouse;
setup_for_dialog()338 void setup_for_dialog() {
339     cbuttfont = play.normal_font;
340     acdialog_font = play.normal_font;
341     SetVirtualScreen(virtual_screen);
342     if (!play.mouse_cursor_hidden)
343         domouse(1);
344     oldmouse=cur_cursor; set_mouse_cursor(CURS_ARROW);
345 }
restore_after_dialog()346 void restore_after_dialog() {
347     set_mouse_cursor(oldmouse);
348     if (!play.mouse_cursor_hidden)
349         domouse(2);
350     construct_virtual_screen(true);
351 }
352 
353 
354 
get_save_game_path(int slotNum)355 String get_save_game_path(int slotNum) {
356     String filename;
357     filename.Format(sgnametemplate, slotNum);
358     String path = saveGameDirectory;
359     path.Append(filename);
360     path.Append(saveGameSuffix);
361     return path;
362 }
363 
364 // Convert a path possibly containing path tags into acceptable save path
MakeSaveGameDir(const char * newFolder)365 String MakeSaveGameDir(const char *newFolder)
366 {
367     // don't allow absolute paths
368     if (!is_relative_filename(newFolder))
369         return "";
370 
371     String newSaveGameDir = FixSlashAfterToken(newFolder);
372 
373     if (newSaveGameDir.CompareLeft(UserSavedgamesRootToken, UserSavedgamesRootToken.GetLength()) == 0)
374     {
375         if (saveGameParent.IsEmpty())
376         {
377             newSaveGameDir.ReplaceMid(0, UserSavedgamesRootToken.GetLength(),
378                 PathOrCurDir(platform->GetUserSavedgamesDirectory()));
379         }
380         else
381         {
382             // If there is a custom save parent directory, then replace
383             // not only root token, but also first subdirectory
384             newSaveGameDir.ClipSection('/', 0, 1);
385             if (!newSaveGameDir.IsEmpty())
386                 newSaveGameDir.PrependChar('/');
387             newSaveGameDir.Prepend(saveGameParent);
388         }
389     }
390     else
391     {
392         // Convert the path relative to installation folder into path relative to the
393         // safe save path with default name
394         if (saveGameParent.IsEmpty())
395             newSaveGameDir.Format("%s/%s/%s", PathOrCurDir(platform->GetUserSavedgamesDirectory()),
396                 game.saveGameFolderName, newFolder);
397         else
398             newSaveGameDir.Format("%s/%s", saveGameParent.GetCStr(), newFolder);
399         // For games made in the safe-path-aware versions of AGS, report a warning
400         if (game.options[OPT_SAFEFILEPATHS])
401         {
402             debug_script_warn("Attempt to explicitly set savegame location relative to the game installation directory ('%s') denied;\nPath will be remapped to the user documents directory: '%s'",
403                 newFolder, newSaveGameDir.GetCStr());
404         }
405     }
406     return newSaveGameDir;
407 }
408 
SetCustomSaveParent(const String & path)409 bool SetCustomSaveParent(const String &path)
410 {
411     if (SetSaveGameDirectoryPath(path, true))
412     {
413         saveGameParent = path;
414         return true;
415     }
416     return false;
417 }
418 
SetSaveGameDirectoryPath(const char * newFolder,bool explicit_path)419 bool SetSaveGameDirectoryPath(const char *newFolder, bool explicit_path)
420 {
421     if (!newFolder || newFolder[0] == 0)
422         newFolder = ".";
423     String newSaveGameDir = explicit_path ? String(newFolder) : MakeSaveGameDir(newFolder);
424     if (newSaveGameDir.IsEmpty())
425         return false;
426 
427     if (!Directory::CreateDirectory(newSaveGameDir))
428         return false;
429     newSaveGameDir.AppendChar('/');
430 
431     char newFolderTempFile[260];
432     strcpy(newFolderTempFile, newSaveGameDir);
433     strcat(newFolderTempFile, "agstmp.tmp");
434     if (!Common::File::TestCreateFile(newFolderTempFile))
435         return false;
436 
437     // copy the Restart Game file, if applicable
438     char restartGamePath[260];
439     sprintf(restartGamePath, "%s""agssave.%d%s", saveGameDirectory, RESTART_POINT_SAVE_GAME_NUMBER, saveGameSuffix.GetCStr());
440     Stream *restartGameFile = Common::File::OpenFileRead(restartGamePath);
441     if (restartGameFile != NULL)
442 	{
443         long fileSize = restartGameFile->GetLength();
444         char *mbuffer = (char*)malloc(fileSize);
445         restartGameFile->Read(mbuffer, fileSize);
446         delete restartGameFile;
447 
448         sprintf(restartGamePath, "%s""agssave.%d%s", newSaveGameDir.GetCStr(), RESTART_POINT_SAVE_GAME_NUMBER, saveGameSuffix.GetCStr());
449         restartGameFile = Common::File::CreateFile(restartGamePath);
450         restartGameFile->Write(mbuffer, fileSize);
451         delete restartGameFile;
452         free(mbuffer);
453     }
454 
455     strcpy(saveGameDirectory, newSaveGameDir);
456     return true;
457 }
458 
Game_SetSaveGameDirectory(const char * newFolder)459 int Game_SetSaveGameDirectory(const char *newFolder)
460 {
461     return SetSaveGameDirectoryPath(newFolder, false) ? 1 : 0;
462 }
463 
Game_GetSaveSlotDescription(int slnum)464 const char* Game_GetSaveSlotDescription(int slnum) {
465     String description;
466     if (read_savedgame_description(get_save_game_path(slnum), description))
467     {
468         return CreateNewScriptString(description);
469     }
470     return NULL;
471 }
472 
473 
restore_game_dialog()474 void restore_game_dialog() {
475     can_run_delayed_command();
476     if (thisroom.options[ST_SAVELOAD] == 1) {
477         DisplayMessage (983);
478         return;
479     }
480     if (inside_script) {
481         curscript->queue_action(ePSARestoreGameDialog, 0, "RestoreGameDialog");
482         return;
483     }
484     setup_for_dialog();
485     int toload=loadgamedialog();
486     restore_after_dialog();
487     if (toload>=0) {
488         try_restore_save(toload);
489     }
490 }
491 
save_game_dialog()492 void save_game_dialog() {
493     if (thisroom.options[ST_SAVELOAD] == 1) {
494         DisplayMessage (983);
495         return;
496     }
497     if (inside_script) {
498         curscript->queue_action(ePSASaveGameDialog, 0, "SaveGameDialog");
499         return;
500     }
501     setup_for_dialog();
502     int toload=savegamedialog();
503     restore_after_dialog();
504     if (toload>=0)
505         save_game(toload,buffer2);
506 }
507 
508 
509 
510 
511 
setup_sierra_interface()512 void setup_sierra_interface() {
513     int rr;
514     game.numgui =0;
515     for (rr=0;rr<42;rr++) game.paluses[rr]=PAL_GAMEWIDE;
516     for (rr=42;rr<256;rr++) game.paluses[rr]=PAL_BACKGROUND;
517 }
518 
519 
free_do_once_tokens()520 void free_do_once_tokens()
521 {
522     for (int i = 0; i < play.num_do_once_tokens; i++)
523     {
524         free(play.do_once_tokens[i]);
525     }
526     if (play.do_once_tokens != NULL)
527     {
528         free(play.do_once_tokens);
529         play.do_once_tokens = NULL;
530     }
531     play.num_do_once_tokens = 0;
532 }
533 
534 
535 // Free all the memory associated with the game
unload_game_file()536 void unload_game_file() {
537     int bb, ee;
538 
539     for (bb = 0; bb < game.numcharacters; bb++) {
540         if (game.charScripts != NULL)
541             delete game.charScripts[bb];
542 
543         if (game.intrChar != NULL)
544         {
545             if (game.intrChar[bb] != NULL)
546                 delete game.intrChar[bb];
547             game.intrChar[bb] = NULL;
548         }
549     }
550     if (game.intrChar != NULL)
551     {
552         free(game.intrChar);
553         game.intrChar = NULL;
554     }
555     characterScriptObjNames.clear();
556     free(charextra);
557     free(mls);
558     free(actsps);
559     free(actspsbmp);
560     free(actspswb);
561     free(actspswbbmp);
562     free(actspswbcache);
563     game.charProps.clear();
564 
565     for (bb = 1; bb < game.numinvitems; bb++) {
566         if (game.invScripts != NULL)
567             delete game.invScripts[bb];
568         if (game.intrInv[bb] != NULL)
569             delete game.intrInv[bb];
570         game.intrInv[bb] = NULL;
571     }
572 
573     if (game.charScripts != NULL)
574     {
575         delete game.charScripts;
576         delete game.invScripts;
577         game.charScripts = NULL;
578         game.invScripts = NULL;
579     }
580 
581     if (game.dict != NULL) {
582         game.dict->free_memory();
583         free (game.dict);
584         game.dict = NULL;
585     }
586 
587     if ((gameinst != NULL) && (gameinst->pc != 0))
588         quit("Error: unload_game called while script still running");
589     //->AbortAndDestroy (gameinst);
590     else {
591         delete gameinstFork;
592         delete gameinst;
593         gameinstFork = NULL;
594         gameinst = NULL;
595     }
596 
597     gamescript.reset();
598 
599     if ((dialogScriptsInst != NULL) && (dialogScriptsInst->pc != 0))
600         quit("Error: unload_game called while dialog script still running");
601     else if (dialogScriptsInst != NULL)
602     {
603         delete dialogScriptsInst;
604         dialogScriptsInst = NULL;
605     }
606 
607     dialogScriptsScript.reset();
608 
609     for (ee = 0; ee < numScriptModules; ee++) {
610         delete moduleInstFork[ee];
611         delete moduleInst[ee];
612         scriptModules[ee].reset();
613     }
614     moduleInstFork.resize(0);
615     moduleInst.resize(0);
616     scriptModules.resize(0);
617     repExecAlways.moduleHasFunction.resize(0);
618     lateRepExecAlways.moduleHasFunction.resize(0);
619     getDialogOptionsDimensionsFunc.moduleHasFunction.resize(0);
620     renderDialogOptionsFunc.moduleHasFunction.resize(0);
621     getDialogOptionUnderCursorFunc.moduleHasFunction.resize(0);
622     runDialogOptionMouseClickHandlerFunc.moduleHasFunction.resize(0);
623     runDialogOptionKeyPressHandlerFunc.moduleHasFunction.resize(0);
624     runDialogOptionRepExecFunc.moduleHasFunction.resize(0);
625     numScriptModules = 0;
626 
627     if (game.audioClipCount > 0)
628     {
629         free(game.audioClips);
630         game.audioClipCount = 0;
631         free(game.audioClipTypes);
632         game.audioClipTypeCount = 0;
633     }
634 
635     game.viewNames.clear();
636     free (views);
637     views = NULL;
638 
639     free (game.chars);
640     game.chars = NULL;
641 
642     free (charcache);
643     charcache = NULL;
644 
645     if (splipsync != NULL)
646     {
647         for (ee = 0; ee < numLipLines; ee++)
648         {
649             free(splipsync[ee].endtimeoffs);
650             free(splipsync[ee].frame);
651         }
652         free(splipsync);
653         splipsync = NULL;
654         numLipLines = 0;
655         curLipLine = -1;
656     }
657 
658     for (ee=0;ee < MAXGLOBALMES;ee++) {
659         if (game.messages[ee]==NULL) continue;
660         free (game.messages[ee]);
661         game.messages[ee] = NULL;
662     }
663 
664     for (ee = 0; ee < game.roomCount; ee++)
665     {
666         free(game.roomNames[ee]);
667     }
668     if (game.roomCount > 0)
669     {
670         free(game.roomNames);
671         free(game.roomNumbers);
672         game.roomCount = 0;
673     }
674 
675     for (ee=0;ee<game.numdialog;ee++) {
676         if (dialog[ee].optionscripts!=NULL)
677             free (dialog[ee].optionscripts);
678         dialog[ee].optionscripts = NULL;
679     }
680     free (dialog);
681     dialog = NULL;
682     delete [] scrDialog;
683     scrDialog = NULL;
684 
685     for (ee = 0; ee < game.numgui; ee++) {
686         free (guibg[ee]);
687         guibg[ee] = NULL;
688     }
689 
690     guiScriptObjNames.clear();
691     free(guibg);
692     guis.clear();
693     free(scrGui);
694 
695     pl_stop_plugins();
696     ccRemoveAllSymbols();
697     ccUnregisterAllObjects();
698 
699     for (ee=0;ee<game.numfonts;ee++)
700         wfreefont(ee);
701 
702     free_do_once_tokens();
703     free(play.gui_draw_order);
704 
705     resetRoomStatuses();
706 
707 }
708 
709 
710 
711 
712 
713 
Game_GetGlobalStrings(int index)714 const char* Game_GetGlobalStrings(int index) {
715     if ((index < 0) || (index >= MAXGLOBALSTRINGS))
716         quit("!Game.GlobalStrings: invalid index");
717 
718     return CreateNewScriptString(play.globalstrings[index]);
719 }
720 
721 
722 
723 char gamefilenamebuf[200];
724 
725 
726 // ** GetGameParameter replacement functions
727 
Game_GetInventoryItemCount()728 int Game_GetInventoryItemCount() {
729     // because of the dummy item 0, this is always one higher than it should be
730     return game.numinvitems - 1;
731 }
732 
Game_GetFontCount()733 int Game_GetFontCount() {
734     return game.numfonts;
735 }
736 
Game_GetMouseCursorCount()737 int Game_GetMouseCursorCount() {
738     return game.numcursors;
739 }
740 
Game_GetCharacterCount()741 int Game_GetCharacterCount() {
742     return game.numcharacters;
743 }
744 
Game_GetGUICount()745 int Game_GetGUICount() {
746     return game.numgui;
747 }
748 
Game_GetViewCount()749 int Game_GetViewCount() {
750     return game.numviews;
751 }
752 
Game_GetUseNativeCoordinates()753 int Game_GetUseNativeCoordinates()
754 {
755     if (game.options[OPT_NATIVECOORDINATES] != 0)
756     {
757         return 1;
758     }
759     return 0;
760 }
761 
Game_GetSpriteWidth(int spriteNum)762 int Game_GetSpriteWidth(int spriteNum) {
763     if ((spriteNum < 0) || (spriteNum >= MAX_SPRITES))
764         return 0;
765 
766     if (!spriteset.doesSpriteExist(spriteNum))
767         return 0;
768 
769     return divide_down_coordinate(spritewidth[spriteNum]);
770 }
771 
Game_GetSpriteHeight(int spriteNum)772 int Game_GetSpriteHeight(int spriteNum) {
773     if ((spriteNum < 0) || (spriteNum >= MAX_SPRITES))
774         return 0;
775 
776     if (!spriteset.doesSpriteExist(spriteNum))
777         return 0;
778 
779     return divide_down_coordinate(spriteheight[spriteNum]);
780 }
781 
Game_GetLoopCountForView(int viewNumber)782 int Game_GetLoopCountForView(int viewNumber) {
783     if ((viewNumber < 1) || (viewNumber > game.numviews))
784         quit("!GetGameParameter: invalid view specified");
785 
786     return views[viewNumber - 1].numLoops;
787 }
788 
Game_GetRunNextSettingForLoop(int viewNumber,int loopNumber)789 int Game_GetRunNextSettingForLoop(int viewNumber, int loopNumber) {
790     if ((viewNumber < 1) || (viewNumber > game.numviews))
791         quit("!GetGameParameter: invalid view specified");
792     if ((loopNumber < 0) || (loopNumber >= views[viewNumber - 1].numLoops))
793         quit("!GetGameParameter: invalid loop specified");
794 
795     return (views[viewNumber - 1].loops[loopNumber].RunNextLoop()) ? 1 : 0;
796 }
797 
Game_GetFrameCountForLoop(int viewNumber,int loopNumber)798 int Game_GetFrameCountForLoop(int viewNumber, int loopNumber) {
799     if ((viewNumber < 1) || (viewNumber > game.numviews))
800         quit("!GetGameParameter: invalid view specified");
801     if ((loopNumber < 0) || (loopNumber >= views[viewNumber - 1].numLoops))
802         quit("!GetGameParameter: invalid loop specified");
803 
804     return views[viewNumber - 1].loops[loopNumber].numFrames;
805 }
806 
Game_GetViewFrame(int viewNumber,int loopNumber,int frame)807 ScriptViewFrame* Game_GetViewFrame(int viewNumber, int loopNumber, int frame) {
808     if ((viewNumber < 1) || (viewNumber > game.numviews))
809         quit("!GetGameParameter: invalid view specified");
810     if ((loopNumber < 0) || (loopNumber >= views[viewNumber - 1].numLoops))
811         quit("!GetGameParameter: invalid loop specified");
812     if ((frame < 0) || (frame >= views[viewNumber - 1].loops[loopNumber].numFrames))
813         quit("!GetGameParameter: invalid frame specified");
814 
815     ScriptViewFrame *sdt = new ScriptViewFrame(viewNumber - 1, loopNumber, frame);
816     ccRegisterManagedObject(sdt, sdt);
817     return sdt;
818 }
819 
Game_DoOnceOnly(const char * token)820 int Game_DoOnceOnly(const char *token)
821 {
822     if (strlen(token) > 199)
823         quit("!Game.DoOnceOnly: token length cannot be more than 200 chars");
824 
825     for (int i = 0; i < play.num_do_once_tokens; i++)
826     {
827         if (strcmp(play.do_once_tokens[i], token) == 0)
828         {
829             return 0;
830         }
831     }
832     play.do_once_tokens = (char**)realloc(play.do_once_tokens, sizeof(char*) * (play.num_do_once_tokens + 1));
833     play.do_once_tokens[play.num_do_once_tokens] = (char*)malloc(strlen(token) + 1);
834     strcpy(play.do_once_tokens[play.num_do_once_tokens], token);
835     play.num_do_once_tokens++;
836     return 1;
837 }
838 
Game_GetTextReadingSpeed()839 int Game_GetTextReadingSpeed()
840 {
841     return play.text_speed;
842 }
843 
Game_SetTextReadingSpeed(int newTextSpeed)844 void Game_SetTextReadingSpeed(int newTextSpeed)
845 {
846     if (newTextSpeed < 1)
847         quitprintf("!Game.TextReadingSpeed: %d is an invalid speed", newTextSpeed);
848 
849     play.text_speed = newTextSpeed;
850 }
851 
Game_GetMinimumTextDisplayTimeMs()852 int Game_GetMinimumTextDisplayTimeMs()
853 {
854     return play.text_min_display_time_ms;
855 }
856 
Game_SetMinimumTextDisplayTimeMs(int newTextMinTime)857 void Game_SetMinimumTextDisplayTimeMs(int newTextMinTime)
858 {
859     play.text_min_display_time_ms = newTextMinTime;
860 }
861 
Game_GetIgnoreUserInputAfterTextTimeoutMs()862 int Game_GetIgnoreUserInputAfterTextTimeoutMs()
863 {
864     return play.ignore_user_input_after_text_timeout_ms;
865 }
866 
Game_SetIgnoreUserInputAfterTextTimeoutMs(int newValueMs)867 void Game_SetIgnoreUserInputAfterTextTimeoutMs(int newValueMs)
868 {
869     play.ignore_user_input_after_text_timeout_ms = newValueMs;
870 }
871 
Game_GetFileName()872 const char *Game_GetFileName() {
873     return CreateNewScriptString(usetup.main_data_filename);
874 }
875 
Game_GetName()876 const char *Game_GetName() {
877     return CreateNewScriptString(play.game_name);
878 }
879 
Game_SetName(const char * newName)880 void Game_SetName(const char *newName) {
881     strncpy(play.game_name, newName, 99);
882     play.game_name[99] = 0;
883 
884 #if (ALLEGRO_DATE > 19990103)
885     set_window_title(play.game_name);
886 #endif
887 }
888 
Game_GetSkippingCutscene()889 int Game_GetSkippingCutscene()
890 {
891     if (play.fast_forward)
892     {
893         return 1;
894     }
895     return 0;
896 }
897 
Game_GetInSkippableCutscene()898 int Game_GetInSkippableCutscene()
899 {
900     if (play.in_cutscene)
901     {
902         return 1;
903     }
904     return 0;
905 }
906 
Game_GetColorFromRGB(int red,int grn,int blu)907 int Game_GetColorFromRGB(int red, int grn, int blu) {
908     if ((red < 0) || (red > 255) || (grn < 0) || (grn > 255) ||
909         (blu < 0) || (blu > 255))
910         quit("!GetColorFromRGB: colour values must be 0-255");
911 
912     if (game.color_depth == 1)
913     {
914         return makecol8(red, grn, blu);
915     }
916 
917     int agscolor = ((blu >> 3) & 0x1f);
918     agscolor += ((grn >> 2) & 0x3f) << 5;
919     agscolor += ((red >> 3) & 0x1f) << 11;
920     return agscolor;
921 }
922 
Game_InputBox(const char * msg)923 const char* Game_InputBox(const char *msg) {
924     char buffer[STD_BUFFER_SIZE];
925     sc_inputbox(msg, buffer);
926     return CreateNewScriptString(buffer);
927 }
928 
Game_GetLocationName(int x,int y)929 const char* Game_GetLocationName(int x, int y) {
930     char buffer[STD_BUFFER_SIZE];
931     GetLocationName(x, y, buffer);
932     return CreateNewScriptString(buffer);
933 }
934 
Game_GetGlobalMessages(int index)935 const char* Game_GetGlobalMessages(int index) {
936     if ((index < 500) || (index >= MAXGLOBALMES + 500)) {
937         return NULL;
938     }
939     char buffer[STD_BUFFER_SIZE];
940     buffer[0] = 0;
941     replace_tokens(get_translation(get_global_message(index)), buffer, STD_BUFFER_SIZE);
942     return CreateNewScriptString(buffer);
943 }
944 
Game_GetSpeechFont()945 int Game_GetSpeechFont() {
946     return play.speech_font;
947 }
Game_GetNormalFont()948 int Game_GetNormalFont() {
949     return play.normal_font;
950 }
951 
Game_GetTranslationFilename()952 const char* Game_GetTranslationFilename() {
953     char buffer[STD_BUFFER_SIZE];
954     GetTranslationName(buffer);
955     return CreateNewScriptString(buffer);
956 }
957 
Game_ChangeTranslation(const char * newFilename)958 int Game_ChangeTranslation(const char *newFilename)
959 {
960     if ((newFilename == NULL) || (newFilename[0] == 0))
961     {
962         close_translation();
963         strcpy(transFileName, "");
964         return 1;
965     }
966 
967     String oldTransFileName;
968     oldTransFileName = transFileName;
969 
970     if (!init_translation(newFilename, oldTransFileName.LeftSection('.'), false))
971     {
972         strcpy(transFileName, oldTransFileName);
973         return 0;
974     }
975 
976     return 1;
977 }
978 
Game_GetAudioClip(int index)979 ScriptAudioClip *Game_GetAudioClip(int index)
980 {
981     if (index < 0 || index >= game.audioClipCount)
982         return NULL;
983     return &game.audioClips[index];
984 }
985 
986 //=============================================================================
987 
988 // save game functions
989 
990 
991 
serialize_bitmap(const Common::Bitmap * thispic,Stream * out)992 void serialize_bitmap(const Common::Bitmap *thispic, Stream *out) {
993     if (thispic != NULL) {
994         out->WriteInt32(thispic->GetWidth());
995         out->WriteInt32(thispic->GetHeight());
996         out->WriteInt32(thispic->GetColorDepth());
997         for (int cc=0;cc<thispic->GetHeight();cc++)
998         {
999           switch (thispic->GetColorDepth())
1000           {
1001           case 8:
1002           // CHECKME: originally, AGS does not use real BPP here, but simply divides color depth by 8;
1003           // therefore 15-bit bitmaps are saved only partially? is this a bug? or?
1004           case 15:
1005             out->WriteArray(&thispic->GetScanLine(cc)[0], thispic->GetWidth(), 1);
1006             break;
1007           case 16:
1008             out->WriteArrayOfInt16((const int16_t*)&thispic->GetScanLine(cc)[0], thispic->GetWidth());
1009             break;
1010           case 32:
1011             out->WriteArrayOfInt32((const int32_t*)&thispic->GetScanLine(cc)[0], thispic->GetWidth());
1012             break;
1013           }
1014         }
1015     }
1016 }
1017 
1018 // On Windows we could just use IIDFromString but this is platform-independant
convert_guid_from_text_to_binary(const char * guidText,unsigned char * buffer)1019 void convert_guid_from_text_to_binary(const char *guidText, unsigned char *buffer)
1020 {
1021     guidText++; // skip {
1022     for (int bytesDone = 0; bytesDone < 16; bytesDone++)
1023     {
1024         if (*guidText == '-')
1025             guidText++;
1026 
1027         char tempString[3];
1028         tempString[0] = guidText[0];
1029         tempString[1] = guidText[1];
1030         tempString[2] = 0;
1031         int thisByte = 0;
1032         sscanf(tempString, "%X", &thisByte);
1033 
1034         buffer[bytesDone] = thisByte;
1035         guidText += 2;
1036     }
1037 
1038     // Swap bytes to give correct GUID order
1039     unsigned char temp;
1040     temp = buffer[0]; buffer[0] = buffer[3]; buffer[3] = temp;
1041     temp = buffer[1]; buffer[1] = buffer[2]; buffer[2] = temp;
1042     temp = buffer[4]; buffer[4] = buffer[5]; buffer[5] = temp;
1043     temp = buffer[6]; buffer[6] = buffer[7]; buffer[7] = temp;
1044 }
1045 
read_serialized_bitmap(Stream * in)1046 Bitmap *read_serialized_bitmap(Stream *in) {
1047     Bitmap *thispic;
1048     int picwid = in->ReadInt32();
1049     int pichit = in->ReadInt32();
1050     int piccoldep = in->ReadInt32();
1051     thispic = BitmapHelper::CreateBitmap(picwid,pichit,piccoldep);
1052     if (thispic == NULL)
1053         return NULL;
1054     for (int vv=0; vv < pichit; vv++)
1055     {
1056       switch (piccoldep)
1057       {
1058       case 8:
1059       // CHECKME: originally, AGS does not use real BPP here, but simply divides color depth by 8
1060       case 15:
1061         in->ReadArray(thispic->GetScanLineForWriting(vv), picwid, 1);
1062         break;
1063       case 16:
1064         in->ReadArrayOfInt16((int16_t*)thispic->GetScanLineForWriting(vv), picwid);
1065         break;
1066       case 32:
1067         in->ReadArrayOfInt32((int32_t*)thispic->GetScanLineForWriting(vv), picwid);
1068         break;
1069       }
1070     }
1071 
1072     return thispic;
1073 }
1074 
skip_serialized_bitmap(Stream * in)1075 void skip_serialized_bitmap(Stream *in)
1076 {
1077     int picwid = in->ReadInt32();
1078     int pichit = in->ReadInt32();
1079     int piccoldep = in->ReadInt32();
1080     // CHECKME: originally, AGS does not use real BPP here, but simply divides color depth by 8
1081     int bpp = piccoldep / 8;
1082     in->Seek(picwid * pichit * bpp);
1083 }
1084 
write_screen_shot_for_vista(Stream * out,Bitmap * screenshot)1085 long write_screen_shot_for_vista(Stream *out, Bitmap *screenshot)
1086 {
1087     long fileSize = 0;
1088     char tempFileName[MAX_PATH];
1089     sprintf(tempFileName, "%s""_tmpscht.bmp", saveGameDirectory);
1090 
1091 	screenshot->SaveToFile(tempFileName, palette);
1092 
1093     update_polled_stuff_if_runtime();
1094 
1095     if (exists(tempFileName))
1096     {
1097         fileSize = file_size_ex(tempFileName);
1098         char *buffer = (char*)malloc(fileSize);
1099 
1100         Stream *temp_in = Common::File::OpenFileRead(tempFileName);
1101         temp_in->Read(buffer, fileSize);
1102         delete temp_in;
1103         unlink(tempFileName);
1104 
1105         out->Write(buffer, fileSize);
1106         free(buffer);
1107     }
1108     return fileSize;
1109 }
1110 
save_game_head_dynamic_values(Stream * out)1111 void save_game_head_dynamic_values(Stream *out)
1112 {
1113     out->WriteInt32(frames_per_second);
1114     out->WriteInt32(cur_mode);
1115     out->WriteInt32(cur_cursor);
1116     out->WriteInt32(offsetx); out->WriteInt32(offsety);
1117     out->WriteInt32(loopcounter);
1118 }
1119 
save_game_spriteset(Stream * out)1120 void save_game_spriteset(Stream *out)
1121 {
1122     out->WriteInt32(spriteset.elements);
1123     for (int bb = 1; bb < spriteset.elements; bb++) {
1124         if (game.spriteflags[bb] & SPF_DYNAMICALLOC) {
1125             out->WriteInt32(bb);
1126             out->WriteInt8(game.spriteflags[bb]);
1127             serialize_bitmap(spriteset[bb], out);
1128         }
1129     }
1130     // end of dynamic sprite list
1131     out->WriteInt32(0);
1132 }
1133 
save_game_scripts(Stream * out)1134 void save_game_scripts(Stream *out)
1135 {
1136     // write the data segment of the global script
1137     int gdatasize=gameinst->globaldatasize;
1138     out->WriteInt32(gdatasize);
1139     // MACPORT FIX: just in case gdatasize is 2 or 4, don't want to swap endian
1140     out->Write(&gameinst->globaldata[0], gdatasize);
1141     // write the script modules data segments
1142     out->WriteInt32(numScriptModules);
1143     for (int bb = 0; bb < numScriptModules; bb++) {
1144         int glsize = moduleInst[bb]->globaldatasize;
1145         out->WriteInt32(glsize);
1146         if (glsize > 0) {
1147             out->Write(&moduleInst[bb]->globaldata[0], glsize);
1148         }
1149     }
1150 }
1151 
WriteRoomStatus_Aligned(RoomStatus * roomstat,Stream * out)1152 void WriteRoomStatus_Aligned(RoomStatus *roomstat, Stream *out)
1153 {
1154     AlignedStream align_s(out, Common::kAligned_Write);
1155     roomstat->WriteToFile_v321(&align_s);
1156 }
1157 
save_game_room_state(Stream * out)1158 void save_game_room_state(Stream *out)
1159 {
1160     out->WriteInt32(displayed_room);
1161 
1162     // write the room state for all the rooms the player has been in
1163     RoomStatus* roomstat;
1164     for (int bb = 0; bb < MAX_ROOMS; bb++) {
1165         if (isRoomStatusValid(bb))
1166         {
1167             roomstat = getRoomStatus(bb);
1168             if (roomstat->beenhere) {
1169                 out->WriteInt8 (1);
1170                 WriteRoomStatus_Aligned(roomstat, out);
1171                 if (roomstat->tsdatasize>0)
1172                     out->Write(&roomstat->tsdata[0], roomstat->tsdatasize);
1173             }
1174             else
1175                 out->WriteInt8(0);
1176         }
1177         else
1178             out->WriteInt8(0);
1179     }
1180 }
1181 
save_game_play_ex_data(Stream * out)1182 void save_game_play_ex_data(Stream *out)
1183 {
1184     for (int bb = 0; bb < play.num_do_once_tokens; bb++)
1185     {
1186         fputstring(play.do_once_tokens[bb], out);
1187     }
1188     out->WriteArrayOfInt32(&play.gui_draw_order[0], game.numgui);
1189 }
1190 
WriteMoveList_Aligned(Stream * out)1191 void WriteMoveList_Aligned(Stream *out)
1192 {
1193     AlignedStream align_s(out, Common::kAligned_Write);
1194     for (int i = 0; i < game.numcharacters + MAX_INIT_SPR + 1; ++i)
1195     {
1196         mls[i].WriteToFile(&align_s);
1197         align_s.Reset();
1198     }
1199 }
1200 
WriteGameSetupStructBase_Aligned(Stream * out)1201 void WriteGameSetupStructBase_Aligned(Stream *out)
1202 {
1203     AlignedStream align_s(out, Common::kAligned_Write);
1204     game.GameSetupStructBase::WriteToFile(&align_s);
1205 }
1206 
WriteCharacterExtras_Aligned(Stream * out)1207 void WriteCharacterExtras_Aligned(Stream *out)
1208 {
1209     AlignedStream align_s(out, Common::kAligned_Write);
1210     for (int i = 0; i < game.numcharacters; ++i)
1211     {
1212         charextra[i].WriteToFile(&align_s);
1213         align_s.Reset();
1214     }
1215 }
1216 
save_game_palette(Stream * out)1217 void save_game_palette(Stream *out)
1218 {
1219     out->WriteArray(&palette[0],sizeof(color),256);
1220 }
1221 
save_game_dialogs(Stream * out)1222 void save_game_dialogs(Stream *out)
1223 {
1224     for (int bb=0;bb<game.numdialog;bb++)
1225         out->WriteArrayOfInt32(&dialog[bb].optionflags[0],MAXTOPICOPTIONS);
1226 }
1227 
1228 // [IKM] yea, okay this is just plain silly name :)
save_game_more_dynamic_values(Stream * out)1229 void save_game_more_dynamic_values(Stream *out)
1230 {
1231     out->WriteInt32(mouse_on_iface);
1232     out->WriteInt32(mouse_on_iface_button);
1233     out->WriteInt32(mouse_pushed_iface);
1234     out->WriteInt32 (ifacepopped);
1235     out->WriteInt32(game_paused);
1236     //out->WriteInt32(mi.trk);
1237 }
1238 
WriteAnimatedButtons_Aligned(Stream * out)1239 void WriteAnimatedButtons_Aligned(Stream *out)
1240 {
1241     AlignedStream align_s(out, Common::kAligned_Write);
1242     for (int i = 0; i < numAnimButs; ++i)
1243     {
1244         animbuts[i].WriteToFile(&align_s);
1245         align_s.Reset();
1246     }
1247 }
1248 
save_game_gui(Stream * out)1249 void save_game_gui(Stream *out)
1250 {
1251     write_gui(out,guis,&game,true);
1252     out->WriteInt32(numAnimButs);
1253     WriteAnimatedButtons_Aligned(out);
1254 }
1255 
save_game_audiocliptypes(Stream * out)1256 void save_game_audiocliptypes(Stream *out)
1257 {
1258     out->WriteInt32(game.audioClipTypeCount);
1259     for (int i = 0; i < game.audioClipTypeCount; ++i)
1260     {
1261         game.audioClipTypes[i].WriteToFile(out);
1262     }
1263 }
1264 
save_game_thisroom(Stream * out)1265 void save_game_thisroom(Stream *out)
1266 {
1267     out->WriteArrayOfInt16(&thisroom.regionLightLevel[0],MAX_REGIONS);
1268     out->WriteArrayOfInt32(&thisroom.regionTintLevel[0],MAX_REGIONS);
1269     out->WriteArrayOfInt16(&thisroom.walk_area_zoom[0],MAX_WALK_AREAS + 1);
1270     out->WriteArrayOfInt16(&thisroom.walk_area_zoom2[0],MAX_WALK_AREAS + 1);
1271 }
1272 
save_game_ambientsounds(Stream * out)1273 void save_game_ambientsounds(Stream *out)
1274 {
1275     for (int i = 0; i < MAX_SOUND_CHANNELS; ++i)
1276     {
1277         ambient[i].WriteToFile(out);
1278     }
1279     //out->WriteArray (&ambient[0], sizeof(AmbientSound), MAX_SOUND_CHANNELS);
1280 }
1281 
WriteOverlays_Aligned(Stream * out)1282 void WriteOverlays_Aligned(Stream *out)
1283 {
1284     AlignedStream align_s(out, Common::kAligned_Write);
1285     for (int i = 0; i < numscreenover; ++i)
1286     {
1287         screenover[i].WriteToFile(&align_s);
1288         align_s.Reset();
1289     }
1290 }
1291 
save_game_overlays(Stream * out)1292 void save_game_overlays(Stream *out)
1293 {
1294     out->WriteInt32(numscreenover);
1295     WriteOverlays_Aligned(out);
1296     for (int bb=0;bb<numscreenover;bb++) {
1297         serialize_bitmap (screenover[bb].pic, out);
1298     }
1299 }
1300 
save_game_dynamic_surfaces(Stream * out)1301 void save_game_dynamic_surfaces(Stream *out)
1302 {
1303     for (int bb = 0; bb < MAX_DYNAMIC_SURFACES; bb++)
1304     {
1305         if (dynamicallyCreatedSurfaces[bb] == NULL)
1306         {
1307             out->WriteInt8(0);
1308         }
1309         else
1310         {
1311             out->WriteInt8(1);
1312             serialize_bitmap(dynamicallyCreatedSurfaces[bb], out);
1313         }
1314     }
1315 }
1316 
save_game_displayed_room_status(Stream * out)1317 void save_game_displayed_room_status(Stream *out)
1318 {
1319     if (displayed_room >= 0) {
1320 
1321         for (int bb = 0; bb < MAX_BSCENE; bb++) {
1322             if (play.raw_modified[bb])
1323                 serialize_bitmap (thisroom.ebscene[bb], out);
1324         }
1325 
1326         out->WriteInt32 ((raw_saved_screen == NULL) ? 0 : 1);
1327         if (raw_saved_screen)
1328             serialize_bitmap (raw_saved_screen, out);
1329 
1330         // save the current troom, in case they save in room 600 or whatever
1331         WriteRoomStatus_Aligned(&troom, out);
1332         if (troom.tsdatasize>0)
1333             out->Write(&troom.tsdata[0],troom.tsdatasize);
1334 
1335     }
1336 }
1337 
save_game_globalvars(Stream * out)1338 void save_game_globalvars(Stream *out)
1339 {
1340     out->WriteInt32 (numGlobalVars);
1341     for (int i = 0; i < numGlobalVars; ++i)
1342     {
1343         globalvars[i].Write(out);
1344     }
1345 }
1346 
save_game_views(Stream * out)1347 void save_game_views(Stream *out)
1348 {
1349     out->WriteInt32 (game.numviews);
1350     for (int bb = 0; bb < game.numviews; bb++) {
1351         for (int cc = 0; cc < views[bb].numLoops; cc++) {
1352             for (int dd = 0; dd < views[bb].loops[cc].numFrames; dd++)
1353             {
1354                 out->WriteInt32(views[bb].loops[cc].frames[dd].sound);
1355                 out->WriteInt32(views[bb].loops[cc].frames[dd].pic);
1356             }
1357         }
1358     }
1359 }
1360 
save_game_audioclips_and_crossfade(Stream * out)1361 void save_game_audioclips_and_crossfade(Stream *out)
1362 {
1363     out->WriteInt32(game.audioClipCount);
1364     for (int bb = 0; bb <= MAX_SOUND_CHANNELS; bb++)
1365     {
1366         if ((channels[bb] != NULL) && (channels[bb]->done == 0) && (channels[bb]->sourceClip != NULL))
1367         {
1368             out->WriteInt32(((ScriptAudioClip*)channels[bb]->sourceClip)->id);
1369             out->WriteInt32(channels[bb]->get_pos());
1370             out->WriteInt32(channels[bb]->priority);
1371             out->WriteInt32(channels[bb]->repeat ? 1 : 0);
1372             out->WriteInt32(channels[bb]->vol);
1373             out->WriteInt32(channels[bb]->panning);
1374             out->WriteInt32(channels[bb]->volAsPercentage);
1375             out->WriteInt32(channels[bb]->panningAsPercentage);
1376             if (loaded_game_file_version >= kGameVersion_340_2)
1377                 out->WriteInt32(channels[bb]->speed);
1378         }
1379         else
1380         {
1381             out->WriteInt32(-1);
1382         }
1383     }
1384     out->WriteInt32(crossFading);
1385     out->WriteInt32(crossFadeVolumePerStep);
1386     out->WriteInt32(crossFadeStep);
1387     out->WriteInt32(crossFadeVolumeAtStart);
1388 }
1389 
WriteGameState_Aligned(Stream * out)1390 void WriteGameState_Aligned(Stream *out)
1391 {
1392     AlignedStream align_s(out, Common::kAligned_Write);
1393     play.WriteToFile_v321(&align_s);
1394 }
1395 
1396 #define MAGICNUMBER 0xbeefcafe
1397 // Write the save game position to the file
save_game_data(Stream * out)1398 void save_game_data(Stream *out)
1399 {
1400     save_game_head_dynamic_values(out);
1401     save_game_spriteset(out);
1402     save_game_scripts(out);
1403     save_game_room_state(out);
1404 
1405     update_polled_stuff_if_runtime();
1406 
1407     //----------------------------------------------------------------
1408     WriteGameState_Aligned(out);
1409 
1410     save_game_play_ex_data(out);
1411     //----------------------------------------------------------------
1412 
1413     WriteMoveList_Aligned(out);
1414 
1415     WriteGameSetupStructBase_Aligned(out);
1416 
1417     //----------------------------------------------------------------
1418     game.WriteForSaveGame_v321(out);
1419 
1420     // Modified custom properties are written separately to keep existing save format
1421     play.WriteCustomProperties(out);
1422 
1423     WriteCharacterExtras_Aligned(out);
1424     save_game_palette(out);
1425     save_game_dialogs(out);
1426     save_game_more_dynamic_values(out);
1427     save_game_gui(out);
1428     save_game_audiocliptypes(out);
1429     save_game_thisroom(out);
1430     save_game_ambientsounds(out);
1431     save_game_overlays(out);
1432 
1433     update_polled_stuff_if_runtime();
1434 
1435     save_game_dynamic_surfaces(out);
1436 
1437     update_polled_stuff_if_runtime();
1438 
1439     save_game_displayed_room_status(out);
1440     save_game_globalvars(out);
1441     save_game_views(out);
1442 
1443     out->WriteInt32 (MAGICNUMBER+1);
1444 
1445     save_game_audioclips_and_crossfade(out);
1446 
1447     // [IKM] Plugins expect FILE pointer! // TODO something with this later...
1448     int pluginFileHandle = AGSE_SAVEGAME;
1449     pl_set_file_handle(pluginFileHandle, out);
1450     pl_run_plugin_hooks(AGSE_SAVEGAME, pluginFileHandle);
1451     pl_clear_file_handle();
1452     out->WriteInt32 (MAGICNUMBER);  // to verify the plugins
1453 
1454     // save the room music volume
1455     out->WriteInt32(thisroom.options[ST_VOLUME]);
1456 
1457     ccSerializeAllObjects(out);
1458 
1459     out->WriteInt32(current_music_type);
1460 
1461     update_polled_stuff_if_runtime();
1462 }
1463 
create_savegame_screenshot(Bitmap * & screenShot)1464 void create_savegame_screenshot(Bitmap *&screenShot)
1465 {
1466     if (game.options[OPT_SAVESCREENSHOT]) {
1467         int usewid = multiply_up_coordinate(play.screenshot_width);
1468         int usehit = multiply_up_coordinate(play.screenshot_height);
1469         const Rect &viewport = play.viewport;
1470         if (usewid > viewport.GetWidth())
1471             usewid = viewport.GetWidth();
1472         if (usehit > viewport.GetHeight())
1473             usehit = viewport.GetHeight();
1474 
1475         if ((play.screenshot_width < 16) || (play.screenshot_height < 16))
1476             quit("!Invalid game.screenshot_width/height, must be from 16x16 to screen res");
1477 
1478         screenShot = CopyScreenIntoBitmap(usewid, usehit);
1479     }
1480 }
1481 
save_game(int slotn,const char * descript)1482 void save_game(int slotn, const char*descript) {
1483 
1484     // dont allow save in rep_exec_always, because we dont save
1485     // the state of blocked scripts
1486     can_run_delayed_command();
1487 
1488     if (inside_script) {
1489         strcpy(curscript->postScriptSaveSlotDescription[curscript->queue_action(ePSASaveGame, slotn, "SaveGameSlot")], descript);
1490         return;
1491     }
1492 
1493     if (platform->GetDiskFreeSpaceMB() < 2) {
1494         Display("ERROR: There is not enough disk space free to save the game. Clear some disk space and try again.");
1495         return;
1496     }
1497 
1498     VALIDATE_STRING(descript);
1499     String nametouse;
1500     nametouse = get_save_game_path(slotn);
1501 
1502     Bitmap *screenShot = NULL;
1503 
1504     // Screenshot
1505     create_savegame_screenshot(screenShot);
1506 
1507     Stream *out = StartSavegame(nametouse, descript, screenShot);
1508     if (out == NULL)
1509         quit("save_game: unable to open savegame file for writing");
1510 
1511     update_polled_stuff_if_runtime();
1512 
1513     // Actual dynamic game data is saved here
1514     SaveGameState(out);
1515 
1516     if (screenShot != NULL)
1517     {
1518         int screenShotOffset = out->GetPosition() - sizeof(RICH_GAME_MEDIA_HEADER);
1519         int screenShotSize = write_screen_shot_for_vista(out, screenShot);
1520         delete out;
1521 
1522         update_polled_stuff_if_runtime();
1523 
1524         out = Common::File::OpenFile(nametouse, Common::kFile_Open, Common::kFile_ReadWrite);
1525         out->Seek(12, kSeekBegin);
1526         out->WriteInt32(screenShotOffset);
1527         out->Seek(4);
1528         out->WriteInt32(screenShotSize);
1529     }
1530 
1531     if (screenShot != NULL)
1532         delete screenShot;
1533 
1534     delete out;
1535 }
1536 
1537 char rbuffer[200];
1538 
restore_game_head_dynamic_values(Stream * in,RestoredData & r_data)1539 SavegameError restore_game_head_dynamic_values(Stream *in, RestoredData &r_data)
1540 {
1541     r_data.FPS = in->ReadInt32();
1542     r_data.CursorMode = in->ReadInt32();
1543     r_data.CursorID = in->ReadInt32();
1544     offsetx = in->ReadInt32();
1545     offsety = in->ReadInt32();
1546     loopcounter = in->ReadInt32();
1547 
1548     return kSvgErr_NoError;
1549 }
1550 
restore_game_spriteset(Stream * in)1551 void restore_game_spriteset(Stream *in)
1552 {
1553     // ensure the sprite set is at least as large as it was
1554     // when the game was saved
1555     spriteset.enlargeTo(in->ReadInt32());
1556     // get serialized dynamic sprites
1557     int sprnum = in->ReadInt32();
1558     while (sprnum) {
1559         unsigned char spriteflag = in->ReadByte();
1560         add_dynamic_sprite(sprnum, read_serialized_bitmap(in));
1561         game.spriteflags[sprnum] = spriteflag;
1562         sprnum = in->ReadInt32();
1563     }
1564 }
1565 
restore_game_scripts(Stream * in,const PreservedParams & pp,RestoredData & r_data)1566 SavegameError restore_game_scripts(Stream *in, const PreservedParams &pp, RestoredData &r_data)
1567 {
1568     // read the global script data segment
1569     int gdatasize = in->ReadInt32();
1570     if (pp.GlScDataSize != gdatasize)
1571     {
1572         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching size of global script data");
1573         return kSvgErr_GameContentAssertion;
1574     }
1575     r_data.GlobalScript.Len = gdatasize;
1576     r_data.GlobalScript.Data.reset(new char[gdatasize]);
1577     in->Read(r_data.GlobalScript.Data.get(), gdatasize);
1578 
1579     if (in->ReadInt32() != numScriptModules)
1580     {
1581         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of script modules");
1582         return kSvgErr_GameContentAssertion;
1583     }
1584     r_data.ScriptModules.resize(numScriptModules);
1585     for (int i = 0; i < numScriptModules; ++i)
1586     {
1587         size_t module_size = in->ReadInt32();
1588         if (pp.ScMdDataSize[i] != module_size)
1589         {
1590             Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching size of script module data, module %d", i);
1591             return kSvgErr_GameContentAssertion;
1592         }
1593         r_data.ScriptModules[i].Len = module_size;
1594         r_data.ScriptModules[i].Data.reset(new char[module_size]);
1595         in->Read(r_data.ScriptModules[i].Data.get(), module_size);
1596     }
1597     return kSvgErr_NoError;
1598 }
1599 
ReadRoomStatus_Aligned(RoomStatus * roomstat,Stream * in)1600 void ReadRoomStatus_Aligned(RoomStatus *roomstat, Stream *in)
1601 {
1602     AlignedStream align_s(in, Common::kAligned_Read);
1603     roomstat->ReadFromFile_v321(&align_s);
1604 }
1605 
restore_game_room_state(Stream * in)1606 void restore_game_room_state(Stream *in)
1607 {
1608     int vv;
1609 
1610     displayed_room = in->ReadInt32();
1611 
1612     // read the room state for all the rooms the player has been in
1613     RoomStatus* roomstat;
1614     int beenhere;
1615     for (vv=0;vv<MAX_ROOMS;vv++)
1616     {
1617         beenhere = in->ReadByte();
1618         if (beenhere)
1619         {
1620             roomstat = getRoomStatus(vv);
1621             roomstat->beenhere = beenhere;
1622 
1623             if (roomstat->beenhere)
1624             {
1625                 ReadRoomStatus_Aligned(roomstat, in);
1626                 if (roomstat->tsdatasize > 0)
1627                 {
1628                     roomstat->tsdata=(char*)malloc(roomstat->tsdatasize + 8);  // JJS: Why allocate 8 additional bytes?
1629                     in->Read(&roomstat->tsdata[0], roomstat->tsdatasize);
1630                 }
1631             }
1632         }
1633     }
1634 }
1635 
ReadGameState_Aligned(Stream * in)1636 void ReadGameState_Aligned(Stream *in)
1637 {
1638     AlignedStream align_s(in, Common::kAligned_Read);
1639     play.ReadFromFile_v321(&align_s);
1640 }
1641 
restore_game_play_ex_data(Stream * in)1642 void restore_game_play_ex_data(Stream *in)
1643 {
1644     if (play.num_do_once_tokens > 0)
1645     {
1646         play.do_once_tokens = (char**)malloc(sizeof(char*) * play.num_do_once_tokens);
1647         for (int bb = 0; bb < play.num_do_once_tokens; bb++)
1648         {
1649             fgetstring_limit(rbuffer, in, 200);
1650             play.do_once_tokens[bb] = (char*)malloc(strlen(rbuffer) + 1);
1651             strcpy(play.do_once_tokens[bb], rbuffer);
1652         }
1653     }
1654 
1655     in->ReadArrayOfInt32(&play.gui_draw_order[0], game.numgui);
1656 }
1657 
restore_game_play(Stream * in)1658 void restore_game_play(Stream *in)
1659 {
1660     // preserve the replay settings
1661     int playback_was = play.playback, recording_was = play.recording;
1662     int gamestep_was = play.gamestep;
1663     int screenfadedout_was = play.screen_is_faded_out;
1664     int roomchanges_was = play.room_changes;
1665     // make sure the pointer is preserved
1666     int *gui_draw_order_was = play.gui_draw_order;
1667 
1668     ReadGameState_Aligned(in);
1669 
1670     play.screen_is_faded_out = screenfadedout_was;
1671     play.playback = playback_was;
1672     play.recording = recording_was;
1673     play.gamestep = gamestep_was;
1674     play.room_changes = roomchanges_was;
1675     play.gui_draw_order = gui_draw_order_was;
1676 
1677     restore_game_play_ex_data(in);
1678 }
1679 
ReadMoveList_Aligned(Stream * in)1680 void ReadMoveList_Aligned(Stream *in)
1681 {
1682     AlignedStream align_s(in, Common::kAligned_Read);
1683     for (int i = 0; i < game.numcharacters + MAX_INIT_SPR + 1; ++i)
1684     {
1685         mls[i].ReadFromFile(&align_s);
1686         align_s.Reset();
1687     }
1688 }
1689 
ReadGameSetupStructBase_Aligned(Stream * in)1690 void ReadGameSetupStructBase_Aligned(Stream *in)
1691 {
1692     AlignedStream align_s(in, Common::kAligned_Read);
1693     game.GameSetupStructBase::ReadFromFile(&align_s);
1694 }
1695 
ReadCharacterExtras_Aligned(Stream * in)1696 void ReadCharacterExtras_Aligned(Stream *in)
1697 {
1698     AlignedStream align_s(in, Common::kAligned_Read);
1699     for (int i = 0; i < game.numcharacters; ++i)
1700     {
1701         charextra[i].ReadFromFile(&align_s);
1702         align_s.Reset();
1703     }
1704 }
1705 
restore_game_palette(Stream * in)1706 void restore_game_palette(Stream *in)
1707 {
1708     in->ReadArray(&palette[0],sizeof(color),256);
1709 }
1710 
restore_game_dialogs(Stream * in)1711 void restore_game_dialogs(Stream *in)
1712 {
1713     for (int vv=0;vv<game.numdialog;vv++)
1714         in->ReadArrayOfInt32(&dialog[vv].optionflags[0],MAXTOPICOPTIONS);
1715 }
1716 
restore_game_more_dynamic_values(Stream * in)1717 void restore_game_more_dynamic_values(Stream *in)
1718 {
1719     mouse_on_iface=in->ReadInt32();
1720     mouse_on_iface_button=in->ReadInt32();
1721     mouse_pushed_iface=in->ReadInt32();
1722     ifacepopped = in->ReadInt32();
1723     game_paused=in->ReadInt32();
1724 }
1725 
ReadAnimatedButtons_Aligned(Stream * in)1726 void ReadAnimatedButtons_Aligned(Stream *in)
1727 {
1728     AlignedStream align_s(in, Common::kAligned_Read);
1729     for (int i = 0; i < numAnimButs; ++i)
1730     {
1731         animbuts[i].ReadFromFile(&align_s);
1732         align_s.Reset();
1733     }
1734 }
1735 
restore_game_gui(Stream * in,int numGuisWas)1736 SavegameError restore_game_gui(Stream *in, int numGuisWas)
1737 {
1738     read_gui(in,guis,&game);
1739 
1740     if (numGuisWas != game.numgui)
1741     {
1742         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of GUI");
1743         return kSvgErr_GameContentAssertion;
1744     }
1745 
1746     numAnimButs = in->ReadInt32();
1747     ReadAnimatedButtons_Aligned(in);
1748     return kSvgErr_NoError;
1749 }
1750 
restore_game_audiocliptypes(Stream * in)1751 SavegameError restore_game_audiocliptypes(Stream *in)
1752 {
1753     if (in->ReadInt32() != game.audioClipTypeCount)
1754     {
1755         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of Audio Clip Types");
1756         return kSvgErr_GameContentAssertion;
1757     }
1758 
1759     for (int i = 0; i < game.audioClipTypeCount; ++i)
1760     {
1761         game.audioClipTypes[i].ReadFromFile(in);
1762     }
1763     return kSvgErr_NoError;
1764 }
1765 
restore_game_thisroom(Stream * in,RestoredData & r_data)1766 void restore_game_thisroom(Stream *in, RestoredData &r_data)
1767 {
1768     in->ReadArrayOfInt16(r_data.RoomLightLevels, MAX_REGIONS);
1769     in->ReadArrayOfInt32(r_data.RoomTintLevels, MAX_REGIONS);
1770     in->ReadArrayOfInt16(r_data.RoomZoomLevels1, MAX_WALK_AREAS + 1);
1771     in->ReadArrayOfInt16(r_data.RoomZoomLevels2, MAX_WALK_AREAS + 1);
1772 }
1773 
restore_game_ambientsounds(Stream * in,RestoredData & r_data)1774 void restore_game_ambientsounds(Stream *in, RestoredData &r_data)
1775 {
1776     int bb;
1777 
1778     for (int i = 0; i < MAX_SOUND_CHANNELS; ++i)
1779     {
1780         ambient[i].ReadFromFile(in);
1781     }
1782 
1783     for (bb = 1; bb < MAX_SOUND_CHANNELS; bb++) {
1784         if (ambient[bb].channel == 0)
1785             r_data.DoAmbient[bb] = 0;
1786         else {
1787             r_data.DoAmbient[bb] = ambient[bb].num;
1788             ambient[bb].channel = 0;
1789         }
1790     }
1791 }
1792 
ReadOverlays_Aligned(Stream * in)1793 void ReadOverlays_Aligned(Stream *in)
1794 {
1795     AlignedStream align_s(in, Common::kAligned_Read);
1796     for (int i = 0; i < numscreenover; ++i)
1797     {
1798         screenover[i].ReadFromFile(&align_s);
1799         align_s.Reset();
1800     }
1801 }
1802 
restore_game_overlays(Stream * in)1803 void restore_game_overlays(Stream *in)
1804 {
1805     numscreenover = in->ReadInt32();
1806     ReadOverlays_Aligned(in);
1807     for (int bb=0;bb<numscreenover;bb++) {
1808         if (screenover[bb].hasSerializedBitmap)
1809             screenover[bb].pic = read_serialized_bitmap(in);
1810     }
1811 }
1812 
restore_game_dynamic_surfaces(Stream * in,RestoredData & r_data)1813 void restore_game_dynamic_surfaces(Stream *in, RestoredData &r_data)
1814 {
1815     // load into a temp array since ccUnserialiseObjects will destroy
1816     // it otherwise
1817     r_data.DynamicSurfaces.resize(MAX_DYNAMIC_SURFACES);
1818     for (int i = 0; i < MAX_DYNAMIC_SURFACES; ++i)
1819     {
1820         if (in->ReadInt8() == 0)
1821         {
1822             r_data.DynamicSurfaces[i] = NULL;
1823         }
1824         else
1825         {
1826             r_data.DynamicSurfaces[i] = read_serialized_bitmap(in);
1827         }
1828     }
1829 }
1830 
restore_game_displayed_room_status(Stream * in,RestoredData & r_data)1831 void restore_game_displayed_room_status(Stream *in, RestoredData &r_data)
1832 {
1833     int bb;
1834     for (bb = 0; bb < MAX_BSCENE; bb++)
1835         r_data.RoomBkgScene[bb] = NULL;
1836 
1837     if (displayed_room >= 0) {
1838 
1839         for (bb = 0; bb < MAX_BSCENE; bb++) {
1840             r_data.RoomBkgScene[bb] = NULL;
1841             if (play.raw_modified[bb]) {
1842                 r_data.RoomBkgScene[bb] = read_serialized_bitmap (in);
1843             }
1844         }
1845         bb = in->ReadInt32();
1846 
1847         if (bb)
1848             raw_saved_screen = read_serialized_bitmap(in);
1849 
1850         // get the current troom, in case they save in room 600 or whatever
1851         ReadRoomStatus_Aligned(&troom, in);
1852 
1853         if (troom.tsdatasize > 0) {
1854             troom.tsdata=(char*)malloc(troom.tsdatasize+5);
1855             in->Read(&troom.tsdata[0],troom.tsdatasize);
1856         }
1857         else
1858             troom.tsdata = NULL;
1859     }
1860 }
1861 
restore_game_globalvars(Stream * in)1862 SavegameError restore_game_globalvars(Stream *in)
1863 {
1864     if (in->ReadInt32() != numGlobalVars)
1865     {
1866         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of Global Variables");
1867         return kSvgErr_GameContentAssertion;
1868     }
1869 
1870     for (int i = 0; i < numGlobalVars; ++i)
1871     {
1872         globalvars[i].Read(in);
1873     }
1874     return kSvgErr_NoError;
1875 }
1876 
restore_game_views(Stream * in)1877 SavegameError restore_game_views(Stream *in)
1878 {
1879     if (in->ReadInt32() != game.numviews)
1880     {
1881         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of Views");
1882         return kSvgErr_GameContentAssertion;
1883     }
1884 
1885     for (int bb = 0; bb < game.numviews; bb++) {
1886         for (int cc = 0; cc < views[bb].numLoops; cc++) {
1887             for (int dd = 0; dd < views[bb].loops[cc].numFrames; dd++)
1888             {
1889                 views[bb].loops[cc].frames[dd].sound = in->ReadInt32();
1890                 views[bb].loops[cc].frames[dd].pic = in->ReadInt32();
1891             }
1892         }
1893     }
1894     return kSvgErr_NoError;
1895 }
1896 
restore_game_audioclips_and_crossfade(Stream * in,RestoredData & r_data)1897 SavegameError restore_game_audioclips_and_crossfade(Stream *in, RestoredData &r_data)
1898 {
1899     if (in->ReadInt32() != game.audioClipCount)
1900     {
1901         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of Audio Clips");
1902         return kSvgErr_GameContentAssertion;
1903     }
1904 
1905     for (int i = 0; i <= MAX_SOUND_CHANNELS; ++i)
1906     {
1907         RestoredData::ChannelInfo &chan_info = r_data.AudioChans[i];
1908         chan_info.Pos = 0;
1909         chan_info.ClipID = in->ReadInt32();
1910         if (chan_info.ClipID >= 0)
1911         {
1912             if (chan_info.ClipID >= game.audioClipCount)
1913             {
1914                 Debug::Printf(kDbgMsg_Error, "Restore game error: invalid audio clip index");
1915                 return kSvgErr_GameObjectInitFailed;
1916             }
1917 
1918             chan_info.Pos = in->ReadInt32();
1919             if (chan_info.Pos < 0)
1920                 chan_info.Pos = 0;
1921             chan_info.Priority = in->ReadInt32();
1922             chan_info.Repeat = in->ReadInt32();
1923             chan_info.Vol = in->ReadInt32();
1924             chan_info.Pan = in->ReadInt32();
1925             chan_info.VolAsPercent = in->ReadInt32();
1926             chan_info.PanAsPercent = in->ReadInt32();
1927             chan_info.Speed = 1000;
1928             if (loaded_game_file_version >= kGameVersion_340_2)
1929                 chan_info.Speed = in->ReadInt32();
1930         }
1931     }
1932     crossFading = in->ReadInt32();
1933     crossFadeVolumePerStep = in->ReadInt32();
1934     crossFadeStep = in->ReadInt32();
1935     crossFadeVolumeAtStart = in->ReadInt32();
1936     return kSvgErr_NoError;
1937 }
1938 
restore_game_data(Stream * in,SavegameVersion svg_version,const PreservedParams & pp,RestoredData & r_data)1939 SavegameError restore_game_data(Stream *in, SavegameVersion svg_version, const PreservedParams &pp, RestoredData &r_data)
1940 {
1941     int vv;
1942 
1943     SavegameError err = restore_game_head_dynamic_values(in, r_data);
1944     if (err != kSvgErr_NoError)
1945         return err;
1946     restore_game_spriteset(in);
1947 
1948     update_polled_stuff_if_runtime();
1949 
1950     err = restore_game_scripts(in, pp, r_data);
1951     if (err != kSvgErr_NoError)
1952         return err;
1953     restore_game_room_state(in);
1954     restore_game_play(in);
1955     ReadMoveList_Aligned(in);
1956 
1957     // save pointer members before reading
1958     char* gswas=game.globalscript;
1959     ccScript* compsc=game.compiled_script;
1960     CharacterInfo* chwas=game.chars;
1961     WordsDictionary *olddict = game.dict;
1962     char* mesbk[MAXGLOBALMES];
1963     int numchwas = game.numcharacters;
1964     for (vv=0;vv<MAXGLOBALMES;vv++) mesbk[vv]=game.messages[vv];
1965     int numdiwas = game.numdialog;
1966     int numinvwas = game.numinvitems;
1967     int numviewswas = game.numviews;
1968     int numGuisWas = game.numgui;
1969 
1970     ReadGameSetupStructBase_Aligned(in);
1971 
1972     // Delete unneeded data
1973     // TODO: reorganize this (may be solved by optimizing safe format too)
1974     delete [] game.load_messages;
1975     game.load_messages = NULL;
1976 
1977     if (game.numdialog!=numdiwas)
1978     {
1979         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of Dialogs");
1980         return kSvgErr_GameContentAssertion;
1981     }
1982     if (numchwas != game.numcharacters)
1983     {
1984         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of Characters");
1985         return kSvgErr_GameContentAssertion;
1986     }
1987     if (numinvwas != game.numinvitems)
1988     {
1989         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of Inventory Items");
1990         return kSvgErr_GameContentAssertion;
1991     }
1992     if (game.numviews != numviewswas)
1993     {
1994         Debug::Printf(kDbgMsg_Error, "Restore game error: mismatching number of Views");
1995         return kSvgErr_GameContentAssertion;
1996     }
1997 
1998     game.ReadFromSaveGame_v321(in, gswas, compsc, chwas, olddict, mesbk);
1999 
2000     // Modified custom properties are read separately to keep existing save format
2001     play.ReadCustomProperties(in);
2002 
2003     ReadCharacterExtras_Aligned(in);
2004     restore_game_palette(in);
2005     restore_game_dialogs(in);
2006     restore_game_more_dynamic_values(in);
2007     err = restore_game_gui(in, numGuisWas);
2008     if (err != kSvgErr_NoError)
2009         return err;
2010     err = restore_game_audiocliptypes(in);
2011     if (err != kSvgErr_NoError)
2012         return err;
2013     restore_game_thisroom(in, r_data);
2014     restore_game_ambientsounds(in, r_data);
2015     restore_game_overlays(in);
2016 
2017     update_polled_stuff_if_runtime();
2018 
2019     restore_game_dynamic_surfaces(in, r_data);
2020 
2021     update_polled_stuff_if_runtime();
2022 
2023     restore_game_displayed_room_status(in, r_data);
2024     err = restore_game_globalvars(in);
2025     if (err != kSvgErr_NoError)
2026         return err;
2027     err = restore_game_views(in);
2028     if (err != kSvgErr_NoError)
2029         return err;
2030 
2031     if (in->ReadInt32() != MAGICNUMBER+1)
2032     {
2033         Debug::Printf(kDbgMsg_Error, "Restore game error: MAGICNUMBER not found before Audio Clips");
2034         return kSvgErr_InconsistentFormat;
2035     }
2036 
2037     err = restore_game_audioclips_and_crossfade(in, r_data);
2038     if (err != kSvgErr_NoError)
2039         return err;
2040 
2041     int pluginFileHandle = AGSE_RESTOREGAME;
2042     pl_set_file_handle(pluginFileHandle, in);
2043     pl_run_plugin_hooks(AGSE_RESTOREGAME, pluginFileHandle);
2044     pl_clear_file_handle();
2045     if (in->ReadInt32() != (unsigned)MAGICNUMBER)
2046         return kSvgErr_InconsistentPlugin;
2047 
2048     // save the new room music vol for later use
2049     r_data.RoomVolume = in->ReadInt32();
2050 
2051     if (ccUnserializeAllObjects(in, &ccUnserializer))
2052     {
2053         Debug::Printf(kDbgMsg_Error, "Restore game error: managed pool deserialization failed: %s", ccErrorString);
2054         return kSvgErr_GameObjectInitFailed;
2055     }
2056 
2057     // preserve legacy music type setting
2058     current_music_type = in->ReadInt32();
2059 
2060     return kSvgErr_NoError;
2061 }
2062 
2063 int gameHasBeenRestored = 0;
2064 int oldeip;
2065 
read_savedgame_description(const String & savedgame,String & description)2066 bool read_savedgame_description(const String &savedgame, String &description)
2067 {
2068     SavegameDescription desc;
2069     SavegameError err = OpenSavegame(savedgame, desc, kSvgDesc_UserText);
2070     if (err == kSvgErr_NoError)
2071     {
2072         description = desc.UserText;
2073         return true;
2074     }
2075     return false;
2076 }
2077 
read_savedgame_screenshot(const String & savedgame,int & want_shot)2078 bool read_savedgame_screenshot(const String &savedgame, int &want_shot)
2079 {
2080     want_shot = 0;
2081 
2082     SavegameDescription desc;
2083     SavegameError err = OpenSavegame(savedgame, desc, kSvgDesc_UserImage);
2084     if (err != kSvgErr_NoError)
2085         return false;
2086 
2087     if (desc.UserImage.get())
2088     {
2089         int slot = spriteset.findFreeSlot();
2090         if (slot > 0)
2091         {
2092             // add it into the sprite set
2093             add_dynamic_sprite(slot, ReplaceBitmapWithSupportedFormat(desc.UserImage.release()));
2094             want_shot = slot;
2095         }
2096     }
2097     return true;
2098 }
2099 
load_game(const String & path,int slotNumber,bool & data_overwritten)2100 SavegameError load_game(const String &path, int slotNumber, bool &data_overwritten)
2101 {
2102     data_overwritten = false;
2103     gameHasBeenRestored++;
2104 
2105     oldeip = our_eip;
2106     our_eip = 2050;
2107 
2108     SavegameError err;
2109     SavegameSource src;
2110     SavegameDescription desc;
2111     err = OpenSavegame(path, src, desc, kSvgDesc_EnvInfo);
2112 
2113     our_eip = 2051;
2114 
2115     // saved in incompatible enviroment
2116     if (err != kSvgErr_NoError)
2117         return err;
2118     // CHECKME: is this color depth test still essential? if yes, is there possible workaround?
2119     else if (desc.ColorDepth != game.GetColorDepth())
2120         return kSvgErr_DifferentColorDepth;
2121     else if (!src.InputStream.get())
2122         return kSvgErr_NoStream;
2123 
2124     // saved with different game file
2125     if (Path::ComparePaths(desc.MainDataFilename, usetup.main_data_filename))
2126     {
2127         // [IKM] 2012-11-26: this is a workaround, indeed.
2128         // Try to find wanted game's executable; if it does not exist,
2129         // continue loading savedgame in current game, and pray for the best
2130         get_install_dir_path(gamefilenamebuf, desc.MainDataFilename);
2131         if (Common::File::TestReadFile(gamefilenamebuf))
2132         {
2133             RunAGSGame (desc.MainDataFilename, 0, 0);
2134             load_new_game_restore = slotNumber;
2135             return kSvgErr_NoError;
2136         }
2137         Common::Debug::Printf(kDbgMsg_Warn, "WARNING: the saved game '%s' references game file '%s', but it cannot be found in the current directory. Trying to restore in the running game instead.",
2138             path.GetCStr(), desc.MainDataFilename.GetCStr());
2139     }
2140 
2141     // do the actual restore
2142     err = RestoreGameState(src.InputStream.get(), src.Version);
2143     data_overwritten = true;
2144     if (err != kSvgErr_NoError)
2145         return err;
2146     src.InputStream.reset();
2147     our_eip = oldeip;
2148 
2149     // ensure keyboard buffer is clean
2150     // use the raw versions rather than the rec_ versions so we don't
2151     // interfere with the replay sync
2152     while (keypressed()) readkey();
2153     // call "After Restore" event callback
2154     run_on_event(GE_RESTORE_GAME, RuntimeScriptValue().SetInt32(slotNumber));
2155     return kSvgErr_NoError;
2156 }
2157 
try_restore_save(int slot)2158 bool try_restore_save(int slot)
2159 {
2160     return try_restore_save(get_save_game_path(slot), slot);
2161 }
2162 
try_restore_save(const Common::String & path,int slot)2163 bool try_restore_save(const Common::String &path, int slot)
2164 {
2165     bool data_overwritten;
2166     SavegameError err = load_game(path, slot, data_overwritten);
2167     if (err != kSvgErr_NoError)
2168     {
2169         String error = String::FromFormat("Unable to restore game:\n%s", GetSavegameErrorText(err).GetCStr());
2170         // currently AGS cannot properly revert to stable state if some of the
2171         // game data was released or overwritten by the data from save file,
2172         // this is why we tell engine to shutdown if that happened.
2173         if (data_overwritten)
2174             quitprintf(error);
2175         else
2176             Display(error);
2177         return false;
2178     }
2179     return true;
2180 }
2181 
start_skipping_cutscene()2182 void start_skipping_cutscene () {
2183     play.fast_forward = 1;
2184     // if a drop-down icon bar is up, remove it as it will pause the game
2185     if (ifacepopped>=0)
2186         remove_popup_interface(ifacepopped);
2187 
2188     // if a text message is currently displayed, remove it
2189     if (is_text_overlay > 0)
2190         remove_screen_overlay(OVER_TEXTMSG);
2191 
2192 }
2193 
check_skip_cutscene_keypress(int kgn)2194 void check_skip_cutscene_keypress (int kgn) {
2195 
2196     if ((play.in_cutscene > 0) && (play.in_cutscene != 3)) {
2197         if ((kgn != 27) && ((play.in_cutscene == 1) || (play.in_cutscene == 5)))
2198             ;
2199         else
2200             start_skipping_cutscene();
2201     }
2202 
2203 }
2204 
2205 // Helper functions used by StartCutscene/EndCutscene, but also
2206 // by SkipUntilCharacterStops
initialize_skippable_cutscene()2207 void initialize_skippable_cutscene() {
2208     play.end_cutscene_music = -1;
2209 }
2210 
stop_fast_forwarding()2211 void stop_fast_forwarding() {
2212     // when the skipping of a cutscene comes to an end, update things
2213     play.fast_forward = 0;
2214     setpal();
2215     if (play.end_cutscene_music >= 0)
2216         newmusic(play.end_cutscene_music);
2217 
2218     // Restore actual volume of sounds
2219     for (int aa = 0; aa < MAX_SOUND_CHANNELS; aa++)
2220     {
2221         if ((channels[aa] != NULL) && (!channels[aa]->done))
2222         {
2223             channels[aa]->set_mute(false);
2224         }
2225     }
2226 
2227     update_music_volume();
2228 }
2229 
2230 // allowHotspot0 defines whether Hotspot 0 returns LOCTYPE_HOTSPOT
2231 // or whether it returns 0
__GetLocationType(int xxx,int yyy,int allowHotspot0)2232 int __GetLocationType(int xxx,int yyy, int allowHotspot0) {
2233     getloctype_index = 0;
2234     // If it's not in ProcessClick, then return 0 when over a GUI
2235     if ((GetGUIAt(xxx, yyy) >= 0) && (getloctype_throughgui == 0))
2236         return 0;
2237 
2238     getloctype_throughgui = 0;
2239 
2240     xxx += divide_down_coordinate(offsetx);
2241     yyy += divide_down_coordinate(offsety);
2242     if ((xxx>=thisroom.width) | (xxx<0) | (yyy<0) | (yyy>=thisroom.height))
2243         return 0;
2244 
2245     // check characters, objects and walkbehinds, work out which is
2246     // foremost visible to the player
2247     int charat = is_pos_on_character(xxx,yyy);
2248     int hsat = get_hotspot_at(xxx,yyy);
2249     int objat = GetObjectAt(xxx - divide_down_coordinate(offsetx), yyy - divide_down_coordinate(offsety));
2250 
2251     multiply_up_coordinates(&xxx, &yyy);
2252 
2253     int wbat = thisroom.object->GetPixel(xxx, yyy);
2254 
2255     if (wbat <= 0) wbat = 0;
2256     else wbat = croom->walkbehind_base[wbat];
2257 
2258     int winner = 0;
2259     // if it's an Ignore Walkbehinds object, then ignore the walkbehind
2260     if ((objat >= 0) && ((objs[objat].flags & OBJF_NOWALKBEHINDS) != 0))
2261         wbat = 0;
2262     if ((charat >= 0) && ((game.chars[charat].flags & CHF_NOWALKBEHINDS) != 0))
2263         wbat = 0;
2264 
2265     if ((charat >= 0) && (objat >= 0)) {
2266         if ((wbat > obj_lowest_yp) && (wbat > char_lowest_yp))
2267             winner = LOCTYPE_HOTSPOT;
2268         else if (obj_lowest_yp > char_lowest_yp)
2269             winner = LOCTYPE_OBJ;
2270         else
2271             winner = LOCTYPE_CHAR;
2272     }
2273     else if (charat >= 0) {
2274         if (wbat > char_lowest_yp)
2275             winner = LOCTYPE_HOTSPOT;
2276         else
2277             winner = LOCTYPE_CHAR;
2278     }
2279     else if (objat >= 0) {
2280         if (wbat > obj_lowest_yp)
2281             winner = LOCTYPE_HOTSPOT;
2282         else
2283             winner = LOCTYPE_OBJ;
2284     }
2285 
2286     if (winner == 0) {
2287         if (hsat >= 0)
2288             winner = LOCTYPE_HOTSPOT;
2289     }
2290 
2291     if ((winner == LOCTYPE_HOTSPOT) && (!allowHotspot0) && (hsat == 0))
2292         winner = 0;
2293 
2294     if (winner == LOCTYPE_HOTSPOT)
2295         getloctype_index = hsat;
2296     else if (winner == LOCTYPE_CHAR)
2297         getloctype_index = charat;
2298     else if (winner == LOCTYPE_OBJ)
2299         getloctype_index = objat;
2300 
2301     return winner;
2302 }
2303 
2304 // Called whenever game looses input focus
display_switch_out()2305 void display_switch_out()
2306 {
2307     switched_away = true;
2308     clear_input_buffer();
2309     // Always unlock mouse when switching out from the game
2310     Mouse::UnlockFromWindow();
2311     platform->DisplaySwitchOut();
2312     platform->ExitFullscreenMode();
2313 }
2314 
display_switch_out_suspend()2315 void display_switch_out_suspend()
2316 {
2317     // this is only called if in SWITCH_PAUSE mode
2318     //debug_script_warn("display_switch_out");
2319     display_switch_out();
2320 
2321     switching_away_from_game++;
2322 
2323     platform->PauseApplication();
2324 
2325     // allow background running temporarily to halt the sound
2326     if (set_display_switch_mode(SWITCH_BACKGROUND) == -1)
2327         set_display_switch_mode(SWITCH_BACKAMNESIA);
2328 
2329     // stop the sound stuttering
2330     for (int i = 0; i <= MAX_SOUND_CHANNELS; i++) {
2331         if ((channels[i] != NULL) && (channels[i]->done == 0)) {
2332             channels[i]->pause();
2333         }
2334     }
2335 
2336     rest(1000);
2337 
2338     // restore the callbacks
2339     SetMultitasking(0);
2340 
2341     switching_away_from_game--;
2342 }
2343 
2344 // Called whenever game gets input focus
display_switch_in()2345 void display_switch_in()
2346 {
2347     switched_away = false;
2348     if (gfxDriver)
2349     {
2350         DisplayMode mode = gfxDriver->GetDisplayMode();
2351         if (!mode.Windowed)
2352             platform->EnterFullscreenMode(mode);
2353     }
2354     platform->DisplaySwitchIn();
2355     clear_input_buffer();
2356     // If auto lock option is set, lock mouse to the game window
2357     if (usetup.mouse_auto_lock && scsystem.windowed)
2358         Mouse::TryLockToWindow();
2359 }
2360 
display_switch_in_resume()2361 void display_switch_in_resume()
2362 {
2363     display_switch_in();
2364 
2365     for (int i = 0; i <= MAX_SOUND_CHANNELS; i++) {
2366         if ((channels[i] != NULL) && (channels[i]->done == 0)) {
2367             channels[i]->resume();
2368         }
2369     }
2370 
2371     // clear the screen if necessary
2372     if (gfxDriver && gfxDriver->UsesMemoryBackBuffer())
2373         gfxDriver->ClearRectangle(0, 0, game.size.Width - 1, game.size.Height - 1, NULL);
2374 
2375     platform->ResumeApplication();
2376 }
2377 
replace_tokens(const char * srcmes,char * destm,int maxlen)2378 void replace_tokens(const char*srcmes,char*destm, int maxlen) {
2379     int indxdest=0,indxsrc=0;
2380     const char*srcp;
2381     char *destp;
2382     while (srcmes[indxsrc]!=0) {
2383         srcp=&srcmes[indxsrc];
2384         destp=&destm[indxdest];
2385         if ((strncmp(srcp,"@IN",3)==0) | (strncmp(srcp,"@GI",3)==0)) {
2386             int tokentype=0;
2387             if (srcp[1]=='I') tokentype=1;
2388             else tokentype=2;
2389             int inx=atoi(&srcp[3]);
2390             srcp++;
2391             indxsrc+=2;
2392             while (srcp[0]!='@') {
2393                 if (srcp[0]==0) quit("!Display: special token not terminated");
2394                 srcp++;
2395                 indxsrc++;
2396             }
2397             char tval[10];
2398             if (tokentype==1) {
2399                 if ((inx<1) | (inx>=game.numinvitems))
2400                     quit("!Display: invalid inv item specified in @IN@");
2401                 sprintf(tval,"%d",playerchar->inv[inx]);
2402             }
2403             else {
2404                 if ((inx<0) | (inx>=MAXGSVALUES))
2405                     quit("!Display: invalid global int index speicifed in @GI@");
2406                 sprintf(tval,"%d",GetGlobalInt(inx));
2407             }
2408             strcpy(destp,tval);
2409             indxdest+=strlen(tval);
2410         }
2411         else {
2412             destp[0]=srcp[0];
2413             indxdest++;
2414             indxsrc++;
2415         }
2416         if (indxdest >= maxlen - 3)
2417             break;
2418     }
2419     destm[indxdest]=0;
2420 }
2421 
get_global_message(int msnum)2422 const char *get_global_message (int msnum) {
2423     if (game.messages[msnum-500] == NULL)
2424         return "";
2425     return get_translation(game.messages[msnum-500]);
2426 }
2427 
get_message_text(int msnum,char * buffer,char giveErr)2428 void get_message_text (int msnum, char *buffer, char giveErr) {
2429     int maxlen = 9999;
2430     if (!giveErr)
2431         maxlen = MAX_MAXSTRLEN;
2432 
2433     if (msnum>=500) { //quit("global message requseted, nto yet supported");
2434 
2435         if ((msnum >= MAXGLOBALMES + 500) || (game.messages[msnum-500]==NULL)) {
2436             if (giveErr)
2437                 quit("!DisplayGlobalMessage: message does not exist");
2438             buffer[0] = 0;
2439             return;
2440         }
2441         buffer[0] = 0;
2442         replace_tokens(get_translation(game.messages[msnum-500]), buffer, maxlen);
2443         return;
2444     }
2445     else if (msnum >= thisroom.nummes) {
2446         if (giveErr)
2447             quit("!DisplayMessage: Invalid message number to display");
2448         buffer[0] = 0;
2449         return;
2450     }
2451 
2452     buffer[0]=0;
2453     replace_tokens(get_translation(thisroom.message[msnum]), buffer, maxlen);
2454 }
2455 
unserialize_audio_script_object(int index,const char * objectType,const char * serializedData,int dataSize)2456 bool unserialize_audio_script_object(int index, const char *objectType, const char *serializedData, int dataSize)
2457 {
2458     if (strcmp(objectType, "AudioChannel") == 0)
2459     {
2460         ccDynamicAudio.Unserialize(index, serializedData, dataSize);
2461     }
2462     else if (strcmp(objectType, "AudioClip") == 0)
2463     {
2464         ccDynamicAudioClip.Unserialize(index, serializedData, dataSize);
2465     }
2466     else
2467     {
2468         return false;
2469     }
2470     return true;
2471 }
2472 
2473 //=============================================================================
2474 //
2475 // Script API Functions
2476 //
2477 //=============================================================================
2478 
2479 #include "debug/out.h"
2480 #include "script/script_api.h"
2481 #include "script/script_runtime.h"
2482 
2483 // int  (int audioType);
Sc_Game_IsAudioPlaying(const RuntimeScriptValue * params,int32_t param_count)2484 RuntimeScriptValue Sc_Game_IsAudioPlaying(const RuntimeScriptValue *params, int32_t param_count)
2485 {
2486     API_SCALL_INT_PINT(Game_IsAudioPlaying);
2487 }
2488 
2489 // void (int audioType, int volumeDrop)
Sc_Game_SetAudioTypeSpeechVolumeDrop(const RuntimeScriptValue * params,int32_t param_count)2490 RuntimeScriptValue Sc_Game_SetAudioTypeSpeechVolumeDrop(const RuntimeScriptValue *params, int32_t param_count)
2491 {
2492     API_SCALL_VOID_PINT2(Game_SetAudioTypeSpeechVolumeDrop);
2493 }
2494 
2495 // void (int audioType, int volume, int changeType)
Sc_Game_SetAudioTypeVolume(const RuntimeScriptValue * params,int32_t param_count)2496 RuntimeScriptValue Sc_Game_SetAudioTypeVolume(const RuntimeScriptValue *params, int32_t param_count)
2497 {
2498     API_SCALL_VOID_PINT3(Game_SetAudioTypeVolume);
2499 }
2500 
2501 // void (int audioType)
Sc_Game_StopAudio(const RuntimeScriptValue * params,int32_t param_count)2502 RuntimeScriptValue Sc_Game_StopAudio(const RuntimeScriptValue *params, int32_t param_count)
2503 {
2504     API_SCALL_VOID_PINT(Game_StopAudio);
2505 }
2506 
2507 // int (const char *newFilename)
Sc_Game_ChangeTranslation(const RuntimeScriptValue * params,int32_t param_count)2508 RuntimeScriptValue Sc_Game_ChangeTranslation(const RuntimeScriptValue *params, int32_t param_count)
2509 {
2510     API_SCALL_INT_POBJ(Game_ChangeTranslation, const char);
2511 }
2512 
2513 // int (const char *token)
Sc_Game_DoOnceOnly(const RuntimeScriptValue * params,int32_t param_count)2514 RuntimeScriptValue Sc_Game_DoOnceOnly(const RuntimeScriptValue *params, int32_t param_count)
2515 {
2516     API_SCALL_INT_POBJ(Game_DoOnceOnly, const char);
2517 }
2518 
2519 // int (int red, int grn, int blu)
Sc_Game_GetColorFromRGB(const RuntimeScriptValue * params,int32_t param_count)2520 RuntimeScriptValue Sc_Game_GetColorFromRGB(const RuntimeScriptValue *params, int32_t param_count)
2521 {
2522     API_SCALL_INT_PINT3(Game_GetColorFromRGB);
2523 }
2524 
2525 // int (int viewNumber, int loopNumber)
Sc_Game_GetFrameCountForLoop(const RuntimeScriptValue * params,int32_t param_count)2526 RuntimeScriptValue Sc_Game_GetFrameCountForLoop(const RuntimeScriptValue *params, int32_t param_count)
2527 {
2528     API_SCALL_INT_PINT2(Game_GetFrameCountForLoop);
2529 }
2530 
2531 // const char* (int x, int y)
Sc_Game_GetLocationName(const RuntimeScriptValue * params,int32_t param_count)2532 RuntimeScriptValue Sc_Game_GetLocationName(const RuntimeScriptValue *params, int32_t param_count)
2533 {
2534     API_SCALL_OBJ_PINT2(const char, myScriptStringImpl, Game_GetLocationName);
2535 }
2536 
2537 // int (int viewNumber)
Sc_Game_GetLoopCountForView(const RuntimeScriptValue * params,int32_t param_count)2538 RuntimeScriptValue Sc_Game_GetLoopCountForView(const RuntimeScriptValue *params, int32_t param_count)
2539 {
2540     API_SCALL_INT_PINT(Game_GetLoopCountForView);
2541 }
2542 
2543 // int ()
Sc_Game_GetMODPattern(const RuntimeScriptValue * params,int32_t param_count)2544 RuntimeScriptValue Sc_Game_GetMODPattern(const RuntimeScriptValue *params, int32_t param_count)
2545 {
2546     API_SCALL_INT(Game_GetMODPattern);
2547 }
2548 
2549 // int (int viewNumber, int loopNumber)
Sc_Game_GetRunNextSettingForLoop(const RuntimeScriptValue * params,int32_t param_count)2550 RuntimeScriptValue Sc_Game_GetRunNextSettingForLoop(const RuntimeScriptValue *params, int32_t param_count)
2551 {
2552     API_SCALL_INT_PINT2(Game_GetRunNextSettingForLoop);
2553 }
2554 
2555 // const char* (int slnum)
Sc_Game_GetSaveSlotDescription(const RuntimeScriptValue * params,int32_t param_count)2556 RuntimeScriptValue Sc_Game_GetSaveSlotDescription(const RuntimeScriptValue *params, int32_t param_count)
2557 {
2558     API_SCALL_OBJ_PINT(const char, myScriptStringImpl, Game_GetSaveSlotDescription);
2559 }
2560 
2561 // ScriptViewFrame* (int viewNumber, int loopNumber, int frame)
Sc_Game_GetViewFrame(const RuntimeScriptValue * params,int32_t param_count)2562 RuntimeScriptValue Sc_Game_GetViewFrame(const RuntimeScriptValue *params, int32_t param_count)
2563 {
2564     API_SCALL_OBJAUTO_PINT3(ScriptViewFrame, Game_GetViewFrame);
2565 }
2566 
2567 // const char* (const char *msg)
Sc_Game_InputBox(const RuntimeScriptValue * params,int32_t param_count)2568 RuntimeScriptValue Sc_Game_InputBox(const RuntimeScriptValue *params, int32_t param_count)
2569 {
2570     API_SCALL_OBJ_POBJ(const char, myScriptStringImpl, Game_InputBox, const char);
2571 }
2572 
2573 // int (const char *newFolder)
Sc_Game_SetSaveGameDirectory(const RuntimeScriptValue * params,int32_t param_count)2574 RuntimeScriptValue Sc_Game_SetSaveGameDirectory(const RuntimeScriptValue *params, int32_t param_count)
2575 {
2576     API_SCALL_INT_POBJ(Game_SetSaveGameDirectory, const char);
2577 }
2578 
2579 // void (int evenAmbient);
Sc_StopAllSounds(const RuntimeScriptValue * params,int32_t param_count)2580 RuntimeScriptValue Sc_StopAllSounds(const RuntimeScriptValue *params, int32_t param_count)
2581 {
2582     API_SCALL_VOID_PINT(StopAllSounds);
2583 }
2584 
2585 // int ()
Sc_Game_GetCharacterCount(const RuntimeScriptValue * params,int32_t param_count)2586 RuntimeScriptValue Sc_Game_GetCharacterCount(const RuntimeScriptValue *params, int32_t param_count)
2587 {
2588     API_SCALL_INT(Game_GetCharacterCount);
2589 }
2590 
2591 // int ()
Sc_Game_GetDialogCount(const RuntimeScriptValue * params,int32_t param_count)2592 RuntimeScriptValue Sc_Game_GetDialogCount(const RuntimeScriptValue *params, int32_t param_count)
2593 {
2594     API_SCALL_INT(Game_GetDialogCount);
2595 }
2596 
2597 // const char *()
Sc_Game_GetFileName(const RuntimeScriptValue * params,int32_t param_count)2598 RuntimeScriptValue Sc_Game_GetFileName(const RuntimeScriptValue *params, int32_t param_count)
2599 {
2600     API_SCALL_OBJ(const char, myScriptStringImpl, Game_GetFileName);
2601 }
2602 
2603 // int ()
Sc_Game_GetFontCount(const RuntimeScriptValue * params,int32_t param_count)2604 RuntimeScriptValue Sc_Game_GetFontCount(const RuntimeScriptValue *params, int32_t param_count)
2605 {
2606     API_SCALL_INT(Game_GetFontCount);
2607 }
2608 
2609 // const char* (int index)
Sc_Game_GetGlobalMessages(const RuntimeScriptValue * params,int32_t param_count)2610 RuntimeScriptValue Sc_Game_GetGlobalMessages(const RuntimeScriptValue *params, int32_t param_count)
2611 {
2612     API_SCALL_OBJ_PINT(const char, myScriptStringImpl, Game_GetGlobalMessages);
2613 }
2614 
2615 // const char* (int index)
Sc_Game_GetGlobalStrings(const RuntimeScriptValue * params,int32_t param_count)2616 RuntimeScriptValue Sc_Game_GetGlobalStrings(const RuntimeScriptValue *params, int32_t param_count)
2617 {
2618     API_SCALL_OBJ_PINT(const char, myScriptStringImpl, Game_GetGlobalStrings);
2619 }
2620 
2621 // void  (int index, char *newval);
Sc_SetGlobalString(const RuntimeScriptValue * params,int32_t param_count)2622 RuntimeScriptValue Sc_SetGlobalString(const RuntimeScriptValue *params, int32_t param_count)
2623 {
2624     API_SCALL_VOID_PINT_POBJ(SetGlobalString, const char);
2625 }
2626 
2627 // int ()
Sc_Game_GetGUICount(const RuntimeScriptValue * params,int32_t param_count)2628 RuntimeScriptValue Sc_Game_GetGUICount(const RuntimeScriptValue *params, int32_t param_count)
2629 {
2630     API_SCALL_INT(Game_GetGUICount);
2631 }
2632 
2633 // int ()
Sc_Game_GetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue * params,int32_t param_count)2634 RuntimeScriptValue Sc_Game_GetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue *params, int32_t param_count)
2635 {
2636     API_SCALL_INT(Game_GetIgnoreUserInputAfterTextTimeoutMs);
2637 }
2638 
2639 // void (int newValueMs)
Sc_Game_SetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue * params,int32_t param_count)2640 RuntimeScriptValue Sc_Game_SetIgnoreUserInputAfterTextTimeoutMs(const RuntimeScriptValue *params, int32_t param_count)
2641 {
2642     API_SCALL_VOID_PINT(Game_SetIgnoreUserInputAfterTextTimeoutMs);
2643 }
2644 
2645 // int ()
Sc_Game_GetInSkippableCutscene(const RuntimeScriptValue * params,int32_t param_count)2646 RuntimeScriptValue Sc_Game_GetInSkippableCutscene(const RuntimeScriptValue *params, int32_t param_count)
2647 {
2648     API_SCALL_INT(Game_GetInSkippableCutscene);
2649 }
2650 
2651 // int ()
Sc_Game_GetInventoryItemCount(const RuntimeScriptValue * params,int32_t param_count)2652 RuntimeScriptValue Sc_Game_GetInventoryItemCount(const RuntimeScriptValue *params, int32_t param_count)
2653 {
2654     API_SCALL_INT(Game_GetInventoryItemCount);
2655 }
2656 
2657 // int ()
Sc_Game_GetMinimumTextDisplayTimeMs(const RuntimeScriptValue * params,int32_t param_count)2658 RuntimeScriptValue Sc_Game_GetMinimumTextDisplayTimeMs(const RuntimeScriptValue *params, int32_t param_count)
2659 {
2660     API_SCALL_INT(Game_GetMinimumTextDisplayTimeMs);
2661 }
2662 
2663 // void (int newTextMinTime)
Sc_Game_SetMinimumTextDisplayTimeMs(const RuntimeScriptValue * params,int32_t param_count)2664 RuntimeScriptValue Sc_Game_SetMinimumTextDisplayTimeMs(const RuntimeScriptValue *params, int32_t param_count)
2665 {
2666     API_SCALL_VOID_PINT(Game_SetMinimumTextDisplayTimeMs);
2667 }
2668 
2669 // int ()
Sc_Game_GetMouseCursorCount(const RuntimeScriptValue * params,int32_t param_count)2670 RuntimeScriptValue Sc_Game_GetMouseCursorCount(const RuntimeScriptValue *params, int32_t param_count)
2671 {
2672     API_SCALL_INT(Game_GetMouseCursorCount);
2673 }
2674 
2675 // const char *()
Sc_Game_GetName(const RuntimeScriptValue * params,int32_t param_count)2676 RuntimeScriptValue Sc_Game_GetName(const RuntimeScriptValue *params, int32_t param_count)
2677 {
2678     API_SCALL_OBJ(const char, myScriptStringImpl, Game_GetName);
2679 }
2680 
2681 // void (const char *newName)
Sc_Game_SetName(const RuntimeScriptValue * params,int32_t param_count)2682 RuntimeScriptValue Sc_Game_SetName(const RuntimeScriptValue *params, int32_t param_count)
2683 {
2684     API_SCALL_VOID_POBJ(Game_SetName, const char);
2685 }
2686 
2687 // int ()
Sc_Game_GetNormalFont(const RuntimeScriptValue * params,int32_t param_count)2688 RuntimeScriptValue Sc_Game_GetNormalFont(const RuntimeScriptValue *params, int32_t param_count)
2689 {
2690     API_SCALL_INT(Game_GetNormalFont);
2691 }
2692 
2693 // void  (int fontnum);
Sc_SetNormalFont(const RuntimeScriptValue * params,int32_t param_count)2694 RuntimeScriptValue Sc_SetNormalFont(const RuntimeScriptValue *params, int32_t param_count)
2695 {
2696     API_SCALL_VOID_PINT(SetNormalFont);
2697 }
2698 
2699 // int ()
Sc_Game_GetSkippingCutscene(const RuntimeScriptValue * params,int32_t param_count)2700 RuntimeScriptValue Sc_Game_GetSkippingCutscene(const RuntimeScriptValue *params, int32_t param_count)
2701 {
2702     API_SCALL_INT(Game_GetSkippingCutscene);
2703 }
2704 
2705 // int ()
Sc_Game_GetSpeechFont(const RuntimeScriptValue * params,int32_t param_count)2706 RuntimeScriptValue Sc_Game_GetSpeechFont(const RuntimeScriptValue *params, int32_t param_count)
2707 {
2708     API_SCALL_INT(Game_GetSpeechFont);
2709 }
2710 
2711 // void  (int fontnum);
Sc_SetSpeechFont(const RuntimeScriptValue * params,int32_t param_count)2712 RuntimeScriptValue Sc_SetSpeechFont(const RuntimeScriptValue *params, int32_t param_count)
2713 {
2714     API_SCALL_VOID_PINT(SetSpeechFont);
2715 }
2716 
2717 // int (int spriteNum)
Sc_Game_GetSpriteWidth(const RuntimeScriptValue * params,int32_t param_count)2718 RuntimeScriptValue Sc_Game_GetSpriteWidth(const RuntimeScriptValue *params, int32_t param_count)
2719 {
2720     API_SCALL_INT_PINT(Game_GetSpriteWidth);
2721 }
2722 
2723 // int (int spriteNum)
Sc_Game_GetSpriteHeight(const RuntimeScriptValue * params,int32_t param_count)2724 RuntimeScriptValue Sc_Game_GetSpriteHeight(const RuntimeScriptValue *params, int32_t param_count)
2725 {
2726     API_SCALL_INT_PINT(Game_GetSpriteHeight);
2727 }
2728 
2729 // int ()
Sc_Game_GetTextReadingSpeed(const RuntimeScriptValue * params,int32_t param_count)2730 RuntimeScriptValue Sc_Game_GetTextReadingSpeed(const RuntimeScriptValue *params, int32_t param_count)
2731 {
2732     API_SCALL_INT(Game_GetTextReadingSpeed);
2733 }
2734 
2735 // void (int newTextSpeed)
Sc_Game_SetTextReadingSpeed(const RuntimeScriptValue * params,int32_t param_count)2736 RuntimeScriptValue Sc_Game_SetTextReadingSpeed(const RuntimeScriptValue *params, int32_t param_count)
2737 {
2738     API_SCALL_VOID_PINT(Game_SetTextReadingSpeed);
2739 }
2740 
2741 // const char* ()
Sc_Game_GetTranslationFilename(const RuntimeScriptValue * params,int32_t param_count)2742 RuntimeScriptValue Sc_Game_GetTranslationFilename(const RuntimeScriptValue *params, int32_t param_count)
2743 {
2744     API_SCALL_OBJ(const char, myScriptStringImpl, Game_GetTranslationFilename);
2745 }
2746 
2747 // int ()
Sc_Game_GetUseNativeCoordinates(const RuntimeScriptValue * params,int32_t param_count)2748 RuntimeScriptValue Sc_Game_GetUseNativeCoordinates(const RuntimeScriptValue *params, int32_t param_count)
2749 {
2750     API_SCALL_INT(Game_GetUseNativeCoordinates);
2751 }
2752 
2753 // int ()
Sc_Game_GetViewCount(const RuntimeScriptValue * params,int32_t param_count)2754 RuntimeScriptValue Sc_Game_GetViewCount(const RuntimeScriptValue *params, int32_t param_count)
2755 {
2756     API_SCALL_INT(Game_GetViewCount);
2757 }
2758 
Sc_Game_GetAudioClipCount(const RuntimeScriptValue * params,int32_t param_count)2759 RuntimeScriptValue Sc_Game_GetAudioClipCount(const RuntimeScriptValue *params, int32_t param_count)
2760 {
2761     API_VARGET_INT(game.audioClipCount);
2762 }
2763 
Sc_Game_GetAudioClip(const RuntimeScriptValue * params,int32_t param_count)2764 RuntimeScriptValue Sc_Game_GetAudioClip(const RuntimeScriptValue *params, int32_t param_count)
2765 {
2766     API_SCALL_OBJ_PINT(ScriptAudioClip, ccDynamicAudioClip, Game_GetAudioClip);
2767 }
2768 
Sc_Game_IsPluginLoaded(const RuntimeScriptValue * params,int32_t param_count)2769 RuntimeScriptValue Sc_Game_IsPluginLoaded(const RuntimeScriptValue *params, int32_t param_count)
2770 {
2771     API_SCALL_BOOL_OBJ(pl_is_plugin_loaded, const char);
2772 }
2773 
2774 
RegisterGameAPI()2775 void RegisterGameAPI()
2776 {
2777     ccAddExternalStaticFunction("Game::IsAudioPlaying^1",                       Sc_Game_IsAudioPlaying);
2778     ccAddExternalStaticFunction("Game::SetAudioTypeSpeechVolumeDrop^2",         Sc_Game_SetAudioTypeSpeechVolumeDrop);
2779     ccAddExternalStaticFunction("Game::SetAudioTypeVolume^3",                   Sc_Game_SetAudioTypeVolume);
2780     ccAddExternalStaticFunction("Game::StopAudio^1",                            Sc_Game_StopAudio);
2781     ccAddExternalStaticFunction("Game::ChangeTranslation^1",                    Sc_Game_ChangeTranslation);
2782     ccAddExternalStaticFunction("Game::DoOnceOnly^1",                           Sc_Game_DoOnceOnly);
2783     ccAddExternalStaticFunction("Game::GetColorFromRGB^3",                      Sc_Game_GetColorFromRGB);
2784     ccAddExternalStaticFunction("Game::GetFrameCountForLoop^2",                 Sc_Game_GetFrameCountForLoop);
2785     ccAddExternalStaticFunction("Game::GetLocationName^2",                      Sc_Game_GetLocationName);
2786     ccAddExternalStaticFunction("Game::GetLoopCountForView^1",                  Sc_Game_GetLoopCountForView);
2787     ccAddExternalStaticFunction("Game::GetMODPattern^0",                        Sc_Game_GetMODPattern);
2788     ccAddExternalStaticFunction("Game::GetRunNextSettingForLoop^2",             Sc_Game_GetRunNextSettingForLoop);
2789     ccAddExternalStaticFunction("Game::GetSaveSlotDescription^1",               Sc_Game_GetSaveSlotDescription);
2790     ccAddExternalStaticFunction("Game::GetViewFrame^3",                         Sc_Game_GetViewFrame);
2791     ccAddExternalStaticFunction("Game::InputBox^1",                             Sc_Game_InputBox);
2792     ccAddExternalStaticFunction("Game::SetSaveGameDirectory^1",                 Sc_Game_SetSaveGameDirectory);
2793     ccAddExternalStaticFunction("Game::StopSound^1",                            Sc_StopAllSounds);
2794     ccAddExternalStaticFunction("Game::get_CharacterCount",                     Sc_Game_GetCharacterCount);
2795     ccAddExternalStaticFunction("Game::get_DialogCount",                        Sc_Game_GetDialogCount);
2796     ccAddExternalStaticFunction("Game::get_FileName",                           Sc_Game_GetFileName);
2797     ccAddExternalStaticFunction("Game::get_FontCount",                          Sc_Game_GetFontCount);
2798     ccAddExternalStaticFunction("Game::geti_GlobalMessages",                    Sc_Game_GetGlobalMessages);
2799     ccAddExternalStaticFunction("Game::geti_GlobalStrings",                     Sc_Game_GetGlobalStrings);
2800     ccAddExternalStaticFunction("Game::seti_GlobalStrings",                     Sc_SetGlobalString);
2801     ccAddExternalStaticFunction("Game::get_GUICount",                           Sc_Game_GetGUICount);
2802     ccAddExternalStaticFunction("Game::get_IgnoreUserInputAfterTextTimeoutMs",  Sc_Game_GetIgnoreUserInputAfterTextTimeoutMs);
2803     ccAddExternalStaticFunction("Game::set_IgnoreUserInputAfterTextTimeoutMs",  Sc_Game_SetIgnoreUserInputAfterTextTimeoutMs);
2804     ccAddExternalStaticFunction("Game::get_InSkippableCutscene",                Sc_Game_GetInSkippableCutscene);
2805     ccAddExternalStaticFunction("Game::get_InventoryItemCount",                 Sc_Game_GetInventoryItemCount);
2806     ccAddExternalStaticFunction("Game::get_MinimumTextDisplayTimeMs",           Sc_Game_GetMinimumTextDisplayTimeMs);
2807     ccAddExternalStaticFunction("Game::set_MinimumTextDisplayTimeMs",           Sc_Game_SetMinimumTextDisplayTimeMs);
2808     ccAddExternalStaticFunction("Game::get_MouseCursorCount",                   Sc_Game_GetMouseCursorCount);
2809     ccAddExternalStaticFunction("Game::get_Name",                               Sc_Game_GetName);
2810     ccAddExternalStaticFunction("Game::set_Name",                               Sc_Game_SetName);
2811     ccAddExternalStaticFunction("Game::get_NormalFont",                         Sc_Game_GetNormalFont);
2812     ccAddExternalStaticFunction("Game::set_NormalFont",                         Sc_SetNormalFont);
2813     ccAddExternalStaticFunction("Game::get_SkippingCutscene",                   Sc_Game_GetSkippingCutscene);
2814     ccAddExternalStaticFunction("Game::get_SpeechFont",                         Sc_Game_GetSpeechFont);
2815     ccAddExternalStaticFunction("Game::set_SpeechFont",                         Sc_SetSpeechFont);
2816     ccAddExternalStaticFunction("Game::geti_SpriteWidth",                       Sc_Game_GetSpriteWidth);
2817     ccAddExternalStaticFunction("Game::geti_SpriteHeight",                      Sc_Game_GetSpriteHeight);
2818     ccAddExternalStaticFunction("Game::get_TextReadingSpeed",                   Sc_Game_GetTextReadingSpeed);
2819     ccAddExternalStaticFunction("Game::set_TextReadingSpeed",                   Sc_Game_SetTextReadingSpeed);
2820     ccAddExternalStaticFunction("Game::get_TranslationFilename",                Sc_Game_GetTranslationFilename);
2821     ccAddExternalStaticFunction("Game::get_UseNativeCoordinates",               Sc_Game_GetUseNativeCoordinates);
2822     ccAddExternalStaticFunction("Game::get_ViewCount",                          Sc_Game_GetViewCount);
2823     ccAddExternalStaticFunction("Game::get_AudioClipCount",                     Sc_Game_GetAudioClipCount);
2824     ccAddExternalStaticFunction("Game::geti_AudioClips",                         Sc_Game_GetAudioClip);
2825     ccAddExternalStaticFunction("Game::IsPluginLoaded",                         Sc_Game_IsPluginLoaded);
2826 
2827     /* ----------------------- Registering unsafe exports for plugins -----------------------*/
2828 
2829     ccAddExternalFunctionForPlugin("Game::IsAudioPlaying^1",                       (void*)Game_IsAudioPlaying);
2830     ccAddExternalFunctionForPlugin("Game::SetAudioTypeSpeechVolumeDrop^2",         (void*)Game_SetAudioTypeSpeechVolumeDrop);
2831     ccAddExternalFunctionForPlugin("Game::SetAudioTypeVolume^3",                   (void*)Game_SetAudioTypeVolume);
2832     ccAddExternalFunctionForPlugin("Game::StopAudio^1",                            (void*)Game_StopAudio);
2833     ccAddExternalFunctionForPlugin("Game::ChangeTranslation^1",                    (void*)Game_ChangeTranslation);
2834     ccAddExternalFunctionForPlugin("Game::DoOnceOnly^1",                           (void*)Game_DoOnceOnly);
2835     ccAddExternalFunctionForPlugin("Game::GetColorFromRGB^3",                      (void*)Game_GetColorFromRGB);
2836     ccAddExternalFunctionForPlugin("Game::GetFrameCountForLoop^2",                 (void*)Game_GetFrameCountForLoop);
2837     ccAddExternalFunctionForPlugin("Game::GetLocationName^2",                      (void*)Game_GetLocationName);
2838     ccAddExternalFunctionForPlugin("Game::GetLoopCountForView^1",                  (void*)Game_GetLoopCountForView);
2839     ccAddExternalFunctionForPlugin("Game::GetMODPattern^0",                        (void*)Game_GetMODPattern);
2840     ccAddExternalFunctionForPlugin("Game::GetRunNextSettingForLoop^2",             (void*)Game_GetRunNextSettingForLoop);
2841     ccAddExternalFunctionForPlugin("Game::GetSaveSlotDescription^1",               (void*)Game_GetSaveSlotDescription);
2842     ccAddExternalFunctionForPlugin("Game::GetViewFrame^3",                         (void*)Game_GetViewFrame);
2843     ccAddExternalFunctionForPlugin("Game::InputBox^1",                             (void*)Game_InputBox);
2844     ccAddExternalFunctionForPlugin("Game::SetSaveGameDirectory^1",                 (void*)Game_SetSaveGameDirectory);
2845     ccAddExternalFunctionForPlugin("Game::StopSound^1",                            (void*)StopAllSounds);
2846     ccAddExternalFunctionForPlugin("Game::get_CharacterCount",                     (void*)Game_GetCharacterCount);
2847     ccAddExternalFunctionForPlugin("Game::get_DialogCount",                        (void*)Game_GetDialogCount);
2848     ccAddExternalFunctionForPlugin("Game::get_FileName",                           (void*)Game_GetFileName);
2849     ccAddExternalFunctionForPlugin("Game::get_FontCount",                          (void*)Game_GetFontCount);
2850     ccAddExternalFunctionForPlugin("Game::geti_GlobalMessages",                    (void*)Game_GetGlobalMessages);
2851     ccAddExternalFunctionForPlugin("Game::geti_GlobalStrings",                     (void*)Game_GetGlobalStrings);
2852     ccAddExternalFunctionForPlugin("Game::seti_GlobalStrings",                     (void*)SetGlobalString);
2853     ccAddExternalFunctionForPlugin("Game::get_GUICount",                           (void*)Game_GetGUICount);
2854     ccAddExternalFunctionForPlugin("Game::get_IgnoreUserInputAfterTextTimeoutMs",  (void*)Game_GetIgnoreUserInputAfterTextTimeoutMs);
2855     ccAddExternalFunctionForPlugin("Game::set_IgnoreUserInputAfterTextTimeoutMs",  (void*)Game_SetIgnoreUserInputAfterTextTimeoutMs);
2856     ccAddExternalFunctionForPlugin("Game::get_InSkippableCutscene",                (void*)Game_GetInSkippableCutscene);
2857     ccAddExternalFunctionForPlugin("Game::get_InventoryItemCount",                 (void*)Game_GetInventoryItemCount);
2858     ccAddExternalFunctionForPlugin("Game::get_MinimumTextDisplayTimeMs",           (void*)Game_GetMinimumTextDisplayTimeMs);
2859     ccAddExternalFunctionForPlugin("Game::set_MinimumTextDisplayTimeMs",           (void*)Game_SetMinimumTextDisplayTimeMs);
2860     ccAddExternalFunctionForPlugin("Game::get_MouseCursorCount",                   (void*)Game_GetMouseCursorCount);
2861     ccAddExternalFunctionForPlugin("Game::get_Name",                               (void*)Game_GetName);
2862     ccAddExternalFunctionForPlugin("Game::set_Name",                               (void*)Game_SetName);
2863     ccAddExternalFunctionForPlugin("Game::get_NormalFont",                         (void*)Game_GetNormalFont);
2864     ccAddExternalFunctionForPlugin("Game::set_NormalFont",                         (void*)SetNormalFont);
2865     ccAddExternalFunctionForPlugin("Game::get_SkippingCutscene",                   (void*)Game_GetSkippingCutscene);
2866     ccAddExternalFunctionForPlugin("Game::get_SpeechFont",                         (void*)Game_GetSpeechFont);
2867     ccAddExternalFunctionForPlugin("Game::set_SpeechFont",                         (void*)SetSpeechFont);
2868     ccAddExternalFunctionForPlugin("Game::geti_SpriteWidth",                       (void*)Game_GetSpriteWidth);
2869     ccAddExternalFunctionForPlugin("Game::geti_SpriteHeight",                      (void*)Game_GetSpriteHeight);
2870     ccAddExternalFunctionForPlugin("Game::get_TextReadingSpeed",                   (void*)Game_GetTextReadingSpeed);
2871     ccAddExternalFunctionForPlugin("Game::set_TextReadingSpeed",                   (void*)Game_SetTextReadingSpeed);
2872     ccAddExternalFunctionForPlugin("Game::get_TranslationFilename",                (void*)Game_GetTranslationFilename);
2873     ccAddExternalFunctionForPlugin("Game::get_UseNativeCoordinates",               (void*)Game_GetUseNativeCoordinates);
2874     ccAddExternalFunctionForPlugin("Game::get_ViewCount",                          (void*)Game_GetViewCount);
2875 }
2876 
RegisterStaticObjects()2877 void RegisterStaticObjects()
2878 {
2879     ccAddExternalStaticObject("game",&play, &GameStaticManager);
2880 	ccAddExternalStaticObject("gs_globals",&play.globalvars[0], &GlobalStaticManager);
2881 	ccAddExternalStaticObject("mouse",&scmouse, &GlobalStaticManager);
2882 	ccAddExternalStaticObject("palette",&palette[0], &GlobalStaticManager);
2883 	ccAddExternalStaticObject("system",&scsystem, &GlobalStaticManager);
2884 	ccAddExternalStaticObject("savegameindex",&play.filenumbers[0], &GlobalStaticManager);
2885 }
2886