#include "core/jail/jfile.hpp" #include #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #endif namespace { constexpr const char* DEFAULT_FILENAME = "data.jf2"; constexpr const char* DEFAULT_FOLDER = "data/"; struct file_entry { std::string path; uint32_t size; uint32_t offset; }; struct keyvalue { std::string key; std::string value; }; std::vector toc; std::vector config; std::string resource_filename; std::string resource_folder; std::string config_folder; int file_source = SOURCE_FILE; bool dictionary_loaded() { if (resource_filename.empty()) resource_filename = DEFAULT_FILENAME; std::ifstream fi(resource_filename, std::ios::binary); if (!fi.is_open()) return false; char header[4]; fi.read(header, 4); uint32_t num_files, toc_offset; fi.read(reinterpret_cast(&num_files), 4); fi.read(reinterpret_cast(&toc_offset), 4); fi.seekg(toc_offset); for (uint32_t i = 0; i < num_files; ++i) { uint32_t file_offset, file_size; fi.read(reinterpret_cast(&file_offset), 4); fi.read(reinterpret_cast(&file_size), 4); uint8_t path_size; fi.read(reinterpret_cast(&path_size), 1); char file_name[256]; fi.read(file_name, path_size); file_name[path_size] = 0; toc.push_back({std::string(file_name), file_size, file_offset}); } return true; } std::string filename_with_folder(const char* filename) { return resource_folder + filename; } void load_config_values() { config.clear(); const std::string config_file = config_folder + "/config.txt"; std::ifstream fi(config_file); if (!fi.is_open()) return; std::string line; while (std::getline(fi, line)) { const auto eq = line.find('='); if (eq == std::string::npos) continue; config.push_back({line.substr(0, eq), line.substr(eq + 1)}); } } void save_config_values() { const std::string config_file = config_folder + "/config.txt"; std::ofstream fo(config_file); if (!fo.is_open()) return; for (const auto& pair : config) { fo << pair.key << '=' << pair.value << '\n'; } } } // namespace void file_setresourcefilename(const char* str) { resource_filename = str; } void file_setresourcefolder(const char* str) { resource_folder = str; } const char* file_getresourcefolder() { return resource_folder.c_str(); } void file_setsource(const int src) { file_source = src % 2; if (src == SOURCE_FOLDER && resource_folder.empty()) file_setresourcefolder(DEFAULT_FOLDER); } FILE* file_getfilepointer(const char* resourcename, int& filesize, const bool binary) { if (file_source == SOURCE_FILE && toc.empty()) { if (!dictionary_loaded()) file_setsource(SOURCE_FOLDER); } FILE* f = nullptr; if (file_source == SOURCE_FILE) { const std::string name(resourcename); size_t count = 0; for (; count < toc.size(); ++count) { if (toc[count].path == name) break; } if (count == toc.size()) { perror("El recurs no s'ha trobat en l'arxiu de recursos"); exit(1); } filesize = static_cast(toc[count].size); f = fopen(resource_filename.c_str(), binary ? "rb" : "r"); if (!f) { perror("No s'ha pogut obrir l'arxiu de recursos"); exit(1); } fseek(f, toc[count].offset, SEEK_SET); } else { const std::string full = filename_with_folder(resourcename); f = fopen(full.c_str(), binary ? "rb" : "r"); if (!f) return nullptr; fseek(f, 0, SEEK_END); filesize = static_cast(ftell(f)); fseek(f, 0, SEEK_SET); } return f; } std::vector file_readfile(const char* resourcename) { int filesize = 0; FILE* f = file_getfilepointer(resourcename, filesize, true); if (!f) return {}; std::vector buffer(filesize); fread(buffer.data(), filesize, 1, f); fclose(f); return buffer; } // Crea la carpeta del sistema on guardar les dades. // Accepta rutes amb subdirectoris (ex: "jailgames/aee") i crea tota la jerarquia. void file_setconfigfolder(const char* foldername) { #ifdef _WIN32 const char* base = getenv("APPDATA"); if (!base) base = "C:/"; config_folder = std::string(base) + "/" + foldername; #elif __APPLE__ struct passwd* pw = getpwuid(getuid()); const char* homedir = (pw && pw->pw_dir) ? pw->pw_dir : nullptr; if (!homedir) homedir = getenv("HOME"); if (!homedir) homedir = "/tmp"; config_folder = std::string(homedir) + "/Library/Application Support/" + foldername; #elif __linux__ // Nota emscripten: `__linux__` també està definit, però `getpwuid` no // troba cap /etc/passwd al MEMFS i retorna nullptr. Amb els fallbacks // HOME → /tmp evitem crashejar al primer arranque dins del navegador. // La config no persistirà entre recàrregues de la pàgina (MEMFS és // volàtil); caldria IDBFS si volguéssem persistència a web. struct passwd* pw = getpwuid(getuid()); const char* homedir = (pw && pw->pw_dir) ? pw->pw_dir : nullptr; if (!homedir) homedir = getenv("HOME"); if (!homedir) homedir = "/tmp"; config_folder = std::string(homedir) + "/.config/" + foldername; #endif if (!config_folder.empty()) { std::filesystem::create_directories(config_folder); } } const char* file_getconfigfolder() { thread_local std::string folder; folder = config_folder + "/"; return folder.c_str(); } const char* file_getconfigvalue(const char* key) { if (config.empty()) load_config_values(); for (const auto& pair : config) { if (pair.key == key) { thread_local std::string value_cache; value_cache = pair.value; return value_cache.c_str(); } } return nullptr; } void file_setconfigvalue(const char* key, const char* value) { if (config.empty()) load_config_values(); for (auto& pair : config) { if (pair.key == key) { pair.value = value; save_config_values(); return; } } config.push_back({std::string(key), std::string(value)}); save_config_values(); }