stuff/umodextract.c
2022-01-01 20:50:20 +01:00

170 lines
3.9 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#define UMOD_MAGIC 0x9FE3C5A3
typedef struct
{
uint32_t sig, dir, size, ver, crc;
} umodfoot_t;
typedef struct
{
char *fname;
uint32_t ofs, len, flags;
} umoddir_t;
// CRC has to be done in this very specific way or shit will go wrong
// I totally did not take this from leaked Unreal Engine source code
uint32_t crctable[256];
void crcinit( void )
{
for( uint32_t i=0; i<256; i++ ) for ( uint32_t c=(i<<24), j=8; j; j-- )
crctable[i] = c = (c&0x80000000)?((c<<1)^0x04C11DB7):(c<<1);
}
uint32_t crc( const void *s, size_t l, uint32_t crc )
{
uint8_t *d = (uint8_t*)s;
crc = ~crc;
for ( size_t i=0; i<l; i++ ) crc = (crc<<8)^crctable[(crc>>24)^d[i]];
return ~crc;
}
// fuck you tim sweeney (or whoever else is responsible for this crime)
int32_t readindex( FILE *f )
{
uint8_t byte[5] = {0};
fread(&byte[0],1,1,f);
if ( !byte[0] ) return 0;
if ( byte[0]&0x40 )
{
for ( int i=1; i<5; i++ )
{
fread(&byte[i],1,1,f);
if ( !(byte[i]&0x80) ) break;
}
}
int32_t tf = byte[0]&0x3f;
tf |= (int32_t)(byte[1]&0x7f)<<6;
tf |= (int32_t)(byte[2]&0x7f)<<13;
tf |= (int32_t)(byte[3]&0x7f)<<20;
tf |= (int32_t)(byte[4]&0x7f)<<27;
if ( byte[0]&0x80 ) tf *= -1;
return tf;
}
int main( int argc, char **argv )
{
if ( argc < 2 )
{
fprintf(stderr,"usage: umodextract <umod file>\n");
return 1;
}
FILE *f = fopen(argv[1],"rb");
fseek(f,-20,SEEK_END);
umodfoot_t feet;
fread(&feet,1,20,f);
if ( feet.sig != UMOD_MAGIC )
{
fclose(f);
return 2;
}
//printf("sig: %08X\ndir: &%u\nsize: %u bytes\nver: %u\ncrc: %08X\n",
// feet.sig,feet.dir,feet.size,feet.ver,feet.crc);
// CRC verification
void *dat = malloc(feet.size-20);
fseek(f,0,SEEK_SET);
fread(dat,1,feet.size-20,f);
crcinit();
uint32_t thiscrc = crc(dat,feet.size-20,0);
free(dat);
if ( thiscrc != feet.crc )
{
fprintf(stderr,"CRC mismatch! %08X != %08X\n",thiscrc,feet.crc);
fclose(f);
return 4;
}
fseek(f,feet.dir,SEEK_SET);
int32_t ndir = readindex(f);
//printf("ndir: %d\n",ndir);
umoddir_t *dir = calloc(ndir,sizeof(umoddir_t));
for ( int32_t i=0; i<ndir; i++ )
{
int32_t flen = readindex(f);
dir[i].fname = malloc(flen);
fread(dir[i].fname,1,flen,f);
fread(&(dir[i].ofs),1,4,f);
fread(&(dir[i].len),1,4,f);
fread(&(dir[i].flags),1,4,f);
//printf("fname: %.*s (%d)\nofs: &%u\nlen: %u\nflags: %08X\n",
// flen,dir[i].fname,flen,dir[i].ofs,dir[i].len,dir[i].flags);
long saved = ftell(f);
fseek(f,dir[i].ofs,SEEK_SET);
int isini = 0;
if ( (isini=!!strstr(dir[i].fname,"Manifest.ini")) || !!strstr(dir[i].fname,"Manifest.int") )
{
printf("==== BEGIN Manifest.in%c ====\n\n",isini?'i':'t');
uint8_t *str = malloc(dir[i].len);
fread(str,1,dir[i].len,f);
fwrite(str,1,dir[i].len,stdout);
free(str);
printf("\n==== END Manifest.in%c ====\n",isini?'i':'t');
}
else
{
char *rpath = dir[i].fname;
if ( !strchr(rpath,'\\') )
{
FILE *fout = fopen(rpath,"wb");
void *dat = malloc(dir[i].len);
fread(dat,1,dir[i].len,f);
fwrite(dat,1,dir[i].len,fout);
free(dat);
fclose(fout);
}
else
{
FILE *fout;
// subdivide folders
char *nxt = 0;
//int i = 0;
do
{
nxt = strchr(rpath,'\\');
if ( nxt )
{
*nxt = '\0';
//printf("path%d: %s\n",i,rpath);
mkdir(rpath,0755);
*nxt = '/';
rpath = nxt+1;
}
else
{
// the file itself
//printf("fout: %s\n",dir[i].fname);
printf("==== EXTRACTING %s (%u bytes) ====\n",dir[i].fname,dir[i].len);
fout = fopen(dir[i].fname,"wb");
void *dat = malloc(dir[i].len);
fread(dat,1,dir[i].len,f);
fwrite(dat,1,dir[i].len,fout);
free(dat);
fclose(fout);
}
//i++;
} while ( nxt );
}
}
fseek(f,saved,SEEK_SET);
}
for ( int32_t i=0; i<ndir; i++ ) free(dir[i].fname);
free(dir);
fclose(f);
return 0;
}