static void load_packages(const char *dir, const char *unionmp) { // go over the packages in dir in alphapetical order and merge them into unionmp // e.g. files in 00-aaa.zip will be shadowed by files in 99-zzz.zip size_t numpaks = 0; char **paklist = vfs_dir_list_sorted(dir, &numpaks, vfs_dir_list_order_ascending, NULL); if(!paklist) { log_fatal("VFS error: %s", vfs_get_error()); } for(size_t i = 0; i < numpaks; ++i) { const char *entry = paklist[i]; struct pkg_loader_t *loader = find_loader(entry); if(loader == NULL) { continue; } log_info("Adding package: %s", entry); assert(loader->mount != NULL); char *tmp = strfmt("%s/%s", dir, entry); if(!loader->mount(unionmp, tmp)) { log_error("VFS error: %s", vfs_get_error()); } free(tmp); } vfs_dir_list_free(paklist, numpaks); }
bool vfs_mount_syspath(const char *mountpoint, const char *fspath, uint flags) { VFSNode *rdir = vfs_alloc(); if(!vfs_syspath_init(rdir, fspath)) { vfs_set_error("Can't initialize path: %s", vfs_get_error()); vfs_decref(rdir); return false; } if((flags & VFS_SYSPATH_MOUNT_MKDIR) && !vfs_node_mkdir(rdir, NULL)) { vfs_set_error("Can't create directory: %s", vfs_get_error()); vfs_decref(rdir); return false; } if(flags & VFS_SYSPATH_MOUNT_READONLY) { VFSNode *rdir_ro = vfs_ro_wrap(rdir); vfs_decref(rdir); rdir = rdir_ro; } return vfs_mount_or_decref(vfs_root, mountpoint, rdir); }
bool replay_save(Replay *rpy, const char *name) { char *p = replay_getpath(name, !strendswith(name, REPLAY_EXTENSION)); char *sp = vfs_repr(p, true); log_info("Saving %s", sp); free(sp); SDL_RWops *file = vfs_open(p, VFS_MODE_WRITE); free(p); if(!file) { log_warn("VFS error: %s", vfs_get_error()); return false; } bool result = replay_write(rpy, file, REPLAY_STRUCT_VERSION_WRITE); SDL_RWclose(file); return result; }
static int gamepad_load_mappings(const char *vpath, int warn_noexist) { char *repr = vfs_repr(vpath, true); char *errstr = NULL; const char *const_errstr = NULL; SDL_RWops *mappings = vfs_open(vpath, VFS_MODE_READ | VFS_MODE_SEEKABLE); int num_loaded = -1; LogLevel loglvl = LOG_WARN; if(!mappings) { if(!warn_noexist) { VFSInfo vinfo = vfs_query(vpath); if(!vinfo.error && !vinfo.exists && !vinfo.is_dir) { loglvl = LOG_INFO; const_errstr = errstr = strfmt("Custom mappings file '%s' does not exist (this is ok)", repr); goto cleanup; } } const_errstr = vfs_get_error(); goto cleanup; } if((num_loaded = SDL_GameControllerAddMappingsFromRW(mappings, true)) < 0) { const_errstr = SDL_GetError(); } cleanup: if(const_errstr) { log_custom(loglvl, "Couldn't load mappings: %s", const_errstr); } else if(num_loaded >= 0) { log_info("Loaded %i mappings from '%s'", num_loaded, repr); } free(repr); free(errstr); return num_loaded; }
bool replay_load(Replay *rpy, const char *name, ReplayReadMode mode) { char *p = replay_getpath(name, !strendswith(name, REPLAY_EXTENSION)); char *sp = vfs_repr(p, true); log_info("Loading %s (%s)", sp, replay_mode_string(mode)); SDL_RWops *file = vfs_open(p, VFS_MODE_READ); free(p); if(!file) { log_warn("VFS error: %s", vfs_get_error()); free(sp); return false; } bool result = replay_read(rpy, file, mode, sp); if(!result) { replay_destroy(rpy); } free(sp); SDL_RWclose(file); return result; }
static bool parse_obj(const char *filename, ObjFileData *data) { SDL_RWops *rw = vfs_open(filename, VFS_MODE_READ); if(!rw) { log_error("VFS error: %s", vfs_get_error()); return false; } char line[256], *save; vec3_noalign buf; char mode; int linen = 0; memset(data, 0, sizeof(ObjFileData)); while(SDL_RWgets(rw, line, sizeof(line))) { linen++; char *first; first = strtok_r(line, " \n", &save); if(strcmp(first, "v") == 0) mode = 'v'; else if(strcmp(first, "vt") == 0) mode = 't'; else if(strcmp(first, "vn") == 0) mode = 'n'; else if(strcmp(first, "f") == 0) mode = 'f'; else mode = 0; if(mode != 0 && mode != 'f') { buf[0] = atof(strtok_r(NULL, " \n", &save)); char *wtf = strtok_r(NULL, " \n", &save); buf[1] = atof(wtf); if(mode != 't') buf[2] = atof(strtok_r(NULL, " \n", &save)); switch(mode) { case 'v': data->xs = realloc(data->xs, sizeof(vec3_noalign)*(++data->xcount)); memcpy(data->xs[data->xcount-1], buf, sizeof(vec3_noalign)); break; case 't': data->texcoords = realloc(data->texcoords, sizeof(vec3_noalign)*(++data->tcount)); memcpy(data->texcoords[data->tcount-1], buf, sizeof(vec3_noalign)); break; case 'n': data->normals = realloc(data->normals, sizeof(vec3_noalign)*(++data->ncount)); memcpy(data->normals[data->ncount-1], buf, sizeof(vec3_noalign)); break; } } else if(mode == 'f') { char *segment, *seg; int j = 0, jj; ivec3_noalign ibuf; memset(ibuf, 0, sizeof(ibuf)); while((segment = strtok_r(NULL, " \n", &save))) { seg = segment; j++; jj = 0; while(jj < 3) { ibuf[jj] = atoi(seg); jj++; while(*seg != '\0' && *(++seg) != '/'); if(*seg == '\0') break; else seg++; } if(strstr(segment, "//")) { ibuf[2] = ibuf[1]; ibuf[1] = 0; } if(jj == 0 || jj > 3 || segment[0] == '/') { log_error("OBJ file '%s:%d': Parsing error: Corrupt face definition", filename, linen); goto fail; } data->indices = realloc(data->indices, sizeof(ivec3_noalign)*(++data->icount)); memcpy(data->indices[data->icount-1], ibuf, sizeof(ivec3_noalign)); } if(data->fverts == 0) data->fverts = j; if(data->fverts != j) { log_error("OBJ file '%s:%d': Parsing error: face vertex count must stay the same in the whole file", filename, linen); goto fail; } if(data->fverts != 3) { log_error("OBJ file '%s:%d': Parsing error: face vertex count must be 3", filename, linen); goto fail; } } } SDL_RWclose(rw); return true; fail: SDL_RWclose(rw); free(data->indices); free(data->normals); free(data->xs); return false; }
void vfs_setup(CallChain next) { char *res_path, *storage_path, *cache_path; get_core_paths(&res_path, &storage_path, &cache_path); char *local_res_path = strfmt("%s%cresources", storage_path, vfs_get_syspath_separator()); vfs_syspath_normalize_inplace(local_res_path); log_info("Resource path: %s", res_path); log_info("Storage path: %s", storage_path); log_info("Local resource path: %s", local_res_path); log_info("Cache path: %s", cache_path); struct mpoint_t { const char *dest; const char *syspath; bool loadpaks; uint flags; } mpts[] = { // per-user directory, where configs, replays, screenshots, etc. get stored { "storage", storage_path, false, VFS_SYSPATH_MOUNT_MKDIR }, // system-wide directory, contains all of the game assets { "resdirs", res_path, true, VFS_SYSPATH_MOUNT_READONLY }, // subpath of storage, files here override the global assets { "resdirs", local_res_path, true, VFS_SYSPATH_MOUNT_MKDIR | VFS_SYSPATH_MOUNT_READONLY }, // per-user directory, to contain various cached resources to speed up loading times { "cache", cache_path, false, VFS_SYSPATH_MOUNT_MKDIR }, {NULL} }; vfs_init(); // temporary union of the "real" directories vfs_create_union_mountpoint("resdirs"); // temporary union of the packages (e.g. zip files) vfs_create_union_mountpoint("respkgs"); // permanent union of respkgs and resdirs // this way, files in any of the "real" directories always have priority over anything in packages vfs_create_union_mountpoint("res"); for(struct mpoint_t *mp = mpts; mp->dest; ++mp) { if(mp->loadpaks) { // mount it to a temporary mountpoint to get a list of packages from this directory if(!vfs_mount_syspath("tmp", mp->syspath, mp->flags)) { log_fatal("Failed to mount '%s': %s", mp->syspath, vfs_get_error()); } if(!vfs_query("tmp").is_dir) { log_error("'%s' is not a directory", mp->syspath); vfs_unmount("tmp"); continue; } // load all packages from this directory into the respkgs union load_packages("tmp", "respkgs"); // now mount it to the intended destination, and remove the temporary mountpoint vfs_mount_alias(mp->dest, "tmp"); vfs_unmount("tmp"); } else if(!vfs_mount_syspath(mp->dest, mp->syspath, mp->flags)) { log_fatal("Failed to mount '%s': %s", mp->syspath, vfs_get_error()); } } vfs_mkdir_required("storage/replays"); vfs_mkdir_required("storage/screenshots"); free(local_res_path); free(res_path); free(storage_path); free(cache_path); // set up the final "res" union and get rid of the temporaries vfs_mount_alias("res", "respkgs"); vfs_mount_alias("res", "resdirs"); // vfs_make_readonly("res"); vfs_unmount("resdirs"); vfs_unmount("respkgs"); run_call_chain(&next, NULL); }