data_compiler.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155
  1. /*
  2. * Copyright (c) 2012-2018 Daniele Bartolini and individual contributors.
  3. * License: https://github.com/dbartolini/crown/blob/master/LICENSE
  4. */
  5. #include "config.h"
  6. #include "core/containers/hash_map.h"
  7. #include "core/containers/hash_set.h"
  8. #include "core/containers/vector.h"
  9. #include "core/filesystem/file.h"
  10. #include "core/filesystem/filesystem_disk.h"
  11. #include "core/filesystem/path.h"
  12. #include "core/json/json_object.h"
  13. #include "core/json/sjson.h"
  14. #include "core/memory/allocator.h"
  15. #include "core/memory/temp_allocator.h"
  16. #include "core/os.h"
  17. #include "core/strings/dynamic_string.h"
  18. #include "core/strings/string_stream.h"
  19. #include "core/time.h"
  20. #include "device/console_server.h"
  21. #include "device/device_options.h"
  22. #include "device/log.h"
  23. #include "resource/compile_options.h"
  24. #include "resource/config_resource.h"
  25. #include "resource/data_compiler.h"
  26. #include "resource/font_resource.h"
  27. #include "resource/level_resource.h"
  28. #include "resource/lua_resource.h"
  29. #include "resource/material_resource.h"
  30. #include "resource/mesh_resource.h"
  31. #include "resource/package_resource.h"
  32. #include "resource/physics_resource.h"
  33. #include "resource/shader_resource.h"
  34. #include "resource/sound_resource.h"
  35. #include "resource/sprite_resource.h"
  36. #include "resource/state_machine_resource.h"
  37. #include "resource/texture_resource.h"
  38. #include "resource/types.h"
  39. #include "resource/unit_resource.h"
  40. #include <algorithm>
  41. #include <inttypes.h>
  42. #if CROWN_PLATFORM_POSIX
  43. #include <signal.h>
  44. #endif
  45. LOG_SYSTEM(DATA_COMPILER, "data_compiler")
  46. #define CROWN_DATA_VERSIONS "data_versions.sjson"
  47. #define CROWN_DATA_INDEX "data_index.sjson"
  48. #define CROWN_DATA_MTIMES "data_mtimes.sjson"
  49. #define CROWN_DATA_DEPENDENCIES "data_dependencies.sjson"
  50. namespace crown
  51. {
  52. static volatile bool _quit = false;
  53. static void notify_add_file(const char* path)
  54. {
  55. TempAllocator512 ta;
  56. StringStream ss(ta);
  57. ss << "{\"type\":\"add_file\",\"path\":\"" << path << "\"}";
  58. console_server()->send(string_stream::c_str(ss));
  59. }
  60. static void notify_remove_file(const char* path)
  61. {
  62. TempAllocator512 ta;
  63. StringStream ss(ta);
  64. ss << "{\"type\":\"remove_file\",\"path\":\"" << path << "\"}";
  65. console_server()->send(string_stream::c_str(ss));
  66. }
  67. static void notify_add_tree(const char* path)
  68. {
  69. TempAllocator512 ta;
  70. StringStream ss(ta);
  71. ss << "{\"type\":\"add_tree\",\"path\":\"" << path << "\"}";
  72. console_server()->send(string_stream::c_str(ss));
  73. }
  74. static void notify_remove_tree(const char* path)
  75. {
  76. TempAllocator512 ta;
  77. StringStream ss(ta);
  78. ss << "{\"type\":\"remove_tree\",\"path\":\"" << path << "\"}";
  79. console_server()->send(string_stream::c_str(ss));
  80. }
  81. SourceIndex::SourceIndex()
  82. : _paths(default_allocator())
  83. {
  84. }
  85. void SourceIndex::scan_directory(FilesystemDisk& fs, const char* prefix, const char* directory)
  86. {
  87. Vector<DynamicString> files(default_allocator());
  88. fs.list_files(directory != NULL ? directory : "", files);
  89. for (u32 i = 0; i < vector::size(files); ++i)
  90. {
  91. TempAllocator512 ta;
  92. DynamicString file_i(ta);
  93. if (directory != NULL)
  94. {
  95. file_i += directory;
  96. file_i += '/';
  97. }
  98. file_i += files[i];
  99. if (fs.is_directory(file_i.c_str()))
  100. {
  101. scan_directory(fs, prefix, file_i.c_str());
  102. }
  103. else // Assume a regular file
  104. {
  105. DynamicString resource_name(ta);
  106. if (strcmp(prefix, "") != 0)
  107. {
  108. resource_name += prefix;
  109. resource_name += '/';
  110. }
  111. resource_name += file_i;
  112. Stat stat;
  113. stat = fs.stat(file_i.c_str());
  114. hash_map::set(_paths, resource_name, stat);
  115. notify_add_file(resource_name.c_str());
  116. }
  117. }
  118. }
  119. void SourceIndex::scan(const HashMap<DynamicString, DynamicString>& source_dirs)
  120. {
  121. auto cur = hash_map::begin(source_dirs);
  122. auto end = hash_map::end(source_dirs);
  123. for (; cur != end; ++cur)
  124. {
  125. HASH_MAP_SKIP_HOLE(source_dirs, cur);
  126. DynamicString prefix(default_allocator());
  127. path::join(prefix, cur->second.c_str(), cur->first.c_str());
  128. FilesystemDisk fs(default_allocator());
  129. fs.set_prefix(prefix.c_str());
  130. scan_directory(fs, cur->first.c_str(), NULL);
  131. }
  132. }
  133. struct LineReader
  134. {
  135. const char* _str;
  136. const u32 _len;
  137. u32 _pos;
  138. LineReader(const char* str)
  139. : _str(str)
  140. , _len(strlen32(str))
  141. , _pos(0)
  142. {
  143. }
  144. void read_line(DynamicString& line)
  145. {
  146. const char* s = &_str[_pos];
  147. const char* nl = strnl(s);
  148. _pos += u32(nl - s);
  149. line.set(s, u32(nl - s));
  150. }
  151. bool eof()
  152. {
  153. return _str[_pos] == '\0';
  154. }
  155. };
  156. static void console_command_compile(ConsoleServer& cs, TCPSocket client, const char* json, void* user_data)
  157. {
  158. TempAllocator4096 ta;
  159. JsonObject obj(ta);
  160. DynamicString id(ta);
  161. DynamicString data_dir(ta);
  162. DynamicString platform(ta);
  163. sjson::parse(json, obj);
  164. sjson::parse_string(obj["id"], id);
  165. sjson::parse_string(obj["data_dir"], data_dir);
  166. sjson::parse_string(obj["platform"], platform);
  167. {
  168. TempAllocator512 ta;
  169. StringStream ss(ta);
  170. ss << "{\"type\":\"compile\",\"id\":\"" << id.c_str() << "\",\"start\":true}";
  171. cs.send(client, string_stream::c_str(ss));
  172. }
  173. bool succ = ((DataCompiler*)user_data)->compile(data_dir.c_str(), platform.c_str());
  174. {
  175. TempAllocator512 ta;
  176. StringStream ss(ta);
  177. ss << "{\"type\":\"compile\",\"id\":\"" << id.c_str() << "\",\"success\":" << (succ ? "true" : "false") << "}";
  178. cs.send(client, string_stream::c_str(ss));
  179. }
  180. }
  181. static void console_command_quit(ConsoleServer& /*cs*/, TCPSocket /*client*/, const char* /*json*/, void* /*user_data*/)
  182. {
  183. _quit = true;
  184. }
  185. static Buffer read(FilesystemDisk& data_fs, const char* filename)
  186. {
  187. Buffer buffer(default_allocator());
  188. // FIXME: better return NULL in Filesystem::open().
  189. if (data_fs.exists(filename))
  190. {
  191. File* file = data_fs.open(filename, FileOpenMode::READ);
  192. if (file)
  193. {
  194. u32 size = file->size();
  195. if (size == 0)
  196. {
  197. data_fs.close(*file);
  198. return buffer;
  199. }
  200. array::resize(buffer, size);
  201. file->read(array::begin(buffer), size);
  202. data_fs.close(*file);
  203. }
  204. }
  205. return buffer;
  206. }
  207. static void read_data_versions(HashMap<DynamicString, u32>& versions, FilesystemDisk& data_fs, const char* filename)
  208. {
  209. Buffer json = read(data_fs, filename);
  210. TempAllocator512 ta;
  211. JsonObject object(ta);
  212. sjson::parse(json, object);
  213. auto cur = json_object::begin(object);
  214. auto end = json_object::end(object);
  215. for (; cur != end; ++cur)
  216. {
  217. JSON_OBJECT_SKIP_HOLE(object, cur);
  218. TempAllocator256 ta;
  219. DynamicString type(ta);
  220. type.set(cur->first.data(), cur->first.length());
  221. hash_map::set(versions, type, (u32)sjson::parse_int(cur->second));
  222. }
  223. }
  224. static void read_data_index(HashMap<StringId64, DynamicString>& index, FilesystemDisk& data_fs, const char* filename)
  225. {
  226. Buffer json = read(data_fs, filename);
  227. TempAllocator512 ta;
  228. JsonObject object(ta);
  229. sjson::parse(json, object);
  230. auto cur = json_object::begin(object);
  231. auto end = json_object::end(object);
  232. for (; cur != end; ++cur)
  233. {
  234. JSON_OBJECT_SKIP_HOLE(object, cur);
  235. TempAllocator256 ta;
  236. StringId64 dst_name;
  237. DynamicString src_path(ta);
  238. dst_name.parse(cur->first.data());
  239. sjson::parse_string(cur->second, src_path);
  240. hash_map::set(index, dst_name, src_path);
  241. }
  242. }
  243. static void read_data_mtimes(HashMap<StringId64, u64>& mtimes, FilesystemDisk& data_fs, const char* filename)
  244. {
  245. Buffer json = read(data_fs, filename);
  246. TempAllocator128 ta;
  247. JsonObject object(ta);
  248. sjson::parse(json, object);
  249. auto cur = json_object::begin(object);
  250. auto end = json_object::end(object);
  251. for (; cur != end; ++cur)
  252. {
  253. JSON_OBJECT_SKIP_HOLE(object, cur);
  254. TempAllocator64 ta;
  255. StringId64 dst_name;
  256. DynamicString mtime_json(ta);
  257. dst_name.parse(cur->first.data());
  258. sjson::parse_string(cur->second, mtime_json);
  259. u64 mtime;
  260. sscanf(mtime_json.c_str(), "%" SCNu64, &mtime);
  261. hash_map::set(mtimes, dst_name, mtime);
  262. }
  263. }
  264. static void read_data_dependencies(DataCompiler& dc, FilesystemDisk& data_fs, const char* filename)
  265. {
  266. Buffer json = read(data_fs, filename);
  267. TempAllocator1024 ta;
  268. JsonObject object(ta);
  269. sjson::parse(json, object);
  270. auto cur = json_object::begin(object);
  271. auto end = json_object::end(object);
  272. for (; cur != end; ++cur)
  273. {
  274. JSON_OBJECT_SKIP_HOLE(object, cur);
  275. StringId64 dst_name;
  276. dst_name.parse(cur->first.data());
  277. JsonArray dependency_array(ta);
  278. sjson::parse_array(cur->second, dependency_array);
  279. for (u32 i = 0; i < array::size(dependency_array); ++i)
  280. {
  281. DynamicString src_path(ta);
  282. sjson::parse_string(dependency_array[i], src_path);
  283. dc.add_dependency(dst_name, src_path.c_str());
  284. }
  285. }
  286. }
  287. static void write_data_index(FilesystemDisk& data_fs, const char* filename, const HashMap<StringId64, DynamicString>& index)
  288. {
  289. StringStream ss(default_allocator());
  290. File* file = data_fs.open(filename, FileOpenMode::WRITE);
  291. if (file)
  292. {
  293. auto cur = hash_map::begin(index);
  294. auto end = hash_map::end(index);
  295. for (; cur != end; ++cur)
  296. {
  297. HASH_MAP_SKIP_HOLE(index, cur);
  298. TempAllocator256 ta;
  299. DynamicString str(ta);
  300. str.from_string_id(cur->first);
  301. ss << "\"" << str.c_str() << "\" = \"" << cur->second.c_str() << "\"\n";
  302. }
  303. file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  304. data_fs.close(*file);
  305. }
  306. }
  307. static void write_data_versions(FilesystemDisk& data_fs, const char* filename, const HashMap<DynamicString, u32>& versions)
  308. {
  309. StringStream ss(default_allocator());
  310. File* file = data_fs.open(filename, FileOpenMode::WRITE);
  311. if (file)
  312. {
  313. auto cur = hash_map::begin(versions);
  314. auto end = hash_map::end(versions);
  315. for (; cur != end; ++cur)
  316. {
  317. HASH_MAP_SKIP_HOLE(versions, cur);
  318. ss << cur->first.c_str() << " = " << cur->second << "\n";
  319. }
  320. file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  321. data_fs.close(*file);
  322. }
  323. }
  324. static void write_data_mtimes(FilesystemDisk& data_fs, const char* filename, const HashMap<StringId64, u64>& mtimes)
  325. {
  326. StringStream ss(default_allocator());
  327. File* file = data_fs.open(filename, FileOpenMode::WRITE);
  328. if (file)
  329. {
  330. auto cur = hash_map::begin(mtimes);
  331. auto end = hash_map::end(mtimes);
  332. for (; cur != end; ++cur)
  333. {
  334. HASH_MAP_SKIP_HOLE(mtimes, cur);
  335. TempAllocator64 ta;
  336. DynamicString key(ta);
  337. key.from_string_id(cur->first);
  338. ss << "\"" << key.c_str() << "\" = \"" << cur->second << "\"\n";
  339. }
  340. file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  341. data_fs.close(*file);
  342. }
  343. }
  344. static void write_source_files(StringStream& ss, HashMap<DynamicString, u32> sources)
  345. {
  346. auto cur = hash_map::begin(sources);
  347. auto end = hash_map::end(sources);
  348. for (; cur != end; ++cur)
  349. {
  350. HASH_MAP_SKIP_HOLE(sources, cur);
  351. ss << " \"" << cur->first.c_str() << "\"\n";
  352. }
  353. }
  354. static void write_data_dependencies(FilesystemDisk& data_fs, const char* filename, const HashMap<StringId64, HashMap<DynamicString, u32> >& dependencies)
  355. {
  356. StringStream ss(default_allocator());
  357. File* file = data_fs.open(filename, FileOpenMode::WRITE);
  358. if (file)
  359. {
  360. auto cur = hash_map::begin(dependencies);
  361. auto end = hash_map::end(dependencies);
  362. for (; cur != end; ++cur)
  363. {
  364. HASH_MAP_SKIP_HOLE(dependencies, cur);
  365. TempAllocator64 ta;
  366. DynamicString key(ta);
  367. key.from_string_id(cur->first);
  368. ss << "\"" << key.c_str() << "\" = [\n";
  369. write_source_files(ss, cur->second);
  370. ss << "]\n";
  371. }
  372. file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  373. data_fs.close(*file);
  374. }
  375. }
  376. DataCompiler::DataCompiler(ConsoleServer& cs)
  377. : _console_server(&cs)
  378. , _source_fs(default_allocator())
  379. , _source_dirs(default_allocator())
  380. , _compilers(default_allocator())
  381. , _globs(default_allocator())
  382. , _data_index(default_allocator())
  383. , _data_mtimes(default_allocator())
  384. , _data_dependencies(default_allocator())
  385. , _data_versions(default_allocator())
  386. , _file_monitor(default_allocator())
  387. {
  388. cs.register_command("compile", console_command_compile, this);
  389. cs.register_command("quit", console_command_quit, this);
  390. }
  391. DataCompiler::~DataCompiler()
  392. {
  393. _file_monitor.stop();
  394. }
  395. void DataCompiler::add_file(const char* path)
  396. {
  397. // Get source directory prefix
  398. TempAllocator512 ta;
  399. DynamicString source_dir(ta);
  400. source_dir = hash_map::get(_source_dirs, source_dir, source_dir);
  401. // Convert to DynamicString
  402. DynamicString str(ta);
  403. str.set(path, strlen32(path));
  404. // Get file status
  405. FilesystemDisk fs(default_allocator());
  406. fs.set_prefix(source_dir.c_str());
  407. Stat stat;
  408. stat = fs.stat(path);
  409. hash_map::set(_source_index._paths, str, stat);
  410. notify_add_file(path);
  411. }
  412. void DataCompiler::remove_file(const char* path)
  413. {
  414. // Convert to DynamicString
  415. TempAllocator512 ta;
  416. DynamicString str(ta);
  417. str.set(path, strlen32(path));
  418. hash_map::remove(_source_index._paths, str);
  419. notify_remove_file(path);
  420. }
  421. void DataCompiler::add_tree(const char* path)
  422. {
  423. #if CROWN_PLATFORM_LINUX
  424. // Get source directory prefix
  425. TempAllocator512 ta;
  426. DynamicString source_dir(ta);
  427. source_dir = hash_map::get(_source_dirs, source_dir, source_dir);
  428. // Scan the directory tree.
  429. // See file_monitor_linux.cpp:180.
  430. FilesystemDisk fs(default_allocator());
  431. fs.set_prefix(source_dir.c_str());
  432. _source_index.scan_directory(fs, "", path);
  433. #endif // CROWN_PLATFORM_LINUX
  434. notify_add_tree(path);
  435. }
  436. void DataCompiler::remove_tree(const char* path)
  437. {
  438. notify_remove_tree(path);
  439. }
  440. void DataCompiler::map_source_dir(const char* name, const char* source_dir)
  441. {
  442. TempAllocator256 ta;
  443. DynamicString sname(ta);
  444. DynamicString sdir(ta);
  445. sname.set(name, strlen32(name));
  446. sdir.set(source_dir, strlen32(source_dir));
  447. hash_map::set(_source_dirs, sname, sdir);
  448. }
  449. void DataCompiler::source_dir(const char* resource_name, DynamicString& source_dir)
  450. {
  451. const char* slash = strchr(resource_name, '/');
  452. TempAllocator256 ta;
  453. DynamicString source_name(ta);
  454. if (slash != NULL)
  455. source_name.set(resource_name, u32(slash - resource_name));
  456. else
  457. source_name.set("", 0);
  458. DynamicString deffault(ta);
  459. DynamicString empty(ta);
  460. empty = "";
  461. deffault = hash_map::get(_source_dirs, empty, empty);
  462. source_dir = hash_map::get(_source_dirs, source_name, deffault);
  463. }
  464. void DataCompiler::add_ignore_glob(const char* glob)
  465. {
  466. TempAllocator64 ta;
  467. DynamicString str(ta);
  468. str.set(glob, strlen32(glob));
  469. vector::push_back(_globs, str);
  470. }
  471. void DataCompiler::scan_and_restore(const char* data_dir)
  472. {
  473. // Scan all source directories
  474. s64 time_start = time::now();
  475. // FIXME: refactor this whole garbage
  476. Array<const char*> directories(default_allocator());
  477. auto cur = hash_map::begin(_source_dirs);
  478. auto end = hash_map::end(_source_dirs);
  479. for (; cur != end; ++cur)
  480. {
  481. HASH_MAP_SKIP_HOLE(_source_dirs, cur);
  482. DynamicString prefix(default_allocator());
  483. path::join(prefix, cur->second.c_str(), cur->first.c_str());
  484. char* str = (char*)default_allocator().allocate(prefix.length() + 1);
  485. strcpy(str, prefix.c_str());
  486. array::push_back(directories, (const char*)str);
  487. _source_fs.set_prefix(prefix.c_str());
  488. if (_source_fs.exists(CROWN_DATAIGNORE))
  489. {
  490. File& file = *_source_fs.open(CROWN_DATAIGNORE, FileOpenMode::READ);
  491. const u32 size = file.size();
  492. char* data = (char*)default_allocator().allocate(size + 1);
  493. file.read(data, size);
  494. data[size] = '\0';
  495. _source_fs.close(file);
  496. LineReader lr(data);
  497. while (!lr.eof())
  498. {
  499. TempAllocator512 ta;
  500. DynamicString line(ta);
  501. lr.read_line(line);
  502. line.trim();
  503. if (line.empty() || line.has_prefix("#"))
  504. continue;
  505. add_ignore_glob(line.c_str());
  506. }
  507. default_allocator().deallocate(data);
  508. }
  509. }
  510. _source_index.scan(_source_dirs);
  511. logi(DATA_COMPILER, "Scanned data in %.2fs", time::seconds(time::now() - time_start));
  512. // Restore state from previous run
  513. time_start = time::now();
  514. FilesystemDisk data_fs(default_allocator());
  515. data_fs.set_prefix(data_dir);
  516. read_data_versions(_data_versions, data_fs, CROWN_DATA_VERSIONS);
  517. read_data_index(_data_index, data_fs, CROWN_DATA_INDEX);
  518. read_data_mtimes(_data_mtimes, data_fs, CROWN_DATA_MTIMES);
  519. read_data_dependencies(*this, data_fs, CROWN_DATA_DEPENDENCIES);
  520. logi(DATA_COMPILER, "Restored state in %.2fs", time::seconds(time::now() - time_start));
  521. // Start file monitor
  522. time_start = time::now();
  523. _file_monitor.start(array::size(directories)
  524. , array::begin(directories)
  525. , true
  526. , file_monitor_callback
  527. , this
  528. );
  529. logi(DATA_COMPILER, "Started file monitor in %.2fs", time::seconds(time::now() - time_start));
  530. // Cleanup
  531. for (u32 i = 0, n = array::size(directories); i < n; ++i)
  532. default_allocator().deallocate((void*)directories[n-1-i]);
  533. }
  534. void DataCompiler::save(const char* data_dir)
  535. {
  536. s64 time_start = time::now();
  537. FilesystemDisk data_fs(default_allocator());
  538. data_fs.set_prefix(data_dir);
  539. write_data_index(data_fs, CROWN_DATA_INDEX, _data_index);
  540. write_data_versions(data_fs, CROWN_DATA_VERSIONS, _data_versions);
  541. write_data_mtimes(data_fs, CROWN_DATA_MTIMES, _data_mtimes);
  542. write_data_dependencies(data_fs, CROWN_DATA_DEPENDENCIES, _data_dependencies);
  543. logi(DATA_COMPILER, "Saved state in %.2fs", time::seconds(time::now() - time_start));
  544. }
  545. /// Returns the resource id from @a type and @a name.
  546. ResourceId resource_id(const char* type, u32 type_len, const char* name, u32 name_len)
  547. {
  548. ResourceId id;
  549. id._id = StringId64(type, type_len)._id ^ StringId64(name, name_len)._id;
  550. return id;
  551. }
  552. /// Returns the resource id from @a filename.
  553. ResourceId resource_id(const char* filename)
  554. {
  555. const char* type = path::extension(filename);
  556. const u32 len = u32(type - filename - 1);
  557. return resource_id(type, strlen32(type), filename, len);
  558. }
  559. void destination_path(DynamicString& path, ResourceId id)
  560. {
  561. TempAllocator128 ta;
  562. DynamicString id_hex(ta);
  563. id_hex.from_string_id(id);
  564. path::join(path, CROWN_DATA_DIRECTORY, id_hex.c_str());
  565. }
  566. bool DataCompiler::dependency_changed(const DynamicString& src_path, ResourceId id, u64 dst_mtime)
  567. {
  568. TempAllocator1024 ta;
  569. DynamicString path(ta);
  570. destination_path(path, id);
  571. Stat src_stat;
  572. src_stat = hash_map::get(_source_index._paths, src_path, src_stat);
  573. if (src_stat.mtime > dst_mtime)
  574. return true;
  575. HashMap<DynamicString, u32> deffault(default_allocator());
  576. HashMap<DynamicString, u32>& deps = hash_map::get(_data_dependencies, id, deffault);
  577. auto cur = hash_map::begin(deps);
  578. auto end = hash_map::end(deps);
  579. for (; cur != end; ++cur)
  580. {
  581. HASH_MAP_SKIP_HOLE(deps, cur);
  582. if (src_path == cur->first)
  583. continue;
  584. if (dependency_changed(cur->first, resource_id(cur->first.c_str()), dst_mtime))
  585. return true;
  586. }
  587. return false;
  588. }
  589. bool DataCompiler::version_changed(const DynamicString& src_path, ResourceId id)
  590. {
  591. const char* type = path::extension(src_path.c_str());
  592. if (data_version_stored(type) != data_version(type))
  593. return true;
  594. HashMap<DynamicString, u32> deffault(default_allocator());
  595. HashMap<DynamicString, u32>& deps = hash_map::get(_data_dependencies, id, deffault);
  596. auto cur = hash_map::begin(deps);
  597. auto end = hash_map::end(deps);
  598. for (; cur != end; ++cur)
  599. {
  600. HASH_MAP_SKIP_HOLE(deps, cur);
  601. if (src_path == cur->first)
  602. continue;
  603. if (version_changed(cur->first, resource_id(cur->first.c_str())))
  604. return true;
  605. }
  606. return false;
  607. }
  608. bool DataCompiler::should_ignore(const char* path)
  609. {
  610. for (u32 ii = 0, nn = vector::size(_globs); ii < nn; ++ii)
  611. {
  612. if (wildcmp(_globs[ii].c_str(), path))
  613. return true;
  614. }
  615. return false;
  616. }
  617. bool DataCompiler::compile(const char* data_dir, const char* platform)
  618. {
  619. const s64 time_start = time::now();
  620. FilesystemDisk data_fs(default_allocator());
  621. data_fs.set_prefix(data_dir);
  622. data_fs.create_directory("");
  623. data_fs.create_directory(CROWN_DATA_DIRECTORY);
  624. data_fs.create_directory(CROWN_TEMP_DIRECTORY);
  625. // Find the set of resources to be compiled
  626. Vector<DynamicString> to_compile(default_allocator());
  627. auto cur = hash_map::begin(_source_index._paths);
  628. auto end = hash_map::end(_source_index._paths);
  629. for (; cur != end; ++cur)
  630. {
  631. HASH_MAP_SKIP_HOLE(_source_index._paths, cur);
  632. const DynamicString& src_path = cur->first;
  633. const char* filename = src_path.c_str();
  634. const char* type = path::extension(filename);
  635. if (should_ignore(filename))
  636. continue;
  637. if (type == NULL || !can_compile(type))
  638. {
  639. loge(DATA_COMPILER, "Unknown resource file: '%s'", filename);
  640. loge(DATA_COMPILER, "Append matching pattern to " CROWN_DATAIGNORE " to ignore it");
  641. continue;
  642. }
  643. ResourceId id = resource_id(filename);
  644. TempAllocator256 ta;
  645. DynamicString path(ta);
  646. destination_path(path, id);
  647. u64 dst_mtime = 0;
  648. dst_mtime = hash_map::get(_data_mtimes, id, dst_mtime);
  649. Stat src_stat;
  650. src_stat = hash_map::get(_source_index._paths, src_path, src_stat);
  651. bool source_never_compiled_before = hash_map::has(_data_index, id) == false;
  652. bool source_has_been_changed = src_stat.mtime > dst_mtime;
  653. bool source_dependency_changed = dependency_changed(src_path, id, dst_mtime);
  654. bool data_version_changed = data_version_stored(type) != data_version(type);
  655. bool data_version_dependency_changed = version_changed(src_path, id);
  656. if (source_never_compiled_before
  657. || source_has_been_changed
  658. || source_dependency_changed
  659. || data_version_changed
  660. || data_version_dependency_changed
  661. )
  662. {
  663. vector::push_back(to_compile, src_path);
  664. }
  665. }
  666. // Sort to_compile so that ".package" resources get compiled last
  667. std::sort(vector::begin(to_compile), vector::end(to_compile), [](const DynamicString& resource_a, const DynamicString& resource_b)
  668. {
  669. #define PACKAGE ".package"
  670. if ( resource_a.has_suffix(PACKAGE) && !resource_b.has_suffix(PACKAGE))
  671. return false;
  672. if (!resource_a.has_suffix(PACKAGE) && resource_b.has_suffix(PACKAGE))
  673. return true;
  674. return resource_a < resource_b;
  675. #undef PACKAGE
  676. });
  677. bool success = true;
  678. // Compile all changed resources
  679. for (u32 i = 0; i < vector::size(to_compile); ++i)
  680. {
  681. const DynamicString& src_path = to_compile[i];
  682. const char* filename = src_path.c_str();
  683. const char* type = path::extension(filename);
  684. if (type == NULL)
  685. continue;
  686. TempAllocator1024 ta;
  687. DynamicString path(ta);
  688. // Build destination file path
  689. ResourceId id = resource_id(filename);
  690. destination_path(path, id);
  691. logi(DATA_COMPILER, "%s", src_path.c_str());
  692. // Compile data
  693. ResourceTypeData rtd;
  694. rtd.version = 0;
  695. rtd.compiler = NULL;
  696. DynamicString type_str(ta);
  697. type_str = type;
  698. rtd = hash_map::get(_compilers, type_str, rtd);
  699. {
  700. Buffer output(default_allocator());
  701. array::reserve(output, 4*1024*1024);
  702. CompileOptions opts(*this, data_fs, id, src_path, output, platform);
  703. success = rtd.compiler(opts) == 0;
  704. if (success)
  705. {
  706. File* outf = data_fs.open(path.c_str(), FileOpenMode::WRITE);
  707. u32 size = array::size(output);
  708. u32 written = outf->write(array::begin(output), size);
  709. data_fs.close(*outf);
  710. success = size == written;
  711. }
  712. }
  713. if (success)
  714. {
  715. hash_map::set(_data_index, id, src_path);
  716. hash_map::set(_data_versions, type_str, rtd.version);
  717. hash_map::set(_data_mtimes, id, data_fs.last_modified_time(path.c_str()));
  718. }
  719. else
  720. {
  721. loge(DATA_COMPILER, "Failed to compile data");
  722. break;
  723. }
  724. }
  725. if (success)
  726. {
  727. if (vector::size(to_compile))
  728. logi(DATA_COMPILER, "Compiled data in %.2fs", time::seconds(time::now() - time_start));
  729. else
  730. logi(DATA_COMPILER, "Data is up to date");
  731. }
  732. return success;
  733. }
  734. void DataCompiler::register_compiler(const char* type, u32 version, CompileFunction compiler)
  735. {
  736. TempAllocator64 ta;
  737. DynamicString type_str(ta);
  738. type_str = type;
  739. CE_ASSERT(!hash_map::has(_compilers, type_str), "Type already registered");
  740. CE_ENSURE(NULL != compiler);
  741. ResourceTypeData rtd;
  742. rtd.version = version;
  743. rtd.compiler = compiler;
  744. hash_map::set(_compilers, type_str, rtd);
  745. }
  746. u32 DataCompiler::data_version(const char* type)
  747. {
  748. TempAllocator64 ta;
  749. DynamicString type_str(ta);
  750. type_str = type;
  751. ResourceTypeData rtd;
  752. rtd.version = COMPILER_NOT_FOUND;
  753. rtd.compiler = NULL;
  754. return hash_map::get(_compilers, type_str, rtd).version;
  755. }
  756. u32 DataCompiler::data_version_stored(const char* type)
  757. {
  758. TempAllocator256 ta;
  759. DynamicString ds(ta);
  760. ds = type;
  761. u32 version = UINT32_MAX;
  762. return hash_map::get(_data_versions, ds, version);
  763. }
  764. void DataCompiler::add_dependency(ResourceId id, const char* dependency)
  765. {
  766. HashMap<DynamicString, u32> deps_deffault(default_allocator());
  767. if (hash_map::has(_data_dependencies, id))
  768. {
  769. HashMap<DynamicString, u32>& deps = hash_map::get(_data_dependencies, id, deps_deffault);
  770. TempAllocator256 ta;
  771. DynamicString dependency_ds(ta);
  772. dependency_ds = dependency;
  773. hash_map::set(deps, dependency_ds, 0u);
  774. }
  775. else
  776. {
  777. TempAllocator256 ta;
  778. DynamicString dependency_ds(ta);
  779. dependency_ds = dependency;
  780. hash_map::set(deps_deffault, dependency_ds, 0u);
  781. hash_map::set(_data_dependencies, id, deps_deffault);
  782. }
  783. }
  784. bool DataCompiler::can_compile(const char* type)
  785. {
  786. TempAllocator64 ta;
  787. DynamicString type_str(ta);
  788. type_str = type;
  789. return hash_map::has(_compilers, type_str);
  790. }
  791. void DataCompiler::error(const char* msg, va_list args)
  792. {
  793. vloge(DATA_COMPILER, msg, args);
  794. }
  795. void DataCompiler::file_monitor_callback(FileMonitorEvent::Enum fme, bool is_dir, const char* path, const char* path_renamed)
  796. {
  797. TempAllocator512 ta;
  798. DynamicString source_dir(ta);
  799. DynamicString resource_name(ta);
  800. // Find source directory by matching mapped
  801. // directory prefix with `path`.
  802. auto cur = hash_map::begin(_source_dirs);
  803. auto end = hash_map::end(_source_dirs);
  804. for (; cur != end; ++cur)
  805. {
  806. HASH_MAP_SKIP_HOLE(_source_dirs, cur);
  807. path::join(source_dir, cur->second.c_str(), cur->first.c_str());
  808. if (str_has_prefix(path, source_dir.c_str()))
  809. break;
  810. }
  811. if (cur != end)
  812. {
  813. // All events received must refer to directories
  814. // mapped with map_source_dir().
  815. const char* filename = &path[source_dir.length()+1];
  816. path::join(resource_name, cur->first.c_str(), filename);
  817. #if 0
  818. logi(DATA_COMPILER, "path : %s", path);
  819. logi(DATA_COMPILER, "source_dir : %s", source_dir.c_str());
  820. logi(DATA_COMPILER, "resource_name: %s", resource_name.c_str());
  821. #endif
  822. switch (fme)
  823. {
  824. case FileMonitorEvent::CREATED:
  825. if (!is_dir)
  826. add_file(resource_name.c_str());
  827. else
  828. add_tree(resource_name.c_str());
  829. break;
  830. case FileMonitorEvent::DELETED:
  831. if (!is_dir)
  832. remove_file(resource_name.c_str());
  833. else
  834. remove_tree(resource_name.c_str());
  835. break;
  836. case FileMonitorEvent::RENAMED:
  837. {
  838. DynamicString resource_name_renamed(ta);
  839. path::join(resource_name_renamed, cur->first.c_str(), &path_renamed[source_dir.length()+1]);
  840. if (!is_dir)
  841. {
  842. remove_file(resource_name.c_str());
  843. add_file(resource_name_renamed.c_str());
  844. }
  845. else
  846. {
  847. remove_tree(resource_name.c_str());
  848. add_tree(resource_name_renamed.c_str());
  849. }
  850. }
  851. break;
  852. case FileMonitorEvent::CHANGED:
  853. if (!is_dir)
  854. {
  855. FilesystemDisk fs(default_allocator());
  856. fs.set_prefix(source_dir.c_str());
  857. Stat stat;
  858. stat = fs.stat(filename);
  859. hash_map::set(_source_index._paths, resource_name, stat);
  860. }
  861. break;
  862. default:
  863. CE_ASSERT(false, "Unknown FileMonitorEvent: %d", fme);
  864. break;
  865. }
  866. }
  867. }
  868. void DataCompiler::file_monitor_callback(void* thiz, FileMonitorEvent::Enum fme, bool is_dir, const char* path_original, const char* path_modified)
  869. {
  870. ((DataCompiler*)thiz)->file_monitor_callback(fme, is_dir, path_original, path_modified);
  871. }
  872. int main_data_compiler(const DeviceOptions& opts)
  873. {
  874. #if CROWN_PLATFORM_POSIX
  875. struct sigaction act;
  876. act.sa_handler = [](int /*signum*/) { _quit = true; };
  877. sigemptyset(&act.sa_mask);
  878. act.sa_flags = 0;
  879. sigaction(SIGINT, &act, NULL);
  880. #endif // CROWN_PLATFORM_POSIX
  881. console_server_globals::init();
  882. console_server()->listen(CROWN_DEFAULT_COMPILER_PORT, opts._wait_console);
  883. namespace cor = config_resource_internal;
  884. namespace ftr = font_resource_internal;
  885. namespace lur = lua_resource_internal;
  886. namespace lvr = level_resource_internal;
  887. namespace mhr = mesh_resource_internal;
  888. namespace mtr = material_resource_internal;
  889. namespace pcr = physics_config_resource_internal;
  890. namespace phr = physics_resource_internal;
  891. namespace pkr = package_resource_internal;
  892. namespace sar = sprite_animation_resource_internal;
  893. namespace sdr = sound_resource_internal;
  894. namespace shr = shader_resource_internal;
  895. namespace smr = state_machine_internal;
  896. namespace spr = sprite_resource_internal;
  897. namespace txr = texture_resource_internal;
  898. namespace utr = unit_resource_internal;
  899. DataCompiler* dc = CE_NEW(default_allocator(), DataCompiler)(*console_server());
  900. dc->register_compiler("config", RESOURCE_VERSION_CONFIG, cor::compile);
  901. dc->register_compiler("font", RESOURCE_VERSION_FONT, ftr::compile);
  902. dc->register_compiler("level", RESOURCE_VERSION_LEVEL, lvr::compile);
  903. dc->register_compiler("material", RESOURCE_VERSION_MATERIAL, mtr::compile);
  904. dc->register_compiler("mesh", RESOURCE_VERSION_MESH, mhr::compile);
  905. dc->register_compiler("package", RESOURCE_VERSION_PACKAGE, pkr::compile);
  906. dc->register_compiler("physics_config", RESOURCE_VERSION_PHYSICS_CONFIG, pcr::compile);
  907. dc->register_compiler("lua", RESOURCE_VERSION_SCRIPT, lur::compile);
  908. dc->register_compiler("shader", RESOURCE_VERSION_SHADER, shr::compile);
  909. dc->register_compiler("sound", RESOURCE_VERSION_SOUND, sdr::compile);
  910. dc->register_compiler("sprite", RESOURCE_VERSION_SPRITE, spr::compile);
  911. dc->register_compiler("sprite_animation", RESOURCE_VERSION_SPRITE_ANIMATION, sar::compile);
  912. dc->register_compiler("state_machine", RESOURCE_VERSION_STATE_MACHINE, smr::compile);
  913. dc->register_compiler("texture", RESOURCE_VERSION_TEXTURE, txr::compile);
  914. dc->register_compiler("unit", RESOURCE_VERSION_UNIT, utr::compile);
  915. // Add ignore globs
  916. dc->add_ignore_glob("*.bak");
  917. dc->add_ignore_glob("*.dds");
  918. dc->add_ignore_glob("*.importer_settings");
  919. dc->add_ignore_glob("*.ktx");
  920. dc->add_ignore_glob("*.ogg");
  921. dc->add_ignore_glob("*.png");
  922. dc->add_ignore_glob("*.pvr");
  923. dc->add_ignore_glob("*.swn"); // VIM swap file.
  924. dc->add_ignore_glob("*.swo"); // VIM swap file.
  925. dc->add_ignore_glob("*.swp"); // VIM swap file.
  926. dc->add_ignore_glob("*.tga");
  927. dc->add_ignore_glob("*.tmp");
  928. dc->add_ignore_glob("*.wav");
  929. dc->add_ignore_glob("*~");
  930. dc->add_ignore_glob(".*");
  931. dc->map_source_dir("", opts._source_dir.c_str());
  932. if (opts._map_source_dir_name)
  933. {
  934. dc->map_source_dir(opts._map_source_dir_name
  935. , opts._map_source_dir_prefix.c_str()
  936. );
  937. }
  938. dc->scan_and_restore(opts._data_dir.c_str());
  939. bool success = true;
  940. if (opts._server)
  941. {
  942. while (!_quit)
  943. {
  944. console_server()->update();
  945. os::sleep(60);
  946. }
  947. }
  948. else
  949. {
  950. success = dc->compile(opts._data_dir.c_str(), opts._platform);
  951. }
  952. dc->save(opts._data_dir.c_str());
  953. CE_DELETE(default_allocator(), dc);
  954. console_server_globals::shutdown();
  955. return success ? EXIT_SUCCESS : EXIT_FAILURE;
  956. }
  957. } // namespace crown