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 #include "tools.h"
14 #include "endianm.h"
15 #include "mkwad.h"
16 #include "texture.h"
17 #include "ident.h"
18
19 extern char file[128];
20 /*
21 ** list WAD directory and identify entries
22 */
23 static char *Views[] = { "?", "All ",
24 "Front", "FrRgt", "Right", "RrRgt",
25 "Rear ", "RrLft", "Left ", "FrLft"
26 };
27
IdentView(char view)28 static char *IdentView(char view)
29 {
30 switch (view) {
31 case '0':
32 case '1':
33 case '2':
34 case '3':
35 case '4':
36 case '5':
37 case '6':
38 case '7':
39 case '8':
40 return Views[1 + view - '0'];
41 }
42 return Views[0];
43 }
44
45 /*
46 * XTRlistDir - implementation of -wadir
47 */
XTRlistDir(const char * doomwad,const char * wadin,NTRYB select)48 void XTRlistDir(const char *doomwad, const char *wadin, NTRYB select)
49 {
50 int16_t n;
51 static struct WADINFO pwad;
52 static struct WADINFO iwad;
53 static char buffer[128];
54 ENTRY *iden;
55 ENTRY type;
56 int32_t ntry;
57 struct WADDIR *dir;
58 char *typ;
59 int16_t pnm;
60 char *Pnam;
61 int32_t Pnamsz = 0;
62
63 /*open iwad,get iwad directory */
64 iwad.ok = 0;
65 WADRopenR(&iwad, doomwad);
66 /*find PNAMES */
67 pnm = WADRfindEntry(&iwad, "PNAMES");
68 if (pnm < 0)
69 ProgError("LS10", "Can't find PNAMES in main WAD");
70 Pnam = WADRreadEntry(&iwad, pnm, &Pnamsz);
71 WADRclose(&iwad);
72 if (wadin != NULL) {
73 pwad.ok = 0;
74 WADRopenR(&pwad, wadin);
75 iden = IDENTentriesPWAD(&pwad, Pnam, Pnamsz);
76 ntry = pwad.ntry;
77 dir = pwad.dir;
78 } else {
79 iwad.ok = 0;
80 WADRopenR(&iwad, doomwad);
81 iden = IDENTentriesIWAD(&iwad, Pnam, Pnamsz, false);
82 ntry = iwad.ntry;
83 dir = iwad.dir;
84 }
85 free(Pnam);
86 Output("\nListing of WAD directory for %s\n\n",
87 wadin != NULL ? wadin : doomwad);
88 Output("Entry\t Size\tType\n\n");
89 for (n = 0; n < ntry; n++) {
90 type = iden[n];
91 typ = "unknown";
92 /*Don't list unwanted entries */
93 switch (type & EMASK) {
94 case EVOID:
95 case EDATA:
96 if ((select & BALL) != BALL)
97 continue;
98 break; /*show VOID? only if all entries */
99 case ELEVEL:
100 if (!(select & BLEVEL))
101 continue;
102 break;
103 case EMAP:
104 if (!(select & BLEVEL))
105 continue;
106 break;
107 case ETEXTUR:
108 if (!(select & BTEXTUR))
109 continue;
110 break;
111 case EPNAME:
112 if (!(select & BTEXTUR))
113 continue;
114 break;
115 case ESOUND:
116 if (!(select & BSOUND))
117 continue;
118 break;
119 case EGRAPHIC:
120 if (!(select & BGRAPHIC))
121 continue;
122 break;
123 case ELUMP:
124 if (!(select & BLUMP))
125 continue;
126 break;
127 case ESPRITE:
128 if (!(select & BSPRITE))
129 continue;
130 break;
131 case EPATCH:
132 if (!(select & BPATCH))
133 continue;
134 break;
135 case EFLAT:
136 if (!(select & BFLAT))
137 continue;
138 break;
139 case EMUSIC:
140 if (!(select & BMUSIC))
141 continue;
142 break;
143 }
144 switch (type & EMASK) {
145 case EVOID:
146 case EDATA:
147 typ = ".";
148 break;
149 case ELEVEL:
150 sprintf(buffer, "Episod %c Map %c", '0' + ((type & 0xF0) >> 4),
151 '0' + (type & 0xF));
152 typ = buffer;
153 break;
154 case EMAP:
155 sprintf(buffer, "Level Map %d", (type & 0xFF));
156 typ = buffer;
157 break;
158 case ETEXTUR:
159 typ = "List of textures";
160 break;
161 case EPNAME:
162 typ = "List of patches";
163 break;
164 case ESOUND:
165 typ = "Sound";
166 break;
167 case EMUSIC:
168 typ = "Music";
169 break;
170 case EGRAPHIC:
171 if ((type & 0xFF) == 0xFF) {
172 typ = "Graphic";
173 } else {
174 sprintf(buffer, "Graphic");
175 typ = buffer;
176 }
177 break;
178 case ELUMP:
179 typ = "Lump of raw data";
180 break;
181 case ESPRITE:
182 typ = "Sprite";
183 if (strncmp(dir[n].name, "ARTI", 4) == 0) {
184 sprintf(buffer, "Artifact\t%4.4s", &(dir[n].name[4]));
185 } else if (dir[n].name[6] != '\0') {
186 sprintf(buffer, "Sprite %4.4s\tph:%c %s\tph:%c %s inv.",
187 dir[n].name, dir[n].name[4],
188 IdentView(dir[n].name[5]), dir[n].name[6],
189 IdentView(dir[n].name[7]));
190 } else {
191 sprintf(buffer, "Sprite %4.4s\tph:%c %s", dir[n].name,
192 dir[n].name[4], IdentView(dir[n].name[5]));
193 }
194 typ = buffer;
195 break;
196 case EPATCH:
197 typ = "Patch";
198 break;
199 case EFLAT:
200 typ = "Flat";
201 break;
202 case EZZZZ:
203 typ = "?";
204 break;
205 }
206 switch (type) {
207 case EPATCH1:
208 typ = "Patch 1";
209 break;
210 case EPATCH2:
211 typ = "Patch 2";
212 break;
213 case EPATCH3:
214 typ = "Patch 3";
215 break;
216 case EFLAT1:
217 typ = "Flat 1";
218 break;
219 case EFLAT2:
220 typ = "Flat 2";
221 break;
222 case EFLAT3:
223 typ = "Flat 3";
224 break;
225 case ESNDPC:
226 typ = "PC sound";
227 break;
228 case ESNDWAV:
229 typ = "WAV sound";
230 break;
231 }
232 Output("%-8s %8ld\t%s\n", lump_name(dir[n].name), dir[n].size,
233 typ);
234 }
235 if (wadin != NULL)
236 WADRclose(&pwad);
237 else
238 WADRclose(&iwad);
239 free(iden);
240 }
241
XTRdirCmp(const void * d1,const void * d2)242 int XTRdirCmp(const void *d1, const void *d2)
243 {
244 int32_t res;
245 struct WADDIR *dir1 = (struct WADDIR *) d1;
246 struct WADDIR *dir2 = (struct WADDIR *) d2;
247 res = (dir1->start) - (dir2->start);
248 if (res < 0)
249 return -1;
250 if (res > 0)
251 return 1;
252 res = (dir1->size) - (dir2->size);
253 if (res < 0)
254 return -1;
255 if (res > 0)
256 return 1;
257 return 0;
258 }
259
XTRvoidSpacesInWAD(const char * wadin)260 void XTRvoidSpacesInWAD(const char *wadin)
261 {
262 int16_t n;
263 static struct WADINFO pwad;
264 int32_t ntry;
265 struct WADDIR *dir;
266 int32_t startpos, lastpos, ll, diff, wtotal;
267 int32_t w3, w20, w100, w1000, w10000, w100000;
268 wtotal = w3 = w20 = w100 = w1000 = w10000 = w100000 = 0;
269
270 pwad.ok = 0;
271 WADRopenR(&pwad, wadin);
272 ntry = pwad.ntry;
273 dir = pwad.dir;
274 qsort(dir, (size_t) ntry, sizeof(struct WADDIR), XTRdirCmp);
275 Output("\nListing of unused spaces in WAD %s\n", wadin);
276 lastpos = 4 + 4 + 4;
277 for (n = 0; n < ntry; n++) {
278 diff = dir[n].start - lastpos;
279 startpos = lastpos;
280 ll = dir[n].start + dir[n].size;
281 if (lastpos < ll)
282 lastpos = ll;
283 if (diff < 0)
284 continue;
285 wtotal += diff;
286 if (diff <= 3)
287 w3 += diff;
288 else if (diff <= 20)
289 w20 += diff;
290 else if (diff <= 100)
291 w100 += diff;
292 else if (diff <= 1000)
293 w1000 += diff;
294 else if (diff <= 10000)
295 w10000 += diff;
296 else
297 w100000 += diff;
298 if (diff >= 4) /*suppress word alignement */
299 Output(" At offset 0x%8.8lx, %ld bytes wasted\n", startpos,
300 diff);
301 }
302 Output("\nRepartition of wasted bytes:\n");
303 Output(" All blocks<=3 \t%ld\n", w3);
304 Output(" All blocks<=20 \t%ld\n", w20);
305 Output(" All blocks<=100 \t%ld\n", w100);
306 Output(" All blocks<=1000 \t%ld\n", w1000);
307 Output(" All blocks<=10000\t%ld\n", w10000);
308 Output(" All blocks> 10000\t%ld\n", w100000);
309 Output(" \t-------\n");
310 Output(" Total wasted bytes\t%ld\n", wtotal);
311 WADRclose(&pwad);
312 }
313
314 struct SIDEDEF {
315 int16_t ofsx; /* X offset for texture */
316 int16_t ofsy; /* Y offset for texture */
317 char Above[8]; /* texture name for the part above */
318 char Below[8]; /* texture name for the part below */
319 char Center[8]; /* texture name for the regular part */
320 int16_t Sector; /* adjacent sector */
321 };
322
323 /*
324 ** Check a level
325 ** Assumes TEXTURES are already read somewhere.
326 */
CheckTexture(char * tex,bool IsDef)327 void CheckTexture(char *tex, bool IsDef)
328 {
329 int n;
330 char Name[8];
331 for (n = 0; n < 8; n++) {
332 Name[n] = tex[n];
333 if (tex[n] == '\0')
334 break;
335 }
336 Normalise(Name, Name);
337 switch (Name[0]) {
338 case '-':
339 case '\0':
340 break;
341 default:
342 if (IsDef) { /*if we only wish to declare the tex */
343 TXUfakeTex(Name);
344 } else {
345 if (!TXUexist(Name))
346 Output("Warning: undefined sidedef %s\n", lump_name(Name));
347 }
348 }
349 }
350
351 /*
352 **check textures
353 ** IsDef=true if we just wish to declare textures
354 */
CheckSideDefs(struct WADINFO * pwad,int32_t start,int32_t size,bool IsDef)355 void CheckSideDefs(struct WADINFO *pwad, int32_t start, int32_t size,
356 bool IsDef)
357 {
358 struct SIDEDEF *sid;
359 struct SIDEDEF *side;
360 int32_t s;
361 sid = (struct SIDEDEF *) Malloc(size);
362 WADRseek(pwad, start);
363 WADRreadBytes(pwad, (char *) sid, size);
364 for (s = 0; (s * sizeof(struct SIDEDEF)) < size; s += 1) {
365 side = sid + s;
366 CheckTexture(side->Above, IsDef);
367 CheckTexture(side->Below, IsDef);
368 CheckTexture(side->Center, IsDef);
369 }
370 free(sid);
371 }
372
CheckLevels(struct WADINFO * pwad,bool IsDef)373 void CheckLevels(struct WADINFO *pwad, bool IsDef)
374 {
375 int16_t lev, lin, id, top;
376 int32_t ntry = pwad->ntry;
377 struct WADDIR *pdir = pwad->dir;
378 for (lev = 0; lev < ntry; lev++) {
379 id = IDENTlevel(pdir[lev].name);
380 if (id >= 0) {
381 for (lin = lev; lin < ntry; lin++) {
382 top = lev + 11;
383 if (lin > top)
384 break;
385 if (strncmp(pdir[lin].name, "SIDEDEFS", 8) == 0) {
386 Output("\nChecking LEVEL %s\n\n",
387 lump_name(pdir[lev].name));
388 CheckSideDefs(pwad, pdir[lin].start, pdir[lin].size,
389 IsDef);
390 }
391 }
392 }
393 }
394 }
395
396 /*
397 ** Test a PWAD (-check)
398 **
399 ** this is absolutely sub optimal.
400 */
XTRstructureTest(const char * doomwad,const char * wadin)401 void XTRstructureTest(const char *doomwad, const char *wadin)
402 {
403 static struct WADINFO pwad, iwad;
404 char *Pnames;
405 int16_t p, pnm, nbPatchs;
406 int32_t size;
407 static struct PICH {
408 int16_t Xsz;
409 int16_t Ysz;
410 } pich;
411 int16_t *PszX;
412 static char name[8];
413 char *buffer;
414 int16_t cs, ce;
415
416 /*read PNAME in wad, if defined */
417 Phase("LS21", "Reading WADs");
418 iwad.ok = 0;
419 WADRopenR(&iwad, doomwad);
420 pwad.ok = 0;
421 WADRopenR(&pwad, wadin);
422 Phase("LS23", "Reading Patches");
423 pnm = WADRfindEntry(&pwad, "PNAMES");
424 if (pnm >= 0) {
425 size = pwad.dir[pnm].size;
426 Pnames = (char *) Malloc(size);
427 WADRseek(&pwad, pwad.dir[pnm].start);
428 WADRreadBytes(&pwad, Pnames, size);
429 } else {
430 pnm = WADRfindEntry(&iwad, "PNAMES");
431 if (pnm < 0)
432 ProgError("LS25", "PNAMES not found");
433 size = iwad.dir[pnm].size;
434 Pnames = (char *) Malloc(size);
435 WADRseek(&iwad, iwad.dir[pnm].start);
436 WADRreadBytes(&iwad, Pnames, size);
437 }
438 PNMinit(Pnames, size);
439 free(Pnames);
440 /*
441 ** find each PNAME and identify Xsz Ysz
442 */
443 Phase("LS27", "Checking Patches");
444 nbPatchs = PNMgetNbOfPatch();
445 PszX = (int16_t *) Malloc(nbPatchs * sizeof(int16_t));
446 for (p = 0; p < nbPatchs; p++) { /*for all patches */
447 PNMgetPatchName(name, p);
448 pnm = WADRfindEntry(&pwad, name);
449 if (pnm >= 0) {
450 WADRseek(&pwad, pwad.dir[pnm].start);
451 WADRreadBytes(&pwad, (char *) &pich, sizeof(struct PICH));
452 } else {
453 pnm = WADRfindEntry(&iwad, name);
454 if (pnm < 0)
455 Output("Warning: Patch %s not found\n", lump_name(name));
456 else {
457 WADRseek(&iwad, iwad.dir[pnm].start);
458 WADRreadBytes(&iwad, (char *) &pich, sizeof(struct PICH));
459 }
460 }
461 PszX[p] = peek_i16_le(&pich.Xsz);
462 }
463 /*
464 ** read TEXTURES
465 */
466 Phase("LS29", "Reading Textures");
467 pnm = WADRfindEntry(&pwad, "TEXTURE1");
468 if (pnm >= 0) { /* read texture */
469 const struct WADDIR *e = pwad.dir + pnm;
470 buffer = (char *) Malloc(e->size);
471 WADRseek(&pwad, e->start);
472 WADRreadBytes(&pwad, buffer, e->size);
473 TXUinit();
474 TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true);
475 free(buffer);
476 /*for each textures, check what is covered */
477 Phase("LS31", "Checking TEXTURE1");
478 TXUcheckTex(nbPatchs, PszX);
479 TXUfree();
480 }
481 pnm = WADRfindEntry(&pwad, "TEXTURE2");
482 if (pnm >= 0) { /* read texture */
483 const struct WADDIR *e = pwad.dir + pnm;
484 buffer = (char *) Malloc(e->size);
485 WADRseek(&pwad, e->start);
486 WADRreadBytes(&pwad, buffer, e->size);
487 TXUinit();
488 TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true);
489 free(buffer);
490 /*for each textures, check what is covered */
491 Phase("LS33", "Checking TEXTURE2");
492 TXUcheckTex(nbPatchs, PszX);
493 TXUfree();
494 }
495 free(PszX);
496 /*
497 ** check if all textures composing walls are here
498 **
499 */
500 Phase("LS35", "Checking Level SIDEDEFS for missing textures");
501 TXUinit();
502 pnm = WADRfindEntry(&pwad, "TEXTURE1");
503 if (pnm >= 0) {
504 const struct WADDIR *e = pwad.dir + pnm;
505 buffer = (char *) Malloc(e->size);
506 WADRseek(&pwad, e->start);
507 WADRreadBytes(&pwad, buffer, e->size);
508 TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true);
509 free(buffer);
510 } else {
511 pnm = WADRfindEntry(&iwad, "TEXTURE1");
512 if (pnm >= 0) {
513 const struct WADDIR *e = iwad.dir + pnm;
514 buffer = (char *) Malloc(e->size);
515 WADRseek(&iwad, e->start);
516 WADRreadBytes(&iwad, buffer, e->size);
517 TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true);
518 free(buffer);
519 }
520 }
521 pnm = WADRfindEntry(&pwad, "TEXTURE2");
522 if (pnm >= 0) {
523 const struct WADDIR *e = pwad.dir + pnm;
524 buffer = (char *) Malloc(e->size);
525 WADRseek(&pwad, e->start);
526 WADRreadBytes(&pwad, buffer, e->size);
527 TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true);
528 free(buffer);
529 } else {
530 pnm = WADRfindEntry(&iwad, "TEXTURE2");
531 if (pnm >= 0) {
532 const struct WADDIR *e = iwad.dir + pnm;
533 buffer = (char *) Malloc(e->size);
534 WADRseek(&iwad, e->start);
535 WADRreadBytes(&iwad, buffer, e->size);
536 TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true);
537 free(buffer);
538 }
539 }
540 CheckLevels(&pwad, false);
541 TXUfree();
542 PNMfree();
543 WADRclose(&iwad);
544 /*
545 ** check sprite markers
546 **
547 */
548 Phase("LS37", "Checking Sprites");
549 for (cs = ce = 0, p = 0; p < pwad.ntry; p++) {
550 if (strncmp(pwad.dir[p].name, "S_START", 8) == 0)
551 cs++;
552 if (strncmp(pwad.dir[p].name, "SS_START", 8) == 0)
553 cs++;
554 if (strncmp(pwad.dir[p].name, "SS_END", 8) == 0)
555 ce++;
556 if (strncmp(pwad.dir[p].name, "S_END", 8) == 0)
557 ce++;
558 }
559 if (cs > 1)
560 ProgError("LS39", "Duplicate S_START");
561 if (ce > 1)
562 ProgError("LS41", "Duplicate S_END");
563 if ((cs > 0) & (ce < 1))
564 Output("Warning: S_START but no S_END\n");
565 if ((cs < 1) & (ce < 1))
566 Info("LS43", "No need to check sprites");
567 else {
568 for (cs = ce = 0, p = 0; p < pwad.ntry; p++) {
569 if (strncmp(pwad.dir[p].name, "S_START", 8) == 0)
570 cs = p + 1;
571 if (strncmp(pwad.dir[p].name, "SS_START", 8) == 0)
572 cs = p + 1;
573 if (strncmp(pwad.dir[p].name, "SS_END", 8) == 0)
574 ce = p;
575 if (strncmp(pwad.dir[p].name, "S_END", 8) == 0)
576 ce = p;
577 }
578 }
579 /*
580 ** Check flat markers
581 */
582 Phase("LS45", "Checking Flats");
583 for (cs = ce = 0, p = 0; p < pwad.ntry; p++) {
584 if (strncmp(pwad.dir[p].name, "F_START", 8) == 0)
585 cs++;
586 if (strncmp(pwad.dir[p].name, "FF_START", 8) == 0)
587 cs++;
588 if (strncmp(pwad.dir[p].name, "FF_END", 8) == 0)
589 ce++;
590 if (strncmp(pwad.dir[p].name, "F_END", 8) == 0)
591 ce++;
592 }
593 if (cs > 1)
594 Output("Warning: Duplicate F_START\n");
595 if (ce > 1)
596 Output("Warning: Duplicate F_END\n");
597 if ((cs > 0) & (ce < 1))
598 Output("Warning: F_START but no F_END\n");
599 if ((cs < 1) & (ce < 1))
600 Info("LS47", "No need to check flats");
601 WADRclose(&pwad);
602 }
603
604 /*
605 ** Test a PWAD (-usedtex)
606 **
607 */
XTRtextureUsed(const char * wadin)608 void XTRtextureUsed(const char *wadin)
609 {
610 static struct WADINFO pwad;
611 /*read PNAME in wad, if defined */
612 Phase("LS50", "Listing texture used in the levels of %s",
613 fname(wadin));
614 pwad.ok = 0;
615 WADRopenR(&pwad, wadin);
616 /*
617 ** list all textures composing walls
618 */
619 TXUinit();
620 CheckLevels(&pwad, true);
621 Output("List of textures used in %s\n\n", wadin);
622 TXUlistTex();
623 TXUfree();
624 WADRclose(&pwad);
625 }
626
627 /*
628 ** Detect duplicate entries (-packgfx, -packnorm)
629 ** ShowIdx = true if we also output the indexes
630 **
631 ** Optimise for speed with a CRC-based check
632 */
XTRcompakWAD(const char * DataDir,const char * wadin,const char * texout,bool ShowIdx)633 void XTRcompakWAD(const char *DataDir, const char *wadin,
634 const char *texout, bool ShowIdx)
635 {
636 static struct WADINFO pwad;
637 struct WADDIR *pdir;
638 int16_t pnb;
639 int16_t p, bas, tst, ofsx, ofsy;
640 int32_t size, rsize, sz;
641 bool *psame;
642 FILE *out;
643 char *bbas;
644 char *btst;
645 Phase("LS60", "Detecting duplicate entries in WAD %s", fname(wadin));
646 pwad.ok = 0;
647 WADRopenR(&pwad, wadin);
648 pnb = (int16_t) pwad.ntry;
649 pdir = pwad.dir;
650 psame = (bool *) Malloc(pnb);
651 for (p = 0; p < pnb; p++) {
652 psame[p] = false;
653 }
654 if (texout == NULL)
655 MakeFileName(file, DataDir, "", "", "WADINFOP", "TXT");
656 else
657 sprintf(file, "%.120s", texout);
658 out = fopen(file, FOPEN_WT);
659 fprintf(out, "; Indication of similar entries\n\n");
660 bbas = (char *) Malloc(MEMORYCACHE);
661 btst = (char *) Malloc(MEMORYCACHE);
662 for (bas = 0; bas < pnb; bas++)
663 if (!psame[bas]) { /*skip already treated */
664 size = pdir[bas].size;
665 if (pdir[bas].start <= 8)
666 continue;
667 if (size < 1)
668 continue;
669 if ((size >= 8) && ShowIdx) {
670 WADRseek(&pwad, pdir[bas].start);
671 WADRreadBytes(&pwad, bbas, 8);
672 ofsx = ((bbas[5] << 8) & 0xFF00) + (bbas[4] & 0xFF);
673 ofsy = ((bbas[7] << 8) & 0xFF00) + (bbas[6] & 0xFF);
674 fprintf(out, "%-8.8s\t%d\t%d\n", pdir[bas].name, ofsx,
675 ofsy);
676 } else
677 fprintf(out, "%-8.8s\n", pdir[bas].name);
678 for (tst = bas + 1; tst < pnb; tst++) { /*if same size */
679 if (pdir[tst].start < 0)
680 continue;
681 if (pdir[tst].size != size)
682 continue;
683 /*check that equal */
684 for (sz = rsize = 0; rsize < size; rsize += sz) {
685 sz = (size - rsize >
686 MEMORYCACHE) ? MEMORYCACHE : size - rsize;
687 WADRseek(&pwad, pdir[bas].start + rsize);
688 WADRreadBytes(&pwad, bbas, sz);
689 WADRseek(&pwad, pdir[tst].start + rsize);
690 WADRreadBytes(&pwad, btst, sz);
691 for (p = 0; p < sz; p++) {
692 if (bbas[p] != btst[p])
693 break;
694 }
695 if (p < sz)
696 break;
697 }
698 if (rsize == size) { /*entry identical to reference */
699 psame[tst] = true;
700 fprintf(out, "%-8.8s\t*\n", pdir[tst].name);
701 }
702 }
703 }
704 fclose(out);
705 WADRclose(&pwad);
706 free(bbas);
707 free(btst);
708 free(psame);
709 }
710