#include "devpakinstaller.h"
#include <bzlib.h>
#include <wx/intl.h>
#include "mytar.h"
#include "cbiniparser.h"
#include <wx/filename.h>
#include <wx/msgdlg.h>
DevPakInstaller::DevPakInstaller()
: m_pDlg(0)
{
//ctor
}
DevPakInstaller::~DevPakInstaller()
{
//dtor
EndProgressDialog();
}
void DevPakInstaller::CreateProgressDialog(int max)
{
EndProgressDialog();
m_pDlg = new wxProgressDialog(_("Progress"), _T(""), max, 0, wxPD_APP_MODAL);
m_pDlg->SetSize(480, m_pDlg->GetSize().y);
m_pDlg->Centre();
}
void DevPakInstaller::EndProgressDialog()
{
if (m_pDlg)
m_pDlg->Destroy();
m_pDlg = 0;
}
void DevPakInstaller::UpdateProgress(int val, const wxString& newtext)
{
if (m_pDlg)
{
m_pDlg->Update(val, newtext);
wxYield();
}
}
bool DevPakInstaller::Install(const wxString& name, const wxString& filename, const wxString& dir, wxArrayString* files)
{
CreateProgressDialog(4);
m_Status.Clear();
// Step 1: decompress
UpdateProgress(0, _("Decompressing ") + filename);
wxYield();
wxString m_Status;
wxString tmpfile = wxFileName::CreateTempFileName(_T("cb"));
if (!Decompress(filename, tmpfile))
{
m_Status += _(" [Decompression failed]");
wxRemoveFile(tmpfile);
EndProgressDialog();
return false;
}
// Step 2: un-tar .DevPackage file
UpdateProgress(1, _("Unpacking control-file"));
wxYield();
TAR* t = new TAR(tmpfile.c_str());
TAR::Record* r = t->FindFile(_T("*.DevPackage"));
wxString controlFile = r->name;
wxString status2;
if (!r || !t->ExtractFile(r, dir, status2))
{
m_Status << _(" [Control file unpacking failed] - ");
m_Status << status2;
delete t;
wxRemoveFile(tmpfile);
EndProgressDialog();
return false;
}
// Step 3: un-tar
UpdateProgress(2, _("Unpacking all files"));
wxYield();
if (!Untar(controlFile, tmpfile, dir, files))
{
m_Status += _(" [Unpacking failed]");
delete t;
wxRemoveFile(tmpfile);
RemoveControlFile(dir + _T("/") + controlFile);
EndProgressDialog();
return false;
}
UpdateProgress(3, _("Done"));
delete t;
wxRemoveFile(tmpfile);
RemoveControlFile(dir + _T("/") + controlFile);
EndProgressDialog();
return true;
}
bool DevPakInstaller::Uninstall(const wxString& entry)
{
m_Status.Clear();
IniParser* p = new IniParser;
p->ParseFile(entry);
int idx = p->FindGroupByName(_T("Files"), false);
if (idx == -1)
{
m_Status << _("No [Files] section in ") << entry << _T('\n');
return false;
}
wxArrayString pathlist;
CreateProgressDialog(p->GetKeysCount(idx));
for (int i = 0; i < p->GetKeysCount(idx); ++i)
{
wxString file = p->GetKeyName(idx, i);
UpdateProgress(i, _("Removing: ") + file);
if (!wxRemoveFile(file))
m_Status << _("Can't remove ") << file << _T('\n');
else
{
wxString path = wxPathOnly(file);
while (path.Last() == _T('/') || path.Last() == _T('\\'))
path.RemoveLast();
if (pathlist.Index(path) == wxNOT_FOUND)
pathlist.Add(path);
}
}
EndProgressDialog();
// remove dirs
CreateProgressDialog(pathlist.GetCount());
for (unsigned int i = 0; i < pathlist.GetCount(); ++i)
{
wxString path = pathlist[i];
int pos = path.Find(_T('/'), true);
while (pos != wxNOT_FOUND)
{
wxRmdir(path);
path.Remove(pos, path.Length() - pos);
UpdateProgress(i, _("Removing directory: ") + path);
pos = path.Find(_T('/'), true);
}
}
EndProgressDialog();
delete p;
if (!wxRemoveFile(entry))
m_Status << _("Can't remove ") << entry << _T('\n');
return true; //m_Status.IsEmpty();
}
void DevPakInstaller::RemoveControlFile(const wxString& filename)
{
if (filename.IsEmpty())
return;
wxRemoveFile(filename);
wxRmdir(wxFileName(filename).GetPath()); // deletes it if non-empty
}
bool DevPakInstaller::Decompress(const wxString& filename, const wxString& tmpfile)
{
// open file
FILE* f = fopen(filename.mb_str(), "rb");
if (!f)
{
m_Status = _("Error opening input file!");
return false;
}
// open BZIP2 stream
int bzerror;
BZFILE* bz = BZ2_bzReadOpen(&bzerror, f, 0, 0, 0L, 0);
if (!bz || bzerror != BZ_OK)
{
m_Status = _("Can't read compressed stream!");
fclose(f);
return false;
}
// open output file
FILE* fo = fopen(tmpfile.mb_str(), "wb");
if (!fo)
{
m_Status = _("Error opening output file!");
fclose(f);
return false;
}
// read stream writing to uncompressed file
char buffer[2048];
while (bzerror != BZ_STREAM_END)
{
BZ2_bzRead(&bzerror, bz, buffer, 2048);
if (bzerror != BZ_OK && bzerror != BZ_STREAM_END)
{
m_Status = _("Error reading from stream!");
BZ2_bzReadClose(&bzerror, bz);
fclose(fo);
fclose(f);
return false;
}
fwrite(buffer, 2048, 1, fo);
}
BZ2_bzReadClose(&bzerror, bz);
fclose(fo);
fclose(f);
return true;
}
bool DevPakInstaller::Untar(const wxString& controlFile, const wxString& filename, const wxString& dirname, wxArrayString* files)
{
TAR t(filename.c_str());
wxString tmpControlFile;
if (!controlFile.IsEmpty())
{
IniParser ini;
ini.ParseFile(dirname + _T("/") + controlFile);
int grp = ini.FindGroupByName(_T("Files"));
for (int i = 0; grp != -1 && i < ini.GetKeysCount(grp); ++i)
{
t.AddReplacer(ini.GetKeyName(grp, i), ini.GetKeyValue(grp, i));
}
// add replacers for all paths above controlFile
// i.e. in Allegro.DevPak:
// Test/Allegro.DevPackage (everything is below Test/),
// add replacer for Test/
int pos = controlFile.Last('/');
if (pos != wxNOT_FOUND)
{
wxString replacer = controlFile.SubString(0, pos);
tmpControlFile = controlFile;
tmpControlFile.Replace(replacer, _T(""));
t.AddReplacer(replacer, _T(""));
}
// add replacers (with user's permission) for DLLs
#ifdef __WXMSW__
TAR::Record* r = t.FindFile(_T("*.dll"));
if (!r)
r = t.FindFile(_T("*.DLL"));
if (r)
{
if (wxMessageBox(_("This package contains some DLLs.\nDo you want to install them system-wide (YES recommended)?"), _("Confirmation"), wxICON_QUESTION | wxYES_NO) == wxYES)
{
wxFileName fname(wxGetOSDirectory() + _T("/system32/"));
fname.MakeRelativeTo(dirname);
t.AddReplacer(_T("dll/"), fname.GetFullPath());
}
wxYield();
}
#endif
}
bool ret = t.ExtractAll(dirname.c_str(), m_Status, files);
if (!tmpControlFile.IsEmpty())
RemoveControlFile(dirname + _T("/") + tmpControlFile);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1