every malloc/calloc/free converted to qmalloc/qfree with tracking (memstats command...
[xonotic/darkplaces.git] / sys_win.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sys_win.c -- Win32 system interface code
21
22 #include "quakedef.h"
23 #include "winquake.h"
24 #include "errno.h"
25 #include "resource.h"
26 #include "conproc.h"
27 #include "direct.h"
28
29 // LordHavoc: raised min to 64mb (was 8.5mb)
30 #define MINIMUM_WIN_MEMORY              0x04000000
31 // LordHavoc: raised max to 64mb (was 16mb)
32 #define MAXIMUM_WIN_MEMORY              0x04000000
33
34 #define CONSOLE_ERROR_TIMEOUT   60.0    // # of seconds to wait on Sys_Error running
35                                                                                 //  dedicated before exiting
36 #define PAUSE_SLEEP             50                              // sleep time on pause or minimization
37 #define NOT_FOCUS_SLEEP 20                              // sleep time when not focus
38
39 int                     starttime;
40 qboolean        ActiveApp, Minimized;
41 qboolean        WinNT;
42
43 static double           pfreq;
44 static double           curtime = 0.0;
45 static double           lastcurtime = 0.0;
46 static int                      lowshift;
47 qboolean                        isDedicated;
48 static qboolean         sc_return_on_enter = false;
49 HANDLE                          hinput, houtput;
50
51 static char                     *tracking_tag = "Clams & Mooses";
52
53 static HANDLE   tevent;
54 static HANDLE   hFile;
55 static HANDLE   heventParent;
56 static HANDLE   heventChild;
57
58 void Sys_InitFloatTime (void);
59
60 volatile int                                    sys_checksum;
61
62
63 /*
64 ================
65 Sys_PageIn
66 ================
67 */
68 /*
69 void Sys_PageIn (void *ptr, int size)
70 {
71         byte    *x;
72         int             m, n;
73
74 // touch all the memory to make sure it's there. The 16-page skip is to
75 // keep Win 95 from thinking we're trying to page ourselves in (we are
76 // doing that, of course, but there's no reason we shouldn't)
77         x = (byte *)ptr;
78
79         for (n=0 ; n<4 ; n++)
80         {
81                 for (m=0 ; m<(size - 16 * 0x1000) ; m += 4)
82                 {
83                         sys_checksum += *(int *)&x[m];
84                         sys_checksum += *(int *)&x[m + 16 * 0x1000];
85                 }
86         }
87 }
88 */
89
90
91 /*
92 ===============================================================================
93
94 FILE IO
95
96 ===============================================================================
97 */
98
99 // LordHavoc: 256 pak files (was 10)
100 #define MAX_HANDLES             256
101 FILE    *sys_handles[MAX_HANDLES];
102
103 int             findhandle (void)
104 {
105         int             i;
106         
107         for (i=1 ; i<MAX_HANDLES ; i++)
108                 if (!sys_handles[i])
109                         return i;
110         Sys_Error ("out of handles");
111         return -1;
112 }
113
114 /*
115 ================
116 filelength
117 ================
118 */
119 int filelength (FILE *f)
120 {
121         int             pos;
122         int             end;
123
124         pos = ftell (f);
125         fseek (f, 0, SEEK_END);
126         end = ftell (f);
127         fseek (f, pos, SEEK_SET);
128
129         return end;
130 }
131
132 int Sys_FileOpenRead (char *path, int *hndl)
133 {
134         FILE    *f;
135         int             i, retval;
136
137         i = findhandle ();
138
139         f = fopen(path, "rb");
140
141         if (!f)
142         {
143                 *hndl = -1;
144                 retval = -1;
145         }
146         else
147         {
148                 sys_handles[i] = f;
149                 *hndl = i;
150                 retval = filelength(f);
151         }
152
153         return retval;
154 }
155
156 int Sys_FileOpenWrite (char *path)
157 {
158         FILE    *f;
159         int             i;
160
161         i = findhandle ();
162
163         f = fopen(path, "wb");
164         if (!f)
165                 Host_Error ("Error opening %s: %s", path,strerror(errno));
166         sys_handles[i] = f;
167         
168         return i;
169 }
170
171 void Sys_FileClose (int handle)
172 {
173         fclose (sys_handles[handle]);
174         sys_handles[handle] = NULL;
175 }
176
177 void Sys_FileSeek (int handle, int position)
178 {
179         fseek (sys_handles[handle], position, SEEK_SET);
180 }
181
182 int Sys_FileRead (int handle, void *dest, int count)
183 {
184         return fread (dest, 1, count, sys_handles[handle]);
185 }
186
187 int Sys_FileWrite (int handle, void *data, int count)
188 {
189         return fwrite (data, 1, count, sys_handles[handle]);
190 }
191
192 int     Sys_FileTime (char *path)
193 {
194         FILE    *f;
195         int             retval;
196
197         f = fopen(path, "rb");
198
199         if (f)
200         {
201                 fclose(f);
202                 retval = 1;
203         }
204         else
205         {
206                 retval = -1;
207         }
208         
209         return retval;
210 }
211
212 void Sys_mkdir (char *path)
213 {
214         _mkdir (path);
215 }
216
217
218 /*
219 ===============================================================================
220
221 SYSTEM IO
222
223 ===============================================================================
224 */
225
226 /*
227 ================
228 Sys_MakeCodeWriteable
229 ================
230 */
231 void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
232 {
233         DWORD  flOldProtect;
234
235         if (!VirtualProtect((LPVOID)startaddr, length, PAGE_READWRITE, &flOldProtect))
236                 Sys_Error("Protection change failed\n");
237 }
238
239
240 /*
241 ================
242 Sys_Init
243 ================
244 */
245 void Sys_Init (void)
246 {
247         LARGE_INTEGER   PerformanceFreq;
248         unsigned int    lowpart, highpart;
249         OSVERSIONINFO   vinfo;
250
251         if (!QueryPerformanceFrequency (&PerformanceFreq))
252                 Sys_Error ("No hardware timer available");
253
254 // get 32 out of the 64 time bits such that we have around
255 // 1 microsecond resolution
256 #ifdef __BORLANDC__
257         lowpart = (unsigned int)PerformanceFreq.u.LowPart;
258         highpart = (unsigned int)PerformanceFreq.u.HighPart;
259 #else
260         lowpart = (unsigned int)PerformanceFreq.LowPart;
261         highpart = (unsigned int)PerformanceFreq.HighPart;
262 #endif  
263         lowshift = 0;
264
265         while (highpart || (lowpart > 2000000.0))
266         {
267                 lowshift++;
268                 lowpart >>= 1;
269                 lowpart |= (highpart & 1) << 31;
270                 highpart >>= 1;
271         }
272
273         pfreq = 1.0 / (double)lowpart;
274
275         Sys_InitFloatTime ();
276
277         vinfo.dwOSVersionInfoSize = sizeof(vinfo);
278
279         if (!GetVersionEx (&vinfo))
280                 Sys_Error ("Couldn't get OS info");
281
282         if ((vinfo.dwMajorVersion < 4) ||
283                 (vinfo.dwPlatformId == VER_PLATFORM_WIN32s))
284         {
285                 Sys_Error ("WinQuake requires at least Win95 or NT 4.0");
286         }
287
288         if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
289                 WinNT = true;
290         else
291                 WinNT = false;
292 }
293
294
295 void Sys_Error (char *error, ...)
296 {
297         va_list         argptr;
298         char            text[1024], text2[1024];
299         char            *text3 = "Press Enter to exit\n";
300         char            *text4 = "***********************************\n";
301         char            *text5 = "\n";
302         DWORD           dummy;
303         double          starttime;
304         static int      in_sys_error0 = 0;
305         static int      in_sys_error1 = 0;
306         static int      in_sys_error2 = 0;
307         static int      in_sys_error3 = 0;
308
309         if (!in_sys_error3)
310                 in_sys_error3 = 1;
311
312         va_start (argptr, error);
313         vsprintf (text, error, argptr);
314         va_end (argptr);
315
316         if (isDedicated)
317         {
318                 va_start (argptr, error);
319                 vsprintf (text, error, argptr);
320                 va_end (argptr);
321
322                 sprintf (text2, "ERROR: %s\n", text);
323                 WriteFile (houtput, text5, strlen (text5), &dummy, NULL);
324                 WriteFile (houtput, text4, strlen (text4), &dummy, NULL);
325                 WriteFile (houtput, text2, strlen (text2), &dummy, NULL);
326                 WriteFile (houtput, text3, strlen (text3), &dummy, NULL);
327                 WriteFile (houtput, text4, strlen (text4), &dummy, NULL);
328
329
330                 starttime = Sys_FloatTime ();
331                 sc_return_on_enter = true;      // so Enter will get us out of here
332
333                 while (!Sys_ConsoleInput () &&
334                                 ((Sys_FloatTime () - starttime) < CONSOLE_ERROR_TIMEOUT))
335                 {
336                 }
337         }
338         else
339         {
340         // switch to windowed so the message box is visible, unless we already
341         // tried that and failed
342                 if (!in_sys_error0)
343                 {
344                         in_sys_error0 = 1;
345                         VID_SetDefaultMode ();
346                         MessageBox(NULL, text, "Quake Error",
347                                            MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
348                 }
349                 else
350                 {
351                         MessageBox(NULL, text, "Double Quake Error",
352                                            MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
353                 }
354         }
355
356         if (!in_sys_error1)
357         {
358                 in_sys_error1 = 1;
359                 Host_Shutdown ();
360         }
361
362 // shut down QHOST hooks if necessary
363         if (!in_sys_error2)
364         {
365                 in_sys_error2 = 1;
366                 DeinitConProc ();
367         }
368
369         exit (1);
370 }
371
372 void Sys_Printf (char *fmt, ...)
373 {
374         va_list         argptr;
375         char            text[1024];
376         DWORD           dummy;
377         
378         if (isDedicated)
379         {
380                 va_start (argptr,fmt);
381                 vsprintf (text, fmt, argptr);
382                 va_end (argptr);
383
384                 WriteFile(houtput, text, strlen (text), &dummy, NULL);  
385         }
386 }
387
388 void Sys_Quit (void)
389 {
390
391         Host_Shutdown();
392
393         if (tevent)
394                 CloseHandle (tevent);
395
396         if (isDedicated)
397                 FreeConsole ();
398
399 // shut down QHOST hooks if necessary
400         DeinitConProc ();
401
402         exit (0);
403 }
404
405
406 /*
407 ================
408 Sys_FloatTime
409 ================
410 */
411 double Sys_FloatTime (void)
412 {
413         static int                      sametimecount;
414         static unsigned int     oldtime;
415         static int                      first = 1;
416         LARGE_INTEGER           PerformanceCount;
417         unsigned int            temp, t2;
418         double                          time;
419
420         QueryPerformanceCounter (&PerformanceCount);
421
422 #ifdef __BORLANDC__
423         temp = ((unsigned int)PerformanceCount.u.LowPart >> lowshift) |
424             ((unsigned int)PerformanceCount.u.HighPart << (32 - lowshift));
425 #else
426
427         temp = ((unsigned int)PerformanceCount.LowPart >> lowshift) |
428                    ((unsigned int)PerformanceCount.HighPart << (32 - lowshift));
429 #endif
430         if (first)
431         {
432                 oldtime = temp;
433                 first = 0;
434         }
435         else
436         {
437         // check for turnover or backward time
438                 if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000))
439                 {
440                         oldtime = temp; // so we can't get stuck
441                 }
442                 else
443                 {
444                         t2 = temp - oldtime;
445
446                         time = (double)t2 * pfreq;
447                         oldtime = temp;
448
449                         curtime += time;
450
451                         if (curtime == lastcurtime)
452                         {
453                                 sametimecount++;
454
455                                 if (sametimecount > 100000)
456                                 {
457                                         curtime += 1.0;
458                                         sametimecount = 0;
459                                 }
460                         }
461                         else
462                         {
463                                 sametimecount = 0;
464                         }
465
466                         lastcurtime = curtime;
467                 }
468         }
469
470     return curtime;
471 }
472
473
474 /*
475 ================
476 Sys_InitFloatTime
477 ================
478 */
479 void Sys_InitFloatTime (void)
480 {
481         int             j;
482
483         Sys_FloatTime ();
484
485         j = COM_CheckParm("-starttime");
486
487         if (j)
488         {
489                 curtime = (double) (atof(com_argv[j+1]));
490         }
491         else
492         {
493                 curtime = 0.0;
494         }
495
496         lastcurtime = curtime;
497 }
498
499
500 char *Sys_ConsoleInput (void)
501 {
502         static char     text[256];
503         static int              len;
504         INPUT_RECORD    recs[1024];
505         int             dummy;
506         int             ch, numread, numevents;
507
508         if (!isDedicated)
509                 return NULL;
510
511
512         for ( ;; )
513         {
514                 if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
515                         Sys_Error ("Error getting # of console events");
516
517                 if (numevents <= 0)
518                         break;
519
520                 if (!ReadConsoleInput(hinput, recs, 1, &numread))
521                         Sys_Error ("Error reading console input");
522
523                 if (numread != 1)
524                         Sys_Error ("Couldn't read console input");
525
526                 if (recs[0].EventType == KEY_EVENT)
527                 {
528                         if (!recs[0].Event.KeyEvent.bKeyDown)
529                         {
530                                 ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
531
532                                 switch (ch)
533                                 {
534                                         case '\r':
535                                                 WriteFile(houtput, "\r\n", 2, &dummy, NULL);    
536
537                                                 if (len)
538                                                 {
539                                                         text[len] = 0;
540                                                         len = 0;
541                                                         return text;
542                                                 }
543                                                 else if (sc_return_on_enter)
544                                                 {
545                                                 // special case to allow exiting from the error handler on Enter
546                                                         text[0] = '\r';
547                                                         len = 0;
548                                                         return text;
549                                                 }
550
551                                                 break;
552
553                                         case '\b':
554                                                 WriteFile(houtput, "\b \b", 3, &dummy, NULL);   
555                                                 if (len)
556                                                 {
557                                                         len--;
558                                                 }
559                                                 break;
560
561                                         default:
562                                                 if (ch >= ' ')
563                                                 {
564                                                         WriteFile(houtput, &ch, 1, &dummy, NULL);       
565                                                         text[len] = ch;
566                                                         len = (len + 1) & 0xff;
567                                                 }
568
569                                                 break;
570
571                                 }
572                         }
573                 }
574         }
575
576         return NULL;
577 }
578
579 void Sys_Sleep (void)
580 {
581         Sleep (1);
582 }
583
584
585 void Sys_SendKeyEvents (void)
586 {
587     MSG        msg;
588
589         while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
590         {
591         // we always update if there are any event, even if we're paused
592                 scr_skipupdate = 0;
593
594                 if (!GetMessage (&msg, NULL, 0, 0))
595                         Sys_Quit ();
596
597         TranslateMessage (&msg);
598         DispatchMessage (&msg);
599         }
600 }
601
602
603 /*
604 ==============================================================================
605
606  WINDOWS CRAP
607
608 ==============================================================================
609 */
610
611
612 /*
613 ==================
614 WinMain
615 ==================
616 */
617 void SleepUntilInput (int time)
618 {
619
620         MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT);
621 }
622
623
624 /*
625 ==================
626 WinMain
627 ==================
628 */
629 HINSTANCE       global_hInstance;
630 int                     global_nCmdShow;
631 char            *argv[MAX_NUM_ARGVS];
632 static char     *empty_string = "";
633
634 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
635 {
636         double                  time, oldtime, newtime/*, timediff*/;
637         MEMORYSTATUS    lpBuffer;
638         static  char    cwd[1024];
639         int                             t;
640
641     /* previous instances do not exist in Win32 */
642     if (hPrevInstance)
643         return 0;
644
645         global_hInstance = hInstance;
646         global_nCmdShow = nCmdShow;
647
648         lpBuffer.dwLength = sizeof(MEMORYSTATUS);
649         GlobalMemoryStatus (&lpBuffer);
650
651         if (!GetCurrentDirectory (sizeof(cwd), cwd))
652                 Sys_Error ("Couldn't determine current directory");
653
654         if (cwd[strlen(cwd)-1] == '/')
655                 cwd[strlen(cwd)-1] = 0;
656
657         host_parms.basedir = cwd;
658         host_parms.cachedir = NULL;
659
660         host_parms.argc = 1;
661         argv[0] = empty_string;
662
663         while (*lpCmdLine && (host_parms.argc < MAX_NUM_ARGVS))
664         {
665                 while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
666                         lpCmdLine++;
667
668                 if (*lpCmdLine)
669                 {
670                         argv[host_parms.argc] = lpCmdLine;
671                         host_parms.argc++;
672
673                         while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
674                                 lpCmdLine++;
675
676                         if (*lpCmdLine)
677                         {
678                                 *lpCmdLine = 0;
679                                 lpCmdLine++;
680                         }
681                         
682                 }
683         }
684
685         host_parms.argv = argv;
686
687         COM_InitArgv (host_parms.argc, host_parms.argv);
688
689         host_parms.argc = com_argc;
690         host_parms.argv = com_argv;
691
692         isDedicated = (COM_CheckParm ("-dedicated") != 0);
693
694 // take the greater of all the available memory or half the total memory,
695 // but at least 8 Mb and no more than 16 Mb, unless they explicitly
696 // request otherwise
697         host_parms.memsize = lpBuffer.dwAvailPhys;
698
699         if (host_parms.memsize < MINIMUM_WIN_MEMORY)
700                 host_parms.memsize = MINIMUM_WIN_MEMORY;
701
702         if (host_parms.memsize < (lpBuffer.dwTotalPhys >> 1))
703                 host_parms.memsize = lpBuffer.dwTotalPhys >> 1;
704
705         if (host_parms.memsize > MAXIMUM_WIN_MEMORY)
706                 host_parms.memsize = MAXIMUM_WIN_MEMORY;
707
708         if ((t = COM_CheckParm("-heapsize")))
709         {
710                 t++;
711                 if (t < com_argc)
712                         host_parms.memsize = atoi (com_argv[t]) * 1024;
713         }
714         else if ((t = COM_CheckParm("-mem")) || (t = COM_CheckParm("-winmem")))
715         {
716                 t++;
717                 if (t < com_argc)
718                         host_parms.memsize = atoi (com_argv[t]) * 1048576;
719         }
720
721         host_parms.membase = qmalloc(host_parms.memsize);
722
723         if (!host_parms.membase)
724                 Sys_Error ("Not enough memory free; check disk space\n");
725
726 //      Sys_PageIn (parms.membase, parms.memsize);
727
728         tevent = CreateEvent(NULL, FALSE, FALSE, NULL);
729
730         if (!tevent)
731                 Sys_Error ("Couldn't create event");
732
733         if (isDedicated)
734         {
735                 if (!AllocConsole ())
736                 {
737                         Sys_Error ("Couldn't create dedicated server console");
738                 }
739
740                 hinput = GetStdHandle (STD_INPUT_HANDLE);
741                 houtput = GetStdHandle (STD_OUTPUT_HANDLE);
742
743         // give QHOST a chance to hook into the console
744                 if ((t = COM_CheckParm ("-HFILE")) > 0)
745                 {
746                         if (t < com_argc)
747                                 hFile = (HANDLE)atoi (com_argv[t+1]);
748                 }
749                         
750                 if ((t = COM_CheckParm ("-HPARENT")) > 0)
751                 {
752                         if (t < com_argc)
753                                 heventParent = (HANDLE)atoi (com_argv[t+1]);
754                 }
755                         
756                 if ((t = COM_CheckParm ("-HCHILD")) > 0)
757                 {
758                         if (t < com_argc)
759                                 heventChild = (HANDLE)atoi (com_argv[t+1]);
760                 }
761
762                 InitConProc (hFile, heventParent, heventChild);
763         }
764
765         Sys_Init ();
766
767 // because sound is off until we become active
768         S_BlockSound ();
769
770         Sys_Printf ("Host_Init\n");
771         Host_Init ();
772
773         oldtime = Sys_FloatTime ();
774
775     /* main window message loop */
776         while (1)
777         {
778                 if (isDedicated)
779                 {
780                         newtime = Sys_FloatTime ();
781                         time = newtime - oldtime;
782
783                         while (time < sys_ticrate.value )
784                         {
785                                 Sys_Sleep();
786                                 newtime = Sys_FloatTime ();
787                                 time = newtime - oldtime;
788                         }
789                 }
790                 else
791                 {
792                 // yield the CPU for a little while when paused, minimized, or not the focus
793                         if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized)
794                         {
795                                 SleepUntilInput (PAUSE_SLEEP);
796                                 scr_skipupdate = 1;             // no point in bothering to draw
797                         }
798                         else if (!ActiveApp && !DDActive)
799                         {
800                                 SleepUntilInput (NOT_FOCUS_SLEEP);
801                         }
802                         /*
803                         else if (!cls.timedemo && time < (timediff = 1.0 / maxfps.value))
804                         {
805                                 newtime = Sys_FloatTime ();
806                                 time = newtime - oldtime;
807
808                                 while (time < timediff)
809                                 {
810                                         Sys_Sleep();
811                                         newtime = Sys_FloatTime ();
812                                         time = newtime - oldtime;
813                                 }
814                         }
815                         */
816
817                         newtime = Sys_FloatTime ();
818                         time = newtime - oldtime;
819                 }
820
821                 Host_Frame (time);
822                 oldtime = newtime;
823         }
824
825     /* return success of application */
826     return TRUE;
827 }
828