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