8 #include <ext/zlib/zlib.h>
24 using std::make_unique;
28 using std::unique_ptr;
66 virtual ~ListFilter() {}
68 bool accept(
const string& pathName,
const string& fileName)
override {
69 if (fileName ==
".")
return false;
70 if (fileName ==
"..")
return false;
71 if (FileSystem::getInstance()->isPath(pathName +
"/" + fileName) ==
true)
return true;
72 auto fileNameLowerCase = StringTools::toLowerCase(fileName);
74 if (StringTools::endsWith(fileNameLowerCase,
".ogg") ==
true)
return true;
76 if (StringTools::endsWith(fileNameLowerCase,
".ttf") ==
true)
return true;
78 if (StringTools::endsWith(fileNameLowerCase,
".icns") ==
true)
return true;
79 if (StringTools::endsWith(fileNameLowerCase,
".ico") ==
true)
return true;
80 if (StringTools::endsWith(fileNameLowerCase,
".png") ==
true)
return true;
82 if (StringTools::endsWith(fileNameLowerCase,
".mpg") ==
true)
return true;
84 if (StringTools::endsWith(fileNameLowerCase,
".dae") ==
true)
return true;
85 if (StringTools::endsWith(fileNameLowerCase,
".fbx") ==
true)
return true;
86 if (StringTools::endsWith(fileNameLowerCase,
".glb") ==
true)
return true;
87 if (StringTools::endsWith(fileNameLowerCase,
".tm") ==
true)
return true;
89 if (StringTools::endsWith(fileNameLowerCase,
".properties") ==
true)
return true;
91 if (StringTools::endsWith(fileNameLowerCase,
".cl") ==
true)
return true;
92 if (StringTools::endsWith(fileNameLowerCase,
".frag") ==
true)
return true;
93 if (StringTools::endsWith(fileNameLowerCase,
".glsl") ==
true)
return true;
94 if (StringTools::endsWith(fileNameLowerCase,
".vert") ==
true)
return true;
96 if (StringTools::endsWith(fileNameLowerCase,
".tempty") ==
true)
return true;
98 if (StringTools::endsWith(fileNameLowerCase,
".ttrigger") ==
true)
return true;
100 if (StringTools::endsWith(fileNameLowerCase,
".tenvmap") ==
true)
return true;
102 if (StringTools::endsWith(fileNameLowerCase,
".tmodel") ==
true)
return true;
104 if (StringTools::endsWith(fileNameLowerCase,
".tscene") ==
true)
return true;
106 if (StringTools::endsWith(fileNameLowerCase,
".tparticle") ==
true)
return true;
108 if (StringTools::endsWith(fileNameLowerCase,
".tterrain") ==
true)
return true;
110 if (StringTools::endsWith(fileNameLowerCase,
".tscript") ==
true)
return true;
112 if (StringTools::endsWith(fileNameLowerCase,
".xml") ==
true)
return true;
114 if (StringTools::endsWith(fileNameLowerCase,
".plist") ==
true)
return true;
116 if (StringTools::endsWith(fileNameLowerCase,
".md") ==
true)
return true;
118 if (fileNameLowerCase ==
"license")
return true;
120 if (fileName.rfind(
".") == string::npos ||
121 (fileName.rfind(
"/") != string::npos &&
122 fileName.rfind(
".") < fileName.rfind(
"/"))) {
130 ListFilter listFilter;
131 vector<string> files;
133 if (FileSystem::getInstance()->exists(path) ==
false) {
134 Console::println(
"Error: scanPathResources: file does not exist: " + path);
136 if (FileSystem::getInstance()->isPath(path) ==
false) {
137 if (listFilter.accept(
".", path) ==
true) {
138 totalFiles.push_back(path);
140 Console::println(
"Error: scanPathResources: file exist, but does not match filter: " + path);
143 FileSystem::getInstance()->list(path, files, &listFilter);
144 for (
const auto& fileName: files) {
145 if (FileSystem::getInstance()->isPath(path +
"/" + fileName) ==
false) {
146 totalFiles.push_back(path +
"/" + fileName);
157 virtual ~ListFilter() {}
159 bool accept(
const string& pathName,
const string& fileName)
override {
160 if (fileName ==
".")
return false;
161 if (fileName ==
"..")
return false;
163 if (FileSystem::getInstance()->isPath(pathName +
"/" + fileName) ==
true) {
165 #if defined(__APPLE__)
166 if (StringTools::endsWith(fileName,
".dSYM") ==
true)
return false;
171 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".a") ==
true)
return true;
174 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".dll") ==
true)
return true;
175 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".lib") ==
true)
return true;
176 #elif defined(__APPLE__)
177 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".dylib") ==
true)
return true;
178 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".so") ==
true)
return true;
180 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".so") ==
true)
return true;
187 ListFilter listFilter;
188 vector<string> files;
190 if (FileSystem::getInstance()->exists(path) ==
false) {
191 Console::println(
"Error: scanPathLibraries: file does not exist: " + path);
193 if (FileSystem::getInstance()->isPath(path) ==
false) {
194 if (listFilter.accept(
".", path) ==
true) {
195 totalFiles.push_back(path);
197 Console::println(
"Error: scanPathLibraries: file exist, but does not match filter: " + path);
200 FileSystem::getInstance()->list(path, files, &listFilter);
201 for (
const auto& fileName: files) {
202 if (FileSystem::getInstance()->isPath(path +
"/" + fileName) ==
false) {
203 totalFiles.push_back(path +
"/" + fileName);
214 virtual ~ListFilter() {}
216 bool accept(
const string& pathName,
const string& fileName)
override {
217 if (fileName ==
".")
return false;
218 if (fileName ==
"..")
return false;
219 if (FileSystem::getInstance()->isPath(pathName +
"/" + fileName) ==
true)
return true;
221 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".h") ==
true)
return true;
227 ListFilter listFilter;
228 vector<string> files;
230 if (FileSystem::getInstance()->exists(path) ==
false) {
231 Console::println(
"Error: scanPathHeaders: file does not exist: " + path);
233 if (FileSystem::getInstance()->isPath(path) ==
false) {
234 if (listFilter.accept(
".", path) ==
true) {
235 totalFiles.push_back(path);
237 Console::println(
"Error: scanPathHeaders: file exist, but does not match filter: " + path);
240 FileSystem::getInstance()->list(path, files, &listFilter);
241 for (
const auto& fileName: files) {
242 if (FileSystem::getInstance()->isPath(path +
"/" + fileName) ==
false) {
243 totalFiles.push_back(path +
"/" + fileName);
254 virtual ~ListFilter() {}
256 bool accept(
const string& pathName,
const string& fileName)
override {
257 if (fileName ==
".")
return false;
258 if (fileName ==
"..")
return false;
260 if (FileSystem::getInstance()->isPath(pathName +
"/" + fileName) ==
true) {
262 #if defined(__APPLE__)
263 if (StringTools::endsWith(fileName,
".dSYM") ==
true)
return false;
269 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".exe") ==
true)
return true;
270 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".dll") ==
true)
return true;
271 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".bat") ==
true)
return true;
272 #elif defined(__APPLE__)
274 if (fileName.rfind(
".") == string::npos ||
275 (fileName.rfind(
"/") != string::npos &&
276 fileName.rfind(
".") < fileName.rfind(
"/"))) {
279 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".dylib") ==
true)
return true;
280 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".so") ==
true)
return true;
281 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".sh") ==
true)
return true;
284 if (fileName.rfind(
".") == string::npos ||
285 (fileName.rfind(
"/") != string::npos &&
286 fileName.rfind(
".") < fileName.rfind(
"/"))) {
289 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".so") ==
true)
return true;
290 if (StringTools::endsWith(StringTools::toLowerCase(fileName),
".sh") ==
true)
return true;
297 ListFilter listFilter;
298 vector<string> files;
300 if (FileSystem::getInstance()->exists(path) ==
false) {
301 Console::println(
"Error: scanPathExecutables: file does not exist: " + path);
303 if (FileSystem::getInstance()->isPath(path) ==
false) {
304 if (listFilter.accept(
".", path) ==
true) {
305 totalFiles.push_back(path);
307 Console::println(
"Error: scanPathExecutables: file exist, but does not match filter: " + path);
310 FileSystem::getInstance()->list(path, files, &listFilter);
311 for (
const auto& fileName: files) {
312 if (FileSystem::getInstance()->isPath(path +
"/" + fileName) ==
false) {
313 totalFiles.push_back(path +
"/" + fileName);
321 void processFile(
const string& fileName, vector<FileInformation>& fileInformations,
const string& archiveFileName,
bool executableFile,
const string& basePath,
const string& executablePath =
string()) {
323 vector<uint8_t> content;
324 FileSystem::getInstance()->getContent(
325 FileSystem::getInstance()->getPathName(fileName),
326 FileSystem::getInstance()->getFileName(fileName),
330 auto fileNameToUse = StringTools::startsWith(fileName, basePath +
"/") ==
true?StringTools::substring(fileName, (basePath +
"/").size(), fileName.size()):fileName;
332 if (executableFile ==
true && fileName.find_last_of(
'/') != string::npos) {
333 fileNameToUse = (executablePath.empty() ==
false?executablePath +
"/":
"") + StringTools::substring(fileNameToUse, fileNameToUse.find_last_of(
'/') + 1);
336 Console::print(archiveFileName +
": Processing file: " + fileNameToUse);
339 ofstream ofs(std::filesystem::u8path(archiveFileName), ofstream::binary | ofstream::app);
340 ofs.seekp(0, ofstream::end);
341 uint64_t fileOffset = ofs.tellp();
344 uint64_t bytesCompressed = 0;
345 uint8_t compressed = 1;
348 if (compressed == 1) {
356 unsigned char in[
CHUNK];
357 unsigned char out[
CHUNK];
360 strm.zalloc = Z_NULL;
362 strm.opaque = Z_NULL;
363 ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
365 Console::println(
"processFile(): Error compressing file: Aborting");
370 size_t inPosition = 0;
371 size_t inBytes = content.size();
373 auto inStartPosition = inPosition;
374 for (
size_t i = 0; i <
CHUNK; i++) {
375 if (inPosition == inBytes)
break;
376 in[i] = content[inPosition];
379 strm.avail_in = inPosition - inStartPosition;
380 flush = inPosition == inBytes?Z_FINISH:Z_NO_FLUSH;
385 strm.avail_out =
CHUNK;
387 ret = deflate(&strm, flush);
388 assert(ret != Z_STREAM_ERROR);
389 have =
CHUNK - strm.avail_out;
390 ofs.write((
char*)out, have);
391 bytesCompressed+= have;
392 }
while (strm.avail_out == 0);
393 assert(strm.avail_in == 0);
396 }
while (flush != Z_FINISH);
397 assert(ret == Z_STREAM_END);
400 (void)deflateEnd(&strm);
402 ofs.write((
char*)content.data(), content.size());
408 fileInformation.
name = fileNameToUse;
409 fileInformation.
bytes = content.size();
412 fileInformation.
offset = fileOffset;
414 fileInformations.push_back(fileInformation);
417 Console::println(
", processed " + to_string(content.size()) +
" bytes" + (compressed == 1?
", " + to_string(bytesCompressed) +
" bytes compressed":
""));
420 #if defined(__APPLE__)
421 void createMacApplication(
const Properties& installerProperties,
const string& fileName,
const string& pathName =
string()) {
422 auto _pathName = pathName.empty() ==
true?
"":pathName +
"/";
423 auto _fileName = StringTools::substring(fileName, fileName.rfind(
'/') + 1, fileName.size());
424 auto _filePath = StringTools::substring(fileName, 0, fileName.rfind(
'/'));
425 auto startMenuName = installerProperties.
get(
"startmenu_" + StringTools::toLowerCase(_fileName),
"");
426 if (startMenuName.empty() ==
false) {
427 auto executablePathName = FileSystem::getInstance()->getPathName(fileName);
428 auto executableFileName = FileSystem::getInstance()->getFileName(fileName);
429 auto iconFileName = StringTools::toLowerCase(executableFileName) +
"-icon.icns";
430 if (FileSystem::getInstance()->exists(
"resources/platforms/macos/" + iconFileName) ==
false &&
431 FileSystem::getInstance()->exists(executablePathName +
"/resources/platforms/macos/" + iconFileName) ==
false) iconFileName =
"default-icon.icns";
432 auto infoplistFile = FileSystem::getInstance()->getContentAsString(
"resources/platforms/macos",
"Info.plist");
433 infoplistFile = StringTools::replace(infoplistFile,
"{__EXECUTABLE__}", executableFileName);
434 infoplistFile = StringTools::replace(infoplistFile,
"{__EXECUTABLE_LOWERCASE__}", StringTools::toLowerCase(executableFileName));
435 infoplistFile = StringTools::replace(infoplistFile,
"{__ICON__}",
"icon.icns");
436 infoplistFile = StringTools::replace(infoplistFile,
"{__COPYRIGHT__}", Version::getCopyright());
437 infoplistFile = StringTools::replace(infoplistFile,
"{__VERSION__}", Version::getVersion());
438 FileSystem::getInstance()->createPath(_pathName + executableFileName +
".app");
439 FileSystem::getInstance()->createPath(_pathName + executableFileName +
".app/Contents");
440 FileSystem::getInstance()->createPath(_pathName + executableFileName +
".app/Contents/MacOS");
441 FileSystem::getInstance()->createPath(_pathName + executableFileName +
".app/Contents/Resources");
442 FileSystem::getStandardFileSystem()->setContentFromString(
443 _pathName + executableFileName +
".app/Contents",
448 vector<uint8_t> content;
449 FileSystem::getInstance()->getContent(
450 "resources/platforms/macos",
454 FileSystem::getStandardFileSystem()->setContent(
455 _pathName + executableFileName +
".app/Contents/Resources",
461 vector<uint8_t> content;
462 FileSystem::getInstance()->getContent(
467 FileSystem::getInstance()->setContent(
468 _pathName + executableFileName +
".app/Contents/MacOS",
472 FileSystem::getInstance()->setExecutable(
473 _pathName + executableFileName +
".app/Contents/MacOS",
477 auto codeSignCommand =
"codesign -s \"" + installerProperties.
get(
"macos_codesign_identity",
"No identity") +
"\" \"" + _pathName + executableFileName +
".app\"";
478 Console::println(
"Signing '" + fileName +
"': " + codeSignCommand);
479 Console::println(Application::execute(codeSignCommand));
484 int main(
int argc,
char** argv)
489 Console::println(
string(
"create-installer ") + Version::getVersion());
490 Console::println(Version::getCopyright());
494 string tdmePath =
"../tdme2";
495 auto cpu = Application::getCPUName();
496 auto os = Application::getOSName();
497 auto fileNameTime = StringTools::replace(StringTools::replace(StringTools::replace(Time::getAsString(),
" ",
"-" ),
":",
""),
"-",
"");
501 installerProperties.
load(
"resources/installer",
"installer.properties");
502 for (
auto componentIdx = 0;
true; componentIdx++) {
503 auto componentId = componentIdx == 0?
"installer":
"component" + to_string(componentIdx);
504 auto componentName = installerProperties.
get(componentId,
"");
505 if (componentName.empty() ==
true)
break;
506 Console::println(
"Having component: " + to_string(componentIdx) +
": " + componentName);
507 auto componentInclude = installerProperties.
get(componentId +
"_include",
"");
508 if (componentInclude.empty() ==
true) {
509 Console::println(
"component: " + to_string(componentIdx) +
": missing includes. Skipping.");
514 auto componentFileName = os +
"-" + cpu +
"-" + StringTools::replace(StringTools::replace(componentName,
" - ",
"-"),
" ",
"-") +
"-" + fileNameTime +
".ta";
516 Console::println(
"Component: " + to_string(componentIdx) +
": component file name: " + componentFileName);
518 if (FileSystem::getInstance()->exists(
"installer") ==
false) {
519 FileSystem::getInstance()->createPath(
"installer");
524 ofstream ofs(std::filesystem::u8path(
"installer/" + componentFileName), ofstream::binary | ofstream::trunc);
529 vector<FileInformation> fileInformations;
530 vector<string> filesData;
531 vector<string> filesBin;
538 auto componentIncludeDefinition = t.
nextToken();
539 t2.
tokenize(componentIncludeDefinition,
":");
542 if (type.empty() ==
true || file.empty() ==
true) {
543 Console::println(
"Component: " + to_string(componentIdx) +
": type or file empty. Skipping");
546 Console::println(
"Component: " + to_string(componentIdx) +
": type = " + type +
"; file = " + file);
551 t.
tokenize(installerProperties.
get(
"exe_path",
""),
",");
556 if (type ==
"!exe") {
558 t.
tokenize(installerProperties.
get(
"exe_path",
""),
",");
566 if (type ==
"!res") {
575 Console::println(
"Component: " + to_string(componentIdx) +
": type = " + type +
" unsupported!");
579 Console::println(
"Component: " + to_string(componentIdx) +
": Processing files");
583 for (
const auto& fileName: filesData) {
584 processFile(fileName, fileInformations,
"installer/" + componentFileName,
false, tdmePath);
588 for (
const auto& fileName: filesBin) {
589 #if defined(__APPLE__)
590 auto _fileName = StringTools::substring(fileName, fileName.rfind(
'/') + 1, fileName.size());
591 auto _filePath = StringTools::substring(fileName, 0, fileName.rfind(
'/'));
592 auto startMenuName = installerProperties.
get(
"startmenu_" + StringTools::toLowerCase(_fileName),
"");
593 if (startMenuName.empty() ==
false) {
594 createMacApplication(installerProperties, fileName);
595 auto executableFileName = FileSystem::getInstance()->getFileName(fileName);
596 processFile(executableFileName +
".app/Contents/Info.plist", fileInformations,
"installer/" + componentFileName,
false, tdmePath);
597 processFile(executableFileName +
".app/Contents/Resources/icon.icns", fileInformations,
"installer/" + componentFileName,
false, tdmePath);
598 processFile(executableFileName +
".app/Contents/_CodeSignature/CodeResources", fileInformations,
"installer/" + componentFileName,
false, tdmePath);
599 processFile(executableFileName +
".app/Contents/MacOS/" + executableFileName, fileInformations,
"installer/" + componentFileName,
true, tdmePath, executableFileName +
".app/Contents/MacOS");
600 FileSystem::getInstance()->removePath(executableFileName +
".app",
true);
602 auto executablePathName = FileSystem::getInstance()->getPathName(fileName);
603 auto executableFileName = FileSystem::getInstance()->getFileName(fileName);
604 FileSystem::getInstance()->createPath(
"signed-executables");
606 vector<uint8_t> content;
607 FileSystem::getInstance()->getContent(
612 FileSystem::getInstance()->setContent(
613 "signed-executables",
617 FileSystem::getInstance()->setExecutable(
618 "signed-executables",
622 auto signedFileName =
"signed-executables/" + executableFileName;
623 auto codeSignCommand =
"codesign -s \"" + installerProperties.
get(
"macos_codesign_identity",
"No identity") +
"\" \"" + signedFileName +
"\"";
624 Console::println(
"Signing '" + fileName +
"': " + codeSignCommand);
625 Console::println(Application::execute(codeSignCommand));
626 processFile(signedFileName, fileInformations,
"installer/" + componentFileName,
true, tdmePath);
627 FileSystem::getInstance()->removePath(
"signed-executables",
true);
630 processFile(fileName, fileInformations,
"installer/" + componentFileName,
true, tdmePath);
636 ofstream ofs(std::filesystem::u8path(
"installer/" + componentFileName), ofstream::binary | ofstream::app);
637 ofs.seekp(0, ofstream::end);
638 uint32_t fileInformationOffsetEnd = 0LL;
639 uint64_t fileInformationOffset = ofs.tellp();
640 for (
const auto& fileInformation: fileInformations) {
641 uint32_t nameSize = fileInformation.name.size();
642 ofs.write((
char*)&nameSize,
sizeof(nameSize));
643 for (
auto i = 0; i < nameSize; i++) ofs.write(&fileInformation.name[i], 1);
644 ofs.write((
char*)&fileInformation.bytes,
sizeof(fileInformation.bytes));
645 ofs.write((
char*)&fileInformation.compressed,
sizeof(fileInformation.compressed));
646 ofs.write((
char*)&fileInformation.bytesCompressed,
sizeof(fileInformation.bytesCompressed));
647 ofs.write((
char*)&fileInformation.offset,
sizeof(fileInformation.offset));
648 ofs.write((
char*)&fileInformation.executable,
sizeof(fileInformation.executable));
650 ofs.write((
char*)&fileInformationOffsetEnd,
sizeof(fileInformationOffsetEnd));
651 ofs.write((
char*)&fileInformationOffset,
sizeof(fileInformationOffset));
656 auto archiveFileSystem = make_unique<ArchiveFileSystem>(
"installer/" + componentFileName);
657 auto archiveHash = archiveFileSystem->computeSHA256Hash();
658 FileSystem::getStandardFileSystem()->setContentFromString(
"installer", componentFileName +
".sha256", archiveHash);
659 Console::println(
"Component: " + to_string(componentIdx) +
": component file name: " + componentFileName +
": hash: " + archiveHash);
663 auto completionFileName = os +
"-" + cpu +
"-upload-" + fileNameTime;
666 ofstream ofs(std::filesystem::u8path(
"installer/" + completionFileName), ofstream::binary | ofstream::trunc);
671 #if defined(__APPLE__)
672 if (FileSystem::getInstance()->exists(
"installer-package") ==
true &&
673 FileSystem::getInstance()->isPath(
"installer-package") ==
true) {
674 FileSystem::getInstance()->removePath(
"installer-package",
true);
676 FileSystem::getInstance()->createPath(
"installer-package");
677 createMacApplication(installerProperties,
"bin/tdme/tools/installer/Installer",
"installer-package");
681 Application::exit(Application::EXITCODE_SUCCESS);
Application base class, please make sure to allocate application on heap to have correct application ...
Archive file system implementation.
File system singleton class.
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.
void processFile(const string &fileName, vector< FileInformation > &fileInformations, const string &archiveFileName, bool executableFile, const string &basePath, const string &executablePath=string())
int main(int argc, char **argv)
static void scanPathResources(const string &path, vector< string > &totalFiles)
static void scanPathHeaders(const string &path, vector< string > &totalFiles)
static void scanPathLibraries(const string &path, vector< string > &totalFiles)
static void scanPathExecutables(const string &path, vector< string > &totalFiles)
std::exception Exception
Exception base class.
File system file name filter interface.
virtual bool accept(const string &path, const string &file)=0
Accept a file.