9 #include <unordered_map>
10 #include <unordered_set>
55 using std::make_unique;
61 using std::unique_ptr;
62 using std::unordered_map;
63 using std::unordered_set;
105 Installer::Installer(): installThreadMutex(
"install-thread-mutex")
107 Application::setLimitFPS(
true);
108 Tools::loadSettings(
this);
109 this->
engine = Engine::getInstance();
110 this->
popUps = make_unique<PopUps>();
119 vector<string> installedComponents;
121 FileSystem::getStandardFileSystem()->getContentAsStringArray(
".",
"install.components.db", installedComponents);
127 timestamp = FileSystem::getStandardFileSystem()->getContentAsString(
".",
"install.version.db");
132 string installFolder;
135 FileSystem::getStandardFileSystem()->getContentAsStringArray(
".",
"install.files.db", log);
136 if (log.size() > 0) installFolder = log[0];
147 unordered_map<string, string> variables = {
150 {
"installfolder", installFolder.empty() ==
true?
homeFolder +
"/Applications/" +
installerProperties.
get(
"install_path",
"TDME2-based-application"):installFolder}
155 "resources/installer",
156 "installer_welcome.xml",
163 "resources/installer",
164 "installer_license.xml",
170 "installer_components",
172 "resources/installer",
173 "installer_components.xml",
177 string componentsXML =
"<space height=\"10\" />\n";
178 for (
auto componentIdx = 1;
true; componentIdx++) {
180 auto componentRequired = StringTools::trim(StringTools::toLowerCase(
installerProperties.
get(
"component" + to_string(componentIdx) +
"_required",
"false"))) ==
"true";
181 auto componentInstalled =
false;
182 for (
const auto& installedComponentName: installedComponents) {
183 if (installedComponentName == componentName) {
184 componentInstalled =
true;
188 if (componentName.empty() ==
true)
break;
190 string(
"<element id=\"component" + to_string(componentIdx) +
"\" width=\"100%\" height=\"25\">\n") +
191 string(
" <layout width=\"100%\" alignment=\"horizontal\">\n") +
192 string(
" <space width=\"10\" />\n") +
193 string(
" <checkbox id=\"checkbox_component" + to_string(componentIdx) +
"\" name=\"checkbox_component" + to_string(componentIdx) +
"\" value=\"1\" selected=\"" + (componentRequired ==
true || componentInstalled ==
true?
"true":
"false") +
"\" disabled=\"" + (componentRequired ==
true?
"true":
"false") +
"\" />\n") +
194 string(
" <space width=\"10\" />\n") +
195 string(
" <text width=\"*\" font=\"{$font.default}\" size=\"16\" text=\"" + GUIParser::escape(componentName) +
"\" color=\"{$color.font_normal}\" height=\"100%\" vertical-align=\"center\" />\n") +
196 string(
" </layout>\n") +
197 string(
"</element>\n");
206 "resources/installer",
207 "installer_folder.xml",
212 "installer_installing",
214 "resources/installer",
215 "installer_installing.xml",
220 "installer_finished",
222 "resources/installer",
223 "installer_finished.xml",
228 "installer_welcome2",
230 "resources/installer",
231 "installer_welcome2.xml",
236 "installer_uninstalling",
238 "resources/installer",
239 "installer_uninstalling.xml",
266 popUps->getInfoDialogScreenController()->show(
"An error occurred:", exception.what());
291 class CheckForUpdateThread:
public Thread {
293 CheckForUpdateThread(
Installer* installer):
Thread(
"checkforupdate-thread",
true), installer(installer) {
301 Console::println(
"CheckForUpdateThread::run(): init");
304 auto currentTimestamp = installer->
timestamp;
307 auto repairHaveLocalFile =
false;
308 auto completionFileName = Application::getOSName() +
"-" + Application::getCPUName() +
"-upload-";
313 vector<string> files;
314 FileSystem::getStandardFileSystem()->list(
"installer", files);
315 for (
const auto& file: files) {
316 if (StringTools::startsWith(file, completionFileName) ==
true) {
317 Console::println(
"CheckForUpdateThread: Have upload completion file: " + file);
318 installer->
timestamp = StringTools::substring(file, completionFileName.size());
319 repairHaveLocalFile =
true;
323 if (installer->
timestamp.empty() ==
false) {
324 Console::println(
"CheckForUpdateThread::run(): filesystem: newest timestamp: " + installer->
timestamp);
337 httpClient.
setMethod(HTTPClient::HTTP_METHOD_GET);
338 httpClient.
setURL(repository +
"?timestamp=" + to_string(Time::getCurrentMillis()));
344 while ((pos = response.find(completionFileName, pos)) != string::npos) {
345 pos+= completionFileName.size();
346 auto timestampWebNew = StringTools::substring(response, pos, pos + 14);
348 if ((installer->
timestamp.empty() ==
true && (timestampWeb.empty() ==
true || timestampWebNew > timestampWeb)) ||
349 (installer->
timestamp.empty() ==
false && ((timestampWeb.empty() ==
true || timestampWebNew > timestampWeb) && timestampWebNew > installer->
timestamp))) {
350 timestampWeb = timestampWebNew;
354 Console::println(
string(
"CheckForUpdateThread::run(): An error occurred: ") + exception.what());
362 if (timestampWeb.empty() ==
false) {
363 Console::println(
"CheckForUpdateThread::run(): repository: newest timestamp: " + timestampWeb);
370 for (
auto componentIdx = 0;
true; componentIdx++) {
371 auto componentId = componentIdx == 0?
"installer":
"component" + to_string(componentIdx);
373 if (componentName.empty() ==
true)
break;
374 Console::println(
"CheckForUpdateThread::run(): Having component: " + to_string(componentIdx) +
": " + componentName);
376 if (componentInclude.empty() ==
true) {
377 Console::println(
"CheckForUpdateThread::run(): component: " + to_string(componentIdx) +
": missing includes. Skipping.");
380 auto componentFileName = Application::getOSName() +
"-" + Application::getCPUName() +
"-" + StringTools::replace(StringTools::replace(componentName,
" - ",
"-"),
" ",
"-") +
"-" + installer->
timestamp +
".ta";
383 Console::println(
"CheckForUpdateThread::run(): Component: " + to_string(componentIdx) +
": component file name: " + componentFileName +
": Downloading");
392 if (componentIdx > 0) {
394 installer->
downloadedFiles.push_back(
"installer/" + componentFileName +
".sha256.download");
395 installer->
downloadedFiles.push_back(
"installer/" + componentFileName +
".sha256");
398 httpDownloadClient.
reset();
401 httpDownloadClient.
setFile(
"installer/" + componentFileName +
".sha256");
403 httpDownloadClient.
start();
404 while (httpDownloadClient.
isFinished() ==
false) {
408 Thread::sleep(250LL);
410 httpDownloadClient.
join();
412 installer->
popUps->getInfoDialogScreenController()->show(
"An error occurred:",
"File not found in repository: " + componentFileName +
".sha256(" + to_string(httpDownloadClient.
getStatusCode()) +
")");
414 Console::println(
"CheckForUpdateThread::run(): done");
420 if (componentIdx > 0) {
422 installer->
downloadedFiles.push_back(
"installer/" + componentFileName +
".download");
423 installer->
downloadedFiles.push_back(
"installer/" + componentFileName);
426 httpDownloadClient.
reset();
427 httpDownloadClient.
setFile(
"installer/" + componentFileName);
429 httpDownloadClient.
start();
430 while (httpDownloadClient.
isFinished() ==
false) {
434 Thread::sleep(250LL);
436 httpDownloadClient.
join();
438 installer->
popUps->getInfoDialogScreenController()->show(
"An error occurred:",
"File not found in repository: " + componentFileName +
"(" + to_string(httpDownloadClient.
getStatusCode()) +
")");
440 Console::println(
"CheckForUpdateThread::run(): done");
450 installer->
popUps->getInfoDialogScreenController()->show(
"Information",
"No update available");
452 Console::println(
"CheckForUpdateThread::run(): done");
464 Console::println(
"CheckForUpdateThread::run(): done");
468 auto checkForUpdateThread =
new CheckForUpdateThread(
this);
469 checkForUpdateThread->start();
488 class InstallThread:
public Thread {
490 InstallThread(
Installer* installer):
Thread(
"install-thread",
true), installer(installer) {
494 Console::println(
"InstallThread::run(): init");
499 vector<string> windowsUpdateRenameFiles;
510 auto hadException =
false;
512 vector<string> components;
517 installer->
popUps->getInfoDialogScreenController()->show(
"An error occurred:", exception.what());
522 log.push_back(installFolder);
523 if (hadException ==
false) {
526 auto file =
dynamic_cast<ArchiveFileSystem*
>(FileSystem::getInstance())->getArchiveFileName();
528 vector<uint8_t> content;
529 Console::println(
"InstallThread::run(): Installer: Copy: " + file);
530 FileSystem::getStandardFileSystem()->getContent(
531 FileSystem::getStandardFileSystem()->getPathName(file),
532 FileSystem::getStandardFileSystem()->getFileName(file),
535 auto generatedFileName = installFolder +
"/" + file;
537 FileSystem::getStandardFileSystem()->getPathName(generatedFileName)
539 FileSystem::getStandardFileSystem()->setContent(
540 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
541 FileSystem::getStandardFileSystem()->getFileName(generatedFileName),
547 vector<uint8_t> content;
548 Console::println(
"InstallThread::run(): Installer: Copy: " + file);
549 FileSystem::getStandardFileSystem()->getContent(
550 FileSystem::getStandardFileSystem()->getPathName(file),
551 FileSystem::getStandardFileSystem()->getFileName(file),
554 auto generatedFileName = installFolder +
"/" + file;
556 FileSystem::getStandardFileSystem()->getPathName(generatedFileName)
558 FileSystem::getStandardFileSystem()->setContent(
559 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
560 FileSystem::getStandardFileSystem()->getFileName(generatedFileName),
566 for (
auto componentIdx = 1;
true; componentIdx++) {
569 if (componentName.empty() ==
true)
break;
572 auto componentFileName = Application::getOSName() +
"-" + Application::getCPUName() +
"-" + StringTools::replace(StringTools::replace(componentName,
" - ",
"-"),
" ",
"-") +
"-" + installer->
timestamp +
".ta";
585 FileSystem::getStandardFileSystem()->removeFile(
"installer", componentFileName);
589 FileSystem::getStandardFileSystem()->removeFile(
"installer", componentFileName +
".sha256");
599 components.push_back(componentName);
602 Console::println(
"InstallThread::run(): Having component: " + to_string(componentIdx) +
": " + componentName);
603 auto componentInclude = installer->
installerProperties.
get(
"component" + to_string(componentIdx) +
"_include",
"");
604 if (componentInclude.empty() ==
true) {
605 Console::println(
"InstallThread::run(): component: " + to_string(componentIdx) +
": missing includes. Skipping.");
609 Console::println(
"InstallThread::run(): Component: " + to_string(componentIdx) +
": component file name: " + componentFileName);
616 unique_ptr<ArchiveFileSystem> archiveFileSystem;
621 archiveFileSystem = make_unique<ArchiveFileSystem>(
"installer/" + componentFileName);
622 if (archiveFileSystem->computeSHA256Hash() != FileSystem::getStandardFileSystem()->getContentAsString(
"installer", componentFileName +
".sha256")) {
623 throw ExceptionBase(
"Failed to verify: " + componentFileName +
", remove files in ./installer/ and try again");
628 vector<string> files;
630 uint64_t totalSize = 0LL;
631 uint64_t doneSize = 0LL;
632 for (
const auto& file: files) {
633 totalSize+= archiveFileSystem->getFileSize(
634 archiveFileSystem->getPathName(file),
635 archiveFileSystem->getFileName(file)
638 for (
const auto& file: files) {
639 vector<uint8_t> content;
640 Console::println(
"InstallThread::run(): Component: " + to_string(componentIdx) +
": " + file);
641 archiveFileSystem->getContent(
642 archiveFileSystem->getPathName(file),
643 archiveFileSystem->getFileName(file),
646 auto generatedFileName = installFolder +
"/" + file;
648 FileSystem::getStandardFileSystem()->getPathName(generatedFileName)
651 auto windowsGeneratedFile = generatedFileName;
655 (StringTools::endsWith(StringTools::toLowerCase(generatedFileName),
".exe") ==
true ||
656 StringTools::endsWith(StringTools::toLowerCase(generatedFileName),
".dll") ==
true)) {
657 windowsUpdateRenameFiles.push_back(FileSystem::getStandardFileSystem()->getFileName(generatedFileName));
658 windowsGeneratedFile+=
".update";
662 if (archiveFileSystem->isExecutable(archiveFileSystem->getPathName(file), archiveFileSystem->getFileName(file)) ==
true) {
663 #if defined(__FreeBSD__) || defined(__linux__) || defined(__NetBSD__)
664 FileSystem::getStandardFileSystem()->setContent(
665 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
666 FileSystem::getStandardFileSystem()->getFileName(generatedFileName),
669 log.push_back(generatedFileName);
670 FileSystem::getStandardFileSystem()->setExecutable(
671 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
672 FileSystem::getStandardFileSystem()->getFileName(generatedFileName)
674 auto launcherName = installer->
installerProperties.
get(
"launcher_" + StringTools::toLowerCase(FileSystem::getStandardFileSystem()->getFileName(generatedFileName)),
"");
675 if (launcherName.empty() ==
false) {
677 FileSystem::getStandardFileSystem()->setContentFromString(
678 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
679 FileSystem::getStandardFileSystem()->getFileName(generatedFileName) +
".sh",
682 "cd " + installFolder +
"\n" +
684 FileSystem::getStandardFileSystem()->getPathName(generatedFileName) +
"/" + FileSystem::getStandardFileSystem()->getFileName(generatedFileName) +
" " +
685 "</dev/null &>/dev/null &\n"
687 FileSystem::getStandardFileSystem()->setExecutable(
688 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
689 FileSystem::getStandardFileSystem()->getFileName(generatedFileName) +
".sh"
691 auto executablePathName = FileSystem::getInstance()->getPathName(generatedFileName);
694 if (archiveFileSystem->exists(
"resources/platforms/icons/" + iconFileName) ==
false &&
695 FileSystem::getInstance()->exists(executablePathName +
"/resources/platforms/icons/" + iconFileName) ==
false) iconFileName =
"default-icon.png";
696 FileSystem::getStandardFileSystem()->setContentFromString(
697 installer->
homeFolder +
"/" +
".local/share/applications",
698 launcherName +
".desktop",
700 "[Desktop Entry]\n" +
701 "Name=" + launcherName +
"\n" +
702 "Exec=" + FileSystem::getStandardFileSystem()->getPathName(generatedFileName) +
"/" + FileSystem::getStandardFileSystem()->getFileName(generatedFileName) +
".sh\n" +
704 "Type=Application\n" +
705 "Icon=" + installFolder +
"/resources/platforms/icons/" + iconFileName +
"\n"
707 log.push_back(generatedFileName +
".sh");
708 log.push_back(installer->
homeFolder +
"/" +
".local/share/applications/" + launcherName +
".desktop");
710 #elif defined(_WIN32)
711 FileSystem::getStandardFileSystem()->setContent(
712 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
713 FileSystem::getStandardFileSystem()->getFileName(windowsGeneratedFile),
716 log.push_back(generatedFileName);
717 auto executable = FileSystem::getStandardFileSystem()->getFileName(generatedFileName);
720 StringTools::substring(
721 StringTools::toLowerCase(executable),
723 executable.rfind(
'.')
727 if (launcherName.empty() ==
false) {
728 FileSystem::getStandardFileSystem()->setContentFromString(
730 "windows-create-shortcut.bat",
731 FileSystem::getInstance()->getContentAsString(
".",
"windows-create-shortcut.bat")
733 auto startMenuFolder = string(installer->
homeFolder) +
"/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/" + installer->
installerProperties.
get(
"name",
"TDME2 based application");
735 auto linkFile = startMenuFolder +
"/" + launcherName +
".lnk";
736 auto executablePathName = FileSystem::getInstance()->getPathName(generatedFileName);
738 auto iconFileName = StringTools::replace(StringTools::toLowerCase(
executableFileName),
".exe",
"") +
"-icon.ico";
739 if (FileSystem::getInstance()->exists(
"resources/platforms/win32/" + iconFileName) ==
false &&
740 FileSystem::getInstance()->exists(executablePathName +
"/resources/platforms/win32/" + iconFileName) ==
false) iconFileName =
"default-icon.ico";
742 StringTools::replace(StringTools::replace(installFolder,
"/",
"\\"),
" ",
"^ ") +
"\\windows-create-shortcut.bat " +
743 "\"" + StringTools::replace(generatedFileName,
"/",
"\\") +
"\" " +
744 "\"" + StringTools::replace(linkFile,
"/",
"\\") +
"\" " +
745 "\"resources\\platforms\\win32\\" + iconFileName +
"\" "
748 Application::execute(
749 StringTools::replace(StringTools::replace(installFolder,
"/",
"\\"),
" ",
"^ ") +
"\\windows-create-shortcut.bat " +
750 "\"" + StringTools::replace(generatedFileName,
"/",
"\\") +
"\" " +
751 "\"" + StringTools::replace(linkFile,
"/",
"\\") +
"\" " +
752 "\"resources\\platforms\\win32\\" + iconFileName +
"\" "
755 log.push_back(linkFile);
756 FileSystem::getStandardFileSystem()->removeFile(installFolder,
"windows-create-shortcut.bat");
759 FileSystem::getStandardFileSystem()->setContent(
760 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
761 FileSystem::getStandardFileSystem()->getFileName(generatedFileName),
764 log.push_back(generatedFileName);
765 FileSystem::getStandardFileSystem()->setExecutable(
766 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
767 FileSystem::getStandardFileSystem()->getFileName(generatedFileName)
772 FileSystem::getStandardFileSystem()->setContent(
773 FileSystem::getStandardFileSystem()->getPathName(generatedFileName),
775 FileSystem::getStandardFileSystem()->getFileName(windowsGeneratedFile),
777 FileSystem::getStandardFileSystem()->getFileName(generatedFileName),
781 log.push_back(generatedFileName);
783 doneSize+= content.size();
797 FileSystem::getStandardFileSystem()->removeFile(
"installer", componentFileName);
801 FileSystem::getStandardFileSystem()->removeFile(
"installer", componentFileName +
".sha256");
807 installer->
popUps->getInfoDialogScreenController()->show(
"An error occurred:", exception.what());
817 string updateFinishBatch;
818 updateFinishBatch+=
"@ECHO OFF\r\n";
819 updateFinishBatch+=
"ECHO FINISHING UPDATE. PLEASE DO NOT CLOSE.\r\n";
820 updateFinishBatch+=
"setlocal EnableDelayedExpansion\r\n";
822 for (
const auto& file: windowsUpdateRenameFiles) {
823 auto updateFile = file +
".update";
825 ":loop" + to_string(loopIdx) +
"\r\n" +
826 "if exist \"" + updateFile +
"\" (\r\n" +
827 " if exist \"" + file +
"\" (\r\n" +
828 " del \"" + file +
"\" > nul 2>&1\r\n" +
829 " if exist \"" + file +
"\" goto loop" + to_string(loopIdx) +
"\r\n" +
830 " rename \"" + updateFile +
"\" \"" + file +
"\" > nul 2>&1\r\n" +
832 " rename \"" + updateFile +
"\" \"" + file +
"\" > nul 2>&1\r\n" +
838 FileSystem::getStandardFileSystem()->setContentFromString(installFolder,
"update-finish.bat", updateFinishBatch);
840 installer->
popUps->getInfoDialogScreenController()->show(
"An error occurred:", exception.what());
847 FileSystem::getStandardFileSystem()->setContentFromStringArray(installFolder,
"install.files.db", log);
848 FileSystem::getStandardFileSystem()->setContentFromStringArray(installFolder,
"install.components.db", components);
849 FileSystem::getStandardFileSystem()->setContentFromString(installFolder,
"install.version.db", installer->
timestamp);
851 installer->
popUps->getInfoDialogScreenController()->show(
"An error occurred:", exception.what());
856 if (hadException ==
false) {
866 Console::println(
"InstallThread::run(): done");
872 auto installThread =
new InstallThread(
this);
873 installThread->start();
892 class UninstallThread:
public Thread {
894 UninstallThread(
Installer* installer):
Thread(
"install-thread",
true), installer(installer) {
898 Console::println(
"UninstallThread::run(): init");
908 auto hadException =
false;
911 FileSystem::getStandardFileSystem()->getContentAsStringArray(
".",
"install.files.db", log);
913 installer->
popUps->getInfoDialogScreenController()->show(
"An error occurred:", exception.what());
918 auto installFolder = log.size() > 0?log[0] +
"/":
"";
920 if (hadException ==
false) {
922 auto uninstallFinishBatchLoopIdx = 0;
923 string uninstallFinishBatch;
924 uninstallFinishBatch+=
"@ECHO OFF\r\n";
925 uninstallFinishBatch+=
"ECHO FINISHING UNINSTALL. PLEASE DO NOT CLOSE.\r\n";
926 uninstallFinishBatch+=
"setlocal EnableDelayedExpansion\r\n";
928 for (
auto i = 1; i < log.size(); i++) {
935 if (FileSystem::getStandardFileSystem()->exists(log[i]) ==
true) {
936 FileSystem::getStandardFileSystem()->removeFile(
937 FileSystem::getStandardFileSystem()->getPathName(log[i]),
938 FileSystem::getStandardFileSystem()->getFileName(log[i])
942 Console::println(
string(
"UninstallThread::run(): An error occurred: ") + innerException.what());
945 (StringTools::endsWith(log[i],
".dll") ==
true ||
946 StringTools::endsWith(log[i],
".exe") ==
true)) {
947 auto file = FileSystem::getStandardFileSystem()->getFileName(log[i]);
948 uninstallFinishBatch+=
949 ":loop" + to_string(uninstallFinishBatchLoopIdx) +
"\r\n" +
950 "if exist \"" + file +
"\" (\r\n" +
951 " del \"" + file +
"\" > nul 2>&1\r\n" +
952 " if exist \"" + file +
"\" goto loop" + to_string(uninstallFinishBatchLoopIdx) +
"\r\n" +
954 uninstallFinishBatchLoopIdx++;
962 auto file =
"console.log";
963 uninstallFinishBatch+=
964 ":loop" + to_string(uninstallFinishBatchLoopIdx) +
"\r\n" +
965 "if exist \"" + file +
"\" (\r\n" +
966 " del \"" + file +
"\" > nul 2>&1\r\n" +
967 " if exist \"" + file +
"\" goto loop" + to_string(uninstallFinishBatchLoopIdx) +
"\r\n" +
969 uninstallFinishBatchLoopIdx++;
972 FileSystem::getStandardFileSystem()->setContentFromString(installFolder,
"uninstall-finish.bat", uninstallFinishBatch);
974 installer->
popUps->getInfoDialogScreenController()->show(
"An error occurred:", exception.what());
982 if (hadException ==
false) {
983 vector<string> folders;
984 for (
auto i = 1; i < log.size(); i++) {
985 auto folderCandidate = FileSystem::getStandardFileSystem()->getPathName(log[i]);
986 if (FileSystem::getStandardFileSystem()->isPath(folderCandidate) ==
true) {
987 while (StringTools::startsWith(folderCandidate, installFolder) ==
true) {
988 folders.push_back(folderCandidate);
989 folderCandidate = FileSystem::getStandardFileSystem()->getPathName(folderCandidate);
993 sort(folders.begin(), folders.end());
994 reverse(folders.begin(), folders.end());
995 auto newEnd = unique(folders.begin(), folders.end());
996 folders.resize(distance(folders.begin(), newEnd));
997 for (
const auto& folder: folders) {
999 FileSystem::getStandardFileSystem()->removePath(
1004 Console::println(
string(
"UninstallThread::run(): An error occurred: ") + exception.what());
1010 FileSystem::unsetFileSystem();
1011 vector<string> installerFiles;
1013 FileSystem::getStandardFileSystem()->list(
1014 installFolder +
"installer",
1018 Console::println(
string(
"UninstallThread::run(): An error occurred: ") + exception.what());
1020 for (
const auto& installerFile: installerFiles) {
1021 if (StringTools::endsWith(installerFile,
".ta") ==
true ||
1022 StringTools::endsWith(installerFile,
".ta.sha256") ==
true) {
1024 FileSystem::getStandardFileSystem()->removeFile(
1025 installFolder +
"installer",
1029 Console::println(
string(
"UninstallThread::run(): An error occurred: ") + exception.what());
1034 FileSystem::getStandardFileSystem()->removePath(installFolder +
"installer",
false);
1036 Console::println(
string(
"UninstallThread::run(): An error occurred: ") + exception.what());
1042 if (hadException ==
false) {
1044 FileSystem::getStandardFileSystem()->removeFile(
".",
"install.files.db");
1045 FileSystem::getStandardFileSystem()->removeFile(
".",
"install.components.db");
1046 FileSystem::getStandardFileSystem()->removeFile(
".",
"install.version.db");
1047 FileSystem::getStandardFileSystem()->removeFile(
".",
"console.log");
1049 installer->
popUps->getInfoDialogScreenController()->show(
"An error occurred:", exception.what());
1050 hadException =
true;
1055 if (hadException ==
false) {
1057 FileSystem::getStandardFileSystem()->removePath(installFolder,
false);
1059 Console::println(
string(
"UninstallThread::run(): An error occurred: ") + exception.what());
1064 if (hadException ==
false) {
1074 if (installFolder[1] ==
':') drive = StringTools::substring(installFolder, 0, 2) +
" && ";
1080 "\"" + installFolder +
"/" +
"\"" +
1081 " && start cmd /c \"uninstall-finish.bat && del uninstall-finish.bat && cd .. && rmdir \"\"\"" + StringTools::replace(installFolder,
"/",
"\\") +
"\"\"\" > nul 2>&1\"").c_str()
1085 Application::exit(0);
1089 Console::println(
"UninstallThread::run(): done");
1095 UninstallThread* uninstallThread =
new UninstallThread(
this);
1096 uninstallThread->start();
1101 Console::println(
"Installer::performScreenAction(): Unhandled screen: " + to_string(
screen));
1112 FileSystem::getStandardFileSystem()->exists(
"install.files.db") ==
true &&
1113 FileSystem::getStandardFileSystem()->exists(
"install.components.db") ==
true &&
1114 FileSystem::getStandardFileSystem()->exists(
"install.version.db") ==
true;
1121 homeFolder = StringTools::replace(
string(getenv(
"USERPROFILE")),
'\\',
'/');
1129 popUps->getInfoDialogScreenController()->show(
"An error occurred:", exception.what());
1146 if (node->
getId() ==
"button_next") {
1154 if (node->
getId() ==
"button_back") {
1157 timestamp = FileSystem::getStandardFileSystem()->getContentAsString(
".",
"install.version.db");
1159 Console::println(
string(
"Installer::onAction(): An error occurred: ") + exception.what());
1170 if (node->
getId() ==
"button_agree") {
1175 if (node->
getId() ==
"button_install") {
1180 if (node->
getId() ==
"button_cancel") {
1181 FileSystem::unsetFileSystem();
1185 FileSystem::getStandardFileSystem()->removeFile(
".", downloadedFile);
1187 Console::println(
"Installer::onAction(): Removing downloaded file failed: " + downloadedFile);
1190 Application::exit(0);
1192 if (node->
getId() ==
"button_finish") {
1198 if (installFolder[1] ==
':') drive = StringTools::substring(installFolder, 0, 2) +
" && ";
1199 string finishCommand;
1202 finishCommand+=
" && start cmd /c \"update-finish.bat && del update-finish.bat && " +
installerProperties.
get(
"launch",
"") +
".exe" +
"\"";
1211 "\"" + installFolder +
"/" +
"\"" +
1215 #elif defined(__APPLE__)
1216 Application::executeBackground(
1222 Application::executeBackground(
1231 if (installFolder[1] ==
':') drive = StringTools::substring(installFolder, 0, 2) +
" && ";
1239 "\"" + installFolder +
"/" +
"\"" +
1240 " && start cmd /c \"update-finish.bat && del update-finish.bat\"").c_str()
1245 FileSystem::unsetFileSystem();
1246 Application::exit(0);
1248 if (node->
getId() ==
"button_browse") {
1249 class OnBrowseAction:
public virtual Action
1252 void performAction()
override {
1253 installer->popUps->getFileDialogScreenController()->close();
1254 auto pathToShow = installer->popUps->getFileDialogScreenController()->getPathName() +
"/" + installer->installerProperties.get(
"name",
"TDME2 based application");
1255 dynamic_cast<GUIElementNode*
>(installer->engine->getGUI()->getScreen(
"installer_folder")->getNodeById(
"install_folder"))->getController()->setValue(
MutableString(pathToShow));
1262 OnBrowseAction(
Installer* installer): installer(installer) {
1269 vector<string> extensions;
1271 pathToShow = StringTools::replace(pathToShow,
"\\",
"/");
1272 while (FileSystem::getStandardFileSystem()->exists(pathToShow) ==
false || FileSystem::getStandardFileSystem()->isPath(pathToShow) ==
false) {
1273 pathToShow = FileSystem::getStandardFileSystem()->getPathName(pathToShow);
1275 if (StringTools::startsWith(pathToShow,
"/") ==
false) pathToShow =
homeFolder;
1276 popUps->getFileDialogScreenController()->show(
1282 new OnBrowseAction(
this)
1285 if (node->
getId() ==
"button_uninstall") {
1290 if (node->
getId() ==
"button_update") {
1295 if (node->
getId() ==
"button_repair") {
1300 if (StringTools::startsWith(node->
getId(),
"component") ==
true) {
1301 auto componentIdx = Integer::parse(StringTools::substring(node->
getId(),
string(
"component").size()));
1327 Console::println(
"Installer::mountInstallerFileSystem(): timestamp: " + (
timestamp.empty() ==
false?
timestamp:
"no timestamp"));
1330 auto installerArchiveFileNameStart = Application::getOSName() +
"-" + Application::getCPUName() +
"-Installer-" + (
timestamp.empty() ==
false?
timestamp +
".ta":
"");
1331 string installerArchiveFileName;
1333 vector<string> files;
1334 FileSystem::getStandardFileSystem()->list(
"installer", files);
1335 for (
const auto& file: files) {
1336 if (StringTools::startsWith(file, installerArchiveFileNameStart) ==
true &&
1337 StringTools::endsWith(file,
".ta") ==
true) {
1338 Console::println(
"Installer::main(): Have installer tdme archive file: " + file);
1339 installerArchiveFileName = file;
1342 if (installerArchiveFileName.empty() ==
true) {
1343 Console::println(
"Installer::main(): No installer TDME archive found. Exiting.");
1344 Application::exit(1);
1347 auto installerFileSystem = make_unique<ArchiveFileSystem>(
"installer/" + installerArchiveFileName);
1348 if (installerFileSystem->computeSHA256Hash() != FileSystem::getStandardFileSystem()->getContentAsString(
"installer", installerArchiveFileName +
".sha256")) {
1349 throw ExceptionBase(
"Installer::main(): Failed to verify: " + installerArchiveFileName +
", get new installer and try again");
1351 Console::println(
"Installer::mountInstallerFileSystem(): unmounting");
1354 FileSystem::unsetFileSystem();
1356 if (lastInstallerArchiveFileName.empty() ==
false && lastInstallerArchiveFileName !=
"installer/" + installerArchiveFileName) {
1358 Console::println(
"Installer::mountInstallerFileSystem(): deleting installer tdme archive file: " + lastInstallerArchiveFileName);
1360 FileSystem::getStandardFileSystem()->removeFile(
1361 FileSystem::getStandardFileSystem()->getPathName(lastInstallerArchiveFileName),
1362 FileSystem::getStandardFileSystem()->getFileName(lastInstallerArchiveFileName)
1364 FileSystem::getStandardFileSystem()->removeFile(
1365 FileSystem::getStandardFileSystem()->getPathName(lastInstallerArchiveFileName),
1366 FileSystem::getStandardFileSystem()->getFileName(lastInstallerArchiveFileName) +
".sha256"
1369 Console::println(
string(
"Installer::mountInstallerFileSystem(): An error occurred: ") + exception.what());
1372 Console::println(
"Installer::mountInstallerFileSystem(): mounting: " + installerArchiveFileName);
1373 FileSystem::setupFileSystem(installerFileSystem.release());
1375 Console::println(
string(
"Installer::mountInstallerFileSystem(): ") + exception.what());
1376 Application::exit(1);
1382 Console::println(
string(
"Installer ") + Version::getVersion());
1383 Console::println(Version::getCopyright());
1386 Console::println(
"Usage: Installer");
1387 Application::exit(1);
1389 #if defined(__APPLE__)
1392 auto executablePathName = string(argv[0]);
1393 if (executablePathName.find(
".app/Contents/MacOS/") != string::npos) {
1394 auto appBundleName = StringTools::substring(executablePathName, 0, executablePathName.rfind(
".app") +
string(
".app").size());
1395 auto workingPathName = StringTools::substring(appBundleName, 0, appBundleName.rfind(
'/'));
1396 FileSystem::getStandardFileSystem()->changePath(workingPathName);
1401 return installer->run(argc, argv,
"Installer",
nullptr, Application::WINDOW_HINT_NOTRESIZEABLE);
1405 vector<string> files;
1406 archiveFileSystem->
list(pathName, files);
1407 for (
const auto& fileName: files) {
1408 if (archiveFileSystem->
isPath(pathName +
"/" + fileName) ==
false) {
1409 totalFiles.push_back((pathName.empty() ==
true?
"":pathName +
"/") + fileName);
1411 scanArchive(archiveFileSystem, totalFiles, pathName +
"/" + fileName);
1420 string pathCreating;
1424 pathCreating+= pathCreating.empty() ==
true?pathComponent:
"/" + pathComponent;
1426 pathCreating+=
"/" + pathComponent;
1428 if (FileSystem::getStandardFileSystem()->isDrive(pathCreating) ==
false && FileSystem::getStandardFileSystem()->exists(pathCreating) ==
false) {
1429 Console::println(
"Creating: " + pathCreating);
1430 FileSystem::getStandardFileSystem()->createPath(pathCreating);
1436 Application::cancelExit();
Application base class, please make sure to allocate application on heap to have correct application ...
int run(int argc, char **argv, const string &title, InputEventHandler *inputEventHandler=nullptr, int windowHints=WINDOW_HINT_NONE)
Run this application.
string executableFileName
void setInputEventHandler(InputEventHandler *inputEventHandler)
Set input event handler.
Color 4 definition class.
void reshape(int32_t width, int32_t height)
Reshape.
void display()
Renders the scene.
void initialize()
Initialize render engine.
void dispose()
Shutdown the engine.
void setSceneColor(const Color4 &sceneColor)
Set scene color.
void handleEvents(bool clearEvents=true)
Handle screen events.
void addRenderScreen(const string &screenId, int screenIdx=-1)
Add render screen.
GUIScreenNode * getScreen(const string &id)
Get screen.
void render()
Render GUIs.
void addScreen(const string &id, GUIScreenNode *screen)
Add screen.
void reset()
Removes all screens and caches.
void resetRenderScreens()
Reset render screens.
const string & getValue()
GUI node controller base class.
virtual const MutableString & getValue()=0
GUINodeController * getController()
GUI parent node base class thats supporting child nodes.
GUI screen node that represents a screen that can be rendered via GUI system.
void addChangeListener(GUIChangeListener *listener)
Add change listener.
void addActionListener(GUIActionListener *listener)
Add action listener.
GUINode * getNodeById(const string &nodeId)
Get GUI node by id.
void setPassword(const string &password)
Set password.
stringstream & getResponse()
void execute()
Execute HTTP request.
void setUsername(const string &username)
Set username.
void setMethod(const string &method)
Set method.
void setURL(const string &url)
Set URL.
void setPassword(const string &password)
Set password.
void start()
Starts the HTTP download to file.
void join()
Wait until underlying thread has finished.
void setUsername(const string &username)
Set username.
void reset()
Reset this HTTP client.
void setFile(const string &file)
Set file to download to.
void setURL(const string &url)
Set URL.
Archive file system implementation.
void list(const string &pathName, vector< string > &files, FileNameFilter *filter=nullptr, bool addDrives=false)
List files for given path and filter by a file name filter if not null.
bool isPath(const string &uri)
Check if file is a path.
File system singleton class.
void unlock()
Unlocks this mutex.
void lock()
Locks the mutex, additionally mutex locks will block until other locks have been unlocked.
Mutable utf8 aware string class.
bool equals(const string &s2) const
Equals.
const string & getString() const
Properties class, which helps out with storeing or loading key value pairs from/to property files.
const string & get(const string &key, const string &defaultValue) const
Get property value by key.
void load(const string &pathName, const string &fileName, FileSystemInterface *fileSystem=nullptr)
Load property file.
const string & nextToken()
void tokenize(const string &str, const string &delimiters, bool emptyTokens=false)
Tokenize.
std::exception Exception
Exception base class.
GUI action listener interface.
GUI change listener interface.