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 //
16 // Engine initialization
17 //
18 
19 #include "main/mainheader.h"
20 #include "ac/asset_helper.h"
21 #include "ac/common.h"
22 #include "ac/character.h"
23 #include "ac/characterextras.h"
24 #include "ac/characterinfo.h"
25 #include "ac/draw.h"
26 #include "ac/game.h"
27 #include "ac/gamesetup.h"
28 #include "ac/gamesetupstruct.h"
29 #include "ac/global_character.h"
30 #include "ac/global_game.h"
31 #include "ac/gui.h"
32 #include "ac/lipsync.h"
33 #include "ac/objectcache.h"
34 #include "ac/path_helper.h"
35 #include "ac/record.h"
36 #include "ac/roomstatus.h"
37 #include "ac/speech.h"
38 #include "ac/translation.h"
39 #include "ac/viewframe.h"
40 #include "ac/dynobj/scriptobject.h"
41 #include "ac/dynobj/scriptsystem.h"
42 #include "debug/debug_log.h"
43 #include "debug/debugger.h"
44 #include "debug/out.h"
45 #include "font/agsfontrenderer.h"
46 #include "font/fonts.h"
47 #include "game/main_game_file.h"
48 #include "main/config.h"
49 #include "main/game_start.h"
50 #include "main/engine.h"
51 #include "main/engine_setup.h"
52 #include "main/graphics_mode.h"
53 #include "main/main.h"
54 #include "main/main_allegro.h"
55 #include "media/audio/sound.h"
56 #include "ac/spritecache.h"
57 #include "util/filestream.h"
58 #include "gfx/graphicsdriver.h"
59 #include "core/assetmanager.h"
60 #include "util/misc.h"
61 #include "platform/util/pe.h"
62 #include "util/directory.h"
63 #include "util/path.h"
64 #include "main/game_file.h"
65 #include "debug/out.h"
66 
67 using namespace AGS::Common;
68 using namespace AGS::Engine;
69 
70 extern char check_dynamic_sprites_at_exit;
71 extern int our_eip;
72 extern volatile char want_exit, abort_engine;
73 extern bool justRunSetup;
74 extern GameSetup usetup;
75 extern GameSetupStruct game;
76 extern int proper_exit;
77 extern char pexbuf[STD_BUFFER_SIZE];
78 extern char saveGameDirectory[260];
79 extern int spritewidth[MAX_SPRITES],spriteheight[MAX_SPRITES];
80 extern SpriteCache spriteset;
81 extern ObjectCache objcache[MAX_INIT_SPR];
82 extern ScriptObject scrObj[MAX_INIT_SPR];
83 extern ViewStruct*views;
84 extern int displayed_room;
85 extern int eip_guinum;
86 extern int eip_guiobj;
87 extern const char *replayTempFile;
88 extern SpeechLipSyncLine *splipsync;
89 extern int numLipLines, curLipLine, curLipLinePhoneme;
90 extern ScriptSystem scsystem;
91 extern IGraphicsDriver *gfxDriver;
92 extern Bitmap **actsps;
93 extern color palette[256];
94 extern CharacterExtras *charextra;
95 extern CharacterInfo*playerchar;
96 extern Bitmap **guibg;
97 extern IDriverDependantBitmap **guibgbmp;
98 
99 String music_file;
100 String speech_file;
101 
102 t_engine_pre_init_callback engine_pre_init_callback = 0;
103 
104 #define ALLEGRO_KEYBOARD_HANDLER
105 // KEYBOARD HANDLER
106 #if !defined (WINDOWS_VERSION)
107 int myerrno;
108 #else
109 int errno;
110 #define myerrno errno
111 #endif
112 
engine_init_allegro()113 bool engine_init_allegro()
114 {
115     Debug::Printf(kDbgMsg_Init, "Initializing allegro");
116 
117     our_eip = -199;
118     // Initialize allegro
119     set_uformat(U_ASCII);
120     if (install_allegro(SYSTEM_AUTODETECT, &myerrno, atexit))
121     {
122         const char *al_err = get_allegro_error();
123         const char *user_hint = platform->GetAllegroFailUserHint();
124         platform->DisplayAlert("Unable to initialize Allegro system driver.\n%s\n\n%s",
125             al_err[0] ? al_err : "Allegro library provided no further information on the problem.",
126             user_hint);
127         return false;
128     }
129     return true;
130 }
131 
engine_setup_allegro()132 void engine_setup_allegro()
133 {
134     // Setup allegro using constructed config string
135     const char *al_config_data = "[mouse]\n"
136         "mouse_accel_factor = 0\n";
137     override_config_data(al_config_data, ustrsize(al_config_data));
138 }
139 
winclosehook()140 void winclosehook() {
141   want_exit = 1;
142   abort_engine = 1;
143   check_dynamic_sprites_at_exit = 0;
144 }
145 
engine_setup_window()146 void engine_setup_window()
147 {
148     Debug::Printf(kDbgMsg_Init, "Setting up window");
149 
150     our_eip = -198;
151 #if (ALLEGRO_DATE > 19990103)
152     set_window_title("Adventure Game Studio");
153 #if (ALLEGRO_DATE > 20021115)
154     set_close_button_callback (winclosehook);
155 #else
156     set_window_close_hook (winclosehook);
157 #endif
158 
159     our_eip = -197;
160 #endif
161 
162     platform->SetGameWindowIcon();
163 }
164 
engine_check_run_setup(const String & exe_path,ConfigTree & cfg)165 bool engine_check_run_setup(const String &exe_path, ConfigTree &cfg)
166 {
167 #if defined (WINDOWS_VERSION)
168     // check if Setup needs to be run instead
169     if (justRunSetup)
170     {
171             String cfg_file = find_user_cfg_file();
172             if (cfg_file.IsEmpty())
173                 return false;
174 
175             Debug::Printf(kDbgMsg_Init, "Running Setup");
176 
177             // Add information about game resolution and let setup application
178             // display some properties to the user
179             INIwriteint(cfg, "misc", "defaultres", game.GetDefaultResolution());
180             INIwriteint(cfg, "misc", "letterbox", game.options[OPT_LETTERBOX]);
181             INIwriteint(cfg, "misc", "game_width", game.size.Width);
182             INIwriteint(cfg, "misc", "game_height", game.size.Height);
183             INIwriteint(cfg, "misc", "gamecolordepth", game.color_depth * 8);
184             if (game.options[OPT_RENDERATSCREENRES] != kRenderAtScreenRes_UserDefined)
185             {
186                 // force enabled/disabled
187                 INIwriteint(cfg, "graphics", "render_at_screenres", game.options[OPT_RENDERATSCREENRES] == kRenderAtScreenRes_Enabled);
188                 INIwriteint(cfg, "disabled", "render_at_screenres", 1);
189             }
190 
191             ConfigTree cfg_out;
192             SetupReturnValue res = platform->RunSetup(cfg, cfg_out);
193             if (res != kSetup_Cancel)
194             {
195                 if (!IniUtil::Merge(cfg_file, cfg_out))
196                 {
197                     platform->DisplayAlert("Unable to write to the configuration file (error code 0x%08X).\n%s",
198                         platform->GetLastSystemError(), platform->GetDiskWriteAccessTroubleshootingText());
199                 }
200             }
201             if (res != kSetup_RunGame)
202                 return false;
203 
204             // TODO: investigate if the full program restart may (should) be avoided
205 
206             // Just re-reading the config file seems to cause a caching
207             // problem on Win9x, so let's restart the process.
208             allegro_exit();
209             char quotedpath[MAX_PATH];
210             snprintf(quotedpath, MAX_PATH, "\"%s\"", exe_path.GetCStr());
211             _spawnl (_P_OVERLAY, exe_path, quotedpath, NULL);
212     }
213 #endif
214 
215     return true;
216 }
217 
engine_force_window()218 void engine_force_window()
219 {
220     // Force to run in a window, override the config file
221     // TODO: actually overwrite config tree instead
222     if (force_window == 1)
223     {
224         usetup.Screen.DisplayMode.Windowed = true;
225         usetup.Screen.DisplayMode.ScreenSize.SizeDef = kScreenDef_ByGameScaling;
226     }
227     else if (force_window == 2)
228     {
229         usetup.Screen.DisplayMode.Windowed = false;
230         usetup.Screen.DisplayMode.ScreenSize.SizeDef = kScreenDef_MaxDisplay;
231     }
232 }
233 
init_game_file_name_from_cmdline()234 String init_game_file_name_from_cmdline()
235 {
236     String filename;
237 #if defined(PSP_VERSION) || defined(ANDROID_VERSION) || defined(IOS_VERSION) || defined(MAC_VERSION)
238     filename = psp_game_file_name;
239 #else
240     filename = GetPathFromCmdArg(datafile_argv);
241 #endif
242     return filename;
243 }
244 
find_game_data_in_directory(const String & path)245 String find_game_data_in_directory(const String &path)
246 {
247     al_ffblk ff;
248     String test_file;
249     String first_nonstd_fn;
250     String pattern = path;
251     pattern.Append("/*");
252 
253     if (al_findfirst(pattern, &ff, FA_ALL & ~(FA_DIREC)) != 0)
254         return "";
255     // Select first found data file; files with standart names (*.ags) have
256     // higher priority over files with custom names.
257     do
258     {
259         test_file = ff.name;
260         // Add a bit of sanity and do not parse contents of the 10k-files-large
261         // digital sound libraries.
262         // NOTE: we could certainly benefit from any kind of flag in file lib
263         // that would tell us this is the main lib without extra parsing.
264         if (test_file.CompareRightNoCase(".vox") == 0)
265             continue;
266 
267         // *.ags is a standart cross-platform file pattern for AGS games,
268         // ac2game.dat is a legacy file name for very old games,
269         // *.exe is a MS Win executable; it is included to this case because
270         // users often run AGS ports with Windows versions of games.
271         bool is_std_name = test_file.CompareRightNoCase(".ags") == 0 ||
272             test_file.CompareNoCase("ac2game.dat") == 0 ||
273             test_file.CompareRightNoCase(".exe") == 0;
274         if (is_std_name || first_nonstd_fn.IsEmpty())
275         {
276             test_file.Format("%s/%s", path.GetCStr(), ff.name);
277             if (IsMainGameLibrary(test_file))
278             {
279                 if (is_std_name)
280                 {
281                     al_findclose(&ff);
282                     return test_file;
283                 }
284                 else
285                     first_nonstd_fn = test_file;
286             }
287         }
288     }
289     while(al_findnext(&ff) == 0);
290     al_findclose(&ff);
291     return first_nonstd_fn;
292 }
293 
search_for_game_data_file(String & filename)294 bool search_for_game_data_file(String &filename)
295 {
296     Debug::Printf("Looking for the game data file");
297     String search_path;
298     // 1. From command line argument
299     if (datafile_argv > 0)
300     {
301         // set from cmd arg (do any convertions if needed)
302         filename = init_game_file_name_from_cmdline();
303         if (!filename.IsEmpty() && !Path::IsFile(filename))
304         {
305             // if it is not a file, assume it is a directory and seek for data file
306             search_path = filename;
307             filename = find_game_data_in_directory(search_path);
308         }
309     }
310     // 2. From setup
311     // 2.1. Use the provided data dir and filename
312     else if (!usetup.main_data_filename.IsEmpty())
313     {
314         if (!usetup.data_files_dir.IsEmpty() && is_relative_filename(usetup.main_data_filename))
315         {
316             filename = usetup.data_files_dir;
317             if (filename.GetLast() != '/' && filename.GetLast() != '\\')
318                 filename.AppendChar('/');
319             filename.Append(usetup.main_data_filename);
320         }
321         else
322         {
323             filename = usetup.main_data_filename;
324         }
325     }
326     // 2.2. Search in the provided data dir
327     else if (!usetup.data_files_dir.IsEmpty())
328     {
329         search_path = usetup.data_files_dir;
330         filename = find_game_data_in_directory(search_path);
331     }
332     // 3. Look in known locations
333     else
334     {
335         // 3.1. Look for attachment in the running executable
336         //
337         // set filename from cmd arg (do any conversions if needed)
338         // this will use argument zero, the executable's name
339         filename = init_game_file_name_from_cmdline();
340         if (filename.IsEmpty() || !Common::AssetManager::IsDataFile(filename))
341         {
342             // 3.2 Look in current directory
343             search_path = Directory::GetCurrentDirectory();
344             filename = find_game_data_in_directory(search_path);
345             if (filename.IsEmpty())
346             {
347                 // 3.3 Look in executable's directory (if it's different from current dir)
348                 if (Path::ComparePaths(appDirectory, search_path))
349                 {
350                     search_path = appDirectory;
351                     filename = find_game_data_in_directory(search_path);
352                 }
353             }
354         }
355     }
356 
357     // Finally, store game file's absolute path, or report error
358     if (filename.IsEmpty())
359     {
360         Debug::Printf(kDbgMsg_Error, "Game data file could not be found. Search path used: '%s'", search_path.GetCStr());
361         return false;
362     }
363     filename = Path::MakeAbsolutePath(filename);
364     Debug::Printf(kDbgMsg_Init, "Located game data file: %s", filename.GetCStr());
365     return true;
366 }
367 
engine_init_game_data()368 bool engine_init_game_data()
369 {
370     // Search for an available game package in the known locations
371     AssetError err;
372     if (search_for_game_data_file(game_file_name))
373         err = AssetManager::SetDataFile(game_file_name);
374     else
375         err = kAssetErrNoLibFile;
376 
377     if (err != kAssetNoError)
378     {  // there's a problem
379         String emsg;
380         if (err == Common::kAssetErrLibParse)
381         {
382             emsg = String::FromFormat("ERROR: The game data is of unsupported format or file is corrupt.\nFile: '%s'", game_file_name.GetCStr());
383         }
384         else
385         { // file not found, or another problem
386             if (game_file_name.IsEmpty())
387                 emsg = "ERROR: Unable to find game data files. The necessary files are either missing or are of unsupported format.";
388             else
389                 emsg = String::FromFormat("ERROR: Unable to find or open '%s'.", game_file_name.GetCStr());
390         }
391 
392         platform->DisplayAlert(emsg);
393         main_print_help();
394         return false;
395     }
396 
397     // Save data file name and data folder
398     usetup.main_data_filename = get_filename(game_file_name);
399     // There is a path in the game file name (and the user/ has not specified
400     // another one) save the path, so that it can load the VOX files, etc
401     if (usetup.data_files_dir.IsEmpty())
402     {
403         size_t ichar = game_file_name.FindCharReverse('/');
404         if (ichar != -1)
405         {
406             usetup.data_files_dir = game_file_name.Left(ichar);
407         }
408     }
409     return true;
410 }
411 
engine_init_fonts()412 void engine_init_fonts()
413 {
414     Debug::Printf(kDbgMsg_Init, "Initializing TTF renderer");
415 
416     init_font_renderer();
417 }
418 
engine_init_mouse()419 void engine_init_mouse()
420 {
421     int res = minstalled();
422     if (res < 0)
423         Debug::Printf(kDbgMsg_Init, "Initializing mouse: failed");
424     else
425         Debug::Printf(kDbgMsg_Init, "Initializing mouse: number of buttons reported is %d", res);
426     Mouse::SetSpeed(usetup.mouse_speed);
427 }
428 
engine_check_memory()429 int engine_check_memory()
430 {
431     Debug::Printf(kDbgMsg_Init, "Checking memory");
432 
433     char*memcheck=(char*)malloc(4000000);
434     if (memcheck==NULL) {
435         platform->DisplayAlert("There is not enough memory available to run this game. You need 4 Mb free\n"
436             "extended memory to run the game.\n"
437             "If you are running from Windows, check the 'DPMI memory' setting on the DOS box\n"
438             "properties.\n");
439         return EXIT_NORMAL;
440     }
441     free(memcheck);
442     unlink (replayTempFile);
443     return RETURN_CONTINUE;
444 }
445 
engine_init_rooms()446 void engine_init_rooms()
447 {
448     // Obsolete now since room statuses are allocated only when needed
449 }
450 
engine_init_speech()451 void engine_init_speech()
452 {
453     play.want_speech=-2;
454 
455     if (!usetup.no_speech_pack) {
456         speech_file = "speech.vox";
457         String speech_filepath = find_assetlib(speech_file);
458         if (!speech_filepath.IsEmpty()) {
459             Debug::Printf("Initializing speech vox");
460             if (AssetManager::SetDataFile(speech_filepath)!=Common::kAssetNoError) {
461                 platform->DisplayAlert("Unable to read voice pack, file could be corrupted or of unknown format.\nSpeech voice-over will be disabled.");
462                 AssetManager::SetDataFile(game_file_name); // switch back to the main data pack
463                 return;
464             }
465             Stream *speechsync = AssetManager::OpenAsset("syncdata.dat");
466             if (speechsync != NULL) {
467                 // this game has voice lip sync
468                 int lipsync_fmt = speechsync->ReadInt32();
469                 if (lipsync_fmt != 4)
470                 {
471                     Debug::Printf(kDbgMsg_Init, "Unknown speech lip sync format (%d).\nLip sync disabled.", lipsync_fmt);
472                 }
473                 else {
474                     numLipLines = speechsync->ReadInt32();
475                     splipsync = (SpeechLipSyncLine*)malloc (sizeof(SpeechLipSyncLine) * numLipLines);
476                     for (int ee = 0; ee < numLipLines; ee++)
477                     {
478                         splipsync[ee].numPhonemes = speechsync->ReadInt16();
479                         speechsync->Read(splipsync[ee].filename, 14);
480                         splipsync[ee].endtimeoffs = (int*)malloc(splipsync[ee].numPhonemes * sizeof(int));
481                         speechsync->ReadArrayOfInt32(splipsync[ee].endtimeoffs, splipsync[ee].numPhonemes);
482                         splipsync[ee].frame = (short*)malloc(splipsync[ee].numPhonemes * sizeof(short));
483                         speechsync->ReadArrayOfInt16(splipsync[ee].frame, splipsync[ee].numPhonemes);
484                     }
485                 }
486                 delete speechsync;
487             }
488             AssetManager::SetDataFile(game_file_name); // switch back to the main data pack
489             Debug::Printf(kDbgMsg_Init, "Voice pack found and initialized.");
490             play.want_speech=1;
491         }
492         else if (Path::ComparePaths(usetup.data_files_dir, get_voice_install_dir()) != 0)
493         {
494             // If we have custom voice directory set, we will enable voice-over even if speech.vox does not exist
495             Debug::Printf(kDbgMsg_Init, "Voice pack was not found, but voice installation directory is defined: enabling voice-over.");
496             play.want_speech=1;
497         }
498     }
499 }
500 
engine_init_digital_audio()501 void engine_init_digital_audio()
502 {
503     play.separate_music_lib = 0;
504     music_file = game.GetAudioVOXName();
505     String music_filepath = find_assetlib(music_file);
506     if (!music_filepath.IsEmpty())
507     {
508         if (AssetManager::SetDataFile(music_filepath) == kAssetNoError)
509         {
510             AssetManager::SetDataFile(game_file_name);
511             Debug::Printf(kDbgMsg_Init, "%s found and initialized.", music_file.GetCStr());
512             play.separate_music_lib = 1;
513         }
514         else
515         {
516             platform->DisplayAlert("Unable to initialize digital audio pack '%s', file could be corrupt or of unsupported format.",
517                 music_file.GetCStr());
518         }
519     }
520 }
521 
engine_init_keyboard()522 void engine_init_keyboard()
523 {
524 #ifdef ALLEGRO_KEYBOARD_HANDLER
525     Debug::Printf(kDbgMsg_Init, "Initializing keyboard");
526 
527     install_keyboard();
528 #endif
529 }
530 
engine_init_timer()531 void engine_init_timer()
532 {
533     Debug::Printf(kDbgMsg_Init, "Install timer");
534     install_timer();
535 }
536 
537 typedef char AlIDStr[5];
538 
AlIDToChars(int al_id,AlIDStr & id_str)539 void AlIDToChars(int al_id, AlIDStr &id_str)
540 {
541     id_str[0] = (al_id >> 24) & 0xFF;
542     id_str[1] = (al_id >> 16) & 0xFF;
543     id_str[2] = (al_id >> 8) & 0xFF;
544     id_str[3] = (al_id) & 0xFF;
545     id_str[4] = 0;
546 }
547 
AlDigiToChars(int digi_id,AlIDStr & id_str)548 void AlDigiToChars(int digi_id, AlIDStr &id_str)
549 {
550     if (digi_id == DIGI_NONE)
551         strcpy(id_str, "None");
552     else if (digi_id == DIGI_AUTODETECT)
553         strcpy(id_str, "Auto");
554     else
555         AlIDToChars(digi_id, id_str);
556 }
557 
AlMidiToChars(int midi_id,AlIDStr & id_str)558 void AlMidiToChars(int midi_id, AlIDStr &id_str)
559 {
560     if (midi_id == MIDI_NONE)
561         strcpy(id_str, "None");
562     else if (midi_id == MIDI_AUTODETECT)
563         strcpy(id_str, "Auto");
564     else
565         AlIDToChars(midi_id, id_str);
566 }
567 
try_install_sound(int digi_id,int midi_id,String * p_err_msg=NULL)568 bool try_install_sound(int digi_id, int midi_id, String *p_err_msg = NULL)
569 {
570     if (install_sound(digi_id, midi_id, NULL) == 0)
571         return true;
572     // Allegro does not let you try digital and MIDI drivers separately,
573     // and does not indicate which driver failed by return value.
574     // Therefore we try to guess.
575     if (p_err_msg)
576         *p_err_msg = get_allegro_error();
577     if (midi_id != MIDI_NONE)
578     {
579         Debug::Printf(kDbgMsg_Error, "Failed to init one of the drivers; Error: '%s'.\nWill try to start without MIDI", get_allegro_error());
580         if (install_sound(digi_id, MIDI_NONE, NULL) == 0)
581             return true;
582     }
583     if (digi_id != DIGI_NONE)
584     {
585         Debug::Printf(kDbgMsg_Error, "Failed to init one of the drivers; Error: '%s'.\nWill try to start without DIGI", get_allegro_error());
586         if (install_sound(DIGI_NONE, midi_id, NULL) == 0)
587             return true;
588     }
589     Debug::Printf(kDbgMsg_Error, "Failed to init sound drivers. Error: %s", get_allegro_error());
590     return false;
591 }
592 
engine_init_sound()593 void engine_init_sound()
594 {
595     if (opts.mod_player)
596         reserve_voices(NUM_DIGI_VOICES, -1);
597     // maybe this line will solve the sound volume? [??? wth is this]
598     set_volume_per_voice(1);
599 
600     Debug::Printf("Initialize sound drivers");
601 
602     // TODO: apply those options during config reading instead
603     if (!psp_audio_enabled)
604     {
605         usetup.digicard = DIGI_NONE;
606         usetup.midicard = MIDI_NONE;
607     }
608 
609     if (!psp_midi_enabled)
610         usetup.midicard = MIDI_NONE;
611 
612     AlIDStr digi_id;
613     AlIDStr midi_id;
614     AlDigiToChars(usetup.digicard, digi_id);
615     AlMidiToChars(usetup.midicard, midi_id);
616     Debug::Printf(kDbgMsg_Init, "Sound settings: digital driver ID: '%s' (0x%x), MIDI driver ID: '%s' (0x%x)",
617         digi_id, usetup.digicard, midi_id, usetup.midicard);
618 
619     String err_msg;
620     bool sound_res = try_install_sound(usetup.digicard, usetup.midicard, &err_msg);
621     if (!sound_res && opts.mod_player)
622     {
623         Debug::Printf("Resetting to default sound parameters and trying again.");
624         reserve_voices(-1, -1); // this resets voice number to defaults
625         opts.mod_player = 0;
626         sound_res = try_install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT);
627     }
628     if (!sound_res)
629     {
630         Debug::Printf("Everything failed, installing dummy no-sound drivers.");
631         reserve_voices(0, 0);
632         install_sound(DIGI_NONE, MIDI_NONE, NULL);
633     }
634     // Only display a warning if they wanted a sound card
635     const bool digi_failed = usetup.digicard != DIGI_NONE && digi_card == DIGI_NONE;
636     const bool midi_failed = usetup.midicard != MIDI_NONE && midi_card == MIDI_NONE;
637     if (digi_failed || midi_failed)
638     {
639         platform->DisplayAlert("Warning: cannot enable %s.\nProblem: %s.\n\nYou may supress this message by disabling %s in the game setup.",
640             (digi_failed && midi_failed ? "game audio" : (digi_failed ? "digital audio" : "MIDI audio") ),
641             (err_msg.IsEmpty() ? "No compatible drivers found in the system." : err_msg.GetCStr()),
642             (digi_failed && midi_failed ? "sound" : (digi_failed ? "digital sound" : "MIDI sound") ));
643     }
644 
645     usetup.digicard = digi_card;
646     usetup.midicard = midi_card;
647 
648     AlDigiToChars(usetup.digicard, digi_id);
649     AlMidiToChars(usetup.midicard, midi_id);
650     Debug::Printf(kDbgMsg_Init, "Installed digital driver ID: '%s' (0x%x), MIDI driver ID: '%s' (0x%x)",
651         digi_id, usetup.digicard, midi_id, usetup.midicard);
652 
653     our_eip = -181;
654 
655     if (usetup.digicard == DIGI_NONE)
656     {
657         // disable speech and music if no digital sound
658         // therefore the MIDI soundtrack will be used if present,
659         // and the voice mode should not go to Voice Only
660         play.want_speech = -2;
661         play.separate_music_lib = 0;
662     }
663 
664 #ifdef WINDOWS_VERSION
665     if (usetup.digicard == DIGI_DIRECTX(0))
666     {
667         // DirectX mixer seems to buffer an extra sample itself
668         use_extra_sound_offset = 1;
669     }
670 #endif
671 }
672 
engine_init_debug()673 void engine_init_debug()
674 {
675     //set_volume(255,-1);
676     if ((debug_flags & (~DBG_DEBUGMODE)) >0) {
677         platform->DisplayAlert("Engine debugging enabled.\n"
678             "\nNOTE: You have selected to enable one or more engine debugging options.\n"
679             "These options cause many parts of the game to behave abnormally, and you\n"
680             "may not see the game as you are used to it. The point is to test whether\n"
681             "the engine passes a point where it is crashing on you normally.\n"
682             "[Debug flags enabled: 0x%02X]\n"
683             "Press a key to continue.\n",debug_flags);
684     }
685 }
686 
atexit_handler()687 void atexit_handler() {
688     if (proper_exit==0) {
689         sprintf(pexbuf,"\nError: the program has exited without requesting it.\n"
690             "Program pointer: %+03d  (write this number down), ACI version %s\n"
691             "If you see a list of numbers above, please write them down and contact\n"
692             "developers. Otherwise, note down any other information displayed.\n",
693             our_eip, EngineVersion.LongString.GetCStr());
694         platform->DisplayAlert(pexbuf);
695     }
696 }
697 
engine_init_exit_handler()698 void engine_init_exit_handler()
699 {
700     Debug::Printf(kDbgMsg_Init, "Install exit handler");
701 
702     atexit(atexit_handler);
703 }
704 
engine_init_rand()705 void engine_init_rand()
706 {
707     play.randseed = time(NULL);
708     srand (play.randseed);
709 }
710 
engine_init_pathfinder()711 void engine_init_pathfinder()
712 {
713     Debug::Printf(kDbgMsg_Init, "Initialize path finder library");
714 
715     init_pathfinder();
716 }
717 
engine_pre_init_gfx()718 void engine_pre_init_gfx()
719 {
720     //Debug::Printf("Initialize gfx");
721 
722     //platform->InitialiseAbufAtStartup();
723 }
724 
engine_load_game_data()725 int engine_load_game_data()
726 {
727     Debug::Printf("Load game data");
728 
729     our_eip=-17;
730     String err_str;
731     if (!load_game_file(err_str))
732     {
733         proper_exit=1;
734         platform->FinishedUsingGraphicsMode();
735         display_game_file_error(err_str);
736         return EXIT_NORMAL;
737     }
738 
739     return RETURN_CONTINUE;
740 }
741 
engine_check_register_game()742 int engine_check_register_game()
743 {
744     if (justRegisterGame)
745     {
746         platform->RegisterGameWithGameExplorer();
747         proper_exit = 1;
748         return EXIT_NORMAL;
749     }
750 
751     if (justUnRegisterGame)
752     {
753         platform->UnRegisterGameWithGameExplorer();
754         proper_exit = 1;
755         return EXIT_NORMAL;
756     }
757 
758     return RETURN_CONTINUE;
759 }
760 
engine_init_title()761 void engine_init_title()
762 {
763     //platform->DisplayAlert("loaded game");
764     our_eip=-91;
765 #if (ALLEGRO_DATE > 19990103)
766     set_window_title(game.gamename);
767 #endif
768 
769     Debug::Printf(kDbgMsg_Init, "Game title: '%s'", game.gamename);
770 }
771 
engine_init_directories()772 void engine_init_directories()
773 {
774     Debug::Printf(kDbgMsg_Init, "Data directory: %s", usetup.data_files_dir.GetCStr());
775     if (!usetup.install_dir.IsEmpty())
776         Debug::Printf(kDbgMsg_Init, "Optional install directory: %s", usetup.install_dir.GetCStr());
777     if (!usetup.install_audio_dir.IsEmpty())
778         Debug::Printf(kDbgMsg_Init, "Optional audio directory: %s", usetup.install_audio_dir.GetCStr());
779     if (!usetup.install_voice_dir.IsEmpty())
780         Debug::Printf(kDbgMsg_Init, "Optional voice-over directory: %s", usetup.install_voice_dir.GetCStr());
781     if (!usetup.user_data_dir.IsEmpty())
782         Debug::Printf(kDbgMsg_Init, "User data directory: %s", usetup.user_data_dir.GetCStr());
783     if (!usetup.shared_data_dir.IsEmpty())
784         Debug::Printf(kDbgMsg_Init, "Shared data directory: %s", usetup.shared_data_dir.GetCStr());
785 
786     set_install_dir(usetup.install_dir, usetup.install_audio_dir, usetup.install_voice_dir);
787     if (!usetup.install_dir.IsEmpty())
788     {
789         // running in debugger: don't redirect to the game exe folder (_Debug)
790         // TODO: find out why we need to do this (and do we?)
791         usetup.data_files_dir = ".";
792     }
793 
794     // if end-user specified custom save path, use it
795     bool res = false;
796     if (!usetup.user_data_dir.IsEmpty())
797     {
798         res = SetCustomSaveParent(usetup.user_data_dir);
799         if (!res)
800         {
801             Debug::Printf(kDbgMsg_Warn, "WARNING: custom user save path failed, using default system paths");
802             usetup.user_data_dir.Empty();
803             res = false;
804         }
805     }
806     // if there is no custom path, or if custom path failed, use default system path
807     if (!res)
808     {
809         char newDirBuffer[MAX_PATH];
810         sprintf(newDirBuffer, "%s/%s", UserSavedgamesRootToken.GetCStr(), game.saveGameFolderName);
811         SetSaveGameDirectoryPath(newDirBuffer);
812     }
813 }
814 
815 #if defined(ANDROID_VERSION)
816 extern char android_base_directory[256];
817 #endif // ANDROID_VERSION
818 
check_write_access()819 int check_write_access() {
820 
821   if (platform->GetDiskFreeSpaceMB() < 2)
822     return 0;
823 
824   our_eip = -1895;
825 
826   // The Save Game Dir is the only place that we should write to
827   char tempPath[MAX_PATH];
828   sprintf(tempPath, "%s""tmptest.tmp", saveGameDirectory);
829   Stream *temp_s = Common::File::CreateFile(tempPath);
830   if (!temp_s)
831       // TODO: move this somewhere else (Android platform driver init?)
832 #if defined(ANDROID_VERSION)
833   {
834 	  put_backslash(android_base_directory);
835 	  sprintf(tempPath, "%s""tmptest.tmp", android_base_directory);
836 	  temp_s = Common::File::CreateFile(tempPath);
837 	  if (temp_s == NULL) return 0;
838 	  else SetCustomSaveParent(android_base_directory);
839   }
840 #else
841     return 0;
842 #endif // ANDROID_VERSION
843 
844   our_eip = -1896;
845 
846   temp_s->Write("just to test the drive free space", 30);
847   delete temp_s;
848 
849   our_eip = -1897;
850 
851   if (unlink(tempPath))
852     return 0;
853 
854   return 1;
855 }
856 
engine_check_disk_space()857 int engine_check_disk_space()
858 {
859     Debug::Printf(kDbgMsg_Init, "Checking for disk space");
860 
861     if (check_write_access()==0) {
862         platform->DisplayAlert("Unable to write in the savegame directory.\n%s", platform->GetDiskWriteAccessTroubleshootingText());
863         proper_exit = 1;
864         return EXIT_NORMAL;
865     }
866 
867     return RETURN_CONTINUE;
868 }
869 
engine_check_font_was_loaded()870 int engine_check_font_was_loaded()
871 {
872     if (!font_first_renderer_loaded())
873     {
874         platform->DisplayAlert("No game fonts found. At least one font is required to run the game.");
875         proper_exit = 1;
876         return EXIT_NORMAL;
877     }
878 
879     return RETURN_CONTINUE;
880 }
881 
engine_init_modxm_player()882 void engine_init_modxm_player()
883 {
884 #ifndef PSP_NO_MOD_PLAYBACK
885     if (game.options[OPT_NOMODMUSIC])
886         opts.mod_player = 0;
887 
888     if (opts.mod_player) {
889         Debug::Printf(kDbgMsg_Init, "Initializing MOD/XM player");
890 
891         if (init_mod_player(NUM_MOD_DIGI_VOICES) < 0) {
892             platform->DisplayAlert("Warning: install_mod: MOD player failed to initialize.");
893             opts.mod_player=0;
894         }
895     }
896 #else
897     opts.mod_player = 0;
898     Debug::Printf(kDbgMsg_Init, "Compiled without MOD/XM player");
899 #endif
900 }
901 
show_preload()902 void show_preload () {
903     // ** Do the preload graphic if available
904     color temppal[256];
905 	Bitmap *splashsc = BitmapHelper::CreateRawBitmapOwner( load_pcx("preload.pcx",temppal) );
906     if (splashsc != NULL) {
907         if (splashsc->GetColorDepth() == 8)
908             set_palette_range(temppal, 0, 255, 0);
909 		Bitmap *screen_bmp = BitmapHelper::GetScreenBitmap();
910         Bitmap *tsc = BitmapHelper::CreateBitmapCopy(splashsc, screen_bmp->GetColorDepth());
911 
912 		screen_bmp->Fill(0);
913         screen_bmp->StretchBlt(tsc, RectWH(0, 0, play.viewport.GetWidth(),play.viewport.GetHeight()), Common::kBitmap_Transparency);
914 
915         gfxDriver->ClearDrawList();
916 
917         if (!gfxDriver->UsesMemoryBackBuffer())
918         {
919             IDriverDependantBitmap *ddb = gfxDriver->CreateDDBFromBitmap(screen_bmp, false, true);
920             gfxDriver->DrawSprite(0, 0, ddb);
921             render_to_screen(screen_bmp, 0, 0);
922             gfxDriver->DestroyDDB(ddb);
923         }
924         else
925 			render_to_screen(screen_bmp, 0, 0);
926 
927         delete splashsc;
928         delete tsc;
929         platform->Delay(500);
930     }
931 }
932 
engine_show_preload()933 void engine_show_preload()
934 {
935     Debug::Printf("Check for preload image");
936 
937     show_preload ();
938 }
939 
engine_init_sprites()940 int engine_init_sprites()
941 {
942     Debug::Printf(kDbgMsg_Init, "Initialize sprites");
943 
944     if (spriteset.initFile ("acsprset.spr"))
945     {
946         platform->FinishedUsingGraphicsMode();
947         allegro_exit();
948         proper_exit=1;
949         platform->DisplayAlert("Could not load sprite set file ACSPRSET.SPR\n"
950             "This means that the file is missing or there is not enough free\n"
951             "system memory to load the file.\n");
952         return EXIT_NORMAL;
953     }
954 
955     return RETURN_CONTINUE;
956 }
957 
engine_init_game_settings()958 void engine_init_game_settings()
959 {
960     our_eip=-7;
961     Debug::Printf("Initialize game settings");
962 
963     int ee;
964 
965     for (ee = 0; ee < MAX_INIT_SPR + game.numcharacters; ee++)
966         actsps[ee] = NULL;
967 
968     for (ee=0;ee<256;ee++) {
969         if (game.paluses[ee]!=PAL_BACKGROUND)
970             palette[ee]=game.defpal[ee];
971     }
972 
973     if (game.options[OPT_NOSCALEFNT]) wtext_multiply=1;
974 
975     for (ee = 0; ee < game.numcursors; ee++)
976     {
977         // The cursor graphics are assigned to mousecurs[] and so cannot
978         // be removed from memory
979         if (game.mcurs[ee].pic >= 0)
980             spriteset.precache (game.mcurs[ee].pic);
981 
982         // just in case they typed an invalid view number in the editor
983         if (game.mcurs[ee].view >= game.numviews)
984             game.mcurs[ee].view = -1;
985 
986         if (game.mcurs[ee].view >= 0)
987             precache_view (game.mcurs[ee].view);
988     }
989     // may as well preload the character gfx
990     if (playerchar->view >= 0)
991         precache_view (playerchar->view);
992 
993     for (ee = 0; ee < MAX_INIT_SPR; ee++)
994         objcache[ee].image = NULL;
995 
996     /*  dummygui.guiId = -1;
997     dummyguicontrol.guin = -1;
998     dummyguicontrol.objn = -1;*/
999 
1000     our_eip=-6;
1001     //  game.chars[0].talkview=4;
1002     //init_language_text(game.langcodes[0]);
1003 
1004     for (ee = 0; ee < MAX_INIT_SPR; ee++) {
1005         scrObj[ee].id = ee;
1006         // 64 bit: Using the id instead
1007         // scrObj[ee].obj = NULL;
1008     }
1009 
1010     for (ee=0;ee<game.numcharacters;ee++) {
1011         memset(&game.chars[ee].inv[0],0,MAX_INV*sizeof(short));
1012         game.chars[ee].activeinv=-1;
1013         game.chars[ee].following=-1;
1014         game.chars[ee].followinfo=97 | (10 << 8);
1015         game.chars[ee].idletime=20;  // can be overridden later with SetIdle or summink
1016         game.chars[ee].idleleft=game.chars[ee].idletime;
1017         game.chars[ee].transparency = 0;
1018         game.chars[ee].baseline = -1;
1019         game.chars[ee].walkwaitcounter = 0;
1020         game.chars[ee].z = 0;
1021         charextra[ee].xwas = INVALID_X;
1022         charextra[ee].zoom = 100;
1023         if (game.chars[ee].view >= 0) {
1024             // set initial loop to 0
1025             game.chars[ee].loop = 0;
1026             // or to 1 if they don't have up/down frames
1027             if (views[game.chars[ee].view].loops[0].numFrames < 1)
1028                 game.chars[ee].loop = 1;
1029         }
1030         charextra[ee].process_idle_this_time = 0;
1031         charextra[ee].invorder_count = 0;
1032         charextra[ee].slow_move_counter = 0;
1033         charextra[ee].animwait = 0;
1034     }
1035     // multiply up gui positions
1036     guibg = (Bitmap **)malloc(sizeof(Bitmap *) * game.numgui);
1037     guibgbmp = (IDriverDependantBitmap**)malloc(sizeof(IDriverDependantBitmap*) * game.numgui);
1038     for (ee=0;ee<game.numgui;ee++) {
1039         guibg[ee] = NULL;
1040         guibgbmp[ee] = NULL;
1041     }
1042 
1043     our_eip=-5;
1044     for (ee=0;ee<game.numinvitems;ee++) {
1045         if (game.invinfo[ee].flags & IFLG_STARTWITH) playerchar->inv[ee]=1;
1046         else playerchar->inv[ee]=0;
1047     }
1048     play.score=0;
1049     play.sierra_inv_color=7;
1050     // copy the value set by the editor
1051     if (game.options[OPT_GLOBALTALKANIMSPD] >= 0)
1052     {
1053         play.talkanim_speed = game.options[OPT_GLOBALTALKANIMSPD];
1054         game.options[OPT_GLOBALTALKANIMSPD] = 1;
1055     }
1056     else
1057     {
1058         play.talkanim_speed = -game.options[OPT_GLOBALTALKANIMSPD] - 1;
1059         game.options[OPT_GLOBALTALKANIMSPD] = 0;
1060     }
1061     play.inv_item_wid = 40;
1062     play.inv_item_hit = 22;
1063     play.messagetime=-1;
1064     play.disabled_user_interface=0;
1065     play.gscript_timer=-1;
1066     play.debug_mode=game.options[OPT_DEBUGMODE];
1067     play.inv_top=0;
1068     play.inv_numdisp=0;
1069     play.obsolete_inv_numorder=0;
1070     play.text_speed=15;
1071     play.text_min_display_time_ms = 1000;
1072     play.ignore_user_input_after_text_timeout_ms = 500;
1073     play.ignore_user_input_until_time = 0;
1074     play.lipsync_speed = 15;
1075     play.close_mouth_speech_time = 10;
1076     play.disable_antialiasing = 0;
1077     play.rtint_enabled = false;
1078     play.rtint_level = 0;
1079     play.rtint_light = 0;
1080     play.text_speed_modifier = 0;
1081     play.text_align = SCALIGN_LEFT;
1082     // Make the default alignment to the right with right-to-left text
1083     if (game.options[OPT_RIGHTLEFTWRITE])
1084         play.text_align = SCALIGN_RIGHT;
1085 
1086     play.speech_bubble_width = get_fixed_pixel_size(100);
1087     play.bg_frame=0;
1088     play.bg_frame_locked=0;
1089     play.bg_anim_delay=0;
1090     play.anim_background_speed = 0;
1091     play.silent_midi = 0;
1092     play.current_music_repeating = 0;
1093     play.skip_until_char_stops = -1;
1094     play.get_loc_name_last_time = -1;
1095     play.get_loc_name_save_cursor = -1;
1096     play.restore_cursor_mode_to = -1;
1097     play.restore_cursor_image_to = -1;
1098     play.ground_level_areas_disabled = 0;
1099     play.next_screen_transition = -1;
1100     play.temporarily_turned_off_character = -1;
1101     play.inv_backwards_compatibility = 0;
1102     play.gamma_adjustment = 100;
1103     play.num_do_once_tokens = 0;
1104     play.do_once_tokens = NULL;
1105     play.music_queue_size = 0;
1106     play.shakesc_length = 0;
1107     play.wait_counter=0;
1108     play.key_skip_wait = 0;
1109     play.cur_music_number=-1;
1110     play.music_repeat=1;
1111     play.music_master_volume=100 + LegacyMusicMasterVolumeAdjustment;
1112     play.digital_master_volume = 100;
1113     play.screen_flipped=0;
1114     play.offsets_locked=0;
1115     play.cant_skip_speech = user_to_internal_skip_speech((SkipSpeechStyle)game.options[OPT_NOSKIPTEXT]);
1116     play.sound_volume = 255;
1117     play.speech_volume = 255;
1118     play.normal_font = 0;
1119     play.speech_font = 1;
1120     play.speech_text_shadow = 16;
1121     play.screen_tint = -1;
1122     play.bad_parsed_word[0] = 0;
1123     play.swap_portrait_side = 0;
1124     play.swap_portrait_lastchar = -1;
1125     play.swap_portrait_lastlastchar = -1;
1126     play.in_conversation = 0;
1127     play.skip_display = 3;
1128     play.no_multiloop_repeat = 0;
1129     play.in_cutscene = 0;
1130     play.fast_forward = 0;
1131     play.totalscore = game.totalscore;
1132     play.roomscript_finished = 0;
1133     play.no_textbg_when_voice = 0;
1134     play.max_dialogoption_width = get_fixed_pixel_size(180);
1135     play.no_hicolor_fadein = 0;
1136     play.bgspeech_game_speed = 0;
1137     play.bgspeech_stay_on_display = 0;
1138     play.unfactor_speech_from_textlength = 0;
1139     play.mp3_loop_before_end = 70;
1140     play.speech_music_drop = 60;
1141     play.room_changes = 0;
1142     play.check_interaction_only = 0;
1143     play.replay_hotkey = 318;  // Alt+R
1144     play.dialog_options_x = 0;
1145     play.dialog_options_y = 0;
1146     play.min_dialogoption_width = 0;
1147     play.disable_dialog_parser = 0;
1148     play.ambient_sounds_persist = 0;
1149     play.screen_is_faded_out = 0;
1150     play.player_on_region = 0;
1151     play.top_bar_backcolor = 8;
1152     play.top_bar_textcolor = 16;
1153     play.top_bar_bordercolor = 8;
1154     play.top_bar_borderwidth = 1;
1155     play.top_bar_ypos = 25;
1156     play.top_bar_font = -1;
1157     play.screenshot_width = 160;
1158     play.screenshot_height = 100;
1159     play.speech_text_align = SCALIGN_CENTRE;
1160     play.auto_use_walkto_points = 1;
1161     play.inventory_greys_out = 0;
1162     play.skip_speech_specific_key = 0;
1163     play.abort_key = 324;  // Alt+X
1164     play.fade_to_red = 0;
1165     play.fade_to_green = 0;
1166     play.fade_to_blue = 0;
1167     play.show_single_dialog_option = 0;
1168     play.keep_screen_during_instant_transition = 0;
1169     play.read_dialog_option_colour = -1;
1170     play.speech_portrait_placement = 0;
1171     play.speech_portrait_x = 0;
1172     play.speech_portrait_y = 0;
1173     play.speech_display_post_time_ms = 0;
1174     play.dialog_options_highlight_color = DIALOG_OPTIONS_HIGHLIGHT_COLOR_DEFAULT;
1175     play.speech_in_post_state = false;
1176     play.narrator_speech = game.playercharacter;
1177     play.crossfading_out_channel = 0;
1178     play.speech_textwindow_gui = game.options[OPT_TWCUSTOM];
1179     if (play.speech_textwindow_gui == 0)
1180         play.speech_textwindow_gui = -1;
1181     strcpy(play.game_name, game.gamename);
1182     play.lastParserEntry[0] = 0;
1183     play.follow_change_room_timer = 150;
1184     for (ee = 0; ee < MAX_BSCENE; ee++)
1185         play.raw_modified[ee] = 0;
1186     play.game_speed_modifier = 0;
1187     if (debug_flags & DBG_DEBUGMODE)
1188         play.debug_mode = 1;
1189     gui_disabled_style = convert_gui_disabled_style(game.options[OPT_DISABLEOFF]);
1190 
1191     memset(&play.walkable_areas_on[0],1,MAX_WALK_AREAS+1);
1192     memset(&play.script_timers[0],0,MAX_TIMERS * sizeof(int));
1193     memset(&play.default_audio_type_volumes[0], -1, MAX_AUDIO_TYPES * sizeof(int));
1194 
1195     // reset graphical script vars (they're still used by some games)
1196     for (ee = 0; ee < MAXGLOBALVARS; ee++)
1197         play.globalvars[ee] = 0;
1198 
1199     for (ee = 0; ee < MAXGLOBALSTRINGS; ee++)
1200         play.globalstrings[ee][0] = 0;
1201 
1202     for (ee = 0; ee < MAX_SOUND_CHANNELS; ee++)
1203         last_sound_played[ee] = -1;
1204 
1205     if (!usetup.translation.IsEmpty())
1206         init_translation (usetup.translation, "", true);
1207 
1208     update_invorder();
1209     displayed_room = -10;
1210 
1211     currentcursor=0;
1212     our_eip=-4;
1213     mousey=100;  // stop icon bar popping up
1214 
1215     // We use same variable to read config and be used at runtime for now,
1216     // so update it here with regards to game design option
1217     usetup.RenderAtScreenRes =
1218         (game.options[OPT_RENDERATSCREENRES] == kRenderAtScreenRes_UserDefined && usetup.RenderAtScreenRes) ||
1219          game.options[OPT_RENDERATSCREENRES] == kRenderAtScreenRes_Enabled;
1220 }
1221 
engine_setup_scsystem_auxiliary()1222 void engine_setup_scsystem_auxiliary()
1223 {
1224     // ScriptSystem::aci_version is only 10 chars long
1225     strncpy(scsystem.aci_version, EngineVersion.LongString, 10);
1226     if (usetup.override_script_os >= 0)
1227     {
1228         scsystem.os = usetup.override_script_os;
1229     }
1230     else
1231     {
1232         scsystem.os = platform->GetSystemOSID();
1233     }
1234 }
1235 
engine_update_mp3_thread()1236 void engine_update_mp3_thread()
1237 {
1238   update_mp3_thread();
1239   platform->Delay(50);
1240 }
1241 
engine_start_multithreaded_audio()1242 void engine_start_multithreaded_audio()
1243 {
1244   // PSP: Initialize the sound cache.
1245   clear_sound_cache();
1246 
1247   // Create sound update thread. This is a workaround for sound stuttering.
1248   if (psp_audio_multithreaded)
1249   {
1250     if (!audioThread.CreateAndStart(engine_update_mp3_thread, true))
1251     {
1252       Debug::Printf(kDbgMsg_Init, "Failed to start audio thread, audio will be processed on the main thread");
1253       psp_audio_multithreaded = 0;
1254     }
1255     else
1256     {
1257       Debug::Printf(kDbgMsg_Init, "Audio thread started");
1258     }
1259   }
1260   else
1261   {
1262     Debug::Printf(kDbgMsg_Init, "Audio is processed on the main thread");
1263   }
1264 }
1265 
engine_prepare_to_start_game()1266 void engine_prepare_to_start_game()
1267 {
1268     Debug::Printf("Prepare to start game");
1269 
1270     engine_setup_scsystem_auxiliary();
1271     engine_start_multithreaded_audio();
1272 
1273 #if defined(ANDROID_VERSION)
1274     if (psp_load_latest_savegame)
1275         selectLatestSavegame();
1276 #endif
1277 }
1278 
1279 // TODO: move to test unit
1280 Bitmap *test_allegro_bitmap;
1281 IDriverDependantBitmap *test_allegro_ddb;
allegro_bitmap_test_init()1282 void allegro_bitmap_test_init()
1283 {
1284 	test_allegro_bitmap = NULL;
1285 	// Switched the test off for now
1286 	//test_allegro_bitmap = AllegroBitmap::CreateBitmap(320,200,32);
1287 }
1288 
engine_init_gamefile(const String & exe_path)1289 bool engine_init_gamefile(const String &exe_path)
1290 {
1291     Debug::Printf(kDbgMsg_Init, "Initializing game data");
1292     // Read game data location from the default config file.
1293     // This is an optional setting that may instruct which game file to use as a primary asset library.
1294     ConfigTree cfg;
1295     String def_cfg_file = find_default_cfg_file(exe_path);
1296     IniUtil::Read(def_cfg_file, cfg);
1297     read_game_data_location(cfg);
1298 
1299     // Deduce the game data file location and initialize assets library.
1300     if (!engine_init_game_data())
1301         return false;
1302 
1303     // Pre-load game name and savegame folder names from data file
1304     // TODO: research if that is possible to avoid this step and just
1305     // read the full head game data at this point. This might require
1306     // further changes of the order of initialization.
1307     String err_str;
1308     if (!preload_game_data(err_str))
1309     {
1310         display_game_file_error(err_str);
1311         return false;
1312     }
1313     return true;
1314 }
1315 
engine_read_config(const String & exe_path,ConfigTree & cfg)1316 void engine_read_config(const String &exe_path, ConfigTree &cfg)
1317 {
1318     // Read default configuration file
1319     String def_cfg_file = find_default_cfg_file(exe_path);
1320     IniUtil::Read(def_cfg_file, cfg);
1321 
1322     // Disabled on Windows because people were afraid that this config could be mistakenly
1323     // created by some installer and screw up their games. Until any kind of solution is found.
1324     String user_global_cfg_file;
1325 #if !defined (WINDOWS_VERSION)
1326     // Read user global configuration file
1327     user_global_cfg_file = find_user_global_cfg_file();
1328     if (Path::ComparePaths(user_global_cfg_file, def_cfg_file) != 0)
1329         IniUtil::Read(user_global_cfg_file, cfg);
1330 #endif
1331 
1332     // Read user configuration file
1333     String user_cfg_file = find_user_cfg_file();
1334     if (Path::ComparePaths(user_cfg_file, def_cfg_file) != 0 &&
1335         Path::ComparePaths(user_cfg_file, user_global_cfg_file) != 0)
1336         IniUtil::Read(user_cfg_file, cfg);
1337 
1338     // Apply overriding options from mobile port settings
1339     // TODO: normally, those should be instead stored in the same config file in a uniform way
1340     // NOTE: the variable is historically called "ignore" but we use it in "override" meaning here
1341     if (psp_ignore_acsetup_cfg_file)
1342         override_config_ext(cfg);
1343 
1344     // Apply overriding options from command line
1345     // TODO: override config tree with all the command-line args.
1346     if (disable_log_file)
1347         INIwriteint(cfg, "misc", "log", 0);
1348     else if (enable_log_file)
1349         INIwriteint(cfg, "misc", "log", 1);
1350 }
1351 
engine_do_config(const String & exe_path)1352 bool engine_do_config(const String &exe_path)
1353 {
1354     Debug::Printf(kDbgMsg_Init, "Setting up game configuration");
1355     // Init default options
1356     config_defaults();
1357     ConfigTree cfg;
1358     // Read configuration files
1359     engine_read_config(exe_path, cfg);
1360     // Set up game options from user config
1361     apply_config(cfg);
1362     // Fixup configuration if necessary
1363     post_config();
1364     // Test if need to run built-in setup program (where available)
1365     return engine_check_run_setup(exe_path, cfg);
1366 }
1367 
initialize_engine(int argc,char * argv[])1368 int initialize_engine(int argc,char*argv[])
1369 {
1370     if (engine_pre_init_callback) {
1371         engine_pre_init_callback();
1372     }
1373 
1374     int res;
1375     if (!engine_init_allegro())
1376         return EXIT_NORMAL;
1377 
1378     const String exe_path = argv[0];
1379     if (!engine_init_gamefile(exe_path))
1380         return EXIT_NORMAL;
1381     if (!engine_do_config(exe_path))
1382         return EXIT_NORMAL;
1383 
1384     engine_setup_allegro();
1385 
1386     engine_setup_window();
1387 
1388     our_eip = -196;
1389 
1390     engine_force_window();
1391 
1392     our_eip = -195;
1393 
1394     our_eip = -192;
1395 
1396     engine_init_fonts();
1397 
1398     our_eip = -188;
1399 
1400     engine_init_mouse();
1401 
1402     our_eip = -187;
1403 
1404     res = engine_check_memory();
1405     if (res != RETURN_CONTINUE) {
1406         return res;
1407     }
1408 
1409     engine_init_directories();
1410 
1411     engine_init_rooms();
1412 
1413     our_eip = -186;
1414 
1415     engine_init_speech();
1416 
1417     our_eip = -185;
1418 
1419     engine_init_digital_audio();
1420 
1421     our_eip = -184;
1422 
1423     engine_init_keyboard();
1424 
1425     our_eip = -183;
1426 
1427     engine_init_timer();
1428 
1429     our_eip = -182;
1430 
1431     engine_init_sound();
1432 
1433     engine_init_debug();
1434 
1435     our_eip = -10;
1436 
1437     engine_init_exit_handler();
1438 
1439     // [IKM] I seriously don't get it why do we need to delete warnings.log
1440     // in the middle of procedure; some warnings may have already being
1441     // written there at this point, no?
1442     unlink("warnings.log");
1443 
1444     engine_init_rand();
1445 
1446     engine_init_pathfinder();
1447 
1448     //engine_pre_init_gfx();
1449 
1450     LOCK_VARIABLE(timerloop);
1451     LOCK_FUNCTION(dj_timer_handler);
1452     set_game_speed(40);
1453 
1454     our_eip=-20;
1455     //thisroom.allocall();
1456     our_eip=-19;
1457     //setup_sierra_interface();   // take this out later
1458 
1459     res = engine_load_game_data();
1460     if (res != RETURN_CONTINUE) {
1461         return res;
1462     }
1463 
1464     res = engine_check_register_game();
1465     if (res != RETURN_CONTINUE) {
1466         return res;
1467     }
1468 
1469     engine_init_title();
1470 
1471     our_eip = -189;
1472 
1473     res = engine_check_disk_space();
1474     if (res != RETURN_CONTINUE) {
1475         return res;
1476     }
1477 
1478     // Make sure that at least one font was loaded in the process of loading
1479     // the game data.
1480     // TODO: Fold this check into engine_load_game_data()
1481     res = engine_check_font_was_loaded();
1482     if (res != RETURN_CONTINUE) {
1483         return res;
1484     }
1485 
1486     our_eip = -179;
1487 
1488     engine_init_modxm_player();
1489 
1490     engine_init_resolution_settings(game.size);
1491 
1492     // Attempt to initialize graphics mode
1493     if (!engine_try_set_gfxmode_any(usetup.Screen))
1494         return EXIT_NORMAL;
1495 
1496     SetMultitasking(0);
1497 
1498     // [ER] 2014-03-13
1499     // Hide the system cursor via allegro
1500     show_os_cursor(MOUSE_CURSOR_NONE);
1501 
1502     engine_show_preload();
1503 
1504     res = engine_init_sprites();
1505     if (res != RETURN_CONTINUE) {
1506         return res;
1507     }
1508 
1509     engine_init_game_settings();
1510 
1511     engine_prepare_to_start_game();
1512 
1513 	allegro_bitmap_test_init();
1514 
1515     initialize_start_and_play_game(override_start_room, loadSaveGameOnStartup);
1516 
1517     quit("|bye!");
1518     return 0;
1519 }
1520 
engine_try_set_gfxmode_any(const ScreenSetup & setup)1521 bool engine_try_set_gfxmode_any(const ScreenSetup &setup)
1522 {
1523     engine_shutdown_gfxmode();
1524 
1525     const Size init_desktop = get_desktop_size();
1526     if (!graphics_mode_init_any(game.size, setup, ColorDepthOption(game.GetColorDepth())))
1527         return false;
1528 
1529     engine_post_gfxmode_setup(init_desktop);
1530     return true;
1531 }
1532 
engine_try_switch_windowed_gfxmode()1533 bool engine_try_switch_windowed_gfxmode()
1534 {
1535     if (!gfxDriver || !gfxDriver->IsModeSet())
1536         return false;
1537 
1538     // Keep previous mode in case we need to revert back
1539     DisplayMode old_dm = gfxDriver->GetDisplayMode();
1540     GameFrameSetup old_frame = graphics_mode_get_render_frame();
1541 
1542     // Release engine resources that depend on display mode
1543     engine_pre_gfxmode_release();
1544 
1545     Size init_desktop = get_desktop_size();
1546     bool switch_to_windowed = !old_dm.Windowed;
1547     ActiveDisplaySetting setting = graphics_mode_get_last_setting(switch_to_windowed);
1548     DisplayMode last_opposite_mode = setting.Dm;
1549     GameFrameSetup use_frame_setup = setting.FrameSetup;
1550 
1551     // If there are saved parameters for given mode (fullscreen/windowed)
1552     // then use them, if there are not, get default setup for the new mode.
1553     bool res;
1554     if (last_opposite_mode.IsValid())
1555     {
1556         res = graphics_mode_set_dm(last_opposite_mode);
1557     }
1558     else
1559     {
1560         // we need to clone from initial config, because not every parameter is set by graphics_mode_get_defaults()
1561         DisplayModeSetup dm_setup = usetup.Screen.DisplayMode;
1562         dm_setup.Windowed = !old_dm.Windowed;
1563         graphics_mode_get_defaults(dm_setup.Windowed, dm_setup.ScreenSize, use_frame_setup);
1564         res = graphics_mode_set_dm_any(game.size, dm_setup, old_dm.ColorDepth, use_frame_setup);
1565     }
1566 
1567     // Apply corresponding frame render method
1568     if (res)
1569         res = graphics_mode_set_render_frame(use_frame_setup);
1570 
1571     if (!res)
1572     {
1573         // If failed, try switching back to previous gfx mode
1574         res = graphics_mode_set_dm(old_dm) &&
1575               graphics_mode_set_render_frame(old_frame);
1576     }
1577 
1578     if (res)
1579     {
1580         // If succeeded (with any case), update engine objects that rely on
1581         // active display mode.
1582         if (gfxDriver->GetDisplayMode().Windowed)
1583             init_desktop = get_desktop_size();
1584         engine_post_gfxmode_setup(init_desktop);
1585     }
1586     clear_input_buffer();
1587     return res;
1588 }
1589 
engine_shutdown_gfxmode()1590 void engine_shutdown_gfxmode()
1591 {
1592     if (!gfxDriver)
1593         return;
1594 
1595     engine_pre_gfxsystem_shutdown();
1596     graphics_mode_shutdown();
1597 }
1598 
1599 
1600 #ifdef WINDOWS_VERSION
1601 // in ac_minidump
1602 extern int CustomExceptionHandler (LPEXCEPTION_POINTERS exinfo);
1603 extern EXCEPTION_RECORD excinfo;
1604 extern int miniDumpResultCode;
1605 #endif
1606 
1607 // defined in main/main
1608 extern char tempmsg[100];
1609 extern char*printfworkingspace;
1610 
initialize_engine_with_exception_handling(int argc,char * argv[])1611 int initialize_engine_with_exception_handling(int argc,char*argv[])
1612 {
1613 #ifdef USE_CUSTOM_EXCEPTION_HANDLER
1614     __try
1615     {
1616         Debug::Printf(kDbgMsg_Init, "Installing exception handler");
1617 #endif
1618 
1619         return initialize_engine(argc, argv);
1620 
1621 #ifdef USE_CUSTOM_EXCEPTION_HANDLER
1622     }
1623     __except (CustomExceptionHandler ( GetExceptionInformation() ))
1624     {
1625         strcpy (tempmsg, "");
1626         sprintf (printfworkingspace, "An exception 0x%X occurred in ACWIN.EXE at EIP = 0x%08X %s; program pointer is %+d, ACI version %s, gtags (%d,%d)\n\n"
1627             "AGS cannot continue, this exception was fatal. Please note down the numbers above, remember what you were doing at the time and post the details on the AGS Technical Forum.\n\n%s\n\n"
1628             "Most versions of Windows allow you to press Ctrl+C now to copy this entire message to the clipboard for easy reporting.\n\n%s (code %d)",
1629             excinfo.ExceptionCode, excinfo.ExceptionAddress, tempmsg, our_eip, EngineVersion.LongString.GetCStr(), eip_guinum, eip_guiobj, get_cur_script(5),
1630             (miniDumpResultCode == 0) ? "An error file CrashInfo.dmp has been created. You may be asked to upload this file when reporting this problem on the AGS Forums." :
1631             "Unable to create an error dump file.", miniDumpResultCode);
1632         MessageBoxA(win_get_window(), printfworkingspace, "Illegal exception", MB_ICONSTOP | MB_OK);
1633         proper_exit = 1;
1634     }
1635     return EXIT_CRASH;
1636 #endif
1637 }
1638 
get_engine_version()1639 const char *get_engine_version() {
1640     return EngineVersion.LongString.GetCStr();
1641 }
1642 
engine_set_pre_init_callback(t_engine_pre_init_callback callback)1643 void engine_set_pre_init_callback(t_engine_pre_init_callback callback) {
1644     engine_pre_init_callback = callback;
1645 }
1646