stuff/dtexdupes.c

313 lines
6.5 KiB
C

/*
dtexdupes.c : Find textures with identical names in multiple doom mods.
Supports wad, pk3 and folders. Handles TEXTURE1/2 and TEXTURES.
(C)2018 Marisa Kirisame, UnSX Team.
Released under the GNU General Public License version 3 (or later).
Requires libarchive. Made for *nix platforms.
*/
#define _XOPEN_SOURCE 700
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <archive.h>
#include <archive_entry.h>
#include <sys/stat.h>
#include <ftw.h>
// wad stuff
typedef struct
{
char id[4];
uint32_t nlumps;
uint32_t dirpos;
} wad_t;
typedef struct
{
uint32_t pos;
uint32_t size;
char name[8];
} lump_t;
// list of texture names
uint8_t **tex = 0;
size_t ntex = 0, nresv = 0;
void freetex( void )
{
if ( !tex ) return;
for ( int i=0; i<ntex; i++ )
if ( tex[i] ) free(tex[i]);
free(tex);
}
int findtex( const char *name, uint8_t len )
{
for ( int i=0; i<ntex; i++ )
if ( !strncasecmp(tex[i],name,len) )
return 1;
return 0;
}
// we don't need a very large chunk size, this isn't a time-critical program
#define CHUNK_SZ 64
int appendtex( const char *name, uint8_t len )
{
if ( findtex(name,len) )
{
printf(" Texture %.*s already defined!\n",len,name);
return 0;
}
if ( !tex )
{
tex = malloc(sizeof(uint8_t*)*CHUNK_SZ);
nresv = CHUNK_SZ;
}
else if ( ntex >= nresv )
{
nresv += CHUNK_SZ;
tex = realloc(tex,sizeof(uint8_t*)*nresv);
}
// append
tex[ntex] = malloc(len+1);
strncpy(tex[ntex],name,len);
tex[ntex][len] = '\0'; // guarantee null-termination
ntex++;
}
int processtexture12( uint8_t type, uint8_t *data )
{
// parse TEXTURE1/2 lumps
int32_t nent = *(int32_t*)data;
int32_t *ofs = (int32_t*)(data+4);
for ( int i=0; i<nent; i++ )
{
uint8_t *name = (uint8_t*)(data+ofs[i]);
appendtex(name,8);
}
printf(" TEXTURE%c read (%d entries)\n",type,nent);
return nent;
}
int processtextures( uint8_t *data )
{
// parse TEXTURES entries
printf(" TEXTURES not yet supported, skipping\n");
return 0; // TODO
}
int processflats( lump_t *head )
{
// check everything between F_START and F_END
int cnt = 0;
lump_t *cur = head;
while ( cur )
{
cur++;
if ( !strncmp(cur->name,"F_END",8) ) break;
if ( cur->size != 4096 ) continue;
appendtex(cur->name,8);
cnt++;
}
printf(" F_START/END read (%d entries)\n",cnt);
return cnt;
}
int processtxsection( lump_t *head )
{
// check everything between TX_START and TX_END
int cnt = 0;
lump_t *cur = head;
while ( cur )
{
cur++;
if ( !strncmp(cur->name,"TX_END",8) ) break;
appendtex(cur->name,8);
cnt++;
}
printf(" TX_START/END read (%d entries)\n",cnt);
return cnt;
}
int processwad( uint8_t *w )
{
lump_t *dir = (lump_t*)(w+((wad_t*)w)->dirpos);
for ( int i=0; i<((wad_t*)w)->nlumps; i++ )
{
if ( !strncmp(dir->name,"TEXTURES",8) )
processtextures(w+dir->pos);
else if ( !strncmp(dir->name,"TEXTURE1",8)
|| !strncmp(dir->name,"TEXTURE2",8) )
processtexture12(dir->name[7],w+dir->pos);
else if ( !strncmp(dir->name,"TX_START",8) )
processtxsection(dir);
else if ( !strncmp(dir->name,"F_START",8) )
processflats(dir);
dir++;
}
}
int processtxfolder_arc( struct archive *ar )
{
// libarchive apparently has no concept of "folders" so we have to
// loop through everything here and check its full path
return 0; // TODO
}
static int ncnt = 0, fcnt = 0;
static int ftw_callback( const char *path, const struct stat *st,
const int type, struct FTW *info )
{
const char *bname = path+info->base;
if ( info->level == 1 )
{
if ( (type == FTW_D) && strcasecmp(bname,"textures") && strcasecmp(bname,"flats") )
return FTW_SKIP_SUBTREE;
if ( type != FTW_F ) return FTW_CONTINUE;
FILE *f;
if ( !(f = fopen(path,"rb")) )
{
fprintf(stderr,"cannot open %s: %s\n",bname,
strerror(errno));
return FTW_CONTINUE;
}
char magic[4];
if ( fread(magic,1,4,f) == 4 )
{
if ( !strncmp(magic,"IWAD",4)
|| !strncmp(magic,"PWAD",4) )
{
uint8_t *w = malloc(st->st_size);
if ( !w )
{
fprintf(stderr,"cannot allocate memory"
" for %s: %s\n",bname,
strerror(errno));
fclose(f);
return FTW_CONTINUE;
}
fseek(f,0,SEEK_SET);
fread(w,1,st->st_size,f);
fclose(f);
printf("Processing %.4s %s\n",magic,bname);
ncnt += processwad(w);
free(w);
return FTW_CONTINUE;
}
}
fseek(f,0,SEEK_SET);
if ( !strncasecmp(bname,"TEXTURES",8) )
{
uint8_t *d;
if ( !(d = malloc(st->st_size)) )
{
fprintf(stderr,"cannot allocate memory for %s:"
" %s\n",bname,strerror(errno));
fclose(f);
return FTW_CONTINUE;
}
fread(d,1,st->st_size,f);
ncnt += processtextures(d);
free(d);
}
else if ( !strncasecmp(bname,"TEXTURE1",8)
|| !strncasecmp(bname,"TEXTURE2",8) )
{
uint8_t *d;
if ( !(d = malloc(st->st_size)) )
{
fprintf(stderr,"cannot allocate memory for %s:"
" %s\n",bname,strerror(errno));
fclose(f);
return FTW_CONTINUE;
}
fread(d,1,st->st_size,f);
ncnt += processtexture12(bname[7],d);
free(d);
}
fclose(f);
return FTW_CONTINUE;
}
if ( type == FTW_F )
{
// strip extension
char *ext = strchr(bname,'.');
if ( ext ) *ext = '\0';
appendtex(bname,8);
ncnt++;
fcnt++;
}
return FTW_CONTINUE;
}
int processtxfolder( const char *path )
{
ncnt = 0, fcnt = 0;
// recurse through specified directory and check any and all files
nftw(path,ftw_callback,15,FTW_ACTIONRETVAL|FTW_PHYS);
printf(" Directory read (%d entries)\n",fcnt);
return ncnt;
}
int main( int argc, char **argv )
{
struct stat st;
FILE *f;
int tot = 0;
// sift through all parameters
for ( int i=1; i<argc; i++ )
{
if ( stat(argv[i],&st) )
{
fprintf(stderr,"cannot stat %s: %s\n",argv[i],
strerror(errno));
continue;
}
if ( S_ISDIR(st.st_mode) )
{
printf("Processing directory %s\n",argv[i]);
tot += processtxfolder(argv[i]);
continue;
}
else if ( !(f = fopen(argv[i],"rb")) )
{
fprintf(stderr,"cannot open file %s: %s\n",argv[i],
strerror(errno));
continue;
}
/* check for wad */
char magic[4];
if ( fread(magic,1,4,f) == 4 )
{
if ( !strncmp(magic,"IWAD",4)
|| !strncmp(magic,"PWAD",4) )
{
uint8_t *w = malloc(st.st_size);
if ( !w )
{
fprintf(stderr,"cannot allocate memory"
" for %s: %s\n",argv[i],
strerror(errno));
fclose(f);
continue;
}
fseek(f,0,SEEK_SET);
fread(w,1,st.st_size,f);
fclose(f);
printf("Processing %.4s %s\n",magic,argv[i]);
tot = processwad(w);
free(w);
continue;
}
}
fclose(f);
}
freetex();
return 0;
}