blob: 4a5f9b78b6f65247bc4c94eb836ffaae2be9c88f [file] [log] [blame]
#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 */