diff --git a/apps/lua/data/packs/data.toml b/apps/lua/data/packs/data.toml new file mode 100644 index 0000000..5778b2a --- /dev/null +++ b/apps/lua/data/packs/data.toml @@ -0,0 +1,3 @@ +[[deps]] +source = "../../common-data" # where does it come from, might be an url +name = "common-data" # should be globally unique diff --git a/apps/lua/data/twn.toml b/apps/lua/data/twn.toml new file mode 100644 index 0000000..d22c227 --- /dev/null +++ b/apps/lua/data/twn.toml @@ -0,0 +1,11 @@ +[about] +title = "Lua Townengine!" +developer = "Somebody" +app_id = "twnlua" +dev_id = "somebody" + +[game] +base_render_width = 640 +base_render_height = 360 + +[engine] diff --git a/docs/packaging.txt b/docs/packaging.txt new file mode 100644 index 0000000..80518cb --- /dev/null +++ b/docs/packaging.txt @@ -0,0 +1,15 @@ +assets are distributed by packs, which can come in archived or folder form (for development purposes) + +one pack by the name of 'data' is always assumed to be present alongside game executable root, +whether in folder /data/ or /data.btw file, where precedence is taking over /data/ + +root 'data' should be used to point to other dependency packs in /packs/data.toml file + +--- +[[deps]] +source = "../../common-data" # where does it come from, might be an url +name = "common-data" # should be globally unique +--- + +they're mounted to / in the virtual file system by default, if same files are present in multiple packs, +only the one loaded first is visible diff --git a/src/twn_loop.c b/src/twn_loop.c index 520a6f5..de12118 100644 --- a/src/twn_loop.c +++ b/src/twn_loop.c @@ -188,6 +188,96 @@ static void main_loop(void) { } +/* TODO: cache and deny duplicates */ +/* TODO: ability to redefine mounting point for a dependency */ +/* TODO: ability to load external sources over HTTP if url is given */ +static void resolve_pack_dependencies(const char *pack_name) { + char *path; + if (SDL_asprintf(&path, "/packs/%s.toml", pack_name) == -1) { + CRY("resolve_pack_dependencies()", "Allocation error"); + goto ERR_PACK_MANIFEST_PATH_ALLOC_FAIL; + } + + if (!PHYSFS_exists(path)) + /* no package manifest provided, abort */ + goto OK_NO_MANIFEST; + + char *manifest_file = file_to_str(path); + if (!manifest_file) { + CRY_PHYSFS("Pack manifest file loading failed"); + goto ERR_PACK_MANIFEST_FILE_LOADING; + } + + char errbuf[256]; /* tomlc99 example implies that this is enough... */ + toml_table_t *manifest = toml_parse(manifest_file, errbuf, sizeof errbuf); + SDL_free(manifest_file); + + if (!manifest) { + CRY("Pack manifest decoding failed", errbuf); + goto ERR_PACK_MANIFEST_DECODING; + } + + toml_array_t *deps = toml_array_in(manifest, "deps"); + if (!deps) + goto OK_NO_DEPS; + + /* iterate over entries in [[deps]] array */ + /* TODO: use sub procedures for failable loops? so that error mechanism cleans well */ + toml_table_t *cur_dep = toml_table_at(deps, 0); + for (int idx = 0; cur_dep; ++idx, cur_dep = toml_table_at(deps, idx)) { + toml_datum_t dep_name = toml_string_in(cur_dep, "name"); + if (!dep_name.ok) { + log_warn("No 'name' is present in pack dependency, skipping it"); + continue; + } + + char *dep_pack_name; + if (SDL_asprintf(&dep_pack_name, "%s.btw", dep_name.u.s) == -1) { + CRY("resolve_pack_dependencies()", "Allocation error"); + SDL_free(dep_name.u.s); + goto ERR_DEP_PATH_ALLOC_FAIL; + } + + /* try loading pack */ + if (!PHYSFS_mount(dep_pack_name, "/", true)) { + /* if it failes, try going for source */ + toml_datum_t dep_source = toml_string_in(cur_dep, "source"); + if (!dep_source.ok) { + log_warn("No 'source' is present in pack %s, while %s.btw isn't present, skipping it", dep_name.u.s, dep_name.u.s); + SDL_free(dep_name.u.s); + SDL_free(dep_pack_name); + continue; + } + + if (!PHYSFS_mount(dep_source.u.s, "/", true)) + CRY("Cannot load pack", "Nothing is given to work with"); + + log_info("Pack loaded: %s\n", dep_source.u.s); + + SDL_free(dep_source.u.s); + } else + log_info("Pack loaded: %s\n", dep_pack_name); + + SDL_free(dep_pack_name); + + /* recursive resolution */ + resolve_pack_dependencies(dep_name.u.s); + + SDL_free(dep_name.u.s); + } + +ERR_DEP_PATH_ALLOC_FAIL: +OK_NO_DEPS: + toml_free(manifest); +ERR_PACK_MANIFEST_DECODING: +ERR_PACK_MANIFEST_FILE_LOADING: +OK_NO_MANIFEST: + SDL_free(path); +ERR_PACK_MANIFEST_PATH_ALLOC_FAIL: + return; +} + + static bool initialize(void) { if (SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC) == -1) { CRY_SDL("SDL initialization failed."); @@ -199,6 +289,9 @@ static bool initialize(void) { /* that is why PhysicsFS is initialized before anything else */ toml_set_memutil(SDL_malloc, SDL_free); + /* time to orderly resolve any dependencies present */ + resolve_pack_dependencies("data"); + /* load the config file into an opaque table */ { char *config_file = file_to_str("/twn.toml"); @@ -562,33 +655,16 @@ int enter_loop(int argc, char **argv) { /* needs to be done before anything else so config can be loaded */ /* TODO: ANDROID: see the warning in physicsfs PHYSFS_init docs/header */ - if (!PHYSFS_init(ctx.argv[0]) || !PHYSFS_mount(PHYSFS_getBaseDir(), NULL, true)) { + if (!PHYSFS_init(ctx.argv[0])) { CRY_PHYSFS("Filesystem initialization failed"); return EXIT_FAILURE; } - /* base dir is fine, but we'd like to look into all the data archives, too */ - char **files_here = PHYSFS_enumerateFiles("/"); - if (files_here == NULL) { - CRY_PHYSFS("Filesystem initialization failed"); - return EXIT_FAILURE; - } - for (char **ptr = files_here; *ptr != NULL; ++ptr) { - char *file = *ptr; - - if (!strends(file, "." PACKAGE_EXTENSION)) { - continue; - } - - if (!PHYSFS_mount(file, "/", true)) { - CRY_PHYSFS("Filesystem initialization failed"); - return EXIT_FAILURE; - } - } - PHYSFS_freeList(files_here); /* process arguments */ bool force_debug = false; bool force_release = false; + bool data_dir_mounted = false; + for (int i = 1; i < argc; ++i) { /* override data directory */ if (SDL_strcmp(argv[i], "--data-dir") == 0) { @@ -602,6 +678,8 @@ int enter_loop(int argc, char **argv) { return EXIT_FAILURE; } + data_dir_mounted = true; + continue; } @@ -618,6 +696,16 @@ int enter_loop(int argc, char **argv) { } } + /* data path not explicitly specified, look into convention defined places */ + if (!data_dir_mounted) { + /* try mouning data folder first, relative to executable root */ + if (!PHYSFS_mount("data", NULL, true)) { + if (!PHYSFS_mount("data.btw", NULL, true)) + CRY_PHYSFS("Cannot find data.btw or data directory in root. Please create them or specify with --data-dir parameter."); + return EXIT_FAILURE; + } + } + if (!initialize()) return EXIT_FAILURE; diff --git a/src/twn_util.c b/src/twn_util.c index 5f7c0d3..be4edcd 100644 --- a/src/twn_util.c +++ b/src/twn_util.c @@ -191,6 +191,8 @@ void textures_dump_atlases(void) { SDL_SaveBMP_RW(ctx.texture_cache.atlas_surfaces[i], handle, true); log_info("Dumped atlas %zu to %s", i, buf); + + SDL_free(buf); } }