X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fnetradiant.git;a=blobdiff_plain;f=radiant%2Fqe3.cpp;h=a658456407672f7222613fc6c4345e3d8cd08793;hp=e3f9cec65142cf8413afaec9a53f78377666c8a2;hb=1099f571fe0e8512e4fb36cb84aa3bfc49282bb9;hpb=80378101101ca1762bbf5638a9e3566893096d8a diff --git a/radiant/qe3.cpp b/radiant/qe3.cpp index e3f9cec6..a6584564 100644 --- a/radiant/qe3.cpp +++ b/radiant/qe3.cpp @@ -1,1797 +1,1792 @@ -/* -Copyright (C) 1999-2007 id Software, Inc. and contributors. -For a list of contributors, see the accompanying CONTRIBUTORS file. - -This file is part of GtkRadiant. - -GtkRadiant 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. - -GtkRadiant 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 GtkRadiant; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -// -// Linux stuff -// -// Leonardo Zide (leo@lokigames.com) -// - -#include "stdafx.h" -#include -#include -#include "gtkmisc.h" -#if defined (__linux__) || defined (__APPLE__) -#include -#include -#include -#include -#endif -// for the logging part -#include -#include - -QEGlobals_t g_qeglobals; -QEGlobals_GUI_t g_qeglobals_gui; - -// leo: Track memory allocations for debugging -// NOTE TTimo this was never used and probably not relevant -// there are tools to do that -#ifdef MEM_DEBUG - -static GList *memblocks; - -void* debug_malloc (size_t size, const char* file, int line) -{ - void *buf = g_malloc (size + 8); - - *((const char**)buf) = file; - buf = (char*)buf + 4; - *((int*)buf) = line; - buf = (char*)buf + 4; - - memblocks = g_list_append (memblocks, buf); - - return buf; -} - -void debug_free (void *buf, const char* file, int line) -{ - const char *f; - int l; - - if (g_list_find (memblocks, buf)) - { - memblocks = g_list_remove (memblocks, buf); - - buf = (char*)buf - 4; - l = *((int*)buf); - buf = (char*)buf - 4; - f = *((const char**)buf); - - Sys_FPrintf (SYS_DBG, "free: %s %d", file, line); - Sys_FPrintf (SYS_DBG, " allocated: %s %d\n", f, l); - - g_free (buf); - } -// else -// free (buf); // from qmalloc, will leak unless we add this same hack to cmdlib -} - -#endif - -vec_t Rad_rint (vec_t in) -{ - if (g_PrefsDlg.m_bNoClamp) - return in; - else - return (float)floor (in + 0.5); -} - -void WINAPI QE_CheckOpenGLForErrors(void) -{ - char strMsg[1024]; - int i = qglGetError(); - if (i != GL_NO_ERROR) - { - if (i == GL_OUT_OF_MEMORY) - { - sprintf(strMsg, "OpenGL out of memory error %s\nDo you wish to save before exiting?", qgluErrorString((GLenum)i)); - if (gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg, "Radiant Error", MB_YESNO) == IDYES) - { - Map_SaveFile(NULL, false); - } - _exit(1); - } - else - { - Sys_Printf ("Warning: OpenGL Error %s\n", qgluErrorString((GLenum)i)); - } - } -} - -// NOTE: don't this function, use VFS instead -char *ExpandReletivePath (char *p) -{ - static char temp[1024]; - const char *base; - - if (!p || !p[0]) - return NULL; - if (p[0] == '/' || p[0] == '\\') - return p; - - base = ValueForKey(g_qeglobals.d_project_entity, "basepath"); - sprintf (temp, "%s/%s", base, p); - return temp; -} - -char *copystring (char *s) -{ - char *b; - b = (char*)malloc(strlen(s)+1); - strcpy (b,s); - return b; -} - - -bool DoesFileExist(const char* pBuff, long& lSize) -{ - FileStream file; - if (file.Open(pBuff, "r")) - { - lSize += file.GetLength(); - file.Close(); - return true; - } - return false; -} - - -void Map_Snapshot() -{ - CString strMsg; - - // I hope the modified flag is kept correctly up to date - if (!modified) - return; - - // we need to do the following - // 1. make sure the snapshot directory exists (create it if it doesn't) - // 2. find out what the lastest save is based on number - // 3. inc that and save the map - CString strOrgPath, strOrgFile; - ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile); - AddSlash(strOrgPath); - strOrgPath += "snapshots"; - bool bGo = true; - struct stat Stat; - if (stat(strOrgPath, &Stat) == -1) - { -#ifdef _WIN32 - bGo = (_mkdir(strOrgPath) != -1); -#endif - -#if defined (__linux__) || defined (__APPLE__) - bGo = (mkdir(strOrgPath,0755) != -1); -#endif - } - AddSlash(strOrgPath); - if (bGo) - { - int nCount = 0; - long lSize = 0; - CString strNewPath; - strNewPath = strOrgPath; - strNewPath += strOrgFile; - CString strFile; - while (bGo) - { - char buf[PATH_MAX]; - sprintf( buf, "%s.%i", strNewPath.GetBuffer(), nCount ); - strFile = buf; - bGo = DoesFileExist(strFile, lSize); - nCount++; - } - // strFile has the next available slot - Map_SaveFile(strFile, false); - // it is still a modified map (we enter this only if this is a modified map) - Sys_SetTitle (currentmap); - Sys_MarkMapModified(); - if (lSize > 12 * 1024 * 1024) // total size of saves > 4 mb - { - Sys_Printf("The snapshot files in %s total more than 4 megabytes. You might consider cleaning up.", strOrgPath.GetBuffer()); - } - } - else - { - strMsg.Format("Snapshot save failed.. unabled to create directory\n%s", strOrgPath.GetBuffer()); - gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg); - } - strOrgPath = ""; - strOrgFile = ""; -} -/* -=============== -QE_CheckAutoSave - -If five minutes have passed since making a change -and the map hasn't been saved, save it out. -=============== -*/ - - -void QE_CheckAutoSave( void ) -{ - static time_t s_start; - time_t now; - time (&now); - - if (modified != 1 || !s_start) - { - s_start = now; - return; - } - - if ((now - s_start) > (60 * g_PrefsDlg.m_nAutoSave)) - { - if (g_PrefsDlg.m_bAutoSave) - { - CString strMsg; - strMsg = g_PrefsDlg.m_bSnapShots ? "Autosaving snapshot..." : "Autosaving..."; - Sys_Printf(strMsg); - Sys_Printf("\n"); - Sys_Status (strMsg,0); - - // only snapshot if not working on a default map - if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0) - { - Map_Snapshot(); - } - else - { - Map_SaveFile (ValueForKey(g_qeglobals.d_project_entity, "autosave"), false); - } - - Sys_Status ("Autosaving...Saved.", 0 ); - modified = 2; - } - else - { - Sys_Printf ("Autosave skipped...\n"); - Sys_Status ("Autosave skipped...", 0 ); - } - s_start = now; - } -} - - -// NOTE TTimo we don't like that BuildShortPathName too much -// the VFS provides a vfsCleanFileName which should perform the cleanup tasks -// in the long run I'd like to completely get rid of this - -// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=144 -// used to be disabled, but caused problems - -// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=291 -// can't work with long win32 names until the BSP commands are not working differently -#ifdef _WIN32 -int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen) -{ - char *pFile = NULL; - int nResult = GetFullPathName(pPath, nBufferLen, pBuffer, &pFile); - nResult = GetShortPathName(pPath, pBuffer, nBufferLen); - if (nResult == 0) - strcpy(pBuffer, pPath); // Use long filename - return nResult; -} -#endif - -#if defined (__linux__) || defined (__APPLE__) -int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen) -{ - // remove /../ from directories - const char *scr = pPath; char *dst = pBuffer; - for (int i = 0; (i < nBufferLen) && (*scr != 0); i++) - { - if (*scr == '/' && *(scr+1) == '.' && *(scr+2) == '.') - { - scr += 3; - while (dst != pBuffer && *(--dst) != '/') - { - i--; - } - } - - *dst = *scr; - - scr++; dst++; - } - *dst = 0; - - return strlen (pBuffer); -} -#endif - -/* -const char *g_pPathFixups[]= -{ - "basepath", - "autosave", -}; - -const int g_nPathFixupCount = sizeof(g_pPathFixups) / sizeof(const char*); - -void QE_CheckProjectEntity() -{ - char *pFile; - char pBuff[PATH_MAX]; - char pNewPath[PATH_MAX]; - for (int i = 0; i < g_nPathFixupCount; i++) - { - char *pPath = ValueForKey (g_qeglobals.d_project_entity, g_pPathFixups[i]); - - strcpy (pNewPath, pPath); - if (pPath[0] != '\\' && pPath[0] != '/') - if (GetFullPathName(pPath, PATH_MAX, pBuff, &pFile)) - strcpy (pNewPath, pBuff); - - BuildShortPathName (pNewPath, pBuff, PATH_MAX); - - // check it's not ending with a filename seperator - if (pBuff[strlen(pBuff)-1] == '/' || pBuff[strlen(pBuff)-1] == '\\') - { - Sys_FPrintf(SYS_WRN, "WARNING: \"%s\" path in the project file has an ending file seperator, fixing.\n", g_pPathFixups[i]); - pBuff[strlen(pBuff)-1]=0; - } - - SetKeyValue(g_qeglobals.d_project_entity, g_pPathFixups[i], pBuff); - } -} -*/ - -void HandleXMLError( void* ctxt, const char* text, ... ) -{ - va_list argptr; - static char buf[32768]; - - va_start (argptr,text); - vsprintf (buf, text, argptr); - Sys_FPrintf (SYS_ERR, "XML %s\n", buf); - va_end (argptr); -} - -#define DTD_BUFFER_LENGTH 1024 -xmlDocPtr ParseXMLStream(IDataStream *stream, bool validate = false) -{ - xmlDocPtr doc = NULL; - bool wellFormed = false, valid = false; - int res, size = 1024; - char chars[1024]; - xmlParserCtxtPtr ctxt; - - // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=433 - //if(validate) - // xmlDoValidityCheckingDefaultValue = 1; - //else - xmlDoValidityCheckingDefaultValue = 0; - xmlSetGenericErrorFunc(NULL, HandleXMLError); - - // SPoG - // HACK: use AppPath to resolve DTD location - // do a buffer-safe string copy and concatenate - int i; - char* w; - const char* r; - char buf[DTD_BUFFER_LENGTH]; - - w = buf; - i = 0; - // copy - //assert(g_strAppPath.GetBuffer() != NULL); - for(r = g_strAppPath.GetBuffer(); iRead(chars, 4); - if (res > 0) - { - ctxt = xmlCreatePushParserCtxt(NULL, NULL, chars, res, buf); - - while ((res = stream->Read(chars, size)) > 0) - { - xmlParseChunk(ctxt, chars, res, 0); - } - xmlParseChunk(ctxt, chars, 0, 1); - doc = ctxt->myDoc; - - wellFormed = (ctxt->wellFormed == 1); - valid = (ctxt->valid == 1); - - xmlFreeParserCtxt(ctxt); - } - - if(wellFormed && (!validate || (validate && valid))) - return doc; - - if(doc != NULL) - xmlFreeDoc(doc); - - return NULL; -} - -xmlDocPtr ParseXMLFile(const char* filename, bool validate = false) -{ - FileStream stream; - if (stream.Open(filename, "r")) - return ParseXMLStream(&stream, validate); - - Sys_FPrintf(SYS_ERR, "Failed to open file: %s\n",filename); - return NULL; -} - -// copy a string r to a buffer w -// replace $string as appropriate -void ReplaceTemplates(char* w, const char* r) -{ - const char *p; - const char *__ENGINEPATH = "TEMPLATEenginepath"; - const char *__USERHOMEPATH = "TEMPLATEuserhomepath"; - const char *__TOOLSPATH = "TEMPLATEtoolspath"; - const char *__BASEDIR = "TEMPLATEbasedir"; - const char *__APPPATH = "TEMPLATEapppath"; - - // iterate through string r - while(*r!='\0') - { - // check for special character - if(*r=='$') - { - if(strncmp(r+1, __ENGINEPATH, strlen(__ENGINEPATH)) == 0) - { - r+=strlen(__ENGINEPATH)+1; - p = g_pGameDescription->mEnginePath.GetBuffer(); - } - else if(strncmp(r+1, __USERHOMEPATH, strlen(__USERHOMEPATH)) == 0) - { - r+=strlen(__USERHOMEPATH)+1; - p = g_qeglobals.m_strHomeGame.GetBuffer(); - } - else if(strncmp(r+1, __BASEDIR, strlen(__BASEDIR)) == 0) - { - r+=strlen(__BASEDIR)+1; - p = g_pGameDescription->mBaseGame; - } - else if(strncmp(r+1, __TOOLSPATH, strlen(__TOOLSPATH)) == 0) - { - r+=strlen(__TOOLSPATH)+1; - p = g_strGameToolsPath.GetBuffer(); - } - else if(strncmp(r+1, __APPPATH, strlen(__APPPATH)) == 0) - { - r+=strlen(__APPPATH)+1; - p = g_strAppPath.GetBuffer(); - } - else - { - r++; - p = "$"; - } - - while(*p!='\0') *w++ = *p++; - } - else *w++ = *r++; - } - *w = '\0'; -} - -/* -=========== -QE_LoadProject -TODO TODO TODO (don't think this got fully merged in) -TTimo: added project file "version", version 2 adds '#' chars to the BSP command strings -version 3 was .. I don't remember .. version 4 adds q3map2 commands -TTimo: when QE_LoadProject is called, the prefs are updated with path to the latest project and saved on disk -=========== -*/ -/*\todo decide on a sensible location/name for project files.*/ -bool QE_LoadProject (const char *projectfile) -{ - char buf[1024]; - xmlDocPtr doc; - xmlNodePtr node, project; - - Sys_Printf("Loading project file: \"%s\"\n", projectfile); - doc = ParseXMLFile(projectfile, true); - - if(doc == NULL) return false; - - node=doc->children; - while(node != NULL && node->type != XML_DTD_NODE) node=node->next; - if(node == NULL || strcmp((char*)node->name, "project") != 0) - { - Sys_FPrintf(SYS_ERR, "ERROR: invalid file type\n"); - return false; - } - - while(node->type != XML_ELEMENT_NODE) node=node->next; - // - project = node; - - if(g_qeglobals.d_project_entity != NULL) Entity_Free(g_qeglobals.d_project_entity); - g_qeglobals.d_project_entity = Entity_Alloc(); - - for(node = project->children; node != NULL; node=node->next) - { - if(node->type != XML_ELEMENT_NODE) continue; - - // - ReplaceTemplates(buf, (char*)node->properties->next->children->content); - - SetKeyValue(g_qeglobals.d_project_entity, (char*)node->properties->children->content, buf); - } - - xmlFreeDoc(doc); - - // project file version checking - // add a version checking to avoid people loading later versions of the project file and bitching - int ver = IntForKey( g_qeglobals.d_project_entity, "version" ); - if (ver > PROJECT_VERSION) - { - char strMsg[1024]; - sprintf (strMsg, "This is a version %d project file. This build only supports <=%d project files.\n" - "Please choose another project file or upgrade your version of Radiant.", ver, PROJECT_VERSION); - gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK); - // set the project file to nothing so we are sure we'll ask next time? - g_PrefsDlg.m_strLastProject = ""; - g_PrefsDlg.SavePrefs(); - return false; - } - - // set here some default project settings you need - if ( strlen( ValueForKey( g_qeglobals.d_project_entity, "brush_primit" ) ) == 0 ) - { - SetKeyValue( g_qeglobals.d_project_entity, "brush_primit", "0" ); - } - - g_qeglobals.m_bBrushPrimitMode = IntForKey( g_qeglobals.d_project_entity, "brush_primit" ); - - g_qeglobals.m_strHomeMaps = g_qeglobals.m_strHomeGame; - const char* str = ValueForKey(g_qeglobals.d_project_entity, "gamename"); - if(str[0] == '\0') str = g_pGameDescription->mBaseGame.GetBuffer(); - g_qeglobals.m_strHomeMaps += str; - g_qeglobals.m_strHomeMaps += '/'; - - // don't forget to create the dirs - Q_mkdir(g_qeglobals.m_strHomeGame.GetBuffer(), 0775); - Q_mkdir(g_qeglobals.m_strHomeMaps.GetBuffer(), 0775); - - // usefull for the log file and debuggin fucked up configurations from users: - // output the basic information of the .qe4 project file - // SPoG - // all these paths should be unix format, with a trailing slash at the end - // if not.. to debug, check that the project file paths are set up correctly - Sys_Printf("basepath : %s\n", ValueForKey( g_qeglobals.d_project_entity, "basepath") ); - Sys_Printf("entitypath : %s\n", ValueForKey( g_qeglobals.d_project_entity, "entitypath" ) ); - - - // check whether user_project key exists.. - // if not, save the current project under a new name - if (ValueForKey(g_qeglobals.d_project_entity, "user_project")[0] == '\0') - { - Sys_Printf("Loaded a template project file\n"); - - // create the user_project key - SetKeyValue( g_qeglobals.d_project_entity, "user_project", "1" ); - - // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672 - if (IntForKey( g_qeglobals.d_project_entity, "version" ) != PROJECT_VERSION) - { - char strMsg[2048]; - sprintf(strMsg, - "The template project '%s' has version %d. The editor binary is configured for version %d.\n" - "This indicates a problem in your setup. See http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672\n" - "I will keep going with this project till you fix this", - projectfile, IntForKey( g_qeglobals.d_project_entity, "version" ), PROJECT_VERSION); - gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK); - } - - // create the writable project file path - strcpy(buf, g_qeglobals.m_strHomeGame.GetBuffer()); - strcat(buf, g_pGameDescription->mBaseGame.GetBuffer()); - strcat(buf, "/scripts/"); - // while the filename is already in use, increment the number we add to the end - int counter = 0; - char pUser[PATH_MAX]; - while (1) - { - sprintf( pUser, "%suser%d." PROJECT_FILETYPE, buf, counter ); - counter++; - if (access( pUser, R_OK) != 0) - { - // this is the one - strcpy( buf, pUser ); - break; - } - } - // saving project will cause a save prefs - g_PrefsDlg.m_strLastProject = buf; - g_PrefsDlg.m_nLastProjectVer = IntForKey( g_qeglobals.d_project_entity, "version" ); - QE_SaveProject(buf); - } - else - { - // update preferences::LastProject with path of this successfully-loaded project - // save preferences - Sys_Printf("Setting current project in prefs to \"%s\"\n", g_PrefsDlg.m_strLastProject.GetBuffer() ); - g_PrefsDlg.m_strLastProject = projectfile; - g_PrefsDlg.SavePrefs(); - } - - return true; -} - -/* -=========== -QE_SaveProject -TTimo: whenever QE_SaveProject is called, prefs are updated and saved with the path to the project -=========== -*/ -qboolean QE_SaveProject (const char* filename) -{ - Sys_Printf("Save project file '%s'\n", filename); - - xmlNodePtr node; - xmlDocPtr doc = xmlNewDoc((xmlChar *)"1.0"); - // create DTD node - xmlCreateIntSubset(doc, (xmlChar *)"project", NULL, (xmlChar *)"project.dtd"); - // create project node - doc->children->next = xmlNewDocNode(doc, NULL, (xmlChar *)"project", NULL); - - for(epair_t* epair = g_qeglobals.d_project_entity->epairs; epair != NULL; epair = epair->next) - { - node = xmlNewChild(doc->children->next, NULL, (xmlChar *)"key", NULL); - xmlSetProp(node, (xmlChar*)"name", (xmlChar*)epair->key); - xmlSetProp(node, (xmlChar*)"value", (xmlChar*)epair->value); - } - - CreateDirectoryPath(filename); - if (xmlSaveFormatFile(filename, doc, 1) != -1) - { - xmlFreeDoc(doc); - Sys_Printf("Setting current project in prefs to \"%s\"\n", filename ); - g_PrefsDlg.m_strLastProject = filename; - g_PrefsDlg.SavePrefs(); - return TRUE; - } - else - { - xmlFreeDoc(doc); - Sys_FPrintf(SYS_ERR, "failed to save project file: \"%s\"\n", filename); - return FALSE; - } -} - - - -/* -=========== -QE_KeyDown -=========== -*/ -#define SPEED_MOVE 32 -#define SPEED_TURN 22.5 - - -/* -=============== -ConnectEntities - -Sets target / targetname on the two entities selected -from the first selected to the secon -=============== -*/ -void ConnectEntities (void) -{ - entity_t *e1, *e2; - const char *target; - char *newtarg = NULL; - - if (g_qeglobals.d_select_count != 2) - { - Sys_Status ("Must have two brushes selected", 0); - Sys_Beep (); - return; - } - - e1 = g_qeglobals.d_select_order[0]->owner; - e2 = g_qeglobals.d_select_order[1]->owner; - - if (e1 == world_entity || e2 == world_entity) - { - Sys_Status ("Can't connect to the world", 0); - Sys_Beep (); - return; - } - - if (e1 == e2) - { - Sys_Status ("Brushes are from same entity", 0); - Sys_Beep (); - return; - } - - target = ValueForKey (e1, "target"); - if (target && target[0]) - newtarg = g_strdup(target); - else - { - target = ValueForKey(e2, "targetname"); - if(target && target[0]) - newtarg = g_strdup(target); - else - Entity_Connect(e1, e2); - } - - if(newtarg != NULL) - { - SetKeyValue(e1, "target", newtarg); - SetKeyValue(e2, "targetname", newtarg); - g_free(newtarg); - } - - Sys_UpdateWindows (W_XY | W_CAMERA); - - Select_Deselect(); - Select_Brush (g_qeglobals.d_select_order[1]); -} - -qboolean QE_SingleBrush (bool bQuiet) -{ - if ( (selected_brushes.next == &selected_brushes) - || (selected_brushes.next->next != &selected_brushes) ) - { - if (!bQuiet) - { - Sys_Printf ("Error: you must have a single brush selected\n"); - } - return false; - } - if (selected_brushes.next->owner->eclass->fixedsize) - { - if (!bQuiet) - { - Sys_Printf ("Error: you cannot manipulate fixed size entities\n"); - } - return false; - } - - return true; -} - -void QE_InitVFS (void) -{ - // VFS initialization ----------------------- - // we will call vfsInitDirectory, giving the directories to look in (for files in pk3's and for standalone files) - // we need to call in order, the mod ones first, then the base ones .. they will be searched in this order - // *nix systems have a dual filesystem in ~/.q3a, which is searched first .. so we need to add that too - Str directory,prefabs; - - // TTimo: let's leave this to HL mode for now - if (g_pGameDescription->mGameFile == "hl.game") - { - // Hydra: we search the "gametools" path first so that we can provide editor - // specific pk3's wads and misc files for use by the editor. - // the relevant map compiler tools will NOT use this directory, so this helps - // to ensure that editor files are not used/required in release versions of maps - // it also helps keep your editor files all in once place, with the editor modules, - // plugins, scripts and config files. - // it also helps when testing maps, as you'll know your files won't/can't be used - // by the game engine itself. - - // - directory = g_pGameDescription->mGameToolsPath; - vfsInitDirectory(directory.GetBuffer()); - } - - // NOTE TTimo about the mymkdir calls .. this is a bit dirty, but a safe thing on *nix - - // if we have a mod dir - if (*ValueForKey(g_qeglobals.d_project_entity, "gamename") != '\0') - { - -#if defined (__linux__) || defined (__APPLE__) - // ~/./ - directory = g_qeglobals.m_strHomeGame.GetBuffer(); - Q_mkdir (directory.GetBuffer (), 0775); - directory += ValueForKey(g_qeglobals.d_project_entity, "gamename"); - Q_mkdir (directory.GetBuffer (), 0775); - vfsInitDirectory(directory.GetBuffer()); - AddSlash (directory); - prefabs = directory; - // also create the maps dir, it will be used as prompt for load/save - directory += "/maps"; - Q_mkdir (directory, 0775); - // and the prefabs dir - prefabs += "/prefabs"; - Q_mkdir (prefabs, 0775); - -#endif - - // / - directory = g_pGameDescription->mEnginePath; - directory += ValueForKey(g_qeglobals.d_project_entity, "gamename"); - Q_mkdir (directory.GetBuffer (), 0775); - vfsInitDirectory(directory.GetBuffer()); - AddSlash(directory); - prefabs = directory; - // also create the maps dir, it will be used as prompt for load/save - directory += "/maps"; - Q_mkdir (directory.GetBuffer (), 0775); - // and the prefabs dir - prefabs += "/prefabs"; - Q_mkdir (prefabs, 0775); - } - -#if defined (__linux__) || defined (__APPLE__) - // ~/./ - directory = g_qeglobals.m_strHomeGame.GetBuffer(); - directory += g_pGameDescription->mBaseGame; - vfsInitDirectory (directory.GetBuffer ()); -#endif - - // / - directory = g_pGameDescription->mEnginePath; - directory += g_pGameDescription->mBaseGame; - vfsInitDirectory(directory.GetBuffer()); -} - -void QE_Init (void) -{ - /* - ** initialize variables - */ - g_qeglobals.d_gridsize = 8; - g_qeglobals.d_showgrid = true; - - QE_InitVFS(); - - Eclass_Init(); - FillClassList(); // list in entity window - Map_Init(); - - FillTextureMenu(); - FillBSPMenu(); - - /* - ** other stuff - */ - Z_Init (); -} - -void WINAPI QE_ConvertDOSToUnixName( char *dst, const char *src ) -{ - while ( *src ) - { - if ( *src == '\\' ) - *dst = '/'; - else - *dst = *src; - dst++; src++; - } - *dst = 0; -} - -int g_numbrushes, g_numentities; - -void QE_CountBrushesAndUpdateStatusBar( void ) -{ - static int s_lastbrushcount, s_lastentitycount; - static qboolean s_didonce; - - //entity_t *e; - brush_t *b, *next; - - g_numbrushes = 0; - g_numentities = 0; - - if ( active_brushes.next != NULL ) - { - for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next) - { - next = b->next; - if (b->brush_faces ) - { - if ( !b->owner->eclass->fixedsize) - g_numbrushes++; - else - g_numentities++; - } - } - } -/* - if ( entities.next != NULL ) - { - for ( e = entities.next ; e != &entities && g_numentities != MAX_MAP_ENTITIES ; e = e->next) - { - g_numentities++; - } - } -*/ - if ( ( ( g_numbrushes != s_lastbrushcount ) || ( g_numentities != s_lastentitycount ) ) || ( !s_didonce ) ) - { - Sys_UpdateStatusBar(); - - s_lastbrushcount = g_numbrushes; - s_lastentitycount = g_numentities; - s_didonce = true; - } -} - -char com_token[1024]; -qboolean com_eof; - -/* -================ -I_FloatTime -================ -*/ -double I_FloatTime (void) -{ - time_t t; - - time (&t); - - return t; -#if 0 -// more precise, less portable - struct timeval tp; - struct timezone tzp; - static int secbase; - - gettimeofday(&tp, &tzp); - - if (!secbase) - { - secbase = tp.tv_sec; - return tp.tv_usec/1000000.0; - } - - return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; -#endif -} - - -/* -============== -COM_Parse - -Parse a token out of a string -============== -*/ -char *COM_Parse (char *data) -{ - int c; - int len; - - len = 0; - com_token[0] = 0; - - if (!data) - return NULL; - -// skip whitespace -skipwhite: - while ( (c = *data) <= ' ') - { - if (c == 0) - { - com_eof = true; - return NULL; // end of file; - } - data++; - } - -// skip // comments - if (c=='/' && data[1] == '/') - { - while (*data && *data != '\n') - data++; - goto skipwhite; - } - - -// handle quoted strings specially - if (c == '\"') - { - data++; - do - { - c = *data++; - if (c=='\"') - { - com_token[len] = 0; - return data; - } - com_token[len] = c; - len++; - } while (1); - } - -// parse single characters - if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') - { - com_token[len] = c; - len++; - com_token[len] = 0; - return data+1; - } - -// parse a regular word - do - { - com_token[len] = c; - data++; - len++; - c = *data; - if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') - break; - } while (c>32); - - com_token[len] = 0; - return data; -} - -char* Get_COM_Token() -{ - return com_token; -} - -/* -============================================================================= - - MISC FUNCTIONS - -============================================================================= -*/ - - -int argc; -char *argv[MAX_NUM_ARGVS]; - -/* -============ -ParseCommandLine -============ -*/ -void ParseCommandLine (char *lpCmdLine) -{ - argc = 1; - argv[0] = "programname"; - - 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++; - } - - } - } -} - - - -/* -================= -CheckParm - -Checks for the given parameter in the program's command line arguments -Returns the argument number (1 to argc-1) or 0 if not present -================= -*/ -int CheckParm (char *check) -{ - int i; - - for (i = 1;i= '0' && *str <= '9') - num += *str-'0'; - else if (*str >= 'a' && *str <= 'f') - num += 10 + *str-'a'; - else if (*str >= 'A' && *str <= 'F') - num += 10 + *str-'A'; - else - Error ("Bad hex number: %s",hex); - str++; - } - - return num; -} - - -int ParseNum (char *str) -{ - if (str[0] == '$') - return ParseHex (str+1); - if (str[0] == '0' && str[1] == 'x') - return ParseHex (str+2); - return atol (str); -} - -// BSP frontend plugin -// global flag for BSP frontend plugin is g_qeglobals.bBSPFrontendPlugin -_QERPlugBSPFrontendTable g_BSPFrontendTable; - -// ============================================================================= -// Sys_ functions - -bool Sys_AltDown () -{ -#ifdef _WIN32 - return (GetKeyState(VK_MENU) & 0x8000) != 0; -#endif - -#if defined (__linux__) || defined (__APPLE__) - char keys[32]; - int x; - - XQueryKeymap(GDK_DISPLAY(), keys); - - x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_L); - if (keys[x/8] & (1 << (x % 8))) - return true; - - x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_R); - if (keys[x/8] & (1 << (x % 8))) - return true; - - return false; -#endif -} - -bool Sys_ShiftDown () -{ -#ifdef _WIN32 - return (GetKeyState(VK_SHIFT) & 0x8000) != 0; -#endif - -#if defined (__linux__) || defined (__APPLE__) - char keys[32]; - int x; - - XQueryKeymap(GDK_DISPLAY(), keys); - - x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_L); - if (keys[x/8] & (1 << (x % 8))) - return true; - - x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_R); - if (keys[x/8] & (1 << (x % 8))) - return true; - - return false; -#endif -} - -void Sys_MarkMapModified (void) -{ - char title[PATH_MAX]; - - if (modified != 1) - { - modified = true; // mark the map as changed - sprintf (title, "%s *", currentmap); - - QE_ConvertDOSToUnixName( title, title ); - Sys_SetTitle (title); - } -} - -void Sys_SetTitle (const char *text) -{ - gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_main_window), text); -} - -bool g_bWaitCursor = false; - -void WINAPI Sys_BeginWait (void) -{ - GdkCursor *cursor = gdk_cursor_new (GDK_WATCH); - gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor); - gdk_cursor_unref (cursor); - g_bWaitCursor = true; -} - -void WINAPI Sys_EndWait (void) -{ - GdkCursor *cursor = gdk_cursor_new (GDK_LEFT_PTR); - gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor); - gdk_cursor_unref (cursor); - g_bWaitCursor = false; -} - -void Sys_GetCursorPos (int *x, int *y) -{ - // FIXME: not multihead safe - gdk_window_get_pointer (NULL, x, y, NULL); -} - -void Sys_SetCursorPos (int x, int y) -{ - // NOTE: coordinates are in GDK space, not OS space -#ifdef _WIN32 - int sys_x = x - g_pParentWnd->GetGDKOffsetX(); - int sys_y = y - g_pParentWnd->GetGDKOffsetY(); - - SetCursorPos (sys_x, sys_y); -#endif - -#if defined (__linux__) || defined (__APPLE__) - XWarpPointer (GDK_DISPLAY(), None, GDK_ROOT_WINDOW(), 0, 0, 0, 0, x, y); -#endif -} - -void Sys_Beep (void) -{ -#if defined (__linux__) || defined (__APPLE__) - gdk_beep (); -#else - MessageBeep (MB_ICONASTERISK); -#endif -} - -double Sys_DoubleTime (void) -{ - return clock()/ 1000.0; -} - -/* -=============================================================== - - STATUS WINDOW - -=============================================================== -*/ - -void Sys_UpdateStatusBar( void ) -{ - extern int g_numbrushes, g_numentities; - - char numbrushbuffer[100]=""; - - sprintf( numbrushbuffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities ); - g_pParentWnd->SetStatusText(2, numbrushbuffer); - //Sys_Status( numbrushbuffer, 2 ); -} - -void Sys_Status(const char *psz, int part ) -{ - g_pParentWnd->SetStatusText (part, psz); -} - -// ============================================================================= -// MRU - -#define MRU_MAX 4 -static GtkWidget *MRU_items[MRU_MAX]; -static int MRU_used; -typedef char MRU_filename_t[PATH_MAX]; -MRU_filename_t MRU_filenames[MRU_MAX]; - -static char* MRU_GetText (int index) -{ - return MRU_filenames[index]; -} - -void buffer_write_escaped_mnemonic(char* buffer, const char* string) -{ - while(*string != '\0') - { - if(*string == '_') - { - *buffer++ = '_'; - } - - *buffer++ = *string++; - } - *buffer = '\0'; -} - -static void MRU_SetText (int index, const char *filename) -{ - strcpy(MRU_filenames[index], filename); - - char mnemonic[PATH_MAX * 2 + 4]; - mnemonic[0] = '_'; - sprintf(mnemonic+1, "%d", index+1); - mnemonic[2] = '-'; - mnemonic[3] = ' '; - buffer_write_escaped_mnemonic(mnemonic+4, filename); - gtk_label_set_text_with_mnemonic(GTK_LABEL (GTK_BIN (MRU_items[index])->child), mnemonic); -} - -void MRU_Load () -{ - int i = g_PrefsDlg.m_nMRUCount; - - if(i > 4) - i = 4; //FIXME: make this a define - - for (; i > 0; i--) - MRU_AddFile (g_PrefsDlg.m_strMRUFiles[i-1].GetBuffer()); -} - -void MRU_Save () -{ - g_PrefsDlg.m_nMRUCount = MRU_used; - - for (int i = 0; i < MRU_used; i++) - g_PrefsDlg.m_strMRUFiles[i] = MRU_GetText (i); -} - -void MRU_AddWidget (GtkWidget *widget, int pos) -{ - if (pos < MRU_MAX) - MRU_items[pos] = widget; -} - -void MRU_AddFile (const char *str) -{ - int i; - char* text; - - // check if file is already in our list - for (i = 0; i < MRU_used; i++) - { - text = MRU_GetText (i); - - if (strcmp (text, str) == 0) - { - // reorder menu - for (; i > 0; i--) - MRU_SetText (i, MRU_GetText (i-1)); - - MRU_SetText (0, str); - - return; - } - } - - if (MRU_used < MRU_MAX) - MRU_used++; - - // move items down - for (i = MRU_used-1; i > 0; i--) - MRU_SetText (i, MRU_GetText (i-1)); - - MRU_SetText (0, str); - gtk_widget_set_sensitive (MRU_items[0], TRUE); - gtk_widget_show (MRU_items[MRU_used-1]); -} - -void MRU_Activate (int index) -{ - char *text = MRU_GetText (index); - - if (access (text, R_OK) == 0) - { - text = strdup (text); - MRU_AddFile (text); - Map_LoadFile (text); - free (text); - } - else - { - MRU_used--; - - for (int i = index; i < MRU_used; i++) - MRU_SetText (i, MRU_GetText (i+1)); - - if (MRU_used == 0) - { - gtk_label_set_text (GTK_LABEL (GTK_BIN (MRU_items[0])->child), "Recent Files"); - gtk_widget_set_sensitive (MRU_items[0], FALSE); - } - else - { - gtk_widget_hide (MRU_items[MRU_used]); - } - } -} - -/* -====================================================================== - -FILE DIALOGS - -====================================================================== -*/ - -qboolean ConfirmModified () -{ - if (!modified) - return true; - - if (gtk_MessageBox (g_pParentWnd->m_pWidget, "This will lose changes to the map", "warning", MB_OKCANCEL) == IDCANCEL) - return false; - return true; -} - -void ProjectDialog () -{ - const char *filename; - char buffer[NAME_MAX]; - - /* - * Obtain the system directory name and - * store it in buffer. - */ - - strcpy(buffer, g_qeglobals.m_strHomeGame.GetBuffer()); - strcat(buffer, g_pGameDescription->mBaseGame.GetBuffer()); - strcat (buffer, "/scripts/"); - - // Display the Open dialog box - filename = file_dialog (NULL, TRUE, "Open File", buffer, "project"); - - if (filename == NULL) - return; // canceled - - // Open the file. - // NOTE: QE_LoadProject takes care of saving prefs with new path to the project file - if (!QE_LoadProject(filename)) - Sys_Printf ("Failed to load project from file: %s\n", filename); - else - // FIXME TTimo QE_Init is probably broken if you don't call it during startup right now .. - QE_Init(); -} - -/* -======================================================= - -Menu modifications - -======================================================= -*/ - -/* -================== -FillBSPMenu - -================== -*/ -char *bsp_commands[256]; - -void FillBSPMenu () -{ - GtkWidget *item, *menu; // menu points to a GtkMenu (not an item) - epair_t *ep; - GList *lst; - int i; - - menu = GTK_WIDGET (g_object_get_data (G_OBJECT (g_qeglobals_gui.d_main_window), "menu_bsp")); - - while ((lst = gtk_container_children (GTK_CONTAINER (menu))) != NULL) - gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (lst->data)); - - if (g_PrefsDlg.m_bDetachableMenus) { - item = gtk_tearoff_menu_item_new (); - gtk_menu_append (GTK_MENU (menu), item); - gtk_widget_set_sensitive (item, TRUE); - gtk_widget_show (item); - } - - if (g_qeglobals.bBSPFrontendPlugin) - { - CString str = g_BSPFrontendTable.m_pfnGetBSPMenu(); - char cTemp[1024]; - strcpy(cTemp, str); - char* token = strtok(cTemp, ",;"); - if (token && *token == ' ') - { - while (*token == ' ') - token++; - } - i = 0; - - // first token is menu name - item = gtk_menu_get_attach_widget (GTK_MENU (menu)); - gtk_label_set_text (GTK_LABEL (GTK_BIN (item)->child), token); - - token = strtok(NULL, ",;"); - while (token != NULL) - { - g_BSPFrontendCommands = g_slist_append (g_BSPFrontendCommands, g_strdup (token)); - item = gtk_menu_item_new_with_label (token); - gtk_widget_show (item); - gtk_container_add (GTK_CONTAINER (menu), item); - gtk_signal_connect (GTK_OBJECT (item), "activate", - GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i)); - token = strtok(NULL, ",;"); - i++; - } - } - else - { - i = 0; - for (ep = g_qeglobals.d_project_entity->epairs; ep; ep = ep->next) - { - if (strncmp(ep->key, "bsp_", 4)==0) - { - bsp_commands[i] = ep->key; - item = gtk_menu_item_new_with_label (ep->key+4); - gtk_widget_show (item); - gtk_container_add (GTK_CONTAINER (menu), item); - gtk_signal_connect (GTK_OBJECT (item), "activate", - GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i)); - i++; - } - } - } -} - -//============================================== - -void AddSlash(CString& strPath) -{ - if (strPath.GetLength() > 0) - { - if ((strPath.GetAt(strPath.GetLength()-1) != '/') && - (strPath.GetAt(strPath.GetLength()-1) != '\\')) - strPath += '/'; - } -} - -bool ExtractPath_and_Filename(const char* pPath, CString& strPath, CString& strFilename) -{ - CString strPathName; - strPathName = pPath; - int nSlash = strPathName.ReverseFind('\\'); - if (nSlash == -1) - // TTimo: try forward slash, some are using forward - nSlash = strPathName.ReverseFind('/'); - if (nSlash >= 0) - { - strPath = strPathName.Left(nSlash+1); - strFilename = strPathName.Right(strPathName.GetLength() - nSlash - 1); - } - // TTimo: try forward slash, some are using forward - else - strFilename = pPath; - return true; -} - -//=========================================== - -//++timo FIXME: no longer used .. remove! -char *TranslateString (char *buf) -{ - static char buf2[32768]; - int i, l; - char *out; - - l = strlen(buf); - out = buf2; - for (i=0 ; i +#include +#include "gtkmisc.h" +#if defined (__linux__) || defined (__APPLE__) +#include +#include +#include +#include +#endif +// for the logging part +#include +#include + +QEGlobals_t g_qeglobals; +QEGlobals_GUI_t g_qeglobals_gui; + +// leo: Track memory allocations for debugging +// NOTE TTimo this was never used and probably not relevant +// there are tools to do that +#ifdef MEM_DEBUG + +static GList *memblocks; + +void* debug_malloc (size_t size, const char* file, int line) +{ + void *buf = g_malloc (size + 8); + + *((const char**)buf) = file; + buf = (char*)buf + 4; + *((int*)buf) = line; + buf = (char*)buf + 4; + + memblocks = g_list_append (memblocks, buf); + + return buf; +} + +void debug_free (void *buf, const char* file, int line) +{ + const char *f; + int l; + + if (g_list_find (memblocks, buf)) + { + memblocks = g_list_remove (memblocks, buf); + + buf = (char*)buf - 4; + l = *((int*)buf); + buf = (char*)buf - 4; + f = *((const char**)buf); + + Sys_FPrintf (SYS_DBG, "free: %s %d", file, line); + Sys_FPrintf (SYS_DBG, " allocated: %s %d\n", f, l); + + g_free (buf); + } +// else +// free (buf); // from qmalloc, will leak unless we add this same hack to cmdlib +} + +#endif + +vec_t Rad_rint (vec_t in) +{ + if (g_PrefsDlg.m_bNoClamp) + return in; + else + return (float)floor (in + 0.5); +} + +void WINAPI QE_CheckOpenGLForErrors(void) +{ + char strMsg[1024]; + int i = qglGetError(); + if (i != GL_NO_ERROR) + { + if (i == GL_OUT_OF_MEMORY) + { + sprintf(strMsg, "OpenGL out of memory error %s\nDo you wish to save before exiting?", qgluErrorString((GLenum)i)); + if (gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg, "Radiant Error", MB_YESNO) == IDYES) + { + Map_SaveFile(NULL, false); + } + _exit(1); + } + else + { + Sys_Printf ("Warning: OpenGL Error %s\n", qgluErrorString((GLenum)i)); + } + } +} + +// NOTE: don't this function, use VFS instead +char *ExpandReletivePath (char *p) +{ + static char temp[1024]; + const char *base; + + if (!p || !p[0]) + return NULL; + if (p[0] == '/' || p[0] == '\\') + return p; + + base = ValueForKey(g_qeglobals.d_project_entity, "basepath"); + sprintf (temp, "%s/%s", base, p); + return temp; +} + +char *copystring (char *s) +{ + char *b; + b = (char*)malloc(strlen(s)+1); + strcpy (b,s); + return b; +} + + +bool DoesFileExist(const char* pBuff, long& lSize) +{ + FileStream file; + if (file.Open(pBuff, "r")) + { + lSize += file.GetLength(); + file.Close(); + return true; + } + return false; +} + + +void Map_Snapshot() +{ + CString strMsg; + + // I hope the modified flag is kept correctly up to date + if (!modified) + return; + + // we need to do the following + // 1. make sure the snapshot directory exists (create it if it doesn't) + // 2. find out what the lastest save is based on number + // 3. inc that and save the map + CString strOrgPath, strOrgFile; + ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile); + AddSlash(strOrgPath); + strOrgPath += "snapshots"; + bool bGo = true; + struct stat Stat; + if (stat(strOrgPath, &Stat) == -1) + { +#ifdef _WIN32 + bGo = (_mkdir(strOrgPath) != -1); +#endif + +#if defined (__linux__) || defined (__APPLE__) + bGo = (mkdir(strOrgPath,0755) != -1); +#endif + } + AddSlash(strOrgPath); + if (bGo) + { + int nCount = 0; + long lSize = 0; + CString strNewPath; + strNewPath = strOrgPath; + strNewPath += strOrgFile; + CString strFile; + while (bGo) + { + char buf[PATH_MAX]; + sprintf( buf, "%s.%i", strNewPath.GetBuffer(), nCount ); + strFile = buf; + bGo = DoesFileExist(strFile, lSize); + nCount++; + } + // strFile has the next available slot + Map_SaveFile(strFile, false); + // it is still a modified map (we enter this only if this is a modified map) + Sys_SetTitle (currentmap); + Sys_MarkMapModified(); + if (lSize > 12 * 1024 * 1024) // total size of saves > 4 mb + { + Sys_Printf("The snapshot files in %s total more than 4 megabytes. You might consider cleaning up.", strOrgPath.GetBuffer()); + } + } + else + { + strMsg.Format("Snapshot save failed.. unabled to create directory\n%s", strOrgPath.GetBuffer()); + gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg); + } + strOrgPath = ""; + strOrgFile = ""; +} +/* +=============== +QE_CheckAutoSave + +If five minutes have passed since making a change +and the map hasn't been saved, save it out. +=============== +*/ + + +void QE_CheckAutoSave( void ) +{ + static time_t s_start; + time_t now; + time (&now); + + if (modified != 1 || !s_start) + { + s_start = now; + return; + } + + if ((now - s_start) > (60 * g_PrefsDlg.m_nAutoSave)) + { + if (g_PrefsDlg.m_bAutoSave) + { + CString strMsg; + strMsg = g_PrefsDlg.m_bSnapShots ? "Autosaving snapshot..." : "Autosaving..."; + Sys_Printf(strMsg); + Sys_Printf("\n"); + Sys_Status (strMsg,0); + + // only snapshot if not working on a default map + if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0) + { + Map_Snapshot(); + } + else + { + Map_SaveFile (ValueForKey(g_qeglobals.d_project_entity, "autosave"), false); + } + + Sys_Status ("Autosaving...Saved.", 0 ); + modified = 2; + } + else + { + Sys_Printf ("Autosave skipped...\n"); + Sys_Status ("Autosave skipped...", 0 ); + } + s_start = now; + } +} + + +// NOTE TTimo we don't like that BuildShortPathName too much +// the VFS provides a vfsCleanFileName which should perform the cleanup tasks +// in the long run I'd like to completely get rid of this + +// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=144 +// used to be disabled, but caused problems + +// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=291 +// can't work with long win32 names until the BSP commands are not working differently +#ifdef _WIN32 +int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen) +{ + char *pFile = NULL; + int nResult = GetFullPathName(pPath, nBufferLen, pBuffer, &pFile); + nResult = GetShortPathName(pPath, pBuffer, nBufferLen); + if (nResult == 0) + strcpy(pBuffer, pPath); // Use long filename + return nResult; +} +#endif + +#if defined (__linux__) || defined (__APPLE__) +int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen) +{ + // remove /../ from directories + const char *scr = pPath; char *dst = pBuffer; + for (int i = 0; (i < nBufferLen) && (*scr != 0); i++) + { + if (*scr == '/' && *(scr+1) == '.' && *(scr+2) == '.') + { + scr += 3; + while (dst != pBuffer && *(--dst) != '/') + { + i--; + } + } + + *dst = *scr; + + scr++; dst++; + } + *dst = 0; + + return strlen (pBuffer); +} +#endif + +/* +const char *g_pPathFixups[]= +{ + "basepath", + "autosave", +}; + +const int g_nPathFixupCount = sizeof(g_pPathFixups) / sizeof(const char*); + +void QE_CheckProjectEntity() +{ + char *pFile; + char pBuff[PATH_MAX]; + char pNewPath[PATH_MAX]; + for (int i = 0; i < g_nPathFixupCount; i++) + { + char *pPath = ValueForKey (g_qeglobals.d_project_entity, g_pPathFixups[i]); + + strcpy (pNewPath, pPath); + if (pPath[0] != '\\' && pPath[0] != '/') + if (GetFullPathName(pPath, PATH_MAX, pBuff, &pFile)) + strcpy (pNewPath, pBuff); + + BuildShortPathName (pNewPath, pBuff, PATH_MAX); + + // check it's not ending with a filename seperator + if (pBuff[strlen(pBuff)-1] == '/' || pBuff[strlen(pBuff)-1] == '\\') + { + Sys_FPrintf(SYS_WRN, "WARNING: \"%s\" path in the project file has an ending file seperator, fixing.\n", g_pPathFixups[i]); + pBuff[strlen(pBuff)-1]=0; + } + + SetKeyValue(g_qeglobals.d_project_entity, g_pPathFixups[i], pBuff); + } +} +*/ + +void HandleXMLError( void* ctxt, const char* text, ... ) +{ + va_list argptr; + static char buf[32768]; + + va_start (argptr,text); + vsprintf (buf, text, argptr); + Sys_FPrintf (SYS_ERR, "XML %s\n", buf); + va_end (argptr); +} + +#define DTD_BUFFER_LENGTH 1024 +xmlDocPtr ParseXMLStream(IDataStream *stream, bool validate = false) +{ + xmlDocPtr doc = NULL; + bool wellFormed = false, valid = false; + int res, size = 1024; + char chars[1024]; + xmlParserCtxtPtr ctxt; + + // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=433 + //if(validate) + // xmlDoValidityCheckingDefaultValue = 1; + //else + xmlDoValidityCheckingDefaultValue = 0; + xmlSetGenericErrorFunc(NULL, HandleXMLError); + + // SPoG + // HACK: use AppPath to resolve DTD location + // do a buffer-safe string copy and concatenate + int i; + char* w; + const char* r; + char buf[DTD_BUFFER_LENGTH]; + + w = buf; + i = 0; + // copy + //assert(g_strAppPath.GetBuffer() != NULL); + for(r = g_strAppPath.GetBuffer(); iRead(chars, 4); + if (res > 0) + { + ctxt = xmlCreatePushParserCtxt(NULL, NULL, chars, res, buf); + + while ((res = stream->Read(chars, size)) > 0) + { + xmlParseChunk(ctxt, chars, res, 0); + } + xmlParseChunk(ctxt, chars, 0, 1); + doc = ctxt->myDoc; + + wellFormed = (ctxt->wellFormed == 1); + valid = (ctxt->valid == 1); + + xmlFreeParserCtxt(ctxt); + } + + if(wellFormed && (!validate || (validate && valid))) + return doc; + + if(doc != NULL) + xmlFreeDoc(doc); + + return NULL; +} + +xmlDocPtr ParseXMLFile(const char* filename, bool validate = false) +{ + FileStream stream; + if (stream.Open(filename, "r")) + return ParseXMLStream(&stream, validate); + + Sys_FPrintf(SYS_ERR, "Failed to open file: %s\n",filename); + return NULL; +} + +// copy a string r to a buffer w +// replace $string as appropriate +void ReplaceTemplates(char* w, const char* r) +{ + const char *p; + const char *__ENGINEPATH = "TEMPLATEenginepath"; + const char *__USERHOMEPATH = "TEMPLATEuserhomepath"; + const char *__TOOLSPATH = "TEMPLATEtoolspath"; + const char *__BASEDIR = "TEMPLATEbasedir"; + const char *__APPPATH = "TEMPLATEapppath"; + + // iterate through string r + while(*r!='\0') + { + // check for special character + if(*r=='$') + { + if(strncmp(r+1, __ENGINEPATH, strlen(__ENGINEPATH)) == 0) + { + r+=strlen(__ENGINEPATH)+1; + p = g_pGameDescription->mEnginePath.GetBuffer(); + } + else if(strncmp(r+1, __USERHOMEPATH, strlen(__USERHOMEPATH)) == 0) + { + r+=strlen(__USERHOMEPATH)+1; + p = g_qeglobals.m_strHomeGame.GetBuffer(); + } + else if(strncmp(r+1, __BASEDIR, strlen(__BASEDIR)) == 0) + { + r+=strlen(__BASEDIR)+1; + p = g_pGameDescription->mBaseGame; + } + else if(strncmp(r+1, __TOOLSPATH, strlen(__TOOLSPATH)) == 0) + { + r+=strlen(__TOOLSPATH)+1; + p = g_strGameToolsPath.GetBuffer(); + } + else if(strncmp(r+1, __APPPATH, strlen(__APPPATH)) == 0) + { + r+=strlen(__APPPATH)+1; + p = g_strAppPath.GetBuffer(); + } + else + { + r++; + p = "$"; + } + + while(*p!='\0') *w++ = *p++; + } + else *w++ = *r++; + } + *w = '\0'; +} + +/* +=========== +QE_LoadProject +TODO TODO TODO (don't think this got fully merged in) +TTimo: added project file "version", version 2 adds '#' chars to the BSP command strings +version 3 was .. I don't remember .. version 4 adds q3map2 commands +TTimo: when QE_LoadProject is called, the prefs are updated with path to the latest project and saved on disk +=========== +*/ +/*\todo decide on a sensible location/name for project files.*/ +bool QE_LoadProject (const char *projectfile) +{ + char buf[1024]; + xmlDocPtr doc; + xmlNodePtr node, project; + + Sys_Printf("Loading project file: \"%s\"\n", projectfile); + doc = ParseXMLFile(projectfile, true); + + if(doc == NULL) return false; + + node=doc->children; + while(node != NULL && node->type != XML_DTD_NODE) node=node->next; + if(node == NULL || strcmp((char*)node->name, "project") != 0) + { + Sys_FPrintf(SYS_ERR, "ERROR: invalid file type\n"); + return false; + } + + while(node->type != XML_ELEMENT_NODE) node=node->next; + // + project = node; + + if(g_qeglobals.d_project_entity != NULL) Entity_Free(g_qeglobals.d_project_entity); + g_qeglobals.d_project_entity = Entity_Alloc(); + + for(node = project->children; node != NULL; node=node->next) + { + if(node->type != XML_ELEMENT_NODE) continue; + + // + ReplaceTemplates(buf, (char*)node->properties->next->children->content); + + SetKeyValue(g_qeglobals.d_project_entity, (char*)node->properties->children->content, buf); + } + + xmlFreeDoc(doc); + + // project file version checking + // add a version checking to avoid people loading later versions of the project file and bitching + int ver = IntForKey( g_qeglobals.d_project_entity, "version" ); + if (ver > PROJECT_VERSION) + { + char strMsg[1024]; + sprintf (strMsg, "This is a version %d project file. This build only supports <=%d project files.\n" + "Please choose another project file or upgrade your version of Radiant.", ver, PROJECT_VERSION); + gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK); + // set the project file to nothing so we are sure we'll ask next time? + g_PrefsDlg.m_strLastProject = ""; + g_PrefsDlg.SavePrefs(); + return false; + } + + // set here some default project settings you need + if ( strlen( ValueForKey( g_qeglobals.d_project_entity, "brush_primit" ) ) == 0 ) + { + SetKeyValue( g_qeglobals.d_project_entity, "brush_primit", "0" ); + } + + g_qeglobals.m_bBrushPrimitMode = IntForKey( g_qeglobals.d_project_entity, "brush_primit" ); + + g_qeglobals.m_strHomeMaps = g_qeglobals.m_strHomeGame; + const char* str = ValueForKey(g_qeglobals.d_project_entity, "gamename"); + if(str[0] == '\0') str = g_pGameDescription->mBaseGame.GetBuffer(); + g_qeglobals.m_strHomeMaps += str; + g_qeglobals.m_strHomeMaps += '/'; + + // don't forget to create the dirs + Q_mkdir(g_qeglobals.m_strHomeGame.GetBuffer(), 0775); + Q_mkdir(g_qeglobals.m_strHomeMaps.GetBuffer(), 0775); + + // usefull for the log file and debuggin fucked up configurations from users: + // output the basic information of the .qe4 project file + // SPoG + // all these paths should be unix format, with a trailing slash at the end + // if not.. to debug, check that the project file paths are set up correctly + Sys_Printf("basepath : %s\n", ValueForKey( g_qeglobals.d_project_entity, "basepath") ); + Sys_Printf("entitypath : %s\n", ValueForKey( g_qeglobals.d_project_entity, "entitypath" ) ); + + + // check whether user_project key exists.. + // if not, save the current project under a new name + if (ValueForKey(g_qeglobals.d_project_entity, "user_project")[0] == '\0') + { + Sys_Printf("Loaded a template project file\n"); + + // create the user_project key + SetKeyValue( g_qeglobals.d_project_entity, "user_project", "1" ); + + // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672 + if (IntForKey( g_qeglobals.d_project_entity, "version" ) != PROJECT_VERSION) + { + char strMsg[2048]; + sprintf(strMsg, + "The template project '%s' has version %d. The editor binary is configured for version %d.\n" + "This indicates a problem in your setup. See http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672\n" + "I will keep going with this project till you fix this", + projectfile, IntForKey( g_qeglobals.d_project_entity, "version" ), PROJECT_VERSION); + gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK); + } + + // create the writable project file path + strcpy(buf, g_qeglobals.m_strHomeGame.GetBuffer()); + strcat(buf, g_pGameDescription->mBaseGame.GetBuffer()); + strcat(buf, "/scripts/"); + // while the filename is already in use, increment the number we add to the end + int counter = 0; + char pUser[PATH_MAX]; + while (1) + { + sprintf( pUser, "%suser%d." PROJECT_FILETYPE, buf, counter ); + counter++; + if (access( pUser, R_OK) != 0) + { + // this is the one + strcpy( buf, pUser ); + break; + } + } + // saving project will cause a save prefs + g_PrefsDlg.m_strLastProject = buf; + g_PrefsDlg.m_nLastProjectVer = IntForKey( g_qeglobals.d_project_entity, "version" ); + QE_SaveProject(buf); + } + else + { + // update preferences::LastProject with path of this successfully-loaded project + // save preferences + Sys_Printf("Setting current project in prefs to \"%s\"\n", g_PrefsDlg.m_strLastProject.GetBuffer() ); + g_PrefsDlg.m_strLastProject = projectfile; + g_PrefsDlg.SavePrefs(); + } + + return true; +} + +/* +=========== +QE_SaveProject +TTimo: whenever QE_SaveProject is called, prefs are updated and saved with the path to the project +=========== +*/ +qboolean QE_SaveProject (const char* filename) +{ + Sys_Printf("Save project file '%s'\n", filename); + + xmlNodePtr node; + xmlDocPtr doc = xmlNewDoc((xmlChar *)"1.0"); + // create DTD node + xmlCreateIntSubset(doc, (xmlChar *)"project", NULL, (xmlChar *)"project.dtd"); + // create project node + doc->children->next = xmlNewDocNode(doc, NULL, (xmlChar *)"project", NULL); + + for(epair_t* epair = g_qeglobals.d_project_entity->epairs; epair != NULL; epair = epair->next) + { + node = xmlNewChild(doc->children->next, NULL, (xmlChar *)"key", NULL); + xmlSetProp(node, (xmlChar*)"name", (xmlChar*)epair->key); + xmlSetProp(node, (xmlChar*)"value", (xmlChar*)epair->value); + } + + CreateDirectoryPath(filename); + if (xmlSaveFormatFile(filename, doc, 1) != -1) + { + xmlFreeDoc(doc); + Sys_Printf("Setting current project in prefs to \"%s\"\n", filename ); + g_PrefsDlg.m_strLastProject = filename; + g_PrefsDlg.SavePrefs(); + return TRUE; + } + else + { + xmlFreeDoc(doc); + Sys_FPrintf(SYS_ERR, "failed to save project file: \"%s\"\n", filename); + return FALSE; + } +} + + + +/* +=========== +QE_KeyDown +=========== +*/ +#define SPEED_MOVE 32 +#define SPEED_TURN 22.5 + + +/* +=============== +ConnectEntities + +Sets target / targetname on the two entities selected +from the first selected to the secon +=============== +*/ +void ConnectEntities (void) +{ + entity_t *e1, *e2; + const char *target; + char *newtarg = NULL; + + if (g_qeglobals.d_select_count != 2) + { + Sys_Status ("Must have two brushes selected", 0); + Sys_Beep (); + return; + } + + e1 = g_qeglobals.d_select_order[0]->owner; + e2 = g_qeglobals.d_select_order[1]->owner; + + if (e1 == world_entity || e2 == world_entity) + { + Sys_Status ("Can't connect to the world", 0); + Sys_Beep (); + return; + } + + if (e1 == e2) + { + Sys_Status ("Brushes are from same entity", 0); + Sys_Beep (); + return; + } + + target = ValueForKey (e1, "target"); + if (target && target[0]) + newtarg = g_strdup(target); + else + { + target = ValueForKey(e2, "targetname"); + if(target && target[0]) + newtarg = g_strdup(target); + else + Entity_Connect(e1, e2); + } + + if(newtarg != NULL) + { + SetKeyValue(e1, "target", newtarg); + SetKeyValue(e2, "targetname", newtarg); + g_free(newtarg); + } + + Sys_UpdateWindows (W_XY | W_CAMERA); + + Select_Deselect(); + Select_Brush (g_qeglobals.d_select_order[1]); +} + +qboolean QE_SingleBrush (bool bQuiet) +{ + if ( (selected_brushes.next == &selected_brushes) + || (selected_brushes.next->next != &selected_brushes) ) + { + if (!bQuiet) + { + Sys_Printf ("Error: you must have a single brush selected\n"); + } + return false; + } + if (selected_brushes.next->owner->eclass->fixedsize) + { + if (!bQuiet) + { + Sys_Printf ("Error: you cannot manipulate fixed size entities\n"); + } + return false; + } + + return true; +} + +void QE_InitVFS (void) +{ + // VFS initialization ----------------------- + // we will call vfsInitDirectory, giving the directories to look in (for files in pk3's and for standalone files) + // we need to call in order, the mod ones first, then the base ones .. they will be searched in this order + // *nix systems have a dual filesystem in ~/.q3a, which is searched first .. so we need to add that too + Str directory,prefabs; + + // TTimo: let's leave this to HL mode for now + if (g_pGameDescription->mGameFile == "hl.game") + { + // Hydra: we search the "gametools" path first so that we can provide editor + // specific pk3's wads and misc files for use by the editor. + // the relevant map compiler tools will NOT use this directory, so this helps + // to ensure that editor files are not used/required in release versions of maps + // it also helps keep your editor files all in once place, with the editor modules, + // plugins, scripts and config files. + // it also helps when testing maps, as you'll know your files won't/can't be used + // by the game engine itself. + + // + directory = g_pGameDescription->mGameToolsPath; + vfsInitDirectory(directory.GetBuffer()); + } + + // NOTE TTimo about the mymkdir calls .. this is a bit dirty, but a safe thing on *nix + + // if we have a mod dir + if (*ValueForKey(g_qeglobals.d_project_entity, "gamename") != '\0') + { + +#if defined (__linux__) || defined (__APPLE__) + // ~/./ + directory = g_qeglobals.m_strHomeGame.GetBuffer(); + Q_mkdir (directory.GetBuffer (), 0775); + directory += ValueForKey(g_qeglobals.d_project_entity, "gamename"); + Q_mkdir (directory.GetBuffer (), 0775); + vfsInitDirectory(directory.GetBuffer()); + AddSlash (directory); + prefabs = directory; + // also create the maps dir, it will be used as prompt for load/save + directory += "/maps"; + Q_mkdir (directory, 0775); + // and the prefabs dir + prefabs += "/prefabs"; + Q_mkdir (prefabs, 0775); + +#endif + + // / + directory = g_pGameDescription->mEnginePath; + directory += ValueForKey(g_qeglobals.d_project_entity, "gamename"); + Q_mkdir (directory.GetBuffer (), 0775); + vfsInitDirectory(directory.GetBuffer()); + AddSlash(directory); + prefabs = directory; + // also create the maps dir, it will be used as prompt for load/save + directory += "/maps"; + Q_mkdir (directory.GetBuffer (), 0775); + // and the prefabs dir + prefabs += "/prefabs"; + Q_mkdir (prefabs, 0775); + } + +#if defined (__linux__) || defined (__APPLE__) + // ~/./ + directory = g_qeglobals.m_strHomeGame.GetBuffer(); + directory += g_pGameDescription->mBaseGame; + vfsInitDirectory (directory.GetBuffer ()); +#endif + + // / + directory = g_pGameDescription->mEnginePath; + directory += g_pGameDescription->mBaseGame; + vfsInitDirectory(directory.GetBuffer()); +} + +void QE_Init (void) +{ + /* + ** initialize variables + */ + g_qeglobals.d_gridsize = 8; + g_qeglobals.d_showgrid = true; + + QE_InitVFS(); + + Eclass_Init(); + FillClassList(); // list in entity window + Map_Init(); + + FillTextureMenu(); + FillBSPMenu(); + + /* + ** other stuff + */ + Z_Init (); +} + +void WINAPI QE_ConvertDOSToUnixName( char *dst, const char *src ) +{ + while ( *src ) + { + if ( *src == '\\' ) + *dst = '/'; + else + *dst = *src; + dst++; src++; + } + *dst = 0; +} + +int g_numbrushes, g_numentities; + +void QE_CountBrushesAndUpdateStatusBar( void ) +{ + static int s_lastbrushcount, s_lastentitycount; + static qboolean s_didonce; + + //entity_t *e; + brush_t *b, *next; + + g_numbrushes = 0; + g_numentities = 0; + + if ( active_brushes.next != NULL ) + { + for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next) + { + next = b->next; + if (b->brush_faces ) + { + if ( !b->owner->eclass->fixedsize) + g_numbrushes++; + else + g_numentities++; + } + } + } +/* + if ( entities.next != NULL ) + { + for ( e = entities.next ; e != &entities && g_numentities != MAX_MAP_ENTITIES ; e = e->next) + { + g_numentities++; + } + } +*/ + if ( ( ( g_numbrushes != s_lastbrushcount ) || ( g_numentities != s_lastentitycount ) ) || ( !s_didonce ) ) + { + Sys_UpdateStatusBar(); + + s_lastbrushcount = g_numbrushes; + s_lastentitycount = g_numentities; + s_didonce = true; + } +} + +char com_token[1024]; +qboolean com_eof; + +/* +================ +I_FloatTime +================ +*/ +double I_FloatTime (void) +{ + time_t t; + + time (&t); + + return t; +#if 0 +// more precise, less portable + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +#endif +} + + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + int c; + int len; + + len = 0; + com_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + { + com_eof = true; + return NULL; // end of file; + } + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + do + { + c = *data++; + if (c=='\"') + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } while (1); + } + +// parse single characters + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + { + com_token[len] = c; + len++; + com_token[len] = 0; + return data+1; + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + break; + } while (c>32); + + com_token[len] = 0; + return data; +} + +char* Get_COM_Token() +{ + return com_token; +} + +/* +============================================================================= + + MISC FUNCTIONS + +============================================================================= +*/ + + +int argc; +char *argv[MAX_NUM_ARGVS]; + +/* +============ +ParseCommandLine +============ +*/ +void ParseCommandLine (char *lpCmdLine) +{ + argc = 1; + argv[0] = "programname"; + + 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++; + } + + } + } +} + + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int CheckParm (char *check) +{ + int i; + + for (i = 1;i= '0' && *str <= '9') + num += *str-'0'; + else if (*str >= 'a' && *str <= 'f') + num += 10 + *str-'a'; + else if (*str >= 'A' && *str <= 'F') + num += 10 + *str-'A'; + else + Error ("Bad hex number: %s",hex); + str++; + } + + return num; +} + + +int ParseNum (char *str) +{ + if (str[0] == '$') + return ParseHex (str+1); + if (str[0] == '0' && str[1] == 'x') + return ParseHex (str+2); + return atol (str); +} + +// BSP frontend plugin +// global flag for BSP frontend plugin is g_qeglobals.bBSPFrontendPlugin +_QERPlugBSPFrontendTable g_BSPFrontendTable; + +// ============================================================================= +// Sys_ functions + +bool Sys_AltDown () +{ +#ifdef _WIN32 + return (GetKeyState(VK_MENU) & 0x8000) != 0; +#endif + +#if defined (__linux__) || defined (__APPLE__) + char keys[32]; + int x; + + XQueryKeymap(GDK_DISPLAY(), keys); + + x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_L); + if (keys[x/8] & (1 << (x % 8))) + return true; + + x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_R); + if (keys[x/8] & (1 << (x % 8))) + return true; + + return false; +#endif +} + +bool Sys_ShiftDown () +{ +#ifdef _WIN32 + return (GetKeyState(VK_SHIFT) & 0x8000) != 0; +#endif + +#if defined (__linux__) || defined (__APPLE__) + char keys[32]; + int x; + + XQueryKeymap(GDK_DISPLAY(), keys); + + x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_L); + if (keys[x/8] & (1 << (x % 8))) + return true; + + x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_R); + if (keys[x/8] & (1 << (x % 8))) + return true; + + return false; +#endif +} + +void Sys_MarkMapModified (void) +{ + char title[PATH_MAX]; + + if (modified != 1) + { + modified = true; // mark the map as changed + sprintf (title, "%s *", currentmap); + + QE_ConvertDOSToUnixName( title, title ); + Sys_SetTitle (title); + } +} + +void Sys_SetTitle (const char *text) +{ + gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_main_window), text); +} + +bool g_bWaitCursor = false; + +void WINAPI Sys_BeginWait (void) +{ + GdkCursor *cursor = gdk_cursor_new (GDK_WATCH); + gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor); + gdk_cursor_unref (cursor); + g_bWaitCursor = true; +} + +void WINAPI Sys_EndWait (void) +{ + GdkCursor *cursor = gdk_cursor_new (GDK_LEFT_PTR); + gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor); + gdk_cursor_unref (cursor); + g_bWaitCursor = false; +} + +void Sys_GetCursorPos (int *x, int *y) +{ + // FIXME: not multihead safe + gdk_window_get_pointer (NULL, x, y, NULL); +} + +void Sys_SetCursorPos (int x, int y) +{ + // NOTE: coordinates are in GDK space, not OS space +#ifdef _WIN32 + int sys_x = x - g_pParentWnd->GetGDKOffsetX(); + int sys_y = y - g_pParentWnd->GetGDKOffsetY(); + + SetCursorPos (sys_x, sys_y); +#endif + +#if defined (__linux__) || defined (__APPLE__) + XWarpPointer (GDK_DISPLAY(), None, GDK_ROOT_WINDOW(), 0, 0, 0, 0, x, y); +#endif +} + +void Sys_Beep (void) +{ +#if defined (__linux__) || defined (__APPLE__) + gdk_beep (); +#else + MessageBeep (MB_ICONASTERISK); +#endif +} + +double Sys_DoubleTime (void) +{ + return clock()/ 1000.0; +} + +/* +=============================================================== + + STATUS WINDOW + +=============================================================== +*/ + +void Sys_UpdateStatusBar( void ) +{ + extern int g_numbrushes, g_numentities; + + char numbrushbuffer[100]=""; + + sprintf( numbrushbuffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities ); + g_pParentWnd->SetStatusText(2, numbrushbuffer); + //Sys_Status( numbrushbuffer, 2 ); +} + +void Sys_Status(const char *psz, int part ) +{ + g_pParentWnd->SetStatusText (part, psz); +} + +// ============================================================================= +// MRU + +#define MRU_MAX 4 +static GtkWidget *MRU_items[MRU_MAX]; +static int MRU_used; +typedef char MRU_filename_t[PATH_MAX]; +MRU_filename_t MRU_filenames[MRU_MAX]; + +static char* MRU_GetText (int index) +{ + return MRU_filenames[index]; +} + +void buffer_write_escaped_mnemonic(char* buffer, const char* string) +{ + while(*string != '\0') + { + if(*string == '_') + { + *buffer++ = '_'; + } + + *buffer++ = *string++; + } + *buffer = '\0'; +} + +static void MRU_SetText (int index, const char *filename) +{ + strcpy(MRU_filenames[index], filename); + + char mnemonic[PATH_MAX * 2 + 4]; + mnemonic[0] = '_'; + sprintf(mnemonic+1, "%d", index+1); + mnemonic[2] = '-'; + mnemonic[3] = ' '; + buffer_write_escaped_mnemonic(mnemonic+4, filename); + gtk_label_set_text_with_mnemonic(GTK_LABEL (GTK_BIN (MRU_items[index])->child), mnemonic); +} + +void MRU_Load () +{ + int i = g_PrefsDlg.m_nMRUCount; + + if(i > 4) + i = 4; //FIXME: make this a define + + for (; i > 0; i--) + MRU_AddFile (g_PrefsDlg.m_strMRUFiles[i-1].GetBuffer()); +} + +void MRU_Save () +{ + g_PrefsDlg.m_nMRUCount = MRU_used; + + for (int i = 0; i < MRU_used; i++) + g_PrefsDlg.m_strMRUFiles[i] = MRU_GetText (i); +} + +void MRU_AddWidget (GtkWidget *widget, int pos) +{ + if (pos < MRU_MAX) + MRU_items[pos] = widget; +} + +void MRU_AddFile (const char *str) +{ + int i; + char* text; + + // check if file is already in our list + for (i = 0; i < MRU_used; i++) + { + text = MRU_GetText (i); + + if (strcmp (text, str) == 0) + { + // reorder menu + for (; i > 0; i--) + MRU_SetText (i, MRU_GetText (i-1)); + + MRU_SetText (0, str); + + return; + } + } + + if (MRU_used < MRU_MAX) + MRU_used++; + + // move items down + for (i = MRU_used-1; i > 0; i--) + MRU_SetText (i, MRU_GetText (i-1)); + + MRU_SetText (0, str); + gtk_widget_set_sensitive (MRU_items[0], TRUE); + gtk_widget_show (MRU_items[MRU_used-1]); +} + +void MRU_Activate (int index) +{ + char *text = MRU_GetText (index); + + if (access (text, R_OK) == 0) + { + text = strdup (text); + MRU_AddFile (text); + Map_LoadFile (text); + free (text); + } + else + { + MRU_used--; + + for (int i = index; i < MRU_used; i++) + MRU_SetText (i, MRU_GetText (i+1)); + + if (MRU_used == 0) + { + gtk_label_set_text (GTK_LABEL (GTK_BIN (MRU_items[0])->child), "Recent Files"); + gtk_widget_set_sensitive (MRU_items[0], FALSE); + } + else + { + gtk_widget_hide (MRU_items[MRU_used]); + } + } +} + +/* +====================================================================== + +FILE DIALOGS + +====================================================================== +*/ + +qboolean ConfirmModified () +{ + if (!modified) + return true; + + if (gtk_MessageBox (g_pParentWnd->m_pWidget, "This will lose changes to the map", "warning", MB_OKCANCEL) == IDCANCEL) + return false; + return true; +} + +void ProjectDialog () +{ + const char *filename; + char buffer[NAME_MAX]; + + /* + * Obtain the system directory name and + * store it in buffer. + */ + + strcpy(buffer, g_qeglobals.m_strHomeGame.GetBuffer()); + strcat(buffer, g_pGameDescription->mBaseGame.GetBuffer()); + strcat (buffer, "/scripts/"); + + // Display the Open dialog box + filename = file_dialog (NULL, TRUE, "Open File", buffer, "project"); + + if (filename == NULL) + return; // canceled + + // Open the file. + // NOTE: QE_LoadProject takes care of saving prefs with new path to the project file + if (!QE_LoadProject(filename)) + Sys_Printf ("Failed to load project from file: %s\n", filename); + else + // FIXME TTimo QE_Init is probably broken if you don't call it during startup right now .. + QE_Init(); +} + +/* +======================================================= + +Menu modifications + +======================================================= +*/ + +/* +================== +FillBSPMenu + +================== +*/ +char *bsp_commands[256]; + +void FillBSPMenu () +{ + GtkWidget *item, *menu; // menu points to a GtkMenu (not an item) + epair_t *ep; + GList *lst; + int i; + + menu = GTK_WIDGET (g_object_get_data (G_OBJECT (g_qeglobals_gui.d_main_window), "menu_bsp")); + + while ((lst = gtk_container_children (GTK_CONTAINER (menu))) != NULL) + gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (lst->data)); + + if (g_PrefsDlg.m_bDetachableMenus) { + item = gtk_tearoff_menu_item_new (); + gtk_menu_append (GTK_MENU (menu), item); + gtk_widget_set_sensitive (item, TRUE); + gtk_widget_show (item); + } + + if (g_qeglobals.bBSPFrontendPlugin) + { + CString str = g_BSPFrontendTable.m_pfnGetBSPMenu(); + char cTemp[1024]; + strcpy(cTemp, str); + char* token = strtok(cTemp, ",;"); + if (token && *token == ' ') + { + while (*token == ' ') + token++; + } + i = 0; + + // first token is menu name + item = gtk_menu_get_attach_widget (GTK_MENU (menu)); + gtk_label_set_text (GTK_LABEL (GTK_BIN (item)->child), token); + + token = strtok(NULL, ",;"); + while (token != NULL) + { + g_BSPFrontendCommands = g_slist_append (g_BSPFrontendCommands, g_strdup (token)); + item = gtk_menu_item_new_with_label (token); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + gtk_signal_connect (GTK_OBJECT (item), "activate", + GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i)); + token = strtok(NULL, ",;"); + i++; + } + } + else + { + i = 0; + for (ep = g_qeglobals.d_project_entity->epairs; ep; ep = ep->next) + { + if (strncmp(ep->key, "bsp_", 4)==0) + { + bsp_commands[i] = ep->key; + item = gtk_menu_item_new_with_label (ep->key+4); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + gtk_signal_connect (GTK_OBJECT (item), "activate", + GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i)); + i++; + } + } + } +} + +//============================================== + +void AddSlash(CString& strPath) +{ + if (strPath.GetLength() > 0) + { + if ((strPath.GetAt(strPath.GetLength()-1) != '/') && + (strPath.GetAt(strPath.GetLength()-1) != '\\')) + strPath += '/'; + } +} + +bool ExtractPath_and_Filename(const char* pPath, CString& strPath, CString& strFilename) +{ + CString strPathName; + strPathName = pPath; + int nSlash = strPathName.ReverseFind('\\'); + if (nSlash == -1) + // TTimo: try forward slash, some are using forward + nSlash = strPathName.ReverseFind('/'); + if (nSlash >= 0) + { + strPath = strPathName.Left(nSlash+1); + strFilename = strPathName.Right(strPathName.GetLength() - nSlash - 1); + } + // TTimo: try forward slash, some are using forward + else + strFilename = pPath; + return true; +} + +//=========================================== + +//++timo FIXME: no longer used .. remove! +char *TranslateString (char *buf) +{ + static char buf2[32768]; + int i, l; + char *out; + + l = strlen(buf); + out = buf2; + for (i=0 ; i