New storage directory and structure, migration of old storage dir

Closes #188
This commit is contained in:
ROllerozxa 2025-12-04 22:15:41 +01:00
commit cffeaf50d3
3 changed files with 172 additions and 27 deletions

View file

@ -92,7 +92,7 @@ init_curl()
exit(1);
}
snprintf(cookie_file, 1024, "%s/c", tms_storage_path());
snprintf(cookie_file, 1024, "%s/cookie_jar", tms_storage_path());
lock_curl("initial_loader-curl_init");
P.curl = curl_easy_init();

View file

@ -675,7 +675,7 @@ pkgman::get_pkg_path(int type)
else if (type == LEVEL_DB)
snprintf(_pkg_path[type], 1023, "%s/pkg/db", tms_storage_cache_path());
else
snprintf(_pkg_path[type], 1023, "%s/pkg/local", tms_storage_path());
snprintf(_pkg_path[type], 1023, "%s/packages", tms_storage_path());
}
return _pkg_path[type];
@ -696,7 +696,7 @@ pkgman::get_level_path(int level_type)
if (level_type >= LEVEL_LOCAL_STATE) {
if (!_state_path[0])
snprintf(_state_path, 1023, "%s/sav", tms_storage_path());
snprintf(_state_path, 1023, "%s/saves", tms_storage_path());
return _state_path;
}
@ -715,7 +715,7 @@ pkgman::get_level_path(int level_type)
else if (level_type == LEVEL_DB)
snprintf(_level_path[level_type], 1023, "%s/lvl/db", tms_storage_cache_path());
else
snprintf(_level_path[level_type], 1023, "%s/lvl/local", tms_storage_path());
snprintf(_level_path[level_type], 1023, "%s/levels", tms_storage_path());
}
return _level_path[level_type];

View file

