DEADSOFTWARE

system: remove sys_GetTicks
[d2df-sdl.git] / src / game / Doom2DF.lpr
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 {$IFDEF ANDROID}library{$ELSE}program{$ENDIF} Doom2DF;
18 {$IFNDEF HEADLESS}
19 {$IFDEF WINDOWS}
20 {$APPTYPE GUI}
21 {$ENDIF}
22 {$ENDIF}
23 {$HINTS OFF}
25 uses
26 {$IFDEF ANDROID}
27 ctypes, jni,
28 {$ENDIF}
29 {$IFDEF UNIX}
30 cthreads, BaseUnix,
31 {$ENDIF}
32 {$IFDEF DARWIN}
33 MacOSAll, CocoaAll,
34 {$ENDIF}
35 mempool in '../shared/mempool.pas',
36 conbuf in '../shared/conbuf.pas',
37 geom in '../shared/geom.pas',
38 math,
40 {$IFDEF USE_MINIUPNPC}
41 miniupnpc in '../lib/miniupnpc/miniupnpc.pas',
42 {$ENDIF}
44 {$IFDEF USE_SDL}
45 SDL,
46 {$IFDEF USE_SDLMIXER}
47 SDL_mixer,
48 {$ENDIF}
49 {$ENDIF}
50 {$IFDEF USE_SDL2}
51 SDL2 in '../lib/sdl2/sdl2.pas',
52 {$IFDEF USE_SDLMIXER}
53 SDL2_mixer in '../lib/sdl2/SDL2_mixer.pas',
54 {$ENDIF}
55 {$ENDIF}
56 {$IFDEF USE_SYSSTUB}
57 {$IFDEF USE_SDLMIXER}
58 SDL2 in '../lib/sdl2/sdl2.pas',
59 SDL2_mixer in '../lib/sdl2/SDL2_mixer.pas',
60 {$ENDIF}
61 {$ENDIF}
63 {$IFDEF USE_OPENAL}
64 AL in '../lib/openal/al.pas',
65 e_soundfile in '../engine/e_soundfile.pas',
66 {$IF DEFINED(USE_SDL) OR DEFINED(USE_SDL2)}
67 e_soundfile_wav in '../engine/e_soundfile_wav.pas',
68 {$ENDIF}
69 {$IFDEF USE_VORBIS}
70 vorbis in '../lib/vorbis/vorbis.pas',
71 e_soundfile_vorbis in '../engine/e_soundfile_vorbis.pas',
72 {$ENDIF}
73 {$IFDEF USE_FLUIDSYNTH}
74 fluidsynth in '../lib/fluidsynth/fluidsynth.pas',
75 e_soundfile_fluid in '../engine/e_soundfile_fluid.pas',
76 {$ENDIF}
77 {$IFDEF USE_MODPLUG}
78 modplug in '../lib/modplug/modplug.pas',
79 e_soundfile_modplug in '../engine/e_soundfile_modplug.pas',
80 {$ENDIF}
81 {$IFDEF USE_XMP}
82 xmp in '../lib/xmp/xmp.pas',
83 e_soundfile_xmp in '../engine/e_soundfile_xmp.pas',
84 {$ENDIF}
85 {$IFDEF USE_GME}
86 gme in '../lib/gme/gme.pas',
87 e_soundfile_gme in '../engine/e_soundfile_gme.pas',
88 {$ENDIF}
89 {$IFDEF USE_MPG123}
90 mpg123 in '../lib/mpg123/mpg123.pas',
91 e_soundfile_mp3 in '../engine/e_soundfile_mp3.pas',
92 {$ENDIF}
93 {$IFDEF USE_OPUS}
94 opus in '../lib/opus/opus.pas',
95 e_soundfile_opus in '../engine/e_soundfile_opus.pas',
96 {$ENDIF}
97 {$IF DEFINED(USE_VORBIS) OR DEFINED(USE_OPUS)}
98 ogg in '../lib/vorbis/ogg.pas', // this has to come last because link order
99 {$ENDIF}
100 {$ENDIF}
102 ENet in '../lib/enet/enet.pp',
103 e_input in '../engine/e_input.pas',
104 e_log in '../engine/e_log.pas',
105 e_sound in '../engine/e_sound.pas',
106 e_msg in '../engine/e_msg.pas',
107 e_res in '../engine/e_res.pas',
108 utils in '../shared/utils.pas',
109 xstreams in '../shared/xstreams.pas',
110 sfs in '../sfs/sfs.pas',
111 sfsPlainFS in '../sfs/sfsPlainFS.pas',
112 sfsZipFS in '../sfs/sfsZipFS.pas',
113 wadreader in '../shared/wadreader.pas',
114 MAPDEF in '../shared/MAPDEF.pas',
115 CONFIG in '../shared/CONFIG.pas',
116 g_base in 'g_base.pas',
117 g_basic in 'g_basic.pas',
118 g_console in 'g_console.pas',
119 g_net in 'g_net.pas',
120 g_netmsg in 'g_netmsg.pas',
121 g_nethandler in 'g_nethandler.pas',
122 g_netmaster in 'g_netmaster.pas',
123 g_res_downloader in 'g_res_downloader.pas',
124 g_grid in 'g_grid.pas',
125 g_game in 'g_game.pas',
126 g_gfx in 'g_gfx.pas',
127 g_gui in 'g_gui.pas',
128 g_items in 'g_items.pas',
129 g_map in 'g_map.pas',
130 g_menu in 'g_menu.pas',
131 g_monsters in 'g_monsters.pas',
132 g_options in 'g_options.pas',
133 g_phys in 'g_phys.pas',
134 g_player in 'g_player.pas',
135 g_playermodel in 'g_playermodel.pas',
136 g_saveload in 'g_saveload.pas',
137 g_sound in 'g_sound.pas',
138 g_textures in 'g_textures.pas',
139 g_triggers in 'g_triggers.pas',
140 g_weapons in 'g_weapons.pas',
141 g_window in 'g_window.pas',
142 {$IFDEF USE_SYSSTUB}
143 g_system in 'stub/g_system.pas',
144 g_touch in 'stub/g_touch.pas',
145 {$ENDIF}
146 {$IFDEF USE_SDL}
147 g_system in 'sdl/g_system.pas',
148 g_touch in 'sdl/g_touch.pas',
149 {$ENDIF}
150 {$IFDEF USE_SDL2}
151 g_system in 'sdl2/g_system.pas',
152 g_touch in 'sdl2/g_touch.pas',
153 {$ENDIF}
155 r_console in 'opengl/r_console.pas',
156 r_game in 'opengl/r_game.pas',
157 r_gfx in 'opengl/r_gfx.pas',
158 r_graphics in 'opengl/r_graphics.pas',
159 r_items in 'opengl/r_items.pas',
160 r_map in 'opengl/r_map.pas',
161 r_monsters in 'opengl/r_monsters.pas',
162 r_netmaster in 'opengl/r_netmaster.pas',
163 r_panel in 'opengl/r_panel.pas',
164 r_player in 'opengl/r_player.pas',
165 r_playermodel in 'opengl/r_playermodel.pas',
166 r_texture in 'opengl/r_texture.pas',
167 r_weapons in 'opengl/r_weapons.pas',
168 r_window in 'opengl/r_window.pas',
169 r_render in 'opengl/r_render.pas',
171 {$IFDEF USE_FMOD}
172 fmod in '../lib/FMOD/fmod.pas',
173 fmoderrors in '../lib/FMOD/fmoderrors.pas',
174 fmodpresets in '../lib/FMOD/fmodpresets.pas',
175 fmodtypes in '../lib/FMOD/fmodtypes.pas',
176 {$ENDIF}
177 xprofiler in '../shared/xprofiler.pas',
178 binheap in '../shared/binheap.pas',
179 hashtable in '../shared/hashtable.pas',
180 fhashdb in '../shared/fhashdb.pas',
181 idpool in '../shared/idpool.pas',
182 xparser in '../shared/xparser.pas',
183 xdynrec in '../shared/xdynrec.pas',
184 exoma in '../shared/exoma.pas',
185 envvars in '../shared/envvars.pas',
186 g_panel in 'g_panel.pas',
187 g_language in 'g_language.pas',
189 {$IFDEF ENABLE_HOLMES}
190 g_holmes in 'g_holmes.pas',
192 sdlcarcass in '../flexui/sdlcarcass.pas',
193 //sdlstandalone in '../flexui/sdlstandalone.pas',
195 fui_wadread in '../flexui/fui_wadread.pas',
196 fui_common in '../flexui/fui_common.pas',
197 fui_gfx_gl in '../flexui/fui_gfx_gl.pas',
198 fui_events in '../flexui/fui_events.pas',
199 fui_style in '../flexui/fui_style.pas',
200 fui_flexlay in '../flexui/fui_flexlay.pas',
201 fui_ctls in '../flexui/fui_ctls.pas',
202 {$ENDIF}
203 {$I ../shared/vampimg.inc}
205 SysUtils, Classes;
207 {$IFDEF WINDOWS}
208 {$R *.res}
209 {$ENDIF}
211 const
212 autoexecScript = 'autoexec.cfg';
214 var
215 noct: Boolean = False;
216 binPath: AnsiString = '';
217 forceBinDir: Boolean = False;
219 Time_Old: Int64;
220 NoSound: Boolean;
222 procedure Update ();
223 begin
224 // remember old mobj positions, prepare for update
225 g_Game_PreUpdate();
226 // server: receive client commands for new frame
227 // client: receive game state changes from server
228 if (NetMode = NET_SERVER) then g_Net_Host_Update()
229 else if (NetMode = NET_CLIENT) then g_Net_Client_Update();
230 // think
231 g_Game_Update();
232 // server: send any accumulated outgoing data to clients
233 if NetMode = NET_SERVER then g_Net_Flush();
234 end;
236 function ProcessMessage (): Boolean;
237 var
238 i, t: Integer;
239 flag: Boolean;
240 Time, Time_Delta: Int64;
241 Frame: Int64;
242 begin
243 result := sys_HandleInput();
245 Time := GetTickCount64();
246 Time_Delta := Time-Time_Old;
248 flag := false;
250 if wNeedTimeReset then
251 begin
252 Frame := 0;
253 Time_Delta := 28;
254 wNeedTimeReset := false;
255 end;
257 g_Map_ProfilersBegin();
258 g_Mons_ProfilersBegin();
260 t := Time_Delta div 28;
261 if (t > 0) then
262 begin
263 flag := true;
264 for i := 1 to t do
265 Update();
266 end;
268 g_Map_ProfilersEnd();
269 g_Mons_ProfilersEnd();
271 if (gExit = EXIT_QUIT) then
272 begin
273 result := true;
274 exit;
275 end;
277 // Время предыдущего обновления
278 if flag then
279 Time_Old := Time - (Time_Delta mod 28);
281 // don't wait if VSync is on, GL already probably waits enough
282 if gLerpActors then
283 flag := (Time - Frame >= gFrameTime) or gVSync;
285 if flag then
286 begin
287 if gPause or (not gLerpActors) or (gState = STATE_FOLD) then
288 gLerpFactor := 1.0
289 else
290 gLerpFactor := nmin(1.0, (Time - Time_Old) / 28.0);
291 r_Game_Draw;
292 sys_Repaint;
293 Frame := Time
294 end
295 else
296 sys_Delay(1);
298 e_SoundUpdate();
299 end;
301 procedure DebugOptions;
302 var
303 idx: Integer;
304 arg: AnsiString;
305 mdfo: TStream;
306 {$IFDEF ENABLE_HOLMES}
307 itmp: Integer;
308 valres: Word;
309 {$ENDIF}
310 begin
311 idx := 1;
312 while (idx <= ParamCount) do
313 begin
314 arg := ParamStr(idx);
315 Inc(idx);
316 //if arg = '--twinkletwinkle' then gwin_k8_enable_light_experiments := true;
317 if arg = '--jah' then g_profile_history_size := 100;
318 if arg = '--no-particles' then gpart_dbg_enabled := false;
319 if arg = '--no-los' then gmon_dbg_los_enabled := false;
321 if arg = '--profile-render' then g_profile_frame_draw := true;
322 if arg = '--profile-coldet' then g_profile_collision := true;
323 if arg = '--profile-los' then g_profile_los := true;
325 if arg = '--no-part-phys' then gpart_dbg_phys_enabled := false;
326 if arg = '--no-part-physics' then gpart_dbg_phys_enabled := false;
327 if arg = '--no-particles-phys' then gpart_dbg_phys_enabled := false;
328 if arg = '--no-particles-physics' then gpart_dbg_phys_enabled := false;
329 if arg = '--no-particle-phys' then gpart_dbg_phys_enabled := false;
330 if arg = '--no-particle-physics' then gpart_dbg_phys_enabled := false;
332 if arg = '--debug-input' then g_dbg_input := True;
334 {.$IF DEFINED(D2F_DEBUG)}
335 if arg = '--aimline' then g_dbg_aimline_on := true;
336 {.$ENDIF}
338 {$IFDEF ENABLE_HOLMES}
339 if arg = '--holmes' then begin g_holmes_enabled := true; g_Game_SetDebugMode(); end;
341 if (arg = '--holmes-ui-scale') or (arg = '-holmes-ui-scale') then
342 begin
343 if (idx <= ParamCount) then
344 begin
345 if not conParseFloat(fuiRenderScale, ParamStr(idx)) then fuiRenderScale := 1.0;
346 Inc(idx);
347 end;
348 end;
350 if (arg = '--holmes-font') or (arg = '-holmes-font') then
351 begin
352 if (idx <= ParamCount) then
353 begin
354 itmp := 0;
355 val(ParamStr(idx), itmp, valres);
356 {$IFNDEF HEADLESS}
357 if (valres = 0) and (not g_holmes_imfunctional) then
358 begin
359 case itmp of
360 8: uiContext.font := 'win8';
361 14: uiContext.font := 'win14';
362 16: uiContext.font := 'win16';
363 end;
364 end;
365 {$ELSE}
366 // fuck off, fpc!
367 itmp := itmp;
368 valres := valres;
369 {$ENDIF}
370 Inc(idx);
371 end;
372 end;
373 {$ENDIF}
375 if (arg = '--game-scale') or (arg = '-game-scale') then
376 begin
377 if (idx <= ParamCount) then
378 begin
379 if not conParseFloat(g_dbg_scale, ParamStr(idx)) then g_dbg_scale := 1.0;
380 Inc(idx);
381 end;
382 end;
384 if (arg = '--write-mapdef') or (arg = '-write-mapdef') then
385 begin
386 mdfo := createDiskFile('mapdef.txt');
387 mdfo.WriteBuffer(defaultMapDef[1], Length(defaultMapDef));
388 mdfo.Free();
389 Halt(0);
390 end;
392 if (arg = '--pixel-scale') or (arg = '-pixel-scale') then
393 begin
394 if (idx <= ParamCount) then
395 begin
396 if not conParseFloat(r_pixel_scale, ParamStr(idx)) then r_pixel_scale := 1.0;
397 Inc(idx);
398 end;
399 end;
400 end;
401 end;
403 function GetBinaryPath (): AnsiString;
404 {$IFDEF LINUX}
405 var sl: AnsiString;
406 {$ENDIF}
407 begin
408 result := ExtractFilePath(ParamStr(0));
409 {$IFDEF LINUX}
410 // it may be a symlink; do some guesswork here
411 sl := fpReadLink(ExtractFileName(ParamStr(0)));
412 if (sl = ParamStr(0)) then
413 begin
414 // use current directory, as we don't have anything better
415 //result := '.';
416 GetDir(0, result);
417 end;
418 {$ENDIF}
419 result := fixSlashes(result);
420 if (length(result) > 0) and (result[length(result)] <> '/') then
421 result := result + '/';
422 end;
424 procedure PrintDirs (msg: AnsiString; dirs: SSArray);
425 var dir: AnsiString;
426 begin
427 e_LogWriteln(msg + ':');
428 for dir in dirs do
429 e_LogWriteln(' ' + dir);
430 end;
432 {$IFDEF DARWIN}
433 function NSStringToAnsiString (s: NSString): AnsiString;
434 var i: Integer;
435 begin
436 result := '';
437 for i := 0 to s.length - 1 do
438 result := result + AnsiChar(s.characterAtIndex(i));
439 end;
441 function GetBundlePath (): AnsiString;
442 var pathRef: CFURLRef; pathCFStr: CFStringRef; pathStr: ShortString;
443 begin
444 pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
445 pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);
446 CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());
447 CFRelease(pathRef);
448 CFRelease(pathCFStr);
449 Result := pathStr;
450 end;
451 {$ENDIF}
453 procedure InitPath;
454 var i: Integer; rwdir, rodir: AnsiString; rwdirs, rodirs: SSArray;
456 procedure AddDir (var dirs: SSArray; append: AnsiString);
457 begin
458 SetLength(dirs, Length(dirs) + 1);
459 dirs[High(dirs)] := ExpandFileName(append)
460 end;
462 function IsSep (ch: Char): Boolean;
463 begin
464 {$IFDEF WINDOWS}
465 result := (ch = '/') or (ch = '\');
466 {$ELSE}
467 result := (ch = '/');
468 {$ENDIF}
469 end;
471 function OptimizePath (dir: AnsiString): AnsiString;
472 var i, len: Integer; s: AnsiString;
473 begin
474 i := 1; len := Length(dir); s := '';
475 while i <= len do
476 begin
477 if IsSep(dir[i]) then
478 begin
479 s := s + DirectorySeparator;
480 Inc(i);
481 while (i <= len) and IsSep(dir[i]) do Inc(i);
482 if (i <= len) and (dir[i] = '.') then
483 begin
484 if (i = len) or IsSep(dir[i + 1]) then
485 begin
486 Inc(i)
487 end
488 else if (i + 1 <= len) and (dir[i + 1] = '.') then
489 begin
490 if (i + 1 = len) or IsSep(dir[i + 2]) then
491 begin
492 s := e_UpperDir(s);
493 Inc(i, 2)
494 end
495 end
496 end
497 end
498 else
499 begin
500 s := s + dir[i];
501 Inc(i)
502 end
503 end;
504 result := s
505 end;
507 procedure OptimizeDirs (var dirs: SSArray);
508 var i, j, k: Integer;
509 begin
510 for i := 0 to High(dirs) do
511 dirs[i] := OptimizePath(dirs[i]);
512 // deduplicate
513 i := High(dirs);
514 while i >= 0 do
515 begin
516 j := 0;
517 while j < i do
518 begin
519 if dirs[j] = dirs[i] then
520 begin
521 for k := j + 1 to High(dirs) do
522 dirs[k - 1] := dirs[k];
523 Dec(i);
524 SetLength(dirs, High(dirs))
525 end
526 else
527 begin
528 Inc(j)
529 end
530 end;
531 Dec(i)
532 end
533 end;
535 procedure AddDef (var dirs: SSArray; base: SSArray; append: AnsiString);
536 var s: AnsiString;
537 begin
538 if Length(dirs) = 0 then
539 for s in base do
540 AddDir(dirs, e_CatPath(s, append));
541 OptimizeDirs(dirs)
542 end;
544 function GetDefaultRODirs (): SSArray;
545 {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
546 var home: AnsiString;
547 {$ENDIF}
548 {$IFDEF WINDOWS}
549 var appdata: AnsiString;
550 {$ENDIF}
551 {$IFDEF DARWIN}
552 var bundle, s: AnsiString; dirArr: NSArray; i: Integer;
553 {$ENDIF}
554 begin
555 result := nil;
556 {$IFDEF DARWIN}
557 bundle := GetBundlePath();
558 if ExtractFileExt(bundle) <> '.app' then
559 AddDir(result, binpath);
560 {$ELSE}
561 AddDir(result, binPath);
562 {$ENDIF}
563 if forceBinDir = false then
564 begin
565 {$IFDEF USE_SDL2}
566 AddDir(result, SDL_GetBasePath());
567 AddDir(result, SDL_GetPrefPath('', 'doom2df'));
568 {$ENDIF}
569 {$IFDEF WINDOWS}
570 appdata := GetEnvironmentVariable('APPDATA') + '\doom2df';
571 if appdata <> '' then
572 AddDir(result, appdata);
573 {$ENDIF}
574 {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
575 AddDir(result, '/usr/share/doom2df');
576 AddDir(result, '/usr/local/share/doom2df');
577 home := GetEnvironmentVariable('HOME');
578 if home <> '' then
579 AddDir(result, e_CatPath(home, '.doom2df'));
580 {$ENDIF}
581 {$IFDEF DARWIN}
582 bundle := GetBundlePath();
583 if bundle <> '' then
584 AddDir(result, e_CatPath(bundle, 'Contents/Resources'));
585 dirArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true);
586 for i := 0 to dirArr.count - 1 do
587 begin
588 s := NSStringToAnsiString(dirArr.objectAtIndex(i));
589 AddDir(result, e_CatPath(s, 'Doom 2D Forever'))
590 end;
591 {$ENDIF}
592 {$IF DEFINED(ANDROID) AND DEFINED(USE_SDL2)}
593 AddDir(result, SDL_AndroidGetInternalStoragePath());
594 if SDL_AndroidGetExternalStorageState() <> 0 then
595 AddDir(result, SDL_AndroidGetExternalStoragePath());
596 {$ENDIF}
597 end
598 end;
600 function GetDefaultRWDirs (): SSArray;
601 {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
602 var home: AnsiString;
603 {$ENDIF}
604 {$IFDEF WINDOWS}
605 var appdata: AnsiString;
606 {$ENDIF}
607 {$IFDEF DARWIN}
608 var bundle, s: AnsiString; dirArr: NSArray; i: Integer;
609 {$ENDIF}
610 begin
611 result := nil;
612 {$IFDEF DARWIN}
613 bundle := GetBundlePath();
614 if ExtractFileExt(bundle) <> '.app' then
615 AddDir(result, binPath);
616 {$ELSE}
617 AddDir(result, binPath);
618 {$ENDIF}
619 if forceBinDir = false then
620 begin
621 {$IFDEF USE_SDL2}
622 AddDir(result, SDL_GetPrefPath('', 'doom2df'));
623 {$ENDIF}
624 {$IFDEF WINDOWS}
625 appdata := GetEnvironmentVariable('APPDATA') + '\doom2df';
626 if appdata <> '' then
627 AddDir(result, appdata);
628 {$ENDIF}
629 {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
630 home := GetEnvironmentVariable('HOME');
631 if home <> '' then
632 AddDir(result, e_CatPath(home, '.doom2df'));
633 {$ENDIF}
634 {$IFDEF DARWIN}
635 dirArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true);
636 for i := 0 to dirArr.count - 1 do
637 begin
638 s := NSStringToAnsiString(dirArr.objectAtIndex(i));
639 AddDir(result, e_CatPath(s, 'Doom 2D Forever'))
640 end;
641 {$ENDIF}
642 {$IF DEFINED(ANDROID) AND DEFINED(USE_SDL2)}
643 if SDL_AndroidGetExternalStorageState() <> 0 then
644 AddDir(result, SDL_AndroidGetExternalStoragePath());
645 {$ENDIF}
646 end
647 end;
649 begin
650 forceBinDir := false;
651 binPath := GetBinaryPath();
653 i := 1;
654 while i < ParamCount do
655 begin
656 case ParamStr(i) of
657 '--like-windoze': forceBinDir := true;
658 '--rw-dir':
659 begin
660 Inc(i);
661 rwdir := ParamStr(i);
662 (* RW *)
663 AddDir(LogDirs, e_CatPath(rwdir, ''));
664 AddDir(SaveDirs, e_CatPath(rwdir, 'data'));
665 AddDir(CacheDirs, e_CatPath(rwdir, 'data/cache'));
666 AddDir(ConfigDirs, e_CatPath(rwdir, ''));
667 AddDir(MapDownloadDirs, e_CatPath(rwdir, 'maps/downloads'));
668 AddDir(WadDownloadDirs, e_CatPath(rwdir, 'wads/downloads'));
669 AddDir(ScreenshotDirs, e_CatPath(rwdir, 'screenshots'));
670 AddDir(StatsDirs, e_CatPath(rwdir, 'stats'));
671 (* RO *)
672 AddDir(DataDirs, e_CatPath(rwdir, 'data'));
673 AddDir(ModelDirs, e_CatPath(rwdir, 'data/models'));
674 AddDir(MegawadDirs, e_CatPath(rwdir, 'maps/megawads'));
675 AddDir(MapDirs, e_CatPath(rwdir, 'maps'));
676 AddDir(WadDirs, e_CatPath(rwdir, 'wads'));
677 end;
678 '--ro-dir':
679 begin
680 Inc(i);
681 rodir := ParamStr(i);
682 (* RO *)
683 AddDir(DataDirs, e_CatPath(rodir, 'data'));
684 AddDir(ModelDirs, e_CatPath(rodir, 'data/models'));
685 AddDir(MegawadDirs, e_CatPath(rodir, 'maps/megawads'));
686 AddDir(MapDirs, e_CatPath(rodir, 'maps'));
687 AddDir(WadDirs, e_CatPath(rodir, 'wads'));
688 end;
689 '--game-wad':
690 begin
691 Inc(i);
692 GameWADName := ParamStr(i);
693 end;
694 '--config':
695 begin
696 Inc(i);
697 gConfigScript := ParamStr(i);
698 end;
699 end;
700 Inc(i)
701 end;
703 // prefer bin dir if it writable and contains game.wad
704 if forceBinDir = false then
705 begin
706 if findDiskWad(binPath + 'data' + '/' + GameWADName) <> '' then
707 if e_CanCreateFilesAt(binPath) then
708 forceBinDir := true
709 end;
711 (* RO *)
712 rodirs := GetDefaultRODirs();
713 AddDef(DataDirs, rodirs, 'data');
714 AddDef(ModelDirs, rodirs, 'data/models');
715 AddDef(MegawadDirs, rodirs, 'maps/megawads');
716 AddDef(MapDirs, rodirs, 'maps');
717 AddDef(WadDirs, rodirs, 'wads');
719 (* RW *)
720 rwdirs := GetDefaultRWDirs();
721 AddDef(LogDirs, rwdirs, '');
722 AddDef(SaveDirs, rwdirs, 'data');
723 AddDef(CacheDirs, rwdirs, 'data/cache');
724 AddDef(ConfigDirs, rwdirs, '');
725 AddDef(MapDownloadDirs, rwdirs, 'maps/downloads');
726 AddDef(WadDownloadDirs, rwdirs, 'wads/downloads');
727 AddDef(ScreenshotDirs, rwdirs, 'screenshots');
728 AddDef(StatsDirs, rwdirs, 'stats');
730 for i := 0 to High(MapDirs) do
731 AddDir(AllMapDirs, MapDirs[i]);
732 for i := 0 to High(MegawadDirs) do
733 AddDir(AllMapDirs, MegawadDirs[i]);
734 OptimizeDirs(AllMapDirs);
736 // HACK: ensure the screenshots folder also has a stats subfolder in it
737 rwdir := e_GetWriteableDir(ScreenshotDirs, false);
738 if rwdir <> '' then CreateDir(rwdir + '/stats');
739 end;
741 procedure EntryParams;
742 var i: Integer;
743 begin
744 i := 1;
745 while i <= ParamCount do
746 begin
747 case ParamStr(i) of
748 '--gdb': noct := true;
749 '--log', '--con-stdout': conbufDumpToStdOut := true;
750 '--safe-log': e_SetSafeSlowLog(true);
751 '--log-file':
752 if i + 1 <= ParamCount then
753 begin
754 Inc(i);
755 LogFileName := ParamStr(i)
756 end;
757 '--no-fbo': glRenderToFBO := false;
758 end;
759 Inc(i)
760 end
761 end;
763 procedure InitLog;
764 var rwdir: AnsiString;
765 begin
766 if LogFileName = '' then
767 begin
768 rwdir := e_GetWriteableDir(LogDirs, false);
769 if rwdir <> '' then
770 begin
771 {$IFDEF HEADLESS}
772 LogFileName := e_CatPath(rwdir, 'Doom2DF_H.log');
773 {$ELSE}
774 LogFileName := e_CatPath(rwdir, 'Doom2DF.log');
775 {$ENDIF}
776 end
777 end;
778 if LogFileName <> '' then
779 e_InitLog(LogFileName, TWriteMode.WM_NEWFILE);
780 e_InitWritelnDriver
781 end;
783 procedure InitPrep;
784 {$IF DEFINED(ANDROID) AND DEFINED(USE_SDLMIXER)}
785 var timiditycfg: AnsiString;
786 {$ENDIF}
787 begin
788 e_WriteLog('Doom 2D: Forever version ' + GAME_VERSION + ' proto ' + IntToStr(NET_PROTOCOL_VER), TMsgType.Notify);
789 e_WriteLog('Build date: ' + GAME_BUILDDATE + ' ' + GAME_BUILDTIME, TMsgType.Notify);
790 e_WriteLog('Build hash: ' + g_GetBuildHash(), TMsgType.Notify);
791 e_WriteLog('Build by: ' + g_GetBuilderName(), TMsgType.Notify);
793 e_LogWritefln('Force bin dir: %s', [forceBinDir], TMsgType.Notify);
794 e_LogWritefln('BINARY PATH: [%s]', [binPath], TMsgType.Notify);
796 PrintDirs('DataDirs', DataDirs);
797 PrintDirs('ModelDirs', ModelDirs);
798 PrintDirs('MegawadDirs', MegawadDirs);
799 PrintDirs('MapDirs', MapDirs);
800 PrintDirs('WadDirs', WadDirs);
802 PrintDirs('LogDirs', LogDirs);
803 PrintDirs('SaveDirs', SaveDirs);
804 PrintDirs('CacheDirs', CacheDirs);
805 PrintDirs('ConfigDirs', ConfigDirs);
806 PrintDirs('ScreenshotDirs', ScreenshotDirs);
807 PrintDirs('StatsDirs', StatsDirs);
808 PrintDirs('MapDownloadDirs', MapDownloadDirs);
809 PrintDirs('WadDownloadDirs', WadDownloadDirs);
811 {$IFDEF HEADLESS}
812 {$IFDEF USE_SDLMIXER}
813 NoSound := False; // hope env has set SDL_AUDIODRIVER to dummy
814 {$ELSE}
815 NoSound := True; // FMOD backend will sort it out
816 {$ENDIF}
817 {$ELSE}
818 NoSound := False;
819 {$ENDIF}
821 {$IF DEFINED(ANDROID) AND DEFINED(USE_SDLMIXER)}
822 timiditycfg := 'timidity.cfg';
823 if e_FindResource(ConfigDirs, timiditycfg) = true then
824 begin
825 timiditycfg := ExpandFileName(timiditycfg);
826 SetEnvVar('TIMIDITY_CFG', timiditycfg);
827 e_LogWritefln('Set TIMIDITY_CFG = "%s"', [timiditycfg]);
828 end;
829 {$ENDIF}
831 GameWAD := e_FindWad(DataDirs, GameWADName);
832 if GameWad = '' then
833 begin
834 e_WriteLog('WAD ' + GameWADName + ' not found in data directories.', TMsgType.Fatal);
835 {$IF DEFINED(USE_SDL2) AND NOT DEFINED(HEADLESS)}
836 if forceBinDir = false then
837 SDL_ShowSimpleMessageBox(
838 SDL_MESSAGEBOX_ERROR,
839 'Doom 2D Forever',
840 PChar('WAD ' + GameWADName + ' not found in data directories.'),
841 nil
842 );
843 {$ENDIF}
844 e_DeinitLog;
845 Halt(1);
846 end
847 end;
849 {$IFDEF ENABLE_HOLMES}
850 procedure InitHolmes;
851 var flexloaded: Boolean;
852 begin
853 flexloaded := true;
854 if not fuiAddWad('flexui.wad') then
855 begin
856 if not fuiAddWad('./data/flexui.wad') then fuiAddWad('./flexui.wad');
857 end;
858 try
859 fuiGfxLoadFont('win8', 'flexui/fonts/win8.fuifont');
860 fuiGfxLoadFont('win14', 'flexui/fonts/win14.fuifont');
861 fuiGfxLoadFont('win16', 'flexui/fonts/win16.fuifont');
862 fuiGfxLoadFont('dos8', 'flexui/fonts/dos8.fuifont');
863 fuiGfxLoadFont('msx6', 'flexui/fonts/msx6.fuifont');
864 except on e: Exception do
865 begin
866 writeln('ERROR loading FlexUI fonts');
867 flexloaded := false;
868 //raise;
869 end;
870 else
871 begin
872 flexloaded := false;
873 //raise;
874 end;
875 end;
876 if flexloaded then
877 begin
878 try
879 e_LogWriteln('FlexUI: loading stylesheet...');
880 uiLoadStyles('flexui/widgets.wgs');
881 except on e: TParserException do
882 begin
883 writeln('ERROR at (', e.tokLine, ',', e.tokCol, '): ', e.message);
884 //raise;
885 flexloaded := false;
886 end;
887 else
888 begin
889 //raise;
890 flexloaded := false;
891 end;
892 end;
893 end;
894 g_holmes_imfunctional := not flexloaded;
895 if not g_holmes_imfunctional then
896 begin
897 uiInitialize();
898 uiContext.font := 'win14';
899 end;
900 if assigned(oglInitCB) then oglInitCB;
901 end;
903 procedure FreeHolmes;
904 begin
905 if assigned(oglDeinitCB) then
906 oglDeinitCB
907 end;
908 {$ENDIF}
910 procedure ScreenResize (w, h: Integer);
911 begin
912 r_Render_Resize(w, h);
913 {$IFDEF ENABLE_HOLMES}
914 fuiScrWdt := w;
915 fuiScrHgt := h;
916 {$ENDIF}
917 g_Game_SetupScreenSize;
918 {$IFNDEF ANDROID}
919 (* This will fix menu reset on keyboard showing *)
920 g_Menu_Reset;
921 {$ENDIF}
922 //g_Game_ClearLoading;
923 {$IFDEF ENABLE_HOLMES}
924 if assigned(oglInitCB) then oglInitCB;
925 {$ENDIF}
926 end;
928 procedure Startup;
929 begin
930 Randomize;
931 InitPath;
932 InitLog;
933 InitPrep;
934 e_Input_Initialize;
935 e_InitSoundSystem(NoSound);
936 sys_Init;
937 sys_CharPress := @CharPress; (* install hook *)
938 sys_ScreenResize := @ScreenResize; (* install hook *)
939 g_Options_SetDefault;
940 g_Options_SetDefaultVideo;
941 g_Console_Initialize;
942 // TODO move load configs here
943 g_Language_Set(gLanguage);
944 r_Render_Initialize;
945 g_Touch_Init;
946 DebugOptions;
947 g_Net_InitLowLevel;
948 // TODO init serverlist
949 {$IFDEF ENABLE_HOLMES}
950 InitHolmes;
951 {$ENDIF}
952 g_Game_Init;
953 {$IFNDEF HEADLESS}
954 g_Menu_Init;
955 g_GUI_Init;
956 {$ENDIF}
957 g_Game_Process_Params;
958 // TODO reload GAME textures
959 g_Console_Init; // welcome message
960 {$IFNDEF HEADLESS}
961 if (not gGameOn) and gAskLanguage then
962 g_Menu_AskLanguage;
963 {$ENDIF}
964 Time_Old := GetTickCount64();
965 while not ProcessMessage() do begin end;
966 g_Console_WriteGameConfig;
967 {$IFNDEF HEADLESS}
968 g_GUI_Destroy;
969 g_Menu_Free;
970 {$ENDIF}
971 {$IFDEF ENABLE_HOLMES}
972 FreeHolmes;
973 {$ENDIF}
974 g_Net_Slist_ShutdownAll;
975 g_Net_DeinitLowLevel;
976 (* g_Touch_Finalize; *)
977 r_Render_Finalize;
978 sys_Final;
979 g_Console_Finalize;
980 e_ReleaseSoundSystem;
981 e_Input_Finalize;
982 e_WriteLog('Shutdown with no errors.', TMsgType.Notify)
983 end;
985 procedure EntryPoint;
986 begin
987 SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]); //k8: fuck off, that's why
988 EntryParams;
989 e_Log_Initialize;
990 {$IFDEF HEADLESS}
991 conbufDumpToStdOut := true;
992 {$ENDIF}
993 if noct then
994 Startup
995 else
996 try
997 Startup
998 except on e: Exception do
999 e_WriteStackTrace(e.message)
1000 else
1001 e_WriteStackTrace('FATAL ERROR')
1002 end;
1003 e_Log_Finalize
1004 end;
1006 {$IFDEF ANDROID}
1007 function SDL_main (argc: CInt; argv: PPChar): CInt; cdecl;
1008 begin
1009 // TODO pass argc+argv to rtl
1010 EntryPoint;
1011 result := 0
1012 end;
1014 function JNI_OnLoad (vm: PJavaVM; reserved: pointer): JInt; cdecl;
1015 begin
1016 result:= JNI_VERSION_1_6;
1017 end;
1019 procedure JNI_OnUnload(vm: PJavaVM; reserved: pointer); cdecl;
1020 begin
1021 end;
1023 // DONT REMOVE JNI FUNCTIONS. SPECIAL HANDLING BY FPC.
1024 exports SDL_main name 'SDL_main';
1025 exports JNI_OnLoad name 'JNI_OnLoad';
1026 exports JNI_OnUnload name 'JNI_Unload';
1027 {$ELSE}
1028 begin
1029 EntryPoint
1030 {$ENDIF}
1032 end.