221 lines
6.3 KiB
C++
221 lines
6.3 KiB
C++
#include "core/jail/jfile.hpp"
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#ifndef _WIN32
|
|
#include <pwd.h>
|
|
#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<file_entry> toc;
|
|
std::vector<keyvalue> 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<char*>(&num_files), 4);
|
|
fi.read(reinterpret_cast<char*>(&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<char*>(&file_offset), 4);
|
|
fi.read(reinterpret_cast<char*>(&file_size), 4);
|
|
uint8_t path_size;
|
|
fi.read(reinterpret_cast<char*>(&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;
|
|
}
|
|
|
|
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<int>(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<int>(ftell(f));
|
|
fseek(f, 0, SEEK_SET);
|
|
}
|
|
return f;
|
|
}
|
|
|
|
std::vector<char> file_readfile(const char* resourcename) {
|
|
int filesize = 0;
|
|
FILE* f = file_getfilepointer(resourcename, filesize, true);
|
|
if (!f) return {};
|
|
std::vector<char> 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();
|
|
}
|