mirror of
https://gitlab.com/Mr_Goldberg/goldberg_emulator
synced 2025-05-23 21:57:40 +08:00
1220 lines
34 KiB
C++
1220 lines
34 KiB
C++
/* Copyright (C) 2019 Mr Goldberg
|
|
This file is part of the Goldberg Emulator
|
|
|
|
The Goldberg Emulator is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 3 of the License, or (at your option) any later version.
|
|
|
|
The Goldberg Emulator is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the Goldberg Emulator; if not, see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "local_storage.h"
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#define STB_IMAGE_STATIC
|
|
#define STBI_ONLY_PNG
|
|
#define STBI_ONLY_JPEG
|
|
#if defined(__WINDOWS__)
|
|
#define STBI_WINDOWS_UTF8
|
|
#endif
|
|
#include "../stb/stb_image.h"
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#define STB_IMAGE_WRITE_STATIC
|
|
#include "../stb/stb_image_write.h"
|
|
|
|
struct File_Data {
|
|
std::string name;
|
|
};
|
|
|
|
std::string convert_vector_image_pixel_t_to_std_string(std::vector<image_pixel_t> in) {
|
|
std::string out;
|
|
|
|
for (auto i : in) {
|
|
out += i.channels.r;
|
|
out += i.channels.g;
|
|
out += i.channels.b;
|
|
out += i.channels.a;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
struct our_stbi_buffer {
|
|
uint8 * target;
|
|
size_t target_length;
|
|
size_t current_offset;
|
|
};
|
|
|
|
void our_stbi_write_func(void *context, void *data, int size) {
|
|
struct our_stbi_buffer * con = (struct our_stbi_buffer *)context;
|
|
uint8 * in_data = (uint8 *)data;
|
|
if (in_data != NULL &&
|
|
con != NULL &&
|
|
con->target != NULL &&
|
|
con->target_length > 0 &&
|
|
con->current_offset < con->target_length &&
|
|
(con->current_offset + size) < con->target_length) {
|
|
for (size_t x = 0; (x < size && (con->current_offset + x) < con->target_length); x++) {
|
|
con->target[(con->current_offset + x)] = in_data[x];
|
|
}
|
|
con->current_offset += size;
|
|
}
|
|
return;
|
|
}
|
|
|
|
std::string convert_raw_uint8_to_png_std_string(uint8 * in, int width, int height, int components) {
|
|
struct our_stbi_buffer buf;
|
|
std::string out;
|
|
out.clear();
|
|
|
|
if (in != NULL && width > 0 && height > 0 && components > 0 && components <= 4) {
|
|
buf.target_length = (width * height * components);
|
|
buf.current_offset = 0;
|
|
buf.target = new uint8[(width * height * components)];
|
|
if (buf.target != NULL) {
|
|
if (stbi_write_png_to_func(our_stbi_write_func, &buf, width, height, components, in, 0) == 1) {
|
|
for (size_t x = 0; x < (width * height * components); x++) {
|
|
char a = (char)buf.target[x];
|
|
out += a;
|
|
}
|
|
}
|
|
|
|
delete buf.target;
|
|
buf.target = NULL;
|
|
}
|
|
buf.target_length = 0;
|
|
buf.current_offset = 0;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
std::string convert_raw_uint8_to_jpg_std_string(uint8 * in, int width, int height, int components) {
|
|
struct our_stbi_buffer buf;
|
|
std::string out;
|
|
out.clear();
|
|
|
|
if (in != NULL && width > 0 && height > 0 && components > 0 && components <= 4) {
|
|
buf.target_length = (width * height * components);
|
|
buf.current_offset = 0;
|
|
buf.target = new uint8[(width * height * components)];
|
|
if (buf.target != NULL) {
|
|
if (stbi_write_jpg_to_func(our_stbi_write_func, &buf, width, height, components, in, 0) == 1) {
|
|
for (size_t x = 0; x < (width * height * components); x++) {
|
|
char a = (char)buf.target[x];
|
|
out += a;
|
|
}
|
|
}
|
|
|
|
delete buf.target;
|
|
buf.target = NULL;
|
|
}
|
|
buf.target_length = 0;
|
|
buf.current_offset = 0;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
std::string convert_imgbuf_std_string_to_std_string_uint8(std::string in,
|
|
int * out_width,
|
|
int * out_height,
|
|
int * out_components,
|
|
int desired_components) {
|
|
std::string out;
|
|
out.clear();
|
|
int w = 0;
|
|
int h = 0;
|
|
int c = 0;
|
|
|
|
if (in.length() > 0 &&
|
|
desired_components > 0 &&
|
|
desired_components <= 4) {
|
|
uint8 * buf = (uint8*)stbi_load_from_memory((stbi_uc *)in.c_str(), in.length(), &w, &h, &c, desired_components);
|
|
if (buf != NULL) {
|
|
if (w > 0 && h > 0 && desired_components > 0) {
|
|
for (size_t x = 0; x < (w * h * desired_components); x++) {
|
|
char a = buf[x];
|
|
out += a;
|
|
}
|
|
}
|
|
if (out_width != NULL) {
|
|
*out_width = w;
|
|
}
|
|
if (out_height != NULL) {
|
|
*out_height = h;
|
|
}
|
|
if (out_components != NULL) {
|
|
*out_components = c;
|
|
}
|
|
stbi_image_free(buf);
|
|
} else {
|
|
out.clear();
|
|
if (out_width != NULL) {
|
|
*out_width = 0;
|
|
}
|
|
if (out_height != NULL) {
|
|
*out_height = 0;
|
|
}
|
|
if (out_components != NULL) {
|
|
*out_components = 0;
|
|
}
|
|
PRINT_DEBUG("%s %p. reason: %s\n", "Failed to decode image at", &in, stbi_failure_reason());
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
std::string convert_png_buffer_std_string_to_std_string_uint8(std::string in, int * width, int * height, int * components, int desired_components) {
|
|
return convert_imgbuf_std_string_to_std_string_uint8(in, width, height, components, desired_components);
|
|
}
|
|
|
|
std::string convert_jpg_buffer_std_string_to_std_string_uint8(std::string in, int * width, int * height, int * components, int desired_components) {
|
|
return convert_imgbuf_std_string_to_std_string_uint8(in, width, height, components, desired_components);
|
|
}
|
|
|
|
#ifdef NO_DISK_WRITES
|
|
std::string Local_Storage::get_program_path()
|
|
{
|
|
return " ";
|
|
}
|
|
|
|
std::string Local_Storage::get_user_pictures_path()
|
|
{
|
|
return " ";
|
|
}
|
|
|
|
std::string Local_Storage::get_user_appdata_path()
|
|
{
|
|
return " ";
|
|
}
|
|
|
|
bool Local_Storage::is_directory(std::string &path)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::string Local_Storage::get_parent_directory(std::string &path)
|
|
{
|
|
return " ";
|
|
}
|
|
|
|
std::string Local_Storage::get_game_settings_path()
|
|
{
|
|
return " ";
|
|
}
|
|
|
|
std::string Local_Storage::get_path(std::string folder)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
std::string Local_Storage::get_global_settings_path()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
Local_Storage::Local_Storage(std::string save_directory)
|
|
{
|
|
|
|
}
|
|
|
|
void Local_Storage::setAppId(uint32 appid)
|
|
{
|
|
|
|
}
|
|
|
|
int Local_Storage::store_file_data(std::string folder, std::string file, const char *data, unsigned int length)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int Local_Storage::store_data(std::string folder, std::string file, char *data, unsigned int length)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int Local_Storage::store_data_settings(std::string file, const char *data, unsigned int length)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length, unsigned int offset)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int Local_Storage::get_data_settings(std::string file, char *data, unsigned int max_length)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int Local_Storage::count_files(std::string folder)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool Local_Storage::file_exists(std::string folder, std::string file)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Local_Storage::data_settings_exists(std::string file)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
unsigned int Local_Storage::file_size(std::string folder, std::string file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool Local_Storage::file_delete(std::string folder, std::string file)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Local_Storage::delete_data_settings(std::string file)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint64_t Local_Storage::file_timestamp(std::string folder, std::string file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool Local_Storage::iterate_file(std::string folder, int index, char *output_filename, int32 *output_size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Local_Storage::update_save_filenames(std::string folder)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool Local_Storage::load_json(std::string full_path, nlohmann::json& json)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
|
|
{
|
|
return std::vector<std::string>();
|
|
}
|
|
|
|
std::vector<image_pixel_t> Local_Storage::load_image(std::string const& image_path, uint32_t * out_width, uint32_t * out_height)
|
|
{
|
|
return std::vector<image_pixel_t>();
|
|
}
|
|
|
|
bool Local_Storage::save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#else
|
|
#if defined(__WINDOWS__)
|
|
|
|
static BOOL DirectoryExists(LPCWSTR szPath)
|
|
{
|
|
DWORD dwAttrib = GetFileAttributesW(szPath);
|
|
|
|
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
|
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
|
}
|
|
|
|
static void createDirectoryRecursively(std::wstring path)
|
|
{
|
|
unsigned long long pos = 0;
|
|
do
|
|
{
|
|
pos = path.find_first_of(L"\\/", pos + 1);
|
|
CreateDirectoryW(path.substr(0, pos).c_str(), NULL);
|
|
} while (pos != std::string::npos);
|
|
}
|
|
|
|
static void create_directory(std::string in_path)
|
|
{
|
|
std::wstring strPath = utf8_decode(in_path);
|
|
if (DirectoryExists(strPath.c_str()) == FALSE)
|
|
createDirectoryRecursively(strPath);
|
|
}
|
|
|
|
static std::vector<struct File_Data> get_filenames(std::string in_path)
|
|
{
|
|
std::vector<struct File_Data> output;
|
|
in_path = in_path.append("\\*");
|
|
WIN32_FIND_DATAW ffd;
|
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
|
|
|
std::wstring strPath = utf8_decode(in_path);
|
|
// Start iterating over the files in the path directory.
|
|
hFind = ::FindFirstFileW (strPath.c_str(), &ffd);
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
do // Managed to locate and create an handle to that folder.
|
|
{
|
|
if (wcscmp(L".", ffd.cFileName) == 0) continue;
|
|
if (wcscmp(L"..", ffd.cFileName) == 0) continue;
|
|
struct File_Data f_data;
|
|
f_data.name = utf8_encode(ffd.cFileName);
|
|
output.push_back(f_data);
|
|
} while (::FindNextFileW(hFind, &ffd) == TRUE);
|
|
::FindClose(hFind);
|
|
} else {
|
|
//printf("Failed to find path: %s", strPath.c_str());
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
static std::vector<struct File_Data> get_filenames_recursive_w(std::wstring base_path)
|
|
{
|
|
if (base_path.back() == *L"\\")
|
|
base_path.pop_back();
|
|
std::vector<struct File_Data> output;
|
|
std::wstring strPath = base_path;
|
|
strPath = strPath.append(L"\\*");
|
|
WIN32_FIND_DATAW ffd;
|
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
|
|
|
// Start iterating over the files in the path directory.
|
|
hFind = ::FindFirstFileW (strPath.c_str(), &ffd);
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
do // Managed to locate and create an handle to that folder.
|
|
{
|
|
if (wcscmp(L".", ffd.cFileName) == 0) continue;
|
|
if (wcscmp(L"..", ffd.cFileName) == 0) continue;
|
|
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
// Construct new path from our base path
|
|
std::wstring dir_name = ffd.cFileName;
|
|
|
|
std::wstring path = base_path;
|
|
path += L"\\";
|
|
path += dir_name;
|
|
|
|
std::vector<struct File_Data> lower = get_filenames_recursive_w(path);
|
|
std::transform(lower.begin(), lower.end(), std::back_inserter(output), [&dir_name](File_Data f) {f.name = utf8_encode(dir_name) + "\\" + f.name; return f;});
|
|
} else {
|
|
File_Data f;
|
|
f.name = utf8_encode(ffd.cFileName);
|
|
output.push_back(f);
|
|
}
|
|
} while (::FindNextFileW(hFind, &ffd) == TRUE);
|
|
::FindClose(hFind);
|
|
} else {
|
|
//printf("Failed to find path: %s", strPath.c_str());
|
|
}
|
|
|
|
reset_LastError();
|
|
return output;
|
|
}
|
|
|
|
static std::vector<struct File_Data> get_filenames_recursive(std::string base_path)
|
|
{
|
|
return get_filenames_recursive_w(utf8_decode(base_path));
|
|
}
|
|
|
|
#else
|
|
|
|
static bool DirectoryExists(const char *path) {
|
|
char tmp[PATH_MAX_STRING_SIZE];
|
|
struct stat sb;
|
|
size_t len;
|
|
|
|
/* copy path */
|
|
len = strnlen (path, PATH_MAX_STRING_SIZE);
|
|
if (len == 0 || len == PATH_MAX_STRING_SIZE) {
|
|
return false;
|
|
}
|
|
memcpy (tmp, path, len);
|
|
tmp[len] = '\0';
|
|
|
|
/* check if path exists and is a directory */
|
|
if (stat (tmp, &sb) == 0) {
|
|
if (S_ISDIR (sb.st_mode)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* recursive mkdir */
|
|
static int mkdir_p(const char *dir, const mode_t mode) {
|
|
char tmp[PATH_MAX_STRING_SIZE];
|
|
char *p = NULL;
|
|
struct stat sb;
|
|
size_t len;
|
|
|
|
/* copy path */
|
|
len = strnlen (dir, PATH_MAX_STRING_SIZE);
|
|
if (len == 0 || len == PATH_MAX_STRING_SIZE) {
|
|
return -1;
|
|
}
|
|
memcpy (tmp, dir, len);
|
|
tmp[len] = '\0';
|
|
|
|
/* remove trailing slash */
|
|
if(tmp[len - 1] == '/') {
|
|
tmp[len - 1] = '\0';
|
|
}
|
|
|
|
/* check if path exists and is a directory */
|
|
if (stat (tmp, &sb) == 0) {
|
|
if (S_ISDIR (sb.st_mode)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* recursive mkdir */
|
|
for(p = tmp + 1; *p; p++) {
|
|
if(*p == '/') {
|
|
*p = 0;
|
|
/* test path */
|
|
if (stat(tmp, &sb) != 0) {
|
|
/* path does not exist - create directory */
|
|
if (mkdir(tmp, mode) < 0) {
|
|
return -1;
|
|
}
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
/* not a directory */
|
|
return -1;
|
|
}
|
|
*p = '/';
|
|
}
|
|
}
|
|
/* test path */
|
|
if (stat(tmp, &sb) != 0) {
|
|
/* path does not exist - create directory */
|
|
if (mkdir(tmp, mode) < 0) {
|
|
return -1;
|
|
}
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
/* not a directory */
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void create_directory(std::string strPath)
|
|
{
|
|
mkdir_p(strPath.c_str(), 0777);
|
|
}
|
|
|
|
static std::vector<struct File_Data> get_filenames(std::string strPath)
|
|
{
|
|
DIR *dp;
|
|
int i = 0;
|
|
struct dirent *ep;
|
|
std::vector<struct File_Data> output;
|
|
dp = opendir (strPath.c_str());
|
|
|
|
if (dp != NULL)
|
|
{
|
|
while ((ep = readdir (dp))) {
|
|
if (memcmp(ep->d_name, ".", 2) != 0 && memcmp(ep->d_name, "..", 3) != 0) {
|
|
struct File_Data f_data;
|
|
f_data.name = ep->d_name;
|
|
output.push_back(f_data);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
(void) closedir (dp);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
static std::vector<struct File_Data> get_filenames_recursive(std::string base_path)
|
|
{
|
|
std::vector<struct File_Data> output;
|
|
std::string path;
|
|
struct dirent *dp;
|
|
DIR *dir = opendir(base_path.c_str());
|
|
|
|
// Unable to open directory stream
|
|
if (!dir)
|
|
return output;
|
|
|
|
while ((dp = readdir(dir)) != NULL)
|
|
{
|
|
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
|
|
{
|
|
if (dp->d_type == DT_REG) {
|
|
File_Data f;
|
|
f.name = dp->d_name;
|
|
output.push_back(f);
|
|
} else if (dp->d_type == DT_DIR) {
|
|
// Construct new path from our base path
|
|
std::string dir_name = dp->d_name;
|
|
|
|
path = base_path;
|
|
path += "/";
|
|
path += dir_name;
|
|
|
|
std::vector<struct File_Data> lower = get_filenames_recursive(path);
|
|
std::transform(lower.begin(), lower.end(), std::back_inserter(output), [&dir_name](File_Data f) {f.name = dir_name + "/" + f.name; return f;});
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
return output;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
std::string Local_Storage::get_program_path()
|
|
{
|
|
return get_full_program_path();
|
|
}
|
|
|
|
std::string Local_Storage::get_game_settings_path()
|
|
{
|
|
return get_program_path().append(game_settings_folder).append(PATH_SEPARATOR);
|
|
}
|
|
|
|
std::string Local_Storage::get_user_pictures_path() {
|
|
std::string user_pictures_path = "Pictures";
|
|
#if defined(STEAM_WIN32)
|
|
WCHAR szPath[MAX_PATH] = {};
|
|
|
|
HRESULT hr = SHGetFolderPathW(NULL, CSIDL_MYPICTURES, NULL, 0, szPath);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
user_pictures_path = utf8_encode(szPath);
|
|
}
|
|
|
|
#else
|
|
char *datadir = getenv("HOME");
|
|
if (datadir) {
|
|
user_pictures_path = datadir;
|
|
}
|
|
#endif
|
|
return user_pictures_path;
|
|
}
|
|
|
|
std::string Local_Storage::get_user_appdata_path()
|
|
{
|
|
std::string user_appdata_path = "SAVE";
|
|
#if defined(STEAM_WIN32)
|
|
WCHAR szPath[MAX_PATH] = {};
|
|
|
|
HRESULT hr = SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, szPath);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
user_appdata_path = utf8_encode(szPath);
|
|
}
|
|
|
|
#else
|
|
/* $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
|
|
If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. */
|
|
char *datadir = getenv("XDG_DATA_HOME");
|
|
if (datadir) {
|
|
user_appdata_path = datadir;
|
|
} else {
|
|
char *homedir = getenv("HOME");
|
|
if (homedir) {
|
|
user_appdata_path = std::string(homedir) + "/.local/share";
|
|
}
|
|
}
|
|
#endif
|
|
return user_appdata_path.append(PATH_SEPARATOR).append(PROGRAM_NAME_1).append(PROGRAM_NAME_2).append(PROGRAM_NAME_3).append(PROGRAM_NAME_4).append(PROGRAM_NAME_5).append(PROGRAM_NAME_6).append(PROGRAM_NAME_7).append(PROGRAM_NAME_8).append(" Saves");
|
|
}
|
|
|
|
static std::string replace_with(std::string s, std::string const &old, const char *new_str)
|
|
{
|
|
int pos;
|
|
while ((pos = s.find(old)) != std::string::npos)
|
|
s.replace(pos, old.length(), new_str);
|
|
|
|
return s;
|
|
}
|
|
|
|
static std::string sanitize_file_name(std::string name)
|
|
{
|
|
//I'm not sure all of these are necessary but just to be sure
|
|
if (name[0] == '.' && name.size() > 2 && (name[1] == '\\' || name[1] == '/')) name.erase(0, 2);
|
|
|
|
#if defined(STEAM_WIN32)
|
|
name = replace_with(name, "/", PATH_SEPARATOR);
|
|
#else
|
|
//On linux does using "\\" in a remote storage file name create a directory?
|
|
//I didn't test but I'm going to say yes
|
|
name = replace_with(name, "\\", PATH_SEPARATOR);
|
|
#endif
|
|
name = replace_with(name, "|", ".V_SLASH.");
|
|
name = replace_with(name, ":", ".COLON.");
|
|
name = replace_with(name, "*", ".ASTERISK.");
|
|
name = replace_with(name, "\"", ".QUOTE.");
|
|
name = replace_with(name, "?", ".Q_MARK.");
|
|
|
|
return name;
|
|
}
|
|
|
|
static std::string desanitize_file_name(std::string name)
|
|
{
|
|
//I'm not sure all of these are necessary but just to be sure
|
|
name = replace_with(name, ".SLASH.", "/");
|
|
name = replace_with(name, ".B_SLASH.", "\\");
|
|
name = replace_with(name, ".F_SLASH.", "/");
|
|
name = replace_with(name, ".V_SLASH.", "|");
|
|
name = replace_with(name, ".COLON.", ":");
|
|
name = replace_with(name, ".ASTERISK.", "*");
|
|
name = replace_with(name, ".QUOTE.", "\"");
|
|
name = replace_with(name, ".Q_MARK.", "?");
|
|
|
|
return name;
|
|
}
|
|
|
|
Local_Storage::Local_Storage(std::string save_directory)
|
|
{
|
|
this->save_directory = save_directory;
|
|
|
|
if (this->save_directory.back() != *PATH_SEPARATOR) {
|
|
this->save_directory.append(PATH_SEPARATOR);
|
|
}
|
|
}
|
|
|
|
std::string Local_Storage::get_parent_directory(std::string &path)
|
|
{
|
|
std::string ret = "";
|
|
std::string temp = "";
|
|
|
|
ret = sanitize_file_name(path);
|
|
if (ret.length() > 1) {
|
|
temp = ret[(ret.length() - 1)];
|
|
if (temp == PATH_SEPARATOR) {
|
|
ret.erase((ret.length() - 1), 1);
|
|
}
|
|
}
|
|
|
|
if (ret.length() > 1) {
|
|
size_t last_pos = 0;
|
|
size_t count = 0;
|
|
for (auto i : ret) {
|
|
temp = i;
|
|
if (temp == PATH_SEPARATOR) {
|
|
last_pos = count;
|
|
}
|
|
count = count + 1;
|
|
}
|
|
if ((last_pos > 0) && (last_pos < (count - 1))) {
|
|
ret.erase(last_pos, (count - 1));
|
|
}
|
|
}
|
|
|
|
ret = desanitize_file_name(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool Local_Storage::is_directory(std::string &path)
|
|
{
|
|
bool ret = false;
|
|
#if defined(STEAM_WIN32)
|
|
std::wstring strPath = utf8_decode(path);
|
|
ret = DirectoryExists(strPath.c_str());
|
|
#else
|
|
ret = DirectoryExists(path.c_str());
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
std::vector<std::string> Local_Storage::get_drive_list()
|
|
{
|
|
std::vector<std::string> ret;
|
|
#if defined(STEAM_WIN32)
|
|
DWORD drives = GetLogicalDrives();
|
|
if (drives != 0) {
|
|
for (unsigned int x = 0; x < 26; x++) {
|
|
if (drives & (((DWORD)0x1) << x)) {
|
|
char tmp[2] = { 'A' + (char)x, '\0' };
|
|
ret.push_back(std::string(tmp) + ":");
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
//TODO: Parse /proc/self/mountinfo
|
|
ret.push_back("/");
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
void Local_Storage::setAppId(uint32 appid)
|
|
{
|
|
this->appid = std::to_string(appid) + PATH_SEPARATOR;
|
|
}
|
|
|
|
int Local_Storage::store_file_data(std::string folder, std::string file, const char *data, unsigned int length)
|
|
{
|
|
if (folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
file = sanitize_file_name(file);
|
|
std::string::size_type pos = file.rfind(PATH_SEPARATOR);
|
|
|
|
std::string file_folder;
|
|
if (pos == 0 || pos == std::string::npos) {
|
|
file_folder = "";
|
|
} else {
|
|
file_folder = file.substr(0,pos);
|
|
}
|
|
|
|
create_directory(folder + file_folder);
|
|
std::ofstream myfile;
|
|
myfile.open(utf8_decode(folder + file), std::ios::binary | std::ios::out);
|
|
if (!myfile.is_open()) return -1;
|
|
myfile.write(data, length);
|
|
int position = myfile.tellp();
|
|
myfile.close();
|
|
return position;
|
|
}
|
|
|
|
std::string Local_Storage::get_path(std::string folder)
|
|
{
|
|
std::string path = save_directory + appid + folder;
|
|
create_directory(path);
|
|
return path;
|
|
}
|
|
|
|
std::string Local_Storage::get_global_settings_path()
|
|
{
|
|
return save_directory + settings_storage_folder + PATH_SEPARATOR;
|
|
}
|
|
|
|
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
|
|
{
|
|
if (path.back() != *PATH_SEPARATOR) {
|
|
path.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
std::vector<struct File_Data> filenames = get_filenames(path);
|
|
std::vector<std::string> output;
|
|
std::transform(filenames.begin(), filenames.end(), std::back_inserter(output), [](struct File_Data d) { return d.name;});
|
|
return output;
|
|
}
|
|
|
|
int Local_Storage::store_data(std::string folder, std::string file, char *data, unsigned int length)
|
|
{
|
|
if (folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
return store_file_data(save_directory + appid + folder, file, data, length);
|
|
}
|
|
|
|
int Local_Storage::store_data_settings(std::string file, const char *data, unsigned int length)
|
|
{
|
|
return store_file_data(get_global_settings_path(), file, data, length);
|
|
}
|
|
|
|
int Local_Storage::copy_file_data(std::string src_full_path, std::string dest_full_path)
|
|
{
|
|
std::ifstream srcfile;
|
|
std::ofstream destfile;
|
|
char * buf = NULL;
|
|
size_t readcount = 0;
|
|
const size_t bufsize = 1024;
|
|
bool fail = false;
|
|
|
|
buf = new char[bufsize];
|
|
if (buf == NULL) return -1;
|
|
|
|
srcfile.open(utf8_decode(src_full_path), std::ios::binary | std::ios::in);
|
|
if (!srcfile.is_open()) {
|
|
delete buf;
|
|
buf = NULL;
|
|
return -1;
|
|
}
|
|
|
|
destfile.open(utf8_decode(dest_full_path), std::ios::binary | std::ios::out);
|
|
if (!destfile.is_open()) {
|
|
delete buf;
|
|
buf = NULL;
|
|
srcfile.close();
|
|
return -1;
|
|
}
|
|
|
|
srcfile.seekg(0, std::ios::beg);
|
|
destfile.seekp(0, std::ios::beg);
|
|
|
|
do {
|
|
buf[readcount] = srcfile.get();
|
|
if (srcfile.eof() || (readcount + 1) == bufsize) {
|
|
destfile.write(buf, (readcount + 1));
|
|
readcount = 0;
|
|
} else {
|
|
readcount++;
|
|
}
|
|
if (srcfile.fail() || destfile.fail()) {
|
|
fail = true;
|
|
}
|
|
} while (srcfile.eof() == false && srcfile.fail() == false && destfile.fail() == false);
|
|
|
|
srcfile.close();
|
|
destfile.close();
|
|
reset_LastError();
|
|
delete buf;
|
|
buf = NULL;
|
|
return (!fail) ? 1 : 0;
|
|
}
|
|
|
|
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset)
|
|
{
|
|
std::ifstream myfile;
|
|
myfile.open(utf8_decode(full_path), std::ios::binary | std::ios::in);
|
|
if (!myfile.is_open()) return -1;
|
|
|
|
myfile.seekg (offset, std::ios::beg);
|
|
myfile.read (data, max_length);
|
|
myfile.close();
|
|
reset_LastError();
|
|
return myfile.gcount();
|
|
}
|
|
|
|
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length, unsigned int offset)
|
|
{
|
|
file = sanitize_file_name(file);
|
|
if (folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
std::string full_path = save_directory + appid + folder + file;
|
|
return get_file_data(full_path, data, max_length, offset);
|
|
}
|
|
|
|
int Local_Storage::get_data_settings(std::string file, char *data, unsigned int max_length)
|
|
{
|
|
file = sanitize_file_name(file);
|
|
std::string full_path = get_global_settings_path() + file;
|
|
return get_file_data(full_path, data, max_length);
|
|
}
|
|
|
|
int Local_Storage::count_files(std::string folder)
|
|
{
|
|
if (folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
return get_filenames_recursive(save_directory + appid + folder).size();
|
|
}
|
|
|
|
bool Local_Storage::file_exists(std::string folder, std::string file)
|
|
{
|
|
file = sanitize_file_name(file);
|
|
if (folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
std::string full_path = save_directory + appid + folder + file;
|
|
return file_exists_(full_path);
|
|
}
|
|
|
|
bool Local_Storage::data_settings_exists(std::string file)
|
|
{
|
|
file = sanitize_file_name(file);
|
|
|
|
std::string full_path = get_global_settings_path() + file;
|
|
return file_exists_(full_path);
|
|
}
|
|
|
|
unsigned int Local_Storage::file_size(std::string folder, std::string file)
|
|
{
|
|
file = sanitize_file_name(file);
|
|
if (folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
std::string full_path = save_directory + appid + folder + file;
|
|
return file_size_(full_path);
|
|
}
|
|
|
|
unsigned int Local_Storage::data_settings_size(std::string file)
|
|
{
|
|
file = sanitize_file_name(file);
|
|
|
|
std::string full_path = get_global_settings_path() + file;
|
|
return file_size_(full_path);
|
|
}
|
|
|
|
bool _internal_file_delete(std::string & full_path)
|
|
{
|
|
#if defined(STEAM_WIN32)
|
|
return _wremove(utf8_decode(full_path).c_str()) == 0;
|
|
#else
|
|
return remove(full_path.c_str()) == 0;
|
|
#endif
|
|
}
|
|
|
|
bool Local_Storage::file_delete(std::string folder, std::string file)
|
|
{
|
|
file = sanitize_file_name(file);
|
|
if (folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
std::string full_path = save_directory + appid + folder + file;
|
|
return _internal_file_delete(full_path);
|
|
}
|
|
|
|
bool Local_Storage::delete_data_settings(std::string file)
|
|
{
|
|
file = sanitize_file_name(file);
|
|
|
|
std::string full_path = get_global_settings_path() + file;
|
|
return _internal_file_delete(full_path);
|
|
}
|
|
|
|
uint64_t Local_Storage::file_timestamp(std::string folder, std::string file)
|
|
{
|
|
file = sanitize_file_name(file);
|
|
if (folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
std::string full_path = save_directory + appid + folder + file;
|
|
|
|
#if defined(STEAM_WIN32)
|
|
struct _stat buffer = {};
|
|
if (_wstat(utf8_decode(full_path).c_str(), &buffer) != 0) return 0;
|
|
#else
|
|
struct stat buffer = {};
|
|
if (stat (full_path.c_str(), &buffer) != 0) return 0;
|
|
#endif
|
|
return buffer.st_mtime;
|
|
}
|
|
|
|
bool Local_Storage::iterate_file(std::string folder, int index, char *output_filename, int32 *output_size)
|
|
{
|
|
if (folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
|
|
std::vector<struct File_Data> files = get_filenames_recursive(save_directory + appid + folder);
|
|
if (index < 0 || index >= files.size()) return false;
|
|
|
|
std::string name = desanitize_file_name(files[index].name);
|
|
if (output_size) *output_size = file_size(folder, name);
|
|
#if defined(STEAM_WIN32)
|
|
name = replace_with(name, PATH_SEPARATOR, "/");
|
|
#endif
|
|
strcpy(output_filename, name.c_str());
|
|
return true;
|
|
}
|
|
|
|
bool Local_Storage::update_save_filenames(std::string folder)
|
|
{
|
|
std::vector<struct File_Data> files = get_filenames_recursive(save_directory + appid + folder);
|
|
|
|
for (auto &f : files) {
|
|
std::string path = f.name;
|
|
PRINT_DEBUG("Local_Storage:: remote file %s\n", path.c_str());
|
|
std::string to = sanitize_file_name(desanitize_file_name(path));
|
|
if (path != to && !file_exists(folder, to)) {
|
|
//create the folder
|
|
store_data(folder, to, (char *)"", 0);
|
|
file_delete(folder, to);
|
|
|
|
std::string from = (save_directory + appid + folder + PATH_SEPARATOR + path);
|
|
to = (save_directory + appid + folder + PATH_SEPARATOR + to);
|
|
PRINT_DEBUG("Local_Storage::update_save_filenames renaming %s to %s\n", from.c_str(), to.c_str());
|
|
if (std::rename(from.c_str(), to.c_str()) < 0) {
|
|
PRINT_DEBUG("ERROR RENAMING\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Local_Storage::load_json(std::string full_path, nlohmann::json& json)
|
|
{
|
|
std::ifstream inventory_file(utf8_decode(full_path));
|
|
// If there is a file and we opened it
|
|
if (inventory_file)
|
|
{
|
|
inventory_file.seekg(0, std::ios::end);
|
|
size_t size = inventory_file.tellg();
|
|
std::string buffer(size, '\0');
|
|
inventory_file.seekg(0);
|
|
// Read it entirely, if the .json file gets too big,
|
|
// I should look into this and split reads into smaller parts.
|
|
inventory_file.read(&buffer[0], size);
|
|
inventory_file.close();
|
|
|
|
try {
|
|
json = std::move(nlohmann::json::parse(buffer));
|
|
PRINT_DEBUG("Loaded json \"%s\". Loaded %" PRI_ZU " items.\n", full_path.c_str(), json.size());
|
|
return true;
|
|
} catch (std::exception& e) {
|
|
PRINT_DEBUG("Error while parsing \"%s\" json: %s\n", full_path.c_str(), e.what());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PRINT_DEBUG("Couldn't open file \"%s\" to read json\n", full_path.c_str());
|
|
}
|
|
|
|
reset_LastError();
|
|
return false;
|
|
}
|
|
|
|
bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json)
|
|
{
|
|
if (!folder.empty() && folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
std::string inv_path = std::move(save_directory + appid + folder);
|
|
std::string full_path = inv_path + file;
|
|
|
|
return load_json(full_path, json);
|
|
}
|
|
|
|
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json)
|
|
{
|
|
if (!folder.empty() && folder.back() != *PATH_SEPARATOR) {
|
|
folder.append(PATH_SEPARATOR);
|
|
}
|
|
std::string inv_path = std::move(save_directory + appid + folder);
|
|
std::string full_path = inv_path + file;
|
|
|
|
create_directory(inv_path);
|
|
|
|
std::ofstream inventory_file(utf8_decode(full_path), std::ios::trunc | std::ios::out);
|
|
if (inventory_file)
|
|
{
|
|
inventory_file << std::setw(2) << json;
|
|
return true;
|
|
}
|
|
|
|
PRINT_DEBUG("Couldn't open file \"%s\" to write json\n", full_path.c_str());
|
|
|
|
reset_LastError();
|
|
return false;
|
|
}
|
|
|
|
std::vector<image_pixel_t> Local_Storage::load_image(std::string const& image_path, uint32_t * out_width, uint32_t * out_height)
|
|
{
|
|
std::vector<image_pixel_t> res;
|
|
int32_t width = 0;
|
|
int32_t height = 0;
|
|
image_pixel_t* img = (image_pixel_t*)stbi_load(image_path.c_str(), &width, &height, nullptr, 4);
|
|
if (img != nullptr)
|
|
{
|
|
res.resize(width*height);
|
|
std::copy(img, img + width * height, res.begin());
|
|
|
|
stbi_image_free(img);
|
|
} else {
|
|
width = 0;
|
|
height = 0;
|
|
PRINT_DEBUG("%s %s. reason: %s\n", "Failed to load image at", image_path.c_str(), stbi_failure_reason());
|
|
}
|
|
if (out_width != nullptr) {
|
|
if (width > 0) {
|
|
*out_width = static_cast<uint32_t>(width);
|
|
} else {
|
|
*out_width = 0;
|
|
}
|
|
}
|
|
if (out_height != nullptr) {
|
|
if (height > 0) {
|
|
*out_height = static_cast<uint32_t>(height);
|
|
} else {
|
|
*out_height = 0;
|
|
}
|
|
}
|
|
|
|
reset_LastError();
|
|
return res;
|
|
}
|
|
|
|
int32_t Local_Storage::save_avatar_image(int32_t eAvatarSize, int32_t width, int32_t height, uint8_t * img_ptr)
|
|
{
|
|
int32_t ret = 0;
|
|
std::string image_path = "";
|
|
switch (eAvatarSize) {
|
|
case k_EAvatarSize32x32:
|
|
if (width > 0 &&
|
|
width <= 32 &&
|
|
height > 0 &&
|
|
height <= 32) {
|
|
image_path += "avatar_small.";
|
|
}
|
|
break;
|
|
case k_EAvatarSize64x64:
|
|
if (width > 32 &&
|
|
width <= 64 &&
|
|
height > 32 &&
|
|
height <= 64) {
|
|
image_path += "avatar_medium.";
|
|
}
|
|
break;
|
|
case k_EAvatarSize184x184:
|
|
if (width > 64 &&
|
|
width <= 184 &&
|
|
height > 64 &&
|
|
height <= 184) {
|
|
image_path += "avatar_large.";
|
|
}
|
|
break;
|
|
case k_EAvatarSizeMAX:
|
|
default:
|
|
image_path.clear();
|
|
break;
|
|
};
|
|
|
|
if (image_path.length() > 0 && img_ptr != NULL) {
|
|
delete_data_settings(image_path + "jpg");
|
|
image_path += "png";
|
|
delete_data_settings(image_path);
|
|
|
|
image_path = get_global_settings_path() + image_path;
|
|
ret = (stbi_write_png(image_path.c_str(), width, height, 4, img_ptr, 0) == 1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool Local_Storage::save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels)
|
|
{
|
|
std::string screenshot_path = std::move(save_directory + appid + screenshots_folder + PATH_SEPARATOR);
|
|
create_directory(screenshot_path);
|
|
screenshot_path += image_path;
|
|
return stbi_write_png(screenshot_path.c_str(), width, height, channels, img_ptr, 0) == 1;
|
|
}
|
|
|
|
#endif
|