| #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; |
| scnaf("%i", &dummy); |
| #endif |
| } |
| } |