175 lines
4 KiB
C
175 lines
4 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.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));
|
|
char cwd[4096];
|
|
getcwd(&cwd[0],4096);
|
|
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 j = 0;
|
|
do
|
|
{
|
|
nxt = strchr(rpath,'\\');
|
|
if ( nxt )
|
|
{
|
|
*nxt = '\0';
|
|
//printf("path%d: %s\n",j,rpath);
|
|
mkdir(rpath,0755);
|
|
chdir(rpath);
|
|
*nxt = '/';
|
|
rpath = nxt+1;
|
|
}
|
|
else
|
|
{
|
|
// the file itself
|
|
chdir(cwd);
|
|
//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);
|
|
}
|
|
j++;
|
|
} 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;
|
|
}
|