blob: 5db3a1f6047a6577d320ca7c7c1fdb8b9b8bdd9c [file] [log] [blame] [edit]
#include <cerrno>
#include <string>
#include <map>
#ifdef WIN32
#include <direct.h>
#include <shlobj.h>
#else
#include <dirent.h>
#include <sys/stat.h>
#endif
#include "SDL.h"
#include "File.hpp"
#include "UserCode.hpp"
static std::string basePath;
void setup_base_path() {
auto basePathPtr = SDL_GetBasePath();
if(basePathPtr)
basePath = std::string(basePathPtr);
SDL_free(basePathPtr);
}
void *open_file(const std::string &name, int mode) {
const char *str_mode;
if(mode == blit::OpenMode::read)
str_mode = "rb";
else if(mode == blit::OpenMode::write)
str_mode = "wb";
else if(mode == (blit::OpenMode::read | blit::OpenMode::write))
str_mode = "r+";
else
return nullptr;
auto file = SDL_RWFromFile((basePath + name).c_str(), str_mode);
if(!file) {
// check if the path is under the save path
std::string save_path = get_save_path();
if(name.compare(0, save_path.length(), save_path) == 0)
file = SDL_RWFromFile(name.c_str(), str_mode);
}
return file;
}
int32_t read_file(void *fh, uint32_t offset, uint32_t length, char *buffer) {
auto file = (SDL_RWops *)fh;
if(file && SDL_RWseek(file, offset, RW_SEEK_SET) != -1) {
size_t bytes_read = SDL_RWread(file, buffer, 1, length);
if(bytes_read > 0)
return (int32_t)bytes_read;
}
return -1;
}
int32_t write_file(void *fh, uint32_t offset, uint32_t length, const char *buffer) {
auto file = (SDL_RWops *)fh;
if(file && SDL_RWseek(file, offset, RW_SEEK_SET) != -1) {
size_t bytes_written = SDL_RWwrite(file, buffer, 1, length);
if(bytes_written > 0)
return (int32_t)bytes_written;
}
return -1;
}
int32_t close_file(void *fh) {
return SDL_RWclose((SDL_RWops *)fh) == 0 ? 0 : -1;
}
uint32_t get_file_length(void *fh)
{
auto file = (SDL_RWops *)fh;
SDL_RWseek(file, 0, RW_SEEK_END);
return (uint32_t)SDL_RWtell(file);
}
void list_files(const std::string &path, std::function<void(blit::FileInfo &)> callback) {
#ifdef WIN32
HANDLE file;
WIN32_FIND_DATAA findData;
file = FindFirstFileA((basePath + path + "\\*").c_str(), &findData);
if(file == INVALID_HANDLE_VALUE)
return;
do
{
blit::FileInfo info;
info.name = findData.cFileName;
if(info.name == "." || info.name == "..")
continue;
info.flags = 0;
info.size = findData.nFileSizeLow;
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
info.flags |= blit::FileFlags::directory;
callback(info);
}
while(FindNextFileA(file, &findData) != 0);
FindClose(file);
#else
auto dir = opendir((basePath + path).c_str());
if(!dir)
return;
struct dirent *ent;
while((ent = readdir(dir))) {
blit::FileInfo info;
info.name = ent->d_name;
if(info.name == "." || info.name == "..")
continue;
struct stat stat_buf;
if(stat((basePath + path + "/" + info.name).c_str(), &stat_buf) < 0)
continue;
info.flags = 0;
info.size = stat_buf.st_size;
if(S_ISDIR(stat_buf.st_mode))
info.flags |= blit::FileFlags::directory;
callback(info);
}
closedir(dir);
#endif
}
bool file_exists(const std::string &path) {
#ifdef WIN32
DWORD attribs = GetFileAttributesA((basePath + path).c_str());
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#else
struct stat stat_buf;
return (stat((basePath + path).c_str(), &stat_buf) == 0 && S_ISREG(stat_buf.st_mode));
#endif
}
bool directory_exists(const std::string &path) {
#ifdef WIN32
DWORD attribs = GetFileAttributesA((basePath + path).c_str());
return (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY));
#else
struct stat stat_buf;
return (stat((basePath + path).c_str(), &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode));
#endif
}
bool create_directory(const std::string &path) {
#ifdef WIN32
return _mkdir((basePath + path).c_str()) == 0 || errno == EEXIST;
#else
return mkdir((basePath + path).c_str(), 0755) == 0 || errno == EEXIST;
#endif
}
bool rename_file(const std::string &old_name, const std::string &new_name) {
return rename((basePath + old_name).c_str(), (basePath + new_name).c_str()) == 0;
}
bool remove_file(const std::string &path) {
return remove((basePath + path).c_str()) == 0;
}
static std::string save_path;
const char *get_save_path() {
#ifdef __EMSCRIPTEN__
// The Emscripten backend's GetPrefPath is a little broken (doesn't create the dirs correctly)
// Work around it until the fix gets released
mkdir("/libsdl", 0700);
mkdir((std::string("/libsdl/") + metadata_author).c_str(), 0700);
#endif
auto tmp = SDL_GetPrefPath(metadata_author, metadata_title);
save_path = std::string(tmp);
SDL_free(tmp);
return save_path.c_str();
}
bool is_storage_available() {
return true;
}