/* 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 . */ #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 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 Local_Storage::get_filenames_path(std::string path) { return std::vector(); } std::vector Local_Storage::load_image(std::string const& image_path, uint32_t * out_width, uint32_t * out_height) { return std::vector(); } 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 get_filenames(std::string in_path) { std::vector 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 get_filenames_recursive_w(std::wstring base_path) { if (base_path.back() == *L"\\") base_path.pop_back(); std::vector 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 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 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 get_filenames(std::string strPath) { DIR *dp; int i = 0; struct dirent *ep; std::vector 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 get_filenames_recursive(std::string base_path) { std::vector 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 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 Local_Storage::get_drive_list() { std::vector 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 Local_Storage::get_filenames_path(std::string path) { if (path.back() != *PATH_SEPARATOR) { path.append(PATH_SEPARATOR); } std::vector filenames = get_filenames(path); std::vector 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 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 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 Local_Storage::load_image(std::string const& image_path, uint32_t * out_width, uint32_t * out_height) { std::vector 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(width); } else { *out_width = 0; } } if (out_height != nullptr) { if (height > 0) { *out_height = static_cast(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