diff --git a/src/windows/system.c b/src/windows/system.c new file mode 100644 index 00000000..039f3a44 --- /dev/null +++ b/src/windows/system.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * This file is the starting point of the program and implements + * several support functions and the main loop. + * + * ======================================================================= + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../common/header/common.h" +#include "conproc.h" +#include "resource.h" +#include "winquake.h" + +#define MAX_NUM_ARGVS 128 + +int starttime; +int ActiveApp; +qboolean Minimized; + +static HANDLE hinput, houtput; +static HANDLE qwclsemaphore; + +HINSTANCE global_hInstance; +static HINSTANCE game_library; + +unsigned int sys_msg_time; +unsigned int sys_frame_time; + +static char console_text[256]; +static int console_textlen; + +int argc; +char *argv[MAX_NUM_ARGVS]; + +/* ================================================================ */ + +void +Sys_Error(char *error, ...) +{ + va_list argptr; + char text[1024]; + + CL_Shutdown(); + Qcommon_Shutdown(); + + va_start(argptr, error); + vsprintf(text, error, argptr); + va_end(argptr); + + MessageBox(NULL, text, "Error", 0 /* MB_OK */); + + if (qwclsemaphore) + { + CloseHandle(qwclsemaphore); + } + + /* shut down QHOST hooks if necessary */ + DeinitConProc(); + + exit(1); +} + +void +Sys_Quit(void) +{ + timeEndPeriod(1); + + CL_Shutdown(); + Qcommon_Shutdown(); + CloseHandle(qwclsemaphore); + + if (dedicated && dedicated->value) + { + FreeConsole(); + } + + /* shut down QHOST hooks if necessary */ + DeinitConProc(); + + exit(0); +} + +void +WinError(void) +{ + LPVOID lpMsgBuf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, 0, NULL); + + /* Display the string. */ + MessageBox(NULL, lpMsgBuf, "GetLastError", MB_OK | MB_ICONINFORMATION); + + /* Free the buffer. */ + LocalFree(lpMsgBuf); +} + +/* ================================================================ */ + +char * +Sys_ScanForCD(void) +{ + static char cddir[MAX_OSPATH]; + static qboolean done; + + char drive[4]; + FILE *f; + char test[MAX_QPATH]; + + if (done) /* don't re-check */ + { + return cddir; + } + + /* no abort/retry/fail errors */ + SetErrorMode(SEM_FAILCRITICALERRORS); + + drive[0] = 'c'; + drive[1] = ':'; + drive[2] = '\\'; + drive[3] = 0; + + done = true; + + /* scan the drives */ + for (drive[0] = 'c'; drive[0] <= 'z'; drive[0]++) + { + /* where activision put the stuff... */ + sprintf(cddir, "%sinstall\\data", drive); + sprintf(test, "%sinstall\\data\\quake2.exe", drive); + f = fopen(test, "r"); + + if (f) + { + fclose(f); + + if (GetDriveType(drive) == DRIVE_CDROM) + { + return cddir; + } + } + } + + cddir[0] = 0; + + return NULL; +} + +/* ================================================================ */ + +void +Sys_Init(void) +{ + OSVERSIONINFO vinfo; + + timeBeginPeriod(1); + vinfo.dwOSVersionInfoSize = sizeof(vinfo); + + if (!GetVersionEx(&vinfo)) + { + Sys_Error("Couldn't get OS info"); + } + + /* While Quake II should run on older versions, + limit Yamagi Quake II to Windows XP and + above. Testing older version would be a + PITA. */ + if (!(vinfo.dwMajorVersion > 5) || + ((vinfo.dwMajorVersion == 5) && + (vinfo.dwMinorVersion >= 1))) + { + Sys_Error("Yamagi Quake II needs Windows XP or higher!\n"); + } + + + if (dedicated->value) + { + if (!AllocConsole()) + { + Sys_Error("Couldn't create dedicated server console"); + } + + hinput = GetStdHandle(STD_INPUT_HANDLE); + houtput = GetStdHandle(STD_OUTPUT_HANDLE); + + /* let QHOST hook in */ + InitConProc(argc, argv); + } +} + +char * +Sys_ConsoleInput(void) +{ + INPUT_RECORD recs[1024]; + int dummy; + int ch, numread, numevents; + + if (!dedicated || !dedicated->value) + { + return NULL; + } + + for ( ; ; ) + { + if (!GetNumberOfConsoleInputEvents(hinput, &numevents)) + { + Sys_Error("Error getting # of console events"); + } + + if (numevents <= 0) + { + break; + } + + if (!ReadConsoleInput(hinput, recs, 1, &numread)) + { + Sys_Error("Error reading console input"); + } + + if (numread != 1) + { + Sys_Error("Couldn't read console input"); + } + + if (recs[0].EventType == KEY_EVENT) + { + if (!recs[0].Event.KeyEvent.bKeyDown) + { + ch = recs[0].Event.KeyEvent.uChar.AsciiChar; + + switch (ch) + { + case '\r': + WriteFile(houtput, "\r\n", 2, &dummy, NULL); + + if (console_textlen) + { + console_text[console_textlen] = 0; + console_textlen = 0; + return console_text; + } + + break; + + case '\b': + + if (console_textlen) + { + console_textlen--; + WriteFile(houtput, "\b \b", 3, &dummy, NULL); + } + + break; + + default: + + if (ch >= ' ') + { + if (console_textlen < sizeof(console_text) - 2) + { + WriteFile(houtput, &ch, 1, &dummy, NULL); + console_text[console_textlen] = ch; + console_textlen++; + } + } + + break; + } + } + } + } + + return NULL; +} + +void +Sys_ConsoleOutput(char *string) +{ + int dummy; + char text[256]; + + if (!dedicated || !dedicated->value) + { + return; + } + + if (console_textlen) + { + text[0] = '\r'; + memset(&text[1], ' ', console_textlen); + text[console_textlen + 1] = '\r'; + text[console_textlen + 2] = 0; + WriteFile(houtput, text, console_textlen + 2, &dummy, NULL); + } + + WriteFile(houtput, string, strlen(string), &dummy, NULL); + + if (console_textlen) + { + WriteFile(houtput, console_text, console_textlen, &dummy, NULL); + } +} + +void +Sys_SendKeyEvents(void) +{ + MSG msg; + + while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) + { + if (!GetMessage(&msg, NULL, 0, 0)) + { + Sys_Quit(); + } + + sys_msg_time = msg.time; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* grab frame time */ + sys_frame_time = timeGetTime(); +} + +char * +Sys_GetClipboardData(void) +{ + char *data = NULL; + char *cliptext; + + if (OpenClipboard(NULL) != 0) + { + HANDLE hClipboardData; + + if ((hClipboardData = GetClipboardData(CF_TEXT)) != 0) + { + if ((cliptext = GlobalLock(hClipboardData)) != 0) + { + data = malloc(GlobalSize(hClipboardData) + 1); + strcpy(data, cliptext); + GlobalUnlock(hClipboardData); + } + } + + CloseClipboard(); + } + + return data; +} + +/* ================================================================ */ + +void +Sys_AppActivate(void) +{ + ShowWindow(cl_hwnd, SW_RESTORE); + SetForegroundWindow(cl_hwnd); +} + +/* ================================================================ */ + +void +Sys_UnloadGame(void) +{ + if (!FreeLibrary(game_library)) + { + Com_Error(ERR_FATAL, "FreeLibrary failed for game library"); + } + + game_library = NULL; +} + +void * +Sys_GetGameAPI(void *parms) +{ + void *(*GetGameAPI)(void *); + const char *gamename = "game.dll"; + char name[MAX_OSPATH]; + char *path = NULL; + char cwd[MAX_OSPATH]; + + if (game_library) + { + Com_Error(ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame"); + } + + /* now run through the search paths */ + path = NULL; + + while (1) + { + path = FS_NextPath(path); + + if (!path) + { + return NULL; /* couldn't find one anywhere */ + } + + Com_sprintf(name, sizeof(name), "%s/%s", path, gamename); + game_library = LoadLibrary(name); + + if (game_library) + { + Com_DPrintf("LoadLibrary (%s)\n", name); + break; + } + } + + GetGameAPI = (void *)GetProcAddress(game_library, "GetGameAPI"); + + if (!GetGameAPI) + { + Sys_UnloadGame(); + return NULL; + } + + return GetGameAPI(parms); +} + +/* ======================================================================= */ + +void +ParseCommandLine(LPSTR lpCmdLine) +{ + argc = 1; + argv[0] = "exe"; + + while (*lpCmdLine && (argc < MAX_NUM_ARGVS)) + { + while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) + { + lpCmdLine++; + } + + if (*lpCmdLine) + { + argv[argc] = lpCmdLine; + argc++; + + while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) + { + lpCmdLine++; + } + + if (*lpCmdLine) + { + *lpCmdLine = 0; + lpCmdLine++; + } + } + } +} + +/* + * Windows main function. Containts the + * initialization code and the main loop + */ +int WINAPI +WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + int time, oldtime, newtime; + char *cddir; + + /* Previous instances do not exist in Win32 */ + if (hPrevInstance) + { + return 0; + } + + /* Make the current instance global */ + global_hInstance = hInstance; + + /* Parse the command line arguments */ + ParseCommandLine(lpCmdLine); + + /* Search the CD (for partial installations) */ + cddir = Sys_ScanForCD(); + + if (cddir && (argc < MAX_NUM_ARGVS - 3)) + { + int i; + + /* don't override a cddir on the command line */ + for (i = 0; i < argc; i++) + { + if (!strcmp(argv[i], "cddir")) + { + break; + } + } + + if (i == argc) + { + argv[argc++] = "+set"; + argv[argc++] = "cddir"; + argv[argc++] = cddir; + } + } + + /* Call the initialization code */ + Qcommon_Init(argc, argv); + + /* Save our time */ + oldtime = Sys_Milliseconds(); + + /* The legendary main loop */ + while (1) + { + /* If at a full screen console, don't update unless needed */ + if (Minimized || (dedicated && dedicated->value)) + { + Sleep(1); + } + + while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) + { + if (!GetMessage(&msg, NULL, 0, 0)) + { + Com_Quit(); + } + + sys_msg_time = msg.time; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + do + { + newtime = Sys_Milliseconds(); + time = newtime - oldtime; + } + while (time < 1); + + _controlfp(_PC_24, _MCW_PC); + Qcommon_Frame(time); + + oldtime = newtime; + } + + /* never gets here */ + return TRUE; +} +