/* * psupdaterengine.cpp * * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util/pscssetup.h" #include "updaterglobals.h" #include #include "psupdaterengine.h" #include "paws/pawsmanager.h" #include "paws/pawsmainwidget.h" #include "paws/pawstextbox.h" #include "paws/pawsbutton.h" #include "pawsupdaterwindow.h" #include "pawsserverlist.h" #include "pawsupdateroptions.h" #include "registry.h" #include "registrycreator.h" #include "updater.h" #include "updaterconfig.h" #include "fileutil.h" #ifdef CS_PLATFORM_WIN32 #include #endif using updater::Updater; using updater::Registry; using updater::ChangeList; using updater::RegistryCreator; psUpdaterEngine* psupdaterengine; #define CONFIGFILENAME "/this/psupdater.cfg" #ifdef APPNAME #undef APPNAME #endif #define APPNAME "PlaneShift Updater" #define SLEEP_TIME 100 /*********************************************************************/ CS_IMPLEMENT_APPLICATION /*********************************************************************/ psUpdaterEngine::psUpdaterEngine (iObjectRegistry *objectreg, bool silent) { object_reg = objectreg; paws = NULL; drawScreen = true; silentMode = silent; createMode = false; canUpdate = false; update = NULL; // Create the mutex that will control access to data updated from the background thread (in non-silent mode) status_mutex = csMutex::Create(); } /*********************************************************************/ psUpdaterEngine::~psUpdaterEngine () { if (scfiEventHandler && queue) queue->RemoveListener (scfiEventHandler); delete paws; update = NULL; vfs = NULL; } /*********************************************************************/ #define PSAPP "planeshift.updater.application" #define PS_QUERY_PLUGIN(myref,intf, str) \ myref = CS_QUERY_REGISTRY (object_reg, intf); \ if (!myref) { \ csReport (object_reg, CS_REPORTER_SEVERITY_ERROR, PSAPP, \ "No " str " plugin!");\ return false;\ } bool psUpdaterEngine::Initialize () { // Initialize updater update = csPtr(new Updater); if (update != NULL) { if (update->Initialize(object_reg)) { canUpdate = true; } else { pawsUpdaterWindow* updWnd = (pawsUpdaterWindow*)paws->FindWidget("updater"); pawsButton* btn = (pawsButton*)updWnd->FindWidget("UpdateButton"); btn->SetColour(graphics2d->FindRGB(255,0,0)); canUpdate = false; } } if (!silentMode) { graphics2d = graphics3d->GetDriver2D(); graphics2d->AllowResize(false); iNativeWindow *nw = graphics2d->GetNativeWindow(); if (nw) nw->SetTitle(APPNAME); // Create the PAWS window manager paws = new PawsManager( object_reg ,"/planeshift/art/apps.zip"); if ( !paws ) return false; mainWidget = new pawsMainWidget(); paws->SetMainWidget( mainWidget ); RegisterFactories(); // Load and assign a default button click sound for pawsbutton //paws->LoadSound("/this/art/music/gui/ccreate/next.wav","sound.standardButtonClick"); psupdaterengine->LoadWidgets(); pawsWidget* setup = paws->FindWidget("updater"); setup->SetBackgroundAlpha(0); paws->GetMouse()->ChangeImage("Standard Mouse Pointer"); // Register our event handler scfiEventHandler = csPtr (new EventHandler (this)); csEventID esub[] = { csevPreProcess (object_reg), csevProcess (object_reg), csevPostProcess (object_reg), csevFinalProcess (object_reg), csevFrame (object_reg), csevMouseEvent (object_reg), csevKeyboardEvent (object_reg), CS_EVENTLIST_END }; queue->RegisterListener(scfiEventHandler, esub); //End silent mode skipper } // Inform debug that everything initialized succesfully csReport (object_reg, CS_REPORTER_SEVERITY_NOTIFY, PSAPP, "psUpdaterEngine initialized."); return true; } /*********************************************************************/ bool psUpdaterEngine::HandleEvent (iEvent &ev) { lastEvent = &ev; static bool drawFrame; if ( paws->HandleEvent( ev ) ) { return true; } // Scoped lock is automatically released csScopedMutexLock status_lock(status_mutex); if (ev.Name == csevProcess (object_reg)) return true; else if (ev.Name == csevFinalProcess (object_reg)) { FinishFrame (); return true; } else if (ev.Name == csevPostProcess (object_reg)) { SetupFrame (); if (drawScreen) { FrameLimit(); graphics3d->BeginDraw(engine->GetBeginDrawFlags() | CSDRAW_2DGRAPHICS ); paws->Draw(); } else { csSleep(150); } } else if (ev.Name == csevCanvasHidden (object_reg, graphics3d->GetDriver2D ())) { drawScreen = false; } else if (ev.Name == csevCanvasExposed (object_reg, graphics3d->GetDriver2D ())) { drawScreen = true; } else if (ev.Name == csevQuit (object_reg)) { if(GetUpdater()) GetUpdater()->Die(); } return false; } /*********************************************************************/ void psUpdaterEngine::FrameLimit() { csTicks sleeptime; elapsed +=vc->GetElapsedTicks(); // Define sleeptime to limit fps to around 20 sleeptime = 50; // Here we sacrifice drawing time if(elapsed < sleeptime) csSleep(sleeptime - elapsed); elapsed = 0; } void psUpdaterEngine::QuitClient() { csRef cfg (CS_QUERY_REGISTRY (object_reg, iConfigManager)); if (cfg) cfg->Save(); queue->GetEventOutlet()->Broadcast (csevQuit (object_reg)); } /*********************************************************************/ void psUpdaterEngine::SetupFrame() { } void psUpdaterEngine::FinishFrame() { graphics3d->FinishDraw (); graphics3d->Print (NULL); } /*********************************************************************/ #define RegisterFactory(factoryclass) \ factory = new factoryclass(); void psUpdaterEngine::RegisterFactories() { pawsWidgetFactory* factory; RegisterFactory (pawsUpdaterWindowFactory); RegisterFactory (pawsServerListFactory); RegisterFactory (pawsUpdaterOptionsFactory); } void psUpdaterEngine::LoadWidgets() { bool ok = true; if(!paws->LoadWidget( "data/updater/updaterwindow.xml" )) { printf("Widget data/updater/updaterwindow.xml failed to load!\n"); ok = false; } if(!paws->LoadWidget( "data/updater/updateroptions.xml" )) { printf("Widget data/updater/updateroptions.xml failed to load!\n"); ok = false; } if(!paws->LoadWidget( "data/updater/serverlist.xml" )) { printf("Widget data/updater/serverlist.xml failed to load!\n"); ok = false; } if(!paws->LoadWidget( "data/gui/ok.xml" )) { printf("Widget data/gui/ok.xml failed to load!\n"); ok = false; } if ( !ok ) { printf("One or more widgets failed to load\n"); PS_PAUSEEXIT(1); } } void psUpdaterEngine::OutToScreen(const char* format,...) { csString string; va_list args; va_start (args, format); string.FormatV (format, args); va_end (args); OutToScreen(-1,string); } void psUpdaterEngine::OutToScreen(int color,const char* format,...) { csString string; va_list args; va_start (args, format); string.FormatV (format, args); va_end (args); // Scoped lock is automatically released at function exit csScopedMutexLock status_lock(status_mutex); if (silentMode) { printf("%s\n",string.GetData()); return; } pawsUpdaterWindow* updWnd = (pawsUpdaterWindow*)PawsManager::GetSingleton().FindWidget("updater"); pawsMessageTextBox* msgBox = (pawsMessageTextBox*)updWnd->FindWidget("list"); msgBox->AddMessage(string,color); } bool psUpdaterEngine::LaunchProgram(const char* path,const char* in) { #ifdef CS_PLATFORM_WIN32 STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); // Start the child process. if( !CreateProcess( NULL, // No module name (use command line). LPSTR(path), // Command line. NULL, // Process handle not inheritable. NULL, // Thread handle not inheritable. false, // Set handle inheritance to FALSE. CREATE_NEW_CONSOLE, // Detach the process from the parent, this will also remove the console NULL, // Use parent's environment block. LPSTR(in), // Use the specified startup directory &si, // Pointer to STARTUPINFO structure. &pi ) // Pointer to PROCESS_INFORMATION structure. ) { printf( "CreateProcess failed (%d).\n", GetLastError() ); return false; } #else printf("Program launch N/A\n"); #endif return true; } int psUpdaterEngine::FindRGB(int r,int g, int b) { if(silentMode) return 0; else return graphics2d->FindRGB(r,g,b); } bool psUpdaterEngine::InitializeCS() { if (!object_reg) PS_PAUSEEXIT(1); csDebuggingGraph::SetupGraph(object_reg); //Load VFS and set up the standard stuff if ( !csInitializer::SetupConfigManager(object_reg, 0)) { csReport(object_reg, CS_REPORTER_SEVERITY_ERROR, "psclient", "csInitializer::SetupConfigManager failed!\n" "Is your CRYSTAL environment variable set?"); PS_PAUSEEXIT(1); } vfs = CS_QUERY_REGISTRY (object_reg, iVFS); configMgr = CS_QUERY_REGISTRY (object_reg, iConfigManager); iConfigFile* cfg = configMgr->AddDomain(CONFIGFILENAME,vfs,iConfigManager::ConfigPriorityApplication+1); configMgr->SetDynamicDomain(cfg); //Load 3D driver if we're in non-silent mode if (!silentMode) { if (!csInitializer::RequestPlugins(object_reg, CS_REQUEST_ENGINE, CS_REQUEST_PLUGIN("crystalspace.font.server.freetype2",iFontServer), CS_REQUEST_OPENGL3D, CS_REQUEST_PLUGIN("crystalspace.graphic.image.io.multiplexer",iImageIO), CS_REQUEST_END)) { csReport(object_reg, CS_REPORTER_SEVERITY_ERROR, "psupdater", "Failed to initialize plugins!"); return 0; } } csRef pluginMgr; PS_QUERY_PLUGIN(pluginMgr,iPluginManager,"iPluginManager"); // Load plugins xmlSystem = CS_LOAD_PLUGIN(pluginMgr,"crystalspace.documentsystem.tinyxml",iDocumentSystem); if(!xmlSystem) { csReport(object_reg, CS_REPORTER_SEVERITY_ERROR, "psupdater", "Failed to load the XML parser!"); return false; } // Check for commandline help. if (csCommandLineHelper::CheckHelp (object_reg)) { csCommandLineHelper::Help (object_reg); PS_PAUSEEXIT(1); } //Open application (?) if (!csInitializer::OpenApplication (object_reg)) { csReport (object_reg, CS_REPORTER_SEVERITY_ERROR, "psclient", "csInitializer::OpenApplication failed!\n" "Is your CRYSTAL environment var set?"); csInitializer::DestroyApplication (object_reg); PS_PAUSEEXIT(1); } // tweak reporter plugin to report everything... // is there a command line switch or something to do this which I've missed? csRef reporter = CS_QUERY_REGISTRY (object_reg, iStandardReporterListener); if (reporter) { reporter->ShowMessageID(CS_REPORTER_SEVERITY_BUG, true); reporter->ShowMessageID(CS_REPORTER_SEVERITY_ERROR, true); reporter->ShowMessageID(CS_REPORTER_SEVERITY_NOTIFY, true); reporter->ShowMessageID(CS_REPORTER_SEVERITY_DEBUG, true); reporter->ShowMessageID(CS_REPORTER_SEVERITY_WARNING, true); reporter->SetMessageDestination(CS_REPORTER_SEVERITY_NOTIFY, true, false, false, false, true); reporter->SetMessageDestination(CS_REPORTER_SEVERITY_ERROR, true, false, false, false, true); reporter->SetMessageDestination(CS_REPORTER_SEVERITY_WARNING, true, false, false, false, true); reporter->SetMessageDestination(CS_REPORTER_SEVERITY_BUG, true, false, false, false, true); reporter->SetMessageDestination(CS_REPORTER_SEVERITY_DEBUG, true, false, false, false, true); } //mount some paths if ( !vfs->Mount ("/planeshift/", "$^") ) return false; //set the plugins PS_QUERY_PLUGIN (queue, iEventQueue, "iEventQueue"); if(!silentMode) { PS_QUERY_PLUGIN (engine, iEngine, "iEngine"); PS_QUERY_PLUGIN( graphics3d , iGraphics3D, "iGraphics3D" ); } PS_QUERY_PLUGIN (cfgmgr, iConfigManager, "iConfigManager"); PS_QUERY_PLUGIN (vc, iVirtualClock, "iVirtualClock"); return true; } void psUpdaterEngine::CriticalError(const char* format,...) { csString error; va_list args; va_start (args, format); error.FormatV (format, args); va_end (args); printf("CRITICAL ERROR: %s\n",error.GetData()); if (!silentMode) { #ifdef CS_PLATFORM_WIN32 MessageBox(0,error.GetData(),"Critical Error",MB_ICONERROR); #else PawsManager::GetSingleton().CreateWarningBox(error.GetData()); psupdaterengine->OutToScreen("Exiting in 10 seconds..."); csSleep(10000); #endif } PS_PAUSEEXIT(EXIT_FAILURE); } void psUpdaterEngine::InstallDirectory(const char* path,const char* to) { #ifdef CS_PLATFORM_WIN32 csRef files = vfs->FindFiles("/this/" + csString(path)); // Copy all files printf("Installing %d files..\n",(int)files->Length()); for (size_t i = 0;i < files->Length();i++) { csString fileName = files->Get(i); fileName = fileName.Slice(6,fileName.Length()-6); csString newp; newp.Format("%s\\%s",to,fileName.GetData()); // Win32 API fileName.ReplaceAll("/","\\"); // Windows uses \ newp .ReplaceAll("/","\\"); // WIndows uses \ if(GetFileAttributes(fileName.GetData()) & FILE_ATTRIBUTE_DIRECTORY) { updater::MakeDirectory(to + csString("\\") + fileName); fileName.ReplaceAll("\\","/"); // VFS uses / InstallDirectory(fileName,to); continue; } printf("Installing file %s..\n",fileName.GetData()); if(CopyFile(fileName.GetData(),newp.GetData(),false) == 0) { /* And some more cryptical Win32 API to get the error message :\*/ char buf[80]; LPVOID msgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &msgBuf, 0, NULL ); wsprintf(buf,"%s", msgBuf); // Make it csString csString error = buf; error = error.Slice(0,error.Length() -3); csString msg; msg.Format("Couldn't copy file %s!\n%s (%d)",fileName.GetData(),error.GetData(),dw); LocalFree(msgBuf); int ret = MessageBox(NULL, msg.GetData(), "File copy error", MB_ICONERROR | MB_RETRYCANCEL); if(ret == IDRETRY) { i--; continue; } } } #endif } void psUpdaterEngine::Install(const char* to) { #ifdef CS_PLATFORM_WIN32 // Give time to the previous installer to close Sleep(5000); // Copy files from updatertemp to main dir InstallDirectory("",to); if(!LaunchProgram(csString(to) + "\\updater.exe -autolaunch",to)) MessageBox(0,"Autolaunch of the new updater failed.\nPlease restart the updater manualy","Updater - Selfupdating",MB_ICONINFORMATION); #endif } /************************************************************/ int main(int argc, char** argv) { bool auto_sw = false; bool compare = false; bool addresses= true; bool autolaunch = false; csString create; csString updateRepo,file,mirror,install; //Check for silent and if we should create a new resp csRef cmdParser = csPtr (new csCommandLineParser); cmdParser->Initialize(argc,argv); // Help? if(cmdParser->GetBoolOption("help")) { csString ver; ver = UPDATER_VERSION; for(size_t i = 1;i < ver.Length();i++) { ver.Insert(i,'.'); i++; // Jump twice } printf("\nPlaneShift updater version %s\n",ver.GetData()); printf("Help section\n"); printf("===============================================================================\n"); printf(" -auto Switches to automatic mode without a GUI\n"); printf(" -create=/path/ Creates a new repository in the path specified\n"); printf(" -compare Compares your files and the default mirrors and prints\n"); printf(" which files that you need to update or create to make\n"); printf(" the repository exactly as your folder\n"); printf(" -updaterep=/repos/ Adds a file('-file=?') to a repository (Local use only)\n"); printf(" -addresses Prints a list of files to download instead of\n"); printf(" downloading them. You can specify the mirror to\n"); printf(" use with '-mirror=?'\n"); printf(" -autolaunch Auto start the updater (not needed with -auto)\n"); exit(0); } auto_sw = cmdParser->GetBoolOption("auto"); create = cmdParser->GetOption("create"); compare = cmdParser->GetBoolOption("compare"); updateRepo = cmdParser->GetOption("updaterep"); addresses = cmdParser->GetBoolOption("addresses"); mirror = cmdParser->GetOption("mirror"); // If we use addresses, otherwise no use install = cmdParser->GetOption("install"); // Not listed in help since it's only for internal use autolaunch = cmdParser->GetBoolOption("autolaunch"); if(updateRepo != "") { file = cmdParser->GetOption("file"); if(file == "") { printf("Trying to update the repository but no file option. Ignoring"); updateRepo = ""; } else auto_sw = true; } //We will be in silent mode when creating or updating the updater if (create.Length() > 0) auto_sw = true; if (compare) auto_sw = true; if (addresses) auto_sw = true; if(install.Length() > 0) auto_sw = true; //Create the standard stuff iObjectRegistry* objreg = csInitializer::CreateEnvironment (argc, argv); // Create our application object psupdaterengine = new psUpdaterEngine(objreg,auto_sw); // Initialize Crystal space for the engine if (!psupdaterengine->InitializeCS()) { csReport (objreg, CS_REPORTER_SEVERITY_ERROR, PSAPP, "Failed to init Crystal Space for app!"); PS_PAUSEEXIT(1); } // Initialize engine (skip if we're in install mode, need to load as little as possible) if(!install.Length()) { if (!psupdaterengine->Initialize()) { csReport (objreg, CS_REPORTER_SEVERITY_ERROR, PSAPP, "Failed to init app!"); PS_PAUSEEXIT(1); } } #if defined(CS_PLATFORM_WIN32) && defined(NDEBUG) if(!cmdParser->GetBoolOption("console") && !auto_sw) { FreeConsole(); } #endif // Do some cleanup if(psupdaterengine->GetVFS()->Exists("/this/updatertemp")) psupdaterengine->GetUpdater()->CleanUp(); // Begin argument specific code if (create.Length() > 0) { // Add the destination dir to the omit list to prevent scanning itself csString dirName(create); if (dirName.GetAt(0) == '/') dirName.DeleteAt(0); if (dirName.GetAt(dirName.Length()-1) == '/') dirName.DeleteAt(dirName.Length()-1); if (dirName.StartsWith("this",true)) dirName.DeleteAt(0,5); psupdaterengine->GetUpdater()->GetConfig()->AddDirOmit(dirName); psupdaterengine->SetCreateMode(); psupdaterengine->GetUpdater()->CreateRegistry(objreg,create); psupdaterengine->QuitClient(); } else if (updateRepo.Length() > 0) { RegistryCreator* creator = new RegistryCreator(objreg); creator->UpdateRegistry(updateRepo,file); psupdaterengine->QuitClient(); } else if (compare) { psupdaterengine->GetUpdater()->Compare(); psupdaterengine->QuitClient(); } else if (install.Length() > 0) { // Only used in windows #ifdef CS_PLATFORM_WIN32 psupdaterengine->Install(install); psupdaterengine->QuitClient(); #endif } else if (addresses) { int mirrori = 0; if(!mirror) mirrori = psupdaterengine->GetUpdater()->GetConfig()->GetMirror()->GetID(); else { mirrori = atoi(mirror); psupdaterengine->GetUpdater()->GetConfig()->SetMirror(mirrori); } ChangeList list; psupdaterengine->GetUpdater()->DownloadRegistry(); printf("Checking for updates..\n"); psupdaterengine->GetUpdater()->CheckUpdates(list); psupdaterengine->GetUpdater()->CreateToDoList(&list,mirrori); } else if (auto_sw) { psupdaterengine->GetUpdater()->StartUpdate(); } else if(autolaunch) { psupdaterengine->GetUpdater()->StartUpdate(); } // start the main event loop csDefaultRunLoop(objreg); // remove engine before destroying Application delete psupdaterengine; psupdaterengine = NULL; csInitializer::DestroyApplication (objreg); return 0; }