1 /*
2  *	wads2.cc
3  *	Wads functions that are not needed during editing.
4  *	AYM 1998-08-09
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 "game.h"	/* yg_picture_format */
33 #include "serialnum.h"
34 #include "wadfile.h"
35 #include "wadlist.h"
36 #include "wads.h"
37 #include "wads2.h"
38 
39 
40 static char *locate_pwad (const char *filename);
41 static int level_name_order (const void *p1, const void *p2);
42 
43 
44 /*
45  *	OpenMainWad - open the iwad
46  *
47  *	Open the main wad file, read in its directory and create
48  *	the master directory.
49  *
50  *	Return 0 on success, non-zero on failure.
51  */
OpenMainWad(const char * filename)52 int OpenMainWad (const char *filename)
53 {
54 MDirPtr lastp, newp;
55 long n;
56 Wad_file *wf;
57 
58 /* open the wad file */
59 printf ("Loading iwad: %s...\n", filename);
60 wf = BasicWadOpen (filename, yg_picture_format);
61 if (wf == 0)
62    return 1;
63 if (strncmp (wf->type, "IWAD", 4))
64    warn ("%.128s: is a pwad, not an iwad. Will use it anyway.\n", filename);
65 
66 /* create the master directory */
67 lastp = NULL;
68 for (n = 0; n < wf->dirsize; n++)
69    {
70    newp = (MDirPtr) GetMemory (sizeof (struct MasterDirectory));
71    newp->next = NULL;
72    newp->wadfile = wf;
73    memcpy (&(newp->dir), &(wf->directory[n]), sizeof (struct Directory));
74    if (MasterDir)
75       lastp->next = newp;
76    else
77       MasterDir = newp;
78    lastp = newp;
79    }
80 master_dir_serial.bump ();
81 
82 /* check if registered version */
83 if (FindMasterDir (MasterDir, "E2M1") == NULL
84  && FindMasterDir (MasterDir, "MAP01") == NULL
85  && FindMasterDir (MasterDir, "MAP33") == NULL
86  && strcmp (Game, "doom02")
87  && strcmp (Game, "doom04")
88  && strcmp (Game, "doom05")
89  && strcmp (Game, "doompr"))
90    {
91    printf ("   *-----------------------------------------------------*\n");
92    printf ("   | Warning: this is the shareware version of the game. |\n");
93    printf ("   |     You won't be allowed to save your changes.      |\n");
94    printf ("   |       PLEASE REGISTER YOUR COPY OF THE GAME.        |\n");
95    printf ("   *-----------------------------------------------------*\n");
96    Registered = false; // If you remove this, bad things will happen to you...
97    }
98 else
99    Registered = true;
100 return 0;
101 }
102 
103 
104 /*
105  *	OpenPatchWad - add a pwad
106  *
107  *	Open a patch wad file, read in its directory and alter
108  *	the master directory.
109  *
110  *	Return 0 on success, non-zero on failure.
111  */
OpenPatchWad(const char * filename)112 int OpenPatchWad (const char *filename)
113 {
114 Wad_file * wad;
115 MDirPtr mdir = 0;
116 long n;
117 char entryname[WAD_NAME + 1];
118 const char *entry_type = 0;
119 char *real_name;
120 int nitems = 0;		// Number of items in group of flats/patches/sprites
121 
122 // Look for the file and ignore it if it doesn't exist
123 real_name = locate_pwad (filename);
124 if (real_name == NULL)
125    {
126    warn ("%.128s: not found.\n", filename);
127    return 1;
128    }
129 
130 /* open the wad file */
131 printf ("Loading pwad: %s...\n", real_name);
132 // By default, assume pwads use the normal picture format.
133 wad = BasicWadOpen (real_name, YGPF_NORMAL);
134 FreeMemory (real_name);
135 if (! wad)
136    return 1;
137 if (strncmp (wad->type, "PWAD", 4))
138    warn ("%.128s: is an iwad, not a pwad. Will use it anyway.\n", filename);
139 
140 /* alter the master directory */
141 
142 /* AYM: now, while the directory is scanned, a state variable is
143    updated. its values are :
144    0    no special state
145    1-11 reading level lumps */
146 /* AYM 1998-11-15: FIXME: to be on the safe side, should consider
147    FF_END to end a group of flats if the following entry is neither
148    F_END nor F?_START. */
149 
150 int state = 0;
151 int replaces = 0;
152 int state_prev;
153 int replaces_prev;
154 int names = 0;		// Number of names already printed on current line
155 const char *entry_type_prev;
156 typedef char level_list_item_t[WAD_NAME];
157 level_list_item_t *level_list = 0;
158 size_t nlevels = 0;
159 for (n = 0; n < wad->dirsize; n++)
160    {
161    strncpy (entryname, wad->directory[n].name, WAD_NAME);
162    entryname[WAD_NAME] = '\0';
163    state_prev = state;
164    replaces_prev = replaces;
165    entry_type_prev = entry_type;
166    if (state == 0)
167       {
168       if (! strcmp (entryname, "F_START")
169        || ! strcmp (entryname, "P_START")
170        || ! strcmp (entryname, "S_START"))
171 	 {
172 	 entry_type = "label";
173 	 replaces   = 0;
174 	 }
175       // DeuTex puts flats between FF_START and FF_END/F_END.
176       // All lumps between those markers are assumed
177       // to be flats.
178       else if (! strncmp (entryname, "FF_START", WAD_NAME))
179 	 {
180 	 state      = 'f';
181 	 entry_type = "group of flats";
182 	 replaces   = 0;
183          nitems     = 0;
184 	 }
185       // DeuTex puts patches between PP_START and PP_END.
186       // All lumps between those markers are assumed
187       // to be patches.
188       else if (! strncmp (entryname, "PP_START", WAD_NAME))
189 	 {
190 	 state      = 'p';
191 	 entry_type = "group of patches";
192 	 replaces   = 0;
193          nitems     = 0;
194 	 }
195       // DeuTex puts patches between SS_START and SS_END/S_END.
196       // All lumps between those markers are assumed
197       // to be sprites.
198       else if (! strncmp (entryname, "SS_START", WAD_NAME))
199 	 {
200 	 state      = 's';
201 	 entry_type = "group of sprites";
202 	 replaces   = 0;
203          nitems     = 0;
204 	 }
205       else
206 	 {
207 	 mdir = FindMasterDir (MasterDir, entryname);
208 	 replaces = mdir != NULL;
209 	 /* if it is a level, do the same thing for the next 10 entries too */
210 	 if (levelname2levelno (entryname))
211 	    {
212 	    state = 11;
213 	    entry_type = "level";
214 	    // Add to list of level names
215 	    {
216 	    level_list_item_t *new_list;
217 	    new_list = (level_list_item_t *)
218 	       realloc (level_list, (nlevels + 1) * sizeof *level_list);
219 	    if (new_list != 0)
220 	       {
221 	       level_list = new_list;
222 	       strncpy (level_list[nlevels], entryname, sizeof *level_list);
223 	       nlevels++;
224 	       }
225 	    }
226 	    }
227 	 else
228 	    entry_type = "entry";
229 	 }
230       if (n == 0
231 	  || state_prev      != state
232 	  || replaces_prev   != replaces
233 	  || entry_type_prev != entry_type)
234          {
235 	 if (n > 0)
236 	    verbmsg ("\n");
237 	 names = 0;
238 	 verbmsg ("  %s %s", replaces ? "Updating" : "Adding new", entry_type);
239          }
240       if (names >= 6)
241          {
242 	 verbmsg ("\n  %-*s %-*s",
243 	     strlen (replaces ? "Updating" : "Adding new"), "",
244 	     strlen (entry_type), "");
245 	 names = 0;
246          }
247       verbmsg  (" %-*s", WAD_NAME, entryname);
248       names++;
249       if ((*entry_type == 'm' || *entry_type == 'l') && wad->directory[n].size)
250 	 verbmsg (" warning: non-zero length (%ld)", wad->directory[n].size);
251       }
252    // Either F_END or FF_END mark the end of a
253    // DeuTex-generated group of flats.
254    else if (state == 'f')
255       {
256       if (! strncmp (entryname, "F_END", WAD_NAME)
257 	  || ! strncmp (entryname, "FF_END", WAD_NAME))
258          {
259 	 state = 0;
260          verbmsg ("/%.*s (%d flats)", WAD_NAME, entryname, nitems);
261          }
262       // Of course, F?_START and F?_END don't count
263       // toward the number of flats in the group.
264       else if (! (*entryname == 'F'
265                   && (! strncmp (entryname + 2, "_START", 6)
266                       || ! strcmp (entryname + 2, "_END"))))
267          nitems++;
268       }
269    // PP_END marks the end of a DeuTex-generated group of patches.
270    else if (state == 'p')
271       {
272       if (! strncmp (entryname, "PP_END", WAD_NAME))
273          {
274          state = 0;
275          verbmsg ("/PP_END (%d patches)", nitems);
276          }
277       // Of course, P?_START and P?_END don't count
278       // toward the number of flats in the group.
279       else if (! (*entryname == 'P'
280                   && (! strncmp (entryname + 2, "_START", 6)
281                       || ! strcmp (entryname + 2, "_END"))))
282          nitems++;
283       }
284    // Either S_END or SS_END mark the end of a
285    // DeuTex-generated group of sprites.
286    else if (state == 's')
287       {
288       if (! strncmp (entryname, "S_END", WAD_NAME)
289 	  || ! strncmp (entryname, "SS_END", WAD_NAME))
290          {
291 	 state = 0;
292          verbmsg ("/%.*s (%d sprites)", WAD_NAME, entryname, nitems);
293          }
294       // Of course, S?_START and S?_END don't count
295       // toward the number of sprites in the group.
296       else if (! (*entryname == 'S'
297                   && (! strncmp (entryname + 2, "_START", 6)
298                       || ! strcmp (entryname + 2, "_END"))))
299          nitems++;
300       }
301 
302    /* if this entry is not in the master directory, then add it */
303    if (!replaces)
304       {
305       mdir = MasterDir;
306       while (mdir->next)
307 	 mdir = mdir->next;
308       mdir->next = (MDirPtr) GetMemory (sizeof (struct MasterDirectory));
309       mdir = mdir->next;
310       mdir->next = NULL;
311       }
312    /* else, simply replace it */
313    mdir->wadfile = wad;
314    memcpy (&(mdir->dir), &(wad->directory[n]), sizeof (struct Directory));
315    mdir = mdir->next;
316 
317    if (state > 0 && state <= 11)
318       state--;
319    }
320 verbmsg ("\n");
321 master_dir_serial.bump ();
322 
323 // Print list of levels found in this pwad
324 if (level_list != 0)
325    {
326    printf ("  Levels: ");
327    qsort (level_list, nlevels, sizeof *level_list, level_name_order);
328    for (size_t n = 0; n < nlevels; n++)
329       {
330       int prev = n > 0           ? levelname2rank (level_list[n - 1]) : INT_MIN;
331       int cur  =                   levelname2rank (level_list[n    ]);
332       int next = n + 1 < nlevels ? levelname2rank (level_list[n + 1]) : INT_MAX;
333       if (cur != prev + 1 || cur != next - 1)
334          {
335          if (cur == prev + 1)
336 	    putchar ('-');
337 	 else if (n > 0)
338 	    putchar (' ');
339          printf ("%.*s", (int) sizeof *level_list, level_list[n]);
340          }
341       }
342    putchar ('\n');
343    free (level_list);
344    }
345 return 0;
346 }
347 
348 
349 /*
350  *	level_name_order - -cmp-style comparison of two level names
351  */
level_name_order(const void * p1,const void * p2)352 static int level_name_order (const void *p1, const void *p2)
353 {
354 return levelname2rank ((const char *) p1)
355      - levelname2rank ((const char *) p2);
356 }
357 
358 
359 /*
360  *	CloseWadFiles - close all wads
361  *
362  *	Close all the wad, deallocating the wad file structures.
363  */
CloseWadFiles()364 void CloseWadFiles ()
365 {
366 MDirPtr curd, nextd;
367 
368 // Close the wad files
369 Wad_file *wf;
370 wad_list.rewind ();
371 while (wad_list.get (wf))
372    wad_list.del ();
373 
374 // Delete the master directory
375 curd = MasterDir;
376 MasterDir = NULL;
377 while (curd)
378    {
379    nextd = curd->next;
380    FreeMemory (curd);
381    curd = nextd;
382    }
383 master_dir_serial.bump ();
384 }
385 
386 
387 /*
388  *	CloseUnusedWadFiles - forget unused patch wad files
389  */
CloseUnusedWadFiles()390 void CloseUnusedWadFiles ()
391 {
392 Wad_file *wf;
393 wad_list.rewind ();
394 while (wad_list.get (wf))
395    {
396    // Check if the wad file is used by a directory entry
397    MDirPtr mdir = MasterDir;
398    while (mdir && mdir->wadfile != wf)
399       mdir = mdir->next;
400    if (mdir == 0)
401       wad_list.del ();
402    }
403 }
404 
405 
406 /*
407  *	BasicWadOpen - open a wad
408  *
409  *	Basic opening of wad file and creation of node in Wad
410  *	linked list.
411  *
412  *	Return a null pointer on error.
413  */
BasicWadOpen(const char * filename,ygpf_t pic_format)414 Wad_file *BasicWadOpen (const char *filename, ygpf_t pic_format)
415 {
416 bool fail = false;
417 
418 /* If this wad is already open, close it first (it's not always
419    possible to open the same file twice). Also position the
420    wad_list pointer on the old wad (or at the end of the list if
421    this is a new wad) so that the reopening a wad doesn't change
422    it's relative position in the list.
423 
424    FIXME if reopening fails, we're left in the cold. I'm not
425    sure how to avoid that, though. */
426 {
427    Wad_file *dummy;
428    wad_list.rewind ();
429    while (wad_list.get (dummy))
430       if (fncmp (filename, dummy->filename) == 0)
431 	 {
432 	 wad_list.del ();
433 	 break;
434 	 }
435 }
436 
437 // Create a new Wad_file
438 Wad_file *wf = new Wad_file;
439 wf->pic_format_ = pic_format;
440 wf->directory   = 0;
441 wf->filename    = (char *) GetMemory (strlen (filename) + 1);
442 strcpy (wf->filename, filename);
443 
444 // Open the wad and read its header.
445 wf->fp = fopen (filename, "rb");
446 if (wf->fp == 0)
447    {
448    printf ("%.128s: %s\n", filename, strerror (errno));
449    fail = true;
450    goto byebye;
451    }
452 {
453 bool e = file_read_bytes (wf->fp, wf->type, 4);
454 e     |= file_read_i32   (wf->fp, &wf->dirsize);
455 e     |= file_read_i32   (wf->fp, &wf->dirstart);
456 if (e || memcmp (wf->type, "IWAD", 4) != 0 && memcmp (wf->type, "PWAD", 4) != 0)
457    {
458    printf ("%.128s: not a wad (bad header)\n", filename);
459    fail = true;
460    goto byebye;
461    }
462 }
463 verbmsg ("  Type %.4s, directory has %ld entries at offset %08lXh\n",
464    wf->type, (long) wf->dirsize, (long) wf->dirstart);
465 
466 // Load the directory of the wad
467 wf->directory = (DirPtr) GetMemory ((long) sizeof (struct Directory)
468    * wf->dirsize);
469 if (fseek (wf->fp, wf->dirstart, SEEK_SET) != 0)
470    {
471    printf ("%.128s: can't seek to directory at %08lXh\n",
472       filename, wf->dirstart);
473    fail = true;
474    goto byebye;
475    }
476 for (i32 n = 0; n < wf->dirsize; n++)
477    {
478    bool e  = file_read_i32   (wf->fp, &wf->directory[n].start);
479    e      |= file_read_i32   (wf->fp, &wf->directory[n].size);
480    e      |= file_read_bytes (wf->fp, wf->directory[n].name, WAD_NAME);
481    if (e)
482       {
483       printf ("%.128s: read error on directory entry %ld\n", filename, (long)n);
484       fail = true;
485       goto byebye;
486       }
487    }
488 
489 // Insert the new wad in the list
490 wad_list.insert (wf);
491 
492 byebye:
493 if (fail)
494   {
495   delete wf;
496   return 0;
497   }
498 return wf;
499 }
500 
501 
502 /*
503  *	ListMasterDirectory - list the master directory
504  */
ListMasterDirectory(FILE * file)505 void ListMasterDirectory (FILE *file)
506 {
507 char dataname[WAD_NAME + 1];
508 MDirPtr dir;
509 char key;
510 int lines = 3;
511 
512 dataname[WAD_NAME] = '\0';
513 fprintf (file, "The Master Directory\n");
514 fprintf (file, "====================\n\n");
515 fprintf (file, "NAME____  FILE______________________________________________"
516    "  SIZE__  START____\n");
517 for (dir = MasterDir; dir; dir = dir->next)
518    {
519    strncpy (dataname, dir->dir.name, WAD_NAME);
520    fprintf (file, "%-*s  %-50s  %6ld  x%08lx\n",
521     WAD_NAME, dataname, dir->wadfile->pathname (),
522     dir->dir.size, dir->dir.start);
523    if (file == stdout && lines++ > screen_lines - 4)
524       {
525       lines = 0;
526       printf ("['Q' followed by Return to abort, Return only to continue]");
527       key = getchar ();
528       printf ("\r%57s\r", "");
529       if (key == 'Q' || key == 'q')
530 	 {
531          getchar ();  // Read the '\n'
532          break;
533          }
534       }
535    }
536 }
537 
538 
539 /*
540  *	ListFileDirectory - list the directory of a wad
541  */
ListFileDirectory(FILE * file,const Wad_file * wad)542 void ListFileDirectory (FILE *file, const Wad_file *wad)
543 {
544 char dataname[WAD_NAME + 1];
545 char key;
546 int lines = 5;
547 long n;
548 
549 dataname[WAD_NAME] = '\0';
550 fprintf (file, "Wad File Directory\n");
551 fprintf (file, "==================\n\n");
552 fprintf (file, "Wad File: %s\n\n", wad->pathname ());
553 fprintf (file, "NAME____  SIZE__  START____  END______\n");
554 for (n = 0; n < wad->dirsize; n++)
555    {
556    strncpy (dataname, wad->directory[n].name, WAD_NAME);
557    fprintf (file, "%-*s  %6ld  x%08lx  x%08lx\n",
558      WAD_NAME, dataname,
559      wad->directory[n].size,
560      wad->directory[n].start,
561      wad->directory[n].size + wad->directory[n].start - 1);
562    if (file == stdout && lines++ > screen_lines - 4)
563       {
564       lines = 0;
565       printf ("['Q' followed by Return to abort, Return only to continue]");
566       key = getchar ();
567       printf ("\r%57s\r", "");
568       if (key == 'Q' || key == 'q')
569          {
570          getchar ();  // Read the '\n'
571 	 break;
572          }
573       }
574    }
575 }
576 
577 
578 /*
579  *	BuildNewMainWad - build a new iwad (or pwad)
580  *
581  *	Build a new wad file from master directory.
582  */
BuildNewMainWad(const char * filename,bool patchonly)583 void BuildNewMainWad (const char *filename, bool patchonly)
584 {
585 FILE *file;
586 long counter = 12;
587 MDirPtr cur;
588 long size;
589 long dirstart;
590 long dirnum;
591 
592 /* open the file and store signatures */
593 if (patchonly)
594    printf ("Building a compound Patch Wad file \"%s\".\n", filename);
595 else
596    printf ("Building a new Main Wad file \"%s\" (size approx 10 MB)\n",
597       filename);
598 if (FindMasterDir (MasterDir, "E2M4") == NULL
599  && FindMasterDir (MasterDir, "MAP01") == NULL
600  && FindMasterDir (MasterDir, "MAP33") == NULL
601  && strcmp (Game, "doom02")
602  && strcmp (Game, "doom04")
603  && strcmp (Game, "doom05")
604  && strcmp (Game, "doompr"))
605    fatal_error ("You were warned: you are not allowed to do this.");
606 if ((file = fopen (filename, "wb")) == NULL)
607    fatal_error ("unable to open file \"%s\"", filename);
608 if (patchonly)
609    WriteBytes (file, "PWAD", 4);
610 else
611    WriteBytes (file, "IWAD", 4);
612 file_write_i32 (file, 0xdeadbeef);      /* put true value in later */
613 file_write_i32 (file, 0xdeadbeef);      /* put true value in later */
614 
615 /* output the directory data chunks */
616 const Wad_file *iwad = 0;	// FIXME unreliable way of knowing the iwad
617 wad_list.rewind ();		// got to look into this
618 wad_list.get (iwad);
619 for (cur = MasterDir; cur; cur = cur->next)
620    {
621    if (patchonly && cur->wadfile == iwad)
622       continue;
623    size = cur->dir.size;
624    counter += size;
625    cur->wadfile->seek (cur->dir.start);
626    if (cur->wadfile->error ())
627       ;  // FIXME
628    if (copy_bytes (file, cur->wadfile->fp, size) != 0)
629       ;  // FIXME
630    printf ("Size: %luK\r", counter / 1024);
631    }
632 
633 /* output the directory */
634 dirstart = counter;
635 counter = 12;
636 dirnum = 0;
637 for (cur = MasterDir; cur; cur = cur->next)
638    {
639    if (patchonly && cur->wadfile == iwad)
640       continue;
641    if (dirnum % 100 == 0)
642       printf ("Outputting directory %04ld...\r", dirnum);
643    if (cur->dir.start)
644       file_write_i32 (file, counter);
645    else
646       file_write_i32 (file, 0);
647    file_write_i32 (file, cur->dir.size);
648    file_write_name (file, cur->dir.name);
649    counter += cur->dir.size;
650    dirnum++;
651    }
652 
653 /* fix up the number of entries and directory start information */
654 if (fseek (file, 4L, 0))
655    fatal_error ("error writing to file");
656 file_write_i32 (file, dirnum);
657 file_write_i32 (file, dirstart);
658 
659 /* close the file */
660 printf ("                            \r");
661 fclose (file);
662 }
663 
664 
665 /*
666  *	DumpDirectoryEntry - hexadecimal dump of a lump
667  *
668  *	Dump a directory entry in hex
669  */
DumpDirectoryEntry(FILE * file,const char * entryname)670 void DumpDirectoryEntry (FILE *file, const char *entryname)
671 {
672 char dataname[WAD_NAME + 1];
673 char key;
674 int lines = 5;
675 long n = 0;
676 unsigned char buf[16];
677 const int bytes_per_line = 16;
678 
679 for (MDirPtr entry = MasterDir; entry != 0; entry = entry->next)
680    {
681    if (y_strnicmp (entry->dir.name, entryname, WAD_NAME) != 0)
682       continue;
683    strncpy (dataname, entry->dir.name, WAD_NAME);
684    dataname[WAD_NAME] = '\0';
685    fprintf (file, "Contents of entry %s (size = %ld bytes):\n",
686       dataname, entry->dir.size);
687    const Wad_file *wf = entry->wadfile;
688    wf->seek (entry->dir.start);
689    for (n = 0; n < entry->dir.size;)
690       {
691       int i;
692       fprintf (file, "%04lX: ", n);
693 
694       // Nb of bytes to read for this line
695       long bytes_to_read = entry->dir.size - n;
696       if (bytes_to_read > bytes_per_line)
697 	 bytes_to_read = bytes_per_line;
698       long nbytes = wf->read_vbytes (buf, bytes_to_read);
699       if (wf->error ())
700 	 break;
701       n += nbytes;
702 
703       for (i = 0; i < nbytes; i++)
704 	 fprintf (file, " %02X", buf[i]);
705       for (; i < bytes_per_line; i++)
706 	 fputs ("   ", file);
707       fprintf (file, "   ");
708 
709       for (i = 0; i < nbytes; i++)
710 	 {
711 	 if (buf[i] >= 0x20
712 	    && buf[i] != 0x7f
713 #ifdef Y_UNIX
714 	    && ! (buf[i] >= 0x80 && buf[i] <= 0xa0)  // ISO 8859-1
715 #endif
716 	    )
717 	    putc (buf[i], file);
718 	 else
719 	    putc ('.', file);
720 	 }
721       putc ('\n', file);
722 
723       if (file == stdout && lines++ > screen_lines - 4)
724 	 {
725 	 lines = 0;
726 	 printf ("[%d%% - Q + Return to abort,"
727 	     " S + Return to skip this entry,"
728 	     " Return to continue]", (int) (n * 100 / entry->dir.size));
729 	 key = getchar ();
730 	 printf ("\r%68s\r", "");
731 	 if (key == 'S' || key == 's')
732 	    {
733 	    getchar ();  // Read the '\n'
734 	    break;
735 	    }
736 	 if (key == 'Q' || key == 'q')
737 	    {
738 	    getchar ();  // Read the '\n'
739 	    return;
740 	    }
741 	 }
742       }
743    }
744 if (! n)
745    {
746    printf ("Entry not in master directory.\n");
747    return;
748    }
749 }
750 
751 
752 
753 /*
754  *	SaveDirectoryEntry - write the contents of a lump to a new pwad
755  *
756  *	Save a directory entry to disk
757  */
SaveDirectoryEntry(FILE * file,const char * entryname)758 void SaveDirectoryEntry (FILE *file, const char *entryname)
759 {
760 MDirPtr entry;
761 
762 for (entry = MasterDir; entry; entry = entry->next)
763    if (!y_strnicmp (entry->dir.name, entryname, WAD_NAME))
764       break;
765 if (entry)
766    {
767    // Write the header
768    WriteBytes (file, "PWAD", 4);	// Type = PWAD
769    file_write_i32 (file, 1);		// 1 entry in the directory
770    file_write_i32 (file, 12);		// The directory starts at offset 12
771 
772    // Write the directory
773    file_write_i32 (file, 28);		// First entry starts at offset 28
774    file_write_i32 (file, entry->dir.size);	// Size of first entry
775    file_write_name (file, entry->dir.name);	// Name of first entry
776 
777    // Write the lump data
778    entry->wadfile->seek (entry->dir.start);
779    if (entry->wadfile->error ())
780       {
781       err ("%s: seek error", entryname);
782       return;
783       }
784    int r = copy_bytes (file, entry->wadfile->fp, entry->dir.size);
785    if (r != 0)
786       {
787       if (r == 1)
788 	err ("%s: error reading from source wad", entryname);
789       else if (r == 2)
790 	err ("%s: error writing to destination wad", entryname);
791       else
792 	nf_bug ("%s: copy_bytes() returned %d", entryname, r);
793       return;
794       }
795    }
796 else
797    {
798    printf ("Entry not in master directory.\n");
799    return;
800    }
801 }
802 
803 
804 /*
805  *	SaveEntryToRawFile - write the contents of a lump to a new file
806  *
807  *	Save a directory entry to disk, without a pwad header
808  */
SaveEntryToRawFile(FILE * file,const char * entryname)809 void SaveEntryToRawFile (FILE *file, const char *entryname)
810 {
811 MDirPtr entry;
812 
813 for (entry = MasterDir; entry; entry = entry->next)
814    if (!y_strnicmp (entry->dir.name, entryname, WAD_NAME))
815       break;
816 if (entry)
817    {
818    verbmsg ("Writing %ld bytes starting from offset %lX...\n",
819       (long) entry->dir.size, (unsigned long) entry->dir.start);
820    entry->wadfile->seek (entry->dir.start);
821    if (entry->wadfile->error ())
822       {
823       err ("%s: seek error", entryname);
824       return;
825       }
826    int r = copy_bytes (file, entry->wadfile->fp, entry->dir.size);
827    if (r != 0)
828       {
829       if (r == 1)
830 	err ("%s: error reading from source wad", entryname);
831       else if (r == 2)
832 	err ("%s: error writing to destination file", entryname);
833       else
834 	nf_bug ("%s: copy_bytes() returned %d", entryname, r);
835       return;
836       }
837    }
838 else
839    {
840    printf ("[Entry not in master directory]\n");
841    return;
842    }
843 }
844 
845 
846 /*
847  *	SaveEntryFromRawFile - encapsulate a raw file in a pwad
848  *
849  *	Encapsulate a raw file in a pwad file
850  */
SaveEntryFromRawFile(FILE * file,FILE * raw,const char * entryname)851 void SaveEntryFromRawFile (FILE *file, FILE *raw, const char *entryname)
852 {
853 long    size;
854 char    name8[WAD_NAME];
855 
856 // Write the header
857 WriteBytes (file, "PWAD", 4);		// Type = PWAD
858 file_write_i32 (file, 1);		// 1 entry in the directory
859 file_write_i32 (file, 12);		// The directory starts at offset 12
860 
861 // Write the directory
862 file_write_i32 (file, 28);		// First entry starts at offset 28
863 
864 if (fseek (raw, 0L, SEEK_END) != 0)
865    fatal_error ("error reading from raw file");
866 size = ftell (raw);
867 if (size < 0)
868    fatal_error ("error reading from raw file");
869 if (fseek (raw, 0L, SEEK_SET) != 0)
870    fatal_error ("error reading from raw file");
871 file_write_i32 (file, size);		// Size of first entry
872 
873 memset (name8, '\0', WAD_NAME);
874 strncpy (name8, entryname, WAD_NAME);
875 file_write_name (file, name8);		// Name of first entry
876 
877 // Write the lump data
878 int r = copy_bytes (file, raw, size);
879 if (r != 0)
880    {
881    if (r == 1)
882      err ("%s: error reading from source file", entryname);
883    else if (r == 2)
884      err ("%s: error writing to destination wad", entryname);
885    else
886      nf_bug ("%s: copy_bytes() returned %d", entryname, r);
887    return;
888    }
889 }
890 
891 
892 /*
893  *	locate_pwad
894  *	Look for a PWAD in the standard directories
895  *	and returns its name in a GetMemory'd buffer
896  *	(or NULL if not found). It's up to the caller
897  *	to free the buffer after use.
898  */
899 
900 
901 /* Directories that are searched for PWADs */
902 static const char *standard_directories[] =
903    {
904    "",
905    "~/",                            // "~" means "the user's home directory"
906    "/usr/local/share/games/%s/",    // %s is replaced by <Game>
907    "/usr/share/games/%s/",          // %s is replaced by <Game>
908    "/usr/local/share/games/wads/",
909    "/usr/share/games/wads/",
910    0
911    };
912 
913 
locate_pwad(const char * filename)914 static char *locate_pwad (const char *filename)
915 {
916 al_fext_t ext;
917 const char **dirname;
918 char *real_basename;
919 char *real_name;
920 
921 // Get the extension in <ext>
922 al_fana (filename, NULL, NULL, NULL, ext);
923 
924 // If it's an absolute name, stop there.
925 if (is_absolute (filename))
926    {
927    real_name = (char *) GetMemory (strlen (filename) + 1 + (*ext ? 0 : 4));
928    strcpy (real_name, filename);
929    if (! *ext)
930       strcat (real_name, ".wad");
931    bool r = file_exists (real_name);
932    if (! r)
933       {
934       FreeMemory (real_name);
935       return 0;
936       }
937    return real_name;
938    }
939 
940 // It's a relative name. If no extension given, append ".wad"
941 real_basename = (char *) GetMemory (strlen (filename) + 1 + (*ext ? 0 : 4));
942 strcpy (real_basename, filename);
943 if (! *ext)
944    strcat (real_basename, ".wad");
945 
946 // Then search for a file of that name in the standard directories.
947 real_name = (char *) GetMemory (Y_FILE_NAME + 1);
948 for (dirname = standard_directories; *dirname; dirname++)
949    {
950    if (! strcmp (*dirname, "~/"))
951       if (getenv ("HOME"))
952          {
953 	 al_scps (real_name, getenv ("HOME"), Y_FILE_NAME);
954          al_sapc (real_name, '/', Y_FILE_NAME);
955          }
956       else
957 	 continue;
958    else
959       y_snprintf (real_name, Y_FILE_NAME + 1, *dirname, Game ? Game : "");
960    al_saps (real_name, real_basename, Y_FILE_NAME);
961    verbmsg ("  Trying \"%s\"... ", real_name);
962    if (file_exists (real_name))
963       {
964       verbmsg ("right on !\n");
965       FreeMemory (real_basename);
966       return real_name;
967       }
968    verbmsg ("nuts\n");
969    }
970 FreeMemory (real_name);
971 FreeMemory (real_basename);
972 return NULL;
973 }
974 
975 
976 /* end of file */
977 
978