diff --git a/src/Style.cpp b/src/Style.cpp index 347b2c367..4ffd6c488 100644 --- a/src/Style.cpp +++ b/src/Style.cpp @@ -11,8 +11,8 @@ namespace style { ui::Colour Colour::InformationTitle = ui::Colour(140, 140, 255); - ui::Colour Colour::WarningTitle = ui::Colour(255, 255, 50); - ui::Colour Colour::ErrorTitle = ui::Colour(255, 20, 20); + ui::Colour Colour::WarningTitle = ui::Colour(255, 216, 32); + ui::Colour Colour::ErrorTitle = ui::Colour(255, 64, 32); ui::Colour Colour::ConfirmButton = ui::Colour(255, 255, 50); diff --git a/src/Update.cpp b/src/Update.cpp new file mode 100644 index 000000000..cee0b7c51 --- /dev/null +++ b/src/Update.cpp @@ -0,0 +1,187 @@ +#include +#include +#ifndef WIN32 +#include +#endif +#if !defined(MACOSX) && !defined(BSD) +#include +#endif +#include + +#ifdef WIN32 +#include +#else +#include +#include +#endif +#ifdef MACOSX +#include +#include +#endif + +#include +#include + +/*char *exe_name(void) +{ +#if defined WIN32 + char *name= (char *)malloc(64); + DWORD max=64, res; + while ((res = GetModuleFileName(NULL, name, max)) >= max) + { +#elif defined MACOSX + char *fn=malloc(64),*name=malloc(PATH_MAX); + uint32_t max=64, res; + if (_NSGetExecutablePath(fn, &max) != 0) + { + fn = realloc(fn, max); + _NSGetExecutablePath(fn, &max); + } + if (realpath(fn, name) == NULL) + { + free(fn); + free(name); + return NULL; + } + res = 1; +#else + char fn[64], *name=malloc(64); + size_t max=64, res; + sprintf(fn, "/proc/self/exe"); + memset(name, 0, max); + while ((res = readlink(fn, name, max)) >= max-1) + { +#endif +#ifndef MACOSX + max *= 2; + name = (char*)realloc(name, max); + memset(name, 0, max); + } +#endif + if (res <= 0) + { + free(name); + return NULL; + } + return name; +}*/ + +int update_start(char *data, int len) +{ + char *self=exe_name(), *temp; +#ifdef WIN32 + char *p; +#endif + FILE *f; + int res = 1; + + if (!self) + return 1; + +#ifdef WIN32 + temp = (char*)malloc(strlen(self)+12); + strcpy(temp, self); + p = temp + strlen(temp) - 4; + if (_stricmp(p, ".exe")) + p += 4; + strcpy(p, "_update.exe"); + + if (!MoveFile(self, temp)) + goto fail; + + f = fopen(self, "wb"); + if (!f) + goto fail; + if (fwrite(data, 1, len, f) != len) + { + fclose(f); + DeleteFile(self); + goto fail; + } + fclose(f); + + if ((int)ShellExecute(NULL, "open", self, NULL, NULL, SW_SHOWNORMAL) <= 32) + { + DeleteFile(self); + goto fail; + } + + return 0; +#else + temp = malloc(strlen(self)+8); + strcpy(temp, self); + strcat(temp, "-update"); + + f = fopen(temp, "w"); + if (!f) + goto fail; + if (fwrite(data, 1, len, f) != len) + { + fclose(f); + unlink(temp); + goto fail; + } + fclose(f); + + if (chmod(temp, 0755)) + { + unlink(temp); + goto fail; + } + + if (rename(temp, self)) + { + unlink(temp); + goto fail; + } + + execl(self, "powder-update", NULL); +#endif + +fail: + free(temp); + free(self); + return res; +} + +int update_finish(void) +{ +#ifdef WIN32 + char *temp, *self=exe_name(), *p; + int timeout = 60, err; + + temp = (char*)malloc(strlen(self)+12); + strcpy(temp, self); + p = temp + strlen(temp) - 4; + if (_stricmp(p, ".exe")) + p += 4; + strcpy(p, "_update.exe"); + + while (!DeleteFile(temp)) + { + err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND) + { + // just as well, then + free(temp); + return 0; + } + Sleep(500); + timeout--; + if (timeout <= 0) + { + free(temp); + return 1; + } + } + free(temp); +#endif + return 0; +} + +void update_cleanup(void) +{ +#ifdef WIN32 + update_finish(); +#endif +} diff --git a/src/Update.h b/src/Update.h new file mode 100644 index 000000000..118396b30 --- /dev/null +++ b/src/Update.h @@ -0,0 +1,16 @@ +/* + * Update.h + * + * Created on: Jun 21, 2012 + * Author: Simon + */ + +#ifndef UPDATE_H_ +#define UPDATE_H_ + +//char *exe_name(void); +int update_start(char *data, int len); +int update_finish(void); +void update_cleanup(void); + +#endif /* UPDATE_H_ */ diff --git a/src/dialogues/ConfirmPrompt.cpp b/src/dialogues/ConfirmPrompt.cpp index 6943555bd..91a13267e 100644 --- a/src/dialogues/ConfirmPrompt.cpp +++ b/src/dialogues/ConfirmPrompt.cpp @@ -6,6 +6,7 @@ */ #include "ConfirmPrompt.h" +#include "Style.h" #include "interface/Textblock.h" #include "interface/Button.h" @@ -15,7 +16,7 @@ ConfirmPrompt::ConfirmPrompt(std::string title, std::string message, ConfirmDial { int width, height; ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), title); - titleLabel->SetTextColour(ui::Colour(220, 220, 50)); + titleLabel->SetTextColour(style::Colour::WarningTitle); titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; AddComponent(titleLabel); @@ -55,7 +56,7 @@ ConfirmPrompt::ConfirmPrompt(std::string title, std::string message, ConfirmDial ui::Button * okayButton = new ui::Button(ui::Point(Size.X-76, Size.Y-16), ui::Point(76, 16), "Continue"); okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - okayButton->Appearance.TextInactive = ui::Colour(220, 220, 50); + okayButton->Appearance.TextInactive = style::Colour::WarningTitle; okayButton->SetActionCallback(new CloseAction(this, ResultOkay)); AddComponent(okayButton); diff --git a/src/dialogues/ErrorMessage.cpp b/src/dialogues/ErrorMessage.cpp index ebcfeae5d..ccd4a8ebe 100644 --- a/src/dialogues/ErrorMessage.cpp +++ b/src/dialogues/ErrorMessage.cpp @@ -5,6 +5,7 @@ * Author: Simon */ +#include "Style.h" #include "ErrorMessage.h" #include "interface/Button.h" #include "interface/Label.h" @@ -13,7 +14,7 @@ ErrorMessage::ErrorMessage(std::string title, std::string message): ui::Window(ui::Point(-1, -1), ui::Point(200, 75)) { ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), title); - titleLabel->SetTextColour(ui::Colour(200, 100, 50)); + titleLabel->SetTextColour(style::Colour::ErrorTitle); titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; AddComponent(titleLabel); diff --git a/src/game/GameController.cpp b/src/game/GameController.cpp index 71e485d39..affb88572 100644 --- a/src/game/GameController.cpp +++ b/src/game/GameController.cpp @@ -13,6 +13,7 @@ #include "dialogues/ConfirmPrompt.h" #include "GameModelException.h" #include "simulation/Air.h" +#include "update/UpdateActivity.h" #include "Notification.h" using namespace std; @@ -675,5 +676,6 @@ void GameController::RemoveNotification(Notification * notification) void GameController::RunUpdater() { Exit(); + new UpdateActivity(); } diff --git a/src/simulation/SaveRenderer.cpp b/src/simulation/SaveRenderer.cpp index 3d0e7c7eb..79dd4217b 100644 --- a/src/simulation/SaveRenderer.cpp +++ b/src/simulation/SaveRenderer.cpp @@ -20,6 +20,7 @@ SaveRenderer::SaveRenderer(){ Thumbnail * SaveRenderer::Render(GameSave * save) { int width, height; + Thumbnail * tempThumb; #ifdef OGLR width = save->blockWidth*CELL; height = save->blockHeight*CELL; @@ -27,7 +28,7 @@ Thumbnail * SaveRenderer::Render(GameSave * save) VideoBuffer buffer(width, height); buffer.BlendCharacter((width/2)-3, (height/2)-5, 'x', 255, 255, 255, 255); - Thumbnail * tempThumb = new Thumbnail(0, 0, buffer.Buffer, ui::Point(width, height)); + tempThumb = new Thumbnail(0, 0, buffer.Buffer, ui::Point(width, height)); return tempThumb; #else diff --git a/src/tasks/Task.cpp b/src/tasks/Task.cpp index dfb8a2ba0..7be013e4e 100644 --- a/src/tasks/Task.cpp +++ b/src/tasks/Task.cpp @@ -10,7 +10,7 @@ #include "Task.h" #include "TaskListener.h" -void Task::SetTaskListener(TaskListener * listener) +void Task::AddTaskListener(TaskListener * listener) { this->listener = listener; } @@ -37,6 +37,11 @@ std::string Task::GetStatus() return status; } +std::string Task::GetError() +{ + return error; +} + bool Task::GetDone() { return done; @@ -44,38 +49,50 @@ bool Task::GetDone() void Task::Poll() { - int newProgress; - bool newDone = false; - std::string newStatus; - pthread_mutex_lock(&taskMutex); - newProgress = thProgress; - newDone = thDone; - newStatus = std::string(thStatus); - pthread_mutex_unlock(&taskMutex); - - if(newProgress!=progress) { - progress = newProgress; - if(listener) - listener->NotifyProgress(this); - } - if(newStatus!=status) { - status = std::string(newStatus); - if(listener) - listener->NotifyStatus(this); - } - - if(done) + if(!done) { - pthread_join(doWorkThread, NULL); - pthread_mutex_destroy(&taskMutex); - after(); - } + int newProgress; + bool newDone = false; + std::string newStatus; + std::string newError; + pthread_mutex_lock(&taskMutex); + newProgress = thProgress; + newDone = thDone; + newStatus = std::string(thStatus); + newError = std::string(thError); + pthread_mutex_unlock(&taskMutex); - if(newDone!=done) - { - done = newDone; - if(listener) - listener->NotifyDone(this); + if(newProgress!=progress) { + progress = newProgress; + if(listener) + listener->NotifyProgress(this); + } + + if(newError!=error) { + error = std::string(newError); + if(listener) + listener->NotifyError(this); + } + + if(newStatus!=status) { + status = std::string(newStatus); + if(listener) + listener->NotifyStatus(this); + } + + if(done) + { + pthread_join(doWorkThread, NULL); + pthread_mutex_destroy(&taskMutex); + after(); + } + + if(newDone!=done) + { + done = newDone; + if(listener) + listener->NotifyDone(this); + } } } @@ -131,3 +148,10 @@ void Task::notifyDone() thDone = true; pthread_mutex_unlock(&taskMutex); } + +void Task::notifyError(std::string error) +{ + pthread_mutex_lock(&taskMutex); + thError = std::string(error); + pthread_mutex_unlock(&taskMutex); +} diff --git a/src/tasks/Task.h b/src/tasks/Task.h index 10a8b905f..ae83e1ddb 100644 --- a/src/tasks/Task.h +++ b/src/tasks/Task.h @@ -15,10 +15,11 @@ class TaskListener; class Task { public: - void SetTaskListener(TaskListener * listener); + void AddTaskListener(TaskListener * listener); void Start(); int GetProgress(); bool GetDone(); + std::string GetError(); std::string GetStatus(); void Poll(); Task() {} @@ -27,10 +28,12 @@ protected: int progress; bool done; std::string status; + std::string error; int thProgress; bool thDone; std::string thStatus; + std::string thError; TaskListener * listener; pthread_t doWorkThread; @@ -43,9 +46,10 @@ protected: virtual void doWork(); static void * doWork_helper(void * ref); - void notifyProgress(int progress); - void notifyStatus(std::string status); - void notifyDone(); + virtual void notifyProgress(int progress); + virtual void notifyError(std::string error); + virtual void notifyStatus(std::string status); + virtual void notifyDone(); }; #endif /* TASK_H_ */ diff --git a/src/tasks/TaskListener.h b/src/tasks/TaskListener.h index fc5d5e7ae..7405c03ee 100644 --- a/src/tasks/TaskListener.h +++ b/src/tasks/TaskListener.h @@ -12,6 +12,7 @@ class Task; class TaskListener { public: virtual void NotifyDone(Task * task) {} + virtual void NotifyError(Task * task) {} virtual void NotifyProgress(Task * task) {} virtual void NotifyStatus(Task * task) {} virtual ~TaskListener() {} diff --git a/src/tasks/TaskWindow.cpp b/src/tasks/TaskWindow.cpp index c705d36ec..910e11a52 100644 --- a/src/tasks/TaskWindow.cpp +++ b/src/tasks/TaskWindow.cpp @@ -5,28 +5,37 @@ * Author: Simon */ +#include #include "interface/Label.h" #include "TaskWindow.h" +#include "dialogues/ErrorMessage.h" +#include "Style.h" #include "Task.h" TaskWindow::TaskWindow(std::string title_, Task * task_, bool closeOnDone): task(task_), title(title_), - ui::Window(ui::Point(-1, -1), ui::Point(300, 200)), + ui::Window(ui::Point(-1, -1), ui::Point(240, 60)), progress(0), done(false), - closeOnDone(closeOnDone) + closeOnDone(closeOnDone), + progressStatus("0%") { - ui::Label * tempLabel = new ui::Label(ui::Point(3, 3), ui::Point(Size.X-6, 16), title); + ui::Label * tempLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), title); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + tempLabel->SetTextColour(style::Colour::WarningTitle); AddComponent(tempLabel); - statusLabel = new ui::Label(ui::Point(3, 19), ui::Point(Size.X-6, 16), ""); + statusLabel = new ui::Label(ui::Point(4, 23), ui::Point(Size.X-8, 15), ""); + statusLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + statusLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; AddComponent(statusLabel); ui::Engine::Ref().ShowWindow(this); - task->SetTaskListener(this); + task->AddTaskListener(this); task->Start(); } @@ -35,6 +44,11 @@ void TaskWindow::NotifyStatus(Task * task) statusLabel->SetText(task->GetStatus()); } +void TaskWindow::NotifyError(Task * task) +{ + new ErrorMessage("Error", task->GetError()); +} + void TaskWindow::NotifyDone(Task * task) { if(closeOnDone) @@ -53,6 +67,16 @@ void TaskWindow::Exit() void TaskWindow::NotifyProgress(Task * task) { progress = task->GetProgress(); + std::stringstream pStream; + if(progress>-1) + { + pStream << progress << "%"; + } + else + { + pStream << "Please wait..."; + } + progressStatus = pStream.str(); } void TaskWindow::OnTick(float dt) @@ -69,26 +93,32 @@ void TaskWindow::OnDraw() g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); - g->drawrect(Position.X + 20, Position.Y + 36, Size.X-40, 24, 255, 255, 255, 255); + g->draw_line(Position.X, Position.Y + Size.Y-17, Position.X + Size.X - 1, Position.Y + Size.Y-17, 255, 255, 255, 255); + + ui::Colour progressBarColour = style::Colour::WarningTitle; if(progress!=-1) { - float size = float(Size.X-40)*(float(progress)/100.0f); // TIL... - g->fillrect(Position.X + 20, Position.Y + 36, size, 24, 255, 255, 255, 255); + float size = float(Size.X-4)*(float(progress)/100.0f); // TIL... + g->fillrect(Position.X + 2, Position.Y + Size.Y-15, size, 13, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); } else { int size = 40, rsize = 0; - float position = float(Size.X-40)*(intermediatePos/100.0f); - if(position + size > Size.X-40) + float position = float(Size.X-4)*(intermediatePos/100.0f); + if(position + size - 1 > Size.X-4) { - size = (Size.X-40)-position; + size = (Size.X-4)-position+1; rsize = 40-size; } - g->fillrect(Position.X + 20 + position, Position.Y + 36, size, 24, 255, 255, 255, 255); + g->fillrect(Position.X + 2 + position, Position.Y + Size.Y-15, size, 13, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); if(rsize) { - g->fillrect(Position.X + 20, Position.Y + 36, rsize, 24, 255, 255, 255, 255); + g->fillrect(Position.X + 2, Position.Y + Size.Y-15, rsize, 13, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); } } + if(progress<50) + g->drawtext(Position.X + ((Size.X-Graphics::textwidth(progressStatus.c_str()))/2), Position.Y + Size.Y-13, progressStatus, 255, 255, 255, 255); + else + g->drawtext(Position.X + ((Size.X-Graphics::textwidth(progressStatus.c_str()))/2), Position.Y + Size.Y-13, progressStatus, 0, 0, 0, 255); } TaskWindow::~TaskWindow() { diff --git a/src/tasks/TaskWindow.h b/src/tasks/TaskWindow.h index 09893bc9b..820265c63 100644 --- a/src/tasks/TaskWindow.h +++ b/src/tasks/TaskWindow.h @@ -22,11 +22,13 @@ class TaskWindow: public ui::Window, public TaskListener { bool done; bool closeOnDone; ui::Label * statusLabel; + std::string progressStatus; public: TaskWindow(std::string title_, Task * task_, bool closeOnDone = true); virtual void NotifyStatus(Task * task); virtual void NotifyDone(Task * task); virtual void NotifyProgress(Task * task); + virtual void NotifyError(Task * task); virtual void OnTick(float dt); virtual void OnDraw(); virtual void Exit(); diff --git a/src/update/UpdateActivity.cpp b/src/update/UpdateActivity.cpp index 607204565..a8fb7fc79 100644 --- a/src/update/UpdateActivity.cpp +++ b/src/update/UpdateActivity.cpp @@ -5,21 +5,148 @@ * Author: Simon */ +#include +#include +#include "interface/Engine.h" #include "UpdateActivity.h" #include "tasks/Task.h" +#include "client/HTTP.h" +#include "Update.h" + class UpdateDownloadTask : public Task { - UpdateDownloadTask() {}; - /*virtual void doWork() +public: + UpdateDownloadTask(std::string updateName, UpdateActivity * a) : updateName(updateName), a(a) {}; +private: + UpdateActivity * a; + std::string updateName; + virtual void notifyDone() { - while(1) - }*/ + a->NotifyDone(this); + } + virtual void doWork() + { + std::stringstream errorStream; + void * request = http_async_req_start(NULL, (char*)updateName.c_str(), NULL, 0, 0); + notifyStatus("Downloading update"); + notifyProgress(-1); + while(!http_async_req_status(request)) + { + int total, done; + http_async_get_length(request, &total, &done); + notifyProgress((float(done)/float(total))*100.0f); + } + + char * data; + int dataLength, status; + data = http_async_req_stop(request, &status, &dataLength); + if (status!=200) + { + if (data) + free(data); + errorStream << "Server responded with Status " << status; + notifyError("Could not download update"); + return; + } + if (!data) + { + errorStream << "Server responded with nothing"; + notifyError("Server did not return any data"); + return; + } + + notifyStatus("Unpacking update"); + notifyProgress(-1); + + int uncompressedLength; + + if(dataLength<16) + { + errorStream << "Unsufficient data, got " << dataLength << " bytes"; + goto corrupt; + } + if (data[0]!=0x42 || data[1]!=0x75 || data[2]!=0x54 || data[3]!=0x54) + { + errorStream << "Invalid update format"; + goto corrupt; + } + + uncompressedLength = (unsigned char)data[4]; + uncompressedLength |= ((unsigned char)data[5])<<8; + uncompressedLength |= ((unsigned char)data[6])<<16; + uncompressedLength |= ((unsigned char)data[7])<<24; + + char * res; + res = (char *)malloc(uncompressedLength); + if (!res) + { + errorStream << "Unable to allocate " << uncompressedLength << " bytes of memory for decompression"; + goto corrupt; + } + + int dstate; + dstate = BZ2_bzBuffToBuffDecompress((char *)res, (unsigned *)&uncompressedLength, (char *)(data+8), dataLength-8, 0, 0); + if (dstate) + { + errorStream << "Unable to decompress update: " << dstate; + free(res); + goto corrupt; + } + + free(data); + + notifyStatus("Applying update"); + notifyProgress(-1); + + if (update_start(res, uncompressedLength)) + { + update_cleanup(); + notifyError("Update failed - try downloading a new version."); + } + + return; + + corrupt: + notifyError("Downloaded update is corrupted\n" + errorStream.str()); + free(data); + return; + } }; UpdateActivity::UpdateActivity() { - // TODO Auto-generated constructor stub + char my_uri[] = "http://" SERVER "/Update.api?Action=Download&Architecture=" + #if defined WIN32 + "Windows32" + #elif defined LIN32 + "Linux32" + #elif defined LIN64 + "Linux64" + #elif defined MACOSX + "MacOSX" + #else + "Unknown" + #endif + "&InstructionSet=" + #if defined X86_SSE3 + "SSE3" + #elif defined X86_SSE2 + "SSE2" + #elif defined X86_SSE + "SSE" + #else + "SSE" + #endif + ; + updateDownloadTask = new UpdateDownloadTask(my_uri, this); + updateWindow = new TaskWindow("Downloading update...", updateDownloadTask, true); +} +void UpdateActivity::NotifyDone(Task * sender) +{ + updateWindow->Exit(); + ui::Engine::Ref().Exit(); + delete this; } UpdateActivity::~UpdateActivity() { diff --git a/src/update/UpdateActivity.h b/src/update/UpdateActivity.h index 6865fdfe0..1f3da7375 100644 --- a/src/update/UpdateActivity.h +++ b/src/update/UpdateActivity.h @@ -17,6 +17,7 @@ class UpdateActivity { public: UpdateActivity(); virtual ~UpdateActivity(); + virtual void NotifyDone(Task * sender); }; #endif /* UPDATEACTIVITY_H_ */