| #include "globals.h" | |
| #include "sfxformat.h" | |
| #include "Formatter.h" | |
| #include "String.h" | |
| #include "assert.h" | |
| #include <stdlib.h> | |
| #include <memory.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #ifndef WINDOWS | |
| #include <unistd.h> | |
| #endif | |
| #define MAX_SUPPORTED_VERSION 1 | |
| #define BUFFER_SIZE 4*1024 /*4 kb buffer*/ | |
| static bool in_debug_mode = false; | |
| #ifdef WINDOWS | |
| #define LOG(x) if (in_debug_mode) System::WriteLog(x) | |
| #else | |
| #define LOG(x) if (in_debug_mode) printf(x) | |
| #endif | |
| STRING GetExtractorFileName() | |
| { | |
| #ifdef WINDOWS | |
| STRING file_name = (STRING) System::SafeAllocMemory((MAX_PATH + 1) * sizeof(char)); | |
| if ( 0 == file_name) | |
| { | |
| return 0; | |
| } | |
| GetModuleFileNameA(GetModuleHandleW(NULL),file_name,MAX_PATH); | |
| return file_name; | |
| #endif | |
| } | |
| bool IsHeaderValid( SFXHeader* header) | |
| { | |
| if ( 0 != System::MemCmp((BYTE*)SIGNATURE,header->Signature, 19)) | |
| { | |
| return false; | |
| } | |
| else if ( MAX_SUPPORTED_VERSION < header->Version) | |
| { | |
| return false; | |
| } | |
| else | |
| return true; | |
| } | |
| STRING CreateTempFolder() | |
| { | |
| UINT someUniqValue = 0; | |
| #ifdef WINDOWS | |
| someUniqValue = GetTickCount(); | |
| #else | |
| someUniqValue = rand(); | |
| #endif | |
| STRING result = (STRING)System::SafeAllocMemory(20); | |
| if ( 0 == result ) | |
| return 0; | |
| String::Sprintf2(result,"sfx%u.tmp%s", someUniqValue, (int)PATH_SEPARATOR); | |
| return result; | |
| } | |
| static BYTE BUFFER[BUFFER_SIZE]; | |
| bool CopyFileData(File* source, File* dest, UINT copyAmount) | |
| { | |
| UINT fileSize = copyAmount; | |
| while ( fileSize != 0 ) | |
| { | |
| UINT dataSize = 0; | |
| dataSize = fileSize < BUFFER_SIZE ? fileSize : BUFFER_SIZE; | |
| if ( dataSize != source->Read(source,BUFFER,dataSize) ) | |
| return false; | |
| if ( false == dest->Write(dest,BUFFER,dataSize)) | |
| return false; | |
| fileSize -= dataSize; | |
| } | |
| return true; | |
| } | |
| STRING GetStringById(UINT id, UINT* stringOffsetTable, File* tableHolder) | |
| { | |
| //get absolute offset of desired string | |
| UINT absoluteStringOffset = *(stringOffsetTable + id); | |
| //move file pointer to it | |
| if ( false == tableHolder->Seek(tableHolder,absoluteStringOffset,SEEK__BEGIN)) | |
| return 0; | |
| UINT fileNameSize = tableHolder->ReadUINT(tableHolder); | |
| //get file path | |
| STRING fileName = (STRING)System::SafeAllocMemory(fileNameSize + 1); | |
| if ( 0 == fileName) | |
| return 0; | |
| if (fileNameSize != tableHolder->Read(tableHolder,(BYTE*)fileName, fileNameSize)) | |
| return 0; | |
| return fileName; | |
| } | |
| static UINT filesDataStartOffset = 0; | |
| static FileInformation* fileInfoTable = 0; | |
| bool ExtractFile(UINT fileID, FileInformation* fileInfoTable, UINT* stringsOffsetsTable, File* dataHolder, STRING targetFolder ) | |
| { | |
| UINT fileSize = (fileInfoTable + fileID)->Size; | |
| UINT fileNameId = /*(fileInfoTable + fileID)->NameStringId*/ fileID; | |
| UINT currentFileDataOffset = filesDataStartOffset; | |
| if ( false == dataHolder->Seek(dataHolder, filesDataStartOffset, SEEK__BEGIN) ) | |
| return false; | |
| for ( UINT index = 0; index < fileID; index++) | |
| { | |
| currentFileDataOffset+= (fileInfoTable + index)->Size; | |
| } | |
| STRING fileName = GetStringById(fileNameId, stringsOffsetsTable, dataHolder); | |
| if ( 0 == fileName ) | |
| return false; | |
| STRING fileFullPath = Path::Combine(targetFolder, fileName); | |
| if ( 0 == fileFullPath) | |
| return false; | |
| LOG("extracting '");LOG(fileName);LOG("'..."); | |
| if ( Path::IsDirectory(fileFullPath) ) | |
| File::CreateDirectory(fileFullPath); | |
| else | |
| { | |
| //we must be sure, that directory for file exists | |
| //if not - create it | |
| STRING fileDirectory = Path::GetFileFolder(fileFullPath); | |
| if ( fileDirectory == 0){ | |
| LOG("ERROR:failed to find file '");LOG(fileFullPath);LOG("' directory!\n"); | |
| return false; | |
| } | |
| File::CreateDirectory(fileDirectory); | |
| System::SafeFreeMemory(fileDirectory); | |
| File* targetFile = File::Create(fileFullPath); | |
| if ( 0 == targetFile) | |
| return false; | |
| //move file pointer back to file data | |
| if ( false == dataHolder->Seek(dataHolder,currentFileDataOffset, SEEK__BEGIN)) | |
| return false; | |
| if ( false == CopyFileData(dataHolder, targetFile, fileSize)) | |
| return false; | |
| targetFile->Close(targetFile); | |
| System::SafeFreeMemory(targetFile); | |
| } | |
| System::SafeFreeMemory(fileName); | |
| System::SafeFreeMemory(fileFullPath); | |
| LOG("OK\n"); | |
| return true; | |
| } | |
| bool RemoveFolder ( STRING folderName) | |
| { | |
| #ifdef WINDOWS | |
| STRING folder = (STRING) System::SafeAllocMemory(String::StrLen(folderName) + 30);//20 actually | |
| String::StrCpy(folder,"cmd /C rmdir /S /Q "); | |
| String::StrCat(folder,folderName); | |
| WinExec(folder, SW_HIDE); | |
| System::SafeFreeMemory(folder); | |
| return true; | |
| #else | |
| STRING folder = (STRING) System::SafeAllocMemory(strlen(folderName) + 30);//20 actually | |
| strcpy(folder,"rm -rf "); | |
| strcat(folder,folderName); | |
| system(folder); | |
| System::SafeFreeMemory(folder); | |
| return true; | |
| #endif | |
| } | |
| int install(STRING extractorFullName){ | |
| if ( 0 == extractorFullName) | |
| { | |
| System::ShowErrorMessage("Failed to get extractor name"); | |
| return -1; | |
| } | |
| //open self | |
| LOG("launcher file name ='");LOG(extractorFullName);LOG("'\n"); | |
| File* self = File::Open(extractorFullName); | |
| if ( 0 == self ) | |
| { | |
| System::ShowErrorMessage("Failed to open extractor"); | |
| return -1; | |
| } | |
| Formatter* formatter = (Formatter*)System::SafeAllocMemory(sizeof(Formatter)); | |
| formatter->_file = self; | |
| SFXFooter* footer = formatter->ParseFooter(); | |
| if ( 0 == footer ) | |
| { | |
| self->Close(self); | |
| System::ShowErrorMessage("Failed to navigate to SFX Footer"); | |
| return -1; | |
| } | |
| if (in_debug_mode ){ | |
| STRING footer_buffer = (STRING)System::SafeAllocMemory(40); //39 should be enough | |
| String::Sprintf2(footer_buffer,"PackageSize=%u CRC=%u\n", footer->PackageSize, footer->CRC32); | |
| LOG("SFX Footer record parsed:");LOG(footer_buffer); | |
| } | |
| //navigate to header | |
| if ( false == self->Seek(self,-8 - footer->PackageSize, SEEK__END) ){ | |
| self->Close(self); | |
| LOG("ERROR: Failed to navigate to Header Record!"); | |
| return -1; | |
| } | |
| SFXHeader* header = formatter->ParseHeader(); | |
| if ( 0 == header ){ | |
| self->Close(self); | |
| //delete footer; | |
| System::ShowErrorMessage("Failed to parse SFX Header"); | |
| LOG("Failed to parse SFX Header\n"); | |
| return -1; | |
| } | |
| LOG("SFX Header record parsing..."); | |
| //check header | |
| if ( ! IsHeaderValid(header) ) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header;*/ | |
| System::ShowErrorMessage("SFX Header invalid"); | |
| LOG("FAILED! Header is invalid.\n"); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| LOG("Execution Info parsing..."); | |
| ExecutionInfo* executionInfo = formatter->ParseExecutionInfo(); | |
| if ( 0 == executionInfo) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header;*/ | |
| System::ShowErrorMessage("Failed to parse ExecutionInfo"); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| LOG("File Information Table parsing..."); | |
| fileInfoTable = formatter->ParseFileInformationTable(header->numberOfFiles); | |
| if ( 0 == fileInfoTable) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| System::ShowErrorMessage("Failed to parse File Information Table"); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| LOG("Required Files Table parsing..."); | |
| UINT requiredIdsCount = self->ReadUINT(self); //if zero - all content of archive need to be extracted | |
| UINT* requiredIdsTable = 0; | |
| if ( 0 != requiredIdsCount) | |
| { | |
| //read ids then | |
| UINT tableSize = requiredIdsCount * 4; | |
| requiredIdsTable = (UINT*)System::SafeAllocMemory(tableSize); | |
| if ( 0 == requiredIdsTable || tableSize != self->Read(self,(BYTE*)requiredIdsTable,tableSize) ) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| System::ShowErrorMessage("Failed to parse Reqired Files Table"); | |
| return -1; | |
| } | |
| } | |
| LOG("OK\n"); | |
| LOG("Allocating memory for Strings Table..."); | |
| UINT stringTableOffset = self->GetCurentPos(self); | |
| UINT stringTableEntriesCount = header->numberOfFiles + 2; | |
| UINT* stringTableOffsetsTable = (UINT*)System::SafeAllocMemory( 4 * (stringTableEntriesCount)); | |
| if ( 0 == stringTableOffsetsTable) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| if ( 0 != requiredIdsCount) | |
| System::SafeFreeMemory( requiredIdsTable); | |
| System::ShowErrorMessage("Failed to allocate memory for Strings Table"); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| LOG("Parsing Strings Table..."); | |
| UINT skippedBytes = 0; | |
| //skip string table and build table of strings offsets. it's entries count = numberOfFiles + 2; 1 - for runnable id and 1 for args | |
| for ( UINT entryIndex = 0; entryIndex < stringTableEntriesCount; entryIndex++) | |
| { | |
| *(stringTableOffsetsTable + entryIndex) = stringTableOffset + skippedBytes; | |
| skippedBytes += 4; | |
| UINT stringSize = self->ReadUINT(self); | |
| //skip string | |
| if ( false == self->Skip(self,stringSize)) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| if ( 0 != requiredIdsCount) | |
| System::SafeFreeMemory( requiredIdsTable); | |
| System::SafeFreeMemory(stringTableOffsetsTable); | |
| System::ShowErrorMessage("Failed to parse Strings Table"); | |
| return -1; | |
| } | |
| skippedBytes += stringSize; | |
| } | |
| LOG("OK\n"); | |
| filesDataStartOffset = self->GetCurentPos(self); | |
| LOG("Getting system temporary folder..."); | |
| //now, prepare for extraction | |
| STRING temporaryFolder = System::GetTemporaryFolder(); | |
| if ( 0 == temporaryFolder ) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| if ( 0 != requiredIdsCount) | |
| System::SafeFreeMemory( requiredIdsTable); | |
| System::SafeFreeMemory(stringTableOffsetsTable); | |
| System::ShowErrorMessage("Failed to get system temporary folder"); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| LOG("Forming temporary folder..."); | |
| //form extraction folder | |
| STRING tempFolder = CreateTempFolder(); | |
| if (0 == tempFolder) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| if ( 0 != requiredIdsCount) | |
| System::SafeFreeMemory( requiredIdsTable); | |
| System::SafeFreeMemory(stringTableOffsetsTable); | |
| System::ShowErrorMessage("Failed to form extraction temporary folder"); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| LOG("Creating extraction folder..."); | |
| STRING extractionFolder = Path::Combine( temporaryFolder, tempFolder); | |
| if (0 == extractionFolder) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| if ( 0 != requiredIdsCount) | |
| System::SafeFreeMemory( requiredIdsTable); | |
| System::SafeFreeMemory(stringTableOffsetsTable); | |
| System::ShowErrorMessage("Failed to combine extraction folder"); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| //System::SafeFreeMemory(temporaryFolder); | |
| System::SafeFreeMemory(tempFolder); | |
| LOG("Extracting required files\n"); | |
| //do we need to extract all files? | |
| if ( 0 == requiredIdsCount ) | |
| {//extract all | |
| for ( UINT fileID = 0; fileID < header->numberOfFiles; fileID++) | |
| if (false == ExtractFile(fileID, fileInfoTable, stringTableOffsetsTable, self, extractionFolder)) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| if ( 0 != requiredIdsCount) | |
| System::SafeFreeMemory( requiredIdsTable); | |
| System::SafeFreeMemory(stringTableOffsetsTable); | |
| System::ShowErrorMessage("Failed to extract file(s). Ensure that your temporary folder is accessable and have enough free size."); | |
| RemoveFolder(extractionFolder); | |
| return -1; | |
| } | |
| } | |
| else | |
| for ( UINT reqId = 0; reqId < requiredIdsCount; reqId++ ) | |
| { | |
| UINT requiredFileId = *(requiredIdsTable + reqId); | |
| if (false == ExtractFile(requiredFileId, fileInfoTable, stringTableOffsetsTable, self, extractionFolder)) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| if ( 0 != requiredIdsCount) | |
| System::SafeFreeMemory( requiredIdsTable); | |
| System::SafeFreeMemory(stringTableOffsetsTable); | |
| System::ShowErrorMessage("Failed to extract file(s). Ensure that your temporary folder is accessable and have enough free size."); | |
| RemoveFolder(extractionFolder); | |
| return -1; | |
| } | |
| } | |
| LOG("Extraction succseeded\n"); | |
| LOG("Reading autorun arguments..."); | |
| //get argument string | |
| STRING tmpArguments = GetStringById(executionInfo->ArgsStringId, stringTableOffsetsTable, self); | |
| if ( 0 == tmpArguments) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| if ( 0 != requiredIdsCount) | |
| System::SafeFreeMemory( requiredIdsTable); | |
| System::SafeFreeMemory(stringTableOffsetsTable); | |
| System::ShowErrorMessage("Failed to retreive arguments for executable."); | |
| RemoveFolder(extractionFolder); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| LOG("Reading autorun path..."); | |
| //prepare it | |
| STRING arguments = String::ReplaceAll(tmpArguments,ARCHIVE_PATH_PLACE_HOLDER, extractorFullName); | |
| STRING origExecString = GetStringById(executionInfo->ExecutableStringId, stringTableOffsetsTable, self); | |
| STRING tmpExecutionString = Path::Combine(extractionFolder, origExecString); //here we leak some memory | |
| STRING executionString = (STRING)System::SafeAllocMemory(String::StrLen(tmpExecutionString) + 7); | |
| if ( 0 == executionString) | |
| { | |
| self->Close(self); | |
| /*delete footer; | |
| delete header; | |
| delete executionInfo;*/ | |
| if ( 0 != requiredIdsCount) | |
| System::SafeFreeMemory( requiredIdsTable); | |
| System::SafeFreeMemory(stringTableOffsetsTable); | |
| System::ShowErrorMessage("Failed to allocate enough memory to form execution string."); | |
| RemoveFolder(extractionFolder); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| UINT flags = executionInfo->Flags; | |
| if ( (flags & FLAG_JAVA_APP ) == 1) | |
| //analyze flags - is executable Java class? | |
| { | |
| //yes: start JVM then | |
| if ( flags & FLAG_JAVA_WINDOWED_APP ) | |
| { | |
| String::StrCpy(executionString,"javax"); | |
| // strcat(executionString, origExecString); | |
| } | |
| else | |
| { | |
| String::StrCpy(executionString,"java"); | |
| // strcat(executionString, origExecString); | |
| } | |
| //args here a bit altered | |
| STRING tmp = (STRING)System::SafeAllocMemory(String::StrLen(arguments) + String::StrLen(origExecString) + 20); | |
| String::StrCpy(tmp, origExecString); | |
| String::StrCat(tmp," "); | |
| String::StrCat(tmp, arguments); | |
| System::SafeFreeMemory(arguments); | |
| arguments = tmp; | |
| } | |
| else | |
| //no, native executable | |
| { | |
| System::SafeFreeMemory(executionString); | |
| executionString = tmpExecutionString; | |
| } | |
| #ifdef WINDOWS | |
| bool result = SetCurrentDirectoryA(extractionFolder); | |
| #else | |
| chdir(extractionFolder); | |
| #endif | |
| LOG("Executing autorun: '");LOG(executionString);LOG("'..."); | |
| //execute what needed and wait for it | |
| if ( false == System::ExecuteAndWait(executionString, arguments)) | |
| { | |
| System::ShowErrorMessage("Failed to start executable."); | |
| RemoveFolder(extractionFolder); | |
| return -1; | |
| } | |
| LOG("OK\n"); | |
| //executable finished - do clean up | |
| //simply remove recursively extraction directory | |
| LOG("Removing temporary folder..."); | |
| #ifdef WINDOWS | |
| SetCurrentDirectoryA(temporaryFolder); | |
| #else | |
| chdir(temporaryFolder); | |
| #endif | |
| RemoveFolder(extractionFolder); | |
| LOG("FINISHED\n"); | |
| LOG("All went OK\n"); | |
| //whoo... we done | |
| return 0; | |
| } | |
| #ifdef WINDOWS | |
| int main() | |
| #else | |
| int main(int argc, char** argv) | |
| #endif | |
| { | |
| //__asm int 3; | |
| //get extractor file name | |
| #ifdef WINDOWS | |
| STRING extractorFullName = GetExtractorFileName(); | |
| STRING commandLine = GetCommandLineA(); | |
| //are we in debug mode? | |
| in_debug_mode = String::EndsWith(commandLine,"-debug"); | |
| #else | |
| STRING extractorFullName = argv[0]; | |
| if ( argc > 1) | |
| in_debug_mode = String::EndsWith(argv[1],"-debug"); | |
| #endif | |
| if ( in_debug_mode ) | |
| { | |
| #ifdef WINDOWS | |
| if ( false == System::AllocateConsole("org.eclipse.epp.sfx native launcher debug console")) | |
| { | |
| System::ShowErrorMessage("Failed to allocate debug console"); | |
| return -1; | |
| } | |
| #else | |
| printf("[DEBUG]org.eclipse.epp.sfx native launcher started\n"); | |
| #endif | |
| } | |
| int retCode = install(extractorFullName); | |
| if ( in_debug_mode ){ | |
| LOG("In debug mode, press any key to close this window"); | |
| #ifdef WINDOWS | |
| char buffer; | |
| DWORD dwDummy; | |
| ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE),&buffer, 1, &dwDummy, NULL ); | |
| #else | |
| int dummy; | |
| scanf("%i", &dummy); | |
| #endif | |
| } | |
| } |