2020-01-01 21:29:24 +01:00
|
|
|
#include "FileSystemUtils.h"
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2020-06-01 01:31:02 +02:00
|
|
|
#include "Graphics.h"
|
|
|
|
|
2020-04-09 21:03:24 +02:00
|
|
|
#include <iterator>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <iostream>
|
|
|
|
|
2020-01-10 19:59:34 +01:00
|
|
|
#include <SDL.h>
|
2020-01-01 21:29:24 +01:00
|
|
|
#include <physfs.h>
|
|
|
|
|
2020-06-04 04:37:01 +02:00
|
|
|
#include "tinyxml2.h"
|
Replace TiXmlDocument load and save functions by PHYSFS
The TinyXml functions to load and save files don't properly support
unicode file paths on Windows, so in order to support that properly, I
saw no other option than to do the actual loading and saving via PHYSFS
(or to use the Windows API on Windows and retain doc.LoadFile and
doc.SaveFile on other OSes, but that'd be more complicated and
unnecessary, we already have PHYSFS, right?).
There are two new functions in FileSystemUtils:
bool FILESYSTEM_saveTiXmlDocument(const char *name, TiXmlDocument *doc)
bool FILESYSTEM_loadTiXmlDocument(const char *name, TiXmlDocument *doc)
Any instances of doc.SaveFile(<FULL_PATH>) have been replaced by
FILESYSTEM_saveTiXmlDocument(<VVVVVV_FOLDER_PATH>, &doc), where
<FULL_PATH> included the full path to the saves or levels directory,
and <VVVVVV_FOLDER_PATH> only includes the path relative to the VVVVVV
directory.
When loading a document, a TiXmlDocument used to be created with a full
path in its constructor and doc.LoadFile() would then be called, now a
TiXmlDocument is constructed with no path name and
FILESYSTEM_loadTiXmlDocument(<VVVVVV_FOLDER_PATH>, &doc) is called.
2020-01-12 15:17:39 +01:00
|
|
|
|
2020-06-12 22:20:18 +02:00
|
|
|
/* These are needed for PLATFORM_* crap */
|
2020-01-01 21:29:24 +01:00
|
|
|
#if defined(_WIN32)
|
|
|
|
#include <windows.h>
|
|
|
|
#include <shlobj.h>
|
2020-04-18 03:48:55 +02:00
|
|
|
#include <shellapi.h>
|
2020-04-20 15:41:11 +02:00
|
|
|
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) || defined(__DragonFly__)
|
2020-01-01 21:29:24 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <dirent.h>
|
2020-06-12 22:20:18 +02:00
|
|
|
#include <limits.h>
|
|
|
|
#include <sys/stat.h>
|
2020-04-18 03:48:55 +02:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <spawn.h>
|
2020-01-01 21:29:24 +01:00
|
|
|
#define MAX_PATH PATH_MAX
|
|
|
|
#endif
|
|
|
|
|
2020-07-06 20:13:30 +02:00
|
|
|
char saveDir[MAX_PATH] = {'\0'};
|
|
|
|
char levelDir[MAX_PATH] = {'\0'};
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
void PLATFORM_getOSDirectory(char* output);
|
|
|
|
void PLATFORM_migrateSaveData(char* output);
|
|
|
|
void PLATFORM_copyFile(const char *oldLocation, const char *newLocation);
|
|
|
|
|
2020-02-09 00:49:03 +01:00
|
|
|
int FILESYSTEM_init(char *argvZero, char* baseDir, char *assetsPath)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
char output[MAX_PATH];
|
|
|
|
int mkdirResult;
|
2020-02-09 00:49:03 +01:00
|
|
|
const char* pathSep = PHYSFS_getDirSeparator();
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
PHYSFS_init(argvZero);
|
2020-01-12 16:52:42 +01:00
|
|
|
PHYSFS_permitSymbolicLinks(1);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
/* Determine the OS user directory */
|
2020-02-09 00:49:03 +01:00
|
|
|
if (baseDir && strlen(baseDir) > 0)
|
|
|
|
{
|
|
|
|
strcpy(output, baseDir);
|
|
|
|
|
|
|
|
/* We later append to this path and assume it ends in a slash */
|
|
|
|
if (strcmp(std::string(1, output[strlen(output) - 1]).c_str(), pathSep) != 0)
|
|
|
|
{
|
|
|
|
strcat(output, pathSep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PLATFORM_getOSDirectory(output);
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
/* Create base user directory, mount */
|
2020-06-12 22:20:18 +02:00
|
|
|
mkdirResult = PHYSFS_mkdir(output);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
/* Mount our base user directory */
|
2020-06-01 01:31:02 +02:00
|
|
|
PHYSFS_mount(output, NULL, 0);
|
Replace TiXmlDocument load and save functions by PHYSFS
The TinyXml functions to load and save files don't properly support
unicode file paths on Windows, so in order to support that properly, I
saw no other option than to do the actual loading and saving via PHYSFS
(or to use the Windows API on Windows and retain doc.LoadFile and
doc.SaveFile on other OSes, but that'd be more complicated and
unnecessary, we already have PHYSFS, right?).
There are two new functions in FileSystemUtils:
bool FILESYSTEM_saveTiXmlDocument(const char *name, TiXmlDocument *doc)
bool FILESYSTEM_loadTiXmlDocument(const char *name, TiXmlDocument *doc)
Any instances of doc.SaveFile(<FULL_PATH>) have been replaced by
FILESYSTEM_saveTiXmlDocument(<VVVVVV_FOLDER_PATH>, &doc), where
<FULL_PATH> included the full path to the saves or levels directory,
and <VVVVVV_FOLDER_PATH> only includes the path relative to the VVVVVV
directory.
When loading a document, a TiXmlDocument used to be created with a full
path in its constructor and doc.LoadFile() would then be called, now a
TiXmlDocument is constructed with no path name and
FILESYSTEM_loadTiXmlDocument(<VVVVVV_FOLDER_PATH>, &doc) is called.
2020-01-12 15:17:39 +01:00
|
|
|
PHYSFS_setWriteDir(output);
|
2020-01-01 21:29:24 +01:00
|
|
|
printf("Base directory: %s\n", output);
|
|
|
|
|
2020-07-02 21:57:40 +02:00
|
|
|
/* Create the save/level folders */
|
|
|
|
mkdirResult |= PHYSFS_mkdir("saves");
|
|
|
|
mkdirResult |= PHYSFS_mkdir("levels");
|
|
|
|
|
|
|
|
/* Store full save directory */
|
|
|
|
SDL_snprintf(saveDir, sizeof(saveDir), "%s%s%s",
|
|
|
|
output,
|
|
|
|
"saves",
|
|
|
|
PHYSFS_getDirSeparator()
|
|
|
|
);
|
|
|
|
printf("Save directory: %s\n", saveDir);
|
|
|
|
|
|
|
|
/* Store full level directory */
|
|
|
|
SDL_snprintf(levelDir, sizeof(levelDir), "%s%s%s",
|
|
|
|
output,
|
|
|
|
"levels",
|
|
|
|
PHYSFS_getDirSeparator()
|
|
|
|
);
|
|
|
|
printf("Level directory: %s\n", levelDir);
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
/* We didn't exist until now, migrate files! */
|
2020-06-12 22:21:45 +02:00
|
|
|
if (mkdirResult == 0)
|
2020-01-01 21:29:24 +01:00
|
|
|
{
|
|
|
|
PLATFORM_migrateSaveData(output);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mount the stock content last */
|
2020-06-01 01:43:10 +02:00
|
|
|
if (assetsPath)
|
|
|
|
{
|
2020-02-03 00:28:26 +01:00
|
|
|
strcpy(output, assetsPath);
|
2020-06-01 01:43:10 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-02-03 00:28:26 +01:00
|
|
|
strcpy(output, PHYSFS_getBaseDir());
|
|
|
|
strcat(output, "data.zip");
|
|
|
|
}
|
2020-01-10 19:59:34 +01:00
|
|
|
if (!PHYSFS_mount(output, NULL, 1))
|
|
|
|
{
|
2020-01-10 22:04:04 +01:00
|
|
|
puts("Error: data.zip missing!");
|
|
|
|
puts("You do not have data.zip!");
|
|
|
|
puts("Grab it from your purchased copy of the game,");
|
|
|
|
puts("or get it from the free Make and Play Edition.");
|
|
|
|
|
2020-01-10 19:59:34 +01:00
|
|
|
SDL_ShowSimpleMessageBox(
|
|
|
|
SDL_MESSAGEBOX_ERROR,
|
|
|
|
"data.zip missing!",
|
|
|
|
"You do not have data.zip!"
|
|
|
|
"\n\nGrab it from your purchased copy of the game,"
|
2020-01-10 20:00:45 +01:00
|
|
|
"\nor get it from the free Make and Play Edition.",
|
2020-01-10 19:59:34 +01:00
|
|
|
NULL
|
|
|
|
);
|
2020-01-10 22:04:04 +01:00
|
|
|
return 0;
|
2020-01-10 19:59:34 +01:00
|
|
|
}
|
2020-01-17 18:43:42 +01:00
|
|
|
|
2020-06-24 01:08:08 +02:00
|
|
|
SDL_snprintf(output, sizeof(output), "%s%s", PHYSFS_getBaseDir(), "gamecontrollerdb.txt");
|
2020-01-17 18:43:42 +01:00
|
|
|
if (SDL_GameControllerAddMappingsFromFile(output) < 0)
|
|
|
|
{
|
|
|
|
printf("gamecontrollerdb.txt not found!\n");
|
|
|
|
}
|
2020-01-10 22:04:04 +01:00
|
|
|
return 1;
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FILESYSTEM_deinit()
|
|
|
|
{
|
|
|
|
PHYSFS_deinit();
|
|
|
|
}
|
|
|
|
|
|
|
|
char *FILESYSTEM_getUserSaveDirectory()
|
|
|
|
{
|
|
|
|
return saveDir;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *FILESYSTEM_getUserLevelDirectory()
|
|
|
|
{
|
|
|
|
return levelDir;
|
|
|
|
}
|
|
|
|
|
2020-06-01 01:31:02 +02:00
|
|
|
bool FILESYSTEM_directoryExists(const char *fname)
|
|
|
|
{
|
2020-06-01 01:43:10 +02:00
|
|
|
return PHYSFS_exists(fname);
|
2020-06-01 01:31:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void FILESYSTEM_mount(const char *fname)
|
|
|
|
{
|
2020-06-01 01:43:10 +02:00
|
|
|
std::string path(PHYSFS_getRealDir(fname));
|
|
|
|
path += PHYSFS_getDirSeparator();
|
|
|
|
path += fname;
|
|
|
|
if (!PHYSFS_mount(path.c_str(), NULL, 0))
|
|
|
|
{
|
|
|
|
printf("Error mounting: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
graphics.assetdir = path.c_str();
|
|
|
|
}
|
2020-06-01 01:31:02 +02:00
|
|
|
}
|
|
|
|
|
2020-06-30 20:55:21 +02:00
|
|
|
bool FILESYSTEM_assetsmounted = false;
|
|
|
|
|
2020-06-21 23:26:40 +02:00
|
|
|
void FILESYSTEM_mountassets(const char* path)
|
|
|
|
{
|
|
|
|
const std::string _path(path);
|
|
|
|
|
|
|
|
std::string zippath = "levels/" + _path.substr(7,_path.size()-14) + ".data.zip";
|
|
|
|
std::string dirpath = "levels/" + _path.substr(7,_path.size()-14) + "/";
|
|
|
|
std::string zip_path;
|
|
|
|
const char* cstr = PHYSFS_getRealDir(_path.c_str());
|
|
|
|
|
|
|
|
if (cstr) {
|
|
|
|
zip_path = cstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cstr && FILESYSTEM_directoryExists(zippath.c_str())) {
|
|
|
|
printf("Custom asset directory exists at %s\n", zippath.c_str());
|
|
|
|
FILESYSTEM_mount(zippath.c_str());
|
|
|
|
graphics.reloadresources();
|
2020-06-30 20:55:21 +02:00
|
|
|
FILESYSTEM_assetsmounted = true;
|
2020-06-21 23:26:40 +02:00
|
|
|
} else if (zip_path != "data.zip" && !endsWith(zip_path, "/data.zip") && endsWith(zip_path, ".zip")) {
|
|
|
|
printf("Custom asset directory is .zip at %s\n", zip_path.c_str());
|
|
|
|
PHYSFS_File* zip = PHYSFS_openRead(zip_path.c_str());
|
|
|
|
zip_path += ".data.zip";
|
|
|
|
if (zip == NULL) {
|
|
|
|
printf("error loading .zip: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
|
|
|
} else if (PHYSFS_mountHandle(zip, zip_path.c_str(), "/", 0) == 0) {
|
|
|
|
printf("error mounting .zip: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
|
|
|
} else {
|
|
|
|
graphics.assetdir = zip_path;
|
|
|
|
}
|
2020-06-30 20:55:21 +02:00
|
|
|
FILESYSTEM_assetsmounted = true;
|
2020-06-21 23:26:40 +02:00
|
|
|
graphics.reloadresources();
|
|
|
|
} else if (FILESYSTEM_directoryExists(dirpath.c_str())) {
|
|
|
|
printf("Custom asset directory exists at %s\n",dirpath.c_str());
|
|
|
|
FILESYSTEM_mount(dirpath.c_str());
|
|
|
|
graphics.reloadresources();
|
2020-06-30 20:55:21 +02:00
|
|
|
FILESYSTEM_assetsmounted = true;
|
2020-06-21 23:26:40 +02:00
|
|
|
} else {
|
|
|
|
printf("Custom asset directory does not exist\n");
|
2020-06-30 20:55:21 +02:00
|
|
|
FILESYSTEM_assetsmounted = false;
|
2020-06-21 23:26:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-01 01:31:02 +02:00
|
|
|
void FILESYSTEM_unmountassets()
|
|
|
|
{
|
2020-06-01 01:43:10 +02:00
|
|
|
if (graphics.assetdir != "")
|
|
|
|
{
|
|
|
|
printf("Unmounting %s\n", graphics.assetdir.c_str());
|
|
|
|
PHYSFS_unmount(graphics.assetdir.c_str());
|
|
|
|
graphics.assetdir = "";
|
|
|
|
graphics.reloadresources();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("Cannot unmount when no asset directory is mounted\n");
|
|
|
|
}
|
2020-06-30 20:55:21 +02:00
|
|
|
FILESYSTEM_assetsmounted = false;
|
2020-06-01 01:31:02 +02:00
|
|
|
}
|
|
|
|
|
2020-06-01 01:43:10 +02:00
|
|
|
void FILESYSTEM_loadFileToMemory(
|
|
|
|
const char *name,
|
|
|
|
unsigned char **mem,
|
|
|
|
size_t *len,
|
|
|
|
bool addnull
|
|
|
|
) {
|
|
|
|
if (strcmp(name, "levels/special/stdin.vvvvvv") == 0)
|
|
|
|
{
|
2020-04-09 21:03:24 +02:00
|
|
|
// this isn't *technically* necessary when piping directly from a file, but checking for that is annoying
|
|
|
|
static std::vector<char> STDIN_BUFFER;
|
|
|
|
static bool STDIN_LOADED = false;
|
2020-06-01 01:43:10 +02:00
|
|
|
if (!STDIN_LOADED)
|
|
|
|
{
|
2020-04-09 21:03:24 +02:00
|
|
|
std::istreambuf_iterator<char> begin(std::cin), end;
|
|
|
|
STDIN_BUFFER.assign(begin, end);
|
|
|
|
STDIN_BUFFER.push_back(0); // there's no observable change in behavior if addnull is always true, but not vice versa
|
|
|
|
STDIN_LOADED = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t length = STDIN_BUFFER.size() - 1;
|
2020-06-01 01:43:10 +02:00
|
|
|
if (len != NULL)
|
|
|
|
{
|
2020-04-09 21:03:24 +02:00
|
|
|
*len = length;
|
|
|
|
}
|
|
|
|
|
|
|
|
++length;
|
|
|
|
*mem = static_cast<unsigned char*>(malloc(length)); // STDIN_BUFFER.data() causes double-free
|
|
|
|
std::copy(STDIN_BUFFER.begin(), STDIN_BUFFER.end(), reinterpret_cast<char*>(*mem));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
PHYSFS_File *handle = PHYSFS_openRead(name);
|
|
|
|
if (handle == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
PHYSFS_uint32 length = PHYSFS_fileLength(handle);
|
|
|
|
if (len != NULL)
|
|
|
|
{
|
|
|
|
*len = length;
|
|
|
|
}
|
2020-01-24 21:35:46 +01:00
|
|
|
if (addnull)
|
|
|
|
{
|
|
|
|
*mem = (unsigned char *) malloc(length + 1);
|
2020-01-24 22:12:45 +01:00
|
|
|
(*mem)[length] = 0;
|
2020-01-24 21:35:46 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*mem = (unsigned char*) malloc(length);
|
|
|
|
}
|
2020-04-18 00:33:37 +02:00
|
|
|
int success = PHYSFS_readBytes(handle, *mem, length);
|
|
|
|
if (success == -1)
|
|
|
|
{
|
|
|
|
FILESYSTEM_freeMemory(mem);
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
PHYSFS_close(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FILESYSTEM_freeMemory(unsigned char **mem)
|
|
|
|
{
|
|
|
|
free(*mem);
|
|
|
|
*mem = NULL;
|
|
|
|
}
|
|
|
|
|
2020-06-03 18:54:05 +02:00
|
|
|
bool FILESYSTEM_saveTiXml2Document(const char *name, tinyxml2::XMLDocument& doc)
|
|
|
|
{
|
|
|
|
/* XMLDocument.SaveFile doesn't account for Unicode paths, PHYSFS does */
|
|
|
|
tinyxml2::XMLPrinter printer;
|
|
|
|
doc.Print(&printer);
|
|
|
|
PHYSFS_File* handle = PHYSFS_openWrite(name);
|
|
|
|
if (handle == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
PHYSFS_writeBytes(handle, printer.CStr(), printer.CStrSize() - 1); // subtract one because CStrSize includes terminating null
|
|
|
|
PHYSFS_close(handle);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-06-03 19:00:53 +02:00
|
|
|
bool FILESYSTEM_loadTiXml2Document(const char *name, tinyxml2::XMLDocument& doc)
|
|
|
|
{
|
|
|
|
/* XMLDocument.LoadFile doesn't account for Unicode paths, PHYSFS does */
|
|
|
|
unsigned char *mem = NULL;
|
|
|
|
FILESYSTEM_loadFileToMemory(name, &mem, NULL, true);
|
|
|
|
if (mem == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
doc.Parse((const char*) mem);
|
|
|
|
FILESYSTEM_freeMemory(&mem);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-01 21:29:24 +01:00
|
|
|
std::vector<std::string> FILESYSTEM_getLevelDirFileNames()
|
|
|
|
{
|
|
|
|
std::vector<std::string> list;
|
|
|
|
char **fileList = PHYSFS_enumerateFiles("/levels");
|
|
|
|
char **i;
|
|
|
|
std::string builtLocation;
|
|
|
|
|
|
|
|
for (i = fileList; *i != NULL; i++)
|
|
|
|
{
|
|
|
|
if (strcmp(*i, "data") == 0)
|
|
|
|
{
|
|
|
|
continue; /* FIXME: lolwut -flibit */
|
|
|
|
}
|
|
|
|
builtLocation = "levels/";
|
|
|
|
builtLocation += *i;
|
|
|
|
list.push_back(builtLocation);
|
|
|
|
}
|
|
|
|
|
|
|
|
PHYSFS_freeList(fileList);
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PLATFORM_getOSDirectory(char* output)
|
|
|
|
{
|
2020-01-11 17:29:07 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
/* This block is here for compatibility, do not touch it! */
|
2020-01-12 12:37:22 +01:00
|
|
|
WCHAR utf16_path[MAX_PATH];
|
|
|
|
SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, utf16_path);
|
|
|
|
WideCharToMultiByte(CP_UTF8, 0, utf16_path, -1, output, MAX_PATH, NULL, NULL);
|
2020-01-01 21:29:24 +01:00
|
|
|
strcat(output, "\\VVVVVV\\");
|
|
|
|
#else
|
2020-01-11 17:29:07 +01:00
|
|
|
strcpy(output, PHYSFS_getPrefDir("distractionware", "VVVVVV"));
|
2020-01-01 21:29:24 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void PLATFORM_migrateSaveData(char* output)
|
|
|
|
{
|
|
|
|
char oldLocation[MAX_PATH];
|
|
|
|
char newLocation[MAX_PATH];
|
|
|
|
char oldDirectory[MAX_PATH];
|
2020-04-20 15:41:11 +02:00
|
|
|
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) || defined(__DragonFly__)
|
2020-01-01 21:29:24 +01:00
|
|
|
DIR *dir = NULL;
|
|
|
|
struct dirent *de = NULL;
|
|
|
|
DIR *subDir = NULL;
|
|
|
|
struct dirent *subDe = NULL;
|
|
|
|
char subDirLocation[MAX_PATH];
|
|
|
|
const char *homeDir = getenv("HOME");
|
|
|
|
if (homeDir == NULL)
|
|
|
|
{
|
|
|
|
/* Uhh, I don't want to get near this. -flibit */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
strcpy(oldDirectory, homeDir);
|
2020-05-18 23:07:52 +02:00
|
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) || defined(__DragonFly__)
|
2020-01-01 21:29:24 +01:00
|
|
|
strcat(oldDirectory, "/.vvvvvv/");
|
2020-05-18 23:07:52 +02:00
|
|
|
#elif defined(__APPLE__)
|
2020-01-01 21:29:24 +01:00
|
|
|
strcat(oldDirectory, "/Documents/VVVVVV/");
|
2020-05-18 23:07:52 +02:00
|
|
|
#endif
|
2020-01-01 21:29:24 +01:00
|
|
|
dir = opendir(oldDirectory);
|
|
|
|
if (!dir)
|
|
|
|
{
|
|
|
|
printf("Could not find directory %s\n", oldDirectory);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Migrating old savedata to new location...\n");
|
|
|
|
for (de = readdir(dir); de != NULL; de = readdir(dir))
|
|
|
|
{
|
|
|
|
if ( strcmp(de->d_name, "..") == 0 ||
|
|
|
|
strcmp(de->d_name, ".") == 0 )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#define COPY_SAVEFILE(name) \
|
|
|
|
else if (strcmp(de->d_name, name) == 0) \
|
|
|
|
{ \
|
|
|
|
strcpy(oldLocation, oldDirectory); \
|
|
|
|
strcat(oldLocation, name); \
|
|
|
|
strcpy(newLocation, output); \
|
|
|
|
strcat(newLocation, "saves/"); \
|
|
|
|
strcat(newLocation, name); \
|
|
|
|
PLATFORM_copyFile(oldLocation, newLocation); \
|
|
|
|
}
|
|
|
|
COPY_SAVEFILE("unlock.vvv")
|
|
|
|
COPY_SAVEFILE("tsave.vvv")
|
|
|
|
COPY_SAVEFILE("qsave.vvv")
|
|
|
|
#undef COPY_SAVEFILE
|
|
|
|
else if (strstr(de->d_name, ".vvvvvv.vvv") != NULL)
|
|
|
|
{
|
|
|
|
strcpy(oldLocation, oldDirectory);
|
|
|
|
strcat(oldLocation, de->d_name);
|
|
|
|
strcpy(newLocation, output);
|
|
|
|
strcat(newLocation, "saves/");
|
|
|
|
strcat(newLocation, de->d_name);
|
|
|
|
PLATFORM_copyFile(oldLocation, newLocation);
|
|
|
|
}
|
|
|
|
else if (strstr(de->d_name, ".vvvvvv") != NULL)
|
|
|
|
{
|
|
|
|
strcpy(oldLocation, oldDirectory);
|
|
|
|
strcat(oldLocation, de->d_name);
|
|
|
|
strcpy(newLocation, output);
|
|
|
|
strcat(newLocation, "levels/");
|
|
|
|
strcat(newLocation, de->d_name);
|
|
|
|
PLATFORM_copyFile(oldLocation, newLocation);
|
|
|
|
}
|
|
|
|
else if (strcmp(de->d_name, "Saves") == 0)
|
|
|
|
{
|
|
|
|
strcpy(subDirLocation, oldDirectory);
|
|
|
|
strcat(subDirLocation, "Saves/");
|
|
|
|
subDir = opendir(subDirLocation);
|
|
|
|
if (!subDir)
|
|
|
|
{
|
|
|
|
printf("Could not open Saves/ subdir!\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (
|
|
|
|
subDe = readdir(subDir);
|
|
|
|
subDe != NULL;
|
|
|
|
subDe = readdir(subDir)
|
|
|
|
) {
|
|
|
|
#define COPY_SAVEFILE(name) \
|
|
|
|
(strcmp(subDe->d_name, name) == 0) \
|
|
|
|
{ \
|
|
|
|
strcpy(oldLocation, subDirLocation); \
|
|
|
|
strcat(oldLocation, name); \
|
|
|
|
strcpy(newLocation, output); \
|
|
|
|
strcat(newLocation, "saves/"); \
|
|
|
|
strcat(newLocation, name); \
|
|
|
|
PLATFORM_copyFile(oldLocation, newLocation); \
|
|
|
|
}
|
|
|
|
if COPY_SAVEFILE("unlock.vvv")
|
|
|
|
else if COPY_SAVEFILE("tsave.vvv")
|
|
|
|
else if COPY_SAVEFILE("qsave.vvv")
|
|
|
|
#undef COPY_SAVEFILE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
WIN32_FIND_DATA findHandle;
|
|
|
|
HANDLE hFind = NULL;
|
|
|
|
char fileSearch[MAX_PATH];
|
|
|
|
|
|
|
|
/* Same place, different layout. */
|
|
|
|
strcpy(oldDirectory, output);
|
|
|
|
|
|
|
|
/* In theory we don't need to worry about this, thanks case insensitivity!
|
|
|
|
sprintf(fileSearch, "%s\\Saves\\*.vvv", oldDirectory);
|
|
|
|
hFind = FindFirstFile(fileSearch, &findHandle);
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
printf("Could not find directory %s\\Saves\\\n", oldDirectory);
|
|
|
|
}
|
|
|
|
else do
|
|
|
|
{
|
|
|
|
if ((findHandle.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
|
|
|
{
|
|
|
|
#define COPY_SAVEFILE(name) \
|
|
|
|
(strcmp(findHandle.cFileName, name) == 0) \
|
|
|
|
{ \
|
|
|
|
strcpy(oldLocation, oldDirectory); \
|
|
|
|
strcat(oldLocation, "Saves\\"); \
|
|
|
|
strcat(oldLocation, name); \
|
|
|
|
strcpy(newLocation, output); \
|
|
|
|
strcat(newLocation, "saves\\"); \
|
|
|
|
strcat(newLocation, name); \
|
|
|
|
PLATFORM_copyFile(oldLocation, newLocation); \
|
|
|
|
}
|
|
|
|
if COPY_SAVEFILE("unlock.vvv")
|
|
|
|
else if COPY_SAVEFILE("tsave.vvv")
|
|
|
|
else if COPY_SAVEFILE("qsave.vvv")
|
|
|
|
#undef COPY_SAVEFILE
|
|
|
|
}
|
|
|
|
} while (FindNextFile(hFind, &findHandle));
|
|
|
|
*/
|
|
|
|
|
|
|
|
sprintf(fileSearch, "%s\\*.vvvvvv", oldDirectory);
|
|
|
|
hFind = FindFirstFile(fileSearch, &findHandle);
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
printf("Could not find directory %s\n", oldDirectory);
|
|
|
|
}
|
|
|
|
else do
|
|
|
|
{
|
|
|
|
if ((findHandle.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
|
|
|
{
|
|
|
|
strcpy(oldLocation, oldDirectory);
|
|
|
|
strcat(oldLocation, findHandle.cFileName);
|
|
|
|
strcpy(newLocation, output);
|
|
|
|
strcat(newLocation, "levels\\");
|
|
|
|
strcat(newLocation, findHandle.cFileName);
|
|
|
|
PLATFORM_copyFile(oldLocation, newLocation);
|
|
|
|
}
|
|
|
|
} while (FindNextFile(hFind, &findHandle));
|
|
|
|
#else
|
|
|
|
#error See PLATFORM_migrateSaveData
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void PLATFORM_copyFile(const char *oldLocation, const char *newLocation)
|
|
|
|
{
|
|
|
|
char *data;
|
2020-05-18 23:03:14 +02:00
|
|
|
size_t length, bytes_read, bytes_written;
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
/* Read data */
|
|
|
|
FILE *file = fopen(oldLocation, "rb");
|
|
|
|
if (!file)
|
|
|
|
{
|
|
|
|
printf("Cannot open/copy %s\n", oldLocation);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
length = ftell(file);
|
|
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
data = (char*) malloc(length);
|
2020-05-18 23:03:14 +02:00
|
|
|
bytes_read = fread(data, 1, length, file);
|
2020-01-01 21:29:24 +01:00
|
|
|
fclose(file);
|
2020-05-18 23:03:14 +02:00
|
|
|
if (bytes_read != length)
|
|
|
|
{
|
|
|
|
printf("An error occurred when reading from %s\n", oldLocation);
|
|
|
|
free(data);
|
|
|
|
return;
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
|
|
|
|
/* Write data */
|
|
|
|
file = fopen(newLocation, "wb");
|
|
|
|
if (!file)
|
|
|
|
{
|
|
|
|
printf("Could not write to %s\n", newLocation);
|
|
|
|
free(data);
|
|
|
|
return;
|
|
|
|
}
|
2020-05-18 23:03:14 +02:00
|
|
|
bytes_written = fwrite(data, 1, length, file);
|
2020-01-01 21:29:24 +01:00
|
|
|
fclose(file);
|
|
|
|
free(data);
|
|
|
|
|
|
|
|
/* WTF did we just do */
|
|
|
|
printf("Copied:\n\tOld: %s\n\tNew: %s\n", oldLocation, newLocation);
|
2020-05-18 23:03:14 +02:00
|
|
|
if (bytes_written != length)
|
|
|
|
{
|
|
|
|
printf("Warning: an error occurred when writing to %s\n", newLocation);
|
|
|
|
}
|
2020-01-01 21:29:24 +01:00
|
|
|
}
|
2020-04-18 03:48:55 +02:00
|
|
|
|
|
|
|
bool FILESYSTEM_openDirectoryEnabled()
|
|
|
|
{
|
2020-04-18 17:37:28 +02:00
|
|
|
/* This is just a check to see if we're on a desktop or tenfoot setup.
|
|
|
|
* If you're working on a tenfoot-only build, add a def that always
|
|
|
|
* returns false!
|
|
|
|
*/
|
2020-04-18 17:38:27 +02:00
|
|
|
return !SDL_GetHintBoolean("SteamTenfoot", SDL_FALSE);
|
2020-04-18 03:48:55 +02:00
|
|
|
}
|
|
|
|
|
2020-04-18 17:37:28 +02:00
|
|
|
#ifdef _WIN32
|
2020-04-18 03:48:55 +02:00
|
|
|
bool FILESYSTEM_openDirectory(const char *dname)
|
|
|
|
{
|
|
|
|
ShellExecute(NULL, "open", dname, NULL, NULL, SW_SHOWMINIMIZED);
|
|
|
|
return true;
|
|
|
|
}
|
2020-04-20 15:41:11 +02:00
|
|
|
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) || defined(__DragonFly__)
|
2020-05-14 22:46:57 +02:00
|
|
|
#if defined(__APPLE__) || defined(__HAIKU__)
|
2020-04-18 03:48:55 +02:00
|
|
|
const char* open_cmd = "open";
|
2020-05-14 22:46:57 +02:00
|
|
|
#else
|
|
|
|
const char* open_cmd = "xdg-open";
|
2020-04-18 03:48:55 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
extern "C" char** environ;
|
|
|
|
|
|
|
|
bool FILESYSTEM_openDirectory(const char *dname)
|
|
|
|
{
|
|
|
|
pid_t child;
|
|
|
|
// This const_cast is legal (ctrl-f "The statement" at https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
|
2020-06-01 01:43:10 +02:00
|
|
|
char* argv[3] =
|
|
|
|
{
|
|
|
|
const_cast<char*>(open_cmd),
|
|
|
|
const_cast<char*>(dname),
|
|
|
|
NULL
|
|
|
|
};
|
2020-04-18 03:48:55 +02:00
|
|
|
posix_spawnp(&child, open_cmd, NULL, NULL, argv, environ);
|
|
|
|
int status = 0;
|
|
|
|
waitpid(child, &status, 0);
|
|
|
|
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
bool FILESYSTEM_openDirectory(const char *dname)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
2020-04-26 22:22:26 +02:00
|
|
|
|
|
|
|
bool FILESYSTEM_delete(const char *name)
|
|
|
|
{
|
2020-05-18 23:07:52 +02:00
|
|
|
return PHYSFS_delete(name) != 0;
|
2020-04-26 22:22:26 +02:00
|
|
|
}
|