]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/radiant/EditorMap.cpp
hello world
[icculus/iodoom3.git] / neo / tools / radiant / EditorMap.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "qe3.h"
33
34 int                     mapModified;                    // for quit confirmation (0 = clean, 1 = unsaved,
35
36 // 2 = autosaved, but not regular saved)
37 char            currentmap[1024];
38
39 brush_t         active_brushes;                 // brushes currently being displayed
40 brush_t         selected_brushes;               // highlighted
41
42 face_t          *selected_face;
43 brush_t         *selected_face_brush;
44
45 brush_t         filtered_brushes;               // brushes that have been filtered or regioned
46
47 entity_t        entities;                               // head/tail of doubly linked list
48
49 entity_t        *world_entity = NULL;   // "classname" "worldspawn" !
50
51 void            AddRegionBrushes(void);
52 void            RemoveRegionBrushes(void);
53
54 /*
55  =======================================================================================================================
56  =======================================================================================================================
57  */
58 void DupLists() {
59         DWORD   dw = GetTickCount();
60 }
61
62 /*
63  * Cross map selection saving this could mess this up if you have only part of a
64  * complex entity selected...
65  */
66 brush_t         between_brushes;
67 entity_t        between_entities;
68
69 bool            g_bRestoreBetween = false;
70
71 /*
72  =======================================================================================================================
73  =======================================================================================================================
74  */
75 void Map_SaveBetween(void) {
76         if (g_pParentWnd->ActiveXY()) {
77                 g_bRestoreBetween = true;
78                 g_pParentWnd->ActiveXY()->Copy();
79         }
80
81         return;
82
83 }
84
85 /*
86  =======================================================================================================================
87  =======================================================================================================================
88  */
89 void Map_RestoreBetween(void) {
90         if (g_pParentWnd->ActiveXY() && g_bRestoreBetween) {
91                 g_pParentWnd->ActiveXY()->Paste();
92         }
93
94         return;
95
96 }
97
98 /*
99  =======================================================================================================================
100  =======================================================================================================================
101  */
102 bool CheckForTinyBrush(brush_t *b, int n, float fSize) {
103         bool    bTiny = false;
104         for (int i = 0; i < 3; i++) {
105                 if (b->maxs[i] - b->mins[i] < fSize) {
106                         bTiny = true;
107                 }
108         }
109
110         if (bTiny) {
111                 common->Printf("Possible problem brush (too small) #%i ", n);
112         }
113
114         return bTiny;
115 }
116
117 /*
118  =======================================================================================================================
119  =======================================================================================================================
120  */
121 void Map_BuildBrushData(void) {
122         brush_t *b, *next;
123
124         if (active_brushes.next == NULL) {
125                 return;
126         }
127
128         Sys_BeginWait();        // this could take a while
129
130         int n = 0;
131         for (b = active_brushes.next; b != NULL && b != &active_brushes; b = next) {
132                 next = b->next;
133                 Brush_Build(b, true, false, false);
134                 if (!b->brush_faces || (g_PrefsDlg.m_bCleanTiny && CheckForTinyBrush(b, n++, g_PrefsDlg.m_fTinySize))) {
135                         Brush_Free(b);
136                         common->Printf("Removed degenerate brush\n");
137                 }
138         }
139
140         Sys_EndWait();
141 }
142
143 /*
144  =======================================================================================================================
145  =======================================================================================================================
146  */
147 entity_t *Map_FindClass(char *cname) {
148         entity_t        *ent;
149
150         for (ent = entities.next; ent != &entities; ent = ent->next) {
151                 if (!strcmp(cname, ValueForKey(ent, "classname"))) {
152                         return ent;
153                 }
154         }
155
156         return NULL;
157 }
158
159 /*
160  =======================================================================================================================
161  =======================================================================================================================
162  */
163 int Map_GetUniqueEntityID(const char *prefix, const char *eclass) {
164         entity_t        *ent;
165         int                     id = 0;
166         for (ent = entities.next; ent != &entities; ent = ent->next) {
167                 if (!strcmp(eclass, ValueForKey(ent, "classname"))) {
168                         const char      *name = ValueForKey(ent, "name");
169                         if (name && name[0]) {
170                                 const char *buf;
171                                 if (prefix && *prefix) {
172                                         buf = va("%s_%s_", prefix, eclass);
173                                 } else {
174                                         buf = va("%s_", eclass);
175                                 }
176                                 int                     len = strlen(buf);
177                                 if ( idStr::Cmpn(name, buf, len) == 0 ) {
178                                         int j = atoi(name + len);
179                                         if (j > id) {
180                                                 id = j;
181                                         }
182                                 }
183                         }
184                 }
185         }
186
187         return id + 1;
188 }
189
190 /*
191  =======================================================================================================================
192  =======================================================================================================================
193  */
194 bool Entity_NameIsUnique(const char *name) {
195         entity_t        *ent;
196         if (name == NULL) {
197                 return false;
198         }
199
200         for (ent = entities.next; ent != &entities; ent = ent->next) {
201                 const char      *testName = ValueForKey(ent, "name");
202                 if (testName) {
203                         if ( idStr::Icmp(name, testName) == 0 ) {
204                                 return false;
205                         }
206                 }
207         }
208
209         return true;
210 }
211
212 /*
213  =======================================================================================================================
214     Map_Free
215  =======================================================================================================================
216  */
217 void Map_Free(void) {
218         g_bRestoreBetween = false;
219         if (selected_brushes.next && (selected_brushes.next != &selected_brushes)) {
220                 if (g_pParentWnd->MessageBox("Copy selection?", "", MB_YESNO) == IDYES) {
221                         Map_SaveBetween();
222                 }
223         }
224
225         // clear all the render and sound system data
226         g_qeglobals.rw->InitFromMap( NULL );
227         g_qeglobals.sw->ClearAllSoundEmitters();
228
229         Texture_ClearInuse();
230         Pointfile_Clear();
231         strcpy(currentmap, "unnamed.map");
232         Sys_SetTitle(currentmap);
233         g_qeglobals.d_num_entities = 0;
234
235         if (!active_brushes.next) { // first map
236                 active_brushes.prev = active_brushes.next = &active_brushes;
237                 selected_brushes.prev = selected_brushes.next = &selected_brushes;
238                 filtered_brushes.prev = filtered_brushes.next = &filtered_brushes;
239
240                 entities.prev = entities.next = &entities;
241         }
242         else {
243                 while (active_brushes.next != &active_brushes) {
244                         Brush_Free(active_brushes.next, false);
245                 }
246
247                 while (selected_brushes.next != &selected_brushes) {
248                         Brush_Free(selected_brushes.next, false);
249                 }
250
251                 while (filtered_brushes.next != &filtered_brushes) {
252                         Brush_Free(filtered_brushes.next, false);
253                 }
254
255                 while (entities.next != &entities) {
256                         Entity_Free(entities.next);
257                 }
258         }
259
260         if (world_entity) {
261                 Entity_Free(world_entity);
262         }
263
264         world_entity = NULL;
265 }
266
267 /*
268  =======================================================================================================================
269  =======================================================================================================================
270  */
271 entity_t *AngledEntity() {
272         entity_t        *ent = Map_FindClass("info_player_start");
273         if (!ent) {
274                 ent = Map_FindClass("info_player_deathmatch");
275         }
276
277         if (!ent) {
278                 ent = Map_FindClass("info_player_deathmatch");
279         }
280
281         if (!ent) {
282                 ent = Map_FindClass("team_CTF_redplayer");
283         }
284
285         if (!ent) {
286                 ent = Map_FindClass("team_CTF_blueplayer");
287         }
288
289         if (!ent) {
290                 ent = Map_FindClass("team_CTF_redspawn");
291         }
292
293         if (!ent) {
294                 ent = Map_FindClass("team_CTF_bluespawn");
295         }
296
297         return ent;
298 }
299
300
301 brush_t *BrushFromMapPatch(idMapPatch *mappatch, idVec3 origin) {
302         patchMesh_t *pm = MakeNewPatch(mappatch->GetWidth(), mappatch->GetHeight());
303         pm->d_texture = Texture_ForName(mappatch->GetMaterial());
304         for (int i = 0; i < mappatch->GetWidth(); i++) {
305                 for (int j = 0; j < mappatch->GetHeight(); j++) {
306                         pm->ctrl(i, j).xyz = (*mappatch)[j * mappatch->GetWidth() + i].xyz + origin;
307                         pm->ctrl(i, j).st = (*mappatch)[j * mappatch->GetWidth() + i].st;
308                 }
309         }
310         pm->horzSubdivisions = mappatch->GetHorzSubdivisions();
311         pm->vertSubdivisions = mappatch->GetVertSubdivisions();
312         pm->explicitSubdivisions = mappatch->GetExplicitlySubdivided();
313         if (mappatch->epairs.GetNumKeyVals()) {
314                 pm->epairs = new idDict;
315                 *pm->epairs = mappatch->epairs;
316         }
317         brush_t *b = AddBrushForPatch(pm, false);
318         return b;
319 }
320
321 brush_t *BrushFromMapBrush(idMapBrush *mapbrush, idVec3 origin) {
322         brush_t *b = NULL;
323         if (mapbrush) {
324                 b = Brush_Alloc();
325                 int count = mapbrush->GetNumSides();
326                 for (int i = 0; i < count; i++) {
327                         idMapBrushSide *side = mapbrush->GetSide(i);
328                         face_t *f = Face_Alloc();
329                         f->next = NULL;
330                         if (!b->brush_faces) {
331                                 b->brush_faces = f;
332                         }
333                         else {
334                                 face_t  *scan;
335                                 for (scan = b->brush_faces; scan->next; scan = scan->next) {
336                                         ;
337                                 }
338                                 scan->next = f;
339                         }
340                         f->plane = side->GetPlane();
341                         f->originalPlane = f->plane;
342                         f->dirty = false;
343
344                         idWinding w;
345                         w.BaseForPlane(f->plane);
346
347                         for (int j = 0; j < 3; j++) {
348                                 f->planepts[j].x = w[j].x + origin.x;
349                                 f->planepts[j].y = w[j].y + origin.y;
350                                 f->planepts[j].z = w[j].z + origin.z;
351                         }
352
353                         idVec3 mat[2];
354                         side->GetTextureMatrix(mat[0], mat[1]);
355                         f->brushprimit_texdef.coords[0][0] = mat[0][0];
356                         f->brushprimit_texdef.coords[0][1] = mat[0][1];
357                         f->brushprimit_texdef.coords[0][2] = mat[0][2];
358                         f->brushprimit_texdef.coords[1][0] = mat[1][0];
359                         f->brushprimit_texdef.coords[1][1] = mat[1][1];
360                         f->brushprimit_texdef.coords[1][2] = mat[1][2];
361
362                         f->texdef.SetName(side->GetMaterial());
363                 }
364         }
365         return b;
366 }
367
368 entity_t *EntityFromMapEntity(idMapEntity *mapent, CWaitDlg *dlg) {
369         entity_t *ent = NULL;
370         if (mapent) {
371                 ent = Entity_New();
372                 ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
373                 ent->origin.Zero();
374                 ent->epairs = mapent->epairs;
375                 GetVectorForKey(ent, "origin", ent->origin);
376                 int count = mapent->GetNumPrimitives();
377                 long lastUpdate = 0;
378                 idStr status;
379                 for (int i = 0; i < count; i++) {
380                         idMapPrimitive *prim = mapent->GetPrimitive(i);
381                         if (prim) {
382                                 // update 20 times a second
383                                 if ( (GetTickCount() - lastUpdate) > 50 ) {
384                                         lastUpdate = GetTickCount();
385                                         if (prim->GetType() == idMapPrimitive::TYPE_BRUSH) {
386                                                 sprintf(status, "Reading primitive %i (brush)", i);
387                                         } else if (prim->GetType() == idMapPrimitive::TYPE_PATCH) {
388                                                 sprintf(status, "Reading primitive %i (patch)", i);
389                                         }
390                                         dlg->SetText(status, true);
391                                 }
392                                 if ( dlg->CancelPressed() ) {
393                                         return ent;
394                                 }
395
396                                 brush_t *b = NULL;
397                                 if (prim->GetType() == idMapPrimitive::TYPE_BRUSH) {
398                                         idMapBrush *mapbrush = reinterpret_cast<idMapBrush*>(prim);
399                                         b = BrushFromMapBrush(mapbrush, ent->origin);
400                                 } else if (prim->GetType() == idMapPrimitive::TYPE_PATCH) {
401                                         idMapPatch *mappatch = reinterpret_cast<idMapPatch*>(prim);
402                                         b = BrushFromMapPatch(mappatch, ent->origin);
403                                 }
404                                 if (b) {
405                                         b->owner = ent;
406                                         // add to the end of the entity chain
407                                         b->onext = &ent->brushes;
408                                         b->oprev = ent->brushes.oprev;
409                                         ent->brushes.oprev->onext = b;
410                                         ent->brushes.oprev = b;
411                                 }
412                         }
413                 }
414         }
415         return ent;
416 }
417
418 extern entity_t *Entity_PostParse(entity_t *ent, brush_t *pList);
419  /*
420  =======================================================================================================================
421     Map_LoadFile
422  =======================================================================================================================
423  */
424 void Map_LoadFile(const char *filename) {
425         entity_t *ent;
426         CWaitDlg dlg;
427         idStr fileStr, status;
428         idMapFile mapfile;
429
430         Sys_BeginWait();
431         Select_Deselect();
432
433         dlg.AllowCancel( true );
434         idStr( filename ).ExtractFileName( fileStr );
435         sprintf( status, "Loading %s...", fileStr.c_str() );
436         dlg.SetWindowText( status );
437         sprintf( status, "Reading file %s...", fileStr.c_str() );
438         dlg.SetText( status );
439
440         // SetInspectorMode(W_CONSOLE);
441         fileStr = filename;
442         fileStr.BackSlashesToSlashes();
443
444         common->Printf( "Map_LoadFile: %s\n", fileStr.c_str() );
445
446         Map_Free();
447
448         g_qeglobals.d_parsed_brushes = 0;
449         strcpy( currentmap, filename );
450
451         if(mapfile.Parse(filename, true, true)) {
452                 g_qeglobals.bNeedConvert = false;
453                 g_qeglobals.bOldBrushes = false;
454                 g_qeglobals.bPrimitBrushes = false;
455                 g_qeglobals.mapVersion = 1.0;
456
457                 long lastUpdate = 0;
458                 int count = mapfile.GetNumEntities();
459                 for (int i = 0; i < count; i++) {
460                         idMapEntity *mapent = mapfile.GetEntity(i);
461                         if (mapent) {
462                                 idStr classname = mapent->epairs.GetString("classname");
463                                 // Update 20 times a second
464                                 if ( (GetTickCount() - lastUpdate) > 50 ) {
465                                         lastUpdate = GetTickCount();
466                                         sprintf(status, "Loading entity %i (%s)...", i, classname.c_str());
467                                         dlg.SetText(status);
468                                 }
469                                 if ( dlg.CancelPressed() ) {
470                                         Sys_Status("Map load cancelled.\n");
471                                         Map_New();
472                                         return;
473                                 }
474                                 if (classname == "worldspawn") {
475                                         world_entity = EntityFromMapEntity(mapent, &dlg);
476                                         Entity_PostParse(world_entity, &active_brushes);
477                                 } else {
478                                         ent = EntityFromMapEntity(mapent, &dlg);
479                                         Entity_PostParse(ent, &active_brushes);
480                                         Entity_Name(ent, true);
481                                         // add the entity to the end of the entity list
482                                         ent->next = &entities;
483                                         ent->prev = entities.prev;
484                                         entities.prev->next = ent;
485                                         entities.prev = ent;
486                                         g_qeglobals.d_num_entities++;
487                                 }
488                         }
489                 }
490         }
491
492         if (!world_entity) {
493                 Sys_Status("No worldspawn in map.\n");
494                 Map_New();
495                 return;
496         }
497
498         common->Printf("--- LoadMapFile ---\n");
499         common->Printf("%s\n", fileStr.c_str());
500
501         common->Printf("%5i brushes\n", g_qeglobals.d_parsed_brushes);
502         common->Printf("%5i entities\n", g_qeglobals.d_num_entities);
503
504         dlg.SetText("Restoring Between");
505         Map_RestoreBetween();
506
507         dlg.SetText("Building Brush Data");
508         common->Printf("Map_BuildAllDisplayLists\n");
509         Map_BuildBrushData();
510
511         //
512         // reset the "need conversion" flag conversion to the good format done in
513         // Map_BuildBrushData
514         //
515         g_qeglobals.bNeedConvert = false;
516
517         // move the view to a start position
518         ent = AngledEntity();
519
520         g_pParentWnd->GetCamera()->Camera().angles[PITCH] = 0;
521         
522         if (ent) {
523                 GetVectorForKey(ent, "origin", g_pParentWnd->GetCamera()->Camera().origin);
524                 GetVectorForKey(ent, "origin", g_pParentWnd->GetXYWnd()->GetOrigin());
525                 g_pParentWnd->GetCamera()->Camera().angles[YAW] = FloatForKey(ent, "angle");
526         }
527         else {
528                 g_pParentWnd->GetCamera()->Camera().angles[YAW] = 0;
529                 VectorCopy(vec3_origin, g_pParentWnd->GetCamera()->Camera().origin);
530                 VectorCopy(vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin());
531         }
532
533         Map_RegionOff();
534
535         mapModified = 0;
536
537         if (GetFileAttributes(filename) & FILE_ATTRIBUTE_READONLY) {
538                 fileStr += " (read only) ";
539         }
540         Sys_SetTitle(fileStr);
541
542         Texture_ShowInuse();
543
544         if (g_pParentWnd->GetCamera()->GetRenderMode()) {
545                 g_pParentWnd->GetCamera()->BuildRendererState();
546         }
547
548         Sys_EndWait();
549         Sys_UpdateWindows(W_ALL);
550 }
551
552
553 void Map_VerifyCurrentMap(const char *map) {
554         if ( idStr::Icmp( map, currentmap ) != 0 ) {
555                 Map_LoadFile( map );
556         }
557 }
558
559 idMapPrimitive *BrushToMapPrimitive( const brush_t *b, const idVec3 &origin ) {
560         if ( b->pPatch ) {
561                 idMapPatch *patch = new idMapPatch( b->pPatch->width * 6, b->pPatch->height * 6 );
562                 patch->SetSize( b->pPatch->width, b->pPatch->height );
563                 for ( int i = 0; i < b->pPatch->width; i++ ) {
564                         for ( int j = 0; j < b->pPatch->height; j++ ) {
565                                 (*patch)[j*patch->GetWidth()+i].xyz =  b->pPatch->ctrl(i, j).xyz - origin;
566                                 (*patch)[j*patch->GetWidth()+i].st = b->pPatch->ctrl(i, j).st;
567                         }
568                 }
569                 patch->SetExplicitlySubdivided( b->pPatch->explicitSubdivisions );
570                 if ( b->pPatch->explicitSubdivisions ) {
571                         patch->SetHorzSubdivisions( b->pPatch->horzSubdivisions );
572                         patch->SetVertSubdivisions( b->pPatch->vertSubdivisions );
573                 }
574                 patch->SetMaterial( b->pPatch->d_texture->GetName() );
575                 if ( b->pPatch->epairs ) {
576                         patch->epairs = *b->pPatch->epairs;
577                 }
578                 return patch;
579         }
580         else {
581                 idMapBrush *mapbrush = new idMapBrush;
582                 for ( face_t *f = b->brush_faces; f; f = f->next ) {
583                         idMapBrushSide *side = new idMapBrushSide;
584
585                         idPlane plane;
586                         if ( f->dirty ) {
587                                 f->planepts[0] -= origin;
588                                 f->planepts[1] -= origin;
589                                 f->planepts[2] -= origin;
590                                 plane.FromPoints( f->planepts[0], f->planepts[1], f->planepts[2], false );
591                                 f->planepts[0] += origin;
592                                 f->planepts[1] += origin;
593                                 f->planepts[2] += origin;
594                         } else {
595                                 plane = f->originalPlane;
596                         }
597                         side->SetPlane( plane );
598                         side->SetMaterial( f->d_texture->GetName() );
599                         idVec3 mat[2];
600                         mat[0][0] = f->brushprimit_texdef.coords[0][0];
601                         mat[0][1] = f->brushprimit_texdef.coords[0][1];
602                         mat[0][2] = f->brushprimit_texdef.coords[0][2];
603                         mat[1][0] = f->brushprimit_texdef.coords[1][0];
604                         mat[1][1] = f->brushprimit_texdef.coords[1][1];
605                         mat[1][2] = f->brushprimit_texdef.coords[1][2];
606                         side->SetTextureMatrix(mat);
607                         mapbrush->AddSide(side);
608                         mapbrush->epairs = b->epairs;
609                 }
610                 return mapbrush;
611         }
612 }
613
614 idMapEntity *EntityToMapEntity(entity_t *e, bool use_region, CWaitDlg *dlg) {
615         idMapEntity *mapent = new idMapEntity;
616         mapent->epairs = e->epairs;
617         idStr status;
618         int count = 0;
619         long lastUpdate = 0;
620         if ( !EntityHasModel( e ) ) {
621                 for ( brush_t *b = e->brushes.onext; b != &e->brushes; b = b->onext ) {
622                         count++;                                        
623                         if ( e->eclass->fixedsize && !b->entityModel ) {
624                                 continue;
625                         }
626                         if ( !use_region || !Map_IsBrushFiltered( b ) ) {
627                                 // Update 20 times a second
628                                 if ( GetTickCount() - lastUpdate > 50 ) {
629                                         lastUpdate = GetTickCount();
630                                         if ( b->pPatch ) {
631                                                 sprintf( status, "Adding primitive %i (patch)", count );
632                                                 dlg->SetText( status, true );
633                                         } else {
634                                                 sprintf( status, "Adding primitive %i (brush)", count );
635                                                 dlg->SetText( status, true );
636                                         }
637                                 }
638                                 idMapPrimitive *prim = BrushToMapPrimitive( b, e->origin );
639                                 if ( prim ) {
640                                         mapent->AddPrimitive( prim );
641                                 }
642                         }
643                  }
644         }
645         return mapent;
646 }
647
648 /*
649  =======================================================================================================================
650     Map_SaveFile
651  =======================================================================================================================
652  */
653 bool Map_SaveFile(const char *filename, bool use_region, bool autosave) {
654         entity_t        *e, *next;
655         idStr           temp;
656         int                     count;
657         brush_t         *b;
658         idStr status;
659
660         int len = strlen(filename);
661         WIN32_FIND_DATA FileData;
662         if (FindFirstFile(filename, &FileData) != INVALID_HANDLE_VALUE) { 
663                 // the file exists;
664                 if (len > 0 && GetFileAttributes(filename) & FILE_ATTRIBUTE_READONLY) {
665                         g_pParentWnd->MessageBox("File is read only", "Read Only", MB_OK);
666                         return false;
667                 }
668         }
669
670         if (filename == NULL || len == 0 || (filename && stricmp(filename, "unnamed.map") == 0)) {
671                 CFileDialog dlgSave(FALSE,"map",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"Map Files (*.map)|*.map||",AfxGetMainWnd());
672                 if (dlgSave.DoModal() == IDOK) {
673                         filename = dlgSave.m_ofn.lpstrFile;
674                         strcpy(currentmap, filename);
675                 }
676                 else {
677                         return false;
678                 }
679         }
680
681         MEMORYSTATUSEX statex;
682         statex.dwLength = sizeof (statex);
683         GlobalMemoryStatusEx (&statex);
684         if ( statex.dwMemoryLoad > 95 ) {
685                 g_pParentWnd->MessageBox("Physical memory is over 95% utilized. Consider saving and restarting", "Memory");
686         }
687
688         CWaitDlg dlg;
689         Pointfile_Clear();
690
691         temp = filename;
692         temp.BackSlashesToSlashes();
693
694         if ( !use_region ) {
695                 idStr backup;
696                 backup = temp;
697                 backup.StripFileExtension();
698                 backup.SetFileExtension( ".bak" );
699                 if ( _unlink(backup) != 0 && errno != 2 ) { // errno 2 means the file doesn't exist, which we don't care about
700                         g_pParentWnd->MessageBox( va("Unable to delete %s: %s", backup.c_str(), strerror(errno) ), "File Error" );
701                 }
702
703                 if ( rename(filename, backup) != 0 ) {
704                         g_pParentWnd->MessageBox( va("Unable to rename %s to %s: %s", filename, backup.c_str(), strerror(errno) ), "File Error" );
705                 }
706         }
707
708         common->Printf("Map_SaveFile: %s\n", filename);
709
710         idStr mapFile;
711         bool localFile = (strstr(filename, ":") != NULL);
712         if (autosave || localFile) {
713                 mapFile = filename;
714         } else {
715                 mapFile = fileSystem->OSPathToRelativePath( filename );
716         }
717
718         if (use_region) {
719                 AddRegionBrushes();
720         }
721
722         idMapFile map;
723         world_entity->origin.Zero();
724         idMapEntity *mapentity = EntityToMapEntity(world_entity, use_region, &dlg);
725         dlg.SetText("Saving worldspawn...");
726         map.AddEntity(mapentity);
727
728         if ( use_region ) {
729                 idStr buf;
730                 sprintf( buf, "{\n\"classname\"    \"info_player_start\"\n\"origin\"\t \"%i %i %i\"\n\"angle\"\t \"%i\"\n}\n", 
731                                         (int)g_pParentWnd->GetCamera()->Camera().origin[0],
732                                         (int)g_pParentWnd->GetCamera()->Camera().origin[1],
733                                         (int)g_pParentWnd->GetCamera()->Camera().origin[2],
734                                         (int)g_pParentWnd->GetCamera()->Camera().angles[YAW] );
735                 idLexer src( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
736                 src.LoadMemory( buf, buf.Length(), "regionbuf" );
737                 idMapEntity *playerstart = idMapEntity::Parse( src );
738                 map.AddEntity( playerstart );
739         }
740
741         count = -1;
742         for ( e = entities.next; e != &entities; e = next ) {
743                 count++;
744                 next = e->next;
745                 if (e->brushes.onext == &e->brushes) {
746                         Entity_Free(e); // no brushes left, so remove it
747                 }
748                 else {
749                         if (use_region) {
750                                 for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
751                                         if (!Map_IsBrushFiltered(b)) {
752                                                 break;  // got one
753                                         }
754                                 }
755
756                                 if (b == &e->brushes) {
757                                         continue;               // nothing visible
758                                 }
759
760                         }
761                         idVec3 origin;
762                         if (!GetVectorForKey(e, "origin", origin)) {
763                                 idStr text;
764                                 VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
765                                 sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
766                                 SetKeyValue(e, "origin", text);
767                         }
768
769                         if (use_region && !idStr::Icmp(ValueForKey(e, "classname"), "info_player_start")) {
770                                 continue;
771                         } 
772                 
773                         idStr classname = e->epairs.GetString("classname");
774                         sprintf(status, "Saving entity %i (%s)...", count, classname.c_str());
775                         dlg.SetText(status);
776
777                         map.AddEntity(EntityToMapEntity(e, use_region, &dlg));
778                         count++;
779                 }
780         }
781
782         mapFile.StripFileExtension();
783         idStr mapExt = (use_region) ? ".reg" : ".map";
784         sprintf(status, "Writing file %s.%s...", mapFile.c_str(), mapExt.c_str());
785         dlg.SetText(status);
786         map.Write(mapFile, mapExt, !(autosave || localFile));
787         mapModified = 0;
788
789         if (use_region) {
790                 RemoveRegionBrushes();
791         }
792
793         if (!strstr(temp, "autosave")) {
794                 Sys_SetTitle(temp);
795         }
796
797         Sys_Status("Saved.\n", 0);
798
799         return true;
800 }
801
802 /*
803  =======================================================================================================================
804     Map_New
805  =======================================================================================================================
806  */
807 void Map_New(void) {
808         common->Printf("Map_New\n");
809         Map_Free();
810
811         Patch_Cleanup();
812         g_Inspectors->entityDlg.SetEditEntity ( NULL );
813
814         world_entity = Entity_New();
815         world_entity->brushes.onext = world_entity->brushes.oprev = &world_entity->brushes;
816         SetKeyValue(world_entity, "classname", "worldspawn");
817         world_entity->eclass = Eclass_ForName("worldspawn", true);
818
819         g_pParentWnd->GetCamera()->Camera().angles[YAW] = 0;
820         g_pParentWnd->GetCamera()->Camera().angles[PITCH] = 0;
821         VectorCopy(vec3_origin, g_pParentWnd->GetCamera()->Camera().origin);
822         g_pParentWnd->GetCamera()->Camera().origin[2] = 48;
823         VectorCopy(vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin());
824
825         Map_RestoreBetween();
826
827         Sys_UpdateWindows(W_ALL);
828         mapModified = 0;
829
830         g_qeglobals.mapVersion = MAP_VERSION;
831
832 }
833
834
835 bool    region_active;
836 idVec3  region_mins(MIN_WORLD_COORD, MIN_WORLD_COORD, MIN_WORLD_COORD);
837 idVec3  region_maxs(MAX_WORLD_COORD, MAX_WORLD_COORD, MAX_WORLD_COORD);
838
839 brush_t *region_sides[6];
840
841 /*
842  =======================================================================================================================
843     AddRegionBrushes a regioned map will have temp walls put up at the region boundary
844  =======================================================================================================================
845  */
846 void AddRegionBrushes(void) {
847         idVec3          mins, maxs;
848         int                     i;
849         texdef_t        td;
850
851         if (!region_active) {
852                 return;
853         }
854
855         memset(&td, 0, sizeof(td));
856         td = g_qeglobals.d_texturewin.texdef;
857
858         // strcpy (td.name, "REGION");
859         td.SetName("textures/REGION");
860
861 const int REGION_WIDTH = 1024;
862
863
864         mins[0] = region_mins[0] - REGION_WIDTH;
865         maxs[0] = region_mins[0] + 1;
866         mins[1] = region_mins[1] - REGION_WIDTH;
867         maxs[1] = region_maxs[1] + REGION_WIDTH;
868         mins[2] = MIN_WORLD_COORD;
869         maxs[2] = MAX_WORLD_COORD;
870         region_sides[0] = Brush_Create(mins, maxs, &td);
871
872
873         mins[0] = region_maxs[0] - 1;
874         maxs[0] = region_maxs[0] + REGION_WIDTH;
875         region_sides[1] = Brush_Create(mins, maxs, &td);
876
877         mins[0] = region_mins[0] - REGION_WIDTH;
878         maxs[0] = region_maxs[0] + REGION_WIDTH;
879         mins[1] = region_mins[1] - REGION_WIDTH;
880         maxs[1] = region_mins[1] + 1;
881         region_sides[2] = Brush_Create(mins, maxs, &td);
882
883         mins[1] = region_maxs[1] - 1;
884         maxs[1] = region_maxs[1] + REGION_WIDTH;
885         region_sides[3] = Brush_Create(mins, maxs, &td);
886
887         mins = region_mins;
888         maxs = region_maxs;
889         maxs[2] = mins[2] + REGION_WIDTH;
890         region_sides[4] = Brush_Create(mins, maxs, &td);
891
892         mins = region_mins;
893         maxs = region_maxs;
894         mins[2] = maxs[2] - REGION_WIDTH;
895         region_sides[5] = Brush_Create(mins, maxs, &td);
896
897         for (i = 0; i < 6; i++) {
898                 Brush_AddToList(region_sides[i], &selected_brushes);
899                 Entity_LinkBrush(world_entity, region_sides[i]);
900                 Brush_Build(region_sides[i]);
901         }
902 }
903
904 /*
905  =======================================================================================================================
906  =======================================================================================================================
907  */
908 void RemoveRegionBrushes(void) {
909         int i;
910
911         if (!region_active) {
912                 return;
913         }
914
915         for (i = 0; i < 6; i++) {
916                 Brush_Free(region_sides[i]);
917         }
918 }
919
920 /*
921  =======================================================================================================================
922  =======================================================================================================================
923  */
924 bool Map_IsBrushFiltered(brush_t *b) {
925         int i;
926
927         if (!region_active) {
928                 return false;
929         }
930
931         for (i = 0; i < 3; i++) {
932                 if (b->mins[i] > region_maxs[i]) {
933                         return true;
934                 }
935
936                 if (b->maxs[i] < region_mins[i]) {
937                         return true;
938                 }
939         }
940
941         return false;
942 }
943
944 /*
945  =======================================================================================================================
946     Map_RegionOff Other filtering options may still be on
947  =======================================================================================================================
948  */
949 void Map_RegionOff(void) {
950         brush_t *b, *next;
951         int             i;
952
953         region_active = false;
954         for (i = 0; i < 3; i++) {
955                 region_maxs[i] = MAX_WORLD_COORD;       // 4096;
956                 region_mins[i] = MIN_WORLD_COORD;       // -4096;
957         }
958
959         for (b = filtered_brushes.next; b != &filtered_brushes; b = next) {
960                 next = b->next;
961                 if (Map_IsBrushFiltered(b)) {
962                         continue;                                               // still filtered
963                 }
964
965                 Brush_RemoveFromList(b);
966                 if (active_brushes.next == NULL || active_brushes.prev == NULL) {
967                         active_brushes.next = &active_brushes;
968                         active_brushes.prev = &active_brushes;
969                 }
970
971                 Brush_AddToList(b, &active_brushes);
972         }
973
974         Sys_UpdateWindows(W_ALL);
975 }
976
977 /*
978  =======================================================================================================================
979  =======================================================================================================================
980  */
981 void Map_ApplyRegion(void) {
982         brush_t *b, *next;
983
984         region_active = true;
985         for (b = active_brushes.next; b != &active_brushes; b = next) {
986                 next = b->next;
987                 if (!Map_IsBrushFiltered(b)) {
988                         continue;       // still filtered
989                 }
990
991                 Brush_RemoveFromList(b);
992                 Brush_AddToList(b, &filtered_brushes);
993         }
994
995         Sys_UpdateWindows(W_ALL);
996 }
997
998 /*
999  =======================================================================================================================
1000     Map_RegionSelectedBrushes
1001  =======================================================================================================================
1002  */
1003 void Map_RegionSelectedBrushes(void) {
1004         Map_RegionOff();
1005
1006         if (selected_brushes.next == &selected_brushes) {       // nothing selected
1007                 Sys_Status("Tried to region with no selection...\n");
1008                 return;
1009         }
1010
1011         region_active = true;
1012         Select_GetBounds(region_mins, region_maxs);
1013
1014         // move the entire active_brushes list to filtered_brushes
1015         filtered_brushes.next = active_brushes.next;
1016         filtered_brushes.prev = active_brushes.prev;
1017         filtered_brushes.next->prev = &filtered_brushes;
1018         filtered_brushes.prev->next = &filtered_brushes;
1019
1020         Patch_Deselect();
1021         // move the entire selected_brushes list to active_brushes
1022         active_brushes.next = selected_brushes.next;
1023         active_brushes.prev = selected_brushes.prev;
1024         active_brushes.next->prev = &active_brushes;
1025         active_brushes.prev->next = &active_brushes;
1026
1027         // clear selected_brushes
1028         selected_brushes.next = selected_brushes.prev = &selected_brushes;
1029
1030         Sys_UpdateWindows(W_ALL);
1031 }
1032
1033 /*
1034  =======================================================================================================================
1035     Map_RegionXY
1036  =======================================================================================================================
1037  */
1038 void Map_RegionXY(void) {
1039         Map_RegionOff();
1040
1041         region_mins[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] -
1042                 0.5 *
1043                 g_pParentWnd->GetXYWnd()->Width() /
1044                 g_pParentWnd->GetXYWnd()->Scale();
1045         region_maxs[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] +
1046                 0.5 *
1047                 g_pParentWnd->GetXYWnd()->Width() /
1048                 g_pParentWnd->GetXYWnd()->Scale();
1049         region_mins[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] -
1050                 0.5 *
1051                 g_pParentWnd->GetXYWnd()->Height() /
1052                 g_pParentWnd->GetXYWnd()->Scale();
1053         region_maxs[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] +
1054                 0.5 *
1055                 g_pParentWnd->GetXYWnd()->Height() /
1056                 g_pParentWnd->GetXYWnd()->Scale();
1057         region_mins[2] = MIN_WORLD_COORD;
1058         region_maxs[2] = MAX_WORLD_COORD;
1059         Map_ApplyRegion();
1060 }
1061
1062 /*
1063  =======================================================================================================================
1064     Map_RegionTallBrush
1065  =======================================================================================================================
1066  */
1067 void Map_RegionTallBrush(void) {
1068         brush_t *b;
1069
1070         if (!QE_SingleBrush()) {
1071                 return;
1072         }
1073
1074         b = selected_brushes.next;
1075
1076         Map_RegionOff();
1077
1078         VectorCopy(b->mins, region_mins);
1079         VectorCopy(b->maxs, region_maxs);
1080         region_mins[2] = MIN_WORLD_COORD;
1081         region_maxs[2] = MAX_WORLD_COORD;
1082
1083         Select_Delete();
1084         Map_ApplyRegion();
1085 }
1086
1087 /*
1088  =======================================================================================================================
1089     Map_RegionBrush
1090  =======================================================================================================================
1091  */
1092 void Map_RegionBrush(void) {
1093         brush_t *b;
1094
1095         if (!QE_SingleBrush()) {
1096                 return;
1097         }
1098
1099         b = selected_brushes.next;
1100
1101         Map_RegionOff();
1102
1103         VectorCopy(b->mins, region_mins);
1104         VectorCopy(b->maxs, region_maxs);
1105
1106         Select_Delete();
1107         Map_ApplyRegion();
1108 }
1109
1110 /*
1111  =======================================================================================================================
1112  =======================================================================================================================
1113  */
1114 void UniqueTargetName(idStr &rStr) {
1115         // make a unique target value
1116         int maxtarg = 0;
1117         for (entity_t * e = entities.next; e != &entities; e = e->next) {
1118                 const char      *tn = ValueForKey(e, "name");
1119                 if (tn && tn[0]) {
1120                         int targetnum = atoi(tn + 1);
1121                         if (targetnum > maxtarg) {
1122                                 maxtarg = targetnum;
1123                         }
1124                 }
1125                 else {
1126                         tn = ValueForKey(e, "target");
1127                         if (tn && tn[0]) {
1128                                 int targetnum = atoi(tn + 1);
1129                                 if (targetnum > maxtarg) {
1130                                         maxtarg = targetnum;
1131                                 }
1132                         }
1133                 }
1134         }
1135         
1136         sprintf(rStr, "t%i", maxtarg + 1);
1137 }
1138
1139 //
1140 // =======================================================================================================================
1141 //    Map_ImportFile Timo 09/01/99:: called by CXYWnd::Paste & Map_ImportFile if Map_ImportFile ( prefab ), the buffer
1142 //    may contain brushes in old format ( conversion needed )
1143 // =======================================================================================================================
1144 //
1145 void Map_ImportBuffer(char *buf, bool renameEntities) {
1146         entity_t        *ent;
1147         brush_t         *b = NULL;
1148         CPtrArray       ptrs;
1149
1150         Select_Deselect();
1151
1152         Undo_Start("import buffer");
1153
1154         g_qeglobals.d_parsed_brushes = 0;
1155         if (buf) {
1156                 CMapStringToString      mapStr;
1157                 StartTokenParsing(buf);
1158                 g_qeglobals.d_num_entities = 0;
1159
1160                 //
1161                 // Timo will be used in Entity_Parse to detect if a conversion between brush
1162                 // formats is needed
1163                 //
1164                 g_qeglobals.bNeedConvert = false;
1165                 g_qeglobals.bOldBrushes = false;
1166                 g_qeglobals.bPrimitBrushes = false;
1167                 g_qeglobals.mapVersion = 1.0;
1168
1169                 if (GetToken(true)) {
1170                         if (stricmp(token, "Version") == 0) {
1171                                 GetToken(false);
1172                                 g_qeglobals.mapVersion = atof(token);
1173                                 common->Printf("Map version: %1.2f\n", g_qeglobals.mapVersion);
1174                         } else {
1175                                 UngetToken();
1176                         }
1177                 }
1178
1179                 idDict RemappedNames;   // since I can't use "map <string, string>"... sigh. So much for STL...
1180
1181                 while (1) {
1182                         //
1183                         // use the selected brushes list as it's handy ent = Entity_Parse (false,
1184                         // &selected_brushes);
1185                         //
1186                         ent = Entity_Parse(false, &active_brushes);
1187                         if (!ent) {
1188                                 break;
1189                         }
1190
1191                         // end entity for undo
1192                         Undo_EndEntity(ent);
1193
1194                         // end brushes for undo
1195                         for (b = ent->brushes.onext; b && b != &ent->brushes; b = b->onext) {
1196                                 Undo_EndBrush(b);
1197                         }
1198
1199                         if (!strcmp(ValueForKey(ent, "classname"), "worldspawn")) {
1200                                 // world brushes need to be added to the current world entity
1201                                 b = ent->brushes.onext;
1202                                 while (b && b != &ent->brushes) {
1203                                         brush_t *bNext = b->onext;
1204                                         Entity_UnlinkBrush(b);
1205                                         Entity_LinkBrush(world_entity, b);
1206                                         ptrs.Add(b);
1207                                         b = bNext;
1208                                 }
1209                         }
1210                         else {
1211                                 // the following bit remaps conflicting target/targetname key/value pairs
1212                                 CString str = ValueForKey(ent, "target");
1213                                 CString strKey;
1214                                 CString strTarget("");
1215                                 if (str.GetLength() > 0) {
1216                                         if (FindEntity("target", str.GetBuffer(0))) {
1217                                                 if (!mapStr.Lookup(str, strKey)) {
1218                                                         idStr key;
1219                                                         UniqueTargetName(key);
1220                                                         strKey = key;
1221                                                         mapStr.SetAt(str, strKey);
1222                                                 }
1223
1224                                                 strTarget = strKey;
1225                                                 SetKeyValue(ent, "target", strTarget.GetBuffer(0));
1226                                         }
1227                                 }
1228
1229                                 /*
1230                                  * str = ValueForKey(ent, "name"); if (str.GetLength() > 0) { if
1231                                  * (FindEntity("name", str.GetBuffer(0))) { if (!mapStr.Lookup(str, strKey)) {
1232                                  * UniqueTargetName(strKey); mapStr.SetAt(str, strKey); } Entity_SetName(ent,
1233                                  * strKey.GetBuffer(0)); } }
1234                                  */
1235                                 CString cstrNameOld = ValueForKey(ent, "name");
1236                                 Entity_Name(ent, renameEntities);
1237                                 CString cstrNameNew = ValueForKey(ent, "name");
1238                                 if (cstrNameOld != cstrNameNew)
1239                                 {
1240                                         RemappedNames.Set(cstrNameOld, cstrNameNew);
1241                                 }
1242                                 //
1243                                 // if (strTarget.GetLength() > 0) SetKeyValue(ent, "target",
1244                                 // strTarget.GetBuffer(0));
1245                                 // add the entity to the end of the entity list
1246                                 //
1247                                 ent->next = &entities;
1248                                 ent->prev = entities.prev;
1249                                 entities.prev->next = ent;
1250                                 entities.prev = ent;
1251                                 g_qeglobals.d_num_entities++;
1252
1253                                 for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
1254                                         ptrs.Add(b);
1255                                 }
1256                         }
1257                 }
1258
1259                 // now iterate through the remapped names, and see if there are any target-connections that need remaking...
1260                 //
1261                 // (I could probably write this in half the size with STL, but WTF, work with what we have...)
1262                 //
1263                 int iNumKeyVals = RemappedNames.GetNumKeyVals();
1264                 for (int iKeyVal=0; iKeyVal < iNumKeyVals; iKeyVal++)
1265                 {
1266                         const idKeyValue *pKeyVal = RemappedNames.GetKeyVal( iKeyVal );
1267
1268                         LPCSTR psOldName = pKeyVal->GetKey().c_str();
1269                         LPCSTR psNewName = pKeyVal->GetValue().c_str();
1270
1271                         entity_t *pEntOld = FindEntity("name", psOldName);      // original ent we cloned from
1272                         entity_t *pEntNew = FindEntity("name", psNewName);      // cloned ent
1273
1274                         if (pEntOld && pEntNew)
1275                         {
1276                                 CString cstrTargetNameOld = ValueForKey(pEntOld, "target");
1277                                 if (!cstrTargetNameOld.IsEmpty())
1278                                 {
1279                                         // ok, this ent was targeted at another ent, so it's clone needs updating to point to
1280                                         //      the clone of that target, so...
1281                                         //
1282                                         entity_t *pEntOldTarget = FindEntity("name", cstrTargetNameOld);
1283                                         if ( pEntOldTarget )
1284                                         {
1285                                                 LPCSTR psNewTargetName = RemappedNames.GetString( cstrTargetNameOld );
1286                                                 if (psNewTargetName && psNewTargetName[0])
1287                                                 {                                                               
1288                                                         SetKeyValue(pEntNew, "target", psNewTargetName);
1289                                                 }
1290                                         }
1291                                 }
1292                         }
1293                 }
1294         }
1295
1296         //
1297         // ::ShowWindow(g_qeglobals.d_hwndEntity, FALSE);
1298         // ::LockWindowUpdate(g_qeglobals.d_hwndEntity);
1299         //
1300         g_bScreenUpdates = false;
1301         for (int i = 0; i < ptrs.GetSize(); i++) {
1302                 Brush_Build(reinterpret_cast < brush_t * > (ptrs[i]), true, false);
1303                 Select_Brush(reinterpret_cast < brush_t * > (ptrs[i]), true, false);
1304         }
1305
1306         // ::LockWindowUpdate(NULL);
1307         g_bScreenUpdates = true;
1308
1309         ptrs.RemoveAll();
1310
1311         //
1312         // reset the "need conversion" flag conversion to the good format done in
1313         // Map_BuildBrushData
1314         //
1315         g_qeglobals.bNeedConvert = false;
1316
1317         Sys_UpdateWindows(W_ALL);
1318
1319         // Sys_MarkMapModified();
1320         mapModified = 1;
1321
1322         Undo_End();
1323 }
1324
1325 //
1326 // =======================================================================================================================
1327 //    Map_ImportFile
1328 // =======================================================================================================================
1329 //
1330 void Map_ImportFile(char *fileName) {
1331         char    *buf;
1332         idStr   temp;
1333         Sys_BeginWait();
1334         temp = fileName;
1335         temp.BackSlashesToSlashes();
1336         if (LoadFile( temp, (void **) &buf) != -1) {
1337                 Map_ImportBuffer(buf);
1338                 Mem_Free( buf );
1339                 Map_BuildBrushData();
1340         }
1341
1342         Sys_UpdateWindows(W_ALL);
1343         mapModified = 1;
1344         Sys_EndWait();
1345 }
1346
1347 //
1348 // =======================================================================================================================
1349 //    Map_SaveSelected Saves selected world brushes and whole entities with partial/full selections
1350 // =======================================================================================================================
1351 //
1352 void Map_SaveSelected(char *fileName) {
1353         entity_t        *e, *next;
1354         FILE            *f;
1355         idStr           temp;
1356         int                     count;
1357
1358         temp = fileName;
1359         temp.BackSlashesToSlashes();
1360         f = fopen(temp, "w");
1361
1362         if ( !f ) {
1363                 common->Printf( "ERROR!!!! Couldn't open %s\n", temp.c_str() );
1364                 return;
1365         }
1366
1367         // write version
1368         g_qeglobals.mapVersion = MAP_VERSION;
1369         fprintf( f, "Version %1.2f\n", MAP_VERSION );
1370
1371         // write world entity second
1372         world_entity->origin.Zero();
1373         Entity_WriteSelected( world_entity, f );
1374
1375         // then write all other ents
1376         count = 1;
1377         for ( e = entities.next; e != &entities; e = next ) {
1378                 fprintf( f, "// entity %i\n", count );
1379                 count++;
1380                 Entity_WriteSelected( e, f );
1381                 next = e->next;
1382         }
1383
1384         fclose( f );
1385 }
1386
1387 //
1388 // =======================================================================================================================
1389 //    Map_SaveSelected Saves selected world brushes and whole entities with partial/full selections
1390 // =======================================================================================================================
1391 //
1392 void Map_SaveSelected(CMemFile *pMemFile, CMemFile *pPatchFile) {
1393         entity_t        *e, *next;
1394         int                     count;
1395         CString         strTemp;
1396
1397         // write version
1398         g_qeglobals.mapVersion = MAP_VERSION;
1399         MemFile_fprintf(pMemFile, "Version %1.2f\n", MAP_VERSION);
1400
1401         // write world entity first
1402         world_entity->origin.Zero();
1403         Entity_WriteSelected(world_entity, pMemFile);
1404
1405         // then write all other ents
1406         count = 1;
1407         for (e = entities.next; e != &entities; e = next) {
1408                 MemFile_fprintf(pMemFile, "// entity %i\n", count);
1409                 count++;
1410                 Entity_WriteSelected(e, pMemFile);
1411                 next = e->next;
1412         }
1413
1414         // if (pPatchFile) Patch_WriteFile(pPatchFile);
1415 }
1416
1417 /*
1418  =======================================================================================================================
1419  =======================================================================================================================
1420  */
1421
1422 /*
1423 ================
1424 WriteFileString
1425 ================
1426 */
1427 bool WriteFileString( FILE *fp, char *string, ... ) {
1428         long i;
1429         unsigned long u;
1430         double f;
1431         char *str;
1432         idStr buf;
1433         va_list argPtr;
1434
1435         va_start( argPtr, string );
1436
1437         while( *string ) {
1438                 switch( *string ) {
1439                         case '%':
1440                                 string++;
1441                                 while ( (*string >= '0' && *string <= '9') ||
1442                                                 *string == '.' || *string == '-' || *string == '+' || *string == '#') {
1443                                         string++;
1444                                 }
1445                                 switch( *string ) {
1446                                         case 'f':
1447                                         case 'e':
1448                                         case 'E':
1449                                         case 'g':
1450                                         case 'G':
1451                                                 f = va_arg( argPtr, double );
1452                                                 sprintf( buf, "%1.10f", f );
1453                                                 buf.StripTrailing( '0' );
1454                                                 buf.StripTrailing( '.' );
1455                                                 fprintf( fp, "%s", buf.c_str() );
1456                                                 break;
1457                                         case 'd':
1458                                         case 'i':
1459                                                 i = va_arg( argPtr, long );
1460                                                 fprintf( fp, "%d", i );
1461                                                 break;
1462                                         case 'u':
1463                                                 u = va_arg( argPtr, unsigned long );
1464                                                 fprintf( fp, "%u", u );
1465                                                 break;
1466                                         case 'o':
1467                                                 u = va_arg( argPtr, unsigned long );
1468                                                 fprintf( fp, "%o", u );
1469                                                 break;
1470                                         case 'x':
1471                                                 u = va_arg( argPtr, unsigned long );
1472                                                 fprintf( fp, "%x", u );
1473                                                 break;
1474                                         case 'X':
1475                                                 u = va_arg( argPtr, unsigned long );
1476                                                 fprintf( fp, "%X", u );
1477                                                 break;
1478                                         case 'c':
1479                                                 i = va_arg( argPtr, long );
1480                                                 fprintf( fp, "%c", (char) i );
1481                                                 break;
1482                                         case 's':
1483                                                 str = va_arg( argPtr, char * );
1484                                                 fprintf( fp, "%s", str );
1485                                                 break;
1486                                         case '%':
1487                                                 fprintf( fp, "%%" );
1488                                                 break;
1489                                         default:
1490                                                 common->Error( "WriteFileString: invalid %%%c", *string );
1491                                                 break;
1492                                 }
1493                                 string++;
1494                                 break;
1495                         case '\\':
1496                                 string++;
1497                                 switch( *string ) {
1498                                         case 't':
1499                                                 fprintf( fp, "\t" );
1500                                                 break;
1501                                         case 'n':
1502                                                 fprintf( fp, "\n" );
1503                                         default:
1504                                                 common->Error( "WriteFileString: unknown escape character \'%c\'", *string );
1505                                                 break;
1506                                 }
1507                                 string++;
1508                                 break;
1509                         default:
1510                                 fprintf( fp, "%c", *string );
1511                                 string++;
1512                                 break;
1513                 }
1514         }
1515
1516         va_end( argPtr );
1517
1518         return true;
1519 }
1520
1521 /*
1522 ================
1523 MemFile_fprintf
1524 ================
1525 */
1526 void MemFile_fprintf( CMemFile *pMemFile, const char *string, ... ) {
1527         char    Buffer[4096];
1528         long i;
1529         unsigned long u;
1530         double f;
1531         char *str;
1532         idStr buf, out;
1533         va_list argPtr;
1534
1535         char *buff = Buffer;
1536
1537         va_start( argPtr, string );
1538
1539         while( *string ) {
1540                 switch( *string ) {
1541                         case '%':
1542                                 string++;
1543                                 while ( (*string >= '0' && *string <= '9') ||
1544                                                 *string == '.' || *string == '-' || *string == '+' || *string == '#') {
1545                                         string++;
1546                                 }
1547                                 switch( *string ) {
1548                                         case 'f':
1549                                         case 'e':
1550                                         case 'E':
1551                                         case 'g':
1552                                         case 'G':
1553                                                 f = va_arg( argPtr, double );
1554                                                 sprintf( buf, "%1.10f", f );
1555                                                 buf.StripTrailing( '0' );
1556                                                 buf.StripTrailing( '.' );
1557                                                 sprintf( buff, "%s", buf.c_str() );
1558                                                 break;
1559                                         case 'd':
1560                                         case 'i':
1561                                                 i = va_arg( argPtr, long );
1562                                                 sprintf( buff, "%d", i );
1563                                                 break;
1564                                         case 'u':
1565                                                 u = va_arg( argPtr, unsigned long );
1566                                                 sprintf( buff, "%u", u );
1567                                                 break;
1568                                         case 'o':
1569                                                 u = va_arg( argPtr, unsigned long );
1570                                                 sprintf( buff, "%o", u );
1571                                                 break;
1572                                         case 'x':
1573                                                 u = va_arg( argPtr, unsigned long );
1574                                                 sprintf( buff, "%x", u );
1575                                                 break;
1576                                         case 'X':
1577                                                 u = va_arg( argPtr, unsigned long );
1578                                                 sprintf( buff, "%X", u );
1579                                                 break;
1580                                         case 'c':
1581                                                 i = va_arg( argPtr, long );
1582                                                 sprintf( buff, "%c", (char) i );
1583                                                 break;
1584                                         case 's':
1585                                                 str = va_arg( argPtr, char * );
1586                                                 sprintf( buff, "%s", str );
1587                                                 break;
1588                                         case '%':
1589                                                 sprintf( buff, "%%" );
1590                                                 break;
1591                                         default:
1592                                                 common->Error( "MemFile_fprintf: invalid %%%c", *string );
1593                                                 break;
1594                                 }
1595                                 string++;
1596                                 break;
1597                         case '\\':
1598                                 string++;
1599                                 switch( *string ) {
1600                                         case 't':
1601                                                 sprintf( buff, "\t" );
1602                                                 break;
1603                                         case 'n':
1604                                                 sprintf( buff, "\n" );
1605                                         default:
1606                                                 common->Error( "MemFile_fprintf: unknown escape character \'%c\'", *string );
1607                                                 break;
1608                                 }
1609                                 string++;
1610                                 break;
1611                         default:
1612                                 sprintf( buff, "%c", *string );
1613                                 string++;
1614                                 break;
1615                 }
1616
1617                 buff = Buffer + strlen(Buffer);
1618         }
1619
1620         va_end( argPtr );
1621
1622         out = Buffer;
1623         pMemFile->Write( out.c_str(), out.Length() );
1624 }