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