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