data_compiler.cpp 28 KB

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