Quake II RTX doxygen  1.0 dev
system.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (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. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 
19 #include "client.h"
20 #include "common/cvar.h"
21 #include "common/field.h"
22 #include "common/prompt.h"
23 #include <mmsystem.h>
24 #if USE_WINSVC
25 #include <winsvc.h>
26 #endif
27 
28 HINSTANCE hGlobalInstance;
29 
30 #if USE_DBGHELP
31 HANDLE mainProcessThread;
32 LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionFilter;
33 #endif
34 
35 static char currentDirectory[MAX_OSPATH];
36 
37 #if USE_WINSVC
38 static SERVICE_STATUS_HANDLE statusHandle;
39 #endif
40 
41 typedef enum {
46 
47 static volatile should_exit_t shouldExit;
48 static volatile qboolean errorEntered;
49 
50 cvar_t *sys_basedir;
51 cvar_t *sys_libdir;
52 cvar_t *sys_homedir;
54 
55 // Enable Windows visual styles for message boxes
56 #pragma comment(linker,"\"/manifestdependency:type='win32' \
57 name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
58 processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
59 
60 /*
61 ===============================================================================
62 
63 CONSOLE I/O
64 
65 ===============================================================================
66 */
67 
68 #if USE_SYSCON
69 
70 #define MAX_CONSOLE_INPUT_EVENTS 16
71 
72 static HANDLE hinput = INVALID_HANDLE_VALUE;
73 static HANDLE houtput = INVALID_HANDLE_VALUE;
74 
75 #if USE_CLIENT
76 static cvar_t *sys_viewlog;
77 #endif
78 
79 static commandPrompt_t sys_con;
80 static int sys_hidden;
81 static CONSOLE_SCREEN_BUFFER_INFO sbinfo;
82 static qboolean gotConsole;
83 
84 static void write_console_data(void *data, size_t len)
85 {
86  DWORD dummy;
87 
88  WriteFile(houtput, data, len, &dummy, NULL);
89 }
90 
91 static void hide_console_input(void)
92 {
93  int i;
94 
95  if (!sys_hidden) {
96  for (i = 0; i <= sys_con.inputLine.cursorPos; i++) {
97  write_console_data("\b \b", 3);
98  }
99  }
100  sys_hidden++;
101 }
102 
103 static void show_console_input(void)
104 {
105  if (!sys_hidden) {
106  return;
107  }
108 
109  sys_hidden--;
110  if (!sys_hidden) {
111  write_console_data("]", 1);
112  write_console_data(sys_con.inputLine.text, sys_con.inputLine.cursorPos);
113  }
114 }
115 
116 /*
117 ================
118 Sys_ConsoleInput
119 ================
120 */
121 void Sys_RunConsole(void)
122 {
123  INPUT_RECORD recs[MAX_CONSOLE_INPUT_EVENTS];
124  int ch;
125  DWORD numread, numevents;
126  int i;
127  inputField_t *f;
128  char *s;
129 
130  if (hinput == INVALID_HANDLE_VALUE) {
131  return;
132  }
133 
134  if (!gotConsole) {
135  return;
136  }
137 
138  f = &sys_con.inputLine;
139  while (1) {
140  if (!GetNumberOfConsoleInputEvents(hinput, &numevents)) {
141  Com_EPrintf("Error %lu getting number of console events.\n", GetLastError());
142  gotConsole = qfalse;
143  return;
144  }
145 
146  if (numevents <= 0)
147  break;
148  if (numevents > MAX_CONSOLE_INPUT_EVENTS) {
149  numevents = MAX_CONSOLE_INPUT_EVENTS;
150  }
151 
152  if (!ReadConsoleInput(hinput, recs, numevents, &numread)) {
153  Com_EPrintf("Error %lu reading console input.\n", GetLastError());
154  gotConsole = qfalse;
155  return;
156  }
157 
158  for (i = 0; i < numread; i++) {
159  if (recs[i].EventType == WINDOW_BUFFER_SIZE_EVENT) {
160  // determine terminal width
161  size_t width = recs[i].Event.WindowBufferSizeEvent.dwSize.X;
162 
163  if (!width) {
164  Com_EPrintf("Invalid console buffer width.\n");
165  gotConsole = qfalse;
166  return;
167  }
168 
169  sys_con.widthInChars = width;
170 
171  // figure out input line width
172  width--;
173  if (width > MAX_FIELD_TEXT - 1) {
174  width = MAX_FIELD_TEXT - 1;
175  }
176 
177  hide_console_input();
178  IF_Init(&sys_con.inputLine, width, width);
179  show_console_input();
180  continue;
181  }
182  if (recs[i].EventType != KEY_EVENT) {
183  continue;
184  }
185 
186  if (!recs[i].Event.KeyEvent.bKeyDown) {
187  continue;
188  }
189 
190  switch (recs[i].Event.KeyEvent.wVirtualKeyCode) {
191  case VK_UP:
192  hide_console_input();
193  Prompt_HistoryUp(&sys_con);
194  show_console_input();
195  break;
196  case VK_DOWN:
197  hide_console_input();
198  Prompt_HistoryDown(&sys_con);
199  show_console_input();
200  break;
201  case VK_RETURN:
202  hide_console_input();
203  s = Prompt_Action(&sys_con);
204  if (s) {
205  if (*s == '\\' || *s == '/') {
206  s++;
207  }
208  Sys_Printf("]%s\n", s);
210  Cbuf_AddText(&cmd_buffer, "\n");
211  } else {
212  write_console_data("\n", 1);
213  }
214  show_console_input();
215  break;
216  case VK_BACK:
217  if (f->cursorPos) {
218  f->text[--f->cursorPos] = 0;
219  write_console_data("\b \b", 3);
220  }
221  break;
222  case VK_TAB:
223  hide_console_input();
224  Prompt_CompleteCommand(&sys_con, qfalse);
225  f->cursorPos = strlen(f->text);
226  show_console_input();
227  break;
228  default:
229  ch = recs[i].Event.KeyEvent.uChar.AsciiChar;
230  if (ch < 32) {
231  break;
232  }
233  if (f->cursorPos < f->maxChars - 1) {
234  write_console_data(&ch, 1);
235  f->text[f->cursorPos] = ch;
236  f->text[++f->cursorPos] = 0;
237  }
238  break;
239  }
240  }
241  }
242 }
243 
244 #define FOREGROUND_BLACK 0
245 #define FOREGROUND_WHITE (FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED)
246 
247 static const WORD textColors[8] = {
248  FOREGROUND_BLACK,
249  FOREGROUND_RED,
250  FOREGROUND_GREEN,
251  FOREGROUND_RED | FOREGROUND_GREEN,
252  FOREGROUND_BLUE,
253  FOREGROUND_BLUE | FOREGROUND_GREEN,
254  FOREGROUND_RED | FOREGROUND_BLUE,
255  FOREGROUND_WHITE
256 };
257 
258 void Sys_SetConsoleColor(color_index_t color)
259 {
260  WORD attr, w;
261 
262  if (houtput == INVALID_HANDLE_VALUE) {
263  return;
264  }
265 
266  if (!gotConsole) {
267  return;
268  }
269 
270  attr = sbinfo.wAttributes & ~FOREGROUND_WHITE;
271 
272  switch (color) {
273  case COLOR_NONE:
274  w = sbinfo.wAttributes;
275  break;
276  case COLOR_ALT:
277  w = attr | FOREGROUND_GREEN;
278  break;
279  default:
280  w = attr | textColors[color];
281  break;
282  }
283 
284  if (color != COLOR_NONE) {
285  hide_console_input();
286  }
287  SetConsoleTextAttribute(houtput, w);
288  if (color == COLOR_NONE) {
289  show_console_input();
290  }
291 }
292 
293 static void write_console_output(const char *text)
294 {
295  char buf[MAXPRINTMSG];
296  size_t len;
297 
298  for (len = 0; len < MAXPRINTMSG; len++) {
299  int c = *text++;
300  if (!c) {
301  break;
302  }
303  buf[len] = Q_charascii(c);
304  }
305 
306  write_console_data(buf, len);
307 }
308 
309 /*
310 ================
311 Sys_ConsoleOutput
312 
313 Print text to the dedicated console
314 ================
315 */
316 void Sys_ConsoleOutput(const char *text)
317 {
318  if (houtput == INVALID_HANDLE_VALUE) {
319  return;
320  }
321 
322  if (!gotConsole) {
323  write_console_output(text);
324  } else {
325  hide_console_input();
326  write_console_output(text);
327  show_console_input();
328  }
329 }
330 
331 void Sys_SetConsoleTitle(const char *title)
332 {
333  if (gotConsole) {
334  SetConsoleTitle(title);
335  }
336 }
337 
338 static BOOL WINAPI Sys_ConsoleCtrlHandler(DWORD dwCtrlType)
339 {
340  if (errorEntered) {
341  exit(1);
342  }
344  return TRUE;
345 }
346 
347 static void Sys_ConsoleInit(void)
348 {
349  DWORD mode;
350  size_t width;
351 
352 #if USE_CLIENT
353  if (!AllocConsole()) {
354  Com_EPrintf("Couldn't create system console.\n");
355  return;
356  }
357 #elif USE_WINSVC
358  if (statusHandle) {
359  return;
360  }
361 #endif
362 
363  hinput = GetStdHandle(STD_INPUT_HANDLE);
364  houtput = GetStdHandle(STD_OUTPUT_HANDLE);
365  if (!GetConsoleScreenBufferInfo(houtput, &sbinfo)) {
366  Com_EPrintf("Couldn't get console buffer info.\n");
367  return;
368  }
369 
370  // determine terminal width
371  width = sbinfo.dwSize.X;
372  if (!width) {
373  Com_EPrintf("Invalid console buffer width.\n");
374  return;
375  }
376  sys_con.widthInChars = width;
377  sys_con.printf = Sys_Printf;
378  gotConsole = qtrue;
379 
380  SetConsoleTitle(PRODUCT " console");
381  SetConsoleCtrlHandler(Sys_ConsoleCtrlHandler, TRUE);
382  GetConsoleMode(hinput, &mode);
383  mode |= ENABLE_WINDOW_INPUT;
384  SetConsoleMode(hinput, mode);
385 
386  // figure out input line width
387  width--;
388  if (width > MAX_FIELD_TEXT - 1) {
389  width = MAX_FIELD_TEXT - 1;
390  }
391  IF_Init(&sys_con.inputLine, width, width);
392 
393  Com_DPrintf("System console initialized (%d cols, %d rows).\n",
394  sbinfo.dwSize.X, sbinfo.dwSize.Y);
395 }
396 
397 #endif // USE_SYSCON
398 
399 /*
400 ===============================================================================
401 
402 SERVICE CONTROL
403 
404 ===============================================================================
405 */
406 
407 #if USE_WINSVC
408 
409 static void Sys_InstallService_f(void)
410 {
411  char servicePath[256];
412  char serviceName[1024];
413  SC_HANDLE scm, service;
414  DWORD error, length;
415  char *commandline;
416 
417  if (Cmd_Argc() < 3) {
418  Com_Printf("Usage: %s <servicename> <+command> [...]\n"
419  "Example: %s test +set net_port 27910 +map q2dm1\n",
420  Cmd_Argv(0), Cmd_Argv(0));
421  return;
422  }
423 
424  scm = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
425  if (!scm) {
426  error = GetLastError();
427  if (error == ERROR_ACCESS_DENIED) {
428  Com_Printf("Insufficient privileges for opening Service Control Manager.\n");
429  } else {
430  Com_EPrintf("%#lx opening Service Control Manager.\n", error);
431  }
432  return;
433  }
434 
435  Q_concat(serviceName, sizeof(serviceName), "Q2PRO - ", Cmd_Argv(1), NULL);
436 
437  length = GetModuleFileName(NULL, servicePath, MAX_PATH);
438  if (!length) {
439  error = GetLastError();
440  Com_EPrintf("%#lx getting module file name.\n", error);
441  goto fail;
442  }
443  commandline = Cmd_RawArgsFrom(2);
444  if (length + strlen(commandline) + 10 > sizeof(servicePath) - 1) {
445  Com_Printf("Oversize service command line.\n");
446  goto fail;
447  }
448  strcpy(servicePath + length, " -service ");
449  strcpy(servicePath + length + 10, commandline);
450 
451  service = CreateService(
452  scm,
453  serviceName,
454  serviceName,
455  SERVICE_START,
456  SERVICE_WIN32_OWN_PROCESS,
457  SERVICE_AUTO_START,
458  SERVICE_ERROR_IGNORE,
459  servicePath,
460  NULL,
461  NULL,
462  NULL,
463  NULL,
464  NULL);
465 
466  if (!service) {
467  error = GetLastError();
468  if (error == ERROR_SERVICE_EXISTS || error == ERROR_DUPLICATE_SERVICE_NAME) {
469  Com_Printf("Service already exists.\n");
470  } else {
471  Com_EPrintf("%#lx creating service.\n", error);
472  }
473  goto fail;
474  }
475 
476  Com_Printf("Service created successfully.\n");
477 
478  CloseServiceHandle(service);
479 
480 fail:
481  CloseServiceHandle(scm);
482 }
483 
484 static void Sys_DeleteService_f(void)
485 {
486  char serviceName[256];
487  SC_HANDLE scm, service;
488  DWORD error;
489 
490  if (Cmd_Argc() < 2) {
491  Com_Printf("Usage: %s <servicename>\n", Cmd_Argv(0));
492  return;
493  }
494 
495  scm = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
496  if (!scm) {
497  error = GetLastError();
498  if (error == ERROR_ACCESS_DENIED) {
499  Com_Printf("Insufficient privileges for opening Service Control Manager.\n");
500  } else {
501  Com_EPrintf("%#lx opening Service Control Manager.\n", error);
502  }
503  return;
504  }
505 
506  Q_concat(serviceName, sizeof(serviceName), "Q2PRO - ", Cmd_Argv(1), NULL);
507 
508  service = OpenService(
509  scm,
510  serviceName,
511  DELETE);
512 
513  if (!service) {
514  error = GetLastError();
515  if (error == ERROR_SERVICE_DOES_NOT_EXIST) {
516  Com_Printf("Service doesn't exist.\n");
517  } else {
518  Com_EPrintf("%#lx opening service.\n", error);
519  }
520  goto fail;
521  }
522 
523  if (!DeleteService(service)) {
524  error = GetLastError();
525  if (error == ERROR_SERVICE_MARKED_FOR_DELETE) {
526  Com_Printf("Service has already been marked for deletion.\n");
527  } else {
528  Com_EPrintf("%#lx deleting service.\n", error);
529  }
530  } else {
531  Com_Printf("Service deleted successfully.\n");
532  }
533 
534  CloseServiceHandle(service);
535 
536 fail:
537  CloseServiceHandle(scm);
538 }
539 
540 #endif // USE_WINSVC
541 
542 /*
543 ===============================================================================
544 
545 MISC
546 
547 ===============================================================================
548 */
549 
550 #if USE_SYSCON
551 /*
552 ================
553 Sys_Printf
554 ================
555 */
556 void Sys_Printf(const char *fmt, ...)
557 {
558  va_list argptr;
559  char msg[MAXPRINTMSG];
560 
561  va_start(argptr, fmt);
562  Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
563  va_end(argptr);
564 
565  Sys_ConsoleOutput(msg);
566 }
567 #endif
568 
569 /*
570 ================
571 Sys_Error
572 ================
573 */
574 void Sys_Error(const char *error, ...)
575 {
576  va_list argptr;
577  char text[MAXERRORMSG];
578 
579  va_start(argptr, error);
580  Q_vsnprintf(text, sizeof(text), error, argptr);
581  va_end(argptr);
582 
583  errorEntered = qtrue;
584 
585 #if USE_CLIENT
586  VID_Shutdown();
587 #endif
588 
589 #if USE_SYSCON
590  Sys_SetConsoleColor(COLOR_RED);
591  Sys_Printf("********************\n"
592  "FATAL: %s\n"
593  "********************\n", text);
594  Sys_SetConsoleColor(COLOR_NONE);
595 #endif
596 
597 #if USE_WINSVC
598  if (!statusHandle)
599 #endif
600  {
601 #if USE_SYSCON
602  if (gotConsole) {
603  Sleep(INFINITE);
604  }
605 #endif
606  MessageBoxA(NULL, text, PRODUCT " Fatal Error", MB_ICONERROR | MB_OK);
607  }
608 
609  exit(1);
610 }
611 
612 /*
613 ================
614 Sys_Quit
615 
616 This function never returns.
617 ================
618 */
619 void Sys_Quit(void)
620 {
621  timeEndPeriod(1);
622 
623 #if USE_CLIENT
624 #if USE_SYSCON
625  if (dedicated && dedicated->integer) {
626  FreeConsole();
627  }
628 #endif
629 #elif USE_WINSVC
630  if (statusHandle && !shouldExit) {
631  shouldExit = SE_YES;
632  Com_AbortFrame();
633  }
634 #endif
635 
636  exit(0);
637 }
638 
639 void Sys_DebugBreak(void)
640 {
641  DebugBreak();
642 }
643 
644 unsigned Sys_Milliseconds(void)
645 {
646  return timeGetTime();
647 }
648 
650 {
651 }
652 
653 void Sys_Sleep(int msec)
654 {
655  Sleep(msec);
656 }
657 
658 qboolean
659 Sys_IsDir(const char *path)
660 {
661  WCHAR wpath[MAX_OSPATH] = { 0 };
662  MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_OSPATH);
663 
664  DWORD fileAttributes = GetFileAttributesW(wpath);
665  if (fileAttributes == INVALID_FILE_ATTRIBUTES)
666  {
667  return qfalse;
668  }
669 
670  return (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
671 }
672 
673 qboolean
674 Sys_IsFile(const char *path)
675 {
676  WCHAR wpath[MAX_OSPATH] = { 0 };
677  MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_OSPATH);
678 
679  DWORD fileAttributes = GetFileAttributesW(wpath);
680  if (fileAttributes == INVALID_FILE_ATTRIBUTES)
681  {
682  return qfalse;
683  }
684 
685  // I guess the assumption that if it's not a file or device
686  // then it's a directory is good enough for us?
687  return (fileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0;
688 }
689 
690 /*
691 ================
692 Sys_Init
693 ================
694 */
695 void Sys_Init(void)
696 {
697  OSVERSIONINFO vinfo;
698 #ifndef _WIN64
699  HMODULE module;
700  BOOL (WINAPI * pSetProcessDEPPolicy)(DWORD);
701 #endif
702  cvar_t *var = NULL;
703 
704  timeBeginPeriod(1);
705 
706  // check windows version
707  vinfo.dwOSVersionInfoSize = sizeof(vinfo);
708 #pragma warning(push)
709 #pragma warning(disable: 4996) // warning C4996: 'GetVersionExA': was declared deprecated
710  if (!GetVersionEx(&vinfo)) {
711  Sys_Error("Couldn't get OS info");
712  }
713 #pragma warning(pop)
714  if (vinfo.dwPlatformId != VER_PLATFORM_WIN32_NT) {
715  Sys_Error(PRODUCT " requires Windows NT");
716  }
717  if (vinfo.dwMajorVersion < 5) {
718  Sys_Error(PRODUCT " requires Windows 2000 or greater");
719  }
720 
721  // basedir <path>
722  // allows the game to run from outside the data tree
723  sys_basedir = Cvar_Get("basedir", currentDirectory, CVAR_NOSET);
724  sys_libdir = Cvar_Get("libdir", currentDirectory, CVAR_NOSET);
725 
726  // homedir <path>
727  // specifies per-user writable directory for demos, screenshots, etc
728  sys_homedir = Cvar_Get("homedir", "", CVAR_NOSET);
729 
730  sys_forcegamelib = Cvar_Get("sys_forcegamelib", "", CVAR_NOSET);
731 
732 #if USE_WINSVC
733  Cmd_AddCommand("installservice", Sys_InstallService_f);
734  Cmd_AddCommand("deleteservice", Sys_DeleteService_f);
735 #endif
736 
737 #if USE_SYSCON
738  houtput = GetStdHandle(STD_OUTPUT_HANDLE);
739 #if USE_CLIENT
740  sys_viewlog = Cvar_Get("sys_viewlog", "0", CVAR_NOSET);
741 
742  if (dedicated->integer || sys_viewlog->integer)
743 #endif
744  Sys_ConsoleInit();
745 #endif // USE_SYSCON
746 
747 #if USE_DBGHELP
748  var = Cvar_Get("sys_disablecrashdump", "0", CVAR_NOSET);
749 
750  // install our exception filter
751  if (!var->integer) {
752  mainProcessThread = GetCurrentThread();
753  prevExceptionFilter = SetUnhandledExceptionFilter(
755  }
756 #endif
757 
758 #ifndef _WIN64
759  module = GetModuleHandle("kernel32.dll");
760  if (module) {
761  pSetProcessDEPPolicy = (PVOID)GetProcAddress(module,
762  "SetProcessDEPPolicy");
763  if (pSetProcessDEPPolicy) {
764  var = Cvar_Get("sys_disabledep", "0", CVAR_NOSET);
765 
766  // opt-in or opt-out for DEP
767  if (!var->integer) {
768  pSetProcessDEPPolicy(
771  } else if (var->integer == 2) {
772  pSetProcessDEPPolicy(0);
773  }
774  }
775  }
776 #endif
777 }
778 
779 /*
780 ========================================================================
781 
782 DLL LOADING
783 
784 ========================================================================
785 */
786 
788 {
789  if (handle && !FreeLibrary(handle)) {
790  Com_Error(ERR_FATAL, "FreeLibrary failed on %p", handle);
791  }
792 }
793 
794 void *Sys_LoadLibrary(const char *path, const char *sym, void **handle)
795 {
796  HMODULE module;
797  void *entry;
798 
799  *handle = NULL;
800 
801  module = LoadLibraryA(path);
802  if (!module) {
803  Com_SetLastError(va("%s: LoadLibrary failed with error %lu",
804  path, GetLastError()));
805  return NULL;
806  }
807 
808  if (sym) {
809  entry = GetProcAddress(module, sym);
810  if (!entry) {
811  Com_SetLastError(va("%s: GetProcAddress(%s) failed with error %lu",
812  path, sym, GetLastError()));
813  FreeLibrary(module);
814  return NULL;
815  }
816  } else {
817  entry = NULL;
818  }
819 
820  *handle = module;
821  return entry;
822 }
823 
824 void *Sys_GetProcAddress(void *handle, const char *sym)
825 {
826  void *entry;
827 
828  entry = GetProcAddress(handle, sym);
829  if (!entry)
830  Com_SetLastError(va("GetProcAddress(%s) failed with error %lu",
831  sym, GetLastError()));
832 
833  return entry;
834 }
835 
836 /*
837 ========================================================================
838 
839 FILESYSTEM
840 
841 ========================================================================
842 */
843 
844 static inline time_t file_time_to_unix(FILETIME *f)
845 {
846  ULARGE_INTEGER u = *(ULARGE_INTEGER *)f;
847  return (time_t)((u.QuadPart - 116444736000000000ULL) / 10000000);
848 }
849 
850 static void *copy_info(const char *name, const LPWIN32_FIND_DATAA data)
851 {
852  time_t ctime = file_time_to_unix(&data->ftCreationTime);
853  time_t mtime = file_time_to_unix(&data->ftLastWriteTime);
854 
855  return FS_CopyInfo(name, data->nFileSizeLow, ctime, mtime);
856 }
857 
858 /*
859 =================
860 Sys_ListFiles_r
861 
862 Internal function to filesystem. Conventions apply:
863  - files should hold at least MAX_LISTED_FILES
864  - *count_p must be initialized in range [0, MAX_LISTED_FILES - 1]
865  - depth must be 0 on the first call
866 =================
867 */
868 void Sys_ListFiles_r(const char *path,
869  const char *filter,
870  unsigned flags,
871  size_t baselen,
872  int *count_p,
873  void **files,
874  int depth)
875 {
876  WIN32_FIND_DATAA data;
877  HANDLE handle;
878  char fullpath[MAX_OSPATH], *name;
879  size_t pathlen, len;
880  unsigned mask;
881  void *info;
882 
883  // optimize single extension search
884  if (!(flags & FS_SEARCH_BYFILTER) &&
885  filter && !strchr(filter, ';')) {
886  if (*filter == '.') {
887  filter++;
888  }
889  len = Q_concat(fullpath, sizeof(fullpath),
890  path, "\\*.", filter, NULL);
891  filter = NULL; // do not check it later
892  } else {
893  len = Q_concat(fullpath, sizeof(fullpath),
894  path, "\\*", NULL);
895  }
896 
897  if (len >= sizeof(fullpath)) {
898  return;
899  }
900 
901  // format path to windows style
902  // done on the first run only
903  if (!depth) {
904  FS_ReplaceSeparators(fullpath, '\\');
905  }
906 
907  handle = FindFirstFileA(fullpath, &data);
908  if (handle == INVALID_HANDLE_VALUE) {
909  return;
910  }
911 
912  // make it point right after the slash
913  pathlen = strlen(path) + 1;
914 
915  do {
916  if (!strcmp(data.cFileName, ".") ||
917  !strcmp(data.cFileName, "..")) {
918  continue; // ignore special entries
919  }
920 
921  // construct full path
922  len = strlen(data.cFileName);
923  if (pathlen + len >= sizeof(fullpath)) {
924  continue;
925  }
926 
927  memcpy(fullpath + pathlen, data.cFileName, len + 1);
928 
929  if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
930  mask = FS_SEARCH_DIRSONLY;
931  } else {
932  mask = 0;
933  }
934 
935  // pattern search implies recursive search
936  if ((flags & FS_SEARCH_BYFILTER) && mask &&
937  depth < MAX_LISTED_DEPTH) {
938  Sys_ListFiles_r(fullpath, filter, flags, baselen,
939  count_p, files, depth + 1);
940 
941  // re-check count
942  if (*count_p >= MAX_LISTED_FILES) {
943  break;
944  }
945  }
946 
947  // check type
948  if ((flags & FS_SEARCH_DIRSONLY) != mask) {
949  continue;
950  }
951 
952  // check filter
953  if (filter) {
954  if (flags & FS_SEARCH_BYFILTER) {
955  if (!FS_WildCmp(filter, fullpath + baselen)) {
956  continue;
957  }
958  } else {
959  if (!FS_ExtCmp(filter, data.cFileName)) {
960  continue;
961  }
962  }
963  }
964 
965  // strip path
966  if (flags & FS_SEARCH_SAVEPATH) {
967  name = fullpath + baselen;
968  } else {
969  name = data.cFileName;
970  }
971 
972  // reformat it back to quake filesystem style
973  FS_ReplaceSeparators(name, '/');
974 
975  // strip extension
976  if (flags & FS_SEARCH_STRIPEXT) {
977  *COM_FileExtension(name) = 0;
978 
979  if (!*name) {
980  continue;
981  }
982  }
983 
984  // copy info off
985  if (flags & FS_SEARCH_EXTRAINFO) {
986  info = copy_info(name, &data);
987  } else {
988  info = FS_CopyString(name);
989  }
990 
991  files[(*count_p)++] = info;
992  } while (*count_p < MAX_LISTED_FILES &&
993  FindNextFileA(handle, &data) != FALSE);
994 
995  FindClose(handle);
996 }
997 
998 /*
999 ========================================================================
1000 
1001 MAIN
1002 
1003 ========================================================================
1004 */
1005 
1007 {
1008  char *p;
1009 
1010  if (!GetModuleFileNameA(NULL, currentDirectory, sizeof(currentDirectory) - 1)) {
1011  return FALSE;
1012  }
1013 
1014  if ((p = strrchr(currentDirectory, '\\')) != NULL) {
1015  *p = 0;
1016  }
1017 
1018 #ifndef UNDER_CE
1019  if (!SetCurrentDirectoryA(currentDirectory)) {
1020  return FALSE;
1021  }
1022 #endif
1023 
1024  return TRUE;
1025 }
1026 
1027 #if (_MSC_VER >= 1400)
1028 static void msvcrt_sucks(const wchar_t *expr, const wchar_t *func,
1029  const wchar_t *file, unsigned int line, uintptr_t unused)
1030 {
1031 }
1032 #endif
1033 
1034 static int Sys_Main(int argc, char **argv)
1035 {
1036  // fix current directory to point to the basedir
1037  if (!fix_current_directory()) {
1038  return 1;
1039  }
1040 
1041 #if (_MSC_VER >= 1400)
1042  // work around strftime given invalid format string
1043  // killing the whole fucking process :((
1044  _set_invalid_parameter_handler(msvcrt_sucks);
1045 #endif
1046 
1047  Qcommon_Init(argc, argv);
1048 
1049  // main program loop
1050  while (1) {
1051  Qcommon_Frame();
1052  if (shouldExit) {
1053 #if USE_WINSVC
1054  if (shouldExit == SE_FULL)
1055 #endif
1056  Com_Quit(NULL, ERR_DISCONNECT);
1057  break;
1058  }
1059  }
1060 
1061  // may get here when our service stops
1062  return 0;
1063 }
1064 
1065 #if USE_CLIENT
1066 
1067 #define MAX_LINE_TOKENS 128
1068 
1069 static char *sys_argv[MAX_LINE_TOKENS];
1070 static int sys_argc;
1071 
1072 /*
1073 ===============
1074 Sys_ParseCommandLine
1075 
1076 ===============
1077 */
1078 static void Sys_ParseCommandLine(char *line)
1079 {
1080  sys_argc = 1;
1081  sys_argv[0] = APPLICATION;
1082  while (*line) {
1083  while (*line && *line <= 32) {
1084  line++;
1085  }
1086  if (*line == 0) {
1087  break;
1088  }
1089  sys_argv[sys_argc++] = line;
1090  while (*line > 32) {
1091  line++;
1092  }
1093  if (*line == 0) {
1094  break;
1095  }
1096  *line = 0;
1097  if (sys_argc == MAX_LINE_TOKENS) {
1098  break;
1099  }
1100  line++;
1101  }
1102 }
1103 
1104 /*
1105 ==================
1106 WinMain
1107 
1108 ==================
1109 */
1110 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
1111 {
1112  // previous instances do not exist in Win32
1113  if (hPrevInstance) {
1114  return 1;
1115  }
1116 
1117  hGlobalInstance = hInstance;
1118 #ifndef UNICODE
1119  // TODO: wince support
1120  Sys_ParseCommandLine(lpCmdLine);
1121 #endif
1122  return Sys_Main(sys_argc, sys_argv);
1123 }
1124 
1125 #else // USE_CLIENT
1126 
1127 #if USE_WINSVC
1128 
1129 static char **sys_argv;
1130 static int sys_argc;
1131 
1132 static VOID WINAPI ServiceHandler(DWORD fdwControl)
1133 {
1134  if (fdwControl == SERVICE_CONTROL_STOP) {
1135  shouldExit = SE_FULL;
1136  }
1137 }
1138 
1139 static VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
1140 {
1141  SERVICE_STATUS status;
1142 
1143  statusHandle = RegisterServiceCtrlHandler(APPLICATION, ServiceHandler);
1144  if (!statusHandle) {
1145  return;
1146  }
1147 
1148  memset(&status, 0, sizeof(status));
1149  status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
1150  status.dwCurrentState = SERVICE_RUNNING;
1151  status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1152  SetServiceStatus(statusHandle, &status);
1153 
1154  Sys_Main(sys_argc, sys_argv);
1155 
1156  status.dwCurrentState = SERVICE_STOPPED;
1157  status.dwControlsAccepted = 0;
1158  SetServiceStatus(statusHandle, &status);
1159 }
1160 
1161 static SERVICE_TABLE_ENTRY serviceTable[] = {
1162  { APPLICATION, ServiceMain },
1163  { NULL, NULL }
1164 };
1165 
1166 #endif // USE_WINSVC
1167 
1168 /*
1169 ==================
1170 main
1171 
1172 ==================
1173 */
1174 int main(int argc, char **argv)
1175 {
1176 #if USE_WINSVC
1177  int i;
1178 #endif
1179 
1180  hGlobalInstance = GetModuleHandle(NULL);
1181 
1182 #if USE_WINSVC
1183  for (i = 1; i < argc; i++) {
1184  if (!strcmp(argv[i], "-service")) {
1185  argv[i] = NULL;
1186  sys_argc = argc;
1187  sys_argv = argv;
1188  if (StartServiceCtrlDispatcher(serviceTable)) {
1189  return 0;
1190  }
1191  if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
1192  break; // fall back to normal server startup
1193  }
1194  return 1;
1195  }
1196  }
1197 #endif
1198 
1199  return Sys_Main(argc, argv);
1200 }
1201 
1202 #endif // !USE_CLIENT
1203 
handle
static void * handle
Definition: dynamic.c:52
Prompt_CompleteCommand
void Prompt_CompleteCommand(commandPrompt_t *prompt, qboolean backslash)
Definition: prompt.c:190
FS_CopyInfo
file_info_t * FS_CopyInfo(const char *name, size_t size, time_t ctime, time_t mtime)
Definition: files.c:2599
file_time_to_unix
static time_t file_time_to_unix(FILETIME *f)
Definition: system.c:844
Sys_ListFiles_r
void Sys_ListFiles_r(const char *path, const char *filter, unsigned flags, size_t baselen, int *count_p, void **files, int depth)
Definition: system.c:868
Com_AbortFrame
void Com_AbortFrame(void)
Definition: common.c:595
Prompt_HistoryDown
void Prompt_HistoryDown(commandPrompt_t *prompt)
Definition: prompt.c:519
Qcommon_Frame
void Qcommon_Frame(void)
Definition: common.c:1078
FS_ExtCmp
qboolean FS_ExtCmp(const char *ext, const char *name)
Definition: files.c:2649
Cmd_AddCommand
void Cmd_AddCommand(const char *name, xcommand_t function)
Definition: cmd.c:1562
sys_homedir
cvar_t * sys_homedir
Definition: system.c:52
Cvar_Get
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags)
Definition: cvar.c:257
sys_libdir
cvar_t * sys_libdir
Definition: system.c:51
FS_ReplaceSeparators
char * FS_ReplaceSeparators(char *s, int separator)
Definition: files.c:235
shouldExit
static volatile should_exit_t shouldExit
Definition: system.c:47
Sys_ExceptionFilter
LONG WINAPI Sys_ExceptionFilter(LPEXCEPTION_POINTERS exceptionInfo)
Definition: debug.c:180
Prompt_Action
char * Prompt_Action(commandPrompt_t *prompt)
Definition: prompt.c:453
Q_vsnprintf
size_t Q_vsnprintf(char *dest, size_t size, const char *fmt, va_list argptr)
Definition: shared.c:791
FindNextFileA
BOOL WINAPI FindNextFileA(HANDLE handle, LPWIN32_FIND_DATAA data)
Definition: ascii.c:22
Sys_AddDefaultConfig
void Sys_AddDefaultConfig(void)
Definition: system.c:649
Cmd_RawArgsFrom
char * Cmd_RawArgsFrom(int from)
Definition: cmd.c:1021
Sys_Main
static int Sys_Main(int argc, char **argv)
Definition: system.c:1034
Cmd_Argv
char * Cmd_Argv(int arg)
Definition: cmd.c:899
Sys_Milliseconds
unsigned Sys_Milliseconds(void)
Definition: system.c:644
Sys_Error
void Sys_Error(const char *error,...)
Definition: system.c:574
Cmd_Argc
int Cmd_Argc(void)
Definition: cmd.c:889
BOOL
BOOL(WINAPI *qwglSetPixelFormat)(HDC
width
static int width
Definition: physical_sky.c:38
Sys_IsDir
qboolean Sys_IsDir(const char *path)
Definition: system.c:659
cmd_buffer
cmdbuf_t cmd_buffer
Definition: cmd.c:49
fix_current_directory
static BOOL fix_current_directory(void)
Definition: system.c:1006
should_exit_t
should_exit_t
Definition: system.c:41
hGlobalInstance
HINSTANCE hGlobalInstance
Definition: system.c:28
Com_Error
void Com_Error(error_type_t type, const char *fmt,...)
Definition: g_main.c:258
dedicated
cvar_t * dedicated
Definition: g_main.c:46
Prompt_HistoryUp
void Prompt_HistoryUp(commandPrompt_t *prompt)
Definition: prompt.c:490
errorEntered
static volatile qboolean errorEntered
Definition: system.c:48
currentDirectory
static char currentDirectory[MAX_OSPATH]
Definition: system.c:35
Cbuf_AddText
void Cbuf_AddText(cmdbuf_t *buf, const char *text)
Definition: cmd.c:95
va
char * va(const char *format,...)
Definition: shared.c:429
client.h
PROCESS_DEP_ENABLE
#define PROCESS_DEP_ENABLE
Definition: client.h:46
VID_Shutdown
void VID_Shutdown(void)
Definition: glimp.c:54
FS_WildCmp
qboolean FS_WildCmp(const char *filter, const char *string)
Definition: files.c:2636
SE_FULL
@ SE_FULL
Definition: system.c:44
Sys_GetProcAddress
void * Sys_GetProcAddress(void *handle, const char *sym)
Definition: system.c:824
Sys_IsFile
qboolean Sys_IsFile(const char *path)
Definition: system.c:674
copy_info
static void * copy_info(const char *name, const LPWIN32_FIND_DATAA data)
Definition: system.c:850
FindFirstFileA
HANDLE WINAPI FindFirstFileA(LPCSTR path, LPWIN32_FIND_DATAA data)
Definition: ascii.c:3
SE_YES
@ SE_YES
Definition: system.c:43
GetModuleFileNameA
DWORD WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFileName, DWORD nSize)
Definition: ascii.c:84
Sys_LoadLibrary
void * Sys_LoadLibrary(const char *path, const char *sym, void **handle)
Definition: system.c:794
main
int main(int argc, char **argv)
Definition: system.c:1174
c
statCounters_t c
Definition: main.c:30
sys_forcegamelib
cvar_t * sys_forcegamelib
Definition: system.c:53
Com_Quit
void Com_Quit(const char *reason, error_type_t type)
Definition: common.c:609
MessageBoxA
int WINAPI MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
Definition: ascii.c:47
Com_SetLastError
void Com_SetLastError(const char *msg)
Definition: common.c:382
Sys_Sleep
void Sys_Sleep(int msec)
Definition: system.c:653
msg
const char * msg
Definition: win.h:25
Qcommon_Init
void Qcommon_Init(int argc, char **argv)
Definition: common.c:893
Sys_Init
void Sys_Init(void)
Definition: system.c:695
sys_basedir
cvar_t * sys_basedir
Definition: system.c:50
color
static vec4_t color
Definition: mesh.c:33
SE_NOT
@ SE_NOT
Definition: system.c:42
Q_concat
size_t Q_concat(char *dest, size_t size,...)
Definition: shared.c:758
Sys_Quit
void Sys_Quit(void)
Definition: system.c:619
Sys_FreeLibrary
void Sys_FreeLibrary(void *handle)
Definition: system.c:787
COM_FileExtension
char * COM_FileExtension(const char *in)
Definition: shared.c:199
IF_Init
void IF_Init(inputField_t *field, size_t visibleChars, size_t maxChars)
Definition: field.c:36
PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION
#define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION
Definition: client.h:49
LoadLibraryA
HINSTANCE WINAPI LoadLibraryA(LPCSTR path)
Definition: ascii.c:36
Sys_DebugBreak
void Sys_DebugBreak(void)
Definition: system.c:639