A minor removal of a few pieces of dead code. Nothing major. This is
[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 #define WIN32_USETIMEGETTIME 0
23
24 #include "quakedef.h"
25 #include "winquake.h"
26 #include "errno.h"
27 #include "resource.h"
28 #include "conproc.h"
29 #include "direct.h"
30
31 #define CONSOLE_ERROR_TIMEOUT   60.0    // # of seconds to wait on Sys_Error running
32                                                                                 //  dedicated before exiting
33 #define PAUSE_SLEEP             50                              // sleep time on pause or minimization
34 #define NOT_FOCUS_SLEEP 20                              // sleep time when not focus
35
36 int                     starttime;
37 qboolean        ActiveApp, Minimized;
38
39 static qboolean         sc_return_on_enter = false;
40 HANDLE                          hinput, houtput;
41
42 static HANDLE   tevent;
43 static HANDLE   hFile;
44 static HANDLE   heventParent;
45 static HANDLE   heventChild;
46
47 /*
48 ===============================================================================
49
50 QFile IO
51
52 ===============================================================================
53 */
54
55 // LordHavoc: 256 pak files (was 10)
56 #define MAX_HANDLES             256
57 QFile   *sys_handles[MAX_HANDLES];
58
59 int             findhandle (void)
60 {
61         int             i;
62         
63         for (i=1 ; i<MAX_HANDLES ; i++)
64                 if (!sys_handles[i])
65                         return i;
66         Sys_Error ("out of handles");
67         return -1;
68 }
69
70 /*
71 ================
72 Sys_FileLength
73 ================
74 */
75 int Sys_FileLength (QFile *f)
76 {
77         int             pos;
78         int             end;
79
80         pos = Qtell (f);
81         Qseek (f, 0, SEEK_END);
82         end = Qtell (f);
83         Qseek (f, pos, SEEK_SET);
84
85         return end;
86 }
87
88 int Sys_FileOpenRead (char *path, int *hndl)
89 {
90         QFile   *f;
91         int             i, retval;
92
93         i = findhandle ();
94
95         f = Qopen(path, "rbz");
96
97         if (!f)
98         {
99                 *hndl = -1;
100                 retval = -1;
101         }
102         else
103         {
104                 sys_handles[i] = f;
105                 *hndl = i;
106                 retval = Sys_FileLength(f);
107         }
108
109         return retval;
110 }
111
112 int Sys_FileOpenWrite (char *path)
113 {
114         QFile   *f;
115         int             i;
116
117         i = findhandle ();
118
119         f = Qopen(path, "wb");
120         if (!f)
121         {
122                 Con_Printf("Sys_FileOpenWrite: Error opening %s: %s", path, strerror(errno));
123                 return 0;
124         }
125         sys_handles[i] = f;
126         
127         return i;
128 }
129
130 void Sys_FileClose (int handle)
131 {
132         Qclose (sys_handles[handle]);
133         sys_handles[handle] = NULL;
134 }
135
136 void Sys_FileSeek (int handle, int position)
137 {
138         Qseek (sys_handles[handle], position, SEEK_SET);
139 }
140
141 int Sys_FileRead (int handle, void *dest, int count)
142 {
143         return Qread (sys_handles[handle], dest, count);
144 }
145
146 int Sys_FileWrite (int handle, void *data, int count)
147 {
148         return Qwrite (sys_handles[handle], data, count);
149 }
150
151 int     Sys_FileTime (char *path)
152 {
153         QFile   *f;
154         
155         f = Qopen(path, "rb");
156         if (f)
157         {
158                 Qclose(f);
159                 return 1;
160         }
161         
162         return -1;
163 }
164
165 void Sys_mkdir (char *path)
166 {
167         _mkdir (path);
168 }
169
170
171 /*
172 ===============================================================================
173
174 SYSTEM IO
175
176 ===============================================================================
177 */
178
179 void Sys_Error (char *error, ...)
180 {
181         va_list         argptr;
182         char            text[1024], text2[1024];
183         char            *text3 = "Press Enter to exit\n";
184         char            *text4 = "***********************************\n";
185         char            *text5 = "\n";
186         DWORD           dummy;
187         double          starttime;
188         static int      in_sys_error0 = 0;
189         static int      in_sys_error1 = 0;
190         static int      in_sys_error2 = 0;
191         static int      in_sys_error3 = 0;
192
193         if (!in_sys_error3)
194                 in_sys_error3 = 1;
195
196         va_start (argptr, error);
197         vsprintf (text, error, argptr);
198         va_end (argptr);
199
200         if (cls.state == ca_dedicated)
201         {
202                 va_start (argptr, error);
203                 vsprintf (text, error, argptr);
204                 va_end (argptr);
205
206                 sprintf (text2, "ERROR: %s\n", text);
207                 WriteFile (houtput, text5, strlen (text5), &dummy, NULL);
208                 WriteFile (houtput, text4, strlen (text4), &dummy, NULL);
209                 WriteFile (houtput, text2, strlen (text2), &dummy, NULL);
210                 WriteFile (houtput, text3, strlen (text3), &dummy, NULL);
211                 WriteFile (houtput, text4, strlen (text4), &dummy, NULL);
212
213
214                 starttime = Sys_DoubleTime ();
215                 sc_return_on_enter = true;      // so Enter will get us out of here
216
217                 while (!Sys_ConsoleInput () && ((Sys_DoubleTime () - starttime) < CONSOLE_ERROR_TIMEOUT))
218                 {
219                 }
220         }
221         else
222         {
223         // switch to windowed so the message box is visible, unless we already
224         // tried that and failed
225                 if (!in_sys_error0)
226                 {
227                         in_sys_error0 = 1;
228                         VID_SetDefaultMode ();
229                         MessageBox(NULL, text, "Quake Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
230                 }
231                 else
232                 {
233                         MessageBox(NULL, text, "Double Quake Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
234                 }
235         }
236
237         if (!in_sys_error1)
238         {
239                 in_sys_error1 = 1;
240                 Host_Shutdown ();
241         }
242
243 // shut down QHOST hooks if necessary
244         if (!in_sys_error2)
245         {
246                 in_sys_error2 = 1;
247                 DeinitConProc ();
248         }
249
250         exit (1);
251 }
252
253 void Sys_Quit (void)
254 {
255
256         Host_Shutdown();
257
258         if (tevent)
259                 CloseHandle (tevent);
260
261         if (cls.state == ca_dedicated)
262                 FreeConsole ();
263
264 // shut down QHOST hooks if necessary
265         DeinitConProc ();
266
267         exit (0);
268 }
269
270
271 /*
272 ================
273 Sys_DoubleTime
274 ================
275 */
276 double Sys_DoubleTime (void)
277 {
278         // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
279 #if WIN32_USETIMEGETTIME
280         // timeGetTime
281         // platform:
282         // Windows 95/98/ME/NT/2000
283         // features:
284         // reasonable accuracy (millisecond)
285         // issues:
286         // none known
287         static int first = true;
288         static double oldtime = 0.0, basetime = 0.0, old = 0.0;
289         double newtime, now;
290
291         now = (double) timeGetTime () + basetime;
292
293         if (first)
294         {
295                 first = false;
296                 basetime = now;
297                 now = 0;
298         }
299
300         if (now < old)
301         {
302                 // wrapped
303                 basetime += (65536.0 * 65536.0);
304                 now += (65536.0 * 65536.0);
305         }
306         old = now;
307
308         newtime = now / 1000.0;
309
310         if (newtime < oldtime)
311                 Sys_Error("Sys_DoubleTime: time running backwards??\n");
312
313         oldtime = newtime;
314
315         return newtime;
316 #else
317         // QueryPerformanceCounter
318         // platform:
319         // Windows 95/98/ME/NT/2000
320         // features:
321         // very accurate (CPU cycles)
322         // known issues:
323         // does not necessarily match realtime too well (tends to get faster and faster in win98)
324         static int first = true;
325         static double oldtime = 0.0, basetime = 0.0, timescale = 0.0;
326         double newtime;
327         LARGE_INTEGER PerformanceFreq;
328         LARGE_INTEGER PerformanceCount;
329
330         if (first)
331         {
332                 if (!QueryPerformanceFrequency (&PerformanceFreq))
333                         Sys_Error ("No hardware timer available");
334
335 #ifdef __BORLANDC__
336                 timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
337 #else
338                 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
339 #endif  
340         }
341
342         QueryPerformanceCounter (&PerformanceCount);
343
344 #ifdef __BORLANDC__
345         newtime = ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale - basetime;
346 #else
347         newtime = ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale - basetime;
348 #endif  
349
350         if (first)
351         {
352                 first = false;
353                 basetime = newtime;
354                 newtime = 0;
355         }
356
357         if (newtime < oldtime)
358                 Sys_Error("Sys_DoubleTime: time running backwards??\n");
359
360         oldtime = newtime;
361
362         return newtime;
363 #endif
364 }
365
366
367 char *Sys_ConsoleInput (void)
368 {
369         static char text[256];
370         static int len;
371         INPUT_RECORD recs[1024];
372         int ch;
373         DWORD numread, numevents, dummy;
374
375         if (cls.state != ca_dedicated)
376                 return NULL;
377
378
379         for ( ;; )
380         {
381                 if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
382                         Sys_Error ("Error getting # of console events");
383
384                 if (numevents <= 0)
385                         break;
386
387                 if (!ReadConsoleInput(hinput, recs, 1, &numread))
388                         Sys_Error ("Error reading console input");
389
390                 if (numread != 1)
391                         Sys_Error ("Couldn't read console input");
392
393                 if (recs[0].EventType == KEY_EVENT)
394                 {
395                         if (!recs[0].Event.KeyEvent.bKeyDown)
396                         {
397                                 ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
398
399                                 switch (ch)
400                                 {
401                                         case '\r':
402                                                 WriteFile(houtput, "\r\n", 2, &dummy, NULL);    
403
404                                                 if (len)
405                                                 {
406                                                         text[len] = 0;
407                                                         len = 0;
408                                                         return text;
409                                                 }
410                                                 else if (sc_return_on_enter)
411                                                 {
412                                                 // special case to allow exiting from the error handler on Enter
413                                                         text[0] = '\r';
414                                                         len = 0;
415                                                         return text;
416                                                 }
417
418                                                 break;
419
420                                         case '\b':
421                                                 WriteFile(houtput, "\b \b", 3, &dummy, NULL);   
422                                                 if (len)
423                                                 {
424                                                         len--;
425                                                 }
426                                                 break;
427
428                                         default:
429                                                 if (ch >= ' ')
430                                                 {
431                                                         WriteFile(houtput, &ch, 1, &dummy, NULL);       
432                                                         text[len] = ch;
433                                                         len = (len + 1) & 0xff;
434                                                 }
435
436                                                 break;
437
438                                 }
439                         }
440                 }
441         }
442
443         return NULL;
444 }
445
446 void Sys_Sleep (void)
447 {
448         Sleep (1);
449 }
450
451
452 void Sys_SendKeyEvents (void)
453 {
454     MSG        msg;
455
456         while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
457         {
458         // we always update if there are any event, even if we're paused
459                 scr_skipupdate = 0;
460
461                 if (!GetMessage (&msg, NULL, 0, 0))
462                         Sys_Quit ();
463
464         TranslateMessage (&msg);
465         DispatchMessage (&msg);
466         }
467 }
468
469
470 /*
471 ==============================================================================
472
473  WINDOWS CRAP
474
475 ==============================================================================
476 */
477
478
479 void SleepUntilInput (int time)
480 {
481         MsgWaitForMultipleObjects(1, &tevent, false, time, QS_ALLINPUT);
482 }
483
484
485 /*
486 ==================
487 WinMain
488 ==================
489 */
490 HINSTANCE       global_hInstance;
491 int                     global_nCmdShow;
492 char            *argv[MAX_NUM_ARGVS];
493 static char     *empty_string = "";
494
495 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
496 {
497         double                  oldtime, newtime;
498         MEMORYSTATUS    lpBuffer;
499         static  char    cwd[1024];
500         int                             t;
501
502     /* previous instances do not exist in Win32 */
503     if (hPrevInstance)
504         return 0;
505
506         global_hInstance = hInstance;
507         global_nCmdShow = nCmdShow;
508
509         lpBuffer.dwLength = sizeof(MEMORYSTATUS);
510         GlobalMemoryStatus (&lpBuffer);
511
512         if (!GetCurrentDirectory (sizeof(cwd), cwd))
513                 Sys_Error ("Couldn't determine current directory");
514
515         if (cwd[strlen(cwd)-1] == '/')
516                 cwd[strlen(cwd)-1] = 0;
517
518         memset(&host_parms, 0, sizeof(host_parms));
519
520         host_parms.basedir = cwd;
521
522         host_parms.argc = 1;
523         argv[0] = empty_string;
524
525         while (*lpCmdLine && (host_parms.argc < MAX_NUM_ARGVS))
526         {
527                 while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
528                         lpCmdLine++;
529
530                 if (*lpCmdLine)
531                 {
532                         argv[host_parms.argc] = lpCmdLine;
533                         host_parms.argc++;
534
535                         while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
536                                 lpCmdLine++;
537
538                         if (*lpCmdLine)
539                         {
540                                 *lpCmdLine = 0;
541                                 lpCmdLine++;
542                         }
543
544                 }
545         }
546
547         host_parms.argv = argv;
548
549         COM_InitArgv (host_parms.argc, host_parms.argv);
550
551         host_parms.argc = com_argc;
552         host_parms.argv = com_argv;
553
554         Sys_Shared_EarlyInit();
555
556         tevent = CreateEvent(NULL, false, false, NULL);
557
558         if (!tevent)
559                 Sys_Error ("Couldn't create event");
560
561         // LordHavoc: can't check cls.state because it hasn't been initialized yet
562         // if (cls.state == ca_dedicated)
563         if (COM_CheckParm("-dedicated"))
564         {
565                 if (!AllocConsole ())
566                         Sys_Error ("Couldn't create dedicated server console");
567
568                 hinput = GetStdHandle (STD_INPUT_HANDLE);
569                 houtput = GetStdHandle (STD_OUTPUT_HANDLE);
570
571         // give QHOST a chance to hook into the console
572                 if ((t = COM_CheckParm ("-HFILE")) > 0)
573                 {
574                         if (t < com_argc)
575                                 hFile = (HANDLE)atoi (com_argv[t+1]);
576                 }
577
578                 if ((t = COM_CheckParm ("-HPARENT")) > 0)
579                 {
580                         if (t < com_argc)
581                                 heventParent = (HANDLE)atoi (com_argv[t+1]);
582                 }
583
584                 if ((t = COM_CheckParm ("-HCHILD")) > 0)
585                 {
586                         if (t < com_argc)
587                                 heventChild = (HANDLE)atoi (com_argv[t+1]);
588                 }
589
590                 InitConProc (hFile, heventParent, heventChild);
591         }
592
593 // because sound is off until we become active
594         S_BlockSound ();
595
596 #if WIN32_USETIMEGETTIME
597         // make sure the timer is high precision, otherwise NT gets 18ms resolution
598         // LordHavoc:
599         // Windows 2000 Advanced Server (and possibly other versions)
600         // apparently have a broken timer, because it runs at more like 10x speed
601         // if this isn't used, heh
602         timeBeginPeriod (1);
603 #endif
604
605         Host_Init ();
606
607         Sys_Shared_LateInit();
608
609         oldtime = Sys_DoubleTime ();
610
611     /* main window message loop */
612         while (1)
613         {
614                 if (cls.state != ca_dedicated)
615                 {
616                 // yield the CPU for a little while when paused, minimized, or not the focus
617                         if ((cl.paused && !ActiveApp) || Minimized)
618                         {
619                                 SleepUntilInput (PAUSE_SLEEP);
620                                 scr_skipupdate = 1;             // no point in bothering to draw
621                         }
622                         else if (!ActiveApp)
623                                 SleepUntilInput (NOT_FOCUS_SLEEP);
624                 }
625
626                 newtime = Sys_DoubleTime ();
627                 Host_Frame (newtime - oldtime);
628                 oldtime = newtime;
629         }
630
631     /* return success of application */
632     return true;
633 }
634