1 /*
2   This file is Copyright © 1994-1995 Olivier Montanuy,
3                Copyright © 1999-2005 André Majorel,
4                Copyright © 2006-2019 contributors to the DeuTex project.
5 
6   DeuTex incorporates code derived from DEU 5.21 that was put in the
7   public domain in 1994 by Raphaël Quinet and Brendon Wyber.
8 
9   SPDX-License-Identifier: GPL-2.0-or-later
10 */
11 
12 #include "deutex.h"
13 
14 #include <errno.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <time.h>
18 #include <unistd.h>
19 
20 #include "tools.h"
21 #include "mkwad.h"
22 #include "ident.h"
23 #include "wadio.h"
24 
25 /*
26 ** Open PWAD file. *pcount=global byte counter
27 ** leaves number of entries and directory pointer NULL
28 */
29 
30 const int WADR_READ = 1;
31 const int WADR_WRITE = 2;
32 const int WADR_RDWR = 3;
33 const int WADR_PIPO = 8;
34 
WADRopenPipo(struct WADINFO * info,int32_t ntry)35 void WADRopenPipo(struct WADINFO *info, int32_t ntry)
36 {                               /*directory */
37     if ((info->ok & WADR_RDWR))
38         Bug("WR80", "WadPpk");
39     info->ok = WADR_PIPO;
40     if (ntry <= 0)
41         Bug("WR81", "WadPpo");
42     info->maxdir = ntry;
43     info->dir =
44         (struct WADDIR *) Malloc((info->maxdir) * sizeof(struct WADDIR));
45     info->maxpos = ntry * sizeof(struct WADDIR);
46     info->ntry = 0;
47     info->wposit = info->maxpos;
48 }
49 
WADRclosePipo(struct WADINFO * info,int32_t * ntry)50 struct WADDIR *WADRclosePipo(struct WADINFO *info, int32_t * ntry)
51 {
52     if ((info->ok != WADR_PIPO))
53         Bug("WR84", "WadPpc");
54     info->ok = false;
55     if (info->ntry < 0)
56         info->ntry = 0;
57     info->dir = (struct WADDIR *)
58         Realloc(info->dir, (info->ntry) * sizeof(struct WADDIR));
59     *ntry = info->ntry;
60     return info->dir;
61 }
62 
WADRdirAddPipo(struct WADINFO * info,int32_t start,int32_t size,const char * entry)63 int32_t WADRdirAddPipo(struct WADINFO * info, int32_t start, int32_t size,
64                        const char *entry)
65 {
66     int16_t n;
67     if (info->ok != WADR_PIPO)
68         Bug("WR82", "WadDaP");
69     n = (int16_t) info->ntry;   /*position of new entry */
70     if (n < 0)
71         Bug("WR83", "WadDa2");
72     if (n < info->maxdir) {     /*can add to the dir */
73         info->ntry++;           /*new dir size */
74         info->dir[n].size = size;
75         info->dir[n].start = start;
76         Normalise(info->dir[n].name, entry);
77     }
78     return n;                   /* nb entries */
79 }
80 
WADRopenR(struct WADINFO * info,const char * wadin)81 void WADRopenR(struct WADINFO *info, const char *wadin)
82 {                               /*directory */
83     int32_t ntry, dirpos;
84     int16_t n;
85     struct WADDIR dir;
86     if ((info->ok & WADR_RDWR))
87         Bug("WR01", "%s: WadOpr", fname(wadin));
88     info->fd = fopen(wadin, FOPEN_RB);
89     if ((info->fd) == NULL)
90         ProgError("WR03", "%s: %s", fname(wadin), strerror(errno));
91     info->filename = Malloc(strlen(wadin) + 1);
92     strcpy(info->filename, wadin);
93     info->ok = WADR_READ;
94     /*signature */
95     switch (WADRreadShort(info)) {
96     case PWAD:
97     case IWAD:
98         break;
99     default:
100         ProgError("WR05", "%s: invalid wad magic", fname(wadin));
101     }
102     if (WADRreadShort(info) != WADMAGIC)
103         ProgError("WR07", "%s: read error in header", fname(wadin));
104     /*start of directory */
105     ntry = WADRreadLong(info);
106     if (ntry <= 0)
107         ProgError("WR09", "%s: zero entries", fname(wadin));
108     if (ntry > 0xfce)
109         Warning("WR11", "%s has more than 4046 lumps: vanilla-incompatible", fname(wadin));
110     info->dirpos = dirpos = WADRreadLong(info);
111     if ((dirpos < 0) || (dirpos > 0x10000000L))
112         ProgError("WR13", "%s: invalid directory offset %08lX",
113                   fname(wadin), (unsigned long) dirpos);
114     /*allocate directory */
115     info->maxdir = ntry;
116     info->dir =
117         (struct WADDIR *) Malloc((info->maxdir) * sizeof(struct WADDIR));
118     /*read directory, calculate further byte in wad */
119     info->maxpos = dirpos + (ntry * sizeof(struct WADDIR));
120     WADRseek(info, dirpos);
121     info->ntry = 0;
122     for (n = 0; n < ntry; n++) {
123         if (wad_read_i32(info->fd, &dir.start)
124             || wad_read_i32(info->fd, &dir.size)
125             || wad_read_name(info->fd, dir.name))
126             ProgError("WR15", "%s: read error in directory", fname(wadin));
127         WADRdirAddEntry(info, dir.start, dir.size, dir.name);
128     }
129     if (info->ntry != ntry)
130         Bug("WR17", "WadOrN %ld %ld", (long) ntry, (long) info->ntry);
131     info->wposit = info->maxpos;
132     Phase("WR19", "Reading WAD %s:\t(%ld entries)", fname(wadin),
133           (long) ntry);
134 }
135 
WADRopenW(struct WADINFO * info,const char * wadout,WADTYPE type,int verbose)136 void WADRopenW(struct WADINFO *info, const char *wadout, WADTYPE type,
137                int verbose)
138 {
139     if (verbose)
140         Phase("WW01", "Creating %s %s", (type == IWAD) ? "iwad" : "pwad",
141               fname(wadout));
142     if ((info->ok & WADR_RDWR))
143         Bug("WW02", "%s: WadOpW", fname(wadout));
144 
145     /*check file */
146     if (clobber == CLOBBER_NO) {
147         info->fd = fopen(wadout, FOPEN_RB);
148         if (info->fd != NULL)
149             ProgError("WW03", "Won't overwrite existing file %s",
150                       fname(wadout));
151     }
152     /*open file */
153     info->fd = fopen(wadout, FOPEN_WB);
154     if (info->fd == NULL)
155         ProgError("WW04", "%s: %s", fname(wadout), strerror(errno));
156     info->filename = Malloc(strlen(wadout) + 1);
157     strcpy(info->filename, wadout);
158     info->ok = WADR_WRITE;
159     info->wposit = 0;
160     info->ntry = 0;
161     info->maxdir = MAXPWADDIR;
162     info->dir =
163         (struct WADDIR *) Malloc((info->maxdir) * sizeof(struct WADDIR));
164     WADRwriteShort(info, type); /* WAD type: PW or IW */
165     WADRwriteShort(info, WADMAGIC);     /* WAD type: AD */
166     /* will be fixed when closing the WAD */
167     WADRwriteLong(info, -1);    /* no counter of dir entries */
168     WADRwriteLong(info, -1);    /* no dir pointer */
169     WADRalign4(info);
170 }
171 
172 /*
173 ** Assumes file already opened write
174 ** if not, open it first
175 ** OPEN READ-WRITE,not APPEND
176 ** because APPEND can't FSEEK to start of file
177 */
WADRopenA(struct WADINFO * info,const char * wadinout)178 void WADRopenA(struct WADINFO *info, const char *wadinout)
179 {
180     Phase("WW10", "Modifying wad %s", fname(wadinout));
181     if ((info->ok & WADR_WRITE))
182         Bug("WW11", "%s: WadOpA", fname(wadinout));
183     if (!(info->ok & WADR_READ)) {
184         WADRopenR(info, wadinout);
185     }
186     /*reopen for append */
187     fclose(info->fd);
188     info->fd = fopen(wadinout, FOPEN_RBP);      /*rb+ = read/write binary */
189     if (info->fd == NULL)
190         ProgError("WW12", "%s: %s", fname(wadinout), strerror(errno));
191     info->filename = Malloc(strlen(wadinout) + 1);
192     strcpy(info->filename, wadinout);
193     info->ok = WADR_RDWR;
194     WADRseek(info, info->wposit);
195 }
196 
197 /*
198 ** Add a new entry in the directory
199 ** increase ntry, redim dir
200 ** update maxdir and maxpos
201 ** returns entry ref
202 */
WADRdirAddEntry(struct WADINFO * info,int32_t start,int32_t size,const char * entry)203 int32_t WADRdirAddEntry(struct WADINFO *info, int32_t start, int32_t size, const char
204                         *entry)
205 {
206     int16_t n;
207     int32_t sz;
208     if (!(info->ok & (WADR_RDWR)))
209         Bug("WW40", "%s: WadDAE", fname(info->filename));
210     n = (int16_t) info->ntry;   /*position of new entry */
211     if (n >= info->maxdir) {    /*shall we move the dir? */
212         info->maxdir += MAXPWADDIR;
213         info->dir =
214             (struct WADDIR *) Realloc((char *) info->dir,
215                                       (info->maxdir) *
216                                       sizeof(struct WADDIR));
217     }
218     info->ntry++;               /*new dir size */
219     info->dir[n].size = size;
220     info->dir[n].start = start;
221     Normalise(info->dir[n].name, entry);
222     sz = start + size;
223     if (sz > info->maxpos)
224         info->maxpos = sz;
225     return n;                   /* nb entries */
226 }
227 
228 /*
229 ** write the directory (names, counts, lengths)
230 ** then update the number of  entries and dir pointer
231 */
WADRwriteDir(struct WADINFO * info,int verbose)232 void WADRwriteDir(struct WADINFO *info, int verbose)
233 {
234     int16_t n;
235     if (!(info->ok & WADR_WRITE))
236         Bug("WW20", "%s: WadWD", fname(info->filename));
237     WADRalign4(info);           /*align entry on int32_t word */
238     info->dirpos = info->wposit;        /*current position */
239     /* write the new WAD directory */
240     for (n = 0; n < info->ntry; n++) {
241         if (wad_write_i32(info->fd, info->dir[n].start)
242             || wad_write_i32(info->fd, info->dir[n].size)
243             || wad_write_name(info->fd, info->dir[n].name))
244             ProgError("WW22", "%s: write error in directory",
245                       fname(info->filename));
246     }
247     /* fix up the directory start information */
248     WADRsetDirRef(info, info->ntry, info->dirpos);
249     n = (int16_t) (info->dirpos + (sizeof(struct WADDIR) * info->ntry));
250     if (n > info->maxpos)
251         info->maxpos = n;
252     if (verbose)
253         Phase("WW28", "%s: wad is complete, %ld entries, %ld bytes",
254               fname(info->filename), (long) info->ntry,
255               (long) info->wposit);
256 }
257 
WADRsetDirRef(struct WADINFO * info,int32_t ntry,int32_t dirpos)258 void WADRsetDirRef(struct WADINFO *info, int32_t ntry, int32_t dirpos)
259 {
260     if (!(info->ok & WADR_WRITE))
261         Bug("WW24", "%s: WadSDR", fname(info->filename));
262     WADRseek(info, 4);
263     if (wad_write_i32(info->fd, ntry)
264         || wad_write_i32(info->fd, dirpos))
265         ProgError("WW26", "%s: write error while fixing up the header",
266                   fname(info->filename));
267     WADRseek(info, info->wposit);
268     info->ntry = ntry;
269     info->dirpos = dirpos;
270 }
271 
WADRchsize(struct WADINFO * info,int32_t fsize)272 void WADRchsize(struct WADINFO *info, int32_t fsize)
273 {
274     if (!(info->ok & WADR_WRITE))
275         Bug("WW93", "WadcSz");
276     if (ftruncate(fileno(info->fd), fsize) != 0)
277         ProgError("WW95", "%s: %s", fname(info->filename),
278                   strerror(errno));
279     /*@ AEO while trying the change the size of the wad */
280     info->maxpos = fsize;
281     info->wposit = fsize;
282 }
283 
WADRseek(struct WADINFO * info,int32_t position)284 void WADRseek(struct WADINFO *info, int32_t position)
285 {
286     long ofs;
287     if (!(info->ok & WADR_RDWR))
288         Bug("WR31", "WadSk");
289     ofs = ftell(info->fd);
290     if (position > info->maxpos)
291         Bug("WR33", "WadSk>");
292     if (fseek(info->fd, position, SEEK_SET))
293         ProgError("WR35", "%s: Can't seek to %06lXh (%s)",
294                   fnameofs(info->filename, ofs), (unsigned long) position,
295                   strerror(errno));
296 }
297 
298 /*
299  *      WADRreadBytes2
300  *      Attempt to read up to <nbytes> bytes from wad <info>.
301  *      Return the actual number of bytes read.
302  */
WADRreadBytes2(struct WADINFO * info,char * buffer,iolen_t nbytes)303 iolen_t WADRreadBytes2(struct WADINFO *info, char *buffer, iolen_t nbytes)
304 {
305     size_t attempt = MEMORYCACHE;
306     iolen_t bytes_read = 0;
307 
308     if (!(info->ok & WADR_READ))
309         Bug("WR41", "WadRdB");
310 
311     while (nbytes > 0) {
312         size_t result;
313         if (attempt > nbytes)
314             attempt = nbytes;
315         result = fread(buffer, 1, attempt, info->fd);
316         bytes_read += result;
317         if (result == 0)        /* Hit EOF */
318             break;
319         buffer += result;
320         nbytes -= result;
321     }
322 
323     return bytes_read;
324 }
325 
326 /*
327  *      WADRreadBytes
328  *      Attempt to read <nbytes> bytes from wad <info>. If EOF
329  *      is hit too soon, trigger a fatal error. Else, return
330  *      <nbytes>.
331  */
WADRreadBytes(struct WADINFO * info,char * buffer,iolen_t nbytes)332 iolen_t WADRreadBytes(struct WADINFO * info, char *buffer, iolen_t nbytes)
333 {
334     long ofs = ftell(info->fd);
335     iolen_t result = WADRreadBytes2(info, buffer, nbytes);
336     if (result != nbytes) {
337         if (ferror(info->fd)) {
338             ProgError("WR43", "%s: read error (got %lu/%lu bytes)",
339                       fnameofs(info->filename, ofs),
340                       (unsigned long) result, (unsigned long) nbytes);
341         } else {
342             ProgError("WR45", "%s: unexpected EOF (got %lu/%lu bytes)",
343                       fnameofs(info->filename, ofs),
344                       (unsigned long) result, (unsigned long) nbytes);
345         }
346     }
347 
348     return nbytes;
349 }
350 
WADRreadShort(struct WADINFO * info)351 int16_t WADRreadShort(struct WADINFO * info)
352 {
353     int16_t res;
354     long ofs = ftell(info->fd);
355     if (!(info->ok & WADR_READ))
356         Bug("WR51", "WadRdS");
357     if (wad_read_i16(info->fd, &res)) {
358         if (ferror(info->fd)) {
359             ProgError("WR53", "%s: read error",
360                       fnameofs(info->filename, ofs));
361         } else {
362             ProgError("WR55", "%s: unexpected EOF",
363                       fnameofs(info->filename, ofs));
364         }
365     }
366     return res;
367 }
368 
WADRreadLong(struct WADINFO * info)369 int32_t WADRreadLong(struct WADINFO * info)
370 {
371     int32_t res;
372     long ofs = ftell(info->fd);
373     if (!(info->ok & WADR_READ))
374         Bug("WR61", "WadRdL");
375     if (wad_read_i32(info->fd, &res)) {
376         if (ferror(info->fd)) {
377             ProgError("WR63", "%s: read error",
378                       fnameofs(info->filename, ofs));
379         } else {
380             ProgError("WR65", "%s: unexpected EOF",
381                       fnameofs(info->filename, ofs));
382         }
383     }
384     return res;
385 }
386 
WADRclose(struct WADINFO * info)387 void WADRclose(struct WADINFO *info)
388 {
389     if (!(info->ok & WADR_RDWR))
390         Bug("WR97", "WadClo");
391     info->ok = false;
392     free(info->filename);
393     free(info->dir);
394     fclose(info->fd);
395 }
396 
WADRfindEntry(struct WADINFO * info,const char * entry)397 int16_t WADRfindEntry(struct WADINFO *info, const char *entry)
398 {
399     int16_t i;
400     static char name[8];
401     struct WADDIR *dir;
402     if (!(info->ok & WADR_RDWR))
403         Bug("WR91", "WadFE");
404     for (i = 0, dir = info->dir; i < info->ntry; i++, dir += 1) {
405         Normalise(name, dir->name);
406         if (strncmp(name, entry, 8) == 0) {
407             return i;
408         }
409     }
410     return -1;
411 }
412 
413 /*
414 ** load data in buffer
415 */
WADRreadEntry(struct WADINFO * info,int16_t n,int32_t * psize)416 char *WADRreadEntry(struct WADINFO *info, int16_t n, int32_t * psize)
417 {
418     char *buffer;
419     int32_t start, size;
420     if (!(info->ok & WADR_READ))
421         Bug("WR71", "WadRE");
422     if (n >= (info->ntry))
423         Bug("WR73", "WadRE>");
424     start = info->dir[n].start;
425     size = info->dir[n].size;
426     buffer = (char *) Malloc(size);
427     WADRseek(info, start);
428     WADRreadBytes(info, buffer, size);
429     *psize = size;
430     return buffer;
431 }
432 
433 /*
434  *      WADRreadEntry2
435  *      Read at most the *psize first bytes of a lump.
436  *      If the lump is shorter than *psize, it's _not_ an error.
437  *      Return the actual number of bytes read in *psize.
438  */
WADRreadEntry2(struct WADINFO * info,int16_t n,int32_t * psize)439 char *WADRreadEntry2(struct WADINFO *info, int16_t n, int32_t * psize)
440 {
441     char *buffer;
442     long start;
443     iolen_t size;
444     iolen_t actual_size;
445 
446     if (!(info->ok & WADR_READ))
447         Bug("WR75", "WadRE");
448     if (n >= info->ntry)
449         Bug("WR76", "WadRE>");
450     start = info->dir[n].start;
451     size = *psize < info->dir[n].size ? *psize : info->dir[n].size;
452     buffer = Malloc(size);
453     WADRseek(info, start);
454     actual_size = WADRreadBytes2(info, buffer, size);
455     if (actual_size < size) {
456         if (ferror(info->fd)) {
457             ProgError("WR78", "%s: Lump %s: read error at byte %ld",
458                       fnameofs(info->filename, start + actual_size),
459                       lump_name(info->dir[n].name), (long) actual_size);
460         } else {
461             ProgError("WR79", "%s: Lump %s: unexpected EOF at byte %ld",
462                       fnameofs(info->filename, start + actual_size),
463                       lump_name(info->dir[n].name), (long) actual_size);
464         }
465     }
466     *psize = actual_size;
467     return buffer;
468 }
469 
470 /*
471 **  copy data from WAD to file
472 */
WADRsaveEntry(struct WADINFO * info,int16_t n,const char * file)473 void WADRsaveEntry(struct WADINFO *info, int16_t n, const char *file)
474 {
475     int32_t wsize, sz = 0;
476     char *buffer;
477     int32_t start, size;
478     FILE *fd;
479     if (!(info->ok & WADR_READ))
480         Bug("WR86", "WadSE");
481     if (n >= (info->ntry))
482         Bug("WR87", "WadSE>");
483     start = info->dir[n].start;
484     size = info->dir[n].size;
485     fd = fopen(file, FOPEN_WB);
486     if (fd == NULL)
487         ProgError("WR88", "%s: Can't open for writing (%s)", fname(file),
488                   strerror(errno));
489     buffer = (char *) Malloc(MEMORYCACHE);
490     WADRseek(info, start);
491     for (wsize = 0; wsize < size; wsize += sz) {
492         sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : size - wsize;
493         WADRreadBytes(info, buffer, sz);
494         if (fwrite(buffer, (size_t) sz, 1, fd) != 1) {
495             free(buffer);
496             ProgError("WR89", "%s: write error", fname(file));
497         }
498     }                           /*declare in WAD directory */
499     free(buffer);
500     if (fclose(fd))
501         ProgError("WR90", "%s: %s", fname(file), strerror(errno));
502 }
503 
WADRsetLong(struct WADINFO * info,int32_t pos,int32_t val)504 void WADRsetLong(struct WADINFO *info, int32_t pos, int32_t val)
505 {
506     if (!(info->ok & WADR_WRITE))
507         Bug("WW56", "WadStL");
508     if (pos > (info->maxpos))
509         Bug("WW57", "WadSL>");
510     if (fseek(info->fd, pos, SEEK_SET))
511         ProgError("WW58", "%s: Can't seek to %06lXh (%s)",
512                   fname(info->filename), (unsigned long) pos,
513                   strerror(errno));
514     if (wad_write_i32(info->fd, val))
515         ProgError("WW59", "%s: write error",
516                   fnameofs(info->filename, pos));
517 }
518 
WADRsetShort(struct WADINFO * info,int32_t pos,int16_t val)519 void WADRsetShort(struct WADINFO *info, int32_t pos, int16_t val)
520 {
521     if (!(info->ok & WADR_WRITE))
522         Bug("WW51", "WadStS");
523     if (pos > (info->maxpos))
524         Bug("WW52", "WadSS>");
525     if (fseek(info->fd, pos, SEEK_SET))
526         ProgError("WW53", "%s: Can't seek to %06lXh (%s)",
527                   fname(info->filename), (unsigned long) pos,
528                   strerror(errno));
529     if (wad_write_i16(info->fd, val))
530         ProgError("WW54", "%s: write error",
531                   fnameofs(info->filename, pos));
532 }
533 
534 /*
535 ** internal functions
536 **
537 */
WADRcheckWritePos(struct WADINFO * info)538 static void WADRcheckWritePos(struct WADINFO *info)
539 {
540     if (!(info->ok & WADR_WRITE))
541         Bug("WW61", "WadCkW");
542     if (fseek(info->fd, info->wposit, SEEK_SET))
543         ProgError("WW63", "%s: Can't seek to %06lXh (%s)",
544                   fnameofs(info->filename, ftell(info->fd)), info->wposit,
545                   strerror(errno));
546 }
547 
WADRwriteBlock(struct WADINFO * info,char * data,int32_t sz)548 static int32_t WADRwriteBlock(struct WADINFO *info, char *data, int32_t sz)
549 {
550     if (fwrite(data, (size_t) sz, 1, info->fd) != 1)
551         ProgError("WW65", "%s: write error", fname(info->filename));
552     info->wposit += sz;
553     if (info->maxpos < info->wposit)
554         info->maxpos = info->wposit;
555     return sz;
556 }
557 
558 /*
559 ** align, give position
560 */
WADRalign4(struct WADINFO * info)561 void WADRalign4(struct WADINFO *info)
562 {
563     int16_t remain;
564     static char buffer[] = { 0, 0x24, 0x6, 0x68 };
565     WADRcheckWritePos(info);
566     remain = (int16_t) (info->wposit & 0x03);   /*0 to 3 */
567     if (remain > 0)
568         WADRwriteBytes(info, buffer, 4 - remain);
569 }
570 
571 /*must be equal to ftell*/
WADRposition(struct WADINFO * info)572 int32_t WADRposition(struct WADINFO *info)
573 {
574     WADRcheckWritePos(info);
575     return info->wposit;
576 }
577 
578 /*
579 ** write
580 */
WADRwriteLong(struct WADINFO * info,int32_t val)581 int32_t WADRwriteLong(struct WADINFO * info, int32_t val)
582 {
583     WADRcheckWritePos(info);
584     if (wad_write_i32(info->fd, val))
585         ProgError("WW67", "%s: write error", fname(info->filename));
586     info->wposit += sizeof val;
587     if (info->maxpos < info->wposit)
588         info->maxpos = info->wposit;
589     return sizeof val;
590 }
591 
WADRwriteShort(struct WADINFO * info,int16_t val)592 int32_t WADRwriteShort(struct WADINFO *info, int16_t val)
593 {
594     WADRcheckWritePos(info);
595     if (wad_write_i16(info->fd, val))
596         ProgError("WW69", "%s: write error", fname(info->filename));
597     info->wposit += sizeof val;
598     if (info->maxpos < info->wposit)
599         info->maxpos = info->wposit;
600     return sizeof val;
601 }
602 
WADRwriteBytes(struct WADINFO * info,char * data,int32_t size)603 int32_t WADRwriteBytes(struct WADINFO *info, char *data, int32_t size)
604 {
605     int32_t wsize, sz = 0;
606     WADRcheckWritePos(info);
607     if (size <= 0)
608         Bug("WW71", "WadWb<");
609     for (wsize = 0; wsize < size;) {
610         sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : size - wsize;
611         wsize += WADRwriteBlock(info, &data[wsize], sz);
612     }
613     return wsize;
614 }
615 
WADRwriteBlock2(struct WADINFO * info,char * data,int32_t sz)616 static int32_t WADRwriteBlock2(struct WADINFO *info, char *data,
617                                int32_t sz)
618 {
619     if (fwrite(data, (size_t) sz, 1, info->fd) != 1)
620         return -1;
621     info->wposit += sz;
622     if (info->maxpos < info->wposit)
623         info->maxpos = info->wposit;
624     return sz;
625 }
626 
WADRwriteBytes2(struct WADINFO * info,char * data,int32_t size)627 int32_t WADRwriteBytes2(struct WADINFO * info, char *data, int32_t size)
628 {
629     int32_t wsize, sz = 0;
630     WADRcheckWritePos(info);
631     if (size <= 0)
632         Bug("WW73", "WadWb<");
633     for (wsize = 0; wsize < size;) {
634         sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : size - wsize;
635         sz = WADRwriteBlock2(info, &data[wsize], sz);
636         if (sz < 0)
637             return -1;
638         wsize += sz;
639     }
640     return wsize;
641 }
642 
643 /*
644 **  copy data from SOURCE WAD to WAD
645 */
WADRwriteWADbytes(struct WADINFO * info,struct WADINFO * src,int32_t start,int32_t size)646 int32_t WADRwriteWADbytes(struct WADINFO * info, struct WADINFO * src,
647                           int32_t start, int32_t size)
648 {
649     int32_t wsize, sz = 0;
650     char *data;
651     data = (char *) Malloc(MEMORYCACHE);
652     WADRseek(src, start);
653     WADRcheckWritePos(info);
654     for (wsize = 0; wsize < size;) {
655         sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : size - wsize;
656         WADRreadBytes(src, data, sz);
657         wsize += WADRwriteBlock(info, data, sz);
658     }                           /*declare in WAD directory */
659     free(data);
660     return wsize;
661 }
662 
663 /*
664 ** copy lump from file into WAD
665 ** returns size
666 */
WADRwriteLump(struct WADINFO * info,const char * file)667 int32_t WADRwriteLump(struct WADINFO * info, const char *file)
668 {
669     int32_t size, sz = 0;
670     FILE *fd;
671     char *data;
672     WADRcheckWritePos(info);
673     /*Look for entry in master directory */
674     fd = fopen(file, FOPEN_RB);
675     if (fd == NULL)
676         ProgError("WW75", "%s: %s", fname(file), strerror(errno));
677     data = (char *) Malloc(MEMORYCACHE);
678     for (size = 0;;) {
679         sz = fread(data, 1, (size_t) MEMORYCACHE, fd);
680         if (sz <= 0)
681             break;
682         size += WADRwriteBlock(info, data, sz);
683     }
684     free(data);
685     fclose(fd);
686     return size;
687 }
688 
WADRwriteWADentry(struct WADINFO * info,struct WADINFO * src,int16_t n)689 int32_t WADRwriteWADentry(struct WADINFO * info, struct WADINFO * src,
690                           int16_t n)
691 {
692     if (n > (src->ntry))
693         Bug("WW77", "WadWW>");
694     return WADRwriteWADbytes(info, src, src->dir[n].start,
695                              src->dir[n].size);
696 }
697 
698 /*
699 ** copy level parts
700 */
WADRwriteWADlevelParts(struct WADINFO * info,struct WADINFO * src,int16_t N,size_t nlumps)701 void WADRwriteWADlevelParts(struct WADINFO *info, struct WADINFO *src,
702                             int16_t N, size_t nlumps)
703 {
704     int32_t start, size;
705     int16_t n;
706 
707     for (n = N + 1; n < src->ntry && n < N + nlumps; n++) {
708         WADRalign4(info);
709         start = WADRposition(info);
710         size = WADRwriteWADentry(info, src, n);
711         WADRdirAddEntry(info, start, size, src->dir[n].name);
712     }
713 }
714 
715 /*
716 ** copy level from WAD
717 ** try to match level name (multi-level)
718 ** if level name not found, then take the first level...
719 */
WADRwriteWADlevel(struct WADINFO * info,const char * file,const char * level)720 void WADRwriteWADlevel(struct WADINFO *info, const char *file,
721                        const char *level)
722 {
723     int16_t N, l;
724     int32_t pos;
725     /*char Level[8]; */
726     struct WADINFO src;
727     if (IDENTlevel(level) < 0)
728         ProgError("WW79", "Bad level name %s", level);
729     src.ok = 0;
730     WADRopenR(&src, file);
731     /*search for entry in level */
732     N = WADRfindEntry(&src, level);
733     if (N < 0) {                /* no? then search first level */
734         for (N = 0;; N++) {
735             l = IDENTlevel(src.dir[N].name);
736             if (l >= 0)
737                 break;
738             if (N >= src.ntry)
739                 ProgError("WW81", "No level in WAD %s", file);
740         }
741     }
742     /*set level name */
743     WADRalign4(info);
744     pos = WADRposition(info);   /*BC++ 4.5 bug! */
745     WADRdirAddEntry(info, pos, 0L, level);
746     /* 9999 is a way of saying "copy all the lumps". The rationale
747        is "let's assume the user knows what he/she is doing. If
748        he/she wants us to include a 100-lump level, let's do it".
749 
750        On the other hand, this stance is in contradiction with using
751        WADRfindEntry() (see above). This needs to be fixed.
752 
753        There are two choices :
754        - make this function discriminating and prevent
755        experimentation
756        - make it dumb but allow one to put multi-level wads in
757        levels/.
758 
759        My real motivation for doing things the way I did was that I
760        didn't want to copy-paste from IDENTdirLevels() into
761        WADRwriteWADlevelParts() (these things should be at a single
762        place). */
763     WADRwriteWADlevelParts(info, &src, N, 9999);
764     WADRclose(&src);
765 }
766 
767 /*
768 ** replace dir of rwad by dir of newwad
769 ** prepare to write at the end of rwad
770 ** open for append
771 */
WADRprepareAppend(const char * wadres,struct WADINFO * rwad,struct WADDIR * NewDir,int32_t NewNtry,int32_t * dirpos,int32_t * ntry,int32_t * size)772 int32_t WADRprepareAppend(const char *wadres, struct WADINFO *rwad,
773                           struct WADDIR *NewDir, int32_t NewNtry,
774                           int32_t * dirpos, int32_t * ntry, int32_t * size)
775 {
776     int32_t ewadstart;
777     int32_t rwadsize;
778     int32_t time;
779     time = Get_File_Time(wadres);
780     /* append to the Result WAD */
781     WADRopenA(rwad, wadres);
782     /*get original size */
783     rwadsize = rwad->maxpos;
784     /*last warning */
785     Output
786         ("The WAD file %s will be modified, but it can be restored with:\n",
787          wadres);
788     Output("%s -res %s\n", PACKAGE, wadres);
789     Output
790         ("Restoration may fail if you modified the WAD with another tool.\n");
791     Output("In case of failure, you can salvage your WAD by:\n");
792     /* Assuming wad is little endian... */
793     Output("- setting bytes 4-7  to \t%02Xh %02Xh %02Xh %02Xh\n",
794            (unsigned short) (rwad->ntry & 0xff),
795            (unsigned short) ((rwad->ntry >> 8) & 0xff),
796            (unsigned short) ((rwad->ntry >> 16) & 0xff),
797            (unsigned short) ((rwad->ntry >> 24) & 0xff));
798     Output("- and setting bytes 8-11 to \t%02Xh %02Xh %02Xh %02Xh\n",
799            (unsigned short) (rwad->dirpos & 0xff),
800            (unsigned short) ((rwad->dirpos >> 8) & 0xff),
801            (unsigned short) ((rwad->dirpos >> 16) & 0xff),
802            (unsigned short) ((rwad->dirpos >> 24) & 0xff));
803     Output("If possible, set the file size to %ld bytes.\n", rwadsize);
804     /*align */
805     ewadstart = ((rwadsize + 0xF) & (~0xFL));
806     if ((ewadstart | rwadsize) & EXTERNAL)
807         ProgError("WW91", "Too big WADs");
808     *dirpos = rwad->dirpos;
809     *ntry = rwad->ntry;
810     *size = rwadsize;
811     /*Change size */
812     WADRchsize(rwad, ewadstart);
813     /*Write will start at file end */
814     rwad->maxpos = ewadstart;
815     rwad->wposit = ewadstart;
816     WADRseek(rwad, ewadstart);
817     /*Change to New directory */
818     free(rwad->dir);
819     rwad->dir = NewDir;
820     rwad->ntry = NewNtry;
821     rwad->maxdir = NewNtry;
822     rwad->dirpos = -1;
823     return time;
824 }
825