| #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 */ |