data_compiler.cpp 29 KB

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