#include "installOS.h" | |
#include "log.h" | |
#include "extract.h" | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
/* | |
* Self-Extracting File Structure | |
* | |
* |---------------------------| | |
* | Executable Extractor | | |
* | | | |
* |---------------------------| | |
* | Files Data | | |
* | | | |
* |---------------------------| | |
* | SFX Header | | |
* | | | |
* |---------------------------| | |
* | Table Of Contents | | |
* | (TOC) | | |
* |---------------------------| | |
* | SFX Footer | | |
* | | | |
* |---------------------------| | |
* | |
* | |
* SFX Header: | |
* 12 bytes - SFX signature ( including format version ) | |
* 4 bytes - number of files | |
* | |
* TOC Entry: | |
* 4 bytes - file data offset | |
* 4 bytes - file data size | |
* 4 bytes - filename length (N) | |
* N bytes - filename | |
* | |
* SFX Footer: | |
* 4 bytes - SFX header offset | |
* 4 bytes - 32-bit complementary checksum | |
*/ | |
static const char* SIGNATURE = "!SFX.PKG-0.1"; | |
static TOC_ENTRY * toc = NULL; | |
static unsigned int file_count = 0; | |
static int doError( int error, _TCHAR* str ); | |
#ifdef MACOSX | |
#define SWAP32(x) /* do nothing */ | |
#else | |
#define SWAP32(x) (x) = ((x)>>24)|(((x)&0x00FF0000)>>8)|(((x)&0x0000FF00)<<8)|(((x)&0x000000FF)<<24) | |
#endif | |
#define BUFFER_SIZE 1024 | |
#define MIN(a,b) (a)<(b) ? (a) : (b) | |
#define FLAGS_EXTRACT (0x00000001) | |
/* | |
* Errors | |
*/ | |
#define ERROR_READ (-1) | |
#define ERROR_INVALID_CONTENT (-2) | |
#define ERROR_WRITE (-3) | |
/* | |
* Read Table Of Contents | |
*/ | |
static int readTOC( FILE *file ) | |
{ | |
long filesize = 0; | |
unsigned int value = 0; | |
unsigned int i; | |
char sign[12]; | |
char * filename; | |
TOC_ENTRY* entry; | |
/* verify checksum here */ | |
if ( fseek(file, -8, SEEK_END ) != 0 ) return ERROR_READ; | |
if ( fread(&value,4,1,file) != 1 ) return ERROR_READ; | |
SWAP32(value); | |
if ( (filesize = ftell(file)) <= (long)value ) return ERROR_INVALID_CONTENT; | |
if ( fseek(file, value, SEEK_SET ) != 0 ) return ERROR_READ; | |
if ( fread(sign,sizeof(sign),1,file) != 1 ) return ERROR_READ; | |
if ( strncmp(SIGNATURE,sign,sizeof(sign)) != 0 ) return ERROR_INVALID_CONTENT; | |
if ( fread(&file_count,4,1,file) != 1 ) return ERROR_READ; | |
SWAP32(file_count); | |
toc = entry = calloc(file_count,sizeof(TOC_ENTRY)); | |
for( i = 0; i < file_count; ++i, ++entry ) | |
{ | |
if ( fread(entry,4,3,file) != 3 ) return ERROR_READ; | |
if ( fread(&value,4,1,file) != 1 ) return ERROR_READ; | |
SWAP32(entry->OFFSET); | |
SWAP32(entry->SIZE); | |
SWAP32(entry->FLAGS); | |
SWAP32(value); | |
if ( filesize <= (long)(entry->OFFSET+entry->SIZE) ) return ERROR_INVALID_CONTENT; | |
filename = malloc(value+1); | |
if ( fread(filename,1,value,file) != value ) return ERROR_READ; | |
#ifdef _UNICODE | |
{ | |
WCHAR *wstring = malloc((value + 1) * sizeof(WCHAR)); | |
value = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, filename, value, wstring, value); | |
wstring[value] = 0; | |
entry->FILENAME = wstring; | |
free(filename); | |
} | |
#else | |
filename[value] = '\0'; | |
entry->FILENAME = filename; | |
#endif | |
} | |
return 0; | |
} | |
/* | |
* Free TOC resources | |
*/ | |
static void freeTOC() | |
{ | |
if ( toc != NULL ) | |
{ | |
unsigned int i; | |
TOC_ENTRY* entry = toc; | |
for( i = 0; i < file_count; ++i, ++entry ) | |
free(entry->FILENAME); | |
free(toc); | |
toc = NULL; | |
} | |
} | |
/* | |
* Extract all contents to directory | |
*/ | |
int extract( _TCHAR* fromfile, _TCHAR* todir ) | |
{ | |
int error = 0; | |
unsigned int i; | |
TOC_ENTRY* entry; | |
_TCHAR * string; | |
char * buffer; | |
FILE *fout; | |
FILE *file = _tfopen(fromfile, _T_INSTALL("rb")); | |
if (file == NULL) | |
return doError(ERROR_READ,fromfile); | |
if ( (error = readTOC(file)) != 0 ) { | |
fclose(file); | |
return doError(error,fromfile); | |
} | |
entry = toc; | |
string = malloc( MAX_PATH * sizeof(_TCHAR) ); | |
buffer = malloc( BUFFER_SIZE ); | |
for( i = 0; i < file_count; ++i, ++entry ) | |
{ | |
unsigned int size = entry->SIZE; | |
if ( (entry->FLAGS & FLAGS_EXTRACT ) == 0 ) continue; | |
if ( entry->CREATED) continue; | |
_tcscpy(string, todir); | |
_tcscat(string,entry->FILENAME); | |
logFormatS(_T_INSTALL("[i] Extracting file %s"),string); | |
fout = _tfopen(string, _T_INSTALL("wb")); | |
if (fout == NULL) { error = doError(ERROR_WRITE,string); break; } | |
if ( fseek(file, entry->OFFSET, SEEK_SET ) != 0 ) { error = doError(ERROR_READ,fromfile); fclose(fout); break; } | |
while( size > 0 ) | |
{ | |
unsigned int n = MIN(size,BUFFER_SIZE); | |
if ( fread(buffer,n,1,file) != 1 ) { error = doError(ERROR_READ,fromfile); fclose(fout); break; } | |
if ( fwrite(buffer,n,1,fout) != 1 ) { error = doError(ERROR_WRITE,string); fclose(fout); break; } | |
size -= n; | |
} | |
if ( error != 0 ) break; | |
fclose(fout); | |
entry->FILENAME = _tcsdup(string); | |
entry->CREATED = 1; | |
} | |
if ( error != 0 ) | |
{ | |
logFormatI(_T_INSTALL("[i] Total %i files extracted"),file_count); | |
} | |
free(buffer); | |
free(string); | |
fclose(file); | |
return error; | |
} | |
/* | |
* Extract splash file to directory | |
*/ | |
int extractFile( _TCHAR* fromfile, _TCHAR* todir, _TCHAR* fileName ) | |
{ | |
int error = 0; | |
unsigned int i; | |
TOC_ENTRY* entry; | |
_TCHAR * string; | |
char * buffer; | |
FILE *fout; | |
FILE *file = _tfopen(fromfile, _T_INSTALL("rb")); | |
if (file == NULL) | |
return doError(ERROR_READ,fromfile); | |
if ( (error = readTOC(file)) != 0 ) { | |
fclose(file); | |
return doError(error,fromfile); | |
} | |
entry = toc; | |
string = malloc( MAX_PATH * sizeof(_TCHAR) ); | |
buffer = malloc( BUFFER_SIZE ); | |
for( i = 0; i < file_count; ++i, ++entry ) | |
{ | |
unsigned int size = entry->SIZE; | |
if ( (entry->FLAGS & FLAGS_EXTRACT ) == 0 ) continue; | |
if(_tcscmp(entry->FILENAME,fileName)!=0) continue; | |
_tcscpy(string, todir); | |
_tcscat(string,entry->FILENAME); | |
logFormatS(_T_INSTALL("[i] Extracting file %s"),string); | |
fout = _tfopen(string, _T_INSTALL("wb")); | |
if (fout == NULL) { error = doError(ERROR_WRITE,string); break; } | |
if ( fseek(file, entry->OFFSET, SEEK_SET ) != 0 ) { error = doError(ERROR_READ,fromfile); fclose(fout); break; } | |
while( size > 0 ) | |
{ | |
unsigned int n = MIN(size,BUFFER_SIZE); | |
if ( fread(buffer,n,1,file) != 1 ) { error = doError(ERROR_READ,fromfile); fclose(fout); break; } | |
if ( fwrite(buffer,n,1,fout) != 1 ) { error = doError(ERROR_WRITE,string); fclose(fout); break; } | |
size -= n; | |
} | |
if ( error != 0 ) break; | |
fclose(fout); | |
entry->FILENAME = _tcsdup(string); | |
entry->CREATED = 1; | |
} | |
if ( error != 0 ) | |
{ | |
logFormatI(_T_INSTALL("[i] Total %i files extracted"),file_count); | |
} | |
free(buffer); | |
free(string); | |
fclose(file); | |
return error; | |
} | |
/* | |
* Delete all extracted contents | |
*/ | |
void cleanup() | |
{ | |
unsigned int i; | |
TOC_ENTRY* entry = toc; | |
if ( toc == NULL ) return; | |
for( i = 0; i < file_count; ++i, ++entry ) | |
{ | |
if ( entry->CREATED ) | |
{ | |
logFormatS(_T_INSTALL("[i] Deleting file %s"),entry->FILENAME); | |
_tremove(entry->FILENAME); | |
} | |
} | |
freeTOC(); | |
} | |
static int doError( int error, _TCHAR* str ) | |
{ | |
switch( error ) { | |
case ERROR_READ: | |
writeLog(_T_INSTALL("[x] Error reading INSTALL_FILE")); | |
break; | |
case ERROR_INVALID_CONTENT: | |
writeLog(_T_INSTALL("[x] File INSTALL_FILE has no valid SFX signature or its content corrupted")); | |
break; | |
case ERROR_WRITE: | |
logFormatS(_T_INSTALL("[x] Error writing to file %s"),str); | |
break; | |
} | |
return error; | |
} | |
/* EOF */ |