blob: f276b433bc52c600216f8d1537a01b4f901a7714 [file] [log] [blame]
#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
}
}