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