1 //------------------------------------------------------------------------
2 // LEVEL LOAD / SAVE / NEW
3 //------------------------------------------------------------------------
4 //
5 // Eureka DOOM Editor
6 //
7 // Copyright (C) 2001-2019 Andrew Apted
8 // Copyright (C) 2015 Ioan Chera
9 // Copyright (C) 1997-2003 Andr� Majorel et al
10 //
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License
13 // as published by the Free Software Foundation; either version 2
14 // of the License, or (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 //------------------------------------------------------------------------
22 //
23 // Based on Yadex which incorporated code from DEU 5.21 that was put
24 // in the public domain in 1994 by Rapha�l Quinet and Brendon Wyber.
25 //
26 //------------------------------------------------------------------------
27
28 #include "main.h"
29
30 #include "lib_adler.h"
31
32 #include "e_basis.h"
33 #include "e_checks.h"
34 #include "e_main.h" // CalculateLevelBounds()
35 #include "m_config.h"
36 #include "m_files.h"
37 #include "m_loadsave.h"
38 #include "m_nodes.h"
39 #include "m_udmf.h"
40 #include "r_subdiv.h"
41 #include "w_rawdef.h"
42 #include "w_wad.h"
43
44 #include "ui_window.h"
45 #include "ui_file.h"
46
47
48 int last_given_file;
49
50
51 // this is only used to prevent a M_SaveMap which happens inside
52 // CMD_BuildAllNodes from building that saved level twice.
53 bool inhibit_node_build;
54
55
56 static void SaveLevel(const char *level);
57
58 static const char * overwrite_message =
59 "The %s PWAD already contains this map. "
60 "This operation will destroy that map (overwrite it)."
61 "\n\n"
62 "Are you sure you want to continue?";
63
64
RemoveEditWad()65 void RemoveEditWad()
66 {
67 if (! edit_wad)
68 return;
69
70 MasterDir_Remove(edit_wad);
71 delete edit_wad;
72
73 edit_wad = NULL;
74 Pwad_name = NULL;
75 }
76
77
ReplaceEditWad(Wad_file * new_wad)78 static void ReplaceEditWad(Wad_file *new_wad)
79 {
80 RemoveEditWad();
81
82 edit_wad = new_wad;
83
84 Pwad_name = edit_wad->PathName();
85
86 MasterDir_Add(edit_wad);
87 }
88
89
FreshLevel()90 static void FreshLevel()
91 {
92 BA_ClearAll();
93
94 Sector *sec = new Sector;
95 Sectors.push_back(sec);
96
97 sec->SetDefaults();
98
99 for (int i = 0 ; i < 4 ; i++)
100 {
101 Vertex *v = new Vertex;
102 Vertices.push_back(v);
103
104 v->SetRawX((i >= 2) ? 256 : -256);
105 v->SetRawY((i==1 || i==2) ? 256 :-256);
106
107 SideDef *sd = new SideDef;
108 SideDefs.push_back(sd);
109
110 sd->SetDefaults(false);
111
112 LineDef *ld = new LineDef;
113 LineDefs.push_back(ld);
114
115 ld->start = i;
116 ld->end = (i+1) % 4;
117 ld->flags = MLF_Blocking;
118 ld->right = i;
119 }
120
121 for (int pl = 1 ; pl <= 4 ; pl++)
122 {
123 Thing *th = new Thing;
124 Things.push_back(th);
125
126 th->type = pl;
127 th->angle = 90;
128
129 th->SetRawX((pl == 1) ? 0 : (pl - 3) * 48);
130 th->SetRawY((pl == 1) ? 48 : (pl == 3) ? -48 : 0);
131 }
132
133 CalculateLevelBounds();
134
135 ZoomWholeMap();
136
137 Editor_DefaultState();
138 }
139
140
Project_AskFile(char * filename)141 static bool Project_AskFile(char *filename)
142 {
143 // this returns false if user cancelled
144
145 Fl_Native_File_Chooser chooser;
146
147 chooser.title("Pick file to create");
148 chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
149 chooser.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM);
150 chooser.filter("Wads\t*.wad");
151 chooser.directory(Main_FileOpFolder());
152
153 // Show native chooser
154 switch (chooser.show())
155 {
156 case -1:
157 LogPrintf("New Project: error choosing file:\n");
158 LogPrintf(" %s\n", chooser.errmsg());
159
160 DLG_Notify("Unable to create a new project:\n\n%s", chooser.errmsg());
161 return false;
162
163 case 1:
164 LogPrintf("New Project: cancelled by user\n");
165 return false;
166
167 default:
168 break; // OK
169 }
170
171 // if extension is missing, add ".wad"
172
173 strcpy(filename, chooser.filename());
174
175 char *pos = (char *)fl_filename_ext(filename);
176 if (! *pos)
177 strcat(filename, ".wad");
178
179 return true;
180 }
181
182
Project_ApplyChanges(UI_ProjectSetup * dialog)183 void Project_ApplyChanges(UI_ProjectSetup *dialog)
184 {
185 // grab the new information
186
187 Game_name = StringDup(dialog->game);
188 Port_name = StringDup(dialog->port);
189
190 SYS_ASSERT(Game_name);
191
192 Iwad_name = StringDup(M_QueryKnownIWAD(Game_name));
193 SYS_ASSERT(Iwad_name);
194
195 Level_format = dialog->map_format;
196 Udmf_namespace = dialog->name_space;
197
198 SYS_ASSERT(Level_format != MAPF_INVALID);
199
200 Resource_list.clear();
201
202 for (int i = 0 ; i < UI_ProjectSetup::RES_NUM ; i++)
203 {
204 if (dialog->res[i])
205 Resource_list.push_back(StringDup(dialog->res[i]));
206 }
207
208 Fl::wait(0.1);
209
210 Main_LoadResources();
211
212 Fl::wait(0.1);
213 }
214
215
CMD_ManageProject()216 void CMD_ManageProject()
217 {
218 UI_ProjectSetup * dialog = new UI_ProjectSetup(false /* new_project */, false /* is_startup */);
219
220 bool ok = dialog->Run();
221
222 if (ok)
223 {
224 Project_ApplyChanges(dialog);
225 }
226
227 delete dialog;
228 }
229
230
CMD_NewProject()231 void CMD_NewProject()
232 {
233 if (! Main_ConfirmQuit("create a new project"))
234 return;
235
236
237 /* first, ask for the output file */
238
239 char filename[FL_PATH_MAX];
240
241 if (! Project_AskFile(filename))
242 return;
243
244
245 /* second, query what Game, Port and Resources to use */
246
247 UI_ProjectSetup * dialog = new UI_ProjectSetup(true /* new_project */, false /* is_startup */);
248
249 bool ok = dialog->Run();
250
251 if (! ok)
252 {
253 delete dialog;
254 return;
255 }
256
257
258 /* third, delete file if it already exists
259 [ the file chooser should have asked for confirmation ]
260 */
261
262 if (FileExists(filename))
263 {
264 // TODO?? M_BackupWad(wad);
265
266 if (! FileDelete(filename))
267 {
268 DLG_Notify("Unable to delete the existing file.");
269
270 delete dialog;
271 return;
272 }
273
274 Fl::wait(0.1);
275 Fl::wait(0.1);
276 }
277
278
279 RemoveEditWad();
280
281 // this calls Main_LoadResources which resets the master directory
282 Project_ApplyChanges(dialog);
283
284 delete dialog;
285
286
287 // determine map name (same as first level in the IWAD)
288 const char *map_name = "MAP01";
289
290 short idx = game_wad->LevelFindFirst();
291
292 if (idx >= 0)
293 {
294 idx = game_wad->LevelHeader(idx);
295 map_name = game_wad->GetLump(idx)->Name();
296 }
297
298 LogPrintf("Creating New File : %s in %s\n", map_name, filename);
299
300
301 Wad_file * wad = Wad_file::Open(filename, 'w');
302
303 if (! wad)
304 {
305 DLG_Notify("Unable to create the new WAD file.");
306 return;
307 }
308
309 edit_wad = wad;
310 Pwad_name = edit_wad->PathName();
311
312 MasterDir_Add(edit_wad);
313
314
315 FreshLevel();
316
317 // save it now : sets Level_name and window title
318 SaveLevel(map_name);
319 }
320
321
MissingIWAD_Dialog()322 bool MissingIWAD_Dialog()
323 {
324 UI_ProjectSetup * dialog = new UI_ProjectSetup(false /* new_project */, true /* is_startup */);
325
326 bool ok = dialog->Run();
327
328 if (ok)
329 {
330 Game_name = StringDup(dialog->game);
331 SYS_ASSERT(Game_name);
332
333 Iwad_name = StringDup(M_QueryKnownIWAD(Game_name));
334 SYS_ASSERT(Iwad_name);
335 }
336
337 delete dialog;
338
339 return ok;
340 }
341
342
CMD_FreshMap()343 void CMD_FreshMap()
344 {
345 if (! edit_wad)
346 {
347 DLG_Notify("Cannot create a fresh map unless editing a PWAD.");
348 return;
349 }
350
351 if (edit_wad->IsReadOnly())
352 {
353 DLG_Notify("Cannot create a fresh map : file is read-only.");
354 return;
355 }
356
357 if (! Main_ConfirmQuit("create a fresh map"))
358 return;
359
360
361 UI_ChooseMap * dialog = new UI_ChooseMap(Level_name);
362
363 dialog->PopulateButtons(toupper(Level_name[0]), edit_wad);
364
365 const char *map_name = dialog->Run();
366
367 delete dialog;
368
369 // cancelled?
370 if (! map_name)
371 return;
372
373 // would this replace an existing map?
374 if (edit_wad->LevelFind(map_name) >= 0)
375 {
376 if (DLG_Confirm("Cancel|&Overwrite",
377 overwrite_message, "current") <= 0)
378 {
379 return;
380 }
381 }
382
383
384 M_BackupWad(edit_wad);
385
386 LogPrintf("Created NEW map : %s\n", map_name);
387
388 FreshLevel();
389
390 // save it now : sets Level_name and window title
391 SaveLevel(map_name);
392 }
393
394
395 //------------------------------------------------------------------------
396 // LOADING CODE
397 //------------------------------------------------------------------------
398
399 static Wad_file * load_wad;
400
401 // TODO ideally static, but needed by m_udmf.cc too
402 short loading_level;
403
404 static int bad_linedef_count;
405
406 static int bad_sector_refs;
407 static int bad_sidedef_refs;
408
409
410
UpperCaseShortStr(char * buf,int max_len)411 static void UpperCaseShortStr(char *buf, int max_len)
412 {
413 for (int i = 0 ; (i < max_len) && buf[i] ; i++)
414 {
415 buf[i] = toupper(buf[i]);
416 }
417 }
418
419
Load_LookupAndSeek(const char * name)420 Lump_c * Load_LookupAndSeek(const char *name)
421 {
422 short idx = load_wad->LevelLookupLump(loading_level, name);
423
424 if (idx < 0)
425 return NULL;
426
427 Lump_c *lump = load_wad->GetLump(idx);
428
429 if (! lump->Seek())
430 {
431 LogPrintf("WARNING: failed to seek to %s lump!\n", name);
432 }
433
434 return lump;
435 }
436
437
LoadVertices()438 static void LoadVertices()
439 {
440 Lump_c *lump = Load_LookupAndSeek("VERTEXES");
441 if (! lump)
442 FatalError("No vertex lump!\n");
443
444 int count = lump->Length() / sizeof(raw_vertex_t);
445
446 # if DEBUG_LOAD
447 PrintDebug("GetVertices: num = %d\n", count);
448 # endif
449
450 Vertices.reserve(count);
451
452 for (int i = 0 ; i < count ; i++)
453 {
454 raw_vertex_t raw;
455
456 if (! lump->Read(&raw, sizeof(raw)))
457 FatalError("Error reading vertices.\n");
458
459 Vertex *vert = new Vertex;
460
461 vert->raw_x = INT_TO_COORD(LE_S16(raw.x));
462 vert->raw_y = INT_TO_COORD(LE_S16(raw.y));
463
464 Vertices.push_back(vert);
465 }
466 }
467
468
LoadSectors()469 static void LoadSectors()
470 {
471 Lump_c *lump = Load_LookupAndSeek("SECTORS");
472 if (! lump)
473 FatalError("No sector lump!\n");
474
475 int count = lump->Length() / sizeof(raw_sector_t);
476
477 # if DEBUG_LOAD
478 PrintDebug("GetSectors: num = %d\n", count);
479 # endif
480
481 Sectors.reserve(count);
482
483 for (int i = 0 ; i < count ; i++)
484 {
485 raw_sector_t raw;
486
487 if (! lump->Read(&raw, sizeof(raw)))
488 FatalError("Error reading sectors.\n");
489
490 Sector *sec = new Sector;
491
492 sec->floorh = LE_S16(raw.floorh);
493 sec->ceilh = LE_S16(raw.ceilh);
494
495 UpperCaseShortStr(raw.floor_tex, 8);
496 UpperCaseShortStr(raw. ceil_tex, 8);
497
498 sec->floor_tex = BA_InternaliseShortStr(raw.floor_tex, 8);
499 sec->ceil_tex = BA_InternaliseShortStr(raw.ceil_tex, 8);
500
501 sec->light = LE_U16(raw.light);
502 sec->type = LE_U16(raw.type);
503 sec->tag = LE_S16(raw.tag);
504
505 Sectors.push_back(sec);
506 }
507 }
508
509
CreateFallbackSector()510 static void CreateFallbackSector()
511 {
512 LogPrintf("Creating a fallback sector.\n");
513
514 Sector *sec = new Sector;
515
516 sec->SetDefaults();
517
518 Sectors.push_back(sec);
519 }
520
CreateFallbackSideDef()521 static void CreateFallbackSideDef()
522 {
523 // we need a valid sector too!
524 if (NumSectors == 0)
525 CreateFallbackSector();
526
527 LogPrintf("Creating a fallback sidedef.\n");
528
529 SideDef *sd = new SideDef;
530
531 sd->SetDefaults(false);
532
533 SideDefs.push_back(sd);
534 }
535
CreateFallbackVertices()536 static void CreateFallbackVertices()
537 {
538 LogPrintf("Creating two fallback vertices.\n");
539
540 Vertex *v1 = new Vertex;
541 Vertex *v2 = new Vertex;
542
543 v1->raw_x = INT_TO_COORD(-777);
544 v1->raw_y = INT_TO_COORD(-777);
545
546 v2->raw_x = INT_TO_COORD(555);
547 v2->raw_y = INT_TO_COORD(555);
548
549 Vertices.push_back(v1);
550 Vertices.push_back(v2);
551 }
552
553
ValidateSidedefRefs(LineDef * ld,int num)554 void ValidateSidedefRefs(LineDef * ld, int num)
555 {
556 if (ld->right >= NumSideDefs || ld->left >= NumSideDefs)
557 {
558 LogPrintf("WARNING: linedef #%d has invalid sidedefs (%d / %d)\n",
559 num, ld->right, ld->left);
560
561 bad_sidedef_refs++;
562
563 // ensure we have a usable sidedef
564 if (NumSideDefs == 0)
565 CreateFallbackSideDef();
566
567 if (ld->right >= NumSideDefs)
568 ld->right = 0;
569
570 if (ld->left >= NumSideDefs)
571 ld->left = 0;
572 }
573 }
574
ValidateVertexRefs(LineDef * ld,int num)575 void ValidateVertexRefs(LineDef *ld, int num)
576 {
577 if (ld->start >= NumVertices || ld->end >= NumVertices ||
578 ld->start == ld->end)
579 {
580 LogPrintf("WARNING: linedef #%d has invalid vertices (%d -> %d)\n",
581 num, ld->start, ld->end);
582
583 bad_linedef_count++;
584
585 // ensure we have a valid vertex
586 if (NumVertices < 2)
587 CreateFallbackVertices();
588
589 ld->start = 0;
590 ld->end = NumVertices - 1;
591 }
592 }
593
ValidateSectorRef(SideDef * sd,int num)594 void ValidateSectorRef(SideDef *sd, int num)
595 {
596 if (sd->sector >= NumSectors)
597 {
598 LogPrintf("WARNING: sidedef #%d has invalid sector (%d)\n",
599 num, sd->sector);
600
601 bad_sector_refs++;
602
603 // ensure we have a valid sector
604 if (NumSectors == 0)
605 CreateFallbackSector();
606
607 sd->sector = 0;
608 }
609 }
610
611
LoadHeader()612 static void LoadHeader()
613 {
614 Lump_c *lump = load_wad->GetLump(load_wad->LevelHeader(loading_level));
615
616 int length = lump->Length();
617
618 if (length == 0)
619 return;
620
621 HeaderData.resize(length);
622
623 if (! lump->Seek())
624 FatalError("Error seeking to header lump!\n");
625
626 if (! lump->Read(& HeaderData[0], length))
627 FatalError("Error reading header lump.\n");
628 }
629
630
LoadBehavior()631 static void LoadBehavior()
632 {
633 // IOANCH 9/2015: support Hexen maps
634 Lump_c *lump = Load_LookupAndSeek("BEHAVIOR");
635 if (! lump)
636 FatalError("No BEHAVIOR lump!\n");
637
638 int length = lump->Length();
639
640 BehaviorData.resize(length);
641
642 if (length == 0)
643 return;
644
645 if (! lump->Read(& BehaviorData[0], length))
646 FatalError("Error reading BEHAVIOR.\n");
647 }
648
649
LoadScripts()650 static void LoadScripts()
651 {
652 // the SCRIPTS lump is usually absent
653 Lump_c *lump = Load_LookupAndSeek("SCRIPTS");
654 if (! lump)
655 return;
656
657 int length = lump->Length();
658
659 ScriptsData.resize(length);
660
661 if (length == 0)
662 return;
663
664 if (! lump->Read(& ScriptsData[0], length))
665 FatalError("Error reading SCRIPTS.\n");
666 }
667
668
LoadThings()669 static void LoadThings()
670 {
671 Lump_c *lump = Load_LookupAndSeek("THINGS");
672 if (! lump)
673 FatalError("No things lump!\n");
674
675 int count = lump->Length() / sizeof(raw_thing_t);
676
677 # if DEBUG_LOAD
678 PrintDebug("GetThings: num = %d\n", count);
679 # endif
680
681 for (int i = 0 ; i < count ; i++)
682 {
683 raw_thing_t raw;
684
685 if (! lump->Read(&raw, sizeof(raw)))
686 FatalError("Error reading things.\n");
687
688 Thing *th = new Thing;
689
690 th->raw_x = INT_TO_COORD(LE_S16(raw.x));
691 th->raw_y = INT_TO_COORD(LE_S16(raw.y));
692
693 th->angle = LE_U16(raw.angle);
694 th->type = LE_U16(raw.type);
695 th->options = LE_U16(raw.options);
696
697 Things.push_back(th);
698 }
699 }
700
701
702 // IOANCH 9/2015
LoadThings_Hexen()703 static void LoadThings_Hexen()
704 {
705 Lump_c *lump = Load_LookupAndSeek("THINGS");
706 if (! lump)
707 FatalError("No things lump!\n");
708
709 int count = lump->Length() / sizeof(raw_hexen_thing_t);
710
711 # if DEBUG_LOAD
712 PrintDebug("GetThings: num = %d\n", count);
713 # endif
714
715 for (int i = 0; i < count; ++i)
716 {
717 raw_hexen_thing_t raw;
718
719 if (! lump->Read(&raw, sizeof(raw)))
720 FatalError("Error reading things.\n");
721
722 Thing *th = new Thing;
723
724 th->tid = LE_S16(raw.tid);
725 th->raw_x = INT_TO_COORD(LE_S16(raw.x));
726 th->raw_y = INT_TO_COORD(LE_S16(raw.y));
727 th->raw_h = INT_TO_COORD(LE_S16(raw.height));
728
729 th->angle = LE_U16(raw.angle);
730 th->type = LE_U16(raw.type);
731 th->options = LE_U16(raw.options);
732
733 th->special = raw.special;
734 th->arg1 = raw.args[0];
735 th->arg2 = raw.args[1];
736 th->arg3 = raw.args[2];
737 th->arg4 = raw.args[3];
738 th->arg5 = raw.args[4];
739
740 Things.push_back(th);
741 }
742 }
743
744
LoadSideDefs()745 static void LoadSideDefs()
746 {
747 Lump_c *lump = Load_LookupAndSeek("SIDEDEFS");
748 if (! lump)
749 FatalError("No sidedefs lump!\n");
750
751 int count = lump->Length() / sizeof(raw_sidedef_t);
752
753 # if DEBUG_LOAD
754 PrintDebug("GetSidedefs: num = %d\n", count);
755 # endif
756
757 for (int i = 0 ; i < count ; i++)
758 {
759 raw_sidedef_t raw;
760
761 if (! lump->Read(&raw, sizeof(raw)))
762 FatalError("Error reading sidedefs.\n");
763
764 SideDef *sd = new SideDef;
765
766 sd->x_offset = LE_S16(raw.x_offset);
767 sd->y_offset = LE_S16(raw.y_offset);
768
769 // convert empty names to the "-" null texture
770 if (raw.upper_tex[0] == 0) strcpy(raw.upper_tex, "-");
771 if (raw.lower_tex[0] == 0) strcpy(raw.lower_tex, "-");
772 if (raw. mid_tex[0] == 0) strcpy(raw. mid_tex, "-");
773
774 UpperCaseShortStr(raw.upper_tex, 8);
775 UpperCaseShortStr(raw.lower_tex, 8);
776 UpperCaseShortStr(raw. mid_tex, 8);
777
778 sd->upper_tex = BA_InternaliseShortStr(raw.upper_tex, 8);
779 sd->lower_tex = BA_InternaliseShortStr(raw.lower_tex, 8);
780 sd-> mid_tex = BA_InternaliseShortStr(raw. mid_tex, 8);
781
782 sd->sector = LE_U16(raw.sector);
783
784 ValidateSectorRef(sd, i);
785
786 SideDefs.push_back(sd);
787 }
788 }
789
790
LoadLineDefs()791 static void LoadLineDefs()
792 {
793 Lump_c *lump = Load_LookupAndSeek("LINEDEFS");
794 if (! lump)
795 FatalError("No linedefs lump!\n");
796
797 int count = lump->Length() / sizeof(raw_linedef_t);
798
799 # if DEBUG_LOAD
800 PrintDebug("GetLinedefs: num = %d\n", count);
801 # endif
802
803 if (count == 0)
804 return;
805
806 for (int i = 0 ; i < count ; i++)
807 {
808 raw_linedef_t raw;
809
810 if (! lump->Read(&raw, sizeof(raw)))
811 FatalError("Error reading linedefs.\n");
812
813 LineDef *ld = new LineDef;
814
815 ld->start = LE_U16(raw.start);
816 ld->end = LE_U16(raw.end);
817
818 ld->flags = LE_U16(raw.flags);
819 ld->type = LE_U16(raw.type);
820 ld->tag = LE_S16(raw.tag);
821
822 ld->right = LE_U16(raw.right);
823 ld->left = LE_U16(raw.left);
824
825 if (ld->right == 0xFFFF) ld->right = -1;
826 if (ld-> left == 0xFFFF) ld-> left = -1;
827
828 ValidateVertexRefs(ld, i);
829 ValidateSidedefRefs(ld, i);
830
831 LineDefs.push_back(ld);
832 }
833 }
834
835
836 // IOANCH 9/2015
LoadLineDefs_Hexen()837 static void LoadLineDefs_Hexen()
838 {
839 Lump_c *lump = Load_LookupAndSeek("LINEDEFS");
840 if (! lump)
841 FatalError("No linedefs lump!\n");
842
843 int count = lump->Length() / sizeof(raw_hexen_linedef_t);
844
845 # if DEBUG_LOAD
846 PrintDebug("GetLinedefs: num = %d\n", count);
847 # endif
848
849 if (count == 0)
850 return;
851
852 for (int i = 0 ; i < count ; i++)
853 {
854 raw_hexen_linedef_t raw;
855
856 if (! lump->Read(&raw, sizeof(raw)))
857 FatalError("Error reading linedefs.\n");
858
859 LineDef *ld = new LineDef;
860
861 ld->start = LE_U16(raw.start);
862 ld->end = LE_U16(raw.end);
863
864 ld->flags = LE_U16(raw.flags);
865 ld->type = raw.type;
866 ld->tag = raw.args[0];
867 ld->arg2 = raw.args[1];
868 ld->arg3 = raw.args[2];
869 ld->arg4 = raw.args[3];
870 ld->arg5 = raw.args[4];
871
872 ld->right = LE_U16(raw.right);
873 ld->left = LE_U16(raw.left);
874
875 if (ld->right == 0xFFFF) ld->right = -1;
876 if (ld-> left == 0xFFFF) ld-> left = -1;
877
878 ValidateVertexRefs(ld, i);
879 ValidateSidedefRefs(ld, i);
880
881 LineDefs.push_back(ld);
882 }
883 }
884
885
RemoveUnusedVerticesAtEnd()886 static void RemoveUnusedVerticesAtEnd()
887 {
888 if (NumVertices == 0)
889 return;
890
891 bitvec_c used_verts(NumVertices);
892
893 for (int i = 0 ; i < NumLineDefs ; i++)
894 {
895 used_verts.set(LineDefs[i]->start);
896 used_verts.set(LineDefs[i]->end);
897 }
898
899 int new_count = NumVertices;
900
901 while (new_count > 2 && !used_verts.get(new_count-1))
902 new_count--;
903
904 // we directly modify the vertex array here (which is not
905 // normally kosher, but level loading is a special case).
906 if (new_count < NumVertices)
907 {
908 LogPrintf("Removing %d unused vertices at end\n", NumVertices - new_count);
909
910 for (int i = new_count ; i < NumVertices ; i++)
911 delete Vertices[i];
912
913 Vertices.resize(new_count);
914 }
915 }
916
917
ShowLoadProblem()918 static void ShowLoadProblem()
919 {
920 LogPrintf("Map load problems:\n");
921 LogPrintf(" %d linedefs with bad vertex refs\n", bad_linedef_count);
922 LogPrintf(" %d linedefs with bad sidedef refs\n", bad_sidedef_refs);
923 LogPrintf(" %d sidedefs with bad sector refs\n", bad_sector_refs);
924
925 static char message[MSG_BUF_LEN];
926
927 if (bad_linedef_count > 0)
928 {
929 snprintf(message, sizeof(message),
930 "Found %d linedefs with bad vertex references.\n"
931 "These references have been replaced.",
932 bad_linedef_count);
933 }
934 else
935 {
936 snprintf(message, sizeof(message),
937 "Found %d bad sector refs, %d bad sidedef refs.\n"
938 "These references have been replaced.",
939 bad_sector_refs, bad_sidedef_refs);
940 }
941
942 DLG_Notify("Map validation report:\n\n%s", message);
943 }
944
945
GetLevelFormat(Wad_file * wad,const char * level)946 void GetLevelFormat(Wad_file *wad, const char *level)
947 {
948 int lev_num = wad->LevelFind(level);
949
950 // ignore failure here, it will be caught later
951 if (lev_num >= 0)
952 {
953 Level_format = wad->LevelFormat(lev_num);
954 }
955 }
956
957
958 //
959 // Read in the level data
960 //
961
LoadLevel(Wad_file * wad,const char * level)962 void LoadLevel(Wad_file *wad, const char *level)
963 {
964 short lev_num = wad->LevelFind(level);
965
966 if (lev_num < 0)
967 FatalError("No such map: %s\n", level);
968
969 LoadLevelNum(wad, lev_num);
970
971 // reset various editor state
972 Editor_ClearAction();
973 Selection_InvalidateLast();
974
975 edit.Selected->clear_all();
976 edit.highlight.clear();
977
978 main_win->UpdateTotals();
979 main_win->UpdateGameInfo();
980 main_win->InvalidatePanelObj();
981 main_win->redraw();
982
983 if (main_win)
984 {
985 main_win->SetTitle(wad->PathName(), level, wad->IsReadOnly());
986
987 // load the user state associated with this map
988 crc32_c adler_crc;
989
990 BA_LevelChecksum(adler_crc);
991
992 if (! M_LoadUserState())
993 {
994 M_DefaultUserState();
995 }
996 }
997
998 Level_name = StringUpper(level);
999
1000 Status_Set("Loaded %s", Level_name);
1001
1002 RedrawMap();
1003 }
1004
1005
LoadLevelNum(Wad_file * wad,short lev_num)1006 void LoadLevelNum(Wad_file *wad, short lev_num)
1007 {
1008 load_wad = wad;
1009 loading_level = lev_num;
1010
1011 Level_format = load_wad->LevelFormat(loading_level);
1012
1013 BA_ClearAll();
1014
1015 bad_linedef_count = 0;
1016 bad_sector_refs = 0;
1017 bad_sidedef_refs = 0;
1018
1019 LoadHeader();
1020
1021 if (Level_format == MAPF_UDMF)
1022 {
1023 UDMF_LoadLevel();
1024 }
1025 else
1026 {
1027 if (Level_format == MAPF_Hexen)
1028 LoadThings_Hexen();
1029 else
1030 LoadThings();
1031
1032 LoadVertices();
1033 LoadSectors();
1034 LoadSideDefs();
1035
1036 if (Level_format == MAPF_Hexen)
1037 {
1038 LoadLineDefs_Hexen();
1039
1040 LoadBehavior();
1041 LoadScripts();
1042 }
1043 else
1044 {
1045 LoadLineDefs();
1046 }
1047 }
1048
1049 if (bad_linedef_count || bad_sector_refs || bad_sidedef_refs)
1050 {
1051 ShowLoadProblem();
1052 }
1053
1054 // Node builders create a lot of new vertices for segs.
1055 // However they just get in the way for editing, so remove them.
1056 RemoveUnusedVerticesAtEnd();
1057
1058 SideDefs_Unpack(true);
1059
1060 CalculateLevelBounds();
1061 Subdiv_InvalidateAll();
1062
1063 MadeChanges = 0;
1064 }
1065
1066
1067 //
1068 // open a new wad file.
1069 // when 'map_name' is not NULL, try to open that map.
1070 //
OpenFileMap(const char * filename,const char * map_name)1071 void OpenFileMap(const char *filename, const char *map_name)
1072 {
1073 if (! Main_ConfirmQuit("open another map"))
1074 return;
1075
1076
1077 Wad_file *wad = NULL;
1078
1079 // make sure file exists, as Open() with 'a' would create it otherwise
1080 if (FileExists(filename))
1081 {
1082 wad = Wad_file::Open(filename, 'a');
1083 }
1084
1085 if (! wad)
1086 {
1087 // FIXME: get an error message, add it here
1088
1089 DLG_Notify("Unable to open that WAD file.");
1090 return;
1091 }
1092
1093
1094 // determine which level to use
1095 int lev_num = -1;
1096
1097 if (map_name)
1098 {
1099 lev_num = wad->LevelFind(map_name);
1100 }
1101
1102 if (lev_num < 0)
1103 {
1104 lev_num = wad->LevelFindFirst();
1105 }
1106
1107 if (lev_num < 0)
1108 {
1109 DLG_Notify("No levels found in that WAD.");
1110
1111 delete wad;
1112 return;
1113 }
1114
1115 if (wad->FindLump(EUREKA_LUMP))
1116 {
1117 if (! M_ParseEurekaLump(wad))
1118 {
1119 delete wad;
1120 return;
1121 }
1122 }
1123
1124
1125 /* OK, open it */
1126
1127
1128 // this wad replaces the current PWAD
1129 ReplaceEditWad(wad);
1130
1131 SYS_ASSERT(edit_wad == wad);
1132
1133
1134 // always grab map_name from the actual level
1135 {
1136 short idx = edit_wad->LevelHeader(lev_num);
1137 map_name = edit_wad->GetLump(idx)->Name();
1138 }
1139
1140 LogPrintf("Loading Map : %s of %s\n", map_name, edit_wad->PathName());
1141
1142 LoadLevel(edit_wad, map_name);
1143
1144 // must be after LoadLevel as we need the Level_format
1145 Main_LoadResources();
1146 }
1147
1148
CMD_OpenMap()1149 void CMD_OpenMap()
1150 {
1151 if (! Main_ConfirmQuit("open another map"))
1152 return;
1153
1154
1155 UI_OpenMap * dialog = new UI_OpenMap();
1156
1157 const char *map_name = NULL;
1158 bool did_load = false;
1159
1160 Wad_file *wad = dialog->Run(&map_name, &did_load);
1161
1162 delete dialog;
1163
1164 if (! wad) // cancelled
1165 return;
1166
1167
1168 // this shouldn't happen -- but just in case...
1169 if (wad->LevelFind(map_name) < 0)
1170 {
1171 DLG_Notify("Hmmmm, cannot find that map !?!");
1172
1173 delete wad;
1174 return;
1175 }
1176
1177
1178 if (did_load && wad->FindLump(EUREKA_LUMP))
1179 {
1180 if (! M_ParseEurekaLump(wad))
1181 {
1182 delete wad;
1183 return;
1184 }
1185 }
1186
1187
1188 // does this wad replace the currently edited wad?
1189 bool new_resources = false;
1190
1191 if (did_load)
1192 {
1193 SYS_ASSERT(wad != edit_wad);
1194 SYS_ASSERT(wad != game_wad);
1195
1196 ReplaceEditWad(wad);
1197
1198 new_resources = true;
1199 }
1200 // ...or does it remove the edit_wad? (e.g. wad == game_wad)
1201 else if (edit_wad && wad != edit_wad)
1202 {
1203 RemoveEditWad();
1204
1205 new_resources = true;
1206 }
1207
1208 LogPrintf("Loading Map : %s of %s\n", map_name, wad->PathName());
1209
1210 LoadLevel(wad, map_name);
1211
1212 if (new_resources)
1213 {
1214 // this can invalidate the 'wad' var (since it closes/reopens
1215 // all wads in the master_dir), so it MUST be after LoadLevel.
1216 // less importantly, we need to know the Level_format.
1217 Main_LoadResources();
1218 }
1219 }
1220
1221
CMD_GivenFile()1222 void CMD_GivenFile()
1223 {
1224 const char *mode = EXEC_Param[0];
1225
1226 int index = last_given_file;
1227
1228 if (! mode[0] || y_stricmp(mode, "current") == 0)
1229 {
1230 // index = index + 0;
1231 }
1232 else if (y_stricmp(mode, "next") == 0)
1233 {
1234 index = index + 1;
1235 }
1236 else if (y_stricmp(mode, "prev") == 0)
1237 {
1238 index = index - 1;
1239 }
1240 else if (y_stricmp(mode, "first") == 0)
1241 {
1242 index = 0;
1243 }
1244 else if (y_stricmp(mode, "last") == 0)
1245 {
1246 index = (int)Pwad_list.size() - 1;
1247 }
1248 else
1249 {
1250 Beep("GivenFile: unknown keyword: %s", mode);
1251 return;
1252 }
1253
1254 if (index < 0 || index >= (int)Pwad_list.size())
1255 {
1256 Beep("No more files");
1257 return;
1258 }
1259
1260 last_given_file = index;
1261
1262 // TODO: remember last map visited in this wad
1263
1264 OpenFileMap(Pwad_list[index], NULL);
1265 }
1266
1267
CMD_FlipMap()1268 void CMD_FlipMap()
1269 {
1270 const char *mode = EXEC_Param[0];
1271
1272 if (! mode[0])
1273 {
1274 Beep("FlipMap: missing keyword");
1275 return;
1276 }
1277
1278
1279 if (! Main_ConfirmQuit("open another map"))
1280 return;
1281
1282
1283 Wad_file *wad = edit_wad ? edit_wad : game_wad;
1284
1285 // the level might not be found (lev_num < 0) -- that is OK
1286 int lev_idx = wad->LevelFind(Level_name);
1287 int max_idx = wad->LevelCount() - 1;
1288
1289 if (max_idx < 0)
1290 {
1291 Beep("No maps ?!?");
1292 return;
1293 }
1294
1295 SYS_ASSERT(lev_idx <= max_idx);
1296
1297
1298 if (y_stricmp(mode, "next") == 0)
1299 {
1300 if (lev_idx < 0)
1301 lev_idx = 0;
1302 else if (lev_idx < max_idx)
1303 lev_idx++;
1304 else
1305 {
1306 Beep("No more maps");
1307 return;
1308 }
1309 }
1310 else if (y_stricmp(mode, "prev") == 0)
1311 {
1312 if (lev_idx < 0)
1313 lev_idx = max_idx;
1314 else if (lev_idx > 0)
1315 lev_idx--;
1316 else
1317 {
1318 Beep("No more maps");
1319 return;
1320 }
1321 }
1322 else if (y_stricmp(mode, "first") == 0)
1323 {
1324 lev_idx = 0;
1325 }
1326 else if (y_stricmp(mode, "last") == 0)
1327 {
1328 lev_idx = max_idx;
1329 }
1330 else
1331 {
1332 Beep("FlipMap: unknown keyword: %s", mode);
1333 return;
1334 }
1335
1336 SYS_ASSERT(lev_idx >= 0);
1337 SYS_ASSERT(lev_idx <= max_idx);
1338
1339
1340 short lump_idx = wad->LevelHeader(lev_idx);
1341 Lump_c * lump = wad->GetLump(lump_idx);
1342 const char *map_name = lump->Name();
1343
1344 LogPrintf("Flipping Map to : %s\n", map_name);
1345
1346 LoadLevel(wad, map_name);
1347 }
1348
1349
1350 //------------------------------------------------------------------------
1351 // SAVING CODE
1352 //------------------------------------------------------------------------
1353
1354 static short saving_level;
1355
1356
SaveHeader(const char * level)1357 static void SaveHeader(const char *level)
1358 {
1359 int size = (int)HeaderData.size();
1360
1361 Lump_c *lump = edit_wad->AddLevel(level, size, &saving_level);
1362
1363 if (size > 0)
1364 {
1365 lump->Write(& HeaderData[0], size);
1366 }
1367
1368 lump->Finish();
1369 }
1370
1371
SaveBehavior()1372 static void SaveBehavior()
1373 {
1374 int size = (int)BehaviorData.size();
1375
1376 Lump_c *lump = edit_wad->AddLump("BEHAVIOR", size);
1377
1378 if (size > 0)
1379 {
1380 lump->Write(& BehaviorData[0], size);
1381 }
1382
1383 lump->Finish();
1384 }
1385
1386
SaveScripts()1387 static void SaveScripts()
1388 {
1389 int size = (int)ScriptsData.size();
1390
1391 if (size > 0)
1392 {
1393 Lump_c *lump = edit_wad->AddLump("SCRIPTS", size);
1394
1395 lump->Write(& ScriptsData[0], size);
1396 lump->Finish();
1397 }
1398 }
1399
1400
SaveVertices()1401 static void SaveVertices()
1402 {
1403 int size = NumVertices * (int)sizeof(raw_vertex_t);
1404
1405 Lump_c *lump = edit_wad->AddLump("VERTEXES", size);
1406
1407 for (int i = 0 ; i < NumVertices ; i++)
1408 {
1409 const Vertex *vert = Vertices[i];
1410
1411 raw_vertex_t raw;
1412
1413 raw.x = LE_S16(COORD_TO_INT(vert->raw_x));
1414 raw.y = LE_S16(COORD_TO_INT(vert->raw_y));
1415
1416 lump->Write(&raw, sizeof(raw));
1417 }
1418
1419 lump->Finish();
1420 }
1421
1422
SaveSectors()1423 static void SaveSectors()
1424 {
1425 int size = NumSectors * (int)sizeof(raw_sector_t);
1426
1427 Lump_c *lump = edit_wad->AddLump("SECTORS", size);
1428
1429 for (int i = 0 ; i < NumSectors ; i++)
1430 {
1431 const Sector *sec = Sectors[i];
1432
1433 raw_sector_t raw;
1434
1435 raw.floorh = LE_S16(sec->floorh);
1436 raw.ceilh = LE_S16(sec->ceilh);
1437
1438 W_StoreString(raw.floor_tex, sec->FloorTex(), sizeof(raw.floor_tex));
1439 W_StoreString(raw.ceil_tex, sec->CeilTex(), sizeof(raw.ceil_tex));
1440
1441 raw.light = LE_U16(sec->light);
1442 raw.type = LE_U16(sec->type);
1443 raw.tag = LE_U16(sec->tag);
1444
1445 lump->Write(&raw, sizeof(raw));
1446 }
1447
1448 lump->Finish();
1449 }
1450
1451
SaveThings()1452 static void SaveThings()
1453 {
1454 int size = NumThings * (int)sizeof(raw_thing_t);
1455
1456 Lump_c *lump = edit_wad->AddLump("THINGS", size);
1457
1458 for (int i = 0 ; i < NumThings ; i++)
1459 {
1460 const Thing *th = Things[i];
1461
1462 raw_thing_t raw;
1463
1464 raw.x = LE_S16(COORD_TO_INT(th->raw_x));
1465 raw.y = LE_S16(COORD_TO_INT(th->raw_y));
1466
1467 raw.angle = LE_U16(th->angle);
1468 raw.type = LE_U16(th->type);
1469 raw.options = LE_U16(th->options);
1470
1471 lump->Write(&raw, sizeof(raw));
1472 }
1473
1474 lump->Finish();
1475 }
1476
1477
1478 // IOANCH 9/2015
SaveThings_Hexen()1479 static void SaveThings_Hexen()
1480 {
1481 int size = NumThings * (int)sizeof(raw_hexen_thing_t);
1482
1483 Lump_c *lump = edit_wad->AddLump("THINGS", size);
1484
1485 for (int i = 0 ; i < NumThings ; i++)
1486 {
1487 const Thing *th = Things[i];
1488
1489 raw_hexen_thing_t raw;
1490
1491 raw.tid = LE_S16(th->tid);
1492
1493 raw.x = LE_S16(COORD_TO_INT(th->raw_x));
1494 raw.y = LE_S16(COORD_TO_INT(th->raw_y));
1495 raw.height = LE_S16(COORD_TO_INT(th->raw_h));
1496
1497 raw.angle = LE_U16(th->angle);
1498 raw.type = LE_U16(th->type);
1499 raw.options = LE_U16(th->options);
1500
1501 raw.special = th->special;
1502 raw.args[0] = th->arg1;
1503 raw.args[1] = th->arg2;
1504 raw.args[2] = th->arg3;
1505 raw.args[3] = th->arg4;
1506 raw.args[4] = th->arg5;
1507
1508 lump->Write(&raw, sizeof(raw));
1509 }
1510
1511 lump->Finish();
1512 }
1513
1514
SaveSideDefs()1515 static void SaveSideDefs()
1516 {
1517 int size = NumSideDefs * (int)sizeof(raw_sidedef_t);
1518
1519 Lump_c *lump = edit_wad->AddLump("SIDEDEFS", size);
1520
1521 for (int i = 0 ; i < NumSideDefs ; i++)
1522 {
1523 const SideDef *side = SideDefs[i];
1524
1525 raw_sidedef_t raw;
1526
1527 raw.x_offset = LE_S16(side->x_offset);
1528 raw.y_offset = LE_S16(side->y_offset);
1529
1530 W_StoreString(raw.upper_tex, side->UpperTex(), sizeof(raw.upper_tex));
1531 W_StoreString(raw.lower_tex, side->LowerTex(), sizeof(raw.lower_tex));
1532 W_StoreString(raw.mid_tex, side->MidTex(), sizeof(raw.mid_tex));
1533
1534 raw.sector = LE_U16(side->sector);
1535
1536 lump->Write(&raw, sizeof(raw));
1537 }
1538
1539 lump->Finish();
1540 }
1541
1542
SaveLineDefs()1543 static void SaveLineDefs()
1544 {
1545 int size = NumLineDefs * (int)sizeof(raw_linedef_t);
1546
1547 Lump_c *lump = edit_wad->AddLump("LINEDEFS", size);
1548
1549 for (int i = 0 ; i < NumLineDefs ; i++)
1550 {
1551 const LineDef *ld = LineDefs[i];
1552
1553 raw_linedef_t raw;
1554
1555 raw.start = LE_U16(ld->start);
1556 raw.end = LE_U16(ld->end);
1557
1558 raw.flags = LE_U16(ld->flags);
1559 raw.type = LE_U16(ld->type);
1560 raw.tag = LE_S16(ld->tag);
1561
1562 raw.right = (ld->right >= 0) ? LE_U16(ld->right) : 0xFFFF;
1563 raw.left = (ld->left >= 0) ? LE_U16(ld->left) : 0xFFFF;
1564
1565 lump->Write(&raw, sizeof(raw));
1566 }
1567
1568 lump->Finish();
1569 }
1570
1571
1572 // IOANCH 9/2015
SaveLineDefs_Hexen()1573 static void SaveLineDefs_Hexen()
1574 {
1575 int size = NumLineDefs * (int)sizeof(raw_hexen_linedef_t);
1576
1577 Lump_c *lump = edit_wad->AddLump("LINEDEFS", size);
1578
1579 for (int i = 0 ; i < NumLineDefs ; i++)
1580 {
1581 const LineDef *ld = LineDefs[i];
1582
1583 raw_hexen_linedef_t raw;
1584
1585 raw.start = LE_U16(ld->start);
1586 raw.end = LE_U16(ld->end);
1587
1588 raw.flags = LE_U16(ld->flags);
1589 raw.type = ld->type;
1590
1591 raw.args[0] = ld->tag;
1592 raw.args[1] = ld->arg2;
1593 raw.args[2] = ld->arg3;
1594 raw.args[3] = ld->arg4;
1595 raw.args[4] = ld->arg5;
1596
1597 raw.right = (ld->right >= 0) ? LE_U16(ld->right) : 0xFFFF;
1598 raw.left = (ld->left >= 0) ? LE_U16(ld->left) : 0xFFFF;
1599
1600 lump->Write(&raw, sizeof(raw));
1601 }
1602
1603 lump->Finish();
1604 }
1605
1606
EmptyLump(const char * name)1607 static void EmptyLump(const char *name)
1608 {
1609 edit_wad->AddLump(name)->Finish();
1610 }
1611
1612
1613 //
1614 // Write out the level data
1615 //
1616
SaveLevel(const char * level)1617 static void SaveLevel(const char *level)
1618 {
1619 // set global level name now (for debugging code)
1620 Level_name = StringUpper(level);
1621
1622 edit_wad->BeginWrite();
1623
1624 // remove previous version of level (if it exists)
1625 int lev_num = edit_wad->LevelFind(level);
1626 int level_lump = -1;
1627
1628 if (lev_num >= 0)
1629 {
1630 level_lump = edit_wad->LevelHeader(lev_num);
1631
1632 edit_wad->RemoveLevel(lev_num);
1633 }
1634
1635 edit_wad->InsertPoint(level_lump);
1636
1637 SaveHeader(level);
1638
1639 if (Level_format == MAPF_UDMF)
1640 {
1641 UDMF_SaveLevel();
1642 }
1643 else
1644 {
1645 // IOANCH 9/2015: save Hexen format maps
1646 if (Level_format == MAPF_Hexen)
1647 {
1648 SaveThings_Hexen();
1649 SaveLineDefs_Hexen();
1650 }
1651 else
1652 {
1653 SaveThings();
1654 SaveLineDefs();
1655 }
1656
1657 SaveSideDefs();
1658 SaveVertices();
1659
1660 EmptyLump("SEGS");
1661 EmptyLump("SSECTORS");
1662 EmptyLump("NODES");
1663
1664 SaveSectors();
1665
1666 EmptyLump("REJECT");
1667 EmptyLump("BLOCKMAP");
1668
1669 if (Level_format == MAPF_Hexen)
1670 {
1671 SaveBehavior();
1672 SaveScripts();
1673 }
1674 }
1675
1676 // write out the new directory
1677 edit_wad->EndWrite();
1678
1679
1680 // build the nodes
1681 if (bsp_on_save && ! inhibit_node_build)
1682 {
1683 BuildNodesAfterSave(saving_level);
1684 }
1685
1686
1687 // this is mainly for Next/Prev-map commands
1688 // [ it doesn't change the on-disk wad file at all ]
1689 edit_wad->SortLevels();
1690
1691 M_WriteEurekaLump(edit_wad);
1692
1693 M_AddRecent(edit_wad->PathName(), Level_name);
1694
1695 Status_Set("Saved %s", Level_name);
1696
1697 if (main_win)
1698 {
1699 main_win->SetTitle(edit_wad->PathName(), Level_name, false);
1700
1701 // save the user state associated with this map
1702 M_SaveUserState();
1703 }
1704
1705 MadeChanges = 0;
1706 }
1707
1708
M_SaveMap()1709 bool M_SaveMap()
1710 {
1711 // we require a wad file to save into.
1712 // if there is none, then need to create one via Export function.
1713
1714 if (! edit_wad)
1715 {
1716 return M_ExportMap();
1717 }
1718
1719 if (edit_wad->IsReadOnly())
1720 {
1721 if (DLG_Confirm("Cancel|&Export",
1722 "The current pwad is a READ-ONLY file. "
1723 "Do you want to export this map into a new file?") <= 0)
1724 {
1725 return false;
1726 }
1727
1728 return M_ExportMap();
1729 }
1730
1731
1732 M_BackupWad(edit_wad);
1733
1734 LogPrintf("Saving Map : %s in %s\n", Level_name, edit_wad->PathName());
1735
1736 SaveLevel(Level_name);
1737
1738 return true;
1739 }
1740
1741
M_ExportMap()1742 bool M_ExportMap()
1743 {
1744 Fl_Native_File_Chooser chooser;
1745
1746 chooser.title("Pick file to export to");
1747 chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
1748 chooser.filter("Wads\t*.wad");
1749 chooser.directory(Main_FileOpFolder());
1750
1751 // Show native chooser
1752 switch (chooser.show())
1753 {
1754 case -1:
1755 LogPrintf("Export Map: error choosing file:\n");
1756 LogPrintf(" %s\n", chooser.errmsg());
1757
1758 DLG_Notify("Unable to export the map:\n\n%s", chooser.errmsg());
1759 return false;
1760
1761 case 1:
1762 LogPrintf("Export Map: cancelled by user\n");
1763 return false;
1764
1765 default:
1766 break; // OK
1767 }
1768
1769 // if extension is missing then add ".wad"
1770 char filename[FL_PATH_MAX];
1771
1772 strcpy(filename, chooser.filename());
1773
1774 char *pos = (char *)fl_filename_ext(filename);
1775 if (! *pos)
1776 strcat(filename, ".wad");
1777
1778
1779 // don't export into a file we currently have open
1780 if (MasterDir_HaveFilename(filename))
1781 {
1782 DLG_Notify("Unable to export the map:\n\nFile already in use");
1783 return false;
1784 }
1785
1786
1787 // does the file already exist? if not, create it...
1788 bool exists = FileExists(filename);
1789
1790 Wad_file *wad;
1791
1792 if (exists)
1793 {
1794 wad = Wad_file::Open(filename, 'a');
1795
1796 if (wad && wad->IsReadOnly())
1797 {
1798 DLG_Notify("Cannot export the map into a READ-ONLY file.");
1799
1800 delete wad;
1801 return false;
1802 }
1803
1804 // adopt iwad/port/resources of the target wad
1805 if (wad->FindLump(EUREKA_LUMP))
1806 {
1807 if (! M_ParseEurekaLump(wad))
1808 {
1809 delete wad;
1810 return false;
1811 }
1812 }
1813 }
1814 else
1815 {
1816 wad = Wad_file::Open(filename, 'w');
1817 }
1818
1819 if (! wad)
1820 {
1821 DLG_Notify("Unable to export the map:\n\n%s",
1822 "Error creating output file");
1823 return false;
1824 }
1825
1826
1827 // ask user for map name
1828
1829 UI_ChooseMap * dialog = new UI_ChooseMap(Level_name);
1830
1831 dialog->PopulateButtons(toupper(Level_name[0]), wad);
1832
1833 const char *map_name = dialog->Run();
1834
1835 delete dialog;
1836
1837
1838 // cancelled?
1839 if (! map_name)
1840 {
1841 delete wad;
1842 return false;
1843 }
1844
1845
1846 // we will write into the chosen wad.
1847 // however if the level already exists, get confirmation first
1848
1849 if (exists && wad->LevelFind(map_name) >= 0)
1850 {
1851 if (DLG_Confirm("Cancel|&Overwrite",
1852 overwrite_message, "selected") <= 0)
1853 {
1854 delete wad;
1855 return false;
1856 }
1857 }
1858
1859 // back-up an existing wad
1860 if (exists)
1861 {
1862 M_BackupWad(wad);
1863 }
1864
1865
1866 LogPrintf("Exporting Map : %s in %s\n", map_name, wad->PathName());
1867
1868 // the new wad replaces the current PWAD
1869 ReplaceEditWad(wad);
1870
1871 SaveLevel(map_name);
1872
1873 // do this after the save (in case it fatal errors)
1874 Main_LoadResources();
1875
1876 return true;
1877 }
1878
1879
CMD_SaveMap()1880 void CMD_SaveMap()
1881 {
1882 M_SaveMap();
1883 }
1884
1885
CMD_ExportMap()1886 void CMD_ExportMap()
1887 {
1888 M_ExportMap();
1889 }
1890
1891
1892 //------------------------------------------------------------------------
1893 // COPY, RENAME and DELETE MAP
1894 //------------------------------------------------------------------------
1895
CMD_CopyMap()1896 void CMD_CopyMap()
1897 {
1898 if (! edit_wad)
1899 {
1900 DLG_Notify("Cannot copy a map unless editing a PWAD.");
1901 return;
1902 }
1903
1904 if (edit_wad->IsReadOnly())
1905 {
1906 DLG_Notify("Cannot copy map : file is read-only.");
1907 return;
1908 }
1909
1910 // ask user for map name
1911
1912 UI_ChooseMap * dialog = new UI_ChooseMap(Level_name, edit_wad);
1913
1914 dialog->PopulateButtons(toupper(Level_name[0]), edit_wad);
1915
1916 const char *new_name = dialog->Run();
1917
1918 delete dialog;
1919
1920 // cancelled?
1921 if (! new_name)
1922 return;
1923
1924 // sanity check that the name is different
1925 // (should be prevented by the choose-map dialog)
1926 if (y_stricmp(new_name, Level_name) == 0)
1927 {
1928 Beep("Name is same!?!");
1929 return;
1930 }
1931
1932 // perform the copy (just a save)
1933 LogPrintf("Copying Map : %s --> %s\n", Level_name, new_name);
1934
1935 SaveLevel(new_name);
1936
1937 Status_Set("Copied to %s", Level_name);
1938 }
1939
1940
CMD_RenameMap()1941 void CMD_RenameMap()
1942 {
1943 if (! edit_wad)
1944 {
1945 DLG_Notify("Cannot rename a map unless editing a PWAD.");
1946 return;
1947 }
1948
1949 if (edit_wad->IsReadOnly())
1950 {
1951 DLG_Notify("Cannot rename map : file is read-only.");
1952 return;
1953 }
1954
1955
1956 // ask user for map name
1957
1958 UI_ChooseMap * dialog = new UI_ChooseMap(Level_name, edit_wad /* rename_wad */);
1959
1960 // pick level format from the IWAD
1961 // [ user may be trying to rename map after changing the IWAD ]
1962 char format = 'M';
1963 {
1964 short idx = game_wad->LevelFindFirst();
1965
1966 if (idx >= 0)
1967 {
1968 idx = game_wad->LevelHeader(idx);
1969 const char *name = game_wad->GetLump(idx)->Name();
1970 format = toupper(name[0]);
1971 }
1972 }
1973
1974 dialog->PopulateButtons(format, edit_wad);
1975
1976 const char *new_name = dialog->Run();
1977
1978 delete dialog;
1979
1980 // cancelled?
1981 if (! new_name)
1982 return;
1983
1984 // sanity check that the name is different
1985 // (should be prevented by the choose-map dialog)
1986 if (y_stricmp(new_name, Level_name) == 0)
1987 {
1988 Beep("Name is same!?!");
1989 return;
1990 }
1991
1992
1993 // perform the rename
1994 short lev_num = edit_wad->LevelFind(Level_name);
1995
1996 if (lev_num >= 0)
1997 {
1998 short level_lump = edit_wad->LevelHeader(lev_num);
1999
2000 edit_wad->BeginWrite();
2001 edit_wad->RenameLump(level_lump, new_name);
2002 edit_wad->EndWrite();
2003 }
2004
2005 Level_name = StringUpper(new_name);
2006
2007 main_win->SetTitle(edit_wad->PathName(), Level_name, false);
2008
2009 Status_Set("Renamed to %s", Level_name);
2010 }
2011
2012
CMD_DeleteMap()2013 void CMD_DeleteMap()
2014 {
2015 if (! edit_wad)
2016 {
2017 DLG_Notify("Cannot delete a map unless editing a PWAD.");
2018 return;
2019 }
2020
2021 if (edit_wad->IsReadOnly())
2022 {
2023 DLG_Notify("Cannot delete map : file is read-only.");
2024 return;
2025 }
2026
2027 if (edit_wad->LevelCount() < 2)
2028 {
2029 // perhaps ask either to Rename map, or Delete the file (and Eureka will shut down)
2030
2031 DLG_Notify("Cannot delete the last map in a PWAD.");
2032 return;
2033 }
2034
2035 if (DLG_Confirm("Cancel|&Delete",
2036 "Are you sure you want to delete this map? "
2037 "It will be permanently removed from the current PWAD.") <= 0)
2038 {
2039 return;
2040 }
2041
2042 LogPrintf("Deleting Map : %s...\n", Level_name);
2043
2044 short lev_num = edit_wad->LevelFind(Level_name);
2045
2046 if (lev_num < 0)
2047 {
2048 Beep("No such map ?!?");
2049 return;
2050 }
2051
2052
2053 // kick it to the curb
2054 edit_wad->BeginWrite();
2055 edit_wad->RemoveLevel(lev_num);
2056 edit_wad->EndWrite();
2057
2058
2059 // choose a new level to load
2060 {
2061 if (lev_num >= edit_wad->LevelCount())
2062 lev_num = edit_wad->LevelCount() - 1;
2063
2064 short lump_idx = edit_wad->LevelHeader(lev_num);
2065 Lump_c * lump = edit_wad->GetLump(lump_idx);
2066 const char *map_name = lump->Name();
2067
2068 LogPrintf("OK. Loading : %s....\n", map_name);
2069
2070 LoadLevel(edit_wad, map_name);
2071 }
2072 }
2073
2074 //--- editor settings ---
2075 // vi:ts=4:sw=4:noexpandtab
2076