1 /*
2 * yadex.cc
3 * The main module.
4 * BW & RQ sometime in 1993 or 1994.
5 */
6
7
8 /*
9 This file is part of Yadex.
10
11 Yadex incorporates code from DEU 5.21 that was put in the public domain in
12 1994 by Rapha�l Quinet and Brendon Wyber.
13
14 The rest of Yadex is Copyright � 1997-2003 Andr� Majorel and others.
15
16 This program is free software; you can redistribute it and/or modify it under
17 the terms of the GNU General Public License as published by the Free Software
18 Foundation; either version 2 of the License, or (at your option) any later
19 version.
20
21 This program is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License along with
26 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
27 Place, Suite 330, Boston, MA 02111-1307, USA.
28 */
29
30
31 #include "yadex.h"
32 #include <time.h>
33 #include "acolours.h"
34 #include "bench.h"
35 #include "cfgfile.h"
36 #include "disppic.h" /* Because of "p" */
37 #include "editlev.h"
38 #include "endian.h"
39 #include "flats.h"
40 #include "game.h"
41 #include "gfx.h"
42 #include "gfx2.h"
43 #include "help1.h"
44 #include "levels.h" /* Because of "viewtex" */
45 #include "lists.h"
46 #include "mkpalette.h"
47 #include "palview.h"
48 #include "patchdir.h" /* Because of "p" */
49 #include "rgb.h"
50 #include "sanity.h"
51 #include "textures.h"
52 #include "x11.h"
53 #include "wadfile.h"
54 #include "wadlist.h"
55 #include "wadname.h"
56 #include "wadres.h"
57 #include "wads2.h"
58
59
60 /*
61 * Constants (declared in yadex.h)
62 */
63 const char *const log_file = "yadex.log";
64 const char *const msg_unexpected = "unexpected error";
65 const char *const msg_nomem = "Not enough memory";
66
67
68 /*
69 * Not real variables -- just unique pointer values
70 * used by functions that return pointers to
71 */
72 char error_non_unique[1]; // Found more than one
73 char error_none[1]; // Found none
74 char error_invalid[1]; // Invalid parameter
75
76
77 /*
78 * Global variables
79 */
80 const char *install_dir = 0; // Where Yadex is installed
81 FILE * logfile = NULL; // Filepointer to the error log
82 bool Registered = false; // Registered or shareware game?
83 int screen_lines = 24; // Lines that our TTY can display
84 int remind_to_build_nodes = 0; // Remind user to build nodes
85 Wad_res wad_res (&MasterDir);
86
87 // Set from command line and/or config file
88 bool autoscroll = 0;
89 unsigned long autoscroll_amp = 10;
90 unsigned long autoscroll_edge = 30;
91 const char *config_file = NULL;
92 int copy_linedef_reuse_sidedefs = 0;
93 int cpu_big_endian = 0;
94 bool Debug = false;
95 int default_ceiling_height = 128;
96 char default_ceiling_texture[WAD_FLAT_NAME + 1] = "CEIL3_5";
97 int default_floor_height = 0;
98 char default_floor_texture[WAD_FLAT_NAME + 1] = "FLOOR4_8";
99 int default_light_level = 144;
100 char default_lower_texture[WAD_TEX_NAME + 1] = "STARTAN3";
101 char default_middle_texture[WAD_TEX_NAME + 1] = "STARTAN3";
102 char default_upper_texture[WAD_TEX_NAME + 1] = "STARTAN3";
103 int default_thing = 3004;
104 int double_click_timeout = 200;
105 bool Expert = false;
106 const char *Game = NULL;
107 int grid_pixels_min = 10;
108 int GridMin = 2;
109 int GridMax = 128;
110 int idle_sleep_ms = 50;
111 bool InfoShown = true;
112 int zoom_default = 0; // 0 means fit
113 int zoom_step = 0; // 0 means sqrt(2)
114 int digit_zoom_base = 100;
115 int digit_zoom_step = 0; // 0 means sqrt(2)
116 confirm_t insert_vertex_split_linedef = YC_ASK_ONCE;
117 confirm_t insert_vertex_merge_vertices = YC_ASK_ONCE;
118 bool blindly_swap_sidedefs = false;
119 const char *Iwad1 = NULL;
120 const char *Iwad2 = NULL;
121 const char *Iwad3 = NULL;
122 const char *Iwad4 = NULL;
123 const char *Iwad5 = NULL;
124 const char *Iwad6 = NULL;
125 const char *Iwad7 = NULL;
126 const char *Iwad8 = NULL;
127 const char *Iwad9 = NULL;
128 const char *Iwad10 = NULL;
129 const char *MainWad = NULL;
130 #ifdef AYM_MOUSE_HACKS
131 int MouseMickeysH = 5;
132 int MouseMickeysV = 5;
133 #endif
134 char ** PatchWads = NULL;
135 bool Quiet = false;
136 bool Quieter = false;
137 unsigned long scroll_less = 10;
138 unsigned long scroll_more = 90;
139 bool Select0 = false;
140 int show_help = 0;
141 int sprite_scale = 100;
142 bool SwapButtons = false;
143 int verbose = 0;
144 int welcome_message = 1;
145 const char *bench = 0;
146
147 // Global variables declared in game.h
148 yglf_t yg_level_format = YGLF__;
149 ygln_t yg_level_name = YGLN__;
150 ygpf_t yg_picture_format = YGPF_NORMAL;
151 ygtf_t yg_texture_format = YGTF_NORMAL;
152 ygtl_t yg_texture_lumps = YGTL_NORMAL;
153 al_llist_t *ldtdef = NULL;
154 al_llist_t *ldtgroup = NULL;
155 al_llist_t *stdef = NULL;
156 al_llist_t *thingdef = NULL;
157 al_llist_t *thinggroup = NULL;
158 Wad_name sky_flat;
159
160
161 /*
162 * Prototypes of private functions
163 */
164 static int parse_environment_vars ();
165 static void MainLoop ();
166 static void print_error_message (const char *fmt, va_list args);
167 static void add_base_colours ();
168 static const Wad_file *wad_by_name (const char *pathname);
169 static bool wad_already_loaded (const char *pathname);
170
171
172 /*
173 * main
174 * Guess what.
175 */
main(int argc,char * argv[])176 int main (int argc, char *argv[])
177 {
178 int r;
179
180 // Set <screen_lines>
181 if (getenv ("LINES") != NULL)
182 screen_lines = atoi (getenv ("LINES"));
183 else
184 screen_lines = 0;
185 if (screen_lines == 0)
186 screen_lines = 24;
187
188 // InitSwap must be called before any call to GetMemory(), etc.
189 InitSwap ();
190
191 // First detect manually --help and --version
192 // because parse_command_line_options() cannot.
193 if (argc == 2 && strcmp (argv[1], "--help") == 0)
194 {
195 print_usage (stdout);
196 if (fflush (stdout) != 0)
197 fatal_error ("stdout: %s", strerror (errno));
198 exit (0);
199 }
200 if (argc == 2 && strcmp (argv[1], "--version") == 0)
201 {
202 puts (what ());
203 puts (config_file_magic);
204 puts (ygd_file_magic);
205 if (fflush (stdout) != 0)
206 fatal_error ("stdout: %s", strerror (errno));
207 exit (0);
208 }
209
210 // Second a quick pass through the command line
211 // arguments to detect -?, -f and -help.
212 r = parse_command_line_options (argc - 1, argv + 1, 1);
213 if (r)
214 goto syntax_error;
215
216 if (show_help)
217 {
218 print_usage (stdout);
219 exit (1);
220 }
221
222 printf ("%s\n", what ());
223
224 // Where am I installed ? (the config file might be there)
225 #if defined Y_DOS
226 install_dir = spec_path (argv[0]);
227 #endif
228
229 // The config file provides some values.
230 if (config_file != NULL)
231 r = parse_config_file_user (config_file);
232 else
233 r = parse_config_file_default ();
234 if (r == 0)
235 {
236 // Environment variables can override them.
237 r = parse_environment_vars ();
238 if (r == 0)
239 {
240 // And the command line argument can override both.
241 r = parse_command_line_options (argc - 1, argv + 1, 2);
242 }
243 }
244 if (r != 0)
245 {
246 syntax_error :
247 fprintf (stderr, "Try \"yadex --help\" or \"man yadex\".\n");
248 exit (1);
249 }
250
251 if (Game != NULL && strcmp (Game, "doom") == 0)
252 {
253 if (Iwad1 == NULL)
254 {
255 err ("You have to tell me where doom.wad is.");
256 fprintf (stderr,
257 "Use \"-i1 <file>\" or put \"iwad1=<file>\" in yadex.cfg.\n");
258 exit (1);
259 }
260 MainWad = Iwad1;
261 }
262 else if (Game != NULL && strcmp (Game, "doom2") == 0)
263 {
264 if (Iwad2 == NULL)
265 {
266 err ("You have to tell me where doom2.wad is.");
267 fprintf (stderr,
268 "Use \"-i2 <file>\" or put \"iwad2=<file>\" in yadex.cfg.\n");
269 exit (1);
270 }
271 MainWad = Iwad2;
272 }
273 else if (Game != NULL && strcmp (Game, "heretic") == 0)
274 {
275 if (Iwad3 == NULL)
276 {
277 err ("You have to tell me where heretic.wad is.");
278 fprintf (stderr,
279 "Use \"-i3 <file>\" or put \"iwad3=<file>\" in yadex.cfg.\n");
280 exit (1);
281 }
282 MainWad = Iwad3;
283 }
284 else if (Game != NULL && strcmp (Game, "hexen") == 0)
285 {
286 if (Iwad4 == NULL)
287 {
288 err ("You have to tell me where hexen.wad is.");
289 fprintf (stderr,
290 "Use \"-i4 <file>\" or put \"iwad4=<file>\" in yadex.cfg.\n");
291 exit (1);
292 }
293 MainWad = Iwad4;
294 }
295 else if (Game != NULL && strcmp (Game, "strife") == 0)
296 {
297 if (Iwad5 == NULL)
298 {
299 err ("You have to tell me where strife1.wad is.");
300 fprintf (stderr,
301 "Use \"-i5 <file>\" or put \"iwad5=<file>\" in yadex.cfg.\n");
302 exit (1);
303 }
304 MainWad = Iwad5;
305 }
306 else if (Game != NULL && strcmp (Game, "doom02") == 0)
307 {
308 if (Iwad6 == NULL)
309 {
310 err ("You have to tell me where the Doom alpha 0.2 iwad is.");
311 fprintf (stderr,
312 "Use \"-i6 <file>\" or put \"iwad6=<file>\" in yadex.cfg.\n");
313 exit (1);
314 }
315 MainWad = Iwad6;
316 }
317 else if (Game != NULL && strcmp (Game, "doom04") == 0)
318 {
319 if (Iwad7 == NULL)
320 {
321 err ("You have to tell me where the Doom alpha 0.4 iwad is.");
322 fprintf (stderr,
323 "Use \"-i7 <file>\" or put \"iwad7=<file>\" in yadex.cfg.\n");
324 exit (1);
325 }
326 MainWad = Iwad7;
327 }
328 else if (Game != NULL && strcmp (Game, "doom05") == 0)
329 {
330 if (Iwad8 == NULL)
331 {
332 err ("You have to tell me where the Doom alpha 0.5 iwad is.");
333 fprintf (stderr,
334 "Use \"-i8 <file>\" or put \"iwad8=<file>\" in yadex.cfg.\n");
335 exit (1);
336 }
337 MainWad = Iwad8;
338 }
339 else if (Game != NULL && strcmp (Game, "doompr") == 0)
340 {
341 if (Iwad9 == NULL)
342 {
343 err ("You have to tell me where the Doom press release iwad is.");
344 fprintf (stderr,
345 "Use \"-i9 <file>\" or put \"iwad9=<file>\" in yadex.cfg.\n");
346 exit (1);
347 }
348 MainWad = Iwad9;
349 }
350 else if (Game != NULL && strcmp (Game, "strife10") == 0)
351 {
352 if (Iwad10 == NULL)
353 {
354 err ("You have to tell me where strife1.wad is.");
355 fprintf (stderr,
356 "Use \"-i10 <file>\" or put \"iwad10=<file>\" in yadex.cfg.\n");
357 exit (1);
358 }
359 MainWad = Iwad10;
360 }
361 else
362 {
363 if (Game == NULL)
364 err ("You didn't say for which game you want to edit.");
365 else
366 err ("Unknown game \"%s\"", Game);
367 fprintf (stderr,
368 "Use \"-g <game>\" on the command line or put \"game=<game>\" in yadex.cfg\n"
369 "where <game> is one of \"doom\", \"doom02\", \"doom04\", \"doom05\","
370 " \"doom2\",\n\"doompr\", \"heretic\", \"hexen\", \"strife\" and "
371 "\"strife10\".\n");
372 exit (1);
373 }
374 if (Debug)
375 {
376 logfile = fopen (log_file, "a");
377 if (logfile == NULL)
378 warn ("can't open log file \"%s\" (%s)", log_file, strerror (errno));
379 LogMessage (": Welcome to Yadex!\n");
380 }
381 if (Quieter)
382 Quiet = true;
383
384 // Sanity checks (useful when porting).
385 check_types ();
386 check_charset ();
387
388 // Misc. things done only once.
389 cpu_big_endian = native_endianness ();
390 add_base_colours ();
391
392 // Load game definitions (*.ygd).
393 InitGameDefs ();
394 LoadGameDefs (Game);
395
396 // Load the iwad and the pwads.
397 if (OpenMainWad (MainWad))
398 fatal_error ("If you don't give me an iwad, I'll quit. I'm serious.");
399 if (PatchWads)
400 {
401 const char * const *pwad_name;
402 for (pwad_name = PatchWads; *pwad_name; pwad_name++)
403 OpenPatchWad (*pwad_name);
404 }
405 /* sanity check */
406 CloseUnusedWadFiles ();
407
408 // BRANCH 1 : benchmarking (-b)
409 if (bench != 0)
410 {
411 benchmark (bench);
412 return 0; // Exit successfully
413 }
414
415 // BRANCH 2 : normal use ("yadex:" prompt)
416 else
417 {
418 if (welcome_message)
419 print_welcome (stdout);
420
421 if (strcmp (Game, "hexen") == 0)
422 printf (
423 "WARNING: Hexen mode is experimental. Don't expect to be able to do any\n"
424 "real Hexen editing with it. You can edit levels but you can't save them.\n"
425 "And there might be other bugs... BE CAREFUL !\n\n");
426
427 if (strcmp (Game, "strife") == 0)
428 printf (
429 "WARNING: Strife mode is experimental. Many thing types, linedef types,\n"
430 "etc. are missing or wrong. And be careful, there might be bugs.\n\n");
431
432 /* all systems go! */
433 MainLoop ();
434 }
435
436 /* that's all, folks! */
437 CloseWadFiles ();
438 FreeGameDefs ();
439 LogMessage (": The end!\n\n\n");
440 if (logfile != NULL)
441 fclose (logfile);
442 if (remind_to_build_nodes)
443 printf ("\n"
444 "** You have made changes to one or more wads. Don't forget to pass\n"
445 "** them through a nodes builder (E.G. BSP) before running them.\n"
446 "** Like this: \"ybsp foo.wad -o tmp.wad; doom -file tmp.wad\"\n\n");
447 return 0;
448 }
449
450
451 /*
452 * parse_environment_vars
453 * Check certain environment variables.
454 * Returns 0 on success, <>0 on error.
455 */
parse_environment_vars()456 static int parse_environment_vars ()
457 {
458 char *value;
459
460 value = getenv ("YADEX_GAME");
461 if (value != NULL)
462 Game = value;
463 return 0;
464 }
465
466
467 /*
468 play a fascinating tune
469 */
470
Beep()471 void Beep ()
472 {
473 #if defined Y_DOS
474 if (! Quieter)
475 {
476 sound (640);
477 delay (100);
478 nosound ();
479 }
480 #elif defined Y_UNIX
481 if (! Quieter)
482 #if 0
483 if (dpy) // FIXME not defined here !
484 #else
485 if (1)
486 #endif
487 x_bell ();
488 else
489 {
490 putchar ('\a');
491 fflush (stdout);
492 }
493 #endif
494 }
495
496
497
498 /*
499 play a sound
500 */
501
PlaySound(int freq,int msec)502 void PlaySound (int freq, int msec)
503 {
504 #if defined Y_DOS
505 if (! Quiet)
506 {
507 sound (freq);
508 delay (msec);
509 nosound ();
510 }
511 #elif defined Y_UNIX
512 freq = msec; // To prevent a warning about unused variables
513 return;
514 #endif
515 }
516
517
518
519 /*
520 * fatal_error
521 * Print an error message and terminate the program with code 2.
522 */
fatal_error(const char * fmt,...)523 void fatal_error (const char *fmt, ...)
524 {
525 // With BGI, we have to switch back to text mode
526 // before printing the error message so do it now...
527 #ifdef Y_BGI
528 if (GfxMode)
529 {
530 sleep (1);
531 TermGfx ();
532 }
533 #endif
534
535 va_list args;
536 va_start (args, fmt);
537 print_error_message (fmt, args);
538
539 // ... on the other hand, with X, we don't have to
540 // call TermGfx() before printing so we do it last so
541 // that a segfault occuring in TermGfx() does not
542 // prevent us from seeing the stderr message.
543 #ifdef Y_X11
544 if (GfxMode)
545 TermGfx (); // Don't need to sleep (1) either.
546 #endif
547
548 // Clean up things and free swap space
549 ForgetLevelData ();
550 ForgetWTextureNames ();
551 ForgetFTextureNames ();
552 CloseWadFiles ();
553 exit (2);
554 }
555
556
557 /*
558 * err
559 * Print an error message but do not terminate the program.
560 */
err(const char * fmt,...)561 void err (const char *fmt, ...)
562 {
563 va_list args;
564
565 va_start (args, fmt);
566 print_error_message (fmt, args);
567 }
568
569
570 /*
571 * print_error_message
572 * Print an error message to stderr.
573 */
print_error_message(const char * fmt,va_list args)574 static void print_error_message (const char *fmt, va_list args)
575 {
576 fflush (stdout);
577 fputs ("Error: ", stderr);
578 vfprintf (stderr, fmt, args);
579 fputc ('\n', stderr);
580 fflush (stderr);
581 if (Debug && logfile != NULL)
582 {
583 fputs ("Error: ", logfile);
584 vfprintf (logfile, fmt, args);
585 fputc ('\n', logfile);
586 fflush (logfile);
587 }
588 }
589
590
591 /*
592 * nf_bug
593 * Report about a non-fatal bug to stderr. The message
594 * should not expand to more than 80 characters.
595 */
nf_bug(const char * fmt,...)596 void nf_bug (const char *fmt, ...)
597 {
598 static bool first_time = 1;
599 static int repeats = 0;
600 static char msg_prev[81];
601 char msg[81];
602
603 va_list args;
604 va_start (args, fmt);
605 y_vsnprintf (msg, sizeof msg, fmt, args);
606 if (first_time || strncmp (msg, msg_prev, sizeof msg))
607 {
608 fflush (stdout);
609 if (repeats)
610 {
611 fprintf (stderr, "Bug: Previous message repeated %d times\n",
612 repeats);
613 repeats = 0;
614 }
615
616 fprintf (stderr, "Bug: %s\n", msg);
617 fflush (stderr);
618 if (first_time)
619 {
620 fputs ("REPORT ALL \"Bug:\" MESSAGES TO THE MAINTAINER !\n", stderr);
621 first_time = 0;
622 }
623 strncpy (msg_prev, msg, sizeof msg_prev);
624 }
625 else
626 {
627 repeats++; // Same message as above
628 if (repeats == 10)
629 {
630 fflush (stdout);
631 fprintf (stderr, "Bug: Previous message repeated %d times\n",
632 repeats);
633 fflush (stderr);
634 repeats = 0;
635 }
636 }
637 }
638
639
640 /*
641 write a message in the log file
642 */
643
LogMessage(const char * logstr,...)644 void LogMessage (const char *logstr, ...)
645 {
646 va_list args;
647 time_t tval;
648 char *tstr;
649
650 if (Debug && logfile != NULL)
651 {
652 va_start (args, logstr);
653 /* if the message begins with ":", output the current date & time first */
654 if (logstr[0] == ':')
655 {
656 time (&tval);
657 tstr = ctime (&tval);
658 tstr[strlen (tstr) - 1] = '\0';
659 fprintf (logfile, "%s", tstr);
660 }
661 vfprintf (logfile, logstr, args);
662 fflush (logfile); /* AYM 19971031 */
663 }
664 }
665
666
667
668 /*
669 the main program menu loop
670 */
671
MainLoop()672 static void MainLoop ()
673 {
674 char input[120];
675 char *com, *out;
676 FILE *file, *raw;
677
678 for (;;)
679 {
680 /* get the input */
681 printf ("yadex: ");
682 if (! fgets (input, sizeof input, stdin))
683 {
684 puts ("q");
685 break;
686 }
687
688 /* Strip the trailing '\n' */
689 if (strlen (input) > 0 && input[strlen (input) - 1] == '\n')
690 input[strlen (input) - 1] = '\0';
691
692 /* eat the white space and get the first command word */
693 com = strtok (input, " ");
694
695 /* user just hit return */
696 if (com == NULL)
697 printf ("Please enter a command or ? for help.\n");
698
699 /* user inputting for help */
700 else if (strcmp (com, "?") == 0
701 || strcmp (com, "h") == 0
702 || strcmp (com, "help") == 0)
703 {
704 printf ("? | h | help --"
705 " display this text\n");
706 printf ("b[uild] <WadFile> --"
707 " build a new iwad\n");
708 printf ("c[reate] <levelname> --"
709 " create and edit a new (empty) level\n");
710 printf ("d[ump] <DirEntry> [outfile] --"
711 " dump a directory entry in hex\n");
712 printf ("e[dit] <levelname> --"
713 " edit a game level saving results to\n");
714 printf (" a patch wad file\n");
715 printf ("g[roup] <WadFile> --"
716 " group all patch wads in a file\n");
717 printf ("i[nsert] <RawFile> <DirEntry> --"
718 " insert a raw file in a patch wad file\n");
719 printf ("l[ist] <WadFile> [outfile] --"
720 " list the directory of a wadfile\n");
721 printf ("m[aster] [outfile] --"
722 " list the master directory\n");
723 printf ("make_gimp_palette <outfile> --"
724 " generate a gimp palette file from\n"
725 " "
726 " entry 0 of lump PLAYPAL.\n");
727 printf ("make_palette_ppm <outfile> --"
728 " generate a palette image from\n"
729 " "
730 " entry 0 of lump PLAYPAL.\n");
731 printf ("q[uit] --"
732 " quit\n");
733 printf ("r[ead] <WadFile> --"
734 " read a new wad patch file\n");
735 printf ("s[ave] <DirEntry> <WadFile> --"
736 " save one object to a separate file\n");
737 printf ("set --"
738 " list all options and their values\n");
739 printf ("v[iew] [<spritename>] --"
740 " display the sprites\n");
741 printf ("viewflat [<flatname>] --"
742 " flat viewer\n");
743 printf ("viewpal --"
744 " palette viewer\n");
745 printf ("viewpat [<patchname>] --"
746 " patch viewer\n");
747 printf ("viewtex [<texname>] --"
748 " texture viewer\n");
749 printf ("w[ads] --"
750 " display the open wads\n");
751 printf ("x[tract] <DirEntry> <RawFile> --"
752 " save (extract) one object to a raw file\n");
753 }
754
755 /* user asked for list of open wad files */
756 else if (strcmp (com, "wads") == 0 || strcmp (com, "w") == 0)
757 {
758 const Wad_file *wf;
759 wad_list.rewind ();
760 if (wad_list.get (wf))
761 printf ("%-40s Iwad\n", wf->pathname ());
762 while (wad_list.get (wf))
763 printf ("%-40s Pwad (%.*s)\n",
764 wf->pathname (), (int) WAD_NAME, wf->what ());
765 }
766
767 /* user asked to quit */
768 else if (strcmp (com, "quit") == 0 || strcmp (com, "q") == 0)
769 {
770 if (! Registered)
771 printf ("Remember to register your copy of the game !\n");
772 break;
773 }
774
775 /* user asked to edit a level */
776 else if (strcmp (com, "edit") == 0 || strcmp (com, "e") == 0
777 || strcmp (com, "create") == 0 || strcmp (com, "c") == 0)
778 {
779 const int newlevel = strcmp (com, "create") == 0
780 || strcmp (com, "c") == 0;
781 char *level_name = 0;
782 com = strtok (NULL, " ");
783 if (com == 0)
784 if (! newlevel)
785 {
786 printf ("Which level ?\n");
787 continue;
788 }
789 else
790 level_name = 0;
791 else
792 {
793 level_name = find_level (com);
794 if (level_name == error_invalid)
795 {
796 printf ("\"%s\" is not a valid level name.\n", com);
797 continue;
798 }
799 else if (level_name == error_none)
800 {
801 printf ("Neither E%dM%d nor MAP%02d exist.\n",
802 atoi (com) / 10, atoi (com) % 10, atoi (com));
803 continue;
804 }
805 else if (level_name == error_non_unique)
806 {
807 printf ("Both E%dM%d and MAP%02d exist. Use an unambiguous name.\n",
808 atoi (com) / 10, atoi (com) % 10, atoi (com));
809 continue;
810 }
811 else if (level_name == NULL)
812 {
813 printf ("Level %s not found.", com);
814 // Hint absent-minded users
815 if (tolower (*com) == 'e' && yg_level_name == YGLN_MAP01
816 || tolower (*com) == 'm' && yg_level_name == YGLN_E1M1)
817 printf (" You are in %s mode.", Game);
818 else if (tolower (*com) == 'e' && com[1] > '1' && ! Registered)
819 printf (" You have the shareware iwad.");
820 putchar ('\n');
821 continue;
822 }
823 }
824 EditLevel (level_name, newlevel);
825 if (level_name)
826 free (level_name);
827 }
828
829 /* user asked to build a new main wad file */
830 else if (strcmp (com, "build") == 0 || strcmp (com, "b") == 0)
831 {
832 com = strtok (NULL, " ");
833 if (com == NULL)
834 {
835 printf ("Wad file name argument missing.\n");
836 continue;
837 }
838 if (wad_already_loaded (com))
839 {
840 printf ("%s: in use, close it first\n", com);
841 continue;
842 }
843 BuildNewMainWad (com, 0);
844 }
845
846 /* user asked to build a compound patch wad file */
847 else if (strcmp (com, "group") == 0 || strcmp (com, "g") == 0)
848 {
849 wad_list.rewind ();
850 const Wad_file *wf;
851 if (! wad_list.get (wf) || ! wad_list.get (wf))
852 {
853 printf ("You need at least two open wad files "
854 "if you want to group them.\n");
855 continue;
856 }
857 com = strtok (NULL, " ");
858 if (com == NULL)
859 {
860 printf ("Wad file name argument missing.\n");
861 continue;
862 }
863 if (wad_already_loaded (com))
864 {
865 printf ("%s: in use, close it first\n", com);
866 continue;
867 }
868 BuildNewMainWad (com, 1);
869 }
870
871 /* user ask for a listing of a wad file */
872 else if (strcmp (com, "list") == 0 || strcmp (com, "l") == 0)
873 {
874 com = strtok (NULL, " ");
875 if (com == NULL)
876 {
877 printf ("Wad file name argument missing.\n");
878 continue;
879 }
880 const Wad_file *wf = wad_by_name (com);
881 if (wf == 0)
882 {
883 printf ("%s: not open\n", com);
884 continue;
885 }
886 out = strtok (NULL, " ");
887 if (out)
888 {
889 printf ("Outputting directory of \"%s\" to \"%s\".\n",
890 wf->pathname (), out);
891 if ((file = fopen (out, "w")) == NULL)
892 fatal_error ("error opening output file \"%s\"", com);
893 fprintf (file, "%s\n", what ());
894 ListFileDirectory (file, wf);
895 fprintf (file, "\nEnd of file.\n");
896 fclose (file);
897 }
898 else
899 ListFileDirectory (stdout, wf);
900 }
901
902 /* user asked for the list of the master directory */
903 else if (strcmp (com, "master") == 0 || strcmp (com, "m") == 0)
904 {
905 out = strtok (NULL, " ");
906 if (out)
907 {
908 printf ("Outputting master directory to \"%s\".\n", out);
909 if ((file = fopen (out, "w")) == NULL)
910 fatal_error ("error opening output file \"%s\"", com);
911 fprintf (file, "%s\n", what ());
912 ListMasterDirectory (file);
913 fprintf (file, "\nEnd of file.\n");
914 fclose (file);
915 }
916 else
917 ListMasterDirectory (stdout);
918 }
919
920 // make_gimp_palette
921 else if (strcmp (com, "make_gimp_palette") == 0)
922 {
923 out = strtok (NULL, " ");
924 if (out == NULL)
925 {
926 printf ("Output file name argument missing.\n");
927 continue;
928 }
929 make_gimp_palette (0, out);
930 }
931
932 // make_palette_ppm
933 else if (strcmp (com, "make_palette_ppm") == 0)
934 {
935 out = strtok (NULL, "");
936 if (out == NULL)
937 {
938 printf ("Output file name argument missing.\n");
939 continue;
940 }
941 make_palette_ppm (0, out);
942 }
943
944 // make_palette_ppm
945 else if (strcmp (com, "mp2") == 0)
946 {
947 out = strtok (NULL, "");
948 if (out == NULL)
949 {
950 printf ("Output file name argument missing.\n");
951 continue;
952 }
953 make_palette_ppm_2 (0, out);
954 }
955
956 /* user asked to list all options and their values */
957 else if (strcmp (com, "set") == 0)
958 {
959 dump_parameters (stdout);
960 }
961
962 /* user asked to read a new patch wad file */
963 else if (strcmp (com, "read") == 0 || strcmp (com, "r") == 0)
964 {
965 com = strtok (NULL, " ");
966 if (com == NULL)
967 {
968 printf ("Wad file name argument missing.\n");
969 continue;
970 }
971 out = strtok (NULL, " ");
972 if (out)
973 *out = '\0';
974 out = (char *) GetMemory (strlen (com) + 1);
975 strcpy (out, com);
976 OpenPatchWad (out);
977 CloseUnusedWadFiles ();
978 }
979
980 /* user asked to dump the contents of a wad file */
981 else if (strcmp (com, "dump") == 0 || strcmp (com, "d") == 0)
982 {
983 com = strtok (NULL, " ");
984 if (com == NULL)
985 {
986 printf ("Object name argument missing.\n");
987 continue;
988 }
989 out = strtok (NULL, " ");
990 if (out)
991 {
992 printf ("Outputting directory entry data to \"%s\".\n", out);
993 if ((file = fopen (out, "w")) == NULL)
994 fatal_error ("error opening output file \"%s\"", com);
995 fprintf (file, "%s\n", what ());
996 DumpDirectoryEntry (file, com);
997 fprintf (file, "\nEnd of file.\n");
998 fclose (file);
999 }
1000 else
1001 DumpDirectoryEntry (stdout, com);
1002 }
1003
1004 // "v"/"view" - view the sprites
1005 else if (strcmp (com, "view") == 0 || strcmp (com, "v") == 0)
1006 {
1007 if (InitGfx ())
1008 goto v_end;
1009 init_input_status ();
1010 do
1011 get_input_status ();
1012 while (is.key != YE_EXPOSE);
1013 force_window_not_pixmap (); // FIXME quick hack
1014 {
1015 Lump_list list;
1016 wad_res.sprites.list (list);
1017 char buf[WAD_PIC_NAME + 1];
1018 const char *sprite = strtok (NULL, " ");
1019 *buf = '\0';
1020 if (sprite != 0)
1021 {
1022 strncat (buf, sprite, sizeof buf - 1);
1023 for (char *p = buf; *p != '\0'; p++)
1024 *p = toupper (*p);
1025 }
1026 InputNameFromListWithFunc (-1, -1, "Sprite viewer", list.size (),
1027 list.data (), 10, buf, 320, 200, display_pic,
1028 HOOK_DISP_SIZE | HOOK_SPRITE);
1029 }
1030 TermGfx ();
1031 v_end:;
1032 }
1033
1034 // "viewflat" - view the flats
1035 else if (strcmp (com, "viewflat") == 0)
1036 {
1037 if (InitGfx ())
1038 goto viewflat_end;
1039 init_input_status ();
1040 do
1041 get_input_status ();
1042 while (is.key != YE_EXPOSE);
1043 com = strtok (NULL, " ");
1044 force_window_not_pixmap (); // FIXME quick hack
1045 char buf[WAD_FLAT_NAME + 1];
1046 *buf = '\0';
1047 if (com != 0)
1048 strncat (buf, com, sizeof buf - 1);
1049 ReadFTextureNames ();
1050 {
1051 char **flat_names =
1052 (char **) GetMemory (NumFTexture * sizeof *flat_names);
1053 for (size_t n = 0; n < NumFTexture; n++)
1054 flat_names[n] = flat_list[n].name;
1055 ChooseFloorTexture (-1, -1, "Flat viewer",
1056 NumFTexture, flat_names, buf);
1057 FreeMemory (flat_names);
1058 }
1059 ForgetFTextureNames ();
1060 TermGfx ();
1061 viewflat_end:;
1062 }
1063
1064 // "viewpal" - view the palette (PLAYPAL and COLORMAP)
1065 else if (strcmp (com, "viewpal") == 0)
1066 {
1067 if (InitGfx ())
1068 goto viewpal_end;
1069 init_input_status ();
1070 do
1071 get_input_status ();
1072 while (is.key != YE_EXPOSE);
1073 force_window_not_pixmap (); // FIXME quick hack
1074 {
1075 Palette_viewer pv;
1076 pv.run ();
1077 }
1078 TermGfx ();
1079 viewpal_end:;
1080 }
1081
1082 // "viewpat" - view the patches
1083 else if (strcmp (com, "viewpat") == 0)
1084 {
1085 if (InitGfx ())
1086 goto viewpat_end;
1087 init_input_status ();
1088 do
1089 get_input_status ();
1090 while (is.key != YE_EXPOSE);
1091 com = strtok (NULL, " ");
1092 force_window_not_pixmap (); // FIXME quick hack
1093 patch_dir.refresh (MasterDir);
1094 {
1095 char buf[WAD_NAME + 1];
1096 *buf = '\0';
1097 if (com != 0)
1098 strncat (buf, com, sizeof buf - 1);
1099 Patch_list pl;
1100 patch_dir.list (pl);
1101 InputNameFromListWithFunc (-1, -1, "Patch viewer", pl.size (),
1102 pl.data (), 10, buf, 256, 256, display_pic,
1103 HOOK_DISP_SIZE | HOOK_PATCH);
1104 }
1105 TermGfx ();
1106 viewpat_end:;
1107 }
1108
1109 // "viewtex" - view the textures
1110 else if (strcmp (com, "viewtex") == 0)
1111 {
1112 if (InitGfx ())
1113 goto viewtex_end;
1114 init_input_status ();
1115 do
1116 get_input_status ();
1117 while (is.key != YE_EXPOSE);
1118 com = strtok (NULL, " ");
1119 force_window_not_pixmap (); // FIXME quick hack
1120 patch_dir.refresh (MasterDir);
1121 {
1122 char buf[WAD_TEX_NAME + 1];
1123 *buf = '\0';
1124 if (com != 0)
1125 strncat (buf, com, sizeof buf - 1);
1126 ReadWTextureNames ();
1127 ChooseWallTexture (-1, -1, "Texture viewer", NumWTexture, WTexture,
1128 buf);
1129 ForgetWTextureNames ();
1130 }
1131 TermGfx ();
1132 viewtex_end:;
1133 }
1134
1135 /* user asked to save an object to a separate pwad file */
1136 else if (strcmp (com, "save") == 0 || strcmp (com, "s") == 0)
1137 {
1138 com = strtok (NULL, " ");
1139 if (com == NULL)
1140 {
1141 printf ("Object name argument missing.\n");
1142 continue;
1143 }
1144 if (strlen (com) > WAD_NAME || strchr (com, '.') != NULL)
1145 {
1146 printf ("Invalid object name.\n");
1147 continue;
1148 }
1149 out = strtok (NULL, " ");
1150 if (out == NULL)
1151 {
1152 printf ("Wad file name argument missing.\n");
1153 continue;
1154 }
1155 if (wad_already_loaded (com))
1156 {
1157 printf ("%s: in use, close it first\n", com);
1158 continue;
1159 }
1160 printf ("Saving directory entry data to \"%s\".\n", out);
1161 if ((file = fopen (out, "wb")) == NULL)
1162 fatal_error ("error opening output file \"%s\"", out);
1163 SaveDirectoryEntry (file, com);
1164 fclose (file);
1165 }
1166
1167 /* user asked to encapsulate a raw file in a pwad file */
1168 else if (strcmp (com, "insert") == 0 || strcmp (com, "i") == 0)
1169 {
1170 com = strtok (NULL, " ");
1171 if (com == NULL)
1172 {
1173 printf ("Raw file name argument missing.\n");
1174 continue;
1175 }
1176 out = strtok (NULL, " ");
1177 if (out == NULL)
1178 {
1179 printf ("Object name argument missing.\n");
1180 continue;
1181 }
1182 if (strlen (out) > WAD_NAME || strchr (out, '.') != NULL)
1183 {
1184 printf ("Invalid object name.\n");
1185 continue;
1186 }
1187 if ((raw = fopen (com, "rb")) == NULL)
1188 fatal_error ("error opening input file \"%s\"", com);
1189 /* kluge */
1190 strcpy (input, out);
1191 strcat (input, ".wad");
1192 if (wad_already_loaded (input))
1193 {
1194 printf ("%s: in use, close it first\n", input);
1195 continue;
1196 }
1197 printf ("Including new object %s in \"%s\".\n", out, input);
1198 if ((file = fopen (input, "wb")) == NULL)
1199 fatal_error ("error opening output file \"%s\"", input);
1200 SaveEntryFromRawFile (file, raw, out);
1201 fclose (raw);
1202 fclose (file);
1203 }
1204
1205 /* user asked to extract an object to a raw binary file */
1206 else if (strcmp (com, "xtract") == 0
1207 || strcmp (com, "extract") == 0
1208 || strcmp (com, "x") == 0)
1209 {
1210 com = strtok (NULL, " ");
1211 if (com == NULL)
1212 {
1213 printf ("Object name argument missing.\n");
1214 continue;
1215 }
1216 if (strlen (com) > WAD_NAME || strchr (com, '.') != NULL)
1217 {
1218 printf ("Invalid object name.\n");
1219 continue;
1220 }
1221 out = strtok (NULL, " ");
1222 if (out == NULL)
1223 {
1224 printf ("Raw file name argument missing.\n");
1225 continue;
1226 }
1227 if (wad_already_loaded (com))
1228 {
1229 printf ("%s: in use, close it first\n", com);
1230 printf ("Besides do you really want to overwrite a wad file with"
1231 " raw data ?\n");
1232 continue;
1233 }
1234 printf ("Saving directory entry data to \"%s\".\n", out);
1235 if ((file = fopen (out, "wb")) == NULL)
1236 fatal_error ("error opening output file \"%s\"", out);
1237 SaveEntryToRawFile (file, com);
1238 fclose (file);
1239 }
1240
1241 /* unknown command */
1242 else
1243 printf ("Unknown command \"%s\"!\n", com);
1244 }
1245 }
1246
1247
1248 /*
1249 * add_base_colours
1250 * Add the NCOLOURS base colours to the list of
1251 * application colours.
1252 */
add_base_colours()1253 static void add_base_colours ()
1254 {
1255 for (size_t n = 0; n < NCOLOURS; n++)
1256 {
1257 rgb_c c;
1258
1259 // The first 16 are the standard IRGB VGA colours.
1260 // FIXME they're to be removed and replaced by
1261 // "logical" colours.
1262 #ifdef WHITE_BACKGROUND
1263 if (n == 0)
1264 irgb2rgb (15, &c);
1265 else if (n == 15)
1266 irgb2rgb (0, &c);
1267 else
1268 #endif
1269 if (n < 16)
1270 irgb2rgb (n, &c);
1271
1272 // Then there are the colours used to draw the
1273 // windows and the map. The colours used to draw
1274 // the things are parametrized in the .ygd ; they
1275 // are added by load_game().
1276 // FIXME they should not be hard-coded, of course !
1277
1278 /* WINBG* is for window backgrounds. Use the _HL variant is
1279 for highlighted parts of windows (E.G. the current line in
1280 a menu). _LIGHT and _DARK are for window borders and
1281 grooves. There is no _HL flavour of these because I didn't
1282 feel the need. */
1283 #ifdef WHITE_BACKGROUND
1284 else if (n == WINBG) c.set (0xe2, 0xdc, 0xd6);
1285 else if (n == WINBG_LIGHT) c.set (0xee, 0xe8, 0xe2);
1286 else if (n == WINBG_DARK) c.set (0xc3, 0xbe, 0xb9);
1287 else if (n == WINBG_HL) c.set (0xf4, 0xee, 0xe7);
1288 #else
1289 else if (n == WINBG) c.set (0x2a, 0x24, 0x18);
1290 else if (n == WINBG_LIGHT) c.set (0x48, 0x42, 0x3c);
1291 else if (n == WINBG_DARK) c.set (0x20, 0x1b, 0x12);
1292 else if (n == WINBG_HL) c.set (0x58, 0x50, 0x48);
1293 #endif
1294
1295 /* WINFG* is for regular text. _DIM is for greyed out text
1296 (for disabled options or text that is not applicable). */
1297 #ifdef WHITE_BACKGROUND
1298 else if (n == WINFG) c.set (0x60, 0x60, 0x60);
1299 else if (n == WINFG_HL) c.set (0x30, 0x30, 0x30);
1300 else if (n == WINFG_DIM) c.set (0xB8, 0xB8, 0xB8);
1301 else if (n == WINFG_DIM_HL) c.set (0x90, 0x90, 0x90);
1302 #else
1303 else if (n == WINFG) c.set (0xa0, 0xa0, 0xa0);
1304 else if (n == WINFG_HL) c.set (0xd0, 0xd0, 0xd0);
1305 else if (n == WINFG_DIM) c.set (0x48, 0x48, 0x48);
1306 else if (n == WINFG_DIM_HL) c.set (0x70, 0x70, 0x70);
1307 #endif
1308
1309 /* WINLABEL is for text of lesser importance. For example,
1310 the brackets around key binding are displayed in WINLABEL,
1311 while what's between them is displayed in WINFG. The
1312 difference with WINFG is not very noticeable but it does
1313 improve readability. The static text in the object info
1314 windows should be displayed in WINLABEL. */
1315 #ifdef WHITE_BACKGROUND
1316 else if (n == WINLABEL) c.set (0x88, 0x88, 0x88);
1317 else if (n == WINLABEL_HL) c.set (0x60, 0x60, 0x60);
1318 else if (n == WINLABEL_DIM) c.set (0xc8, 0xc8, 0xc8);
1319 else if (n == WINLABEL_DIM_HL) c.set (0xb0, 0xb0, 0xb0);
1320 #else
1321 else if (n == WINLABEL) c.set (0x78, 0x78, 0x78);
1322 else if (n == WINLABEL_HL) c.set (0xa0, 0xa0, 0xa0);
1323 else if (n == WINLABEL_DIM) c.set (0x38, 0x38, 0x38);
1324 else if (n == WINLABEL_DIM_HL) c.set (0x50, 0x50, 0x50);
1325 #endif
1326
1327 #ifdef WHITE_BACKGROUND
1328 else if (n == GRID1) c.set (0x80, 0x80, 0xff);
1329 else if (n == GRID2H) c.set (0xf0, 0xf0, 0xff);
1330 else if (n == GRID2V) c.set (0xf0, 0xf0, 0xff);
1331 else if (n == GRID3H) c.set (0xd0, 0xd0, 0xff);
1332 else if (n == GRID3V) c.set (0xd0, 0xd0, 0xff);
1333 else if (n == GRID4H) c.set (0xb0, 0xb0, 0xff);
1334 else if (n == GRID4V) c.set (0xb0, 0xb0, 0xff);
1335 #else
1336 else if (n == GRID1) c.set (0, 0, 0xc0);
1337 else if (n == GRID2H) c.set (0, 0, 0x30);
1338 else if (n == GRID2V) c.set (0, 0, 0x40);
1339 else if (n == GRID3H) c.set (0, 0, 0x50);
1340 else if (n == GRID3V) c.set (0, 0, 0x70);
1341 else if (n == GRID4H) c.set (0, 0, 0x80);
1342 else if (n == GRID4V) c.set (0, 0, 0xc0);
1343 #endif
1344
1345 else if (n == LINEDEF_NO) c.set (0x40, 0xd0, 0xf0);
1346 else if (n == SECTOR_NO) c.set (0x40, 0xd0, 0xf0);
1347 else if (n == THING_NO) c.set (0x40, 0xd0, 0xf0);
1348 else if (n == VERTEX_NO) c.set (0x40, 0xd0, 0xf0);
1349 else if (n == CLR_ERROR) c.set (0xff, 0, 0);
1350 else if (n == THING_REM) c.set (0x40, 0x40, 0x40);
1351
1352 else if (n == SECTOR_TAG) c.set (0x00, 0xff, 0x00);
1353 else if (n == SECTOR_TAGTYPE) c.set (0x00, 0xe0, 0xe0);
1354 else if (n == SECTOR_TYPE) c.set (0x00, 0x80, 0xff);
1355
1356 #ifdef WHITE_BACKGROUND
1357 else if (n == WINTITLE) c.set (0xb0, 0x80, 0x00);
1358 #else
1359 else if (n == WINTITLE) c.set (0xff, 0xff, 0x00);
1360 #endif
1361
1362 else fatal_error ("Wrong acn %d", n);
1363
1364 acolour_t acn = add_app_colour (c);
1365 if (acn != n)
1366 fatal_error ("add_base_colours: got %d for %d\n", acn, n);
1367 }
1368 }
1369
1370
wad_by_name(const char * pathname)1371 static const Wad_file *wad_by_name (const char *pathname)
1372 {
1373 const Wad_file *wf;
1374
1375 for (wad_list.rewind (); wad_list.get (wf);)
1376 if (fncmp (pathname, wf->pathname ()) == 0)
1377 return wf;
1378 return 0;
1379 }
1380
1381
wad_already_loaded(const char * pathname)1382 static bool wad_already_loaded (const char *pathname)
1383 {
1384 return wad_by_name (pathname) != 0;
1385 }
1386
1387
1388