@ -4,7 +4,7 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#ifdef __ANDROID__
@ -13,11 +13,12 @@
#ifdef TMS_BACKEND_WINDOWS
#include <direct.h>
#include <windows.h>
#define mkdir(dirname, ...) _mkdir(dirname)
#define create_dir(dirname, ...) _create_dir(dirname, 0)
#else
#include <dirent.h>
#include <pwd.h>
#include <sys/stat.h>
#define create_dir _create_dir
#endif
@ -81,12 +82,13 @@ const char *tms_storage_path(void)
strcat(path, "userdata");
} else { // System
#ifdef TMS_BACKEND_WINDOWS
strcpy(path, getenv("USERPROFILE"));
strcat(path, "\\Principia");
snprintf(path, 1024, "%s\\Principia", getenv("APPDATA"));
#else
struct passwd *pw = getpwuid(getuid());
strcpy(path, pw->pw_dir);
strcat(path, "/.principia");
const char *xdg = getenv("XDG_DATA_HOME");
if (!xdg)
snprintf(path, 1024, "%s/.local/share/principia", getenv("HOME"));
else
snprintf(path, 1024, "%s/principia", xdg);
#endif
}
@ -114,22 +116,16 @@ const char *tms_storage_cache_path(void)
char *path = (char *)malloc(1024);
if (_storage_portable) { // Portable
char* exedir = SDL_GetBasePath();
strcpy(path, exedir);
strcat(path, "cache");
snprintf(path, 1024, "%s/cache", SDL_GetBasePath());
} else { // System
#ifdef TMS_BACKEND_WINDOWS
strcpy(path, getenv("LOCALAPPDATA"));
strcat(path, "\\Principia");
snprintf(path, 1024, "%s\\Principia", getenv("LOCALAPPDATA"));
#else
const char* xdg_cache_home = getenv("XDG_CACHE_HOME");
if (!xdg_cache_home) {
strcpy(path, getenv("HOME"));
strcat(path, "/.cache/principia");
} else {
strcpy(path, xdg_cache_home);
strcat(path, "/principia");
}
const char *xdg = getenv("XDG_CACHE_HOME");
if (!xdg)
snprintf(path, 1024, "%s/.cache/principia", getenv("HOME"));
else
snprintf(path, 1024, "%s/principia", xdg);
#endif
}
@ -138,14 +134,17 @@ const char *tms_storage_cache_path(void)
#endif
}
static void migrate_principia_data();
void tms_storage_create_dirs(void)
{
migrate_principia_data();
char tmp[1024];
static const char *s_dirs[]={
"",
"/lvl", "/lvl/local",
"/pkg", "/pkg/local",
"/sav"
"/levels",
"/saves"
};
for (int x=0; x<sizeof(s_dirs)/sizeof(const char*); x++) {
@ -164,3 +163,149 @@ void tms_storage_create_dirs(void)
create_dir(tmp, S_IRWXU | S_IRWXG | S_IRWXO);
}
}
// Storage migration code
static int file_exists(const char *path) {
struct stat st;
return stat(path, &st) == 0;
}
static int dir_exists(const char *path) {
struct stat st;
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
}
static void move_matching_files(const char *srcdir, const char *dstdir, const char *ext) {
#ifdef TMS_BACKEND_WINDOWS
char pattern[MAX_PATH];
snprintf(pattern, sizeof(pattern), "%s\\*.%s", srcdir, ext);
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFile(pattern, &ffd);
if (hFind == INVALID_HANDLE_VALUE) return;
do {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
char src[MAX_PATH], dst[MAX_PATH];
snprintf(src, sizeof(src), "%s\\%s", srcdir, ffd.cFileName);
snprintf(dst, sizeof(dst), "%s\\%s", dstdir, ffd.cFileName);
rename(src, dst);
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
#else
DIR *dir = opendir(srcdir);
if (!dir) return;
struct dirent *ent;
while ((ent = readdir(dir))) {
if (ent->d_type == DT_DIR) continue;
const char *fname = ent->d_name;
const char *dot = strrchr(fname, '.');
if (!dot || strcmp(dot + 1, ext) != 0) continue;
char src[1024], dst[1024];
snprintf(src, sizeof(src), "%s/%s", srcdir, fname);
snprintf(dst, sizeof(dst), "%s/%s", dstdir, fname);
rename(src, dst);
}
closedir(dir);
#endif
}
static void migrate_principia_data() {
char oldpath[1024], newpath[1024], oldpath_old[1024],
old_levels[1024], new_levels[1024], old_saves[1024], new_saves[1024];
if (_storage_portable) {
snprintf(oldpath, sizeof(oldpath), "userdata");
snprintf(newpath, sizeof(newpath), "userdata");
} else {
#ifdef TMS_BACKEND_WINDOWS
// Old: %USERPROFILE%/Principia
snprintf(oldpath, sizeof(oldpath), "%s\\Principia", getenv("USERPROFILE"));
// New: %APPDATA%/Principia
snprintf(newpath, sizeof(newpath), "%s\\Principia", getenv("APPDATA"));
#elif defined(TMS_BACKEND_LINUX) && !defined(SCREENSHOT_BUILD)
// Old: ~/.principia
snprintf(oldpath, sizeof(oldpath), "%s/.principia", getenv("HOME"));
// New: $XDG_DATA_HOME/principia or ~/.local/share/principia
const char *xdg = getenv("XDG_DATA_HOME");
if (!xdg)
snprintf(newpath, sizeof(newpath), "%s/.local/share/principia", getenv("HOME"));
else
snprintf(newpath, sizeof(newpath), "%s/principia", xdg);
#elif defined(TMS_BACKEND_ANDROID)
snprintf(oldpath, sizeof(oldpath), "%s", SDL_AndroidGetExternalStoragePath());
snprintf(newpath, sizeof(newpath), "%s", SDL_AndroidGetExternalStoragePath());
#else
// No migration for this platform
return;
#endif
}
snprintf(old_levels, sizeof(old_levels), "%s/levels", oldpath);
// Return if old path does not exist and new path already exists (unless both paths are the same)
if (!dir_exists(oldpath) && dir_exists(newpath))
return;
if (strcmp(oldpath, newpath) == 0 && dir_exists(old_levels))
return;
// We should migrate now
printf("Migrating Principia data from %s -> %s\n", oldpath, newpath);
// Rename old directory to [...]_old
snprintf(oldpath_old, sizeof(oldpath_old), "%s_old", oldpath);
rename(oldpath, oldpath_old);
strncat(oldpath, "_old", sizeof(oldpath)-1);
snprintf(old_levels, sizeof(old_levels), "%s/lvl/local", oldpath);
snprintf(new_levels, sizeof(new_levels), "%s/levels", newpath);
snprintf(old_saves, sizeof(old_saves), "%s/sav", oldpath);
snprintf(new_saves, sizeof(new_saves), "%s/saves", newpath);
// Create dirs for new location
create_dir(newpath, S_IRWXU | S_IRWXG | S_IRWXO);
create_dir(new_levels, S_IRWXU | S_IRWXG | S_IRWXO);
create_dir(new_saves, S_IRWXU | S_IRWXG | S_IRWXO);
// Move individual files
struct {
const char *src;
const char *dst;
} files[] = {
{"c", "cookie_jar"},
{"community_host.txt", "community_host.txt"},
{"data.bin", "data.bin"},
{"run.log", "run.log"},
{"settings.ini", "settings.ini"},
{"lvl/local/.autosave", "levels/.autosave"},
{NULL, NULL}
};
for (int i = 0; files[i].src; i++) {
char src[1024], dst[1024];
snprintf(src, sizeof(src), "%s/%s", oldpath, files[i].src);
snprintf(dst, sizeof(dst), "%s/%s", newpath, files[i].dst);
// Only move if source exists and there is nothing in the new location
if (file_exists(src) && !file_exists(dst))
rename(src, dst);
}
// Move levels, multi-select objects and puzzle solutions
move_matching_files(old_levels, new_levels, "plvl");
move_matching_files(old_levels, new_levels, "pobj");
move_matching_files(old_levels, new_levels, "psol");
move_matching_files(old_saves, new_saves, "psav");
}