data_compiler.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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/vector.h"
  8. #include "core/filesystem/file.h"
  9. #include "core/filesystem/filesystem_disk.h"
  10. #include "core/filesystem/path.h"
  11. #include "core/json/json_object.h"
  12. #include "core/json/sjson.h"
  13. #include "core/memory/allocator.h"
  14. #include "core/memory/temp_allocator.h"
  15. #include "core/os.h"
  16. #include "core/strings/dynamic_string.h"
  17. #include "core/strings/string_stream.h"
  18. #include "core/time.h"
  19. #include "device/console_server.h"
  20. #include "device/device_options.h"
  21. #include "device/log.h"
  22. #include "resource/compile_options.h"
  23. #include "resource/config_resource.h"
  24. #include "resource/data_compiler.h"
  25. #include "resource/font_resource.h"
  26. #include "resource/level_resource.h"
  27. #include "resource/lua_resource.h"
  28. #include "resource/material_resource.h"
  29. #include "resource/mesh_resource.h"
  30. #include "resource/package_resource.h"
  31. #include "resource/physics_resource.h"
  32. #include "resource/shader_resource.h"
  33. #include "resource/sound_resource.h"
  34. #include "resource/sprite_resource.h"
  35. #include "resource/state_machine_resource.h"
  36. #include "resource/texture_resource.h"
  37. #include "resource/types.h"
  38. #include "resource/unit_resource.h"
  39. #include <algorithm>
  40. LOG_SYSTEM(DATA_COMPILER, "data_compiler")
  41. namespace crown
  42. {
  43. struct LineReader
  44. {
  45. const char* _str;
  46. const u32 _len;
  47. u32 _pos;
  48. LineReader(const char* str)
  49. : _str(str)
  50. , _len(strlen32(str))
  51. , _pos(0)
  52. {
  53. }
  54. void read_line(DynamicString& line)
  55. {
  56. const char* s = &_str[_pos];
  57. const char* nl = strnl(s);
  58. _pos += u32(nl - s);
  59. line.set(s, u32(nl - s));
  60. }
  61. bool eof()
  62. {
  63. return _str[_pos] == '\0';
  64. }
  65. };
  66. static void console_command_compile(ConsoleServer& cs, TCPSocket client, const char* json, void* user_data)
  67. {
  68. TempAllocator4096 ta;
  69. JsonObject obj(ta);
  70. DynamicString id(ta);
  71. DynamicString data_dir(ta);
  72. DynamicString platform(ta);
  73. sjson::parse(json, obj);
  74. sjson::parse_string(obj["id"], id);
  75. sjson::parse_string(obj["data_dir"], data_dir);
  76. sjson::parse_string(obj["platform"], platform);
  77. {
  78. TempAllocator512 ta;
  79. StringStream ss(ta);
  80. ss << "{\"type\":\"compile\",\"id\":\"" << id.c_str() << "\",\"start\":true}";
  81. cs.send(client, string_stream::c_str(ss));
  82. }
  83. bool succ = ((DataCompiler*)user_data)->compile(data_dir.c_str(), platform.c_str());
  84. {
  85. TempAllocator512 ta;
  86. StringStream ss(ta);
  87. ss << "{\"type\":\"compile\",\"id\":\"" << id.c_str() << "\",\"success\":" << (succ ? "true" : "false") << "}";
  88. cs.send(client, string_stream::c_str(ss));
  89. }
  90. }
  91. DataCompiler::DataCompiler(ConsoleServer& cs)
  92. : _console_server(&cs)
  93. , _source_fs(default_allocator())
  94. , _source_dirs(default_allocator())
  95. , _compilers(default_allocator())
  96. , _files(default_allocator())
  97. , _globs(default_allocator())
  98. , _data_index(default_allocator())
  99. , _file_monitor(default_allocator())
  100. {
  101. cs.register_command("compile", console_command_compile, this);
  102. }
  103. DataCompiler::~DataCompiler()
  104. {
  105. _file_monitor.stop();
  106. }
  107. void DataCompiler::add_file(const char* path)
  108. {
  109. for (u32 gg = 0; gg < vector::size(_globs); ++gg)
  110. {
  111. if (wildcmp(_globs[gg].c_str(), path))
  112. return;
  113. }
  114. TempAllocator512 ta;
  115. DynamicString str(ta);
  116. str.set(path, strlen32(path));
  117. vector::push_back(_files, str);
  118. StringStream ss(ta);
  119. ss << "{\"type\":\"add_file\",\"path\":\"" << str.c_str() << "\"}";
  120. _console_server->send(string_stream::c_str(ss));
  121. }
  122. void DataCompiler::add_tree(const char* path)
  123. {
  124. TempAllocator512 ta;
  125. DynamicString source_dir(ta);
  126. source_dir = hash_map::get(_source_dirs, source_dir, source_dir);
  127. _source_fs.set_prefix(source_dir.c_str());
  128. DataCompiler::scan_source_dir(source_dir.c_str(), path);
  129. StringStream ss(ta);
  130. ss << "{\"type\":\"add_tree\",\"path\":\"" << path << "\"}";
  131. _console_server->send(string_stream::c_str(ss));
  132. }
  133. void DataCompiler::remove_file(const char* path)
  134. {
  135. for (u32 i = 0; i < vector::size(_files); ++i)
  136. {
  137. if (_files[i] == path)
  138. {
  139. _files[i] = _files[vector::size(_files) - 1];
  140. vector::pop_back(_files);
  141. TempAllocator512 ta;
  142. StringStream ss(ta);
  143. ss << "{\"type\":\"remove_file\",\"path\":\"" << path << "\"}";
  144. _console_server->send(string_stream::c_str(ss));
  145. return;
  146. }
  147. }
  148. }
  149. void DataCompiler::remove_tree(const char* path)
  150. {
  151. TempAllocator512 ta;
  152. StringStream ss(ta);
  153. ss << "{\"type\":\"remove_tree\",\"path\":\"" << path << "\"}";
  154. _console_server->send(string_stream::c_str(ss));
  155. for (u32 i = 0; i < vector::size(_files);)
  156. {
  157. if (_files[i].has_prefix(path))
  158. {
  159. TempAllocator512 ta;
  160. StringStream ss(ta);
  161. ss << "{\"type\":\"remove_file\",\"path\":\"" << _files[i].c_str() << "\"}";
  162. _console_server->send(string_stream::c_str(ss));
  163. _files[i] = _files[vector::size(_files) - 1];
  164. vector::pop_back(_files);
  165. continue;
  166. }
  167. ++i;
  168. }
  169. }
  170. void DataCompiler::scan_source_dir(const char* prefix, const char* cur_dir)
  171. {
  172. Vector<DynamicString> my_files(default_allocator());
  173. _source_fs.list_files(cur_dir, my_files);
  174. for (u32 i = 0; i < vector::size(my_files); ++i)
  175. {
  176. TempAllocator512 ta;
  177. DynamicString file_i(ta);
  178. if (strcmp(cur_dir, "") != 0)
  179. {
  180. file_i += cur_dir;
  181. file_i += '/';
  182. }
  183. file_i += my_files[i];
  184. if (_source_fs.is_directory(file_i.c_str()))
  185. {
  186. DataCompiler::scan_source_dir(prefix, file_i.c_str());
  187. }
  188. else // Assume a regular file
  189. {
  190. DynamicString resource_name(ta);
  191. if (strcmp(prefix, "") != 0)
  192. {
  193. resource_name += prefix;
  194. resource_name += '/';
  195. }
  196. resource_name += file_i;
  197. add_file(resource_name.c_str());
  198. }
  199. }
  200. }
  201. void DataCompiler::map_source_dir(const char* name, const char* source_dir)
  202. {
  203. TempAllocator256 ta;
  204. DynamicString sname(ta);
  205. DynamicString sdir(ta);
  206. sname.set(name, strlen32(name));
  207. sdir.set(source_dir, strlen32(source_dir));
  208. hash_map::set(_source_dirs, sname, sdir);
  209. }
  210. void DataCompiler::source_dir(const char* resource_name, DynamicString& source_dir)
  211. {
  212. const char* slash = strchr(resource_name, '/');
  213. TempAllocator256 ta;
  214. DynamicString source_name(ta);
  215. if (slash != NULL)
  216. source_name.set(resource_name, u32(slash - resource_name));
  217. else
  218. source_name.set("", 0);
  219. DynamicString deffault(ta);
  220. DynamicString empty(ta);
  221. empty = "";
  222. deffault = hash_map::get(_source_dirs, empty, empty);
  223. source_dir = hash_map::get(_source_dirs, source_name, deffault);
  224. }
  225. void DataCompiler::add_ignore_glob(const char* glob)
  226. {
  227. TempAllocator64 ta;
  228. DynamicString str(ta);
  229. str.set(glob, strlen32(glob));
  230. vector::push_back(_globs, str);
  231. }
  232. void DataCompiler::scan()
  233. {
  234. const s64 time_start = time::now();
  235. // Scan all source directories
  236. auto cur = hash_map::begin(_source_dirs);
  237. auto end = hash_map::end(_source_dirs);
  238. for (; cur != end; ++cur)
  239. {
  240. if (hash_map::is_hole(_source_dirs, cur))
  241. continue;
  242. DynamicString prefix(default_allocator());
  243. path::join(prefix, cur->second.c_str(), cur->first.c_str());
  244. _source_fs.set_prefix(prefix.c_str());
  245. if (_source_fs.exists(CROWN_DATAIGNORE))
  246. {
  247. File& file = *_source_fs.open(CROWN_DATAIGNORE, FileOpenMode::READ);
  248. const u32 size = file.size();
  249. char* data = (char*)default_allocator().allocate(size + 1);
  250. file.read(data, size);
  251. data[size] = '\0';
  252. _source_fs.close(file);
  253. LineReader lr(data);
  254. while (!lr.eof())
  255. {
  256. TempAllocator512 ta;
  257. DynamicString line(ta);
  258. lr.read_line(line);
  259. line.trim();
  260. if (line.empty() || line.has_prefix("#"))
  261. continue;
  262. add_ignore_glob(line.c_str());
  263. }
  264. default_allocator().deallocate(data);
  265. }
  266. scan_source_dir(cur->first.c_str(), "");
  267. }
  268. logi(DATA_COMPILER, "Scanned data in %.2fs", time::seconds(time::now() - time_start));
  269. _file_monitor.start(hash_map::begin(_source_dirs)->pair.second.c_str(), true, filemonitor_callback, this);
  270. }
  271. bool DataCompiler::compile(const char* data_dir, const char* platform)
  272. {
  273. const s64 time_start = time::now();
  274. FilesystemDisk data_filesystem(default_allocator());
  275. data_filesystem.set_prefix(data_dir);
  276. data_filesystem.create_directory("");
  277. if (!data_filesystem.exists(CROWN_DATA_DIRECTORY))
  278. data_filesystem.create_directory(CROWN_DATA_DIRECTORY);
  279. if (!data_filesystem.exists(CROWN_TEMP_DIRECTORY))
  280. data_filesystem.create_directory(CROWN_TEMP_DIRECTORY);
  281. std::sort(vector::begin(_files), vector::end(_files), [](const DynamicString& resource_a, const DynamicString& resource_b)
  282. {
  283. #define PACKAGE ".package"
  284. if ( resource_a.has_suffix(PACKAGE) && !resource_b.has_suffix(PACKAGE))
  285. return false;
  286. if (!resource_a.has_suffix(PACKAGE) && resource_b.has_suffix(PACKAGE))
  287. return true;
  288. return resource_a < resource_b;
  289. #undef PACKAGE
  290. });
  291. bool success = false;
  292. // Compile all changed resources
  293. for (u32 i = 0; i < vector::size(_files); ++i)
  294. {
  295. const char* filename = _files[i].c_str();
  296. const char* type = path::extension(filename);
  297. if (type == NULL)
  298. continue;
  299. char name[256];
  300. const u32 size = u32(type - filename - 1);
  301. strncpy(name, filename, size);
  302. name[size] = '\0';
  303. TempAllocator1024 ta;
  304. DynamicString path(ta);
  305. DynamicString src_path(ta);
  306. DynamicString dst_path(ta);
  307. StringId64 _type(type);
  308. StringId64 _name(name);
  309. // Build source file path
  310. src_path += name;
  311. src_path += '.';
  312. src_path += type;
  313. // Build destination file path
  314. StringId64 mix;
  315. mix._id = _type._id ^ _name._id;
  316. mix.to_string(dst_path);
  317. path::join(path, CROWN_DATA_DIRECTORY, dst_path.c_str());
  318. logi(DATA_COMPILER, "%s", src_path.c_str());
  319. if (!can_compile(_type))
  320. {
  321. loge(DATA_COMPILER, "Unknown resource type: '%s'", type);
  322. loge(DATA_COMPILER, "Append extension to " CROWN_DATAIGNORE " to ignore the type");
  323. success = false;
  324. break;
  325. }
  326. Buffer output(default_allocator());
  327. array::reserve(output, 4*1024*1024);
  328. if (!setjmp(_jmpbuf))
  329. {
  330. CompileOptions opts(*this, data_filesystem, src_path, output, platform);
  331. hash_map::get(_compilers, _type, ResourceTypeData()).compiler(opts);
  332. File* outf = data_filesystem.open(path.c_str(), FileOpenMode::WRITE);
  333. u32 size = array::size(output);
  334. u32 written = outf->write(array::begin(output), size);
  335. data_filesystem.close(*outf);
  336. success = size == written;
  337. }
  338. else
  339. {
  340. success = false;
  341. }
  342. if (success)
  343. {
  344. if (!hash_map::has(_data_index, dst_path))
  345. hash_map::set(_data_index, dst_path, src_path);
  346. }
  347. else
  348. {
  349. loge(DATA_COMPILER, "Failed to compile data");
  350. break;
  351. }
  352. }
  353. // Write data index
  354. {
  355. File* file = data_filesystem.open("data_index.sjson", FileOpenMode::WRITE);
  356. if (file)
  357. {
  358. StringStream ss(default_allocator());
  359. auto cur = hash_map::begin(_data_index);
  360. auto end = hash_map::end(_data_index);
  361. for (; cur != end; ++cur)
  362. {
  363. if (hash_map::is_hole(_data_index, cur))
  364. continue;
  365. ss << "\"" << cur->first.c_str() << "\" = \"" << cur->second.c_str() << "\"\n";
  366. }
  367. file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  368. data_filesystem.close(*file);
  369. }
  370. }
  371. if (success)
  372. logi(DATA_COMPILER, "Compiled data in %.2fs", time::seconds(time::now() - time_start));
  373. return success;
  374. }
  375. void DataCompiler::register_compiler(StringId64 type, u32 version, CompileFunction compiler)
  376. {
  377. CE_ASSERT(!hash_map::has(_compilers, type), "Type already registered");
  378. CE_ENSURE(NULL != compiler);
  379. ResourceTypeData rtd;
  380. rtd.version = version;
  381. rtd.compiler = compiler;
  382. hash_map::set(_compilers, type, rtd);
  383. }
  384. u32 DataCompiler::version(StringId64 type)
  385. {
  386. ResourceTypeData rtd;
  387. rtd.version = COMPILER_NOT_FOUND;
  388. rtd.compiler = NULL;
  389. return hash_map::get(_compilers, type, rtd).version;
  390. }
  391. bool DataCompiler::can_compile(StringId64 type)
  392. {
  393. return hash_map::has(_compilers, type);
  394. }
  395. void DataCompiler::error(const char* msg, va_list args)
  396. {
  397. vloge(DATA_COMPILER, msg, args);
  398. longjmp(_jmpbuf, 1);
  399. }
  400. void DataCompiler::filemonitor_callback(FileMonitorEvent::Enum fme, bool is_dir, const char* path, const char* path_renamed)
  401. {
  402. TempAllocator512 ta;
  403. DynamicString resource_name(ta);
  404. DynamicString resource_name_renamed(ta);
  405. DynamicString source_dir(ta);
  406. source_dir = hash_map::get(_source_dirs, source_dir, source_dir);
  407. resource_name = &path[source_dir.length()+1]; // FIXME: add path::relative()
  408. resource_name_renamed = path_renamed ? &path_renamed[source_dir.length()+1] : "";
  409. switch (fme)
  410. {
  411. case FileMonitorEvent::CREATED:
  412. if (!is_dir)
  413. add_file(resource_name.c_str());
  414. else
  415. add_tree(resource_name.c_str());
  416. break;
  417. case FileMonitorEvent::DELETED:
  418. if (!is_dir)
  419. remove_file(resource_name.c_str());
  420. else
  421. remove_tree(resource_name.c_str());
  422. break;
  423. case FileMonitorEvent::RENAMED:
  424. if (!is_dir)
  425. {
  426. remove_file(resource_name.c_str());
  427. add_file(resource_name_renamed.c_str());
  428. }
  429. else
  430. {
  431. remove_tree(resource_name.c_str());
  432. add_tree(resource_name_renamed.c_str());
  433. }
  434. break;
  435. case FileMonitorEvent::CHANGED:
  436. break;
  437. default:
  438. CE_ASSERT(false, "Unknown FileMonitorEvent: %d", fme);
  439. break;
  440. }
  441. }
  442. void DataCompiler::filemonitor_callback(void* thiz, FileMonitorEvent::Enum fme, bool is_dir, const char* path_original, const char* path_modified)
  443. {
  444. ((DataCompiler*)thiz)->filemonitor_callback(fme, is_dir, path_original, path_modified);
  445. }
  446. struct InitMemoryGlobals
  447. {
  448. InitMemoryGlobals()
  449. {
  450. crown::memory_globals::init();
  451. }
  452. ~InitMemoryGlobals()
  453. {
  454. crown::memory_globals::shutdown();
  455. }
  456. };
  457. int main_data_compiler(const DeviceOptions& opts)
  458. {
  459. console_server_globals::init();
  460. console_server()->listen(CROWN_DEFAULT_COMPILER_PORT, opts._wait_console);
  461. namespace cor = config_resource_internal;
  462. namespace ftr = font_resource_internal;
  463. namespace lur = lua_resource_internal;
  464. namespace lvr = level_resource_internal;
  465. namespace mhr = mesh_resource_internal;
  466. namespace mtr = material_resource_internal;
  467. namespace pcr = physics_config_resource_internal;
  468. namespace phr = physics_resource_internal;
  469. namespace pkr = package_resource_internal;
  470. namespace sar = sprite_animation_resource_internal;
  471. namespace sdr = sound_resource_internal;
  472. namespace shr = shader_resource_internal;
  473. namespace smr = state_machine_internal;
  474. namespace spr = sprite_resource_internal;
  475. namespace txr = texture_resource_internal;
  476. namespace utr = unit_resource_internal;
  477. DataCompiler* dc = CE_NEW(default_allocator(), DataCompiler)(*console_server());
  478. dc->register_compiler(RESOURCE_TYPE_CONFIG, RESOURCE_VERSION_CONFIG, cor::compile);
  479. dc->register_compiler(RESOURCE_TYPE_FONT, RESOURCE_VERSION_FONT, ftr::compile);
  480. dc->register_compiler(RESOURCE_TYPE_LEVEL, RESOURCE_VERSION_LEVEL, lvr::compile);
  481. dc->register_compiler(RESOURCE_TYPE_MATERIAL, RESOURCE_VERSION_MATERIAL, mtr::compile);
  482. dc->register_compiler(RESOURCE_TYPE_MESH, RESOURCE_VERSION_MESH, mhr::compile);
  483. dc->register_compiler(RESOURCE_TYPE_PACKAGE, RESOURCE_VERSION_PACKAGE, pkr::compile);
  484. dc->register_compiler(RESOURCE_TYPE_PHYSICS, RESOURCE_VERSION_PHYSICS, phr::compile);
  485. dc->register_compiler(RESOURCE_TYPE_PHYSICS_CONFIG, RESOURCE_VERSION_PHYSICS_CONFIG, pcr::compile);
  486. dc->register_compiler(RESOURCE_TYPE_SCRIPT, RESOURCE_VERSION_SCRIPT, lur::compile);
  487. dc->register_compiler(RESOURCE_TYPE_SHADER, RESOURCE_VERSION_SHADER, shr::compile);
  488. dc->register_compiler(RESOURCE_TYPE_SOUND, RESOURCE_VERSION_SOUND, sdr::compile);
  489. dc->register_compiler(RESOURCE_TYPE_SPRITE, RESOURCE_VERSION_SPRITE, spr::compile);
  490. dc->register_compiler(RESOURCE_TYPE_SPRITE_ANIMATION, RESOURCE_VERSION_SPRITE_ANIMATION, sar::compile);
  491. dc->register_compiler(RESOURCE_TYPE_STATE_MACHINE, RESOURCE_VERSION_STATE_MACHINE, smr::compile);
  492. dc->register_compiler(RESOURCE_TYPE_TEXTURE, RESOURCE_VERSION_TEXTURE, txr::compile);
  493. dc->register_compiler(RESOURCE_TYPE_UNIT, RESOURCE_VERSION_UNIT, utr::compile);
  494. // Add ignore globs
  495. dc->add_ignore_glob("*.bak");
  496. dc->add_ignore_glob("*.dds");
  497. dc->add_ignore_glob("*.importer_settings");
  498. dc->add_ignore_glob("*.ktx");
  499. dc->add_ignore_glob("*.ogg");
  500. dc->add_ignore_glob("*.png");
  501. dc->add_ignore_glob("*.pvr");
  502. dc->add_ignore_glob("*.swn"); // VIM swap file.
  503. dc->add_ignore_glob("*.swo"); // VIM swap file.
  504. dc->add_ignore_glob("*.swp"); // VIM swap file.
  505. dc->add_ignore_glob("*.tga");
  506. dc->add_ignore_glob("*.tmp");
  507. dc->add_ignore_glob("*.wav");
  508. dc->add_ignore_glob("*~");
  509. dc->add_ignore_glob(".*");
  510. dc->map_source_dir("", opts._source_dir.c_str());
  511. if (opts._map_source_dir_name)
  512. {
  513. dc->map_source_dir(opts._map_source_dir_name
  514. , opts._map_source_dir_prefix.c_str()
  515. );
  516. }
  517. dc->scan();
  518. bool success = true;
  519. if (opts._server)
  520. {
  521. while (true)
  522. {
  523. console_server()->update();
  524. os::sleep(60);
  525. }
  526. }
  527. else
  528. {
  529. success = dc->compile(opts._data_dir.c_str(), opts._platform);
  530. }
  531. CE_DELETE(default_allocator(), dc);
  532. console_server_globals::shutdown();
  533. return success ? EXIT_SUCCESS : EXIT_FAILURE;
  534. }
  535. } // namespace crown