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