data_compiler.cpp 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603
  1. /*
  2. * Copyright (c) 2012-2024 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "config.h"
  6. #if CROWN_CAN_COMPILE
  7. #include "core/containers/hash_map.inl"
  8. #include "core/containers/hash_set.inl"
  9. #include "core/containers/vector.inl"
  10. #include "core/filesystem/file.h"
  11. #include "core/filesystem/file_buffer.inl"
  12. #include "core/filesystem/filesystem_disk.h"
  13. #include "core/filesystem/path.h"
  14. #include "core/guid.inl"
  15. #include "core/json/json_object.inl"
  16. #include "core/json/sjson.h"
  17. #include "core/memory/allocator.h"
  18. #include "core/memory/temp_allocator.inl"
  19. #include "core/option.inl"
  20. #include "core/os.h"
  21. #include "core/strings/dynamic_string.inl"
  22. #include "core/strings/string.h"
  23. #include "core/strings/string_id.inl"
  24. #include "core/strings/string_stream.inl"
  25. #include "core/time.h"
  26. #include "device/console_server.h"
  27. #include "device/device_options.h"
  28. #include "device/log.h"
  29. #include "resource/compile_options.inl"
  30. #include "resource/config_resource.h"
  31. #include "resource/data_compiler.h"
  32. #include "resource/font_resource.h"
  33. #include "resource/level_resource.h"
  34. #include "resource/lua_resource.h"
  35. #include "resource/material_resource.h"
  36. #include "resource/mesh_resource.h"
  37. #include "resource/package_resource.h"
  38. #include "resource/physics_resource.h"
  39. #include "resource/resource_id.inl"
  40. #include "resource/shader_resource.h"
  41. #include "resource/sound_resource.h"
  42. #include "resource/sprite_resource.h"
  43. #include "resource/state_machine_resource.h"
  44. #include "resource/texture_resource.h"
  45. #include "resource/types.h"
  46. #include "resource/unit_resource.h"
  47. #include <algorithm>
  48. #include <inttypes.h>
  49. #if CROWN_PLATFORM_WINDOWS
  50. #ifndef WIN32_LEAN_AND_MEAN
  51. #define WIN32_LEAN_AND_MEAN
  52. #endif
  53. #include <windows.h>
  54. #else
  55. #include <signal.h>
  56. #endif
  57. LOG_SYSTEM(DATA_COMPILER, "data_compiler")
  58. #define CROWN_DATA_VERSIONS "data_versions.sjson"
  59. #define CROWN_DATA_INDEX "data_index.sjson"
  60. #define CROWN_DATA_MTIMES "data_mtimes.sjson"
  61. #define CROWN_DATA_DEPENDENCIES "data_dependencies.sjson"
  62. namespace crown
  63. {
  64. struct PlatformInfo
  65. {
  66. const char *name;
  67. Platform::Enum type;
  68. };
  69. static const PlatformInfo platform_info[] =
  70. {
  71. { "android", Platform::ANDROID },
  72. { "android-arm64", Platform::ANDROID_ARM64 },
  73. { "html5", Platform::HTML5 },
  74. { "linux", Platform::LINUX },
  75. { "windows", Platform::WINDOWS }
  76. };
  77. CE_STATIC_ASSERT(countof(platform_info) == Platform::COUNT);
  78. static volatile bool _quit = false;
  79. static void notify_add_file(const char *path, Stat &st)
  80. {
  81. TempAllocator512 ta;
  82. StringStream ss(ta);
  83. ss << "{\"type\":\"add_file\"";
  84. ss << ",\"path\":\"" << path << "\"";
  85. ss << ",\"size\":\"" << st.size << "\"";
  86. ss << ",\"mtime\":\"" << st.mtime << "\"";
  87. ss << "}";
  88. console_server()->broadcast(string_stream::c_str(ss));
  89. }
  90. static void notify_remove_file(const char *path)
  91. {
  92. TempAllocator512 ta;
  93. StringStream ss(ta);
  94. ss << "{\"type\":\"remove_file\",\"path\":\"" << path << "\"}";
  95. console_server()->broadcast(string_stream::c_str(ss));
  96. }
  97. static void notify_add_tree(const char *path)
  98. {
  99. TempAllocator512 ta;
  100. StringStream ss(ta);
  101. ss << "{\"type\":\"add_tree\",\"path\":\"" << path << "\"}";
  102. console_server()->broadcast(string_stream::c_str(ss));
  103. }
  104. static void notify_remove_tree(const char *path)
  105. {
  106. TempAllocator512 ta;
  107. StringStream ss(ta);
  108. ss << "{\"type\":\"remove_tree\",\"path\":\"" << path << "\"}";
  109. console_server()->broadcast(string_stream::c_str(ss));
  110. }
  111. static void notify_change_file(const char *path, Stat &st)
  112. {
  113. TempAllocator512 ta;
  114. StringStream ss(ta);
  115. ss << "{\"type\":\"change_file\"";
  116. ss << ",\"path\":\"" << path << "\"";
  117. ss << ",\"size\":\"" << st.size << "\"";
  118. ss << ",\"mtime\":\"" << st.mtime << "\"";
  119. ss << "}";
  120. console_server()->broadcast(string_stream::c_str(ss));
  121. }
  122. SourceIndex::SourceIndex()
  123. : _paths(default_allocator())
  124. {
  125. }
  126. void SourceIndex::scan_directory(FilesystemDisk &fs, const char *prefix, const char *directory)
  127. {
  128. Vector<DynamicString> files(default_allocator());
  129. fs.list_files(directory != NULL ? directory : "", files);
  130. for (u32 i = 0; i < vector::size(files); ++i) {
  131. TempAllocator512 ta;
  132. DynamicString file_i(ta);
  133. if (directory != NULL) {
  134. file_i += directory;
  135. file_i += '/';
  136. }
  137. file_i += files[i];
  138. if (fs.is_directory(file_i.c_str())) {
  139. DynamicString directory_name(ta);
  140. if (strcmp(prefix, "") != 0) {
  141. directory_name += prefix;
  142. directory_name += '/';
  143. }
  144. directory_name += file_i;
  145. notify_add_tree(directory_name.c_str());
  146. scan_directory(fs, prefix, file_i.c_str());
  147. } else { // Assume a regular file
  148. DynamicString resource_name(ta);
  149. if (strcmp(prefix, "") != 0) {
  150. resource_name += prefix;
  151. resource_name += '/';
  152. }
  153. resource_name += file_i;
  154. Stat st;
  155. st = fs.stat(file_i.c_str());
  156. hash_map::set(_paths, resource_name, st);
  157. notify_add_file(resource_name.c_str(), st);
  158. }
  159. }
  160. }
  161. void SourceIndex::scan(const HashMap<DynamicString, DynamicString> &source_dirs)
  162. {
  163. auto cur = hash_map::begin(source_dirs);
  164. auto end = hash_map::end(source_dirs);
  165. for (; cur != end; ++cur) {
  166. HASH_MAP_SKIP_HOLE(source_dirs, cur);
  167. TempAllocator512 ta;
  168. DynamicString prefix(ta);
  169. path::join(prefix, cur->second.c_str(), cur->first.c_str());
  170. FilesystemDisk fs(default_allocator());
  171. fs.set_prefix(prefix.c_str());
  172. scan_directory(fs, cur->first.c_str(), NULL);
  173. }
  174. }
  175. struct LineReader
  176. {
  177. const char *_str;
  178. const u32 _len;
  179. u32 _pos;
  180. explicit LineReader(const char *str)
  181. : _str(str)
  182. , _len(strlen32(str))
  183. , _pos(0)
  184. {
  185. }
  186. void read_line(DynamicString &line)
  187. {
  188. const char *s = &_str[_pos];
  189. const char *nl = strnl(s);
  190. _pos += u32(nl - s);
  191. line.set(s, u32(nl - s));
  192. }
  193. bool eof() const
  194. {
  195. return _str[_pos] == '\0';
  196. }
  197. };
  198. static void console_command_compile(ConsoleServer &cs, u32 client_id, const char *json, void *user_data)
  199. {
  200. TempAllocator4096 ta;
  201. JsonObject obj(ta);
  202. DynamicString id(ta);
  203. DynamicString data_dir(ta);
  204. DynamicString platform(ta);
  205. StringStream ss(ta);
  206. DataCompiler *dc = (DataCompiler *)user_data;
  207. sjson::parse(obj, json);
  208. sjson::parse_string(id, obj["id"]);
  209. sjson::parse_string(data_dir, obj["data_dir"]);
  210. sjson::parse_string(platform, obj["platform"]);
  211. ss << "{";
  212. ss << "\"type\":\"compile\",";
  213. ss << "\"id\":\"" << id.c_str() << "\",";
  214. ss << "\"start\":true";
  215. ss << "}";
  216. cs.send(client_id, string_stream::c_str(ss));
  217. bool succ = dc->compile(data_dir.c_str(), platform.c_str());
  218. array::clear(ss);
  219. ss << "{";
  220. ss << "\"type\":\"compile\",";
  221. ss << "\"id\":\"" << id.c_str() << "\",";
  222. ss << "\"success\":" << (succ ? "true" : "false") << ",";
  223. ss << "\"revision\":" << dc->_revision;
  224. ss << "}";
  225. cs.send(client_id, string_stream::c_str(ss));
  226. }
  227. static void console_command_quit(ConsoleServer & /*cs*/, u32 /*client_id*/, const char * /*json*/, void * /*user_data*/)
  228. {
  229. _quit = true;
  230. }
  231. static void console_command_refresh_list(ConsoleServer &cs, u32 client_id, const char *json, void *user_data)
  232. {
  233. DataCompiler *dc = (DataCompiler *)user_data;
  234. TempAllocator4096 ta;
  235. StringStream ss(ta);
  236. JsonObject obj(ta);
  237. sjson::parse(obj, json);
  238. const u32 revision = (u32)sjson::parse_int(obj["revision"]);
  239. ss << "{\"type\":\"refresh_list\",\"list\":[";
  240. auto cur = hash_map::begin(dc->_data_revisions);
  241. auto end = hash_map::end(dc->_data_revisions);
  242. for (; cur != end; ++cur) {
  243. HASH_MAP_SKIP_HOLE(dc->_data_revisions, cur);
  244. DynamicString deffault(ta);
  245. if (cur->second > revision) {
  246. ss << "\"";
  247. ss << hash_map::get(dc->_data_index, cur->first, deffault).c_str();
  248. ss << "\",";
  249. }
  250. }
  251. ss << "]}";
  252. cs.send(client_id, string_stream::c_str(ss));
  253. }
  254. static Buffer read(FilesystemDisk &data_fs, const char *filename)
  255. {
  256. Buffer buffer(default_allocator());
  257. File *file = data_fs.open(filename, FileOpenMode::READ);
  258. if (file->is_open()) {
  259. u32 size = file->size();
  260. if (size == 0) {
  261. data_fs.close(*file);
  262. return buffer;
  263. }
  264. array::resize(buffer, size);
  265. file->read(array::begin(buffer), size);
  266. }
  267. data_fs.close(*file);
  268. return buffer;
  269. }
  270. static void parse_data_versions(HashMap<DynamicString, u32> &versions
  271. , Buffer &json
  272. )
  273. {
  274. TempAllocator512 ta;
  275. JsonObject obj(ta);
  276. sjson::parse(obj, json);
  277. auto cur = json_object::begin(obj);
  278. auto end = json_object::end(obj);
  279. for (; cur != end; ++cur) {
  280. JSON_OBJECT_SKIP_HOLE(obj, cur);
  281. TempAllocator256 ta;
  282. DynamicString type(ta);
  283. type.set(cur->first.data(), cur->first.length());
  284. hash_map::set(versions, type, (u32)sjson::parse_int(cur->second));
  285. }
  286. }
  287. static void read_data_versions(HashMap<DynamicString, u32> &versions
  288. , FilesystemDisk &data_fs
  289. , const char *filename
  290. )
  291. {
  292. Buffer json = read(data_fs, filename);
  293. parse_data_versions(versions, json);
  294. }
  295. static void parse_data_index(HashMap<StringId64, DynamicString> &index
  296. , const SourceIndex &sources
  297. , Buffer &json
  298. )
  299. {
  300. TempAllocator512 ta;
  301. JsonObject obj(ta);
  302. sjson::parse(obj, json);
  303. auto cur = json_object::begin(obj);
  304. auto end = json_object::end(obj);
  305. for (; cur != end; ++cur) {
  306. JSON_OBJECT_SKIP_HOLE(obj, cur);
  307. TempAllocator256 ta;
  308. DynamicString path(ta);
  309. sjson::parse_string(path, cur->second);
  310. // Skip reading data that belongs to non-existent source file.
  311. if (!hash_map::has(sources._paths, path))
  312. continue;
  313. StringId64 id;
  314. id.parse(cur->first.data());
  315. hash_map::set(index, id, path);
  316. }
  317. }
  318. static void read_data_index(HashMap<StringId64, DynamicString> &index
  319. , FilesystemDisk &data_fs
  320. , const SourceIndex &sources
  321. , const char *filename
  322. )
  323. {
  324. Buffer json = read(data_fs, filename);
  325. parse_data_index(index, sources, json);
  326. }
  327. static void parse_data_mtimes(HashMap<StringId64, u64> &mtimes
  328. , const HashMap<StringId64, DynamicString> &data_index
  329. , Buffer &json
  330. )
  331. {
  332. TempAllocator128 ta;
  333. JsonObject obj(ta);
  334. sjson::parse(obj, json);
  335. auto cur = json_object::begin(obj);
  336. auto end = json_object::end(obj);
  337. for (; cur != end; ++cur) {
  338. JSON_OBJECT_SKIP_HOLE(obj, cur);
  339. StringId64 id;
  340. id.parse(cur->first.data());
  341. // Skip reading data that belongs to non-existent source file.
  342. if (!hash_map::has(data_index, id))
  343. continue;
  344. TempAllocator64 ta;
  345. DynamicString mtime_json(ta);
  346. sjson::parse_string(mtime_json, cur->second);
  347. u64 mtime = strtoull(mtime_json.c_str(), NULL, 10);
  348. hash_map::set(mtimes, id, mtime);
  349. }
  350. }
  351. static void read_data_mtimes(HashMap<StringId64, u64> &mtimes
  352. , FilesystemDisk &data_fs
  353. , const HashMap<StringId64, DynamicString> &data_index
  354. , const char *filename
  355. )
  356. {
  357. Buffer json = read(data_fs, filename);
  358. parse_data_mtimes(mtimes, data_index, json);
  359. }
  360. static void add_dependency_internal(HashMap<StringId64, HashMap<DynamicString, u32>> &dependencies, ResourceId id, const DynamicString &dependency)
  361. {
  362. HashMap<DynamicString, u32> deps_deffault(default_allocator());
  363. HashMap<DynamicString, u32> &deps = hash_map::get(dependencies, id, deps_deffault);
  364. hash_map::set(deps, dependency, 0u);
  365. if (&deps == &deps_deffault)
  366. hash_map::set(dependencies, id, deps);
  367. }
  368. static void add_dependency_internal(HashMap<StringId64, HashMap<DynamicString, u32>> &dependencies, ResourceId id, const char *dependency)
  369. {
  370. TempAllocator512 ta;
  371. DynamicString dependency_str(ta);
  372. dependency_str = dependency;
  373. add_dependency_internal(dependencies, id, dependency_str);
  374. }
  375. static void read_data_dependencies(DataCompiler &dc
  376. , FilesystemDisk &data_fs
  377. , const HashMap<StringId64, DynamicString> &data_index
  378. , const char *filename
  379. )
  380. {
  381. Buffer json = read(data_fs, filename);
  382. TempAllocator1024 ta;
  383. JsonObject obj(ta);
  384. sjson::parse(obj, json);
  385. auto cur = json_object::begin(obj);
  386. auto end = json_object::end(obj);
  387. for (; cur != end; ++cur) {
  388. JSON_OBJECT_SKIP_HOLE(obj, cur);
  389. StringId64 id;
  390. id.parse(cur->first.data());
  391. // Skip reading data that belongs to non-existent source file.
  392. if (!hash_map::has(data_index, id))
  393. continue;
  394. JsonArray dependency_array(ta);
  395. sjson::parse_array(dependency_array, cur->second);
  396. for (u32 i = 0; i < array::size(dependency_array); ++i) {
  397. DynamicString path(ta);
  398. sjson::parse_string(path, dependency_array[i]);
  399. if (path.has_prefix("//r ")) {
  400. add_dependency_internal(dc._data_requirements, id, path.c_str() + 4);
  401. } else if (path.has_prefix("//- ")) {
  402. add_dependency_internal(dc._data_dependencies, id, path.c_str() + 4);
  403. } else { // Assume regular dependency
  404. add_dependency_internal(dc._data_dependencies, id, path.c_str());
  405. }
  406. }
  407. }
  408. }
  409. static s32 write_data_index(FilesystemDisk &data_fs, const char *filename, const HashMap<StringId64, DynamicString> &index)
  410. {
  411. StringStream ss(default_allocator());
  412. DynamicString temp_filename(default_allocator());
  413. File *file = data_fs.open_temporary(temp_filename);
  414. if (file->is_open()) {
  415. auto cur = hash_map::begin(index);
  416. auto end = hash_map::end(index);
  417. for (; cur != end; ++cur) {
  418. HASH_MAP_SKIP_HOLE(index, cur);
  419. TempAllocator256 ta;
  420. DynamicString str(ta);
  421. str.from_string_id(cur->first);
  422. ss << "\"" << str.c_str() << "\" = \"" << cur->second.c_str() << "\"\n";
  423. }
  424. u32 ss_len = strlen32(string_stream::c_str(ss));
  425. if (ss_len != file->write(string_stream::c_str(ss), ss_len)) {
  426. data_fs.close(*file);
  427. return -1;
  428. }
  429. }
  430. data_fs.close(*file);
  431. RenameResult rr = data_fs.rename(temp_filename.c_str(), filename);
  432. return rr.error == RenameResult::SUCCESS ? 0 : -1;
  433. }
  434. static s32 write_data_versions(FilesystemDisk &data_fs, const char *filename, const HashMap<DynamicString, u32> &versions)
  435. {
  436. StringStream ss(default_allocator());
  437. DynamicString temp_filename(default_allocator());
  438. File *file = data_fs.open_temporary(temp_filename);
  439. if (file->is_open()) {
  440. auto cur = hash_map::begin(versions);
  441. auto end = hash_map::end(versions);
  442. for (; cur != end; ++cur) {
  443. HASH_MAP_SKIP_HOLE(versions, cur);
  444. ss << cur->first.c_str() << " = " << cur->second << "\n";
  445. }
  446. u32 ss_len = strlen32(string_stream::c_str(ss));
  447. if (ss_len != file->write(string_stream::c_str(ss), ss_len)) {
  448. data_fs.close(*file);
  449. return -1;
  450. }
  451. }
  452. data_fs.close(*file);
  453. RenameResult rr = data_fs.rename(temp_filename.c_str(), filename);
  454. return rr.error == RenameResult::SUCCESS ? 0 : -1;
  455. }
  456. static s32 write_data_mtimes(FilesystemDisk &data_fs, const char *filename, const HashMap<StringId64, u64> &mtimes)
  457. {
  458. StringStream ss(default_allocator());
  459. DynamicString temp_filename(default_allocator());
  460. File *file = data_fs.open_temporary(temp_filename);
  461. if (file->is_open()) {
  462. auto cur = hash_map::begin(mtimes);
  463. auto end = hash_map::end(mtimes);
  464. for (; cur != end; ++cur) {
  465. HASH_MAP_SKIP_HOLE(mtimes, cur);
  466. TempAllocator64 ta;
  467. DynamicString key(ta);
  468. key.from_string_id(cur->first);
  469. ss << "\"" << key.c_str() << "\" = \"" << cur->second << "\"\n";
  470. }
  471. u32 ss_len = strlen32(string_stream::c_str(ss));
  472. if (ss_len != file->write(string_stream::c_str(ss), ss_len)) {
  473. data_fs.close(*file);
  474. return -1;
  475. }
  476. }
  477. data_fs.close(*file);
  478. RenameResult rr = data_fs.rename(temp_filename.c_str(), filename);
  479. return rr.error == RenameResult::SUCCESS ? 0 : -1;
  480. }
  481. static s32 write_data_dependencies(FilesystemDisk &data_fs, const char *filename, const HashMap<StringId64, DynamicString> &index, const HashMap<StringId64, HashMap<DynamicString, u32>> &dependencies, const HashMap<StringId64, HashMap<DynamicString, u32>> &requirements)
  482. {
  483. StringStream ss(default_allocator());
  484. DynamicString temp_filename(default_allocator());
  485. File *file = data_fs.open_temporary(temp_filename);
  486. if (file->is_open()) {
  487. auto cur = hash_map::begin(index);
  488. auto end = hash_map::end(index);
  489. for (; cur != end; ++cur) {
  490. HASH_MAP_SKIP_HOLE(index, cur);
  491. HashMap<DynamicString, u32> deps_deffault(default_allocator());
  492. const HashMap<DynamicString, u32> &deps = hash_map::get(dependencies, cur->first, deps_deffault);
  493. HashMap<DynamicString, u32> reqs_deffault(default_allocator());
  494. const HashMap<DynamicString, u32> &reqs = hash_map::get(requirements, cur->first, reqs_deffault);
  495. // Skip if data has no dependencies
  496. if (hash_map::size(deps) == 0 && hash_map::size(reqs) == 0)
  497. continue;
  498. TempAllocator64 ta;
  499. DynamicString key(ta);
  500. key.from_string_id(cur->first);
  501. ss << "\"" << key.c_str() << "\" = [\n";
  502. // Write all dependencies
  503. auto deps_cur = hash_map::begin(deps);
  504. auto deps_end = hash_map::end(deps);
  505. for (; deps_cur != deps_end; ++deps_cur) {
  506. HASH_MAP_SKIP_HOLE(deps, deps_cur);
  507. ss << " \"//- " << deps_cur->first.c_str() << "\"\n";
  508. }
  509. // Write all requirements
  510. auto reqs_cur = hash_map::begin(reqs);
  511. auto reqs_end = hash_map::end(reqs);
  512. for (; reqs_cur != reqs_end; ++reqs_cur) {
  513. HASH_MAP_SKIP_HOLE(reqs, reqs_cur);
  514. ss << " \"//r " << reqs_cur->first.c_str() << "\"\n";
  515. }
  516. ss << "]\n";
  517. }
  518. u32 ss_len = strlen32(string_stream::c_str(ss));
  519. if (ss_len != file->write(string_stream::c_str(ss), ss_len)) {
  520. data_fs.close(*file);
  521. return -1;
  522. }
  523. }
  524. data_fs.close(*file);
  525. RenameResult rr = data_fs.rename(temp_filename.c_str(), filename);
  526. return rr.error == RenameResult::SUCCESS ? 0 : -1;
  527. }
  528. DataCompiler::DataCompiler(const DeviceOptions &opts, ConsoleServer &cs)
  529. : _options(&opts)
  530. , _console_server(&cs)
  531. , _source_fs(default_allocator())
  532. , _source_dirs(default_allocator())
  533. , _compilers(default_allocator())
  534. , _globs(default_allocator())
  535. , _data_index(default_allocator())
  536. , _data_mtimes(default_allocator())
  537. , _data_dependencies(default_allocator())
  538. , _data_requirements(default_allocator())
  539. , _data_versions(default_allocator())
  540. , _file_monitor(default_allocator())
  541. , _data_revisions(default_allocator())
  542. , _revision(0)
  543. {
  544. cs.register_message_type("compile", console_command_compile, this);
  545. cs.register_message_type("quit", console_command_quit, this);
  546. cs.register_message_type("refresh_list", console_command_refresh_list, this);
  547. }
  548. DataCompiler::~DataCompiler()
  549. {
  550. if (_options->_server)
  551. _file_monitor.stop();
  552. }
  553. void DataCompiler::add_file(const char *path)
  554. {
  555. // Get source directory prefix
  556. TempAllocator512 ta;
  557. DynamicString source_dir(ta);
  558. source_dir = hash_map::get(_source_dirs, source_dir, source_dir);
  559. // Convert to DynamicString
  560. DynamicString str(ta);
  561. str.set(path, strlen32(path));
  562. Stat deff_st;
  563. deff_st.file_type = Stat::NO_ENTRY;
  564. deff_st.size = 0;
  565. deff_st.mtime = 0;
  566. Stat prev_st = hash_map::get(_source_index._paths, str, deff_st);
  567. // Get file status.
  568. FilesystemDisk fs(default_allocator());
  569. fs.set_prefix(source_dir.c_str());
  570. Stat st;
  571. st = fs.stat(path);
  572. hash_map::set(_source_index._paths, str, st);
  573. // Avoid sending spurious add_file() notifications for already known paths.
  574. if (prev_st.file_type == Stat::NO_ENTRY)
  575. notify_add_file(path, st);
  576. }
  577. void DataCompiler::remove_file(const char *path)
  578. {
  579. TempAllocator512 ta;
  580. DynamicString path_str(ta);
  581. path_str.set(path, strlen32(path));
  582. // Mark the entry as deleted but do not remove it from the map. We still
  583. // need to know which resource has been deleted in order to remove its
  584. // associated BLOB in the data directory at the next compile() call.
  585. Stat st;
  586. st.file_type = Stat::NO_ENTRY;
  587. st.size = 0;
  588. st.mtime = 0;
  589. hash_map::set(_source_index._paths, path_str, st);
  590. notify_remove_file(path);
  591. }
  592. void DataCompiler::add_tree(const char *path)
  593. {
  594. notify_add_tree(path);
  595. }
  596. void DataCompiler::remove_tree(const char *path)
  597. {
  598. TempAllocator512 ta;
  599. DynamicString tree_path(ta);
  600. tree_path = path;
  601. tree_path += '/';
  602. Vector<DynamicString> dangling_paths(default_allocator());
  603. auto cur = hash_map::begin(_source_index._paths);
  604. auto end = hash_map::end(_source_index._paths);
  605. for (; cur != end; ++cur) {
  606. HASH_MAP_SKIP_HOLE(_source_index._paths, cur);
  607. // Skip if it is not a sub-path
  608. if (!cur->first.has_prefix(tree_path.c_str()))
  609. continue;
  610. // Skip if is has been deleted previously
  611. if (cur->second.file_type == Stat::NO_ENTRY)
  612. continue;
  613. vector::push_back(dangling_paths, cur->first);
  614. }
  615. for (u32 ii = 0; ii < vector::size(dangling_paths); ++ii)
  616. remove_file(dangling_paths[ii].c_str());
  617. notify_remove_tree(path);
  618. }
  619. void DataCompiler::remove_file_or_tree(const char *path)
  620. {
  621. TempAllocator512 ta;
  622. DynamicString path_str(ta);
  623. path_str = path;
  624. if (hash_map::has(_source_index._paths, path_str))
  625. remove_file(path);
  626. else
  627. remove_tree(path);
  628. }
  629. void DataCompiler::map_source_dir(const char *name, const char *source_dir)
  630. {
  631. TempAllocator256 ta;
  632. DynamicString sname(ta);
  633. DynamicString sdir(ta);
  634. sname.set(name, strlen32(name));
  635. sdir.set(source_dir, strlen32(source_dir));
  636. hash_map::set(_source_dirs, sname, sdir);
  637. }
  638. void DataCompiler::source_dir(const char *resource_name, DynamicString &source_dir)
  639. {
  640. const char *slash = strchr(resource_name, '/');
  641. TempAllocator256 ta;
  642. DynamicString source_name(ta);
  643. if (slash != NULL)
  644. source_name.set(resource_name, u32(slash - resource_name));
  645. else
  646. source_name.set("", 0);
  647. DynamicString deffault(ta);
  648. DynamicString empty(ta);
  649. empty = "";
  650. deffault = hash_map::get(_source_dirs, empty, empty);
  651. source_dir = hash_map::get(_source_dirs, source_name, deffault);
  652. }
  653. void DataCompiler::add_ignore_glob(const char *glob)
  654. {
  655. TempAllocator64 ta;
  656. DynamicString str(ta);
  657. str.set(glob, strlen32(glob));
  658. vector::push_back(_globs, str);
  659. }
  660. void DataCompiler::scan_and_restore(const char *data_dir)
  661. {
  662. // Scan all source directories
  663. s64 time_start = time::now();
  664. // FIXME: refactor this whole garbage
  665. Array<const char *> directories(default_allocator());
  666. auto cur = hash_map::begin(_source_dirs);
  667. auto end = hash_map::end(_source_dirs);
  668. for (; cur != end; ++cur) {
  669. HASH_MAP_SKIP_HOLE(_source_dirs, cur);
  670. DynamicString prefix(default_allocator());
  671. path::join(prefix, cur->second.c_str(), cur->first.c_str());
  672. char *str = (char *)default_allocator().allocate(prefix.length() + 1);
  673. strcpy(str, prefix.c_str());
  674. array::push_back(directories, (const char *)str);
  675. _source_fs.set_prefix(prefix.c_str());
  676. File *file = _source_fs.open(CROWN_DATAIGNORE, FileOpenMode::READ);
  677. if (file->is_open()) {
  678. const u32 size = file->size();
  679. char *data = (char *)default_allocator().allocate(size + 1);
  680. file->read(data, size);
  681. data[size] = '\0';
  682. LineReader lr(data);
  683. while (!lr.eof()) {
  684. TempAllocator512 ta;
  685. DynamicString line(ta);
  686. lr.read_line(line);
  687. line.trim();
  688. if (line.empty() || line.has_prefix("#"))
  689. continue;
  690. add_ignore_glob(line.c_str());
  691. }
  692. default_allocator().deallocate(data);
  693. }
  694. _source_fs.close(*file);
  695. }
  696. _source_index.scan(_source_dirs);
  697. logi(DATA_COMPILER, "Scanned data in " TIME_FMT, time::seconds(time::now() - time_start));
  698. // Restore state from previous run
  699. time_start = time::now();
  700. FilesystemDisk data_fs(default_allocator());
  701. data_fs.set_prefix(data_dir);
  702. read_data_index(_data_index, data_fs, _source_index, CROWN_DATA_INDEX);
  703. read_data_mtimes(_data_mtimes, data_fs, _data_index, CROWN_DATA_MTIMES);
  704. read_data_dependencies(*this, data_fs, _data_index, CROWN_DATA_DEPENDENCIES);
  705. read_data_versions(_data_versions, data_fs, CROWN_DATA_VERSIONS);
  706. logi(DATA_COMPILER, "Restored state in " TIME_FMT, time::seconds(time::now() - time_start));
  707. if (_options->_server) {
  708. // Start file monitor
  709. time_start = time::now();
  710. _file_monitor.start(array::size(directories)
  711. , array::begin(directories)
  712. , true
  713. , file_monitor_callback
  714. , this
  715. );
  716. logi(DATA_COMPILER, "Started file monitor in " TIME_FMT, time::seconds(time::now() - time_start));
  717. }
  718. // Cleanup
  719. for (u32 i = 0, n = array::size(directories); i < n; ++i)
  720. default_allocator().deallocate((void *)directories[n - 1 - i]);
  721. }
  722. bool DataCompiler::dependency_changed(const DynamicString &path, ResourceId id, u64 dst_mtime)
  723. {
  724. Stat st;
  725. st.file_type = Stat::FileType::NO_ENTRY;
  726. st.size = 0;
  727. st.mtime = 0;
  728. st = hash_map::get(_source_index._paths, path, st);
  729. if (st.file_type == Stat::FileType::NO_ENTRY || st.mtime > dst_mtime)
  730. return true;
  731. const HashMap<DynamicString, u32> deffault(default_allocator());
  732. const HashMap<DynamicString, u32> &deps = hash_map::get(_data_dependencies, id, deffault);
  733. auto cur = hash_map::begin(deps);
  734. auto end = hash_map::end(deps);
  735. for (; cur != end; ++cur) {
  736. HASH_MAP_SKIP_HOLE(deps, cur);
  737. if (path == cur->first)
  738. continue;
  739. if (dependency_changed(cur->first, resource_id(cur->first.c_str()), dst_mtime))
  740. return true;
  741. }
  742. return false;
  743. }
  744. bool DataCompiler::version_changed(const DynamicString &path, ResourceId id)
  745. {
  746. const char *type = resource_type(path.c_str());
  747. if (data_version_stored(type) != data_version(type))
  748. return true;
  749. const HashMap<DynamicString, u32> deffault(default_allocator());
  750. const HashMap<DynamicString, u32> &deps = hash_map::get(_data_dependencies, id, deffault);
  751. auto cur = hash_map::begin(deps);
  752. auto end = hash_map::end(deps);
  753. for (; cur != end; ++cur) {
  754. HASH_MAP_SKIP_HOLE(deps, cur);
  755. if (path == cur->first)
  756. continue;
  757. if (version_changed(cur->first, resource_id(cur->first.c_str())))
  758. return true;
  759. }
  760. return false;
  761. }
  762. bool DataCompiler::path_matches_ignore_glob(const char *path)
  763. {
  764. for (u32 ii = 0, nn = vector::size(_globs); ii < nn; ++ii) {
  765. if (wildcmp(_globs[ii].c_str(), path))
  766. return true;
  767. }
  768. return false;
  769. }
  770. bool DataCompiler::path_is_special(const char *path)
  771. {
  772. return strcmp(path, "_level_editor_test.level") == 0
  773. || strcmp(path, "_level_editor_test.package") == 0
  774. ;
  775. }
  776. bool DataCompiler::compile(const char *data_dir, const char *platform_name)
  777. {
  778. s64 time_start = time::now();
  779. Platform::Enum platform = Platform::COUNT;
  780. for (u32 ii = 0; ii < countof(platform_info); ++ii) {
  781. if (strcmp(platform_info[ii].name, platform_name) == 0)
  782. platform = platform_info[ii].type;
  783. }
  784. if (platform == Platform::COUNT) {
  785. loge(DATA_COMPILER, "Cannot compile data for unknown platform `%s`", platform_name);
  786. return false;
  787. }
  788. FilesystemDisk data_fs(default_allocator());
  789. data_fs.set_prefix(data_dir);
  790. // Create the data directory on disk.
  791. CreateResult cr;
  792. cr = data_fs.create_directory("");
  793. if (cr.error == CreateResult::SUCCESS || cr.error == CreateResult::ALREADY_EXISTS) {
  794. if (cr.error == CreateResult::SUCCESS) {
  795. // Either the data directory has never been created before or it has
  796. // been deleted while the data compiler was running. In both cases reset
  797. // the tracking structures to force a full compile.
  798. hash_map::clear(_data_index);
  799. hash_map::clear(_data_mtimes);
  800. hash_map::clear(_data_dependencies);
  801. hash_map::clear(_data_requirements);
  802. hash_map::clear(_data_versions);
  803. }
  804. // Create sub-directories.
  805. data_fs.create_directory(CROWN_DATA_DIRECTORY);
  806. data_fs.create_directory(CROWN_TEMP_DIRECTORY);
  807. } else {
  808. loge(DATA_COMPILER, "Failed to create the data directory: `%s`", data_dir);
  809. return false;
  810. }
  811. // Find the set of resources to be compiled, removed etc.
  812. Vector<DynamicString> to_compile(default_allocator());
  813. Vector<DynamicString> to_remove(default_allocator());
  814. auto cur = hash_map::begin(_source_index._paths);
  815. auto end = hash_map::end(_source_index._paths);
  816. for (; cur != end; ++cur) {
  817. HASH_MAP_SKIP_HOLE(_source_index._paths, cur);
  818. const DynamicString &path = cur->first;
  819. if (cur->second.file_type == Stat::NO_ENTRY) {
  820. vector::push_back(to_remove, path);
  821. } else {
  822. if (path_matches_ignore_glob(path.c_str()))
  823. continue;
  824. if (resource_type(path.c_str()) == NULL)
  825. continue;
  826. const ResourceId id = resource_id(path.c_str());
  827. const u64 mtime_epoch = 0u;
  828. const u64 mtime = hash_map::get(_data_mtimes, id, mtime_epoch);
  829. bool source_never_compiled_before = hash_map::has(_data_index, id) == false;
  830. bool source_dependency_changed = dependency_changed(path, id, mtime);
  831. bool data_version_dependency_changed = version_changed(path, id);
  832. if (source_never_compiled_before
  833. || source_dependency_changed
  834. || data_version_dependency_changed
  835. ) {
  836. vector::push_back(to_compile, path);
  837. }
  838. }
  839. }
  840. #if 0
  841. for (u32 i = 0; i < vector::size(to_remove); ++i)
  842. logi(DATA_COMPILER, "gc %s", to_remove[i].c_str());
  843. #endif
  844. // Remove all deleted resources
  845. for (u32 i = 0; i < vector::size(to_remove); ++i) {
  846. // Remove from source index
  847. hash_map::remove(_source_index._paths, to_remove[i]);
  848. // If it does not have extension it cannot be a resource so it cannot be
  849. // in tracking structures nor in the data folder.
  850. if (resource_type(to_remove[i].c_str()) == NULL)
  851. continue;
  852. // Remove from tracking structures
  853. ResourceId id = resource_id(to_remove[i].c_str());
  854. hash_map::remove(_data_index, id);
  855. hash_map::remove(_data_mtimes, id);
  856. hash_map::remove(_data_dependencies, id);
  857. hash_map::remove(_data_requirements, id);
  858. // If present, remove from data folder because we do not want the
  859. // runtime to accidentally load stale data compiled from resources that
  860. // do not exist anymore in the source index.
  861. TempAllocator256 ta;
  862. DynamicString dest(ta);
  863. destination_path(dest, id);
  864. data_fs.delete_file(dest.c_str());
  865. }
  866. // Sort to_compile so that ".package" resources get compiled last
  867. std::sort(vector::begin(to_compile)
  868. , vector::end(to_compile)
  869. , [](const DynamicString &resource_a, const DynamicString &resource_b) {
  870. #define PACKAGE ".package"
  871. if (resource_a.has_suffix(PACKAGE) && !resource_b.has_suffix(PACKAGE))
  872. return false;
  873. if (!resource_a.has_suffix(PACKAGE) && resource_b.has_suffix(PACKAGE))
  874. return true;
  875. return resource_a < resource_b;
  876. #undef PACKAGE
  877. });
  878. bool success = true;
  879. // Compile all changed resources
  880. for (u32 i = 0; i < vector::size(to_compile); ++i) {
  881. const DynamicString &path = to_compile[i];
  882. const char *type = resource_type(path.c_str());
  883. if (type == NULL || !can_compile(type))
  884. continue;
  885. logi(DATA_COMPILER, _options->_server ? RESOURCE_ID_FMT_STR : "%s", path.c_str());
  886. // Build destination file path
  887. ResourceId id = resource_id(path.c_str());
  888. TempAllocator256 ta;
  889. DynamicString dest(ta);
  890. destination_path(dest, id);
  891. // Compile data.
  892. ResourceTypeData rtd;
  893. rtd.version = 0;
  894. rtd.compiler = NULL;
  895. DynamicString type_str(ta);
  896. type_str = type;
  897. // Dependencies and requirements lists must be regenerated each time
  898. // the resource is being compiled. For example, if you delete
  899. // "foo.unit" from a package, you do not want the list of
  900. // requirements to include "foo.unit" again the next time that
  901. // package is compiled.
  902. HashMap<DynamicString, u32> new_dependencies(default_allocator());
  903. HashMap<DynamicString, u32> new_requirements(default_allocator());
  904. Buffer output(default_allocator());
  905. FileBuffer file_buffer(output);
  906. CompileOptions opts(file_buffer
  907. , new_dependencies
  908. , new_requirements
  909. , *this
  910. , data_fs
  911. , data_fs
  912. , id
  913. , path
  914. , platform
  915. , false
  916. );
  917. // Invoke compiler.
  918. rtd = hash_map::get(_compilers, type_str, rtd);
  919. success = rtd.compiler(opts) == 0;
  920. if (success) {
  921. // Update dependencies and requirements only if compiler(opts)
  922. // succeeded. If the compilation fails due to a missing
  923. // dependency and you update the dependency database with new
  924. // partial data, the next call to compile() would not trigger a
  925. // recompilation.
  926. HashMap<DynamicString, u32> dependencies_deffault(default_allocator());
  927. hash_map::clear(hash_map::get(_data_dependencies, id, dependencies_deffault));
  928. HashMap<DynamicString, u32> requirements_deffault(default_allocator());
  929. hash_map::clear(hash_map::get(_data_requirements, id, requirements_deffault));
  930. hash_map::set(_data_dependencies, id, new_dependencies);
  931. hash_map::set(_data_requirements, id, new_requirements);
  932. // Write data to disk.
  933. DynamicString temp_dest(default_allocator());
  934. File *outf = data_fs.open_temporary(temp_dest);
  935. if (outf->is_open()) {
  936. u32 size = array::size(output);
  937. u32 written = outf->write(array::begin(output), size);
  938. success = size == written;
  939. } else {
  940. loge(DATA_COMPILER, "Failed to write data to disk");
  941. success = false;
  942. }
  943. data_fs.close(*outf);
  944. if (success) {
  945. RenameResult rr = data_fs.rename(temp_dest.c_str(), dest.c_str());
  946. success = rr.error == RenameResult::SUCCESS;
  947. }
  948. }
  949. if (success) {
  950. // Do not include special paths in content tracking structures.
  951. if (!path_is_special(path.c_str())) {
  952. hash_map::set(_data_index, id, path);
  953. hash_map::set(_data_mtimes, id, data_fs.last_modified_time(dest.c_str()));
  954. hash_map::set(_data_revisions, id, _revision + 1);
  955. }
  956. } else {
  957. loge(DATA_COMPILER, "Failed to compile data");
  958. break;
  959. }
  960. }
  961. if (success) {
  962. // Data versions are stored per-type, so, before updating _data_versions, we
  963. // need to make sure *all* resource files with that type have been
  964. // successfully compiled.
  965. auto cur = hash_map::begin(_compilers);
  966. auto end = hash_map::end(_compilers);
  967. for (; cur != end; ++cur) {
  968. HASH_MAP_SKIP_HOLE(_compilers, cur);
  969. hash_map::set(_data_versions, cur->first, cur->second.version);
  970. }
  971. if (vector::size(to_compile)) {
  972. _revision++;
  973. logi(DATA_COMPILER, "Compiled data (rev %u) in " TIME_FMT, _revision, time::seconds(time::now() - time_start));
  974. } else {
  975. logi(DATA_COMPILER, "Data is up to date");
  976. }
  977. if (_options->_do_bundle) {
  978. time_start = time::now();
  979. // Find the set of resources to be compiled, removed etc.
  980. Vector<DynamicString> to_bundle(default_allocator());
  981. // Find all packages.
  982. auto cur = hash_map::begin(_source_index._paths);
  983. auto end = hash_map::end(_source_index._paths);
  984. for (; cur != end; ++cur) {
  985. HASH_MAP_SKIP_HOLE(_source_index._paths, cur);
  986. const DynamicString &path = cur->first;
  987. if (path.has_suffix(".package") || path.has_suffix(".config"))
  988. vector::push_back(to_bundle, path);
  989. }
  990. FilesystemDisk data_fs(default_allocator());
  991. data_fs.set_prefix(data_dir);
  992. const char *bundle_dir = _options->_bundle_dir.c_str();
  993. // Create the bundle directory on disk.
  994. FilesystemDisk bundle_fs(default_allocator());
  995. bundle_fs.set_prefix(bundle_dir);
  996. cr = bundle_fs.create_directory("");
  997. if (cr.error == CreateResult::SUCCESS || cr.error == CreateResult::ALREADY_EXISTS) {
  998. bundle_fs.create_directory(CROWN_DATA_DIRECTORY);
  999. } else {
  1000. loge(DATA_COMPILER, "Failed to create the bundle directory: `%s`", bundle_dir);
  1001. return false;
  1002. }
  1003. for (u32 ii = 0; ii < vector::size(to_bundle); ++ii) {
  1004. const DynamicString &path = to_bundle[ii];
  1005. logi(DATA_COMPILER, _options->_server ? RESOURCE_ID_FMT_STR : "%s", path.c_str());
  1006. ResourceId id = resource_id(path.c_str());
  1007. TempAllocator256 ta;
  1008. DynamicString dest(ta);
  1009. destination_path(dest, id);
  1010. // Bundle data.
  1011. ResourceTypeData rtd;
  1012. rtd.version = 0;
  1013. rtd.compiler = NULL;
  1014. DynamicString type_str(ta);
  1015. type_str = resource_type(path.c_str());
  1016. HashMap<DynamicString, u32> new_dependencies(default_allocator());
  1017. HashMap<DynamicString, u32> new_requirements(default_allocator());
  1018. Buffer output(default_allocator());
  1019. FileBuffer file_buffer(output);
  1020. CompileOptions opts(file_buffer
  1021. , new_dependencies
  1022. , new_requirements
  1023. , *this
  1024. , bundle_fs
  1025. , data_fs
  1026. , id
  1027. , path
  1028. , platform
  1029. , true
  1030. );
  1031. // Invoke compiler.
  1032. rtd = hash_map::get(_compilers, type_str, rtd);
  1033. success = rtd.compiler(opts) == 0;
  1034. if (success) {
  1035. // Write data to disk.
  1036. DynamicString temp_dest(default_allocator());
  1037. File *outf = bundle_fs.open_temporary(temp_dest);
  1038. if (outf->is_open()) {
  1039. u32 size = array::size(output);
  1040. u32 written = outf->write(array::begin(output), size);
  1041. success = size == written;
  1042. } else {
  1043. loge(DATA_COMPILER, "Failed to write data to disk");
  1044. success = false;
  1045. }
  1046. bundle_fs.close(*outf);
  1047. if (success) {
  1048. RenameResult rr = bundle_fs.rename(temp_dest.c_str(), dest.c_str());
  1049. success = rr.error == RenameResult::SUCCESS;
  1050. }
  1051. }
  1052. if (!success) {
  1053. loge(DATA_COMPILER, "Failed to generate bundle");
  1054. break;
  1055. }
  1056. }
  1057. if (success) {
  1058. if (vector::size(to_bundle)) {
  1059. logi(DATA_COMPILER, "Bundled data in " TIME_FMT, time::seconds(time::now() - time_start));
  1060. } else {
  1061. logi(DATA_COMPILER, "Bundles are up to date");
  1062. }
  1063. }
  1064. }
  1065. }
  1066. // Save state to disk.
  1067. s32 res = write_data_index(data_fs, CROWN_DATA_INDEX, _data_index);
  1068. if (res != 0) {
  1069. loge(DATA_COMPILER, "Failed to save: %s", CROWN_DATA_INDEX);
  1070. return false;
  1071. }
  1072. res = write_data_mtimes(data_fs, CROWN_DATA_MTIMES, _data_mtimes);
  1073. if (res != 0) {
  1074. loge(DATA_COMPILER, "Failed to save: %s", CROWN_DATA_MTIMES);
  1075. return false;
  1076. }
  1077. res = write_data_dependencies(data_fs, CROWN_DATA_DEPENDENCIES, _data_index, _data_dependencies, _data_requirements);
  1078. if (res != 0) {
  1079. loge(DATA_COMPILER, "Failed to save: %s", CROWN_DATA_DEPENDENCIES);
  1080. return false;
  1081. }
  1082. res = write_data_versions(data_fs, CROWN_DATA_VERSIONS, _data_versions);
  1083. if (res != 0) {
  1084. loge(DATA_COMPILER, "Failed to save: %s", CROWN_DATA_VERSIONS);
  1085. return false;
  1086. }
  1087. return success;
  1088. }
  1089. void DataCompiler::register_compiler(const char *type, u32 version, CompileFunction compiler)
  1090. {
  1091. TempAllocator64 ta;
  1092. DynamicString type_str(ta);
  1093. type_str = type;
  1094. CE_ASSERT(!hash_map::has(_compilers, type_str), "Type already registered");
  1095. CE_ENSURE(NULL != compiler);
  1096. ResourceTypeData rtd;
  1097. rtd.version = version;
  1098. rtd.compiler = compiler;
  1099. hash_map::set(_compilers, type_str, rtd);
  1100. }
  1101. u32 DataCompiler::data_version(const char *type)
  1102. {
  1103. TempAllocator64 ta;
  1104. DynamicString type_str(ta);
  1105. type_str = type;
  1106. ResourceTypeData rtd;
  1107. rtd.version = COMPILER_NOT_FOUND;
  1108. rtd.compiler = NULL;
  1109. return hash_map::get(_compilers, type_str, rtd).version;
  1110. }
  1111. u32 DataCompiler::data_version_stored(const char *type)
  1112. {
  1113. TempAllocator256 ta;
  1114. DynamicString ds(ta);
  1115. ds = type;
  1116. u32 version = UINT32_MAX;
  1117. return hash_map::get(_data_versions, ds, version);
  1118. }
  1119. bool DataCompiler::can_compile(const char *type)
  1120. {
  1121. TempAllocator64 ta;
  1122. DynamicString type_str(ta);
  1123. type_str = type;
  1124. return hash_map::has(_compilers, type_str);
  1125. }
  1126. void DataCompiler::error(const char *msg, va_list args)
  1127. {
  1128. vloge(DATA_COMPILER, msg, args);
  1129. }
  1130. /// Converts @a path to the corresponding resource name.
  1131. /// On Linux, no transformation is needed. On Windows,
  1132. /// backslashes are converted to slashes.
  1133. static void resource_path_to_resource_name(DynamicString &resource_name, const DynamicString &path)
  1134. {
  1135. for (u32 i = 0, n = path.length(); i < n; ++i) {
  1136. if (path._data[i] == '\\')
  1137. resource_name += '/';
  1138. else
  1139. resource_name += path._data[i];
  1140. }
  1141. }
  1142. void DataCompiler::file_monitor_callback(FileMonitorEvent::Enum fme, bool is_dir, const char *path, const char *path_renamed)
  1143. {
  1144. TempAllocator512 ta;
  1145. DynamicString source_dir(ta);
  1146. DynamicString resource_path(ta); // Same as resource_name but with OS-dependent directory separators
  1147. DynamicString resource_name(ta);
  1148. // Find source directory by matching mapped
  1149. // directory prefix with `path`.
  1150. auto cur = hash_map::begin(_source_dirs);
  1151. auto end = hash_map::end(_source_dirs);
  1152. for (; cur != end; ++cur) {
  1153. HASH_MAP_SKIP_HOLE(_source_dirs, cur);
  1154. path::join(source_dir, cur->second.c_str(), cur->first.c_str());
  1155. if (str_has_prefix(path, source_dir.c_str()))
  1156. break;
  1157. }
  1158. if (cur != end) {
  1159. // All events received must refer to directories
  1160. // mapped with map_source_dir().
  1161. const char *filename = &path[source_dir.length() + 1];
  1162. path::join(resource_path, cur->first.c_str(), filename);
  1163. resource_path_to_resource_name(resource_name, resource_path);
  1164. #if 0
  1165. static const char *fme_to_name[] = { "CREATED", "DELETED", "RENAMED", "CHANGED" };
  1166. CE_STATIC_ASSERT(countof(fme_to_name) == FileMonitorEvent::COUNT);
  1167. logi(DATA_COMPILER, "file_monitor_callback: event: %s %s", fme_to_name[fme], is_dir ? "dir" : "file");
  1168. logi(DATA_COMPILER, " path : %s", path);
  1169. if (fme == FileMonitorEvent::RENAMED)
  1170. logi(DATA_COMPILER, " path_renamed : %s", path_renamed);
  1171. logi(DATA_COMPILER, " source_dir : %s", source_dir.c_str());
  1172. logi(DATA_COMPILER, " resource_path: %s", resource_path.c_str());
  1173. logi(DATA_COMPILER, " resource_name: %s", resource_name.c_str());
  1174. #endif
  1175. switch (fme) {
  1176. case FileMonitorEvent::CREATED:
  1177. if (!is_dir)
  1178. add_file(resource_name.c_str());
  1179. else
  1180. add_tree(resource_name.c_str());
  1181. break;
  1182. case FileMonitorEvent::DELETED:
  1183. remove_file_or_tree(resource_name.c_str());
  1184. break;
  1185. case FileMonitorEvent::RENAMED: {
  1186. DynamicString resource_path_renamed(ta); // See resource_path
  1187. DynamicString resource_name_renamed(ta);
  1188. path::join(resource_path_renamed, cur->first.c_str(), &path_renamed[source_dir.length() + 1]);
  1189. resource_path_to_resource_name(resource_name_renamed, resource_path_renamed);
  1190. if (!is_dir) {
  1191. remove_file(resource_name.c_str());
  1192. add_file(resource_name_renamed.c_str());
  1193. } else {
  1194. remove_tree(resource_name.c_str());
  1195. add_tree(resource_name_renamed.c_str());
  1196. }
  1197. break;
  1198. }
  1199. case FileMonitorEvent::CHANGED:
  1200. if (!is_dir) {
  1201. FilesystemDisk fs(default_allocator());
  1202. fs.set_prefix(source_dir.c_str());
  1203. Stat st;
  1204. st = fs.stat(filename);
  1205. hash_map::set(_source_index._paths, resource_name, st);
  1206. notify_change_file(resource_name.c_str(), st);
  1207. }
  1208. break;
  1209. default:
  1210. CE_FATAL("Unknown FileMonitorEvent: %d", fme);
  1211. break;
  1212. }
  1213. }
  1214. }
  1215. void DataCompiler::file_monitor_callback(void *thiz, FileMonitorEvent::Enum fme, bool is_dir, const char *path_original, const char *path_modified)
  1216. {
  1217. ((DataCompiler *)thiz)->file_monitor_callback(fme, is_dir, path_original, path_modified);
  1218. }
  1219. int main_data_compiler(const DeviceOptions &opts)
  1220. {
  1221. #if CROWN_PLATFORM_WINDOWS
  1222. // code-format off
  1223. PHANDLER_ROUTINE signal_handler = [](DWORD dwCtrlType) {
  1224. switch (dwCtrlType) {
  1225. case CTRL_C_EVENT:
  1226. _quit = true;
  1227. if (console_server())
  1228. console_server()->shutdown();
  1229. return TRUE;
  1230. default:
  1231. return FALSE;
  1232. }
  1233. };
  1234. SetConsoleCtrlHandler(signal_handler, TRUE);
  1235. #else
  1236. struct sigaction old_SIGINT;
  1237. struct sigaction old_SIGTERM;
  1238. struct sigaction act;
  1239. act.sa_handler = [](int signum) {
  1240. switch (signum)
  1241. {
  1242. case SIGINT:
  1243. case SIGTERM:
  1244. _quit = true;
  1245. if (console_server())
  1246. console_server()->shutdown();
  1247. break;
  1248. default:
  1249. break;
  1250. }
  1251. };
  1252. sigemptyset(&act.sa_mask);
  1253. act.sa_flags = 0;
  1254. sigaction(SIGINT, NULL, &old_SIGINT);
  1255. sigaction(SIGINT, &act, NULL);
  1256. sigaction(SIGTERM, NULL, &old_SIGTERM);
  1257. sigaction(SIGTERM, &act, NULL);
  1258. // code-format on
  1259. #endif // if CROWN_PLATFORM_WINDOWS
  1260. console_server_globals::init();
  1261. if (opts._server)
  1262. console_server()->listen(CROWN_DEFAULT_COMPILER_PORT, opts._wait_console);
  1263. namespace cor = config_resource_internal;
  1264. namespace ftr = font_resource_internal;
  1265. namespace lur = lua_resource_internal;
  1266. namespace lvr = level_resource_internal;
  1267. namespace mhr = mesh_resource_internal;
  1268. namespace mtr = material_resource_internal;
  1269. namespace pcr = physics_config_resource_internal;
  1270. namespace phr = physics_resource_internal;
  1271. namespace pkr = package_resource_internal;
  1272. namespace sar = sprite_animation_resource_internal;
  1273. namespace sdr = sound_resource_internal;
  1274. namespace shr = shader_resource_internal;
  1275. namespace smr = state_machine_internal;
  1276. namespace spr = sprite_resource_internal;
  1277. namespace txr = texture_resource_internal;
  1278. namespace utr = unit_resource_internal;
  1279. DataCompiler *dc = CE_NEW(default_allocator(), DataCompiler)(opts, *console_server());
  1280. dc->register_compiler("config", RESOURCE_VERSION_CONFIG, cor::compile);
  1281. dc->register_compiler("font", RESOURCE_VERSION_FONT, ftr::compile);
  1282. dc->register_compiler("level", RESOURCE_VERSION_LEVEL, lvr::compile);
  1283. dc->register_compiler("material", RESOURCE_VERSION_MATERIAL, mtr::compile);
  1284. dc->register_compiler("mesh", RESOURCE_VERSION_MESH, mhr::compile);
  1285. dc->register_compiler("package", RESOURCE_VERSION_PACKAGE, pkr::compile);
  1286. dc->register_compiler("physics_config", RESOURCE_VERSION_PHYSICS_CONFIG, pcr::compile);
  1287. dc->register_compiler("lua", RESOURCE_VERSION_SCRIPT, lur::compile);
  1288. dc->register_compiler("shader", RESOURCE_VERSION_SHADER, shr::compile);
  1289. dc->register_compiler("sound", RESOURCE_VERSION_SOUND, sdr::compile);
  1290. dc->register_compiler("sprite", RESOURCE_VERSION_SPRITE, spr::compile);
  1291. dc->register_compiler("sprite_animation", RESOURCE_VERSION_SPRITE_ANIMATION, sar::compile);
  1292. dc->register_compiler("state_machine", RESOURCE_VERSION_STATE_MACHINE, smr::compile);
  1293. dc->register_compiler("texture", RESOURCE_VERSION_TEXTURE, txr::compile);
  1294. dc->register_compiler("unit", RESOURCE_VERSION_UNIT, utr::compile);
  1295. dc->add_ignore_glob("*.bak");
  1296. dc->add_ignore_glob("*.dds");
  1297. dc->add_ignore_glob("*.goutputstream-*"); // https://askubuntu.com/questions/151101/why-are-goutputstream-xxxxx-files-created-in-home-folder
  1298. dc->add_ignore_glob("*.importer_settings");
  1299. dc->add_ignore_glob("*.ktx");
  1300. dc->add_ignore_glob("*.ogg");
  1301. dc->add_ignore_glob("*.png");
  1302. dc->add_ignore_glob("*.pvr");
  1303. dc->add_ignore_glob("*.swn"); // VIM swap file.
  1304. dc->add_ignore_glob("*.swo"); // VIM swap file.
  1305. dc->add_ignore_glob("*.swp"); // VIM swap file.
  1306. dc->add_ignore_glob("*.tga");
  1307. dc->add_ignore_glob("*.tmp");
  1308. dc->add_ignore_glob("*.wav");
  1309. dc->add_ignore_glob("*~");
  1310. dc->add_ignore_glob(".*");
  1311. dc->map_source_dir("", opts._source_dir.c_str());
  1312. if (opts._map_source_dir_name) {
  1313. dc->map_source_dir(opts._map_source_dir_name
  1314. , opts._map_source_dir_prefix.c_str()
  1315. );
  1316. }
  1317. dc->scan_and_restore(opts._data_dir.c_str());
  1318. bool success = true;
  1319. if (opts._server) {
  1320. while (!_quit) {
  1321. console_server()->execute_message_handlers(true);
  1322. }
  1323. } else {
  1324. success = dc->compile(opts._data_dir.c_str(), opts._platform);
  1325. }
  1326. CE_DELETE(default_allocator(), dc);
  1327. console_server_globals::shutdown();
  1328. // Restore original signal handlers.
  1329. #if CROWN_PLATFORM_WINDOWS
  1330. SetConsoleCtrlHandler(signal_handler, FALSE);
  1331. #else
  1332. sigaction(SIGINT, &old_SIGINT, NULL);
  1333. sigaction(SIGTERM, &old_SIGTERM, NULL);
  1334. #endif
  1335. return success ? EXIT_SUCCESS : EXIT_FAILURE;
  1336. }
  1337. } // namespace crown
  1338. #endif // if CROWN_CAN_COMPILE