]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/map.cpp
misc fixes
[xonotic/netradiant.git] / radiant / map.cpp
index 051c0a51ac6f90d4f47d64897b7221557900d0a9..59290f2a7e28d9bd9afcb2b59f17610fa98f9253 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-*/\r
-\r
-#include "stdafx.h"\r
-#include <string.h>\r
-#if defined (__linux__) || defined (__APPLE__)\r
-#include <unistd.h>\r
-#endif\r
-#include "preferences.h"\r
-#include "mainframe.h"\r
-#include "gtkmisc.h"\r
-#include "filters.h"\r
-\r
-extern MainFrame* g_pParentWnd;\r
-\r
-int    modified;       // for quit confirmation (0 = clean, 1 = unsaved,\r
-                               // 2 = autosaved, but not regular saved) \r
-\r
-char           currentmap[1024];\r
-\r
-brush_t        active_brushes;         // brushes currently being displayed\r
-brush_t        selected_brushes;       // highlighted\r
-\r
-face_t *selected_face;\r
-brush_t        *selected_face_brush;\r
-\r
-brush_t        filtered_brushes;       // brushes that have been filtered or regioned\r
-\r
-entity_t       entities;               // head/tail of doubly linked list\r
-\r
-entity_t       *world_entity = NULL; // "classname" "worldspawn" !\r
-\r
-void Map_Init()\r
-{\r
-  Map_Free();\r
-}\r
-\r
-\r
-bool  g_bCancel_Map_LoadFile; // Hydra: moved this here\r
-\r
-// TTimo\r
-// need that in a variable, will have to tweak depending on the game\r
-int g_MaxWorldCoord = 64*1024;\r
-int g_MinWorldCoord = -64*1024;\r
-\r
-// the max size we allow on brushes, this is dependant on world coords too\r
-// makes more sense to say smaller I think?\r
-int g_MaxBrushSize = (g_MaxWorldCoord-1)*2;\r
-\r
-void AddRegionBrushes (void);\r
-void RemoveRegionBrushes (void);\r
-\r
-/*\r
-=============================================================\r
-\r
-  Cross map selection saving\r
-\r
-  this could fuck up if you have only part of a complex entity selected...\r
-=============================================================\r
-*/\r
-\r
-brush_t                between_brushes;\r
-entity_t       between_entities;\r
-\r
-bool g_bRestoreBetween = false;\r
-\r
-void Map_SaveBetween (void)\r
-{\r
-  if (g_pParentWnd->ActiveXY())\r
-  {\r
-    g_bRestoreBetween = true;\r
-    g_pParentWnd->ActiveXY()->Copy();\r
-  }\r
-  return;\r
-}\r
-\r
-void Map_RestoreBetween (void)\r
-{\r
-  if (g_pParentWnd->ActiveXY() && g_bRestoreBetween)\r
-    g_pParentWnd->ActiveXY()->Paste();\r
-}\r
-\r
-//============================================================================\r
-\r
-bool CheckForTinyBrush(brush_t* b, int n, float fSize)\r
-{\r
-  bool bTiny = false;\r
-       for (int i=0 ; i<3 ; i++)\r
-       {\r
-    if (b->maxs[i] - b->mins[i] < fSize)\r
-      bTiny = true;\r
-  }\r
-  if (bTiny)\r
-    Sys_Printf("Possible problem brush (too small) #%i ", n);\r
-  return bTiny;\r
-}\r
-\r
-void Map_BuildBrushData(void)\r
-{\r
-  brush_t *b, *next;\r
-\r
-  if (active_brushes.next == NULL)\r
-    return;\r
-\r
-  Sys_BeginWait ();    // this could take a while\r
-\r
-  int n = 0;\r
-  for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=next)\r
-  {\r
-    next = b->next;\r
-    Brush_Build( b, true, false, false );\r
-    if (!b->brush_faces || (g_PrefsDlg.m_bCleanTiny && CheckForTinyBrush(b, n++, g_PrefsDlg.m_fTinySize)))\r
-    {\r
-      Brush_Free (b);\r
-      Sys_Printf ("Removed degenerate brush\n");\r
-    }\r
-  }\r
-  Sys_EndWait();\r
-}\r
-\r
-entity_t *Map_FindClass (char *cname)\r
-{\r
-       entity_t        *ent;\r
-\r
-       for (ent = entities.next ; ent != &entities ; ent=ent->next)\r
-       {\r
-               if (!strcmp(cname, ValueForKey (ent, "classname")))\r
-                       return ent;\r
-       }\r
-       return NULL;\r
-}\r
-\r
-/*\r
-================\r
-Map_Free\r
-free all map elements, reinitialize the structures that depend on them\r
-================\r
-*/\r
-void Map_Free (void)\r
-{\r
-  g_bRestoreBetween = false;\r
-  if (selected_brushes.next &&\r
-      (selected_brushes.next != &selected_brushes))\r
-    {\r
-      if (gtk_MessageBox (g_pParentWnd->m_pWidget, "Copy selection?", " ", MB_YESNO) == IDYES)\r
-       Map_SaveBetween ();\r
-    }\r
-\r
-       QERApp_ActiveShaders_SetInUse( false );\r
-       Pointfile_Clear ();\r
-       g_qeglobals.d_num_entities = 0;\r
-\r
-       if (!active_brushes.next)\r
-       {\r
-         // first map\r
-         active_brushes.prev = active_brushes.next = &active_brushes;\r
-         selected_brushes.prev = selected_brushes.next = &selected_brushes;\r
-         filtered_brushes.prev = filtered_brushes.next = &filtered_brushes;\r
-         entities.prev = entities.next = &entities;\r
-       }\r
-       else\r
-       {\r
-         // free selected faces array\r
-         g_ptrSelectedFaces.RemoveAll();\r
-         g_ptrSelectedFaceBrushes.RemoveAll();\r
-               while (active_brushes.next != &active_brushes)\r
-                       Brush_Free (active_brushes.next);\r
-               while (selected_brushes.next != &selected_brushes)\r
-                       Brush_Free (selected_brushes.next);\r
-               while (filtered_brushes.next != &filtered_brushes)\r
-                       Brush_Free (filtered_brushes.next);\r
-               while (entities.next != &entities)\r
-                       Entity_Free (entities.next);\r
-       }\r
-\r
-  if (world_entity)\r
-    Entity_Free(world_entity);\r
-       world_entity = NULL;\r
-}\r
-\r
-entity_t *AngledEntity()\r
-{\r
-  entity_t *ent = Map_FindClass ("info_player_start");\r
-       if (!ent)\r
-  {\r
-               ent = Map_FindClass ("info_player_deathmatch");\r
-  }\r
-  if (!ent)\r
-  {\r
-               ent = Map_FindClass ("info_player_deathmatch");\r
-  }\r
-  if (!ent)\r
-  {\r
-    ent = Map_FindClass ("team_CTF_redplayer");\r
-  }\r
-  if (!ent)\r
-  {\r
-    ent = Map_FindClass ("team_CTF_blueplayer");\r
-  }\r
-  if (!ent)\r
-  {\r
-    ent = Map_FindClass ("team_CTF_redspawn");\r
-  }\r
-  if (!ent)\r
-  {\r
-    ent = Map_FindClass ("team_CTF_bluespawn");\r
-  }\r
-  return ent;\r
-}\r
-\r
-//\r
-// move the view to a start position\r
-//\r
-void Map_StartPosition()\r
-{\r
-  entity_t *ent = AngledEntity();\r
-\r
-  g_pParentWnd->GetCamWnd()->Camera()->angles[PITCH] = 0;\r
-  if (ent)\r
-  {\r
-    GetVectorForKey (ent, "origin", g_pParentWnd->GetCamWnd()->Camera()->origin);\r
-    GetVectorForKey (ent, "origin", g_pParentWnd->GetXYWnd()->GetOrigin());\r
-    g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = FloatForKey (ent, "angle");\r
-  }\r
-  else\r
-  {\r
-    g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = 0;\r
-    VectorCopy (vec3_origin, g_pParentWnd->GetCamWnd()->Camera()->origin);\r
-    VectorCopy (vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin());\r
-  }\r
-}\r
-\r
-void Map_FreeEntities(CPtrArray *ents)\r
-{\r
-  int i, j, num_ents, num_brushes;\r
-  entity_t* e;\r
-  CPtrArray* brushes;\r
-\r
-  num_ents = ents->GetSize();\r
-  for(i=0; i<num_ents; i++)\r
-  {\r
-    e = (entity_t*)ents->GetAt(i);\r
-    brushes = (CPtrArray*)e->pData;\r
-    num_brushes = brushes->GetSize();\r
-    for(j=0; j<num_brushes; j++)\r
-      Brush_Free((brush_t*)brushes->GetAt(j));\r
-    brushes->RemoveAll();\r
-    delete (CPtrArray*)e->pData;\r
-    e->pData = NULL;\r
-    Entity_Free(e);\r
-  }\r
-  ents->RemoveAll();\r
-}\r
-\r
-/*!\todo Possibly make the import Undo-friendly by calling Undo_End for new brushes and ents */\r
-void Map_ImportEntities(CPtrArray *ents, bool bAddSelected = false)\r
-{\r
-  int num_ents, num_brushes;\r
-  CPtrArray *brushes;\r
-  vec3_t mins, maxs;\r
-  entity_t *e;\r
-  brush_t *b;\r
-  face_t *f;\r
-  int i,j;\r
-\r
-  GPtrArray *new_ents = g_ptr_array_new();\r
-\r
-  g_qeglobals.bPrimitBrushes = false;\r
-\r
-  brush_t *pBrushList = (bAddSelected) ? &selected_brushes : &active_brushes;\r
-\r
-  bool bDoneBPCheck = false;\r
-  g_qeglobals.bNeedConvert = false;\r
-  // HACK: find out if this map file was a BP one\r
-  // check the first brush in the file that is NOT a patch\r
-  // this will not be necessary when we allow both formats in the same file\r
-  num_ents = ents->GetSize();\r
-  for(i=0; !bDoneBPCheck && i<num_ents; i++)\r
-  {\r
-    e = (entity_t*)ents->GetAt(i);\r
-    brushes = (CPtrArray*)e->pData;\r
-    num_brushes = brushes->GetSize();\r
-    for(j=0; !bDoneBPCheck && j<num_brushes; j++)\r
-    {\r
-      /*!todo Allow mixing texdef formats per-face. */\r
-      b = (brush_t *)brushes->GetAt(j);\r
-      if(b->patchBrush) continue;\r
-      bDoneBPCheck = true;\r
-      int BP_param = -1;\r
-      if(b->bBrushDef && !g_qeglobals.m_bBrushPrimitMode)\r
-        BP_param = 0;\r
-      else if(!b->bBrushDef && g_qeglobals.m_bBrushPrimitMode)\r
-        BP_param = 1;\r
-\r
-      if(BP_param != -1)\r
-      {\r
-        switch(BP_MessageBox(BP_param))\r
-        {\r
-        case 0:\r
-          Map_FreeEntities(ents);\r
-          return;\r
-        case 1:\r
-          g_qeglobals.bNeedConvert = true;\r
-          break;\r
-        case 2:\r
-          g_qeglobals.bNeedConvert = false;\r
-          break;\r
-        }\r
-      }\r
-    }\r
-  }\r
-\r
-  // process the entities into the world geometry\r
-  num_ents = ents->GetSize();\r
-  for(i=0; i<num_ents; i++)\r
-  {\r
-    num_brushes = 0;\r
-    e = (entity_t*)ents->GetAt(i);\r
-    brushes = (CPtrArray*)e->pData;\r
\r
-    num_brushes = brushes->GetSize();\r
-    // link brushes into entity\r
-    for(j=0; j<num_brushes; j++)\r
-    {\r
-      Entity_LinkBrush(e, (brush_t *)brushes->GetAt(j));\r
-      g_qeglobals.d_parsed_brushes++;\r
-    }\r
-    brushes->RemoveAll();\r
-    delete brushes;\r
-    e->pData = NULL;\r
-\r
-    // set entity origin\r
-    GetVectorForKey (e, "origin", e->origin);\r
-    // set entity eclass\r
-    /*!\todo Make SetKeyValue check for "classname" change and assign appropriate eclass */\r
-    e->eclass = Eclass_ForName (ValueForKey (e, "classname"),\r
-      (e->brushes.onext != &e->brushes));\r
-\r
-    // go through all parsed brushes and build stuff\r
-    for(b = e->brushes.onext; b!=&e->brushes; b=b->onext)\r
-    {\r
-      for(f = b->brush_faces; f != NULL; f = f->next)\r
-      {\r
-        f->pShader = QERApp_Shader_ForName(f->texdef.GetName());\r
-        f->d_texture = f->pShader->getTexture();\r
-      }\r
-\r
-      // when brushes are in final state, build the planes and windings\r
-      // NOTE: also converts BP brushes if g_qeglobals.bNeedConvert is true\r
-      Brush_Build(b);\r
-    }\r
-\r
-//#define TERRAIN_HACK\r
-#undef TERRAIN_HACK\r
-\r
-#ifdef TERRAIN_HACK\r
-    if ((strcmp(ValueForKey(e, "terrain"),"1") == 0 && strcmp(e->eclass->name,"func_group") == 0))\r
-    {\r
-      \r
-      // two aux pointers to the shaders used in the terrain entity\r
-      // we don't keep refcount on them since they are only temporary\r
-      // this avoids doing expensive lookups by name for all faces\r
-      IShader *pTerrainShader, *pCaulk;\r
-      \r
-      pTerrainShader = NULL;\r
-      pCaulk = QERApp_Shader_ForName(SHADER_CAULK);\r
-      \r
-      for(b = e->brushes.onext; b!=&e->brushes; b=b->onext)\r
-      {\r
-        if (pTerrainShader == NULL)\r
-          for(f = b->brush_faces; f != NULL; f = f->next)\r
-            if (strcmp(f->texdef.GetName(), SHADER_CAULK)!=0)\r
-              pTerrainShader = f->pShader;\r
-            \r
-            if (pTerrainShader)\r
-            {\r
-              for(f = b->brush_faces; f != NULL; f = f->next)\r
-              {\r
-                if (strcmp(f->texdef.GetName(), SHADER_CAULK)!=0) // not caulk\r
-                  Face_SetShader(f, pTerrainShader->getName());\r
-                else\r
-                  Face_SetShader(f, pCaulk->getName());\r
-              }\r
-            }\r
-            else\r
-              Sys_Printf("WARNING: no terrain shader found for brush\n");\r
-      }\r
-    }\r
-#endif\r
-\r
-#define PATCH_HACK\r
-#ifdef PATCH_HACK\r
-    for(b = e->brushes.onext; b!=&e->brushes; b=b->onext)\r
-    {\r
-      // patch hack, to be removed when dependency on brush_faces is removed\r
-      if (b->patchBrush)\r
-      {\r
-        Patch_CalcBounds(b->pPatch, mins, maxs);\r
-        for (int i=0; i<3; i++)\r
-        {\r
-          if ((int)mins[i] == (int)maxs[i])\r
-          {\r
-            mins[i] -= 4;\r
-            maxs[i] += 4;\r
-          }\r
-        }\r
-        Brush_Resize(b, mins, maxs);\r
-        Brush_Build(b);\r
-      }\r
-    }\r
-#endif\r
-    // add brush for fixedsize entity\r
-    if (e->eclass->fixedsize)\r
-    {\r
-      vec3_t mins, maxs;\r
-      VectorAdd (e->eclass->mins, e->origin, mins);\r
-      VectorAdd (e->eclass->maxs, e->origin, maxs);\r
-      b = Brush_Create (mins, maxs, &e->eclass->texdef);\r
-      Entity_LinkBrush(e, b);\r
-      Brush_Build(b);\r
-    }\r
-\r
-    for(b = e->brushes.onext; b!=&e->brushes; b=b->onext)\r
-      Brush_AddToList(b, pBrushList);\r
-\r
-    if (strcmp(e->eclass->name, "worldspawn") == 0)\r
-    {\r
-      if (world_entity)\r
-      {\r
-        while(e->brushes.onext != &e->brushes)\r
-        {\r
-          b = e->brushes.onext;\r
-          Entity_UnlinkBrush(b);\r
-          Entity_LinkBrush(world_entity, b);\r
-        }\r
-        Entity_Free(e);\r
-      }\r
-      else\r
-      {\r
-        world_entity = e;\r
-      }\r
-    }\r
-    else if (strcmp(e->eclass->name, "group_info") == 0)\r
-    {\r
-      // it's a group thing!\r
-      Group_Add(e);\r
-      Entity_Free(e);\r
-    }\r
-    else\r
-    {\r
-      // fix target/targetname collisions\r
-      if ((g_PrefsDlg.m_bDoTargetFix) && (strcmp(ValueForKey(e, "target"), "") != 0))\r
-      {\r
-        GPtrArray *t_ents = g_ptr_array_new();\r
-        entity_t *e_target;\r
-        const char *target = ValueForKey(e, "target");\r
-        qboolean bCollision=FALSE;\r
-        \r
-        // check the current map entities for an actual collision\r
-        for (e_target = entities.next; e_target != &entities; e_target = e_target->next)\r
-        {\r
-          if(!strcmp(target, ValueForKey(e_target, "target")))\r
-          {\r
-            bCollision = TRUE;\r
-            // make sure the collision is not between two imported entities\r
-            for(j=0; j<(int)new_ents->len; j++)\r
-            {\r
-              if(e_target == g_ptr_array_index(new_ents, j))\r
-                bCollision = FALSE;\r
-            }\r
-          }\r
-        }\r
-        \r
-        // find the matching targeted entity(s)\r
-        if(bCollision)\r
-        {\r
-          for(j=num_ents-1; j>0; j--)\r
-          {\r
-            e_target = (entity_t*)ents->GetAt(j);\r
-            if(e_target != NULL && e_target != e)\r
-            {\r
-              const char *targetname = ValueForKey(e_target, "targetname");\r
-              if( (targetname != NULL) && (strcmp(target, targetname) == 0) )\r
-                g_ptr_array_add(t_ents, (gpointer)e_target);\r
-            }\r
-          }\r
-          if(t_ents->len > 0)\r
-          {\r
-            // link the first to get a unique target/targetname\r
-            Entity_Connect(e, (entity_t*)g_ptr_array_index(t_ents,0));\r
-            // set the targetname of the rest of them manually\r
-            for(j = 1; j < (int)t_ents->len; j++)\r
-              SetKeyValue( (entity_t*)g_ptr_array_index(t_ents, j), "targetname", ValueForKey(e, "target") );\r
-          }\r
-          g_ptr_array_free(t_ents, FALSE);\r
-        }\r
-      }\r
-      \r
-      // add the entity to the end of the entity list\r
-      Entity_AddToList(e, &entities);\r
-      g_qeglobals.d_num_entities++;\r
-      \r
-      // keep a list of ents added to avoid testing collisions against them\r
-      g_ptr_array_add(new_ents, (gpointer)e);\r
-    }\r
-  }\r
-  g_ptr_array_free(new_ents, FALSE);\r
-  \r
-  ents->RemoveAll();\r
-\r
-  g_qeglobals.bNeedConvert = false;\r
-}\r
-\r
-void Map_Import(IDataStream *in, const char *type, bool bAddSelected)\r
-{\r
-  CPtrArray ents;\r
-\r
-  g_pParentWnd->GetSynapseClient().ImportMap(in, &ents, type);\r
-  Map_ImportEntities(&ents, bAddSelected);\r
-}\r
-\r
-/*\r
-================\r
-Map_LoadFile\r
-================\r
-*/\r
-void Map_LoadFile (const char *filename)\r
-{\r
-  clock_t start, finish;\r
-  double elapsed_time;\r
-  start = clock();\r
-\r
-  Sys_BeginWait ();\r
-  Select_Deselect();\r
-  /*!\r
-  \todo FIXME TTimo why is this commented out? \r
-  stability issues maybe? or duplicate feature?\r
-  forcing to show the console during map load was a good thing IMO\r
-  */\r
-  //SetInspectorMode(W_CONSOLE);\r
-  Sys_Printf ("Loading map from %s\n", filename );\r
-\r
-  Map_Free ();\r
-  //++timo FIXME: maybe even easier to have Group_Init called from Map_Free?\r
-  Group_Init();\r
-  g_qeglobals.d_num_entities = 0;\r
-  g_qeglobals.d_parsed_brushes = 0;\r
-\r
-\r
-  // cancel the map loading process\r
-  // used when conversion between standard map format and BP format is required and the user cancels the process\r
-  g_bCancel_Map_LoadFile = false;\r
-\r
-  strcpy (currentmap, filename);\r
-\r
-  g_bScreenUpdates = false; // leo: avoid redraws while loading the map (see fenris:1952)\r
-\r
-  // prepare to let the map module do the parsing\r
-  FileStream file;\r
-  const char* type = strrchr(filename,'.');\r
-  if(type!=NULL) type++;\r
-    // NOTE TTimo opening has binary doesn't make a lot of sense\r
-  // but opening as text confuses the scriptlib parser\r
-  // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=261\r
-  // this may be a problem if we "rb" and use the XML parser, might have an incompatibility\r
-  if (file.Open(filename, "rb"))\r
-    Map_Import(&file, type);\r
-  else\r
-    Sys_FPrintf(SYS_ERR, "ERROR: failed to open %s for read\n", filename);\r
-  file.Close();\r
-\r
-  g_bScreenUpdates = true;\r
-\r
-  if (g_bCancel_Map_LoadFile)\r
-  {\r
-    Sys_Printf("Map_LoadFile canceled\n");\r
-    Map_New();\r
-    Sys_EndWait();\r
-    return;\r
-  }\r
-\r
-  if (!world_entity)\r
-  {\r
-    Sys_Printf ("No worldspawn in map.\n");\r
-    Map_New ();\r
-    Sys_EndWait();\r
-    return;\r
-  }\r
-  finish = clock();\r
-  elapsed_time = (double)(finish - start) / CLOCKS_PER_SEC;\r
-\r
-  Sys_Printf ("--- LoadMapFile ---\n");\r
-  Sys_Printf ("%s\n", filename );\r
-  \r
-  Sys_Printf ("%5i brushes\n",  g_qeglobals.d_parsed_brushes );\r
-  Sys_Printf ("%5i entities\n", g_qeglobals.d_num_entities);\r
-  Sys_Printf ("%5.2f second(s) load time\n", elapsed_time );\r
-  \r
-  Sys_EndWait();\r
-  \r
-  Map_RestoreBetween ();\r
-  \r
-  //\r
-  // move the view to a start position\r
-  //\r
-  Map_StartPosition();\r
-\r
-  Map_RegionOff ();\r
-\r
-  modified = false;\r
-  Sys_SetTitle (filename);\r
-\r
-  Texture_ShowInuse ();\r
-  QERApp_SortActiveShaders();\r
-\r
-  Sys_UpdateWindows (W_ALL);\r
-}\r
-\r
-/*!\r
-===========\r
-Supporting functions for Map_SaveFile, builds a CPtrArray with the filtered / non filtered brushes\r
-===========\r
-*/\r
-void CleanFilter(entity_t *ent)\r
-{\r
-  if (ent->pData)\r
-  {\r
-    delete static_cast<CPtrArray*>(ent->pData);\r
-    ent->pData = NULL;\r
-  }\r
-}\r
-\r
-/*!\r
-filters out the region brushes if necessary\r
-returns true if this entity as a whole is out of the region\r
-(if all brushes are filtered out, then the entity will be completely dropped .. except if it's worldspawn of course)\r
-*/\r
-bool FilterChildren(entity_t *ent, bool bRegionOnly = false, bool bSelectedOnly = false)\r
-{\r
-  if(ent->brushes.onext == &ent->brushes)\r
-    return false;\r
-  // entity without a brush, ignore it... this can be caused by Undo\r
-\r
-  // filter fixedsize ents by their eclass bounding box\r
-  // don't add their brushes\r
-  if (ent->eclass->fixedsize)\r
-  {\r
-    if(bSelectedOnly && !IsBrushSelected(ent->brushes.onext))\r
-      return false;\r
-\r
-    if(bRegionOnly && region_active)\r
-    {\r
-      for (int i=0 ; i<3 ; i++)\r
-           {\r
-                   if ((ent->origin[i] + ent->eclass->mins[i]) > region_maxs[i])\r
-                           return false;\r
-                   if ((ent->origin[i] + ent->eclass->maxs[i]) < region_mins[i])\r
-                           return false;\r
-           }\r
-    }\r
-  }\r
-  else\r
-  {\r
-    for (brush_t *b = ent->brushes.onext ; b != &ent->brushes ; b=b->onext)\r
-    {\r
-      // set flag to use brushprimit_texdef\r
-      if(g_qeglobals.m_bBrushPrimitMode)\r
-        b->bBrushDef = true;\r
-      else\r
-        b->bBrushDef = false;\r
-\r
-      // add brush, unless it's excluded by region\r
-      if ( !(bRegionOnly && Map_IsBrushFiltered(b)) &&\r
-           !(bSelectedOnly && !IsBrushSelected(b)) )\r
-        ((CPtrArray*)ent->pData)->Add(b);\r
-    }\r
-\r
-    if (((CPtrArray*)ent->pData)->GetSize() <= 0)\r
-      return false;\r
-  }\r
-  return true;\r
-}\r
-\r
-entity_t *region_startpoint = NULL;\r
-void Map_ExportEntities(CPtrArray* ents, bool bRegionOnly = false, bool bSelectedOnly = false)\r
-{\r
-  int i;\r
-  entity_t *e;\r
-\r
-  /*!\r
-  \todo the entity_t needs to be reworked and asbtracted some more\r
-  \r
-  keeping the entity_t as the struct providing access to a list of map objects, a list of epairs and various other info?\r
-  but separating some more the data that belongs to the entity_t and the 'sons' data\r
-  on a side note, I don't think that doing that with linked list would be a good thing\r
-  \r
-  for now, we use the blind void* in entity_t casted to a CPtrArray of brush_t* to hand out a list of the brushes for map write\r
-  the next step is very likely to be a change of the brush_t* to a more abstract object?\r
-  */\r
-\r
-  FilterChildren(world_entity, bRegionOnly, bSelectedOnly);\r
-  ents->Add(world_entity);\r
-\r
-  for (e=entities.next ; e!=&entities ; e=e->next)\r
-  {\r
-    // not sure this still happens, probably safe to leave it in\r
-    if ((!strcmp(ValueForKey (e, "classname"), "worldspawn")) && (e!=world_entity))\r
-    {\r
-      Sys_FPrintf(SYS_ERR, "Dropping parasite worldspawn entity\n");\r
-      continue;\r
-    }\r
-\r
-    // entities which brushes are completely filtered out by regioning are not printed to the map\r
-    if (FilterChildren(e, bRegionOnly, bSelectedOnly))\r
-      ents->Add(e);\r
-  }\r
-\r
-  if (bRegionOnly && region_active)\r
-  {\r
-    for(i=0; i<6; i++)\r
-      ((CPtrArray*)world_entity->pData)->Add(region_sides[i]);\r
-\r
-    ents->Add(region_startpoint);\r
-  }\r
-}\r
-\r
-void Map_Export(IDataStream *out, const char *type, bool bRegionOnly, bool bSelectedOnly)\r
-{\r
-  entity_t     *e;\r
-\r
-  CPtrArray ents;\r
-  \r
-  if (bRegionOnly && region_active)\r
-    AddRegionBrushes();\r
-\r
-  // create the filters\r
-  world_entity->pData = new CPtrArray();\r
-  for(e = entities.next; e != &entities; e = e->next)\r
-    e->pData = new CPtrArray();\r
-\r
-  Map_ExportEntities(&ents, bRegionOnly, bSelectedOnly);\r
-\r
-  g_pParentWnd->GetSynapseClient().ExportMap(&ents, out, type);\r
-\r
-  // cleanup the filters\r
-  CleanFilter(world_entity);\r
-  for (e=entities.next ; e!=&entities ; e=e->next)\r
-    CleanFilter(e);\r
-\r
-  if (bRegionOnly && region_active)\r
-    RemoveRegionBrushes();\r
-}\r
-\r
-const char* filename_get_extension(const char* filename)\r
-{\r
-  const char* type = strrchr(filename,'.');\r
-  if(type != NULL)\r
-    return ++type;\r
-  return "";\r
-}\r
-\r
-/*\r
-===========\r
-Map_SaveFile\r
-\todo FIXME remove the use_region, this is broken .. work with a global flag to set region mode or not\r
-===========\r
-*/\r
-void Map_SaveFile (const char *filename, qboolean use_region )\r
-{\r
-  clock_t start, finish;\r
-  double elapsed_time;\r
-  start = clock();\r
-  Sys_Printf("Saving map to %s\n",filename);\r
-\r
-       Pointfile_Clear ();\r
-\r
-       if (!use_region)\r
-       {\r
-               char    backup[1024];\r
-\r
-               // rename current to .bak\r
-               strcpy (backup, filename);\r
-               StripExtension (backup);\r
-               strcat (backup, ".bak");\r
-               unlink (backup);\r
-               rename (filename, backup);\r
-       }\r
-\r
-       Sys_Printf ("Map_SaveFile: %s\n", filename);\r
-\r
-  // build the out data stream\r
-  FileStream file;\r
-  if (!file.Open(filename,"w"))\r
-  {\r
-    Sys_FPrintf(SYS_ERR, "ERROR: couldn't open %s for write\n", filename);\r
-    return;\r
-  }\r
-\r
-  // extract filetype\r
-  Map_Export(&file, filename_get_extension(filename), use_region);\r
-\r
-  file.Close();\r
-\r
-  finish = clock();\r
-  elapsed_time = (double)(finish - start) / CLOCKS_PER_SEC;\r
-\r
-  Sys_Printf ("Saved in %-.2f second(s).\n",elapsed_time);\r
-       modified = false;\r
-\r
-       if ( !strstr( filename, "autosave" ) )\r
-               Sys_SetTitle (filename);\r
-\r
-       if (!use_region)\r
-       {\r
-               time_t  timer;\r
-\r
-               time (&timer);\r
-\r
-    Sys_Beep ();\r
-\r
-               Sys_Status ("Saved.", 0);\r
-       }\r
-}\r
-\r
-/*\r
-===========\r
-Map_New\r
-\r
-===========\r
-*/\r
-void Map_New (void)\r
-{\r
-       Sys_Printf ("Map_New\n");\r
-       Map_Free ();\r
-\r
-       strcpy (currentmap, "unnamed.map");\r
-       Sys_SetTitle (currentmap);\r
-\r
-  world_entity = (entity_s*)qmalloc(sizeof(*world_entity));\r
-       world_entity->brushes.onext = \r
-               world_entity->brushes.oprev = &world_entity->brushes;\r
-       SetKeyValue (world_entity, "classname", "worldspawn");\r
-       world_entity->eclass = Eclass_ForName ("worldspawn", true);\r
-\r
-       g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = 0;\r
-       g_pParentWnd->GetCamWnd()->Camera()->angles[PITCH] = 0;\r
-       VectorCopy (vec3_origin, g_pParentWnd->GetCamWnd()->Camera()->origin);\r
-       g_pParentWnd->GetCamWnd()->Camera()->origin[2] = 48;\r
-       VectorCopy (vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin());\r
-\r
-       Map_RestoreBetween ();\r
-\r
-  Group_Init();\r
-\r
-       Sys_UpdateWindows (W_ALL);\r
-       modified = false;\r
-}\r
-\r
-/*\r
-===========================================================\r
-\r
-  REGION\r
-\r
-===========================================================\r
-*/\r
-qboolean       region_active;\r
-vec3_t region_mins = {g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord};\r
-vec3_t region_maxs = {g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord};\r
-\r
-brush_t        *region_sides[6];\r
-\r
-/*\r
-===========\r
-AddRegionBrushes\r
-a regioned map will have temp walls put up at the region boundary\r
-\todo TODO TTimo old implementation of region brushes\r
-  we still add them straight in the worldspawn and take them out after the map is saved\r
-  with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module\r
-===========\r
-*/\r
-void AddRegionBrushes (void)\r
-{\r
-       vec3_t  mins, maxs;\r
-       int             i;\r
-       texdef_t        td;\r
-\r
-       if (!region_active)\r
-  {\r
-#ifdef _DEBUG\r
-    Sys_FPrintf( SYS_WRN, "Unexpected AddRegionBrushes call.\n");\r
-#endif\r
-               return;\r
-  }\r
-\r
-       memset (&td, 0, sizeof(td));\r
-       td.SetName(SHADER_NOT_FOUND);\r
-\r
-  // set mins\r
-  VectorSet(mins, region_mins[0]-32, region_mins[1]-32, region_mins[2]-32);\r
-\r
-  // vary maxs\r
-  for(i=0; i<3; i++)\r
-  {\r
-    VectorSet(maxs, region_maxs[0]+32, region_maxs[1]+32, region_maxs[2]+32);\r
-    maxs[i] = region_mins[i];\r
-    region_sides[i] = Brush_Create (mins, maxs, &td);\r
-  }\r
-\r
-  // set maxs\r
-  VectorSet(maxs, region_maxs[0]+32, region_maxs[1]+32, region_maxs[2]+32);\r
-\r
-  // vary mins\r
-  for(i=0; i<3; i++)\r
-  {\r
-    VectorSet(mins, region_mins[0]-32, region_mins[1]-32, region_mins[2]-32);\r
-    mins[i] = region_maxs[i];\r
-    region_sides[i+3] = Brush_Create (mins, maxs, &td);\r
-  }\r
-\r
-  \r
-  // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=503\r
-  // this is a safe check, but it should not really happen anymore  \r
-  vec3_t vOrig;\r
-  VectorSet(vOrig,\r
-    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[0],\r
-    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[1], \r
-    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[2]);\r
-\r
-  for (i=0 ; i<3 ; i++)\r
-  {\r
-    if (vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i])\r
-    {\r
-      Sys_FPrintf(SYS_ERR, "Camera is NOT in the region, it's likely that the region won't compile correctly\n");\r
-    }\r
-  }\r
-  \r
-  // write the info_playerstart\r
-  region_startpoint = Entity_Alloc();\r
-  SetKeyValue(region_startpoint, "classname", "info_player_start");\r
-  region_startpoint->eclass = Eclass_ForName ("info_player_start", false);\r
-  char sTmp[1024];\r
-  sprintf(sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2]);\r
-  SetKeyValue(region_startpoint, "origin", sTmp);\r
-  sprintf(sTmp, "%d", (int)g_pParentWnd->GetCamWnd()->Camera()->angles[YAW]);\r
-  SetKeyValue(region_startpoint, "angle", sTmp);\r
-  // empty array of children\r
-  region_startpoint->pData = new CPtrArray;\r
-}\r
-\r
-void RemoveRegionBrushes (void)\r
-{\r
-       int             i;\r
-\r
-       if (!region_active)\r
-               return;\r
-       for (i=0 ; i<6 ; i++)\r
-               Brush_Free (region_sides[i]);\r
-\r
-  CleanFilter(region_startpoint);\r
-  Entity_Free(region_startpoint);\r
-}\r
-\r
-qboolean Map_IsBrushFiltered (brush_t *b)\r
-{\r
-       int             i;\r
-\r
-       for (i=0 ; i<3 ; i++)\r
-       {\r
-               if (b->mins[i] > region_maxs[i])\r
-                       return true;\r
-               if (b->maxs[i] < region_mins[i])\r
-                       return true;\r
-       }\r
-       return false;\r
-}\r
-\r
-/*\r
-===========\r
-Map_RegionOff\r
-\r
-Other filtering options may still be on\r
-===========\r
-*/\r
-void Map_RegionOff (void)\r
-{\r
-       brush_t *b, *next;\r
-       int                     i;\r
-\r
-       region_active = false;\r
-       for (i=0 ; i<3 ; i++)\r
-       {\r
-               region_maxs[i] = g_MaxWorldCoord-64;\r
-               region_mins[i] = g_MinWorldCoord+64;\r
-       }\r
-       \r
-       for (b=filtered_brushes.next ; b != &filtered_brushes ; b=next)\r
-       {\r
-               next = b->next;\r
-               if (Map_IsBrushFiltered (b))\r
-                       continue;               // still filtered\r
-               Brush_RemoveFromList (b);\r
-               if (active_brushes.next == NULL || active_brushes.prev == NULL)\r
-               {\r
-                       active_brushes.next = &active_brushes;\r
-                       active_brushes.prev = &active_brushes;\r
-               }\r
-               Brush_AddToList (b, &active_brushes);\r
-               b->bFiltered = FilterBrush(b);\r
-       }\r
-       Sys_UpdateWindows (W_ALL);\r
-}\r
-\r
-void Map_ApplyRegion (void)\r
-{\r
-       brush_t *b, *next;\r
-\r
-       region_active = true;\r
-       for (b=active_brushes.next ; b != &active_brushes ; b=next)\r
-       {\r
-               next = b->next;\r
-               if (!Map_IsBrushFiltered (b))\r
-                       continue;               // still filtered\r
-               Brush_RemoveFromList (b);\r
-               Brush_AddToList (b, &filtered_brushes);\r
-       }\r
-\r
-       Sys_UpdateWindows (W_ALL);\r
-}\r
-\r
-\r
-/*\r
-========================\r
-Map_RegionSelectedBrushes\r
-========================\r
-*/\r
-void Map_RegionSelectedBrushes (void)\r
-{\r
-       Map_RegionOff ();\r
-\r
-       if (selected_brushes.next == &selected_brushes)  // nothing selected\r
-  {\r
-    Sys_Printf("Tried to region with no selection...\n");\r
-    return;\r
-  }\r
-       region_active = true;\r
-       Select_GetBounds (region_mins, region_maxs);\r
-\r
-#ifdef _DEBUG\r
-       if (filtered_brushes.next != &filtered_brushes)\r
-               Sys_Printf("WARNING: filtered_brushes list may not be empty in Map_RegionSelectedBrushes\n");\r
-#endif\r
-\r
-       if (active_brushes.next == &active_brushes)\r
-       {\r
-               // just have an empty filtered_brushes list\r
-               // this happens if you set region after selecting all the brushes in your map (some weird people do that, ask MrE!)\r
-               filtered_brushes.next = filtered_brushes.prev = &filtered_brushes;\r
-       }\r
-       else\r
-       {\r
-               // move the entire active_brushes list to filtered_brushes\r
-               filtered_brushes.next = active_brushes.next;\r
-               filtered_brushes.prev = active_brushes.prev;\r
-               filtered_brushes.next->prev = &filtered_brushes;\r
-               filtered_brushes.prev->next = &filtered_brushes;\r
-       }\r
-\r
-       // move the entire selected_brushes list to active_brushes\r
-       active_brushes.next = selected_brushes.next;\r
-       active_brushes.prev = selected_brushes.prev;\r
-       active_brushes.next->prev = &active_brushes;\r
-       active_brushes.prev->next = &active_brushes;\r
-\r
-       // deselect patches\r
-       for (brush_t *b = active_brushes.next; b != &active_brushes; b = b->next)\r
-          if (b->patchBrush)\r
-            b->pPatch->bSelected = false;\r
-\r
-       // clear selected_brushes\r
-       selected_brushes.next = selected_brushes.prev = &selected_brushes;\r
-\r
-       Sys_UpdateWindows (W_ALL);\r
-}\r
-\r
-\r
-/*\r
-===========\r
-Map_RegionXY\r
-===========\r
-*/\r
-void Map_RegionXY (void)\r
-{\r
-       Map_RegionOff ();\r
-\r
-       region_mins[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale();\r
-       region_maxs[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale();\r
-       region_mins[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale();\r
-       region_maxs[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale();\r
-       region_mins[2] = g_MinWorldCoord+64;\r
-       region_maxs[2] = g_MaxWorldCoord-64;\r
-       Map_ApplyRegion ();\r
-}\r
-\r
-/*\r
-===========\r
-Map_RegionTallBrush\r
-===========\r
-*/\r
-void Map_RegionTallBrush (void)\r
-{\r
-  brush_t *b;\r
-\r
-  if (!QE_SingleBrush ())\r
-    return;\r
-\r
-  b = selected_brushes.next;\r
-\r
-  Map_RegionOff ();\r
-\r
-  VectorCopy (b->mins, region_mins);\r
-  VectorCopy (b->maxs, region_maxs);\r
-  region_mins[2] = g_MinWorldCoord+64;\r
-  region_maxs[2] = g_MaxWorldCoord-64;\r
-\r
-  Undo_Start("delete");\r
-  Undo_AddBrushList(&selected_brushes);\r
-  Undo_AddEntity(b->owner);\r
-  Select_Delete ();\r
-  Undo_EndBrushList(&selected_brushes);\r
-  Undo_End();\r
-\r
-  Map_ApplyRegion ();\r
-}\r
-\r
-/*\r
-===========\r
-Map_RegionBrush\r
-===========\r
-*/\r
-void Map_RegionBrush (void)\r
-{\r
-  brush_t *b;\r
-\r
-  if (!QE_SingleBrush ())\r
-    return;\r
-\r
-  b = selected_brushes.next;\r
-\r
-  Map_RegionOff ();\r
-\r
-  VectorCopy (b->mins, region_mins);\r
-  VectorCopy (b->maxs, region_maxs);\r
-\r
-  Undo_Start("delete");\r
-  Undo_AddBrushList(&selected_brushes);\r
-  Undo_AddEntity(b->owner);\r
-  Select_Delete ();\r
-  Undo_EndBrushList(&selected_brushes);\r
-  Undo_End();\r
-\r
-  Map_ApplyRegion ();\r
-}\r
-\r
-GList *find_string(GList *glist, const char *buf)\r
-{\r
-  while (glist)\r
-  {\r
-    if (strcmp((char *)glist->data, buf) == 0)\r
-      break; // this name is in our list already\r
-    glist = glist->next;\r
-  }\r
-  return glist;\r
-}\r
-\r
-void Map_ImportBuffer(char *buf)\r
-{\r
-  Select_Deselect();\r
-  \r
-  Undo_Start("import buffer");\r
-\r
-  MemStream stream;\r
-\r
-  stream.Write(buf, strlen(buf));\r
-  Map_Import(&stream, "xmap");\r
-  stream.Close();\r
-  \r
-  Sys_UpdateWindows (W_ALL);\r
-  Sys_MarkMapModified();\r
-  \r
-  Undo_End();\r
-}\r
-\r
-\r
-//\r
-//================\r
-//Map_ImportFile\r
-//================\r
-//\r
-void Map_ImportFile (const char *filename)\r
-{\r
-  FileStream file;\r
-       Sys_BeginWait ();\r
-\r
-  Sys_Printf("Importing map from %s\n",filename);\r
-\r
-  const char* type = strrchr(filename,'.');\r
-  if(type!=NULL) type++;\r
-  /*!\todo Resolve "r" problem in scriptlib" */\r
-  if(file.Open(filename, "rb"))\r
-    Map_Import(&file, type, true);\r
-  else\r
-    Sys_FPrintf(SYS_ERR, "ERROR: couldn't open %s for read\n", filename);\r
-\r
-  file.Close();\r
-\r
-       Sys_UpdateWindows (W_ALL);\r
-       modified = true;\r
-       Sys_EndWait();\r
-}\r
-\r
-//\r
-//===========\r
-//Map_SaveSelected\r
-//===========\r
-//\r
-// Saves selected world brushes and whole entities with partial/full selections\r
-//\r
-void Map_SaveSelected(const char* filename)\r
-{\r
-  FileStream file;\r
-\r
-  Sys_Printf("Saving selection to %s\n",filename);\r
-\r
-  const char* type = strrchr(filename,'.');\r
-  if(type!=NULL) type++;\r
-  if(file.Open(filename, "w"))\r
-    Map_Export (&file, type, false, true);\r
-  else\r
-    Sys_FPrintf(SYS_ERR, "ERROR: failed to open %s for write\n", filename);\r
-\r
-  file.Close();\r
-\r
-}\r
-\r
-//\r
-//===========\r
-//Map_SaveSelected\r
-//===========\r
-//\r
-// Saves selected world brushes and whole entities with partial/full selections\r
-//\r
-void Map_SaveSelected (MemStream* pMemFile, MemStream* pPatchFile)\r
-{\r
-  Map_Export (pMemFile, "xmap", false, true);\r
-\r
- /*\r
-       // write world entity first\r
-       Entity_WriteSelected(world_entity, pMemFile);\r
-\r
-       // then write all other ents\r
-       count = 1;\r
-       for (e=entities.next ; e != &entities ; e=next)\r
-       {\r
-               MemFile_fprintf(pMemFile, "// entity %i\n", count);\r
-               count++;\r
-               Entity_WriteSelected(e, pMemFile);\r
-               next = e->next;\r
-       }\r
-\r
-  //if (pPatchFile)\r
-  //  Patch_WriteFile(pPatchFile);\r
-  */\r
-}\r
-\r
-\r
-void MemFile_fprintf(MemStream* pMemFile, const char* pText, ...)\r
-{\r
-  char Buffer[4096];\r
-  va_list args;\r
-       va_start (args,pText);\r
-  vsprintf(Buffer, pText, args);\r
-  pMemFile->Write(Buffer, strlen(Buffer));\r
-}\r
-\r
-/*!\r
-==============\r
-Region_SpawnPoint\r
-push the region spawn point\r
-\todo FIXME TTimo this was in the #1 MAP module implementation (in the core)\r
-not sure it has any use anymore, should prolly drop it\r
-==============\r
-*/\r
-void Region_SpawnPoint(FILE *f)\r
-{\r
-  // write the info_player_start, we use the camera position\r
-  fprintf (f, "{\n");\r
-  fprintf (f, "\"classname\" \"info_player_start\"\n");\r
-  fprintf (f, "\"origin\" \"%i %i %i\"\n", \r
-    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[0],\r
-    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[1], \r
-    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[2]);\r
-  fprintf (f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamWnd()->Camera()->angles[YAW]);\r
-  fprintf (f, "}\n");\r
-}\r
+/*
+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
+*/
+
+#include "stdafx.h"
+#include <string.h>
+#if defined (__linux__) || defined (__APPLE__)
+#include <unistd.h>
+#endif
+#include "preferences.h"
+#include "mainframe.h"
+#include "gtkmisc.h"
+#include "filters.h"
+
+extern MainFrame* g_pParentWnd;
+
+int    modified;       // for quit confirmation (0 = clean, 1 = unsaved,
+                               // 2 = autosaved, but not regular saved) 
+
+char           currentmap[1024];
+
+brush_t        active_brushes;         // brushes currently being displayed
+brush_t        selected_brushes;       // highlighted
+
+face_t *selected_face;
+brush_t        *selected_face_brush;
+
+brush_t        filtered_brushes;       // brushes that have been filtered or regioned
+
+entity_t       entities;               // head/tail of doubly linked list
+
+entity_t       *world_entity = NULL; // "classname" "worldspawn" !
+
+void Map_Init()
+{
+  Map_Free();
+}
+
+
+bool  g_bCancel_Map_LoadFile; // Hydra: moved this here
+
+// TTimo
+// need that in a variable, will have to tweak depending on the game
+int g_MaxWorldCoord = 64*1024;
+int g_MinWorldCoord = -64*1024;
+
+// the max size we allow on brushes, this is dependant on world coords too
+// makes more sense to say smaller I think?
+int g_MaxBrushSize = (g_MaxWorldCoord-1)*2;
+
+void AddRegionBrushes (void);
+void RemoveRegionBrushes (void);
+
+/*
+=============================================================
+
+  Cross map selection saving
+
+  this could fuck up if you have only part of a complex entity selected...
+=============================================================
+*/
+
+brush_t                between_brushes;
+entity_t       between_entities;
+
+bool g_bRestoreBetween = false;
+
+void Map_SaveBetween (void)
+{
+  if (g_pParentWnd->ActiveXY())
+  {
+    g_bRestoreBetween = true;
+    g_pParentWnd->ActiveXY()->Copy();
+  }
+  return;
+}
+
+void Map_RestoreBetween (void)
+{
+  if (g_pParentWnd->ActiveXY() && g_bRestoreBetween)
+    g_pParentWnd->ActiveXY()->Paste();
+}
+
+//============================================================================
+
+bool CheckForTinyBrush(brush_t* b, int n, float fSize)
+{
+  bool bTiny = false;
+       for (int i=0 ; i<3 ; i++)
+       {
+    if (b->maxs[i] - b->mins[i] < fSize)
+      bTiny = true;
+  }
+  if (bTiny)
+    Sys_Printf("Possible problem brush (too small) #%i ", n);
+  return bTiny;
+}
+
+void Map_BuildBrushData(void)
+{
+  brush_t *b, *next;
+
+  if (active_brushes.next == NULL)
+    return;
+
+  Sys_BeginWait ();    // this could take a while
+
+  int n = 0;
+  for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=next)
+  {
+    next = b->next;
+    Brush_Build( b, true, false, false );
+    if (!b->brush_faces || (g_PrefsDlg.m_bCleanTiny && CheckForTinyBrush(b, n++, g_PrefsDlg.m_fTinySize)))
+    {
+      Brush_Free (b);
+      Sys_Printf ("Removed degenerate brush\n");
+    }
+  }
+  Sys_EndWait();
+}
+
+entity_t *Map_FindClass (char *cname)
+{
+       entity_t        *ent;
+
+       for (ent = entities.next ; ent != &entities ; ent=ent->next)
+       {
+               if (!strcmp(cname, ValueForKey (ent, "classname")))
+                       return ent;
+       }
+       return NULL;
+}
+
+/*
+================
+Map_Free
+free all map elements, reinitialize the structures that depend on them
+================
+*/
+void Map_Free (void)
+{
+  g_bRestoreBetween = false;
+  if (selected_brushes.next &&
+      (selected_brushes.next != &selected_brushes))
+    {
+      if (gtk_MessageBox (g_pParentWnd->m_pWidget, "Copy selection?", " ", MB_YESNO) == IDYES)
+       Map_SaveBetween ();
+    }
+
+       QERApp_ActiveShaders_SetInUse( false );
+       Pointfile_Clear ();
+       g_qeglobals.d_num_entities = 0;
+
+       if (!active_brushes.next)
+       {
+         // first map
+         active_brushes.prev = active_brushes.next = &active_brushes;
+         selected_brushes.prev = selected_brushes.next = &selected_brushes;
+         filtered_brushes.prev = filtered_brushes.next = &filtered_brushes;
+         entities.prev = entities.next = &entities;
+       }
+       else
+       {
+         // free selected faces array
+         g_ptrSelectedFaces.RemoveAll();
+         g_ptrSelectedFaceBrushes.RemoveAll();
+               while (active_brushes.next != &active_brushes)
+                       Brush_Free (active_brushes.next);
+               while (selected_brushes.next != &selected_brushes)
+                       Brush_Free (selected_brushes.next);
+               while (filtered_brushes.next != &filtered_brushes)
+                       Brush_Free (filtered_brushes.next);
+               while (entities.next != &entities)
+                       Entity_Free (entities.next);
+       }
+
+  if (world_entity)
+    Entity_Free(world_entity);
+       world_entity = NULL;
+}
+
+entity_t *AngledEntity()
+{
+  entity_t *ent = Map_FindClass ("info_player_start");
+       if (!ent)
+  {
+               ent = Map_FindClass ("info_player_deathmatch");
+  }
+  if (!ent)
+  {
+               ent = Map_FindClass ("info_player_deathmatch");
+  }
+  if (!ent)
+  {
+    ent = Map_FindClass ("team_CTF_redplayer");
+  }
+  if (!ent)
+  {
+    ent = Map_FindClass ("team_CTF_blueplayer");
+  }
+  if (!ent)
+  {
+    ent = Map_FindClass ("team_CTF_redspawn");
+  }
+  if (!ent)
+  {
+    ent = Map_FindClass ("team_CTF_bluespawn");
+  }
+  return ent;
+}
+
+//
+// move the view to a start position
+//
+void Map_StartPosition()
+{
+  entity_t *ent = AngledEntity();
+
+  g_pParentWnd->GetCamWnd()->Camera()->angles[PITCH] = 0;
+  if (ent)
+  {
+    GetVectorForKey (ent, "origin", g_pParentWnd->GetCamWnd()->Camera()->origin);
+    GetVectorForKey (ent, "origin", g_pParentWnd->GetXYWnd()->GetOrigin());
+    g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = FloatForKey (ent, "angle");
+  }
+  else
+  {
+    g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = 0;
+    VectorCopy (vec3_origin, g_pParentWnd->GetCamWnd()->Camera()->origin);
+    VectorCopy (vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin());
+  }
+}
+
+void Map_FreeEntities(CPtrArray *ents)
+{
+  int i, j, num_ents, num_brushes;
+  entity_t* e;
+  CPtrArray* brushes;
+
+  num_ents = ents->GetSize();
+  for(i=0; i<num_ents; i++)
+  {
+    e = (entity_t*)ents->GetAt(i);
+    brushes = (CPtrArray*)e->pData;
+    num_brushes = brushes->GetSize();
+    for(j=0; j<num_brushes; j++)
+      Brush_Free((brush_t*)brushes->GetAt(j));
+    brushes->RemoveAll();
+    delete (CPtrArray*)e->pData;
+    e->pData = NULL;
+    Entity_Free(e);
+  }
+  ents->RemoveAll();
+}
+
+/*!\todo Possibly make the import Undo-friendly by calling Undo_End for new brushes and ents */
+void Map_ImportEntities(CPtrArray *ents, bool bAddSelected = false)
+{
+  int num_ents, num_brushes;
+  CPtrArray *brushes;
+  vec3_t mins, maxs;
+  entity_t *e;
+  brush_t *b;
+  face_t *f;
+  int i,j;
+
+  GPtrArray *new_ents = g_ptr_array_new();
+
+  g_qeglobals.bPrimitBrushes = false;
+
+  brush_t *pBrushList = (bAddSelected) ? &selected_brushes : &active_brushes;
+
+  bool bDoneBPCheck = false;
+  g_qeglobals.bNeedConvert = false;
+  // HACK: find out if this map file was a BP one
+  // check the first brush in the file that is NOT a patch
+  // this will not be necessary when we allow both formats in the same file
+  num_ents = ents->GetSize();
+  for(i=0; !bDoneBPCheck && i<num_ents; i++)
+  {
+    e = (entity_t*)ents->GetAt(i);
+    brushes = (CPtrArray*)e->pData;
+    num_brushes = brushes->GetSize();
+    for(j=0; !bDoneBPCheck && j<num_brushes; j++)
+    {
+      /*!todo Allow mixing texdef formats per-face. */
+      b = (brush_t *)brushes->GetAt(j);
+      if(b->patchBrush) continue;
+      bDoneBPCheck = true;
+      int BP_param = -1;
+      if(b->bBrushDef && !g_qeglobals.m_bBrushPrimitMode)
+        BP_param = 0;
+      else if(!b->bBrushDef && g_qeglobals.m_bBrushPrimitMode)
+        BP_param = 1;
+
+      if(BP_param != -1)
+      {
+        switch(BP_MessageBox(BP_param))
+        {
+        case 0:
+          Map_FreeEntities(ents);
+          return;
+        case 1:
+          g_qeglobals.bNeedConvert = true;
+          break;
+        case 2:
+          g_qeglobals.bNeedConvert = false;
+          break;
+        }
+      }
+    }
+  }
+
+  // process the entities into the world geometry
+  num_ents = ents->GetSize();
+  for(i=0; i<num_ents; i++)
+  {
+    num_brushes = 0;
+    e = (entity_t*)ents->GetAt(i);
+    brushes = (CPtrArray*)e->pData;
+    num_brushes = brushes->GetSize();
+    // link brushes into entity
+    for(j=0; j<num_brushes; j++)
+    {
+      Entity_LinkBrush(e, (brush_t *)brushes->GetAt(j));
+      g_qeglobals.d_parsed_brushes++;
+    }
+    brushes->RemoveAll();
+    delete brushes;
+    e->pData = NULL;
+
+    // set entity origin
+    GetVectorForKey (e, "origin", e->origin);
+    // set entity eclass
+    /*!\todo Make SetKeyValue check for "classname" change and assign appropriate eclass */
+    e->eclass = Eclass_ForName (ValueForKey (e, "classname"),
+      (e->brushes.onext != &e->brushes));
+
+    // go through all parsed brushes and build stuff
+    for(b = e->brushes.onext; b!=&e->brushes; b=b->onext)
+    {
+      for(f = b->brush_faces; f != NULL; f = f->next)
+      {
+        f->pShader = QERApp_Shader_ForName(f->texdef.GetName());
+        f->d_texture = f->pShader->getTexture();
+      }
+
+      // when brushes are in final state, build the planes and windings
+      // NOTE: also converts BP brushes if g_qeglobals.bNeedConvert is true
+      Brush_Build(b);
+    }
+
+//#define TERRAIN_HACK
+#undef TERRAIN_HACK
+
+#ifdef TERRAIN_HACK
+    if ((strcmp(ValueForKey(e, "terrain"),"1") == 0 && strcmp(e->eclass->name,"func_group") == 0))
+    {
+      
+      // two aux pointers to the shaders used in the terrain entity
+      // we don't keep refcount on them since they are only temporary
+      // this avoids doing expensive lookups by name for all faces
+      IShader *pTerrainShader, *pCaulk;
+      
+      pTerrainShader = NULL;
+      pCaulk = QERApp_Shader_ForName(SHADER_CAULK);
+      
+      for(b = e->brushes.onext; b!=&e->brushes; b=b->onext)
+      {
+        if (pTerrainShader == NULL)
+          for(f = b->brush_faces; f != NULL; f = f->next)
+            if (strcmp(f->texdef.GetName(), SHADER_CAULK)!=0)
+              pTerrainShader = f->pShader;
+            
+            if (pTerrainShader)
+            {
+              for(f = b->brush_faces; f != NULL; f = f->next)
+              {
+                if (strcmp(f->texdef.GetName(), SHADER_CAULK)!=0) // not caulk
+                  Face_SetShader(f, pTerrainShader->getName());
+                else
+                  Face_SetShader(f, pCaulk->getName());
+              }
+            }
+            else
+              Sys_Printf("WARNING: no terrain shader found for brush\n");
+      }
+    }
+#endif
+
+#define PATCH_HACK
+#ifdef PATCH_HACK
+    for(b = e->brushes.onext; b!=&e->brushes; b=b->onext)
+    {
+      // patch hack, to be removed when dependency on brush_faces is removed
+      if (b->patchBrush)
+      {
+        Patch_CalcBounds(b->pPatch, mins, maxs);
+        for (int i=0; i<3; i++)
+        {
+          if ((int)mins[i] == (int)maxs[i])
+          {
+            mins[i] -= 4;
+            maxs[i] += 4;
+          }
+        }
+        Brush_Resize(b, mins, maxs);
+        Brush_Build(b);
+      }
+    }
+#endif
+    // add brush for fixedsize entity
+    if (e->eclass->fixedsize)
+    {
+      vec3_t mins, maxs;
+      VectorAdd (e->eclass->mins, e->origin, mins);
+      VectorAdd (e->eclass->maxs, e->origin, maxs);
+      b = Brush_Create (mins, maxs, &e->eclass->texdef);
+      Entity_LinkBrush(e, b);
+      Brush_Build(b);
+    }
+
+    for(b = e->brushes.onext; b!=&e->brushes; b=b->onext)
+      Brush_AddToList(b, pBrushList);
+
+    if (strcmp(e->eclass->name, "worldspawn") == 0)
+    {
+      if (world_entity)
+      {
+        while(e->brushes.onext != &e->brushes)
+        {
+          b = e->brushes.onext;
+          Entity_UnlinkBrush(b);
+          Entity_LinkBrush(world_entity, b);
+        }
+        Entity_Free(e);
+      }
+      else
+      {
+        world_entity = e;
+      }
+    }
+    else if (strcmp(e->eclass->name, "group_info") == 0)
+    {
+      // it's a group thing!
+      Group_Add(e);
+      Entity_Free(e);
+    }
+    else
+    {
+      // fix target/targetname collisions
+      if ((g_PrefsDlg.m_bDoTargetFix) && (strcmp(ValueForKey(e, "target"), "") != 0))
+      {
+        GPtrArray *t_ents = g_ptr_array_new();
+        entity_t *e_target;
+        const char *target = ValueForKey(e, "target");
+        qboolean bCollision=FALSE;
+        
+        // check the current map entities for an actual collision
+        for (e_target = entities.next; e_target != &entities; e_target = e_target->next)
+        {
+          if(!strcmp(target, ValueForKey(e_target, "target")))
+          {
+            bCollision = TRUE;
+            // make sure the collision is not between two imported entities
+            for(j=0; j<(int)new_ents->len; j++)
+            {
+              if(e_target == g_ptr_array_index(new_ents, j))
+                bCollision = FALSE;
+            }
+          }
+        }
+        
+        // find the matching targeted entity(s)
+        if(bCollision)
+        {
+          for(j=num_ents-1; j>0; j--)
+          {
+            e_target = (entity_t*)ents->GetAt(j);
+            if(e_target != NULL && e_target != e)
+            {
+              const char *targetname = ValueForKey(e_target, "targetname");
+              if( (targetname != NULL) && (strcmp(target, targetname) == 0) )
+                g_ptr_array_add(t_ents, (gpointer)e_target);
+            }
+          }
+          if(t_ents->len > 0)
+          {
+            // link the first to get a unique target/targetname
+            Entity_Connect(e, (entity_t*)g_ptr_array_index(t_ents,0));
+            // set the targetname of the rest of them manually
+            for(j = 1; j < (int)t_ents->len; j++)
+              SetKeyValue( (entity_t*)g_ptr_array_index(t_ents, j), "targetname", ValueForKey(e, "target") );
+          }
+          g_ptr_array_free(t_ents, FALSE);
+        }
+      }
+      
+      // add the entity to the end of the entity list
+      Entity_AddToList(e, &entities);
+      g_qeglobals.d_num_entities++;
+      
+      // keep a list of ents added to avoid testing collisions against them
+      g_ptr_array_add(new_ents, (gpointer)e);
+    }
+  }
+  g_ptr_array_free(new_ents, FALSE);
+  
+  ents->RemoveAll();
+
+  g_qeglobals.bNeedConvert = false;
+}
+
+void Map_Import(IDataStream *in, const char *type, bool bAddSelected)
+{
+  CPtrArray ents;
+
+  g_pParentWnd->GetSynapseClient().ImportMap(in, &ents, type);
+  Map_ImportEntities(&ents, bAddSelected);
+}
+
+/*
+================
+Map_LoadFile
+================
+*/
+void Map_LoadFile (const char *filename)
+{
+  clock_t start, finish;
+  double elapsed_time;
+  start = clock();
+
+  Sys_BeginWait ();
+  Select_Deselect();
+  /*!
+  \todo FIXME TTimo why is this commented out? 
+  stability issues maybe? or duplicate feature?
+  forcing to show the console during map load was a good thing IMO
+  */
+  //SetInspectorMode(W_CONSOLE);
+  Sys_Printf ("Loading map from %s\n", filename );
+
+  Map_Free ();
+  //++timo FIXME: maybe even easier to have Group_Init called from Map_Free?
+  Group_Init();
+  g_qeglobals.d_num_entities = 0;
+  g_qeglobals.d_parsed_brushes = 0;
+
+
+  // cancel the map loading process
+  // used when conversion between standard map format and BP format is required and the user cancels the process
+  g_bCancel_Map_LoadFile = false;
+
+  strcpy (currentmap, filename);
+
+  g_bScreenUpdates = false; // leo: avoid redraws while loading the map (see fenris:1952)
+
+  // prepare to let the map module do the parsing
+  FileStream file;
+  const char* type = strrchr(filename,'.');
+  if(type!=NULL) type++;
+    // NOTE TTimo opening has binary doesn't make a lot of sense
+  // but opening as text confuses the scriptlib parser
+  // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=261
+  // this may be a problem if we "rb" and use the XML parser, might have an incompatibility
+  if (file.Open(filename, "rb"))
+    Map_Import(&file, type);
+  else
+    Sys_FPrintf(SYS_ERR, "ERROR: failed to open %s for read\n", filename);
+  file.Close();
+
+  g_bScreenUpdates = true;
+
+  if (g_bCancel_Map_LoadFile)
+  {
+    Sys_Printf("Map_LoadFile canceled\n");
+    Map_New();
+    Sys_EndWait();
+    return;
+  }
+
+  if (!world_entity)
+  {
+    Sys_Printf ("No worldspawn in map.\n");
+    Map_New ();
+    Sys_EndWait();
+    return;
+  }
+  finish = clock();
+  elapsed_time = (double)(finish - start) / CLOCKS_PER_SEC;
+
+  Sys_Printf ("--- LoadMapFile ---\n");
+  Sys_Printf ("%s\n", filename );
+  
+  Sys_Printf ("%5i brushes\n",  g_qeglobals.d_parsed_brushes );
+  Sys_Printf ("%5i entities\n", g_qeglobals.d_num_entities);
+  Sys_Printf ("%5.2f second(s) load time\n", elapsed_time );
+  
+  Sys_EndWait();
+  
+  Map_RestoreBetween ();
+  
+  //
+  // move the view to a start position
+  //
+  Map_StartPosition();
+
+  Map_RegionOff ();
+
+  modified = false;
+  Sys_SetTitle (filename);
+
+  Texture_ShowInuse ();
+  QERApp_SortActiveShaders();
+
+  Sys_UpdateWindows (W_ALL);
+}
+
+/*!
+===========
+Supporting functions for Map_SaveFile, builds a CPtrArray with the filtered / non filtered brushes
+===========
+*/
+void CleanFilter(entity_t *ent)
+{
+  if (ent->pData)
+  {
+    delete static_cast<CPtrArray*>(ent->pData);
+    ent->pData = NULL;
+  }
+}
+
+/*!
+filters out the region brushes if necessary
+returns true if this entity as a whole is out of the region
+(if all brushes are filtered out, then the entity will be completely dropped .. except if it's worldspawn of course)
+*/
+bool FilterChildren(entity_t *ent, bool bRegionOnly = false, bool bSelectedOnly = false)
+{
+  if(ent->brushes.onext == &ent->brushes)
+    return false;
+  // entity without a brush, ignore it... this can be caused by Undo
+
+  // filter fixedsize ents by their eclass bounding box
+  // don't add their brushes
+  if (ent->eclass->fixedsize)
+  {
+    if(bSelectedOnly && !IsBrushSelected(ent->brushes.onext))
+      return false;
+
+    if(bRegionOnly && region_active)
+    {
+      for (int i=0 ; i<3 ; i++)
+           {
+                   if ((ent->origin[i] + ent->eclass->mins[i]) > region_maxs[i])
+                           return false;
+                   if ((ent->origin[i] + ent->eclass->maxs[i]) < region_mins[i])
+                           return false;
+           }
+    }
+  }
+  else
+  {
+    for (brush_t *b = ent->brushes.onext ; b != &ent->brushes ; b=b->onext)
+    {
+      // set flag to use brushprimit_texdef
+      if(g_qeglobals.m_bBrushPrimitMode)
+        b->bBrushDef = true;
+      else
+        b->bBrushDef = false;
+
+      // add brush, unless it's excluded by region
+      if ( !(bRegionOnly && Map_IsBrushFiltered(b)) &&
+           !(bSelectedOnly && !IsBrushSelected(b)) )
+        ((CPtrArray*)ent->pData)->Add(b);
+    }
+
+    if (((CPtrArray*)ent->pData)->GetSize() <= 0)
+      return false;
+  }
+  return true;
+}
+
+entity_t *region_startpoint = NULL;
+void Map_ExportEntities(CPtrArray* ents, bool bRegionOnly = false, bool bSelectedOnly = false)
+{
+  int i;
+  entity_t *e;
+
+  /*!
+  \todo the entity_t needs to be reworked and asbtracted some more
+  
+  keeping the entity_t as the struct providing access to a list of map objects, a list of epairs and various other info?
+  but separating some more the data that belongs to the entity_t and the 'sons' data
+  on a side note, I don't think that doing that with linked list would be a good thing
+  
+  for now, we use the blind void* in entity_t casted to a CPtrArray of brush_t* to hand out a list of the brushes for map write
+  the next step is very likely to be a change of the brush_t* to a more abstract object?
+  */
+
+  FilterChildren(world_entity, bRegionOnly, bSelectedOnly);
+  ents->Add(world_entity);
+
+  for (e=entities.next ; e!=&entities ; e=e->next)
+  {
+    // not sure this still happens, probably safe to leave it in
+    if ((!strcmp(ValueForKey (e, "classname"), "worldspawn")) && (e!=world_entity))
+    {
+      Sys_FPrintf(SYS_ERR, "Dropping parasite worldspawn entity\n");
+      continue;
+    }
+
+    // entities which brushes are completely filtered out by regioning are not printed to the map
+    if (FilterChildren(e, bRegionOnly, bSelectedOnly))
+      ents->Add(e);
+  }
+
+  if (bRegionOnly && region_active)
+  {
+    for(i=0; i<6; i++)
+      ((CPtrArray*)world_entity->pData)->Add(region_sides[i]);
+
+    ents->Add(region_startpoint);
+  }
+}
+
+void Map_Export(IDataStream *out, const char *type, bool bRegionOnly, bool bSelectedOnly)
+{
+  entity_t     *e;
+
+  CPtrArray ents;
+  
+  if (bRegionOnly && region_active)
+    AddRegionBrushes();
+
+  // create the filters
+  world_entity->pData = new CPtrArray();
+  for(e = entities.next; e != &entities; e = e->next)
+    e->pData = new CPtrArray();
+
+  Map_ExportEntities(&ents, bRegionOnly, bSelectedOnly);
+
+  g_pParentWnd->GetSynapseClient().ExportMap(&ents, out, type);
+
+  // cleanup the filters
+  CleanFilter(world_entity);
+  for (e=entities.next ; e!=&entities ; e=e->next)
+    CleanFilter(e);
+
+  if (bRegionOnly && region_active)
+    RemoveRegionBrushes();
+}
+
+const char* filename_get_extension(const char* filename)
+{
+  const char* type = strrchr(filename,'.');
+  if(type != NULL)
+    return ++type;
+  return "";
+}
+
+/*
+===========
+Map_SaveFile
+\todo FIXME remove the use_region, this is broken .. work with a global flag to set region mode or not
+===========
+*/
+void Map_SaveFile (const char *filename, qboolean use_region )
+{
+  clock_t start, finish;
+  double elapsed_time;
+  start = clock();
+  Sys_Printf("Saving map to %s\n",filename);
+
+       Pointfile_Clear ();
+
+       if (!use_region)
+       {
+               char    backup[1024];
+
+               // rename current to .bak
+               strcpy (backup, filename);
+               StripExtension (backup);
+               strcat (backup, ".bak");
+               unlink (backup);
+               rename (filename, backup);
+       }
+
+       Sys_Printf ("Map_SaveFile: %s\n", filename);
+
+  // build the out data stream
+  FileStream file;
+  if (!file.Open(filename,"w"))
+  {
+    Sys_FPrintf(SYS_ERR, "ERROR: couldn't open %s for write\n", filename);
+    return;
+  }
+
+  // extract filetype
+  Map_Export(&file, filename_get_extension(filename), use_region);
+
+  file.Close();
+
+  finish = clock();
+  elapsed_time = (double)(finish - start) / CLOCKS_PER_SEC;
+
+  Sys_Printf ("Saved in %-.2f second(s).\n",elapsed_time);
+       modified = false;
+
+       if ( !strstr( filename, "autosave" ) )
+               Sys_SetTitle (filename);
+
+       if (!use_region)
+       {
+               time_t  timer;
+
+               time (&timer);
+
+    Sys_Beep ();
+
+               Sys_Status ("Saved.", 0);
+       }
+}
+
+/*
+===========
+Map_New
+
+===========
+*/
+void Map_New (void)
+{
+       Sys_Printf ("Map_New\n");
+       Map_Free ();
+
+       strcpy (currentmap, "unnamed.map");
+       Sys_SetTitle (currentmap);
+
+  world_entity = (entity_s*)qmalloc(sizeof(*world_entity));
+       world_entity->brushes.onext = 
+               world_entity->brushes.oprev = &world_entity->brushes;
+       SetKeyValue (world_entity, "classname", "worldspawn");
+       world_entity->eclass = Eclass_ForName ("worldspawn", true);
+
+       g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = 0;
+       g_pParentWnd->GetCamWnd()->Camera()->angles[PITCH] = 0;
+       VectorCopy (vec3_origin, g_pParentWnd->GetCamWnd()->Camera()->origin);
+       g_pParentWnd->GetCamWnd()->Camera()->origin[2] = 48;
+       VectorCopy (vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin());
+
+       Map_RestoreBetween ();
+
+  Group_Init();
+
+       Sys_UpdateWindows (W_ALL);
+       modified = false;
+}
+
+/*
+===========================================================
+
+  REGION
+
+===========================================================
+*/
+qboolean       region_active;
+vec3_t region_mins = {g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord};
+vec3_t region_maxs = {g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord};
+
+brush_t        *region_sides[6];
+
+/*
+===========
+AddRegionBrushes
+a regioned map will have temp walls put up at the region boundary
+\todo TODO TTimo old implementation of region brushes
+  we still add them straight in the worldspawn and take them out after the map is saved
+  with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
+===========
+*/
+void AddRegionBrushes (void)
+{
+       vec3_t  mins, maxs;
+       int             i;
+       texdef_t        td;
+
+       if (!region_active)
+  {
+#ifdef _DEBUG
+    Sys_FPrintf( SYS_WRN, "Unexpected AddRegionBrushes call.\n");
+#endif
+               return;
+  }
+
+       memset (&td, 0, sizeof(td));
+       td.SetName(SHADER_NOT_FOUND);
+
+  // set mins
+  VectorSet(mins, region_mins[0]-32, region_mins[1]-32, region_mins[2]-32);
+
+  // vary maxs
+  for(i=0; i<3; i++)
+  {
+    VectorSet(maxs, region_maxs[0]+32, region_maxs[1]+32, region_maxs[2]+32);
+    maxs[i] = region_mins[i];
+    region_sides[i] = Brush_Create (mins, maxs, &td);
+  }
+
+  // set maxs
+  VectorSet(maxs, region_maxs[0]+32, region_maxs[1]+32, region_maxs[2]+32);
+
+  // vary mins
+  for(i=0; i<3; i++)
+  {
+    VectorSet(mins, region_mins[0]-32, region_mins[1]-32, region_mins[2]-32);
+    mins[i] = region_maxs[i];
+    region_sides[i+3] = Brush_Create (mins, maxs, &td);
+  }
+
+  
+  // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=503
+  // this is a safe check, but it should not really happen anymore  
+  vec3_t vOrig;
+  VectorSet(vOrig,
+    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[0],
+    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[1], 
+    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[2]);
+
+  for (i=0 ; i<3 ; i++)
+  {
+    if (vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i])
+    {
+      Sys_FPrintf(SYS_ERR, "Camera is NOT in the region, it's likely that the region won't compile correctly\n");
+    }
+  }
+  
+  // write the info_playerstart
+  region_startpoint = Entity_Alloc();
+  SetKeyValue(region_startpoint, "classname", "info_player_start");
+  region_startpoint->eclass = Eclass_ForName ("info_player_start", false);
+  char sTmp[1024];
+  sprintf(sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2]);
+  SetKeyValue(region_startpoint, "origin", sTmp);
+  sprintf(sTmp, "%d", (int)g_pParentWnd->GetCamWnd()->Camera()->angles[YAW]);
+  SetKeyValue(region_startpoint, "angle", sTmp);
+  // empty array of children
+  region_startpoint->pData = new CPtrArray;
+}
+
+void RemoveRegionBrushes (void)
+{
+       int             i;
+
+       if (!region_active)
+               return;
+       for (i=0 ; i<6 ; i++)
+               Brush_Free (region_sides[i]);
+
+  CleanFilter(region_startpoint);
+  Entity_Free(region_startpoint);
+}
+
+qboolean Map_IsBrushFiltered (brush_t *b)
+{
+       int             i;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (b->mins[i] > region_maxs[i])
+                       return true;
+               if (b->maxs[i] < region_mins[i])
+                       return true;
+       }
+       return false;
+}
+
+/*
+===========
+Map_RegionOff
+
+Other filtering options may still be on
+===========
+*/
+void Map_RegionOff (void)
+{
+       brush_t *b, *next;
+       int                     i;
+
+       region_active = false;
+       for (i=0 ; i<3 ; i++)
+       {
+               region_maxs[i] = g_MaxWorldCoord-64;
+               region_mins[i] = g_MinWorldCoord+64;
+       }
+       
+       for (b=filtered_brushes.next ; b != &filtered_brushes ; b=next)
+       {
+               next = b->next;
+               if (Map_IsBrushFiltered (b))
+                       continue;               // still filtered
+               Brush_RemoveFromList (b);
+               if (active_brushes.next == NULL || active_brushes.prev == NULL)
+               {
+                       active_brushes.next = &active_brushes;
+                       active_brushes.prev = &active_brushes;
+               }
+               Brush_AddToList (b, &active_brushes);
+               b->bFiltered = FilterBrush(b);
+       }
+       Sys_UpdateWindows (W_ALL);
+}
+
+void Map_ApplyRegion (void)
+{
+       brush_t *b, *next;
+
+       region_active = true;
+       for (b=active_brushes.next ; b != &active_brushes ; b=next)
+       {
+               next = b->next;
+               if (!Map_IsBrushFiltered (b))
+                       continue;               // still filtered
+               Brush_RemoveFromList (b);
+               Brush_AddToList (b, &filtered_brushes);
+       }
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+========================
+Map_RegionSelectedBrushes
+========================
+*/
+void Map_RegionSelectedBrushes (void)
+{
+       Map_RegionOff ();
+
+       if (selected_brushes.next == &selected_brushes)  // nothing selected
+  {
+    Sys_Printf("Tried to region with no selection...\n");
+    return;
+  }
+       region_active = true;
+       Select_GetBounds (region_mins, region_maxs);
+
+#ifdef _DEBUG
+       if (filtered_brushes.next != &filtered_brushes)
+               Sys_Printf("WARNING: filtered_brushes list may not be empty in Map_RegionSelectedBrushes\n");
+#endif
+
+       if (active_brushes.next == &active_brushes)
+       {
+               // just have an empty filtered_brushes list
+               // this happens if you set region after selecting all the brushes in your map (some weird people do that, ask MrE!)
+               filtered_brushes.next = filtered_brushes.prev = &filtered_brushes;
+       }
+       else
+       {
+               // move the entire active_brushes list to filtered_brushes
+               filtered_brushes.next = active_brushes.next;
+               filtered_brushes.prev = active_brushes.prev;
+               filtered_brushes.next->prev = &filtered_brushes;
+               filtered_brushes.prev->next = &filtered_brushes;
+       }
+
+       // move the entire selected_brushes list to active_brushes
+       active_brushes.next = selected_brushes.next;
+       active_brushes.prev = selected_brushes.prev;
+       active_brushes.next->prev = &active_brushes;
+       active_brushes.prev->next = &active_brushes;
+
+       // deselect patches
+       for (brush_t *b = active_brushes.next; b != &active_brushes; b = b->next)
+          if (b->patchBrush)
+            b->pPatch->bSelected = false;
+
+       // clear selected_brushes
+       selected_brushes.next = selected_brushes.prev = &selected_brushes;
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+===========
+Map_RegionXY
+===========
+*/
+void Map_RegionXY (void)
+{
+       Map_RegionOff ();
+
+       region_mins[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale();
+       region_maxs[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale();
+       region_mins[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale();
+       region_maxs[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale();
+       region_mins[2] = g_MinWorldCoord+64;
+       region_maxs[2] = g_MaxWorldCoord-64;
+       Map_ApplyRegion ();
+}
+
+/*
+===========
+Map_RegionTallBrush
+===========
+*/
+void Map_RegionTallBrush (void)
+{
+  brush_t *b;
+
+  if (!QE_SingleBrush ())
+    return;
+
+  b = selected_brushes.next;
+
+  Map_RegionOff ();
+
+  VectorCopy (b->mins, region_mins);
+  VectorCopy (b->maxs, region_maxs);
+  region_mins[2] = g_MinWorldCoord+64;
+  region_maxs[2] = g_MaxWorldCoord-64;
+
+  Undo_Start("delete");
+  Undo_AddBrushList(&selected_brushes);
+  Undo_AddEntity(b->owner);
+  Select_Delete ();
+  Undo_EndBrushList(&selected_brushes);
+  Undo_End();
+
+  Map_ApplyRegion ();
+}
+
+/*
+===========
+Map_RegionBrush
+===========
+*/
+void Map_RegionBrush (void)
+{
+  brush_t *b;
+
+  if (!QE_SingleBrush ())
+    return;
+
+  b = selected_brushes.next;
+
+  Map_RegionOff ();
+
+  VectorCopy (b->mins, region_mins);
+  VectorCopy (b->maxs, region_maxs);
+
+  Undo_Start("delete");
+  Undo_AddBrushList(&selected_brushes);
+  Undo_AddEntity(b->owner);
+  Select_Delete ();
+  Undo_EndBrushList(&selected_brushes);
+  Undo_End();
+
+  Map_ApplyRegion ();
+}
+
+GList *find_string(GList *glist, const char *buf)
+{
+  while (glist)
+  {
+    if (strcmp((char *)glist->data, buf) == 0)
+      break; // this name is in our list already
+    glist = glist->next;
+  }
+  return glist;
+}
+
+void Map_ImportBuffer(char *buf)
+{
+  Select_Deselect();
+  
+  Undo_Start("import buffer");
+
+  MemStream stream;
+
+  stream.Write(buf, strlen(buf));
+  Map_Import(&stream, "xmap");
+  stream.Close();
+  
+  Sys_UpdateWindows (W_ALL);
+  Sys_MarkMapModified();
+  
+  Undo_End();
+}
+
+
+//
+//================
+//Map_ImportFile
+//================
+//
+void Map_ImportFile (const char *filename)
+{
+  FileStream file;
+       Sys_BeginWait ();
+
+  Sys_Printf("Importing map from %s\n",filename);
+
+  const char* type = strrchr(filename,'.');
+  if(type!=NULL) type++;
+  /*!\todo Resolve "r" problem in scriptlib" */
+  if(file.Open(filename, "rb"))
+    Map_Import(&file, type, true);
+  else
+    Sys_FPrintf(SYS_ERR, "ERROR: couldn't open %s for read\n", filename);
+
+  file.Close();
+
+       Sys_UpdateWindows (W_ALL);
+       modified = true;
+       Sys_EndWait();
+}
+
+//
+//===========
+//Map_SaveSelected
+//===========
+//
+// Saves selected world brushes and whole entities with partial/full selections
+//
+void Map_SaveSelected(const char* filename)
+{
+  FileStream file;
+
+  Sys_Printf("Saving selection to %s\n",filename);
+
+  const char* type = strrchr(filename,'.');
+  if(type!=NULL) type++;
+  if(file.Open(filename, "w"))
+    Map_Export (&file, type, false, true);
+  else
+    Sys_FPrintf(SYS_ERR, "ERROR: failed to open %s for write\n", filename);
+
+  file.Close();
+
+}
+
+//
+//===========
+//Map_SaveSelected
+//===========
+//
+// Saves selected world brushes and whole entities with partial/full selections
+//
+void Map_SaveSelected (MemStream* pMemFile, MemStream* pPatchFile)
+{
+  Map_Export (pMemFile, "xmap", false, true);
+
+ /*
+       // write world entity first
+       Entity_WriteSelected(world_entity, pMemFile);
+
+       // then write all other ents
+       count = 1;
+       for (e=entities.next ; e != &entities ; e=next)
+       {
+               MemFile_fprintf(pMemFile, "// entity %i\n", count);
+               count++;
+               Entity_WriteSelected(e, pMemFile);
+               next = e->next;
+       }
+
+  //if (pPatchFile)
+  //  Patch_WriteFile(pPatchFile);
+  */
+}
+
+
+void MemFile_fprintf(MemStream* pMemFile, const char* pText, ...)
+{
+  char Buffer[4096];
+  va_list args;
+       va_start (args,pText);
+  vsprintf(Buffer, pText, args);
+  pMemFile->Write(Buffer, strlen(Buffer));
+}
+
+/*!
+==============
+Region_SpawnPoint
+push the region spawn point
+\todo FIXME TTimo this was in the #1 MAP module implementation (in the core)
+not sure it has any use anymore, should prolly drop it
+==============
+*/
+void Region_SpawnPoint(FILE *f)
+{
+  // write the info_player_start, we use the camera position
+  fprintf (f, "{\n");
+  fprintf (f, "\"classname\" \"info_player_start\"\n");
+  fprintf (f, "\"origin\" \"%i %i %i\"\n", 
+    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[0],
+    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[1], 
+    (int)g_pParentWnd->GetCamWnd()->Camera()->origin[2]);
+  fprintf (f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamWnd()->Camera()->angles[YAW]);
+  fprintf (f, "}\n");
+}