TDME2  1.9.200
archive-main.cpp
Go to the documentation of this file.
1 #include <cassert>
2 #include <filesystem>
3 #include <fstream>
4 #include <string>
5 #include <vector>
6 
7 #include <ext/zlib/zlib.h>
8 
9 #include <tdme/tdme.h>
11 #include <tdme/engine/Version.h>
15 #include <tdme/utilities/Console.h>
19 
20 using std::ofstream;
21 using std::string;
22 using std::to_string;
23 using std::vector;
24 
34 
35 namespace tdme {
36 namespace tools {
37 namespace cli {
38 namespace archive {
39  struct FileInformation {
40  string name;
41  uint64_t bytes;
42  uint8_t compressed;
43  uint64_t bytesCompressed;
44  uint64_t offset;
45  bool executable;
46  };
47 };
48 };
49 };
50 };
51 
53 
54 void scanPath(const string& path, vector<string>& totalFiles) {
55  class ListFilter : public virtual FileNameFilter {
56  public:
57  virtual ~ListFilter() {}
58 
59  bool accept(const string& pathName, const string& fileName) override {
60  if (fileName == ".") return false;
61  if (fileName == "..") return false;
62  if (FileSystem::getInstance()->isPath(pathName + "/" + fileName) == true) return true;
63  auto fileNameLowerCase = StringTools::toLowerCase(fileName);
64  // audio
65  if (StringTools::endsWith(fileNameLowerCase, ".ogg") == true) return true;
66  // fonts
67  if (StringTools::endsWith(fileNameLowerCase, ".ttf") == true) return true;
68  // images
69  if (StringTools::endsWith(fileNameLowerCase, ".icns") == true) return true;
70  if (StringTools::endsWith(fileNameLowerCase, ".ico") == true) return true;
71  if (StringTools::endsWith(fileNameLowerCase, ".png") == true) return true;
72  // videos
73  if (StringTools::endsWith(fileNameLowerCase, ".mpg") == true) return true;
74  // models
75  if (StringTools::endsWith(fileNameLowerCase, ".dae") == true) return true;
76  if (StringTools::endsWith(fileNameLowerCase, ".fbx") == true) return true;
77  if (StringTools::endsWith(fileNameLowerCase, ".glb") == true) return true;
78  if (StringTools::endsWith(fileNameLowerCase, ".tm") == true) return true;
79  // property files
80  if (StringTools::endsWith(fileNameLowerCase, ".properties") == true) return true;
81  // shader
82  if (StringTools::endsWith(fileNameLowerCase, ".cl") == true) return true;
83  if (StringTools::endsWith(fileNameLowerCase, ".frag") == true) return true;
84  if (StringTools::endsWith(fileNameLowerCase, ".glsl") == true) return true;
85  if (StringTools::endsWith(fileNameLowerCase, ".vert") == true) return true;
86  // tdme model
87  if (StringTools::endsWith(fileNameLowerCase, ".tmodel") == true) return true;
88  // tdme scene
89  if (StringTools::endsWith(fileNameLowerCase, ".tscene") == true) return true;
90  // tdme particle system
91  if (StringTools::endsWith(fileNameLowerCase, ".tparticle") == true) return true;
92  // tdme terrain
93  if (StringTools::endsWith(fileNameLowerCase, ".tterrain") == true) return true;
94  // tdme script
95  if (StringTools::endsWith(fileNameLowerCase, ".tscript") == true) return true;
96  // xml
97  if (StringTools::endsWith(fileNameLowerCase, ".xml") == true) return true;
98  // files without ending
99  if (fileName.rfind(".") == string::npos ||
100  (fileName.rfind("/") != string::npos &&
101  fileName.rfind(".") < fileName.rfind("/"))) {
102  return true;
103  }
104  //
105  return false;
106  }
107  };
108 
109  ListFilter listFilter;
110  vector<string> files;
111 
112  FileSystem::getInstance()->list(path, files, &listFilter);
113 
114  for (const auto& fileName: files) {
115  if (FileSystem::getInstance()->isPath(path + "/" + fileName) == false) {
116  totalFiles.push_back(path + "/" + fileName);
117  } else {
118  scanPath(path + "/" + fileName, totalFiles);
119  }
120  }
121 }
122 
123 void processFile(const string& fileName, vector<FileInformation>& fileInformations) {
124  Console::print("Processing file: " + fileName);
125 
126  // read content
127  vector<uint8_t> content;
128  FileSystem::getInstance()->getContent(
129  FileSystem::getInstance()->getPathName(fileName),
130  FileSystem::getInstance()->getFileName(fileName),
131  content
132  );
133 
134  // append to archive
135  ofstream ofs(std::filesystem::u8path("archive.ta"), ofstream::binary | ofstream::app);
136  ofs.seekp(0, ofstream::end);
137  uint64_t fileOffset = ofs.tellp();
138 
139  //
140  uint64_t bytesCompressed = 0;
141  uint8_t compressed = 1;
142 
143  // always use compression for now
144  if (compressed == 1) {
145  // see: https://www.zlib.net/zpipe.c
146 
147  #define CHUNK 16384
148 
149  int ret;
150  int flush;
151  size_t have;
152  z_stream strm;
153  unsigned char in[CHUNK];
154  unsigned char out[CHUNK];
155 
156  /* allocate deflate state */
157  strm.zalloc = Z_NULL;
158  strm.zfree = Z_NULL;
159  strm.opaque = Z_NULL;
160  ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
161  if (ret != Z_OK) {
162  Console::println("processFile(): Error compressing file: Aborting");
163  return;
164  }
165 
166  // compress until end of file
167  size_t inPosition = 0;
168  size_t inBytes = content.size();
169  do {
170  auto inStartPosition = inPosition;
171  for (size_t i = 0; i < CHUNK; i++) {
172  if (inPosition == inBytes) break;
173  in[i] = content[inPosition];
174  inPosition++;
175  }
176  strm.avail_in = inPosition - inStartPosition;
177  flush = inPosition == inBytes?Z_FINISH:Z_NO_FLUSH;
178  strm.next_in = in;
179 
180  // run deflate() on input until output buffer not full, finish compression if all of source has been read in
181  do {
182  strm.avail_out = CHUNK;
183  strm.next_out = out;
184  ret = deflate(&strm, flush); // no bad return value
185  assert(ret != Z_STREAM_ERROR); // state not clobbered
186  have = CHUNK - strm.avail_out;
187  ofs.write((char*)out, have);
188  bytesCompressed+= have;
189  } while (strm.avail_out == 0);
190  assert(strm.avail_in == 0); // all input will be used
191 
192  // done when last data in file processed
193  } while (flush != Z_FINISH);
194  assert(ret == Z_STREAM_END); // stream will be complete
195 
196  // clean up and return
197  (void) deflateEnd(&strm);
198  } else {
199  ofs.write((char*)content.data(), content.size());
200  }
201  ofs.close();
202 
203  // store file information
204  FileInformation fileInformation;
205  fileInformation.name = fileName;
206  fileInformation.bytes = content.size();
207  fileInformation.compressed = compressed;
208  fileInformation.bytesCompressed = bytesCompressed;
209  fileInformation.offset = fileOffset;
210  fileInformation.executable = false;
211  fileInformations.push_back(fileInformation);
212 
213  // done
214  Console::println(", processed " + to_string(content.size()) + " bytes" + (compressed == 1?", " + to_string(bytesCompressed) + " bytes compressed":""));
215 }
216 
217 int main(int argc, char** argv)
218 {
219  Console::println(string("archive ") + Version::getVersion());
220  Console::println(Version::getCopyright());
221  Console::println();
222 
223  if (argc != 1) {
224  Console::println("Usage: archive");
225  Application::exit(Application::EXITCODE_FAILURE);
226  }
227 
228  // scan files
229  Console::println("Scanning files");
230  vector<string> files;
231  scanPath("resources", files);
232  scanPath("shader", files);
233 
234  // processing
235  Console::println("Processing files");
236 
237  // reset archive
238  {
239  ofstream ofs(std::filesystem::u8path("archive.ta"), ofstream::binary | ofstream::trunc);
240  ofs.close();
241  }
242 
243  // add files to archive
244  vector<FileInformation> fileInformations;
245  for (const auto& fileName: files) {
246  processFile(fileName, fileInformations);
247  }
248 
249  // add file informations
250  {
251  ofstream ofs(std::filesystem::u8path("archive.ta"), ofstream::binary | ofstream::app);
252  ofs.seekp(0, ofstream::end);
253  uint32_t fileInformationOffsetEnd = 0LL;
254  uint64_t fileInformationOffset = ofs.tellp();
255  for (const auto& fileInformation: fileInformations) {
256  uint32_t nameSize = fileInformation.name.size();
257  ofs.write((char*)&nameSize, sizeof(nameSize));
258  for (auto i = 0; i < nameSize; i++) ofs.write(&fileInformation.name[i], 1);
259  ofs.write((char*)&fileInformation.bytes, sizeof(fileInformation.bytes));
260  ofs.write((char*)&fileInformation.compressed, sizeof(fileInformation.compressed));
261  ofs.write((char*)&fileInformation.bytesCompressed, sizeof(fileInformation.bytesCompressed));
262  ofs.write((char*)&fileInformation.offset, sizeof(fileInformation.offset));
263  ofs.write((char*)&fileInformation.executable, sizeof(fileInformation.executable));
264  }
265  ofs.write((char*)&fileInformationOffsetEnd, sizeof(fileInformationOffsetEnd));
266  ofs.write((char*)&fileInformationOffset, sizeof(fileInformationOffset));
267  ofs.close();
268  }
269 
270  //
271  Application::exit(Application::EXITCODE_SUCCESS);
272 }
void scanPath(const string &path, vector< string > &totalFiles)
#define CHUNK
int main(int argc, char **argv)
void processFile(const string &fileName, vector< FileInformation > &fileInformations)
Application base class, please make sure to allocate application on heap to have correct application ...
Definition: Application.h:41
File system singleton class.
Definition: FileSystem.h:17
Console class.
Definition: Console.h:29
String tokenizer class.
String tools class.
Definition: StringTools.h:22
std::exception Exception
Exception base class.
Definition: Exception.h:18
Definition: fwd-tdme.h:4
File system file name filter interface.
virtual bool accept(const string &path, const string &file)=0
Accept a file.