-/*\r
-Copyright (c) 2001, Loki software, inc.\r
-All rights reserved.\r
-\r
-Redistribution and use in source and binary forms, with or without modification, \r
-are permitted provided that the following conditions are met:\r
-\r
-Redistributions of source code must retain the above copyright notice, this list \r
-of conditions and the following disclaimer.\r
-\r
-Redistributions in binary form must reproduce the above copyright notice, this\r
-list of conditions and the following disclaimer in the documentation and/or\r
-other materials provided with the distribution.\r
-\r
-Neither the name of Loki software nor the names of its contributors may be used \r
-to endorse or promote products derived from this software without specific prior \r
-written permission. \r
-\r
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' \r
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \r
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \r
-DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY \r
-DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \r
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \r
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON \r
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT \r
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS \r
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \r
-*/\r
-\r
-//-----------------------------------------------------------------------------\r
-//\r
-// DESCRIPTION:\r
-// monitoring window for running BSP processes (and possibly various other stuff)\r
-\r
-#include "stdafx.h"\r
-#include "watchbsp.h"\r
-#include "feedback.h"\r
-\r
-#ifdef _WIN32\r
-#include <winsock2.h>\r
-#endif\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
-#include <sys/time.h>\r
-#define SOCKET_ERROR -1\r
-#endif\r
-\r
-#ifdef __APPLE__\r
-#include <unistd.h>\r
-#endif\r
-\r
-#include <assert.h>\r
-\r
-// Static functions for the SAX callbacks -------------------------------------------------------\r
-\r
-// utility for saxStartElement below\r
-static void abortStream(message_info_t *data)\r
-{\r
- g_pParentWnd->GetWatchBSP()->Reset();\r
- // tell there has been an error\r
- if (g_pParentWnd->GetWatchBSP()->HasBSPPlugin ())\r
- g_BSPFrontendTable.m_pfnEndListen(2);\r
- // yeah this doesn't look good.. but it's needed so that everything will be ignored until the stream goes out\r
- data->ignore_depth = -1;\r
- data->recurse++;\r
-}\r
-\r
-#include "stream_version.h"\r
-\r
-static void saxStartElement(message_info_t *data, const xmlChar *name, const xmlChar **attrs) \r
-{\r
- if (data->ignore_depth == 0)\r
- {\r
- if (data->bGeometry)\r
- // we have a handler\r
- {\r
- data->pGeometry->saxStartElement (data, name, attrs);\r
- }\r
- else\r
- {\r
- if (strcmp ((char *)name, "q3map_feedback") == 0)\r
- {\r
- // check the correct version\r
- // old q3map don't send a version attribute\r
- // the ones we support .. send Q3MAP_STREAM_VERSION\r
- if (!attrs[0] || !attrs[1] || (strcmp((char*)attrs[0],"version") != 0))\r
- {\r
- Sys_FPrintf(SYS_ERR, "No stream version given in the feedback stream, this is an old q3map version.\n"\r
- "Please turn off monitored compiling if you still wish to use this q3map executable\n");\r
- abortStream(data);\r
- return;\r
- }\r
- else if (strcmp((char*)attrs[1],Q3MAP_STREAM_VERSION) != 0)\r
- {\r
- Sys_FPrintf(SYS_ERR, \r
- "This version of Radiant reads version %s debug streams, I got an incoming connection with version %s\n"\r
- "Please make sure your versions of Radiant and q3map are matching.\n", Q3MAP_STREAM_VERSION, (char*)attrs[1]);\r
- abortStream(data);\r
- return;\r
- }\r
- }\r
- // we don't treat locally\r
- else if (strcmp ((char *)name, "message") == 0)\r
- {\r
- data->msg_level = atoi ((char *)attrs[1]);\r
- }\r
- else if (strcmp ((char *)name, "polyline") == 0) \r
- // polyline has a particular status .. right now we only use it for leakfile ..\r
- {\r
- data->bGeometry = true;\r
- data->pGeometry = &g_pointfile;\r
- data->pGeometry->saxStartElement (data, name, attrs); \r
- }\r
- else if (strcmp ((char *)name, "select") == 0)\r
- {\r
- CSelectMsg *pSelect = new CSelectMsg();\r
- data->bGeometry = true;\r
- data->pGeometry = pSelect;\r
- data->pGeometry->saxStartElement (data, name, attrs);\r
- }\r
- else if (strcmp ((char *)name, "pointmsg") == 0)\r
- {\r
- CPointMsg *pPoint = new CPointMsg();\r
- data->bGeometry = true;\r
- data->pGeometry = pPoint;\r
- data->pGeometry->saxStartElement (data, name, attrs);\r
- }\r
- else if (strcmp ((char *)name, "windingmsg") == 0)\r
- {\r
- CWindingMsg *pWinding = new CWindingMsg();\r
- data->bGeometry = true;\r
- data->pGeometry = pWinding;\r
- data->pGeometry->saxStartElement (data, name, attrs);\r
- }\r
- else\r
- {\r
- Sys_FPrintf (SYS_WRN, "WARNING: ignoring unrecognized node in XML stream (%s)\n", name);\r
- // we don't recognize this node, jump over it\r
- // (NOTE: the ignore mechanism is a bit screwed, only works when starting an ignore at the highest level)\r
- data->ignore_depth = data->recurse;\r
- }\r
- }\r
- }\r
- data->recurse++;\r
-}\r
-\r
-static void saxEndElement(message_info_t *data, const xmlChar *name) \r
-{\r
- data->recurse--;\r
- // we are out of an ignored chunk\r
- if (data->recurse == data->ignore_depth)\r
- {\r
- data->ignore_depth = 0;\r
- return;\r
- }\r
- if (data->bGeometry)\r
- {\r
- data->pGeometry->saxEndElement (data, name);\r
- // we add the object to the debug window\r
- if (!data->bGeometry)\r
- {\r
- g_DbgDlg.Push (data->pGeometry);\r
- }\r
- }\r
- if (data->recurse == data->stop_depth)\r
- {\r
-#ifdef _DEBUG\r
- Sys_Printf ("Received error msg .. shutting down..\n");\r
-#endif\r
- g_pParentWnd->GetWatchBSP()->Reset();\r
- // tell there has been an error\r
- if (g_pParentWnd->GetWatchBSP()->HasBSPPlugin ())\r
- g_BSPFrontendTable.m_pfnEndListen(2);\r
- return;\r
- }\r
-}\r
-\r
-static void saxCharacters(message_info_t *data, const xmlChar *ch, int len)\r
-{\r
- if (data->bGeometry)\r
- {\r
- data->pGeometry->saxCharacters (data, ch, len);\r
- }\r
- else\r
- {\r
- if (data->ignore_depth != 0)\r
- return;\r
- // output the message using the level\r
- char buf[1024];\r
- memcpy( buf, ch, len );\r
- buf[len] = '\0';\r
- Sys_FPrintf (data->msg_level, "%s", buf);\r
- // if this message has error level flag, we mark the depth to stop the compilation when we get out\r
- // we don't set the msg level if we don't stop on leak\r
- if (data->msg_level == 3)\r
- {\r
- data->stop_depth = data->recurse-1;\r
- }\r
- }\r
-}\r
-\r
-static void saxComment(void *ctx, const xmlChar *msg)\r
-{\r
- Sys_Printf("XML comment: %s\n", msg);\r
-}\r
-\r
-static void saxWarning(void *ctx, const char *msg, ...)\r
-{\r
- char saxMsgBuffer[4096];\r
- va_list args;\r
- \r
- va_start(args, msg);\r
- vsprintf (saxMsgBuffer, msg, args);\r
- va_end(args);\r
- Sys_FPrintf(SYS_WRN, "XML warning: %s\n", saxMsgBuffer);\r
-}\r
-\r
-static void saxError(void *ctx, const char *msg, ...)\r
-{\r
- char saxMsgBuffer[4096];\r
- va_list args;\r
- \r
- va_start(args, msg);\r
- vsprintf (saxMsgBuffer, msg, args);\r
- va_end(args);\r
- Sys_FPrintf(SYS_ERR, "XML error: %s\n", saxMsgBuffer);\r
-}\r
-\r
-static void saxFatal(void *ctx, const char *msg, ...)\r
-{\r
- char buffer[4096];\r
- \r
- va_list args;\r
- \r
- va_start(args, msg);\r
- vsprintf (buffer, msg, args);\r
- va_end(args);\r
- Sys_FPrintf(SYS_ERR, "XML fatal error: %s\n", buffer);\r
-}\r
-\r
-static xmlSAXHandler saxParser = {\r
- 0, /* internalSubset */\r
- 0, /* isStandalone */\r
- 0, /* hasInternalSubset */\r
- 0, /* hasExternalSubset */\r
- 0, /* resolveEntity */\r
- 0, /* getEntity */\r
- 0, /* entityDecl */\r
- 0, /* notationDecl */\r
- 0, /* attributeDecl */\r
- 0, /* elementDecl */\r
- 0, /* unparsedEntityDecl */\r
- 0, /* setDocumentLocator */\r
- 0, /* startDocument */\r
- 0, /* endDocument */\r
- (startElementSAXFunc)saxStartElement, /* startElement */\r
- (endElementSAXFunc)saxEndElement, /* endElement */\r
- 0, /* reference */\r
- (charactersSAXFunc)saxCharacters, /* characters */\r
- 0, /* ignorableWhitespace */\r
- 0, /* processingInstruction */\r
- (commentSAXFunc)saxComment, /* comment */\r
- (warningSAXFunc)saxWarning, /* warning */\r
- (errorSAXFunc)saxError, /* error */\r
- (fatalErrorSAXFunc)saxFatal, /* fatalError */\r
-};\r
-\r
-// ------------------------------------------------------------------------------------------------\r
-\r
-CWatchBSP::~CWatchBSP()\r
-{\r
- Reset();\r
- if (m_sBSPName)\r
- {\r
- delete[] m_sBSPName;\r
- m_sBSPName = NULL;\r
- }\r
- Net_Shutdown();\r
-}\r
-\r
-void CWatchBSP::Reset()\r
-{\r
- if (m_pInSocket)\r
- {\r
- Net_Disconnect(m_pInSocket);\r
- m_pInSocket = NULL;\r
- }\r
- if (m_pListenSocket)\r
- {\r
- Net_Disconnect(m_pListenSocket);\r
- m_pListenSocket = NULL;\r
- }\r
- if (m_xmlInputBuffer)\r
- {\r
- xmlFreeParserInputBuffer (m_xmlInputBuffer);\r
- m_xmlInputBuffer = NULL;\r
- }\r
- m_eState = EIdle;\r
-}\r
-\r
-bool CWatchBSP::SetupListening()\r
-{\r
-#ifdef _DEBUG\r
- if (m_pListenSocket)\r
- {\r
- Sys_Printf("ERROR: m_pListenSocket != NULL in CWatchBSP::SetupListening\n");\r
- return false;\r
- }\r
-#endif\r
- Sys_Printf("Setting up\n");\r
- Net_Setup();\r
- m_pListenSocket = Net_ListenSocket(39000);\r
- if (m_pListenSocket == NULL)\r
- return false;\r
- Sys_Printf("Listening...\n");\r
- return true;\r
-}\r
-\r
-void CWatchBSP::DoEBeginStep()\r
-{\r
- Reset();\r
- if (SetupListening() == false)\r
- {\r
- CString msg;\r
- msg = "Failed to get a listening socket on port 39000.\nTry running with BSP monitoring disabled if you can't fix this.\n";\r
- Sys_Printf (msg);\r
- gtk_MessageBox (g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR);\r
- return;\r
- }\r
- // set the timer for timeouts and step cancellation\r
- g_timer_reset( m_pTimer );\r
- g_timer_start( m_pTimer );\r
-\r
- if (!m_bBSPPlugin)\r
- {\r
- Sys_Printf("=== running BSP command ===\n%s\n", g_ptr_array_index( m_pCmd, m_iCurrentStep ) );\r
- \r
- if (!Q_Exec(NULL, (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep ), NULL, true ))\r
- {\r
- CString msg;\r
- msg = "Failed to execute the following command: ";\r
- msg += (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep );\r
- msg += "\nCheck that the file exists and that you don't run out of system resources.\n";\r
- Sys_Printf(msg);\r
- gtk_MessageBox(g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR );\r
- return;\r
- }\r
- // re-initialise the debug window\r
- if (m_iCurrentStep == 0)\r
- g_DbgDlg.Init();\r
- }\r
- m_eState = EBeginStep;\r
-}\r
-\r
-void CWatchBSP::RoutineProcessing()\r
-{\r
- // used for select()\r
-#ifdef _WIN32\r
- TIMEVAL tout = { 0, 0 };\r
-#endif\r
-#if defined (__linux__) || defined (__APPLE__)\r
- timeval tout;\r
- tout.tv_sec = 0;\r
- tout.tv_usec = 0;\r
-#endif\r
-\r
- switch (m_eState)\r
- {\r
- case EBeginStep:\r
- // timeout: if we don't get an incoming connection fast enough, go back to idle\r
- if ( g_timer_elapsed( m_pTimer, NULL ) > g_PrefsDlg.m_iTimeout )\r
- {\r
- gtk_MessageBox(g_pParentWnd->m_pWidget, "The connection timed out, assuming the BSP process failed\nMake sure you are using a networked version of Q3Map?\nOtherwise you need to disable BSP Monitoring in prefs.", "BSP process monitoring", MB_OK );\r
- Reset();\r
- if (m_bBSPPlugin)\r
- {\r
- // status == 1 : didn't get the connection\r
- g_BSPFrontendTable.m_pfnEndListen(1);\r
- }\r
- return;\r
- }\r
-#ifdef _DEBUG\r
- // some debug checks\r
- if (!m_pListenSocket)\r
- {\r
- Sys_Printf("ERROR: m_pListenSocket == NULL in CWatchBSP::RoutineProcessing EBeginStep state\n");\r
- return;\r
- }\r
-#endif\r
- // we are not connected yet, accept any incoming connection\r
- m_pInSocket = Net_Accept(m_pListenSocket);\r
- if (m_pInSocket)\r
- {\r
- Sys_Printf("Connected.\n");\r
- // prepare the message info struct for diving in\r
- memset (&m_message_info, 0, sizeof(message_info_s)); \r
- // a dumb flag to make sure we init the push parser context when first getting a msg\r
- m_bNeedCtxtInit = true;\r
- m_eState = EWatching;\r
- }\r
- break;\r
- case EWatching:\r
-#ifdef _DEBUG\r
- // some debug checks\r
- if (!m_pInSocket)\r
- {\r
- Sys_Printf("ERROR: m_pInSocket == NULL in CWatchBSP::RoutineProcessing EWatching state\n");\r
- return;\r
- }\r
-#endif\r
- // select() will identify if the socket needs an update\r
- // if the socket is identified that means there's either a message or the connection has been closed/reset/terminated\r
- fd_set readfds;\r
- int ret;\r
- FD_ZERO(&readfds);\r
- FD_SET(((unsigned int)m_pInSocket->socket), &readfds);\r
- // from select man page:\r
- // n is the highest-numbered descriptor in any of the three sets, plus 1\r
- // (no use on windows)\r
- ret = select( m_pInSocket->socket + 1, &readfds, NULL, NULL, &tout );\r
- if (ret == SOCKET_ERROR)\r
- {\r
- Sys_Printf("WARNING: SOCKET_ERROR in CWatchBSP::RoutineProcessing\n");\r
- Sys_Printf("Terminating the connection.\n");\r
- Reset();\r
- return;\r
- }\r
-#ifdef _DEBUG\r
- if (ret == -1)\r
- {\r
- // we are non-blocking?? we should never get timeout errors\r
- Sys_Printf("WARNING: unexpected timeout expired in CWatchBSP::Processing\n");\r
- Sys_Printf("Terminating the connection.\n");\r
- Reset();\r
- return;\r
- }\r
-#endif\r
- if (ret == 1)\r
- {\r
- // the socket has been identified, there's something (message or disconnection)\r
- // see if there's anything in input\r
- ret = Net_Receive( m_pInSocket, &msg );\r
- if (ret > 0)\r
- {\r
- // unsigned int size = msg.size; //++timo just a check\r
- strcpy (m_xmlBuf, NMSG_ReadString (&msg));\r
- if (m_bNeedCtxtInit)\r
- {\r
- m_xmlParserCtxt = NULL;\r
- m_xmlParserCtxt = xmlCreatePushParserCtxt (&saxParser, &m_message_info, m_xmlBuf, strlen(m_xmlBuf), NULL);\r
- if (m_xmlParserCtxt == NULL)\r
- {\r
- Sys_FPrintf (SYS_ERR, "Failed to create the XML parser (incoming stream began with: %s)\n", m_xmlBuf);\r
- Reset();\r
- }\r
- m_bNeedCtxtInit = false;\r
- }\r
- else\r
- {\r
- xmlParseChunk (m_xmlParserCtxt, m_xmlBuf, strlen(m_xmlBuf), 0);\r
- }\r
- }\r
- else\r
- {\r
- // error or connection closed/reset\r
- // NOTE: if we get an error down the XML stream we don't reach here\r
- Net_Disconnect( m_pInSocket );\r
- m_pInSocket = NULL;\r
- Sys_Printf("Connection closed.\n");\r
- if (m_bBSPPlugin)\r
- {\r
- Reset();\r
- // let the BSP plugin know that the job is done\r
- g_BSPFrontendTable.m_pfnEndListen(0);\r
- return;\r
- }\r
- // move to next step or finish\r
- m_iCurrentStep++;\r
- if (m_iCurrentStep < m_pCmd->len )\r
- {\r
- DoEBeginStep();\r
- }\r
- else\r
- {\r
- // release the GPtrArray and the strings\r
- if (m_pCmd != NULL)\r
- {\r
- for (m_iCurrentStep=0; m_iCurrentStep < m_pCmd->len; m_iCurrentStep++ )\r
- {\r
- delete[] (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep );\r
- }\r
- g_ptr_array_free( m_pCmd, false );\r
- }\r
- m_pCmd = NULL;\r
- // launch the engine .. OMG\r
- if (g_PrefsDlg.m_bRunQuake)\r
- {\r
- // do we enter sleep mode before?\r
- if (g_PrefsDlg.m_bDoSleep)\r
- {\r
- Sys_Printf("Going into sleep mode..\n");\r
- g_pParentWnd->OnSleep();\r
- }\r
- Sys_Printf("Running engine...\n");\r
- Str cmd;\r
- // build the command line\r
- cmd = g_pGameDescription->mEnginePath.GetBuffer();\r
- // this is game dependant\r
- //!\todo Read the engine binary name from a config file.\r
- if (g_pGameDescription->mGameFile == "wolf.game")\r
- {\r
- if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
- {\r
- // MP\r
-#if defined(WIN32)\r
- cmd += "WolfMP.exe";\r
-#elif defined(__linux__)\r
- cmd += "wolfmp";\r
-#elif defined(__APPLE__)\r
- cmd += "wolfmp.app";\r
-#else\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- else\r
- {\r
- // SP\r
-#if defined(WIN32)\r
- cmd += "WolfSP.exe";\r
-#elif defined(__linux__)\r
- cmd += "wolfsp";\r
-#elif defined(__APPLE__)\r
- cmd += "wolfsp.app";\r
-#else\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- } else if (g_pGameDescription->mGameFile == "et.game")\r
- {\r
-#if defined(WIN32)\r
- cmd += "et.exe";\r
-#elif defined(__linux__)\r
- cmd += "et";\r
-#elif defined(__APPLE__)\r
- cmd += "et.app";\r
-#else\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- // RIANT\r
- // JK2 HACK\r
- else if (g_pGameDescription->mGameFile == "jk2.game")\r
- {\r
- if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
- {\r
- // MP\r
-#if defined(WIN32)\r
- cmd += "jk2MP.exe";\r
-#elif defined(__linux__)\r
- cmd += "jk2mp";\r
-#elif defined(__APPLE__)\r
- cmd += "jk2mp.app";\r
-#else\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- else\r
- {\r
- // SP\r
-#if defined(WIN32)\r
- cmd += "jk2SP.exe";\r
-#elif defined(__linux__)\r
- cmd += "jk2sp";\r
-#elif defined(__APPLE__)\r
- cmd += "jk2sp.app";\r
-#else\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- }\r
- // TTimo\r
- // JA HACK\r
- else if (g_pGameDescription->mGameFile == "ja.game")\r
- {\r
- if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
- {\r
- // MP\r
-#if defined(WIN32)\r
- cmd += "jamp.exe";\r
-#elif !defined(__linux__) && !defined(__APPLE__)\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- else\r
- {\r
- // SP\r
-#if defined(WIN32)\r
- cmd += "jasp.exe";\r
-#elif !defined(__linux__) && !defined(__APPLE__)\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- }\r
- // RIANT\r
- // STVEF HACK\r
- else if (g_pGameDescription->mGameFile == "stvef.game")\r
- {\r
- if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
- {\r
- // MP\r
-#if defined(WIN32)\r
- cmd += "stvoyHM.exe";\r
-#elif defined(__linux__)\r
- cmd += "stvoyHM";\r
-#elif defined(__APPLE__)\r
- cmd += "stvoyHM.app";\r
-#else\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- else\r
- {\r
- // SP\r
-#if defined(WIN32)\r
- cmd += "stvoy.exe";\r
-#elif defined(__linux__)\r
- cmd += "stvoy";\r
-#elif defined(__APPLE__)\r
- cmd += "stvoy.app";\r
-#else\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- }\r
- // RIANT\r
- // SOF2 HACK\r
- else if (g_pGameDescription->mGameFile == "sof2.game")\r
- {\r
- if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
- {\r
- // MP\r
-#if defined(WIN32)\r
- cmd += "sof2MP.exe";\r
-#elif defined(__linux__)\r
- cmd += "b00gus";\r
-#elif defined(__APPLE__)\r
- cmd += "sof2MP.app";\r
-#else\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- else\r
- {\r
- // SP\r
-#if defined(WIN32)\r
- cmd += "sof2.exe";\r
-#elif defined(__linux__)\r
- cmd += "b00gus";\r
-#elif defined(__APPLE__)\r
- cmd += "sof2.app";\r
-#else\r
-#error "WTF are you compiling on"\r
-#endif\r
- }\r
- }\r
- else\r
- {\r
- cmd += g_pGameDescription->mEngine.GetBuffer();\r
- }\r
-#ifdef _WIN32\r
- // NOTE: we are using unix pathnames and CreateProcess doesn't like / in the program path\r
- FindReplace( cmd, "/", "\\" );\r
-#endif\r
- Str cmdline;\r
- if ( (g_pGameDescription->mGameFile == "q2.game") || (g_pGameDescription->mGameFile == "heretic2.game") )\r
- {\r
- cmdline = ". +exec radiant.cfg +map ";\r
- cmdline += m_sBSPName;\r
- }\r
- else\r
- {\r
- cmdline = "+set sv_pure 0 ";\r
- // TTimo: a check for vm_* but that's all fine\r
- //cmdline = "+set sv_pure 0 +set vm_ui 0 +set vm_cgame 0 +set vm_game 0 ";\r
- if (*ValueForKey(g_qeglobals.d_project_entity, "gamename") != '\0')\r
- {\r
- cmdline += "+set fs_game ";\r
- cmdline += ValueForKey(g_qeglobals.d_project_entity, "gamename");\r
- cmdline += " ";\r
- }\r
- //!\todo Read the start-map args from a config file.\r
- if (g_pGameDescription->mGameFile == "wolf.game")\r
- {\r
- if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
- {\r
- // MP\r
- cmdline += "+devmap ";\r
- cmdline += m_sBSPName;\r
- }\r
- else\r
- {\r
- // SP \r
- cmdline += "+set nextmap \"spdevmap ";\r
- cmdline += m_sBSPName;\r
- cmdline += "\"";\r
- }\r
- }\r
- else\r
- {\r
- cmdline += "+devmap ";\r
- cmdline += m_sBSPName;\r
- }\r
- }\r
-\r
- Sys_Printf("%s %s\n", cmd.GetBuffer(), cmdline.GetBuffer());\r
-\r
- // execute now\r
- if (!Q_Exec(cmd.GetBuffer(), (char *)cmdline.GetBuffer(), g_pGameDescription->mEnginePath.GetBuffer(), false))\r
- {\r
- CString msg;\r
- msg = "Failed to execute the following command: ";\r
- msg += cmd; msg += cmdline;\r
- Sys_Printf(msg);\r
- gtk_MessageBox(g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR );\r
- }\r
- }\r
- Reset();\r
- }\r
- }\r
- }\r
- break;\r
- default:\r
- break;\r
- }\r
-}\r
-\r
-void CWatchBSP::DoMonitoringLoop( GPtrArray *pCmd, char *sBSPName )\r
-{\r
- if (m_sBSPName)\r
- {\r
- delete[] m_sBSPName;\r
- }\r
- m_sBSPName = sBSPName;\r
- if (m_eState != EIdle)\r
- {\r
- Sys_Printf("WatchBSP got a monitoring request while not idling...\n");\r
- // prompt the user, should we cancel the current process and go ahead?\r
- if (gtk_MessageBox(g_pParentWnd->m_pWidget, "I am already monitoring a BSP process.\nDo you want me to override and start a new compilation?", \r
- "BSP process monitoring", MB_YESNO ) == IDYES)\r
- {\r
- // disconnect and set EIdle state\r
- Reset();\r
- }\r
- }\r
- m_pCmd = pCmd;\r
- m_iCurrentStep = 0;\r
- DoEBeginStep();\r
-}\r
-\r
-void CWatchBSP::ExternalListen()\r
-{\r
- m_bBSPPlugin = true;\r
- DoEBeginStep ();\r
-}\r
-\r
-// the part of the watchbsp interface we export to plugins\r
-// NOTE: in the long run, the whole watchbsp.cpp interface needs to go out and be handled at the BSP plugin level\r
-// for now we provide something really basic and limited, the essential is to have something that works fine and fast (for 1.1 final)\r
-void WINAPI QERApp_Listen()\r
-{\r
- // open the listening socket\r
- g_pParentWnd->GetWatchBSP()->ExternalListen();\r
-}\r
+/*
+Copyright (c) 2001, Loki software, inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list
+of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+Neither the name of Loki software nor the names of its contributors may be used
+to endorse or promote products derived from this software without specific prior
+written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+//-----------------------------------------------------------------------------
+//
+// DESCRIPTION:
+// monitoring window for running BSP processes (and possibly various other stuff)
+
+#include "stdafx.h"
+#include "watchbsp.h"
+#include "feedback.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+#if defined (__linux__) || defined (__APPLE__)
+#include <sys/time.h>
+#define SOCKET_ERROR -1
+#endif
+
+#ifdef __APPLE__
+#include <unistd.h>
+#endif
+
+#include <assert.h>
+
+// Static functions for the SAX callbacks -------------------------------------------------------
+
+// utility for saxStartElement below
+static void abortStream(message_info_t *data)
+{
+ g_pParentWnd->GetWatchBSP()->Reset();
+ // tell there has been an error
+ if (g_pParentWnd->GetWatchBSP()->HasBSPPlugin ())
+ g_BSPFrontendTable.m_pfnEndListen(2);
+ // yeah this doesn't look good.. but it's needed so that everything will be ignored until the stream goes out
+ data->ignore_depth = -1;
+ data->recurse++;
+}
+
+#include "stream_version.h"
+
+static void saxStartElement(message_info_t *data, const xmlChar *name, const xmlChar **attrs)
+{
+ if (data->ignore_depth == 0)
+ {
+ if (data->bGeometry)
+ // we have a handler
+ {
+ data->pGeometry->saxStartElement (data, name, attrs);
+ }
+ else
+ {
+ if (strcmp ((char *)name, "q3map_feedback") == 0)
+ {
+ // check the correct version
+ // old q3map don't send a version attribute
+ // the ones we support .. send Q3MAP_STREAM_VERSION
+ if (!attrs[0] || !attrs[1] || (strcmp((char*)attrs[0],"version") != 0))
+ {
+ Sys_FPrintf(SYS_ERR, "No stream version given in the feedback stream, this is an old q3map version.\n"
+ "Please turn off monitored compiling if you still wish to use this q3map executable\n");
+ abortStream(data);
+ return;
+ }
+ else if (strcmp((char*)attrs[1],Q3MAP_STREAM_VERSION) != 0)
+ {
+ Sys_FPrintf(SYS_ERR,
+ "This version of Radiant reads version %s debug streams, I got an incoming connection with version %s\n"
+ "Please make sure your versions of Radiant and q3map are matching.\n", Q3MAP_STREAM_VERSION, (char*)attrs[1]);
+ abortStream(data);
+ return;
+ }
+ }
+ // we don't treat locally
+ else if (strcmp ((char *)name, "message") == 0)
+ {
+ data->msg_level = atoi ((char *)attrs[1]);
+ }
+ else if (strcmp ((char *)name, "polyline") == 0)
+ // polyline has a particular status .. right now we only use it for leakfile ..
+ {
+ data->bGeometry = true;
+ data->pGeometry = &g_pointfile;
+ data->pGeometry->saxStartElement( data, name, attrs );
+ }
+ else if (strcmp ((char *)name, "select") == 0)
+ {
+ CSelectMsg *pSelect = new CSelectMsg();
+ data->bGeometry = true;
+ data->pGeometry = pSelect;
+ data->pGeometry->saxStartElement( data, name, attrs );
+ }
+ else if (strcmp ((char *)name, "pointmsg") == 0)
+ {
+ CPointMsg *pPoint = new CPointMsg();
+ data->bGeometry = true;
+ data->pGeometry = pPoint;
+ data->pGeometry->saxStartElement( data, name, attrs );
+ }
+ else if (strcmp ((char *)name, "windingmsg") == 0)
+ {
+ CWindingMsg *pWinding = new CWindingMsg();
+ data->bGeometry = true;
+ data->pGeometry = pWinding;
+ data->pGeometry->saxStartElement( data, name, attrs );
+ }
+ else
+ {
+ Sys_FPrintf (SYS_WRN, "WARNING: ignoring unrecognized node in XML stream (%s)\n", name);
+ // we don't recognize this node, jump over it
+ // (NOTE: the ignore mechanism is a bit screwed, only works when starting an ignore at the highest level)
+ data->ignore_depth = data->recurse;
+ }
+ }
+ }
+ data->recurse++;
+}
+
+static void saxEndElement(message_info_t *data, const xmlChar *name) {
+ data->recurse--;
+ // we are out of an ignored chunk
+ if ( data->recurse == data->ignore_depth ) {
+ data->ignore_depth = 0;
+ return;
+ }
+ if ( data->bGeometry ) {
+ data->pGeometry->saxEndElement( data, name );
+ // we add the object to the debug window
+ if ( !data->bGeometry ) {
+ g_DbgDlg.Push( data->pGeometry );
+ }
+ }
+ if ( data->recurse == data->stop_depth ) {
+#ifdef _DEBUG
+ Sys_Printf ("Received error msg .. shutting down..\n");
+#endif
+ g_pParentWnd->GetWatchBSP()->Reset();
+ // tell there has been an error
+ if ( g_pParentWnd->GetWatchBSP()->HasBSPPlugin() ) {
+ g_BSPFrontendTable.m_pfnEndListen( 2 );
+ }
+ return;
+ }
+}
+
+static void saxCharacters(message_info_t *data, const xmlChar *ch, int len)
+{
+ if (data->bGeometry)
+ {
+ data->pGeometry->saxCharacters (data, ch, len);
+ }
+ else
+ {
+ if (data->ignore_depth != 0)
+ return;
+ // output the message using the level
+ char buf[1024];
+ memcpy( buf, ch, len );
+ buf[len] = '\0';
+ Sys_FPrintf (data->msg_level, "%s", buf);
+ // if this message has error level flag, we mark the depth to stop the compilation when we get out
+ // we don't set the msg level if we don't stop on leak
+ if (data->msg_level == 3)
+ {
+ data->stop_depth = data->recurse-1;
+ }
+ }
+}
+
+static void saxComment(void *ctx, const xmlChar *msg)
+{
+ Sys_Printf("XML comment: %s\n", msg);
+}
+
+static void saxWarning(void *ctx, const char *msg, ...)
+{
+ char saxMsgBuffer[4096];
+ va_list args;
+
+ va_start(args, msg);
+ vsprintf (saxMsgBuffer, msg, args);
+ va_end(args);
+ Sys_FPrintf(SYS_WRN, "XML warning: %s\n", saxMsgBuffer);
+}
+
+static void saxError(void *ctx, const char *msg, ...)
+{
+ char saxMsgBuffer[4096];
+ va_list args;
+
+ va_start(args, msg);
+ vsprintf (saxMsgBuffer, msg, args);
+ va_end(args);
+ Sys_FPrintf(SYS_ERR, "XML error: %s\n", saxMsgBuffer);
+}
+
+static void saxFatal(void *ctx, const char *msg, ...)
+{
+ char buffer[4096];
+
+ va_list args;
+
+ va_start(args, msg);
+ vsprintf (buffer, msg, args);
+ va_end(args);
+ Sys_FPrintf(SYS_ERR, "XML fatal error: %s\n", buffer);
+}
+
+static xmlSAXHandler saxParser = {
+ 0, /* internalSubset */
+ 0, /* isStandalone */
+ 0, /* hasInternalSubset */
+ 0, /* hasExternalSubset */
+ 0, /* resolveEntity */
+ 0, /* getEntity */
+ 0, /* entityDecl */
+ 0, /* notationDecl */
+ 0, /* attributeDecl */
+ 0, /* elementDecl */
+ 0, /* unparsedEntityDecl */
+ 0, /* setDocumentLocator */
+ 0, /* startDocument */
+ 0, /* endDocument */
+ (startElementSAXFunc)saxStartElement, /* startElement */
+ (endElementSAXFunc)saxEndElement, /* endElement */
+ 0, /* reference */
+ (charactersSAXFunc)saxCharacters, /* characters */
+ 0, /* ignorableWhitespace */
+ 0, /* processingInstruction */
+ (commentSAXFunc)saxComment, /* comment */
+ (warningSAXFunc)saxWarning, /* warning */
+ (errorSAXFunc)saxError, /* error */
+ (fatalErrorSAXFunc)saxFatal, /* fatalError */
+};
+
+// ------------------------------------------------------------------------------------------------
+
+CWatchBSP::~CWatchBSP()
+{
+ Reset();
+ if (m_sBSPName)
+ {
+ delete[] m_sBSPName;
+ m_sBSPName = NULL;
+ }
+ Net_Shutdown();
+}
+
+void CWatchBSP::Reset()
+{
+ if (m_pInSocket)
+ {
+ Net_Disconnect(m_pInSocket);
+ m_pInSocket = NULL;
+ }
+ if (m_pListenSocket)
+ {
+ Net_Disconnect(m_pListenSocket);
+ m_pListenSocket = NULL;
+ }
+ if (m_xmlInputBuffer)
+ {
+ xmlFreeParserInputBuffer (m_xmlInputBuffer);
+ m_xmlInputBuffer = NULL;
+ }
+ m_eState = EIdle;
+}
+
+bool CWatchBSP::SetupListening()
+{
+#ifdef _DEBUG
+ if (m_pListenSocket)
+ {
+ Sys_Printf("ERROR: m_pListenSocket != NULL in CWatchBSP::SetupListening\n");
+ return false;
+ }
+#endif
+ Sys_Printf("Setting up\n");
+ if( !Net_Setup() )
+ return false;
+
+ m_pListenSocket = Net_ListenSocket(39000);
+ if (m_pListenSocket == NULL)
+ return false;
+
+ Sys_Printf("Listening...\n");
+ return true;
+}
+
+void CWatchBSP::DoEBeginStep() {
+ Reset();
+ if ( !SetupListening() ) {
+ CString msg;
+ msg = "Failed to get a listening socket on port 39000.\nTry running with BSP monitoring disabled if you can't fix this.\n";
+ Sys_Printf( msg );
+ gtk_MessageBox( g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR );
+ return;
+ }
+ // set the timer for timeouts and step cancellation
+ g_timer_reset( m_pTimer );
+ g_timer_start( m_pTimer );
+
+ if ( !m_bBSPPlugin ) {
+ Sys_Printf( "=== running BSP command ===\n%s\n", g_ptr_array_index( m_pCmd, m_iCurrentStep ) );
+
+ if ( !Q_Exec( NULL, (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep ), NULL, true ) ) {
+ CString msg;
+ msg = "Failed to execute the following command: ";
+ msg += (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep );
+ msg += "\nCheck that the file exists and that you don't run out of system resources.\n";
+ Sys_Printf( msg );
+ gtk_MessageBox( g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR );
+ return;
+ }
+ // re-initialise the debug window
+ if ( m_iCurrentStep == 0 ) {
+ g_DbgDlg.Init();
+ }
+ }
+ m_eState = EBeginStep;
+}
+
+void CWatchBSP::RoutineProcessing()
+{
+ // used for select()
+#ifdef _WIN32
+ TIMEVAL tout = { 0, 0 };
+#endif
+#if defined (__linux__) || defined (__APPLE__)
+ timeval tout;
+ tout.tv_sec = 0;
+ tout.tv_usec = 0;
+#endif
+
+ switch (m_eState)
+ {
+ case EBeginStep:
+ // timeout: if we don't get an incoming connection fast enough, go back to idle
+ if ( g_timer_elapsed( m_pTimer, NULL ) > g_PrefsDlg.m_iTimeout )
+ {
+ gtk_MessageBox(g_pParentWnd->m_pWidget, "The connection timed out, assuming the BSP process failed\nMake sure you are using a networked version of Q3Map?\nOtherwise you need to disable BSP Monitoring in prefs.", "BSP process monitoring", MB_OK );
+ Reset();
+ if (m_bBSPPlugin)
+ {
+ // status == 1 : didn't get the connection
+ g_BSPFrontendTable.m_pfnEndListen(1);
+ }
+ return;
+ }
+#ifdef _DEBUG
+ // some debug checks
+ if (!m_pListenSocket)
+ {
+ Sys_Printf("ERROR: m_pListenSocket == NULL in CWatchBSP::RoutineProcessing EBeginStep state\n");
+ return;
+ }
+#endif
+ // we are not connected yet, accept any incoming connection
+ m_pInSocket = Net_Accept(m_pListenSocket);
+ if (m_pInSocket)
+ {
+ Sys_Printf("Connected.\n");
+ // prepare the message info struct for diving in
+ memset (&m_message_info, 0, sizeof(message_info_s));
+ // a dumb flag to make sure we init the push parser context when first getting a msg
+ m_bNeedCtxtInit = true;
+ m_eState = EWatching;
+ }
+ break;
+ case EWatching:
+#ifdef _DEBUG
+ // some debug checks
+ if (!m_pInSocket)
+ {
+ Sys_Printf("ERROR: m_pInSocket == NULL in CWatchBSP::RoutineProcessing EWatching state\n");
+ return;
+ }
+#endif
+ // select() will identify if the socket needs an update
+ // if the socket is identified that means there's either a message or the connection has been closed/reset/terminated
+ fd_set readfds;
+ int ret;
+ FD_ZERO(&readfds);
+ FD_SET(((unsigned int)m_pInSocket->socket), &readfds);
+ // from select man page:
+ // n is the highest-numbered descriptor in any of the three sets, plus 1
+ // (no use on windows)
+ ret = select( m_pInSocket->socket + 1, &readfds, NULL, NULL, &tout );
+ if (ret == SOCKET_ERROR)
+ {
+ Sys_Printf("WARNING: SOCKET_ERROR in CWatchBSP::RoutineProcessing\n");
+ Sys_Printf("Terminating the connection.\n");
+ Reset();
+ return;
+ }
+#ifdef _DEBUG
+ if (ret == -1)
+ {
+ // we are non-blocking?? we should never get timeout errors
+ Sys_Printf("WARNING: unexpected timeout expired in CWatchBSP::Processing\n");
+ Sys_Printf("Terminating the connection.\n");
+ Reset();
+ return;
+ }
+#endif
+ if (ret == 1)
+ {
+ // the socket has been identified, there's something (message or disconnection)
+ // see if there's anything in input
+ ret = Net_Receive( m_pInSocket, &msg );
+ if (ret > 0)
+ {
+ // unsigned int size = msg.size; //++timo just a check
+ strcpy (m_xmlBuf, NMSG_ReadString (&msg));
+ if (m_bNeedCtxtInit)
+ {
+ m_xmlParserCtxt = NULL;
+ m_xmlParserCtxt = xmlCreatePushParserCtxt (&saxParser, &m_message_info, m_xmlBuf, strlen(m_xmlBuf), NULL);
+ if (m_xmlParserCtxt == NULL)
+ {
+ Sys_FPrintf (SYS_ERR, "Failed to create the XML parser (incoming stream began with: %s)\n", m_xmlBuf);
+ Reset();
+ }
+ m_bNeedCtxtInit = false;
+ }
+ else
+ {
+ xmlParseChunk (m_xmlParserCtxt, m_xmlBuf, strlen(m_xmlBuf), 0);
+ }
+ }
+ else
+ {
+ // error or connection closed/reset
+ // NOTE: if we get an error down the XML stream we don't reach here
+ Net_Disconnect( m_pInSocket );
+ m_pInSocket = NULL;
+ Sys_Printf("Connection closed.\n");
+ if (m_bBSPPlugin)
+ {
+ Reset();
+ // let the BSP plugin know that the job is done
+ g_BSPFrontendTable.m_pfnEndListen(0);
+ return;
+ }
+ // move to next step or finish
+ m_iCurrentStep++;
+ if (m_iCurrentStep < m_pCmd->len )
+ {
+ DoEBeginStep();
+ }
+ else
+ {
+ // release the GPtrArray and the strings
+ if (m_pCmd != NULL)
+ {
+ for (m_iCurrentStep=0; m_iCurrentStep < m_pCmd->len; m_iCurrentStep++ )
+ {
+ delete[] (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep );
+ }
+ g_ptr_array_free( m_pCmd, false );
+ }
+ m_pCmd = NULL;
+ // launch the engine .. OMG
+ if (g_PrefsDlg.m_bRunQuake)
+ {
+ // do we enter sleep mode before?
+ if (g_PrefsDlg.m_bDoSleep)
+ {
+ Sys_Printf("Going into sleep mode..\n");
+ g_pParentWnd->OnSleep();
+ }
+ Sys_Printf("Running engine...\n");
+ Str cmd;
+ // build the command line
+ cmd = g_pGameDescription->mEnginePath.GetBuffer();
+ // this is game dependant
+ //!\todo Read the engine binary name from a config file.
+ if (g_pGameDescription->mGameFile == "wolf.game")
+ {
+ if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))
+ {
+ // MP
+#if defined(WIN32)
+ cmd += "WolfMP.exe";
+#elif defined(__linux__)
+ cmd += "wolfmp";
+#elif defined(__APPLE__)
+ cmd += "wolfmp.app";
+#else
+#error "WTF are you compiling on"
+#endif
+ }
+ else
+ {
+ // SP
+#if defined(WIN32)
+ cmd += "WolfSP.exe";
+#elif defined(__linux__)
+ cmd += "wolfsp";
+#elif defined(__APPLE__)
+ cmd += "wolfsp.app";
+#else
+#error "WTF are you compiling on"
+#endif
+ }
+ } else if (g_pGameDescription->mGameFile == "et.game")
+ {
+#if defined(WIN32)
+ cmd += "et.exe";
+#elif defined(__linux__)
+ cmd += "et";
+#elif defined(__APPLE__)
+ cmd += "et.app";
+#else
+#error "WTF are you compiling on"
+#endif
+ }
+ // RIANT
+ // JK2 HACK
+ else if (g_pGameDescription->mGameFile == "jk2.game")
+ {
+ if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))
+ {
+ // MP
+#if defined(WIN32)
+ cmd += "jk2MP.exe";
+#elif defined(__linux__)
+ cmd += "jk2mp";
+#elif defined(__APPLE__)
+ cmd += "jk2mp.app";
+#else
+#error "WTF are you compiling on"
+#endif
+ }
+ else
+ {
+ // SP
+#if defined(WIN32)
+ cmd += "jk2SP.exe";
+#elif defined(__linux__)
+ cmd += "jk2sp";
+#elif defined(__APPLE__)
+ cmd += "jk2sp.app";
+#else
+#error "WTF are you compiling on"
+#endif
+ }
+ }
+ // TTimo
+ // JA HACK
+ else if (g_pGameDescription->mGameFile == "ja.game")
+ {
+ if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))
+ {
+ // MP
+#if defined(WIN32)
+ cmd += "jamp.exe";
+#elif !defined(__linux__) && !defined(__APPLE__)
+#error "WTF are you compiling on"
+#endif
+ }
+ else
+ {
+ // SP
+#if defined(WIN32)
+ cmd += "jasp.exe";
+#elif !defined(__linux__) && !defined(__APPLE__)
+#error "WTF are you compiling on"
+#endif
+ }
+ }
+ // RIANT
+ // STVEF HACK
+ else if (g_pGameDescription->mGameFile == "stvef.game")
+ {
+ if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))
+ {
+ // MP
+#if defined(WIN32)
+ cmd += "stvoyHM.exe";
+#elif defined(__linux__)
+ cmd += "stvoyHM";
+#elif defined(__APPLE__)
+ cmd += "stvoyHM.app";
+#else
+#error "WTF are you compiling on"
+#endif
+ }
+ else
+ {
+ // SP
+#if defined(WIN32)
+ cmd += "stvoy.exe";
+#elif defined(__linux__)
+ cmd += "stvoy";
+#elif defined(__APPLE__)
+ cmd += "stvoy.app";
+#else
+#error "WTF are you compiling on"
+#endif
+ }
+ }
+ // RIANT
+ // SOF2 HACK
+ else if (g_pGameDescription->mGameFile == "sof2.game")
+ {
+ if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))
+ {
+ // MP
+#if defined(WIN32)
+ cmd += "sof2MP.exe";
+#elif defined(__linux__)
+ cmd += "b00gus";
+#elif defined(__APPLE__)
+ cmd += "sof2MP.app";
+#else
+#error "WTF are you compiling on"
+#endif
+ }
+ else
+ {
+ // SP
+#if defined(WIN32)
+ cmd += "sof2.exe";
+#elif defined(__linux__)
+ cmd += "b00gus";
+#elif defined(__APPLE__)
+ cmd += "sof2.app";
+#else
+#error "WTF are you compiling on"
+#endif
+ }
+ }
+ else
+ {
+ cmd += g_pGameDescription->mEngine.GetBuffer();
+ }
+#ifdef _WIN32
+ // NOTE: we are using unix pathnames and CreateProcess doesn't like / in the program path
+ FindReplace( cmd, "/", "\\" );
+#endif
+ Str cmdline;
+ if ( (g_pGameDescription->mGameFile == "q2.game") || (g_pGameDescription->mGameFile == "heretic2.game") )
+ {
+ cmdline = ". +exec radiant.cfg +map ";
+ cmdline += m_sBSPName;
+ }
+ else
+ {
+ cmdline = "+set sv_pure 0 ";
+ // TTimo: a check for vm_* but that's all fine
+ //cmdline = "+set sv_pure 0 +set vm_ui 0 +set vm_cgame 0 +set vm_game 0 ";
+ if (*ValueForKey(g_qeglobals.d_project_entity, "gamename") != '\0')
+ {
+ cmdline += "+set fs_game ";
+ cmdline += ValueForKey(g_qeglobals.d_project_entity, "gamename");
+ cmdline += " ";
+ }
+ //!\todo Read the start-map args from a config file.
+ if (g_pGameDescription->mGameFile == "wolf.game")
+ {
+ if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))
+ {
+ // MP
+ cmdline += "+devmap ";
+ cmdline += m_sBSPName;
+ }
+ else
+ {
+ // SP
+ cmdline += "+set nextmap \"spdevmap ";
+ cmdline += m_sBSPName;
+ cmdline += "\"";
+ }
+ }
+ else
+ {
+ cmdline += "+devmap ";
+ cmdline += m_sBSPName;
+ }
+ }
+
+ Sys_Printf("%s %s\n", cmd.GetBuffer(), cmdline.GetBuffer());
+
+ // execute now
+ if (!Q_Exec(cmd.GetBuffer(), (char *)cmdline.GetBuffer(), g_pGameDescription->mEnginePath.GetBuffer(), false))
+ {
+ CString msg;
+ msg = "Failed to execute the following command: ";
+ msg += cmd; msg += cmdline;
+ Sys_Printf(msg);
+ gtk_MessageBox(g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR );
+ }
+ }
+ Reset();
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void CWatchBSP::DoMonitoringLoop( GPtrArray *pCmd, char *sBSPName )
+{
+ if (m_sBSPName)
+ {
+ delete[] m_sBSPName;
+ }
+ m_sBSPName = sBSPName;
+ if (m_eState != EIdle)
+ {
+ Sys_Printf("WatchBSP got a monitoring request while not idling...\n");
+ // prompt the user, should we cancel the current process and go ahead?
+ if (gtk_MessageBox(g_pParentWnd->m_pWidget, "I am already monitoring a BSP process.\nDo you want me to override and start a new compilation?",
+ "BSP process monitoring", MB_YESNO ) == IDYES)
+ {
+ // disconnect and set EIdle state
+ Reset();
+ }
+ }
+ m_pCmd = pCmd;
+ m_iCurrentStep = 0;
+ DoEBeginStep();
+}
+
+void CWatchBSP::ExternalListen()
+{
+ m_bBSPPlugin = true;
+ DoEBeginStep ();
+}
+
+// the part of the watchbsp interface we export to plugins
+// NOTE: in the long run, the whole watchbsp.cpp interface needs to go out and be handled at the BSP plugin level
+// for now we provide something really basic and limited, the essential is to have something that works fine and fast (for 1.1 final)
+void WINAPI QERApp_Listen()
+{
+ // open the listening socket
+ g_pParentWnd->GetWatchBSP()->ExternalListen();
+}